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
package/dist/core/common.js
CHANGED
|
@@ -4,29 +4,15 @@
|
|
|
4
4
|
import crypto from "node:crypto";
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
7
|
-
import { getAssetTypes, TYPE_DIRS } from "./asset/asset-spec.js";
|
|
8
7
|
import { ConfigError } from "./errors.js";
|
|
9
8
|
import { getConfigPath, getDefaultStashDir } from "./paths.js";
|
|
10
|
-
// ── Types ───────────────────────────────────────────────────────────────────
|
|
11
|
-
/**
|
|
12
|
-
* The canonical catalog of built-in asset types.
|
|
13
|
-
*
|
|
14
|
-
* SINGLE SOURCE OF TRUTH: derived from the {@link ASSET_SPECS} registry in
|
|
15
|
-
* `asset-spec.ts` rather than hand-maintained here. Before #490/WS7 this was a
|
|
16
|
-
* hand-written literal array that had DRIFTED from the registry (it omitted
|
|
17
|
-
* `task`, which the registry has always carried). Deriving from the registry
|
|
18
|
-
* kills that drift — see `tests/asset-type-union-source.test.ts` for the
|
|
19
|
-
* intentional-`task`-delta guard.
|
|
20
|
-
*
|
|
21
|
-
* Note: `AkmAssetType` stays a static literal union of the BUILT-IN types
|
|
22
|
-
* (those present at module-eval time). Dynamically `registerAssetType`-d types
|
|
23
|
-
* are accepted at runtime via {@link isAssetType} but are not part of the
|
|
24
|
-
* static union — identical to the pre-WS7 contract.
|
|
25
|
-
*/
|
|
26
|
-
export const ASSET_TYPES = Object.freeze([...getAssetTypes()]);
|
|
27
|
-
export const ASSET_TYPE_SET = new Set(ASSET_TYPES);
|
|
28
9
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
29
|
-
|
|
10
|
+
// Moved to the platform leaf so paths.ts can use it without a common↔paths
|
|
11
|
+
// cycle (chunk-8 WI-8.6, DoD 11); re-exported here for the existing surface.
|
|
12
|
+
export { IS_WINDOWS } from "./platform.js";
|
|
13
|
+
export const MAX_CONFIG_FILE_BYTES = 1024 * 1024;
|
|
14
|
+
export const MAX_LOCAL_METADATA_BYTES = 1024 * 1024;
|
|
15
|
+
export const MAX_LOCK_METADATA_BYTES = 64 * 1024;
|
|
30
16
|
export function isHttpUrl(value) {
|
|
31
17
|
return !!value && /^https?:\/\//.test(value);
|
|
32
18
|
}
|
|
@@ -44,20 +30,36 @@ export function isRemoteUrl(value) {
|
|
|
44
30
|
value.startsWith("ssh://") ||
|
|
45
31
|
value.startsWith("git://"));
|
|
46
32
|
}
|
|
47
|
-
// ── Validators ──────────────────────────────────────────────────────────────
|
|
48
|
-
/**
|
|
49
|
-
* Returns true if `type` is a known asset type — either a built-in from
|
|
50
|
-
* {@link ASSET_TYPES} or one dynamically registered via `registerAssetType`.
|
|
51
|
-
*
|
|
52
|
-
* The type guard narrows to `AkmAssetType` for all built-in types. Dynamic
|
|
53
|
-
* types (e.g. registered by plugins) are also accepted at runtime, but the
|
|
54
|
-
* type system treats them as `AkmAssetType` via assertion since they are not
|
|
55
|
-
* part of the static union.
|
|
56
|
-
*/
|
|
57
|
-
export function isAssetType(type) {
|
|
58
|
-
return Object.hasOwn(TYPE_DIRS, type);
|
|
59
|
-
}
|
|
60
33
|
// ── Utilities ───────────────────────────────────────────────────────────────
|
|
34
|
+
export function readTextFileDescriptorWithLimit(fd, maxBytes, label = "File", displayPath = "(open file)") {
|
|
35
|
+
const stat = fs.fstatSync(fd);
|
|
36
|
+
if (!stat.isFile())
|
|
37
|
+
throw new ConfigError(`${label} is not a regular file: ${displayPath}.`, "INVALID_CONFIG_FILE");
|
|
38
|
+
if (stat.size > maxBytes) {
|
|
39
|
+
throw new ConfigError(`${label} exceeds the ${maxBytes}-byte limit: ${displayPath}.`, "INVALID_CONFIG_FILE");
|
|
40
|
+
}
|
|
41
|
+
const buffer = Buffer.allocUnsafe(maxBytes + 1);
|
|
42
|
+
let total = 0;
|
|
43
|
+
while (total <= maxBytes) {
|
|
44
|
+
const bytesRead = fs.readSync(fd, buffer, total, maxBytes + 1 - total, null);
|
|
45
|
+
if (bytesRead === 0)
|
|
46
|
+
break;
|
|
47
|
+
total += bytesRead;
|
|
48
|
+
}
|
|
49
|
+
if (total > maxBytes) {
|
|
50
|
+
throw new ConfigError(`${label} exceeds the ${maxBytes}-byte limit: ${displayPath}.`, "INVALID_CONFIG_FILE");
|
|
51
|
+
}
|
|
52
|
+
return buffer.subarray(0, total).toString("utf8");
|
|
53
|
+
}
|
|
54
|
+
export function readTextFileWithLimit(filePath, maxBytes, label = "File") {
|
|
55
|
+
const fd = fs.openSync(filePath, "r");
|
|
56
|
+
try {
|
|
57
|
+
return readTextFileDescriptorWithLimit(fd, maxBytes, label, filePath);
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
fs.closeSync(fd);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
61
63
|
/**
|
|
62
64
|
* Write content to a file atomically via a temp file + rename.
|
|
63
65
|
* Prevents partial-write corruption on crash.
|
|
@@ -78,60 +80,105 @@ export function isAssetType(type) {
|
|
|
78
80
|
* 2. fsync the parent directory after rename, so the directory entry change
|
|
79
81
|
* is durable too. Some filesystems (FAT, certain FUSE mounts) don't
|
|
80
82
|
* support directory fsync; we ignore EINVAL/ENOTSUP so atomic writes
|
|
81
|
-
* don't fail on
|
|
83
|
+
* don't fail on those mounts. Windows does not support opening a
|
|
84
|
+
* directory for fsync, so the directory-sync step is skipped there.
|
|
82
85
|
*/
|
|
83
86
|
export function writeFileAtomic(target, content, mode) {
|
|
84
87
|
const tmp = `${target}.tmp.${process.pid}.${crypto.randomBytes(8).toString("hex")}`;
|
|
85
|
-
const
|
|
88
|
+
const data = typeof content === "string" ? Buffer.from(content) : content;
|
|
89
|
+
const fileMode = mode ?? 0o600;
|
|
90
|
+
let fd;
|
|
91
|
+
let tempOwned = false;
|
|
92
|
+
let renamed = false;
|
|
93
|
+
let failed = false;
|
|
94
|
+
let failure;
|
|
86
95
|
try {
|
|
87
|
-
fs.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
fd = fs.openSync(tmp, "wx", fileMode);
|
|
97
|
+
tempOwned = true;
|
|
98
|
+
if (process.platform !== "win32")
|
|
99
|
+
fs.fchmodSync(fd, fileMode);
|
|
100
|
+
let offset = 0;
|
|
101
|
+
while (offset < data.byteLength) {
|
|
102
|
+
const written = fs.writeSync(fd, data, offset, data.byteLength - offset);
|
|
103
|
+
if (written <= 0)
|
|
104
|
+
throw new Error(`Could not make progress writing atomic temp file ${tmp}.`);
|
|
105
|
+
offset += written;
|
|
95
106
|
}
|
|
107
|
+
fs.fdatasyncSync(fd);
|
|
108
|
+
fs.closeSync(fd);
|
|
109
|
+
fd = undefined;
|
|
110
|
+
fs.renameSync(tmp, target);
|
|
111
|
+
renamed = true;
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
failed = true;
|
|
115
|
+
failure = error;
|
|
96
116
|
}
|
|
97
117
|
finally {
|
|
98
|
-
|
|
118
|
+
if (fd !== undefined) {
|
|
119
|
+
try {
|
|
120
|
+
fs.closeSync(fd);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
if (!failed) {
|
|
124
|
+
failed = true;
|
|
125
|
+
failure = error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (tempOwned && !renamed) {
|
|
130
|
+
try {
|
|
131
|
+
fs.unlinkSync(tmp);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
if (!failed && !hasErrnoCode(error, "ENOENT")) {
|
|
135
|
+
failed = true;
|
|
136
|
+
failure = error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
99
140
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
141
|
+
if (failed)
|
|
142
|
+
throw failure;
|
|
143
|
+
if (process.platform !== "win32") {
|
|
144
|
+
let dirFd;
|
|
103
145
|
try {
|
|
104
|
-
fs.
|
|
146
|
+
dirFd = fs.openSync(path.dirname(target), "r");
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
if (hasErrnoCode(error, "EINVAL") || hasErrnoCode(error, "ENOTSUP"))
|
|
150
|
+
return;
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
try {
|
|
155
|
+
fs.fsyncSync(dirFd);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
if (!hasErrnoCode(error, "EINVAL") && !hasErrnoCode(error, "ENOTSUP"))
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
105
161
|
}
|
|
106
162
|
finally {
|
|
107
163
|
fs.closeSync(dirFd);
|
|
108
164
|
}
|
|
109
165
|
}
|
|
110
|
-
catch {
|
|
111
|
-
// Directory fsync is unsupported on FAT, some FUSE mounts, and Windows
|
|
112
|
-
// (where directories cannot be opened for read like POSIX). Silently
|
|
113
|
-
// ignore so writeFileAtomic remains portable.
|
|
114
|
-
}
|
|
115
166
|
}
|
|
116
167
|
/**
|
|
117
168
|
* Resolve the stash directory using a three-level fallback chain:
|
|
118
|
-
* 1.
|
|
119
|
-
* 2.
|
|
169
|
+
* 1. AKM_BUNDLE_DIR environment variable (override for CI/scripts)
|
|
170
|
+
* 2. The configured default bundle path
|
|
120
171
|
* 3. Platform default (~/akm or ~/Documents/akm on Windows)
|
|
121
172
|
*
|
|
122
|
-
* Pure read: never writes to disk. The legacy `readOnly` option is accepted
|
|
123
|
-
* (and ignored) for one release cycle so older callers continue to compile;
|
|
124
|
-
* it can be removed in the next minor bump.
|
|
125
|
-
*
|
|
126
173
|
* Throws if no valid stash directory is found.
|
|
127
174
|
*/
|
|
128
|
-
export function resolveStashDir(
|
|
175
|
+
export function resolveStashDir(env = process.env) {
|
|
129
176
|
// 1. Env var override (for CI, scripts, testing)
|
|
130
|
-
const envDir = env.
|
|
177
|
+
const envDir = env.AKM_BUNDLE_DIR?.trim();
|
|
131
178
|
if (envDir) {
|
|
132
179
|
return validateStashDir(envDir);
|
|
133
180
|
}
|
|
134
|
-
// 2.
|
|
181
|
+
// 2. Configured default bundle path
|
|
135
182
|
const configStashDir = readStashDirFromConfig();
|
|
136
183
|
if (configStashDir)
|
|
137
184
|
return validateStashDir(configStashDir);
|
|
@@ -140,8 +187,7 @@ export function resolveStashDir(_options, env = process.env) {
|
|
|
140
187
|
if (isValidDirectory(defaultDir)) {
|
|
141
188
|
return defaultDir;
|
|
142
189
|
}
|
|
143
|
-
throw new ConfigError(`No
|
|
144
|
-
`or set stashDir in ${getConfigPath()}.`, "STASH_DIR_NOT_FOUND");
|
|
190
|
+
throw new ConfigError(`No bundle directory found. Run "akm bundle create" to create one at ${defaultDir}.`, "STASH_DIR_NOT_FOUND");
|
|
145
191
|
}
|
|
146
192
|
function validateStashDir(raw) {
|
|
147
193
|
const stashDir = path.resolve(raw);
|
|
@@ -150,10 +196,10 @@ function validateStashDir(raw) {
|
|
|
150
196
|
stat = fs.statSync(stashDir);
|
|
151
197
|
}
|
|
152
198
|
catch {
|
|
153
|
-
throw new ConfigError(`Unable to read
|
|
199
|
+
throw new ConfigError(`Unable to read bundle directory at "${stashDir}".`, "STASH_DIR_UNREADABLE");
|
|
154
200
|
}
|
|
155
201
|
if (!stat.isDirectory()) {
|
|
156
|
-
throw new ConfigError(`
|
|
202
|
+
throw new ConfigError(`Bundle path must point to a directory: "${stashDir}".`, "STASH_DIR_NOT_A_DIRECTORY");
|
|
157
203
|
}
|
|
158
204
|
return stashDir;
|
|
159
205
|
}
|
|
@@ -166,20 +212,75 @@ function isValidDirectory(dir) {
|
|
|
166
212
|
}
|
|
167
213
|
}
|
|
168
214
|
/**
|
|
169
|
-
* Read
|
|
170
|
-
* module, to avoid circular dependencies.
|
|
215
|
+
* Read the primary stash path directly from config.json without pulling in the
|
|
216
|
+
* full config module, to avoid circular dependencies.
|
|
217
|
+
*
|
|
218
|
+
* Reads ONLY the 0.9.0 `bundles`/`defaultBundle` shape. A config still carrying
|
|
219
|
+
* the retired `stashDir`/`sources`/`installed` keys (with no usable bundles
|
|
220
|
+
* path) is an unmigrated config: this refuses it with the same `akm-migrate
|
|
221
|
+
* apply` hint the schema hard-reject uses (config-schema.ts), rather than
|
|
222
|
+
* silently honouring the retired key — so every `resolveStashDir` caller gets
|
|
223
|
+
* the coherent migrate posture instead of split-brain success.
|
|
171
224
|
*/
|
|
172
225
|
function readStashDirFromConfig() {
|
|
173
226
|
try {
|
|
174
227
|
const configPath = getConfigPath();
|
|
175
|
-
const text =
|
|
228
|
+
const text = readTextFileWithLimit(configPath, MAX_CONFIG_FILE_BYTES, "Config file");
|
|
176
229
|
const raw = JSON.parse(text);
|
|
177
|
-
if (typeof raw
|
|
178
|
-
return
|
|
230
|
+
if (typeof raw !== "object" || raw === null)
|
|
231
|
+
return undefined;
|
|
232
|
+
// 0.9.0 config-shape cutover (spec §10.1): the primary stash is the
|
|
233
|
+
// `defaultBundle`'s filesystem `path`. Read it directly (no config module
|
|
234
|
+
// import) so the primary-stash location survives the stashDir → bundles
|
|
235
|
+
// migration without a runtime rewire.
|
|
236
|
+
const bundles = raw.bundles;
|
|
237
|
+
const defaultBundle = raw.defaultBundle;
|
|
238
|
+
if (bundles &&
|
|
239
|
+
typeof bundles === "object" &&
|
|
240
|
+
typeof defaultBundle === "string" &&
|
|
241
|
+
bundles[defaultBundle] &&
|
|
242
|
+
typeof bundles[defaultBundle] === "object" &&
|
|
243
|
+
typeof bundles[defaultBundle].path === "string" &&
|
|
244
|
+
bundles[defaultBundle].path.trim()) {
|
|
245
|
+
const bundle = bundles[defaultBundle];
|
|
246
|
+
const bundlePath = bundle.path.trim();
|
|
247
|
+
if (bundle.components !== undefined) {
|
|
248
|
+
if (typeof bundle.components !== "object" || bundle.components === null) {
|
|
249
|
+
throw new ConfigError("A bundle components map must contain exactly one component.", "INVALID_CONFIG_FILE");
|
|
250
|
+
}
|
|
251
|
+
const components = Object.values(bundle.components);
|
|
252
|
+
if (components.length !== 1) {
|
|
253
|
+
throw new ConfigError("A bundle components map must contain exactly one component.", "INVALID_CONFIG_FILE");
|
|
254
|
+
}
|
|
255
|
+
const component = components[0];
|
|
256
|
+
if (typeof component === "object" && component !== null) {
|
|
257
|
+
const componentConfig = component;
|
|
258
|
+
if (typeof componentConfig.root !== "string")
|
|
259
|
+
return bundlePath;
|
|
260
|
+
const bundleRoot = path.resolve(bundlePath);
|
|
261
|
+
const componentRoot = path.resolve(bundleRoot, componentConfig.root);
|
|
262
|
+
if (!isWithin(componentRoot, bundleRoot)) {
|
|
263
|
+
throw new ConfigError(`Component root "${componentConfig.root}" escapes bundle "${defaultBundle}".`, "INVALID_CONFIG_FILE");
|
|
264
|
+
}
|
|
265
|
+
return componentRoot;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return bundlePath;
|
|
269
|
+
}
|
|
270
|
+
// Retired pre-cutover shape with no usable bundles path: refuse with the
|
|
271
|
+
// migrate hint (matches the schema's hard-reject, config-schema.ts) instead
|
|
272
|
+
// of silently resolving the old key.
|
|
273
|
+
for (const key of ["stashDir", "sources", "installed"]) {
|
|
274
|
+
if (key in raw && raw[key] !== undefined) {
|
|
275
|
+
throw new ConfigError(`${key} is the retired pre-cutover source shape; run \`akm-migrate apply\` to convert it to bundles`, "INVALID_CONFIG_FILE");
|
|
276
|
+
}
|
|
179
277
|
}
|
|
180
278
|
}
|
|
181
|
-
catch {
|
|
182
|
-
//
|
|
279
|
+
catch (err) {
|
|
280
|
+
// A retired-shape refusal must reach the caller; genuine missing/invalid
|
|
281
|
+
// config (read or JSON-parse failure) falls through to the platform default.
|
|
282
|
+
if (err instanceof ConfigError)
|
|
283
|
+
throw err;
|
|
183
284
|
}
|
|
184
285
|
return undefined;
|
|
185
286
|
}
|
|
@@ -240,14 +341,34 @@ function normalizeFsPathForComparison(value) {
|
|
|
240
341
|
/**
|
|
241
342
|
* Fetch with an AbortController timeout.
|
|
242
343
|
* Defaults to 30 seconds if no timeout is specified.
|
|
344
|
+
*
|
|
345
|
+
* SCOPE — connection + response-header phase only. `timeoutMs` bounds the time
|
|
346
|
+
* until `fetch` RESOLVES (i.e. until the status line + headers arrive); the
|
|
347
|
+
* timer is cleared once the `Response` is returned. It does NOT bound the time
|
|
348
|
+
* spent streaming the response BODY: a server can dribble body bytes forever
|
|
349
|
+
* under any per-byte limit. Callers that read the body MUST bound it
|
|
350
|
+
* themselves — pass `{ bodyTimeoutMs, signal }` to {@link readBodyWithByteCap}
|
|
351
|
+
* (in-memory reads) or use a capped streaming writer for downloads. That is the
|
|
352
|
+
* sanctioned "body-deadline mechanism"; a bounded header timeout here plus a
|
|
353
|
+
* bounded body read there gives a bounded TOTAL window.
|
|
354
|
+
*
|
|
355
|
+
* External `signal`: a caller-supplied `AbortSignal` aborts the in-flight
|
|
356
|
+
* request with the caller's own `reason`. The bridged listener is removed in
|
|
357
|
+
* `finally`, so once this function returns the caller's signal no longer
|
|
358
|
+
* governs the returned `Response`'s body stream — pass the SAME `signal` to the
|
|
359
|
+
* body-read helper so cancellation continues to apply to the body phase with
|
|
360
|
+
* the caller's reason. (A timeout and an external abort can never overwrite
|
|
361
|
+
* each other: whichever fires first aborts the controller, and a second
|
|
362
|
+
* `controller.abort()` is a no-op that preserves the first reason.)
|
|
243
363
|
*/
|
|
244
364
|
export async function fetchWithTimeout(url, opts, timeoutMs = 30_000, signal) {
|
|
245
365
|
const controller = new AbortController();
|
|
246
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
366
|
+
const timer = timeoutMs === null ? undefined : setTimeout(() => controller.abort(), timeoutMs);
|
|
247
367
|
const abortExternal = () => controller.abort(signal?.reason);
|
|
248
368
|
if (signal) {
|
|
249
369
|
if (signal.aborted) {
|
|
250
|
-
|
|
370
|
+
if (timer)
|
|
371
|
+
clearTimeout(timer);
|
|
251
372
|
controller.abort(signal.reason);
|
|
252
373
|
}
|
|
253
374
|
else {
|
|
@@ -269,7 +390,8 @@ export async function fetchWithTimeout(url, opts, timeoutMs = 30_000, signal) {
|
|
|
269
390
|
finally {
|
|
270
391
|
if (signal)
|
|
271
392
|
signal.removeEventListener("abort", abortExternal);
|
|
272
|
-
|
|
393
|
+
if (timer)
|
|
394
|
+
clearTimeout(timer);
|
|
273
395
|
}
|
|
274
396
|
}
|
|
275
397
|
/**
|
|
@@ -342,6 +464,69 @@ export class ResponseTooLargeError extends Error {
|
|
|
342
464
|
this.observedBytes = observedBytes;
|
|
343
465
|
}
|
|
344
466
|
}
|
|
467
|
+
/**
|
|
468
|
+
* Thrown by {@link readBodyWithByteCap} / {@link readChunkWithDeadline} (and the
|
|
469
|
+
* capped disk writer) when streaming a response body exceeds the caller's
|
|
470
|
+
* overall body-read deadline. Distinct from a connection/header timeout
|
|
471
|
+
* (`fetchWithTimeout`) — this is the body-phase bound.
|
|
472
|
+
*/
|
|
473
|
+
export class BodyReadTimeoutError extends Error {
|
|
474
|
+
url;
|
|
475
|
+
timeoutMs;
|
|
476
|
+
constructor(url, timeoutMs) {
|
|
477
|
+
super(`Response body read exceeded ${timeoutMs}ms: ${url}`);
|
|
478
|
+
this.name = "BodyReadTimeoutError";
|
|
479
|
+
this.url = url;
|
|
480
|
+
this.timeoutMs = timeoutMs;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function bodyAbortError(signal, url) {
|
|
484
|
+
const reason = signal?.reason;
|
|
485
|
+
return reason instanceof Error ? reason : new Error(`Response body read aborted: ${url}`);
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Read one chunk from `reader`, rejecting if the overall body deadline passes
|
|
489
|
+
* or `signal` aborts first. `deadlineAt` is an absolute epoch-ms instant (null
|
|
490
|
+
* = no deadline). Only races the pending `read()` so a stalled body cannot
|
|
491
|
+
* block forever; the CALLER cancels the reader on rejection. Throws
|
|
492
|
+
* {@link BodyReadTimeoutError} on deadline, or the signal's reason (or an
|
|
493
|
+
* AbortError) on external abort.
|
|
494
|
+
*/
|
|
495
|
+
export async function readChunkWithDeadline(reader, deadlineAt, signal, url, timeoutMs) {
|
|
496
|
+
if (signal?.aborted)
|
|
497
|
+
throw bodyAbortError(signal, url);
|
|
498
|
+
const remaining = deadlineAt === null ? null : deadlineAt - Date.now();
|
|
499
|
+
if (remaining !== null && remaining <= 0)
|
|
500
|
+
throw new BodyReadTimeoutError(url, timeoutMs);
|
|
501
|
+
if (remaining === null && !signal)
|
|
502
|
+
return reader.read();
|
|
503
|
+
return new Promise((resolve, reject) => {
|
|
504
|
+
let timer;
|
|
505
|
+
const cleanup = () => {
|
|
506
|
+
if (timer)
|
|
507
|
+
clearTimeout(timer);
|
|
508
|
+
signal?.removeEventListener("abort", onAbort);
|
|
509
|
+
};
|
|
510
|
+
const onAbort = () => {
|
|
511
|
+
cleanup();
|
|
512
|
+
reject(bodyAbortError(signal, url));
|
|
513
|
+
};
|
|
514
|
+
if (remaining !== null) {
|
|
515
|
+
timer = setTimeout(() => {
|
|
516
|
+
cleanup();
|
|
517
|
+
reject(new BodyReadTimeoutError(url, timeoutMs));
|
|
518
|
+
}, remaining);
|
|
519
|
+
}
|
|
520
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
521
|
+
reader.read().then((result) => {
|
|
522
|
+
cleanup();
|
|
523
|
+
resolve(result);
|
|
524
|
+
}, (err) => {
|
|
525
|
+
cleanup();
|
|
526
|
+
reject(err);
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
}
|
|
345
530
|
/**
|
|
346
531
|
* Read a Response body as a UTF-8 string with a byte-count cap.
|
|
347
532
|
*
|
|
@@ -350,9 +535,12 @@ export class ResponseTooLargeError extends Error {
|
|
|
350
535
|
* `Content-Length` larger than the cap, we refuse before reading any
|
|
351
536
|
* bytes. `response.body` is consumed and cancelled on cap breach.
|
|
352
537
|
*
|
|
353
|
-
* `maxBytes` defaults to {@link DEFAULT_RESPONSE_BYTE_CAP} (10 MB).
|
|
538
|
+
* `maxBytes` defaults to {@link DEFAULT_RESPONSE_BYTE_CAP} (10 MB). `limits`
|
|
539
|
+
* bounds the body PHASE (duration + caller abort) that `fetchWithTimeout` does
|
|
540
|
+
* not cover; pass the same `signal` you gave `fetchWithTimeout` so caller
|
|
541
|
+
* cancellation keeps applying while the body streams.
|
|
354
542
|
*/
|
|
355
|
-
export async function readBodyWithByteCap(response, maxBytes = DEFAULT_RESPONSE_BYTE_CAP) {
|
|
543
|
+
export async function readBodyWithByteCap(response, maxBytes = DEFAULT_RESPONSE_BYTE_CAP, limits) {
|
|
356
544
|
const url = response.url || "(unknown URL)";
|
|
357
545
|
const contentLengthHeader = response.headers.get("content-length");
|
|
358
546
|
if (contentLengthHeader) {
|
|
@@ -368,28 +556,35 @@ export async function readBodyWithByteCap(response, maxBytes = DEFAULT_RESPONSE_
|
|
|
368
556
|
// No streaming body available (e.g., some mock environments). Fall
|
|
369
557
|
// back to text() but still enforce the cap post-hoc.
|
|
370
558
|
const text = await response.text();
|
|
371
|
-
|
|
372
|
-
|
|
559
|
+
const byteLength = Buffer.byteLength(text, "utf8");
|
|
560
|
+
if (byteLength > maxBytes)
|
|
561
|
+
throw new ResponseTooLargeError(url, maxBytes, byteLength);
|
|
373
562
|
return text;
|
|
374
563
|
}
|
|
375
564
|
const reader = body.getReader();
|
|
376
565
|
const chunks = [];
|
|
377
566
|
let total = 0;
|
|
567
|
+
const bodyTimeoutMs = limits?.bodyTimeoutMs;
|
|
568
|
+
const deadlineAt = bodyTimeoutMs != null ? Date.now() + bodyTimeoutMs : null;
|
|
378
569
|
try {
|
|
379
570
|
while (true) {
|
|
380
|
-
const { done, value } = await reader
|
|
571
|
+
const { done, value } = await readChunkWithDeadline(reader, deadlineAt, limits?.signal, url, bodyTimeoutMs ?? 0);
|
|
381
572
|
if (done)
|
|
382
573
|
break;
|
|
383
574
|
if (!value)
|
|
384
575
|
continue;
|
|
385
576
|
total += value.byteLength;
|
|
386
|
-
if (total > maxBytes)
|
|
387
|
-
await reader.cancel().catch(() => undefined);
|
|
577
|
+
if (total > maxBytes)
|
|
388
578
|
throw new ResponseTooLargeError(url, maxBytes, total);
|
|
389
|
-
}
|
|
390
579
|
chunks.push(value);
|
|
391
580
|
}
|
|
392
581
|
}
|
|
582
|
+
catch (err) {
|
|
583
|
+
// Cancel the underlying stream on ANY failure (cap breach, body-read
|
|
584
|
+
// timeout, or caller abort) so the socket is released, not just on cap.
|
|
585
|
+
await reader.cancel().catch(() => undefined);
|
|
586
|
+
throw err;
|
|
587
|
+
}
|
|
393
588
|
finally {
|
|
394
589
|
reader.releaseLock?.();
|
|
395
590
|
}
|
|
@@ -410,8 +605,8 @@ export async function readBodyWithByteCap(response, maxBytes = DEFAULT_RESPONSE_
|
|
|
410
605
|
* around {@link readBodyWithByteCap}; prefer this for registry index
|
|
411
606
|
* fetches, GitHub API responses, and any other untrusted JSON source.
|
|
412
607
|
*/
|
|
413
|
-
export async function jsonWithByteCap(response, maxBytes = DEFAULT_RESPONSE_BYTE_CAP) {
|
|
414
|
-
const text = await readBodyWithByteCap(response, maxBytes);
|
|
608
|
+
export async function jsonWithByteCap(response, maxBytes = DEFAULT_RESPONSE_BYTE_CAP, limits) {
|
|
609
|
+
const text = await readBodyWithByteCap(response, maxBytes, limits);
|
|
415
610
|
return JSON.parse(text);
|
|
416
611
|
}
|
|
417
612
|
function parseRetryAfter(response) {
|