@hivemagnus/magnus-agent 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +3 -0
- package/README.md +47 -2
- package/deploy/magnus-agent-unprivileged.service +5 -0
- package/deploy/magnus-agent.service +5 -0
- package/dist/agent.d.ts +2 -0
- package/dist/agent.js +18 -3
- package/dist/env-catalog.js +2 -1
- package/dist/executors/historical-mutation.d.ts +5 -6
- package/dist/executors/historical-mutation.js +15 -2
- package/dist/executors/reviewed-historical-records.d.ts +22 -0
- package/dist/executors/reviewed-historical-records.js +117 -0
- package/dist/executors/scheduler-runner.js +12 -1
- package/dist/upgrade/install-service.js +15 -5
- package/dist/upgrade/routes.js +6 -1
- package/dist/upgrade/self-upgrade.d.ts +85 -10
- package/dist/upgrade/self-upgrade.js +459 -57
- package/launcher/launcher.mjs +383 -50
- package/package.json +5 -2
|
@@ -1,6 +1,23 @@
|
|
|
1
|
-
import { type AgentSelfUpgradeStatus, type AgentUpgradeRequest } from '@hivemagnus/magnus-contracts';
|
|
1
|
+
import { type AgentSelfUpgradeStatus, type AgentUpgradeReport, type AgentUpgradeRequest } from '@hivemagnus/magnus-contracts';
|
|
2
2
|
export declare const STATE_FILE = "upgrade-state.json";
|
|
3
|
+
export declare const LOCK_FILE = "upgrade-state.lock";
|
|
4
|
+
/** Written while on trial when heartbeats cannot reach the control API; the launcher then extends the trial. */
|
|
5
|
+
export declare const ALIVE_FILE = "trial-alive.json";
|
|
3
6
|
export declare const AGENT_PACKAGE = "@hivemagnus/magnus-agent";
|
|
7
|
+
/** The launcher protocol this agent needs. Kept equal to `magnusAgent.minLauncher` in package.json. */
|
|
8
|
+
export declare const MIN_LAUNCHER_PROTOCOL = 1;
|
|
9
|
+
/** Oldest npm the upgrade steps are tested with (overrides, `audit signatures`). */
|
|
10
|
+
export declare const MIN_NPM_VERSION = "10.0.0";
|
|
11
|
+
/** Per-step limits and the total budget; the server waits 15 minutes for an upgrade. */
|
|
12
|
+
export declare const STEP_TIMEOUT_MS: {
|
|
13
|
+
readonly pack: number;
|
|
14
|
+
readonly install: number;
|
|
15
|
+
readonly audit: number;
|
|
16
|
+
readonly smoke: 60000;
|
|
17
|
+
};
|
|
18
|
+
export declare const STAGE_BUDGET_MS: number;
|
|
19
|
+
/** After staging, the agent exits even if a graceful shutdown hangs. */
|
|
20
|
+
export declare const EXIT_BACKSTOP_MS = 60000;
|
|
4
21
|
export interface UpgradeState {
|
|
5
22
|
state: 'idle' | 'staged' | 'trial' | 'confirmed' | 'reverted' | 'failed';
|
|
6
23
|
from: string | null;
|
|
@@ -9,6 +26,7 @@ export interface UpgradeState {
|
|
|
9
26
|
attempt?: number;
|
|
10
27
|
maxAttempts?: number;
|
|
11
28
|
trialDeadline?: string;
|
|
29
|
+
extended?: number;
|
|
12
30
|
reason?: string | null;
|
|
13
31
|
updatedAt?: string;
|
|
14
32
|
}
|
|
@@ -20,51 +38,108 @@ export declare function readUpgradeState(dir: string): UpgradeState | null;
|
|
|
20
38
|
* the owner can still replace or remove it. Otherwise this does nothing.
|
|
21
39
|
*/
|
|
22
40
|
export declare function adoptInstallOwner(dir: string, path: string, uid?: number | undefined): void;
|
|
41
|
+
/** Temp file, fsync, rename, fsync the directory: a crash leaves the old or the new content, never a torn file. */
|
|
42
|
+
export declare function writeDurable(dir: string, name: string, text: string, mode?: number): void;
|
|
23
43
|
export declare function writeUpgradeState(dir: string, state: UpgradeState): void;
|
|
44
|
+
/** Atomically point `<dir>/<name>` (current, last-good) at versions/<version>. */
|
|
45
|
+
export declare function pointLink(dir: string, name: string, version: string): void;
|
|
24
46
|
export declare function pointCurrentAt(dir: string, version: string): void;
|
|
47
|
+
/** The X of a `<name> -> versions/X` link, or null. */
|
|
48
|
+
export declare function linkVersion(dir: string, name: string): string | null;
|
|
49
|
+
/** versions/<version> holds a runnable agent. */
|
|
50
|
+
export declare function versionComplete(dir: string, version: string | null | undefined): boolean;
|
|
51
|
+
/** ./current resolves to versions/<version>. */
|
|
52
|
+
export declare function runsVersion(dir: string, version: string): boolean;
|
|
53
|
+
/** O_EXCL lock shared with the launcher around a read-modify-write of the state file. Synchronous; waits up to timeoutMs. */
|
|
54
|
+
export declare function withStateLock<T>(dir: string, fn: () => T, { timeoutMs, staleMs }?: {
|
|
55
|
+
timeoutMs?: number | undefined;
|
|
56
|
+
staleMs?: number | undefined;
|
|
57
|
+
}): T;
|
|
25
58
|
export type Layout = AgentSelfUpgradeStatus['layout'];
|
|
26
59
|
export declare function detectLayout(dir: string, env?: NodeJS.ProcessEnv): Layout;
|
|
60
|
+
export interface NpmCommand {
|
|
61
|
+
command: string;
|
|
62
|
+
args: string[];
|
|
63
|
+
/** Prepended to PATH so npm and lifecycle scripts run the agent's own Node.js. */
|
|
64
|
+
pathPrefix: string;
|
|
65
|
+
version: string | null;
|
|
66
|
+
problem: string | null;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The npm that belongs to the running Node.js (npm-cli.js beside it, run with
|
|
70
|
+
* process.execPath), then MAGNUS_AGENT_NPM, then npm on PATH. A systemd PATH
|
|
71
|
+
* often lacks the directory of a version-managed Node.js.
|
|
72
|
+
*/
|
|
73
|
+
export declare function resolveNpm(env?: NodeJS.ProcessEnv, execPath?: string): NpmCommand;
|
|
27
74
|
/** Whether this agent can upgrade itself, and why not. Reported with every heartbeat. */
|
|
28
75
|
export declare function selfUpgradeStatus(version: string, env?: NodeJS.ProcessEnv): AgentSelfUpgradeStatus;
|
|
76
|
+
/** The upgrade state when `version` is the one on trial. */
|
|
77
|
+
export declare function trialOf(version: string, env?: NodeJS.ProcessEnv): UpgradeState | null;
|
|
78
|
+
/** Tell the launcher this version runs but cannot reach the control API, so it extends the trial instead of reverting. */
|
|
79
|
+
export declare function recordTrialAlive(version: string, error: string, env?: NodeJS.ProcessEnv, now?: number): boolean;
|
|
80
|
+
/** A failed heartbeat POST that says nothing about this version: no answer, or a gateway with no upstream. */
|
|
81
|
+
export declare function controlApiUnreachable(error: unknown): boolean;
|
|
82
|
+
/** `export const LAUNCHER_PROTOCOL = N` in a launcher file, without running it. */
|
|
83
|
+
export declare function declaredLauncherProtocol(path: string): number | null;
|
|
29
84
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
85
|
+
* Replace launcher/launcher.mjs with the confirmed version's copy when that
|
|
86
|
+
* declares a higher protocol and passes a self-check (`--protocol`). The
|
|
87
|
+
* running launcher keeps its loaded code; the new one is used from the next
|
|
88
|
+
* service start. The previous file is kept as launcher.mjs.previous.
|
|
89
|
+
*/
|
|
90
|
+
export declare function upgradeLauncher(dir: string, version: string, execPath?: string): string | null;
|
|
91
|
+
/**
|
|
92
|
+
* Called after every accepted heartbeat: the first one from a version on
|
|
93
|
+
* trial confirms it (unless the trial window has closed or ./current no
|
|
94
|
+
* longer runs it), records it as last-good, and prunes other versions.
|
|
32
95
|
*/
|
|
33
96
|
export declare function confirmTrial(version: string, env?: NodeJS.ProcessEnv, now?: Date): boolean;
|
|
34
97
|
export declare function pruneVersions(dir: string, keep: ReadonlyArray<string | null>): void;
|
|
35
98
|
export declare class SelfUpgradeError extends Error {
|
|
36
99
|
readonly code: string;
|
|
37
|
-
readonly status: 400 | 409 | 422 | 500;
|
|
38
|
-
constructor(code: string, message: string, status?: 400 | 409 | 422 | 500);
|
|
100
|
+
readonly status: 400 | 409 | 422 | 500 | 504;
|
|
101
|
+
constructor(code: string, message: string, status?: 400 | 409 | 422 | 500 | 504);
|
|
102
|
+
}
|
|
103
|
+
export interface RunResult {
|
|
104
|
+
code: number;
|
|
105
|
+
stdout: string;
|
|
106
|
+
stderr: string;
|
|
107
|
+
timedOut?: boolean;
|
|
39
108
|
}
|
|
40
109
|
export interface Runner {
|
|
41
110
|
(command: string, args: readonly string[], options: {
|
|
42
111
|
cwd: string;
|
|
43
112
|
env: NodeJS.ProcessEnv;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
stdout: string;
|
|
47
|
-
stderr: string;
|
|
48
|
-
}>;
|
|
113
|
+
timeoutMs?: number;
|
|
114
|
+
}): Promise<RunResult>;
|
|
49
115
|
}
|
|
116
|
+
/** Runs a command in its own process group; at timeoutMs the whole group gets SIGTERM, then SIGKILL. */
|
|
50
117
|
export declare const runProcess: Runner;
|
|
51
118
|
/** npm pack names files `<scope>-<name>-<version>.tgz`. */
|
|
52
119
|
export declare function tarballName(version: string): string;
|
|
53
120
|
export declare function sha512Integrity(file: string): string;
|
|
54
121
|
/** Other @hivemagnus tarballs in the source directory, used instead of the registry (offline nodes). */
|
|
55
122
|
export declare function sourceOverrides(sourceDir: string, agentTarball: string): Record<string, string>;
|
|
123
|
+
/** Why `engines.node` rules out `nodeVersion`, or null. Range forms satisfiesRange cannot judge are not refused. */
|
|
124
|
+
export declare function engineProblem(range: string | undefined, nodeVersion: string): string | null;
|
|
56
125
|
export interface StageOptions {
|
|
57
126
|
request: AgentUpgradeRequest;
|
|
58
127
|
version: string;
|
|
59
128
|
env?: NodeJS.ProcessEnv;
|
|
60
129
|
run?: Runner;
|
|
61
130
|
now?: Date;
|
|
131
|
+
/** Monotonic-enough milliseconds for the step budget (tests). */
|
|
132
|
+
clock?: () => number;
|
|
133
|
+
nodeVersion?: string;
|
|
62
134
|
}
|
|
63
135
|
export interface StageResult {
|
|
64
136
|
from: string;
|
|
65
137
|
to: string;
|
|
66
138
|
integrity: string;
|
|
67
139
|
requestId: string;
|
|
140
|
+
/** The same request was already handled: nothing was changed, `upgrade` is the current state. */
|
|
141
|
+
repeated?: boolean;
|
|
142
|
+
upgrade?: AgentUpgradeReport;
|
|
68
143
|
}
|
|
69
144
|
/** Download, verify, install and switch. The caller exits afterwards so the launcher starts the new version. */
|
|
70
145
|
export declare function stageUpgrade(options: StageOptions): Promise<StageResult>;
|