borgmcp 2.14.2 → 2.15.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.
- package/dist/agent-runtime.d.ts +5 -0
- package/dist/agent-runtime.d.ts.map +1 -1
- package/dist/agent-runtime.js +14 -0
- package/dist/agent-runtime.js.map +1 -1
- package/dist/assimilate-cmd.d.ts +3 -0
- package/dist/assimilate-cmd.d.ts.map +1 -1
- package/dist/assimilate-cmd.js +33 -7
- package/dist/assimilate-cmd.js.map +1 -1
- package/dist/assimilate-deps.d.ts.map +1 -1
- package/dist/assimilate-deps.js +17 -3
- package/dist/assimilate-deps.js.map +1 -1
- package/dist/claude.d.ts.map +1 -1
- package/dist/claude.js +2 -1
- package/dist/claude.js.map +1 -1
- package/dist/cleanup-cmd.d.ts.map +1 -1
- package/dist/cleanup-cmd.js +2 -2
- package/dist/cleanup-cmd.js.map +1 -1
- package/dist/cli-tool-approval.d.ts.map +1 -1
- package/dist/cli-tool-approval.js +3 -2
- package/dist/cli-tool-approval.js.map +1 -1
- package/dist/codex-remote.d.ts.map +1 -1
- package/dist/codex-remote.js +4 -3
- package/dist/codex-remote.js.map +1 -1
- package/dist/config-utils.d.ts +16 -1
- package/dist/config-utils.d.ts.map +1 -1
- package/dist/config-utils.js +176 -17
- package/dist/config-utils.js.map +1 -1
- package/dist/credential-paths.d.ts.map +1 -1
- package/dist/credential-paths.js +2 -3
- package/dist/credential-paths.js.map +1 -1
- package/dist/cubes.js +2 -2
- package/dist/cubes.js.map +1 -1
- package/dist/foreign-path-reminder.d.ts +12 -0
- package/dist/foreign-path-reminder.d.ts.map +1 -0
- package/dist/foreign-path-reminder.js +122 -0
- package/dist/foreign-path-reminder.js.map +1 -0
- package/dist/launch-access.d.ts +22 -0
- package/dist/launch-access.d.ts.map +1 -0
- package/dist/launch-access.js +27 -0
- package/dist/launch-access.js.map +1 -0
- package/dist/launch-all-deps.d.ts.map +1 -1
- package/dist/launch-all-deps.js +2 -2
- package/dist/launch-all-deps.js.map +1 -1
- package/dist/lifecycle-log-guard.js +2 -2
- package/dist/lifecycle-log-guard.js.map +1 -1
- package/dist/local-server-cursor.js +2 -2
- package/dist/local-server-cursor.js.map +1 -1
- package/dist/opencode-plugin.js +2 -2
- package/dist/opencode-plugin.js.map +1 -1
- package/dist/private-root.d.ts +17 -0
- package/dist/private-root.d.ts.map +1 -1
- package/dist/private-root.js +62 -2
- package/dist/private-root.js.map +1 -1
- package/dist/seat-store.d.ts.map +1 -1
- package/dist/seat-store.js +4 -3
- package/dist/seat-store.js.map +1 -1
- package/dist/seats.d.ts.map +1 -1
- package/dist/seats.js +2 -2
- package/dist/seats.js.map +1 -1
- package/dist/self-path.d.ts +2 -0
- package/dist/self-path.d.ts.map +1 -1
- package/dist/self-path.js +4 -0
- package/dist/self-path.js.map +1 -1
- package/dist/server-trust.d.ts.map +1 -1
- package/dist/server-trust.js +3 -3
- package/dist/server-trust.js.map +1 -1
- package/dist/setup-confirm.d.ts +1 -1
- package/dist/setup-confirm.js +1 -1
- package/dist/stream-owner.js +2 -2
- package/dist/stream-owner.js.map +1 -1
- package/docs/EXTRACTION_PROVENANCE.md +2 -2
- package/docs/LOCAL_SERVER.md +23 -0
- package/docs/RELEASING.md +123 -1
- package/package.json +2 -1
- package/src/agent-runtime.ts +17 -0
- package/src/assimilate-cmd.ts +49 -7
- package/src/assimilate-deps.ts +21 -3
- package/src/claude.ts +2 -0
- package/src/cleanup-cmd.ts +2 -2
- package/src/cli-tool-approval.ts +3 -2
- package/src/codex-remote.ts +4 -2
- package/src/config-utils.ts +186 -18
- package/src/credential-paths.ts +2 -3
- package/src/cubes.ts +2 -2
- package/src/foreign-path-reminder.ts +141 -0
- package/src/launch-access.ts +40 -0
- package/src/launch-all-deps.ts +2 -2
- package/src/lifecycle-log-guard.ts +2 -2
- package/src/local-server-cursor.ts +2 -2
- package/src/opencode-plugin.ts +2 -2
- package/src/private-root.ts +62 -2
- package/src/seat-store.ts +4 -3
- package/src/seats.ts +2 -2
- package/src/self-path.ts +5 -0
- package/src/server-trust.ts +3 -3
- package/src/setup-confirm.ts +1 -1
- package/src/stream-owner.ts +2 -2
package/docs/RELEASING.md
CHANGED
|
@@ -216,7 +216,7 @@ Never move, replace, reuse, or rerun that tag or workflow. The annotated `v2.14.
|
|
|
216
216
|
successfully published that exact source as `borgmcp@2.14.1`; the same-run artifact report records integrity
|
|
217
217
|
`sha512-VqkOp3jo/KnzgusCk98TFj/U8U7Q/gt8agvIbjiSWrV0lIHnsD+A+7w3C+GMQeyA08KXxRuZDF3No9sDomqMTQ==`.
|
|
218
218
|
Never move, replace, reuse, or rerun that tag or workflow. The next candidate
|
|
219
|
-
uses the unused `v2.
|
|
219
|
+
uses the unused `v2.15.0` identity from a fresh reviewed protected-main commit
|
|
220
220
|
and requires the complete release gate again.
|
|
221
221
|
|
|
222
222
|
## Release Prerequisites
|
|
@@ -340,6 +340,128 @@ Before preparing a candidate, independently verify:
|
|
|
340
340
|
|
|
341
341
|
## Release Workflow
|
|
342
342
|
|
|
343
|
+
### Clean-environment rig lifecycle
|
|
344
|
+
|
|
345
|
+
Every clean-environment verification rig has an explicit identity and an
|
|
346
|
+
explicit end-of-life. Keep filesystem workspaces under the seat's disposable
|
|
347
|
+
`~/.borg/scratch/<seat>/` root, and give each workspace a name beginning with
|
|
348
|
+
`borg-rig-` followed by the seat, purpose, and a unique suffix. Before the
|
|
349
|
+
first command that can create files, anchor the rig as its own npm project:
|
|
350
|
+
|
|
351
|
+
```sh
|
|
352
|
+
BORG_SEAT_LABEL="${BORG_SEAT_LABEL:?set the seat label}"
|
|
353
|
+
BORG_SCRATCH_ROOT="${BORG_SCRATCH_ROOT:-$HOME/.borg/scratch/$BORG_SEAT_LABEL}"
|
|
354
|
+
RIG_NONCE="${RIG_NONCE:-$(date +%Y%m%d%H%M%S)-$$}"
|
|
355
|
+
RIG_ID="borg-rig-${BORG_SEAT_LABEL}-release-${RIG_NONCE}"
|
|
356
|
+
RIG_ROOT="$BORG_SCRATCH_ROOT/$RIG_ID"
|
|
357
|
+
mkdir -p "$RIG_ROOT"
|
|
358
|
+
printf '%s\n' '{"private":true}' > "$RIG_ROOT/package.json"
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
The manifest anchor is required before any `npm install`, `npm update`, or
|
|
362
|
+
other npm command. Without it, npm can walk up from an empty scratch directory
|
|
363
|
+
and write the operator's `package.json` instead of creating project-local
|
|
364
|
+
state. The committed `release:exercise` follows this rule for its temporary
|
|
365
|
+
consumer; other QA scripts and manual rigs must do the same.
|
|
366
|
+
|
|
367
|
+
The system temporary root is shared with the whole machine, so an unbounded
|
|
368
|
+
listing there is not an inspectable cleanup check. Bound that leg by the
|
|
369
|
+
invoking user and the run-window markers, which select directories whose
|
|
370
|
+
timestamps fall within this run window without requiring a prefix inventory.
|
|
371
|
+
New `mkdtemp` prefixes therefore require no documentation change. Keep both
|
|
372
|
+
time predicates and the ownership predicate: removing the run-window bound
|
|
373
|
+
turns a populated shared temp root back into an uninspectable listing.
|
|
374
|
+
|
|
375
|
+
On a machine where several runs share one user account, this listing can
|
|
376
|
+
include another run's live workspace. Removal is scoped to paths created by
|
|
377
|
+
this run; anything else in the output is reported, not deleted. Unrelated
|
|
378
|
+
same-user temporary directories created during the window can therefore
|
|
379
|
+
appear in the result, so never remove a path merely because this listing found
|
|
380
|
+
it.
|
|
381
|
+
|
|
382
|
+
```sh
|
|
383
|
+
TEMP_ROOT="${TMPDIR:-/tmp}"
|
|
384
|
+
TEMP_OWNER="$(id -un)"
|
|
385
|
+
TEMP_SCAN_START="$TEMP_ROOT/.${RIG_ID}-temp-scan-start"
|
|
386
|
+
TEMP_SCAN_END="$TEMP_ROOT/.${RIG_ID}-temp-scan-end"
|
|
387
|
+
|
|
388
|
+
list_recent_owned_temp_rigs() {
|
|
389
|
+
find "$TEMP_ROOT" -mindepth 1 -prune \
|
|
390
|
+
-type d \
|
|
391
|
+
-user "$TEMP_OWNER" \
|
|
392
|
+
-newer "$TEMP_SCAN_START" \
|
|
393
|
+
! -newer "$TEMP_SCAN_END" \
|
|
394
|
+
-print
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
touch "$TEMP_SCAN_START"
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Container-backed rigs use the same `RIG_ID` as the container name and carry
|
|
401
|
+
both labels below. `--rm` is preferred; a runtime without automatic removal
|
|
402
|
+
must remove the exact `RIG_ID` in its cleanup path and retain the labels for a
|
|
403
|
+
bounded sweep:
|
|
404
|
+
|
|
405
|
+
```sh
|
|
406
|
+
RIG_IMAGE="${RIG_IMAGE:?set the rig image}"
|
|
407
|
+
docker run --rm \
|
|
408
|
+
--name "$RIG_ID" \
|
|
409
|
+
--label borg-rig=1 \
|
|
410
|
+
--label "borg-rig-owner=$BORG_SEAT_LABEL" \
|
|
411
|
+
"$RIG_IMAGE"
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Register the exact-target cleanup before launching the rig:
|
|
415
|
+
|
|
416
|
+
```sh
|
|
417
|
+
cleanup_done=0
|
|
418
|
+
cleanup() {
|
|
419
|
+
[ "$cleanup_done" -eq 0 ] || return
|
|
420
|
+
cleanup_done=1
|
|
421
|
+
trap - EXIT HUP INT TERM
|
|
422
|
+
docker container rm --force "$RIG_ID" >/dev/null 2>&1 || true
|
|
423
|
+
rm -rf -- "$RIG_ROOT"
|
|
424
|
+
if [ -e "$TEMP_SCAN_START" ]; then
|
|
425
|
+
touch "$TEMP_SCAN_END"
|
|
426
|
+
list_recent_owned_temp_rigs
|
|
427
|
+
fi
|
|
428
|
+
rm -f -- "$TEMP_SCAN_START" "$TEMP_SCAN_END"
|
|
429
|
+
}
|
|
430
|
+
trap cleanup EXIT
|
|
431
|
+
trap 'exit 129' HUP
|
|
432
|
+
trap 'exit 130' INT
|
|
433
|
+
trap 'exit 143' TERM
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
List before starting and after cleanup. The filters distinguish this seat's
|
|
437
|
+
rigs from unrelated containers on a shared host:
|
|
438
|
+
|
|
439
|
+
```sh
|
|
440
|
+
docker container ls --all \
|
|
441
|
+
--filter label=borg-rig=1 \
|
|
442
|
+
--filter "label=borg-rig-owner=${BORG_SEAT_LABEL:?}" \
|
|
443
|
+
--format '{{.ID}}\t{{.Names}}\t{{.Status}}'
|
|
444
|
+
find "$BORG_SCRATCH_ROOT" -mindepth 1 -print
|
|
445
|
+
find "$BORG_SCRATCH_ROOT" -name 'borg-rig-*' -print
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
The unfiltered filesystem listings expose legacy names as well as conforming
|
|
449
|
+
rigs. The bounded temporary-directory function covers tools that use `mkdtemp`
|
|
450
|
+
instead of the seat scratch root. Run the container and scratch listings before
|
|
451
|
+
launch and again from the cleanup path; the temporary-directory function runs
|
|
452
|
+
after cleanup, between the start and end markers. Inspect its results and do
|
|
453
|
+
not remove unrelated temporary files.
|
|
454
|
+
|
|
455
|
+
Register cleanup before launching any process, run it on success and failure,
|
|
456
|
+
and do not deliver a verdict until the container listing, both scratch listings,
|
|
457
|
+
and the bounded temporary-directory listing show no rig owned by the seat from
|
|
458
|
+
this run. For a non-`--rm` container runtime,
|
|
459
|
+
remove only the named rig or the same owner label; never prune unrelated
|
|
460
|
+
containers. A completed verification
|
|
461
|
+
therefore implies zero running or stopped rig containers and no rig workspace
|
|
462
|
+
created by this run left in either the seat scratch root or the system temporary
|
|
463
|
+
directory.
|
|
464
|
+
|
|
343
465
|
### Pre-tag composed exercise
|
|
344
466
|
|
|
345
467
|
Before creating a release tag, exercise the packed client against the selected
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "borgmcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.0",
|
|
4
4
|
"description": "Coordinate AI coding agents in shared cubes. Works with Claude Code, Codex, and OpenCode.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"borg-regen": "./dist/regen.js",
|
|
20
20
|
"borg-clear-rewake": "./dist/clear-rewake.js",
|
|
21
21
|
"borg-log-audit": "./dist/log-audit.js",
|
|
22
|
+
"borg-foreign-path-reminder": "./dist/foreign-path-reminder.js",
|
|
22
23
|
"borg-inbox-monitor": "./dist/inbox-monitor.js"
|
|
23
24
|
},
|
|
24
25
|
"scripts": {
|
package/src/agent-runtime.ts
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* agent_kind without re-assimilating.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { BORG_STATE_ROOT_ENV } from './private-root.js';
|
|
9
|
+
|
|
8
10
|
export type AgentKind = 'claude' | 'codex' | 'opencode';
|
|
9
11
|
|
|
10
12
|
/** Pinned into MCP-child environments by Borg launch paths. */
|
|
@@ -74,3 +76,18 @@ export function codexRemoteWakeConfigArgs(enabled: boolean = true): string[] {
|
|
|
74
76
|
`mcp_servers.borg.env.${BORG_CODEX_REMOTE_WAKE_ENV}="${enabled ? '1' : '0'}"`,
|
|
75
77
|
];
|
|
76
78
|
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Codex MCP children do not inherit the wrapper environment. Pin the explicit
|
|
82
|
+
* Borg state root into the per-launch child environment when one is active.
|
|
83
|
+
*/
|
|
84
|
+
export function codexStateRootConfigArgs(
|
|
85
|
+
env: NodeJS.ProcessEnv = process.env
|
|
86
|
+
): string[] {
|
|
87
|
+
const stateRoot = env[BORG_STATE_ROOT_ENV];
|
|
88
|
+
if (stateRoot === undefined) return [];
|
|
89
|
+
return [
|
|
90
|
+
'-c',
|
|
91
|
+
`mcp_servers.borg.env.${BORG_STATE_ROOT_ENV}=${JSON.stringify(stateRoot)}`,
|
|
92
|
+
];
|
|
93
|
+
}
|
package/src/assimilate-cmd.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { codexBorgSessionConfigArgs } from './launch-gate.js';
|
|
|
23
23
|
import {
|
|
24
24
|
codexAgentKindConfigArgs,
|
|
25
25
|
codexRemoteWakeConfigArgs,
|
|
26
|
+
codexStateRootConfigArgs,
|
|
26
27
|
withAgentRuntimeEnv,
|
|
27
28
|
} from './agent-runtime.js';
|
|
28
29
|
import type { BorgCli } from './cubes.js';
|
|
@@ -74,6 +75,14 @@ import type { SeatBinding, BindPendingSeatOutcome } from './seats.js';
|
|
|
74
75
|
import { createHash } from 'node:crypto';
|
|
75
76
|
import { buildOpenCodeLaunchArgs, type LaunchApprovalDecision } from './cli-tool-approval.js';
|
|
76
77
|
import { resolveWorkingRepo, type WorkingRepo } from './working-repo.js';
|
|
78
|
+
import {
|
|
79
|
+
BORG_LAUNCH_CLI_ENV,
|
|
80
|
+
BORG_LAUNCH_SCRATCH_ENV,
|
|
81
|
+
BORG_LAUNCH_WORKTREE_ENV,
|
|
82
|
+
codexLaunchDirectoryArgs,
|
|
83
|
+
scratchRootForSeat,
|
|
84
|
+
type LaunchAccessPaths,
|
|
85
|
+
} from './launch-access.js';
|
|
77
86
|
import type {
|
|
78
87
|
AssociateRepositoryCubeResponse,
|
|
79
88
|
CreateCubeRepository,
|
|
@@ -287,6 +296,12 @@ export interface AssimilateDeps {
|
|
|
287
296
|
// launch root's .claude/settings.local.json (project-local; idempotent).
|
|
288
297
|
// Real wiring = config-utils addProjectSessionStartHook.
|
|
289
298
|
installProjectSessionHook: (projectRoot: string) => void;
|
|
299
|
+
/** Pre-authorize only the launched seat's worktree + scratch paths. */
|
|
300
|
+
provisionLaunchAccess?: (
|
|
301
|
+
cli: BorgCli,
|
|
302
|
+
projectRoot: string,
|
|
303
|
+
paths: LaunchAccessPaths,
|
|
304
|
+
) => void;
|
|
290
305
|
|
|
291
306
|
/** gh#27: optional test seam — when set, selectAssimilationAuthority uses
|
|
292
307
|
* this instead of prompting/failing. Not wired in production. */
|
|
@@ -1807,6 +1822,22 @@ export async function runAssimilate(
|
|
|
1807
1822
|
spawnedWorktreePath = deps.cwd();
|
|
1808
1823
|
}
|
|
1809
1824
|
|
|
1825
|
+
// ----- Step 7b: provision launch access before persisting/launching -----
|
|
1826
|
+
// The launched process gets exactly its current worktree plus a stable,
|
|
1827
|
+
// disposable per-seat scratch root. Provision this before FINALIZE so a
|
|
1828
|
+
// failed config write cannot produce a saved seat that launches without
|
|
1829
|
+
// its promised path grants.
|
|
1830
|
+
const agentCwd = deps.cwd(); // post-chdir if step 3 spawned a worktree
|
|
1831
|
+
const seatWorktree = deps.findProjectRoot(agentCwd);
|
|
1832
|
+
const monitorStateRoot = monitorStateRootForWorktree(seatWorktree);
|
|
1833
|
+
const scratchRoot = scratchRootForSeat(deps.homedir(), result.drone_label, result.drone_id);
|
|
1834
|
+
const launchAccessPaths: LaunchAccessPaths = {
|
|
1835
|
+
// Access is granted at the repository root even when the harness starts in
|
|
1836
|
+
// a nested package. The launch cwd remains the operator's chosen subdir.
|
|
1837
|
+
worktree: seatWorktree,
|
|
1838
|
+
scratch: scratchRoot,
|
|
1839
|
+
};
|
|
1840
|
+
|
|
1810
1841
|
// ----- Step 8: persist the binding (narrow rollback — worktree exists if spawned) -----
|
|
1811
1842
|
const activeCube: ActiveCube = {
|
|
1812
1843
|
cubeId: result.cube_id,
|
|
@@ -1836,6 +1867,19 @@ export async function runAssimilate(
|
|
|
1836
1867
|
}
|
|
1837
1868
|
};
|
|
1838
1869
|
|
|
1870
|
+
try {
|
|
1871
|
+
deps.mkdirp(scratchRoot);
|
|
1872
|
+
deps.provisionLaunchAccess?.(cli, seatWorktree, launchAccessPaths);
|
|
1873
|
+
} catch (err) {
|
|
1874
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1875
|
+
deps.stderr(
|
|
1876
|
+
`Borg could not pre-authorize the ${cli} launch paths: ${message}. ` +
|
|
1877
|
+
'No agent was launched; correct the local configuration and retry.\n',
|
|
1878
|
+
);
|
|
1879
|
+
rollbackWorktree();
|
|
1880
|
+
return 1;
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1839
1883
|
// Local-server authority: drive the COMPOSITE cube-owned FINALIZE (Race 2).
|
|
1840
1884
|
// The cube lock is held OUTER across revalidate → binding-write → activate; the
|
|
1841
1885
|
// typed expectation is declared HERE at the orchestration layer (reattach =
|
|
@@ -1945,13 +1989,6 @@ export async function runAssimilate(
|
|
|
1945
1989
|
}
|
|
1946
1990
|
}
|
|
1947
1991
|
|
|
1948
|
-
// The worktree, not a reminted drone UUID, is the stable local seat identity
|
|
1949
|
-
// for monitor runtime state. Capture it once before the lazy GC and launch
|
|
1950
|
-
// paths so both use the exact same explicit root.
|
|
1951
|
-
const agentCwd = deps.cwd(); // post-chdir if step 3 spawned a worktree
|
|
1952
|
-
const seatWorktree = deps.findProjectRoot(agentCwd);
|
|
1953
|
-
const monitorStateRoot = monitorStateRootForWorktree(seatWorktree);
|
|
1954
|
-
|
|
1955
1992
|
// gh#793: best-effort GC of orphaned inbox files (evicted/dead drones) in the
|
|
1956
1993
|
// cube just joined — lazy-on-assimilate, no cron/new command. NEVER blocks or
|
|
1957
1994
|
// fails the assimilate (whole call swallowed). Local-only signal (CubeDetail
|
|
@@ -2082,6 +2119,9 @@ export async function runAssimilate(
|
|
|
2082
2119
|
...(withAgentRuntimeEnv(process.env, cli) as Record<string, string>),
|
|
2083
2120
|
...modelEnv.set,
|
|
2084
2121
|
BORG_SESSION: '1',
|
|
2122
|
+
[BORG_LAUNCH_CLI_ENV]: cli,
|
|
2123
|
+
[BORG_LAUNCH_WORKTREE_ENV]: seatWorktree,
|
|
2124
|
+
[BORG_LAUNCH_SCRATCH_ENV]: scratchRoot,
|
|
2085
2125
|
};
|
|
2086
2126
|
if (cli === 'opencode' && launchApproval.openCodePermission) {
|
|
2087
2127
|
childEnv.OPENCODE_PERMISSION = launchApproval.openCodePermission;
|
|
@@ -2127,10 +2167,12 @@ export async function runAssimilate(
|
|
|
2127
2167
|
// off when no socket is available, overriding legacy static configs that
|
|
2128
2168
|
// formerly used this transport marker as Codex identity.
|
|
2129
2169
|
launchArgs = [
|
|
2170
|
+
...codexLaunchDirectoryArgs(launchAccessPaths),
|
|
2130
2171
|
...launchApproval.codexArgs,
|
|
2131
2172
|
...codexBorgSessionConfigArgs(),
|
|
2132
2173
|
...codexAgentKindConfigArgs(),
|
|
2133
2174
|
...codexRemoteWakeConfigArgs(codexSocketPath !== null),
|
|
2175
|
+
...codexStateRootConfigArgs(),
|
|
2134
2176
|
...remoteArgs,
|
|
2135
2177
|
...withCodexCwdArg(launchArgs, agentCwd),
|
|
2136
2178
|
];
|
package/src/assimilate-deps.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { spawnSync, spawn as spawnChild } from 'node:child_process';
|
|
12
12
|
import { randomBytes } from 'node:crypto';
|
|
13
13
|
import { existsSync, mkdirSync } from 'node:fs';
|
|
14
|
-
import { hostname as osHostname
|
|
14
|
+
import { hostname as osHostname } from 'node:os';
|
|
15
15
|
import { createInterface } from 'node:readline/promises';
|
|
16
16
|
import prompts from 'prompts';
|
|
17
17
|
import { readinessProbeEnv } from './readiness-probe.js';
|
|
@@ -62,7 +62,13 @@ import {
|
|
|
62
62
|
inboxPathForDrone,
|
|
63
63
|
setCodexWakeTarget,
|
|
64
64
|
} from './cubes.js';
|
|
65
|
-
import {
|
|
65
|
+
import {
|
|
66
|
+
addClaudeLaunchAccess,
|
|
67
|
+
addCodexForeignPathReminderHook,
|
|
68
|
+
addOpenCodeLaunchAccess,
|
|
69
|
+
addProjectSessionStartHook,
|
|
70
|
+
} from './config-utils.js';
|
|
71
|
+
import type { LaunchAccessPaths } from './launch-access.js';
|
|
66
72
|
import { findPendingServerEnrollment } from './config.js';
|
|
67
73
|
import { setTerminalTitle as setTitle } from './terminal-title.js';
|
|
68
74
|
import { defaultCliChoiceDeps, resolveCliChoice } from './cli-platform.js';
|
|
@@ -77,6 +83,7 @@ import {
|
|
|
77
83
|
saveRepositoryAssociation,
|
|
78
84
|
} from './repository-identity.js';
|
|
79
85
|
import { PromptInterruptedError } from './repository-cube-init.js';
|
|
86
|
+
import { borgHomeRoot } from './private-root.js';
|
|
80
87
|
|
|
81
88
|
/**
|
|
82
89
|
* Wraps the readline question operation with the production interruption
|
|
@@ -126,7 +133,7 @@ export function buildDefaultAssimilateDeps(
|
|
|
126
133
|
// gh#556 Part 1: ~/.borg/worktrees relocation. homedir seams $HOME;
|
|
127
134
|
// mkdirp is a plain recursive create (NO chmod of existing parents — the
|
|
128
135
|
// ~/.borg credentials file's perms stay untouched).
|
|
129
|
-
homedir: () =>
|
|
136
|
+
homedir: () => borgHomeRoot(),
|
|
130
137
|
mkdirp: (dir) => mkdirSync(dir, { recursive: true }),
|
|
131
138
|
preparePrivateRoot: () => ensurePrivateBorgConfigRoot(),
|
|
132
139
|
exec: (cmd, args, cwd, env) =>
|
|
@@ -235,6 +242,17 @@ export function buildDefaultAssimilateDeps(
|
|
|
235
242
|
installProjectSessionHook: (projectRoot) => {
|
|
236
243
|
addProjectSessionStartHook(projectRoot);
|
|
237
244
|
},
|
|
245
|
+
provisionLaunchAccess: (cli, projectRoot, paths: LaunchAccessPaths) => {
|
|
246
|
+
if (cli === 'claude') {
|
|
247
|
+
addClaudeLaunchAccess(projectRoot, paths);
|
|
248
|
+
} else if (cli === 'codex') {
|
|
249
|
+
// Codex receives the path grant on the launch command itself. Its
|
|
250
|
+
// native PreToolUse hook is global but reads this launch's scoped env.
|
|
251
|
+
addCodexForeignPathReminderHook();
|
|
252
|
+
} else {
|
|
253
|
+
addOpenCodeLaunchAccess(projectRoot, paths);
|
|
254
|
+
}
|
|
255
|
+
},
|
|
238
256
|
|
|
239
257
|
// #1015: discovery is advisory but still verifies the server-owned CA.
|
|
240
258
|
detectLocalServer: async () =>
|
package/src/claude.ts
CHANGED
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
BORG_CODEX_REMOTE_WAKE_ENV,
|
|
63
63
|
codexAgentKindConfigArgs,
|
|
64
64
|
codexRemoteWakeConfigArgs,
|
|
65
|
+
codexStateRootConfigArgs,
|
|
65
66
|
withAgentRuntimeEnv,
|
|
66
67
|
} from './agent-runtime.js';
|
|
67
68
|
import { findLoadedCodexThread } from './codex-app-server.js';
|
|
@@ -531,6 +532,7 @@ async function main() {
|
|
|
531
532
|
...codexBorgSessionConfigArgs(),
|
|
532
533
|
...codexAgentKindConfigArgs(),
|
|
533
534
|
...codexRemoteWakeConfigArgs(codexSocketPath !== null),
|
|
535
|
+
...codexStateRootConfigArgs(),
|
|
534
536
|
...remoteArgs,
|
|
535
537
|
...withCodexCwdArg([...passthroughArgs, kickoff], process.cwd()),
|
|
536
538
|
];
|
package/src/cleanup-cmd.ts
CHANGED
|
@@ -36,7 +36,6 @@
|
|
|
36
36
|
import { spawnSync } from 'node:child_process';
|
|
37
37
|
import { realpathSync } from 'node:fs';
|
|
38
38
|
import { sep, basename, dirname } from 'node:path';
|
|
39
|
-
import { homedir } from 'node:os';
|
|
40
39
|
import chalk from 'chalk';
|
|
41
40
|
import {
|
|
42
41
|
classifyDirty,
|
|
@@ -51,6 +50,7 @@ import {
|
|
|
51
50
|
type ActiveCube,
|
|
52
51
|
} from './cubes.js';
|
|
53
52
|
import { defaultProbeSeat, type SeatStatus } from './seat-probe.js';
|
|
53
|
+
import { borgHomeRoot } from './private-root.js';
|
|
54
54
|
|
|
55
55
|
// ------------------------------------------------------------------
|
|
56
56
|
// Types
|
|
@@ -208,7 +208,7 @@ const defaultDeps: Required<CleanupDeps> = {
|
|
|
208
208
|
const r = spawnSync(cmd, args, { cwd, encoding: 'utf-8' });
|
|
209
209
|
return { status: r.status, stdout: r.stdout ?? '', stderr: r.stderr ?? '' };
|
|
210
210
|
},
|
|
211
|
-
homeDir: () =>
|
|
211
|
+
homeDir: () => borgHomeRoot(),
|
|
212
212
|
cwd: () => process.cwd(),
|
|
213
213
|
listSeats: () => readAllProjectIdentities(),
|
|
214
214
|
probeSeat: defaultProbeSeat,
|
package/src/cli-tool-approval.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { spawn, spawnSync } from 'node:child_process';
|
|
2
2
|
import { chmod, copyFile, mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
3
|
-
import {
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
4
|
import { dirname, join, resolve } from 'node:path';
|
|
5
5
|
import type { BorgCli } from './cubes.js';
|
|
6
|
+
import { borgHomeRoot } from './private-root.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Client #20: the narrow coordination surface a borg-launched agent needs
|
|
@@ -360,7 +361,7 @@ async function withNativeCodexProfileLayer<T>(
|
|
|
360
361
|
env: NodeJS.ProcessEnv,
|
|
361
362
|
query: (profileCwd: string, trustOverride: string) => Promise<CodexConfigSnapshot>
|
|
362
363
|
): Promise<T> {
|
|
363
|
-
const codexHome = resolve(env.CODEX_HOME || join(
|
|
364
|
+
const codexHome = resolve(env.CODEX_HOME || join(borgHomeRoot(), '.codex'));
|
|
364
365
|
const profilePath = resolve(codexHome, `${profile}.config.toml`);
|
|
365
366
|
if (dirname(profilePath) !== codexHome) {
|
|
366
367
|
throw new Error('Codex profile path was invalid');
|
package/src/codex-remote.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { mkdirSync, chmodSync, readdirSync, rmSync, writeFileSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { homedir } from 'node:os';
|
|
3
2
|
import { join, resolve } from 'node:path';
|
|
4
3
|
import { randomBytes } from 'node:crypto';
|
|
5
4
|
import { spawn } from 'node:child_process';
|
|
@@ -11,7 +10,9 @@ import {
|
|
|
11
10
|
BORG_CODEX_REMOTE_WAKE_ENV,
|
|
12
11
|
codexAgentKindConfigArgs,
|
|
13
12
|
codexRemoteWakeConfigArgs,
|
|
13
|
+
codexStateRootConfigArgs,
|
|
14
14
|
} from './agent-runtime.js';
|
|
15
|
+
import { borgConfigRoot } from './private-root.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* gh#528 — npm-managed Codex remote-wake.
|
|
@@ -75,7 +76,7 @@ export interface PrepareCodexRemoteDeps {
|
|
|
75
76
|
isAlive?: (pid: number) => boolean;
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
export const DEFAULT_CODEX_REMOTE_DIR = join(
|
|
79
|
+
export const DEFAULT_CODEX_REMOTE_DIR = join(borgConfigRoot(), 'codex-remote');
|
|
79
80
|
const DEFAULT_CODEX_REMOTE_READY_TIMEOUT_MS = 30_000;
|
|
80
81
|
const MAX_CODEX_APP_SERVER_STDERR_CHARS = 16_384;
|
|
81
82
|
|
|
@@ -426,6 +427,7 @@ export function defaultCodexRemoteDeps(): Pick<
|
|
|
426
427
|
...codexBorgSessionConfigArgs(),
|
|
427
428
|
...codexAgentKindConfigArgs(),
|
|
428
429
|
...codexRemoteWakeConfigArgs(),
|
|
430
|
+
...codexStateRootConfigArgs(),
|
|
429
431
|
...codexAppServerSocketConfigArgs(socketPath),
|
|
430
432
|
'--listen',
|
|
431
433
|
`unix://${socketPath}`,
|