@tablation/crew 0.124.0 → 0.126.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 +288 -126
- package/dist/sandbox-intel-bridge.cjs +25191 -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.";
|
|
@@ -579,11 +588,11 @@ function startSerena(d, s) {
|
|
|
579
588
|
const dest = join5(p.home, "language_servers");
|
|
580
589
|
if (!d.exists(dest)) d.link(s.languageServers, dest);
|
|
581
590
|
}
|
|
582
|
-
const mainCache = join5(p.mainProject, "cache");
|
|
591
|
+
const mainCache = s.seedCache ?? join5(p.mainProject, "cache");
|
|
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,25 +10997,49 @@ 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;
|
|
11004
|
+
let seed;
|
|
10924
11005
|
try {
|
|
10925
11006
|
const repos = reposOf(o.route);
|
|
10926
11007
|
const target = repos.find((r) => r.name === o.repo) ?? (repos.length === 1 ? repos[0] : void 0);
|
|
10927
11008
|
intel = target ? loadRepoConfig(target.dir)?.hooks?.intel : void 0;
|
|
11009
|
+
const origin = target && o.ship.runner === "sandbox" && intelFlavor(intel) === "serena" ? originUrlOf(target.dir, "origin") : null;
|
|
11010
|
+
const remote = origin ? parseRemote(origin) : null;
|
|
11011
|
+
if (remote) seed = `${o.ship.sandbox?.mirrorDir ?? "/cache"}/${intelRelDir(remote)}/cache`;
|
|
10928
11012
|
} catch {
|
|
10929
11013
|
}
|
|
10930
11014
|
if (!wantsCodeBridge(intel)) return void 0;
|
|
10931
|
-
const
|
|
10932
|
-
const
|
|
11015
|
+
const sandbox = o.ship.runner === "sandbox";
|
|
11016
|
+
const args = sandbox ? [
|
|
11017
|
+
SANDBOX_BRIDGE_PATH,
|
|
11018
|
+
"--attach-file",
|
|
11019
|
+
`${SANDBOX_INTEL_DIR}/url`,
|
|
11020
|
+
"--start",
|
|
11021
|
+
intel.trim(),
|
|
11022
|
+
"--clone",
|
|
11023
|
+
o.ship.sandbox?.workDir ?? DEFAULT_SANDBOX_WORKDIR,
|
|
11024
|
+
"--state-dir",
|
|
11025
|
+
dirname13(SANDBOX_INTEL_DIR),
|
|
11026
|
+
"--ticket",
|
|
11027
|
+
number,
|
|
11028
|
+
...seed ? ["--seed-cache", seed] : []
|
|
11029
|
+
] : [...process.execArgv, process.argv[1] ?? "crew", "intel", "bridge", o.route.route, number, "--state-dir", o.stateDir];
|
|
11030
|
+
const bridge = { command: sandbox ? "node" : process.execPath, args };
|
|
10933
11031
|
if (harness === "gemini") {
|
|
10934
|
-
const
|
|
10935
|
-
|
|
10936
|
-
|
|
10937
|
-
|
|
10938
|
-
|
|
11032
|
+
const text2 = `${JSON.stringify({ mcpServers: { [INTEL_SERVER_NAME]: { command: bridge.command, args } } }, null, 2)}
|
|
11033
|
+
`;
|
|
11034
|
+
if (sandbox) {
|
|
11035
|
+
bridge.settingsFile = `${SANDBOX_INTEL_DIR}/gemini-settings.json`;
|
|
11036
|
+
bridge.files = [{ path: bridge.settingsFile, content: text2 }];
|
|
11037
|
+
} else {
|
|
11038
|
+
const file = join29(intelDir(o.stateDir, o.route.route, number), "gemini-settings.json");
|
|
11039
|
+
mkdirSync18(dirname13(file), { recursive: true });
|
|
11040
|
+
writeFileSync19(file, text2);
|
|
11041
|
+
bridge.settingsFile = file;
|
|
11042
|
+
}
|
|
10939
11043
|
}
|
|
10940
11044
|
return bridge;
|
|
10941
11045
|
}
|
|
@@ -10996,11 +11100,13 @@ function planAgentRun(o) {
|
|
|
10996
11100
|
}
|
|
10997
11101
|
const adapter = adapterFor(toolchain.harness);
|
|
10998
11102
|
const code = codeBridgeFor(o, adapter.harness);
|
|
11103
|
+
const sandboxLsp = o.ship.runner === "sandbox" && adapter.harness === "claude" ? sandboxLspPluginFor(o.route, o.repo) : void 0;
|
|
11104
|
+
const sandboxFiles = [...code?.files ?? [], ...sandboxLsp?.files ?? []];
|
|
10999
11105
|
const invocation = adapter.invocation(toolchain, {
|
|
11000
11106
|
code,
|
|
11001
11107
|
role: o.role,
|
|
11002
11108
|
model,
|
|
11003
|
-
pluginDirs: adapter.harness
|
|
11109
|
+
pluginDirs: adapter.harness !== "claude" ? void 0 : sandboxLsp ? [sandboxLsp.dir] : lspPluginDirsFor(o.route, o.repo, o.stateDir),
|
|
11004
11110
|
// Skipped when there's no key to send: a route `crew connect` hasn't
|
|
11005
11111
|
// resolved yet has nothing to authenticate the MCP connection with either.
|
|
11006
11112
|
mcp: o.apiKey ? { url: `${o.route.baseUrl}/api/mcp`, keyVar: API_KEY_VAR } : void 0
|
|
@@ -11047,7 +11153,8 @@ function planAgentRun(o) {
|
|
|
11047
11153
|
...o.ship.runner === "sandbox" ? {
|
|
11048
11154
|
runner: "sandbox",
|
|
11049
11155
|
...o.ship.sandbox ? { sandboxConfig: o.ship.sandbox } : {},
|
|
11050
|
-
sandboxPolicy: sandboxPolicyInputFor(o.route, toolchain, { harnessBin: invocation.bin, repo: o.repo, sandbox: o.ship.sandbox })
|
|
11156
|
+
sandboxPolicy: sandboxPolicyInputFor(o.route, toolchain, { harnessBin: invocation.bin, repo: o.repo, sandbox: o.ship.sandbox }),
|
|
11157
|
+
...sandboxFiles.length ? { sandboxFiles } : {}
|
|
11051
11158
|
} : {},
|
|
11052
11159
|
resolution: { toolchain: seat.toolchainWhy, model: seat.modelWhy },
|
|
11053
11160
|
warnings: [...seat.warnings ?? [], ...expanded.warnings],
|
|
@@ -12243,6 +12350,9 @@ function mirrorRelPath(r) {
|
|
|
12243
12350
|
function depsRelDir(r) {
|
|
12244
12351
|
return `cache/deps/${r.host}/${r.path}`;
|
|
12245
12352
|
}
|
|
12353
|
+
function intelRelDir(r) {
|
|
12354
|
+
return `intel/${r.host}/${r.path}`;
|
|
12355
|
+
}
|
|
12246
12356
|
var q = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
12247
12357
|
function cloneScript(o) {
|
|
12248
12358
|
const mirror = `${o.mirrorDir}/${mirrorRelPath(o.repo)}`;
|
|
@@ -12293,6 +12403,30 @@ find . -name node_modules -type d -prune | while read -r p; do
|
|
|
12293
12403
|
done
|
|
12294
12404
|
rm -rf "$droot/$h"; mv "$droot/$h.tmp" "$droot/$h"
|
|
12295
12405
|
`;
|
|
12406
|
+
var INTEL_SCRIPT = `
|
|
12407
|
+
set -e
|
|
12408
|
+
m=${VOLUME_ROOT}/"$1"; iroot=${VOLUME_ROOT}/"$2"; config="$3"; ls="\${CREW_SERENA_LANGUAGE_SERVERS:-}"
|
|
12409
|
+
export GIT_TERMINAL_PROMPT=0
|
|
12410
|
+
git config --global --add safe.directory '*'
|
|
12411
|
+
tip=$(git -C "$m" rev-parse --verify --quiet "HEAD^{commit}") || { echo "the mirror has no default branch" >&2; exit 3; }
|
|
12412
|
+
[ "$(cat "$iroot/indexed-commit" 2>/dev/null)" = "$tip" ] && { echo "unchanged $tip"; exit 0; }
|
|
12413
|
+
w=$(mktemp -d); trap 'rm -rf "$w"' EXIT
|
|
12414
|
+
git clone --quiet --shared "$m" "$w/src"
|
|
12415
|
+
git -C "$w/src" checkout --quiet "$tip"
|
|
12416
|
+
mkdir -p "$w/home" "$w/project"
|
|
12417
|
+
printf '%s' "$config" | sed "s#@PROJECT@#$w/project#" > "$w/home/serena_config.yml"
|
|
12418
|
+
git -C "$w/src" show "$tip:.serena/project.yml" > "$w/project/project.yml" 2>/dev/null || : > "$w/project/project.yml"
|
|
12419
|
+
if grep -q '^read_only' "$w/project/project.yml"; then sed -i 's/^read_only.*/read_only: true/' "$w/project/project.yml"; else echo 'read_only: true' >> "$w/project/project.yml"; fi
|
|
12420
|
+
[ -n "$ls" ] && [ -d "$ls" ] && ln -s "$ls" "$w/home/language_servers"
|
|
12421
|
+
SERENA_HOME="$w/home" serena project index "$w/src"
|
|
12422
|
+
cd "$w/project"; mkdir -p cache
|
|
12423
|
+
mkdir -p "$(dirname "$iroot")"; rm -rf "$iroot.tmp"; mkdir -p "$iroot.tmp"
|
|
12424
|
+
cp -R -P cache "$iroot.tmp/"
|
|
12425
|
+
printf '%s
|
|
12426
|
+
' "$tip" > "$iroot.tmp/indexed-commit"
|
|
12427
|
+
rm -rf "$iroot"; mv "$iroot.tmp" "$iroot"
|
|
12428
|
+
echo "updated $tip"
|
|
12429
|
+
`;
|
|
12296
12430
|
var MATERIALIZE_SCRIPT = `
|
|
12297
12431
|
set -e
|
|
12298
12432
|
export GIT_TERMINAL_PROMPT=0
|
|
@@ -12371,6 +12505,14 @@ var SandboxCache = class {
|
|
|
12371
12505
|
continue;
|
|
12372
12506
|
}
|
|
12373
12507
|
}
|
|
12508
|
+
if (r.intelImage) {
|
|
12509
|
+
try {
|
|
12510
|
+
await this.helper(r.intelImage, INTEL_SCRIPT, [mirrorRelPath(r), intelRelDir(r), serenaConfigYaml("@PROJECT@")]);
|
|
12511
|
+
} catch (e) {
|
|
12512
|
+
out.push({ repo: r, ok: false, detail: `intel index: ${describe(e)}` });
|
|
12513
|
+
continue;
|
|
12514
|
+
}
|
|
12515
|
+
}
|
|
12374
12516
|
out.push({ repo: r, ok: true });
|
|
12375
12517
|
}
|
|
12376
12518
|
return out;
|
|
@@ -12396,13 +12538,13 @@ var SandboxCache = class {
|
|
|
12396
12538
|
} catch {
|
|
12397
12539
|
return [];
|
|
12398
12540
|
}
|
|
12399
|
-
const script = `cd ${VOLUME_ROOT}/mirrors 2>/dev/null || exit 0; find . -name '*.git' -type d -prune | while read -r d; do printf '%s\\t%s\\n' "
|
|
12541
|
+
const script = `cd ${VOLUME_ROOT}/mirrors 2>/dev/null || exit 0; find . -name '*.git' -type d -prune | while read -r d; do r="\${d#./}"; i=${VOLUME_ROOT}/intel/"\${r%.git}"; printf '%s\\t%s\\t%s\\t%s\\n' "$r" "$(cat "$d/crew-last-refresh" 2>/dev/null)" "$(cat "$i/indexed-commit" 2>/dev/null)" "$(stat -c %y "$i/indexed-commit" 2>/dev/null | cut -c1-19)"; done`;
|
|
12400
12542
|
const { stdout } = await this.helper(this.s.helperImage, script, []);
|
|
12401
12543
|
const rows = [];
|
|
12402
12544
|
for (const line of stdout.split("\n").filter(Boolean)) {
|
|
12403
|
-
const [rel, when] = line.split(" ");
|
|
12545
|
+
const [rel, when, commit, indexedAt] = line.split(" ");
|
|
12404
12546
|
const m = /^([^/]+)\/(.+)\.git$/.exec(rel ?? "");
|
|
12405
|
-
if (m) rows.push({ repo: { host: m[1], path: m[2] }, lastRefresh: when?.trim() || null });
|
|
12547
|
+
if (m) rows.push({ repo: { host: m[1], path: m[2] }, lastRefresh: when?.trim() || null, ...commit?.trim() ? { intel: { commit: commit.trim(), indexedAt: indexedAt?.trim() || null } } : {} });
|
|
12406
12548
|
}
|
|
12407
12549
|
return rows;
|
|
12408
12550
|
}
|
|
@@ -12411,7 +12553,7 @@ var SandboxCache = class {
|
|
|
12411
12553
|
const have = new Set(keep.map((r) => `${r.host}/${r.path}`));
|
|
12412
12554
|
const drop = (await this.list()).map((m) => m.repo).filter((r) => !have.has(`${r.host}/${r.path}`));
|
|
12413
12555
|
for (const r of drop) {
|
|
12414
|
-
await this.helper(this.s.helperImage, `rm -rf ${VOLUME_ROOT}/${q(mirrorRelPath(r))} ${VOLUME_ROOT}/${q(depsRelDir(r))}`, []);
|
|
12556
|
+
await this.helper(this.s.helperImage, `rm -rf ${VOLUME_ROOT}/${q(mirrorRelPath(r))} ${VOLUME_ROOT}/${q(depsRelDir(r))} ${VOLUME_ROOT}/${q(intelRelDir(r))}`, []);
|
|
12415
12557
|
}
|
|
12416
12558
|
return drop;
|
|
12417
12559
|
}
|
|
@@ -12686,6 +12828,14 @@ var SANDBOX_CLAUDE_SHA256 = {
|
|
|
12686
12828
|
};
|
|
12687
12829
|
var SANDBOX_UID = 1e3;
|
|
12688
12830
|
var CREW_PREFIX = "/opt/crew";
|
|
12831
|
+
var SANDBOX_BRIDGE_PATH = `${CREW_PREFIX}/bin/intel-bridge`;
|
|
12832
|
+
function readBridgeBundle() {
|
|
12833
|
+
const here = dirname14(fileURLToPath(import.meta.url));
|
|
12834
|
+
for (const p of [join32(here, "sandbox-intel-bridge.cjs"), join32(here, "..", "dist", "sandbox-intel-bridge.cjs")]) {
|
|
12835
|
+
if (existsSync30(p)) return readFileSync27(p, "utf8");
|
|
12836
|
+
}
|
|
12837
|
+
return void 0;
|
|
12838
|
+
}
|
|
12689
12839
|
var SANDBOX_UV_VERSION = "0.13.6";
|
|
12690
12840
|
var SANDBOX_SERENA_VERSION = "2.1.0";
|
|
12691
12841
|
var SERENA_IMAGE_HOME = `${CREW_PREFIX}/serena`;
|
|
@@ -12749,8 +12899,8 @@ function baseLayer() {
|
|
|
12749
12899
|
].join("\n");
|
|
12750
12900
|
return { name: "base", tag: `crew-sandbox-base:${hash({ SANDBOX_BASE_IMAGE, SANDBOX_OPENSHELL_VERSION })}`, dockerfile, files: {} };
|
|
12751
12901
|
}
|
|
12752
|
-
function crewLayer(base, intel) {
|
|
12753
|
-
const files = { "credential-helper": CREDENTIAL_HELPER, "mcp.json": MCP_CONFIG };
|
|
12902
|
+
function crewLayer(base, intel, bridge) {
|
|
12903
|
+
const files = { "credential-helper": CREDENTIAL_HELPER, "mcp.json": MCP_CONFIG, ...bridge ? { "intel-bridge": bridge } : {} };
|
|
12754
12904
|
const env = Object.entries(CREW_ENV).map(([k, v]) => `${k}=${v}`).join(" ");
|
|
12755
12905
|
const dockerfile = [
|
|
12756
12906
|
`FROM ${base.tag}`,
|
|
@@ -12758,6 +12908,7 @@ function crewLayer(base, intel) {
|
|
|
12758
12908
|
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
12909
|
`COPY credential-helper ${CREW_PREFIX}/bin/credential-helper`,
|
|
12760
12910
|
`COPY mcp.json ${CREW_PREFIX}/mcp/mcp.json`,
|
|
12911
|
+
...bridge ? [`COPY intel-bridge ${SANDBOX_BRIDGE_PATH}`] : [],
|
|
12761
12912
|
run2(claudeInstallCommand()),
|
|
12762
12913
|
...intel ? [run2(`mkdir -p ${CREW_PREFIX}/cache && ${serenaInstallCommand()}`)] : [],
|
|
12763
12914
|
run2(`chmod 0755 ${CREW_PREFIX}/bin/credential-helper && git config --system credential.helper ${CREW_PREFIX}/bin/credential-helper`),
|
|
@@ -12822,9 +12973,9 @@ function repoLayer(repo, decl, crew, warm) {
|
|
|
12822
12973
|
const dockerfile = lines2.join("\n");
|
|
12823
12974
|
return { name: "repo", tag: `crew-sandbox/${slug(repo)}:${hash({ crew: crew.tag, dockerfile, files })}`, dockerfile, files };
|
|
12824
12975
|
}
|
|
12825
|
-
function planSandbox(repo, decl, warm = {}) {
|
|
12976
|
+
function planSandbox(repo, decl, warm = {}, bridge) {
|
|
12826
12977
|
const base = baseLayer();
|
|
12827
|
-
const crew = crewLayer(base, decl.intel);
|
|
12978
|
+
const crew = crewLayer(base, decl.intel, bridge);
|
|
12828
12979
|
const layer = repoLayer(repo, decl, crew, warm);
|
|
12829
12980
|
return { repo, tag: layer.tag, claude: SANDBOX_CLAUDE_VERSION, layers: [base, crew, layer] };
|
|
12830
12981
|
}
|
|
@@ -12844,6 +12995,9 @@ function writeSandboxState(stateDir, state) {
|
|
|
12844
12995
|
`);
|
|
12845
12996
|
renameSync3(`${file}.tmp`, file);
|
|
12846
12997
|
}
|
|
12998
|
+
function sandboxImageFor(state, repo) {
|
|
12999
|
+
return state[repo]?.tags[0]?.tag ?? null;
|
|
13000
|
+
}
|
|
12847
13001
|
function sandboxImageStatus(state, plan) {
|
|
12848
13002
|
const s = state[plan.repo];
|
|
12849
13003
|
const current = s?.tags[0];
|
|
@@ -12934,7 +13088,7 @@ function sandboxFailedItem(repo, tag, layer, logPath, nowIso = (/* @__PURE__ */
|
|
|
12934
13088
|
async function sandboxBuildTick(repos, deps, force = false) {
|
|
12935
13089
|
const nowMs = (deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime();
|
|
12936
13090
|
for (const r of repos) {
|
|
12937
|
-
const plan = planSandbox(r.name, r.decl, r.warm);
|
|
13091
|
+
const plan = planSandbox(r.name, r.decl, r.warm, r.bridge);
|
|
12938
13092
|
const state = readSandboxState(deps.stateDir)[r.name];
|
|
12939
13093
|
const upToDate = state?.tags[0]?.tag === plan.tag && deps.hasImage(plan.tag);
|
|
12940
13094
|
if (upToDate) {
|
|
@@ -13093,7 +13247,7 @@ function managedRuntimeWarnings(kind, ship) {
|
|
|
13093
13247
|
|
|
13094
13248
|
// src/selftest.ts
|
|
13095
13249
|
import { spawn as spawn6 } from "child_process";
|
|
13096
|
-
import { existsSync as
|
|
13250
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, readFileSync as readFileSync28, rmSync as rmSync16, writeFileSync as writeFileSync21 } from "fs";
|
|
13097
13251
|
import { tmpdir as tmpdir4 } from "os";
|
|
13098
13252
|
import { join as join33 } from "path";
|
|
13099
13253
|
var SELFTEST_TIMEOUT_MS = 12e4;
|
|
@@ -13293,7 +13447,7 @@ var fileFor2 = (stateDir) => join33(stateDir, "selftest.json");
|
|
|
13293
13447
|
function readSelftest(stateDir) {
|
|
13294
13448
|
try {
|
|
13295
13449
|
const p = fileFor2(stateDir);
|
|
13296
|
-
return
|
|
13450
|
+
return existsSync31(p) ? JSON.parse(readFileSync28(p, "utf8")) : {};
|
|
13297
13451
|
} catch {
|
|
13298
13452
|
return {};
|
|
13299
13453
|
}
|
|
@@ -13315,7 +13469,7 @@ function doctorSelftestLines(stored, toolchains) {
|
|
|
13315
13469
|
|
|
13316
13470
|
// src/cli.ts
|
|
13317
13471
|
import { resolve as resolve7, dirname as dirname33, join as join55, basename as basename16 } from "path";
|
|
13318
|
-
import { fileURLToPath } from "url";
|
|
13472
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
13319
13473
|
import { hostname as hostname4, homedir as homedir11 } from "os";
|
|
13320
13474
|
import { spawnSync as spawnSync11 } from "child_process";
|
|
13321
13475
|
import { parse as parseYaml2 } from "yaml";
|
|
@@ -13619,7 +13773,7 @@ async function signOutSessions(o) {
|
|
|
13619
13773
|
}
|
|
13620
13774
|
|
|
13621
13775
|
// src/reset.ts
|
|
13622
|
-
import { existsSync as
|
|
13776
|
+
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
13777
|
import { dirname as dirname16, join as join35 } from "path";
|
|
13624
13778
|
import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
|
|
13625
13779
|
function clonedCheckoutsFile(stateDir) {
|
|
@@ -13692,7 +13846,7 @@ async function runReset(o, fx) {
|
|
|
13692
13846
|
}
|
|
13693
13847
|
if (o.beforeState) steps.push(...await o.beforeState());
|
|
13694
13848
|
steps.push(...o.wipeState ? wipeStateDir(o.stateDir, o.dryRun) : removeState(o.stateDir, o.dryRun));
|
|
13695
|
-
if (
|
|
13849
|
+
if (existsSync32(o.configFile)) {
|
|
13696
13850
|
if (!o.dryRun) rmSync18(o.configFile, { force: true });
|
|
13697
13851
|
steps.push({ step: "config", target: o.configFile, action: mark(o.dryRun) });
|
|
13698
13852
|
} else {
|
|
@@ -13701,7 +13855,7 @@ async function runReset(o, fx) {
|
|
|
13701
13855
|
return steps;
|
|
13702
13856
|
}
|
|
13703
13857
|
function removeCheckout(dir, dryRun, fx) {
|
|
13704
|
-
if (!
|
|
13858
|
+
if (!existsSync32(dir)) return { step: "checkout", target: dir, action: "absent", detail: "already gone" };
|
|
13705
13859
|
const { dirty, linkedWorktrees } = fx.inspectCheckout(dir);
|
|
13706
13860
|
if (dirty.length) {
|
|
13707
13861
|
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 +13876,7 @@ function removeState(stateDir, dryRun) {
|
|
|
13722
13876
|
} catch {
|
|
13723
13877
|
}
|
|
13724
13878
|
for (const t of targets) {
|
|
13725
|
-
if (!
|
|
13879
|
+
if (!existsSync32(t)) continue;
|
|
13726
13880
|
if (!dryRun) rmSync18(t, { recursive: true, force: true });
|
|
13727
13881
|
out.push({ step: "state", target: t, action: mark(dryRun) });
|
|
13728
13882
|
}
|
|
@@ -14118,13 +14272,13 @@ function hostArchitecture(arch = process.arch) {
|
|
|
14118
14272
|
|
|
14119
14273
|
// src/pool-member-host.ts
|
|
14120
14274
|
import "child_process";
|
|
14121
|
-
import { existsSync as
|
|
14275
|
+
import { existsSync as existsSync36 } from "fs";
|
|
14122
14276
|
import { hostname as hostname3 } from "os";
|
|
14123
14277
|
|
|
14124
14278
|
// src/machine-id.ts
|
|
14125
14279
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
14126
14280
|
import { createHash as createHash7 } from "crypto";
|
|
14127
|
-
import { existsSync as
|
|
14281
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync32, writeFileSync as writeFileSync25 } from "fs";
|
|
14128
14282
|
import { dirname as dirname18, join as join37 } from "path";
|
|
14129
14283
|
function machineIdPathFor(stateDir) {
|
|
14130
14284
|
return join37(stateDir, "machine-id");
|
|
@@ -14140,7 +14294,7 @@ function readRawMachineId(platform2 = process.platform) {
|
|
|
14140
14294
|
return /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(out)?.[1];
|
|
14141
14295
|
}
|
|
14142
14296
|
for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
|
|
14143
|
-
if (
|
|
14297
|
+
if (existsSync33(p)) {
|
|
14144
14298
|
const id = readFileSync32(p, "utf8").trim();
|
|
14145
14299
|
if (id) return id;
|
|
14146
14300
|
}
|
|
@@ -14152,7 +14306,7 @@ function readRawMachineId(platform2 = process.platform) {
|
|
|
14152
14306
|
function ensureMachineId(stateDir, opts = {}) {
|
|
14153
14307
|
if (opts.override?.trim()) return sha256(opts.override.trim());
|
|
14154
14308
|
const path = machineIdPathFor(stateDir);
|
|
14155
|
-
if (
|
|
14309
|
+
if (existsSync33(path)) {
|
|
14156
14310
|
const cached = readFileSync32(path, "utf8").trim();
|
|
14157
14311
|
if (/^[0-9a-f]{64}$/.test(cached)) return cached;
|
|
14158
14312
|
}
|
|
@@ -14173,7 +14327,7 @@ function matchShipRows(ships, shipName2, machineId) {
|
|
|
14173
14327
|
|
|
14174
14328
|
// src/provision.ts
|
|
14175
14329
|
import { spawnSync as spawnSync8 } from "child_process";
|
|
14176
|
-
import { existsSync as
|
|
14330
|
+
import { existsSync as existsSync34, readFileSync as readFileSync33, writeFileSync as writeFileSync26 } from "fs";
|
|
14177
14331
|
import { hostname as hostname2 } from "os";
|
|
14178
14332
|
import { parseDocument as parseDocument3, isMap as isMap2, isScalar } from "yaml";
|
|
14179
14333
|
|
|
@@ -14767,7 +14921,7 @@ function realProvisionDeps(crewHome) {
|
|
|
14767
14921
|
};
|
|
14768
14922
|
const configFile = () => {
|
|
14769
14923
|
const f = findConfigFile(crewHome);
|
|
14770
|
-
return
|
|
14924
|
+
return existsSync34(f) ? f : null;
|
|
14771
14925
|
};
|
|
14772
14926
|
return {
|
|
14773
14927
|
crew: spawnCrew,
|
|
@@ -14801,14 +14955,14 @@ function realProvisionDeps(crewHome) {
|
|
|
14801
14955
|
continue;
|
|
14802
14956
|
}
|
|
14803
14957
|
for (const p of plan.unitPaths) {
|
|
14804
|
-
if (!
|
|
14958
|
+
if (!existsSync34(p) || readFileSync33(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
|
|
14805
14959
|
}
|
|
14806
14960
|
}
|
|
14807
14961
|
const label = labelFor(crewHome, "run");
|
|
14808
14962
|
let running = false;
|
|
14809
14963
|
let lingering = true;
|
|
14810
14964
|
if (mechanism === "systemd") {
|
|
14811
|
-
const system = ctx.root ||
|
|
14965
|
+
const system = ctx.root || existsSync34(systemUnitPathFor(label));
|
|
14812
14966
|
const ctl = system ? ["systemctl"] : ["systemctl", "--user"];
|
|
14813
14967
|
running = spawnSync8(ctl[0], [...ctl.slice(1), "is-active", "--quiet", `${label}.service`]).status === 0;
|
|
14814
14968
|
if (!system) {
|
|
@@ -14853,7 +15007,7 @@ host=${host}
|
|
|
14853
15007
|
|
|
14854
15008
|
// src/tunnel.ts
|
|
14855
15009
|
import { spawn as spawn7 } from "child_process";
|
|
14856
|
-
import { existsSync as
|
|
15010
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync25, readFileSync as readFileSync34, writeFileSync as writeFileSync27, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
|
|
14857
15011
|
import { randomInt } from "crypto";
|
|
14858
15012
|
import { join as join39 } from "path";
|
|
14859
15013
|
var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
|
|
@@ -14931,7 +15085,7 @@ function tunnelStatePath(stateDir, workspaceId) {
|
|
|
14931
15085
|
}
|
|
14932
15086
|
function readPersistedTunnel(stateDir, workspaceId) {
|
|
14933
15087
|
const path = tunnelStatePath(stateDir, workspaceId);
|
|
14934
|
-
if (!
|
|
15088
|
+
if (!existsSync35(path)) return void 0;
|
|
14935
15089
|
try {
|
|
14936
15090
|
return JSON.parse(readFileSync34(path, "utf8"));
|
|
14937
15091
|
} catch {
|
|
@@ -15044,7 +15198,7 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
|
|
|
15044
15198
|
}
|
|
15045
15199
|
function readAllPersistedWorkspaceIds(stateDir) {
|
|
15046
15200
|
const dir = join39(stateDir, "passengers");
|
|
15047
|
-
if (!
|
|
15201
|
+
if (!existsSync35(dir)) return [];
|
|
15048
15202
|
return readdirSync13(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
|
|
15049
15203
|
}
|
|
15050
15204
|
|
|
@@ -15069,7 +15223,7 @@ function realPoolMemberDeps(o) {
|
|
|
15069
15223
|
crewVersion: crewVersion(o.crewHome),
|
|
15070
15224
|
hostname: hostname3()
|
|
15071
15225
|
},
|
|
15072
|
-
hasShipConfig: () =>
|
|
15226
|
+
hasShipConfig: () => existsSync36(findConfigFile(o.crewHome)),
|
|
15073
15227
|
async provision(lease, instanceClass, shipKind) {
|
|
15074
15228
|
const hosts = lease.forges.map((f) => f.host.toLowerCase());
|
|
15075
15229
|
const lines2 = [];
|
|
@@ -16439,7 +16593,7 @@ async function resolveOne(tracker, t, seat, approvedStatus, buildingStatus, affi
|
|
|
16439
16593
|
}
|
|
16440
16594
|
|
|
16441
16595
|
// src/repo-refresh.ts
|
|
16442
|
-
import { existsSync as
|
|
16596
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync26, readFileSync as readFileSync35, writeFileSync as writeFileSync29 } from "fs";
|
|
16443
16597
|
import { dirname as dirname20 } from "path";
|
|
16444
16598
|
var REFRESH_COOLDOWN_MS = 2 * 6e4;
|
|
16445
16599
|
var UNSERVABLE_PASSES_BEFORE_ATTENTION = 3;
|
|
@@ -16469,7 +16623,7 @@ function applyRepoRows(resolved, rows) {
|
|
|
16469
16623
|
}
|
|
16470
16624
|
function persistRepoRows(stateDir, routeName, rows) {
|
|
16471
16625
|
const path = resolvedPathFor(stateDir, routeName);
|
|
16472
|
-
if (!
|
|
16626
|
+
if (!existsSync37(path)) return;
|
|
16473
16627
|
const raw = JSON.parse(readFileSync35(path, "utf8"));
|
|
16474
16628
|
applyRepoRows(raw, rows);
|
|
16475
16629
|
mkdirSync26(dirname20(path), { recursive: true });
|
|
@@ -16924,12 +17078,12 @@ var localRefSkipReported = (ticket, problem, events) => events.some((e) => e.rec
|
|
|
16924
17078
|
|
|
16925
17079
|
// src/connect-wizard.ts
|
|
16926
17080
|
import { delimiter, dirname as dirname21, join as join41 } from "path";
|
|
16927
|
-
import { accessSync as accessSync3, constants as constants3, existsSync as
|
|
17081
|
+
import { accessSync as accessSync3, constants as constants3, existsSync as existsSync38, mkdirSync as mkdirSync27, writeFileSync as writeFileSync30 } from "fs";
|
|
16928
17082
|
import { stringify as stringify3 } from "yaml";
|
|
16929
17083
|
var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
|
|
16930
17084
|
function shouldRunWizard(opts) {
|
|
16931
17085
|
if (!opts.isTTY) return false;
|
|
16932
|
-
const exists = opts.configExists ??
|
|
17086
|
+
const exists = opts.configExists ?? existsSync38;
|
|
16933
17087
|
return !configSearchPath(opts.crewHome).some((p) => exists(p));
|
|
16934
17088
|
}
|
|
16935
17089
|
function firstRunConfigPath() {
|
|
@@ -17123,7 +17277,7 @@ function flagWizardAnswers(f, env) {
|
|
|
17123
17277
|
}
|
|
17124
17278
|
|
|
17125
17279
|
// src/release-run.ts
|
|
17126
|
-
import { readFileSync as readFileSync36, writeFileSync as writeFileSync31, existsSync as
|
|
17280
|
+
import { readFileSync as readFileSync36, writeFileSync as writeFileSync31, existsSync as existsSync39, copyFileSync, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync3, rmSync as rmSync19 } from "fs";
|
|
17127
17281
|
import { dirname as dirname23, join as join43 } from "path";
|
|
17128
17282
|
import { tmpdir as tmpdir5 } from "os";
|
|
17129
17283
|
|
|
@@ -17867,7 +18021,7 @@ async function bump(o, size) {
|
|
|
17867
18021
|
}
|
|
17868
18022
|
const files = o.repo.release.versionFiles.map((f) => join43(o.cwd, f));
|
|
17869
18023
|
const first = files[0];
|
|
17870
|
-
if (!
|
|
18024
|
+
if (!existsSync39(first)) {
|
|
17871
18025
|
o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
|
|
17872
18026
|
return null;
|
|
17873
18027
|
}
|
|
@@ -17903,7 +18057,7 @@ function writeChangelog(o, version, entries) {
|
|
|
17903
18057
|
o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
|
|
17904
18058
|
return;
|
|
17905
18059
|
}
|
|
17906
|
-
const existing =
|
|
18060
|
+
const existing = existsSync39(path) ? readFileSync36(path, "utf8") : "# Changelog\n";
|
|
17907
18061
|
writeFileSync31(path, insertChangelogSection(existing, section));
|
|
17908
18062
|
}
|
|
17909
18063
|
async function confirm(o, expected) {
|
|
@@ -18009,7 +18163,7 @@ function openReleaseCheckout(o) {
|
|
|
18009
18163
|
}
|
|
18010
18164
|
for (const f of o.repo.worktrees.copy) {
|
|
18011
18165
|
const src = join43(o.cwd, f);
|
|
18012
|
-
if (!
|
|
18166
|
+
if (!existsSync39(src)) continue;
|
|
18013
18167
|
mkdirSync28(dirname23(join43(dir, f)), { recursive: true });
|
|
18014
18168
|
copyFileSync(src, join43(dir, f));
|
|
18015
18169
|
}
|
|
@@ -18990,7 +19144,7 @@ function missingReleaserWarning(repo, release, ships, now = Date.now()) {
|
|
|
18990
19144
|
}
|
|
18991
19145
|
|
|
18992
19146
|
// src/watch.ts
|
|
18993
|
-
import { watch as watchFile, existsSync as
|
|
19147
|
+
import { watch as watchFile, existsSync as existsSync40, statSync as statSync11, openSync as openSync5, readSync as readSync2, closeSync as closeSync5 } from "fs";
|
|
18994
19148
|
var E = "\x1B[";
|
|
18995
19149
|
var dim = (s) => `${E}2m${s}${E}0m`;
|
|
18996
19150
|
var bold = (s) => `${E}1m${s}${E}0m`;
|
|
@@ -19091,7 +19245,7 @@ function renderHeaders(states, now = Date.now()) {
|
|
|
19091
19245
|
return states.map((s) => renderHeader(s, now)).join("");
|
|
19092
19246
|
}
|
|
19093
19247
|
function readFrom(file, from) {
|
|
19094
|
-
if (!
|
|
19248
|
+
if (!existsSync40(file)) return { events: [], offset: from };
|
|
19095
19249
|
const size = statSync11(file).size;
|
|
19096
19250
|
if (size < from) return readFrom(file, 0);
|
|
19097
19251
|
if (size === from) return { events: [], offset: from };
|
|
@@ -19334,7 +19488,7 @@ function nextRoles(decision, ran) {
|
|
|
19334
19488
|
}
|
|
19335
19489
|
|
|
19336
19490
|
// src/ship-name.ts
|
|
19337
|
-
import { existsSync as
|
|
19491
|
+
import { existsSync as existsSync41, mkdirSync as mkdirSync29, readFileSync as readFileSync37, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
|
|
19338
19492
|
import { dirname as dirname24 } from "path";
|
|
19339
19493
|
import { parseDocument as parseDocument5 } from "yaml";
|
|
19340
19494
|
var ShipNameError = class extends Error {
|
|
@@ -19396,7 +19550,7 @@ async function syncRouteName(i) {
|
|
|
19396
19550
|
return { status: "ok", ...base, action, name: i.local };
|
|
19397
19551
|
}
|
|
19398
19552
|
function readSyncedName(resolvedFile) {
|
|
19399
|
-
if (!
|
|
19553
|
+
if (!existsSync41(resolvedFile)) return void 0;
|
|
19400
19554
|
try {
|
|
19401
19555
|
const v = JSON.parse(readFileSync37(resolvedFile, "utf8")).shipName;
|
|
19402
19556
|
return typeof v === "string" ? v : void 0;
|
|
@@ -19405,7 +19559,7 @@ function readSyncedName(resolvedFile) {
|
|
|
19405
19559
|
}
|
|
19406
19560
|
}
|
|
19407
19561
|
function writeSyncedName(resolvedFile, name) {
|
|
19408
|
-
if (!
|
|
19562
|
+
if (!existsSync41(resolvedFile)) return;
|
|
19409
19563
|
let raw;
|
|
19410
19564
|
try {
|
|
19411
19565
|
raw = JSON.parse(readFileSync37(resolvedFile, "utf8"));
|
|
@@ -20107,7 +20261,7 @@ function describeCapability(i) {
|
|
|
20107
20261
|
}
|
|
20108
20262
|
|
|
20109
20263
|
// src/agents-sync-run.ts
|
|
20110
|
-
import { existsSync as
|
|
20264
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync30, readFileSync as readFileSync38, writeFileSync as writeFileSync33 } from "fs";
|
|
20111
20265
|
import { dirname as dirname25 } from "path";
|
|
20112
20266
|
async function runAgentsSync(target, o) {
|
|
20113
20267
|
const dryRun = o.dryRun ?? false;
|
|
@@ -20141,7 +20295,7 @@ async function runAgentsSync(target, o) {
|
|
|
20141
20295
|
for (const s of skillResult?.outcomes ?? []) o.say(describeSkillSyncOutcome(s));
|
|
20142
20296
|
if (!dryRun) {
|
|
20143
20297
|
const resolvedPath = resolvedPathFor(o.stateDir, target.route);
|
|
20144
|
-
const raw =
|
|
20298
|
+
const raw = existsSync42(resolvedPath) ? JSON.parse(readFileSync38(resolvedPath, "utf8")) : {};
|
|
20145
20299
|
raw.agentPersonas = personaResult.agentPersonas;
|
|
20146
20300
|
if (skillResult) raw.agentSkills = skillResult.agentSkills;
|
|
20147
20301
|
if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
|
|
@@ -20836,7 +20990,7 @@ function planSampleTicket(i) {
|
|
|
20836
20990
|
}
|
|
20837
20991
|
|
|
20838
20992
|
// src/worktree-sweep.ts
|
|
20839
|
-
import { existsSync as
|
|
20993
|
+
import { existsSync as existsSync43, readFileSync as readFileSync40, readdirSync as readdirSync14 } from "fs";
|
|
20840
20994
|
import { basename as basename13, dirname as dirname27, join as join45, resolve as resolve5 } from "path";
|
|
20841
20995
|
function planWorktreeSweep(target, tickets, contract) {
|
|
20842
20996
|
const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
|
|
@@ -20949,7 +21103,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
|
|
|
20949
21103
|
let removed = 0;
|
|
20950
21104
|
let keptBranches = 0;
|
|
20951
21105
|
for (const a of actions) {
|
|
20952
|
-
if (!dryRun && base && !a.keepBranch &&
|
|
21106
|
+
if (!dryRun && base && !a.keepBranch && existsSync43(a.path) && isHandBackMerge(a.path, base)) {
|
|
20953
21107
|
if (gitOk(a.path, ["merge", "--abort"]) !== null) {
|
|
20954
21108
|
log.emit(
|
|
20955
21109
|
`abandoned the hand-back merge of ${base} in ${a.path}: ${a.ticket.issue_id} deployed without it`,
|
|
@@ -20957,7 +21111,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
|
|
|
20957
21111
|
);
|
|
20958
21112
|
}
|
|
20959
21113
|
}
|
|
20960
|
-
if (!dryRun &&
|
|
21114
|
+
if (!dryRun && existsSync43(a.path)) {
|
|
20961
21115
|
let dirty = false;
|
|
20962
21116
|
try {
|
|
20963
21117
|
dirty = status(a.path).length > 0;
|
|
@@ -20968,7 +21122,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
|
|
|
20968
21122
|
continue;
|
|
20969
21123
|
}
|
|
20970
21124
|
}
|
|
20971
|
-
if (!dryRun && a.requirePushed && a.branch &&
|
|
21125
|
+
if (!dryRun && a.requirePushed && a.branch && existsSync43(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
|
|
20972
21126
|
refuse2(a, "unpushed commits", `${a.branch} has commits that are not on ${remote}`);
|
|
20973
21127
|
continue;
|
|
20974
21128
|
}
|
|
@@ -21122,12 +21276,12 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
|
|
|
21122
21276
|
}
|
|
21123
21277
|
|
|
21124
21278
|
// src/stream-sweep.ts
|
|
21125
|
-
import { existsSync as
|
|
21279
|
+
import { existsSync as existsSync44, readdirSync as readdirSync15, rmSync as rmSync20, statSync as statSync12 } from "fs";
|
|
21126
21280
|
import { join as join46 } from "path";
|
|
21127
21281
|
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
21128
21282
|
function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
|
|
21129
21283
|
const dir = join46(stateDir, "streams");
|
|
21130
|
-
if (!
|
|
21284
|
+
if (!existsSync44(dir)) return [];
|
|
21131
21285
|
const cutoff = now - retentionDays * DAY_MS2;
|
|
21132
21286
|
return readdirSync15(dir).map((name) => join46(dir, name)).filter((path) => {
|
|
21133
21287
|
try {
|
|
@@ -21155,7 +21309,7 @@ function applyStreamSweep(paths, dryRun, log) {
|
|
|
21155
21309
|
return removed;
|
|
21156
21310
|
}
|
|
21157
21311
|
function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
|
|
21158
|
-
if (!
|
|
21312
|
+
if (!existsSync44(stateDir)) return [];
|
|
21159
21313
|
const cutoff = now - retentionDays * DAY_MS2;
|
|
21160
21314
|
return readdirSync15(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join46(stateDir, name)).filter((path) => {
|
|
21161
21315
|
try {
|
|
@@ -21167,11 +21321,11 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
|
|
|
21167
21321
|
}
|
|
21168
21322
|
|
|
21169
21323
|
// src/cli.ts
|
|
21170
|
-
import { realpathSync as realpathSync3, readFileSync as readFileSync46, existsSync as
|
|
21324
|
+
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
21325
|
import { createInterface } from "readline/promises";
|
|
21172
21326
|
|
|
21173
21327
|
// src/periscope-cmd.ts
|
|
21174
|
-
import { existsSync as
|
|
21328
|
+
import { existsSync as existsSync45, mkdirSync as mkdirSync31, readFileSync as readFileSync41, rmSync as rmSync21, writeFileSync as writeFileSync34 } from "fs";
|
|
21175
21329
|
import { join as join47 } from "path";
|
|
21176
21330
|
var PeriscopeError = class extends Error {
|
|
21177
21331
|
};
|
|
@@ -21197,7 +21351,7 @@ function writePeriscopePrior(stateDir, ticketRecordId, prior) {
|
|
|
21197
21351
|
function readPeriscopePrior(stateDir, ticketRecordId) {
|
|
21198
21352
|
try {
|
|
21199
21353
|
const p = priorPath(stateDir, ticketRecordId);
|
|
21200
|
-
return
|
|
21354
|
+
return existsSync45(p) ? JSON.parse(readFileSync41(p, "utf8")) : void 0;
|
|
21201
21355
|
} catch {
|
|
21202
21356
|
return void 0;
|
|
21203
21357
|
}
|
|
@@ -21752,7 +21906,7 @@ import { dirname as dirOf2, resolve as resolvePath } from "path";
|
|
|
21752
21906
|
|
|
21753
21907
|
// src/ssh-keys.ts
|
|
21754
21908
|
import { execFileSync as execFileSync13 } from "child_process";
|
|
21755
|
-
import { existsSync as
|
|
21909
|
+
import { existsSync as existsSync46, mkdirSync as mkdirSync32, readFileSync as readFileSync42, chmodSync as chmodSync3 } from "fs";
|
|
21756
21910
|
import { dirname as dirname28, join as join49 } from "path";
|
|
21757
21911
|
var SshKeyError = class extends Error {
|
|
21758
21912
|
};
|
|
@@ -21762,7 +21916,7 @@ function shipSshKeyPathFor(stateDir) {
|
|
|
21762
21916
|
function ensureShipSshKeypair(stateDir) {
|
|
21763
21917
|
const privateKeyPath = shipSshKeyPathFor(stateDir);
|
|
21764
21918
|
const publicKeyPath = `${privateKeyPath}.pub`;
|
|
21765
|
-
if (!
|
|
21919
|
+
if (!existsSync46(privateKeyPath)) {
|
|
21766
21920
|
mkdirSync32(dirname28(privateKeyPath), { recursive: true });
|
|
21767
21921
|
try {
|
|
21768
21922
|
execFileSync13("ssh-keygen", [
|
|
@@ -21783,7 +21937,7 @@ function ensureShipSshKeypair(stateDir) {
|
|
|
21783
21937
|
}
|
|
21784
21938
|
chmodSync3(privateKeyPath, 384);
|
|
21785
21939
|
}
|
|
21786
|
-
if (!
|
|
21940
|
+
if (!existsSync46(publicKeyPath)) {
|
|
21787
21941
|
throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
|
|
21788
21942
|
}
|
|
21789
21943
|
const publicKey = readFileSync42(publicKeyPath, "utf8").trim();
|
|
@@ -21950,7 +22104,7 @@ async function unregisterEndpoint(workspaceId, info, d) {
|
|
|
21950
22104
|
import { createServer as createHttpServer } from "http";
|
|
21951
22105
|
import { spawn as spawn9 } from "child_process";
|
|
21952
22106
|
import { randomBytes } from "crypto";
|
|
21953
|
-
import { existsSync as
|
|
22107
|
+
import { existsSync as existsSync47, mkdirSync as mkdirSync34, readFileSync as readFileSync43, writeFileSync as writeFileSync35, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
|
|
21954
22108
|
import { join as join50 } from "path";
|
|
21955
22109
|
|
|
21956
22110
|
// src/passenger-mirror.ts
|
|
@@ -22085,7 +22239,7 @@ function syncDaemonStatePath(stateDir, workspaceId) {
|
|
|
22085
22239
|
}
|
|
22086
22240
|
function readPersistedSyncDaemon(stateDir, workspaceId) {
|
|
22087
22241
|
const path = syncDaemonStatePath(stateDir, workspaceId);
|
|
22088
|
-
if (!
|
|
22242
|
+
if (!existsSync47(path)) return void 0;
|
|
22089
22243
|
try {
|
|
22090
22244
|
return JSON.parse(readFileSync43(path, "utf8"));
|
|
22091
22245
|
} catch {
|
|
@@ -22116,7 +22270,7 @@ function waitForExit(pid, alive, timeoutMs = 5e3) {
|
|
|
22116
22270
|
}
|
|
22117
22271
|
function readAllPersistedSyncWorkspaceIds(stateDir) {
|
|
22118
22272
|
const dir = join50(stateDir, "passengers");
|
|
22119
|
-
if (!
|
|
22273
|
+
if (!existsSync47(dir)) return [];
|
|
22120
22274
|
const suffix = "-sync.json";
|
|
22121
22275
|
return readdirSync16(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
|
|
22122
22276
|
}
|
|
@@ -22613,7 +22767,7 @@ function describeCause(err3) {
|
|
|
22613
22767
|
}
|
|
22614
22768
|
|
|
22615
22769
|
// src/daemon.ts
|
|
22616
|
-
import { existsSync as
|
|
22770
|
+
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
22771
|
import { join as join51 } from "path";
|
|
22618
22772
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
22619
22773
|
var DEFAULT_FLOOR_MS = 5e3;
|
|
@@ -22711,7 +22865,7 @@ function computeTreeSignature(paths) {
|
|
|
22711
22865
|
}
|
|
22712
22866
|
function makeStaleChecker(paths, opts = {}) {
|
|
22713
22867
|
const initial = computeTreeSignature(paths);
|
|
22714
|
-
return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 &&
|
|
22868
|
+
return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync48(opts.reloadSentinel);
|
|
22715
22869
|
}
|
|
22716
22870
|
function reloadSentinelFor(stateDir) {
|
|
22717
22871
|
return join51(stateDir, "reload-requested");
|
|
@@ -23300,7 +23454,7 @@ import { createHash as createHash10 } from "crypto";
|
|
|
23300
23454
|
import {
|
|
23301
23455
|
chmodSync as chmodSync4,
|
|
23302
23456
|
closeSync as closeSync6,
|
|
23303
|
-
existsSync as
|
|
23457
|
+
existsSync as existsSync49,
|
|
23304
23458
|
fstatSync,
|
|
23305
23459
|
openSync as openSync6,
|
|
23306
23460
|
readSync as readSync3,
|
|
@@ -23671,7 +23825,7 @@ function lastLines2(path, n) {
|
|
|
23671
23825
|
}
|
|
23672
23826
|
function readTail(live, opts) {
|
|
23673
23827
|
const max = opts.maxBytes ?? MAX_TAIL_BYTES;
|
|
23674
|
-
if (!
|
|
23828
|
+
if (!existsSync49(live)) {
|
|
23675
23829
|
return { lines: [], cursor: opts.after ?? encodeCursor({ dev: 0, ino: 0, off: 0 }), reset: false };
|
|
23676
23830
|
}
|
|
23677
23831
|
const cur = opts.after ? decodeCursor(opts.after) : null;
|
|
@@ -23828,7 +23982,7 @@ async function route(deps, method, url, req, abort) {
|
|
|
23828
23982
|
const run3 = decodeURIComponent(p.slice("/streams/".length));
|
|
23829
23983
|
if (!RUN_NAME_RE.test(run3) || run3.includes("..") || /\.(pid|meta\.json|events\.jsonl)$/.test(run3)) return err2(400, "not a run name");
|
|
23830
23984
|
const file = join52(deps.stateDir, "streams", run3.endsWith(".jsonl") ? run3 : `${run3}.jsonl`);
|
|
23831
|
-
if (!
|
|
23985
|
+
if (!existsSync49(file)) return err2(404, "no such run");
|
|
23832
23986
|
return tailReply(file, url.searchParams, false, abort);
|
|
23833
23987
|
}
|
|
23834
23988
|
if ((p === "/pause" || p === "/resume") && post3) {
|
|
@@ -23872,7 +24026,7 @@ function send(res, r) {
|
|
|
23872
24026
|
res.end(body);
|
|
23873
24027
|
}
|
|
23874
24028
|
async function socketIsStale(path) {
|
|
23875
|
-
if (!
|
|
24029
|
+
if (!existsSync49(path)) return false;
|
|
23876
24030
|
return new Promise((resolve8) => {
|
|
23877
24031
|
const c = createConnection(path);
|
|
23878
24032
|
c.once("connect", () => {
|
|
@@ -23891,7 +24045,7 @@ async function startControlServer(deps) {
|
|
|
23891
24045
|
return null;
|
|
23892
24046
|
}
|
|
23893
24047
|
if (await socketIsStale(path)) rmSync23(path, { force: true });
|
|
23894
|
-
else if (
|
|
24048
|
+
else if (existsSync49(path)) {
|
|
23895
24049
|
log(`control API not started: ${path} is in use by another process`);
|
|
23896
24050
|
return null;
|
|
23897
24051
|
}
|
|
@@ -23984,7 +24138,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
|
|
|
23984
24138
|
|
|
23985
24139
|
// src/periscope-endpoints.ts
|
|
23986
24140
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
23987
|
-
import { chmodSync as chmodSync5, existsSync as
|
|
24141
|
+
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
24142
|
import { join as join53 } from "path";
|
|
23989
24143
|
var secretDir = (stateDir) => join53(stateDir, "periscope-mcp");
|
|
23990
24144
|
var secretFile = (stateDir, id) => join53(secretDir(stateDir), `${id}.json`);
|
|
@@ -24027,7 +24181,7 @@ function removePeriscopeEndpointRecord(stateDir, ticketRecordId) {
|
|
|
24027
24181
|
}
|
|
24028
24182
|
function listPeriscopeEndpoints(stateDir) {
|
|
24029
24183
|
const dir = endpointDir(stateDir);
|
|
24030
|
-
if (!
|
|
24184
|
+
if (!existsSync50(dir)) return [];
|
|
24031
24185
|
const out = [];
|
|
24032
24186
|
for (const name of readdirSync19(dir)) {
|
|
24033
24187
|
if (!name.endsWith(".json")) continue;
|
|
@@ -24391,7 +24545,7 @@ function findPeriscope(sidecars, segment) {
|
|
|
24391
24545
|
}
|
|
24392
24546
|
|
|
24393
24547
|
// src/cli.ts
|
|
24394
|
-
var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname33(process.execPath) : resolve7(dirname33(
|
|
24548
|
+
var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname33(process.execPath) : resolve7(dirname33(fileURLToPath2(import.meta.url)), "..");
|
|
24395
24549
|
var RELEASE_LOCK_TTL_MS = 20 * 60 * 1e3;
|
|
24396
24550
|
var CYCLE_SECONDS2 = 120;
|
|
24397
24551
|
var STALE_CYCLES = 3;
|
|
@@ -24907,6 +25061,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
24907
25061
|
return { action: out.action, unsafe, detail: unsafe ? describeUnsafeBase(out, remote, base) : out.detail };
|
|
24908
25062
|
}, sandboxCacheRepos = function(c, ship) {
|
|
24909
25063
|
const out = [];
|
|
25064
|
+
let images;
|
|
24910
25065
|
for (const t of reposOf(c)) {
|
|
24911
25066
|
const origin = originUrlOf(t.dir, "origin");
|
|
24912
25067
|
const repo = origin ? parseRemote(origin) : null;
|
|
@@ -24916,7 +25071,12 @@ if (command === "passenger-sync-daemon") {
|
|
|
24916
25071
|
token = readForgeToken(ship.forges, repo.host, ship.stateDir);
|
|
24917
25072
|
} catch {
|
|
24918
25073
|
}
|
|
24919
|
-
|
|
25074
|
+
let intelImage = null;
|
|
25075
|
+
try {
|
|
25076
|
+
if (intelFlavor(loadRepoConfig(t.dir)?.hooks.intel) === "serena") intelImage = sandboxImageFor(images ??= readSandboxState(ship.stateDir), t.name);
|
|
25077
|
+
} catch {
|
|
25078
|
+
}
|
|
25079
|
+
out.push({ ...repo, dir: t.dir, token, username: forgeFor(ship.forges, repo.host)?.entry.username, intelImage });
|
|
24920
25080
|
}
|
|
24921
25081
|
return out;
|
|
24922
25082
|
}, buildSandboxCachePass = function(c, ship, state2, emit2) {
|
|
@@ -25004,7 +25164,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
25004
25164
|
} else {
|
|
25005
25165
|
const toleratesNoRoutes = command === "credential" || command === "forge" || command === "repos" || command === "ship" || command === "disconnect" && flag("reset") || command === "deprovision" || command === "connect" && flag("sign-out");
|
|
25006
25166
|
const configless = command === "connect" && flag("sign-out") || command === "disconnect" && flag("reset") || command === "deprovision";
|
|
25007
|
-
if (configless && !
|
|
25167
|
+
if (configless && !existsSync51(findConfigFile(CREW_HOME))) {
|
|
25008
25168
|
cfg = loadConfig(CREW_HOME, firstRunConfigPath(), { text: "ship: {}\n", allowNoRoutes: true });
|
|
25009
25169
|
} else {
|
|
25010
25170
|
cfg = loadConfig(CREW_HOME, void 0, toleratesNoRoutes ? { allowNoRoutes: true } : {});
|
|
@@ -25327,7 +25487,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
25327
25487
|
if (host === "linux" && hasSystemd) {
|
|
25328
25488
|
const label = labelFor(CREW_HOME, "run");
|
|
25329
25489
|
const staleTimer = join55(homedir11(), ".config", "systemd", "user", `${label}.timer`);
|
|
25330
|
-
if (
|
|
25490
|
+
if (existsSync51(staleTimer)) {
|
|
25331
25491
|
process.stdout.write(
|
|
25332
25492
|
`removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
|
|
25333
25493
|
`
|
|
@@ -25354,7 +25514,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
25354
25514
|
const tracker = new Tracker(c, cfg.ship);
|
|
25355
25515
|
problems.push(...validateContract(tracker.contract).map((p) => `contract: ${p}`));
|
|
25356
25516
|
for (const r of await resolvedRepos(c)) {
|
|
25357
|
-
if (!
|
|
25517
|
+
if (!existsSync51(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
|
|
25358
25518
|
if (!satisfies(host, r.config.platform)) problems.push(`repo ${r.name}: requires ${r.config.platform}, this host is ${host}`);
|
|
25359
25519
|
readiness.push(...validateEffective(r.config).map((p) => `repo ${r.name}: ${p}`));
|
|
25360
25520
|
}
|
|
@@ -25381,7 +25541,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
25381
25541
|
}
|
|
25382
25542
|
async function doctorFix(o) {
|
|
25383
25543
|
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) =>
|
|
25544
|
+
const unitExists = (p) => existsSync51(p);
|
|
25385
25545
|
const crontab = o.host === "linux" ? spawnSync11("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
|
|
25386
25546
|
const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run", installContext()), unitExists, crontab) : true;
|
|
25387
25547
|
const plan = planFix({ routes, schedulerAbsent, host: o.host });
|
|
@@ -25427,7 +25587,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
25427
25587
|
warm = readWarmFiles(r.dir, `${r.config.branch.remote}/${r.config.branch.base}`, r.config.sandbox.warm ?? []);
|
|
25428
25588
|
} catch {
|
|
25429
25589
|
}
|
|
25430
|
-
out.set(r.name, { name: r.name, decl: intelFlavor(r.config.hooks.intel) === "serena" ? { ...r.config.sandbox, intel: "serena" } : r.config.sandbox, warm });
|
|
25590
|
+
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
25591
|
}
|
|
25432
25592
|
}
|
|
25433
25593
|
return [...out.values()];
|
|
@@ -26842,7 +27002,7 @@ ${cu.tail.trimEnd()}
|
|
|
26842
27002
|
`);
|
|
26843
27003
|
if (!dryRun) {
|
|
26844
27004
|
const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
|
|
26845
|
-
const raw =
|
|
27005
|
+
const raw = existsSync51(resolvedPath) ? JSON.parse(readFileSync46(resolvedPath, "utf8")) : {};
|
|
26846
27006
|
raw.agentSkills = result.agentSkills;
|
|
26847
27007
|
mkdirSync38(dirOf2(resolvedPath), { recursive: true });
|
|
26848
27008
|
writeFileSync38(resolvedPath, `${JSON.stringify(raw, null, 2)}
|
|
@@ -26947,8 +27107,8 @@ ${cu.tail.trimEnd()}
|
|
|
26947
27107
|
try {
|
|
26948
27108
|
const a = parseHooksArgs(positional.slice(2), routeNamesOf(configText));
|
|
26949
27109
|
const dir = repoDirOf(configText, dirname33(cfg.configFile), a.route, a.name);
|
|
26950
|
-
if (!
|
|
26951
|
-
const files = { exists: (r) =>
|
|
27110
|
+
if (!existsSync51(dir)) throw new ReposError(`${dir} does not exist on disk`);
|
|
27111
|
+
const files = { exists: (r) => existsSync51(resolvePath(dir, r)), list: (r) => {
|
|
26952
27112
|
try {
|
|
26953
27113
|
return readdirSync20(resolvePath(dir, r));
|
|
26954
27114
|
} catch {
|
|
@@ -27066,7 +27226,7 @@ ${cu.tail.trimEnd()}
|
|
|
27066
27226
|
if (sub === "list" && !routeName) usage2();
|
|
27067
27227
|
if (sub === "add") {
|
|
27068
27228
|
try {
|
|
27069
|
-
const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) =>
|
|
27229
|
+
const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync51(resolvePath(p.replace(/^~(?=\/|$)/, homedir11()))));
|
|
27070
27230
|
routeName = a.route;
|
|
27071
27231
|
addPath = a.path;
|
|
27072
27232
|
addName = a.name;
|
|
@@ -27096,7 +27256,7 @@ ${cu.tail.trimEnd()}
|
|
|
27096
27256
|
const resolvedFile = resolvedPathFor(cfg.ship.stateDir, routeName);
|
|
27097
27257
|
if (sub === "list" || sub === "add") {
|
|
27098
27258
|
const liveRoute = cfg.routes.find((r) => r.route === routeName);
|
|
27099
|
-
if (liveRoute?.resolved?.reposModelId &&
|
|
27259
|
+
if (liveRoute?.resolved?.reposModelId && existsSync51(resolvedFile) && !dryRun) {
|
|
27100
27260
|
try {
|
|
27101
27261
|
await hydrateApiKeys([liveRoute]);
|
|
27102
27262
|
persistRepoRows(cfg.ship.stateDir, liveRoute.route, await new Tracker(liveRoute, cfg.ship).repoRowsForMap());
|
|
@@ -27105,7 +27265,7 @@ ${cu.tail.trimEnd()}
|
|
|
27105
27265
|
}
|
|
27106
27266
|
}
|
|
27107
27267
|
let resolvedRemotes;
|
|
27108
|
-
if (
|
|
27268
|
+
if (existsSync51(resolvedFile)) {
|
|
27109
27269
|
try {
|
|
27110
27270
|
resolvedRemotes = JSON.parse(readFileSync46(resolvedFile, "utf8"));
|
|
27111
27271
|
} catch {
|
|
@@ -27114,7 +27274,7 @@ ${cu.tail.trimEnd()}
|
|
|
27114
27274
|
const originOf = (dir) => gitOk(dir, ["remote", "get-url", "origin"]) ?? void 0;
|
|
27115
27275
|
const text2 = configText;
|
|
27116
27276
|
const base = dirname33(cfg.configFile);
|
|
27117
|
-
const isCheckout = (d) =>
|
|
27277
|
+
const isCheckout = (d) => existsSync51(`${d}/.git`);
|
|
27118
27278
|
try {
|
|
27119
27279
|
if (sub === "list") {
|
|
27120
27280
|
if (flag("json")) {
|
|
@@ -27124,7 +27284,7 @@ ${cu.tail.trimEnd()}
|
|
|
27124
27284
|
base,
|
|
27125
27285
|
resolved: resolvedRemotes,
|
|
27126
27286
|
originOf,
|
|
27127
|
-
exists: (d) =>
|
|
27287
|
+
exists: (d) => existsSync51(d),
|
|
27128
27288
|
isCheckout,
|
|
27129
27289
|
repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
|
|
27130
27290
|
try {
|
|
@@ -27145,7 +27305,7 @@ ${cu.tail.trimEnd()}
|
|
|
27145
27305
|
for (const r of repos) {
|
|
27146
27306
|
const origin2 = originOf(r.dir);
|
|
27147
27307
|
const row = matchRepoName(origin2, resolvedRemotes);
|
|
27148
|
-
const note = !
|
|
27308
|
+
const note = !existsSync51(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
|
|
27149
27309
|
process.stdout.write(`${r.name} ${r.dir} ${note}
|
|
27150
27310
|
`);
|
|
27151
27311
|
}
|
|
@@ -27157,9 +27317,9 @@ ${cu.tail.trimEnd()}
|
|
|
27157
27317
|
` : `${human}
|
|
27158
27318
|
`);
|
|
27159
27319
|
const plan2 = planRepoClone({ text: text2, base, route: routeName, name: addName, resolved: resolvedRemotes, dir: value("dir"), isCheckout });
|
|
27160
|
-
if (
|
|
27320
|
+
if (existsSync51(plan2.target) && (existsSync51(`${plan2.target}/.git`) || readdirSync20(plan2.target).length > 0)) {
|
|
27161
27321
|
throw new ReposError(
|
|
27162
|
-
|
|
27322
|
+
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
27323
|
);
|
|
27164
27324
|
}
|
|
27165
27325
|
if (dryRun) {
|
|
@@ -27184,7 +27344,7 @@ ${cu.tail.trimEnd()}
|
|
|
27184
27344
|
break;
|
|
27185
27345
|
}
|
|
27186
27346
|
const target = resolvePath(addPath.replace(/^~(?=\/|$)/, homedir11()));
|
|
27187
|
-
const top =
|
|
27347
|
+
const top = existsSync51(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
|
|
27188
27348
|
if (!top) throw new ReposError(`${target} is not a git checkout`);
|
|
27189
27349
|
const origin = originOf(top);
|
|
27190
27350
|
const plan = planRepoAdd({
|
|
@@ -27236,7 +27396,7 @@ ${cu.tail.trimEnd()}
|
|
|
27236
27396
|
const created = await rowTracker.createRepoRow(rowPayload);
|
|
27237
27397
|
process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
|
|
27238
27398
|
`);
|
|
27239
|
-
if (
|
|
27399
|
+
if (existsSync51(resolvedFile)) {
|
|
27240
27400
|
const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
|
|
27241
27401
|
raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
|
|
27242
27402
|
raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
|
|
@@ -27658,7 +27818,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
27658
27818
|
}
|
|
27659
27819
|
}
|
|
27660
27820
|
let previous;
|
|
27661
|
-
const hadPreviousFile =
|
|
27821
|
+
const hadPreviousFile = existsSync51(resolvedPath);
|
|
27662
27822
|
if (hadPreviousFile) {
|
|
27663
27823
|
try {
|
|
27664
27824
|
previous = JSON.parse(readFileSync46(resolvedPath, "utf8"));
|
|
@@ -28190,7 +28350,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
28190
28350
|
if (subS === "images") {
|
|
28191
28351
|
const stateS = readSandboxState(cfg.ship.stateDir);
|
|
28192
28352
|
const rows = declared.map((r) => {
|
|
28193
|
-
const plan = planSandbox(r.name, r.decl, r.warm);
|
|
28353
|
+
const plan = planSandbox(r.name, r.decl, r.warm, r.bridge);
|
|
28194
28354
|
return { repo: r.name, wants: plan.tag, tags: stateS[r.name]?.tags ?? [], ...sandboxImageStatus(stateS, plan) };
|
|
28195
28355
|
});
|
|
28196
28356
|
if (jsonS) {
|
|
@@ -28224,7 +28384,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
28224
28384
|
for (const t of targets) {
|
|
28225
28385
|
await sandboxBuildTick([t], depsS, true);
|
|
28226
28386
|
const after = readSandboxState(cfg.ship.stateDir);
|
|
28227
|
-
const st = sandboxImageStatus(after, planSandbox(t.name, t.decl, t.warm));
|
|
28387
|
+
const st = sandboxImageStatus(after, planSandbox(t.name, t.decl, t.warm, t.bridge));
|
|
28228
28388
|
if (st.status === "failed") failed = true;
|
|
28229
28389
|
process.stdout.write(`${t.name}: ${st.status} \u2014 ${st.detail}
|
|
28230
28390
|
`);
|
|
@@ -28257,8 +28417,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
28257
28417
|
`);
|
|
28258
28418
|
if (mirrors.length === 0) process.stdout.write(`volume ${settings.volume}: no mirrors yet
|
|
28259
28419
|
`);
|
|
28260
|
-
for (const m of mirrors)
|
|
28420
|
+
for (const m of mirrors) {
|
|
28421
|
+
process.stdout.write(`${m.repo.host}/${m.repo.path} last refresh ${m.lastRefresh ?? "never"}${m.intel ? ` intel ${m.intel.commit.slice(0, 12)} (${m.intel.indexedAt ?? "age unknown"})` : ""}
|
|
28261
28422
|
`);
|
|
28423
|
+
}
|
|
28262
28424
|
}
|
|
28263
28425
|
} catch (e) {
|
|
28264
28426
|
process.stderr.write(`crew sandbox cache: ${e instanceof Error ? e.message : String(e)}
|
|
@@ -28536,7 +28698,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
28536
28698
|
prepare: async (path) => {
|
|
28537
28699
|
for (const f of repoWt.config.worktrees.copy) {
|
|
28538
28700
|
const src = join55(repoWt.dir, f);
|
|
28539
|
-
if (!
|
|
28701
|
+
if (!existsSync51(src) || existsSync51(join55(path, f))) continue;
|
|
28540
28702
|
mkdirSync38(dirOf2(join55(path, f)), { recursive: true });
|
|
28541
28703
|
copyFileSync2(src, join55(path, f));
|
|
28542
28704
|
}
|
|
@@ -28758,7 +28920,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
28758
28920
|
const shellR = hit && resolveShell(hit.repo.config.shell ?? cfg.ship.shell);
|
|
28759
28921
|
const resumed = await runPeriscopeResume({
|
|
28760
28922
|
find: async () => hit && { row: hit.row, tag: hit.tag },
|
|
28761
|
-
worktreeExists: (p) =>
|
|
28923
|
+
worktreeExists: (p) => existsSync51(p),
|
|
28762
28924
|
prepareDropFile,
|
|
28763
28925
|
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
28926
|
caps: periscopeCaps(cfg.ship),
|
|
@@ -28855,7 +29017,7 @@ ${where.join("\n")}`);
|
|
|
28855
29017
|
prepare: async (path) => {
|
|
28856
29018
|
for (const f of repoP.config.worktrees.copy) {
|
|
28857
29019
|
const src = join55(repoP.dir, f);
|
|
28858
|
-
if (!
|
|
29020
|
+
if (!existsSync51(src)) continue;
|
|
28859
29021
|
mkdirSync38(dirOf2(join55(path, f)), { recursive: true });
|
|
28860
29022
|
copyFileSync2(src, join55(path, f));
|
|
28861
29023
|
}
|
|
@@ -29347,7 +29509,7 @@ ${where.join("\n")}`);
|
|
|
29347
29509
|
}
|
|
29348
29510
|
if (row) {
|
|
29349
29511
|
await tracker.deleteRepoRow(row.id);
|
|
29350
|
-
if (
|
|
29512
|
+
if (existsSync51(resolvedFile)) {
|
|
29351
29513
|
const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
|
|
29352
29514
|
if (raw.repoNames) delete raw.repoNames[row.id];
|
|
29353
29515
|
if (raw.repoRemotes) delete raw.repoRemotes[row.id];
|
|
@@ -29355,13 +29517,13 @@ ${where.join("\n")}`);
|
|
|
29355
29517
|
`);
|
|
29356
29518
|
}
|
|
29357
29519
|
}
|
|
29358
|
-
if (
|
|
29520
|
+
if (existsSync51(join55(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync25(dir, { recursive: true, force: true });
|
|
29359
29521
|
say("removed", { dir, closed }, `removed the sample repo${closed.length ? `, closed ${closed.join(", ")}` : ""}`);
|
|
29360
29522
|
break;
|
|
29361
29523
|
}
|
|
29362
29524
|
const projectId = route2.resolved.areaId ?? resolveRepoProject(await tracker.projectRows(), value("project"));
|
|
29363
|
-
const fresh = !
|
|
29364
|
-
if (
|
|
29525
|
+
const fresh = !existsSync51(join55(dir, ".git"));
|
|
29526
|
+
if (existsSync51(dir) && fresh && readdirSync20(dir).length > 0) fail2(`${dir} exists and is not a git checkout \u2014 move it aside`);
|
|
29365
29527
|
if (dryRun) {
|
|
29366
29528
|
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
29529
|
break;
|
|
@@ -29387,7 +29549,7 @@ ${where.join("\n")}`);
|
|
|
29387
29549
|
spec: { platform: "unix", releaseMode: "integrate", ciProvider: "none" }
|
|
29388
29550
|
});
|
|
29389
29551
|
repoId = (await tracker.createRepoRow(payload)).id;
|
|
29390
|
-
if (
|
|
29552
|
+
if (existsSync51(resolvedFile)) {
|
|
29391
29553
|
const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
|
|
29392
29554
|
raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
|
|
29393
29555
|
raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
|
|
@@ -29743,7 +29905,7 @@ watermark: ${state.watermark()}
|
|
|
29743
29905
|
}
|
|
29744
29906
|
for (const r of repos) {
|
|
29745
29907
|
const remote = r.config.branch.remote;
|
|
29746
|
-
if (
|
|
29908
|
+
if (existsSync51(r.dir)) {
|
|
29747
29909
|
pushAuth.push({
|
|
29748
29910
|
route: c.route,
|
|
29749
29911
|
repo: r.name,
|
|
@@ -29839,7 +30001,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
|
|
|
29839
30001
|
}
|
|
29840
30002
|
try {
|
|
29841
30003
|
const evFile = eventFileFor(cfg.ship.stateDir);
|
|
29842
|
-
const evSize =
|
|
30004
|
+
const evSize = existsSync51(evFile) ? statSync16(evFile).size : 0;
|
|
29843
30005
|
process.stdout.write(
|
|
29844
30006
|
evSize > cfg.ship.eventsRotateBytes * 4 ? `events log: WARN ${evFile} is ${Math.round(evSize / 1048576)} MB (> 4x eventsRotateBytes); run \`crew reap\` to rotate it
|
|
29845
30007
|
` : "events log: ok\n"
|
|
@@ -30303,7 +30465,7 @@ ${foreign.join("\n")}
|
|
|
30303
30465
|
const declaredS = await sandboxReposOf(cfg.routes.filter((r) => r.enabled));
|
|
30304
30466
|
const stateS = readSandboxState(cfg.ship.stateDir);
|
|
30305
30467
|
for (const r of declaredS) {
|
|
30306
|
-
const st = sandboxImageStatus(stateS, planSandbox(r.name, r.decl, r.warm));
|
|
30468
|
+
const st = sandboxImageStatus(stateS, planSandbox(r.name, r.decl, r.warm, r.bridge));
|
|
30307
30469
|
process.stdout.write(`sandbox image: ${r.name}: ${st.status.toUpperCase()} \u2014 ${st.detail}
|
|
30308
30470
|
`);
|
|
30309
30471
|
}
|