@gajae-gateway/cli 0.2.3 → 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/list.d.ts.map +1 -1
- package/dist/main.d.ts +1 -11
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +196 -68
- package/dist/restart-stack.d.ts.map +1 -1
- package/dist/services.d.ts +20 -0
- package/dist/services.d.ts.map +1 -1
- package/dist/update.d.ts +4 -1
- package/dist/update.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/list.ts +1 -0
- package/src/main.ts +67 -16
- package/src/restart-stack.ts +39 -9
- package/src/services.ts +71 -5
- package/src/update.ts +33 -18
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restart-stack.d.ts","sourceRoot":"","sources":["../src/restart-stack.ts"],"names":[],"mappings":"AAcA,OAAO,
|
|
1
|
+
{"version":3,"file":"restart-stack.d.ts","sourceRoot":"","sources":["../src/restart-stack.ts"],"names":[],"mappings":"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"}
|
package/dist/services.d.ts
CHANGED
|
@@ -15,6 +15,10 @@ export interface ResolvePathOptions {
|
|
|
15
15
|
}
|
|
16
16
|
/** Service managers this CLI can write definitions for. */
|
|
17
17
|
export type ServicePlatform = "darwin" | "linux";
|
|
18
|
+
export interface ServiceCommandOptions {
|
|
19
|
+
readonly allowFailure?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export type ServiceCommandRunner = (command: readonly string[], options?: ServiceCommandOptions) => number | PromiseLike<number>;
|
|
18
22
|
export interface InstallServicesOptions {
|
|
19
23
|
readonly binDir: string;
|
|
20
24
|
readonly launchAgentsDir?: string;
|
|
@@ -49,6 +53,12 @@ interface ServiceSpec {
|
|
|
49
53
|
export declare const BROKER_UNIT = "gajaeway-broker.service";
|
|
50
54
|
export declare const GATEWAY_UNIT = "gajaeway-gateway.service";
|
|
51
55
|
export declare function serviceSpecs(): readonly ServiceSpec[];
|
|
56
|
+
/**
|
|
57
|
+
* The `adapter-<platform>.json` an adapter reads at startup, or undefined for services
|
|
58
|
+
* that are not adapters. A unit whose file is absent can never come up — the adapter
|
|
59
|
+
* exits on the credential read — so supervisors skip it instead of looping on it.
|
|
60
|
+
*/
|
|
61
|
+
export declare function adapterConfigPath(home: string, specId: string): string | undefined;
|
|
52
62
|
export declare function mergePathEntries(binDir: string, userHome: string, loginPath: string): string[];
|
|
53
63
|
export declare function defaultLoginPathRunner(command: readonly string[]): Promise<string>;
|
|
54
64
|
export declare function discoverLoginPath(env?: NodeJS.ProcessEnv, runner?: LoginPathRunner): Promise<string>;
|
|
@@ -62,6 +72,16 @@ export declare function systemdUnitDir(env: NodeJS.ProcessEnv, userHome: string)
|
|
|
62
72
|
export declare function launchAgentPlistPath(spec: ServiceSpec, launchAgentsDir: string): string;
|
|
63
73
|
/** Gets the path where a service's systemd unit file is stored on linux. */
|
|
64
74
|
export declare function systemdUnitPath(spec: ServiceSpec, unitDir: string): string;
|
|
75
|
+
export interface RemoveStaleServicesOptions extends Pick<InstallServicesOptions, "launchAgentsDir" | "unitDir"> {
|
|
76
|
+
readonly binDir: string;
|
|
77
|
+
readonly platform: ServicePlatform;
|
|
78
|
+
readonly uid?: number;
|
|
79
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
80
|
+
readonly reloadSystemd?: boolean;
|
|
81
|
+
readonly run: ServiceCommandRunner;
|
|
82
|
+
}
|
|
83
|
+
/** Stops and removes service definitions whose executable is no longer installed. */
|
|
84
|
+
export declare function removeStaleServices(options: RemoveStaleServicesOptions): Promise<void>;
|
|
65
85
|
/**
|
|
66
86
|
* The ordered commands that realign the whole stack.
|
|
67
87
|
*
|
package/dist/services.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../src/services.ts"],"names":[],"mappings":"AAIA,OAAO,EAAsB,KAAK,aAAa,EAAsB,MAAM,yBAAyB,CAAC;AAIrG,eAAO,MAAM,kBAAkB,uEAAwE,CAAC;AAGxG,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;AAC3F,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;AAEvF,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC;CAC3C;AAED,2DAA2D;AAC3D,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../src/services.ts"],"names":[],"mappings":"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"}
|
package/dist/update.d.ts
CHANGED
|
@@ -73,7 +73,10 @@ export type ReleaseFetcher = (repo: string) => Promise<ReleaseInfo>;
|
|
|
73
73
|
export type AssetDownloader = (asset: ReleaseAsset) => Promise<Uint8Array>;
|
|
74
74
|
export type TarballExtractor = (tarball: Uint8Array, intoDir: string) => Promise<void>;
|
|
75
75
|
export type RestartQueuer = (home: string, binDir: string, trigger: "update") => Promise<RestartQueueReceipt>;
|
|
76
|
-
export
|
|
76
|
+
export interface UpdateCommandOptions {
|
|
77
|
+
readonly allowFailure?: boolean;
|
|
78
|
+
}
|
|
79
|
+
export type UpdateCommandRunner = (command: readonly string[], options?: UpdateCommandOptions) => Promise<number>;
|
|
77
80
|
type UpdateServiceOptions = Pick<InstallServicesOptions, "launchAgentsDir" | "unitDir" | "loginPathRunner" | "writeFile">;
|
|
78
81
|
export interface UpdateDeps {
|
|
79
82
|
readonly fetchLatestRelease?: ReleaseFetcher;
|
package/dist/update.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,KAAK,gBAAgB,EAAoB,MAAM,eAAe,CAAC;AAExE,OAAO,EACN,KAAK,sBAAsB,
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,KAAK,gBAAgB,EAAoB,MAAM,eAAe,CAAC;AAExE,OAAO,EACN,KAAK,sBAAsB,EAM3B,MAAM,YAAY,CAAC;AAEpB;;;;;;;;GAQG;AAEH,eAAO,MAAM,YAAY,8EAA8E,CAAC;AAExG,eAAO,MAAM,oBAAoB,0BAA0B,CAAC;AAE5D,6FAA6F;AAC7F,wBAAgB,WAAW,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAGxE;AASD,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,gBAAgB,CAuBzE;AAED,iGAAiG;AACjG,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG9D;AAED,yFAAyF;AACzF,wBAAgB,gBAAgB,CAAC,QAAQ,GAAE,MAAM,CAAC,QAA2B,EAAE,IAAI,GAAE,MAAqB,GAAG,MAAM,CAElH;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAE/F;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,wHAOjB,CAAC;AAEX,eAAO,MAAM,aAAa,2CAA4C,CAAC;AAQvE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAOtF;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAClC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,QAAQ,SAAmB,GACzB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAK7B;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,EAAE,CAsB3D;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;AACpE,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAC3E,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AACvF,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAC9G,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CAChC;AACD,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAElH,KAAK,oBAAoB,GAAG,IAAI,CAC/B,sBAAsB,EACtB,iBAAiB,GAAG,SAAS,GAAG,iBAAiB,GAAG,WAAW,CAC/D,CAAC;AAEF,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,cAAc,CAAC;IAC7C,QAAQ,CAAC,aAAa,CAAC,EAAE,eAAe,CAAC;IACzC,QAAQ,CAAC,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAC3C,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAC7C,QAAQ,CAAC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IAC7C,QAAQ,CAAC,cAAc,CAAC,EAAE,oBAAoB,CAAC;CAC/C;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;CAC3B;AAoFD,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAqHhF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gajae-gateway/cli",
|
|
3
|
-
"version": "0.
|
|
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.
|
|
45
|
-
"@gajae-gateway/protocol": "0.
|
|
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/list.ts
CHANGED
|
@@ -159,6 +159,7 @@ export const MONITOR_COLUMNS: readonly Column<MonitorListRow>[] = [
|
|
|
159
159
|
},
|
|
160
160
|
{ name: "events", value: (row) => [...row.monitor.eventTypes] },
|
|
161
161
|
{ name: "target", value: (row) => (row.monitor.channelTarget ? originKey(row.monitor.channelTarget.origin) : null) },
|
|
162
|
+
{ name: "reportTo", value: (row) => (row.monitor.reportTo ? originKey(row.monitor.reportTo) : null) },
|
|
162
163
|
{ name: "enabled", value: (row) => row.monitor.enabled },
|
|
163
164
|
];
|
|
164
165
|
|
package/src/main.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import { isAbsolute } from "node:path";
|
|
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] [--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;
|
|
@@ -486,7 +535,7 @@ export function renderDeliveries(result: OpsDeliveriesResult): string[] {
|
|
|
486
535
|
`deliveries: ${result.rows.length}`,
|
|
487
536
|
...result.rows.map(
|
|
488
537
|
(row) =>
|
|
489
|
-
`${row.createdAt} ${row.state} delivery=${row.deliveryId} turn=${row.turnId} origin=${row.originKey} conversation=${row.conversationId} text=${JSON.stringify(row.textPrefix)}`,
|
|
538
|
+
`${row.createdAt} ${row.state} delivery=${row.deliveryId} turn=${row.turnId} origin=${row.originKey} conversation=${row.conversationId} lastError=${row.lastError ?? "-"} text=${JSON.stringify(row.textPrefix)}`,
|
|
490
539
|
),
|
|
491
540
|
];
|
|
492
541
|
}
|
|
@@ -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));
|
|
@@ -898,7 +948,7 @@ export async function main(args = process.argv.slice(2), options: MainOptions =
|
|
|
898
948
|
case "work": {
|
|
899
949
|
const [command, ...args] = parsed.rest;
|
|
900
950
|
const usage =
|
|
901
|
-
'usage: gajaeway work run|start <name> [--cwd DIR] [--resume] [--model ID|--preset NAME] "<task text>"|status <name>|steer <name> <text>|retire <name>|jobs';
|
|
951
|
+
'usage: gajaeway work run|start <name> [--cwd DIR] [--repo PATH|URL [--base REF]] [--resume] [--model ID|--preset NAME] "<task text>"|status <name>|steer <name> <text>|retire <name>|jobs';
|
|
902
952
|
if (command === "status" || command === "steer") {
|
|
903
953
|
const name = args[0];
|
|
904
954
|
const text = args.slice(1).join(" ").trim();
|
|
@@ -1020,7 +1070,7 @@ export async function main(args = process.argv.slice(2), options: MainOptions =
|
|
|
1020
1070
|
cwd = value;
|
|
1021
1071
|
} else if (arg === "--repo") {
|
|
1022
1072
|
if (repo !== undefined) throw new Error(usage);
|
|
1023
|
-
repo = value;
|
|
1073
|
+
repo = value.includes("://") || /^[^/@\s]+@[^:/\s]+:/.test(value) ? value : resolve(value);
|
|
1024
1074
|
} else if (arg === "--base") {
|
|
1025
1075
|
if (base !== undefined) throw new Error(usage);
|
|
1026
1076
|
base = value;
|
|
@@ -1034,6 +1084,7 @@ export async function main(args = process.argv.slice(2), options: MainOptions =
|
|
|
1034
1084
|
}
|
|
1035
1085
|
const text = textParts.join(" ").trim();
|
|
1036
1086
|
if (!name || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(name) || !text) throw new Error(usage);
|
|
1087
|
+
if (base !== undefined && repo === undefined) throw new Error(usage);
|
|
1037
1088
|
// This is a caller response-wait budget, never an attempt deadline.
|
|
1038
1089
|
// Timeout/disconnect leaves the attempt observable via work.status.
|
|
1039
1090
|
const client = await connectGatewaySocket(parsed.socket, {
|
package/src/restart-stack.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { access, mkdir, readFile,
|
|
2
|
+
import { access, mkdir, readFile, realpath, rename, stat, writeFile } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
4
|
+
import { basename, dirname, join } from "node:path";
|
|
5
5
|
import {
|
|
6
6
|
acquireLease,
|
|
7
7
|
homeLayout,
|
|
@@ -13,9 +13,11 @@ import {
|
|
|
13
13
|
} from "@gajae-gateway/home";
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
|
+
adapterConfigPath,
|
|
16
17
|
BROKER_UNIT,
|
|
17
18
|
currentPlatform,
|
|
18
19
|
launchAgentPlistPath,
|
|
20
|
+
removeStaleServices,
|
|
19
21
|
restartOrder,
|
|
20
22
|
restartStackCommands,
|
|
21
23
|
type ServicePlatform,
|
|
@@ -295,7 +297,26 @@ async function runRestartStackWithLease(options: RunRestartOptions): Promise<Res
|
|
|
295
297
|
const sleep = options.sleep ?? ((ms: number) => Bun.sleep(ms));
|
|
296
298
|
const timeoutMs = options.verifyTimeoutMs ?? VERIFY_TIMEOUT_MS;
|
|
297
299
|
const pollMs = options.pollMs ?? POLL_MS;
|
|
298
|
-
const
|
|
300
|
+
const env = options.env ?? process.env;
|
|
301
|
+
const deployedExecutable = await realpath(process.execPath).catch(() => undefined);
|
|
302
|
+
const installedBinDir =
|
|
303
|
+
options.binDir ??
|
|
304
|
+
env.GAJAEWAY_BIN_DIR ??
|
|
305
|
+
(deployedExecutable !== undefined && basename(deployedExecutable).startsWith("gajaeway")
|
|
306
|
+
? dirname(deployedExecutable)
|
|
307
|
+
: undefined);
|
|
308
|
+
const binDir = installedBinDir ?? dirname(process.execPath);
|
|
309
|
+
if (installedBinDir !== undefined) {
|
|
310
|
+
await removeStaleServices({
|
|
311
|
+
binDir: installedBinDir,
|
|
312
|
+
platform,
|
|
313
|
+
uid: options.uid,
|
|
314
|
+
env,
|
|
315
|
+
...(options.launchAgentsDir === undefined ? {} : { launchAgentsDir: options.launchAgentsDir }),
|
|
316
|
+
...(options.unitDir === undefined ? {} : { unitDir: options.unitDir }),
|
|
317
|
+
run: runner,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
299
320
|
|
|
300
321
|
const originalPids = new Map<string, number | undefined>();
|
|
301
322
|
for (const spec of restartOrder()) {
|
|
@@ -337,6 +358,20 @@ async function runRestartStackWithLease(options: RunRestartOptions): Promise<Res
|
|
|
337
358
|
await writeReceipt(options.home, receipt);
|
|
338
359
|
continue;
|
|
339
360
|
}
|
|
361
|
+
// An installed unit can still be unable to start: an adapter whose
|
|
362
|
+
// `adapter-<platform>.json` is absent exits on the credential read, and
|
|
363
|
+
// launchd KeepAlive turns that into a crash loop. Skip it the same way an
|
|
364
|
+
// uninstalled service is skipped — the rule `gajaeway update` already
|
|
365
|
+
// applies when it renders adapter units — so one unconfigured adapter
|
|
366
|
+
// cannot abort the rest of the sequence.
|
|
367
|
+
const adapterSpec = serviceSpecs().find((candidate) => candidate.label === step.label);
|
|
368
|
+
const adapterConfig = adapterSpec === undefined ? undefined : adapterConfigPath(options.home, adapterSpec.id);
|
|
369
|
+
if (adapterConfig !== undefined && !(await Bun.file(adapterConfig).exists())) {
|
|
370
|
+
step.result = "skipped";
|
|
371
|
+
step.detail = "adapter is not configured";
|
|
372
|
+
await writeReceipt(options.home, receipt);
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
340
375
|
|
|
341
376
|
if (step.label === "dev.gajaeway.broker" && receipt.trigger === "update") {
|
|
342
377
|
const stopCommand = serviceStopCommand(platform, options.uid);
|
|
@@ -389,12 +424,7 @@ async function runRestartStackWithLease(options: RunRestartOptions): Promise<Res
|
|
|
389
424
|
issuedAt = now();
|
|
390
425
|
let command = step.command;
|
|
391
426
|
let status = await runner(command);
|
|
392
|
-
if (
|
|
393
|
-
status !== 0 &&
|
|
394
|
-
receipt.trigger === "update" &&
|
|
395
|
-
platform === "darwin" &&
|
|
396
|
-
(await probe(step.label)) === undefined
|
|
397
|
-
) {
|
|
427
|
+
if (status !== 0 && platform === "darwin" && (await probe(step.label)) === undefined) {
|
|
398
428
|
command = serviceBootstrapCommand(options, platform, step.label);
|
|
399
429
|
step.command = command;
|
|
400
430
|
issuedAt = now();
|
package/src/services.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
1
|
+
import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { homeLayout } from "@gajae-gateway/home";
|
|
@@ -26,6 +26,13 @@ export interface ResolvePathOptions {
|
|
|
26
26
|
|
|
27
27
|
/** Service managers this CLI can write definitions for. */
|
|
28
28
|
export type ServicePlatform = "darwin" | "linux";
|
|
29
|
+
export interface ServiceCommandOptions {
|
|
30
|
+
readonly allowFailure?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export type ServiceCommandRunner = (
|
|
33
|
+
command: readonly string[],
|
|
34
|
+
options?: ServiceCommandOptions,
|
|
35
|
+
) => number | PromiseLike<number>;
|
|
29
36
|
|
|
30
37
|
export interface InstallServicesOptions {
|
|
31
38
|
readonly binDir: string;
|
|
@@ -106,6 +113,16 @@ export function serviceSpecs(): readonly ServiceSpec[] {
|
|
|
106
113
|
return SERVICE_SPECS;
|
|
107
114
|
}
|
|
108
115
|
|
|
116
|
+
/**
|
|
117
|
+
* The `adapter-<platform>.json` an adapter reads at startup, or undefined for services
|
|
118
|
+
* that are not adapters. A unit whose file is absent can never come up — the adapter
|
|
119
|
+
* exits on the credential read — so supervisors skip it instead of looping on it.
|
|
120
|
+
*/
|
|
121
|
+
export function adapterConfigPath(home: string, specId: string): string | undefined {
|
|
122
|
+
if (!specId.startsWith("adapter-")) return undefined;
|
|
123
|
+
return join(home, `adapter-${specId.slice("adapter-".length)}.json`);
|
|
124
|
+
}
|
|
125
|
+
|
|
109
126
|
function homeForEnvironment(env: NodeJS.ProcessEnv): string {
|
|
110
127
|
return env.GAJAEWAY_HOME || join(env.HOME || homedir(), ".gajaeway");
|
|
111
128
|
}
|
|
@@ -288,6 +305,55 @@ export function systemdUnitPath(spec: ServiceSpec, unitDir: string): string {
|
|
|
288
305
|
return join(unitDir, systemdUnitName(spec));
|
|
289
306
|
}
|
|
290
307
|
|
|
308
|
+
export interface RemoveStaleServicesOptions extends Pick<InstallServicesOptions, "launchAgentsDir" | "unitDir"> {
|
|
309
|
+
readonly binDir: string;
|
|
310
|
+
readonly platform: ServicePlatform;
|
|
311
|
+
readonly uid?: number;
|
|
312
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
313
|
+
readonly reloadSystemd?: boolean;
|
|
314
|
+
readonly run: ServiceCommandRunner;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** Stops and removes service definitions whose executable is no longer installed. */
|
|
318
|
+
export async function removeStaleServices(options: RemoveStaleServicesOptions): Promise<void> {
|
|
319
|
+
const env = options.env ?? process.env;
|
|
320
|
+
const userHome = env.HOME || homedir();
|
|
321
|
+
const definitionDir =
|
|
322
|
+
options.platform === "darwin"
|
|
323
|
+
? (options.launchAgentsDir ?? join(userHome, "Library", "LaunchAgents"))
|
|
324
|
+
: (options.unitDir ?? systemdUnitDir(env, userHome));
|
|
325
|
+
const expandedDefinitionDir = expandHome(definitionDir, userHome);
|
|
326
|
+
const staleDefinitions: Array<{ readonly label: string; readonly path: string; readonly unitName: string }> = [];
|
|
327
|
+
for (const spec of SERVICE_SPECS) {
|
|
328
|
+
if (await Bun.file(join(options.binDir, spec.binary)).exists()) continue;
|
|
329
|
+
const path =
|
|
330
|
+
options.platform === "darwin"
|
|
331
|
+
? launchAgentPlistPath(spec, expandedDefinitionDir)
|
|
332
|
+
: systemdUnitPath(spec, expandedDefinitionDir);
|
|
333
|
+
if (await Bun.file(path).exists())
|
|
334
|
+
staleDefinitions.push({ label: spec.label, path, unitName: systemdUnitName(spec) });
|
|
335
|
+
}
|
|
336
|
+
const execute = async (command: readonly string[], allowFailure = false): Promise<number> => {
|
|
337
|
+
const exitCode = await options.run(command, allowFailure ? { allowFailure: true } : undefined);
|
|
338
|
+
if (exitCode !== 0 && !allowFailure) throw new Error(`${command.join(" ")} exited with status ${exitCode}`);
|
|
339
|
+
return exitCode;
|
|
340
|
+
};
|
|
341
|
+
if (options.platform === "linux") {
|
|
342
|
+
if (staleDefinitions.length === 0) return;
|
|
343
|
+
await execute(["systemctl", "--user", "disable", "--now", ...staleDefinitions.map(({ unitName }) => unitName)]);
|
|
344
|
+
for (const { path } of staleDefinitions) await rm(path, { force: true });
|
|
345
|
+
if (options.reloadSystemd ?? true) await execute(["systemctl", "--user", "daemon-reload"]);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const target = `gui/${options.uid ?? process.getuid?.() ?? 0}`;
|
|
349
|
+
for (const { label, path } of staleDefinitions) {
|
|
350
|
+
const serviceTarget = `${target}/${label}`;
|
|
351
|
+
if ((await execute(["launchctl", "print", serviceTarget], true)) === 0)
|
|
352
|
+
await execute(["launchctl", "bootout", serviceTarget]);
|
|
353
|
+
await rm(path, { force: true });
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
291
357
|
/**
|
|
292
358
|
* The ordered commands that realign the whole stack.
|
|
293
359
|
*
|
|
@@ -375,10 +441,10 @@ export async function installServices(options: InstallServicesOptions): Promise<
|
|
|
375
441
|
await mkdir(targetDir, { recursive: true, mode: 0o700 });
|
|
376
442
|
const specs: ServiceSpec[] = [];
|
|
377
443
|
for (const spec of SERVICE_SPECS) {
|
|
378
|
-
if (
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
444
|
+
if (!(await Bun.file(join(binDir, spec.binary)).exists())) continue;
|
|
445
|
+
const adapterConfig = adapterConfigPath(home, spec.id);
|
|
446
|
+
if (options.configuredAdaptersOnly && adapterConfig !== undefined && !(await Bun.file(adapterConfig).exists()))
|
|
447
|
+
continue;
|
|
382
448
|
specs.push(spec);
|
|
383
449
|
}
|
|
384
450
|
const definitions = specs.map((spec) =>
|
package/src/update.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { launchRestartStack } from "./restart-stack";
|
|
|
20
20
|
import {
|
|
21
21
|
type InstallServicesOptions,
|
|
22
22
|
installServices,
|
|
23
|
+
removeStaleServices,
|
|
23
24
|
type ServicePlatform,
|
|
24
25
|
serviceSpecs,
|
|
25
26
|
systemdUnitName,
|
|
@@ -39,8 +40,6 @@ export const UPDATE_USAGE = "usage: gajaeway update [--check] [--force] [--bin-d
|
|
|
39
40
|
|
|
40
41
|
export const DEFAULT_RELEASE_REPO = "Yeachan-Heo/gajae-way";
|
|
41
42
|
const RELEASE_REPO_OVERRIDE_ENV = "GAJAEWAY_UPDATE_REPO";
|
|
42
|
-
/** The release manifest carrying the gjc pin; installed beside the binaries. */
|
|
43
|
-
const PIN_MANIFEST = "package.json";
|
|
44
43
|
/** A fork can point updates at its own releases; the declared upstream stays the default. */
|
|
45
44
|
export function releaseRepo(env: NodeJS.ProcessEnv = process.env): string {
|
|
46
45
|
const override = env[RELEASE_REPO_OVERRIDE_ENV]?.trim();
|
|
@@ -214,7 +213,10 @@ export type ReleaseFetcher = (repo: string) => Promise<ReleaseInfo>;
|
|
|
214
213
|
export type AssetDownloader = (asset: ReleaseAsset) => Promise<Uint8Array>;
|
|
215
214
|
export type TarballExtractor = (tarball: Uint8Array, intoDir: string) => Promise<void>;
|
|
216
215
|
export type RestartQueuer = (home: string, binDir: string, trigger: "update") => Promise<RestartQueueReceipt>;
|
|
217
|
-
export
|
|
216
|
+
export interface UpdateCommandOptions {
|
|
217
|
+
readonly allowFailure?: boolean;
|
|
218
|
+
}
|
|
219
|
+
export type UpdateCommandRunner = (command: readonly string[], options?: UpdateCommandOptions) => Promise<number>;
|
|
218
220
|
|
|
219
221
|
type UpdateServiceOptions = Pick<
|
|
220
222
|
InstallServicesOptions,
|
|
@@ -262,10 +264,10 @@ async function readPinnedGjcVersion(path: string): Promise<string> {
|
|
|
262
264
|
return version;
|
|
263
265
|
}
|
|
264
266
|
|
|
265
|
-
async function defaultUpdateCommandRunner(command: readonly string[]): Promise<number> {
|
|
267
|
+
async function defaultUpdateCommandRunner(command: readonly string[], options?: UpdateCommandOptions): Promise<number> {
|
|
266
268
|
const child = Bun.spawn([...command], { stdin: "ignore", stdout: "ignore", stderr: "pipe" });
|
|
267
269
|
const [stderr, exitCode] = await Promise.all([new Response(child.stderr).text(), child.exited]);
|
|
268
|
-
if (exitCode !== 0) {
|
|
270
|
+
if (exitCode !== 0 && !options?.allowFailure) {
|
|
269
271
|
const detail = stderr.trim();
|
|
270
272
|
throw new Error(`${command.join(" ")} exited with status ${exitCode}${detail.length === 0 ? "" : `: ${detail}`}`);
|
|
271
273
|
}
|
|
@@ -281,7 +283,22 @@ async function installUpdatedServices(options: {
|
|
|
281
283
|
}): Promise<boolean> {
|
|
282
284
|
const { home, binDir, platform, noRestart, deps } = options;
|
|
283
285
|
const brokerProvisioned = await exists(homeLayout(home).brokerProvisionManifest);
|
|
284
|
-
const env = { ...(deps.env ?? process.env), GAJAEWAY_HOME: home };
|
|
286
|
+
const env: NodeJS.ProcessEnv = { ...(deps.env ?? process.env), GAJAEWAY_HOME: home };
|
|
287
|
+
const run = deps.serviceRunner ?? defaultUpdateCommandRunner;
|
|
288
|
+
const execute = async (command: readonly string[]): Promise<void> => {
|
|
289
|
+
const exitCode = await run(command);
|
|
290
|
+
if (exitCode !== 0) throw new Error(`${command.join(" ")} exited with status ${exitCode}`);
|
|
291
|
+
};
|
|
292
|
+
if (!noRestart) {
|
|
293
|
+
await removeStaleServices({
|
|
294
|
+
binDir,
|
|
295
|
+
platform,
|
|
296
|
+
env,
|
|
297
|
+
reloadSystemd: false,
|
|
298
|
+
...deps.serviceOptions,
|
|
299
|
+
run,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
285
302
|
await installServices({
|
|
286
303
|
binDir,
|
|
287
304
|
platform,
|
|
@@ -291,15 +308,11 @@ async function installUpdatedServices(options: {
|
|
|
291
308
|
...deps.serviceOptions,
|
|
292
309
|
});
|
|
293
310
|
if (platform === "linux") {
|
|
294
|
-
const run = deps.serviceRunner ?? defaultUpdateCommandRunner;
|
|
295
|
-
const execute = async (command: readonly string[]): Promise<void> => {
|
|
296
|
-
const exitCode = await run(command);
|
|
297
|
-
if (exitCode !== 0) throw new Error(`${command.join(" ")} exited with status ${exitCode}`);
|
|
298
|
-
};
|
|
299
311
|
await execute(["systemctl", "--user", "daemon-reload"]);
|
|
300
312
|
if (!noRestart) {
|
|
301
313
|
const enableUnits: string[] = [];
|
|
302
314
|
for (const spec of serviceSpecs()) {
|
|
315
|
+
if (!(await exists(join(binDir, spec.binary)))) continue;
|
|
303
316
|
if (spec.id === "broker" && !brokerProvisioned) continue;
|
|
304
317
|
if (
|
|
305
318
|
spec.id.startsWith("adapter-") &&
|
|
@@ -362,7 +375,7 @@ export async function runUpdate(options: RunUpdateOptions): Promise<UpdateResult
|
|
|
362
375
|
throw new Error(`release tarball is missing core binaries: ${missingCore.join(", ")}`);
|
|
363
376
|
await assertStagedBinariesUsable(staged, toInstall);
|
|
364
377
|
|
|
365
|
-
const gjcVersion = await readPinnedGjcVersion(join(staged.dir,
|
|
378
|
+
const gjcVersion = await readPinnedGjcVersion(join(staged.dir, "package.json"));
|
|
366
379
|
await installPinnedGjc({
|
|
367
380
|
home: options.home,
|
|
368
381
|
version: gjcVersion,
|
|
@@ -372,20 +385,22 @@ export async function runUpdate(options: RunUpdateOptions): Promise<UpdateResult
|
|
|
372
385
|
const stamp = new Date((deps.now ?? Date.now)()).toISOString().replace(/[-:]/g, "").slice(0, 15);
|
|
373
386
|
const backupDir = join(binDir, `backup-${current}-${stamp}Z`);
|
|
374
387
|
await mkdir(backupDir, { recursive: false });
|
|
375
|
-
|
|
376
|
-
// beside them; a stale one makes the new binaries refuse the gjc just installed.
|
|
377
|
-
const replaced = [...toInstall, PIN_MANIFEST];
|
|
378
|
-
for (const name of replaced) {
|
|
388
|
+
for (const name of toInstall) {
|
|
379
389
|
const target = join(binDir, name);
|
|
380
390
|
if (await exists(target)) await copyFile(target, join(backupDir, name));
|
|
381
391
|
}
|
|
382
|
-
|
|
392
|
+
const packagePath = join(binDir, "package.json");
|
|
393
|
+
if (await exists(packagePath)) await copyFile(packagePath, join(backupDir, "package.json"));
|
|
394
|
+
for (const name of toInstall) {
|
|
383
395
|
const stagedPath = join(staged.dir, name);
|
|
384
396
|
const tempPath = join(binDir, `.update-${name}`);
|
|
385
397
|
await copyFile(stagedPath, tempPath);
|
|
386
|
-
await chmod(tempPath,
|
|
398
|
+
await chmod(tempPath, 0o755);
|
|
387
399
|
await rename(tempPath, join(binDir, name));
|
|
388
400
|
}
|
|
401
|
+
const packageTempPath = join(binDir, ".update-package.json");
|
|
402
|
+
await copyFile(join(staged.dir, "package.json"), packageTempPath);
|
|
403
|
+
await rename(packageTempPath, packagePath);
|
|
389
404
|
await writeFile(releaseMarkerPath(binDir), `${latest}\n`, "utf8");
|
|
390
405
|
const brokerNotEnabled = await installUpdatedServices({
|
|
391
406
|
home: options.home,
|