primitive-admin 1.0.60 → 1.0.61
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/dist/bin/primitive.js +4 -0
- package/dist/bin/primitive.js.map +1 -1
- package/dist/src/commands/admins.js +22 -8
- package/dist/src/commands/admins.js.map +1 -1
- package/dist/src/commands/analytics.js +60 -0
- package/dist/src/commands/analytics.js.map +1 -1
- package/dist/src/commands/blob-buckets.js +37 -2
- package/dist/src/commands/blob-buckets.js.map +1 -1
- package/dist/src/commands/connections.d.ts +2 -0
- package/dist/src/commands/connections.js +95 -0
- package/dist/src/commands/connections.js.map +1 -0
- package/dist/src/commands/init.js +2 -2
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/sessions.d.ts +2 -0
- package/dist/src/commands/sessions.js +63 -0
- package/dist/src/commands/sessions.js.map +1 -0
- package/dist/src/commands/skill.js +2 -2
- package/dist/src/commands/skill.js.map +1 -1
- package/dist/src/commands/sync.js +120 -13
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/commands/workflows.js +56 -13
- package/dist/src/commands/workflows.js.map +1 -1
- package/dist/src/lib/api-client.d.ts +89 -0
- package/dist/src/lib/api-client.js +118 -15
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/generated-allowlist.js +1 -0
- package/dist/src/lib/generated-allowlist.js.map +1 -1
- package/dist/src/lib/skill-installer.d.ts +4 -2
- package/dist/src/lib/skill-installer.js +139 -11
- package/dist/src/lib/skill-installer.js.map +1 -1
- package/dist/src/lib/workflow-fragments.d.ts +23 -0
- package/dist/src/lib/workflow-fragments.js +229 -8
- package/dist/src/lib/workflow-fragments.js.map +1 -1
- package/dist/src/lib/workflow-payload.d.ts +1 -1
- package/dist/src/lib/workflow-payload.js +17 -0
- package/dist/src/lib/workflow-payload.js.map +1 -1
- package/dist/src/lib/workflow-toml-validator.d.ts +57 -1
- package/dist/src/lib/workflow-toml-validator.js +104 -1
- package/dist/src/lib/workflow-toml-validator.js.map +1 -1
- package/package.json +1 -1
|
@@ -14,6 +14,29 @@
|
|
|
14
14
|
* - Unique step ids validated post-expansion; collisions name both
|
|
15
15
|
* locations in the error message.
|
|
16
16
|
*
|
|
17
|
+
* Parameterized includes (#1448):
|
|
18
|
+
* - Two include forms are supported. The bare form
|
|
19
|
+
* `include = ["name", ...]` (an array of fragment-name strings) is
|
|
20
|
+
* unchanged. The parameterized form uses `[[include]]` array-of-tables:
|
|
21
|
+
*
|
|
22
|
+
* [[include]]
|
|
23
|
+
* fragment = "lifecycle-email" # required
|
|
24
|
+
* runIf = "input.type == 'x'" # optional; ANDed onto each step
|
|
25
|
+
* with = { id = "...", subject = "..." } # params, exposed as {{ params.* }}
|
|
26
|
+
*
|
|
27
|
+
* - A file uses one form or the other — mixing string and table entries in a
|
|
28
|
+
* single `include` array is an error.
|
|
29
|
+
* - `{{ params.* }}` references inside the fragment's steps are substituted
|
|
30
|
+
* at EXPAND time from the include's `with` table. A whole-string
|
|
31
|
+
* `{{ params.x }}` splices the raw typed value (number/bool/table);
|
|
32
|
+
* an embedded `{{ params.x }}` inside a larger string stringifies it.
|
|
33
|
+
* A missing param is a hard error. Only the `params.` namespace is
|
|
34
|
+
* touched — every other `{{ ... }}` template passes through to the server
|
|
35
|
+
* verbatim, and `params.` never survives into the expanded output.
|
|
36
|
+
* - The include-level `runIf` (CEL) is ANDed onto every expanded step:
|
|
37
|
+
* both present → `(<includeRunIf>) && (<stepRunIf>)`; one present → that
|
|
38
|
+
* one; neither → no runIf.
|
|
39
|
+
*
|
|
17
40
|
* Wiring: `parseTomlFile()` in `cli/src/commands/sync.ts` calls
|
|
18
41
|
* `expandWorkflowTomlData()` after parsing. All 24 push parse sites in
|
|
19
42
|
* sync.ts route through that single seam, so the expansion is uniform.
|
|
@@ -14,6 +14,29 @@
|
|
|
14
14
|
* - Unique step ids validated post-expansion; collisions name both
|
|
15
15
|
* locations in the error message.
|
|
16
16
|
*
|
|
17
|
+
* Parameterized includes (#1448):
|
|
18
|
+
* - Two include forms are supported. The bare form
|
|
19
|
+
* `include = ["name", ...]` (an array of fragment-name strings) is
|
|
20
|
+
* unchanged. The parameterized form uses `[[include]]` array-of-tables:
|
|
21
|
+
*
|
|
22
|
+
* [[include]]
|
|
23
|
+
* fragment = "lifecycle-email" # required
|
|
24
|
+
* runIf = "input.type == 'x'" # optional; ANDed onto each step
|
|
25
|
+
* with = { id = "...", subject = "..." } # params, exposed as {{ params.* }}
|
|
26
|
+
*
|
|
27
|
+
* - A file uses one form or the other — mixing string and table entries in a
|
|
28
|
+
* single `include` array is an error.
|
|
29
|
+
* - `{{ params.* }}` references inside the fragment's steps are substituted
|
|
30
|
+
* at EXPAND time from the include's `with` table. A whole-string
|
|
31
|
+
* `{{ params.x }}` splices the raw typed value (number/bool/table);
|
|
32
|
+
* an embedded `{{ params.x }}` inside a larger string stringifies it.
|
|
33
|
+
* A missing param is a hard error. Only the `params.` namespace is
|
|
34
|
+
* touched — every other `{{ ... }}` template passes through to the server
|
|
35
|
+
* verbatim, and `params.` never survives into the expanded output.
|
|
36
|
+
* - The include-level `runIf` (CEL) is ANDed onto every expanded step:
|
|
37
|
+
* both present → `(<includeRunIf>) && (<stepRunIf>)`; one present → that
|
|
38
|
+
* one; neither → no runIf.
|
|
39
|
+
*
|
|
17
40
|
* Wiring: `parseTomlFile()` in `cli/src/commands/sync.ts` calls
|
|
18
41
|
* `expandWorkflowTomlData()` after parsing. All 24 push parse sites in
|
|
19
42
|
* sync.ts route through that single seam, so the expansion is uniform.
|
|
@@ -23,6 +46,22 @@
|
|
|
23
46
|
import { existsSync, readFileSync } from "fs";
|
|
24
47
|
import { dirname, join, basename } from "path";
|
|
25
48
|
import { parseConfigToml } from "./config-toml.js";
|
|
49
|
+
/**
|
|
50
|
+
* True only for a genuine plain TOML table: a non-null, non-array object whose
|
|
51
|
+
* prototype is `Object.prototype` or `null`. This deliberately rejects the
|
|
52
|
+
* non-plain objects that a bare `typeof x === "object"` check would accept —
|
|
53
|
+
* notably smol-toml's `TomlDate` (a `Date` subclass) and any other boxed value
|
|
54
|
+
* — so a TOML date/time literal (`with = 1979-05-27T07:32:00Z`) can never slip
|
|
55
|
+
* through a guard that expects a parameter table. Used everywhere a `with` /
|
|
56
|
+
* params sub-table is required so the plain-table rule is applied consistently.
|
|
57
|
+
*/
|
|
58
|
+
function isPlainTable(value) {
|
|
59
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const proto = Object.getPrototypeOf(value);
|
|
63
|
+
return proto === Object.prototype || proto === null;
|
|
64
|
+
}
|
|
26
65
|
/**
|
|
27
66
|
* Read and expand a workflow TOML file from disk. Returns the parsed
|
|
28
67
|
* TOML with all `include` fragments spliced in. If the file has no
|
|
@@ -58,16 +97,35 @@ export function expandWorkflowTomlData(parsed, workflowPath) {
|
|
|
58
97
|
? parsed.steps
|
|
59
98
|
: [];
|
|
60
99
|
const allOriginated = [];
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
100
|
+
// Classify each entry as a bare fragment-name string or a parameterized
|
|
101
|
+
// `[[include]]` table. A single file must use one form throughout. A
|
|
102
|
+
// `[[include]]` entry must be a genuine plain table — a non-plain object
|
|
103
|
+
// (e.g. a TOML date literal parsed to a `TomlDate`) is neither a valid
|
|
104
|
+
// string entry nor a valid table and falls through to the per-entry error.
|
|
105
|
+
const isTable = (e) => isPlainTable(e);
|
|
106
|
+
const hasStringEntry = includeList.some((e) => typeof e === "string");
|
|
107
|
+
const hasTableEntry = includeList.some((e) => isTable(e));
|
|
108
|
+
if (hasStringEntry && hasTableEntry) {
|
|
109
|
+
throw new Error(`Workflow '${workflowPath}' mixes bare-string include entries and [[include]] tables in one 'include' array. Pick one form per file: either \`include = ["a", "b"]\` or repeated \`[[include]]\` tables.`);
|
|
110
|
+
}
|
|
111
|
+
includeList.forEach((entry, index) => {
|
|
112
|
+
if (typeof entry === "string") {
|
|
113
|
+
if (entry.length === 0) {
|
|
114
|
+
throw new Error(`Workflow '${workflowPath}' has an invalid include entry: ${JSON.stringify(entry)}. Each entry must be a fragment name string.`);
|
|
115
|
+
}
|
|
116
|
+
const fragmentPath = join(fragmentsDir, `${entry}.toml`);
|
|
117
|
+
const fragment = readFragmentSafely(fragmentPath, workflowPath, entry);
|
|
118
|
+
for (const step of fragment.steps) {
|
|
119
|
+
allOriginated.push({ step, origin: `fragment '${entry}'` });
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
64
122
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
allOriginated.push({ step, origin: `fragment '${fragmentName}'` });
|
|
123
|
+
if (isTable(entry)) {
|
|
124
|
+
expandParameterizedInclude(entry, index, fragmentsDir, workflowPath, allOriginated);
|
|
125
|
+
return;
|
|
69
126
|
}
|
|
70
|
-
|
|
127
|
+
throw new Error(`Workflow '${workflowPath}' has an invalid include entry: ${JSON.stringify(entry)}. Each entry must be a fragment name string or a [[include]] table.`);
|
|
128
|
+
});
|
|
71
129
|
for (const step of workflowOwnSteps) {
|
|
72
130
|
allOriginated.push({ step, origin: `workflow '${basename(workflowPath)}'` });
|
|
73
131
|
}
|
|
@@ -103,6 +161,169 @@ function readFragmentSafely(fragmentPath, workflowPath, fragmentName) {
|
|
|
103
161
|
: [];
|
|
104
162
|
return { steps };
|
|
105
163
|
}
|
|
164
|
+
// ── Parameterized include expansion (#1448) ─────────────────────────────────
|
|
165
|
+
const ALLOWED_INCLUDE_KEYS = new Set(["fragment", "runIf", "with"]);
|
|
166
|
+
// The dotted path after `params.`. Each segment is a TOML bare key, which may
|
|
167
|
+
// contain letters, digits, `_` and `-`; segments are joined by dots. Keeping
|
|
168
|
+
// the full bare-key charset means hyphenated keys like `subject-line` expand
|
|
169
|
+
// instead of leaking `{{ params.subject-line }}` to the server.
|
|
170
|
+
const PARAM_PATH = "[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*";
|
|
171
|
+
// A string that is EXACTLY one `{{ params.<path> }}` token (raw typed splice).
|
|
172
|
+
const WHOLE_PARAM_RE = new RegExp(`^\\{\\{\\s*params\\.(${PARAM_PATH})\\s*\\}\\}$`);
|
|
173
|
+
// Any embedded `{{ params.<path> }}` token (string interpolation).
|
|
174
|
+
const EMBEDDED_PARAM_RE = new RegExp(`\\{\\{\\s*params\\.(${PARAM_PATH})\\s*\\}\\}`, "g");
|
|
175
|
+
function expandParameterizedInclude(entry, index, fragmentsDir, workflowPath, allOriginated) {
|
|
176
|
+
const includeLabel = `[[include]] #${index + 1}`;
|
|
177
|
+
for (const key of Object.keys(entry)) {
|
|
178
|
+
if (!ALLOWED_INCLUDE_KEYS.has(key)) {
|
|
179
|
+
throw new Error(`Workflow '${workflowPath}' has an unknown key '${key}' in ${includeLabel}. Allowed keys are: fragment, runIf, with.`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const fragmentName = entry.fragment;
|
|
183
|
+
if (typeof fragmentName !== "string" || fragmentName.length === 0) {
|
|
184
|
+
throw new Error(`Workflow '${workflowPath}' has a ${includeLabel} missing a valid 'fragment' name (got ${JSON.stringify(fragmentName)}).`);
|
|
185
|
+
}
|
|
186
|
+
const includeRunIf = entry.runIf;
|
|
187
|
+
if (includeRunIf !== undefined && typeof includeRunIf !== "string") {
|
|
188
|
+
throw new Error(`Workflow '${workflowPath}' has a non-string 'runIf' in ${includeLabel} (fragment '${fragmentName}'). runIf must be a CEL string.`);
|
|
189
|
+
}
|
|
190
|
+
const withParams = entry.with;
|
|
191
|
+
// `with` must be a genuine plain table. `isPlainTable` rejects arrays and any
|
|
192
|
+
// non-plain object — including a TOML date/time literal (`with = 1979-…Z`),
|
|
193
|
+
// which parses to a `TomlDate` and would otherwise be accepted as a param
|
|
194
|
+
// table by a bare `typeof === "object"` check.
|
|
195
|
+
if (withParams !== undefined && !isPlainTable(withParams)) {
|
|
196
|
+
throw new Error(`Workflow '${workflowPath}' has a non-table 'with' in ${includeLabel} (fragment '${fragmentName}'). 'with' must be a table of parameters.`);
|
|
197
|
+
}
|
|
198
|
+
const params = withParams ?? {};
|
|
199
|
+
const fragmentPath = join(fragmentsDir, `${fragmentName}.toml`);
|
|
200
|
+
const fragment = readFragmentSafely(fragmentPath, workflowPath, fragmentName);
|
|
201
|
+
const ctx = {
|
|
202
|
+
fragment: fragmentName,
|
|
203
|
+
includeLabel,
|
|
204
|
+
workflowPath,
|
|
205
|
+
};
|
|
206
|
+
for (const rawStep of fragment.steps) {
|
|
207
|
+
const step = substituteParamsDeep(rawStep, params, ctx);
|
|
208
|
+
applyIncludeRunIf(step, includeRunIf, ctx);
|
|
209
|
+
allOriginated.push({
|
|
210
|
+
step,
|
|
211
|
+
origin: `fragment '${fragmentName}' (${includeLabel})`,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Recursively substitute `{{ params.* }}` references throughout a fragment
|
|
217
|
+
* step. Returns a new value; the input is not mutated. Only the `params.`
|
|
218
|
+
* namespace is touched — every other `{{ ... }}` template is preserved
|
|
219
|
+
* verbatim for the server to render.
|
|
220
|
+
*/
|
|
221
|
+
function substituteParamsDeep(node, params, ctx) {
|
|
222
|
+
if (typeof node === "string") {
|
|
223
|
+
return substituteParamsInString(node, params, ctx);
|
|
224
|
+
}
|
|
225
|
+
if (Array.isArray(node)) {
|
|
226
|
+
return node.map((el) => substituteParamsDeep(el, params, ctx));
|
|
227
|
+
}
|
|
228
|
+
if (node !== null && typeof node === "object") {
|
|
229
|
+
// Only recurse into plain tables. Non-plain objects (e.g. smol-toml's
|
|
230
|
+
// `TomlDate`, which extends `Date`) carry internal state that is not
|
|
231
|
+
// enumerable, so rebuilding them via `Object.entries` would collapse them
|
|
232
|
+
// to `{}`. TOML date/time literals can only appear as leaf param values —
|
|
233
|
+
// there is nothing to substitute inside them — so pass them through
|
|
234
|
+
// unchanged.
|
|
235
|
+
const proto = Object.getPrototypeOf(node);
|
|
236
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
237
|
+
return node;
|
|
238
|
+
}
|
|
239
|
+
// Clone into a null-prototype object so a legal TOML key that collides with
|
|
240
|
+
// a prototype member — `__proto__`, `constructor`, `prototype` — is written
|
|
241
|
+
// as an ordinary own data property instead of tripping the `__proto__`
|
|
242
|
+
// setter (which would drop the field or reparent the clone). `Object.entries`
|
|
243
|
+
// reads only own enumerable keys, so no inherited members leak in either.
|
|
244
|
+
const out = Object.create(null);
|
|
245
|
+
for (const [k, v] of Object.entries(node)) {
|
|
246
|
+
out[k] = substituteParamsDeep(v, params, ctx);
|
|
247
|
+
}
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
250
|
+
return node;
|
|
251
|
+
}
|
|
252
|
+
function substituteParamsInString(value, params, ctx) {
|
|
253
|
+
// Whole-string `{{ params.x }}` → splice the raw typed value.
|
|
254
|
+
const whole = value.match(WHOLE_PARAM_RE);
|
|
255
|
+
if (whole) {
|
|
256
|
+
return resolveParamPath(whole[1], params, ctx);
|
|
257
|
+
}
|
|
258
|
+
// Embedded `{{ params.x }}` → stringify the resolved value in place.
|
|
259
|
+
return value.replace(EMBEDDED_PARAM_RE, (_match, path) => stringifyParamValue(resolveParamPath(path, params, ctx)));
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Resolve a dotted `params.<path>` reference against the include's `with`
|
|
263
|
+
* table. A missing key (or missing sub-path) is a hard error naming the
|
|
264
|
+
* fragment, the param, and the include.
|
|
265
|
+
*/
|
|
266
|
+
function resolveParamPath(path, params, ctx) {
|
|
267
|
+
const segments = path.split(".");
|
|
268
|
+
let cursor = params;
|
|
269
|
+
for (const segment of segments) {
|
|
270
|
+
// Resolve only OWN properties at each segment. Using `in` here would walk
|
|
271
|
+
// the prototype chain, so `{{ params.constructor }}`, `{{ params.toString }}`,
|
|
272
|
+
// `{{ params.__proto__ }}`, or `{{ params.hasOwnProperty }}` would resolve
|
|
273
|
+
// an inherited `Object.prototype` member instead of hard-erroring like any
|
|
274
|
+
// other missing param. `Object.hasOwn` restricts the lookup to real,
|
|
275
|
+
// author-declared keys.
|
|
276
|
+
if (cursor === null ||
|
|
277
|
+
typeof cursor !== "object" ||
|
|
278
|
+
Array.isArray(cursor) ||
|
|
279
|
+
!Object.hasOwn(cursor, segment)) {
|
|
280
|
+
throw new Error(`Fragment '${ctx.fragment}' references '{{ params.${path} }}' but no matching key exists in the ${ctx.includeLabel} 'with' table (workflow '${ctx.workflowPath}').`);
|
|
281
|
+
}
|
|
282
|
+
cursor = cursor[segment];
|
|
283
|
+
}
|
|
284
|
+
return cursor;
|
|
285
|
+
}
|
|
286
|
+
function stringifyParamValue(value) {
|
|
287
|
+
if (value === null || value === undefined)
|
|
288
|
+
return "";
|
|
289
|
+
if (typeof value === "object")
|
|
290
|
+
return JSON.stringify(value);
|
|
291
|
+
return String(value);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* AND the include-level `runIf` onto a step's own `runIf`. Each operand is
|
|
295
|
+
* paren-wrapped because CEL's `&&` binds tighter than `||`. Mutates `step`.
|
|
296
|
+
*/
|
|
297
|
+
function applyIncludeRunIf(step, includeRunIf, ctx) {
|
|
298
|
+
// A non-string step `runIf` (e.g. `runIf = 123`, or a param that spliced a
|
|
299
|
+
// non-string value in) must hard-error, consistent with the include-level
|
|
300
|
+
// non-string `runIf` rejection. Otherwise it would fall through the
|
|
301
|
+
// string-only guard below and be silently dropped, turning a guarded step
|
|
302
|
+
// into an unconditional one.
|
|
303
|
+
if ("runIf" in step &&
|
|
304
|
+
step.runIf !== undefined &&
|
|
305
|
+
typeof step.runIf !== "string") {
|
|
306
|
+
throw new Error(`Fragment '${ctx.fragment}' produced a non-string 'runIf' (got ${JSON.stringify(step.runIf)}) in ${ctx.includeLabel} (workflow '${ctx.workflowPath}'). runIf must be a CEL string.`);
|
|
307
|
+
}
|
|
308
|
+
const include = typeof includeRunIf === "string" && includeRunIf.length > 0
|
|
309
|
+
? includeRunIf
|
|
310
|
+
: undefined;
|
|
311
|
+
const own = typeof step.runIf === "string" && step.runIf.length > 0
|
|
312
|
+
? step.runIf
|
|
313
|
+
: undefined;
|
|
314
|
+
let combined;
|
|
315
|
+
if (include && own)
|
|
316
|
+
combined = `(${include}) && (${own})`;
|
|
317
|
+
else
|
|
318
|
+
combined = include ?? own;
|
|
319
|
+
if (combined === undefined) {
|
|
320
|
+
if ("runIf" in step)
|
|
321
|
+
delete step.runIf;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
step.runIf = combined;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
106
327
|
function assertUniqueStepIds(originated, workflowPath) {
|
|
107
328
|
const seen = new Map();
|
|
108
329
|
for (const { step, origin } of originated) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-fragments.js","sourceRoot":"","sources":["../../../src/lib/workflow-fragments.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"workflow-fragments.js","sourceRoot":"","sources":["../../../src/lib/workflow-fragments.ts"],"names":[],"mappings":"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"}
|
|
@@ -73,7 +73,7 @@ export declare function parseWorkflowExpressionsToml(tomlData: any): WorkflowExp
|
|
|
73
73
|
* the recurring bug class this builder closes. `update` omits `syncCallable`
|
|
74
74
|
* (deferred second PATCH, #807); everything else is shared.
|
|
75
75
|
*/
|
|
76
|
-
export declare const WORKFLOW_PAYLOAD_FIELDS_CREATE: readonly ["name", "description", "status", "accessRule", "runAs", "capabilities", "perUserMaxRunning", "perUserMaxQueued", "perAppMaxRunning", "perAppMaxQueued", "queueTtlSeconds", "dequeueOrder", "requiresClientApply", "inputSchema", "outputSchema", "syncCallable"];
|
|
76
|
+
export declare const WORKFLOW_PAYLOAD_FIELDS_CREATE: readonly ["name", "description", "status", "accessRule", "runAs", "capabilities", "perUserMaxRunning", "perUserMaxQueued", "perAppMaxRunning", "perAppMaxQueued", "queueTtlSeconds", "dequeueOrder", "requiresClientApply", "inputSchema", "outputSchema", "lock", "syncCallable"];
|
|
77
77
|
export declare const WORKFLOW_PAYLOAD_FIELDS_UPDATE: ReadonlyArray<string>;
|
|
78
78
|
/**
|
|
79
79
|
* Build the workflow payload's `[workflow]`-derived fields from a parsed TOML
|
|
@@ -96,6 +96,7 @@ export const WORKFLOW_PAYLOAD_FIELDS_CREATE = [
|
|
|
96
96
|
"requiresClientApply",
|
|
97
97
|
"inputSchema",
|
|
98
98
|
"outputSchema",
|
|
99
|
+
"lock",
|
|
99
100
|
"syncCallable",
|
|
100
101
|
];
|
|
101
102
|
export const WORKFLOW_PAYLOAD_FIELDS_UPDATE = WORKFLOW_PAYLOAD_FIELDS_CREATE.filter((f) => f !== "syncCallable");
|
|
@@ -160,6 +161,22 @@ export function buildWorkflowPayloadFromToml(workflow, { mode }) {
|
|
|
160
161
|
: create
|
|
161
162
|
? undefined
|
|
162
163
|
: null,
|
|
164
|
+
// #1972 — declarative run-scoped lock (`[workflow.lock]`, #1518 Phase 3).
|
|
165
|
+
// A TOML-owned nested-object field, mirroring the schemas above. Presence-
|
|
166
|
+
// keyed, not truthiness-keyed: forward the value whenever the KEY is present
|
|
167
|
+
// (`!== undefined`) — the server's create/PATCH accept an object OR a JSON
|
|
168
|
+
// string, so a present-but-malformed falsy value (`lock = false`/`0`) must
|
|
169
|
+
// be forwarded so the server surfaces its validation error, rather than
|
|
170
|
+
// being silently treated as absent (dropped on create, cleared on update).
|
|
171
|
+
// Absent on create → omit (server default: no lock); absent on update →
|
|
172
|
+
// null so removing `[workflow.lock]` clears the server-side lock
|
|
173
|
+
// (config-as-code). Before this the field was dropped from both sync paths,
|
|
174
|
+
// so a pull → push cycle silently lost the lock (#1972).
|
|
175
|
+
lock: workflow.lock !== undefined
|
|
176
|
+
? workflow.lock
|
|
177
|
+
: create
|
|
178
|
+
? undefined
|
|
179
|
+
: null,
|
|
163
180
|
};
|
|
164
181
|
// syncCallable — create includes it (raw boolean pass-through); update omits
|
|
165
182
|
// it (deferred second PATCH, #807 — see module doc).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-payload.js","sourceRoot":"","sources":["../../../src/lib/workflow-payload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAIH;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,KAAU;IACtC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5D,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACxD,OAAO,KAAK,CAAC,CAAC,gDAAgD;AAChE,CAAC;AAWD;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B,CAC1C,QAAa;IAEb,MAAM,GAAG,GAAG,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC;IAChC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5E,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAA8B,CAAC,EAAE,CAAC;QAC1E,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACjD,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAChE,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG;IAC5C,MAAM;IACN,aAAa;IACb,QAAQ;IACR,YAAY;IACZ,OAAO;IACP,cAAc;IACd,mBAAmB;IACnB,kBAAkB;IAClB,kBAAkB;IAClB,iBAAiB;IACjB,iBAAiB;IACjB,cAAc;IACd,qBAAqB;IACrB,aAAa;IACb,cAAc;IACd,cAAc;CACN,CAAC;AAEX,MAAM,CAAC,MAAM,8BAA8B,GAAG,8BAA8B,CAAC,MAAM,CACjF,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,cAAc,CACH,CAAC;AAE3B;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAC1C,QAAa,EACb,EAAE,IAAI,EAAiC;IAEvC,MAAM,MAAM,GAAG,IAAI,KAAK,QAAQ,CAAC;IAEjC,MAAM,OAAO,GAAwB;QACnC,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,wEAAwE;QACxE,oEAAoE;QACpE,0EAA0E;QAC1E,gDAAgD;QAChD,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM;QAC/D,oEAAoE;QACpE,sEAAsE;QACtE,cAAc;QACd,UAAU,EAAE,MAAM;YAChB,CAAC,CAAC,QAAQ,CAAC,UAAU,IAAI,SAAS;YAClC,CAAC,CAAC,QAAQ,CAAC,UAAU,KAAK,SAAS;gBACjC,CAAC,CAAC,QAAQ,CAAC,UAAU,IAAI,IAAI;gBAC7B,CAAC,CAAC,SAAS;QACf,KAAK,EAAE,MAAM;YACX,CAAC,CAAC,QAAQ,CAAC,KAAK,IAAI,SAAS;YAC7B,CAAC,CAAC,QAAQ,CAAC,KAAK,KAAK,SAAS;gBAC5B,CAAC,CAAC,QAAQ,CAAC,KAAK,IAAI,IAAI;gBACxB,CAAC,CAAC,SAAS;QACf,uEAAuE;QACvE,wEAAwE;QACxE,4EAA4E;QAC5E,iEAAiE;QACjE,YAAY,EAAE,MAAM;YAClB,CAAC,CAAC,QAAQ,CAAC,YAAY;YACvB,CAAC,CAAC,QAAQ,CAAC,YAAY,KAAK,SAAS;gBACnC,CAAC,CAAC,QAAQ,CAAC,YAAY;gBACvB,CAAC,CAAC,SAAS;QACf,sEAAsE;QACtE,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB;QAC7C,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;QAC3C,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;QAC3C,eAAe,EAAE,QAAQ,CAAC,eAAe;QACzC,eAAe,EAAE,QAAQ,CAAC,eAAe;QACzC,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,2EAA2E;QAC3E,wEAAwE;QACxE,mBAAmB,EAAE,QAAQ,CAAC,mBAAmB;QACjD,0EAA0E;QAC1E,kEAAkE;QAClE,WAAW,EAAE,QAAQ,CAAC,WAAW;YAC/B,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC;YACrC,CAAC,CAAC,MAAM;gBACN,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,IAAI;QACV,YAAY,EAAE,QAAQ,CAAC,YAAY;YACjC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,YAAY,CAAC;YACtC,CAAC,CAAC,MAAM;gBACN,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,IAAI;
|
|
1
|
+
{"version":3,"file":"workflow-payload.js","sourceRoot":"","sources":["../../../src/lib/workflow-payload.ts"],"names":[],"mappings":"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"}
|
|
@@ -54,6 +54,9 @@ export declare const KNOWN_KINDS_FOR_DRIFT_GUARD: ReadonlySet<string>;
|
|
|
54
54
|
* errors:
|
|
55
55
|
* - `__steps_shape__`: `steps` is not an array (table or scalar).
|
|
56
56
|
* - `__step_shape__`: a step is not an object.
|
|
57
|
+
* - `__workflow_system_access_rule__`: `[workflow] runAs = "system"` with a
|
|
58
|
+
* non-empty `accessRule` — dead config the server rejects (#1447/#1258).
|
|
59
|
+
* A workflow-level error (not tied to a step): `stepIndex: -1, stepId: null`.
|
|
57
60
|
* `hint` is a short, user-facing explanation (used by
|
|
58
61
|
* `formatWorkflowTomlErrors` to render the multi-line diagnostic).
|
|
59
62
|
* `parentStepId` is set when this error came from a `collect` step's
|
|
@@ -70,14 +73,67 @@ export type WorkflowTomlError = {
|
|
|
70
73
|
hint: string;
|
|
71
74
|
parentStepId?: string | null;
|
|
72
75
|
};
|
|
76
|
+
/**
|
|
77
|
+
* #1447 workflow-identity predicate, extracted (codex review, 2026-07-15) so
|
|
78
|
+
* command sites that apply command-line metadata overrides can re-run it
|
|
79
|
+
* against the merged/effective workflow rather than only the parsed file.
|
|
80
|
+
*
|
|
81
|
+
* `runAs = "system"` + a non-empty `accessRule` is dead config: a system run
|
|
82
|
+
* can only be started by the system and never evaluates its accessRule, so any
|
|
83
|
+
* non-empty rule is inert. The server rejects this at save time (#1258,
|
|
84
|
+
* `src/workflows/definition-store.ts:271-280`); catching it locally keeps
|
|
85
|
+
* `sync push` all-or-nothing instead of failing mid-push and leaving partial
|
|
86
|
+
* server state (#1418 drift).
|
|
87
|
+
*
|
|
88
|
+
* Mirrors the server predicate byte-for-byte: returns the sentinel error only
|
|
89
|
+
* when `runAs === "system"` AND `typeof accessRule === "string"` AND
|
|
90
|
+
* `accessRule.trim() !== ""`. Default/unset/empty/whitespace-only runAs or
|
|
91
|
+
* accessRule (and a bare non-string boolean accessRule) return `null`.
|
|
92
|
+
*
|
|
93
|
+
* @param workflow The workflow-metadata object — the `[workflow]` table, or
|
|
94
|
+
* for commands that accept unwrapped root-level metadata, the root TOML
|
|
95
|
+
* object (`tomlData.workflow || tomlData`). Non-object inputs return `null`.
|
|
96
|
+
*/
|
|
97
|
+
export declare function validateWorkflowIdentity(workflow: any): WorkflowTomlError | null;
|
|
98
|
+
/** The runAs / accessRule pair the identity lint reads. */
|
|
99
|
+
export interface WorkflowIdentityFields {
|
|
100
|
+
runAs?: unknown;
|
|
101
|
+
accessRule?: unknown;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* #1447 (maintainer decision, 2026-07-21): compute the EFFECTIVE workflow
|
|
105
|
+
* identity that an `update --from-file` push will leave on the server — the
|
|
106
|
+
* merge of the persisted record with the incoming file/flags.
|
|
107
|
+
*
|
|
108
|
+
* `applyWorkflowBody` omits undefined fields, so the server retains whichever
|
|
109
|
+
* side of the runAs/accessRule pair the incoming payload doesn't set. Linting
|
|
110
|
+
* the incoming payload alone therefore misses the case where the OTHER side is
|
|
111
|
+
* already `system` / a stored rule: an existing `runAs="system"` workflow plus
|
|
112
|
+
* a file adding only an `accessRule`, or a file flipping a workflow with a
|
|
113
|
+
* stored rule to `runAs="system"`. Both pass a payload-only lint and are then
|
|
114
|
+
* rejected by the server's PATCH. Merging the two before `validateWorkflowIdentity`
|
|
115
|
+
* catches them locally at zero extra cost — the persisted record is already
|
|
116
|
+
* fetched at the call site.
|
|
117
|
+
*
|
|
118
|
+
* Presence semantics mirror the update payload builder: a field PRESENT in
|
|
119
|
+
* `incoming` (including an empty-string clear) overrides the stored value; an
|
|
120
|
+
* ABSENT field (undefined) falls back to what the server keeps.
|
|
121
|
+
*/
|
|
122
|
+
export declare function effectiveWorkflowIdentity(existing: WorkflowIdentityFields | null | undefined, incoming: WorkflowIdentityFields | null | undefined): WorkflowIdentityFields;
|
|
73
123
|
/**
|
|
74
124
|
* Walk a parsed workflow TOML document and return all unknown-field errors.
|
|
75
125
|
*
|
|
76
126
|
* @param tomlData The output of `parseConfigToml()` for a workflow TOML file.
|
|
127
|
+
* @param opts.skipWorkflowIdentity When true, suppress the #1447 workflow
|
|
128
|
+
* identity check (`runAs="system"` + non-empty `accessRule`). Callers that
|
|
129
|
+
* merge command-line overrides after parsing re-run `validateWorkflowIdentity`
|
|
130
|
+
* against the effective metadata themselves.
|
|
77
131
|
* @returns An array of error objects, one per offending field. Empty if no
|
|
78
132
|
* errors were found.
|
|
79
133
|
*/
|
|
80
|
-
export declare function validateWorkflowToml(tomlData: any
|
|
134
|
+
export declare function validateWorkflowToml(tomlData: any, opts?: {
|
|
135
|
+
skipWorkflowIdentity?: boolean;
|
|
136
|
+
}): WorkflowTomlError[];
|
|
81
137
|
/**
|
|
82
138
|
* Render a multi-line diagnostic for a list of errors. Designed for the
|
|
83
139
|
* shape called out in the issue:
|
|
@@ -57,18 +57,115 @@ export const ALLOWLISTED_FIELDS_FOR_DRIFT_GUARD = ALLOWLISTED_FIELDS;
|
|
|
57
57
|
* guard assert the generated known-kind set matches a fresh scan.
|
|
58
58
|
*/
|
|
59
59
|
export const KNOWN_KINDS_FOR_DRIFT_GUARD = KNOWN_KINDS;
|
|
60
|
+
/**
|
|
61
|
+
* #1447 workflow-identity predicate, extracted (codex review, 2026-07-15) so
|
|
62
|
+
* command sites that apply command-line metadata overrides can re-run it
|
|
63
|
+
* against the merged/effective workflow rather than only the parsed file.
|
|
64
|
+
*
|
|
65
|
+
* `runAs = "system"` + a non-empty `accessRule` is dead config: a system run
|
|
66
|
+
* can only be started by the system and never evaluates its accessRule, so any
|
|
67
|
+
* non-empty rule is inert. The server rejects this at save time (#1258,
|
|
68
|
+
* `src/workflows/definition-store.ts:271-280`); catching it locally keeps
|
|
69
|
+
* `sync push` all-or-nothing instead of failing mid-push and leaving partial
|
|
70
|
+
* server state (#1418 drift).
|
|
71
|
+
*
|
|
72
|
+
* Mirrors the server predicate byte-for-byte: returns the sentinel error only
|
|
73
|
+
* when `runAs === "system"` AND `typeof accessRule === "string"` AND
|
|
74
|
+
* `accessRule.trim() !== ""`. Default/unset/empty/whitespace-only runAs or
|
|
75
|
+
* accessRule (and a bare non-string boolean accessRule) return `null`.
|
|
76
|
+
*
|
|
77
|
+
* @param workflow The workflow-metadata object — the `[workflow]` table, or
|
|
78
|
+
* for commands that accept unwrapped root-level metadata, the root TOML
|
|
79
|
+
* object (`tomlData.workflow || tomlData`). Non-object inputs return `null`.
|
|
80
|
+
*/
|
|
81
|
+
export function validateWorkflowIdentity(workflow) {
|
|
82
|
+
if (workflow === null ||
|
|
83
|
+
typeof workflow !== "object" ||
|
|
84
|
+
Array.isArray(workflow)) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const runAs = workflow.runAs;
|
|
88
|
+
const accessRule = workflow.accessRule;
|
|
89
|
+
if (runAs === "system" &&
|
|
90
|
+
typeof accessRule === "string" &&
|
|
91
|
+
accessRule.trim() !== "") {
|
|
92
|
+
return {
|
|
93
|
+
stepIndex: -1,
|
|
94
|
+
stepId: null,
|
|
95
|
+
field: "__workflow_system_access_rule__",
|
|
96
|
+
hint: 'accessRule has no effect when runAs = "system" (system runs skip ' +
|
|
97
|
+
"accessRule; gate callers with syncCallable + caller-mode, or see #1433)",
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* #1447 (maintainer decision, 2026-07-21): compute the EFFECTIVE workflow
|
|
104
|
+
* identity that an `update --from-file` push will leave on the server — the
|
|
105
|
+
* merge of the persisted record with the incoming file/flags.
|
|
106
|
+
*
|
|
107
|
+
* `applyWorkflowBody` omits undefined fields, so the server retains whichever
|
|
108
|
+
* side of the runAs/accessRule pair the incoming payload doesn't set. Linting
|
|
109
|
+
* the incoming payload alone therefore misses the case where the OTHER side is
|
|
110
|
+
* already `system` / a stored rule: an existing `runAs="system"` workflow plus
|
|
111
|
+
* a file adding only an `accessRule`, or a file flipping a workflow with a
|
|
112
|
+
* stored rule to `runAs="system"`. Both pass a payload-only lint and are then
|
|
113
|
+
* rejected by the server's PATCH. Merging the two before `validateWorkflowIdentity`
|
|
114
|
+
* catches them locally at zero extra cost — the persisted record is already
|
|
115
|
+
* fetched at the call site.
|
|
116
|
+
*
|
|
117
|
+
* Presence semantics mirror the update payload builder: a field PRESENT in
|
|
118
|
+
* `incoming` (including an empty-string clear) overrides the stored value; an
|
|
119
|
+
* ABSENT field (undefined) falls back to what the server keeps.
|
|
120
|
+
*/
|
|
121
|
+
export function effectiveWorkflowIdentity(existing, incoming) {
|
|
122
|
+
const ex = existing ?? {};
|
|
123
|
+
const inc = incoming ?? {};
|
|
124
|
+
return {
|
|
125
|
+
runAs: inc.runAs !== undefined ? inc.runAs : ex.runAs,
|
|
126
|
+
accessRule: inc.accessRule !== undefined ? inc.accessRule : ex.accessRule,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
60
129
|
/**
|
|
61
130
|
* Walk a parsed workflow TOML document and return all unknown-field errors.
|
|
62
131
|
*
|
|
63
132
|
* @param tomlData The output of `parseConfigToml()` for a workflow TOML file.
|
|
133
|
+
* @param opts.skipWorkflowIdentity When true, suppress the #1447 workflow
|
|
134
|
+
* identity check (`runAs="system"` + non-empty `accessRule`). Callers that
|
|
135
|
+
* merge command-line overrides after parsing re-run `validateWorkflowIdentity`
|
|
136
|
+
* against the effective metadata themselves.
|
|
64
137
|
* @returns An array of error objects, one per offending field. Empty if no
|
|
65
138
|
* errors were found.
|
|
66
139
|
*/
|
|
67
|
-
export function validateWorkflowToml(tomlData) {
|
|
140
|
+
export function validateWorkflowToml(tomlData, opts) {
|
|
68
141
|
const errors = [];
|
|
69
142
|
if (tomlData === null || tomlData === undefined) {
|
|
70
143
|
return errors;
|
|
71
144
|
}
|
|
145
|
+
// #1447 — workflow-level identity lint: `runAs = "system"` + a non-empty
|
|
146
|
+
// `accessRule` is dead config. See `validateWorkflowIdentity` for the
|
|
147
|
+
// predicate. This runs BEFORE the `steps` early-returns below so a file
|
|
148
|
+
// carrying the bad combo but with missing/malformed `[[steps]]` is still
|
|
149
|
+
// flagged.
|
|
150
|
+
//
|
|
151
|
+
// Metadata shape (codex review, 2026-07-15): read the workflow table with
|
|
152
|
+
// the same `tomlData.workflow || tomlData` fallback the consuming commands
|
|
153
|
+
// use (`workflows create/update --from-file`), so an UNWRAPPED root-level
|
|
154
|
+
// TOML carrying `runAs`/`accessRule` is checked too — not only the wrapped
|
|
155
|
+
// `[workflow]` form.
|
|
156
|
+
//
|
|
157
|
+
// `skipWorkflowIdentity` (codex review, 2026-07-15): callers that apply
|
|
158
|
+
// command-line metadata overrides AFTER parsing (e.g. `workflows update
|
|
159
|
+
// --from-file` with `--access-rule`) pass this to suppress the identity
|
|
160
|
+
// check on the raw file, then re-run `validateWorkflowIdentity` against the
|
|
161
|
+
// merged/effective metadata themselves. Otherwise `--access-rule ""` could
|
|
162
|
+
// not repair an offending file, and an override-introduced accessRule would
|
|
163
|
+
// escape the lint. Structural checks (#685 misnest, etc.) still run.
|
|
164
|
+
if (!opts?.skipWorkflowIdentity) {
|
|
165
|
+
const identityError = validateWorkflowIdentity(tomlData.workflow || tomlData);
|
|
166
|
+
if (identityError)
|
|
167
|
+
errors.push(identityError);
|
|
168
|
+
}
|
|
72
169
|
const steps = tomlData.steps;
|
|
73
170
|
// No steps section at all → nothing to validate. This is fine for
|
|
74
171
|
// partial TOML files (some commands accept a metadata-only file).
|
|
@@ -291,6 +388,12 @@ export function formatWorkflowTomlErrors(filePath, errors) {
|
|
|
291
388
|
lines.push(` steps[${err.stepIndex}] is malformed: ${err.hint}`);
|
|
292
389
|
continue;
|
|
293
390
|
}
|
|
391
|
+
// #1447 — workflow-level error, not tied to a step. Render the bare hint
|
|
392
|
+
// (no `steps[N]` prefix) under the file header.
|
|
393
|
+
if (err.field === "__workflow_system_access_rule__") {
|
|
394
|
+
lines.push(` ${err.hint}`);
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
294
397
|
// For errors inside a `collect` step's nested inner step (codex review,
|
|
295
398
|
// 2026-05-15), use a `steps[N].step` slot prefix so operators see that
|
|
296
399
|
// the offending field is on the inner step, not the outer collect.
|