@tablation/crew 0.96.0 → 0.97.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 +146 -41
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -7278,6 +7278,18 @@ var Tracker = class {
|
|
|
7278
7278
|
if (!row) return;
|
|
7279
7279
|
await this.client.records.update(this.route.resolved.shipsModelId, row.id, patch2);
|
|
7280
7280
|
}
|
|
7281
|
+
/**
|
|
7282
|
+
* Writes (or, with `null`, clears) this ship's `relay_url` (CREW-2803). A
|
|
7283
|
+
* no-op when there is no row for this ship or the Ships table has not taken
|
|
7284
|
+
* the column (probed by the key's absence from the row), so the tunnel
|
|
7285
|
+
* still runs on an install that predates it.
|
|
7286
|
+
*/
|
|
7287
|
+
async updateRelayUrl(shipName2, url) {
|
|
7288
|
+
const row = await this.myShipRow(shipName2);
|
|
7289
|
+
if (!row || !("relay_url" in row)) return;
|
|
7290
|
+
if ((row.relay_url ?? null) === url) return;
|
|
7291
|
+
await this.client.records.update(this.route.resolved.shipsModelId, row.id, { relay_url: url });
|
|
7292
|
+
}
|
|
7281
7293
|
/** The other half of `beatEngaged` — clears engagement back to idle. */
|
|
7282
7294
|
async beatIdle(shipName2) {
|
|
7283
7295
|
const row = await this.myShipRow(shipName2);
|
|
@@ -18145,6 +18157,26 @@ function sshKeygenAvailable() {
|
|
|
18145
18157
|
}
|
|
18146
18158
|
}
|
|
18147
18159
|
|
|
18160
|
+
// src/relay-probe.ts
|
|
18161
|
+
async function probeRelayControl(relayUrl, apiKey, userAgent, fetchFn = fetch, timeoutMs = 1e4) {
|
|
18162
|
+
const started = Date.now();
|
|
18163
|
+
try {
|
|
18164
|
+
const res = await fetchFn(`${relayUrl.replace(/\/+$/, "")}/control/status`, {
|
|
18165
|
+
headers: { Authorization: `Bearer ${apiKey}`, "User-Agent": userAgent },
|
|
18166
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
18167
|
+
});
|
|
18168
|
+
const latencyMs = Date.now() - started;
|
|
18169
|
+
await res.arrayBuffer().catch(() => void 0);
|
|
18170
|
+
if (res.ok) return { ok: true, status: res.status, latencyMs };
|
|
18171
|
+
return { ok: false, status: res.status, error: `HTTP ${res.status}` };
|
|
18172
|
+
} catch (e) {
|
|
18173
|
+
return { ok: false, error: e.message };
|
|
18174
|
+
}
|
|
18175
|
+
}
|
|
18176
|
+
function describeRelayProbe(url, p) {
|
|
18177
|
+
return p.ok ? `relay control: OK ${url}/control/status (${p.status}, ${p.latencyMs} ms)` : `relay control: FAILED ${url}/control/status \u2014 ${p.error}`;
|
|
18178
|
+
}
|
|
18179
|
+
|
|
18148
18180
|
// src/passenger-containers.ts
|
|
18149
18181
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
18150
18182
|
import { createHash as createHash7 } from "crypto";
|
|
@@ -18362,15 +18394,21 @@ function generateTunnelSlug() {
|
|
|
18362
18394
|
for (let i = 0; i < SLUG_LENGTH; i++) out += SLUG_ALPHABET[randomInt(SLUG_ALPHABET.length)];
|
|
18363
18395
|
return out;
|
|
18364
18396
|
}
|
|
18365
|
-
function bindAddrFor(o) {
|
|
18366
|
-
const base = `workspace-${o.workspaceId}-slug-${o.slug}-key-${o.apiKey}`;
|
|
18367
|
-
return o.mcpSecret ? `${base}-secret-${o.mcpSecret}` : base;
|
|
18397
|
+
function bindAddrFor(o, path) {
|
|
18398
|
+
const base = `workspace-${o.workspaceId}-slug-${o.slug}${path ? `-path-${path}` : ""}-key-${o.apiKey}`;
|
|
18399
|
+
return o.mcpSecret && !path ? `${base}-secret-${o.mcpSecret}` : base;
|
|
18368
18400
|
}
|
|
18369
|
-
function
|
|
18401
|
+
function relayBaseUrlFor(slug, relayHost, relayHttpPort, relayPublicDomain) {
|
|
18370
18402
|
const domain = relayPublicDomain ?? relayHost.replace(/^.*@/, "");
|
|
18371
|
-
return `https://${slug}.${domain}${relayHttpPort === 443 ? "" : `:${relayHttpPort}`}
|
|
18403
|
+
return `https://${slug}.${domain}${relayHttpPort === 443 ? "" : `:${relayHttpPort}`}`;
|
|
18404
|
+
}
|
|
18405
|
+
function publicUrlFor(slug, relayHost, relayHttpPort, relayPublicDomain) {
|
|
18406
|
+
return `${relayBaseUrlFor(slug, relayHost, relayHttpPort, relayPublicDomain)}/mcp`;
|
|
18372
18407
|
}
|
|
18373
18408
|
function sshArgsFor(o) {
|
|
18409
|
+
const forwards = [];
|
|
18410
|
+
if (o.localPort !== void 0) forwards.push("-R", `${bindAddrFor(o)}:0:127.0.0.1:${o.localPort}`);
|
|
18411
|
+
if (o.controlSocket) forwards.push("-R", `${bindAddrFor(o, "control")}:0:${o.controlSocket}`);
|
|
18374
18412
|
return [
|
|
18375
18413
|
"-N",
|
|
18376
18414
|
"-o",
|
|
@@ -18385,8 +18423,7 @@ function sshArgsFor(o) {
|
|
|
18385
18423
|
o.privateKeyPath,
|
|
18386
18424
|
"-p",
|
|
18387
18425
|
String(o.relayPort),
|
|
18388
|
-
|
|
18389
|
-
`${bindAddrFor(o)}:0:127.0.0.1:${o.localPort}`,
|
|
18426
|
+
...forwards,
|
|
18390
18427
|
o.relayHost
|
|
18391
18428
|
];
|
|
18392
18429
|
}
|
|
@@ -18457,16 +18494,19 @@ function isPidAlive(pid) {
|
|
|
18457
18494
|
return false;
|
|
18458
18495
|
}
|
|
18459
18496
|
}
|
|
18497
|
+
var CONTROL_ONLY_HASH = "control-only";
|
|
18460
18498
|
function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, stateDir, mcpSecrets = /* @__PURE__ */ new Map(), deps = {}) {
|
|
18461
18499
|
const alive = deps.isPidAlive ?? isPidAlive;
|
|
18462
18500
|
const plannedIds = new Set(plans.map((p) => p.workspaceId));
|
|
18463
18501
|
for (const plan of plans) {
|
|
18464
18502
|
const existing = readPersistedTunnel(stateDir, plan.workspaceId);
|
|
18503
|
+
const mountsHash = plan.mountsHash ?? CONTROL_ONLY_HASH;
|
|
18504
|
+
const controlUnchanged = existing?.controlSocket === deps.controlSocket;
|
|
18465
18505
|
const currentSecret = mcpSecrets.get(plan.workspaceId);
|
|
18466
18506
|
const secretUnchanged = (existing?.mcpSecret ?? void 0) === (currentSecret ?? void 0);
|
|
18467
18507
|
const slug = existing?.slug ?? generateTunnelSlug();
|
|
18468
18508
|
const slugUnchanged = existing?.liveSlug === slug;
|
|
18469
|
-
if (existing && alive(existing.pid) && existing.mountsHash ===
|
|
18509
|
+
if (existing && alive(existing.pid) && existing.mountsHash === mountsHash && secretUnchanged && slugUnchanged && controlUnchanged) {
|
|
18470
18510
|
if (existing.status === "connecting") {
|
|
18471
18511
|
const settled = { ...existing, status: "connected" };
|
|
18472
18512
|
writePersistedTunnel(stateDir, settled);
|
|
@@ -18493,6 +18533,7 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
|
|
|
18493
18533
|
slug,
|
|
18494
18534
|
apiKey,
|
|
18495
18535
|
localPort: plan.port,
|
|
18536
|
+
controlSocket: deps.controlSocket,
|
|
18496
18537
|
privateKeyPath,
|
|
18497
18538
|
mcpSecret: currentSecret,
|
|
18498
18539
|
spawnFn: deps.spawnFn,
|
|
@@ -18502,9 +18543,10 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
|
|
|
18502
18543
|
});
|
|
18503
18544
|
const started = {
|
|
18504
18545
|
pid: tunnel.pid ?? -1,
|
|
18505
|
-
containerName: plan.containerName,
|
|
18546
|
+
containerName: plan.containerName ?? "",
|
|
18506
18547
|
workspaceId: plan.workspaceId,
|
|
18507
|
-
mountsHash
|
|
18548
|
+
mountsHash,
|
|
18549
|
+
controlSocket: deps.controlSocket,
|
|
18508
18550
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18509
18551
|
status: settledStatus,
|
|
18510
18552
|
mcpSecret: currentSecret,
|
|
@@ -20484,6 +20526,11 @@ if (command === "passenger-sync-daemon") {
|
|
|
20484
20526
|
}, parseTicketNumber = function(input) {
|
|
20485
20527
|
const m = /^(?:[A-Za-z]+-)?(\d+)$/.exec(input.trim());
|
|
20486
20528
|
return m ? m[1] : null;
|
|
20529
|
+
}, routeRelayUrl = function(route3) {
|
|
20530
|
+
const ws = route3.resolved?.workspaceId;
|
|
20531
|
+
const slug = ws ? readPersistedTunnel(cfg.ship.stateDir, ws)?.slug : void 0;
|
|
20532
|
+
if (!cfg.ship.relayHost || !slug) return null;
|
|
20533
|
+
return relayBaseUrlFor(slug, cfg.ship.relayHost, cfg.ship.relayHttpPort, cfg.ship.relayPublicDomain);
|
|
20487
20534
|
}, passengerApiKeys = function(routes) {
|
|
20488
20535
|
const keys = /* @__PURE__ */ new Map();
|
|
20489
20536
|
for (const r of routes) {
|
|
@@ -20531,7 +20578,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
20531
20578
|
const unsafe = baseBranchUnsafe(out);
|
|
20532
20579
|
return { action: out.action, unsafe, detail: unsafe ? describeUnsafeBase(out, remote, base) : out.detail };
|
|
20533
20580
|
};
|
|
20534
|
-
requireArmed2 = requireArmed, parseTicketNumber2 = parseTicketNumber, passengerApiKeys2 = passengerApiKeys, endpointApiFor2 = endpointApiFor, endpointReconcileDeps2 = endpointReconcileDeps, releaseRefused2 = releaseRefused, doctorMachineId2 = doctorMachineId, baseHealth2 = baseHealth;
|
|
20581
|
+
requireArmed2 = requireArmed, parseTicketNumber2 = parseTicketNumber, routeRelayUrl2 = routeRelayUrl, passengerApiKeys2 = passengerApiKeys, endpointApiFor2 = endpointApiFor, endpointReconcileDeps2 = endpointReconcileDeps, releaseRefused2 = releaseRefused, doctorMachineId2 = doctorMachineId, baseHealth2 = baseHealth;
|
|
20535
20582
|
let cfg;
|
|
20536
20583
|
let firstRun;
|
|
20537
20584
|
let firstRunFromFlags = false;
|
|
@@ -20664,7 +20711,10 @@ if (command === "passenger-sync-daemon") {
|
|
|
20664
20711
|
async function syncHostPassengers() {
|
|
20665
20712
|
if (dryRun) return;
|
|
20666
20713
|
const passengerRoutes = cfg.routes.filter((r) => r.enabled && r.hostPassengers);
|
|
20667
|
-
if (passengerRoutes.length === 0)
|
|
20714
|
+
if (passengerRoutes.length === 0) {
|
|
20715
|
+
await syncShipTunnels([], /* @__PURE__ */ new Map(), []);
|
|
20716
|
+
return;
|
|
20717
|
+
}
|
|
20668
20718
|
if (!dockerAvailable()) {
|
|
20669
20719
|
const pathProblem = dockerPathProblem(cfg.ship, COMMON_DOCKER_DIRS);
|
|
20670
20720
|
emit.warn(
|
|
@@ -20706,37 +20756,64 @@ if (command === "passenger-sync-daemon") {
|
|
|
20706
20756
|
{ step: "passengers", data: { ...result } }
|
|
20707
20757
|
);
|
|
20708
20758
|
}
|
|
20709
|
-
|
|
20710
|
-
try {
|
|
20711
|
-
const { privateKeyPath } = ensureShipSshKeypair(cfg.ship.stateDir);
|
|
20712
|
-
const plans = planContainers(cfg);
|
|
20713
|
-
const mcpSecrets = new Map([...syncEndpoints].map(([workspaceId, e]) => [workspaceId, e.secret]));
|
|
20714
|
-
const relayHost = cfg.ship.relayHost;
|
|
20715
|
-
const reconcile = endpointReconcileDeps(passengerRoutes, relayHost);
|
|
20716
|
-
const pending = [];
|
|
20717
|
-
syncPassengerTunnels(plans, relayHost, cfg.ship.relayPort, privateKeyPath, cfg.ship.stateDir, mcpSecrets, {
|
|
20718
|
-
apiKeys: passengerApiKeys(passengerRoutes),
|
|
20719
|
-
onStatus: (workspaceId, status2, info) => {
|
|
20720
|
-
const owningRoute = passengerRoutes.find((r) => r.resolved?.workspaceId === workspaceId);
|
|
20721
|
-
if (!owningRoute) return;
|
|
20722
|
-
pending.push(
|
|
20723
|
-
new Tracker(owningRoute, cfg.ship).updateTunnelState(cfg.ship.name, { tunnel_status: status2 }).catch((e) => {
|
|
20724
|
-
emit.warn(`could not write tunnel_status: ${e.message}`, { step: "passengers" });
|
|
20725
|
-
})
|
|
20726
|
-
);
|
|
20727
|
-
if (status2 === "disconnected") pending.push(unregisterEndpoint(workspaceId, info, reconcile));
|
|
20728
|
-
}
|
|
20729
|
-
});
|
|
20730
|
-
for (const plan of plans) pending.push(reconcileEndpoint(plan.workspaceId, reconcile));
|
|
20731
|
-
await Promise.all(pending);
|
|
20732
|
-
} catch (e) {
|
|
20733
|
-
emit.warn(`passenger tunnel sync failed: ${e.message}`, { step: "passengers" });
|
|
20734
|
-
}
|
|
20735
|
-
}
|
|
20759
|
+
await syncShipTunnels(planContainers(cfg), new Map([...syncEndpoints].map(([workspaceId, e]) => [workspaceId, e.secret])), passengerRoutes);
|
|
20736
20760
|
} catch (e) {
|
|
20737
20761
|
emit.warn(`passenger container sync failed: ${e.message}`, { step: "passengers" });
|
|
20738
20762
|
}
|
|
20739
20763
|
}
|
|
20764
|
+
async function syncShipTunnels(containerPlans, mcpSecrets, passengerRoutes) {
|
|
20765
|
+
if (!cfg.ship.relayHost) return;
|
|
20766
|
+
const relayHost = cfg.ship.relayHost;
|
|
20767
|
+
try {
|
|
20768
|
+
const { privateKeyPath } = ensureShipSshKeypair(cfg.ship.stateDir);
|
|
20769
|
+
const controlRoutes = cfg.routes.filter((r) => r.enabled && r.resolved?.workspaceId);
|
|
20770
|
+
const planned = new Set(containerPlans.map((p) => p.workspaceId));
|
|
20771
|
+
const plans = [...containerPlans];
|
|
20772
|
+
for (const r of controlRoutes) {
|
|
20773
|
+
const ws = r.resolved.workspaceId;
|
|
20774
|
+
if (planned.has(ws)) continue;
|
|
20775
|
+
planned.add(ws);
|
|
20776
|
+
plans.push({ workspaceId: ws });
|
|
20777
|
+
}
|
|
20778
|
+
const apiRoutes = controlRoutes;
|
|
20779
|
+
const reconcile = endpointReconcileDeps(passengerRoutes, relayHost);
|
|
20780
|
+
const pending = [];
|
|
20781
|
+
syncPassengerTunnels(plans, relayHost, cfg.ship.relayPort, privateKeyPath, cfg.ship.stateDir, mcpSecrets, {
|
|
20782
|
+
apiKeys: passengerApiKeys(apiRoutes),
|
|
20783
|
+
controlSocket: controlSocketFor(cfg.ship.stateDir),
|
|
20784
|
+
onStatus: (workspaceId, status2, info) => {
|
|
20785
|
+
const owningRoute = apiRoutes.find((r) => r.resolved?.workspaceId === workspaceId);
|
|
20786
|
+
if (!owningRoute) return;
|
|
20787
|
+
pending.push(
|
|
20788
|
+
new Tracker(owningRoute, cfg.ship).updateTunnelState(cfg.ship.name, { tunnel_status: status2 }).catch((e) => {
|
|
20789
|
+
emit.warn(`could not write tunnel_status: ${e.message}`, { step: "passengers" });
|
|
20790
|
+
})
|
|
20791
|
+
);
|
|
20792
|
+
if (status2 === "disconnected") pending.push(unregisterEndpoint(workspaceId, info, reconcile));
|
|
20793
|
+
}
|
|
20794
|
+
});
|
|
20795
|
+
for (const plan of containerPlans) pending.push(reconcileEndpoint(plan.workspaceId, reconcile));
|
|
20796
|
+
for (const plan of plans) pending.push(reconcileRelayUrl(plan.workspaceId, apiRoutes));
|
|
20797
|
+
await Promise.all(pending);
|
|
20798
|
+
} catch (e) {
|
|
20799
|
+
emit.warn(`ship tunnel sync failed: ${e.message}`, { step: "passengers" });
|
|
20800
|
+
}
|
|
20801
|
+
}
|
|
20802
|
+
async function reconcileRelayUrl(workspaceId, routes) {
|
|
20803
|
+
if (!cfg.ship.relayHost) return;
|
|
20804
|
+
const persisted = readPersistedTunnel(cfg.ship.stateDir, workspaceId);
|
|
20805
|
+
if (!persisted?.slug || persisted.status !== "connected") return;
|
|
20806
|
+
const url = relayBaseUrlFor(persisted.slug, cfg.ship.relayHost, cfg.ship.relayHttpPort, cfg.ship.relayPublicDomain);
|
|
20807
|
+
if (persisted.relayUrl === url) return;
|
|
20808
|
+
const route3 = routes.find((r) => r.resolved?.workspaceId === workspaceId);
|
|
20809
|
+
if (!route3) return;
|
|
20810
|
+
try {
|
|
20811
|
+
await new Tracker(route3, cfg.ship).updateRelayUrl(cfg.ship.name, url);
|
|
20812
|
+
updatePersistedTunnel(cfg.ship.stateDir, workspaceId, { relayUrl: url });
|
|
20813
|
+
} catch (e) {
|
|
20814
|
+
emit.warn(`could not write relay_url: ${e.message}`, { step: "passengers" });
|
|
20815
|
+
}
|
|
20816
|
+
}
|
|
20740
20817
|
async function releaseFleet(opts = {}) {
|
|
20741
20818
|
if (releaseRefused()) return;
|
|
20742
20819
|
precheckTickets.clear();
|
|
@@ -23753,6 +23830,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
23753
23830
|
const rotatePending = [];
|
|
23754
23831
|
syncPassengerTunnels(plans, cfg.ship.relayHost, cfg.ship.relayPort, privateKeyPath, cfg.ship.stateDir, /* @__PURE__ */ new Map(), {
|
|
23755
23832
|
apiKeys: passengerApiKeys([route2]),
|
|
23833
|
+
controlSocket: controlSocketFor(cfg.ship.stateDir),
|
|
23756
23834
|
onStatus: (wsId, status2, info) => {
|
|
23757
23835
|
rotatePending.push(
|
|
23758
23836
|
new Tracker(route2, cfg.ship).updateTunnelState(cfg.ship.name, { tunnel_status: status2 }).catch((e) => {
|
|
@@ -24329,6 +24407,14 @@ ${where.join("\n")}`);
|
|
|
24329
24407
|
const url = mcpUrlFor(value("base-url") ?? cfg.routes[0]?.baseUrl ?? DEFAULT_BASE_URL);
|
|
24330
24408
|
const disconnectHarnesses = /* @__PURE__ */ new Set([...shipPair(cfg.ship), ...shipToolchains(cfg.ship).map((t) => t.harness).filter(isPairHarness)]);
|
|
24331
24409
|
const removePairEntries = (mcpUrl, dry) => [...disconnectHarnesses].map((h) => pairInstallerFor(h).removePair({ url: mcpUrl, bin: cfg.ship.agent.bin, dryRun: dry }));
|
|
24410
|
+
if (!dryRun) {
|
|
24411
|
+
for (const r of cfg.routes.filter((x) => x.resolved?.shipsModelId)) {
|
|
24412
|
+
await new Tracker(r, cfg.ship).updateRelayUrl(cfg.ship.name, null).catch((e) => {
|
|
24413
|
+
process.stderr.write(`could not clear relay_url for ${r.route}: ${e.message}
|
|
24414
|
+
`);
|
|
24415
|
+
});
|
|
24416
|
+
}
|
|
24417
|
+
}
|
|
24332
24418
|
if (!flag("reset")) {
|
|
24333
24419
|
for (const line of removePairEntries(url, dryRun)) process.stderr.write(`${line}
|
|
24334
24420
|
`);
|
|
@@ -24763,7 +24849,9 @@ ${where.join("\n")}`);
|
|
|
24763
24849
|
attention: openShipAttention(state),
|
|
24764
24850
|
// Additive (CREW-2495): this ship's Ships-row name and whether it has drifted from `name`.
|
|
24765
24851
|
rowName: nameSync.rowName ?? null,
|
|
24766
|
-
nameDrift: nameSync.drift
|
|
24852
|
+
nameDrift: nameSync.drift,
|
|
24853
|
+
// Additive (CREW-2803): the relay URL this route's tunnel is reachable at, or null.
|
|
24854
|
+
relayUrl: routeRelayUrl(route2)
|
|
24767
24855
|
},
|
|
24768
24856
|
// Additive (CREW-1402): what the operator owes. `version` stays 1.
|
|
24769
24857
|
todo: todo.map((i) => ({ ticket: displayKey(i.ticket), title: i.ticket.title ?? null, reasons: i.reasons })),
|
|
@@ -24853,7 +24941,8 @@ ${owed.map((l) => ` ${l}
|
|
|
24853
24941
|
route: ${route2.route} -> ${route2.dir}
|
|
24854
24942
|
enabled: ${route2.enabled}
|
|
24855
24943
|
periscopes: ${periscopeSummary(cfg)}
|
|
24856
|
-
` +
|
|
24944
|
+
` + (routeRelayUrl(route2) ? `relay: ${routeRelayUrl(route2)}
|
|
24945
|
+
` : "") + periscopes.map((p) => `periscope: ${p.ticket} ${p.repo} ${p.url} ${p.state}${p.serving ? "" : " (not serving)"} expires ${p.expiresAt}
|
|
24857
24946
|
`).join("") + `crew: ${state.isPaused() ? "paused" : "active"}
|
|
24858
24947
|
release: ${state.releaseSkipReason() ? "paused" : "active"}
|
|
24859
24948
|
watermark: ${state.watermark()}
|
|
@@ -25445,6 +25534,21 @@ ${foreign.join("\n")}
|
|
|
25445
25534
|
);
|
|
25446
25535
|
}
|
|
25447
25536
|
}
|
|
25537
|
+
if (cfg.ship.relayHost && !flag("offline")) {
|
|
25538
|
+
const relayUrl = routeRelayUrl(route2);
|
|
25539
|
+
if (!relayUrl) {
|
|
25540
|
+
process.stdout.write("relay control: no tunnel URL yet (the first `crew passengers` cycle opens it)\n");
|
|
25541
|
+
} else {
|
|
25542
|
+
let key;
|
|
25543
|
+
try {
|
|
25544
|
+
key = resolveApiKey(route2);
|
|
25545
|
+
} catch {
|
|
25546
|
+
key = void 0;
|
|
25547
|
+
}
|
|
25548
|
+
process.stdout.write(`${key ? describeRelayProbe(relayUrl, await probeRelayControl(relayUrl, key, cfg.ship.userAgent)) : "relay control: could not check (no API key for this route)"}
|
|
25549
|
+
`);
|
|
25550
|
+
}
|
|
25551
|
+
}
|
|
25448
25552
|
for (const l of doctorSelftestLines(readSelftest(cfg.ship.stateDir), shipToolchains(cfg.ship).map((t) => t.name))) {
|
|
25449
25553
|
process.stdout.write(`${l}
|
|
25450
25554
|
`);
|
|
@@ -25466,6 +25570,7 @@ ${foreign.join("\n")}
|
|
|
25466
25570
|
}
|
|
25467
25571
|
var requireArmed2;
|
|
25468
25572
|
var parseTicketNumber2;
|
|
25573
|
+
var routeRelayUrl2;
|
|
25469
25574
|
var passengerApiKeys2;
|
|
25470
25575
|
var endpointApiFor2;
|
|
25471
25576
|
var endpointReconcileDeps2;
|
package/package.json
CHANGED