@clipboard-health/groundcrew 4.7.1 → 4.7.3
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/bin/run.js +2 -2
- package/bin/runCli.js +3 -3
- package/clearance-allow-hosts +2 -0
- package/dist/commands/init.js +4 -4
- package/dist/commands/upgrade.js +2 -2
- package/dist/lib/adapters/registry.js +2 -2
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +36 -36
- package/dist/lib/launchCommand.js +7 -7
- package/dist/lib/npmGlobal.d.ts +1 -1
- package/dist/lib/npmGlobal.d.ts.map +1 -1
- package/dist/lib/npmGlobal.js +7 -7
- package/dist/lib/runState.js +7 -7
- package/dist/lib/stagedLaunch.js +5 -5
- package/dist/lib/util.d.ts +1 -1
- package/dist/lib/util.d.ts.map +1 -1
- package/dist/lib/util.js +4 -4
- package/dist/lib/worktrees.js +18 -18
- package/dist/lib/xdg.js +5 -5
- package/package.json +2 -2
package/bin/run.js
CHANGED
package/bin/runCli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { constants as osConstants } from "node:os";
|
|
4
|
-
import
|
|
4
|
+
import path from "node:path";
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -14,13 +14,13 @@ import { pathToFileURL } from "node:url";
|
|
|
14
14
|
* @param {string} name
|
|
15
15
|
*/
|
|
16
16
|
export async function runCli(packageRoot, name) {
|
|
17
|
-
const compiledPath = join(packageRoot, "dist", `${name}.js`);
|
|
17
|
+
const compiledPath = path.join(packageRoot, "dist", `${name}.js`);
|
|
18
18
|
if (existsSync(compiledPath)) {
|
|
19
19
|
await import(pathToFileURL(compiledPath).href);
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const sourcePath = join(packageRoot, "src", `${name}.ts`);
|
|
23
|
+
const sourcePath = path.join(packageRoot, "src", `${name}.ts`);
|
|
24
24
|
const result = spawnSync(process.execPath, [sourcePath, ...process.argv.slice(2)], {
|
|
25
25
|
stdio: "inherit",
|
|
26
26
|
});
|
package/clearance-allow-hosts
CHANGED
package/dist/commands/init.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `cp` dance documented in the README.
|
|
6
6
|
*/
|
|
7
7
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
-
import
|
|
8
|
+
import path from "node:path";
|
|
9
9
|
import { LOCAL_RUNNER_SETTINGS } from "../lib/config.js";
|
|
10
10
|
import { shellSingleQuote } from "../lib/shell.js";
|
|
11
11
|
import { log, writeOutput } from "../lib/util.js";
|
|
@@ -28,7 +28,7 @@ export function initConfig(options = {}) {
|
|
|
28
28
|
log(`[dry-run] would write ${destination}`);
|
|
29
29
|
return { destination, outcome: "dry-run-would-write" };
|
|
30
30
|
}
|
|
31
|
-
mkdirSync(dirname(destination), { recursive: true });
|
|
31
|
+
mkdirSync(path.dirname(destination), { recursive: true });
|
|
32
32
|
writeFileSync(destination, renderConfig(source, options));
|
|
33
33
|
log(`[wrote] ${destination}`);
|
|
34
34
|
return { destination, outcome: "wrote" };
|
|
@@ -117,12 +117,12 @@ function destinationFor(args) {
|
|
|
117
117
|
if (args.scope === "global") {
|
|
118
118
|
return xdgConfigPath("groundcrew", CONFIG_FILE_NAME);
|
|
119
119
|
}
|
|
120
|
-
return resolve(args.cwd, CONFIG_FILE_NAME);
|
|
120
|
+
return path.resolve(args.cwd, CONFIG_FILE_NAME);
|
|
121
121
|
}
|
|
122
122
|
function resolveExamplePath() {
|
|
123
123
|
// `init.ts` lives at src/commands/init.ts in source and dist/commands/init.js
|
|
124
124
|
// after build; the example ships at the package root in both cases.
|
|
125
|
-
return resolve(import.meta.dirname, "..", "..", EXAMPLE_FILE_NAME);
|
|
125
|
+
return path.resolve(import.meta.dirname, "..", "..", EXAMPLE_FILE_NAME);
|
|
126
126
|
}
|
|
127
127
|
function readOptionValue(argv, index, flag) {
|
|
128
128
|
const value = argv[index + 1];
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import
|
|
2
|
+
import path from "node:path";
|
|
3
3
|
import { runCommand } from "../lib/commandRunner.js";
|
|
4
4
|
import { which } from "../lib/host.js";
|
|
5
5
|
import { classifyInstall, createDefaultNpmSpawner, detectInstallPath, detectIsSymlink, detectNpmRootGlobal, runNpmInstallGlobal, } from "../lib/npmGlobal.js";
|
|
@@ -137,7 +137,7 @@ export async function createDefaultUpgradeCliOptions(args) {
|
|
|
137
137
|
function readInstalledVersionFromDisk(installPath) {
|
|
138
138
|
let raw;
|
|
139
139
|
try {
|
|
140
|
-
raw = readFileSync(join(installPath, "package.json"), "utf8");
|
|
140
|
+
raw = readFileSync(path.join(installPath, "package.json"), "utf8");
|
|
141
141
|
}
|
|
142
142
|
catch {
|
|
143
143
|
return undefined;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { __rewriteRelativeImportExtension } from "tslib";
|
|
13
13
|
import { readdirSync } from "node:fs";
|
|
14
|
-
import
|
|
14
|
+
import path from "node:path";
|
|
15
15
|
import { z } from "zod";
|
|
16
16
|
/**
|
|
17
17
|
* Pure logic: given a list of subdirectory names and an async loader, build a
|
|
@@ -62,7 +62,7 @@ const here = import.meta.dirname;
|
|
|
62
62
|
async function defaultImportLoader(directoryName) {
|
|
63
63
|
// Resolve relative to this module's directory. tsx maps `.js` → `.ts` in dev;
|
|
64
64
|
// prod Node ESM resolves the actual `.js` file.
|
|
65
|
-
const modulePath = resolve(here, directoryName, "index.js");
|
|
65
|
+
const modulePath = path.resolve(here, directoryName, "index.js");
|
|
66
66
|
// oxlint-disable-next-line typescript/no-unsafe-assignment -- dynamic import return type is `any`; adapter contract is enforced by buildRegistry
|
|
67
67
|
const mod = await import(__rewriteRelativeImportExtension(modulePath));
|
|
68
68
|
return mod.default;
|
package/dist/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { __rewriteRelativeImportExtension } from "tslib";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import
|
|
4
|
+
import path from "node:path";
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
import { cosmiconfig } from "cosmiconfig";
|
|
7
7
|
import { debug, log, readEnvironmentVariable, setLogFile } from "./util.js";
|
|
@@ -88,7 +88,7 @@ function expandHome(p) {
|
|
|
88
88
|
return homedir();
|
|
89
89
|
}
|
|
90
90
|
if (p.startsWith("~/")) {
|
|
91
|
-
return resolve(homedir(), p.slice(2));
|
|
91
|
+
return path.resolve(homedir(), p.slice(2));
|
|
92
92
|
}
|
|
93
93
|
return p;
|
|
94
94
|
}
|
|
@@ -98,45 +98,45 @@ function fail(message) {
|
|
|
98
98
|
function isNonEmptyString(value) {
|
|
99
99
|
return typeof value === "string" && value.length > 0;
|
|
100
100
|
}
|
|
101
|
-
function requireString(value,
|
|
101
|
+
function requireString(value, configKey) {
|
|
102
102
|
if (!isNonEmptyString(value)) {
|
|
103
|
-
fail(`${
|
|
103
|
+
fail(`${configKey} must be a non-empty string (got ${JSON.stringify(value)})`);
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
function requirePositiveInt(value,
|
|
106
|
+
function requirePositiveInt(value, configKey, min = 1) {
|
|
107
107
|
if (typeof value !== "number" || !Number.isInteger(value) || value < min) {
|
|
108
|
-
fail(`${
|
|
108
|
+
fail(`${configKey} must be an integer ≥ ${min} (got ${JSON.stringify(value)})`);
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
-
function requirePercent(value,
|
|
111
|
+
function requirePercent(value, configKey) {
|
|
112
112
|
if (typeof value !== "number" ||
|
|
113
113
|
!Number.isFinite(value) ||
|
|
114
114
|
value <= PERCENT_MIN_EXCLUSIVE ||
|
|
115
115
|
value > PERCENT_MAX) {
|
|
116
|
-
fail(`${
|
|
116
|
+
fail(`${configKey} must be a finite number in (0, 100] (got ${JSON.stringify(value)})`);
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
function cloneModelDefinition(definition) {
|
|
120
120
|
return structuredClone(definition);
|
|
121
121
|
}
|
|
122
|
-
function normalizeOptionalString(value,
|
|
122
|
+
function normalizeOptionalString(value, configKey) {
|
|
123
123
|
if (value === undefined) {
|
|
124
124
|
return undefined;
|
|
125
125
|
}
|
|
126
126
|
if (typeof value !== "string" || value.trim().length === 0) {
|
|
127
|
-
fail(`${
|
|
127
|
+
fail(`${configKey} must be a non-empty string`);
|
|
128
128
|
}
|
|
129
129
|
return value.trim();
|
|
130
130
|
}
|
|
131
131
|
const ENV_VAR_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
132
132
|
function validatePreLaunchEnv(modelName, value) {
|
|
133
|
-
const
|
|
133
|
+
const configPath = `models.definitions.${modelName}.preLaunchEnv`;
|
|
134
134
|
if (!Array.isArray(value)) {
|
|
135
|
-
fail(`${
|
|
135
|
+
fail(`${configPath} must be an array of env var names (got ${JSON.stringify(value)})`);
|
|
136
136
|
}
|
|
137
137
|
for (const [index, entry] of value.entries()) {
|
|
138
138
|
if (typeof entry !== "string" || !ENV_VAR_NAME_PATTERN.test(entry)) {
|
|
139
|
-
fail(`${
|
|
139
|
+
fail(`${configPath}[${index}] must be a POSIX env var name matching ${ENV_VAR_NAME_PATTERN.source} (got ${JSON.stringify(entry)})`);
|
|
140
140
|
}
|
|
141
141
|
// Build secrets are sourced into the host launch shell, forwarded only to
|
|
142
142
|
// the Safehouse *setup* wrap, and `unset` on the host before the agent
|
|
@@ -144,7 +144,7 @@ function validatePreLaunchEnv(modelName, value) {
|
|
|
144
144
|
// fail loudly so the operator picks a different name (or removes the
|
|
145
145
|
// entry) instead of debugging a missing env var at runtime.
|
|
146
146
|
if (BUILD_SECRET_NAMES.includes(entry)) {
|
|
147
|
-
fail(`${
|
|
147
|
+
fail(`${configPath}[${index}] cannot be a BUILD_SECRET_NAMES entry (${BUILD_SECRET_NAMES.join(", ")}); ` +
|
|
148
148
|
"those are unset on the host before the agent wrap is exec'd, so forwarding them via --env-pass would be a no-op.");
|
|
149
149
|
}
|
|
150
150
|
}
|
|
@@ -165,52 +165,52 @@ export function hasPreLaunchEnv(definition) {
|
|
|
165
165
|
function isWorkspaceKindSetting(value) {
|
|
166
166
|
return (typeof value === "string" && WORKSPACE_KIND_SETTINGS.includes(value));
|
|
167
167
|
}
|
|
168
|
-
function normalizeWorkspaceKind(value,
|
|
168
|
+
function normalizeWorkspaceKind(value, configKey) {
|
|
169
169
|
if (value === undefined) {
|
|
170
170
|
return undefined;
|
|
171
171
|
}
|
|
172
172
|
if (!isWorkspaceKindSetting(value)) {
|
|
173
|
-
fail(`${
|
|
173
|
+
fail(`${configKey} must be one of ${WORKSPACE_KIND_SETTINGS.join(", ")} (got ${JSON.stringify(value)})`);
|
|
174
174
|
}
|
|
175
175
|
return value;
|
|
176
176
|
}
|
|
177
177
|
function isLocalRunnerSetting(value) {
|
|
178
178
|
return typeof value === "string" && LOCAL_RUNNER_SETTINGS.includes(value);
|
|
179
179
|
}
|
|
180
|
-
function normalizeLocalRunner(value,
|
|
180
|
+
function normalizeLocalRunner(value, configKey) {
|
|
181
181
|
if (value === undefined) {
|
|
182
182
|
return undefined;
|
|
183
183
|
}
|
|
184
184
|
if (!isLocalRunnerSetting(value)) {
|
|
185
|
-
fail(`${
|
|
185
|
+
fail(`${configKey} must be one of ${LOCAL_RUNNER_SETTINGS.join(", ")} (got ${JSON.stringify(value)})`);
|
|
186
186
|
}
|
|
187
187
|
return value;
|
|
188
188
|
}
|
|
189
|
-
function normalizeSandbox(value,
|
|
189
|
+
function normalizeSandbox(value, configKey) {
|
|
190
190
|
if (!isPlainObject(value)) {
|
|
191
|
-
fail(`${
|
|
191
|
+
fail(`${configKey} must be an object`);
|
|
192
192
|
}
|
|
193
193
|
if (Object.hasOwn(value, "template")) {
|
|
194
|
-
failRemovedConfigKey(`${
|
|
194
|
+
failRemovedConfigKey(`${configKey}.template`, "Groundcrew no longer creates or re-templates sdx sandboxes.");
|
|
195
195
|
}
|
|
196
196
|
if (Object.hasOwn(value, "kits")) {
|
|
197
|
-
failRemovedConfigKey(`${
|
|
197
|
+
failRemovedConfigKey(`${configKey}.kits`, "Groundcrew no longer creates sdx sandboxes or applies sandbox kits.");
|
|
198
198
|
}
|
|
199
199
|
const { agent, setupCommand } = value;
|
|
200
|
-
requireString(agent, `${
|
|
200
|
+
requireString(agent, `${configKey}.agent`);
|
|
201
201
|
const trimmedAgent = agent.trim();
|
|
202
202
|
if (trimmedAgent.length === 0) {
|
|
203
|
-
fail(`${
|
|
203
|
+
fail(`${configKey}.agent must be a non-empty string (got ${JSON.stringify(agent)})`);
|
|
204
204
|
}
|
|
205
205
|
const sandbox = { agent: trimmedAgent };
|
|
206
|
-
const normalizedSetup = normalizeOptionalString(setupCommand, `${
|
|
206
|
+
const normalizedSetup = normalizeOptionalString(setupCommand, `${configKey}.setupCommand`);
|
|
207
207
|
if (normalizedSetup !== undefined) {
|
|
208
208
|
sandbox.setupCommand = normalizedSetup;
|
|
209
209
|
}
|
|
210
210
|
return sandbox;
|
|
211
211
|
}
|
|
212
|
-
function failRemovedConfigKey(
|
|
213
|
-
fail(`${
|
|
212
|
+
function failRemovedConfigKey(configKey, reason) {
|
|
213
|
+
fail(`${configKey} is no longer supported: ${reason} ` +
|
|
214
214
|
"Provision and manage the sandbox yourself with `sbx` (for example `sbx create --name groundcrew-<agent> <agent> <projectDir>`), then keep only `models.definitions.<model>.sandbox.agent` plus optional `setupCommand` in crew.config.ts.");
|
|
215
215
|
}
|
|
216
216
|
function failIfLegacyModelKeys(name, override) {
|
|
@@ -322,9 +322,9 @@ function mergeDefinitions(user) {
|
|
|
322
322
|
function isPlainObject(value) {
|
|
323
323
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
324
324
|
}
|
|
325
|
-
function requireObject(value,
|
|
325
|
+
function requireObject(value, configKey) {
|
|
326
326
|
if (!isPlainObject(value)) {
|
|
327
|
-
fail(`${
|
|
327
|
+
fail(`${configKey} must be an object (got ${JSON.stringify(value)})`);
|
|
328
328
|
}
|
|
329
329
|
}
|
|
330
330
|
function failOnLegacyLinearShape(user) {
|
|
@@ -362,12 +362,12 @@ function normalizeSources(raw) {
|
|
|
362
362
|
}
|
|
363
363
|
const names = new Map();
|
|
364
364
|
for (const [index, entry] of raw.entries()) {
|
|
365
|
-
const
|
|
365
|
+
const configPath = `sources[${index}]`;
|
|
366
366
|
if (!isPlainObject(entry)) {
|
|
367
|
-
fail(`${
|
|
367
|
+
fail(`${configPath} must be an object`);
|
|
368
368
|
}
|
|
369
369
|
const { kind, name } = entry;
|
|
370
|
-
requireString(kind, `${
|
|
370
|
+
requireString(kind, `${configPath}.kind`);
|
|
371
371
|
// Per-adapter Zod validation runs in `buildSources`. Here we check name
|
|
372
372
|
// uniqueness — the Board composer relies on it for writeback routing.
|
|
373
373
|
// When `name` is omitted, the adapter's runtime default is `kind` (the
|
|
@@ -375,14 +375,14 @@ function normalizeSources(raw) {
|
|
|
375
375
|
// dedup on the effective runtime name to catch e.g. two `{kind: "linear"}`
|
|
376
376
|
// entries that would both produce a source named `"linear"`.
|
|
377
377
|
if (name !== undefined) {
|
|
378
|
-
requireString(name, `${
|
|
378
|
+
requireString(name, `${configPath}.name`);
|
|
379
379
|
}
|
|
380
380
|
/* v8 ignore next @preserve -- both `name`-set and `name`-unset paths are covered by separate dedup tests; coverage for the fallback's `kind` arm only fires when both entries in the dedup set come from `name`, which the second test already covers */
|
|
381
381
|
const effectiveName = name ?? kind;
|
|
382
382
|
const previous = names.get(effectiveName);
|
|
383
383
|
if (previous !== undefined) {
|
|
384
384
|
/* v8 ignore next 3 @preserve -- the `name === undefined` ternary arm requires two unnamed entries colliding; we keep the conditional for the better error message but only one path is exercised in tests */
|
|
385
|
-
fail(`${
|
|
385
|
+
fail(`${configPath} would produce a source named "${effectiveName}" (from ${name === undefined ? "default `kind` since `name` is omitted" : "`name`"}), duplicating sources[${previous}]. Configure distinct \`name\` fields.`);
|
|
386
386
|
}
|
|
387
387
|
names.set(effectiveName, index);
|
|
388
388
|
}
|
|
@@ -567,12 +567,12 @@ async function loadAt(filepath) {
|
|
|
567
567
|
return result;
|
|
568
568
|
}
|
|
569
569
|
function findXdgConfigFile() {
|
|
570
|
-
return XDG_FALLBACK_NAMES.map((name) => xdgConfigPath("groundcrew", name)).find((
|
|
570
|
+
return XDG_FALLBACK_NAMES.map((name) => xdgConfigPath("groundcrew", name)).find((p) => existsSync(p));
|
|
571
571
|
}
|
|
572
572
|
async function discoverUserConfig() {
|
|
573
573
|
const override = readEnvironmentVariable("GROUNDCREW_CONFIG");
|
|
574
574
|
if (override !== undefined && override.length > 0) {
|
|
575
|
-
const overridePath = resolve(override);
|
|
575
|
+
const overridePath = path.resolve(override);
|
|
576
576
|
if (!existsSync(overridePath)) {
|
|
577
577
|
fail(`GROUNDCREW_CONFIG=${overridePath} not found`);
|
|
578
578
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import
|
|
2
|
+
import path from "node:path";
|
|
3
3
|
import { BUILD_SECRET_NAMES, hasPreLaunchEnv, } from "./config.js";
|
|
4
4
|
import { shellSingleQuote } from "./shell.js";
|
|
5
5
|
export { shellSingleQuote } from "./shell.js";
|
|
@@ -22,7 +22,7 @@ export function resolveSafehouseClearancePath(baseUrl = import.meta.url) {
|
|
|
22
22
|
throw new Error("@clipboard-health/clearance is required by @clipboard-health/groundcrew but could not be resolved. " +
|
|
23
23
|
"Install it alongside groundcrew (for example: `npm install -g @clipboard-health/clearance`).", { cause: error });
|
|
24
24
|
}
|
|
25
|
-
return resolve(dirname(clearancePackageJson), "safehouse", "safehouse-clearance");
|
|
25
|
+
return path.resolve(path.dirname(clearancePackageJson), "safehouse", "safehouse-clearance");
|
|
26
26
|
}
|
|
27
27
|
const SAFEHOUSE_CLEARANCE_WRAPPER_PATH = resolveSafehouseClearancePath();
|
|
28
28
|
/**
|
|
@@ -155,7 +155,7 @@ function safehouseProfileCommandName(agentCmd) {
|
|
|
155
155
|
if (commandToken === undefined) {
|
|
156
156
|
throw new Error(`Cannot infer Safehouse agent profile command from model cmd ${JSON.stringify(agentCmd)}.`);
|
|
157
157
|
}
|
|
158
|
-
const commandName = basename(commandToken);
|
|
158
|
+
const commandName = path.basename(commandToken);
|
|
159
159
|
if (commandName === "." ||
|
|
160
160
|
commandName === ".." ||
|
|
161
161
|
commandName.startsWith("-") ||
|
|
@@ -211,7 +211,7 @@ function shouldWrapWithSafehouse(arguments_) {
|
|
|
211
211
|
* host shell because there is no groundcrew-managed sandbox to run them inside.
|
|
212
212
|
*/
|
|
213
213
|
function buildUnwrappedHostLaunchCommand(arguments_) {
|
|
214
|
-
const promptDir = dirname(arguments_.promptFile);
|
|
214
|
+
const promptDir = path.dirname(arguments_.promptFile);
|
|
215
215
|
const agentCmd = renderAgentCommand({
|
|
216
216
|
agentCmd: arguments_.definition.cmd,
|
|
217
217
|
worktreeDir: arguments_.worktreeDir,
|
|
@@ -243,7 +243,7 @@ function buildUnwrappedHostLaunchCommand(arguments_) {
|
|
|
243
243
|
* 2. **Agent wrap**: `safehouse-clearance "$shim" -c '<exec agent>' sh "$_p"`
|
|
244
244
|
* where `$shim` is a `mktemp`-d symlink to `/bin/sh` named after the
|
|
245
245
|
* agent (e.g. `claude`). Safehouse selects the matching agent profile
|
|
246
|
-
* from the wrapped command's basename (`claude-code.sb` etc.) without
|
|
246
|
+
* from the wrapped command's path.basename (`claude-code.sb` etc.) without
|
|
247
247
|
* needing every agent profile enabled globally.
|
|
248
248
|
*
|
|
249
249
|
* Host ordering matters: when a `preLaunch` hook is present, inherited
|
|
@@ -263,7 +263,7 @@ function buildUnwrappedHostLaunchCommand(arguments_) {
|
|
|
263
263
|
* reach the profile-neutral setup phase.
|
|
264
264
|
*/
|
|
265
265
|
function buildSafehouseLaunchCommand(arguments_) {
|
|
266
|
-
const promptDir = dirname(arguments_.promptFile);
|
|
266
|
+
const promptDir = path.dirname(arguments_.promptFile);
|
|
267
267
|
const safehouseCommandName = safehouseProfileCommandName(arguments_.definition.cmd);
|
|
268
268
|
const agentCmd = renderAgentCommand({
|
|
269
269
|
agentCmd: arguments_.definition.cmd,
|
|
@@ -315,7 +315,7 @@ function buildSdxLaunchCommand(arguments_) {
|
|
|
315
315
|
if (arguments_.sandboxName === undefined || arguments_.definition.sandbox === undefined) {
|
|
316
316
|
throw new Error("buildLaunchCommand: runner='sdx' requires sandboxName and a model `sandbox` config block (set sandbox.agent on the model in config.ts).");
|
|
317
317
|
}
|
|
318
|
-
const promptDir = dirname(arguments_.promptFile);
|
|
318
|
+
const promptDir = path.dirname(arguments_.promptFile);
|
|
319
319
|
const agentCmd = renderAgentCommand({
|
|
320
320
|
agentCmd: arguments_.definition.cmd,
|
|
321
321
|
worktreeDir: arguments_.worktreeDir,
|
package/dist/lib/npmGlobal.d.ts
CHANGED
|
@@ -25,6 +25,6 @@ export declare function runNpmInstallGlobal(options: RunNpmInstallOptions): Prom
|
|
|
25
25
|
export declare function detectInstallPath(cliMetaUrl: string): string;
|
|
26
26
|
export type NpmRootRunner = (command: string, args: readonly string[]) => string;
|
|
27
27
|
export declare function detectNpmRootGlobal(npmBin: string, runner: NpmRootRunner): string | undefined;
|
|
28
|
-
export declare function detectIsSymlink(
|
|
28
|
+
export declare function detectIsSymlink(filePath: string): boolean;
|
|
29
29
|
export declare function createDefaultNpmSpawner(): NpmSpawner;
|
|
30
30
|
//# sourceMappingURL=npmGlobal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"npmGlobal.d.ts","sourceRoot":"","sources":["../../src/lib/npmGlobal.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,CAAC;AAE9E,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACtC;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,WAAW,CAY5E;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAEjG,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,UAAU,CAAC;CACrB;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,YAAY,CAAC,CAQ9F;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC;AAEjF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAM7F;AAED,wBAAgB,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"npmGlobal.d.ts","sourceRoot":"","sources":["../../src/lib/npmGlobal.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,CAAC;AAE9E,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACtC;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,WAAW,CAY5E;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAEjG,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,UAAU,CAAC;CACrB;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,YAAY,CAAC,CAQ9F;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC;AAEjF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAM7F;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAMzD;AAED,wBAAgB,uBAAuB,IAAI,UAAU,CAmBpD"}
|
package/dist/lib/npmGlobal.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { lstatSync } from "node:fs";
|
|
3
|
-
import
|
|
3
|
+
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
export function classifyInstall(options) {
|
|
6
6
|
const { installPath, npmRootGlobal, isSymlink } = options;
|
|
7
|
-
if (npmRootGlobal !== undefined && installPath.startsWith(`${npmRootGlobal}${sep}`)) {
|
|
7
|
+
if (npmRootGlobal !== undefined && installPath.startsWith(`${npmRootGlobal}${path.sep}`)) {
|
|
8
8
|
return isSymlink(installPath) ? "linked" : "global";
|
|
9
9
|
}
|
|
10
|
-
if (installPath.includes(`${sep}_npx${sep}`)) {
|
|
10
|
+
if (installPath.includes(`${path.sep}_npx${path.sep}`)) {
|
|
11
11
|
return "npx";
|
|
12
12
|
}
|
|
13
|
-
if (installPath.includes(`${sep}node_modules${sep}`)) {
|
|
13
|
+
if (installPath.includes(`${path.sep}node_modules${path.sep}`)) {
|
|
14
14
|
return "project";
|
|
15
15
|
}
|
|
16
16
|
return "unknown";
|
|
@@ -25,7 +25,7 @@ export async function runNpmInstallGlobal(options) {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
export function detectInstallPath(cliMetaUrl) {
|
|
28
|
-
return dirname(dirname(fileURLToPath(cliMetaUrl)));
|
|
28
|
+
return path.dirname(path.dirname(fileURLToPath(cliMetaUrl)));
|
|
29
29
|
}
|
|
30
30
|
export function detectNpmRootGlobal(npmBin, runner) {
|
|
31
31
|
try {
|
|
@@ -35,9 +35,9 @@ export function detectNpmRootGlobal(npmBin, runner) {
|
|
|
35
35
|
return undefined;
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
export function detectIsSymlink(
|
|
38
|
+
export function detectIsSymlink(filePath) {
|
|
39
39
|
try {
|
|
40
|
-
return lstatSync(
|
|
40
|
+
return lstatSync(filePath).isSymbolicLink();
|
|
41
41
|
}
|
|
42
42
|
catch {
|
|
43
43
|
return false;
|
package/dist/lib/runState.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
-
import
|
|
2
|
+
import path from "node:path";
|
|
3
3
|
const TICKET_RE = /^[a-z][\da-z]*-\d+$/;
|
|
4
4
|
const RUN_STATE_DIRECTORY_NAME = "runs";
|
|
5
5
|
function ticketKey(ticket) {
|
|
@@ -10,10 +10,10 @@ function ticketKey(ticket) {
|
|
|
10
10
|
return normalized;
|
|
11
11
|
}
|
|
12
12
|
export function runStateDirectory(config) {
|
|
13
|
-
return resolve(dirname(config.logging.file), RUN_STATE_DIRECTORY_NAME);
|
|
13
|
+
return path.resolve(path.dirname(config.logging.file), RUN_STATE_DIRECTORY_NAME);
|
|
14
14
|
}
|
|
15
15
|
export function runStatePath(config, ticket) {
|
|
16
|
-
return resolve(runStateDirectory(config), `${ticketKey(ticket)}.json`);
|
|
16
|
+
return path.resolve(runStateDirectory(config), `${ticketKey(ticket)}.json`);
|
|
17
17
|
}
|
|
18
18
|
function nowIso() {
|
|
19
19
|
return new Date().toISOString();
|
|
@@ -80,11 +80,11 @@ function parseRunState(value) {
|
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
82
|
function writeState(config, state) {
|
|
83
|
-
const
|
|
84
|
-
mkdirSync(dirname(
|
|
85
|
-
const tmpPath = `${
|
|
83
|
+
const statePath = runStatePath(config, state.ticket);
|
|
84
|
+
mkdirSync(path.dirname(statePath), { recursive: true });
|
|
85
|
+
const tmpPath = `${statePath}.${process.pid}.tmp`;
|
|
86
86
|
writeFileSync(tmpPath, `${JSON.stringify(state, undefined, 2)}\n`, { mode: 0o600 });
|
|
87
|
-
renameSync(tmpPath,
|
|
87
|
+
renameSync(tmpPath, statePath);
|
|
88
88
|
}
|
|
89
89
|
export function readRunState(config, ticket) {
|
|
90
90
|
let raw;
|
package/dist/lib/stagedLaunch.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
|
-
import
|
|
3
|
+
import path from "node:path";
|
|
4
4
|
import { BUILD_SECRET_NAMES } from "./config.js";
|
|
5
5
|
import { shellSingleQuote } from "./launchCommand.js";
|
|
6
6
|
import { readEnvironmentVariable } from "./util.js";
|
|
@@ -13,8 +13,8 @@ function renderPromptTemplate(template, variables) {
|
|
|
13
13
|
.replaceAll("{{workspaceContinuationInstruction}}", variables.workspaceContinuationInstruction);
|
|
14
14
|
}
|
|
15
15
|
export function stagePromptText(input) {
|
|
16
|
-
const promptDir = mkdtempSync(join(tmpdir(), `${input.prefix}-${input.ticket}-`));
|
|
17
|
-
const promptFile = join(promptDir, "prompt.txt");
|
|
16
|
+
const promptDir = mkdtempSync(path.join(tmpdir(), `${input.prefix}-${input.ticket}-`));
|
|
17
|
+
const promptFile = path.join(promptDir, "prompt.txt");
|
|
18
18
|
writeFileSync(promptFile, input.text);
|
|
19
19
|
return { directory: promptDir, file: promptFile };
|
|
20
20
|
}
|
|
@@ -42,12 +42,12 @@ export function stageBuildSecrets(promptDir) {
|
|
|
42
42
|
if (lines.length === 0) {
|
|
43
43
|
return undefined;
|
|
44
44
|
}
|
|
45
|
-
const secretsFile = join(promptDir, "secrets.env");
|
|
45
|
+
const secretsFile = path.join(promptDir, "secrets.env");
|
|
46
46
|
writeFileSync(secretsFile, `${lines.join("\n")}\n`, { mode: 0o600 });
|
|
47
47
|
return secretsFile;
|
|
48
48
|
}
|
|
49
49
|
function stageLaunchScript(promptDir, command) {
|
|
50
|
-
const launcherFile = join(promptDir, "launch.sh");
|
|
50
|
+
const launcherFile = path.join(promptDir, "launch.sh");
|
|
51
51
|
writeFileSync(launcherFile, `#!/usr/bin/env bash\n${command}\n`, { mode: 0o700 });
|
|
52
52
|
return launcherFile;
|
|
53
53
|
}
|
package/dist/lib/util.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare function okMark(): string;
|
|
|
7
7
|
export declare function failMark(): string;
|
|
8
8
|
export declare function styleWarning(text: string): string;
|
|
9
9
|
export declare function styleDim(text: string): string;
|
|
10
|
-
export declare function setLogFile(
|
|
10
|
+
export declare function setLogFile(filePath: string | undefined): void;
|
|
11
11
|
export declare function withLogOutputSuppressed<T>(operation: () => Promise<T>): Promise<T>;
|
|
12
12
|
/** Important tier: always on the console (dimmed timestamp) and the log file. */
|
|
13
13
|
export declare function log(message: string): void;
|
package/dist/lib/util.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/lib/util.ts"],"names":[],"mappings":"AAIA,wBAAsB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3E;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAIlD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGhD;AAQD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE/C;AAED,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAWD,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AAQD,wBAAgB,UAAU,CAAC,
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/lib/util.ts"],"names":[],"mappings":"AAIA,wBAAsB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3E;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAIlD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGhD;AAQD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE/C;AAED,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAWD,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AAQD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAE7D;AAED,wBAAsB,uBAAuB,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAOxF;AAuBD,iFAAiF;AACjF,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAOzC;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAS3C;AAED,KAAK,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;AAUpF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAiBxF;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGxE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAMzF;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAcvF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAcnD"}
|
package/dist/lib/util.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { appendFileSync, mkdirSync } from "node:fs";
|
|
2
|
-
import
|
|
2
|
+
import path from "node:path";
|
|
3
3
|
import { styleText } from "node:util";
|
|
4
4
|
export async function sleep(ms, signal) {
|
|
5
5
|
if (signal?.aborted === true) {
|
|
@@ -62,8 +62,8 @@ export function styleDim(text) {
|
|
|
62
62
|
// loadConfig() resolves `logging.file`.
|
|
63
63
|
let logFilePath;
|
|
64
64
|
let suppressedLogDepth = 0;
|
|
65
|
-
export function setLogFile(
|
|
66
|
-
logFilePath =
|
|
65
|
+
export function setLogFile(filePath) {
|
|
66
|
+
logFilePath = filePath;
|
|
67
67
|
}
|
|
68
68
|
export async function withLogOutputSuppressed(operation) {
|
|
69
69
|
suppressedLogDepth += 1;
|
|
@@ -79,7 +79,7 @@ function appendLogLine(line) {
|
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
try {
|
|
82
|
-
mkdirSync(dirname(logFilePath), { recursive: true });
|
|
82
|
+
mkdirSync(path.dirname(logFilePath), { recursive: true });
|
|
83
83
|
appendFileSync(logFilePath, `${line}\n`);
|
|
84
84
|
}
|
|
85
85
|
catch {
|
package/dist/lib/worktrees.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { existsSync, readdirSync, rmSync } from "node:fs";
|
|
11
11
|
import { userInfo } from "node:os";
|
|
12
|
-
import
|
|
12
|
+
import path from "node:path";
|
|
13
13
|
import { runCommandAsync } from "./commandRunner.js";
|
|
14
14
|
import { resolveDefaultBranch } from "./defaultBranch.js";
|
|
15
15
|
import { debug, errorMessage, isVerbose } from "./util.js";
|
|
@@ -42,7 +42,7 @@ function repoDirFor(config, repository) {
|
|
|
42
42
|
if (!config.workspace.knownRepositories.includes(repository)) {
|
|
43
43
|
throw new Error(`Repository "${repository}" is not in workspace.knownRepositories: ${config.workspace.knownRepositories.join(", ")}`);
|
|
44
44
|
}
|
|
45
|
-
const repoDir = resolve(config.workspace.projectDir, repository);
|
|
45
|
+
const repoDir = path.resolve(config.workspace.projectDir, repository);
|
|
46
46
|
if (!existsSync(repoDir)) {
|
|
47
47
|
throw new Error(`Repository not found: ${repoDir}`);
|
|
48
48
|
}
|
|
@@ -51,14 +51,14 @@ function repoDirFor(config, repository) {
|
|
|
51
51
|
function basePaths(config, repository, ticket) {
|
|
52
52
|
// Tickets must match the same shape the worktree discovery regexes use,
|
|
53
53
|
// so create()/list()/findByTicket() agree on what's a valid worktree.
|
|
54
|
-
// This also rejects traversal tokens before they reach resolve().
|
|
54
|
+
// This also rejects traversal tokens before they reach path.resolve().
|
|
55
55
|
if (!TICKET_RE.test(ticket)) {
|
|
56
56
|
throw new Error(`Invalid ticket "${ticket}": must be a plain ticket id`);
|
|
57
57
|
}
|
|
58
|
-
const projectDir = resolve(config.workspace.projectDir);
|
|
58
|
+
const projectDir = path.resolve(config.workspace.projectDir);
|
|
59
59
|
const repoDir = repoDirFor(config, repository);
|
|
60
60
|
const hostWorktreeName = `${repository}-${ticket}`;
|
|
61
|
-
const hostWorktreeDir = resolve(projectDir, hostWorktreeName);
|
|
61
|
+
const hostWorktreeDir = path.resolve(projectDir, hostWorktreeName);
|
|
62
62
|
return {
|
|
63
63
|
projectDir,
|
|
64
64
|
repoDir,
|
|
@@ -122,17 +122,17 @@ async function createWorktree(config, spec, signal) {
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
function listWorktrees(config) {
|
|
125
|
-
const projectDir = resolve(config.workspace.projectDir);
|
|
125
|
+
const projectDir = path.resolve(config.workspace.projectDir);
|
|
126
126
|
const entries = [];
|
|
127
127
|
// Worktrees live at `projectDir/<repository>-<ticket>`. When `repository`
|
|
128
|
-
// contains a slash (e.g. "owner/repo"), `resolve()` nests one level
|
|
128
|
+
// contains a slash (e.g. "owner/repo"), `path.resolve()` nests one level
|
|
129
129
|
// deeper, so the worktree path is `projectDir/owner/repo-<ticket>`.
|
|
130
130
|
// Scan each known repository's parent directory rather than the project
|
|
131
131
|
// root, so nested worktrees are discovered alongside bare ones.
|
|
132
132
|
const reposByParent = new Map();
|
|
133
133
|
for (const repository of config.workspace.knownRepositories) {
|
|
134
134
|
const lastSlash = repository.lastIndexOf("/");
|
|
135
|
-
const parentDir = lastSlash === -1 ? projectDir : resolve(projectDir, repository.slice(0, lastSlash));
|
|
135
|
+
const parentDir = lastSlash === -1 ? projectDir : path.resolve(projectDir, repository.slice(0, lastSlash));
|
|
136
136
|
const basename = lastSlash === -1 ? repository : repository.slice(lastSlash + 1);
|
|
137
137
|
let repoByBasename = reposByParent.get(parentDir);
|
|
138
138
|
if (repoByBasename === undefined) {
|
|
@@ -170,7 +170,7 @@ function listWorktrees(config) {
|
|
|
170
170
|
repository,
|
|
171
171
|
ticket,
|
|
172
172
|
branchName: branchNameForTicket(ticket),
|
|
173
|
-
dir: resolve(parentDir, entry.name),
|
|
173
|
+
dir: path.resolve(parentDir, entry.name),
|
|
174
174
|
kind: "host",
|
|
175
175
|
});
|
|
176
176
|
}
|
|
@@ -178,8 +178,8 @@ function listWorktrees(config) {
|
|
|
178
178
|
return entries;
|
|
179
179
|
}
|
|
180
180
|
async function removeWorktree(config, entry, options) {
|
|
181
|
-
const projectDir = resolve(config.workspace.projectDir);
|
|
182
|
-
const repoDir = resolve(projectDir, entry.repository);
|
|
181
|
+
const projectDir = path.resolve(config.workspace.projectDir);
|
|
182
|
+
const repoDir = path.resolve(projectDir, entry.repository);
|
|
183
183
|
if (existsSync(entry.dir)) {
|
|
184
184
|
debug(`Removing worktree ${entry.dir}${options.force ? " (--force)" : ""}...`);
|
|
185
185
|
const removeArguments = ["-C", repoDir, "worktree", "remove"];
|
|
@@ -297,12 +297,12 @@ async function probeWorktreeRegistration(arguments_) {
|
|
|
297
297
|
catch {
|
|
298
298
|
return "unknown";
|
|
299
299
|
}
|
|
300
|
-
const resolvedWorktreeDir = resolve(arguments_.worktreeDir);
|
|
300
|
+
const resolvedWorktreeDir = path.resolve(arguments_.worktreeDir);
|
|
301
301
|
for (const line of output.split("\n")) {
|
|
302
302
|
if (!line.startsWith(WORKTREE_LIST_PREFIX)) {
|
|
303
303
|
continue;
|
|
304
304
|
}
|
|
305
|
-
if (resolve(line.slice(WORKTREE_LIST_PREFIX.length)) === resolvedWorktreeDir) {
|
|
305
|
+
if (path.resolve(line.slice(WORKTREE_LIST_PREFIX.length)) === resolvedWorktreeDir) {
|
|
306
306
|
return "registered";
|
|
307
307
|
}
|
|
308
308
|
}
|
|
@@ -313,18 +313,18 @@ function describeOrphanWorktree(arguments_) {
|
|
|
313
313
|
return `directory exists but is not a registered git worktree. Run \`crew cleanup --force ${ticket}\` to remove ${dir}, or inspect it first if it may contain valuable files.`;
|
|
314
314
|
}
|
|
315
315
|
function expectedHostWorktreeDir(config, entry) {
|
|
316
|
-
return resolve(config.workspace.projectDir, `${entry.repository}-${entry.ticket}`);
|
|
316
|
+
return path.resolve(config.workspace.projectDir, `${entry.repository}-${entry.ticket}`);
|
|
317
317
|
}
|
|
318
318
|
function isInsideDirectory(parentDir, childDir) {
|
|
319
|
-
const childRelativePath = relative(parentDir, childDir);
|
|
319
|
+
const childRelativePath = path.relative(parentDir, childDir);
|
|
320
320
|
return (childRelativePath.length > 0 &&
|
|
321
321
|
!childRelativePath.startsWith("..") &&
|
|
322
|
-
!isAbsolute(childRelativePath));
|
|
322
|
+
!path.isAbsolute(childRelativePath));
|
|
323
323
|
}
|
|
324
324
|
function removeOrphanWorktreeDirectory(config, entry) {
|
|
325
|
-
const projectDir = resolve(config.workspace.projectDir);
|
|
325
|
+
const projectDir = path.resolve(config.workspace.projectDir);
|
|
326
326
|
const expectedDir = expectedHostWorktreeDir(config, entry);
|
|
327
|
-
const targetDir = resolve(entry.dir);
|
|
327
|
+
const targetDir = path.resolve(entry.dir);
|
|
328
328
|
if (targetDir !== expectedDir || !isInsideDirectory(projectDir, targetDir)) {
|
|
329
329
|
throw new Error(`Refusing to force-delete ${entry.dir}: expected groundcrew worktree path ${expectedDir}.`);
|
|
330
330
|
}
|
package/dist/lib/xdg.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
|
-
import
|
|
2
|
+
import path from "node:path";
|
|
3
3
|
import { readEnvironmentVariable } from "./util.js";
|
|
4
4
|
// Per the XDG Base Directory spec, relative override paths are invalid and
|
|
5
5
|
// must be ignored — without this guard, a relative override would anchor to
|
|
6
6
|
// the cwd via `resolve` instead of falling back to $HOME.
|
|
7
7
|
function xdgBase(envName, fallbackSegments) {
|
|
8
8
|
const override = readEnvironmentVariable(envName);
|
|
9
|
-
if (override !== undefined && override.length > 0 && isAbsolute(override)) {
|
|
9
|
+
if (override !== undefined && override.length > 0 && path.isAbsolute(override)) {
|
|
10
10
|
return override;
|
|
11
11
|
}
|
|
12
|
-
return resolve(homedir(), ...fallbackSegments);
|
|
12
|
+
return path.resolve(homedir(), ...fallbackSegments);
|
|
13
13
|
}
|
|
14
14
|
export function xdgConfigPath(...segments) {
|
|
15
|
-
return resolve(xdgBase("XDG_CONFIG_HOME", [".config"]), ...segments);
|
|
15
|
+
return path.resolve(xdgBase("XDG_CONFIG_HOME", [".config"]), ...segments);
|
|
16
16
|
}
|
|
17
17
|
export function xdgStatePath(...segments) {
|
|
18
|
-
return resolve(xdgBase("XDG_STATE_HOME", [".local", "state"]), ...segments);
|
|
18
|
+
return path.resolve(xdgBase("XDG_STATE_HOME", [".local", "state"]), ...segments);
|
|
19
19
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipboard-health/groundcrew",
|
|
3
|
-
"version": "4.7.
|
|
3
|
+
"version": "4.7.3",
|
|
4
4
|
"description": "Linear-driven orchestrator that launches AI coding agents in git worktrees, with workspace lifecycle and usage tracking.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"@tsconfig/node24": "24.0.4",
|
|
82
82
|
"@tsconfig/strictest": "2.0.8",
|
|
83
83
|
"@types/node": "24.12.4",
|
|
84
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
84
|
+
"@typescript/native-preview": "7.0.0-dev.20260527.2",
|
|
85
85
|
"@vitest/coverage-v8": "4.1.7",
|
|
86
86
|
"cspell": "10.0.0",
|
|
87
87
|
"dependency-cruiser": "17.4.2",
|