@tablation/crew 0.124.0 → 0.125.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.
- package/dist/cli.js +230 -119
- package/dist/sandbox-intel-bridge.cjs +25189 -0
- package/package.json +1 -1
- package/prompts/default/skills/sandbox-setup.md +3 -0
package/dist/cli.js
CHANGED
|
@@ -234,14 +234,8 @@ function ensureLspPlugin(stateDir, flavor, mainCheckout) {
|
|
|
234
234
|
const def = LSP_SERVERS[flavor];
|
|
235
235
|
const dir = join2(stateDir, "intel", "plugins", flavor);
|
|
236
236
|
const command2 = existsSync3(lspLocalBin(mainCheckout, flavor)) ? `\${CLAUDE_PROJECT_DIR}/node_modules/.bin/${def.bin}` : def.bin;
|
|
237
|
-
const
|
|
238
|
-
[join2(".claude-plugin", "plugin.json"), { name: `crew-${flavor}`, version: "1.0.0", description: `Language server for the ${flavor} intel flavor, generated by crew` }],
|
|
239
|
-
[".lsp.json", { [def.bin]: { command: command2, args: [...def.args], extensionToLanguage: def.extensionToLanguage } }]
|
|
240
|
-
];
|
|
241
|
-
for (const [rel, doc] of files) {
|
|
237
|
+
for (const [rel, text2] of lspPluginFiles(flavor, command2)) {
|
|
242
238
|
const path = join2(dir, rel);
|
|
243
|
-
const text2 = `${JSON.stringify(doc, null, 2)}
|
|
244
|
-
`;
|
|
245
239
|
let old;
|
|
246
240
|
try {
|
|
247
241
|
old = readFileSync2(path, "utf8");
|
|
@@ -253,6 +247,21 @@ function ensureLspPlugin(stateDir, flavor, mainCheckout) {
|
|
|
253
247
|
}
|
|
254
248
|
return dir;
|
|
255
249
|
}
|
|
250
|
+
function lspPluginFiles(flavor, command2) {
|
|
251
|
+
const def = LSP_SERVERS[flavor];
|
|
252
|
+
const files = [
|
|
253
|
+
[join2(".claude-plugin", "plugin.json"), { name: `crew-${flavor}`, version: "1.0.0", description: `Language server for the ${flavor} intel flavor, generated by crew` }],
|
|
254
|
+
[".lsp.json", { [def.bin]: { command: command2, args: [...def.args], extensionToLanguage: def.extensionToLanguage } }]
|
|
255
|
+
];
|
|
256
|
+
return files.map(([rel, doc]) => [rel, `${JSON.stringify(doc, null, 2)}
|
|
257
|
+
`]);
|
|
258
|
+
}
|
|
259
|
+
var SANDBOX_INTEL_DIR = "/sandbox/.crew/intel";
|
|
260
|
+
function sandboxLspPlugin(flavor) {
|
|
261
|
+
const dir = `${SANDBOX_INTEL_DIR}/plugins/${flavor}`;
|
|
262
|
+
const command2 = `\${CLAUDE_PROJECT_DIR}/node_modules/.bin/${LSP_SERVERS[flavor].bin}`;
|
|
263
|
+
return { dir, files: lspPluginFiles(flavor, command2).map(([rel, content]) => ({ path: `${dir}/${rel}`, content })) };
|
|
264
|
+
}
|
|
256
265
|
function lspToolsParagraph(harness, omlx = false) {
|
|
257
266
|
if (harness !== "claude") return "No symbol service is available for this harness: this repository's `intel` flavor serves only the Claude Code harness's own LSP tool.";
|
|
258
267
|
if (omlx) return "The harness's LSP tool is off for this launcher, so no symbol service is available for this run.";
|
|
@@ -583,7 +592,7 @@ function startSerena(d, s) {
|
|
|
583
592
|
const cache = join5(p.project, "cache");
|
|
584
593
|
if (!d.exists(cache) && d.exists(mainCache)) d.copyDir(mainCache, cache);
|
|
585
594
|
try {
|
|
586
|
-
ensureMainIndex(d, s);
|
|
595
|
+
if (!s.noMainIndex) ensureMainIndex(d, s);
|
|
587
596
|
} catch {
|
|
588
597
|
}
|
|
589
598
|
const port = intelPortFor(s.ticket);
|
|
@@ -6295,8 +6304,9 @@ function attentionProblems(items, already) {
|
|
|
6295
6304
|
import { createHash as createHash6 } from "crypto";
|
|
6296
6305
|
import { spawn as spawn5 } from "child_process";
|
|
6297
6306
|
import { execFileSync as execFileSync8 } from "child_process";
|
|
6298
|
-
import { mkdirSync as mkdirSync19, readFileSync as readFileSync27, renameSync as renameSync3, writeFileSync as writeFileSync20, rmSync as rmSync15 } from "fs";
|
|
6307
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync27, renameSync as renameSync3, writeFileSync as writeFileSync20, rmSync as rmSync15 } from "fs";
|
|
6299
6308
|
import { dirname as dirname14, join as join32 } from "path";
|
|
6309
|
+
import { fileURLToPath } from "url";
|
|
6300
6310
|
import { tmpdir as tmpdir3 } from "os";
|
|
6301
6311
|
|
|
6302
6312
|
// src/passenger-containers.ts
|
|
@@ -6850,6 +6860,12 @@ exit 125
|
|
|
6850
6860
|
...CONTAINER_ENGINES.map((e) => `printf '%b' ${shSingle(body)} > ${shSingle(`${dir}/${e}`)} && chmod 755 ${shSingle(`${dir}/${e}`)}`)
|
|
6851
6861
|
].join(" && ");
|
|
6852
6862
|
}
|
|
6863
|
+
function writeFilesScript(files) {
|
|
6864
|
+
return files.map((f) => {
|
|
6865
|
+
const dir = f.path.slice(0, f.path.lastIndexOf("/")) || "/";
|
|
6866
|
+
return `mkdir -p ${shSingle(dir)} && printf %s ${shSingle(Buffer.from(f.content, "utf8").toString("base64"))} | base64 -d > ${shSingle(f.path)}`;
|
|
6867
|
+
}).join(" && ");
|
|
6868
|
+
}
|
|
6853
6869
|
function shSingle(s) {
|
|
6854
6870
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
6855
6871
|
}
|
|
@@ -6876,6 +6892,17 @@ function sandboxCheckoutSection(workDir, heading) {
|
|
|
6876
6892
|
"skill for setup, ports and the hand-off push."
|
|
6877
6893
|
];
|
|
6878
6894
|
}
|
|
6895
|
+
function sandboxCodeToolsSection(intel) {
|
|
6896
|
+
if (lspFlavor(intel)) return ["", `**Code tools.** On the Claude Code harness: ${lspToolsParagraph("claude").replace(/^The harness's/, "the harness's")}`];
|
|
6897
|
+
if (!wantsCodeBridge(intel)) return [];
|
|
6898
|
+
return [
|
|
6899
|
+
"",
|
|
6900
|
+
`**Code tools.** A \`${INTEL_SERVER_NAME}\` MCP server serves this repository's symbols from its language server. It offers at least: ${INTEL_MINIMUM_TOOLS.join("; ")}.`,
|
|
6901
|
+
"It lists only `code_status` until the symbol service is up. The service starts inside this sandbox",
|
|
6902
|
+
"the first time the session asks the server for tools, and it expects `setup` to have run, so run `setup`",
|
|
6903
|
+
"before your first lookup. `code_status` says whether it is starting, attached, or failed to start (and why)."
|
|
6904
|
+
];
|
|
6905
|
+
}
|
|
6879
6906
|
function sandboxEnv(ship) {
|
|
6880
6907
|
return ship.runner === "sandbox" ? { sandbox: { workDir: ship.sandbox?.workDir ?? DEFAULT_SANDBOX_WORKDIR } } : {};
|
|
6881
6908
|
}
|
|
@@ -7175,7 +7202,7 @@ function renderEnvironment(i) {
|
|
|
7175
7202
|
);
|
|
7176
7203
|
}
|
|
7177
7204
|
if (only && i.sandbox) {
|
|
7178
|
-
lines2.push(...sandboxCheckoutSection(i.sandbox.workDir, "## Your checkout"));
|
|
7205
|
+
lines2.push(...sandboxCheckoutSection(i.sandbox.workDir, "## Your checkout"), ...sandboxCodeToolsSection(only.config.hooks.intel));
|
|
7179
7206
|
} else if (only) {
|
|
7180
7207
|
lines2.push(...repoSection(only, key, "## Your worktree"));
|
|
7181
7208
|
} else {
|
|
@@ -7191,7 +7218,7 @@ function renderEnvironment(i) {
|
|
|
7191
7218
|
);
|
|
7192
7219
|
if (i.sandbox) lines2.push(...sandboxCheckoutSection(i.sandbox.workDir, "### Your checkout"));
|
|
7193
7220
|
for (const r of repos) {
|
|
7194
|
-
lines2.push(...i.sandbox ? ["", `### ${r.name}
|
|
7221
|
+
lines2.push(...i.sandbox ? ["", `### ${r.name}`, ...sandboxCodeToolsSection(r.config.hooks.intel)] : repoSection(r, key, `### ${r.name} \u2014 \`${r.dir}\``));
|
|
7195
7222
|
lines2.push("", `Hooks for ${r.name} \u2014 the only commands you should run here:`, "", hookTable(r.config));
|
|
7196
7223
|
lines2.push(...mapSection(r, `#### Repository map \u2014 ${r.name}`));
|
|
7197
7224
|
}
|
|
@@ -7219,6 +7246,8 @@ import {
|
|
|
7219
7246
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
7220
7247
|
var STATUS_TOOL = "code_status";
|
|
7221
7248
|
var STATUS_WAITING = "The symbol service is not available yet: the worktree has not been cut. Run `crew worktree` for the ticket; the symbol tools appear in this server afterwards.";
|
|
7249
|
+
var STATUS_STARTING = "The symbol service is starting inside this sandbox. Call `code_status` again in a few seconds; the symbol tools replace it once the service answers.";
|
|
7250
|
+
var statusFailed = (reason) => `The symbol service did not start (${reason}). Run the repository's \`setup\` hook first if you have not; this call has started a retry, so call \`code_status\` again in a few seconds; until it starts use text search.`;
|
|
7222
7251
|
var STATUS_LOST = "The symbol service stopped answering, so its tools are unavailable. They return if the service comes back; until then use text search.";
|
|
7223
7252
|
function intelDir(stateDir, route2, ticket) {
|
|
7224
7253
|
const safe = (s) => s.replace(/[^A-Za-z0-9._-]+/g, "_");
|
|
@@ -7258,6 +7287,8 @@ function createBridge(o) {
|
|
|
7258
7287
|
let lost = false;
|
|
7259
7288
|
let connecting = false;
|
|
7260
7289
|
let timer;
|
|
7290
|
+
let starting;
|
|
7291
|
+
let startError;
|
|
7261
7292
|
const notify2 = () => {
|
|
7262
7293
|
void server.sendToolListChanged().catch(() => {
|
|
7263
7294
|
});
|
|
@@ -7270,8 +7301,40 @@ function createBridge(o) {
|
|
|
7270
7301
|
});
|
|
7271
7302
|
notify2();
|
|
7272
7303
|
};
|
|
7273
|
-
const
|
|
7304
|
+
const statusText = () => lost ? STATUS_LOST : starting ? STATUS_STARTING : startError !== void 0 ? statusFailed(startError) : STATUS_WAITING;
|
|
7305
|
+
const status2 = () => ({ content: [{ type: "text", text: statusText() }] });
|
|
7306
|
+
const ensureStarted = () => {
|
|
7307
|
+
if (!o.start || upstream || starting) return;
|
|
7308
|
+
startError = void 0;
|
|
7309
|
+
starting = (async () => {
|
|
7310
|
+
try {
|
|
7311
|
+
const r = await o.start();
|
|
7312
|
+
if ("error" in r) {
|
|
7313
|
+
startError = r.error;
|
|
7314
|
+
return;
|
|
7315
|
+
}
|
|
7316
|
+
writeAttachFile(o.attachFile, r.url);
|
|
7317
|
+
await tryAttach();
|
|
7318
|
+
if (!upstream) startError = `${r.url} did not accept a connection`;
|
|
7319
|
+
} catch (e) {
|
|
7320
|
+
startError = e.message.replace(/\s+/g, " ").slice(0, 200);
|
|
7321
|
+
} finally {
|
|
7322
|
+
starting = void 0;
|
|
7323
|
+
notify2();
|
|
7324
|
+
}
|
|
7325
|
+
})();
|
|
7326
|
+
};
|
|
7327
|
+
const waitForStart = async () => {
|
|
7328
|
+
const running = starting;
|
|
7329
|
+
if (!running) return;
|
|
7330
|
+
let t;
|
|
7331
|
+
await Promise.race([running, new Promise((r) => {
|
|
7332
|
+
t = setTimeout(r, o.startWaitMs ?? 12e4);
|
|
7333
|
+
})]);
|
|
7334
|
+
if (t) clearTimeout(t);
|
|
7335
|
+
};
|
|
7274
7336
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
7337
|
+
ensureStarted();
|
|
7275
7338
|
if (!upstream) return { tools: [statusTool] };
|
|
7276
7339
|
try {
|
|
7277
7340
|
return await upstream.listTools();
|
|
@@ -7281,6 +7344,11 @@ function createBridge(o) {
|
|
|
7281
7344
|
}
|
|
7282
7345
|
});
|
|
7283
7346
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
7347
|
+
const failedNow = req.params.name === STATUS_TOOL && !upstream && !starting && startError !== void 0;
|
|
7348
|
+
const failedText = failedNow ? statusText() : void 0;
|
|
7349
|
+
ensureStarted();
|
|
7350
|
+
if (failedText !== void 0) return { content: [{ type: "text", text: failedText }] };
|
|
7351
|
+
if (req.params.name !== STATUS_TOOL) await waitForStart();
|
|
7284
7352
|
if (!upstream) return status2();
|
|
7285
7353
|
try {
|
|
7286
7354
|
return await upstream.callTool({ name: req.params.name, arguments: req.params.arguments });
|
|
@@ -10733,6 +10801,9 @@ var SandboxRunner = class {
|
|
|
10733
10801
|
lockfile: cfg.depsLockfile ?? DEFAULT_CACHE_SETTINGS.depsLockfile
|
|
10734
10802
|
})} && ${containerEngineStubScript()}`;
|
|
10735
10803
|
await this.os(["sandbox", "exec", "-n", name, "--no-login-shell", "--", "sh", "-c", clone]);
|
|
10804
|
+
if (plan.sandboxFiles?.length) {
|
|
10805
|
+
await this.os(["sandbox", "exec", "-n", name, "--no-login-shell", "--", "sh", "-c", writeFilesScript(plan.sandboxFiles)]);
|
|
10806
|
+
}
|
|
10736
10807
|
const harness = cfg.agentBin ?? basename8(plan.bin);
|
|
10737
10808
|
const viaUpload = Buffer.byteLength(plan.prompt) > SANDBOX_STDIN_LIMIT;
|
|
10738
10809
|
if (viaUpload) {
|
|
@@ -10882,6 +10953,15 @@ function lspPluginDirsFor(route2, repo, stateDir) {
|
|
|
10882
10953
|
return void 0;
|
|
10883
10954
|
}
|
|
10884
10955
|
}
|
|
10956
|
+
function sandboxLspPluginFor(route2, repo) {
|
|
10957
|
+
try {
|
|
10958
|
+
const dir = reposOf(route2).find((r) => r.name === repo)?.dir ?? route2.dir;
|
|
10959
|
+
const flavor = lspFlavor(loadRepoConfig(dir)?.hooks.intel);
|
|
10960
|
+
return flavor ? sandboxLspPlugin(flavor) : void 0;
|
|
10961
|
+
} catch {
|
|
10962
|
+
return void 0;
|
|
10963
|
+
}
|
|
10964
|
+
}
|
|
10885
10965
|
var AgentSpawnError = class extends AgentError {
|
|
10886
10966
|
};
|
|
10887
10967
|
var WorkspaceAuthRefused = class extends AgentSpawnError {
|
|
@@ -10917,7 +10997,7 @@ function streamShowsAllowanceRefusal(streamPath) {
|
|
|
10917
10997
|
}
|
|
10918
10998
|
var digestPathFor = (stateDir, routeLabel, role, runId) => join29(stateDir, `digest-${routeLabel}-${role}${runId ? `-${runId}` : ""}.md`);
|
|
10919
10999
|
function codeBridgeFor(o, harness) {
|
|
10920
|
-
if (
|
|
11000
|
+
if (!o.ticket) return void 0;
|
|
10921
11001
|
const number = /(\d+)$/.exec(o.ticket)?.[1];
|
|
10922
11002
|
if (!number) return void 0;
|
|
10923
11003
|
let intel;
|
|
@@ -10928,14 +11008,33 @@ function codeBridgeFor(o, harness) {
|
|
|
10928
11008
|
} catch {
|
|
10929
11009
|
}
|
|
10930
11010
|
if (!wantsCodeBridge(intel)) return void 0;
|
|
10931
|
-
const
|
|
10932
|
-
const
|
|
11011
|
+
const sandbox = o.ship.runner === "sandbox";
|
|
11012
|
+
const args = sandbox ? [
|
|
11013
|
+
SANDBOX_BRIDGE_PATH,
|
|
11014
|
+
"--attach-file",
|
|
11015
|
+
`${SANDBOX_INTEL_DIR}/url`,
|
|
11016
|
+
"--start",
|
|
11017
|
+
intel.trim(),
|
|
11018
|
+
"--clone",
|
|
11019
|
+
o.ship.sandbox?.workDir ?? DEFAULT_SANDBOX_WORKDIR,
|
|
11020
|
+
"--state-dir",
|
|
11021
|
+
dirname13(SANDBOX_INTEL_DIR),
|
|
11022
|
+
"--ticket",
|
|
11023
|
+
number
|
|
11024
|
+
] : [...process.execArgv, process.argv[1] ?? "crew", "intel", "bridge", o.route.route, number, "--state-dir", o.stateDir];
|
|
11025
|
+
const bridge = { command: sandbox ? "node" : process.execPath, args };
|
|
10933
11026
|
if (harness === "gemini") {
|
|
10934
|
-
const
|
|
10935
|
-
|
|
10936
|
-
|
|
10937
|
-
|
|
10938
|
-
|
|
11027
|
+
const text2 = `${JSON.stringify({ mcpServers: { [INTEL_SERVER_NAME]: { command: bridge.command, args } } }, null, 2)}
|
|
11028
|
+
`;
|
|
11029
|
+
if (sandbox) {
|
|
11030
|
+
bridge.settingsFile = `${SANDBOX_INTEL_DIR}/gemini-settings.json`;
|
|
11031
|
+
bridge.files = [{ path: bridge.settingsFile, content: text2 }];
|
|
11032
|
+
} else {
|
|
11033
|
+
const file = join29(intelDir(o.stateDir, o.route.route, number), "gemini-settings.json");
|
|
11034
|
+
mkdirSync18(dirname13(file), { recursive: true });
|
|
11035
|
+
writeFileSync19(file, text2);
|
|
11036
|
+
bridge.settingsFile = file;
|
|
11037
|
+
}
|
|
10939
11038
|
}
|
|
10940
11039
|
return bridge;
|
|
10941
11040
|
}
|
|
@@ -10996,11 +11095,13 @@ function planAgentRun(o) {
|
|
|
10996
11095
|
}
|
|
10997
11096
|
const adapter = adapterFor(toolchain.harness);
|
|
10998
11097
|
const code = codeBridgeFor(o, adapter.harness);
|
|
11098
|
+
const sandboxLsp = o.ship.runner === "sandbox" && adapter.harness === "claude" ? sandboxLspPluginFor(o.route, o.repo) : void 0;
|
|
11099
|
+
const sandboxFiles = [...code?.files ?? [], ...sandboxLsp?.files ?? []];
|
|
10999
11100
|
const invocation = adapter.invocation(toolchain, {
|
|
11000
11101
|
code,
|
|
11001
11102
|
role: o.role,
|
|
11002
11103
|
model,
|
|
11003
|
-
pluginDirs: adapter.harness
|
|
11104
|
+
pluginDirs: adapter.harness !== "claude" ? void 0 : sandboxLsp ? [sandboxLsp.dir] : lspPluginDirsFor(o.route, o.repo, o.stateDir),
|
|
11004
11105
|
// Skipped when there's no key to send: a route `crew connect` hasn't
|
|
11005
11106
|
// resolved yet has nothing to authenticate the MCP connection with either.
|
|
11006
11107
|
mcp: o.apiKey ? { url: `${o.route.baseUrl}/api/mcp`, keyVar: API_KEY_VAR } : void 0
|
|
@@ -11047,7 +11148,8 @@ function planAgentRun(o) {
|
|
|
11047
11148
|
...o.ship.runner === "sandbox" ? {
|
|
11048
11149
|
runner: "sandbox",
|
|
11049
11150
|
...o.ship.sandbox ? { sandboxConfig: o.ship.sandbox } : {},
|
|
11050
|
-
sandboxPolicy: sandboxPolicyInputFor(o.route, toolchain, { harnessBin: invocation.bin, repo: o.repo, sandbox: o.ship.sandbox })
|
|
11151
|
+
sandboxPolicy: sandboxPolicyInputFor(o.route, toolchain, { harnessBin: invocation.bin, repo: o.repo, sandbox: o.ship.sandbox }),
|
|
11152
|
+
...sandboxFiles.length ? { sandboxFiles } : {}
|
|
11051
11153
|
} : {},
|
|
11052
11154
|
resolution: { toolchain: seat.toolchainWhy, model: seat.modelWhy },
|
|
11053
11155
|
warnings: [...seat.warnings ?? [], ...expanded.warnings],
|
|
@@ -12686,6 +12788,14 @@ var SANDBOX_CLAUDE_SHA256 = {
|
|
|
12686
12788
|
};
|
|
12687
12789
|
var SANDBOX_UID = 1e3;
|
|
12688
12790
|
var CREW_PREFIX = "/opt/crew";
|
|
12791
|
+
var SANDBOX_BRIDGE_PATH = `${CREW_PREFIX}/bin/intel-bridge`;
|
|
12792
|
+
function readBridgeBundle() {
|
|
12793
|
+
const here = dirname14(fileURLToPath(import.meta.url));
|
|
12794
|
+
for (const p of [join32(here, "sandbox-intel-bridge.cjs"), join32(here, "..", "dist", "sandbox-intel-bridge.cjs")]) {
|
|
12795
|
+
if (existsSync30(p)) return readFileSync27(p, "utf8");
|
|
12796
|
+
}
|
|
12797
|
+
return void 0;
|
|
12798
|
+
}
|
|
12689
12799
|
var SANDBOX_UV_VERSION = "0.13.6";
|
|
12690
12800
|
var SANDBOX_SERENA_VERSION = "2.1.0";
|
|
12691
12801
|
var SERENA_IMAGE_HOME = `${CREW_PREFIX}/serena`;
|
|
@@ -12749,8 +12859,8 @@ function baseLayer() {
|
|
|
12749
12859
|
].join("\n");
|
|
12750
12860
|
return { name: "base", tag: `crew-sandbox-base:${hash({ SANDBOX_BASE_IMAGE, SANDBOX_OPENSHELL_VERSION })}`, dockerfile, files: {} };
|
|
12751
12861
|
}
|
|
12752
|
-
function crewLayer(base, intel) {
|
|
12753
|
-
const files = { "credential-helper": CREDENTIAL_HELPER, "mcp.json": MCP_CONFIG };
|
|
12862
|
+
function crewLayer(base, intel, bridge) {
|
|
12863
|
+
const files = { "credential-helper": CREDENTIAL_HELPER, "mcp.json": MCP_CONFIG, ...bridge ? { "intel-bridge": bridge } : {} };
|
|
12754
12864
|
const env = Object.entries(CREW_ENV).map(([k, v]) => `${k}=${v}`).join(" ");
|
|
12755
12865
|
const dockerfile = [
|
|
12756
12866
|
`FROM ${base.tag}`,
|
|
@@ -12758,6 +12868,7 @@ function crewLayer(base, intel) {
|
|
|
12758
12868
|
run2(`apt-get update && apt-get install -y --no-install-recommends ca-certificates curl git${intel ? " python3 python3-venv nodejs" : ""} && rm -rf /var/lib/apt/lists/*`),
|
|
12759
12869
|
`COPY credential-helper ${CREW_PREFIX}/bin/credential-helper`,
|
|
12760
12870
|
`COPY mcp.json ${CREW_PREFIX}/mcp/mcp.json`,
|
|
12871
|
+
...bridge ? [`COPY intel-bridge ${SANDBOX_BRIDGE_PATH}`] : [],
|
|
12761
12872
|
run2(claudeInstallCommand()),
|
|
12762
12873
|
...intel ? [run2(`mkdir -p ${CREW_PREFIX}/cache && ${serenaInstallCommand()}`)] : [],
|
|
12763
12874
|
run2(`chmod 0755 ${CREW_PREFIX}/bin/credential-helper && git config --system credential.helper ${CREW_PREFIX}/bin/credential-helper`),
|
|
@@ -12822,9 +12933,9 @@ function repoLayer(repo, decl, crew, warm) {
|
|
|
12822
12933
|
const dockerfile = lines2.join("\n");
|
|
12823
12934
|
return { name: "repo", tag: `crew-sandbox/${slug(repo)}:${hash({ crew: crew.tag, dockerfile, files })}`, dockerfile, files };
|
|
12824
12935
|
}
|
|
12825
|
-
function planSandbox(repo, decl, warm = {}) {
|
|
12936
|
+
function planSandbox(repo, decl, warm = {}, bridge) {
|
|
12826
12937
|
const base = baseLayer();
|
|
12827
|
-
const crew = crewLayer(base, decl.intel);
|
|
12938
|
+
const crew = crewLayer(base, decl.intel, bridge);
|
|
12828
12939
|
const layer = repoLayer(repo, decl, crew, warm);
|
|
12829
12940
|
return { repo, tag: layer.tag, claude: SANDBOX_CLAUDE_VERSION, layers: [base, crew, layer] };
|
|
12830
12941
|
}
|
|
@@ -12934,7 +13045,7 @@ function sandboxFailedItem(repo, tag, layer, logPath, nowIso = (/* @__PURE__ */
|
|
|
12934
13045
|
async function sandboxBuildTick(repos, deps, force = false) {
|
|
12935
13046
|
const nowMs = (deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime();
|
|
12936
13047
|
for (const r of repos) {
|
|
12937
|
-
const plan = planSandbox(r.name, r.decl, r.warm);
|
|
13048
|
+
const plan = planSandbox(r.name, r.decl, r.warm, r.bridge);
|
|
12938
13049
|
const state = readSandboxState(deps.stateDir)[r.name];
|
|
12939
13050
|
const upToDate = state?.tags[0]?.tag === plan.tag && deps.hasImage(plan.tag);
|
|
12940
13051
|
if (upToDate) {
|
|
@@ -13093,7 +13204,7 @@ function managedRuntimeWarnings(kind, ship) {
|
|
|
13093
13204
|
|
|
13094
13205
|
// src/selftest.ts
|
|
13095
13206
|
import { spawn as spawn6 } from "child_process";
|
|
13096
|
-
import { existsSync as
|
|
13207
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, readFileSync as readFileSync28, rmSync as rmSync16, writeFileSync as writeFileSync21 } from "fs";
|
|
13097
13208
|
import { tmpdir as tmpdir4 } from "os";
|
|
13098
13209
|
import { join as join33 } from "path";
|
|
13099
13210
|
var SELFTEST_TIMEOUT_MS = 12e4;
|
|
@@ -13293,7 +13404,7 @@ var fileFor2 = (stateDir) => join33(stateDir, "selftest.json");
|
|
|
13293
13404
|
function readSelftest(stateDir) {
|
|
13294
13405
|
try {
|
|
13295
13406
|
const p = fileFor2(stateDir);
|
|
13296
|
-
return
|
|
13407
|
+
return existsSync31(p) ? JSON.parse(readFileSync28(p, "utf8")) : {};
|
|
13297
13408
|
} catch {
|
|
13298
13409
|
return {};
|
|
13299
13410
|
}
|
|
@@ -13315,7 +13426,7 @@ function doctorSelftestLines(stored, toolchains) {
|
|
|
13315
13426
|
|
|
13316
13427
|
// src/cli.ts
|
|
13317
13428
|
import { resolve as resolve7, dirname as dirname33, join as join55, basename as basename16 } from "path";
|
|
13318
|
-
import { fileURLToPath } from "url";
|
|
13429
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
13319
13430
|
import { hostname as hostname4, homedir as homedir11 } from "os";
|
|
13320
13431
|
import { spawnSync as spawnSync11 } from "child_process";
|
|
13321
13432
|
import { parse as parseYaml2 } from "yaml";
|
|
@@ -13619,7 +13730,7 @@ async function signOutSessions(o) {
|
|
|
13619
13730
|
}
|
|
13620
13731
|
|
|
13621
13732
|
// src/reset.ts
|
|
13622
|
-
import { existsSync as
|
|
13733
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync22, readFileSync as readFileSync30, readdirSync as readdirSync12, rmSync as rmSync18, statSync as statSync10, writeFileSync as writeFileSync23 } from "fs";
|
|
13623
13734
|
import { dirname as dirname16, join as join35 } from "path";
|
|
13624
13735
|
import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
|
|
13625
13736
|
function clonedCheckoutsFile(stateDir) {
|
|
@@ -13692,7 +13803,7 @@ async function runReset(o, fx) {
|
|
|
13692
13803
|
}
|
|
13693
13804
|
if (o.beforeState) steps.push(...await o.beforeState());
|
|
13694
13805
|
steps.push(...o.wipeState ? wipeStateDir(o.stateDir, o.dryRun) : removeState(o.stateDir, o.dryRun));
|
|
13695
|
-
if (
|
|
13806
|
+
if (existsSync32(o.configFile)) {
|
|
13696
13807
|
if (!o.dryRun) rmSync18(o.configFile, { force: true });
|
|
13697
13808
|
steps.push({ step: "config", target: o.configFile, action: mark(o.dryRun) });
|
|
13698
13809
|
} else {
|
|
@@ -13701,7 +13812,7 @@ async function runReset(o, fx) {
|
|
|
13701
13812
|
return steps;
|
|
13702
13813
|
}
|
|
13703
13814
|
function removeCheckout(dir, dryRun, fx) {
|
|
13704
|
-
if (!
|
|
13815
|
+
if (!existsSync32(dir)) return { step: "checkout", target: dir, action: "absent", detail: "already gone" };
|
|
13705
13816
|
const { dirty, linkedWorktrees } = fx.inspectCheckout(dir);
|
|
13706
13817
|
if (dirty.length) {
|
|
13707
13818
|
return { step: "checkout", target: dir, action: "kept", detail: `has uncommitted changes (${dirty.length} path${dirty.length === 1 ? "" : "s"}: ${dirty.slice(0, 5).join(", ")}${dirty.length > 5 ? ", \u2026" : ""})` };
|
|
@@ -13722,7 +13833,7 @@ function removeState(stateDir, dryRun) {
|
|
|
13722
13833
|
} catch {
|
|
13723
13834
|
}
|
|
13724
13835
|
for (const t of targets) {
|
|
13725
|
-
if (!
|
|
13836
|
+
if (!existsSync32(t)) continue;
|
|
13726
13837
|
if (!dryRun) rmSync18(t, { recursive: true, force: true });
|
|
13727
13838
|
out.push({ step: "state", target: t, action: mark(dryRun) });
|
|
13728
13839
|
}
|
|
@@ -14118,13 +14229,13 @@ function hostArchitecture(arch = process.arch) {
|
|
|
14118
14229
|
|
|
14119
14230
|
// src/pool-member-host.ts
|
|
14120
14231
|
import "child_process";
|
|
14121
|
-
import { existsSync as
|
|
14232
|
+
import { existsSync as existsSync36 } from "fs";
|
|
14122
14233
|
import { hostname as hostname3 } from "os";
|
|
14123
14234
|
|
|
14124
14235
|
// src/machine-id.ts
|
|
14125
14236
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
14126
14237
|
import { createHash as createHash7 } from "crypto";
|
|
14127
|
-
import { existsSync as
|
|
14238
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync32, writeFileSync as writeFileSync25 } from "fs";
|
|
14128
14239
|
import { dirname as dirname18, join as join37 } from "path";
|
|
14129
14240
|
function machineIdPathFor(stateDir) {
|
|
14130
14241
|
return join37(stateDir, "machine-id");
|
|
@@ -14140,7 +14251,7 @@ function readRawMachineId(platform2 = process.platform) {
|
|
|
14140
14251
|
return /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(out)?.[1];
|
|
14141
14252
|
}
|
|
14142
14253
|
for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
|
|
14143
|
-
if (
|
|
14254
|
+
if (existsSync33(p)) {
|
|
14144
14255
|
const id = readFileSync32(p, "utf8").trim();
|
|
14145
14256
|
if (id) return id;
|
|
14146
14257
|
}
|
|
@@ -14152,7 +14263,7 @@ function readRawMachineId(platform2 = process.platform) {
|
|
|
14152
14263
|
function ensureMachineId(stateDir, opts = {}) {
|
|
14153
14264
|
if (opts.override?.trim()) return sha256(opts.override.trim());
|
|
14154
14265
|
const path = machineIdPathFor(stateDir);
|
|
14155
|
-
if (
|
|
14266
|
+
if (existsSync33(path)) {
|
|
14156
14267
|
const cached = readFileSync32(path, "utf8").trim();
|
|
14157
14268
|
if (/^[0-9a-f]{64}$/.test(cached)) return cached;
|
|
14158
14269
|
}
|
|
@@ -14173,7 +14284,7 @@ function matchShipRows(ships, shipName2, machineId) {
|
|
|
14173
14284
|
|
|
14174
14285
|
// src/provision.ts
|
|
14175
14286
|
import { spawnSync as spawnSync8 } from "child_process";
|
|
14176
|
-
import { existsSync as
|
|
14287
|
+
import { existsSync as existsSync34, readFileSync as readFileSync33, writeFileSync as writeFileSync26 } from "fs";
|
|
14177
14288
|
import { hostname as hostname2 } from "os";
|
|
14178
14289
|
import { parseDocument as parseDocument3, isMap as isMap2, isScalar } from "yaml";
|
|
14179
14290
|
|
|
@@ -14767,7 +14878,7 @@ function realProvisionDeps(crewHome) {
|
|
|
14767
14878
|
};
|
|
14768
14879
|
const configFile = () => {
|
|
14769
14880
|
const f = findConfigFile(crewHome);
|
|
14770
|
-
return
|
|
14881
|
+
return existsSync34(f) ? f : null;
|
|
14771
14882
|
};
|
|
14772
14883
|
return {
|
|
14773
14884
|
crew: spawnCrew,
|
|
@@ -14801,14 +14912,14 @@ function realProvisionDeps(crewHome) {
|
|
|
14801
14912
|
continue;
|
|
14802
14913
|
}
|
|
14803
14914
|
for (const p of plan.unitPaths) {
|
|
14804
|
-
if (!
|
|
14915
|
+
if (!existsSync34(p) || readFileSync33(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
|
|
14805
14916
|
}
|
|
14806
14917
|
}
|
|
14807
14918
|
const label = labelFor(crewHome, "run");
|
|
14808
14919
|
let running = false;
|
|
14809
14920
|
let lingering = true;
|
|
14810
14921
|
if (mechanism === "systemd") {
|
|
14811
|
-
const system = ctx.root ||
|
|
14922
|
+
const system = ctx.root || existsSync34(systemUnitPathFor(label));
|
|
14812
14923
|
const ctl = system ? ["systemctl"] : ["systemctl", "--user"];
|
|
14813
14924
|
running = spawnSync8(ctl[0], [...ctl.slice(1), "is-active", "--quiet", `${label}.service`]).status === 0;
|
|
14814
14925
|
if (!system) {
|
|
@@ -14853,7 +14964,7 @@ host=${host}
|
|
|
14853
14964
|
|
|
14854
14965
|
// src/tunnel.ts
|
|
14855
14966
|
import { spawn as spawn7 } from "child_process";
|
|
14856
|
-
import { existsSync as
|
|
14967
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync25, readFileSync as readFileSync34, writeFileSync as writeFileSync27, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
|
|
14857
14968
|
import { randomInt } from "crypto";
|
|
14858
14969
|
import { join as join39 } from "path";
|
|
14859
14970
|
var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
|
|
@@ -14931,7 +15042,7 @@ function tunnelStatePath(stateDir, workspaceId) {
|
|
|
14931
15042
|
}
|
|
14932
15043
|
function readPersistedTunnel(stateDir, workspaceId) {
|
|
14933
15044
|
const path = tunnelStatePath(stateDir, workspaceId);
|
|
14934
|
-
if (!
|
|
15045
|
+
if (!existsSync35(path)) return void 0;
|
|
14935
15046
|
try {
|
|
14936
15047
|
return JSON.parse(readFileSync34(path, "utf8"));
|
|
14937
15048
|
} catch {
|
|
@@ -15044,7 +15155,7 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
|
|
|
15044
15155
|
}
|
|
15045
15156
|
function readAllPersistedWorkspaceIds(stateDir) {
|
|
15046
15157
|
const dir = join39(stateDir, "passengers");
|
|
15047
|
-
if (!
|
|
15158
|
+
if (!existsSync35(dir)) return [];
|
|
15048
15159
|
return readdirSync13(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
|
|
15049
15160
|
}
|
|
15050
15161
|
|
|
@@ -15069,7 +15180,7 @@ function realPoolMemberDeps(o) {
|
|
|
15069
15180
|
crewVersion: crewVersion(o.crewHome),
|
|
15070
15181
|
hostname: hostname3()
|
|
15071
15182
|
},
|
|
15072
|
-
hasShipConfig: () =>
|
|
15183
|
+
hasShipConfig: () => existsSync36(findConfigFile(o.crewHome)),
|
|
15073
15184
|
async provision(lease, instanceClass, shipKind) {
|
|
15074
15185
|
const hosts = lease.forges.map((f) => f.host.toLowerCase());
|
|
15075
15186
|
const lines2 = [];
|
|
@@ -16439,7 +16550,7 @@ async function resolveOne(tracker, t, seat, approvedStatus, buildingStatus, affi
|
|
|
16439
16550
|
}
|
|
16440
16551
|
|
|
16441
16552
|
// src/repo-refresh.ts
|
|
16442
|
-
import { existsSync as
|
|
16553
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync26, readFileSync as readFileSync35, writeFileSync as writeFileSync29 } from "fs";
|
|
16443
16554
|
import { dirname as dirname20 } from "path";
|
|
16444
16555
|
var REFRESH_COOLDOWN_MS = 2 * 6e4;
|
|
16445
16556
|
var UNSERVABLE_PASSES_BEFORE_ATTENTION = 3;
|
|
@@ -16469,7 +16580,7 @@ function applyRepoRows(resolved, rows) {
|
|
|
16469
16580
|
}
|
|
16470
16581
|
function persistRepoRows(stateDir, routeName, rows) {
|
|
16471
16582
|
const path = resolvedPathFor(stateDir, routeName);
|
|
16472
|
-
if (!
|
|
16583
|
+
if (!existsSync37(path)) return;
|
|
16473
16584
|
const raw = JSON.parse(readFileSync35(path, "utf8"));
|
|
16474
16585
|
applyRepoRows(raw, rows);
|
|
16475
16586
|
mkdirSync26(dirname20(path), { recursive: true });
|
|
@@ -16924,12 +17035,12 @@ var localRefSkipReported = (ticket, problem, events) => events.some((e) => e.rec
|
|
|
16924
17035
|
|
|
16925
17036
|
// src/connect-wizard.ts
|
|
16926
17037
|
import { delimiter, dirname as dirname21, join as join41 } from "path";
|
|
16927
|
-
import { accessSync as accessSync3, constants as constants3, existsSync as
|
|
17038
|
+
import { accessSync as accessSync3, constants as constants3, existsSync as existsSync38, mkdirSync as mkdirSync27, writeFileSync as writeFileSync30 } from "fs";
|
|
16928
17039
|
import { stringify as stringify3 } from "yaml";
|
|
16929
17040
|
var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
|
|
16930
17041
|
function shouldRunWizard(opts) {
|
|
16931
17042
|
if (!opts.isTTY) return false;
|
|
16932
|
-
const exists = opts.configExists ??
|
|
17043
|
+
const exists = opts.configExists ?? existsSync38;
|
|
16933
17044
|
return !configSearchPath(opts.crewHome).some((p) => exists(p));
|
|
16934
17045
|
}
|
|
16935
17046
|
function firstRunConfigPath() {
|
|
@@ -17123,7 +17234,7 @@ function flagWizardAnswers(f, env) {
|
|
|
17123
17234
|
}
|
|
17124
17235
|
|
|
17125
17236
|
// src/release-run.ts
|
|
17126
|
-
import { readFileSync as readFileSync36, writeFileSync as writeFileSync31, existsSync as
|
|
17237
|
+
import { readFileSync as readFileSync36, writeFileSync as writeFileSync31, existsSync as existsSync39, copyFileSync, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync3, rmSync as rmSync19 } from "fs";
|
|
17127
17238
|
import { dirname as dirname23, join as join43 } from "path";
|
|
17128
17239
|
import { tmpdir as tmpdir5 } from "os";
|
|
17129
17240
|
|
|
@@ -17867,7 +17978,7 @@ async function bump(o, size) {
|
|
|
17867
17978
|
}
|
|
17868
17979
|
const files = o.repo.release.versionFiles.map((f) => join43(o.cwd, f));
|
|
17869
17980
|
const first = files[0];
|
|
17870
|
-
if (!
|
|
17981
|
+
if (!existsSync39(first)) {
|
|
17871
17982
|
o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
|
|
17872
17983
|
return null;
|
|
17873
17984
|
}
|
|
@@ -17903,7 +18014,7 @@ function writeChangelog(o, version, entries) {
|
|
|
17903
18014
|
o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
|
|
17904
18015
|
return;
|
|
17905
18016
|
}
|
|
17906
|
-
const existing =
|
|
18017
|
+
const existing = existsSync39(path) ? readFileSync36(path, "utf8") : "# Changelog\n";
|
|
17907
18018
|
writeFileSync31(path, insertChangelogSection(existing, section));
|
|
17908
18019
|
}
|
|
17909
18020
|
async function confirm(o, expected) {
|
|
@@ -18009,7 +18120,7 @@ function openReleaseCheckout(o) {
|
|
|
18009
18120
|
}
|
|
18010
18121
|
for (const f of o.repo.worktrees.copy) {
|
|
18011
18122
|
const src = join43(o.cwd, f);
|
|
18012
|
-
if (!
|
|
18123
|
+
if (!existsSync39(src)) continue;
|
|
18013
18124
|
mkdirSync28(dirname23(join43(dir, f)), { recursive: true });
|
|
18014
18125
|
copyFileSync(src, join43(dir, f));
|
|
18015
18126
|
}
|
|
@@ -18990,7 +19101,7 @@ function missingReleaserWarning(repo, release, ships, now = Date.now()) {
|
|
|
18990
19101
|
}
|
|
18991
19102
|
|
|
18992
19103
|
// src/watch.ts
|
|
18993
|
-
import { watch as watchFile, existsSync as
|
|
19104
|
+
import { watch as watchFile, existsSync as existsSync40, statSync as statSync11, openSync as openSync5, readSync as readSync2, closeSync as closeSync5 } from "fs";
|
|
18994
19105
|
var E = "\x1B[";
|
|
18995
19106
|
var dim = (s) => `${E}2m${s}${E}0m`;
|
|
18996
19107
|
var bold = (s) => `${E}1m${s}${E}0m`;
|
|
@@ -19091,7 +19202,7 @@ function renderHeaders(states, now = Date.now()) {
|
|
|
19091
19202
|
return states.map((s) => renderHeader(s, now)).join("");
|
|
19092
19203
|
}
|
|
19093
19204
|
function readFrom(file, from) {
|
|
19094
|
-
if (!
|
|
19205
|
+
if (!existsSync40(file)) return { events: [], offset: from };
|
|
19095
19206
|
const size = statSync11(file).size;
|
|
19096
19207
|
if (size < from) return readFrom(file, 0);
|
|
19097
19208
|
if (size === from) return { events: [], offset: from };
|
|
@@ -19334,7 +19445,7 @@ function nextRoles(decision, ran) {
|
|
|
19334
19445
|
}
|
|
19335
19446
|
|
|
19336
19447
|
// src/ship-name.ts
|
|
19337
|
-
import { existsSync as
|
|
19448
|
+
import { existsSync as existsSync41, mkdirSync as mkdirSync29, readFileSync as readFileSync37, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
|
|
19338
19449
|
import { dirname as dirname24 } from "path";
|
|
19339
19450
|
import { parseDocument as parseDocument5 } from "yaml";
|
|
19340
19451
|
var ShipNameError = class extends Error {
|
|
@@ -19396,7 +19507,7 @@ async function syncRouteName(i) {
|
|
|
19396
19507
|
return { status: "ok", ...base, action, name: i.local };
|
|
19397
19508
|
}
|
|
19398
19509
|
function readSyncedName(resolvedFile) {
|
|
19399
|
-
if (!
|
|
19510
|
+
if (!existsSync41(resolvedFile)) return void 0;
|
|
19400
19511
|
try {
|
|
19401
19512
|
const v = JSON.parse(readFileSync37(resolvedFile, "utf8")).shipName;
|
|
19402
19513
|
return typeof v === "string" ? v : void 0;
|
|
@@ -19405,7 +19516,7 @@ function readSyncedName(resolvedFile) {
|
|
|
19405
19516
|
}
|
|
19406
19517
|
}
|
|
19407
19518
|
function writeSyncedName(resolvedFile, name) {
|
|
19408
|
-
if (!
|
|
19519
|
+
if (!existsSync41(resolvedFile)) return;
|
|
19409
19520
|
let raw;
|
|
19410
19521
|
try {
|
|
19411
19522
|
raw = JSON.parse(readFileSync37(resolvedFile, "utf8"));
|
|
@@ -20107,7 +20218,7 @@ function describeCapability(i) {
|
|
|
20107
20218
|
}
|
|
20108
20219
|
|
|
20109
20220
|
// src/agents-sync-run.ts
|
|
20110
|
-
import { existsSync as
|
|
20221
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync30, readFileSync as readFileSync38, writeFileSync as writeFileSync33 } from "fs";
|
|
20111
20222
|
import { dirname as dirname25 } from "path";
|
|
20112
20223
|
async function runAgentsSync(target, o) {
|
|
20113
20224
|
const dryRun = o.dryRun ?? false;
|
|
@@ -20141,7 +20252,7 @@ async function runAgentsSync(target, o) {
|
|
|
20141
20252
|
for (const s of skillResult?.outcomes ?? []) o.say(describeSkillSyncOutcome(s));
|
|
20142
20253
|
if (!dryRun) {
|
|
20143
20254
|
const resolvedPath = resolvedPathFor(o.stateDir, target.route);
|
|
20144
|
-
const raw =
|
|
20255
|
+
const raw = existsSync42(resolvedPath) ? JSON.parse(readFileSync38(resolvedPath, "utf8")) : {};
|
|
20145
20256
|
raw.agentPersonas = personaResult.agentPersonas;
|
|
20146
20257
|
if (skillResult) raw.agentSkills = skillResult.agentSkills;
|
|
20147
20258
|
if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
|
|
@@ -20836,7 +20947,7 @@ function planSampleTicket(i) {
|
|
|
20836
20947
|
}
|
|
20837
20948
|
|
|
20838
20949
|
// src/worktree-sweep.ts
|
|
20839
|
-
import { existsSync as
|
|
20950
|
+
import { existsSync as existsSync43, readFileSync as readFileSync40, readdirSync as readdirSync14 } from "fs";
|
|
20840
20951
|
import { basename as basename13, dirname as dirname27, join as join45, resolve as resolve5 } from "path";
|
|
20841
20952
|
function planWorktreeSweep(target, tickets, contract) {
|
|
20842
20953
|
const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
|
|
@@ -20949,7 +21060,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
|
|
|
20949
21060
|
let removed = 0;
|
|
20950
21061
|
let keptBranches = 0;
|
|
20951
21062
|
for (const a of actions) {
|
|
20952
|
-
if (!dryRun && base && !a.keepBranch &&
|
|
21063
|
+
if (!dryRun && base && !a.keepBranch && existsSync43(a.path) && isHandBackMerge(a.path, base)) {
|
|
20953
21064
|
if (gitOk(a.path, ["merge", "--abort"]) !== null) {
|
|
20954
21065
|
log.emit(
|
|
20955
21066
|
`abandoned the hand-back merge of ${base} in ${a.path}: ${a.ticket.issue_id} deployed without it`,
|
|
@@ -20957,7 +21068,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
|
|
|
20957
21068
|
);
|
|
20958
21069
|
}
|
|
20959
21070
|
}
|
|
20960
|
-
if (!dryRun &&
|
|
21071
|
+
if (!dryRun && existsSync43(a.path)) {
|
|
20961
21072
|
let dirty = false;
|
|
20962
21073
|
try {
|
|
20963
21074
|
dirty = status(a.path).length > 0;
|
|
@@ -20968,7 +21079,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
|
|
|
20968
21079
|
continue;
|
|
20969
21080
|
}
|
|
20970
21081
|
}
|
|
20971
|
-
if (!dryRun && a.requirePushed && a.branch &&
|
|
21082
|
+
if (!dryRun && a.requirePushed && a.branch && existsSync43(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
|
|
20972
21083
|
refuse2(a, "unpushed commits", `${a.branch} has commits that are not on ${remote}`);
|
|
20973
21084
|
continue;
|
|
20974
21085
|
}
|
|
@@ -21122,12 +21233,12 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
|
|
|
21122
21233
|
}
|
|
21123
21234
|
|
|
21124
21235
|
// src/stream-sweep.ts
|
|
21125
|
-
import { existsSync as
|
|
21236
|
+
import { existsSync as existsSync44, readdirSync as readdirSync15, rmSync as rmSync20, statSync as statSync12 } from "fs";
|
|
21126
21237
|
import { join as join46 } from "path";
|
|
21127
21238
|
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
21128
21239
|
function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
|
|
21129
21240
|
const dir = join46(stateDir, "streams");
|
|
21130
|
-
if (!
|
|
21241
|
+
if (!existsSync44(dir)) return [];
|
|
21131
21242
|
const cutoff = now - retentionDays * DAY_MS2;
|
|
21132
21243
|
return readdirSync15(dir).map((name) => join46(dir, name)).filter((path) => {
|
|
21133
21244
|
try {
|
|
@@ -21155,7 +21266,7 @@ function applyStreamSweep(paths, dryRun, log) {
|
|
|
21155
21266
|
return removed;
|
|
21156
21267
|
}
|
|
21157
21268
|
function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
|
|
21158
|
-
if (!
|
|
21269
|
+
if (!existsSync44(stateDir)) return [];
|
|
21159
21270
|
const cutoff = now - retentionDays * DAY_MS2;
|
|
21160
21271
|
return readdirSync15(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join46(stateDir, name)).filter((path) => {
|
|
21161
21272
|
try {
|
|
@@ -21167,11 +21278,11 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
|
|
|
21167
21278
|
}
|
|
21168
21279
|
|
|
21169
21280
|
// src/cli.ts
|
|
21170
|
-
import { realpathSync as realpathSync3, readFileSync as readFileSync46, existsSync as
|
|
21281
|
+
import { realpathSync as realpathSync3, readFileSync as readFileSync46, existsSync as existsSync51, mkdirSync as mkdirSync38, writeFileSync as writeFileSync38, rmSync as rmSync25, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync6, statSync as statSync16, readdirSync as readdirSync20 } from "fs";
|
|
21171
21282
|
import { createInterface } from "readline/promises";
|
|
21172
21283
|
|
|
21173
21284
|
// src/periscope-cmd.ts
|
|
21174
|
-
import { existsSync as
|
|
21285
|
+
import { existsSync as existsSync45, mkdirSync as mkdirSync31, readFileSync as readFileSync41, rmSync as rmSync21, writeFileSync as writeFileSync34 } from "fs";
|
|
21175
21286
|
import { join as join47 } from "path";
|
|
21176
21287
|
var PeriscopeError = class extends Error {
|
|
21177
21288
|
};
|
|
@@ -21197,7 +21308,7 @@ function writePeriscopePrior(stateDir, ticketRecordId, prior) {
|
|
|
21197
21308
|
function readPeriscopePrior(stateDir, ticketRecordId) {
|
|
21198
21309
|
try {
|
|
21199
21310
|
const p = priorPath(stateDir, ticketRecordId);
|
|
21200
|
-
return
|
|
21311
|
+
return existsSync45(p) ? JSON.parse(readFileSync41(p, "utf8")) : void 0;
|
|
21201
21312
|
} catch {
|
|
21202
21313
|
return void 0;
|
|
21203
21314
|
}
|
|
@@ -21752,7 +21863,7 @@ import { dirname as dirOf2, resolve as resolvePath } from "path";
|
|
|
21752
21863
|
|
|
21753
21864
|
// src/ssh-keys.ts
|
|
21754
21865
|
import { execFileSync as execFileSync13 } from "child_process";
|
|
21755
|
-
import { existsSync as
|
|
21866
|
+
import { existsSync as existsSync46, mkdirSync as mkdirSync32, readFileSync as readFileSync42, chmodSync as chmodSync3 } from "fs";
|
|
21756
21867
|
import { dirname as dirname28, join as join49 } from "path";
|
|
21757
21868
|
var SshKeyError = class extends Error {
|
|
21758
21869
|
};
|
|
@@ -21762,7 +21873,7 @@ function shipSshKeyPathFor(stateDir) {
|
|
|
21762
21873
|
function ensureShipSshKeypair(stateDir) {
|
|
21763
21874
|
const privateKeyPath = shipSshKeyPathFor(stateDir);
|
|
21764
21875
|
const publicKeyPath = `${privateKeyPath}.pub`;
|
|
21765
|
-
if (!
|
|
21876
|
+
if (!existsSync46(privateKeyPath)) {
|
|
21766
21877
|
mkdirSync32(dirname28(privateKeyPath), { recursive: true });
|
|
21767
21878
|
try {
|
|
21768
21879
|
execFileSync13("ssh-keygen", [
|
|
@@ -21783,7 +21894,7 @@ function ensureShipSshKeypair(stateDir) {
|
|
|
21783
21894
|
}
|
|
21784
21895
|
chmodSync3(privateKeyPath, 384);
|
|
21785
21896
|
}
|
|
21786
|
-
if (!
|
|
21897
|
+
if (!existsSync46(publicKeyPath)) {
|
|
21787
21898
|
throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
|
|
21788
21899
|
}
|
|
21789
21900
|
const publicKey = readFileSync42(publicKeyPath, "utf8").trim();
|
|
@@ -21950,7 +22061,7 @@ async function unregisterEndpoint(workspaceId, info, d) {
|
|
|
21950
22061
|
import { createServer as createHttpServer } from "http";
|
|
21951
22062
|
import { spawn as spawn9 } from "child_process";
|
|
21952
22063
|
import { randomBytes } from "crypto";
|
|
21953
|
-
import { existsSync as
|
|
22064
|
+
import { existsSync as existsSync47, mkdirSync as mkdirSync34, readFileSync as readFileSync43, writeFileSync as writeFileSync35, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
|
|
21954
22065
|
import { join as join50 } from "path";
|
|
21955
22066
|
|
|
21956
22067
|
// src/passenger-mirror.ts
|
|
@@ -22085,7 +22196,7 @@ function syncDaemonStatePath(stateDir, workspaceId) {
|
|
|
22085
22196
|
}
|
|
22086
22197
|
function readPersistedSyncDaemon(stateDir, workspaceId) {
|
|
22087
22198
|
const path = syncDaemonStatePath(stateDir, workspaceId);
|
|
22088
|
-
if (!
|
|
22199
|
+
if (!existsSync47(path)) return void 0;
|
|
22089
22200
|
try {
|
|
22090
22201
|
return JSON.parse(readFileSync43(path, "utf8"));
|
|
22091
22202
|
} catch {
|
|
@@ -22116,7 +22227,7 @@ function waitForExit(pid, alive, timeoutMs = 5e3) {
|
|
|
22116
22227
|
}
|
|
22117
22228
|
function readAllPersistedSyncWorkspaceIds(stateDir) {
|
|
22118
22229
|
const dir = join50(stateDir, "passengers");
|
|
22119
|
-
if (!
|
|
22230
|
+
if (!existsSync47(dir)) return [];
|
|
22120
22231
|
const suffix = "-sync.json";
|
|
22121
22232
|
return readdirSync16(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
|
|
22122
22233
|
}
|
|
@@ -22613,7 +22724,7 @@ function describeCause(err3) {
|
|
|
22613
22724
|
}
|
|
22614
22725
|
|
|
22615
22726
|
// src/daemon.ts
|
|
22616
|
-
import { existsSync as
|
|
22727
|
+
import { existsSync as existsSync48, mkdirSync as mkdirSync36, readFileSync as readFileSync44, readdirSync as readdirSync17, rmSync as rmSync22, statSync as statSync14, writeFileSync as writeFileSync36 } from "fs";
|
|
22617
22728
|
import { join as join51 } from "path";
|
|
22618
22729
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
22619
22730
|
var DEFAULT_FLOOR_MS = 5e3;
|
|
@@ -22711,7 +22822,7 @@ function computeTreeSignature(paths) {
|
|
|
22711
22822
|
}
|
|
22712
22823
|
function makeStaleChecker(paths, opts = {}) {
|
|
22713
22824
|
const initial = computeTreeSignature(paths);
|
|
22714
|
-
return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 &&
|
|
22825
|
+
return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync48(opts.reloadSentinel);
|
|
22715
22826
|
}
|
|
22716
22827
|
function reloadSentinelFor(stateDir) {
|
|
22717
22828
|
return join51(stateDir, "reload-requested");
|
|
@@ -23300,7 +23411,7 @@ import { createHash as createHash10 } from "crypto";
|
|
|
23300
23411
|
import {
|
|
23301
23412
|
chmodSync as chmodSync4,
|
|
23302
23413
|
closeSync as closeSync6,
|
|
23303
|
-
existsSync as
|
|
23414
|
+
existsSync as existsSync49,
|
|
23304
23415
|
fstatSync,
|
|
23305
23416
|
openSync as openSync6,
|
|
23306
23417
|
readSync as readSync3,
|
|
@@ -23671,7 +23782,7 @@ function lastLines2(path, n) {
|
|
|
23671
23782
|
}
|
|
23672
23783
|
function readTail(live, opts) {
|
|
23673
23784
|
const max = opts.maxBytes ?? MAX_TAIL_BYTES;
|
|
23674
|
-
if (!
|
|
23785
|
+
if (!existsSync49(live)) {
|
|
23675
23786
|
return { lines: [], cursor: opts.after ?? encodeCursor({ dev: 0, ino: 0, off: 0 }), reset: false };
|
|
23676
23787
|
}
|
|
23677
23788
|
const cur = opts.after ? decodeCursor(opts.after) : null;
|
|
@@ -23828,7 +23939,7 @@ async function route(deps, method, url, req, abort) {
|
|
|
23828
23939
|
const run3 = decodeURIComponent(p.slice("/streams/".length));
|
|
23829
23940
|
if (!RUN_NAME_RE.test(run3) || run3.includes("..") || /\.(pid|meta\.json|events\.jsonl)$/.test(run3)) return err2(400, "not a run name");
|
|
23830
23941
|
const file = join52(deps.stateDir, "streams", run3.endsWith(".jsonl") ? run3 : `${run3}.jsonl`);
|
|
23831
|
-
if (!
|
|
23942
|
+
if (!existsSync49(file)) return err2(404, "no such run");
|
|
23832
23943
|
return tailReply(file, url.searchParams, false, abort);
|
|
23833
23944
|
}
|
|
23834
23945
|
if ((p === "/pause" || p === "/resume") && post3) {
|
|
@@ -23872,7 +23983,7 @@ function send(res, r) {
|
|
|
23872
23983
|
res.end(body);
|
|
23873
23984
|
}
|
|
23874
23985
|
async function socketIsStale(path) {
|
|
23875
|
-
if (!
|
|
23986
|
+
if (!existsSync49(path)) return false;
|
|
23876
23987
|
return new Promise((resolve8) => {
|
|
23877
23988
|
const c = createConnection(path);
|
|
23878
23989
|
c.once("connect", () => {
|
|
@@ -23891,7 +24002,7 @@ async function startControlServer(deps) {
|
|
|
23891
24002
|
return null;
|
|
23892
24003
|
}
|
|
23893
24004
|
if (await socketIsStale(path)) rmSync23(path, { force: true });
|
|
23894
|
-
else if (
|
|
24005
|
+
else if (existsSync49(path)) {
|
|
23895
24006
|
log(`control API not started: ${path} is in use by another process`);
|
|
23896
24007
|
return null;
|
|
23897
24008
|
}
|
|
@@ -23984,7 +24095,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
|
|
|
23984
24095
|
|
|
23985
24096
|
// src/periscope-endpoints.ts
|
|
23986
24097
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
23987
|
-
import { chmodSync as chmodSync5, existsSync as
|
|
24098
|
+
import { chmodSync as chmodSync5, existsSync as existsSync50, mkdirSync as mkdirSync37, readdirSync as readdirSync19, readFileSync as readFileSync45, rmSync as rmSync24, writeFileSync as writeFileSync37 } from "fs";
|
|
23988
24099
|
import { join as join53 } from "path";
|
|
23989
24100
|
var secretDir = (stateDir) => join53(stateDir, "periscope-mcp");
|
|
23990
24101
|
var secretFile = (stateDir, id) => join53(secretDir(stateDir), `${id}.json`);
|
|
@@ -24027,7 +24138,7 @@ function removePeriscopeEndpointRecord(stateDir, ticketRecordId) {
|
|
|
24027
24138
|
}
|
|
24028
24139
|
function listPeriscopeEndpoints(stateDir) {
|
|
24029
24140
|
const dir = endpointDir(stateDir);
|
|
24030
|
-
if (!
|
|
24141
|
+
if (!existsSync50(dir)) return [];
|
|
24031
24142
|
const out = [];
|
|
24032
24143
|
for (const name of readdirSync19(dir)) {
|
|
24033
24144
|
if (!name.endsWith(".json")) continue;
|
|
@@ -24391,7 +24502,7 @@ function findPeriscope(sidecars, segment) {
|
|
|
24391
24502
|
}
|
|
24392
24503
|
|
|
24393
24504
|
// src/cli.ts
|
|
24394
|
-
var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname33(process.execPath) : resolve7(dirname33(
|
|
24505
|
+
var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname33(process.execPath) : resolve7(dirname33(fileURLToPath2(import.meta.url)), "..");
|
|
24395
24506
|
var RELEASE_LOCK_TTL_MS = 20 * 60 * 1e3;
|
|
24396
24507
|
var CYCLE_SECONDS2 = 120;
|
|
24397
24508
|
var STALE_CYCLES = 3;
|
|
@@ -25004,7 +25115,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
25004
25115
|
} else {
|
|
25005
25116
|
const toleratesNoRoutes = command === "credential" || command === "forge" || command === "repos" || command === "ship" || command === "disconnect" && flag("reset") || command === "deprovision" || command === "connect" && flag("sign-out");
|
|
25006
25117
|
const configless = command === "connect" && flag("sign-out") || command === "disconnect" && flag("reset") || command === "deprovision";
|
|
25007
|
-
if (configless && !
|
|
25118
|
+
if (configless && !existsSync51(findConfigFile(CREW_HOME))) {
|
|
25008
25119
|
cfg = loadConfig(CREW_HOME, firstRunConfigPath(), { text: "ship: {}\n", allowNoRoutes: true });
|
|
25009
25120
|
} else {
|
|
25010
25121
|
cfg = loadConfig(CREW_HOME, void 0, toleratesNoRoutes ? { allowNoRoutes: true } : {});
|
|
@@ -25327,7 +25438,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
25327
25438
|
if (host === "linux" && hasSystemd) {
|
|
25328
25439
|
const label = labelFor(CREW_HOME, "run");
|
|
25329
25440
|
const staleTimer = join55(homedir11(), ".config", "systemd", "user", `${label}.timer`);
|
|
25330
|
-
if (
|
|
25441
|
+
if (existsSync51(staleTimer)) {
|
|
25331
25442
|
process.stdout.write(
|
|
25332
25443
|
`removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
|
|
25333
25444
|
`
|
|
@@ -25354,7 +25465,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
25354
25465
|
const tracker = new Tracker(c, cfg.ship);
|
|
25355
25466
|
problems.push(...validateContract(tracker.contract).map((p) => `contract: ${p}`));
|
|
25356
25467
|
for (const r of await resolvedRepos(c)) {
|
|
25357
|
-
if (!
|
|
25468
|
+
if (!existsSync51(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
|
|
25358
25469
|
if (!satisfies(host, r.config.platform)) problems.push(`repo ${r.name}: requires ${r.config.platform}, this host is ${host}`);
|
|
25359
25470
|
readiness.push(...validateEffective(r.config).map((p) => `repo ${r.name}: ${p}`));
|
|
25360
25471
|
}
|
|
@@ -25381,7 +25492,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
25381
25492
|
}
|
|
25382
25493
|
async function doctorFix(o) {
|
|
25383
25494
|
const routes = await Promise.all(routesInScope(cfg.routes, named).map((c) => gatherHealth({ route: c.route, enabled: c.enabled, hasRepos: reposOf(c).length > 0 }, () => routeProblems(c))));
|
|
25384
|
-
const unitExists = (p) =>
|
|
25495
|
+
const unitExists = (p) => existsSync51(p);
|
|
25385
25496
|
const crontab = o.host === "linux" ? spawnSync11("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
|
|
25386
25497
|
const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run", installContext()), unitExists, crontab) : true;
|
|
25387
25498
|
const plan = planFix({ routes, schedulerAbsent, host: o.host });
|
|
@@ -25427,7 +25538,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
25427
25538
|
warm = readWarmFiles(r.dir, `${r.config.branch.remote}/${r.config.branch.base}`, r.config.sandbox.warm ?? []);
|
|
25428
25539
|
} catch {
|
|
25429
25540
|
}
|
|
25430
|
-
out.set(r.name, { name: r.name, decl: intelFlavor(r.config.hooks.intel) === "serena" ? { ...r.config.sandbox, intel: "serena" } : r.config.sandbox, warm });
|
|
25541
|
+
out.set(r.name, { name: r.name, decl: intelFlavor(r.config.hooks.intel) === "serena" ? { ...r.config.sandbox, intel: "serena" } : r.config.sandbox, warm, ...wantsCodeBridge(r.config.hooks.intel) ? { bridge: readBridgeBundle() } : {} });
|
|
25431
25542
|
}
|
|
25432
25543
|
}
|
|
25433
25544
|
return [...out.values()];
|
|
@@ -26842,7 +26953,7 @@ ${cu.tail.trimEnd()}
|
|
|
26842
26953
|
`);
|
|
26843
26954
|
if (!dryRun) {
|
|
26844
26955
|
const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
|
|
26845
|
-
const raw =
|
|
26956
|
+
const raw = existsSync51(resolvedPath) ? JSON.parse(readFileSync46(resolvedPath, "utf8")) : {};
|
|
26846
26957
|
raw.agentSkills = result.agentSkills;
|
|
26847
26958
|
mkdirSync38(dirOf2(resolvedPath), { recursive: true });
|
|
26848
26959
|
writeFileSync38(resolvedPath, `${JSON.stringify(raw, null, 2)}
|
|
@@ -26947,8 +27058,8 @@ ${cu.tail.trimEnd()}
|
|
|
26947
27058
|
try {
|
|
26948
27059
|
const a = parseHooksArgs(positional.slice(2), routeNamesOf(configText));
|
|
26949
27060
|
const dir = repoDirOf(configText, dirname33(cfg.configFile), a.route, a.name);
|
|
26950
|
-
if (!
|
|
26951
|
-
const files = { exists: (r) =>
|
|
27061
|
+
if (!existsSync51(dir)) throw new ReposError(`${dir} does not exist on disk`);
|
|
27062
|
+
const files = { exists: (r) => existsSync51(resolvePath(dir, r)), list: (r) => {
|
|
26952
27063
|
try {
|
|
26953
27064
|
return readdirSync20(resolvePath(dir, r));
|
|
26954
27065
|
} catch {
|
|
@@ -27066,7 +27177,7 @@ ${cu.tail.trimEnd()}
|
|
|
27066
27177
|
if (sub === "list" && !routeName) usage2();
|
|
27067
27178
|
if (sub === "add") {
|
|
27068
27179
|
try {
|
|
27069
|
-
const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) =>
|
|
27180
|
+
const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync51(resolvePath(p.replace(/^~(?=\/|$)/, homedir11()))));
|
|
27070
27181
|
routeName = a.route;
|
|
27071
27182
|
addPath = a.path;
|
|
27072
27183
|
addName = a.name;
|
|
@@ -27096,7 +27207,7 @@ ${cu.tail.trimEnd()}
|
|
|
27096
27207
|
const resolvedFile = resolvedPathFor(cfg.ship.stateDir, routeName);
|
|
27097
27208
|
if (sub === "list" || sub === "add") {
|
|
27098
27209
|
const liveRoute = cfg.routes.find((r) => r.route === routeName);
|
|
27099
|
-
if (liveRoute?.resolved?.reposModelId &&
|
|
27210
|
+
if (liveRoute?.resolved?.reposModelId && existsSync51(resolvedFile) && !dryRun) {
|
|
27100
27211
|
try {
|
|
27101
27212
|
await hydrateApiKeys([liveRoute]);
|
|
27102
27213
|
persistRepoRows(cfg.ship.stateDir, liveRoute.route, await new Tracker(liveRoute, cfg.ship).repoRowsForMap());
|
|
@@ -27105,7 +27216,7 @@ ${cu.tail.trimEnd()}
|
|
|
27105
27216
|
}
|
|
27106
27217
|
}
|
|
27107
27218
|
let resolvedRemotes;
|
|
27108
|
-
if (
|
|
27219
|
+
if (existsSync51(resolvedFile)) {
|
|
27109
27220
|
try {
|
|
27110
27221
|
resolvedRemotes = JSON.parse(readFileSync46(resolvedFile, "utf8"));
|
|
27111
27222
|
} catch {
|
|
@@ -27114,7 +27225,7 @@ ${cu.tail.trimEnd()}
|
|
|
27114
27225
|
const originOf = (dir) => gitOk(dir, ["remote", "get-url", "origin"]) ?? void 0;
|
|
27115
27226
|
const text2 = configText;
|
|
27116
27227
|
const base = dirname33(cfg.configFile);
|
|
27117
|
-
const isCheckout = (d) =>
|
|
27228
|
+
const isCheckout = (d) => existsSync51(`${d}/.git`);
|
|
27118
27229
|
try {
|
|
27119
27230
|
if (sub === "list") {
|
|
27120
27231
|
if (flag("json")) {
|
|
@@ -27124,7 +27235,7 @@ ${cu.tail.trimEnd()}
|
|
|
27124
27235
|
base,
|
|
27125
27236
|
resolved: resolvedRemotes,
|
|
27126
27237
|
originOf,
|
|
27127
|
-
exists: (d) =>
|
|
27238
|
+
exists: (d) => existsSync51(d),
|
|
27128
27239
|
isCheckout,
|
|
27129
27240
|
repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
|
|
27130
27241
|
try {
|
|
@@ -27145,7 +27256,7 @@ ${cu.tail.trimEnd()}
|
|
|
27145
27256
|
for (const r of repos) {
|
|
27146
27257
|
const origin2 = originOf(r.dir);
|
|
27147
27258
|
const row = matchRepoName(origin2, resolvedRemotes);
|
|
27148
|
-
const note = !
|
|
27259
|
+
const note = !existsSync51(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
|
|
27149
27260
|
process.stdout.write(`${r.name} ${r.dir} ${note}
|
|
27150
27261
|
`);
|
|
27151
27262
|
}
|
|
@@ -27157,9 +27268,9 @@ ${cu.tail.trimEnd()}
|
|
|
27157
27268
|
` : `${human}
|
|
27158
27269
|
`);
|
|
27159
27270
|
const plan2 = planRepoClone({ text: text2, base, route: routeName, name: addName, resolved: resolvedRemotes, dir: value("dir"), isCheckout });
|
|
27160
|
-
if (
|
|
27271
|
+
if (existsSync51(plan2.target) && (existsSync51(`${plan2.target}/.git`) || readdirSync20(plan2.target).length > 0)) {
|
|
27161
27272
|
throw new ReposError(
|
|
27162
|
-
|
|
27273
|
+
existsSync51(`${plan2.target}/.git`) ? `${plan2.target} is already a checkout \u2014 attach it with \`crew repos add ${routeName} ${plan2.name} ${plan2.target}\`` : `${plan2.target} exists and is not empty \u2014 pass --dir to clone somewhere else`
|
|
27163
27274
|
);
|
|
27164
27275
|
}
|
|
27165
27276
|
if (dryRun) {
|
|
@@ -27184,7 +27295,7 @@ ${cu.tail.trimEnd()}
|
|
|
27184
27295
|
break;
|
|
27185
27296
|
}
|
|
27186
27297
|
const target = resolvePath(addPath.replace(/^~(?=\/|$)/, homedir11()));
|
|
27187
|
-
const top =
|
|
27298
|
+
const top = existsSync51(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
|
|
27188
27299
|
if (!top) throw new ReposError(`${target} is not a git checkout`);
|
|
27189
27300
|
const origin = originOf(top);
|
|
27190
27301
|
const plan = planRepoAdd({
|
|
@@ -27236,7 +27347,7 @@ ${cu.tail.trimEnd()}
|
|
|
27236
27347
|
const created = await rowTracker.createRepoRow(rowPayload);
|
|
27237
27348
|
process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
|
|
27238
27349
|
`);
|
|
27239
|
-
if (
|
|
27350
|
+
if (existsSync51(resolvedFile)) {
|
|
27240
27351
|
const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
|
|
27241
27352
|
raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
|
|
27242
27353
|
raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
|
|
@@ -27658,7 +27769,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
27658
27769
|
}
|
|
27659
27770
|
}
|
|
27660
27771
|
let previous;
|
|
27661
|
-
const hadPreviousFile =
|
|
27772
|
+
const hadPreviousFile = existsSync51(resolvedPath);
|
|
27662
27773
|
if (hadPreviousFile) {
|
|
27663
27774
|
try {
|
|
27664
27775
|
previous = JSON.parse(readFileSync46(resolvedPath, "utf8"));
|
|
@@ -28190,7 +28301,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
28190
28301
|
if (subS === "images") {
|
|
28191
28302
|
const stateS = readSandboxState(cfg.ship.stateDir);
|
|
28192
28303
|
const rows = declared.map((r) => {
|
|
28193
|
-
const plan = planSandbox(r.name, r.decl, r.warm);
|
|
28304
|
+
const plan = planSandbox(r.name, r.decl, r.warm, r.bridge);
|
|
28194
28305
|
return { repo: r.name, wants: plan.tag, tags: stateS[r.name]?.tags ?? [], ...sandboxImageStatus(stateS, plan) };
|
|
28195
28306
|
});
|
|
28196
28307
|
if (jsonS) {
|
|
@@ -28224,7 +28335,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
28224
28335
|
for (const t of targets) {
|
|
28225
28336
|
await sandboxBuildTick([t], depsS, true);
|
|
28226
28337
|
const after = readSandboxState(cfg.ship.stateDir);
|
|
28227
|
-
const st = sandboxImageStatus(after, planSandbox(t.name, t.decl, t.warm));
|
|
28338
|
+
const st = sandboxImageStatus(after, planSandbox(t.name, t.decl, t.warm, t.bridge));
|
|
28228
28339
|
if (st.status === "failed") failed = true;
|
|
28229
28340
|
process.stdout.write(`${t.name}: ${st.status} \u2014 ${st.detail}
|
|
28230
28341
|
`);
|
|
@@ -28536,7 +28647,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
28536
28647
|
prepare: async (path) => {
|
|
28537
28648
|
for (const f of repoWt.config.worktrees.copy) {
|
|
28538
28649
|
const src = join55(repoWt.dir, f);
|
|
28539
|
-
if (!
|
|
28650
|
+
if (!existsSync51(src) || existsSync51(join55(path, f))) continue;
|
|
28540
28651
|
mkdirSync38(dirOf2(join55(path, f)), { recursive: true });
|
|
28541
28652
|
copyFileSync2(src, join55(path, f));
|
|
28542
28653
|
}
|
|
@@ -28758,7 +28869,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
28758
28869
|
const shellR = hit && resolveShell(hit.repo.config.shell ?? cfg.ship.shell);
|
|
28759
28870
|
const resumed = await runPeriscopeResume({
|
|
28760
28871
|
find: async () => hit && { row: hit.row, tag: hit.tag },
|
|
28761
|
-
worktreeExists: (p) =>
|
|
28872
|
+
worktreeExists: (p) => existsSync51(p),
|
|
28762
28873
|
prepareDropFile,
|
|
28763
28874
|
startProxy: (wt, url, n) => launchProxy(crewInvocation(CREW_HOME), wt, url, Number(n), { host: hit.row.host || "localhost", inject: hit.repo.config.periscope?.inspector !== "repo" }),
|
|
28764
28875
|
caps: periscopeCaps(cfg.ship),
|
|
@@ -28855,7 +28966,7 @@ ${where.join("\n")}`);
|
|
|
28855
28966
|
prepare: async (path) => {
|
|
28856
28967
|
for (const f of repoP.config.worktrees.copy) {
|
|
28857
28968
|
const src = join55(repoP.dir, f);
|
|
28858
|
-
if (!
|
|
28969
|
+
if (!existsSync51(src)) continue;
|
|
28859
28970
|
mkdirSync38(dirOf2(join55(path, f)), { recursive: true });
|
|
28860
28971
|
copyFileSync2(src, join55(path, f));
|
|
28861
28972
|
}
|
|
@@ -29347,7 +29458,7 @@ ${where.join("\n")}`);
|
|
|
29347
29458
|
}
|
|
29348
29459
|
if (row) {
|
|
29349
29460
|
await tracker.deleteRepoRow(row.id);
|
|
29350
|
-
if (
|
|
29461
|
+
if (existsSync51(resolvedFile)) {
|
|
29351
29462
|
const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
|
|
29352
29463
|
if (raw.repoNames) delete raw.repoNames[row.id];
|
|
29353
29464
|
if (raw.repoRemotes) delete raw.repoRemotes[row.id];
|
|
@@ -29355,13 +29466,13 @@ ${where.join("\n")}`);
|
|
|
29355
29466
|
`);
|
|
29356
29467
|
}
|
|
29357
29468
|
}
|
|
29358
|
-
if (
|
|
29469
|
+
if (existsSync51(join55(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync25(dir, { recursive: true, force: true });
|
|
29359
29470
|
say("removed", { dir, closed }, `removed the sample repo${closed.length ? `, closed ${closed.join(", ")}` : ""}`);
|
|
29360
29471
|
break;
|
|
29361
29472
|
}
|
|
29362
29473
|
const projectId = route2.resolved.areaId ?? resolveRepoProject(await tracker.projectRows(), value("project"));
|
|
29363
|
-
const fresh = !
|
|
29364
|
-
if (
|
|
29474
|
+
const fresh = !existsSync51(join55(dir, ".git"));
|
|
29475
|
+
if (existsSync51(dir) && fresh && readdirSync20(dir).length > 0) fail2(`${dir} exists and is not a git checkout \u2014 move it aside`);
|
|
29365
29476
|
if (dryRun) {
|
|
29366
29477
|
say("dry-run", { dir, newCheckout: fresh, repoRow: row?.id ?? null }, `would ${fresh ? "create" : "reuse"} ${dir}${row ? "" : ", create the Repos row"}, and file one ${tracker.contract.statuses.approved} ticket`);
|
|
29367
29478
|
break;
|
|
@@ -29387,7 +29498,7 @@ ${where.join("\n")}`);
|
|
|
29387
29498
|
spec: { platform: "unix", releaseMode: "integrate", ciProvider: "none" }
|
|
29388
29499
|
});
|
|
29389
29500
|
repoId = (await tracker.createRepoRow(payload)).id;
|
|
29390
|
-
if (
|
|
29501
|
+
if (existsSync51(resolvedFile)) {
|
|
29391
29502
|
const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
|
|
29392
29503
|
raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
|
|
29393
29504
|
raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
|
|
@@ -29743,7 +29854,7 @@ watermark: ${state.watermark()}
|
|
|
29743
29854
|
}
|
|
29744
29855
|
for (const r of repos) {
|
|
29745
29856
|
const remote = r.config.branch.remote;
|
|
29746
|
-
if (
|
|
29857
|
+
if (existsSync51(r.dir)) {
|
|
29747
29858
|
pushAuth.push({
|
|
29748
29859
|
route: c.route,
|
|
29749
29860
|
repo: r.name,
|
|
@@ -29839,7 +29950,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
|
|
|
29839
29950
|
}
|
|
29840
29951
|
try {
|
|
29841
29952
|
const evFile = eventFileFor(cfg.ship.stateDir);
|
|
29842
|
-
const evSize =
|
|
29953
|
+
const evSize = existsSync51(evFile) ? statSync16(evFile).size : 0;
|
|
29843
29954
|
process.stdout.write(
|
|
29844
29955
|
evSize > cfg.ship.eventsRotateBytes * 4 ? `events log: WARN ${evFile} is ${Math.round(evSize / 1048576)} MB (> 4x eventsRotateBytes); run \`crew reap\` to rotate it
|
|
29845
29956
|
` : "events log: ok\n"
|
|
@@ -30303,7 +30414,7 @@ ${foreign.join("\n")}
|
|
|
30303
30414
|
const declaredS = await sandboxReposOf(cfg.routes.filter((r) => r.enabled));
|
|
30304
30415
|
const stateS = readSandboxState(cfg.ship.stateDir);
|
|
30305
30416
|
for (const r of declaredS) {
|
|
30306
|
-
const st = sandboxImageStatus(stateS, planSandbox(r.name, r.decl, r.warm));
|
|
30417
|
+
const st = sandboxImageStatus(stateS, planSandbox(r.name, r.decl, r.warm, r.bridge));
|
|
30307
30418
|
process.stdout.write(`sandbox image: ${r.name}: ${st.status.toUpperCase()} \u2014 ${st.detail}
|
|
30308
30419
|
`);
|
|
30309
30420
|
}
|