@tablation/crew 0.47.0 → 0.47.2
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 +122 -74
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { resolve as resolve4, dirname as dirname12, join as
|
|
4
|
+
import { resolve as resolve4, dirname as dirname12, join as join24, basename as basename9 } from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
import { hostname as hostname2, homedir as homedir8 } from "os";
|
|
7
7
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
@@ -3482,7 +3482,7 @@ function describeRelease(o, route) {
|
|
|
3482
3482
|
level: "fail",
|
|
3483
3483
|
headline: `${route}: deploy FAILED`,
|
|
3484
3484
|
detail: withHookTail(
|
|
3485
|
-
"the version is merged to the base branch but not live \u2014 run `crew deploy` to retry
|
|
3485
|
+
"the version is merged to the base branch but not live \u2014 if the deploy target was the problem, run `crew deploy` to retry (do not re-release); if the merged code itself failed to build, fix forward on a ticket branch, and merging it clears the not-retrying guard",
|
|
3486
3486
|
o.hookFailure
|
|
3487
3487
|
)
|
|
3488
3488
|
};
|
|
@@ -4030,9 +4030,11 @@ import {
|
|
|
4030
4030
|
statSync as statSync4,
|
|
4031
4031
|
unlinkSync as unlinkSync2,
|
|
4032
4032
|
readFileSync as readFileSync9,
|
|
4033
|
-
writeFileSync as writeFileSync6
|
|
4033
|
+
writeFileSync as writeFileSync6,
|
|
4034
|
+
readdirSync as readdirSync3,
|
|
4035
|
+
rmSync as rmSync6
|
|
4034
4036
|
} from "fs";
|
|
4035
|
-
import { dirname as dirname5, basename as basename4 } from "path";
|
|
4037
|
+
import { dirname as dirname5, basename as basename4, join as join10 } from "path";
|
|
4036
4038
|
var GitError = class extends Error {
|
|
4037
4039
|
};
|
|
4038
4040
|
function streamText(x) {
|
|
@@ -4367,12 +4369,36 @@ function recoverStrandedReleaseCheckouts(cwd) {
|
|
|
4367
4369
|
if (line.startsWith("worktree ")) path = line.slice("worktree ".length);
|
|
4368
4370
|
else if (line.startsWith("branch refs/heads/crew/release-") && path) {
|
|
4369
4371
|
gitOk(cwd, ["worktree", "remove", "--force", path]);
|
|
4372
|
+
const root = dirname5(path);
|
|
4373
|
+
if (basename4(root).startsWith("crew-release-")) rmSync6(root, { recursive: true, force: true });
|
|
4370
4374
|
}
|
|
4371
4375
|
}
|
|
4372
4376
|
const stranded = (gitOk(cwd, ["for-each-ref", "--format=%(refname:short)", "refs/heads/crew/release-*"]) ?? "").split("\n").filter(Boolean);
|
|
4373
4377
|
for (const b of stranded) gitOk(cwd, ["branch", "-q", "-D", b]);
|
|
4374
4378
|
return stranded;
|
|
4375
4379
|
}
|
|
4380
|
+
function sweepStaleReleaseRoots(tmp, now = Date.now(), maxAgeMs = 60 * 60 * 1e3) {
|
|
4381
|
+
const removed = [];
|
|
4382
|
+
let names;
|
|
4383
|
+
try {
|
|
4384
|
+
names = readdirSync3(tmp);
|
|
4385
|
+
} catch {
|
|
4386
|
+
return removed;
|
|
4387
|
+
}
|
|
4388
|
+
for (const name of names) {
|
|
4389
|
+
if (!name.startsWith("crew-release-")) continue;
|
|
4390
|
+
const root = join10(tmp, name);
|
|
4391
|
+
try {
|
|
4392
|
+
const st = statSync4(root);
|
|
4393
|
+
if (!st.isDirectory() || now - st.mtimeMs < maxAgeMs) continue;
|
|
4394
|
+
if (existsSync9(join10(root, "checkout"))) continue;
|
|
4395
|
+
rmSync6(root, { recursive: true, force: true });
|
|
4396
|
+
removed.push(name);
|
|
4397
|
+
} catch {
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
return removed;
|
|
4401
|
+
}
|
|
4376
4402
|
function pushBranch(cwd, remote, branch, dst = branch) {
|
|
4377
4403
|
git(cwd, ["push", remote, `${branch}:${dst}`]);
|
|
4378
4404
|
}
|
|
@@ -6268,7 +6294,7 @@ function materializeBranchForTicket(dir, cfg, t, role) {
|
|
|
6268
6294
|
|
|
6269
6295
|
// src/poll.ts
|
|
6270
6296
|
import { writeFileSync as writeFileSync7 } from "fs";
|
|
6271
|
-
import { join as
|
|
6297
|
+
import { join as join11 } from "path";
|
|
6272
6298
|
|
|
6273
6299
|
// src/stalled.ts
|
|
6274
6300
|
var STALLED_AFTER_CYCLES = 30;
|
|
@@ -6545,7 +6571,7 @@ async function decideCycle(o) {
|
|
|
6545
6571
|
}
|
|
6546
6572
|
return decision;
|
|
6547
6573
|
}
|
|
6548
|
-
var digestPath = (stateDir, route, role) =>
|
|
6574
|
+
var digestPath = (stateDir, route, role) => join11(stateDir, `digest-${route}-${role}.md`);
|
|
6549
6575
|
function writeDigest(o, d, role, stateDir) {
|
|
6550
6576
|
const me = o.route.resolved?.seats[role];
|
|
6551
6577
|
if (!me) return false;
|
|
@@ -6993,7 +7019,7 @@ var alreadyReported = (ticket, g, events) => events.some((e) => e.record_id ===
|
|
|
6993
7019
|
import { spawn as spawn2 } from "child_process";
|
|
6994
7020
|
import { readFileSync as readFileSync12, existsSync as existsSync12, statSync as statSync5, mkdirSync as mkdirSync6, writeFileSync as writeFileSync8, createWriteStream } from "fs";
|
|
6995
7021
|
import { finished } from "stream/promises";
|
|
6996
|
-
import { join as
|
|
7022
|
+
import { join as join14, dirname as dirname6 } from "path";
|
|
6997
7023
|
import { createHash as createHash2 } from "crypto";
|
|
6998
7024
|
|
|
6999
7025
|
// src/environment.ts
|
|
@@ -7408,14 +7434,14 @@ function expandSkillMarkers(text, lookup, opts = {}) {
|
|
|
7408
7434
|
}
|
|
7409
7435
|
|
|
7410
7436
|
// src/skills.ts
|
|
7411
|
-
import { readFileSync as readFileSync11, readdirSync as
|
|
7412
|
-
import { join as
|
|
7437
|
+
import { readFileSync as readFileSync11, readdirSync as readdirSync4, existsSync as existsSync11 } from "fs";
|
|
7438
|
+
import { join as join13 } from "path";
|
|
7413
7439
|
import { parse as parseYaml } from "yaml";
|
|
7414
7440
|
import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError8, TablationApiError as TablationApiError2 } from "@tablation/client";
|
|
7415
7441
|
|
|
7416
7442
|
// src/agents.ts
|
|
7417
7443
|
import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
|
|
7418
|
-
import { join as
|
|
7444
|
+
import { join as join12 } from "path";
|
|
7419
7445
|
import { TablationClient as TablationClient2, StaleWriteError as StaleWriteError7, TablationApiError } from "@tablation/client";
|
|
7420
7446
|
var PERSONA_NAME = {
|
|
7421
7447
|
dev: "Developer",
|
|
@@ -7429,10 +7455,10 @@ var CREW_AGENT_COLUMN = "agent_id";
|
|
|
7429
7455
|
var AgentsSyncError = class extends Error {
|
|
7430
7456
|
};
|
|
7431
7457
|
function personaDefaultPrompt(promptsDir, role) {
|
|
7432
|
-
const briefPath =
|
|
7458
|
+
const briefPath = join12(promptsDir, "personas", `lane-${role}.md`);
|
|
7433
7459
|
if (!existsSync10(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
|
|
7434
7460
|
if (SELF_CONTAINED_ROLES.has(role)) return readFileSync10(briefPath, "utf8");
|
|
7435
|
-
const commonPath =
|
|
7461
|
+
const commonPath = join12(promptsDir, "personas", "common.md");
|
|
7436
7462
|
if (!existsSync10(commonPath)) return readFileSync10(briefPath, "utf8");
|
|
7437
7463
|
return `${readFileSync10(commonPath, "utf8")}${readFileSync10(briefPath, "utf8")}`;
|
|
7438
7464
|
}
|
|
@@ -7812,9 +7838,9 @@ function parseSkillFile(path) {
|
|
|
7812
7838
|
return { name: meta.name, description: meta.description, prompt: body, file: path };
|
|
7813
7839
|
}
|
|
7814
7840
|
function listLocalSkills(promptsDir) {
|
|
7815
|
-
const dir =
|
|
7841
|
+
const dir = join13(promptsDir, "skills");
|
|
7816
7842
|
if (!existsSync11(dir)) return [];
|
|
7817
|
-
return
|
|
7843
|
+
return readdirSync4(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join13(dir, f)));
|
|
7818
7844
|
}
|
|
7819
7845
|
async function syncSkills(route, opts) {
|
|
7820
7846
|
if (!route.resolved) {
|
|
@@ -8047,15 +8073,15 @@ var AgentError = class extends Error {
|
|
|
8047
8073
|
var AgentSpawnError = class extends AgentError {
|
|
8048
8074
|
};
|
|
8049
8075
|
function planAgentRun(o) {
|
|
8050
|
-
const briefPath =
|
|
8076
|
+
const briefPath = join14(o.route.promptsDir, "personas", `lane-${o.role}.md`);
|
|
8051
8077
|
if (!existsSync12(briefPath)) {
|
|
8052
8078
|
throw new AgentError(`no brief at ${briefPath} \u2014 refusing to run an unscoped session`);
|
|
8053
8079
|
}
|
|
8054
|
-
const commonPath =
|
|
8080
|
+
const commonPath = join14(o.route.promptsDir, "personas", "common.md");
|
|
8055
8081
|
const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) && existsSync12(commonPath);
|
|
8056
8082
|
const now = o.now ?? (() => Date.now());
|
|
8057
8083
|
const routeLabel = routeSlug(o.route.route);
|
|
8058
|
-
const digestPath2 =
|
|
8084
|
+
const digestPath2 = join14(o.stateDir, `digest-${routeLabel}-${o.role}.md`);
|
|
8059
8085
|
let digest;
|
|
8060
8086
|
let ageSeconds;
|
|
8061
8087
|
if (existsSync12(digestPath2)) {
|
|
@@ -8085,7 +8111,7 @@ function planAgentRun(o) {
|
|
|
8085
8111
|
digest
|
|
8086
8112
|
});
|
|
8087
8113
|
const promptSha = createHash2("sha256").update(expanded.text).digest("hex");
|
|
8088
|
-
const streamsDir =
|
|
8114
|
+
const streamsDir = join14(o.stateDir, "streams");
|
|
8089
8115
|
const base = `${routeLabel}-${o.role}-${o.cycle}`;
|
|
8090
8116
|
const seat = resolveSeat(o.ship, o.route, o.role, o.agentPersona ?? (o.agentModel ? { model: o.agentModel } : {}), o.repo);
|
|
8091
8117
|
const { toolchain, model } = seat;
|
|
@@ -8141,8 +8167,8 @@ function planAgentRun(o) {
|
|
|
8141
8167
|
digestAttached: digest !== void 0,
|
|
8142
8168
|
digestAgeSeconds: ageSeconds,
|
|
8143
8169
|
model,
|
|
8144
|
-
streamPath:
|
|
8145
|
-
eventsPath:
|
|
8170
|
+
streamPath: join14(streamsDir, `${base}.jsonl`),
|
|
8171
|
+
eventsPath: join14(streamsDir, `${base}.events.jsonl`),
|
|
8146
8172
|
ticket: o.ticket,
|
|
8147
8173
|
agentLog,
|
|
8148
8174
|
promptVersion: o.route.resolved?.agentPersonas?.[o.role]?.historyId,
|
|
@@ -8349,12 +8375,12 @@ function spawnAgent(plan, emit) {
|
|
|
8349
8375
|
|
|
8350
8376
|
// src/run-worktree.ts
|
|
8351
8377
|
import { existsSync as existsSync14 } from "fs";
|
|
8352
|
-
import { dirname as dirname7, join as
|
|
8378
|
+
import { dirname as dirname7, join as join16 } from "path";
|
|
8353
8379
|
|
|
8354
8380
|
// src/ports.ts
|
|
8355
8381
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
8356
8382
|
import { existsSync as existsSync13 } from "fs";
|
|
8357
|
-
import { join as
|
|
8383
|
+
import { join as join15 } from "path";
|
|
8358
8384
|
var run = (cmd, args) => {
|
|
8359
8385
|
try {
|
|
8360
8386
|
return execFileSync2(cmd, args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
@@ -8399,7 +8425,7 @@ function ticketForPort(port, s = DEFAULT_PORTS) {
|
|
|
8399
8425
|
}
|
|
8400
8426
|
function worktreeExists(parent, prefix, n) {
|
|
8401
8427
|
for (const name of [`${prefix}${n}`, `${prefix}${String(n).padStart(3, "0")}`]) {
|
|
8402
|
-
if (existsSync13(
|
|
8428
|
+
if (existsSync13(join15(parent, name, ".git"))) return true;
|
|
8403
8429
|
}
|
|
8404
8430
|
return false;
|
|
8405
8431
|
}
|
|
@@ -8487,7 +8513,7 @@ function planRunWorktree(dir, cfg, t, role) {
|
|
|
8487
8513
|
if (countCommits(existing.path, `${baseRef}..HEAD`) > 0) return { action: "leave-unpushed", path: existing.path, branch };
|
|
8488
8514
|
return { action: "recut", path: existing.path, branch, base: baseRef };
|
|
8489
8515
|
}
|
|
8490
|
-
const path =
|
|
8516
|
+
const path = join16(dirname7(dir), worktreeForTicket(dir, cfg, t, role));
|
|
8491
8517
|
if (located?.where === "remote" || located && hasRemote && remoteBranchExists(dir, remote, located.branch)) {
|
|
8492
8518
|
return { action: "cut-remote", path, branch: located.branch };
|
|
8493
8519
|
}
|
|
@@ -8695,7 +8721,7 @@ async function retireRunWorktree(tracker, dir, cfg, t, role, seatId, shipName2,
|
|
|
8695
8721
|
}
|
|
8696
8722
|
|
|
8697
8723
|
// src/connect-wizard.ts
|
|
8698
|
-
import { delimiter, dirname as dirname8, join as
|
|
8724
|
+
import { delimiter, dirname as dirname8, join as join17 } from "path";
|
|
8699
8725
|
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync15, mkdirSync as mkdirSync7, writeFileSync as writeFileSync9 } from "fs";
|
|
8700
8726
|
import { stringify } from "yaml";
|
|
8701
8727
|
var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
|
|
@@ -8718,7 +8744,7 @@ function defaultIsExecutable(p) {
|
|
|
8718
8744
|
function findOnPath2(name, pathEnv, isExecutable = defaultIsExecutable) {
|
|
8719
8745
|
for (const dir of pathEnv.split(delimiter)) {
|
|
8720
8746
|
if (!dir) continue;
|
|
8721
|
-
const candidate =
|
|
8747
|
+
const candidate = join17(dir, name);
|
|
8722
8748
|
if (isExecutable(candidate)) return candidate;
|
|
8723
8749
|
}
|
|
8724
8750
|
return void 0;
|
|
@@ -8813,8 +8839,8 @@ Wrote ${o.configPath}.
|
|
|
8813
8839
|
}
|
|
8814
8840
|
|
|
8815
8841
|
// src/release-run.ts
|
|
8816
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync16, copyFileSync, mkdirSync as mkdirSync8, mkdtempSync, rmSync as
|
|
8817
|
-
import { dirname as dirname9, join as
|
|
8842
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync16, copyFileSync, mkdirSync as mkdirSync8, mkdtempSync, rmSync as rmSync7 } from "fs";
|
|
8843
|
+
import { dirname as dirname9, join as join18 } from "path";
|
|
8818
8844
|
import { tmpdir as tmpdir2 } from "os";
|
|
8819
8845
|
|
|
8820
8846
|
// src/release.ts
|
|
@@ -9472,7 +9498,7 @@ async function bump(o, size) {
|
|
|
9472
9498
|
}
|
|
9473
9499
|
return printed;
|
|
9474
9500
|
}
|
|
9475
|
-
const files = o.repo.release.versionFiles.map((f) =>
|
|
9501
|
+
const files = o.repo.release.versionFiles.map((f) => join18(o.cwd, f));
|
|
9476
9502
|
const first = files[0];
|
|
9477
9503
|
if (!existsSync16(first)) {
|
|
9478
9504
|
o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
|
|
@@ -9504,7 +9530,7 @@ async function bump(o, size) {
|
|
|
9504
9530
|
function writeChangelog(o, version, entries) {
|
|
9505
9531
|
const file = o.repo.release.changelog;
|
|
9506
9532
|
if (!file || entries.length === 0) return;
|
|
9507
|
-
const path =
|
|
9533
|
+
const path = join18(o.cwd, file);
|
|
9508
9534
|
const section = renderChangelogSection(version, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), entries);
|
|
9509
9535
|
if (o.dryRun) {
|
|
9510
9536
|
o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
|
|
@@ -9589,17 +9615,25 @@ async function confirmLastRelease(o, decision) {
|
|
|
9589
9615
|
if (!confirmed) o.emit.warn(`${decision.lastTag} is still not confirmed live \u2014 tickets stay verified`);
|
|
9590
9616
|
return { confirmed, version };
|
|
9591
9617
|
}
|
|
9618
|
+
function closeReleaseCheckout(cwd, root, dir, branch, run2 = gitOk) {
|
|
9619
|
+
try {
|
|
9620
|
+
run2(cwd, ["worktree", "remove", "--force", dir]);
|
|
9621
|
+
} finally {
|
|
9622
|
+
try {
|
|
9623
|
+
rmSync7(root, { recursive: true, force: true });
|
|
9624
|
+
} finally {
|
|
9625
|
+
run2(cwd, ["worktree", "prune"]);
|
|
9626
|
+
run2(cwd, ["branch", "-D", branch]);
|
|
9627
|
+
}
|
|
9628
|
+
}
|
|
9629
|
+
}
|
|
9592
9630
|
function openReleaseCheckout(o) {
|
|
9593
9631
|
const { remote, base } = o.repo.branch;
|
|
9594
9632
|
const branch = `crew/release-${Date.now()}`;
|
|
9595
|
-
const root = mkdtempSync(
|
|
9596
|
-
const dir =
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
rmSync6(root, { recursive: true, force: true });
|
|
9600
|
-
gitOk(o.cwd, ["worktree", "prune"]);
|
|
9601
|
-
gitOk(o.cwd, ["branch", "-D", branch]);
|
|
9602
|
-
};
|
|
9633
|
+
const root = mkdtempSync(join18(tmpdir2(), "crew-release-"));
|
|
9634
|
+
const dir = join18(root, "checkout");
|
|
9635
|
+
o.emit.emit(`release checkout: ${dir}`, { step: "release" });
|
|
9636
|
+
const close = () => closeReleaseCheckout(o.cwd, root, dir, branch);
|
|
9603
9637
|
try {
|
|
9604
9638
|
git(o.cwd, ["worktree", "add", "-q", "-b", branch, dir, `${remote}/${base}`]);
|
|
9605
9639
|
} catch (e) {
|
|
@@ -9607,10 +9641,10 @@ function openReleaseCheckout(o) {
|
|
|
9607
9641
|
throw e;
|
|
9608
9642
|
}
|
|
9609
9643
|
for (const f of o.repo.worktrees.copy) {
|
|
9610
|
-
const src =
|
|
9644
|
+
const src = join18(o.cwd, f);
|
|
9611
9645
|
if (!existsSync16(src)) continue;
|
|
9612
|
-
mkdirSync8(dirname9(
|
|
9613
|
-
copyFileSync(src,
|
|
9646
|
+
mkdirSync8(dirname9(join18(dir, f)), { recursive: true });
|
|
9647
|
+
copyFileSync(src, join18(dir, f));
|
|
9614
9648
|
}
|
|
9615
9649
|
return { dir, branch, close };
|
|
9616
9650
|
}
|
|
@@ -9689,6 +9723,8 @@ async function runRelease(o) {
|
|
|
9689
9723
|
let checkout;
|
|
9690
9724
|
const stranded = recoverStrandedReleaseCheckouts(o.cwd);
|
|
9691
9725
|
if (stranded.length > 0) o.emit.warn(scoped(`dropped release checkout(s) stranded by a crash: ${stranded.join(", ")}`));
|
|
9726
|
+
const swept = sweepStaleReleaseRoots(tmpdir2());
|
|
9727
|
+
if (swept.length > 0) o.emit.emit(scoped(`removed ${swept.length} stale crew-release-* temp dir(s)`), { step: "release" });
|
|
9692
9728
|
try {
|
|
9693
9729
|
checkout = openReleaseCheckout(o);
|
|
9694
9730
|
} catch (e) {
|
|
@@ -10049,7 +10085,7 @@ ${n.detail ?? n.headline}
|
|
|
10049
10085
|
|
|
10050
10086
|
Filed automatically \u2014 no \`notify\` hook is configured for route \`${route}\`, so this is the crew's built-in fallback rather than a silent log line. Configure \`hooks.notify\` on this route to replace it.`;
|
|
10051
10087
|
}
|
|
10052
|
-
async function applyFailureAlert(writer, n, route, openTickets, memberId, log, dryRun) {
|
|
10088
|
+
async function applyFailureAlert(writer, n, route, openTickets, memberId, log, dryRun, placement = {}) {
|
|
10053
10089
|
const existing = findOpenFailureAlert(openTickets, n.headline);
|
|
10054
10090
|
if (existing) {
|
|
10055
10091
|
const label = displayKey(existing);
|
|
@@ -10076,7 +10112,9 @@ Still failing: ${n.detail ?? n.headline}`, memberId, "failure_alert");
|
|
|
10076
10112
|
description: ticketBody(n, route),
|
|
10077
10113
|
report_type: "bug",
|
|
10078
10114
|
severity: "s2",
|
|
10079
|
-
reporter_name: "Crew release"
|
|
10115
|
+
reporter_name: "Crew release",
|
|
10116
|
+
...placement.repo_id ? { repo_id: placement.repo_id } : {},
|
|
10117
|
+
...placement.project_id ? { project_id: placement.project_id } : {}
|
|
10080
10118
|
}, memberId);
|
|
10081
10119
|
const label = created.issue_id ? displayKey(created) : created.id;
|
|
10082
10120
|
log.warn(`release failure \u2014 filed ${label}`, { step: "release" });
|
|
@@ -11449,14 +11487,14 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
|
|
|
11449
11487
|
}
|
|
11450
11488
|
|
|
11451
11489
|
// src/stream-sweep.ts
|
|
11452
|
-
import { existsSync as existsSync19, readdirSync as
|
|
11453
|
-
import { join as
|
|
11490
|
+
import { existsSync as existsSync19, readdirSync as readdirSync5, rmSync as rmSync8, statSync as statSync7 } from "fs";
|
|
11491
|
+
import { join as join19 } from "path";
|
|
11454
11492
|
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
11455
11493
|
function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
|
|
11456
|
-
const dir =
|
|
11494
|
+
const dir = join19(stateDir, "streams");
|
|
11457
11495
|
if (!existsSync19(dir)) return [];
|
|
11458
11496
|
const cutoff = now - retentionDays * DAY_MS2;
|
|
11459
|
-
return
|
|
11497
|
+
return readdirSync5(dir).map((name) => join19(dir, name)).filter((path) => {
|
|
11460
11498
|
try {
|
|
11461
11499
|
return statSync7(path).mtimeMs < cutoff;
|
|
11462
11500
|
} catch {
|
|
@@ -11472,7 +11510,7 @@ function applyStreamSweep(paths, dryRun, log) {
|
|
|
11472
11510
|
continue;
|
|
11473
11511
|
}
|
|
11474
11512
|
try {
|
|
11475
|
-
|
|
11513
|
+
rmSync8(path, { force: true });
|
|
11476
11514
|
removed++;
|
|
11477
11515
|
log.emit(`removed stream artifact ${path}`, { step: "worktree" });
|
|
11478
11516
|
} catch (e) {
|
|
@@ -11484,7 +11522,7 @@ function applyStreamSweep(paths, dryRun, log) {
|
|
|
11484
11522
|
function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
|
|
11485
11523
|
if (!existsSync19(stateDir)) return [];
|
|
11486
11524
|
const cutoff = now - retentionDays * DAY_MS2;
|
|
11487
|
-
return
|
|
11525
|
+
return readdirSync5(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join19(stateDir, name)).filter((path) => {
|
|
11488
11526
|
try {
|
|
11489
11527
|
return statSync7(path).mtimeMs < cutoff;
|
|
11490
11528
|
} catch {
|
|
@@ -11501,11 +11539,11 @@ import { dirname as dirOf, resolve as resolvePath } from "path";
|
|
|
11501
11539
|
// src/ssh-keys.ts
|
|
11502
11540
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
11503
11541
|
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync14, chmodSync } from "fs";
|
|
11504
|
-
import { dirname as dirname11, join as
|
|
11542
|
+
import { dirname as dirname11, join as join20 } from "path";
|
|
11505
11543
|
var SshKeyError = class extends Error {
|
|
11506
11544
|
};
|
|
11507
11545
|
function shipSshKeyPathFor(stateDir) {
|
|
11508
|
-
return
|
|
11546
|
+
return join20(stateDir, "ssh", "id_ed25519");
|
|
11509
11547
|
}
|
|
11510
11548
|
function ensureShipSshKeypair(stateDir) {
|
|
11511
11549
|
const privateKeyPath = shipSshKeyPathFor(stateDir);
|
|
@@ -11750,9 +11788,9 @@ function syncAllPassengerCheckouts(cfg) {
|
|
|
11750
11788
|
|
|
11751
11789
|
// src/tunnel.ts
|
|
11752
11790
|
import { spawn as spawn3 } from "child_process";
|
|
11753
|
-
import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync11, unlinkSync as unlinkSync3, readdirSync as
|
|
11791
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync11, unlinkSync as unlinkSync3, readdirSync as readdirSync6 } from "fs";
|
|
11754
11792
|
import { randomInt } from "crypto";
|
|
11755
|
-
import { join as
|
|
11793
|
+
import { join as join21 } from "path";
|
|
11756
11794
|
var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
|
|
11757
11795
|
var defaultSpawn = (cmd, args) => spawn3(cmd, args, DEFAULT_SPAWN_OPTIONS);
|
|
11758
11796
|
var SLUG_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
@@ -11819,7 +11857,7 @@ function startTunnel(opts) {
|
|
|
11819
11857
|
};
|
|
11820
11858
|
}
|
|
11821
11859
|
function tunnelStatePath(stateDir, workspaceId) {
|
|
11822
|
-
return
|
|
11860
|
+
return join21(stateDir, "passengers", `${workspaceId}.json`);
|
|
11823
11861
|
}
|
|
11824
11862
|
function readPersistedTunnel(stateDir, workspaceId) {
|
|
11825
11863
|
const path = tunnelStatePath(stateDir, workspaceId);
|
|
@@ -11832,7 +11870,7 @@ function readPersistedTunnel(stateDir, workspaceId) {
|
|
|
11832
11870
|
}
|
|
11833
11871
|
function writePersistedTunnel(stateDir, state) {
|
|
11834
11872
|
const path = tunnelStatePath(stateDir, state.workspaceId);
|
|
11835
|
-
mkdirSync10(
|
|
11873
|
+
mkdirSync10(join21(stateDir, "passengers"), { recursive: true });
|
|
11836
11874
|
writeFileSync11(path, `${JSON.stringify(state, null, 2)}
|
|
11837
11875
|
`);
|
|
11838
11876
|
}
|
|
@@ -11930,9 +11968,9 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
|
|
|
11930
11968
|
}
|
|
11931
11969
|
}
|
|
11932
11970
|
function readAllPersistedWorkspaceIds(stateDir) {
|
|
11933
|
-
const dir =
|
|
11971
|
+
const dir = join21(stateDir, "passengers");
|
|
11934
11972
|
if (!existsSync21(dir)) return [];
|
|
11935
|
-
return
|
|
11973
|
+
return readdirSync6(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
|
|
11936
11974
|
}
|
|
11937
11975
|
|
|
11938
11976
|
// src/mcp-endpoint-registry.ts
|
|
@@ -12052,8 +12090,8 @@ async function unregisterEndpoint(workspaceId, info, d) {
|
|
|
12052
12090
|
import { createServer as createHttpServer } from "http";
|
|
12053
12091
|
import { spawn as spawn4 } from "child_process";
|
|
12054
12092
|
import { randomBytes } from "crypto";
|
|
12055
|
-
import { existsSync as existsSync22, mkdirSync as mkdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync12, unlinkSync as unlinkSync4, readdirSync as
|
|
12056
|
-
import { join as
|
|
12093
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync12, unlinkSync as unlinkSync4, readdirSync as readdirSync7 } from "fs";
|
|
12094
|
+
import { join as join22 } from "path";
|
|
12057
12095
|
|
|
12058
12096
|
// src/secret-compare.ts
|
|
12059
12097
|
import { timingSafeEqual } from "crypto";
|
|
@@ -12142,7 +12180,7 @@ var defaultSpawn2 = (cmd, args, env) => {
|
|
|
12142
12180
|
return child;
|
|
12143
12181
|
};
|
|
12144
12182
|
function syncDaemonStatePath(stateDir, workspaceId) {
|
|
12145
|
-
return
|
|
12183
|
+
return join22(stateDir, "passengers", `${workspaceId}-sync.json`);
|
|
12146
12184
|
}
|
|
12147
12185
|
function readPersistedSyncDaemon(stateDir, workspaceId) {
|
|
12148
12186
|
const path = syncDaemonStatePath(stateDir, workspaceId);
|
|
@@ -12155,7 +12193,7 @@ function readPersistedSyncDaemon(stateDir, workspaceId) {
|
|
|
12155
12193
|
}
|
|
12156
12194
|
function writePersistedSyncDaemon(stateDir, state) {
|
|
12157
12195
|
const path = syncDaemonStatePath(stateDir, state.workspaceId);
|
|
12158
|
-
mkdirSync11(
|
|
12196
|
+
mkdirSync11(join22(stateDir, "passengers"), { recursive: true });
|
|
12159
12197
|
writeFileSync12(path, `${JSON.stringify(state, null, 2)}
|
|
12160
12198
|
`);
|
|
12161
12199
|
}
|
|
@@ -12176,10 +12214,10 @@ function waitForExit(pid, alive, timeoutMs = 5e3) {
|
|
|
12176
12214
|
}
|
|
12177
12215
|
}
|
|
12178
12216
|
function readAllPersistedSyncWorkspaceIds(stateDir) {
|
|
12179
|
-
const dir =
|
|
12217
|
+
const dir = join22(stateDir, "passengers");
|
|
12180
12218
|
if (!existsSync22(dir)) return [];
|
|
12181
12219
|
const suffix = "-sync.json";
|
|
12182
|
-
return
|
|
12220
|
+
return readdirSync7(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
|
|
12183
12221
|
}
|
|
12184
12222
|
function syncPassengerSyncDaemons(plans, cfg, stateDir, deps = {}) {
|
|
12185
12223
|
const alive = deps.isPidAlive ?? isPidAlive;
|
|
@@ -12234,8 +12272,8 @@ function syncPassengerSyncDaemons(plans, cfg, stateDir, deps = {}) {
|
|
|
12234
12272
|
}
|
|
12235
12273
|
|
|
12236
12274
|
// src/daemon.ts
|
|
12237
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync12, readFileSync as readFileSync17, readdirSync as
|
|
12238
|
-
import { join as
|
|
12275
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync12, readFileSync as readFileSync17, readdirSync as readdirSync8, rmSync as rmSync9, statSync as statSync8, writeFileSync as writeFileSync13 } from "fs";
|
|
12276
|
+
import { join as join23 } from "path";
|
|
12239
12277
|
var DEFAULT_FLOOR_MS = 5e3;
|
|
12240
12278
|
var DEFAULT_CEILING_MS = INTERVAL_SECONDS * 1e3;
|
|
12241
12279
|
var defaultSleep = (ms2) => new Promise((resolve5) => setTimeout(resolve5, ms2));
|
|
@@ -12273,8 +12311,8 @@ function latestMtime(path) {
|
|
|
12273
12311
|
if (stat.isFile()) return stat.mtimeMs;
|
|
12274
12312
|
if (!stat.isDirectory()) return 0;
|
|
12275
12313
|
let latest = stat.mtimeMs;
|
|
12276
|
-
for (const entry of
|
|
12277
|
-
latest = Math.max(latest, latestMtime(
|
|
12314
|
+
for (const entry of readdirSync8(path, { withFileTypes: true })) {
|
|
12315
|
+
latest = Math.max(latest, latestMtime(join23(path, entry.name)));
|
|
12278
12316
|
}
|
|
12279
12317
|
return latest;
|
|
12280
12318
|
}
|
|
@@ -12286,14 +12324,14 @@ function makeStaleChecker(paths, opts = {}) {
|
|
|
12286
12324
|
return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync23(opts.reloadSentinel);
|
|
12287
12325
|
}
|
|
12288
12326
|
function reloadSentinelFor(stateDir) {
|
|
12289
|
-
return
|
|
12327
|
+
return join23(stateDir, "reload-requested");
|
|
12290
12328
|
}
|
|
12291
12329
|
function daemonStampFor(stateDir) {
|
|
12292
|
-
return
|
|
12330
|
+
return join23(stateDir, "daemon.started");
|
|
12293
12331
|
}
|
|
12294
12332
|
function noteDaemonStarted(stateDir, now = Date.now()) {
|
|
12295
12333
|
mkdirSync12(stateDir, { recursive: true });
|
|
12296
|
-
|
|
12334
|
+
rmSync9(reloadSentinelFor(stateDir), { force: true });
|
|
12297
12335
|
writeFileSync13(daemonStampFor(stateDir), `${JSON.stringify({ pid: process.pid, startedAt: now })}
|
|
12298
12336
|
`);
|
|
12299
12337
|
}
|
|
@@ -13113,7 +13151,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
13113
13151
|
}
|
|
13114
13152
|
if (host === "linux" && hasSystemd) {
|
|
13115
13153
|
const label = labelFor(CREW_HOME, "run");
|
|
13116
|
-
const staleTimer =
|
|
13154
|
+
const staleTimer = join24(homedir8(), ".config", "systemd", "user", `${label}.timer`);
|
|
13117
13155
|
if (existsSync24(staleTimer)) {
|
|
13118
13156
|
process.stdout.write(
|
|
13119
13157
|
`removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
|
|
@@ -13489,7 +13527,17 @@ ${changed2.map((c) => ` - ${c}
|
|
|
13489
13527
|
const notified = await notify(c, cfg.ship, news, remit, dryRun);
|
|
13490
13528
|
if (!notified && news.level === "fail") {
|
|
13491
13529
|
const memberId = releaseSeat(c);
|
|
13492
|
-
|
|
13530
|
+
const placement = {};
|
|
13531
|
+
try {
|
|
13532
|
+
const repoId = repoIdForName(c, target.name);
|
|
13533
|
+
if (repoId) {
|
|
13534
|
+
placement.repo_id = repoId;
|
|
13535
|
+
const projectId = (await tracker.repoRow(repoId))?.project_id;
|
|
13536
|
+
if (projectId) placement.project_id = projectId;
|
|
13537
|
+
}
|
|
13538
|
+
} catch {
|
|
13539
|
+
}
|
|
13540
|
+
await applyFailureAlert(tracker, news, scope, all, memberId, remit, dryRun, placement).catch((e) => {
|
|
13493
13541
|
remit.warn(`could not file/update a failure ticket: ${e.message}`, { step: "release" });
|
|
13494
13542
|
});
|
|
13495
13543
|
}
|
|
@@ -14111,7 +14159,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
14111
14159
|
process.exit(1);
|
|
14112
14160
|
}
|
|
14113
14161
|
requireArmed("run the daemon loop");
|
|
14114
|
-
const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [
|
|
14162
|
+
const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join24(CREW_HOME, "src"), join24(CREW_HOME, "dist"), join24(CREW_HOME, "bin")];
|
|
14115
14163
|
const configPaths = daemonConfigPaths(cfg);
|
|
14116
14164
|
watchPaths.push(...configPaths);
|
|
14117
14165
|
noteDaemonStarted(cfg.ship.stateDir);
|
|
@@ -14791,7 +14839,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
14791
14839
|
const keyPersisted = keychainBacked || apiKeyFile !== void 0;
|
|
14792
14840
|
const wizard = firstRun;
|
|
14793
14841
|
const writeFirstRun = wizard !== void 0 && !dryRun && keyPersisted;
|
|
14794
|
-
const routeDir = value("dir") ?? (writeFirstRun ?
|
|
14842
|
+
const routeDir = value("dir") ?? (writeFirstRun ? join24(DEFAULT_REPOS_BASE_PATH, found.workspaceSlug) : void 0);
|
|
14795
14843
|
const routeBlock = renderConnection(
|
|
14796
14844
|
found,
|
|
14797
14845
|
route2,
|
package/package.json
CHANGED