akm-cli 0.9.0-rc.8 → 0.9.0

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 (450) hide show
  1. package/CHANGELOG.md +1063 -44
  2. package/README.md +51 -25
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +497 -0
  5. package/dist/akm +148 -35
  6. package/dist/{akm-migrate-storage → akm-migrate} +6 -9
  7. package/dist/assets/hints/cli-hints-full.md +223 -95
  8. package/dist/assets/hints/cli-hints-short.md +85 -22
  9. package/dist/assets/improve-strategies/default.json +1 -1
  10. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  11. package/dist/assets/prompts/memory-infer-user.md +2 -3
  12. package/dist/assets/stash-skeleton/README.md +6 -5
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
  24. package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
  25. package/dist/assets/tasks/core/extract.yml +1 -1
  26. package/dist/assets/tasks/core/version-check.yml +1 -1
  27. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  28. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  29. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  30. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  31. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  32. package/dist/assets/templates/html/health.html +1 -3
  33. package/dist/assets/workflows/workflow-template.md +32 -15
  34. package/dist/cli/invocation.js +40 -15
  35. package/dist/cli/parse-args.js +0 -22
  36. package/dist/cli/retired-commands.js +121 -0
  37. package/dist/cli/shared.js +154 -22
  38. package/dist/cli/unknown-flags.js +236 -0
  39. package/dist/cli-node.mjs +2 -1
  40. package/dist/cli.js +696 -258
  41. package/dist/commands/agent/agent-dispatch.js +14 -3
  42. package/dist/commands/agent/contribute-cli.js +73 -88
  43. package/dist/commands/completions.js +79 -22
  44. package/dist/commands/config-cli.js +17 -150
  45. package/dist/commands/env/env-cli.js +59 -143
  46. package/dist/commands/env/env.js +12 -163
  47. package/dist/commands/env/marker-path.js +6 -0
  48. package/dist/commands/env/secret-cli.js +36 -66
  49. package/dist/commands/env/secret.js +24 -57
  50. package/dist/commands/feedback-cli.js +141 -87
  51. package/dist/commands/health/accept-rate.js +58 -0
  52. package/dist/commands/health/advisories.js +3 -4
  53. package/dist/commands/health/checks.js +85 -23
  54. package/dist/commands/health/html-report.js +7 -10
  55. package/dist/commands/health/improve-metrics.js +25 -83
  56. package/dist/commands/health/md-report.js +5 -9
  57. package/dist/commands/health/metrics.js +62 -20
  58. package/dist/commands/health/renderers.js +47 -0
  59. package/dist/commands/health/report-view-model.js +4 -5
  60. package/dist/commands/health/stash-exposure.js +1 -1
  61. package/dist/commands/health/surfaces.js +3 -48
  62. package/dist/commands/health/task-runs.js +3 -67
  63. package/dist/commands/health/types-improve.js +7 -0
  64. package/dist/commands/health.js +99 -28
  65. package/dist/commands/improve/anti-collapse.js +2 -2
  66. package/dist/commands/improve/autonomy-gate.js +68 -0
  67. package/dist/commands/improve/collapse-detector.js +41 -40
  68. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  69. package/dist/commands/improve/consolidate/merge.js +4 -0
  70. package/dist/commands/improve/consolidate.js +140 -1000
  71. package/dist/commands/improve/distill/promote-memory.js +12 -12
  72. package/dist/commands/improve/distill/quality-gate.js +6 -6
  73. package/dist/commands/improve/distill.js +58 -69
  74. package/dist/commands/improve/eligibility.js +105 -57
  75. package/dist/commands/improve/extract-cli.js +14 -133
  76. package/dist/commands/improve/improve-cli.js +98 -114
  77. package/dist/commands/improve/improve-result-file.js +1 -28
  78. package/dist/commands/improve/improve-strategies.js +8 -5
  79. package/dist/commands/improve/improve.js +128 -91
  80. package/dist/commands/improve/loop-stages.js +182 -20
  81. package/dist/commands/improve/memory/derived-ref.js +45 -43
  82. package/dist/commands/improve/memory/memory-belief.js +1 -1
  83. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  84. package/dist/commands/improve/memory/memory-improve.js +6 -5
  85. package/dist/commands/improve/outcome-loop.js +22 -65
  86. package/dist/commands/improve/preparation.js +114 -123
  87. package/dist/commands/improve/proactive-maintenance.js +2 -5
  88. package/dist/commands/improve/reflect.js +56 -160
  89. package/dist/commands/improve/salience.js +11 -122
  90. package/dist/commands/improve/source-identity.js +10 -38
  91. package/dist/commands/lint/base-linter.js +20 -124
  92. package/dist/commands/lint/env-key-rules.js +31 -47
  93. package/dist/commands/lint/index.js +249 -43
  94. package/dist/commands/{events.js → log.js} +33 -38
  95. package/dist/commands/migrate-cli.js +92 -12
  96. package/dist/commands/migration-tool.js +46 -0
  97. package/dist/commands/observability-cli.js +70 -209
  98. package/dist/commands/proposal/drain.js +101 -29
  99. package/dist/commands/proposal/proposal-cli.js +76 -48
  100. package/dist/commands/proposal/proposal.js +54 -18
  101. package/dist/commands/proposal/propose-cli.js +88 -0
  102. package/dist/commands/proposal/propose.js +23 -15
  103. package/dist/commands/proposal/repository.js +701 -278
  104. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  105. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  106. package/dist/commands/proposal/validators/proposals.js +4 -7
  107. package/dist/commands/read/curate.js +34 -53
  108. package/dist/commands/read/knowledge.js +150 -95
  109. package/dist/commands/read/registry-search.js +2 -2
  110. package/dist/commands/read/remember-cli.js +42 -15
  111. package/dist/commands/read/search-cli.js +180 -78
  112. package/dist/commands/read/search.js +58 -43
  113. package/dist/commands/read/show.js +197 -141
  114. package/dist/commands/registry-cli.js +12 -51
  115. package/dist/commands/remember.js +14 -57
  116. package/dist/commands/sources/add-cli.js +100 -31
  117. package/dist/commands/sources/bundle-cli.js +166 -0
  118. package/dist/commands/sources/bundle-config-ops.js +7 -2
  119. package/dist/commands/sources/info.js +18 -5
  120. package/dist/commands/sources/init.js +12 -12
  121. package/dist/commands/sources/installed-stashes.js +382 -98
  122. package/dist/commands/sources/schema-repair.js +3 -2
  123. package/dist/commands/sources/self-update.js +131 -38
  124. package/dist/commands/sources/source-add.js +72 -17
  125. package/dist/commands/sources/source-clone.js +129 -45
  126. package/dist/commands/sources/source-manage.js +43 -23
  127. package/dist/commands/sources/sources-cli.js +57 -208
  128. package/dist/commands/sources/stash-cli.js +46 -53
  129. package/dist/commands/tasks/tasks-cli.js +91 -97
  130. package/dist/commands/tasks/tasks.js +276 -421
  131. package/dist/commands/workflow-cli.js +175 -450
  132. package/dist/core/adapter/adapters/akm-adapter.js +47 -28
  133. package/dist/core/adapter/adapters/akm-lint.js +42 -27
  134. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  135. package/dist/core/adapter/adapters/akm-task-adapter.js +15 -13
  136. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  137. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  138. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  139. package/dist/core/adapter/adapters/index.js +6 -6
  140. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  141. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  142. package/dist/core/adapter/adapters/shared.js +3 -19
  143. package/dist/core/adapter/adapters/tool-dir-shared.js +8 -3
  144. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  145. package/dist/core/adapter/detect-adapter.js +17 -0
  146. package/dist/core/adapter/recognize-match.js +6 -4
  147. package/dist/core/adapter/validate-context.js +214 -0
  148. package/dist/core/asset/akm-markdown.js +63 -0
  149. package/dist/core/asset/asset-placement.js +20 -6
  150. package/dist/core/asset/asset-ref.js +11 -9
  151. package/dist/core/asset/frontmatter-lint.js +30 -0
  152. package/dist/core/asset/frontmatter.js +37 -9
  153. package/dist/core/asset/markdown.js +40 -51
  154. package/dist/core/asset/resolve-ref.js +89 -18
  155. package/dist/core/asset/stash-meta.js +1 -1
  156. package/dist/core/bundle-id.js +51 -0
  157. package/dist/core/common.js +152 -38
  158. package/dist/core/config/config-io.js +12 -1
  159. package/dist/core/config/config-schema.js +35 -8
  160. package/dist/core/config/config-sources.js +55 -11
  161. package/dist/core/config/config-walker.js +25 -9
  162. package/dist/core/config/config.js +9 -48
  163. package/dist/core/config/experimental.js +21 -0
  164. package/dist/core/config/schema/embedding.js +5 -1
  165. package/dist/core/config/schema/experimental.js +30 -0
  166. package/dist/core/config/schema/improve-processes.js +0 -6
  167. package/dist/core/config/schema/improve.js +21 -3
  168. package/dist/core/config/schema/index-config.js +8 -15
  169. package/dist/core/config/schema/output.js +4 -1
  170. package/dist/core/config/schema/setup.js +9 -18
  171. package/dist/core/config/schema/sources-bundles.js +49 -33
  172. package/dist/core/config/schema/workflow.js +3 -3
  173. package/dist/core/env-secret-ref.js +76 -46
  174. package/dist/core/errors.js +18 -12
  175. package/dist/core/events.js +46 -128
  176. package/dist/core/file-change.js +6 -5
  177. package/dist/core/fs-txn.js +83 -7
  178. package/dist/core/git-message.js +2 -2
  179. package/dist/core/improve-result.js +1 -100
  180. package/dist/core/lesson-lint.js +1 -17
  181. package/dist/core/logs-db.js +2 -1
  182. package/dist/core/migration-operation.js +16 -0
  183. package/dist/core/mutation-target.js +78 -0
  184. package/dist/core/parse.js +4 -1
  185. package/dist/core/paths.js +17 -20
  186. package/dist/core/recognition-util.js +12 -14
  187. package/dist/core/redaction.js +34 -0
  188. package/dist/core/standards/resolve-standards-context.js +2 -14
  189. package/dist/core/standards/resolve-stash-standards.js +2 -2
  190. package/dist/core/standards/resolve-type-conventions.js +2 -2
  191. package/dist/core/state/migrations.js +41 -18
  192. package/dist/core/state-db.js +5 -14
  193. package/dist/core/structured.js +1 -1
  194. package/dist/core/subprocess.js +6 -4
  195. package/dist/core/text-truncation.js +9 -5
  196. package/dist/core/type-presentation.js +3 -3
  197. package/dist/core/warn.js +0 -3
  198. package/dist/core/write-source.js +771 -95
  199. package/dist/indexer/bundle-identity-guard.js +3 -2
  200. package/dist/indexer/db/graph-db.js +0 -24
  201. package/dist/indexer/ensure-index.js +1 -0
  202. package/dist/indexer/graph/graph-boost.js +9 -34
  203. package/dist/indexer/graph/graph-extraction.js +8 -5
  204. package/dist/indexer/index-writer-lock.js +53 -17
  205. package/dist/indexer/index-written-assets.js +16 -22
  206. package/dist/indexer/indexer.js +497 -239
  207. package/dist/indexer/installations.js +14 -96
  208. package/dist/indexer/passes/dir-staleness.js +16 -9
  209. package/dist/indexer/passes/memory-inference.js +11 -9
  210. package/dist/indexer/passes/metadata.js +113 -47
  211. package/dist/indexer/scan/doc-to-entry.js +38 -1
  212. package/dist/indexer/scan/drain-dir.js +13 -23
  213. package/dist/indexer/search/db-search.js +99 -54
  214. package/dist/indexer/search/fts-query.js +47 -24
  215. package/dist/indexer/search/ranking-contributors.js +42 -20
  216. package/dist/indexer/search/ranking.js +18 -99
  217. package/dist/indexer/search/search-fields.js +7 -2
  218. package/dist/indexer/search/search-source.js +82 -93
  219. package/dist/indexer/usage/usage-events.js +0 -89
  220. package/dist/indexer/walk/file-context.js +2 -1
  221. package/dist/indexer/walk/matchers.js +30 -43
  222. package/dist/indexer/walk/path-resolver.js +7 -2
  223. package/dist/indexer/walk/walker.js +38 -12
  224. package/dist/integrations/agent/builders.js +0 -6
  225. package/dist/integrations/agent/config.js +2 -2
  226. package/dist/integrations/agent/detect.js +49 -19
  227. package/dist/integrations/agent/engine-fallback.js +76 -0
  228. package/dist/integrations/agent/profiles.js +14 -0
  229. package/dist/integrations/agent/prompts.js +12 -8
  230. package/dist/integrations/agent/runner-dispatch.js +4 -2
  231. package/dist/integrations/agent/runner.js +0 -1
  232. package/dist/integrations/agent/spawn.js +5 -6
  233. package/dist/integrations/github.js +1 -1
  234. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  235. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  236. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  237. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  238. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  239. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  240. package/dist/integrations/harnesses/index.js +3 -7
  241. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  242. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  243. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  244. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  245. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  246. package/dist/integrations/lockfile.js +101 -6
  247. package/dist/integrations/session-logs/index.js +3 -28
  248. package/dist/llm/client.js +136 -100
  249. package/dist/llm/embedders/remote.js +13 -5
  250. package/dist/llm/feature-gate.js +4 -12
  251. package/dist/llm/graph-extract.js +5 -11
  252. package/dist/llm/memory-infer.js +144 -1
  253. package/dist/llm/metadata-enhance.js +5 -7
  254. package/dist/llm/structured-call.js +1 -1
  255. package/dist/llm/usage-persist.js +26 -5
  256. package/dist/llm/usage-telemetry.js +25 -2
  257. package/dist/output/cli-hints.js +1 -2
  258. package/dist/output/context.js +22 -7
  259. package/dist/output/format-exempt.js +80 -0
  260. package/dist/output/generic-render.js +259 -0
  261. package/dist/output/render-registry.js +57 -0
  262. package/dist/output/renderers.js +14 -36
  263. package/dist/output/shapes/curate.js +10 -1
  264. package/dist/output/shapes/events.js +12 -7
  265. package/dist/output/shapes/helpers.js +56 -83
  266. package/dist/output/shapes/migrate.js +8 -0
  267. package/dist/output/shapes/passthrough.js +7 -41
  268. package/dist/output/shapes/proposal/producer.js +15 -7
  269. package/dist/output/shapes.js +2 -9
  270. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  271. package/dist/output/text/bundle-show.js +7 -0
  272. package/dist/output/text/command-format.js +164 -96
  273. package/dist/output/text/env.js +1 -3
  274. package/dist/output/text/events.js +8 -7
  275. package/dist/output/text/health-format.js +103 -0
  276. package/dist/output/text/health.js +7 -0
  277. package/dist/output/text/helpers.js +10 -8
  278. package/dist/output/text/lint-format.js +43 -0
  279. package/dist/output/text/{save.js → lint.js} +2 -2
  280. package/dist/output/text/migrate.js +88 -0
  281. package/dist/output/text/proposal/producer.js +4 -2
  282. package/dist/output/text/proposal-format.js +44 -72
  283. package/dist/output/text/registry-commands.js +1 -2
  284. package/dist/output/text/show-directives.js +15 -7
  285. package/dist/output/text/status-list.js +32 -0
  286. package/dist/output/text/sync.js +5 -0
  287. package/dist/output/text/workflow-format.js +24 -203
  288. package/dist/output/text/workflow.js +1 -7
  289. package/dist/output/text.js +16 -17
  290. package/dist/registry/factory.js +4 -6
  291. package/dist/registry/origin-resolve.js +16 -27
  292. package/dist/registry/providers/skills-sh.js +3 -3
  293. package/dist/registry/providers/static-index.js +13 -23
  294. package/dist/registry/resolve.js +42 -7
  295. package/dist/registry/semver.js +34 -84
  296. package/dist/runtime.js +2 -23
  297. package/dist/scripts/akm-migrate-node.js +60290 -0
  298. package/dist/scripts/akm-migrate.js +59628 -0
  299. package/dist/setup/detect.js +42 -15
  300. package/dist/setup/registry-stash-loader.js +2 -2
  301. package/dist/setup/setup.js +236 -136
  302. package/dist/setup/steps/connection.js +7 -9
  303. package/dist/setup/steps/platforms.js +9 -9
  304. package/dist/setup/steps/semantic.js +15 -3
  305. package/dist/setup/steps/sources.js +12 -13
  306. package/dist/setup/steps/stashdir.js +2 -3
  307. package/dist/setup/steps/tasks.js +237 -120
  308. package/dist/sources/freshness.js +1 -1
  309. package/dist/sources/provider-factory.js +11 -17
  310. package/dist/sources/providers/filesystem.js +2 -3
  311. package/dist/sources/providers/git-install.js +278 -34
  312. package/dist/sources/providers/git-provider.js +25 -23
  313. package/dist/sources/providers/git-stash.js +395 -106
  314. package/dist/sources/providers/git.js +2 -2
  315. package/dist/sources/providers/npm.js +16 -19
  316. package/dist/sources/providers/provider-utils.js +7 -4
  317. package/dist/sources/providers/sync-from-ref.js +3 -9
  318. package/dist/sources/providers/website.js +6 -1
  319. package/dist/sources/resolve.js +6 -5
  320. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  321. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  322. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  323. package/dist/sources/snapshot-fetchers/github.js +100 -0
  324. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  325. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  326. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  327. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  328. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  329. package/dist/sources/snapshot-fetchers/website-ingest.js +566 -268
  330. package/dist/sources/snapshot-fetchers/x.js +910 -0
  331. package/dist/storage/database.js +7 -0
  332. package/dist/storage/engines/sqlite-migrations.js +23 -111
  333. package/dist/storage/managed-db.js +2 -2
  334. package/dist/storage/repositories/canaries-repository.js +1 -1
  335. package/dist/storage/repositories/events-repository.js +27 -11
  336. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  337. package/dist/storage/repositories/index-connection.js +17 -6
  338. package/dist/storage/repositories/index-entries-repository.js +151 -240
  339. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  340. package/dist/storage/repositories/index-fts-repository.js +5 -2
  341. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  342. package/dist/storage/repositories/index-meta-repository.js +2 -3
  343. package/dist/storage/repositories/index-schema.js +10 -25
  344. package/dist/storage/repositories/index-utility-repository.js +15 -28
  345. package/dist/storage/repositories/index-vec-repository.js +6 -1
  346. package/dist/storage/repositories/outcome-repository.js +119 -0
  347. package/dist/storage/repositories/proposals-repository.js +296 -59
  348. package/dist/storage/repositories/registry-cache.js +19 -0
  349. package/dist/storage/repositories/salience-repository.js +172 -0
  350. package/dist/storage/repositories/task-history-repository.js +15 -13
  351. package/dist/storage/repositories/workflow-runs-repository.js +52 -40
  352. package/dist/tasks/backends/cron.js +105 -15
  353. package/dist/tasks/backends/index.js +1 -1
  354. package/dist/tasks/backends/launchd.js +85 -38
  355. package/dist/tasks/backends/schtasks.js +135 -15
  356. package/dist/tasks/embedded.js +56 -40
  357. package/dist/tasks/parser.js +7 -157
  358. package/dist/tasks/resolve-akm-bin.js +137 -59
  359. package/dist/tasks/runner.js +79 -42
  360. package/dist/tasks/scheduler-invocation.js +220 -10
  361. package/dist/tasks/schema.js +24 -1
  362. package/dist/tasks/task-id.js +1 -3
  363. package/dist/tasks/validator.js +20 -6
  364. package/dist/workflows/authoring/authoring.js +94 -143
  365. package/dist/workflows/authoring/scope-key.js +1 -1
  366. package/dist/workflows/exec/frozen-judge.js +28 -2
  367. package/dist/workflows/exec/native-executor.js +77 -57
  368. package/dist/workflows/exec/param-secrets.js +9 -9
  369. package/dist/workflows/exec/run-workflow.js +133 -79
  370. package/dist/workflows/exec/step-work.js +219 -346
  371. package/dist/{migrate-storage-node.mjs → workflows/exec/unit-dispatch.js} +1 -5
  372. package/dist/workflows/ir/compile.js +141 -270
  373. package/dist/workflows/ir/freeze.js +40 -30
  374. package/dist/workflows/ir/params.js +135 -11
  375. package/dist/workflows/ir/plan-hash.js +1 -1
  376. package/dist/workflows/ir/schema.js +25 -26
  377. package/dist/workflows/parser.js +872 -307
  378. package/dist/workflows/program/expressions.js +20 -208
  379. package/dist/workflows/program/schema.js +7 -10
  380. package/dist/workflows/renderer.js +95 -68
  381. package/dist/workflows/resource-limits.js +2 -0
  382. package/dist/workflows/runtime/checkin.js +3 -3
  383. package/dist/workflows/runtime/plan-classifier.js +16 -75
  384. package/dist/workflows/runtime/runs.js +186 -127
  385. package/dist/workflows/runtime/unit-checkin.js +1 -1
  386. package/dist/workflows/runtime/unit-phases.js +2 -2
  387. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  388. package/dist/workflows/schema.js +1 -11
  389. package/dist/workflows/validate-summary.js +30 -36
  390. package/dist/workflows/validator.js +21 -62
  391. package/docs/README.md +68 -0
  392. package/docs/migration/README.md +8 -0
  393. package/docs/migration/release-notes/0.7.0.md +11 -11
  394. package/docs/migration/release-notes/0.9.0.md +208 -27
  395. package/docs/migration/v0.7-to-v0.8.md +46 -47
  396. package/docs/migration/v0.8-to-v0.9.md +564 -208
  397. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  398. package/docs/reference/README.md +12 -0
  399. package/docs/reference/cli.md +2253 -0
  400. package/docs/reference/configuration.md +358 -0
  401. package/docs/reference/data-and-telemetry.md +105 -42
  402. package/docs/reference/workflows.md +647 -0
  403. package/package.json +22 -11
  404. package/schemas/akm-asset-envelope.json +93 -0
  405. package/schemas/akm-config.json +81 -128
  406. package/schemas/akm-workflow.json +74 -73
  407. package/dist/assets/tasks/core/backup.yml +0 -5
  408. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  409. package/dist/cli/config-migrate.js +0 -1806
  410. package/dist/cli/config-validate.js +0 -41
  411. package/dist/commands/backup-cli.js +0 -56
  412. package/dist/commands/bundle/bundle-cli.js +0 -68
  413. package/dist/commands/bundle/bundle.js +0 -219
  414. package/dist/commands/graph/graph-cli.js +0 -124
  415. package/dist/commands/graph/graph.js +0 -489
  416. package/dist/commands/improve/extract-watch.js +0 -140
  417. package/dist/commands/mv-cli.js +0 -1221
  418. package/dist/commands/sources/history.js +0 -201
  419. package/dist/commands/tasks/default-tasks.js +0 -186
  420. package/dist/core/migration-backup.js +0 -1234
  421. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  422. package/dist/llm/memory-infer-impl.js +0 -138
  423. package/dist/migrate/legacy/config-source-migration.js +0 -223
  424. package/dist/migrate/legacy/content-migration.js +0 -305
  425. package/dist/migrate/legacy/legacy-layout.js +0 -779
  426. package/dist/migrate/legacy/legacy-paths.js +0 -25
  427. package/dist/migrate/legacy/legacy-stash-json.js +0 -72
  428. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  429. package/dist/migrate/legacy/task-target-ref-migration.js +0 -272
  430. package/dist/migrate/legacy/three-db-cutover.js +0 -841
  431. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  432. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  433. package/dist/migrate/legacy-ref-grammar.js +0 -214
  434. package/dist/output/shapes/distill.js +0 -14
  435. package/dist/output/shapes/history.js +0 -11
  436. package/dist/output/text/distill.js +0 -6
  437. package/dist/output/text/enable-disable.js +0 -8
  438. package/dist/output/text/history.js +0 -6
  439. package/dist/registry/build-index.js +0 -382
  440. package/dist/schemas/akm-config.json +0 -4704
  441. package/dist/schemas/akm-task.json +0 -87
  442. package/dist/schemas/akm-workflow.json +0 -372
  443. package/dist/scripts/migrate-storage.js +0 -3816
  444. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  445. package/dist/workflows/cli.js +0 -53
  446. package/dist/workflows/exec/brief.js +0 -481
  447. package/dist/workflows/exec/report.js +0 -1460
  448. package/dist/workflows/exec/watch.js +0 -116
  449. package/dist/workflows/program/parser.js +0 -813
  450. package/dist/workflows/program/project.js +0 -104
@@ -0,0 +1,100 @@
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
+ import { GITHUB_API_BASE, githubHeaders } from "../../integrations/github.js";
5
+ import { htmlToMarkdown } from "./content-extract.js";
6
+ import { avoidReservedBasename } from "./fetcher-util.js";
7
+ import { fetchPinnedText } from "./host-guard.js";
8
+ const GITHUB_HOSTS = new Set(["github.com", "www.github.com"]);
9
+ const RESERVED_GITHUB_ROOTS = new Set([
10
+ "apps",
11
+ "collections",
12
+ "codespaces",
13
+ "customer-stories",
14
+ "education",
15
+ "enterprise",
16
+ "events",
17
+ "explore",
18
+ "features",
19
+ "issues",
20
+ "login",
21
+ "marketplace",
22
+ "new",
23
+ "notifications",
24
+ "orgs",
25
+ "organizations",
26
+ "pricing",
27
+ "pulls",
28
+ "search",
29
+ "security",
30
+ "settings",
31
+ "solutions",
32
+ "sponsors",
33
+ "topics",
34
+ "trending",
35
+ "users",
36
+ ]);
37
+ const GITHUB_README_BYTE_CAP = 5 * 1024 * 1024;
38
+ const GITHUB_README_BODY_TIMEOUT_MS = 30_000;
39
+ /** Match only repository roots; deeper GitHub pages stay generic web pages. */
40
+ export function extractGithubRepository(url) {
41
+ if (!GITHUB_HOSTS.has(url.hostname.toLowerCase()))
42
+ return null;
43
+ const segments = url.pathname.split("/").filter(Boolean);
44
+ if (segments.length !== 2)
45
+ return null;
46
+ let owner;
47
+ let repo;
48
+ try {
49
+ owner = decodeURIComponent(segments[0] ?? "");
50
+ repo = decodeURIComponent(segments[1] ?? "").replace(/\.git$/i, "");
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$/.test(owner))
56
+ return null;
57
+ if (RESERVED_GITHUB_ROOTS.has(owner.toLowerCase()))
58
+ return null;
59
+ if (!/^[A-Za-z0-9._-]{1,100}$/.test(repo) || repo === "." || repo === "..")
60
+ return null;
61
+ return { owner, repo };
62
+ }
63
+ const githubFetcher = {
64
+ name: "github-repository",
65
+ matches(url) {
66
+ return extractGithubRepository(url) !== null;
67
+ },
68
+ async fetch(url, context) {
69
+ const repository = extractGithubRepository(url);
70
+ if (!repository)
71
+ return null;
72
+ const apiUrl = `${GITHUB_API_BASE}/repos/${encodeURIComponent(repository.owner)}/${encodeURIComponent(repository.repo)}/readme`;
73
+ const headers = new Headers(githubHeaders(apiUrl));
74
+ headers.set("Accept", "application/vnd.github.html+json");
75
+ headers.set("User-Agent", "akm-cli github snapshot fetcher");
76
+ const html = await fetchPinnedText(apiUrl, {
77
+ headers,
78
+ byteCap: GITHUB_README_BYTE_CAP,
79
+ bodyTimeoutMs: GITHUB_README_BODY_TIMEOUT_MS,
80
+ timeoutMs: context.timeoutMs,
81
+ signal: context.signal,
82
+ });
83
+ if (!html)
84
+ return null;
85
+ const canonicalUrl = `https://github.com/${repository.owner}/${repository.repo}`;
86
+ const markdown = htmlToMarkdown(html, `${canonicalUrl}/`);
87
+ if (!markdown)
88
+ return null;
89
+ const repoName = repository.repo.startsWith(".") ? `dot-${repository.repo.slice(1)}` : repository.repo;
90
+ const preferredName = `${repository.owner.toLowerCase()}/${repoName.toLowerCase()}`;
91
+ return {
92
+ url: canonicalUrl,
93
+ title: `${repository.owner}/${repository.repo}`,
94
+ markdown,
95
+ preferredName: avoidReservedBasename(preferredName),
96
+ tags: ["github", "repository", repository.owner.toLowerCase()],
97
+ };
98
+ },
99
+ };
100
+ export default githubFetcher;
@@ -0,0 +1,291 @@
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
+ import { lookup as dnsLookup } from "node:dns/promises";
5
+ import { isIP } from "node:net";
6
+ import { fetchWithRetry, readBodyWithByteCap } from "../../core/common.js";
7
+ /** Redirect budget for a single guarded request chain. */
8
+ const MAX_GUARDED_REDIRECTS = 8;
9
+ export function assertWebsiteRequestUrl(rawUrl, ErrorType = Error, options) {
10
+ const parsedUrl = new URL(rawUrl);
11
+ if (parsedUrl.username || parsedUrl.password) {
12
+ throw new ErrorType(`Refusing to fetch URL with embedded credentials: ${parsedUrl.hostname}`);
13
+ }
14
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
15
+ throw new ErrorType(`Refusing to fetch non-http(s) URL on host: ${parsedUrl.hostname}`);
16
+ }
17
+ const hostname = parsedUrl.hostname.toLowerCase();
18
+ if (hostname.endsWith(".invalid")) {
19
+ throw new ErrorType(`Refusing to fetch reserved invalid hostname: ${parsedUrl.hostname}`);
20
+ }
21
+ if (isForbiddenWebsiteHostname(hostname, options)) {
22
+ throw new ErrorType(`Refusing to fetch non-public website host: ${parsedUrl.hostname}`);
23
+ }
24
+ }
25
+ async function defaultResolveHostname(hostname) {
26
+ const records = await dnsLookup(hostname, { all: true });
27
+ return records.map((record) => record.address);
28
+ }
29
+ /**
30
+ * Resolve-then-validate SSRF guard against DNS rebinding / private-range
31
+ * bypasses. {@link assertWebsiteRequestUrl} only rejects IP-literal and
32
+ * well-known-name hosts; a hostname like `private-host.example.com` that
33
+ * resolves to `10.0.0.1` passes those checks and then fetch connects to the
34
+ * private address. Here we resolve EVERY A/AAAA record and validate each
35
+ * against the same forbidden-range rules, failing CLOSED on an empty answer or
36
+ * resolver error.
37
+ *
38
+ * TOCTOU residual (documented, not fully closable here): Bun's `fetch` exposes
39
+ * no custom `lookup`/agent hook, so we cannot pin the socket to the exact IP we
40
+ * validated — a hostile resolver could return a public IP to this lookup and a
41
+ * private IP microseconds later at connect time (classic rebinding). This still
42
+ * removes the TRIVIAL `hostname A 10.0.0.1` bypass, which is the strongest
43
+ * guarantee available without a pinned-connection fetch API. Re-run on every
44
+ * redirect hop (the crawler recurses through `fetchWebsiteResponse`).
45
+ */
46
+ export async function assertResolvedHostAllowed(hostname, options) {
47
+ if (options?.allowPrivateHosts === true)
48
+ return;
49
+ const bare = stripIpv6Brackets(hostname.toLowerCase());
50
+ const literalVersion = isIP(bare);
51
+ if (literalVersion !== 0) {
52
+ const forbidden = literalVersion === 4 ? isForbiddenIpv4(bare) : isForbiddenIpv6(bare);
53
+ if (forbidden)
54
+ throw new Error(`Refusing to fetch non-public website host: ${hostname}`);
55
+ return;
56
+ }
57
+ const resolve = options?.resolveHostname ?? defaultResolveHostname;
58
+ let addresses;
59
+ try {
60
+ addresses = await resolve(bare);
61
+ }
62
+ catch {
63
+ throw new Error(`Refusing to fetch ${hostname}: DNS resolution failed`);
64
+ }
65
+ if (addresses.length === 0) {
66
+ throw new Error(`Refusing to fetch ${hostname}: hostname resolved to no addresses`);
67
+ }
68
+ for (const address of addresses) {
69
+ const version = isIP(address);
70
+ const forbidden = version === 4 ? isForbiddenIpv4(address) : version === 6 ? isForbiddenIpv6(stripIpv6Brackets(address)) : true;
71
+ if (forbidden) {
72
+ throw new Error(`Refusing to fetch ${hostname}: resolves to non-public or unparseable address ${address}`);
73
+ }
74
+ }
75
+ }
76
+ // WHATWG URL.hostname wraps IPv6 literals in brackets (e.g. "[::1]"), but
77
+ // node:net's isIP() only recognizes the bare address form and returns 0 for
78
+ // anything bracketed — silently skipping all IPv6 forbidden-host checks
79
+ // below for every hostname parsed off a URL. Strip the brackets before any
80
+ // isIP()/isForbiddenIpv6() call so those checks actually run.
81
+ function stripIpv6Brackets(hostname) {
82
+ return hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
83
+ }
84
+ function isForbiddenWebsiteHostname(hostname, options) {
85
+ if (options?.allowPrivateHosts === true)
86
+ return false;
87
+ if (hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "metadata.google.internal") {
88
+ return true;
89
+ }
90
+ const bareHostname = stripIpv6Brackets(hostname);
91
+ const ipVersion = isIP(bareHostname);
92
+ if (ipVersion === 4)
93
+ return isForbiddenIpv4(bareHostname);
94
+ if (ipVersion === 6)
95
+ return isForbiddenIpv6(bareHostname);
96
+ return false;
97
+ }
98
+ export function isLoopbackWebsiteHostname(hostname) {
99
+ if (hostname === "localhost" || hostname.endsWith(".localhost"))
100
+ return true;
101
+ const bareHostname = stripIpv6Brackets(hostname);
102
+ const ipVersion = isIP(bareHostname);
103
+ if (ipVersion === 4)
104
+ return bareHostname.startsWith("127.");
105
+ if (ipVersion === 6)
106
+ return bareHostname === "::1";
107
+ return false;
108
+ }
109
+ function isForbiddenIpv4(hostname) {
110
+ const parts = hostname.split(".").map((part) => Number.parseInt(part, 10));
111
+ const [a = -1, b = -1, c = -1, d = -1] = parts;
112
+ if (parts.length !== 4 || [a, b, c, d].some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
113
+ return true;
114
+ }
115
+ return (a === 0 ||
116
+ a === 10 ||
117
+ (a === 100 && b >= 64 && b <= 127) ||
118
+ a === 127 ||
119
+ (a === 169 && b === 254) ||
120
+ (a === 172 && b >= 16 && b <= 31) ||
121
+ (a === 192 && b === 0 && c === 0 && d !== 9 && d !== 10) ||
122
+ (a === 192 && b === 0 && c === 2) ||
123
+ (a === 192 && b === 168) ||
124
+ (a === 192 && b === 88 && c === 99) ||
125
+ (a === 198 && (b === 18 || b === 19)) ||
126
+ (a === 198 && b === 51 && c === 100) ||
127
+ (a === 203 && b === 0 && c === 113) ||
128
+ a >= 224);
129
+ }
130
+ function parseIpv6Words(hostname) {
131
+ let normalized;
132
+ try {
133
+ normalized = stripIpv6Brackets(new URL(`http://[${stripIpv6Brackets(hostname)}]/`).hostname).toLowerCase();
134
+ }
135
+ catch {
136
+ return null;
137
+ }
138
+ const halves = normalized.split("::");
139
+ if (halves.length > 2)
140
+ return null;
141
+ const left = halves[0] ? halves[0].split(":") : [];
142
+ const right = halves[1] ? halves[1].split(":") : [];
143
+ const missing = 8 - left.length - right.length;
144
+ if ((halves.length === 1 && missing !== 0) || (halves.length === 2 && missing < 1))
145
+ return null;
146
+ const parts = halves.length === 2 ? [...left, ...Array(missing).fill("0"), ...right] : left;
147
+ if (parts.length !== 8 || parts.some((part) => !/^[0-9a-f]{1,4}$/.test(part)))
148
+ return null;
149
+ return parts.map((part) => Number.parseInt(part, 16));
150
+ }
151
+ function ipv6Value(hostname) {
152
+ const words = parseIpv6Words(hostname);
153
+ if (!words)
154
+ return null;
155
+ return words.reduce((value, word) => (value << 16n) | BigInt(word), 0n);
156
+ }
157
+ const FORBIDDEN_IPV6_RANGES = [
158
+ ["64:ff9b:1::", 48],
159
+ ["100::", 64],
160
+ ["2001::", 32],
161
+ ["2001:2::", 48],
162
+ ["2001:10::", 28],
163
+ ["2001:20::", 28],
164
+ ["2001:db8::", 32],
165
+ ["3fff::", 20],
166
+ ["5f00::", 16],
167
+ ["fc00::", 7],
168
+ ["fe80::", 10],
169
+ ["fec0::", 10],
170
+ ["ff00::", 8],
171
+ ];
172
+ function embeddedIpv4Address(words) {
173
+ const firstSixZero = words.slice(0, 6).every((word) => word === 0);
174
+ const mapped = words.slice(0, 5).every((word) => word === 0) && words[5] === 0xffff;
175
+ const translated = words.slice(0, 4).every((word) => word === 0) && words[4] === 0xffff && words[5] === 0;
176
+ const wellKnownNat64 = words[0] === 0x64 && words[1] === 0xff9b && words.slice(2, 6).every((word) => word === 0);
177
+ if (words[0] === 0x2002)
178
+ return ipv4FromHextets(words[1] ?? 0, words[2] ?? 0);
179
+ if (!firstSixZero && !mapped && !translated && !wellKnownNat64)
180
+ return null;
181
+ return ipv4FromHextets(words[6] ?? 0, words[7] ?? 0);
182
+ }
183
+ function ipv4FromHextets(high, low) {
184
+ return `${(high >> 8) & 0xff}.${high & 0xff}.${(low >> 8) & 0xff}.${low & 0xff}`;
185
+ }
186
+ function isForbiddenIpv6(hostname) {
187
+ const words = parseIpv6Words(hostname);
188
+ const value = ipv6Value(hostname);
189
+ if (!words || value === null)
190
+ return true;
191
+ const embeddedIpv4 = embeddedIpv4Address(words);
192
+ if (embeddedIpv4)
193
+ return isForbiddenIpv4(embeddedIpv4);
194
+ return FORBIDDEN_IPV6_RANGES.some(([prefix, bits]) => {
195
+ const prefixValue = ipv6Value(prefix);
196
+ if (prefixValue === null)
197
+ return true;
198
+ const shift = BigInt(128 - bits);
199
+ return value >> shift === prefixValue >> shift;
200
+ });
201
+ }
202
+ function redirectInit(init, currentUrl, nextUrl) {
203
+ if (new URL(currentUrl).origin === new URL(nextUrl).origin)
204
+ return init;
205
+ const headers = new Headers(init.headers);
206
+ headers.delete("authorization");
207
+ headers.delete("cookie");
208
+ headers.delete("proxy-authorization");
209
+ return { ...init, headers };
210
+ }
211
+ /**
212
+ * Fetch with the full SSRF guard chain applied to the initial URL and to every
213
+ * redirect hop.
214
+ *
215
+ * `redirect: "manual"` is essential: with the default `follow`, the platform
216
+ * resolves and connects to redirect targets that were never validated, so a
217
+ * public host can bounce a request into the private network.
218
+ */
219
+ export async function fetchGuardedResponse(url, init, opts, redirectCount = 0) {
220
+ assertWebsiteRequestUrl(url, Error, opts);
221
+ await assertResolvedHostAllowed(new URL(url).hostname, opts);
222
+ const response = await fetchWithRetry(url, { ...init, redirect: "manual" }, {
223
+ timeout: opts.timeoutMs,
224
+ retries: opts.retries ?? 1,
225
+ });
226
+ if (response.status >= 300 && response.status < 400) {
227
+ if (redirectCount >= MAX_GUARDED_REDIRECTS) {
228
+ await response.body?.cancel().catch(() => undefined);
229
+ throw new Error(`Too many redirects while fetching ${url}`);
230
+ }
231
+ const location = response.headers.get("location");
232
+ if (!location) {
233
+ await response.body?.cancel().catch(() => undefined);
234
+ throw new Error(`Redirect response from ${url} did not include a Location header`);
235
+ }
236
+ await response.body?.cancel().catch(() => undefined);
237
+ const nextUrl = new URL(location, url).toString();
238
+ return fetchGuardedResponse(nextUrl, redirectInit(init, url, nextUrl), opts, redirectCount + 1);
239
+ }
240
+ return { response, finalUrl: url };
241
+ }
242
+ /**
243
+ * GET text from a compile-time fixed API host, rejecting redirects so request
244
+ * headers (which may contain a token) can never be forwarded elsewhere.
245
+ */
246
+ export async function fetchPinnedText(url, options) {
247
+ const response = await fetchWithRetry(url, { headers: options.headers, signal: options.signal, redirect: "manual" }, { timeout: options.timeoutMs, retries: options.retries ?? 1 });
248
+ if (response.status >= 300 && response.status < 400) {
249
+ await response.body?.cancel().catch(() => undefined);
250
+ return null;
251
+ }
252
+ if (!response.ok) {
253
+ await response.body?.cancel().catch(() => undefined);
254
+ return null;
255
+ }
256
+ try {
257
+ return await readBodyWithByteCap(response, options.byteCap, {
258
+ bodyTimeoutMs: options.bodyTimeoutMs,
259
+ signal: options.signal,
260
+ });
261
+ }
262
+ catch (error) {
263
+ if (options.signal?.aborted)
264
+ throw error;
265
+ return null;
266
+ }
267
+ }
268
+ /**
269
+ * GET a JSON document from a PINNED, constant API host (X, Bluesky XRPC), with
270
+ * a byte-capped read. Returns the parsed value, or null on any 3xx/non-2xx/
271
+ * parse failure.
272
+ *
273
+ * `redirect: "manual"` + rejecting 3xx is load-bearing: these endpoints have no
274
+ * legitimate reason to redirect, and following one would send the next request
275
+ * (bearer token included) to an unvalidated host with none of the SSRF guards
276
+ * the crawl path applies. The full resolve-then-validate DNS guard is
277
+ * intentionally NOT applied here because the host is a compile-time constant,
278
+ * not caller-supplied — the guarded chokepoint is {@link fetchGuardedResponse},
279
+ * which the caller-supplied crawl/feed paths use instead.
280
+ */
281
+ export async function fetchPinnedJson(url, options) {
282
+ const body = await fetchPinnedText(url, options);
283
+ if (body === null)
284
+ return null;
285
+ try {
286
+ return JSON.parse(body);
287
+ }
288
+ catch {
289
+ return null;
290
+ }
291
+ }
@@ -5,10 +5,26 @@ import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import { pathToFileURL } from "node:url";
7
7
  import { warn } from "../../core/warn.js";
8
+ import blueskyFetcher from "./bluesky.js";
9
+ import githubFetcher from "./github.js";
10
+ import rssFetcher from "./rss.js";
11
+ import xFetcher from "./x.js";
8
12
  import youtubeFetcher from "./youtube.js";
9
13
  const FETCHER_DIR = path.join("scripts", "wiki-fetchers");
10
14
  const FETCHER_FILE_PATTERN = /\.(?:ts|js|mjs)$/i;
11
- const BUILTIN_FETCHERS = [youtubeFetcher];
15
+ /**
16
+ * Order matters: the first fetcher whose `matches` returns true and whose
17
+ * `fetch` returns non-null wins. Host-specific fetchers therefore precede the
18
+ * generic RSS fetcher, whose `matches` is path-shaped and deliberately loose.
19
+ * Any fetcher returning null falls through to the generic website crawler.
20
+ */
21
+ const BUILTIN_FETCHERS = [
22
+ githubFetcher,
23
+ youtubeFetcher,
24
+ blueskyFetcher,
25
+ xFetcher,
26
+ rssFetcher,
27
+ ];
12
28
  function isWikiSnapshotFetcher(value) {
13
29
  if (!value || typeof value !== "object")
14
30
  return false;