@gajae-gateway/cli 0.2.0 → 0.2.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 +14 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +154 -33
- package/dist/restart-stack.d.ts +6 -2
- package/dist/restart-stack.d.ts.map +1 -1
- package/dist/update.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/main.ts +119 -10
- package/src/restart-stack.ts +73 -24
- package/src/update.ts +9 -4
package/dist/main.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import type { OpsCycleResult, OpsDeliveriesResult, OpsSloResult } from "@gajae-gateway/protocol";
|
|
3
|
+
import { GajaewayClient } from "@gajae-gateway/sdk";
|
|
3
4
|
import { type LaunchRestartOptions, type RunRestartOptions } from "./restart-stack";
|
|
4
5
|
import { type InstallServicesOptions, type ServicePlatform } from "./services";
|
|
5
6
|
import { type RunSetupOptions } from "./setup";
|
|
@@ -12,7 +13,7 @@ export declare function socketPath(home?: string | undefined): string;
|
|
|
12
13
|
* never after, so the list is checked up front.
|
|
13
14
|
*/
|
|
14
15
|
export declare const COMMANDS: readonly ["status", "shutdown", "chat", "daemon", "sessions", "ops", "memory", "monitors", "work", "services", "migrate", "update", "setup"];
|
|
15
|
-
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 ... (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";
|
|
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] [--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
17
|
/** Usage errors exit 2, as `gajaeway-gateway` does; 1 stays a runtime failure. */
|
|
17
18
|
export declare const USAGE_EXIT_CODE = 2;
|
|
18
19
|
/** The usage text when `command` cannot be dispatched, undefined when it can. */
|
|
@@ -62,5 +63,17 @@ export declare function cycleExitCode(cycle: OpsCycleResult): number;
|
|
|
62
63
|
export declare function renderSlo(result: OpsSloResult): string[];
|
|
63
64
|
export declare function sloExitCode(result: OpsSloResult): number;
|
|
64
65
|
export declare function renderDeliveries(result: OpsDeliveriesResult): string[];
|
|
66
|
+
/**
|
|
67
|
+
* Poll for memory operation completion. Memory operations (audit, autolink) are
|
|
68
|
+
* long-running and return an opRef immediately; this function polls for the result.
|
|
69
|
+
* If the CLI wait expires before completion, report 'still running' instead of failure.
|
|
70
|
+
*/
|
|
71
|
+
export declare function pollMemoryOp(client: Pick<GajaewayClient, "request">, opRef: string, maxWaitMs?: number, // 5 min default CLI timeout
|
|
72
|
+
pollIntervalMs?: number): Promise<{
|
|
73
|
+
status: "completed" | "failed" | "still_running";
|
|
74
|
+
elapsedMs: number;
|
|
75
|
+
result?: unknown;
|
|
76
|
+
error?: string;
|
|
77
|
+
}>;
|
|
65
78
|
export declare function main(args?: string[], options?: MainOptions): Promise<void>;
|
|
66
79
|
//# sourceMappingURL=main.d.ts.map
|
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
|
@@ -8,7 +8,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.2.
|
|
11
|
+
version: "0.2.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",
|
|
@@ -1879,7 +1879,9 @@ async function runRestartStackWithLease(options) {
|
|
|
1879
1879
|
originalPids.set(spec.label, (await probe(spec.label))?.pid);
|
|
1880
1880
|
}
|
|
1881
1881
|
const existing = await readRestartReceipt(options.home);
|
|
1882
|
-
|
|
1882
|
+
if (existing?.id === options.id && existing.state !== "queued")
|
|
1883
|
+
throw new Error(`restart-stack ${options.id}: receipt is ${existing.state}; refusing to start worker`);
|
|
1884
|
+
const receipt = existing?.id === options.id ? existing : newReceipt(options.id, platform, options.uid, now(), options.trigger ?? "manual");
|
|
1883
1885
|
receipt.state = "running";
|
|
1884
1886
|
receipt.supervisorPid = process.pid;
|
|
1885
1887
|
await writeReceipt(options.home, receipt);
|
|
@@ -2034,6 +2036,7 @@ async function runRestartStackWithLease(options) {
|
|
|
2034
2036
|
}
|
|
2035
2037
|
}
|
|
2036
2038
|
}
|
|
2039
|
+
var SYSTEMD_RUN_TIMEOUT_MS = 30000;
|
|
2037
2040
|
function cliCommand() {
|
|
2038
2041
|
return Bun.main.startsWith("/$bunfs/") ? [process.execPath] : [process.execPath, Bun.main];
|
|
2039
2042
|
}
|
|
@@ -2045,31 +2048,67 @@ async function launchRestartStack(options) {
|
|
|
2045
2048
|
await writeReceipt(options.home, receipt);
|
|
2046
2049
|
const worker = [...options.worker ?? [...cliCommand(), "ops", "restart-stack"], "--run", receipt.id];
|
|
2047
2050
|
const argv = supervisorArgv(platform, worker, options.home, receipt.id, options.binDir);
|
|
2051
|
+
const spawnOptions = {
|
|
2052
|
+
detached: true,
|
|
2053
|
+
stdin: "ignore",
|
|
2054
|
+
stdout: "ignore",
|
|
2055
|
+
stderr: "ignore",
|
|
2056
|
+
env: {
|
|
2057
|
+
...options.env ?? process.env,
|
|
2058
|
+
GAJAEWAY_HOME: options.home,
|
|
2059
|
+
...options.binDir === undefined ? {} : { GAJAEWAY_BIN_DIR: options.binDir }
|
|
2060
|
+
}
|
|
2061
|
+
};
|
|
2048
2062
|
let child;
|
|
2049
2063
|
try {
|
|
2050
|
-
child = Bun.spawn(argv,
|
|
2051
|
-
detached: true,
|
|
2052
|
-
stdin: "ignore",
|
|
2053
|
-
stdout: "ignore",
|
|
2054
|
-
stderr: "ignore",
|
|
2055
|
-
env: {
|
|
2056
|
-
...options.env ?? process.env,
|
|
2057
|
-
GAJAEWAY_HOME: options.home,
|
|
2058
|
-
...options.binDir === undefined ? {} : { GAJAEWAY_BIN_DIR: options.binDir }
|
|
2059
|
-
}
|
|
2060
|
-
});
|
|
2064
|
+
child = platform === "linux" ? (options.systemdRun ?? ((command) => Bun.spawn([...command], spawnOptions)))(argv) : Bun.spawn(argv, spawnOptions);
|
|
2061
2065
|
} catch (error) {
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
await
|
|
2068
|
-
|
|
2066
|
+
const detail = platform === "linux" ? `supervisor systemd-run could not be started (${error instanceof Error ? error.message : String(error)}); without a service manager here, restart through the host's own supervisor` : `supervisor ${argv[0]} could not be started (${error instanceof Error ? error.message : String(error)})`;
|
|
2067
|
+
await failLaunch(options.home, receipt, detail);
|
|
2068
|
+
throw new Error(`restart-stack ${receipt.id}: failed: ${detail}`);
|
|
2069
|
+
}
|
|
2070
|
+
if (platform === "linux") {
|
|
2071
|
+
const outcome = await waitForLauncher(child, options.systemdRunTimeoutMs ?? SYSTEMD_RUN_TIMEOUT_MS);
|
|
2072
|
+
if (outcome.kind === "timed_out") {
|
|
2073
|
+
const detail = `supervisor systemd-run timed out after ${options.systemdRunTimeoutMs ?? SYSTEMD_RUN_TIMEOUT_MS}ms; handoff outcome is indeterminate`;
|
|
2074
|
+
await failLaunch(options.home, receipt, detail);
|
|
2075
|
+
throw new Error(`restart-stack ${receipt.id}: failed: ${detail}`);
|
|
2076
|
+
}
|
|
2077
|
+
if (outcome.exitStatus !== 0) {
|
|
2078
|
+
const detail = `supervisor systemd-run exited with status ${outcome.exitStatus}`;
|
|
2079
|
+
await failLaunch(options.home, receipt, detail);
|
|
2080
|
+
throw new Error(`restart-stack ${receipt.id}: failed: ${detail}`);
|
|
2081
|
+
}
|
|
2069
2082
|
}
|
|
2070
2083
|
child.unref();
|
|
2071
2084
|
return { receipt, supervisorPid: child.pid };
|
|
2072
2085
|
}
|
|
2086
|
+
async function failLaunch(home, receipt, detail) {
|
|
2087
|
+
receipt.state = "failed";
|
|
2088
|
+
receipt.finishedAt = new Date().toISOString();
|
|
2089
|
+
receipt.detail = detail;
|
|
2090
|
+
for (const step of receipt.steps)
|
|
2091
|
+
step.result = "skipped";
|
|
2092
|
+
await writeReceipt(home, receipt);
|
|
2093
|
+
}
|
|
2094
|
+
async function waitForLauncher(child, timeoutMs) {
|
|
2095
|
+
let timer;
|
|
2096
|
+
const outcome = await Promise.race([
|
|
2097
|
+
child.exited.then((exitStatus) => ({ kind: "exited", exitStatus })),
|
|
2098
|
+
new Promise((resolve) => {
|
|
2099
|
+
timer = setTimeout(() => resolve({ kind: "timed_out" }), timeoutMs);
|
|
2100
|
+
})
|
|
2101
|
+
]);
|
|
2102
|
+
if (timer !== undefined)
|
|
2103
|
+
clearTimeout(timer);
|
|
2104
|
+
if (outcome.kind === "timed_out") {
|
|
2105
|
+
child.kill("SIGKILL");
|
|
2106
|
+
child.exited.catch(() => {
|
|
2107
|
+
return;
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2110
|
+
return outcome;
|
|
2111
|
+
}
|
|
2073
2112
|
function supervisorArgv(platform, worker, home, id, binDir) {
|
|
2074
2113
|
if (platform === "darwin")
|
|
2075
2114
|
return [...worker];
|
|
@@ -2078,6 +2117,7 @@ function supervisorArgv(platform, worker, home, id, binDir) {
|
|
|
2078
2117
|
"--user",
|
|
2079
2118
|
"--collect",
|
|
2080
2119
|
"--quiet",
|
|
2120
|
+
"--service-type=exec",
|
|
2081
2121
|
`--unit=gajaeway-restart-stack-${id}`,
|
|
2082
2122
|
`--setenv=GAJAEWAY_HOME=${home}`,
|
|
2083
2123
|
...binDir === undefined ? [] : [`--setenv=GAJAEWAY_BIN_DIR=${binDir}`],
|
|
@@ -2841,6 +2881,7 @@ async function installPinnedGjc(options) {
|
|
|
2841
2881
|
var UPDATE_USAGE = "usage: gajaeway update [--check] [--force] [--bin-dir DIR] [--no-restart]";
|
|
2842
2882
|
var DEFAULT_RELEASE_REPO = "Yeachan-Heo/gajae-way";
|
|
2843
2883
|
var RELEASE_REPO_OVERRIDE_ENV = "GAJAEWAY_UPDATE_REPO";
|
|
2884
|
+
var PIN_MANIFEST = "package.json";
|
|
2844
2885
|
function releaseRepo(env = process.env) {
|
|
2845
2886
|
const override = env[RELEASE_REPO_OVERRIDE_ENV]?.trim();
|
|
2846
2887
|
return override ? override : declaredReleaseRepo();
|
|
@@ -3051,7 +3092,7 @@ async function runUpdate(options) {
|
|
|
3051
3092
|
if (missingCore.length > 0)
|
|
3052
3093
|
throw new Error(`release tarball is missing core binaries: ${missingCore.join(", ")}`);
|
|
3053
3094
|
await assertStagedBinariesUsable(staged, toInstall);
|
|
3054
|
-
const gjcVersion = await readPinnedGjcVersion(join10(staged.dir,
|
|
3095
|
+
const gjcVersion = await readPinnedGjcVersion(join10(staged.dir, PIN_MANIFEST));
|
|
3055
3096
|
await installPinnedGjc({
|
|
3056
3097
|
home: options.home,
|
|
3057
3098
|
version: gjcVersion,
|
|
@@ -3060,16 +3101,17 @@ async function runUpdate(options) {
|
|
|
3060
3101
|
const stamp = new Date((deps.now ?? Date.now)()).toISOString().replace(/[-:]/g, "").slice(0, 15);
|
|
3061
3102
|
const backupDir = join10(binDir, `backup-${current}-${stamp}Z`);
|
|
3062
3103
|
await mkdir7(backupDir, { recursive: false });
|
|
3063
|
-
|
|
3104
|
+
const replaced = [...toInstall, PIN_MANIFEST];
|
|
3105
|
+
for (const name of replaced) {
|
|
3064
3106
|
const target = join10(binDir, name);
|
|
3065
3107
|
if (await exists(target))
|
|
3066
3108
|
await copyFile(target, join10(backupDir, name));
|
|
3067
3109
|
}
|
|
3068
|
-
for (const name of
|
|
3110
|
+
for (const name of replaced) {
|
|
3069
3111
|
const stagedPath = join10(staged.dir, name);
|
|
3070
3112
|
const tempPath = join10(binDir, `.update-${name}`);
|
|
3071
3113
|
await copyFile(stagedPath, tempPath);
|
|
3072
|
-
await chmod5(tempPath, 493);
|
|
3114
|
+
await chmod5(tempPath, name === PIN_MANIFEST ? 420 : 493);
|
|
3073
3115
|
await rename5(tempPath, join10(binDir, name));
|
|
3074
3116
|
}
|
|
3075
3117
|
await writeFile6(releaseMarkerPath(binDir), `${latest}
|
|
@@ -3211,7 +3253,7 @@ var COMMANDS = [
|
|
|
3211
3253
|
"update",
|
|
3212
3254
|
"setup"
|
|
3213
3255
|
];
|
|
3214
|
-
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 ... (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";
|
|
3256
|
+
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] [--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";
|
|
3215
3257
|
var USAGE_EXIT_CODE = 2;
|
|
3216
3258
|
function usageFor(command) {
|
|
3217
3259
|
return command !== undefined && COMMANDS.includes(command) ? undefined : CLI_USAGE;
|
|
@@ -3320,6 +3362,13 @@ function parseMonitorUpdateArgs(args) {
|
|
|
3320
3362
|
var MONITOR_TEST_USAGE = "usage: gajaeway monitors test <id> [--type T] [--payload J] [--wait[=SECONDS]]";
|
|
3321
3363
|
var MONITOR_WAIT_ERROR = `${MONITOR_TEST_USAGE}
|
|
3322
3364
|
--wait expects a finite non-negative integer up to 86400`;
|
|
3365
|
+
var MONITOR_OPERATOR_USAGE = "usage: gajaeway monitors settle <eventId>|retry <eventId>";
|
|
3366
|
+
function parseMonitorEventOperatorArgs(args) {
|
|
3367
|
+
const eventId = args[0];
|
|
3368
|
+
if (args.length !== 1 || !eventId?.trim() || eventId.startsWith("--"))
|
|
3369
|
+
throw new Error(MONITOR_OPERATOR_USAGE);
|
|
3370
|
+
return eventId;
|
|
3371
|
+
}
|
|
3323
3372
|
function parseMonitorTestArgs(args) {
|
|
3324
3373
|
const [monitorId, ...flags] = args;
|
|
3325
3374
|
if (!monitorId || monitorId.startsWith("--"))
|
|
@@ -3370,7 +3419,7 @@ unknown argument: ${flag}`);
|
|
|
3370
3419
|
...waitSeconds === undefined ? {} : { waitSeconds }
|
|
3371
3420
|
};
|
|
3372
3421
|
}
|
|
3373
|
-
var TERMINAL_MONITOR_STAGES = new Set(["delivered", "authored_no_delivery", "failed", "failed_no_retry"]);
|
|
3422
|
+
var TERMINAL_MONITOR_STAGES = new Set(["delivered", "authored_no_delivery", "failed", "failed_no_retry", "skipped"]);
|
|
3374
3423
|
function waitForMonitorStage(client, eventId, timeoutSeconds) {
|
|
3375
3424
|
return new Promise((resolve) => {
|
|
3376
3425
|
let stage = "admitted";
|
|
@@ -3607,6 +3656,25 @@ async function chat(socket) {
|
|
|
3607
3656
|
await client.close();
|
|
3608
3657
|
}
|
|
3609
3658
|
}
|
|
3659
|
+
async function pollMemoryOp(client, opRef, maxWaitMs = 300000, pollIntervalMs = 1000) {
|
|
3660
|
+
const startTime = Date.now();
|
|
3661
|
+
let operationElapsedMs = 0;
|
|
3662
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
3663
|
+
const status = await client.request("memory.status", { opRef });
|
|
3664
|
+
operationElapsedMs = status.elapsedMs;
|
|
3665
|
+
if (status.status === "completed") {
|
|
3666
|
+
return { status: "completed", elapsedMs: status.elapsedMs, result: status.result };
|
|
3667
|
+
}
|
|
3668
|
+
if (status.status === "failed") {
|
|
3669
|
+
return { status: "failed", elapsedMs: status.elapsedMs, error: status.error };
|
|
3670
|
+
}
|
|
3671
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
3672
|
+
}
|
|
3673
|
+
const waitedMs = Date.now() - startTime;
|
|
3674
|
+
console.error(`memory operation still running (${Math.round(operationElapsedMs / 1000)}s operation elapsed; CLI wait ended after ${Math.round(waitedMs / 1000)}s).
|
|
3675
|
+
Check status: gajaeway memory status ${opRef}`);
|
|
3676
|
+
return { status: "still_running", elapsedMs: operationElapsedMs };
|
|
3677
|
+
}
|
|
3610
3678
|
async function main(args = process.argv.slice(2), options = {}) {
|
|
3611
3679
|
const parsed = parseArgs(args);
|
|
3612
3680
|
if (parsed.command === "--version" && parsed.rest.length === 0) {
|
|
@@ -3771,20 +3839,52 @@ ${OPS_USAGE}`);
|
|
|
3771
3839
|
break;
|
|
3772
3840
|
}
|
|
3773
3841
|
case "memory": {
|
|
3774
|
-
const usage2 = "usage: gajaeway memory audit|
|
|
3842
|
+
const usage2 = "usage: gajaeway memory audit|autolink|search <query>|status <opRef>";
|
|
3775
3843
|
if ((parsed.rest[0] === "audit" || parsed.rest[0] === "autolink") && parsed.rest.length > 1)
|
|
3776
3844
|
throw new Error(`${usage2} (unknown argument: ${parsed.rest[1]})`);
|
|
3845
|
+
if (parsed.rest[0] === "status" && parsed.rest.length < 2)
|
|
3846
|
+
throw new Error(`${usage2} (status requires opRef)`);
|
|
3777
3847
|
const client = await connectGatewaySocket(parsed.socket);
|
|
3778
3848
|
try {
|
|
3779
3849
|
if (parsed.rest[0] === "audit") {
|
|
3780
|
-
const
|
|
3781
|
-
|
|
3782
|
-
if (
|
|
3850
|
+
const initResp = await client.request("memory.audit");
|
|
3851
|
+
const poll = await pollMemoryOp(client, initResp.opRef);
|
|
3852
|
+
if (poll.status === "failed") {
|
|
3853
|
+
console.error(`audit failed after ${poll.elapsedMs}ms: ${poll.error}`);
|
|
3783
3854
|
process.exitCode = 1;
|
|
3855
|
+
} else if (poll.status === "still_running") {
|
|
3856
|
+
process.exitCode = 75;
|
|
3857
|
+
} else {
|
|
3858
|
+
const auditResult = poll.result;
|
|
3859
|
+
console.log(JSON.stringify(auditResult?.issues ?? []));
|
|
3860
|
+
console.error(`audit completed in ${poll.elapsedMs}ms`);
|
|
3861
|
+
if (!auditResult?.ok)
|
|
3862
|
+
process.exitCode = 1;
|
|
3863
|
+
}
|
|
3784
3864
|
} else if (parsed.rest[0] === "autolink") {
|
|
3785
|
-
|
|
3865
|
+
const initResp = await client.request("memory.autolink");
|
|
3866
|
+
const poll = await pollMemoryOp(client, initResp.opRef);
|
|
3867
|
+
if (poll.status === "failed") {
|
|
3868
|
+
console.error(`autolink failed after ${poll.elapsedMs}ms: ${poll.error}`);
|
|
3869
|
+
process.exitCode = 1;
|
|
3870
|
+
} else if (poll.status === "still_running") {
|
|
3871
|
+
process.exitCode = 75;
|
|
3872
|
+
} else {
|
|
3873
|
+
console.log(JSON.stringify(poll.result));
|
|
3874
|
+
}
|
|
3786
3875
|
} else if (parsed.rest[0] === "search" && parsed.rest.slice(1).join(" ")) {
|
|
3787
3876
|
console.log(JSON.stringify(await client.request("memory.search", { query: parsed.rest.slice(1).join(" ") })));
|
|
3877
|
+
} else if (parsed.rest[0] === "status" && parsed.rest[1]) {
|
|
3878
|
+
const opRef = parsed.rest[1];
|
|
3879
|
+
const result = await client.request("memory.status", { opRef });
|
|
3880
|
+
if (result.status === "failed") {
|
|
3881
|
+
console.error(`operation ${opRef} failed after ${result.elapsedMs}ms: ${result.error}`);
|
|
3882
|
+
process.exitCode = 1;
|
|
3883
|
+
} else if (result.status === "completed") {
|
|
3884
|
+
console.log(JSON.stringify(result));
|
|
3885
|
+
} else {
|
|
3886
|
+
console.log(JSON.stringify({ status: result.status, elapsedMs: result.elapsedMs }));
|
|
3887
|
+
}
|
|
3788
3888
|
} else
|
|
3789
3889
|
throw new Error(usage2);
|
|
3790
3890
|
} finally {
|
|
@@ -3797,6 +3897,7 @@ ${OPS_USAGE}`);
|
|
|
3797
3897
|
const listOptions = command === "list" ? parseListOptions(args2, MONITOR_COLUMNS) : undefined;
|
|
3798
3898
|
const update = command === "update" ? parseMonitorUpdateArgs(args2) : undefined;
|
|
3799
3899
|
const monitorTest = command === "test" ? parseMonitorTestArgs(args2) : undefined;
|
|
3900
|
+
const operatorEventId = command === "settle" || command === "retry" ? parseMonitorEventOperatorArgs(args2) : undefined;
|
|
3800
3901
|
const client = await connectGatewaySocket(parsed.socket);
|
|
3801
3902
|
try {
|
|
3802
3903
|
if (command === "add" && args2[0] === "--json" && args2[1])
|
|
@@ -3832,8 +3933,15 @@ ${OPS_USAGE}`);
|
|
|
3832
3933
|
const stage = await waitForMonitorStage(client, result.eventId, monitorTest.waitSeconds);
|
|
3833
3934
|
console.log(JSON.stringify({ eventId: result.eventId, stage }));
|
|
3834
3935
|
}
|
|
3936
|
+
} else if ((command === "settle" || command === "retry") && operatorEventId !== undefined) {
|
|
3937
|
+
const result = await client.request(`monitor.${command}`, {
|
|
3938
|
+
eventId: operatorEventId
|
|
3939
|
+
});
|
|
3940
|
+
console.log(JSON.stringify(result));
|
|
3941
|
+
if (!result.done)
|
|
3942
|
+
process.exitCode = 1;
|
|
3835
3943
|
} else
|
|
3836
|
-
throw new Error(`usage: gajaeway monitors add --json '<MonitorSpec json>'|update <id> (--json '<partial MonitorSpec JSON>'|--schedule '<cron>' [--enabled true|false]|--enabled true|false [--schedule '<cron>'])|list [--json] [--fields ${columnNames(MONITOR_COLUMNS).join(",")}] [--limit N] [--offset N]|inspect <id>|remove <id>|test <id> [--type T] [--payload J] [--wait[=SECONDS]]`);
|
|
3944
|
+
throw new Error(`usage: gajaeway monitors add --json '<MonitorSpec json>'|update <id> (--json '<partial MonitorSpec JSON>'|--schedule '<cron>' [--enabled true|false]|--enabled true|false [--schedule '<cron>'])|list [--json] [--fields ${columnNames(MONITOR_COLUMNS).join(",")}] [--limit N] [--offset N]|inspect <id>|remove <id>|settle <eventId>|retry <eventId>|test <id> [--type T] [--payload J] [--wait[=SECONDS]]`);
|
|
3837
3945
|
} finally {
|
|
3838
3946
|
await client.close();
|
|
3839
3947
|
}
|
|
@@ -3930,11 +4038,13 @@ ${OPS_USAGE}`);
|
|
|
3930
4038
|
const name = args2[0];
|
|
3931
4039
|
let cwd;
|
|
3932
4040
|
let resume = false;
|
|
4041
|
+
let repo;
|
|
4042
|
+
let base;
|
|
3933
4043
|
let model;
|
|
3934
4044
|
const textParts = [];
|
|
3935
4045
|
for (let i = 1;i < args2.length; i++) {
|
|
3936
4046
|
const arg = args2[i];
|
|
3937
|
-
if (arg === "--cwd" || arg === "--model" || arg === "--preset") {
|
|
4047
|
+
if (arg === "--cwd" || arg === "--repo" || arg === "--base" || arg === "--model" || arg === "--preset") {
|
|
3938
4048
|
const value = args2[++i];
|
|
3939
4049
|
if (!value?.trim() || value.startsWith("--"))
|
|
3940
4050
|
throw new Error(usage2);
|
|
@@ -3942,6 +4052,14 @@ ${OPS_USAGE}`);
|
|
|
3942
4052
|
if (cwd !== undefined || !isAbsolute(value))
|
|
3943
4053
|
throw new Error(usage2);
|
|
3944
4054
|
cwd = value;
|
|
4055
|
+
} else if (arg === "--repo") {
|
|
4056
|
+
if (repo !== undefined)
|
|
4057
|
+
throw new Error(usage2);
|
|
4058
|
+
repo = value;
|
|
4059
|
+
} else if (arg === "--base") {
|
|
4060
|
+
if (base !== undefined)
|
|
4061
|
+
throw new Error(usage2);
|
|
4062
|
+
base = value;
|
|
3945
4063
|
} else {
|
|
3946
4064
|
if (model !== undefined)
|
|
3947
4065
|
throw new Error(`${usage2}
|
|
@@ -3967,6 +4085,8 @@ ${OPS_USAGE}`);
|
|
|
3967
4085
|
name,
|
|
3968
4086
|
text,
|
|
3969
4087
|
...cwd ? { cwd } : {},
|
|
4088
|
+
...repo ? { repo } : {},
|
|
4089
|
+
...base ? { base } : {},
|
|
3970
4090
|
...resume ? { resume: true } : {},
|
|
3971
4091
|
...model === undefined ? {} : { model },
|
|
3972
4092
|
...callerSessionId ? { callerSessionId } : {}
|
|
@@ -4045,6 +4165,7 @@ export {
|
|
|
4045
4165
|
main,
|
|
4046
4166
|
parseArgs,
|
|
4047
4167
|
parseServicesArgs,
|
|
4168
|
+
pollMemoryOp,
|
|
4048
4169
|
renderCycle,
|
|
4049
4170
|
renderDeliveries,
|
|
4050
4171
|
renderSlo,
|
package/dist/restart-stack.d.ts
CHANGED
|
@@ -93,12 +93,16 @@ export interface LaunchRestartOptions {
|
|
|
93
93
|
/** Argv that re-enters the CLI; `--run <id>` is appended. */
|
|
94
94
|
readonly worker?: readonly string[];
|
|
95
95
|
readonly env?: NodeJS.ProcessEnv;
|
|
96
|
+
/** Starts systemd-run; injectable for launcher-result handling. */
|
|
97
|
+
readonly systemdRun?: (command: readonly string[]) => Bun.Subprocess;
|
|
98
|
+
/** Maximum time to wait for systemd-run to acknowledge the worker exec. */
|
|
99
|
+
readonly systemdRunTimeoutMs?: number;
|
|
96
100
|
}
|
|
97
101
|
/** The argv that runs this CLI again, whether compiled or run from source. */
|
|
98
102
|
export declare function cliCommand(): string[];
|
|
99
103
|
/**
|
|
100
|
-
* Queues a restart and hands it to a detached supervisor, returning
|
|
101
|
-
*
|
|
104
|
+
* Queues a restart and hands it to a detached supervisor, returning once the
|
|
105
|
+
* platform launcher acknowledges it so the caller may be killed by the restart.
|
|
102
106
|
*/
|
|
103
107
|
export declare function launchRestartStack(options: LaunchRestartOptions): Promise<{
|
|
104
108
|
readonly receipt: RestartReceipt;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restart-stack.d.ts","sourceRoot":"","sources":["../src/restart-stack.ts"],"names":[],"mappings":"AAcA,OAAO,EAMN,KAAK,eAAe,EAKpB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAClF,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE5E,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8FAA8F;IAC9F,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1G,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACtC,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC;CAC9B;AAED,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;AACzF,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;AAClF,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAErE,2FAA2F;AAC3F,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAC9B,2GAA2G;IAC3G,QAAQ,CAAC,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACjD,uDAAuD;IACvD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,kGAAkG;IAClG,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,mGAAmG;IACnG,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC1B;AAMD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAS1F;AAsDD,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,SAA0B,GAAG,YAAY,CAa1G;AAqED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CAezF;
|
|
1
|
+
{"version":3,"file":"restart-stack.d.ts","sourceRoot":"","sources":["../src/restart-stack.ts"],"names":[],"mappings":"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"}
|
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,EAK3B,MAAM,YAAY,CAAC;AAEpB;;;;;;;;GAQG;AAEH,eAAO,MAAM,YAAY,8EAA8E,CAAC;AAExG,eAAO,MAAM,oBAAoB,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gajae-gateway/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.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.2.
|
|
45
|
-
"@gajae-gateway/protocol": "0.2.
|
|
44
|
+
"@gajae-gateway/sdk": "0.2.1",
|
|
45
|
+
"@gajae-gateway/protocol": "0.2.1"
|
|
46
46
|
},
|
|
47
47
|
"engines": {
|
|
48
48
|
"bun": ">=1.4.0"
|
package/src/main.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import { isAbsolute
|
|
3
|
+
import { isAbsolute } from "node:path";
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
|
+
MonitorEventOperatorResult,
|
|
6
7
|
MonitorEventRecord,
|
|
7
8
|
MonitorRecord,
|
|
8
9
|
MonitorScheduleProjection,
|
|
@@ -84,7 +85,7 @@ export const COMMANDS = [
|
|
|
84
85
|
] as const;
|
|
85
86
|
|
|
86
87
|
export const CLI_USAGE =
|
|
87
|
-
"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 ... (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";
|
|
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";
|
|
88
89
|
|
|
89
90
|
/** Usage errors exit 2, as `gajaeway-gateway` does; 1 stays a runtime failure. */
|
|
90
91
|
export const USAGE_EXIT_CODE = 2;
|
|
@@ -225,6 +226,13 @@ interface ParsedMonitorTestArgs {
|
|
|
225
226
|
|
|
226
227
|
const MONITOR_TEST_USAGE = "usage: gajaeway monitors test <id> [--type T] [--payload J] [--wait[=SECONDS]]";
|
|
227
228
|
const MONITOR_WAIT_ERROR = `${MONITOR_TEST_USAGE}\n--wait expects a finite non-negative integer up to 86400`;
|
|
229
|
+
const MONITOR_OPERATOR_USAGE = "usage: gajaeway monitors settle <eventId>|retry <eventId>";
|
|
230
|
+
|
|
231
|
+
function parseMonitorEventOperatorArgs(args: readonly string[]): string {
|
|
232
|
+
const eventId = args[0];
|
|
233
|
+
if (args.length !== 1 || !eventId?.trim() || eventId.startsWith("--")) throw new Error(MONITOR_OPERATOR_USAGE);
|
|
234
|
+
return eventId;
|
|
235
|
+
}
|
|
228
236
|
|
|
229
237
|
function parseMonitorTestArgs(args: readonly string[]): ParsedMonitorTestArgs {
|
|
230
238
|
const [monitorId, ...flags] = args;
|
|
@@ -265,7 +273,7 @@ function parseMonitorTestArgs(args: readonly string[]): ParsedMonitorTestArgs {
|
|
|
265
273
|
};
|
|
266
274
|
}
|
|
267
275
|
|
|
268
|
-
const TERMINAL_MONITOR_STAGES = new Set(["delivered", "authored_no_delivery", "failed", "failed_no_retry"]);
|
|
276
|
+
const TERMINAL_MONITOR_STAGES = new Set(["delivered", "authored_no_delivery", "failed", "failed_no_retry", "skipped"]);
|
|
269
277
|
|
|
270
278
|
function waitForMonitorStage(client: GajaewayClient, eventId: string, timeoutSeconds: number): Promise<string> {
|
|
271
279
|
return new Promise((resolve) => {
|
|
@@ -543,6 +551,49 @@ async function chat(socket: string): Promise<void> {
|
|
|
543
551
|
}
|
|
544
552
|
}
|
|
545
553
|
|
|
554
|
+
/**
|
|
555
|
+
* Poll for memory operation completion. Memory operations (audit, autolink) are
|
|
556
|
+
* long-running and return an opRef immediately; this function polls for the result.
|
|
557
|
+
* If the CLI wait expires before completion, report 'still running' instead of failure.
|
|
558
|
+
*/
|
|
559
|
+
export async function pollMemoryOp(
|
|
560
|
+
client: Pick<GajaewayClient, "request">,
|
|
561
|
+
opRef: string,
|
|
562
|
+
maxWaitMs = 300_000, // 5 min default CLI timeout
|
|
563
|
+
pollIntervalMs = 1_000,
|
|
564
|
+
): Promise<{ status: "completed" | "failed" | "still_running"; elapsedMs: number; result?: unknown; error?: string }> {
|
|
565
|
+
const startTime = Date.now();
|
|
566
|
+
let operationElapsedMs = 0;
|
|
567
|
+
|
|
568
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
569
|
+
const status = await client.request<{
|
|
570
|
+
status: "pending" | "completed" | "failed";
|
|
571
|
+
elapsedMs: number;
|
|
572
|
+
result?: unknown;
|
|
573
|
+
error?: string;
|
|
574
|
+
}>("memory.status", { opRef });
|
|
575
|
+
operationElapsedMs = status.elapsedMs;
|
|
576
|
+
|
|
577
|
+
if (status.status === "completed") {
|
|
578
|
+
return { status: "completed", elapsedMs: status.elapsedMs, result: status.result };
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (status.status === "failed") {
|
|
582
|
+
return { status: "failed", elapsedMs: status.elapsedMs, error: status.error };
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Wait before polling again
|
|
586
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// CLI wait expired, but operation may still be running on the server
|
|
590
|
+
const waitedMs = Date.now() - startTime;
|
|
591
|
+
console.error(
|
|
592
|
+
`memory operation still running (${Math.round(operationElapsedMs / 1000)}s operation elapsed; CLI wait ended after ${Math.round(waitedMs / 1000)}s).\nCheck status: gajaeway memory status ${opRef}`,
|
|
593
|
+
);
|
|
594
|
+
return { status: "still_running", elapsedMs: operationElapsedMs };
|
|
595
|
+
}
|
|
596
|
+
|
|
546
597
|
export async function main(args = process.argv.slice(2), options: MainOptions = {}): Promise<void> {
|
|
547
598
|
const parsed = parseArgs(args);
|
|
548
599
|
if (parsed.command === "--version" && parsed.rest.length === 0) {
|
|
@@ -721,24 +772,64 @@ export async function main(args = process.argv.slice(2), options: MainOptions =
|
|
|
721
772
|
break;
|
|
722
773
|
}
|
|
723
774
|
case "memory": {
|
|
724
|
-
const usage = "usage: gajaeway memory audit|
|
|
775
|
+
const usage = "usage: gajaeway memory audit|autolink|search <query>|status <opRef>";
|
|
725
776
|
// An unrecognised argument is refused before the socket is opened rather
|
|
726
777
|
// than ignored: a run of `memory audit --fix` that silently degraded to a
|
|
727
778
|
// plain audit would read as a repair attempt that reproduced the failure.
|
|
728
779
|
if ((parsed.rest[0] === "audit" || parsed.rest[0] === "autolink") && parsed.rest.length > 1)
|
|
729
780
|
throw new Error(`${usage} (unknown argument: ${parsed.rest[1]})`);
|
|
781
|
+
if (parsed.rest[0] === "status" && parsed.rest.length < 2) throw new Error(`${usage} (status requires opRef)`);
|
|
730
782
|
const client = await connectGatewaySocket(parsed.socket);
|
|
731
783
|
try {
|
|
732
784
|
if (parsed.rest[0] === "audit") {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
785
|
+
// Request audit operation; get opRef and poll for completion
|
|
786
|
+
const initResp = await client.request<{ opRef: string }>("memory.audit");
|
|
787
|
+
const poll = await pollMemoryOp(client, initResp.opRef);
|
|
788
|
+
if (poll.status === "failed") {
|
|
789
|
+
console.error(`audit failed after ${poll.elapsedMs}ms: ${poll.error}`);
|
|
790
|
+
process.exitCode = 1;
|
|
791
|
+
} else if (poll.status === "still_running") {
|
|
792
|
+
// Operation is still running; exit with distinct code so it's not reported as failure
|
|
793
|
+
process.exitCode = 75;
|
|
794
|
+
} else {
|
|
795
|
+
const auditResult = poll.result as { ok?: boolean; issues?: unknown[] };
|
|
796
|
+
console.log(JSON.stringify(auditResult?.issues ?? []));
|
|
797
|
+
console.error(`audit completed in ${poll.elapsedMs}ms`);
|
|
798
|
+
if (!auditResult?.ok) process.exitCode = 1;
|
|
799
|
+
}
|
|
736
800
|
} else if (parsed.rest[0] === "autolink") {
|
|
737
|
-
|
|
801
|
+
// Request autolink operation; get opRef and poll for completion
|
|
802
|
+
const initResp = await client.request<{ opRef: string }>("memory.autolink");
|
|
803
|
+
const poll = await pollMemoryOp(client, initResp.opRef);
|
|
804
|
+
if (poll.status === "failed") {
|
|
805
|
+
console.error(`autolink failed after ${poll.elapsedMs}ms: ${poll.error}`);
|
|
806
|
+
process.exitCode = 1;
|
|
807
|
+
} else if (poll.status === "still_running") {
|
|
808
|
+
// Operation is still running; exit with distinct code so it's not reported as failure
|
|
809
|
+
process.exitCode = 75;
|
|
810
|
+
} else {
|
|
811
|
+
console.log(JSON.stringify(poll.result));
|
|
812
|
+
}
|
|
738
813
|
} else if (parsed.rest[0] === "search" && parsed.rest.slice(1).join(" ")) {
|
|
739
814
|
console.log(
|
|
740
815
|
JSON.stringify(await client.request("memory.search", { query: parsed.rest.slice(1).join(" ") })),
|
|
741
816
|
);
|
|
817
|
+
} else if (parsed.rest[0] === "status" && parsed.rest[1]) {
|
|
818
|
+
const opRef = parsed.rest[1];
|
|
819
|
+
const result = await client.request<{
|
|
820
|
+
status: "pending" | "completed" | "failed";
|
|
821
|
+
elapsedMs: number;
|
|
822
|
+
result?: unknown;
|
|
823
|
+
error?: string;
|
|
824
|
+
}>("memory.status", { opRef });
|
|
825
|
+
if (result.status === "failed") {
|
|
826
|
+
console.error(`operation ${opRef} failed after ${result.elapsedMs}ms: ${result.error}`);
|
|
827
|
+
process.exitCode = 1;
|
|
828
|
+
} else if (result.status === "completed") {
|
|
829
|
+
console.log(JSON.stringify(result));
|
|
830
|
+
} else {
|
|
831
|
+
console.log(JSON.stringify({ status: result.status, elapsedMs: result.elapsedMs }));
|
|
832
|
+
}
|
|
742
833
|
} else throw new Error(usage);
|
|
743
834
|
} finally {
|
|
744
835
|
await client.close();
|
|
@@ -750,6 +841,8 @@ export async function main(args = process.argv.slice(2), options: MainOptions =
|
|
|
750
841
|
const listOptions = command === "list" ? parseListOptions(args, MONITOR_COLUMNS) : undefined;
|
|
751
842
|
const update = command === "update" ? parseMonitorUpdateArgs(args) : undefined;
|
|
752
843
|
const monitorTest = command === "test" ? parseMonitorTestArgs(args) : undefined;
|
|
844
|
+
const operatorEventId =
|
|
845
|
+
command === "settle" || command === "retry" ? parseMonitorEventOperatorArgs(args) : undefined;
|
|
753
846
|
const client = await connectGatewaySocket(parsed.socket);
|
|
754
847
|
try {
|
|
755
848
|
if (command === "add" && args[0] === "--json" && args[1])
|
|
@@ -787,9 +880,15 @@ export async function main(args = process.argv.slice(2), options: MainOptions =
|
|
|
787
880
|
const stage = await waitForMonitorStage(client, result.eventId, monitorTest.waitSeconds);
|
|
788
881
|
console.log(JSON.stringify({ eventId: result.eventId, stage }));
|
|
789
882
|
}
|
|
883
|
+
} else if ((command === "settle" || command === "retry") && operatorEventId !== undefined) {
|
|
884
|
+
const result = await client.request<MonitorEventOperatorResult>(`monitor.${command}`, {
|
|
885
|
+
eventId: operatorEventId,
|
|
886
|
+
});
|
|
887
|
+
console.log(JSON.stringify(result));
|
|
888
|
+
if (!result.done) process.exitCode = 1;
|
|
790
889
|
} else
|
|
791
890
|
throw new Error(
|
|
792
|
-
`usage: gajaeway monitors add --json '<MonitorSpec json>'|update <id> (--json '<partial MonitorSpec JSON>'|--schedule '<cron>' [--enabled true|false]|--enabled true|false [--schedule '<cron>'])|list [--json] [--fields ${columnNames(MONITOR_COLUMNS).join(",")}] [--limit N] [--offset N]|inspect <id>|remove <id>|test <id> [--type T] [--payload J] [--wait[=SECONDS]]`,
|
|
891
|
+
`usage: gajaeway monitors add --json '<MonitorSpec json>'|update <id> (--json '<partial MonitorSpec JSON>'|--schedule '<cron>' [--enabled true|false]|--enabled true|false [--schedule '<cron>'])|list [--json] [--fields ${columnNames(MONITOR_COLUMNS).join(",")}] [--limit N] [--offset N]|inspect <id>|remove <id>|settle <eventId>|retry <eventId>|test <id> [--type T] [--payload J] [--wait[=SECONDS]]`,
|
|
793
892
|
);
|
|
794
893
|
} finally {
|
|
795
894
|
await client.close();
|
|
@@ -907,16 +1006,24 @@ export async function main(args = process.argv.slice(2), options: MainOptions =
|
|
|
907
1006
|
const name = args[0];
|
|
908
1007
|
let cwd: string | undefined;
|
|
909
1008
|
let resume = false;
|
|
1009
|
+
let repo: string | undefined;
|
|
1010
|
+
let base: string | undefined;
|
|
910
1011
|
let model: string | { preset: string } | undefined;
|
|
911
1012
|
const textParts: string[] = [];
|
|
912
1013
|
for (let i = 1; i < args.length; i++) {
|
|
913
1014
|
const arg = args[i];
|
|
914
|
-
if (arg === "--cwd" || arg === "--model" || arg === "--preset") {
|
|
1015
|
+
if (arg === "--cwd" || arg === "--repo" || arg === "--base" || arg === "--model" || arg === "--preset") {
|
|
915
1016
|
const value = args[++i];
|
|
916
1017
|
if (!value?.trim() || value.startsWith("--")) throw new Error(usage);
|
|
917
1018
|
if (arg === "--cwd") {
|
|
918
1019
|
if (cwd !== undefined || !isAbsolute(value)) throw new Error(usage);
|
|
919
1020
|
cwd = value;
|
|
1021
|
+
} else if (arg === "--repo") {
|
|
1022
|
+
if (repo !== undefined) throw new Error(usage);
|
|
1023
|
+
repo = value;
|
|
1024
|
+
} else if (arg === "--base") {
|
|
1025
|
+
if (base !== undefined) throw new Error(usage);
|
|
1026
|
+
base = value;
|
|
920
1027
|
} else {
|
|
921
1028
|
if (model !== undefined) throw new Error(`${usage}\n--model and --preset are mutually exclusive`);
|
|
922
1029
|
model = arg === "--preset" ? { preset: value } : value;
|
|
@@ -938,6 +1045,8 @@ export async function main(args = process.argv.slice(2), options: MainOptions =
|
|
|
938
1045
|
name,
|
|
939
1046
|
text,
|
|
940
1047
|
...(cwd ? { cwd } : {}),
|
|
1048
|
+
...(repo ? { repo } : {}),
|
|
1049
|
+
...(base ? { base } : {}),
|
|
941
1050
|
...(resume ? { resume: true } : {}),
|
|
942
1051
|
...(model === undefined ? {} : { model }),
|
|
943
1052
|
...(callerSessionId ? { callerSessionId } : {}),
|
package/src/restart-stack.ts
CHANGED
|
@@ -304,8 +304,10 @@ async function runRestartStackWithLease(options: RunRestartOptions): Promise<Res
|
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
const existing = await readRestartReceipt(options.home);
|
|
307
|
+
if (existing?.id === options.id && existing.state !== "queued")
|
|
308
|
+
throw new Error(`restart-stack ${options.id}: receipt is ${existing.state}; refusing to start worker`);
|
|
307
309
|
const receipt =
|
|
308
|
-
existing?.id === options.id
|
|
310
|
+
existing?.id === options.id
|
|
309
311
|
? existing
|
|
310
312
|
: newReceipt(options.id, platform, options.uid, now(), options.trigger ?? "manual");
|
|
311
313
|
receipt.state = "running";
|
|
@@ -491,16 +493,22 @@ export interface LaunchRestartOptions {
|
|
|
491
493
|
/** Argv that re-enters the CLI; `--run <id>` is appended. */
|
|
492
494
|
readonly worker?: readonly string[];
|
|
493
495
|
readonly env?: NodeJS.ProcessEnv;
|
|
496
|
+
/** Starts systemd-run; injectable for launcher-result handling. */
|
|
497
|
+
readonly systemdRun?: (command: readonly string[]) => Bun.Subprocess;
|
|
498
|
+
/** Maximum time to wait for systemd-run to acknowledge the worker exec. */
|
|
499
|
+
readonly systemdRunTimeoutMs?: number;
|
|
494
500
|
}
|
|
495
501
|
|
|
502
|
+
const SYSTEMD_RUN_TIMEOUT_MS = 30_000;
|
|
503
|
+
|
|
496
504
|
/** The argv that runs this CLI again, whether compiled or run from source. */
|
|
497
505
|
export function cliCommand(): string[] {
|
|
498
506
|
return Bun.main.startsWith("/$bunfs/") ? [process.execPath] : [process.execPath, Bun.main];
|
|
499
507
|
}
|
|
500
508
|
|
|
501
509
|
/**
|
|
502
|
-
* Queues a restart and hands it to a detached supervisor, returning
|
|
503
|
-
*
|
|
510
|
+
* Queues a restart and hands it to a detached supervisor, returning once the
|
|
511
|
+
* platform launcher acknowledges it so the caller may be killed by the restart.
|
|
504
512
|
*/
|
|
505
513
|
export async function launchRestartStack(
|
|
506
514
|
options: LaunchRestartOptions,
|
|
@@ -512,35 +520,75 @@ export async function launchRestartStack(
|
|
|
512
520
|
await writeReceipt(options.home, receipt);
|
|
513
521
|
const worker = [...(options.worker ?? [...cliCommand(), "ops", "restart-stack"]), "--run", receipt.id];
|
|
514
522
|
const argv = supervisorArgv(platform, worker, options.home, receipt.id, options.binDir);
|
|
523
|
+
const spawnOptions = {
|
|
524
|
+
detached: true,
|
|
525
|
+
stdin: "ignore" as const,
|
|
526
|
+
stdout: "ignore" as const,
|
|
527
|
+
stderr: "ignore" as const,
|
|
528
|
+
env: {
|
|
529
|
+
...(options.env ?? process.env),
|
|
530
|
+
GAJAEWAY_HOME: options.home,
|
|
531
|
+
...(options.binDir === undefined ? {} : { GAJAEWAY_BIN_DIR: options.binDir }),
|
|
532
|
+
},
|
|
533
|
+
};
|
|
515
534
|
let child: Bun.Subprocess;
|
|
516
535
|
try {
|
|
517
|
-
child =
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
stderr: "ignore",
|
|
522
|
-
env: {
|
|
523
|
-
...(options.env ?? process.env),
|
|
524
|
-
GAJAEWAY_HOME: options.home,
|
|
525
|
-
...(options.binDir === undefined ? {} : { GAJAEWAY_BIN_DIR: options.binDir }),
|
|
526
|
-
},
|
|
527
|
-
});
|
|
536
|
+
child =
|
|
537
|
+
platform === "linux"
|
|
538
|
+
? (options.systemdRun ?? ((command) => Bun.spawn([...command], spawnOptions)))(argv)
|
|
539
|
+
: Bun.spawn(argv, spawnOptions);
|
|
528
540
|
} catch (error) {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
await
|
|
538
|
-
|
|
541
|
+
const detail =
|
|
542
|
+
platform === "linux"
|
|
543
|
+
? `supervisor systemd-run could not be started (${error instanceof Error ? error.message : String(error)}); without a service manager here, restart through the host's own supervisor`
|
|
544
|
+
: `supervisor ${argv[0]} could not be started (${error instanceof Error ? error.message : String(error)})`;
|
|
545
|
+
await failLaunch(options.home, receipt, detail);
|
|
546
|
+
throw new Error(`restart-stack ${receipt.id}: failed: ${detail}`);
|
|
547
|
+
}
|
|
548
|
+
if (platform === "linux") {
|
|
549
|
+
const outcome = await waitForLauncher(child, options.systemdRunTimeoutMs ?? SYSTEMD_RUN_TIMEOUT_MS);
|
|
550
|
+
if (outcome.kind === "timed_out") {
|
|
551
|
+
const detail = `supervisor systemd-run timed out after ${options.systemdRunTimeoutMs ?? SYSTEMD_RUN_TIMEOUT_MS}ms; handoff outcome is indeterminate`;
|
|
552
|
+
await failLaunch(options.home, receipt, detail);
|
|
553
|
+
throw new Error(`restart-stack ${receipt.id}: failed: ${detail}`);
|
|
554
|
+
}
|
|
555
|
+
if (outcome.exitStatus !== 0) {
|
|
556
|
+
const detail = `supervisor systemd-run exited with status ${outcome.exitStatus}`;
|
|
557
|
+
await failLaunch(options.home, receipt, detail);
|
|
558
|
+
throw new Error(`restart-stack ${receipt.id}: failed: ${detail}`);
|
|
559
|
+
}
|
|
539
560
|
}
|
|
540
561
|
child.unref();
|
|
541
562
|
return { receipt, supervisorPid: child.pid };
|
|
542
563
|
}
|
|
543
564
|
|
|
565
|
+
async function failLaunch(home: string, receipt: RestartReceipt, detail: string): Promise<void> {
|
|
566
|
+
receipt.state = "failed";
|
|
567
|
+
receipt.finishedAt = new Date().toISOString();
|
|
568
|
+
receipt.detail = detail;
|
|
569
|
+
for (const step of receipt.steps) step.result = "skipped";
|
|
570
|
+
await writeReceipt(home, receipt);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
async function waitForLauncher(
|
|
574
|
+
child: Bun.Subprocess,
|
|
575
|
+
timeoutMs: number,
|
|
576
|
+
): Promise<{ readonly kind: "exited"; readonly exitStatus: number } | { readonly kind: "timed_out" }> {
|
|
577
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
578
|
+
const outcome = await Promise.race([
|
|
579
|
+
child.exited.then((exitStatus) => ({ kind: "exited" as const, exitStatus })),
|
|
580
|
+
new Promise<{ readonly kind: "timed_out" }>((resolve) => {
|
|
581
|
+
timer = setTimeout(() => resolve({ kind: "timed_out" }), timeoutMs);
|
|
582
|
+
}),
|
|
583
|
+
]);
|
|
584
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
585
|
+
if (outcome.kind === "timed_out") {
|
|
586
|
+
child.kill("SIGKILL");
|
|
587
|
+
void child.exited.catch(() => undefined);
|
|
588
|
+
}
|
|
589
|
+
return outcome;
|
|
590
|
+
}
|
|
591
|
+
|
|
544
592
|
/**
|
|
545
593
|
* A new session escapes the caller's process group, which is enough under
|
|
546
594
|
* launchd. Under systemd the caller usually lives in a service cgroup that the
|
|
@@ -560,6 +608,7 @@ export function supervisorArgv(
|
|
|
560
608
|
"--user",
|
|
561
609
|
"--collect",
|
|
562
610
|
"--quiet",
|
|
611
|
+
"--service-type=exec",
|
|
563
612
|
`--unit=gajaeway-restart-stack-${id}`,
|
|
564
613
|
`--setenv=GAJAEWAY_HOME=${home}`,
|
|
565
614
|
...(binDir === undefined ? [] : [`--setenv=GAJAEWAY_BIN_DIR=${binDir}`]),
|
package/src/update.ts
CHANGED
|
@@ -39,6 +39,8 @@ export const UPDATE_USAGE = "usage: gajaeway update [--check] [--force] [--bin-d
|
|
|
39
39
|
|
|
40
40
|
export const DEFAULT_RELEASE_REPO = "Yeachan-Heo/gajae-way";
|
|
41
41
|
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";
|
|
42
44
|
/** A fork can point updates at its own releases; the declared upstream stays the default. */
|
|
43
45
|
export function releaseRepo(env: NodeJS.ProcessEnv = process.env): string {
|
|
44
46
|
const override = env[RELEASE_REPO_OVERRIDE_ENV]?.trim();
|
|
@@ -360,7 +362,7 @@ export async function runUpdate(options: RunUpdateOptions): Promise<UpdateResult
|
|
|
360
362
|
throw new Error(`release tarball is missing core binaries: ${missingCore.join(", ")}`);
|
|
361
363
|
await assertStagedBinariesUsable(staged, toInstall);
|
|
362
364
|
|
|
363
|
-
const gjcVersion = await readPinnedGjcVersion(join(staged.dir,
|
|
365
|
+
const gjcVersion = await readPinnedGjcVersion(join(staged.dir, PIN_MANIFEST));
|
|
364
366
|
await installPinnedGjc({
|
|
365
367
|
home: options.home,
|
|
366
368
|
version: gjcVersion,
|
|
@@ -370,15 +372,18 @@ export async function runUpdate(options: RunUpdateOptions): Promise<UpdateResult
|
|
|
370
372
|
const stamp = new Date((deps.now ?? Date.now)()).toISOString().replace(/[-:]/g, "").slice(0, 15);
|
|
371
373
|
const backupDir = join(binDir, `backup-${current}-${stamp}Z`);
|
|
372
374
|
await mkdir(backupDir, { recursive: false });
|
|
373
|
-
|
|
375
|
+
// The compiled gateway and broker host read the gjc pin from the package.json
|
|
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) {
|
|
374
379
|
const target = join(binDir, name);
|
|
375
380
|
if (await exists(target)) await copyFile(target, join(backupDir, name));
|
|
376
381
|
}
|
|
377
|
-
for (const name of
|
|
382
|
+
for (const name of replaced) {
|
|
378
383
|
const stagedPath = join(staged.dir, name);
|
|
379
384
|
const tempPath = join(binDir, `.update-${name}`);
|
|
380
385
|
await copyFile(stagedPath, tempPath);
|
|
381
|
-
await chmod(tempPath, 0o755);
|
|
386
|
+
await chmod(tempPath, name === PIN_MANIFEST ? 0o644 : 0o755);
|
|
382
387
|
await rename(tempPath, join(binDir, name));
|
|
383
388
|
}
|
|
384
389
|
await writeFile(releaseMarkerPath(binDir), `${latest}\n`, "utf8");
|