@clipboard-health/groundcrew 3.1.9 → 3.2.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 +10 -6
- package/crew.config.example.ts +19 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +12 -0
- package/dist/commands/dispatcher.d.ts.map +1 -1
- package/dist/commands/dispatcher.js +10 -10
- package/dist/commands/init.d.ts +26 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +82 -0
- package/dist/commands/sandbox/auth.d.ts +3 -0
- package/dist/commands/sandbox/auth.d.ts.map +1 -0
- package/dist/commands/sandbox/auth.js +227 -0
- package/dist/commands/sandbox/index.d.ts +2 -0
- package/dist/commands/sandbox/index.d.ts.map +1 -0
- package/dist/commands/sandbox/index.js +47 -0
- package/dist/commands/sandbox/inspect.d.ts +2 -0
- package/dist/commands/sandbox/inspect.d.ts.map +1 -0
- package/dist/commands/sandbox/inspect.js +18 -0
- package/dist/commands/sandbox/lifecycle.d.ts +7 -0
- package/dist/commands/sandbox/lifecycle.d.ts.map +1 -0
- package/dist/commands/sandbox/lifecycle.js +68 -0
- package/dist/commands/sandbox/model.d.ts +10 -0
- package/dist/commands/sandbox/model.d.ts.map +1 -0
- package/dist/commands/sandbox/model.js +37 -0
- package/dist/commands/sandbox/picker.d.ts +20 -0
- package/dist/commands/sandbox/picker.d.ts.map +1 -0
- package/dist/commands/sandbox/picker.js +23 -0
- package/dist/lib/agentLaunch.d.ts.map +1 -1
- package/dist/lib/agentLaunch.js +1 -0
- package/dist/lib/config.d.ts +70 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +79 -13
- package/dist/lib/dockerSandbox.d.ts +12 -8
- package/dist/lib/dockerSandbox.d.ts.map +1 -1
- package/dist/lib/dockerSandbox.js +33 -22
- package/dist/lib/sandboxGitDefaults.d.ts +10 -0
- package/dist/lib/sandboxGitDefaults.d.ts.map +1 -0
- package/dist/lib/sandboxGitDefaults.js +31 -0
- package/dist/lib/xdg.d.ts +3 -0
- package/dist/lib/xdg.d.ts.map +1 -0
- package/dist/lib/xdg.js +19 -0
- package/package.json +12 -11
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ResolvedConfig, SandboxDefinition } from "../../lib/config.ts";
|
|
2
|
+
export interface SandboxModel {
|
|
3
|
+
modelName: string;
|
|
4
|
+
sandbox: SandboxDefinition;
|
|
5
|
+
sandboxName: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function sandboxModels(config: ResolvedConfig): SandboxModel[];
|
|
8
|
+
export declare function resolveModel(config: ResolvedConfig, modelName: string): SandboxModel;
|
|
9
|
+
export declare function requireOnePositional(argv: string[], usage: string): string;
|
|
10
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/commands/sandbox/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAG7E,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,YAAY,EAAE,CAcpE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,CAapF;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAM1E"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { sandboxNameFor } from "../../lib/dockerSandbox.js";
|
|
2
|
+
export function sandboxModels(config) {
|
|
3
|
+
const models = [];
|
|
4
|
+
for (const [modelName, definition] of Object.entries(config.models.definitions)) {
|
|
5
|
+
const { sandbox } = definition;
|
|
6
|
+
if (sandbox === undefined) {
|
|
7
|
+
continue;
|
|
8
|
+
}
|
|
9
|
+
models.push({
|
|
10
|
+
modelName,
|
|
11
|
+
sandbox,
|
|
12
|
+
sandboxName: sandboxNameFor({ agent: sandbox.agent }),
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return models;
|
|
16
|
+
}
|
|
17
|
+
export function resolveModel(config, modelName) {
|
|
18
|
+
const definition = config.models.definitions[modelName];
|
|
19
|
+
if (definition === undefined) {
|
|
20
|
+
throw new Error(`crew sandbox: unknown model '${modelName}'`);
|
|
21
|
+
}
|
|
22
|
+
if (definition.sandbox === undefined) {
|
|
23
|
+
throw new Error(`crew sandbox: model '${modelName}' has no sandbox config`);
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
modelName,
|
|
27
|
+
sandbox: definition.sandbox,
|
|
28
|
+
sandboxName: sandboxNameFor({ agent: definition.sandbox.agent }),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function requireOnePositional(argv, usage) {
|
|
32
|
+
const [first, ...rest] = argv;
|
|
33
|
+
if (first === undefined || rest.length > 0) {
|
|
34
|
+
throw new Error(usage);
|
|
35
|
+
}
|
|
36
|
+
return first;
|
|
37
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ToolChoice {
|
|
2
|
+
/** Recipe key (e.g. "claude", "github"). Returned in the selection. */
|
|
3
|
+
key: string;
|
|
4
|
+
/** Human-friendly label shown in the prompt. */
|
|
5
|
+
label: string;
|
|
6
|
+
/** Auth status decoration: ✓ when authenticated, ○ otherwise. */
|
|
7
|
+
authenticated: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Show an interactive checkbox picker so the engineer chooses which
|
|
11
|
+
* tools to authenticate. Items marked `authenticated` start unchecked
|
|
12
|
+
* (no need to re-auth); unauthed items start checked (default action
|
|
13
|
+
* is "auth what's missing"). The returned array is the list of `key`
|
|
14
|
+
* values that the engineer left checked when they confirmed.
|
|
15
|
+
*
|
|
16
|
+
* Extracted to its own module so tests can vi.mock it and skip stdin
|
|
17
|
+
* interaction; the real implementation pulls @inquirer/checkbox.
|
|
18
|
+
*/
|
|
19
|
+
export declare function pickTools(choices: readonly ToolChoice[]): Promise<readonly string[]>;
|
|
20
|
+
//# sourceMappingURL=picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"picker.d.ts","sourceRoot":"","sources":["../../../src/commands/sandbox/picker.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,aAAa,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAW1F"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import checkbox from "@inquirer/checkbox";
|
|
2
|
+
/**
|
|
3
|
+
* Show an interactive checkbox picker so the engineer chooses which
|
|
4
|
+
* tools to authenticate. Items marked `authenticated` start unchecked
|
|
5
|
+
* (no need to re-auth); unauthed items start checked (default action
|
|
6
|
+
* is "auth what's missing"). The returned array is the list of `key`
|
|
7
|
+
* values that the engineer left checked when they confirmed.
|
|
8
|
+
*
|
|
9
|
+
* Extracted to its own module so tests can vi.mock it and skip stdin
|
|
10
|
+
* interaction; the real implementation pulls @inquirer/checkbox.
|
|
11
|
+
*/
|
|
12
|
+
export async function pickTools(choices) {
|
|
13
|
+
const selected = await checkbox({
|
|
14
|
+
message: "Select tools to authenticate (space to toggle, enter to confirm):",
|
|
15
|
+
choices: choices.map((choice) => ({
|
|
16
|
+
name: `${choice.authenticated ? "✓" : "○"} ${choice.label}`,
|
|
17
|
+
value: choice.key,
|
|
18
|
+
checked: !choice.authenticated,
|
|
19
|
+
})),
|
|
20
|
+
pageSize: Math.max(choices.length, 1),
|
|
21
|
+
});
|
|
22
|
+
return selected;
|
|
23
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/agentLaunch.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAOhF,UAAU,mBAAmB;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;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,CA6B/B;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,cAAc,CAAC;IACvB,UAAU,EAAE,eAAe,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"agentLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/agentLaunch.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAOhF,UAAU,mBAAmB;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;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,CA6B/B;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,cAAc,CAAC;IACvB,UAAU,EAAE,eAAe,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAYhB;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,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,CAUhB"}
|
package/dist/lib/agentLaunch.js
CHANGED
package/dist/lib/config.d.ts
CHANGED
|
@@ -58,6 +58,42 @@ export interface SandboxDefinition {
|
|
|
58
58
|
*/
|
|
59
59
|
setupCommand?: string;
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Recipe used by `crew sandbox auth <model>` to drive an interactive
|
|
63
|
+
* login flow inside a sbx sandbox and then verify it. The flow is
|
|
64
|
+
* picker-driven — no positional `<tool>` argument; the picker lists
|
|
65
|
+
* every recipe visible to the current sandbox.
|
|
66
|
+
*
|
|
67
|
+
* `binary` defaults to the recipe key (typically the agent or CLI name).
|
|
68
|
+
* `authenticatedPattern` matches against combined stdout+stderr from
|
|
69
|
+
* `statusArgs` — exit code alone isn't reliable because some CLIs
|
|
70
|
+
* report "not logged in" while still exiting 0.
|
|
71
|
+
* `kind` controls visibility in the interactive picker: `"agent"`
|
|
72
|
+
* recipes are scoped to a specific sbx agent and only appear when you
|
|
73
|
+
* `auth` against that agent's sandbox; `"tool"` recipes (default)
|
|
74
|
+
* appear in every sandbox's picker because they're cross-cutting
|
|
75
|
+
* (github, npm, gcloud, …). Defaults to `"tool"` when omitted.
|
|
76
|
+
*
|
|
77
|
+
* Ship-side recipes for `claude`, `codex`, and `cursor` live in
|
|
78
|
+
* `src/commands/sandbox/auth.ts`; users register additional tools
|
|
79
|
+
* under `sandbox.authRecipes` in their config.
|
|
80
|
+
*/
|
|
81
|
+
export interface AuthRecipe {
|
|
82
|
+
displayName: string;
|
|
83
|
+
binary?: string;
|
|
84
|
+
loginArgs: readonly string[];
|
|
85
|
+
statusArgs: readonly string[];
|
|
86
|
+
authenticatedPattern: RegExp;
|
|
87
|
+
kind?: "agent" | "tool";
|
|
88
|
+
/**
|
|
89
|
+
* Environment variables passed to `sbx exec` for both the login and
|
|
90
|
+
* status calls. Use this for CLIs whose default flow assumes a
|
|
91
|
+
* browser or other host-only feature — e.g. cursor-agent wants
|
|
92
|
+
* `NO_OPEN_BROWSER=1` to print a device code instead of trying to
|
|
93
|
+
* launch a browser inside the sandbox.
|
|
94
|
+
*/
|
|
95
|
+
env?: Record<string, string>;
|
|
96
|
+
}
|
|
61
97
|
export interface ModelDefinition {
|
|
62
98
|
/**
|
|
63
99
|
* Shell command launched for the model. Wrapped with Safehouse/clearance
|
|
@@ -192,6 +228,32 @@ export interface Config {
|
|
|
192
228
|
local?: {
|
|
193
229
|
runner?: LocalRunnerSetting;
|
|
194
230
|
};
|
|
231
|
+
/**
|
|
232
|
+
* Sandbox-wide settings. `authRecipes` lets users register additional
|
|
233
|
+
* tools (github, npm, gcloud, …) for `crew sandbox auth <model>` to
|
|
234
|
+
* authenticate inside the sandbox. The auth flow is picker-driven —
|
|
235
|
+
* registered recipes show up in the picker alongside the shipped ones,
|
|
236
|
+
* and a user recipe under the same key (e.g. "claude") overrides the
|
|
237
|
+
* shipped one.
|
|
238
|
+
*/
|
|
239
|
+
sandbox?: {
|
|
240
|
+
authRecipes?: Record<string, AuthRecipe>;
|
|
241
|
+
/**
|
|
242
|
+
* When true (default), every `crew sandbox ensure` / `auth` run applies
|
|
243
|
+
* a small set of git defaults inside the sandbox so robot commits push
|
|
244
|
+
* over `gh`-managed HTTPS regardless of how the user cloned the repo:
|
|
245
|
+
*
|
|
246
|
+
* - disable GPG signing for commits and tags
|
|
247
|
+
* - rewrite `git@github.com:` and `ssh://git@github.com/` URLs to
|
|
248
|
+
* `https://github.com/` so push uses gh's credential helper
|
|
249
|
+
* - after a successful `github` auth recipe login, run
|
|
250
|
+
* `gh auth setup-git` inside the sandbox
|
|
251
|
+
*
|
|
252
|
+
* Set `false` to skip both the git-config block and the post-login
|
|
253
|
+
* `gh auth setup-git` step.
|
|
254
|
+
*/
|
|
255
|
+
gitDefaults?: boolean;
|
|
256
|
+
};
|
|
195
257
|
logging?: {
|
|
196
258
|
/**
|
|
197
259
|
* Append-mode log file destination. `log()` and `logEvent()` tee here
|
|
@@ -261,6 +323,14 @@ export interface ResolvedConfig {
|
|
|
261
323
|
local: {
|
|
262
324
|
runner: LocalRunnerSetting;
|
|
263
325
|
};
|
|
326
|
+
/**
|
|
327
|
+
* Sandbox-wide settings. Always present after defaults; `authRecipes`
|
|
328
|
+
* is `{}` when the user provides none.
|
|
329
|
+
*/
|
|
330
|
+
sandbox: {
|
|
331
|
+
authRecipes: Record<string, AuthRecipe>;
|
|
332
|
+
gitDefaults: boolean;
|
|
333
|
+
};
|
|
264
334
|
logging: {
|
|
265
335
|
file: string;
|
|
266
336
|
};
|
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":"AAOA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;
|
|
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
|
@@ -5,6 +5,7 @@ import { resolve } from "node:path";
|
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
import { cosmiconfig } from "cosmiconfig";
|
|
7
7
|
import { log, readEnvironmentVariable, setLogFile } from "./util.js";
|
|
8
|
+
import { xdgConfigPath, xdgStatePath } from "./xdg.js";
|
|
8
9
|
export { BUILD_SECRET_NAMES } from "./buildSecrets.js";
|
|
9
10
|
/**
|
|
10
11
|
* Reserved model name. A ticket labeled `agent-any` resolves at runtime
|
|
@@ -83,19 +84,6 @@ const ALLOWED_PROMPT_PLACEHOLDERS = new Set([
|
|
|
83
84
|
const PROMPT_PLACEHOLDER_RE = /{{[^{}]*}}/g;
|
|
84
85
|
const PERCENT_MIN_EXCLUSIVE = 0;
|
|
85
86
|
const PERCENT_MAX = 100;
|
|
86
|
-
function xdgBase(envName, fallbackSegments) {
|
|
87
|
-
const override = readEnvironmentVariable(envName);
|
|
88
|
-
if (override !== undefined && override.length > 0) {
|
|
89
|
-
return override;
|
|
90
|
-
}
|
|
91
|
-
return resolve(homedir(), ...fallbackSegments);
|
|
92
|
-
}
|
|
93
|
-
function xdgConfigPath(...segments) {
|
|
94
|
-
return resolve(xdgBase("XDG_CONFIG_HOME", [".config"]), ...segments);
|
|
95
|
-
}
|
|
96
|
-
function xdgStatePath(...segments) {
|
|
97
|
-
return resolve(xdgBase("XDG_STATE_HOME", [".local", "state"]), ...segments);
|
|
98
|
-
}
|
|
99
87
|
function defaultLogFile() {
|
|
100
88
|
return xdgStatePath("groundcrew", "groundcrew.log");
|
|
101
89
|
}
|
|
@@ -144,6 +132,15 @@ function normalizeOptionalString(value, path) {
|
|
|
144
132
|
}
|
|
145
133
|
return value.trim();
|
|
146
134
|
}
|
|
135
|
+
function normalizeOptionalBoolean(value, path) {
|
|
136
|
+
if (value === undefined) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
if (typeof value !== "boolean") {
|
|
140
|
+
fail(`${path} must be a boolean`);
|
|
141
|
+
}
|
|
142
|
+
return value;
|
|
143
|
+
}
|
|
147
144
|
function normalizeOptionalStringArray(value, path) {
|
|
148
145
|
if (value === undefined) {
|
|
149
146
|
return undefined;
|
|
@@ -337,6 +334,11 @@ function requireObject(value, path) {
|
|
|
337
334
|
fail(`${path} must be an object (got ${JSON.stringify(value)})`);
|
|
338
335
|
}
|
|
339
336
|
}
|
|
337
|
+
function requireOptionalObject(value, path) {
|
|
338
|
+
if (value !== undefined && !isPlainObject(value)) {
|
|
339
|
+
fail(`${path} must be an object`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
340
342
|
function normalizeProject(value, index) {
|
|
341
343
|
const path = `linear.projects[${index}]`;
|
|
342
344
|
if (!isPlainObject(value)) {
|
|
@@ -441,6 +443,65 @@ function normalizeProjects(linear) {
|
|
|
441
443
|
});
|
|
442
444
|
return resolved;
|
|
443
445
|
}
|
|
446
|
+
function normalizeAuthRecipes(value, path) {
|
|
447
|
+
if (value === undefined) {
|
|
448
|
+
return {};
|
|
449
|
+
}
|
|
450
|
+
if (!isPlainObject(value)) {
|
|
451
|
+
fail(`${path} must be an object`);
|
|
452
|
+
}
|
|
453
|
+
const recipes = {};
|
|
454
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
455
|
+
const recipePath = `${path}.${key}`;
|
|
456
|
+
if (!isPlainObject(raw)) {
|
|
457
|
+
fail(`${recipePath} must be an object`);
|
|
458
|
+
}
|
|
459
|
+
const { displayName, binary, loginArgs, statusArgs, authenticatedPattern, kind, env } = raw;
|
|
460
|
+
requireString(displayName, `${recipePath}.displayName`);
|
|
461
|
+
const loginArray = normalizeOptionalStringArray(loginArgs, `${recipePath}.loginArgs`);
|
|
462
|
+
const statusArray = normalizeOptionalStringArray(statusArgs, `${recipePath}.statusArgs`);
|
|
463
|
+
if (loginArray === undefined) {
|
|
464
|
+
fail(`${recipePath}.loginArgs is required`);
|
|
465
|
+
}
|
|
466
|
+
if (statusArray === undefined) {
|
|
467
|
+
fail(`${recipePath}.statusArgs is required`);
|
|
468
|
+
}
|
|
469
|
+
if (!(authenticatedPattern instanceof RegExp)) {
|
|
470
|
+
fail(`${recipePath}.authenticatedPattern must be a RegExp`);
|
|
471
|
+
}
|
|
472
|
+
const recipe = {
|
|
473
|
+
displayName,
|
|
474
|
+
loginArgs: loginArray,
|
|
475
|
+
statusArgs: statusArray,
|
|
476
|
+
authenticatedPattern,
|
|
477
|
+
};
|
|
478
|
+
const binaryString = normalizeOptionalString(binary, `${recipePath}.binary`);
|
|
479
|
+
if (binaryString !== undefined) {
|
|
480
|
+
recipe.binary = binaryString;
|
|
481
|
+
}
|
|
482
|
+
if (kind !== undefined) {
|
|
483
|
+
if (kind !== "agent" && kind !== "tool") {
|
|
484
|
+
fail(`${recipePath}.kind must be "agent" or "tool"`);
|
|
485
|
+
}
|
|
486
|
+
recipe.kind = kind;
|
|
487
|
+
}
|
|
488
|
+
if (env !== undefined) {
|
|
489
|
+
if (!isPlainObject(env)) {
|
|
490
|
+
fail(`${recipePath}.env must be an object`);
|
|
491
|
+
}
|
|
492
|
+
const normalizedEnv = {};
|
|
493
|
+
for (const [envKey, envValue] of Object.entries(env)) {
|
|
494
|
+
if (typeof envValue !== "string") {
|
|
495
|
+
fail(`${recipePath}.env.${envKey} must be a string`);
|
|
496
|
+
}
|
|
497
|
+
normalizedEnv[envKey] = envValue;
|
|
498
|
+
}
|
|
499
|
+
recipe.env = normalizedEnv;
|
|
500
|
+
}
|
|
501
|
+
recipes[key] = recipe;
|
|
502
|
+
}
|
|
503
|
+
return recipes;
|
|
504
|
+
}
|
|
444
505
|
function applyDefaults(user) {
|
|
445
506
|
// Guard the top-level shape before reading nested fields, so a
|
|
446
507
|
// malformed runtime config produces a `groundcrew config: ...` error
|
|
@@ -455,6 +516,7 @@ function applyDefaults(user) {
|
|
|
455
516
|
if (Object.hasOwn(user, "remote")) {
|
|
456
517
|
fail("remote is no longer supported: groundcrew runs locally via safehouse/sdx/none; remove the remote block from your config");
|
|
457
518
|
}
|
|
519
|
+
requireOptionalObject(user.sandbox, "sandbox");
|
|
458
520
|
const userLocal = user.local;
|
|
459
521
|
if (userLocal !== undefined && !isPlainObject(userLocal)) {
|
|
460
522
|
fail("local must be an object");
|
|
@@ -482,6 +544,10 @@ function applyDefaults(user) {
|
|
|
482
544
|
local: {
|
|
483
545
|
runner: normalizeLocalRunner(userLocal?.runner, "local.runner") ?? "auto",
|
|
484
546
|
},
|
|
547
|
+
sandbox: {
|
|
548
|
+
authRecipes: normalizeAuthRecipes(user.sandbox?.authRecipes, "sandbox.authRecipes"),
|
|
549
|
+
gitDefaults: normalizeOptionalBoolean(user.sandbox?.gitDefaults, "sandbox.gitDefaults") ?? true,
|
|
550
|
+
},
|
|
485
551
|
logging: {
|
|
486
552
|
file: expandHome(normalizeOptionalString(user.logging?.file, "logging.file") ?? defaultLogFile()),
|
|
487
553
|
},
|
|
@@ -25,15 +25,19 @@ interface EnsureSandboxArguments {
|
|
|
25
25
|
* clone) are visible to `sbx exec -w <worktreeDir>` after creation.
|
|
26
26
|
*/
|
|
27
27
|
mountPath: string;
|
|
28
|
+
/**
|
|
29
|
+
* When true, apply the standard git defaults inside the sandbox after
|
|
30
|
+
* it exists (idempotent, runs whether the sandbox was just created or
|
|
31
|
+
* already there). See `sandboxGitDefaults.ts` for what gets set.
|
|
32
|
+
*/
|
|
33
|
+
gitDefaults: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Result of an earlier `sandboxExists` probe by the caller, used to
|
|
36
|
+
* skip the initial `sbx ls` here. Leave undefined to let this function
|
|
37
|
+
* probe on its own.
|
|
38
|
+
*/
|
|
39
|
+
alreadyExists?: boolean;
|
|
28
40
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Idempotent guard: ensure a Docker Sandboxes container exists for the
|
|
31
|
-
* given repository + model. Probes `sbx ls`; if `sandboxName` is missing,
|
|
32
|
-
* calls `sbx create --name <name> [--template <t>] [--kit <k>]... <agent>
|
|
33
|
-
* <mountPath>` to provision it. First-time agent auth still happens inside
|
|
34
|
-
* the sandbox the first time `sbx exec` runs the agent — `create` only
|
|
35
|
-
* provisions the container, it does not attach.
|
|
36
|
-
*/
|
|
37
41
|
export declare function ensureSandbox(arguments_: EnsureSandboxArguments, signal?: AbortSignal): Promise<void>;
|
|
38
42
|
export {};
|
|
39
43
|
//# sourceMappingURL=dockerSandbox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dockerSandbox.d.ts","sourceRoot":"","sources":["../../src/lib/dockerSandbox.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"dockerSandbox.d.ts","sourceRoot":"","sources":["../../src/lib/dockerSandbox.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAMpE;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAM/F;AAED,UAAU,sBAAsB;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,iBAAiB,CAAC;IAC3B;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAsBD,wBAAsB,aAAa,CACjC,UAAU,EAAE,sBAAsB,EAClC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAuBf"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { runCommandAsync } from "./commandRunner.js";
|
|
2
|
+
import { applyGitDefaults } from "./sandboxGitDefaults.js";
|
|
2
3
|
/**
|
|
3
4
|
* Derive a deterministic sbx sandbox name from the sbx agent so every
|
|
4
5
|
* groundcrew model that targets the same agent reuses one sandbox across
|
|
@@ -29,30 +30,40 @@ export async function sandboxExists(sandboxName, signal) {
|
|
|
29
30
|
* Idempotent guard: ensure a Docker Sandboxes container exists for the
|
|
30
31
|
* given repository + model. Probes `sbx ls`; if `sandboxName` is missing,
|
|
31
32
|
* calls `sbx create --name <name> [--template <t>] [--kit <k>]... <agent>
|
|
32
|
-
* <mountPath>` to provision it.
|
|
33
|
-
*
|
|
34
|
-
*
|
|
33
|
+
* <mountPath>` to provision it. Once the container exists (newly created
|
|
34
|
+
* or pre-existing), applies the standard git defaults when enabled.
|
|
35
|
+
* First-time agent auth still happens inside the sandbox the first time
|
|
36
|
+
* `sbx exec` runs the agent — `create` only provisions the container, it
|
|
37
|
+
* does not attach.
|
|
35
38
|
*/
|
|
36
|
-
|
|
37
|
-
if (
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
const createArguments = ["create", "--name", arguments_.sandboxName];
|
|
41
|
-
if (arguments_.sandbox.template !== undefined) {
|
|
42
|
-
createArguments.push("--template", arguments_.sandbox.template);
|
|
43
|
-
}
|
|
44
|
-
for (const kit of arguments_.sandbox.kits ?? []) {
|
|
45
|
-
createArguments.push("--kit", kit);
|
|
39
|
+
async function resolveExistence(arguments_, signal) {
|
|
40
|
+
if (arguments_.alreadyExists === undefined) {
|
|
41
|
+
return await sandboxExists(arguments_.sandboxName, signal);
|
|
46
42
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (
|
|
54
|
-
|
|
43
|
+
return arguments_.alreadyExists;
|
|
44
|
+
}
|
|
45
|
+
export async function ensureSandbox(arguments_, signal) {
|
|
46
|
+
const existed = await resolveExistence(arguments_, signal);
|
|
47
|
+
if (!existed) {
|
|
48
|
+
const createArguments = ["create", "--name", arguments_.sandboxName];
|
|
49
|
+
if (arguments_.sandbox.template !== undefined) {
|
|
50
|
+
createArguments.push("--template", arguments_.sandbox.template);
|
|
51
|
+
}
|
|
52
|
+
for (const kit of arguments_.sandbox.kits ?? []) {
|
|
53
|
+
createArguments.push("--kit", kit);
|
|
54
|
+
}
|
|
55
|
+
createArguments.push(arguments_.sandbox.agent, arguments_.mountPath);
|
|
56
|
+
const options = signal === undefined ? {} : { signal };
|
|
57
|
+
try {
|
|
58
|
+
await runCommandAsync("sbx", createArguments, options);
|
|
55
59
|
}
|
|
56
|
-
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (!(await sandboxExists(arguments_.sandboxName, signal))) {
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (arguments_.gitDefaults) {
|
|
67
|
+
await applyGitDefaults({ sandboxName: arguments_.sandboxName }, signal);
|
|
57
68
|
}
|
|
58
69
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface ApplyGitDefaultsArguments {
|
|
2
|
+
sandboxName: string;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Apply the standard git defaults inside `sandboxName`. Idempotent —
|
|
6
|
+
* safe to call on every `ensure`/`auth` run to repair drift.
|
|
7
|
+
*/
|
|
8
|
+
export declare function applyGitDefaults(arguments_: ApplyGitDefaultsArguments, signal?: AbortSignal): Promise<void>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=sandboxGitDefaults.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandboxGitDefaults.d.ts","sourceRoot":"","sources":["../../src/lib/sandboxGitDefaults.ts"],"names":[],"mappings":"AAyBA,UAAU,yBAAyB;IACjC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,yBAAyB,EACrC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAOf"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { runCommandAsync } from "./commandRunner.js";
|
|
2
|
+
/**
|
|
3
|
+
* Git defaults applied inside every sandbox when `sandbox.gitDefaults`
|
|
4
|
+
* is enabled (the default).
|
|
5
|
+
*
|
|
6
|
+
* - Disable GPG signing — robot commits inside the sandbox have no key
|
|
7
|
+
* and would otherwise fail or end up unsigned silently.
|
|
8
|
+
* - Rewrite GitHub SSH URLs to HTTPS so push/fetch go through the `gh`
|
|
9
|
+
* credential helper (wired by `gh auth setup-git` after a successful
|
|
10
|
+
* `crew sandbox auth` github login), regardless of how the user
|
|
11
|
+
* originally cloned the repo on the host.
|
|
12
|
+
*
|
|
13
|
+
* `url.<base>.insteadOf` is multi-valued in git; `--unset-all` before
|
|
14
|
+
* `--add` keeps the set identical across repeated runs instead of
|
|
15
|
+
* appending duplicates.
|
|
16
|
+
*/
|
|
17
|
+
const GIT_DEFAULT_COMMANDS = [
|
|
18
|
+
"git config --global commit.gpgsign false",
|
|
19
|
+
"git config --global tag.gpgsign false",
|
|
20
|
+
'(git config --global --unset-all url."https://github.com/".insteadOf 2>/dev/null || true)',
|
|
21
|
+
'git config --global --add url."https://github.com/".insteadOf "git@github.com:"',
|
|
22
|
+
'git config --global --add url."https://github.com/".insteadOf "ssh://git@github.com/"',
|
|
23
|
+
].join(" && ");
|
|
24
|
+
/**
|
|
25
|
+
* Apply the standard git defaults inside `sandboxName`. Idempotent —
|
|
26
|
+
* safe to call on every `ensure`/`auth` run to repair drift.
|
|
27
|
+
*/
|
|
28
|
+
export async function applyGitDefaults(arguments_, signal) {
|
|
29
|
+
const options = signal === undefined ? {} : { signal };
|
|
30
|
+
await runCommandAsync("sbx", ["exec", arguments_.sandboxName, "sh", "-c", GIT_DEFAULT_COMMANDS], options);
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xdg.d.ts","sourceRoot":"","sources":["../../src/lib/xdg.ts"],"names":[],"mappings":"AAgBA,wBAAgB,aAAa,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAE3D;AAED,wBAAgB,YAAY,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAE1D"}
|
package/dist/lib/xdg.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { isAbsolute, resolve } from "node:path";
|
|
3
|
+
import { readEnvironmentVariable } from "./util.js";
|
|
4
|
+
// Per the XDG Base Directory spec, relative override paths are invalid and
|
|
5
|
+
// must be ignored — without this guard, a relative override would anchor to
|
|
6
|
+
// the cwd via `resolve` instead of falling back to $HOME.
|
|
7
|
+
function xdgBase(envName, fallbackSegments) {
|
|
8
|
+
const override = readEnvironmentVariable(envName);
|
|
9
|
+
if (override !== undefined && override.length > 0 && isAbsolute(override)) {
|
|
10
|
+
return override;
|
|
11
|
+
}
|
|
12
|
+
return resolve(homedir(), ...fallbackSegments);
|
|
13
|
+
}
|
|
14
|
+
export function xdgConfigPath(...segments) {
|
|
15
|
+
return resolve(xdgBase("XDG_CONFIG_HOME", [".config"]), ...segments);
|
|
16
|
+
}
|
|
17
|
+
export function xdgStatePath(...segments) {
|
|
18
|
+
return resolve(xdgBase("XDG_STATE_HOME", [".local", "state"]), ...segments);
|
|
19
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipboard-health/groundcrew",
|
|
3
|
-
"version": "3.1
|
|
3
|
+
"version": "3.2.1",
|
|
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",
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
70
|
"@clipboard-health/clearance": "1.0.8",
|
|
71
|
+
"@inquirer/checkbox": "5.1.5",
|
|
71
72
|
"@linear/sdk": "85.0.0",
|
|
72
73
|
"cosmiconfig": "9.0.1",
|
|
73
74
|
"tslib": "2.8.1",
|
|
@@ -76,26 +77,26 @@
|
|
|
76
77
|
"devDependencies": {
|
|
77
78
|
"@clipboard-health/ai-rules": "2.18.7",
|
|
78
79
|
"@clipboard-health/oxlint-config": "1.9.4",
|
|
79
|
-
"@nx/js": "22.7.
|
|
80
|
+
"@nx/js": "22.7.2",
|
|
80
81
|
"@tsconfig/node24": "24.0.4",
|
|
81
82
|
"@tsconfig/strictest": "2.0.8",
|
|
82
|
-
"@types/node": "24.12.
|
|
83
|
+
"@types/node": "24.12.4",
|
|
83
84
|
"@typescript/native-preview": "7.0.0-dev.20260522.1",
|
|
84
|
-
"@vitest/coverage-v8": "4.1.
|
|
85
|
+
"@vitest/coverage-v8": "4.1.6",
|
|
85
86
|
"cspell": "10.0.0",
|
|
86
87
|
"dependency-cruiser": "17.4.0",
|
|
87
88
|
"husky": "9.1.7",
|
|
88
|
-
"jscpd": "4.
|
|
89
|
-
"knip": "6.
|
|
89
|
+
"jscpd": "4.2.3",
|
|
90
|
+
"knip": "6.14.1",
|
|
90
91
|
"lint-staged": "17.0.5",
|
|
91
92
|
"markdownlint-cli2": "0.22.1",
|
|
92
|
-
"nx": "22.7.
|
|
93
|
+
"nx": "22.7.2",
|
|
93
94
|
"oxfmt": "0.50.0",
|
|
94
|
-
"oxlint": "1.
|
|
95
|
-
"oxlint-tsgolint": "0.
|
|
96
|
-
"syncpack": "15.
|
|
95
|
+
"oxlint": "1.65.0",
|
|
96
|
+
"oxlint-tsgolint": "0.23.0",
|
|
97
|
+
"syncpack": "15.2.0",
|
|
97
98
|
"vite": "8.0.14",
|
|
98
|
-
"vitest": "4.1.
|
|
99
|
+
"vitest": "4.1.6"
|
|
99
100
|
},
|
|
100
101
|
"engines": {
|
|
101
102
|
"node": "24.14.1",
|