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
@@ -8,19 +8,29 @@
8
8
  * using a cached result when available. Falls back to FALLBACK_STASHES
9
9
  * when the registry is unreachable or returns no results.
10
10
  *
11
- * Adding a new default-selected stash: append its registry ID to
12
- * DEFAULT_SELECTED_STASH_IDS below. No other change required.
11
+ * Default-selected registry IDs are also bound to an authenticated install
12
+ * target below so a configured registry cannot preselect a different source
13
+ * merely by reusing a trusted display ID.
13
14
  */
15
+ import { jsonWithByteCap } from "../core/common.js";
16
+ import { hasRegistryUrlCredentials } from "../core/registry-url.js";
17
+ import { allowPrivateRegistryFixtureForTests, cancelRegistryResponse, fetchRegistryResponse, } from "../registry/network.js";
18
+ import { parseRegistryRef } from "../registry/resolve.js";
14
19
  // ── Default selections ──────────────────────────────────────────────────────
15
20
  /**
16
21
  * Registry stash IDs that are pre-selected by default during setup.
17
- * To add a new default stash: append its registry ID here.
18
- * IDs must match the `id` field in the official registry index.
22
+ * IDs must match the `id` field in the official registry index and have a
23
+ * matching target in AUTHENTICATED_DEFAULT_TARGETS.
19
24
  *
20
- * This is the single source of truth for which stashes are pre-checked
21
- * in the setup wizard. No other change is required to adjust defaults.
25
+ * The target binding is intentionally separate from display metadata.
22
26
  */
23
27
  export const DEFAULT_SELECTED_STASH_IDS = ["itlackey/akm-stash"];
28
+ const AUTHENTICATED_DEFAULT_TARGETS = {
29
+ "itlackey/akm-stash": {
30
+ installType: "git",
31
+ url: "https://github.com/itlackey/akm-stash",
32
+ },
33
+ };
24
34
  // ── Fallback list ───────────────────────────────────────────────────────────
25
35
  /**
26
36
  * Hardcoded stash list used when the registry is unreachable.
@@ -32,6 +42,7 @@ const FALLBACK_STASHES = [
32
42
  name: "itlackey/akm-stash",
33
43
  description: "Official AKM onboarding bundle",
34
44
  url: "https://github.com/itlackey/akm-stash",
45
+ installType: "git",
35
46
  source: "fallback",
36
47
  defaultSelected: true,
37
48
  },
@@ -40,6 +51,7 @@ const FALLBACK_STASHES = [
40
51
  name: "andrewyng/context-hub",
41
52
  description: "Optional community prompt and context bundle",
42
53
  url: "https://github.com/andrewyng/context-hub",
54
+ installType: "git",
43
55
  source: "fallback",
44
56
  defaultSelected: false,
45
57
  },
@@ -62,19 +74,27 @@ export function _setLoadSetupStashesForTests(fake) {
62
74
  * @param timeoutMs Fetch timeout in ms (default: 4000).
63
75
  */
64
76
  export async function loadSetupStashes(registryUrl, timeoutMs = 4000) {
77
+ if (hasRegistryUrlCredentials(registryUrl))
78
+ return FALLBACK_STASHES;
65
79
  if (loadSetupStashesOverride)
66
80
  return loadSetupStashesOverride(registryUrl, timeoutMs);
67
81
  return loadSetupStashesReal(registryUrl, timeoutMs);
68
82
  }
69
83
  async function loadSetupStashesReal(registryUrl, timeoutMs = 4000) {
70
84
  try {
71
- const response = await fetch(registryUrl, {
72
- signal: AbortSignal.timeout(timeoutMs),
73
- headers: { Accept: "application/json" },
85
+ const response = await fetchRegistryResponse(registryUrl, { headers: { Accept: "application/json" } }, {
86
+ policy: { kind: "public-registry" },
87
+ timeoutMs,
88
+ retries: 0,
89
+ allowPrivateHostsForTesting: allowPrivateRegistryFixtureForTests(registryUrl),
74
90
  });
75
- if (!response.ok)
91
+ if (!response.ok) {
92
+ await cancelRegistryResponse(response);
76
93
  return FALLBACK_STASHES;
77
- const raw = (await response.json());
94
+ }
95
+ const raw = await jsonWithByteCap(response, 10 * 1024 * 1024, {
96
+ bodyTimeoutMs: timeoutMs,
97
+ });
78
98
  if (!Array.isArray(raw.stashes) || raw.stashes.length === 0)
79
99
  return FALLBACK_STASHES;
80
100
  const entries = raw.stashes.flatMap((item) => {
@@ -84,22 +104,17 @@ async function loadSetupStashesReal(registryUrl, timeoutMs = 4000) {
84
104
  const id = typeof s.id === "string" ? s.id : "";
85
105
  const name = typeof s.name === "string" ? s.name : id;
86
106
  const description = typeof s.description === "string" ? s.description : "";
87
- // Prefer github/git source URL built from the ref; fall back to homepage
88
- const url = (s.source === "github" || s.source === "git") && typeof s.ref === "string"
89
- ? `https://github.com/${s.ref.replace(/^github:/, "")}`
90
- : typeof s.homepage === "string"
91
- ? s.homepage
92
- : "";
93
- if (!id || !url)
107
+ const target = setupInstallTarget(s.source, s.ref);
108
+ if (!id || !target)
94
109
  return [];
95
110
  return [
96
111
  {
97
112
  id,
98
113
  name,
99
114
  description,
100
- url,
115
+ ...target,
101
116
  source: "registry",
102
- defaultSelected: DEFAULT_SELECTED_STASH_IDS.includes(id),
117
+ defaultSelected: authenticatedDefaultTarget(id, target),
103
118
  },
104
119
  ];
105
120
  });
@@ -109,3 +124,32 @@ async function loadSetupStashesReal(registryUrl, timeoutMs = 4000) {
109
124
  return FALLBACK_STASHES;
110
125
  }
111
126
  }
127
+ function authenticatedDefaultTarget(id, target) {
128
+ const expected = AUTHENTICATED_DEFAULT_TARGETS[id];
129
+ return expected?.installType === target.installType && expected.url === target.url;
130
+ }
131
+ function setupInstallTarget(source, ref) {
132
+ if (typeof ref !== "string")
133
+ return undefined;
134
+ try {
135
+ if (source === "npm") {
136
+ const candidate = ref.startsWith("npm:") ? ref : `npm:${ref}`;
137
+ const parsed = parseRegistryRef(candidate);
138
+ return parsed.source === "npm" ? { installType: "npm", url: candidate } : undefined;
139
+ }
140
+ if (source === "github") {
141
+ const candidate = ref.startsWith("github:") ? ref : `github:${ref}`;
142
+ const parsed = parseRegistryRef(candidate);
143
+ if (parsed.source !== "github")
144
+ return undefined;
145
+ return {
146
+ installType: "git",
147
+ url: `https://github.com/${encodeURIComponent(parsed.owner)}/${encodeURIComponent(parsed.repo)}`,
148
+ };
149
+ }
150
+ }
151
+ catch {
152
+ // Malformed registry install refs are omitted without breaking setup.
153
+ }
154
+ return undefined;
155
+ }
@@ -2,18 +2,16 @@
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
4
  /**
5
- * Semantic-search asset preparation for the setup wizard. Isolates the one
6
- * `bun add @huggingface/transformers` subprocess and the sqlite-vec probe so
7
- * the rest of setup stays free of subprocess/DB I/O.
5
+ * Semantic-search asset preparation for the setup wizard. Transformers.js is
6
+ * a normal external package dependency; setup never mutates the installed CLI.
7
+ * This module only prepares the model and probes sqlite-vec.
8
8
  */
9
9
  import fs from "node:fs";
10
10
  import os from "node:os";
11
11
  import path from "node:path";
12
12
  import * as p from "../cli/clack.js";
13
13
  import { isHttpUrl } from "../core/common.js";
14
- import { runManagedSubprocess } from "../core/subprocess.js";
15
14
  import { checkEmbeddingAvailability, DEFAULT_LOCAL_MODEL, isTransformersAvailable } from "../llm/embedder.js";
16
- import { getDirname } from "../runtime.js";
17
15
  import { closeDatabase, openIndexDatabase } from "../storage/repositories/index-connection.js";
18
16
  import { isVecAvailable } from "../storage/repositories/index-vec-repository.js";
19
17
  // Approximate first-download sizes used in the setup note.
@@ -41,34 +39,12 @@ export function describeSemanticSearchAssets(embedding) {
41
39
  }
42
40
  export async function prepareSemanticSearchAssets(config) {
43
41
  const remote = isRemoteEmbeddingConfig(config.embedding);
44
- // For local embeddings, ensure the required package is installed first.
42
+ // Local embeddings require the declared Transformers dependency.
45
43
  if (!remote) {
46
44
  if (!isTransformersAvailable()) {
47
- const spin = p.spinner();
48
- spin.start("Installing @huggingface/transformers...");
49
- try {
50
- const pkgRoot = path.resolve(getDirname(import.meta.url), "../..");
51
- // Bounded (10 min) so a stalled `bun add` can't hang setup forever.
52
- const result = await runManagedSubprocess(["bun", "add", "@huggingface/transformers"], {
53
- capture: true,
54
- cwd: pkgRoot,
55
- timeoutMs: 10 * 60_000,
56
- });
57
- if (result.spawnError)
58
- throw result.spawnError;
59
- if (result.exitCode !== 0) {
60
- throw new Error(result.stderr || `exit code ${result.exitCode}`);
61
- }
62
- spin.stop("@huggingface/transformers installed.");
63
- }
64
- catch (err) {
65
- const msg = err instanceof Error ? err.message : String(err);
66
- spin.stop("Could not install @huggingface/transformers.");
67
- p.log.warn(`Automatic install failed: ${msg}\n` +
68
- "Install it manually with: bun add @huggingface/transformers\n" +
69
- "Then re-run `akm setup` or `akm index --full --verbose`.");
70
- return { ok: false, reason: "missing-package", message: `Automatic install failed: ${msg}` };
71
- }
45
+ const message = "The @huggingface/transformers dependency is unavailable. Reinstall akm-cli, then re-run `akm setup` or `akm index --full --verbose`.";
46
+ p.log.warn(message);
47
+ return { ok: false, reason: "missing-package", message };
72
48
  }
73
49
  }
74
50
  const spin = p.spinner();
@@ -83,9 +59,8 @@ export async function prepareSemanticSearchAssets(config) {
83
59
  return { ok: false, reason: "remote-network", message: "The remote embedding endpoint is not reachable." };
84
60
  }
85
61
  else if (result.reason === "missing-package") {
86
- p.log.warn("@huggingface/transformers is not installed. Install it with: bun add @huggingface/transformers\n" +
87
- "Then re-run `akm setup` or `akm index --full --verbose`.");
88
- return { ok: false, reason: "missing-package", message: "@huggingface/transformers is not installed." };
62
+ p.log.warn("The @huggingface/transformers dependency is unavailable. Reinstall akm-cli, then re-run `akm setup` or `akm index --full --verbose`.");
63
+ return { ok: false, reason: "missing-package", message: "The local embedding runtime is unavailable." };
89
64
  }
90
65
  else {
91
66
  p.log.warn(`The local embedding model could not be downloaded: ${result.message}\n` +
@@ -41,6 +41,7 @@ import { detectHarnessConfigs } from "./harness-config-import.js";
41
41
  import { bail, prompt } from "./prompt.js";
42
42
  import { PROVIDER_DEFAULTS } from "./providers.js";
43
43
  import { prepareSemanticSearchAssets } from "./semantic-assets.js";
44
+ import { canonicalSetupGitUrl } from "./source-identity.js";
44
45
  import { createSetupContext, runSetupSteps } from "./steps.js";
45
46
  import { stepAgentConnection, stepLlm, stepOllama, stepSmallModelConnection } from "./steps/connection.js";
46
47
  import { stepOutputConfig } from "./steps/output.js";
@@ -66,7 +67,7 @@ import { stepScheduledTasks } from "./steps/tasks.js";
66
67
  * rules fire and config + cache writes route into `$stashDir/.akm/`
67
68
  * instead of the user's host `~/.config/akm`.
68
69
  */
69
- function assertSetupSandbox(stashDir, dirExplicitlyProvided) {
70
+ export function assertSetupSandbox(stashDir, dirExplicitlyProvided) {
70
71
  if (!dirExplicitlyProvided)
71
72
  return;
72
73
  if (process.env.AKM_FORCE_SETUP_TMP_STASH === "1")
@@ -102,28 +103,10 @@ function applyStashIsolationToEnv(stashDir, dirExplicitlyProvided) {
102
103
  /**
103
104
  * Raw preflight used before setup performs prompts, initialization, or writes.
104
105
  *
105
- * `setup` is allowlisted in `shouldBypassConfigStartup` (src/cli.ts) so it
106
- * never dies on the CLI's own startup config load but every entry point
107
- * below (`runSetupWizard`, `runSetupWithDefaults`, `runSetupFromConfig`,
108
- * `runResetRecommended`) still needs the CURRENT config as its merge base, so
109
- * this is the first thing each one calls. When the on-disk config predates
110
- * 0.9 (or is otherwise unparseable/invalid), `parseAndValidateConfigText`
111
- * throws `ConfigError`; deliberately let that propagate rather than falling
112
- * back to a fresh default config here.
113
- *
114
- * That fallback was considered and rejected: `mutateConfigWithPrecommit`
115
- * (src/core/config/config.ts) re-reads and re-validates this SAME file
116
- * inside its write lock before saving, so a wizard that "survived" this
117
- * preflight by substituting `DEFAULT_CONFIG` would still crash with the
118
- * identical error at the final save — after prompting the user through the
119
- * entire wizard. Worse, making the save path itself tolerate an old config
120
- * would mean writing a freshly-generated 0.9 config over the live 0.8 file
121
- * `akm migrate apply` needs to read as ITS OWN input — replacing the user's
122
- * real engine/source settings with the wizard's placeholder ones, silently,
123
- * on the one path (`akm migrate ...`) the upgrade docs tell users to run
124
- * first. Refusing here — before any prompt, detection, or write — is strictly
125
- * safer: the file is left byte-for-byte untouched and the error message
126
- * below points straight at the real recovery command.
106
+ * Setup merges into a current config, so it rejects an unreadable or obsolete
107
+ * file before prompting or writing. There is no runtime config translator: an
108
+ * operator who wants a fresh setup moves the old file aside, while an operator
109
+ * who wants to preserve its values rewrites them into the current schema.
127
110
  */
128
111
  export function assertSetupConfigPreflight() {
129
112
  const configPath = getConfigPath();
@@ -145,8 +128,7 @@ export function assertSetupConfigPreflight() {
145
128
  if (error.code !== "UNSUPPORTED_CONFIG_VERSION" && error.code !== "INVALID_CONFIG_FILE")
146
129
  throw error;
147
130
  throw new ConfigError(`\`akm setup\` cannot run: the config at ${configPath} did not load (${error.message}). ` +
148
- "It was left untouched — setup never writes over a config it cannot first read cleanly.", error.code, "Run `akm migrate status` to check what this config needs before continuing, or `akm help migrate 0.9.0` " +
149
- "for the full upgrade checklist. To abandon it and start fresh instead, move it aside " +
131
+ "It was left untouched — setup never writes over a config it cannot first read cleanly.", error.code, "Rewrite the file using the current config schema, or move it aside to start fresh " +
150
132
  `(e.g. \`mv ${configPath} ${configPath}.bak\`) and re-run \`akm setup\`.`);
151
133
  }
152
134
  }
@@ -261,10 +243,10 @@ function setupSourceDescriptor(source) {
261
243
  }
262
244
  function selectsExistingBundle(source, id, bundle) {
263
245
  const configured = bundleEntryToSourceEntry(id, bundle);
264
- return (configured !== undefined &&
265
- configured.type === source.type &&
266
- configured.path === source.path &&
267
- configured.url === source.url);
246
+ const sameUrl = configured?.type === "git" && source.type === "git" && configured.url && source.url
247
+ ? canonicalSetupGitUrl(configured.url) === canonicalSetupGitUrl(source.url)
248
+ : configured?.url === source.url;
249
+ return configured !== undefined && configured.type === source.type && configured.path === source.path && sameUrl;
268
250
  }
269
251
  /** Convert only the wizard's private drafts, leaving current bundle entries intact. */
270
252
  function finalizeSetupDraft(draft) {
@@ -914,7 +896,7 @@ export async function runSetupFromConfig(opts) {
914
896
  // Phase 2: Validate — only allow safe top-level keys
915
897
  for (const key of ["stashDir", "sources", "installed"]) {
916
898
  if (key in incoming) {
917
- throw new ConfigError(`${key} is not supported by the current config schema; run the standalone akm-migrate tool first.`, "INVALID_CONFIG_FILE");
899
+ throw new ConfigError(`${key} is not supported by the current config schema; configure bundles/defaultBundle instead.`, "INVALID_CONFIG_FILE");
918
900
  }
919
901
  }
920
902
  // Derived from AkmConfigShape (via `listTopLevelConfigKeys`) rather than a
@@ -0,0 +1,17 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * Canonical identity for Git sources selected by setup.
6
+ *
7
+ * Git accepts a terminal `.git` suffix as an optional spelling of the same
8
+ * remote. Registry, fallback, and historical setup entries have used both
9
+ * forms, so comparisons must not turn that spelling difference into a second
10
+ * configured bundle.
11
+ */
12
+ export function canonicalSetupGitUrl(value) {
13
+ return value
14
+ .trim()
15
+ .replace(/\/+$/, "")
16
+ .replace(/\.git$/i, "");
17
+ }
@@ -11,9 +11,20 @@ import { DEFAULT_CONFIG, getEffectiveRegistries, getSources } from "../../core/c
11
11
  import { readLockfile } from "../../integrations/lockfile.js";
12
12
  import { prompt, promptOrBack } from "../prompt.js";
13
13
  import { loadSetupStashes } from "../registry-stash-loader.js";
14
+ import { canonicalSetupGitUrl } from "../source-identity.js";
14
15
  function configuredSourceKey(source) {
15
16
  return `${source.type}:${source.path ?? source.url ?? source.name ?? "unknown"}`;
16
17
  }
18
+ function configuredSourceTarget(source) {
19
+ return source.type === "npm" ? source.path : source.url;
20
+ }
21
+ function configuredSourceIdentity(source) {
22
+ const target = configuredSourceTarget(source);
23
+ return source.type === "git" && target ? canonicalSetupGitUrl(target) : target;
24
+ }
25
+ function recommendedSourceIdentity(entry) {
26
+ return entry.installType === "git" ? canonicalSetupGitUrl(entry.url) : entry.url;
27
+ }
17
28
  function describeConfiguredSource(source) {
18
29
  const target = source.path ?? source.url ?? "(unknown target)";
19
30
  const typeLabel = source.type === "git" ? "Git" : source.type === "filesystem" ? "Filesystem" : source.type;
@@ -68,10 +79,13 @@ export async function stepAdditionalSources(currentSources) {
68
79
  }));
69
80
  if (name === null)
70
81
  continue;
71
- const entry = { type: "git", url: url.trim() };
82
+ const entryUrl = url.trim();
83
+ const entry = { type: "git", url: entryUrl };
72
84
  if (name.trim())
73
85
  entry.name = name.trim();
74
- if (!sources.some((s) => s.url === entry.url)) {
86
+ if (!sources.some((source) => source.type === "git" &&
87
+ source.url !== undefined &&
88
+ canonicalSetupGitUrl(source.url) === canonicalSetupGitUrl(entryUrl))) {
75
89
  sources.push(entry);
76
90
  }
77
91
  else {
@@ -183,16 +197,18 @@ export async function stepAddSources(current, options) {
183
197
  "https://raw.githubusercontent.com/itlackey/akm-registry/main/index.json";
184
198
  const availableStashes = await loadSetupStashes(registryUrl);
185
199
  if (availableStashes.length > 0) {
186
- const existingUrls = new Set(sources.map((s) => s.url));
200
+ const existingTargets = new Set(sources.map(configuredSourceIdentity).filter((value) => !!value));
187
201
  const stashOptions = availableStashes.map((s) => ({
188
- value: s.url,
202
+ value: recommendedSourceIdentity(s),
189
203
  label: s.name,
190
- hint: existingUrls.has(s.url) ? `${s.description} (already added)` : s.description || s.source,
204
+ hint: existingTargets.has(recommendedSourceIdentity(s))
205
+ ? `${s.description} (already added)`
206
+ : s.description || s.source,
191
207
  }));
192
208
  // Pre-check: already-installed stashes OR default-selected on fresh install
193
209
  const initialValues = sources.length > 0
194
- ? stashOptions.filter((o) => existingUrls.has(o.value)).map((o) => o.value)
195
- : availableStashes.filter((s) => s.defaultSelected).map((s) => s.url);
210
+ ? stashOptions.filter((o) => existingTargets.has(o.value)).map((o) => o.value)
211
+ : availableStashes.filter((s) => s.defaultSelected).map(recommendedSourceIdentity);
196
212
  const selectedUrls = await prompt(() => p.multiselect({
197
213
  message: availableStashes[0]?.source === "registry"
198
214
  ? "Available bundles from the AKM registry — toggle to add or remove:"
@@ -202,20 +218,25 @@ export async function stepAddSources(current, options) {
202
218
  required: false,
203
219
  }));
204
220
  // Add newly selected stashes
205
- for (const url of selectedUrls) {
206
- if (!existingUrls.has(url)) {
207
- const entry = availableStashes.find((s) => s.url === url);
208
- sources.push({ type: "git", url, name: entry?.name });
209
- existingUrls.add(url);
221
+ for (const target of selectedUrls) {
222
+ if (!existingTargets.has(target)) {
223
+ const entry = availableStashes.find((s) => recommendedSourceIdentity(s) === target);
224
+ if (!entry)
225
+ continue;
226
+ sources.push(entry.installType === "npm"
227
+ ? { type: "npm", path: target, name: entry.name }
228
+ : { type: "git", url: target, name: entry.name });
229
+ existingTargets.add(target);
210
230
  }
211
231
  }
212
232
  // Remove deselected stashes that were previously configured
213
233
  for (const entry of availableStashes) {
214
- if (existingUrls.has(entry.url) && !selectedUrls.includes(entry.url)) {
215
- const idx = sources.findIndex((s) => s.url === entry.url);
234
+ const target = recommendedSourceIdentity(entry);
235
+ if (existingTargets.has(target) && !selectedUrls.includes(target)) {
236
+ const idx = sources.findIndex((source) => configuredSourceIdentity(source) === target);
216
237
  if (idx !== -1) {
217
238
  sources.splice(idx, 1);
218
- existingUrls.delete(entry.url);
239
+ existingTargets.delete(target);
219
240
  p.log.info(`Removed ${entry.name}.`);
220
241
  }
221
242
  }
@@ -7,14 +7,14 @@ import os from "node:os";
7
7
  import path from "node:path";
8
8
  import { parse as yamlParse, stringify as yamlStringify } from "yaml";
9
9
  import * as p from "../../cli/clack.js";
10
- import { akmTasksSync, setEnabledInYaml } from "../../commands/tasks/tasks.js";
10
+ import { akmTasksSync } from "../../commands/tasks/tasks.js";
11
11
  import { loadConfig } from "../../core/config/config.js";
12
12
  import { UsageError } from "../../core/errors.js";
13
13
  import { commitWriteTargetBoundary, deleteAssetFromSource, prepareWriteTargetForMutation, resolveWriteTarget, writeAssetToSource, } from "../../core/write-source.js";
14
14
  import { backendNameForPlatform } from "../../tasks/backends/index.js";
15
15
  import { listEmbeddedTasks } from "../../tasks/embedded.js";
16
- import { parseTaskDocument } from "../../tasks/parser.js";
17
16
  import { parseSchedule } from "../../tasks/schedule.js";
17
+ import { parseTaskV3Yaml } from "../../tasks/source-v3.js";
18
18
  import { prompt } from "../prompt.js";
19
19
  /**
20
20
  * A scheduled server-only nightly full sweep exists among the embedded
@@ -71,6 +71,15 @@ export function detectServerDefault() {
71
71
  function normaliseTaskIdForMatch(raw) {
72
72
  return raw.trim().replace(/\.(yml|md)$/, "");
73
73
  }
74
+ function setTaskV3EnabledInYaml(yaml, enabled) {
75
+ const document = yamlParse(yaml);
76
+ const akm = document.akm;
77
+ if (!akm || typeof akm !== "object" || Array.isArray(akm)) {
78
+ throw new UsageError("Task v3 source must declare an akm mapping before setup can change enabled state.");
79
+ }
80
+ akm.enabled = enabled;
81
+ return yamlStringify(document);
82
+ }
74
83
  export function listSetupTaskDefinitions() {
75
84
  const config = loadConfig();
76
85
  const target = resolveWriteTarget(config, config.defaultBundle, { requireWritable: false });
@@ -84,8 +93,21 @@ export function listSetupTaskDefinitions() {
84
93
  const id = file.slice(0, -4);
85
94
  const filePath = path.join(taskDir, file);
86
95
  try {
87
- const task = parseTaskDocument({ yaml: fs.readFileSync(filePath, "utf8"), filePath, id });
88
- tasks.push({ id: task.id, schedule: task.schedule, enabled: task.enabled, description: task.description });
96
+ const task = parseTaskV3Yaml({
97
+ yaml: fs.readFileSync(filePath, "utf8"),
98
+ filePath,
99
+ workspaceRoot: target.source.path,
100
+ });
101
+ if (task.triggers.schedules.length === 0)
102
+ continue;
103
+ const schedules = task.triggers.schedules.map((schedule) => schedule.cron);
104
+ tasks.push({
105
+ id,
106
+ schedule: schedules[0],
107
+ schedules: Object.freeze(schedules),
108
+ enabled: task.akm?.enabled !== false,
109
+ ...(task.akm?.description !== undefined ? { description: task.akm.description } : {}),
110
+ });
89
111
  }
90
112
  catch (error) {
91
113
  throw new UsageError(`Cannot review task definition ${filePath}: ${error instanceof Error ? error.message : String(error)} ` +
@@ -106,16 +128,19 @@ export async function prepareSetupTaskDefinitions(tasks, deps = {}) {
106
128
  const original = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf8") : undefined;
107
129
  let yaml;
108
130
  if (original !== undefined) {
109
- yaml = setEnabledInYaml(original, plan.enabled);
131
+ yaml = setTaskV3EnabledInYaml(original, plan.enabled);
110
132
  }
111
133
  else {
112
134
  const document = yamlParse(plan.task.yaml);
113
- document.schedule = plan.schedule;
114
- document.enabled = plan.enabled;
135
+ const akm = document.akm;
136
+ akm.schedule = plan.schedule;
137
+ akm.enabled = plan.enabled;
115
138
  yaml = yamlStringify(document);
116
139
  }
117
- parseTaskDocument({ yaml, filePath, id: plan.task.id });
118
- parseSchedule(plan.schedule, backendNameForPlatform());
140
+ const parsed = parseTaskV3Yaml({ yaml, filePath, workspaceRoot: target.source.path });
141
+ for (const schedule of parsed.triggers.schedules) {
142
+ parseSchedule(schedule.cron, backendNameForPlatform());
143
+ }
119
144
  return { filePath, original, yaml, ref: { type: "task", name: plan.task.id } };
120
145
  });
121
146
  const changed = prepared.filter((entry) => entry.original !== entry.yaml);
@@ -198,7 +223,7 @@ export async function stepScheduledTasks(deps = DEFAULT_SCHEDULED_TASKS_DEPS, op
198
223
  initialValues: preChecked,
199
224
  options: embedded.map((task) => {
200
225
  const current = byId.get(task.id);
201
- const schedule = current?.schedule ?? task.schedule;
226
+ const schedule = current ? displayTaskSchedules(current) : task.schedule;
202
227
  const state = current ? (current.enabled ? "enabled" : "disabled") : "not prepared";
203
228
  return {
204
229
  value: task.id,
@@ -241,7 +266,7 @@ export async function stepScheduledTasks(deps = DEFAULT_SCHEDULED_TASKS_DEPS, op
241
266
  const custom = installed.filter((task) => !embeddedIds.has(normaliseTaskIdForMatch(task.id)));
242
267
  p.note([
243
268
  ...plans.map((plan) => `${plan.task.label}: ${plan.enabled ? "enabled" : "disabled"} | ${plan.schedule} | ${plan.task.description}`),
244
- ...custom.map((task) => `${task.id}: ${task.enabled ? "enabled" : "disabled"} | ${task.schedule}${task.description ? ` | ${task.description}` : ""}`),
269
+ ...custom.map((task) => `${task.id}: ${task.enabled ? "enabled" : "disabled"} | ${displayTaskSchedules(task)}${task.description ? ` | ${task.description}` : ""}`),
245
270
  ].join("\n"), "Task Schedule Review");
246
271
  const activate = await prompt(() => p.confirm({
247
272
  message: `Activate these schedules now? This will update task files and sync them to the ${backendNameForPlatform()} scheduler.`,
@@ -267,3 +292,6 @@ export async function stepScheduledTasks(deps = DEFAULT_SCHEDULED_TASKS_DEPS, op
267
292
  }
268
293
  p.log.success("Task schedules activated. Verify them with `akm task doctor`.");
269
294
  }
295
+ function displayTaskSchedules(task) {
296
+ return task.schedules && task.schedules.length > 0 ? task.schedules.join(", ") : task.schedule;
297
+ }
@@ -57,9 +57,9 @@ function resolveGitContentDir(config) {
57
57
  // ── Self-register ───────────────────────────────────────────────────────────
58
58
  registerSourceProvider("git", (config) => new GitSourceProvider(config));
59
59
  // ── Cache management ────────────────────────────────────────────────────────
60
- export function getCachePaths(repoUrl) {
60
+ export function getCachePaths(repoUrl, cacheRootOverride) {
61
61
  const key = createHash("sha256").update(repoUrl).digest("hex").slice(0, 16);
62
- const cacheRoot = getRegistryIndexCacheDir();
62
+ const cacheRoot = cacheRootOverride ?? getRegistryIndexCacheDir();
63
63
  const rootDir = path.join(cacheRoot, `git-${key}`);
64
64
  return {
65
65
  rootDir,
@@ -101,7 +101,7 @@ export async function syncMirroredRepo(config, options) {
101
101
  throw new ConfigError("git stash entry requires a URL when no install ref is supplied");
102
102
  }
103
103
  const repo = parseGitRepoUrl(config.url);
104
- const cachePaths = getCachePaths(repo.canonicalUrl);
104
+ const cachePaths = getCachePaths(repo.canonicalUrl, options?.cacheRootDir);
105
105
  await ensureGitMirror(repo, cachePaths, {
106
106
  requireRepoDir: true,
107
107
  writable: options?.writable ?? config.writable === true,
@@ -13,7 +13,7 @@ import fs from "node:fs";
13
13
  import path from "node:path";
14
14
  import { ConfigError, UsageError } from "../../core/errors.js";
15
15
  import { getRegistryCacheDir } from "../../core/paths.js";
16
- import { parseRegistryRef, resolveRegistryArtifact } from "../../registry/resolve.js";
16
+ import { npmArtifactNetworkPolicy, parseRegistryRef, resolveRegistryArtifact } from "../../registry/resolve.js";
17
17
  import { registerSourceProvider } from "../provider-factory.js";
18
18
  import { applyAkmIncludeConfig, buildInstallCacheDir, computeFileHash, detectStashRoot, downloadArchive, isDirectory, } from "./provider-utils.js";
19
19
  import { extractTarGzSecure, verifyArchiveIntegrity } from "./tar-utils.js";
@@ -108,7 +108,7 @@ async function doSyncNpm(parsed, options) {
108
108
  let installRoot;
109
109
  let stashRoot;
110
110
  try {
111
- await downloadArchive(resolved.artifactUrl, archivePath);
111
+ await downloadArchive(resolved.artifactUrl, archivePath, npmArtifactNetworkPolicy(resolved));
112
112
  verifyArchiveIntegrity(archivePath, resolved.resolvedRevision, resolved.source);
113
113
  integrity = await computeFileHash(archivePath);
114
114
  extractTarGzSecure(archivePath, extractedDir);
@@ -6,7 +6,7 @@ import fs from "node:fs";
6
6
  import path from "node:path";
7
7
  import { getAdapters } from "../../core/adapter/registry.js";
8
8
  import { stashDirNames } from "../../core/asset/asset-placement.js";
9
- import { fetchWithRetry } from "../../core/common.js";
9
+ import { cancelRegistryResponse, fetchRegistryResponse } from "../../registry/network.js";
10
10
  import { writeResponseToFileCapped } from "../../runtime.js";
11
11
  import { copyIncludedPaths, findNearestIncludeConfig } from "../include.js";
12
12
  const REGISTRY_STASH_DIR_NAMES = new Set(stashDirNames());
@@ -99,9 +99,10 @@ export function applyAkmIncludeConfig(sourceRoot, cacheDir, searchRoot = sourceR
99
99
  return selectedDir;
100
100
  }
101
101
  /** Stream a remote archive to disk via the runtime boundary's response writer. */
102
- export async function downloadArchive(url, destination) {
103
- const response = await fetchWithRetry(url, undefined, { timeout: 120_000 });
102
+ export async function downloadArchive(url, destination, networkPolicy) {
103
+ const response = await fetchRegistryResponse(url, undefined, { policy: networkPolicy, timeoutMs: 120_000 });
104
104
  if (!response.ok) {
105
+ await cancelRegistryResponse(response);
105
106
  throw new Error(`Failed to download archive (${response.status}) from ${url}`);
106
107
  }
107
108
  // Stream to disk with an explicit byte cap + body-read deadline: the fetch
@@ -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 { ConfigError } from "../../core/errors.js";
4
5
  import { registerSourceProvider } from "../provider-factory.js";
5
- import { ensureWebsiteMirror, getWebsiteCachePaths, shouldAllowPrivateWebsiteUrlForTests, validateWebsiteUrl, } from "../snapshot-fetchers/website-ingest.js";
6
- /**
7
- * Website source provider — thin adapter over the shared website ingest module.
8
- */
9
- registerSourceProvider("website", (config) => {
6
+ import { getWebsiteCachePaths, shouldAllowPrivateWebsiteUrlForTests, validateWebsiteUrl } from "../website-url.js";
7
+ /** Website source provider — URL-derived path plus an injected mirror refresh. */
8
+ export function createWebsiteProvider(config) {
10
9
  const allowPrivateHosts = shouldAllowPrivateWebsiteUrlForTests(config.url ?? "");
11
10
  const url = validateWebsiteUrl(config.url ?? "", { allowPrivateHosts });
12
11
  const name = config.name ?? "website";
@@ -17,10 +16,14 @@ registerSourceProvider("website", (config) => {
17
16
  return getWebsiteCachePaths(url).rootDir;
18
17
  },
19
18
  async sync(options) {
19
+ const ensureWebsiteMirror = options?.ensureWebsiteMirror;
20
+ if (!ensureWebsiteMirror) {
21
+ throw new ConfigError("Website provider sync requires an injected ensureWebsiteMirror capability");
22
+ }
20
23
  await ensureWebsiteMirror(config, {
21
24
  requireStashDir: true,
22
25
  force: options?.force,
23
- // Feed the resolver into the EXISTING website-ingest plumbing that
26
+ // Feed the resolver into the injected mirror plumbing that
24
27
  // already threads `resolveSecret` down to `FetcherContext`. This is the
25
28
  // line that closes the bundle-update / sync() gap: previously the X
26
29
  // fetcher on this path saw only `X_BEARER_TOKEN`, never the store.
@@ -29,4 +32,5 @@ registerSourceProvider("website", (config) => {
29
32
  });
30
33
  },
31
34
  };
32
- });
35
+ }
36
+ registerSourceProvider("website", createWebsiteProvider);