@clipboard-health/groundcrew 4.47.6 → 4.48.1
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/README.md +1 -0
- package/crew.config.example.ts +2 -2
- package/dist/commands/doctor.js +0 -25
- package/dist/commands/openWorkspace.d.ts.map +1 -1
- package/dist/commands/openWorkspace.js +25 -18
- package/dist/commands/resumeWorkspace.d.ts.map +1 -1
- package/dist/commands/resumeWorkspace.js +7 -15
- package/dist/commands/setupWorkspace.d.ts.map +1 -1
- package/dist/commands/setupWorkspace.js +23 -20
- package/dist/lib/agentLaunch.d.ts +14 -11
- package/dist/lib/agentLaunch.d.ts.map +1 -1
- package/dist/lib/agentLaunch.js +102 -51
- package/dist/lib/cmuxAgentHookInstall.d.ts +17 -0
- package/dist/lib/cmuxAgentHookInstall.d.ts.map +1 -0
- package/dist/lib/cmuxAgentHookInstall.js +42 -0
- package/dist/lib/codexConfigRelocation.d.ts +60 -0
- package/dist/lib/codexConfigRelocation.d.ts.map +1 -0
- package/dist/lib/codexConfigRelocation.js +70 -0
- package/dist/lib/commandRunner.d.ts +9 -0
- package/dist/lib/commandRunner.d.ts.map +1 -1
- package/dist/lib/commandRunner.js +1 -0
- package/dist/lib/config.d.ts +31 -12
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +18 -2
- package/dist/lib/gitCommonDir.d.ts +7 -0
- package/dist/lib/gitCommonDir.d.ts.map +1 -0
- package/dist/lib/gitCommonDir.js +15 -0
- package/dist/lib/host.d.ts +0 -14
- package/dist/lib/host.d.ts.map +1 -1
- package/dist/lib/host.js +1 -8
- package/dist/lib/launchCommand.d.ts +39 -51
- package/dist/lib/launchCommand.d.ts.map +1 -1
- package/dist/lib/launchCommand.js +117 -183
- package/dist/lib/localRunner.d.ts +4 -5
- package/dist/lib/localRunner.d.ts.map +1 -1
- package/dist/lib/localRunner.js +4 -23
- package/dist/lib/repositoryHooks.js +13 -0
- package/docs/configuration.md +3 -3
- package/docs/credentials.md +8 -1
- package/docs/runners.md +10 -49
- package/docs/setup-hooks.md +39 -0
- package/docs/spec/e2e-scenario-catalog.md +164 -0
- package/docs/spec/v2-design.md +353 -0
- package/docs/task-sources.md +3 -3
- package/package.json +10 -13
- package/static/demo.tape +0 -2
- package/static/render-demo.sh +0 -1
- package/dist/lib/clearanceHosts.d.ts +0 -31
- package/dist/lib/clearanceHosts.d.ts.map +0 -1
- package/dist/lib/clearanceHosts.js +0 -96
- package/dist/lib/srtLaunch.d.ts +0 -61
- package/dist/lib/srtLaunch.d.ts.map +0 -1
- package/dist/lib/srtLaunch.js +0 -107
- package/dist/lib/srtPolicy.d.ts +0 -118
- package/dist/lib/srtPolicy.d.ts.map +0 -1
- package/dist/lib/srtPolicy.js +0 -371
package/README.md
CHANGED
|
@@ -159,6 +159,7 @@ There is no `linear` config block. Groundcrew reads `GROUNDCREW_LINEAR_API_KEY`
|
|
|
159
159
|
- [Runners](./docs/runners.md): Safehouse, Docker Sandboxes, and the `none` escape hatch.
|
|
160
160
|
- [Credentials](./docs/credentials.md): Linear API keys, 1Password, build secrets, and `preLaunch`.
|
|
161
161
|
- [Prepare worktree hooks](./docs/setup-hooks.md): `.groundcrew/config.json` `hooks.prepareWorktree` for per-repo dependency setup.
|
|
162
|
+
- [Operator host setup](./docs/setup-hooks.md#unsandboxedhooks-operator-only-host-setup): `crew.config.ts` `knownRepositories[].unsandboxedHooks.prepareWorktree` runs trusted, per-repo setup on the host outside the sandbox.
|
|
162
163
|
- [Task sources](./docs/task-sources.md): custom shell/Jira/local-plan adapters.
|
|
163
164
|
- [Development](./docs/development.md): local source workflow and README/demo asset regeneration.
|
|
164
165
|
- [Troubleshooting](./docs/troubleshooting.md): common operational pitfalls and fixes.
|
package/crew.config.example.ts
CHANGED
|
@@ -131,7 +131,7 @@ export default {
|
|
|
131
131
|
// // Install via task-sources/jira (see task-sources/jira/README.md):
|
|
132
132
|
// // cp task-sources/jira/jira.sh ~/.config/groundcrew/jira.sh
|
|
133
133
|
// // Open local task-store directories for read/write inside the
|
|
134
|
-
// // safehouse
|
|
134
|
+
// // safehouse sandbox when this source owns the launched task.
|
|
135
135
|
// sandboxWritePaths: ["~/plans"],
|
|
136
136
|
// commands: {
|
|
137
137
|
// verify: "~/.config/groundcrew/jira.sh verify",
|
|
@@ -179,7 +179,7 @@ export default {
|
|
|
179
179
|
// // deny-by-default policy. Examples: `agent-browser` so the
|
|
180
180
|
// // chrome-devtools MCP server can drive a headless Chrome;
|
|
181
181
|
// // `browser-native-messaging` for `claude --chrome`. Ignored by the
|
|
182
|
-
// //
|
|
182
|
+
// // sdx/none runners.
|
|
183
183
|
// local: { safehouse: { enable: ["agent-browser"] } },
|
|
184
184
|
//
|
|
185
185
|
// // Groundcrew does not create or authenticate sdx sandboxes. For an sdx
|
package/dist/commands/doctor.js
CHANGED
|
@@ -343,22 +343,6 @@ function localCapabilityCheck(host, resolved) {
|
|
|
343
343
|
: "sdx runner requires `sbx` (Docker Sandboxes) on PATH (install from https://docs.docker.com/ai/sandboxes/)",
|
|
344
344
|
};
|
|
345
345
|
}
|
|
346
|
-
if (resolved === "srt") {
|
|
347
|
-
const missingLinuxDeps = host.isLinux
|
|
348
|
-
? [
|
|
349
|
-
host.hasBubblewrap ? undefined : "bubblewrap",
|
|
350
|
-
host.hasSocat ? undefined : "socat",
|
|
351
|
-
host.hasRipgrep ? undefined : "ripgrep (rg)",
|
|
352
|
-
].filter((name) => name !== undefined)
|
|
353
|
-
: [];
|
|
354
|
-
const ok = host.isSrtSupported && missingLinuxDeps.length === 0;
|
|
355
|
-
return {
|
|
356
|
-
name: "local runner (srt)",
|
|
357
|
-
ok,
|
|
358
|
-
required: false,
|
|
359
|
-
hint: srtRunnerHint(ok, host.isSrtSupported, missingLinuxDeps),
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
346
|
// resolved === "none"
|
|
363
347
|
return {
|
|
364
348
|
name: "local runner (none)",
|
|
@@ -367,15 +351,6 @@ function localCapabilityCheck(host, resolved) {
|
|
|
367
351
|
hint: "WARNING: local.runner='none' — agent runs unsandboxed on the host. Only use this when you understand the implications.",
|
|
368
352
|
};
|
|
369
353
|
}
|
|
370
|
-
function srtRunnerHint(ok, isSrtSupported, missingLinuxDeps) {
|
|
371
|
-
if (ok) {
|
|
372
|
-
return "ready (beta: @anthropic-ai/sandbox-runtime is a research preview)";
|
|
373
|
-
}
|
|
374
|
-
if (!isSrtSupported) {
|
|
375
|
-
return "srt runner requires macOS or Linux/WSL";
|
|
376
|
-
}
|
|
377
|
-
return `srt runner on Linux requires ${missingLinuxDeps.join(", ")} on PATH (Debian/Ubuntu: \`apt install bubblewrap socat ripgrep\`; on Ubuntu 24.04+ also \`sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0\`)`;
|
|
378
|
-
}
|
|
379
354
|
function reportLocalCapability(arguments_) {
|
|
380
355
|
writeOutput();
|
|
381
356
|
writeOutput("Local runner");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/openWorkspace.ts"],"names":[],"mappings":"AAKA,OAAO,EAAiC,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"openWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/openWorkspace.ts"],"names":[],"mappings":"AAKA,OAAO,EAAiC,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAgBtF,UAAU,gBAAgB;IACxB,IAAI,EAAE,IAAI,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,WAAW;IACnB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,gBAAgB,GAAG,WAAW,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAwJD,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA2Hf;AA4ID,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,oBAAoB,CAE3E;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAIpE"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readFileSync
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { composeAgentLaunch, openAgentWorkspace, prepareAgentLaunch } from "../lib/agentLaunch.js";
|
|
4
4
|
import { inferAgentCommandName } from "../lib/launchCommand.js";
|
|
@@ -7,7 +7,7 @@ import { resolvePullRequest } from "../lib/pullRequests.js";
|
|
|
7
7
|
import { resolvePrepareWorktreeCommand } from "../lib/repositoryHooks.js";
|
|
8
8
|
import { recordRunState, readRunState } from "../lib/runState.js";
|
|
9
9
|
import { seedLaunchWorkspaceTrust } from "../lib/seedLaunchWorkspaceTrust.js";
|
|
10
|
-
import { stageBuildSecrets, stagePromptText, stageWorkspaceLaunchCommand, } from "../lib/stagedLaunch.js";
|
|
10
|
+
import { removeStagedPrompt, stageBuildSecrets, stagePromptText, stageWorkspaceLaunchCommand, } from "../lib/stagedLaunch.js";
|
|
11
11
|
import { normalizePlainTaskId } from "../lib/taskId.js";
|
|
12
12
|
import { debug, errorMessage, log, okMark } from "../lib/util.js";
|
|
13
13
|
import { failIfWorkspaceAlreadyLive } from "../lib/workspaceLiveness.js";
|
|
@@ -106,15 +106,11 @@ async function rollback(arguments_) {
|
|
|
106
106
|
catch (error) {
|
|
107
107
|
log(`Worktree teardown failed during rollback: ${errorMessage(error)}`);
|
|
108
108
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
catch {
|
|
115
|
-
// already gone
|
|
116
|
-
}
|
|
117
|
-
}
|
|
109
|
+
try {
|
|
110
|
+
removeStagedPrompt(arguments_.promptDir);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// already gone
|
|
118
114
|
}
|
|
119
115
|
}
|
|
120
116
|
export async function openWorkspace(config, options) {
|
|
@@ -154,19 +150,27 @@ export async function openWorkspace(config, options) {
|
|
|
154
150
|
task: target.task,
|
|
155
151
|
text: options.promptText ?? "",
|
|
156
152
|
});
|
|
157
|
-
let
|
|
153
|
+
let cleanupAgentLaunch;
|
|
158
154
|
try {
|
|
155
|
+
const repositoryEntry = config.workspace.repositories.find((entry) => entry.name === repository);
|
|
159
156
|
const prepareWorktreeCommand = resolvePrepareWorktreeCommand({
|
|
160
157
|
worktreeDir: launchDir,
|
|
158
|
+
// Spread-conditional: exactOptionalPropertyTypes forbids an explicit
|
|
159
|
+
// `undefined` for an optional field, and the lookup yields undefined for
|
|
160
|
+
// repos with no hooks. Mirrors setupWorkspace so `crew open` honors the
|
|
161
|
+
// same per-repo operator hooks as `crew setup`.
|
|
162
|
+
...(repositoryEntry?.hooks === undefined ? {} : { perRepoHooks: repositoryEntry.hooks }),
|
|
161
163
|
defaultHooks: config.defaults.hooks,
|
|
162
164
|
});
|
|
163
|
-
const
|
|
165
|
+
const prepareWorktreeUnsandboxedCommand = repositoryEntry?.unsandboxedHooks?.prepareWorktree;
|
|
166
|
+
const secretsFile = prepareWorktreeCommand === undefined && prepareWorktreeUnsandboxedCommand === undefined
|
|
167
|
+
? undefined
|
|
168
|
+
: stageBuildSecrets(stagedPrompt.directory);
|
|
164
169
|
seedLaunchWorkspaceTrust({
|
|
165
170
|
agentCommandName: inferAgentCommandName(definition.cmd),
|
|
166
171
|
launchDir,
|
|
167
172
|
});
|
|
168
|
-
|
|
169
|
-
({ launchCommand, srtSettingsDir } = composeAgentLaunch({
|
|
173
|
+
const launch = composeAgentLaunch({
|
|
170
174
|
runner,
|
|
171
175
|
networkEgress,
|
|
172
176
|
task: target.task,
|
|
@@ -176,12 +180,14 @@ export async function openWorkspace(config, options) {
|
|
|
176
180
|
workingDir: launchDir,
|
|
177
181
|
secretsFile,
|
|
178
182
|
prepareWorktreeCommand,
|
|
183
|
+
prepareWorktreeUnsandboxedCommand,
|
|
179
184
|
sandboxName,
|
|
180
185
|
workspaceKind,
|
|
181
186
|
readOnlyDirs: config.local.readOnlyDirs,
|
|
182
187
|
omitPromptArgument,
|
|
183
|
-
})
|
|
184
|
-
|
|
188
|
+
});
|
|
189
|
+
cleanupAgentLaunch = launch.cleanup;
|
|
190
|
+
const launchCmd = stageWorkspaceLaunchCommand(stagedPrompt.directory, launch.command);
|
|
185
191
|
await openAgentWorkspace({
|
|
186
192
|
config,
|
|
187
193
|
name: target.task,
|
|
@@ -193,7 +199,8 @@ export async function openWorkspace(config, options) {
|
|
|
193
199
|
});
|
|
194
200
|
}
|
|
195
201
|
catch (error) {
|
|
196
|
-
|
|
202
|
+
cleanupAgentLaunch?.();
|
|
203
|
+
await rollback({ config, entry: created, promptDir: stagedPrompt.directory });
|
|
197
204
|
throw error;
|
|
198
205
|
}
|
|
199
206
|
recordRunState({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resumeWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/resumeWorkspace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoC,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAsBzF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAyKD,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"resumeWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/resumeWorkspace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoC,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAsBzF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAyKD,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAiGf;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtE"}
|
|
@@ -170,14 +170,9 @@ export async function resumeWorkspace(config, options) {
|
|
|
170
170
|
text: renderResumePrompt(context),
|
|
171
171
|
});
|
|
172
172
|
const secretsFile = stageBuildSecrets(stagedPrompt.directory);
|
|
173
|
-
|
|
174
|
-
// codex needs its config home re-seeded to authenticate on the resumed launch).
|
|
175
|
-
// Composition runs inside the try so a pre-launch failure still cleans up the
|
|
176
|
-
// staged prompt (and any srt settings) dir.
|
|
177
|
-
let srtSettingsDir;
|
|
173
|
+
let cleanupAgentLaunch;
|
|
178
174
|
try {
|
|
179
|
-
|
|
180
|
-
const taskSourceWritePaths = runner === "safehouse" || runner === "srt"
|
|
175
|
+
const taskSourceWritePaths = runner === "safehouse"
|
|
181
176
|
? taskSourceWritePathsForCompletion({
|
|
182
177
|
config,
|
|
183
178
|
taskId: context.completionTaskId,
|
|
@@ -188,7 +183,7 @@ export async function resumeWorkspace(config, options) {
|
|
|
188
183
|
agentCommandName: inferAgentCommandName(launchDefinition.cmd),
|
|
189
184
|
launchDir,
|
|
190
185
|
});
|
|
191
|
-
|
|
186
|
+
const launch = composeAgentLaunch({
|
|
192
187
|
runner,
|
|
193
188
|
networkEgress,
|
|
194
189
|
task,
|
|
@@ -206,8 +201,9 @@ export async function resumeWorkspace(config, options) {
|
|
|
206
201
|
}),
|
|
207
202
|
taskSourceWritePaths,
|
|
208
203
|
safehouseEnableFeatures: config.local.safehouse.enable,
|
|
209
|
-
})
|
|
210
|
-
|
|
204
|
+
});
|
|
205
|
+
cleanupAgentLaunch = launch.cleanup;
|
|
206
|
+
const launchCmd = stageWorkspaceLaunchCommand(stagedPrompt.directory, launch.command);
|
|
211
207
|
await openAgentWorkspace({
|
|
212
208
|
config,
|
|
213
209
|
name: task,
|
|
@@ -219,12 +215,8 @@ export async function resumeWorkspace(config, options) {
|
|
|
219
215
|
});
|
|
220
216
|
}
|
|
221
217
|
catch (error) {
|
|
218
|
+
cleanupAgentLaunch?.();
|
|
222
219
|
removeStagedPrompt(stagedPrompt.directory);
|
|
223
|
-
// The launch command tears down the settings dir after srt exits; on the
|
|
224
|
-
// pre-launch failure path it never ran, so clean it up here.
|
|
225
|
-
if (srtSettingsDir !== undefined) {
|
|
226
|
-
removeStagedPrompt(srtSettingsDir);
|
|
227
|
-
}
|
|
228
220
|
throw error;
|
|
229
221
|
}
|
|
230
222
|
recordRunState({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setupWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/setupWorkspace.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"setupWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/setupWorkspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AA2BnE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6FAA6F;IAC7F,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAuBD,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,qBAAqB,EAC9B,UAAU,GAAE,wBAA6B,GACxC,OAAO,CAAC,IAAI,CAAC,CA4Jf;AA2MD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,IAAI,CAAC,CAkDf"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { rmSync } from "node:fs";
|
|
2
1
|
import { loadConfig } from "../lib/config.js";
|
|
3
2
|
import { composeAgentLaunch, openAgentWorkspace, prepareAgentLaunch } from "../lib/agentLaunch.js";
|
|
4
3
|
import { inferAgentCommandName, workerEnvironmentForTask } from "../lib/launchCommand.js";
|
|
@@ -8,7 +7,7 @@ import { resolvePrepareWorktreeCommand } from "../lib/repositoryHooks.js";
|
|
|
8
7
|
import { recordRunState } from "../lib/runState.js";
|
|
9
8
|
import { seedLaunchWorkspaceTrust } from "../lib/seedLaunchWorkspaceTrust.js";
|
|
10
9
|
import { sourceSupportsMarkDone } from "../lib/sourceCapabilities.js";
|
|
11
|
-
import { stageBuildSecrets, stagePromptFromTemplate, stageWorkspaceLaunchCommand, } from "../lib/stagedLaunch.js";
|
|
10
|
+
import { removeStagedPrompt, stageBuildSecrets, stagePromptFromTemplate, stageWorkspaceLaunchCommand, } from "../lib/stagedLaunch.js";
|
|
12
11
|
import { taskSourceWritePathsForCompletion } from "../lib/taskSourceFilesystem.js";
|
|
13
12
|
import { naturalIdFromCanonical } from "../lib/taskSource.js";
|
|
14
13
|
import { debug, errorMessage, log, okMark } from "../lib/util.js";
|
|
@@ -72,7 +71,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
72
71
|
// Without rollback the next tick hits "Worktree already exists" and
|
|
73
72
|
// the task strands forever.
|
|
74
73
|
let promptDir;
|
|
75
|
-
let
|
|
74
|
+
let cleanupAgentLaunch;
|
|
76
75
|
try {
|
|
77
76
|
await assertLaunchReady(readinessPromise);
|
|
78
77
|
const taskDetails = options.details;
|
|
@@ -85,7 +84,9 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
85
84
|
workspaceContinuationInstruction: renderWorkspaceContinuationInstruction(accessHint),
|
|
86
85
|
});
|
|
87
86
|
promptDir = stagedPrompt.directory;
|
|
88
|
-
const
|
|
87
|
+
const repositoryEntry = config.workspace.repositories.find((entry) => entry.name === repository);
|
|
88
|
+
const perRepoHooks = repositoryEntry?.hooks;
|
|
89
|
+
const prepareWorktreeUnsandboxedCommand = repositoryEntry?.unsandboxedHooks?.prepareWorktree;
|
|
89
90
|
const prepareWorktreeCommand = resolvePrepareWorktreeCommand({
|
|
90
91
|
worktreeDir: launchDir,
|
|
91
92
|
// Spread-conditional rather than a direct assignment: under
|
|
@@ -94,10 +95,12 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
94
95
|
...(perRepoHooks === undefined ? {} : { perRepoHooks }),
|
|
95
96
|
defaultHooks: config.defaults.hooks,
|
|
96
97
|
});
|
|
97
|
-
const secretsFile = prepareWorktreeCommand === undefined
|
|
98
|
+
const secretsFile = prepareWorktreeCommand === undefined && prepareWorktreeUnsandboxedCommand === undefined
|
|
99
|
+
? undefined
|
|
100
|
+
: stageBuildSecrets(promptDir);
|
|
98
101
|
const completionTaskId = options.completionTaskId ?? task;
|
|
99
102
|
const completionMarkDoneSupported = options.completionMarkDoneSupported ?? true;
|
|
100
|
-
const taskSourceWritePaths = runner === "safehouse"
|
|
103
|
+
const taskSourceWritePaths = runner === "safehouse"
|
|
101
104
|
? taskSourceWritePathsForCompletion({
|
|
102
105
|
config,
|
|
103
106
|
taskId: completionTaskId,
|
|
@@ -108,7 +111,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
108
111
|
agentCommandName: inferAgentCommandName(definition.cmd),
|
|
109
112
|
launchDir,
|
|
110
113
|
});
|
|
111
|
-
const
|
|
114
|
+
const launch = composeAgentLaunch({
|
|
112
115
|
runner,
|
|
113
116
|
networkEgress,
|
|
114
117
|
task,
|
|
@@ -118,6 +121,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
118
121
|
workingDir: launchDir,
|
|
119
122
|
secretsFile,
|
|
120
123
|
prepareWorktreeCommand,
|
|
124
|
+
prepareWorktreeUnsandboxedCommand,
|
|
121
125
|
sandboxName,
|
|
122
126
|
workspaceKind,
|
|
123
127
|
readOnlyDirs: config.local.readOnlyDirs,
|
|
@@ -128,8 +132,8 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
128
132
|
taskSourceWritePaths,
|
|
129
133
|
safehouseEnableFeatures: config.local.safehouse.enable,
|
|
130
134
|
});
|
|
131
|
-
|
|
132
|
-
const launchCmd = stageWorkspaceLaunchCommand(promptDir,
|
|
135
|
+
cleanupAgentLaunch = launch.cleanup;
|
|
136
|
+
const launchCmd = stageWorkspaceLaunchCommand(promptDir, launch.command);
|
|
133
137
|
debug("Opening workspace...");
|
|
134
138
|
await openAgentWorkspace({
|
|
135
139
|
config,
|
|
@@ -162,7 +166,8 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
162
166
|
}
|
|
163
167
|
}
|
|
164
168
|
catch (error) {
|
|
165
|
-
|
|
169
|
+
cleanupAgentLaunch?.();
|
|
170
|
+
await rollbackWorktree({ config, entry: created, promptDir });
|
|
166
171
|
recordFailedToLaunch({ config, options, paths: { worktreeDir, branchName }, error });
|
|
167
172
|
throw error;
|
|
168
173
|
}
|
|
@@ -295,16 +300,14 @@ async function rollbackWorktree(arguments_) {
|
|
|
295
300
|
log(`Worktree teardown failed during rollback: ${errorMessage(error)}`);
|
|
296
301
|
}
|
|
297
302
|
finally {
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
// already gone
|
|
307
|
-
}
|
|
303
|
+
// The prompt dir is normally removed by the launch command; clean it here
|
|
304
|
+
// for the pre-launch failure path. Silent on retry races.
|
|
305
|
+
if (arguments_.promptDir !== undefined) {
|
|
306
|
+
try {
|
|
307
|
+
removeStagedPrompt(arguments_.promptDir);
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
// already gone
|
|
308
311
|
}
|
|
309
312
|
}
|
|
310
313
|
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { type LocalRunner, type AgentDefinition, type NetworkEgressSetting, type ResolvedConfig } from "./config.ts";
|
|
2
2
|
import { type WorkerEnvironment } from "./launchCommand.ts";
|
|
3
3
|
import type { WorkspaceKind } from "./workspaceAdapter.ts";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* Returns `srtSettingsDir` so callers can tear it down on a pre-launch failure.
|
|
10
|
-
*/
|
|
4
|
+
export interface ComposedAgentLaunch {
|
|
5
|
+
command: string;
|
|
6
|
+
cleanup: () => void;
|
|
7
|
+
}
|
|
8
|
+
/** Build the workspace launch command shared by fresh runs and resumes. */
|
|
11
9
|
export declare function composeAgentLaunch(input: {
|
|
12
10
|
runner: LocalRunner;
|
|
13
11
|
networkEgress: NetworkEgressSetting;
|
|
@@ -18,6 +16,7 @@ export declare function composeAgentLaunch(input: {
|
|
|
18
16
|
workingDir: string;
|
|
19
17
|
secretsFile?: string | undefined;
|
|
20
18
|
prepareWorktreeCommand?: string | undefined;
|
|
19
|
+
prepareWorktreeUnsandboxedCommand?: string | undefined;
|
|
21
20
|
sandboxName?: string | undefined;
|
|
22
21
|
workspaceKind: WorkspaceKind;
|
|
23
22
|
workerEnvironment?: WorkerEnvironment | undefined;
|
|
@@ -25,10 +24,14 @@ export declare function composeAgentLaunch(input: {
|
|
|
25
24
|
taskSourceWritePaths?: readonly string[] | undefined;
|
|
26
25
|
safehouseEnableFeatures?: readonly string[] | undefined;
|
|
27
26
|
readOnlyDirs?: readonly string[] | undefined;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Test-only seam: overrides `os.homedir()` when staging a relocated agent
|
|
29
|
+
* config home (codex's `CODEX_HOME` under safehouse). Production callers
|
|
30
|
+
* must omit this so specs can seed a fixture home instead of touching the
|
|
31
|
+
* real `~/.codex`.
|
|
32
|
+
*/
|
|
33
|
+
homeDir?: string;
|
|
34
|
+
}): ComposedAgentLaunch;
|
|
32
35
|
interface PreparedAgentLaunch {
|
|
33
36
|
runner: LocalRunner;
|
|
34
37
|
/** Resolved `config.local.networkEgress`, threaded into `composeAgentLaunch`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/agentLaunch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agentLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/agentLaunch.ts"],"names":[],"mappings":"AAcA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AAErB,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAW5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,MAAM,EAAE,WAAW,CAAC;IACpB,aAAa,EAAE,oBAAoB,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,eAAe,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,iCAAiC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvD,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,aAAa,CAAC;IAC7B,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAClD,kBAAkB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACzC,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACrD,uBAAuB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACxD,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7C;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,mBAAmB,CAgDtB;AAkHD,UAAU,mBAAmB;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,iFAAiF;IACjF,aAAa,EAAE,oBAAoB,CAAC;IACpC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,aAAa,EAAE,aAAa,CAAC;IAC7B,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAsD/B;AAwBD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAahB"}
|
package/dist/lib/agentLaunch.js
CHANGED
|
@@ -1,60 +1,65 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
2
4
|
import { ensureClearance, resolveSafehouseCmuxIntegration, safehouseCmuxIntegrationWarningLines, } from "@clipboard-health/clearance";
|
|
3
5
|
import { clearanceAllowHostsFilesFromEnvironment } from "./clearanceAllowlist.js";
|
|
6
|
+
import { installCmuxAgentHooks } from "./cmuxAgentHookInstall.js";
|
|
4
7
|
import { cmuxAgentHookSettingsJson } from "./cmuxAgentHooks.js";
|
|
5
8
|
import { shellSingleQuote } from "./shell.js";
|
|
6
9
|
import { hasPreLaunchEnv, } from "./config.js";
|
|
7
10
|
import { detectHostCapabilities } from "./host.js";
|
|
8
11
|
import { buildLaunchCommand, inferAgentCommandName, } from "./launchCommand.js";
|
|
12
|
+
import { agentConfigRelocation, stageRelocatedAgentConfigHome, } from "./codexConfigRelocation.js";
|
|
13
|
+
import { resolveGitCommonDir } from "./gitCommonDir.js";
|
|
9
14
|
import { assertLocalRunnerRequirements, resolveLocalRunner } from "./localRunner.js";
|
|
10
15
|
import { sandboxNameFor } from "./sandboxName.js";
|
|
11
|
-
import {
|
|
12
|
-
import { debug, sleep, writeError } from "./util.js";
|
|
16
|
+
import { debug, readEnvironmentVariable, sleep, writeError } from "./util.js";
|
|
13
17
|
import { resolveWorkspaceKind, workspaces } from "./workspaces.js";
|
|
14
|
-
/**
|
|
15
|
-
* Stage any srt settings and build the workspace launch command — the assembly
|
|
16
|
-
* shared verbatim by `setupWorkspace` (fresh runs) and `resumeWorkspace`
|
|
17
|
-
* (resumes). `worktreeDir` is the checkout root (srt grants + `{{worktree}}`);
|
|
18
|
-
* `workingDir` is the agent cwd (the worktree root, or its `workdir` subproject).
|
|
19
|
-
* Returns `srtSettingsDir` so callers can tear it down on a pre-launch failure.
|
|
20
|
-
*/
|
|
18
|
+
/** Build the workspace launch command shared by fresh runs and resumes. */
|
|
21
19
|
export function composeAgentLaunch(input) {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
const homeDir = input.homeDir ?? os.homedir();
|
|
21
|
+
const safehouseAgentIntegration = input.runner === "safehouse"
|
|
22
|
+
? safehouseAgentIntegrationFor({
|
|
23
|
+
workspaceKind: input.workspaceKind,
|
|
26
24
|
definition: input.definition,
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
task: input.task,
|
|
26
|
+
homeDir,
|
|
29
27
|
})
|
|
30
28
|
: undefined;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
29
|
+
function cleanup() {
|
|
30
|
+
for (const teardownPath of safehouseAgentIntegration?.teardownPaths ?? []) {
|
|
31
|
+
rmSync(teardownPath, { recursive: true, force: true });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
return {
|
|
36
|
+
command: buildLaunchCommand({
|
|
37
|
+
definition: input.definition,
|
|
38
|
+
promptFile: input.promptFile,
|
|
39
|
+
worktreeDir: input.worktreeDir,
|
|
40
|
+
workingDir: input.workingDir,
|
|
41
|
+
secretsFile: input.secretsFile,
|
|
42
|
+
prepareWorktreeCommand: input.prepareWorktreeCommand,
|
|
43
|
+
prepareWorktreeUnsandboxedCommand: input.prepareWorktreeUnsandboxedCommand,
|
|
44
|
+
runner: input.runner,
|
|
45
|
+
networkEgress: input.networkEgress,
|
|
46
|
+
sandboxName: input.sandboxName,
|
|
47
|
+
workerEnvironment: input.workerEnvironment,
|
|
48
|
+
omitPromptArgument: input.omitPromptArgument,
|
|
49
|
+
safehouseAddDirs: input.runner === "safehouse" ? resolveSafehouseAddDirs(input.worktreeDir) : undefined,
|
|
50
|
+
safehouseAgentAddDirs: input.runner === "safehouse" ? (input.taskSourceWritePaths ?? []) : undefined,
|
|
51
|
+
safehouseEnableFeatures: input.runner === "safehouse" ? input.safehouseEnableFeatures : undefined,
|
|
52
|
+
// Safehouse rejects nonexistent --add-dirs-ro paths, so drop absent ones.
|
|
53
|
+
safehouseAgentAddDirsReadOnly: input.runner === "safehouse" ? (input.readOnlyDirs ?? []).filter(existsSync) : undefined,
|
|
54
|
+
safehouseAgentIntegration,
|
|
55
|
+
}),
|
|
56
|
+
cleanup,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
cleanup();
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
58
63
|
}
|
|
59
64
|
/**
|
|
60
65
|
* Filesystem paths the safehouse sandbox must be granted (read/write) beyond
|
|
@@ -67,28 +72,38 @@ export function composeAgentLaunch(input) {
|
|
|
67
72
|
* whose store lives outside the worktree tree (e.g. graft's `~/carrot/.git`)
|
|
68
73
|
* gets git access. This is the path the bare cwd grant fundamentally cannot
|
|
69
74
|
* cover, and the reason this resolution exists.
|
|
70
|
-
* Gated to the safehouse runner at the call site
|
|
71
|
-
*
|
|
72
|
-
* don't use it). Deduped defensively in case git resolves either path to the
|
|
73
|
-
* same directory in an unusual checkout shape.
|
|
75
|
+
* Gated to the safehouse runner at the call site. Deduped defensively in case
|
|
76
|
+
* git resolves either path to the same directory in an unusual checkout shape.
|
|
74
77
|
*/
|
|
75
78
|
function resolveSafehouseAddDirs(worktreeDir) {
|
|
76
79
|
return [...new Set([worktreeDir, resolveGitCommonDir(worktreeDir)])];
|
|
77
80
|
}
|
|
78
|
-
function safehouseAgentIntegrationFor(
|
|
79
|
-
if (workspaceKind !== "cmux") {
|
|
81
|
+
function safehouseAgentIntegrationFor(input) {
|
|
82
|
+
if (input.workspaceKind !== "cmux") {
|
|
80
83
|
return undefined;
|
|
81
84
|
}
|
|
82
|
-
const agentCommandName = inferAgentCommandName(definition.cmd);
|
|
85
|
+
const agentCommandName = inferAgentCommandName(input.definition.cmd);
|
|
83
86
|
const isClaudeAgent = agentCommandName === "claude";
|
|
84
87
|
const cmuxIntegration = resolveSafehouseCmuxIntegration();
|
|
85
88
|
if (isClaudeAgent) {
|
|
86
89
|
warnOnCmuxIntegrationDrift({ unreviewedEnvNames: cmuxIntegration.unreviewedEnvNames });
|
|
87
90
|
}
|
|
91
|
+
const relocatedCmuxHooksHome = stageSafehouseCmuxHooksHome({
|
|
92
|
+
agent: agentCommandName,
|
|
93
|
+
task: input.task,
|
|
94
|
+
homeDir: input.homeDir,
|
|
95
|
+
});
|
|
88
96
|
return {
|
|
89
97
|
addDirsReadOnly: cmuxIntegration.addDirsReadOnly,
|
|
90
98
|
envPass: cmuxIntegration.envPass,
|
|
91
|
-
commandPreludes:
|
|
99
|
+
commandPreludes: [
|
|
100
|
+
...(isClaudeAgent ? [cmuxIntegration.claudeCommandPrelude] : []),
|
|
101
|
+
...(relocatedCmuxHooksHome === undefined
|
|
102
|
+
? []
|
|
103
|
+
: [
|
|
104
|
+
`export ${relocatedCmuxHooksHome.configDirEnv.name}=${shellSingleQuote(relocatedCmuxHooksHome.configDirEnv.value)}`,
|
|
105
|
+
]),
|
|
106
|
+
],
|
|
92
107
|
...(isClaudeAgent
|
|
93
108
|
? {
|
|
94
109
|
agentArgs: [
|
|
@@ -97,8 +112,44 @@ function safehouseAgentIntegrationFor(workspaceKind, definition) {
|
|
|
97
112
|
],
|
|
98
113
|
}
|
|
99
114
|
: {}),
|
|
115
|
+
...(relocatedCmuxHooksHome === undefined
|
|
116
|
+
? {}
|
|
117
|
+
: {
|
|
118
|
+
addDirs: [relocatedCmuxHooksHome.configDir],
|
|
119
|
+
writeBackFiles: relocatedCmuxHooksHome.writeBackFiles,
|
|
120
|
+
teardownPaths: [relocatedCmuxHooksHome.parentDir],
|
|
121
|
+
}),
|
|
100
122
|
};
|
|
101
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Relocate + seed a writable config home for a cmux-hosted agent whose hook
|
|
126
|
+
* activation is file-driven (codex reads `$CODEX_HOME/config.toml` +
|
|
127
|
+
* `hooks.json`, unlike Claude's `--settings` flag) and best-effort install
|
|
128
|
+
* cmux's lifecycle hooks into it. Undefined for agents with no registered
|
|
129
|
+
* relocation (claude), which keep reporting status via `--settings` instead.
|
|
130
|
+
*/
|
|
131
|
+
function stageSafehouseCmuxHooksHome(input) {
|
|
132
|
+
const relocation = agentConfigRelocation(input.agent);
|
|
133
|
+
if (relocation === undefined) {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
const parentDir = mkdtempSync(path.join(os.tmpdir(), `groundcrew-safehouse-${input.task}-`));
|
|
137
|
+
try {
|
|
138
|
+
const staged = stageRelocatedAgentConfigHome({
|
|
139
|
+
agent: input.agent,
|
|
140
|
+
relocation,
|
|
141
|
+
parentDir,
|
|
142
|
+
sourceConfigDir: readEnvironmentVariable(relocation.configDirEnv) ??
|
|
143
|
+
path.join(input.homeDir, relocation.sourceHomeRelativeDir),
|
|
144
|
+
});
|
|
145
|
+
installCmuxAgentHooks({ agent: input.agent, configDir: staged.configDir });
|
|
146
|
+
return { ...staged, parentDir };
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
rmSync(parentDir, { recursive: true, force: true });
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
102
153
|
function warnOnCmuxIntegrationDrift(input) {
|
|
103
154
|
for (const warningLine of safehouseCmuxIntegrationWarningLines({
|
|
104
155
|
commandName: "groundcrew",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort `cmux hooks <agent> install --yes` against a relocated,
|
|
3
|
+
* writable config home (e.g. codex's `CODEX_HOME`) so a file-driven agent —
|
|
4
|
+
* whose hook activation reads `$CODEX_HOME/config.toml` + `hooks.json` rather
|
|
5
|
+
* than an `agentArgs` flag — reports its lifecycle to the cmux sidebar the
|
|
6
|
+
* same way Claude already does via `--settings`.
|
|
7
|
+
*
|
|
8
|
+
* Never throws: an agent with no registered config relocation, a missing
|
|
9
|
+
* `cmux` CLI, or a non-zero install exit all degrade to that agent simply
|
|
10
|
+
* emitting no live status — never to a failed launch. A short timeout keeps a
|
|
11
|
+
* hung `cmux` binary from stalling every launch on this agent.
|
|
12
|
+
*/
|
|
13
|
+
export declare function installCmuxAgentHooks(input: {
|
|
14
|
+
agent: string;
|
|
15
|
+
configDir: string;
|
|
16
|
+
}): void;
|
|
17
|
+
//# sourceMappingURL=cmuxAgentHookInstall.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cmuxAgentHookInstall.d.ts","sourceRoot":"","sources":["../../src/lib/cmuxAgentHookInstall.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA0BvF"}
|