akm-cli 0.9.17-alpha.2 → 0.9.17-alpha.4
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 +756 -0
- package/dist/akm +94 -196
- package/dist/cli/shared.js +6 -2
- package/dist/cli.js +22 -9
- package/dist/commands/agent/agent-dispatch.js +1 -1
- package/dist/commands/command/command-execution.js +24 -62
- package/dist/commands/feedback-cli.js +0 -1
- package/dist/commands/health/accept-rate.js +2 -2
- package/dist/commands/health/checks.js +30 -75
- package/dist/commands/health/config-skew.js +38 -0
- package/dist/commands/health/egress.js +54 -0
- package/dist/commands/health/html-report.js +0 -38
- package/dist/commands/health/improve-metrics.js +123 -562
- package/dist/commands/health/plugin-staleness.js +53 -3
- package/dist/commands/health/renderers.js +12 -4
- package/dist/commands/health/report-view-model.js +11 -106
- package/dist/commands/health/types-improve.js +4 -19
- package/dist/commands/health/windows.js +64 -73
- package/dist/commands/health.js +122 -143
- package/dist/commands/improve/consolidate/chunking.js +25 -100
- package/dist/commands/improve/consolidate/sanitize.js +54 -149
- package/dist/commands/improve/consolidate.js +538 -1075
- package/dist/commands/improve/content-hash.js +16 -24
- package/dist/commands/improve/distill/content-repair.js +18 -100
- package/dist/commands/improve/distill-guards.js +20 -81
- package/dist/commands/improve/distill-promotion-policy.js +23 -243
- package/dist/commands/improve/distill.js +608 -1075
- package/dist/commands/improve/eligibility.js +126 -400
- package/dist/commands/improve/execution.js +3 -5
- package/dist/commands/improve/extract.js +487 -1046
- package/dist/commands/improve/feedback-valence.js +0 -25
- package/dist/commands/improve/improve-cli.js +29 -166
- package/dist/commands/improve/improve-result-file.js +10 -66
- package/dist/commands/improve/improve-strategies.js +12 -7
- package/dist/commands/improve/improve-usage-report.js +18 -64
- package/dist/commands/improve/improve.js +443 -1063
- package/dist/commands/improve/ledger.js +114 -0
- package/dist/commands/improve/locks.js +2 -8
- package/dist/commands/improve/loop-stages.js +459 -1172
- package/dist/commands/improve/memory/derived-ref.js +12 -77
- package/dist/commands/improve/memory/memory-belief.js +14 -118
- package/dist/commands/improve/memory/memory-improve.js +4 -3
- package/dist/commands/improve/outcome-loop.js +28 -156
- package/dist/commands/improve/planner.js +5 -10
- package/dist/commands/improve/preparation.js +851 -2339
- package/dist/commands/improve/proactive-maintenance.js +34 -101
- package/dist/commands/improve/reflect-noise.js +104 -280
- package/dist/commands/improve/reflect.js +621 -1367
- package/dist/commands/improve/salience.js +46 -232
- package/dist/commands/improve/session-asset.js +19 -100
- package/dist/commands/improve/stage.js +323 -0
- package/dist/commands/proposal/drain.js +251 -644
- package/dist/commands/proposal/proposal-cli.js +3 -18
- package/dist/commands/proposal/proposal-types.js +20 -41
- package/dist/commands/proposal/proposal.js +1 -2
- package/dist/commands/proposal/propose.js +134 -160
- package/dist/commands/proposal/repository.js +502 -1487
- package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
- package/dist/commands/proposal/validators/proposal-validators.js +1 -1
- package/dist/commands/proposal/validators/proposals.js +13 -89
- package/dist/commands/read/curate.js +63 -413
- package/dist/commands/read/search-cli.js +16 -33
- package/dist/commands/read/search.js +17 -23
- package/dist/commands/read/show.js +2 -13
- package/dist/commands/sources/bundle-cli.js +25 -2
- package/dist/commands/sources/bundle-config-ops.js +7 -0
- package/dist/commands/sources/dangerous-env-audit.js +1 -2
- package/dist/commands/sources/info.js +2 -11
- package/dist/commands/sources/installed-stashes.js +197 -746
- package/dist/commands/sources/schema-repair.js +98 -129
- package/dist/commands/sources/source-add.js +62 -12
- package/dist/commands/sources/stash-cli.js +1 -1
- package/dist/commands/tasks/explain.js +10 -13
- package/dist/commands/tasks/tasks-cli.js +9 -8
- package/dist/commands/tasks/tasks.js +326 -930
- package/dist/commands/tasks/validate.js +42 -21
- package/dist/commands/workflow/plan.js +22 -29
- package/dist/commands/workflow-cli.js +4 -4
- package/dist/core/adapter/adapters/akm-adapter.js +0 -1
- package/dist/core/adapter/adapters/akm-lint.js +2 -3
- package/dist/core/adapter/adapters/akm-metadata.js +11 -12
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
- package/dist/core/adapter/execution-source.js +17 -29
- package/dist/core/asset/resolve-ref.js +1 -1
- package/dist/core/bundle-id.js +42 -5
- package/dist/core/bundle-rename.js +291 -0
- package/dist/core/config/config-io.js +1 -2
- package/dist/core/config/config-schema.js +1 -33
- package/dist/core/config/config-walker.js +1 -1
- package/dist/core/config/config.js +163 -68
- package/dist/core/config/legacy-source-shape-shim.js +38 -9
- package/dist/core/config/schema/embedding.js +20 -5
- package/dist/core/config/schema/engines.js +5 -0
- package/dist/core/config/schema/execution.js +1 -1
- package/dist/core/config/schema/experimental.js +1 -1
- package/dist/core/config/schema/improve-processes.js +21 -95
- package/dist/core/config/schema/improve.js +4 -42
- package/dist/core/config/schema/scheduler.js +12 -12
- package/dist/core/config/schema/search.js +6 -22
- package/dist/core/env-secret-ref.js +0 -1
- package/dist/core/errors.js +8 -9
- package/dist/core/file-lock.js +76 -173
- package/dist/core/logs-db.js +2 -2
- package/dist/core/paths.js +0 -27
- package/dist/core/redaction.js +109 -2
- package/dist/core/run-lock.js +2 -5
- package/dist/core/spawn-env.js +1 -1
- package/dist/core/state/migrations.js +108 -61
- package/dist/core/state-db-scope.js +2 -4
- package/dist/core/state-db.js +126 -692
- package/dist/core/type-presentation.js +1 -9
- package/dist/core/write-source.js +293 -1012
- package/dist/execution/input-contract.js +1 -1
- package/dist/execution/resolved-request.js +135 -689
- package/dist/execution/source.js +63 -257
- package/dist/execution/target-ref.js +1 -1
- package/dist/indexer/bundle-identity-guard.js +2 -2
- package/dist/indexer/db/graph-db.js +106 -46
- package/dist/indexer/ensure-index.js +44 -85
- package/dist/indexer/graph/graph-extraction.js +340 -562
- package/dist/indexer/graph/graph-related.js +130 -0
- package/dist/indexer/index-rebuild-lock.js +3 -11
- package/dist/indexer/index-writer-lock.js +8 -17
- package/dist/indexer/index-written-assets.js +139 -151
- package/dist/indexer/indexer.js +524 -846
- package/dist/indexer/materialize-embeddings.js +60 -397
- package/dist/indexer/passes/memory-inference.js +81 -90
- package/dist/indexer/passes/metadata.js +132 -200
- package/dist/indexer/read-preflight.js +0 -7
- package/dist/indexer/scan/doc-to-entry.js +1 -3
- package/dist/indexer/scan/drain-dir.js +1 -1
- package/dist/indexer/search/db-search.js +181 -590
- package/dist/indexer/search/fts-query.js +30 -41
- package/dist/indexer/search/ranking.js +28 -154
- package/dist/indexer/search/search-attribution.js +12 -32
- package/dist/indexer/search/search-fields.js +11 -15
- package/dist/indexer/search/search-hit-enrichers.js +54 -85
- package/dist/indexer/search/search-source.js +1 -4
- package/dist/indexer/usage/usage-events.js +2 -7
- package/dist/integrations/agent/engine-fallback.js +23 -40
- package/dist/integrations/agent/engine-resolution.js +93 -183
- package/dist/integrations/agent/execution.js +507 -0
- package/dist/integrations/agent/model-map.js +28 -156
- package/dist/integrations/agent/request-lowering.js +66 -141
- package/dist/integrations/agent/runner-dispatch.js +143 -321
- package/dist/integrations/agent/runner.js +54 -14
- package/dist/integrations/lockfile.js +53 -101
- package/dist/llm/embedders/deterministic.js +2 -3
- package/dist/llm/embedders/profile.js +71 -0
- package/dist/llm/embedders/remote.js +10 -15
- package/dist/llm/graph-extract.js +3 -12
- package/dist/llm/index-passes.js +3 -5
- package/dist/llm/memory-infer.js +1 -2
- package/dist/llm/metadata-enhance.js +1 -2
- package/dist/llm/structured-call.js +5 -24
- package/dist/output/generic-render.js +23 -11
- package/dist/output/html-render.js +13 -10
- package/dist/output/render-registry.js +3 -32
- package/dist/output/shapes/helpers.js +2 -34
- package/dist/output/shapes/passthrough.js +1 -9
- package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
- package/dist/output/text/command-format.js +60 -23
- package/dist/output/text/helpers.js +1 -1
- package/dist/output/text/migrate.js +5 -14
- package/dist/output/text/proposal-format.js +1 -2
- package/dist/output/text/workflow-format.js +0 -32
- package/dist/output/text.js +2 -0
- package/dist/registry/factory.js +4 -19
- package/dist/registry/network.js +66 -220
- package/dist/registry/providers/index.js +0 -2
- package/dist/registry/providers/skills-sh.js +3 -14
- package/dist/registry/providers/static-index.js +24 -26
- package/dist/registry/resolve.js +55 -131
- package/dist/scripts/akm-migrate-node.js +43937 -93313
- package/dist/scripts/akm-migrate.js +43697 -93071
- package/dist/setup/registry-stash-loader.js +4 -13
- package/dist/setup/semantic-assets.js +3 -44
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/tasks.js +25 -15
- package/dist/sources/provider-factory.js +17 -18
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +7 -1
- package/dist/sources/providers/git-provider.js +0 -3
- package/dist/sources/providers/git-stash.js +0 -17
- package/dist/sources/providers/npm.js +2 -4
- package/dist/sources/providers/provider-utils.js +5 -10
- package/dist/sources/providers/website.js +0 -2
- package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
- package/dist/sources/website-url.js +2 -2
- package/dist/storage/database.js +9 -35
- package/dist/storage/repositories/improve-ledger-repository.js +168 -0
- package/dist/storage/repositories/index-connection.js +34 -70
- package/dist/storage/repositories/index-entries-repository.js +69 -111
- package/dist/storage/repositories/index-entry-mapper.js +1 -2
- package/dist/storage/repositories/index-entry-schema.js +83 -269
- package/dist/storage/repositories/index-fts-repository.js +86 -256
- package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
- package/dist/storage/repositories/index-meta-repository.js +6 -4
- package/dist/storage/repositories/index-schema.js +192 -220
- package/dist/storage/repositories/index-utility-repository.js +8 -29
- package/dist/storage/repositories/index-vec-repository.js +133 -414
- package/dist/storage/repositories/outcome-repository.js +2 -1
- package/dist/storage/repositories/proposals-repository.js +35 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
- package/dist/storage/repositories/task-history-repository.js +26 -4
- package/dist/storage/repositories/workflow-runs-repository.js +53 -244
- package/dist/storage/sqlite-migrations.js +136 -0
- package/dist/storage/sqlite-pragmas.js +11 -9
- package/dist/storage/sqlite-transaction.js +170 -0
- package/dist/storage/state-db-integrity.js +34 -27
- package/dist/tasks/activation-config.js +134 -62
- package/dist/tasks/backends/cron.js +129 -277
- package/dist/tasks/backends/exec-utils.js +2 -5
- package/dist/tasks/backends/launchd.js +125 -745
- package/dist/tasks/backends/schtasks.js +101 -620
- package/dist/tasks/prepare/prepare-support.js +5 -15
- package/dist/tasks/prepare/prepare.js +0 -2
- package/dist/tasks/resolve-akm-bin.js +20 -79
- package/dist/tasks/run/attempt-lifecycle.js +0 -1
- package/dist/tasks/scheduler-binding.js +18 -238
- package/dist/tasks/scheduler-invocation.js +52 -52
- package/dist/tasks/scheduler-lock.js +53 -0
- package/dist/tasks/scheduler-sync.js +363 -679
- package/dist/tasks/source/parse-task-source.js +160 -10
- package/dist/tasks/source/task-source-v3-frozen.js +3 -4
- package/dist/tasks/source/task-to-v4.js +2 -2
- package/dist/workflows/authoring/authoring.js +3 -12
- package/dist/workflows/compile.js +211 -0
- package/dist/workflows/concurrency-policy.js +13 -74
- package/dist/workflows/exec/child-invocation.js +3 -17
- package/dist/workflows/exec/child-workflow.js +32 -141
- package/dist/workflows/exec/dispatch-redaction.js +13 -53
- package/dist/workflows/exec/environment.js +98 -0
- package/dist/workflows/exec/exec-unit.js +33 -140
- package/dist/workflows/exec/frozen-judge.js +7 -59
- package/dist/workflows/exec/native-executor.js +82 -341
- package/dist/workflows/exec/param-secrets.js +29 -47
- package/dist/workflows/exec/run-workflow.js +154 -387
- package/dist/workflows/exec/scheduler.js +9 -36
- package/dist/workflows/exec/step-work.js +127 -430
- package/dist/workflows/exec/unit-dispatch.js +11 -63
- package/dist/workflows/exec/unit-writer.js +8 -52
- package/dist/workflows/exec/worktree.js +39 -273
- package/dist/workflows/freeze/child-output-references.js +4 -15
- package/dist/workflows/freeze/environment.js +99 -92
- package/dist/workflows/freeze/freeze.js +172 -0
- package/dist/workflows/freeze/step-values.js +19 -21
- package/dist/workflows/freeze/targets/child-workflow.js +23 -92
- package/dist/workflows/freeze/targets/command.js +10 -33
- package/dist/workflows/freeze/targets/script.js +5 -12
- package/dist/workflows/freeze/targets/shell.js +3 -6
- package/dist/workflows/freeze/targets/task.js +25 -80
- package/dist/workflows/freeze/task-bindings.js +20 -67
- package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
- package/dist/workflows/ir/params.js +6 -51
- package/dist/workflows/ir/plan-hash.js +2 -34
- package/dist/workflows/parser.js +140 -43
- package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
- package/dist/workflows/renderer.js +36 -69
- package/dist/workflows/resource-limits.js +12 -120
- package/dist/workflows/runtime/agent-identity.js +8 -40
- package/dist/workflows/runtime/run-outputs.js +3 -6
- package/dist/workflows/runtime/run-plan.js +316 -0
- package/dist/workflows/runtime/runs.js +48 -200
- package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
- package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
- package/dist/workflows/validate-summary.js +2 -7
- package/docs/integration/bundling-akm.md +49 -42
- package/docs/migration/README.md +1 -0
- package/docs/migration/release-notes/0.9.17.md +41 -0
- package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
- package/docs/reference/cli.md +182 -125
- package/docs/reference/configuration.md +49 -56
- package/docs/reference/data-and-telemetry.md +19 -20
- package/docs/reference/tasks.md +86 -38
- package/docs/reference/workflow-schema.md +14 -18
- package/docs/reference/workflows.md +6 -9
- package/package.json +1 -1
- package/schemas/akm-config.json +87 -406
- package/dist/commands/health/advisories.js +0 -150
- package/dist/commands/health/metrics.js +0 -329
- package/dist/commands/health/surfaces.js +0 -102
- package/dist/commands/improve/anti-collapse.js +0 -83
- package/dist/commands/improve/collapse-detector.js +0 -432
- package/dist/commands/improve/consolidate/eligibility.js +0 -48
- package/dist/commands/improve/consolidate/merge.js +0 -146
- package/dist/commands/improve/distill/promote-memory.js +0 -329
- package/dist/commands/improve/distill/quality-gate.js +0 -500
- package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
- package/dist/commands/improve/proposal-envelope.js +0 -31
- package/dist/commands/improve/run-context.js +0 -123
- package/dist/commands/improve/shared.js +0 -21
- package/dist/commands/improve/source-identity.js +0 -28
- package/dist/commands/improve/triage.js +0 -96
- package/dist/commands/proposal/drain-policies.js +0 -151
- package/dist/commands/sources/update-transaction.js +0 -220
- package/dist/core/action-contributors.js +0 -28
- package/dist/core/config/config-version-shim.js +0 -101
- package/dist/core/config/retired-experimental-keys-shim.js +0 -62
- package/dist/core/fs-txn.js +0 -405
- package/dist/core/lexical-score.js +0 -25
- package/dist/core/maintenance-barrier.js +0 -167
- package/dist/execution/executable-identity.js +0 -105
- package/dist/execution/guarded-source.js +0 -427
- package/dist/indexer/graph/graph-boost.js +0 -427
- package/dist/indexer/graph/graph-dedup.js +0 -95
- package/dist/indexer/search/name-match.js +0 -35
- package/dist/indexer/search/ranking-contributors.js +0 -515
- package/dist/indexer/walk/project-context.js +0 -192
- package/dist/integrations/agent/execution-cascade.js +0 -566
- package/dist/integrations/agent/execution-definitions.js +0 -202
- package/dist/integrations/agent/execution-lowering.js +0 -841
- package/dist/integrations/agent/execution-preparation.js +0 -98
- package/dist/integrations/agent/inline-execution.js +0 -74
- package/dist/registry/create-provider-registry.js +0 -29
- package/dist/registry/pinned-request-helper.js +0 -247
- package/dist/registry/pinned-transport.js +0 -717
- package/dist/sources/providers/index.js +0 -14
- package/dist/storage/engines/sqlite-migrations.js +0 -271
- package/dist/storage/repositories/canaries-repository.js +0 -107
- package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
- package/dist/storage/repositories/registry-cache.js +0 -113
- package/dist/tasks/scheduler-sync-preview.js +0 -52
- package/dist/workflows/freeze/resolve-steps.js +0 -86
- package/dist/workflows/freeze/source-freeze.js +0 -64
- package/dist/workflows/ir/compile.js +0 -321
- package/dist/workflows/ir/environment-v4.js +0 -330
- package/dist/workflows/ir/freeze-v4.js +0 -153
- package/dist/workflows/ir/schema-v4.js +0 -745
- package/dist/workflows/ir/schema.js +0 -354
- package/dist/workflows/program/schema.js +0 -78
- package/dist/workflows/runtime/checkin.js +0 -57
- package/dist/workflows/runtime/plan-classifier.js +0 -196
- package/dist/workflows/runtime/unit-checkin.js +0 -45
- package/dist/workflows/runtime/unit-phases.js +0 -20
- package/dist/workflows/schema.js +0 -4
- package/dist/workflows/source-ir/compile.js +0 -200
- package/dist/workflows/source-ir/program.js +0 -50
- package/dist/workflows/source-ir/result.js +0 -26
- package/dist/workflows/source-ir/schema.js +0 -786
- package/dist/workflows/source-ir/triggers.js +0 -79
- package/dist/workflows/source-ir/uses.js +0 -40
- package/dist/workflows/validator.js +0 -60
|
@@ -1,24 +1,14 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
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
|
-
/**
|
|
5
|
-
* File-private helpers prepareTaskV3Execution depends on, moved body-intact
|
|
6
|
-
* out of the pre-P1b src/tasks/runtime-v3.ts (spec
|
|
7
|
-
* docs/plans/specs/p1b-model-extraction.md §4.1, Lane B / D4 module map).
|
|
8
|
-
* None of these was exported at head either; only the subset prepare.ts's
|
|
9
|
-
* moved function body calls is exported here.
|
|
10
|
-
*/
|
|
11
|
-
import path from "node:path";
|
|
12
4
|
import { makeBundleRef, parseBundleRef } from "../../core/asset/asset-ref.js";
|
|
13
5
|
import { COMPOSITION_INVALID_MULTI_JOB_HINT, ConfigError, NotFoundError, UsageError } from "../../core/errors.js";
|
|
14
6
|
import { DURATION_UNITS, parseDuration } from "../../core/time.js";
|
|
15
7
|
import { isPortableExecutionAgentSelector } from "../../execution/source.js";
|
|
16
|
-
import {
|
|
17
|
-
import { lowerResolvedExecutionRequest } from "../../integrations/agent/execution-lowering.js";
|
|
8
|
+
import { buildExecution } from "../../integrations/agent/execution.js";
|
|
18
9
|
import { resolveAssetPath } from "../../sources/resolve.js";
|
|
10
|
+
import { checkWorkflowPlan, compileWorkflowSource } from "../../workflows/compile.js";
|
|
19
11
|
import { detectSecretShapedParams } from "../../workflows/exec/param-secrets.js";
|
|
20
|
-
import { compileWorkflowPlan } from "../../workflows/ir/compile.js";
|
|
21
|
-
import { compileWorkflowSource } from "../../workflows/source-ir/compile.js";
|
|
22
12
|
import { isInferredSecretName } from "../log-redaction.js";
|
|
23
13
|
import { SCHEDULED_TASK_CONTEXT_KEYS } from "../scheduler-invocation.js";
|
|
24
14
|
function own(value, key) {
|
|
@@ -118,14 +108,14 @@ export function defaultTaskShell(platform) {
|
|
|
118
108
|
return platform === "win32" ? "powershell" : "sh";
|
|
119
109
|
}
|
|
120
110
|
export function validatePreparedCommand(invocation, context) {
|
|
121
|
-
const request =
|
|
111
|
+
const { request } = invocation;
|
|
122
112
|
if (!request.engine.name) {
|
|
123
113
|
throw new ConfigError(`Task ${JSON.stringify(context.taskRef)} has no resolved execution engine. Configure defaults.engine or akm.engine before running it.`, "INVALID_CONFIG_FILE");
|
|
124
114
|
}
|
|
125
115
|
// Lowering is pure. Running it before the durable-attempt boundary proves
|
|
126
116
|
// the selected target is transport-projectable; dispatch repeats the same
|
|
127
117
|
// deterministic projection from this frozen request/config snapshot.
|
|
128
|
-
|
|
118
|
+
buildExecution(request, invocation.runner);
|
|
129
119
|
return invocation;
|
|
130
120
|
}
|
|
131
121
|
export function validateWorkflowRuntimeSource(file, workspaceRoot, readFile) {
|
|
@@ -141,7 +131,7 @@ export function validateWorkflowRuntimeSource(file, workspaceRoot, readFile) {
|
|
|
141
131
|
const code = isMultiJob ? "COMPOSITION_INVALID" : "WORKFLOW_SOURCE_INVALID";
|
|
142
132
|
throw new UsageError(`Task workflow target is not projectable: ${detail}`, code, isMultiJob ? COMPOSITION_INVALID_MULTI_JOB_HINT : undefined);
|
|
143
133
|
}
|
|
144
|
-
const planned =
|
|
134
|
+
const planned = checkWorkflowPlan(compiled.plan);
|
|
145
135
|
if (!planned.ok) {
|
|
146
136
|
// Same mapping applied for consistency; `compiled.ir` is already
|
|
147
137
|
// guaranteed exactly one job here, so this arm always resolves to
|
|
@@ -54,7 +54,6 @@ export async function prepareTaskV3Execution(document, context) {
|
|
|
54
54
|
const invocation = validatePreparedCommand(await (context.prepareCommand ?? prepareCommandInvocation)({
|
|
55
55
|
action,
|
|
56
56
|
config: context.config,
|
|
57
|
-
invocationKind: "task",
|
|
58
57
|
current: currentExecutionValues(document, context, commandEnvironment),
|
|
59
58
|
...(context.commandSourceLoader ? { sourceLoader: context.commandSourceLoader } : {}),
|
|
60
59
|
}), context);
|
|
@@ -71,7 +70,6 @@ export async function prepareTaskV3Execution(document, context) {
|
|
|
71
70
|
const invocation = validatePreparedCommand(await (context.prepareCommand ?? prepareCommandInvocation)({
|
|
72
71
|
action: { ref: qualified },
|
|
73
72
|
config: context.config,
|
|
74
|
-
invocationKind: "task",
|
|
75
73
|
current: currentExecutionValues(document, context, commandEnvironment),
|
|
76
74
|
...(context.commandSourceLoader ? { sourceLoader: context.commandSourceLoader } : {}),
|
|
77
75
|
}), context);
|
|
@@ -10,17 +10,16 @@
|
|
|
10
10
|
* Resolution order:
|
|
11
11
|
*
|
|
12
12
|
* 1. `process.execPath` alone for a Bun standalone executable.
|
|
13
|
-
* 2. Absolute Node plus the public `dist/akm` package launcher
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* checkout that requires explicit `--rebind` for scheduler writes.
|
|
13
|
+
* 2. Absolute Node plus the public `dist/akm` package launcher.
|
|
14
|
+
* 3. Absolute runtime plus the source/build CLI entry of a checkout.
|
|
15
|
+
*
|
|
16
|
+
* `via` names which one was used. `checkout` — a package launcher inside a
|
|
17
|
+
* git work tree, or a source/build entry — runs whatever the checkout holds
|
|
18
|
+
* when the scheduler fires; `task sync` says so once when it writes one.
|
|
20
19
|
*
|
|
21
20
|
* Returns the argv array the scheduler should execute (e.g.
|
|
22
|
-
* `["/usr/local/bin/node", "/
|
|
23
|
-
* subcommand args (`"task", "run", "<id>"`).
|
|
21
|
+
* `["/usr/local/bin/node", "/usr/lib/node_modules/akm-cli/dist/akm"]`). The
|
|
22
|
+
* caller appends subcommand args (`"task", "run", "<id>"`).
|
|
24
23
|
*/
|
|
25
24
|
import { spawnSync } from "node:child_process";
|
|
26
25
|
import fs from "node:fs";
|
|
@@ -28,6 +27,8 @@ import path from "node:path";
|
|
|
28
27
|
import { fileURLToPath } from "node:url";
|
|
29
28
|
import { ConfigError } from "../core/errors.js";
|
|
30
29
|
import { mainPath as runtimeMainPath } from "../runtime.js";
|
|
30
|
+
/** A source entry or local build run by a runtime: `src/cli.ts`, `dist/cli.js`, `dist/cli-node.mjs`. */
|
|
31
|
+
const CHECKOUT_ENTRY = /(?:^|[\\/])src[\\/]cli\.ts$|(?:^|[\\/])dist[\\/](?:cli\.js|cli-node\.mjs)$/i;
|
|
31
32
|
/** True only for Bun's virtual main path inside a compiled standalone executable. */
|
|
32
33
|
export function isBunStandaloneMain(mainPath = runtimeMainPath) {
|
|
33
34
|
return (mainPath?.startsWith("/$bunfs/") === true || (mainPath !== undefined && /^[A-Za-z]:[\\/]~BUN[\\/]/i.test(mainPath)));
|
|
@@ -37,44 +38,27 @@ export function resolveAkmInvocation(options = {}) {
|
|
|
37
38
|
const runtime = options.runtime ?? (process.versions.bun ? "bun" : "node");
|
|
38
39
|
const execPath = options.execPath ?? process.execPath;
|
|
39
40
|
const mainPath = options.mainPath ?? runtimeMainPath;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return { argv: [absoluteInvocationPath(execPath)], via: "standalone", kind: "standalone", eligible: true };
|
|
41
|
+
if (runtime === "bun" && isBunStandaloneMain(mainPath) && execPath) {
|
|
42
|
+
return { argv: [absoluteInvocationPath(execPath)], via: "standalone" };
|
|
43
43
|
}
|
|
44
44
|
const launcherPath = options.launcherPath ?? env.AKM_LAUNCHER_PATH?.trim();
|
|
45
45
|
const nodePath = options.nodePath ?? env.AKM_LAUNCHER_NODE?.trim() ?? (runtime === "node" ? execPath : undefined);
|
|
46
46
|
if (launcherPath && nodePath && isPublicPackageLauncher(launcherPath)) {
|
|
47
|
-
const checkout = isCheckoutLauncher(launcherPath);
|
|
48
|
-
let npmGlobalRoot;
|
|
49
|
-
if (!checkout) {
|
|
50
|
-
try {
|
|
51
|
-
npmGlobalRoot = (options.resolveNpmGlobalRoot ?? ((node) => resolveNpmGlobalRoot(node, env)))(nodePath);
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// An unprovable package installation is intentionally ineligible.
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
const npmGlobal = !checkout && packageBelongsToNpmGlobalRoot(launcherPath, npmGlobalRoot);
|
|
58
|
-
const readOnlyInstall = !checkout && !npmGlobal && !(options.isPathWritable ?? isPathWritable)(path.dirname(launcherPath));
|
|
59
|
-
const kind = checkout ? "checkout" : npmGlobal ? "npm" : "package-local";
|
|
60
47
|
return {
|
|
61
48
|
argv: [absoluteInvocationPath(nodePath), absoluteInvocationPath(launcherPath)],
|
|
62
|
-
via:
|
|
63
|
-
kind,
|
|
64
|
-
eligible: npmGlobal || readOnlyInstall,
|
|
49
|
+
via: isCheckoutLauncher(launcherPath) ? "checkout" : "npm",
|
|
65
50
|
};
|
|
66
51
|
}
|
|
67
52
|
const checkoutEntry = resolveCheckoutEntry(options.cliEntryUrl ?? import.meta.url, runtime, mainPath);
|
|
68
53
|
if (checkoutEntry && execPath) {
|
|
69
|
-
return {
|
|
70
|
-
argv: [absoluteInvocationPath(execPath), absoluteInvocationPath(checkoutEntry)],
|
|
71
|
-
via: "checkout",
|
|
72
|
-
kind: "checkout",
|
|
73
|
-
eligible: false,
|
|
74
|
-
};
|
|
54
|
+
return { argv: [absoluteInvocationPath(execPath), absoluteInvocationPath(checkoutEntry)], via: "checkout" };
|
|
75
55
|
}
|
|
76
56
|
throw new ConfigError("Cannot resolve absolute path to the akm binary for scheduler registration.", "INVALID_CONFIG_FILE", "Run the npm-global launcher or a standalone akm executable.");
|
|
77
57
|
}
|
|
58
|
+
/** Whether an installed launcher argv runs akm out of a source checkout. */
|
|
59
|
+
export function isCheckoutInvocation(argv) {
|
|
60
|
+
return argv.some((part) => CHECKOUT_ENTRY.test(part) || (isPublicPackageLauncher(part) && isCheckoutLauncher(part)));
|
|
61
|
+
}
|
|
78
62
|
function resolveCheckoutEntry(moduleUrl, runtime, mainPath) {
|
|
79
63
|
try {
|
|
80
64
|
const modulePath = fileURLToPath(moduleUrl);
|
|
@@ -111,6 +95,7 @@ function isPublicPackageLauncher(file) {
|
|
|
111
95
|
path.basename(path.dirname(file)).toLowerCase() === "dist" &&
|
|
112
96
|
fs.existsSync(file));
|
|
113
97
|
}
|
|
98
|
+
/** A package launcher whose package root is a git work tree (`npm link`, a local clone). */
|
|
114
99
|
function isCheckoutLauncher(file) {
|
|
115
100
|
let launcher = path.resolve(file);
|
|
116
101
|
try {
|
|
@@ -122,52 +107,8 @@ function isCheckoutLauncher(file) {
|
|
|
122
107
|
const packageRoot = path.dirname(path.dirname(launcher));
|
|
123
108
|
return fs.existsSync(path.join(packageRoot, ".git"));
|
|
124
109
|
}
|
|
125
|
-
/**
|
|
126
|
-
function isPathWritable(dir) {
|
|
127
|
-
try {
|
|
128
|
-
fs.accessSync(dir, fs.constants.W_OK);
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
catch {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
function packageBelongsToNpmGlobalRoot(launcherPath, npmGlobalRoot) {
|
|
136
|
-
if (!npmGlobalRoot)
|
|
137
|
-
return false;
|
|
138
|
-
try {
|
|
139
|
-
const packageRoot = path.dirname(path.dirname(fs.realpathSync(launcherPath)));
|
|
140
|
-
const globalRoot = fs.realpathSync(npmGlobalRoot);
|
|
141
|
-
const metadata = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
|
|
142
|
-
return metadata.name === "akm-cli" && samePath(path.dirname(packageRoot), globalRoot);
|
|
143
|
-
}
|
|
144
|
-
catch {
|
|
145
|
-
return false;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
function samePath(left, right) {
|
|
149
|
-
const normalize = (value) => {
|
|
150
|
-
const resolved = path.resolve(value);
|
|
151
|
-
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
152
|
-
};
|
|
153
|
-
return normalize(left) === normalize(right);
|
|
154
|
-
}
|
|
155
|
-
// #901: the global root can't change within a process lifetime, and every
|
|
156
|
-
// `resolveAkmInvocation()` call in a scheduler-sync process re-derives the
|
|
157
|
-
// same `nodePath` — so this spawns `npm root --global` at most once per
|
|
158
|
-
// process instead of once per call. Keyed by nodePath (rather than a bare
|
|
159
|
-
// once-only flag) so a differently-invoked probe later in the same process
|
|
160
|
-
// still gets its own answer instead of a stale one.
|
|
161
|
-
let cachedNpmGlobalRoot;
|
|
110
|
+
/** `npm root --global` for the npm that ships with `nodePath`; `akm upgrade` uses it to detect a global install. */
|
|
162
111
|
export function resolveNpmGlobalRoot(nodePath, env) {
|
|
163
|
-
if (cachedNpmGlobalRoot && cachedNpmGlobalRoot.nodePath === nodePath) {
|
|
164
|
-
return cachedNpmGlobalRoot.value;
|
|
165
|
-
}
|
|
166
|
-
const value = resolveNpmGlobalRootUncached(nodePath, env);
|
|
167
|
-
cachedNpmGlobalRoot = { nodePath, value };
|
|
168
|
-
return value;
|
|
169
|
-
}
|
|
170
|
-
function resolveNpmGlobalRootUncached(nodePath, env) {
|
|
171
112
|
const npmCli = resolveAssociatedNpmCli(nodePath);
|
|
172
113
|
if (!npmCli)
|
|
173
114
|
return undefined;
|
|
@@ -2,12 +2,12 @@
|
|
|
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
|
-
* Secret-free input to every native scheduler backend
|
|
5
|
+
* Secret-free input to every native scheduler backend, and the backend
|
|
6
|
+
* contract itself.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* this boundary or be persisted by an OS scheduler.
|
|
8
|
+
* A binding carries only stable source identity, trigger identity, and the
|
|
9
|
+
* public CLI tail. Source content, action inputs, environment values, and
|
|
10
|
+
* credentials never cross this boundary or reach an OS scheduler.
|
|
11
11
|
*/
|
|
12
12
|
import { createHash } from "node:crypto";
|
|
13
13
|
import { bundleRefToString, parseBundleRef } from "../core/asset/asset-ref.js";
|
|
@@ -20,20 +20,8 @@ export function compileTaskSchedulerBindings(input) {
|
|
|
20
20
|
const bundle = parseBundleRef(ref).bundle;
|
|
21
21
|
if (!bundle)
|
|
22
22
|
throw new Error("invariant: qualified scheduler task ref lost its bundle");
|
|
23
|
-
// P2b Lane B (B-N3): the invocation is compiled PER schedule entry now —
|
|
24
|
-
// each entry's own `inputs` produces its own trailing flag tail, so it can
|
|
25
|
-
// no longer be hoisted as one binding shared by every entry.
|
|
26
23
|
return Object.freeze(input.schedules.map((schedule) => {
|
|
27
24
|
const bindingId = schedule.ordinal === 0 ? id : digestBindingId("task", ref, schedule.ordinal);
|
|
28
|
-
const invocation = Object.freeze([
|
|
29
|
-
"task",
|
|
30
|
-
"run",
|
|
31
|
-
id,
|
|
32
|
-
"--bundle",
|
|
33
|
-
bundle,
|
|
34
|
-
"--scheduled",
|
|
35
|
-
...schedulerInputFlagTail(schedule.inputs),
|
|
36
|
-
]);
|
|
37
25
|
return freezeBinding({
|
|
38
26
|
id: bindingId,
|
|
39
27
|
nativeId: schedulerNativeBindingId(bindingId),
|
|
@@ -42,39 +30,21 @@ export function compileTaskSchedulerBindings(input) {
|
|
|
42
30
|
source: schedule.source,
|
|
43
31
|
ordinal: schedule.ordinal,
|
|
44
32
|
enabled: true,
|
|
45
|
-
invocation,
|
|
33
|
+
invocation: ["task", "run", id, "--bundle", bundle, "--scheduled", ...schedulerInputFlagTail(schedule.inputs)],
|
|
46
34
|
});
|
|
47
35
|
}));
|
|
48
36
|
}
|
|
49
37
|
/**
|
|
50
38
|
* The canonically-sorted `--<name> <value>` flag tail for one schedule
|
|
51
|
-
* entry's `inputs
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* Code-review finding (scheduler-binding.ts:536): a value whose exact text
|
|
56
|
-
* begins with `-` (a negative number, or a string that just happens to
|
|
57
|
-
* start with a dash) is indistinguishable from a NEW flag in the two-token
|
|
58
|
-
* `--<name> <value>` form — `isValidSchedulerInputFlagTail`
|
|
59
|
-
* (scheduler-invocation.ts) refuses it outright, and even a looser parser
|
|
60
|
-
* would still be wrong for a non-numeric dash-leading string: the real `akm
|
|
61
|
-
* task run` flag parser (`parseTaskInputFlags`, tasks-cli.ts) only
|
|
62
|
-
* special-cases a dash-DIGIT lead, so it would silently treat `--scope
|
|
63
|
-
* -urgent` as the boolean flag `--scope` followed by an orphaned,
|
|
64
|
-
* dropped `-urgent` token. The inline `--<name>=<value>` form has no such
|
|
65
|
-
* ambiguity on EITHER side — `isValidSchedulerInputFlagTail` accepts it
|
|
66
|
-
* unconditionally, and `parseTaskInputFlags`'s own inline-`=` branch splits
|
|
67
|
-
* on the first `=` without ever inspecting the value's leading character —
|
|
68
|
-
* so it is used whenever the value's exact text would otherwise be
|
|
69
|
-
* ambiguous. Every other value keeps the existing two-token form
|
|
70
|
-
* byte-identical to before this fix (B-03/B-45).
|
|
39
|
+
* entry's `inputs`. A value whose text starts with `-` uses the inline
|
|
40
|
+
* `--<name>=<value>` form, the only spelling `parseTaskInputFlags` cannot
|
|
41
|
+
* mistake for a flag.
|
|
71
42
|
*/
|
|
72
43
|
function schedulerInputFlagTail(inputs) {
|
|
73
44
|
if (!inputs)
|
|
74
45
|
return [];
|
|
75
|
-
const names = Object.keys(inputs).sort();
|
|
76
46
|
const tail = [];
|
|
77
|
-
for (const name of
|
|
47
|
+
for (const name of Object.keys(inputs).sort()) {
|
|
78
48
|
const text = schedulerInputFlagValueText(inputs[name]);
|
|
79
49
|
if (text.startsWith("-"))
|
|
80
50
|
tail.push(`--${name}=${text}`);
|
|
@@ -83,14 +53,6 @@ function schedulerInputFlagTail(inputs) {
|
|
|
83
53
|
}
|
|
84
54
|
return tail;
|
|
85
55
|
}
|
|
86
|
-
/**
|
|
87
|
-
* One input value's argv text. A scalar is its exact text — `String(value)`
|
|
88
|
-
* for a number/boolean (a `true` boolean is `"true"`, never a bare flag, so
|
|
89
|
-
* the tail round-trips through one parser), the string itself for a string.
|
|
90
|
-
* An object/array value is its `canonicalInputJson` text, which
|
|
91
|
-
* `materializeInputFlags`'s JSON-shorthand path already coerces back through
|
|
92
|
-
* the declaration.
|
|
93
|
-
*/
|
|
94
56
|
function schedulerInputFlagValueText(value) {
|
|
95
57
|
if (typeof value === "string")
|
|
96
58
|
return value;
|
|
@@ -100,7 +62,6 @@ function schedulerInputFlagValueText(value) {
|
|
|
100
62
|
}
|
|
101
63
|
export function compileWorkflowSchedulerBindings(input) {
|
|
102
64
|
const ref = assertQualifiedRef(input.qualifiedRef, "workflow");
|
|
103
|
-
const invocation = Object.freeze(["workflow", "run", ref]);
|
|
104
65
|
return Object.freeze(input.schedules.map((schedule) => {
|
|
105
66
|
const bindingId = digestBindingId("workflow", ref, schedule.ordinal);
|
|
106
67
|
return freezeBinding({
|
|
@@ -111,23 +72,14 @@ export function compileWorkflowSchedulerBindings(input) {
|
|
|
111
72
|
source: schedule.source,
|
|
112
73
|
ordinal: schedule.ordinal,
|
|
113
74
|
enabled: true,
|
|
114
|
-
invocation,
|
|
115
|
-
...(input.executionEvidenceDigest !== undefined
|
|
116
|
-
? { executionEvidenceDigest: assertSchedulerExecutionEvidenceDigest(input.executionEvidenceDigest) }
|
|
117
|
-
: {}),
|
|
75
|
+
invocation: ["workflow", "run", ref],
|
|
118
76
|
});
|
|
119
77
|
}));
|
|
120
78
|
}
|
|
121
|
-
export function assertSchedulerExecutionEvidenceDigest(value) {
|
|
122
|
-
if (!/^[a-f0-9]{64}$/u.test(value)) {
|
|
123
|
-
throw new UsageError("Scheduler workflow execution evidence must be a lowercase SHA-256 digest.", "INVALID_FLAG_VALUE");
|
|
124
|
-
}
|
|
125
|
-
return value;
|
|
126
|
-
}
|
|
127
79
|
/**
|
|
128
80
|
* Map a logical binding id to the flat portable token persisted by native
|
|
129
|
-
* schedulers.
|
|
130
|
-
* component
|
|
81
|
+
* schedulers. Flat ids are byte-for-byte stable; only nested standalone
|
|
82
|
+
* component ids need an encoded native spelling.
|
|
131
83
|
*/
|
|
132
84
|
export function schedulerNativeBindingId(id) {
|
|
133
85
|
if (!id.includes("/"))
|
|
@@ -138,183 +90,17 @@ export function schedulerNativeBindingId(id) {
|
|
|
138
90
|
.slice(0, 32);
|
|
139
91
|
return `task-${digest}`;
|
|
140
92
|
}
|
|
141
|
-
/** Portable collision key for scheduler namespaces with Windows semantics. */
|
|
142
|
-
export function schedulerNativeArtifactKey(nativeId) {
|
|
143
|
-
return nativeId.toLowerCase().replace(/\.+$/u, "");
|
|
144
|
-
}
|
|
145
|
-
export function schedulerNativeArtifactsForKey(artifacts, nativeId) {
|
|
146
|
-
const key = schedulerNativeArtifactKey(nativeId);
|
|
147
|
-
return artifacts.filter((artifact) => schedulerNativeArtifactKey(artifact.nativeId) === key);
|
|
148
|
-
}
|
|
149
|
-
export function assertSchedulerNativeArtifactCardinality(artifacts, nativeId, expectedCount) {
|
|
150
|
-
const matches = schedulerNativeArtifactsForKey(artifacts, nativeId);
|
|
151
|
-
if (matches.length !== expectedCount) {
|
|
152
|
-
const expectedState = expectedCount === 0 ? "absence" : "one present owner";
|
|
153
|
-
throw new UsageError(`Native scheduler artifact ${JSON.stringify(nativeId)} changed: normalized cardinality ${matches.length} violates the expected ${expectedState} before mutation.`, "RESOURCE_ALREADY_EXISTS");
|
|
154
|
-
}
|
|
155
|
-
return matches[0];
|
|
156
|
-
}
|
|
157
|
-
/** Return a logical owner only when the public invocation proves the mapping. */
|
|
158
|
-
export function schedulerLogicalBindingOwner(nativeId, invocation) {
|
|
159
|
-
return schedulerNativeArtifactOwner(nativeId, invocation)?.logicalId;
|
|
160
|
-
}
|
|
161
|
-
export function schedulerNativeArtifactOwner(nativeId, invocation) {
|
|
162
|
-
if (invocation[0] === "workflow" && invocation[1] === "run" && invocation.length === 3) {
|
|
163
|
-
const ref = invocation[2];
|
|
164
|
-
if (ref && workflowRefDigestsToNativeId(ref, nativeId)) {
|
|
165
|
-
return { logicalId: nativeId, logicalKind: "workflow" };
|
|
166
|
-
}
|
|
167
|
-
return undefined;
|
|
168
|
-
}
|
|
169
|
-
const taskId = invocation[0] === "task" && invocation[1] === "run" ? invocation[2] : undefined;
|
|
170
|
-
if (!taskId)
|
|
171
|
-
return undefined;
|
|
172
|
-
return {
|
|
173
|
-
logicalId: schedulerNativeBindingId(taskId) === nativeId ? taskId : nativeId,
|
|
174
|
-
logicalKind: "task",
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
export function assertSchedulerNativeArtifactOwner(nativeId, intended, invocation) {
|
|
178
|
-
const intendedNativeId = intended.nativeId ?? schedulerNativeBindingId(intended.id);
|
|
179
|
-
if (nativeId === intendedNativeId && invocation && sameInvocation(invocation, intended.invocation))
|
|
180
|
-
return;
|
|
181
|
-
const description = invocation ? JSON.stringify(invocation) : "an unproven or malformed invocation";
|
|
182
|
-
throw new UsageError(`Native scheduler artifact ${JSON.stringify(nativeId)} belongs to ${description}, not the exact ${intended.logicalSource.kind} owner ${JSON.stringify(intended.logicalSource.ref)} with binding ${JSON.stringify(intended.id)}.`, "RESOURCE_ALREADY_EXISTS");
|
|
183
|
-
}
|
|
184
|
-
/** Recover binding identity without treating the native hash as source ownership. */
|
|
185
|
-
export function schedulerLogicalBindingId(nativeId, invocation) {
|
|
186
|
-
return schedulerLogicalBindingOwner(nativeId, invocation) ?? nativeId;
|
|
187
|
-
}
|
|
188
93
|
export function schedulerBindingNativeId(binding) {
|
|
189
94
|
return binding.nativeId ?? schedulerNativeBindingId(binding.id);
|
|
190
95
|
}
|
|
191
|
-
export function assertSchedulerRemovalArtifact(nativeId, expected, invocation, fingerprint) {
|
|
192
|
-
const normalized = normalizeRemovalExpectation(expected);
|
|
193
|
-
assertSchedulerExpectationIdentity(normalized);
|
|
194
|
-
const fingerprintMatches = normalized.fingerprint === undefined || normalized.fingerprint === fingerprint;
|
|
195
|
-
if (nativeId === normalized.nativeId &&
|
|
196
|
-
invocation !== undefined &&
|
|
197
|
-
sameInvocation(invocation, normalized.invocation) &&
|
|
198
|
-
fingerprintMatches) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
throw new UsageError(`Native scheduler artifact ${JSON.stringify(nativeId)} changed owner or fingerprint; refusing to remove an unverified ${normalized.logicalSource.kind} owner ${JSON.stringify(normalized.logicalSource.ref)}.`, "RESOURCE_ALREADY_EXISTS");
|
|
202
|
-
}
|
|
203
|
-
export function assertSchedulerExpectationIdentity(expected, binding) {
|
|
204
|
-
const identity = canonicalSchedulerIdentity(expected.logicalSource, expected.ordinal, expected.invocation);
|
|
205
|
-
if (identity.bindingId !== expected.bindingId ||
|
|
206
|
-
identity.nativeId !== expected.nativeId ||
|
|
207
|
-
!sameInvocation(identity.invocation, expected.invocation) ||
|
|
208
|
-
(binding !== undefined &&
|
|
209
|
-
(binding.id !== expected.bindingId ||
|
|
210
|
-
schedulerBindingNativeId(binding) !== expected.nativeId ||
|
|
211
|
-
binding.ordinal !== expected.ordinal ||
|
|
212
|
-
binding.logicalSource.kind !== expected.logicalSource.kind ||
|
|
213
|
-
binding.logicalSource.ref !== expected.logicalSource.ref ||
|
|
214
|
-
!sameInvocation(binding.invocation, expected.invocation)))) {
|
|
215
|
-
throw new UsageError("Scheduler mutation expectation has a forged or inconsistent binding identity.", "INVALID_FLAG_VALUE");
|
|
216
|
-
}
|
|
217
|
-
if (expected.state === "absent" && expected.fingerprint !== undefined) {
|
|
218
|
-
throw new UsageError("An absent scheduler expectation cannot carry prior artifact state.", "INVALID_FLAG_VALUE");
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
export function assertSchedulerMutationArtifact(artifact, expected) {
|
|
222
|
-
assertSchedulerExpectationIdentity(expected);
|
|
223
|
-
if (expected.state === "absent") {
|
|
224
|
-
if (artifact === undefined)
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
else if (artifact !== undefined &&
|
|
228
|
-
artifact.nativeId === expected.nativeId &&
|
|
229
|
-
artifact.bindingId === expected.bindingId &&
|
|
230
|
-
artifact.invocation !== undefined &&
|
|
231
|
-
expected.fingerprint !== undefined &&
|
|
232
|
-
artifact.fingerprint === expected.fingerprint) {
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
throw new UsageError(`Native scheduler artifact ${JSON.stringify(expected.nativeId)} changed from its frozen ${expected.state} state; refusing mutation.`, "RESOURCE_ALREADY_EXISTS");
|
|
236
|
-
}
|
|
237
|
-
export function assertSchedulerRollbackArtifact(artifact, expected) {
|
|
238
|
-
const matches = expected.allowed.some((state) => {
|
|
239
|
-
if (state.state === "absent")
|
|
240
|
-
return artifact === undefined;
|
|
241
|
-
if (!artifact || artifact.nativeId !== expected.nativeId)
|
|
242
|
-
return false;
|
|
243
|
-
if (artifact.fingerprint !== state.fingerprint || artifact.bindingId !== state.bindingId)
|
|
244
|
-
return false;
|
|
245
|
-
return state.invocation === undefined
|
|
246
|
-
? artifact.invocation === undefined
|
|
247
|
-
: artifact.invocation !== undefined && sameInvocation(artifact.invocation, state.invocation);
|
|
248
|
-
});
|
|
249
|
-
if (matches)
|
|
250
|
-
return;
|
|
251
|
-
throw new UsageError(`Native scheduler artifact ${JSON.stringify(expected.nativeId)} changed after the transaction began; refusing rollback over a concurrent owner or fingerprint.`, "RESOURCE_ALREADY_EXISTS");
|
|
252
|
-
}
|
|
253
96
|
/**
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
97
|
+
* Recover the logical id of an installed row from its native spelling and
|
|
98
|
+
* public invocation: an ordinal-zero task row is its task id (possibly a
|
|
99
|
+
* nested one behind a digest spelling); every other row is its native id.
|
|
257
100
|
*/
|
|
258
|
-
export function
|
|
259
|
-
const matches = schedulerNativeArtifactsForKey(artifacts, expected.nativeId);
|
|
260
|
-
if (matches.length > 1) {
|
|
261
|
-
throw new UsageError(`Native scheduler artifact ${JSON.stringify(expected.nativeId)} changed after the transaction began: normalized rollback cardinality ${matches.length} exceeds one.`, "RESOURCE_ALREADY_EXISTS");
|
|
262
|
-
}
|
|
263
|
-
const artifact = matches[0];
|
|
264
|
-
assertSchedulerRollbackArtifact(artifact, expected);
|
|
265
|
-
return artifact;
|
|
266
|
-
}
|
|
267
|
-
export function canonicalSchedulerIdentity(logicalSource, ordinal, invocation) {
|
|
268
|
-
const parsed = parseBundleRef(logicalSource.ref);
|
|
269
|
-
if (!parsed.bundle || parsed.fragment !== undefined || bundleRefToString(parsed) !== logicalSource.ref) {
|
|
270
|
-
throw new UsageError("Scheduler expectation requires one canonical fully-qualified source.", "INVALID_FLAG_VALUE");
|
|
271
|
-
}
|
|
272
|
-
if (!Number.isSafeInteger(ordinal) || ordinal < 0) {
|
|
273
|
-
throw new UsageError("Scheduler expectation ordinal must be a non-negative integer.", "INVALID_FLAG_VALUE");
|
|
274
|
-
}
|
|
275
|
-
if (logicalSource.kind === "workflow") {
|
|
276
|
-
const canonicalInvocation = ["workflow", "run", logicalSource.ref];
|
|
277
|
-
if (!sameInvocation(invocation, canonicalInvocation)) {
|
|
278
|
-
throw new UsageError("Workflow scheduler expectation invocation does not match its qualified source.", "INVALID_FLAG_VALUE");
|
|
279
|
-
}
|
|
280
|
-
const bindingId = digestBindingId("workflow", logicalSource.ref, ordinal);
|
|
281
|
-
return Object.freeze({ bindingId, nativeId: bindingId, invocation: Object.freeze(canonicalInvocation) });
|
|
282
|
-
}
|
|
101
|
+
export function schedulerLogicalBindingId(nativeId, invocation) {
|
|
283
102
|
const taskId = invocation[0] === "task" && invocation[1] === "run" ? invocation[2] : undefined;
|
|
284
|
-
|
|
285
|
-
throw new UsageError("Task scheduler expectation has an invalid public task id.", "INVALID_FLAG_VALUE");
|
|
286
|
-
}
|
|
287
|
-
const canonicalInvocation = ["task", "run", taskId, "--bundle", parsed.bundle, "--scheduled"];
|
|
288
|
-
if (!sameInvocation(invocation, canonicalInvocation)) {
|
|
289
|
-
throw new UsageError(`Task scheduler expectation invocation does not match its qualified source: expected ${JSON.stringify(canonicalInvocation)}, got ${JSON.stringify(invocation)}.`, "INVALID_FLAG_VALUE");
|
|
290
|
-
}
|
|
291
|
-
if (parsed.conceptId !== taskId && parsed.conceptId !== `tasks/${taskId}`) {
|
|
292
|
-
throw new UsageError("Task scheduler expectation id does not match its qualified source.", "INVALID_FLAG_VALUE");
|
|
293
|
-
}
|
|
294
|
-
const bindingId = ordinal === 0 ? taskId : digestBindingId("task", logicalSource.ref, ordinal);
|
|
295
|
-
return Object.freeze({
|
|
296
|
-
bindingId,
|
|
297
|
-
nativeId: schedulerNativeBindingId(bindingId),
|
|
298
|
-
invocation: Object.freeze(canonicalInvocation),
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
/** Recover the authored schedule ordinal from the exact source/binding identity. */
|
|
302
|
-
export function schedulerBindingOrdinal(bindingId, logicalSource, invocation) {
|
|
303
|
-
if (logicalSource.kind === "task" && bindingId === invocation[2])
|
|
304
|
-
return 0;
|
|
305
|
-
for (let ordinal = 0; ordinal < 4096; ordinal += 1) {
|
|
306
|
-
if (digestBindingId(logicalSource.kind, logicalSource.ref, ordinal) === bindingId)
|
|
307
|
-
return ordinal;
|
|
308
|
-
}
|
|
309
|
-
return undefined;
|
|
310
|
-
}
|
|
311
|
-
/** Whether some schedule ordinal on `ref` would digest to `nativeId` (mirrors schedulerBindingOrdinal). */
|
|
312
|
-
function workflowRefDigestsToNativeId(ref, nativeId) {
|
|
313
|
-
for (let ordinal = 0; ordinal < 4096; ordinal += 1) {
|
|
314
|
-
if (digestBindingId("workflow", ref, ordinal) === nativeId)
|
|
315
|
-
return true;
|
|
316
|
-
}
|
|
317
|
-
return false;
|
|
103
|
+
return taskId !== undefined && schedulerNativeBindingId(taskId) === nativeId ? taskId : nativeId;
|
|
318
104
|
}
|
|
319
105
|
function digestBindingId(kind, ref, ordinal) {
|
|
320
106
|
const prefix = kind === "workflow" ? "wf" : "task";
|
|
@@ -338,9 +124,3 @@ function freezeBinding(binding) {
|
|
|
338
124
|
invocation: Object.freeze([...binding.invocation]),
|
|
339
125
|
});
|
|
340
126
|
}
|
|
341
|
-
function sameInvocation(left, right) {
|
|
342
|
-
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
343
|
-
}
|
|
344
|
-
function normalizeRemovalExpectation(expected) {
|
|
345
|
-
return { ...expected, state: "present" };
|
|
346
|
-
}
|