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,24 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
import type { PluginInfo } from '../../api/plugins'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const inlineJson = (value: unknown) => JSON.stringify(value).replace(/</g, '\\u003c')
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
return
|
|
10
|
-
if (!item || typeof item !== 'object' || typeof item.id !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(item.id)
|
|
11
|
-
// eslint-disable-next-line no-control-regex -- rejects labels that contain control characters
|
|
12
|
-
|| typeof item.label !== 'string' || !item.label.trim() || item.label.length > 160 || /[\u0000-\u001f\u007f]/.test(item.label) || seen.has(item.id)) return []
|
|
13
|
-
seen.add(item.id)
|
|
14
|
-
return [{ id: item.id, label: item.label.trim() }]
|
|
15
|
-
})
|
|
5
|
+
export function browserPluginOperations(plugin: PluginInfo) {
|
|
6
|
+
const manifestCli = plugin.manifest?.cli as { subcommands?: NonNullable<PluginInfo['cli']>['subcommands'] } | undefined
|
|
7
|
+
const blocked = (command: { sensitive_output?: boolean; terminal_only?: boolean }) => command.sensitive_output === true || command.terminal_only === true
|
|
8
|
+
const excluded = new Set(manifestCli?.subcommands?.filter(blocked).map(command => command.name))
|
|
9
|
+
return (plugin.cli?.subcommands || []).filter(command => !blocked(command) && !excluded.has(command.name))
|
|
16
10
|
}
|
|
17
11
|
|
|
18
12
|
/** Owner code runs in an opaque origin, with no dashboard DOM/storage or direct network. */
|
|
19
|
-
export function pluginFrameDocument(source: string, tag: string, channel: string, theme: Record<string, string> = {}, pluginId = '', commandTimeouts: Record<string, number> = {}, operations: readonly string[] = []) {
|
|
13
|
+
export function pluginFrameDocument(source: string, tag: string, channel: string, theme: Record<string, string> = {}, pluginId = '', commandTimeouts: Record<string, number> = {}, operations: readonly string[] = [], credentialPanel = false, authInventory = false, authChanges = false) {
|
|
20
14
|
return `<!doctype html><html><head>
|
|
21
|
-
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'nonce-${channel}' blob:; style-src 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com data:; img-src data: https
|
|
15
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'nonce-${channel}' blob:; style-src 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com data:; img-src data:${authChanges ? '' : ' https:'}; connect-src 'none'; frame-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'">
|
|
22
16
|
<meta name="referrer" content="no-referrer">
|
|
23
17
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
24
18
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
@@ -27,20 +21,16 @@ export function pluginFrameDocument(source: string, tag: string, channel: string
|
|
|
27
21
|
</head><body><script nonce="${channel}" type="module">
|
|
28
22
|
const channel=${inlineJson(channel)};
|
|
29
23
|
const pending=new Map();let sequence=0;
|
|
24
|
+
const baseStyle=document.createElement('style');
|
|
25
|
+
const designStyle=document.createElement('style');
|
|
26
|
+
baseStyle.textContent=':root{}';
|
|
27
|
+
document.head.append(baseStyle,designStyle);
|
|
30
28
|
const send=(type,extra={})=>parent.postMessage({channel,type,...extra},'*');
|
|
31
|
-
|
|
32
|
-
const sanitizeScreens=${sanitizePluginScreens.toString()};
|
|
33
|
-
function publishScreens(element){
|
|
34
|
-
ownerScreens=sanitizeScreens(element.screens);
|
|
35
|
-
send('screens',{screens:ownerScreens});
|
|
36
|
-
if(ownerScreens.some(screen=>screen.id===element.screen))host.screenChanged(element.screen);
|
|
37
|
-
}
|
|
38
|
-
window.addEventListener('contextmenu',event=>{event.preventDefault();send('contextmenu');});
|
|
39
|
-
for(const [name,value] of Object.entries(${inlineJson(theme)}))document.documentElement.style.setProperty(name,value);
|
|
29
|
+
for(const [name,value] of Object.entries(${inlineJson(theme)}))baseStyle.sheet.cssRules[0].style.setProperty(name,value);
|
|
40
30
|
window.addEventListener('message',event=>{
|
|
41
31
|
if(event.source!==parent||event.data?.channel!==channel)return;
|
|
42
|
-
if(event.data.type==='
|
|
43
|
-
|
|
32
|
+
if(event.data.type==='design'&&typeof event.data.css==='string'){
|
|
33
|
+
designStyle.textContent=event.data.css;
|
|
44
34
|
return;
|
|
45
35
|
}
|
|
46
36
|
if(event.data.type!=='result')return;
|
|
@@ -48,22 +38,43 @@ window.addEventListener('message',event=>{
|
|
|
48
38
|
pending.delete(event.data.requestId);clearTimeout(request.timer);
|
|
49
39
|
if(event.data.error)request.reject(new Error(event.data.error));else request.resolve(event.data.result);
|
|
50
40
|
});
|
|
51
|
-
|
|
41
|
+
function request(type, extra, timeout=30000){return new Promise((resolve,reject)=>{
|
|
52
42
|
const requestId=String(++sequence);
|
|
43
|
+
const timer=setTimeout(()=>{pending.delete(requestId);reject(new Error('Operation timed out'));},timeout+5000);
|
|
44
|
+
pending.set(requestId,{resolve,reject,timer});send(type,{requestId,...extra});
|
|
45
|
+
});}
|
|
46
|
+
const host=Object.freeze({pluginId:${inlineJson(pluginId)},operations:Object.freeze(${inlineJson(operations)}),
|
|
47
|
+
...(${authInventory}?{readInventory:resource=>request('auth-inventory',{resource})}:{}),
|
|
48
|
+
...(${authChanges}?{changeCredential:change=>request('auth-credential-mutate',{change})}:{}),
|
|
49
|
+
run(subcommand,args=[],options={}){
|
|
53
50
|
const declared=${inlineJson(commandTimeouts)}[subcommand];
|
|
54
51
|
const timeout=Number.isInteger(declared)&&declared>=1&&declared<=600000?declared:30000;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
});}});
|
|
52
|
+
return request('run',{subcommand,args,...(options.stdin!==undefined?{stdin:options.stdin}:{})},timeout);
|
|
53
|
+
}});
|
|
58
54
|
try{
|
|
59
55
|
const url=URL.createObjectURL(new Blob([${inlineJson(source)}],{type:'text/javascript'}));
|
|
60
56
|
try{await import(url);}finally{URL.revokeObjectURL(url);}
|
|
61
57
|
const tag=${inlineJson(tag)};
|
|
62
58
|
if(!customElements.get(tag))throw new Error('The plugin did not register its declared interface');
|
|
63
|
-
const element=document.createElement(tag);element.host=host;
|
|
64
|
-
|
|
59
|
+
const element=document.createElement(tag);element.host=host;
|
|
60
|
+
if(${credentialPanel}){
|
|
61
|
+
const message=document.createElement('p');message.setAttribute('role','status');
|
|
62
|
+
message.textContent='Checking saved credentials…';document.body.append(message);
|
|
63
|
+
try{
|
|
64
|
+
element.view=await request('credential-status',{});
|
|
65
|
+
message.textContent='';
|
|
66
|
+
element.addEventListener('save-token',async event=>{
|
|
67
|
+
message.textContent='Checking the credential…';
|
|
68
|
+
try{
|
|
69
|
+
element.view=await request('credential-save',{key:event.detail?.key,value:event.detail?.token});
|
|
70
|
+
message.textContent='Credential saved.';
|
|
71
|
+
}catch(error){message.textContent=error.message;}
|
|
72
|
+
});
|
|
73
|
+
document.body.append(element);
|
|
74
|
+
}catch(error){message.textContent=error.message;}
|
|
75
|
+
}else document.body.append(element);
|
|
65
76
|
if(typeof element.load==='function'&&!element.hasAttribute('data-loaded')){try{element.load();}catch(_){}}
|
|
66
|
-
if('view' in element||tag==='domains-console'||tag==='stripe-panel'){
|
|
77
|
+
if(!${credentialPanel}&&('view' in element||tag==='domains-console'||tag==='stripe-panel')){
|
|
67
78
|
host.run('view').then(v=>{element.view=v;}).catch(()=>{
|
|
68
79
|
host.run('status').then(s=>{element.view=s;}).catch(()=>{});
|
|
69
80
|
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { PluginInfo } from '../../api/plugins'
|
|
2
|
+
import { browserPluginOperations } from './plugin-frame'
|
|
3
|
+
|
|
4
|
+
export type Argument = {
|
|
5
|
+
name: string
|
|
6
|
+
label: string
|
|
7
|
+
flag?: string
|
|
8
|
+
type: 'string' | 'number' | 'boolean' | 'choice'
|
|
9
|
+
options?: string[]
|
|
10
|
+
required?: boolean
|
|
11
|
+
secret?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type Command = ReturnType<typeof browserPluginOperations>[number] & {
|
|
15
|
+
effect?: 'read' | 'write' | 'destructive' | 'send'
|
|
16
|
+
args?: Argument[]
|
|
17
|
+
terminal_only_args?: string[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function scopedPluginOperations(plugin: PluginInfo, operationIds?: readonly string[]): Command[] {
|
|
21
|
+
const manifest = plugin.manifest?.cli as { subcommands?: Command[] } | undefined
|
|
22
|
+
const declared = new Map(manifest?.subcommands?.map(operation => [operation.name, operation]))
|
|
23
|
+
return browserPluginOperations(plugin)
|
|
24
|
+
.map(operation => ({ ...operation, ...declared.get(operation.name) }))
|
|
25
|
+
.filter(operation => operation.name !== 'on' && operation.name !== 'off' &&
|
|
26
|
+
(!operationIds || operationIds.includes(operation.name)) &&
|
|
27
|
+
!operation.args?.some(arg => arg.secret || (arg.required && operation.terminal_only_args?.includes(arg.flag || ''))))
|
|
28
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState, type FormEvent } from 'react'
|
|
2
|
+
import { runPluginCommand, type PluginInfo } from '../../api/plugins'
|
|
3
|
+
import { parsePluginOutput } from '../plugins/PluginPanel'
|
|
4
|
+
|
|
5
|
+
interface Harness { id: string; name: string }
|
|
6
|
+
interface ChatTarget {
|
|
7
|
+
ok: boolean
|
|
8
|
+
configured: boolean
|
|
9
|
+
harness?: Harness
|
|
10
|
+
href?: string
|
|
11
|
+
context?: string
|
|
12
|
+
choices?: Harness[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function launchHref(value?: string) {
|
|
16
|
+
try {
|
|
17
|
+
if (!value) return undefined
|
|
18
|
+
const url = new URL(value)
|
|
19
|
+
if (url.username || url.password) return undefined
|
|
20
|
+
if (url.protocol === 'https:') return url.href
|
|
21
|
+
if (url.protocol === 'codex:' && url.hostname === 'threads' && /^\/(?:new|[0-9a-f-]{36})$/i.test(url.pathname)) return url.href
|
|
22
|
+
} catch { /* An invalid configuration is shown without a launch link. */ }
|
|
23
|
+
return undefined
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function DrawerChat({ plugin, activeBrainId }: { plugin?: PluginInfo; activeBrainId: string }) {
|
|
27
|
+
const [target, setTarget] = useState<ChatTarget | null>(null)
|
|
28
|
+
const [error, setError] = useState('')
|
|
29
|
+
const [edit, setEdit] = useState(false)
|
|
30
|
+
const [harness, setHarness] = useState('')
|
|
31
|
+
const [url, setUrl] = useState('')
|
|
32
|
+
const [saving, setSaving] = useState(false)
|
|
33
|
+
const [copied, setCopied] = useState(false)
|
|
34
|
+
const hasTarget = !!plugin?.valid && plugin.activation?.enabled === true &&
|
|
35
|
+
!!plugin.cli?.subcommands?.some(command => command.name === 'chat-target')
|
|
36
|
+
const getTarget = useCallback(async () => {
|
|
37
|
+
const result = await runPluginCommand('meta-harness', 'chat-target', ['--brains', activeBrainId, '--json'])
|
|
38
|
+
const data = parsePluginOutput(result.output) as Partial<ChatTarget> | undefined
|
|
39
|
+
if (!result.success || data?.ok !== true || typeof data.configured !== 'boolean') {
|
|
40
|
+
throw new Error('Could not read the default harness from Metaharness.')
|
|
41
|
+
}
|
|
42
|
+
return data as ChatTarget
|
|
43
|
+
}, [activeBrainId])
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!hasTarget) return
|
|
46
|
+
let current = true
|
|
47
|
+
void getTarget().then(data => { if (current) { setTarget(data); setError(''); setCopied(false) } },
|
|
48
|
+
error => { if (current) { setTarget(null); setError(error.message) } })
|
|
49
|
+
return () => { current = false }
|
|
50
|
+
}, [getTarget, hasTarget])
|
|
51
|
+
|
|
52
|
+
async function save(event: FormEvent<HTMLFormElement>) {
|
|
53
|
+
event.preventDefault()
|
|
54
|
+
setSaving(true)
|
|
55
|
+
setError('')
|
|
56
|
+
try {
|
|
57
|
+
const response = await runPluginCommand('meta-harness', 'chat-default', [
|
|
58
|
+
harness, ...(url.trim() ? ['--url', url.trim()] : []), '--json',
|
|
59
|
+
])
|
|
60
|
+
const data = parsePluginOutput(response.output) as { ok?: boolean; error?: string } | undefined
|
|
61
|
+
if (!response.success || data?.ok !== true) throw new Error(data?.error || response.error || 'Could not save the default harness.')
|
|
62
|
+
setTarget(await getTarget())
|
|
63
|
+
setEdit(false)
|
|
64
|
+
} catch (error) { setError(error instanceof Error ? error.message : 'Could not save the default harness.') }
|
|
65
|
+
finally { setSaving(false) }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!hasTarget) return <p className="drawer-note">
|
|
69
|
+
{!plugin ? 'Install Metaharness to open Chat.' : plugin.activation?.enabled !== true
|
|
70
|
+
? 'Enable Metaharness to open Chat.' : 'This Metaharness version does not provide a default Chat harness yet.'}
|
|
71
|
+
</p>
|
|
72
|
+
|
|
73
|
+
const href = launchHref(target?.href)
|
|
74
|
+
const selectedCount = activeBrainId.split(',').filter(Boolean).length
|
|
75
|
+
return <section className="drawer-stack">
|
|
76
|
+
<div className="drawer-row">
|
|
77
|
+
<div>
|
|
78
|
+
<h3>{target?.harness?.name || 'Default harness'}</h3>
|
|
79
|
+
<p className="drawer-note">{selectedCount === 1 ? '1 brain selected' : `${selectedCount} brains selected`}</p>
|
|
80
|
+
</div>
|
|
81
|
+
{target && <button type="button" className="btn btn-ghost btn-sm" onClick={() => {
|
|
82
|
+
setHarness(target.harness?.id || ''); setUrl(''); setEdit(!edit)
|
|
83
|
+
}}>{edit ? 'Cancel' : 'Configure default'}</button>}
|
|
84
|
+
</div>
|
|
85
|
+
{error && <p role="alert" className="drawer-error">{error}</p>}
|
|
86
|
+
{!target && !error && <p role="status">Loading the default harness…</p>}
|
|
87
|
+
{edit && <form className="drawer-stack" onSubmit={save}>
|
|
88
|
+
<label>Harness
|
|
89
|
+
<select className="select" value={harness} onChange={event => setHarness(event.target.value)} required>
|
|
90
|
+
<option value="">Choose a harness…</option>
|
|
91
|
+
{target?.choices?.map(item => <option key={item.id} value={item.id}>{item.name}</option>)}
|
|
92
|
+
</select>
|
|
93
|
+
</label>
|
|
94
|
+
<label>Custom launch URL (optional)<input className="input" type="text" value={url}
|
|
95
|
+
placeholder="Use the harness default" onChange={event => setUrl(event.target.value)} /></label>
|
|
96
|
+
<div><button className="btn btn-primary" disabled={saving || !harness}>{saving ? 'Saving…' : 'Save default'}</button></div>
|
|
97
|
+
</form>}
|
|
98
|
+
{!edit && target && (target.configured ? <>
|
|
99
|
+
<div className="drawer-actions">
|
|
100
|
+
{href && <a className="btn btn-primary" href={href} rel="noreferrer" target={href.startsWith('https:') ? '_blank' : undefined}>Open Chat</a>}
|
|
101
|
+
{target.context && <button type="button" className="btn btn-ghost" onClick={async () => {
|
|
102
|
+
try { await navigator.clipboard.writeText(target.context!); setCopied(true) }
|
|
103
|
+
catch { setError('Copy the selected brain context below.') }
|
|
104
|
+
}}>{copied ? 'Copied' : 'Copy brain context'}</button>}
|
|
105
|
+
</div>
|
|
106
|
+
{!href && <p className="drawer-note">This harness has no launch URL. Open it directly and paste the selected brain context.</p>}
|
|
107
|
+
{target.context && <details><summary>Selected brain context</summary><pre className="drawer-context">{target.context}</pre></details>}
|
|
108
|
+
</> : <p className="drawer-note">Choose the harness Chat should open for your selected brains.</p>)}
|
|
109
|
+
</section>
|
|
110
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import { Link } from 'react-router-dom'
|
|
3
|
+
import type { PluginInfo } from '../../api/plugins'
|
|
4
|
+
import {
|
|
5
|
+
findWorkspaceChunks, readDrawer, readWorkspaceChunk, saveDrawer,
|
|
6
|
+
type DrawerConfig, type DrawerRevision, type DrawerSide, type PanelRef, type PluginDesign, type WorkspaceChunk,
|
|
7
|
+
} from '../../api/workspace-drawers'
|
|
8
|
+
import { PluginPanel, pluginElements } from '../plugins/PluginPanel'
|
|
9
|
+
import { ScopedPluginCommands } from '../plugins/ScopedPluginCommands'
|
|
10
|
+
import { DrawerMarkup } from './DrawerMarkup'
|
|
11
|
+
import { usePluginDesign } from './usePluginDesign'
|
|
12
|
+
|
|
13
|
+
export function drawerPanels(plugin: PluginInfo) {
|
|
14
|
+
return pluginElements(plugin).filter(item => item.kind !== 'settings')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function DrawerPanel({ plugins, reference, designCss, brainId }: {
|
|
18
|
+
plugins: PluginInfo[] | null; reference: PanelRef; designCss?: string; brainId?: string
|
|
19
|
+
}) {
|
|
20
|
+
if (!plugins) return <p role="status">Loading installed interfaces…</p>
|
|
21
|
+
const plugin = plugins.find(plugin => plugin.id === reference.plugin)
|
|
22
|
+
const panel = plugin && drawerPanels(plugin).find(item => item.id === reference.element)
|
|
23
|
+
if (!plugin || !panel) return <p className="drawer-note">This plugin interface is not installed.</p>
|
|
24
|
+
if (!plugin.valid || plugin.activation?.state === 'error') {
|
|
25
|
+
return <p role="alert" className="drawer-error">{plugin.name} could not load.</p>
|
|
26
|
+
}
|
|
27
|
+
if (plugin.activation?.enabled !== true) return <p className="drawer-note">
|
|
28
|
+
{plugin.name} is off. <Link to={{ search: `?plugin=${encodeURIComponent(plugin.id)}` }}>Open plugin settings</Link>
|
|
29
|
+
</p>
|
|
30
|
+
return <PluginPanel key={`${plugin.id}:${panel.id}`} plugin={plugin} elementId={panel.id} designCss={designCss} brainId={brainId} />
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function PublishedChunk({ name, side, plugins, design, designOwner, brainId }: {
|
|
34
|
+
name: string; side: DrawerSide | 'page'; plugins: PluginInfo[] | null; design?: PluginDesign; designOwner?: string; brainId?: string
|
|
35
|
+
}) {
|
|
36
|
+
const ownerDesign = usePluginDesign(designOwner, brainId)
|
|
37
|
+
const theme = ownerDesign?.design || design
|
|
38
|
+
const requestKey = JSON.stringify([name, side, brainId])
|
|
39
|
+
const [result, setResult] = useState<{ key: string; chunk: (WorkspaceChunk & { html: string }) | null }>()
|
|
40
|
+
const current = result?.key === requestKey ? result : undefined
|
|
41
|
+
const chunk = current?.chunk
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const controller = new AbortController()
|
|
44
|
+
void readWorkspaceChunk(name, side, controller.signal, brainId).then(
|
|
45
|
+
data => { if (!controller.signal.aborted) setResult({ key: requestKey, chunk: data }) },
|
|
46
|
+
() => { if (!controller.signal.aborted) setResult({ key: requestKey, chunk: null }) },
|
|
47
|
+
)
|
|
48
|
+
return () => controller.abort()
|
|
49
|
+
}, [name, side, brainId, requestKey])
|
|
50
|
+
if (current && (!chunk || !chunk.surfaces.includes(side))) return <p className="drawer-note">{name} is unavailable here.</p>
|
|
51
|
+
if (!chunk) return <p role="status">Loading {name}…</p>
|
|
52
|
+
const owner = plugins?.find(plugin => plugin.id === chunk.plugin)
|
|
53
|
+
return <section className="drawer-stack" aria-label={chunk.title}>
|
|
54
|
+
{chunk.html && <DrawerMarkup title={chunk.title} html={chunk.html} design={theme} />}
|
|
55
|
+
{chunk.panel && <DrawerPanel plugins={plugins} reference={{ plugin: chunk.plugin, element: chunk.panel }} designCss={theme?.css ?? ''} brainId={brainId} />}
|
|
56
|
+
{brainId && owner?.valid && owner.activation?.enabled === true && owner.activation.state !== 'error' &&
|
|
57
|
+
!!chunk.operations?.length && <ScopedPluginCommands key={`${requestKey}:${owner.sha256}:${chunk.sha256}`} plugin={owner} brainId={brainId}
|
|
58
|
+
operationIds={chunk.operations} label={`${chunk.title} operations`} />}
|
|
59
|
+
</section>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function ShortcutList({ shortcuts }: { shortcuts: DrawerConfig['shortcuts'] }) {
|
|
63
|
+
if (!shortcuts.length) return <p className="drawer-note">Add a shortcut to a dashboard page, plugin, or website.</p>
|
|
64
|
+
return <nav className="drawer-actions" aria-label="Saved shortcuts">
|
|
65
|
+
{shortcuts.map(({ label, href }, index) => href.startsWith('https://')
|
|
66
|
+
? <a key={index} className="btn btn-ghost" href={href} target="_blank" rel="noopener noreferrer">{label}</a>
|
|
67
|
+
: <Link key={index} className="btn btn-ghost" to={href}>{label}</Link>)}
|
|
68
|
+
</nav>
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function LayoutEditor({ side, draft, onChange, plugins, brainId }: {
|
|
72
|
+
side: DrawerSide; draft: DrawerConfig; onChange: (next: DrawerConfig) => void; plugins: PluginInfo[] | null; brainId?: string
|
|
73
|
+
}) {
|
|
74
|
+
const [query, setQuery] = useState('')
|
|
75
|
+
const [chunks, setChunks] = useState<WorkspaceChunk[]>([])
|
|
76
|
+
const [catalogError, setCatalogError] = useState(false)
|
|
77
|
+
const [preview, setPreview] = useState(false)
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
const controller = new AbortController()
|
|
80
|
+
const timer = window.setTimeout(() => {
|
|
81
|
+
void findWorkspaceChunks(side, query, controller.signal, undefined, brainId).then(
|
|
82
|
+
data => { if (!controller.signal.aborted) { setChunks(data.chunks); setCatalogError(false) } },
|
|
83
|
+
() => { if (!controller.signal.aborted) { setChunks([]); setCatalogError(true) } },
|
|
84
|
+
)
|
|
85
|
+
}, 180)
|
|
86
|
+
return () => { controller.abort(); window.clearTimeout(timer) }
|
|
87
|
+
}, [side, query, brainId])
|
|
88
|
+
const panels = (plugins || []).filter(plugin => plugin.valid && plugin.activation?.enabled === true)
|
|
89
|
+
.flatMap(plugin => drawerPanels(plugin).map(element => ({ plugin, element })))
|
|
90
|
+
const choices = panels.filter(({ plugin, element }) => !draft.panels.some(ref => ref.plugin === plugin.id && ref.element === element.id))
|
|
91
|
+
return <div className="drawer-stack">
|
|
92
|
+
{side === 'bottom' && <fieldset className="drawer-stack drawer-fields">
|
|
93
|
+
<legend>Shortcuts</legend>
|
|
94
|
+
{draft.shortcuts.map((shortcut, index) => <div className="drawer-shortcut-edit" key={index}>
|
|
95
|
+
<input className="input" required maxLength={80} value={shortcut.label} aria-label={`Shortcut ${index + 1} name`}
|
|
96
|
+
placeholder="Name" onChange={event => onChange({ ...draft, shortcuts: draft.shortcuts.map((link, i) => i === index ? { ...link, label: event.target.value } : link) })} />
|
|
97
|
+
<input className="input" required maxLength={1024} value={shortcut.href} aria-label={`Shortcut ${index + 1} address`}
|
|
98
|
+
placeholder="/memory?plugin=… or https://…" onChange={event => onChange({ ...draft, shortcuts: draft.shortcuts.map((link, i) => i === index ? { ...link, href: event.target.value } : link) })} />
|
|
99
|
+
<button type="button" className="btn btn-ghost btn-sm" aria-label={`Remove shortcut ${index + 1}`}
|
|
100
|
+
onClick={() => onChange({ ...draft, shortcuts: draft.shortcuts.filter((_, i) => i !== index) })}>Remove</button>
|
|
101
|
+
</div>)}
|
|
102
|
+
<div><button type="button" className="btn btn-ghost btn-sm" disabled={draft.shortcuts.length >= 16}
|
|
103
|
+
onClick={() => onChange({ ...draft, shortcuts: [...draft.shortcuts, { label: '', href: '' }] })}>Add shortcut</button></div>
|
|
104
|
+
</fieldset>}
|
|
105
|
+
<label>Drawer HTML
|
|
106
|
+
<textarea className="input drawer-code" rows={7} maxLength={65536} value={draft.html}
|
|
107
|
+
placeholder="<section>…</section>" onChange={event => onChange({ ...draft, html: event.target.value })} />
|
|
108
|
+
</label>
|
|
109
|
+
<div><button type="button" className="btn btn-ghost btn-sm" aria-expanded={preview} disabled={!draft.html.trim()}
|
|
110
|
+
onClick={() => setPreview(!preview)}>{preview ? 'Hide preview' : 'Preview HTML'}</button></div>
|
|
111
|
+
{preview && draft.html.trim() && <DrawerMarkup title="Draft HTML preview" html={draft.html} />}
|
|
112
|
+
<fieldset className="drawer-stack drawer-fields">
|
|
113
|
+
<legend>Plugin chunks</legend>
|
|
114
|
+
{draft.chunks.map(key => <div key={key} className="drawer-row">
|
|
115
|
+
<span>{key}</span>
|
|
116
|
+
<button type="button" className="btn btn-ghost btn-sm" aria-label={`Remove ${key}`}
|
|
117
|
+
onClick={() => onChange({ ...draft, chunks: draft.chunks.filter(item => item !== key) })}>Remove</button>
|
|
118
|
+
</div>)}
|
|
119
|
+
<input className="input" type="search" aria-label="Search installed chunks" placeholder="Search installed chunks…"
|
|
120
|
+
value={query} onChange={event => setQuery(event.target.value)} />
|
|
121
|
+
{catalogError ? <p className="drawer-note">The chunk catalog is unavailable on this server.</p> :
|
|
122
|
+
<select className="select" aria-label="Add chunk" value="" disabled={draft.chunks.length >= 8}
|
|
123
|
+
onChange={event => { if (event.target.value) onChange({ ...draft, chunks: [...draft.chunks, event.target.value] }) }}>
|
|
124
|
+
<option value="">Add an installed chunk…</option>
|
|
125
|
+
{chunks.filter(chunk => !draft.chunks.includes(chunk.key)).map(chunk =>
|
|
126
|
+
<option key={chunk.key} value={chunk.key}>{chunk.title} · {chunk.plugin}</option>)}
|
|
127
|
+
</select>}
|
|
128
|
+
</fieldset>
|
|
129
|
+
<fieldset className="drawer-stack drawer-fields">
|
|
130
|
+
<legend>Plugin interfaces</legend>
|
|
131
|
+
{draft.panels.map(ref => <div key={`${ref.plugin}:${ref.element}`} className="drawer-row">
|
|
132
|
+
<span>{ref.plugin} · {ref.element}</span>
|
|
133
|
+
<button type="button" className="btn btn-ghost btn-sm" aria-label={`Remove ${ref.plugin} ${ref.element}`}
|
|
134
|
+
onClick={() => onChange({ ...draft, panels: draft.panels.filter(item => item !== ref) })}>Remove</button>
|
|
135
|
+
</div>)}
|
|
136
|
+
<select className="select" aria-label="Add plugin interface" value="" disabled={!choices.length || draft.panels.length >= 6}
|
|
137
|
+
onChange={event => {
|
|
138
|
+
const match = choices.find(({ plugin, element }) => `${plugin.id}:${element.id}` === event.target.value)
|
|
139
|
+
if (match) onChange({ ...draft, panels: [...draft.panels, { plugin: match.plugin.id, element: match.element.id }] })
|
|
140
|
+
}}>
|
|
141
|
+
<option value="">Add an installed interface…</option>
|
|
142
|
+
{choices.map(({ plugin, element }) =>
|
|
143
|
+
<option key={`${plugin.id}:${element.id}`} value={`${plugin.id}:${element.id}`}>{plugin.name} · {element.description || element.id}</option>)}
|
|
144
|
+
</select>
|
|
145
|
+
</fieldset>
|
|
146
|
+
</div>
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function DrawerLayout({ side, view, plugins, brainId }: {
|
|
150
|
+
side: DrawerSide; view: 'custom' | 'shortcuts'; plugins: PluginInfo[] | null; brainId?: string
|
|
151
|
+
}) {
|
|
152
|
+
const [revision, setRevision] = useState<DrawerRevision | null>(null)
|
|
153
|
+
const [draft, setDraft] = useState<DrawerConfig | null>(null)
|
|
154
|
+
const [error, setError] = useState('')
|
|
155
|
+
const [busy, setBusy] = useState(false)
|
|
156
|
+
const [refresh, setRefresh] = useState(0)
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
const controller = new AbortController()
|
|
159
|
+
void readDrawer(side, controller.signal).then(
|
|
160
|
+
current => { if (!controller.signal.aborted) { setRevision(current); setError('') } },
|
|
161
|
+
error => { if (!controller.signal.aborted) setError(error.message) },
|
|
162
|
+
)
|
|
163
|
+
return () => controller.abort()
|
|
164
|
+
}, [refresh, side])
|
|
165
|
+
return <div className="drawer-stack">
|
|
166
|
+
<div className="drawer-actions">
|
|
167
|
+
{!draft && <button type="button" className="btn btn-primary btn-sm" disabled={!revision}
|
|
168
|
+
onClick={() => { setDraft(revision!.config); setError('') }}>{view === 'shortcuts' ? 'Edit shortcuts' : 'Edit drawer'}</button>}
|
|
169
|
+
{!draft && <button type="button" className="btn btn-ghost btn-sm" onClick={() => setRefresh(refresh + 1)}>Refresh</button>}
|
|
170
|
+
</div>
|
|
171
|
+
{error && <p className="drawer-error" role="alert">{error}</p>}
|
|
172
|
+
{!revision && !error && <p role="status">Loading the saved drawer…</p>}
|
|
173
|
+
{draft && revision && <form className="drawer-stack" onSubmit={async event => {
|
|
174
|
+
event.preventDefault()
|
|
175
|
+
setBusy(true); setError('')
|
|
176
|
+
try {
|
|
177
|
+
setRevision(await saveDrawer(side, { config: draft, version: revision.version }))
|
|
178
|
+
setDraft(null)
|
|
179
|
+
} catch (error) { setError(error instanceof Error ? error.message : 'Could not save the drawer.') }
|
|
180
|
+
finally { setBusy(false) }
|
|
181
|
+
}}>
|
|
182
|
+
<LayoutEditor side={side} draft={draft} onChange={setDraft} plugins={plugins} brainId={brainId} />
|
|
183
|
+
<div className="drawer-actions">
|
|
184
|
+
<button className="btn btn-primary" disabled={busy}>{busy ? 'Saving…' : 'Save drawer'}</button>
|
|
185
|
+
<button type="button" className="btn btn-ghost" disabled={busy} onClick={() => { setDraft(null); setError('') }}>Cancel</button>
|
|
186
|
+
</div>
|
|
187
|
+
</form>}
|
|
188
|
+
{!draft && revision && (view === 'shortcuts' ? <ShortcutList shortcuts={revision.config.shortcuts} /> : <>
|
|
189
|
+
{revision.config.html && <DrawerMarkup title={`${side} drawer custom HTML`} html={revision.config.html} />}
|
|
190
|
+
{revision.config.chunks.map(key => <PublishedChunk key={`${brainId}:${key}:${refresh}`} name={key} side={side} plugins={plugins} brainId={brainId} designOwner={key.split(':')[0]} />)}
|
|
191
|
+
{revision.config.panels.map(ref => <DrawerPanel key={`${brainId}:${ref.plugin}:${ref.element}:${refresh}`} plugins={plugins} reference={ref} brainId={brainId} />)}
|
|
192
|
+
{!revision.config.html && !revision.config.chunks.length && !revision.config.panels.length &&
|
|
193
|
+
<p className="drawer-note">Add HTML or an installed plugin interface to make this drawer yours.</p>}
|
|
194
|
+
</>)}
|
|
195
|
+
</div>
|
|
196
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import type { PluginDesign } from '../../api/workspace-drawers'
|
|
3
|
+
|
|
4
|
+
const TAGS = new Set('a abbr article aside b bdi blockquote br button caption code col colgroup dd del details div dl dt em fieldset figcaption figure footer h1 h2 h3 h4 h5 h6 header hr i img input kbd label legend li main mark meter nav ol optgroup option p pre progress q s samp section select small span strong style sub summary sup table tbody td textarea tfoot th thead time tr u ul var wbr'.split(' '))
|
|
5
|
+
const ATTRIBUTES = new Set('alt checked class cols colspan datetime dir disabled for height hidden id lang max maxlength min multiple name open placeholder role rows rowspan scope selected size step style tabindex title type value width'.split(' '))
|
|
6
|
+
const POLICY = "default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'; object-src 'none'"
|
|
7
|
+
|
|
8
|
+
/** Parse in an inert template; only the reconstructed markup goes into an opaque frame. */
|
|
9
|
+
export function drawerMarkupDocument(source: string, design?: Pick<PluginDesign, 'css' | 'components'>) {
|
|
10
|
+
const template = document.createElement('template')
|
|
11
|
+
template.innerHTML = source
|
|
12
|
+
for (const recipe of [...template.content.querySelectorAll<HTMLTemplateElement>('template[data-design-components]')].slice(0, 8)) {
|
|
13
|
+
const element = recipe.content.firstElementChild
|
|
14
|
+
if (!element || recipe.content.children.length !== 1 || recipe.innerHTML.length > 4096) continue
|
|
15
|
+
const copies = (design?.components || []).slice(0, 64).map(name => {
|
|
16
|
+
const node = element.cloneNode(true) as Element
|
|
17
|
+
node.setAttribute('data-design-component', name)
|
|
18
|
+
if (node.hasAttribute('data-design-label')) node.textContent = name
|
|
19
|
+
for (const label of node.querySelectorAll('[data-design-label]')) label.textContent = name
|
|
20
|
+
return node
|
|
21
|
+
})
|
|
22
|
+
recipe.replaceWith(...copies)
|
|
23
|
+
}
|
|
24
|
+
for (const element of template.content.querySelectorAll('*')) {
|
|
25
|
+
const name = element.localName
|
|
26
|
+
if (!TAGS.has(name) || element.namespaceURI !== 'http://www.w3.org/1999/xhtml') {
|
|
27
|
+
element.remove()
|
|
28
|
+
continue
|
|
29
|
+
}
|
|
30
|
+
for (const attribute of [...element.attributes]) {
|
|
31
|
+
const { name: key, value } = attribute
|
|
32
|
+
if (ATTRIBUTES.has(key) || key === 'data-design-component' || /^aria-[a-z-]+$/.test(key)) continue
|
|
33
|
+
if (name === 'img' && key === 'src' && /^data:image\/(?:png|jpeg|gif|webp);base64,[a-z0-9+/=]+$/i.test(value)) continue
|
|
34
|
+
element.removeAttribute(key)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
38
|
+
<meta http-equiv="Content-Security-Policy" content="${POLICY}">
|
|
39
|
+
<style>:root{color-scheme:light dark}body{margin:0;padding:var(--spacing-md,16px);font:14px/1.55 var(--typography-body-font-family,var(--font-sans,system-ui)),sans-serif;color:var(--color-text,CanvasText);background:var(--color-background,Canvas)}img{max-width:100%;height:auto}*{box-sizing:border-box}</style>
|
|
40
|
+
<style>${(design?.css || '').replaceAll('<', '')}</style>
|
|
41
|
+
</head><body>${template.innerHTML}</body></html>`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function DrawerMarkup({ title, html, design }: { title: string; html: string; design?: PluginDesign }) {
|
|
45
|
+
const source = useMemo(() => drawerMarkupDocument(html, design), [html, design])
|
|
46
|
+
return <iframe className="drawer-html" title={title} sandbox="" referrerPolicy="no-referrer"
|
|
47
|
+
allow="camera 'none'; microphone 'none'; geolocation 'none'; clipboard-read 'none'; clipboard-write 'none'"
|
|
48
|
+
srcDoc={source} />
|
|
49
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import { findWorkspaceChunks, type WorkspaceChunk } from '../../api/workspace-drawers'
|
|
3
|
+
import type { PluginInfo } from '../../api/plugins'
|
|
4
|
+
import { PublishedChunk } from './DrawerLayout'
|
|
5
|
+
|
|
6
|
+
const pageSize = 20
|
|
7
|
+
|
|
8
|
+
export function PluginChunkLibrary({ plugin, brainId }: {
|
|
9
|
+
plugin: PluginInfo; brainId: string
|
|
10
|
+
}) {
|
|
11
|
+
const [catalog, setCatalog] = useState<{ chunks: WorkspaceChunk[]; total: number } | null>(null)
|
|
12
|
+
const [key, setKey] = useState('')
|
|
13
|
+
const [draft, setDraft] = useState('')
|
|
14
|
+
const [query, setQuery] = useState('')
|
|
15
|
+
const [offset, setOffset] = useState(0)
|
|
16
|
+
const [refresh, setRefresh] = useState(0)
|
|
17
|
+
const [error, setError] = useState('')
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const controller = new AbortController()
|
|
21
|
+
void findWorkspaceChunks('page', query, controller.signal, plugin.id, brainId, { limit: pageSize, offset }).then(
|
|
22
|
+
data => {
|
|
23
|
+
if (controller.signal.aborted) return
|
|
24
|
+
if (!data.chunks.length && offset > 0) {
|
|
25
|
+
setOffset(Math.max(0, Math.floor((data.total - 1) / pageSize) * pageSize))
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
setCatalog(data)
|
|
29
|
+
setKey(current => data.chunks.some(chunk => chunk.key === current) ? current : data.chunks[0]?.key || '')
|
|
30
|
+
},
|
|
31
|
+
() => {
|
|
32
|
+
if (controller.signal.aborted) return
|
|
33
|
+
setError('Could not load the published blocks.')
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
return () => controller.abort()
|
|
37
|
+
}, [plugin.id, brainId, offset, query, refresh])
|
|
38
|
+
|
|
39
|
+
function clearResult() {
|
|
40
|
+
setCatalog(null)
|
|
41
|
+
setError('')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function movePage(next: number) {
|
|
45
|
+
clearResult()
|
|
46
|
+
setOffset(next)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const selected = catalog?.chunks.find(chunk => chunk.key === key)
|
|
50
|
+
|
|
51
|
+
return <div className="drawer-stack">
|
|
52
|
+
<form className="drawer-actions" onSubmit={event => {
|
|
53
|
+
event.preventDefault()
|
|
54
|
+
clearResult()
|
|
55
|
+
setQuery(draft.trim())
|
|
56
|
+
setOffset(0)
|
|
57
|
+
setRefresh(value => value + 1)
|
|
58
|
+
}}>
|
|
59
|
+
<label>Search blocks
|
|
60
|
+
<input className="input" type="search" value={draft} maxLength={1024}
|
|
61
|
+
onChange={event => setDraft(event.target.value)} />
|
|
62
|
+
</label>
|
|
63
|
+
<button type="submit" className="btn btn-ghost btn-sm">Search</button>
|
|
64
|
+
<button type="button" className="btn btn-ghost btn-sm" onClick={() => {
|
|
65
|
+
clearResult()
|
|
66
|
+
setRefresh(value => value + 1)
|
|
67
|
+
}}>Refresh blocks</button>
|
|
68
|
+
</form>
|
|
69
|
+
<div className="drawer-actions">
|
|
70
|
+
{catalog && catalog.chunks.length > 0 && <label>Block
|
|
71
|
+
<select className="select" value={key} onChange={event => setKey(event.target.value)}>
|
|
72
|
+
{catalog.chunks.map(chunk => <option key={chunk.key} value={chunk.key}>{chunk.title}</option>)}
|
|
73
|
+
</select>
|
|
74
|
+
</label>}
|
|
75
|
+
</div>
|
|
76
|
+
{catalog && catalog.total > 0 && <div className="drawer-actions">
|
|
77
|
+
<span role="status">Showing {offset + 1}–{offset + catalog.chunks.length} of {catalog.total} blocks</span>
|
|
78
|
+
{catalog.total > pageSize && <>
|
|
79
|
+
<button type="button" className="btn btn-ghost btn-sm" disabled={offset === 0}
|
|
80
|
+
onClick={() => movePage(offset - pageSize)}>Previous blocks</button>
|
|
81
|
+
<button type="button" className="btn btn-ghost btn-sm" disabled={offset + pageSize >= catalog.total}
|
|
82
|
+
onClick={() => movePage(offset + pageSize)}>Next blocks</button>
|
|
83
|
+
</>}
|
|
84
|
+
</div>}
|
|
85
|
+
{error && <p role="alert">{error}</p>}
|
|
86
|
+
{!catalog && !error && <p role="status">Loading published blocks…</p>}
|
|
87
|
+
{catalog?.total === 0 && <p className="drawer-note">{query
|
|
88
|
+
? 'No published blocks match your search.' : 'This plugin has no published page blocks yet.'}</p>}
|
|
89
|
+
{selected && <PublishedChunk key={`${selected.key}:${selected.sha256}:${refresh}`}
|
|
90
|
+
name={selected.key} side="page" plugins={[plugin]} brainId={brainId} designOwner={plugin.id} />}
|
|
91
|
+
</div>
|
|
92
|
+
}
|