@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.
- package/changelog.json +34 -0
- package/deploy/unraid/Dockerfile +27 -0
- package/deploy/unraid/README.txt +1605 -0
- package/deploy/unraid/audit-container-spec.sh +4 -0
- package/deploy/unraid/container-runtime.json +1 -0
- package/deploy/unraid/sanctuary-acceptance-adapter.sh +37 -0
- package/deploy/unraid/sanctuary-acceptance-contract.json +200 -0
- package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
- package/deploy/unraid/sanctuary-deployment-target.mjs +705 -0
- package/deploy/unraid/sanctuary-safe-rename.py +24 -0
- package/deploy/unraid/sanctuary-unit16-host-broker.mjs +1777 -0
- package/deploy/unraid/sanctuary-unit16-run.sh +502 -0
- package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
- package/deploy/unraid/sanctuary.ouro/agent.json +24 -0
- package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
- package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
- package/deploy/unraid/sanctuary.ouro/provider-readiness.json +18 -0
- package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +5 -0
- package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +7 -0
- package/deploy/unraid/sanctuary.ouro/tool-profiles.json +21 -0
- package/deploy/unraid/sanctuary.xml +25 -0
- package/dist/heart/approval-files.js +113 -0
- package/dist/heart/approval-store.js +168 -9
- package/dist/heart/auth/auth-flow.js +17 -1
- package/dist/heart/autonomy-budget.js +44 -1
- package/dist/heart/awaiting/await-scheduler.js +6 -2
- package/dist/heart/config.js +8 -0
- package/dist/heart/core.js +117 -132
- package/dist/heart/daemon/cli-exec.js +73 -1
- package/dist/heart/daemon/cli-help.js +5 -0
- package/dist/heart/daemon/cli-parse.js +6 -4
- package/dist/heart/daemon/container-credential-bootstrap.js +208 -0
- package/dist/heart/daemon/container-healthcheck.js +94 -0
- package/dist/heart/daemon/container-runtime.js +84 -0
- package/dist/heart/daemon/container-spec-auditor-main.js +156 -0
- package/dist/heart/daemon/container-spec-auditor.js +156 -0
- package/dist/heart/daemon/daemon-bootstrap-startup.js +41 -0
- package/dist/heart/daemon/daemon-entry.js +116 -28
- package/dist/heart/daemon/daemon-health.js +10 -0
- package/dist/heart/daemon/daemon.js +92 -5
- package/dist/heart/daemon/runtime-logging.js +6 -0
- package/dist/heart/daemon/runtime-mode.js +13 -2
- package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2365 -0
- package/dist/heart/daemon/sanctuary-acceptance-harness.js +1624 -0
- package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
- package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +985 -0
- package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
- package/dist/heart/daemon/sanctuary-scheduler-origin.js +126 -0
- package/dist/heart/daemon/sense-manager.js +25 -6
- package/dist/heart/daemon/supercronic-supervisor.js +267 -0
- package/dist/heart/habits/habit-scheduler.js +7 -3
- package/dist/heart/identity.js +13 -3
- package/dist/heart/provider-credentials.js +9 -1
- package/dist/heart/provider-models.js +8 -0
- package/dist/heart/provider-ping.js +5 -2
- package/dist/heart/providers/openai-compatible.js +177 -0
- package/dist/heart/runtime-credentials.js +217 -8
- package/dist/heart/sense-truth.js +2 -0
- package/dist/heart/tool-approval.js +1 -0
- package/dist/heart/turn-context.js +8 -0
- package/dist/mind/prompt.js +9 -0
- package/dist/nerves/coverage/source-scanner.js +4 -4
- package/dist/nerves/index.js +75 -9
- package/dist/nerves/runtime.js +12 -0
- package/dist/repertoire/tool-arguments.js +39 -0
- package/dist/repertoire/tools-unraid.js +353 -0
- package/dist/repertoire/tools.js +19 -1
- package/dist/repertoire/unraid-client.js +155 -0
- package/dist/repertoire/unraid-restart.js +130 -0
- package/dist/senses/private-runtime-worker.js +3 -2
- package/dist/senses/sanctuary-grounding.js +118 -0
- package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
- package/dist/senses/sanctuary-health-acceptance-probe.js +772 -0
- package/dist/senses/sanctuary-health-runner.js +146 -0
- package/dist/senses/sanctuary-health.js +447 -0
- package/dist/senses/sanctuary-interactive-control.js +306 -0
- package/dist/senses/sanctuary-runtime.js +206 -0
- package/dist/senses/shared-turn.js +11 -3
- package/dist/senses/telegram-approval-runtime.js +526 -0
- package/dist/senses/telegram-audit-ledger.js +220 -0
- package/dist/senses/telegram-client.js +1471 -0
- package/dist/senses/telegram-entry.js +80 -0
- package/dist/senses/telegram.js +1232 -0
- package/npm-shrinkwrap.json +6558 -0
- package/package.json +4 -2
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SANCTUARY_RESTART_MUTATION = void 0;
|
|
4
|
+
exports.createApprovedUnraidRestartExecutor = createApprovedUnraidRestartExecutor;
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const runtime_1 = require("../nerves/runtime");
|
|
7
|
+
const unraid_client_1 = require("./unraid-client");
|
|
8
|
+
exports.SANCTUARY_RESTART_MUTATION = `mutation SanctuaryRestart($id: PrefixedID!) {
|
|
9
|
+
docker { restart(id: $id) { id names state status autoStart } }
|
|
10
|
+
}`;
|
|
11
|
+
function failure(code, message) {
|
|
12
|
+
return { ok: false, error: { code, message, degraded: true } };
|
|
13
|
+
}
|
|
14
|
+
function exactTarget(result, name) {
|
|
15
|
+
if (!result.ok)
|
|
16
|
+
return failure("invalid_response", result.error.message);
|
|
17
|
+
const matches = result.data.containers.filter((container) => container.name === name);
|
|
18
|
+
if (matches.length === 0)
|
|
19
|
+
return failure("not_found", `container '${name}' was not found`);
|
|
20
|
+
if (matches.length !== 1)
|
|
21
|
+
return failure("ambiguous", `container '${name}' is ambiguous`);
|
|
22
|
+
return matches[0];
|
|
23
|
+
}
|
|
24
|
+
function validArgument(value) {
|
|
25
|
+
return typeof value === "string" && value.length > 0 && Buffer.byteLength(value, "utf8") <= 128 && !value.includes("\uFFFD");
|
|
26
|
+
}
|
|
27
|
+
function acknowledgedIdentity(data, target) {
|
|
28
|
+
const docker = data.docker;
|
|
29
|
+
if (!docker || typeof docker !== "object" || Array.isArray(docker))
|
|
30
|
+
return false;
|
|
31
|
+
const restart = docker.restart;
|
|
32
|
+
if (!restart || typeof restart !== "object" || Array.isArray(restart))
|
|
33
|
+
return false;
|
|
34
|
+
const record = restart;
|
|
35
|
+
return record.id === target.id && Array.isArray(record.names)
|
|
36
|
+
&& record.names.length === 1 && String(record.names[0]).replace(/^\//u, "") === target.name;
|
|
37
|
+
}
|
|
38
|
+
function createApprovedUnraidRestartExecutor(options) {
|
|
39
|
+
const createClient = options.createClient ?? ((input) => new unraid_client_1.UnraidClient(input));
|
|
40
|
+
const sleep = options.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
41
|
+
const now = options.now ?? (() => new Date());
|
|
42
|
+
const observationTimeoutMs = options.observationTimeoutMs ?? 30_000;
|
|
43
|
+
return async (args) => {
|
|
44
|
+
if (!validArgument(args.container))
|
|
45
|
+
return failure("invalid_response", "container must be one bounded exact name");
|
|
46
|
+
const resolved = exactTarget(await options.listContainers(), args.container);
|
|
47
|
+
if ("ok" in resolved)
|
|
48
|
+
return resolved;
|
|
49
|
+
const fresh = exactTarget(await options.listContainers(), args.container);
|
|
50
|
+
if ("ok" in fresh)
|
|
51
|
+
return fresh;
|
|
52
|
+
if (fresh.id !== resolved.id)
|
|
53
|
+
return failure("stale_target", "container identity changed before restart");
|
|
54
|
+
const scenarioHandleDigest = options.acceptanceScenarioHandleDigest?.();
|
|
55
|
+
const approval = options.acceptanceApproval?.();
|
|
56
|
+
const attempt = {
|
|
57
|
+
container: { id: fresh.id, name: fresh.name },
|
|
58
|
+
beforeState: fresh.state,
|
|
59
|
+
observedAt: now().toISOString(),
|
|
60
|
+
actionDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ operation: "restart", container: { id: fresh.id, name: fresh.name } })).digest("hex"),
|
|
61
|
+
argumentDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ container: args.container })).digest("hex"),
|
|
62
|
+
...(scenarioHandleDigest ? { scenarioHandleDigest } : {}),
|
|
63
|
+
...(approval ? { approvalId: approval.approvalId } : {}),
|
|
64
|
+
attemptId: (0, node_crypto_1.randomUUID)(),
|
|
65
|
+
mutationAcknowledged: false,
|
|
66
|
+
afterState: null,
|
|
67
|
+
};
|
|
68
|
+
if (approval && approval.argumentDigest !== attempt.argumentDigest)
|
|
69
|
+
return failure("stale_target", "approval arguments changed before restart");
|
|
70
|
+
await options.persistAttempt?.({ ...attempt, observedAt: now().toISOString(), state: "attempt_not_started" });
|
|
71
|
+
const apiKey = await options.loadWriteApiKey();
|
|
72
|
+
if (!apiKey.trim())
|
|
73
|
+
return failure("invalid_response", "Unraid write credential is unavailable");
|
|
74
|
+
const client = createClient({ endpoint: options.endpoint, apiKey });
|
|
75
|
+
await options.persistAttempt?.({ ...attempt, observedAt: now().toISOString(), state: "attempting" });
|
|
76
|
+
(0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.unraid_restart_start", message: "approved Unraid restart started", meta: { containerId: fresh.id, containerName: fresh.name } });
|
|
77
|
+
const persistTerminal = async (terminal) => {
|
|
78
|
+
try {
|
|
79
|
+
await options.persistAttempt?.(terminal);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", component: "repertoire", event: "repertoire.unraid_restart_error", message: "approved Unraid restart terminal receipt persistence failed", meta: { containerId: fresh.id, category: error instanceof Error ? error.name : "unknown" } });
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
let acknowledged = false;
|
|
88
|
+
try {
|
|
89
|
+
const mutation = await client.mutate(exports.SANCTUARY_RESTART_MUTATION, { id: fresh.id });
|
|
90
|
+
acknowledged = acknowledgedIdentity(mutation, fresh);
|
|
91
|
+
if (!acknowledged)
|
|
92
|
+
throw new Error("restart response identity was invalid");
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
acknowledged = false;
|
|
96
|
+
}
|
|
97
|
+
const startedAt = now().getTime();
|
|
98
|
+
let sawRestarting = false;
|
|
99
|
+
try {
|
|
100
|
+
do {
|
|
101
|
+
const observed = exactTarget(await options.listContainers(), args.container);
|
|
102
|
+
if (!("ok" in observed)) {
|
|
103
|
+
if (observed.id !== fresh.id) {
|
|
104
|
+
await persistTerminal({ ...attempt, observedAt: now().toISOString(), state: "attempted_or_indeterminate", mutationAcknowledged: acknowledged });
|
|
105
|
+
return failure("ambiguous", "container identity changed after restart attempt");
|
|
106
|
+
}
|
|
107
|
+
sawRestarting ||= observed.state === "restarting";
|
|
108
|
+
if (observed.state === "running" && !observed.degraded && (acknowledged || sawRestarting)) {
|
|
109
|
+
if (!await persistTerminal({ ...attempt, observedAt: now().toISOString(), state: "succeeded", mutationAcknowledged: acknowledged, afterState: observed.state })) {
|
|
110
|
+
return failure("ambiguous", "restart succeeded but its terminal receipt could not be persisted; it was not retried");
|
|
111
|
+
}
|
|
112
|
+
(0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.unraid_restart_end", message: "approved Unraid restart completed", meta: { containerId: fresh.id, observedRestart: true } });
|
|
113
|
+
return { ok: true, data: { container: { id: fresh.id, name: fresh.name }, beforeState: fresh.state, afterState: observed.state, observedRestart: true, degraded: false } };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (now().getTime() - startedAt >= observationTimeoutMs)
|
|
117
|
+
break;
|
|
118
|
+
await sleep(1_000);
|
|
119
|
+
} while (true);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
await persistTerminal({ ...attempt, observedAt: now().toISOString(), state: "attempted_or_indeterminate", mutationAcknowledged: acknowledged });
|
|
123
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", component: "repertoire", event: "repertoire.unraid_restart_error", message: "approved Unraid restart observation failed", meta: { containerId: fresh.id, acknowledged } });
|
|
124
|
+
return failure("ambiguous", "restart was attempted but observation failed; it was not retried");
|
|
125
|
+
}
|
|
126
|
+
await persistTerminal({ ...attempt, observedAt: now().toISOString(), state: "attempted_or_indeterminate", mutationAcknowledged: acknowledged });
|
|
127
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", component: "repertoire", event: "repertoire.unraid_restart_error", message: "approved Unraid restart outcome was ambiguous", meta: { containerId: fresh.id, acknowledged } });
|
|
128
|
+
return failure("ambiguous", "restart was attempted but could not be proven; it was not retried");
|
|
129
|
+
};
|
|
130
|
+
}
|
|
@@ -146,15 +146,16 @@ function reserveHabitAutonomyBudget(habitRun, nowIso) {
|
|
|
146
146
|
target.rsvpBudgetRef = habitRun.rsvpPolicy.budgetRef;
|
|
147
147
|
target.rsvpIdempotencyRef = habitRun.rsvpPolicy.idempotencyRef;
|
|
148
148
|
}
|
|
149
|
+
const agentName = (0, identity_1.getAgentName)();
|
|
149
150
|
return (0, autonomy_budget_1.reserveAutonomyBudget)(habitRun.agentRoot, {
|
|
150
|
-
agent:
|
|
151
|
+
agent: agentName,
|
|
151
152
|
triggerType: "habit",
|
|
152
153
|
sourceKind: "private-runtime",
|
|
153
154
|
senseOrHabit: habitRun.habit.name,
|
|
154
155
|
target,
|
|
155
156
|
idempotencyKey: `habit:${habitRun.habit.name}:${habitRun.runId}`,
|
|
156
157
|
now: nowIso,
|
|
157
|
-
});
|
|
158
|
+
}, (0, autonomy_budget_1.resolveAutonomyBudgetPolicy)(habitRun.agentRoot, agentName));
|
|
158
159
|
}
|
|
159
160
|
/**
|
|
160
161
|
* Cap on consecutive `instinct` follow-on turns triggered by `hasPendingWork()`
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sanctuaryGroundingDigest = sanctuaryGroundingDigest;
|
|
4
|
+
exports.projectSanctuaryGrounding = projectSanctuaryGrounding;
|
|
5
|
+
exports.renderSanctuaryGroundedResponse = renderSanctuaryGroundedResponse;
|
|
6
|
+
exports.sanctuaryGroundedResponseAccurate = sanctuaryGroundedResponseAccurate;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const runtime_1 = require("../nerves/runtime");
|
|
9
|
+
function object(value, label) {
|
|
10
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
11
|
+
throw new Error(`${label} must be an object`);
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function sanctuaryGroundingDigest(facts) {
|
|
15
|
+
return (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(facts)).digest("hex");
|
|
16
|
+
}
|
|
17
|
+
function projectSanctuaryGrounding(toolName, result) {
|
|
18
|
+
if (toolName !== "unraid_get_system" && toolName !== "unraid_get_storage")
|
|
19
|
+
return null;
|
|
20
|
+
const envelope = object(result, "Sanctuary grounding result");
|
|
21
|
+
if (envelope.ok !== true)
|
|
22
|
+
throw new Error("Sanctuary grounding result must succeed");
|
|
23
|
+
const data = object(envelope.data, "Sanctuary grounding data");
|
|
24
|
+
if (toolName === "unraid_get_system") {
|
|
25
|
+
const exact = ["apiVersion", "arrayState", "degraded", "serverName", "sourceIdentityDigest", "unraidVersion", "uptimeSeconds"];
|
|
26
|
+
if (JSON.stringify(Object.keys(data).sort()) !== JSON.stringify(exact))
|
|
27
|
+
throw new Error("Sanctuary system grounding shape is invalid");
|
|
28
|
+
for (const key of ["serverName", "unraidVersion", "apiVersion", "arrayState"])
|
|
29
|
+
if (typeof data[key] !== "string" || !data[key])
|
|
30
|
+
throw new Error("Sanctuary system grounding value is invalid");
|
|
31
|
+
if (typeof data.degraded !== "boolean" || (data.uptimeSeconds !== null && (!Number.isSafeInteger(data.uptimeSeconds) || Number(data.uptimeSeconds) < 0)))
|
|
32
|
+
throw new Error("Sanctuary system grounding value is invalid");
|
|
33
|
+
const facts = { serverName: data.serverName, unraidVersion: data.unraidVersion, apiVersion: data.apiVersion, arrayState: data.arrayState, degraded: data.degraded };
|
|
34
|
+
(0, runtime_1.emitNervesEvent)({ component: "senses", event: "senses.sanctuary_grounding_projected", message: "Sanctuary grounding facts projected", meta: { toolName } });
|
|
35
|
+
return facts;
|
|
36
|
+
}
|
|
37
|
+
const exact = ["array", "shares", "sourceIdentityDigest", "truncated"];
|
|
38
|
+
if (JSON.stringify(Object.keys(data).sort()) !== JSON.stringify(exact) || !Array.isArray(data.shares) || typeof data.truncated !== "boolean")
|
|
39
|
+
throw new Error("Sanctuary storage grounding shape is invalid");
|
|
40
|
+
const array = object(data.array, "Sanctuary storage array grounding");
|
|
41
|
+
if (JSON.stringify(Object.keys(array).sort()) !== JSON.stringify(["degraded", "freeBytes", "state", "usedBytes", "usedPercent"]))
|
|
42
|
+
throw new Error("Sanctuary storage array grounding shape is invalid");
|
|
43
|
+
if (typeof array.state !== "string" || !array.state || typeof array.degraded !== "boolean"
|
|
44
|
+
|| ![array.usedBytes, array.freeBytes].every((value) => value === null || (Number.isSafeInteger(value) && Number(value) >= 0))
|
|
45
|
+
|| (array.usedPercent !== null && (typeof array.usedPercent !== "number" || !Number.isFinite(array.usedPercent))))
|
|
46
|
+
throw new Error("Sanctuary storage array grounding value is invalid");
|
|
47
|
+
const { sourceIdentityDigest: _sourceIdentityDigest, ...facts } = data;
|
|
48
|
+
(0, runtime_1.emitNervesEvent)({ component: "senses", event: "senses.sanctuary_grounding_projected", message: "Sanctuary grounding facts projected", meta: { toolName } });
|
|
49
|
+
return structuredClone(facts);
|
|
50
|
+
}
|
|
51
|
+
function normalized(value) {
|
|
52
|
+
return value.normalize("NFKC").toLocaleLowerCase("en-US");
|
|
53
|
+
}
|
|
54
|
+
function regexEscape(value) {
|
|
55
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
56
|
+
}
|
|
57
|
+
function systemResponseAccurate(facts, response) {
|
|
58
|
+
if (typeof facts.serverName !== "string" || typeof facts.unraidVersion !== "string" || typeof facts.arrayState !== "string" || typeof facts.degraded !== "boolean")
|
|
59
|
+
return false;
|
|
60
|
+
const pattern = new RegExp(`^${regexEscape(normalized(facts.serverName))} is running unraid ${regexEscape(normalized(facts.unraidVersion))}(?:; the array is| with the array) ${regexEscape(normalized(facts.arrayState))} and (not degraded|healthy|nominal|degraded|unhealthy)\\.?$`, "u");
|
|
61
|
+
const match = pattern.exec(response);
|
|
62
|
+
if (!match)
|
|
63
|
+
return false;
|
|
64
|
+
return facts.degraded ? match[1] === "degraded" || match[1] === "unhealthy" : match[1] === "not degraded" || match[1] === "healthy" || match[1] === "nominal";
|
|
65
|
+
}
|
|
66
|
+
function byteAmount(amount, unit) {
|
|
67
|
+
const divisors = { tb: 1_000_000_000_000, tib: 1_099_511_627_776, gb: 1_000_000_000, gib: 1_073_741_824, byte: 1, bytes: 1 };
|
|
68
|
+
return Number(amount) * divisors[unit];
|
|
69
|
+
}
|
|
70
|
+
function storageResponseAccurate(facts, response) {
|
|
71
|
+
const array = object(facts.array, "Sanctuary response storage facts");
|
|
72
|
+
if (typeof array.freeBytes !== "number" || typeof array.usedPercent !== "number" || typeof array.state !== "string" || typeof array.degraded !== "boolean")
|
|
73
|
+
return false;
|
|
74
|
+
const pattern = /^(?:there is|about) (\d+(?:\.\d+)?)\s*(tb|tib|gb|gib|bytes?) (?:is )?(?:available|free|left)(?:;|,| and) (?:(?:the array is )(\d+(?:\.\d+)?)\s*%\s*(?:used|full)|(?:usage is )(\d+(?:\.\d+)?)\s*%)\.(?: the array is ([\p{L}\p{N}_-]+) and (not degraded|healthy|nominal|degraded|unhealthy)\.)?$/u;
|
|
75
|
+
const match = pattern.exec(response);
|
|
76
|
+
if (!match)
|
|
77
|
+
return false;
|
|
78
|
+
const bytesAccurate = Math.abs(byteAmount(match[1], match[2]) - array.freeBytes) <= Math.max(1, array.freeBytes * 0.025);
|
|
79
|
+
const percentAccurate = Math.abs(Number(match[3] ?? match[4]) - array.usedPercent) <= 0.51;
|
|
80
|
+
if (!bytesAccurate || !percentAccurate)
|
|
81
|
+
return false;
|
|
82
|
+
if (match[5] === undefined && match[6] === undefined)
|
|
83
|
+
return true;
|
|
84
|
+
const healthAccurate = array.degraded ? match[6] === "degraded" || match[6] === "unhealthy" : match[6] === "not degraded" || match[6] === "healthy" || match[6] === "nominal";
|
|
85
|
+
return normalized(String(match[5])) === normalized(array.state) && healthAccurate;
|
|
86
|
+
}
|
|
87
|
+
function compactNumber(value) {
|
|
88
|
+
return value.toFixed(2).replace(/\.0+$/u, "").replace(/(\.\d*?)0+$/u, "$1");
|
|
89
|
+
}
|
|
90
|
+
function renderSanctuaryGroundedResponse(toolName, facts) {
|
|
91
|
+
let response;
|
|
92
|
+
if (toolName === "unraid_get_system") {
|
|
93
|
+
if (typeof facts.serverName !== "string" || typeof facts.unraidVersion !== "string" || typeof facts.arrayState !== "string" || typeof facts.degraded !== "boolean")
|
|
94
|
+
throw new Error("Sanctuary system response facts are invalid");
|
|
95
|
+
response = `${facts.serverName} is running Unraid ${facts.unraidVersion} with the array ${facts.arrayState} and ${facts.degraded ? "degraded" : "not degraded"}.`;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
const array = object(facts.array, "Sanctuary storage response facts");
|
|
99
|
+
if (typeof array.freeBytes !== "number" || typeof array.usedPercent !== "number")
|
|
100
|
+
throw new Error("Sanctuary storage response facts are invalid");
|
|
101
|
+
const units = [[1_000_000_000_000, "TB"], [1_000_000_000, "GB"], [1, "bytes"]];
|
|
102
|
+
const freeBytes = Number(array.freeBytes);
|
|
103
|
+
const usedPercent = Number(array.usedPercent);
|
|
104
|
+
const [divisor, unit] = units.find(([candidate]) => freeBytes >= candidate) ?? units[units.length - 1];
|
|
105
|
+
response = `There is ${compactNumber(freeBytes / divisor)} ${unit} free and the array is ${compactNumber(usedPercent)}% used.`;
|
|
106
|
+
}
|
|
107
|
+
(0, runtime_1.emitNervesEvent)({
|
|
108
|
+
component: "senses",
|
|
109
|
+
event: "senses.sanctuary_grounded_response_rendered",
|
|
110
|
+
message: "Rendered a deterministic Sanctuary response from grounded facts",
|
|
111
|
+
meta: { toolName },
|
|
112
|
+
});
|
|
113
|
+
return response;
|
|
114
|
+
}
|
|
115
|
+
function sanctuaryGroundedResponseAccurate(toolName, facts, responseText) {
|
|
116
|
+
const response = normalized(responseText);
|
|
117
|
+
return toolName === "unraid_get_system" ? systemResponseAccurate(facts, response) : storageResponseAccurate(facts, response);
|
|
118
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const sanctuary_health_acceptance_probe_1 = require("./sanctuary-health-acceptance-probe");
|
|
5
|
+
const runtime_1 = require("../nerves/runtime");
|
|
6
|
+
(0, runtime_1.emitNervesEvent)({
|
|
7
|
+
component: "senses",
|
|
8
|
+
event: "senses.entry_boot",
|
|
9
|
+
message: "booting Sanctuary health acceptance probe entrypoint",
|
|
10
|
+
meta: { entry: "sanctuary-health-acceptance-probe" },
|
|
11
|
+
});
|
|
12
|
+
(0, sanctuary_health_acceptance_probe_1.startSanctuaryHealthAcceptanceProbeCli)(process.argv.slice(2), (0, sanctuary_health_acceptance_probe_1.createSanctuaryHealthAcceptanceProbeCliOutput)());
|