akm-cli 0.9.0-rc.0 → 0.9.0-rc.13

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 (598) hide show
  1. package/CHANGELOG.md +1283 -22
  2. package/README.md +62 -37
  3. package/SECURITY.md +46 -31
  4. package/dist/akm +162 -38
  5. package/dist/akm-migrate +44 -0
  6. package/dist/assets/backends/schtasks-template.xml +2 -1
  7. package/dist/assets/hints/cli-hints-full.md +268 -118
  8. package/dist/assets/hints/cli-hints-short.md +87 -24
  9. package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
  10. package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
  11. package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
  12. package/dist/assets/improve-strategies/frequent.json +15 -0
  13. package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
  14. package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
  15. package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
  16. package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
  17. package/dist/assets/improve-strategies/reflect-distill.json +30 -0
  18. package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
  19. package/dist/assets/prompts/consolidate-system.md +5 -5
  20. package/dist/assets/prompts/extract-session.md +2 -6
  21. package/dist/assets/prompts/memory-infer-user.md +2 -3
  22. package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
  23. package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
  24. package/dist/assets/prompts/reflect-output-repair.md +3 -0
  25. package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
  26. package/dist/assets/stash-skeleton/README.md +38 -10
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
  29. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
  39. package/dist/assets/tasks/core/extract.yml +3 -2
  40. package/dist/assets/tasks/core/improve.yml +2 -1
  41. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  42. package/dist/assets/tasks/core/sync.yml +1 -0
  43. package/dist/assets/tasks/core/version-check.yml +2 -1
  44. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  45. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  46. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  47. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  48. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  49. package/dist/assets/templates/html/health.html +5 -4
  50. package/dist/assets/workflows/workflow-template.md +31 -15
  51. package/dist/cli/invocation.js +279 -0
  52. package/dist/cli/parse-args.js +5 -90
  53. package/dist/cli/retired-commands.js +78 -0
  54. package/dist/cli/shared.js +158 -48
  55. package/dist/cli-node.mjs +2 -1
  56. package/dist/cli.js +747 -293
  57. package/dist/commands/agent/agent-dispatch.js +19 -18
  58. package/dist/commands/agent/agent-support.js +0 -24
  59. package/dist/commands/agent/contribute-cli.js +43 -97
  60. package/dist/commands/completions.js +80 -23
  61. package/dist/commands/config-cli.js +44 -281
  62. package/dist/commands/env/env-binding.js +99 -0
  63. package/dist/commands/env/env-cli.js +84 -224
  64. package/dist/commands/env/env.js +12 -163
  65. package/dist/commands/env/marker-path.js +6 -0
  66. package/dist/commands/env/secret-cli.js +45 -61
  67. package/dist/commands/env/secret.js +32 -62
  68. package/dist/commands/feedback-cli.js +179 -85
  69. package/dist/commands/health/accept-rate.js +58 -0
  70. package/dist/commands/health/advisories.js +7 -8
  71. package/dist/commands/health/checks.js +279 -94
  72. package/dist/commands/health/html-report.js +197 -578
  73. package/dist/commands/health/improve-metrics.js +277 -246
  74. package/dist/commands/health/llm-usage.js +19 -19
  75. package/dist/commands/health/md-report.js +16 -7
  76. package/dist/commands/health/metrics.js +67 -32
  77. package/dist/commands/health/renderers.js +47 -0
  78. package/dist/commands/health/report-view-model.js +508 -0
  79. package/dist/commands/health/stash-exposure.js +1 -1
  80. package/dist/commands/health/surfaces.js +16 -56
  81. package/dist/commands/health/task-runs.js +3 -67
  82. package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
  83. package/dist/commands/health/types-improve.js +29 -0
  84. package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
  85. package/dist/commands/health/types-result.js +7 -0
  86. package/dist/commands/health/types-runs.js +4 -0
  87. package/dist/commands/health/types-session-log.js +4 -0
  88. package/dist/commands/health/types-windows.js +4 -0
  89. package/dist/commands/health/types.js +26 -21
  90. package/dist/commands/health/windows.js +2 -3
  91. package/dist/commands/health.js +296 -167
  92. package/dist/commands/improve/anti-collapse.js +5 -5
  93. package/dist/commands/improve/autonomy-gate.js +68 -0
  94. package/dist/commands/improve/collapse-detector.js +65 -52
  95. package/dist/commands/improve/consolidate/chunking.js +9 -7
  96. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  97. package/dist/commands/improve/consolidate/merge.js +4 -0
  98. package/dist/commands/improve/consolidate.js +454 -1354
  99. package/dist/commands/improve/content-hash.js +39 -0
  100. package/dist/commands/improve/distill/content-repair.js +4 -10
  101. package/dist/commands/improve/distill/promote-memory.js +89 -64
  102. package/dist/commands/improve/distill/quality-gate.js +118 -42
  103. package/dist/commands/improve/distill-guards.js +1 -1
  104. package/dist/commands/improve/distill-promotion-policy.js +33 -888
  105. package/dist/commands/improve/distill.js +607 -363
  106. package/dist/commands/improve/eligibility.js +165 -79
  107. package/dist/commands/improve/extract-cli.js +35 -126
  108. package/dist/commands/improve/extract-prompt.js +6 -35
  109. package/dist/commands/improve/extract.js +640 -391
  110. package/dist/commands/improve/feedback-valence.js +2 -12
  111. package/dist/commands/improve/improve-cli.js +134 -135
  112. package/dist/commands/improve/improve-result-file.js +30 -50
  113. package/dist/commands/improve/improve-run-types.js +4 -0
  114. package/dist/commands/improve/improve-strategies.js +135 -0
  115. package/dist/commands/improve/improve.js +904 -701
  116. package/dist/commands/improve/locks.js +64 -111
  117. package/dist/commands/improve/loop-stages.js +1110 -923
  118. package/dist/commands/improve/memory/derived-ref.js +124 -0
  119. package/dist/commands/improve/memory/memory-belief.js +79 -7
  120. package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
  121. package/dist/commands/improve/memory/memory-improve.js +25 -37
  122. package/dist/commands/improve/outcome-loop.js +25 -88
  123. package/dist/commands/improve/preparation.js +1034 -813
  124. package/dist/commands/improve/proactive-maintenance.js +34 -9
  125. package/dist/commands/improve/proposal-envelope.js +31 -0
  126. package/dist/commands/improve/reflect.js +983 -794
  127. package/dist/commands/improve/run-context.js +119 -0
  128. package/dist/commands/improve/salience.js +24 -127
  129. package/dist/commands/improve/session-asset.js +7 -3
  130. package/dist/commands/improve/shared.js +14 -34
  131. package/dist/commands/improve/source-identity.js +28 -0
  132. package/dist/commands/improve/triage.js +20 -17
  133. package/dist/commands/lint/base-linter.js +340 -313
  134. package/dist/commands/lint/env-key-rules.js +31 -47
  135. package/dist/commands/lint/index.js +185 -30
  136. package/dist/commands/{events.js → log.js} +28 -38
  137. package/dist/commands/migrate-cli.js +54 -0
  138. package/dist/commands/migration-tool.js +55 -0
  139. package/dist/commands/observability-cli.js +70 -208
  140. package/dist/commands/proposal/diff-format.js +50 -0
  141. package/dist/commands/proposal/drain-policies.js +0 -6
  142. package/dist/commands/proposal/drain.js +91 -40
  143. package/dist/commands/proposal/proposal-cli.js +134 -132
  144. package/dist/commands/proposal/proposal-types.js +56 -0
  145. package/dist/commands/proposal/proposal.js +83 -65
  146. package/dist/commands/proposal/propose-cli.js +88 -0
  147. package/dist/commands/proposal/propose.js +105 -88
  148. package/dist/commands/proposal/repository.js +1303 -278
  149. package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
  150. package/dist/commands/proposal/validators/proposal-validators.js +61 -12
  151. package/dist/commands/proposal/validators/proposals.js +6 -8
  152. package/dist/commands/read/curate.js +78 -73
  153. package/dist/commands/read/knowledge.js +510 -13
  154. package/dist/commands/read/registry-search.js +2 -2
  155. package/dist/commands/read/remember-cli.js +84 -15
  156. package/dist/commands/read/search-cli.js +203 -96
  157. package/dist/commands/read/search.js +126 -94
  158. package/dist/commands/read/show.js +226 -250
  159. package/dist/commands/registry-cli.js +34 -60
  160. package/dist/commands/remember.js +18 -57
  161. package/dist/commands/sources/add-cli.js +104 -49
  162. package/dist/commands/sources/bundle-cli.js +166 -0
  163. package/dist/commands/sources/bundle-config-ops.js +63 -0
  164. package/dist/commands/sources/info.js +27 -15
  165. package/dist/commands/sources/init.js +30 -40
  166. package/dist/commands/sources/installed-stashes.js +469 -172
  167. package/dist/commands/sources/migration-help.js +7 -4
  168. package/dist/commands/sources/schema-repair.js +10 -9
  169. package/dist/commands/sources/self-update.js +182 -121
  170. package/dist/commands/sources/source-add.js +169 -178
  171. package/dist/commands/sources/source-clone.js +144 -41
  172. package/dist/commands/sources/source-manage.js +94 -59
  173. package/dist/commands/sources/sources-cli.js +64 -205
  174. package/dist/commands/sources/stash-cli.js +91 -54
  175. package/dist/commands/sources/stash-skeleton.js +1 -1
  176. package/dist/commands/tasks/tasks-cli.js +106 -104
  177. package/dist/commands/tasks/tasks.js +445 -262
  178. package/dist/commands/workflow-cli.js +232 -121
  179. package/dist/core/action-contributors.js +1 -1
  180. package/dist/core/activation-policy.js +49 -0
  181. package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
  182. package/dist/core/adapter/adapters/akm-adapter.js +528 -0
  183. package/dist/core/adapter/adapters/akm-lint.js +392 -0
  184. package/dist/core/adapter/adapters/akm-metadata.js +387 -0
  185. package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
  186. package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
  187. package/dist/core/adapter/adapters/claude-adapter.js +61 -0
  188. package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
  189. package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
  190. package/dist/core/adapter/adapters/index.js +80 -0
  191. package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
  192. package/dist/core/adapter/adapters/okf-adapter.js +391 -0
  193. package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
  194. package/dist/core/adapter/adapters/shared.js +286 -0
  195. package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
  196. package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
  197. package/dist/core/adapter/bundle-adapter.js +4 -0
  198. package/dist/core/adapter/detect-adapter.js +17 -0
  199. package/dist/core/adapter/recognize-match.js +44 -0
  200. package/dist/core/adapter/registry.js +56 -0
  201. package/dist/core/adapter/types.js +4 -0
  202. package/dist/core/asset/akm-markdown.js +30 -0
  203. package/dist/core/asset/asset-placement.js +243 -0
  204. package/dist/core/asset/asset-ref.js +110 -79
  205. package/dist/core/asset/asset-serialize.js +20 -0
  206. package/dist/core/asset/frontmatter.js +28 -12
  207. package/dist/core/asset/markdown.js +40 -51
  208. package/dist/core/asset/resolve-ref.js +274 -0
  209. package/dist/core/asset/stash-meta.js +2 -2
  210. package/dist/core/bundle-id.js +51 -0
  211. package/dist/core/common.js +281 -86
  212. package/dist/core/config/config-io.js +42 -128
  213. package/dist/core/config/config-schema.js +233 -834
  214. package/dist/core/config/config-sources.js +162 -39
  215. package/dist/core/config/config-types.js +16 -11
  216. package/dist/core/config/config-version.js +29 -0
  217. package/dist/core/config/config-walker.js +126 -37
  218. package/dist/core/config/config.js +154 -331
  219. package/dist/core/config/deep-merge.js +41 -0
  220. package/dist/core/config/engine-semantics.js +28 -0
  221. package/dist/core/config/experimental.js +21 -0
  222. package/dist/core/config/schema/embedding.js +38 -0
  223. package/dist/core/config/schema/engines.js +116 -0
  224. package/dist/core/config/schema/experimental.js +47 -0
  225. package/dist/core/config/schema/feedback.js +31 -0
  226. package/dist/core/config/schema/improve-processes.js +389 -0
  227. package/dist/core/config/schema/improve.js +94 -0
  228. package/dist/core/config/schema/index-config.js +176 -0
  229. package/dist/core/config/schema/output.js +18 -0
  230. package/dist/core/config/schema/primitives.js +94 -0
  231. package/dist/core/config/schema/search.js +30 -0
  232. package/dist/core/config/schema/setup.js +18 -0
  233. package/dist/core/config/schema/sources-bundles.js +169 -0
  234. package/dist/core/config/schema/workflow.js +29 -0
  235. package/dist/core/env-secret-ref.js +155 -20
  236. package/dist/core/errors.js +17 -15
  237. package/dist/core/events-types.js +4 -0
  238. package/dist/core/events.js +46 -128
  239. package/dist/core/extra-params.js +62 -0
  240. package/dist/core/file-change.js +17 -0
  241. package/dist/core/file-lock.js +202 -57
  242. package/dist/core/fs-txn.js +392 -0
  243. package/dist/core/git-message.js +59 -0
  244. package/dist/core/improve-result.js +167 -0
  245. package/dist/core/json-schema.js +142 -0
  246. package/dist/core/lesson-lint.js +1 -17
  247. package/dist/core/logs-db.js +1 -1
  248. package/dist/core/maintenance-barrier.js +135 -0
  249. package/dist/core/migration-operation.js +44 -0
  250. package/dist/core/mutation-target.js +78 -0
  251. package/dist/core/paths.js +22 -25
  252. package/dist/core/platform.js +10 -0
  253. package/dist/core/recognition-util.js +128 -0
  254. package/dist/core/redaction.js +392 -0
  255. package/dist/core/standards/resolve-standards-context.js +36 -65
  256. package/dist/core/standards/resolve-stash-standards.js +2 -2
  257. package/dist/core/standards/resolve-type-conventions.js +5 -5
  258. package/dist/core/state/migrations.js +242 -11
  259. package/dist/core/state-db.js +98 -10
  260. package/dist/core/structured.js +1 -1
  261. package/dist/core/subprocess.js +303 -0
  262. package/dist/core/text-truncation.js +9 -5
  263. package/dist/core/time.js +20 -0
  264. package/dist/core/type-presentation.js +130 -0
  265. package/dist/core/warn.js +0 -3
  266. package/dist/core/write-source.js +834 -118
  267. package/dist/indexer/bundle-identity-guard.js +92 -0
  268. package/dist/indexer/db/graph-db.js +1 -25
  269. package/dist/indexer/db/llm-cache.js +1 -1
  270. package/dist/indexer/ensure-index.js +30 -9
  271. package/dist/indexer/graph/graph-boost.js +9 -30
  272. package/dist/indexer/graph/graph-extraction.js +41 -27
  273. package/dist/indexer/graph/graph-types.js +4 -0
  274. package/dist/indexer/index-writer-lock.js +93 -49
  275. package/dist/indexer/index-written-assets.js +100 -53
  276. package/dist/indexer/indexer.js +746 -329
  277. package/dist/indexer/init.js +18 -25
  278. package/dist/indexer/installations.js +142 -0
  279. package/dist/indexer/passes/dir-staleness.js +18 -10
  280. package/dist/indexer/passes/memory-inference.js +25 -15
  281. package/dist/indexer/passes/metadata.js +412 -243
  282. package/dist/indexer/scan/doc-to-entry.js +160 -0
  283. package/dist/indexer/scan/drain-dir.js +134 -0
  284. package/dist/indexer/search/db-search.js +292 -108
  285. package/dist/indexer/search/fts-query.js +64 -0
  286. package/dist/indexer/search/ranking-contributors.js +145 -25
  287. package/dist/indexer/search/ranking-types.js +4 -0
  288. package/dist/indexer/search/ranking.js +28 -71
  289. package/dist/indexer/search/search-attribution.js +67 -0
  290. package/dist/indexer/search/search-fields.js +18 -3
  291. package/dist/indexer/search/search-hit-enrichers.js +30 -40
  292. package/dist/indexer/search/search-source.js +157 -111
  293. package/dist/indexer/search/semantic-status.js +4 -1
  294. package/dist/indexer/usage/usage-events.js +10 -30
  295. package/dist/indexer/walk/file-context.js +3 -45
  296. package/dist/indexer/walk/matchers.js +42 -34
  297. package/dist/indexer/walk/path-resolver.js +11 -5
  298. package/dist/indexer/walk/walker.js +42 -14
  299. package/dist/integrations/agent/builder-shared.js +7 -0
  300. package/dist/integrations/agent/builders.js +5 -56
  301. package/dist/integrations/agent/config.js +3 -143
  302. package/dist/integrations/agent/detect.js +17 -2
  303. package/dist/integrations/agent/engine-resolution.js +231 -0
  304. package/dist/integrations/agent/index.js +1 -2
  305. package/dist/integrations/agent/model-aliases.js +16 -2
  306. package/dist/integrations/agent/profiles.js +36 -62
  307. package/dist/integrations/agent/prompts.js +46 -18
  308. package/dist/integrations/agent/runner-dispatch.js +93 -4
  309. package/dist/integrations/agent/runner.js +76 -208
  310. package/dist/integrations/agent/spawn.js +88 -196
  311. package/dist/integrations/harnesses/aider/agent-builder.js +114 -0
  312. package/dist/integrations/harnesses/aider/index.js +48 -0
  313. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  314. package/dist/integrations/harnesses/amazonq/agent-builder.js +147 -0
  315. package/dist/integrations/harnesses/amazonq/index.js +45 -0
  316. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  317. package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
  318. package/dist/integrations/harnesses/claude/config-import.js +1 -3
  319. package/dist/integrations/harnesses/claude/index.js +24 -35
  320. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  321. package/dist/integrations/harnesses/claude/session-log.js +27 -75
  322. package/dist/integrations/harnesses/codex/agent-builder.js +138 -0
  323. package/dist/integrations/harnesses/codex/index.js +52 -0
  324. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  325. package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
  326. package/dist/integrations/harnesses/copilot/index.js +48 -0
  327. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  328. package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
  329. package/dist/integrations/harnesses/gemini/index.js +48 -0
  330. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  331. package/dist/integrations/harnesses/ids.js +24 -0
  332. package/dist/integrations/harnesses/index.js +54 -34
  333. package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
  334. package/dist/integrations/harnesses/opencode/config-import.js +1 -3
  335. package/dist/integrations/harnesses/opencode/index.js +14 -32
  336. package/dist/integrations/harnesses/opencode/session-log.js +67 -125
  337. package/dist/integrations/harnesses/opencode-sdk/harness.js +51 -0
  338. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
  339. package/dist/integrations/harnesses/openhands/agent-builder.js +128 -0
  340. package/dist/integrations/harnesses/openhands/index.js +48 -0
  341. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  342. package/dist/integrations/harnesses/pi/agent-builder.js +97 -0
  343. package/dist/integrations/harnesses/pi/index.js +45 -0
  344. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  345. package/dist/integrations/harnesses/shared.js +17 -0
  346. package/dist/integrations/harnesses/types.js +43 -32
  347. package/dist/integrations/lockfile.js +211 -24
  348. package/dist/integrations/session-logs/index.js +36 -39
  349. package/dist/integrations/session-logs/provider-base.js +113 -0
  350. package/dist/llm/client.js +182 -110
  351. package/dist/llm/embedders/deterministic.js +2 -2
  352. package/dist/llm/embedders/remote.js +21 -9
  353. package/dist/llm/feature-gate.js +17 -57
  354. package/dist/llm/graph-extract.js +12 -13
  355. package/dist/llm/index-passes.js +8 -42
  356. package/dist/llm/memory-infer.js +144 -1
  357. package/dist/llm/metadata-enhance.js +45 -30
  358. package/dist/llm/structured-call.js +16 -8
  359. package/dist/llm/usage-persist.js +30 -5
  360. package/dist/llm/usage-telemetry.js +59 -6
  361. package/dist/output/cli-hints.js +1 -2
  362. package/dist/output/command-registry.js +27 -0
  363. package/dist/output/context.js +22 -7
  364. package/dist/output/format-exempt.js +80 -0
  365. package/dist/output/generic-render.js +251 -0
  366. package/dist/output/html-render.js +11 -16
  367. package/dist/output/render-registry.js +57 -0
  368. package/dist/output/renderers.js +14 -279
  369. package/dist/output/shapes/curate.js +10 -1
  370. package/dist/output/shapes/events.js +12 -7
  371. package/dist/output/shapes/helpers.js +58 -84
  372. package/dist/output/shapes/passthrough.js +11 -39
  373. package/dist/output/shapes/proposal/producer.js +15 -7
  374. package/dist/output/shapes/registry.js +12 -6
  375. package/dist/output/shapes.js +0 -9
  376. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  377. package/dist/output/text/bundle-show.js +7 -0
  378. package/dist/output/text/command-format.js +562 -0
  379. package/dist/output/text/env.js +1 -3
  380. package/dist/output/text/events.js +8 -7
  381. package/dist/output/text/helpers.js +15 -1164
  382. package/dist/output/text/proposal/producer.js +4 -2
  383. package/dist/output/text/proposal-format.js +202 -0
  384. package/dist/output/text/registry-commands.js +1 -2
  385. package/dist/output/text/registry.js +12 -6
  386. package/dist/output/text/show-directives.js +117 -0
  387. package/dist/output/text/show-format.js +103 -0
  388. package/dist/output/text/sync.js +5 -0
  389. package/dist/output/text/workflow-format.js +332 -0
  390. package/dist/output/text/workflow.js +3 -2
  391. package/dist/output/text.js +10 -19
  392. package/dist/registry/factory.js +4 -6
  393. package/dist/registry/origin-resolve.js +16 -27
  394. package/dist/registry/providers/skills-sh.js +3 -3
  395. package/dist/registry/providers/static-index.js +15 -25
  396. package/dist/registry/resolve.js +43 -94
  397. package/dist/registry/semver.js +43 -0
  398. package/dist/runtime.js +81 -12
  399. package/dist/scripts/akm-migrate.js +35529 -0
  400. package/dist/setup/detect.js +5 -7
  401. package/dist/setup/detected-engines.js +136 -0
  402. package/dist/setup/engine-config.js +100 -0
  403. package/dist/setup/registry-stash-loader.js +3 -3
  404. package/dist/setup/semantic-assets.js +12 -9
  405. package/dist/setup/setup.js +444 -208
  406. package/dist/setup/steps/connection-shared.js +120 -0
  407. package/dist/setup/steps/connection.js +108 -305
  408. package/dist/setup/steps/platforms.js +13 -12
  409. package/dist/setup/steps/semantic.js +15 -3
  410. package/dist/setup/steps/sources.js +21 -15
  411. package/dist/setup/steps/stashdir.js +6 -4
  412. package/dist/setup/steps/tasks.js +236 -119
  413. package/dist/setup/steps.js +3 -2
  414. package/dist/sources/freshness.js +39 -0
  415. package/dist/sources/provider-factory.js +11 -17
  416. package/dist/sources/providers/filesystem.js +2 -3
  417. package/dist/sources/providers/git-install.js +278 -34
  418. package/dist/sources/providers/git-provider.js +54 -56
  419. package/dist/sources/providers/git-stash.js +420 -91
  420. package/dist/sources/providers/git.js +2 -2
  421. package/dist/sources/providers/npm.js +16 -19
  422. package/dist/sources/providers/provider-utils.js +47 -22
  423. package/dist/sources/providers/sync-from-ref.js +3 -9
  424. package/dist/sources/providers/website.js +2 -2
  425. package/dist/sources/resolve.js +11 -10
  426. package/dist/sources/snapshot-fetchers/types.js +4 -0
  427. package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
  428. package/dist/storage/database.js +60 -4
  429. package/dist/storage/engines/sqlite-migrations.js +156 -5
  430. package/dist/storage/locations.js +1 -2
  431. package/dist/storage/repositories/canaries-repository.js +1 -1
  432. package/dist/storage/repositories/events-repository.js +51 -11
  433. package/dist/storage/repositories/improve-runs-repository.js +6 -32
  434. package/dist/storage/repositories/index-connection.js +79 -0
  435. package/dist/storage/repositories/index-db.js +4 -3
  436. package/dist/storage/repositories/index-entries-repository.js +863 -0
  437. package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
  438. package/dist/storage/repositories/index-entry-types.js +4 -0
  439. package/dist/storage/repositories/index-fts-repository.js +167 -0
  440. package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
  441. package/dist/storage/repositories/index-meta-repository.js +49 -0
  442. package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
  443. package/dist/storage/repositories/index-sql.js +12 -0
  444. package/dist/storage/repositories/index-utility-repository.js +356 -0
  445. package/dist/storage/repositories/index-vec-repository.js +250 -0
  446. package/dist/storage/repositories/outcome-repository.js +119 -0
  447. package/dist/storage/repositories/proposals-repository.js +317 -75
  448. package/dist/storage/repositories/registry-cache.js +1 -1
  449. package/dist/storage/repositories/salience-repository.js +172 -0
  450. package/dist/storage/repositories/task-history-repository.js +110 -3
  451. package/dist/storage/repositories/workflow-runs-repository.js +240 -19
  452. package/dist/tasks/backends/cron.js +169 -46
  453. package/dist/tasks/backends/exec-utils.js +76 -3
  454. package/dist/tasks/backends/index.js +6 -9
  455. package/dist/tasks/backends/launchd.js +292 -55
  456. package/dist/tasks/backends/schtasks.js +557 -70
  457. package/dist/tasks/backends/types.js +4 -0
  458. package/dist/tasks/command-executable.js +93 -0
  459. package/dist/tasks/embedded.js +56 -38
  460. package/dist/tasks/parser.js +156 -64
  461. package/dist/tasks/resolve-akm-bin.js +144 -51
  462. package/dist/tasks/runner.js +377 -209
  463. package/dist/tasks/schedule.js +108 -19
  464. package/dist/tasks/scheduler-invocation.js +296 -0
  465. package/dist/tasks/schema.js +1 -1
  466. package/dist/tasks/task-id.js +35 -0
  467. package/dist/tasks/validator.js +30 -16
  468. package/dist/text-import-hook.mjs +1 -1
  469. package/dist/workflows/authoring/authoring.js +104 -43
  470. package/dist/workflows/authoring/scope-key.js +1 -1
  471. package/dist/workflows/cli.js +0 -16
  472. package/dist/workflows/concurrency-policy.js +15 -0
  473. package/dist/workflows/exec/brief.js +450 -0
  474. package/dist/workflows/exec/frozen-judge.js +47 -0
  475. package/dist/workflows/exec/native-executor.js +1038 -0
  476. package/dist/workflows/exec/param-secrets.js +115 -0
  477. package/dist/workflows/exec/report.js +1460 -0
  478. package/dist/workflows/exec/run-workflow.js +602 -0
  479. package/dist/workflows/exec/scheduler.js +71 -0
  480. package/dist/workflows/exec/step-work.js +1190 -0
  481. package/dist/workflows/exec/unit-writer.js +23 -0
  482. package/dist/workflows/exec/workflow-engine-gate.js +67 -0
  483. package/dist/workflows/exec/worktree.js +171 -0
  484. package/dist/workflows/ir/compile.js +246 -0
  485. package/dist/workflows/ir/freeze.js +233 -0
  486. package/dist/workflows/ir/params.js +54 -0
  487. package/dist/workflows/ir/plan-hash.js +68 -0
  488. package/dist/workflows/ir/schema.js +540 -0
  489. package/dist/workflows/parser.js +878 -304
  490. package/dist/workflows/program/expressions.js +181 -0
  491. package/dist/workflows/program/schema.js +51 -0
  492. package/dist/workflows/renderer.js +100 -45
  493. package/dist/workflows/resource-limits.js +22 -0
  494. package/dist/workflows/runtime/agent-identity.js +59 -14
  495. package/dist/workflows/runtime/checkin.js +1 -1
  496. package/dist/workflows/runtime/plan-classifier.js +131 -0
  497. package/dist/workflows/runtime/runs.js +376 -119
  498. package/dist/workflows/runtime/unit-checkin.js +45 -0
  499. package/dist/workflows/runtime/unit-phases.js +20 -0
  500. package/dist/workflows/runtime/workflow-asset-loader.js +241 -40
  501. package/dist/workflows/schema.js +1 -11
  502. package/dist/workflows/validate-summary.js +2 -3
  503. package/dist/workflows/validator.js +52 -30
  504. package/docs/README.md +42 -78
  505. package/docs/migration/README.md +8 -0
  506. package/docs/migration/release-notes/0.6.0.md +1 -1
  507. package/docs/migration/release-notes/0.7.0.md +9 -8
  508. package/docs/migration/release-notes/0.9.0.md +158 -14
  509. package/docs/migration/v0.7-to-v0.8.md +46 -47
  510. package/docs/migration/v0.8-to-v0.9.md +844 -0
  511. package/docs/reference/README.md +12 -0
  512. package/docs/reference/data-and-telemetry.md +333 -0
  513. package/package.json +21 -17
  514. package/schemas/akm-asset-envelope.json +93 -0
  515. package/schemas/akm-config.json +4636 -0
  516. package/schemas/akm-task.json +87 -0
  517. package/schemas/akm-workflow.json +373 -0
  518. package/dist/akm-migrate-storage +0 -38
  519. package/dist/assets/help/help-accept.md +0 -12
  520. package/dist/assets/help/help-improve.md +0 -84
  521. package/dist/assets/help/help-proposals.md +0 -17
  522. package/dist/assets/help/help-propose.md +0 -17
  523. package/dist/assets/help/help-reject.md +0 -11
  524. package/dist/assets/profiles/frequent.json +0 -13
  525. package/dist/assets/profiles/recombine-only.json +0 -21
  526. package/dist/assets/profiles/reflect-distill.json +0 -30
  527. package/dist/assets/profiles/synthesize.json +0 -15
  528. package/dist/assets/prompts/procedural-system.md +0 -44
  529. package/dist/assets/prompts/recombine-system.md +0 -40
  530. package/dist/assets/prompts/staleness-detect-system.md +0 -6
  531. package/dist/assets/tasks/core/backup.yml +0 -4
  532. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  533. package/dist/assets/templates/html/default.html +0 -78
  534. package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
  535. package/dist/assets/wiki/index-template.md +0 -12
  536. package/dist/assets/wiki/ingest-workflow-template.md +0 -83
  537. package/dist/assets/wiki/log-template.md +0 -8
  538. package/dist/assets/wiki/schema-template.md +0 -61
  539. package/dist/cli/config-migrate.js +0 -150
  540. package/dist/cli/config-validate.js +0 -39
  541. package/dist/commands/graph/graph-cli.js +0 -124
  542. package/dist/commands/graph/graph.js +0 -487
  543. package/dist/commands/improve/calibration.js +0 -161
  544. package/dist/commands/improve/dedup.js +0 -482
  545. package/dist/commands/improve/extract-watch.js +0 -140
  546. package/dist/commands/improve/hot-probation.js +0 -45
  547. package/dist/commands/improve/improve-auto-accept.js +0 -276
  548. package/dist/commands/improve/improve-profiles.js +0 -168
  549. package/dist/commands/improve/procedural.js +0 -398
  550. package/dist/commands/improve/recombine.js +0 -818
  551. package/dist/commands/improve/schema-similarity-gate.js +0 -168
  552. package/dist/commands/lint/agent-linter.js +0 -44
  553. package/dist/commands/lint/command-linter.js +0 -44
  554. package/dist/commands/lint/default-linter.js +0 -16
  555. package/dist/commands/lint/fact-linter.js +0 -39
  556. package/dist/commands/lint/knowledge-linter.js +0 -16
  557. package/dist/commands/lint/memory-linter.js +0 -61
  558. package/dist/commands/lint/registry.js +0 -41
  559. package/dist/commands/lint/skill-linter.js +0 -45
  560. package/dist/commands/lint/task-linter.js +0 -50
  561. package/dist/commands/lint/workflow-linter.js +0 -81
  562. package/dist/commands/proposal/legacy-import.js +0 -115
  563. package/dist/commands/sources/history.js +0 -196
  564. package/dist/commands/tasks/default-tasks.js +0 -186
  565. package/dist/commands/wiki-cli.js +0 -292
  566. package/dist/core/asset/asset-registry.js +0 -76
  567. package/dist/core/asset/asset-spec.js +0 -259
  568. package/dist/core/config/config-migration.js +0 -602
  569. package/dist/core/deep-merge.js +0 -38
  570. package/dist/core/eval/rank-metrics.js +0 -113
  571. package/dist/core/ripgrep/install.js +0 -163
  572. package/dist/core/ripgrep/resolve.js +0 -81
  573. package/dist/indexer/db/db.js +0 -1413
  574. package/dist/indexer/manifest.js +0 -170
  575. package/dist/indexer/passes/metadata-contributors.js +0 -31
  576. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
  577. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -49
  578. package/dist/llm/call-ai.js +0 -62
  579. package/dist/llm/memory-infer-impl.js +0 -138
  580. package/dist/output/shapes/distill.js +0 -14
  581. package/dist/output/shapes/history.js +0 -11
  582. package/dist/output/text/distill.js +0 -6
  583. package/dist/output/text/enable-disable.js +0 -8
  584. package/dist/output/text/history.js +0 -6
  585. package/dist/output/text/wiki.js +0 -16
  586. package/dist/registry/build-index.js +0 -386
  587. package/dist/scripts/migrate-storage.js +0 -19108
  588. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -9411
  589. package/dist/scripts/migrations/v16-to-v17.js +0 -141
  590. package/dist/setup/legacy-config.js +0 -106
  591. package/dist/storage/repositories/consolidation-repository.js +0 -38
  592. package/dist/storage/repositories/recombine-repository.js +0 -213
  593. package/dist/wiki/wiki-templates.js +0 -15
  594. package/dist/wiki/wiki.js +0 -1012
  595. package/dist/workflows/db.js +0 -215
  596. package/docs/data-and-telemetry.md +0 -226
  597. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
  598. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
@@ -2,7 +2,7 @@
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  /**
5
- * schtasks.exe backend for `akm tasks` (Windows default).
5
+ * schtasks.exe backend for `akm task` (Windows default).
6
6
  *
7
7
  * Each task is registered under the `\akm\` Task Scheduler folder so the
8
8
  * backend never touches user-managed tasks. The full task definition is
@@ -13,9 +13,8 @@
13
13
  * • `LogonType=InteractiveToken` means the task runs in the context of
14
14
  * the registering user only when they are logged in — there is no
15
15
  * stored password and the task will not fire at the lock screen.
16
- * • `<Principal>` deliberately omits `<UserId>`; per the Task Scheduler
17
- * 2.0 schema (`principalType.UserId` minOccurs=0) this is valid and
18
- * defaults to the registering user.
16
+ * • `<Principal>` records the current user SID so sync can detect identity
17
+ * drift instead of silently accepting a task registered to another user.
19
18
  * • `<DisallowStartIfOnBatteries>false</…>` and `<StopIfGoingOnBatteries>
20
19
  * false</…>` allow the task to run on battery — utility tasks would
21
20
  * otherwise be silently skipped on laptops.
@@ -29,6 +28,7 @@
29
28
  *
30
29
  * Tests inject a fake exec + filesystem.
31
30
  */
31
+ import { createHash } from "node:crypto";
32
32
  import fs from "node:fs";
33
33
  import os from "node:os";
34
34
  import path from "node:path";
@@ -37,33 +37,90 @@ import { ConfigError } from "../../core/errors.js";
37
37
  import { getTaskLogDir } from "../../core/paths.js";
38
38
  import { resolveAkmInvocation } from "../resolve-akm-bin.js";
39
39
  import { parseSchedule, translateToSchtasks } from "../schedule.js";
40
- import { escapeXml, nodeExec, nodeFs } from "./exec-utils.js";
40
+ import { buildScheduledTaskInvocation, parseScheduledTaskArgv, resolveScheduledTaskContext, schedulerContextDescriptor, schedulerContextPath, } from "../scheduler-invocation.js";
41
+ import { escapeXml, nodeExec, nodeFs, normalizeXmlForUtf16File, runOrThrow, } from "./exec-utils.js";
41
42
  export const DEFAULT_FOLDER_PREFIX = "\\akm\\";
43
+ const SIGNATURE_PREFIX = "akm:v1:";
42
44
  export function SCHTASKS_BACKEND(options = {}) {
43
45
  const exec = options.exec ?? defaultSchtasksExec();
44
46
  const fsLike = options.fs ?? defaultSchtasksFs();
45
47
  const akmArgv = options.akmArgv ?? resolveAkmInvocation().argv;
46
48
  const logDir = options.logDir ?? getTaskLogDir();
47
49
  const folder = options.folderPrefix ?? DEFAULT_FOLDER_PREFIX;
50
+ const scheduledContext = options.scheduledContext ?? resolveScheduledTaskContext();
51
+ const defaultContextPath = schedulerContextPath(schedulerContextDescriptor(scheduledContext, process.env.PATH ?? ""));
52
+ const userSid = options.userSid ?? resolveCurrentUserSid(exec);
48
53
  const taskName = (id) => `${folder}${id}`;
49
54
  return {
50
55
  name: "schtasks",
51
- install(task) {
56
+ install(task, opts) {
57
+ const xml = normalizeXmlForUtf16File(buildSchtasksXml(task, akmArgv, logDir, {
58
+ folderPrefix: folder,
59
+ contextPath: opts?.contextPath ?? defaultContextPath,
60
+ userSid,
61
+ binding: [...(opts?.binding ?? akmArgv)],
62
+ ...(opts?.target !== undefined ? { target: opts.target } : {}),
63
+ }));
64
+ const query = runOrThrow(exec, ["schtasks", "/Query", "/TN", taskName(task.id), "/XML"], {
65
+ isOk: (r) => r.status === 0 || isMissingTaskResult(r),
66
+ message: (r) => `schtasks /Query failed (exit ${r.status}): ${r.stderr || r.stdout || "no output"}.`,
67
+ });
68
+ let previous;
69
+ if (query.status === 0) {
70
+ const enabled = taskXmlEnabled(query.stdout);
71
+ if (enabled === undefined) {
72
+ throw new ConfigError(`schtasks /Query returned an unreadable definition for "${taskName(task.id)}"; refusing to replace it.`, "INVALID_CONFIG_FILE");
73
+ }
74
+ previous = { xml: normalizeXmlForUtf16File(query.stdout), enabled };
75
+ }
52
76
  fsLike.ensureDir(logDir);
53
- const xml = buildSchtasksXml(task, akmArgv, logDir, { folderPrefix: folder });
54
77
  const tmpFile = path.join(fsLike.tmpdir(), `akm-task-${task.id}-${Date.now()}.xml`);
55
78
  fsLike.writeFile(tmpFile, xml);
56
79
  try {
57
- // /F forces overwrite if a task with the same name exists.
58
- const r = exec.run(["schtasks", "/Create", "/TN", taskName(task.id), "/XML", tmpFile, "/F"]);
59
- if (r.status !== 0) {
60
- throw new ConfigError(`schtasks /Create failed (exit ${r.status}): ${r.stderr || r.stdout || "no output"}.`, "INVALID_CONFIG_FILE");
80
+ try {
81
+ // /F forces overwrite if a task with the same name exists.
82
+ runOrThrow(exec, ["schtasks", "/Create", "/TN", taskName(task.id), "/XML", tmpFile, "/F"], {
83
+ message: (r) => `schtasks /Create failed (exit ${r.status}): ${r.stderr || r.stdout || "no output"}.`,
84
+ });
85
+ if (!task.enabled) {
86
+ runOrThrow(exec, ["schtasks", "/Change", "/TN", taskName(task.id), "/DISABLE"], {
87
+ message: (r) => `schtasks /Change /DISABLE failed: ${r.stderr || r.stdout || "no output"}.`,
88
+ });
89
+ }
61
90
  }
62
- if (!task.enabled) {
63
- const dis = exec.run(["schtasks", "/Change", "/TN", taskName(task.id), "/DISABLE"]);
64
- if (dis.status !== 0) {
65
- throw new ConfigError(`schtasks /Change /DISABLE failed: ${dis.stderr || dis.stdout || "no output"}.`, "INVALID_CONFIG_FILE");
91
+ catch (err) {
92
+ const rollbackErrors = [];
93
+ if (previous === undefined) {
94
+ try {
95
+ const remove = exec.run(["schtasks", "/Delete", "/TN", taskName(task.id), "/F"]);
96
+ if (remove.status !== 0 && !isMissingTaskResult(remove)) {
97
+ rollbackErrors.push(new ConfigError(`schtasks /Delete during rollback failed: ${remove.stderr || remove.stdout || "no output"}.`, "INVALID_CONFIG_FILE"));
98
+ }
99
+ }
100
+ catch (rollbackError) {
101
+ rollbackErrors.push(rollbackError);
102
+ }
66
103
  }
104
+ else {
105
+ try {
106
+ fsLike.writeFile(tmpFile, previous.xml);
107
+ runOrThrow(exec, ["schtasks", "/Create", "/TN", taskName(task.id), "/XML", tmpFile, "/F"], {
108
+ message: (r) => `schtasks /Create during rollback failed: ${r.stderr || r.stdout || "no output"}.`,
109
+ });
110
+ const stateFlag = previous.enabled ? "/ENABLE" : "/DISABLE";
111
+ runOrThrow(exec, ["schtasks", "/Change", "/TN", taskName(task.id), stateFlag], {
112
+ message: (r) => `schtasks /Change ${stateFlag} during rollback failed: ${r.stderr || r.stdout || "no output"}.`,
113
+ });
114
+ }
115
+ catch (rollbackError) {
116
+ rollbackErrors.push(rollbackError);
117
+ }
118
+ }
119
+ if (rollbackErrors.length > 0) {
120
+ const message = err instanceof Error ? err.message : String(err);
121
+ throw new AggregateError([err, ...rollbackErrors], `${message}; rollback for Task Scheduler task "${task.id}" was incomplete.`);
122
+ }
123
+ throw err;
67
124
  }
68
125
  }
69
126
  finally {
@@ -71,22 +128,21 @@ export function SCHTASKS_BACKEND(options = {}) {
71
128
  }
72
129
  },
73
130
  uninstall(id) {
74
- const r = exec.run(["schtasks", "/Delete", "/TN", taskName(id), "/F"]);
75
- if (r.status !== 0 && !/cannot find/i.test(r.stderr ?? "")) {
76
- throw new ConfigError(`schtasks /Delete failed: ${r.stderr || r.stdout || "no output"}.`, "INVALID_CONFIG_FILE");
77
- }
131
+ runOrThrow(exec, ["schtasks", "/Delete", "/TN", taskName(id), "/F"], {
132
+ isOk: (r) => r.status === 0 || /cannot find/i.test(r.stderr ?? ""),
133
+ message: (r) => `schtasks /Delete failed: ${r.stderr || r.stdout || "no output"}.`,
134
+ });
78
135
  },
79
136
  setEnabled(id, enabled) {
80
137
  const flag = enabled ? "/ENABLE" : "/DISABLE";
81
- const r = exec.run(["schtasks", "/Change", "/TN", taskName(id), flag]);
82
- if (r.status !== 0) {
83
- throw new ConfigError(`schtasks /Change ${flag} failed: ${r.stderr || r.stdout || "no output"}.`, "INVALID_CONFIG_FILE");
84
- }
138
+ runOrThrow(exec, ["schtasks", "/Change", "/TN", taskName(id), flag], {
139
+ message: (r) => `schtasks /Change ${flag} failed: ${r.stderr || r.stdout || "no output"}.`,
140
+ });
85
141
  },
86
142
  list() {
87
- const r = exec.run(["schtasks", "/Query", "/FO", "CSV", "/NH"]);
88
- if (r.status !== 0)
89
- return [];
143
+ const r = runOrThrow(exec, ["schtasks", "/Query", "/FO", "CSV", "/NH"], {
144
+ message: (res) => `schtasks /Query failed (exit ${res.status}): ${res.stderr || res.stdout || "no output"}.`,
145
+ });
90
146
  const ids = [];
91
147
  for (const line of (r.stdout ?? "").split(/\r?\n/)) {
92
148
  const m = line.match(/^"([^"]+)",/);
@@ -97,58 +153,218 @@ export function SCHTASKS_BACKEND(options = {}) {
97
153
  ids.push(name.slice(folder.length));
98
154
  }
99
155
  }
100
- return ids.map((id) => ({ id }));
156
+ const refs = [];
157
+ for (const id of ids) {
158
+ const query = runOrThrow(exec, ["schtasks", "/Query", "/TN", taskName(id), "/XML"], {
159
+ message: (r) => `schtasks /Query /XML for "${taskName(id)}" failed (exit ${r.status}): ${r.stderr || r.stdout || "no output"}.`,
160
+ });
161
+ const signature = installedSignature(query.stdout);
162
+ const installed = extractSchtasksInvocation(query.stdout);
163
+ // An invocation that no longer parses (e.g. a pre-0.9 `tasks run`
164
+ // entry surviving the scheduler-ABI respelling) is an orphan of its
165
+ // marker id, not a hard failure: omit it so `akmTasksSync` treats the
166
+ // id as "not present" and reinstalls it from the current task file.
167
+ if (!installed)
168
+ continue;
169
+ refs.push({
170
+ id,
171
+ ...(signature !== undefined ? { signature } : {}),
172
+ ...(installed.target !== undefined ? { target: installed.target } : {}),
173
+ binding: installed.binding,
174
+ contextPath: installed.contextPath,
175
+ });
176
+ }
177
+ return refs;
178
+ },
179
+ listForRebind() {
180
+ const r = runOrThrow(exec, ["schtasks", "/Query", "/FO", "CSV", "/NH"], {
181
+ message: (res) => `schtasks /Query failed (exit ${res.status}): ${res.stderr || res.stdout || "no output"}.`,
182
+ });
183
+ const refs = [];
184
+ for (const line of (r.stdout ?? "").split(/\r?\n/)) {
185
+ const match = line.match(/^"([^"]+)",/);
186
+ const name = match?.[1];
187
+ if (!name?.startsWith(folder))
188
+ continue;
189
+ const id = name.slice(folder.length);
190
+ const query = runOrThrow(exec, ["schtasks", "/Query", "/TN", taskName(id), "/XML"], {
191
+ message: (result) => `schtasks /Query /XML for "${taskName(id)}" failed (exit ${result.status}): ${result.stderr || result.stdout || "no output"}.`,
192
+ });
193
+ const signature = installedSignature(query.stdout);
194
+ const installed = extractSchtasksInvocation(query.stdout);
195
+ refs.push({
196
+ id,
197
+ ...(signature !== undefined ? { signature } : {}),
198
+ ...(installed?.target !== undefined ? { target: installed.target } : {}),
199
+ });
200
+ }
201
+ return refs;
202
+ },
203
+ expectedSignature(task, opts) {
204
+ const signature = taskXmlSignature(buildSchtasksXml(task, akmArgv, logDir, {
205
+ folderPrefix: folder,
206
+ contextPath: opts?.contextPath ?? defaultContextPath,
207
+ userSid,
208
+ binding: [...(opts?.binding ?? akmArgv)],
209
+ ...(opts?.target !== undefined ? { target: opts.target } : {}),
210
+ }));
211
+ if (signature === undefined)
212
+ throw new Error("Failed to fingerprint generated Task Scheduler XML.");
213
+ return signature;
101
214
  },
102
215
  };
103
216
  }
104
- export function buildSchtasksXml(task, akmArgv, logDir, options = {}) {
217
+ /**
218
+ * Recover the bundle name embedded as a `--bundle <bundle>` pair in the
219
+ * PowerShell `<Arguments>` of an installed Task Scheduler definition. Returns
220
+ * undefined for the primary/default form.
221
+ */
222
+ export function extractSchtasksTarget(xml) {
223
+ return extractSchtasksInvocation(xml)?.target;
224
+ }
225
+ export function extractSchtasksInvocation(xml) {
226
+ const argsElement = xml.match(/<(?:[\w.-]+:)?Arguments>([\s\S]*?)<\/(?:[\w.-]+:)?Arguments>/i);
227
+ if (!argsElement)
228
+ return undefined;
229
+ const commandLine = decodeXml(argsElement[1]);
230
+ const invocationStart = findPowerShellInvocationOperator(commandLine);
231
+ if (invocationStart === undefined)
232
+ return undefined;
233
+ return parseScheduledTaskArgv(parsePowerShellSingleQuotedArgs(commandLine, invocationStart + 1));
234
+ }
235
+ function findPowerShellInvocationOperator(script) {
236
+ let inSingleQuote = false;
237
+ for (let index = 0; index < script.length; index += 1) {
238
+ const char = script[index];
239
+ if (char === "'") {
240
+ if (inSingleQuote && script[index + 1] === "'") {
241
+ index += 1;
242
+ }
243
+ else {
244
+ inSingleQuote = !inSingleQuote;
245
+ }
246
+ continue;
247
+ }
248
+ if (!inSingleQuote && char === "&" && /\s/.test(script[index + 1] ?? ""))
249
+ return index;
250
+ }
251
+ return undefined;
252
+ }
253
+ function parsePowerShellSingleQuotedArgs(script, start) {
254
+ const argv = [];
255
+ let index = start;
256
+ while (index < script.length) {
257
+ while (/\s/.test(script[index] ?? ""))
258
+ index += 1;
259
+ if (script[index] === ";" || script[index] === '"' || index >= script.length)
260
+ break;
261
+ if (script[index] !== "'")
262
+ return [];
263
+ index += 1;
264
+ let value = "";
265
+ let closed = false;
266
+ while (index < script.length) {
267
+ const char = script[index];
268
+ if (char !== "'") {
269
+ value += char;
270
+ index += 1;
271
+ continue;
272
+ }
273
+ if (script[index + 1] === "'") {
274
+ value += "'";
275
+ index += 2;
276
+ continue;
277
+ }
278
+ index += 1;
279
+ closed = true;
280
+ break;
281
+ }
282
+ if (!closed)
283
+ return [];
284
+ argv.push(value);
285
+ }
286
+ return argv;
287
+ }
288
+ export function buildSchtasksXml(task, akmArgv, logDir, options) {
105
289
  const folder = options.folderPrefix ?? DEFAULT_FOLDER_PREFIX;
106
290
  const now = options.now ? options.now() : new Date();
107
- const startBoundary = formatStartBoundary(now);
108
- const spec = parseSchedule(task.schedule, "schtasks");
109
- const trigger = translateToSchtasks(spec);
110
- const command = akmArgv[0];
111
- const args = [...akmArgv.slice(1), "tasks", "run", task.id].map((a) => quoteArg(a)).join(" ");
112
- const triggerXml = renderSchtasksTrigger(trigger, startBoundary);
113
- const logPath = path.join(logDir, `${task.id}.log`);
291
+ const definition = buildSchtasksDefinition(task, options.binding ?? akmArgv, logDir, folder, options.contextPath, options.userSid, options.target);
292
+ const triggerXml = renderSchtasksTrigger(definition.trigger, now);
114
293
  return schtasksTemplate
115
294
  .replaceAll("{{TASK_ID}}", escapeXml(task.id))
116
295
  .replaceAll("{{FOLDER}}", escapeXml(folder))
296
+ .replace("{{SIGNATURE}}", definition.signature)
117
297
  .replace("{{TRIGGER_XML}}", triggerXml)
298
+ .replace('<Principal id="Author">', `<Principal id="Author">\n <UserId>${escapeXml(options.userSid)}</UserId>`)
118
299
  .replace("{{ENABLED}}", task.enabled ? "true" : "false")
119
- .replace("{{COMMAND}}", escapeXml(command))
120
- .replace("{{ARGS}}", escapeXml(args))
121
- .replace("{{LOG_PATH}}", escapeXml(logPath));
300
+ .replace("{{COMMAND}}", escapeXml(definition.command))
301
+ .replace("{{ARGS}}", escapeXml(definition.args))
302
+ .replace("{{LOG_PATH}}", escapeXml(definition.logPath));
303
+ }
304
+ function buildSchtasksDefinition(task, akmArgv, logDir, folder, contextPath, userSid, target) {
305
+ const spec = parseSchedule(task.schedule, "schtasks");
306
+ const trigger = translateToSchtasks(spec);
307
+ const invocation = buildScheduledTaskInvocation(akmArgv, task.id, contextPath, target);
308
+ const invoke = `& ${invocation.argv.map((arg) => quotePowerShell(arg)).join(" ")}`;
309
+ const script = `${invoke}; exit $LASTEXITCODE`;
310
+ const command = "powershell.exe";
311
+ const args = ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", script].map(quoteArg).join(" ");
312
+ const logPath = path.join(logDir, `${task.id}.log`);
313
+ // The boundary changes on reinstall, and enabled state can change via /Change.
314
+ // Keep both outside the stored definition fingerprint so no-op sync stays stable.
315
+ const fingerprint = createHash("sha256")
316
+ .update(JSON.stringify({ folder, id: task.id, trigger, command, args, logPath, userSid }))
317
+ .digest("hex");
318
+ return { trigger, command, args, logPath, signature: `${SIGNATURE_PREFIX}${fingerprint}` };
122
319
  }
123
- function renderSchtasksTrigger(trigger, startBoundary) {
320
+ function renderSchtasksTrigger(trigger, now) {
321
+ return expandNativeTriggers(trigger)
322
+ .map((native) => renderNativeTrigger(native, formatStartBoundary(nextStartBoundary(native, now))))
323
+ .join("\n");
324
+ }
325
+ function expandNativeTriggers(trigger) {
124
326
  switch (trigger.kind) {
125
327
  case "minute":
126
- return ` <TimeTrigger>
127
- <Repetition>
128
- <Interval>PT${trigger.everyMinutes}M</Interval>
129
- </Repetition>
130
- <StartBoundary>${startBoundary}</StartBoundary>
131
- <Enabled>true</Enabled>
132
- </TimeTrigger>`;
328
+ return [{ kind: "daily", atHour: 0, atMinute: 0, repeatEveryMinutes: trigger.everyMinutes }];
329
+ case "minuteValues":
330
+ return trigger.minutes.map((atMinute) => ({ kind: "daily", atHour: 0, atMinute, repeatEveryMinutes: 60 }));
133
331
  case "hour":
134
- return ` <TimeTrigger>
135
- <Repetition>
136
- <Interval>PT${trigger.everyHours}H</Interval>
137
- </Repetition>
138
- <StartBoundary>${startBoundary}</StartBoundary>
139
- <Enabled>true</Enabled>
140
- </TimeTrigger>`;
332
+ return [
333
+ {
334
+ kind: "daily",
335
+ atHour: 0,
336
+ atMinute: trigger.atMinute,
337
+ repeatEveryMinutes: trigger.everyHours * 60,
338
+ },
339
+ ];
340
+ case "hourValues":
341
+ return trigger.hours.map((atHour) => ({ kind: "daily", atHour, atMinute: trigger.atMinute }));
141
342
  case "daily":
142
- return ` <CalendarTrigger>
143
- <StartBoundary>${pad(startBoundary, trigger.atHour, trigger.atMinute)}</StartBoundary>
343
+ return [{ kind: "daily", atHour: trigger.atHour, atMinute: trigger.atMinute }];
344
+ case "weekly":
345
+ return [trigger];
346
+ }
347
+ }
348
+ function renderNativeTrigger(trigger, startBoundary) {
349
+ if (trigger.kind === "daily") {
350
+ const repetition = trigger.repeatEveryMinutes === undefined
351
+ ? ""
352
+ : ` <Repetition>
353
+ <Interval>${formatRepetitionInterval(trigger.repeatEveryMinutes)}</Interval>
354
+ <Duration>${formatRepetitionDuration(trigger.repeatEveryMinutes)}</Duration>
355
+ <StopAtDurationEnd>false</StopAtDurationEnd>
356
+ </Repetition>
357
+ `;
358
+ return ` <CalendarTrigger>
359
+ ${repetition} <StartBoundary>${startBoundary}</StartBoundary>
144
360
  <Enabled>true</Enabled>
145
361
  <ScheduleByDay><DaysInterval>1</DaysInterval></ScheduleByDay>
146
362
  </CalendarTrigger>`;
147
- case "weekly": {
148
- const dayMap = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
149
- const days = trigger.daysOfWeek.map((d) => ` <${dayMap[d]} />`).join("\n");
150
- return ` <CalendarTrigger>
151
- <StartBoundary>${pad(startBoundary, trigger.atHour, trigger.atMinute)}</StartBoundary>
363
+ }
364
+ const dayMap = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
365
+ const days = trigger.daysOfWeek.map((d) => ` <${dayMap[d]} />`).join("\n");
366
+ return ` <CalendarTrigger>
367
+ <StartBoundary>${startBoundary}</StartBoundary>
152
368
  <Enabled>true</Enabled>
153
369
  <ScheduleByWeek>
154
370
  <DaysOfWeek>
@@ -157,16 +373,42 @@ ${days}
157
373
  <WeeksInterval>1</WeeksInterval>
158
374
  </ScheduleByWeek>
159
375
  </CalendarTrigger>`;
160
- }
161
- }
162
376
  }
163
- function pad(base, hour, minute) {
164
- // Rewrite the time component of an ISO-8601 boundary while preserving
165
- // the date so daily/weekly triggers fire at the configured wall-clock
166
- // time rather than the install instant.
167
- const hh = String(hour).padStart(2, "0");
168
- const mm = String(minute).padStart(2, "0");
169
- return base.replace(/T\d\d:\d\d:\d\d$/, `T${hh}:${mm}:00`);
377
+ function formatRepetitionInterval(minutes) {
378
+ return formatMinuteDuration(minutes);
379
+ }
380
+ function formatRepetitionDuration(intervalMinutes) {
381
+ return formatMinuteDuration(24 * 60 - intervalMinutes);
382
+ }
383
+ function formatMinuteDuration(minutes) {
384
+ const hours = Math.floor(minutes / 60);
385
+ const remainingMinutes = minutes % 60;
386
+ const hourPart = hours > 0 ? `${hours}H` : "";
387
+ const minutePart = remainingMinutes > 0 ? `${remainingMinutes}M` : "";
388
+ return `PT${hourPart}${minutePart}`;
389
+ }
390
+ function nextStartBoundary(trigger, now) {
391
+ const boundary = new Date(now.getTime());
392
+ switch (trigger.kind) {
393
+ case "daily":
394
+ boundary.setHours(trigger.atHour, trigger.atMinute, 0, 0);
395
+ if (trigger.repeatEveryMinutes !== undefined) {
396
+ while (boundary.getTime() <= now.getTime()) {
397
+ boundary.setMinutes(boundary.getMinutes() + trigger.repeatEveryMinutes);
398
+ }
399
+ }
400
+ else if (boundary.getTime() <= now.getTime()) {
401
+ boundary.setDate(boundary.getDate() + 1);
402
+ }
403
+ return boundary;
404
+ case "weekly":
405
+ boundary.setHours(trigger.atHour, trigger.atMinute, 0, 0);
406
+ while (!trigger.daysOfWeek.includes(boundary.getDay()) || boundary.getTime() <= now.getTime()) {
407
+ boundary.setDate(boundary.getDate() + 1);
408
+ boundary.setHours(trigger.atHour, trigger.atMinute, 0, 0);
409
+ }
410
+ return boundary;
411
+ }
170
412
  }
171
413
  function formatStartBoundary(d) {
172
414
  // Local-time ISO-8601 (no zone suffix) — Task Scheduler interprets a
@@ -183,7 +425,249 @@ function formatStartBoundary(d) {
183
425
  function quoteArg(s) {
184
426
  if (/^[A-Za-z0-9_\-./@:%=+,\\]+$/.test(s))
185
427
  return s;
186
- return `"${s.replace(/"/g, '\\"')}"`;
428
+ // CommandLineToArgvW requires backslashes before quotes (including the
429
+ // closing quote) to be doubled so they survive as literal backslashes.
430
+ return `"${s.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/g, "$1$1")}"`;
431
+ }
432
+ function quotePowerShell(value) {
433
+ return `'${value.replaceAll("'", "''")}'`;
434
+ }
435
+ function installedSignature(xml) {
436
+ return taskXmlSignature(xml);
437
+ }
438
+ function isMissingTaskResult(result) {
439
+ return /cannot find|not found/i.test(`${result.stderr ?? ""}\n${result.stdout ?? ""}`);
440
+ }
441
+ function taskXmlSignature(xml) {
442
+ try {
443
+ const document = parseXml(xml);
444
+ const task = findChild(document, "Task");
445
+ const triggers = findChild(task, "Triggers");
446
+ const principals = findChild(task, "Principals");
447
+ const settings = findChild(task, "Settings");
448
+ const actions = findChild(task, "Actions");
449
+ if (!triggers || !principals || !settings || !actions)
450
+ return undefined;
451
+ normalizeTriggerBoundaries(triggers);
452
+ normalizeNativeDefaults(triggers, principals, settings);
453
+ const enabledElement = findChild(settings, "Enabled");
454
+ const enabledValue = enabledElement ? elementText(enabledElement).toLowerCase() : undefined;
455
+ const enabled = enabledValue === undefined || enabledValue === "true" || enabledValue === "1";
456
+ // Enabled is represented explicitly in the signature suffix. Removing it
457
+ // here also treats an omitted Enabled element as its schema default, true.
458
+ settings.children = settings.children.filter((child) => typeof child === "string" || child.name.toLowerCase() !== "enabled");
459
+ const canonical = [triggers, principals, settings, actions].map(canonicalXmlElement).join("\n");
460
+ const fingerprint = createHash("sha256").update(canonical).digest("hex");
461
+ return signatureWithEnabled(`${SIGNATURE_PREFIX}${fingerprint}`, enabled);
462
+ }
463
+ catch {
464
+ return undefined;
465
+ }
466
+ }
467
+ function taskXmlEnabled(xml) {
468
+ try {
469
+ const document = parseXml(xml);
470
+ const settings = findChild(findChild(document, "Task"), "Settings");
471
+ if (!settings)
472
+ return undefined;
473
+ const enabledElement = findChild(settings, "Enabled");
474
+ if (!enabledElement)
475
+ return true;
476
+ const enabled = elementText(enabledElement).toLowerCase();
477
+ if (enabled === "true" || enabled === "1")
478
+ return true;
479
+ if (enabled === "false" || enabled === "0")
480
+ return false;
481
+ return undefined;
482
+ }
483
+ catch {
484
+ return undefined;
485
+ }
486
+ }
487
+ function parseXml(xml) {
488
+ const document = { name: "#document", attributes: {}, children: [] };
489
+ const stack = [document];
490
+ const tokens = xml.match(/<!--[\s\S]*?-->|<\?[\s\S]*?\?>|<[^>]+>|[^<]+/g) ?? [];
491
+ for (const token of tokens) {
492
+ if (token.startsWith("<!--") || token.startsWith("<?"))
493
+ continue;
494
+ if (token.startsWith("</")) {
495
+ if (stack.length === 1)
496
+ throw new Error("Unexpected XML closing tag.");
497
+ const closingName = localXmlName(token.slice(2, -1).trim());
498
+ const current = stack.pop();
499
+ if (current?.name !== closingName)
500
+ throw new Error("Mismatched XML closing tag.");
501
+ continue;
502
+ }
503
+ if (token.startsWith("<")) {
504
+ const selfClosing = /\/\s*>$/.test(token);
505
+ const match = token.match(/^<\s*([^\s/>]+)([\s\S]*?)\/?\s*>$/);
506
+ if (!match)
507
+ throw new Error("Invalid XML opening tag.");
508
+ const element = {
509
+ name: localXmlName(match[1]),
510
+ attributes: parseXmlAttributes(match[2]),
511
+ children: [],
512
+ };
513
+ stack[stack.length - 1].children.push(element);
514
+ if (!selfClosing)
515
+ stack.push(element);
516
+ continue;
517
+ }
518
+ const text = decodeXml(token.trim());
519
+ if (text.length > 0)
520
+ stack[stack.length - 1].children.push(text);
521
+ }
522
+ if (stack.length !== 1)
523
+ throw new Error("Unclosed XML tag.");
524
+ return document;
525
+ }
526
+ function parseXmlAttributes(raw) {
527
+ const attributes = {};
528
+ const pattern = /([^\s=]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
529
+ for (const match of raw.matchAll(pattern)) {
530
+ const name = localXmlName(match[1]);
531
+ if (match[1] === "xmlns" || match[1].startsWith("xmlns:"))
532
+ continue;
533
+ attributes[name] = decodeXml(match[2] ?? match[3] ?? "");
534
+ }
535
+ return attributes;
536
+ }
537
+ function localXmlName(name) {
538
+ return name.slice(name.lastIndexOf(":") + 1);
539
+ }
540
+ function decodeXml(value) {
541
+ return value.replace(/&(?:#(\d+)|#x([\da-f]+)|amp|lt|gt|quot|apos);/gi, (entity, decimal, hex) => {
542
+ if (decimal !== undefined)
543
+ return String.fromCodePoint(Number(decimal));
544
+ if (hex !== undefined)
545
+ return String.fromCodePoint(Number.parseInt(hex, 16));
546
+ switch (entity.toLowerCase()) {
547
+ case "&amp;":
548
+ return "&";
549
+ case "&lt;":
550
+ return "<";
551
+ case "&gt;":
552
+ return ">";
553
+ case "&quot;":
554
+ return '"';
555
+ case "&apos;":
556
+ return "'";
557
+ default:
558
+ return entity;
559
+ }
560
+ });
561
+ }
562
+ function findChild(parent, name) {
563
+ return parent?.children.find((child) => typeof child !== "string" && child.name.toLowerCase() === name.toLowerCase());
564
+ }
565
+ function elementText(element) {
566
+ return element.children.filter((child) => typeof child === "string").join("");
567
+ }
568
+ function normalizeTriggerBoundaries(triggers) {
569
+ for (const trigger of triggers.children) {
570
+ if (typeof trigger === "string")
571
+ continue;
572
+ const boundary = findChild(trigger, "StartBoundary");
573
+ if (!boundary)
574
+ continue;
575
+ const time = elementText(boundary).match(/T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?/);
576
+ if (!time)
577
+ continue;
578
+ const secondsSinceMidnight = Number(time[1]) * 3600 + Number(time[2]) * 60 + Number(time[3]);
579
+ const interval = findChild(findChild(trigger, "Repetition"), "Interval");
580
+ const intervalSeconds = interval ? parseIsoDurationSeconds(elementText(interval)) : undefined;
581
+ boundary.children = [
582
+ intervalSeconds === undefined
583
+ ? `dynamic-date;time=${time[1]}:${time[2]}:${time[3]}`
584
+ : `dynamic-cycle;phase-seconds=${secondsSinceMidnight % intervalSeconds}`,
585
+ ];
586
+ }
587
+ }
588
+ const MATERIALIZED_SETTING_DEFAULTS = {
589
+ allowstartondemand: "true",
590
+ allowhardterminate: "true",
591
+ startwhenavailable: "false",
592
+ runonlyifnetworkavailable: "false",
593
+ hidden: "false",
594
+ runonlyifidle: "false",
595
+ waketorun: "false",
596
+ executiontimelimit: "PT72H",
597
+ priority: "7",
598
+ compatibility: "Vista",
599
+ useunifiedschedulingengine: "true",
600
+ disallowstartonremoteappsession: "false",
601
+ volatile: "false",
602
+ };
603
+ const MATERIALIZED_IDLE_DEFAULTS = {
604
+ duration: "PT10M",
605
+ waittimeout: "PT1H",
606
+ stoponidleend: "true",
607
+ restartonidle: "false",
608
+ };
609
+ function normalizeNativeDefaults(triggers, principals, settings) {
610
+ for (const principal of elementChildren(principals)) {
611
+ principal.children = principal.children.filter((child) => {
612
+ if (typeof child === "string")
613
+ return true;
614
+ return child.name.toLowerCase() !== "runlevel" || elementText(child).toLowerCase() !== "leastprivilege";
615
+ });
616
+ }
617
+ settings.children = settings.children.filter((child) => {
618
+ if (typeof child === "string")
619
+ return true;
620
+ const name = child.name.toLowerCase();
621
+ if (name === "idlesettings")
622
+ return !containsOnlyDefaults(child, MATERIALIZED_IDLE_DEFAULTS);
623
+ const defaultValue = MATERIALIZED_SETTING_DEFAULTS[name];
624
+ return defaultValue === undefined || elementText(child) !== defaultValue;
625
+ });
626
+ for (const trigger of elementChildren(triggers)) {
627
+ trigger.children = trigger.children.filter((child) => {
628
+ if (typeof child === "string")
629
+ return true;
630
+ const name = child.name.toLowerCase();
631
+ if (name === "enabled")
632
+ return elementText(child).toLowerCase() !== "true";
633
+ return name !== "executiontimelimit" || elementText(child) !== "PT72H";
634
+ });
635
+ }
636
+ }
637
+ function resolveCurrentUserSid(exec) {
638
+ const result = runOrThrow(exec, ["whoami", "/user", "/fo", "csv", "/nh"], {
639
+ message: (r) => `whoami /user failed (exit ${r.status}): ${r.stderr || r.stdout || "no output"}.`,
640
+ });
641
+ const match = `${result.stdout ?? ""}\n${result.stderr ?? ""}`.match(/\bS-\d+(?:-\d+){2,}\b/i);
642
+ if (!match) {
643
+ throw new ConfigError("whoami /user returned no Windows user SID.", "INVALID_CONFIG_FILE");
644
+ }
645
+ return `S${match[0].slice(1)}`;
646
+ }
647
+ function elementChildren(element) {
648
+ return element.children.filter((child) => typeof child !== "string");
649
+ }
650
+ function containsOnlyDefaults(element, defaults) {
651
+ const children = elementChildren(element);
652
+ return (children.length > 0 &&
653
+ children.length === element.children.length &&
654
+ children.every((child) => defaults[child.name.toLowerCase()] === elementText(child)));
655
+ }
656
+ function parseIsoDurationSeconds(value) {
657
+ const match = value.match(/^PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$/i);
658
+ if (!match)
659
+ return undefined;
660
+ const seconds = Number(match[1] ?? 0) * 3600 + Number(match[2] ?? 0) * 60 + Number(match[3] ?? 0);
661
+ return seconds > 0 ? seconds : undefined;
662
+ }
663
+ function canonicalXmlElement(element) {
664
+ const attributes = Object.entries(element.attributes).sort(([a], [b]) => a.localeCompare(b));
665
+ const children = element.children.map((child) => typeof child === "string" ? ["text", child] : ["element", canonicalXmlElement(child)]);
666
+ children.sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
667
+ return JSON.stringify([element.name, attributes, children]);
668
+ }
669
+ function signatureWithEnabled(signature, enabled) {
670
+ return `${signature}|enabled=${enabled ? "true" : "false"}`;
187
671
  }
188
672
  function defaultSchtasksExec() {
189
673
  return nodeExec();
@@ -191,6 +675,9 @@ function defaultSchtasksExec() {
191
675
  function defaultSchtasksFs() {
192
676
  return {
193
677
  ...nodeFs(),
678
+ writeFile(file, content) {
679
+ fs.writeFileSync(file, `\uFEFF${content}`, { encoding: "utf16le" });
680
+ },
194
681
  removeFile(file) {
195
682
  try {
196
683
  fs.rmSync(file, { force: true });