cartethyia 2.3.2 → 2.3.3
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/dashboard/assets/{BannedPage-BprrKF3t.js → BannedPage-B8MQBL1Y.js} +1 -1
- package/dist/dashboard/assets/{CliToolDetailPage-CeeIxvCm.js → CliToolDetailPage-DAAUVo7w.js} +1 -1
- package/dist/dashboard/assets/{CliToolsPage-BQjMUpJJ.js → CliToolsPage--hLkg7LU.js} +1 -1
- package/dist/dashboard/assets/{ConsoleLogPage-CAzWm-tU.js → ConsoleLogPage-CRaToMW5.js} +1 -1
- package/dist/dashboard/assets/{CustomizationPage-B3SKxD3y.js → CustomizationPage-CYiaepa1.js} +1 -1
- package/dist/dashboard/assets/{HealthEventsModal-DQNKf-5H.js → HealthEventsModal-CjsAm6Gm.js} +1 -1
- package/dist/dashboard/assets/{LoginPage-OXNbMIZC.js → LoginPage-DVL5oW3v.js} +1 -1
- package/dist/dashboard/assets/{OverviewPage-UKI1t9nU.js → OverviewPage-DJ8WM59t.js} +1 -1
- package/dist/dashboard/assets/{ProviderDetailPage-Bh_MZ03h.js → ProviderDetailPage-CK0q-HYZ.js} +1 -1
- package/dist/dashboard/assets/{ProvidersPage-sP8nDPZN.js → ProvidersPage-BdVj-Vab.js} +1 -1
- package/dist/dashboard/assets/{ProxyPage-Ci1Ps9K3.js → ProxyPage-CTohaici.js} +1 -1
- package/dist/dashboard/assets/{QuotaPage-A1jKKb0S.js → QuotaPage-CkHjJoim.js} +1 -1
- package/dist/dashboard/assets/{SetupPage-Ce4ldWt7.js → SetupPage-CRahC2bj.js} +1 -1
- package/dist/dashboard/assets/{StudioPage-CFDyo2dT.js → StudioPage-CxDqECuN.js} +1 -1
- package/dist/dashboard/assets/{UsagePage-BLCXYWis.js → UsagePage-TzfUYdQD.js} +1 -1
- package/dist/dashboard/assets/{entry-B2Cv6U9r.js → entry-KOZCZ9VG.js} +3 -3
- package/dist/dashboard/assets/{index-C7EcMumQ.js → index-Bj96PK8B.js} +2 -2
- package/dist/dashboard/index.html +1 -1
- package/package.json +1 -1
- package/scripts/cli/cli.ts +95 -5
package/scripts/cli/cli.ts
CHANGED
|
@@ -19,7 +19,7 @@ Usage:
|
|
|
19
19
|
cartethyia setup [--non-interactive]
|
|
20
20
|
cartethyia start
|
|
21
21
|
cartethyia menu
|
|
22
|
-
cartethyia
|
|
22
|
+
cartethyia stop stop a gateway left running in the background
|
|
23
23
|
cartethyia update
|
|
24
24
|
|
|
25
25
|
carte starts the gateway and opens a menu driven by the arrow keys and Enter:
|
|
@@ -48,6 +48,54 @@ async function loadUserConfig(): Promise<void> {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Runtime state for a gateway left running by "Hide to background".
|
|
53
|
+
*
|
|
54
|
+
* Kept out of `.env` on purpose: the drain token authorises a shutdown, so it
|
|
55
|
+
* belongs in a machine-written file the operator never hand-edits and never
|
|
56
|
+
* commits. It is written when the menu detaches and removed when the gateway
|
|
57
|
+
* is drained, so a stale file never outlives the process it refers to.
|
|
58
|
+
*/
|
|
59
|
+
const RUNTIME_STATE_FILE = "runtime.json";
|
|
60
|
+
|
|
61
|
+
function writePersistedDrainToken(token: string): void {
|
|
62
|
+
const configDir = process.env.CARTETHYIA_CONFIG_DIR;
|
|
63
|
+
if (!configDir) return;
|
|
64
|
+
try {
|
|
65
|
+
writeFileSync(join(configDir, RUNTIME_STATE_FILE), JSON.stringify({ drainToken: token }), {
|
|
66
|
+
encoding: "utf8",
|
|
67
|
+
mode: 0o600,
|
|
68
|
+
});
|
|
69
|
+
} catch {
|
|
70
|
+
// A read-only config directory costs only the `stop` command, not startup.
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function readPersistedDrainToken(): string | null {
|
|
75
|
+
const configDir = process.env.CARTETHYIA_CONFIG_DIR;
|
|
76
|
+
if (!configDir) return null;
|
|
77
|
+
const statePath = join(configDir, RUNTIME_STATE_FILE);
|
|
78
|
+
if (!existsSync(statePath)) return null;
|
|
79
|
+
try {
|
|
80
|
+
const parsed: unknown = JSON.parse(readFileSync(statePath, "utf8"));
|
|
81
|
+
if (isRecord(parsed) && typeof parsed["drainToken"] === "string") return parsed["drainToken"];
|
|
82
|
+
} catch {
|
|
83
|
+
// A corrupt or stale state file reads as "no background gateway".
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function clearPersistedDrainToken(): void {
|
|
89
|
+
const configDir = process.env.CARTETHYIA_CONFIG_DIR;
|
|
90
|
+
if (!configDir) return;
|
|
91
|
+
try {
|
|
92
|
+
unlinkSync(join(configDir, RUNTIME_STATE_FILE));
|
|
93
|
+
} catch {
|
|
94
|
+
// Best effort: the drain request already succeeded.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
51
99
|
type TrayStartupSignal =
|
|
52
100
|
| { readonly status: "ready"; readonly url: string }
|
|
53
101
|
| { readonly status: "failed"; readonly message: string };
|
|
@@ -402,9 +450,13 @@ async function startMenu(): Promise<void> {
|
|
|
402
450
|
continue;
|
|
403
451
|
}
|
|
404
452
|
if (choice === 2) {
|
|
405
|
-
// The gateway is
|
|
406
|
-
//
|
|
407
|
-
|
|
453
|
+
// The gateway is a detached child, so returning here leaves it running
|
|
454
|
+
// while this process lets go of the terminal. Persist the drain token
|
|
455
|
+
// now, because `cartethyia stop` runs in a later shell and has no other
|
|
456
|
+
// way to reach this process.
|
|
457
|
+
writePersistedDrainToken(drainToken);
|
|
458
|
+
console.log(`Cartethyia keeps running at ${dashboardUrl}.`);
|
|
459
|
+
console.log("Stop it later with: cartethyia stop");
|
|
408
460
|
return;
|
|
409
461
|
}
|
|
410
462
|
if (choice === 3) {
|
|
@@ -412,9 +464,44 @@ async function startMenu(): Promise<void> {
|
|
|
412
464
|
console.log(code === 0 ? "Update finished." : "Update failed.");
|
|
413
465
|
}
|
|
414
466
|
}
|
|
467
|
+
clearPersistedDrainToken();
|
|
415
468
|
await shutDown();
|
|
416
469
|
}
|
|
417
470
|
|
|
471
|
+
/**
|
|
472
|
+
* Stops a gateway left running by "Hide to background". That process is not a
|
|
473
|
+
* child of this one, so it cannot be signalled directly — the only handle is
|
|
474
|
+
* the loopback drain endpoint, which needs the drain token the start wrote.
|
|
475
|
+
*/
|
|
476
|
+
async function stopRunningGateway(): Promise<number> {
|
|
477
|
+
const port = resolvePort();
|
|
478
|
+
const token = readPersistedDrainToken();
|
|
479
|
+
if (token === null) {
|
|
480
|
+
console.error(
|
|
481
|
+
"No background Cartethyia gateway was found for this install. Start one with `carte`, or stop a foreground run with Ctrl-C.",
|
|
482
|
+
);
|
|
483
|
+
return 1;
|
|
484
|
+
}
|
|
485
|
+
try {
|
|
486
|
+
const response = await fetch(`http://127.0.0.1:${port}/admin/drain`, {
|
|
487
|
+
method: "POST",
|
|
488
|
+
headers: { "x-drain-token": token },
|
|
489
|
+
signal: AbortSignal.timeout(3_000),
|
|
490
|
+
});
|
|
491
|
+
if (response.status !== 202) {
|
|
492
|
+
console.error(`The gateway refused the drain request (HTTP ${response.status}).`);
|
|
493
|
+
return 1;
|
|
494
|
+
}
|
|
495
|
+
} catch {
|
|
496
|
+
console.error("No gateway is listening on the configured port. It may already be stopped.");
|
|
497
|
+
return 1;
|
|
498
|
+
}
|
|
499
|
+
clearPersistedDrainToken();
|
|
500
|
+
console.log("Cartethyia is draining and will stop once in-flight requests finish.");
|
|
501
|
+
return 0;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
|
|
418
505
|
async function startTray(): Promise<void> {
|
|
419
506
|
const port = resolvePort();
|
|
420
507
|
const drainToken = randomBytes(32).toString("hex");
|
|
@@ -660,7 +747,7 @@ async function startTray(): Promise<void> {
|
|
|
660
747
|
async function main(): Promise<void> {
|
|
661
748
|
assertBunVersion();
|
|
662
749
|
const [command] = process.argv.slice(2);
|
|
663
|
-
if (command === "setup" || command === "start" || command === "tray" || command === "menu") {
|
|
750
|
+
if (command === "setup" || command === "start" || command === "tray" || command === "menu" || command === "stop") {
|
|
664
751
|
await loadUserConfig();
|
|
665
752
|
}
|
|
666
753
|
|
|
@@ -679,6 +766,9 @@ async function main(): Promise<void> {
|
|
|
679
766
|
case "tray":
|
|
680
767
|
await startTray();
|
|
681
768
|
return;
|
|
769
|
+
case "stop":
|
|
770
|
+
process.exitCode = await stopRunningGateway();
|
|
771
|
+
return;
|
|
682
772
|
case "update": {
|
|
683
773
|
const code = await npmUpdate();
|
|
684
774
|
if (code !== 0) process.exitCode = code;
|