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,910 @@
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 { parse } from "node-html-parser";
5
+ import { ResponseTooLargeError, readBodyWithByteCap } from "../../core/common.js";
6
+ import { warn } from "../../core/warn.js";
7
+ import { avoidReservedBasename, coerceString, escapeMarkdownStructure } from "./fetcher-util.js";
8
+ import { fetchGuardedResponse, fetchPinnedJson } from "./host-guard.js";
9
+ import rssFetcher from "./rss.js";
10
+ /**
11
+ * X / Twitter snapshot fetcher.
12
+ *
13
+ * Profiles retain the API-v2 -> RSS-template strategy. Posts first inspect the
14
+ * public page for an embedded Article, then use exact API-v2 lookup when a token
15
+ * is available, and finally use public Open Graph post text. Public page code is
16
+ * never executed; Article fields are read as bounded JSON-compatible strings.
17
+ */
18
+ const X_HOSTS = new Set(["x.com", "www.x.com", "twitter.com", "www.twitter.com"]);
19
+ const X_API_BASE = "https://api.x.com/2";
20
+ const DEFAULT_TWEET_LIMIT = 50;
21
+ const X_BYTE_CAP = 4 * 1024 * 1024;
22
+ const X_BODY_TIMEOUT_MS = 30_000;
23
+ const X_PAGE_BYTE_CAP = 5 * 1024 * 1024;
24
+ /** Reserved x.com paths that are not user profiles. */
25
+ const RESERVED_X_PATHS = new Set([
26
+ "home",
27
+ "explore",
28
+ "notifications",
29
+ "messages",
30
+ "search",
31
+ "settings",
32
+ "i",
33
+ "intent",
34
+ "share",
35
+ "compose",
36
+ "login",
37
+ "signup",
38
+ "about",
39
+ "tos",
40
+ "privacy",
41
+ ]);
42
+ function validXUsername(value) {
43
+ const username = value?.trim().replace(/^@/, "") ?? "";
44
+ if (!/^[A-Za-z0-9_]{1,15}$/.test(username))
45
+ return null;
46
+ if (RESERVED_X_PATHS.has(username.toLowerCase()))
47
+ return null;
48
+ return username;
49
+ }
50
+ function validXId(value) {
51
+ return value && /^[0-9]{1,19}$/.test(value) ? value : null;
52
+ }
53
+ export function extractXResource(url) {
54
+ if (!X_HOSTS.has(url.hostname.toLowerCase()))
55
+ return null;
56
+ const segments = url.pathname.split("/").filter(Boolean);
57
+ if (segments.length === 1) {
58
+ const username = validXUsername(segments[0]);
59
+ return username ? { kind: "profile", username } : null;
60
+ }
61
+ if (segments.length === 3 && ["status", "statuses"].includes(segments[1]?.toLowerCase() ?? "")) {
62
+ const usernameHint = validXUsername(segments[0]);
63
+ const postId = validXId(segments[2]);
64
+ if (usernameHint && postId)
65
+ return { kind: "status", postId, usernameHint };
66
+ }
67
+ if (segments[0]?.toLowerCase() === "i") {
68
+ const resourceType = segments[1]?.toLowerCase();
69
+ const id = validXId(segments[2]);
70
+ if (segments.length === 3 && resourceType === "status" && id)
71
+ return { kind: "status", postId: id };
72
+ if (segments.length === 3 && resourceType === "article" && id)
73
+ return { kind: "article", articleId: id };
74
+ const webPostId = validXId(segments[3]);
75
+ if (segments.length === 4 && resourceType === "web" && segments[2]?.toLowerCase() === "status" && webPostId) {
76
+ return { kind: "status", postId: webPostId };
77
+ }
78
+ }
79
+ return null;
80
+ }
81
+ export function extractXUsername(url) {
82
+ const resource = extractXResource(url);
83
+ return resource?.kind === "profile" ? resource.username : null;
84
+ }
85
+ /** Secret ref consulted when `X_BEARER_TOKEN` is not in the environment. */
86
+ export const X_BEARER_TOKEN_SECRET_REF = "secrets/x-bearer-token";
87
+ export function resolveXBearerToken(context) {
88
+ const fromEnv = process.env.X_BEARER_TOKEN?.trim();
89
+ if (fromEnv)
90
+ return fromEnv;
91
+ try {
92
+ return context?.resolveSecret?.(X_BEARER_TOKEN_SECRET_REF)?.trim() || null;
93
+ }
94
+ catch {
95
+ return null;
96
+ }
97
+ }
98
+ const MAX_RELAY_OBJECT_CANDIDATES = 16;
99
+ const EXECUTABLE_SCRIPT_TYPES = new Set([
100
+ "module",
101
+ "application/ecmascript",
102
+ "application/javascript",
103
+ "application/x-ecmascript",
104
+ "application/x-javascript",
105
+ "text/ecmascript",
106
+ "text/javascript",
107
+ "text/javascript1.0",
108
+ "text/javascript1.1",
109
+ "text/javascript1.2",
110
+ "text/javascript1.3",
111
+ "text/javascript1.4",
112
+ "text/javascript1.5",
113
+ "text/jscript",
114
+ "text/livescript",
115
+ "text/x-ecmascript",
116
+ "text/x-javascript",
117
+ ]);
118
+ const EXECUTABLE_SCRIPT_LANGUAGES = new Set([
119
+ "ecmascript",
120
+ "javascript",
121
+ "javascript1.0",
122
+ "javascript1.1",
123
+ "javascript1.2",
124
+ "javascript1.3",
125
+ "javascript1.4",
126
+ "javascript1.5",
127
+ "jscript",
128
+ "livescript",
129
+ ]);
130
+ const CONTROL_CONDITION_KEYWORDS = new Set(["catch", "for", "if", "switch", "while", "with"]);
131
+ const REGEX_PREFIX_KEYWORDS = new Set([
132
+ "await",
133
+ "case",
134
+ "delete",
135
+ "do",
136
+ "else",
137
+ "in",
138
+ "instanceof",
139
+ "new",
140
+ "of",
141
+ "return",
142
+ "throw",
143
+ "typeof",
144
+ "void",
145
+ "yield",
146
+ ]);
147
+ function closesControlCondition(source, code, closeIndex) {
148
+ let depth = 0;
149
+ for (let i = closeIndex; i >= 0; i--) {
150
+ if (code[i] !== 1)
151
+ continue;
152
+ const char = source[i] ?? "";
153
+ if (char === ")")
154
+ depth += 1;
155
+ else if (char === "(") {
156
+ depth -= 1;
157
+ if (depth !== 0)
158
+ continue;
159
+ let end = i - 1;
160
+ while (end >= 0 && (code[end] !== 1 || /\s/.test(source[end] ?? "")))
161
+ end -= 1;
162
+ let start = end;
163
+ while (start >= 0 && code[start] === 1 && /[A-Za-z]/.test(source[start] ?? ""))
164
+ start -= 1;
165
+ return CONTROL_CONDITION_KEYWORDS.has(source.slice(start + 1, end + 1));
166
+ }
167
+ }
168
+ return false;
169
+ }
170
+ function startsRegexLiteral(source, code, slashIndex) {
171
+ let previous = slashIndex - 1;
172
+ while (previous >= 0 && (code[previous] !== 1 || /\s/.test(source[previous] ?? "")))
173
+ previous -= 1;
174
+ if (previous < 0)
175
+ return true;
176
+ const previousChar = source[previous] ?? "";
177
+ if (previousChar === ")" && closesControlCondition(source, code, previous))
178
+ return true;
179
+ if (previousChar === "}")
180
+ return true;
181
+ if (/[([{:;,=!?&|+*%^~<>-]/.test(previousChar))
182
+ return true;
183
+ if (!/[A-Za-z]/.test(previousChar))
184
+ return false;
185
+ let start = previous;
186
+ while (start > 0 && /[A-Za-z]/.test(source[start - 1] ?? ""))
187
+ start -= 1;
188
+ return REGEX_PREFIX_KEYWORDS.has(source.slice(start, previous + 1));
189
+ }
190
+ function executableCodeMask(source) {
191
+ const code = new Uint8Array(source.length);
192
+ let quote = "";
193
+ let escaped = false;
194
+ let lineComment = false;
195
+ let blockComment = false;
196
+ let regex = false;
197
+ let regexClass = false;
198
+ for (let i = 0; i < source.length; i++) {
199
+ const char = source[i] ?? "";
200
+ const next = source[i + 1] ?? "";
201
+ if (lineComment) {
202
+ if (char === "\n" || char === "\r") {
203
+ lineComment = false;
204
+ code[i] = 1;
205
+ }
206
+ continue;
207
+ }
208
+ if (regex) {
209
+ if (escaped)
210
+ escaped = false;
211
+ else if (char === "\\")
212
+ escaped = true;
213
+ else if (char === "[")
214
+ regexClass = true;
215
+ else if (char === "]")
216
+ regexClass = false;
217
+ else if (char === "/" && !regexClass)
218
+ regex = false;
219
+ continue;
220
+ }
221
+ if (blockComment) {
222
+ if (char === "*" && next === "/") {
223
+ blockComment = false;
224
+ i += 1;
225
+ }
226
+ continue;
227
+ }
228
+ if (quote) {
229
+ if (escaped)
230
+ escaped = false;
231
+ else if (char === "\\")
232
+ escaped = true;
233
+ else if (char === quote)
234
+ quote = "";
235
+ continue;
236
+ }
237
+ if (char === "/" && next === "/") {
238
+ lineComment = true;
239
+ i += 1;
240
+ continue;
241
+ }
242
+ if (char === "/" && next === "*") {
243
+ blockComment = true;
244
+ i += 1;
245
+ continue;
246
+ }
247
+ if (source.startsWith("<!--", i)) {
248
+ lineComment = true;
249
+ i += 3;
250
+ continue;
251
+ }
252
+ if (source.startsWith("-->", i) && /^\s*$/.test(source.slice(source.lastIndexOf("\n", i - 1) + 1, i))) {
253
+ lineComment = true;
254
+ i += 2;
255
+ continue;
256
+ }
257
+ if (char === "/" && startsRegexLiteral(source, code, i)) {
258
+ regex = true;
259
+ regexClass = false;
260
+ continue;
261
+ }
262
+ // Relay state uses JSON-compatible strings, never template literals. Mask
263
+ // the remainder rather than partially interpreting nested `${...}` code.
264
+ if (char === "`")
265
+ break;
266
+ if (char === '"' || char === "'") {
267
+ quote = char;
268
+ continue;
269
+ }
270
+ code[i] = 1;
271
+ }
272
+ return code;
273
+ }
274
+ function serializedScriptPayload(html) {
275
+ let root;
276
+ try {
277
+ root = parse(html, { comment: false });
278
+ }
279
+ catch {
280
+ return null;
281
+ }
282
+ const scripts = [];
283
+ for (const script of root.querySelectorAll("script")) {
284
+ if (script.hasAttribute("src") || script.hasAttribute("nomodule"))
285
+ continue;
286
+ const scriptType = (script.getAttribute("type") ?? "").trim().toLowerCase();
287
+ if (scriptType && !EXECUTABLE_SCRIPT_TYPES.has(scriptType))
288
+ continue;
289
+ const scriptLanguage = (script.getAttribute("language") ?? "").trim().toLowerCase();
290
+ if (!scriptType && scriptLanguage && !EXECUTABLE_SCRIPT_LANGUAGES.has(scriptLanguage))
291
+ continue;
292
+ let parent = script.parentNode;
293
+ let inert = false;
294
+ while (parent && "tagName" in parent) {
295
+ const tagName = typeof parent.tagName === "string" ? parent.tagName.toLowerCase() : "";
296
+ if ([
297
+ "iframe",
298
+ "math",
299
+ "noembed",
300
+ "noframes",
301
+ "noscript",
302
+ "plaintext",
303
+ "style",
304
+ "svg",
305
+ "template",
306
+ "textarea",
307
+ "title",
308
+ "xmp",
309
+ ].includes(tagName)) {
310
+ inert = true;
311
+ break;
312
+ }
313
+ parent = parent.parentNode;
314
+ }
315
+ if (inert)
316
+ continue;
317
+ const body = script.innerHTML;
318
+ if (body.includes("$R[") || body.includes('__typename:"ArticleEntity"')) {
319
+ scripts.push({ source: body, code: executableCodeMask(body) });
320
+ }
321
+ }
322
+ if (scripts.length === 0)
323
+ return null;
324
+ const separator = "\0;\n";
325
+ const source = scripts.map((script) => script.source).join(separator);
326
+ const code = new Uint8Array(source.length);
327
+ let offset = 0;
328
+ for (const [index, script] of scripts.entries()) {
329
+ code.set(script.code, offset);
330
+ offset += script.source.length;
331
+ if (index < scripts.length - 1) {
332
+ code.fill(1, offset, offset + separator.length);
333
+ offset += separator.length;
334
+ }
335
+ }
336
+ return { source, code };
337
+ }
338
+ async function xApiJson(url, token, context) {
339
+ return (await fetchPinnedJson(url, {
340
+ headers: {
341
+ Accept: "application/json",
342
+ Authorization: `Bearer ${token}`,
343
+ "User-Agent": "akm-cli x fetcher",
344
+ },
345
+ byteCap: X_BYTE_CAP,
346
+ bodyTimeoutMs: X_BODY_TIMEOUT_MS,
347
+ timeoutMs: context.timeoutMs,
348
+ signal: context.signal,
349
+ }));
350
+ }
351
+ async function fetchProfileViaApi(username, token, context) {
352
+ const lookup = await xApiJson(`${X_API_BASE}/users/by/username/${encodeURIComponent(username)}`, token, context);
353
+ const userId = coerceString(lookup?.data?.id);
354
+ if (!userId)
355
+ return null;
356
+ const timeline = await xApiJson(`${X_API_BASE}/users/${encodeURIComponent(userId)}/tweets` +
357
+ `?max_results=${DEFAULT_TWEET_LIMIT}&tweet.fields=created_at`, token, context);
358
+ const data = timeline?.data;
359
+ if (!Array.isArray(data))
360
+ return null;
361
+ return data.map((raw) => {
362
+ const tweet = (raw ?? {});
363
+ return {
364
+ id: validXId(coerceString(tweet.id)) ?? "",
365
+ text: coerceString(tweet.text),
366
+ createdAt: toIsoDate(coerceString(tweet.created_at)),
367
+ };
368
+ });
369
+ }
370
+ async function fetchPostViaApi(postId, usernameHint, token, context) {
371
+ const params = new URLSearchParams({
372
+ "tweet.fields": "author_id,created_at,note_tweet",
373
+ expansions: "author_id",
374
+ "user.fields": "username",
375
+ });
376
+ const response = await xApiJson(`${X_API_BASE}/tweets/${encodeURIComponent(postId)}?${params}`, token, context);
377
+ const data = response?.data;
378
+ if (!data)
379
+ return null;
380
+ if (coerceString(data.id) !== postId)
381
+ return null;
382
+ const noteTweet = data.note_tweet;
383
+ const text = coerceString(noteTweet?.text) || coerceString(data.text);
384
+ if (!text)
385
+ return null;
386
+ const authorId = coerceString(data.author_id);
387
+ const users = response?.includes?.users;
388
+ const author = Array.isArray(users)
389
+ ? users.find((raw) => coerceString(raw?.id) === authorId)
390
+ : undefined;
391
+ const username = validXUsername(coerceString(author?.username)) || usernameHint || "";
392
+ return {
393
+ id: postId,
394
+ text,
395
+ createdAt: toIsoDate(coerceString(data.created_at)),
396
+ username,
397
+ };
398
+ }
399
+ function toIsoDate(value) {
400
+ if (!value)
401
+ return "";
402
+ const parsed = new Date(value);
403
+ return Number.isNaN(parsed.getTime()) ? "" : parsed.toISOString();
404
+ }
405
+ function renderProfileMarkdown(username, tweets) {
406
+ const sections = [];
407
+ for (const tweet of tweets) {
408
+ if (!tweet.text)
409
+ continue;
410
+ const when = tweet.createdAt ? new Date(tweet.createdAt) : null;
411
+ const iso = when && !Number.isNaN(when.getTime()) ? when.toISOString() : "";
412
+ sections.push(`## ${iso || "(undated)"}`, "", escapeXPlainText(tweet.text), "");
413
+ if (tweet.id)
414
+ sections.push(`https://x.com/${username}/status/${tweet.id}`, "");
415
+ }
416
+ return sections.join("\n").trimEnd();
417
+ }
418
+ function readJsonStringAt(value, quoteIndex) {
419
+ if (value[quoteIndex] !== '"')
420
+ return null;
421
+ let escaped = false;
422
+ for (let i = quoteIndex + 1; i < value.length; i++) {
423
+ const char = value[i];
424
+ if (escaped) {
425
+ escaped = false;
426
+ continue;
427
+ }
428
+ if (char === "\\") {
429
+ escaped = true;
430
+ continue;
431
+ }
432
+ if (char !== '"')
433
+ continue;
434
+ try {
435
+ const parsed = JSON.parse(value.slice(quoteIndex, i + 1));
436
+ return typeof parsed === "string" ? { value: parsed, end: i + 1 } : null;
437
+ }
438
+ catch {
439
+ return null;
440
+ }
441
+ }
442
+ return null;
443
+ }
444
+ function readSerializedObjectAt(payload, openIndex) {
445
+ if (payload.source[openIndex] !== "{" || payload.code[openIndex] !== 1)
446
+ return null;
447
+ let depth = 0;
448
+ for (let i = openIndex; i < payload.source.length; i++) {
449
+ if (payload.code[i] !== 1)
450
+ continue;
451
+ const char = payload.source[i] ?? "";
452
+ if (char === "\0")
453
+ return null;
454
+ if (char === "{")
455
+ depth += 1;
456
+ else if (char === "}") {
457
+ depth -= 1;
458
+ if (depth === 0) {
459
+ return {
460
+ start: openIndex,
461
+ end: i + 1,
462
+ source: payload.source.slice(openIndex, i + 1),
463
+ code: payload.code.slice(openIndex, i + 1),
464
+ };
465
+ }
466
+ }
467
+ }
468
+ return null;
469
+ }
470
+ function isRelayAssignment(payload, assignmentIndex) {
471
+ if (payload.code[assignmentIndex] !== 1 || payload.code[assignmentIndex + 1] !== 1)
472
+ return false;
473
+ const prefixStart = Math.max(0, assignmentIndex - 40);
474
+ const match = /\$R\[\d+\]$/.exec(payload.source.slice(prefixStart, assignmentIndex));
475
+ if (!match)
476
+ return false;
477
+ let boundaryIndex = prefixStart + match.index - 1;
478
+ while (boundaryIndex >= 0 && (payload.code[boundaryIndex] !== 1 || /\s/.test(payload.source[boundaryIndex] ?? ""))) {
479
+ boundaryIndex -= 1;
480
+ }
481
+ return boundaryIndex < 0 || /[:,;=({[]/.test(payload.source[boundaryIndex] ?? "");
482
+ }
483
+ function objectContaining(payload, markerIndex) {
484
+ let assignmentIndex = payload.source.lastIndexOf("={", markerIndex);
485
+ while (assignmentIndex >= 0) {
486
+ if (isRelayAssignment(payload, assignmentIndex)) {
487
+ const object = readSerializedObjectAt(payload, assignmentIndex + 1);
488
+ if (object && object.end > markerIndex)
489
+ return object;
490
+ }
491
+ assignmentIndex = payload.source.lastIndexOf("={", assignmentIndex - 1);
492
+ }
493
+ return null;
494
+ }
495
+ function topLevelPropertyValueIndexes(object, property) {
496
+ const indexes = [];
497
+ let depth = 0;
498
+ for (let i = 0; i < object.source.length; i++) {
499
+ if (object.code[i] !== 1)
500
+ continue;
501
+ const char = object.source[i] ?? "";
502
+ if (char === "{") {
503
+ depth += 1;
504
+ continue;
505
+ }
506
+ if (char === "}") {
507
+ depth -= 1;
508
+ continue;
509
+ }
510
+ if (depth !== 1 || !object.source.startsWith(property, i))
511
+ continue;
512
+ let beforeIndex = i - 1;
513
+ while (beforeIndex >= 0 && (object.code[beforeIndex] !== 1 || /\s/.test(object.source[beforeIndex] ?? ""))) {
514
+ beforeIndex -= 1;
515
+ }
516
+ const before = object.source[beforeIndex];
517
+ if (before !== "{" && before !== ",")
518
+ continue;
519
+ let cursor = i + property.length;
520
+ while (/\s/.test(object.source[cursor] ?? ""))
521
+ cursor += 1;
522
+ if (object.source[cursor] !== ":" || object.code[cursor] !== 1)
523
+ continue;
524
+ indexes.push(cursor + 1);
525
+ }
526
+ return indexes;
527
+ }
528
+ function nextExecutableIndex(object, from) {
529
+ for (let i = from; i < object.source.length; i++) {
530
+ if (object.code[i] === 1 && !/\s/.test(object.source[i] ?? ""))
531
+ return i;
532
+ }
533
+ return -1;
534
+ }
535
+ function topLevelStringProperty(object, property) {
536
+ let valueIndex = topLevelPropertyValueIndexes(object, property).at(-1) ?? -1;
537
+ if (valueIndex < 0)
538
+ return "";
539
+ while (/\s/.test(object.source[valueIndex] ?? ""))
540
+ valueIndex += 1;
541
+ const parsed = readJsonStringAt(object.source, valueIndex);
542
+ if (!parsed)
543
+ return "";
544
+ const delimiterIndex = nextExecutableIndex(object, parsed.end);
545
+ if (delimiterIndex < 0 || (object.source[delimiterIndex] !== "," && object.source[delimiterIndex] !== "}"))
546
+ return "";
547
+ return parsed.value;
548
+ }
549
+ function topLevelObjectProperty(object, property) {
550
+ const valueIndex = topLevelPropertyValueIndexes(object, property).at(-1) ?? -1;
551
+ if (valueIndex < 0)
552
+ return null;
553
+ let openIndex = -1;
554
+ for (let i = valueIndex; i < object.source.length; i++) {
555
+ if (object.code[i] !== 1)
556
+ continue;
557
+ if (object.source[i] === ",")
558
+ return null;
559
+ if (object.source[i] === "{") {
560
+ openIndex = i;
561
+ break;
562
+ }
563
+ }
564
+ if (openIndex < 0)
565
+ return null;
566
+ return readSerializedObjectAt(object, openIndex);
567
+ }
568
+ function findObjectById(payload, id) {
569
+ if (!/^[A-Za-z0-9_:+/=-]{1,512}$/.test(id))
570
+ return null;
571
+ const marker = `__id:"${id}"`;
572
+ let from = 0;
573
+ let found = null;
574
+ let candidates = 0;
575
+ while (from < payload.source.length) {
576
+ const index = payload.source.indexOf(marker, from);
577
+ if (index < 0)
578
+ break;
579
+ if (payload.code[index] !== 1) {
580
+ from = index + marker.length;
581
+ continue;
582
+ }
583
+ const object = objectContaining(payload, index);
584
+ if (object) {
585
+ candidates += 1;
586
+ if (candidates > MAX_RELAY_OBJECT_CANDIDATES)
587
+ return null;
588
+ if (topLevelStringProperty(object, "__id") === id) {
589
+ if (found && found.start !== object.start)
590
+ return null;
591
+ found = object;
592
+ }
593
+ }
594
+ from = index + marker.length;
595
+ }
596
+ return found;
597
+ }
598
+ function articleFromObject(object) {
599
+ if (topLevelStringProperty(object, "__typename") !== "ArticleEntity")
600
+ return null;
601
+ const body = topLevelStringProperty(object, "plain_text").trim();
602
+ if (!body)
603
+ return null;
604
+ return {
605
+ title: topLevelStringProperty(object, "title").replace(/\s+/g, " ").trim(),
606
+ body,
607
+ restId: topLevelStringProperty(object, "rest_id"),
608
+ };
609
+ }
610
+ function referencedObject(payload, object, property) {
611
+ const reference = topLevelObjectProperty(object, property);
612
+ const id = reference ? topLevelStringProperty(reference, "__ref") : "";
613
+ return id ? findObjectById(payload, id) : null;
614
+ }
615
+ function serializedArticles(payload) {
616
+ const marker = '__typename:"ArticleEntity"';
617
+ const seen = new Set();
618
+ const articles = [];
619
+ let from = 0;
620
+ let candidates = 0;
621
+ while (from < payload.source.length) {
622
+ const index = payload.source.indexOf(marker, from);
623
+ if (index < 0)
624
+ break;
625
+ if (payload.code[index] !== 1) {
626
+ from = index + marker.length;
627
+ continue;
628
+ }
629
+ const object = objectContaining(payload, index);
630
+ if (object && !seen.has(object.start)) {
631
+ candidates += 1;
632
+ if (candidates > MAX_RELAY_OBJECT_CANDIDATES)
633
+ return [];
634
+ seen.add(object.start);
635
+ const article = articleFromObject(object);
636
+ if (article)
637
+ articles.push(article);
638
+ }
639
+ from = index + marker.length;
640
+ }
641
+ return articles;
642
+ }
643
+ function articleByRestId(payload, articleId) {
644
+ const marker = `rest_id:"${articleId}"`;
645
+ let from = 0;
646
+ let found = null;
647
+ let candidates = 0;
648
+ while (from < payload.source.length) {
649
+ const index = payload.source.indexOf(marker, from);
650
+ if (index < 0)
651
+ break;
652
+ if (payload.code[index] !== 1) {
653
+ from = index + marker.length;
654
+ continue;
655
+ }
656
+ const object = objectContaining(payload, index);
657
+ if (object) {
658
+ candidates += 1;
659
+ if (candidates > MAX_RELAY_OBJECT_CANDIDATES)
660
+ return null;
661
+ }
662
+ const article = object ? articleFromObject(object) : null;
663
+ if (article?.restId === articleId) {
664
+ if (found)
665
+ return null;
666
+ found = article;
667
+ }
668
+ from = index + marker.length;
669
+ }
670
+ return found;
671
+ }
672
+ export function extractPublicXArticle(html, target) {
673
+ const payload = serializedScriptPayload(html);
674
+ if (!payload)
675
+ return null;
676
+ let article;
677
+ if (target?.articleId) {
678
+ article = articleByRestId(payload, target.articleId) ?? undefined;
679
+ }
680
+ else if (target?.postId) {
681
+ const encodedId = Buffer.from(`Tweet:${target.postId}`).toString("base64");
682
+ const tweetArticle = findObjectById(payload, `client:${encodedId}:article`);
683
+ const articleResults = tweetArticle ? referencedObject(payload, tweetArticle, "article_results") : null;
684
+ const articleObject = articleResults ? referencedObject(payload, articleResults, "result") : null;
685
+ article = articleObject ? (articleFromObject(articleObject) ?? undefined) : undefined;
686
+ }
687
+ else {
688
+ const articles = serializedArticles(payload);
689
+ if (articles.length === 1)
690
+ article = articles[0];
691
+ }
692
+ return article ? { title: article.title, body: article.body } : null;
693
+ }
694
+ function escapeXPlainText(value) {
695
+ const inlineSafe = value
696
+ .replace(/\r\n?/g, "\n")
697
+ .replace(/\\/g, "\\\\")
698
+ .replace(/([[\]`])/g, "\\$1")
699
+ .replace(/<(?=[a-zA-Z/!?])/g, "&lt;");
700
+ return escapeMarkdownStructure(inlineSafe);
701
+ }
702
+ function metaContent(root, name) {
703
+ for (const meta of root.querySelectorAll("meta")) {
704
+ const key = (meta.getAttribute("property") ?? meta.getAttribute("name") ?? "").toLowerCase();
705
+ if (key === name)
706
+ return meta.getAttribute("content")?.trim() ?? "";
707
+ }
708
+ return "";
709
+ }
710
+ function targetFullText(html, postId) {
711
+ const payload = serializedScriptPayload(html);
712
+ if (!payload)
713
+ return "";
714
+ const encodedId = Buffer.from(`Tweet:${postId}`).toString("base64");
715
+ const details = findObjectById(payload, `client:${encodedId}:details`);
716
+ return details ? topLevelStringProperty(details, "full_text") : "";
717
+ }
718
+ function publicPostFromHtml(html, postId, usernameHint) {
719
+ let root;
720
+ try {
721
+ root = parse(html, { comment: false });
722
+ }
723
+ catch {
724
+ return null;
725
+ }
726
+ const title = metaContent(root, "og:title");
727
+ const titleUsername = title.match(/\(@([A-Za-z0-9_]{1,15})\)/)?.[1] ?? "";
728
+ const text = (titleUsername ? metaContent(root, "og:description") : "") || targetFullText(html, postId);
729
+ if (!text.trim())
730
+ return null;
731
+ const username = titleUsername || usernameHint || "";
732
+ return { id: postId, text: text.trim(), createdAt: "", username };
733
+ }
734
+ async function fetchPublicXHtml(url, context) {
735
+ try {
736
+ const fetched = await fetchGuardedResponse(url.toString(), {
737
+ headers: { Accept: "text/html, application/xhtml+xml", "User-Agent": "akm-cli x fetcher" },
738
+ signal: context.signal,
739
+ }, { timeoutMs: context.timeoutMs, retries: 1, allowPrivateHosts: context.allowPrivateHosts });
740
+ const { response } = fetched;
741
+ if (!response.ok) {
742
+ await response.body?.cancel().catch(() => undefined);
743
+ return null;
744
+ }
745
+ const html = await readBodyWithByteCap(response, X_PAGE_BYTE_CAP, {
746
+ bodyTimeoutMs: X_BODY_TIMEOUT_MS,
747
+ signal: context.signal,
748
+ });
749
+ return { html, finalUrl: fetched.finalUrl };
750
+ }
751
+ catch (error) {
752
+ if (context.signal?.aborted)
753
+ throw error;
754
+ if (error instanceof ResponseTooLargeError)
755
+ return null;
756
+ return null;
757
+ }
758
+ }
759
+ function postSnapshot(post) {
760
+ const postId = validXId(post.id);
761
+ if (!postId)
762
+ throw new Error("X API returned an invalid post id");
763
+ const username = post.username || "unknown";
764
+ const canonicalUrl = post.username
765
+ ? `https://x.com/${post.username}/status/${postId}`
766
+ : `https://x.com/i/status/${postId}`;
767
+ const markdown = [escapeXPlainText(post.text), post.createdAt ? `\n${post.createdAt}` : ""]
768
+ .filter(Boolean)
769
+ .join("\n");
770
+ return {
771
+ url: canonicalUrl,
772
+ title: post.username ? `X post by @${post.username}` : `X post ${postId}`,
773
+ markdown,
774
+ preferredName: avoidReservedBasename(`x/${username}/status/${postId}`),
775
+ tags: ["x", "twitter", "social", "post"],
776
+ };
777
+ }
778
+ function articleSnapshot(article, resource, usernameHint) {
779
+ const id = resource.kind === "article" ? resource.articleId : resource.postId;
780
+ const url = resource.kind === "article"
781
+ ? `https://x.com/i/article/${resource.articleId}`
782
+ : usernameHint
783
+ ? `https://x.com/${usernameHint}/status/${resource.postId}`
784
+ : `https://x.com/i/status/${resource.postId}`;
785
+ const preferredName = resource.kind === "article"
786
+ ? `x/article/${resource.articleId}`
787
+ : usernameHint
788
+ ? `x/${usernameHint}/status/${resource.postId}`
789
+ : `x/status/${resource.postId}`;
790
+ return {
791
+ url,
792
+ title: article.title || `X Article ${id}`,
793
+ markdown: escapeXPlainText(article.body),
794
+ preferredName: avoidReservedBasename(preferredName),
795
+ tags: ["x", "twitter", "social", "article"],
796
+ };
797
+ }
798
+ /** Build the RSS fallback URL from a template containing `{username}`. */
799
+ export function buildXRssUrl(template, username) {
800
+ const raw = template?.trim();
801
+ if (!raw)
802
+ return null;
803
+ if (!raw.includes("{username}")) {
804
+ warn('[akm] x-profile: X_RSS_TEMPLATE must contain the "{username}" placeholder; ignoring it.');
805
+ return null;
806
+ }
807
+ try {
808
+ const url = new URL(raw.replaceAll("{username}", encodeURIComponent(username)));
809
+ if (url.protocol !== "http:" && url.protocol !== "https:")
810
+ return null;
811
+ return url;
812
+ }
813
+ catch {
814
+ return null;
815
+ }
816
+ }
817
+ async function fetchProfile(username, context) {
818
+ const token = resolveXBearerToken(context);
819
+ if (token) {
820
+ try {
821
+ const tweets = await fetchProfileViaApi(username, token, context);
822
+ const markdown = tweets ? renderProfileMarkdown(username, tweets) : "";
823
+ if (markdown) {
824
+ return {
825
+ url: `https://x.com/${username}`,
826
+ title: `X — @${username}`,
827
+ markdown,
828
+ preferredName: avoidReservedBasename(`x/${username}`),
829
+ tags: ["x", "twitter", "social"],
830
+ };
831
+ }
832
+ }
833
+ catch (error) {
834
+ if (context.signal?.aborted)
835
+ throw error;
836
+ }
837
+ }
838
+ const rssUrl = buildXRssUrl(process.env.X_RSS_TEMPLATE, username);
839
+ if (rssUrl) {
840
+ const snapshot = await rssFetcher.fetch(rssUrl, context);
841
+ if (snapshot) {
842
+ return {
843
+ ...snapshot,
844
+ url: `https://x.com/${username}`,
845
+ title: `X — @${username}`,
846
+ preferredName: avoidReservedBasename(`x/${username}`),
847
+ tags: ["x", "twitter", "social"],
848
+ };
849
+ }
850
+ }
851
+ warn("[akm] x-profile: no content for @%s. Set X_BEARER_TOKEN, or store the token as the " +
852
+ "secrets/x-bearer-token akm secret, or set X_RSS_TEMPLATE to an RSS bridge URL containing {username}.", username);
853
+ return null;
854
+ }
855
+ const xFetcher = {
856
+ name: "x",
857
+ matches(url) {
858
+ return extractXResource(url) !== null;
859
+ },
860
+ async fetch(url, context) {
861
+ const resource = extractXResource(url);
862
+ if (!resource)
863
+ return null;
864
+ if (resource.kind === "profile")
865
+ return fetchProfile(resource.username, context);
866
+ const publicUrl = resource.kind === "status"
867
+ ? new URL(resource.usernameHint
868
+ ? `https://x.com/${resource.usernameHint}/status/${resource.postId}`
869
+ : `https://x.com/i/status/${resource.postId}`)
870
+ : url;
871
+ const publicPage = await fetchPublicXHtml(publicUrl, context);
872
+ let html = null;
873
+ let resolvedResource = resource;
874
+ if (publicPage) {
875
+ const finalResource = extractXResource(new URL(publicPage.finalUrl));
876
+ if ((resource.kind === "status" && finalResource?.kind === "status" && finalResource.postId === resource.postId) ||
877
+ (resource.kind === "article" &&
878
+ finalResource?.kind === "article" &&
879
+ finalResource.articleId === resource.articleId)) {
880
+ resolvedResource = finalResource;
881
+ html = publicPage.html;
882
+ }
883
+ }
884
+ if (html) {
885
+ const article = extractPublicXArticle(html, resource.kind === "status" ? { postId: resource.postId } : { articleId: resource.articleId });
886
+ if (article) {
887
+ return articleSnapshot(article, resolvedResource, resolvedResource.kind === "status" ? resolvedResource.usernameHint : undefined);
888
+ }
889
+ }
890
+ if (resource.kind === "article")
891
+ return null;
892
+ const token = resolveXBearerToken(context);
893
+ if (token) {
894
+ try {
895
+ const post = await fetchPostViaApi(resource.postId, resolvedResource.kind === "status" ? resolvedResource.usernameHint : resource.usernameHint, token, context);
896
+ if (post)
897
+ return postSnapshot(post);
898
+ }
899
+ catch (error) {
900
+ if (context.signal?.aborted)
901
+ throw error;
902
+ }
903
+ }
904
+ const publicPost = html
905
+ ? publicPostFromHtml(html, resource.postId, resolvedResource.kind === "status" ? resolvedResource.usernameHint : resource.usernameHint)
906
+ : null;
907
+ return publicPost ? postSnapshot(publicPost) : null;
908
+ },
909
+ };
910
+ export default xFetcher;