@north-light/crouter 0.3.227 → 0.3.228
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/dist/api/client.d.ts +5 -5
- package/dist/api/client.js +3 -3
- package/dist/api/dto/broker-ops.d.ts +8 -0
- package/dist/api/dto/memory.d.ts +8 -7
- package/dist/api/dto/memory.js +2 -2
- package/dist/builtin-memory/05-kinds/design/00-base.md +3 -4
- package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +4 -3
- package/dist/builtin-memory/05-kinds/design/design-contract.md +3 -6
- package/dist/builtin-memory/05-kinds/plan/00-base.md +3 -4
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +4 -5
- package/dist/builtin-memory/05-kinds/plan/plan-contract.md +5 -13
- package/dist/builtin-memory/design/guide.md +38 -16
- package/dist/builtin-memory/design/roadmap.md +4 -4
- package/dist/builtin-memory/insights/init.md +5 -5
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
- package/dist/builtin-memory/internal/memory-loading.md +6 -6
- package/dist/builtin-memory/internal/plugins.md +1 -1
- package/dist/builtin-memory/plan/guide.md +53 -0
- package/dist/builtin-memory/plan/roadmap.md +10 -8
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +50 -37
- package/dist/clients/attach/__tests__/pane-tag-successor.test.js +56 -0
- package/dist/clients/attach/__tests__/profile-files.test.js +21 -0
- package/dist/clients/attach/chrome/inbox-strip.js +11 -7
- package/dist/clients/attach/session/pane-tag.d.ts +28 -2
- package/dist/clients/attach/session/pane-tag.js +48 -22
- package/dist/clients/attach/session/profile-files.d.ts +6 -0
- package/dist/clients/attach/session/profile-files.js +44 -11
- package/dist/clients/attach/slash/dispatch.js +4 -7
- package/dist/clients/attach/viewer.js +588 -596
- package/dist/commands/__tests__/surface-reload-target.test.js +21 -0
- package/dist/commands/api-client.d.ts +3 -7
- package/dist/commands/api-client.js +7 -12
- package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +292 -0
- package/dist/commands/memory/__tests__/repository-root-lint.test.js +146 -0
- package/dist/commands/memory/delete.js +51 -24
- package/dist/commands/memory/edit.js +50 -10
- package/dist/commands/memory/find.js +105 -56
- package/dist/commands/memory/history.js +51 -31
- package/dist/commands/memory/lint.d.ts +0 -9
- package/dist/commands/memory/lint.js +78 -322
- package/dist/commands/memory/list.d.ts +12 -4
- package/dist/commands/memory/list.js +64 -25
- package/dist/commands/memory/move.js +103 -73
- package/dist/commands/memory/origin.js +35 -5
- package/dist/commands/memory/read.d.ts +4 -0
- package/dist/commands/memory/read.js +132 -80
- package/dist/commands/memory/shared.d.ts +115 -25
- package/dist/commands/memory/shared.js +331 -74
- package/dist/commands/memory/write.js +72 -21
- package/dist/commands/memory.js +2 -2
- package/dist/commands/node/lifecycle.js +2 -2
- package/dist/commands/pkg/market-manage.js +25 -24
- package/dist/commands/pkg/plugin-manage.d.ts +22 -6
- package/dist/commands/pkg/plugin-manage.js +118 -25
- package/dist/commands/pkg/shared.d.ts +8 -0
- package/dist/commands/pkg/shared.js +23 -20
- package/dist/commands/surface-reload.d.ts +5 -0
- package/dist/commands/surface-reload.js +9 -1
- package/dist/commands/sys/__tests__/migrate.test.js +1140 -22
- package/dist/commands/sys/__tests__/sync-project-guidance.test.js +218 -0
- package/dist/commands/sys/migrate.js +126 -140
- package/dist/commands/sys/panels/profiles-panel.d.ts +66 -0
- package/dist/commands/sys/panels/profiles-panel.js +599 -0
- package/dist/commands/sys/settings-shell.d.ts +4 -2
- package/dist/commands/sys/settings-shell.js +35 -4
- package/dist/commands/sys/settings.js +21 -5
- package/dist/commands/sys/sync-deps.d.ts +2 -0
- package/dist/commands/sys/sync-deps.js +26 -15
- package/dist/commands/sys/sync-project-guidance.js +228 -145
- package/dist/commands/sys/sync-skills.js +28 -17
- package/dist/commands/sys/update.js +11 -3
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +15 -2
- package/dist/core/__tests__/config-change-delta.test.js +106 -0
- package/dist/core/__tests__/context-intro.test.js +13 -9
- package/dist/core/__tests__/daemon-boot.test.js +22 -16
- package/dist/core/__tests__/fixtures/memory-slash-live-probe.js +5 -1
- package/dist/core/__tests__/human-deliver.test.js +19 -0
- package/dist/core/__tests__/inline-memory-refs.test.js +50 -46
- package/dist/core/__tests__/{serial → integration}/broker-fork-seam.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/broker-sdk-wiring.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/broker-snapshot-history.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/command-plugins.test.js +94 -3
- package/dist/core/__tests__/{serial → integration}/deferred-no-wake.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/flagship-lifecycle.test.js +13 -27
- package/dist/core/__tests__/{serial → integration}/host-teardown-process-group.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/human-deliver-e2e.test.js +1 -1
- package/dist/core/__tests__/{live-mutation-verbs.test.js → integration/live-mutation-verbs.test.js} +22 -33
- package/dist/core/__tests__/{serial → integration}/live-mutation.test.js +21 -41
- package/dist/core/__tests__/integration/refresh-stall-recycle.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/refresh-stall-recycle.test.js +1 -1
- package/dist/core/__tests__/integration/revive.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/revive.test.js +38 -19
- package/dist/core/__tests__/integration/spawn-root.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/spawn-root.test.js +104 -1
- package/dist/core/__tests__/integration/subscription-delivery.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/subscription-delivery.test.js +1 -1
- package/dist/core/__tests__/integration/tmux-surface.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/tmux-surface.test.js +1 -1
- package/dist/core/__tests__/integration/worktree-land.test.d.ts +1 -0
- package/dist/core/__tests__/integration/worktree-land.test.js +400 -0
- package/dist/core/__tests__/integration/worktree-reap.test.d.ts +1 -0
- package/dist/core/__tests__/{serial/worktree.test.js → integration/worktree-reap.test.js} +6 -338
- package/dist/core/__tests__/kickoff.test.js +16 -5
- package/dist/core/__tests__/memory-resolver-precedence.test.js +122 -91
- package/dist/core/__tests__/nested-store-discovery.test.js +5 -3
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
- package/dist/core/__tests__/on-read-dedup-resume.test.js +39 -27
- package/dist/core/__tests__/on-read-nested-store.test.js +19 -13
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +138 -44
- package/dist/core/__tests__/repository-association.test.d.ts +1 -0
- package/dist/core/__tests__/repository-association.test.js +153 -0
- package/dist/core/__tests__/repository-root-identity.test.d.ts +1 -0
- package/dist/core/__tests__/repository-root-identity.test.js +219 -0
- package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +27 -11
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.d.ts +1 -0
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +40 -0
- package/dist/core/canvas/canvas.d.ts +3 -0
- package/dist/core/canvas/canvas.js +30 -10
- package/dist/core/canvas/db.js +28 -1
- package/dist/core/canvas/paths.d.ts +4 -6
- package/dist/core/canvas/paths.js +8 -6
- package/dist/core/canvas/render-source.js +2 -2
- package/dist/core/canvas/types.d.ts +9 -13
- package/dist/core/exclusive-lock.d.ts +2 -0
- package/dist/core/exclusive-lock.js +21 -0
- package/dist/core/git.d.ts +0 -1
- package/dist/core/git.js +0 -3
- package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +1 -0
- package/dist/core/human/feedback-companion.js +3 -0
- package/dist/core/human/scan.js +8 -1
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +2 -1
- package/dist/core/memory/doc-link-grammar.js +2 -2
- package/dist/core/memory/history.d.ts +24 -0
- package/dist/core/memory/history.js +66 -1
- package/dist/core/memory/identity.d.ts +65 -0
- package/dist/core/memory/identity.js +185 -0
- package/dist/core/memory/inline-ref-guidance.d.ts +1 -1
- package/dist/core/memory/inline-ref-guidance.js +1 -1
- package/dist/core/memory/inline-ref-inventory.d.ts +3 -10
- package/dist/core/memory/inline-ref-inventory.js +30 -62
- package/dist/core/memory/lint.d.ts +129 -0
- package/dist/core/memory/lint.js +517 -0
- package/dist/core/memory/project-namespace.d.ts +31 -0
- package/dist/core/memory/project-namespace.js +85 -0
- package/dist/core/memory/repository-association.d.ts +31 -0
- package/dist/core/memory/repository-association.js +129 -0
- package/dist/core/memory/tree.d.ts +39 -0
- package/dist/core/memory/tree.js +93 -0
- package/dist/core/memory-resolver.d.ts +193 -90
- package/dist/core/memory-resolver.js +461 -375
- package/dist/core/nested-stores.js +6 -13
- package/dist/core/profiles/select.d.ts +4 -1
- package/dist/core/profiles/select.js +108 -48
- package/dist/core/review/__tests__/capture-origin.test.js +2 -2
- package/dist/core/review/__tests__/stage-identity.test.js +2 -2
- package/dist/core/review/companion.js +11 -2
- package/dist/core/runtime/bearings.d.ts +2 -2
- package/dist/core/runtime/bearings.js +3 -3
- package/dist/core/runtime/broker/daemon-ops.d.ts +2 -2
- package/dist/core/runtime/broker/rebind.js +5 -0
- package/dist/core/runtime/broker-extension-render.d.ts +7 -3
- package/dist/core/runtime/broker-extension-render.js +10 -5
- package/dist/core/runtime/broker-persona-guidance.d.ts +19 -5
- package/dist/core/runtime/broker-persona-guidance.js +122 -27
- package/dist/core/runtime/deliver-live.d.ts +16 -4
- package/dist/core/runtime/deliver-live.js +29 -15
- package/dist/core/runtime/kickoff.d.ts +3 -3
- package/dist/core/runtime/kickoff.js +8 -16
- package/dist/core/runtime/lifecycle.js +2 -3
- package/dist/core/runtime/nodes.d.ts +3 -4
- package/dist/core/runtime/nodes.js +3 -4
- package/dist/core/runtime/persona.d.ts +8 -12
- package/dist/core/runtime/persona.js +24 -96
- package/dist/core/runtime/promote.d.ts +2 -2
- package/dist/core/runtime/promote.js +8 -21
- package/dist/core/runtime/revive.js +20 -17
- package/dist/core/runtime/spawn.js +18 -7
- package/dist/core/runtime/tmux-bindings.js +2 -3
- package/dist/core/runtime/warm-pool.js +3 -4
- package/dist/core/scope.js +2 -0
- package/dist/core/self-update.d.ts +0 -2
- package/dist/core/self-update.js +2 -35
- package/dist/core/substrate/__tests__/surface-match-memory-read.test.d.ts +1 -0
- package/dist/core/substrate/__tests__/surface-match-memory-read.test.js +28 -0
- package/dist/core/substrate/index.d.ts +2 -2
- package/dist/core/substrate/index.js +1 -1
- package/dist/core/substrate/injected-store.d.ts +43 -27
- package/dist/core/substrate/injected-store.js +208 -104
- package/dist/core/substrate/listings.d.ts +19 -12
- package/dist/core/substrate/listings.js +75 -52
- package/dist/core/substrate/on-read-node.d.ts +4 -7
- package/dist/core/substrate/on-read-node.js +6 -8
- package/dist/core/substrate/on-read.d.ts +22 -25
- package/dist/core/substrate/on-read.js +103 -147
- package/dist/core/substrate/render-node.d.ts +4 -7
- package/dist/core/substrate/render-node.js +5 -7
- package/dist/core/substrate/render.d.ts +21 -3
- package/dist/core/substrate/render.js +291 -223
- package/dist/core/substrate/schema.d.ts +1 -13
- package/dist/core/substrate/schema.js +5 -40
- package/dist/core/substrate/session-cache.d.ts +14 -4
- package/dist/core/substrate/session-cache.js +40 -22
- package/dist/core/substrate/surface-match.d.ts +9 -7
- package/dist/core/substrate/surface-match.js +26 -25
- package/dist/daemon/__tests__/helpers/source-daemon.d.ts +30 -0
- package/dist/daemon/__tests__/helpers/source-daemon.js +174 -0
- package/dist/daemon/__tests__/integration/migration-startup.test.d.ts +1 -0
- package/dist/daemon/__tests__/integration/migration-startup.test.js +97 -0
- package/dist/daemon/api/__tests__/bridge-heartbeat.test.js +21 -5
- package/dist/daemon/api/handlers/broker-ops.js +21 -16
- package/dist/daemon/api/handlers/memory.js +2 -0
- package/dist/daemon/crtrd.js +2 -0
- package/dist/daemon/human/finish.js +8 -1
- package/dist/daemon/manage.d.ts +0 -1
- package/dist/daemon/manage.js +7 -16
- package/dist/daemon/startup-policy.d.ts +1 -0
- package/dist/daemon/startup-policy.js +1 -0
- package/dist/migrations/001-surfaces-frontmatter.js +21 -109
- package/dist/migrations/002-profile-project-memory.js +1 -0
- package/dist/migrations/003-repository-root-memory-identity/front-door.d.ts +26 -0
- package/dist/migrations/003-repository-root-memory-identity/front-door.js +231 -0
- package/dist/migrations/003-repository-root-memory-identity/index.d.ts +2 -0
- package/dist/migrations/003-repository-root-memory-identity/index.js +513 -0
- package/dist/migrations/003-repository-root-memory-identity/references.d.ts +95 -0
- package/dist/migrations/003-repository-root-memory-identity/references.js +469 -0
- package/dist/migrations/003-repository-root-memory-identity/repository-facts.d.ts +39 -0
- package/dist/migrations/003-repository-root-memory-identity/repository-facts.js +349 -0
- package/dist/migrations/__tests__/activation-concurrency.test.d.ts +1 -0
- package/dist/migrations/__tests__/activation-concurrency.test.js +145 -0
- package/dist/migrations/__tests__/activation.test.d.ts +1 -0
- package/dist/migrations/__tests__/activation.test.js +149 -0
- package/dist/migrations/__tests__/deletion-and-root-declaration.test.d.ts +1 -0
- package/dist/migrations/__tests__/deletion-and-root-declaration.test.js +149 -0
- package/dist/migrations/activation.d.ts +16 -0
- package/dist/migrations/activation.js +78 -0
- package/dist/migrations/convergent.d.ts +14 -3
- package/dist/migrations/convergent.js +21 -10
- package/dist/migrations/corpus.d.ts +78 -0
- package/dist/migrations/corpus.js +497 -0
- package/dist/migrations/frontmatter-splice.d.ts +15 -0
- package/dist/migrations/frontmatter-splice.js +176 -0
- package/dist/migrations/registry.d.ts +6 -1
- package/dist/migrations/registry.js +7 -2
- package/dist/migrations/runner.d.ts +41 -0
- package/dist/migrations/runner.js +81 -0
- package/dist/migrations/types.d.ts +148 -9
- package/dist/migrations/types.js +9 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +225 -17
- package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +11 -3
- package/dist/pi-extensions/canvas-context-intro.d.ts +3 -5
- package/dist/pi-extensions/canvas-context-intro.js +50 -46
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +1 -8
- package/dist/pi-extensions/canvas-doc-substrate.js +55 -122
- package/dist/pi-extensions/canvas-stophook.js +6 -13
- package/dist/shared/generated-context.d.ts +0 -3
- package/dist/shared/generated-context.js +0 -57
- package/dist/shared/tool-groups.js +2 -3
- package/package.json +5 -4
- package/runtime.lock.json +2 -2
- /package/dist/api/__tests__/{serial → integration}/client.test.d.ts +0 -0
- /package/dist/api/__tests__/{serial → integration}/client.test.js +0 -0
- /package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/{serial → integration}/provider-rotation.test.ts +0 -0
- /package/dist/clients/{inbox/__tests__/serial/inbox-controller.test.d.ts → attach/__tests__/pane-tag-successor.test.d.ts} +0 -0
- /package/dist/clients/{inbox/__tests__/serial/mount-panel.test.d.ts → attach/__tests__/profile-files.test.d.ts} +0 -0
- /package/dist/{core/__tests__/live-mutation-verbs.test.d.ts → clients/inbox/__tests__/integration/inbox-controller.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{serial → integration}/inbox-controller.test.js +0 -0
- /package/dist/{core/__tests__/serial/broker-fork-seam.test.d.ts → clients/inbox/__tests__/integration/mount-panel.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{serial → integration}/mount-panel.test.js +0 -0
- /package/dist/{core/__tests__/serial/broker-sdk-wiring.test.d.ts → commands/__tests__/surface-reload-target.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/broker-snapshot-history.test.d.ts → commands/memory/__tests__/command-selector-and-mutation-guards.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/command-plugins.test.d.ts → commands/memory/__tests__/repository-root-lint.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/deferred-no-wake.test.d.ts → commands/sys/__tests__/sync-project-guidance.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/flagship-lifecycle.test.d.ts → config-change-delta.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/host-teardown-process-group.test.d.ts → integration/broker-fork-seam.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/human-deliver-e2e.test.d.ts → integration/broker-sdk-wiring.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/live-mutation.test.d.ts → integration/broker-snapshot-history.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/refresh-stall-recycle.test.d.ts → integration/command-plugins.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/revive.test.d.ts → integration/deferred-no-wake.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/spawn-root.test.d.ts → integration/flagship-lifecycle.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/subscription-delivery.test.d.ts → integration/host-teardown-process-group.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/tmux-surface.test.d.ts → integration/human-deliver-e2e.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/worktree.test.d.ts → integration/live-mutation-verbs.test.d.ts} +0 -0
- /package/dist/core/{human/__tests__/serial/inbox-core.test.d.ts → __tests__/integration/live-mutation.test.d.ts} +0 -0
- /package/dist/core/human/__tests__/{serial → integration}/inbox-core.test.js +0 -0
|
@@ -4,13 +4,15 @@ import { createHash } from 'node:crypto';
|
|
|
4
4
|
import { defineLeaf } from '../../core/command.js';
|
|
5
5
|
import { notFound, usage, general, network } from '../../core/errors.js';
|
|
6
6
|
import { findMarketplaceByName, findPluginByName, listAllPlugins } from '../../core/resolver.js';
|
|
7
|
-
import { pluginsDir, ensureProjectScopeRoot, userScopeRoot, resolveScopeArg, projectScopeRoot, requireScopeRoot, } from '../../core/scope.js';
|
|
7
|
+
import { NEUTRAL_PROJECT_MEMORY, pluginsDir, ensureProjectScopeRoot, userScopeRoot, resolveScopeArg, projectScopeRoot, requireScopeRoot, } from '../../core/scope.js';
|
|
8
8
|
import { updateConfig, updateState, ensureScopeInitialized, invalidPluginKindsReasons, invalidPluginBinReasons, invalidPluginRequiresReasons, normalizeRequires } from '../../core/config.js';
|
|
9
9
|
import { invalidPageComponentsReasons } from '../../core/human/page-catalog.js';
|
|
10
|
-
import { pathExists, ensureDir, removePath, nowIso, linkOrCopy, isSymlink, readSymlinkTarget, atomicWriteJson, readText, walkFiles } from '../../core/fs-utils.js';
|
|
10
|
+
import { pathExists, ensureDir, removePath, nowIso, linkOrCopy, isSymlink, readSymlinkTarget, atomicWriteJson, readText, realpathOrSelf, walkFiles } from '../../core/fs-utils.js';
|
|
11
11
|
import { clone, deriveNameFromUrl, currentSha, gitSync, isGitRepo } from '../../core/git.js';
|
|
12
12
|
import { readMarketplaceManifest, readPluginManifest, validatePluginManifestMemoryExtensions } from '../../core/manifest.js';
|
|
13
13
|
import { memoryExtensionValidationCatalog, validateMemoryExtensionValues } from '../../core/memory/extensions.js';
|
|
14
|
+
import { lintMemoryStore, MEMORY_LINT_IDENTITY_RULES } from '../../core/memory/lint.js';
|
|
15
|
+
import { buildCorpusSnapshot, loadCorpusSnapshots, parseFailureBlockers, runCorpusMigrations } from '../../migrations/corpus.js';
|
|
14
16
|
import { lintSubstrateFrontmatter } from '../../core/substrate/frontmatter-validation.js';
|
|
15
17
|
import { parseFrontmatterGeneric } from '../../core/frontmatter.js';
|
|
16
18
|
import { validatePluginCommands, validateEffectiveCommandPlugins } from '../../core/command-plugins/discovery.js';
|
|
@@ -21,7 +23,7 @@ import { compileHookRegistry, discoverCandidateHookRegistry, discoverCandidateHo
|
|
|
21
23
|
import { hasDeclaredHooks, hookReport } from '../../core/command-hooks/report.js';
|
|
22
24
|
import { fetchHttpPluginBundle } from '../../core/command-plugins/transport/http-fetch.js';
|
|
23
25
|
import { SUBTREE_NAMES, coreCommandPaths, coreHookCatalog } from '../../build-root.js';
|
|
24
|
-
import { isGitUrl, setPluginEnabled, resolveInstallScope } from './shared.js';
|
|
26
|
+
import { isGitUrl, resolvePluginConfigTarget, setPluginEnabled, resolveInstallScope } from './shared.js';
|
|
25
27
|
/** Validate a just-installed/updated plugin's command manifest, if it declares
|
|
26
28
|
* one. Returns undefined for plugins without a `commands` pointer. Never
|
|
27
29
|
* executes the plugin binary — validation is fully static. */
|
|
@@ -158,9 +160,71 @@ async function assertManifestHooksValid(manifest, root, scope, scopeRootPath, wh
|
|
|
158
160
|
if (issues.length > 0)
|
|
159
161
|
throw invalidBundleError(`plugin hook behavior at ${where} is invalid`, issues);
|
|
160
162
|
}
|
|
163
|
+
function pluginCandidateStore(name, scope, storeRoot) {
|
|
164
|
+
return { scope, storeRoot, namespace: name, plugin: name, mountStatus: 'ready', projectMemory: NEUTRAL_PROJECT_MEMORY };
|
|
165
|
+
}
|
|
166
|
+
function storeRelative(storeRoot, path) {
|
|
167
|
+
const rel = relative(storeRoot, path);
|
|
168
|
+
return rel === '' ? storeRoot : rel;
|
|
169
|
+
}
|
|
170
|
+
function corpusBlockedError(name, where, storeRoot, blockers, wrote) {
|
|
171
|
+
const lines = blockers.map((b) => `- ${b.kind}${b.path === undefined ? '' : ` at ${storeRelative(storeRoot, b.path)}`}: ${b.message}`);
|
|
172
|
+
return general(`plugin "${name}" memory corpus at ${where} cannot be migrated to exact canonical identity:\n${lines.join('\n')}`, {
|
|
173
|
+
blockers,
|
|
174
|
+
next: wrote === 0
|
|
175
|
+
? `Correct the package's memory corpus, then retry — nothing was written and "${name}" was not activated.`
|
|
176
|
+
: `Correct the package's memory corpus, then re-run — the rewrite is on disk but failed verification, and "${name}" was not activated.`,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/** Converge a candidate package's memory corpus onto exact canonical identity
|
|
180
|
+
* through the same corpus planner `crtr sys migrate` runs, then refuse the
|
|
181
|
+
* candidate on any identity finding that would leave it unmountable. The
|
|
182
|
+
* package is one store no target mounts, so its links and routes resolve
|
|
183
|
+
* against itself; a blocked plan writes nothing at all. */
|
|
184
|
+
function migratePluginMemory(manifest, root, scope, where, ownership) {
|
|
185
|
+
const storeRoot = join(root, 'memory');
|
|
186
|
+
if (!pathExists(storeRoot))
|
|
187
|
+
return undefined;
|
|
188
|
+
const discovery = {
|
|
189
|
+
roots: [{ reason: 'plugin', root: storeRoot, boundary: 'store', detail: manifest.name }],
|
|
190
|
+
stores: [{
|
|
191
|
+
key: realpathOrSelf(storeRoot),
|
|
192
|
+
descriptor: pluginCandidateStore(manifest.name, scope, storeRoot),
|
|
193
|
+
discovery: [{ reason: 'plugin', root: storeRoot }],
|
|
194
|
+
targets: [],
|
|
195
|
+
writable: true,
|
|
196
|
+
}],
|
|
197
|
+
targets: [],
|
|
198
|
+
blockers: [],
|
|
199
|
+
nodeMetadata: { available: true, nodes: 0 },
|
|
200
|
+
};
|
|
201
|
+
const load = () => {
|
|
202
|
+
const { snapshots, skipped } = loadCorpusSnapshots(discovery);
|
|
203
|
+
return { corpus: buildCorpusSnapshot(discovery, snapshots), blockers: parseFailureBlockers(skipped) };
|
|
204
|
+
};
|
|
205
|
+
const initial = load();
|
|
206
|
+
const run = runCorpusMigrations(initial.corpus, { blockers: initial.blockers, rebuild: () => load().corpus });
|
|
207
|
+
const wrote = run.wrote.consumers.length + run.wrote.identities.length;
|
|
208
|
+
if (run.blocked)
|
|
209
|
+
throw corpusBlockedError(manifest.name, where, storeRoot, run.blockers, wrote);
|
|
210
|
+
// Reference checks are deliberately skipped (no `resolvable`): a package's
|
|
211
|
+
// links reach documents that only exist in the corpus it will be mounted
|
|
212
|
+
// into, which this isolated store cannot see.
|
|
213
|
+
const lint = lintMemoryStore(pluginCandidateStore(manifest.name, scope, storeRoot));
|
|
214
|
+
const identityErrors = lint.errors.filter((finding) => MEMORY_LINT_IDENTITY_RULES.has(finding.rule));
|
|
215
|
+
if (identityErrors.length > 0) {
|
|
216
|
+
const lines = identityErrors.map((f) => `- ${f.rule} at ${storeRelative(storeRoot, f.path)}: ${f.message}`);
|
|
217
|
+
throw general(`plugin "${manifest.name}" memory corpus at ${where} is incompatible with exact canonical identity:\n${lines.join('\n')}`, { issues: identityErrors, next: `Correct the package's memory documents, then retry — "${manifest.name}" was not activated.` });
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
docs: lint.documents,
|
|
221
|
+
migrated: [...new Set([...run.wrote.consumers, ...run.wrote.identities])].map((path) => storeRelative(storeRoot, path)).sort(),
|
|
222
|
+
...(ownership === 'linked' ? { linkedSource: realpathOrSelf(storeRoot) } : {}),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
161
225
|
/** Validate a package candidate before it gains an active plugin path. The
|
|
162
226
|
* candidate namespace shadows any installed declaration of the same name. */
|
|
163
|
-
export async function assertPluginCandidateValid(manifest, root, scope, scopeRootPath, where, candidates) {
|
|
227
|
+
export async function assertPluginCandidateValid(manifest, root, scope, scopeRootPath, where, candidates, ownership = 'package') {
|
|
164
228
|
try {
|
|
165
229
|
validatePluginManifestMemoryExtensions(manifest);
|
|
166
230
|
}
|
|
@@ -195,6 +259,7 @@ export async function assertPluginCandidateValid(manifest, root, scope, scopeRoo
|
|
|
195
259
|
throw general(`plugin memory document ${relative(root, file)} is invalid: ${issue.path}: ${issue.message}`);
|
|
196
260
|
}
|
|
197
261
|
}
|
|
262
|
+
return migratePluginMemory(manifest, root, scope, where, ownership);
|
|
198
263
|
}
|
|
199
264
|
function invocationTransport(bundle) {
|
|
200
265
|
return { kind: 'http', endpoint: new URL(bundle.endpoint).origin, ...(bundle.authEnv !== undefined ? { authEnv: bundle.authEnv } : {}) };
|
|
@@ -218,12 +283,13 @@ function writeStagedBundle(stagingRoot, manifest, bundle) {
|
|
|
218
283
|
}
|
|
219
284
|
}
|
|
220
285
|
async function validateStagedBundle(name, scope, root, scopeRootPath, manifest) {
|
|
221
|
-
await assertPluginCandidateValid(manifest, root, scope, scopeRootPath, `${root}/.crouter-plugin/plugin.json`);
|
|
286
|
+
const memory = await assertPluginCandidateValid(manifest, root, scope, scopeRootPath, `${root}/.crouter-plugin/plugin.json`);
|
|
222
287
|
const staged = { name, scope, root, manifest, enabled: true };
|
|
223
288
|
const validation = validatePluginCommands(staged, new Set(SUBTREE_NAMES), await coreCommandPaths());
|
|
224
289
|
if (validation.issues.length > 0) {
|
|
225
290
|
throw invalidBundleError(`bundle staged package for "${name}" is invalid`, validation.issues);
|
|
226
291
|
}
|
|
292
|
+
return memory;
|
|
227
293
|
}
|
|
228
294
|
function isSourceInstalled(plugin) {
|
|
229
295
|
return plugin.sourceMarketplace !== undefined || isGitRepo(plugin.root) || isSymlink(plugin.root);
|
|
@@ -287,10 +353,11 @@ async function replaceBundlePlugin(name, bundleSource, scope, options) {
|
|
|
287
353
|
ensureDir(tmpRoot);
|
|
288
354
|
ensureDir(join(scopeRootPath, 'plugins'));
|
|
289
355
|
removePath(staging);
|
|
356
|
+
let memory;
|
|
290
357
|
try {
|
|
291
358
|
ensureDir(staging);
|
|
292
359
|
writeStagedBundle(staging, manifest, validated.bundle);
|
|
293
|
-
await validateStagedBundle(name, scope, staging, scopeRootPath, manifest);
|
|
360
|
+
memory = await validateStagedBundle(name, scope, staging, scopeRootPath, manifest);
|
|
294
361
|
let movedPrevious = false;
|
|
295
362
|
if (pathExists(root)) {
|
|
296
363
|
renameSync(root, previous);
|
|
@@ -322,7 +389,7 @@ async function replaceBundlePlugin(name, bundleSource, scope, options) {
|
|
|
322
389
|
const commands = await commandReport(name, scope);
|
|
323
390
|
if (commands === undefined)
|
|
324
391
|
throw general(`bundle plugin "${name}" has no staged command report`);
|
|
325
|
-
return { name, scope, path: root, transport: 'http', version, docs: validated.bundle.memory.length, kinds: Object.keys(validated.bundle.kinds ?? {}).length, pageComponents: (validated.bundle.page_components ?? []).length, commands };
|
|
392
|
+
return { name, scope, path: root, transport: 'http', version, docs: validated.bundle.memory.length, kinds: Object.keys(validated.bundle.kinds ?? {}).length, pageComponents: (validated.bundle.page_components ?? []).length, commands, ...(memory !== undefined ? { memory } : {}) };
|
|
326
393
|
}
|
|
327
394
|
async function installHttpPlugin(name, endpoint, authEnv, scope) {
|
|
328
395
|
const source = validateHttpInstall(name, endpoint, authEnv);
|
|
@@ -451,7 +518,7 @@ async function installFromGit(source, ref, scope) {
|
|
|
451
518
|
const manifest = readPluginManifest(staging);
|
|
452
519
|
if (manifest === null)
|
|
453
520
|
throw general(`cloned repo does not contain a valid .crouter-plugin/plugin.json: ${source}`);
|
|
454
|
-
await assertPluginCandidateValid(manifest, staging, scope, scopeRootPath, `${source}/.crouter-plugin/plugin.json`);
|
|
521
|
+
const memory = await assertPluginCandidateValid(manifest, staging, scope, scopeRootPath, `${source}/.crouter-plugin/plugin.json`);
|
|
455
522
|
const finalDir = join(pDir, manifest.name);
|
|
456
523
|
if (pathExists(finalDir))
|
|
457
524
|
throw general(`plugin "${manifest.name}" is already installed at ${finalDir}`);
|
|
@@ -460,7 +527,7 @@ async function installFromGit(source, ref, scope) {
|
|
|
460
527
|
updateConfig(scope, (cfg) => {
|
|
461
528
|
cfg.plugins[manifest.name] = { enabled: true, version: manifest.version };
|
|
462
529
|
});
|
|
463
|
-
return { name: manifest.name, scope, path: finalDir };
|
|
530
|
+
return { name: manifest.name, scope, path: finalDir, ...(memory !== undefined ? { memory } : {}) };
|
|
464
531
|
}
|
|
465
532
|
finally {
|
|
466
533
|
removePath(staging);
|
|
@@ -473,7 +540,9 @@ async function installFromLocal(source, scope) {
|
|
|
473
540
|
throw notFound(`plugin manifest not found at ${sourcePath}/.crouter-plugin/plugin.json`);
|
|
474
541
|
}
|
|
475
542
|
const scopeRootPath = scope === 'project' ? ensureProjectScopeRoot() : userScopeRoot();
|
|
476
|
-
|
|
543
|
+
// The install keeps `sourcePath` as a live link, so the caller's own
|
|
544
|
+
// directory is the installed physical corpus and is converged in place.
|
|
545
|
+
const memory = await assertPluginCandidateValid(manifest, sourcePath, scope, scopeRootPath, `${sourcePath}/.crouter-plugin/plugin.json`, undefined, 'linked');
|
|
477
546
|
ensureScopeInitialized(scope, scopeRootPath);
|
|
478
547
|
const destDir = join(scopeRootPath, 'plugins', manifest.name);
|
|
479
548
|
if (pathExists(destDir)) {
|
|
@@ -486,7 +555,7 @@ async function installFromLocal(source, scope) {
|
|
|
486
555
|
version: manifest.version,
|
|
487
556
|
};
|
|
488
557
|
});
|
|
489
|
-
return { name: manifest.name, scope, path: destDir };
|
|
558
|
+
return { name: manifest.name, scope, path: destDir, ...(memory !== undefined ? { memory } : {}) };
|
|
490
559
|
}
|
|
491
560
|
/** Install one marketplace plugin into a scope. Exported so `sys setup` can
|
|
492
561
|
* install the default plugin set through the same path `pkg plugin install`
|
|
@@ -505,6 +574,7 @@ export async function installFromMarketplace(ref, scope) {
|
|
|
505
574
|
if (pathExists(destPluginDir))
|
|
506
575
|
throw general(`plugin "${ref.plugin}" is already installed at ${destPluginDir}`);
|
|
507
576
|
let pluginManifest;
|
|
577
|
+
let memory;
|
|
508
578
|
if (isMarketplaceRelativeSource(entry.source)) {
|
|
509
579
|
const sourcePath = join(refreshed.marketplace.root, entry.source);
|
|
510
580
|
if (!pathExists(sourcePath))
|
|
@@ -512,7 +582,9 @@ export async function installFromMarketplace(ref, scope) {
|
|
|
512
582
|
const candidateManifest = readPluginManifest(sourcePath);
|
|
513
583
|
if (candidateManifest === null)
|
|
514
584
|
throw notFound(`plugin manifest not found at ${sourcePath}/.crouter-plugin/plugin.json`);
|
|
515
|
-
|
|
585
|
+
// Linked to the marketplace checkout: that directory IS the installed
|
|
586
|
+
// corpus, so it is converged where it lies.
|
|
587
|
+
memory = await assertPluginCandidateValid(candidateManifest, sourcePath, scope, destRoot, `${sourcePath}/.crouter-plugin/plugin.json`, undefined, 'linked');
|
|
516
588
|
linkOrCopy(sourcePath, destPluginDir);
|
|
517
589
|
pluginManifest = candidateManifest;
|
|
518
590
|
}
|
|
@@ -524,7 +596,7 @@ export async function installFromMarketplace(ref, scope) {
|
|
|
524
596
|
const candidateManifest = readPluginManifest(staging);
|
|
525
597
|
if (candidateManifest === null)
|
|
526
598
|
throw notFound(`plugin manifest not found at ${staging}/.crouter-plugin/plugin.json`);
|
|
527
|
-
await assertPluginCandidateValid(candidateManifest, staging, scope, destRoot, `${staging}/.crouter-plugin/plugin.json`);
|
|
599
|
+
memory = await assertPluginCandidateValid(candidateManifest, staging, scope, destRoot, `${staging}/.crouter-plugin/plugin.json`);
|
|
528
600
|
ensureDir(join(destRoot, 'plugins'));
|
|
529
601
|
renameSync(staging, destPluginDir);
|
|
530
602
|
pluginManifest = candidateManifest;
|
|
@@ -543,7 +615,7 @@ export async function installFromMarketplace(ref, scope) {
|
|
|
543
615
|
pluginCfg.version = version;
|
|
544
616
|
cfg.plugins[ref.plugin] = pluginCfg;
|
|
545
617
|
});
|
|
546
|
-
return { name: ref.plugin, scope, path: destPluginDir };
|
|
618
|
+
return { name: ref.plugin, scope, path: destPluginDir, ...(memory !== undefined ? { memory } : {}) };
|
|
547
619
|
}
|
|
548
620
|
async function updateGitPluginCandidate(plugin) {
|
|
549
621
|
if (isSymlink(plugin.root)) {
|
|
@@ -551,16 +623,16 @@ async function updateGitPluginCandidate(plugin) {
|
|
|
551
623
|
if (manifest === null)
|
|
552
624
|
throw notFound(`plugin manifest not found at ${plugin.root}/.crouter-plugin/plugin.json`);
|
|
553
625
|
const scopeRootPath = plugin.scope === 'project' ? requireScopeRoot('project') : userScopeRoot();
|
|
554
|
-
await assertPluginCandidateValid(manifest, plugin.root, plugin.scope, scopeRootPath, `${plugin.root}/.crouter-plugin/plugin.json
|
|
555
|
-
return { updated: false, sha: currentSha(plugin.root) ?? '', manifest };
|
|
626
|
+
const memory = await assertPluginCandidateValid(manifest, plugin.root, plugin.scope, scopeRootPath, `${plugin.root}/.crouter-plugin/plugin.json`, undefined, 'linked');
|
|
627
|
+
return { updated: false, sha: currentSha(plugin.root) ?? '', manifest, ...(memory !== undefined ? { memory } : {}) };
|
|
556
628
|
}
|
|
557
629
|
if (!isGitRepo(plugin.root)) {
|
|
558
630
|
const manifest = readPluginManifest(plugin.root);
|
|
559
631
|
if (manifest === null)
|
|
560
632
|
throw notFound(`plugin manifest not found at ${plugin.root}/.crouter-plugin/plugin.json`);
|
|
561
633
|
const scopeRootPath = plugin.scope === 'project' ? requireScopeRoot('project') : userScopeRoot();
|
|
562
|
-
await assertPluginCandidateValid(manifest, plugin.root, plugin.scope, scopeRootPath, `${plugin.root}/.crouter-plugin/plugin.json`);
|
|
563
|
-
return { updated: false, sha: '', manifest };
|
|
634
|
+
const memory = await assertPluginCandidateValid(manifest, plugin.root, plugin.scope, scopeRootPath, `${plugin.root}/.crouter-plugin/plugin.json`);
|
|
635
|
+
return { updated: false, sha: '', manifest, ...(memory !== undefined ? { memory } : {}) };
|
|
564
636
|
}
|
|
565
637
|
const remote = gitSync(['config', '--get', 'remote.origin.url'], plugin.root).stdout.trim();
|
|
566
638
|
if (remote === '')
|
|
@@ -578,7 +650,7 @@ async function updateGitPluginCandidate(plugin) {
|
|
|
578
650
|
const manifest = readPluginManifest(staging);
|
|
579
651
|
if (manifest === null)
|
|
580
652
|
throw notFound(`plugin manifest not found in update candidate: ${staging}/.crouter-plugin/plugin.json`);
|
|
581
|
-
await assertPluginCandidateValid(manifest, staging, plugin.scope, scopeRootPath, `${staging}/.crouter-plugin/plugin.json`);
|
|
653
|
+
const memory = await assertPluginCandidateValid(manifest, staging, plugin.scope, scopeRootPath, `${staging}/.crouter-plugin/plugin.json`);
|
|
582
654
|
renameSync(plugin.root, previous);
|
|
583
655
|
try {
|
|
584
656
|
renameSync(staging, plugin.root);
|
|
@@ -589,7 +661,7 @@ async function updateGitPluginCandidate(plugin) {
|
|
|
589
661
|
}
|
|
590
662
|
removePath(previous);
|
|
591
663
|
const sha = currentSha(plugin.root) ?? '';
|
|
592
|
-
return { updated: before !== sha, sha, manifest };
|
|
664
|
+
return { updated: before !== sha, sha, manifest, ...(memory !== undefined ? { memory } : {}) };
|
|
593
665
|
}
|
|
594
666
|
finally {
|
|
595
667
|
removePath(staging);
|
|
@@ -601,7 +673,7 @@ async function updateOnePlugin(plugin, marketplaceCache, opts) {
|
|
|
601
673
|
const sourceInstalled = isSourceInstalled(plugin);
|
|
602
674
|
if (!sourceInstalled && plugin.manifest.bundle !== undefined) {
|
|
603
675
|
const replaced = await replaceBundlePlugin(plugin.name, plugin.manifest.bundle, plugin.scope, { enable: plugin.enabled });
|
|
604
|
-
return { name: plugin.name, transport: 'http', updated: true, version: replaced.version, docs: replaced.docs, kinds: replaced.kinds, pageComponents: replaced.pageComponents, commands: replaced.commands };
|
|
676
|
+
return { name: plugin.name, transport: 'http', updated: true, version: replaced.version, docs: replaced.docs, kinds: replaced.kinds, pageComponents: replaced.pageComponents, commands: replaced.commands, ...(replaced.memory !== undefined ? { memory: replaced.memory } : {}) };
|
|
605
677
|
}
|
|
606
678
|
if (plugin.manifest.transport?.kind === 'http' && !sourceInstalled)
|
|
607
679
|
rejectLegacyHttpPlugin(plugin.name);
|
|
@@ -627,6 +699,7 @@ async function updateOnePlugin(plugin, marketplaceCache, opts) {
|
|
|
627
699
|
sha: candidate.sha,
|
|
628
700
|
...(commands !== undefined ? { commands } : {}),
|
|
629
701
|
...(hooks !== undefined ? { hooks } : {}),
|
|
702
|
+
...(candidate.memory !== undefined ? { memory: candidate.memory } : {}),
|
|
630
703
|
...(warnings.length > 0 ? { warnings } : {}),
|
|
631
704
|
};
|
|
632
705
|
}
|
|
@@ -657,6 +730,7 @@ export const pluginInstall = defineLeaf({
|
|
|
657
730
|
{ name: 'commands', type: 'object', required: false, constraint: 'Present only when the plugin declares a command manifest. {mounts: string[] (accepted top-level command names now live for the next invocation), issues: object[] (typed validation issues that rejected a contribution — {code, path?, message, received, expected, next})}. Validated statically; commands are never executed.' },
|
|
658
731
|
{ name: 'hooks', type: 'object', required: false, constraint: 'Present only when the source plugin declares hooks. {manifestPath?, executablePath?, declarations: {target, phase, op, description, effects}[], issues: object[], trust: string}. Declarations, eligible targets, and replacement collisions are validated statically; hook executables are never run.' },
|
|
659
732
|
{ name: 'warnings', type: 'string[]', required: false, constraint: 'Advisory missing PATH executable requirements declared by the installed plugin. The install still succeeds and leaves the plugin enabled.' },
|
|
733
|
+
{ name: 'memory', type: 'object', required: false, constraint: 'Present only when the plugin ships a memory store. {docs: integer (documents checked), migrated: string[] (store-relative documents rewritten to reach exact canonical identity), linkedSource?: string (the caller-owned directory converged in place, present only when the install keeps that directory as a live link)}.' },
|
|
660
734
|
],
|
|
661
735
|
outputKind: 'object',
|
|
662
736
|
effects: [
|
|
@@ -665,6 +739,7 @@ export const pluginInstall = defineLeaf({
|
|
|
665
739
|
'If the plugin declares a command manifest, its commands go live on the next crtr invocation. Exec commands run trusted local code only when explicitly invoked; HTTP commands call their declared endpoint only when explicitly invoked.',
|
|
666
740
|
'Source plugin hooks go live on the next crtr invocation. They run implicitly with caller authority, may receive normalized inputs and after results, may block or replace declared targets, and may transmit received data. Hook executables are never run during install or update validation.',
|
|
667
741
|
'A plugin manifest `requires` declaration is checked against the PATH a node bash receives after install. Missing executables emit a warning with the declared install hint but never fail the install, disable the plugin, or remove its bare-binary contributions.',
|
|
742
|
+
'Before activation the candidate\u2019s memory corpus is migrated to exact canonical identity and refused if it cannot mount. A staged candidate is migrated in staging and rolls back with the package; a local path or marketplace-relative source stays a live link, so its own directory is rewritten in place, keeps that rewrite whether or not the install completes, and is named as `memory.linkedSource`. A refused corpus writes nothing and installs nothing.',
|
|
668
743
|
],
|
|
669
744
|
},
|
|
670
745
|
run: async (input) => {
|
|
@@ -775,11 +850,26 @@ export const pluginEnable = defineLeaf({
|
|
|
775
850
|
{ name: 'name', type: 'string', required: true, constraint: 'Plugin name.' },
|
|
776
851
|
{ name: 'scope', type: 'string', required: true, constraint: 'Scope the change was applied in.' },
|
|
777
852
|
{ name: 'enabled', type: 'boolean', required: true, constraint: 'Always true.' },
|
|
853
|
+
{ name: 'memory', type: 'object', required: false, constraint: 'Present only when the plugin ships a memory store. {docs: integer (documents checked), migrated: string[] (store-relative documents rewritten to reach exact canonical identity), linkedSource?: string (the caller-owned directory converged in place, present only for a symlinked install)}.' },
|
|
778
854
|
],
|
|
779
855
|
outputKind: 'object',
|
|
780
|
-
effects: [
|
|
856
|
+
effects: [
|
|
857
|
+
'Validates the installed package before any config change: its memory corpus is migrated to exact canonical identity and refused if it cannot mount. A refusal leaves the plugin disabled.',
|
|
858
|
+
'Sets plugin enabled=true in config.json.',
|
|
859
|
+
],
|
|
860
|
+
},
|
|
861
|
+
run: async (input) => {
|
|
862
|
+
const name = input['name'];
|
|
863
|
+
const target = resolvePluginConfigTarget(name, input['scope']);
|
|
864
|
+
if (target === null)
|
|
865
|
+
throw notFound(`plugin not found: ${name}`);
|
|
866
|
+
const manifest = readPluginManifest(target.root);
|
|
867
|
+
if (manifest === null)
|
|
868
|
+
throw notFound(`plugin manifest not found at ${target.root}/.crouter-plugin/plugin.json`);
|
|
869
|
+
const scopeRootPath = target.scope === 'project' ? requireScopeRoot('project') : userScopeRoot();
|
|
870
|
+
const memory = await assertPluginCandidateValid(manifest, target.root, target.scope, scopeRootPath, `${target.root}/.crouter-plugin/plugin.json`, undefined, isSymlink(target.root) ? 'linked' : 'package');
|
|
871
|
+
return { ...(await setPluginEnabled(input, true)), ...(memory !== undefined ? { memory } : {}) };
|
|
781
872
|
},
|
|
782
|
-
run: async (input) => setPluginEnabled(input, true),
|
|
783
873
|
});
|
|
784
874
|
export const pluginDisable = defineLeaf({
|
|
785
875
|
name: 'disable',
|
|
@@ -814,10 +904,13 @@ export const pluginUpdate = defineLeaf({
|
|
|
814
904
|
{ kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'Narrows resolution.' },
|
|
815
905
|
],
|
|
816
906
|
output: [
|
|
817
|
-
{ name: 'updated', type: 'object[]', required: true, constraint: 'One entry per plugin processed: {name, transport?, updated, sha?, version?, docs?, kinds?, pageComponents?, commands?, hooks?, warnings?, error?}. Archive plugins report transport=http and replace their complete directory after an authenticated refetch; version is the archive content identifier, docs is the written memory-document count, kinds is the declared kind-registry entry count, and pageComponents is the declared page-component registration count. sha is present for git updates. A bulk update reports and skips a failed archive plugin. commands is present for validated command plugins: {mounts: string[], issues: object[]}. hooks is present for source hook plugins: {manifestPath?, executablePath?, declarations: {target, phase, op, description, effects}[], issues: object[], trust: string}; it is static and never executes hooks. warnings lists advisory PATH requirements that remain absent after update.' },
|
|
907
|
+
{ name: 'updated', type: 'object[]', required: true, constraint: 'One entry per plugin processed: {name, transport?, updated, sha?, version?, docs?, kinds?, pageComponents?, commands?, hooks?, memory?, warnings?, error?}. memory is present for a plugin shipping a memory store and reports the same corpus migration install performs: {docs, migrated, linkedSource?}. Archive plugins report transport=http and replace their complete directory after an authenticated refetch; version is the archive content identifier, docs is the written memory-document count, kinds is the declared kind-registry entry count, and pageComponents is the declared page-component registration count. sha is present for git updates. A bulk update reports and skips a failed archive plugin. commands is present for validated command plugins: {mounts: string[], issues: object[]}. hooks is present for source hook plugins: {manifestPath?, executablePath?, declarations: {target, phase, op, description, effects}[], issues: object[], trust: string}; it is static and never executes hooks. warnings lists advisory PATH requirements that remain absent after update.' },
|
|
818
908
|
],
|
|
819
909
|
outputKind: 'object',
|
|
820
|
-
effects: [
|
|
910
|
+
effects: [
|
|
911
|
+
'Archive plugins unconditionally re-fetch, validate, and replace their complete package; a named failure exits nonzero and preserves the prior package. Bulk update reports a failed archive plugin and continues. Source-installed plugins retain their source update, version, and last_updated behavior.',
|
|
912
|
+
'Each candidate passes the same pre-activation memory-corpus migration install performs, with the same linked-source rewrite semantics for a symlinked install.',
|
|
913
|
+
],
|
|
821
914
|
},
|
|
822
915
|
run: async (input) => {
|
|
823
916
|
const name = input['name'];
|
|
@@ -2,4 +2,12 @@ import type { Scope } from '../../types.js';
|
|
|
2
2
|
export declare const GIT_URL_RE: RegExp;
|
|
3
3
|
export declare function isGitUrl(arg: string): boolean;
|
|
4
4
|
export declare function resolveInstallScope(scopeInput: string | undefined): Scope;
|
|
5
|
+
/** The one installed copy an enable/disable acts on: the first scope in
|
|
6
|
+
* precedence whose plugins dir holds `name`, disabled copies included. Enable
|
|
7
|
+
* validates that exact copy before flipping its config, so both share this
|
|
8
|
+
* resolution rather than each walking the scopes. */
|
|
9
|
+
export declare function resolvePluginConfigTarget(name: string, scopeInput: string | undefined): {
|
|
10
|
+
scope: Scope;
|
|
11
|
+
root: string;
|
|
12
|
+
} | null;
|
|
5
13
|
export declare function setPluginEnabled(input: Record<string, unknown>, enabled: boolean): Promise<Record<string, unknown>>;
|
|
@@ -21,9 +21,11 @@ export function resolveInstallScope(scopeInput) {
|
|
|
21
21
|
// Default: project if available, else user
|
|
22
22
|
return projectScopeRoot() !== null ? 'project' : 'user';
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
/** The one installed copy an enable/disable acts on: the first scope in
|
|
25
|
+
* precedence whose plugins dir holds `name`, disabled copies included. Enable
|
|
26
|
+
* validates that exact copy before flipping its config, so both share this
|
|
27
|
+
* resolution rather than each walking the scopes. */
|
|
28
|
+
export function resolvePluginConfigTarget(name, scopeInput) {
|
|
27
29
|
let scopes;
|
|
28
30
|
if (scopeInput !== undefined) {
|
|
29
31
|
const resolved = resolveScopeArg(scopeInput);
|
|
@@ -34,28 +36,29 @@ export async function setPluginEnabled(input, enabled) {
|
|
|
34
36
|
else {
|
|
35
37
|
scopes = ['project', 'user'].filter((s) => s !== 'project' || projectScopeRoot() !== null);
|
|
36
38
|
}
|
|
37
|
-
let actedScope;
|
|
38
39
|
for (const scope of scopes) {
|
|
39
40
|
const pDir = pluginsDir(scope);
|
|
40
41
|
if (pDir === null)
|
|
41
42
|
continue;
|
|
42
43
|
const pluginDir = join(pDir, name);
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
updateConfig(scope, (cfg) => {
|
|
46
|
-
const entry = cfg.plugins[name];
|
|
47
|
-
if (entry !== undefined) {
|
|
48
|
-
entry.enabled = enabled;
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
cfg.plugins[name] = { enabled };
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
actedScope = scope;
|
|
55
|
-
break; // only act on first found scope
|
|
44
|
+
if (pathExists(pluginDir))
|
|
45
|
+
return { scope, root: pluginDir };
|
|
56
46
|
}
|
|
57
|
-
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
export async function setPluginEnabled(input, enabled) {
|
|
50
|
+
const name = input['name'];
|
|
51
|
+
const target = resolvePluginConfigTarget(name, input['scope']);
|
|
52
|
+
if (target === null)
|
|
58
53
|
throw notFound(`plugin not found: ${name}`);
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
updateConfig(target.scope, (cfg) => {
|
|
55
|
+
const entry = cfg.plugins[name];
|
|
56
|
+
if (entry !== undefined) {
|
|
57
|
+
entry.enabled = enabled;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
cfg.plugins[name] = { enabled };
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
return { name, scope: target.scope, enabled };
|
|
61
64
|
}
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import type { LeafDef } from '../core/command.js';
|
|
2
|
+
/** The node a `crtr surface attach to <id>` command line targets. Panes opened
|
|
3
|
+
* by a respawn path (the Markdown file-open bridge, the review companion) carry
|
|
4
|
+
* a shell-quoted id, so the raw command line is not comparable to the canonical
|
|
5
|
+
* one — the target node is. */
|
|
6
|
+
export declare function attachTargetNode(command: string): string | undefined;
|
|
2
7
|
export declare const surfaceReloadLeaf: LeafDef;
|
|
@@ -48,6 +48,14 @@ function paneTreeHasViewer(rootPid, nodeId) {
|
|
|
48
48
|
}
|
|
49
49
|
return false;
|
|
50
50
|
}
|
|
51
|
+
/** The node a `crtr surface attach to <id>` command line targets. Panes opened
|
|
52
|
+
* by a respawn path (the Markdown file-open bridge, the review companion) carry
|
|
53
|
+
* a shell-quoted id, so the raw command line is not comparable to the canonical
|
|
54
|
+
* one — the target node is. */
|
|
55
|
+
export function attachTargetNode(command) {
|
|
56
|
+
const match = /(?:^|\s)surface attach to\s+(?:'([^']*)'|"([^"]*)"|(\S+))/.exec(command);
|
|
57
|
+
return match?.[1] ?? match?.[2] ?? match?.[3];
|
|
58
|
+
}
|
|
51
59
|
export const surfaceReloadLeaf = defineLeaf({
|
|
52
60
|
name: 'reload',
|
|
53
61
|
description: 'respawn a viewer pane in place onto the current runtime generation',
|
|
@@ -94,7 +102,7 @@ export const surfaceReloadLeaf = defineLeaf({
|
|
|
94
102
|
if (startCommand !== undefined && startCommand.startsWith('crtr surface attach ')) {
|
|
95
103
|
// The start command must target the TAGGED node — a mismatch means the
|
|
96
104
|
// tag is stale or the viewer belongs to another node; don't touch it.
|
|
97
|
-
if (startCommand !==
|
|
105
|
+
if (attachTargetNode(startCommand) !== nodeId) {
|
|
98
106
|
displayMessage('crtr: viewer/node mismatch in pane', pane);
|
|
99
107
|
throw new InputError({ error: 'no_viewer', message: `this pane's viewer command (${startCommand}) does not match its node tag (${nodeId})`, next: 'Pass --pane <pane-id> for a pane running `crtr surface attach to <node>`.' });
|
|
100
108
|
}
|