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.
- package/CHANGELOG.md +1283 -22
- package/README.md +62 -37
- package/SECURITY.md +46 -31
- package/dist/akm +162 -38
- package/dist/akm-migrate +44 -0
- package/dist/assets/backends/schtasks-template.xml +2 -1
- package/dist/assets/hints/cli-hints-full.md +268 -118
- package/dist/assets/hints/cli-hints-short.md +87 -24
- package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
- package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
- package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
- package/dist/assets/improve-strategies/frequent.json +15 -0
- package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
- package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
- package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
- package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
- package/dist/assets/improve-strategies/reflect-distill.json +30 -0
- package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +5 -5
- package/dist/assets/prompts/extract-session.md +2 -6
- package/dist/assets/prompts/memory-infer-user.md +2 -3
- package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
- package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
- package/dist/assets/prompts/reflect-output-repair.md +3 -0
- package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
- package/dist/assets/stash-skeleton/README.md +38 -10
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
- package/dist/assets/tasks/core/extract.yml +3 -2
- package/dist/assets/tasks/core/improve.yml +2 -1
- package/dist/assets/tasks/core/index-refresh.yml +1 -0
- package/dist/assets/tasks/core/sync.yml +1 -0
- package/dist/assets/tasks/core/version-check.yml +2 -1
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
- package/dist/assets/templates/html/health.html +5 -4
- package/dist/assets/workflows/workflow-template.md +31 -15
- package/dist/cli/invocation.js +279 -0
- package/dist/cli/parse-args.js +5 -90
- package/dist/cli/retired-commands.js +78 -0
- package/dist/cli/shared.js +158 -48
- package/dist/cli-node.mjs +2 -1
- package/dist/cli.js +747 -293
- package/dist/commands/agent/agent-dispatch.js +19 -18
- package/dist/commands/agent/agent-support.js +0 -24
- package/dist/commands/agent/contribute-cli.js +43 -97
- package/dist/commands/completions.js +80 -23
- package/dist/commands/config-cli.js +44 -281
- package/dist/commands/env/env-binding.js +99 -0
- package/dist/commands/env/env-cli.js +84 -224
- package/dist/commands/env/env.js +12 -163
- package/dist/commands/env/marker-path.js +6 -0
- package/dist/commands/env/secret-cli.js +45 -61
- package/dist/commands/env/secret.js +32 -62
- package/dist/commands/feedback-cli.js +179 -85
- package/dist/commands/health/accept-rate.js +58 -0
- package/dist/commands/health/advisories.js +7 -8
- package/dist/commands/health/checks.js +279 -94
- package/dist/commands/health/html-report.js +197 -578
- package/dist/commands/health/improve-metrics.js +277 -246
- package/dist/commands/health/llm-usage.js +19 -19
- package/dist/commands/health/md-report.js +16 -7
- package/dist/commands/health/metrics.js +67 -32
- package/dist/commands/health/renderers.js +47 -0
- package/dist/commands/health/report-view-model.js +508 -0
- package/dist/commands/health/stash-exposure.js +1 -1
- package/dist/commands/health/surfaces.js +16 -56
- package/dist/commands/health/task-runs.js +3 -67
- package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
- package/dist/commands/health/types-improve.js +29 -0
- package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
- package/dist/commands/health/types-result.js +7 -0
- package/dist/commands/health/types-runs.js +4 -0
- package/dist/commands/health/types-session-log.js +4 -0
- package/dist/commands/health/types-windows.js +4 -0
- package/dist/commands/health/types.js +26 -21
- package/dist/commands/health/windows.js +2 -3
- package/dist/commands/health.js +296 -167
- package/dist/commands/improve/anti-collapse.js +5 -5
- package/dist/commands/improve/autonomy-gate.js +68 -0
- package/dist/commands/improve/collapse-detector.js +65 -52
- package/dist/commands/improve/consolidate/chunking.js +9 -7
- package/dist/commands/improve/consolidate/eligibility.js +1 -23
- package/dist/commands/improve/consolidate/merge.js +4 -0
- package/dist/commands/improve/consolidate.js +454 -1354
- package/dist/commands/improve/content-hash.js +39 -0
- package/dist/commands/improve/distill/content-repair.js +4 -10
- package/dist/commands/improve/distill/promote-memory.js +89 -64
- package/dist/commands/improve/distill/quality-gate.js +118 -42
- package/dist/commands/improve/distill-guards.js +1 -1
- package/dist/commands/improve/distill-promotion-policy.js +33 -888
- package/dist/commands/improve/distill.js +607 -363
- package/dist/commands/improve/eligibility.js +165 -79
- package/dist/commands/improve/extract-cli.js +35 -126
- package/dist/commands/improve/extract-prompt.js +6 -35
- package/dist/commands/improve/extract.js +640 -391
- package/dist/commands/improve/feedback-valence.js +2 -12
- package/dist/commands/improve/improve-cli.js +134 -135
- package/dist/commands/improve/improve-result-file.js +30 -50
- package/dist/commands/improve/improve-run-types.js +4 -0
- package/dist/commands/improve/improve-strategies.js +135 -0
- package/dist/commands/improve/improve.js +904 -701
- package/dist/commands/improve/locks.js +64 -111
- package/dist/commands/improve/loop-stages.js +1110 -923
- package/dist/commands/improve/memory/derived-ref.js +124 -0
- package/dist/commands/improve/memory/memory-belief.js +79 -7
- package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
- package/dist/commands/improve/memory/memory-improve.js +25 -37
- package/dist/commands/improve/outcome-loop.js +25 -88
- package/dist/commands/improve/preparation.js +1034 -813
- package/dist/commands/improve/proactive-maintenance.js +34 -9
- package/dist/commands/improve/proposal-envelope.js +31 -0
- package/dist/commands/improve/reflect.js +983 -794
- package/dist/commands/improve/run-context.js +119 -0
- package/dist/commands/improve/salience.js +24 -127
- package/dist/commands/improve/session-asset.js +7 -3
- package/dist/commands/improve/shared.js +14 -34
- package/dist/commands/improve/source-identity.js +28 -0
- package/dist/commands/improve/triage.js +20 -17
- package/dist/commands/lint/base-linter.js +340 -313
- package/dist/commands/lint/env-key-rules.js +31 -47
- package/dist/commands/lint/index.js +185 -30
- package/dist/commands/{events.js → log.js} +28 -38
- package/dist/commands/migrate-cli.js +54 -0
- package/dist/commands/migration-tool.js +55 -0
- package/dist/commands/observability-cli.js +70 -208
- package/dist/commands/proposal/diff-format.js +50 -0
- package/dist/commands/proposal/drain-policies.js +0 -6
- package/dist/commands/proposal/drain.js +91 -40
- package/dist/commands/proposal/proposal-cli.js +134 -132
- package/dist/commands/proposal/proposal-types.js +56 -0
- package/dist/commands/proposal/proposal.js +83 -65
- package/dist/commands/proposal/propose-cli.js +88 -0
- package/dist/commands/proposal/propose.js +105 -88
- package/dist/commands/proposal/repository.js +1303 -278
- package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
- package/dist/commands/proposal/validators/proposal-validators.js +61 -12
- package/dist/commands/proposal/validators/proposals.js +6 -8
- package/dist/commands/read/curate.js +78 -73
- package/dist/commands/read/knowledge.js +510 -13
- package/dist/commands/read/registry-search.js +2 -2
- package/dist/commands/read/remember-cli.js +84 -15
- package/dist/commands/read/search-cli.js +203 -96
- package/dist/commands/read/search.js +126 -94
- package/dist/commands/read/show.js +226 -250
- package/dist/commands/registry-cli.js +34 -60
- package/dist/commands/remember.js +18 -57
- package/dist/commands/sources/add-cli.js +104 -49
- package/dist/commands/sources/bundle-cli.js +166 -0
- package/dist/commands/sources/bundle-config-ops.js +63 -0
- package/dist/commands/sources/info.js +27 -15
- package/dist/commands/sources/init.js +30 -40
- package/dist/commands/sources/installed-stashes.js +469 -172
- package/dist/commands/sources/migration-help.js +7 -4
- package/dist/commands/sources/schema-repair.js +10 -9
- package/dist/commands/sources/self-update.js +182 -121
- package/dist/commands/sources/source-add.js +169 -178
- package/dist/commands/sources/source-clone.js +144 -41
- package/dist/commands/sources/source-manage.js +94 -59
- package/dist/commands/sources/sources-cli.js +64 -205
- package/dist/commands/sources/stash-cli.js +91 -54
- package/dist/commands/sources/stash-skeleton.js +1 -1
- package/dist/commands/tasks/tasks-cli.js +106 -104
- package/dist/commands/tasks/tasks.js +445 -262
- package/dist/commands/workflow-cli.js +232 -121
- package/dist/core/action-contributors.js +1 -1
- package/dist/core/activation-policy.js +49 -0
- package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
- package/dist/core/adapter/adapters/akm-adapter.js +528 -0
- package/dist/core/adapter/adapters/akm-lint.js +392 -0
- package/dist/core/adapter/adapters/akm-metadata.js +387 -0
- package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
- package/dist/core/adapter/adapters/claude-adapter.js +61 -0
- package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
- package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
- package/dist/core/adapter/adapters/index.js +80 -0
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
- package/dist/core/adapter/adapters/okf-adapter.js +391 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
- package/dist/core/adapter/adapters/shared.js +286 -0
- package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
- package/dist/core/adapter/bundle-adapter.js +4 -0
- package/dist/core/adapter/detect-adapter.js +17 -0
- package/dist/core/adapter/recognize-match.js +44 -0
- package/dist/core/adapter/registry.js +56 -0
- package/dist/core/adapter/types.js +4 -0
- package/dist/core/asset/akm-markdown.js +30 -0
- package/dist/core/asset/asset-placement.js +243 -0
- package/dist/core/asset/asset-ref.js +110 -79
- package/dist/core/asset/asset-serialize.js +20 -0
- package/dist/core/asset/frontmatter.js +28 -12
- package/dist/core/asset/markdown.js +40 -51
- package/dist/core/asset/resolve-ref.js +274 -0
- package/dist/core/asset/stash-meta.js +2 -2
- package/dist/core/bundle-id.js +51 -0
- package/dist/core/common.js +281 -86
- package/dist/core/config/config-io.js +42 -128
- package/dist/core/config/config-schema.js +233 -834
- package/dist/core/config/config-sources.js +162 -39
- package/dist/core/config/config-types.js +16 -11
- package/dist/core/config/config-version.js +29 -0
- package/dist/core/config/config-walker.js +126 -37
- package/dist/core/config/config.js +154 -331
- package/dist/core/config/deep-merge.js +41 -0
- package/dist/core/config/engine-semantics.js +28 -0
- package/dist/core/config/experimental.js +21 -0
- package/dist/core/config/schema/embedding.js +38 -0
- package/dist/core/config/schema/engines.js +116 -0
- package/dist/core/config/schema/experimental.js +47 -0
- package/dist/core/config/schema/feedback.js +31 -0
- package/dist/core/config/schema/improve-processes.js +389 -0
- package/dist/core/config/schema/improve.js +94 -0
- package/dist/core/config/schema/index-config.js +176 -0
- package/dist/core/config/schema/output.js +18 -0
- package/dist/core/config/schema/primitives.js +94 -0
- package/dist/core/config/schema/search.js +30 -0
- package/dist/core/config/schema/setup.js +18 -0
- package/dist/core/config/schema/sources-bundles.js +169 -0
- package/dist/core/config/schema/workflow.js +29 -0
- package/dist/core/env-secret-ref.js +155 -20
- package/dist/core/errors.js +17 -15
- package/dist/core/events-types.js +4 -0
- package/dist/core/events.js +46 -128
- package/dist/core/extra-params.js +62 -0
- package/dist/core/file-change.js +17 -0
- package/dist/core/file-lock.js +202 -57
- package/dist/core/fs-txn.js +392 -0
- package/dist/core/git-message.js +59 -0
- package/dist/core/improve-result.js +167 -0
- package/dist/core/json-schema.js +142 -0
- package/dist/core/lesson-lint.js +1 -17
- package/dist/core/logs-db.js +1 -1
- package/dist/core/maintenance-barrier.js +135 -0
- package/dist/core/migration-operation.js +44 -0
- package/dist/core/mutation-target.js +78 -0
- package/dist/core/paths.js +22 -25
- package/dist/core/platform.js +10 -0
- package/dist/core/recognition-util.js +128 -0
- package/dist/core/redaction.js +392 -0
- package/dist/core/standards/resolve-standards-context.js +36 -65
- package/dist/core/standards/resolve-stash-standards.js +2 -2
- package/dist/core/standards/resolve-type-conventions.js +5 -5
- package/dist/core/state/migrations.js +242 -11
- package/dist/core/state-db.js +98 -10
- package/dist/core/structured.js +1 -1
- package/dist/core/subprocess.js +303 -0
- package/dist/core/text-truncation.js +9 -5
- package/dist/core/time.js +20 -0
- package/dist/core/type-presentation.js +130 -0
- package/dist/core/warn.js +0 -3
- package/dist/core/write-source.js +834 -118
- package/dist/indexer/bundle-identity-guard.js +92 -0
- package/dist/indexer/db/graph-db.js +1 -25
- package/dist/indexer/db/llm-cache.js +1 -1
- package/dist/indexer/ensure-index.js +30 -9
- package/dist/indexer/graph/graph-boost.js +9 -30
- package/dist/indexer/graph/graph-extraction.js +41 -27
- package/dist/indexer/graph/graph-types.js +4 -0
- package/dist/indexer/index-writer-lock.js +93 -49
- package/dist/indexer/index-written-assets.js +100 -53
- package/dist/indexer/indexer.js +746 -329
- package/dist/indexer/init.js +18 -25
- package/dist/indexer/installations.js +142 -0
- package/dist/indexer/passes/dir-staleness.js +18 -10
- package/dist/indexer/passes/memory-inference.js +25 -15
- package/dist/indexer/passes/metadata.js +412 -243
- package/dist/indexer/scan/doc-to-entry.js +160 -0
- package/dist/indexer/scan/drain-dir.js +134 -0
- package/dist/indexer/search/db-search.js +292 -108
- package/dist/indexer/search/fts-query.js +64 -0
- package/dist/indexer/search/ranking-contributors.js +145 -25
- package/dist/indexer/search/ranking-types.js +4 -0
- package/dist/indexer/search/ranking.js +28 -71
- package/dist/indexer/search/search-attribution.js +67 -0
- package/dist/indexer/search/search-fields.js +18 -3
- package/dist/indexer/search/search-hit-enrichers.js +30 -40
- package/dist/indexer/search/search-source.js +157 -111
- package/dist/indexer/search/semantic-status.js +4 -1
- package/dist/indexer/usage/usage-events.js +10 -30
- package/dist/indexer/walk/file-context.js +3 -45
- package/dist/indexer/walk/matchers.js +42 -34
- package/dist/indexer/walk/path-resolver.js +11 -5
- package/dist/indexer/walk/walker.js +42 -14
- package/dist/integrations/agent/builder-shared.js +7 -0
- package/dist/integrations/agent/builders.js +5 -56
- package/dist/integrations/agent/config.js +3 -143
- package/dist/integrations/agent/detect.js +17 -2
- package/dist/integrations/agent/engine-resolution.js +231 -0
- package/dist/integrations/agent/index.js +1 -2
- package/dist/integrations/agent/model-aliases.js +16 -2
- package/dist/integrations/agent/profiles.js +36 -62
- package/dist/integrations/agent/prompts.js +46 -18
- package/dist/integrations/agent/runner-dispatch.js +93 -4
- package/dist/integrations/agent/runner.js +76 -208
- package/dist/integrations/agent/spawn.js +88 -196
- package/dist/integrations/harnesses/aider/agent-builder.js +114 -0
- package/dist/integrations/harnesses/aider/index.js +48 -0
- package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
- package/dist/integrations/harnesses/amazonq/agent-builder.js +147 -0
- package/dist/integrations/harnesses/amazonq/index.js +45 -0
- package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
- package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
- package/dist/integrations/harnesses/claude/config-import.js +1 -3
- package/dist/integrations/harnesses/claude/index.js +24 -35
- package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
- package/dist/integrations/harnesses/claude/session-log.js +27 -75
- package/dist/integrations/harnesses/codex/agent-builder.js +138 -0
- package/dist/integrations/harnesses/codex/index.js +52 -0
- package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
- package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
- package/dist/integrations/harnesses/copilot/index.js +48 -0
- package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
- package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
- package/dist/integrations/harnesses/gemini/index.js +48 -0
- package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
- package/dist/integrations/harnesses/ids.js +24 -0
- package/dist/integrations/harnesses/index.js +54 -34
- package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
- package/dist/integrations/harnesses/opencode/config-import.js +1 -3
- package/dist/integrations/harnesses/opencode/index.js +14 -32
- package/dist/integrations/harnesses/opencode/session-log.js +67 -125
- package/dist/integrations/harnesses/opencode-sdk/harness.js +51 -0
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
- package/dist/integrations/harnesses/openhands/agent-builder.js +128 -0
- package/dist/integrations/harnesses/openhands/index.js +48 -0
- package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
- package/dist/integrations/harnesses/pi/agent-builder.js +97 -0
- package/dist/integrations/harnesses/pi/index.js +45 -0
- package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
- package/dist/integrations/harnesses/shared.js +17 -0
- package/dist/integrations/harnesses/types.js +43 -32
- package/dist/integrations/lockfile.js +211 -24
- package/dist/integrations/session-logs/index.js +36 -39
- package/dist/integrations/session-logs/provider-base.js +113 -0
- package/dist/llm/client.js +182 -110
- package/dist/llm/embedders/deterministic.js +2 -2
- package/dist/llm/embedders/remote.js +21 -9
- package/dist/llm/feature-gate.js +17 -57
- package/dist/llm/graph-extract.js +12 -13
- package/dist/llm/index-passes.js +8 -42
- package/dist/llm/memory-infer.js +144 -1
- package/dist/llm/metadata-enhance.js +45 -30
- package/dist/llm/structured-call.js +16 -8
- package/dist/llm/usage-persist.js +30 -5
- package/dist/llm/usage-telemetry.js +59 -6
- package/dist/output/cli-hints.js +1 -2
- package/dist/output/command-registry.js +27 -0
- package/dist/output/context.js +22 -7
- package/dist/output/format-exempt.js +80 -0
- package/dist/output/generic-render.js +251 -0
- package/dist/output/html-render.js +11 -16
- package/dist/output/render-registry.js +57 -0
- package/dist/output/renderers.js +14 -279
- package/dist/output/shapes/curate.js +10 -1
- package/dist/output/shapes/events.js +12 -7
- package/dist/output/shapes/helpers.js +58 -84
- package/dist/output/shapes/passthrough.js +11 -39
- package/dist/output/shapes/proposal/producer.js +15 -7
- package/dist/output/shapes/registry.js +12 -6
- package/dist/output/shapes.js +0 -9
- package/dist/output/text/{init.js → bundle-create.js} +3 -1
- package/dist/output/text/bundle-show.js +7 -0
- package/dist/output/text/command-format.js +562 -0
- package/dist/output/text/env.js +1 -3
- package/dist/output/text/events.js +8 -7
- package/dist/output/text/helpers.js +15 -1164
- package/dist/output/text/proposal/producer.js +4 -2
- package/dist/output/text/proposal-format.js +202 -0
- package/dist/output/text/registry-commands.js +1 -2
- package/dist/output/text/registry.js +12 -6
- package/dist/output/text/show-directives.js +117 -0
- package/dist/output/text/show-format.js +103 -0
- package/dist/output/text/sync.js +5 -0
- package/dist/output/text/workflow-format.js +332 -0
- package/dist/output/text/workflow.js +3 -2
- package/dist/output/text.js +10 -19
- package/dist/registry/factory.js +4 -6
- package/dist/registry/origin-resolve.js +16 -27
- package/dist/registry/providers/skills-sh.js +3 -3
- package/dist/registry/providers/static-index.js +15 -25
- package/dist/registry/resolve.js +43 -94
- package/dist/registry/semver.js +43 -0
- package/dist/runtime.js +81 -12
- package/dist/scripts/akm-migrate.js +35529 -0
- package/dist/setup/detect.js +5 -7
- package/dist/setup/detected-engines.js +136 -0
- package/dist/setup/engine-config.js +100 -0
- package/dist/setup/registry-stash-loader.js +3 -3
- package/dist/setup/semantic-assets.js +12 -9
- package/dist/setup/setup.js +444 -208
- package/dist/setup/steps/connection-shared.js +120 -0
- package/dist/setup/steps/connection.js +108 -305
- package/dist/setup/steps/platforms.js +13 -12
- package/dist/setup/steps/semantic.js +15 -3
- package/dist/setup/steps/sources.js +21 -15
- package/dist/setup/steps/stashdir.js +6 -4
- package/dist/setup/steps/tasks.js +236 -119
- package/dist/setup/steps.js +3 -2
- package/dist/sources/freshness.js +39 -0
- package/dist/sources/provider-factory.js +11 -17
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +278 -34
- package/dist/sources/providers/git-provider.js +54 -56
- package/dist/sources/providers/git-stash.js +420 -91
- package/dist/sources/providers/git.js +2 -2
- package/dist/sources/providers/npm.js +16 -19
- package/dist/sources/providers/provider-utils.js +47 -22
- package/dist/sources/providers/sync-from-ref.js +3 -9
- package/dist/sources/providers/website.js +2 -2
- package/dist/sources/resolve.js +11 -10
- package/dist/sources/snapshot-fetchers/types.js +4 -0
- package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
- package/dist/storage/database.js +60 -4
- package/dist/storage/engines/sqlite-migrations.js +156 -5
- package/dist/storage/locations.js +1 -2
- package/dist/storage/repositories/canaries-repository.js +1 -1
- package/dist/storage/repositories/events-repository.js +51 -11
- package/dist/storage/repositories/improve-runs-repository.js +6 -32
- package/dist/storage/repositories/index-connection.js +79 -0
- package/dist/storage/repositories/index-db.js +4 -3
- package/dist/storage/repositories/index-entries-repository.js +863 -0
- package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
- package/dist/storage/repositories/index-entry-types.js +4 -0
- package/dist/storage/repositories/index-fts-repository.js +167 -0
- package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
- package/dist/storage/repositories/index-meta-repository.js +49 -0
- package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
- package/dist/storage/repositories/index-sql.js +12 -0
- package/dist/storage/repositories/index-utility-repository.js +356 -0
- package/dist/storage/repositories/index-vec-repository.js +250 -0
- package/dist/storage/repositories/outcome-repository.js +119 -0
- package/dist/storage/repositories/proposals-repository.js +317 -75
- package/dist/storage/repositories/registry-cache.js +1 -1
- package/dist/storage/repositories/salience-repository.js +172 -0
- package/dist/storage/repositories/task-history-repository.js +110 -3
- package/dist/storage/repositories/workflow-runs-repository.js +240 -19
- package/dist/tasks/backends/cron.js +169 -46
- package/dist/tasks/backends/exec-utils.js +76 -3
- package/dist/tasks/backends/index.js +6 -9
- package/dist/tasks/backends/launchd.js +292 -55
- package/dist/tasks/backends/schtasks.js +557 -70
- package/dist/tasks/backends/types.js +4 -0
- package/dist/tasks/command-executable.js +93 -0
- package/dist/tasks/embedded.js +56 -38
- package/dist/tasks/parser.js +156 -64
- package/dist/tasks/resolve-akm-bin.js +144 -51
- package/dist/tasks/runner.js +377 -209
- package/dist/tasks/schedule.js +108 -19
- package/dist/tasks/scheduler-invocation.js +296 -0
- package/dist/tasks/schema.js +1 -1
- package/dist/tasks/task-id.js +35 -0
- package/dist/tasks/validator.js +30 -16
- package/dist/text-import-hook.mjs +1 -1
- package/dist/workflows/authoring/authoring.js +104 -43
- package/dist/workflows/authoring/scope-key.js +1 -1
- package/dist/workflows/cli.js +0 -16
- package/dist/workflows/concurrency-policy.js +15 -0
- package/dist/workflows/exec/brief.js +450 -0
- package/dist/workflows/exec/frozen-judge.js +47 -0
- package/dist/workflows/exec/native-executor.js +1038 -0
- package/dist/workflows/exec/param-secrets.js +115 -0
- package/dist/workflows/exec/report.js +1460 -0
- package/dist/workflows/exec/run-workflow.js +602 -0
- package/dist/workflows/exec/scheduler.js +71 -0
- package/dist/workflows/exec/step-work.js +1190 -0
- package/dist/workflows/exec/unit-writer.js +23 -0
- package/dist/workflows/exec/workflow-engine-gate.js +67 -0
- package/dist/workflows/exec/worktree.js +171 -0
- package/dist/workflows/ir/compile.js +246 -0
- package/dist/workflows/ir/freeze.js +233 -0
- package/dist/workflows/ir/params.js +54 -0
- package/dist/workflows/ir/plan-hash.js +68 -0
- package/dist/workflows/ir/schema.js +540 -0
- package/dist/workflows/parser.js +878 -304
- package/dist/workflows/program/expressions.js +181 -0
- package/dist/workflows/program/schema.js +51 -0
- package/dist/workflows/renderer.js +100 -45
- package/dist/workflows/resource-limits.js +22 -0
- package/dist/workflows/runtime/agent-identity.js +59 -14
- package/dist/workflows/runtime/checkin.js +1 -1
- package/dist/workflows/runtime/plan-classifier.js +131 -0
- package/dist/workflows/runtime/runs.js +376 -119
- package/dist/workflows/runtime/unit-checkin.js +45 -0
- package/dist/workflows/runtime/unit-phases.js +20 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +241 -40
- package/dist/workflows/schema.js +1 -11
- package/dist/workflows/validate-summary.js +2 -3
- package/dist/workflows/validator.js +52 -30
- package/docs/README.md +42 -78
- package/docs/migration/README.md +8 -0
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +9 -8
- package/docs/migration/release-notes/0.9.0.md +158 -14
- package/docs/migration/v0.7-to-v0.8.md +46 -47
- package/docs/migration/v0.8-to-v0.9.md +844 -0
- package/docs/reference/README.md +12 -0
- package/docs/reference/data-and-telemetry.md +333 -0
- package/package.json +21 -17
- package/schemas/akm-asset-envelope.json +93 -0
- package/schemas/akm-config.json +4636 -0
- package/schemas/akm-task.json +87 -0
- package/schemas/akm-workflow.json +373 -0
- package/dist/akm-migrate-storage +0 -38
- package/dist/assets/help/help-accept.md +0 -12
- package/dist/assets/help/help-improve.md +0 -84
- package/dist/assets/help/help-proposals.md +0 -17
- package/dist/assets/help/help-propose.md +0 -17
- package/dist/assets/help/help-reject.md +0 -11
- package/dist/assets/profiles/frequent.json +0 -13
- package/dist/assets/profiles/recombine-only.json +0 -21
- package/dist/assets/profiles/reflect-distill.json +0 -30
- package/dist/assets/profiles/synthesize.json +0 -15
- package/dist/assets/prompts/procedural-system.md +0 -44
- package/dist/assets/prompts/recombine-system.md +0 -40
- package/dist/assets/prompts/staleness-detect-system.md +0 -6
- package/dist/assets/tasks/core/backup.yml +0 -4
- package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
- package/dist/assets/templates/html/default.html +0 -78
- package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
- package/dist/assets/wiki/index-template.md +0 -12
- package/dist/assets/wiki/ingest-workflow-template.md +0 -83
- package/dist/assets/wiki/log-template.md +0 -8
- package/dist/assets/wiki/schema-template.md +0 -61
- package/dist/cli/config-migrate.js +0 -150
- package/dist/cli/config-validate.js +0 -39
- package/dist/commands/graph/graph-cli.js +0 -124
- package/dist/commands/graph/graph.js +0 -487
- package/dist/commands/improve/calibration.js +0 -161
- package/dist/commands/improve/dedup.js +0 -482
- package/dist/commands/improve/extract-watch.js +0 -140
- package/dist/commands/improve/hot-probation.js +0 -45
- package/dist/commands/improve/improve-auto-accept.js +0 -276
- package/dist/commands/improve/improve-profiles.js +0 -168
- package/dist/commands/improve/procedural.js +0 -398
- package/dist/commands/improve/recombine.js +0 -818
- package/dist/commands/improve/schema-similarity-gate.js +0 -168
- package/dist/commands/lint/agent-linter.js +0 -44
- package/dist/commands/lint/command-linter.js +0 -44
- package/dist/commands/lint/default-linter.js +0 -16
- package/dist/commands/lint/fact-linter.js +0 -39
- package/dist/commands/lint/knowledge-linter.js +0 -16
- package/dist/commands/lint/memory-linter.js +0 -61
- package/dist/commands/lint/registry.js +0 -41
- package/dist/commands/lint/skill-linter.js +0 -45
- package/dist/commands/lint/task-linter.js +0 -50
- package/dist/commands/lint/workflow-linter.js +0 -81
- package/dist/commands/proposal/legacy-import.js +0 -115
- package/dist/commands/sources/history.js +0 -196
- package/dist/commands/tasks/default-tasks.js +0 -186
- package/dist/commands/wiki-cli.js +0 -292
- package/dist/core/asset/asset-registry.js +0 -76
- package/dist/core/asset/asset-spec.js +0 -259
- package/dist/core/config/config-migration.js +0 -602
- package/dist/core/deep-merge.js +0 -38
- package/dist/core/eval/rank-metrics.js +0 -113
- package/dist/core/ripgrep/install.js +0 -163
- package/dist/core/ripgrep/resolve.js +0 -81
- package/dist/indexer/db/db.js +0 -1413
- package/dist/indexer/manifest.js +0 -170
- package/dist/indexer/passes/metadata-contributors.js +0 -31
- package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
- package/dist/integrations/harnesses/opencode-sdk/index.js +0 -49
- package/dist/llm/call-ai.js +0 -62
- package/dist/llm/memory-infer-impl.js +0 -138
- package/dist/output/shapes/distill.js +0 -14
- package/dist/output/shapes/history.js +0 -11
- package/dist/output/text/distill.js +0 -6
- package/dist/output/text/enable-disable.js +0 -8
- package/dist/output/text/history.js +0 -6
- package/dist/output/text/wiki.js +0 -16
- package/dist/registry/build-index.js +0 -386
- package/dist/scripts/migrate-storage.js +0 -19108
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -9411
- package/dist/scripts/migrations/v16-to-v17.js +0 -141
- package/dist/setup/legacy-config.js +0 -106
- package/dist/storage/repositories/consolidation-repository.js +0 -38
- package/dist/storage/repositories/recombine-repository.js +0 -213
- package/dist/wiki/wiki-templates.js +0 -15
- package/dist/wiki/wiki.js +0 -1012
- package/dist/workflows/db.js +0 -215
- package/docs/data-and-telemetry.md +0 -226
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
- /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
|
|
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>`
|
|
17
|
-
*
|
|
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 {
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
63
|
-
const
|
|
64
|
-
if (
|
|
65
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
108
|
-
const
|
|
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,
|
|
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
|
|
127
|
-
|
|
128
|
-
|
|
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
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
|
143
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
<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
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
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 "&":
|
|
548
|
+
return "&";
|
|
549
|
+
case "<":
|
|
550
|
+
return "<";
|
|
551
|
+
case ">":
|
|
552
|
+
return ">";
|
|
553
|
+
case """:
|
|
554
|
+
return '"';
|
|
555
|
+
case "'":
|
|
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 });
|