@ouro.bot/cli 0.1.0-alpha.733 → 0.1.0-alpha.735

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.
Files changed (89) hide show
  1. package/changelog.json +34 -0
  2. package/deploy/unraid/Dockerfile +27 -0
  3. package/deploy/unraid/README.txt +1605 -0
  4. package/deploy/unraid/audit-container-spec.sh +4 -0
  5. package/deploy/unraid/container-runtime.json +1 -0
  6. package/deploy/unraid/sanctuary-acceptance-adapter.sh +37 -0
  7. package/deploy/unraid/sanctuary-acceptance-contract.json +200 -0
  8. package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
  9. package/deploy/unraid/sanctuary-deployment-target.mjs +705 -0
  10. package/deploy/unraid/sanctuary-safe-rename.py +24 -0
  11. package/deploy/unraid/sanctuary-unit16-host-broker.mjs +1777 -0
  12. package/deploy/unraid/sanctuary-unit16-run.sh +502 -0
  13. package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
  14. package/deploy/unraid/sanctuary.ouro/agent.json +24 -0
  15. package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
  16. package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
  17. package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
  18. package/deploy/unraid/sanctuary.ouro/provider-readiness.json +18 -0
  19. package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +3 -0
  20. package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +3 -0
  21. package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +3 -0
  22. package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +5 -0
  23. package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +7 -0
  24. package/deploy/unraid/sanctuary.ouro/tool-profiles.json +21 -0
  25. package/deploy/unraid/sanctuary.xml +25 -0
  26. package/dist/heart/approval-files.js +113 -0
  27. package/dist/heart/approval-store.js +168 -9
  28. package/dist/heart/auth/auth-flow.js +17 -1
  29. package/dist/heart/autonomy-budget.js +44 -1
  30. package/dist/heart/awaiting/await-scheduler.js +6 -2
  31. package/dist/heart/config.js +8 -0
  32. package/dist/heart/core.js +117 -132
  33. package/dist/heart/daemon/cli-exec.js +73 -1
  34. package/dist/heart/daemon/cli-help.js +5 -0
  35. package/dist/heart/daemon/cli-parse.js +6 -4
  36. package/dist/heart/daemon/container-credential-bootstrap.js +208 -0
  37. package/dist/heart/daemon/container-healthcheck.js +94 -0
  38. package/dist/heart/daemon/container-runtime.js +84 -0
  39. package/dist/heart/daemon/container-spec-auditor-main.js +156 -0
  40. package/dist/heart/daemon/container-spec-auditor.js +156 -0
  41. package/dist/heart/daemon/daemon-bootstrap-startup.js +41 -0
  42. package/dist/heart/daemon/daemon-entry.js +116 -28
  43. package/dist/heart/daemon/daemon-health.js +10 -0
  44. package/dist/heart/daemon/daemon.js +92 -5
  45. package/dist/heart/daemon/runtime-logging.js +6 -0
  46. package/dist/heart/daemon/runtime-mode.js +13 -2
  47. package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2365 -0
  48. package/dist/heart/daemon/sanctuary-acceptance-harness.js +1624 -0
  49. package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
  50. package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +985 -0
  51. package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
  52. package/dist/heart/daemon/sanctuary-scheduler-origin.js +126 -0
  53. package/dist/heart/daemon/sense-manager.js +25 -6
  54. package/dist/heart/daemon/supercronic-supervisor.js +267 -0
  55. package/dist/heart/habits/habit-scheduler.js +7 -3
  56. package/dist/heart/identity.js +13 -3
  57. package/dist/heart/provider-credentials.js +9 -1
  58. package/dist/heart/provider-models.js +8 -0
  59. package/dist/heart/provider-ping.js +5 -2
  60. package/dist/heart/providers/openai-compatible.js +177 -0
  61. package/dist/heart/runtime-credentials.js +217 -8
  62. package/dist/heart/sense-truth.js +2 -0
  63. package/dist/heart/tool-approval.js +1 -0
  64. package/dist/heart/turn-context.js +8 -0
  65. package/dist/mind/prompt.js +9 -0
  66. package/dist/nerves/coverage/source-scanner.js +4 -4
  67. package/dist/nerves/index.js +75 -9
  68. package/dist/nerves/runtime.js +12 -0
  69. package/dist/repertoire/tool-arguments.js +39 -0
  70. package/dist/repertoire/tools-unraid.js +353 -0
  71. package/dist/repertoire/tools.js +19 -1
  72. package/dist/repertoire/unraid-client.js +155 -0
  73. package/dist/repertoire/unraid-restart.js +130 -0
  74. package/dist/senses/private-runtime-worker.js +3 -2
  75. package/dist/senses/sanctuary-grounding.js +118 -0
  76. package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
  77. package/dist/senses/sanctuary-health-acceptance-probe.js +772 -0
  78. package/dist/senses/sanctuary-health-runner.js +146 -0
  79. package/dist/senses/sanctuary-health.js +447 -0
  80. package/dist/senses/sanctuary-interactive-control.js +306 -0
  81. package/dist/senses/sanctuary-runtime.js +206 -0
  82. package/dist/senses/shared-turn.js +11 -3
  83. package/dist/senses/telegram-approval-runtime.js +526 -0
  84. package/dist/senses/telegram-audit-ledger.js +220 -0
  85. package/dist/senses/telegram-client.js +1471 -0
  86. package/dist/senses/telegram-entry.js +80 -0
  87. package/dist/senses/telegram.js +1232 -0
  88. package/npm-shrinkwrap.json +6558 -0
  89. package/package.json +4 -2
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getDefaultContainerCredentialBootstrapPath = getDefaultContainerCredentialBootstrapPath;
37
+ exports.loadContainerCredentialBootstrap = loadContainerCredentialBootstrap;
38
+ const fs = __importStar(require("node:fs"));
39
+ const os = __importStar(require("node:os"));
40
+ const path = __importStar(require("node:path"));
41
+ const runtime_credentials_1 = require("../runtime-credentials");
42
+ const runtime_1 = require("../../nerves/runtime");
43
+ const machine_identity_1 = require("../machine-identity");
44
+ const MAX_BOOTSTRAP_BYTES = 128 * 1024;
45
+ function isRecord(value) {
46
+ return !!value && typeof value === "object" && !Array.isArray(value);
47
+ }
48
+ function parseEnvelope(raw) {
49
+ const value = JSON.parse(raw);
50
+ if (!isRecord(value) || value.schemaVersion !== 1 || !Array.isArray(value.credentials)) {
51
+ throw new Error("container credential bootstrap envelope is invalid");
52
+ }
53
+ if (Object.keys(value).sort().join(",") !== "credentials,schemaVersion") {
54
+ throw new Error("container credential bootstrap has unsupported fields");
55
+ }
56
+ if (value.credentials.length === 0) {
57
+ throw new Error("container credential bootstrap must contain at least one credential");
58
+ }
59
+ return value;
60
+ }
61
+ function fsyncDirectory(directoryPath) {
62
+ const descriptor = fs.openSync(directoryPath, "r");
63
+ try {
64
+ fs.fsyncSync(descriptor);
65
+ }
66
+ finally {
67
+ fs.closeSync(descriptor);
68
+ }
69
+ }
70
+ function deleteDurably(filePath) {
71
+ fs.unlinkSync(filePath);
72
+ fsyncDirectory(path.dirname(filePath));
73
+ }
74
+ function assertSafeBootstrapFile(stat, label) {
75
+ if (!stat.isFile() || stat.isSymbolicLink()) {
76
+ throw new Error(`container credential ${label} must be a regular file`);
77
+ }
78
+ if ((stat.mode & 0o077) !== 0)
79
+ throw new Error("container credential bootstrap must have mode 0600");
80
+ if (typeof process.getuid === "function" && stat.uid !== process.getuid()) {
81
+ throw new Error("container credential bootstrap must be owned by the runtime user");
82
+ }
83
+ if (stat.size > MAX_BOOTSTRAP_BYTES)
84
+ throw new Error("container credential bootstrap is too large");
85
+ }
86
+ function getDefaultContainerCredentialBootstrapPath() {
87
+ return path.join(os.homedir(), ".ouro-cli", "container-credentials.json");
88
+ }
89
+ function optionalStat(filePath) {
90
+ try {
91
+ return fs.lstatSync(filePath);
92
+ }
93
+ catch (error) {
94
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
95
+ return null;
96
+ throw error;
97
+ }
98
+ }
99
+ async function loadContainerCredentialBootstrap(enabledAgents, options = {}) {
100
+ const filePath = options.path ?? getDefaultContainerCredentialBootstrapPath();
101
+ const consumingPath = `${filePath}.consuming`;
102
+ let sourceStat = optionalStat(filePath);
103
+ const claimedStat = optionalStat(consumingPath);
104
+ if (sourceStat && claimedStat) {
105
+ assertSafeBootstrapFile(sourceStat, "bootstrap");
106
+ assertSafeBootstrapFile(claimedStat, "consuming state");
107
+ const sourceBytes = fs.readFileSync(filePath);
108
+ const claimedBytes = fs.readFileSync(consumingPath);
109
+ if (!sourceBytes.equals(claimedBytes)) {
110
+ throw new Error("human-required: container credential source and claim differ; securely compare and quarantine them without printing their contents");
111
+ }
112
+ deleteDurably(filePath);
113
+ sourceStat = null;
114
+ }
115
+ if (claimedStat)
116
+ assertSafeBootstrapFile(claimedStat, "consuming state");
117
+ let stat;
118
+ if (sourceStat) {
119
+ if (!sourceStat.isFile() || sourceStat.isSymbolicLink()) {
120
+ throw new Error("container credential bootstrap must be a regular file");
121
+ }
122
+ fs.renameSync(filePath, consumingPath);
123
+ fsyncDirectory(path.dirname(filePath));
124
+ stat = sourceStat;
125
+ }
126
+ else if (claimedStat) {
127
+ stat = claimedStat;
128
+ }
129
+ else {
130
+ return [];
131
+ }
132
+ assertSafeBootstrapFile(stat, claimedStat ? "consuming state" : "bootstrap");
133
+ const envelope = parseEnvelope(fs.readFileSync(consumingPath, "utf8"));
134
+ const allowed = new Set(enabledAgents);
135
+ const loaded = new Set();
136
+ for (const message of envelope.credentials) {
137
+ if (!isRecord(message) || typeof message.agentName !== "string" || !allowed.has(message.agentName)) {
138
+ throw new Error("container credential bootstrap agent is not enabled");
139
+ }
140
+ if (loaded.has(message.agentName))
141
+ throw new Error("container credential bootstrap contains a duplicate agent");
142
+ if (!(0, runtime_credentials_1.isRuntimeCredentialBootstrapMessage)(message))
143
+ throw new Error("container credential bootstrap message is invalid");
144
+ loaded.add(message.agentName);
145
+ }
146
+ const machineId = (0, machine_identity_1.loadOrCreateMachineIdentity)().machineId;
147
+ const migration = options.machineIdMigration;
148
+ let messages = envelope.credentials;
149
+ if (migration) {
150
+ const { sourceMachineId, targetMachineId } = migration;
151
+ if (!sourceMachineId
152
+ || !targetMachineId
153
+ || sourceMachineId !== sourceMachineId.trim()
154
+ || targetMachineId !== targetMachineId.trim()
155
+ || sourceMachineId === targetMachineId) {
156
+ throw new Error("container credential bootstrap machineId migration is invalid");
157
+ }
158
+ if (machineId !== targetMachineId) {
159
+ throw new Error("container credential bootstrap machineId migration target does not match this machine");
160
+ }
161
+ for (const message of messages) {
162
+ if (!isRecord(message) || message.machineId !== sourceMachineId) {
163
+ throw new Error("container credential bootstrap machineId migration source does not match");
164
+ }
165
+ }
166
+ messages = messages.map((message) => ({ ...message, machineId: targetMachineId }));
167
+ }
168
+ else {
169
+ for (const message of messages) {
170
+ if (isRecord(message) && typeof message.machineId === "string" && message.machineId !== machineId) {
171
+ throw new Error("container credential bootstrap machineId does not match this machine");
172
+ }
173
+ }
174
+ }
175
+ const persist = options.persist ?? ((message) => (0, runtime_credentials_1.persistRuntimeCredentialBootstrapMessage)(message, {
176
+ machineId,
177
+ }));
178
+ try {
179
+ for (const message of messages) {
180
+ if (!await persist(message))
181
+ throw new Error("invalid bootstrap message");
182
+ }
183
+ }
184
+ catch {
185
+ (0, runtime_1.emitNervesEvent)({
186
+ level: "error",
187
+ component: "config/identity",
188
+ event: "config.container_credentials_persist_error",
189
+ message: "container credential bootstrap persistence failed; recoverable claim retained",
190
+ meta: { agents: [...loaded].sort(), count: loaded.size, claimedPath: consumingPath },
191
+ });
192
+ throw new Error("container credential bootstrap persistence failed; recoverable claim retained for reconciliation");
193
+ }
194
+ if (options.apply) {
195
+ for (const message of messages) {
196
+ if (!options.apply(message))
197
+ throw new Error("container credential bootstrap message is invalid");
198
+ }
199
+ }
200
+ deleteDurably(consumingPath);
201
+ (0, runtime_1.emitNervesEvent)({
202
+ component: "config/identity",
203
+ event: "config.container_credentials_loaded",
204
+ message: "loaded container credential bootstrap",
205
+ meta: { agents: [...loaded].sort(), count: loaded.size },
206
+ });
207
+ return [...loaded].sort();
208
+ }
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runContainerHealthcheck = runContainerHealthcheck;
37
+ exports.runContainerHealthcheckMain = runContainerHealthcheckMain;
38
+ const fs = __importStar(require("node:fs"));
39
+ const os = __importStar(require("node:os"));
40
+ const path = __importStar(require("node:path"));
41
+ const node_child_process_1 = require("node:child_process");
42
+ const runtime_1 = require("../../nerves/runtime");
43
+ const container_runtime_1 = require("./container-runtime");
44
+ function fail(reason) {
45
+ (0, runtime_1.emitNervesEvent)({ level: "error", component: "daemon", event: "daemon.container_healthcheck_error", message: "container healthcheck failed", meta: { reason } });
46
+ process.exitCode = 1;
47
+ throw new Error(reason);
48
+ }
49
+ function runContainerHealthcheck(options = {}) {
50
+ const argv = options.argv ?? process.argv;
51
+ const now = options.now ?? Date.now;
52
+ const index = argv.indexOf("--agent");
53
+ const agent = index >= 0 ? argv[index + 1] : undefined;
54
+ if (!agent)
55
+ fail("missing agent");
56
+ const healthPath = path.join(os.homedir(), ".ouro-cli", "daemon-health.json");
57
+ let stat;
58
+ let health;
59
+ try {
60
+ stat = fs.statSync(healthPath);
61
+ health = JSON.parse(fs.readFileSync(healthPath, "utf8"));
62
+ }
63
+ catch {
64
+ fail("health state unavailable");
65
+ }
66
+ const checkedAt = now();
67
+ if (checkedAt - stat.mtimeMs > 90_000 || stat.mtimeMs > checkedAt + 1_000)
68
+ fail("health state stale");
69
+ if (health.pid !== 1 || !fs.existsSync("/proc/1"))
70
+ fail("daemon is not PID 1");
71
+ if (health.status !== "healthy" && health.status !== "partial")
72
+ fail("daemon status is not ready");
73
+ const agents = health.agents;
74
+ const managed = agents?.[agent];
75
+ if (!managed || managed.status !== "running" || typeof managed.pid !== "number" || managed.pid <= 0 || !fs.existsSync(`/proc/${managed.pid}`))
76
+ fail("managed agent is not running");
77
+ let processArguments = "";
78
+ try {
79
+ processArguments = (0, node_child_process_1.execFileSync)("ps", ["-eo", "args="], { encoding: "utf8", timeout: 2_000 });
80
+ }
81
+ catch {
82
+ fail("managed process inventory is unavailable");
83
+ }
84
+ if (!(0, container_runtime_1.hasManagedTelegramProcess)(processArguments, agent))
85
+ fail("managed Telegram sense is not running exactly once");
86
+ if (!(0, container_runtime_1.hasManagedSupercronicProcess)(processArguments, agent))
87
+ fail("managed Supercronic scheduler is not running exactly once");
88
+ (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.container_healthcheck_end", message: "container healthcheck passed", meta: { agent } });
89
+ }
90
+ function runContainerHealthcheckMain(isMain, runner) {
91
+ if (isMain)
92
+ runner();
93
+ }
94
+ runContainerHealthcheckMain(require.main === module, runContainerHealthcheck);
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.hasManagedTelegramProcess = hasManagedTelegramProcess;
37
+ exports.hasManagedSupercronicProcess = hasManagedSupercronicProcess;
38
+ exports.readContainerRuntimePolicy = readContainerRuntimePolicy;
39
+ const fs = __importStar(require("node:fs"));
40
+ const runtime_1 = require("../../nerves/runtime");
41
+ function hasManagedTelegramProcess(processArguments, agent) {
42
+ const wanted = agent.trim();
43
+ if (!wanted)
44
+ return false;
45
+ const matches = processArguments.split("\n").filter((line) => {
46
+ const fields = line.trim().split(/\s+/u);
47
+ const entryIndex = fields.findIndex((field) => field === "/opt/ouro/dist/senses/telegram-entry.js");
48
+ const agentIndex = fields.findIndex((field) => field === "--agent");
49
+ return entryIndex >= 0 && agentIndex >= 0 && fields[agentIndex + 1] === wanted;
50
+ });
51
+ return matches.length === 1;
52
+ }
53
+ function hasManagedSupercronicProcess(processArguments, agent) {
54
+ const expected = [
55
+ "/usr/local/bin/supercronic",
56
+ "-split-logs",
57
+ "-inotify",
58
+ `/home/ouro/.ouro-cli/scheduler/${agent}.crontab`,
59
+ ];
60
+ const matches = processArguments.split("\n").filter((line) => {
61
+ const fields = line.trim().split(/\s+/u);
62
+ return fields.length === expected.length && fields.every((field, index) => field === expected[index]);
63
+ });
64
+ return matches.length === 1;
65
+ }
66
+ function readContainerRuntimePolicy(options = {}) {
67
+ const filePath = options.path ?? "/opt/ouro/container-runtime.json";
68
+ const readFile = options.readFile ?? ((target) => fs.readFileSync(target, "utf8"));
69
+ let raw;
70
+ try {
71
+ raw = readFile(filePath);
72
+ }
73
+ catch (error) {
74
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
75
+ return null;
76
+ throw error;
77
+ }
78
+ const value = JSON.parse(raw);
79
+ if (value.scheduler !== "supercronic" || value.updates !== "disabled" || Object.keys(value).sort().join(",") !== "scheduler,updates") {
80
+ throw new Error("container runtime policy is invalid");
81
+ }
82
+ (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.container_runtime_policy_loaded", message: "container runtime policy loaded", meta: { scheduler: value.scheduler, updates: value.updates } });
83
+ return { scheduler: "supercronic", updates: "disabled" };
84
+ }
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runContainerSpecAuditorCli = runContainerSpecAuditorCli;
37
+ exports.runContainerSpecAuditorMain = runContainerSpecAuditorMain;
38
+ const fs = __importStar(require("node:fs"));
39
+ const runtime_1 = require("../../nerves/runtime");
40
+ const container_spec_auditor_1 = require("./container-spec-auditor");
41
+ function parseModeArguments(args, flags) {
42
+ if (args.length !== flags.length * 2)
43
+ return null;
44
+ const allowed = new Set(flags);
45
+ const parsed = {};
46
+ for (let index = 0; index < args.length; index += 2) {
47
+ const flag = args[index];
48
+ const value = args[index + 1];
49
+ if (!allowed.has(flag) || Object.prototype.hasOwnProperty.call(parsed, flag) || !value)
50
+ return null;
51
+ parsed[flag] = value;
52
+ }
53
+ return parsed;
54
+ }
55
+ function parseSingleInspect(raw) {
56
+ try {
57
+ const value = JSON.parse(raw);
58
+ if (!Array.isArray(value) || value.length !== 1)
59
+ return null;
60
+ const item = value[0];
61
+ if (!item || typeof item !== "object" || Array.isArray(item))
62
+ return null;
63
+ return item;
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ }
69
+ function runContainerSpecAuditorCli(args, deps = {}) {
70
+ const readFile = deps.readFile ?? ((filePath) => fs.readFileSync(filePath, "utf8"));
71
+ const write = deps.write ?? ((text) => process.stdout.write(text));
72
+ const staged = parseModeArguments(args, ["--template", "--runtime-policy", "--expected-image"]);
73
+ const effective = parseModeArguments(args, ["--inspect", "--image-inspect", "--expected-image"]);
74
+ if (!staged && !effective) {
75
+ write(JSON.stringify({ ok: false, error: "usage: staged --template <path> --runtime-policy <path> --expected-image <id>; effective --inspect <path> --image-inspect <path> --expected-image <id>" }) + "\n");
76
+ (0, runtime_1.emitNervesEvent)({
77
+ level: "error",
78
+ component: "daemon",
79
+ event: "daemon.container_spec_auditor_cli_error",
80
+ message: "container spec auditor arguments were invalid",
81
+ meta: { argumentCount: args.length },
82
+ });
83
+ return 2;
84
+ }
85
+ if (effective) {
86
+ let containerText;
87
+ let imageText;
88
+ try {
89
+ containerText = readFile(effective["--inspect"]);
90
+ imageText = readFile(effective["--image-inspect"]);
91
+ }
92
+ catch (error) {
93
+ write(JSON.stringify({ ok: false, error: "effective audit inputs are unreadable" }) + "\n");
94
+ (0, runtime_1.emitNervesEvent)({
95
+ level: "error",
96
+ component: "daemon",
97
+ event: "daemon.container_spec_auditor_cli_error",
98
+ message: "container spec auditor could not read effective inputs",
99
+ meta: { reason: error instanceof Error ? error.message : String(error) },
100
+ });
101
+ return 2;
102
+ }
103
+ const containerInspect = parseSingleInspect(containerText);
104
+ const imageInspect = parseSingleInspect(imageText);
105
+ const imageConfig = imageInspect?.Config;
106
+ const expectedEnvironment = imageConfig && typeof imageConfig === "object" && !Array.isArray(imageConfig)
107
+ ? imageConfig.Env
108
+ : null;
109
+ if (!containerInspect || !imageInspect || !Array.isArray(expectedEnvironment) || !expectedEnvironment.every((entry) => typeof entry === "string")) {
110
+ write(JSON.stringify({ ok: false, error: "effective audit inputs must each contain exactly one canonical inspect record" }) + "\n");
111
+ (0, runtime_1.emitNervesEvent)({
112
+ level: "error",
113
+ component: "daemon",
114
+ event: "daemon.container_spec_auditor_cli_error",
115
+ message: "container spec auditor effective inputs were invalid",
116
+ meta: { containerValid: !!containerInspect, imageValid: !!imageInspect },
117
+ });
118
+ return 2;
119
+ }
120
+ const result = (0, container_spec_auditor_1.auditSanctuaryContainerSpec)(containerInspect, {
121
+ expectedImage: effective["--expected-image"],
122
+ expectedEnvironment,
123
+ });
124
+ if (imageInspect.Id !== effective["--expected-image"]) {
125
+ result.ok = false;
126
+ result.violations.unshift("reviewed image inspect identity does not match the expected local image ID");
127
+ }
128
+ write(JSON.stringify(result) + "\n");
129
+ return result.ok ? 0 : 1;
130
+ }
131
+ let templateXml;
132
+ let runtimePolicyText;
133
+ try {
134
+ templateXml = readFile(staged["--template"]);
135
+ runtimePolicyText = readFile(staged["--runtime-policy"]);
136
+ }
137
+ catch (error) {
138
+ write(JSON.stringify({ ok: false, error: "staged audit inputs are unreadable" }) + "\n");
139
+ (0, runtime_1.emitNervesEvent)({
140
+ level: "error",
141
+ component: "daemon",
142
+ event: "daemon.container_spec_auditor_cli_error",
143
+ message: "container spec auditor could not read staged files",
144
+ meta: { reason: error instanceof Error ? error.message : String(error) },
145
+ });
146
+ return 2;
147
+ }
148
+ const result = (0, container_spec_auditor_1.auditSanctuaryStagedFiles)({ templateXml, runtimePolicyText, expectedImage: staged["--expected-image"] });
149
+ write(JSON.stringify(result) + "\n");
150
+ return result.ok ? 0 : 1;
151
+ }
152
+ function runContainerSpecAuditorMain(isMain, args, runner) {
153
+ if (isMain)
154
+ process.exitCode = runner(args);
155
+ }
156
+ runContainerSpecAuditorMain(require.main === module, process.argv.slice(2), runContainerSpecAuditorCli);