@xingular/harness 0.1.6 → 1.0.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/CHANGELOG.md +107 -0
- package/LICENSE +202 -0
- package/NOTICE.md +60 -0
- package/README.md +265 -71
- package/THIRD_PARTY_LICENSES.md +942 -0
- package/dist/cli.js +14842 -9445
- package/dist/sdk/accounts/accounts.d.ts +165 -0
- package/dist/sdk/accounts/accounts.js +730 -0
- package/dist/sdk/accounts/choices.d.ts +23 -0
- package/dist/sdk/accounts/choices.js +62 -0
- package/dist/sdk/accounts/env.d.ts +78 -0
- package/dist/sdk/accounts/env.js +294 -0
- package/dist/sdk/accounts/secrets.d.ts +16 -0
- package/dist/sdk/accounts/secrets.js +40 -0
- package/dist/sdk/accounts/store.d.ts +114 -0
- package/dist/sdk/accounts/store.js +255 -0
- package/dist/sdk/browser/config.d.ts +16 -0
- package/dist/sdk/browser/config.js +22 -0
- package/dist/sdk/browser/diagnostics.d.ts +4 -0
- package/dist/sdk/browser/diagnostics.js +38 -0
- package/dist/sdk/browser/index.d.ts +17 -0
- package/dist/sdk/browser/index.js +65 -0
- package/dist/sdk/browser/network.d.ts +10 -0
- package/dist/sdk/browser/network.js +59 -0
- package/dist/sdk/browser/session.d.ts +84 -0
- package/dist/sdk/browser/session.js +192 -0
- package/dist/sdk/browser/tools.d.ts +8 -0
- package/dist/sdk/browser/tools.js +329 -0
- package/dist/sdk/capabilities/api.d.ts +22 -0
- package/dist/sdk/capabilities/api.js +54 -0
- package/dist/sdk/capabilities/runSnapshot.d.ts +18 -0
- package/dist/sdk/capabilities/runSnapshot.js +47 -0
- package/dist/sdk/capabilities/types.d.ts +29 -0
- package/dist/sdk/capabilities/types.js +1 -0
- package/dist/sdk/cli.d.ts +2 -0
- package/dist/sdk/cli.js +142 -0
- package/dist/sdk/core/bus.d.ts +23 -0
- package/dist/sdk/core/bus.js +76 -0
- package/dist/sdk/core/caller.d.ts +22 -0
- package/dist/sdk/core/caller.js +16 -0
- package/dist/sdk/core/changes.d.ts +15 -0
- package/dist/sdk/core/changes.js +15 -0
- package/dist/sdk/core/effort.d.ts +2 -0
- package/dist/sdk/core/effort.js +1 -0
- package/dist/sdk/core/env.d.ts +18 -0
- package/dist/sdk/core/env.js +8 -0
- package/dist/sdk/core/errors.d.ts +15 -0
- package/dist/sdk/core/errors.js +24 -0
- package/dist/sdk/core/frontmatter.d.ts +12 -0
- package/dist/sdk/core/frontmatter.js +50 -0
- package/dist/sdk/core/fs.d.ts +18 -0
- package/dist/sdk/core/fs.js +106 -0
- package/dist/sdk/core/index.d.ts +24 -0
- package/dist/sdk/core/index.js +187 -0
- package/dist/sdk/core/path.d.ts +7 -0
- package/dist/sdk/core/path.js +79 -0
- package/dist/sdk/core/projects.d.ts +18 -0
- package/dist/sdk/core/projects.js +67 -0
- package/dist/sdk/core/secrets.d.ts +2 -0
- package/dist/sdk/core/secrets.js +5 -0
- package/dist/sdk/core/text.d.ts +7 -0
- package/dist/sdk/core/text.js +40 -0
- package/dist/sdk/doctor/checks.d.ts +5 -0
- package/dist/sdk/doctor/checks.js +208 -0
- package/dist/sdk/doctor/probe.d.ts +4 -0
- package/dist/sdk/doctor/probe.js +92 -0
- package/dist/sdk/doctor/service.d.ts +23 -0
- package/dist/sdk/doctor/service.js +144 -0
- package/dist/sdk/doctor/types.d.ts +141 -0
- package/dist/sdk/doctor/types.js +29 -0
- package/dist/sdk/harness.d.ts +133 -0
- package/dist/sdk/harness.js +299 -0
- package/dist/sdk/history/lessonPrompt.d.ts +2 -0
- package/dist/sdk/history/lessonPrompt.js +15 -0
- package/dist/sdk/history/lessons.d.ts +52 -0
- package/dist/sdk/history/lessons.js +173 -0
- package/dist/sdk/history/recall.d.ts +68 -0
- package/dist/sdk/history/recall.js +199 -0
- package/dist/sdk/history/trajectory.d.ts +7 -0
- package/dist/sdk/history/trajectory.js +216 -0
- package/dist/sdk/hub/catalog.json +123 -0
- package/dist/sdk/hub/cli.d.ts +7 -0
- package/dist/sdk/hub/cli.js +81 -0
- package/dist/sdk/hub/hub.d.ts +74 -0
- package/dist/sdk/hub/hub.js +474 -0
- package/dist/sdk/hub/schema.d.ts +280 -0
- package/dist/sdk/hub/schema.js +78 -0
- package/dist/sdk/index.d.ts +63 -0
- package/dist/sdk/index.js +48 -0
- package/dist/sdk/mcp/connections.d.ts +83 -0
- package/dist/sdk/mcp/connections.js +669 -0
- package/dist/sdk/mcp/manager.d.ts +26 -0
- package/dist/sdk/mcp/manager.js +124 -0
- package/dist/sdk/mcp/oauth.d.ts +35 -0
- package/dist/sdk/mcp/oauth.js +171 -0
- package/dist/sdk/mcp/scopeMigration.d.ts +19 -0
- package/dist/sdk/mcp/scopeMigration.js +198 -0
- package/dist/sdk/mcp/service.d.ts +160 -0
- package/dist/sdk/mcp/service.js +510 -0
- package/dist/sdk/mcp/types.d.ts +131 -0
- package/dist/sdk/mcp/types.js +49 -0
- package/dist/sdk/pi/runtime.d.ts +13 -0
- package/dist/sdk/pi/runtime.js +31 -0
- package/dist/sdk/plugins/api.d.ts +66 -0
- package/dist/sdk/plugins/api.js +155 -0
- package/dist/sdk/plugins/attachments.d.ts +46 -0
- package/dist/sdk/plugins/attachments.js +111 -0
- package/dist/sdk/plugins/builtin.d.ts +6 -0
- package/dist/sdk/plugins/builtin.js +68 -0
- package/dist/sdk/plugins/catalogue.d.ts +29 -0
- package/dist/sdk/plugins/catalogue.js +196 -0
- package/dist/sdk/plugins/cli.d.ts +13 -0
- package/dist/sdk/plugins/cli.js +187 -0
- package/dist/sdk/plugins/discover.d.ts +42 -0
- package/dist/sdk/plugins/discover.js +115 -0
- package/dist/sdk/plugins/firstParty.d.ts +50 -0
- package/dist/sdk/plugins/firstParty.js +113 -0
- package/dist/sdk/plugins/hooks.d.ts +94 -0
- package/dist/sdk/plugins/hooks.js +162 -0
- package/dist/sdk/plugins/identity.d.ts +19 -0
- package/dist/sdk/plugins/identity.js +48 -0
- package/dist/sdk/plugins/install.d.ts +20 -0
- package/dist/sdk/plugins/install.js +110 -0
- package/dist/sdk/plugins/inventory.d.ts +22 -0
- package/dist/sdk/plugins/inventory.js +110 -0
- package/dist/sdk/plugins/manifest.d.ts +13 -0
- package/dist/sdk/plugins/manifest.js +56 -0
- package/dist/sdk/plugins/plugins.d.ts +104 -0
- package/dist/sdk/plugins/plugins.js +164 -0
- package/dist/sdk/plugins/runtime.d.ts +9 -0
- package/dist/sdk/plugins/runtime.js +95 -0
- package/dist/sdk/plugins/trust.d.ts +44 -0
- package/dist/sdk/plugins/trust.js +120 -0
- package/dist/sdk/plugins/types.d.ts +159 -0
- package/dist/sdk/plugins/types.js +7 -0
- package/dist/sdk/prompt/compile.d.ts +174 -0
- package/dist/sdk/prompt/compile.js +396 -0
- package/dist/sdk/refine/cli.d.ts +4 -0
- package/dist/sdk/refine/cli.js +186 -0
- package/dist/sdk/refine/demo.d.ts +21 -0
- package/dist/sdk/refine/demo.js +114 -0
- package/dist/sdk/refine/evidence.d.ts +55 -0
- package/dist/sdk/refine/evidence.js +330 -0
- package/dist/sdk/refine/host.d.ts +51 -0
- package/dist/sdk/refine/host.js +76 -0
- package/dist/sdk/refine/ledger.d.ts +48 -0
- package/dist/sdk/refine/ledger.js +76 -0
- package/dist/sdk/refine/library.d.ts +55 -0
- package/dist/sdk/refine/library.js +177 -0
- package/dist/sdk/refine/pi.d.ts +72 -0
- package/dist/sdk/refine/pi.js +248 -0
- package/dist/sdk/refine/prompts.d.ts +45 -0
- package/dist/sdk/refine/prompts.js +390 -0
- package/dist/sdk/refine/recursion.d.ts +113 -0
- package/dist/sdk/refine/recursion.js +258 -0
- package/dist/sdk/refine/runner.d.ts +81 -0
- package/dist/sdk/refine/runner.js +150 -0
- package/dist/sdk/refine/selection.d.ts +57 -0
- package/dist/sdk/refine/selection.js +251 -0
- package/dist/sdk/refine/service.d.ts +84 -0
- package/dist/sdk/refine/service.js +165 -0
- package/dist/sdk/refine/suite.d.ts +67 -0
- package/dist/sdk/refine/suite.js +174 -0
- package/dist/sdk/refine/trajectory.d.ts +9 -0
- package/dist/sdk/refine/trajectory.js +68 -0
- package/dist/sdk/refine/types.d.ts +77 -0
- package/dist/sdk/refine/types.js +10 -0
- package/dist/sdk/server/methods.d.ts +3 -0
- package/dist/sdk/server/methods.js +370 -0
- package/dist/sdk/server/remote.d.ts +19 -0
- package/dist/sdk/server/remote.js +84 -0
- package/dist/sdk/server/rpc.d.ts +20 -0
- package/dist/sdk/server/rpc.js +102 -0
- package/dist/sdk/server/serve.d.ts +10 -0
- package/dist/sdk/server/serve.js +18 -0
- package/dist/sdk/sessions/approvals.d.ts +47 -0
- package/dist/sdk/sessions/approvals.js +119 -0
- package/dist/sdk/sessions/continuation.d.ts +48 -0
- package/dist/sdk/sessions/continuation.js +85 -0
- package/dist/sdk/sessions/contracts.d.ts +534 -0
- package/dist/sdk/sessions/contracts.js +299 -0
- package/dist/sdk/sessions/extensionUi.d.ts +19 -0
- package/dist/sdk/sessions/extensionUi.js +64 -0
- package/dist/sdk/sessions/history.d.ts +16 -0
- package/dist/sdk/sessions/history.js +68 -0
- package/dist/sdk/sessions/lineageJoin.d.ts +2 -0
- package/dist/sdk/sessions/lineageJoin.js +61 -0
- package/dist/sdk/sessions/planMode.d.ts +36 -0
- package/dist/sdk/sessions/planMode.js +84 -0
- package/dist/sdk/sessions/policy.d.ts +43 -0
- package/dist/sdk/sessions/policy.js +1 -0
- package/dist/sdk/sessions/pricing.d.ts +34 -0
- package/dist/sdk/sessions/pricing.js +40 -0
- package/dist/sdk/sessions/reasoningSummaries.d.ts +11 -0
- package/dist/sdk/sessions/reasoningSummaries.js +61 -0
- package/dist/sdk/sessions/registry.d.ts +102 -0
- package/dist/sdk/sessions/registry.js +383 -0
- package/dist/sdk/sessions/runIndex.d.ts +21 -0
- package/dist/sdk/sessions/runIndex.js +65 -0
- package/dist/sdk/sessions/runStore.d.ts +66 -0
- package/dist/sdk/sessions/runStore.js +335 -0
- package/dist/sdk/sessions/runner.d.ts +213 -0
- package/dist/sdk/sessions/runner.js +589 -0
- package/dist/sdk/sessions/sessions.d.ts +140 -0
- package/dist/sdk/sessions/sessions.js +394 -0
- package/dist/sdk/sessions/subagentService.d.ts +41 -0
- package/dist/sdk/sessions/subagentService.js +247 -0
- package/dist/sdk/sessions/subagents.d.ts +50 -0
- package/dist/sdk/sessions/subagents.js +68 -0
- package/dist/sdk/terminal/accountsMenu.d.ts +46 -0
- package/dist/sdk/terminal/accountsMenu.js +392 -0
- package/dist/sdk/terminal/cli.d.ts +20 -0
- package/dist/sdk/terminal/cli.js +121 -0
- package/dist/sdk/terminal/client.d.ts +15 -0
- package/dist/sdk/terminal/client.js +126 -0
- package/dist/sdk/terminal/control.d.ts +4 -0
- package/dist/sdk/terminal/control.js +25 -0
- package/dist/sdk/terminal/controller.d.ts +72 -0
- package/dist/sdk/terminal/controller.js +351 -0
- package/dist/sdk/terminal/hubMenu.d.ts +10 -0
- package/dist/sdk/terminal/hubMenu.js +34 -0
- package/dist/sdk/terminal/launch.d.ts +5 -0
- package/dist/sdk/terminal/launch.js +27 -0
- package/dist/sdk/terminal/logs.d.ts +14 -0
- package/dist/sdk/terminal/logs.js +59 -0
- package/dist/sdk/terminal/piExtension.d.ts +6 -0
- package/dist/sdk/terminal/piExtension.js +392 -0
- package/dist/sdk/terminal/pluginMenu.d.ts +34 -0
- package/dist/sdk/terminal/pluginMenu.js +101 -0
- package/dist/sdk/terminal/socket.d.ts +70 -0
- package/dist/sdk/terminal/socket.js +256 -0
- package/dist/sdk/terminal/standalone.d.ts +18 -0
- package/dist/sdk/terminal/standalone.js +146 -0
- package/dist/sdk/terminal/terminals.d.ts +176 -0
- package/dist/sdk/terminal/terminals.js +767 -0
- package/dist/sdk/terminal/trajectory.d.ts +10 -0
- package/dist/sdk/terminal/trajectory.js +30 -0
- package/dist/sdk/terminal/tui/chrome.d.ts +64 -0
- package/dist/sdk/terminal/tui/chrome.js +169 -0
- package/dist/sdk/terminal/tui/clipboardFiles.d.ts +11 -0
- package/dist/sdk/terminal/tui/clipboardFiles.js +74 -0
- package/dist/sdk/terminal/tui/commandPages.d.ts +7 -0
- package/dist/sdk/terminal/tui/commandPages.js +63 -0
- package/dist/sdk/terminal/tui/commands.d.ts +42 -0
- package/dist/sdk/terminal/tui/commands.js +276 -0
- package/dist/sdk/terminal/tui/dialogGeometry.d.ts +27 -0
- package/dist/sdk/terminal/tui/dialogGeometry.js +50 -0
- package/dist/sdk/terminal/tui/dialogs.d.ts +75 -0
- package/dist/sdk/terminal/tui/dialogs.js +363 -0
- package/dist/sdk/terminal/tui/keybindings.d.ts +11 -0
- package/dist/sdk/terminal/tui/keybindings.js +49 -0
- package/dist/sdk/terminal/tui/list.d.ts +73 -0
- package/dist/sdk/terminal/tui/list.js +196 -0
- package/dist/sdk/terminal/tui/modeChoices.d.ts +42 -0
- package/dist/sdk/terminal/tui/modeChoices.js +70 -0
- package/dist/sdk/terminal/tui/modelPicker.d.ts +55 -0
- package/dist/sdk/terminal/tui/modelPicker.js +155 -0
- package/dist/sdk/terminal/tui/mouse.d.ts +3 -0
- package/dist/sdk/terminal/tui/mouse.js +3 -0
- package/dist/sdk/terminal/tui/page.d.ts +14 -0
- package/dist/sdk/terminal/tui/page.js +1 -0
- package/dist/sdk/terminal/tui/pageOverlay.d.ts +27 -0
- package/dist/sdk/terminal/tui/pageOverlay.js +145 -0
- package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +54 -0
- package/dist/sdk/terminal/tui/pages/accountsPage.js +181 -0
- package/dist/sdk/terminal/tui/pages/componentsPage.d.ts +47 -0
- package/dist/sdk/terminal/tui/pages/componentsPage.js +119 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.d.ts +37 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.js +145 -0
- package/dist/sdk/terminal/tui/pages/hubPage.d.ts +39 -0
- package/dist/sdk/terminal/tui/pages/hubPage.js +128 -0
- package/dist/sdk/terminal/tui/pages/logPage.d.ts +38 -0
- package/dist/sdk/terminal/tui/pages/logPage.js +189 -0
- package/dist/sdk/terminal/tui/pages/permissionsPage.d.ts +44 -0
- package/dist/sdk/terminal/tui/pages/permissionsPage.js +124 -0
- package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +57 -0
- package/dist/sdk/terminal/tui/pages/pluginsPage.js +243 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.d.ts +55 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.js +88 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +58 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.js +377 -0
- package/dist/sdk/terminal/tui/piAdapter.d.ts +18 -0
- package/dist/sdk/terminal/tui/piAdapter.js +48 -0
- package/dist/sdk/terminal/tui/planMode.d.ts +2 -0
- package/dist/sdk/terminal/tui/planMode.js +2 -0
- package/dist/sdk/terminal/tui/questionPicker.d.ts +20 -0
- package/dist/sdk/terminal/tui/questionPicker.js +150 -0
- package/dist/sdk/terminal/tui/questions.d.ts +19 -0
- package/dist/sdk/terminal/tui/questions.js +42 -0
- package/dist/sdk/terminal/tui/runtime.d.ts +114 -0
- package/dist/sdk/terminal/tui/runtime.js +441 -0
- package/dist/sdk/terminal/tui/search.d.ts +22 -0
- package/dist/sdk/terminal/tui/search.js +60 -0
- package/dist/sdk/terminal/tui/selection.d.ts +16 -0
- package/dist/sdk/terminal/tui/selection.js +16 -0
- package/dist/sdk/terminal/tui/sessionUi.d.ts +3 -0
- package/dist/sdk/terminal/tui/sessionUi.js +30 -0
- package/dist/sdk/terminal/tui/settingsModel.d.ts +12 -0
- package/dist/sdk/terminal/tui/settingsModel.js +84 -0
- package/dist/sdk/terminal/tui/sideQuestion.d.ts +53 -0
- package/dist/sdk/terminal/tui/sideQuestion.js +197 -0
- package/dist/sdk/terminal/tui/skillInvocation.d.ts +9 -0
- package/dist/sdk/terminal/tui/skillInvocation.js +43 -0
- package/dist/sdk/terminal/tui/status.d.ts +18 -0
- package/dist/sdk/terminal/tui/status.js +67 -0
- package/dist/sdk/terminal/tui/theme.d.ts +45 -0
- package/dist/sdk/terminal/tui/theme.js +176 -0
- package/dist/sdk/terminal/tui/treeModel.d.ts +79 -0
- package/dist/sdk/terminal/tui/treeModel.js +483 -0
- package/dist/sdk/terminal/tui/treePicker.d.ts +49 -0
- package/dist/sdk/terminal/tui/treePicker.js +251 -0
- package/dist/sdk/terminal/tui/ui.d.ts +47 -0
- package/dist/sdk/terminal/tui/ui.js +148 -0
- package/dist/sdk/terminal/tui/workingStatus.d.ts +9 -0
- package/dist/sdk/terminal/tui/workingStatus.js +229 -0
- package/dist/sdk/tools/askUser.d.ts +6 -0
- package/dist/sdk/tools/askUser.js +74 -0
- package/dist/sdk/tools/checklist.d.ts +9 -0
- package/dist/sdk/tools/checklist.js +54 -0
- package/dist/sdk/tools/definitions.d.ts +30 -0
- package/dist/sdk/tools/definitions.js +117 -0
- package/dist/sdk/tools/dispatch.d.ts +16 -0
- package/dist/sdk/tools/dispatch.js +67 -0
- package/dist/sdk/tools/harnessTools.d.ts +6 -0
- package/dist/sdk/tools/harnessTools.js +20 -0
- package/dist/sdk/tools/sessionName.d.ts +7 -0
- package/dist/sdk/tools/sessionName.js +31 -0
- package/dist/sdk/tools/spawnSubagent.d.ts +4 -0
- package/dist/sdk/tools/spawnSubagent.js +49 -0
- package/dist/sdk/tools/types.d.ts +48 -0
- package/dist/sdk/tools/types.js +1 -0
- package/dist/sdk/variant/components.d.ts +34 -0
- package/dist/sdk/variant/components.js +230 -0
- package/dist/sdk/variant/materialise.d.ts +66 -0
- package/dist/sdk/variant/materialise.js +81 -0
- package/dist/sdk/variant/migrate.d.ts +4 -0
- package/dist/sdk/variant/migrate.js +41 -0
- package/dist/sdk/variant/seed.d.ts +20 -0
- package/dist/sdk/variant/seed.js +116 -0
- package/dist/sdk/variant/store.d.ts +82 -0
- package/dist/sdk/variant/store.js +151 -0
- package/dist/sdk/variant/surface.d.ts +56 -0
- package/dist/sdk/variant/surface.js +81 -0
- package/dist/sdk/variant/trust.d.ts +27 -0
- package/dist/sdk/variant/trust.js +64 -0
- package/dist/sdk/variant/variant.d.ts +71 -0
- package/dist/sdk/variant/variant.js +331 -0
- package/dist/sdk/web/markdown.d.ts +10 -0
- package/dist/sdk/web/markdown.js +66 -0
- package/dist/sdk/web/safeRequest.d.ts +34 -0
- package/dist/sdk/web/safeRequest.js +175 -0
- package/dist/sdk/web/search.d.ts +53 -0
- package/dist/sdk/web/search.js +168 -0
- package/dist/sdk/web/text.d.ts +2 -0
- package/dist/sdk/web/text.js +7 -0
- package/dist/sdk/web/tools.d.ts +22 -0
- package/dist/sdk/web/tools.js +195 -0
- package/dist/sdk/web/turndown-plugin-gfm.d.ts +4 -0
- package/dist/sdk/wiki/notes.d.ts +68 -0
- package/dist/sdk/wiki/notes.js +297 -0
- package/dist/sdk/world/actions.d.ts +23 -0
- package/dist/sdk/world/actions.js +31 -0
- package/dist/sdk/world/cli.d.ts +12 -0
- package/dist/sdk/world/cli.js +166 -0
- package/dist/sdk/world/images.d.ts +20 -0
- package/dist/sdk/world/images.js +48 -0
- package/dist/sdk/world/untrusted.d.ts +12 -0
- package/dist/sdk/world/untrusted.js +20 -0
- package/dist/sdk/world/world.d.ts +65 -0
- package/dist/sdk/world/world.js +80 -0
- package/dist/terminal-opentui.js +13612 -0
- package/dist/terminal-pi.js +1170 -62
- package/docs/README.md +34 -0
- package/docs/architecture/00-overview.md +159 -0
- package/docs/architecture/01-extension-points.md +78 -0
- package/docs/architecture/02-sessions-and-runs.md +146 -0
- package/docs/architecture/03-variants.md +95 -0
- package/docs/architecture/04-prompt.md +102 -0
- package/docs/architecture/05-refinement.md +107 -0
- package/docs/architecture/06-plugins-and-capabilities.md +93 -0
- package/docs/architecture/07-tools-and-gate.md +88 -0
- package/docs/architecture/08-memory-and-history.md +94 -0
- package/docs/architecture/09-accounts-and-connectors.md +90 -0
- package/docs/architecture/10-server-protocol.md +84 -0
- package/docs/architecture/11-terminal.md +81 -0
- package/docs/architecture/12-storage.md +61 -0
- package/docs/architecture/13-world-tools.md +23 -0
- package/docs/architecture/README.md +114 -0
- package/docs/architecture/proposals/PROP-0001-reporting-section.md +106 -0
- package/docs/architecture/proposals/PROP-0002-retain-rollout-workspaces.md +104 -0
- package/docs/architecture/proposals/PROP-0003-executor-settings.md +81 -0
- package/docs/architecture/proposals/PROP-0004-flexible-reporting-guidance.md +71 -0
- package/docs/architecture/proposals/PROP-0005-xharness-cli-name.md +78 -0
- package/docs/architecture/proposals/PROP-0006-run-checklist.md +68 -0
- package/docs/architecture/proposals/PROP-0007-run-continuation.md +80 -0
- package/docs/architecture/proposals/PROP-0008-lessons-from-runs.md +80 -0
- package/docs/architecture/proposals/PROP-0009-foundation-and-variants.md +78 -0
- package/docs/architecture/proposals/PROP-0010-session-names.md +73 -0
- package/docs/architecture/proposals/PROP-0011-doctor.md +63 -0
- package/docs/architecture/proposals/PROP-0012-release-baseline.md +56 -0
- package/docs/architecture/proposals/README.md +76 -0
- package/docs/decisions/ADR-0001-owned-terminal.md +87 -0
- package/docs/decisions/ADR-0002-native-subagent-lifecycle.md +49 -0
- package/docs/decisions/ADR-0003-shared-asynchronous-subagents.md +70 -0
- package/docs/decisions/ADR-0004-fullscreen-terminal-layout.md +50 -0
- package/docs/decisions/ADR-0005-app-provided-oauth-clients.md +54 -0
- package/docs/decisions/ADR-0006-opentui-only-terminal.md +47 -0
- package/docs/decisions/ADR-0007-reporting-section.md +33 -0
- package/docs/decisions/ADR-0008-flexible-reporting-guidance.md +38 -0
- package/docs/decisions/ADR-0009-xharness-command.md +38 -0
- package/docs/decisions/ADR-0010-lessons-in-the-wiki.md +31 -0
- package/docs/decisions/ADR-0011-foundation-and-variants.md +28 -0
- package/docs/decisions/ADR-0012-native-session-names.md +34 -0
- package/docs/decisions/ADR-0013-doctor.md +27 -0
- package/docs/decisions/ADR-0014-public-release-baseline.md +21 -0
- package/docs/doctor.md +74 -0
- package/docs/experiments/EXP-0001-terminal-input-latency/plan.md +81 -0
- package/docs/experiments/EXP-0001-terminal-input-latency/results.md +46 -0
- package/docs/experiments/EXP-0002-opentui-typing-latency/plan.md +70 -0
- package/docs/experiments/EXP-0002-opentui-typing-latency/results.md +92 -0
- package/docs/features/README.md +20 -0
- package/docs/features/automations/README.md +41 -0
- package/docs/features/cores/README.md +39 -0
- package/docs/features/google-harness2/README.md +52 -0
- package/docs/features/integrations/README.md +40 -0
- package/docs/features/interfaces/README.md +29 -0
- package/docs/features/memory/README.md +28 -0
- package/docs/releases/1.0.0.md +40 -0
- package/docs/releases/README.md +36 -0
- package/docs/study/00-index.md +78 -0
- package/docs/study/arms/00-matrix.md +20 -0
- package/docs/study/arms/01-system-prompt.md +211 -0
- package/docs/study/arms/02-tools.md +79 -0
- package/docs/study/arms/03-subagents.md +90 -0
- package/docs/study/arms/04-skills.md +75 -0
- package/docs/study/arms/05-context-management.md +90 -0
- package/docs/study/arms/06-memory.md +90 -0
- package/docs/study/arms/07-governance.md +71 -0
- package/docs/study/arms/08-execution.md +69 -0
- package/docs/study/arms/09-observability.md +60 -0
- package/docs/study/arms/10-refinement.md +110 -0
- package/docs/study/btw-comparison.md +37 -0
- package/docs/study/feature-gap-review.md +42 -0
- package/docs/study/insights.md +123 -0
- package/docs/study/roadmap.md +72 -0
- package/docs/study/sources/claude-code.md +44 -0
- package/docs/study/sources/deepseek-harness.md +102 -0
- package/docs/study/sources/harness2.md +50 -0
- package/docs/study/sources/kimi-cli.md +42 -0
- package/docs/study/sources/omp.md +93 -0
- package/docs/study/sources/opencode.md +43 -0
- package/docs/study/sources/openhands.md +46 -0
- package/docs/study/sources/smolagents.md +40 -0
- package/docs/study/sources/swe-agent.md +34 -0
- package/docs/system-prompt-design.md +196 -0
- package/docs/terminal-tui.md +671 -0
- package/package.json +48 -12
- package/src/accounts/accounts.test.ts +0 -223
- package/src/accounts/accounts.ts +0 -522
- package/src/accounts/env.test.ts +0 -95
- package/src/accounts/env.ts +0 -338
- package/src/accounts/secrets.ts +0 -50
- package/src/accounts/store.ts +0 -338
- package/src/capabilities/api.ts +0 -75
- package/src/capabilities/runSnapshot.ts +0 -59
- package/src/capabilities/types.ts +0 -34
- package/src/cli.ts +0 -114
- package/src/core/bus.ts +0 -34
- package/src/core/changes.ts +0 -33
- package/src/core/env.ts +0 -27
- package/src/core/errors.ts +0 -32
- package/src/core/frontmatter.ts +0 -47
- package/src/core/fs.test.ts +0 -38
- package/src/core/fs.ts +0 -92
- package/src/core/index.ts +0 -193
- package/src/core/path.test.ts +0 -14
- package/src/core/path.ts +0 -79
- package/src/core/projects.ts +0 -90
- package/src/core/text.ts +0 -39
- package/src/harness.ts +0 -299
- package/src/index.ts +0 -70
- package/src/mcp/connections.ts +0 -614
- package/src/mcp/manager.ts +0 -130
- package/src/mcp/oauth.ts +0 -195
- package/src/mcp/scopeMigration.test.ts +0 -79
- package/src/mcp/scopeMigration.ts +0 -204
- package/src/mcp/service.test.ts +0 -376
- package/src/mcp/service.ts +0 -549
- package/src/mcp/types.ts +0 -108
- package/src/memory/api.test.ts +0 -82
- package/src/memory/api.ts +0 -289
- package/src/memory/blocks.test.ts +0 -78
- package/src/memory/blocks.ts +0 -406
- package/src/memory/caller.ts +0 -57
- package/src/memory/dispatch.ts +0 -59
- package/src/memory/facts.test.ts +0 -41
- package/src/memory/facts.ts +0 -248
- package/src/memory/graph.ts +0 -103
- package/src/memory/health.test.ts +0 -130
- package/src/memory/health.ts +0 -74
- package/src/memory/maintainer.ts +0 -272
- package/src/memory/maintenance.test.ts +0 -176
- package/src/memory/maintenance.ts +0 -330
- package/src/memory/migrate.ts +0 -79
- package/src/memory/operations.ts +0 -138
- package/src/memory/proposals.test.ts +0 -168
- package/src/memory/proposals.ts +0 -576
- package/src/memory/recall.test.ts +0 -104
- package/src/memory/recall.ts +0 -271
- package/src/memory/references.ts +0 -77
- package/src/memory/runContext.ts +0 -93
- package/src/memory/secrets.ts +0 -6
- package/src/memory/store.test.ts +0 -66
- package/src/memory/store.ts +0 -385
- package/src/memory/wiki.test.ts +0 -47
- package/src/memory/wiki.ts +0 -422
- package/src/memory/wikiIndex.test.ts +0 -88
- package/src/memory/wikiIndex.ts +0 -116
- package/src/plugins/api.ts +0 -181
- package/src/plugins/attachments.test.ts +0 -84
- package/src/plugins/attachments.ts +0 -141
- package/src/plugins/builtin.ts +0 -82
- package/src/plugins/catalogue.test.ts +0 -254
- package/src/plugins/catalogue.ts +0 -251
- package/src/plugins/cli.ts +0 -184
- package/src/plugins/discover.ts +0 -151
- package/src/plugins/firstParty.test.ts +0 -143
- package/src/plugins/firstParty.ts +0 -129
- package/src/plugins/hooks.test.ts +0 -250
- package/src/plugins/hooks.ts +0 -239
- package/src/plugins/identity.ts +0 -37
- package/src/plugins/install.test.ts +0 -107
- package/src/plugins/install.ts +0 -106
- package/src/plugins/inventory.ts +0 -123
- package/src/plugins/manifest.ts +0 -76
- package/src/plugins/plugins.test.ts +0 -205
- package/src/plugins/plugins.ts +0 -189
- package/src/plugins/proposal.ts +0 -121
- package/src/plugins/runtime.test.ts +0 -40
- package/src/plugins/runtime.ts +0 -82
- package/src/plugins/trust.ts +0 -142
- package/src/plugins/types.ts +0 -149
- package/src/prompt/compile.test.ts +0 -261
- package/src/prompt/compile.ts +0 -732
- package/src/server/methods.ts +0 -314
- package/src/server/remote.ts +0 -77
- package/src/server/rpc.ts +0 -116
- package/src/server/serve.ts +0 -28
- package/src/server/server.test.ts +0 -174
- package/src/sessions/approvals.ts +0 -118
- package/src/sessions/contracts.ts +0 -310
- package/src/sessions/conversation.test.ts +0 -150
- package/src/sessions/extensionSession.test.ts +0 -74
- package/src/sessions/extensionUi.test.ts +0 -65
- package/src/sessions/extensionUi.ts +0 -102
- package/src/sessions/history.ts +0 -51
- package/src/sessions/lineageJoin.ts +0 -75
- package/src/sessions/policy.ts +0 -32
- package/src/sessions/pricing.ts +0 -60
- package/src/sessions/reasoningSummaries.ts +0 -59
- package/src/sessions/registry.ts +0 -294
- package/src/sessions/runIndex.ts +0 -99
- package/src/sessions/runStore.test.ts +0 -152
- package/src/sessions/runStore.ts +0 -332
- package/src/sessions/runner.ts +0 -569
- package/src/sessions/runtimeResources.test.ts +0 -61
- package/src/sessions/sessions.test.ts +0 -455
- package/src/sessions/sessions.ts +0 -295
- package/src/skills/registry.test.ts +0 -104
- package/src/skills/registry.ts +0 -407
- package/src/terminal/cli.ts +0 -149
- package/src/terminal/client.ts +0 -74
- package/src/terminal/controller.ts +0 -227
- package/src/terminal/launch.test.ts +0 -34
- package/src/terminal/launch.ts +0 -88
- package/src/terminal/piExtension.ts +0 -210
- package/src/terminal/pluginControl.test.ts +0 -146
- package/src/terminal/pluginMenu.ts +0 -112
- package/src/terminal/socket.test.ts +0 -71
- package/src/terminal/socket.ts +0 -221
- package/src/terminal/standalone.test.ts +0 -130
- package/src/terminal/standalone.ts +0 -144
- package/src/terminal/terminal.test.ts +0 -330
- package/src/terminal/terminals.ts +0 -443
- package/src/tools/askUser.ts +0 -45
- package/src/tools/definitions.ts +0 -187
- package/src/tools/memoryTools.ts +0 -22
- package/src/tools/pluginPropose.ts +0 -41
- package/src/tools/types.ts +0 -39
- package/src/web/safeRequest.ts +0 -171
- package/src/web/text.ts +0 -53
- package/src/web/tools.ts +0 -134
- package/src/web/web.test.ts +0 -141
package/src/plugins/plugins.ts
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
// The harness decides what a Pi session loads. Pi's own discovery is switched off for sessions
|
|
2
|
-
// and terminals; they receive the paths chosen here, so nothing reaches a model unlisted.
|
|
3
|
-
import { chmodSync, readdirSync } from 'node:fs'
|
|
4
|
-
import { join } from 'node:path'
|
|
5
|
-
|
|
6
|
-
import type { Bus } from '../core/bus'
|
|
7
|
-
import { now, readJsonOr, writeJson } from '../core/fs'
|
|
8
|
-
import { removeLinkedSkills } from '../skills/registry'
|
|
9
|
-
import { attached, type Attachments, type AttachScope } from './attachments'
|
|
10
|
-
import { piResolver, type Found, type ResolveInput, type Resolver } from './discover'
|
|
11
|
-
import { foundIds, pluginIdOf } from './identity'
|
|
12
|
-
import { packageCode, projectCode, Trust, type ProjectCode } from './trust'
|
|
13
|
-
|
|
14
|
-
/** What one session or terminal loads besides the registry's own items. */
|
|
15
|
-
export interface Loading {
|
|
16
|
-
/** Extension files and folders, in the order Pi would have loaded them. */
|
|
17
|
-
extensionPaths: string[]
|
|
18
|
-
skillPaths: string[]
|
|
19
|
-
promptPaths: string[]
|
|
20
|
-
themePaths: string[]
|
|
21
|
-
/** The `pi` command's own extensions left on, as `builtin:<name>`. Only a terminal has them. */
|
|
22
|
-
builtins: string[]
|
|
23
|
-
/** Hooks files the harness runs itself. Pi is never given them. */
|
|
24
|
-
hooks: { path: string; packageRoot: string; hash: string }[]
|
|
25
|
-
/** Everything found, loaded or not. */
|
|
26
|
-
found: Found[]
|
|
27
|
-
/** Paths of found items a person switched off for this caller. */
|
|
28
|
-
detached: string[]
|
|
29
|
-
/** Applies the same attachment policy to resources supplied by a trusted extension. */
|
|
30
|
-
resourceAllowed?: (item: Found) => boolean
|
|
31
|
-
/** Code the working folder brings, and whether a person trusted it. */
|
|
32
|
-
projectCode: (ProjectCode & { trusted: boolean }) | null
|
|
33
|
-
/** Each installed package that brings code, by its folder, and whether a person trusted it. */
|
|
34
|
-
packageCode: Record<string, ProjectCode & { trusted: boolean }>
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** What one loaded extension added to a session. */
|
|
38
|
-
export interface Registered {
|
|
39
|
-
path: string
|
|
40
|
-
hooks: string[]
|
|
41
|
-
tools: string[]
|
|
42
|
-
commands: string[]
|
|
43
|
-
flags: string[]
|
|
44
|
-
shortcuts: string[]
|
|
45
|
-
/** Fingerprint at observation time; stale registrations are not presented as current. */
|
|
46
|
-
hash?: string | null
|
|
47
|
-
seenAt?: string
|
|
48
|
-
resources?: RuntimeResource[]
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface RuntimeResource {
|
|
52
|
-
kind: 'skill' | 'command' | 'theme'
|
|
53
|
-
name: string
|
|
54
|
-
path: string
|
|
55
|
-
chars: number | null
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface PluginsOptions {
|
|
59
|
-
stateDir: string
|
|
60
|
-
bus: Bus
|
|
61
|
-
resolver?: Resolver
|
|
62
|
-
/** The choices a person made about what is switched on, or `null` while there is no data root. */
|
|
63
|
-
attachments?: () => Attachments | null
|
|
64
|
-
/** The folder of packages kept with a person's data, or `null` while there is no data root. */
|
|
65
|
-
packagesDir?: () => string | null
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** The package folders inside a folder: each direct child that is a folder and not hidden. */
|
|
69
|
-
export function packageFolders(directory: string | null): string[] {
|
|
70
|
-
if (!directory) return []
|
|
71
|
-
try {
|
|
72
|
-
return readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith('.')).map((entry) => join(directory, entry.name)).sort()
|
|
73
|
-
} catch {
|
|
74
|
-
return []
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export interface LoadingInput extends ResolveInput {
|
|
79
|
-
/** Who the session is for. Something switched off for this agent or project does not load. */
|
|
80
|
-
caller?: AttachScope
|
|
81
|
-
/** The contributions frozen for a run. When present, this snapshot governs loading. */
|
|
82
|
-
contributions?: readonly string[]
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Which code a person trusted: the working folder's as a whole, and each installed package's. */
|
|
86
|
-
export interface Trusted {
|
|
87
|
-
project: boolean
|
|
88
|
-
/** Folders of installed packages whose code is not trusted. */
|
|
89
|
-
untrustedPackages: ReadonlySet<string>
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export const trustedBy = (loading: Pick<Loading, 'projectCode' | 'packageCode'> | null): Trusted => ({
|
|
93
|
-
project: !!loading?.projectCode?.trusted,
|
|
94
|
-
untrustedPackages: new Set(Object.entries(loading?.packageCode ?? {}).filter(([, code]) => !code.trusted).map(([root]) => root))
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Whether a found item loads: it is on in Pi's settings, and if it is code, a person trusted it.
|
|
99
|
-
* A loose file in the shared folder was put there by hand and needs no trust. Text always loads.
|
|
100
|
-
*/
|
|
101
|
-
export function loads(item: Found, trusted: Trusted): boolean {
|
|
102
|
-
if (!item.enabled) return false
|
|
103
|
-
if (item.kind !== 'extension' && item.kind !== 'hook') return true
|
|
104
|
-
if (item.project) return trusted.project
|
|
105
|
-
return !(item.packageRoot && trusted.untrustedPackages.has(item.packageRoot))
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Whether a person left a found item switched on for a caller. Pi reads its instruction files
|
|
110
|
-
* itself and its command owns its built-in extensions, so neither can be switched off here.
|
|
111
|
-
*/
|
|
112
|
-
export function switchable(item: Found): boolean {
|
|
113
|
-
return item.kind !== 'context' && item.origin !== 'builtin'
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export class Plugins {
|
|
117
|
-
readonly trust: Trust
|
|
118
|
-
private readonly resolver: Resolver
|
|
119
|
-
private readonly registeredPath: string
|
|
120
|
-
|
|
121
|
-
constructor(private readonly options: PluginsOptions) {
|
|
122
|
-
this.trust = new Trust(options.stateDir, options.bus)
|
|
123
|
-
this.resolver = options.resolver ?? piResolver
|
|
124
|
-
this.registeredPath = join(options.stateDir, 'plugin-registered.json')
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/** Finds what Pi would load in this folder, and chooses what does. */
|
|
128
|
-
async loading(input: LoadingInput): Promise<Loading> {
|
|
129
|
-
// Links an earlier version put in the shared skills folder would be found as skills again.
|
|
130
|
-
removeLinkedSkills(join(input.agentDir, 'skills'))
|
|
131
|
-
const found = await this.resolver({ ...input, packages: packageFolders(this.options.packagesDir?.() ?? null) })
|
|
132
|
-
const code = projectCode(found, input.cwd)
|
|
133
|
-
const packages = packageCode(found)
|
|
134
|
-
// Packages that loaded before trust was asked per package keep loading: they are taken over once.
|
|
135
|
-
if (!this.trust.adopted) this.trust.adopt([...packages].map(([folder, packaged]) => ({ code: packaged, folder })))
|
|
136
|
-
const trustedHashes = new Set(this.trust.list().map((record) => record.hash))
|
|
137
|
-
const projectCodeState = code ? { ...code, trusted: trustedHashes.has(code.hash) } : null
|
|
138
|
-
const packageCodeState = Object.fromEntries([...packages].map(([folder, packaged]) => [folder, { ...packaged, trusted: trustedHashes.has(packaged.hash) }]))
|
|
139
|
-
const trusted = trustedBy({ projectCode: projectCodeState, packageCode: packageCodeState })
|
|
140
|
-
const choices = this.options.attachments?.() ?? null
|
|
141
|
-
const off = (item: Found): boolean => {
|
|
142
|
-
if (!choices || !switchable(item)) return false
|
|
143
|
-
// A hook runs for everyone or not at all, so only its plugin's switch for everyone counts.
|
|
144
|
-
if (item.kind === 'hook') return !attached(choices, [pluginIdOf(item)], {}, true)
|
|
145
|
-
return !attached(choices, foundIds(item), input.caller ?? {}, true)
|
|
146
|
-
}
|
|
147
|
-
const allowed = (item: Found): boolean => input.contributions === undefined
|
|
148
|
-
? !off(item)
|
|
149
|
-
: input.contributions.includes(foundIds(item)[0])
|
|
150
|
-
const detached = found.filter((item) => !allowed(item)).map((item) => item.path)
|
|
151
|
-
const chosen = found.filter((item) => loads(item, trusted) && allowed(item))
|
|
152
|
-
const paths = (kind: Found['kind'], builtin: boolean): string[] => chosen.filter((item) => item.kind === kind && (item.origin === 'builtin') === builtin).map((item) => item.path)
|
|
153
|
-
return {
|
|
154
|
-
extensionPaths: paths('extension', false),
|
|
155
|
-
skillPaths: paths('skill', false),
|
|
156
|
-
promptPaths: paths('prompt', false),
|
|
157
|
-
themePaths: paths('theme', false),
|
|
158
|
-
builtins: paths('extension', true),
|
|
159
|
-
hooks: chosen.filter((item) => item.kind === 'hook' && item.packageRoot).map((item) => ({ path: item.path, packageRoot: item.packageRoot!, hash: packageCodeState[item.packageRoot!]?.hash ?? '' })),
|
|
160
|
-
found,
|
|
161
|
-
detached,
|
|
162
|
-
resourceAllowed: allowed,
|
|
163
|
-
projectCode: projectCodeState,
|
|
164
|
-
packageCode: packageCodeState
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/** What each extension registered the last time a session loaded it. */
|
|
169
|
-
registered(): Record<string, Registered & { seenAt: string }> {
|
|
170
|
-
return readJsonOr<Record<string, Registered & { seenAt: string }>>(this.registeredPath, {})
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/** Records what a session's extensions registered, so the list can show it without loading code. */
|
|
174
|
-
loaded(extensions: readonly Registered[]): void {
|
|
175
|
-
if (!extensions.length) return
|
|
176
|
-
const stored = this.registered()
|
|
177
|
-
let changed = false
|
|
178
|
-
for (const extension of extensions) {
|
|
179
|
-
const { seenAt: _seenAt, ...before } = stored[extension.path] ?? ({} as Registered & { seenAt: string })
|
|
180
|
-
if (JSON.stringify(before) === JSON.stringify(extension)) continue
|
|
181
|
-
stored[extension.path] = { ...extension, seenAt: now() }
|
|
182
|
-
changed = true
|
|
183
|
-
}
|
|
184
|
-
if (!changed) return
|
|
185
|
-
writeJson(this.registeredPath, stored)
|
|
186
|
-
chmodSync(this.registeredPath, 0o600)
|
|
187
|
-
this.options.bus.emit({ type: 'plugins.changed' })
|
|
188
|
-
}
|
|
189
|
-
}
|
package/src/plugins/proposal.ts
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
// A plugin an agent wrote for a person to consider. Nothing here installs anything on its own:
|
|
2
|
-
// the proposal waits in the Inbox; accepting it writes a package beside the person's data,
|
|
3
|
-
// switched off and (when it brings code) untrusted. The person then reads it, switches it on,
|
|
4
|
-
// and trusts it. No agent has a tool for either of those.
|
|
5
|
-
import { existsSync, mkdirSync, mkdtempSync, renameSync, rmSync, writeFileSync } from 'node:fs'
|
|
6
|
-
import { dirname, join } from 'node:path'
|
|
7
|
-
import { z } from 'zod'
|
|
8
|
-
|
|
9
|
-
import { invalid } from '../core/errors'
|
|
10
|
-
import { now, safeRelative, writeJson } from '../core/fs'
|
|
11
|
-
import { slugify } from '../core/projects'
|
|
12
|
-
import { inbox } from '../memory/operations'
|
|
13
|
-
import { looksSecret } from '../memory/secrets'
|
|
14
|
-
import * as store from '../memory/store'
|
|
15
|
-
import * as attachments from './attachments'
|
|
16
|
-
|
|
17
|
-
/** The parts a plugin can say it extends. */
|
|
18
|
-
export const EXTENDABLE = ['memory', 'wiki', 'history'] as const
|
|
19
|
-
|
|
20
|
-
const MAX_FILES = 40
|
|
21
|
-
const MAX_BYTES = 256 * 1024
|
|
22
|
-
const HOOKS_FILES = ['hooks.mjs', 'hooks.js']
|
|
23
|
-
|
|
24
|
-
export const pluginProposalSchema = z.object({
|
|
25
|
-
name: z.string(),
|
|
26
|
-
description: z.string().default(''),
|
|
27
|
-
extends: z.array(z.string()).default([]),
|
|
28
|
-
reason: z.string().default(''),
|
|
29
|
-
files: z.array(z.object({ path: z.string(), content: z.string() }))
|
|
30
|
-
})
|
|
31
|
-
export type PluginProposal = z.infer<typeof pluginProposalSchema>
|
|
32
|
-
|
|
33
|
-
interface Checked {
|
|
34
|
-
slug: string
|
|
35
|
-
extends: string[]
|
|
36
|
-
hooks: string | null
|
|
37
|
-
runsCode: boolean
|
|
38
|
-
files: { path: string; content: string }[]
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** The proposal with its paths made safe, or the reason it cannot be a plugin. */
|
|
42
|
-
export function checkPluginProposal(proposal: PluginProposal): Checked {
|
|
43
|
-
const name = proposal.name.trim()
|
|
44
|
-
const slug = slugify(name)
|
|
45
|
-
if (!name || slug.startsWith('page-') || name.length > 64) throw invalid('Give the plugin a short name of letters and numbers.')
|
|
46
|
-
if (!proposal.description.trim()) throw invalid('Say in one line what the plugin does; that line is what the user sees first.')
|
|
47
|
-
const unknown = proposal.extends.find((part) => !(EXTENDABLE as readonly string[]).includes(part))
|
|
48
|
-
if (unknown !== undefined) throw invalid(`A plugin can extend ${EXTENDABLE.join(', ')}; "${unknown}" is none of them.`)
|
|
49
|
-
if (!proposal.files.length || proposal.files.length > MAX_FILES) throw invalid(`A plugin needs between 1 and ${MAX_FILES} files.`)
|
|
50
|
-
const files: Checked['files'] = []
|
|
51
|
-
let bytes = 0
|
|
52
|
-
for (const file of proposal.files) {
|
|
53
|
-
let path: string
|
|
54
|
-
try {
|
|
55
|
-
path = safeRelative(file.path.replace(/^\.\//, ''))
|
|
56
|
-
} catch {
|
|
57
|
-
throw invalid(`"${file.path}" is not a path inside the plugin.`)
|
|
58
|
-
}
|
|
59
|
-
const parts = path.split('/')
|
|
60
|
-
if (parts.some((part) => part.startsWith('.') || part.includes(':') || part === 'node_modules')) throw invalid(`"${file.path}" cannot be part of a plugin.`)
|
|
61
|
-
if (path === 'package.json') throw invalid('Leave package.json out; it is written from the name and description you gave.')
|
|
62
|
-
if (files.some((existing) => existing.path.toLowerCase() === path.toLowerCase())) throw invalid(`"${file.path}" is listed twice.`)
|
|
63
|
-
bytes += Buffer.byteLength(file.content, 'utf8')
|
|
64
|
-
if (bytes > MAX_BYTES) throw invalid('The plugin is larger than 256 KiB.')
|
|
65
|
-
if (looksSecret(file.content)) throw invalid(`"${file.path}" contains text that looks like a credential. A plugin reaches outside through a connected app, never with a key of its own.`)
|
|
66
|
-
files.push({ path, content: file.content })
|
|
67
|
-
}
|
|
68
|
-
const hooks = files.find((file) => HOOKS_FILES.includes(file.path))?.path ?? null
|
|
69
|
-
return { slug, extends: [...new Set(proposal.extends)], hooks, runsCode: hooks !== null || files.some((file) => file.path.startsWith('extensions/')), files }
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Puts a proposed plugin in the Inbox, for a person to read before anything is written. */
|
|
73
|
-
export function parkPluginProposal(wiki: string, from: string, proposal: PluginProposal): string {
|
|
74
|
-
const checked = checkPluginProposal(proposal)
|
|
75
|
-
const listed = checked.files.map((file) => `- ${file.path} (${file.content.length} characters)`).join('\n')
|
|
76
|
-
const code = checked.runsCode ? '\n\nIt brings code. Accepting only saves the files: the code runs after you also switch the plugin on and trust it.' : ''
|
|
77
|
-
inbox(
|
|
78
|
-
wiki,
|
|
79
|
-
'proposed',
|
|
80
|
-
`Proposed plugin "${proposal.name.trim()}"`,
|
|
81
|
-
`${proposal.description.trim()}\n\n${proposal.reason.trim() || 'Review it before it is saved.'}${checked.extends.length ? `\n\nExtends: ${checked.extends.join(', ')}.` : ''}\n\nFiles:\n${listed}${code}\n\nFrom ${from}.`,
|
|
82
|
-
[],
|
|
83
|
-
null,
|
|
84
|
-
{ kind: 'plugin', proposal: { ...proposal, files: checked.files }, trigger: from }
|
|
85
|
-
)
|
|
86
|
-
store.log(wiki, `- ${now()} · ${from} · proposed plugin \`${checked.slug}\` · waiting in Inbox`)
|
|
87
|
-
return checked.slug
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** Writes an accepted proposal as a package beside the person's data, switched off everywhere. */
|
|
91
|
-
export function acceptPluginProposal(root: string, proposal: PluginProposal): string {
|
|
92
|
-
const checked = checkPluginProposal(proposal)
|
|
93
|
-
const directory = join(root, 'plugins')
|
|
94
|
-
const target = join(directory, checked.slug)
|
|
95
|
-
if (existsSync(target)) throw invalid(`There is already a plugin folder called "${checked.slug}". Remove it or rename the proposal.`)
|
|
96
|
-
mkdirSync(directory, { recursive: true })
|
|
97
|
-
// Written beside its place and moved in whole, so a half-written plugin is never found.
|
|
98
|
-
const staging = mkdtempSync(join(directory, '.accepting-'))
|
|
99
|
-
try {
|
|
100
|
-
writeJson(join(staging, 'package.json'), {
|
|
101
|
-
name: checked.slug,
|
|
102
|
-
version: '0.1.0',
|
|
103
|
-
description: proposal.description.trim(),
|
|
104
|
-
keywords: ['pi-package'],
|
|
105
|
-
...(checked.extends.length || checked.hooks ? { harness: { ...(checked.extends.length ? { extends: checked.extends } : {}), ...(checked.hooks ? { hooks: `./${checked.hooks}` } : {}) } } : {})
|
|
106
|
-
})
|
|
107
|
-
for (const file of checked.files) {
|
|
108
|
-
const path = join(staging, ...file.path.split('/'))
|
|
109
|
-
mkdirSync(dirname(path), { recursive: true })
|
|
110
|
-
writeFileSync(path, file.content, { flag: 'wx' })
|
|
111
|
-
}
|
|
112
|
-
renameSync(staging, target)
|
|
113
|
-
} catch (error) {
|
|
114
|
-
rmSync(staging, { recursive: true, force: true })
|
|
115
|
-
throw error
|
|
116
|
-
}
|
|
117
|
-
const choices = attachments.load(root)
|
|
118
|
-
attachments.set(choices, [`package:${checked.slug}`], {}, false, true)
|
|
119
|
-
attachments.save(root, choices)
|
|
120
|
-
return `Saved the plugin "${checked.slug}", switched off.${checked.runsCode ? ' Its code does not run until you switch it on and trust it.' : ' Switch it on to use it.'}`
|
|
121
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { mkdtempSync, symlinkSync } from 'node:fs'
|
|
2
|
-
import { tmpdir } from 'node:os'
|
|
3
|
-
import { join } from 'node:path'
|
|
4
|
-
import { expect, it } from 'vitest'
|
|
5
|
-
import type * as Pi from '@earendil-works/pi-coding-agent'
|
|
6
|
-
|
|
7
|
-
import { writeFile } from '../../tests/helpers'
|
|
8
|
-
import type { Found } from './discover'
|
|
9
|
-
import type { Loading, Registered } from './plugins'
|
|
10
|
-
import { observeResources } from './runtime'
|
|
11
|
-
|
|
12
|
-
it('expands theme directories, filters individual files and replaces each handler observation', async () => {
|
|
13
|
-
const pi = await import('@earendil-works/pi-coding-agent')
|
|
14
|
-
const base = mkdtempSync(join(tmpdir(), 'resource-observation-'))
|
|
15
|
-
const path = join(base, 'extension.ts')
|
|
16
|
-
writeFile(path, 'export default function() {}')
|
|
17
|
-
const first = join(base, 'themes/first.json')
|
|
18
|
-
const second = join(base, 'themes/second.json')
|
|
19
|
-
writeFile(first, '{}')
|
|
20
|
-
writeFile(second, '{}')
|
|
21
|
-
symlinkSync(join(base, 'absent.json'), join(base, 'themes/broken.json'))
|
|
22
|
-
writeFile(join(base, 'review.md'), '---\nname: runtime-review\ndescription: Review.\n---\nReview text.')
|
|
23
|
-
let active = true
|
|
24
|
-
const handlers = new Map([['resources_discover', [async () => active ? { themePaths: ['./themes'] } : undefined, async () => ({ promptPaths: ['./review.md'] })]]])
|
|
25
|
-
const extension = { path, resolvedPath: path, handlers }
|
|
26
|
-
const loader = { getExtensions: () => ({ extensions: [extension] }) } as unknown as Pi.DefaultResourceLoader
|
|
27
|
-
const owner: Found = { kind: 'extension', path, origin: 'shared', project: false, packageRoot: null, source: null, enabled: true }
|
|
28
|
-
const loading: Loading = { extensionPaths: [path], skillPaths: [], promptPaths: [], themePaths: [], builtins: [], hooks: [], found: [owner], detached: [], projectCode: null, packageCode: {}, resourceAllowed: (item) => item.path !== first }
|
|
29
|
-
const records: Registered[] = [{ path, tools: [], commands: [], hooks: ['resources_discover'], flags: [], shortcuts: [] }]
|
|
30
|
-
let published = 0
|
|
31
|
-
observeResources(pi, loader, loading, records, () => { published++ })
|
|
32
|
-
const wrapped = extension.handlers.get('resources_discover')! as unknown as Array<(event: unknown, context: unknown) => Promise<Pi.ResourcesDiscoverResult | undefined>>
|
|
33
|
-
expect(await wrapped[0]({}, {})).toMatchObject({ themePaths: [second] })
|
|
34
|
-
await wrapped[1]({}, {})
|
|
35
|
-
expect(records[0].resources?.map((resource) => resource.name)).toEqual(['first.json', 'second.json', 'runtime-review'])
|
|
36
|
-
active = false
|
|
37
|
-
await wrapped[0]({}, {})
|
|
38
|
-
expect(records[0].resources?.map((resource) => resource.name)).toEqual(['runtime-review'])
|
|
39
|
-
expect(published).toBe(3)
|
|
40
|
-
})
|
package/src/plugins/runtime.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
// Observe Pi's structured resource discovery without running code while the catalogue is read.
|
|
2
|
-
import { createHash } from 'node:crypto'
|
|
3
|
-
import { readFileSync, readdirSync, statSync } from 'node:fs'
|
|
4
|
-
import { dirname, resolve } from 'node:path'
|
|
5
|
-
import type * as Pi from '@earendil-works/pi-coding-agent'
|
|
6
|
-
|
|
7
|
-
import { charCount } from '../core/text'
|
|
8
|
-
import { FOUND_KINDS, describeFound } from './identity'
|
|
9
|
-
import type { Found } from './discover'
|
|
10
|
-
import type { Loading, Registered, RuntimeResource } from './plugins'
|
|
11
|
-
|
|
12
|
-
export function runtimeOwner(loading: Loading, path: string): Found | undefined {
|
|
13
|
-
return loading.found.find((item) => item.kind === 'extension' && (item.path === path || path.startsWith(`${item.path}/`)))
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function registrationHash(loading: Loading, path: string): string | null {
|
|
17
|
-
const owner = runtimeOwner(loading, path)
|
|
18
|
-
if (owner?.project) return loading.projectCode?.hash ?? null
|
|
19
|
-
if (owner?.packageRoot) return loading.packageCode[owner.packageRoot]?.hash ?? null
|
|
20
|
-
try { return createHash('sha256').update(readFileSync(path)).digest('hex') } catch { return null }
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** A discovered resource has the same identity for its switch, catalogue and load decision. */
|
|
24
|
-
export function resourceOf(owner: Found, kind: 'skill' | 'prompt' | 'theme', path: string): RuntimeResource {
|
|
25
|
-
const found = { ...owner, kind, path }
|
|
26
|
-
const described = describeFound(found)
|
|
27
|
-
let chars: number | null = null
|
|
28
|
-
try { chars = charCount(readFileSync(path, 'utf8')) } catch { /* Missing files remain observable. */ }
|
|
29
|
-
return { kind: FOUND_KINDS[kind] as RuntimeResource['kind'], name: described.name, path, chars }
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Wrap each handler separately so identical extension basenames never lose their owner. */
|
|
33
|
-
export function observeResources(pi: typeof Pi, loader: Pi.DefaultResourceLoader, loading: Loading, records: Registered[], publish: () => void): void {
|
|
34
|
-
for (const extension of loader.getExtensions().extensions) {
|
|
35
|
-
if (extension.hidden) continue
|
|
36
|
-
const path = extension.resolvedPath || extension.path
|
|
37
|
-
const owner = runtimeOwner(loading, path)
|
|
38
|
-
const record = records.find((entry) => entry.path === path)
|
|
39
|
-
if (!owner || !record) continue
|
|
40
|
-
const handlers = extension.handlers.get('resources_discover')
|
|
41
|
-
if (!handlers) continue
|
|
42
|
-
const observations = new Map<number, RuntimeResource[]>()
|
|
43
|
-
extension.handlers.set('resources_discover', handlers.map((handler, index) => async (event, context) => {
|
|
44
|
-
const result = await handler(event, context) as Pi.ResourcesDiscoverResult | undefined
|
|
45
|
-
if (!result) {
|
|
46
|
-
observations.delete(index)
|
|
47
|
-
record.resources = [...observations.values()].flat()
|
|
48
|
-
publish()
|
|
49
|
-
return result
|
|
50
|
-
}
|
|
51
|
-
const collected: RuntimeResource[] = []
|
|
52
|
-
const allowed = (kind: 'skill' | 'prompt' | 'theme', resourcePath: string): boolean => {
|
|
53
|
-
const resource = resourceOf(owner, kind, resourcePath)
|
|
54
|
-
collected.push(resource)
|
|
55
|
-
return loading.resourceAllowed?.({ ...owner, kind, path: resourcePath }) !== false
|
|
56
|
-
}
|
|
57
|
-
const absolute = (entry: string): string => resolve(dirname(path), entry)
|
|
58
|
-
const skills = pi.loadSkills({ cwd: dirname(path), agentDir: dirname(path), includeDefaults: false, skillPaths: (result.skillPaths ?? []).map(absolute) }).skills
|
|
59
|
-
const scanner = new pi.DefaultResourceLoader({ cwd: dirname(path), agentDir: dirname(path), settingsManager: pi.SettingsManager.inMemory({}), noExtensions: true, noSkills: true, noThemes: true, noContextFiles: true, noPromptTemplates: true, additionalPromptTemplatePaths: (result.promptPaths ?? []).map(absolute) })
|
|
60
|
-
await scanner.reload()
|
|
61
|
-
const prompts = scanner.getPrompts().prompts
|
|
62
|
-
const themes = (result.themePaths ?? []).map(absolute).flatMap((theme) => {
|
|
63
|
-
try {
|
|
64
|
-
if (statSync(theme).isDirectory()) return readdirSync(theme).filter((name) => {
|
|
65
|
-
if (!name.endsWith('.json')) return false
|
|
66
|
-
try { return statSync(resolve(theme, name)).isFile() } catch { return false }
|
|
67
|
-
}).map((name) => resolve(theme, name))
|
|
68
|
-
} catch { /* Preserve missing paths for Pi's diagnostics. */ }
|
|
69
|
-
return [theme]
|
|
70
|
-
})
|
|
71
|
-
const filtered = {
|
|
72
|
-
skillPaths: skills.filter((skill) => allowed('skill', skill.filePath)).map((skill) => skill.filePath),
|
|
73
|
-
promptPaths: prompts.filter((prompt) => allowed('prompt', prompt.filePath)).map((prompt) => prompt.filePath),
|
|
74
|
-
themePaths: themes.filter((theme) => allowed('theme', theme))
|
|
75
|
-
}
|
|
76
|
-
observations.set(index, collected)
|
|
77
|
-
record.resources = [...new Map([...observations.values()].flat().map((entry) => [`${entry.kind}:${entry.path}`, entry])).values()]
|
|
78
|
-
publish()
|
|
79
|
-
return filtered
|
|
80
|
-
}))
|
|
81
|
-
}
|
|
82
|
-
}
|
package/src/plugins/trust.ts
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
// Code a person did not write into the shared folder by hand loads only after they trusted it:
|
|
2
|
-
// a working folder's own (its `.pi/extensions`, or a package its settings name), and every
|
|
3
|
-
// installed package. Trust belongs to the code, not the folder: it is a hash of the files, so a
|
|
4
|
-
// copy of the same code elsewhere is trusted too and a changed file asks again.
|
|
5
|
-
import { createHash } from 'node:crypto'
|
|
6
|
-
import { chmodSync, lstatSync, readdirSync, readFileSync } from 'node:fs'
|
|
7
|
-
import { basename, dirname, join, relative, sep } from 'node:path'
|
|
8
|
-
|
|
9
|
-
import type { Bus } from '../core/bus'
|
|
10
|
-
import { now, readJsonOr, writeJson } from '../core/fs'
|
|
11
|
-
import { compareText } from '../core/text'
|
|
12
|
-
import type { Found } from './discover'
|
|
13
|
-
|
|
14
|
-
export interface ProjectCode {
|
|
15
|
-
/** SHA-256 over every file's name and contents. */
|
|
16
|
-
hash: string
|
|
17
|
-
/** The files, named from the working folder where they are inside it. */
|
|
18
|
-
files: string[]
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Dependencies and version control are left out: the hash says whether the code a person
|
|
22
|
-
// read has changed, and is not a seal on everything that code can import.
|
|
23
|
-
const SKIPPED = new Set(['node_modules', '.git'])
|
|
24
|
-
const MAX_FILES = 5000
|
|
25
|
-
|
|
26
|
-
function walk(path: string, into: string[]): void {
|
|
27
|
-
if (into.length >= MAX_FILES) return
|
|
28
|
-
let meta
|
|
29
|
-
try {
|
|
30
|
-
meta = lstatSync(path)
|
|
31
|
-
} catch {
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
|
-
if (meta.isSymbolicLink()) return
|
|
35
|
-
if (meta.isFile()) {
|
|
36
|
-
into.push(path)
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
if (!meta.isDirectory()) return
|
|
40
|
-
for (const name of readdirSync(path).sort(compareText)) if (!SKIPPED.has(name)) walk(join(path, name), into)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** The folder or file that holds one extension: its package, its own folder, or the file. */
|
|
44
|
-
const unit = (item: Found): string => item.packageRoot ?? (basename(item.path).startsWith('index.') ? dirname(item.path) : item.path)
|
|
45
|
-
|
|
46
|
-
/** A hash over the files of some folders and files, each named from `base` where it is inside it. */
|
|
47
|
-
function hashOf(units: readonly string[], base: string): ProjectCode {
|
|
48
|
-
const paths: string[] = []
|
|
49
|
-
for (const path of [...units].sort(compareText)) walk(path, paths)
|
|
50
|
-
const name = (path: string): string => {
|
|
51
|
-
const within = relative(base, path)
|
|
52
|
-
return within && !within.startsWith('..') ? within.split(sep).join('/') : path
|
|
53
|
-
}
|
|
54
|
-
const hash = createHash('sha256')
|
|
55
|
-
const files: string[] = []
|
|
56
|
-
for (const path of [...new Set(paths)].sort(compareText)) {
|
|
57
|
-
let contents: Buffer
|
|
58
|
-
try {
|
|
59
|
-
contents = readFileSync(path)
|
|
60
|
-
} catch {
|
|
61
|
-
continue
|
|
62
|
-
}
|
|
63
|
-
files.push(name(path))
|
|
64
|
-
hash.update(`${name(path)}\0${contents.length}\0`).update(contents)
|
|
65
|
-
}
|
|
66
|
-
return { hash: hash.digest('hex'), files }
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** The code the working folder brings, or `null` when it brings none. */
|
|
70
|
-
export function projectCode(found: readonly Found[], cwd: string): ProjectCode | null {
|
|
71
|
-
const units = [...new Set(found.filter((item) => item.project && item.kind === 'extension' && item.enabled).map(unit))]
|
|
72
|
-
return units.length ? hashOf(units, cwd) : null
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Each package a person installed that brings code, by its folder: the whole package is hashed,
|
|
77
|
-
* so a changed skill or manifest asks again as well. A working folder's packages are part of
|
|
78
|
-
* `projectCode`.
|
|
79
|
-
*/
|
|
80
|
-
export function packageCode(found: readonly Found[]): Map<string, ProjectCode> {
|
|
81
|
-
const roots = [...new Set(found.filter((item) => !item.project && (item.kind === 'extension' || item.kind === 'hook') && item.packageRoot).map((item) => item.packageRoot!))]
|
|
82
|
-
return new Map(roots.map((root) => [root, hashOf([root], root)]))
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export interface TrustRecord {
|
|
86
|
-
hash: string
|
|
87
|
-
/** The working folder it was trusted in. */
|
|
88
|
-
folder: string
|
|
89
|
-
files: string[]
|
|
90
|
-
trustedAt: string
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export class Trust {
|
|
94
|
-
private readonly path: string
|
|
95
|
-
|
|
96
|
-
constructor(stateDir: string, private readonly bus: Bus) {
|
|
97
|
-
this.path = join(stateDir, 'plugin-trust.json')
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
private read(): { trusted?: unknown; adopted?: unknown } {
|
|
101
|
-
return readJsonOr<{ trusted?: unknown; adopted?: unknown }>(this.path, {})
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
list(): TrustRecord[] {
|
|
105
|
-
const stored = this.read()
|
|
106
|
-
return Array.isArray(stored.trusted) ? (stored.trusted as TrustRecord[]).filter((record) => typeof record?.hash === 'string') : []
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** Whether the packages that loaded before trust was asked per package were taken over as trusted. */
|
|
110
|
-
get adopted(): boolean {
|
|
111
|
-
return this.read().adopted === true
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Trusts the packages present the first time this runs, so nothing that loaded before stops.
|
|
116
|
-
* It happens once; a package installed later, or one of these after a change, waits for a person.
|
|
117
|
-
*/
|
|
118
|
-
adopt(codes: readonly { code: ProjectCode; folder: string }[]): void {
|
|
119
|
-
const records = this.list()
|
|
120
|
-
for (const { code, folder } of codes) if (!records.some((record) => record.hash === code.hash)) records.push({ hash: code.hash, folder, files: code.files, trustedAt: now() })
|
|
121
|
-
writeJson(this.path, { trusted: records, adopted: true })
|
|
122
|
-
chmodSync(this.path, 0o600)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
has(hash: string): boolean {
|
|
126
|
-
return this.list().some((record) => record.hash === hash)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
private save(records: TrustRecord[]): void {
|
|
130
|
-
writeJson(this.path, { trusted: records, ...(this.adopted ? { adopted: true } : {}) })
|
|
131
|
-
chmodSync(this.path, 0o600)
|
|
132
|
-
this.bus.emit({ type: 'plugins.changed' })
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
grant(code: ProjectCode, folder: string): void {
|
|
136
|
-
this.save([...this.list().filter((record) => record.hash !== code.hash), { hash: code.hash, folder, files: code.files, trustedAt: now() }])
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
revoke(hash: string): void {
|
|
140
|
-
this.save(this.list().filter((record) => record.hash !== hash))
|
|
141
|
-
}
|
|
142
|
-
}
|