akm-cli 0.9.0-rc.9 → 0.9.1-beta.1

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 (477) hide show
  1. package/CHANGELOG.md +1770 -44
  2. package/README.md +38 -47
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +499 -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 +74 -89
  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 +148 -88
  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/llm-usage.js +2 -1
  57. package/dist/commands/health/md-report.js +5 -9
  58. package/dist/commands/health/metrics.js +62 -20
  59. package/dist/commands/health/renderers.js +47 -0
  60. package/dist/commands/health/report-view-model.js +4 -5
  61. package/dist/commands/health/stash-exposure.js +1 -1
  62. package/dist/commands/health/surfaces.js +4 -122
  63. package/dist/commands/health/task-runs.js +3 -67
  64. package/dist/commands/health/types-improve.js +7 -0
  65. package/dist/commands/health.js +160 -35
  66. package/dist/commands/improve/anti-collapse.js +2 -2
  67. package/dist/commands/improve/autonomy-gate.js +68 -0
  68. package/dist/commands/improve/collapse-detector.js +41 -40
  69. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  70. package/dist/commands/improve/consolidate/merge.js +4 -0
  71. package/dist/commands/improve/consolidate.js +140 -1000
  72. package/dist/commands/improve/distill/promote-memory.js +12 -12
  73. package/dist/commands/improve/distill/quality-gate.js +11 -6
  74. package/dist/commands/improve/distill.js +58 -69
  75. package/dist/commands/improve/eligibility.js +101 -55
  76. package/dist/commands/improve/extract-cli.js +14 -133
  77. package/dist/commands/improve/improve-cli.js +98 -114
  78. package/dist/commands/improve/improve-result-file.js +1 -28
  79. package/dist/commands/improve/improve-strategies.js +8 -5
  80. package/dist/commands/improve/improve.js +254 -101
  81. package/dist/commands/improve/locks.js +7 -0
  82. package/dist/commands/improve/loop-stages.js +182 -20
  83. package/dist/commands/improve/memory/derived-ref.js +45 -43
  84. package/dist/commands/improve/memory/memory-belief.js +1 -1
  85. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  86. package/dist/commands/improve/memory/memory-improve.js +14 -5
  87. package/dist/commands/improve/outcome-loop.js +22 -65
  88. package/dist/commands/improve/preparation.js +114 -123
  89. package/dist/commands/improve/proactive-maintenance.js +2 -5
  90. package/dist/commands/improve/reflect.js +56 -160
  91. package/dist/commands/improve/run-context.js +5 -0
  92. package/dist/commands/improve/salience.js +11 -122
  93. package/dist/commands/improve/session-asset.js +4 -0
  94. package/dist/commands/improve/source-identity.js +10 -38
  95. package/dist/commands/lint/base-linter.js +51 -131
  96. package/dist/commands/lint/env-key-rules.js +31 -47
  97. package/dist/commands/lint/index.js +444 -88
  98. package/dist/commands/lint/types.js +22 -1
  99. package/dist/commands/{events.js → log.js} +33 -38
  100. package/dist/commands/migrate-cli.js +92 -12
  101. package/dist/commands/migration-tool.js +46 -0
  102. package/dist/commands/observability-cli.js +70 -209
  103. package/dist/commands/proposal/drain.js +101 -29
  104. package/dist/commands/proposal/proposal-cli.js +76 -48
  105. package/dist/commands/proposal/proposal.js +54 -18
  106. package/dist/commands/proposal/propose-cli.js +88 -0
  107. package/dist/commands/proposal/propose.js +23 -15
  108. package/dist/commands/proposal/repository.js +718 -279
  109. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  110. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  111. package/dist/commands/proposal/validators/proposals.js +4 -7
  112. package/dist/commands/read/curate.js +34 -53
  113. package/dist/commands/read/knowledge.js +150 -95
  114. package/dist/commands/read/registry-search.js +2 -2
  115. package/dist/commands/read/remember-cli.js +42 -15
  116. package/dist/commands/read/search-cli.js +180 -78
  117. package/dist/commands/read/search.js +58 -43
  118. package/dist/commands/read/show.js +197 -141
  119. package/dist/commands/registry-cli.js +12 -51
  120. package/dist/commands/remember.js +14 -57
  121. package/dist/commands/sources/add-cli.js +100 -31
  122. package/dist/commands/sources/bundle-cli.js +166 -0
  123. package/dist/commands/sources/bundle-config-ops.js +7 -2
  124. package/dist/commands/sources/info.js +30 -7
  125. package/dist/commands/sources/init.js +12 -12
  126. package/dist/commands/sources/installed-stashes.js +387 -98
  127. package/dist/commands/sources/schema-repair.js +3 -2
  128. package/dist/commands/sources/self-update.js +131 -38
  129. package/dist/commands/sources/source-add.js +72 -17
  130. package/dist/commands/sources/source-clone.js +129 -45
  131. package/dist/commands/sources/source-manage.js +43 -23
  132. package/dist/commands/sources/sources-cli.js +57 -208
  133. package/dist/commands/sources/stash-cli.js +46 -53
  134. package/dist/commands/tasks/tasks-cli.js +91 -97
  135. package/dist/commands/tasks/tasks.js +284 -423
  136. package/dist/commands/workflow-cli.js +181 -450
  137. package/dist/core/abort-deadline.js +28 -0
  138. package/dist/core/adapter/adapters/agent-skills-adapter.js +67 -5
  139. package/dist/core/adapter/adapters/akm-adapter.js +60 -38
  140. package/dist/core/adapter/adapters/akm-lint.js +109 -42
  141. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  142. package/dist/core/adapter/adapters/akm-task-adapter.js +54 -31
  143. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  144. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  145. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  146. package/dist/core/adapter/adapters/index.js +6 -6
  147. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  148. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  149. package/dist/core/adapter/adapters/shared.js +3 -19
  150. package/dist/core/adapter/adapters/tool-dir-shared.js +13 -6
  151. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  152. package/dist/core/adapter/detect-adapter.js +17 -0
  153. package/dist/core/adapter/recognize-match.js +6 -4
  154. package/dist/core/adapter/validate-context.js +214 -0
  155. package/dist/core/asset/akm-markdown.js +63 -0
  156. package/dist/core/asset/asset-placement.js +20 -6
  157. package/dist/core/asset/asset-ref.js +11 -9
  158. package/dist/core/asset/frontmatter-lint.js +30 -0
  159. package/dist/core/asset/frontmatter.js +29 -9
  160. package/dist/core/asset/markdown.js +40 -51
  161. package/dist/core/asset/resolve-ref.js +89 -18
  162. package/dist/core/asset/stash-meta.js +1 -1
  163. package/dist/core/bundle-id.js +51 -0
  164. package/dist/core/common.js +218 -44
  165. package/dist/core/concurrent.js +32 -0
  166. package/dist/core/config/config-io.js +12 -1
  167. package/dist/core/config/config-schema.js +35 -8
  168. package/dist/core/config/config-sources.js +55 -11
  169. package/dist/core/config/config-walker.js +25 -9
  170. package/dist/core/config/config.js +9 -48
  171. package/dist/core/config/experimental.js +21 -0
  172. package/dist/core/config/schema/embedding.js +5 -1
  173. package/dist/core/config/schema/experimental.js +30 -0
  174. package/dist/core/config/schema/improve-processes.js +0 -6
  175. package/dist/core/config/schema/improve.js +21 -3
  176. package/dist/core/config/schema/index-config.js +8 -15
  177. package/dist/core/config/schema/output.js +4 -1
  178. package/dist/core/config/schema/setup.js +9 -18
  179. package/dist/core/config/schema/sources-bundles.js +49 -33
  180. package/dist/core/config/schema/workflow.js +14 -3
  181. package/dist/core/env-secret-ref.js +76 -46
  182. package/dist/core/errors.js +43 -12
  183. package/dist/core/events.js +76 -152
  184. package/dist/core/file-change.js +6 -5
  185. package/dist/core/file-lock.js +7 -1
  186. package/dist/core/fs-txn.js +83 -7
  187. package/dist/core/git-message.js +2 -2
  188. package/dist/core/improve-result.js +6 -100
  189. package/dist/core/json-schema.js +327 -9
  190. package/dist/core/lesson-lint.js +1 -17
  191. package/dist/core/logs-db.js +2 -1
  192. package/dist/core/loopback.js +89 -0
  193. package/dist/core/migration-operation.js +33 -2
  194. package/dist/core/mutation-target.js +78 -0
  195. package/dist/core/parse.js +4 -1
  196. package/dist/core/path-access.js +107 -0
  197. package/dist/core/paths.js +17 -20
  198. package/dist/core/recognition-util.js +12 -14
  199. package/dist/core/redaction.js +98 -17
  200. package/dist/core/spawn-env.js +234 -0
  201. package/dist/core/standards/resolve-standards-context.js +2 -14
  202. package/dist/core/standards/resolve-stash-standards.js +2 -2
  203. package/dist/core/standards/resolve-type-conventions.js +2 -2
  204. package/dist/core/state/migrations.js +41 -18
  205. package/dist/core/state-db-scope.js +134 -0
  206. package/dist/core/state-db.js +5 -14
  207. package/dist/core/structured.js +1 -1
  208. package/dist/core/subprocess.js +182 -36
  209. package/dist/core/text-truncation.js +9 -5
  210. package/dist/core/type-presentation.js +3 -3
  211. package/dist/core/warn.js +0 -3
  212. package/dist/core/write-provenance.js +85 -0
  213. package/dist/core/write-source.js +778 -95
  214. package/dist/indexer/bundle-identity-guard.js +3 -2
  215. package/dist/indexer/db/graph-db.js +17 -30
  216. package/dist/indexer/ensure-index.js +11 -3
  217. package/dist/indexer/graph/graph-boost.js +9 -34
  218. package/dist/indexer/graph/graph-extraction.js +8 -5
  219. package/dist/indexer/index-writer-lock.js +53 -17
  220. package/dist/indexer/index-written-assets.js +33 -24
  221. package/dist/indexer/indexer.js +551 -253
  222. package/dist/indexer/installations.js +14 -96
  223. package/dist/indexer/passes/dir-staleness.js +16 -9
  224. package/dist/indexer/passes/memory-inference.js +15 -9
  225. package/dist/indexer/passes/metadata.js +113 -47
  226. package/dist/indexer/scan/doc-to-entry.js +38 -1
  227. package/dist/indexer/scan/drain-dir.js +13 -23
  228. package/dist/indexer/search/db-search.js +107 -69
  229. package/dist/indexer/search/fts-query.js +47 -24
  230. package/dist/indexer/search/ranking-contributors.js +42 -20
  231. package/dist/indexer/search/ranking.js +18 -99
  232. package/dist/indexer/search/search-fields.js +7 -2
  233. package/dist/indexer/search/search-source.js +82 -93
  234. package/dist/indexer/usage/usage-events.js +0 -89
  235. package/dist/indexer/walk/file-context.js +2 -1
  236. package/dist/indexer/walk/matchers.js +30 -43
  237. package/dist/indexer/walk/path-resolver.js +7 -2
  238. package/dist/indexer/walk/walker.js +38 -12
  239. package/dist/integrations/agent/builders.js +0 -6
  240. package/dist/integrations/agent/config.js +2 -2
  241. package/dist/integrations/agent/detect.js +49 -19
  242. package/dist/integrations/agent/engine-fallback.js +76 -0
  243. package/dist/integrations/agent/engine-resolution.js +24 -11
  244. package/dist/integrations/agent/model-aliases.js +1 -1
  245. package/dist/integrations/agent/profiles.js +23 -1
  246. package/dist/integrations/agent/prompts.js +12 -8
  247. package/dist/integrations/agent/runner-dispatch.js +4 -2
  248. package/dist/integrations/agent/runner.js +0 -1
  249. package/dist/integrations/agent/spawn.js +20 -93
  250. package/dist/integrations/github.js +1 -1
  251. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  252. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  253. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  254. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  255. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  256. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  257. package/dist/integrations/harnesses/index.js +3 -7
  258. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  259. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  260. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  261. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  262. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  263. package/dist/integrations/lockfile.js +132 -8
  264. package/dist/integrations/session-logs/index.js +3 -28
  265. package/dist/llm/client.js +136 -100
  266. package/dist/llm/embedders/remote.js +13 -5
  267. package/dist/llm/feature-gate.js +4 -12
  268. package/dist/llm/graph-extract.js +5 -11
  269. package/dist/llm/memory-infer.js +144 -1
  270. package/dist/llm/metadata-enhance.js +5 -7
  271. package/dist/llm/structured-call.js +1 -1
  272. package/dist/llm/usage-persist.js +26 -5
  273. package/dist/llm/usage-telemetry.js +25 -2
  274. package/dist/output/cli-hints.js +1 -2
  275. package/dist/output/context.js +22 -7
  276. package/dist/output/format-exempt.js +80 -0
  277. package/dist/output/generic-render.js +259 -0
  278. package/dist/output/render-registry.js +57 -0
  279. package/dist/output/renderers.js +14 -36
  280. package/dist/output/shapes/curate.js +10 -1
  281. package/dist/output/shapes/events.js +12 -7
  282. package/dist/output/shapes/helpers.js +56 -83
  283. package/dist/output/shapes/migrate.js +8 -0
  284. package/dist/output/shapes/passthrough.js +7 -41
  285. package/dist/output/shapes/proposal/producer.js +15 -7
  286. package/dist/output/shapes.js +2 -9
  287. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  288. package/dist/output/text/bundle-show.js +7 -0
  289. package/dist/output/text/command-format.js +164 -96
  290. package/dist/output/text/env.js +1 -3
  291. package/dist/output/text/events.js +8 -7
  292. package/dist/output/text/health-format.js +103 -0
  293. package/dist/output/text/health.js +7 -0
  294. package/dist/output/text/helpers.js +10 -8
  295. package/dist/output/text/lint-format.js +56 -0
  296. package/dist/{migrate-storage-node.mjs → output/text/lint.js} +2 -5
  297. package/dist/output/text/migrate.js +88 -0
  298. package/dist/output/text/proposal/producer.js +4 -2
  299. package/dist/output/text/proposal-format.js +44 -72
  300. package/dist/output/text/registry-commands.js +1 -2
  301. package/dist/output/text/show-directives.js +15 -7
  302. package/dist/output/text/status-list.js +32 -0
  303. package/dist/output/text/{save.js → sync.js} +2 -2
  304. package/dist/output/text/workflow-format.js +24 -203
  305. package/dist/output/text/workflow.js +1 -7
  306. package/dist/output/text.js +16 -17
  307. package/dist/registry/factory.js +4 -6
  308. package/dist/registry/origin-resolve.js +16 -27
  309. package/dist/registry/providers/skills-sh.js +3 -3
  310. package/dist/registry/providers/static-index.js +13 -23
  311. package/dist/registry/resolve.js +42 -7
  312. package/dist/registry/semver.js +34 -84
  313. package/dist/runtime.js +2 -23
  314. package/dist/scripts/akm-migrate-node.js +60972 -0
  315. package/dist/scripts/akm-migrate.js +60310 -0
  316. package/dist/setup/detect.js +42 -15
  317. package/dist/setup/registry-stash-loader.js +2 -2
  318. package/dist/setup/setup.js +236 -136
  319. package/dist/setup/steps/connection.js +7 -9
  320. package/dist/setup/steps/platforms.js +9 -9
  321. package/dist/setup/steps/semantic.js +15 -3
  322. package/dist/setup/steps/sources.js +12 -13
  323. package/dist/setup/steps/stashdir.js +2 -3
  324. package/dist/setup/steps/tasks.js +237 -120
  325. package/dist/sources/freshness.js +1 -1
  326. package/dist/sources/provider-factory.js +11 -17
  327. package/dist/sources/providers/filesystem.js +2 -3
  328. package/dist/sources/providers/git-install.js +278 -34
  329. package/dist/sources/providers/git-provider.js +25 -23
  330. package/dist/sources/providers/git-stash.js +414 -106
  331. package/dist/sources/providers/git.js +2 -2
  332. package/dist/sources/providers/npm.js +16 -19
  333. package/dist/sources/providers/provider-utils.js +7 -4
  334. package/dist/sources/providers/sync-from-ref.js +3 -9
  335. package/dist/sources/providers/website.js +6 -1
  336. package/dist/sources/resolve.js +6 -5
  337. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  338. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  339. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  340. package/dist/sources/snapshot-fetchers/github.js +100 -0
  341. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  342. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  343. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  344. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  345. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  346. package/dist/sources/snapshot-fetchers/website-ingest.js +686 -282
  347. package/dist/sources/snapshot-fetchers/x.js +910 -0
  348. package/dist/storage/database.js +13 -1
  349. package/dist/storage/engines/sqlite-migrations.js +23 -111
  350. package/dist/storage/managed-db.js +21 -2
  351. package/dist/storage/repositories/canaries-repository.js +1 -1
  352. package/dist/storage/repositories/events-repository.js +27 -11
  353. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  354. package/dist/storage/repositories/index-connection.js +44 -8
  355. package/dist/storage/repositories/index-entries-repository.js +157 -241
  356. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  357. package/dist/storage/repositories/index-fts-repository.js +5 -2
  358. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  359. package/dist/storage/repositories/index-meta-repository.js +2 -3
  360. package/dist/storage/repositories/index-schema.js +10 -25
  361. package/dist/storage/repositories/index-utility-repository.js +15 -28
  362. package/dist/storage/repositories/index-vec-repository.js +6 -1
  363. package/dist/storage/repositories/outcome-repository.js +119 -0
  364. package/dist/storage/repositories/proposals-repository.js +296 -59
  365. package/dist/storage/repositories/registry-cache.js +19 -0
  366. package/dist/storage/repositories/salience-repository.js +172 -0
  367. package/dist/storage/repositories/task-history-repository.js +15 -13
  368. package/dist/storage/repositories/workflow-runs-repository.js +118 -53
  369. package/dist/tasks/backends/cron.js +105 -15
  370. package/dist/tasks/backends/index.js +1 -1
  371. package/dist/tasks/backends/launchd.js +85 -38
  372. package/dist/tasks/backends/schtasks.js +135 -15
  373. package/dist/tasks/embedded.js +56 -40
  374. package/dist/tasks/log-redaction.js +156 -0
  375. package/dist/tasks/parser.js +87 -160
  376. package/dist/tasks/resolve-akm-bin.js +137 -59
  377. package/dist/tasks/runner.js +209 -44
  378. package/dist/tasks/scheduler-invocation.js +220 -10
  379. package/dist/tasks/schema.js +89 -1
  380. package/dist/tasks/task-id.js +1 -3
  381. package/dist/tasks/validator.js +20 -6
  382. package/dist/workflows/authoring/authoring.js +94 -143
  383. package/dist/workflows/authoring/scope-key.js +1 -1
  384. package/dist/workflows/concurrency-policy.js +95 -1
  385. package/dist/workflows/exec/dispatch-redaction.js +114 -0
  386. package/dist/workflows/exec/exec-unit.js +542 -0
  387. package/dist/workflows/exec/frozen-judge.js +130 -32
  388. package/dist/workflows/exec/native-executor.js +513 -274
  389. package/dist/workflows/exec/param-secrets.js +12 -11
  390. package/dist/workflows/exec/run-workflow.js +496 -237
  391. package/dist/workflows/exec/step-work.js +616 -412
  392. package/dist/workflows/exec/unit-dispatch.js +34 -0
  393. package/dist/workflows/exec/unit-writer.js +53 -13
  394. package/dist/workflows/exec/worktree.js +454 -41
  395. package/dist/workflows/ir/compile.js +166 -271
  396. package/dist/workflows/ir/freeze.js +119 -42
  397. package/dist/workflows/ir/params.js +135 -11
  398. package/dist/workflows/ir/plan-hash.js +1 -1
  399. package/dist/workflows/ir/schema.js +129 -45
  400. package/dist/workflows/parser.js +1070 -307
  401. package/dist/workflows/program/expressions.js +20 -208
  402. package/dist/workflows/program/schema.js +31 -10
  403. package/dist/workflows/renderer.js +123 -68
  404. package/dist/workflows/resource-limits.js +184 -0
  405. package/dist/workflows/runtime/checkin.js +3 -3
  406. package/dist/workflows/runtime/plan-classifier.js +16 -75
  407. package/dist/workflows/runtime/runs.js +331 -132
  408. package/dist/workflows/runtime/unit-checkin.js +1 -1
  409. package/dist/workflows/runtime/unit-phases.js +2 -2
  410. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  411. package/dist/workflows/schema.js +1 -11
  412. package/dist/workflows/validate-summary.js +47 -38
  413. package/dist/workflows/validator.js +21 -62
  414. package/docs/README.md +110 -0
  415. package/docs/migration/README.md +8 -0
  416. package/docs/migration/release-notes/0.7.0.md +11 -11
  417. package/docs/migration/release-notes/0.9.0.md +209 -27
  418. package/docs/migration/v0.7-to-v0.8.md +48 -48
  419. package/docs/migration/v0.8-to-v0.9.md +567 -209
  420. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  421. package/docs/reference/README.md +19 -0
  422. package/docs/reference/bundle-types.md +19 -0
  423. package/docs/reference/cli.md +2342 -0
  424. package/docs/reference/configuration.md +371 -0
  425. package/docs/reference/data-and-telemetry.md +129 -46
  426. package/docs/reference/supported-formats.md +50 -0
  427. package/docs/reference/workflow-schema.md +1014 -0
  428. package/docs/reference/workflows.md +51 -0
  429. package/package.json +31 -13
  430. package/schemas/akm-asset-envelope.json +93 -0
  431. package/schemas/akm-config.json +89 -128
  432. package/schemas/akm-task.json +27 -5
  433. package/schemas/akm-workflow.json +157 -81
  434. package/dist/assets/tasks/core/backup.yml +0 -5
  435. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  436. package/dist/cli/config-migrate.js +0 -1878
  437. package/dist/cli/config-validate.js +0 -41
  438. package/dist/commands/backup-cli.js +0 -56
  439. package/dist/commands/bundle/bundle-cli.js +0 -68
  440. package/dist/commands/bundle/bundle.js +0 -219
  441. package/dist/commands/graph/graph-cli.js +0 -124
  442. package/dist/commands/graph/graph.js +0 -489
  443. package/dist/commands/improve/extract-watch.js +0 -140
  444. package/dist/commands/mv-cli.js +0 -1221
  445. package/dist/commands/sources/history.js +0 -201
  446. package/dist/commands/tasks/default-tasks.js +0 -186
  447. package/dist/core/migration-backup.js +0 -1231
  448. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  449. package/dist/llm/memory-infer-impl.js +0 -138
  450. package/dist/migrate/legacy/config-source-migration.js +0 -257
  451. package/dist/migrate/legacy/content-migration.js +0 -350
  452. package/dist/migrate/legacy/legacy-layout.js +0 -779
  453. package/dist/migrate/legacy/legacy-paths.js +0 -25
  454. package/dist/migrate/legacy/legacy-stash-json.js +0 -80
  455. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  456. package/dist/migrate/legacy/task-target-ref-migration.js +0 -278
  457. package/dist/migrate/legacy/three-db-cutover.js +0 -845
  458. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  459. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  460. package/dist/migrate/legacy-ref-grammar.js +0 -214
  461. package/dist/output/shapes/distill.js +0 -14
  462. package/dist/output/shapes/history.js +0 -11
  463. package/dist/output/text/distill.js +0 -6
  464. package/dist/output/text/enable-disable.js +0 -8
  465. package/dist/output/text/history.js +0 -6
  466. package/dist/registry/build-index.js +0 -382
  467. package/dist/schemas/akm-config.json +0 -4704
  468. package/dist/schemas/akm-task.json +0 -87
  469. package/dist/schemas/akm-workflow.json +0 -372
  470. package/dist/scripts/migrate-storage.js +0 -3816
  471. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  472. package/dist/workflows/cli.js +0 -53
  473. package/dist/workflows/exec/brief.js +0 -481
  474. package/dist/workflows/exec/report.js +0 -1460
  475. package/dist/workflows/exec/watch.js +0 -116
  476. package/dist/workflows/program/parser.js +0 -813
  477. 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;