@clipboard-health/groundcrew 4.48.2 → 4.48.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +24 -0
- package/dist/lib/launchCommand.d.ts.map +1 -1
- package/dist/lib/launchCommand.js +6 -2
- package/dist/lib/preLaunchEmptyCheck.d.ts +29 -0
- package/dist/lib/preLaunchEmptyCheck.d.ts.map +1 -0
- package/dist/lib/preLaunchEmptyCheck.js +33 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA0SH,wBAAsB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAyF/C"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -210,6 +210,29 @@ function gatherToolTargets(config) {
|
|
|
210
210
|
}
|
|
211
211
|
return [...all].map(([token, hint]) => (hint === undefined ? { token } : { token, hint }));
|
|
212
212
|
}
|
|
213
|
+
function preLaunchEnvAdvisories(config) {
|
|
214
|
+
const advisories = [];
|
|
215
|
+
for (const [agentName, definition] of Object.entries(config.agents.definitions)) {
|
|
216
|
+
const names = definition.preLaunchEnv ?? [];
|
|
217
|
+
if (names.length === 0) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
// The runtime empty-check is spliced only when `preLaunch` is present
|
|
221
|
+
// (see `hostPreLaunchSourceAndReadPrompt` / `preLaunchPromptAndExec` in
|
|
222
|
+
// `src/lib/launchCommand.ts`). Without `preLaunch`, forwarded values come
|
|
223
|
+
// straight from the parent shell and no launch-time guard runs.
|
|
224
|
+
const warnClause = definition.preLaunch === undefined
|
|
225
|
+
? "no preLaunch is set, so empty values are NOT checked at launch"
|
|
226
|
+
: "empty values are warned at launch";
|
|
227
|
+
advisories.push({
|
|
228
|
+
name: `preLaunchEnv: ${agentName}`,
|
|
229
|
+
ok: true,
|
|
230
|
+
required: false,
|
|
231
|
+
hint: `forwards ${names.length} var(s): ${names.join(", ")}. ${warnClause}.`,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return advisories;
|
|
235
|
+
}
|
|
213
236
|
function agentCliHint(agentName, token) {
|
|
214
237
|
if (!isBuiltInAgentName(agentName)) {
|
|
215
238
|
return undefined;
|
|
@@ -289,6 +312,7 @@ export async function doctor() {
|
|
|
289
312
|
const check = await checkCmd(token, required, required ? hint : "required for local runs");
|
|
290
313
|
checks.push(check);
|
|
291
314
|
}
|
|
315
|
+
checks.push(...preLaunchEnvAdvisories(config));
|
|
292
316
|
const usageGatedAgents = gatedAgents(config);
|
|
293
317
|
if (usageGatedAgents.length > 0) {
|
|
294
318
|
const codexbarPath = await which("codexbar");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launchCommand.d.ts","sourceRoot":"","sources":["../../src/lib/launchCommand.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EAC1B,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"launchCommand.d.ts","sourceRoot":"","sources":["../../src/lib/launchCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { BUILD_SECRET_NAMES, hasPreLaunchEnv, } from "./config.js";
|
|
4
4
|
import { clearanceAllowHostsFilesFromEnvironment } from "./clearanceAllowlist.js";
|
|
5
|
+
import { buildPreLaunchEmptyCheckLines } from "./preLaunchEmptyCheck.js";
|
|
5
6
|
import { shellSingleQuote } from "./shell.js";
|
|
6
7
|
export { shellSingleQuote } from "./shell.js";
|
|
7
8
|
/**
|
|
@@ -116,7 +117,7 @@ function hostSourceSecrets(secretsFile) {
|
|
|
116
117
|
function preLaunchPromptAndExec(arguments_) {
|
|
117
118
|
const lines = [];
|
|
118
119
|
if (arguments_.definition.preLaunch !== undefined) {
|
|
119
|
-
lines.push(renderPreLaunch(arguments_.definition.preLaunch, arguments_.worktreeDir));
|
|
120
|
+
lines.push(renderPreLaunch(arguments_.definition.preLaunch, arguments_.worktreeDir), ...buildPreLaunchEmptyCheckLines(arguments_.definition.preLaunchEnv ?? []));
|
|
120
121
|
}
|
|
121
122
|
lines.push(`_p=$(cat ${shellSingleQuote(arguments_.promptFile)})`, `rm -rf ${shellSingleQuote(arguments_.promptDir)}`, arguments_.execLine);
|
|
122
123
|
return lines;
|
|
@@ -150,7 +151,10 @@ function renderPrepareAndAgentCommand(arguments_) {
|
|
|
150
151
|
function hostPreLaunchSourceAndReadPrompt(arguments_) {
|
|
151
152
|
const lines = [];
|
|
152
153
|
if (arguments_.definition.preLaunch !== undefined) {
|
|
153
|
-
|
|
154
|
+
const preLaunchEnv = arguments_.definition.preLaunchEnv ?? [];
|
|
155
|
+
// Order is load-bearing: unset scrubs inherited values so the empty-check
|
|
156
|
+
// reads what preLaunch actually produced, not what the parent shell leaked.
|
|
157
|
+
lines.push(unsetEnvironmentLine([...BUILD_SECRET_NAMES, ...preLaunchEnv]), renderPreLaunch(arguments_.definition.preLaunch, arguments_.worktreeDir), ...buildPreLaunchEmptyCheckLines(preLaunchEnv));
|
|
154
158
|
}
|
|
155
159
|
lines.push(...hostSourceSecrets(arguments_.secretsFile), `_p=$(cat ${shellSingleQuote(arguments_.promptFile)})`, `rm -rf ${shellSingleQuote(arguments_.promptDir)}`);
|
|
156
160
|
return lines;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emit POSIX-shell lines that WARN (to stderr, never abort) when a
|
|
3
|
+
* `preLaunchEnv` name resolves to an empty string after `preLaunch` runs.
|
|
4
|
+
* Length-0 is the reliable tell for the `export VAR="$(cat missing-file)"`
|
|
5
|
+
* failure mode: `export` masks the substitution's non-zero exit status, so
|
|
6
|
+
* `set -e` and exit-code checks do not catch it.
|
|
7
|
+
*
|
|
8
|
+
* Warn-only is deliberate — a hard-abort mode is a separate opt-in, so the
|
|
9
|
+
* chain (prompt read, prompt-dir cleanup, exec) must not be short-circuited
|
|
10
|
+
* by an empty value here.
|
|
11
|
+
*
|
|
12
|
+
* Returns `[]` for an empty input list so callers can splat unconditionally
|
|
13
|
+
* into their `&&` chain.
|
|
14
|
+
*
|
|
15
|
+
* Caveat: the check reads the shell's post-preLaunch view of the var. On the
|
|
16
|
+
* safehouse chain (`hostPreLaunchSourceAndReadPrompt`) the caller unsets each
|
|
17
|
+
* `preLaunchEnv` name before running `preLaunch`, so the check truly reflects
|
|
18
|
+
* what `preLaunch` produced. On the unwrapped-host chain
|
|
19
|
+
* (`preLaunchPromptAndExec`) no unset happens first, so a value inherited
|
|
20
|
+
* from the parent shell can survive `preLaunch` and mask a failed `export`.
|
|
21
|
+
* That asymmetry is a property of the runners; closing it requires an env
|
|
22
|
+
* scrub on the runner=none path, tracked as a follow-up.
|
|
23
|
+
*
|
|
24
|
+
* Names must be POSIX identifiers (`[A-Za-z_][A-Za-z0-9_]*`); groundcrew's
|
|
25
|
+
* `validatePreLaunchEnv` in `src/lib/config.ts` enforces that, so no shell
|
|
26
|
+
* escaping is done here.
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildPreLaunchEmptyCheckLines(names: readonly string[]): string[];
|
|
29
|
+
//# sourceMappingURL=preLaunchEmptyCheck.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preLaunchEmptyCheck.d.ts","sourceRoot":"","sources":["../../src/lib/preLaunchEmptyCheck.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAQhF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emit POSIX-shell lines that WARN (to stderr, never abort) when a
|
|
3
|
+
* `preLaunchEnv` name resolves to an empty string after `preLaunch` runs.
|
|
4
|
+
* Length-0 is the reliable tell for the `export VAR="$(cat missing-file)"`
|
|
5
|
+
* failure mode: `export` masks the substitution's non-zero exit status, so
|
|
6
|
+
* `set -e` and exit-code checks do not catch it.
|
|
7
|
+
*
|
|
8
|
+
* Warn-only is deliberate — a hard-abort mode is a separate opt-in, so the
|
|
9
|
+
* chain (prompt read, prompt-dir cleanup, exec) must not be short-circuited
|
|
10
|
+
* by an empty value here.
|
|
11
|
+
*
|
|
12
|
+
* Returns `[]` for an empty input list so callers can splat unconditionally
|
|
13
|
+
* into their `&&` chain.
|
|
14
|
+
*
|
|
15
|
+
* Caveat: the check reads the shell's post-preLaunch view of the var. On the
|
|
16
|
+
* safehouse chain (`hostPreLaunchSourceAndReadPrompt`) the caller unsets each
|
|
17
|
+
* `preLaunchEnv` name before running `preLaunch`, so the check truly reflects
|
|
18
|
+
* what `preLaunch` produced. On the unwrapped-host chain
|
|
19
|
+
* (`preLaunchPromptAndExec`) no unset happens first, so a value inherited
|
|
20
|
+
* from the parent shell can survive `preLaunch` and mask a failed `export`.
|
|
21
|
+
* That asymmetry is a property of the runners; closing it requires an env
|
|
22
|
+
* scrub on the runner=none path, tracked as a follow-up.
|
|
23
|
+
*
|
|
24
|
+
* Names must be POSIX identifiers (`[A-Za-z_][A-Za-z0-9_]*`); groundcrew's
|
|
25
|
+
* `validatePreLaunchEnv` in `src/lib/config.ts` enforces that, so no shell
|
|
26
|
+
* escaping is done here.
|
|
27
|
+
*/
|
|
28
|
+
export function buildPreLaunchEmptyCheckLines(names) {
|
|
29
|
+
if (names.length === 0) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
return [...new Set(names)].map((name) => `if [ -z "\${${name}-}" ]; then echo "preLaunchEnv: ${name} is empty after preLaunch (value length 0)" >&2; fi`);
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipboard-health/groundcrew",
|
|
3
|
-
"version": "4.48.
|
|
3
|
+
"version": "4.48.3",
|
|
4
4
|
"description": "Linear-driven orchestrator that launches AI coding agents in git worktrees, with workspace lifecycle and usage tracking.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"jscpd": "5.0.12",
|
|
91
91
|
"knip": "6.27.0",
|
|
92
92
|
"lint-staged": "17.0.8",
|
|
93
|
-
"markdownlint-cli2": "0.23.
|
|
93
|
+
"markdownlint-cli2": "0.23.1",
|
|
94
94
|
"nx": "22.7.7",
|
|
95
95
|
"oxfmt": "0.59.0",
|
|
96
96
|
"oxlint": "1.73.0",
|