akm-cli 0.9.1 → 0.9.2-alpha.2

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 (350) hide show
  1. package/CHANGELOG.md +103 -28
  2. package/README.md +3 -1
  3. package/SECURITY.md +1 -1
  4. package/STABILITY.md +1 -1
  5. package/dist/akm +2 -2
  6. package/dist/akm-migrate +2 -2
  7. package/dist/assets/hints/cli-hints-full.md +14 -9
  8. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
  9. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  10. package/dist/assets/models.json +35 -0
  11. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
  12. package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
  13. package/dist/assets/tasks/core/extract.yml +6 -5
  14. package/dist/assets/tasks/core/improve.yml +6 -5
  15. package/dist/assets/tasks/core/index-refresh.yml +6 -5
  16. package/dist/assets/tasks/core/sync.yml +6 -5
  17. package/dist/assets/tasks/core/version-check.yml +6 -5
  18. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
  19. package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
  20. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
  21. package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
  22. package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
  23. package/dist/cli/confirm.js +2 -2
  24. package/dist/cli/parse-args.js +3 -24
  25. package/dist/cli/retired-commands.js +1 -1
  26. package/dist/cli/shared.js +2 -2
  27. package/dist/cli.js +11 -9
  28. package/dist/commands/agent/agent-dispatch.js +55 -89
  29. package/dist/commands/agent/contribute-cli.js +12 -45
  30. package/dist/commands/command/builtin-action.js +32 -0
  31. package/dist/commands/command/command-cli.js +99 -0
  32. package/dist/commands/command/command-execution.js +308 -0
  33. package/dist/commands/command/execution-source-loader.js +176 -0
  34. package/dist/commands/command/portable-template.js +60 -0
  35. package/dist/commands/config-cli.js +10 -4
  36. package/dist/commands/env/env.js +4 -2
  37. package/dist/commands/feedback-cli.js +1 -1
  38. package/dist/commands/health/checks.js +241 -29
  39. package/dist/commands/health/html-report.js +0 -14
  40. package/dist/commands/health/report-view-model.js +0 -1
  41. package/dist/commands/health/surfaces.js +6 -7
  42. package/dist/commands/health/types.js +0 -2
  43. package/dist/commands/health.js +63 -18
  44. package/dist/commands/improve/collapse-detector.js +5 -6
  45. package/dist/commands/improve/consolidate.js +251 -214
  46. package/dist/commands/improve/distill/promote-memory.js +71 -34
  47. package/dist/commands/improve/distill/quality-gate.js +17 -5
  48. package/dist/commands/improve/distill.js +232 -155
  49. package/dist/commands/improve/eligibility.js +112 -79
  50. package/dist/commands/improve/execution.js +57 -0
  51. package/dist/commands/improve/extract-cli.js +5 -5
  52. package/dist/commands/improve/extract-prompt.js +64 -22
  53. package/dist/commands/improve/extract.js +608 -360
  54. package/dist/commands/improve/improve-strategies.js +43 -14
  55. package/dist/commands/improve/improve.js +249 -29
  56. package/dist/commands/improve/loop-stages.js +11 -17
  57. package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
  58. package/dist/commands/improve/outcome-loop.js +22 -38
  59. package/dist/commands/improve/planner.js +134 -0
  60. package/dist/commands/improve/preparation.js +730 -409
  61. package/dist/commands/improve/reflect.js +386 -223
  62. package/dist/commands/improve/run-context.js +3 -4
  63. package/dist/commands/improve/salience.js +6 -58
  64. package/dist/commands/improve/session-asset.js +12 -12
  65. package/dist/commands/lint/index.js +101 -29
  66. package/dist/commands/migrate-cli.js +11 -69
  67. package/dist/commands/migration-tool.js +6 -9
  68. package/dist/commands/models-cli.js +27 -0
  69. package/dist/commands/proposal/drain.js +258 -186
  70. package/dist/commands/proposal/proposal-cli.js +32 -10
  71. package/dist/commands/proposal/proposal.js +2 -5
  72. package/dist/commands/proposal/propose.js +192 -172
  73. package/dist/commands/proposal/repository.js +54 -91
  74. package/dist/commands/proposal/validators/proposal-validators.js +9 -7
  75. package/dist/commands/read/curate.js +53 -22
  76. package/dist/commands/read/registry-search.js +25 -9
  77. package/dist/commands/read/remember-cli.js +14 -2
  78. package/dist/commands/read/search.js +10 -4
  79. package/dist/commands/read/show.js +139 -153
  80. package/dist/commands/registry-cli.js +16 -7
  81. package/dist/commands/remember.js +33 -18
  82. package/dist/commands/sources/add-cli.js +19 -178
  83. package/dist/commands/sources/bundle-cli.js +15 -3
  84. package/dist/commands/sources/dangerous-env-audit.js +135 -0
  85. package/dist/commands/sources/info.js +2 -1
  86. package/dist/commands/sources/installed-stashes.js +901 -177
  87. package/dist/commands/sources/schema-repair.js +174 -95
  88. package/dist/commands/sources/self-update.js +30 -74
  89. package/dist/commands/sources/source-add.js +3 -5
  90. package/dist/commands/sources/sources-cli.js +2 -15
  91. package/dist/commands/sources/update-transaction.js +220 -0
  92. package/dist/commands/tasks/tasks-cli.js +3 -3
  93. package/dist/commands/tasks/tasks.js +736 -317
  94. package/dist/commands/workflow-cli.js +2 -2
  95. package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
  96. package/dist/core/adapter/adapters/akm-adapter.js +85 -35
  97. package/dist/core/adapter/adapters/akm-lint.js +54 -39
  98. package/dist/core/adapter/adapters/akm-metadata.js +45 -45
  99. package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
  100. package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
  101. package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
  102. package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
  103. package/dist/core/adapter/adapters/index.js +0 -9
  104. package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
  105. package/dist/core/adapter/adapters/okf-adapter.js +4 -0
  106. package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
  107. package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
  108. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
  109. package/dist/core/adapter/execution-source.js +308 -0
  110. package/dist/core/adapter/recognize-match.js +36 -13
  111. package/dist/core/adapter/registry.js +0 -9
  112. package/dist/core/asset/stash-meta.js +94 -4
  113. package/dist/core/common.js +6 -11
  114. package/dist/core/config/config-io.js +3 -3
  115. package/dist/core/config/config-schema.js +18 -40
  116. package/dist/core/config/config-sources.js +11 -21
  117. package/dist/core/config/config-walker.js +31 -13
  118. package/dist/core/config/config.js +23 -26
  119. package/dist/core/config/schema/engines.js +8 -7
  120. package/dist/core/config/schema/improve-processes.js +29 -5
  121. package/dist/core/config/schema/index-config.js +0 -27
  122. package/dist/core/config/schema/primitives.js +1 -23
  123. package/dist/core/config/schema/sources-bundles.js +13 -16
  124. package/dist/core/errors.js +2 -0
  125. package/dist/core/events.js +68 -32
  126. package/dist/core/extra-params.js +1 -0
  127. package/dist/core/improve-result.js +315 -0
  128. package/dist/core/lesson-lint.js +0 -6
  129. package/dist/core/maintenance-barrier.js +4 -4
  130. package/dist/core/network-policy.js +152 -0
  131. package/dist/core/paths.js +1 -1
  132. package/dist/core/recognition-util.js +4 -4
  133. package/dist/core/registry-url.js +456 -0
  134. package/dist/core/state/migrations.js +161 -47
  135. package/dist/core/state-db.js +453 -80
  136. package/dist/core/system-error.js +32 -0
  137. package/dist/core/time.js +2 -12
  138. package/dist/core/write-source.js +0 -18
  139. package/dist/execution/directory-identity.js +52 -0
  140. package/dist/execution/executable-identity.js +107 -0
  141. package/dist/execution/guarded-source.js +398 -0
  142. package/dist/execution/json.js +95 -0
  143. package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
  144. package/dist/execution/record.js +55 -0
  145. package/dist/execution/resolved-request.js +730 -0
  146. package/dist/execution/source.js +320 -0
  147. package/dist/indexer/bundle-identity-guard.js +5 -4
  148. package/dist/indexer/db/graph-db.js +33 -0
  149. package/dist/indexer/graph/graph-boost.js +3 -4
  150. package/dist/indexer/graph/graph-extraction.js +562 -373
  151. package/dist/indexer/index-written-assets.js +78 -39
  152. package/dist/indexer/indexer.js +471 -432
  153. package/dist/indexer/installations.js +6 -0
  154. package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
  155. package/dist/indexer/materialize-embeddings.js +155 -0
  156. package/dist/indexer/passes/memory-inference.js +227 -174
  157. package/dist/indexer/passes/metadata.js +263 -118
  158. package/dist/indexer/scan/doc-to-entry.js +7 -10
  159. package/dist/indexer/scan/drain-dir.js +51 -23
  160. package/dist/indexer/search/db-search.js +156 -50
  161. package/dist/indexer/search/fts-query.js +40 -40
  162. package/dist/indexer/search/ranking.js +36 -1
  163. package/dist/indexer/search/search-attribution.js +3 -1
  164. package/dist/indexer/search/search-fields.js +23 -14
  165. package/dist/indexer/search/search-hit-enrichers.js +1 -1
  166. package/dist/indexer/search/search-source.js +7 -16
  167. package/dist/indexer/search/semantic-status.js +10 -1
  168. package/dist/indexer/usage/show-usage.js +105 -0
  169. package/dist/indexer/usage/usage-events.js +7 -2
  170. package/dist/indexer/walk/matchers.js +40 -10
  171. package/dist/indexer/walk/path-resolver.js +5 -2
  172. package/dist/indexer/walk/walker.js +20 -2
  173. package/dist/integrations/agent/builder-shared.js +3 -6
  174. package/dist/integrations/agent/conversation-fallback.js +16 -0
  175. package/dist/integrations/agent/engine-resolution.js +87 -87
  176. package/dist/integrations/agent/execution-cascade.js +566 -0
  177. package/dist/integrations/agent/execution-definitions.js +211 -0
  178. package/dist/integrations/agent/execution-lowering.js +811 -0
  179. package/dist/integrations/agent/execution-preparation.js +67 -0
  180. package/dist/integrations/agent/index.js +0 -2
  181. package/dist/integrations/agent/inline-execution.js +74 -0
  182. package/dist/integrations/agent/model-map.js +515 -0
  183. package/dist/integrations/agent/persona-fallback.js +30 -0
  184. package/dist/integrations/agent/request-lowering.js +186 -0
  185. package/dist/integrations/agent/runner-dispatch.js +230 -37
  186. package/dist/integrations/agent/runner.js +12 -83
  187. package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
  188. package/dist/integrations/harnesses/aider/index.js +0 -1
  189. package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
  190. package/dist/integrations/harnesses/amazonq/index.js +0 -1
  191. package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
  192. package/dist/integrations/harnesses/claude/index.js +1 -5
  193. package/dist/integrations/harnesses/claude/session-log.js +3 -33
  194. package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
  195. package/dist/integrations/harnesses/codex/index.js +0 -1
  196. package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
  197. package/dist/integrations/harnesses/copilot/index.js +0 -1
  198. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
  199. package/dist/integrations/harnesses/gemini/index.js +0 -1
  200. package/dist/integrations/harnesses/index.js +4 -44
  201. package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
  202. package/dist/integrations/harnesses/opencode/index.js +0 -2
  203. package/dist/integrations/harnesses/opencode/session-log.js +14 -204
  204. package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
  205. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
  206. package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
  207. package/dist/integrations/harnesses/openhands/index.js +0 -1
  208. package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
  209. package/dist/integrations/harnesses/pi/index.js +0 -1
  210. package/dist/integrations/harnesses/shared.js +0 -1
  211. package/dist/integrations/harnesses/types.js +1 -3
  212. package/dist/integrations/lockfile.js +82 -79
  213. package/dist/integrations/session-logs/index.js +6 -17
  214. package/dist/integrations/session-logs/provider-base.js +1 -29
  215. package/dist/llm/client.js +10 -5
  216. package/dist/llm/embedder.js +6 -7
  217. package/dist/llm/embedders/local.js +37 -88
  218. package/dist/llm/embedders/types.js +1 -1
  219. package/dist/llm/graph-extract.js +75 -50
  220. package/dist/llm/index-passes.js +43 -5
  221. package/dist/llm/memory-infer.js +8 -6
  222. package/dist/llm/metadata-enhance.js +5 -3
  223. package/dist/llm/structured-call.js +122 -25
  224. package/dist/output/format-exempt.js +1 -1
  225. package/dist/output/render-registry.js +0 -16
  226. package/dist/output/renderers.js +12 -7
  227. package/dist/output/shapes/curate.js +1 -0
  228. package/dist/output/shapes/helpers.js +10 -2
  229. package/dist/output/shapes/passthrough.js +2 -0
  230. package/dist/output/text/command-format.js +31 -33
  231. package/dist/output/text/health-format.js +1 -29
  232. package/dist/output/text/migrate.js +6 -56
  233. package/dist/output/text/proposal-format.js +16 -1
  234. package/dist/output/text/workflow-format.js +16 -0
  235. package/dist/registry/network.js +279 -0
  236. package/dist/registry/pinned-request-helper.js +247 -0
  237. package/dist/registry/pinned-transport.js +717 -0
  238. package/dist/registry/providers/skills-sh.js +18 -6
  239. package/dist/registry/providers/static-index.js +20 -7
  240. package/dist/registry/resolve.js +53 -28
  241. package/dist/scripts/akm-migrate-node.js +19334 -52269
  242. package/dist/scripts/akm-migrate.js +19270 -51612
  243. package/dist/setup/registry-stash-loader.js +64 -20
  244. package/dist/setup/semantic-assets.js +9 -34
  245. package/dist/setup/setup.js +12 -30
  246. package/dist/setup/source-identity.js +17 -0
  247. package/dist/setup/steps/sources.js +36 -15
  248. package/dist/setup/steps/tasks.js +39 -11
  249. package/dist/sources/providers/git-provider.js +3 -3
  250. package/dist/sources/providers/npm.js +2 -2
  251. package/dist/sources/providers/provider-utils.js +4 -3
  252. package/dist/sources/providers/website.js +11 -7
  253. package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
  254. package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
  255. package/dist/sources/website-url.js +73 -0
  256. package/dist/storage/engines/sqlite-migrations.js +81 -26
  257. package/dist/storage/managed-db.js +27 -24
  258. package/dist/storage/repositories/events-repository.js +3 -0
  259. package/dist/storage/repositories/index-connection.js +42 -10
  260. package/dist/storage/repositories/index-entries-repository.js +203 -229
  261. package/dist/storage/repositories/index-entry-mapper.js +8 -12
  262. package/dist/storage/repositories/index-entry-schema.js +255 -0
  263. package/dist/storage/repositories/index-fts-repository.js +64 -71
  264. package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
  265. package/dist/storage/repositories/index-meta-repository.js +0 -11
  266. package/dist/storage/repositories/index-schema.js +74 -350
  267. package/dist/storage/repositories/index-utility-repository.js +12 -17
  268. package/dist/storage/repositories/index-vec-repository.js +56 -7
  269. package/dist/storage/repositories/proposals-repository.js +4 -127
  270. package/dist/storage/repositories/registry-cache.js +2 -1
  271. package/dist/storage/repositories/task-history-repository.js +20 -40
  272. package/dist/storage/repositories/workflow-runs-repository.js +228 -129
  273. package/dist/storage/sqlite-read-snapshot.js +148 -0
  274. package/dist/tasks/backends/cron.js +170 -42
  275. package/dist/tasks/backends/index.js +1 -1
  276. package/dist/tasks/backends/launchd.js +787 -202
  277. package/dist/tasks/backends/schtasks.js +282 -83
  278. package/dist/tasks/embedded.js +7 -7
  279. package/dist/tasks/frozen-script.js +50 -0
  280. package/dist/tasks/resolve-akm-bin.js +5 -1
  281. package/dist/tasks/runner.js +239 -251
  282. package/dist/tasks/runtime-v3.js +281 -0
  283. package/dist/tasks/scheduler-binding.js +272 -0
  284. package/dist/tasks/scheduler-invocation.js +57 -43
  285. package/dist/tasks/scheduler-sync.js +654 -0
  286. package/dist/tasks/source-v3.js +752 -0
  287. package/dist/tasks/standalone-script-entry.js +5 -0
  288. package/dist/tasks/task-id.js +29 -0
  289. package/dist/workflows/authoring/authoring.js +15 -32
  290. package/dist/workflows/exec/dispatch-redaction.js +14 -8
  291. package/dist/workflows/exec/exec-unit.js +7 -28
  292. package/dist/workflows/exec/frozen-judge.js +57 -89
  293. package/dist/workflows/exec/lowering-notices.js +23 -0
  294. package/dist/workflows/exec/native-executor.js +301 -458
  295. package/dist/workflows/exec/param-secrets.js +4 -3
  296. package/dist/workflows/exec/run-workflow.js +26 -32
  297. package/dist/workflows/exec/step-work.js +105 -109
  298. package/dist/workflows/exec/unit-dispatch.js +103 -27
  299. package/dist/workflows/exec/unit-writer.js +3 -3
  300. package/dist/workflows/exec/worktree.js +2 -2
  301. package/dist/workflows/ir/compile.js +86 -72
  302. package/dist/workflows/ir/environment-v4.js +328 -0
  303. package/dist/workflows/ir/freeze-v4.js +122 -0
  304. package/dist/workflows/ir/plan-hash.js +13 -7
  305. package/dist/workflows/ir/schema-v4.js +525 -0
  306. package/dist/workflows/ir/schema.js +25 -284
  307. package/dist/workflows/ir/source-freeze-v4.js +506 -0
  308. package/dist/workflows/parser.js +27 -24
  309. package/dist/workflows/program/schema.js +1 -2
  310. package/dist/workflows/renderer.js +42 -29
  311. package/dist/workflows/resource-limits.js +4 -5
  312. package/dist/workflows/runtime/agent-identity.js +11 -13
  313. package/dist/workflows/runtime/plan-classifier.js +8 -8
  314. package/dist/workflows/runtime/runs.js +27 -43
  315. package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
  316. package/dist/workflows/source-files.js +373 -0
  317. package/dist/workflows/source-ir/compile.js +196 -0
  318. package/dist/workflows/source-ir/github-yaml.js +577 -0
  319. package/dist/workflows/source-ir/ordering.js +38 -0
  320. package/dist/workflows/source-ir/program.js +50 -0
  321. package/dist/workflows/source-ir/result.js +26 -0
  322. package/dist/workflows/source-ir/schema.js +772 -0
  323. package/dist/workflows/source-ir/semantics.js +242 -0
  324. package/dist/workflows/source-ir/uses.js +14 -0
  325. package/docs/README.md +2 -0
  326. package/docs/migration/README.md +3 -1
  327. package/docs/migration/release-notes/0.9.2.md +55 -0
  328. package/docs/migration/release-notes/README.md +5 -0
  329. package/docs/migration/v0.8-to-v0.9.md +76 -1077
  330. package/docs/migration/v0.9.0-troubleshooting.md +104 -516
  331. package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
  332. package/docs/reference/README.md +1 -0
  333. package/docs/reference/cli.md +230 -98
  334. package/docs/reference/configuration.md +159 -36
  335. package/docs/reference/data-and-telemetry.md +19 -1
  336. package/docs/reference/supported-formats.md +23 -3
  337. package/docs/reference/tasks.md +182 -0
  338. package/docs/reference/workflow-schema.md +91 -40
  339. package/docs/reference/workflows.md +33 -6
  340. package/package.json +10 -6
  341. package/schemas/akm-config.json +372 -224
  342. package/schemas/akm-task.json +324 -80
  343. package/schemas/akm-workflow.json +6 -9
  344. package/dist/core/migration-operation.js +0 -75
  345. package/dist/integrations/agent/model-aliases.js +0 -74
  346. package/dist/tasks/parser.js +0 -380
  347. package/dist/tasks/schema.js +0 -123
  348. package/dist/tasks/validator.js +0 -80
  349. package/dist/workflows/ir/freeze.js +0 -320
  350. package/dist/workflows/runtime/document-cache.js +0 -13
@@ -1,10 +1,12 @@
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 { fetchWithRetry } from "../../core/common.js";
4
+ import { jsonWithByteCap } from "../../core/common.js";
5
+ import { formatRegistryCredentialWarning, formatRegistryError, formatRegistryLabel, hasRegistryUrlCredentials, } from "../../core/registry-url.js";
5
6
  import { md5Hex } from "../../runtime.js";
6
7
  import { fetchCachedJson } from "../../storage/repositories/registry-cache.js";
7
8
  import { registerRegistryProvider } from "../factory.js";
9
+ import { allowPrivateRegistryFixtureForTests, cancelRegistryResponse, fetchRegistryResponse } from "../network.js";
8
10
  // ── Constants ───────────────────────────────────────────────────────────────
9
11
  /** Per-query cache TTL in milliseconds (15 minutes). */
10
12
  const QUERY_CACHE_TTL_MS = 15 * 60 * 1000;
@@ -16,6 +18,9 @@ class SkillsShProvider {
16
18
  this.config = config;
17
19
  }
18
20
  async search(options) {
21
+ if (hasRegistryUrlCredentials(this.config.url)) {
22
+ return { hits: [], warnings: [formatRegistryCredentialWarning(this.config)] };
23
+ }
19
24
  try {
20
25
  const entries = await this.fetchSkills(options.query, options.limit);
21
26
  const limited = entries.slice(0, options.limit);
@@ -27,9 +32,10 @@ class SkillsShProvider {
27
32
  return { hits, assetHits };
28
33
  }
29
34
  catch (err) {
30
- const label = this.config.name ?? "skills.sh";
31
- const message = err instanceof Error ? err.message : String(err);
32
- return { hits: [], warnings: [`Registry ${label}: ${message}`] };
35
+ return {
36
+ hits: [],
37
+ warnings: [`Registry ${formatRegistryLabel(this.config)}: ${formatRegistryError(err)}`],
38
+ };
33
39
  }
34
40
  }
35
41
  async fetchSkills(query, limit) {
@@ -57,11 +63,17 @@ class SkillsShProvider {
57
63
  }
58
64
  },
59
65
  fetchFresh: async () => {
60
- const response = await fetchWithRetry(url, undefined, { timeout: 10_000, retries: 1 });
66
+ const response = await fetchRegistryResponse(url, undefined, {
67
+ policy: { kind: "public-registry" },
68
+ timeoutMs: 10_000,
69
+ retries: 1,
70
+ allowPrivateHostsForTesting: allowPrivateRegistryFixtureForTests(url),
71
+ });
61
72
  if (!response.ok) {
73
+ await cancelRegistryResponse(response);
62
74
  throw new Error(`HTTP ${response.status}`);
63
75
  }
64
- const data = (await response.json());
76
+ const data = await jsonWithByteCap(response, 10 * 1024 * 1024, { bodyTimeoutMs: 10_000 });
65
77
  const entries = parseSkillsResponse(data);
66
78
  return { value: entries, cacheJson: JSON.stringify(entries) };
67
79
  },
@@ -1,10 +1,12 @@
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 { fetchWithRetry, jsonWithByteCap, toErrorMessage } from "../../core/common.js";
4
+ import { jsonWithByteCap } from "../../core/common.js";
5
+ import { formatRegistryCredentialWarning, formatRegistryError, formatRegistryLabel, hasRegistryUrlCredentials, } from "../../core/registry-url.js";
5
6
  import { asString } from "../../integrations/github.js";
6
7
  import { fetchCachedJson } from "../../storage/repositories/registry-cache.js";
7
8
  import { registerRegistryProvider } from "../factory.js";
9
+ import { allowPrivateRegistryFixtureForTests, cancelRegistryResponse, fetchRegistryResponse } from "../network.js";
8
10
  import { buildInstallRef } from "../resolve.js";
9
11
  // ── Constants ───────────────────────────────────────────────────────────────
10
12
  /** Cache TTL in milliseconds (1 hour). */
@@ -17,6 +19,9 @@ class StaticIndexProvider {
17
19
  this.config = config;
18
20
  }
19
21
  async search(options) {
22
+ if (hasRegistryUrlCredentials(this.config.url)) {
23
+ return { hits: [], warnings: [formatRegistryCredentialWarning(this.config)] };
24
+ }
20
25
  const warnings = [];
21
26
  const bundles = await this.loadBundles(warnings);
22
27
  const hits = scoreBundles(bundles, options.query, options.limit);
@@ -36,13 +41,16 @@ class StaticIndexProvider {
36
41
  if (index) {
37
42
  const regName = this.config.name;
38
43
  for (const stash of index.stashes) {
44
+ if (stash.source === "git") {
45
+ warnings.push(`Registry ${formatRegistryLabel(this.config)}: ignored ${stash.id} because registry-provided git transport refs are not installable`);
46
+ continue;
47
+ }
39
48
  bundles.push({ stash, registryName: regName });
40
49
  }
41
50
  }
42
51
  }
43
52
  catch (err) {
44
- const label = this.config.name ? `${this.config.name} (${this.config.url})` : this.config.url;
45
- warnings.push(`Registry ${label}: ${toErrorMessage(err)}`);
53
+ warnings.push(`Registry ${formatRegistryLabel(this.config)}: ${formatRegistryError(err)}`);
46
54
  }
47
55
  return bundles;
48
56
  }
@@ -58,13 +66,18 @@ async function loadIndex(entry) {
58
66
  // cache row lets JSON.parse throw out of the load.
59
67
  parseCache: (json) => parseRegistryIndex(JSON.parse(json)) ?? undefined,
60
68
  fetchFresh: async () => {
61
- const response = await fetchWithRetry(entry.url, undefined, { timeout: 10_000 });
69
+ const response = await fetchRegistryResponse(entry.url, undefined, {
70
+ policy: { kind: "public-registry" },
71
+ timeoutMs: 10_000,
72
+ allowPrivateHostsForTesting: allowPrivateRegistryFixtureForTests(entry.url),
73
+ });
62
74
  if (!response.ok) {
75
+ await cancelRegistryResponse(response);
63
76
  throw new Error(`HTTP ${response.status}`);
64
77
  }
65
78
  // Cap at 50 MB — registry indexes can grow large but unbounded
66
79
  // responses from a compromised server would OOM us.
67
- const data = await jsonWithByteCap(response, 50 * 1024 * 1024);
80
+ const data = await jsonWithByteCap(response, 50 * 1024 * 1024, { bodyTimeoutMs: 10_000 });
68
81
  const index = parseRegistryIndex(data);
69
82
  if (!index) {
70
83
  throw new Error("Invalid registry index format");
@@ -182,7 +195,7 @@ function toSearchHit(stash, score, registryName) {
182
195
  title: stash.name,
183
196
  description: stash.description,
184
197
  ref: stash.ref,
185
- installRef: buildInstallRef(stash.source, stash.ref),
198
+ installRef: buildInstallRef(stash.source, stash.ref, "registry"),
186
199
  homepage: stash.homepage,
187
200
  score: Math.round(score * 1000) / 1000,
188
201
  metadata,
@@ -224,7 +237,7 @@ function scoreAssets(stashes, query, limit) {
224
237
  for (const { stash, registryName } of stashes) {
225
238
  if (!stash.assets || stash.assets.length === 0)
226
239
  continue;
227
- const installRef = buildInstallRef(stash.source, stash.ref);
240
+ const installRef = buildInstallRef(stash.source, stash.ref, "registry");
228
241
  for (const asset of stash.assets) {
229
242
  const score = scoreAsset(asset, tokens);
230
243
  if (score > 0) {
@@ -6,9 +6,10 @@ import fs from "node:fs";
6
6
  import os from "node:os";
7
7
  import path from "node:path";
8
8
  import { fileURLToPath, pathToFileURL } from "node:url";
9
- import { fetchWithRetry, jsonWithByteCap } from "../core/common.js";
9
+ import { jsonWithByteCap } from "../core/common.js";
10
10
  import { NotFoundError, UsageError } from "../core/errors.js";
11
11
  import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "../integrations/github.js";
12
+ import { cancelRegistryResponse, fetchRegistryResponse } from "./network.js";
12
13
  import { isExactSemver, isSemverRange, maxSatisfying } from "./semver.js";
13
14
  /**
14
15
  * Validate that a URL is safe to pass to git.
@@ -72,7 +73,10 @@ export function parseRegistryRef(rawRef) {
72
73
  return parseGithubShorthand(ref, ref);
73
74
  }
74
75
  /** Inverse of {@link parseRegistryRef}: build the install ref for a source kind. */
75
- export function buildInstallRef(source, ref) {
76
+ export function buildInstallRef(source, ref, provenance = "operator") {
77
+ if (source === "git" && provenance === "registry") {
78
+ throw new UsageError("Registry-provided git transport refs are not installable. Add a trusted git URL directly if you intend to use it.");
79
+ }
76
80
  switch (source) {
77
81
  case "npm":
78
82
  return `npm:${ref}`;
@@ -306,11 +310,12 @@ export function trustedNpmTarballHosts() {
306
310
  return hosts;
307
311
  }
308
312
  /**
309
- * Validate that an npm tarball URL points at a trusted registry host. A
310
- * compromised mirror could otherwise return an attacker-controlled
311
- * `dist.tarball` field, redirecting installs to a third-party host.
313
+ * Validate that an npm tarball URL starts at the exact metadata registry
314
+ * origin. A compromised mirror must not change scheme or port (or nominate a
315
+ * different host) in `dist.tarball`; later public redirects remain subject to
316
+ * the outbound boundary's hop-by-hop policy.
312
317
  */
313
- export function validateNpmTarballUrl(tarballUrl, packageRef) {
318
+ export function validateNpmTarballUrl(tarballUrl, packageRef, registryOrigin) {
314
319
  let url;
315
320
  try {
316
321
  url = new URL(tarballUrl);
@@ -321,12 +326,21 @@ export function validateNpmTarballUrl(tarballUrl, packageRef) {
321
326
  if (url.protocol !== "https:" && url.protocol !== "http:") {
322
327
  throw new UntrustedNpmTarballError(`npm package ${packageRef} returned a tarball with disallowed scheme "${url.protocol}".`);
323
328
  }
324
- const trusted = trustedNpmTarballHosts();
325
- const host = url.hostname.toLowerCase();
326
- if (!trusted.has(host)) {
327
- const allowed = Array.from(trusted).join(", ");
328
- throw new UntrustedNpmTarballError(`npm package ${packageRef} returned a tarball URL on untrusted host "${host}" (allowed: ${allowed}).`);
329
+ const expectedOrigin = registryOrigin ?? new URL(npmMetadataRegistry().baseUrl).origin;
330
+ if (url.origin !== expectedOrigin) {
331
+ throw new UntrustedNpmTarballError(`npm package ${packageRef} returned a tarball URL on untrusted origin "${url.origin}" (expected: ${expectedOrigin}).`);
332
+ }
333
+ }
334
+ /** Network policy for a tarball URL that already passed {@link validateNpmTarballUrl}. */
335
+ export function npmArtifactNetworkPolicy(artifact) {
336
+ if (!artifact.registryOrigin) {
337
+ throw new UsageError("npm artifact network policy requires the registry origin that authorized its metadata.");
329
338
  }
339
+ return {
340
+ kind: "npm-api",
341
+ registryOrigin: new URL(artifact.registryOrigin).origin,
342
+ allowPrivateRegistryOrigin: artifact.allowPrivateRegistryOrigin === true,
343
+ };
330
344
  }
331
345
  /**
332
346
  * Resolve the npm registry base URL used to fetch package METADATA.
@@ -341,24 +355,29 @@ export function validateNpmTarballUrl(tarballUrl, packageRef) {
341
355
  * default wholesale (like npm's own `--registry` flag) rather than being
342
356
  * merged with it.
343
357
  */
344
- function npmMetadataRegistryBase() {
358
+ function npmMetadataRegistry() {
345
359
  const override = process.env.AKM_NPM_REGISTRY?.trim();
346
360
  if (override) {
347
361
  try {
348
362
  const url = new URL(override);
349
363
  const base = `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
350
- return base.replace(/\/+$/, "");
364
+ return { baseUrl: base.replace(/\/+$/, ""), allowPrivateRegistryOrigin: true };
351
365
  }
352
366
  catch {
353
367
  // Ignore unparseable overrides; fall back to the public registry.
354
368
  }
355
369
  }
356
- return `https://${DEFAULT_NPM_REGISTRY_HOST}`;
370
+ return { baseUrl: `https://${DEFAULT_NPM_REGISTRY_HOST}`, allowPrivateRegistryOrigin: false };
357
371
  }
358
372
  async function resolveNpmArtifact(parsed) {
359
373
  const encodedName = encodeURIComponent(parsed.packageName);
360
- const registryBase = npmMetadataRegistryBase();
361
- const metadata = await fetchJson(`${registryBase}/${encodedName}`);
374
+ const npmRegistry = npmMetadataRegistry();
375
+ const npmPolicy = {
376
+ kind: "npm-api",
377
+ registryOrigin: new URL(npmRegistry.baseUrl).origin,
378
+ allowPrivateRegistryOrigin: npmRegistry.allowPrivateRegistryOrigin,
379
+ };
380
+ const metadata = await fetchJson(`${npmRegistry.baseUrl}/${encodedName}`, undefined, npmPolicy);
362
381
  const versions = asRecord(metadata.versions);
363
382
  const distTags = asRecord(metadata["dist-tags"]);
364
383
  const requested = parsed.requestedVersionOrTag;
@@ -387,7 +406,7 @@ async function resolveNpmArtifact(parsed) {
387
406
  if (!tarballUrl) {
388
407
  throw new Error(`npm package ${parsed.packageName}@${resolvedVersion} does not expose a tarball URL.`);
389
408
  }
390
- validateNpmTarballUrl(tarballUrl, `${parsed.packageName}@${resolvedVersion}`);
409
+ validateNpmTarballUrl(tarballUrl, `${parsed.packageName}@${resolvedVersion}`, npmPolicy.registryOrigin);
391
410
  const resolvedRevision = asString(dist.shasum) ?? asString(dist.integrity);
392
411
  return {
393
412
  id: parsed.id,
@@ -396,6 +415,8 @@ async function resolveNpmArtifact(parsed) {
396
415
  artifactUrl: tarballUrl,
397
416
  resolvedVersion,
398
417
  resolvedRevision,
418
+ registryOrigin: npmPolicy.registryOrigin,
419
+ allowPrivateRegistryOrigin: npmPolicy.allowPrivateRegistryOrigin,
399
420
  };
400
421
  }
401
422
  async function resolveGithubArtifact(parsed) {
@@ -416,7 +437,7 @@ async function resolveGithubArtifact(parsed) {
416
437
  }
417
438
  const headers = githubHeaders();
418
439
  if (parsed.requestedRef) {
419
- const commit = await tryFetchJson(`${repoBase}/commits/${encodeURIComponent(parsed.requestedRef)}`, headers);
440
+ const commit = await tryFetchJson(`${repoBase}/commits/${encodeURIComponent(parsed.requestedRef)}`, headers, GITHUB_API_POLICY);
420
441
  const resolvedRevision = asString(commit?.sha) ?? parsed.requestedRef;
421
442
  return {
422
443
  id: parsed.id,
@@ -427,7 +448,7 @@ async function resolveGithubArtifact(parsed) {
427
448
  resolvedVersion: parsed.requestedRef,
428
449
  };
429
450
  }
430
- const latestRelease = await tryFetchJson(`${repoBase}/releases/latest`, headers);
451
+ const latestRelease = await tryFetchJson(`${repoBase}/releases/latest`, headers, GITHUB_API_POLICY);
431
452
  if (latestRelease) {
432
453
  const tarballUrl = asString(latestRelease.tarball_url);
433
454
  if (tarballUrl) {
@@ -441,12 +462,12 @@ async function resolveGithubArtifact(parsed) {
441
462
  };
442
463
  }
443
464
  }
444
- const repoMeta = await fetchJson(repoBase, headers);
465
+ const repoMeta = await fetchJson(repoBase, headers, GITHUB_API_POLICY);
445
466
  const defaultBranch = asString(repoMeta.default_branch);
446
467
  if (!defaultBranch) {
447
468
  throw new Error(`Unable to resolve default branch for ${parsed.owner}/${parsed.repo}.`);
448
469
  }
449
- const commit = await tryFetchJson(`${repoBase}/commits/${encodeURIComponent(defaultBranch)}`, headers);
470
+ const commit = await tryFetchJson(`${repoBase}/commits/${encodeURIComponent(defaultBranch)}`, headers, GITHUB_API_POLICY);
450
471
  return {
451
472
  id: parsed.id,
452
473
  source: parsed.source,
@@ -603,16 +624,20 @@ function readGitValue(repoRoot, ...args) {
603
624
  // responses are typically a few KB; a compromised registry streaming
604
625
  // tens of MB of JSON is a DoS surface, not a feature.
605
626
  const REGISTRY_JSON_BYTE_CAP = 10 * 1024 * 1024;
606
- async function fetchJson(url, headers) {
607
- const response = await fetchWithRetry(url, { headers });
627
+ const GITHUB_API_POLICY = { kind: "github-api" };
628
+ async function fetchJson(url, headers, policy) {
629
+ const response = await fetchRegistryResponse(url, { headers }, { policy, timeoutMs: 30_000 });
608
630
  if (!response.ok) {
631
+ await cancelRegistryResponse(response);
609
632
  throw new Error(`Request failed (${response.status}) for ${url}`);
610
633
  }
611
- return jsonWithByteCap(response, REGISTRY_JSON_BYTE_CAP);
634
+ return jsonWithByteCap(response, REGISTRY_JSON_BYTE_CAP, { bodyTimeoutMs: 30_000 });
612
635
  }
613
- async function tryFetchJson(url, headers) {
614
- const response = await fetchWithRetry(url, { headers });
615
- if (!response.ok)
636
+ async function tryFetchJson(url, headers, policy) {
637
+ const response = await fetchRegistryResponse(url, { headers }, { policy, timeoutMs: 30_000 });
638
+ if (!response.ok) {
639
+ await cancelRegistryResponse(response);
616
640
  return null;
617
- return jsonWithByteCap(response, REGISTRY_JSON_BYTE_CAP);
641
+ }
642
+ return jsonWithByteCap(response, REGISTRY_JSON_BYTE_CAP, { bodyTimeoutMs: 30_000 });
618
643
  }