@runcofactor/cli 0.10.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/dist/{api-catalog-GFGCLVS4.js → api-catalog-7HLEN7C2.js} +5 -4
  2. package/dist/{api-catalog-GFGCLVS4.js.map → api-catalog-7HLEN7C2.js.map} +1 -1
  3. package/dist/{attachment-SJC7D7FY.js → attachment-OVNQD6Z2.js} +5 -4
  4. package/dist/{attachment-SJC7D7FY.js.map → attachment-OVNQD6Z2.js.map} +1 -1
  5. package/dist/{auth-EN6NQCJY.js → auth-VUHL74FH.js} +3 -2
  6. package/dist/{chunk-IMPZAX6L.js → chunk-5GD47G2D.js} +37 -1
  7. package/dist/{chunk-IMPZAX6L.js.map → chunk-5GD47G2D.js.map} +1 -1
  8. package/dist/{chunk-WOHL4FTB.js → chunk-CQE46HSM.js} +5 -5
  9. package/dist/chunk-CQE46HSM.js.map +1 -0
  10. package/dist/{chunk-52IO7ACJ.js → chunk-CRHPDPRS.js} +39 -3
  11. package/dist/{chunk-52IO7ACJ.js.map → chunk-CRHPDPRS.js.map} +1 -1
  12. package/dist/{chunk-XQXVG7RA.js → chunk-EEN2TZUQ.js} +3 -3
  13. package/dist/{chunk-I4QLI3JO.js → chunk-EJ4U242G.js} +2 -2
  14. package/dist/{chunk-CLAWT7BJ.js → chunk-ENDHMYEJ.js} +3 -3
  15. package/dist/chunk-G4UYGRZR.js +2 -0
  16. package/dist/chunk-JZMJUPZP.js +92 -0
  17. package/dist/chunk-JZMJUPZP.js.map +1 -0
  18. package/dist/{chunk-GKY2KXO5.js → chunk-NL7CVG56.js} +105 -35
  19. package/dist/chunk-NL7CVG56.js.map +1 -0
  20. package/dist/{chunk-A4OFTUVV.js → chunk-PZ4LCN6K.js} +7 -4
  21. package/dist/chunk-PZ4LCN6K.js.map +1 -0
  22. package/dist/{chunk-SJJW77WN.js → chunk-QGI4UCJX.js} +69 -5
  23. package/dist/chunk-QGI4UCJX.js.map +1 -0
  24. package/dist/{chunk-4PNOV7OV.js → chunk-QQVWGWPI.js} +1023 -150
  25. package/dist/chunk-QQVWGWPI.js.map +1 -0
  26. package/dist/{chunk-N7HFALX3.js → chunk-VGTKUOYS.js} +2 -2
  27. package/dist/{chunk-B2VISN2V.js → chunk-YN763NWQ.js} +230 -66
  28. package/dist/chunk-YN763NWQ.js.map +1 -0
  29. package/dist/{chunk-QFDOWABX.js → chunk-Z5LUTUDN.js} +6 -3
  30. package/dist/chunk-Z5LUTUDN.js.map +1 -0
  31. package/dist/{chunk-Q4BO4Z5V.js → chunk-ZTOE3ND3.js} +4 -4
  32. package/dist/{credential-COLTBV6D.js → credential-LO2SEEHU.js} +24 -20
  33. package/dist/credential-LO2SEEHU.js.map +1 -0
  34. package/dist/{credential-template-EEPYFG7R.js → credential-template-ZN7W7734.js} +30 -28
  35. package/dist/credential-template-ZN7W7734.js.map +1 -0
  36. package/dist/{debug-workflow-KOVH5GOP.js → debug-workflow-R3RYYAXK.js} +5 -4
  37. package/dist/{debug-workflow-KOVH5GOP.js.map → debug-workflow-R3RYYAXK.js.map} +1 -1
  38. package/dist/{docs-3SSORHB5.js → docs-DIAFBDXQ.js} +5 -4
  39. package/dist/{docs-3SSORHB5.js.map → docs-DIAFBDXQ.js.map} +1 -1
  40. package/dist/{doctor-OTSIWV6G.js → doctor-NZPUSCRE.js} +9 -5
  41. package/dist/doctor-NZPUSCRE.js.map +1 -0
  42. package/dist/environment-RQVU4P5M.js +221 -0
  43. package/dist/environment-RQVU4P5M.js.map +1 -0
  44. package/dist/{feature-flag-T2LRZME3.js → feature-flag-XG26RURR.js} +72 -10
  45. package/dist/feature-flag-XG26RURR.js.map +1 -0
  46. package/dist/index.js +57 -48
  47. package/dist/index.js.map +1 -1
  48. package/dist/{init-TGWRECT2.js → init-EDNLKGPT.js} +5 -4
  49. package/dist/{init-TGWRECT2.js.map → init-EDNLKGPT.js.map} +1 -1
  50. package/dist/{install-DXSNWZKO.js → install-FYZ3HNZK.js} +7 -7
  51. package/dist/{issues-XIZOPX6T.js → issues-NNRVTJ4L.js} +5 -4
  52. package/dist/{issues-XIZOPX6T.js.map → issues-NNRVTJ4L.js.map} +1 -1
  53. package/dist/{knobs-NBINZIUB.js → knobs-A2BOBH22.js} +5 -4
  54. package/dist/{knobs-NBINZIUB.js.map → knobs-A2BOBH22.js.map} +1 -1
  55. package/dist/{list-3TDHK637.js → list-F33VZ4BJ.js} +5 -4
  56. package/dist/{list-3TDHK637.js.map → list-F33VZ4BJ.js.map} +1 -1
  57. package/dist/{local-HICVD6PU.js → local-DQDTM3XA.js} +7 -6
  58. package/dist/{local-HICVD6PU.js.map → local-DQDTM3XA.js.map} +1 -1
  59. package/dist/{login-UOIUXVUK.js → login-BUMVIB36.js} +5 -4
  60. package/dist/{login-UOIUXVUK.js.map → login-BUMVIB36.js.map} +1 -1
  61. package/dist/{mcp-54IJEOUA.js → mcp-B5Y5CDMQ.js} +23 -24
  62. package/dist/mcp-B5Y5CDMQ.js.map +1 -0
  63. package/dist/{property-UXLOLPTD.js → property-NJ4SRXQG.js} +76 -4
  64. package/dist/property-NJ4SRXQG.js.map +1 -0
  65. package/dist/{psql-BYUUNLGG.js → psql-VBZFCMNR.js} +3 -2
  66. package/dist/{psql-BYUUNLGG.js.map → psql-VBZFCMNR.js.map} +1 -1
  67. package/dist/{record-4TR2UIIS.js → record-WAO36O7G.js} +16 -17
  68. package/dist/record-WAO36O7G.js.map +1 -0
  69. package/dist/{redis-35VBUYW7.js → redis-DHZXHL4S.js} +3 -2
  70. package/dist/{redis-35VBUYW7.js.map → redis-DHZXHL4S.js.map} +1 -1
  71. package/dist/{release-OOLTUBUT.js → release-STJKUKTE.js} +5 -4
  72. package/dist/{release-OOLTUBUT.js.map → release-STJKUKTE.js.map} +1 -1
  73. package/dist/{sandbox-XGSUU6G7.js → sandbox-DWSGLY7B.js} +9 -8
  74. package/dist/{sandbox-XGSUU6G7.js.map → sandbox-DWSGLY7B.js.map} +1 -1
  75. package/dist/{scenarios-GWLJEGHA.js → scenarios-HCIDY725.js} +5 -4
  76. package/dist/{scenarios-GWLJEGHA.js.map → scenarios-HCIDY725.js.map} +1 -1
  77. package/dist/{session-MYFNZPRP.js → session-Y5RHU54D.js} +76 -8
  78. package/dist/session-Y5RHU54D.js.map +1 -0
  79. package/dist/{skill-KM34LDMO.js → skill-23WAHUOB.js} +217 -67
  80. package/dist/skill-23WAHUOB.js.map +1 -0
  81. package/dist/skills/debug-run/SKILL.md +33 -22
  82. package/dist/skills/debug-run/history/1.2.0.md +262 -0
  83. package/dist/skills/debug-run/history/1.3.0.md +273 -0
  84. package/dist/skills/debug-run/version.json +1 -1
  85. package/dist/skills/optimize-cache/SKILL.md +206 -0
  86. package/dist/{spec-RA3CFEKG.js → spec-LM5XHOQ5.js} +54 -25
  87. package/dist/spec-LM5XHOQ5.js.map +1 -0
  88. package/dist/{src-LB4HBTJQ.js → src-22RI5VP2.js} +19 -11
  89. package/dist/src-22RI5VP2.js.map +1 -0
  90. package/dist/src-BGDRMOKC.js +5748 -0
  91. package/dist/src-BGDRMOKC.js.map +1 -0
  92. package/dist/{src-N3TPKGTY.js → src-NCJPUAE6.js} +3 -2
  93. package/dist/{src-UVDX5UP6.js → src-Y3K7UJEF.js} +4 -2
  94. package/dist/{start-PSEQSQA3.js → start-SWY55CZX.js} +5 -4
  95. package/dist/{start-PSEQSQA3.js.map → start-SWY55CZX.js.map} +1 -1
  96. package/dist/{tag-YY6YAABQ.js → tag-YSU2XXPI.js} +6 -5
  97. package/dist/{task-IQC3YR5K.js → task-NLBKLD7D.js} +5 -4
  98. package/dist/{task-IQC3YR5K.js.map → task-NLBKLD7D.js.map} +1 -1
  99. package/dist/{test-suite-DZNJFXDW.js → test-suite-QCPWZ2LS.js} +9 -6
  100. package/dist/{test-suite-DZNJFXDW.js.map → test-suite-QCPWZ2LS.js.map} +1 -1
  101. package/dist/{trace-HXIW7U2D.js → trace-A3ZY6K26.js} +6 -4
  102. package/dist/{triage-ORG45R5W.js → triage-TT4BMDBU.js} +6 -5
  103. package/dist/triage-TT4BMDBU.js.map +1 -0
  104. package/dist/{upgrade-L2VYMP2V.js → upgrade-6ZUFETKX.js} +3 -3
  105. package/dist/{workflow-XYFH5LTP.js → workflow-2CX3ZNOQ.js} +241 -237
  106. package/dist/workflow-2CX3ZNOQ.js.map +1 -0
  107. package/dist/{workflow-cache-NQJQFWMV.js → workflow-cache-GXNE6SU2.js} +140 -7
  108. package/dist/workflow-cache-GXNE6SU2.js.map +1 -0
  109. package/dist/{workflow-debug-BGD4UIXU.js → workflow-debug-PRX3DWGD.js} +8 -7
  110. package/dist/{workflow-debug-BGD4UIXU.js.map → workflow-debug-PRX3DWGD.js.map} +1 -1
  111. package/dist/{workflow-proposals-MTUDX46O.js → workflow-proposals-BW327WGL.js} +5 -4
  112. package/dist/{workflow-proposals-MTUDX46O.js.map → workflow-proposals-BW327WGL.js.map} +1 -1
  113. package/package.json +2 -1
  114. package/dist/chunk-4PNOV7OV.js.map +0 -1
  115. package/dist/chunk-A4OFTUVV.js.map +0 -1
  116. package/dist/chunk-B2VISN2V.js.map +0 -1
  117. package/dist/chunk-GKY2KXO5.js.map +0 -1
  118. package/dist/chunk-JICSYFRJ.js +0 -46
  119. package/dist/chunk-JICSYFRJ.js.map +0 -1
  120. package/dist/chunk-QFDOWABX.js.map +0 -1
  121. package/dist/chunk-SJJW77WN.js.map +0 -1
  122. package/dist/chunk-WOHL4FTB.js.map +0 -1
  123. package/dist/credential-COLTBV6D.js.map +0 -1
  124. package/dist/credential-template-EEPYFG7R.js.map +0 -1
  125. package/dist/doctor-OTSIWV6G.js.map +0 -1
  126. package/dist/feature-flag-T2LRZME3.js.map +0 -1
  127. package/dist/mcp-54IJEOUA.js.map +0 -1
  128. package/dist/property-UXLOLPTD.js.map +0 -1
  129. package/dist/record-4TR2UIIS.js.map +0 -1
  130. package/dist/session-MYFNZPRP.js.map +0 -1
  131. package/dist/skill-KM34LDMO.js.map +0 -1
  132. package/dist/spec-RA3CFEKG.js.map +0 -1
  133. package/dist/src-LB4HBTJQ.js.map +0 -1
  134. package/dist/workflow-XYFH5LTP.js.map +0 -1
  135. package/dist/workflow-cache-NQJQFWMV.js.map +0 -1
  136. /package/dist/{auth-EN6NQCJY.js.map → auth-VUHL74FH.js.map} +0 -0
  137. /package/dist/{chunk-XQXVG7RA.js.map → chunk-EEN2TZUQ.js.map} +0 -0
  138. /package/dist/{chunk-I4QLI3JO.js.map → chunk-EJ4U242G.js.map} +0 -0
  139. /package/dist/{chunk-CLAWT7BJ.js.map → chunk-ENDHMYEJ.js.map} +0 -0
  140. /package/dist/{src-N3TPKGTY.js.map → chunk-G4UYGRZR.js.map} +0 -0
  141. /package/dist/{chunk-N7HFALX3.js.map → chunk-VGTKUOYS.js.map} +0 -0
  142. /package/dist/{chunk-Q4BO4Z5V.js.map → chunk-ZTOE3ND3.js.map} +0 -0
  143. /package/dist/{install-DXSNWZKO.js.map → install-FYZ3HNZK.js.map} +0 -0
  144. /package/dist/{src-UVDX5UP6.js.map → src-NCJPUAE6.js.map} +0 -0
  145. /package/dist/{tag-YY6YAABQ.js.map → src-Y3K7UJEF.js.map} +0 -0
  146. /package/dist/{trace-HXIW7U2D.js.map → tag-YSU2XXPI.js.map} +0 -0
  147. /package/dist/{triage-ORG45R5W.js.map → trace-A3ZY6K26.js.map} +0 -0
  148. /package/dist/{upgrade-L2VYMP2V.js.map → upgrade-6ZUFETKX.js.map} +0 -0
@@ -4,22 +4,17 @@ import {
4
4
  } from "./chunk-4NRDONSC.js";
5
5
  import {
6
6
  workflowToSpec
7
- } from "./chunk-B2VISN2V.js";
7
+ } from "./chunk-YN763NWQ.js";
8
8
  import {
9
9
  applyWorkflowTagFlags
10
- } from "./chunk-XQXVG7RA.js";
10
+ } from "./chunk-EEN2TZUQ.js";
11
11
  import {
12
12
  alignSnapshotNodeIds
13
13
  } from "./chunk-JDBCZ3Z2.js";
14
14
  import {
15
15
  apiRequest,
16
16
  renderWorkflowUserError
17
- } from "./chunk-WOHL4FTB.js";
18
- import {
19
- NODE_TYPE_LABEL,
20
- USER_SETTABLE_WORKFLOW_STATUSES,
21
- normalizeNodeType
22
- } from "./chunk-SJJW77WN.js";
17
+ } from "./chunk-CQE46HSM.js";
23
18
  import {
24
19
  apiFetch,
25
20
  exitOnFailure,
@@ -30,8 +25,16 @@ import {
30
25
  hasFlag,
31
26
  resolveAppUrl,
32
27
  resolveConfig
33
- } from "./chunk-A4OFTUVV.js";
28
+ } from "./chunk-PZ4LCN6K.js";
29
+ import {
30
+ specExtension
31
+ } from "./chunk-JZMJUPZP.js";
34
32
  import "./chunk-JWHYXEA2.js";
33
+ import {
34
+ NODE_TYPE_LABEL,
35
+ USER_SETTABLE_WORKFLOW_STATUSES,
36
+ normalizeNodeType
37
+ } from "./chunk-QGI4UCJX.js";
35
38
  import "./chunk-VKVL7WBN.js";
36
39
 
37
40
  // src/workflow.ts
@@ -41,7 +44,6 @@ import process9 from "process";
41
44
  // src/workflow-create.ts
42
45
  import fs from "fs/promises";
43
46
  import process2 from "process";
44
- import { createParser } from "eventsource-parser";
45
47
  async function readFromStdin() {
46
48
  const chunks = [];
47
49
  const stdin = process2.stdin;
@@ -144,165 +146,6 @@ Available credentials:
144
146
  ${lines.join("\n")}`
145
147
  );
146
148
  }
147
- async function fetchSuiteLiveState(apiUrl, token, suiteId) {
148
- try {
149
- const res = await fetch(`${apiUrl}/workflows/test-executions/${suiteId}/live-state`, {
150
- headers: { Authorization: `Bearer ${token}` }
151
- });
152
- if (!res.ok) return null;
153
- return await res.json();
154
- } catch {
155
- return null;
156
- }
157
- }
158
- async function runAndPublish(opts) {
159
- const { apiUrl, token, workflowId, workflowName, timeoutMs } = opts;
160
- const body = {};
161
- if (opts.propertyId) body.propertyId = opts.propertyId;
162
- if (opts.environmentId) body.environmentId = opts.environmentId;
163
- const triggerUrl = `${apiUrl}/workflows/test-runs?namePattern=${encodeURIComponent(workflowName)}`;
164
- const triggerRes = await fetchOrExit(triggerUrl, {
165
- method: "POST",
166
- headers: {
167
- Authorization: `Bearer ${token}`,
168
- "Content-Type": "application/json"
169
- },
170
- token,
171
- body: JSON.stringify(body)
172
- });
173
- if (!triggerRes.ok) {
174
- const errorText = await triggerRes.text();
175
- console.error(`Failed to trigger test run: ${triggerRes.status} ${errorText}`);
176
- return false;
177
- }
178
- const triggerData = await triggerRes.json();
179
- if (!triggerData.ok || !triggerData.suiteId) {
180
- console.error(`Failed to trigger test run: ${triggerData.error ?? "Unknown error"}`);
181
- return false;
182
- }
183
- const { suiteId } = triggerData;
184
- console.log(`Test run started (suite: ${suiteId})`);
185
- console.log("Streaming results...\n");
186
- const streamUrl = `${apiUrl}/workflows/test-runs/stream?suiteId=${suiteId}`;
187
- const streamRes = await fetchOrExit(streamUrl, {
188
- headers: {
189
- Authorization: `Bearer ${token}`,
190
- Accept: "text/event-stream"
191
- },
192
- token,
193
- // SSE connections stay open; don't let the default 30s timeout cut them off.
194
- timeoutMs: 0
195
- });
196
- if (!streamRes.ok || !streamRes.body) {
197
- console.error(`Failed to connect to event stream: ${streamRes.status}`);
198
- return false;
199
- }
200
- let success = false;
201
- let hasCompleted = false;
202
- const parser = createParser({
203
- onEvent: (event) => {
204
- if (!event.data) return;
205
- try {
206
- const data = JSON.parse(event.data);
207
- if (event.event === "workflow-test") {
208
- const testEvent = data;
209
- if (testEvent.status === "success") {
210
- console.log(` \u2713 ${workflowName}`);
211
- success = true;
212
- } else if (testEvent.status === "failed") {
213
- console.log(` \u2717 ${workflowName}`);
214
- if (testEvent.errorMessage) {
215
- console.log(` Error: ${testEvent.errorMessage.slice(0, 200)}`);
216
- }
217
- success = false;
218
- } else if (testEvent.status === "running") {
219
- console.log(` \u25B6 Running ${workflowName}...`);
220
- }
221
- }
222
- if (event.event === "workflow-test-suite") {
223
- const suiteEvent = data;
224
- if (suiteEvent.status === "completed") {
225
- hasCompleted = true;
226
- }
227
- }
228
- } catch {
229
- }
230
- }
231
- });
232
- const reader = streamRes.body.getReader();
233
- const decoder = new TextDecoder();
234
- let timedOut = false;
235
- const timeoutHandle = setTimeout(() => {
236
- timedOut = true;
237
- reader.cancel().catch(() => {
238
- });
239
- }, timeoutMs);
240
- try {
241
- while (!hasCompleted) {
242
- const { done, value } = await reader.read();
243
- if (done) break;
244
- parser.feed(decoder.decode(value, { stream: true }));
245
- }
246
- } finally {
247
- clearTimeout(timeoutHandle);
248
- try {
249
- reader.releaseLock();
250
- } catch {
251
- }
252
- }
253
- if (!hasCompleted) {
254
- const reason = timedOut ? `Timed out after ${Math.round(timeoutMs / 1e3)}s waiting for test completion` : "Event stream closed before completion";
255
- console.warn(`
256
- \u26A0 ${reason}. Polling final state...`);
257
- const live = await fetchSuiteLiveState(apiUrl, token, suiteId);
258
- const exec = live?.execution;
259
- if (exec && (exec.status === "completed" || exec.status === "completed_with_errors")) {
260
- success = exec.failedWorkflows === 0;
261
- const match = live?.runs?.find((r) => r.workflowId === workflowId);
262
- if (match) {
263
- console.log(
264
- success ? ` \u2713 ${workflowName}` : ` \u2717 ${workflowName}`
265
- );
266
- if (!success && match.errorMessage) {
267
- console.log(` Error: ${match.errorMessage.slice(0, 200)}`);
268
- }
269
- }
270
- } else {
271
- console.error(
272
- `Suite ${suiteId} is still ${exec?.status ?? "unknown"} on the server. Check it in the web app.`
273
- );
274
- return false;
275
- }
276
- }
277
- if (success) {
278
- try {
279
- const publishRes = await fetchOrExit(`${apiUrl}/workflows/${workflowId}/publish`, {
280
- method: "POST",
281
- headers: {
282
- Authorization: `Bearer ${token}`,
283
- "Content-Type": "application/json"
284
- },
285
- token
286
- });
287
- if (publishRes.ok) {
288
- console.log(`
289
- \u2713 Workflow published successfully`);
290
- return true;
291
- } else {
292
- console.error(`
293
- Failed to publish workflow: ${publishRes.status}`);
294
- return false;
295
- }
296
- } catch (err) {
297
- console.error(`
298
- Failed to publish workflow: ${err}`);
299
- return false;
300
- }
301
- }
302
- console.log(`
303
- \u2717 Test failed \u2014 workflow left in draft for manual review`);
304
- return false;
305
- }
306
149
  async function readApiSequenceConfig(argv) {
307
150
  const fromFile = getArgValue(argv, "--sequence-from-file");
308
151
  const fromStdin = hasFlag(argv, "--sequence-from-stdin");
@@ -409,13 +252,10 @@ async function handleWorkflowCreate(argv, apiUrl, token) {
409
252
  await handleApiOnlyCreate(argv, apiUrl, token);
410
253
  return;
411
254
  }
412
- const runAndPublishFlag = hasFlag(argv, "--run-and-publish");
413
- const propertyArg = getArgValue(argv, "--property");
414
- const environmentArg = getArgValue(argv, "--environment");
415
- const timeoutArg = getArgValue(argv, "--publish-timeout");
416
- const timeoutSec = timeoutArg ? Number.parseInt(timeoutArg, 10) : 900;
417
- if (!Number.isFinite(timeoutSec) || timeoutSec <= 0) {
418
- console.error(`Error: --publish-timeout must be a positive integer (seconds), got "${timeoutArg}"`);
255
+ if (hasFlag(argv, "--run-and-publish")) {
256
+ console.error(
257
+ "Error: --run-and-publish was removed. Run the test (canary workflow run <id>), then\napprove it (canary workflow approve <id> --yes) and promote it (canary workflow promote <id>)."
258
+ );
419
259
  process2.exit(1);
420
260
  }
421
261
  let input;
@@ -495,21 +335,10 @@ Error applying tags:
495
335
  ${err instanceof Error ? err.message : String(err)}`);
496
336
  process2.exit(1);
497
337
  }
498
- if (runAndPublishFlag) {
499
- console.log("\nTriggering test run...");
500
- const passed = await runAndPublish({
501
- apiUrl,
502
- token,
503
- workflowId: workflow.id,
504
- workflowName: workflow.name,
505
- propertyId: propertyArg ?? input.propertyId,
506
- environmentId: environmentArg,
507
- timeoutMs: timeoutSec * 1e3
508
- });
509
- if (!passed) {
510
- process2.exit(1);
511
- }
512
- }
338
+ console.log(
339
+ `
340
+ Next: run it (canary workflow run ${workflow.id}), approve it (canary workflow approve ${workflow.id} --yes), then promote it into suites (canary workflow promote ${workflow.id}).`
341
+ );
513
342
  }
514
343
 
515
344
  // src/workflow-run.ts
@@ -1246,7 +1075,7 @@ async function handleWorkflowSync(argv, apiUrl, token) {
1246
1075
 
1247
1076
  // src/workflow-api-sequence.ts
1248
1077
  import process6 from "process";
1249
- import { createParser as createParser2 } from "eventsource-parser";
1078
+ import { createParser } from "eventsource-parser";
1250
1079
  async function handleWorkflowGenerateApiSequence(argv, apiUrl, token) {
1251
1080
  const workflowId = argv.find((a) => !a.startsWith("-"));
1252
1081
  const prompt = getArgValue(argv, "--prompt");
@@ -1331,7 +1160,7 @@ async function handleWorkflowGenerateApiSequence(argv, apiUrl, token) {
1331
1160
  let resultSummary = "";
1332
1161
  let hasCompleted = false;
1333
1162
  let exitCode = 0;
1334
- const parser = createParser2({
1163
+ const parser = createParser({
1335
1164
  onEvent: (event) => {
1336
1165
  if (!event.data || event.event !== "api-sequence-generation") return;
1337
1166
  try {
@@ -2086,7 +1915,7 @@ ACTION
2086
1915
 
2087
1916
  ASSERTION
2088
1917
  Verifies expected outcomes. Define a condition to check.
2089
- The run fails if the condition is not met.
1918
+ What happens on failure depends on the severity.
2090
1919
 
2091
1920
  Prefer inline assertions on action nodes over standalone assertion nodes.
2092
1921
  Inline assertions keep the test on track and produce cleaner failure
@@ -2098,8 +1927,11 @@ ASSERTION
2098
1927
  confirming data absence after a sequence of steps.
2099
1928
 
2100
1929
  Severity levels:
2101
- fail Critical \u2014 the test stops if this fails
2102
- warn Non-critical \u2014 logged but doesn't fail the test
1930
+ fatal The test must end now \u2014 the run halts at this step.
1931
+ Use for preconditions later steps depend on.
1932
+ fail The test may continue \u2014 the run finishes marked failed,
1933
+ but later steps still run and produce signal.
1934
+ warn The test should continue \u2014 logged as a warning, run passes.
2103
1935
 
2104
1936
  Agentic assertions: for visual/layout checks that can't be mechanically
2105
1937
  validated, enable agent evaluation. These require the AI agent during
@@ -2157,6 +1989,37 @@ API SEQUENCE
2157
1989
  browser-based seed to discover the right API calls, then convert
2158
1990
  to an API sequence for speed and reliability.
2159
1991
 
1992
+ SANDBOX ACTION
1993
+ Executes a sandbox action \u2014 a server-side script defined on the
1994
+ environment template \u2014 directly on the run environment's sandbox
1995
+ instance. No browser involved. The action's structured output is
1996
+ registered as workflow variables: each output key becomes
1997
+ $var.{key} for downstream steps.
1998
+
1999
+ Config:
2000
+ actionSlug Slug of the action (required; discover via
2001
+ list_sandbox_actions)
2002
+ params Input values keyed by the action's inputSchema
2003
+ property names; values may use $var.{name} / macros
2004
+ outputKeys Output variable keys (defaults to the action's
2005
+ outputSchema keys)
2006
+
2007
+ Output registration is allowlisted by declared intent: the action's
2008
+ outputSchema keys, else the node's outputKeys. If neither declares
2009
+ anything, NO result keys register \u2014 declare outputs explicitly for
2010
+ schema-less actions.
2011
+
2012
+ Environment constraint: the action runs on the environment's
2013
+ backing sandbox instance. On a non-sandbox environment (static
2014
+ staging/prod) the node SKIPS with a warning and registers no
2015
+ variables \u2014 so workflows containing one are effectively
2016
+ sandbox-only.
2017
+
2018
+ Seeds: a seed workflow whose body is a single sandbox action node
2019
+ is the fastest way to provision data \u2014 it runs entirely
2020
+ server-side (no browser session), and its outputs satisfy the
2021
+ seed's declared outputs like any captured value.
2022
+
2160
2023
  NOTIFICATION
2161
2024
  Sends notifications (Slack, email, etc.) at a point in the workflow.
2162
2025
 
@@ -2634,6 +2497,23 @@ TEARDOWN WORKFLOWS
2634
2497
  Teardowns are the cleanest way to handle cleanup \u2014 they keep cleanup
2635
2498
  logic separate from test logic and run automatically.
2636
2499
 
2500
+ SANDBOX-ACTION-BACKED SEEDS
2501
+
2502
+ When the property has a verified sandbox template with actions, a seed
2503
+ workflow can provision data by running a SANDBOX ACTION node instead of
2504
+ driving the UI or composing API calls. The seed's body is a single
2505
+ sandbox_action node (plus End); the action's outputs register as
2506
+ $var.{key} and satisfy the seed's declared outputs directly.
2507
+
2508
+ - Fastest seed form: runs entirely server-side, no browser session
2509
+ - Discover actions with list_sandbox_actions; outputs come from the
2510
+ action's outputSchema
2511
+ - Constraint: consuming workflows become sandbox-only \u2014 on a
2512
+ non-sandbox environment the action node skips with a warning and
2513
+ produces no variables
2514
+ - Teardown is usually unnecessary: sandbox environments are destroyed
2515
+ after the suite, taking provisioned data with them
2516
+
2637
2517
  SEED BEST PRACTICES
2638
2518
 
2639
2519
  - Use macros in all created data ($EMAIL, $RANDOM_STRING, etc.)
@@ -2643,6 +2523,8 @@ SEED BEST PRACTICES
2643
2523
  - Export only what downstream workflows need \u2014 don't over-export
2644
2524
  - Start with browser-based seeds, then use auto-conversion to generate
2645
2525
  an API sequence from a completed run
2526
+ - For sandbox-backed properties, prefer a sandbox-action-backed seed
2527
+ (see above) \u2014 server-side, fast, and reliable
2646
2528
  - Pair seeds with teardown workflows for automatic cleanup
2647
2529
  - Use suite-level setup nodes to share seed data across multiple
2648
2530
  workflows in the same test suite
@@ -2771,13 +2653,31 @@ INLINE ASSERTIONS (PREFERRED)
2771
2653
  Action: "Submit the support ticket"
2772
2654
  Inline assertions:
2773
2655
  - "Success toast appears" (severity: warn)
2774
- - "Form is no longer visible" (severity: fail)
2775
-
2776
- Severity levels:
2777
- fail Critical \u2014 the test stops if this fails.
2778
- Use for: data saved, redirect happened, error absent.
2779
- warn Non-critical \u2014 logged but doesn't fail the test.
2780
- Use for: toast appeared, animation completed.
2656
+ - "Ticket appears in the ticket list" (severity: fail)
2657
+ - "Form is no longer visible" (severity: fatal)
2658
+
2659
+ Severity levels \u2014 what a failure means for the run:
2660
+ fatal The test must end now. The run halts at this step.
2661
+ Use when continuing is pointless: login succeeded, the page
2662
+ loaded, a precondition every later step depends on.
2663
+ Example: "User is logged in and the dashboard is visible"
2664
+ fail The test may continue. The step is marked failed and the run
2665
+ finishes marked failed, but later steps still execute and
2666
+ produce signal.
2667
+ Use for independent checks: data saved, expected row present,
2668
+ error absent.
2669
+ Example: "The new record appears in the table"
2670
+ warn The test should continue. The failure is recorded as a warning
2671
+ and the run still passes.
2672
+ Use for cosmetic or non-critical checks: toast appeared,
2673
+ animation completed.
2674
+ Example: "Success toast appears"
2675
+
2676
+ Choosing a severity: ask "if this is wrong, is there any value in
2677
+ running the rest of the test?" No \u2192 fatal. "Does this being wrong mean
2678
+ the feature is broken?" Yes \u2192 fail. Otherwise \u2192 warn. When unsure,
2679
+ prefer fatal for preconditions and fail for feature claims. Always set
2680
+ a severity explicitly \u2014 don't leave it unspecified.
2781
2681
 
2782
2682
  If verifying something requires significant interaction (scrolling,
2783
2683
  navigating, expanding sections), model that as an action node with
@@ -2876,8 +2776,8 @@ var WORKFLOW_GUIDE_SECTIONS = [
2876
2776
 
2877
2777
  // src/workflow.ts
2878
2778
  var WORKFLOW_STATUS_DESCRIPTIONS = {
2879
- draft: "editable, not run by suites",
2880
- published: "active, included in regression runs"
2779
+ draft: "not yet approved \u2014 no human has signed off on the spec",
2780
+ approved: "a human verified the test checks the right thing"
2881
2781
  };
2882
2782
  var formatWorkflowStatusList = () => USER_SETTABLE_WORKFLOW_STATUSES.map(
2883
2783
  (s) => `${s} (${WORKFLOW_STATUS_DESCRIPTIONS[s]})`
@@ -3288,41 +3188,129 @@ async function handleUnarchive(argv, apiUrl, token) {
3288
3188
  console.log(`Restored workflow ${workflowId}.`);
3289
3189
  }
3290
3190
  }
3291
- async function handlePublish(argv, apiUrl, token) {
3191
+ var APPROVAL_GUIDANCE = [
3192
+ "Approving a test asserts that a HUMAN has verified it tests the right thing.",
3193
+ "",
3194
+ "If you are an agent running this command on behalf of a user:",
3195
+ " - Describe to the user what this test does and what approval means.",
3196
+ " - Confirm the user has personally verified the test is correct.",
3197
+ " - Only re-run with --yes after the user explicitly confirms.",
3198
+ "",
3199
+ "Approval does NOT put the test into suites \u2014 use `canary workflow promote` for that."
3200
+ ].join("\n");
3201
+ async function handleApprove(argv, apiUrl, token) {
3202
+ const workflowId = argv.find((a) => !a.startsWith("-"));
3203
+ if (!workflowId) {
3204
+ console.error("Error: Missing workflow ID.");
3205
+ console.error("Usage: canary workflow approve <workflowId> [--yes] [--json]");
3206
+ process9.exit(1);
3207
+ }
3208
+ const jsonOutput = hasFlag(argv, "--json");
3209
+ if (!hasFlag(argv, "--yes")) {
3210
+ console.error(APPROVAL_GUIDANCE);
3211
+ console.error(`
3212
+ Re-run with --yes to approve: canary workflow approve ${workflowId} --yes`);
3213
+ process9.exit(1);
3214
+ }
3215
+ const result = await apiRequest(
3216
+ apiUrl,
3217
+ token,
3218
+ "POST",
3219
+ `/workflows/${workflowId}/approve`,
3220
+ {}
3221
+ );
3222
+ if (result.error) {
3223
+ if (jsonOutput) {
3224
+ console.log(JSON.stringify(result, null, 2));
3225
+ } else {
3226
+ console.error(`Error: ${result.error}`);
3227
+ }
3228
+ process9.exit(1);
3229
+ }
3230
+ if (jsonOutput) {
3231
+ console.log(
3232
+ JSON.stringify({ ok: true, workflowId, workflow: result.workflow ?? null }, null, 2)
3233
+ );
3234
+ return;
3235
+ }
3236
+ console.log(`Workflow ${workflowId} approved.`);
3237
+ console.log(`Promote it into suites with: canary workflow promote ${workflowId}`);
3238
+ }
3239
+ async function handlePromote(argv, apiUrl, token, options) {
3292
3240
  const workflowId = argv.find((a) => !a.startsWith("-"));
3293
3241
  if (!workflowId) {
3294
3242
  console.error("Error: Missing workflow ID.");
3295
- console.error("Usage: canary workflow publish <workflowId> [--force] [--json]");
3243
+ console.error("Usage: canary workflow promote <workflowId> [--force] [--json]");
3296
3244
  process9.exit(1);
3297
3245
  }
3298
3246
  const force = hasFlag(argv, "--force");
3299
3247
  const jsonOutput = hasFlag(argv, "--json");
3248
+ if (options?.deprecatedAlias && !jsonOutput) {
3249
+ console.error(
3250
+ `Note: \`canary workflow ${options.deprecatedAlias}\` is deprecated \u2014 use \`canary workflow promote\`.`
3251
+ );
3252
+ }
3300
3253
  const result = await apiRequest(
3301
3254
  apiUrl,
3302
3255
  token,
3303
3256
  "POST",
3304
- `/workflows/${workflowId}/publish`,
3257
+ `/workflows/${workflowId}/promote`,
3305
3258
  force ? { force: true } : {}
3306
3259
  );
3307
3260
  if (result.error) {
3308
3261
  if (jsonOutput) {
3309
3262
  console.log(JSON.stringify(result, null, 2));
3310
3263
  } else {
3311
- const hint = result.code === "FLOW_NOT_READY_TO_PUBLISH" ? "\n Trigger a successful run first (canary workflow run <id>), or pass --force." : "";
3264
+ const hint = result.code === "FLOW_NOT_READY_TO_PUBLISH" ? "\n Trigger a successful run first (canary workflow run <id>), or pass --force." : result.code === "PROMOTION_REQUIRES_APPROVAL" ? `
3265
+ Approve the test first: canary workflow approve ${workflowId} --yes
3266
+ (Approval asserts a human verified the test checks the right thing \u2014 it cannot be forced.)` : "";
3312
3267
  console.error(`Error: ${result.error}${hint}`);
3313
3268
  }
3314
3269
  process9.exit(1);
3315
3270
  }
3316
- const version = result.workflow?.snapshotVersion;
3317
3271
  if (jsonOutput) {
3318
3272
  console.log(
3319
3273
  JSON.stringify({ ok: true, workflowId, workflow: result.workflow ?? null }, null, 2)
3320
3274
  );
3321
3275
  return;
3322
3276
  }
3323
- console.log(
3324
- `Workflow ${workflowId} published${version !== void 0 ? ` as snapshot v${version}` : ""}.`
3277
+ console.log(`Workflow ${workflowId} promoted \u2014 it will run when test suites run.`);
3278
+ }
3279
+ async function handlePause(argv, apiUrl, token) {
3280
+ const workflowId = argv.find((a) => !a.startsWith("-"));
3281
+ if (!workflowId) {
3282
+ console.error("Error: Missing workflow ID.");
3283
+ console.error("Usage: canary workflow pause <workflowId> [--json]");
3284
+ process9.exit(1);
3285
+ }
3286
+ const jsonOutput = hasFlag(argv, "--json");
3287
+ const result = await apiRequest(
3288
+ apiUrl,
3289
+ token,
3290
+ "POST",
3291
+ `/workflows/${workflowId}/pause`,
3292
+ {}
3325
3293
  );
3294
+ if (result.error) {
3295
+ if (jsonOutput) {
3296
+ console.log(JSON.stringify(result, null, 2));
3297
+ } else {
3298
+ console.error(`Error: ${result.error}`);
3299
+ }
3300
+ process9.exit(1);
3301
+ }
3302
+ if (jsonOutput) {
3303
+ console.log(
3304
+ JSON.stringify({ ok: true, workflowId, workflow: result.workflow ?? null }, null, 2)
3305
+ );
3306
+ return;
3307
+ }
3308
+ const promotion = result.workflow?.promotion ?? null;
3309
+ if (promotion === "paused") {
3310
+ console.log(`Workflow ${workflowId} paused \u2014 excluded from suite runs until re-promoted.`);
3311
+ } else {
3312
+ console.log(`Workflow ${workflowId} was not promoted; nothing to pause.`);
3313
+ }
3326
3314
  }
3327
3315
  function getAllArgValues2(argv, flag) {
3328
3316
  const values = [];
@@ -3455,7 +3443,10 @@ function printWorkflowHelp() {
3455
3443
  " create [options] Create workflow from JSON",
3456
3444
  " sync <workflowId> [options] Replace all nodes (same format as create)",
3457
3445
  " patch <workflowId> [options] Update workflow properties",
3458
- " publish <workflowId> [--force] [--json] Promote the current draft to a new published snapshot",
3446
+ " approve <workflowId> [--yes] [--json] Mark the test as human-verified (spec is right)",
3447
+ " promote <workflowId> [--force] [--json] Include the test in suite runs (requires approval)",
3448
+ " pause <workflowId> [--json] Exclude a promoted test from suite runs",
3449
+ " publish <workflowId> [--force] [--json] (deprecated alias for promote)",
3459
3450
  " archive <workflowId> [--yes] [--json] Soft-delete: hide from lists, skip in test suites (reversible)",
3460
3451
  " unarchive <workflowId> [--json] Restore a previously archived workflow",
3461
3452
  " run <workflowId> [options] Trigger a workflow execution",
@@ -3469,7 +3460,7 @@ function printWorkflowHelp() {
3469
3460
  "List options:",
3470
3461
  " --search <query> Search by name",
3471
3462
  " --flow-type <type> Filter: standard, setup, seed, teardown",
3472
- " --status <status> Filter: draft, published",
3463
+ " --status <status> Filter: draft, approved",
3473
3464
  " --page <n> Page number (default: 1)",
3474
3465
  " --page-size <n> Page size (default: 25)",
3475
3466
  " --json Output raw JSON",
@@ -3477,7 +3468,7 @@ function printWorkflowHelp() {
3477
3468
  "Get options:",
3478
3469
  " --format json|markdown|spec Output format (default: json)",
3479
3470
  " --json Shorthand for --format json",
3480
- " --markdown Shorthand for editable .canary.md spec output",
3471
+ ` --markdown Shorthand for editable ${specExtension} spec output`,
3481
3472
  " --output <file> Write output to file",
3482
3473
  "",
3483
3474
  "Create options:",
@@ -3489,10 +3480,6 @@ function printWorkflowHelp() {
3489
3480
  " --sequence-from-file <path> or --sequence-from-stdin",
3490
3481
  " Input shape: { steps, description?, stopOnFailure?, exports? }",
3491
3482
  " Returns: { workflowId, apiSequenceId, nodeId }",
3492
- " --run-and-publish Run test first, publish on success",
3493
- " --publish-timeout <sec> Timeout waiting for the test to finish (default: 900)",
3494
- " --property <name|id> Property for run-and-publish",
3495
- " --environment <name|id> Environment for run-and-publish",
3496
3483
  " --tags <names> Comma-separated tag names to assign (names, not IDs)",
3497
3484
  " --tag-id <ids> Comma-separated raw tag IDs (escape hatch)",
3498
3485
  "",
@@ -3521,10 +3508,11 @@ function printWorkflowHelp() {
3521
3508
  "Patch options:",
3522
3509
  " --name <name> Update workflow name",
3523
3510
  " --description <desc> Update description",
3524
- " --status <status> Set status. Valid:",
3525
- " draft \u2014 editable, not run by suites",
3526
- " published \u2014 active, included in regression runs",
3527
- " (reliability is tracked automatically by health)",
3511
+ " --status <status> Set status (approval axis). Valid:",
3512
+ " draft \u2014 not yet approved",
3513
+ " approved \u2014 a human verified the test checks the right thing",
3514
+ " (suite inclusion is the separate promote/pause axis;",
3515
+ " reliability is tracked automatically by health)",
3528
3516
  " --flow-type <type> Set type: standard, setup, seed, teardown",
3529
3517
  " --seed-outputs-json <json> Replace seedOutputsJson (array) \u2014 seed flows only",
3530
3518
  " --seed-outputs-file <path> Replace seedOutputsJson from a JSON file",
@@ -3534,15 +3522,22 @@ function printWorkflowHelp() {
3534
3522
  " --tag-id <ids> Raw tag IDs (comma-separated)",
3535
3523
  " --json Output raw JSON",
3536
3524
  "",
3537
- "Publish options:",
3538
- " --force Publish without requiring a successful run (skips the",
3539
- " ready-to-publish gate; the new snapshot has no linked run)",
3525
+ "Approve options:",
3526
+ " --yes Confirm approval (required non-interactively)",
3527
+ " --json Output raw JSON",
3528
+ "",
3529
+ " Approval asserts a human verified the test checks the right thing.",
3530
+ " Agents: describe the test to the user and confirm they personally",
3531
+ " verified it before re-running with --yes.",
3532
+ "",
3533
+ "Promote options:",
3534
+ " --force Promote without requiring a successful run (skips the",
3535
+ " run gate; the approval gate can never be forced)",
3540
3536
  " --json Output raw JSON",
3541
3537
  "",
3542
- " Publish snapshots the current workflow definition and promotes it",
3543
- " into the suite. Without --force, the workflow must have a passing",
3544
- " run (ready_to_publish=true). Different from --status=published which",
3545
- " only sets a column and does NOT create a new snapshot.",
3538
+ " Promote includes the test in suite runs. Suites always execute the",
3539
+ " live definition \u2014 edits take effect immediately. Requires the test",
3540
+ " to be approved, and (without --force) a passing run.",
3546
3541
  "",
3547
3542
  "Run options:",
3548
3543
  " --stream Follow execution until completion",
@@ -3591,7 +3586,7 @@ function printWorkflowHelp() {
3591
3586
  " Targeted cache invalidation (rows kept, isValid=false)",
3592
3587
  " cache clear <workflowId> --mode hard --yes DESTRUCTIVE: delete cache rows, strip playbookSteps,",
3593
3588
  ' null lastRunId. Same as the UI "Reset flow" button',
3594
- " cache optimize <workflowId> Enqueue a shadow-cache-writer heal run",
3589
+ " cache optimize <workflowId> [--run <runId>] Enqueue the async cache-optimizer job",
3595
3590
  "",
3596
3591
  "Common options:",
3597
3592
  " --env <env> Target environment (prod, dev, local)",
@@ -3607,7 +3602,7 @@ async function runWorkflow(argv) {
3607
3602
  return;
3608
3603
  }
3609
3604
  if (subCommand === "debug" && hasFlag(rest, "--help", "-h")) {
3610
- const { handleWorkflowDebug } = await import("./workflow-debug-BGD4UIXU.js");
3605
+ const { handleWorkflowDebug } = await import("./workflow-debug-PRX3DWGD.js");
3611
3606
  await handleWorkflowDebug(rest, "", "");
3612
3607
  return;
3613
3608
  }
@@ -3635,8 +3630,17 @@ async function runWorkflow(argv) {
3635
3630
  case "patch":
3636
3631
  await handlePatch(rest, apiUrl, token);
3637
3632
  break;
3633
+ case "approve":
3634
+ await handleApprove(rest, apiUrl, token);
3635
+ break;
3636
+ case "promote":
3637
+ await handlePromote(rest, apiUrl, token);
3638
+ break;
3639
+ case "pause":
3640
+ await handlePause(rest, apiUrl, token);
3641
+ break;
3638
3642
  case "publish":
3639
- await handlePublish(rest, apiUrl, token);
3643
+ await handlePromote(rest, apiUrl, token, { deprecatedAlias: "publish" });
3640
3644
  break;
3641
3645
  case "archive":
3642
3646
  await handleArchive(rest, apiUrl, token);
@@ -3676,7 +3680,7 @@ async function runWorkflow(argv) {
3676
3680
  await handleWorkflowSync(rest, apiUrl, token);
3677
3681
  break;
3678
3682
  case "proposals": {
3679
- const { handleWorkflowProposals } = await import("./workflow-proposals-MTUDX46O.js");
3683
+ const { handleWorkflowProposals } = await import("./workflow-proposals-BW327WGL.js");
3680
3684
  await handleWorkflowProposals(rest, apiUrl, token);
3681
3685
  break;
3682
3686
  }
@@ -3687,12 +3691,12 @@ async function runWorkflow(argv) {
3687
3691
  await handleWorkflowApiSequence(rest, apiUrl, token);
3688
3692
  break;
3689
3693
  case "cache": {
3690
- const { handleWorkflowCache } = await import("./workflow-cache-NQJQFWMV.js");
3694
+ const { handleWorkflowCache } = await import("./workflow-cache-GXNE6SU2.js");
3691
3695
  await handleWorkflowCache(rest, apiUrl, token);
3692
3696
  break;
3693
3697
  }
3694
3698
  case "debug": {
3695
- const { handleWorkflowDebug } = await import("./workflow-debug-BGD4UIXU.js");
3699
+ const { handleWorkflowDebug } = await import("./workflow-debug-PRX3DWGD.js");
3696
3700
  await handleWorkflowDebug(rest, apiUrl, token);
3697
3701
  break;
3698
3702
  }
@@ -3705,4 +3709,4 @@ async function runWorkflow(argv) {
3705
3709
  export {
3706
3710
  runWorkflow
3707
3711
  };
3708
- //# sourceMappingURL=workflow-XYFH5LTP.js.map
3712
+ //# sourceMappingURL=workflow-2CX3ZNOQ.js.map