@clipboard-health/groundcrew 1.12.5 → 1.12.6
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 +2 -2
- package/dist/commands/remoteSetup.d.ts +0 -1
- package/dist/commands/remoteSetup.d.ts.map +1 -1
- package/dist/commands/remoteSetup.js +199 -17
- package/dist/lib/spriteRemoteRunnerProvider.d.ts.map +1 -1
- package/dist/lib/spriteRemoteRunnerProvider.js +24 -15
- package/dist/lib/worktrees.d.ts.map +1 -1
- package/dist/lib/worktrees.js +31 -6
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -84,7 +84,7 @@ This installs the `crew` binary. `@clipboard-health/clearance` is pulled in tran
|
|
|
84
84
|
|
|
85
85
|
Watch `${XDG_CACHE_HOME:-$HOME/.cache}/clearance/clearance.log` for `DENY` lines and add only the domains your agents actually need.
|
|
86
86
|
|
|
87
|
-
7. **Optional: prepare a remote runner.** The current remote provider is Sprite, so setup is intentionally explicit: choose which agent CLIs and MCP servers to
|
|
87
|
+
7. **Optional: prepare a remote runner.** The current remote provider is Sprite, so setup is intentionally explicit: choose which agent CLIs to authenticate and which MCP servers to add. `--mcp` adds and authenticates only the servers you name; it does not attempt to authenticate every MCP server visible in your Claude account.
|
|
88
88
|
|
|
89
89
|
```bash
|
|
90
90
|
crew remote setup crew-claude-1 \
|
|
@@ -100,7 +100,7 @@ This installs the `crew` binary. `@clipboard-health/clearance` is pulled in tran
|
|
|
100
100
|
--checkpoint
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
Known MCP aliases are `linear`, `slack`, and `notion`. For another HTTP MCP server, pass `--mcp name=https://example.com/mcp`. The command creates the remote runner if needed, prepares `~/dev`, configures Git, runs selected auth flows, adds selected MCP servers to Claude Code,
|
|
103
|
+
Known MCP aliases are `linear`, `slack`, and `notion`. For another HTTP MCP server, pass `--mcp name=https://example.com/mcp`. The command creates the remote runner if needed, prepares `~/dev`, configures Git, runs selected auth flows, and adds selected MCP servers to Claude Code. With the Sprite provider, `--claude` detects Claude Code's dynamic localhost OAuth callback port inside the remote runner and temporarily runs `sprite proxy` for that port while login runs. Selected MCP servers authenticate through one interactive Claude Code session by default, and when combined with `--claude`, setup skips the separate headless Claude login and uses that one session for both Claude login and MCP auth while forwarding Claude localhost callback ports. `--copy-local-codex-auth` copies `${CODEX_HOME:-$HOME/.codex}/auth.json` into `/home/sprite/.codex/auth.json` and then verifies `codex login status`; it never prints the file contents. `--datadog` installs a pinned `pup` release in the remote runner, verifies its checksum, installs the `dd-pup` guidance for Claude and Codex, verifies `pup auth status`, and when auth is missing temporarily runs `sprite proxy` for the OAuth callback while `pup auth login --read-only` runs in the remote runner. Open the Datadog URL printed by `pup` if your terminal does not open it automatically.
|
|
104
104
|
|
|
105
105
|
Repo setup is separate from runner setup and should run after the ticket branch exists, immediately before launching an agent. It clones/fetches the repo in the remote runner using `git.remote`, checks out the requested branch (creating it from the base branch when it does not exist on that remote), forwards only build-time secrets for the dependency install, removes the temporary secret file, clears those env vars, and then exits. It uses groundcrew's remote setup command.
|
|
106
106
|
|
|
@@ -11,7 +11,6 @@ export interface RemoteSetupOptions {
|
|
|
11
11
|
shouldCopyLocalCodexAuth?: boolean;
|
|
12
12
|
shouldSetupDatadog?: boolean;
|
|
13
13
|
shouldAuthenticateGithub: boolean;
|
|
14
|
-
shouldAuthenticateMcp: boolean;
|
|
15
14
|
shouldCheckpoint: boolean;
|
|
16
15
|
checkpointComment: string;
|
|
17
16
|
gitName?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remoteSetup.d.ts","sourceRoot":"","sources":["../../src/commands/remoteSetup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"remoteSetup.d.ts","sourceRoot":"","sources":["../../src/commands/remoteSetup.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,kBAAkB,CAAC;AAkB1B,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,wBAAwB,EAAE,OAAO,CAAC;IAClC,uBAAuB,EAAE,OAAO,CAAC;IACjC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,wBAAwB,EAAE,OAAO,CAAC;IAClC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,SAAS,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,4BAA4B,EAAE,OAAO,CAAC;IACtC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAiBD,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAilCD,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsC9F;AAUD,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAItF;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrF;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAItF;AAED,wBAAsB,2BAA2B,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhG;AAYD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqClF;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2B7D"}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { homedir, tmpdir } from "node:os";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
|
-
import { setTimeout as sleep } from "node:timers/promises";
|
|
5
4
|
import { DEFAULT_REMOTE_SETUP_COMMAND, loadConfig, } from "../lib/config.js";
|
|
6
5
|
import { shellSingleQuote } from "../lib/shell.js";
|
|
7
6
|
import { getRemoteRunnerProvider, remotePathJoin, remoteConfigWithRunnerName, remoteRepositoryDirectoryName, remoteRepositorySlug, } from "../lib/spriteRemoteRunnerProvider.js";
|
|
8
|
-
import { log, readEnvironmentVariable, writeOutput } from "../lib/util.js";
|
|
7
|
+
import { errorMessage, log, readEnvironmentVariable, writeOutput } from "../lib/util.js";
|
|
9
8
|
const KNOWN_MCP_SERVER_URLS = {
|
|
10
9
|
linear: "https://mcp.linear.app/mcp",
|
|
11
10
|
slack: "https://mcp.slack.com/mcp",
|
|
12
11
|
notion: "https://mcp.notion.com/mcp",
|
|
13
12
|
};
|
|
14
13
|
const DEFAULT_CHECKPOINT_COMMENT = "groundcrew remote runner baseline: selected agent auth, git identity, and MCP config";
|
|
15
|
-
const CLAUDE_SUBSCRIPTION_LOGIN_FLAG = ["--claude", "ai"].join("");
|
|
16
14
|
const DEFAULT_GIT_REMOTE = "origin";
|
|
17
15
|
const BOOTSTRAP_SECRET_FLAGS_CONFLICT_MESSAGE = "--secret and --no-secrets are mutually exclusive. Use --secret to forward selected build secrets or --no-secrets to disable secret forwarding.";
|
|
16
|
+
const CLAUDE_AUTH_CALLBACK_PORT_RETRY_ATTEMPTS = 80;
|
|
17
|
+
const CLAUDE_AUTH_CALLBACK_PORT_RETRY_DELAY_MS = 250;
|
|
18
18
|
const DATADOG_PUP_VERSION = "0.63.0";
|
|
19
19
|
const DATADOG_OAUTH_CALLBACK_PORT = 8000;
|
|
20
20
|
const DATADOG_AUTH_STATUS_RETRY_ATTEMPTS = 5;
|
|
@@ -22,6 +22,11 @@ const DATADOG_AUTH_STATUS_RETRY_DELAY_MS = 250;
|
|
|
22
22
|
const REMOTE_SECRETS_FILE = "/tmp/groundcrew-build-secrets.env";
|
|
23
23
|
const REMOTE_CODEX_AUTH_UPLOAD_FILE = "/tmp/groundcrew-codex-auth.json";
|
|
24
24
|
const REMOTE_CODEX_AUTH_FILE = "/home/sprite/.codex/auth.json";
|
|
25
|
+
async function sleep(milliseconds) {
|
|
26
|
+
await new Promise((_resolve) => {
|
|
27
|
+
setTimeout(_resolve, milliseconds);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
25
30
|
function usage() {
|
|
26
31
|
return [
|
|
27
32
|
"Usage:",
|
|
@@ -38,9 +43,8 @@ function usage() {
|
|
|
38
43
|
" --copy-local-codex-auth With --codex, copy local CODEX_HOME auth.json into the remote runner",
|
|
39
44
|
" --datadog Install pup, add dd-pup skills, and authenticate Datadog",
|
|
40
45
|
" --github Authenticate gh for GitHub PRs",
|
|
41
|
-
" --mcp <alias|name=url> Add
|
|
46
|
+
" --mcp <alias|name=url> Add one MCP server; repeat for multiple",
|
|
42
47
|
" Known aliases: linear, slack, notion",
|
|
43
|
-
" --skip-mcp-auth Add selected MCP servers but do not launch Claude /mcp",
|
|
44
48
|
" --git-name <name> Set git user.name inside the remote runner",
|
|
45
49
|
" --git-email <email> Set git user.email inside the remote runner",
|
|
46
50
|
" --checkpoint Create a provider checkpoint after setup",
|
|
@@ -142,7 +146,6 @@ function parseArguments(argv) {
|
|
|
142
146
|
let shouldCopyLocalCodexAuth = false;
|
|
143
147
|
let shouldSetupDatadog = false;
|
|
144
148
|
let shouldAuthenticateGithub = false;
|
|
145
|
-
let shouldAuthenticateMcp = true;
|
|
146
149
|
let shouldCheckpoint = false;
|
|
147
150
|
let checkpointComment = DEFAULT_CHECKPOINT_COMMENT;
|
|
148
151
|
let gitName;
|
|
@@ -177,10 +180,6 @@ function parseArguments(argv) {
|
|
|
177
180
|
index += 1;
|
|
178
181
|
continue;
|
|
179
182
|
}
|
|
180
|
-
if (argument === "--skip-mcp-auth") {
|
|
181
|
-
shouldAuthenticateMcp = false;
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
183
|
if (argument === "--git-name") {
|
|
185
184
|
gitName = requireValue(argv, index, "--git-name");
|
|
186
185
|
index += 1;
|
|
@@ -215,7 +214,6 @@ function parseArguments(argv) {
|
|
|
215
214
|
shouldCopyLocalCodexAuth,
|
|
216
215
|
shouldSetupDatadog,
|
|
217
216
|
shouldAuthenticateGithub,
|
|
218
|
-
shouldAuthenticateMcp,
|
|
219
217
|
shouldCheckpoint,
|
|
220
218
|
checkpointComment,
|
|
221
219
|
mcpServers,
|
|
@@ -418,10 +416,185 @@ async function authenticateClaude(provider, config) {
|
|
|
418
416
|
return;
|
|
419
417
|
}
|
|
420
418
|
log("Starting Claude subscription auth inside the remote runner");
|
|
421
|
-
|
|
419
|
+
writeOutput();
|
|
420
|
+
writeOutput("Claude OAuth will run inside the remote runner.");
|
|
421
|
+
writeOutput("Groundcrew will forward the localhost callback port after Claude opens it.");
|
|
422
|
+
writeOutput();
|
|
423
|
+
const controller = new AbortController();
|
|
424
|
+
let isLoginComplete = false;
|
|
425
|
+
let loginError;
|
|
426
|
+
const login = provider
|
|
427
|
+
.runTtyCommand({
|
|
422
428
|
config,
|
|
423
|
-
remoteArguments: ["claude", "auth", "login"
|
|
429
|
+
remoteArguments: ["claude", "auth", "login"],
|
|
430
|
+
options: { signal: controller.signal },
|
|
431
|
+
})
|
|
432
|
+
.then(() => {
|
|
433
|
+
isLoginComplete = true;
|
|
434
|
+
return null;
|
|
435
|
+
}, (error) => {
|
|
436
|
+
isLoginComplete = true;
|
|
437
|
+
loginError = new Error(String(error), { cause: error });
|
|
438
|
+
return null;
|
|
424
439
|
});
|
|
440
|
+
let proxy;
|
|
441
|
+
try {
|
|
442
|
+
const callbackPort = await waitForClaudeCallbackPort({
|
|
443
|
+
provider,
|
|
444
|
+
config,
|
|
445
|
+
isLoginComplete: () => isLoginComplete,
|
|
446
|
+
});
|
|
447
|
+
if (callbackPort !== undefined) {
|
|
448
|
+
writeOutput(`Groundcrew is forwarding local port ${callbackPort} to the remote Claude callback server.`);
|
|
449
|
+
proxy = await provider.startPortProxy(config, callbackPort);
|
|
450
|
+
}
|
|
451
|
+
await login;
|
|
452
|
+
if (loginError !== undefined) {
|
|
453
|
+
throw new Error(loginError.message, { cause: loginError });
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
finally {
|
|
457
|
+
if (proxy !== undefined) {
|
|
458
|
+
await closePortProxyBestEffort(proxy);
|
|
459
|
+
}
|
|
460
|
+
if (!isLoginComplete) {
|
|
461
|
+
controller.abort();
|
|
462
|
+
await login;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
async function waitForClaudeCallbackPort(arguments_) {
|
|
467
|
+
for (let attempt = 1; attempt <= CLAUDE_AUTH_CALLBACK_PORT_RETRY_ATTEMPTS; attempt += 1) {
|
|
468
|
+
if (arguments_.isLoginComplete()) {
|
|
469
|
+
return undefined;
|
|
470
|
+
}
|
|
471
|
+
// oxlint-disable-next-line no-await-in-loop -- bounded polling detects Claude's dynamic localhost callback port.
|
|
472
|
+
const callbackPort = await detectClaudeCallbackPort(arguments_.provider, arguments_.config);
|
|
473
|
+
if (callbackPort !== undefined) {
|
|
474
|
+
return callbackPort;
|
|
475
|
+
}
|
|
476
|
+
if (arguments_.isLoginComplete()) {
|
|
477
|
+
return undefined;
|
|
478
|
+
}
|
|
479
|
+
if (attempt < CLAUDE_AUTH_CALLBACK_PORT_RETRY_ATTEMPTS) {
|
|
480
|
+
// oxlint-disable-next-line no-await-in-loop -- retries are intentionally spaced while auth starts.
|
|
481
|
+
await sleep(CLAUDE_AUTH_CALLBACK_PORT_RETRY_DELAY_MS);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
throw new Error("Timed out waiting for Claude to open its OAuth callback port inside the remote runner.");
|
|
485
|
+
}
|
|
486
|
+
async function detectClaudeCallbackPort(provider, config) {
|
|
487
|
+
const output = await provider.runCommand({
|
|
488
|
+
config,
|
|
489
|
+
remoteArguments: ["bash", "-lc", claudeCallbackPortProbeCommand()],
|
|
490
|
+
});
|
|
491
|
+
return parseClaudeCallbackPorts(String(output)).at(0);
|
|
492
|
+
}
|
|
493
|
+
async function detectClaudeCallbackPorts(provider, config) {
|
|
494
|
+
const output = await provider.runCommand({
|
|
495
|
+
config,
|
|
496
|
+
remoteArguments: ["bash", "-lc", claudeCallbackPortProbeCommand()],
|
|
497
|
+
});
|
|
498
|
+
return parseClaudeCallbackPorts(String(output));
|
|
499
|
+
}
|
|
500
|
+
function claudeCallbackPortProbeCommand() {
|
|
501
|
+
return "ss --no-header --listening --tcp --processes --numeric 2>/dev/null | grep claude || true";
|
|
502
|
+
}
|
|
503
|
+
function parseClaudeCallbackPorts(output) {
|
|
504
|
+
const ports = [];
|
|
505
|
+
for (const line of output.split("\n")) {
|
|
506
|
+
const localAddress = line.trim().split(/\s+/).at(3);
|
|
507
|
+
if (localAddress === undefined || !line.includes("claude")) {
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
const match = /:(\d+)$/.exec(localAddress);
|
|
511
|
+
if (match === null) {
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
const port = Number(match[1]);
|
|
515
|
+
if (Number.isInteger(port) && port > 0 && !ports.includes(port)) {
|
|
516
|
+
ports.push(port);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return ports;
|
|
520
|
+
}
|
|
521
|
+
async function runClaudeTtyCommandWithOptionalCallbackProxy(arguments_) {
|
|
522
|
+
const { provider, config, remoteArguments } = arguments_;
|
|
523
|
+
const controller = new AbortController();
|
|
524
|
+
const proxies = new Map();
|
|
525
|
+
let isCommandComplete = false;
|
|
526
|
+
let commandError;
|
|
527
|
+
let watcherError;
|
|
528
|
+
const command = provider
|
|
529
|
+
.runTtyCommand({ config, remoteArguments, options: { signal: controller.signal } })
|
|
530
|
+
.then(() => {
|
|
531
|
+
isCommandComplete = true;
|
|
532
|
+
return null;
|
|
533
|
+
}, (error) => {
|
|
534
|
+
isCommandComplete = true;
|
|
535
|
+
commandError = new Error(String(error), { cause: error });
|
|
536
|
+
return null;
|
|
537
|
+
});
|
|
538
|
+
const watcher = watchClaudeCallbackPorts({
|
|
539
|
+
provider,
|
|
540
|
+
config,
|
|
541
|
+
isCommandComplete: () => isCommandComplete,
|
|
542
|
+
proxies,
|
|
543
|
+
}).catch((error) => {
|
|
544
|
+
if (isCommandComplete) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
watcherError = new Error(String(error), { cause: error });
|
|
548
|
+
controller.abort();
|
|
549
|
+
});
|
|
550
|
+
try {
|
|
551
|
+
await command;
|
|
552
|
+
await watcher;
|
|
553
|
+
if (watcherError !== undefined) {
|
|
554
|
+
throw new Error(watcherError.message, { cause: watcherError });
|
|
555
|
+
}
|
|
556
|
+
if (commandError !== undefined) {
|
|
557
|
+
throw new Error(commandError.message, { cause: commandError });
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
finally {
|
|
561
|
+
isCommandComplete = true;
|
|
562
|
+
await watcher;
|
|
563
|
+
await closeClaudeCallbackProxies(proxies);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
async function watchClaudeCallbackPorts(arguments_) {
|
|
567
|
+
while (!arguments_.isCommandComplete()) {
|
|
568
|
+
// oxlint-disable-next-line no-await-in-loop -- interactive Claude can open localhost callbacks at any point during MCP auth.
|
|
569
|
+
const callbackPorts = await detectClaudeCallbackPorts(arguments_.provider, arguments_.config);
|
|
570
|
+
for (const callbackPort of callbackPorts) {
|
|
571
|
+
if (arguments_.proxies.has(callbackPort)) {
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
writeOutput(`Groundcrew is forwarding local port ${callbackPort} to the remote Claude callback server.`);
|
|
575
|
+
// oxlint-disable-next-line no-await-in-loop -- proxies must bind distinct local ports one at a time.
|
|
576
|
+
const proxy = await arguments_.provider.startPortProxy(arguments_.config, callbackPort);
|
|
577
|
+
arguments_.proxies.set(callbackPort, proxy);
|
|
578
|
+
}
|
|
579
|
+
if (!arguments_.isCommandComplete()) {
|
|
580
|
+
// oxlint-disable-next-line no-await-in-loop -- polling stays active only while the user is in Claude.
|
|
581
|
+
await sleep(CLAUDE_AUTH_CALLBACK_PORT_RETRY_DELAY_MS);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
async function closeClaudeCallbackProxies(proxies) {
|
|
586
|
+
for (const proxy of proxies.values()) {
|
|
587
|
+
// oxlint-disable-next-line no-await-in-loop -- close proxy subprocesses deterministically before returning.
|
|
588
|
+
await closePortProxyBestEffort(proxy);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
async function closePortProxyBestEffort(proxy) {
|
|
592
|
+
try {
|
|
593
|
+
await proxy.close();
|
|
594
|
+
}
|
|
595
|
+
catch (error) {
|
|
596
|
+
log(`Port proxy close failed; continuing cleanup: ${errorMessage(error)}`);
|
|
597
|
+
}
|
|
425
598
|
}
|
|
426
599
|
function localCodexAuthFile() {
|
|
427
600
|
const codexHome = readEnvironmentVariable("CODEX_HOME");
|
|
@@ -506,7 +679,7 @@ async function installDatadogPup(provider, config) {
|
|
|
506
679
|
log(`Installing Datadog pup ${DATADOG_PUP_VERSION} inside the remote runner`);
|
|
507
680
|
await provider.runCommand({
|
|
508
681
|
config,
|
|
509
|
-
remoteArguments: ["bash", "-
|
|
682
|
+
remoteArguments: ["bash", "-c", datadogPupInstallCommand()],
|
|
510
683
|
});
|
|
511
684
|
}
|
|
512
685
|
async function installDatadogPupSkills(provider, config) {
|
|
@@ -603,7 +776,7 @@ async function addMcpServer(arguments_) {
|
|
|
603
776
|
}
|
|
604
777
|
async function authenticateMcpServers(arguments_) {
|
|
605
778
|
const { provider, config, options } = arguments_;
|
|
606
|
-
if (
|
|
779
|
+
if (!shouldAuthenticateMcpInteractively(options)) {
|
|
607
780
|
return;
|
|
608
781
|
}
|
|
609
782
|
writeOutput();
|
|
@@ -611,14 +784,19 @@ async function authenticateMcpServers(arguments_) {
|
|
|
611
784
|
for (const server of options.mcpServers) {
|
|
612
785
|
writeOutput(` - ${server.name}`);
|
|
613
786
|
}
|
|
787
|
+
writeOutput("Groundcrew will forward Claude localhost callback ports while this session is open.");
|
|
614
788
|
writeOutput("Inside Claude, run /mcp, select each listed server, choose Authenticate, then /exit.");
|
|
615
789
|
writeOutput("If the browser redirects to localhost and cannot connect, paste that callback URL into Claude when prompted.");
|
|
616
790
|
writeOutput();
|
|
617
|
-
await
|
|
791
|
+
await runClaudeTtyCommandWithOptionalCallbackProxy({
|
|
792
|
+
provider,
|
|
618
793
|
config,
|
|
619
794
|
remoteArguments: ["claude", "--permission-mode", "auto"],
|
|
620
795
|
});
|
|
621
796
|
}
|
|
797
|
+
function shouldAuthenticateMcpInteractively(options) {
|
|
798
|
+
return options.mcpServers.length > 0;
|
|
799
|
+
}
|
|
622
800
|
async function checkpoint(arguments_) {
|
|
623
801
|
const { provider, config, options } = arguments_;
|
|
624
802
|
if (!options.shouldCheckpoint) {
|
|
@@ -799,9 +977,13 @@ export async function setupRemoteRunner(options) {
|
|
|
799
977
|
if (options.shouldAuthenticateGithub) {
|
|
800
978
|
await authenticateGithub(provider, config);
|
|
801
979
|
}
|
|
802
|
-
|
|
980
|
+
const shouldUseInteractiveClaudeForMcpAuth = shouldAuthenticateMcpInteractively(options);
|
|
981
|
+
if (options.shouldAuthenticateClaude && !shouldUseInteractiveClaudeForMcpAuth) {
|
|
803
982
|
await authenticateClaude(provider, config);
|
|
804
983
|
}
|
|
984
|
+
else if (options.shouldAuthenticateClaude && shouldUseInteractiveClaudeForMcpAuth) {
|
|
985
|
+
log("Skipping separate Claude subscription auth; interactive MCP auth will handle Claude login if needed.");
|
|
986
|
+
}
|
|
805
987
|
if (options.shouldAuthenticateCodex) {
|
|
806
988
|
await authenticateCodex({
|
|
807
989
|
provider,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spriteRemoteRunnerProvider.d.ts","sourceRoot":"","sources":["../../src/lib/spriteRemoteRunnerProvider.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"spriteRemoteRunnerProvider.d.ts","sourceRoot":"","sources":["../../src/lib/spriteRemoteRunnerProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -143,22 +143,31 @@ function spriteRemoveWorktreeCommand(entry, force) {
|
|
|
143
143
|
if (entry.remoteRepoDir === undefined) {
|
|
144
144
|
throw new Error(`Remote worktree entry missing remoteRepoDir: ${entry.dir}`);
|
|
145
145
|
}
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
removeArguments.push("--force");
|
|
155
|
-
}
|
|
156
|
-
removeArguments.push(shellSingleQuote(entry.dir));
|
|
146
|
+
const forceFlag = force ? " --force" : "";
|
|
147
|
+
const missingRepositoryLines = force
|
|
148
|
+
? [
|
|
149
|
+
' echo "Remote repository missing; removing stale remote worktree directory: $worktree_dir" >&2',
|
|
150
|
+
' rm -rf -- "$worktree_dir"',
|
|
151
|
+
" exit 0",
|
|
152
|
+
]
|
|
153
|
+
: [' echo "Remote repository missing: $repo_dir" >&2', " exit 1"];
|
|
157
154
|
return [
|
|
158
155
|
"set -euo pipefail",
|
|
159
|
-
|
|
160
|
-
`
|
|
161
|
-
`
|
|
156
|
+
`repo_dir=${shellSingleQuote(entry.remoteRepoDir)}`,
|
|
157
|
+
`worktree_dir=${shellSingleQuote(entry.dir)}`,
|
|
158
|
+
`branch=${shellSingleQuote(entry.branchName)}`,
|
|
159
|
+
'if ! git -C "$repo_dir" rev-parse --git-dir >/dev/null 2>&1; then',
|
|
160
|
+
...missingRepositoryLines,
|
|
161
|
+
"fi",
|
|
162
|
+
'if [ ! -e "$worktree_dir" ]; then',
|
|
163
|
+
' echo "Remote worktree directory not found; pruning stale refs: $worktree_dir" >&2',
|
|
164
|
+
' git -C "$repo_dir" worktree prune',
|
|
165
|
+
' git -C "$repo_dir" branch -D "$branch" || true',
|
|
166
|
+
" exit 0",
|
|
167
|
+
"fi",
|
|
168
|
+
`git -C "$repo_dir" worktree remove${forceFlag} "$worktree_dir"`,
|
|
169
|
+
'git -C "$repo_dir" branch -D "$branch" || true',
|
|
170
|
+
'git -C "$repo_dir" worktree prune',
|
|
162
171
|
].join("\n");
|
|
163
172
|
}
|
|
164
173
|
async function spriteRunnerExists(config) {
|
|
@@ -352,7 +361,7 @@ export const spriteRemoteRunnerProvider = {
|
|
|
352
361
|
entry.remoteRunnerName,
|
|
353
362
|
"--",
|
|
354
363
|
"bash",
|
|
355
|
-
"-
|
|
364
|
+
"-c",
|
|
356
365
|
spriteRemoveWorktreeCommand(entry, force),
|
|
357
366
|
], longRunningCommandOptions(signal));
|
|
358
367
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worktrees.d.ts","sourceRoot":"","sources":["../../src/lib/worktrees.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAiBH,OAAO,EAA8B,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAMpG,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,iBAAiB,CAAC;AAIlE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE7C,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,YAAY,CAAC;IACnB,mCAAmC;IACnC,cAAc,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,CAAC;IACtD,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAaD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAW7E;
|
|
1
|
+
{"version":3,"file":"worktrees.d.ts","sourceRoot":"","sources":["../../src/lib/worktrees.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAiBH,OAAO,EAA8B,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAMpG,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,iBAAiB,CAAC;AAIlE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE7C,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,YAAY,CAAC;IACnB,mCAAmC;IACnC,cAAc,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,CAAC;IACtD,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAaD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAW7E;AAkfD,4FAA4F;AAC5F,iBAAS,IAAI,CAAC,MAAM,EAAE,cAAc,GAAG,aAAa,EAAE,CAErD;AAED,iBAAS,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE,CAE7E;AAED,iBAAS,YAAY,CACnB,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,aAAa,GAAG,SAAS,CAI3B;AAED,iBAAe,MAAM,CACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,YAAY,EAClB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,aAAa,CAAC,CAUxB;AAED,iBAAe,MAAM,CACnB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,aAAa,EACpB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAClD,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,MAAM,MAAM,YAAY,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,CAAC;IACrB,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,+DAA+D;IAC/D,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,sCAAsC;IACtC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,wDAAwD;IACxD,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,cAAc,EAAE,cAAc,CAAC;CAChC;AAKD,iBAAe,QAAQ,CACrB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,SAAS,aAAa,EAAE,EACjC,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAClD,OAAO,CAAC,cAAc,CAAC,CAkDzB;AAED,eAAO,MAAM,SAAS;;;;;;;CAOrB,CAAC"}
|
package/dist/lib/worktrees.js
CHANGED
|
@@ -308,6 +308,15 @@ function deleteRemoteEntry(config, entry) {
|
|
|
308
308
|
function remoteProviderFor(config, entry) {
|
|
309
309
|
return getRemoteRunnerProvider(entry?.remoteProvider ?? config.remote.provider);
|
|
310
310
|
}
|
|
311
|
+
async function remoteRunnerExistsAfterCleanupFailure(arguments_) {
|
|
312
|
+
try {
|
|
313
|
+
return await arguments_.provider.runnerExists(arguments_.remoteConfig);
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
log(`Remote runner availability check failed after cleanup error: ${errorMessage(error)}; keeping local state for retry.`);
|
|
317
|
+
throw arguments_.cleanupError;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
311
320
|
async function removeCreatedRemoteWorktreeBestEffort(arguments_) {
|
|
312
321
|
try {
|
|
313
322
|
log(`Rolling back remote worktree ${arguments_.entry.dir} after local state persistence failed...`);
|
|
@@ -369,17 +378,33 @@ const remoteWorktreeAdapter = {
|
|
|
369
378
|
},
|
|
370
379
|
async remove(config, entry, options) {
|
|
371
380
|
log(`Removing remote worktree ${entry.dir}${options.force ? " (--force)" : ""}...`);
|
|
381
|
+
const provider = remoteProviderFor(config, entry);
|
|
372
382
|
const remoteConfig = {
|
|
373
383
|
...config.remote,
|
|
374
384
|
provider: entry.remoteProvider ?? config.remote.provider,
|
|
375
385
|
runnerName: entry.remoteRunnerName ?? config.remote.runnerName,
|
|
376
386
|
};
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
387
|
+
try {
|
|
388
|
+
await provider.removeWorktree({
|
|
389
|
+
config: remoteConfig,
|
|
390
|
+
entry,
|
|
391
|
+
force: options.force,
|
|
392
|
+
...signalProperty(options.signal),
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
if (options.signal?.aborted === true || !options.force) {
|
|
397
|
+
throw error;
|
|
398
|
+
}
|
|
399
|
+
if (await remoteRunnerExistsAfterCleanupFailure({
|
|
400
|
+
provider,
|
|
401
|
+
remoteConfig,
|
|
402
|
+
cleanupError: error,
|
|
403
|
+
})) {
|
|
404
|
+
throw error;
|
|
405
|
+
}
|
|
406
|
+
log(`Remote runner ${remoteConfig.runnerName} not found; deleting stale local remote worktree record.`);
|
|
407
|
+
}
|
|
383
408
|
deleteRemoteEntry(config, entry);
|
|
384
409
|
},
|
|
385
410
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipboard-health/groundcrew",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.6",
|
|
4
4
|
"description": "Linear-driven orchestrator that launches AI coding agents in git worktrees, with workspace lifecycle, remote runners, and usage tracking.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -47,9 +47,10 @@
|
|
|
47
47
|
"scripts": {
|
|
48
48
|
"architecture:check": "depcruise src bin configExample.ts vitest.config.ts",
|
|
49
49
|
"build": "tsgo --build --force tsconfig.lib.json",
|
|
50
|
+
"build:dev": "tsgo --build tsconfig.lib.json",
|
|
50
51
|
"cpd": "jscpd .",
|
|
51
|
-
"crew": "node ./bin/run.js",
|
|
52
|
-
"crew:op": "op run --env-file \"${XDG_CONFIG_HOME:-$HOME/.config}/groundcrew/op.env\" -- node ./bin/run.js",
|
|
52
|
+
"crew": "node --run build:dev && node ./bin/run.js",
|
|
53
|
+
"crew:op": "node --run build:dev && op run --env-file \"${XDG_CONFIG_HOME:-$HOME/.config}/groundcrew/op.env\" -- node ./bin/run.js",
|
|
53
54
|
"format": "oxfmt",
|
|
54
55
|
"format:check": "oxfmt --check",
|
|
55
56
|
"hook:pre-commit": "lint-staged",
|