@clipboard-health/groundcrew 4.2.3 → 4.2.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"launchCommand.d.ts","sourceRoot":"","sources":["../../src/lib/launchCommand.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAGzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAcvF;AAID;;;GAGG;AACH,eAAO,MAAM,aAAa,mFACwD,CAAC;AAiCnF,UAAU,sBAAsB;IAC9B,UAAU,EAAE,eAAe,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,sBAAsB,GAAG,MAAM,CAK7E"}
1
+ {"version":3,"file":"launchCommand.d.ts","sourceRoot":"","sources":["../../src/lib/launchCommand.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAGzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAcvF;AAID;;;GAGG;AACH,eAAO,MAAM,aAAa,mFACwD,CAAC;AAiCnF,UAAU,sBAAsB;IAC9B,UAAU,EAAE,eAAe,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,sBAAsB,GAAG,MAAM,CAQ7E"}
@@ -65,20 +65,35 @@ export function buildLaunchCommand(arguments_) {
65
65
  if (arguments_.runner === "sdx") {
66
66
  return buildSdxLaunchCommand(arguments_);
67
67
  }
68
- return buildHostLaunchCommand(arguments_);
68
+ if (shouldWrapWithSafehouse(arguments_)) {
69
+ return buildSafehouseLaunchCommand(arguments_);
70
+ }
71
+ return buildUnwrappedHostLaunchCommand(arguments_);
69
72
  }
70
- function buildHostLaunchCommand(arguments_) {
73
+ /**
74
+ * The Safehouse wrap applies only when `runner === "safehouse"` and `cmd` does
75
+ * not already invoke `safehouse` itself. A `safehouse …` cmd owns its own
76
+ * sandbox flags, and we can't splice setup into a command we don't control, so
77
+ * those (and the `none` runner) fall through to the unwrapped host path.
78
+ */
79
+ function shouldWrapWithSafehouse(arguments_) {
80
+ if (arguments_.runner !== "safehouse") {
81
+ return false;
82
+ }
83
+ return !/^safehouse(\s|$)/.test(arguments_.definition.cmd);
84
+ }
85
+ /**
86
+ * Unsandboxed host launch (`runner === "none"`, or a `safehouse …` cmd that
87
+ * brings its own wrap). Setup, secret sourcing, and the agent all run on the
88
+ * host shell because there is no groundcrew-managed sandbox to run them inside.
89
+ */
90
+ function buildUnwrappedHostLaunchCommand(arguments_) {
71
91
  const promptDir = dirname(arguments_.promptFile);
72
92
  const agentCmd = renderAgentCommand({
73
93
  agentCmd: arguments_.definition.cmd,
74
94
  worktreeDir: arguments_.worktreeDir,
75
95
  sandboxName: "",
76
96
  });
77
- const wrapped = wrapAgentForHostRunner({
78
- runner: arguments_.runner,
79
- rawCmd: arguments_.definition.cmd,
80
- agentCmd,
81
- });
82
97
  const lines = [`cd ${shellSingleQuote(arguments_.worktreeDir)}`];
83
98
  if (arguments_.secretsFile !== undefined) {
84
99
  lines.push(sourceSecretsLine(arguments_.secretsFile));
@@ -87,28 +102,40 @@ function buildHostLaunchCommand(arguments_) {
87
102
  if (arguments_.secretsFile !== undefined) {
88
103
  lines.push(unsetSecretsLine());
89
104
  }
90
- lines.push(`_p=$(cat ${shellSingleQuote(arguments_.promptFile)})`, `rm -rf ${shellSingleQuote(promptDir)}`, `exec ${wrapped} "$_p"`);
105
+ lines.push(`_p=$(cat ${shellSingleQuote(arguments_.promptFile)})`, `rm -rf ${shellSingleQuote(promptDir)}`, `exec ${agentCmd} "$_p"`);
91
106
  return lines.join(" && ");
92
107
  }
93
- function wrapAgentForHostRunner(arguments_) {
94
- if (arguments_.runner === "none") {
95
- return arguments_.agentCmd;
96
- }
97
- // buildLaunchCommand routes `sdx` through buildSdxLaunchCommand, so the
98
- // only remaining shape here is `safehouse`. Treat the explicit branch as
99
- // the safehouse wrap to keep this function readable; the `sdx` arm exists
100
- // only to satisfy TS's exhaustiveness checker.
101
- /* v8 ignore next 3 @preserve -- buildLaunchCommand short-circuits sdx before calling this helper */
102
- if (arguments_.runner === "sdx") {
103
- return arguments_.agentCmd;
108
+ /**
109
+ * Safehouse launch. Setup runs *inside* the `safehouse-clearance` wrap (mirroring
110
+ * the sdx runner) so the repo's `.groundcrew/setup.sh` and its `npm install` are
111
+ * filesystem-isolated and egress-restricted, rather than running on the bare host.
112
+ *
113
+ * Build secrets are sourced into the host launch shell so Safehouse can forward
114
+ * them into the sandbox via `--env-pass` (Safehouse's `--env=FILE` mode otherwise
115
+ * strips them); they're `unset` inside the wrap after setup so the agent process
116
+ * never inherits them. The host keeps only `cd`, the prompt read, and the wrap exec.
117
+ */
118
+ function buildSafehouseLaunchCommand(arguments_) {
119
+ const promptDir = dirname(arguments_.promptFile);
120
+ const agentCmd = renderAgentCommand({
121
+ agentCmd: arguments_.definition.cmd,
122
+ worktreeDir: arguments_.worktreeDir,
123
+ sandboxName: "",
124
+ });
125
+ const innerParts = [setupWithStatusReporting(SETUP_COMMAND)];
126
+ if (arguments_.secretsFile !== undefined) {
127
+ innerParts.push(unsetSecretsLine());
104
128
  }
105
- // safehouse: skip the wrap if `cmd` already starts with `safehouse` so
106
- // legacy configs don't double-wrap.
107
- const cmdStartsWithSafehouse = /^safehouse(\s|$)/.test(arguments_.rawCmd);
108
- if (cmdStartsWithSafehouse) {
109
- return arguments_.agentCmd;
129
+ innerParts.push(`exec ${agentCmd} "$@"`);
130
+ const innerCommand = innerParts.join("; ");
131
+ // Trailing space keeps the flag and `sh` separated; empty when no secrets.
132
+ const envPassFlag = arguments_.secretsFile === undefined ? "" : `--env-pass=${BUILD_SECRET_NAMES.join(",")} `;
133
+ const lines = [`cd ${shellSingleQuote(arguments_.worktreeDir)}`];
134
+ if (arguments_.secretsFile !== undefined) {
135
+ lines.push(sourceSecretsLine(arguments_.secretsFile));
110
136
  }
111
- return [shellSingleQuote(SAFEHOUSE_CLEARANCE_WRAPPER_PATH), arguments_.agentCmd].join(" ");
137
+ lines.push(`_p=$(cat ${shellSingleQuote(arguments_.promptFile)})`, `rm -rf ${shellSingleQuote(promptDir)}`, `exec ${shellSingleQuote(SAFEHOUSE_CLEARANCE_WRAPPER_PATH)} ${envPassFlag}sh -lc ${shellSingleQuote(innerCommand)} sh "$_p"`);
138
+ return lines.join(" && ");
112
139
  }
113
140
  function buildSdxLaunchCommand(arguments_) {
114
141
  /* v8 ignore next 5 @preserve -- setupWorkspace passes sandboxName + sandbox config when picking sdx; missing fields are programmer errors */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/groundcrew",
3
- "version": "4.2.3",
3
+ "version": "4.2.4",
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",