total-recall-brain 3.44.0 → 3.45.0
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/bin/total-recall.mjs +6 -4
- package/docs/ARCHITECTURE.md +10 -7
- package/docs/reference/cli-reference.md +13 -8
- package/frontend/dist/assets/index-DACh1dhx.js +5613 -0
- package/frontend/dist/index.html +1 -1
- package/frontend/src/App.tsx +2 -2
- package/frontend/src/pages/PluginsPage.tsx +1 -1
- package/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +20 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-store.mjs +12 -1
- package/scaffold/.agent/skills/total-recall/plugins/automations/BRICK.md +7 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/README.md +66 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/adapters.mjs +231 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/capability/index.mjs +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/cli.mjs +121 -0
- package/{src/core/plugin-cron.mjs → scaffold/.agent/skills/total-recall/plugins/automations/cron.mjs} +1 -3
- package/scaffold/.agent/skills/total-recall/plugins/automations/inventory.mjs +88 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/package-lock.json +7661 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/package.json +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/plugin.json +167 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/scheduler.mjs +246 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/DESIGN.md +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/automations.js +2 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/card.js +273 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +57 -5
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report-data.mjs +40 -14
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report.mjs +15 -40
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/git/README.md +112 -0
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/cli.mjs +135 -23
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/git/plugin.json +161 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/context.mjs +94 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/phase3-spec-support.mjs +50 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/runtime.mjs +40 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/spec-support.mjs +51 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/_support.mjs +195 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/auth.mjs +201 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/bisect.mjs +40 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/cli-meta.mjs +159 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit-memory.mjs +30 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit.mjs +46 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/fleet.mjs +156 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/force-push.mjs +58 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/group.mjs +119 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/handoff.mjs +59 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/message.mjs +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/resolve.mjs +60 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/rules.mjs +81 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/secret-firewall.mjs +66 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/undo.mjs +132 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/why.mjs +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/ui/card.js +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/README.md +25 -9
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/BRICK.md +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +18 -6
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +124 -24
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +147 -14
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +17 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/adoption.mjs +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +92 -18
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/control.mjs +145 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +42 -21
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch-request.mjs +4 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +33 -9
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +55 -11
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +26 -4
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/hermes.mjs +29 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/index.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/included-usage.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/jobs.mjs +84 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +3 -12
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/maintenance.mjs +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/mcp-server.mjs +228 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/process-manager.mjs +25 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reasoning.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/remote-operation.mjs +5 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/review.mjs +34 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reviews.mjs +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/claude.mjs +319 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/codex.mjs +387 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/result.mjs +67 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/resume.mjs +373 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/run.mjs +223 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session-jobs.mjs +72 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/setup.mjs +6 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/termination.mjs +16 -12
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/threads.mjs +32 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/usage-queue.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/verify-ssss.mjs +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +285 -40
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worktree.mjs +76 -9
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/DESIGN.md +5 -5
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +47 -18
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh-diagnostics.mjs +44 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +120 -113
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/references/operations.md +1 -1
- package/src/cli/agent.mjs +1 -1
- package/src/cli/command.mjs +41 -2
- package/src/cli/dream.mjs +93 -69
- package/src/cli/harness.mjs +4 -4
- package/src/cli/help.mjs +1 -1
- package/src/cli/init.mjs +0 -25
- package/src/cli/lessons.mjs +51 -0
- package/src/cli/memory-plugin.mjs +1 -1
- package/src/cli/plugin/copies.mjs +64 -14
- package/src/cli/plugin/create.mjs +6 -7
- package/src/cli/plugin/index.mjs +11 -7
- package/src/cli/plugin/install.mjs +10 -15
- package/src/cli/plugin/remove.mjs +1 -1
- package/src/cli/plugin/search.mjs +2 -2
- package/src/cli/preflight.mjs +19 -6
- package/src/cli/proposals.mjs +1 -1
- package/src/cli/recall.mjs +66 -23
- package/src/cli/remember.mjs +3 -14
- package/src/cli/server.mjs +18 -0
- package/src/cli/startup.mjs +1 -0
- package/src/cli/task.mjs +9 -2
- package/src/cli/update.mjs +6 -2
- package/src/core/access-ledger.mjs +78 -0
- package/src/core/agent-manager.mjs +2 -2
- package/src/core/brain-locate.mjs +15 -0
- package/src/core/brain-updates.mjs +31 -11
- package/src/core/claude-hooks.mjs +3 -1
- package/src/core/cleanup-task.mjs +15 -0
- package/src/core/cli-commands.mjs +2 -0
- package/src/core/cli-maintenance.mjs +4 -1
- package/src/core/cli-semantic-index.mjs +23 -3
- package/src/core/context-compiler.mjs +20 -0
- package/src/core/daemon-loop.mjs +39 -62
- package/src/core/default-plugins.mjs +1 -1
- package/src/core/dream.mjs +33 -105
- package/src/core/ensure-dependencies.mjs +148 -0
- package/src/core/fast-recall.mjs +2 -2
- package/src/core/field-evaluation.mjs +23 -0
- package/src/core/gate-mode.mjs +1 -1
- package/src/core/harness-hooks.mjs +14 -2
- package/src/core/lesson-hooks.mjs +74 -0
- package/src/core/lesson-memory.mjs +24 -0
- package/src/core/lesson-sessions.mjs +52 -0
- package/src/core/lessons.mjs +96 -0
- package/src/core/meta-harness.mjs +2 -2
- package/src/core/optimizer.mjs +66 -165
- package/src/core/plugin-contracts/card.mjs +3 -0
- package/src/core/plugin-loader.mjs +12 -3
- package/src/core/plugin-runtime.mjs +40 -1
- package/src/core/plugin-store.mjs +35 -23
- package/src/core/post-mortem.mjs +52 -36
- package/src/core/preflight-gate.mjs +23 -2
- package/src/core/proposal-applier.mjs +31 -2
- package/src/core/repo-sync.mjs +4 -6
- package/src/core/required-plugins.mjs +26 -4
- package/src/core/rule-length.mjs +23 -0
- package/src/core/runtime.mjs +1 -1
- package/src/core/scheduler.mjs +35 -43
- package/src/core/schema.mjs +1 -2
- package/src/core/search.mjs +4 -3
- package/src/core/server-service.mjs +148 -0
- package/src/core/session-watcher.mjs +19 -1
- package/src/core/skill-gap.mjs +16 -0
- package/src/core/ssss-kernel-bridge.mjs +32 -4
- package/src/core/startup-health.mjs +6 -1
- package/src/core/surface.mjs +65 -54
- package/src/core/task-executors.mjs +54 -106
- package/src/core/validated-write.mjs +1 -0
- package/src/core/vector-field.mjs +134 -192
- package/src/server/invoke-core-child.mjs +24 -0
- package/src/server/invoke-core-operations.mjs +102 -0
- package/src/server/route-manifest.json +0 -56
- package/src/server/routes/brains.mjs +0 -18
- package/src/server/routes/context.mjs +0 -41
- package/src/server/routes/invoke.mjs +26 -1
- package/src/server/routes/ssss.mjs +0 -169
- package/src/server/routes/system.mjs +0 -15
- package/src/server/routes/tasks.mjs +0 -26
- package/src/server/routes/vault.mjs +2 -23
- package/frontend/dist/assets/index-DD4qONvH.js +0 -5607
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +0 -53
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +0 -129
- package/src/core/append-log.mjs +0 -131
- package/src/core/plugin-tasks.mjs +0 -173
- package/templates/onboarding-interview.md +0 -82
- /package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/LICENSE +0 -0
- /package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/ui/DESIGN.md +0 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { meshAutomationInventory, filterInventory } from './inventory.mjs';
|
|
2
|
+
import { cancelAutomation, SOURCES } from './adapters.mjs';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
export const HELP = `Usage: total-recall automations <command> [options]
|
|
6
|
+
|
|
7
|
+
Commands:
|
|
8
|
+
list List automation records and source coverage across the mesh
|
|
9
|
+
status Report node/source coverage and observed automation states
|
|
10
|
+
show <id> Inspect one source-owned automation record
|
|
11
|
+
cancel <id> Cancel a pending local durable task through its owner
|
|
12
|
+
dream --all-brains Run dream, including decay, for every brain on this node
|
|
13
|
+
|
|
14
|
+
Options:
|
|
15
|
+
--local Inspect this node only (also used by authenticated peers)
|
|
16
|
+
--node <id|hostname> Select one known mesh node
|
|
17
|
+
--source <source> Filter records by source
|
|
18
|
+
--trigger <kind> Filter records by trigger kind
|
|
19
|
+
--state <state> Filter records by observed state
|
|
20
|
+
--json Machine-readable output (also the default)
|
|
21
|
+
--help, -h Show this help
|
|
22
|
+
|
|
23
|
+
Sources: ${SOURCES.join(', ')}
|
|
24
|
+
Declarations, runner availability and observed outcomes remain separate.
|
|
25
|
+
Unsupported or failed sources are reported explicitly; they are never empty success.`;
|
|
26
|
+
|
|
27
|
+
export function parseArgs(argv) {
|
|
28
|
+
const args = argv[2] === 'automations' ? argv.slice(3) : [...argv];
|
|
29
|
+
if (!args.length || args.includes('--help') || args.includes('-h')) return { help: true };
|
|
30
|
+
const command = args.shift();
|
|
31
|
+
if (!['list', 'status', 'show', 'cancel', 'dream'].includes(command)) throw new Error(`Unknown command: ${command}`);
|
|
32
|
+
if (command === 'dream') {
|
|
33
|
+
if (args.filter(arg => arg === '--all-brains').length !== 1 || args.some(arg => !['--all-brains', '--json'].includes(arg))) {
|
|
34
|
+
throw new Error('dream requires --all-brains and accepts only --json');
|
|
35
|
+
}
|
|
36
|
+
return { command };
|
|
37
|
+
}
|
|
38
|
+
const result = { command, filters: {}, localOnly: false };
|
|
39
|
+
if (['show', 'cancel'].includes(command)) {
|
|
40
|
+
result.id = args.shift(); if (!result.id || !/^[a-z-]+:[a-f0-9]{20}$/.test(result.id)) throw new Error('A valid automation id is required');
|
|
41
|
+
}
|
|
42
|
+
while (args.length) {
|
|
43
|
+
const flag = args.shift();
|
|
44
|
+
if (flag === '--json') continue;
|
|
45
|
+
if (flag === '--local') { result.localOnly = true; continue; }
|
|
46
|
+
const name = { '--node': 'node', '--source': 'source', '--trigger': 'trigger', '--state': 'state' }[flag];
|
|
47
|
+
if (!name) throw new Error(`Unknown option: ${flag}`);
|
|
48
|
+
const value = args.shift(); if (!value || value.startsWith('-')) throw new Error(`Missing value for ${flag}`);
|
|
49
|
+
if (result.filters[name]) throw new Error(`Duplicate option: ${flag}`);
|
|
50
|
+
result.filters[name] = value;
|
|
51
|
+
}
|
|
52
|
+
if (result.filters.source && !SOURCES.includes(result.filters.source)) throw new Error('Unknown source');
|
|
53
|
+
if (command === 'cancel' && (Object.keys(result.filters).length || result.localOnly)) throw new Error('cancel accepts only a local automation id and --json');
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Core owns dream execution. Check support before an old core can ignore the flag. */
|
|
58
|
+
export function runDream(spawn = spawnSync) {
|
|
59
|
+
const options = { shell: false, encoding: 'utf8', timeout: 30000, maxBuffer: 4 * 1024 * 1024, windowsHide: true };
|
|
60
|
+
const failure = (error, child = {}, results = []) => ({
|
|
61
|
+
ok: false, exit_code: Number.isInteger(child.status) && child.status !== 0 ? child.status : 1,
|
|
62
|
+
child_exit_code: child.status ?? null, signal: child.signal || null, results, error,
|
|
63
|
+
});
|
|
64
|
+
try {
|
|
65
|
+
const help = spawn('total-recall', ['dream', '--help'], options);
|
|
66
|
+
if (help.error || help.signal || help.status !== 0) return failure(`Cannot check core dream support: ${help.error?.message || help.stderr || help.signal || 'help failed'}`, help);
|
|
67
|
+
if (!String(help.stdout).includes('--all-brains')) return failure('Core dream does not support --all-brains. Update Total Recall before this task can run.', help);
|
|
68
|
+
const child = spawn('total-recall', ['dream', '--all-brains', '--json'], { ...options, timeout: 1800000 });
|
|
69
|
+
if (child.error || child.signal) return failure(`Core dream did not complete: ${child.error?.message || child.signal}`, child);
|
|
70
|
+
let report;
|
|
71
|
+
try {
|
|
72
|
+
report = JSON.parse(child.stdout);
|
|
73
|
+
} catch {
|
|
74
|
+
// Core log lines may surround one JSON report.
|
|
75
|
+
const reports = String(child.stdout || '').split('\n').flatMap(line => {
|
|
76
|
+
try { const value = JSON.parse(line); return Array.isArray(value) || Array.isArray(value?.results) || Array.isArray(value?.brains) ? [value] : []; } catch { return []; }
|
|
77
|
+
});
|
|
78
|
+
if (reports.length === 1) report = reports[0];
|
|
79
|
+
}
|
|
80
|
+
const results = Array.isArray(report) ? report : report?.results ?? report?.brains;
|
|
81
|
+
if (!Array.isArray(results) || !results.length || results.some(result => !result || typeof result !== 'object' || Array.isArray(result)
|
|
82
|
+
|| !(typeof result.ok === 'boolean' || typeof result.success === 'boolean' || ['ok', 'success', 'completed', 'failed', 'error', 'skipped'].includes(result.status) || ['ok', 'success', 'completed', 'failed', 'error', 'skipped'].includes(result.state)))) {
|
|
83
|
+
return failure(`Core dream returned no per-brain results (exit ${child.status}). Update Total Recall if --all-brains is unavailable. ${String(child.stderr || '').trim()}`.trim(), child);
|
|
84
|
+
}
|
|
85
|
+
if (child.status !== 0 || report?.ok === false || report?.success === false || results.some(result => result.ok === false || result.success === false || result.error
|
|
86
|
+
|| (Number.isInteger(result.exit_code) && result.exit_code !== 0) || ['failed', 'error'].includes(result.status) || ['failed', 'error'].includes(result.state))) {
|
|
87
|
+
return failure(`Core dream failed (exit ${child.status}). ${String(child.stderr || '').trim()}`.trim(), child, results);
|
|
88
|
+
}
|
|
89
|
+
return { ok: true, exit_code: 0, child_exit_code: 0, results };
|
|
90
|
+
} catch (error) {
|
|
91
|
+
return failure(`Cannot run core dream: ${error.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function run(argv = [], dependencies = {}) {
|
|
96
|
+
const parsed = parseArgs(argv), write = dependencies.write || (output => process.stdout.write(output));
|
|
97
|
+
if (parsed.help) { write(`${HELP}\n`); return; }
|
|
98
|
+
if (parsed.command === 'dream') {
|
|
99
|
+
const result = runDream(dependencies.spawnSync);
|
|
100
|
+
write(`${JSON.stringify(result)}\n`);
|
|
101
|
+
if (!result.ok) (dependencies.setExitCode || (code => { process.exitCode = code; }))(result.exit_code);
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
const root = dependencies.packageRoot || process.env.TR_PACKAGE_ROOT;
|
|
105
|
+
if (!root) throw new Error('TR_PACKAGE_ROOT is required');
|
|
106
|
+
if (parsed.command === 'cancel') {
|
|
107
|
+
const result = await (dependencies.cancel || cancelAutomation)(root, parsed.id);
|
|
108
|
+
write(`${JSON.stringify(result)}\n`); return result;
|
|
109
|
+
}
|
|
110
|
+
let inventory = await (dependencies.inventory || meshAutomationInventory)(root, { localOnly: parsed.localOnly });
|
|
111
|
+
inventory = filterInventory(inventory, parsed.filters);
|
|
112
|
+
let result = inventory;
|
|
113
|
+
if (parsed.command === 'show') {
|
|
114
|
+
const records = inventory.nodes.flatMap(node => node.automations).filter(record => record.id === parsed.id);
|
|
115
|
+
if (records.length !== 1) throw new Error(records.length ? 'Automation id is ambiguous' : 'Automation not found');
|
|
116
|
+
result = { schema_version: 1, checked_at: inventory.checked_at, automation: records[0] };
|
|
117
|
+
}
|
|
118
|
+
if (parsed.command === 'status') result = { ...inventory, nodes: inventory.nodes.map(node => ({ ...node, counts: node.automations.reduce((counts, record) => ({ ...counts, [record.state]: (counts[record.state] || 0) + 1 }), {}), automations: undefined })) };
|
|
119
|
+
write(`${JSON.stringify(result)}\n`); return result;
|
|
120
|
+
}
|
|
121
|
+
export default run;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Five-field cron parsing and matching for manifest plugin tasks. */
|
|
2
2
|
const FIELD_RANGES = [
|
|
3
3
|
[0, 59], // minute
|
|
4
4
|
[0, 23], // hour
|
|
@@ -53,5 +53,3 @@ export function cronMatches(cron, date) {
|
|
|
53
53
|
if (cron.dowStar) return domOk;
|
|
54
54
|
return domOk || dowOk;
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import { collectLocalAutomations, coreModule, SOURCES, scheduledTasks, recordId } from './adapters.mjs';
|
|
3
|
+
export { scheduledTasks } from './adapters.mjs';
|
|
4
|
+
|
|
5
|
+
const MAX_PEER_BYTES = 4 * 1024 * 1024;
|
|
6
|
+
export function parseInventoryOutput(output) {
|
|
7
|
+
if (typeof output !== 'string' || Buffer.byteLength(output) > MAX_PEER_BYTES) throw new Error('Invalid inventory output');
|
|
8
|
+
try { return JSON.parse(output); } catch {
|
|
9
|
+
// Host logs may surround command output. Accept one versioned inventory,
|
|
10
|
+
// never guess between multiple command results or parse a logger payload.
|
|
11
|
+
const values = output.trim().split('\n').flatMap(line => { try { const value = JSON.parse(line); return value?.schema_version === 1 && Array.isArray(value.nodes) ? [value] : []; } catch { return []; } });
|
|
12
|
+
if (values.length !== 1) throw new Error('Expected one versioned inventory result');
|
|
13
|
+
return values[0];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const unavailableCoverage = (status, checked_at) => SOURCES.map(source => ({ source, status, checked_at }));
|
|
17
|
+
function verifyLocalResult(value) {
|
|
18
|
+
if (!value || !Array.isArray(value.automations) || !Array.isArray(value.coverage)) throw new Error('Invalid local inventory');
|
|
19
|
+
if (value.automations.length > 10000 * SOURCES.length) throw new Error('Peer inventory exceeds record limit');
|
|
20
|
+
const statuses = new Set(['ok', 'partial', 'unsupported', 'disabled', 'error', 'unauthorized', 'offline']);
|
|
21
|
+
for (const coverage of value.coverage) if (!SOURCES.includes(coverage.source) || !statuses.has(coverage.status)) throw new Error('Invalid peer coverage');
|
|
22
|
+
for (const record of value.automations) if (typeof record.id !== 'string' || !SOURCES.includes(record.source) || typeof record.name !== 'string') throw new Error('Invalid peer automation');
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
function nodeResult(base, value) {
|
|
26
|
+
const missing = SOURCES.filter(source => !value.coverage.some(item => item.source === source));
|
|
27
|
+
const coverage = [...value.coverage, ...missing.map(source => ({ source, status: 'unsupported', checked_at: base.checked_at, error: 'Peer did not report this source' }))];
|
|
28
|
+
// Remote mutation is never advertised through a local-only control.
|
|
29
|
+
const automations = value.automations.map(record => ({ ...record, node_id: base.node_id, actions: base.self ? record.actions || [] : [] }));
|
|
30
|
+
return { ...base, status: coverage.every(source => ['ok', 'disabled'].includes(source.status)) ? 'ok' : 'partial', automations, coverage };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The plugin owns composition; every source and peer failure remains visible. */
|
|
34
|
+
export async function meshAutomationInventory(packageRoot, options = {}) {
|
|
35
|
+
const checked_at = new Date().toISOString();
|
|
36
|
+
const mesh = options.mesh || await coreModule(packageRoot, 'mesh');
|
|
37
|
+
const self = options.self || mesh.getMeshSelf?.() || { hostname: os.hostname(), self: true };
|
|
38
|
+
const selfId = self.ip || self.hostname || os.hostname();
|
|
39
|
+
let nodes = options.localOnly ? [{ ...self, self: true }] : options.nodes || mesh.listEnrichedMeshNodes();
|
|
40
|
+
if (!nodes.some(node => node.self || (node.ip && node.ip === self.ip))) nodes = [{ ...self, self: true }, ...nodes];
|
|
41
|
+
nodes = [...new Map(nodes.map(node => [node.ip || node.hostname, node])).values()];
|
|
42
|
+
const local = options.local || await collectLocalAutomations(packageRoot, { ...options, node_id: selfId });
|
|
43
|
+
const execPeer = options.execPeer || mesh.execMeshCommand;
|
|
44
|
+
let authorization = options.authorization;
|
|
45
|
+
if (!options.localOnly && authorization === undefined) {
|
|
46
|
+
try { authorization = await (await coreModule(packageRoot, 'mesh-auth')).getMeshSyncAuthorization(); } catch { authorization = null; }
|
|
47
|
+
}
|
|
48
|
+
const peerUrl = options.peerUrl || (await coreModule(packageRoot, 'plugin-peers')).meshPeerUrl;
|
|
49
|
+
const fetchImpl = options.fetchImpl || (await coreModule(packageRoot, 'throttled-fetch')).throttledFetch;
|
|
50
|
+
const records = await Promise.all(nodes.map(async node => {
|
|
51
|
+
const node_id = node.ip || node.hostname;
|
|
52
|
+
const base = { node_id, hostname: node.hostname, ip: node.ip, self: Boolean(node.self || node_id === selfId), online: Boolean(node.self || node.online), checked_at, automations: [], coverage: [] };
|
|
53
|
+
if (base.self) return nodeResult(base, verifyLocalResult(local));
|
|
54
|
+
const unavailable = (status, error) => ({ ...base, status, ...(error ? { error } : {}), coverage: unavailableCoverage(status === 'unreachable' ? 'error' : status, checked_at) });
|
|
55
|
+
if (!node.online) return unavailable('offline');
|
|
56
|
+
const port = node.brain_port || Number(process.env.TR_SERVER_PORT || process.env.PORT || 3000);
|
|
57
|
+
try {
|
|
58
|
+
if (execPeer) {
|
|
59
|
+
try {
|
|
60
|
+
// Mesh owns the authenticated transport. The command is fixed and read-only.
|
|
61
|
+
// A mesh-sync token is not a PAT for the generic plugin-run HTTP route.
|
|
62
|
+
const result = await execPeer(node_id, 'total-recall automations list --local --json', { timeoutMs: 15000 });
|
|
63
|
+
if (result.success && result.exitCode === 0) {
|
|
64
|
+
const inventory = parseInventoryOutput(result.stdout);
|
|
65
|
+
if (inventory.schema_version !== 1 || !Array.isArray(inventory.nodes) || inventory.nodes.length !== 1 || inventory.nodes[0].self !== true) throw new Error('Unsupported peer inventory');
|
|
66
|
+
return nodeResult(base, verifyLocalResult(inventory.nodes[0]));
|
|
67
|
+
}
|
|
68
|
+
} catch { /* Retain metadata through its separate mesh-auth contract below. */ }
|
|
69
|
+
}
|
|
70
|
+
if (!authorization) return unavailable('unauthorized', 'Full peer inventory is unavailable; mesh metadata authorization is unavailable');
|
|
71
|
+
// Old or unreachable CLI hosts may still expose plugin declarations.
|
|
72
|
+
const fallback = await fetchImpl(peerUrl(node.ip, '/api/mesh/plugins/installed', port), { headers: { Authorization: authorization } }, 5000);
|
|
73
|
+
if (!fallback.ok) return unavailable(fallback.status === 404 ? 'unsupported' : [401, 403].includes(fallback.status) ? 'unauthorized' : 'error', `HTTP ${fallback.status}`);
|
|
74
|
+
const data = await fallback.json();
|
|
75
|
+
if (!Array.isArray(data.plugins)) throw new Error('Invalid legacy peer response');
|
|
76
|
+
return nodeResult(base, { automations: scheduledTasks(data.plugins, { node_id }), coverage: SOURCES.map(source => ({ source, status: source === 'plugin-tasks' ? 'partial' : 'unsupported', checked_at,
|
|
77
|
+
error: source === 'plugin-tasks' ? 'Peer metadata reports declarations only; placement and outcome evidence may be missing' : 'Full peer inventory is unavailable' })) });
|
|
78
|
+
} catch (error) { return unavailable('unreachable', error.name === 'SyntaxError' ? 'Invalid peer JSON' : error.message); }
|
|
79
|
+
}));
|
|
80
|
+
return { schema_version: 1, checked_at, complete: records.every(node => node.status === 'ok'), nodes: records };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function filterInventory(inventory, filters = {}) {
|
|
84
|
+
let nodes = inventory.nodes;
|
|
85
|
+
if (filters.node) nodes = nodes.filter(node => [node.node_id, node.hostname, node.ip].includes(filters.node));
|
|
86
|
+
if (filters.node && !nodes.length) throw new Error('Unknown node');
|
|
87
|
+
return { ...inventory, nodes: nodes.map(node => ({ ...node, automations: node.automations.filter(record => (!filters.source || record.source === filters.source) && (!filters.trigger || record.trigger?.kind === filters.trigger) && (!filters.state || record.state === filters.state)) })) };
|
|
88
|
+
}
|