feat(server): auto-provision default agent + watcher; add manual-trigger endpoint#824
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds sentinel-based default provisioning (default agent + device-pinned daily watcher), a device-scoped manual watcher trigger endpoint, wiring to provision watchers during device registration and at startup, and integration tests for provisioning and manual triggering. ChangesDefault provisioning and watcher manual trigger
Sequence DiagramsequenceDiagram
participant Device as Mac App Device
participant Server as Lobu Server
participant Database
rect rgba(76, 175, 80, 0.5)
note over Device,Database: 1. Device Registration & Default Provisioning
Device->>Server: pollWorkerJob (device_worker registration)
Server->>Database: INSERT device_worker (created event)
Server->>Database: check org agent sentinel
alt agent sentinel set
Server->>Database: ensureDefaultWatcher(org, device_worker_id)
Database-->>Server: daily check-in watcher pinned
end
end
rect rgba(33, 150, 243, 0.5)
note over Device,Database: 2. Manual Trigger by Device
Device->>Server: POST /api/workers/me/watchers/:id/trigger (device-bound PAT)
Server->>Database: verify token bound to device_worker_id
Server->>Database: load watcher, verify org & device pin
Server->>Database: enqueueWatcherRunForWatcher('manual')
Database-->>Server: {run_id, already_queued}
Server-->>Device: 200 {run_id, status, already_queued}
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
…ger endpoint Adds the server half of the Mac-app onboarding flow: - `auth/default-provisioning.ts` provisions a default `owletto-default` agent for the bootstrap org at boot, and a daily-check-in watcher pinned to the user's Mac the first time it polls. Both writes are guarded by sentinel timestamps in `organization.metadata`: a deletion via the web UI does NOT trigger auto-recreation on the next boot/poll. - `start-local.ts` calls `ensureDefaultAgent` after `ensureBootstrapPat`. - `pollWorkerJob` (in `worker-api.ts`) calls `ensureDefaultWatcher` only when a device_workers row is freshly INSERTed AND the org has the agent sentinel set — defers watcher creation until the device id exists, then pins the watcher to that exact device via `device_worker_id`. - New endpoint `POST /api/workers/me/watchers/:watcher_id/trigger` lets the Mac app re-fire a watcher on demand. Auth: bound device_worker token with `device_worker:run` scope. The handler enforces a strict bound-worker → watcher.device_worker_id match, and reuses `enqueueWatcherRunForWatcher` so the existing active-run lane (pending/claimed/running) gives broad idempotency. Manual fires do NOT advance `watchers.next_run_at`. - The `/api/workers/*` middleware whitelist now lets the trigger path through for user-scoped workers; trusted-fleet/admin paths stay blocked. - `listWatchers` projects `last_fired_at` so the Mac app's watcher list can surface "last fired" without a follow-up request. Tests: - `integration/auth/default-provisioning.test.ts` covers all sentinel behavior: idempotency, deletion stickiness, has_agents short-circuit, agent-fallback when the default agent is gone, no-agent skip-and-stamp. - `integration/watchers/manual-trigger.test.ts` covers happy path, wrong device 403, idempotent re-trigger against pending/claimed/running runs, no next_run_at advance, 404 for unknown watcher id. Pi review concerns addressed: - **Deletion stickiness**: sentinels in `organization.metadata`, never rewritten by deletion; tests pin this. - **Provisioning timing**: watcher creation deferred to first device registration so it can pin to a real device_worker_id. - **Route whitelist**: explicit regex added to the user-scoped allowlist for `/api/workers/me/watchers/<id>/trigger`. - **Broad idempotency**: any active-status run (pending / claimed / running) reuses the existing run_id with `already_queued: true`. - **Prompt guardrail**: both the agent identity and watcher prompt explicitly instruct the model to say "I don't have access to recent history or context" when that's the case, instead of fabricating.
5dd75f7 to
f3adb65
Compare
There was a problem hiding this comment.
💡 Codex Review
lobu/packages/server/src/worker-api.ts
Line 293 in 5dd75f7
When the first newly registered device in the bootstrap org is not the Mac app (for example a chrome-extension or ios worker), this block still provisions the daily check-in watcher and pins it to that device. The watcher claim path does not require any capability for watcher runs, so the non-Mac device can receive the watcher run while the later Mac device will never get the default watcher because the org sentinel is set. Please require the effective/stored platform to be macos before calling ensureDefaultWatcher.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| 1, NULL, ${'{}'}::text[], | ||
| 'active', ${createdBy}, NOW(), NOW(), | ||
| ${watcherId}, | ||
| ${params.deviceWorkerId}::uuid, NULL, |
There was a problem hiding this comment.
Unpin default watchers before deleting devices
Because this new default watcher stores a device_worker_id, deleting that device now hits the existing watchers_device_worker_id_fkey (which has no ON DELETE action). The current device delete handler only clears connections.device_worker_id before DELETE FROM device_workers, so users with the auto-provisioned watcher will get a failed device deletion until the watcher is unpinned/archived or the FK behavior is changed.
Useful? React with 👍 / 👎.
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Pi review flagged `await import('./auth/default-provisioning')` violating
the project's no-dynamic-imports rule. Hoist to a top-level static import.
Pi review summaryManual review (pi CLI was broken — stale ctx in chatgpt-multi-account ext). Findings
Green (no concerns)
|
Pulls in #823 (revert goals primitive), #824 (auto-provision default agent + manual trigger), #825 (owletto submodule bump to #154). Submodule merged origin/main into our feat/chrome-debugger-executor branch separately; bumps the parent's submodule pointer to the merge SHA so both the executor work and main's recent owletto changes (Mac app LobuClient + connection-settings UI rework + drop default-form-layout) ship together.
Summary
Server-side half of the Mac-app onboarding flow.
owletto-defaultagent for the bootstrap org at boot, sticky against deletion via a sentinel inorganization.metadata.Spec / plan: see the conversation that opened this PR.
Pi review concerns — resolution
Test plan
Out of scope
Summary by CodeRabbit
New Features
Tests