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,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tr-plugin-automations",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Scheduled plugin task runner plus automation sources, triggers, observed outcomes and coverage across the Total Recall mesh.",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "vitest run",
|
|
8
|
+
"ssss": "ssss"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"files": [
|
|
12
|
+
"cli.mjs",
|
|
13
|
+
"plugin.json",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"inventory.mjs",
|
|
17
|
+
"adapters.mjs",
|
|
18
|
+
"scheduler.mjs",
|
|
19
|
+
"cron.mjs",
|
|
20
|
+
"capability/",
|
|
21
|
+
"BRICK.md",
|
|
22
|
+
"ui/card.js",
|
|
23
|
+
"ui/automations.js",
|
|
24
|
+
"ui/DESIGN.md"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@gregiteen/ssss-cli": "0.14.5"
|
|
31
|
+
},
|
|
32
|
+
"overrides": {
|
|
33
|
+
"@ssss/cli": "npm:@gregiteen/ssss-cli@0.14.5"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://github.com/gregiteen/total-recall/blob/main/metadata.plugin.schema.json",
|
|
3
|
+
"id": "automations",
|
|
4
|
+
"name": "Automations",
|
|
5
|
+
"version": "1.1.0",
|
|
6
|
+
"description": "Run scheduled plugin tasks and inspect automation sources, triggers, observed outcomes and coverage across the mesh.",
|
|
7
|
+
"author": "Total Recall",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"use_cases": [
|
|
10
|
+
"automation",
|
|
11
|
+
"monitoring"
|
|
12
|
+
],
|
|
13
|
+
"gate": {
|
|
14
|
+
"tasks": ["List scheduled tasks and automations across the mesh", "Check an automation's real outcome", "Cancel a pending local task"],
|
|
15
|
+
"owned_commands": [
|
|
16
|
+
{ "pattern": "(crontab\\s+-l\\b|launchctl\\s+list\\b)", "use": "total-recall automations list --json" }
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"cli": {
|
|
20
|
+
"answers_help": true,
|
|
21
|
+
"command": "automations",
|
|
22
|
+
"handler": "./cli.mjs",
|
|
23
|
+
"subcommands": [
|
|
24
|
+
{
|
|
25
|
+
"name": "list",
|
|
26
|
+
"description": "List automations and source coverage across known nodes",
|
|
27
|
+
"timeout_ms": 25000,
|
|
28
|
+
"effect": "read",
|
|
29
|
+
"output": "json",
|
|
30
|
+
"args": []
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "status",
|
|
34
|
+
"description": "Report observed automation states and source coverage",
|
|
35
|
+
"timeout_ms": 25000,
|
|
36
|
+
"effect": "read",
|
|
37
|
+
"output": "json",
|
|
38
|
+
"args": []
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "show",
|
|
42
|
+
"description": "Inspect one automation by its stable id",
|
|
43
|
+
"timeout_ms": 25000,
|
|
44
|
+
"effect": "read",
|
|
45
|
+
"output": "json",
|
|
46
|
+
"args": []
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "dream",
|
|
50
|
+
"description": "Run dream, including decay, for every brain on this node",
|
|
51
|
+
"timeout_ms": 600000,
|
|
52
|
+
"effect": "write",
|
|
53
|
+
"output": "json",
|
|
54
|
+
"args": []
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "cancel",
|
|
58
|
+
"description": "Cancel one pending local durable task through its owner",
|
|
59
|
+
"effect": "write",
|
|
60
|
+
"output": "json",
|
|
61
|
+
"args": []
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"tasks": [
|
|
66
|
+
{
|
|
67
|
+
"intent": "Dream every brain on this node",
|
|
68
|
+
"schedule": "40 3 * * *",
|
|
69
|
+
"command": "dream --all-brains",
|
|
70
|
+
"placement": "per-node"
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"runtime": {
|
|
74
|
+
"modules": {
|
|
75
|
+
"scheduler": "scheduler.mjs"
|
|
76
|
+
},
|
|
77
|
+
"daemon": ["scheduler"]
|
|
78
|
+
},
|
|
79
|
+
"ui": {
|
|
80
|
+
"design_tokens": "ui/DESIGN.md",
|
|
81
|
+
"elements": [
|
|
82
|
+
{
|
|
83
|
+
"id": "automations",
|
|
84
|
+
"kind": "panel",
|
|
85
|
+
"tag": "tr-automations",
|
|
86
|
+
"module": "ui/card.js",
|
|
87
|
+
"description": "Live automation inventory with per-source node coverage",
|
|
88
|
+
"events": [],
|
|
89
|
+
"tokens": [
|
|
90
|
+
"color-text",
|
|
91
|
+
"color-text-secondary",
|
|
92
|
+
"color-surface",
|
|
93
|
+
"color-border",
|
|
94
|
+
"color-success",
|
|
95
|
+
"color-error",
|
|
96
|
+
"spacing-md",
|
|
97
|
+
"radius-md"
|
|
98
|
+
],
|
|
99
|
+
"optional_tokens": []
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"id": "card",
|
|
103
|
+
"kind": "card",
|
|
104
|
+
"tag": "tr-automations-card",
|
|
105
|
+
"module": "ui/card.js",
|
|
106
|
+
"description": "Automations workspace"
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
"card_face": {
|
|
111
|
+
"skin": "standard",
|
|
112
|
+
"theme": "precision-chronometer",
|
|
113
|
+
"headline": "Automation sources, triggers and outcomes",
|
|
114
|
+
"primary_color": "#f2bd63",
|
|
115
|
+
"secondary_color": "#24221c",
|
|
116
|
+
"accent_color": "#f2bd63",
|
|
117
|
+
"touch_layout": "comfortable",
|
|
118
|
+
"interactive": true,
|
|
119
|
+
"quick_actions": [
|
|
120
|
+
{
|
|
121
|
+
"subcommand": "list",
|
|
122
|
+
"label": "Inspect automations"
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
"auth": {
|
|
127
|
+
"plugin": "auth",
|
|
128
|
+
"methods": [
|
|
129
|
+
"passkey",
|
|
130
|
+
"google-oauth2"
|
|
131
|
+
],
|
|
132
|
+
"session_verification": true
|
|
133
|
+
},
|
|
134
|
+
"deploy": {
|
|
135
|
+
"targets": [
|
|
136
|
+
"ssss-app",
|
|
137
|
+
"nextjs",
|
|
138
|
+
"react",
|
|
139
|
+
"flask"
|
|
140
|
+
],
|
|
141
|
+
"required_ssss_version": ">=0.9.3",
|
|
142
|
+
"access_grants": [
|
|
143
|
+
"auth:session:verify",
|
|
144
|
+
"ssss:vault:read",
|
|
145
|
+
"ssss:vault:write",
|
|
146
|
+
"ssss:events:append"
|
|
147
|
+
]
|
|
148
|
+
},
|
|
149
|
+
"branding": {
|
|
150
|
+
"name": "Automations",
|
|
151
|
+
"color": "#f2bd63",
|
|
152
|
+
"badge": "Source coverage",
|
|
153
|
+
"font": "space-grotesk"
|
|
154
|
+
},
|
|
155
|
+
"status": {
|
|
156
|
+
"command": "status",
|
|
157
|
+
"refresh_seconds": 300
|
|
158
|
+
},
|
|
159
|
+
"capability": {
|
|
160
|
+
"entry": "capability/index.mjs",
|
|
161
|
+
"exports": ["collectSources"]
|
|
162
|
+
},
|
|
163
|
+
"distribution": {
|
|
164
|
+
"tier": "official",
|
|
165
|
+
"license": "open"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduled plugin tasks.
|
|
3
|
+
*
|
|
4
|
+
* A manifest task `{ intent, schedule: "<5-field cron>", command }` runs the
|
|
5
|
+
* plugin's own CLI on that schedule, out of process. `command` is a subcommand
|
|
6
|
+
* followed by its arguments ("cleanup --auto --json"). Tasks are per node by
|
|
7
|
+
* default; selected-node tasks run only on the node named in
|
|
8
|
+
* plugin_record.task_nodes[command].
|
|
9
|
+
*
|
|
10
|
+
* Scheduling runs against the global brain (the home installation), so run
|
|
11
|
+
* records and plugin.task_run events land where `automations list` reads
|
|
12
|
+
* them, whatever directory the daemon was started from.
|
|
13
|
+
*
|
|
14
|
+
* The last minute-slot each command ran in is kept in the plugin_record, so a
|
|
15
|
+
* restart neither repeats a slot nor loses one: when the daemon was busy or
|
|
16
|
+
* down, the most recent matching slot since the last run (within a day) runs
|
|
17
|
+
* once. A task never runs just because it was installed: the first run is the
|
|
18
|
+
* first matching slot after the scheduler first sees it.
|
|
19
|
+
*
|
|
20
|
+
* The daemon starts this module through the plugin's `runtime.daemon`
|
|
21
|
+
* declaration; Total Recall core holds no scheduler of its own.
|
|
22
|
+
*/
|
|
23
|
+
import os from 'node:os';
|
|
24
|
+
import path from 'node:path';
|
|
25
|
+
import { pathToFileURL } from 'node:url';
|
|
26
|
+
import { parseCron, cronMatches } from './cron.mjs';
|
|
27
|
+
|
|
28
|
+
export { parseCron, cronMatches } from './cron.mjs';
|
|
29
|
+
|
|
30
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
31
|
+
const MAX_CATCHUP_MINUTES = 24 * 60;
|
|
32
|
+
const TAIL_BYTES = 2000;
|
|
33
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
34
|
+
|
|
35
|
+
/** Core primitives the scheduler uses, loaded from the running Total Recall package. */
|
|
36
|
+
export async function loadCore(packageRoot = process.env.TR_PACKAGE_ROOT) {
|
|
37
|
+
if (!packageRoot) throw new Error('TR_PACKAGE_ROOT is not set; the scheduler runs inside the Total Recall daemon');
|
|
38
|
+
const load = (name) => import(pathToFileURL(path.join(packageRoot, 'src', 'core', `${name}.mjs`)).href);
|
|
39
|
+
const [loader, store, state, runner, events, log, mesh] = await Promise.all([
|
|
40
|
+
load('plugin-loader'), load('plugin-store'), load('plugin-state'), load('plugin-runner'),
|
|
41
|
+
load('ssss-operation-service'), load('logger'), load('mesh'),
|
|
42
|
+
]);
|
|
43
|
+
return {
|
|
44
|
+
discoverPlugins: loader.discoverPlugins,
|
|
45
|
+
vaultForPluginsDir: loader.vaultForPluginsDir,
|
|
46
|
+
readPluginRecord: store.readPluginRecord,
|
|
47
|
+
patchPluginRecord: store.patchPluginRecord,
|
|
48
|
+
ensurePluginRecord: store.ensurePluginRecord,
|
|
49
|
+
pluginExecutionEnabled: state.pluginExecutionEnabled,
|
|
50
|
+
runPluginCommand: runner.runPluginCommand,
|
|
51
|
+
appendVfsEvent: events.appendVfsEvent,
|
|
52
|
+
logger: log.logger,
|
|
53
|
+
getMeshSelf: mesh.getMeshSelf,
|
|
54
|
+
meshNodeKey: mesh.meshNodeKey,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Split a manifest task command into its subcommand and arguments. */
|
|
59
|
+
export function splitTaskCommand(command) {
|
|
60
|
+
const [subcommand = '', ...args] = String(command || '').trim().split(/\s+/).filter(Boolean);
|
|
61
|
+
return { subcommand, args };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Task timeout: the task's own, else the subcommand's declared one, else 60 s. */
|
|
65
|
+
export function taskTimeoutMs(task, manifest) {
|
|
66
|
+
if (Number.isInteger(task.timeout_ms)) return task.timeout_ms;
|
|
67
|
+
const { subcommand } = splitTaskCommand(task.command);
|
|
68
|
+
const declared = manifest?.cli?.subcommands?.find((s) => s.name === subcommand);
|
|
69
|
+
const value = declared?.timeout_ms ?? declared?.timeoutMs;
|
|
70
|
+
return Number.isInteger(value) ? value : DEFAULT_TIMEOUT_MS;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** A selected-node task stays idle until this node's plugin record selects it. */
|
|
74
|
+
export function taskRunsOnNode(task, record, self, meshNodeKey = (v) => String(v).toLowerCase()) {
|
|
75
|
+
if (task.placement !== 'selected-node') return true;
|
|
76
|
+
const selected = String(record?.task_nodes?.[task.command] || '').trim();
|
|
77
|
+
if (!selected || !self?.hostname) return false;
|
|
78
|
+
return selected === self.ip || meshNodeKey(selected) === meshNodeKey(self.hostname);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Local minute-slot key, e.g. 2026-09-22T14:05. */
|
|
82
|
+
export function minuteSlot(date) {
|
|
83
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function slotToDate(slot) {
|
|
87
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/.exec(String(slot || ''));
|
|
88
|
+
return m ? new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]), Number(m[4]), Number(m[5])) : null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** The most recent slot matching `cron` in (lastSlot, now], or null. */
|
|
92
|
+
export function latestDueSlot(cron, lastSlot, now = new Date()) {
|
|
93
|
+
const floorNow = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes());
|
|
94
|
+
const last = slotToDate(lastSlot);
|
|
95
|
+
for (let i = 0; i < MAX_CATCHUP_MINUTES; i++) {
|
|
96
|
+
const candidate = new Date(floorNow.getTime() - i * 60_000);
|
|
97
|
+
if (last && candidate <= last) return null;
|
|
98
|
+
if (cronMatches(cron, candidate)) return minuteSlot(candidate);
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Last non-empty lines of a run's output, bounded for logs and alerts. */
|
|
104
|
+
export function outputTail(output, maxChars = 400) {
|
|
105
|
+
const lines = String(output || '').trim().split('\n').map((l) => l.trim()).filter(Boolean);
|
|
106
|
+
const tail = lines.slice(-3).join(' | ');
|
|
107
|
+
return tail.length > maxChars ? tail.slice(-maxChars) : tail;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Ask the operator-alerts owner to report a failed run, once per task per day. */
|
|
111
|
+
async function alertFailure(core, { plugin, command, slot, result, projectRoot }) {
|
|
112
|
+
const owner = core.discoverPlugins(projectRoot).find((p) => p.id === 'operator-alerts' && p.valid);
|
|
113
|
+
if (!owner) return { sent: false, error: 'operator-alerts is not installed' };
|
|
114
|
+
const reason = result.timedOut ? 'timed out' : `exit ${result.exitCode}`;
|
|
115
|
+
const tail = outputTail(result.output) || 'no output';
|
|
116
|
+
const args = [
|
|
117
|
+
'--title', `Scheduled task failed: ${plugin.id} ${command}`,
|
|
118
|
+
'--message', `${plugin.id} "${command}" (slot ${slot}) failed: ${reason}. ${tail}`,
|
|
119
|
+
'--event-id', `plugin-task-${plugin.id}-${command.replace(/[^a-z0-9]+/gi, '-')}-${slot.slice(0, 10)}`,
|
|
120
|
+
];
|
|
121
|
+
try {
|
|
122
|
+
const sent = await core.runPluginCommand(owner, { subcommand: 'send', args, cwd: projectRoot, timeoutMs: 30_000 });
|
|
123
|
+
return { sent: sent.ok, error: sent.ok ? null : outputTail(sent.output) };
|
|
124
|
+
} catch (err) {
|
|
125
|
+
return { sent: false, error: err.message };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Run every task that is due. Tasks run sequentially so a slow plugin cannot
|
|
131
|
+
* multiply into concurrent processes on a constrained host.
|
|
132
|
+
* @param {{ core: object, projectRoot?: string, now?: Date, selfNode?: object, alert?: Function }} options
|
|
133
|
+
*/
|
|
134
|
+
export async function runDuePluginTasks(options) {
|
|
135
|
+
const { core, projectRoot = os.homedir(), now = new Date() } = options;
|
|
136
|
+
const alert = options.alert || ((info) => alertFailure(core, info));
|
|
137
|
+
const selfNode = options.selfNode === undefined ? core.getMeshSelf() : options.selfNode;
|
|
138
|
+
const results = [];
|
|
139
|
+
|
|
140
|
+
for (const plugin of core.discoverPlugins(projectRoot)) {
|
|
141
|
+
if (!plugin.valid || !Array.isArray(plugin.manifest.tasks) || plugin.manifest.tasks.length === 0) continue;
|
|
142
|
+
|
|
143
|
+
let record = core.readPluginRecord(plugin);
|
|
144
|
+
if (!record) record = await core.ensurePluginRecord(plugin);
|
|
145
|
+
if (!core.pluginExecutionEnabled(plugin, { projectRoot })) continue;
|
|
146
|
+
const runs = { ...(record?.task_runs || {}) };
|
|
147
|
+
let changed = false;
|
|
148
|
+
|
|
149
|
+
for (const task of plugin.manifest.tasks) {
|
|
150
|
+
if (!taskRunsOnNode(task, record, selfNode, core.meshNodeKey)) continue;
|
|
151
|
+
let cron;
|
|
152
|
+
try {
|
|
153
|
+
cron = parseCron(task.schedule);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
core.logger.warn('plugin-tasks', `Skipping ${plugin.id}:${task.command} — ${err.message}`);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!runs[task.command]) {
|
|
160
|
+
// First sighting: mark the present so the first run is the next real slot.
|
|
161
|
+
runs[task.command] = minuteSlot(now);
|
|
162
|
+
changed = true;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const slot = latestDueSlot(cron, runs[task.command], now);
|
|
167
|
+
if (!slot) continue;
|
|
168
|
+
|
|
169
|
+
const { subcommand, args } = splitTaskCommand(task.command);
|
|
170
|
+
const result = await core.runPluginCommand(plugin, { subcommand, args, cwd: projectRoot, timeoutMs: taskTimeoutMs(task, plugin.manifest) })
|
|
171
|
+
.catch((err) => ({ ok: false, exitCode: null, output: err.message, durationMs: 0, timedOut: false }));
|
|
172
|
+
runs[task.command] = slot;
|
|
173
|
+
changed = true;
|
|
174
|
+
const entry = { plugin: plugin.id, command: task.command, slot, ok: result.ok, exitCode: result.exitCode, timedOut: !!result.timedOut, tail: result.ok ? '' : outputTail(result.output) };
|
|
175
|
+
if (!result.ok) entry.alert = await alert({ plugin, command: task.command, slot, result, projectRoot });
|
|
176
|
+
results.push(entry);
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
await core.appendVfsEvent(
|
|
180
|
+
`plugins/${plugin.id}/task-${Date.now()}`,
|
|
181
|
+
{
|
|
182
|
+
kind: 'plugin.task_run',
|
|
183
|
+
plugin_id: plugin.id,
|
|
184
|
+
command: task.command,
|
|
185
|
+
intent: task.intent,
|
|
186
|
+
slot,
|
|
187
|
+
ok: result.ok,
|
|
188
|
+
exit_code: result.exitCode,
|
|
189
|
+
timed_out: !!result.timedOut,
|
|
190
|
+
duration_ms: result.durationMs,
|
|
191
|
+
output_tail: String(result.output || '').slice(-TAIL_BYTES),
|
|
192
|
+
},
|
|
193
|
+
{ actorRole: 'system', intent: 'Record plugin task run', vaultRoot: core.vaultForPluginsDir(plugin.pluginsDir) }
|
|
194
|
+
);
|
|
195
|
+
} catch (err) {
|
|
196
|
+
core.logger.warn('plugin-tasks', `Could not record task run for ${plugin.id}: ${err.message}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (changed) {
|
|
201
|
+
try {
|
|
202
|
+
await core.patchPluginRecord(plugin, { task_runs: runs });
|
|
203
|
+
} catch (err) {
|
|
204
|
+
core.logger.warn('plugin-tasks', `Could not update task runs for ${plugin.id}: ${err.message}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return results;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** One log line per run; failures carry the reason and the output tail. */
|
|
213
|
+
export function describeRun(r) {
|
|
214
|
+
if (r.ok) return `${r.plugin}:${r.command} slot ${r.slot} ok`;
|
|
215
|
+
const reason = r.timedOut ? 'timed out' : `exit ${r.exitCode}`;
|
|
216
|
+
const alert = r.alert ? (r.alert.sent ? '; operator alerted' : `; alert not sent (${r.alert.error})`) : '';
|
|
217
|
+
return `${r.plugin}:${r.command} slot ${r.slot} failed (${reason}): ${r.tail || 'no output'}${alert}`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Daemon service entry point: check for due tasks once a minute.
|
|
222
|
+
* @returns {Promise<Function>} stop function
|
|
223
|
+
*/
|
|
224
|
+
export async function start(options = {}) {
|
|
225
|
+
const core = options.core || await loadCore();
|
|
226
|
+
const projectRoot = options.projectRoot || os.homedir();
|
|
227
|
+
let running = false;
|
|
228
|
+
const tick = async () => {
|
|
229
|
+
if (running) return;
|
|
230
|
+
running = true;
|
|
231
|
+
try {
|
|
232
|
+
for (const r of await runDuePluginTasks({ core, projectRoot })) {
|
|
233
|
+
core.logger[r.ok ? 'info' : 'warn']('plugin-tasks', describeRun(r));
|
|
234
|
+
}
|
|
235
|
+
} catch (err) {
|
|
236
|
+
core.logger.warn('plugin-tasks', `Scheduler tick failed: ${err.message}`);
|
|
237
|
+
} finally {
|
|
238
|
+
running = false;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
const timer = setInterval(tick, 60_000);
|
|
242
|
+
timer.unref?.();
|
|
243
|
+
tick();
|
|
244
|
+
core.logger.info('plugin-tasks', `Plugin task scheduler started (automations) for ${projectRoot}`);
|
|
245
|
+
return () => clearInterval(timer);
|
|
246
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Automations — Control ledger
|
|
2
|
+
|
|
3
|
+
```yaml
|
|
4
|
+
tokens:
|
|
5
|
+
brand-name: "Automations"
|
|
6
|
+
wordmark: "AUTOMATIONS"
|
|
7
|
+
color-primary: "#f2bd63"
|
|
8
|
+
color-background: "#181713"
|
|
9
|
+
color-surface: "#24221c"
|
|
10
|
+
color-text: "#f4efe5"
|
|
11
|
+
color-muted: "#c5bca9"
|
|
12
|
+
color-border: "#726652"
|
|
13
|
+
color-success: "#a8d5b0"
|
|
14
|
+
color-error: "#ffb5a9"
|
|
15
|
+
color-focus: "#b8d9ff"
|
|
16
|
+
font-family: "system-ui, sans-serif"
|
|
17
|
+
font-mono: "ui-monospace, monospace"
|
|
18
|
+
border-radius: "12px"
|
|
19
|
+
spacing-unit: "8px"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Purpose and first-principles decision
|
|
23
|
+
Greg checks which work is automated, where its source is available, and whether an observed run failed. He needs automation origins and coverage before interpreting counts. Replace the old chronometer card, fixed schedule chips and terminal output: they obscure these tasks and invent healthy state. Retain external element names and host.run because these are useful host contracts.
|
|
24
|
+
|
|
25
|
+
The control-ledger arrangement pairs an automation index with selected automation provenance. Its branching vector mark represents one trigger feeding several owned runners; it does not imply active work. One brand, Automations, appears once. The amber accent marks selection and refresh; textual state identifies every failure or unknown. No ambient decoration or hardware imitation.
|
|
26
|
+
|
|
27
|
+
## Hierarchy and screens
|
|
28
|
+
The first visible line states whether inventory is unchecked, loading, complete, partial or failed, plus the last actual attempt. Refresh is the primary read action. Four purpose-defined tabs preserve the requested tasks: Scheduled (automation index, not a promise every item uses cron), Nodes (source coverage per node), Audit (observed outcomes separately from handled slots), Run (only source-advertised actions). These are domain views, not generated CLI screens. Filters cover node, source, trigger and observed state.
|
|
29
|
+
|
|
30
|
+
The selected record shows trigger, enabled state, runner availability, source, placement and origin independently. Unknown values include a reason. Failed reads clear current inventory. Unsupported sources and unavailable nodes remain visible. Counts derive only from returned records and cannot imply coverage completeness.
|
|
31
|
+
|
|
32
|
+
## Interaction and safety
|
|
33
|
+
Read through host.run('list', []) inside the sandbox. The full panel may use the authenticated generic plugin route when no host channel is available. Reject failed wrappers and malformed inventory; do not create successful fallback output. A latest-request counter prevents stale refreshes overwriting newer results. Disconnect invalidates pending work; reconnect reuses its shadow root and selected tab.
|
|
34
|
+
|
|
35
|
+
Every control is at least 44px, with visible focus. Tabs support arrow/Home/End navigation and maintain selected-screen state. Filters have labels; loading/errors use a live region. Layout becomes one column under 600px; text wraps safely. State change motion is a short content reveal; reduced motion removes it. Copy feedback follows actual clipboard completion. Source-advertised write controls state their target and require confirmation; arbitrary commands and credential inputs are absent.
|
|
36
|
+
|
|
37
|
+
## Verification
|
|
38
|
+
Use real dark host tokens, then inspect at 360, 768 and 1280px. Test unchecked/loading/partial/failed/empty states, malformed/auth-rejected requests, escaping, request races, reconnect, tab keyboard behavior and clipboard rejection. Tests use explicit fixtures; the shipped component has no fixtures or seed state. Live source authenticity is a separate backend verification obligation.
|
|
39
|
+
|
|
40
|
+
## References reviewed 2026-10-29
|
|
41
|
+
- [Custom element lifecycle](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements)
|
|
42
|
+
- [Accessible tabs pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/)
|