@clipboard-health/groundcrew 4.0.3 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +37 -13
  2. package/crew.config.example.ts +5 -18
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +27 -49
  5. package/dist/commands/resumeWorkspace.d.ts.map +1 -1
  6. package/dist/commands/resumeWorkspace.js +1 -2
  7. package/dist/commands/setupWorkspace.d.ts.map +1 -1
  8. package/dist/commands/setupWorkspace.js +1 -7
  9. package/dist/commands/upgrade.d.ts +0 -11
  10. package/dist/commands/upgrade.d.ts.map +1 -1
  11. package/dist/commands/upgrade.js +14 -100
  12. package/dist/lib/agentLaunch.d.ts +0 -6
  13. package/dist/lib/agentLaunch.d.ts.map +1 -1
  14. package/dist/lib/agentLaunch.js +1 -12
  15. package/dist/lib/cmuxAdapter.d.ts +8 -0
  16. package/dist/lib/cmuxAdapter.d.ts.map +1 -0
  17. package/dist/lib/cmuxAdapter.js +163 -0
  18. package/dist/lib/config.d.ts +2 -76
  19. package/dist/lib/config.d.ts.map +1 -1
  20. package/dist/lib/config.js +29 -102
  21. package/dist/lib/launchCommand.d.ts +3 -3
  22. package/dist/lib/sandboxName.d.ts +9 -0
  23. package/dist/lib/sandboxName.d.ts.map +1 -0
  24. package/dist/lib/sandboxName.js +12 -0
  25. package/dist/lib/tmuxAdapter.d.ts +9 -0
  26. package/dist/lib/tmuxAdapter.d.ts.map +1 -0
  27. package/dist/lib/tmuxAdapter.js +156 -0
  28. package/dist/lib/workspaceAdapter.d.ts +79 -0
  29. package/dist/lib/workspaceAdapter.d.ts.map +1 -0
  30. package/dist/lib/workspaceAdapter.js +17 -0
  31. package/dist/lib/workspaces.d.ts +7 -55
  32. package/dist/lib/workspaces.d.ts.map +1 -1
  33. package/dist/lib/workspaces.js +8 -404
  34. package/package.json +1 -2
  35. package/dist/commands/sandbox/auth.d.ts +0 -3
  36. package/dist/commands/sandbox/auth.d.ts.map +0 -1
  37. package/dist/commands/sandbox/auth.js +0 -227
  38. package/dist/commands/sandbox/index.d.ts +0 -2
  39. package/dist/commands/sandbox/index.d.ts.map +0 -1
  40. package/dist/commands/sandbox/index.js +0 -47
  41. package/dist/commands/sandbox/inspect.d.ts +0 -2
  42. package/dist/commands/sandbox/inspect.d.ts.map +0 -1
  43. package/dist/commands/sandbox/inspect.js +0 -18
  44. package/dist/commands/sandbox/lifecycle.d.ts +0 -7
  45. package/dist/commands/sandbox/lifecycle.d.ts.map +0 -1
  46. package/dist/commands/sandbox/lifecycle.js +0 -68
  47. package/dist/commands/sandbox/model.d.ts +0 -10
  48. package/dist/commands/sandbox/model.d.ts.map +0 -1
  49. package/dist/commands/sandbox/model.js +0 -37
  50. package/dist/commands/sandbox/picker.d.ts +0 -20
  51. package/dist/commands/sandbox/picker.d.ts.map +0 -1
  52. package/dist/commands/sandbox/picker.js +0 -23
  53. package/dist/commands/setupRepos.d.ts +0 -44
  54. package/dist/commands/setupRepos.d.ts.map +0 -1
  55. package/dist/commands/setupRepos.js +0 -212
  56. package/dist/lib/dockerSandbox.d.ts +0 -43
  57. package/dist/lib/dockerSandbox.d.ts.map +0 -1
  58. package/dist/lib/dockerSandbox.js +0 -69
  59. package/dist/lib/sandboxGitDefaults.d.ts +0 -10
  60. package/dist/lib/sandboxGitDefaults.d.ts.map +0 -1
  61. package/dist/lib/sandboxGitDefaults.js +0 -31
  62. package/dist/lib/upgrade.d.ts +0 -66
  63. package/dist/lib/upgrade.d.ts.map +0 -1
  64. package/dist/lib/upgrade.js +0 -178
@@ -0,0 +1,163 @@
1
+ /**
2
+ * cmux Workspace backend. cmux is the macOS TUI; workspaces surface in its
3
+ * own app, so `accessHint` has nothing concise to emit. cmux can paint a
4
+ * per-workspace status pill, which `open` applies best-effort.
5
+ */
6
+ import { isSignalAborted, runWorkspaceCommand, } from "./workspaceAdapter.js";
7
+ import { errorMessage, log } from "./util.js";
8
+ export const cmuxAdapter = {
9
+ async open(spec, signal) {
10
+ const output = await runWorkspaceCommand("cmux", [
11
+ "--json",
12
+ "new-workspace",
13
+ "--name",
14
+ spec.name,
15
+ "--cwd",
16
+ spec.cwd,
17
+ "--command",
18
+ spec.command,
19
+ ], signal);
20
+ const workspaceId = extractCmuxOpenId(output);
21
+ if (workspaceId === undefined) {
22
+ log(`cmux new-workspace returned unrecognized output for ${spec.name}; if a workspace was created, run \`cmux close-workspace\` manually.`);
23
+ throw new Error(`Unexpected cmux output: ${output}`);
24
+ }
25
+ if (spec.status !== undefined) {
26
+ try {
27
+ await applyCmuxStatus(workspaceId, spec.status, signal);
28
+ }
29
+ catch (error) {
30
+ // Status pills are best-effort. cmux v2+ dropped `set-status` entirely,
31
+ // so swallow that specific gap silently; surface anything else so a real
32
+ // regression doesn't hide behind the same swallow.
33
+ if (!isCmuxSetStatusUnsupported(error)) {
34
+ log(`cmux set-status failed for ${spec.name} (continuing): ${errorMessage(error)}`);
35
+ }
36
+ }
37
+ }
38
+ },
39
+ async list(signal) {
40
+ const raw = await listCmuxRaw(signal);
41
+ return raw?.map((ws) => ({ name: ws.title }));
42
+ },
43
+ async close(name, signal) {
44
+ const raw = await listCmuxRaw(signal);
45
+ if (raw === undefined) {
46
+ // cmux v2 `workspace.close` rejects titles, so forwarding `name`
47
+ // would always fail. The list failure has already been logged by
48
+ // `listCmuxRaw`; bail rather than guarantee a downstream error.
49
+ log(`cmux close-workspace skipped for ${name}: list-workspaces failed, no usable id`);
50
+ return { kind: "unavailable" };
51
+ }
52
+ const match = raw.find((ws) => ws.title === name);
53
+ if (match === undefined) {
54
+ return { kind: "missing" };
55
+ }
56
+ try {
57
+ await closeCmuxWorkspace(match.id, signal);
58
+ return { kind: "closed" };
59
+ }
60
+ catch (error) {
61
+ if (isSignalAborted(signal)) {
62
+ throw error;
63
+ }
64
+ const remaining = await listCmuxRaw(signal);
65
+ if (remaining === undefined) {
66
+ return { kind: "unavailable", error };
67
+ }
68
+ const isStillPresent = remaining.some((ws) => ws.title === name);
69
+ if (!isStillPresent) {
70
+ return { kind: "closed" };
71
+ }
72
+ throw error;
73
+ }
74
+ },
75
+ accessHint(_name) {
76
+ // cmux is a TUI; users surface workspaces by launching the cmux app,
77
+ // not a shell command. No useful hint to emit.
78
+ // oxlint-disable-next-line unicorn/no-useless-undefined -- explicit signal that the backend has no hint
79
+ return undefined;
80
+ },
81
+ };
82
+ function parseCmuxList(output) {
83
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- cmux --json list-workspaces always emits this shape
84
+ const parsed = JSON.parse(output);
85
+ const items = [];
86
+ /* v8 ignore next @preserve -- cmux always emits a workspaces field; default keeps the loop safe */
87
+ for (const ws of parsed.workspaces ?? []) {
88
+ if (typeof ws.title !== "string" || ws.title.length === 0) {
89
+ continue;
90
+ }
91
+ const id = pickCmuxId(ws);
92
+ if (id === undefined) {
93
+ log(`cmux list-workspaces returned workspace "${ws.title}" without a usable id or ref; skipping`);
94
+ continue;
95
+ }
96
+ items.push({ title: ws.title, id });
97
+ }
98
+ return items;
99
+ }
100
+ /**
101
+ * The stable workspace handle cmux v2 expects in JSON-RPC params. Prefer
102
+ * the UUID; fall back to the legacy `workspace:N` short ref when older
103
+ * cmux builds don't surface it. Returns `undefined` when neither is
104
+ * available — cmux v2 `workspace.close` rejects titles, so we must never
105
+ * forward `title` as a workspace handle.
106
+ */
107
+ function pickCmuxId(ws) {
108
+ if (typeof ws.id === "string" && ws.id.length > 0) {
109
+ return ws.id;
110
+ }
111
+ if (typeof ws.ref === "string" && ws.ref.length > 0) {
112
+ return ws.ref;
113
+ }
114
+ return undefined;
115
+ }
116
+ async function listCmuxRaw(signal) {
117
+ try {
118
+ return parseCmuxList(await runWorkspaceCommand("cmux", ["--json", "list-workspaces"], signal));
119
+ }
120
+ catch (error) {
121
+ if (isSignalAborted(signal)) {
122
+ throw error;
123
+ }
124
+ log(`cmux list-workspaces failed: ${errorMessage(error)}`);
125
+ return undefined;
126
+ }
127
+ }
128
+ function extractCmuxOpenId(output) {
129
+ try {
130
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- cmux --json prints a workspace_id/ref object
131
+ const parsed = JSON.parse(output);
132
+ const uuid = parsed.workspace_id ?? parsed.id ?? "";
133
+ if (uuid.length > 0) {
134
+ return uuid;
135
+ }
136
+ const ref = parsed.workspace_ref ?? parsed.ref ?? "";
137
+ if (ref.length > 0) {
138
+ return ref;
139
+ }
140
+ }
141
+ catch {
142
+ /* not JSON; fall through to regex */
143
+ }
144
+ const match = /workspace:\d+/.exec(output);
145
+ return match ? match[0] : undefined;
146
+ }
147
+ async function applyCmuxStatus(workspaceId, status, signal) {
148
+ const arguments_ = ["set-status", "model", status.text];
149
+ if (status.icon !== undefined) {
150
+ arguments_.push("--icon", status.icon);
151
+ }
152
+ if (status.color !== undefined) {
153
+ arguments_.push("--color", status.color);
154
+ }
155
+ arguments_.push("--workspace", workspaceId);
156
+ await runWorkspaceCommand("cmux", arguments_, signal);
157
+ }
158
+ async function closeCmuxWorkspace(workspaceId, signal) {
159
+ await runWorkspaceCommand("cmux", ["close-workspace", "--workspace", workspaceId], signal);
160
+ }
161
+ function isCmuxSetStatusUnsupported(error) {
162
+ return errorMessage(error).includes('unknown command "set-status"');
163
+ }
@@ -41,16 +41,12 @@ export type LocalRunnerSetting = LocalRunner | "auto";
41
41
  export declare const LOCAL_RUNNER_SETTINGS: readonly LocalRunnerSetting[];
42
42
  /**
43
43
  * Per-model Docker Sandboxes (sdx) binding. Required at launch when
44
- * `local.runner` resolves to `sdx` so groundcrew knows which sbx agent
45
- * to address and how to seed the sandbox.
44
+ * `local.runner` resolves to `sdx` so groundcrew knows which existing
45
+ * sbx sandbox to address.
46
46
  */
47
47
  export interface SandboxDefinition {
48
48
  /** sbx agent name (e.g. "claude", "codex"). */
49
49
  agent: string;
50
- /** Optional `sbx run --template` value. */
51
- template?: string;
52
- /** Optional `sbx run --kit` values (each passed as a separate flag). */
53
- kits?: string[];
54
50
  /**
55
51
  * Setup command run **inside** the sandbox before the agent exec.
56
52
  * Defaults to the shared `.groundcrew/setup.sh --deps-only` convention
@@ -58,42 +54,6 @@ export interface SandboxDefinition {
58
54
  */
59
55
  setupCommand?: string;
60
56
  }
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
- }
97
57
  export interface ModelDefinition {
98
58
  /**
99
59
  * Shell command launched for the model. Wrapped with Safehouse/clearance
@@ -201,32 +161,6 @@ export interface Config {
201
161
  local?: {
202
162
  runner?: LocalRunnerSetting;
203
163
  };
204
- /**
205
- * Sandbox-wide settings. `authRecipes` lets users register additional
206
- * tools (github, npm, gcloud, …) for `crew sandbox auth <model>` to
207
- * authenticate inside the sandbox. The auth flow is picker-driven —
208
- * registered recipes show up in the picker alongside the shipped ones,
209
- * and a user recipe under the same key (e.g. "claude") overrides the
210
- * shipped one.
211
- */
212
- sandbox?: {
213
- authRecipes?: Record<string, AuthRecipe>;
214
- /**
215
- * When true (default), every `crew sandbox ensure` / `auth` run applies
216
- * a small set of git defaults inside the sandbox so robot commits push
217
- * over `gh`-managed HTTPS regardless of how the user cloned the repo:
218
- *
219
- * - disable GPG signing for commits and tags
220
- * - rewrite `git@github.com:` and `ssh://git@github.com/` URLs to
221
- * `https://github.com/` so push uses gh's credential helper
222
- * - after a successful `github` auth recipe login, run
223
- * `gh auth setup-git` inside the sandbox
224
- *
225
- * Set `false` to skip both the git-config block and the post-login
226
- * `gh auth setup-git` step.
227
- */
228
- gitDefaults?: boolean;
229
- };
230
164
  logging?: {
231
165
  /**
232
166
  * Append-mode log file destination. `log()` and `logEvent()` tee here
@@ -281,14 +215,6 @@ export interface ResolvedConfig {
281
215
  local: {
282
216
  runner: LocalRunnerSetting;
283
217
  };
284
- /**
285
- * Sandbox-wide settings. Always present after defaults; `authRecipes`
286
- * is `{}` when the user provides none.
287
- */
288
- sandbox: {
289
- authRecipes: Record<string, AuthRecipe>;
290
- gitDefaults: boolean;
291
- };
292
218
  logging: {
293
219
  file: string;
294
220
  };
@@ -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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"}
@@ -126,29 +126,6 @@ function normalizeOptionalString(value, path) {
126
126
  }
127
127
  return value.trim();
128
128
  }
129
- function normalizeOptionalBoolean(value, path) {
130
- if (value === undefined) {
131
- return undefined;
132
- }
133
- if (typeof value !== "boolean") {
134
- fail(`${path} must be a boolean`);
135
- }
136
- return value;
137
- }
138
- function normalizeOptionalStringArray(value, path) {
139
- if (value === undefined) {
140
- return undefined;
141
- }
142
- if (!Array.isArray(value)) {
143
- fail(`${path} must be an array`);
144
- }
145
- return value.map((entry, index) => {
146
- if (typeof entry !== "string" || entry.trim().length === 0) {
147
- fail(`${path}[${index}] must be a non-empty string`);
148
- }
149
- return entry.trim();
150
- });
151
- }
152
129
  function isWorkspaceKindSetting(value) {
153
130
  return (typeof value === "string" && WORKSPACE_KIND_SETTINGS.includes(value));
154
131
  }
@@ -177,27 +154,29 @@ function normalizeSandbox(value, path) {
177
154
  if (!isPlainObject(value)) {
178
155
  fail(`${path} must be an object`);
179
156
  }
180
- const { agent, template, kits, setupCommand } = value;
157
+ if (Object.hasOwn(value, "template")) {
158
+ failRemovedConfigKey(`${path}.template`, "Groundcrew no longer creates or re-templates sdx sandboxes.");
159
+ }
160
+ if (Object.hasOwn(value, "kits")) {
161
+ failRemovedConfigKey(`${path}.kits`, "Groundcrew no longer creates sdx sandboxes or applies sandbox kits.");
162
+ }
163
+ const { agent, setupCommand } = value;
181
164
  requireString(agent, `${path}.agent`);
182
165
  const trimmedAgent = agent.trim();
183
166
  if (trimmedAgent.length === 0) {
184
167
  fail(`${path}.agent must be a non-empty string (got ${JSON.stringify(agent)})`);
185
168
  }
186
169
  const sandbox = { agent: trimmedAgent };
187
- const normalizedTemplate = normalizeOptionalString(template, `${path}.template`);
188
- if (normalizedTemplate !== undefined) {
189
- sandbox.template = normalizedTemplate;
190
- }
191
- const normalizedKits = normalizeOptionalStringArray(kits, `${path}.kits`);
192
- if (normalizedKits !== undefined) {
193
- sandbox.kits = normalizedKits;
194
- }
195
170
  const normalizedSetup = normalizeOptionalString(setupCommand, `${path}.setupCommand`);
196
171
  if (normalizedSetup !== undefined) {
197
172
  sandbox.setupCommand = normalizedSetup;
198
173
  }
199
174
  return sandbox;
200
175
  }
176
+ function failRemovedConfigKey(path, reason) {
177
+ fail(`${path} is no longer supported: ${reason} ` +
178
+ "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.");
179
+ }
201
180
  function failIfLegacyModelKeys(name, override) {
202
181
  if (!isPlainObject(override)) {
203
182
  fail(`models.definitions.${name} must be an object`);
@@ -296,11 +275,6 @@ function requireObject(value, path) {
296
275
  fail(`${path} must be an object (got ${JSON.stringify(value)})`);
297
276
  }
298
277
  }
299
- function requireOptionalObject(value, path) {
300
- if (value !== undefined && !isPlainObject(value)) {
301
- fail(`${path} must be an object`);
302
- }
303
- }
304
278
  function failOnLegacyLinearShape(user) {
305
279
  if (!Object.hasOwn(user, "linear")) {
306
280
  return;
@@ -312,6 +286,21 @@ function failOnLegacyLinearShape(user) {
312
286
  "If you only want a subset of your Linear tickets to be picked up, leave the unwanted tickets unassigned or remove their `agent-*` label.",
313
287
  ].join("\n"));
314
288
  }
289
+ function failOnRemovedSandboxSettings(user) {
290
+ const { sandbox } = user;
291
+ if (sandbox === undefined) {
292
+ return;
293
+ }
294
+ if (!isPlainObject(sandbox)) {
295
+ fail("sandbox must be an object");
296
+ }
297
+ if (Object.hasOwn(sandbox, "authRecipes")) {
298
+ failRemovedConfigKey("sandbox.authRecipes", "Groundcrew no longer drives in-sandbox auth flows.");
299
+ }
300
+ if (Object.hasOwn(sandbox, "gitDefaults")) {
301
+ failRemovedConfigKey("sandbox.gitDefaults", "Groundcrew no longer seeds git defaults inside sdx sandboxes.");
302
+ }
303
+ }
315
304
  function normalizeSources(raw) {
316
305
  if (raw === undefined) {
317
306
  return [];
@@ -348,71 +337,14 @@ function normalizeSources(raw) {
348
337
  // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- structural validation above guarantees array of {kind: string} entries; per-source Zod validation lives in buildSources
349
338
  return raw;
350
339
  }
351
- function normalizeAuthRecipes(value, path) {
352
- if (value === undefined) {
353
- return {};
354
- }
355
- if (!isPlainObject(value)) {
356
- fail(`${path} must be an object`);
357
- }
358
- const recipes = {};
359
- for (const [key, raw] of Object.entries(value)) {
360
- const recipePath = `${path}.${key}`;
361
- if (!isPlainObject(raw)) {
362
- fail(`${recipePath} must be an object`);
363
- }
364
- const { displayName, binary, loginArgs, statusArgs, authenticatedPattern, kind, env } = raw;
365
- requireString(displayName, `${recipePath}.displayName`);
366
- const loginArray = normalizeOptionalStringArray(loginArgs, `${recipePath}.loginArgs`);
367
- const statusArray = normalizeOptionalStringArray(statusArgs, `${recipePath}.statusArgs`);
368
- if (loginArray === undefined) {
369
- fail(`${recipePath}.loginArgs is required`);
370
- }
371
- if (statusArray === undefined) {
372
- fail(`${recipePath}.statusArgs is required`);
373
- }
374
- if (!(authenticatedPattern instanceof RegExp)) {
375
- fail(`${recipePath}.authenticatedPattern must be a RegExp`);
376
- }
377
- const recipe = {
378
- displayName,
379
- loginArgs: loginArray,
380
- statusArgs: statusArray,
381
- authenticatedPattern,
382
- };
383
- const binaryString = normalizeOptionalString(binary, `${recipePath}.binary`);
384
- if (binaryString !== undefined) {
385
- recipe.binary = binaryString;
386
- }
387
- if (kind !== undefined) {
388
- if (kind !== "agent" && kind !== "tool") {
389
- fail(`${recipePath}.kind must be "agent" or "tool"`);
390
- }
391
- recipe.kind = kind;
392
- }
393
- if (env !== undefined) {
394
- if (!isPlainObject(env)) {
395
- fail(`${recipePath}.env must be an object`);
396
- }
397
- const normalizedEnv = {};
398
- for (const [envKey, envValue] of Object.entries(env)) {
399
- if (typeof envValue !== "string") {
400
- fail(`${recipePath}.env.${envKey} must be a string`);
401
- }
402
- normalizedEnv[envKey] = envValue;
403
- }
404
- recipe.env = normalizedEnv;
405
- }
406
- recipes[key] = recipe;
407
- }
408
- return recipes;
409
- }
410
340
  function applyDefaults(user) {
411
341
  // Guard the top-level shape before reading nested fields, so a
412
342
  // malformed runtime config produces a `groundcrew config: ...` error
413
343
  // instead of a raw `TypeError: Cannot read properties of undefined`.
414
344
  // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- `user` is loosely typed input from the loader; we narrow with requireObject below
415
- failOnLegacyLinearShape(user);
345
+ const rawUser = user;
346
+ failOnLegacyLinearShape(rawUser);
347
+ failOnRemovedSandboxSettings(rawUser);
416
348
  requireObject(user.workspace, "workspace");
417
349
  if (isPlainObject(user.models) && Object.hasOwn(user.models, "isolation")) {
418
350
  fail("models.isolation is no longer supported: set `local.runner` ('safehouse' | 'sdx' | 'none' | 'auto') instead");
@@ -420,7 +352,6 @@ function applyDefaults(user) {
420
352
  if (Object.hasOwn(user, "remote")) {
421
353
  fail("remote is no longer supported: groundcrew runs locally via safehouse/sdx/none; remove the remote block from your config");
422
354
  }
423
- requireOptionalObject(user.sandbox, "sandbox");
424
355
  const userLocal = user.local;
425
356
  if (userLocal !== undefined && !isPlainObject(userLocal)) {
426
357
  fail("local must be an object");
@@ -445,10 +376,6 @@ function applyDefaults(user) {
445
376
  local: {
446
377
  runner: normalizeLocalRunner(userLocal?.runner, "local.runner") ?? "auto",
447
378
  },
448
- sandbox: {
449
- authRecipes: normalizeAuthRecipes(user.sandbox?.authRecipes, "sandbox.authRecipes"),
450
- gitDefaults: normalizeOptionalBoolean(user.sandbox?.gitDefaults, "sandbox.gitDefaults") ?? true,
451
- },
452
379
  logging: {
453
380
  file: expandHome(normalizeOptionalString(user.logging?.file, "logging.file") ?? defaultLogFile()),
454
381
  },
@@ -36,9 +36,9 @@ interface LaunchCommandArguments {
36
36
  runner: LocalRunner;
37
37
  /**
38
38
  * sbx sandbox name when `runner === "sdx"`. Derived by the caller from
39
- * `sandboxNameFor({ repository, model })`. Required for sdx; ignored
40
- * otherwise. Kept off the model definition so a model can launch under
41
- * safehouse on one host and sdx on another without config edits.
39
+ * `sandboxNameFor({ agent })`. Required for sdx; ignored otherwise.
40
+ * Kept off the model definition so a model can launch under safehouse
41
+ * on one host and sdx on another without config edits.
42
42
  */
43
43
  sandboxName?: string | undefined;
44
44
  }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Derive the sbx sandbox name groundcrew expects for a given sbx agent.
3
+ * Groundcrew only addresses this existing sandbox at launch time; it does
4
+ * not probe, create, mutate, or remove it.
5
+ */
6
+ export declare function sandboxNameFor(arguments_: {
7
+ agent: string;
8
+ }): string;
9
+ //# sourceMappingURL=sandboxName.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandboxName.d.ts","sourceRoot":"","sources":["../../src/lib/sandboxName.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAMpE"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Derive the sbx sandbox name groundcrew expects for a given sbx agent.
3
+ * Groundcrew only addresses this existing sandbox at launch time; it does
4
+ * not probe, create, mutate, or remove it.
5
+ */
6
+ export function sandboxNameFor(arguments_) {
7
+ const raw = `groundcrew-${arguments_.agent}`.toLowerCase();
8
+ return raw
9
+ .replaceAll(/[^a-z0-9.+-]+/g, "-")
10
+ .replaceAll(/-+/g, "-")
11
+ .replaceAll(/^-|-$/g, "");
12
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * tmux Workspace backend. Workspaces live as windows inside one dedicated
3
+ * `groundcrew` tmux session; the window name is the ticket id. tmux can't
4
+ * paint status pills, so `open` silently drops `spec.status`. This is the
5
+ * Linux/WSL path where cmux is unavailable.
6
+ */
7
+ import { type Adapter } from "./workspaceAdapter.ts";
8
+ export declare const tmuxAdapter: Adapter;
9
+ //# sourceMappingURL=tmuxAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmuxAdapter.d.ts","sourceRoot":"","sources":["../../src/lib/tmuxAdapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,KAAK,OAAO,EAIb,MAAM,uBAAuB,CAAC;AAY/B,eAAO,MAAM,WAAW,EAAE,OAgEzB,CAAC"}