akm-cli 0.9.0-rc.0 → 0.9.0-rc.13
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 +1283 -22
- package/README.md +62 -37
- package/SECURITY.md +46 -31
- package/dist/akm +162 -38
- package/dist/akm-migrate +44 -0
- package/dist/assets/backends/schtasks-template.xml +2 -1
- package/dist/assets/hints/cli-hints-full.md +268 -118
- package/dist/assets/hints/cli-hints-short.md +87 -24
- package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
- package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
- package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
- package/dist/assets/improve-strategies/frequent.json +15 -0
- package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
- package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
- package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
- package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
- package/dist/assets/improve-strategies/reflect-distill.json +30 -0
- package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +5 -5
- package/dist/assets/prompts/extract-session.md +2 -6
- package/dist/assets/prompts/memory-infer-user.md +2 -3
- package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
- package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
- package/dist/assets/prompts/reflect-output-repair.md +3 -0
- package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
- package/dist/assets/stash-skeleton/README.md +38 -10
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
- package/dist/assets/tasks/core/extract.yml +3 -2
- package/dist/assets/tasks/core/improve.yml +2 -1
- package/dist/assets/tasks/core/index-refresh.yml +1 -0
- package/dist/assets/tasks/core/sync.yml +1 -0
- package/dist/assets/tasks/core/version-check.yml +2 -1
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
- package/dist/assets/templates/html/health.html +5 -4
- package/dist/assets/workflows/workflow-template.md +31 -15
- package/dist/cli/invocation.js +279 -0
- package/dist/cli/parse-args.js +5 -90
- package/dist/cli/retired-commands.js +78 -0
- package/dist/cli/shared.js +158 -48
- package/dist/cli-node.mjs +2 -1
- package/dist/cli.js +747 -293
- package/dist/commands/agent/agent-dispatch.js +19 -18
- package/dist/commands/agent/agent-support.js +0 -24
- package/dist/commands/agent/contribute-cli.js +43 -97
- package/dist/commands/completions.js +80 -23
- package/dist/commands/config-cli.js +44 -281
- package/dist/commands/env/env-binding.js +99 -0
- package/dist/commands/env/env-cli.js +84 -224
- package/dist/commands/env/env.js +12 -163
- package/dist/commands/env/marker-path.js +6 -0
- package/dist/commands/env/secret-cli.js +45 -61
- package/dist/commands/env/secret.js +32 -62
- package/dist/commands/feedback-cli.js +179 -85
- package/dist/commands/health/accept-rate.js +58 -0
- package/dist/commands/health/advisories.js +7 -8
- package/dist/commands/health/checks.js +279 -94
- package/dist/commands/health/html-report.js +197 -578
- package/dist/commands/health/improve-metrics.js +277 -246
- package/dist/commands/health/llm-usage.js +19 -19
- package/dist/commands/health/md-report.js +16 -7
- package/dist/commands/health/metrics.js +67 -32
- package/dist/commands/health/renderers.js +47 -0
- package/dist/commands/health/report-view-model.js +508 -0
- package/dist/commands/health/stash-exposure.js +1 -1
- package/dist/commands/health/surfaces.js +16 -56
- package/dist/commands/health/task-runs.js +3 -67
- package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
- package/dist/commands/health/types-improve.js +29 -0
- package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
- package/dist/commands/health/types-result.js +7 -0
- package/dist/commands/health/types-runs.js +4 -0
- package/dist/commands/health/types-session-log.js +4 -0
- package/dist/commands/health/types-windows.js +4 -0
- package/dist/commands/health/types.js +26 -21
- package/dist/commands/health/windows.js +2 -3
- package/dist/commands/health.js +296 -167
- package/dist/commands/improve/anti-collapse.js +5 -5
- package/dist/commands/improve/autonomy-gate.js +68 -0
- package/dist/commands/improve/collapse-detector.js +65 -52
- package/dist/commands/improve/consolidate/chunking.js +9 -7
- package/dist/commands/improve/consolidate/eligibility.js +1 -23
- package/dist/commands/improve/consolidate/merge.js +4 -0
- package/dist/commands/improve/consolidate.js +454 -1354
- package/dist/commands/improve/content-hash.js +39 -0
- package/dist/commands/improve/distill/content-repair.js +4 -10
- package/dist/commands/improve/distill/promote-memory.js +89 -64
- package/dist/commands/improve/distill/quality-gate.js +118 -42
- package/dist/commands/improve/distill-guards.js +1 -1
- package/dist/commands/improve/distill-promotion-policy.js +33 -888
- package/dist/commands/improve/distill.js +607 -363
- package/dist/commands/improve/eligibility.js +165 -79
- package/dist/commands/improve/extract-cli.js +35 -126
- package/dist/commands/improve/extract-prompt.js +6 -35
- package/dist/commands/improve/extract.js +640 -391
- package/dist/commands/improve/feedback-valence.js +2 -12
- package/dist/commands/improve/improve-cli.js +134 -135
- package/dist/commands/improve/improve-result-file.js +30 -50
- package/dist/commands/improve/improve-run-types.js +4 -0
- package/dist/commands/improve/improve-strategies.js +135 -0
- package/dist/commands/improve/improve.js +904 -701
- package/dist/commands/improve/locks.js +64 -111
- package/dist/commands/improve/loop-stages.js +1110 -923
- package/dist/commands/improve/memory/derived-ref.js +124 -0
- package/dist/commands/improve/memory/memory-belief.js +79 -7
- package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
- package/dist/commands/improve/memory/memory-improve.js +25 -37
- package/dist/commands/improve/outcome-loop.js +25 -88
- package/dist/commands/improve/preparation.js +1034 -813
- package/dist/commands/improve/proactive-maintenance.js +34 -9
- package/dist/commands/improve/proposal-envelope.js +31 -0
- package/dist/commands/improve/reflect.js +983 -794
- package/dist/commands/improve/run-context.js +119 -0
- package/dist/commands/improve/salience.js +24 -127
- package/dist/commands/improve/session-asset.js +7 -3
- package/dist/commands/improve/shared.js +14 -34
- package/dist/commands/improve/source-identity.js +28 -0
- package/dist/commands/improve/triage.js +20 -17
- package/dist/commands/lint/base-linter.js +340 -313
- package/dist/commands/lint/env-key-rules.js +31 -47
- package/dist/commands/lint/index.js +185 -30
- package/dist/commands/{events.js → log.js} +28 -38
- package/dist/commands/migrate-cli.js +54 -0
- package/dist/commands/migration-tool.js +55 -0
- package/dist/commands/observability-cli.js +70 -208
- package/dist/commands/proposal/diff-format.js +50 -0
- package/dist/commands/proposal/drain-policies.js +0 -6
- package/dist/commands/proposal/drain.js +91 -40
- package/dist/commands/proposal/proposal-cli.js +134 -132
- package/dist/commands/proposal/proposal-types.js +56 -0
- package/dist/commands/proposal/proposal.js +83 -65
- package/dist/commands/proposal/propose-cli.js +88 -0
- package/dist/commands/proposal/propose.js +105 -88
- package/dist/commands/proposal/repository.js +1303 -278
- package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
- package/dist/commands/proposal/validators/proposal-validators.js +61 -12
- package/dist/commands/proposal/validators/proposals.js +6 -8
- package/dist/commands/read/curate.js +78 -73
- package/dist/commands/read/knowledge.js +510 -13
- package/dist/commands/read/registry-search.js +2 -2
- package/dist/commands/read/remember-cli.js +84 -15
- package/dist/commands/read/search-cli.js +203 -96
- package/dist/commands/read/search.js +126 -94
- package/dist/commands/read/show.js +226 -250
- package/dist/commands/registry-cli.js +34 -60
- package/dist/commands/remember.js +18 -57
- package/dist/commands/sources/add-cli.js +104 -49
- package/dist/commands/sources/bundle-cli.js +166 -0
- package/dist/commands/sources/bundle-config-ops.js +63 -0
- package/dist/commands/sources/info.js +27 -15
- package/dist/commands/sources/init.js +30 -40
- package/dist/commands/sources/installed-stashes.js +469 -172
- package/dist/commands/sources/migration-help.js +7 -4
- package/dist/commands/sources/schema-repair.js +10 -9
- package/dist/commands/sources/self-update.js +182 -121
- package/dist/commands/sources/source-add.js +169 -178
- package/dist/commands/sources/source-clone.js +144 -41
- package/dist/commands/sources/source-manage.js +94 -59
- package/dist/commands/sources/sources-cli.js +64 -205
- package/dist/commands/sources/stash-cli.js +91 -54
- package/dist/commands/sources/stash-skeleton.js +1 -1
- package/dist/commands/tasks/tasks-cli.js +106 -104
- package/dist/commands/tasks/tasks.js +445 -262
- package/dist/commands/workflow-cli.js +232 -121
- package/dist/core/action-contributors.js +1 -1
- package/dist/core/activation-policy.js +49 -0
- package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
- package/dist/core/adapter/adapters/akm-adapter.js +528 -0
- package/dist/core/adapter/adapters/akm-lint.js +392 -0
- package/dist/core/adapter/adapters/akm-metadata.js +387 -0
- package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
- package/dist/core/adapter/adapters/claude-adapter.js +61 -0
- package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
- package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
- package/dist/core/adapter/adapters/index.js +80 -0
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
- package/dist/core/adapter/adapters/okf-adapter.js +391 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
- package/dist/core/adapter/adapters/shared.js +286 -0
- package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
- package/dist/core/adapter/bundle-adapter.js +4 -0
- package/dist/core/adapter/detect-adapter.js +17 -0
- package/dist/core/adapter/recognize-match.js +44 -0
- package/dist/core/adapter/registry.js +56 -0
- package/dist/core/adapter/types.js +4 -0
- package/dist/core/asset/akm-markdown.js +30 -0
- package/dist/core/asset/asset-placement.js +243 -0
- package/dist/core/asset/asset-ref.js +110 -79
- package/dist/core/asset/asset-serialize.js +20 -0
- package/dist/core/asset/frontmatter.js +28 -12
- package/dist/core/asset/markdown.js +40 -51
- package/dist/core/asset/resolve-ref.js +274 -0
- package/dist/core/asset/stash-meta.js +2 -2
- package/dist/core/bundle-id.js +51 -0
- package/dist/core/common.js +281 -86
- package/dist/core/config/config-io.js +42 -128
- package/dist/core/config/config-schema.js +233 -834
- package/dist/core/config/config-sources.js +162 -39
- package/dist/core/config/config-types.js +16 -11
- package/dist/core/config/config-version.js +29 -0
- package/dist/core/config/config-walker.js +126 -37
- package/dist/core/config/config.js +154 -331
- package/dist/core/config/deep-merge.js +41 -0
- package/dist/core/config/engine-semantics.js +28 -0
- package/dist/core/config/experimental.js +21 -0
- package/dist/core/config/schema/embedding.js +38 -0
- package/dist/core/config/schema/engines.js +116 -0
- package/dist/core/config/schema/experimental.js +47 -0
- package/dist/core/config/schema/feedback.js +31 -0
- package/dist/core/config/schema/improve-processes.js +389 -0
- package/dist/core/config/schema/improve.js +94 -0
- package/dist/core/config/schema/index-config.js +176 -0
- package/dist/core/config/schema/output.js +18 -0
- package/dist/core/config/schema/primitives.js +94 -0
- package/dist/core/config/schema/search.js +30 -0
- package/dist/core/config/schema/setup.js +18 -0
- package/dist/core/config/schema/sources-bundles.js +169 -0
- package/dist/core/config/schema/workflow.js +29 -0
- package/dist/core/env-secret-ref.js +155 -20
- package/dist/core/errors.js +17 -15
- package/dist/core/events-types.js +4 -0
- package/dist/core/events.js +46 -128
- package/dist/core/extra-params.js +62 -0
- package/dist/core/file-change.js +17 -0
- package/dist/core/file-lock.js +202 -57
- package/dist/core/fs-txn.js +392 -0
- package/dist/core/git-message.js +59 -0
- package/dist/core/improve-result.js +167 -0
- package/dist/core/json-schema.js +142 -0
- package/dist/core/lesson-lint.js +1 -17
- package/dist/core/logs-db.js +1 -1
- package/dist/core/maintenance-barrier.js +135 -0
- package/dist/core/migration-operation.js +44 -0
- package/dist/core/mutation-target.js +78 -0
- package/dist/core/paths.js +22 -25
- package/dist/core/platform.js +10 -0
- package/dist/core/recognition-util.js +128 -0
- package/dist/core/redaction.js +392 -0
- package/dist/core/standards/resolve-standards-context.js +36 -65
- package/dist/core/standards/resolve-stash-standards.js +2 -2
- package/dist/core/standards/resolve-type-conventions.js +5 -5
- package/dist/core/state/migrations.js +242 -11
- package/dist/core/state-db.js +98 -10
- package/dist/core/structured.js +1 -1
- package/dist/core/subprocess.js +303 -0
- package/dist/core/text-truncation.js +9 -5
- package/dist/core/time.js +20 -0
- package/dist/core/type-presentation.js +130 -0
- package/dist/core/warn.js +0 -3
- package/dist/core/write-source.js +834 -118
- package/dist/indexer/bundle-identity-guard.js +92 -0
- package/dist/indexer/db/graph-db.js +1 -25
- package/dist/indexer/db/llm-cache.js +1 -1
- package/dist/indexer/ensure-index.js +30 -9
- package/dist/indexer/graph/graph-boost.js +9 -30
- package/dist/indexer/graph/graph-extraction.js +41 -27
- package/dist/indexer/graph/graph-types.js +4 -0
- package/dist/indexer/index-writer-lock.js +93 -49
- package/dist/indexer/index-written-assets.js +100 -53
- package/dist/indexer/indexer.js +746 -329
- package/dist/indexer/init.js +18 -25
- package/dist/indexer/installations.js +142 -0
- package/dist/indexer/passes/dir-staleness.js +18 -10
- package/dist/indexer/passes/memory-inference.js +25 -15
- package/dist/indexer/passes/metadata.js +412 -243
- package/dist/indexer/scan/doc-to-entry.js +160 -0
- package/dist/indexer/scan/drain-dir.js +134 -0
- package/dist/indexer/search/db-search.js +292 -108
- package/dist/indexer/search/fts-query.js +64 -0
- package/dist/indexer/search/ranking-contributors.js +145 -25
- package/dist/indexer/search/ranking-types.js +4 -0
- package/dist/indexer/search/ranking.js +28 -71
- package/dist/indexer/search/search-attribution.js +67 -0
- package/dist/indexer/search/search-fields.js +18 -3
- package/dist/indexer/search/search-hit-enrichers.js +30 -40
- package/dist/indexer/search/search-source.js +157 -111
- package/dist/indexer/search/semantic-status.js +4 -1
- package/dist/indexer/usage/usage-events.js +10 -30
- package/dist/indexer/walk/file-context.js +3 -45
- package/dist/indexer/walk/matchers.js +42 -34
- package/dist/indexer/walk/path-resolver.js +11 -5
- package/dist/indexer/walk/walker.js +42 -14
- package/dist/integrations/agent/builder-shared.js +7 -0
- package/dist/integrations/agent/builders.js +5 -56
- package/dist/integrations/agent/config.js +3 -143
- package/dist/integrations/agent/detect.js +17 -2
- package/dist/integrations/agent/engine-resolution.js +231 -0
- package/dist/integrations/agent/index.js +1 -2
- package/dist/integrations/agent/model-aliases.js +16 -2
- package/dist/integrations/agent/profiles.js +36 -62
- package/dist/integrations/agent/prompts.js +46 -18
- package/dist/integrations/agent/runner-dispatch.js +93 -4
- package/dist/integrations/agent/runner.js +76 -208
- package/dist/integrations/agent/spawn.js +88 -196
- package/dist/integrations/harnesses/aider/agent-builder.js +114 -0
- package/dist/integrations/harnesses/aider/index.js +48 -0
- package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
- package/dist/integrations/harnesses/amazonq/agent-builder.js +147 -0
- package/dist/integrations/harnesses/amazonq/index.js +45 -0
- package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
- package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
- package/dist/integrations/harnesses/claude/config-import.js +1 -3
- package/dist/integrations/harnesses/claude/index.js +24 -35
- package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
- package/dist/integrations/harnesses/claude/session-log.js +27 -75
- package/dist/integrations/harnesses/codex/agent-builder.js +138 -0
- package/dist/integrations/harnesses/codex/index.js +52 -0
- package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
- package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
- package/dist/integrations/harnesses/copilot/index.js +48 -0
- package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
- package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
- package/dist/integrations/harnesses/gemini/index.js +48 -0
- package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
- package/dist/integrations/harnesses/ids.js +24 -0
- package/dist/integrations/harnesses/index.js +54 -34
- package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
- package/dist/integrations/harnesses/opencode/config-import.js +1 -3
- package/dist/integrations/harnesses/opencode/index.js +14 -32
- package/dist/integrations/harnesses/opencode/session-log.js +67 -125
- package/dist/integrations/harnesses/opencode-sdk/harness.js +51 -0
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
- package/dist/integrations/harnesses/openhands/agent-builder.js +128 -0
- package/dist/integrations/harnesses/openhands/index.js +48 -0
- package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
- package/dist/integrations/harnesses/pi/agent-builder.js +97 -0
- package/dist/integrations/harnesses/pi/index.js +45 -0
- package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
- package/dist/integrations/harnesses/shared.js +17 -0
- package/dist/integrations/harnesses/types.js +43 -32
- package/dist/integrations/lockfile.js +211 -24
- package/dist/integrations/session-logs/index.js +36 -39
- package/dist/integrations/session-logs/provider-base.js +113 -0
- package/dist/llm/client.js +182 -110
- package/dist/llm/embedders/deterministic.js +2 -2
- package/dist/llm/embedders/remote.js +21 -9
- package/dist/llm/feature-gate.js +17 -57
- package/dist/llm/graph-extract.js +12 -13
- package/dist/llm/index-passes.js +8 -42
- package/dist/llm/memory-infer.js +144 -1
- package/dist/llm/metadata-enhance.js +45 -30
- package/dist/llm/structured-call.js +16 -8
- package/dist/llm/usage-persist.js +30 -5
- package/dist/llm/usage-telemetry.js +59 -6
- package/dist/output/cli-hints.js +1 -2
- package/dist/output/command-registry.js +27 -0
- package/dist/output/context.js +22 -7
- package/dist/output/format-exempt.js +80 -0
- package/dist/output/generic-render.js +251 -0
- package/dist/output/html-render.js +11 -16
- package/dist/output/render-registry.js +57 -0
- package/dist/output/renderers.js +14 -279
- package/dist/output/shapes/curate.js +10 -1
- package/dist/output/shapes/events.js +12 -7
- package/dist/output/shapes/helpers.js +58 -84
- package/dist/output/shapes/passthrough.js +11 -39
- package/dist/output/shapes/proposal/producer.js +15 -7
- package/dist/output/shapes/registry.js +12 -6
- package/dist/output/shapes.js +0 -9
- package/dist/output/text/{init.js → bundle-create.js} +3 -1
- package/dist/output/text/bundle-show.js +7 -0
- package/dist/output/text/command-format.js +562 -0
- package/dist/output/text/env.js +1 -3
- package/dist/output/text/events.js +8 -7
- package/dist/output/text/helpers.js +15 -1164
- package/dist/output/text/proposal/producer.js +4 -2
- package/dist/output/text/proposal-format.js +202 -0
- package/dist/output/text/registry-commands.js +1 -2
- package/dist/output/text/registry.js +12 -6
- package/dist/output/text/show-directives.js +117 -0
- package/dist/output/text/show-format.js +103 -0
- package/dist/output/text/sync.js +5 -0
- package/dist/output/text/workflow-format.js +332 -0
- package/dist/output/text/workflow.js +3 -2
- package/dist/output/text.js +10 -19
- package/dist/registry/factory.js +4 -6
- package/dist/registry/origin-resolve.js +16 -27
- package/dist/registry/providers/skills-sh.js +3 -3
- package/dist/registry/providers/static-index.js +15 -25
- package/dist/registry/resolve.js +43 -94
- package/dist/registry/semver.js +43 -0
- package/dist/runtime.js +81 -12
- package/dist/scripts/akm-migrate.js +35529 -0
- package/dist/setup/detect.js +5 -7
- package/dist/setup/detected-engines.js +136 -0
- package/dist/setup/engine-config.js +100 -0
- package/dist/setup/registry-stash-loader.js +3 -3
- package/dist/setup/semantic-assets.js +12 -9
- package/dist/setup/setup.js +444 -208
- package/dist/setup/steps/connection-shared.js +120 -0
- package/dist/setup/steps/connection.js +108 -305
- package/dist/setup/steps/platforms.js +13 -12
- package/dist/setup/steps/semantic.js +15 -3
- package/dist/setup/steps/sources.js +21 -15
- package/dist/setup/steps/stashdir.js +6 -4
- package/dist/setup/steps/tasks.js +236 -119
- package/dist/setup/steps.js +3 -2
- package/dist/sources/freshness.js +39 -0
- package/dist/sources/provider-factory.js +11 -17
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +278 -34
- package/dist/sources/providers/git-provider.js +54 -56
- package/dist/sources/providers/git-stash.js +420 -91
- package/dist/sources/providers/git.js +2 -2
- package/dist/sources/providers/npm.js +16 -19
- package/dist/sources/providers/provider-utils.js +47 -22
- package/dist/sources/providers/sync-from-ref.js +3 -9
- package/dist/sources/providers/website.js +2 -2
- package/dist/sources/resolve.js +11 -10
- package/dist/sources/snapshot-fetchers/types.js +4 -0
- package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
- package/dist/storage/database.js +60 -4
- package/dist/storage/engines/sqlite-migrations.js +156 -5
- package/dist/storage/locations.js +1 -2
- package/dist/storage/repositories/canaries-repository.js +1 -1
- package/dist/storage/repositories/events-repository.js +51 -11
- package/dist/storage/repositories/improve-runs-repository.js +6 -32
- package/dist/storage/repositories/index-connection.js +79 -0
- package/dist/storage/repositories/index-db.js +4 -3
- package/dist/storage/repositories/index-entries-repository.js +863 -0
- package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
- package/dist/storage/repositories/index-entry-types.js +4 -0
- package/dist/storage/repositories/index-fts-repository.js +167 -0
- package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
- package/dist/storage/repositories/index-meta-repository.js +49 -0
- package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
- package/dist/storage/repositories/index-sql.js +12 -0
- package/dist/storage/repositories/index-utility-repository.js +356 -0
- package/dist/storage/repositories/index-vec-repository.js +250 -0
- package/dist/storage/repositories/outcome-repository.js +119 -0
- package/dist/storage/repositories/proposals-repository.js +317 -75
- package/dist/storage/repositories/registry-cache.js +1 -1
- package/dist/storage/repositories/salience-repository.js +172 -0
- package/dist/storage/repositories/task-history-repository.js +110 -3
- package/dist/storage/repositories/workflow-runs-repository.js +240 -19
- package/dist/tasks/backends/cron.js +169 -46
- package/dist/tasks/backends/exec-utils.js +76 -3
- package/dist/tasks/backends/index.js +6 -9
- package/dist/tasks/backends/launchd.js +292 -55
- package/dist/tasks/backends/schtasks.js +557 -70
- package/dist/tasks/backends/types.js +4 -0
- package/dist/tasks/command-executable.js +93 -0
- package/dist/tasks/embedded.js +56 -38
- package/dist/tasks/parser.js +156 -64
- package/dist/tasks/resolve-akm-bin.js +144 -51
- package/dist/tasks/runner.js +377 -209
- package/dist/tasks/schedule.js +108 -19
- package/dist/tasks/scheduler-invocation.js +296 -0
- package/dist/tasks/schema.js +1 -1
- package/dist/tasks/task-id.js +35 -0
- package/dist/tasks/validator.js +30 -16
- package/dist/text-import-hook.mjs +1 -1
- package/dist/workflows/authoring/authoring.js +104 -43
- package/dist/workflows/authoring/scope-key.js +1 -1
- package/dist/workflows/cli.js +0 -16
- package/dist/workflows/concurrency-policy.js +15 -0
- package/dist/workflows/exec/brief.js +450 -0
- package/dist/workflows/exec/frozen-judge.js +47 -0
- package/dist/workflows/exec/native-executor.js +1038 -0
- package/dist/workflows/exec/param-secrets.js +115 -0
- package/dist/workflows/exec/report.js +1460 -0
- package/dist/workflows/exec/run-workflow.js +602 -0
- package/dist/workflows/exec/scheduler.js +71 -0
- package/dist/workflows/exec/step-work.js +1190 -0
- package/dist/workflows/exec/unit-writer.js +23 -0
- package/dist/workflows/exec/workflow-engine-gate.js +67 -0
- package/dist/workflows/exec/worktree.js +171 -0
- package/dist/workflows/ir/compile.js +246 -0
- package/dist/workflows/ir/freeze.js +233 -0
- package/dist/workflows/ir/params.js +54 -0
- package/dist/workflows/ir/plan-hash.js +68 -0
- package/dist/workflows/ir/schema.js +540 -0
- package/dist/workflows/parser.js +878 -304
- package/dist/workflows/program/expressions.js +181 -0
- package/dist/workflows/program/schema.js +51 -0
- package/dist/workflows/renderer.js +100 -45
- package/dist/workflows/resource-limits.js +22 -0
- package/dist/workflows/runtime/agent-identity.js +59 -14
- package/dist/workflows/runtime/checkin.js +1 -1
- package/dist/workflows/runtime/plan-classifier.js +131 -0
- package/dist/workflows/runtime/runs.js +376 -119
- package/dist/workflows/runtime/unit-checkin.js +45 -0
- package/dist/workflows/runtime/unit-phases.js +20 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +241 -40
- package/dist/workflows/schema.js +1 -11
- package/dist/workflows/validate-summary.js +2 -3
- package/dist/workflows/validator.js +52 -30
- package/docs/README.md +42 -78
- package/docs/migration/README.md +8 -0
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +9 -8
- package/docs/migration/release-notes/0.9.0.md +158 -14
- package/docs/migration/v0.7-to-v0.8.md +46 -47
- package/docs/migration/v0.8-to-v0.9.md +844 -0
- package/docs/reference/README.md +12 -0
- package/docs/reference/data-and-telemetry.md +333 -0
- package/package.json +21 -17
- package/schemas/akm-asset-envelope.json +93 -0
- package/schemas/akm-config.json +4636 -0
- package/schemas/akm-task.json +87 -0
- package/schemas/akm-workflow.json +373 -0
- package/dist/akm-migrate-storage +0 -38
- package/dist/assets/help/help-accept.md +0 -12
- package/dist/assets/help/help-improve.md +0 -84
- package/dist/assets/help/help-proposals.md +0 -17
- package/dist/assets/help/help-propose.md +0 -17
- package/dist/assets/help/help-reject.md +0 -11
- package/dist/assets/profiles/frequent.json +0 -13
- package/dist/assets/profiles/recombine-only.json +0 -21
- package/dist/assets/profiles/reflect-distill.json +0 -30
- package/dist/assets/profiles/synthesize.json +0 -15
- package/dist/assets/prompts/procedural-system.md +0 -44
- package/dist/assets/prompts/recombine-system.md +0 -40
- package/dist/assets/prompts/staleness-detect-system.md +0 -6
- package/dist/assets/tasks/core/backup.yml +0 -4
- package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
- package/dist/assets/templates/html/default.html +0 -78
- package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
- package/dist/assets/wiki/index-template.md +0 -12
- package/dist/assets/wiki/ingest-workflow-template.md +0 -83
- package/dist/assets/wiki/log-template.md +0 -8
- package/dist/assets/wiki/schema-template.md +0 -61
- package/dist/cli/config-migrate.js +0 -150
- package/dist/cli/config-validate.js +0 -39
- package/dist/commands/graph/graph-cli.js +0 -124
- package/dist/commands/graph/graph.js +0 -487
- package/dist/commands/improve/calibration.js +0 -161
- package/dist/commands/improve/dedup.js +0 -482
- package/dist/commands/improve/extract-watch.js +0 -140
- package/dist/commands/improve/hot-probation.js +0 -45
- package/dist/commands/improve/improve-auto-accept.js +0 -276
- package/dist/commands/improve/improve-profiles.js +0 -168
- package/dist/commands/improve/procedural.js +0 -398
- package/dist/commands/improve/recombine.js +0 -818
- package/dist/commands/improve/schema-similarity-gate.js +0 -168
- package/dist/commands/lint/agent-linter.js +0 -44
- package/dist/commands/lint/command-linter.js +0 -44
- package/dist/commands/lint/default-linter.js +0 -16
- package/dist/commands/lint/fact-linter.js +0 -39
- package/dist/commands/lint/knowledge-linter.js +0 -16
- package/dist/commands/lint/memory-linter.js +0 -61
- package/dist/commands/lint/registry.js +0 -41
- package/dist/commands/lint/skill-linter.js +0 -45
- package/dist/commands/lint/task-linter.js +0 -50
- package/dist/commands/lint/workflow-linter.js +0 -81
- package/dist/commands/proposal/legacy-import.js +0 -115
- package/dist/commands/sources/history.js +0 -196
- package/dist/commands/tasks/default-tasks.js +0 -186
- package/dist/commands/wiki-cli.js +0 -292
- package/dist/core/asset/asset-registry.js +0 -76
- package/dist/core/asset/asset-spec.js +0 -259
- package/dist/core/config/config-migration.js +0 -602
- package/dist/core/deep-merge.js +0 -38
- package/dist/core/eval/rank-metrics.js +0 -113
- package/dist/core/ripgrep/install.js +0 -163
- package/dist/core/ripgrep/resolve.js +0 -81
- package/dist/indexer/db/db.js +0 -1413
- package/dist/indexer/manifest.js +0 -170
- package/dist/indexer/passes/metadata-contributors.js +0 -31
- package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
- package/dist/integrations/harnesses/opencode-sdk/index.js +0 -49
- package/dist/llm/call-ai.js +0 -62
- package/dist/llm/memory-infer-impl.js +0 -138
- package/dist/output/shapes/distill.js +0 -14
- package/dist/output/shapes/history.js +0 -11
- package/dist/output/text/distill.js +0 -6
- package/dist/output/text/enable-disable.js +0 -8
- package/dist/output/text/history.js +0 -6
- package/dist/output/text/wiki.js +0 -16
- package/dist/registry/build-index.js +0 -386
- package/dist/scripts/migrate-storage.js +0 -19108
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -9411
- package/dist/scripts/migrations/v16-to-v17.js +0 -141
- package/dist/setup/legacy-config.js +0 -106
- package/dist/storage/repositories/consolidation-repository.js +0 -38
- package/dist/storage/repositories/recombine-repository.js +0 -213
- package/dist/wiki/wiki-templates.js +0 -15
- package/dist/wiki/wiki.js +0 -1012
- package/dist/workflows/db.js +0 -215
- package/docs/data-and-telemetry.md +0 -226
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
|
@@ -11,46 +11,106 @@
|
|
|
11
11
|
* helpers only resolve refs to absolute paths and guard against directory
|
|
12
12
|
* traversal.
|
|
13
13
|
*/
|
|
14
|
+
import fs from "node:fs";
|
|
14
15
|
import path from "node:path";
|
|
15
16
|
import { resolveSourceEntries } from "../indexer/search/search-source.js";
|
|
17
|
+
import { resolveSourcesForOrigin } from "../registry/origin-resolve.js";
|
|
16
18
|
import { assertFlatAssetName, combineCreatePath, normalizeCreateSubPath } from "./asset/asset-create.js";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
+
import { assetPathForName } from "./asset/asset-placement.js";
|
|
20
|
+
import { displayRef, isFullRefInput, parseRefInput } from "./asset/resolve-ref.js";
|
|
19
21
|
import { isWithin } from "./common.js";
|
|
20
22
|
import { loadConfig } from "./config/config.js";
|
|
21
23
|
import { NotFoundError, UsageError } from "./errors.js";
|
|
24
|
+
import { resolveMutationTarget } from "./mutation-target.js";
|
|
25
|
+
import { formatRefForMessage, withWriteTargetMutation } from "./write-source.js";
|
|
26
|
+
/**
|
|
27
|
+
* The `vault` asset type was removed in 0.9.0. The env/secret input path no
|
|
28
|
+
* longer routes through the legacy stored-ref parser (which carries the removal
|
|
29
|
+
* signpost), so a `vault:`/`vault/` leading token would otherwise be silently
|
|
30
|
+
* qualified into an `env/vault:…` not-found. Detect it here and re-emit the
|
|
31
|
+
* migration signpost so 0.8→0.9 muscle memory still gets pointed at env/secret.
|
|
32
|
+
*/
|
|
33
|
+
function assertNotRemovedVaultRef(ref) {
|
|
34
|
+
const boundary = ref.indexOf("//");
|
|
35
|
+
const bare = boundary >= 0 ? ref.slice(boundary + 2) : ref;
|
|
36
|
+
if (/^vault[:/]/.test(bare.trim())) {
|
|
37
|
+
throw new UsageError("The `vault` asset type was removed in 0.9.0 — use `env/` (whole .env config) or `secrets/` (a single value).", "INVALID_FLAG_VALUE");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Q-08 ruling: the pre-0.9.0 `type:name` ref grammar taught `env:<name>` /
|
|
42
|
+
* `secret:<name>` (help text and docs also implied `environment:`/`secrets:`
|
|
43
|
+
* variants) as the way to address a single env/secret. That grammar is GONE —
|
|
44
|
+
* NO alias, no re-acceptance (same rule as the retired `vault:` prefix above).
|
|
45
|
+
* Left unchecked, a colon-prefixed ref does not error here at all: it falls
|
|
46
|
+
* through to the "bare name" convenience below and gets silently qualified
|
|
47
|
+
* into a literal `env/env:name` (or `secrets/secret:name`) file that can never
|
|
48
|
+
* exist — a confusing not-found that hides the real mistake instead of naming
|
|
49
|
+
* it. Reject it here, loudly, before that happens.
|
|
50
|
+
*/
|
|
51
|
+
function assertNotColonRef(ref, aliases, replacement) {
|
|
52
|
+
const boundary = ref.indexOf("//");
|
|
53
|
+
const bare = (boundary >= 0 ? ref.slice(boundary + 2) : ref).trim();
|
|
54
|
+
const colon = bare.indexOf(":");
|
|
55
|
+
if (colon <= 0)
|
|
56
|
+
return;
|
|
57
|
+
const head = bare.slice(0, colon).toLowerCase();
|
|
58
|
+
if (!aliases.includes(head))
|
|
59
|
+
return;
|
|
60
|
+
const name = bare.slice(colon + 1);
|
|
61
|
+
throw new UsageError(`The \`${head}:\` ref spelling was removed in 0.9.0 — use the slash form instead: \`${replacement}${name}\`.`, "INVALID_FLAG_VALUE");
|
|
62
|
+
}
|
|
22
63
|
export function parseEnvRef(ref) {
|
|
23
|
-
|
|
64
|
+
// Accept a bare env name (`prod`, `sub/prod`) or the new-grammar
|
|
65
|
+
// `[bundle//]env/name` conceptId. A bare name's leading segment maps to no
|
|
66
|
+
// asset type, so it is qualified with the `env/` conceptId prefix; anything
|
|
67
|
+
// already a full new-grammar ref is parsed as-is.
|
|
68
|
+
assertNotRemovedVaultRef(ref);
|
|
69
|
+
assertNotColonRef(ref, ["env", "environment"], "env/");
|
|
70
|
+
return parseRefInput(isFullRefInput(ref) ? ref : `env/${ref}`);
|
|
24
71
|
}
|
|
25
|
-
export function findEnvSource(origin) {
|
|
72
|
+
export function findEnvSource(origin, type, name) {
|
|
26
73
|
const sources = resolveSourceEntries(undefined, loadConfig());
|
|
27
74
|
if (sources.length === 0) {
|
|
28
|
-
throw new UsageError("No
|
|
75
|
+
throw new UsageError("No bundles configured. Run `akm bundle create` to create your working bundle.");
|
|
76
|
+
}
|
|
77
|
+
const candidates = origin ? resolveSourcesForOrigin(origin, sources) : sources;
|
|
78
|
+
const typeDir = type === "env" ? "env" : "secrets";
|
|
79
|
+
const member = candidates.find((source) => fs.existsSync(assetPathForName(type, path.join(source.path, typeDir), name)));
|
|
80
|
+
if (member)
|
|
81
|
+
return member;
|
|
82
|
+
if (!origin) {
|
|
83
|
+
const fallback = candidates[0];
|
|
84
|
+
if (fallback)
|
|
85
|
+
return fallback;
|
|
86
|
+
throw new UsageError("No bundles configured. Run `akm bundle create` to create your working bundle.");
|
|
29
87
|
}
|
|
30
|
-
|
|
31
|
-
return sources[0];
|
|
32
|
-
const named = sources.find((source) => source.registryId === origin);
|
|
88
|
+
const named = candidates[0];
|
|
33
89
|
if (!named) {
|
|
34
90
|
throw new NotFoundError(`Source not found for origin: ${origin}`);
|
|
35
91
|
}
|
|
36
92
|
return named;
|
|
37
93
|
}
|
|
38
94
|
export function makeEnvRef(name, source) {
|
|
39
|
-
|
|
95
|
+
// F4b output-spelling flip: `env/name` in the primary stash, `bundle//env/name`
|
|
96
|
+
// for a slug-clean named source.
|
|
97
|
+
return displayRef({ type: "env", name, bundleId: source?.registryId }, displayDefaultBundle(source));
|
|
40
98
|
}
|
|
41
99
|
/**
|
|
42
|
-
* Resolve an env ref to an absolute `.env` path. Accepts `env
|
|
43
|
-
*
|
|
44
|
-
*
|
|
100
|
+
* Resolve an env ref to an absolute `.env` path. Accepts the `env/<name>`
|
|
101
|
+
* conceptId (or a bare name, auto-qualified into it) — the retired
|
|
102
|
+
* `env:`/`environment:` colon spelling is rejected loudly (Q-08), never
|
|
103
|
+
* silently resolved. The path is returned even when the file does not yet
|
|
104
|
+
* exist (so `create` writes under `env/`).
|
|
45
105
|
*/
|
|
46
106
|
export function resolveEnvPath(ref) {
|
|
47
107
|
const parsed = parseEnvRef(ref);
|
|
48
108
|
if (parsed.type !== "env") {
|
|
49
|
-
throw new UsageError(`Expected an env ref (env
|
|
109
|
+
throw new UsageError(`Expected an env ref (env/<name>); got "${ref}".`);
|
|
50
110
|
}
|
|
51
|
-
const source = findEnvSource(parsed.origin);
|
|
111
|
+
const source = findEnvSource(parsed.origin, "env", parsed.name);
|
|
52
112
|
const envRoot = path.join(source.path, "env");
|
|
53
|
-
const envPath =
|
|
113
|
+
const envPath = assetPathForName("env", envRoot, parsed.name);
|
|
54
114
|
// Defense-in-depth: ensure the resolved path stays inside the env directory.
|
|
55
115
|
// validateName already rejects traversal patterns like "../../foo", but an
|
|
56
116
|
// absolute-path override or symlink-based attack could still escape without
|
|
@@ -61,10 +121,23 @@ export function resolveEnvPath(ref) {
|
|
|
61
121
|
return { name: parsed.name, absPath: envPath, source, parsedRef: parsed, dir: "env" };
|
|
62
122
|
}
|
|
63
123
|
export function parseSecretRef(ref) {
|
|
64
|
-
|
|
124
|
+
// Same bare-name-vs-full-ref rule as parseEnvRef; a bare name is qualified
|
|
125
|
+
// with the `secrets/` conceptId prefix (secret's stash subdir).
|
|
126
|
+
assertNotRemovedVaultRef(ref);
|
|
127
|
+
assertNotColonRef(ref, ["secret", "secrets"], "secrets/");
|
|
128
|
+
return parseRefInput(isFullRefInput(ref) ? ref : `secrets/${ref}`);
|
|
65
129
|
}
|
|
66
130
|
export function makeSecretRef(name, source) {
|
|
67
|
-
|
|
131
|
+
// F4b output-spelling flip: `secrets/name` in the primary stash,
|
|
132
|
+
// `bundle//secrets/name` for a slug-clean named source.
|
|
133
|
+
return displayRef({ type: "secret", name, bundleId: source?.registryId }, displayDefaultBundle(source));
|
|
134
|
+
}
|
|
135
|
+
function displayDefaultBundle(source) {
|
|
136
|
+
const config = loadConfig();
|
|
137
|
+
if (config.defaultBundle || !source)
|
|
138
|
+
return config.defaultBundle;
|
|
139
|
+
const primary = resolveSourceEntries(undefined, config)[0];
|
|
140
|
+
return primary && path.resolve(primary.path) === path.resolve(source.path) ? source.registryId : undefined;
|
|
68
141
|
}
|
|
69
142
|
export function resolveSecretPath(ref,
|
|
70
143
|
// Create-only (`secret set`): enforce a flat ref name and apply `--path` as
|
|
@@ -72,19 +145,81 @@ export function resolveSecretPath(ref,
|
|
|
72
145
|
create) {
|
|
73
146
|
const parsed = parseSecretRef(ref);
|
|
74
147
|
if (parsed.type !== "secret") {
|
|
75
|
-
throw new UsageError(`Expected a secret ref (
|
|
148
|
+
throw new UsageError(`Expected a secret ref (secrets/<name>); got "${ref}".`);
|
|
76
149
|
}
|
|
77
150
|
if (create) {
|
|
78
151
|
assertFlatAssetName(parsed.name);
|
|
79
152
|
parsed.name = combineCreatePath(normalizeCreateSubPath(create.subPath), parsed.name);
|
|
80
153
|
}
|
|
81
154
|
// Source resolution is identical for every asset type; reuse the env helper.
|
|
82
|
-
const source = findEnvSource(parsed.origin);
|
|
155
|
+
const source = findEnvSource(parsed.origin, "secret", parsed.name);
|
|
83
156
|
const typeRoot = path.join(source.path, "secrets");
|
|
84
|
-
const absPath =
|
|
157
|
+
const absPath = assetPathForName("secret", typeRoot, parsed.name);
|
|
85
158
|
// Defense-in-depth: ensure the resolved path stays inside the secrets dir.
|
|
86
159
|
if (!isWithin(absPath, typeRoot)) {
|
|
87
160
|
throw new UsageError(`Secret name "${parsed.name}" escapes the secrets directory.`);
|
|
88
161
|
}
|
|
89
162
|
return { name: parsed.name, absPath, source };
|
|
90
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Resolve the destination for an env mutation. Mirrors {@link resolveEnvPath}
|
|
166
|
+
* but selects the source via {@link resolveWriteTarget} (writability-checked)
|
|
167
|
+
* instead of the read-side {@link findEnvSource}. `create` enforces a flat ref
|
|
168
|
+
* name and applies `--path` as the subdirectory (matching `env create`).
|
|
169
|
+
*/
|
|
170
|
+
export function resolveEnvWriteTarget(ref, writeTarget, create) {
|
|
171
|
+
const parsed = parseEnvRef(ref);
|
|
172
|
+
if (parsed.type !== "env") {
|
|
173
|
+
throw new UsageError(`Expected an env ref (env/<name>); got "${ref}".`);
|
|
174
|
+
}
|
|
175
|
+
if (create) {
|
|
176
|
+
assertFlatAssetName(parsed.name);
|
|
177
|
+
parsed.name = combineCreatePath(normalizeCreateSubPath(create.subPath), parsed.name);
|
|
178
|
+
}
|
|
179
|
+
const resolved = resolveMutationTarget(loadConfig(), parsed, writeTarget, { allowedAdapters: ["akm", "dotenv"] });
|
|
180
|
+
const { target } = resolved;
|
|
181
|
+
const envRoot = path.join(target.source.path, "env");
|
|
182
|
+
const absPath = assetPathForName("env", envRoot, parsed.name);
|
|
183
|
+
if (!isWithin(absPath, envRoot)) {
|
|
184
|
+
throw new UsageError(`Env name "${parsed.name}" escapes the env directory.`);
|
|
185
|
+
}
|
|
186
|
+
return { name: parsed.name, absPath, target, parsedRef: resolved.ref, ref: resolved.displayRef };
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Resolve the destination for a secret mutation. Mirrors
|
|
190
|
+
* {@link resolveSecretPath} but selects the source via
|
|
191
|
+
* {@link resolveWriteTarget} (writability-checked) instead of the read-side
|
|
192
|
+
* {@link findEnvSource}.
|
|
193
|
+
*/
|
|
194
|
+
export function resolveSecretWriteTarget(ref, writeTarget, create) {
|
|
195
|
+
const parsed = parseSecretRef(ref);
|
|
196
|
+
if (parsed.type !== "secret") {
|
|
197
|
+
throw new UsageError(`Expected a secret ref (secrets/<name>); got "${ref}".`);
|
|
198
|
+
}
|
|
199
|
+
if (create) {
|
|
200
|
+
assertFlatAssetName(parsed.name);
|
|
201
|
+
parsed.name = combineCreatePath(normalizeCreateSubPath(create.subPath), parsed.name);
|
|
202
|
+
}
|
|
203
|
+
const resolved = resolveMutationTarget(loadConfig(), parsed, writeTarget, { allowedAdapters: ["akm", "dotenv"] });
|
|
204
|
+
const { target } = resolved;
|
|
205
|
+
const typeRoot = path.join(target.source.path, "secrets");
|
|
206
|
+
const absPath = assetPathForName("secret", typeRoot, parsed.name);
|
|
207
|
+
if (!isWithin(absPath, typeRoot)) {
|
|
208
|
+
throw new UsageError(`Secret name "${parsed.name}" escapes the secrets directory.`);
|
|
209
|
+
}
|
|
210
|
+
return { name: parsed.name, absPath, target, ref: resolved.displayRef };
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Land an env/secret mutation on its write target's git boundary. Mirrors the
|
|
214
|
+
* tasks/knowledge write path: record each mutated path, then fire the single
|
|
215
|
+
* batch-at-boundary commit. Both steps are no-ops for filesystem targets (the
|
|
216
|
+
* primary stash included) and for `env/` paths a stash `.gitignore` excludes, so
|
|
217
|
+
* callers invoke it unconditionally after every create/ingest/set/remove.
|
|
218
|
+
*/
|
|
219
|
+
export function withEnvSecretWrite(target, ref, op, paths, mutate) {
|
|
220
|
+
return withWriteTargetMutation(target, paths, {
|
|
221
|
+
ignored: "local-only",
|
|
222
|
+
purpose: `${ref.type}-${op.toLowerCase()}`,
|
|
223
|
+
message: `${op} ${formatRefForMessage({ type: ref.type, name: ref.name, origin: target.source.name })}`,
|
|
224
|
+
}, mutate);
|
|
225
|
+
}
|
package/dist/core/errors.js
CHANGED
|
@@ -6,32 +6,34 @@
|
|
|
6
6
|
* imperative. Returning undefined means "no canned hint".
|
|
7
7
|
*/
|
|
8
8
|
const CONFIG_HINTS = {
|
|
9
|
-
STASH_DIR_NOT_FOUND: "Run `akm setup` to create and configure your
|
|
10
|
-
STASH_DIR_NOT_A_DIRECTORY: "The configured
|
|
11
|
-
STASH_DIR_UNREADABLE: "Check the path exists and your user has read permission, or update
|
|
9
|
+
STASH_DIR_NOT_FOUND: "Run `akm setup` to create and configure your bundle, or configure a defaultBundle path.",
|
|
10
|
+
STASH_DIR_NOT_A_DIRECTORY: "The configured default bundle path exists but isn't a directory. Update it to point at a folder.",
|
|
11
|
+
STASH_DIR_UNREADABLE: "Check the path exists and your user has read permission, or update the default bundle path.",
|
|
12
12
|
EMBEDDING_NOT_CONFIGURED: 'Run `akm config set embedding \'{"endpoint":"...","model":"..."}\'` to enable embeddings.',
|
|
13
|
-
LLM_NOT_CONFIGURED: 'Run `akm setup` or `
|
|
14
|
-
TEST_ISOLATION_MISSING: "Under bun test, when
|
|
15
|
-
SETUP_TMP_STASH_REFUSED: "Use a persistent directory, or set AKM_FORCE_SETUP_TMP_STASH=1 to opt in to a sandboxed setup (setup also pre-sets
|
|
16
|
-
UNSAFE_STASH_DIR: "Choose a path inside your home directory (e.g. ~/akm) or another empty workspace. The
|
|
17
|
-
|
|
13
|
+
LLM_NOT_CONFIGURED: 'Run `akm setup` or configure an `engines` entry with `kind: "llm"`, then select it with `defaults.llmEngine`.',
|
|
14
|
+
TEST_ISOLATION_MISSING: "Under bun test, when AKM_BUNDLE_DIR is set you MUST also set XDG_DATA_HOME (or AKM_DATA_DIR) and XDG_STATE_HOME (or AKM_STATE_DIR) to temp directories so the test does not touch the developer's real ~/.local/share/akm or ~/.local/state/akm.",
|
|
15
|
+
SETUP_TMP_STASH_REFUSED: "Use a persistent directory, or set AKM_FORCE_SETUP_TMP_STASH=1 to opt in to a sandboxed setup (setup also pre-sets AKM_BUNDLE_DIR so config and cache writes auto-isolate into $stashDir/.akm/ — host config is preserved).",
|
|
16
|
+
UNSAFE_STASH_DIR: "Choose a path inside your home directory (e.g. ~/akm) or another empty workspace. The bundle directory cannot be the filesystem root, your home directory itself, or a sensitive system path like /etc, /var, ~/.config, or ~/.ssh.",
|
|
17
|
+
UNKNOWN_IMPROVE_STRATEGY: "Pass one of the listed strategy names to `--strategy`, or define it under `improve.strategies`. Names are case-sensitive.",
|
|
18
|
+
WORKFLOW_ENGINE_NOT_ENABLED: "Run `akm config set experimental.workflowEngine true` to enable it.",
|
|
18
19
|
};
|
|
19
20
|
/** Default hint for each UsageError code. */
|
|
20
21
|
const USAGE_HINTS = {
|
|
21
22
|
INVALID_FLAG_VALUE: "Run `akm <command> --help` to see accepted values.",
|
|
22
|
-
INVALID_SOURCE_VALUE: "Pick one of:
|
|
23
|
-
INVALID_FORMAT_VALUE: "Pick one of: json, jsonl, text,
|
|
23
|
+
INVALID_SOURCE_VALUE: "Pick one of: local, registry, all, or a configured source name.",
|
|
24
|
+
INVALID_FORMAT_VALUE: "Pick one of: json, jsonl, yaml, text, md, html.",
|
|
24
25
|
INVALID_DETAIL_VALUE: "Pick one of: brief, normal, full. For agent/summary projections use --shape.",
|
|
25
26
|
INVALID_SHAPE_VALUE: "Pick one of: human, agent, summary (summary is only valid on `akm show`).",
|
|
26
27
|
INVALID_JSON_CONFIG_VALUE: 'Quote JSON values in your shell, for example: akm config set embedding \'{"endpoint":"http://localhost:11434/v1/embeddings","model":"nomic-embed-text"}\'.',
|
|
27
|
-
MISSING_OR_AMBIGUOUS_TARGET: "Use `akm update --all` or pass a target like `akm update npm:@scope/pkg` (not both).",
|
|
28
|
-
TARGET_NOT_UPDATABLE: "Run `akm list` to view your sources, then retry with one of those values.",
|
|
29
|
-
MISSING_REQUIRED_ARGUMENT: "Refs use the form
|
|
28
|
+
MISSING_OR_AMBIGUOUS_TARGET: "Use `akm bundle update --all` or pass a target like `akm bundle update npm:@scope/pkg` (not both).",
|
|
29
|
+
TARGET_NOT_UPDATABLE: "Run `akm bundle list` to view your sources, then retry with one of those values.",
|
|
30
|
+
MISSING_REQUIRED_ARGUMENT: "Refs use the form [bundle//]conceptId, e.g. `akm show knowledge/guide.md` or `akm show skills/deploy`.",
|
|
31
|
+
UNKNOWN_COMMAND: "Run `akm --help` to see available commands.",
|
|
30
32
|
};
|
|
31
33
|
/** Default hint for each NotFoundError code. */
|
|
32
34
|
const NOT_FOUND_HINTS = {
|
|
33
35
|
ASSET_NOT_FOUND: "Run `akm search <query>` or `akm index` to refresh the index.",
|
|
34
|
-
SOURCE_NOT_FOUND: "Run `akm list` to view your sources, then retry with one of those values.",
|
|
36
|
+
SOURCE_NOT_FOUND: "Run `akm bundle list` to view your sources, then retry with one of those values.",
|
|
35
37
|
WORKFLOW_NOT_FOUND: "Run `akm workflow list --active` to see runs.",
|
|
36
38
|
FILE_NOT_FOUND: "Check the path exists and is readable.",
|
|
37
39
|
};
|
|
@@ -97,7 +99,7 @@ export class NotFoundError extends AkmError {
|
|
|
97
99
|
* Test-isolation guard helper.
|
|
98
100
|
*
|
|
99
101
|
* `src/core/paths.ts` throws `ConfigError("TEST_ISOLATION_MISSING")` under
|
|
100
|
-
* `bun test` when `
|
|
102
|
+
* `bun test` when `AKM_BUNDLE_DIR` is set without a paired data-dir or
|
|
101
103
|
* state-dir override. That throw must never be swallowed by best-effort
|
|
102
104
|
* catches around DB/data-dir operations — otherwise the guard's loud failure
|
|
103
105
|
* silently degrades into a "no result" outcome (cold cache, missing snapshot,
|
package/dist/core/events.js
CHANGED
|
@@ -1,52 +1,19 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
/**
|
|
5
|
-
* Append-only events stream — backed by state.db (#204, Phase 3).
|
|
6
|
-
*
|
|
7
|
-
* Every mutating CLI verb funnels through `appendEvent` so external
|
|
8
|
-
* observers (sync, replication, audit, dashboards) can react to stash
|
|
9
|
-
* changes. Events are stored in the `events` table in `state.db`
|
|
10
|
-
* (SQLite, WAL mode) instead of a flat `events.jsonl` file.
|
|
11
|
-
*
|
|
12
|
-
* The helper is the only thing in akm that writes to the events table. It
|
|
13
|
-
* accepts an injectable `dbPath` (via `EventsContext`) so tests can pin a
|
|
14
|
-
* tmpdir without any global mutation.
|
|
15
|
-
*
|
|
16
|
-
* Format (each EventEnvelope):
|
|
17
|
-
* { "schemaVersion": 1, "id": <number>, "ts": "<ISO>",
|
|
18
|
-
* "eventType": "<verb>", "ref"?: "<asset-ref>", ... }
|
|
19
|
-
*
|
|
20
|
-
* - `id` is a monotonic SQLite AUTOINCREMENT rowid. Callers can persist it
|
|
21
|
-
* as a durable cursor for `--since` resumption (replaces the old byte-offset
|
|
22
|
-
* cursor). The public API still surfaces this as `nextOffset` (an opaque
|
|
23
|
-
* number) for backward compatibility with callers that stored byte-offset
|
|
24
|
-
* cursors.
|
|
25
|
-
* - `ts` is ISO-8601 (UTC, millisecond precision).
|
|
26
|
-
*/
|
|
27
|
-
import path from "node:path";
|
|
28
4
|
import { insertEvent, readStateEvents } from "../storage/repositories/events-repository.js";
|
|
29
5
|
import { rethrowIfTestIsolationError } from "./errors.js";
|
|
30
|
-
import {
|
|
31
|
-
import { openStateDatabase, withStateDb } from "./state-db.js";
|
|
6
|
+
import { getStateDbPath, openStateDatabase, withStateDb } from "./state-db.js";
|
|
32
7
|
import { error } from "./warn.js";
|
|
33
|
-
/**
|
|
34
|
-
* Legacy events.jsonl path — used only by the migration script
|
|
35
|
-
* (`scripts/migrate-storage.ts`) to import existing event history into
|
|
36
|
-
* state.db. No events are written here by akm v0.9+.
|
|
37
|
-
*/
|
|
38
|
-
export function getEventsPath() {
|
|
39
|
-
return path.join(getDataDir(), "events.jsonl");
|
|
40
|
-
}
|
|
41
8
|
/**
|
|
42
9
|
* Resolve the state.db path from context:
|
|
43
10
|
* 1. `ctx.dbPath` — explicit override (test seam)
|
|
44
|
-
* 2. default —
|
|
11
|
+
* 2. default — the canonical state.db path
|
|
45
12
|
*/
|
|
46
13
|
function resolveDbPath(ctx) {
|
|
47
14
|
if (ctx?.dbPath)
|
|
48
15
|
return ctx.dbPath;
|
|
49
|
-
return
|
|
16
|
+
return getStateDbPath();
|
|
50
17
|
}
|
|
51
18
|
function resolveNow(ctx) {
|
|
52
19
|
return ctx?.now ?? Date.now;
|
|
@@ -100,6 +67,17 @@ export function appendEvent(input, ctx) {
|
|
|
100
67
|
error(`akm: appendEvent failed: ${String(err)}`);
|
|
101
68
|
}
|
|
102
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* 0.9.0 breaking change (owner ruling 12): `akm sync` used to persist
|
|
72
|
+
* `eventType: "save"`; it now writes `"sync"` instead (matching the command
|
|
73
|
+
* name). Existing `state.db` rows — and any user script running
|
|
74
|
+
* `akm log --type save` — still carry the old spelling. Rather than
|
|
75
|
+
* rewriting historical rows (a migration users never asked for, on data we
|
|
76
|
+
* don't get to touch at rest), reads treat the two names as synonyms: asking
|
|
77
|
+
* for either "save" or "sync" returns rows written under both names. Only
|
|
78
|
+
* the WRITE path (sources-cli.ts's `runSyncBody`) changed.
|
|
79
|
+
*/
|
|
80
|
+
const SAVE_SYNC_EVENT_TYPE_ALIASES = new Set(["save", "sync"]);
|
|
103
81
|
/**
|
|
104
82
|
* Read all events matching the filter. Returns a `nextOffset` that callers
|
|
105
83
|
* can persist between processes for monotonic resumption.
|
|
@@ -117,14 +95,37 @@ export function readEvents(options = {}, ctx) {
|
|
|
117
95
|
return { events: [], nextOffset: 0 };
|
|
118
96
|
}
|
|
119
97
|
try {
|
|
98
|
+
// A "save"/"sync" query can't be expressed as a single SQL `event_type =
|
|
99
|
+
// ?` match (see SAVE_SYNC_EVENT_TYPE_ALIASES above), so widen the SQL
|
|
100
|
+
// filter to "no type filter" for that one case and apply the alias match
|
|
101
|
+
// client-side alongside the existing tag post-filter below.
|
|
102
|
+
const typeIsAliased = options.type !== undefined && SAVE_SYNC_EVENT_TYPE_ALIASES.has(options.type);
|
|
103
|
+
// D-38: a JS-side post-filter (the type alias above, or the tag filters
|
|
104
|
+
// below) runs AFTER the SQL read, so a SQL-level LIMIT applied before it
|
|
105
|
+
// could drop rows the post-filter would have kept out anyway, silently
|
|
106
|
+
// returning fewer than `limit` (or the wrong — oldest-in-the-SQL-window —
|
|
107
|
+
// events). Only push `limit` into SQL (readStateEvents) when nothing
|
|
108
|
+
// downstream can shrink the result further; otherwise read unbounded (the
|
|
109
|
+
// pre-existing behavior) and apply `limit` ourselves, below, AFTER the
|
|
110
|
+
// post-filter runs.
|
|
111
|
+
const needsPostFilter = typeIsAliased ||
|
|
112
|
+
(options.excludeTags?.length ?? 0) > 0 ||
|
|
113
|
+
(options.includeTags?.length ?? 0) > 0 ||
|
|
114
|
+
options.runId !== undefined;
|
|
115
|
+
const pushLimitToSql = options.limit !== undefined && !needsPostFilter;
|
|
120
116
|
const { events: rawEvents, nextId } = readStateEvents(db, {
|
|
121
117
|
sinceId: options.sinceOffset,
|
|
122
118
|
since: options.since,
|
|
123
|
-
type: options.type,
|
|
119
|
+
type: typeIsAliased ? undefined : options.type,
|
|
124
120
|
ref: options.ref,
|
|
121
|
+
...(pushLimitToSql ? { limit: options.limit } : {}),
|
|
125
122
|
});
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
const filtered = rawEvents.filter((envelope) => {
|
|
124
|
+
if (typeIsAliased && !SAVE_SYNC_EVENT_TYPE_ALIASES.has(envelope.eventType))
|
|
125
|
+
return false;
|
|
126
|
+
if (options.runId !== undefined && envelope.metadata?.runId !== options.runId)
|
|
127
|
+
return false;
|
|
128
|
+
// Apply tag filters after the indexed state.db read.
|
|
128
129
|
const tags = envelope.metadata?.tags ?? [];
|
|
129
130
|
if (options.excludeTags?.some((t) => tags.includes(t)))
|
|
130
131
|
return false;
|
|
@@ -132,98 +133,15 @@ export function readEvents(options = {}, ctx) {
|
|
|
132
133
|
return false;
|
|
133
134
|
return true;
|
|
134
135
|
});
|
|
136
|
+
// `filtered` is ascending by id; slicing off the end keeps the MOST
|
|
137
|
+
// RECENT `limit` events post-filter, matching the SQL-pushdown path's
|
|
138
|
+
// semantics exactly. `nextOffset` intentionally stays `nextId` — the
|
|
139
|
+
// durable resume cursor tracks the underlying SQL read, not this
|
|
140
|
+
// display-only truncation.
|
|
141
|
+
const events = options.limit !== undefined && !pushLimitToSql ? filtered.slice(-options.limit) : filtered;
|
|
135
142
|
return { events, nextOffset: nextId };
|
|
136
143
|
}
|
|
137
144
|
finally {
|
|
138
145
|
db.close();
|
|
139
146
|
}
|
|
140
147
|
}
|
|
141
|
-
/**
|
|
142
|
-
* Follow the events table in state.db. Polls at `intervalMs` (default 75ms)
|
|
143
|
-
* and emits every new event to `onEvent`. Resolves when `signal` aborts, when
|
|
144
|
-
* `maxEvents` events have been observed, or when `maxDurationMs` elapses.
|
|
145
|
-
*
|
|
146
|
-
* The polling cursor is a monotonic SQLite rowid so concurrent writers cannot
|
|
147
|
-
* cause skips: between two reads we always pick up everything inserted since
|
|
148
|
-
* the last `nextOffset`.
|
|
149
|
-
*/
|
|
150
|
-
export async function tailEvents(options = {}, ctx) {
|
|
151
|
-
const intervalMs = options.intervalMs ?? 75;
|
|
152
|
-
const collected = [];
|
|
153
|
-
let cursor = options.sinceOffset ?? 0;
|
|
154
|
-
// Seed the cursor: if the caller passed --since (timestamp) but no
|
|
155
|
-
// sinceOffset, do an initial filtered read so they see history before
|
|
156
|
-
// we start polling. This matches the documented behaviour of `tail
|
|
157
|
-
// --since`: emit existing events that match, then follow.
|
|
158
|
-
if (options.sinceOffset === undefined) {
|
|
159
|
-
const initial = readEvents({
|
|
160
|
-
since: options.since,
|
|
161
|
-
type: options.type,
|
|
162
|
-
ref: options.ref,
|
|
163
|
-
excludeTags: options.excludeTags,
|
|
164
|
-
includeTags: options.includeTags,
|
|
165
|
-
}, ctx);
|
|
166
|
-
for (const event of initial.events) {
|
|
167
|
-
collected.push(event);
|
|
168
|
-
options.onEvent?.(event);
|
|
169
|
-
if (options.maxEvents !== undefined && collected.length >= options.maxEvents) {
|
|
170
|
-
return { events: collected, nextOffset: initial.nextOffset, reason: "maxEvents" };
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
cursor = initial.nextOffset;
|
|
174
|
-
}
|
|
175
|
-
const startedAt = Date.now();
|
|
176
|
-
return new Promise((resolve) => {
|
|
177
|
-
let resolved = false;
|
|
178
|
-
let timer;
|
|
179
|
-
function finish(reason) {
|
|
180
|
-
if (resolved)
|
|
181
|
-
return;
|
|
182
|
-
resolved = true;
|
|
183
|
-
if (timer)
|
|
184
|
-
clearInterval(timer);
|
|
185
|
-
resolve({ events: collected, nextOffset: cursor, reason });
|
|
186
|
-
}
|
|
187
|
-
function tick() {
|
|
188
|
-
try {
|
|
189
|
-
const result = readEvents({
|
|
190
|
-
sinceOffset: cursor,
|
|
191
|
-
type: options.type,
|
|
192
|
-
ref: options.ref,
|
|
193
|
-
excludeTags: options.excludeTags,
|
|
194
|
-
includeTags: options.includeTags,
|
|
195
|
-
}, ctx);
|
|
196
|
-
cursor = result.nextOffset;
|
|
197
|
-
for (const event of result.events) {
|
|
198
|
-
// Apply --since filter inside the polling loop too — the cursor is
|
|
199
|
-
// rowid-based so it can hand us events the user filtered out.
|
|
200
|
-
if (options.since && event.ts && event.ts < options.since)
|
|
201
|
-
continue;
|
|
202
|
-
collected.push(event);
|
|
203
|
-
options.onEvent?.(event);
|
|
204
|
-
if (options.maxEvents !== undefined && collected.length >= options.maxEvents) {
|
|
205
|
-
finish("maxEvents");
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
catch {
|
|
211
|
-
// Non-fatal: stay in the loop.
|
|
212
|
-
}
|
|
213
|
-
if (options.maxDurationMs !== undefined && Date.now() - startedAt >= options.maxDurationMs) {
|
|
214
|
-
finish("maxDuration");
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
if (options.signal) {
|
|
218
|
-
if (options.signal.aborted) {
|
|
219
|
-
finish("signal");
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
options.signal.addEventListener("abort", () => finish("signal"), { once: true });
|
|
223
|
-
}
|
|
224
|
-
timer = setInterval(tick, intervalMs);
|
|
225
|
-
// Run one tick immediately so callers don't have to wait an interval
|
|
226
|
-
// for events written in the same tick as the tail starts.
|
|
227
|
-
tick();
|
|
228
|
-
});
|
|
229
|
-
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
export const EXTRA_PARAMS_PROTECTED_TOP_LEVEL_KEYS = [
|
|
5
|
+
"model",
|
|
6
|
+
"messages",
|
|
7
|
+
"temperature",
|
|
8
|
+
"maxtokens",
|
|
9
|
+
"responseformat",
|
|
10
|
+
"stream",
|
|
11
|
+
"streamoptions",
|
|
12
|
+
"enablethinking",
|
|
13
|
+
"chattemplatekwargs",
|
|
14
|
+
];
|
|
15
|
+
export const EXTRA_PARAMS_CREDENTIAL_KEYS = [
|
|
16
|
+
"authorization",
|
|
17
|
+
"headers",
|
|
18
|
+
"apikey",
|
|
19
|
+
"token",
|
|
20
|
+
"password",
|
|
21
|
+
"secret",
|
|
22
|
+
"cookie",
|
|
23
|
+
"setcookie",
|
|
24
|
+
];
|
|
25
|
+
const PROTECTED_TOP_LEVEL_KEYS = new Set(EXTRA_PARAMS_PROTECTED_TOP_LEVEL_KEYS);
|
|
26
|
+
const CREDENTIAL_KEYS = new Set(EXTRA_PARAMS_CREDENTIAL_KEYS);
|
|
27
|
+
export function normalizeExtraParamKey(key) {
|
|
28
|
+
return key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
29
|
+
}
|
|
30
|
+
/** Validate provider extras without allowing them to override AKM fields or carry credentials. */
|
|
31
|
+
export function validateExtraParams(value) {
|
|
32
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
33
|
+
return [{ path: [], message: "must be an object" }];
|
|
34
|
+
}
|
|
35
|
+
const issues = [];
|
|
36
|
+
const visit = (entry, path) => {
|
|
37
|
+
if (Array.isArray(entry)) {
|
|
38
|
+
entry.forEach((child, index) => {
|
|
39
|
+
visit(child, [...path, index]);
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (!entry || typeof entry !== "object")
|
|
44
|
+
return;
|
|
45
|
+
for (const [key, child] of Object.entries(entry)) {
|
|
46
|
+
const normalized = normalizeExtraParamKey(key);
|
|
47
|
+
if (path.length === 0 && PROTECTED_TOP_LEVEL_KEYS.has(normalized)) {
|
|
48
|
+
issues.push({ path: [key], message: `${key} is protected by AKM` });
|
|
49
|
+
}
|
|
50
|
+
if (CREDENTIAL_KEYS.has(normalized)) {
|
|
51
|
+
issues.push({ path: [...path, key], message: `${key} cannot carry credentials` });
|
|
52
|
+
}
|
|
53
|
+
visit(child, [...path, key]);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
visit(value, []);
|
|
57
|
+
return issues;
|
|
58
|
+
}
|
|
59
|
+
export function formatExtraParamsIssue(label, issue) {
|
|
60
|
+
const suffix = issue.path.map((part) => (typeof part === "number" ? `[${part}]` : `.${part}`)).join("");
|
|
61
|
+
return `${label}${suffix} ${issue.message}`;
|
|
62
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* The content of a proposal's PRIMARY change (WI-6.2 envelope read path).
|
|
6
|
+
*
|
|
7
|
+
* By construction `changes[0].after === payload.content`; consumers read
|
|
8
|
+
* through this accessor so the single-content assumption lives in ONE place
|
|
9
|
+
* once multi-file proposals (consolidate ops) ride the envelope. Typed
|
|
10
|
+
* structurally (not against `Proposal`) so this module stays dependency-free.
|
|
11
|
+
*/
|
|
12
|
+
export function proposalContent(p) {
|
|
13
|
+
const content = p.changes[0]?.after;
|
|
14
|
+
if (content === undefined)
|
|
15
|
+
throw new Error("Proposal primary change has no content.");
|
|
16
|
+
return content;
|
|
17
|
+
}
|