@clipboard-health/groundcrew 2.2.0 → 2.3.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.
Files changed (41) hide show
  1. package/README.md +72 -8
  2. package/configExample.ts +9 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +22 -10
  5. package/dist/commands/dispatcher.d.ts.map +1 -1
  6. package/dist/commands/dispatcher.js +10 -35
  7. package/dist/commands/doctor.d.ts +4 -1
  8. package/dist/commands/doctor.d.ts.map +1 -1
  9. package/dist/commands/doctor.js +60 -13
  10. package/dist/commands/eligibility.d.ts +14 -0
  11. package/dist/commands/eligibility.d.ts.map +1 -1
  12. package/dist/commands/eligibility.js +44 -0
  13. package/dist/commands/setupWorkspace.d.ts.map +1 -1
  14. package/dist/commands/setupWorkspace.js +23 -4
  15. package/dist/commands/ticketDoctor.d.ts +48 -0
  16. package/dist/commands/ticketDoctor.d.ts.map +1 -0
  17. package/dist/commands/ticketDoctor.js +402 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +2 -0
  21. package/dist/lib/boardSource.d.ts +55 -0
  22. package/dist/lib/boardSource.d.ts.map +1 -1
  23. package/dist/lib/boardSource.js +171 -26
  24. package/dist/lib/config.d.ts +63 -0
  25. package/dist/lib/config.d.ts.map +1 -1
  26. package/dist/lib/config.js +75 -7
  27. package/dist/lib/dockerSandbox.d.ts +40 -0
  28. package/dist/lib/dockerSandbox.d.ts.map +1 -0
  29. package/dist/lib/dockerSandbox.js +58 -0
  30. package/dist/lib/host.d.ts +10 -0
  31. package/dist/lib/host.d.ts.map +1 -1
  32. package/dist/lib/host.js +8 -3
  33. package/dist/lib/launchCommand.d.ts +17 -3
  34. package/dist/lib/launchCommand.d.ts.map +1 -1
  35. package/dist/lib/launchCommand.js +66 -8
  36. package/dist/lib/localRunner.d.ts +22 -1
  37. package/dist/lib/localRunner.d.ts.map +1 -1
  38. package/dist/lib/localRunner.js +48 -5
  39. package/dist/lib/workspaces.d.ts.map +1 -1
  40. package/dist/lib/workspaces.js +138 -40
  41. package/package.json +1 -1
@@ -129,9 +129,9 @@ async function fetchBoard(client, config) {
129
129
  const issues = nodes
130
130
  .filter((node) => node.children.nodes.length === 0)
131
131
  .map((node) => {
132
- const parsedAgentLabels = parseAgentLabels(node.labels.nodes, config);
133
- warnIfDisabledFallback(node.identifier, parsedAgentLabels, config);
134
- const repository = parsedAgentLabels === undefined
132
+ const modelResolution = resolveModelFor({ labels: node.labels.nodes, config });
133
+ warnIfDisabledFallback(node.identifier, modelResolution, config);
134
+ const repository = modelResolution.kind === "no-label"
135
135
  ? undefined
136
136
  : parseRepository({
137
137
  description: node.description ?? undefined,
@@ -139,6 +139,16 @@ async function fetchBoard(client, config) {
139
139
  repositoryRegex,
140
140
  ticket: node.identifier,
141
141
  });
142
+ let model;
143
+ if (modelResolution.kind === "matched") {
144
+ ({ model } = modelResolution);
145
+ }
146
+ else if (modelResolution.kind === "disabled-fallback") {
147
+ model = modelResolution.fallbackModel;
148
+ }
149
+ else if (modelResolution.kind === "agent-any") {
150
+ model = AGENT_ANY_MODEL;
151
+ }
142
152
  return {
143
153
  id: node.identifier.toLowerCase(),
144
154
  uuid: node.id,
@@ -148,7 +158,7 @@ async function fetchBoard(client, config) {
148
158
  assignee: node.assignee?.name ?? "Unassigned",
149
159
  updatedAt: node.updatedAt,
150
160
  repository,
151
- model: parsedAgentLabels?.model,
161
+ model,
152
162
  teamId: node.team?.id ?? "",
153
163
  blockers: blockersFromRelations(node.inverseRelations?.nodes ?? []),
154
164
  hasMoreBlockers: node.inverseRelations?.pageInfo.hasNextPage ?? false,
@@ -174,22 +184,64 @@ function buildRepositoryRegex(config) {
174
184
  return new RegExp(String.raw `\b(${alternation})\b`);
175
185
  }
176
186
  const ISSUE_LABEL_PAGE_SIZE = 50;
177
- /**
178
- * `agent-any` collapses to `models.default` here — manual setup doesn't run
179
- * the usage-gated `any` resolver, so the caller gets a concrete model name
180
- * instead of a sentinel that downstream code can't interpret.
181
- */
182
- export async function fetchResolvedIssue(arguments_) {
183
- const { client, config, ticket } = arguments_;
187
+ const ISSUE_RELATION_PAGE_SIZE = 50;
188
+ export async function fetchBlockersForTicket(arguments_) {
189
+ const { client, uuid } = arguments_;
190
+ const relations = [];
191
+ let after = null;
192
+ for (;;) {
193
+ // oxlint-disable-next-line no-await-in-loop -- pagination cursor depends on the previous response
194
+ const response = await client.client.rawRequest(`query IssueBlockers($id: String!, $after: String) {
195
+ issue(id: $id) {
196
+ inverseRelations(first: ${ISSUE_RELATION_PAGE_SIZE}, after: $after, includeArchived: false) {
197
+ nodes {
198
+ type
199
+ issue {
200
+ identifier
201
+ title
202
+ state { name }
203
+ }
204
+ }
205
+ pageInfo { hasNextPage endCursor }
206
+ }
207
+ }
208
+ }`, { id: uuid, after });
209
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- shape is fixed by our GraphQL query above
210
+ const { issue } = response.data;
211
+ if (issue === null) {
212
+ return [];
213
+ }
214
+ relations.push(...issue.inverseRelations.nodes);
215
+ if (!issue.inverseRelations.pageInfo.hasNextPage) {
216
+ break;
217
+ }
218
+ after = issue.inverseRelations.pageInfo.endCursor;
219
+ }
220
+ return blockersFromRelations(relations);
221
+ }
222
+ export async function fetchRawLinearIssue(arguments_) {
223
+ const { client, ticket } = arguments_;
184
224
  const response = await client.client.rawRequest(`query ResolveIssue($id: String!) {
185
225
  issue(id: $id) {
186
226
  id
187
227
  title
188
228
  description
189
229
  team { id }
230
+ state { name }
190
231
  labels(first: ${ISSUE_LABEL_PAGE_SIZE}) {
191
232
  nodes { name }
192
233
  }
234
+ inverseRelations(first: 50, includeArchived: false) {
235
+ nodes {
236
+ type
237
+ issue {
238
+ identifier
239
+ title
240
+ state { name }
241
+ }
242
+ }
243
+ pageInfo { hasNextPage }
244
+ }
193
245
  }
194
246
  }`, { id: ticket.toUpperCase() });
195
247
  // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- shape is fixed by our GraphQL query above
@@ -197,26 +249,119 @@ export async function fetchResolvedIssue(arguments_) {
197
249
  if (issue === null) {
198
250
  throw new Error(`Ticket ${ticket.toUpperCase()} not found in Linear`);
199
251
  }
200
- const description = issue.description ?? "";
201
- const repository = parseRepository({
202
- description,
252
+ return {
253
+ uuid: issue.id,
254
+ title: issue.title,
255
+ description: issue.description ?? "",
256
+ teamId: issue.team?.id ?? "",
257
+ labels: issue.labels.nodes,
258
+ stateName: issue.state?.name ?? "",
259
+ blockers: blockersFromRelations(issue.inverseRelations?.nodes ?? []),
260
+ hasMoreBlockers: issue.inverseRelations?.pageInfo.hasNextPage ?? false,
261
+ };
262
+ }
263
+ export async function fetchInProgressIssueCount(arguments_) {
264
+ const { client, config } = arguments_;
265
+ let after = null;
266
+ let count = 0;
267
+ for (;;) {
268
+ // oxlint-disable-next-line no-await-in-loop -- pagination cursor depends on the previous response
269
+ const response = await client.client.rawRequest(`query InProgressIssues($slugId: String!, $stateName: String!, $agentLabelPrefix: String!, $after: String) {
270
+ issues(
271
+ filter: {
272
+ project: { slugId: { eq: $slugId } }
273
+ state: { name: { eq: $stateName } }
274
+ labels: { some: { name: { startsWith: $agentLabelPrefix } } }
275
+ }
276
+ first: ${ISSUES_PAGE_SIZE}
277
+ after: $after
278
+ includeArchived: false
279
+ ) {
280
+ nodes { id }
281
+ pageInfo { hasNextPage endCursor }
282
+ }
283
+ }`, {
284
+ slugId: config.linear.slugId,
285
+ stateName: config.linear.statuses.inProgress,
286
+ agentLabelPrefix: AGENT_LABEL_PREFIX,
287
+ after,
288
+ });
289
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- shape is fixed by our GraphQL query above
290
+ const { issues: page } = response.data;
291
+ count += page.nodes.length;
292
+ if (!page.pageInfo.hasNextPage) {
293
+ return count;
294
+ }
295
+ after = page.pageInfo.endCursor;
296
+ }
297
+ }
298
+ export function resolveRepositoryFor(arguments_) {
299
+ const { description, config } = arguments_;
300
+ if (description === undefined || description.length === 0) {
301
+ return { kind: "missing" };
302
+ }
303
+ const repository = buildRepositoryRegex(config).exec(description)?.[1];
304
+ if (repository === undefined) {
305
+ return { kind: "missing" };
306
+ }
307
+ return { kind: "ok", repository };
308
+ }
309
+ export function resolveModelFor(arguments_) {
310
+ const { labels, config } = arguments_;
311
+ const parsed = parseAgentLabels(labels, config);
312
+ if (parsed === undefined) {
313
+ return { kind: "no-label" };
314
+ }
315
+ if (parsed.model === AGENT_ANY_MODEL) {
316
+ return { kind: "agent-any" };
317
+ }
318
+ if (parsed.disabledFallback !== undefined) {
319
+ return {
320
+ kind: "disabled-fallback",
321
+ requestedModel: parsed.disabledFallback,
322
+ fallbackModel: parsed.model,
323
+ };
324
+ }
325
+ return { kind: "matched", model: parsed.model };
326
+ }
327
+ /**
328
+ * `agent-any` collapses to `models.default` here — manual setup doesn't run
329
+ * the usage-gated `any` resolver, so the caller gets a concrete model name
330
+ * instead of a sentinel that downstream code can't interpret.
331
+ */
332
+ export async function fetchResolvedIssue(arguments_) {
333
+ const { client, config, ticket } = arguments_;
334
+ const raw = await fetchRawLinearIssue({ client, ticket });
335
+ const repositoryResolution = resolveRepositoryFor({
336
+ description: raw.description,
203
337
  config,
204
- repositoryRegex: buildRepositoryRegex(config),
205
338
  ticket: ticket.toUpperCase(),
206
339
  });
340
+ if (repositoryResolution.kind === "missing") {
341
+ throw new RepositoryResolutionError({
342
+ ticket: ticket.toUpperCase(),
343
+ repositories: config.workspace.knownRepositories,
344
+ });
345
+ }
207
346
  // Manual setup is an explicit per-ticket opt-in by the user, so an
208
347
  // unlabeled ticket still resolves to `models.default` — different from
209
348
  // the auto-pickup path, where unlabeled tickets are ignored.
210
- const parsed = parseAgentLabels(issue.labels.nodes, config);
211
- warnIfDisabledFallback(ticket, parsed, config);
212
- const model = parsed === undefined || parsed.model === AGENT_ANY_MODEL ? config.models.default : parsed.model;
349
+ const modelResolution = resolveModelFor({ labels: raw.labels, config });
350
+ warnIfDisabledFallback(ticket, modelResolution, config);
351
+ let model = config.models.default;
352
+ if (modelResolution.kind === "matched") {
353
+ ({ model } = modelResolution);
354
+ }
355
+ else if (modelResolution.kind === "disabled-fallback") {
356
+ model = modelResolution.fallbackModel;
357
+ }
213
358
  return {
214
- uuid: issue.id,
215
- title: issue.title,
216
- description,
217
- repository,
359
+ uuid: raw.uuid,
360
+ title: raw.title,
361
+ description: raw.description,
362
+ repository: repositoryResolution.repository,
218
363
  model,
219
- teamId: issue.team?.id ?? "",
364
+ teamId: raw.teamId,
220
365
  };
221
366
  }
222
367
  function parseRepository(arguments_) {
@@ -278,11 +423,11 @@ function parseAgentLabels(labels, config) {
278
423
  }
279
424
  return fallback;
280
425
  }
281
- function warnIfDisabledFallback(ticket, parsed, config) {
282
- if (parsed?.disabledFallback === undefined) {
426
+ function warnIfDisabledFallback(ticket, modelResolution, config) {
427
+ if (modelResolution.kind !== "disabled-fallback") {
283
428
  return;
284
429
  }
285
- log(`${ticket.toLowerCase()}: agent-${parsed.disabledFallback} label refers to a disabled model; falling back to models.default (${config.models.default})`);
430
+ log(`${ticket.toLowerCase()}: agent-${modelResolution.requestedModel} label refers to a disabled model; falling back to models.default (${config.models.default})`);
286
431
  }
287
432
  function blockersFromRelations(relations) {
288
433
  return relations
@@ -15,6 +15,39 @@ export declare const AGENT_ANY_MODEL = "any";
15
15
  */
16
16
  export type WorkspaceKindSetting = "auto" | "cmux" | "tmux";
17
17
  export declare const WORKSPACE_KIND_SETTINGS: readonly WorkspaceKindSetting[];
18
+ /**
19
+ * Concrete local isolation backend selected for a launch. `safehouse` is
20
+ * macOS-only (clearance HTTP-egress + sandbox profile); `sdx` is Docker
21
+ * Sandboxes (`sbx` CLI) — works on Linux and macOS and is the only known
22
+ * option that lets the agent use Docker safely without exposing the host
23
+ * socket; `none` is an explicit unsandboxed escape hatch.
24
+ */
25
+ export type LocalRunner = "safehouse" | "sdx" | "none";
26
+ /**
27
+ * User-facing local runner setting. `auto` resolves at launch time:
28
+ * macOS picks `safehouse`, Linux picks `sdx`. `none` is never picked
29
+ * implicitly.
30
+ */
31
+ export type LocalRunnerSetting = LocalRunner | "auto";
32
+ export declare const LOCAL_RUNNER_SETTINGS: readonly LocalRunnerSetting[];
33
+ /**
34
+ * Per-model Docker Sandboxes (sdx) binding. Required at launch when
35
+ * `local.runner` resolves to `sdx` so groundcrew knows which sbx agent
36
+ * to address and how to seed the sandbox.
37
+ */
38
+ export interface SandboxDefinition {
39
+ /** sbx agent name (e.g. "claude", "codex"). */
40
+ agent: string;
41
+ /** Optional `sbx run --template` value. */
42
+ template?: string;
43
+ /** Optional `sbx run --kit` values (each passed as a separate flag). */
44
+ kits?: string[];
45
+ /**
46
+ * Setup command run **inside** the sandbox before the agent exec.
47
+ * Defaults to `DEFAULT_SANDBOX_SETUP_COMMAND` when omitted.
48
+ */
49
+ setupCommand?: string;
50
+ }
18
51
  export interface ModelDefinition {
19
52
  /**
20
53
  * Shell command launched for the model. Wrapped with Safehouse/clearance
@@ -32,6 +65,12 @@ export interface ModelDefinition {
32
65
  source?: string;
33
66
  };
34
67
  };
68
+ /**
69
+ * Docker Sandboxes binding. Required when `local.runner` resolves to
70
+ * `sdx` — pure additive: omitted models can still run under `safehouse`
71
+ * or `none` without surprise.
72
+ */
73
+ sandbox?: SandboxDefinition;
35
74
  }
36
75
  /**
37
76
  * User-facing model entry shape. Discriminated union so the type system
@@ -51,6 +90,14 @@ type UserModelDefinition = EnabledUserModelDefinition | DisabledUserModelDefinit
51
90
  * assumed to already have the right Node and npm versions.
52
91
  */
53
92
  export declare const DEFAULT_HOST_SETUP_COMMAND = "if [ -x .claude/setup.sh ]; then ./.claude/setup.sh --deps-only; elif [ -f .claude/setup.sh ] && command -v bash >/dev/null 2>&1; then bash .claude/setup.sh --deps-only; else npm clean-install; fi";
93
+ /**
94
+ * Setup command run inside an sdx (Docker Sandboxes) sandbox before the
95
+ * agent process exec. Independent of the host setup — sandboxes typically
96
+ * lack Node tooling on first start, so we keep the recipe scoped to the
97
+ * common case of an npm-managed repo while still letting per-model
98
+ * `sandbox.setupCommand` override it for languages outside that path.
99
+ */
100
+ export declare const DEFAULT_SANDBOX_SETUP_COMMAND = "if [ -x .claude/setup.sh ]; then ./.claude/setup.sh --deps-only; elif [ -f .claude/setup.sh ] && command -v bash >/dev/null 2>&1; then bash .claude/setup.sh --deps-only; else npm clean-install; fi";
54
101
  /**
55
102
  * Loose user-facing shape — what a `config.ts` file declares.
56
103
  * Fields with defaults are optional; only `linear.projectSlug` and the
@@ -107,6 +154,14 @@ export interface Config {
107
154
  * to fail loudly when the chosen backend is missing.
108
155
  */
109
156
  workspaceKind?: WorkspaceKindSetting;
157
+ /**
158
+ * Local isolation backend selector. Defaults to `"auto"` (macOS →
159
+ * safehouse, Linux → sdx). `"none"` is an explicit unsandboxed escape
160
+ * hatch — never selected implicitly.
161
+ */
162
+ local?: {
163
+ runner?: LocalRunnerSetting;
164
+ };
110
165
  logging?: {
111
166
  /**
112
167
  * Append-mode log file destination. `log()` and `logEvent()` tee here
@@ -158,6 +213,14 @@ export interface ResolvedConfig {
158
213
  * `auto` resolves to cmux when installed, else tmux.
159
214
  */
160
215
  workspaceKind: WorkspaceKindSetting;
216
+ /**
217
+ * Local isolation selection. The user-facing `auto` is preserved here
218
+ * so `localRunner.resolve()` can pick the platform default later — the
219
+ * resolver is the only place that knows the host capabilities.
220
+ */
221
+ local: {
222
+ runner: LocalRunnerSetting;
223
+ };
161
224
  logging: {
162
225
  file: string;
163
226
  };
@@ -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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"}
@@ -17,11 +17,25 @@ export const WORKSPACE_KIND_SETTINGS = [
17
17
  "cmux",
18
18
  "tmux",
19
19
  ];
20
+ export const LOCAL_RUNNER_SETTINGS = [
21
+ "auto",
22
+ "safehouse",
23
+ "sdx",
24
+ "none",
25
+ ];
20
26
  /**
21
27
  * Setup command run inside sibling worktrees on the host. The host is
22
28
  * assumed to already have the right Node and npm versions.
23
29
  */
24
30
  export const DEFAULT_HOST_SETUP_COMMAND = "if [ -x .claude/setup.sh ]; then ./.claude/setup.sh --deps-only; elif [ -f .claude/setup.sh ] && command -v bash >/dev/null 2>&1; then bash .claude/setup.sh --deps-only; else npm clean-install; fi";
31
+ /**
32
+ * Setup command run inside an sdx (Docker Sandboxes) sandbox before the
33
+ * agent process exec. Independent of the host setup — sandboxes typically
34
+ * lack Node tooling on first start, so we keep the recipe scoped to the
35
+ * common case of an npm-managed repo while still letting per-model
36
+ * `sandbox.setupCommand` override it for languages outside that path.
37
+ */
38
+ export const DEFAULT_SANDBOX_SETUP_COMMAND = "if [ -x .claude/setup.sh ]; then ./.claude/setup.sh --deps-only; elif [ -f .claude/setup.sh ] && command -v bash >/dev/null 2>&1; then bash .claude/setup.sh --deps-only; else npm clean-install; fi";
25
39
  const DEFAULT_STATUSES = {
26
40
  todo: "Todo",
27
41
  inProgress: "In Progress",
@@ -194,6 +208,43 @@ function normalizeWorkspaceKind(value, path) {
194
208
  }
195
209
  return value;
196
210
  }
211
+ function isLocalRunnerSetting(value) {
212
+ return typeof value === "string" && LOCAL_RUNNER_SETTINGS.includes(value);
213
+ }
214
+ function normalizeLocalRunner(value, path) {
215
+ if (value === undefined) {
216
+ return undefined;
217
+ }
218
+ if (!isLocalRunnerSetting(value)) {
219
+ fail(`${path} must be one of ${LOCAL_RUNNER_SETTINGS.join(", ")} (got ${JSON.stringify(value)})`);
220
+ }
221
+ return value;
222
+ }
223
+ function normalizeSandbox(value, path) {
224
+ if (!isPlainObject(value)) {
225
+ fail(`${path} must be an object`);
226
+ }
227
+ const { agent, template, kits, setupCommand } = value;
228
+ requireString(agent, `${path}.agent`);
229
+ const trimmedAgent = agent.trim();
230
+ if (trimmedAgent.length === 0) {
231
+ fail(`${path}.agent must be a non-empty string (got ${JSON.stringify(agent)})`);
232
+ }
233
+ const sandbox = { agent: trimmedAgent };
234
+ const normalizedTemplate = normalizeOptionalString(template, `${path}.template`);
235
+ if (normalizedTemplate !== undefined) {
236
+ sandbox.template = normalizedTemplate;
237
+ }
238
+ const normalizedKits = normalizeOptionalStringArray(kits, `${path}.kits`);
239
+ if (normalizedKits !== undefined) {
240
+ sandbox.kits = normalizedKits;
241
+ }
242
+ const normalizedSetup = normalizeOptionalString(setupCommand, `${path}.setupCommand`);
243
+ if (normalizedSetup !== undefined) {
244
+ sandbox.setupCommand = normalizedSetup;
245
+ }
246
+ return sandbox;
247
+ }
197
248
  function failIfLegacyModelKeys(name, override) {
198
249
  if (!isPlainObject(override)) {
199
250
  fail(`models.definitions.${name} must be an object`);
@@ -201,14 +252,11 @@ function failIfLegacyModelKeys(name, override) {
201
252
  if (Object.hasOwn(override, "isolation")) {
202
253
  fail(`models.definitions.${name}.isolation is no longer supported: per-model isolation is no longer supported`);
203
254
  }
204
- if (Object.hasOwn(override, "sandbox")) {
205
- fail(`models.definitions.${name}.sandbox is no longer supported: Docker Sandboxes are no longer supported`);
206
- }
207
255
  if (Object.hasOwn(override, "disabled")) {
208
256
  if (override["disabled"] !== true) {
209
257
  fail(`models.definitions.${name}.disabled must be exactly \`true\` when set (got ${JSON.stringify(override["disabled"])})`);
210
258
  }
211
- const conflicting = ["cmd", "color", "usage"].filter((key) => Object.hasOwn(override, key));
259
+ const conflicting = ["cmd", "color", "usage", "sandbox"].filter((key) => Object.hasOwn(override, key));
212
260
  if (conflicting.length > 0) {
213
261
  fail(`models.definitions.${name}: cannot combine \`disabled: true\` with other fields (${conflicting.join(", ")}). Either disable the model or override its fields, not both.`);
214
262
  }
@@ -258,7 +306,10 @@ function mergeDefinitions(user) {
258
306
  if (override.usage !== undefined) {
259
307
  candidate.usage = override.usage;
260
308
  }
261
- const { cmd, color, usage } = candidate;
309
+ if (override.sandbox !== undefined) {
310
+ candidate.sandbox = normalizeSandbox(override.sandbox, `models.definitions.${name}.sandbox`);
311
+ }
312
+ const { cmd, color, usage, sandbox } = candidate;
262
313
  if (typeof cmd !== "string" || cmd.length === 0) {
263
314
  fail(`models.definitions.${name}.cmd must be a non-empty string`);
264
315
  }
@@ -269,6 +320,9 @@ function mergeDefinitions(user) {
269
320
  if (usage !== undefined) {
270
321
  definition.usage = usage;
271
322
  }
323
+ if (sandbox !== undefined) {
324
+ definition.sandbox = sandbox;
325
+ }
272
326
  merged[name] = definition;
273
327
  }
274
328
  return merged;
@@ -294,10 +348,14 @@ function applyDefaults(user) {
294
348
  requireString(user.linear.projectSlug, "linear.projectSlug");
295
349
  requireObject(user.workspace, "workspace");
296
350
  if (isPlainObject(user.models) && Object.hasOwn(user.models, "isolation")) {
297
- fail("models.isolation is no longer supported: local isolation is always Safehouse; remove this key");
351
+ fail("models.isolation is no longer supported: set `local.runner` ('safehouse' | 'sdx' | 'none' | 'auto') instead");
298
352
  }
299
353
  if (Object.hasOwn(user, "remote")) {
300
- fail("remote is no longer supported: groundcrew is macOS + Safehouse only; remove the remote block from your config");
354
+ fail("remote is no longer supported: groundcrew runs locally via safehouse/sdx/none; remove the remote block from your config");
355
+ }
356
+ const userLocal = user.local;
357
+ if (userLocal !== undefined && !isPlainObject(userLocal)) {
358
+ fail("local must be an object");
301
359
  }
302
360
  const slugId = extractSlugId(user.linear.projectSlug);
303
361
  if (slugId === undefined) {
@@ -323,6 +381,9 @@ function applyDefaults(user) {
323
381
  initial: user.prompts?.initial ?? DEFAULT_PROMPT_INITIAL,
324
382
  },
325
383
  workspaceKind: normalizeWorkspaceKind(user.workspaceKind, "workspaceKind") ?? "auto",
384
+ local: {
385
+ runner: normalizeLocalRunner(userLocal?.runner, "local.runner") ?? "auto",
386
+ },
326
387
  logging: {
327
388
  file: expandHome(normalizeOptionalString(user.logging?.file, "logging.file") ?? defaultLogFile()),
328
389
  },
@@ -379,6 +440,13 @@ function validate(config) {
379
440
  }
380
441
  requireString(codexbar.provider, `${usagePath}.codexbar.provider`);
381
442
  }
443
+ if (definition.sandbox !== undefined) {
444
+ requireString(definition.sandbox.agent, `models.definitions.${name}.sandbox.agent`);
445
+ }
446
+ }
447
+ /* v8 ignore next 5 @preserve -- normalizeLocalRunner rejects invalid strings before validate() runs; this is a belt-and-suspenders guard */
448
+ if (!LOCAL_RUNNER_SETTINGS.includes(config.local.runner)) {
449
+ fail(`local.runner must be one of ${LOCAL_RUNNER_SETTINGS.join(", ")} (got ${JSON.stringify(config.local.runner)})`);
382
450
  }
383
451
  // Disabled-default check must run before the generic "not a key" check so
384
452
  // the user gets the specific "is disabled" message instead of a stale-list
@@ -0,0 +1,40 @@
1
+ import type { SandboxDefinition } from "./config.ts";
2
+ /**
3
+ * Derive a deterministic sbx sandbox name from the repository + model
4
+ * tuple so `crew sandbox auth <repo>` and the subsequent `crew local`
5
+ * launch agree on which sandbox to target. Lowercased and reduced to the
6
+ * sbx-safe charset (`a-z0-9.+-`) so unusual repo names still round-trip
7
+ * cleanly. Keep the prefix stable — doctor and teardown use it to
8
+ * identify groundcrew-owned sandboxes.
9
+ */
10
+ export declare function sandboxNameFor(arguments_: {
11
+ repository: string;
12
+ model: string;
13
+ }): string;
14
+ /**
15
+ * Probe `sbx ls` to see whether a sandbox with `sandboxName` already
16
+ * exists. Used by `crew sandbox auth` to switch between create vs reuse
17
+ * branches without surfacing the raw sbx error on first run.
18
+ */
19
+ export declare function sandboxExists(sandboxName: string, signal?: AbortSignal): Promise<boolean>;
20
+ interface EnsureSandboxArguments {
21
+ sandboxName: string;
22
+ sandbox: SandboxDefinition;
23
+ /**
24
+ * Host path bound into the sandbox at the same path. Pass the workspace
25
+ * `projectDir` so all per-ticket worktrees (siblings of the bare repo
26
+ * clone) are visible to `sbx exec -w <worktreeDir>` after creation.
27
+ */
28
+ mountPath: string;
29
+ }
30
+ /**
31
+ * Idempotent guard: ensure a Docker Sandboxes container exists for the
32
+ * given repository + model. Probes `sbx ls`; if `sandboxName` is missing,
33
+ * calls `sbx create --name <name> [--template <t>] [--kit <k>]... <agent>
34
+ * <mountPath>` to provision it. First-time agent auth still happens inside
35
+ * the sandbox the first time `sbx exec` runs the agent — `create` only
36
+ * provisions the container, it does not attach.
37
+ */
38
+ export declare function ensureSandbox(arguments_: EnsureSandboxArguments, signal?: AbortSignal): Promise<void>;
39
+ export {};
40
+ //# sourceMappingURL=dockerSandbox.d.ts.map
@@ -0,0 +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;AAErD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAMxF;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;CACnB;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,sBAAsB,EAClC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAqBf"}
@@ -0,0 +1,58 @@
1
+ import { runCommandAsync } from "./commandRunner.js";
2
+ /**
3
+ * Derive a deterministic sbx sandbox name from the repository + model
4
+ * tuple so `crew sandbox auth <repo>` and the subsequent `crew local`
5
+ * launch agree on which sandbox to target. Lowercased and reduced to the
6
+ * sbx-safe charset (`a-z0-9.+-`) so unusual repo names still round-trip
7
+ * cleanly. Keep the prefix stable — doctor and teardown use it to
8
+ * identify groundcrew-owned sandboxes.
9
+ */
10
+ export function sandboxNameFor(arguments_) {
11
+ const raw = `groundcrew-${arguments_.repository}-${arguments_.model}`.toLowerCase();
12
+ return raw
13
+ .replaceAll(/[^a-z0-9.+-]+/g, "-")
14
+ .replaceAll(/-+/g, "-")
15
+ .replaceAll(/^-|-$/g, "");
16
+ }
17
+ /**
18
+ * Probe `sbx ls` to see whether a sandbox with `sandboxName` already
19
+ * exists. Used by `crew sandbox auth` to switch between create vs reuse
20
+ * branches without surfacing the raw sbx error on first run.
21
+ */
22
+ export async function sandboxExists(sandboxName, signal) {
23
+ const out = signal === undefined
24
+ ? await runCommandAsync("sbx", ["ls"])
25
+ : await runCommandAsync("sbx", ["ls"], { signal });
26
+ return out.split("\n").some((line) => line.trim().split(/\s+/)[0] === sandboxName);
27
+ }
28
+ /**
29
+ * Idempotent guard: ensure a Docker Sandboxes container exists for the
30
+ * given repository + model. Probes `sbx ls`; if `sandboxName` is missing,
31
+ * calls `sbx create --name <name> [--template <t>] [--kit <k>]... <agent>
32
+ * <mountPath>` to provision it. First-time agent auth still happens inside
33
+ * the sandbox the first time `sbx exec` runs the agent — `create` only
34
+ * provisions the container, it does not attach.
35
+ */
36
+ export async function ensureSandbox(arguments_, signal) {
37
+ if (await sandboxExists(arguments_.sandboxName, signal)) {
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);
46
+ }
47
+ createArguments.push(arguments_.sandbox.agent, arguments_.mountPath);
48
+ const options = signal === undefined ? {} : { signal };
49
+ try {
50
+ await runCommandAsync("sbx", createArguments, options);
51
+ }
52
+ catch (error) {
53
+ if (await sandboxExists(arguments_.sandboxName, signal)) {
54
+ return;
55
+ }
56
+ throw error;
57
+ }
58
+ }
@@ -6,18 +6,28 @@
6
6
  export interface HostCapabilities {
7
7
  /** True when the `safehouse` binary is on PATH. */
8
8
  hasSafehouse: boolean;
9
+ /** True when the `sbx` (Docker Sandboxes) binary is on PATH. */
10
+ hasSbx: boolean;
9
11
  /** True when the `cmux` binary is on PATH. */
10
12
  hasCmux: boolean;
11
13
  /** True when the `tmux` binary is on PATH. */
12
14
  hasTmux: boolean;
13
15
  /** True when the host platform is macOS. Safehouse is macOS-only. */
14
16
  isMacOS: boolean;
17
+ /** True when the host platform is Linux. */
18
+ isLinux: boolean;
15
19
  /**
16
20
  * True when the host platform is one Safehouse supports. Safehouse is
17
21
  * macOS-only at time of writing; local setup uses this to reject Linux
18
22
  * or WSL before creating a worktree.
19
23
  */
20
24
  isSafehouseSupported: boolean;
25
+ /**
26
+ * True when sdx (Docker Sandboxes) is supportable on this platform —
27
+ * sbx is published for both macOS and Linux, so this stays in sync with
28
+ * "macOS || Linux". WSL inherits Linux capabilities transparently.
29
+ */
30
+ isSdxSupported: boolean;
21
31
  }
22
32
  /**
23
33
  * Resolves a binary on PATH the same way `which` does. Returns the first
@@ -1 +1 @@
1
- {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/lib/host.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,MAAM,WAAW,gBAAgB;IAC/B,mDAAmD;IACnD,YAAY,EAAE,OAAO,CAAC;IACtB,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,oBAAoB,EAAE,OAAO,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAc1F;AAED,wBAAsB,sBAAsB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAc5F"}
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/lib/host.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,MAAM,WAAW,gBAAgB;IAC/B,mDAAmD;IACnD,YAAY,EAAE,OAAO,CAAC;IACtB,gEAAgE;IAChE,MAAM,EAAE,OAAO,CAAC;IAChB,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,OAAO,EAAE,OAAO,CAAC;IACjB,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,oBAAoB,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAc1F;AAED,wBAAsB,sBAAsB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAmB5F"}