@hivemagnus/magnus-contracts 1.1.7 → 1.1.10
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/ardent.d.ts +19 -0
- package/dist/ardent.js +6 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -0
- package/dist/release.d.ts +142 -0
- package/dist/release.js +65 -0
- package/dist/upgrade-remedies.d.ts +450 -0
- package/dist/upgrade-remedies.js +95 -0
- package/package.json +5 -1
package/dist/ardent.d.ts
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
export declare const ArdentContextAdoptionInputSchema: z.ZodObject<{
|
|
3
|
+
release_id: z.ZodString;
|
|
4
|
+
expected_bundle_sha256: z.ZodString;
|
|
5
|
+
expected_base_sha256: z.ZodString;
|
|
6
|
+
expected_policy_sha256: z.ZodString;
|
|
7
|
+
expected_config_sha256: z.ZodString;
|
|
8
|
+
}, z.core.$strict>;
|
|
9
|
+
export declare const ArdentContextReleaseSchema: z.ZodObject<{
|
|
10
|
+
schema_version: z.ZodLiteral<1>;
|
|
11
|
+
engine_version: z.ZodLiteral<"0.1.0">;
|
|
12
|
+
bundle_sha256: z.ZodString;
|
|
13
|
+
base_sha256: z.ZodString;
|
|
14
|
+
policy_sha256: z.ZodString;
|
|
15
|
+
config_sha256: z.ZodString;
|
|
16
|
+
single_profile: z.ZodBoolean;
|
|
17
|
+
service_unit_matches: z.ZodBoolean;
|
|
18
|
+
}, z.core.$strict>;
|
|
19
|
+
export type ArdentContextAdoptionInput = z.infer<typeof ArdentContextAdoptionInputSchema>;
|
|
20
|
+
export type ArdentContextRelease = z.infer<typeof ArdentContextReleaseSchema>;
|
|
2
21
|
export declare const ArdentProfileSummarySchema: z.ZodObject<{
|
|
3
22
|
id: z.ZodString;
|
|
4
23
|
name: z.ZodString;
|
package/dist/ardent.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
export const ArdentContextAdoptionInputSchema = z.object({ release_id: z.string().guid(),
|
|
3
|
+
expected_bundle_sha256: z.string().regex(/^[a-f0-9]{64}$/), expected_base_sha256: z.string().regex(/^[a-f0-9]{64}$/),
|
|
4
|
+
expected_policy_sha256: z.string().regex(/^[a-f0-9]{64}$/), expected_config_sha256: z.string().regex(/^[a-f0-9]{64}$/) }).strict();
|
|
5
|
+
export const ArdentContextReleaseSchema = z.object({ schema_version: z.literal(1), engine_version: z.literal('0.1.0'),
|
|
6
|
+
bundle_sha256: z.string().regex(/^[a-f0-9]{64}$/), base_sha256: z.string().regex(/^[a-f0-9]{64}$/),
|
|
7
|
+
policy_sha256: z.string().regex(/^[a-f0-9]{64}$/), config_sha256: z.string().regex(/^[a-f0-9]{64}$/), single_profile: z.boolean(), service_unit_matches: z.boolean() }).strict();
|
|
2
8
|
export const ArdentProfileSummarySchema = z.object({
|
|
3
9
|
id: z.string(), name: z.string(), backend: z.enum(['llamacpp', 'codex']).nullable(),
|
|
4
10
|
apiPort: z.number().int().nullable(), running: z.boolean(), a2aEnabled: z.boolean(),
|
package/dist/index.d.ts
CHANGED
|
@@ -154,8 +154,8 @@ export declare const securityFusionSourceSchema: z.ZodEnum<{
|
|
|
154
154
|
security_console: "security_console";
|
|
155
155
|
}>;
|
|
156
156
|
export declare const securityFusionSeveritySchema: z.ZodEnum<{
|
|
157
|
-
info: "info";
|
|
158
157
|
low: "low";
|
|
158
|
+
info: "info";
|
|
159
159
|
medium: "medium";
|
|
160
160
|
high: "high";
|
|
161
161
|
critical: "critical";
|
|
@@ -210,8 +210,8 @@ export declare const securityFusionEventSchema: z.ZodObject<{
|
|
|
210
210
|
kind: z.ZodString;
|
|
211
211
|
observedAt: z.ZodString;
|
|
212
212
|
severity: z.ZodEnum<{
|
|
213
|
-
info: "info";
|
|
214
213
|
low: "low";
|
|
214
|
+
info: "info";
|
|
215
215
|
medium: "medium";
|
|
216
216
|
high: "high";
|
|
217
217
|
critical: "critical";
|
|
@@ -261,8 +261,8 @@ export declare const securityFusionEventBatchSchema: z.ZodObject<{
|
|
|
261
261
|
kind: z.ZodString;
|
|
262
262
|
observedAt: z.ZodString;
|
|
263
263
|
severity: z.ZodEnum<{
|
|
264
|
-
info: "info";
|
|
265
264
|
low: "low";
|
|
265
|
+
info: "info";
|
|
266
266
|
medium: "medium";
|
|
267
267
|
high: "high";
|
|
268
268
|
critical: "critical";
|
|
@@ -408,3 +408,4 @@ export declare const securityInventoryExportSchema: z.ZodObject<{
|
|
|
408
408
|
export type SecurityInventoryTarget = z.infer<typeof securityInventoryTargetSchema>;
|
|
409
409
|
export type SecurityInventoryExport = z.infer<typeof securityInventoryExportSchema>;
|
|
410
410
|
export * from './release.js';
|
|
411
|
+
export * from './upgrade-remedies.js';
|
package/dist/index.js
CHANGED
package/dist/release.d.ts
CHANGED
|
@@ -179,6 +179,42 @@ export declare const agentManagedEnrollmentSchema: z.ZodObject<{
|
|
|
179
179
|
nodeId: z.ZodString;
|
|
180
180
|
nodeFqdn: z.ZodString;
|
|
181
181
|
}, z.core.$strip>;
|
|
182
|
+
/** Highest fence the agent's ledger accepted, per resource key. Decimal strings: a fence can exceed 2^53. */
|
|
183
|
+
export declare const fenceHighWaterSchema: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
184
|
+
export declare const agentLedgerHealthSchema: z.ZodObject<{
|
|
185
|
+
readable: z.ZodBoolean;
|
|
186
|
+
activeLock: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
187
|
+
commandId: z.ZodString;
|
|
188
|
+
receipt: z.ZodString;
|
|
189
|
+
}, z.core.$strip>>>;
|
|
190
|
+
nonTerminalReceipts: z.ZodOptional<z.ZodNumber>;
|
|
191
|
+
unresolvedReceipts: z.ZodOptional<z.ZodNumber>;
|
|
192
|
+
truncated: z.ZodOptional<z.ZodBoolean>;
|
|
193
|
+
error: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
194
|
+
}, z.core.$strip>;
|
|
195
|
+
export declare const agentDiskHealthSchema: z.ZodObject<{
|
|
196
|
+
freeBytes: z.ZodNumber;
|
|
197
|
+
requiredBytes: z.ZodNumber;
|
|
198
|
+
low: z.ZodBoolean;
|
|
199
|
+
}, z.core.$strip>;
|
|
200
|
+
export declare const agentNpmHealthSchema: z.ZodObject<{
|
|
201
|
+
present: z.ZodBoolean;
|
|
202
|
+
version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
203
|
+
problem: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
204
|
+
}, z.core.$strip>;
|
|
205
|
+
export declare const agentMarkerStateSchema: z.ZodEnum<{
|
|
206
|
+
unknown: "unknown";
|
|
207
|
+
present: "present";
|
|
208
|
+
absent: "absent";
|
|
209
|
+
}>;
|
|
210
|
+
/** Known reasons plus any future one, which the server treats as a problem and not as an expected state. */
|
|
211
|
+
export declare const agentSelfUpgradeReasonCode: z.ZodUnion<readonly [z.ZodEnum<{
|
|
212
|
+
SWITCHED_OFF: "SWITCHED_OFF";
|
|
213
|
+
CONTAINER: "CONTAINER";
|
|
214
|
+
LEGACY_LAYOUT: "LEGACY_LAYOUT";
|
|
215
|
+
NOT_WRITABLE: "NOT_WRITABLE";
|
|
216
|
+
NPM: "NPM";
|
|
217
|
+
}>, z.ZodString]>;
|
|
182
218
|
export declare const agentSelfUpgradeStatusSchema: z.ZodObject<{
|
|
183
219
|
protocol: z.ZodNumber;
|
|
184
220
|
launcherProtocol: z.ZodNullable<z.ZodNumber>;
|
|
@@ -189,6 +225,13 @@ export declare const agentSelfUpgradeStatusSchema: z.ZodObject<{
|
|
|
189
225
|
}>;
|
|
190
226
|
enabled: z.ZodBoolean;
|
|
191
227
|
reason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
228
|
+
reasonCode: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodEnum<{
|
|
229
|
+
SWITCHED_OFF: "SWITCHED_OFF";
|
|
230
|
+
CONTAINER: "CONTAINER";
|
|
231
|
+
LEGACY_LAYOUT: "LEGACY_LAYOUT";
|
|
232
|
+
NOT_WRITABLE: "NOT_WRITABLE";
|
|
233
|
+
NPM: "NPM";
|
|
234
|
+
}>, z.ZodString]>>>>;
|
|
192
235
|
upgrade: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
193
236
|
state: z.ZodUnion<readonly [z.ZodEnum<{
|
|
194
237
|
failed: "failed";
|
|
@@ -210,6 +253,101 @@ export declare const agentSelfUpgradeStatusSchema: z.ZodObject<{
|
|
|
210
253
|
nodeId: z.ZodString;
|
|
211
254
|
nodeFqdn: z.ZodString;
|
|
212
255
|
}, z.core.$strip>>>;
|
|
256
|
+
managedEnrollmentState: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
|
|
257
|
+
unknown: "unknown";
|
|
258
|
+
present: "present";
|
|
259
|
+
absent: "absent";
|
|
260
|
+
}>>>;
|
|
261
|
+
fenceHighWater: z.ZodCatch<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>>;
|
|
262
|
+
ledger: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
263
|
+
readable: z.ZodBoolean;
|
|
264
|
+
activeLock: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
265
|
+
commandId: z.ZodString;
|
|
266
|
+
receipt: z.ZodString;
|
|
267
|
+
}, z.core.$strip>>>;
|
|
268
|
+
nonTerminalReceipts: z.ZodOptional<z.ZodNumber>;
|
|
269
|
+
unresolvedReceipts: z.ZodOptional<z.ZodNumber>;
|
|
270
|
+
truncated: z.ZodOptional<z.ZodBoolean>;
|
|
271
|
+
error: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
272
|
+
}, z.core.$strip>>>;
|
|
273
|
+
disk: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
274
|
+
freeBytes: z.ZodNumber;
|
|
275
|
+
requiredBytes: z.ZodNumber;
|
|
276
|
+
low: z.ZodBoolean;
|
|
277
|
+
}, z.core.$strip>>>;
|
|
278
|
+
npm: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
279
|
+
present: z.ZodBoolean;
|
|
280
|
+
version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
281
|
+
problem: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
282
|
+
}, z.core.$strip>>>;
|
|
283
|
+
}, z.core.$strip>;
|
|
284
|
+
/** The answer of an agent's read-only GET /api/v1/agent-upgrade/preflight. Each field drops alone when malformed, and a newer agent's extras are ignored. */
|
|
285
|
+
export declare const agentUpgradePreflightSchema: z.ZodObject<{
|
|
286
|
+
preflightProtocol: z.ZodNumber;
|
|
287
|
+
version: z.ZodString;
|
|
288
|
+
target: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
289
|
+
layout: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
|
|
290
|
+
versioned: "versioned";
|
|
291
|
+
legacy: "legacy";
|
|
292
|
+
container: "container";
|
|
293
|
+
}>>>;
|
|
294
|
+
launcherProtocol: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodNumber>>>;
|
|
295
|
+
enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
296
|
+
reason: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
297
|
+
reasonCode: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodEnum<{
|
|
298
|
+
SWITCHED_OFF: "SWITCHED_OFF";
|
|
299
|
+
CONTAINER: "CONTAINER";
|
|
300
|
+
LEGACY_LAYOUT: "LEGACY_LAYOUT";
|
|
301
|
+
NOT_WRITABLE: "NOT_WRITABLE";
|
|
302
|
+
NPM: "NPM";
|
|
303
|
+
}>, z.ZodString]>>>>;
|
|
304
|
+
writable: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
305
|
+
ok: z.ZodBoolean;
|
|
306
|
+
blocked: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
307
|
+
}, z.core.$strip>>>;
|
|
308
|
+
disk: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
309
|
+
freeBytes: z.ZodNumber;
|
|
310
|
+
requiredBytes: z.ZodNumber;
|
|
311
|
+
low: z.ZodBoolean;
|
|
312
|
+
}, z.core.$strip>>>;
|
|
313
|
+
npm: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
314
|
+
present: z.ZodBoolean;
|
|
315
|
+
version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
316
|
+
problem: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
317
|
+
}, z.core.$strip>>>;
|
|
318
|
+
fromVersionComplete: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
319
|
+
ledger: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
320
|
+
readable: z.ZodBoolean;
|
|
321
|
+
activeLock: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
322
|
+
commandId: z.ZodString;
|
|
323
|
+
receipt: z.ZodString;
|
|
324
|
+
}, z.core.$strip>>>;
|
|
325
|
+
nonTerminalReceipts: z.ZodOptional<z.ZodNumber>;
|
|
326
|
+
unresolvedReceipts: z.ZodOptional<z.ZodNumber>;
|
|
327
|
+
truncated: z.ZodOptional<z.ZodBoolean>;
|
|
328
|
+
error: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
329
|
+
}, z.core.$strip>>>;
|
|
330
|
+
fenceHighWater: z.ZodCatch<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>>;
|
|
331
|
+
managedEnrollment: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
332
|
+
nodeId: z.ZodString;
|
|
333
|
+
nodeFqdn: z.ZodString;
|
|
334
|
+
}, z.core.$strip>>>>;
|
|
335
|
+
managedEnrollmentState: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
|
|
336
|
+
unknown: "unknown";
|
|
337
|
+
present: "present";
|
|
338
|
+
absent: "absent";
|
|
339
|
+
}>>>;
|
|
340
|
+
fenceProof: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
341
|
+
nonce: z.ZodString;
|
|
342
|
+
nodeFqdn: z.ZodString;
|
|
343
|
+
signature: z.ZodString;
|
|
344
|
+
}, z.core.$strip>>>;
|
|
345
|
+
engines: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
346
|
+
node: z.ZodString;
|
|
347
|
+
range: z.ZodNullable<z.ZodString>;
|
|
348
|
+
compatible: z.ZodNullable<z.ZodBoolean>;
|
|
349
|
+
problem: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
350
|
+
}, z.core.$strip>>>;
|
|
213
351
|
}, z.core.$strip>;
|
|
214
352
|
export declare const agentUpgradeRequestSchema: z.ZodObject<{
|
|
215
353
|
version: z.ZodString;
|
|
@@ -222,6 +360,10 @@ export type AgentUpgradeReport = z.infer<typeof agentUpgradeReportSchema>;
|
|
|
222
360
|
export type AgentSelfUpgradeStatus = z.infer<typeof agentSelfUpgradeStatusSchema>;
|
|
223
361
|
export type AgentManagedEnrollment = z.infer<typeof agentManagedEnrollmentSchema>;
|
|
224
362
|
export type AgentUpgradeRequest = z.infer<typeof agentUpgradeRequestSchema>;
|
|
363
|
+
export type AgentLedgerHealth = z.infer<typeof agentLedgerHealthSchema>;
|
|
364
|
+
export type AgentDiskHealth = z.infer<typeof agentDiskHealthSchema>;
|
|
365
|
+
export type AgentNpmHealth = z.infer<typeof agentNpmHealthSchema>;
|
|
366
|
+
export type AgentUpgradePreflight = z.infer<typeof agentUpgradePreflightSchema>;
|
|
225
367
|
/** Name for the schema kept aside during a restore. */
|
|
226
368
|
export declare function restoreAsideSchema(now: Date): string;
|
|
227
369
|
/** Moves `public` aside and recreates it with the same owner and privileges. */
|
package/dist/release.js
CHANGED
|
@@ -244,16 +244,81 @@ export const agentManagedEnrollmentSchema = z.object({
|
|
|
244
244
|
nodeId: z.string().min(1).max(128),
|
|
245
245
|
nodeFqdn: z.string().min(1).max(253),
|
|
246
246
|
});
|
|
247
|
+
/** Highest fence the agent's ledger accepted, per resource key. Decimal strings: a fence can exceed 2^53. */
|
|
248
|
+
export const fenceHighWaterSchema = z.record(z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9:._-]{0,199}$/), z.string().regex(/^[0-9]{1,20}$/))
|
|
249
|
+
.refine(value => Object.keys(value).length <= 64, 'too many fence keys');
|
|
250
|
+
export const agentLedgerHealthSchema = z.object({
|
|
251
|
+
readable: z.boolean(),
|
|
252
|
+
/** The command that owns active.lock, and the state of its receipt (`none` when it has no receipt). */
|
|
253
|
+
activeLock: z.object({ commandId: z.string().max(128), receipt: z.string().max(32) }).nullable().optional(),
|
|
254
|
+
/** Receipts that are neither completed nor reconciled, and those of them no process is running. */
|
|
255
|
+
nonTerminalReceipts: z.number().int().nonnegative().optional(),
|
|
256
|
+
unresolvedReceipts: z.number().int().nonnegative().optional(),
|
|
257
|
+
/** More receipts than one scan reads: the counts are a lower bound. */
|
|
258
|
+
truncated: z.boolean().optional(),
|
|
259
|
+
error: z.string().max(300).nullable().optional(),
|
|
260
|
+
});
|
|
261
|
+
export const agentDiskHealthSchema = z.object({
|
|
262
|
+
freeBytes: z.number().nonnegative(),
|
|
263
|
+
requiredBytes: z.number().nonnegative(),
|
|
264
|
+
low: z.boolean(),
|
|
265
|
+
});
|
|
266
|
+
export const agentNpmHealthSchema = z.object({
|
|
267
|
+
present: z.boolean(),
|
|
268
|
+
version: z.string().max(64).nullable().optional(),
|
|
269
|
+
problem: z.string().max(500).nullable().optional(),
|
|
270
|
+
});
|
|
271
|
+
export const agentMarkerStateSchema = z.enum(['absent', 'present', 'unknown']);
|
|
272
|
+
/** Known reasons plus any future one, which the server treats as a problem and not as an expected state. */
|
|
273
|
+
export const agentSelfUpgradeReasonCode = z.union([z.enum(['SWITCHED_OFF', 'CONTAINER', 'LEGACY_LAYOUT', 'NOT_WRITABLE', 'NPM']), z.string().regex(/^[A-Z][A-Z0-9_]{0,63}$/)]);
|
|
247
274
|
export const agentSelfUpgradeStatusSchema = z.object({
|
|
248
275
|
protocol: z.number().int().nonnegative(),
|
|
249
276
|
launcherProtocol: z.number().int().nonnegative().nullable(),
|
|
250
277
|
layout: z.enum(['versioned', 'legacy', 'container']),
|
|
251
278
|
enabled: z.boolean(),
|
|
252
279
|
reason: z.string().max(1000).nullable().optional(),
|
|
280
|
+
/** Why self-upgrade is off, for the server; `reason` is for people. Older agents leave it out. */
|
|
281
|
+
reasonCode: agentSelfUpgradeReasonCode.nullable().optional().catch(undefined),
|
|
253
282
|
upgrade: agentUpgradeReportSchema.nullable().optional(),
|
|
254
283
|
/** Seconds a new version has on trial before the launcher switches back (MAGNUS_AGENT_UPGRADE_CONFIRM_SECONDS). */
|
|
255
284
|
confirmSeconds: z.number().int().positive().max(86_400).nullable().optional(),
|
|
256
285
|
managedEnrollment: agentManagedEnrollmentSchema.nullable().optional(),
|
|
286
|
+
/** `unknown`: the marker could not be read, which says nothing about a mismatch either way. */
|
|
287
|
+
managedEnrollmentState: agentMarkerStateSchema.optional().catch(undefined),
|
|
288
|
+
// Drift evidence from 1.1.8 agents. Each drops alone when malformed, so a bad value never costs the whole report.
|
|
289
|
+
fenceHighWater: fenceHighWaterSchema.optional().catch(undefined),
|
|
290
|
+
ledger: agentLedgerHealthSchema.optional().catch(undefined),
|
|
291
|
+
disk: agentDiskHealthSchema.optional().catch(undefined),
|
|
292
|
+
npm: agentNpmHealthSchema.optional().catch(undefined),
|
|
293
|
+
});
|
|
294
|
+
/** The answer of an agent's read-only GET /api/v1/agent-upgrade/preflight. Each field drops alone when malformed, and a newer agent's extras are ignored. */
|
|
295
|
+
export const agentUpgradePreflightSchema = z.object({
|
|
296
|
+
/** Read-only report format. */
|
|
297
|
+
preflightProtocol: z.number().int().nonnegative(),
|
|
298
|
+
version: z.string().max(64),
|
|
299
|
+
target: z.string().max(64).nullable().optional().catch(null),
|
|
300
|
+
layout: z.enum(['versioned', 'legacy', 'container']).optional().catch(undefined),
|
|
301
|
+
launcherProtocol: z.number().int().nonnegative().nullable().optional().catch(null),
|
|
302
|
+
enabled: z.boolean().optional().catch(undefined),
|
|
303
|
+
reason: z.string().max(1000).nullable().optional().catch(null),
|
|
304
|
+
reasonCode: agentSelfUpgradeReasonCode.nullable().optional().catch(undefined),
|
|
305
|
+
writable: z.object({ ok: z.boolean(), blocked: z.array(z.string().max(255)).max(16).optional() }).optional().catch(undefined),
|
|
306
|
+
disk: agentDiskHealthSchema.optional().catch(undefined),
|
|
307
|
+
npm: agentNpmHealthSchema.optional().catch(undefined),
|
|
308
|
+
/** versions/<from> is complete and ./current runs it, so a failed upgrade can switch back. */
|
|
309
|
+
fromVersionComplete: z.boolean().optional().catch(undefined),
|
|
310
|
+
ledger: agentLedgerHealthSchema.optional().catch(undefined),
|
|
311
|
+
fenceHighWater: fenceHighWaterSchema.optional().catch(undefined),
|
|
312
|
+
managedEnrollment: agentManagedEnrollmentSchema.nullable().optional().catch(null),
|
|
313
|
+
managedEnrollmentState: agentMarkerStateSchema.optional().catch(undefined),
|
|
314
|
+
/** `fenceHighWater`, signed by the agent for this nonce. Only a verified proof can move a server generation. */
|
|
315
|
+
fenceProof: z.object({ nonce: z.string().max(64), nodeFqdn: z.string().max(253), signature: z.string().regex(/^[a-f0-9]{64}$/) }).optional().catch(undefined),
|
|
316
|
+
engines: z.object({
|
|
317
|
+
node: z.string().max(64),
|
|
318
|
+
range: z.string().max(200).nullable(),
|
|
319
|
+
compatible: z.boolean().nullable(),
|
|
320
|
+
problem: z.string().max(500).nullable().optional(),
|
|
321
|
+
}).optional().catch(undefined),
|
|
257
322
|
});
|
|
258
323
|
export const agentUpgradeRequestSchema = z.object({
|
|
259
324
|
version: versionString,
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
/** `release`: evidence about the release itself. `node`: this node's own state. */
|
|
2
|
+
export type UpgradeFailureClass = 'release' | 'node';
|
|
3
|
+
export interface UpgradeRemedy {
|
|
4
|
+
class: UpgradeFailureClass;
|
|
5
|
+
/** Same artifact everywhere: a single occurrence stops the whole run. */
|
|
6
|
+
stopsRun?: boolean;
|
|
7
|
+
/** Clears by itself: the run tries the node again at the end of the wave. Never set for ambiguous outcomes. */
|
|
8
|
+
transient?: boolean;
|
|
9
|
+
/** The request may have reached the agent: watched by request id, never retried automatically. */
|
|
10
|
+
ambiguous?: boolean;
|
|
11
|
+
/** An expected state that does not make a run `partial`, such as a node already on the target. */
|
|
12
|
+
informational?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Nodes that all refuse with this, with nothing confirmed, point at what they have in common: the release.
|
|
15
|
+
* Off for any code that names a node-level cause of its own, which says nothing about the release however many nodes share it.
|
|
16
|
+
*/
|
|
17
|
+
suspectable?: boolean;
|
|
18
|
+
/** Not in the catalogue, for example from a newer agent: treated as node-level, and shown as unrecognised. */
|
|
19
|
+
unknown?: boolean;
|
|
20
|
+
cause: string;
|
|
21
|
+
remedy: string;
|
|
22
|
+
}
|
|
23
|
+
export declare const UPGRADE_REMEDIES: {
|
|
24
|
+
readonly SELF_UPGRADE_INTEGRITY: {
|
|
25
|
+
readonly class: "release";
|
|
26
|
+
readonly stopsRun: true;
|
|
27
|
+
readonly cause: "The downloaded tarball does not match the pinned npm integrity.";
|
|
28
|
+
readonly remedy: "Do not retry. Check the registry or MAGNUS_AGENT_UPGRADE_SOURCE_DIR, and the integrity passed to the run.";
|
|
29
|
+
};
|
|
30
|
+
readonly SELF_UPGRADE_SOURCE: {
|
|
31
|
+
readonly class: "release";
|
|
32
|
+
readonly stopsRun: true;
|
|
33
|
+
readonly cause: "The tarball is missing from MAGNUS_AGENT_UPGRADE_SOURCE_DIR.";
|
|
34
|
+
readonly remedy: "Copy the exact tarball to that directory on the nodes, then retry.";
|
|
35
|
+
};
|
|
36
|
+
readonly SELF_UPGRADE_FAILED: {
|
|
37
|
+
readonly class: "release";
|
|
38
|
+
readonly cause: "The package itself failed on the node: a bad tarball, an install error, missing provenance or a smoke test that failed or hung. An agent older than 1.1.8 also reports environment causes this way.";
|
|
39
|
+
readonly remedy: "Read the detail and the agent log. If several nodes fail the same way, the release is at fault.";
|
|
40
|
+
};
|
|
41
|
+
readonly SELF_UPGRADE_REVERTED: {
|
|
42
|
+
readonly class: "release";
|
|
43
|
+
readonly cause: "The new version crashed or stayed silent during its trial, and the launcher switched back.";
|
|
44
|
+
readonly remedy: "Read the reason on the node and in the agent log. To try again, retry with allowRetryReverted (--retry-reverted).";
|
|
45
|
+
};
|
|
46
|
+
readonly CONFIRM_TIMEOUT: {
|
|
47
|
+
readonly class: "release";
|
|
48
|
+
readonly cause: "The agent accepted the request, but the new version was not confirmed within its trial window.";
|
|
49
|
+
readonly remedy: "Read the agent log on the node. A later report for this request is still recorded in the run.";
|
|
50
|
+
};
|
|
51
|
+
readonly AGENT_SILENT: {
|
|
52
|
+
readonly class: "release";
|
|
53
|
+
readonly cause: "The agent accepted the request and then stopped sending heartbeats.";
|
|
54
|
+
readonly remedy: "Check the service on the node. The launcher switches back by itself if it is running.";
|
|
55
|
+
};
|
|
56
|
+
readonly MUTATION_OUTCOME_UNKNOWN: {
|
|
57
|
+
readonly class: "node";
|
|
58
|
+
readonly ambiguous: true;
|
|
59
|
+
readonly cause: "The request was sent and no usable answer came back.";
|
|
60
|
+
readonly remedy: "The run keeps watching the node by request id. If it never reports, check the node, then reconcile the lease of the request (agent-upgrade-<requestId>).";
|
|
61
|
+
};
|
|
62
|
+
readonly MANAGED_MUTATION_UNCERTAIN: {
|
|
63
|
+
readonly class: "node";
|
|
64
|
+
readonly ambiguous: true;
|
|
65
|
+
readonly cause: "The agent cannot say whether a managed request ran. Any version answers this when a handler failed after it may have started; an agent older than 1.1.8 also answers it for every refusal, whatever the real cause. The command lease stays unknown and blocks later managed requests on this node.";
|
|
66
|
+
readonly remedy: "Read the agent log for the real cause. If the agent holds a receipt for the command (agent-upgrade-<requestId>), settle it with signed evidence: POST /api/v1/commands/<id>/reconcile-resource. If it holds none, an agent older than 1.1.8 offers no way out: upgrade that agent by hand on the node, after which the readiness monitor or `magnus-server agents doctor --heal` can settle the lease. If the cause was a stale fence, first run `magnus-server agents fence NODE VALUE`, or the next request fails the same way.";
|
|
67
|
+
};
|
|
68
|
+
readonly OUTCOME_UNKNOWN: {
|
|
69
|
+
readonly class: "node";
|
|
70
|
+
readonly ambiguous: true;
|
|
71
|
+
readonly cause: "The agent ledger holds an unresolved receipt for this command.";
|
|
72
|
+
readonly remedy: "Inspect the receipt and reconcile it with signed evidence (docs/COMMAND_RESOURCE_LEASES.md).";
|
|
73
|
+
};
|
|
74
|
+
readonly RUN_INTERRUPTED: {
|
|
75
|
+
readonly class: "node";
|
|
76
|
+
readonly ambiguous: true;
|
|
77
|
+
readonly cause: "The server stopped watching this node when the run was interrupted.";
|
|
78
|
+
readonly remedy: "A later report is recorded in the run. If the node does not report, retry it.";
|
|
79
|
+
};
|
|
80
|
+
readonly SELF_UPGRADE_ENV_NETWORK: {
|
|
81
|
+
readonly class: "node";
|
|
82
|
+
readonly transient: true;
|
|
83
|
+
readonly cause: "The node could not reach the registry while fetching the new version.";
|
|
84
|
+
readonly remedy: "Check the path from the node to the registry, or use MAGNUS_AGENT_UPGRADE_SOURCE_DIR, then retry it.";
|
|
85
|
+
};
|
|
86
|
+
readonly AGENT_UNREACHABLE: {
|
|
87
|
+
readonly class: "node";
|
|
88
|
+
readonly transient: true;
|
|
89
|
+
readonly cause: "The server could not open a connection to the agent.";
|
|
90
|
+
readonly remedy: "Check that the agent is running and that its address and port in the inventory are correct.";
|
|
91
|
+
};
|
|
92
|
+
readonly SELF_UPGRADE_BUSY: {
|
|
93
|
+
readonly class: "node";
|
|
94
|
+
readonly transient: true;
|
|
95
|
+
readonly cause: "The agent is already staging an upgrade or another process holds its upgrade lock.";
|
|
96
|
+
readonly remedy: "Wait for it to finish, then retry the node.";
|
|
97
|
+
};
|
|
98
|
+
readonly RESOURCE_BUSY: {
|
|
99
|
+
readonly class: "node";
|
|
100
|
+
readonly transient: true;
|
|
101
|
+
readonly cause: "Another command owns the single execution slot on the node.";
|
|
102
|
+
readonly remedy: "Wait for the command to finish, then retry the node.";
|
|
103
|
+
};
|
|
104
|
+
readonly RESOURCE_LEASE_CONFLICT: {
|
|
105
|
+
readonly class: "node";
|
|
106
|
+
readonly transient: true;
|
|
107
|
+
readonly cause: "The server holds another unsettled command lease for the node.";
|
|
108
|
+
readonly remedy: "Wait for it to settle. If it stays `unknown`, run `magnus-server agents doctor --heal` or reconcile it.";
|
|
109
|
+
};
|
|
110
|
+
readonly ENROLLMENT_AUTHORITY_UNAVAILABLE: {
|
|
111
|
+
readonly class: "node";
|
|
112
|
+
readonly transient: true;
|
|
113
|
+
readonly cause: "The agent could not reach the server to confirm its enrollment.";
|
|
114
|
+
readonly remedy: "Check the path from the node to the server (CONTROL_API_URL, TLS trust).";
|
|
115
|
+
};
|
|
116
|
+
readonly STALE_FENCE: {
|
|
117
|
+
readonly class: "node";
|
|
118
|
+
readonly transient: true;
|
|
119
|
+
readonly cause: "The agent holds a resource fence at least as high as the one the server sent, and refused without running anything.";
|
|
120
|
+
readonly remedy: "The server repairs this by itself by moving its generation up, and tries the node again. Nothing to do unless the problem persists.";
|
|
121
|
+
};
|
|
122
|
+
readonly LEASE_EXPIRED: {
|
|
123
|
+
readonly class: "node";
|
|
124
|
+
readonly transient: true;
|
|
125
|
+
readonly cause: "The command lease expired before the agent could start it; nothing ran.";
|
|
126
|
+
readonly remedy: "Retry the node. If this repeats, compare the clocks of the node and the server.";
|
|
127
|
+
};
|
|
128
|
+
readonly NOT_DISPATCHED: {
|
|
129
|
+
readonly class: "node";
|
|
130
|
+
readonly transient: true;
|
|
131
|
+
readonly cause: "The server did not send the request.";
|
|
132
|
+
readonly remedy: "Read the detail, then retry the node.";
|
|
133
|
+
};
|
|
134
|
+
readonly CHECK_FAILED: {
|
|
135
|
+
readonly class: "node";
|
|
136
|
+
readonly transient: true;
|
|
137
|
+
readonly cause: "The server could not check this node right before asking it, so it did not ask.";
|
|
138
|
+
readonly remedy: "Read the detail, usually a database or Redis problem on the server, then retry the node.";
|
|
139
|
+
};
|
|
140
|
+
readonly AGENT_SILENT_AFTER_LOST_REPLY: {
|
|
141
|
+
readonly class: "node";
|
|
142
|
+
readonly ambiguous: true;
|
|
143
|
+
readonly cause: "The request may or may not have arrived, and the agent has stopped sending heartbeats.";
|
|
144
|
+
readonly remedy: "Check the service on the node. A later report for this request is still recorded in the run.";
|
|
145
|
+
};
|
|
146
|
+
readonly FENCE_UNREPORTED: {
|
|
147
|
+
readonly class: "node";
|
|
148
|
+
readonly cause: "This agent is older than 1.1.8, has handled managed requests before, and cannot say what fence it holds. If that fence is ahead of the server, its request fails as MANAGED_MUTATION_UNCERTAIN and strands a lease.";
|
|
149
|
+
readonly remedy: "Before the wave, read .magnus/command-receipts/fence-*.json on the node and run `magnus-server agents fence NODE VALUE`. Nothing to do if the fence is not ahead.";
|
|
150
|
+
};
|
|
151
|
+
readonly LEDGER_MISSING: {
|
|
152
|
+
readonly class: "node";
|
|
153
|
+
readonly cause: "The agent has no command ledger, so its \"no receipt\" proves nothing about a lease held for it. The ledger may have moved, or the install may have been rebuilt.";
|
|
154
|
+
readonly remedy: "Check AGENT_INSTALL_DIR and restore .magnus/command-receipts if it moved; the lease then settles by itself. If it is gone, Magnus has no way to settle the lease: establish by hand what the command did, then change the lease row in the database yourself.";
|
|
155
|
+
};
|
|
156
|
+
readonly LEDGER_NO_FENCE: {
|
|
157
|
+
readonly class: "node";
|
|
158
|
+
readonly cause: "The agent ledger holds neither a fence nor this node's enrollment marker, so nothing shows it is the ledger this command would have used.";
|
|
159
|
+
readonly remedy: "Check whether .magnus/command-receipts was replaced, and restore it if so; the lease then settles by itself. If it cannot be restored, Magnus has no way to settle the lease: establish by hand what the command did, then change the lease row in the database yourself.";
|
|
160
|
+
};
|
|
161
|
+
readonly LEDGER_EVIDENCE_INCONSISTENT: {
|
|
162
|
+
readonly class: "node";
|
|
163
|
+
readonly cause: "The agent ledger accepted this lease's fence, but holds no receipt, no claim and no record of a failed admission for it. Either a receipt was removed, or an admission failed on an agent build that kept no such record.";
|
|
164
|
+
readonly remedy: "Inspect .magnus/command-receipts on the node and the agent log around the lease's time. Magnus has no way to settle the lease: establish by hand what the command did, then change the lease row in the database yourself.";
|
|
165
|
+
};
|
|
166
|
+
readonly ENROLLMENT_MARKER_UNKNOWN: {
|
|
167
|
+
readonly class: "node";
|
|
168
|
+
readonly cause: "The agent could not read its managed-enrollment marker, so a mismatch cannot be ruled out.";
|
|
169
|
+
readonly remedy: "Check .magnus/command-receipts/managed-enrollment.json on the node: its mode, owner and content.";
|
|
170
|
+
};
|
|
171
|
+
readonly SELF_UPGRADE_ENV_TLS: {
|
|
172
|
+
readonly class: "node";
|
|
173
|
+
readonly cause: "npm would not trust the registry's certificate on this node: a wrong clock, an expired certificate or an intercepting proxy.";
|
|
174
|
+
readonly remedy: "Check the clock on the node and its CA trust for the registry, then retry it.";
|
|
175
|
+
};
|
|
176
|
+
readonly SELF_UPGRADE_ENV_AUTH: {
|
|
177
|
+
readonly class: "node";
|
|
178
|
+
readonly cause: "The registry refused this node's request (401 or 403).";
|
|
179
|
+
readonly remedy: "Check the npm credentials and registry settings on the node, or use MAGNUS_AGENT_UPGRADE_SOURCE_DIR, then retry it.";
|
|
180
|
+
};
|
|
181
|
+
readonly SELF_UPGRADE_ENV_RESOURCE: {
|
|
182
|
+
readonly class: "node";
|
|
183
|
+
readonly cause: "The node ran out of memory or file handles, npm was killed, or a file was busy. A killed npm is told apart by how the process ended; a lifecycle script killed inside npm still reads as a package fault.";
|
|
184
|
+
readonly remedy: "Check the memory on the node and its kernel log, then retry it.";
|
|
185
|
+
};
|
|
186
|
+
readonly RETRY_HELD_SUSPECTED: {
|
|
187
|
+
readonly class: "node";
|
|
188
|
+
readonly cause: "The run this one retries stopped because older agents answered ambiguously with nothing confirmed. Each further attempt on an older agent can strand another lease, so this retry leaves them alone.";
|
|
189
|
+
readonly remedy: "Read the agent log of the nodes that answered, and run `magnus-server agents fence NODE VALUE` where a fence is ahead. Then retry with acknowledgeSuspicion (--acknowledge-suspicion).";
|
|
190
|
+
};
|
|
191
|
+
readonly SELF_UPGRADE_ENV_DISK: {
|
|
192
|
+
readonly class: "node";
|
|
193
|
+
readonly cause: "The node ran out of disk space or quota, or hit an I/O error, while installing the new version.";
|
|
194
|
+
readonly remedy: "Free disk space on the node, then retry it.";
|
|
195
|
+
};
|
|
196
|
+
readonly SELF_UPGRADE_ENV_ACCESS: {
|
|
197
|
+
readonly class: "node";
|
|
198
|
+
readonly cause: "The node refused a write while installing the new version.";
|
|
199
|
+
readonly remedy: "Run `sudo magnus-agent install-service` again on the node, which repairs ownership, then retry it.";
|
|
200
|
+
};
|
|
201
|
+
readonly RESOURCE_ENROLLMENT_REQUIRED: {
|
|
202
|
+
readonly class: "node";
|
|
203
|
+
readonly cause: "Admission refused the request because the node's resource enrollment is not complete.";
|
|
204
|
+
readonly remedy: "Complete the node's resource enrollment (docs/COMMAND_RESOURCE_LEASES.md).";
|
|
205
|
+
};
|
|
206
|
+
readonly FENCE_OVERFLOW: {
|
|
207
|
+
readonly class: "node";
|
|
208
|
+
readonly cause: "The agent reports a resource fence above the highest value the server will adopt.";
|
|
209
|
+
readonly remedy: "Stop the agent, inspect .magnus/command-receipts/fence-*.json on the node, and repair that file by hand. The server does not change its generation.";
|
|
210
|
+
};
|
|
211
|
+
readonly FENCE_INVALID: {
|
|
212
|
+
readonly class: "node";
|
|
213
|
+
readonly cause: "The agent reports a resource fence that is not a decimal value for its own node.";
|
|
214
|
+
readonly remedy: "Inspect .magnus/command-receipts/fence-*.json on the node.";
|
|
215
|
+
};
|
|
216
|
+
readonly FENCE_AHEAD: {
|
|
217
|
+
readonly class: "node";
|
|
218
|
+
readonly cause: "The agent holds a resource fence above the server generation, so managed requests would be refused.";
|
|
219
|
+
readonly remedy: "Nothing to do for an upgrade run: it repairs this at its start. `magnus-server agents doctor --heal` repairs it now.";
|
|
220
|
+
};
|
|
221
|
+
readonly IDEMPOTENCY_CONFLICT: {
|
|
222
|
+
readonly class: "node";
|
|
223
|
+
readonly cause: "The agent already has a receipt for this command identifier with different input.";
|
|
224
|
+
readonly remedy: "Retry the node, which uses a new identifier, and report the conflict if it repeats.";
|
|
225
|
+
};
|
|
226
|
+
readonly LEDGER_UNAVAILABLE: {
|
|
227
|
+
readonly class: "node";
|
|
228
|
+
readonly cause: "The agent could not read or write its command ledger.";
|
|
229
|
+
readonly remedy: "Check ownership, mode 0700 and free space of .magnus/command-receipts on the node.";
|
|
230
|
+
};
|
|
231
|
+
readonly LEDGER_UNRESOLVED: {
|
|
232
|
+
readonly class: "node";
|
|
233
|
+
readonly cause: "The agent ledger holds a command with an unresolved outcome, which blocks every later managed request.";
|
|
234
|
+
readonly remedy: "Reconcile that command with signed evidence (docs/COMMAND_RESOURCE_LEASES.md).";
|
|
235
|
+
};
|
|
236
|
+
readonly LEASE_HELD: {
|
|
237
|
+
readonly class: "node";
|
|
238
|
+
readonly cause: "A command lease on the node is reserved, running or unknown.";
|
|
239
|
+
readonly remedy: "Wait for it to finish. On a 1.1.8 agent, a lease stuck `unknown` with no receipt is settled by the readiness monitor, a run, or `magnus-server agents doctor --heal`; one with a receipt needs signed reconciliation. On an older agent, see MANAGED_MUTATION_UNCERTAIN.";
|
|
240
|
+
};
|
|
241
|
+
readonly WORK_HELD: {
|
|
242
|
+
readonly class: "node";
|
|
243
|
+
readonly cause: "Participant work, such as a console terminal or SSH session, is still held on the node.";
|
|
244
|
+
readonly remedy: "Finish or reconcile that work, then retry the node.";
|
|
245
|
+
};
|
|
246
|
+
readonly UNMANAGED_MUTATION: {
|
|
247
|
+
readonly class: "node";
|
|
248
|
+
readonly suspectable: true;
|
|
249
|
+
readonly cause: "The agent is enrolled for managed requests and refused a request without a valid claim.";
|
|
250
|
+
readonly remedy: "Complete the node's resource enrollment on the server (docs/COMMAND_RESOURCE_LEASES.md), or upgrade the agent by hand.";
|
|
251
|
+
};
|
|
252
|
+
readonly RESOURCE_CLAIM_INVALID: {
|
|
253
|
+
readonly class: "node";
|
|
254
|
+
readonly cause: "The server would not confirm the agent's claim for this request.";
|
|
255
|
+
readonly remedy: "Check the enrollment and inventory host name of the node, then retry.";
|
|
256
|
+
};
|
|
257
|
+
readonly RESOURCE_TARGET_INVALID: {
|
|
258
|
+
readonly class: "node";
|
|
259
|
+
readonly cause: "Admission refused the node: its inventory address changed or its health is not fresh.";
|
|
260
|
+
readonly remedy: "Wait for a fresh heartbeat, or repair the inventory address.";
|
|
261
|
+
};
|
|
262
|
+
readonly ADMISSION_FENCE_CLOSED: {
|
|
263
|
+
readonly class: "node";
|
|
264
|
+
readonly cause: "A server release closed admission to this node.";
|
|
265
|
+
readonly remedy: "Release the admission fence (POST /api/v1/resource-leases/admission/<nodeId>/release).";
|
|
266
|
+
};
|
|
267
|
+
readonly ENROLLMENT_MISSING: {
|
|
268
|
+
readonly class: "node";
|
|
269
|
+
readonly cause: "Resource leases apply to this node but it is not enrolled.";
|
|
270
|
+
readonly remedy: "Enroll the node (docs/COMMAND_RESOURCE_LEASES.md).";
|
|
271
|
+
};
|
|
272
|
+
readonly ENROLLMENT_DRAINING: {
|
|
273
|
+
readonly class: "node";
|
|
274
|
+
readonly cause: "The node's resource enrollment is stuck in draining.";
|
|
275
|
+
readonly remedy: "Finish the drain: POST /api/v1/resource-leases/enrollment/<nodeId>/complete.";
|
|
276
|
+
};
|
|
277
|
+
readonly ENROLLMENT_HOST_MISMATCH: {
|
|
278
|
+
readonly class: "node";
|
|
279
|
+
readonly cause: "The enrollment names another host than the inventory.";
|
|
280
|
+
readonly remedy: "Correct the host name of the node in the inventory.";
|
|
281
|
+
};
|
|
282
|
+
readonly ENROLLMENT_INCOMPLETE: {
|
|
283
|
+
readonly class: "node";
|
|
284
|
+
readonly cause: "The node's resource enrollment is not complete.";
|
|
285
|
+
readonly remedy: "Enroll the node (docs/COMMAND_RESOURCE_LEASES.md).";
|
|
286
|
+
};
|
|
287
|
+
readonly ENROLLMENT_MARKER_MISMATCH: {
|
|
288
|
+
readonly class: "node";
|
|
289
|
+
readonly cause: "The agent holds a local managed-enrollment marker the server does not share.";
|
|
290
|
+
readonly remedy: "Complete the node's resource enrollment on the server (docs/COMMAND_RESOURCE_LEASES.md), or upgrade the agent by hand.";
|
|
291
|
+
};
|
|
292
|
+
readonly NODE_OFFLINE: {
|
|
293
|
+
readonly class: "node";
|
|
294
|
+
readonly transient: true;
|
|
295
|
+
readonly cause: "The node is offline or has no recent heartbeat.";
|
|
296
|
+
readonly remedy: "Bring the agent back, then retry the node.";
|
|
297
|
+
};
|
|
298
|
+
readonly NODE_NOT_FOUND: {
|
|
299
|
+
readonly class: "node";
|
|
300
|
+
readonly cause: "The node is no longer in the inventory.";
|
|
301
|
+
readonly remedy: "Register the node again if it should be managed.";
|
|
302
|
+
};
|
|
303
|
+
readonly VERSION_UNKNOWN: {
|
|
304
|
+
readonly class: "node";
|
|
305
|
+
readonly cause: "The agent has not reported a usable version.";
|
|
306
|
+
readonly remedy: "Wait for a heartbeat, or upgrade the agent by hand.";
|
|
307
|
+
};
|
|
308
|
+
readonly SELF_UPGRADE_TIMEOUT: {
|
|
309
|
+
readonly class: "node";
|
|
310
|
+
readonly cause: "An npm step of the upgrade did not finish in its time budget on the node. A new version that hangs when it is run reports SELF_UPGRADE_FAILED instead. A lifecycle script that hangs inside npm also ends up here: it cannot be told apart.";
|
|
311
|
+
readonly remedy: "Check the network and disk speed of the node, then retry it. If every node hangs the same way, suspect the release.";
|
|
312
|
+
};
|
|
313
|
+
readonly SELF_UPGRADE_UNAVAILABLE: {
|
|
314
|
+
readonly class: "node";
|
|
315
|
+
readonly cause: "The agent cannot upgrade itself now: its layout, launcher, directories or npm are not usable.";
|
|
316
|
+
readonly remedy: "Read the detail. Running `sudo magnus-agent install-service` once on the node usually repairs it.";
|
|
317
|
+
};
|
|
318
|
+
readonly SELF_UPGRADE_LAUNCHER: {
|
|
319
|
+
readonly class: "node";
|
|
320
|
+
readonly suspectable: true;
|
|
321
|
+
readonly cause: "The new version needs a newer launcher than the node has.";
|
|
322
|
+
readonly remedy: "Run `sudo magnus-agent install-service` once on the node.";
|
|
323
|
+
};
|
|
324
|
+
readonly SELF_UPGRADE_ENGINE: {
|
|
325
|
+
readonly class: "node";
|
|
326
|
+
readonly suspectable: true;
|
|
327
|
+
readonly cause: "The new version does not support the Node.js on this node.";
|
|
328
|
+
readonly remedy: "Upgrade Node.js on the node.";
|
|
329
|
+
};
|
|
330
|
+
readonly SELF_UPGRADE_PACKAGE: {
|
|
331
|
+
readonly class: "node";
|
|
332
|
+
readonly suspectable: true;
|
|
333
|
+
readonly cause: "The agent refused to install a package other than magnus-agent.";
|
|
334
|
+
readonly remedy: "Report this: the server sent an unexpected package name.";
|
|
335
|
+
};
|
|
336
|
+
readonly SELF_UPGRADE_REFUSED: {
|
|
337
|
+
readonly class: "node";
|
|
338
|
+
readonly suspectable: true;
|
|
339
|
+
readonly cause: "The agent refused the version change.";
|
|
340
|
+
readonly remedy: "Read the detail. Crossing a major version is done by hand.";
|
|
341
|
+
};
|
|
342
|
+
readonly PREFLIGHT_NOT_WRITABLE: {
|
|
343
|
+
readonly class: "node";
|
|
344
|
+
readonly cause: "The agent cannot write to its install directory.";
|
|
345
|
+
readonly remedy: "Run `sudo magnus-agent install-service` again on the node, which repairs ownership.";
|
|
346
|
+
};
|
|
347
|
+
readonly PREFLIGHT_DISK_LOW: {
|
|
348
|
+
readonly class: "node";
|
|
349
|
+
readonly cause: "The install directory has less free space than an upgrade needs.";
|
|
350
|
+
readonly remedy: "Free disk space on the node.";
|
|
351
|
+
};
|
|
352
|
+
readonly PREFLIGHT_NPM: {
|
|
353
|
+
readonly class: "node";
|
|
354
|
+
readonly cause: "npm is missing on the node or too old.";
|
|
355
|
+
readonly remedy: "Install npm beside the agent's Node.js, or set MAGNUS_AGENT_NPM.";
|
|
356
|
+
};
|
|
357
|
+
readonly PREFLIGHT_FROM_INCOMPLETE: {
|
|
358
|
+
readonly class: "node";
|
|
359
|
+
readonly cause: "The running version is not complete under versions/, so a failed upgrade could not switch back.";
|
|
360
|
+
readonly remedy: "Run `sudo magnus-agent install-service` again on the node.";
|
|
361
|
+
};
|
|
362
|
+
readonly PREFLIGHT_ENGINE: {
|
|
363
|
+
readonly class: "node";
|
|
364
|
+
readonly cause: "The target version does not support the Node.js on this node.";
|
|
365
|
+
readonly remedy: "Upgrade Node.js on the node.";
|
|
366
|
+
};
|
|
367
|
+
readonly NOT_REACHED: {
|
|
368
|
+
readonly class: "node";
|
|
369
|
+
readonly cause: "The run ended before it reached this node.";
|
|
370
|
+
readonly remedy: "Retry the run, once what ended it is dealt with. This node was not asked and did not change.";
|
|
371
|
+
};
|
|
372
|
+
readonly RUN_STOPPED: {
|
|
373
|
+
readonly class: "node";
|
|
374
|
+
readonly cause: "Someone stopped the run before it reached this node.";
|
|
375
|
+
readonly remedy: "Retry the run when you want this node upgraded. This node was not asked and did not change.";
|
|
376
|
+
};
|
|
377
|
+
readonly NOT_ELIGIBLE: {
|
|
378
|
+
readonly class: "node";
|
|
379
|
+
readonly cause: "This node became ineligible at its turn, for a reason without a code of its own.";
|
|
380
|
+
readonly remedy: "Read the detail, then retry the node.";
|
|
381
|
+
};
|
|
382
|
+
readonly ALREADY_CURRENT: {
|
|
383
|
+
readonly class: "node";
|
|
384
|
+
readonly informational: true;
|
|
385
|
+
readonly cause: "The node already runs the target version.";
|
|
386
|
+
readonly remedy: "Nothing to do.";
|
|
387
|
+
};
|
|
388
|
+
readonly SELF_UPGRADE_CURRENT: {
|
|
389
|
+
readonly class: "node";
|
|
390
|
+
readonly informational: true;
|
|
391
|
+
readonly cause: "The agent already runs the target version.";
|
|
392
|
+
readonly remedy: "Nothing to do.";
|
|
393
|
+
};
|
|
394
|
+
readonly DOWNGRADE: {
|
|
395
|
+
readonly class: "node";
|
|
396
|
+
readonly informational: true;
|
|
397
|
+
readonly cause: "The node runs a newer version than the target.";
|
|
398
|
+
readonly remedy: "Nothing to do, or choose a newer target.";
|
|
399
|
+
};
|
|
400
|
+
readonly MAJOR_VERSION: {
|
|
401
|
+
readonly class: "node";
|
|
402
|
+
readonly informational: true;
|
|
403
|
+
readonly cause: "The change crosses a major version.";
|
|
404
|
+
readonly remedy: "Upgrade this node by hand.";
|
|
405
|
+
};
|
|
406
|
+
readonly NOT_SELF_UPGRADABLE: {
|
|
407
|
+
readonly class: "node";
|
|
408
|
+
readonly informational: true;
|
|
409
|
+
readonly cause: "This agent cannot upgrade itself: it is too old, or not set up with the launcher.";
|
|
410
|
+
readonly remedy: "Run `sudo magnus-agent install-service` once on the node.";
|
|
411
|
+
};
|
|
412
|
+
readonly PROTOCOL_TOO_OLD: {
|
|
413
|
+
readonly class: "node";
|
|
414
|
+
readonly informational: true;
|
|
415
|
+
readonly cause: "The agent speaks an older self-upgrade protocol.";
|
|
416
|
+
readonly remedy: "Upgrade this agent by hand.";
|
|
417
|
+
};
|
|
418
|
+
readonly SELF_UPGRADE_DISABLED: {
|
|
419
|
+
readonly class: "node";
|
|
420
|
+
readonly informational: true;
|
|
421
|
+
readonly cause: "An operator switched self-upgrade off on this node, or it runs in a container or the legacy layout.";
|
|
422
|
+
readonly remedy: "Nothing to do if that is intended. Otherwise set MAGNUS_AGENT_SELF_UPGRADE=true, pull a newer image, or run `sudo magnus-agent install-service` once.";
|
|
423
|
+
};
|
|
424
|
+
readonly UPGRADE_IN_PROGRESS: {
|
|
425
|
+
readonly class: "node";
|
|
426
|
+
readonly cause: "An upgrade is staged or on trial on this node, and it can still switch back.";
|
|
427
|
+
readonly remedy: "Wait for its trial to end, then retry the node if it did not confirm.";
|
|
428
|
+
};
|
|
429
|
+
readonly ALREADY_REVERTED: {
|
|
430
|
+
readonly class: "node";
|
|
431
|
+
readonly cause: "This node already switched back from this exact version.";
|
|
432
|
+
readonly remedy: "Read the reason. To try again, use allowRetryReverted (--retry-reverted).";
|
|
433
|
+
};
|
|
434
|
+
readonly PREFLIGHT_UNAVAILABLE: {
|
|
435
|
+
readonly class: "node";
|
|
436
|
+
readonly informational: true;
|
|
437
|
+
readonly cause: "An agent older than 1.1.8 has no preflight check; heartbeat data is used instead.";
|
|
438
|
+
readonly remedy: "Nothing to do: the agent gains the check when it is upgraded.";
|
|
439
|
+
};
|
|
440
|
+
readonly PREFLIGHT_UNREACHABLE: {
|
|
441
|
+
readonly class: "node";
|
|
442
|
+
readonly informational: true;
|
|
443
|
+
readonly cause: "The preflight check got no answer. The node stays in the run, and dispatch decides.";
|
|
444
|
+
readonly remedy: "Nothing to do unless the node then fails with AGENT_UNREACHABLE.";
|
|
445
|
+
};
|
|
446
|
+
};
|
|
447
|
+
export type UpgradeProblemCode = keyof typeof UPGRADE_REMEDIES;
|
|
448
|
+
/** Cause and remedy for a code; an unknown code (a newer agent, an unexpected 4xx) is a node-level problem. */
|
|
449
|
+
export declare function upgradeRemedy(code: string | null | undefined): UpgradeRemedy;
|
|
450
|
+
export declare const upgradeFailureClass: (code: string | null | undefined) => UpgradeFailureClass;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// The one table of agent upgrade problem codes. The server sorts failures with
|
|
2
|
+
// it, and the console, the CLI and UPGRADING.md show its cause and remedy.
|
|
3
|
+
// Dependency-free so browser code can import it through
|
|
4
|
+
// `@hivemagnus/magnus-contracts/upgrade-remedies`.
|
|
5
|
+
export const UPGRADE_REMEDIES = {
|
|
6
|
+
// Release evidence.
|
|
7
|
+
SELF_UPGRADE_INTEGRITY: { class: 'release', stopsRun: true, cause: 'The downloaded tarball does not match the pinned npm integrity.', remedy: 'Do not retry. Check the registry or MAGNUS_AGENT_UPGRADE_SOURCE_DIR, and the integrity passed to the run.' },
|
|
8
|
+
SELF_UPGRADE_SOURCE: { class: 'release', stopsRun: true, cause: 'The tarball is missing from MAGNUS_AGENT_UPGRADE_SOURCE_DIR.', remedy: 'Copy the exact tarball to that directory on the nodes, then retry.' },
|
|
9
|
+
SELF_UPGRADE_FAILED: { class: 'release', cause: 'The package itself failed on the node: a bad tarball, an install error, missing provenance or a smoke test that failed or hung. An agent older than 1.1.8 also reports environment causes this way.', remedy: 'Read the detail and the agent log. If several nodes fail the same way, the release is at fault.' },
|
|
10
|
+
SELF_UPGRADE_REVERTED: { class: 'release', cause: 'The new version crashed or stayed silent during its trial, and the launcher switched back.', remedy: 'Read the reason on the node and in the agent log. To try again, retry with allowRetryReverted (--retry-reverted).' },
|
|
11
|
+
CONFIRM_TIMEOUT: { class: 'release', cause: 'The agent accepted the request, but the new version was not confirmed within its trial window.', remedy: 'Read the agent log on the node. A later report for this request is still recorded in the run.' },
|
|
12
|
+
AGENT_SILENT: { class: 'release', cause: 'The agent accepted the request and then stopped sending heartbeats.', remedy: 'Check the service on the node. The launcher switches back by itself if it is running.' },
|
|
13
|
+
// Ambiguous: the request may have run.
|
|
14
|
+
MUTATION_OUTCOME_UNKNOWN: { class: 'node', ambiguous: true, cause: 'The request was sent and no usable answer came back.', remedy: 'The run keeps watching the node by request id. If it never reports, check the node, then reconcile the lease of the request (agent-upgrade-<requestId>).' },
|
|
15
|
+
MANAGED_MUTATION_UNCERTAIN: { class: 'node', ambiguous: true, cause: 'The agent cannot say whether a managed request ran. Any version answers this when a handler failed after it may have started; an agent older than 1.1.8 also answers it for every refusal, whatever the real cause. The command lease stays unknown and blocks later managed requests on this node.', remedy: 'Read the agent log for the real cause. If the agent holds a receipt for the command (agent-upgrade-<requestId>), settle it with signed evidence: POST /api/v1/commands/<id>/reconcile-resource. If it holds none, an agent older than 1.1.8 offers no way out: upgrade that agent by hand on the node, after which the readiness monitor or `magnus-server agents doctor --heal` can settle the lease. If the cause was a stale fence, first run `magnus-server agents fence NODE VALUE`, or the next request fails the same way.' },
|
|
16
|
+
OUTCOME_UNKNOWN: { class: 'node', ambiguous: true, cause: 'The agent ledger holds an unresolved receipt for this command.', remedy: 'Inspect the receipt and reconcile it with signed evidence (docs/COMMAND_RESOURCE_LEASES.md).' },
|
|
17
|
+
RUN_INTERRUPTED: { class: 'node', ambiguous: true, cause: 'The server stopped watching this node when the run was interrupted.', remedy: 'A later report is recorded in the run. If the node does not report, retry it.' },
|
|
18
|
+
// Node-level and transient: tried again at the end of the wave.
|
|
19
|
+
SELF_UPGRADE_ENV_NETWORK: { class: 'node', transient: true, cause: 'The node could not reach the registry while fetching the new version.', remedy: 'Check the path from the node to the registry, or use MAGNUS_AGENT_UPGRADE_SOURCE_DIR, then retry it.' },
|
|
20
|
+
AGENT_UNREACHABLE: { class: 'node', transient: true, cause: 'The server could not open a connection to the agent.', remedy: 'Check that the agent is running and that its address and port in the inventory are correct.' },
|
|
21
|
+
SELF_UPGRADE_BUSY: { class: 'node', transient: true, cause: 'The agent is already staging an upgrade or another process holds its upgrade lock.', remedy: 'Wait for it to finish, then retry the node.' },
|
|
22
|
+
RESOURCE_BUSY: { class: 'node', transient: true, cause: 'Another command owns the single execution slot on the node.', remedy: 'Wait for the command to finish, then retry the node.' },
|
|
23
|
+
RESOURCE_LEASE_CONFLICT: { class: 'node', transient: true, cause: 'The server holds another unsettled command lease for the node.', remedy: 'Wait for it to settle. If it stays `unknown`, run `magnus-server agents doctor --heal` or reconcile it.' },
|
|
24
|
+
ENROLLMENT_AUTHORITY_UNAVAILABLE: { class: 'node', transient: true, cause: 'The agent could not reach the server to confirm its enrollment.', remedy: 'Check the path from the node to the server (CONTROL_API_URL, TLS trust).' },
|
|
25
|
+
STALE_FENCE: { class: 'node', transient: true, cause: 'The agent holds a resource fence at least as high as the one the server sent, and refused without running anything.', remedy: 'The server repairs this by itself by moving its generation up, and tries the node again. Nothing to do unless the problem persists.' },
|
|
26
|
+
LEASE_EXPIRED: { class: 'node', transient: true, cause: 'The command lease expired before the agent could start it; nothing ran.', remedy: 'Retry the node. If this repeats, compare the clocks of the node and the server.' },
|
|
27
|
+
NOT_DISPATCHED: { class: 'node', transient: true, cause: 'The server did not send the request.', remedy: 'Read the detail, then retry the node.' },
|
|
28
|
+
CHECK_FAILED: { class: 'node', transient: true, cause: 'The server could not check this node right before asking it, so it did not ask.', remedy: 'Read the detail, usually a database or Redis problem on the server, then retry the node.' },
|
|
29
|
+
// Node-level: needs someone.
|
|
30
|
+
AGENT_SILENT_AFTER_LOST_REPLY: { class: 'node', ambiguous: true, cause: 'The request may or may not have arrived, and the agent has stopped sending heartbeats.', remedy: 'Check the service on the node. A later report for this request is still recorded in the run.' },
|
|
31
|
+
FENCE_UNREPORTED: { class: 'node', cause: 'This agent is older than 1.1.8, has handled managed requests before, and cannot say what fence it holds. If that fence is ahead of the server, its request fails as MANAGED_MUTATION_UNCERTAIN and strands a lease.', remedy: 'Before the wave, read .magnus/command-receipts/fence-*.json on the node and run `magnus-server agents fence NODE VALUE`. Nothing to do if the fence is not ahead.' },
|
|
32
|
+
LEDGER_MISSING: { class: 'node', cause: 'The agent has no command ledger, so its "no receipt" proves nothing about a lease held for it. The ledger may have moved, or the install may have been rebuilt.', remedy: 'Check AGENT_INSTALL_DIR and restore .magnus/command-receipts if it moved; the lease then settles by itself. If it is gone, Magnus has no way to settle the lease: establish by hand what the command did, then change the lease row in the database yourself.' },
|
|
33
|
+
LEDGER_NO_FENCE: { class: 'node', cause: 'The agent ledger holds neither a fence nor this node\'s enrollment marker, so nothing shows it is the ledger this command would have used.', remedy: 'Check whether .magnus/command-receipts was replaced, and restore it if so; the lease then settles by itself. If it cannot be restored, Magnus has no way to settle the lease: establish by hand what the command did, then change the lease row in the database yourself.' },
|
|
34
|
+
LEDGER_EVIDENCE_INCONSISTENT: { class: 'node', cause: 'The agent ledger accepted this lease\'s fence, but holds no receipt, no claim and no record of a failed admission for it. Either a receipt was removed, or an admission failed on an agent build that kept no such record.', remedy: 'Inspect .magnus/command-receipts on the node and the agent log around the lease\'s time. Magnus has no way to settle the lease: establish by hand what the command did, then change the lease row in the database yourself.' },
|
|
35
|
+
ENROLLMENT_MARKER_UNKNOWN: { class: 'node', cause: 'The agent could not read its managed-enrollment marker, so a mismatch cannot be ruled out.', remedy: 'Check .magnus/command-receipts/managed-enrollment.json on the node: its mode, owner and content.' },
|
|
36
|
+
SELF_UPGRADE_ENV_TLS: { class: 'node', cause: 'npm would not trust the registry\'s certificate on this node: a wrong clock, an expired certificate or an intercepting proxy.', remedy: 'Check the clock on the node and its CA trust for the registry, then retry it.' },
|
|
37
|
+
SELF_UPGRADE_ENV_AUTH: { class: 'node', cause: 'The registry refused this node\'s request (401 or 403).', remedy: 'Check the npm credentials and registry settings on the node, or use MAGNUS_AGENT_UPGRADE_SOURCE_DIR, then retry it.' },
|
|
38
|
+
SELF_UPGRADE_ENV_RESOURCE: { class: 'node', cause: 'The node ran out of memory or file handles, npm was killed, or a file was busy. A killed npm is told apart by how the process ended; a lifecycle script killed inside npm still reads as a package fault.', remedy: 'Check the memory on the node and its kernel log, then retry it.' },
|
|
39
|
+
RETRY_HELD_SUSPECTED: { class: 'node', cause: 'The run this one retries stopped because older agents answered ambiguously with nothing confirmed. Each further attempt on an older agent can strand another lease, so this retry leaves them alone.', remedy: 'Read the agent log of the nodes that answered, and run `magnus-server agents fence NODE VALUE` where a fence is ahead. Then retry with acknowledgeSuspicion (--acknowledge-suspicion).' },
|
|
40
|
+
SELF_UPGRADE_ENV_DISK: { class: 'node', cause: 'The node ran out of disk space or quota, or hit an I/O error, while installing the new version.', remedy: 'Free disk space on the node, then retry it.' },
|
|
41
|
+
SELF_UPGRADE_ENV_ACCESS: { class: 'node', cause: 'The node refused a write while installing the new version.', remedy: 'Run `sudo magnus-agent install-service` again on the node, which repairs ownership, then retry it.' },
|
|
42
|
+
RESOURCE_ENROLLMENT_REQUIRED: { class: 'node', cause: 'Admission refused the request because the node\'s resource enrollment is not complete.', remedy: 'Complete the node\'s resource enrollment (docs/COMMAND_RESOURCE_LEASES.md).' },
|
|
43
|
+
FENCE_OVERFLOW: { class: 'node', cause: 'The agent reports a resource fence above the highest value the server will adopt.', remedy: 'Stop the agent, inspect .magnus/command-receipts/fence-*.json on the node, and repair that file by hand. The server does not change its generation.' },
|
|
44
|
+
FENCE_INVALID: { class: 'node', cause: 'The agent reports a resource fence that is not a decimal value for its own node.', remedy: 'Inspect .magnus/command-receipts/fence-*.json on the node.' },
|
|
45
|
+
FENCE_AHEAD: { class: 'node', cause: 'The agent holds a resource fence above the server generation, so managed requests would be refused.', remedy: 'Nothing to do for an upgrade run: it repairs this at its start. `magnus-server agents doctor --heal` repairs it now.' },
|
|
46
|
+
IDEMPOTENCY_CONFLICT: { class: 'node', cause: 'The agent already has a receipt for this command identifier with different input.', remedy: 'Retry the node, which uses a new identifier, and report the conflict if it repeats.' },
|
|
47
|
+
LEDGER_UNAVAILABLE: { class: 'node', cause: 'The agent could not read or write its command ledger.', remedy: 'Check ownership, mode 0700 and free space of .magnus/command-receipts on the node.' },
|
|
48
|
+
LEDGER_UNRESOLVED: { class: 'node', cause: 'The agent ledger holds a command with an unresolved outcome, which blocks every later managed request.', remedy: 'Reconcile that command with signed evidence (docs/COMMAND_RESOURCE_LEASES.md).' },
|
|
49
|
+
LEASE_HELD: { class: 'node', cause: 'A command lease on the node is reserved, running or unknown.', remedy: 'Wait for it to finish. On a 1.1.8 agent, a lease stuck `unknown` with no receipt is settled by the readiness monitor, a run, or `magnus-server agents doctor --heal`; one with a receipt needs signed reconciliation. On an older agent, see MANAGED_MUTATION_UNCERTAIN.' },
|
|
50
|
+
WORK_HELD: { class: 'node', cause: 'Participant work, such as a console terminal or SSH session, is still held on the node.', remedy: 'Finish or reconcile that work, then retry the node.' },
|
|
51
|
+
UNMANAGED_MUTATION: { class: 'node', suspectable: true, cause: 'The agent is enrolled for managed requests and refused a request without a valid claim.', remedy: 'Complete the node\'s resource enrollment on the server (docs/COMMAND_RESOURCE_LEASES.md), or upgrade the agent by hand.' },
|
|
52
|
+
RESOURCE_CLAIM_INVALID: { class: 'node', cause: 'The server would not confirm the agent\'s claim for this request.', remedy: 'Check the enrollment and inventory host name of the node, then retry.' },
|
|
53
|
+
RESOURCE_TARGET_INVALID: { class: 'node', cause: 'Admission refused the node: its inventory address changed or its health is not fresh.', remedy: 'Wait for a fresh heartbeat, or repair the inventory address.' },
|
|
54
|
+
ADMISSION_FENCE_CLOSED: { class: 'node', cause: 'A server release closed admission to this node.', remedy: 'Release the admission fence (POST /api/v1/resource-leases/admission/<nodeId>/release).' },
|
|
55
|
+
ENROLLMENT_MISSING: { class: 'node', cause: 'Resource leases apply to this node but it is not enrolled.', remedy: 'Enroll the node (docs/COMMAND_RESOURCE_LEASES.md).' },
|
|
56
|
+
ENROLLMENT_DRAINING: { class: 'node', cause: 'The node\'s resource enrollment is stuck in draining.', remedy: 'Finish the drain: POST /api/v1/resource-leases/enrollment/<nodeId>/complete.' },
|
|
57
|
+
ENROLLMENT_HOST_MISMATCH: { class: 'node', cause: 'The enrollment names another host than the inventory.', remedy: 'Correct the host name of the node in the inventory.' },
|
|
58
|
+
ENROLLMENT_INCOMPLETE: { class: 'node', cause: 'The node\'s resource enrollment is not complete.', remedy: 'Enroll the node (docs/COMMAND_RESOURCE_LEASES.md).' },
|
|
59
|
+
ENROLLMENT_MARKER_MISMATCH: { class: 'node', cause: 'The agent holds a local managed-enrollment marker the server does not share.', remedy: 'Complete the node\'s resource enrollment on the server (docs/COMMAND_RESOURCE_LEASES.md), or upgrade the agent by hand.' },
|
|
60
|
+
NODE_OFFLINE: { class: 'node', transient: true, cause: 'The node is offline or has no recent heartbeat.', remedy: 'Bring the agent back, then retry the node.' },
|
|
61
|
+
NODE_NOT_FOUND: { class: 'node', cause: 'The node is no longer in the inventory.', remedy: 'Register the node again if it should be managed.' },
|
|
62
|
+
VERSION_UNKNOWN: { class: 'node', cause: 'The agent has not reported a usable version.', remedy: 'Wait for a heartbeat, or upgrade the agent by hand.' },
|
|
63
|
+
SELF_UPGRADE_TIMEOUT: { class: 'node', cause: 'An npm step of the upgrade did not finish in its time budget on the node. A new version that hangs when it is run reports SELF_UPGRADE_FAILED instead. A lifecycle script that hangs inside npm also ends up here: it cannot be told apart.', remedy: 'Check the network and disk speed of the node, then retry it. If every node hangs the same way, suspect the release.' },
|
|
64
|
+
SELF_UPGRADE_UNAVAILABLE: { class: 'node', cause: 'The agent cannot upgrade itself now: its layout, launcher, directories or npm are not usable.', remedy: 'Read the detail. Running `sudo magnus-agent install-service` once on the node usually repairs it.' },
|
|
65
|
+
SELF_UPGRADE_LAUNCHER: { class: 'node', suspectable: true, cause: 'The new version needs a newer launcher than the node has.', remedy: 'Run `sudo magnus-agent install-service` once on the node.' },
|
|
66
|
+
SELF_UPGRADE_ENGINE: { class: 'node', suspectable: true, cause: 'The new version does not support the Node.js on this node.', remedy: 'Upgrade Node.js on the node.' },
|
|
67
|
+
SELF_UPGRADE_PACKAGE: { class: 'node', suspectable: true, cause: 'The agent refused to install a package other than magnus-agent.', remedy: 'Report this: the server sent an unexpected package name.' },
|
|
68
|
+
SELF_UPGRADE_REFUSED: { class: 'node', suspectable: true, cause: 'The agent refused the version change.', remedy: 'Read the detail. Crossing a major version is done by hand.' },
|
|
69
|
+
PREFLIGHT_NOT_WRITABLE: { class: 'node', cause: 'The agent cannot write to its install directory.', remedy: 'Run `sudo magnus-agent install-service` again on the node, which repairs ownership.' },
|
|
70
|
+
PREFLIGHT_DISK_LOW: { class: 'node', cause: 'The install directory has less free space than an upgrade needs.', remedy: 'Free disk space on the node.' },
|
|
71
|
+
PREFLIGHT_NPM: { class: 'node', cause: 'npm is missing on the node or too old.', remedy: 'Install npm beside the agent\'s Node.js, or set MAGNUS_AGENT_NPM.' },
|
|
72
|
+
PREFLIGHT_FROM_INCOMPLETE: { class: 'node', cause: 'The running version is not complete under versions/, so a failed upgrade could not switch back.', remedy: 'Run `sudo magnus-agent install-service` again on the node.' },
|
|
73
|
+
PREFLIGHT_ENGINE: { class: 'node', cause: 'The target version does not support the Node.js on this node.', remedy: 'Upgrade Node.js on the node.' },
|
|
74
|
+
NOT_REACHED: { class: 'node', cause: 'The run ended before it reached this node.', remedy: 'Retry the run, once what ended it is dealt with. This node was not asked and did not change.' },
|
|
75
|
+
RUN_STOPPED: { class: 'node', cause: 'Someone stopped the run before it reached this node.', remedy: 'Retry the run when you want this node upgraded. This node was not asked and did not change.' },
|
|
76
|
+
NOT_ELIGIBLE: { class: 'node', cause: 'This node became ineligible at its turn, for a reason without a code of its own.', remedy: 'Read the detail, then retry the node.' },
|
|
77
|
+
// Expected states: reported, but not a problem with the run.
|
|
78
|
+
ALREADY_CURRENT: { class: 'node', informational: true, cause: 'The node already runs the target version.', remedy: 'Nothing to do.' },
|
|
79
|
+
SELF_UPGRADE_CURRENT: { class: 'node', informational: true, cause: 'The agent already runs the target version.', remedy: 'Nothing to do.' },
|
|
80
|
+
DOWNGRADE: { class: 'node', informational: true, cause: 'The node runs a newer version than the target.', remedy: 'Nothing to do, or choose a newer target.' },
|
|
81
|
+
MAJOR_VERSION: { class: 'node', informational: true, cause: 'The change crosses a major version.', remedy: 'Upgrade this node by hand.' },
|
|
82
|
+
NOT_SELF_UPGRADABLE: { class: 'node', informational: true, cause: 'This agent cannot upgrade itself: it is too old, or not set up with the launcher.', remedy: 'Run `sudo magnus-agent install-service` once on the node.' },
|
|
83
|
+
PROTOCOL_TOO_OLD: { class: 'node', informational: true, cause: 'The agent speaks an older self-upgrade protocol.', remedy: 'Upgrade this agent by hand.' },
|
|
84
|
+
SELF_UPGRADE_DISABLED: { class: 'node', informational: true, cause: 'An operator switched self-upgrade off on this node, or it runs in a container or the legacy layout.', remedy: 'Nothing to do if that is intended. Otherwise set MAGNUS_AGENT_SELF_UPGRADE=true, pull a newer image, or run `sudo magnus-agent install-service` once.' },
|
|
85
|
+
UPGRADE_IN_PROGRESS: { class: 'node', cause: 'An upgrade is staged or on trial on this node, and it can still switch back.', remedy: 'Wait for its trial to end, then retry the node if it did not confirm.' },
|
|
86
|
+
ALREADY_REVERTED: { class: 'node', cause: 'This node already switched back from this exact version.', remedy: 'Read the reason. To try again, use allowRetryReverted (--retry-reverted).' },
|
|
87
|
+
PREFLIGHT_UNAVAILABLE: { class: 'node', informational: true, cause: 'An agent older than 1.1.8 has no preflight check; heartbeat data is used instead.', remedy: 'Nothing to do: the agent gains the check when it is upgraded.' },
|
|
88
|
+
PREFLIGHT_UNREACHABLE: { class: 'node', informational: true, cause: 'The preflight check got no answer. The node stays in the run, and dispatch decides.', remedy: 'Nothing to do unless the node then fails with AGENT_UNREACHABLE.' },
|
|
89
|
+
};
|
|
90
|
+
const FALLBACK = { class: 'node', unknown: true, suspectable: true, cause: 'Unrecognised code: the agent or the server refused this node for a reason the catalogue does not list.', remedy: 'Read the detail and the agent log, then retry the node. An agent newer than the server can cause this.' };
|
|
91
|
+
/** Cause and remedy for a code; an unknown code (a newer agent, an unexpected 4xx) is a node-level problem. */
|
|
92
|
+
export function upgradeRemedy(code) {
|
|
93
|
+
return (code ? UPGRADE_REMEDIES[code] : undefined) ?? FALLBACK;
|
|
94
|
+
}
|
|
95
|
+
export const upgradeFailureClass = (code) => upgradeRemedy(code).class;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hivemagnus/magnus-contracts",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.10",
|
|
4
4
|
"description": "Shared Zod schemas and TypeScript types for the Magnus server, UI and node agent wire contracts.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"magnus",
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
".": {
|
|
27
27
|
"types": "./dist/index.d.ts",
|
|
28
28
|
"default": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./upgrade-remedies": {
|
|
31
|
+
"types": "./dist/upgrade-remedies.d.ts",
|
|
32
|
+
"default": "./dist/upgrade-remedies.js"
|
|
29
33
|
}
|
|
30
34
|
},
|
|
31
35
|
"files": [
|