omp-conductor 0.3.25 → 0.4.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/README.md +733 -30
- package/package.json +1 -1
- package/skills/conductor-onboarding/SKILL.md +56 -0
- package/skills/conductor-update/SKILL.md +27 -0
- package/src/board.ts +24 -5
- package/src/briefs/orchestrator.md +106 -20
- package/src/briefs/policy.md +48 -27
- package/src/briefs/worker.md +82 -31
- package/src/cli.ts +155 -2
- package/src/config.ts +669 -16
- package/src/confinement.ts +506 -25
- package/src/credentials.ts +2029 -0
- package/src/daemon.ts +1021 -32
- package/src/diff-flags.ts +696 -0
- package/src/escalate.ts +136 -9
- package/src/fleet.ts +110 -3
- package/src/omp.ts +471 -15
- package/src/orchestrator-tick.ts +42 -19
- package/src/orchestrator.ts +32 -5
- package/src/plugin.ts +267 -15
- package/src/release-policy.ts +191 -29
- package/src/reports.ts +440 -0
- package/src/session-host.ts +307 -0
- package/src/setup-host.ts +162 -1
- package/src/setup.ts +207 -18
- package/src/store.ts +506 -3
- package/src/tracker/github.ts +45 -0
- package/src/types.ts +847 -10
- package/src/upgrade.ts +96 -3
- package/src/usage.ts +726 -0
- package/src/verbs/actions.ts +142 -0
- package/src/verbs/client.ts +207 -0
- package/src/verbs/ledger.ts +77 -0
- package/src/verbs/protocol.ts +465 -0
- package/src/verbs/server.ts +1098 -0
- package/src/verbs/socket.ts +446 -0
- package/src/worker.ts +36 -7
- package/src/worktree.ts +202 -109
- package/systemd/omp-conductor.service.example +96 -8
package/src/upgrade.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { inspectBriefLayout, type BriefLayout } from "./brief-upgrade.ts";
|
|
3
3
|
import { setPaused, statusSnapshot } from "./daemon.ts";
|
|
4
|
-
import { fleetLayers, type DispatchLayer, type FleetLayers } from "./fleet.ts";
|
|
4
|
+
import { fleetLayers, telegramStateDir, type DispatchLayer, type FleetLayers } from "./fleet.ts";
|
|
5
5
|
import { livingDaemon, restartDaemon } from "./lifecycle.ts";
|
|
6
|
-
import { findProject, loadConfig } from "./config.ts";
|
|
6
|
+
import { findProject, loadConfig, resolveCaps } from "./config.ts";
|
|
7
7
|
import { renderBriefForProject } from "./setup.ts";
|
|
8
|
+
import {
|
|
9
|
+
STAGED_SERVICE_NAME,
|
|
10
|
+
parseEffectiveUnit,
|
|
11
|
+
planHostRuntime,
|
|
12
|
+
unitDriftReason,
|
|
13
|
+
writeHostRuntime,
|
|
14
|
+
type EffectiveUnit,
|
|
15
|
+
} from "./setup-host.ts";
|
|
8
16
|
|
|
9
17
|
const PACKAGE = "omp-conductor";
|
|
10
18
|
const HERDR_PLUGIN = "herdr-conductor";
|
|
@@ -33,6 +41,15 @@ export interface UpgradeResult {
|
|
|
33
41
|
dispatch: DispatchLayer;
|
|
34
42
|
}
|
|
35
43
|
|
|
44
|
+
/** What the unit check reads, and how it repairs what it can. */
|
|
45
|
+
export interface UnitFiles {
|
|
46
|
+
rendered: string;
|
|
47
|
+
/** What systemd loaded, not what is on disk. Undefined means no systemd here. */
|
|
48
|
+
effective: EffectiveUnit | undefined;
|
|
49
|
+
/** Writes the corrected unit to the state dir; returns the commands root must run. */
|
|
50
|
+
stage?(): readonly string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
36
53
|
export interface UpgradeDeps {
|
|
37
54
|
run(command: string, args: readonly string[]): Promise<UpgradeCommandResult>;
|
|
38
55
|
snapshot(project?: string): { liveWorkers: number };
|
|
@@ -44,6 +61,12 @@ export interface UpgradeDeps {
|
|
|
44
61
|
sleep(ms: number): Promise<void>;
|
|
45
62
|
env: NodeJS.ProcessEnv;
|
|
46
63
|
log(message: string): void;
|
|
64
|
+
/**
|
|
65
|
+
* What this version would render as the unit, and what systemd actually
|
|
66
|
+
* booted from. Injected so the drift check is testable without a systemd host
|
|
67
|
+
* — and defaulted, so no production caller has to know it exists.
|
|
68
|
+
*/
|
|
69
|
+
unitFiles?(): Promise<UnitFiles | undefined> | UnitFiles | undefined;
|
|
47
70
|
}
|
|
48
71
|
|
|
49
72
|
async function runCommand(command: string, args: readonly string[]): Promise<UpgradeCommandResult> {
|
|
@@ -381,6 +404,49 @@ async function rollbackUpgrade(
|
|
|
381
404
|
if (failures.length > 0) throw new Error(failures.join("; "));
|
|
382
405
|
}
|
|
383
406
|
|
|
407
|
+
/**
|
|
408
|
+
* The rendered-vs-installed pair for {@link unitDriftReason}.
|
|
409
|
+
*
|
|
410
|
+
* Best-effort by design: a host with no config, no project, or no systemd is
|
|
411
|
+
* not a host with a broken unit, and an upgrade must not fail because it could
|
|
412
|
+
* not answer a question that does not apply there.
|
|
413
|
+
*/
|
|
414
|
+
async function defaultUnitFiles(deps: UpgradeDeps): Promise<UnitFiles | undefined> {
|
|
415
|
+
try {
|
|
416
|
+
const cfg = loadConfig();
|
|
417
|
+
const project = cfg.projects[0];
|
|
418
|
+
if (project === undefined) return undefined;
|
|
419
|
+
const plan = planHostRuntime(project, resolveCaps(project, cfg.defaults), telegramStateDir());
|
|
420
|
+
const shown = await deps.run("systemctl", [
|
|
421
|
+
"show",
|
|
422
|
+
STAGED_SERVICE_NAME,
|
|
423
|
+
"-p",
|
|
424
|
+
"AmbientCapabilities",
|
|
425
|
+
"-p",
|
|
426
|
+
"CapabilityBoundingSet",
|
|
427
|
+
"-p",
|
|
428
|
+
"Environment",
|
|
429
|
+
"-p",
|
|
430
|
+
"NeedDaemonReload",
|
|
431
|
+
]);
|
|
432
|
+
return {
|
|
433
|
+
rendered: plan.service.content,
|
|
434
|
+
effective: shown.code === 0 ? parseEffectiveUnit(shown.stdout) : undefined,
|
|
435
|
+
// Staging needs no privilege, so the repair is reduced to the two lines
|
|
436
|
+
// that genuinely do. Installing the unit is root's, and this command runs
|
|
437
|
+
// as the fleet user by design — so it goes as far as it can and then says
|
|
438
|
+
// exactly what is left, rather than sending the operator back through a
|
|
439
|
+
// wizard to regenerate a file it could write itself.
|
|
440
|
+
stage: () => {
|
|
441
|
+
writeHostRuntime(plan);
|
|
442
|
+
return plan.installCommands;
|
|
443
|
+
},
|
|
444
|
+
};
|
|
445
|
+
} catch {
|
|
446
|
+
return undefined;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
384
450
|
export async function upgradeConductor(
|
|
385
451
|
options: UpgradeOptions = {},
|
|
386
452
|
overrides: Partial<UpgradeDeps> = {},
|
|
@@ -407,6 +473,22 @@ export async function upgradeConductor(
|
|
|
407
473
|
const surfaces = await inspectSurfaces(deps);
|
|
408
474
|
const brief = deps.brief(project);
|
|
409
475
|
const installNeeded = !surfacesCurrent(surfaces, release.version, release.gitHead);
|
|
476
|
+
// Computed BEFORE the no-op decision, not inside verification, and that
|
|
477
|
+
// placement is the whole point. The upgrade that installs a version is run by
|
|
478
|
+
// the *previous* CLI, so a check living only in the new code never executes
|
|
479
|
+
// for the release that introduces it — and re-running afterwards would take
|
|
480
|
+
// the already-current early return and skip it forever. A fleet whose unit is
|
|
481
|
+
// stale is not current, whatever its package identities say.
|
|
482
|
+
const files = await (deps.unitFiles === undefined ? defaultUnitFiles(deps) : deps.unitFiles());
|
|
483
|
+
const drift = files === undefined ? undefined : unitDriftReason(files.rendered, files.effective);
|
|
484
|
+
if (drift !== undefined) {
|
|
485
|
+
const commands = files?.stage?.() ?? [];
|
|
486
|
+
throw new Error(
|
|
487
|
+
commands.length === 0
|
|
488
|
+
? drift
|
|
489
|
+
: `${drift}\n\nThe corrected unit has been staged. Run:\n${commands.map((c) => ` ${c}`).join("\n")}`,
|
|
490
|
+
);
|
|
491
|
+
}
|
|
410
492
|
if (!installNeeded && brief.current) {
|
|
411
493
|
return {
|
|
412
494
|
previousVersion: surfaces.cliVersion,
|
|
@@ -489,6 +571,17 @@ export async function upgradeConductor(
|
|
|
489
571
|
const active = await mustRun(deps, "systemctl", ["is-active", HERDR_UNIT]);
|
|
490
572
|
if (active.stdout.trim() !== "active") throw new Error(`${HERDR_UNIT} is not active after restart`);
|
|
491
573
|
}
|
|
574
|
+
// The unit is the one surface `upgrade` never looked at, and since 0.4.0 it
|
|
575
|
+
// is the difference between a working per-run fleet and one that refuses
|
|
576
|
+
// every issue. Fatal rather than a warning: the whole point of this command
|
|
577
|
+
// is that a fleet is either upgraded or explicitly left paused, and a
|
|
578
|
+
// "verified" fleet that cannot dispatch is the worse outcome.
|
|
579
|
+
// Re-read rather than trust the earlier pass: the daemon has restarted
|
|
580
|
+
// since, and an operator who installed a unit mid-upgrade should not get a
|
|
581
|
+
// green verification for a file nobody checked.
|
|
582
|
+
const after = await (deps.unitFiles === undefined ? defaultUnitFiles(deps) : deps.unitFiles());
|
|
583
|
+
const stillDrifted = after === undefined ? undefined : unitDriftReason(after.rendered, after.effective);
|
|
584
|
+
if (stillDrifted !== undefined) throw new Error(stillDrifted);
|
|
492
585
|
await waitForRecovery(deps, initial, project);
|
|
493
586
|
deps.log("verify 2/2: recovered fleet remains stable");
|
|
494
587
|
await deps.sleep(1_000);
|