total-recall-brain 3.44.0 → 3.46.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 +10 -6
- package/docs/ARCHITECTURE.md +10 -7
- package/docs/reference/cli-reference.md +13 -8
- package/frontend/dist/assets/index-BRYE1jmv.css +1 -0
- package/frontend/dist/assets/index-D1goIPjC.js +5598 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/dist/sw.js +5 -4
- package/frontend/src/App.tsx +185 -238
- package/frontend/src/api/auth-inventory.ts +62 -0
- package/frontend/src/api/memory.ts +13 -8
- package/frontend/src/api/plugin-credentials.ts +23 -0
- package/frontend/src/api/plugins.ts +37 -53
- package/frontend/src/api/workspace-drawers.ts +97 -0
- package/frontend/src/components/BrainSelector.tsx +9 -3
- package/frontend/src/components/plugins/PluginPanel.tsx +83 -42
- package/frontend/src/components/plugins/PluginSettings.tsx +10 -20
- package/frontend/src/components/plugins/PluginWorkspace.tsx +367 -0
- package/frontend/src/components/plugins/ScopedPluginCommands.tsx +142 -0
- package/frontend/src/components/plugins/plugin-frame.ts +44 -33
- package/frontend/src/components/plugins/scoped-plugin-operations.ts +28 -0
- package/frontend/src/components/workspace/DrawerChat.tsx +110 -0
- package/frontend/src/components/workspace/DrawerLayout.tsx +196 -0
- package/frontend/src/components/workspace/DrawerMarkup.tsx +49 -0
- package/frontend/src/components/workspace/PluginChunkLibrary.tsx +92 -0
- package/frontend/src/components/workspace/PluginHome.tsx +76 -0
- package/frontend/src/components/workspace/WorkspaceDrawers.tsx +96 -0
- package/frontend/src/components/workspace/WorkspacePage.tsx +95 -0
- package/frontend/src/components/workspace/usePluginDesign.ts +38 -0
- package/frontend/src/index.css +409 -136
- package/frontend/src/pages/HelpPage.tsx +15 -46
- package/frontend/src/pages/MemoryPage.tsx +19 -36
- package/frontend/src/pages/SettingsPage.tsx +1 -1
- package/metadata.plugin.schema.json +53 -227
- package/package.json +1 -1
- package/scaffold/.agent/skills/plugin/SKILL.md +13 -11
- package/scaffold/.agent/skills/plugin/evals/README.md +14 -21
- package/scaffold/.agent/skills/plugin/evals/cases/01-fabricated-seed.mjs +15 -18
- package/scaffold/.agent/skills/plugin/evals/cases/02-reassuring-default.mjs +19 -24
- package/scaffold/.agent/skills/plugin/evals/cases/03-declared-template.mjs +12 -20
- package/scaffold/.agent/skills/plugin/evals/cases/04-home-collects-credential.mjs +35 -0
- package/scaffold/.agent/skills/plugin/evals/cases/05-host-leak-in-scaffold.mjs +8 -28
- package/scaffold/.agent/skills/plugin/evals/cases/06-correct-plugin.mjs +85 -47
- package/scaffold/.agent/skills/plugin/evals/run.mjs +17 -18
- package/scaffold/.agent/skills/plugin/prompts/05-credentials.md +4 -4
- package/scaffold/.agent/skills/plugin/prompts/07-scaffold.md +2 -2
- package/scaffold/.agent/skills/plugin/prompts/08-verify.md +4 -4
- package/scaffold/.agent/skills/plugin/references/anti-patterns.md +2 -2
- package/scaffold/.agent/skills/plugin/references/generative-home.md +38 -0
- package/scaffold/.agent/skills/plugin/references/scaffold.md +5 -5
- package/scaffold/.agent/skills/plugin/references/surfaces.md +13 -4
- package/scaffold/.agent/skills/plugin/scripts/record.mjs +75 -153
- package/scaffold/.agent/skills/plugin/subagents/plugin-reviewer.md +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/DESIGN.md +76 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/README.md +3 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/cli.mjs +20 -15
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +13 -51
- 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/auth/ui/chunks/access-overview.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/agent-login.md +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/baseline-audit.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/credential-repository-binding.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/credential-store-maintenance.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/dashboard-password.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/dashboard-sessions.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/endpoint-probe.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/google-sign-in.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/mesh-machines.md +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/passkey-enrollment.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/registered-brains.md +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/scoped-access-keys.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/secret-inventory.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/signing-key-rotation.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/home.js +375 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/BRICK.md +7 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/DESIGN.md +53 -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 +247 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/capability/index.mjs +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/cli.mjs +130 -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 +36 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/plugin.json +151 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/scheduler.mjs +246 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/automations.js +2 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/control-ledger.md +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/local-inventory-triage.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/pending-control.md +39 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/run-evidence.md +41 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/schedule-vs-run-evidence.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/source-coverage.md +41 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/home.js +278 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/DESIGN.md +53 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/cli.mjs +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +5 -3
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +61 -7
- 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 +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +40 -40
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/config.md +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/detect.md +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/gate.md +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/init.md +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/report.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/run.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/snapshot.md +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/code-quality-report.mjs +55 -23
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/home.mjs +214 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/DESIGN.md +146 -0
- 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 +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +51 -34
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/command.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/create.md +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/history.md +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/library.md +49 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/list.md +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/promote.md +40 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/read.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/remove.md +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/rollback.md +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/show.md +53 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/status.md +40 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/test-path.md +38 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/test.md +46 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/update.md +32 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/home.js +533 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/DESIGN.md +110 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +13 -10
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/benchmark.md +49 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/coverage.md +53 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/models.md +43 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/pull.md +41 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/status.md +59 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/home.js +353 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/DESIGN.md +31 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/README.md +136 -0
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/cli.mjs +140 -24
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package-lock.json +9 -9
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package.json +4 -2
- package/scaffold/.agent/skills/total-recall/plugins/git/plugin.json +151 -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 +54 -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 +174 -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 +92 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/undo.mjs +156 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/unmerged.mjs +182 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/why.mjs +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/change-comparison.md +27 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/commit-identity.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/create-worktree.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/registered-worktrees.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/remove-worktree.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/repository-ledger.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/unmerged-work.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/upstream-divergence.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/worktree-conflict-triage.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/worktree-terminal.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/home.js +273 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/DESIGN.md +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/README.md +4 -5
- package/scaffold/.agent/skills/total-recall/plugins/mesh/cli.mjs +9 -3
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +37 -39
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +5 -3
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/active-device-dispatch.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/control-probe.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/control-server.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/credential-readiness.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/elected-leader.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/enrollment-key.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/file-receipt.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/fleet-execution.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/fleet-presence.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/interactive-ssh.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/local-enrollment.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/mesh-policy.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/network-atlas.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/node-roster.md +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/node-selection-proof.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/owner-observations.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/peer-path.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/pinned-leader.md +24 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/reachability-evidence-ladder.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/recorded-login.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/remote-execution.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/secret-grants.md +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/{card.js → home.js} +93 -22
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/DESIGN.md +86 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/README.md +44 -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 +128 -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 +3 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +188 -34
- 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/chat.mjs +80 -0
- 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/chunks/adapter-docs-freshness.md +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/chat-handoff.md +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/fleet-maintenance.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/harness-inventory.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/included-access.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/included-usage-verification.md +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/independent-review.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/managed-execution.md +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/official-documentation.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/quota-recovery.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/review-routing-decision.md +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/route-work.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/routing-evidence.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/run-ledger.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/home.js +198 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/DESIGN.md +75 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/README.md +2 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +64 -32
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +10 -44
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/generate.mjs +5 -4
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/graph.mjs +3 -4
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/ingest.mjs +23 -18
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/mesh-vaults.mjs +8 -3
- 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 +123 -113
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/search.mjs +3 -5
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/token.mjs +21 -47
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/upstream-watcher.mjs +17 -6
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/auto-refresh.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/bootstrap-pages.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/generate-wiki.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/generator-settings.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/host-probe.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/ingest-preview.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/link-hygiene.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/link-topology.md +19 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/passage-search.md +19 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/propagation-plan.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/provider-credentials.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/refresh-report.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/task-evidence.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/upstream-record.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/watch-upstream.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/wiki-atlas.md +19 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/panel.js +261 -513
- package/scaffold/.agent/skills/total-recall/plugins/security/DESIGN.md +84 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/README.md +4 -2
- package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +7 -19
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +2 -3
- package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +19 -52
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/device-evidence.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/evidence-desk.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/guided-device-review.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/host-and-network.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/https-headers.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/mesh-coverage.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/private-security-tools.md +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/single-origin-header-check.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/source-review.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/ssh-permission-plan.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/home.js +283 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/DESIGN.md +93 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/package-lock.json +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/ssss/package.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/ssss/plugin.json +5 -3
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/conformance.md +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/registry.md +37 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/repair.md +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/scaffold.md +33 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/status.md +43 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/validate.md +34 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/validation-diagnostics.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/workspace-registry-preflight.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/home.js +348 -0
- package/scaffold/.agent/skills/total-recall/references/operations.md +3 -1
- package/src/cli/agent.mjs +1 -1
- package/src/cli/api.mjs +3 -3
- package/src/cli/brief.mjs +3 -0
- package/src/cli/command.mjs +41 -2
- package/src/cli/config.mjs +16 -0
- package/src/cli/connect.mjs +2 -7
- 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/chunks.mjs +145 -0
- package/src/cli/plugin/copies.mjs +64 -14
- package/src/cli/plugin/create.mjs +30 -59
- package/src/cli/plugin/index.mjs +31 -16
- 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/setup.mjs +2 -9
- package/src/cli/start.mjs +1 -0
- package/src/cli/startup.mjs +19 -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/app-deploy/design-tokens.mjs +14 -9
- package/src/core/app-deploy/ui-elements.mjs +1 -1
- package/src/core/brain-locate.mjs +15 -0
- package/src/core/brain-registry.mjs +5 -0
- package/src/core/brain-updates.mjs +35 -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-control.mjs +5 -1
- package/src/core/daemon-loop.mjs +39 -62
- package/src/core/dashboard-bundle.mjs +261 -0
- package/src/core/default-plugins.mjs +1 -1
- package/src/core/dream.mjs +33 -105
- package/src/core/ensure-dependencies.mjs +152 -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/memory-brain.mjs +30 -8
- package/src/core/meta-harness.mjs +2 -2
- package/src/core/optimizer.mjs +66 -165
- package/src/core/plugin-activation.mjs +63 -20
- package/src/core/plugin-contracts/{card.mjs → manifest.mjs} +26 -57
- package/src/core/plugin-contracts/startup-check.mjs +49 -0
- package/src/core/plugin-contracts/ui.mjs +9 -3
- package/src/core/plugin-distribution.mjs +6 -6
- package/src/core/plugin-lifecycle.mjs +58 -0
- package/src/core/plugin-loader.mjs +21 -8
- package/src/core/plugin-runner.mjs +21 -10
- package/src/core/plugin-runtime.mjs +40 -1
- package/src/core/plugin-startup-checks.mjs +44 -0
- package/src/core/plugin-state.mjs +19 -5
- package/src/core/plugin-store.mjs +64 -33
- package/src/core/post-mortem.mjs +52 -36
- package/src/core/preflight-gate.mjs +23 -2
- package/src/core/project-id.mjs +15 -5
- package/src/core/proposal-applier.mjs +31 -2
- package/src/core/repo-sync.mjs +4 -6
- package/src/core/required-plugins.mjs +57 -10
- 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 +3 -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/ssss-operation-service.mjs +1 -0
- package/src/core/startup-health.mjs +34 -4
- package/src/core/surface.mjs +65 -54
- package/src/core/task-executors.mjs +54 -106
- package/src/core/testing/plugin-fixture.mjs +22 -21
- package/src/core/throttled-fetch.mjs +25 -18
- package/src/core/ui-chunk-choice.mjs +77 -0
- package/src/core/ui-chunk.registry.json +23 -0
- package/src/core/ui-chunks.mjs +434 -0
- package/src/core/validated-write.mjs +1 -0
- package/src/core/vault-cache.mjs +58 -59
- package/src/core/vector-field.mjs +134 -192
- package/src/core/workspace-compositions.mjs +151 -0
- package/src/core/workspace-drawers.mjs +79 -0
- package/src/server/api.mjs +4 -27
- package/src/server/index.mjs +28 -21
- package/src/server/invoke-core-child.mjs +24 -0
- package/src/server/invoke-core-operations.mjs +102 -0
- package/src/server/memory-events.mjs +31 -0
- package/src/server/plugin-brain-targets.mjs +89 -0
- package/src/server/rest.mjs +2 -0
- package/src/server/route-inventory.mjs +3 -2
- package/src/server/route-manifest.json +47 -55
- package/src/server/routes/_shared.mjs +7 -14
- package/src/server/routes/brains.mjs +29 -62
- package/src/server/routes/context.mjs +0 -41
- package/src/server/routes/dashboard.mjs +26 -0
- package/src/server/routes/invoke.mjs +26 -1
- package/src/server/routes/plugins.mjs +154 -45
- 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/ui-chunks.mjs +192 -0
- package/src/server/routes/vault.mjs +2 -23
- package/frontend/dist/assets/index-DD4qONvH.js +0 -5607
- package/frontend/dist/assets/index-DtwsDyFM.css +0 -1
- package/frontend/src/components/plugins/card/CardEngine.tsx +0 -108
- package/frontend/src/components/plugins/card/CardScenes.tsx +0 -103
- package/frontend/src/components/plugins/card/ExecutionPicker.tsx +0 -64
- package/frontend/src/components/plugins/card/args.ts +0 -28
- package/frontend/src/components/plugins/card/card.css +0 -32
- package/frontend/src/components/plugins/card/cli-line.ts +0 -13
- package/frontend/src/components/plugins/card/contract.ts +0 -91
- package/frontend/src/components/plugins/card/format.ts +0 -49
- package/frontend/src/components/plugins/card/runner-context.ts +0 -11
- package/frontend/src/components/plugins/card/runner.tsx +0 -43
- package/frontend/src/components/plugins/card/theme.ts +0 -35
- package/frontend/src/components/plugins/card/use-source.ts +0 -22
- package/frontend/src/components/plugins/card/widgets.tsx +0 -94
- package/frontend/src/components/plugins/workspace/WorkspaceHost.tsx +0 -23
- package/frontend/src/pages/PluginsPage.tsx +0 -1719
- package/scaffold/.agent/skills/plugin/evals/cases/04-card-collects-credential.mjs +0 -47
- package/scaffold/.agent/skills/plugin/prompts/06-card.md +0 -46
- package/scaffold/.agent/skills/plugin/references/card.md +0 -90
- package/scaffold/.agent/skills/plugin/subagents/card-designer.md +0 -60
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/DESIGN.md +0 -67
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +0 -1179
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +0 -32
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +0 -588
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +0 -575
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/DESIGN.md +0 -32
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +0 -482
- 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/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/DESIGN.md +0 -100
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +0 -637
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/DESIGN.md +0 -217
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/DESIGN.md +0 -32
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +0 -1276
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +0 -45
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +0 -1401
- package/scaffold/.agent/skills/total-recall/plugins/security/scripts/build-ui.mjs +0 -4
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +0 -41
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +0 -396
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/panel.js +0 -98
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/DESIGN.md +0 -15
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/card.js +0 -182
- 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
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Functional card contract. A plugin's card face is its own bespoke Web
|
|
3
|
-
* Component, declared as a `ui.elements[]` entry of kind `card`. There is no
|
|
4
|
-
* template: no shared layout, no widget vocabulary, no allowlist of shapes.
|
|
5
|
-
* Core validates that the face exists and that the commands behind it are
|
|
6
|
-
* declared; it never dictates how the face is arranged.
|
|
7
|
-
*
|
|
8
|
-
* A plugin carries exactly one face and AS MANY secondary screens as it needs,
|
|
9
|
-
* each declared `kind: 'panel'`. Screens are the plugin's own design: it names
|
|
10
|
-
* them, orders them and decides what is on them. The host imposes no count and
|
|
11
|
-
* no screen set, and never derives a screen from the command list.
|
|
12
|
-
*/
|
|
1
|
+
/** Cross-field validation for plugin manifests and their command contracts. */
|
|
13
2
|
const plain = (v) => v && typeof v === 'object' && !Array.isArray(v);
|
|
14
3
|
const COMMAND = /^[a-z][a-z0-9-]{0,63}$/;
|
|
15
4
|
const ARG_NAME = /^[a-z][a-zA-Z0-9]{0,63}$/;
|
|
@@ -19,11 +8,10 @@ export const COMMAND_EFFECTS = ['read', 'write', 'destructive', 'send'];
|
|
|
19
8
|
export const OUTPUT_KINDS = ['json', 'text'];
|
|
20
9
|
export const TIERS = ['core', 'official', 'personal', 'community'];
|
|
21
10
|
export const LICENSE_CLASSES = ['open', 'closed'];
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export const CARD_HEADERS = ['plain', 'ledger-rule', 'band', 'tab-strip', 'badge-left'];
|
|
11
|
+
const BRAND_FONTS = ['system', 'inter', 'ibm-plex', 'space-grotesk', 'jetbrains-mono', 'dm-sans', 'outfit', 'fraunces', 'newsreader', 'chivo-mono'];
|
|
12
|
+
const BRAND_DENSITIES = ['compact', 'comfortable', 'spacious'];
|
|
13
|
+
const BRAND_RADII = ['none', 'sm', 'md', 'lg', 'pill'];
|
|
14
|
+
const BRAND_HEADERS = ['plain', 'ledger-rule', 'band', 'tab-strip', 'badge-left'];
|
|
27
15
|
export const WORKSPACE_PERMISSIONS = ['microphone', 'camera', 'notifications'];
|
|
28
16
|
const HEX = /^#[0-9a-fA-F]{6}$/;
|
|
29
17
|
|
|
@@ -41,7 +29,7 @@ function validateArg(arg, where, errors) {
|
|
|
41
29
|
if (arg.secret === true && arg.type !== 'string') errors.push(`${where}: a secret stdin argument must be a string`);
|
|
42
30
|
}
|
|
43
31
|
|
|
44
|
-
export function
|
|
32
|
+
export function validateManifestContract(manifest) {
|
|
45
33
|
const errors = [];
|
|
46
34
|
const warnings = [];
|
|
47
35
|
const subs = manifest.cli?.subcommands;
|
|
@@ -55,6 +43,16 @@ export function validateCardContract(manifest) {
|
|
|
55
43
|
else names.add(sub.name);
|
|
56
44
|
if (sub.effect !== undefined && !COMMAND_EFFECTS.includes(sub.effect)) errors.push(`${where}.effect must be one of: ${COMMAND_EFFECTS.join(', ')}`);
|
|
57
45
|
if (sub.output !== undefined && !OUTPUT_KINDS.includes(sub.output)) errors.push(`${where}.output must be one of: ${OUTPUT_KINDS.join(', ')}`);
|
|
46
|
+
const terminalFlags = sub.terminal_only_args;
|
|
47
|
+
if (sub.terminal_only !== undefined && typeof sub.terminal_only !== 'boolean') errors.push(`${where}.terminal_only must be a boolean`);
|
|
48
|
+
if (terminalFlags !== undefined && (!Array.isArray(terminalFlags) ||
|
|
49
|
+
terminalFlags.some(flag => typeof flag !== 'string' || !FLAG.test(flag)) ||
|
|
50
|
+
new Set(terminalFlags).size !== terminalFlags.length)) {
|
|
51
|
+
errors.push(`${where}.terminal_only_args must contain unique long flags`);
|
|
52
|
+
}
|
|
53
|
+
// The runner refuses anything outside this range, so a manifest that
|
|
54
|
+
// declares it must fail validation rather than every call at run time.
|
|
55
|
+
if (sub.timeout_ms !== undefined && (!Number.isInteger(sub.timeout_ms) || sub.timeout_ms < 1 || sub.timeout_ms > 600000)) errors.push(`${where}.timeout_ms must be an integer from 1 to 600000`);
|
|
58
56
|
if (sub.args !== undefined) {
|
|
59
57
|
if (!Array.isArray(sub.args)) errors.push(`${where}.args must be an array`);
|
|
60
58
|
else {
|
|
@@ -88,7 +86,7 @@ export function validateCardContract(manifest) {
|
|
|
88
86
|
if (!LICENSE_CLASSES.includes(manifest.distribution.license)) errors.push(`'distribution.license' must be one of: ${LICENSE_CLASSES.join(', ')}`);
|
|
89
87
|
}
|
|
90
88
|
}
|
|
91
|
-
|
|
89
|
+
validatePresentation(manifest, errors, warnings);
|
|
92
90
|
validateBranding(manifest.branding, errors, warnings);
|
|
93
91
|
validateWorkspace(manifest.workspace, errors);
|
|
94
92
|
if (Array.isArray(manifest.secrets)) {
|
|
@@ -96,30 +94,15 @@ export function validateCardContract(manifest) {
|
|
|
96
94
|
if (!plain(s) || typeof s.key !== 'string' || !/^[A-Z][A-Z0-9_]{0,63}$/.test(s.key)) errors.push(`secrets[${i}].key must be an uppercase identifier`);
|
|
97
95
|
});
|
|
98
96
|
}
|
|
99
|
-
|
|
100
|
-
warnings.push("card_face.metrics are fixed text and cannot reflect real state; declare a 'status' command instead");
|
|
101
|
-
}
|
|
102
|
-
return { errors, warnings, states: STATUS_STATES };
|
|
97
|
+
return { errors, warnings };
|
|
103
98
|
}
|
|
104
99
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
* a shared template is invalid.
|
|
109
|
-
*
|
|
110
|
-
* The *scaffold* emitted by `plugin create` deliberately contains no card: a
|
|
111
|
-
* face is authored by hand once the plugin's function is understood. A scaffold
|
|
112
|
-
* declares `scaffold: true`, so it installs while the author works and reports
|
|
113
|
-
* its missing face as a warning rather than an error. Any other plugin without
|
|
114
|
-
* a face is an error, because nothing else explains why it is missing.
|
|
115
|
-
*/
|
|
116
|
-
function validateCardFace(manifest, errors, warnings) {
|
|
117
|
-
if (manifest.card_layout !== undefined) {
|
|
118
|
-
errors.push("'card_layout' is not supported: a card face is bespoke owner code in ui.elements[], never a shared template");
|
|
100
|
+
function validatePresentation(manifest, errors, warnings) {
|
|
101
|
+
for (const field of ['card', 'card_face', 'card_layout']) {
|
|
102
|
+
if (manifest[field] !== undefined) errors.push(`'${field}' is not supported: declare a native ui.home instead`);
|
|
119
103
|
}
|
|
120
104
|
const elements = manifest.ui?.elements;
|
|
121
|
-
// A brick is distributed as capability code alone
|
|
122
|
-
// has no face; every other plugin still requires exactly one.
|
|
105
|
+
// A brick is distributed as capability code alone and has no UI.
|
|
123
106
|
if (manifest.brick === true) {
|
|
124
107
|
validateCapability(manifest, errors);
|
|
125
108
|
if (manifest.ui !== undefined && Array.isArray(elements) && elements.length > 0) {
|
|
@@ -128,23 +111,9 @@ function validateCardFace(manifest, errors, warnings) {
|
|
|
128
111
|
return;
|
|
129
112
|
}
|
|
130
113
|
if (manifest.capability !== undefined) validateCapability(manifest, errors);
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// shippable plugin, so the missing face is reported and stays visible.
|
|
135
|
-
if (manifest.scaffold === true) {
|
|
136
|
-
warnings.push("'ui.elements' declares no card face yet: author ui/card.js and ui/DESIGN.md by hand for this plugin's own function, then declare the face in ui.elements[]");
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
errors.push("'ui.elements' must declare the plugin's card face; every plugin has one, authored by hand");
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
// One face, and as many secondary screens as the plugin needs. Cardinality is
|
|
143
|
-
// checked for `card` alone, so panels may be declared in any number and on
|
|
144
|
-
// either side of the face.
|
|
145
|
-
const faces = elements.filter((element) => plain(element) && element.kind === 'card');
|
|
146
|
-
if (faces.length === 0) errors.push("'ui.elements' must declare an element of kind 'card'");
|
|
147
|
-
if (faces.length > 1) errors.push("'ui.elements' must declare exactly one card face; further screens use kind 'panel'");
|
|
114
|
+
if (manifest.ui?.home !== undefined) return;
|
|
115
|
+
if (manifest.scaffold === true) warnings.push("Scaffold needs an owner-authored 'ui.home' and DESIGN.md before shipping");
|
|
116
|
+
else errors.push("A plugin must declare a native 'ui.home'");
|
|
148
117
|
}
|
|
149
118
|
|
|
150
119
|
/**
|
|
@@ -185,7 +154,7 @@ function validateBranding(branding, errors, warnings) {
|
|
|
185
154
|
if (branding.icon !== undefined) {
|
|
186
155
|
warnings.push('branding.icon is deprecated: show the real logo with branding.logo instead of an emoji or generic icon');
|
|
187
156
|
}
|
|
188
|
-
const enums = { font:
|
|
157
|
+
const enums = { font: BRAND_FONTS, density: BRAND_DENSITIES, radius: BRAND_RADII, header: BRAND_HEADERS };
|
|
189
158
|
for (const [key, allowed] of Object.entries(enums)) {
|
|
190
159
|
const value = branding[key];
|
|
191
160
|
if (value === undefined || allowed.includes(value)) continue;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `startup_check`: a read command a plugin contributes to `startup check` and
|
|
3
|
+
* `brief`, so something the plugin owns that needs attention is visible when a
|
|
4
|
+
* session starts. Core runs it and prints what it says; it never inspects the
|
|
5
|
+
* plugin's domain itself.
|
|
6
|
+
*
|
|
7
|
+
* "startup_check": { "command": "unmerged", "args": ["--json"], "timeout_ms": 15000 }
|
|
8
|
+
*
|
|
9
|
+
* The command prints one JSON object: { ok: boolean, outstanding: integer,
|
|
10
|
+
* summary: string }. `outstanding` above zero means attention is needed and
|
|
11
|
+
* `summary` says what and where, in one line.
|
|
12
|
+
*/
|
|
13
|
+
const plain = (v) => v && typeof v === 'object' && !Array.isArray(v);
|
|
14
|
+
const ARG = /^[A-Za-z0-9][A-Za-z0-9._=:\/-]{0,127}$|^--?[a-z][a-z0-9-]{0,63}$/;
|
|
15
|
+
export const STARTUP_CHECK_MAX_TIMEOUT_MS = 30000;
|
|
16
|
+
|
|
17
|
+
/** @returns {string[]} errors */
|
|
18
|
+
export function validateStartupCheck(manifest) {
|
|
19
|
+
const check = manifest?.startup_check;
|
|
20
|
+
if (check === undefined) return [];
|
|
21
|
+
if (!plain(check)) return ["'startup_check' must be an object"];
|
|
22
|
+
const errors = [];
|
|
23
|
+
const subs = Array.isArray(manifest.cli?.subcommands) ? manifest.cli.subcommands : [];
|
|
24
|
+
const sub = subs.find((s) => s?.name === check.command);
|
|
25
|
+
if (typeof check.command !== 'string' || !sub) errors.push("'startup_check.command' must name a declared cli subcommand");
|
|
26
|
+
else if (sub.effect && sub.effect !== 'read') errors.push("'startup_check.command' must be a read command");
|
|
27
|
+
if (check.args !== undefined && (!Array.isArray(check.args) || check.args.length > 8 || check.args.some((a) => typeof a !== 'string' || !ARG.test(a)))) {
|
|
28
|
+
errors.push("'startup_check.args' must be at most 8 plain arguments");
|
|
29
|
+
}
|
|
30
|
+
if (check.timeout_ms !== undefined && (!Number.isInteger(check.timeout_ms) || check.timeout_ms < 1000 || check.timeout_ms > STARTUP_CHECK_MAX_TIMEOUT_MS)) {
|
|
31
|
+
errors.push(`'startup_check.timeout_ms' must be an integer from 1000 to ${STARTUP_CHECK_MAX_TIMEOUT_MS}`);
|
|
32
|
+
}
|
|
33
|
+
return errors;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Read a startup check's output. Anything other than the declared shape is
|
|
38
|
+
* reported as unavailable, never as clean.
|
|
39
|
+
* @returns {{ status: 'ok'|'attention'|'unavailable', outstanding?: number, summary?: string, reason?: string }}
|
|
40
|
+
*/
|
|
41
|
+
export function readStartupCheckOutput(stdout) {
|
|
42
|
+
let body;
|
|
43
|
+
try { body = JSON.parse(String(stdout || '').trim()); } catch { return { status: 'unavailable', reason: 'output is not JSON' }; }
|
|
44
|
+
if (!plain(body) || typeof body.ok !== 'boolean' || !Number.isInteger(body.outstanding) || body.outstanding < 0 || typeof body.summary !== 'string') {
|
|
45
|
+
return { status: 'unavailable', reason: 'output is not { ok, outstanding, summary }' };
|
|
46
|
+
}
|
|
47
|
+
if (!body.ok) return { status: 'unavailable', outstanding: body.outstanding, summary: body.summary.slice(0, 500), reason: 'the check reported a failure' };
|
|
48
|
+
return { status: body.outstanding > 0 ? 'attention' : 'ok', outstanding: body.outstanding, summary: body.summary.slice(0, 500) };
|
|
49
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Pure plugin UI manifest validation; rendering belongs to capability owners. */
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
export const UI_ELEMENT_KINDS = ['panel', 'form', 'list', 'detail', 'settings', 'widget'
|
|
3
|
+
export const UI_ELEMENT_KINDS = ['panel', 'form', 'list', 'detail', 'settings', 'widget'];
|
|
4
4
|
const PROP_TYPES = ['string', 'number', 'boolean', 'object', 'array'];
|
|
5
5
|
const ELEMENT_ID = /^[a-z][a-z0-9-]{0,63}$/;
|
|
6
6
|
// Custom element names: lowercase, start with a letter, contain a hyphen.
|
|
@@ -117,13 +117,13 @@ function normalizeElement(raw, index, errors) {
|
|
|
117
117
|
/**
|
|
118
118
|
* Validates and normalizes a manifest `ui` block.
|
|
119
119
|
* @param {object} ui
|
|
120
|
-
* @returns {{ design_tokens?: string, elements: object[] }}
|
|
120
|
+
* @returns {{ design_tokens?: string, home?: string, elements: object[] }}
|
|
121
121
|
* @throws {UiSpecError}
|
|
122
122
|
*/
|
|
123
123
|
export function normalizeUiSpec(ui) {
|
|
124
124
|
const errors = [];
|
|
125
125
|
if (!isPlainObject(ui)) throw new UiSpecError(["'ui' must be an object"]);
|
|
126
|
-
unknownKeys(ui, ['design_tokens', 'elements'], 'ui', errors);
|
|
126
|
+
unknownKeys(ui, ['design_tokens', 'elements', 'home'], 'ui', errors);
|
|
127
127
|
const spec = { elements: [] };
|
|
128
128
|
if (ui.design_tokens !== undefined) {
|
|
129
129
|
if (!isSafePluginPath(ui.design_tokens, /\.md$/)) {
|
|
@@ -142,6 +142,12 @@ export function normalizeUiSpec(ui) {
|
|
|
142
142
|
tags.add(el.tag);
|
|
143
143
|
spec.elements.push(el);
|
|
144
144
|
});
|
|
145
|
+
if (ui.home !== undefined) {
|
|
146
|
+
if (!spec.elements.some(el => el.id === ui.home && ['panel', 'form', 'list', 'detail', 'widget'].includes(el.kind))) {
|
|
147
|
+
errors.push("'ui.home' must name a declared content element");
|
|
148
|
+
} else spec.home = ui.home;
|
|
149
|
+
if (!spec.design_tokens) errors.push("'ui.home' requires 'ui.design_tokens'");
|
|
150
|
+
}
|
|
145
151
|
if (errors.length) throw new UiSpecError(errors);
|
|
146
152
|
return spec;
|
|
147
153
|
}
|
|
@@ -6,8 +6,8 @@ import { installPlugin as installLocal, readPluginRecord, patchPluginRecord, des
|
|
|
6
6
|
findInstalled, emit, vaultFor } from './plugin-store.mjs';
|
|
7
7
|
export * from './plugin-store.mjs';
|
|
8
8
|
|
|
9
|
-
function describePluginWithShare(plugin) {
|
|
10
|
-
const result = describePlugin(plugin);
|
|
9
|
+
function describePluginWithShare(plugin, projectRoot) {
|
|
10
|
+
const result = describePlugin(plugin, projectRoot);
|
|
11
11
|
result.share_url = result.shared && result.sha256 ? publicPluginShareUrl(plugin.id, result.sha256) : null;
|
|
12
12
|
return result;
|
|
13
13
|
}
|
|
@@ -50,7 +50,7 @@ export async function setPluginShared(id, shared, options = {}) {
|
|
|
50
50
|
if (shared && !plugin.valid) throw new Error(`Plugin '${id}' has an invalid manifest and cannot be shared`);
|
|
51
51
|
await patchPluginRecord(plugin, { shared: !!shared, public_shared: !!shared });
|
|
52
52
|
await emit(vaultFor(plugin), id, shared ? 'shared' : 'unshared', { scope: plugin.scope });
|
|
53
|
-
return describePluginWithShare(plugin);
|
|
53
|
+
return describePluginWithShare(plugin, options.projectRoot);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
export async function setPluginBranding(id, branding, options = {}) {
|
|
@@ -68,7 +68,7 @@ export async function setPluginBranding(id, branding, options = {}) {
|
|
|
68
68
|
};
|
|
69
69
|
await patchPluginRecord(plugin, { branding: cleanBranding });
|
|
70
70
|
await emit(vaultFor(plugin), id, 'branding_updated', { branding: cleanBranding, scope: plugin.scope });
|
|
71
|
-
return describePluginWithShare(plugin);
|
|
71
|
+
return describePluginWithShare(plugin, options.projectRoot);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
export async function setPluginLocked(id, locked, options = {}) {
|
|
@@ -76,7 +76,7 @@ export async function setPluginLocked(id, locked, options = {}) {
|
|
|
76
76
|
if (!plugin) throw new Error(`Plugin '${id}' is not installed`);
|
|
77
77
|
await patchPluginRecord(plugin, { locked: !!locked });
|
|
78
78
|
await emit(vaultFor(plugin), id, locked ? 'locked' : 'unlocked', { scope: plugin.scope });
|
|
79
|
-
return describePluginWithShare(plugin);
|
|
79
|
+
return describePluginWithShare(plugin, options.projectRoot);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
export async function deployPluginToStore(id, options = {}) {
|
|
@@ -97,7 +97,7 @@ export async function deployPluginToStore(id, options = {}) {
|
|
|
97
97
|
scope: plugin.scope,
|
|
98
98
|
locked: autoLock
|
|
99
99
|
});
|
|
100
|
-
return describePluginWithShare(plugin);
|
|
100
|
+
return describePluginWithShare(plugin, options.projectRoot);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
/** What this node offers to mesh peers: valid installed plugins whose record says shared. */
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { FileLeaseStore } from '@ssss/cli/leases';
|
|
5
|
+
import { globalPluginsDir, projectPluginsDir } from './plugin-loader.mjs';
|
|
6
|
+
|
|
7
|
+
/** Use only real directories below the repository, including any archive parents. */
|
|
8
|
+
function directory(root, names, create = false) {
|
|
9
|
+
let target = root;
|
|
10
|
+
for (const name of names) {
|
|
11
|
+
target = path.join(target, name);
|
|
12
|
+
let stat = fs.lstatSync(target, { throwIfNoEntry: false });
|
|
13
|
+
if (!stat && create) {
|
|
14
|
+
try { fs.mkdirSync(target, { mode: 0o700 }); }
|
|
15
|
+
catch (error) { if (error.code !== 'EEXIST') throw error; }
|
|
16
|
+
stat = fs.lstatSync(target);
|
|
17
|
+
}
|
|
18
|
+
if (!stat) return null;
|
|
19
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error('Plugin lifecycle paths must be real directories.');
|
|
20
|
+
}
|
|
21
|
+
return target;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function withRepositoryPluginLifecycle(projectRoot, id, change) {
|
|
25
|
+
if (!projectRoot) return change(null);
|
|
26
|
+
if (!/^[a-z][a-z0-9-]{1,63}$/.test(id)) throw new Error('Invalid plugin identity');
|
|
27
|
+
const root = fs.realpathSync(projectRoot);
|
|
28
|
+
const brainPath = path.dirname(projectPluginsDir(root));
|
|
29
|
+
const shared = path.dirname(globalPluginsDir());
|
|
30
|
+
if (brainPath === path.resolve(shared) ||
|
|
31
|
+
(fs.existsSync(brainPath) && fs.existsSync(shared) && fs.realpathSync(brainPath) === fs.realpathSync(shared))) {
|
|
32
|
+
return change(null);
|
|
33
|
+
}
|
|
34
|
+
const brain = directory(root, path.relative(root, brainPath).split(path.sep), true);
|
|
35
|
+
const store = new FileLeaseStore(directory(brain, ['config', 'plugin-activation-leases'], true));
|
|
36
|
+
const lease = await store.acquire({ workspace_id: 'plugin-activation', target: id, principal_id: 'total-recall', operation_id: crypto.randomUUID() }, 120_000);
|
|
37
|
+
try {
|
|
38
|
+
return await change(brain);
|
|
39
|
+
} finally {
|
|
40
|
+
await store.release(lease);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Move the complete local runtime directory without copying or traversing its contents. */
|
|
45
|
+
export function moveRepositoryPluginState(brain, id, direction) {
|
|
46
|
+
if (!brain) return { ok: true, state: 'none' };
|
|
47
|
+
const live = ['plugin-state'];
|
|
48
|
+
const archive = ['config', 'plugin-state-archive'];
|
|
49
|
+
const restoring = direction === 'restore';
|
|
50
|
+
const source = directory(brain, [...(restoring ? archive : live), id]);
|
|
51
|
+
const destination = restoring ? live : archive;
|
|
52
|
+
const existing = directory(brain, [...destination, id]);
|
|
53
|
+
if (source && existing) throw new Error('Active and archived plugin state both exist. Both copies were preserved.');
|
|
54
|
+
if (!source) return { ok: true, state: !restoring && existing ? 'already-archived' : 'none' };
|
|
55
|
+
const parent = directory(brain, destination, true);
|
|
56
|
+
fs.renameSync(source, path.join(parent, id));
|
|
57
|
+
return { ok: true, state: restoring ? 'restored' : 'archived' };
|
|
58
|
+
}
|
|
@@ -5,7 +5,8 @@ import { fileURLToPath } from 'node:url';
|
|
|
5
5
|
import { validateAppCliSpec } from './plugin-contracts/app-cli.mjs';
|
|
6
6
|
import { validateUiSpec } from './plugin-contracts/ui.mjs';
|
|
7
7
|
import { validateConfigurationSpec } from './plugin-contracts/configuration.mjs';
|
|
8
|
-
import {
|
|
8
|
+
import { validateManifestContract } from './plugin-contracts/manifest.mjs';
|
|
9
|
+
import { validateStartupCheck } from './plugin-contracts/startup-check.mjs';
|
|
9
10
|
|
|
10
11
|
const ID_PATTERN = /^[a-z][a-z0-9-]{1,63}$/;
|
|
11
12
|
const VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
@@ -24,6 +25,8 @@ export function projectPluginsDir(projectRoot = process.cwd()) {
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
export function globalPluginsDir() {
|
|
28
|
+
// A staged server can use its matching plugin packages with the same brain and Auth store.
|
|
29
|
+
if (process.env.TR_RUNTIME_PLUGINS_DIR) return path.resolve(process.env.TR_RUNTIME_PLUGINS_DIR);
|
|
27
30
|
const agentDir = process.env.AGENT_DIR || process.env._TR_TEST_AGENT_DIR || path.join(os.homedir(), '.agent');
|
|
28
31
|
return path.join(agentDir, 'skills', 'total-recall', 'plugins');
|
|
29
32
|
}
|
|
@@ -48,7 +51,7 @@ export function resolveProjectRoot(projectRoot, vaultDir) {
|
|
|
48
51
|
return fs.existsSync(path.join(parent, '.agent')) ? parent : (projectRoot || process.cwd());
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
/** True when a 5-field cron expression is syntactically plausible.
|
|
54
|
+
/** True when a 5-field cron expression is syntactically plausible. The scheduling owner checks semantics. */
|
|
52
55
|
export function isCronExpression(expr) {
|
|
53
56
|
if (typeof expr !== 'string') return false;
|
|
54
57
|
const fields = expr.trim().split(/\s+/);
|
|
@@ -94,8 +97,9 @@ export function validatePluginManifest(manifest) {
|
|
|
94
97
|
return { valid: false, errors: ['Manifest must be a non-null object'] };
|
|
95
98
|
}
|
|
96
99
|
if (manifest.configuration !== undefined) errors.push(...validateConfigurationSpec(manifest.configuration));
|
|
97
|
-
const
|
|
98
|
-
errors.push(...
|
|
100
|
+
const contract = validateManifestContract(manifest);
|
|
101
|
+
errors.push(...contract.errors);
|
|
102
|
+
errors.push(...validateStartupCheck(manifest));
|
|
99
103
|
|
|
100
104
|
if (!manifest.id || typeof manifest.id !== 'string') {
|
|
101
105
|
errors.push("Missing required field 'id'");
|
|
@@ -277,6 +281,13 @@ export function validatePluginManifest(manifest) {
|
|
|
277
281
|
if (!/^[a-z][a-z0-9-]*$/.test(name) || !isSafeRelativePath(file)) errors.push(`Invalid runtime module ${name}`);
|
|
278
282
|
}
|
|
279
283
|
if (runtime.routers !== undefined && (!Array.isArray(runtime.routers) || runtime.routers.some(file => !isSafeRelativePath(file)))) errors.push('runtime.routers must contain safe relative paths');
|
|
284
|
+
// Daemon services name declared runtime modules; the daemon starts each one.
|
|
285
|
+
if (runtime.daemon !== undefined) {
|
|
286
|
+
if (!Array.isArray(runtime.daemon)) errors.push('runtime.daemon must be an array of runtime module names');
|
|
287
|
+
else for (const name of runtime.daemon) {
|
|
288
|
+
if (typeof name !== 'string' || !Object.hasOwn(runtime.modules || {}, name)) errors.push(`runtime.daemon names undeclared module ${name}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
280
291
|
}
|
|
281
292
|
}
|
|
282
293
|
|
|
@@ -325,7 +336,7 @@ export function validatePluginManifest(manifest) {
|
|
|
325
336
|
return {
|
|
326
337
|
valid: errors.length === 0,
|
|
327
338
|
errors,
|
|
328
|
-
warnings:
|
|
339
|
+
warnings: contract.warnings
|
|
329
340
|
};
|
|
330
341
|
}
|
|
331
342
|
|
|
@@ -405,15 +416,17 @@ export function readPluginsDir(dir, scope) {
|
|
|
405
416
|
/**
|
|
406
417
|
* Discover installed plugins across project and global directories.
|
|
407
418
|
* A project plugin shadows a global plugin with the same id.
|
|
408
|
-
* @param {string} [projectRoot] - Current project root
|
|
419
|
+
* @param {string|null} [projectRoot] - Current project root, or null for only shared and bundled plugins
|
|
409
420
|
*/
|
|
410
421
|
export function discoverPlugins(projectRoot = process.cwd()) {
|
|
411
422
|
const seen = new Set();
|
|
412
423
|
const plugins = [];
|
|
413
424
|
const global = globalPluginsDir();
|
|
414
|
-
|
|
425
|
+
// Nothing is installed per repository: every plugin lives in the shared
|
|
426
|
+
// folder, and a repository only switches it on or off. A leftover repository
|
|
427
|
+
// plugin folder is never read (`plugin copies` reports and removes it).
|
|
428
|
+
// `projectRoot` stays in the signature for callers; discovery no longer depends on it.
|
|
415
429
|
const sources = [
|
|
416
|
-
...(path.resolve(project) === path.resolve(global) ? [] : [[project, 'project']]),
|
|
417
430
|
[global, 'global'],
|
|
418
431
|
[path.join(PACKAGE_ROOT, 'scaffold', '.agent', 'skills', 'total-recall', 'plugins'), 'scaffold']
|
|
419
432
|
];
|
|
@@ -9,10 +9,10 @@ import { manifestHelp } from './plugin-help.mjs';
|
|
|
9
9
|
import { spawn } from 'node:child_process';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
|
-
import { PACKAGE_ROOT, resolvePluginFile } from './plugin-loader.mjs';
|
|
13
|
-
import { readPluginRecord } from './plugin-store.mjs';
|
|
12
|
+
import { PACKAGE_ROOT, projectPluginsDir, resolvePluginFile } from './plugin-loader.mjs';
|
|
13
|
+
import { readPluginRecord, recordPluginsDir } from './plugin-store.mjs';
|
|
14
14
|
import { pluginExecutionEnabled } from './plugin-state.mjs';
|
|
15
|
-
import { setPluginActive } from './plugin-activation.mjs';
|
|
15
|
+
import { activatePluginOnUse, setPluginActive } from './plugin-activation.mjs';
|
|
16
16
|
|
|
17
17
|
const CHILD = path.join(path.dirname(fileURLToPath(import.meta.url)), 'plugin-runner-child.mjs');
|
|
18
18
|
export const DEFAULT_TIMEOUT_MS = 30_000;
|
|
@@ -39,11 +39,12 @@ export function validatePluginSecretInput(plugin, subcommand, input) {
|
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* @param {object} plugin A discovered plugin ({ id, dir, manifest }).
|
|
42
|
-
* @param {{ subcommand?: string, args?: string[], cwd?: string, timeoutMs?: number }} [options]
|
|
42
|
+
* @param {{ subcommand?: string, args?: string[], cwd?: string, projectRoot?: string|null, timeoutMs?: number }} [options]
|
|
43
43
|
* @returns {Promise<{ ok: boolean, exitCode: number|null, signal: string|null, timedOut: boolean, truncated: boolean, output: string, durationMs: number }>}
|
|
44
44
|
*/
|
|
45
45
|
export function runPluginCommand(plugin, options = {}) {
|
|
46
46
|
const { subcommand = '', args = [], cwd = process.cwd(), timeoutMs: requestedTimeoutMs, stdin = 'ignore', stdinText, stdinRequest, composable = false, secretsBrainDir } = options;
|
|
47
|
+
const { projectRoot = cwd } = options;
|
|
47
48
|
const help = manifestHelp(plugin, { subcommand, args });
|
|
48
49
|
if (help !== null) return Promise.resolve({ ok: true, exitCode: 0, stdout: help, stderr: '', output: help, durationMs: 0, timedOut: false });
|
|
49
50
|
const maxOutputBytes = options.maxOutputBytes ?? (stdin === 'inherit' ? CLI_MAX_OUTPUT_BYTES : MAX_OUTPUT_BYTES);
|
|
@@ -58,19 +59,26 @@ export function runPluginCommand(plugin, options = {}) {
|
|
|
58
59
|
const stdinPayload = stdinText ?? stdinRequest;
|
|
59
60
|
const operation = subcommand || args[0];
|
|
60
61
|
if (operation === 'on' || operation === 'off') {
|
|
61
|
-
return setPluginActive(plugin.id, operation === 'on', { projectRoot: cwd }).then(result => ({
|
|
62
|
-
ok: result.compilation.ok, exitCode: result.compilation.ok ? 0 : 1,
|
|
62
|
+
return setPluginActive(plugin.id, operation === 'on', { projectRoot: projectRoot ?? cwd }).then(result => ({
|
|
63
|
+
ok: result.compilation.ok && result.cleanup?.ok !== false, exitCode: result.compilation.ok && result.cleanup?.ok !== false ? 0 : 1,
|
|
63
64
|
output: JSON.stringify(result), durationMs: 0, timedOut: false,
|
|
64
65
|
}));
|
|
65
66
|
}
|
|
66
|
-
|
|
67
|
-
return Promise.reject(new Error(`Plugin ${plugin.id} is inactive; run total-recall plugin on ${plugin.id}`));
|
|
68
|
-
}
|
|
67
|
+
const passive = ['status', 'health', 'jobs', '--help', '-h', 'help'].includes(operation);
|
|
69
68
|
// Manifests declare this as `timeoutMs`; the runner only read `timeout_ms`, so every declared
|
|
70
69
|
// timeout was ignored and each command fell back to DEFAULT_TIMEOUT_MS (30s). That killed work
|
|
71
70
|
// that legitimately takes longer — `mesh nodes` and `tr-transcribe transcribe` (model load) both
|
|
72
71
|
// died at 30s while their manifests asked for far more. Accept both spellings.
|
|
73
72
|
const declaration = plugin?.manifest?.cli?.subcommands?.find(item => item.name === operation);
|
|
73
|
+
if (!pluginExecutionEnabled(plugin, { projectRoot }) && !passive) {
|
|
74
|
+
if (options.activateOnUse && declaration && !args.includes('--help') && !args.includes('-h')) {
|
|
75
|
+
return activatePluginOnUse(plugin.id, { projectRoot }).then(result => {
|
|
76
|
+
if (result && !result.compilation.ok) throw new Error('Plugin was activated, but the repository instructions could not be refreshed.');
|
|
77
|
+
return runPluginCommand(plugin, { ...options, activateOnUse: false });
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return Promise.reject(new Error(`Plugin ${plugin.id} is inactive; run total-recall plugin on ${plugin.id}`));
|
|
81
|
+
}
|
|
74
82
|
const declaredTimeout = declaration?.timeout_ms ?? declaration?.timeoutMs;
|
|
75
83
|
const timeoutMs = requestedTimeoutMs ?? declaredTimeout ?? DEFAULT_TIMEOUT_MS;
|
|
76
84
|
if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 600000) return Promise.reject(new Error('Plugin timeout must be 1..600000 ms'));
|
|
@@ -85,6 +93,8 @@ export function runPluginCommand(plugin, options = {}) {
|
|
|
85
93
|
const safeArgs = (Array.isArray(args) ? args : []).map(String);
|
|
86
94
|
if (stdinPayload !== undefined && !safeArgs.includes('--stdin')) safeArgs.push('--stdin');
|
|
87
95
|
const childArgv = [CHILD, handler, command, ...(subcommand ? [String(subcommand)] : []), ...safeArgs];
|
|
96
|
+
// Only an explicitly selected project owns repository state. The cwd fallback above preserves legacy activation.
|
|
97
|
+
const statePluginsDir = options.projectRoot ? projectPluginsDir(projectRoot) : recordPluginsDir(plugin);
|
|
88
98
|
|
|
89
99
|
return new Promise((resolve) => {
|
|
90
100
|
const started = Date.now();
|
|
@@ -101,10 +111,11 @@ export function runPluginCommand(plugin, options = {}) {
|
|
|
101
111
|
env: {
|
|
102
112
|
...process.env,
|
|
103
113
|
TR_PACKAGE_ROOT: PACKAGE_ROOT,
|
|
114
|
+
TOTAL_RECALL_BIN: path.join(PACKAGE_ROOT, 'bin', 'total-recall.mjs'),
|
|
104
115
|
TR_PLUGIN_ID: plugin.id,
|
|
105
116
|
TR_PLUGIN_DIR: plugin.dir,
|
|
106
117
|
TR_PLUGIN_CONFIG: JSON.stringify(readPluginRecord(plugin)?.configuration || {}),
|
|
107
|
-
...(
|
|
118
|
+
...(statePluginsDir ? { TR_PLUGIN_STATE_DIR: path.join(path.dirname(statePluginsDir), 'plugin-state', plugin.id) } : {}),
|
|
108
119
|
...(secretsBrainDir ? { TR_SECRETS_BRAIN: secretsBrainDir } : {}),
|
|
109
120
|
TR_PLUGIN_COMMAND_MODE: composable ? 'composable' : 'cli',
|
|
110
121
|
FORCE_COLOR: '0',
|
|
@@ -1,7 +1,45 @@
|
|
|
1
1
|
/** Generic in-process extension loading. Domain behavior stays with its owner. */
|
|
2
|
+
import os from 'node:os';
|
|
2
3
|
import { pathToFileURL } from 'node:url';
|
|
3
4
|
import { discoverPlugins, resolvePluginFile, PACKAGE_ROOT } from './plugin-loader.mjs';
|
|
4
5
|
import { pluginActivation } from './plugin-state.mjs';
|
|
6
|
+
import { logger } from './logger.mjs';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Start every daemon service an active plugin declares in `runtime.daemon`.
|
|
10
|
+
* Each named runtime module exports `start({ projectRoot })` and may return a
|
|
11
|
+
* stop function. Services run against the global brain (home), never the
|
|
12
|
+
* directory the daemon happens to start in. One failing service is logged and
|
|
13
|
+
* never stops the others.
|
|
14
|
+
* @returns {Promise<{ started: string[], failed: string[], stop: Function }>}
|
|
15
|
+
*/
|
|
16
|
+
export async function startPluginDaemonServices({ projectRoot = os.homedir(), load = loadPluginModule } = {}) {
|
|
17
|
+
const stops = [];
|
|
18
|
+
const started = [];
|
|
19
|
+
const failed = [];
|
|
20
|
+
for (const plugin of discoverPlugins(projectRoot).filter(item => item.valid)) {
|
|
21
|
+
const names = plugin.manifest.runtime?.daemon || [];
|
|
22
|
+
if (!names.length || !pluginActivation(plugin, { projectRoot }).enabled) continue;
|
|
23
|
+
for (const name of names) {
|
|
24
|
+
const label = `${plugin.id}:${name}`;
|
|
25
|
+
try {
|
|
26
|
+
const module = await load(plugin.id, name, { projectRoot, activeRequired: true });
|
|
27
|
+
if (typeof module.start !== 'function') throw new Error('daemon module must export start()');
|
|
28
|
+
const stop = await module.start({ projectRoot });
|
|
29
|
+
if (typeof stop === 'function') stops.push(stop);
|
|
30
|
+
started.push(label);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
failed.push(label);
|
|
33
|
+
logger.warn('plugin-runtime', `Daemon service ${label} failed to start: ${err.message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
started,
|
|
39
|
+
failed,
|
|
40
|
+
stop: () => { for (const stop of stops) { try { stop(); } catch { /* already stopped */ } } },
|
|
41
|
+
};
|
|
42
|
+
}
|
|
5
43
|
|
|
6
44
|
export async function loadPluginModule(id, name, { activeRequired = false, requireValid = true, projectRoot = process.cwd() } = {}) {
|
|
7
45
|
const plugin = discoverPlugins(projectRoot).find(item => item.id === id);
|
|
@@ -18,7 +56,8 @@ export async function loadPluginModule(id, name, { activeRequired = false, requi
|
|
|
18
56
|
}
|
|
19
57
|
process.env.TR_PACKAGE_ROOT = PACKAGE_ROOT;
|
|
20
58
|
const file = resolvePluginFile(plugin.dir, relative);
|
|
21
|
-
|
|
59
|
+
// A file URL is unambiguous on every platform and under every module loader.
|
|
60
|
+
return import(pathToFileURL(file).href);
|
|
22
61
|
}
|
|
23
62
|
|
|
24
63
|
export async function loadPluginRouters() {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run the read commands installed plugins contribute to `startup check` and
|
|
3
|
+
* `brief` (`startup_check` in plugin.json). Core runs them and reports what
|
|
4
|
+
* they say; the domain knowledge stays in the plugin. A check that fails,
|
|
5
|
+
* times out or prints the wrong shape is reported as unavailable, never as
|
|
6
|
+
* clean.
|
|
7
|
+
*/
|
|
8
|
+
import { discoverPlugins } from './plugin-loader.mjs';
|
|
9
|
+
import { runPluginCommand } from './plugin-runner.mjs';
|
|
10
|
+
import { pluginExecutionEnabled } from './plugin-state.mjs';
|
|
11
|
+
import { readStartupCheckOutput, STARTUP_CHECK_MAX_TIMEOUT_MS } from './plugin-contracts/startup-check.mjs';
|
|
12
|
+
|
|
13
|
+
const DEFAULT_TIMEOUT_MS = 15000;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {{ cwd?: string, plugins?: object[], run?: Function, enabled?: Function }} [options]
|
|
17
|
+
* @returns {Promise<Array<{ plugin: string, command: string, status: string, outstanding?: number, summary?: string, reason?: string }>>}
|
|
18
|
+
*/
|
|
19
|
+
export async function runPluginStartupChecks({ cwd = process.cwd(), plugins, run = runPluginCommand, enabled = pluginExecutionEnabled } = {}) {
|
|
20
|
+
let found;
|
|
21
|
+
try { found = plugins ?? discoverPlugins(cwd); } catch (error) { return [{ plugin: '*', command: 'plugin discovery', status: 'unavailable', reason: error.message }]; }
|
|
22
|
+
const declared = found.filter((plugin) => plugin.valid && plugin.manifest?.startup_check);
|
|
23
|
+
return Promise.all(declared.map(async (plugin) => {
|
|
24
|
+
const check = plugin.manifest.startup_check;
|
|
25
|
+
const command = `${plugin.manifest.cli?.command || plugin.id} ${check.command}`;
|
|
26
|
+
try {
|
|
27
|
+
if (!enabled(plugin, { projectRoot: cwd })) return { plugin: plugin.id, command, status: 'inactive' };
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return { plugin: plugin.id, command, status: 'unavailable', reason: `activation policy unreadable: ${error.message}` };
|
|
30
|
+
}
|
|
31
|
+
const timeoutMs = Math.min(check.timeout_ms ?? DEFAULT_TIMEOUT_MS, STARTUP_CHECK_MAX_TIMEOUT_MS);
|
|
32
|
+
try {
|
|
33
|
+
const result = await run(plugin, { subcommand: check.command, args: check.args || [], cwd, timeoutMs });
|
|
34
|
+
if (result.timedOut) return { plugin: plugin.id, command, status: 'unavailable', reason: `timed out after ${timeoutMs} ms` };
|
|
35
|
+
const read = readStartupCheckOutput(result.stdout);
|
|
36
|
+
if (read.status !== 'unavailable' && result.exitCode !== 0) {
|
|
37
|
+
return { plugin: plugin.id, command, ...read, status: 'unavailable', reason: `exited ${result.exitCode}` };
|
|
38
|
+
}
|
|
39
|
+
return { plugin: plugin.id, command, ...read };
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return { plugin: plugin.id, command, status: 'unavailable', reason: error.message };
|
|
42
|
+
}
|
|
43
|
+
}));
|
|
44
|
+
}
|