@gajae-gateway/cli 0.3.0 → 0.3.1
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/main.d.ts +1 -11
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +58 -15
- package/package.json +3 -3
- package/src/main.ts +63 -13
package/dist/main.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export declare function socketPath(home?: string | undefined): string;
|
|
|
13
13
|
* never after, so the list is checked up front.
|
|
14
14
|
*/
|
|
15
15
|
export declare const COMMANDS: readonly ["status", "shutdown", "chat", "daemon", "sessions", "ops", "memory", "monitors", "work", "services", "migrate", "update", "setup"];
|
|
16
|
-
export declare const CLI_USAGE = "usage: gajaeway [--version] [--socket PATH] status|shutdown|chat|daemon run|sessions list [--json] [--fields a,b,c] [--limit N] [--offset N]|sessions inspect <originKey-or-index>|memory audit|memory search <query>|monitors ... (settle <eventId>|retry <eventId>|test <id> [--type T] [--payload J] [--wait[=SECONDS]])|work run|start <name> [--cwd DIR] [--repo PATH|URL [--base REF]] [--resume] [--model ID|--preset NAME] [--notify originKey (start only)] <text>|work status <name>|work steer <name> <text>|work retire [--force] <name>|work retire --all-dead|work jobs [--limit N] [--offset N]|ops backup <path>|ops deliveries [--since ISO] [--state S] [--limit N] [--json]|ops redeliver <deliveryId>|ops redeliver --since <iso>|ops cycle [--json]|ops integrity|ops restart-stack [--status]|services install|repair --bin-dir DIR [--launch-agents-dir DIR] [--unit-dir DIR] [--platform darwin|linux]|migrate [--source PATH] [--target PATH] [--dry-run]|update [--check] [--force] [--bin-dir DIR] [--no-restart]|setup [--from-env] [--adapters discord,slack,telegram] [--owner ID] [--discord-app-id ID]|setup --status|setup --broker-agent [--from DIR] (work run waits for a response; caller timeout does not end the attempt); cron timezone is an IANA zone and defaults to the gateway host's local timezone";
|
|
16
|
+
export declare const CLI_USAGE = "usage: gajaeway [--version] [--socket PATH] status|shutdown|chat|daemon run|sessions list [--json] [--fields a,b,c] [--limit N] [--offset N]|sessions inspect <originKey-or-index>|memory audit|memory search <query>|monitors ... (settle <eventId>|retry <eventId>|test <id> [--type T] [--payload J] [--wait[=SECONDS]])|work run|start <name> [--cwd DIR] [--repo PATH|URL [--base REF]] [--resume] [--model ID|--preset NAME] [--notify originKey (start only)] <text>|work status <name>|work steer <name> <text>|work retire [--force] <name>|work retire --all-dead|work jobs [--limit N] [--offset N]|ops backup <path>|ops deliveries [--since ISO] [--state S] [--limit N] [--json]|ops redeliver <deliveryId>|ops redeliver --since <iso>|ops cycle [--limit N] [--offset N] [--json]|ops integrity|ops restart-stack [--status]|services install|repair --bin-dir DIR [--launch-agents-dir DIR] [--unit-dir DIR] [--platform darwin|linux]|migrate [--source PATH] [--target PATH] [--dry-run]|update [--check] [--force] [--bin-dir DIR] [--no-restart]|setup [--from-env] [--adapters discord,slack,telegram] [--owner ID] [--discord-app-id ID]|setup --status|setup --broker-agent [--from DIR] (work run waits for a response; caller timeout does not end the attempt); cron timezone is an IANA zone and defaults to the gateway host's local timezone";
|
|
17
17
|
/** Usage errors exit 2, as `gajaeway-gateway` does; 1 stays a runtime failure. */
|
|
18
18
|
export declare const USAGE_EXIT_CODE = 2;
|
|
19
19
|
/** The usage text when `command` cannot be dispatched, undefined when it can. */
|
|
@@ -44,16 +44,6 @@ export interface ParsedServicesArgs {
|
|
|
44
44
|
readonly platform?: ServicePlatform;
|
|
45
45
|
}
|
|
46
46
|
export declare function parseServicesArgs(args: readonly string[]): ParsedServicesArgs;
|
|
47
|
-
/**
|
|
48
|
-
* Operator runtime-cycle view (`gajaeway ops cycle`).
|
|
49
|
-
*
|
|
50
|
-
* Rendering contract:
|
|
51
|
-
* - The aggregate phase line always prints.
|
|
52
|
-
* - Any gate prints under `gates:` and forces exit code 1 — a gated cycle is
|
|
53
|
-
* never reported as healthy, so scripting cannot mistake it for idle.
|
|
54
|
-
* - `--json` emits the typed OpsCycleResult verbatim; the exit-code contract
|
|
55
|
-
* is identical.
|
|
56
|
-
*/
|
|
57
47
|
export declare function renderCycle(cycle: OpsCycleResult): string[];
|
|
58
48
|
/**
|
|
59
49
|
* Exit-code contract for `gajaeway ops cycle`: 0 only when the projection is
|
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AAIA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
|
package/dist/main.js
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
|
|
4
4
|
// src/main.ts
|
|
5
5
|
import { isAbsolute, resolve } from "path";
|
|
6
|
-
import { LOOPBACK_ORIGIN, originKey as originKey2, WORK_JOBS_MAX_LIMIT } from "@gajae-gateway/protocol";
|
|
6
|
+
import { LOOPBACK_ORIGIN, OPS_CYCLE_MAX_LIMIT, originKey as originKey2, WORK_JOBS_MAX_LIMIT } from "@gajae-gateway/protocol";
|
|
7
7
|
import { GajaewayClient } from "@gajae-gateway/sdk";
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "@gajae-gateway/cli",
|
|
11
|
-
version: "0.3.
|
|
11
|
+
version: "0.3.1",
|
|
12
12
|
type: "module",
|
|
13
13
|
description: "Owner CLI for the gajaeway gateway: status, sessions, chat, monitors, and worker lanes over the gateway socket. Requires the Bun runtime (uses Bun.file/Bun.spawn).",
|
|
14
14
|
license: "AGPL-3.0-only",
|
|
@@ -3336,7 +3336,7 @@ var COMMANDS = [
|
|
|
3336
3336
|
"update",
|
|
3337
3337
|
"setup"
|
|
3338
3338
|
];
|
|
3339
|
-
var CLI_USAGE = "usage: gajaeway [--version] [--socket PATH] status|shutdown|chat|daemon run|sessions list [--json] [--fields a,b,c] [--limit N] [--offset N]|sessions inspect <originKey-or-index>|memory audit|memory search <query>|monitors ... (settle <eventId>|retry <eventId>|test <id> [--type T] [--payload J] [--wait[=SECONDS]])|work run|start <name> [--cwd DIR] [--repo PATH|URL [--base REF]] [--resume] [--model ID|--preset NAME] [--notify originKey (start only)] <text>|work status <name>|work steer <name> <text>|work retire [--force] <name>|work retire --all-dead|work jobs [--limit N] [--offset N]|ops backup <path>|ops deliveries [--since ISO] [--state S] [--limit N] [--json]|ops redeliver <deliveryId>|ops redeliver --since <iso>|ops cycle [--json]|ops integrity|ops restart-stack [--status]|services install|repair --bin-dir DIR [--launch-agents-dir DIR] [--unit-dir DIR] [--platform darwin|linux]|migrate [--source PATH] [--target PATH] [--dry-run]|update [--check] [--force] [--bin-dir DIR] [--no-restart]|setup [--from-env] [--adapters discord,slack,telegram] [--owner ID] [--discord-app-id ID]|setup --status|setup --broker-agent [--from DIR] (work run waits for a response; caller timeout does not end the attempt); cron timezone is an IANA zone and defaults to the gateway host's local timezone";
|
|
3339
|
+
var CLI_USAGE = "usage: gajaeway [--version] [--socket PATH] status|shutdown|chat|daemon run|sessions list [--json] [--fields a,b,c] [--limit N] [--offset N]|sessions inspect <originKey-or-index>|memory audit|memory search <query>|monitors ... (settle <eventId>|retry <eventId>|test <id> [--type T] [--payload J] [--wait[=SECONDS]])|work run|start <name> [--cwd DIR] [--repo PATH|URL [--base REF]] [--resume] [--model ID|--preset NAME] [--notify originKey (start only)] <text>|work status <name>|work steer <name> <text>|work retire [--force] <name>|work retire --all-dead|work jobs [--limit N] [--offset N]|ops backup <path>|ops deliveries [--since ISO] [--state S] [--limit N] [--json]|ops redeliver <deliveryId>|ops redeliver --since <iso>|ops cycle [--limit N] [--offset N] [--json]|ops integrity|ops restart-stack [--status]|services install|repair --bin-dir DIR [--launch-agents-dir DIR] [--unit-dir DIR] [--platform darwin|linux]|migrate [--source PATH] [--target PATH] [--dry-run]|update [--check] [--force] [--bin-dir DIR] [--no-restart]|setup [--from-env] [--adapters discord,slack,telegram] [--owner ID] [--discord-app-id ID]|setup --status|setup --broker-agent [--from DIR] (work run waits for a response; caller timeout does not end the attempt); cron timezone is an IANA zone and defaults to the gateway host's local timezone";
|
|
3340
3340
|
var USAGE_EXIT_CODE = 2;
|
|
3341
3341
|
function usageFor(command) {
|
|
3342
3342
|
return command !== undefined && COMMANDS.includes(command) ? undefined : CLI_USAGE;
|
|
@@ -3534,7 +3534,13 @@ function waitForMonitorStage(client, eventId, timeoutSeconds) {
|
|
|
3534
3534
|
timer = setTimeout(finish, timeoutSeconds * 1000);
|
|
3535
3535
|
});
|
|
3536
3536
|
}
|
|
3537
|
+
var OPS_CYCLE_PAGING_UNSUPPORTED = "ops.cycle paging is unsupported by the running gateway; restart the gateway after updating it";
|
|
3538
|
+
function hasCyclePagingMetadata(cycle) {
|
|
3539
|
+
return Number.isSafeInteger(cycle.sessionsTotal) && cycle.sessionsTotal >= 0 && Number.isSafeInteger(cycle.sessionsOffset) && cycle.sessionsOffset >= 0;
|
|
3540
|
+
}
|
|
3537
3541
|
function renderCycle(cycle) {
|
|
3542
|
+
if (!hasCyclePagingMetadata(cycle))
|
|
3543
|
+
throw new Error(OPS_CYCLE_PAGING_UNSUPPORTED);
|
|
3538
3544
|
const lines = [];
|
|
3539
3545
|
lines.push(`phase: ${cycle.phase}`);
|
|
3540
3546
|
if (cycle.gates.length > 0)
|
|
@@ -3567,13 +3573,16 @@ function renderCycle(cycle) {
|
|
|
3567
3573
|
lines.push(`monitors: ${cycle.monitorEvents.length ? cycle.monitorEvents.map((m) => `${m.stage}=${m.count}`).join(" ") : "none"}`);
|
|
3568
3574
|
if (cycle.monitorAuthoringLost.length > 0)
|
|
3569
3575
|
lines.push(`monitor authoring lost: ${cycle.monitorAuthoringLost.map((m) => `${m.eventType}=${m.consecutive} (last ${m.lastFiredAt})`).join(" ")}`);
|
|
3570
|
-
if (cycle.sessions.length
|
|
3571
|
-
lines.push(
|
|
3576
|
+
if (cycle.sessionsTotal > 0 && cycle.sessions.length === 0) {
|
|
3577
|
+
lines.push(`sessions: none at offset ${cycle.sessionsOffset} of ${cycle.sessionsTotal}`);
|
|
3578
|
+
} else if (cycle.sessions.length > 0) {
|
|
3579
|
+
const shownEnd = cycle.sessionsOffset + cycle.sessions.length;
|
|
3580
|
+
lines.push(`sessions: ${cycle.sessionsOffset + 1}-${shownEnd} of ${cycle.sessionsTotal}${shownEnd < cycle.sessionsTotal ? ` (next: --offset ${shownEnd})` : ""}`);
|
|
3572
3581
|
lines.push("INDEX ORIGIN EPOCH SESSION PENDING UNSETTLED OLDEST");
|
|
3573
3582
|
for (const [index, session] of cycle.sessions.entries()) {
|
|
3574
3583
|
const sessionId = session.sessionId === "" ? "(rebinding)" : session.sessionId.slice(0, 11);
|
|
3575
3584
|
const oldest = session.oldestUnsettledAgeMs === null ? "-" : `${Math.round(session.oldestUnsettledAgeMs / 1000)}s`;
|
|
3576
|
-
lines.push(`${String(index).padEnd(6)} ${session.originKey.padEnd(43)} ${String(session.epoch).padEnd(6)} ${sessionId.padEnd(12)} ${String(session.pendingInbound).padEnd(8)} ${String(session.unsettledDeliveries).padEnd(10)} ${oldest}`);
|
|
3585
|
+
lines.push(`${String(cycle.sessionsOffset + index).padEnd(6)} ${session.originKey.padEnd(43)} ${String(session.epoch).padEnd(6)} ${sessionId.padEnd(12)} ${String(session.pendingInbound).padEnd(8)} ${String(session.unsettledDeliveries).padEnd(10)} ${oldest}`);
|
|
3577
3586
|
}
|
|
3578
3587
|
}
|
|
3579
3588
|
return lines;
|
|
@@ -3593,7 +3602,40 @@ function renderSlo(result) {
|
|
|
3593
3602
|
function sloExitCode(result) {
|
|
3594
3603
|
return result.ok ? 0 : 1;
|
|
3595
3604
|
}
|
|
3596
|
-
var OPS_USAGE = "usage: gajaeway ops backup <path>|deliveries [--since ISO] [--state S] [--limit N] [--json]|redeliver <deliveryId>|redeliver --since <iso>|slo [--days N] [--json]|cycle [--json]|integrity|restart-stack [--status]";
|
|
3605
|
+
var OPS_USAGE = "usage: gajaeway ops backup <path>|deliveries [--since ISO] [--state S] [--limit N] [--json]|redeliver <deliveryId>|redeliver --since <iso>|slo [--days N] [--json]|cycle [--limit N] [--offset N] [--json]|integrity|restart-stack [--status]";
|
|
3606
|
+
var OPS_CYCLE_USAGE = "usage: gajaeway ops cycle [--limit N] [--offset N] [--json]";
|
|
3607
|
+
function parseOpsCycleArgs(args) {
|
|
3608
|
+
const params = {};
|
|
3609
|
+
const seen = new Set;
|
|
3610
|
+
let json = false;
|
|
3611
|
+
for (let index = 0;index < args.length; index++) {
|
|
3612
|
+
const flag = args[index];
|
|
3613
|
+
if (flag === "--json") {
|
|
3614
|
+
if (json)
|
|
3615
|
+
throw new Error(OPS_CYCLE_USAGE);
|
|
3616
|
+
json = true;
|
|
3617
|
+
continue;
|
|
3618
|
+
}
|
|
3619
|
+
if (flag !== "--limit" && flag !== "--offset")
|
|
3620
|
+
throw new Error(OPS_CYCLE_USAGE);
|
|
3621
|
+
if (seen.has(flag))
|
|
3622
|
+
throw new Error(OPS_CYCLE_USAGE);
|
|
3623
|
+
seen.add(flag);
|
|
3624
|
+
const value = args[++index];
|
|
3625
|
+
if (!value || value.startsWith("--") || !/^\d+$/.test(value))
|
|
3626
|
+
throw new Error(OPS_CYCLE_USAGE);
|
|
3627
|
+
const number = Number(value);
|
|
3628
|
+
if (!Number.isSafeInteger(number))
|
|
3629
|
+
throw new Error(OPS_CYCLE_USAGE);
|
|
3630
|
+
if (flag === "--limit") {
|
|
3631
|
+
if (number < 1 || number > OPS_CYCLE_MAX_LIMIT)
|
|
3632
|
+
throw new Error(OPS_CYCLE_USAGE);
|
|
3633
|
+
params.limit = number;
|
|
3634
|
+
} else
|
|
3635
|
+
params.offset = number;
|
|
3636
|
+
}
|
|
3637
|
+
return { params, json };
|
|
3638
|
+
}
|
|
3597
3639
|
function parseOpsSloArgs(args) {
|
|
3598
3640
|
let days = 7;
|
|
3599
3641
|
let json = false;
|
|
@@ -3841,6 +3883,7 @@ async function main(args = process.argv.slice(2), options = {}) {
|
|
|
3841
3883
|
${OPS_USAGE}`);
|
|
3842
3884
|
const deliveries = command === "deliveries" ? parseOpsDeliveriesArgs(parsed.rest.slice(1)) : undefined;
|
|
3843
3885
|
const slo = command === "slo" ? parseOpsSloArgs(parsed.rest.slice(1)) : undefined;
|
|
3886
|
+
const cycle = command === "cycle" ? parseOpsCycleArgs(parsed.rest.slice(1)) : undefined;
|
|
3844
3887
|
if (command === "restart-stack") {
|
|
3845
3888
|
const usage2 = "usage: gajaeway ops restart-stack [--status]";
|
|
3846
3889
|
const home = gatewayHome();
|
|
@@ -3896,16 +3939,16 @@ ${OPS_USAGE}`);
|
|
|
3896
3939
|
console.log(JSON.stringify(await client.request("ops.redeliver", redeliverParams)));
|
|
3897
3940
|
else if (command === "integrity")
|
|
3898
3941
|
console.log(JSON.stringify(await client.request("ops.integrity")));
|
|
3899
|
-
else if (
|
|
3900
|
-
const
|
|
3901
|
-
if (
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3942
|
+
else if (cycle) {
|
|
3943
|
+
const result = await client.opsCycle(cycle.params);
|
|
3944
|
+
if (!hasCyclePagingMetadata(result))
|
|
3945
|
+
throw new Error(OPS_CYCLE_PAGING_UNSUPPORTED);
|
|
3946
|
+
if (cycle.json)
|
|
3947
|
+
console.log(JSON.stringify(result));
|
|
3905
3948
|
else
|
|
3906
|
-
for (const line of renderCycle(
|
|
3949
|
+
for (const line of renderCycle(result))
|
|
3907
3950
|
console.log(line);
|
|
3908
|
-
process.exitCode = cycleExitCode(
|
|
3951
|
+
process.exitCode = cycleExitCode(result);
|
|
3909
3952
|
} else if (slo) {
|
|
3910
3953
|
const result = await client.opsSlo(slo.params);
|
|
3911
3954
|
if (slo.json)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gajae-gateway/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Owner CLI for the gajaeway gateway: status, sessions, chat, monitors, and worker lanes over the gateway socket. Requires the Bun runtime (uses Bun.file/Bun.spawn).",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"prepublishOnly": "bun run build"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@gajae-gateway/sdk": "0.3.
|
|
45
|
-
"@gajae-gateway/protocol": "0.3.
|
|
44
|
+
"@gajae-gateway/sdk": "0.3.1",
|
|
45
|
+
"@gajae-gateway/protocol": "0.3.1"
|
|
46
46
|
},
|
|
47
47
|
"engines": {
|
|
48
48
|
"bun": ">=1.4.0"
|
package/src/main.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import { isAbsolute,
|
|
3
|
+
import { isAbsolute, resolve } from "node:path";
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
6
|
MonitorEventOperatorResult,
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
MonitorRecord,
|
|
9
9
|
MonitorScheduleProjection,
|
|
10
10
|
MonitorSpec,
|
|
11
|
+
OpsCycleParams,
|
|
11
12
|
OpsCycleResult,
|
|
12
13
|
OpsDeliveriesParams,
|
|
13
14
|
OpsDeliveriesResult,
|
|
@@ -22,7 +23,7 @@ import type {
|
|
|
22
23
|
WorkStatusResult,
|
|
23
24
|
WorkSteerResult,
|
|
24
25
|
} from "@gajae-gateway/protocol";
|
|
25
|
-
import { LOOPBACK_ORIGIN, originKey, WORK_JOBS_MAX_LIMIT } from "@gajae-gateway/protocol";
|
|
26
|
+
import { LOOPBACK_ORIGIN, OPS_CYCLE_MAX_LIMIT, originKey, WORK_JOBS_MAX_LIMIT } from "@gajae-gateway/protocol";
|
|
26
27
|
|
|
27
28
|
import { GajaewayClient } from "@gajae-gateway/sdk";
|
|
28
29
|
import pkg from "../package.json";
|
|
@@ -85,7 +86,7 @@ export const COMMANDS = [
|
|
|
85
86
|
] as const;
|
|
86
87
|
|
|
87
88
|
export const CLI_USAGE =
|
|
88
|
-
"usage: gajaeway [--version] [--socket PATH] status|shutdown|chat|daemon run|sessions list [--json] [--fields a,b,c] [--limit N] [--offset N]|sessions inspect <originKey-or-index>|memory audit|memory search <query>|monitors ... (settle <eventId>|retry <eventId>|test <id> [--type T] [--payload J] [--wait[=SECONDS]])|work run|start <name> [--cwd DIR] [--repo PATH|URL [--base REF]] [--resume] [--model ID|--preset NAME] [--notify originKey (start only)] <text>|work status <name>|work steer <name> <text>|work retire [--force] <name>|work retire --all-dead|work jobs [--limit N] [--offset N]|ops backup <path>|ops deliveries [--since ISO] [--state S] [--limit N] [--json]|ops redeliver <deliveryId>|ops redeliver --since <iso>|ops cycle [--json]|ops integrity|ops restart-stack [--status]|services install|repair --bin-dir DIR [--launch-agents-dir DIR] [--unit-dir DIR] [--platform darwin|linux]|migrate [--source PATH] [--target PATH] [--dry-run]|update [--check] [--force] [--bin-dir DIR] [--no-restart]|setup [--from-env] [--adapters discord,slack,telegram] [--owner ID] [--discord-app-id ID]|setup --status|setup --broker-agent [--from DIR] (work run waits for a response; caller timeout does not end the attempt); cron timezone is an IANA zone and defaults to the gateway host's local timezone";
|
|
89
|
+
"usage: gajaeway [--version] [--socket PATH] status|shutdown|chat|daemon run|sessions list [--json] [--fields a,b,c] [--limit N] [--offset N]|sessions inspect <originKey-or-index>|memory audit|memory search <query>|monitors ... (settle <eventId>|retry <eventId>|test <id> [--type T] [--payload J] [--wait[=SECONDS]])|work run|start <name> [--cwd DIR] [--repo PATH|URL [--base REF]] [--resume] [--model ID|--preset NAME] [--notify originKey (start only)] <text>|work status <name>|work steer <name> <text>|work retire [--force] <name>|work retire --all-dead|work jobs [--limit N] [--offset N]|ops backup <path>|ops deliveries [--since ISO] [--state S] [--limit N] [--json]|ops redeliver <deliveryId>|ops redeliver --since <iso>|ops cycle [--limit N] [--offset N] [--json]|ops integrity|ops restart-stack [--status]|services install|repair --bin-dir DIR [--launch-agents-dir DIR] [--unit-dir DIR] [--platform darwin|linux]|migrate [--source PATH] [--target PATH] [--dry-run]|update [--check] [--force] [--bin-dir DIR] [--no-restart]|setup [--from-env] [--adapters discord,slack,telegram] [--owner ID] [--discord-app-id ID]|setup --status|setup --broker-agent [--from DIR] (work run waits for a response; caller timeout does not end the attempt); cron timezone is an IANA zone and defaults to the gateway host's local timezone";
|
|
89
90
|
|
|
90
91
|
/** Usage errors exit 2, as `gajaeway-gateway` does; 1 stays a runtime failure. */
|
|
91
92
|
export const USAGE_EXIT_CODE = 2;
|
|
@@ -309,10 +310,24 @@ function waitForMonitorStage(client: GajaewayClient, eventId: string, timeoutSec
|
|
|
309
310
|
* - The aggregate phase line always prints.
|
|
310
311
|
* - Any gate prints under `gates:` and forces exit code 1 — a gated cycle is
|
|
311
312
|
* never reported as healthy, so scripting cannot mistake it for idle.
|
|
313
|
+
* - Session details are paged; gates and census totals cover the full home.
|
|
312
314
|
* - `--json` emits the typed OpsCycleResult verbatim; the exit-code contract
|
|
313
315
|
* is identical.
|
|
314
316
|
*/
|
|
317
|
+
const OPS_CYCLE_PAGING_UNSUPPORTED =
|
|
318
|
+
"ops.cycle paging is unsupported by the running gateway; restart the gateway after updating it";
|
|
319
|
+
|
|
320
|
+
function hasCyclePagingMetadata(cycle: OpsCycleResult): boolean {
|
|
321
|
+
return (
|
|
322
|
+
Number.isSafeInteger(cycle.sessionsTotal) &&
|
|
323
|
+
cycle.sessionsTotal >= 0 &&
|
|
324
|
+
Number.isSafeInteger(cycle.sessionsOffset) &&
|
|
325
|
+
cycle.sessionsOffset >= 0
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
315
329
|
export function renderCycle(cycle: OpsCycleResult): string[] {
|
|
330
|
+
if (!hasCyclePagingMetadata(cycle)) throw new Error(OPS_CYCLE_PAGING_UNSUPPORTED);
|
|
316
331
|
const lines: string[] = [];
|
|
317
332
|
lines.push(`phase: ${cycle.phase}`);
|
|
318
333
|
if (cycle.gates.length > 0) lines.push(`gates: ${cycle.gates.join(", ")}`);
|
|
@@ -363,15 +378,20 @@ export function renderCycle(cycle: OpsCycleResult): string[] {
|
|
|
363
378
|
lines.push(
|
|
364
379
|
`monitor authoring lost: ${cycle.monitorAuthoringLost.map((m) => `${m.eventType}=${m.consecutive} (last ${m.lastFiredAt})`).join(" ")}`,
|
|
365
380
|
);
|
|
366
|
-
if (cycle.sessions.length
|
|
367
|
-
lines.push(
|
|
381
|
+
if (cycle.sessionsTotal > 0 && cycle.sessions.length === 0) {
|
|
382
|
+
lines.push(`sessions: none at offset ${cycle.sessionsOffset} of ${cycle.sessionsTotal}`);
|
|
383
|
+
} else if (cycle.sessions.length > 0) {
|
|
384
|
+
const shownEnd = cycle.sessionsOffset + cycle.sessions.length;
|
|
385
|
+
lines.push(
|
|
386
|
+
`sessions: ${cycle.sessionsOffset + 1}-${shownEnd} of ${cycle.sessionsTotal}${shownEnd < cycle.sessionsTotal ? ` (next: --offset ${shownEnd})` : ""}`,
|
|
387
|
+
);
|
|
368
388
|
lines.push("INDEX ORIGIN EPOCH SESSION PENDING UNSETTLED OLDEST");
|
|
369
389
|
for (const [index, session] of cycle.sessions.entries()) {
|
|
370
390
|
const sessionId = session.sessionId === "" ? "(rebinding)" : session.sessionId.slice(0, 11);
|
|
371
391
|
const oldest =
|
|
372
392
|
session.oldestUnsettledAgeMs === null ? "-" : `${Math.round(session.oldestUnsettledAgeMs / 1000)}s`;
|
|
373
393
|
lines.push(
|
|
374
|
-
`${String(index).padEnd(6)} ${session.originKey.padEnd(43)} ${String(session.epoch).padEnd(6)} ${sessionId.padEnd(12)} ${String(session.pendingInbound).padEnd(8)} ${String(session.unsettledDeliveries).padEnd(10)} ${oldest}`,
|
|
394
|
+
`${String(cycle.sessionsOffset + index).padEnd(6)} ${session.originKey.padEnd(43)} ${String(session.epoch).padEnd(6)} ${sessionId.padEnd(12)} ${String(session.pendingInbound).padEnd(8)} ${String(session.unsettledDeliveries).padEnd(10)} ${oldest}`,
|
|
375
395
|
);
|
|
376
396
|
}
|
|
377
397
|
}
|
|
@@ -410,7 +430,36 @@ export function sloExitCode(result: OpsSloResult): number {
|
|
|
410
430
|
}
|
|
411
431
|
|
|
412
432
|
const OPS_USAGE =
|
|
413
|
-
"usage: gajaeway ops backup <path>|deliveries [--since ISO] [--state S] [--limit N] [--json]|redeliver <deliveryId>|redeliver --since <iso>|slo [--days N] [--json]|cycle [--json]|integrity|restart-stack [--status]";
|
|
433
|
+
"usage: gajaeway ops backup <path>|deliveries [--since ISO] [--state S] [--limit N] [--json]|redeliver <deliveryId>|redeliver --since <iso>|slo [--days N] [--json]|cycle [--limit N] [--offset N] [--json]|integrity|restart-stack [--status]";
|
|
434
|
+
|
|
435
|
+
const OPS_CYCLE_USAGE = "usage: gajaeway ops cycle [--limit N] [--offset N] [--json]";
|
|
436
|
+
|
|
437
|
+
function parseOpsCycleArgs(args: readonly string[]): { params: OpsCycleParams; json: boolean } {
|
|
438
|
+
const params: { limit?: number; offset?: number } = {};
|
|
439
|
+
const seen = new Set<string>();
|
|
440
|
+
let json = false;
|
|
441
|
+
for (let index = 0; index < args.length; index++) {
|
|
442
|
+
const flag = args[index];
|
|
443
|
+
if (flag === "--json") {
|
|
444
|
+
if (json) throw new Error(OPS_CYCLE_USAGE);
|
|
445
|
+
json = true;
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
if (flag !== "--limit" && flag !== "--offset") throw new Error(OPS_CYCLE_USAGE);
|
|
449
|
+
if (seen.has(flag)) throw new Error(OPS_CYCLE_USAGE);
|
|
450
|
+
seen.add(flag);
|
|
451
|
+
const value = args[++index];
|
|
452
|
+
if (!value || value.startsWith("--") || !/^\d+$/.test(value)) throw new Error(OPS_CYCLE_USAGE);
|
|
453
|
+
const number = Number(value);
|
|
454
|
+
if (!Number.isSafeInteger(number)) throw new Error(OPS_CYCLE_USAGE);
|
|
455
|
+
if (flag === "--limit") {
|
|
456
|
+
if (number < 1 || number > OPS_CYCLE_MAX_LIMIT) throw new Error(OPS_CYCLE_USAGE);
|
|
457
|
+
params.limit = number;
|
|
458
|
+
} else params.offset = number;
|
|
459
|
+
}
|
|
460
|
+
return { params, json };
|
|
461
|
+
}
|
|
462
|
+
|
|
414
463
|
function parseOpsSloArgs(args: readonly string[]): { params: OpsSloParams; json: boolean } {
|
|
415
464
|
let days = 7;
|
|
416
465
|
let json = false;
|
|
@@ -700,6 +749,7 @@ export async function main(args = process.argv.slice(2), options: MainOptions =
|
|
|
700
749
|
throw new Error(`unknown ops command: ${command ?? "(missing)"}\n${OPS_USAGE}`);
|
|
701
750
|
const deliveries = command === "deliveries" ? parseOpsDeliveriesArgs(parsed.rest.slice(1)) : undefined;
|
|
702
751
|
const slo = command === "slo" ? parseOpsSloArgs(parsed.rest.slice(1)) : undefined;
|
|
752
|
+
const cycle = command === "cycle" ? parseOpsCycleArgs(parsed.rest.slice(1)) : undefined;
|
|
703
753
|
if (command === "restart-stack") {
|
|
704
754
|
// Deliberately socket-free: the reason to run this is a gateway that
|
|
705
755
|
// has to come back, so it must not need the gateway to answer first.
|
|
@@ -752,14 +802,14 @@ export async function main(args = process.argv.slice(2), options: MainOptions =
|
|
|
752
802
|
} else if (redeliverParams)
|
|
753
803
|
console.log(JSON.stringify(await client.request("ops.redeliver", redeliverParams)));
|
|
754
804
|
else if (command === "integrity") console.log(JSON.stringify(await client.request("ops.integrity")));
|
|
755
|
-
else if (
|
|
756
|
-
const
|
|
757
|
-
if (
|
|
758
|
-
|
|
759
|
-
else for (const line of renderCycle(
|
|
805
|
+
else if (cycle) {
|
|
806
|
+
const result = await client.opsCycle(cycle.params);
|
|
807
|
+
if (!hasCyclePagingMetadata(result)) throw new Error(OPS_CYCLE_PAGING_UNSUPPORTED);
|
|
808
|
+
if (cycle.json) console.log(JSON.stringify(result));
|
|
809
|
+
else for (const line of renderCycle(result)) console.log(line);
|
|
760
810
|
// Fail-closed: a gated/degraded cycle exits non-zero even though the
|
|
761
811
|
// request itself succeeded, so scripts can never read it as healthy.
|
|
762
|
-
process.exitCode = cycleExitCode(
|
|
812
|
+
process.exitCode = cycleExitCode(result);
|
|
763
813
|
} else if (slo) {
|
|
764
814
|
const result = await client.opsSlo(slo.params);
|
|
765
815
|
if (slo.json) console.log(JSON.stringify(result));
|