@tablation/crew 0.83.0 → 0.84.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 +61 -5
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -34,6 +34,29 @@ function doctorBaseReport(r, base, health) {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
// src/periscopes.ts
|
|
38
|
+
import { lookup } from "dns/promises";
|
|
39
|
+
function periscopeRoutes(cfg) {
|
|
40
|
+
return cfg.routes.filter((r) => r.enabled && r.hostPeriscopes).map((r) => r.route);
|
|
41
|
+
}
|
|
42
|
+
function periscopeSummary(cfg) {
|
|
43
|
+
const routes = periscopeRoutes(cfg);
|
|
44
|
+
const limits = `max ${cfg.ship.maxPeriscopes}, idle ${cfg.ship.periscopeIdleMinutes} min`;
|
|
45
|
+
if (routes.length === 0) return `off (no enabled route sets hostPeriscopes: true; ${limits})`;
|
|
46
|
+
return `ON for ${routes.join(", ")} (host ${cfg.ship.previewHost ?? "NOT SET (ship.previewHost)"}, ${limits})`;
|
|
47
|
+
}
|
|
48
|
+
async function previewHostProblem(cfg, resolve6 = (h) => lookup(h)) {
|
|
49
|
+
if (periscopeRoutes(cfg).length === 0) return void 0;
|
|
50
|
+
const host = cfg.ship.previewHost;
|
|
51
|
+
if (!host) return "ship.previewHost is not set \u2014 a route hosts periscopes, so Passengers have no host to be sent to";
|
|
52
|
+
try {
|
|
53
|
+
await resolve6(host);
|
|
54
|
+
} catch {
|
|
55
|
+
return `ship.previewHost "${host}" does not resolve from this machine`;
|
|
56
|
+
}
|
|
57
|
+
return void 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
37
60
|
// src/config.ts
|
|
38
61
|
import { readFileSync as readFileSync8, existsSync as existsSync7 } from "fs";
|
|
39
62
|
import { resolve, dirname as dirname3, isAbsolute, join as join9, basename as basename3 } from "path";
|
|
@@ -2034,6 +2057,8 @@ var ROLE_LABEL = {
|
|
|
2034
2057
|
};
|
|
2035
2058
|
var ConfigError = class extends Error {
|
|
2036
2059
|
};
|
|
2060
|
+
var DEFAULT_MAX_PERISCOPES = 3;
|
|
2061
|
+
var DEFAULT_PERISCOPE_IDLE_MINUTES = 120;
|
|
2037
2062
|
function parseCloneScheme(v) {
|
|
2038
2063
|
if (v === void 0 || v === null || v === "") return void 0;
|
|
2039
2064
|
if (v === "ssh" || v === "https") return v;
|
|
@@ -2065,7 +2090,10 @@ var SHIP_KEYS = /* @__PURE__ */ new Set([
|
|
|
2065
2090
|
"relayHttpPort",
|
|
2066
2091
|
"relayPublicDomain",
|
|
2067
2092
|
"passengerImage",
|
|
2068
|
-
"cloneScheme"
|
|
2093
|
+
"cloneScheme",
|
|
2094
|
+
"previewHost",
|
|
2095
|
+
"maxPeriscopes",
|
|
2096
|
+
"periscopeIdleMinutes"
|
|
2069
2097
|
]);
|
|
2070
2098
|
var ROUTE_KEYS = /* @__PURE__ */ new Set([
|
|
2071
2099
|
"route",
|
|
@@ -2088,6 +2116,7 @@ var ROUTE_KEYS = /* @__PURE__ */ new Set([
|
|
|
2088
2116
|
"resolved",
|
|
2089
2117
|
"promptSet",
|
|
2090
2118
|
"hostPassengers",
|
|
2119
|
+
"hostPeriscopes",
|
|
2091
2120
|
"toolchain"
|
|
2092
2121
|
]);
|
|
2093
2122
|
function parseWorktreeName(v, where, file) {
|
|
@@ -2399,6 +2428,14 @@ Create the first of those:
|
|
|
2399
2428
|
if (!Number.isInteger(streamRetentionDays) || streamRetentionDays < 1) {
|
|
2400
2429
|
missing.add(`ship.streamRetentionDays must be a positive integer (got "${shipRaw.streamRetentionDays}")`);
|
|
2401
2430
|
}
|
|
2431
|
+
const maxPeriscopes = shipRaw.maxPeriscopes === void 0 ? DEFAULT_MAX_PERISCOPES : Number(shipRaw.maxPeriscopes);
|
|
2432
|
+
if (!Number.isInteger(maxPeriscopes) || maxPeriscopes < 1) {
|
|
2433
|
+
missing.add(`ship.maxPeriscopes must be a positive integer (got "${shipRaw.maxPeriscopes}")`);
|
|
2434
|
+
}
|
|
2435
|
+
const periscopeIdleMinutes = shipRaw.periscopeIdleMinutes === void 0 ? DEFAULT_PERISCOPE_IDLE_MINUTES : Number(shipRaw.periscopeIdleMinutes);
|
|
2436
|
+
if (!Number.isInteger(periscopeIdleMinutes) || periscopeIdleMinutes < 1) {
|
|
2437
|
+
missing.add(`ship.periscopeIdleMinutes must be a positive integer (got "${shipRaw.periscopeIdleMinutes}")`);
|
|
2438
|
+
}
|
|
2402
2439
|
const eventsRotateBytes = shipRaw.eventsRotateBytes === void 0 ? DEFAULT_EVENTS_ROTATE_BYTES : Number(shipRaw.eventsRotateBytes);
|
|
2403
2440
|
if (!Number.isInteger(eventsRotateBytes) || eventsRotateBytes < 1) {
|
|
2404
2441
|
missing.add(`ship.eventsRotateBytes must be a positive integer (got "${shipRaw.eventsRotateBytes}")`);
|
|
@@ -2468,7 +2505,10 @@ Create the first of those:
|
|
|
2468
2505
|
relayHttpPort: shipRaw.relayHttpPort !== void 0 ? Number(shipRaw.relayHttpPort) : 443,
|
|
2469
2506
|
relayPublicDomain: shipRaw.relayPublicDomain ? String(shipRaw.relayPublicDomain) : void 0,
|
|
2470
2507
|
passengerImage: shipRaw.passengerImage ? String(shipRaw.passengerImage) : void 0,
|
|
2471
|
-
cloneScheme: parseCloneScheme(shipRaw.cloneScheme)
|
|
2508
|
+
cloneScheme: parseCloneScheme(shipRaw.cloneScheme),
|
|
2509
|
+
previewHost: shipRaw.previewHost ? String(shipRaw.previewHost).trim() || void 0 : void 0,
|
|
2510
|
+
maxPeriscopes,
|
|
2511
|
+
periscopeIdleMinutes
|
|
2472
2512
|
},
|
|
2473
2513
|
routes,
|
|
2474
2514
|
crewHome,
|
|
@@ -2569,6 +2609,9 @@ function parseOneRoute(c, where, file, base, stateDir, raw, missing, crewHome, t
|
|
|
2569
2609
|
missing.add(`${where}.toolchain "${toolchain}" is not defined in ship.toolchains (have: ${[...toolchainNames].join(", ")})`);
|
|
2570
2610
|
}
|
|
2571
2611
|
const route = missing.req(c?.route, `${where}.route`);
|
|
2612
|
+
if (c?.hostPeriscopes === true && c?.hostPassengers !== true) {
|
|
2613
|
+
missing.add(`${where} (route "${route}") sets hostPeriscopes: true without hostPassengers: true \u2014 a Passenger must be able to board before they can open a periscope`);
|
|
2614
|
+
}
|
|
2572
2615
|
const routeParts = route ? route.split("/") : [];
|
|
2573
2616
|
const routeWellFormed = routeParts.length === 2 && routeParts.every(Boolean);
|
|
2574
2617
|
if (route && !routeWellFormed) {
|
|
@@ -2643,7 +2686,8 @@ function parseOneRoute(c, where, file, base, stateDir, raw, missing, crewHome, t
|
|
|
2643
2686
|
},
|
|
2644
2687
|
release: parseReleaseOverride(c?.release, where, file),
|
|
2645
2688
|
branch: parseBranchOverride(c?.branch, where, file),
|
|
2646
|
-
hostPassengers: c?.hostPassengers === true
|
|
2689
|
+
hostPassengers: c?.hostPassengers === true,
|
|
2690
|
+
hostPeriscopes: c?.hostPeriscopes === true
|
|
2647
2691
|
};
|
|
2648
2692
|
const resolvedFile = routeWellFormed ? readResolvedFile(stateDir, route) : void 0;
|
|
2649
2693
|
result.resolved = parseResolved(resolvedFile ?? c?.resolved, missing, where);
|
|
@@ -7762,7 +7806,7 @@ function skillMarkerNames(text) {
|
|
|
7762
7806
|
}
|
|
7763
7807
|
return names;
|
|
7764
7808
|
}
|
|
7765
|
-
function expandSkillMarkers(text,
|
|
7809
|
+
function expandSkillMarkers(text, lookup2, opts = {}) {
|
|
7766
7810
|
const maxDepth = opts.maxDepth ?? MAX_INCLUDE_DEPTH;
|
|
7767
7811
|
const maxBytes = opts.maxBytes ?? MAX_INCLUDED_BYTES;
|
|
7768
7812
|
const warnings = [];
|
|
@@ -7778,7 +7822,7 @@ function expandSkillMarkers(text, lookup, opts = {}) {
|
|
|
7778
7822
|
warnings.push(`skill include "${name}" left literal: nested deeper than ${maxDepth} levels`);
|
|
7779
7823
|
return literal;
|
|
7780
7824
|
}
|
|
7781
|
-
const body =
|
|
7825
|
+
const body = lookup2(name);
|
|
7782
7826
|
if (body === void 0) throw new SkillIncludeError(`no skill named "${name}" for the include ${literal}`);
|
|
7783
7827
|
const trimmed = body.replace(/^\n+/, "").replace(/\n+$/, "");
|
|
7784
7828
|
const bytes = Buffer.byteLength(trimmed, "utf8");
|
|
@@ -20385,6 +20429,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
20385
20429
|
platform: cfg.ship.platform,
|
|
20386
20430
|
machineId,
|
|
20387
20431
|
hostPassengers: firstRun?.answers.hostPassengers ?? existingRoute?.hostPassengers === true,
|
|
20432
|
+
hostPeriscopes: existingRoute?.hostPeriscopes === true,
|
|
20388
20433
|
sshPublicKey,
|
|
20389
20434
|
lastSyncedName: (r) => readSyncedName(resolvedPathFor(cfg.ship.stateDir, r))
|
|
20390
20435
|
};
|
|
@@ -21848,6 +21893,7 @@ ${owed.map((l) => ` ${l}
|
|
|
21848
21893
|
`ship: ${cfg.ship.name} (${cfg.ship.platform})
|
|
21849
21894
|
route: ${route.route} -> ${route.dir}
|
|
21850
21895
|
enabled: ${route.enabled}
|
|
21896
|
+
periscopes: ${periscopeSummary(cfg)}
|
|
21851
21897
|
crew: ${state.isPaused() ? "paused" : "active"}
|
|
21852
21898
|
release: ${state.releaseSkipReason() ? "paused" : "active"}
|
|
21853
21899
|
watermark: ${state.watermark()}
|
|
@@ -21965,6 +22011,8 @@ watermark: ${state.watermark()}
|
|
|
21965
22011
|
}
|
|
21966
22012
|
}
|
|
21967
22013
|
for (const message of roleCapWarnings(cfg.ship)) problems.push({ severity: "note", kind: "role_cap_above_ship_cap", message });
|
|
22014
|
+
const previewProblem2 = await previewHostProblem(cfg);
|
|
22015
|
+
if (previewProblem2) problems.push({ severity: "blocker", kind: "preview_host", message: previewProblem2 });
|
|
21968
22016
|
problems.push(...attentionProblems(openShipAttention(state), problems));
|
|
21969
22017
|
if (problems.some((p) => p.severity === "blocker")) process.exitCode = 1;
|
|
21970
22018
|
process.stdout.write(`${JSON.stringify({ offline: flag("offline"), pushAuth, tools: toolRows, problems, passed }, null, 2)}
|
|
@@ -21978,6 +22026,14 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
|
|
|
21978
22026
|
);
|
|
21979
22027
|
for (const w of roleCapWarnings(cfg.ship)) process.stdout.write(`role concurrency: WARN ${w}
|
|
21980
22028
|
`);
|
|
22029
|
+
process.stdout.write(`periscopes: ${periscopeSummary(cfg)}
|
|
22030
|
+
`);
|
|
22031
|
+
const previewProblem = await previewHostProblem(cfg);
|
|
22032
|
+
if (previewProblem) {
|
|
22033
|
+
process.stdout.write(` BLOCKER ${previewProblem}
|
|
22034
|
+
`);
|
|
22035
|
+
process.exitCode = 1;
|
|
22036
|
+
}
|
|
21981
22037
|
try {
|
|
21982
22038
|
const evFile = eventFileFor(cfg.ship.stateDir);
|
|
21983
22039
|
const evSize = existsSync35(evFile) ? statSync10(evFile).size : 0;
|
package/package.json
CHANGED