@tablation/crew 0.46.0 → 0.47.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 +174 -71
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1963,6 +1963,11 @@ var ROLE_LABEL = {
|
|
|
1963
1963
|
};
|
|
1964
1964
|
var ConfigError = class extends Error {
|
|
1965
1965
|
};
|
|
1966
|
+
function parseCloneScheme(v) {
|
|
1967
|
+
if (v === void 0 || v === null || v === "") return void 0;
|
|
1968
|
+
if (v === "ssh" || v === "https") return v;
|
|
1969
|
+
throw new ConfigError(`ship.cloneScheme must be "ssh" or "https", got ${JSON.stringify(v)}`);
|
|
1970
|
+
}
|
|
1966
1971
|
var SHIP_KEYS = /* @__PURE__ */ new Set([
|
|
1967
1972
|
"name",
|
|
1968
1973
|
"platform",
|
|
@@ -1987,7 +1992,8 @@ var SHIP_KEYS = /* @__PURE__ */ new Set([
|
|
|
1987
1992
|
"relayPort",
|
|
1988
1993
|
"relayHttpPort",
|
|
1989
1994
|
"relayPublicDomain",
|
|
1990
|
-
"passengerImage"
|
|
1995
|
+
"passengerImage",
|
|
1996
|
+
"cloneScheme"
|
|
1991
1997
|
]);
|
|
1992
1998
|
var ROUTE_KEYS = /* @__PURE__ */ new Set([
|
|
1993
1999
|
"route",
|
|
@@ -2250,6 +2256,7 @@ function parseResolved(raw, m, where) {
|
|
|
2250
2256
|
holds: holds2,
|
|
2251
2257
|
contract: raw.contract ?? void 0,
|
|
2252
2258
|
...typeof raw.releaseSeat === "string" && raw.releaseSeat ? { releaseSeat: raw.releaseSeat } : {},
|
|
2259
|
+
...typeof raw.releaseAgentId === "string" && raw.releaseAgentId ? { releaseAgentId: raw.releaseAgentId } : {},
|
|
2253
2260
|
...Object.keys(agentPersonas).length > 0 ? { agentPersonas } : {},
|
|
2254
2261
|
...Object.keys(agentSkills).length > 0 ? { agentSkills } : {}
|
|
2255
2262
|
};
|
|
@@ -2355,7 +2362,8 @@ Create the first of those:
|
|
|
2355
2362
|
relayPort: shipRaw.relayPort !== void 0 ? Number(shipRaw.relayPort) : 2222,
|
|
2356
2363
|
relayHttpPort: shipRaw.relayHttpPort !== void 0 ? Number(shipRaw.relayHttpPort) : 443,
|
|
2357
2364
|
relayPublicDomain: shipRaw.relayPublicDomain ? String(shipRaw.relayPublicDomain) : void 0,
|
|
2358
|
-
passengerImage: shipRaw.passengerImage ? String(shipRaw.passengerImage) : void 0
|
|
2365
|
+
passengerImage: shipRaw.passengerImage ? String(shipRaw.passengerImage) : void 0,
|
|
2366
|
+
cloneScheme: parseCloneScheme(shipRaw.cloneScheme)
|
|
2359
2367
|
},
|
|
2360
2368
|
routes,
|
|
2361
2369
|
crewHome,
|
|
@@ -4020,17 +4028,22 @@ import {
|
|
|
4020
4028
|
closeSync as closeSync2,
|
|
4021
4029
|
writeSync,
|
|
4022
4030
|
statSync as statSync4,
|
|
4023
|
-
unlinkSync as unlinkSync2
|
|
4031
|
+
unlinkSync as unlinkSync2,
|
|
4032
|
+
readFileSync as readFileSync9,
|
|
4033
|
+
writeFileSync as writeFileSync6
|
|
4024
4034
|
} from "fs";
|
|
4025
4035
|
import { dirname as dirname5, basename as basename4 } from "path";
|
|
4026
4036
|
var GitError = class extends Error {
|
|
4027
4037
|
};
|
|
4038
|
+
function streamText(x) {
|
|
4039
|
+
return String(x ?? "").trim();
|
|
4040
|
+
}
|
|
4028
4041
|
function gitRaw(cwd, args) {
|
|
4029
4042
|
try {
|
|
4030
4043
|
return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
4031
4044
|
} catch (e) {
|
|
4032
4045
|
const err = e;
|
|
4033
|
-
const why = [err.stderr, err.stdout].map(
|
|
4046
|
+
const why = [err.stderr, err.stdout].map(streamText).filter(Boolean).join(" / ");
|
|
4034
4047
|
throw new GitError(`git ${args.join(" ")}: ${why || err.message.trim()}`);
|
|
4035
4048
|
}
|
|
4036
4049
|
}
|
|
@@ -4039,7 +4052,7 @@ function git(cwd, args) {
|
|
|
4039
4052
|
return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
4040
4053
|
} catch (e) {
|
|
4041
4054
|
const err = e;
|
|
4042
|
-
const why = [err.stderr, err.stdout].map(
|
|
4055
|
+
const why = [err.stderr, err.stdout].map(streamText).filter(Boolean).join(" / ");
|
|
4043
4056
|
throw new GitError(`git ${args.join(" ")}: ${why || err.message.trim()}`);
|
|
4044
4057
|
}
|
|
4045
4058
|
}
|
|
@@ -4382,10 +4395,19 @@ function worktrees(cwd) {
|
|
|
4382
4395
|
if (current.path) list.push({ path: current.path, branch: current.branch ?? null, prunable: current.prunable ?? false });
|
|
4383
4396
|
return list;
|
|
4384
4397
|
}
|
|
4385
|
-
function cloneUrlFor(remote) {
|
|
4386
|
-
|
|
4398
|
+
function cloneUrlFor(remote, opts = {}) {
|
|
4399
|
+
const origin = opts.originUrl?.trim() ?? "";
|
|
4400
|
+
const scheme = /^https?:\/\//i.test(origin) ? "https" : /^(git@|ssh:\/\/)/i.test(origin) ? "ssh" : opts.scheme ?? "ssh";
|
|
4401
|
+
return scheme === "https" ? `https://github.com/${remote}.git` : `git@github.com:${remote}.git`;
|
|
4402
|
+
}
|
|
4403
|
+
function originUrlOf(dir, remoteName) {
|
|
4404
|
+
try {
|
|
4405
|
+
return git(dir, ["remote", "get-url", remoteName]) || null;
|
|
4406
|
+
} catch {
|
|
4407
|
+
return null;
|
|
4408
|
+
}
|
|
4387
4409
|
}
|
|
4388
|
-
function ensureRepoCheckout(dir, remote) {
|
|
4410
|
+
function ensureRepoCheckout(dir, remote, opts = {}) {
|
|
4389
4411
|
if (existsSync9(`${dir}/.git`)) return false;
|
|
4390
4412
|
if (!remote) {
|
|
4391
4413
|
throw new GitError(
|
|
@@ -4393,12 +4415,13 @@ function ensureRepoCheckout(dir, remote) {
|
|
|
4393
4415
|
);
|
|
4394
4416
|
}
|
|
4395
4417
|
mkdirSync5(dirname5(dir), { recursive: true });
|
|
4418
|
+
const url = cloneUrlFor(remote, opts);
|
|
4396
4419
|
try {
|
|
4397
|
-
execFileSync("git", ["clone",
|
|
4420
|
+
execFileSync("git", ["clone", url, dir], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
4398
4421
|
} catch (e) {
|
|
4399
4422
|
const err = e;
|
|
4400
|
-
const why = [err.stderr, err.stdout].map(
|
|
4401
|
-
throw new GitError(`git clone ${
|
|
4423
|
+
const why = [err.stderr, err.stdout].map(streamText).filter(Boolean).join(" / ");
|
|
4424
|
+
throw new GitError(`git clone ${url} ${dir}: ${why || err.message.trim()}`);
|
|
4402
4425
|
}
|
|
4403
4426
|
return true;
|
|
4404
4427
|
}
|
|
@@ -4445,10 +4468,31 @@ function acquireCheckoutLock(dir, timeoutMs = 3e4) {
|
|
|
4445
4468
|
}
|
|
4446
4469
|
}
|
|
4447
4470
|
}
|
|
4448
|
-
function syncPassengerCheckout(dir, githubRemote, gitRemoteName, base) {
|
|
4471
|
+
function syncPassengerCheckout(dir, githubRemote, gitRemoteName, base, clone = {}) {
|
|
4449
4472
|
const release = acquireCheckoutLock(dir);
|
|
4450
4473
|
try {
|
|
4451
|
-
if (
|
|
4474
|
+
if (!existsSync9(`${dir}/.git`) && githubRemote) {
|
|
4475
|
+
const url = cloneUrlFor(githubRemote, clone);
|
|
4476
|
+
const marker = cloneFailureMarker(dir);
|
|
4477
|
+
if (existsSync9(marker) && readFileSync9(marker, "utf8").split("\n")[0] === url) {
|
|
4478
|
+
return { action: "clone-failed", detail: readFileSync9(marker, "utf8").split("\n").slice(1).join("\n"), repeat: true };
|
|
4479
|
+
}
|
|
4480
|
+
try {
|
|
4481
|
+
ensureRepoCheckout(dir, githubRemote, clone);
|
|
4482
|
+
} catch (e) {
|
|
4483
|
+
if (!(e instanceof GitError)) throw e;
|
|
4484
|
+
writeFileSync6(marker, `${url}
|
|
4485
|
+
${e.message}`);
|
|
4486
|
+
return { action: "clone-failed", detail: e.message, repeat: false };
|
|
4487
|
+
}
|
|
4488
|
+
try {
|
|
4489
|
+
unlinkSync2(marker);
|
|
4490
|
+
} catch {
|
|
4491
|
+
}
|
|
4492
|
+
if (currentBranch(dir) !== base) git(dir, ["checkout", base]);
|
|
4493
|
+
return { action: "cloned", detail: `cloned ${githubRemote} into ${dir}` };
|
|
4494
|
+
}
|
|
4495
|
+
if (ensureRepoCheckout(dir, githubRemote, clone)) {
|
|
4452
4496
|
if (currentBranch(dir) !== base) git(dir, ["checkout", base]);
|
|
4453
4497
|
return { action: "cloned", detail: `cloned ${githubRemote} into ${dir}` };
|
|
4454
4498
|
}
|
|
@@ -4457,6 +4501,9 @@ function syncPassengerCheckout(dir, githubRemote, gitRemoteName, base) {
|
|
|
4457
4501
|
release();
|
|
4458
4502
|
}
|
|
4459
4503
|
}
|
|
4504
|
+
function cloneFailureMarker(dir) {
|
|
4505
|
+
return `${dir}.clone-failed`;
|
|
4506
|
+
}
|
|
4460
4507
|
|
|
4461
4508
|
// src/ship-attention.ts
|
|
4462
4509
|
var RELEASE_STALE_AFTER_INTERVALS = 3;
|
|
@@ -6220,7 +6267,7 @@ function materializeBranchForTicket(dir, cfg, t, role) {
|
|
|
6220
6267
|
}
|
|
6221
6268
|
|
|
6222
6269
|
// src/poll.ts
|
|
6223
|
-
import { writeFileSync as
|
|
6270
|
+
import { writeFileSync as writeFileSync7 } from "fs";
|
|
6224
6271
|
import { join as join10 } from "path";
|
|
6225
6272
|
|
|
6226
6273
|
// src/stalled.ts
|
|
@@ -6560,7 +6607,7 @@ function writeDigest(o, d, role, stateDir) {
|
|
|
6560
6607
|
};
|
|
6561
6608
|
const text = role === "qa" ? qaDigest(input) : buildingDigest(input);
|
|
6562
6609
|
const path = digestPath(stateDir, routeSlug(o.route.route), role);
|
|
6563
|
-
|
|
6610
|
+
writeFileSync7(path, text);
|
|
6564
6611
|
o.emit.emit(`queue digest written for ${role} (${Buffer.byteLength(text)} bytes)`, {
|
|
6565
6612
|
data: { role, bytes: Buffer.byteLength(text) }
|
|
6566
6613
|
});
|
|
@@ -6944,7 +6991,7 @@ var alreadyReported = (ticket, g, events) => events.some((e) => e.record_id ===
|
|
|
6944
6991
|
|
|
6945
6992
|
// src/agent.ts
|
|
6946
6993
|
import { spawn as spawn2 } from "child_process";
|
|
6947
|
-
import { readFileSync as
|
|
6994
|
+
import { readFileSync as readFileSync12, existsSync as existsSync12, statSync as statSync5, mkdirSync as mkdirSync6, writeFileSync as writeFileSync8, createWriteStream } from "fs";
|
|
6948
6995
|
import { finished } from "stream/promises";
|
|
6949
6996
|
import { join as join13, dirname as dirname6 } from "path";
|
|
6950
6997
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -7361,13 +7408,13 @@ function expandSkillMarkers(text, lookup, opts = {}) {
|
|
|
7361
7408
|
}
|
|
7362
7409
|
|
|
7363
7410
|
// src/skills.ts
|
|
7364
|
-
import { readFileSync as
|
|
7411
|
+
import { readFileSync as readFileSync11, readdirSync as readdirSync3, existsSync as existsSync11 } from "fs";
|
|
7365
7412
|
import { join as join12 } from "path";
|
|
7366
7413
|
import { parse as parseYaml } from "yaml";
|
|
7367
7414
|
import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError8, TablationApiError as TablationApiError2 } from "@tablation/client";
|
|
7368
7415
|
|
|
7369
7416
|
// src/agents.ts
|
|
7370
|
-
import { readFileSync as
|
|
7417
|
+
import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
|
|
7371
7418
|
import { join as join11 } from "path";
|
|
7372
7419
|
import { TablationClient as TablationClient2, StaleWriteError as StaleWriteError7, TablationApiError } from "@tablation/client";
|
|
7373
7420
|
var PERSONA_NAME = {
|
|
@@ -7384,10 +7431,10 @@ var AgentsSyncError = class extends Error {
|
|
|
7384
7431
|
function personaDefaultPrompt(promptsDir, role) {
|
|
7385
7432
|
const briefPath = join11(promptsDir, "personas", `lane-${role}.md`);
|
|
7386
7433
|
if (!existsSync10(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
|
|
7387
|
-
if (SELF_CONTAINED_ROLES.has(role)) return
|
|
7434
|
+
if (SELF_CONTAINED_ROLES.has(role)) return readFileSync10(briefPath, "utf8");
|
|
7388
7435
|
const commonPath = join11(promptsDir, "personas", "common.md");
|
|
7389
|
-
if (!existsSync10(commonPath)) return
|
|
7390
|
-
return `${
|
|
7436
|
+
if (!existsSync10(commonPath)) return readFileSync10(briefPath, "utf8");
|
|
7437
|
+
return `${readFileSync10(commonPath, "utf8")}${readFileSync10(briefPath, "utf8")}`;
|
|
7391
7438
|
}
|
|
7392
7439
|
async function resolveAgentId(route, role, opts) {
|
|
7393
7440
|
const cached = route.resolved?.agentPersonas?.[role]?.agentId;
|
|
@@ -7502,6 +7549,8 @@ function needsBaselineBackfill(model, row) {
|
|
|
7502
7549
|
function isPromptGateRejection(e) {
|
|
7503
7550
|
return e instanceof TablationApiError && e.status === 422;
|
|
7504
7551
|
}
|
|
7552
|
+
var RELEASE_AGENT_NAME = "Release agent";
|
|
7553
|
+
var RELEASE_AGENT_PROMPT = "Principal for the crew release phase (merge, deploy, release comments). It runs no model; this row only gives those tracker writes an agent identity.";
|
|
7505
7554
|
async function syncPersonas(route, opts) {
|
|
7506
7555
|
if (!route.resolved) {
|
|
7507
7556
|
throw new AgentsSyncError(`route "${route.route}" has no resolved ids \u2014 run \`crew connect\` first`);
|
|
@@ -7633,7 +7682,24 @@ async function syncPersonas(route, opts) {
|
|
|
7633
7682
|
}
|
|
7634
7683
|
}
|
|
7635
7684
|
const crewLinks = opts.dryRun ? [] : await linkCrewSeats(route, client2, agentsModel.id, agentPersonas, opts.userAgent);
|
|
7636
|
-
|
|
7685
|
+
const releaseAgentId = opts.dryRun ? route.resolved.releaseAgentId : await linkReleaseSeat(route, client2, agentsModel, opts.userAgent);
|
|
7686
|
+
return { outcomes, agentPersonas, crewLinks, ...releaseAgentId ? { releaseAgentId } : {} };
|
|
7687
|
+
}
|
|
7688
|
+
async function linkReleaseSeat(route, client2, agentsModel, userAgent) {
|
|
7689
|
+
const seatId = route.resolved.releaseSeat;
|
|
7690
|
+
if (!seatId) return void 0;
|
|
7691
|
+
const rows = await client2.records.list(agentsModel.id, { limit: 200 });
|
|
7692
|
+
const agentId = rows.find((r) => r.name === RELEASE_AGENT_NAME)?.id ?? (await client2.records.create(agentsModel.id, {
|
|
7693
|
+
name: RELEASE_AGENT_NAME,
|
|
7694
|
+
prompt: RELEASE_AGENT_PROMPT,
|
|
7695
|
+
...promptSourceFields(agentsModel, RELEASE_AGENT_PROMPT)
|
|
7696
|
+
})).id;
|
|
7697
|
+
const crewModelId = route.resolved.models.crew;
|
|
7698
|
+
const crewModel = await client2.dataModels.get(crewModelId);
|
|
7699
|
+
const columnName = await ensureCrewAgentField(route, client2, crewModel, agentsModel.id, userAgent);
|
|
7700
|
+
const seatRow = await client2.records.get(crewModelId, seatId);
|
|
7701
|
+
if (seatRow[CREW_AGENT_COLUMN] !== agentId) await client2.records.update(crewModelId, seatId, { [columnName]: agentId });
|
|
7702
|
+
return agentId;
|
|
7637
7703
|
}
|
|
7638
7704
|
async function linkCrewSeats(route, client2, agentsModelId, agentPersonas, userAgent) {
|
|
7639
7705
|
const seats2 = route.resolved.seats;
|
|
@@ -7733,7 +7799,7 @@ function describeCrewLink(o) {
|
|
|
7733
7799
|
var SkillSyncError = class extends Error {
|
|
7734
7800
|
};
|
|
7735
7801
|
function parseSkillFile(path) {
|
|
7736
|
-
const text =
|
|
7802
|
+
const text = readFileSync11(path, "utf8");
|
|
7737
7803
|
const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(text);
|
|
7738
7804
|
if (!match) throw new SkillSyncError(`${path}: missing YAML frontmatter (expected a leading --- block)`);
|
|
7739
7805
|
const frontmatter = match[1];
|
|
@@ -7994,10 +8060,10 @@ function planAgentRun(o) {
|
|
|
7994
8060
|
let ageSeconds;
|
|
7995
8061
|
if (existsSync12(digestPath2)) {
|
|
7996
8062
|
ageSeconds = Math.floor((now() - statSync5(digestPath2).mtimeMs) / 1e3);
|
|
7997
|
-
if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest =
|
|
8063
|
+
if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync12(digestPath2, "utf8");
|
|
7998
8064
|
}
|
|
7999
|
-
const common = usesCommon ?
|
|
8000
|
-
const brief =
|
|
8065
|
+
const common = usesCommon ? readFileSync12(commonPath, "utf8") : "";
|
|
8066
|
+
const brief = readFileSync12(briefPath, "utf8");
|
|
8001
8067
|
const effectiveCommon = o.divergedPrompt ? "" : common;
|
|
8002
8068
|
const effectiveBrief = o.divergedPrompt ?? brief;
|
|
8003
8069
|
const localSkills = new Map(listLocalSkills(o.route.promptsDir).map((s) => [s.name, s.prompt]));
|
|
@@ -8223,7 +8289,7 @@ function spawnAgent(plan, emit) {
|
|
|
8223
8289
|
provider: plan.provider ?? LEGACY_TOOLCHAIN_PROVIDER
|
|
8224
8290
|
};
|
|
8225
8291
|
try {
|
|
8226
|
-
|
|
8292
|
+
writeFileSync8(`${plan.streamPath}.meta.json`, JSON.stringify({
|
|
8227
8293
|
route: plan.route,
|
|
8228
8294
|
role: plan.role,
|
|
8229
8295
|
ticket: plan.ticket ?? null,
|
|
@@ -8630,7 +8696,7 @@ async function retireRunWorktree(tracker, dir, cfg, t, role, seatId, shipName2,
|
|
|
8630
8696
|
|
|
8631
8697
|
// src/connect-wizard.ts
|
|
8632
8698
|
import { delimiter, dirname as dirname8, join as join16 } from "path";
|
|
8633
|
-
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync15, mkdirSync as mkdirSync7, writeFileSync as
|
|
8699
|
+
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync15, mkdirSync as mkdirSync7, writeFileSync as writeFileSync9 } from "fs";
|
|
8634
8700
|
import { stringify } from "yaml";
|
|
8635
8701
|
var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
|
|
8636
8702
|
function shouldRunWizard(opts) {
|
|
@@ -8715,7 +8781,7 @@ ${routeBlock}`;
|
|
|
8715
8781
|
function writeNewConfig(path, text) {
|
|
8716
8782
|
mkdirSync7(dirname8(path), { recursive: true });
|
|
8717
8783
|
try {
|
|
8718
|
-
|
|
8784
|
+
writeFileSync9(path, text, { mode: 384, flag: "wx" });
|
|
8719
8785
|
return true;
|
|
8720
8786
|
} catch (e) {
|
|
8721
8787
|
if (e.code === "EEXIST") return false;
|
|
@@ -8747,7 +8813,7 @@ Wrote ${o.configPath}.
|
|
|
8747
8813
|
}
|
|
8748
8814
|
|
|
8749
8815
|
// src/release-run.ts
|
|
8750
|
-
import { readFileSync as
|
|
8816
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync16, copyFileSync, mkdirSync as mkdirSync8, mkdtempSync, rmSync as rmSync6 } from "fs";
|
|
8751
8817
|
import { dirname as dirname9, join as join17 } from "path";
|
|
8752
8818
|
import { tmpdir as tmpdir2 } from "os";
|
|
8753
8819
|
|
|
@@ -9412,7 +9478,7 @@ async function bump(o, size) {
|
|
|
9412
9478
|
o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
|
|
9413
9479
|
return null;
|
|
9414
9480
|
}
|
|
9415
|
-
const current = JSON.parse(
|
|
9481
|
+
const current = JSON.parse(readFileSync13(first, "utf8")).version;
|
|
9416
9482
|
if (!current) {
|
|
9417
9483
|
o.emit.error(`no version field in ${o.repo.release.versionFiles[0]}`);
|
|
9418
9484
|
return null;
|
|
@@ -9425,13 +9491,13 @@ async function bump(o, size) {
|
|
|
9425
9491
|
return version;
|
|
9426
9492
|
}
|
|
9427
9493
|
for (const f of files) {
|
|
9428
|
-
const src =
|
|
9494
|
+
const src = readFileSync13(f, "utf8");
|
|
9429
9495
|
const out = src.replace(/("version"\s*:\s*")[^"]*(")/, `$1${version}$2`);
|
|
9430
9496
|
if (out === src) {
|
|
9431
9497
|
o.emit.error(`no version field in ${f}`);
|
|
9432
9498
|
return null;
|
|
9433
9499
|
}
|
|
9434
|
-
|
|
9500
|
+
writeFileSync10(f, out);
|
|
9435
9501
|
}
|
|
9436
9502
|
return version;
|
|
9437
9503
|
}
|
|
@@ -9444,8 +9510,8 @@ function writeChangelog(o, version, entries) {
|
|
|
9444
9510
|
o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
|
|
9445
9511
|
return;
|
|
9446
9512
|
}
|
|
9447
|
-
const existing = existsSync16(path) ?
|
|
9448
|
-
|
|
9513
|
+
const existing = existsSync16(path) ? readFileSync13(path, "utf8") : "# Changelog\n";
|
|
9514
|
+
writeFileSync10(path, insertChangelogSection(existing, section));
|
|
9449
9515
|
}
|
|
9450
9516
|
async function confirm(o, expected) {
|
|
9451
9517
|
if (!o.repo.hooks.released) return void 0;
|
|
@@ -10824,6 +10890,18 @@ function slugFor(route, agentId) {
|
|
|
10824
10890
|
function seatAgentId(route, role) {
|
|
10825
10891
|
return route.resolved?.agentPersonas?.[role]?.agentId;
|
|
10826
10892
|
}
|
|
10893
|
+
async function resolveReleaseKey(route, getStore = getSessionStore2) {
|
|
10894
|
+
const ship = (reason) => ({ kind: "ship", apiKey: resolveApiKey(route), reason });
|
|
10895
|
+
const agentId = route.resolved?.releaseAgentId;
|
|
10896
|
+
if (!agentId) return ship("release seat has no linked Agents row (run `crew agents sync`)");
|
|
10897
|
+
try {
|
|
10898
|
+
const session = await getStore().get(hostFromUrl2(route.baseUrl), slugFor(route, agentId));
|
|
10899
|
+
if (session?.apiKey) return { kind: "agent", apiKey: session.apiKey, agentId };
|
|
10900
|
+
return ship("no agent key stored for the release seat (run `crew agents sync`)");
|
|
10901
|
+
} catch {
|
|
10902
|
+
return ship("OS keychain unavailable");
|
|
10903
|
+
}
|
|
10904
|
+
}
|
|
10827
10905
|
async function resolveSeatKey(route, role, getStore = getSessionStore2) {
|
|
10828
10906
|
const ship = (reason) => ({ kind: "ship", apiKey: resolveApiKey(route), reason });
|
|
10829
10907
|
if (!SEAT_KEY_ROLES.includes(role)) return ship(`${role} always writes as the ship`);
|
|
@@ -10842,12 +10920,15 @@ async function mintMissingSeatKeys(route, o) {
|
|
|
10842
10920
|
if (!route.resolved) return out;
|
|
10843
10921
|
const resolved = route.resolved;
|
|
10844
10922
|
const doFetch = o.fetchImpl ?? fetch;
|
|
10845
|
-
const
|
|
10923
|
+
const targets = [
|
|
10924
|
+
...SEAT_KEY_ROLES.filter((r) => resolved.seats[r]).map((role) => ({ role, agentId: seatAgentId(route, role) })),
|
|
10925
|
+
...resolved.releaseSeat ? [{ role: "release", agentId: resolved.releaseAgentId }] : []
|
|
10926
|
+
];
|
|
10846
10927
|
let store;
|
|
10847
10928
|
try {
|
|
10848
10929
|
store = (o.getStore ?? getSessionStore2)();
|
|
10849
10930
|
} catch (e) {
|
|
10850
|
-
return
|
|
10931
|
+
return targets.map(({ role }) => ({
|
|
10851
10932
|
role,
|
|
10852
10933
|
action: "failed",
|
|
10853
10934
|
detail: `OS keychain unavailable: ${e.message}`
|
|
@@ -10855,8 +10936,7 @@ async function mintMissingSeatKeys(route, o) {
|
|
|
10855
10936
|
}
|
|
10856
10937
|
const host = hostFromUrl2(route.baseUrl);
|
|
10857
10938
|
const base = route.baseUrl.replace(/\/+$/, "");
|
|
10858
|
-
for (const role of
|
|
10859
|
-
const agentId = seatAgentId(route, role);
|
|
10939
|
+
for (const { role, agentId } of targets) {
|
|
10860
10940
|
if (!agentId) {
|
|
10861
10941
|
out.push({ role, action: "no_agent" });
|
|
10862
10942
|
continue;
|
|
@@ -11414,13 +11494,13 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
|
|
|
11414
11494
|
}
|
|
11415
11495
|
|
|
11416
11496
|
// src/cli.ts
|
|
11417
|
-
import { readFileSync as
|
|
11497
|
+
import { readFileSync as readFileSync18, existsSync as existsSync24, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync2, statSync as statSync9 } from "fs";
|
|
11418
11498
|
import { createInterface } from "readline/promises";
|
|
11419
11499
|
import { dirname as dirOf, resolve as resolvePath } from "path";
|
|
11420
11500
|
|
|
11421
11501
|
// src/ssh-keys.ts
|
|
11422
11502
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
11423
|
-
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as
|
|
11503
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync14, chmodSync } from "fs";
|
|
11424
11504
|
import { dirname as dirname11, join as join19 } from "path";
|
|
11425
11505
|
var SshKeyError = class extends Error {
|
|
11426
11506
|
};
|
|
@@ -11454,7 +11534,7 @@ function ensureShipSshKeypair(stateDir) {
|
|
|
11454
11534
|
if (!existsSync20(publicKeyPath)) {
|
|
11455
11535
|
throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
|
|
11456
11536
|
}
|
|
11457
|
-
const publicKey =
|
|
11537
|
+
const publicKey = readFileSync14(publicKeyPath, "utf8").trim();
|
|
11458
11538
|
return { privateKeyPath, publicKeyPath, publicKey };
|
|
11459
11539
|
}
|
|
11460
11540
|
function sshKeygenAvailable() {
|
|
@@ -11652,7 +11732,9 @@ function passengerCheckoutSyncSpecs(cfg, workspaceId) {
|
|
|
11652
11732
|
dir: target.dir,
|
|
11653
11733
|
remote: target.remote,
|
|
11654
11734
|
gitRemoteName: effective.branch.remote,
|
|
11655
|
-
base: effective.branch.base
|
|
11735
|
+
base: effective.branch.base,
|
|
11736
|
+
originUrl: ordinary ? originUrlOf(ordinary.dir, effective.branch.remote) : null,
|
|
11737
|
+
cloneScheme: cfg.ship.cloneScheme
|
|
11656
11738
|
});
|
|
11657
11739
|
}
|
|
11658
11740
|
}
|
|
@@ -11662,13 +11744,13 @@ function syncAllPassengerCheckouts(cfg) {
|
|
|
11662
11744
|
return passengerCheckoutSyncSpecs(cfg).map((spec) => ({
|
|
11663
11745
|
name: spec.name,
|
|
11664
11746
|
workspaceId: spec.workspaceId,
|
|
11665
|
-
outcome: syncPassengerCheckout(spec.dir, spec.remote, spec.gitRemoteName, spec.base)
|
|
11747
|
+
outcome: syncPassengerCheckout(spec.dir, spec.remote, spec.gitRemoteName, spec.base, { originUrl: spec.originUrl, scheme: spec.cloneScheme })
|
|
11666
11748
|
}));
|
|
11667
11749
|
}
|
|
11668
11750
|
|
|
11669
11751
|
// src/tunnel.ts
|
|
11670
11752
|
import { spawn as spawn3 } from "child_process";
|
|
11671
|
-
import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as
|
|
11753
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync11, unlinkSync as unlinkSync3, readdirSync as readdirSync5 } from "fs";
|
|
11672
11754
|
import { randomInt } from "crypto";
|
|
11673
11755
|
import { join as join20 } from "path";
|
|
11674
11756
|
var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
|
|
@@ -11743,7 +11825,7 @@ function readPersistedTunnel(stateDir, workspaceId) {
|
|
|
11743
11825
|
const path = tunnelStatePath(stateDir, workspaceId);
|
|
11744
11826
|
if (!existsSync21(path)) return void 0;
|
|
11745
11827
|
try {
|
|
11746
|
-
return JSON.parse(
|
|
11828
|
+
return JSON.parse(readFileSync15(path, "utf8"));
|
|
11747
11829
|
} catch {
|
|
11748
11830
|
return void 0;
|
|
11749
11831
|
}
|
|
@@ -11751,7 +11833,7 @@ function readPersistedTunnel(stateDir, workspaceId) {
|
|
|
11751
11833
|
function writePersistedTunnel(stateDir, state) {
|
|
11752
11834
|
const path = tunnelStatePath(stateDir, state.workspaceId);
|
|
11753
11835
|
mkdirSync10(join20(stateDir, "passengers"), { recursive: true });
|
|
11754
|
-
|
|
11836
|
+
writeFileSync11(path, `${JSON.stringify(state, null, 2)}
|
|
11755
11837
|
`);
|
|
11756
11838
|
}
|
|
11757
11839
|
function updatePersistedTunnel(stateDir, workspaceId, patch2) {
|
|
@@ -11970,7 +12052,7 @@ async function unregisterEndpoint(workspaceId, info, d) {
|
|
|
11970
12052
|
import { createServer as createHttpServer } from "http";
|
|
11971
12053
|
import { spawn as spawn4 } from "child_process";
|
|
11972
12054
|
import { randomBytes } from "crypto";
|
|
11973
|
-
import { existsSync as existsSync22, mkdirSync as mkdirSync11, readFileSync as
|
|
12055
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync12, unlinkSync as unlinkSync4, readdirSync as readdirSync6 } from "fs";
|
|
11974
12056
|
import { join as join21 } from "path";
|
|
11975
12057
|
|
|
11976
12058
|
// src/secret-compare.ts
|
|
@@ -12025,7 +12107,7 @@ function createSyncDaemonServer(targets, secret) {
|
|
|
12025
12107
|
try {
|
|
12026
12108
|
const synced = targets.map((t) => ({
|
|
12027
12109
|
name: t.name,
|
|
12028
|
-
outcome: syncPassengerCheckout(t.dir, t.remote, t.gitRemoteName, t.base)
|
|
12110
|
+
outcome: syncPassengerCheckout(t.dir, t.remote, t.gitRemoteName, t.base, { originUrl: t.originUrl, scheme: t.cloneScheme })
|
|
12029
12111
|
}));
|
|
12030
12112
|
res.writeHead(200, { "Content-Type": "application/json" }).end(JSON.stringify({ synced }));
|
|
12031
12113
|
} catch (error) {
|
|
@@ -12066,7 +12148,7 @@ function readPersistedSyncDaemon(stateDir, workspaceId) {
|
|
|
12066
12148
|
const path = syncDaemonStatePath(stateDir, workspaceId);
|
|
12067
12149
|
if (!existsSync22(path)) return void 0;
|
|
12068
12150
|
try {
|
|
12069
|
-
return JSON.parse(
|
|
12151
|
+
return JSON.parse(readFileSync16(path, "utf8"));
|
|
12070
12152
|
} catch {
|
|
12071
12153
|
return void 0;
|
|
12072
12154
|
}
|
|
@@ -12074,7 +12156,7 @@ function readPersistedSyncDaemon(stateDir, workspaceId) {
|
|
|
12074
12156
|
function writePersistedSyncDaemon(stateDir, state) {
|
|
12075
12157
|
const path = syncDaemonStatePath(stateDir, state.workspaceId);
|
|
12076
12158
|
mkdirSync11(join21(stateDir, "passengers"), { recursive: true });
|
|
12077
|
-
|
|
12159
|
+
writeFileSync12(path, `${JSON.stringify(state, null, 2)}
|
|
12078
12160
|
`);
|
|
12079
12161
|
}
|
|
12080
12162
|
function removePersistedSyncDaemon(stateDir, workspaceId) {
|
|
@@ -12152,7 +12234,7 @@ function syncPassengerSyncDaemons(plans, cfg, stateDir, deps = {}) {
|
|
|
12152
12234
|
}
|
|
12153
12235
|
|
|
12154
12236
|
// src/daemon.ts
|
|
12155
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync12, readFileSync as
|
|
12237
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync12, readFileSync as readFileSync17, readdirSync as readdirSync7, rmSync as rmSync8, statSync as statSync8, writeFileSync as writeFileSync13 } from "fs";
|
|
12156
12238
|
import { join as join22 } from "path";
|
|
12157
12239
|
var DEFAULT_FLOOR_MS = 5e3;
|
|
12158
12240
|
var DEFAULT_CEILING_MS = INTERVAL_SECONDS * 1e3;
|
|
@@ -12212,12 +12294,12 @@ function daemonStampFor(stateDir) {
|
|
|
12212
12294
|
function noteDaemonStarted(stateDir, now = Date.now()) {
|
|
12213
12295
|
mkdirSync12(stateDir, { recursive: true });
|
|
12214
12296
|
rmSync8(reloadSentinelFor(stateDir), { force: true });
|
|
12215
|
-
|
|
12297
|
+
writeFileSync13(daemonStampFor(stateDir), `${JSON.stringify({ pid: process.pid, startedAt: now })}
|
|
12216
12298
|
`);
|
|
12217
12299
|
}
|
|
12218
12300
|
function readDaemonStarted(stateDir) {
|
|
12219
12301
|
try {
|
|
12220
|
-
const v = JSON.parse(
|
|
12302
|
+
const v = JSON.parse(readFileSync17(daemonStampFor(stateDir), "utf8")).startedAt;
|
|
12221
12303
|
return typeof v === "number" ? v : null;
|
|
12222
12304
|
} catch {
|
|
12223
12305
|
return null;
|
|
@@ -12854,6 +12936,15 @@ if (command === "passenger-sync-daemon") {
|
|
|
12854
12936
|
}
|
|
12855
12937
|
try {
|
|
12856
12938
|
for (const r of syncAllPassengerCheckouts(cfg)) {
|
|
12939
|
+
if (r.outcome.action === "clone-failed") {
|
|
12940
|
+
if (!r.outcome.repeat) {
|
|
12941
|
+
emit.warn(
|
|
12942
|
+
`passenger checkout for ${r.name} could not be cloned (not retried until its clone URL changes): ${r.outcome.detail}`,
|
|
12943
|
+
{ step: "passengers", data: { workspaceId: r.workspaceId, repo: r.name } }
|
|
12944
|
+
);
|
|
12945
|
+
}
|
|
12946
|
+
continue;
|
|
12947
|
+
}
|
|
12857
12948
|
if (r.outcome.action === "diverged" || r.outcome.action === "ff-failed") {
|
|
12858
12949
|
emit.warn(
|
|
12859
12950
|
`passenger checkout for ${r.name} is stale: ${r.outcome.detail}`,
|
|
@@ -13084,7 +13175,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
13084
13175
|
for (const a of plan.actions) {
|
|
13085
13176
|
if (a.kind === "enable") {
|
|
13086
13177
|
if (prompter && !await prompter.confirm(`Route ${a.route} passes every check \u2014 set enabled: true in ${cfg.configFile}?`, false)) continue;
|
|
13087
|
-
|
|
13178
|
+
writeFileSync14(cfg.configFile, planEnable(readFileSync18(cfg.configFile, "utf8"), a.route));
|
|
13088
13179
|
enabledSound.add(a.route);
|
|
13089
13180
|
changed2.push(`enabled route ${a.route} in ${cfg.configFile}`);
|
|
13090
13181
|
} else {
|
|
@@ -13220,7 +13311,9 @@ ${changed2.map((c) => ` - ${c}
|
|
|
13220
13311
|
return { scope, tests: "skipped", outcome: "skipped", detail: note };
|
|
13221
13312
|
}
|
|
13222
13313
|
if (!dryRun && !c.enabled) return { scope, tests: "skipped", outcome: "skipped", detail: "route not enabled" };
|
|
13223
|
-
const
|
|
13314
|
+
const releaseIdentity = await resolveReleaseKey(c);
|
|
13315
|
+
if (releaseIdentity.kind === "ship") remit.warn(`release writes as the ship, not its own agent: ${releaseIdentity.reason}`, { step: "release" });
|
|
13316
|
+
const tracker = new Tracker(c, cfg.ship, { apiKey: releaseIdentity.apiKey });
|
|
13224
13317
|
if (!dryRun) {
|
|
13225
13318
|
const force2 = command === "deploy" || flag("force");
|
|
13226
13319
|
let seen = precheckTickets.get(c.route);
|
|
@@ -14177,15 +14270,17 @@ ${changed2.map((c) => ` - ${c}
|
|
|
14177
14270
|
`);
|
|
14178
14271
|
if (!dryRun) {
|
|
14179
14272
|
const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
|
|
14180
|
-
const raw = existsSync24(resolvedPath) ? JSON.parse(
|
|
14273
|
+
const raw = existsSync24(resolvedPath) ? JSON.parse(readFileSync18(resolvedPath, "utf8")) : {};
|
|
14181
14274
|
raw.agentPersonas = personaResult.agentPersonas;
|
|
14182
14275
|
raw.agentSkills = skillResult.agentSkills;
|
|
14276
|
+
if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
|
|
14183
14277
|
mkdirSync13(dirOf(resolvedPath), { recursive: true });
|
|
14184
|
-
|
|
14278
|
+
writeFileSync14(resolvedPath, `${JSON.stringify(raw, null, 2)}
|
|
14185
14279
|
`);
|
|
14186
14280
|
}
|
|
14187
14281
|
if (!dryRun) {
|
|
14188
14282
|
target.resolved.agentPersonas = personaResult.agentPersonas;
|
|
14283
|
+
if (personaResult.releaseAgentId) target.resolved.releaseAgentId = personaResult.releaseAgentId;
|
|
14189
14284
|
}
|
|
14190
14285
|
for (const o of await mintMissingSeatKeys(target, { userAgent: cfg.ship.userAgent, dryRun, shipName: cfg.ship.name })) {
|
|
14191
14286
|
process.stdout.write(`${describeMintOutcome(o)}
|
|
@@ -14235,10 +14330,10 @@ ${changed2.map((c) => ` - ${c}
|
|
|
14235
14330
|
`);
|
|
14236
14331
|
if (!dryRun) {
|
|
14237
14332
|
const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
|
|
14238
|
-
const raw = existsSync24(resolvedPath) ? JSON.parse(
|
|
14333
|
+
const raw = existsSync24(resolvedPath) ? JSON.parse(readFileSync18(resolvedPath, "utf8")) : {};
|
|
14239
14334
|
raw.agentSkills = result.agentSkills;
|
|
14240
14335
|
mkdirSync13(dirOf(resolvedPath), { recursive: true });
|
|
14241
|
-
|
|
14336
|
+
writeFileSync14(resolvedPath, `${JSON.stringify(raw, null, 2)}
|
|
14242
14337
|
`);
|
|
14243
14338
|
}
|
|
14244
14339
|
if (result.outcomes.some((o) => o.action === "diverged")) process.exitCode = 1;
|
|
@@ -14251,7 +14346,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
14251
14346
|
process.exit(2);
|
|
14252
14347
|
};
|
|
14253
14348
|
if (sub !== "add" && sub !== "list") usage2();
|
|
14254
|
-
const configText =
|
|
14349
|
+
const configText = readFileSync18(cfg.configFile, "utf8");
|
|
14255
14350
|
let routeName = positional[2];
|
|
14256
14351
|
let addPath = ".";
|
|
14257
14352
|
let addName;
|
|
@@ -14275,7 +14370,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
14275
14370
|
let resolvedRemotes;
|
|
14276
14371
|
if (existsSync24(resolvedFile)) {
|
|
14277
14372
|
try {
|
|
14278
|
-
resolvedRemotes = JSON.parse(
|
|
14373
|
+
resolvedRemotes = JSON.parse(readFileSync18(resolvedFile, "utf8"));
|
|
14279
14374
|
} catch {
|
|
14280
14375
|
}
|
|
14281
14376
|
}
|
|
@@ -14347,14 +14442,14 @@ ${changed2.map((c) => ` - ${c}
|
|
|
14347
14442
|
process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
|
|
14348
14443
|
`);
|
|
14349
14444
|
if (existsSync24(resolvedFile)) {
|
|
14350
|
-
const raw = JSON.parse(
|
|
14445
|
+
const raw = JSON.parse(readFileSync18(resolvedFile, "utf8"));
|
|
14351
14446
|
raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
|
|
14352
14447
|
raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
|
|
14353
|
-
|
|
14448
|
+
writeFileSync14(resolvedFile, `${JSON.stringify(raw, null, 2)}
|
|
14354
14449
|
`);
|
|
14355
14450
|
}
|
|
14356
14451
|
}
|
|
14357
|
-
|
|
14452
|
+
writeFileSync14(cfg.configFile, plan.text);
|
|
14358
14453
|
process.stdout.write(`added ${top} to ${routeName} as ${plan.mode === "dir" ? "dir" : `repos.${plan.name}`} in ${cfg.configFile}
|
|
14359
14454
|
`);
|
|
14360
14455
|
} catch (e) {
|
|
@@ -14596,7 +14691,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
14596
14691
|
const hadPreviousFile = existsSync24(resolvedPath);
|
|
14597
14692
|
if (hadPreviousFile) {
|
|
14598
14693
|
try {
|
|
14599
|
-
previous = JSON.parse(
|
|
14694
|
+
previous = JSON.parse(readFileSync18(resolvedPath, "utf8"));
|
|
14600
14695
|
} catch {
|
|
14601
14696
|
}
|
|
14602
14697
|
}
|
|
@@ -14651,7 +14746,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
14651
14746
|
// role as "diverged by definition" on the next sync even though
|
|
14652
14747
|
// nothing about its prompt actually changed.
|
|
14653
14748
|
...previous?.agentPersonas ? { agentPersonas: previous.agentPersonas } : {},
|
|
14654
|
-
...previous?.agentSkills ? { agentSkills: previous.agentSkills } : {}
|
|
14749
|
+
...previous?.agentSkills ? { agentSkills: previous.agentSkills } : {},
|
|
14750
|
+
...previous?.releaseAgentId ? { releaseAgentId: previous.releaseAgentId } : {}
|
|
14655
14751
|
};
|
|
14656
14752
|
if (dryRun) {
|
|
14657
14753
|
process.stderr.write(`(dry run) would write resolved ids to ${resolvedPath}
|
|
@@ -14660,7 +14756,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
14660
14756
|
mkdirSync13(dirname12(resolvedPath), { recursive: true });
|
|
14661
14757
|
if (hadPreviousFile) copyFileSync2(resolvedPath, `${resolvedPath}.bak`);
|
|
14662
14758
|
const tmpPath = `${resolvedPath}.tmp${process.pid}`;
|
|
14663
|
-
|
|
14759
|
+
writeFileSync14(tmpPath, `${JSON.stringify(resolved, null, 2)}
|
|
14664
14760
|
`);
|
|
14665
14761
|
renameSync2(tmpPath, resolvedPath);
|
|
14666
14762
|
process.stderr.write(`Resolved ids written to ${resolvedPath}
|
|
@@ -14686,7 +14782,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
14686
14782
|
`);
|
|
14687
14783
|
} else {
|
|
14688
14784
|
mkdirSync13(dirname12(apiKeyFile), { recursive: true });
|
|
14689
|
-
|
|
14785
|
+
writeFileSync14(apiKeyFile, `${apiKeyVar}=${apiKey}
|
|
14690
14786
|
`, { mode: 384 });
|
|
14691
14787
|
process.stderr.write(`API key written to ${apiKeyFile}
|
|
14692
14788
|
`);
|
|
@@ -15224,7 +15320,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
15224
15320
|
break;
|
|
15225
15321
|
}
|
|
15226
15322
|
mkdirSync13(cfg.ship.stateDir, { recursive: true });
|
|
15227
|
-
|
|
15323
|
+
writeFileSync14(sentinel, `${(/* @__PURE__ */ new Date()).toISOString()}
|
|
15228
15324
|
`);
|
|
15229
15325
|
process.stdout.write("crew reload: requested \u2014 the daemon finishes its in-flight pass, then relaunches with the new config\n");
|
|
15230
15326
|
if (flag("now") && control?.stopCommand && control.startCommand) {
|
|
@@ -15275,7 +15371,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
15275
15371
|
}
|
|
15276
15372
|
case "log": {
|
|
15277
15373
|
try {
|
|
15278
|
-
const text =
|
|
15374
|
+
const text = readFileSync18(cfg.ship.logFile, "utf8").split("\n");
|
|
15279
15375
|
process.stdout.write(`${text.slice(-(Number(value("tail")) || 40)).join("\n")}
|
|
15280
15376
|
`);
|
|
15281
15377
|
} catch {
|
|
@@ -15556,6 +15652,13 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
|
|
|
15556
15652
|
const id = await resolveSeatKey(route, role);
|
|
15557
15653
|
process.stdout.write(
|
|
15558
15654
|
`seat identity: ${role} writes as ${id.kind === "agent" ? `its own agent (${id.agentId})` : `the ship (${id.reason})`}
|
|
15655
|
+
`
|
|
15656
|
+
);
|
|
15657
|
+
}
|
|
15658
|
+
if (route.resolved?.releaseSeat) {
|
|
15659
|
+
const id = await resolveReleaseKey(route);
|
|
15660
|
+
process.stdout.write(
|
|
15661
|
+
`seat identity: release writes as ${id.kind === "agent" ? `its own agent (${id.agentId})` : `the ship (${id.reason})`}
|
|
15559
15662
|
`
|
|
15560
15663
|
);
|
|
15561
15664
|
}
|
package/package.json
CHANGED