@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.
- package/dist/{api-catalog-GFGCLVS4.js → api-catalog-7HLEN7C2.js} +5 -4
- package/dist/{api-catalog-GFGCLVS4.js.map → api-catalog-7HLEN7C2.js.map} +1 -1
- package/dist/{attachment-SJC7D7FY.js → attachment-OVNQD6Z2.js} +5 -4
- package/dist/{attachment-SJC7D7FY.js.map → attachment-OVNQD6Z2.js.map} +1 -1
- package/dist/{auth-EN6NQCJY.js → auth-VUHL74FH.js} +3 -2
- package/dist/{chunk-IMPZAX6L.js → chunk-5GD47G2D.js} +37 -1
- package/dist/{chunk-IMPZAX6L.js.map → chunk-5GD47G2D.js.map} +1 -1
- package/dist/{chunk-WOHL4FTB.js → chunk-CQE46HSM.js} +5 -5
- package/dist/chunk-CQE46HSM.js.map +1 -0
- package/dist/{chunk-52IO7ACJ.js → chunk-CRHPDPRS.js} +39 -3
- package/dist/{chunk-52IO7ACJ.js.map → chunk-CRHPDPRS.js.map} +1 -1
- package/dist/{chunk-XQXVG7RA.js → chunk-EEN2TZUQ.js} +3 -3
- package/dist/{chunk-I4QLI3JO.js → chunk-EJ4U242G.js} +2 -2
- package/dist/{chunk-CLAWT7BJ.js → chunk-ENDHMYEJ.js} +3 -3
- package/dist/chunk-G4UYGRZR.js +2 -0
- package/dist/chunk-JZMJUPZP.js +92 -0
- package/dist/chunk-JZMJUPZP.js.map +1 -0
- package/dist/{chunk-GKY2KXO5.js → chunk-NL7CVG56.js} +105 -35
- package/dist/chunk-NL7CVG56.js.map +1 -0
- package/dist/{chunk-A4OFTUVV.js → chunk-PZ4LCN6K.js} +7 -4
- package/dist/chunk-PZ4LCN6K.js.map +1 -0
- package/dist/{chunk-SJJW77WN.js → chunk-QGI4UCJX.js} +69 -5
- package/dist/chunk-QGI4UCJX.js.map +1 -0
- package/dist/{chunk-4PNOV7OV.js → chunk-QQVWGWPI.js} +1023 -150
- package/dist/chunk-QQVWGWPI.js.map +1 -0
- package/dist/{chunk-N7HFALX3.js → chunk-VGTKUOYS.js} +2 -2
- package/dist/{chunk-B2VISN2V.js → chunk-YN763NWQ.js} +230 -66
- package/dist/chunk-YN763NWQ.js.map +1 -0
- package/dist/{chunk-QFDOWABX.js → chunk-Z5LUTUDN.js} +6 -3
- package/dist/chunk-Z5LUTUDN.js.map +1 -0
- package/dist/{chunk-Q4BO4Z5V.js → chunk-ZTOE3ND3.js} +4 -4
- package/dist/{credential-COLTBV6D.js → credential-LO2SEEHU.js} +24 -20
- package/dist/credential-LO2SEEHU.js.map +1 -0
- package/dist/{credential-template-EEPYFG7R.js → credential-template-ZN7W7734.js} +30 -28
- package/dist/credential-template-ZN7W7734.js.map +1 -0
- package/dist/{debug-workflow-KOVH5GOP.js → debug-workflow-R3RYYAXK.js} +5 -4
- package/dist/{debug-workflow-KOVH5GOP.js.map → debug-workflow-R3RYYAXK.js.map} +1 -1
- package/dist/{docs-3SSORHB5.js → docs-DIAFBDXQ.js} +5 -4
- package/dist/{docs-3SSORHB5.js.map → docs-DIAFBDXQ.js.map} +1 -1
- package/dist/{doctor-OTSIWV6G.js → doctor-NZPUSCRE.js} +9 -5
- package/dist/doctor-NZPUSCRE.js.map +1 -0
- package/dist/environment-RQVU4P5M.js +221 -0
- package/dist/environment-RQVU4P5M.js.map +1 -0
- package/dist/{feature-flag-T2LRZME3.js → feature-flag-XG26RURR.js} +72 -10
- package/dist/feature-flag-XG26RURR.js.map +1 -0
- package/dist/index.js +57 -48
- package/dist/index.js.map +1 -1
- package/dist/{init-TGWRECT2.js → init-EDNLKGPT.js} +5 -4
- package/dist/{init-TGWRECT2.js.map → init-EDNLKGPT.js.map} +1 -1
- package/dist/{install-DXSNWZKO.js → install-FYZ3HNZK.js} +7 -7
- package/dist/{issues-XIZOPX6T.js → issues-NNRVTJ4L.js} +5 -4
- package/dist/{issues-XIZOPX6T.js.map → issues-NNRVTJ4L.js.map} +1 -1
- package/dist/{knobs-NBINZIUB.js → knobs-A2BOBH22.js} +5 -4
- package/dist/{knobs-NBINZIUB.js.map → knobs-A2BOBH22.js.map} +1 -1
- package/dist/{list-3TDHK637.js → list-F33VZ4BJ.js} +5 -4
- package/dist/{list-3TDHK637.js.map → list-F33VZ4BJ.js.map} +1 -1
- package/dist/{local-HICVD6PU.js → local-DQDTM3XA.js} +7 -6
- package/dist/{local-HICVD6PU.js.map → local-DQDTM3XA.js.map} +1 -1
- package/dist/{login-UOIUXVUK.js → login-BUMVIB36.js} +5 -4
- package/dist/{login-UOIUXVUK.js.map → login-BUMVIB36.js.map} +1 -1
- package/dist/{mcp-54IJEOUA.js → mcp-B5Y5CDMQ.js} +23 -24
- package/dist/mcp-B5Y5CDMQ.js.map +1 -0
- package/dist/{property-UXLOLPTD.js → property-NJ4SRXQG.js} +76 -4
- package/dist/property-NJ4SRXQG.js.map +1 -0
- package/dist/{psql-BYUUNLGG.js → psql-VBZFCMNR.js} +3 -2
- package/dist/{psql-BYUUNLGG.js.map → psql-VBZFCMNR.js.map} +1 -1
- package/dist/{record-4TR2UIIS.js → record-WAO36O7G.js} +16 -17
- package/dist/record-WAO36O7G.js.map +1 -0
- package/dist/{redis-35VBUYW7.js → redis-DHZXHL4S.js} +3 -2
- package/dist/{redis-35VBUYW7.js.map → redis-DHZXHL4S.js.map} +1 -1
- package/dist/{release-OOLTUBUT.js → release-STJKUKTE.js} +5 -4
- package/dist/{release-OOLTUBUT.js.map → release-STJKUKTE.js.map} +1 -1
- package/dist/{sandbox-XGSUU6G7.js → sandbox-DWSGLY7B.js} +9 -8
- package/dist/{sandbox-XGSUU6G7.js.map → sandbox-DWSGLY7B.js.map} +1 -1
- package/dist/{scenarios-GWLJEGHA.js → scenarios-HCIDY725.js} +5 -4
- package/dist/{scenarios-GWLJEGHA.js.map → scenarios-HCIDY725.js.map} +1 -1
- package/dist/{session-MYFNZPRP.js → session-Y5RHU54D.js} +76 -8
- package/dist/session-Y5RHU54D.js.map +1 -0
- package/dist/{skill-KM34LDMO.js → skill-23WAHUOB.js} +217 -67
- package/dist/skill-23WAHUOB.js.map +1 -0
- package/dist/skills/debug-run/SKILL.md +33 -22
- package/dist/skills/debug-run/history/1.2.0.md +262 -0
- package/dist/skills/debug-run/history/1.3.0.md +273 -0
- package/dist/skills/debug-run/version.json +1 -1
- package/dist/skills/optimize-cache/SKILL.md +206 -0
- package/dist/{spec-RA3CFEKG.js → spec-LM5XHOQ5.js} +54 -25
- package/dist/spec-LM5XHOQ5.js.map +1 -0
- package/dist/{src-LB4HBTJQ.js → src-22RI5VP2.js} +19 -11
- package/dist/src-22RI5VP2.js.map +1 -0
- package/dist/src-BGDRMOKC.js +5748 -0
- package/dist/src-BGDRMOKC.js.map +1 -0
- package/dist/{src-N3TPKGTY.js → src-NCJPUAE6.js} +3 -2
- package/dist/{src-UVDX5UP6.js → src-Y3K7UJEF.js} +4 -2
- package/dist/{start-PSEQSQA3.js → start-SWY55CZX.js} +5 -4
- package/dist/{start-PSEQSQA3.js.map → start-SWY55CZX.js.map} +1 -1
- package/dist/{tag-YY6YAABQ.js → tag-YSU2XXPI.js} +6 -5
- package/dist/{task-IQC3YR5K.js → task-NLBKLD7D.js} +5 -4
- package/dist/{task-IQC3YR5K.js.map → task-NLBKLD7D.js.map} +1 -1
- package/dist/{test-suite-DZNJFXDW.js → test-suite-QCPWZ2LS.js} +9 -6
- package/dist/{test-suite-DZNJFXDW.js.map → test-suite-QCPWZ2LS.js.map} +1 -1
- package/dist/{trace-HXIW7U2D.js → trace-A3ZY6K26.js} +6 -4
- package/dist/{triage-ORG45R5W.js → triage-TT4BMDBU.js} +6 -5
- package/dist/triage-TT4BMDBU.js.map +1 -0
- package/dist/{upgrade-L2VYMP2V.js → upgrade-6ZUFETKX.js} +3 -3
- package/dist/{workflow-XYFH5LTP.js → workflow-2CX3ZNOQ.js} +241 -237
- package/dist/workflow-2CX3ZNOQ.js.map +1 -0
- package/dist/{workflow-cache-NQJQFWMV.js → workflow-cache-GXNE6SU2.js} +140 -7
- package/dist/workflow-cache-GXNE6SU2.js.map +1 -0
- package/dist/{workflow-debug-BGD4UIXU.js → workflow-debug-PRX3DWGD.js} +8 -7
- package/dist/{workflow-debug-BGD4UIXU.js.map → workflow-debug-PRX3DWGD.js.map} +1 -1
- package/dist/{workflow-proposals-MTUDX46O.js → workflow-proposals-BW327WGL.js} +5 -4
- package/dist/{workflow-proposals-MTUDX46O.js.map → workflow-proposals-BW327WGL.js.map} +1 -1
- package/package.json +2 -1
- package/dist/chunk-4PNOV7OV.js.map +0 -1
- package/dist/chunk-A4OFTUVV.js.map +0 -1
- package/dist/chunk-B2VISN2V.js.map +0 -1
- package/dist/chunk-GKY2KXO5.js.map +0 -1
- package/dist/chunk-JICSYFRJ.js +0 -46
- package/dist/chunk-JICSYFRJ.js.map +0 -1
- package/dist/chunk-QFDOWABX.js.map +0 -1
- package/dist/chunk-SJJW77WN.js.map +0 -1
- package/dist/chunk-WOHL4FTB.js.map +0 -1
- package/dist/credential-COLTBV6D.js.map +0 -1
- package/dist/credential-template-EEPYFG7R.js.map +0 -1
- package/dist/doctor-OTSIWV6G.js.map +0 -1
- package/dist/feature-flag-T2LRZME3.js.map +0 -1
- package/dist/mcp-54IJEOUA.js.map +0 -1
- package/dist/property-UXLOLPTD.js.map +0 -1
- package/dist/record-4TR2UIIS.js.map +0 -1
- package/dist/session-MYFNZPRP.js.map +0 -1
- package/dist/skill-KM34LDMO.js.map +0 -1
- package/dist/spec-RA3CFEKG.js.map +0 -1
- package/dist/src-LB4HBTJQ.js.map +0 -1
- package/dist/workflow-XYFH5LTP.js.map +0 -1
- package/dist/workflow-cache-NQJQFWMV.js.map +0 -1
- /package/dist/{auth-EN6NQCJY.js.map → auth-VUHL74FH.js.map} +0 -0
- /package/dist/{chunk-XQXVG7RA.js.map → chunk-EEN2TZUQ.js.map} +0 -0
- /package/dist/{chunk-I4QLI3JO.js.map → chunk-EJ4U242G.js.map} +0 -0
- /package/dist/{chunk-CLAWT7BJ.js.map → chunk-ENDHMYEJ.js.map} +0 -0
- /package/dist/{src-N3TPKGTY.js.map → chunk-G4UYGRZR.js.map} +0 -0
- /package/dist/{chunk-N7HFALX3.js.map → chunk-VGTKUOYS.js.map} +0 -0
- /package/dist/{chunk-Q4BO4Z5V.js.map → chunk-ZTOE3ND3.js.map} +0 -0
- /package/dist/{install-DXSNWZKO.js.map → install-FYZ3HNZK.js.map} +0 -0
- /package/dist/{src-UVDX5UP6.js.map → src-NCJPUAE6.js.map} +0 -0
- /package/dist/{tag-YY6YAABQ.js.map → src-Y3K7UJEF.js.map} +0 -0
- /package/dist/{trace-HXIW7U2D.js.map → tag-YSU2XXPI.js.map} +0 -0
- /package/dist/{triage-ORG45R5W.js.map → trace-A3ZY6K26.js.map} +0 -0
- /package/dist/{upgrade-L2VYMP2V.js.map → upgrade-6ZUFETKX.js.map} +0 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: optimize-cache
|
|
3
|
+
description: Build an optimized playbook + deterministic cache for a Canary workflow from a real run's Playwright trace. Materializes the run workspace, works node by node, verifies locators offline against trace-reconstructed DOMs, and commits recipes through the server-validated cache-recipe endpoint. Use when the user asks to optimize a workflow's cache, harvest a run into the cache, or repair a low-hit-rate workflow.
|
|
4
|
+
allowed-tools: Read, Grep, Glob, Bash(canary workflow debug:*), Bash(canary workflow cache:*), Bash(canary workflow runs:*), Bash(jq:*), Bash(diff:*), Bash(sed -n:*), Bash(head:*), Bash(tail:*), Bash(wc:*), Bash(grep:*), Bash(find:*), Bash(ls:*), Bash(cat:*)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!-- AUTOGENERATED from packages/workflow-cache/. Do not edit by hand. -->
|
|
8
|
+
<!-- After editing the partials, mint a new version: -->
|
|
9
|
+
<!-- bun run --filter=@canaryai/cli bump-skill optimize-cache [patch|minor|major] -->
|
|
10
|
+
|
|
11
|
+
# Cache Optimization Guide
|
|
12
|
+
|
|
13
|
+
You are building the deterministic cache + playbook for a workflow from the
|
|
14
|
+
evidence of a real run — the same procedure the server's async cache-optimizer
|
|
15
|
+
agent follows, driven manually through the canary CLI.
|
|
16
|
+
|
|
17
|
+
## Materialize the workspace
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
canary workflow debug materialize <runId> --out ./cache-opt-<runId>/
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The workspace contains the trace (`browser/trace.zip`, parsed
|
|
24
|
+
`browser/actions.jsonl`), per-step thoughts, timeline JSONLs, the workflow
|
|
25
|
+
definition, and variable lineage.
|
|
26
|
+
|
|
27
|
+
## CLI tools
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Reconstruct one trace action's DOM and capture its substrate-v2:
|
|
31
|
+
canary workflow cache substrate --workspace ./cache-opt-<runId> --action <traceActionIndex> [--phase before|after]
|
|
32
|
+
|
|
33
|
+
# Derive + offline-verify a locator for a ref in that substrate:
|
|
34
|
+
canary workflow cache locator --workspace ./cache-opt-<runId> --action <traceActionIndex> --ref <ref> [--var name=value ...]
|
|
35
|
+
|
|
36
|
+
# Commit a node's recipe (server recomputes the hash and validates):
|
|
37
|
+
canary workflow cache commit <workflowId> --node <nodeId> --recipe <recipe.json>
|
|
38
|
+
|
|
39
|
+
# Or enqueue the server-side optimizer job instead of working manually:
|
|
40
|
+
canary workflow cache optimize <workflowId> [--run <runId>]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Methodology — node by node, evidence first
|
|
44
|
+
|
|
45
|
+
You are building the deterministic cache and playbook for a workflow from the
|
|
46
|
+
evidence of a real run: its Playwright trace, the agent's tool calls and
|
|
47
|
+
thoughts, and the run's variable/macro records. Work methodically:
|
|
48
|
+
|
|
49
|
+
1. **Orient.** Read the workspace summary, the workflow definition
|
|
50
|
+
(`workflow/`), `timeline/*.jsonl`, `variables/lineage.jsonl`, and the
|
|
51
|
+
per-step thoughts (`thoughts/by-step/`). Note each step's `pathNotes`
|
|
52
|
+
summary when present — the live agent recorded its detours there.
|
|
53
|
+
2. **Pick harvestable nodes.** Only nodes whose step SUCCEEDED are
|
|
54
|
+
harvestable. A node that succeeded in a run that later failed is still
|
|
55
|
+
valid — often the most valuable, since that run won't be green soon.
|
|
56
|
+
Cache-replayed nodes (their trace actions sit under a `Cache Replay:`
|
|
57
|
+
group) carry nothing new — skip them.
|
|
58
|
+
3. **Per node, reconstruct the true path.** Cross-reference the node's trace
|
|
59
|
+
actions with the agent's tool calls and thoughts. Identify what actually
|
|
60
|
+
accomplished the task versus exploration.
|
|
61
|
+
4. **Author the most-direct playbook** of core actions (see Core actions).
|
|
62
|
+
The playbook describes the shortest correct path to the node's goal — not
|
|
63
|
+
a transcript of what the agent happened to do.
|
|
64
|
+
5. **Build and verify each locator** (see Verification bar). Materialize the
|
|
65
|
+
substrate for the action's before-state, derive the locator, verify it
|
|
66
|
+
offline. Iterate or degrade to a gap — never commit an unverified locator.
|
|
67
|
+
6. **Commit the node, then move on.** One node at a time; do not interleave.
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
## Core actions — what becomes a playbook step
|
|
71
|
+
|
|
72
|
+
Every CORE action on the success path becomes a playbook step (cached) or a
|
|
73
|
+
gap. Core actions are:
|
|
74
|
+
|
|
75
|
+
- **Mutations**: click, fill/type, select, check/uncheck, press, hover-to-open,
|
|
76
|
+
navigate, file upload, drag.
|
|
77
|
+
- **Assertions**: explicit verify steps and inline assertions the node declares.
|
|
78
|
+
- **Captures**: values the agent saved for downstream steps (e.g.
|
|
79
|
+
`browser_evaluate` with `saveAs`). These become context extractors on the
|
|
80
|
+
cache row — `declaredOutputs` stay reserved for DOM-captured values.
|
|
81
|
+
- **Non-browser effects**: reading email/inbox, running python, reading or
|
|
82
|
+
verifying downloads. These are invisible in the browser trace — source them
|
|
83
|
+
from the agent's tool-call records and thoughts. They become playbook steps
|
|
84
|
+
whose compiled entry is a **gap** (the replay agent performs them live);
|
|
85
|
+
python steps with a cache label become cached data scripts.
|
|
86
|
+
|
|
87
|
+
Observation tool calls are NOT playbook material: accessibility snapshots,
|
|
88
|
+
screenshots, look-around scrolling, `browser_evaluate` without `saveAs`. They
|
|
89
|
+
informed the agent; they leave no artifact in the playbook.
|
|
90
|
+
|
|
91
|
+
A playbook that silently drops a core action loses functionality — when in
|
|
92
|
+
doubt whether an action was core, check what downstream steps consumed.
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
## Replay mechanics — author recipes that replay, not recipes that look right
|
|
96
|
+
|
|
97
|
+
The trace shows RESOLVED values; the recipe must reference their SOURCES so
|
|
98
|
+
future runs work with fresh data:
|
|
99
|
+
|
|
100
|
+
- **`$var.X` tokens** substitute from the variable registry at replay.
|
|
101
|
+
Workflow `inputValues` auto-promote to `$var.{name}` downstream; values an
|
|
102
|
+
agent captured land under the key it saved them as. Keep these tokens
|
|
103
|
+
verbatim in playbook params — never inline the resolved value you saw in
|
|
104
|
+
the trace.
|
|
105
|
+
- **Macros** (`$EMAIL()`, `$UUID()`, `$RANDOM_STRING()`, …) re-roll fresh on
|
|
106
|
+
every run. The run's macro resolution records map resolved values back to
|
|
107
|
+
expressions; supply action args as literals and let the synthesizer attach
|
|
108
|
+
`macroBindings` — do not hand-write macro expressions into action args.
|
|
109
|
+
- **Dynamic locator text** (`dynamicText` / `containsTextDynamic` bindings)
|
|
110
|
+
re-resolves selector text from run state. When a locator can only be
|
|
111
|
+
disambiguated by a run-specific value, bind it to the variable that
|
|
112
|
+
produced the value — never anchor on the literal.
|
|
113
|
+
- **Gaps** are honest: a `gap` compiled entry means "the replay agent performs
|
|
114
|
+
this step live". A gap replays slower but correctly; a bad cached locator
|
|
115
|
+
replays fast and wrong. Prefer the gap.
|
|
116
|
+
- **Volatile identifiers** (seeded entity numbers, ULIDs/UUIDs, timestamps,
|
|
117
|
+
framework-generated class hashes) WILL differ next run even though they
|
|
118
|
+
point at a real element today. Structural prefixes (e.g. a stable
|
|
119
|
+
`__new__` draft-row prefix) are usable; the embedded number is not.
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
## Exploration filtering — most-direct path, not a transcript
|
|
123
|
+
|
|
124
|
+
The live agent sometimes explores: opens the wrong section, misclicks,
|
|
125
|
+
scrolls to look around, retries. The playbook is the shortest correct path,
|
|
126
|
+
so filter exploration out — but carefully:
|
|
127
|
+
|
|
128
|
+
- Use the agent's thoughts and the step's `pathNotes` ("accidentally opened X
|
|
129
|
+
before finding A under B") to separate detours from the success path.
|
|
130
|
+
- An action is exploratory if the success path works without it. It is
|
|
131
|
+
LOAD-BEARING if a later action's target only exists/becomes visible because
|
|
132
|
+
of it — dismissing a blocking dialog, expanding a collapsed section,
|
|
133
|
+
switching a tab. Keep those even if the agent stumbled into them.
|
|
134
|
+
- The offline check: if the direct-path locator for step N does not resolve
|
|
135
|
+
against the post-state of step N-1, you dropped something load-bearing.
|
|
136
|
+
Re-examine the actions you filtered.
|
|
137
|
+
- Collapse retries: a fill that was typed twice (cleared, retyped) is one
|
|
138
|
+
step with the final value.
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
## Verification bar — never commit an unverified locator
|
|
142
|
+
|
|
143
|
+
Every cached browser action's locator must pass offline verification against
|
|
144
|
+
the trace-reconstructed DOM of the action's before-state:
|
|
145
|
+
|
|
146
|
+
1. Materialize the substrate for the action (its before-snapshot).
|
|
147
|
+
2. Derive the locator from the substrate (the deterministic builder owns the
|
|
148
|
+
predicate ladder — do not hand-craft selectors when the builder succeeds).
|
|
149
|
+
3. Verify: the strategy must resolve to EXACTLY the recorded target element
|
|
150
|
+
(handle identity, count of one) and must not lean on volatile identifier
|
|
151
|
+
tokens.
|
|
152
|
+
|
|
153
|
+
On failure, in order:
|
|
154
|
+
- Re-check the target ref — did you pick the right element in the substrate?
|
|
155
|
+
- Try the builder against a corrected ref or with the run's variable values
|
|
156
|
+
(a row may only be distinguishable via a bound variable).
|
|
157
|
+
- If the element is one of several identical widgets (responsive duplicates,
|
|
158
|
+
repeated list rows with no distinguishing signal), that is IRREDUCIBLE.
|
|
159
|
+
Do not reach for ordinal/nth predicates — ordinals encode layout order,
|
|
160
|
+
not identity. Mark the step as a gap instead.
|
|
161
|
+
- Any other persistent failure: gap, with a reason.
|
|
162
|
+
|
|
163
|
+
A verified-offline locator can still fail live (reconstructed DOMs lack JS
|
|
164
|
+
state and cross-origin frames) — that is what replay healing is for. An
|
|
165
|
+
UNVERIFIED locator must never be committed.
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
## Commit rules — one node, one recipe
|
|
169
|
+
|
|
170
|
+
- Only nodes whose step SUCCEEDED in the source run are committable — the
|
|
171
|
+
commit is rejected otherwise (enforced, not advisory). Failed or
|
|
172
|
+
never-reached nodes have no proven success path to cache.
|
|
173
|
+
- One commit per node, after all its steps are resolved (cached or gap).
|
|
174
|
+
- Playbook steps are numbered contiguously from 1; every step maps to exactly
|
|
175
|
+
one cached action or one gap.
|
|
176
|
+
- **Reconcile, don't duplicate, auto-cached fields.** Assertions the node
|
|
177
|
+
declares are carried as the codified assertion on the recipe; captures are
|
|
178
|
+
carried as context extractors — aligned to the new step order. Do not also
|
|
179
|
+
write them as redundant steps, and never drop them.
|
|
180
|
+
- **Lossless tokens.** Every `$var.X` / macro the node's inputs and previous
|
|
181
|
+
recipe depended on must survive into the new recipe (as a token, a variable
|
|
182
|
+
binding, or a macro binding). The commit validates this and rejects recipes
|
|
183
|
+
that inline run-specific values.
|
|
184
|
+
- The node hash is recomputed server-side from the node context with your
|
|
185
|
+
playbook applied; the next run computes the same hash and replays your
|
|
186
|
+
recipe. You never supply a hash.
|
|
187
|
+
- Be honest in summaries: report how many steps are cached versus gaps and
|
|
188
|
+
why. A mostly-gap recipe for a hard node is a fine outcome.
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
## Recipe file format
|
|
192
|
+
|
|
193
|
+
`canary workflow cache commit` takes a JSON file:
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"sourceRunId": "<runId>",
|
|
198
|
+
"playbookSteps": [{ "order": 1, "action": "fill", "target": "Name field", "params": { "text": "$var.name" } }],
|
|
199
|
+
"plannedActions": [{ "stepOrder": 1, "toolName": "browser_type", "actionType": "type", "args": { "text": "Acme Corp" }, "selectors": { "role": { "role": "textbox", "name": "Name" } }, "elementDescription": "Name field" }],
|
|
200
|
+
"gaps": [{ "stepOrder": 2, "reason": "reads verification email from inbox" }]
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
`plannedActions[].args` carry the LITERAL values from the trace — the server
|
|
205
|
+
maps them back to variables/macros from the run's records and rejects recipes
|
|
206
|
+
that drop required tokens.
|
|
@@ -12,8 +12,7 @@ import {
|
|
|
12
12
|
workflowToSpec,
|
|
13
13
|
writeNodeIds,
|
|
14
14
|
writeWorkflowId
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
import "./chunk-SJJW77WN.js";
|
|
15
|
+
} from "./chunk-YN763NWQ.js";
|
|
17
16
|
import {
|
|
18
17
|
fetchOrExit
|
|
19
18
|
} from "./chunk-3CZU7XCS.js";
|
|
@@ -21,7 +20,15 @@ import {
|
|
|
21
20
|
getArgValue,
|
|
22
21
|
hasFlag,
|
|
23
22
|
resolveAppUrl
|
|
24
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-PZ4LCN6K.js";
|
|
24
|
+
import {
|
|
25
|
+
isSpecFile,
|
|
26
|
+
readableSpecDirs,
|
|
27
|
+
specDir,
|
|
28
|
+
specExtension,
|
|
29
|
+
splitSpecExtension
|
|
30
|
+
} from "./chunk-JZMJUPZP.js";
|
|
31
|
+
import "./chunk-QGI4UCJX.js";
|
|
25
32
|
import "./chunk-VKVL7WBN.js";
|
|
26
33
|
|
|
27
34
|
// src/spec/index.ts
|
|
@@ -175,7 +182,7 @@ async function resolvePropertyNameMap(ctx, names) {
|
|
|
175
182
|
var UnsupportedFieldEditError = class extends Error {
|
|
176
183
|
constructor(fields) {
|
|
177
184
|
super(
|
|
178
|
-
`Editing ${fields.join(", ")} via
|
|
185
|
+
`Editing ${fields.join(", ")} via a spec file is not supported yet. These fields require name \u2192 id resolution that lands in a follow-up. Edit them in the Canary UI for now; the rest of your changes will still apply.`
|
|
179
186
|
);
|
|
180
187
|
this.name = "UnsupportedFieldEditError";
|
|
181
188
|
}
|
|
@@ -289,21 +296,19 @@ function printSpecHelp() {
|
|
|
289
296
|
" validate <file> Parse + lint the spec offline",
|
|
290
297
|
" compile <file> Validate, then create the workflow",
|
|
291
298
|
" diff <file> Preview what `compile` would change (no writes)",
|
|
292
|
-
|
|
299
|
+
` pull <workflow-id> Reverse: workflow \u2192 round-trippable ${specExtension}`,
|
|
293
300
|
" pull --all Sync a directory of specs down from all workflows",
|
|
294
301
|
"",
|
|
295
302
|
"Compile flags:",
|
|
296
|
-
" --publish Create as published instead of draft",
|
|
297
|
-
" --run-and-publish After create, trigger a test run; publish if green",
|
|
298
303
|
" --allow-duplicate Fresh compile: create even when a same-named workflow exists",
|
|
299
304
|
" --create-with-duplicate-title",
|
|
300
305
|
" Alias for --allow-duplicate",
|
|
301
306
|
" --json Machine-readable output",
|
|
302
307
|
"",
|
|
303
308
|
"Pull flags:",
|
|
304
|
-
|
|
309
|
+
` --output <path|dir> File (single) or directory (--all); default ${specDir}/`,
|
|
305
310
|
" --all Sync every workflow into the directory (add new, update matching by workflowId)",
|
|
306
|
-
" --status <s> With --all, filter workflows by status (draft|
|
|
311
|
+
" --status <s> With --all, filter workflows by status (draft|approved|\u2026)",
|
|
307
312
|
" --force Overwrite an existing file (single pull)"
|
|
308
313
|
].join("\n")
|
|
309
314
|
);
|
|
@@ -473,7 +478,7 @@ async function handlePull(argv, apiUrl, token) {
|
|
|
473
478
|
console.error(`Error: workflow ${workflowId} not found (or no permission).`);
|
|
474
479
|
process.exit(1);
|
|
475
480
|
}
|
|
476
|
-
const outPath = getArgValue(argv, "--output") ?? path.join(
|
|
481
|
+
const outPath = getArgValue(argv, "--output") ?? path.join(specDir, `${slugify(live.name)}${specExtension}`);
|
|
477
482
|
const preserved = await readPreservedFrontmatter(outPath);
|
|
478
483
|
const sidecarPath = await fetchAndWriteApiSequenceSidecar(
|
|
479
484
|
apiUrl,
|
|
@@ -524,10 +529,12 @@ async function handlePull(argv, apiUrl, token) {
|
|
|
524
529
|
}
|
|
525
530
|
async function handlePullAll(argv, apiUrl, token) {
|
|
526
531
|
const json = hasFlag(argv, "--json");
|
|
527
|
-
const
|
|
532
|
+
const explicitDir = getArgValue(argv, "--output") ?? argv.find((a) => !a.startsWith("-"));
|
|
533
|
+
const dir = explicitDir ?? specDir;
|
|
528
534
|
const status = getArgValue(argv, "--status");
|
|
529
535
|
const workflows = await fetchWorkflowList(apiUrl, token, status);
|
|
530
|
-
const
|
|
536
|
+
const indexDirs = explicitDir ? [explicitDir] : readableSpecDirs();
|
|
537
|
+
const byWorkflowId = await mergeSpecRoots(indexDirs);
|
|
531
538
|
const usedNames = new Set([...byWorkflowId.values()].map((p) => path.basename(p)));
|
|
532
539
|
const results = [];
|
|
533
540
|
for (const wf of workflows) {
|
|
@@ -651,7 +658,7 @@ async function indexSpecFiles(dir) {
|
|
|
651
658
|
}
|
|
652
659
|
const ID_RE = /^workflowId:\s*([0-9a-f-]{36})\s*$/im;
|
|
653
660
|
for (const rel of entries) {
|
|
654
|
-
if (!rel
|
|
661
|
+
if (!isSpecFile(rel)) continue;
|
|
655
662
|
const full = path.join(dir, rel);
|
|
656
663
|
try {
|
|
657
664
|
const src = await fs.readFile(full, "utf-8");
|
|
@@ -662,9 +669,18 @@ async function indexSpecFiles(dir) {
|
|
|
662
669
|
}
|
|
663
670
|
return map;
|
|
664
671
|
}
|
|
672
|
+
async function mergeSpecRoots(roots) {
|
|
673
|
+
const byWorkflowId = /* @__PURE__ */ new Map();
|
|
674
|
+
for (const root of roots) {
|
|
675
|
+
for (const [id, file] of await indexSpecFiles(root)) {
|
|
676
|
+
if (!byWorkflowId.has(id)) byWorkflowId.set(id, file);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
return byWorkflowId;
|
|
680
|
+
}
|
|
665
681
|
function uniqueName(slug, workflowId, used) {
|
|
666
|
-
let name = `${slug}
|
|
667
|
-
if (used.has(name)) name = `${slug}-${workflowId.slice(0, 8)}
|
|
682
|
+
let name = `${slug}${specExtension}`;
|
|
683
|
+
if (used.has(name)) name = `${slug}-${workflowId.slice(0, 8)}${specExtension}`;
|
|
668
684
|
return name;
|
|
669
685
|
}
|
|
670
686
|
async function runFreshCompile(opts) {
|
|
@@ -699,7 +715,13 @@ async function runFreshCompile(opts) {
|
|
|
699
715
|
}
|
|
700
716
|
process.exit(1);
|
|
701
717
|
}
|
|
702
|
-
|
|
718
|
+
if (hasFlag(argv, "--publish")) {
|
|
719
|
+
console.error(
|
|
720
|
+
"Error: --publish was removed. Compile the spec, then approve it (canary workflow approve <id> --yes) and promote it (canary workflow promote <id>)."
|
|
721
|
+
);
|
|
722
|
+
process.exit(1);
|
|
723
|
+
}
|
|
724
|
+
const status = emitted.status ?? "draft";
|
|
703
725
|
const payload = {
|
|
704
726
|
name: emitted.name,
|
|
705
727
|
description: emitted.description,
|
|
@@ -769,8 +791,10 @@ async function runFreshCompile(opts) {
|
|
|
769
791
|
for (const w of body.warnings) console.log(` \u26A0 ${w}`);
|
|
770
792
|
}
|
|
771
793
|
if (hasFlag(argv, "--run-and-publish")) {
|
|
772
|
-
console.log("\n\u2139 --run-and-publish
|
|
773
|
-
console.log(` canary workflow run ${body.workflow.id}
|
|
794
|
+
console.log("\n\u2139 --run-and-publish was removed. Run, approve, then promote:");
|
|
795
|
+
console.log(` canary workflow run ${body.workflow.id}`);
|
|
796
|
+
console.log(` canary workflow approve ${body.workflow.id} --yes`);
|
|
797
|
+
console.log(` canary workflow promote ${body.workflow.id}`);
|
|
774
798
|
}
|
|
775
799
|
}
|
|
776
800
|
var DUPLICATE_WORKFLOW_TITLE_ALLOW_FLAGS = [
|
|
@@ -823,10 +847,11 @@ async function fetchFreshCompileDuplicateWorkflows(opts) {
|
|
|
823
847
|
return exactWorkflowTitleMatches(body.data ?? [], opts.workflowName);
|
|
824
848
|
}
|
|
825
849
|
function stampedSpecPath(file) {
|
|
826
|
-
|
|
827
|
-
|
|
850
|
+
const split = splitSpecExtension(file);
|
|
851
|
+
if (split) {
|
|
852
|
+
return `${split.base}.stamped${split.ext}`;
|
|
828
853
|
}
|
|
829
|
-
return `${file}.stamped
|
|
854
|
+
return `${file}.stamped${specExtension}`;
|
|
830
855
|
}
|
|
831
856
|
async function runRecompile(opts) {
|
|
832
857
|
const { argv, apiUrl, token, file, emitted } = opts;
|
|
@@ -881,9 +906,9 @@ async function runRecompile(opts) {
|
|
|
881
906
|
if (diff.nameDrift) {
|
|
882
907
|
console.log(` \u270E renamed: "${diff.nameDrift.from}" \u2192 "${diff.nameDrift.to}"`);
|
|
883
908
|
}
|
|
884
|
-
if (live.
|
|
909
|
+
if (live.promotion === "promoted") {
|
|
885
910
|
console.log(
|
|
886
|
-
` \u26A0 workflow is
|
|
911
|
+
` \u26A0 workflow is promoted \u2014 suites run the live definition, so these changes take effect on the next suite run.`
|
|
887
912
|
);
|
|
888
913
|
}
|
|
889
914
|
if (diff.duplicateSpecIds.length > 0) {
|
|
@@ -945,7 +970,11 @@ export {
|
|
|
945
970
|
handleCompile,
|
|
946
971
|
handleSpec,
|
|
947
972
|
handleValidate,
|
|
973
|
+
indexSpecFiles,
|
|
948
974
|
lintSpec,
|
|
949
|
-
|
|
975
|
+
mergeSpecRoots,
|
|
976
|
+
parseSpec,
|
|
977
|
+
stampedSpecPath,
|
|
978
|
+
uniqueName
|
|
950
979
|
};
|
|
951
|
-
//# sourceMappingURL=spec-
|
|
980
|
+
//# sourceMappingURL=spec-LM5XHOQ5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/spec/index.ts","../src/spec/sync.ts"],"sourcesContent":["/**\n * `canary spec` subcommand entry points.\n *\n * Subcommands:\n * canary spec validate <file> — parse + lint, exit non-zero on errors\n * canary spec compile <file> — validate, then POST to /workflows/create-with-nodes\n * canary spec pull <id> — reverse: workflow → spec (TODO)\n * canary spec diff <file> <id> — what would compile change? (TODO)\n *\n * @module spec\n */\n\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { getArgValue, hasFlag, resolveAppUrl } from '../auth.js';\nimport { fetchOrExit } from '../api-fetch.js';\nimport {\n isSpecFile,\n readableSpecDirs,\n specDir,\n specExtension,\n splitSpecExtension,\n} from '../brand.js';\nimport {\n ApiSequenceFileSchema,\n diffWorkflow,\n emitSpec,\n extractPreservedFrontmatter,\n hasAnyChange,\n lintSpec,\n parseSpec,\n summarize,\n workflowToSpec,\n writeNodeIds,\n writeWorkflowId,\n type DiffResult,\n type LiveWorkflow,\n type PullWorkflow,\n type PullOptions,\n type SpecAst,\n type SpecIssue,\n} from '@chatsdet/canary-spec';\nimport { applyDiff } from './sync.js';\n\nexport async function handleSpec(argv: string[], apiUrl: string, token: string): Promise<void> {\n const sub = argv[0];\n switch (sub) {\n case 'validate':\n await handleValidate(argv.slice(1));\n return;\n case 'compile':\n await handleCompile(argv.slice(1), apiUrl, token);\n return;\n case 'pull':\n await handlePull(argv.slice(1), apiUrl, token);\n return;\n case 'diff':\n await handleDiff(argv.slice(1), apiUrl, token);\n return;\n default:\n printSpecHelp();\n process.exit(sub ? 1 : 0);\n }\n}\n\nfunction printSpecHelp(): void {\n console.log(\n [\n 'Usage: canary spec <subcommand> <file>',\n '',\n 'Subcommands:',\n ' validate <file> Parse + lint the spec offline',\n ' compile <file> Validate, then create the workflow',\n ' diff <file> Preview what `compile` would change (no writes)',\n ` pull <workflow-id> Reverse: workflow → round-trippable ${specExtension}`,\n ' pull --all Sync a directory of specs down from all workflows',\n '',\n 'Compile flags:',\n ' --allow-duplicate Fresh compile: create even when a same-named workflow exists',\n ' --create-with-duplicate-title',\n ' Alias for --allow-duplicate',\n ' --json Machine-readable output',\n '',\n 'Pull flags:',\n ` --output <path|dir> File (single) or directory (--all); default ${specDir}/`,\n ' --all Sync every workflow into the directory (add new, update matching by workflowId)',\n ' --status <s> With --all, filter workflows by status (draft|approved|…)',\n ' --force Overwrite an existing file (single pull)',\n ].join('\\n')\n );\n}\n\nasync function handleValidate(argv: string[]): Promise<void> {\n const file = argv[0];\n if (!file) {\n console.error('Error: canary spec validate <file>');\n process.exit(1);\n }\n const json = hasFlag(argv, '--json');\n\n const result = await loadAndValidate(file);\n if (json) {\n console.log(\n JSON.stringify(\n {\n ok: result.ok,\n issues: result.issues,\n ...(result.ok ? { workflowName: result.ast.frontmatter.name } : {}),\n },\n null,\n 2\n )\n );\n process.exit(result.ok ? 0 : 1);\n }\n printIssues(result.issues, file);\n if (!result.ok) {\n process.exit(1);\n }\n console.log(`✓ ${file} validates`);\n}\n\nasync function handleCompile(argv: string[], apiUrl: string, token: string): Promise<void> {\n const file = argv[0];\n if (!file) {\n console.error('Error: canary spec compile <file>');\n process.exit(1);\n }\n const result = await loadAndValidate(file);\n printIssues(result.issues, file);\n if (!result.ok) {\n process.exit(1);\n }\n\n const ast = result.ast;\n // Append a default end node only on a fresh create; on recompile (spec\n // already bound to a workflow) suppress it so an end-less workflow doesn't\n // gain a phantom node on every round-trip.\n const emitted = emitSpec(ast, { appendDefaultEnd: !ast.frontmatter.workflowId });\n\n // Load any api_sequence sidecar referenced by a node. Multiple nodes pointing\n // at the same path share one sidecar (DB enforces one api_sequence row per\n // workflow); divergent paths produce a clear error before any state changes.\n try {\n emitted.apiSequenceConfig = await loadApiSequenceSidecar(emitted.nodes, file);\n } catch (err) {\n console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n\n // Branch: recompile (frontmatter has `workflowId`) vs fresh create.\n if (emitted.workflowId) {\n await runRecompile({ argv, apiUrl, token, file, emitted });\n return;\n }\n\n await runFreshCompile({ argv, apiUrl, token, file, emitted });\n}\n\n/**\n * Resolve and load the api_sequence sidecar(s) referenced by spec nodes.\n *\n * The DB enforces one api_sequence row per workflow, so a single workflow may\n * carry many `nodeType: 'api_sequence'` nodes but they all point at the same\n * sidecar. We accept multiple identical paths but reject divergent ones with\n * a clear error before any state mutation.\n */\nasync function loadApiSequenceSidecar(\n nodes: ReadonlyArray<{ nodeType: string; configJson: Record<string, unknown> }>,\n specFile: string\n): Promise<unknown> {\n const refs = nodes\n .filter((n) => n.nodeType === 'api_sequence')\n .map((n) => n.configJson['__apiSequenceSidecar'])\n .filter((p): p is string => typeof p === 'string' && p.length > 0);\n if (refs.length === 0) return undefined;\n const unique = Array.from(new Set(refs));\n if (unique.length > 1) {\n throw new Error(\n `Spec has divergent api_sequence sidecar paths: ${unique\n .map((p) => `\"${p}\"`)\n .join(', ')}. The DB stores one config per workflow — pick a single path.`\n );\n }\n const sidecarPath = path.resolve(path.dirname(specFile), unique[0]);\n let raw: string;\n try {\n raw = await fs.readFile(sidecarPath, 'utf-8');\n } catch (err) {\n throw new Error(\n `Could not read api_sequence sidecar at ${sidecarPath}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n const yaml = await import('yaml');\n let parsed: unknown;\n try {\n parsed = yaml.parse(raw);\n } catch (err) {\n throw new Error(\n `Sidecar YAML at ${sidecarPath} is invalid: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n const validated = ApiSequenceFileSchema.safeParse(parsed);\n if (!validated.success) {\n throw new Error(\n `Sidecar at ${sidecarPath} doesn't match the api_sequence shape: ${validated.error.message}`\n );\n }\n return validated.data;\n}\n\nasync function handleDiff(argv: string[], apiUrl: string, token: string): Promise<void> {\n const file = argv[0];\n if (!file) {\n console.error('Error: canary spec diff <file>');\n process.exit(1);\n }\n const result = await loadAndValidate(file);\n printIssues(result.issues, file);\n if (!result.ok) process.exit(1);\n\n const emitted = emitSpec(result.ast, { appendDefaultEnd: false });\n if (!emitted.workflowId) {\n console.log(\n `ℹ ${file} has no workflowId in frontmatter — \\`canary spec compile\\` would create a new workflow.`\n );\n console.log(` Nodes that would be created: ${emitted.nodes.length}`);\n return;\n }\n\n const liveRes = await fetchOrExit(`${apiUrl}/workflows/${emitted.workflowId}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!liveRes.ok) {\n console.error(`Error: failed to fetch workflow ${emitted.workflowId} (${liveRes.status})`);\n process.exit(1);\n }\n const liveBody = (await liveRes.json().catch(() => ({}))) as { workflow?: LiveWorkflow };\n const live = liveBody.workflow;\n if (!live) {\n console.error(`Error: workflow ${emitted.workflowId} not found.`);\n process.exit(1);\n }\n\n let diff: DiffResult;\n try {\n diff = diffWorkflow(emitted, live);\n } catch (err) {\n console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n\n if (hasFlag(argv, '--json')) {\n console.log(JSON.stringify({ ok: true, summary: summarize(diff), diff }, null, 2));\n return;\n }\n\n if (!hasAnyChange(diff)) {\n console.log(`✓ ${file} — in sync with workflow ${emitted.workflowId}`);\n return;\n }\n const s = summarize(diff);\n console.log(`\\nPreview for \"${live.name}\" (${emitted.workflowId}):`);\n console.log(\n ` +${s.created} create · ~${s.updated} update · -${s.deleted} delete · ↻ ${s.moved} move`\n );\n if (diff.nameDrift) console.log(` ✎ rename: \"${diff.nameDrift.from}\" → \"${diff.nameDrift.to}\"`);\n const fieldKeys = Object.keys(diff.workflowFieldChanges).filter((k) => k !== 'name');\n if (fieldKeys.length > 0) console.log(` ✎ workflow fields: ${fieldKeys.join(', ')}`);\n console.log(`\\nRun \\`canary spec compile ${file}\\` to apply.`);\n}\n\nasync function handlePull(argv: string[], apiUrl: string, token: string): Promise<void> {\n if (hasFlag(argv, '--all')) {\n await handlePullAll(argv, apiUrl, token);\n return;\n }\n\n const workflowId = argv.find((a) => !a.startsWith('-'));\n if (!workflowId) {\n console.error('Error: canary spec pull <workflow-id> [--output <path>] [--force]');\n console.error(' canary spec pull --all [--output <dir>] [--status <s>] [--force]');\n process.exit(1);\n }\n const json = hasFlag(argv, '--json');\n const force = hasFlag(argv, '--force');\n\n const live = await fetchWorkflow(apiUrl, token, workflowId);\n if (!live) {\n console.error(`Error: workflow ${workflowId} not found (or no permission).`);\n process.exit(1);\n }\n\n const outPath =\n getArgValue(argv, '--output') ??\n path.join(specDir, `${slugify(live.name)}${specExtension}`);\n\n // Preserve spec-source-only frontmatter from the prior file (requirementRef,\n // scenario skipReason). These have no live equivalent, so a naive overwrite\n // would silently drop the author's notes on re-pull.\n const preserved = await readPreservedFrontmatter(outPath);\n // When the workflow has an api_sequence config, fetch it + write a sidecar\n // file alongside the spec. Pull annotates each api_sequence node in the live\n // workflow with `__apiSequenceSidecar` so `workflowToSpec` can emit the\n // `- apiSequence:` bullet. The sidecar path lives next to the spec under a\n // sibling `sequences/` directory by convention.\n const sidecarPath = await fetchAndWriteApiSequenceSidecar(\n apiUrl,\n token,\n workflowId,\n live,\n outPath\n );\n if (sidecarPath) {\n for (const node of live.nodes) {\n if (node.nodeType === 'api_sequence') {\n node.configJson = {\n ...node.configJson,\n __apiSequenceSidecar: path.relative(path.dirname(outPath), sidecarPath),\n };\n }\n }\n }\n const source = workflowToSpec(live, preserved);\n\n // Validate the round-trip so a malformed pull surfaces immediately.\n const parsed = parseSpec(source);\n const lintIssues = parsed.ok ? lintSpec(parsed.ast) : [];\n const parseIssues = parsed.ok ? [] : parsed.issues;\n const issues = [...parseIssues, ...lintIssues];\n\n if (!force) {\n try {\n await fs.access(outPath);\n console.error(\n `Error: ${outPath} already exists. Use --output <path> or --force to overwrite.`\n );\n process.exit(1);\n } catch {\n // file does not exist — good.\n }\n }\n\n await fs.mkdir(path.dirname(outPath), { recursive: true });\n await fs.writeFile(outPath, source, 'utf-8');\n\n if (json) {\n console.log(JSON.stringify({ ok: true, file: outPath, workflowId, issues }, null, 2));\n return;\n }\n\n console.log(`\\n✓ Pulled \"${live.name}\" → ${outPath}`);\n console.log(` Nodes: ${live.nodes.length}`);\n console.log(` Edit the file, then \\`canary spec compile ${outPath}\\` to push changes back.`);\n if (issues.length > 0) {\n console.log('\\n Notes:');\n printIssues(issues, outPath);\n }\n}\n\n/**\n * `canary spec pull --all` — sync a directory of spec files (`.cofactor.md`,\n * legacy `.canary.md`) down from the live workflows. Existing files are matched\n * to a workflow by their `workflowId:` frontmatter and rewritten in place;\n * workflows with no matching file get a fresh `<slug>` spec in the active\n * brand's extension. Files are only written when their\n * content actually changes, so an already-synced repo produces no churn.\n */\nasync function handlePullAll(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n const explicitDir = getArgValue(argv, '--output') ?? argv.find((a) => !a.startsWith('-'));\n // New files default to the active brand's dir (e.g. `cofactor/specs`).\n const dir = explicitDir ?? specDir;\n const status = getArgValue(argv, '--status');\n\n const workflows = await fetchWorkflowList(apiUrl, token, status);\n // Map existing files by their declared workflowId so re-pulls land in place.\n // With no explicit dir, scan every recognized spec root so an existing\n // `canary/specs` repo is matched and rewritten in place rather than\n // duplicated under the new default `cofactor/specs`.\n const indexDirs = explicitDir ? [explicitDir] : readableSpecDirs();\n const byWorkflowId = await mergeSpecRoots(indexDirs);\n const usedNames = new Set<string>([...byWorkflowId.values()].map((p) => path.basename(p)));\n\n const results: Array<{\n id: string;\n name: string;\n file: string;\n action: 'added' | 'synced' | 'unchanged' | 'failed';\n note?: string;\n }> = [];\n\n for (const wf of workflows) {\n const live = await fetchWorkflow(apiUrl, token, wf.id);\n if (!live) {\n results.push({ id: wf.id, name: wf.name, file: '', action: 'failed', note: 'fetch failed' });\n continue;\n }\n const existing = byWorkflowId.get(wf.id);\n let target: string;\n let isNew = false;\n if (existing) {\n target = existing;\n } else {\n target = path.join(dir, uniqueName(slugify(live.name), wf.id, usedNames));\n usedNames.add(path.basename(target));\n isNew = true;\n }\n\n let prior: string | null = null;\n try {\n prior = await fs.readFile(target, 'utf-8');\n } catch {\n // no prior file\n }\n // Spec-source-only fields (requirementRef, scenario skipReason) have no\n // server-side representation — lift them from the prior file before\n // emitting, so re-pull doesn't drop them.\n const preserved = prior ? extractPreservedFrontmatter(prior) : {};\n const source = workflowToSpec(live, preserved);\n if (prior === source) {\n results.push({ id: wf.id, name: live.name, file: target, action: 'unchanged' });\n continue;\n }\n await fs.mkdir(path.dirname(target), { recursive: true });\n await fs.writeFile(target, source, 'utf-8');\n results.push({ id: wf.id, name: live.name, file: target, action: isNew ? 'added' : 'synced' });\n }\n\n if (json) {\n console.log(JSON.stringify({ ok: true, dir, results }, null, 2));\n return;\n }\n const count = (a: string) => results.filter((r) => r.action === a).length;\n console.log(`\\n✓ Synced ${results.length} workflow(s) → ${dir}`);\n console.log(\n ` +${count('added')} added · ~${count('synced')} synced · =${count('unchanged')} unchanged${count('failed') ? ` · ✗${count('failed')} failed` : ''}`\n );\n for (const r of results) {\n if (r.action === 'unchanged') continue;\n const mark = r.action === 'added' ? '+' : r.action === 'synced' ? '~' : '✗';\n console.log(\n ` ${mark} ${path.relative(process.cwd(), r.file) || r.file}${r.note ? ` (${r.note})` : ''}`\n );\n }\n}\n\n/** Fetch the full live workflow (with nodes + edges) for the diff/pull path. */\n/**\n * Fetch the workflow's api_sequence config (if any) and write it to a sidecar\n * YAML alongside the spec. Returns the absolute path to the sidecar, or null\n * when the workflow doesn't have an api_sequence row.\n *\n * The sidecar filename is derived from the workflow slug and lands in a\n * sibling `sequences/` directory: `<spec-dir>/sequences/<slug>.yaml`.\n * Re-pull overwrites the file in place.\n */\nasync function fetchAndWriteApiSequenceSidecar(\n apiUrl: string,\n token: string,\n workflowId: string,\n live: PullWorkflow,\n outPath: string\n): Promise<string | null> {\n // Skip when the workflow has no api_sequence nodes.\n if (!live.nodes.some((n) => n.nodeType === 'api_sequence')) return null;\n const res = await fetchOrExit(`${apiUrl}/api-sequences/by-workflow/${workflowId}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!res.ok) {\n // Workflow may have api_sequence nodes but no row yet (e.g. legacy state);\n // leave the sidecar off so the round-trip stays graceful.\n return null;\n }\n const body = (await res.json().catch(() => ({}))) as { data?: { config?: unknown } };\n const config = body.data?.config;\n if (!config) return null;\n\n const sequencesDir = path.join(path.dirname(outPath), 'sequences');\n const sidecarSlug = slugify(live.name);\n const sidecarPath = path.join(sequencesDir, `${sidecarSlug}.yaml`);\n await fs.mkdir(sequencesDir, { recursive: true });\n const yaml = await import('yaml');\n await fs.writeFile(sidecarPath, yaml.stringify(config), 'utf-8');\n return sidecarPath;\n}\n\n/**\n * Read spec-source-only frontmatter from the prior file at `target` (if any).\n * Returns an empty `PullOptions` when no prior file exists.\n */\nasync function readPreservedFrontmatter(target: string): Promise<PullOptions> {\n try {\n const prior = await fs.readFile(target, 'utf-8');\n return extractPreservedFrontmatter(prior);\n } catch {\n return {};\n }\n}\n\nasync function fetchWorkflow(\n apiUrl: string,\n token: string,\n workflowId: string\n): Promise<PullWorkflow | null> {\n const res = await fetchOrExit(`${apiUrl}/workflows/${workflowId}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!res.ok) return null;\n const body = (await res.json().catch(() => ({}))) as { workflow?: PullWorkflow };\n return body.workflow ?? null;\n}\n\n/** List workflows (paginated), optionally filtered by status. */\nasync function fetchWorkflowList(\n apiUrl: string,\n token: string,\n status: string | undefined\n): Promise<Array<{ id: string; name: string }>> {\n const out: Array<{ id: string; name: string }> = [];\n let page = 1;\n for (;;) {\n const params = new URLSearchParams({ page: String(page), pageSize: '100' });\n if (status) params.set('status', status);\n const res = await fetchOrExit(`${apiUrl}/workflows?${params.toString()}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!res.ok) {\n console.error(`Error: failed to list workflows (${res.status})`);\n process.exit(1);\n }\n const body = (await res.json().catch(() => ({}))) as {\n data?: Array<{ id: string; name: string }>;\n pagination?: { page: number; totalPages: number };\n };\n for (const w of body.data ?? []) out.push({ id: w.id, name: w.name });\n const pg = body.pagination;\n if (!pg || pg.page >= pg.totalPages || (body.data ?? []).length === 0) break;\n page += 1;\n }\n return out;\n}\n\n/** Build a map of `workflowId` → spec file path across a directory tree. */\nexport async function indexSpecFiles(dir: string): Promise<Map<string, string>> {\n const map = new Map<string, string>();\n let entries: string[];\n try {\n entries = await fs.readdir(dir, { recursive: true });\n } catch {\n return map; // directory doesn't exist yet\n }\n const ID_RE = /^workflowId:\\s*([0-9a-f-]{36})\\s*$/im;\n for (const rel of entries) {\n if (!isSpecFile(rel)) continue;\n const full = path.join(dir, rel);\n try {\n const src = await fs.readFile(full, 'utf-8');\n const m = ID_RE.exec(src);\n if (m && !map.has(m[1])) map.set(m[1], full);\n } catch {\n // unreadable — skip\n }\n }\n return map;\n}\n\n/**\n * Index several spec roots and merge into one `workflowId` → path map.\n * Roots are processed in order and the FIRST occurrence of a `workflowId` wins,\n * so callers pass roots highest-precedence-first (see {@link readableSpecDirs}).\n * This is what keeps a `pull --all` from duplicating an existing legacy\n * `canary/specs` file under the new default `cofactor/specs`.\n */\nexport async function mergeSpecRoots(roots: string[]): Promise<Map<string, string>> {\n const byWorkflowId = new Map<string, string>();\n for (const root of roots) {\n for (const [id, file] of await indexSpecFiles(root)) {\n if (!byWorkflowId.has(id)) byWorkflowId.set(id, file);\n }\n }\n return byWorkflowId;\n}\n\n/** Pick a collision-free filename, disambiguating with a short id when needed. */\nexport function uniqueName(slug: string, workflowId: string, used: Set<string>): string {\n let name = `${slug}${specExtension}`;\n if (used.has(name)) name = `${slug}-${workflowId.slice(0, 8)}${specExtension}`;\n return name;\n}\n\nasync function runFreshCompile(opts: {\n argv: string[];\n apiUrl: string;\n token: string;\n file: string;\n emitted: ReturnType<typeof emitSpec>;\n}): Promise<void> {\n const { argv, apiUrl, token, file, emitted } = opts;\n\n const duplicateMatches = await fetchFreshCompileDuplicateWorkflows({\n argv,\n apiUrl,\n token,\n workflowName: emitted.name,\n });\n if (duplicateMatches.length > 0) {\n const message = formatDuplicateWorkflowTitleWarning({\n file,\n workflowName: emitted.name,\n matches: duplicateMatches,\n });\n if (hasFlag(argv, '--json')) {\n console.log(\n JSON.stringify(\n {\n ok: false,\n error: 'duplicate_workflow_title',\n message,\n workflows: duplicateMatches,\n },\n null,\n 2\n )\n );\n } else {\n console.error(message);\n }\n process.exit(1);\n }\n\n // `--publish` was removed with the approval/promotion lifecycle split — approval\n // is an explicit human act (canary workflow approve) and suite inclusion is a\n // separate promote step. Flows always compile as drafts.\n if (hasFlag(argv, '--publish')) {\n console.error(\n 'Error: --publish was removed. Compile the spec, then approve it ' +\n '(canary workflow approve <id> --yes) and promote it (canary workflow promote <id>).'\n );\n process.exit(1);\n }\n const status = emitted.status ?? 'draft';\n\n const payload: Record<string, unknown> = {\n name: emitted.name,\n description: emitted.description,\n preflight: emitted.preflight,\n cleanup: emitted.cleanup,\n status,\n flowType: emitted.flowType,\n createdVia: 'cli-spec',\n // The route only knows about (nodeType, configJson) — strip our meta.\n nodes: emitted.nodes.map(({ nodeType, configJson }) => ({ nodeType, configJson })),\n ...(emitted.inputsJson.length > 0 ? { inputsJson: emitted.inputsJson } : {}),\n ...(emitted.scenariosJson.length > 0 ? { scenariosJson: emitted.scenariosJson } : {}),\n };\n\n const res = await fetchOrExit(`${apiUrl}/workflows/create-with-nodes`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n token,\n body: JSON.stringify(payload),\n });\n\n const body = (await res.json().catch(() => ({}))) as {\n ok: boolean;\n error?: string;\n warnings?: string[];\n workflow?: { id: string; name: string; status: string };\n nodes?: Array<{ id: string; nodeType: string; orderIndex: number }>;\n };\n\n if (!res.ok || !body.ok || !body.workflow) {\n console.error(`Error: ${body.error ?? `${res.status} ${res.statusText}`}`);\n process.exit(1);\n }\n\n // Write workflowId back to frontmatter + @id comments to each lead-in line.\n // Pair each emitted node with its sourceLine and the corresponding new\n // server-side node UUID (the route returns nodes in orderIndex order).\n const writebackPairs: Array<{ sourceLine: number; nodeId: string }> = [];\n if (body.nodes && body.nodes.length === emitted.nodes.length) {\n for (let i = 0; i < emitted.nodes.length; i++) {\n const sourceLine = emitted.nodes[i].sourceLine;\n if (sourceLine === undefined) continue;\n writebackPairs.push({ sourceLine, nodeId: body.nodes[i].id });\n }\n }\n\n try {\n const source = await fs.readFile(file, 'utf-8');\n const withWorkflowId = writeWorkflowId(source, body.workflow.id);\n // writeWorkflowId may insert a new frontmatter line, shifting all subsequent\n // line numbers by +1. Shift sourceLine accordingly so node @id markers land\n // on the lead-in (not on the blank line before it).\n const lineShift = withWorkflowId.split('\\n').length - source.split('\\n').length;\n const shiftedPairs = writebackPairs.map((p) => ({\n ...p,\n sourceLine: p.sourceLine + lineShift,\n }));\n const next = writeNodeIds(withWorkflowId, shiftedPairs);\n if (next !== source) {\n await fs.writeFile(file, next, 'utf-8');\n }\n } catch (err) {\n console.warn(\n `⚠ Workflow created, but failed to write IDs back to ${file}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n\n if (hasFlag(argv, '--json')) {\n console.log(JSON.stringify({ ok: true, workflow: body.workflow, writebackPairs }, null, 2));\n return;\n }\n\n const appUrl = resolveAppUrl(apiUrl);\n console.log(`\\n✓ Spec compiled: ${body.workflow.name}`);\n console.log(` ID: ${body.workflow.id}`);\n console.log(` Status: ${body.workflow.status}`);\n console.log(` Nodes: ${body.nodes?.length ?? 0}`);\n console.log(` URL: ${appUrl}/flows/${body.workflow.id}`);\n console.log(` → wrote workflowId + @id markers back to ${file}`);\n if (body.warnings && body.warnings.length > 0) {\n console.log('\\n Warnings:');\n for (const w of body.warnings) console.log(` ⚠ ${w}`);\n }\n\n if (hasFlag(argv, '--run-and-publish')) {\n console.log('\\nℹ --run-and-publish was removed. Run, approve, then promote:');\n console.log(` canary workflow run ${body.workflow.id}`);\n console.log(` canary workflow approve ${body.workflow.id} --yes`);\n console.log(` canary workflow promote ${body.workflow.id}`);\n }\n}\n\nexport const DUPLICATE_WORKFLOW_TITLE_ALLOW_FLAGS = [\n '--allow-duplicate',\n '--create-with-duplicate-title',\n] as const;\n\nexport type WorkflowTitleMatch = {\n id: string;\n name: string;\n status?: string;\n flowType?: string;\n};\n\nexport function allowsDuplicateWorkflowTitle(argv: string[]): boolean {\n return DUPLICATE_WORKFLOW_TITLE_ALLOW_FLAGS.some((flag) => hasFlag(argv, flag));\n}\n\nexport function exactWorkflowTitleMatches(\n workflows: ReadonlyArray<WorkflowTitleMatch>,\n workflowName: string\n): WorkflowTitleMatch[] {\n return workflows.filter((workflow) => workflow.name === workflowName);\n}\n\nexport function formatDuplicateWorkflowTitleWarning(opts: {\n file: string;\n workflowName: string;\n matches: ReadonlyArray<WorkflowTitleMatch>;\n}): string {\n const first = opts.matches[0];\n const stampedFile = stampedSpecPath(opts.file);\n const workflowLines = opts.matches\n .map((workflow) => {\n const details = [workflow.status, workflow.flowType].filter(Boolean).join(', ');\n return ` - ${workflow.id}${details ? ` (${details})` : ''}`;\n })\n .join('\\n');\n return [\n `Error: fresh spec compile would create a duplicate workflow named \"${opts.workflowName}\".`,\n '',\n 'Existing workflow(s):',\n workflowLines,\n '',\n 'To update an existing workflow instead, pull a stamped spec, re-apply your edits, then compile it:',\n ` canary spec pull ${first.id} --output ${stampedFile}`,\n ` canary spec compile ${stampedFile}`,\n '',\n 'To intentionally create another workflow with the same title, re-run with:',\n ` canary spec compile ${opts.file} --allow-duplicate`,\n ].join('\\n');\n}\n\nasync function fetchFreshCompileDuplicateWorkflows(opts: {\n argv: string[];\n apiUrl: string;\n token: string;\n workflowName: string;\n}): Promise<WorkflowTitleMatch[]> {\n if (allowsDuplicateWorkflowTitle(opts.argv)) return [];\n const params = new URLSearchParams({\n search: opts.workflowName,\n pageSize: '50',\n });\n const res = await fetchOrExit(`${opts.apiUrl}/workflows?${params.toString()}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${opts.token}` },\n token: opts.token,\n });\n if (!res.ok) {\n console.error(`Error: failed to check for existing workflow titles (${res.status})`);\n process.exit(1);\n }\n const body = (await res.json().catch(() => ({}))) as {\n data?: WorkflowTitleMatch[];\n };\n return exactWorkflowTitleMatches(body.data ?? [], opts.workflowName);\n}\n\nexport function stampedSpecPath(file: string): string {\n const split = splitSpecExtension(file);\n if (split) {\n return `${split.base}.stamped${split.ext}`;\n }\n return `${file}.stamped${specExtension}`;\n}\n\nasync function runRecompile(opts: {\n argv: string[];\n apiUrl: string;\n token: string;\n file: string;\n emitted: ReturnType<typeof emitSpec>;\n}): Promise<void> {\n const { argv, apiUrl, token, file, emitted } = opts;\n const workflowId = emitted.workflowId!;\n\n // 1. Fetch live workflow. GET /workflows/:id returns the workflow with\n // nodes + edges via getWorkflowWithDetails.\n const liveRes = await fetchOrExit(`${apiUrl}/workflows/${workflowId}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!liveRes.ok) {\n console.error(`Error: failed to fetch workflow ${workflowId} (${liveRes.status})`);\n process.exit(1);\n }\n const liveBody = (await liveRes.json().catch(() => ({}))) as {\n workflow?: LiveWorkflow & { snapshotVersion?: number };\n };\n const live = liveBody.workflow;\n if (!live) {\n console.error(`Error: workflow ${workflowId} not found (or no permission).`);\n process.exit(1);\n }\n\n // 2. Compute the diff.\n let diff: DiffResult;\n try {\n diff = diffWorkflow(emitted, live);\n } catch (err) {\n console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n\n if (!hasAnyChange(diff)) {\n console.log(`✓ ${file} — no changes (workflow ${workflowId} already in sync)`);\n return;\n }\n\n // 3. Apply.\n const ctx = { apiUrl, token, workflowId };\n const sync = await applyDiff(ctx, diff, emitted, live);\n\n // 4. Write back any new @id comments.\n if (sync.created.length > 0) {\n try {\n const source = await fs.readFile(file, 'utf-8');\n const next = writeNodeIds(source, sync.created);\n if (next !== source) await fs.writeFile(file, next, 'utf-8');\n } catch (err) {\n console.warn(\n `⚠ Recompile applied, but failed to write new @id markers to ${file}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n\n // 5. Summary.\n const s = summarize(diff);\n const appUrl = resolveAppUrl(apiUrl);\n console.log(`\\n✓ Recompiled \"${live.name}\"`);\n console.log(\n ` +${s.created} created · ~${s.updated} updated · -${s.deleted} deleted · ↻ ${s.moved} moved`\n );\n console.log(` URL: ${appUrl}/flows/${workflowId}`);\n if (diff.nameDrift) {\n console.log(` ✎ renamed: \"${diff.nameDrift.from}\" → \"${diff.nameDrift.to}\"`);\n }\n if (live.promotion === 'promoted') {\n console.log(\n ` ⚠ workflow is promoted — suites run the live definition, so these changes take effect on the next suite run.`\n );\n }\n if (diff.duplicateSpecIds.length > 0) {\n console.log(\n ` ⚠ duplicate @id in spec (treated as fresh creates): ${diff.duplicateSpecIds.join(', ')}`\n );\n }\n if (diff.orphanedSpecIds.length > 0) {\n console.log(\n ` ⚠ stale @id in spec (no matching node — rebound to new): ${diff.orphanedSpecIds.join(', ')}`\n );\n }\n\n if (hasFlag(argv, '--json')) {\n console.log(JSON.stringify({ ok: true, workflowId, summary: s, diff }, null, 2));\n }\n}\n\ntype LoadResult =\n | { ok: true; ast: SpecAst; issues: SpecIssue[] }\n | { ok: false; issues: SpecIssue[] };\n\nasync function loadAndValidate(file: string): Promise<LoadResult> {\n let source: string;\n try {\n source = await fs.readFile(file, 'utf-8');\n } catch (err) {\n return {\n ok: false,\n issues: [\n {\n severity: 'error',\n message: `Could not read ${file}: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n };\n }\n const parsed = parseSpec(source);\n if (!parsed.ok) return { ok: false, issues: parsed.issues };\n const lintIssues = lintSpec(parsed.ast);\n const allIssues = [...lintIssues];\n const hasError = allIssues.some((i) => i.severity === 'error');\n if (hasError) return { ok: false, issues: allIssues };\n return { ok: true, ast: parsed.ast, issues: allIssues };\n}\n\n/** Kebab-case slug for the default pull filename. */\nfunction slugify(name: string): string {\n const slug = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 60);\n return slug || 'workflow';\n}\n\nfunction printIssues(issues: SpecIssue[], file: string): void {\n if (issues.length === 0) return;\n for (const i of issues) {\n const prefix = i.severity === 'error' ? '✗ error' : '⚠ warn';\n const loc = i.line ? `${file}:${i.line}` : file;\n console.error(`${prefix} ${loc}: ${i.message}`);\n if (i.hint) console.error(` hint: ${i.hint}`);\n }\n}\n\nexport { handleValidate, handleCompile };\n\n// Used by tests.\nexport { emitSpec, lintSpec, parseSpec };\n","/**\n * Apply a DiffResult to a live workflow via the existing REST endpoints\n * (`POST /workflows/:id/nodes`, `PUT /workflows/:id/nodes/:nodeId`,\n * `POST /workflows/:id/nodes/bulk-delete`, edge routes, `PUT /workflows/:id`).\n *\n * Returns the set of `(sourceLine → newNodeId)` pairs the writeback step\n * needs to stamp the spec file with fresh `@id` comments.\n *\n * @module spec/sync\n */\n\nimport { fetchOrExit } from '../api-fetch.js';\nimport {\n reconcileLinearEdges,\n type DiffResult,\n type EmittedWorkflow,\n type LiveEdge,\n type LiveWorkflow,\n type SpecOp,\n type WorkflowFieldChanges,\n} from '@chatsdet/canary-spec';\n\nexport type SyncContext = {\n apiUrl: string;\n token: string;\n workflowId: string;\n};\n\nexport type SyncResult = {\n /** New nodes that were created — caller uses these to write `@id` back to the spec. */\n created: Array<{ sourceLine: number; nodeId: string }>;\n};\n\nexport async function applyDiff(\n ctx: SyncContext,\n diff: DiffResult,\n emitted: EmittedWorkflow,\n live: LiveWorkflow,\n): Promise<SyncResult> {\n // 1. Bulk-delete removed nodes (reconnect: true so adjacent edges heal).\n const deletes = diff.ops.filter((o): o is Extract<SpecOp, { kind: 'delete' }> => o.kind === 'delete');\n if (deletes.length > 0) {\n await postJson(ctx, `/workflows/${ctx.workflowId}/nodes/bulk-delete`, {\n nodeIds: deletes.map((o) => o.nodeId),\n reconnect: true,\n });\n }\n\n // 2. Create new nodes. The route assigns final orderIndex, but we send the\n // desired orderIndex so it lands at the right position. We need final IDs\n // back for both writeback and downstream edge reconciliation.\n const creates = diff.ops.filter((o): o is Extract<SpecOp, { kind: 'create' }> => o.kind === 'create');\n const createdIds = new Map<number, string>(); // specIndex → new node UUID\n const writebackPairs: SyncResult['created'] = [];\n for (const op of creates) {\n const res = await postJson<{ ok?: boolean; node?: { id: string }; error?: string }>(\n ctx,\n `/workflows/${ctx.workflowId}/nodes`,\n {\n nodeType: op.nodeType,\n configJson: op.configJson,\n orderIndex: op.orderIndex,\n },\n );\n const created = res.node;\n if (!created) {\n throw new Error(\n `Failed to create node at orderIndex ${op.orderIndex}: ${res.error ?? JSON.stringify(res)}`,\n );\n }\n createdIds.set(op.specIndex, created.id);\n if (op.sourceLine !== undefined) {\n writebackPairs.push({ sourceLine: op.sourceLine, nodeId: created.id });\n }\n }\n\n // 3. Update existing nodes whose config or orderIndex changed.\n const updates = diff.ops.filter((o): o is Extract<SpecOp, { kind: 'update' }> => o.kind === 'update');\n for (const op of updates) {\n if (!op.changes.configChanged && !op.changes.orderChanged) continue;\n await putJson(ctx, `/workflows/${ctx.workflowId}/nodes/${op.nodeId}`, {\n ...(op.changes.configChanged ? { configJson: op.configJson } : {}),\n ...(op.changes.orderChanged ? { orderIndex: op.orderIndex } : {}),\n });\n }\n\n // 4. Edge reconciliation. Spec authors a single linear default chain; we\n // re-derive the expected edges from final node order and DELETE+POST to\n // converge. Non-default edges (condition then/else) are left alone.\n if (diff.ops.length > 0) {\n const finalNodeIds = computeFinalNodeOrder(diff, live, createdIds);\n const fresh = await getJson<{ workflow?: { edges?: LiveEdge[] } }>(\n ctx,\n `/workflows/${ctx.workflowId}`,\n );\n const liveEdges = fresh.workflow?.edges ?? live.edges ?? [];\n const { ops: edgeOps } = reconcileLinearEdges(finalNodeIds, liveEdges);\n for (const op of edgeOps) {\n if (op.kind === 'delete') {\n await deleteJson(ctx, `/workflows/${ctx.workflowId}/edges/${op.edgeId}`);\n } else {\n await postJson(ctx, `/workflows/${ctx.workflowId}/edges`, {\n sourceNodeId: op.sourceNodeId,\n targetNodeId: op.targetNodeId,\n edgeType: 'default',\n });\n }\n }\n }\n\n // 5. Workflow-level fields. Some fields require name → id resolution against\n // org/property scope (tags, environments, sandboxTemplate); those land in\n // a follow-up PR. Surface a clear error rather than silently dropping the\n // edit, so the author isn't surprised by missing changes.\n rejectUnsupportedFieldEdits(diff.workflowFieldChanges);\n if (Object.keys(diff.workflowFieldChanges).length > 0) {\n const payload = await resolveFieldUpdatePayload(ctx, diff.workflowFieldChanges);\n await putJson(ctx, `/workflows/${ctx.workflowId}`, payload);\n }\n\n // 6. Optional api_sequence DSL upsert. The spec carries the full\n // ApiSequenceConfig in a YAML sidecar referenced from any api_sequence\n // node; here we POST it to `/api-sequences/save`, which upserts the\n // workflow-scoped row keyed by workflowId. Skipping when the sidecar is\n // absent leaves any existing live config untouched (no implicit clear).\n if (emitted.apiSequenceConfig) {\n await postJson(ctx, `/api-sequences/save`, {\n workflowId: ctx.workflowId,\n config: emitted.apiSequenceConfig,\n });\n }\n\n return { created: writebackPairs };\n}\n\n/**\n * Look up a teardown workflow by name in the org. Matches on (name, flowType\n * === 'teardown'); throws when missing so the author gets a clear \"compile the\n * teardown spec first\" message instead of a silent FK clear.\n */\nasync function resolveTeardownWorkflowId(\n ctx: SyncContext,\n specName: string,\n): Promise<string> {\n const res = await getJson<{\n ok?: boolean;\n data?: Array<{ id: string; name: string; flowType: string }>;\n }>(\n ctx,\n `/workflows?search=${encodeURIComponent(specName)}&flowType=teardown&pageSize=50`,\n );\n const match = (res.data ?? []).find(\n (w) => w.name === specName && w.flowType === 'teardown',\n );\n if (!match) {\n throw new Error(\n `Teardown workflow \"${specName}\" not found (flowType=teardown). ` +\n `Compile the teardown spec first so the seed can link to it by name.`,\n );\n }\n return match.id;\n}\n\n/**\n * Map name-form workflow fields to their wire shape. Currently only teardown\n * and seedOutputs need lookup; tags/environments/sandboxTemplate are gated by\n * `rejectUnsupportedFieldEdits` above and never reach here.\n */\nasync function resolveFieldUpdatePayload(\n ctx: SyncContext,\n changes: WorkflowFieldChanges,\n): Promise<Record<string, unknown>> {\n const payload = fieldUpdatePayload(changes);\n // teardownSpec carries the name; the API expects teardownWorkflowId.\n if ('teardownSpec' in payload) {\n const specName = payload.teardownSpec;\n delete payload.teardownSpec;\n if (specName === null) {\n payload.teardownWorkflowId = null;\n } else if (typeof specName === 'string') {\n payload.teardownWorkflowId = await resolveTeardownWorkflowId(ctx, specName);\n }\n }\n // teardownSnapshotVersion: 'latest' → null on the wire (DB convention).\n if ('teardownSnapshotVersion' in payload) {\n if (payload.teardownSnapshotVersion === 'latest') payload.teardownSnapshotVersion = null;\n }\n // seedOutputs: rename to `seedOutputsJson` and resolve propertyName → id.\n // The route's normalizeSeedOutputs accepts entries with propertyId set;\n // we ferry across credentialOutput/attributes verbatim per the diff layer.\n if ('seedOutputs' in payload) {\n const inputRows = payload.seedOutputs as\n | Array<{\n dataKey: string;\n label?: string;\n description?: string;\n propertyName?: string;\n credentialOutput?: unknown;\n attributes?: unknown;\n }>\n | undefined;\n delete payload.seedOutputs;\n if (inputRows) {\n const propIds = await resolvePropertyNameMap(\n ctx,\n Array.from(new Set(inputRows.map((r) => r.propertyName).filter((p): p is string => !!p))),\n );\n payload.seedOutputsJson = inputRows.map((r) => {\n const propertyId = r.propertyName ? propIds.get(r.propertyName) : undefined;\n if (r.propertyName && !propertyId) {\n throw new Error(\n `Declared output \"${r.dataKey}\": property \"${r.propertyName}\" not found in this org.`,\n );\n }\n return {\n dataKey: r.dataKey,\n name: r.dataKey,\n displayLabel: r.label ?? r.dataKey,\n ...(r.description ? { description: r.description } : {}),\n ...(propertyId ? { propertyId } : {}),\n ...(r.credentialOutput !== undefined ? { credentialOutput: r.credentialOutput } : {}),\n ...(r.attributes !== undefined ? { attributes: r.attributes } : {}),\n };\n });\n }\n }\n return payload;\n}\n\n/**\n * Look up property ids by name in a single round trip. Used for seed-output\n * declarations where the spec author writes `property: <name>` and sync needs\n * the propertyId for the wire payload.\n */\nasync function resolvePropertyNameMap(\n ctx: SyncContext,\n names: string[],\n): Promise<Map<string, string>> {\n const out = new Map<string, string>();\n if (names.length === 0) return out;\n const res = await getJson<{\n ok?: boolean;\n data?: Array<{ id: string; name: string }>;\n properties?: Array<{ id: string; name: string }>;\n }>(ctx, `/org/properties`);\n const all = res.data ?? res.properties ?? [];\n for (const want of names) {\n const match = all.find((p) => p.name === want);\n if (match) out.set(want, match.id);\n }\n return out;\n}\n\nexport class UnsupportedFieldEditError extends Error {\n constructor(fields: string[]) {\n super(\n `Editing ${fields.join(', ')} via a spec file is not supported yet. ` +\n `These fields require name → id resolution that lands in a follow-up. ` +\n `Edit them in the Canary UI for now; the rest of your changes will still apply.`,\n );\n this.name = 'UnsupportedFieldEditError';\n }\n}\n\nexport function rejectUnsupportedFieldEdits(changes: WorkflowFieldChanges): void {\n const unsupported = (['tags', 'environmentConstraints', 'sandboxTemplate'] as const).filter(\n (k) => changes[k] !== undefined,\n );\n if (unsupported.length > 0) throw new UnsupportedFieldEditError(unsupported);\n}\n\n/**\n * Build the final ordered list of node UUIDs after applying the diff —\n * positions correspond to the spec's order, IDs resolved from either the live\n * workflow (for UPDATE) or the new-node map (for CREATE).\n */\nfunction computeFinalNodeOrder(\n diff: DiffResult,\n live: LiveWorkflow,\n createdIds: Map<number, string>,\n): string[] {\n const liveById = new Map(live.nodes.map((n) => [n.id, n] as const));\n const total = diff.ops.filter((o) => o.kind !== 'delete').length;\n const result: string[] = Array.from({ length: total });\n for (const op of diff.ops) {\n if (op.kind === 'create') {\n const id = createdIds.get(op.specIndex);\n if (!id) throw new Error(`Missing created node id for specIndex ${op.specIndex}`);\n result[op.specIndex] = id;\n } else if (op.kind === 'update') {\n if (!liveById.has(op.nodeId)) {\n throw new Error(`UPDATE references nodeId ${op.nodeId} not in live workflow`);\n }\n result[op.specIndex] = op.nodeId;\n }\n }\n return result;\n}\n\nfunction fieldUpdatePayload(changes: WorkflowFieldChanges): Record<string, unknown> {\n // PUT /workflows/:id accepts each field independently; pass through only\n // what changed. Scenario `skipReason` is spec-source-only — the API's\n // `normalizeScenarios` strips it, but stripping here keeps the wire payload\n // honest and the round-trip test deterministic.\n const payload: Record<string, unknown> = {};\n for (const k of Object.keys(changes) as (keyof WorkflowFieldChanges)[]) {\n if (k === 'scenariosJson' && Array.isArray(changes.scenariosJson)) {\n payload.scenariosJson = (changes.scenariosJson as Array<Record<string, unknown>>).map((s) => {\n const { skipReason: _skip, ...rest } = s;\n return rest;\n });\n } else {\n payload[k] = changes[k];\n }\n }\n return payload;\n}\n\nasync function postJson<T = unknown>(\n ctx: SyncContext,\n path: string,\n body: unknown,\n): Promise<T> {\n const res = await fetchOrExit(`${ctx.apiUrl}${path}`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${ctx.token}`,\n 'Content-Type': 'application/json',\n },\n token: ctx.token,\n body: JSON.stringify(body),\n });\n return (await res.json()) as T;\n}\n\nasync function putJson<T = unknown>(\n ctx: SyncContext,\n path: string,\n body: unknown,\n): Promise<T> {\n const res = await fetchOrExit(`${ctx.apiUrl}${path}`, {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${ctx.token}`,\n 'Content-Type': 'application/json',\n },\n token: ctx.token,\n body: JSON.stringify(body),\n });\n return (await res.json()) as T;\n}\n\nasync function getJson<T = unknown>(ctx: SyncContext, path: string): Promise<T> {\n const res = await fetchOrExit(`${ctx.apiUrl}${path}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${ctx.token}` },\n token: ctx.token,\n });\n return (await res.json()) as T;\n}\n\nasync function deleteJson<T = unknown>(ctx: SyncContext, path: string): Promise<T> {\n const res = await fetchOrExit(`${ctx.apiUrl}${path}`, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${ctx.token}` },\n token: ctx.token,\n });\n return (await res.json()) as T;\n}\n"],"mappings":"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","names":["path"]}
|