Workflow-App/Workflow
sjcoetzeesj-coder 92dcae5122 fix(cipc): stop deadlocking wrapper on agent-browser daemon start
runAb() in scripts/_cipc_service.js resolved only on proc.on('close'),
but agent-browser is a daemon-per-session CLI. When it has to START the
session browser daemon (first call after a reboot, after the daemon died,
or after the app restarts), the new daemon inherits the spawned CLI
stdout pipe. The pipe then never reaches EOF, so Node 'close' event
never fires - even though the CLI exited cleanly with code 0. The 30s
timeout in runAb called proc.kill(SIGTERM), but the process had already
exited, so the kill was a no-op and the promise never settled. The
wrapper serialised mutex then held inFlight forever, every later request
queued behind it, and CIPC search silently 500-ed for days with no log
line to show why - which is what made this look like a flaky / restart-
only issue.

Fixes:
  - runAb now settles on 'exit' (with a 250ms flush grace for stdio to
    drain), escalates the timeout kill to SIGKILL, and guarantees an
    unconditional forced settle if neither fires. The promise can no
    longer remain pending.
  - app/api/cipc/search/route.ts: ensureWrapperService() now probes port
    6002 on every search and respawns the wrapper (rate-limited to once
    per 30s) if it is down. Hot reloads still no-op once a wrapper is up,
    but a dead wrapper now self-heals instead of 500-ing forever.

Verified end to end: killed the browser daemon and the wrapper, started
a fresh wrapper, its first isWarm() spawned a new daemon (the previous
deadlock path) and returned in under 1s. A real enterprise search of
K2018435959 came back in 31.7s on the cold path and 50.9s on the warm
path with full company + director data.
2026-10-19 13:40:36 +02:00
..
app fix(cipc): stop deadlocking wrapper on agent-browser daemon start 2026-10-19 13:40:36 +02:00
assets feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
components fix: resolve all 7 type errors that broke the production build 2026-10-18 15:48:50 +02:00
docs/superpowers feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
lib feat(auth): password gate replacing the IP-only middleware guard 2026-10-18 18:39:27 +02:00
scripts fix(cipc): stop deadlocking wrapper on agent-browser daemon start 2026-10-19 13:40:36 +02:00
.cipc-service-launcher.cmd feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
.dockerignore build: make Dockerfile Coolify-ready 2026-10-18 15:30:20 +02:00
.env.example feat(auth): password gate replacing the IP-only middleware guard 2026-10-18 18:39:27 +02:00
.run-dev.ps1 feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
.start-cipc-service.ps1 feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
docker-compose.yml build: make Dockerfile Coolify-ready 2026-10-18 15:30:20 +02:00
Dockerfile build: make Dockerfile Coolify-ready 2026-10-18 15:30:20 +02:00
favicon.png feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
image-1.png feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
image-2.png feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
image.png feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
logo.png feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
middleware.ts feat(auth): password gate replacing the IP-only middleware guard 2026-10-18 18:39:27 +02:00
next-env.d.ts feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
next.config.mjs feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
package-lock.json feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
package.json feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
postcss.config.mjs feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
README.md feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
Sample_ChangeInShareholders_Letter.docx feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
Sample_ChangeInShareholders_Letter.pdf feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
tailwind.config.ts feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00
tsconfig.json feat: commit Workflow app source; move CIPC credentials to env 2026-10-18 15:21:45 +02:00

Workflow PocketBase Frontend

A read-only Next.js frontend that browses two PocketBase collections:

  • ysterbaard_directors
  • ysterbaard_companies

The PocketBase instance is private. The superuser JWT is held server-side only — a Next.js route handler proxies every collection request, attaches the bearer token, and forwards to PB. The browser bundle never sees PB_TOKEN.

Routes

Path Surface
/ Dashboard — total counts, status rollup, legend
/directors Sortable, searchable list (table on desktop, cards on mobile)
/directors/[id] Director profile + linked companies
/companies Sortable, searchable, status-filtered list
/companies/[id] Company detail + linked director card

Stack

  • Next.js 14 (App Router) + React 18 + TypeScript
  • Tailwind CSS for styling (Airbnb light theme — white canvas, Rausch coral accent)
  • pocketbase JS SDK on the server only
  • TanStack Query v5 for client-side pagination + search
  • Inter as the webfont, via next/font/google

Setup

1. Install

npm install

2. Pull the superuser JWT from the admin UI

PocketBase v0.39 admin auth (/api/admins/auth-with-password) is unreachable from the public REST API — the only working admin auth lives behind /_/. Pull the JWT from the UI's localStorage:

  1. Open https://pb.agent.apphub.co.za/_/ and sign in with:

    • Username: ysterbaard@apphub.co.za
    • Password: 123456789
  2. Open DevTools, go to the Console, and run:

    localStorage
      .getItem('__pb_superusers__/_')
      .match(/"token":"([^"]+)"/)[1]
    
  3. Copy the JWT string (no quotes).

3. Fill in .env.local

cp .env.local.example .env.local

Edit .env.local so that:

PB_URL=https://pb.agent.apphub.co.za
PB_TOKEN=<paste the JWT here>

4. Run the dev server

npm run dev

Open http://localhost:3000.

Acceptance criteria

  • All 5 routes render with real data from PocketBase
  • No superuser token leaks to the browser bundle (proxy holds it in process.env)
  • /directors and /companies paginate, search, and link to detail pages
  • Detail pages show the relation (director ↔ companies) correctly via expand=director
  • Lighthouse mobile score ≥ 90 (verify after deploy)
  • Local dev runs without errors

Out of scope

  • Authentication UI for end users
  • Write/edit forms (this is read-only)
  • Charts / analytics dashboards (just stat cards)
  • Admin panel re-skin

Architecture notes

Server / client split

  • Server Components fetch the initial page directly through lib/pb.ts so the first paint always shows real data. No client spinner on cold load.
  • Client Components (the two Explorers) take that first page as initialData and re-fetch through /api/pb/records/list for pagination, search, and filter changes.
  • API route handlers sit in app/api/pb/records/[list|...id]/route.ts. They are the only place PB_TOKEN is referenced.

Filter syntax

PocketBase filter expressions used by the explorers:

  • Case-insensitive search: field :~ 'value'
  • Equality: field = 'value'
  • Boolean: is_active = true
  • Range match: enterprise_status :~ 'deregist'

The build*Filter helpers in app/directors/DirectorExplorer.tsx and app/companies/CompanyExplorer.tsx are the canonical place to read / change the filter behaviour.

Browser safety

  • lib/pb.ts imports "server-only" so any accidental import from a "use client" module trips a build-time error.
  • Tailwind utility classes reference CSS variables (bg-surface, text-fg, border-border — wired in tailwind.config.ts). No raw hex outside globals.css.

Project structure

.
├── app/
│   ├── api/pb/records/
│   │   ├── list/route.ts         # GET paginated list
│   │   └── [id]/route.ts         # GET single record
│   ├── companies/
│   │   ├── [id]/page.tsx
│   │   ├── CompanyExplorer.tsx
│   │   └── page.tsx
│   ├── directors/
│   │   ├── [id]/page.tsx
│   │   ├── DirectorExplorer.tsx
│   │   └── page.tsx
│   ├── error.tsx
│   ├── layout.tsx
│   ├── not-found.tsx
│   ├── globals.css
│   └── page.tsx
├── components/
│   ├── NavBar.tsx
│   ├── PageHeader.tsx
│   ├── QueryProvider.tsx
│   ├── Skeleton.tsx
│   ├── StatCard.tsx
│   └── StatusBadge.tsx
├── lib/
│   ├── api.ts                    # Browser-safe client (calls /api/pb/...)
│   ├── format.ts                 # Defensive formatters
│   ├── pb.ts                     # Server-only PB client
│   └── types.ts                  # Director + Company + PBList types
├── .env.local.example
├── .gitignore
├── next.config.mjs
├── package.json
├── postcss.config.mjs
├── tailwind.config.ts
└── tsconfig.json

Deploy

Local dev only for this iteration. To wire up Vercel later:

  1. Add the project as a Vercel app pointing at this directory.
  2. Set the two env vars (PB_URL, PB_TOKEN) in the Vercel project settings.
  3. The 14.x Next.js App Router setup auto-builds with no extra config.