akm-cli 0.9.7 → 0.9.8-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +218 -0
- package/dist/commands/env/env.js +2 -21
- package/dist/commands/health/checks.js +0 -25
- package/dist/commands/health/improve-metrics.js +8 -34
- package/dist/commands/health/windows.js +0 -4
- package/dist/commands/health.js +1 -35
- package/dist/commands/improve/consolidate/eligibility.js +11 -5
- package/dist/commands/improve/extract.js +36 -32
- package/dist/commands/improve/memory/memory-belief.js +15 -5
- package/dist/commands/improve/memory/memory-contradiction-detect.js +16 -25
- package/dist/commands/improve/memory/memory-improve.js +7 -19
- package/dist/commands/improve/preparation.js +52 -36
- package/dist/commands/improve/reflect.js +13 -17
- package/dist/commands/lint/base-linter.js +42 -2
- package/dist/commands/lint/index.js +4 -5
- package/dist/commands/migrate/config-extra-params.js +61 -0
- package/dist/commands/migrate/dead-residue.js +113 -0
- package/dist/commands/migrate/stale-txn.js +49 -0
- package/dist/commands/migrate-cli.js +42 -1
- package/dist/commands/proposal/proposal.js +1 -21
- package/dist/commands/proposal/repository.js +0 -4
- package/dist/commands/proposal/validators/proposal-quality-validators.js +0 -6
- package/dist/commands/read/curate.js +4 -18
- package/dist/commands/read/show.js +2 -1
- package/dist/commands/sources/installed-stashes.js +5 -1
- package/dist/commands/sources/self-update.js +38 -1
- package/dist/commands/sources/sources-cli.js +17 -1
- package/dist/commands/tasks/tasks.js +0 -8
- package/dist/commands/url-checker.js +20 -28
- package/dist/core/adapter/adapters/agent-skills-adapter.js +2 -6
- package/dist/core/adapter/adapters/akm-adapter.js +2 -3
- package/dist/core/adapter/adapters/akm-lint.js +2 -20
- package/dist/core/adapter/adapters/akm-task-adapter.js +2 -6
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +2 -6
- package/dist/core/adapter/adapters/dotenv-adapter.js +2 -22
- package/dist/core/adapter/adapters/generic-files-adapter.js +3 -9
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +2 -6
- package/dist/core/adapter/adapters/okf-adapter.js +7 -18
- package/dist/core/adapter/adapters/shared.js +2 -0
- package/dist/core/adapter/adapters/tool-dir-shared.js +3 -9
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +2 -6
- package/dist/core/adapter/validate-context.js +1 -3
- package/dist/core/asset/asset-placement.js +1 -3
- package/dist/core/asset/frontmatter.js +96 -0
- package/dist/core/common.js +66 -2
- package/dist/core/config/config-walker.js +6 -10
- package/dist/core/config/config.js +11 -8
- package/dist/core/extra-params.js +17 -13
- package/dist/core/improve-result.js +1 -3
- package/dist/core/json-schema.js +9 -11
- package/dist/core/state/migrations.js +52 -2
- package/dist/core/state-db.js +2 -1
- package/dist/execution/executable-identity.js +1 -3
- package/dist/execution/guarded-source.js +1 -6
- package/dist/indexer/bundle-identity-guard.js +6 -1
- package/dist/indexer/db/graph-db.js +139 -154
- package/dist/indexer/ensure-index.js +11 -19
- package/dist/indexer/graph/graph-boost.js +23 -34
- package/dist/indexer/graph/graph-extraction.js +12 -2
- package/dist/indexer/indexer.js +1 -1
- package/dist/indexer/lookup/adapter-concept-owner.js +12 -9
- package/dist/indexer/passes/memory-inference.js +7 -2
- package/dist/indexer/scan/drain-dir.js +2 -4
- package/dist/indexer/search/db-search.js +3 -3
- package/dist/indexer/search/fts-query.js +10 -15
- package/dist/indexer/search/search-source.js +0 -13
- package/dist/indexer/usage/usage-events.js +9 -1
- package/dist/indexer/walk/walker.js +11 -6
- package/dist/integrations/harnesses/claude/result-extractor.js +30 -15
- package/dist/integrations/harnesses/codex/result-extractor.js +43 -6
- package/dist/integrations/harnesses/copilot/result-extractor.js +39 -12
- package/dist/integrations/harnesses/gemini/result-extractor.js +40 -12
- package/dist/integrations/harnesses/index.js +0 -4
- package/dist/integrations/harnesses/openhands/result-extractor.js +51 -19
- package/dist/integrations/harnesses/pi/result-extractor.js +45 -12
- package/dist/integrations/lockfile.js +0 -14
- package/dist/integrations/session-logs/index.js +0 -81
- package/dist/llm/client.js +0 -12
- package/dist/llm/memory-infer.js +1 -3
- package/dist/llm/usage-telemetry.js +1 -3
- package/dist/registry/create-provider-registry.js +4 -0
- package/dist/registry/factory.js +4 -0
- package/dist/registry/resolve.js +23 -8
- package/dist/runtime.js +0 -12
- package/dist/scripts/akm-migrate-node.js +142 -244
- package/dist/scripts/akm-migrate.js +142 -244
- package/dist/setup/setup.js +6 -12
- package/dist/sources/providers/git-install.js +7 -2
- package/dist/sources/providers/tar-utils.js +1 -7
- package/dist/sources/snapshot-fetchers/content-extract.js +0 -17
- package/dist/storage/managed-db.js +20 -7
- package/dist/storage/repositories/events-repository.js +0 -81
- package/dist/storage/repositories/index-entries-repository.js +0 -17
- package/dist/storage/repositories/index-fts-repository.js +24 -30
- package/dist/storage/repositories/index-utility-repository.js +0 -57
- package/dist/storage/repositories/index-vec-repository.js +25 -27
- package/dist/storage/repositories/task-history-repository.js +9 -3
- package/dist/tasks/backends/launchd.js +8 -18
- package/dist/tasks/run/task-history.js +21 -31
- package/dist/tasks/scheduler-sync.js +1 -6
- package/dist/tasks/source/bounded-document.js +1 -14
- package/dist/workflows/exec/child-workflow.js +1 -1
- package/dist/workflows/exec/native-executor.js +2 -2
- package/dist/workflows/exec/step-work.js +5 -17
- package/dist/workflows/exec/worktree.js +40 -6
- package/dist/workflows/freeze/task-bindings.js +2 -4
- package/dist/workflows/ir/compile.js +3 -14
- package/dist/workflows/ir/schema-v4.js +4 -6
- package/dist/workflows/ir/schema.js +2 -5
- package/dist/workflows/parser.js +23 -26
- package/dist/workflows/source-files.js +8 -13
- package/dist/workflows/source-ir/schema.js +1 -19
- package/docs/migration/v0.9.0-troubleshooting.md +12 -2
- package/docs/reference/cli.md +2 -0
- package/docs/reference/workflow-schema.md +24 -0
- package/package.json +2 -5
- package/dist/workflows/source-ir/compare.js +0 -17
|
@@ -47,6 +47,7 @@ import fs from "node:fs";
|
|
|
47
47
|
import path from "node:path";
|
|
48
48
|
import { types as utilTypes } from "node:util";
|
|
49
49
|
import { isAlias, isMap, isScalar, isSeq, LineCounter, parseDocument } from "yaml";
|
|
50
|
+
import { wellFormedUnicode } from "../../core/common.js";
|
|
50
51
|
import { UsageError } from "../../core/errors.js";
|
|
51
52
|
import { DURATION_UNITS, parseDuration } from "../../core/time.js";
|
|
52
53
|
import { EXECUTION_MAX_TIMEOUT_MS } from "../../execution/limits.js";
|
|
@@ -67,20 +68,6 @@ export function own(value, key) {
|
|
|
67
68
|
export function utf8Bytes(value) {
|
|
68
69
|
return new TextEncoder().encode(value).byteLength;
|
|
69
70
|
}
|
|
70
|
-
export function wellFormedUnicode(value) {
|
|
71
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
72
|
-
const code = value.charCodeAt(index);
|
|
73
|
-
if (code >= 0xd800 && code <= 0xdbff) {
|
|
74
|
-
const next = value.charCodeAt(index + 1);
|
|
75
|
-
if (!(next >= 0xdc00 && next <= 0xdfff))
|
|
76
|
-
return false;
|
|
77
|
-
index += 1;
|
|
78
|
-
}
|
|
79
|
-
else if (code >= 0xdc00 && code <= 0xdfff)
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
return true;
|
|
83
|
-
}
|
|
84
71
|
/** The one per-field error funnel both grammars render through, distinguished only by `ctx.sourceLabel` (D2-N4). */
|
|
85
72
|
export function sourceError(ctx, fieldPath, detail) {
|
|
86
73
|
const dotted = fieldPath.length === 0
|
|
@@ -284,7 +284,7 @@ async function driveChildRun(input, childRow) {
|
|
|
284
284
|
* before step 6 (publication) produces `child_workflow_publish_failed`.
|
|
285
285
|
*/
|
|
286
286
|
export async function driveChildWorkflowUnit(input) {
|
|
287
|
-
const { request,
|
|
287
|
+
const { request, ctx } = input;
|
|
288
288
|
const precheck = precheckAndDeriveInvocationKey(input);
|
|
289
289
|
if (!precheck.ok) {
|
|
290
290
|
return precheck.outcome;
|
|
@@ -708,7 +708,7 @@ async function prepareAttemptWorktree(input) {
|
|
|
708
708
|
}
|
|
709
709
|
return { ok: true, request: { ...input.request, cwd: created.path }, worktreePath: created.path };
|
|
710
710
|
}
|
|
711
|
-
async function reserveJournaledDispatch(input,
|
|
711
|
+
async function reserveJournaledDispatch(input, worktreePath, startedAt) {
|
|
712
712
|
const { plan, workUnit, ctx, attemptId, inputHash } = input;
|
|
713
713
|
let durableAttempt;
|
|
714
714
|
await enqueueUnitWrite(async () => {
|
|
@@ -799,7 +799,7 @@ async function dispatchJournaledAttempt(input) {
|
|
|
799
799
|
const startedAt = new Date().toISOString();
|
|
800
800
|
let durableAttempt;
|
|
801
801
|
try {
|
|
802
|
-
durableAttempt = await reserveJournaledDispatch(input,
|
|
802
|
+
durableAttempt = await reserveJournaledDispatch(input, worktreePath, startedAt);
|
|
803
803
|
}
|
|
804
804
|
catch (err) {
|
|
805
805
|
// A failed dispatch-row insert means NOTHING dispatched (the row is the
|
|
@@ -20,7 +20,7 @@ import { UsageError } from "../../core/errors.js";
|
|
|
20
20
|
import { validateJsonSchemaSubset } from "../../core/json-schema.js";
|
|
21
21
|
import { canonicalInputJson, validateInputs } from "../../execution/input-contract.js";
|
|
22
22
|
import { withWorkflowRunsRepo, } from "../../storage/repositories/workflow-runs-repository.js";
|
|
23
|
-
import { canonicalJson
|
|
23
|
+
import { canonicalJson } from "../ir/plan-hash.js";
|
|
24
24
|
import { parseReference, resolveReferenceString, } from "../program/expressions.js";
|
|
25
25
|
import { clip, WORKFLOW_UNIT_DIAGNOSTIC_CLIP } from "../resource-limits.js";
|
|
26
26
|
import { completeWorkflowStep, isTruncatedEvidence, } from "../runtime/runs.js";
|
|
@@ -463,7 +463,7 @@ function buildExecContextEnv(args) {
|
|
|
463
463
|
function computeUnitInputHash(ctx, item) {
|
|
464
464
|
return createHash("sha256")
|
|
465
465
|
.update("akm.workflow.unit\0v7\0")
|
|
466
|
-
.update(
|
|
466
|
+
.update(canonicalJson({
|
|
467
467
|
hashVersion: 7,
|
|
468
468
|
role: "unit",
|
|
469
469
|
stepId: ctx.plan.stepId,
|
|
@@ -855,20 +855,8 @@ export function unitOutcomeFromRow(unitId, row, hasSchema) {
|
|
|
855
855
|
...(row.tokens !== null ? { tokens: row.tokens } : {}),
|
|
856
856
|
};
|
|
857
857
|
}
|
|
858
|
-
/**
|
|
859
|
-
export
|
|
860
|
-
return JSON.stringify(sortKeys(value));
|
|
861
|
-
}
|
|
862
|
-
function sortKeys(value) {
|
|
863
|
-
if (Array.isArray(value))
|
|
864
|
-
return value.map(sortKeys);
|
|
865
|
-
if (value && typeof value === "object") {
|
|
866
|
-
return Object.fromEntries(Object.entries(value)
|
|
867
|
-
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
868
|
-
.map(([k, v]) => [k, sortKeys(v)]));
|
|
869
|
-
}
|
|
870
|
-
return value;
|
|
871
|
-
}
|
|
858
|
+
/** Re-exported so existing importers (`tests/workflows/fuzz/*`) keep resolving; canonical impl lives in `../ir/plan-hash`. */
|
|
859
|
+
export { canonicalJson };
|
|
872
860
|
// ── Gate-feedback recovery (PURE) ────────────────────────────────────────────
|
|
873
861
|
//
|
|
874
862
|
// A gate rejection is journaled as `<stepId>.gate:l<loop>` with result_json
|
|
@@ -1431,7 +1419,7 @@ export async function finalizeExecutedStep(input) {
|
|
|
1431
1419
|
// fields are unchanged).
|
|
1432
1420
|
inputHash: createHash("sha256")
|
|
1433
1421
|
.update("akm.workflow.gate\0v7\0")
|
|
1434
|
-
.update(
|
|
1422
|
+
.update(canonicalJson({
|
|
1435
1423
|
hashVersion: 7,
|
|
1436
1424
|
dispatch: gateTarget,
|
|
1437
1425
|
invocation: null,
|
|
@@ -71,7 +71,22 @@ import { isProcessAlive, isWithinAsync, safeRealpathAsync } from "../../core/com
|
|
|
71
71
|
import { serializeByKey } from "../../core/concurrent.js";
|
|
72
72
|
import { runManagedSubprocess } from "../../core/subprocess.js";
|
|
73
73
|
import { warn } from "../../core/warn.js";
|
|
74
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Timeout for every `git worktree add|prune|remove|status` call this module
|
|
76
|
+
* makes. Was 30s; raised to 120s after #891 confirmed a real product gap:
|
|
77
|
+
* these calls run under {@link withRepoWorktreeLock}, a per-process,
|
|
78
|
+
* per-repository queue, so a machine also busy with OTHER git-heavy work
|
|
79
|
+
* (other akm runs, other agents' worktrees, a loaded CI runner) can genuinely
|
|
80
|
+
* push one `git worktree remove` past 30s without anything being stuck —
|
|
81
|
+
* plain CPU/fork/IO contention. At 30s that showed up as `{ removed: false,
|
|
82
|
+
* error: "... timed out after 30000ms" }` on a perfectly healthy op: a clean
|
|
83
|
+
* worktree left retained, or a create failing a unit outright, purely because
|
|
84
|
+
* the box was busy. 120s matches the same trade already made for
|
|
85
|
+
* {@link GIT_PUSH_TIMEOUT_MS} (`core/write-source.ts`,
|
|
86
|
+
* `sources/providers/git-stash.ts`) for the same class of administrative git
|
|
87
|
+
* call under load; a call that is truly hung (not just slow) is still caught.
|
|
88
|
+
*/
|
|
89
|
+
const GIT_TIMEOUT_MS = 120_000;
|
|
75
90
|
/** Directory under `os.tmpdir()` that owns every run's worktree roots. */
|
|
76
91
|
export const WORKTREES_DIR_NAME = "akm-worktrees";
|
|
77
92
|
/**
|
|
@@ -89,12 +104,10 @@ function gitExitError(args, code, stderr, stdout) {
|
|
|
89
104
|
return `git ${args.join(" ")} exited ${code}${detail ? `: ${detail}` : ""}`;
|
|
90
105
|
}
|
|
91
106
|
/**
|
|
92
|
-
* Run one git command asynchronously; `ok` = exit
|
|
93
|
-
* errors and the
|
|
94
|
-
* promise instead of blocking the event loop; repo-mutating callers must hold
|
|
95
|
-
* {@link withRepoWorktreeLock}.
|
|
107
|
+
* Run one git command asynchronously via the real `git` binary; `ok` = exit
|
|
108
|
+
* 0. Never throws (spawn errors and the timeout → ok: false).
|
|
96
109
|
*/
|
|
97
|
-
async function
|
|
110
|
+
async function realGitExecutor(cwd, args) {
|
|
98
111
|
const result = await runManagedSubprocess(["git", "-C", cwd, ...args], {
|
|
99
112
|
capture: true,
|
|
100
113
|
timeoutMs: GIT_TIMEOUT_MS,
|
|
@@ -114,6 +127,27 @@ async function git(cwd, args) {
|
|
|
114
127
|
}
|
|
115
128
|
return { ok: true, stdout: result.stdout };
|
|
116
129
|
}
|
|
130
|
+
let gitExecutor = realGitExecutor;
|
|
131
|
+
/**
|
|
132
|
+
* TEST-ONLY seam (#891): swap the executor every repo-mutating git call in
|
|
133
|
+
* this module goes through. Lets a test prove those calls genuinely
|
|
134
|
+
* interleave with other event-loop work — the property the old "count
|
|
135
|
+
* setInterval ticks during real git calls" test asserted before it was
|
|
136
|
+
* deleted for timing on the real scheduler instead of on behavior — using a
|
|
137
|
+
* fake, deterministically-timed async git in place of the real subprocess.
|
|
138
|
+
* Call with `undefined` to restore the real spawn-based executor.
|
|
139
|
+
*/
|
|
140
|
+
export function setGitExecutorForTesting(executor) {
|
|
141
|
+
gitExecutor = executor ?? realGitExecutor;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Run one git command asynchronously; `ok` = exit 0. Never throws. Async so a
|
|
145
|
+
* git lock wait parks a promise instead of blocking the event loop;
|
|
146
|
+
* repo-mutating callers must hold {@link withRepoWorktreeLock}.
|
|
147
|
+
*/
|
|
148
|
+
async function git(cwd, args) {
|
|
149
|
+
return gitExecutor(cwd, args);
|
|
150
|
+
}
|
|
117
151
|
/**
|
|
118
152
|
* Synchronous git for the two read-only probes that run before any unit is in
|
|
119
153
|
* flight ({@link isGitAvailable}, {@link assertGitWorkTree}). They take no
|
|
@@ -20,15 +20,13 @@
|
|
|
20
20
|
* full design history, including why re-binding cannot reuse the
|
|
21
21
|
* shape-driven normalizer (a code-review finding).
|
|
22
22
|
*/
|
|
23
|
+
import { isRecord } from "../../core/common.js";
|
|
23
24
|
import { UsageError } from "../../core/errors.js";
|
|
24
25
|
import { validateInputs } from "../../execution/input-contract.js";
|
|
25
26
|
import { parseReference } from "../program/expressions.js";
|
|
26
27
|
function inputBindingInvalid(message) {
|
|
27
28
|
return new UsageError(message, "INPUT_BINDING_INVALID");
|
|
28
29
|
}
|
|
29
|
-
function isPlainObject(value) {
|
|
30
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
31
|
-
}
|
|
32
30
|
function unknownBindingNameError(stepId, targetRef, name, declaredNames) {
|
|
33
31
|
return inputBindingInvalid(`Workflow step ${stepId} targets ${targetRef} with.${name}, which is not a declared input. ` +
|
|
34
32
|
`Declared inputs: ${declaredNames.length > 0 ? declaredNames.join(", ") : "(none)"}.`);
|
|
@@ -152,7 +150,7 @@ function finalizeBindings(stepId, targetRef, contract, byName) {
|
|
|
152
150
|
* reinterpreted as a literal.
|
|
153
151
|
*/
|
|
154
152
|
function normalizeOneEntry(stepId, targetRef, name, value, schema, earlierStepIds, declaredParamNames) {
|
|
155
|
-
if (!
|
|
153
|
+
if (!isRecord(value) || !Object.hasOwn(value, "from")) {
|
|
156
154
|
return Object.freeze({ kind: "literal", name, value });
|
|
157
155
|
}
|
|
158
156
|
const keys = Object.keys(value);
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
* Returns accumulated `WorkflowError`s rather than throwing. Pure and
|
|
26
26
|
* deterministic: the same document always compiles to the same plan.
|
|
27
27
|
*/
|
|
28
|
+
import { compareCodePoints } from "../../core/common.js";
|
|
28
29
|
import { formatReference, parseReference } from "../program/expressions.js";
|
|
29
30
|
import { projectExecCore } from "../program/schema.js";
|
|
30
31
|
import { sourceStepInstructions, sourceStepProgramUnit, sourceStepRef } from "../source-ir/program.js";
|
|
@@ -114,9 +115,6 @@ function sortedOutputs(outputs) {
|
|
|
114
115
|
}
|
|
115
116
|
return sorted;
|
|
116
117
|
}
|
|
117
|
-
function compareCodePoints(left, right) {
|
|
118
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
119
|
-
}
|
|
120
118
|
function compileStep(step, sequenceIndex, defaults, resolved) {
|
|
121
119
|
const gate = {
|
|
122
120
|
kind: "gate",
|
|
@@ -269,14 +267,12 @@ function checkOutputReference(name, text, check) {
|
|
|
269
267
|
* (human + JSON output, via `core/adapter/adapters/akm-lint.ts#
|
|
270
268
|
* workflowCompileWarnings`) and as `warn()` lines at `workflow run`.
|
|
271
269
|
*
|
|
272
|
-
* A. A
|
|
273
|
-
* raw results as an untyped artifact — permitted, but worth flagging.
|
|
274
|
-
* B. A `params.<name>` reference (in `map.over`/`route.input`) to an
|
|
270
|
+
* A. A `params.<name>` reference (in `map.over`/`route.input`) to an
|
|
275
271
|
* UNDECLARED param, but ONLY when the document declares a `params:`
|
|
276
272
|
* block — a likely typo. Prose can no longer carry param references at
|
|
277
273
|
* all (it is never scanned), so this warning's surface shrinks to the
|
|
278
274
|
* two whole-value fields that can legally contain one.
|
|
279
|
-
*
|
|
275
|
+
* B. `gate.max_loops` above 1 on an `exec` step. The engine judges such a
|
|
280
276
|
* step but never loops it (`exec/step-work.ts#effectiveGateMaxLoops`):
|
|
281
277
|
* a frozen argv cannot read the judge's feedback, so a second loop would
|
|
282
278
|
* only re-run the identical command — and its side effects. The declared
|
|
@@ -299,13 +295,6 @@ export function collectWorkflowWarnings(input) {
|
|
|
299
295
|
`can still fail the step.`,
|
|
300
296
|
});
|
|
301
297
|
}
|
|
302
|
-
if ((step.map || step.route === undefined) && step.output === undefined) {
|
|
303
|
-
warnings.push({
|
|
304
|
-
line: step.source.start,
|
|
305
|
-
message: `Step "${step.id}" declares no \`output:\` schema — its unit results are carried as an untyped ` +
|
|
306
|
-
`artifact (permitted). Add an \`output:\` JSON Schema to type and validate the step artifact.`,
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
298
|
if (declaredParams) {
|
|
310
299
|
const declaredList = [...declaredParams].join(", ");
|
|
311
300
|
const scan = (text, label) => {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { createHash } from "node:crypto";
|
|
12
12
|
import path from "node:path";
|
|
13
|
+
import { compareCodePoints } from "../../core/common.js";
|
|
13
14
|
import { UsageError } from "../../core/errors.js";
|
|
14
15
|
import { decodeFrozenExecutableIdentity } from "../../execution/executable-identity.js";
|
|
15
16
|
import { INPUT_NAME_PATTERN } from "../../execution/input-contract.js";
|
|
@@ -38,7 +39,7 @@ export const WORKFLOW_IR_V5_VERSION = 5;
|
|
|
38
39
|
* freeze-time `COMPOSITION_INVALID` gates live in
|
|
39
40
|
* `src/workflows/freeze/targets/child-workflow.ts` (Lane B).
|
|
40
41
|
*/
|
|
41
|
-
export function decodeWorkflowPlanV4(input,
|
|
42
|
+
export function decodeWorkflowPlanV4(input, depth = 0, budget = { embeddedBytes: 0 }) {
|
|
42
43
|
const raw = record(input, "plan");
|
|
43
44
|
if (raw.irVersion !== WORKFLOW_IR_V5_VERSION)
|
|
44
45
|
fail("irVersion must be 5");
|
|
@@ -51,7 +52,7 @@ export function decodeWorkflowPlanV4(input, hooks = {}, depth = 0, budget = { em
|
|
|
51
52
|
planExtraKeys: ["sourceReadSet", "outputs"],
|
|
52
53
|
unitExtraKeys: ["frozenTarget", "environment"],
|
|
53
54
|
gateExtraKeys: ["frozenJudge"],
|
|
54
|
-
}
|
|
55
|
+
});
|
|
55
56
|
const rawSteps = raw.steps;
|
|
56
57
|
const stepIds = new Set(rawSteps.map((rawStep) => rawStep.stepId));
|
|
57
58
|
const requiredSources = [];
|
|
@@ -232,7 +233,7 @@ function decodeChildWorkflowTarget(target, unit, depth, budget) {
|
|
|
232
233
|
fail(`unit ${unit.id} child workflow contentHash does not match its frozen dispatch`);
|
|
233
234
|
}
|
|
234
235
|
const childDepth = depth + 1;
|
|
235
|
-
const frozenPlan = decodeWorkflowPlanV4(target.frozenPlan,
|
|
236
|
+
const frozenPlan = decodeWorkflowPlanV4(target.frozenPlan, childDepth, budget);
|
|
236
237
|
const embeddedPlanJson = canonicalJsonLocal(frozenPlan);
|
|
237
238
|
const actualPlanHash = sha256(embeddedPlanJson);
|
|
238
239
|
if (actualPlanHash !== planHash) {
|
|
@@ -711,9 +712,6 @@ function digest(value, label) {
|
|
|
711
712
|
function sha256(value) {
|
|
712
713
|
return createHash("sha256").update(value).digest("hex");
|
|
713
714
|
}
|
|
714
|
-
function compareCodePoints(left, right) {
|
|
715
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
716
|
-
}
|
|
717
715
|
function canonicalJsonLocal(value) {
|
|
718
716
|
const sort = (entry) => {
|
|
719
717
|
if (Array.isArray(entry))
|
|
@@ -1,7 +1,7 @@
|
|
|
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
|
-
import { isContainedRelativePath } from "../../core/common.js";
|
|
4
|
+
import { isContainedRelativePath, isRecord } from "../../core/common.js";
|
|
5
5
|
import { UsageError } from "../../core/errors.js";
|
|
6
6
|
import { parseReference } from "../program/expressions.js";
|
|
7
7
|
import { PROGRAM_PARAM_NAME_PATTERN, PROGRAM_RETRY_REASONS, PROGRAM_STEP_ID_PATTERN } from "../program/schema.js";
|
|
@@ -13,7 +13,7 @@ export { WORKFLOW_MAX_CONCURRENCY, WORKFLOW_MAX_TIMEOUT_MS };
|
|
|
13
13
|
const MAX_LIST_ITEMS = 1024;
|
|
14
14
|
const MAX_STRING_LENGTH = 1_000_000;
|
|
15
15
|
/** Validate the current durable execution graph. */
|
|
16
|
-
export function validateWorkflowPlanStructure(input, options
|
|
16
|
+
export function validateWorkflowPlanStructure(input, options) {
|
|
17
17
|
if (!isRecord(input) || input.irVersion !== options.expectedVersion) {
|
|
18
18
|
fail(`irVersion must be ${options.expectedVersion}`);
|
|
19
19
|
}
|
|
@@ -351,9 +351,6 @@ function assertString(value, label) {
|
|
|
351
351
|
if (typeof value !== "string" || value.length === 0 || value.length > MAX_STRING_LENGTH)
|
|
352
352
|
fail(`${label} must be a non-empty bounded string`);
|
|
353
353
|
}
|
|
354
|
-
function isRecord(value) {
|
|
355
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
356
|
-
}
|
|
357
354
|
function fail(message) {
|
|
358
355
|
throw new UsageError(`Invalid frozen workflow plan: ${message}.`);
|
|
359
356
|
}
|
package/dist/workflows/parser.js
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
import { LineCounter, parseDocument } from "yaml";
|
|
34
34
|
import { parseFrontmatterBlock } from "../core/asset/frontmatter.js";
|
|
35
35
|
import { parseMarkdownToc } from "../core/asset/markdown.js";
|
|
36
|
-
import { isContainedRelativePath } from "../core/common.js";
|
|
36
|
+
import { isContainedRelativePath, isRecord } from "../core/common.js";
|
|
37
37
|
import { formatExtraParamsIssue, validateExtraParams } from "../core/extra-params.js";
|
|
38
38
|
import { checkJsonSchemaDefinition, JSON_SCHEMA_SUBSET_SUPPORTED_KEYWORDS } from "../core/json-schema.js";
|
|
39
39
|
import { parseReference } from "./program/expressions.js";
|
|
@@ -165,7 +165,7 @@ export function parseWorkflow(markdown, source) {
|
|
|
165
165
|
};
|
|
166
166
|
if (root === null || root === undefined)
|
|
167
167
|
root = {};
|
|
168
|
-
if (!
|
|
168
|
+
if (!isRecord(root)) {
|
|
169
169
|
return {
|
|
170
170
|
ok: false,
|
|
171
171
|
errors: [{ line: 2, message: `Workflow frontmatter must be a YAML mapping (key: value pairs).` }],
|
|
@@ -355,7 +355,7 @@ function checkEnvelopeFields(ctx, root, fmEndLine) {
|
|
|
355
355
|
checkActorStamp(ctx, root.verified, ["verified"], `"verified"`);
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
|
-
if (root.provenance !== undefined && !
|
|
358
|
+
if (root.provenance !== undefined && !isRecord(root.provenance)) {
|
|
359
359
|
ctx.err(["provenance"], `Workflow frontmatter "provenance" must be a mapping.`);
|
|
360
360
|
}
|
|
361
361
|
if (root.status !== undefined && (typeof root.status !== "string" || !LIFECYCLE_STATUSES.has(root.status))) {
|
|
@@ -368,7 +368,7 @@ function checkEnvelopeFields(ctx, root, fmEndLine) {
|
|
|
368
368
|
function checkActorStamp(ctx, value, path, label) {
|
|
369
369
|
if (value === undefined)
|
|
370
370
|
return;
|
|
371
|
-
if (!
|
|
371
|
+
if (!isRecord(value)) {
|
|
372
372
|
ctx.err(path, `Workflow frontmatter ${label} must be a mapping with a non-empty "by".`);
|
|
373
373
|
return;
|
|
374
374
|
}
|
|
@@ -402,7 +402,7 @@ function checkXrefs(ctx, value, fmEndLine) {
|
|
|
402
402
|
function parseParams(ctx, raw) {
|
|
403
403
|
if (raw === undefined)
|
|
404
404
|
return undefined;
|
|
405
|
-
if (!
|
|
405
|
+
if (!isRecord(raw)) {
|
|
406
406
|
ctx.err(["params"], `"params" must be a mapping of param name to a JSON Schema object (e.g. changed_files: { type: array }).`);
|
|
407
407
|
return undefined;
|
|
408
408
|
}
|
|
@@ -412,7 +412,7 @@ function parseParams(ctx, raw) {
|
|
|
412
412
|
ctx.err(["params", paramName], `Param name "${paramName}" is invalid. Use letters, digits, and underscores, starting with a letter or underscore, so "params.${paramName}" can address it.`);
|
|
413
413
|
continue;
|
|
414
414
|
}
|
|
415
|
-
if (!
|
|
415
|
+
if (!isRecord(value)) {
|
|
416
416
|
ctx.err(["params", paramName], `Param "${paramName}" must be a JSON Schema object (e.g. { type: string }).`);
|
|
417
417
|
continue;
|
|
418
418
|
}
|
|
@@ -441,7 +441,7 @@ function parseParams(ctx, raw) {
|
|
|
441
441
|
function parseOutputs(ctx, raw) {
|
|
442
442
|
if (raw === undefined)
|
|
443
443
|
return undefined;
|
|
444
|
-
if (!
|
|
444
|
+
if (!isRecord(raw)) {
|
|
445
445
|
ctx.err(["outputs"], `"outputs" must be a mapping of output name to { from, schema? } (e.g. report: { from: steps.summarize.output }).`);
|
|
446
446
|
return undefined;
|
|
447
447
|
}
|
|
@@ -453,7 +453,7 @@ function parseOutputs(ctx, raw) {
|
|
|
453
453
|
`underscore, so "steps.<child>.output.${outputName}" can address it.`);
|
|
454
454
|
continue;
|
|
455
455
|
}
|
|
456
|
-
if (!
|
|
456
|
+
if (!isRecord(value)) {
|
|
457
457
|
ctx.err(path, `Output "${outputName}" must be a mapping with "from" (and optional "schema").`);
|
|
458
458
|
continue;
|
|
459
459
|
}
|
|
@@ -474,7 +474,7 @@ function parseOutputs(ctx, raw) {
|
|
|
474
474
|
}
|
|
475
475
|
const entry = { from: value.from };
|
|
476
476
|
if (value.schema !== undefined) {
|
|
477
|
-
if (!
|
|
477
|
+
if (!isRecord(value.schema)) {
|
|
478
478
|
ctx.err([...path, "schema"], `Output "${outputName}" "schema" must be a JSON Schema object.`);
|
|
479
479
|
}
|
|
480
480
|
else {
|
|
@@ -493,7 +493,7 @@ function parseDefaults(ctx, raw) {
|
|
|
493
493
|
if (raw === undefined)
|
|
494
494
|
return undefined;
|
|
495
495
|
const path = ["defaults"];
|
|
496
|
-
if (!
|
|
496
|
+
if (!isRecord(raw)) {
|
|
497
497
|
ctx.err(path, `"defaults" must be a mapping with any of: ${DEFAULTS_KEYS.join(", ")}.`);
|
|
498
498
|
return undefined;
|
|
499
499
|
}
|
|
@@ -525,7 +525,7 @@ function parseBudget(ctx, raw) {
|
|
|
525
525
|
if (raw === undefined)
|
|
526
526
|
return undefined;
|
|
527
527
|
const path = ["budget"];
|
|
528
|
-
if (!
|
|
528
|
+
if (!isRecord(raw)) {
|
|
529
529
|
ctx.err(path, `"budget" must be a mapping with any of: ${BUDGET_KEYS.join(", ")}.`);
|
|
530
530
|
return undefined;
|
|
531
531
|
}
|
|
@@ -558,7 +558,7 @@ function parseSteps(ctx, raw) {
|
|
|
558
558
|
// (including ones that fail their own validation).
|
|
559
559
|
const idIndex = new Map();
|
|
560
560
|
raw.forEach((rawStep, index) => {
|
|
561
|
-
if (
|
|
561
|
+
if (isRecord(rawStep) && typeof rawStep.id === "string" && !idIndex.has(rawStep.id)) {
|
|
562
562
|
idIndex.set(rawStep.id, index);
|
|
563
563
|
}
|
|
564
564
|
});
|
|
@@ -567,7 +567,7 @@ function parseSteps(ctx, raw) {
|
|
|
567
567
|
const routeChecks = [];
|
|
568
568
|
raw.forEach((rawStep, index) => {
|
|
569
569
|
const path = ["steps", index];
|
|
570
|
-
if (!
|
|
570
|
+
if (!isRecord(rawStep)) {
|
|
571
571
|
ctx.err(path, `Step ${index + 1} must be a mapping with an "id".`);
|
|
572
572
|
return;
|
|
573
573
|
}
|
|
@@ -655,7 +655,7 @@ function parseSteps(ctx, raw) {
|
|
|
655
655
|
// Step blocks
|
|
656
656
|
// ---------------------------------------------------------------------------
|
|
657
657
|
function parseUnit(ctx, raw, path, stepLabel) {
|
|
658
|
-
if (!
|
|
658
|
+
if (!isRecord(raw)) {
|
|
659
659
|
ctx.err(path, `${stepLabel} "unit" must be a mapping (a dispatch-override bag).`);
|
|
660
660
|
return undefined;
|
|
661
661
|
}
|
|
@@ -733,7 +733,7 @@ function parseUnit(ctx, raw, path, stepLabel) {
|
|
|
733
733
|
* keeps that decision visible in the frontmatter diff.
|
|
734
734
|
*/
|
|
735
735
|
function parseExec(ctx, raw, path, stepLabel) {
|
|
736
|
-
if (!
|
|
736
|
+
if (!isRecord(raw)) {
|
|
737
737
|
ctx.err(path, `${stepLabel} "exec" must be a mapping with a "command" argv list.`);
|
|
738
738
|
return undefined;
|
|
739
739
|
}
|
|
@@ -832,7 +832,7 @@ function parseExecCwd(ctx, raw, path, stepLabel) {
|
|
|
832
832
|
return value;
|
|
833
833
|
}
|
|
834
834
|
function parseMap(ctx, raw, path, stepLabel) {
|
|
835
|
-
if (!
|
|
835
|
+
if (!isRecord(raw)) {
|
|
836
836
|
ctx.err(path, `${stepLabel} "map" must be a mapping with an "over" key.`);
|
|
837
837
|
return undefined;
|
|
838
838
|
}
|
|
@@ -866,7 +866,7 @@ function parseMap(ctx, raw, path, stepLabel) {
|
|
|
866
866
|
return map;
|
|
867
867
|
}
|
|
868
868
|
function parseRoute(ctx, raw, path, stepLabel, stepIndex, routeChecks) {
|
|
869
|
-
if (!
|
|
869
|
+
if (!isRecord(raw)) {
|
|
870
870
|
ctx.err(path, `${stepLabel} "route" must be a mapping with "input" and "when" keys.`);
|
|
871
871
|
return undefined;
|
|
872
872
|
}
|
|
@@ -888,7 +888,7 @@ function parseRoute(ctx, raw, path, stepLabel, stepIndex, routeChecks) {
|
|
|
888
888
|
const seenMatches = new Map();
|
|
889
889
|
raw.when.forEach((branch, i) => {
|
|
890
890
|
const branchPath = [...whenPath, i];
|
|
891
|
-
if (!
|
|
891
|
+
if (!isRecord(branch)) {
|
|
892
892
|
ctx.err(branchPath, `${stepLabel} "when[${i}]" must be a mapping: { match, step }.`);
|
|
893
893
|
return;
|
|
894
894
|
}
|
|
@@ -967,7 +967,7 @@ function parseInputs(ctx, raw, path, stepLabel) {
|
|
|
967
967
|
return out.length > 0 ? out : undefined;
|
|
968
968
|
}
|
|
969
969
|
function parseGate(ctx, raw, path, stepLabel) {
|
|
970
|
-
if (!
|
|
970
|
+
if (!isRecord(raw)) {
|
|
971
971
|
ctx.err(path, `${stepLabel} "gate" must be a mapping with any of: ${GATE_KEYS.join(", ")}.`);
|
|
972
972
|
return undefined;
|
|
973
973
|
}
|
|
@@ -1009,7 +1009,7 @@ function parseEngineName(ctx, raw, path, label) {
|
|
|
1009
1009
|
function parseRetry(ctx, raw, path, stepLabel) {
|
|
1010
1010
|
if (raw === undefined)
|
|
1011
1011
|
return undefined;
|
|
1012
|
-
if (!
|
|
1012
|
+
if (!isRecord(raw)) {
|
|
1013
1013
|
ctx.err(path, `${stepLabel} "retry" must be a mapping: { max: <n>, on: [<failure_reason>, …] }.`);
|
|
1014
1014
|
return undefined;
|
|
1015
1015
|
}
|
|
@@ -1092,7 +1092,7 @@ function parseEnumField(ctx, raw, path, label, allowed) {
|
|
|
1092
1092
|
function parseLlmOverrides(ctx, raw, path, label) {
|
|
1093
1093
|
if (raw === undefined)
|
|
1094
1094
|
return undefined;
|
|
1095
|
-
if (!
|
|
1095
|
+
if (!isRecord(raw)) {
|
|
1096
1096
|
ctx.err(path, `${label} must be a mapping of LLM invocation overrides.`);
|
|
1097
1097
|
return undefined;
|
|
1098
1098
|
}
|
|
@@ -1127,7 +1127,7 @@ function parseLlmOverrides(ctx, raw, path, label) {
|
|
|
1127
1127
|
ctx.err([...path, "supports_json_schema"], `${label}.supports_json_schema must be a boolean.`);
|
|
1128
1128
|
}
|
|
1129
1129
|
if (raw.extra_params !== undefined) {
|
|
1130
|
-
if (!
|
|
1130
|
+
if (!isRecord(raw.extra_params)) {
|
|
1131
1131
|
ctx.err([...path, "extra_params"], `${label}.extra_params must be a JSON object.`);
|
|
1132
1132
|
}
|
|
1133
1133
|
else {
|
|
@@ -1168,7 +1168,7 @@ function parseLlmOverrides(ctx, raw, path, label) {
|
|
|
1168
1168
|
function parseSchemaObject(ctx, raw, path, label) {
|
|
1169
1169
|
if (raw === undefined)
|
|
1170
1170
|
return undefined;
|
|
1171
|
-
if (!
|
|
1171
|
+
if (!isRecord(raw)) {
|
|
1172
1172
|
ctx.err(path, `${label} must be a JSON Schema object (e.g. { type: object, properties: { … } }).`);
|
|
1173
1173
|
return undefined;
|
|
1174
1174
|
}
|
|
@@ -1212,9 +1212,6 @@ function checkUnknownKeys(ctx, obj, path, allowed, label) {
|
|
|
1212
1212
|
// ---------------------------------------------------------------------------
|
|
1213
1213
|
// Utilities
|
|
1214
1214
|
// ---------------------------------------------------------------------------
|
|
1215
|
-
function isPlainRecord(value) {
|
|
1216
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1217
|
-
}
|
|
1218
1215
|
function describeError(cause) {
|
|
1219
1216
|
return cause instanceof Error ? cause.message : String(cause);
|
|
1220
1217
|
}
|
|
@@ -12,20 +12,21 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import fs from "node:fs";
|
|
14
14
|
import path from "node:path";
|
|
15
|
+
import { compareCodePoints, toPosix } from "../core/common.js";
|
|
15
16
|
import { UsageError } from "../core/errors.js";
|
|
16
17
|
import { canonicalizeWorkflowName, WORKFLOW_EXTENSIONS } from "../core/recognition-util.js";
|
|
17
18
|
export class WorkflowSourceRejectionError extends UsageError {
|
|
18
19
|
sourcePaths;
|
|
19
20
|
constructor(message, code, sourcePaths) {
|
|
20
21
|
super(message, code);
|
|
21
|
-
this.sourcePaths = [...sourcePaths].sort(
|
|
22
|
+
this.sourcePaths = [...sourcePaths].sort(compareCodePoints);
|
|
22
23
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
export class WorkflowSourceCollisionError extends WorkflowSourceRejectionError {
|
|
26
27
|
canonicalName;
|
|
27
28
|
constructor(canonicalName, sourcePaths) {
|
|
28
|
-
const sorted = [...sourcePaths].sort(
|
|
29
|
+
const sorted = [...sourcePaths].sort(compareCodePoints);
|
|
29
30
|
super(`Workflow "${canonicalName}" resolves to multiple workflow source files: ${sorted.join(", ")}. ` +
|
|
30
31
|
"A canonical workflow ref must be owned by exactly one recognized .md or .yml source; remove or rename the duplicate.", "RESOURCE_ALREADY_EXISTS", sorted);
|
|
31
32
|
this.name = "WorkflowSourceCollisionError";
|
|
@@ -36,8 +37,8 @@ export class WorkflowSourceCollisionError extends WorkflowSourceRejectionError {
|
|
|
36
37
|
export class WorkflowSourceDomainError extends WorkflowSourceRejectionError {
|
|
37
38
|
canonicalName;
|
|
38
39
|
constructor(canonicalName, sourcePaths, issues, collidingSourcePaths) {
|
|
39
|
-
const sortedPaths = [...sourcePaths].sort(
|
|
40
|
-
const sortedCollisions = [...collidingSourcePaths].sort(
|
|
40
|
+
const sortedPaths = [...sourcePaths].sort(compareCodePoints);
|
|
41
|
+
const sortedCollisions = [...collidingSourcePaths].sort(compareCodePoints);
|
|
41
42
|
const code = issues.some((issue) => issue.code === "PATH_ESCAPE_VIOLATION")
|
|
42
43
|
? "PATH_ESCAPE_VIOLATION"
|
|
43
44
|
: "WORKFLOW_SOURCE_INVALID";
|
|
@@ -169,7 +170,7 @@ export function listWorkflowSourceFiles(sourceRoot, adapterId, name) {
|
|
|
169
170
|
extensionlessStem: entry.name.slice(0, -extension.length),
|
|
170
171
|
});
|
|
171
172
|
}
|
|
172
|
-
candidates.sort((left, right) =>
|
|
173
|
+
candidates.sort((left, right) => compareCodePoints(left.relativePath, right.relativePath));
|
|
173
174
|
const { sources, issues } = inspectWorkflowSourceDomain(candidates, canonicalName, realRoot);
|
|
174
175
|
if (issues.length > 0) {
|
|
175
176
|
throw workflowSourceDomainError(adapterId, canonicalName, candidates, sources, issues);
|
|
@@ -229,9 +230,9 @@ export function resolveWorkflowSourceDomains(sourceRoot, adapterId, sourcePaths)
|
|
|
229
230
|
candidatesByName.set(canonicalName, domain);
|
|
230
231
|
}
|
|
231
232
|
const resolutions = [];
|
|
232
|
-
for (const canonicalName of [...candidatesByName.keys()].sort(
|
|
233
|
+
for (const canonicalName of [...candidatesByName.keys()].sort(compareCodePoints)) {
|
|
233
234
|
const candidates = candidatesByName.get(canonicalName) ?? [];
|
|
234
|
-
candidates.sort((left, right) =>
|
|
235
|
+
candidates.sort((left, right) => compareCodePoints(left.relativePath, right.relativePath));
|
|
235
236
|
const { sources, issues } = inspectWorkflowSourceDomain(candidates, canonicalName, realRoot);
|
|
236
237
|
const sourcePaths = candidates.map((candidate) => candidate.relativePath);
|
|
237
238
|
if (issues.length > 0) {
|
|
@@ -365,9 +366,3 @@ function isWithinResolved(candidate, root) {
|
|
|
365
366
|
const relative = path.relative(root, path.resolve(candidate));
|
|
366
367
|
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
367
368
|
}
|
|
368
|
-
function toPosix(value) {
|
|
369
|
-
return value.replaceAll("\\", "/");
|
|
370
|
-
}
|
|
371
|
-
function comparePaths(left, right) {
|
|
372
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
373
|
-
}
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { types as utilTypes } from "node:util";
|
|
12
12
|
import { bundleRefToString, parseBundleRef } from "../../core/asset/asset-ref.js";
|
|
13
|
+
import { compareCodePoints, wellFormedUnicode } from "../../core/common.js";
|
|
13
14
|
import { validateExtraParams } from "../../core/extra-params.js";
|
|
14
15
|
import { checkJsonSchemaDefinition } from "../../core/json-schema.js";
|
|
15
16
|
import { parseReference } from "../program/expressions.js";
|
|
16
17
|
import { PROGRAM_RETRY_REASONS } from "../program/schema.js";
|
|
17
18
|
import { jsonBytes, WORKFLOW_ENGINE_NAME_PATTERN, WORKFLOW_ENV_VAR_NAME_PATTERN, WORKFLOW_MAX_CONCURRENCY, WORKFLOW_MAX_ENGINE_NAME_LENGTH, WORKFLOW_MAX_EXTRA_PARAMS_BYTES, WORKFLOW_MAX_SCHEMA_BYTES, WORKFLOW_MAX_TIMEOUT_MS, } from "../resource-limits.js";
|
|
18
|
-
import { compareWorkflowSourceCodePoints } from "./compare.js";
|
|
19
19
|
import { canonicalizeWorkflowCron, canonicalizeWorkflowRun, canonicalizeWorkflowWorkingDirectory, classifyWorkflowStepUses, rejectNulInArgv, validateWorkflowBuiltinCommand, WorkflowSourceSemanticError, } from "./semantics.js";
|
|
20
20
|
export const WORKFLOW_SOURCE_IR_VERSION = 1;
|
|
21
21
|
export const WORKFLOW_SOURCE_IR_MAX_BYTES = 2 * 1024 * 1024;
|
|
@@ -269,9 +269,6 @@ function validateStep(value, jobId, index, stepIds, options) {
|
|
|
269
269
|
extensions(step.extensions, `step ${id} extensions`);
|
|
270
270
|
span(step.source, `step ${id} source`);
|
|
271
271
|
}
|
|
272
|
-
function compareCodePoints(left, right) {
|
|
273
|
-
return compareWorkflowSourceCodePoints(left, right);
|
|
274
|
-
}
|
|
275
272
|
function validateExec(value, location, options) {
|
|
276
273
|
if (value === undefined)
|
|
277
274
|
return;
|
|
@@ -692,21 +689,6 @@ function countJsonBytes(state, token) {
|
|
|
692
689
|
if (state.bytes > WORKFLOW_SOURCE_IR_MAX_BYTES)
|
|
693
690
|
fail("source IR exceeds the byte limit");
|
|
694
691
|
}
|
|
695
|
-
function wellFormedUnicode(value) {
|
|
696
|
-
for (let index = 0; index < value.length; index++) {
|
|
697
|
-
const code = value.charCodeAt(index);
|
|
698
|
-
if (code >= 0xd800 && code <= 0xdbff) {
|
|
699
|
-
const next = value.charCodeAt(index + 1);
|
|
700
|
-
if (!(next >= 0xdc00 && next <= 0xdfff))
|
|
701
|
-
return false;
|
|
702
|
-
index++;
|
|
703
|
-
}
|
|
704
|
-
else if (code >= 0xdc00 && code <= 0xdfff) {
|
|
705
|
-
return false;
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
return true;
|
|
709
|
-
}
|
|
710
692
|
function record(value, location) {
|
|
711
693
|
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
712
694
|
fail(`${location} must be an object`);
|