akm-cli 0.10.261012-beta → 0.10.261014-beta
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 +710 -3
- package/README.md +15 -0
- package/SECURITY.md +11 -10
- package/STABILITY.md +47 -51
- package/dist/assets/hints/cli-hints-full.md +21 -19
- package/dist/assets/hints/cli-hints-short.md +3 -4
- package/dist/assets/opencode/akm-plugin.js +177 -0
- package/dist/assets/stash-skeleton/README.md +1 -1
- package/dist/assets/tasks/core/improve.yml +1 -1
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +1 -1
- package/dist/assets/ui/config.js +27 -10
- package/dist/assets/workflows/workflow-template.md +2 -2
- package/dist/cli/clack.js +3 -1
- package/dist/cli/selection.js +33 -0
- package/dist/cli/shared.js +14 -1
- package/dist/cli/unknown-flags.js +6 -37
- package/dist/cli.js +69 -48
- package/dist/commands/agent/agent-dispatch.js +17 -25
- package/dist/commands/agent/contribute-cli.js +9 -8
- package/dist/commands/command/command-cli.js +4 -8
- package/dist/commands/command/command-execution.js +25 -39
- package/dist/commands/command/execution-source-loader.js +10 -14
- package/dist/commands/completions.js +1 -1
- package/dist/commands/config-cli.js +14 -5
- package/dist/commands/env/child-env.js +12 -14
- package/dist/commands/env/env-binding.js +47 -58
- package/dist/commands/env/env-cli.js +22 -24
- package/dist/commands/env/env.js +10 -58
- package/dist/commands/env/secret-cli.js +11 -29
- package/dist/commands/env/secret.js +3 -10
- package/dist/commands/health/checks.js +10 -849
- package/dist/commands/health/connections-and-harnesses-check.js +109 -0
- package/dist/commands/health/egress.js +3 -3
- package/dist/commands/health/html-report.js +9 -10
- package/dist/commands/health/plugin-staleness.js +18 -11
- package/dist/commands/health/report-view-model.js +16 -12
- package/dist/commands/health/scheduler-binary.js +1 -1
- package/dist/commands/health/semantic-search-check.js +85 -0
- package/dist/commands/health/type-directory-check.js +0 -3
- package/dist/commands/health.js +30 -42
- package/dist/commands/improve/consolidate/pair-pass.js +10 -25
- package/dist/commands/improve/consolidate.js +10 -15
- package/dist/commands/improve/distill.js +5 -17
- package/dist/commands/improve/eligibility.js +1 -3
- package/dist/commands/improve/execution.js +37 -40
- package/dist/commands/improve/extract-cli.js +6 -11
- package/dist/commands/improve/extract-prompt.js +2 -2
- package/dist/commands/improve/extract.js +67 -97
- package/dist/commands/improve/improve-cli.js +22 -128
- package/dist/commands/improve/improve-report.js +1 -1
- package/dist/commands/improve/improve-strategies.js +25 -108
- package/dist/commands/improve/improve.js +10 -14
- package/dist/commands/improve/loop-stages.js +3 -0
- package/dist/commands/improve/memory/memory-improve.js +1 -1
- package/dist/commands/improve/preparation.js +8 -13
- package/dist/commands/improve/reflect.js +203 -332
- package/dist/commands/improve/retrieval-scope.js +1 -1
- package/dist/commands/improve/session-asset.js +3 -2
- package/dist/commands/improve/stage.js +87 -115
- package/dist/commands/lint/base-linter.js +3 -3
- package/dist/commands/lint/env-key-rules.js +7 -6
- package/dist/commands/lint/index.js +3 -3
- package/dist/commands/models-cli.js +103 -63
- package/dist/commands/proposal/drain.js +26 -39
- package/dist/commands/proposal/proposal-cli.js +14 -18
- package/dist/commands/proposal/propose-cli.js +4 -2
- package/dist/commands/proposal/propose.js +37 -64
- package/dist/commands/proposal/repository.js +17 -17
- package/dist/commands/proposal/validators/proposal-validators.js +3 -6
- package/dist/commands/read/curate.js +10 -6
- package/dist/commands/read/registry-search.js +2 -48
- package/dist/commands/read/search-cli.js +1 -8
- package/dist/commands/read/search.js +5 -6
- package/dist/commands/read/show.js +6 -29
- package/dist/commands/remember.js +25 -40
- package/dist/commands/sources/add-cli.js +8 -63
- package/dist/commands/sources/bundle-cli.js +8 -9
- package/dist/commands/sources/bundle-config-ops.js +2 -2
- package/dist/commands/sources/info.js +12 -1
- package/dist/commands/sources/init.js +1 -1
- package/dist/commands/sources/installed-stashes.js +119 -63
- package/dist/commands/sources/migration-help.js +1 -1
- package/dist/commands/sources/plugin-upgrade.js +8 -15
- package/dist/commands/sources/schema-repair.js +4 -10
- package/dist/commands/sources/source-add.js +2 -2
- package/dist/commands/sources/source-clone.js +31 -7
- package/dist/commands/sources/source-manage.js +4 -4
- package/dist/commands/targets.js +175 -0
- package/dist/commands/tasks/explain.js +104 -69
- package/dist/commands/tasks/tasks-cli.js +9 -11
- package/dist/commands/tasks/tasks.js +75 -97
- package/dist/commands/ui/api.js +11 -3
- package/dist/commands/ui/redact.js +5 -31
- package/dist/commands/ui/workspace-actions.js +1 -1
- package/dist/commands/workflow/plan.js +48 -9
- package/dist/commands/workflow-cli.js +40 -45
- package/dist/core/adapter/adapters/agent-skills-adapter.js +10 -11
- package/dist/core/adapter/adapters/akm-adapter.js +36 -41
- package/dist/core/adapter/adapters/akm-lint.js +10 -12
- package/dist/core/adapter/adapters/akm-metadata.js +2 -2
- package/dist/core/adapter/adapters/akm-task-adapter.js +8 -9
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +5 -5
- package/dist/core/adapter/adapters/claude-adapter.js +1 -1
- package/dist/core/adapter/adapters/dotenv-adapter.js +7 -6
- package/dist/core/adapter/adapters/generic-files-adapter.js +5 -5
- package/dist/core/adapter/adapters/index.js +3 -4
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +12 -14
- package/dist/core/adapter/adapters/okf-adapter.js +8 -10
- package/dist/core/adapter/adapters/opencode-adapter.js +1 -1
- package/dist/core/adapter/adapters/shared.js +3 -3
- package/dist/core/adapter/adapters/tool-dir-shared.js +7 -25
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +3 -3
- package/dist/core/adapter/detect-adapter.js +1 -1
- package/dist/core/adapter/execution-source.js +13 -35
- package/dist/core/adapter/registry.js +1 -1
- package/dist/core/adapter/validate-context.js +2 -4
- package/dist/core/asset/asset-placement.js +1 -1
- package/dist/core/asset/asset-ref.js +3 -3
- package/dist/core/asset/resolve-ref.js +5 -0
- package/dist/core/bundle-rename.js +12 -4
- package/dist/core/closest-match.js +37 -0
- package/dist/core/common.js +37 -57
- package/dist/core/config/config-io.js +26 -18
- package/dist/core/config/config-schema.js +19 -106
- package/dist/core/config/config-sources.js +3 -3
- package/dist/core/config/config-types.js +4 -4
- package/dist/core/config/config-walker.js +50 -77
- package/dist/core/config/config.js +438 -109
- package/dist/core/config/convert-engine-references.js +114 -0
- package/dist/core/config/convert-engines-shared.js +40 -0
- package/dist/core/config/convert-engines.js +300 -0
- package/dist/core/config/convert-models-overlay.js +137 -0
- package/dist/core/config/credentials.js +304 -0
- package/dist/core/config/engine-semantics.js +3 -3
- package/dist/core/config/migrate-keys.js +222 -0
- package/dist/core/config/model-aliases.js +37 -0
- package/dist/core/config/models-file.js +34 -0
- package/dist/core/config/retired-engines.js +53 -0
- package/dist/core/config/schema/connections.js +41 -0
- package/dist/core/config/schema/embedding.js +2 -2
- package/dist/core/config/schema/harnesses.js +41 -0
- package/dist/core/config/schema/improve-processes.js +9 -28
- package/dist/core/config/schema/improve.js +36 -1
- package/dist/core/config/schema/index-config.js +18 -20
- package/dist/core/config/schema/primitives.js +42 -17
- package/dist/core/config/schema/search.js +6 -11
- package/dist/core/config/schema/sources-bundles.js +9 -9
- package/dist/core/config/schema/workflow.js +2 -6
- package/dist/core/config/unusable-entries.js +66 -0
- package/dist/core/content-safety.js +3 -6
- package/dist/core/endpoint-url.js +47 -0
- package/dist/core/env-secret-ref.js +84 -73
- package/dist/core/errors.js +10 -16
- package/dist/core/extra-params.js +3 -3
- package/dist/core/improve-result.js +8 -9
- package/dist/core/json-schema.js +1 -1
- package/dist/core/lesson-lint.js +1 -1
- package/dist/core/llm-inference.js +80 -0
- package/dist/core/parse.js +2 -2
- package/dist/core/paths.js +33 -10
- package/dist/core/recognition-util.js +8 -10
- package/dist/core/redaction.js +94 -55
- package/dist/core/ref-token.js +82 -0
- package/dist/core/secret-value.js +14 -0
- package/dist/core/state/migrations.js +17 -22
- package/dist/core/structured.js +2 -68
- package/dist/core/subprocess.js +63 -22
- package/dist/core/type-presentation.js +10 -14
- package/dist/core/write-source.js +20 -4
- package/dist/execution/input-contract.js +3 -6
- package/dist/execution/notices.js +49 -0
- package/dist/execution/resolved-request.js +24 -40
- package/dist/execution/source.js +21 -45
- package/dist/execution/target-ref.js +2 -2
- package/dist/indexer/bundle-identity-guard.js +6 -5
- package/dist/indexer/ensure-index.js +1 -1
- package/dist/indexer/index-writer-lock.js +1 -1
- package/dist/indexer/indexer.js +46 -42
- package/dist/indexer/installations.js +12 -22
- package/dist/indexer/lookup/adapter-concept-owner.js +40 -14
- package/dist/indexer/materialize-embeddings.js +20 -16
- package/dist/indexer/passes/memory-inference.js +21 -50
- package/dist/indexer/passes/metadata.js +5 -5
- package/dist/indexer/scan/doc-to-entry.js +1 -1
- package/dist/indexer/scan/drain-dir.js +9 -11
- package/dist/indexer/search/db-search.js +9 -7
- package/dist/indexer/search/search-source.js +42 -22
- package/dist/indexer/usage/usage-events.js +2 -3
- package/dist/indexer/walk/index-context.js +2 -1
- package/dist/indexer/walk/matchers.js +3 -11
- package/dist/indexer/walk/path-resolver.js +13 -68
- package/dist/indexer/walk/walker.js +32 -26
- package/dist/integrations/agent/akm-read-tools.js +81 -0
- package/dist/integrations/agent/config.js +1 -1
- package/dist/integrations/agent/detect.js +32 -36
- package/dist/integrations/agent/execution.js +130 -428
- package/dist/integrations/agent/index.js +1 -1
- package/dist/integrations/agent/profiles.js +24 -28
- package/dist/integrations/agent/prompts.js +17 -30
- package/dist/integrations/agent/request-lowering.js +67 -101
- package/dist/integrations/agent/resolution.js +332 -0
- package/dist/integrations/agent/runner-dispatch.js +156 -181
- package/dist/integrations/agent/runner.js +20 -70
- package/dist/integrations/agent/runtime-port.js +4 -0
- package/dist/integrations/agent/runtime.js +10 -0
- package/dist/integrations/agent/spawn.js +65 -190
- package/dist/integrations/harnesses/aider/index.js +19 -39
- package/dist/integrations/harnesses/amazonq/index.js +17 -32
- package/dist/integrations/harnesses/claude/index.js +59 -28
- package/dist/integrations/harnesses/claude/session-log.js +7 -5
- package/dist/integrations/harnesses/codex/index.js +19 -31
- package/dist/integrations/harnesses/copilot/index.js +17 -35
- package/dist/integrations/harnesses/gemini/index.js +18 -34
- package/dist/integrations/harnesses/harness.js +36 -0
- package/dist/integrations/harnesses/index.js +20 -51
- package/dist/integrations/harnesses/opencode/agent-builder.js +132 -100
- package/dist/integrations/harnesses/opencode/index.js +11 -33
- package/dist/integrations/harnesses/opencode/roots.js +50 -0
- package/dist/integrations/harnesses/opencode/runtime/binary.js +206 -0
- package/dist/integrations/harnesses/opencode/runtime/command.js +152 -0
- package/dist/integrations/harnesses/opencode/runtime/index.js +177 -0
- package/dist/integrations/harnesses/opencode/runtime/tar.js +107 -0
- package/dist/integrations/harnesses/opencode/session-log-v1.js +15 -7
- package/dist/integrations/harnesses/opencode/session-log-v2.js +72 -48
- package/dist/integrations/harnesses/opencode/session-log.js +58 -137
- package/dist/integrations/harnesses/opencode/version.js +34 -53
- package/dist/integrations/harnesses/openhands/index.js +13 -37
- package/dist/integrations/harnesses/pi/index.js +21 -32
- package/dist/integrations/lockfile.js +1 -1
- package/dist/integrations/session-logs/index.js +11 -31
- package/dist/llm/client.js +93 -111
- package/dist/llm/embedder.js +19 -34
- package/dist/llm/embedders/connection.js +31 -0
- package/dist/llm/embedders/local-model-files.js +56 -0
- package/dist/llm/embedders/local.js +146 -115
- package/dist/llm/embedders/remote.js +26 -23
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/index-passes.js +33 -39
- package/dist/llm/memory-infer.js +67 -77
- package/dist/llm/model-list.js +35 -0
- package/dist/llm/rerank-client.js +8 -14
- package/dist/llm/structured-call.js +45 -99
- package/dist/llm/structured.js +60 -0
- package/dist/output/renderers.js +2 -8
- package/dist/output/shapes/helpers.js +18 -44
- package/dist/output/shapes/passthrough.js +1 -0
- package/dist/output/shapes.js +1 -7
- package/dist/output/text/command-format.js +41 -39
- package/dist/output/text/health-format.js +8 -2
- package/dist/output/text/helpers.js +1 -1
- package/dist/output/text/migrate.js +12 -0
- package/dist/output/text/models.js +3 -2
- package/dist/output/text/registry-commands.js +1 -2
- package/dist/output/text/show-directives.js +16 -28
- package/dist/output/text/show-format.js +0 -2
- package/dist/output/text/targets-table.js +70 -0
- package/dist/output/text/workflow-format.js +44 -15
- package/dist/output/text.js +1 -1
- package/dist/registry/providers/skills-sh.js +1 -28
- package/dist/registry/providers/static-index.js +1 -71
- package/dist/runtime.js +6 -2
- package/dist/scripts/akm-migrate-node.js +47095 -44905
- package/dist/scripts/akm-migrate.js +46946 -44755
- package/dist/setup/detect.js +28 -273
- package/dist/setup/endpoint.js +36 -0
- package/dist/setup/semantic-assets.js +13 -19
- package/dist/setup/setup-flags.js +77 -0
- package/dist/setup/setup-plan.js +144 -0
- package/dist/setup/setup.js +134 -358
- package/dist/setup/steps/connection-shared.js +49 -101
- package/dist/setup/steps/connection.js +121 -524
- package/dist/setup/steps/embeddings.js +151 -0
- package/dist/setup/steps/harnesses.js +118 -0
- package/dist/setup/steps/platforms.js +2 -81
- package/dist/setup/steps/tasks.js +1 -2
- package/dist/setup/steps.js +10 -0
- package/dist/setup/summary.js +67 -0
- package/dist/sources/freshness.js +16 -21
- package/dist/sources/providers/git-provider.js +2 -9
- package/dist/sources/providers/provider-utils.js +13 -5
- package/dist/sources/resolve.js +12 -16
- package/dist/sources/snapshot-fetchers/robots.js +11 -12
- package/dist/sources/snapshot-fetchers/secret-seam.js +2 -4
- package/dist/sources/snapshot-fetchers/website-ingest.js +8 -15
- package/dist/sources/snapshot-fetchers/x.js +1 -1
- package/dist/standalone/onnxruntime-library.js +52 -0
- package/dist/storage/repositories/improve-ledger-repository.js +1 -1
- package/dist/storage/repositories/improve-runs-repository.js +0 -13
- package/dist/storage/repositories/index-entries-repository.js +2 -2
- package/dist/storage/repositories/proposals-repository.js +2 -2
- package/dist/storage/repositories/task-history-repository.js +2 -2
- package/dist/storage/repositories/workflow-runs-repository.js +44 -129
- package/dist/storage/sqlite-read-snapshot.js +33 -29
- package/dist/tasks/embedded.js +1 -1
- package/dist/tasks/log-redaction.js +7 -119
- package/dist/tasks/prepare/prepare-support.js +8 -7
- package/dist/tasks/prepare/prepare.js +8 -9
- package/dist/tasks/prepare/script-capture.js +9 -25
- package/dist/tasks/run/attempt-lifecycle.js +17 -35
- package/dist/tasks/run/load-task.js +54 -27
- package/dist/tasks/run/run-command-task.js +15 -10
- package/dist/tasks/run/run-native-task.js +8 -21
- package/dist/tasks/run/run-task.js +5 -8
- package/dist/tasks/run/run-workflow-task.js +14 -21
- package/dist/tasks/run/task-history.js +9 -17
- package/dist/tasks/run/task-log.js +1 -5
- package/dist/tasks/scheduler-invocation.js +13 -18
- package/dist/tasks/scheduler-sync.js +0 -1
- package/dist/tasks/source/bounded-document.js +31 -117
- package/dist/tasks/source/parse-task-source.js +0 -1
- package/dist/tasks/source/project-v4.js +5 -3
- package/dist/tasks/source/task-input-diagnostics.js +3 -4
- package/dist/tasks/source/task-source-v4.js +48 -72
- package/dist/tasks/source-v3.js +2 -5
- package/dist/tasks/task-run-reserved-flags.js +1 -2
- package/dist/workflows/authoring/authoring.js +2 -7
- package/dist/workflows/compile.js +1 -15
- package/dist/workflows/concurrency-policy.js +4 -4
- package/dist/workflows/exec/child-workflow.js +1 -4
- package/dist/workflows/exec/dispatch-redaction.js +35 -86
- package/dist/workflows/exec/environment.js +13 -77
- package/dist/workflows/exec/exec-unit.js +14 -55
- package/dist/workflows/exec/frozen-judge.js +26 -28
- package/dist/workflows/exec/native-executor.js +126 -71
- package/dist/workflows/exec/run-workflow.js +38 -42
- package/dist/workflows/exec/step-work.js +13 -17
- package/dist/workflows/exec/unit-dispatch.js +7 -21
- package/dist/workflows/freeze/environment.js +14 -119
- package/dist/workflows/freeze/freeze.js +14 -33
- package/dist/workflows/freeze/step-values.js +6 -13
- package/dist/workflows/freeze/targets/child-workflow.js +3 -5
- package/dist/workflows/freeze/targets/command.js +9 -15
- package/dist/workflows/freeze/targets/shell.js +0 -3
- package/dist/workflows/freeze/task-bindings.js +0 -1
- package/dist/workflows/parser.js +50 -59
- package/dist/workflows/plan.js +8 -2
- package/dist/workflows/renderer.js +83 -9
- package/dist/workflows/resource-limits.js +2 -2
- package/dist/workflows/runtime/run-plan.js +38 -23
- package/dist/workflows/runtime/runs.js +55 -93
- package/dist/workflows/runtime/workflow-asset-loader.js +55 -86
- package/dist/workflows/source-files.js +7 -19
- package/docs/README.md +10 -16
- package/docs/architecture/README.md +2 -37
- package/docs/architecture/adapters.md +2 -2
- package/docs/architecture/akm-core-principles.md +256 -118
- package/docs/architecture/architecture.md +283 -99
- package/docs/architecture/improvement.md +8 -2
- package/docs/architecture/internals/classification.md +1 -3
- package/docs/architecture/internals/fresh-host-rebuild-runbook.md +7 -6
- package/docs/architecture/internals/health-advisories.md +35 -74
- package/docs/architecture/internals/improve-workflow.md +27 -26
- package/docs/architecture/internals/indexing.md +3 -3
- package/docs/architecture/internals/storage-locations.md +48 -15
- package/docs/architecture/persisted-data-compat.md +10 -6
- package/docs/architecture/specs/okf-support.md +9 -11
- package/docs/architecture/specs/ref.md +7 -9
- package/docs/architecture/testing/testing-workflow.md +172 -52
- package/docs/architecture/workflow-engine.md +51 -34
- package/docs/guides/README.md +0 -1
- package/docs/guides/author-bundles.md +15 -20
- package/docs/guides/author-workflows.md +93 -71
- package/docs/guides/bundles.md +20 -16
- package/docs/guides/discover-and-load.md +1 -1
- package/docs/guides/getting-started.md +49 -3
- package/docs/guides/improve-the-library.md +10 -4
- package/docs/guides/recipes/headless-install.md +11 -16
- package/docs/guides/run-workflows.md +29 -20
- package/docs/guides/scheduling.md +2 -2
- package/docs/guides/use-with-any-agent.md +20 -22
- package/docs/integration/bundling-akm.md +19 -9
- package/docs/maintainers/README.md +2 -6
- package/docs/maintainers/release-checklist.md +73 -126
- package/docs/migration/README.md +10 -3
- package/docs/migration/release-notes/0.10.0.md +81 -16
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.9.0.md +1 -1
- package/docs/migration/release-notes/README.md +1 -1
- package/docs/migration/v0.9-to-v0.10.md +300 -60
- package/docs/migration/v0.9.1-to-v0.9.2.md +6 -5
- package/docs/posts/config-v2-lessons-16.md +1 -1
- package/docs/posts/llm-mode-reflect-13.md +1 -1
- package/docs/posts/release-0.6.0.md +11 -11
- package/docs/posts/release-0.8.0.md +4 -4
- package/docs/posts/workflows-vaults-09.md +1 -1
- package/docs/reference/README.md +1 -1
- package/docs/reference/asset-types.md +2 -2
- package/docs/reference/cli.md +477 -388
- package/docs/reference/configuration.md +569 -550
- package/docs/reference/data-and-telemetry.md +9 -6
- package/docs/reference/env-and-secrets.md +72 -15
- package/docs/reference/refs.md +3 -20
- package/docs/reference/registry.md +12 -38
- package/docs/reference/supported-formats.md +1 -1
- package/docs/reference/tasks.md +22 -11
- package/docs/reference/ui-api.md +14 -9
- package/docs/reference/workflow-schema.md +199 -154
- package/docs/reference/workflows.md +4 -6
- package/package.json +9 -11
- package/schemas/akm-config.json +1696 -2922
- package/schemas/akm-task.json +4 -4
- package/schemas/akm-workflow.json +36 -9
- package/ROADMAP.md +0 -112
- package/dist/assets/models.json +0 -35
- package/dist/commands/health/engine-usage.js +0 -45
- package/dist/commands/sources/dangerous-env-audit.js +0 -134
- package/dist/core/activation-policy.js +0 -67
- package/dist/core/config/schema/engines.js +0 -160
- package/dist/core/config/schema/execution.js +0 -23
- package/dist/core/spawn-env.js +0 -268
- package/dist/execution/directory-identity.js +0 -78
- package/dist/integrations/agent/builder-shared.js +0 -33
- package/dist/integrations/agent/builders.js +0 -43
- package/dist/integrations/agent/engine-fallback.js +0 -59
- package/dist/integrations/agent/engine-resolution.js +0 -333
- package/dist/integrations/agent/model-map.js +0 -475
- package/dist/integrations/harnesses/aider/agent-builder.js +0 -107
- package/dist/integrations/harnesses/aider/result-extractor.js +0 -53
- package/dist/integrations/harnesses/amazonq/agent-builder.js +0 -138
- package/dist/integrations/harnesses/amazonq/result-extractor.js +0 -48
- package/dist/integrations/harnesses/claude/agent-builder.js +0 -106
- package/dist/integrations/harnesses/claude/config-import.js +0 -67
- package/dist/integrations/harnesses/claude/result-extractor.js +0 -67
- package/dist/integrations/harnesses/codex/agent-builder.js +0 -150
- package/dist/integrations/harnesses/codex/result-extractor.js +0 -110
- package/dist/integrations/harnesses/copilot/agent-builder.js +0 -117
- package/dist/integrations/harnesses/copilot/result-extractor.js +0 -178
- package/dist/integrations/harnesses/gemini/agent-builder.js +0 -114
- package/dist/integrations/harnesses/gemini/result-extractor.js +0 -149
- package/dist/integrations/harnesses/opencode/config-import.js +0 -149
- package/dist/integrations/harnesses/opencode/model-config.js +0 -33
- package/dist/integrations/harnesses/opencode/model-work-agent.js +0 -115
- package/dist/integrations/harnesses/opencode/session-log-types.js +0 -12
- package/dist/integrations/harnesses/opencode-sdk/index.js +0 -44
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +0 -1052
- package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +0 -199
- package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +0 -254
- package/dist/integrations/harnesses/opencode-sdk/wire.js +0 -66
- package/dist/integrations/harnesses/openhands/agent-builder.js +0 -120
- package/dist/integrations/harnesses/openhands/result-extractor.js +0 -135
- package/dist/integrations/harnesses/pi/agent-builder.js +0 -89
- package/dist/integrations/harnesses/pi/result-extractor.js +0 -168
- package/dist/integrations/harnesses/shared.js +0 -18
- package/dist/integrations/harnesses/types.js +0 -55
- package/dist/llm/feature-gate.js +0 -115
- package/dist/output/shapes/models-list.js +0 -16
- package/dist/output/shapes/registry-search.js +0 -10
- package/dist/setup/detected-engines.js +0 -136
- package/dist/setup/engine-config.js +0 -97
- package/dist/setup/harness-config-import.js +0 -47
- package/dist/setup/providers.js +0 -14
- package/dist/setup/steps/semantic.js +0 -39
- package/dist/workflows/authoring/scope-key.js +0 -79
- package/dist/workflows/exec/lowering-notices.js +0 -23
- package/dist/workflows/source-semantics.js +0 -110
- package/docs/architecture/0.10-process-rules.md +0 -832
- package/docs/architecture/akm-architecture-decision-history.md +0 -2055
- package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +0 -550
- package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +0 -72
- package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +0 -142
- package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +0 -189
- package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +0 -97
- package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +0 -108
- package/docs/architecture/decisions/0006-task-source-version-routing.md +0 -124
- package/docs/architecture/decisions/0007-workflow-composition-bounds.md +0 -83
- package/docs/architecture/decisions/0008-task-binding-normalization.md +0 -88
- package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +0 -80
- package/docs/architecture/decisions/0010-driver-protocol-cut.md +0 -36
- package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +0 -125
- package/docs/architecture/decisions/README.md +0 -62
- package/docs/architecture/internals/functional-contract-patterns.md +0 -340
- package/docs/architecture/internals/registry-network-boundary.md +0 -57
- package/docs/architecture/internals/search.md +0 -248
- package/docs/architecture/reviews/env-secret-access.md +0 -319
- package/docs/architecture/runtime-boundary-design.md +0 -153
- package/docs/architecture/specs/0.9.0-close-out-plan.md +0 -361
- package/docs/architecture/specs/0.9.0-decisions.md +0 -532
- package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +0 -745
- package/docs/architecture/specs/0.9.0-open-items-register.md +0 -135
- package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +0 -740
- package/docs/architecture/specs/0.9.0-release-surface-review.md +0 -636
- package/docs/architecture/specs/agent-command-engine-model-design.md +0 -513
- package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +0 -501
- package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +0 -291
- package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +0 -2059
- package/docs/architecture/specs/di-seams-plan.md +0 -539
- package/docs/architecture/specs/driver-protocol-keep-or-cut.md +0 -275
- package/docs/architecture/specs/fact-asset-type.md +0 -142
- package/docs/architecture/specs/improve-collapse-churn-detector-design.md +0 -634
- package/docs/architecture/specs/reviews/task-workflow-unification/README.md +0 -72
- package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +0 -152
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +0 -352
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +0 -300
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +0 -402
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +0 -758
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +0 -645
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +0 -602
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +0 -27
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +0 -27
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +0 -74
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +0 -15
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +0 -132
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +0 -147
- package/docs/architecture/specs/stash-conventions-code-spec.md +0 -275
- package/docs/architecture/specs/stash-organization-conventions.md +0 -377
- package/docs/architecture/specs/task-workflow-format-unification.md +0 -721
- package/docs/architecture/specs/workflow-engine-buy-vs-build.md +0 -231
- package/docs/architecture/specs/workflow-format-unification.md +0 -432
- package/docs/architecture/testing/manual-testing-checklist.md +0 -3411
- package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +0 -924
- package/docs/architecture/testing/task-workflow-manual-tests.md +0 -1720
- package/docs/guides/claude-code-vs-akm-workflows.md +0 -29
- package/docs/guides/knowledge-management.md +0 -18
- package/docs/maintainers/curate-workmap.md +0 -335
- package/docs/migration/v0.5-to-v0.6.md +0 -759
- package/docs/migration/v0.7-to-v0.8.md +0 -1378
- package/docs/plans/0.9.2-agent-command-workflow-plan.md +0 -423
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
# Architecture Review: Runtime secret resolution for in-process source fetchers
|
|
2
|
-
|
|
3
|
-
**Repo:** `/home/user/akm` (read-only review)
|
|
4
|
-
**Subject:** How the X snapshot fetcher (`src/sources/snapshot-fetchers/x.ts`) — and, more generally, the provider `sync()` / bundle-update path — should read a bearer token from akm's secret store at runtime, given a ref like `secrets/x-bearer-token`, without (a) leaking the value into agent/LLM/log context or (b) forming a static or dynamic import cycle.
|
|
5
|
-
**Date:** 2026-08-02
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 1. Executive summary
|
|
10
|
-
|
|
11
|
-
The root cause is a **Dependency Inversion violation**: the secret-store reader (`src/core/env-secret-ref.ts`) transitively imports the entire source-provider → fetcher-registry → fetcher subgraph (via `indexer/search/search-source.ts`), so any module inside that subgraph that reaches back to the store closes an import cycle the repo's ratchet forbids. The already-shipped fix is the right shape — a `resolveSecret?: (ref) => string | null` capability injected onto `FetcherContext` from callers positioned *above* the cycle (`src/sources/snapshot-fetchers/types.ts:43`) — but it is only wired in from the two command-layer entry points, leaving the provider-registry `sync()` / bundle-update path (`src/sources/providers/website.ts:26-32`) environment-variable-only. The recommended solution is to **formalize that existing seam as a first-class `SecretResolver` capability and thread it through the `sync()` path the same way it is already threaded through the `fetch()` path** — construct it once in `secret-seam.ts` (the sole sanctioned `env-secret-ref` importer, already outside the cycle) and pass it as data through `ensureSourceCaches → provider.sync → ensureWebsiteMirror`. This extends the shipped idiom rather than inventing a parallel one, adds no `env-secret-ref` import inside the cycle, and preserves the `secret run` value-containment discipline (same-frame header set, never returned upward, swallow-to-null).
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## 2. Current architecture
|
|
16
|
-
|
|
17
|
-
### 2.1 How a ref resolves to a value
|
|
18
|
-
|
|
19
|
-
A "ref" (`secrets/x-bearer-token`, `env/FOO`) is resolved to an **absolute path** — never to bytes — inside `src/core/env-secret-ref.ts`:
|
|
20
|
-
|
|
21
|
-
1. `parseSecretRef` / `parseEnvRef` parse the ref into an `AssetRef` (`{type, name, origin}`); bare names auto-qualify to `env/` or `secrets/`, and legacy `vault:` / colon spellings are rejected loudly, not translated. (`src/core/env-secret-ref.ts:75-153`)
|
|
22
|
-
2. `findEnvSource` locates which configured bundle source holds the ref by `fs.existsSync` probing candidate source paths, using `resolveSourceEntries(undefined, loadConfig())` + `resolveSourcesForOrigin`. (`src/core/env-secret-ref.ts:85-106`)
|
|
23
|
-
3. `resolveSecretPath` / `resolveEnvPath` compute the absolute path via `assetPathForName` + an `isWithin` traversal guard and return `{name, absPath, source}` only — they never open the file. (`src/core/env-secret-ref.ts:121-145,168-195`)
|
|
24
|
-
|
|
25
|
-
The **only** place raw secret bytes are read is `readValue` (`src/commands/env/secret.ts:102-108`), an `fs.readFileSync` wrapper with a documented no-log / no-stdout contract; `loadEnv` (`src/commands/env/env.ts:89-100`) is the analogous whole-file reader for `.env`.
|
|
26
|
-
|
|
27
|
-
For fetchers specifically, the read path is `resolveSecretFromStore(ref)` in `src/sources/snapshot-fetchers/secret-seam.ts:23-31`: it calls `resolveSecretPath`, `fs.existsSync`, `fs.readFileSync(...).trim()`, and returns `null` for *any* failure — the underlying error (which can embed filesystem paths) is deliberately swallowed.
|
|
28
|
-
|
|
29
|
-
### 2.2 How values are kept out of context
|
|
30
|
-
|
|
31
|
-
akm keeps secret/env values out of agent/LLM/log surfaces with two layers:
|
|
32
|
-
|
|
33
|
-
- **Structural boundary.** The only sanctioned "use the value" path is `secret run`, which reads the file and injects it as `$VAR` into a `spawnSync` child's env — the read string never appears in a return value, in `output()`, or in the audit event (`src/commands/env/secret-cli.ts:213-231`; the `secret_access` event records ref + var name only, "never the value", `:221-226`). `env run` does the identical thing for whole `.env` files (`src/commands/env/env-cli.ts:280-301`). `env export` is the one path that writes a raw value, to a mode-`0600` file, never stdout (`src/commands/env/env-cli.ts:211-237`). `format-exempt.ts:53-67` formally classifies these as non-envelope commands so `--format` can't leak a value.
|
|
34
|
-
- **Defense-in-depth scrubber.** `src/core/redaction.ts` provides `collectSensitiveValues` / `redactSensitiveText` / `redactSensitiveValue` (`:193-398`) plus a pattern-based `redactCredentialPatterns` (`:307-341`, catches `Bearer` tokens, `sk-`/`key-` keys) invoked at every real output boundary — LLM error bodies (`src/llm/client.ts`), workflow reports (`src/workflows/exec/report.ts`), agent dispatch (`src/integrations/agent/runner-dispatch.ts`), proposal/improve output.
|
|
35
|
-
|
|
36
|
-
Today `x.ts` obeys the structural rule: `resolveXBearerToken` (`src/sources/snapshot-fetchers/x.ts:79-87`) returns the token to `fetch()`, which passes it straight into an `Authorization: Bearer` header inside `xApiJson`'s call frame (`x.ts:111-118`) and never onto a `FetcherContext` field, a `WikiSnapshotResult`, or an event.
|
|
37
|
-
|
|
38
|
-
### 2.3 The import cycle
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
src/core/env-secret-ref.ts
|
|
42
|
-
│ import { resolveSourceEntries } (env-secret-ref.ts:18)
|
|
43
|
-
▼
|
|
44
|
-
src/indexer/search/search-source.ts
|
|
45
|
-
│ import { resolveSourceProviderFactory } (search-source.ts:13)
|
|
46
|
-
│ import "../../sources/providers/index" (search-source.ts:16, side-effect)
|
|
47
|
-
▼
|
|
48
|
-
src/sources/providers/index.ts
|
|
49
|
-
│ imports ./website (and ./filesystem, ./git-provider, ./npm)
|
|
50
|
-
▼
|
|
51
|
-
src/sources/providers/website.ts
|
|
52
|
-
│ import { ensureWebsiteMirror } from ../snapshot-fetchers/website-ingest
|
|
53
|
-
▼
|
|
54
|
-
src/sources/snapshot-fetchers/website-ingest.ts
|
|
55
|
-
│ import { loadWikiSnapshotFetchers } from ./registry
|
|
56
|
-
▼
|
|
57
|
-
src/sources/snapshot-fetchers/registry.ts ──▶ x.ts, rss.ts, … (the fetchers)
|
|
58
|
-
|
|
59
|
-
╭──────────────────── THE FORBIDDEN BACK-EDGE ────────────────────╮
|
|
60
|
-
│ If x.ts (or website.ts) imports core/env-secret-ref — directly, │
|
|
61
|
-
│ or via secret-seam.ts, or via a dynamic import() — the arrow │
|
|
62
|
-
│ closes back to the top and the cycle-ratchet rejects it. │
|
|
63
|
-
╰──────────────────────────────────────────────────────────────────╯
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
`origin-resolve.ts` (`src/registry/origin-resolve.ts:5-9`) and `mutation-target.ts` (`src/core/mutation-target.ts:5-11`), both imported by `env-secret-ref.ts`, also reach `search-source.ts` — they reinforce the same vector rather than adding a second one. The pure helpers `env-secret-ref` imports (`config/config.ts`, `asset/*`) carry no provider/fetcher edges and are not part of the cycle.
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## 3. Why it's hard
|
|
71
|
-
|
|
72
|
-
### 3.1 The precise SOLID violation
|
|
73
|
-
|
|
74
|
-
`core/env-secret-ref.ts` is a low-level detail (how akm reads a secret file) that has been made to **depend on** a high-level policy module graph (the whole source-resolution / provider / fetcher subsystem) through the single edge at `env-secret-ref.ts:18`. A fetcher (`x.ts`) is a high-level consumer whose policy is "authorize this outbound request." Because the concrete store sits *upstream* of the fetcher in the import graph, the fetcher cannot depend on the concrete store without a cycle. This is a textbook **DIP** situation: the fix must make both the consumer and the store depend on an **abstraction** (a leaf type), with the concrete store **injected** from a composition root above the cycle.
|
|
75
|
-
|
|
76
|
-
### 3.2 Why each naive fix fails
|
|
77
|
-
|
|
78
|
-
| Attempt | Why it fails |
|
|
79
|
-
|---|---|
|
|
80
|
-
| **Static import** of `env-secret-ref` from `x.ts` | Closes the back-edge directly. Ratchet rejects. |
|
|
81
|
-
| **Lazy `import()`** of `env-secret-ref` (or `secret-seam`) from inside the cycle | Same cycle; the ratchet treats dynamic `import()` as **"cycle-laundering"** and rejects it too. (`x.ts:73-74` documents this.) |
|
|
82
|
-
| **Relocated reader module** | Moving the reader still leaves a module *inside* the subgraph importing something that transitively reaches `env-secret-ref` — the *importer edge* is what closes the cycle, so relocation-without-severing keeps the back-edge. |
|
|
83
|
-
| **Shipped `FetcherContext.resolveSecret` seam** (`types.ts:43`) | Architecturally correct — injected, not imported. But populated **only** by the two command-layer callers (`src/commands/read/knowledge.ts:145`, `src/commands/sources/source-add.ts:173`) via `resolveSecretFromStore`. |
|
|
84
|
-
|
|
85
|
-
### 3.3 The `sync()` gap
|
|
86
|
-
|
|
87
|
-
The provider-registry refresh path never populates the seam. `website.ts`'s registered factory closure calls `ensureWebsiteMirror(config, { requireStashDir, force, ...allowPrivateHosts })` with **no `resolveSecret` key** (`src/sources/providers/website.ts:26-32`), and structurally *cannot* import one: `website.ts` is itself inside the cycle. The full downstream plumbing already exists — `ensureWebsiteMirror` → `scrapeWebsiteToStash` → `fetchSnapshotViaRegistry` → `FetcherContext.resolveSecret` all thread an optional `resolveSecret` (`website-ingest.ts:194-233,273-290,374-385`) — the *only* missing piece is that nothing inside the providers/registry subgraph can construct or receive a real resolver to hand in as that option's value. `ensureSourceCaches` calls `provider.sync({ force })` with no secret parameter (`search-source.ts:383`), and its sole materializing caller `indexer.ts:688-689` passes only `{ force, materialize }`. So the bundle-update path is `X_BEARER_TOKEN`-env-only.
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
## 4. Options considered
|
|
92
|
-
|
|
93
|
-
Four proposals were developed and adversarially critiqued. All four break the cycle correctly against the real edge list; they differ on idiom-fit, blast radius, and risk. Critic ranking: **P4 > P2 > P3 > P1**.
|
|
94
|
-
|
|
95
|
-
### 4.1 Proposal 1 — Secret-resolver runtime-registration port (service locator)
|
|
96
|
-
|
|
97
|
-
A zero-import leaf `src/core/secret-resolver-port.ts` holds a single registrable `SecretResolver` slot; a registration module (importing `secret-seam`) self-registers it, side-effect-imported from `cli.ts`; in-cycle consumers call `resolveRegisteredSecret(ref)`.
|
|
98
|
-
|
|
99
|
-
- **SOLID:** DIP inversion is real; SRP clean. But it introduces a **global mutable singleton / service locator** — an ambient dependency invisible in signatures — which is *not* a house idiom (akm uses context-object injection, not service location). Creates **two parallel seams** for one capability, which the subsystem map explicitly warned against.
|
|
100
|
-
- **Critic verdict: VIABLE, score 5 (lowest).** The "mirrors `registerSourceProvider`" framing is misleading: `registerSourceProvider` is safe because `search-source.ts` *eagerly* side-effect-imports `providers/index`, guaranteeing registration before any `sync()`. P1's registration module imports `env-secret-ref`, so `search-source` **cannot** import it — registration hangs off `cli.ts` alone with **no load-order guarantee**. Any entry that reaches `ensureSourceCaches` without going through `cli.ts` (tests, library embedding, a second binary, a workflow executor importing `search-source` directly) silently degrades to env-var-only. Requires a test-reset hook to avoid cross-test leakage.
|
|
101
|
-
|
|
102
|
-
### 4.2 Proposal 2 — `CredentialProvider` capability (opaque, replaces the field)
|
|
103
|
-
|
|
104
|
-
A pure-type leaf `src/sources/credential-provider.ts` defines `CredentialProvider { has(ref): boolean; authorizeBearer(ref, headers): void }` and `SourceProviderContext { credentials? }`. `FetcherContext.resolveSecret` is **replaced** by `credentials?`; `SourceProviderFactory` gains a 2nd `context?` arg; the value is injected from above.
|
|
105
|
-
|
|
106
|
-
- **SOLID:** Strongest value-containment of the four — `authorizeBearer` returns `void`, `has` returns `boolean`, so **there is no value to return upward**; the token never enters consumer scope as a standalone variable. Clean ISP (two focused methods) and DIP. **But** it deliberately violates the map's "extend not replace / reuse the field name" guidance: it renames the shipped field and changes factory arity, forcing lockstep migration of `x.ts`, `website-ingest` plumbing, two command sites, and all test doubles.
|
|
107
|
-
- **Critic verdict: VIABLE, score 7.** Compiles; cycle-break is real (leaf type is a graph sink). Principled long-term target *if* a breaking contract change is ever acceptable — today the replace-the-field churn is unjustified when P4 reaches the same consumers additively.
|
|
108
|
-
|
|
109
|
-
### 4.3 Proposal 3 — Leaf secret-read module with a provider-free enumerator
|
|
110
|
-
|
|
111
|
-
Split `env-secret-ref.ts` into a WRITE module and a new **leaf** `src/core/env-secret-read.ts` whose source enumeration (`secretSourceRoots`) is rebuilt from `bundlesToSourceEntries` + `lockContentRootFor` + `resolveStashDir` instead of `resolveSourceEntries` — **deleting** the `env-secret-ref → search-source` edge entirely.
|
|
112
|
-
|
|
113
|
-
- **SOLID:** Best SRP (separates read-resolution from write/mutation) and the only proposal that *removes* the load-bearing edge rather than routing a value around it. Verified the leaf's dependency set (`config/config`, `integrations/lockfile`, `core/common`, `asset-placement`, `resolve-ref`) has zero path to `search-source` or the providers subgraph. This is genuinely *not* the failed relocated-reader attempt, because it deletes the `resolveSourceEntries` call rather than moving it.
|
|
114
|
-
- **Critic verdict: VIABLE, score 6.** The decisive risk is an **OCP smell / second source-of-truth**: `secretSourceRoots` reimplements a slice of `resolveSourceEntries`, drops `deriveInstallations` origin matching (registryId-equality only), drops the `provider.path()` fallback for legacy/unlocked git-npm bundles, and skips website caches — so it can silently resolve a **different file** than the CLI would. That is exactly the resolver-mismatch class that got `secret path` / `secret remove` amputated (R-027 / D-49). Only safe if `secretSourceRoots` delegates to a shared lock-first helper also used by `resolveEntryContentDir`. Keep as a **future** structural cleanup, not the primary fix.
|
|
115
|
-
|
|
116
|
-
### 4.4 Proposal 4 — Threaded `SecretResolver` capability (recommended)
|
|
117
|
-
|
|
118
|
-
Promote the ad-hoc `resolveSecret?:(ref)=>string|null` into a first-class `SecretResolver` object, constructed once in `secret-seam.ts` (the sole sanctioned `env-secret-ref` importer, already outside the cycle) and threaded as an explicit **call-time parameter** through the `sync()` path (`ensureSourceCaches → provider.sync options → website.ts`) exactly as it is already threaded through the `fetch()` path. Closes the gap with **no registry, no dynamic import, no reader relocation, no new global**, and **extends** the shipped seam rather than replacing it.
|
|
119
|
-
|
|
120
|
-
- **SOLID:** SRP — the capability has one responsibility (`ref → value | null`); `secret-seam.ts` stays the single place that knows about `env-secret-ref`. OCP — a future provider kind (private git remote, authed npm registry) reads `options.secrets` in its own `sync()` with zero contract change. LSP — every `SecretResolver` substitute (store-backed, env-only stub, fixed-token fake) is interchangeable; `secrets?` stays optional (absent = documented env-var-only). ISP — single-method interface. DIP — this is the crux and the mechanism: in-cycle modules (`search-source`, `website.ts`, `x.ts`) depend on the `SecretResolver` **abstraction** (a leaf type in `provider.ts`), and the concrete binding is injected from composition roots above the cycle.
|
|
121
|
-
- **Critic verdict: RECOMMEND, score 8 (highest).** Compiles, cycle-break is real: `SecretResolver` goes in `src/sources/provider.ts`, a verified graph **sink** (it imports only type-only `config/config` and `sources/types`), so `search-source`/`website` importing a type creates no runtime back-edge; the concrete `storeSecretResolver` lives in `secret-seam.ts` (imported by nothing in the subgraph) and reaches `website.ts` as **plain data** on the sync options object handed down from `indexer.ts` (confirmed above the cycle). Value containment matches the `secret-cli.ts` bar.
|
|
122
|
-
- **One real weakness:** the `secrets` option is optional and *not* compiler-enforced, so a future call site that forgets to forward it silently degrades to env-var-only — the same failure class that produced today's gap, closed here but not eliminated for future callers. (Mitigated by the hybrid grafts below.)
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## 5. Recommendation
|
|
127
|
-
|
|
128
|
-
**Adopt Proposal 4 (Threaded `SecretResolver` capability), with two cheap hardening grafts.** It is the minimal, additive, idiom-faithful fix: it extends the already-shipped `FetcherContext.resolveSecret` capability-injection seam down the `sync()` path instead of inventing a parallel mechanism, reaches both fetchers **and** provider `sync()`, and keeps the ratchet green because no `env-secret-ref` import lands inside the providers/registry/fetchers subgraph.
|
|
129
|
-
|
|
130
|
-
### 5.1 The abstraction (leaf type, a graph sink)
|
|
131
|
-
|
|
132
|
-
In `src/sources/provider.ts` (alongside `SourceProvider`; this module imports only type-only `config/config` and `sources/types`, so it stays a sink):
|
|
133
|
-
|
|
134
|
-
```ts
|
|
135
|
-
export interface SecretResolver {
|
|
136
|
-
/** Resolve a store ref → value or null. Never logs, never throws upward. */
|
|
137
|
-
resolveSecret(ref: string): string | null;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export interface SyncOptions {
|
|
141
|
-
force?: boolean;
|
|
142
|
-
secrets?: SecretResolver;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export interface SourceProvider {
|
|
146
|
-
readonly name: string;
|
|
147
|
-
readonly kind: SourceKind;
|
|
148
|
-
path(): string;
|
|
149
|
-
sync?(options?: SyncOptions): Promise<void>; // was: sync?(options?: { force?: boolean })
|
|
150
|
-
}
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
`SourceProviderFactory` is unchanged (`(config: SourceConfigEntry) => SourceProvider`) — no factory arity change, unlike P2.
|
|
154
|
-
|
|
155
|
-
### 5.2 The single composition root (already outside the cycle)
|
|
156
|
-
|
|
157
|
-
In `src/sources/snapshot-fetchers/secret-seam.ts` (already the ONLY module importing `core/env-secret-ref`, and imported by nothing in the providers/registry/fetchers subgraph — verified: only `commands/read/knowledge.ts` and `commands/sources/source-add.ts` import it today):
|
|
158
|
-
|
|
159
|
-
```ts
|
|
160
|
-
export const storeSecretResolver: SecretResolver = { resolveSecret: resolveSecretFromStore };
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
**Graft (a) — belt-and-suspenders containment (from P2/`llm/client.ts`):** have this resolver register any resolved token into `redactSensitiveText` / `redactSensitiveValue`'s `sensitiveValues` set (`src/core/redaction.ts:193-398`), so that if a request-header ever reaches a serialized error/log surface the token is scrubbed. The structural never-returned-upward guarantee holds independently; this covers the residual transient `headers.Authorization` exposure that all four proposals share.
|
|
164
|
-
|
|
165
|
-
### 5.3 Threading through the `sync()` path (data, not imports)
|
|
166
|
-
|
|
167
|
-
`src/indexer/search/search-source.ts` — import the **type only**:
|
|
168
|
-
|
|
169
|
-
```ts
|
|
170
|
-
export async function ensureSourceCaches(
|
|
171
|
-
config?: AkmConfig,
|
|
172
|
-
options?: { force?: boolean; materialize?: boolean; secrets?: SecretResolver },
|
|
173
|
-
): Promise<void> {
|
|
174
|
-
// …
|
|
175
|
-
await provider.sync({ force, secrets: options?.secrets }); // was: provider.sync({ force }) (search-source.ts:383)
|
|
176
|
-
}
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
`src/sources/providers/website.ts` sync() — the one missing line (imports the `SecretResolver` **type** only; the resolver arrives as data):
|
|
180
|
-
|
|
181
|
-
```ts
|
|
182
|
-
async sync(options?: SyncOptions) {
|
|
183
|
-
await ensureWebsiteMirror(config, {
|
|
184
|
-
requireStashDir: true,
|
|
185
|
-
force: options?.force,
|
|
186
|
-
resolveSecret: options?.secrets?.resolveSecret, // ← closes the gap; feeds the EXISTING plumbing
|
|
187
|
-
...(allowPrivateHosts ? { allowPrivateHosts: true } : {}),
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
`FetcherContext.resolveSecret` (`types.ts:43`) and the entire `website-ingest.ts` `resolveSecret` plumbing (`:194-233,273-290,374-385`) are **unchanged** — the capability's `.resolveSecret` method feeds the existing field.
|
|
193
|
-
|
|
194
|
-
### 5.4 Wiring at the entry surfaces (all verified above the cycle)
|
|
195
|
-
|
|
196
|
-
- `src/indexer/indexer.ts:688-689` (full-index / hydrate — the env-var-only path today): pass `secrets: storeSecretResolver` into the existing `ensureSourceCaches(config, { force: full, materialize: …, secrets: storeSecretResolver })`. This is the step that actually closes the `sync()` gap. (`indexer.ts` reaches `search-source` via dynamic `import(...)`, confirming it is above the cycle.)
|
|
197
|
-
- `src/commands/sources/source-add.ts:173` and `src/commands/read/knowledge.ts:145`: optionally normalize the inline `resolveSecret: resolveSecretFromStore` to `resolveSecret: storeSecretResolver.resolveSecret` — behavior-identical; leaving them untouched still compiles.
|
|
198
|
-
|
|
199
|
-
### 5.5 How it serves fetchers AND provider `sync()`
|
|
200
|
-
|
|
201
|
-
- **Fetchers:** `x.ts` continues to read `context.resolveSecret` via `resolveXBearerToken` (`x.ts:79-87`) — no change to the fetcher. The value now arrives on the `sync()` path too, so `secrets/x-bearer-token` resolves during bundle update, not just from command-layer URL ingest.
|
|
202
|
-
- **Provider `sync()`:** any provider kind that later needs credentials reads `options.secrets` in its own `sync()` — the seam is on the shared `SourceProvider`/`SyncOptions` contract, so it generalizes beyond `website.ts`'s previously-ad-hoc plumbing.
|
|
203
|
-
|
|
204
|
-
**Graft (b) — anti-regression lint:** add a boundary lint (precedent: `scripts/lint-runtime-boundary.ts`, `scripts/lint-write-source-chokepoint.ts`) asserting that `materialize=true` callers of `ensureSourceCaches` supply a `SecretResolver`, neutralizing P4's one weakness (the optional, non-compiler-enforced threading).
|
|
205
|
-
|
|
206
|
-
---
|
|
207
|
-
|
|
208
|
-
## 6. Migration plan (each step keeps the ratchet green; each is independently provable)
|
|
209
|
-
|
|
210
|
-
1. **Add the leaf abstraction.** Add `SecretResolver` + `SyncOptions` to `src/sources/provider.ts` and widen `sync?()` to `SyncOptions`. Type-only, no behavior change — all four provider kinds' `sync` bodies still typecheck.
|
|
211
|
-
*Proves:* `tsc`/build green; import-cycle ratchet green (no new runtime edge; `provider.ts` remains a sink).
|
|
212
|
-
2. **Add the composition root.** Add `export const storeSecretResolver` to `secret-seam.ts`. Include graft (a): register resolved tokens into `redactSensitiveText`'s `sensitiveValues`.
|
|
213
|
-
*Proves:* unit test that `storeSecretResolver.resolveSecret("secrets/x-bearer-token")` returns the file value for a fixture store and `null` on missing/unreadable; a redaction test asserting the token is scrubbed from a synthesized header-bearing error string.
|
|
214
|
-
3. **Thread the option through `ensureSourceCaches`.** Add `secrets?: SecretResolver` to its options and forward it into `provider.sync(...)` (`search-source.ts:383`). Type-only import of `SecretResolver`.
|
|
215
|
-
*Proves:* ratchet green (no `env-secret-ref` import added to the subgraph); existing `ensureSourceCaches` tests unchanged (option is optional).
|
|
216
|
-
4. **Populate `website.ts` sync().** Add `resolveSecret: options?.secrets?.resolveSecret` to the `ensureWebsiteMirror` call. Type-only import.
|
|
217
|
-
*Proves:* ratchet green; a website-provider `sync()` test with a stub `SecretResolver` asserts the stub is invoked with `secrets/x-bearer-token` (previously never called).
|
|
218
|
-
5. **Wire the entry surface.** Pass `secrets: storeSecretResolver` at `indexer.ts:688-689`.
|
|
219
|
-
*Proves:* integration test — run bundle update / full index on a website source whose X fetcher needs `secrets/x-bearer-token` with `X_BEARER_TOKEN` **unset**; assert the store value is used (previously env-only), AND assert the token never appears in `output()`, in `appendEvent`/`logs.db`, or in any structured surface.
|
|
220
|
-
6. **Normalize command call sites (optional, cosmetic).** Swap `source-add.ts:173` / `knowledge.ts:145` to `storeSecretResolver.resolveSecret`.
|
|
221
|
-
*Proves:* existing command-layer fetcher tests remain green (behavior-identical).
|
|
222
|
-
7. **Add the anti-regression lint (graft b).** Assert `materialize=true` callers of `ensureSourceCaches` pass a `SecretResolver`.
|
|
223
|
-
*Proves:* the lint fails on a deliberately-omitted call site and passes on the wired one.
|
|
224
|
-
|
|
225
|
-
Each step is additive and green in isolation, so the cycle ratchet never goes red and the change can land incrementally.
|
|
226
|
-
|
|
227
|
-
---
|
|
228
|
-
|
|
229
|
-
## 7. Risks and non-goals
|
|
230
|
-
|
|
231
|
-
**Risks**
|
|
232
|
-
|
|
233
|
-
- **Optional-threading regression (accepted, mitigated).** `secrets?` is optional and not compiler-enforced; a future `ensureSourceCaches`/`provider.sync` call site that forgets it silently reverts to env-var-only — the exact failure class being fixed, relocated to "did every caller pass it?". Graft (b)'s lint is the guard.
|
|
234
|
-
- **New value-materialization location.** The bundle-update / `sync()` path now reads a secret into memory where it previously did not. It stays confined to the `fetch()` call frame (`x.ts:111-118`), but it is a new location to audit against the never-in-context bar; graft (a) covers the residual serialized-header case.
|
|
235
|
-
- **Shared-contract widening.** `SourceProvider.sync`'s option type widens for a concern only `website` needs today. This is intentional future-proofing (OCP), and all existing `sync` bodies still typecheck, but it does touch a semi-public interface.
|
|
236
|
-
- **Underlying coupling remains.** P4 does **not** delete the `env-secret-ref → search-source` edge; it only guarantees the store stays on the outer side of it. The architectural smell (the store resolver transitively importing the whole source-resolution machinery) persists — see non-goals.
|
|
237
|
-
|
|
238
|
-
**Non-goals**
|
|
239
|
-
|
|
240
|
-
- **No new command surface / return path.** Do not add an `x-bearer-token path`-style command; the precedent (`secret path` / `secret remove` removal, R-027 / D-49) is that akm removes a surface rather than risk a resolver mismatch or accidental exposure.
|
|
241
|
-
- **Do not rely on `--format` / `output()` exemption as the safety mechanism.** The new path must be structurally value-free on its own (same-frame header set, never returned upward), not depend on CLI-level formatting exemptions.
|
|
242
|
-
- **Do not ship Proposal 3's independent enumerator now.** Deleting the `env-secret-ref → search-source` edge (P3) is the correct *eventual* structural cleanup, but only if `secretSourceRoots` **delegates to a shared lock-first helper** also used by `resolveEntryContentDir`; the as-written second-source-of-truth reintroduces the resolver-mismatch class that already cost `secret path` / `secret remove`. Track it as a separate, later refactor. **Update (issue #744): that condition was tested and cannot be met today — see §8 for the measurements and the single blocking edge.**
|
|
243
|
-
- **Do not adopt Proposal 2's breaking field rename now.** The opaque `CredentialProvider` (void/boolean methods, strongest containment) is the principled long-term target if a breaking contract change ever becomes acceptable; today its lockstep churn is unjustified when P4 reaches the same consumers additively.
|
|
244
|
-
|
|
245
|
-
---
|
|
246
|
-
|
|
247
|
-
## 8. P3 feasibility re-assessment (issue #744, 2026-08-12) — BLOCKED on one edge
|
|
248
|
-
|
|
249
|
-
§7 defers P3 with a condition: it may land "only if `secretSourceRoots` **delegates to a shared lock-first helper** also used by `resolveEntryContentDir`." That condition was tested directly. **It cannot be met today.** The blocker is a single import edge, named at the end of this section; P3 should stay deferred until that edge is severed.
|
|
250
|
-
|
|
251
|
-
### 8.1 What was built and measured
|
|
252
|
-
|
|
253
|
-
The strongest possible reading of the §7 condition was implemented as a probe: rather than write a *new* `secretSourceRoots` that "delegates to" a shared helper, `SearchSource`, `resolveSourceEntries` and `resolveEntryContentDir` were moved **verbatim** out of `indexer/search/search-source.ts` into a candidate leaf module, so the reader and the indexer would call the *same function object* — one resolver by construction, not two that agree by convention. That is strictly better than the proposal's sketch and removes any second-source-of-truth argument.
|
|
254
|
-
|
|
255
|
-
It still fails, for a reason the original critique did not reach.
|
|
256
|
-
|
|
257
|
-
### 8.2 Finding 1 — the "lock-first helper" is lock-first but **provider-second**, and the second half is load-bearing
|
|
258
|
-
|
|
259
|
-
`lockContentRootFor` (`src/integrations/lockfile.ts:169-177`) answers **only** for `git`/`npm` bundles that carry a lock `localRoot`; its first line returns `undefined` for every other kind by construction:
|
|
260
|
-
|
|
261
|
-
```ts
|
|
262
|
-
if (!bundleId || (type !== "git" && type !== "npm")) return undefined;
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
So for a **filesystem** bundle (the primary bundle — where `secrets/` actually live), a **website** cache, and a **legacy/unlocked git** bundle, the content root comes exclusively from the second half of `resolveEntryContentDir`: `resolveSourceProviderFactory(entry.type)` → `factory(entry)` → `provider.path()`. Exactly the three cases the issue's acceptance criteria demand be pinned.
|
|
266
|
-
|
|
267
|
-
And that second half is not an implementation detail to route around — it is the repo's *declared* single source of truth (`search-source.ts:161-166`): "each provider owns its own path… This replaces the old per-kind switch ladder (filesystem path / git cache / website cache) that lived here in 0.6.0." Any leaf resolver that derives those paths itself reintroduces the ladder 0.6.0 deleted.
|
|
268
|
-
|
|
269
|
-
### 8.3 Finding 2 — a leaf cannot populate the provider registry, and a half-populated registry mis-resolves **silently**
|
|
270
|
-
|
|
271
|
-
The registry is populated by one eager side-effect import, `search-source.ts:16` (`import "../../sources/providers/index"`). A leaf reader structurally cannot perform it: `providers/index → website.ts → website-ingest.ts → snapshot-fetchers/registry.ts → x.ts` is the fetcher subgraph, and pulling it in is the very coupling P3 exists to delete.
|
|
272
|
-
|
|
273
|
-
Measured against a fixture config holding all five shapes (filesystem primary, lock-managed git, **legacy/unlocked git**, **website cache**, lock-managed npm), same config, two processes:
|
|
274
|
-
|
|
275
|
-
| bundle | via `search-source` (registered) | via the leaf (not registered) |
|
|
276
|
-
|---|---|---|
|
|
277
|
-
| `primary` (filesystem) | `…/fixture/primary-stash` | `…/.akm/unresolved-sources/primary` ❌ |
|
|
278
|
-
| `locked-git` | `…/installs/locked-git/content` | same ✅ (lock-first) |
|
|
279
|
-
| `legacy-git` | `…/cache/registry-index/git-07aa…/repo` | same ✅ *(by accident — see below)* |
|
|
280
|
-
| `site-cache` (website) | `…/cache/registry-index/website-de10…` | `…/.akm/unresolved-sources/site-cache` ❌ |
|
|
281
|
-
| `locked-npm` | `…/installs/locked-npm` | same ✅ (lock-first) |
|
|
282
|
-
|
|
283
|
-
End to end, with the secret genuinely present in the primary bundle:
|
|
284
|
-
|
|
285
|
-
- `resolveSecretPath("secrets/x-bearer-token")` today → `…/fixture/primary-stash/secrets/x-bearer-token`
|
|
286
|
-
- the same logic on the leaf enumerator → `…/.akm/unresolved-sources/primary/secrets/x-bearer-token`
|
|
287
|
-
|
|
288
|
-
**A different file, no error, no warning** — `findEnvSource` falls back to `candidates[0]` when its `existsSync` probe finds nothing, and `resolveSecretFromStore` swallows every failure to `null`. This is precisely the resolver-mismatch class that cost `secret path` / `secret remove` (R-027 / D-49).
|
|
289
|
-
|
|
290
|
-
Worse than a clean failure: registration is **partial and accidental**. In the leaf's import closure `git` *is* registered — not deliberately, but because `core/write-source.ts` happens to import `sources/providers/git.ts`. So the leaf resolves git correctly and filesystem/website incorrectly, and which kinds work depends on which unrelated modules got pulled in. A resolver whose correctness varies by transitive import order is the worst available property.
|
|
291
|
-
|
|
292
|
-
### 8.4 Finding 3 — all three escapes are closed
|
|
293
|
-
|
|
294
|
-
| Escape | Result |
|
|
295
|
-
|---|---|
|
|
296
|
-
| (a) Derive filesystem/website/git roots in the leaf | Reinstates the per-kind switch ladder deleted in 0.6.0 — the second source of truth §7 forbids. |
|
|
297
|
-
| (b) Side-effect-import `providers/index` from the leaf | Measured: **7 cycle participants** (`env-secret-read`, `source-entries`, `providers/index`, `website`, `website-ingest`, `snapshot-fetchers/registry`, `x`) against an **absolute 0 baseline**. This is the subgraph coming back through another door. |
|
|
298
|
-
| (c) Rely on ambient registration order | §4.1's rejected P1 flaw, with a worse failure mode: P1 degraded to env-var-only, this resolves a *different file*. Making it loud needs either a hardcoded four-kind list (new duplicated truth) or turning `resolveEntryContentDir`'s deliberately-soft `undefined` degradation — which the indexer relies on to keep running past a bad source — into a throw. |
|
|
299
|
-
|
|
300
|
-
### 8.5 The one blocking edge, and the prerequisite
|
|
301
|
-
|
|
302
|
-
The single edge that makes `providers/index` unimportable from a leaf is:
|
|
303
|
-
|
|
304
|
-
```
|
|
305
|
-
src/sources/providers/website.ts ──▶ src/sources/snapshot-fetchers/website-ingest.ts
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
`website.ts` needs `getWebsiteCachePaths` / `validateWebsiteUrl` / `shouldAllowPrivateWebsiteUrlForTests` for `path()`, and `ensureWebsiteMirror` for `sync()`. The other three providers are already subgraph-free (`filesystem.ts`, `git.ts`, `npm.ts`).
|
|
309
|
-
|
|
310
|
-
Re-running the P3 graph with **only that edge removed** yields **0 cycle participants**. So the prerequisite is exact and singular:
|
|
311
|
-
|
|
312
|
-
1. Extract the pure path derivations (`getWebsiteCachePaths` + `normalizeSiteUrl`, `validateWebsiteUrl`, `shouldAllowPrivateWebsiteUrlForTests`) from `website-ingest.ts` into a leaf module both it and `website.ts` import. Necessary, **not sufficient** — `sync()` still reaches `ensureWebsiteMirror`.
|
|
313
|
-
2. Stop `website.ts`'s `sync()` from statically importing `ensureWebsiteMirror` — inject the mirror as a capability on `SyncOptions`, bound from a composition root, exactly the P4 idiom applied to the mirror instead of the secret.
|
|
314
|
-
|
|
315
|
-
With both done, `providers/index` becomes a leaf-importable registration point, the moved `resolveSourceEntries` is genuinely one shared resolver, and P3's acceptance criteria are reachable. Until then P3 cannot satisfy §7's condition and must not land.
|
|
316
|
-
|
|
317
|
-
### 8.6 Correction to the issue's framing
|
|
318
|
-
|
|
319
|
-
`CYCLE_PARTICIPANT_BASELINE` is **already empty** and the ratchet is absolute; `bun scripts/lint-import-cycles.ts` reports `0 cycle participant(s)`. There is no import cycle in `src/` today — `env-secret-ref → search-source` is a one-way DAG edge, and the cycle is only *latent* (it closes if a fetcher imports the reader). So #744's "the baseline shrinks" criterion is vacuous as written: the measurable win P3 offers is not a smaller baseline but the removal of the latent back-edge, which is what §8.5 gates.
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
# Runtime Boundary Design — AKM 0.9.0
|
|
2
|
-
|
|
3
|
-
> **Implementation note (added later):** both target files below now exist —
|
|
4
|
-
> `src/storage/database.ts` and `src/runtime.ts` — so this design has landed.
|
|
5
|
-
> The "3 hard-import files" named in the original checklist (`src/indexer/db.ts`,
|
|
6
|
-
> `src/core/state-db.ts`, `src/workflows/db.ts`) no longer describes the current
|
|
7
|
-
> tree: `src/indexer/db.ts` was later split into `src/storage/repositories/index-*.ts`
|
|
8
|
-
> (its `Database` opener lives in `src/storage/repositories/index-connection.ts`,
|
|
9
|
-
> which imports `openDatabase`/`Database` from `src/storage/database.ts` as this
|
|
10
|
-
> design specifies). `src/workflows/db.ts` did exist historically but has since
|
|
11
|
-
> been removed — workflow run persistence now lives in
|
|
12
|
-
> `src/storage/repositories/workflow-runs-repository.ts`.
|
|
13
|
-
> `src/core/state-db.ts` still exists and also imports from `src/storage/database.ts`.
|
|
14
|
-
> The checklist items below are kept as the historical design record.
|
|
15
|
-
|
|
16
|
-
## Problem
|
|
17
|
-
|
|
18
|
-
Runtime-specific dependencies (`bun:sqlite`, `Bun.*` APIs) leak directly into 33+ source files.
|
|
19
|
-
This creates unnecessary coupling, makes Node.js compatibility hard, and means every future
|
|
20
|
-
runtime change has a large blast radius.
|
|
21
|
-
|
|
22
|
-
## Goal
|
|
23
|
-
|
|
24
|
-
Two files own the entire runtime boundary. Everything else is clean application code.
|
|
25
|
-
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
## File 1: `src/storage/database.ts`
|
|
29
|
-
|
|
30
|
-
Single source of truth for SQLite. The rest of the app never imports from `bun:sqlite` or
|
|
31
|
-
`better-sqlite3` directly — only from here.
|
|
32
|
-
|
|
33
|
-
**Responsibilities:**
|
|
34
|
-
- Detect runtime (bun vs node) once at module load
|
|
35
|
-
- Load the appropriate SQLite driver (`bun:sqlite` on Bun, `better-sqlite3` on Node)
|
|
36
|
-
- Export `Database` type — the common structural type both drivers share
|
|
37
|
-
- Export `openDatabase(path, opts?)` factory
|
|
38
|
-
|
|
39
|
-
**Design constraints:**
|
|
40
|
-
- No adapter classes, no interface hierarchies — the driver APIs are already nearly identical
|
|
41
|
-
- The exported `Database` type should be the structural intersection of what both drivers expose
|
|
42
|
-
- `openDatabase()` should be a simple function, not a factory class
|
|
43
|
-
- `db.query()` (Bun-specific) must be normalised — replace with `db.prepare().all()` at this boundary
|
|
44
|
-
|
|
45
|
-
**Architect: investigate and fill in:**
|
|
46
|
-
- [ ] Read `src/indexer/db.ts`, `src/core/state-db.ts`, `src/workflows/db.ts` — the 3 hard-import files
|
|
47
|
-
- [ ] Identify every method AKM actually calls on a `Database` instance (exec, prepare, transaction, close, query, etc.)
|
|
48
|
-
- [ ] Check whether `better-sqlite3`'s type definitions can be used as the exported `Database` type directly, or if a small structural type alias is needed
|
|
49
|
-
- [ ] Check `sqliteVec.load(db)` — confirm whether it accepts both driver handles or needs a raw handle escape hatch
|
|
50
|
-
- [ ] Write the complete file
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## File 2: `src/runtime.ts`
|
|
55
|
-
|
|
56
|
-
~60 lines. Named exports for every `Bun.*` API called outside of the SQLite layer.
|
|
57
|
-
Each export works on both runtimes.
|
|
58
|
-
|
|
59
|
-
**Known Bun.* call sites to cover (architect: verify these are complete):**
|
|
60
|
-
|
|
61
|
-
| Export name | Bun API | Node equivalent |
|
|
62
|
-
|---|---|---|
|
|
63
|
-
| `spawnSync(cmd, opts)` | `Bun.spawnSync` | `child_process.spawnSync` |
|
|
64
|
-
| `spawn(cmd, opts)` | `Bun.spawn` | `child_process.spawn` |
|
|
65
|
-
| `readStdin(limitBytes)` | `Bun.stdin.stream()` | `for await (chunk of process.stdin)` |
|
|
66
|
-
| `writeResponseToFile(path, res)` | `Bun.write(path, response)` | `stream/promises pipeline` |
|
|
67
|
-
| `sha256Hex(data)` | `new Bun.CryptoHasher('sha256')` | `node:crypto createHash` |
|
|
68
|
-
| `md5Hex(data)` | `new Bun.CryptoHasher('md5')` | `node:crypto createHash` |
|
|
69
|
-
| `semverOrder(a, b)` | `Bun.semver.order` | `semver.compare` (add dep) |
|
|
70
|
-
| `getDirname(importMetaUrl)` | `import.meta.dir` | `path.dirname(new URL(url).pathname)` |
|
|
71
|
-
| `resolveModule(spec, from)` | `Bun.resolveSync` | `require.resolve` (already has fallback) |
|
|
72
|
-
| `sleepSync(ms)` | `Bun.sleepSync` | `Atomics.wait` (already has fallback) |
|
|
73
|
-
| `mainPath` | `Bun.main` | `process.argv[1]` |
|
|
74
|
-
|
|
75
|
-
**Design constraints:**
|
|
76
|
-
- Runtime detection: `const isBun = !!process.versions?.bun` at top of file, used inline
|
|
77
|
-
- No class, no factory — just named function exports
|
|
78
|
-
- Each export branches once on `isBun`, no per-call overhead
|
|
79
|
-
- `node:crypto` is available in both Bun and Node — the crypto exports can skip the `isBun`
|
|
80
|
-
branch entirely and just use `createHash` always (simpler, same perf for a CLI)
|
|
81
|
-
|
|
82
|
-
**Architect: investigate and fill in:**
|
|
83
|
-
- [ ] `grep -rn "Bun\." src --include="*.ts"` — confirm the table above is complete
|
|
84
|
-
- [ ] `grep -rn "import\.meta\.dir" src --include="*.ts"` — list all sites, note which have fallbacks
|
|
85
|
-
- [ ] Confirm `Readable.toWeb()` is the right Node 18 approach for `writeResponseToFile`
|
|
86
|
-
- [ ] Write the complete file
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## Call Site Changes
|
|
91
|
-
|
|
92
|
-
Once both files exist, the remaining work is mechanical:
|
|
93
|
-
|
|
94
|
-
**SQLite type-only imports (~30 files):**
|
|
95
|
-
```ts
|
|
96
|
-
// Before
|
|
97
|
-
import type { Database } from 'bun:sqlite'
|
|
98
|
-
// After
|
|
99
|
-
import type { Database } from '../storage/database'
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
**SQLite hard imports (3 files — `indexer/db.ts`, `core/state-db.ts`, `workflows/db.ts`):**
|
|
103
|
-
- Replace `import { Database } from 'bun:sqlite'` with `import { openDatabase, type Database } from '../storage/database'`
|
|
104
|
-
- Replace `new Database(path)` with `openDatabase(path)`
|
|
105
|
-
- Replace `db.query<T>(sql).all(params)` with `db.prepare(sql).all(params)` (Bun-specific API)
|
|
106
|
-
- Remove `SQLQueryBindings` spread casts
|
|
107
|
-
|
|
108
|
-
**Bun.* call sites (~10 files):**
|
|
109
|
-
```ts
|
|
110
|
-
// Before
|
|
111
|
-
Bun.spawnSync(['git', 'ls-files', ...])
|
|
112
|
-
// After
|
|
113
|
-
import { spawnSync } from '../runtime'
|
|
114
|
-
spawnSync(['git', 'ls-files', ...])
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
**Architect: investigate and fill in:**
|
|
118
|
-
- [ ] List every call site file and the exact import line change needed
|
|
119
|
-
- [ ] Flag any call site where the shim return shape differs from the Bun API (e.g. stdout as string vs Buffer)
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## `package.json` Changes
|
|
124
|
-
|
|
125
|
-
- Add `better-sqlite3` as `optionalDependency` (prebuilt binaries, no compile step on common platforms)
|
|
126
|
-
- Add `@types/better-sqlite3` to `devDependencies`
|
|
127
|
-
- Add `semver` to `dependencies` (tiny, zero-deps)
|
|
128
|
-
- Add `node: ">=22"` to `engines` (raised from 20.12 when Node 20 support was dropped, 2026-07) (`@clack/core` uses `node:util.styleText`,
|
|
129
|
-
which was added in Node 20.12)
|
|
130
|
-
- Require Node >= 22 in the npm `preinstall` guard
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
## What This Is NOT
|
|
135
|
-
|
|
136
|
-
- Not an adapter pattern with interface hierarchies
|
|
137
|
-
- Not a ports-and-adapters architecture
|
|
138
|
-
- Not a plugin system
|
|
139
|
-
- Not a DI container
|
|
140
|
-
|
|
141
|
-
Just two modules that own the runtime details so the rest of the codebase doesn't have to.
|
|
142
|
-
|
|
143
|
-
---
|
|
144
|
-
|
|
145
|
-
## Child Issues to Create (after design is complete)
|
|
146
|
-
|
|
147
|
-
1. `src/storage/database.ts` + migrate 3 hard-import files
|
|
148
|
-
2. Mechanical: update 30 type-only imports (sed pass + tsc verify)
|
|
149
|
-
3. `src/runtime.ts` + update ~10 call sites
|
|
150
|
-
4. `package.json` + relax preinstall guard + CI Node matrix
|
|
151
|
-
|
|
152
|
-
Plus: audit open GitHub issues on itlackey/akm and close any that are superseded by this work
|
|
153
|
-
or otherwise stale (milestone 0.9.0 items already addressed, etc.).
|