akm-cli 0.9.16 → 0.9.17-alpha.10

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.
Files changed (403) hide show
  1. package/CHANGELOG.md +2101 -0
  2. package/STABILITY.md +11 -10
  3. package/dist/akm +124 -193
  4. package/dist/akm-migrate +38 -19
  5. package/dist/assets/hints/cli-hints-full.md +6 -7
  6. package/dist/assets/improve-strategies/catchup.json +0 -3
  7. package/dist/assets/improve-strategies/consolidate.json +0 -1
  8. package/dist/assets/improve-strategies/default.json +1 -2
  9. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
  10. package/dist/assets/improve-strategies/quick.json +1 -2
  11. package/dist/assets/improve-strategies/reflect-distill.json +1 -2
  12. package/dist/assets/improve-strategies/thorough.json +0 -3
  13. package/dist/assets/prompts/consolidate-pair.md +20 -0
  14. package/dist/assets/prompts/consolidate-system.md +4 -11
  15. package/dist/assets/prompts/retrieval-relevance-judge.md +6 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +20 -20
  17. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
  18. package/dist/assets/templates/html/health.html +3 -5
  19. package/dist/cli/retired-commands.js +1 -1
  20. package/dist/cli/shared.js +6 -2
  21. package/dist/cli/unknown-flags.js +24 -1
  22. package/dist/cli.js +68 -10
  23. package/dist/commands/agent/agent-dispatch.js +1 -1
  24. package/dist/commands/command/command-execution.js +24 -62
  25. package/dist/commands/feedback-cli.js +0 -1
  26. package/dist/commands/health/accept-rate.js +6 -0
  27. package/dist/commands/health/archive-usage.js +92 -0
  28. package/dist/commands/health/checks.js +83 -74
  29. package/dist/commands/health/config-skew.js +38 -0
  30. package/dist/commands/health/data-dir-usage.js +25 -13
  31. package/dist/commands/health/egress.js +54 -0
  32. package/dist/commands/health/html-report.js +1 -42
  33. package/dist/commands/health/improve-metrics.js +136 -591
  34. package/dist/commands/health/md-report.js +1 -6
  35. package/dist/commands/health/plugin-staleness.js +53 -3
  36. package/dist/commands/health/renderers.js +12 -4
  37. package/dist/commands/health/report-view-model.js +14 -120
  38. package/dist/commands/health/types-improve.js +4 -19
  39. package/dist/commands/health/windows.js +64 -74
  40. package/dist/commands/health.js +145 -143
  41. package/dist/commands/improve/consolidate/chunking.js +26 -117
  42. package/dist/commands/improve/consolidate/continuity-check.js +137 -0
  43. package/dist/commands/improve/consolidate/pair-pass.js +791 -0
  44. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  45. package/dist/commands/improve/consolidate.js +589 -1127
  46. package/dist/commands/improve/content-hash.js +16 -24
  47. package/dist/commands/improve/distill/content-repair.js +18 -100
  48. package/dist/commands/improve/distill-guards.js +20 -81
  49. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  50. package/dist/commands/improve/distill.js +608 -1041
  51. package/dist/commands/improve/eligibility.js +126 -390
  52. package/dist/commands/improve/execution.js +8 -10
  53. package/dist/commands/improve/extract-prompt.js +1 -2
  54. package/dist/commands/improve/extract.js +487 -1046
  55. package/dist/commands/improve/feedback-valence.js +0 -25
  56. package/dist/commands/improve/improve-cli.js +75 -169
  57. package/dist/commands/improve/improve-result-file.js +10 -66
  58. package/dist/commands/improve/improve-strategies.js +52 -4
  59. package/dist/commands/improve/improve-usage-report.js +18 -64
  60. package/dist/commands/improve/improve.js +480 -1074
  61. package/dist/commands/improve/ledger.js +119 -0
  62. package/dist/commands/improve/locks.js +2 -8
  63. package/dist/commands/improve/loop-stages.js +415 -1073
  64. package/dist/commands/improve/memory/derived-ref.js +12 -77
  65. package/dist/commands/improve/memory/memory-belief.js +16 -118
  66. package/dist/commands/improve/memory/memory-improve.js +266 -14
  67. package/dist/commands/improve/outcome-loop.js +28 -156
  68. package/dist/commands/improve/planner.js +5 -15
  69. package/dist/commands/improve/preparation.js +779 -2319
  70. package/dist/commands/improve/proactive-maintenance.js +34 -101
  71. package/dist/commands/improve/reflect-noise.js +104 -280
  72. package/dist/commands/improve/reflect.js +642 -1353
  73. package/dist/commands/improve/retrieval-gate.js +127 -0
  74. package/dist/commands/improve/retrieval-scope.js +92 -0
  75. package/dist/commands/improve/salience.js +41 -240
  76. package/dist/commands/improve/session-asset.js +19 -100
  77. package/dist/commands/improve/stage.js +322 -0
  78. package/dist/commands/lint/base-linter.js +37 -15
  79. package/dist/commands/proposal/drain.js +261 -578
  80. package/dist/commands/proposal/proposal-cli.js +19 -20
  81. package/dist/commands/proposal/proposal-types.js +31 -24
  82. package/dist/commands/proposal/proposal.js +38 -8
  83. package/dist/commands/proposal/propose.js +134 -160
  84. package/dist/commands/proposal/repository.js +1097 -1394
  85. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  86. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  87. package/dist/commands/proposal/validators/proposals.js +22 -89
  88. package/dist/commands/read/curate.js +105 -462
  89. package/dist/commands/read/knowledge.js +3 -2
  90. package/dist/commands/read/search-cli.js +16 -33
  91. package/dist/commands/read/search.js +17 -23
  92. package/dist/commands/read/show.js +57 -108
  93. package/dist/commands/sources/bundle-cli.js +25 -2
  94. package/dist/commands/sources/bundle-config-ops.js +4 -0
  95. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  96. package/dist/commands/sources/info.js +127 -29
  97. package/dist/commands/sources/installed-stashes.js +197 -746
  98. package/dist/commands/sources/schema-repair.js +98 -129
  99. package/dist/commands/sources/source-add.js +62 -12
  100. package/dist/commands/sources/source-manage.js +9 -2
  101. package/dist/commands/sources/stash-cli.js +24 -4
  102. package/dist/commands/tasks/explain.js +10 -13
  103. package/dist/commands/tasks/tasks-cli.js +12 -13
  104. package/dist/commands/tasks/tasks.js +350 -936
  105. package/dist/commands/tasks/validate.js +26 -24
  106. package/dist/commands/workflow/plan.js +22 -29
  107. package/dist/commands/workflow-cli.js +4 -4
  108. package/dist/core/adapter/adapters/akm-adapter.js +2 -1
  109. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  110. package/dist/core/adapter/adapters/akm-metadata.js +42 -12
  111. package/dist/core/adapter/adapters/akm-task-adapter.js +29 -8
  112. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  113. package/dist/core/adapter/execution-source.js +17 -29
  114. package/dist/core/asset/asset-placement.js +4 -13
  115. package/dist/core/asset/frontmatter.js +106 -1
  116. package/dist/core/asset/resolve-ref.js +1 -1
  117. package/dist/core/bundle-id.js +42 -5
  118. package/dist/core/bundle-rename.js +285 -0
  119. package/dist/core/config/config-io.js +1 -2
  120. package/dist/core/config/config-schema.js +9 -34
  121. package/dist/core/config/config-walker.js +1 -1
  122. package/dist/core/config/config.js +184 -111
  123. package/dist/core/config/engine-semantics.js +0 -2
  124. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  125. package/dist/core/config/schema/embedding.js +20 -5
  126. package/dist/core/config/schema/engines.js +5 -0
  127. package/dist/core/config/schema/execution.js +1 -1
  128. package/dist/core/config/schema/experimental.js +1 -1
  129. package/dist/core/config/schema/improve-processes.js +54 -125
  130. package/dist/core/config/schema/improve.js +4 -42
  131. package/dist/core/config/schema/index-config.js +9 -48
  132. package/dist/core/config/schema/scheduler.js +12 -12
  133. package/dist/core/config/schema/search.js +6 -22
  134. package/dist/core/env-secret-ref.js +0 -1
  135. package/dist/core/errors.js +8 -9
  136. package/dist/core/file-change.js +13 -5
  137. package/dist/core/file-lock.js +76 -173
  138. package/dist/core/improve-result.js +35 -7
  139. package/dist/core/improve-types.js +0 -1
  140. package/dist/core/logs-db.js +2 -2
  141. package/dist/core/loopback.js +7 -12
  142. package/dist/core/non-task-input.js +20 -0
  143. package/dist/core/parse.js +13 -16
  144. package/dist/core/paths.js +0 -24
  145. package/dist/core/redaction.js +109 -2
  146. package/dist/core/run-lock.js +2 -5
  147. package/dist/core/spawn-env.js +1 -1
  148. package/dist/core/state/migrations.js +123 -61
  149. package/dist/core/state-db-scope.js +2 -4
  150. package/dist/core/state-db.js +126 -692
  151. package/dist/core/time.js +0 -20
  152. package/dist/core/type-presentation.js +1 -9
  153. package/dist/core/write-source.js +294 -1005
  154. package/dist/execution/input-contract.js +1 -1
  155. package/dist/execution/resolved-request.js +135 -689
  156. package/dist/execution/source.js +63 -257
  157. package/dist/execution/target-ref.js +1 -1
  158. package/dist/indexer/bundle-identity-guard.js +2 -2
  159. package/dist/indexer/db/llm-cache.js +2 -2
  160. package/dist/indexer/ensure-index.js +77 -73
  161. package/dist/indexer/index-rebuild-lock.js +3 -11
  162. package/dist/indexer/index-writer-lock.js +8 -17
  163. package/dist/indexer/index-written-assets.js +141 -154
  164. package/dist/indexer/indexer.js +400 -1124
  165. package/dist/indexer/links/declared-links.js +90 -0
  166. package/dist/indexer/materialize-embeddings.js +60 -397
  167. package/dist/indexer/passes/memory-inference.js +96 -90
  168. package/dist/indexer/passes/metadata.js +132 -219
  169. package/dist/indexer/read-preflight.js +0 -7
  170. package/dist/indexer/scan/doc-to-entry.js +2 -3
  171. package/dist/indexer/scan/drain-dir.js +1 -1
  172. package/dist/indexer/search/db-search.js +190 -590
  173. package/dist/indexer/search/fts-query.js +30 -41
  174. package/dist/indexer/search/ranking.js +28 -154
  175. package/dist/indexer/search/search-attribution.js +12 -32
  176. package/dist/indexer/search/search-fields.js +11 -15
  177. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  178. package/dist/indexer/search/search-source.js +1 -4
  179. package/dist/indexer/usage/usage-events.js +36 -7
  180. package/dist/indexer/walk/walker.js +3 -4
  181. package/dist/integrations/agent/engine-fallback.js +23 -40
  182. package/dist/integrations/agent/engine-resolution.js +93 -183
  183. package/dist/integrations/agent/execution.js +507 -0
  184. package/dist/integrations/agent/model-map.js +28 -156
  185. package/dist/integrations/agent/request-lowering.js +66 -141
  186. package/dist/integrations/agent/runner-dispatch.js +143 -321
  187. package/dist/integrations/agent/runner.js +54 -14
  188. package/dist/integrations/lockfile.js +53 -101
  189. package/dist/llm/client.js +18 -6
  190. package/dist/llm/embedders/deterministic.js +2 -3
  191. package/dist/llm/embedders/profile.js +71 -0
  192. package/dist/llm/embedders/remote.js +11 -17
  193. package/dist/llm/feature-gate.js +0 -8
  194. package/dist/llm/index-passes.js +3 -5
  195. package/dist/llm/memory-infer.js +1 -2
  196. package/dist/llm/structured-call.js +5 -24
  197. package/dist/output/generic-render.js +23 -11
  198. package/dist/output/html-render.js +13 -10
  199. package/dist/output/render-registry.js +3 -32
  200. package/dist/output/shapes/helpers.js +25 -38
  201. package/dist/output/shapes/passthrough.js +1 -9
  202. package/dist/{indexer/graph/graph-types.js → output/text/bundle-rename.js} +4 -1
  203. package/dist/output/text/command-format.js +69 -31
  204. package/dist/output/text/helpers.js +1 -1
  205. package/dist/output/text/migrate.js +5 -14
  206. package/dist/output/text/proposal-format.js +48 -3
  207. package/dist/output/text/show-format.js +13 -17
  208. package/dist/output/text/workflow-format.js +0 -32
  209. package/dist/output/text.js +2 -0
  210. package/dist/registry/factory.js +4 -19
  211. package/dist/registry/network.js +66 -220
  212. package/dist/registry/providers/index.js +0 -2
  213. package/dist/registry/providers/skills-sh.js +3 -14
  214. package/dist/registry/providers/static-index.js +24 -26
  215. package/dist/registry/resolve.js +55 -131
  216. package/dist/scripts/akm-migrate-node.js +42948 -92369
  217. package/dist/scripts/akm-migrate.js +42935 -92354
  218. package/dist/setup/registry-stash-loader.js +4 -13
  219. package/dist/setup/semantic-assets.js +3 -44
  220. package/dist/setup/setup.js +1 -1
  221. package/dist/setup/steps/connection.js +5 -6
  222. package/dist/setup/steps/platforms.js +2 -2
  223. package/dist/setup/steps/tasks.js +25 -15
  224. package/dist/sources/provider-factory.js +17 -18
  225. package/dist/sources/providers/filesystem.js +2 -3
  226. package/dist/sources/providers/git-install.js +7 -1
  227. package/dist/sources/providers/git-provider.js +0 -3
  228. package/dist/sources/providers/git-stash.js +83 -21
  229. package/dist/sources/providers/npm.js +2 -4
  230. package/dist/sources/providers/provider-utils.js +5 -10
  231. package/dist/sources/providers/website.js +0 -2
  232. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  233. package/dist/sources/website-url.js +2 -2
  234. package/dist/storage/database.js +9 -35
  235. package/dist/storage/repositories/improve-ledger-repository.js +209 -0
  236. package/dist/storage/repositories/index-connection.js +39 -72
  237. package/dist/storage/repositories/index-entries-repository.js +131 -129
  238. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  239. package/dist/storage/repositories/index-entry-schema.js +101 -268
  240. package/dist/storage/repositories/index-fts-repository.js +86 -256
  241. package/dist/storage/repositories/index-links-repository.js +143 -0
  242. package/dist/storage/repositories/index-llm-cache-repository.js +7 -9
  243. package/dist/storage/repositories/index-meta-repository.js +6 -4
  244. package/dist/storage/repositories/index-schema.js +257 -325
  245. package/dist/storage/repositories/index-utility-repository.js +8 -29
  246. package/dist/storage/repositories/index-vec-repository.js +133 -414
  247. package/dist/storage/repositories/outcome-repository.js +2 -1
  248. package/dist/storage/repositories/proposals-repository.js +104 -1
  249. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  250. package/dist/storage/repositories/salience-repository.js +1 -19
  251. package/dist/storage/repositories/task-history-repository.js +26 -4
  252. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  253. package/dist/storage/sqlite-migrations.js +136 -0
  254. package/dist/storage/sqlite-pragmas.js +11 -9
  255. package/dist/storage/sqlite-transaction.js +170 -0
  256. package/dist/storage/state-db-integrity.js +130 -0
  257. package/dist/tasks/activation-config.js +134 -62
  258. package/dist/tasks/backends/cron.js +191 -302
  259. package/dist/tasks/backends/exec-utils.js +2 -5
  260. package/dist/tasks/backends/launchd.js +141 -748
  261. package/dist/tasks/backends/schtasks.js +119 -623
  262. package/dist/tasks/prepare/prepare-support.js +5 -15
  263. package/dist/tasks/prepare/prepare.js +0 -2
  264. package/dist/tasks/resolve-akm-bin.js +20 -79
  265. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  266. package/dist/tasks/run/load-task.js +1 -1
  267. package/dist/tasks/scheduler-binding.js +20 -238
  268. package/dist/tasks/scheduler-invocation.js +136 -244
  269. package/dist/tasks/scheduler-lock.js +53 -0
  270. package/dist/tasks/scheduler-sync.js +368 -679
  271. package/dist/tasks/source/parse-task-source.js +55 -9
  272. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  273. package/dist/tasks/source/task-to-v4.js +464 -88
  274. package/dist/workflows/authoring/authoring.js +3 -12
  275. package/dist/workflows/compile.js +211 -0
  276. package/dist/workflows/concurrency-policy.js +13 -74
  277. package/dist/workflows/exec/child-invocation.js +3 -17
  278. package/dist/workflows/exec/child-workflow.js +32 -141
  279. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  280. package/dist/workflows/exec/environment.js +98 -0
  281. package/dist/workflows/exec/exec-unit.js +33 -140
  282. package/dist/workflows/exec/frozen-judge.js +7 -59
  283. package/dist/workflows/exec/native-executor.js +82 -341
  284. package/dist/workflows/exec/param-secrets.js +29 -47
  285. package/dist/workflows/exec/run-workflow.js +154 -387
  286. package/dist/workflows/exec/scheduler.js +9 -36
  287. package/dist/workflows/exec/step-work.js +127 -430
  288. package/dist/workflows/exec/unit-dispatch.js +11 -63
  289. package/dist/workflows/exec/unit-writer.js +8 -52
  290. package/dist/workflows/exec/worktree.js +39 -273
  291. package/dist/workflows/freeze/child-output-references.js +4 -15
  292. package/dist/workflows/freeze/environment.js +99 -92
  293. package/dist/workflows/freeze/freeze.js +172 -0
  294. package/dist/workflows/freeze/step-values.js +19 -21
  295. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  296. package/dist/workflows/freeze/targets/command.js +10 -33
  297. package/dist/workflows/freeze/targets/script.js +5 -12
  298. package/dist/workflows/freeze/targets/shell.js +3 -6
  299. package/dist/workflows/freeze/targets/task.js +25 -80
  300. package/dist/workflows/freeze/task-bindings.js +20 -67
  301. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  302. package/dist/workflows/ir/params.js +6 -51
  303. package/dist/workflows/ir/plan-hash.js +2 -34
  304. package/dist/workflows/parser.js +140 -43
  305. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  306. package/dist/workflows/renderer.js +36 -69
  307. package/dist/workflows/resource-limits.js +12 -120
  308. package/dist/workflows/runtime/agent-identity.js +8 -40
  309. package/dist/workflows/runtime/run-outputs.js +3 -6
  310. package/dist/workflows/runtime/run-plan.js +316 -0
  311. package/dist/workflows/runtime/runs.js +48 -200
  312. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  313. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  314. package/dist/workflows/validate-summary.js +2 -7
  315. package/docs/integration/bundling-akm.md +49 -42
  316. package/docs/migration/README.md +1 -0
  317. package/docs/migration/release-notes/0.9.17.md +43 -0
  318. package/docs/migration/v0.9.1-to-v0.9.2.md +23 -7
  319. package/docs/reference/cli.md +232 -135
  320. package/docs/reference/configuration.md +71 -57
  321. package/docs/reference/data-and-telemetry.md +20 -21
  322. package/docs/reference/tasks.md +105 -39
  323. package/docs/reference/workflow-schema.md +14 -18
  324. package/docs/reference/workflows.md +6 -9
  325. package/package.json +1 -1
  326. package/schemas/akm-config.json +115 -738
  327. package/schemas/akm-workflow.json +1 -0
  328. package/dist/assets/improve-strategies/graph-refresh.json +0 -15
  329. package/dist/assets/prompts/contradiction-judge.md +0 -33
  330. package/dist/assets/prompts/graph-extract-system.md +0 -1
  331. package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
  332. package/dist/assets/prompts/metadata-enhance-system.md +0 -1
  333. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
  334. package/dist/commands/health/advisories.js +0 -150
  335. package/dist/commands/health/metrics.js +0 -329
  336. package/dist/commands/health/surfaces.js +0 -102
  337. package/dist/commands/improve/anti-collapse.js +0 -83
  338. package/dist/commands/improve/collapse-detector.js +0 -432
  339. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  340. package/dist/commands/improve/consolidate/merge.js +0 -149
  341. package/dist/commands/improve/distill/promote-memory.js +0 -291
  342. package/dist/commands/improve/distill/quality-gate.js +0 -337
  343. package/dist/commands/improve/eval-cases.js +0 -52
  344. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  345. package/dist/commands/improve/proposal-envelope.js +0 -31
  346. package/dist/commands/improve/run-context.js +0 -123
  347. package/dist/commands/improve/shared.js +0 -31
  348. package/dist/commands/improve/source-identity.js +0 -28
  349. package/dist/commands/improve/triage.js +0 -96
  350. package/dist/commands/proposal/drain-policies.js +0 -151
  351. package/dist/commands/sources/update-transaction.js +0 -220
  352. package/dist/core/action-contributors.js +0 -28
  353. package/dist/core/config/config-version-shim.js +0 -101
  354. package/dist/core/fs-txn.js +0 -405
  355. package/dist/core/lexical-score.js +0 -25
  356. package/dist/core/maintenance-barrier.js +0 -167
  357. package/dist/execution/executable-identity.js +0 -105
  358. package/dist/execution/guarded-source.js +0 -427
  359. package/dist/indexer/db/graph-db.js +0 -444
  360. package/dist/indexer/graph/graph-boost.js +0 -427
  361. package/dist/indexer/graph/graph-dedup.js +0 -95
  362. package/dist/indexer/graph/graph-extraction.js +0 -1108
  363. package/dist/indexer/search/name-match.js +0 -35
  364. package/dist/indexer/search/ranking-contributors.js +0 -515
  365. package/dist/indexer/search/ranking-types.js +0 -4
  366. package/dist/indexer/walk/project-context.js +0 -192
  367. package/dist/integrations/agent/execution-cascade.js +0 -566
  368. package/dist/integrations/agent/execution-definitions.js +0 -202
  369. package/dist/integrations/agent/execution-lowering.js +0 -841
  370. package/dist/integrations/agent/execution-preparation.js +0 -98
  371. package/dist/integrations/agent/inline-execution.js +0 -74
  372. package/dist/llm/graph-extract.js +0 -728
  373. package/dist/llm/metadata-enhance.js +0 -96
  374. package/dist/registry/create-provider-registry.js +0 -29
  375. package/dist/registry/pinned-request-helper.js +0 -247
  376. package/dist/registry/pinned-transport.js +0 -717
  377. package/dist/sources/providers/index.js +0 -14
  378. package/dist/storage/engines/sqlite-migrations.js +0 -271
  379. package/dist/storage/repositories/canaries-repository.js +0 -107
  380. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  381. package/dist/storage/repositories/registry-cache.js +0 -113
  382. package/dist/tasks/scheduler-sync-preview.js +0 -52
  383. package/dist/tasks/source/task-to-v3.js +0 -507
  384. package/dist/workflows/freeze/resolve-steps.js +0 -86
  385. package/dist/workflows/freeze/source-freeze.js +0 -64
  386. package/dist/workflows/ir/compile.js +0 -321
  387. package/dist/workflows/ir/environment-v4.js +0 -330
  388. package/dist/workflows/ir/freeze-v4.js +0 -153
  389. package/dist/workflows/ir/schema-v4.js +0 -745
  390. package/dist/workflows/ir/schema.js +0 -354
  391. package/dist/workflows/program/schema.js +0 -77
  392. package/dist/workflows/runtime/checkin.js +0 -57
  393. package/dist/workflows/runtime/plan-classifier.js +0 -196
  394. package/dist/workflows/runtime/unit-checkin.js +0 -45
  395. package/dist/workflows/runtime/unit-phases.js +0 -20
  396. package/dist/workflows/schema.js +0 -4
  397. package/dist/workflows/source-ir/compile.js +0 -200
  398. package/dist/workflows/source-ir/program.js +0 -50
  399. package/dist/workflows/source-ir/result.js +0 -26
  400. package/dist/workflows/source-ir/schema.js +0 -786
  401. package/dist/workflows/source-ir/triggers.js +0 -79
  402. package/dist/workflows/source-ir/uses.js +0 -40
  403. package/dist/workflows/validator.js +0 -60
@@ -1,30 +1,15 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- /**
5
- * Registry provider factory map.
6
- *
7
- * Maps registry provider type identifiers (e.g. "static-index", "skills-sh")
8
- * to factory functions that create RegistryProvider instances.
9
- *
10
- * "Registry" here refers to the stash discovery registries (static index files,
11
- * skills.sh API) — not to be confused with the source provider factory map in
12
- * `sources/provider-factory.ts` or the installed-source operations in
13
- * `installed-stashes.ts`.
14
- *
15
- * Factories use the `RegistryProviderFactory` type owned by
16
- * `src/registry/providers/types.ts`.
17
- */
18
- import { createProviderRegistry } from "./create-provider-registry.js";
19
4
  // ── Factory map ─────────────────────────────────────────────────────────────
20
- const registry = createProviderRegistry();
5
+ const factories = new Map();
21
6
  export function registerRegistryProvider(type, factory) {
22
- registry.register(type, factory);
7
+ factories.set(type, factory);
23
8
  }
24
9
  export function resolveRegistryProviderFactory(type) {
25
- return registry.resolve(type);
10
+ return factories.get(type) ?? null;
26
11
  }
27
12
  /** Test-only seam: removes a registration made with {@link registerRegistryProvider}. */
28
13
  export function unregisterRegistryProvider(type) {
29
- registry.unregister(type);
14
+ factories.delete(type);
30
15
  }
@@ -1,247 +1,93 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- import { isIP } from "node:net";
5
- import { backoffDelay, computeRetryDelay, abortableDelay as sharedAbortableDelay, shouldRetry } from "../core/common.js";
6
- import { bareHostname, classifyNetworkAddress, classifyNetworkHostname, resolveHostnameAddresses, } from "../core/network-policy.js";
7
- import { assertRegistryPinnedTransportAvailable, requestRegistryAddressPinned, } from "./pinned-transport.js";
8
- export { requestRegistryAddressPinned } from "./pinned-transport.js";
9
- export class RegistryNetworkError extends Error {
10
- code = "REGISTRY_NETWORK_POLICY";
11
- constructor(message) {
12
- super(message);
13
- this.name = "RegistryNetworkError";
14
- }
15
- }
16
- const GITHUB_API_ORIGIN = "https://api.github.com";
17
- const MAX_REGISTRY_REDIRECTS = 8;
18
- const MAX_REGISTRY_RETRY_DELAY_MS = 30_000;
19
- let allowRegistryFixturesForTests = false;
20
- let registryHostnameResolverForTests;
21
- let registryPinnedRequestForTests;
22
- /**
23
- * Single outbound HTTP boundary for registry metadata requests.
24
- *
25
- * Every retry and redirect returns through this function's resolve, validate,
26
- * and pinned-connect sequence before another request can start.
27
- */
28
- export async function fetchRegistryResponse(rawUrl, init, options, redirectCount = 0) {
4
+ import { abortableDelay, backoffDelay, computeRetryDelay, jsonWithByteCap, ResponseTooLargeError, shouldRetry, toErrorMessage, } from "../core/common.js";
5
+ import { ConfigError, NotFoundError, TransientError } from "../core/errors.js";
6
+ import { formatRegistryUrl } from "../core/registry-url.js";
7
+ const DEFAULT_TIMEOUT_MS = 30_000;
8
+ const DEFAULT_RETRIES = 2;
9
+ const DEFAULT_JSON_BYTE_CAP = 10 * 1024 * 1024;
10
+ const MAX_RETRY_DELAY_MS = 30_000;
11
+ /** Fetch a registry URL. Resolves only to a 2xx response; every other outcome throws an `AkmError`. */
12
+ export async function fetchRegistry(rawUrl, options = {}) {
29
13
  const url = parseRegistryUrl(rawUrl);
30
- const response = await requestRegistryHop(url, init, options);
31
- if (!isRedirect(response.status))
32
- return response;
33
- if (options.policy.kind === "github-api") {
34
- await cancelRegistryResponse(response);
35
- throw new RegistryNetworkError("GitHub API registry metadata policy does not permit redirects");
36
- }
37
- if (redirectCount >= MAX_REGISTRY_REDIRECTS) {
38
- await cancelRegistryResponse(response);
39
- throw new RegistryNetworkError(`Too many redirects while fetching registry metadata from ${url.origin}`);
40
- }
41
- const location = response.headers.get("location");
42
- if (!location) {
43
- await cancelRegistryResponse(response);
44
- throw new RegistryNetworkError(`Redirect from registry host ${url.hostname} did not include Location`);
45
- }
46
- await cancelRegistryResponse(response);
47
- let nextUrl;
48
- try {
49
- nextUrl = new URL(location, url);
50
- }
51
- catch {
52
- throw new RegistryNetworkError(`Redirect from registry host ${url.hostname} included an invalid Location`);
53
- }
54
- const nextInit = redirectInit(init, url, nextUrl, response.status);
55
- return fetchRegistryResponse(nextUrl.toString(), nextInit, options, redirectCount + 1);
56
- }
57
- async function requestRegistryHop(url, init, options) {
58
- const maxRetries = options.retries ?? 3;
59
- const injectedTransport = options.requestPinnedForTesting ?? registryPinnedRequestForTests;
60
- if (!injectedTransport)
61
- assertRegistryPinnedTransportAvailable();
62
- const transport = injectedTransport ?? requestRegistryAddressPinned;
63
- for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
64
- const attemptDeadline = Date.now() + options.timeoutMs;
65
- const allowPrivate = privateNetworkAllowed(url, options);
66
- const addresses = await validatedRegistryAddresses(url, options, allowPrivate, options.timeoutMs, init?.signal);
67
- if (Date.now() >= attemptDeadline) {
68
- throw new RegistryNetworkError(`Registry request to ${url.hostname} attempt deadline expired before transport could start`);
69
- }
70
- const address = addresses[attempt % addresses.length];
71
- if (!address) {
72
- throw new RegistryNetworkError(`Refusing registry request to ${url.hostname}: no validated address available`);
73
- }
14
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
15
+ const retries = options.retries ?? DEFAULT_RETRIES;
16
+ for (let attempt = 0;; attempt += 1) {
17
+ // AbortSignal.timeout() does not keep the event loop alive, and it keeps
18
+ // covering the body after this function returns.
19
+ const timeout = AbortSignal.timeout(timeoutMs);
20
+ const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
21
+ let response;
74
22
  try {
75
- const remainingMs = attemptDeadline - Date.now();
76
- if (remainingMs <= 0) {
77
- throw new RegistryNetworkError(`Registry request to ${url.hostname} attempt deadline expired before transport could start`);
78
- }
79
- const response = await transport(url, address, init, remainingMs);
80
- if (attempt < maxRetries && shouldRetry(response.status)) {
81
- const delay = computeRetryDelay(response, attempt, { maxDelayMs: MAX_REGISTRY_RETRY_DELAY_MS });
82
- await cancelRegistryResponse(response);
83
- await abortableDelay(delay, init?.signal);
84
- continue;
85
- }
86
- return response;
23
+ response = await fetch(rawUrl, { headers: options.headers, signal, redirect: "follow" });
87
24
  }
88
25
  catch (error) {
89
- if (attempt >= maxRetries || init?.signal?.aborted)
26
+ if (options.signal?.aborted)
90
27
  throw error;
91
- await abortableDelay(backoffDelay(attempt, undefined, MAX_REGISTRY_RETRY_DELAY_MS), init?.signal);
28
+ if (attempt < retries) {
29
+ await abortableDelay(backoffDelay(attempt, undefined, MAX_RETRY_DELAY_MS), options.signal);
30
+ continue;
31
+ }
32
+ const reason = timeout.aborted ? `timed out after ${timeoutMs}ms` : `failed: ${toErrorMessage(error)}`;
33
+ throw new TransientError(`Registry request to ${url.host} ${reason} (${rawUrl})`, "REGISTRY_UNREACHABLE");
92
34
  }
35
+ if (attempt < retries && shouldRetry(response.status)) {
36
+ await cancelBody(response);
37
+ await abortableDelay(computeRetryDelay(response, attempt, { maxDelayMs: MAX_RETRY_DELAY_MS }), options.signal);
38
+ continue;
39
+ }
40
+ if (response.ok)
41
+ return response;
42
+ await cancelBody(response);
43
+ throw statusError(response.status, rawUrl);
93
44
  }
94
- throw new Error("Registry retry loop is unreachable");
95
- }
96
- function parseRegistryUrl(rawUrl) {
97
- let url;
98
- try {
99
- url = new URL(rawUrl);
100
- }
101
- catch {
102
- throw new RegistryNetworkError("Registry request URL is invalid");
103
- }
104
- if (url.protocol !== "http:" && url.protocol !== "https:") {
105
- throw new RegistryNetworkError(`Registry requests require HTTP(S), not ${url.protocol || "an empty scheme"}`);
106
- }
107
- if (url.username || url.password) {
108
- throw new RegistryNetworkError(`Registry request URL must not contain embedded credentials: ${url.hostname}`);
109
- }
110
- return url;
111
45
  }
112
- function privateNetworkAllowed(url, options) {
113
- if (options.allowPrivateHostsForTesting === true)
114
- return true;
115
- if (options.policy.kind !== "npm-api" || !options.policy.allowPrivateRegistryOrigin)
116
- return false;
117
- const expectedOrigin = normalizedOrigin(options.policy.registryOrigin, "npm registry");
118
- return url.origin === expectedOrigin;
119
- }
120
- async function validatedRegistryAddresses(url, options, allowPrivate, timeoutMs, signal) {
121
- assertPolicyOrigin(url, options.policy);
122
- const hostname = bareHostname(url.hostname);
123
- const literal = isIP(hostname) !== 0;
124
- const hostClass = classifyNetworkHostname(hostname);
125
- assertAllowedClass(hostClass, hostname, allowPrivate);
126
- if (literal)
127
- return [hostname];
128
- let addresses;
46
+ /** `fetchRegistry` plus a JSON body read under a byte cap. */
47
+ export async function fetchRegistryJson(rawUrl, options = {}) {
48
+ const response = await fetchRegistry(rawUrl, options);
129
49
  try {
130
- addresses = await resolveAddressesWithDeadline(hostname, options.resolveHostname ?? registryHostnameResolverForTests, timeoutMs, signal);
50
+ return await jsonWithByteCap(response, options.maxBytes ?? DEFAULT_JSON_BYTE_CAP, {
51
+ bodyTimeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
52
+ signal: options.signal,
53
+ });
131
54
  }
132
55
  catch (error) {
133
- if (error instanceof RegistryNetworkError || signal?.aborted)
56
+ if (options.signal?.aborted)
134
57
  throw error;
135
- throw new RegistryNetworkError(`Refusing registry request to ${hostname}: DNS resolution failed`);
136
- }
137
- if (addresses.length === 0) {
138
- throw new RegistryNetworkError(`Refusing registry request to ${hostname}: hostname resolved to no addresses`);
139
- }
140
- for (const address of addresses) {
141
- const addressClass = classifyNetworkAddress(address);
142
- assertAllowedClass(addressClass, `${hostname} (${address})`, allowPrivate, true);
143
- }
144
- return addresses;
145
- }
146
- async function resolveAddressesWithDeadline(hostname, resolver, timeoutMs, signal) {
147
- if (signal?.aborted)
148
- throw signal.reason ?? new Error("Registry request aborted");
149
- return new Promise((resolve, reject) => {
150
- let settled = false;
151
- const cleanup = () => {
152
- clearTimeout(timer);
153
- signal?.removeEventListener("abort", onAbort);
154
- };
155
- const finish = (action) => {
156
- if (settled)
157
- return;
158
- settled = true;
159
- cleanup();
160
- action();
161
- };
162
- const onAbort = () => finish(() => reject(signal?.reason ?? new Error("Registry request aborted")));
163
- const timer = setTimeout(() => finish(() => reject(new RegistryNetworkError(`Refusing registry request to ${hostname}: DNS resolution timed out after ${timeoutMs}ms`))), Math.max(1, timeoutMs));
164
- signal?.addEventListener("abort", onAbort, { once: true });
165
- void resolveHostnameAddresses(hostname, resolver).then((addresses) => finish(() => resolve(addresses)), (error) => finish(() => reject(error)));
166
- });
167
- }
168
- function assertPolicyOrigin(url, policy) {
169
- if (policy.kind === "github-api" && url.origin !== GITHUB_API_ORIGIN) {
170
- throw new RegistryNetworkError(`GitHub API origin policy rejected registry request to ${url.origin}`);
171
- }
172
- if (policy.kind === "npm-api") {
173
- const expected = normalizedOrigin(policy.registryOrigin, "npm registry");
174
- if (url.origin !== expected && classifyNetworkHostname(url.hostname) !== "public") {
175
- throw new RegistryNetworkError(`npm registry redirect policy rejected non-public origin ${url.origin}`);
58
+ if (error instanceof ResponseTooLargeError || error instanceof SyntaxError) {
59
+ throw new NotFoundError(`Registry response from ${rawUrl} is not usable: ${toErrorMessage(error)}`, "REGISTRY_RESPONSE_INVALID");
176
60
  }
61
+ throw new TransientError(`Registry response from ${rawUrl} could not be read: ${toErrorMessage(error)}`, "REGISTRY_UNREACHABLE");
177
62
  }
178
63
  }
179
- function normalizedOrigin(rawUrl, label) {
64
+ function parseRegistryUrl(rawUrl) {
65
+ let url;
180
66
  try {
181
- return new URL(rawUrl).origin;
67
+ url = new URL(rawUrl);
182
68
  }
183
69
  catch {
184
- throw new RegistryNetworkError(`Configured ${label} origin is invalid`);
70
+ throw new ConfigError(`Registry URL is not a valid URL: ${formatRegistryUrl(rawUrl)}`, "REGISTRY_URL_INVALID");
185
71
  }
186
- }
187
- function assertAllowedClass(addressClass, destination, allowPrivate, resolved = false) {
188
- if (addressClass === "public")
189
- return;
190
- if (allowPrivate && (addressClass === "private" || addressClass === "loopback"))
191
- return;
192
- const qualifier = resolved ? "resolves to " : "is ";
193
- throw new RegistryNetworkError(`Refusing registry request: ${destination} ${qualifier}non-public ${addressClass} destination`);
194
- }
195
- function redirectInit(init, currentUrl, nextUrl, status) {
196
- if (!init)
197
- return init;
198
- const headers = new Headers(init.headers);
199
- if (currentUrl.origin !== nextUrl.origin) {
200
- headers.delete("authorization");
201
- headers.delete("cookie");
202
- headers.delete("proxy-authorization");
72
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
73
+ throw new ConfigError(`Registry URL must use http(s), not ${url.protocol || "an empty scheme"}: ${formatRegistryUrl(rawUrl)}`, "REGISTRY_URL_INVALID");
203
74
  }
204
- const method = (init.method ?? "GET").toUpperCase();
205
- const switchToGet = ((status === 301 || status === 302) && method === "POST") ||
206
- (status === 303 && method !== "GET" && method !== "HEAD");
207
- if (!switchToGet)
208
- return { ...init, headers };
209
- for (const name of ["content-encoding", "content-language", "content-length", "content-location", "content-type"]) {
210
- headers.delete(name);
75
+ if (url.username || url.password) {
76
+ throw new ConfigError(`Registry URL must not embed credentials: ${url.origin}`, "REGISTRY_URL_INVALID");
211
77
  }
212
- return { ...init, method: "GET", body: undefined, headers };
213
- }
214
- function isRedirect(status) {
215
- return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
78
+ return url;
216
79
  }
217
- /** Release a terminal response body so pinned helper/socket resources cannot linger. */
218
- export async function cancelRegistryResponse(response) {
80
+ function statusError(status, rawUrl) {
81
+ const message = `Registry request failed (HTTP ${status}) for ${rawUrl}`;
82
+ if (shouldRetry(status))
83
+ return new TransientError(message, "REGISTRY_UNREACHABLE");
84
+ if (status === 404 || status === 410)
85
+ return new NotFoundError(message, "REGISTRY_NOT_FOUND");
86
+ const hint = status === 401 || status === 403
87
+ ? "The registry refused the request. For GitHub, set GITHUB_TOKEN or sign in with `gh auth login`; akm's registry providers send no other credentials."
88
+ : undefined;
89
+ return new NotFoundError(message, "REGISTRY_RESPONSE_INVALID", hint);
90
+ }
91
+ async function cancelBody(response) {
219
92
  await response.body?.cancel().catch(() => undefined);
220
93
  }
221
- /** Test-only visibility for the server-controlled Retry-After clamp. */
222
- export function _registryRetryDelayForTests(response, attempt) {
223
- return computeRetryDelay(response, attempt, { maxDelayMs: MAX_REGISTRY_RETRY_DELAY_MS });
224
- }
225
- function abortableDelay(ms, signal) {
226
- return sharedAbortableDelay(ms, signal, "Registry request aborted");
227
- }
228
- /** Existing local HTTP fixtures remain available only inside the test process. */
229
- export function allowPrivateRegistryFixtureForTests(rawUrl) {
230
- if (!allowRegistryFixturesForTests)
231
- return false;
232
- try {
233
- const url = new URL(rawUrl);
234
- const hostClass = classifyNetworkHostname(url.hostname);
235
- const port = Number.parseInt(url.port, 10);
236
- return hostClass === "loopback" && Number.isInteger(port) && port >= 1024 && port <= 65_535;
237
- }
238
- catch {
239
- return false;
240
- }
241
- }
242
- /** Test-harness seam; production composition never enables these overrides. */
243
- export function _setRegistryNetworkOverridesForTests(overrides) {
244
- allowRegistryFixturesForTests = overrides?.allowLoopbackFixtures === true;
245
- registryHostnameResolverForTests = overrides?.resolveHostname;
246
- registryPinnedRequestForTests = overrides?.requestPinned;
247
- }
@@ -7,8 +7,6 @@
7
7
  * Import this module (side-effect import) to register all built-in registry
8
8
  * providers with the provider registry. This replaces the individual
9
9
  * side-effect imports that were duplicated in registry-search.ts.
10
- *
11
- * Mirrors the pattern used by `sources/providers/index.ts`.
12
10
  */
13
11
  import "./static-index.js";
14
12
  import "./skills-sh.js";
@@ -1,12 +1,11 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- import { jsonWithByteCap } from "../../core/common.js";
5
4
  import { formatRegistryCredentialWarning, formatRegistryError, formatRegistryLabel, hasRegistryUrlCredentials, } from "../../core/registry-url.js";
6
5
  import { md5Hex } from "../../runtime.js";
7
- import { fetchCachedJson } from "../../storage/repositories/registry-cache.js";
6
+ import { fetchCachedJson } from "../../storage/repositories/registry-index-cache-repository.js";
8
7
  import { registerRegistryProvider } from "../factory.js";
9
- import { allowPrivateRegistryFixtureForTests, cancelRegistryResponse, fetchRegistryResponse } from "../network.js";
8
+ import { fetchRegistryJson } from "../network.js";
10
9
  // ── Constants ───────────────────────────────────────────────────────────────
11
10
  /** Per-query cache TTL in milliseconds (15 minutes). */
12
11
  const QUERY_CACHE_TTL_MS = 15 * 60 * 1000;
@@ -63,17 +62,7 @@ class SkillsShProvider {
63
62
  }
64
63
  },
65
64
  fetchFresh: async () => {
66
- const response = await fetchRegistryResponse(url, undefined, {
67
- policy: { kind: "public-registry" },
68
- timeoutMs: 10_000,
69
- retries: 1,
70
- allowPrivateHostsForTesting: allowPrivateRegistryFixtureForTests(url),
71
- });
72
- if (!response.ok) {
73
- await cancelRegistryResponse(response);
74
- throw new Error(`HTTP ${response.status}`);
75
- }
76
- const data = await jsonWithByteCap(response, 10 * 1024 * 1024, { bodyTimeoutMs: 10_000 });
65
+ const data = await fetchRegistryJson(url, { timeoutMs: 10_000, retries: 1 });
77
66
  const entries = parseSkillsResponse(data);
78
67
  return { value: entries, cacheJson: JSON.stringify(entries) };
79
68
  },
@@ -1,16 +1,19 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- import { jsonWithByteCap } from "../../core/common.js";
4
+ import { NotFoundError } from "../../core/errors.js";
5
5
  import { formatRegistryCredentialWarning, formatRegistryError, formatRegistryLabel, hasRegistryUrlCredentials, } from "../../core/registry-url.js";
6
+ import { warnOnce } from "../../core/warn.js";
6
7
  import { asString } from "../../integrations/github.js";
7
- import { fetchCachedJson } from "../../storage/repositories/registry-cache.js";
8
+ import { fetchCachedJson } from "../../storage/repositories/registry-index-cache-repository.js";
8
9
  import { registerRegistryProvider } from "../factory.js";
9
- import { allowPrivateRegistryFixtureForTests, cancelRegistryResponse, fetchRegistryResponse } from "../network.js";
10
+ import { fetchRegistryJson } from "../network.js";
10
11
  import { buildInstallRef } from "../resolve.js";
11
12
  // ── Constants ───────────────────────────────────────────────────────────────
12
13
  /** Cache TTL in milliseconds (1 hour). */
13
14
  const CACHE_TTL_MS = 60 * 60 * 1000;
15
+ /** Index format versions this reader was written against; both use the same `stashes[]` wire format. */
16
+ const KNOWN_INDEX_VERSIONS = new Set([2, 3]);
14
17
  // ── Provider class ──────────────────────────────────────────────────────────
15
18
  class StaticIndexProvider {
16
19
  type = "static-index";
@@ -39,6 +42,9 @@ class StaticIndexProvider {
39
42
  try {
40
43
  const index = await loadIndex(this.config);
41
44
  if (index) {
45
+ if (!KNOWN_INDEX_VERSIONS.has(index.version ?? Number.NaN)) {
46
+ warnOnce(`registry-index-version:${this.config.url}`, `Registry ${formatRegistryLabel(this.config)}: index version ${index.version ?? "(missing)"} is not one this akm was written against (2 or 3); reading the entries it can.`);
47
+ }
42
48
  const regName = this.config.name;
43
49
  for (const stash of index.stashes) {
44
50
  if (stash.source === "git") {
@@ -66,46 +72,38 @@ async function loadIndex(entry) {
66
72
  // cache row lets JSON.parse throw out of the load.
67
73
  parseCache: (json) => parseRegistryIndex(JSON.parse(json)) ?? undefined,
68
74
  fetchFresh: async () => {
69
- const response = await fetchRegistryResponse(entry.url, undefined, {
70
- policy: { kind: "public-registry" },
71
- timeoutMs: 10_000,
72
- allowPrivateHostsForTesting: allowPrivateRegistryFixtureForTests(entry.url),
73
- });
74
- if (!response.ok) {
75
- await cancelRegistryResponse(response);
76
- throw new Error(`HTTP ${response.status}`);
77
- }
78
- // Cap at 50 MB — registry indexes can grow large but unbounded
79
- // responses from a compromised server would OOM us.
80
- const data = await jsonWithByteCap(response, 50 * 1024 * 1024, { bodyTimeoutMs: 10_000 });
75
+ // Registry indexes can grow large; 50 MB is the cap on what one is
76
+ // allowed to send.
77
+ const data = await fetchRegistryJson(entry.url, { timeoutMs: 10_000, maxBytes: 50 * 1024 * 1024 });
81
78
  const index = parseRegistryIndex(data);
82
79
  if (!index) {
83
- throw new Error("Invalid registry index format");
80
+ throw new NotFoundError(`Registry index at ${formatRegistryLabel(entry)} has no stashes array`, "REGISTRY_RESPONSE_INVALID");
84
81
  }
85
- const etag = response.headers.get("etag") ?? undefined;
86
- const lastModified = response.headers.get("last-modified") ?? undefined;
87
- return { value: index, cacheJson: JSON.stringify(index), cacheOpts: { etag, lastModified } };
82
+ return { value: index, cacheJson: JSON.stringify(index) };
88
83
  },
89
84
  });
90
85
  }
91
86
  // ── Index parsing (exported for reuse) ──────────────────────────────────────
87
+ /**
88
+ * Read whatever the index carries: the only requirement is a `stashes` array.
89
+ * Entries that lack an id, name, ref or known source are skipped; `version`
90
+ * and `updatedAt` are carried through as published.
91
+ */
92
92
  export function parseRegistryIndex(data) {
93
93
  if (typeof data !== "object" || data === null || Array.isArray(data))
94
94
  return null;
95
95
  const obj = data;
96
- // Accept version 2 and 3 — both use the same stashes[] wire format.
97
- // The live official registry currently publishes version 2.
98
- if (typeof obj.version !== "number" || (obj.version !== 2 && obj.version !== 3))
99
- return null;
100
- if (typeof obj.updatedAt !== "string")
101
- return null;
102
96
  if (!Array.isArray(obj.stashes))
103
97
  return null;
104
98
  const stashes = obj.stashes.flatMap((raw) => {
105
99
  const stash = parseBundleEntry(raw);
106
100
  return stash ? [stash] : [];
107
101
  });
108
- return { version: obj.version, updatedAt: obj.updatedAt, stashes };
102
+ return {
103
+ version: typeof obj.version === "number" ? obj.version : undefined,
104
+ updatedAt: asString(obj.updatedAt),
105
+ stashes,
106
+ };
109
107
  }
110
108
  // ── Stash entry parsing ───────────────────────────────────────────────────────
111
109
  function parseBundleEntry(raw) {