@amaster.ai/mirrorx-device-client 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/LICENSE +172 -0
  2. package/README.md +487 -2
  3. package/dist/codebuddy-auth.d.ts +2 -0
  4. package/dist/codebuddy-auth.d.ts.map +1 -0
  5. package/dist/codebuddy-auth.js +80 -0
  6. package/dist/codebuddy-auth.js.map +1 -0
  7. package/dist/codebuddy-locks.d.ts +12 -0
  8. package/dist/codebuddy-locks.d.ts.map +1 -0
  9. package/dist/codebuddy-locks.js +101 -0
  10. package/dist/codebuddy-locks.js.map +1 -0
  11. package/dist/codebuddy-redaction.d.ts +3 -0
  12. package/dist/codebuddy-redaction.d.ts.map +1 -0
  13. package/dist/codebuddy-redaction.js +31 -0
  14. package/dist/codebuddy-redaction.js.map +1 -0
  15. package/dist/codebuddy-runtime.d.ts +82 -0
  16. package/dist/codebuddy-runtime.d.ts.map +1 -0
  17. package/dist/codebuddy-runtime.js +285 -0
  18. package/dist/codebuddy-runtime.js.map +1 -0
  19. package/dist/codebuddy-session.d.ts +13 -0
  20. package/dist/codebuddy-session.d.ts.map +1 -0
  21. package/dist/codebuddy-session.js +73 -0
  22. package/dist/codebuddy-session.js.map +1 -0
  23. package/dist/codebuddy-tool-gate.d.ts +4 -0
  24. package/dist/codebuddy-tool-gate.d.ts.map +1 -0
  25. package/dist/codebuddy-tool-gate.js +10 -0
  26. package/dist/codebuddy-tool-gate.js.map +1 -0
  27. package/dist/codex-runtime.d.ts +6 -0
  28. package/dist/codex-runtime.d.ts.map +1 -0
  29. package/dist/codex-runtime.js +24 -0
  30. package/dist/codex-runtime.js.map +1 -0
  31. package/dist/computer.d.ts +25 -0
  32. package/dist/computer.d.ts.map +1 -0
  33. package/dist/computer.js +161 -0
  34. package/dist/computer.js.map +1 -0
  35. package/dist/configuration-lifetime.d.ts +15 -0
  36. package/dist/configuration-lifetime.d.ts.map +1 -0
  37. package/dist/configuration-lifetime.js +143 -0
  38. package/dist/configuration-lifetime.js.map +1 -0
  39. package/dist/device-id.d.ts +7 -0
  40. package/dist/device-id.d.ts.map +1 -0
  41. package/dist/device-id.js +84 -0
  42. package/dist/device-id.js.map +1 -0
  43. package/dist/index.d.ts +209 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +1459 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/main.d.ts +3 -0
  48. package/dist/main.d.ts.map +1 -0
  49. package/dist/main.js +126 -0
  50. package/dist/main.js.map +1 -0
  51. package/dist/managed-workspace.d.ts +33 -0
  52. package/dist/managed-workspace.d.ts.map +1 -0
  53. package/dist/managed-workspace.js +149 -0
  54. package/dist/managed-workspace.js.map +1 -0
  55. package/dist/pi-runtime.d.ts +24 -0
  56. package/dist/pi-runtime.d.ts.map +1 -0
  57. package/dist/pi-runtime.js +156 -0
  58. package/dist/pi-runtime.js.map +1 -0
  59. package/dist/readiness.d.ts +12 -0
  60. package/dist/readiness.d.ts.map +1 -0
  61. package/dist/readiness.js +66 -0
  62. package/dist/readiness.js.map +1 -0
  63. package/dist/resource-lock.d.ts +11 -0
  64. package/dist/resource-lock.d.ts.map +1 -0
  65. package/dist/resource-lock.js +74 -0
  66. package/dist/resource-lock.js.map +1 -0
  67. package/dist/session-index.d.ts +41 -0
  68. package/dist/session-index.d.ts.map +1 -0
  69. package/dist/session-index.js +99 -0
  70. package/dist/session-index.js.map +1 -0
  71. package/dist/user-service.d.ts +21 -0
  72. package/dist/user-service.d.ts.map +1 -0
  73. package/dist/user-service.js +224 -0
  74. package/dist/user-service.js.map +1 -0
  75. package/package.json +46 -4
package/README.md CHANGED
@@ -1,3 +1,488 @@
1
- # Temporary Holding Version
1
+ # MirrorX device client
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ This package is a small Node.js bridge for a user-owned Pi, Codex, or WorkBuddy executor.
4
+ It makes outbound requests to MirrorX; the device needs no inbound connection.
5
+ The package has no runtime dependencies on Pi Agent. With
6
+ `MIRRORX_DEVICE_SERVER_URL` set by the Pi distribution, Pi uses its existing
7
+ IAM login to register in every MirrorX company where the linked IAM subject is
8
+ already a member. No second browser approval is needed. The MirrorX server
9
+ uses its existing `IAM_INTERNAL_BASE_URL` to verify the login token through
10
+ IAM userinfo, and `IAM_PUBLIC_BASE_URL` (or `IAM_BASE_URL`) as the issuer used
11
+ by existing IAM account links. No separate device userinfo URL is needed.
12
+ Email is not an identity fallback.
13
+ The Amaster desktop app is the ordinary entry point and carries this library in its own runtime; users do not install Node/npm. In MirrorX Settings → Runtime and integrations, select a connected computer, name the employee and choose Amaster, Codex or WorkBuddy. Automatic managed workspaces are the default and require no second native confirmation. Custom Codex/WorkBuddy project directories retain native confirmation of the exact path; the `amaster://device/connect?intent=UUID` link contains no credential. Login creates/restores only the personal twin. Additional employees consume explicit authorized enrollment intents.
14
+
15
+ The standalone CLI connects a **computer only**. In the web connection dialog,
16
+ generate a one-use computer pairing code, then run `setup --server URL`. No tool,
17
+ workspace, personal twin or other employee is created during pairing. Codes last
18
+ ten minutes and can be generated before a computer is online. Already connected
19
+ computers can be selected directly without another code.
20
+
21
+ ## Online-only operation and recovery
22
+
23
+ V1 submits work only while online and ready. Known offline/not-ready submissions retain browser drafts and send no new task/run request. Reconnection refreshes state; the user must submit again. Existing accepted tasks/timers can still fail while the computer sleeps. There is no offline queue or automatic scheduling in this connector.
24
+
25
+ `setup` installs a current-user LaunchAgent, Windows task or systemd user service. It asks before a foreground-only fallback on unsupported systems. `service stop` requests drain through a matching private instance record; uncertain commands remain for reconciliation. Repeated start does not create another owned supervisor, and unknown service ownership is rejected. OS service installations still require real per-platform validation. App background and login-start options are separate from standalone service ownership.
26
+
27
+ Normal restore requires the original owner, safe disconnect and no unsettled commands/leases. Permanent loss is a distinct MirrorX recovery permission with an exact command-set confirmation, immutable receipt and generation fence. It abandons results, never claims the old process stopped, preserves usage and pauses affected task trees until manual action. Restoring retains the original employee/history and starts new native sessions. Lost index repair requires a valid narrow session; completely lost references require an explicit existing-Pi/new-computer choice.
28
+
29
+ Protocol and distribution metadata are available at `/api/devices/compatibility`. Desktop and enabled npm channels have independent exact versions and integrity/provenance checks. Missing published npm does not block the app. Unsupported protocol stops fresh claims/enrollment, retaining identities and allowing authenticated original in-flight claims to settle. Never delete identity files to fix an upgrade requirement.
30
+
31
+ ## Standalone installation and updates
32
+
33
+ After the first public release, install this connector without the Pi Agent app:
34
+
35
+ ```sh
36
+ npm install -g @amaster.ai/mirrorx-device-client@VERSION
37
+ mirrorx-device --version
38
+ mirrorx-device --help
39
+ ```
40
+
41
+ Replace `VERSION` with a published version number.
42
+ Generate a computer pairing code in MirrorX and run the commands below. Install
43
+ and sign in to the chosen tool later, before assigning work to an employee. Node.js22.19+ is required. Update
44
+ by selecting the new version in the same npm command after your own connector's
45
+ tasks finish and the bridge exits; restart it afterward. Publishing a new version never updates or stops
46
+ existing bridge processes automatically. Keep the old version available while
47
+ reconciling any interrupted work.
48
+
49
+ The release-branch/manual npm release workflow, maintainer credential setup, exact-tarball
50
+ native smoke and rollback instructions are in
51
+ [the release runbook](../../docs/releasing-mirrorx-device-client.md). Before the
52
+ first successful publication, maintainers can use the verified tgz attached to
53
+ a dry-run workflow; a new workflow file in a Draft PR is not a published npm version.
54
+
55
+ ```sh
56
+ mirrorx-device setup --server https://YOUR-MIRRORX
57
+ mirrorx-device service status
58
+ mirrorx-device doctor --kind codex
59
+ ```
60
+
61
+ `setup` prompts for the code without putting it in shell history. A narrow
62
+ computer credential is stored in the owner-only identity directory. The connector
63
+ reports presence every 15 seconds with a 60-second fenced lease, even with zero
64
+ employees or while a tool is unready. The supervisor discovers new computer
65
+ connections without requiring an existing service to be replaced.
66
+
67
+ Create employees explicitly in MirrorX after pairing. This package supports
68
+ managed Codex/WorkBuddy workspaces on macOS (Seatbelt confinement); it does not
69
+ bundle the Amaster Pi runtime. Other platforms can connect and report presence,
70
+ but must not claim managed-workspace support. The desktop app supplies Amaster's
71
+ runtime. Tool execution retains its existing journals, approval gates and local
72
+ readiness checks. A computer credential cannot execute tasks directly.
73
+
74
+ Legacy `pair --kind ... --workspace ...` remains an employee-bound maintainer
75
+ command; its codes are deliberately incompatible with computer-only pairing.
76
+ `setup` rejects those legacy flags rather than silently creating an employee.
77
+ Pi sign-out stops only app-owned bridges and clears their narrow credentials, retaining non-secret binding references. Natural expiry/normal rotation may renew the same Pi binding through the current IAM identity. Explicit revocation, membership loss and protocol rejection never authorize automatic re-enrollment.
78
+
79
+ The bridge journals each claim and durably records a launch intent before
80
+ spawning the process. A verified pre-spawn claim can retry a failed start;
81
+ an already launched or older ambiguous journal entry requires reconciliation.
82
+ A private random UUID lives in an OS-user identity directory independent of the overridable task state/install directory. No hardware identifiers are read. An atomic local lock covers canonical server origin, authenticated principal, profile, company and executor; it never signals unknown owners. MirrorX also leases the active bridge
83
+ instance in PostgreSQL and stores the instance that started each command, so
84
+ a copied configuration on another computer cannot start the same command.
85
+ The bridge includes up to 32 locally in-flight command IDs in each poll,
86
+ including commands awaiting output transfer or completion acknowledgement. MirrorX
87
+ can then deliver queued control commands while a long process runs; a command
88
+ omitted from this list or owned by an older bridge still blocks new claims until
89
+ it is reconciled. Older MirrorX servers ignore the optional poll parameter.
90
+ Acknowledged terminal entries are removed from the local journal. The client
91
+ spools output segments under `~/.mirrorx-device/output-outbox/` and uploads
92
+ their bytes directly to S3 using short-lived URLs while the Run is active.
93
+ PostgreSQL keeps the segment cursor and transfer metadata; the plugin verifies
94
+ each segment before forwarding it to the Run log. A slow upload temporarily pauses
95
+ stdout/stderr reads; if transfer remains unavailable, the process continues
96
+ and output stays in the local outbox for reconnect. There is no task-wide
97
+ output byte limit or output-volume kill switch. If the
98
+ process finished before an acknowledgement was lost, the bridge replays its
99
+ outbox on reconnect. A process crash with uncertain side effects still needs
100
+ reconciliation. If local output staging fails, the bridge stops the process and
101
+ reports a failed command with an explicit output-loss message; it cannot safely
102
+ replay missing bytes. On macOS and Linux, individual stop requests signal the
103
+ managed process group, then escalate to SIGKILL after three seconds if any
104
+ member remains and the root is still owned. If the root exits before escalation,
105
+ the bridge observes for a bounded grace, then requires reconciliation; it cannot
106
+ safely signal an unowned/reused PID. On Windows, the bridge uses `taskkill /T /F` for the process
107
+ tree. A stop is not reported complete until that operation has finished; a
108
+ failed tree stop leaves the command for reconciliation. If the Run
109
+ controller is interrupted, the device keeps running
110
+ and MirrorX acknowledges later ordinary log segments without uploading them.
111
+ The final command result carries a summary budgeted in UTF-8 bytes across both
112
+ output streams so the whole terminal request stays below the host's one-megabyte
113
+ limit. The device plugin removes
114
+ forwarded temporary objects and expires orphaned output after a short grace
115
+ period; it does not keep a second full stdout archive. The host's live log
116
+ notification is best effort and has no durable acknowledgement. Real multi-Pod/OSS
117
+ validation remains before production use.
118
+
119
+ Pi Billing commands use the device's configured model catalog together with
120
+ the per-run routing supplied by MirrorX. The Pi server passes its effective
121
+ configuration directory and default model to the bridge. Standalone Pi bridges
122
+ use `PI_CODING_AGENT_DIR`, falling back to `~/.pi/agent`; that directory must
123
+ contain a configured `amaster` model catalog. A routing-only provider is not a
124
+ catalog. The bridge builds a private, disposable command configuration, retains
125
+ explicit model selection, and only fills an omitted model from the device's
126
+ configured default. Local provider credentials and model-level routing are
127
+ excluded from the merge so the organization Billing lease remains authoritative.
128
+ The user's configuration and the server-staged files are left unchanged.
129
+
130
+ For Billing commands, an omitted model can reuse the configured default model ID
131
+ from another provider only when that ID exists in the `amaster` Billing catalog.
132
+ An unmatched default is an explicit configuration failure, not an arbitrary model
133
+ selection. Missing values for `--model`, `-m`, or `--provider` are rejected before
134
+ injection. Only `models.json` and staged `settings.json` are materialized; caches,
135
+ sessions, local OAuth/authHeader settings and provider-level compat are not copied.
136
+ The injected Billing authentication remains authoritative. Preparation retries
137
+ filesystem resource errors at most three times before launching a process;
138
+ deterministic invalid configuration is not retried.
139
+
140
+ On POSIX, a Billing command remains active while observing managed descendants
141
+ for up to 30 seconds starting at the root's `exit` event. Root exit and stdio
142
+ `close` are tracked separately: descendants inheriting stdout/stderr cannot keep
143
+ the observation deadline or abort cleanup from running. Normal completion still
144
+ waits for output EOF within that deadline. Unconfirmed completion closes only
145
+ local pipe handles, retains the running journal without ACK, and applies the
146
+ platform cleanup policy below. Once the root has exited, this is strictly
147
+ observation: no new stop signal is sent to that PID/group, including on abort or
148
+ deadline. An already-started stop may escalate only while the root is still live;
149
+ otherwise it also switches to observation/reconciliation. This trades automatic
150
+ termination of surviving tools for protection against signalling a reused PID.
151
+ Stop/reap observation is bounded (three-second TERM and KILL grace, followed by
152
+ at most five seconds of reap grace; abort skips the extra reap wait).
153
+
154
+ If exit cannot be confirmed, no completion ACK is sent; the running journal
155
+ requires manual reconciliation and a replacement bridge refuses to relaunch it.
156
+ Execution timers/active ownership are cleared and shutdown releases its bridge lock.
157
+ Unconfirmed execution retains its private Billing directory on both POSIX and
158
+ Windows; the running journal records `configurationDirectory` and an ownership
159
+ token, never the credential contents.
160
+ A surviving tool may still read those files. Retention does not extend the
161
+ server's Billing lease or establish successful completion. Normal completion
162
+ persists cleanup evidence and removes private configuration before replacing the
163
+ running record or sending the completion ACK. Startup and subsequent polls scan
164
+ all journal configuration references, even if the server no longer returns the command.
165
+ POSIX reconciliation keeps passive, unreferenced pipe readers: actual stdout and
166
+ stderr EOF after root exit is persisted in a private ownership marker. Once the
167
+ original process group is also absent, a live desktop reclaims the directory;
168
+ a later bridge can consume the same evidence. Local stream destruction, EPERM,
169
+ an elapsed timeout or PID reuse never establishes exit. These readers/timers do
170
+ not keep a closing CLI alive and send no process signals. Windows uses
171
+ a foreground-command contract: tools must not outlive a normally completed Pi
172
+ root. Normal root close is not evidence of native Windows job/tree containment.
173
+ If the application exits before observing EOF, an old journal has no ownership
174
+ evidence, or a Windows tree stop fails, automatic reclamation cannot prove safety.
175
+ After stopping the bridge and confirming **all tools for that command stopped**, run:
176
+
177
+ ```sh
178
+ mirrorx-device reclaim-config --kind pi --company COMPANY_ID --command COMMAND_ID --confirm-stopped
179
+ ```
180
+
181
+ This local recovery command consumes only that journal entry's private temporary
182
+ directory; it validates the canonical absolute path, location/ownership and token when present,
183
+ refuses an active company/executor bridge lock, and preserves the running state
184
+ and claim for reconciliation. It never acknowledges or relaunches the command.
185
+ It also supports pre-token retained entries. No global `/tmp` sweep is performed.
186
+ Escaped process groups are outside managed ownership. Real Windows runtime
187
+ behavior still needs platform E2E verification.
188
+
189
+ Only the MirrorX managed Billing profile is supported here: its producer writes
190
+ models.json/settings.json, not auth.json, local extensions, or provider state.
191
+ This restriction applies to the command's server-staged `PI_CODING_AGENT_DIR`,
192
+ not the device's `PiDeviceRuntime.configDir`. Extra files, credential caches and
193
+ MCP configuration in the local profile do not reject a command or get copied.
194
+ Any additional staged entry is rejected before launch, regardless of which current
195
+ or future settings key references it. The bridge does not classify Pi package
196
+ sources or interpret resource settings; Pi owns those semantics. Arbitrary Pi
197
+ profiles require a separate resource-transfer contract; they are not cloned.
198
+ Missing settings.json stays absent in the command directory.
199
+ EACCES and ENOSPC are reported immediately; transient filesystem failures retain the
200
+ bounded retry. Materialized files are created mode0600 in a new private directory.
201
+
202
+ When the supplied default model ID is absent, the bridge also checks the device's
203
+ explicit settings default against the merged catalog. If neither is available,
204
+ preparation fails with a configuration error; it does not silently select an
205
+ arbitrary model. Injected provider `api` takes precedence over the local fallback
206
+ because it describes the Billing endpoint protocol. Provider-level `compat` is
207
+ not a supported Pi field; supported model-level `compat` is merged.
208
+
209
+ A catalog-declared dash-prefixed model can be passed as `--model -odd-id`;
210
+ the bridge normalizes `-m`, model/provider equals syntax to Pi's native
211
+ `--model value` / `--provider value` form. Repeated options keep the last value;
212
+ arguments after `--` stay unchanged. Informational commands leave
213
+ model/provider option validation to Pi and do not inject defaults.
214
+ Malformed staged settings retain the filename and `invalid JSON` diagnosis without
215
+ including JSON content or credentials.
216
+
217
+ Stop confirmation has separate platform paths in one completion-state helper.
218
+ An unconfirmed stop emits one reconciliation notice including its failure reason.
219
+ Windows accepts only its own stop-task outcome: a failed/pending stop after root
220
+ exit requires reconciliation and retains configuration, never a POSIX negative-PID
221
+ probe to infer success. POSIX alone performs bounded group observation/reaping.
222
+ `active` owns live execution; `inFlight` continues through output/ACK transfer.
223
+ A late stop during that transfer must not signal a reaped process. Root success
224
+ while owned tools remain alive is not full-command completion; reconciliation
225
+ remains required after the observation deadline. Help/version/list commands do
226
+ not start model execution; ordinary execution receives the validated device model.
227
+ Provider-level compat is stripped from both local and injected input, while
228
+ supported model-level compat from both sources remains merged.
229
+
230
+ ### Embedded desktop runtime
231
+
232
+ Desktop passes its app-owned Node and Pi launcher via `PiDeviceRuntime.executable`
233
+ for recognized `pi` / `pi.cmd` / `pi.exe` commands, including path forms. No global Pi installation or shell
234
+ interpolation is needed. Other commands, including the existing Codex bridge,
235
+ retain their configured executable. Desktop restores only the current IAM
236
+ subject, newly enrolled companies and configured service (`expectedBaseUrl`).
237
+ A failed authenticated enrollment does not reactivate stale stored companies.
238
+ Transport errors, timeouts, HTTP 408/429 and 5xx responses retry automatically
239
+ with exponential delay from one second up to 30 seconds. Sign-out or a new
240
+ identity cancels the pending retry; successful empty enumeration and permanent
241
+ authentication/configuration failures do not restore old pairings or retry.
242
+ A 200 response with only transient company-registration failures also retries.
243
+ Once any company succeeds, an unsuccessful subset does not restart the whole
244
+ bridge set; the client's bounded per-company retries remain in effect.
245
+
246
+ `MIRRORX_DEVICE_HOME` optionally selects the credential/journal root. Desktop
247
+ sets this to its profile's system directory; the standalone CLI default remains
248
+ `~/.mirrorx-device`. Keep different services/profiles in separate roots. This
249
+ override does not migrate or erase existing state.
250
+
251
+ ### Application-owned company recovery
252
+
253
+ `enrollMirrorxDevice` accepts optional `companyIds` and `signal`. Scoped requests
254
+ register only those companies, so recovery cannot rotate healthy companies'
255
+ sessions. Cancellation reaches HTTP requests and short retry delays. Device
256
+ RPC 401/403 terminates the affected loop after draining owned execution and
257
+ releasing its bridge lock; other companies keep running. Network failures retain
258
+ the existing 3s reconnect loop. `onConnectionState` reports actual connection and
259
+ retry/failure transitions, separately from command reconciliation diagnostics.
260
+
261
+ The embedded lifecycle holds one bridge per company, retries transient failed
262
+ registrations with 1s–30s backoff and refreshes same-identity IAM credentials.
263
+ Its existing HTTP status/reconnect entry includes device health and recovers
264
+ failed companies without stopping healthy ones. Normal application quit aborts
265
+ bridges and pending registration/retries; this is not a persistent daemon. Old
266
+ Employee twin identities, tasks and workspaces are not migrated or adopted, and
267
+ legacy PID/status files do not gate the new device connection.
268
+
269
+ ## WorkBuddy on macOS (experimental)
270
+
271
+ Install and sign in to WorkBuddy first, then select **WorkBuddy (macOS)** in
272
+ MirrorX Profile → Connect a device. Pair and run this same standalone client:
273
+
274
+ ```sh
275
+ mirrorx-device pair --server https://your-mirrorx.example --kind codebuddy --code YOUR_CODE --workspace "/path/to/workspace"
276
+ mirrorx-device run --kind codebuddy
277
+ ```
278
+
279
+ Requires Node 22.19+ and the server's built-in `codebuddy_local` adapter
280
+ (`@paperclipai/adapter-workbuddy-local`, registered like Codex and Pi).
281
+ WorkBuddy is another executor kind in this same lightweight connector; no
282
+ separate WorkBuddy connector package or external server adapter installation
283
+ is required. The client uses
284
+ WorkBuddy's built-in `/Applications/WorkBuddy.app/Contents/Resources/app.asar.unpacked/cli/bin/codebuddy`;
285
+ a different executable may be selected **locally** with `MIRRORX_WORKBUDDY_CLI`.
286
+ The server cannot select a desktop-auth source directory. Windows desktop-auth
287
+ reuse is refused until native ACL and stop qualification exists. No API-key
288
+ fallback, automatic WorkBuddy installation, or account migration is provided.
289
+
290
+ Only actual `codebuddy` launches receive a private HOME/config and an opaque
291
+ copy of the OS user's encrypted desktop-auth file. The source is never decrypted,
292
+ logged, uploaded, or written back. A logout marker, symlink, unsafe ownership,
293
+ permissions, or changing file identity refuses startup. Each launch checks CLI
294
+ flags and current auth presence; a previous model success is not permanent login
295
+ proof. Vendor keys, tokens, base URLs, NODE_OPTIONS, and personal configuration
296
+ are excluded by rebuilding the final child environment. Local HTTP_PROXY,
297
+ HTTPS_PROXY, ALL_PROXY and lowercase equivalents are retained and redacted before
298
+ output spooling. Pi/Codex retain their existing environment behavior.
299
+
300
+ Workspaces containing device state, desktop auth, the selected CLI, or run-private
301
+ configuration are refused. The qualified profile exposes adapter-owned workspace file MCP tools and the current run's exact
302
+ authorized MCP tools. Native Read/Write/Edit/Glob/Grep are disabled after a real probe showed Read can access a workspace-external canary. File operations go through the adapter MCP with path/link and size checks. Bash, Skills, subagents, personal MCP, cron and desktop
303
+ features are unavailable. Every tool call also passes a private `PreToolUse`
304
+ positive gate: only this run's exact MCP names are allowed, including rejection
305
+ of unknown native tools or MCP names. `dontAsk` prevents approval prompts; it
306
+ is insufficient on its own because safe native reads can be auto-approved.
307
+ The explicit native deny list remains defense in depth and keeps these tools
308
+ out of the advertised surface; `--tools` alone does not hide them. Workspaces
309
+ must not overlap device state, the desktop-auth tree, or the WorkBuddy app bundle
310
+ in either direction. Model selection uses CodeBuddy's native account
311
+ default unless the employee explicitly selects a model; this mode does not
312
+ use MirrorX Billing. Usage is recorded when supplied; monetary cost is unknown.
313
+
314
+ Session transcripts stay in the client state directory, outside the workspace,
315
+ scoped by service/company/employee/binding/real cwd/tool authorization. The
316
+ connector saves a successful native transcript **before** deleting private auth
317
+ and MCP configuration, then restores it before the next spawn. Only the selected
318
+ transcript is saved; no auth or MCP credential files are copied into session
319
+ storage. A transcript containing current MCP/proxy credential values is rejected.
320
+ Both a workspace lock and a session lock reject overlap. An unsettled attempt fences the previous
321
+ checkpoint rather than silently resuming stale state. CodeBuddy 2.137.1 receives
322
+ new/resume prompts as positional arguments because stdin-only resume did not
323
+ process the new prompt in the real contract probe.
324
+
325
+ WorkBuddy 2.137.1 encodes the launch directory into one native session directory
326
+ component. For long managed task paths, the connector launches in a short,
327
+ command-private directory instead. MCP tools still use the original authorized
328
+ task workspace; session identity and workspace locks keep that original path.
329
+ Only the WorkBuddy executor can request this private launch directory. The
330
+ sandbox still denies neighboring task workspaces. Checkpoints are collected
331
+ from, and restored to, the actual private native-session bucket.
332
+
333
+ Preparation records private-profile ownership in the command journal before
334
+ acquiring workspace/session locks or reading authentication. Lock publication
335
+ includes its owner atomically. A preparation crash retains this command-scoped
336
+ recovery reference: stop the bridge and use `reclaim-config --kind codebuddy`
337
+ with confirmed-stop evidence before retrying. Managed bindings must also pass
338
+ `--company COMPANY_ID --binding BINDING_ID --command COMMAND_ID --confirm-stopped`
339
+ so reclamation selects and locks the exact binding journal. An unknown binding
340
+ fails rather than falling back to another connection. A retry never overwrites the
341
+ retained reference. If preparation was explicitly cancelled before any business
342
+ process started, the bridge rolls back only that preparation's fence and keeps
343
+ the previous successful checkpoint, so resume does not require `reset-session`.
344
+
345
+ Cancellation uses the existing owned process-tree contract, not WorkBuddy GUI
346
+ shutdown. After verified shutdown, a CLI that handles the stop signal and exits
347
+ zero is still acknowledged as stopped; it is not required to emit a successful
348
+ provider result or create a success checkpoint. Unknown descendant shutdown
349
+ continues to require reconciliation. Unconfirmed exit or failed transcript persistence retains the running
350
+ journal/profile and requires reconciliation; it is never replayed. CodeBuddy
351
+ profiles with incomplete shutdown/session evidence require explicit `reclaim-config`
352
+ after independently confirming all tools stopped. Reclamation does not turn an
353
+ unsettled native checkpoint into a resumable session.
354
+
355
+ The protocol readiness check captures help with a private regular file descriptor:
356
+ CodeBuddy 2.137.1 may exit before pipe output drains and omit help tail flags.
357
+ A successful version check alone is insufficient. Only version **2.137.1** is
358
+ qualified; other versions fail before task launch even if their help flags
359
+ match. Qualify the permission contract again before updating this pin.
360
+ See CodeBuddy's [PreToolUse contract](https://www.codebuddy.cn/docs/cli/hooks)
361
+ and [permission-mode semantics](https://www.codebuddy.cn/docs/cli/permission-modes).
362
+ The CLI remains trusted local software; these controls are not an OS sandbox.
363
+
364
+ A clean process exit requires exactly one matching successful result before a
365
+ checkpoint can be saved. Exactly one matching known error result (including
366
+ `error_during_execution` with `is_error: true`) is a confirmed failed command,
367
+ even when CodeBuddy exits zero. After confirming the process tree stopped, the
368
+ bridge cleans its private launch configuration and acknowledges failure. It does
369
+ not checkpoint or replay that failed native session: the unsettled session fence
370
+ remains. Missing, malformed, unknown or duplicate results still require command
371
+ reconciliation; exit zero alone cannot ACK success. A provider 401 needs normal
372
+ WorkBuddy account reauthentication and is never repaired by relabeling the run.
373
+
374
+ ### Discard a fenced native session
375
+
376
+ First reconcile the original command in MirrorX and independently confirm its
377
+ tools stopped. Stop this company's bridge. If a private configuration remains,
378
+ run the existing `reclaim-config --kind codebuddy` operation first. Reclaiming
379
+ configuration alone deliberately leaves the native-session fence in place.
380
+ The fenced-session error supplies `SESSION_KEY` and `SESSION_ID`:
381
+
382
+ ```sh
383
+ mirrorx-device reset-session --kind codebuddy --company COMPANY_ID \
384
+ --session-key SESSION_KEY --session SESSION_ID --confirm-stopped
385
+ ```
386
+
387
+ This explicit reset takes the company bridge and workspace/session locks,
388
+ validates the stored company/identity, and deletes the old checkpoint **before**
389
+ removing the fence. It neither changes the command journal/receipt nor retries
390
+ business work. Resume of the discarded context fails; start a fresh session.
391
+ Existing fenced sessions acquire their recovery identity when preparation is
392
+ retried; do not manually delete state or locks to bypass ownership checks.
393
+
394
+ New WorkBuddy pairings/IAM enrollments default to the sibling
395
+ `<MIRRORX_DEVICE_HOME>-workspaces/<company>/codebuddy` directory (normally
396
+ `~/.mirrorx-device-workspaces/...`). Pi/Codex defaults stay inside their existing
397
+ workspaces directory. No files are moved. If an older WorkBuddy config selects
398
+ a workspace inside the state/auth tree, pair again with an explicit safe
399
+ `--workspace` and move your project files deliberately; the launch guard will
400
+ not weaken itself to accept the old location.
401
+
402
+ ### Managed desktop workspaces
403
+
404
+ In MirrorX **运行与集成**, select an online Amaster computer, choose Amaster, Codex or
405
+ WorkBuddy, and name the employee. Additional Amaster employees require the desktop
406
+ carrier to advertise `managedPiEmployees`; older clients show an upgrade requirement. A compatible desktop consumes
407
+ only the signed intent addressed to its authenticated company/profile; no local
408
+ directory picker or second per-employee connection prompt is needed. Login alone
409
+ still connects only the personal twin. Custom project directories retain native confirmation.
410
+
411
+ The desktop derives its workspace namespace from the canonical service,
412
+ principal, profile, company and enrollment identity. Each employee has its own
413
+ binding/config/journal/lock. The environment provider reuses core execution
414
+ workspace IDs (or the exact task ID) for stable task directories and continuation.
415
+ Managed commands validate ownership and real paths before spawn and use native
416
+ filesystem confinement. Current managed qualification is macOS Seatbelt; other
417
+ platforms fail closed and can use explicitly authorized custom workspaces.
418
+ Existing custom bindings keep their paths and legacy resource lock keys.
419
+
420
+ Native Pi/Codex session files are indexed by exact references under the binding's
421
+ private state. History lookup validates the login, service, company, employee and
422
+ source Run, and does not scan other employee workspaces or recreate legacy daemon
423
+ markers. Task attachment, tool/MCP and artifact delivery still use the existing
424
+ MirrorX adapter and task APIs. Connectivity remains optional and appears in Pi
425
+ Settings → MirrorX.
426
+
427
+ Codex cancellation first sends Ctrl-C (`SIGINT`) with a bounded grace period so Codex can interrupt its active turn and stop tool process groups it owns. It then uses the existing termination fallback while the command root is still owned. A forced Codex stop remains unconfirmed and requires reconciliation rather than acknowledging that detached tools have stopped. Pi/WorkBuddy and Windows retain their existing stop paths; a reaped root never authorizes signaling a reused PID.
428
+
429
+
430
+ ### Desktop sign-in restoration
431
+
432
+ After logout, Pi retains the original tool binding and workspace but clears its credential. On sign-in it first reconnects Pi, then refreshes only previously authorized desktop tool sessions on the same account, company, profile and generation. No employee or workspace is recreated. The server rejects explicit revocation, profile transfers, active bridges and unsettled execution. Standalone connectors are not enrolled or restarted by this flow. Tool failures appear in Pi Settings → MirrorX and do not block normal Pi conversations.
433
+
434
+ Local credential clearing precedes the remote sign-out request, including when a committed remote response is lost. Retained stale credentials may recover only from `DEVICE_SESSION_EXPIRED`, `DEVICE_SESSION_ROTATED`, or `DEVICE_SESSION_SIGNED_OUT` through the authenticated Pi session; `DEVICE_REVOKED` remains terminal. An idle bridge disconnect includes its company and exact instance ID so the server can release only that bridge's lease.
435
+
436
+
437
+ ### Independent Amaster employees
438
+
439
+ An additional Amaster employee has a distinct intent-derived binding, managed
440
+ workspace, journal and bridge. Automatic IAM registration excludes those bindings
441
+ and always retains the original personal twin as the computer carrier. Only that
442
+ carrier polls enrollment intents or restores other desktop sessions; its bridge
443
+ runs with `computerCarrierOnly`, while managed Amaster bridges select one binding
444
+ and receive the bundled Pi runtime. This prevents duplicate workers after restart.
445
+
446
+ Managed Pi commands may read only the two desktop-generated runtime launcher
447
+ files in the profile cache, supplied locally by the desktop (never by remote
448
+ command payloads). The containing profile/cache directory remains unreadable;
449
+ these files are read-only under Seatbelt. This supports the owned Pi entrypoint
450
+ without exposing profile credentials or allowing a global runtime installation.
451
+
452
+ The existing company membership, employee-creation permission, hiring approval,
453
+ profile/generation and filesystem-confinement gates also apply to managed Amaster
454
+ employees. Sign-out/sign-in restores their existing references on the original
455
+ computer, without creating employees or replaying tasks. Moving these employees
456
+ to a replacement computer is not supported by the legacy directory-recovery
457
+ protocol; both server and UI reject that path rather than dropping confinement.
458
+
459
+ ### Review hardening: command secrets, logout, and legacy journals
460
+
461
+ Managed Seatbelt reads are restricted to the current task, its exact private command configuration, approved launcher files and installed system/runtime roots. Other system temporary directories are not readable, including through `/var` aliases or task symlinks; required system alias metadata, shell selection and public CA files remain accessible. This policy is local to managed device commands, not the platform sandbox.
462
+
463
+ The task-local `node` launcher resolves to the host process's canonical executable. When Node is installed by a version manager or CI tool cache outside the system roots, only that executable is additionally readable; its parent directories allow metadata lookup but not listing, adjacent-file reads or writes.
464
+
465
+ Explicit enrollment participates in the desktop lifecycle transition and receives its cancellation signal. Logout waits for it before clearing device credentials. A claim response arriving after cancellation retains only a signed-out binding reference and revokes that response's narrow session; cancellation is checked on both sides of credential persistence.
466
+
467
+ Before adopting a legacy company or single-tool journal into the identity-scoped path, the bridge checks its saved configuration against the authenticated binding, company, server, user and generation. It persists the migrated journal before replay/reconciliation and leaves the original evidence file in place. A different binding cannot inherit results or configuration cleanup references.
468
+
469
+ ### Local executable discovery
470
+
471
+ Codex is launched through the canonical path of the selected local executable,
472
+ so Homebrew symlinks do not hide companion binaries such as
473
+ `codex-code-mode-host`. This does not change the selected installation or copy
474
+ authentication from another environment. Managed macOS commands can inspect
475
+ ancestor metadata of existing system read roots (for example `/opt` for
476
+ `/opt/homebrew`) so Python can resolve its runtime; ancestor directory contents
477
+ and neighboring task files remain denied.
478
+
479
+ A finished journal receipt is retried independently of command polling: the server may have committed a terminal result before the response was lost. This retries output/receipt delivery only; it never relaunches the process. Unacknowledged records remain durable, with at least ten seconds between attempt starts per command, shared by poll replay and background recovery (including the first failed completion). The oldest eligible receipt is selected first so a rejected command cannot starve later receipts during long polling; replay always uses the journal’s original claim.
480
+
481
+ During a requested drain, an unacknowledged finished journal is reported as
482
+ requiring reconciliation after attempts already in flight settle. Neither
483
+ background recovery nor poll replay starts a new receipt attempt after drain,
484
+ even when multiple previous attempts overlap and continue failing. The bridge
485
+ retains the result and does not claim a clean drain or re-execute the command.
486
+ Normal startup retries durable receipts for Pi, Codex and WorkBuddy; only a
487
+ successful acknowledgement removes the journal entry. Authorization/server
488
+ failures keep it available for a later normal reconnect.
@@ -0,0 +1,2 @@
1
+ export declare function snapshotWorkbuddyAuth(targetHome: string, sourceHome?: string): void;
2
+ //# sourceMappingURL=codebuddy-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codebuddy-auth.d.ts","sourceRoot":"","sources":["../src/codebuddy-auth.ts"],"names":[],"mappings":"AAeA,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,SAAe,QAiDlF"}
@@ -0,0 +1,80 @@
1
+ /** Opaque desktop-auth copy. Never parses, decrypts, logs, or writes the source. */
2
+ import { constants, closeSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, writeFileSync } from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ const relativeAuth = path.join("Library", "Application Support", "CodeBuddyExtension", "Data", "Public", "auth", "workbuddy-desktop.info");
6
+ // Directory link counts legitimately change when another local test/project creates a child.
7
+ // File link counts remain part of auth identity; ancestors still require matching dev/inode/owner/mode.
8
+ const same = (a, b) => a.dev === b.dev && a.ino === b.ino && a.uid === b.uid && a.mode === b.mode && (!a.isFile() || a.nlink === b.nlink);
9
+ function present(file) {
10
+ try {
11
+ return lstatSync(file);
12
+ }
13
+ catch (error) {
14
+ if (error.code === "ENOENT")
15
+ return null;
16
+ throw error;
17
+ }
18
+ }
19
+ // sourceHome is supplied only by local host code (os.homedir), never a command payload.
20
+ export function snapshotWorkbuddyAuth(targetHome, sourceHome = os.homedir()) {
21
+ if (process.platform !== "darwin" || !process.getuid)
22
+ throw new Error("WorkBuddy desktop authentication is qualified only on macOS");
23
+ const source = path.join(sourceHome, relativeAuth);
24
+ const loggedOut = `${source}.logged-out`;
25
+ if (present(loggedOut))
26
+ throw new Error("Sign in to WorkBuddy before starting a task");
27
+ const chain = [];
28
+ let current = path.parse(source).root;
29
+ for (const component of ["", ...path.relative(current, source).split(path.sep)]) {
30
+ current = component ? path.join(current, component) : current;
31
+ const entry = present(current);
32
+ const isFile = current === source;
33
+ if (!entry)
34
+ throw new Error("WorkBuddy desktop login is missing");
35
+ if (entry.isSymbolicLink() || (isFile ? !entry.isFile() : !entry.isDirectory())
36
+ || (entry.uid !== process.getuid() && (isFile || entry.uid !== 0))
37
+ || ((entry.mode & 0o022) && !(entry.uid === 0 && !isFile && (entry.mode & 0o1000)))) {
38
+ throw new Error("WorkBuddy authentication path has unsafe ownership, permissions, or links");
39
+ }
40
+ if (isFile && (entry.nlink !== 1 || entry.size <= 0 || entry.size > 4 * 1024 * 1024))
41
+ throw new Error("Invalid WorkBuddy authentication file size");
42
+ chain.push({ file: current, stat: entry });
43
+ }
44
+ const descriptors = [];
45
+ let bytes;
46
+ try {
47
+ for (const { file, stat } of chain) {
48
+ const fd = openSync(file, constants.O_RDONLY | constants.O_NOFOLLOW | (file === source ? 0 : constants.O_DIRECTORY));
49
+ descriptors.push(fd);
50
+ const before = fstatSync(fd);
51
+ if (!same(before, stat))
52
+ throw new Error("WorkBuddy authentication changed during validation");
53
+ if (file === source) {
54
+ bytes = readFileSync(fd);
55
+ const after = fstatSync(fd);
56
+ if (!same(before, after) || before.size !== after.size || before.mtimeMs !== after.mtimeMs || bytes.length !== before.size) {
57
+ throw new Error("WorkBuddy authentication changed during snapshot");
58
+ }
59
+ }
60
+ }
61
+ for (const { file, stat } of chain) {
62
+ const after = lstatSync(file);
63
+ if (after.isSymbolicLink() || !same(stat, after))
64
+ throw new Error("WorkBuddy authentication path changed during snapshot");
65
+ }
66
+ if (present(loggedOut))
67
+ throw new Error("WorkBuddy logged out during snapshot");
68
+ if (!bytes)
69
+ throw new Error("WorkBuddy authentication snapshot is empty");
70
+ const target = path.join(targetHome, relativeAuth);
71
+ mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
72
+ writeFileSync(target, bytes, { flag: "wx", mode: 0o600 });
73
+ }
74
+ finally {
75
+ bytes?.fill(0);
76
+ for (const fd of descriptors.reverse())
77
+ closeSync(fd);
78
+ }
79
+ }
80
+ //# sourceMappingURL=codebuddy-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codebuddy-auth.js","sourceRoot":"","sources":["../src/codebuddy-auth.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAc,MAAM,SAAS,CAAC;AACnI,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,wBAAwB,CAAC,CAAC;AAC3I,6FAA6F;AAC7F,wGAAwG;AACxG,MAAM,IAAI,GAAG,CAAC,CAAQ,EAAE,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;AACxJ,SAAS,OAAO,CAAC,IAAY;IAC3B,IAAI,CAAC;QAAC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;IAAC,CAAC;IAC/B,OAAO,KAAK,EAAE,CAAC;QAAC,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAAC,MAAM,KAAK,CAAC;IAAC,CAAC;AACrG,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,qBAAqB,CAAC,UAAkB,EAAE,UAAU,GAAG,EAAE,CAAC,OAAO,EAAE;IACjF,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACrI,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,GAAG,MAAM,aAAa,CAAC;IACzC,IAAI,OAAO,CAAC,SAAS,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACvF,MAAM,KAAK,GAAyC,EAAE,CAAC;IACvD,IAAI,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;IACtC,KAAK,MAAM,SAAS,IAAI,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAChF,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAG,OAAO,KAAK,MAAM,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAClE,IAAI,KAAK,CAAC,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;eAC1E,CAAC,KAAK,CAAC,GAAG,KAAK,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;eAC/D,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YACtF,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;QAC/F,CAAC;QACD,IAAI,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QACpJ,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,IAAI,KAAyB,CAAC;IAC9B,IAAI,CAAC;QACH,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,CAAC;YACnC,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,UAAU,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC;YACrH,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACrB,MAAM,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;YAC7B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;YAC/F,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;gBACpB,KAAK,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;gBACzB,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;gBAC5B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC;oBAC3H,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;gBACtE,CAAC;YACH,CAAC;QACH,CAAC;QACD,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,KAAK,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAC7H,CAAC;QACD,IAAI,OAAO,CAAC,SAAS,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAChF,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACnD,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAClE,aAAa,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5D,CAAC;YAAS,CAAC;QACT,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QACf,KAAK,MAAM,EAAE,IAAI,WAAW,CAAC,OAAO,EAAE;YAAE,SAAS,CAAC,EAAE,CAAC,CAAC;IACxD,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { ConfigurationReference } from "./configuration-lifetime.js";
2
+ /** Locks fence both native-session mutation and workspace writes, including crash recovery. */
3
+ export declare function acquireCodebuddyLocks(stateRoot: string, keys: string[], recovery?: {
4
+ directory: string;
5
+ persist: () => Promise<void>;
6
+ }): Promise<{
7
+ release: () => Promise<void>;
8
+ save: (directory: string) => Promise<void>;
9
+ }>;
10
+ /** Invoked only after configuration ownership and stopped evidence have been validated. */
11
+ export declare function reclaimCodebuddyLocks(reference: ConfigurationReference, stateRoot: string): Promise<void>;
12
+ //# sourceMappingURL=codebuddy-locks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codebuddy-locks.d.ts","sourceRoot":"","sources":["../src/codebuddy-locks.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAG1E,+FAA+F;AAC/F,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,EAAE;IACxF,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;;sBAqCuB,MAAM;GAI7B;AAED,2FAA2F;AAC3F,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,sBAAsB,EAAE,SAAS,EAAE,MAAM,iBAkB/F"}