@com55/node-red-ai-access 0.2.0-beta.6 → 0.2.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/README.md +90 -10
- package/ai-access.html +1 -1
- package/dist/plugin.cjs +678 -407
- package/dist/plugin.d.cts +107 -6
- package/docs/api/openapi.yaml +325 -42
- package/docs/errors.md +17 -6
- package/docs/mcp-tools.md +20 -7
- package/package.json +1 -1
package/docs/api/openapi.yaml
CHANGED
|
@@ -2,7 +2,7 @@ openapi: 3.1.0
|
|
|
2
2
|
|
|
3
3
|
info:
|
|
4
4
|
title: Node-RED AI Access Plugin API
|
|
5
|
-
version: 0.
|
|
5
|
+
version: 0.2.0-beta.8
|
|
6
6
|
summary: Temporary, owner-approved Node-RED access for AI clients
|
|
7
7
|
description: |
|
|
8
8
|
REST surface exposed by the `@com55/node-red-ai-access` Node-RED plugin
|
|
@@ -10,8 +10,8 @@ info:
|
|
|
10
10
|
|
|
11
11
|
**Git mental model:** a proposal is like a pull request. Human approve =
|
|
12
12
|
merge; reject = close; withdraw = the author closes their own PR. If `rev` /
|
|
13
|
-
`expectedRev` does not match,
|
|
14
|
-
|
|
13
|
+
`expectedRev` does not match, open proposals auto-rebase; only overlapping
|
|
14
|
+
field changes block approval. Read `rebase.state` to assess readiness. Every access link can see all proposals
|
|
15
15
|
(`mine: true|false`).
|
|
16
16
|
|
|
17
17
|
## Base path
|
|
@@ -173,7 +173,7 @@ info:
|
|
|
173
173
|
(`invalid_filter` for list filters, `invalid_arguments` otherwise).
|
|
174
174
|
|
|
175
175
|
On submit and on **get** of a **pending** proposal, responses may include
|
|
176
|
-
`conflictingProposals` (
|
|
176
|
+
`conflictingProposals` (open proposals with overlapping node edits)
|
|
177
177
|
with optional `overlappingNodeIds` / `overlappingFields`. List endpoints
|
|
178
178
|
omit `conflictingProposals`. Submit, get, and list responses include
|
|
179
179
|
`autoClosedCount` when that request auto-closed pending proposals as
|
|
@@ -184,17 +184,25 @@ info:
|
|
|
184
184
|
`N validation errors` and every entry in `errors[]`. Duplicate node ids in a
|
|
185
185
|
full tab or batch return `duplicate_node_id`.
|
|
186
186
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
187
|
+
Open proposals (`pending` or `conflict`) auto-rebase when live revisions change.
|
|
188
|
+
Disjoint node and tab fields merge automatically. Only fields changed on both
|
|
189
|
+
sides conflict (even identical edits); x/y never conflict and proposed positions
|
|
190
|
+
win ties. Env merges by unique name and entry field, credentials by key.
|
|
191
|
+
Duplicate env names fall back to atomic comparison with ambiguity warnings.
|
|
192
|
+
Conflicts block approval until resolved. Proposals expire only after the
|
|
193
|
+
configured `proposalTtlDays` (default 7). Approval rebases other open proposals;
|
|
194
|
+
legacy expired, rejected and superseded proposals stay closed.
|
|
191
195
|
|
|
192
196
|
## Withdraw
|
|
193
197
|
|
|
194
198
|
The creating access link may withdraw its own pending proposal
|
|
195
199
|
(`POST /proposals/{id}/withdraw`, GET `…/proposals/{id}/withdraw?confirm=1`,
|
|
196
200
|
or MCP `withdraw_proposal`). GET withdraw requires `confirm=1` so prefetchers
|
|
197
|
-
cannot trigger it.
|
|
201
|
+
cannot trigger it. If the token is lost, a full (`*`) admin session may use
|
|
202
|
+
POST `/proposals/{id}/withdraw`. Other tokens remain blocked. Recovery is logged
|
|
203
|
+
as `proposal.withdrawn` with the admin identity, originalOwnerId, reason and
|
|
204
|
+
withdrawalAuthority=`admin-recovery` (owner-token withdrawals use `owner-token`).
|
|
205
|
+
Status becomes `withdrawn` (separate from admin
|
|
198
206
|
`rejected`).
|
|
199
207
|
|
|
200
208
|
## Known issues
|
|
@@ -963,10 +971,9 @@ paths:
|
|
|
963
971
|
flow tab. Does not mutate Node-RED until an admin with `*` approves.
|
|
964
972
|
|
|
965
973
|
Include `expectedRev` (preferred; `expected_rev` accepted as alias)
|
|
966
|
-
from
|
|
967
|
-
|
|
968
|
-
`version_mismatch
|
|
969
|
-
proposal `rejected` with a resubmit message (no deploy).
|
|
974
|
+
from `GET /flows` or `GET /flows/{id}`. Recent known stale revisions are
|
|
975
|
+
accepted using snapshot history and rebased immediately. Unknown revisions
|
|
976
|
+
return **409** `version_mismatch`. Approval rechecks against the live revision.
|
|
970
977
|
|
|
971
978
|
Optional `title` (≤80 chars) is stored as a human-friendly label; when
|
|
972
979
|
omitted it is auto-generated from the change set. Responses always
|
|
@@ -1055,14 +1062,18 @@ paths:
|
|
|
1055
1062
|
(long text fields listed for expandable UI). Modified nodes include
|
|
1056
1063
|
line-level diffs for text fields such as `func` and `template`.
|
|
1057
1064
|
|
|
1058
|
-
`
|
|
1059
|
-
`
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1065
|
+
`baseRev` aliases `expectedRev`, the stored proposal base revision.
|
|
1066
|
+
`baseRevChanged` is true only for **open** proposals when
|
|
1067
|
+
`expectedRev` does not match the live revision (informational).
|
|
1068
|
+
This is informational after a successful rebase, not an approval error.
|
|
1069
|
+
`rebase.state` (`clean`, `merged`, `conflict`) reports merge readiness and
|
|
1070
|
+
`rebase.rev` identifies the checked live rev. Closed proposals never set
|
|
1071
|
+
`baseRevChanged` as true. Open proposals include
|
|
1072
|
+
`rebase` and `liveChanges` (editor changes since the stored base).
|
|
1073
|
+
Without conflicts, `vsCurrent` compares the merged deployment to live.
|
|
1074
|
+
Merges use raw stored proposals and live flows; AI diff sections are redacted.
|
|
1075
|
+
|
|
1076
|
+
Admins with `read` and access-link clients can diff any proposal.
|
|
1066
1077
|
operationId: getProposalDiff
|
|
1067
1078
|
security:
|
|
1068
1079
|
- AccessLinkBearer: []
|
|
@@ -1085,6 +1096,102 @@ paths:
|
|
|
1085
1096
|
'404':
|
|
1086
1097
|
$ref: '#/components/responses/NotFound'
|
|
1087
1098
|
|
|
1099
|
+
/proposals/{id}/resolve-conflict:
|
|
1100
|
+
post:
|
|
1101
|
+
tags: [Proposals]
|
|
1102
|
+
summary: Choose live or proposed for one current conflict, or undo the latest choice
|
|
1103
|
+
operationId: resolveProposalConflict
|
|
1104
|
+
security:
|
|
1105
|
+
- AdminSession: []
|
|
1106
|
+
parameters:
|
|
1107
|
+
- $ref: '#/components/parameters/ProposalId'
|
|
1108
|
+
requestBody:
|
|
1109
|
+
required: true
|
|
1110
|
+
content:
|
|
1111
|
+
application/json:
|
|
1112
|
+
schema:
|
|
1113
|
+
$ref: '#/components/schemas/ConflictChoiceRequest'
|
|
1114
|
+
responses:
|
|
1115
|
+
'200':
|
|
1116
|
+
description: Presented proposal with current rebase metadata
|
|
1117
|
+
content:
|
|
1118
|
+
application/json:
|
|
1119
|
+
schema:
|
|
1120
|
+
$ref: '#/components/schemas/Proposal'
|
|
1121
|
+
'400':
|
|
1122
|
+
$ref: '#/components/responses/BadRequest'
|
|
1123
|
+
'401':
|
|
1124
|
+
$ref: '#/components/responses/Unauthorized'
|
|
1125
|
+
'403':
|
|
1126
|
+
$ref: '#/components/responses/Forbidden'
|
|
1127
|
+
'404':
|
|
1128
|
+
description: Proposal not found
|
|
1129
|
+
'409':
|
|
1130
|
+
description: Proposal closed, conflict missing, or revision unavailable
|
|
1131
|
+
/proposals/{id}/recheck:
|
|
1132
|
+
post:
|
|
1133
|
+
tags: [Proposals]
|
|
1134
|
+
summary: Recheck an open proposal against raw live flows
|
|
1135
|
+
operationId: recheckProposal
|
|
1136
|
+
security:
|
|
1137
|
+
- AdminSession: []
|
|
1138
|
+
parameters:
|
|
1139
|
+
- $ref: '#/components/parameters/ProposalId'
|
|
1140
|
+
responses:
|
|
1141
|
+
'200':
|
|
1142
|
+
description: Presented proposal with current rebase metadata
|
|
1143
|
+
content:
|
|
1144
|
+
application/json:
|
|
1145
|
+
schema:
|
|
1146
|
+
$ref: '#/components/schemas/Proposal'
|
|
1147
|
+
'400':
|
|
1148
|
+
$ref: '#/components/responses/BadRequest'
|
|
1149
|
+
'401':
|
|
1150
|
+
$ref: '#/components/responses/Unauthorized'
|
|
1151
|
+
'403':
|
|
1152
|
+
$ref: '#/components/responses/Forbidden'
|
|
1153
|
+
'404':
|
|
1154
|
+
description: Proposal not found
|
|
1155
|
+
'409':
|
|
1156
|
+
description: Proposal closed, conflict missing, or revision unavailable
|
|
1157
|
+
/proposals/{id}/resolve:
|
|
1158
|
+
post:
|
|
1159
|
+
tags: [Proposals]
|
|
1160
|
+
summary: Resolve your own open proposal with a choice or replacement payload
|
|
1161
|
+
description: |
|
|
1162
|
+
Use nodeId + choice for one current conflict (overlapping fields only;
|
|
1163
|
+
independent live and proposed edits survive). Or use expectedRev + payload:
|
|
1164
|
+
operations apply to the flow snapshot at expectedRev, not the stored
|
|
1165
|
+
proposal or merged result. Include every intended edit. Do not mix modes.
|
|
1166
|
+
operationId: resolveProposal
|
|
1167
|
+
security:
|
|
1168
|
+
- AccessLinkBearer: []
|
|
1169
|
+
parameters:
|
|
1170
|
+
- $ref: '#/components/parameters/ProposalId'
|
|
1171
|
+
requestBody:
|
|
1172
|
+
required: true
|
|
1173
|
+
content:
|
|
1174
|
+
application/json:
|
|
1175
|
+
schema:
|
|
1176
|
+
$ref: '#/components/schemas/ResolveProposalRequest'
|
|
1177
|
+
responses:
|
|
1178
|
+
'200':
|
|
1179
|
+
description: Presented proposal with current rebase metadata
|
|
1180
|
+
content:
|
|
1181
|
+
application/json:
|
|
1182
|
+
schema:
|
|
1183
|
+
$ref: '#/components/schemas/Proposal'
|
|
1184
|
+
'400':
|
|
1185
|
+
$ref: '#/components/responses/BadRequest'
|
|
1186
|
+
'401':
|
|
1187
|
+
$ref: '#/components/responses/Unauthorized'
|
|
1188
|
+
'403':
|
|
1189
|
+
$ref: '#/components/responses/Forbidden'
|
|
1190
|
+
'404':
|
|
1191
|
+
description: Proposal not found
|
|
1192
|
+
'409':
|
|
1193
|
+
description: Proposal closed, conflict missing, or revision unavailable
|
|
1194
|
+
|
|
1088
1195
|
/proposals/{id}/approve:
|
|
1089
1196
|
post:
|
|
1090
1197
|
tags: [Proposals]
|
|
@@ -1094,9 +1201,12 @@ paths:
|
|
|
1094
1201
|
Optional body `deploymentType`: `nodes` (default from settings),
|
|
1095
1202
|
`flows`, or `full`.
|
|
1096
1203
|
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1204
|
+
Auto-rebases against raw live flows and deploys the merged tab with the live rev.
|
|
1205
|
+
Conflicts return 409 `proposal_conflict` with field-name-only `conflicts`.
|
|
1206
|
+
Merged editor changes require `mergedHash` from the reviewed diff; otherwise
|
|
1207
|
+
409 `rebase_ack_required` includes the current hash. Revision races retry
|
|
1208
|
+
up to three times then return 409 `rebase_race`, leaving the proposal open.
|
|
1209
|
+
Other deploy errors mark it `failed`. Concurrent approval returns 409.
|
|
1100
1210
|
operationId: approveProposal
|
|
1101
1211
|
security:
|
|
1102
1212
|
- AdminSession: []
|
|
@@ -1179,14 +1289,16 @@ paths:
|
|
|
1179
1289
|
/proposals/{id}/withdraw:
|
|
1180
1290
|
post:
|
|
1181
1291
|
tags: [Proposals]
|
|
1182
|
-
summary: Withdraw
|
|
1292
|
+
summary: Withdraw an open proposal (owner token or full admin recovery)
|
|
1183
1293
|
description: |
|
|
1184
|
-
|
|
1185
|
-
|
|
1294
|
+
Accepts the creating access-link Bearer, or a full (`*`) admin session
|
|
1295
|
+
for lost-token recovery. Other tokens and read-only admins are forbidden.
|
|
1296
|
+
Withdraws an **open** (pending or conflict) proposal. Status becomes `withdrawn` (not `rejected`). Optional
|
|
1186
1297
|
JSON body `{ "reason": "…" }`.
|
|
1187
1298
|
operationId: withdrawProposal
|
|
1188
1299
|
security:
|
|
1189
1300
|
- AccessLinkBearer: []
|
|
1301
|
+
- AdminSession: []
|
|
1190
1302
|
parameters:
|
|
1191
1303
|
- $ref: '#/components/parameters/ProposalId'
|
|
1192
1304
|
requestBody:
|
|
@@ -1208,7 +1320,7 @@ paths:
|
|
|
1208
1320
|
'401':
|
|
1209
1321
|
$ref: '#/components/responses/Unauthorized'
|
|
1210
1322
|
'403':
|
|
1211
|
-
description: Not owned by this access link
|
|
1323
|
+
description: Not owned by this access link or admin lacks full permission
|
|
1212
1324
|
content:
|
|
1213
1325
|
application/json:
|
|
1214
1326
|
schema:
|
|
@@ -1369,8 +1481,8 @@ components:
|
|
|
1369
1481
|
code: access_link_invalid
|
|
1370
1482
|
VersionConflict:
|
|
1371
1483
|
description: |
|
|
1372
|
-
`expectedRev`
|
|
1373
|
-
(`code: version_mismatch`). Re-read flows and resubmit.
|
|
1484
|
+
`expectedRev` is unavailable in recent snapshot history and does not
|
|
1485
|
+
match the live revision (`code: version_mismatch`). Re-read flows and resubmit.
|
|
1374
1486
|
content:
|
|
1375
1487
|
application/json:
|
|
1376
1488
|
schema:
|
|
@@ -1456,6 +1568,14 @@ components:
|
|
|
1456
1568
|
description: |
|
|
1457
1569
|
Machine-readable `code` on JSON error bodies. See docs/errors.md.
|
|
1458
1570
|
|
|
1571
|
+
- `approve_queue_timeout` (504): Approval exceeded its queue-wait deadline (default 60 seconds); no deployment was started for this request
|
|
1572
|
+
- `deploy_timeout` (504): Deployment exceeded its deadline (default 60 seconds); approvals wait for settlement or the hard cap (default 5 minutes)
|
|
1573
|
+
- `proposal_conflict` (409): Editor and proposal changes conflict (conflicts contains field names only)
|
|
1574
|
+
- `rebase_ack_required` (409): The merged result needs review (mergedHash included)
|
|
1575
|
+
- `rebase_race` (409): Flows kept changing during approval
|
|
1576
|
+
- `conflict_not_found` (409): The requested conflict no longer exists
|
|
1577
|
+
- `invalid_choice` (400): Invalid manual conflict choice
|
|
1578
|
+
- `flow_id_mismatch` (400): Resolution tried to change the proposal flow id
|
|
1459
1579
|
- `access_link_missing` (401): No Bearer / path token
|
|
1460
1580
|
- `bearer_scheme_required` (401): Auth used Basic (or non-Bearer)
|
|
1461
1581
|
- `token_expired` (401): Known token past `expiresAt` (body includes `expiredAt` and `accessLinksUrl`)
|
|
@@ -1481,10 +1601,10 @@ components:
|
|
|
1481
1601
|
- `invalid_diff_format` (400): Unknown `format` on proposal diff
|
|
1482
1602
|
- `confirm_required` (400): GET withdraw without `confirm=1`
|
|
1483
1603
|
- `missing_expected_rev` (400): Proposal submitted without `expectedRev` / `rev`
|
|
1484
|
-
- `version_mismatch` (409):
|
|
1604
|
+
- `version_mismatch` (409): Unknown or unavailable `rev` / `expectedRev` snapshot
|
|
1485
1605
|
- `proposal_not_found` (404): Unknown proposal id
|
|
1486
|
-
- `proposal_not_pending` (409): Action needs
|
|
1487
|
-
- `proposal_not_owned` (403):
|
|
1606
|
+
- `proposal_not_pending` (409): Action needs an open proposal
|
|
1607
|
+
- `proposal_not_owned` (403): Resolve or withdraw another token’s proposal
|
|
1488
1608
|
- `validation_failed` (400): Several validation errors at once (message `N validation errors`)
|
|
1489
1609
|
- `duplicate_node_id` (400): The same node id appears more than once in a full tab or batch
|
|
1490
1610
|
- `missing_node_id` (400): Node / op without an id
|
|
@@ -1509,6 +1629,14 @@ components:
|
|
|
1509
1629
|
- `upstream_error` (502): Node-RED runtime/flows error
|
|
1510
1630
|
- `internal_error` (500): Unexpected server error
|
|
1511
1631
|
enum:
|
|
1632
|
+
- approve_queue_timeout
|
|
1633
|
+
- deploy_timeout
|
|
1634
|
+
- proposal_conflict
|
|
1635
|
+
- rebase_ack_required
|
|
1636
|
+
- rebase_race
|
|
1637
|
+
- conflict_not_found
|
|
1638
|
+
- invalid_choice
|
|
1639
|
+
- flow_id_mismatch
|
|
1512
1640
|
- access_link_missing
|
|
1513
1641
|
- bearer_scheme_required
|
|
1514
1642
|
- token_expired
|
|
@@ -1737,6 +1865,11 @@ components:
|
|
|
1737
1865
|
AiAccessSettingsPatch:
|
|
1738
1866
|
type: object
|
|
1739
1867
|
properties:
|
|
1868
|
+
proposalTtlDays:
|
|
1869
|
+
type: integer
|
|
1870
|
+
minimum: 1
|
|
1871
|
+
maximum: 365
|
|
1872
|
+
default: 7
|
|
1740
1873
|
enabled:
|
|
1741
1874
|
type: boolean
|
|
1742
1875
|
allowWithoutAdminAuth:
|
|
@@ -2184,9 +2317,46 @@ components:
|
|
|
2184
2317
|
additionalProperties: true
|
|
2185
2318
|
additionalProperties: true
|
|
2186
2319
|
|
|
2320
|
+
ProposalRebase:
|
|
2321
|
+
type: object
|
|
2322
|
+
description: Merge metadata; conflicts contain field names only, never field values.
|
|
2323
|
+
properties:
|
|
2324
|
+
state:
|
|
2325
|
+
type: string
|
|
2326
|
+
enum: [clean, merged, conflict]
|
|
2327
|
+
rev:
|
|
2328
|
+
type: string
|
|
2329
|
+
mergedHash:
|
|
2330
|
+
type: string
|
|
2331
|
+
conflicts:
|
|
2332
|
+
type: array
|
|
2333
|
+
items:
|
|
2334
|
+
type: object
|
|
2335
|
+
properties:
|
|
2336
|
+
nodeId:
|
|
2337
|
+
type: string
|
|
2338
|
+
kind:
|
|
2339
|
+
type: string
|
|
2340
|
+
fields:
|
|
2341
|
+
type: array
|
|
2342
|
+
items:
|
|
2343
|
+
type: string
|
|
2344
|
+
message:
|
|
2345
|
+
type: string
|
|
2346
|
+
choices:
|
|
2347
|
+
type: array
|
|
2348
|
+
items:
|
|
2349
|
+
type: string
|
|
2350
|
+
enum: [live, proposed]
|
|
2351
|
+
liveChangedCount:
|
|
2352
|
+
type: integer
|
|
2353
|
+
checkedAt:
|
|
2354
|
+
type: string
|
|
2355
|
+
format: date-time
|
|
2356
|
+
|
|
2187
2357
|
ProposalStatus:
|
|
2188
2358
|
type: string
|
|
2189
|
-
enum: [pending, approved, rejected, failed, withdrawn, expired, superseded]
|
|
2359
|
+
enum: [pending, conflict, approved, rejected, failed, withdrawn, expired, superseded]
|
|
2190
2360
|
|
|
2191
2361
|
Proposal:
|
|
2192
2362
|
type: object
|
|
@@ -2195,6 +2365,7 @@ components:
|
|
|
2195
2365
|
- type
|
|
2196
2366
|
- status
|
|
2197
2367
|
- expectedRev
|
|
2368
|
+
- baseRev
|
|
2198
2369
|
- payload
|
|
2199
2370
|
- createdBy
|
|
2200
2371
|
- createdAt
|
|
@@ -2204,6 +2375,30 @@ components:
|
|
|
2204
2375
|
- reviewUrl
|
|
2205
2376
|
- reviewPath
|
|
2206
2377
|
properties:
|
|
2378
|
+
conflictChoice:
|
|
2379
|
+
type: object
|
|
2380
|
+
description: Latest manual resolution; admins can undo it until the next body edit or choice. Undo snapshots are never returned.
|
|
2381
|
+
required: [nodeId, choice]
|
|
2382
|
+
properties:
|
|
2383
|
+
nodeId:
|
|
2384
|
+
type: string
|
|
2385
|
+
choice:
|
|
2386
|
+
type: string
|
|
2387
|
+
enum: [live, proposed]
|
|
2388
|
+
deployPending:
|
|
2389
|
+
type: object
|
|
2390
|
+
description: Timed-out deployment has an unknown outcome; approvals wait until settlement or the hard cap. Check live flows before approving again.
|
|
2391
|
+
required: [mergedHash, reason]
|
|
2392
|
+
properties:
|
|
2393
|
+
mergedHash:
|
|
2394
|
+
type: string
|
|
2395
|
+
reason:
|
|
2396
|
+
type: string
|
|
2397
|
+
queueReleased:
|
|
2398
|
+
type: boolean
|
|
2399
|
+
description: True when the deploy hard cap released the queue. Late settlement no longer updates this proposal; approval always re-reads live flows.
|
|
2400
|
+
rebase:
|
|
2401
|
+
$ref: '#/components/schemas/ProposalRebase'
|
|
2207
2402
|
id:
|
|
2208
2403
|
type: string
|
|
2209
2404
|
type:
|
|
@@ -2213,6 +2408,12 @@ components:
|
|
|
2213
2408
|
$ref: '#/components/schemas/ProposalStatus'
|
|
2214
2409
|
expectedRev:
|
|
2215
2410
|
type: string
|
|
2411
|
+
baseRev:
|
|
2412
|
+
type: string
|
|
2413
|
+
description: Revision of the stored base snapshot; alias of expectedRev.
|
|
2414
|
+
baseRevChanged:
|
|
2415
|
+
type: boolean
|
|
2416
|
+
description: Informational revision difference for open proposals; use rebase.state for merge readiness.
|
|
2216
2417
|
payload:
|
|
2217
2418
|
$ref: '#/components/schemas/ReplaceFlowPayload'
|
|
2218
2419
|
baseFlow:
|
|
@@ -2318,9 +2519,8 @@ components:
|
|
|
2318
2519
|
reason:
|
|
2319
2520
|
type: string
|
|
2320
2521
|
description: |
|
|
2321
|
-
Present when rejected, failed, or
|
|
2322
|
-
|
|
2323
|
-
re-read flows and submit a new proposal.
|
|
2522
|
+
Present when rejected, failed, withdrawn, or expired. Revision changes
|
|
2523
|
+
keep open proposals pending or in conflict; they do not close them.
|
|
2324
2524
|
resultRev:
|
|
2325
2525
|
type: string
|
|
2326
2526
|
|
|
@@ -2372,7 +2572,7 @@ components:
|
|
|
2372
2572
|
type: object
|
|
2373
2573
|
required: [kind]
|
|
2374
2574
|
description: |
|
|
2375
|
-
Admin for approve/reject/fail; access-link for
|
|
2575
|
+
Admin for approve/reject/fail and recovery withdrawal; access-link for owner withdrawal.
|
|
2376
2576
|
properties:
|
|
2377
2577
|
kind:
|
|
2378
2578
|
type: string
|
|
@@ -2424,9 +2624,59 @@ components:
|
|
|
2424
2624
|
find: Hello
|
|
2425
2625
|
replace: Hi
|
|
2426
2626
|
|
|
2627
|
+
ConflictChoiceRequest:
|
|
2628
|
+
type: object
|
|
2629
|
+
required: [nodeId, choice]
|
|
2630
|
+
properties:
|
|
2631
|
+
nodeId:
|
|
2632
|
+
type: string
|
|
2633
|
+
choice:
|
|
2634
|
+
type: string
|
|
2635
|
+
enum: [live, proposed, undo]
|
|
2636
|
+
description: Undo restores the proposal before the latest choice for nodeId and rechecks against live. Available until the next body edit or choice; admin only. Nothing is deployed.
|
|
2637
|
+
|
|
2638
|
+
ResolveProposalRequest:
|
|
2639
|
+
oneOf:
|
|
2640
|
+
- type: object
|
|
2641
|
+
additionalProperties: false
|
|
2642
|
+
required: [nodeId, choice]
|
|
2643
|
+
properties:
|
|
2644
|
+
nodeId:
|
|
2645
|
+
type: string
|
|
2646
|
+
description: Current conflict node id; flow id for tab/env conflicts.
|
|
2647
|
+
choice:
|
|
2648
|
+
type: string
|
|
2649
|
+
enum: [live, proposed]
|
|
2650
|
+
description: Keep this side for overlapping fields only; preserve independent changes and live secrets.
|
|
2651
|
+
- type: object
|
|
2652
|
+
required: [expectedRev]
|
|
2653
|
+
properties:
|
|
2654
|
+
expectedRev:
|
|
2655
|
+
type: string
|
|
2656
|
+
description: Flow snapshot revision to which replacement operations apply.
|
|
2657
|
+
title:
|
|
2658
|
+
type: string
|
|
2659
|
+
maxLength: 80
|
|
2660
|
+
payload:
|
|
2661
|
+
$ref: '#/components/schemas/ReplaceFlowPayload'
|
|
2662
|
+
description: Complete intended edits based on expectedRev; not applied to the stored proposal or merged result.
|
|
2663
|
+
nodes:
|
|
2664
|
+
type: array
|
|
2665
|
+
description: Legacy alias for replacement payload nodes, applied to expectedRev.
|
|
2666
|
+
items:
|
|
2667
|
+
type: object
|
|
2668
|
+
additionalProperties: true
|
|
2669
|
+
not:
|
|
2670
|
+
anyOf:
|
|
2671
|
+
- required: [nodeId]
|
|
2672
|
+
- required: [choice]
|
|
2673
|
+
|
|
2427
2674
|
ApproveProposalRequest:
|
|
2428
2675
|
type: object
|
|
2429
2676
|
properties:
|
|
2677
|
+
mergedHash:
|
|
2678
|
+
type: string
|
|
2679
|
+
description: Hash of the reviewed merged result; required when editor changes are merged.
|
|
2430
2680
|
deploymentType:
|
|
2431
2681
|
$ref: '#/components/schemas/DeploymentType'
|
|
2432
2682
|
|
|
@@ -2536,6 +2786,11 @@ components:
|
|
|
2536
2786
|
type: object
|
|
2537
2787
|
required: [summary, added, removed, modified]
|
|
2538
2788
|
properties:
|
|
2789
|
+
tabProperties:
|
|
2790
|
+
type: array
|
|
2791
|
+
description: Before and after values of changed flow settings, including env. Uses the caller's redacted view.
|
|
2792
|
+
items:
|
|
2793
|
+
$ref: '#/components/schemas/ProposalDiffPropertyChange'
|
|
2539
2794
|
summary:
|
|
2540
2795
|
type: object
|
|
2541
2796
|
required: [added, removed, modified, tabChanges]
|
|
@@ -2572,7 +2827,8 @@ components:
|
|
|
2572
2827
|
- flowId
|
|
2573
2828
|
- expectedRev
|
|
2574
2829
|
- currentRev
|
|
2575
|
-
-
|
|
2830
|
+
- baseRevChanged
|
|
2831
|
+
- baseRev
|
|
2576
2832
|
- compareAgainst
|
|
2577
2833
|
properties:
|
|
2578
2834
|
proposalId:
|
|
@@ -2581,12 +2837,15 @@ components:
|
|
|
2581
2837
|
type: string
|
|
2582
2838
|
expectedRev:
|
|
2583
2839
|
type: string
|
|
2840
|
+
baseRev:
|
|
2841
|
+
type: string
|
|
2842
|
+
description: Alias of expectedRev, the stored base revision.
|
|
2584
2843
|
currentRev:
|
|
2585
2844
|
type: string
|
|
2586
|
-
|
|
2845
|
+
baseRevChanged:
|
|
2587
2846
|
type: boolean
|
|
2588
2847
|
description: |
|
|
2589
|
-
|
|
2848
|
+
Informational: true for open proposals when expectedRev ≠ live rev.
|
|
2590
2849
|
Never true for approved/rejected/failed.
|
|
2591
2850
|
compareAgainst:
|
|
2592
2851
|
type: string
|
|
@@ -2594,10 +2853,34 @@ components:
|
|
|
2594
2853
|
baseSnapshotMissing:
|
|
2595
2854
|
type: boolean
|
|
2596
2855
|
description: True for legacy proposals without a stored baseFlow
|
|
2856
|
+
rebase:
|
|
2857
|
+
$ref: '#/components/schemas/ProposalRebase'
|
|
2858
|
+
conflictValues:
|
|
2859
|
+
type: array
|
|
2860
|
+
description: Base, live and proposed values for each conflicting path. AI callers receive redacted values.
|
|
2861
|
+
items:
|
|
2862
|
+
type: object
|
|
2863
|
+
required: [nodeId, fields]
|
|
2864
|
+
properties:
|
|
2865
|
+
nodeId:
|
|
2866
|
+
type: string
|
|
2867
|
+
fields:
|
|
2868
|
+
type: array
|
|
2869
|
+
items:
|
|
2870
|
+
type: object
|
|
2871
|
+
required: [field]
|
|
2872
|
+
properties:
|
|
2873
|
+
field:
|
|
2874
|
+
type: string
|
|
2875
|
+
base: {}
|
|
2876
|
+
live: {}
|
|
2877
|
+
proposed: {}
|
|
2878
|
+
liveChanges:
|
|
2879
|
+
$ref: '#/components/schemas/ProposalDiffSections'
|
|
2597
2880
|
vsCurrent:
|
|
2598
2881
|
$ref: '#/components/schemas/ProposalDiffSections'
|
|
2599
2882
|
description: |
|
|
2600
|
-
Present
|
|
2883
|
+
Present for open proposals without conflicts — merged result vs live flow.
|
|
2601
2884
|
|
|
2602
2885
|
AuditActor:
|
|
2603
2886
|
oneOf:
|
package/docs/errors.md
CHANGED
|
@@ -13,6 +13,14 @@ GET-link `401` responses include a `WWW-Authenticate: Bearer …` header.
|
|
|
13
13
|
|
|
14
14
|
| Code | HTTP | Meaning | Hint / next step | Channels |
|
|
15
15
|
| --- | --- | --- | --- | --- |
|
|
16
|
+
| `approve_queue_timeout` | 504 | Approval exceeded its queue-wait deadline (default 60 seconds); no deployment was started for this request | Check the live flow and retry approval | admin |
|
|
17
|
+
| `deploy_timeout` | 504 | Deployment exceeded its deadline (default 60 seconds); approvals wait for settlement or the hard cap (default 5 minutes) | Before the cap, late settlement updates the proposal; after the cap, deployPending.queueReleased is true and the outcome stays unknown. Check the live flow before approving again; approval always re-reads live | admin |
|
|
18
|
+
| `proposal_conflict` | 409 | Editor and proposal changes conflict (conflicts contains field names only) | Read the current flow and resolve the proposal before approval | GET, REST, MCP, admin |
|
|
19
|
+
| `rebase_ack_required` | 409 | The merged result needs review (mergedHash included) | Review the diff and approve with its mergedHash | admin |
|
|
20
|
+
| `rebase_race` | 409 | Flows kept changing during approval | Recheck the proposal and retry approval | admin |
|
|
21
|
+
| `conflict_not_found` | 409 | The requested conflict no longer exists | Reload the proposal and its conflicts | REST, MCP, admin |
|
|
22
|
+
| `invalid_choice` | 400 | Invalid manual conflict choice | Use nodeId plus an available live or proposed choice, without replacement payload or revision | REST, MCP, admin |
|
|
23
|
+
| `flow_id_mismatch` | 400 | Resolution tried to change the proposal flow id | Keep the same flowId or submit a new proposal | REST, MCP |
|
|
16
24
|
| `access_link_missing` | 401 | No Bearer / path token | Ask the user for a new access link of the right kind | GET, REST, MCP |
|
|
17
25
|
| `bearer_scheme_required` | 401 | Auth used Basic (or non-Bearer) | Send `Authorization: Bearer <token>` | REST, MCP |
|
|
18
26
|
| `token_expired` | 401 | Known token past `expiresAt` (body includes `expiredAt` and `accessLinksUrl`) | Ask the user to create a new link on the `/ai-access` dashboard → Access tokens (`accessLinksUrl`) | GET, REST, MCP |
|
|
@@ -38,10 +46,10 @@ GET-link `401` responses include a `WWW-Authenticate: Bearer …` header.
|
|
|
38
46
|
| `invalid_diff_format` | 400 | Unknown `format` on proposal diff | Use `format=summary` (default) or `format=full` | GET, REST, MCP |
|
|
39
47
|
| `confirm_required` | 400 | GET withdraw without `confirm=1` | Repeat the request with `confirm=1` | GET |
|
|
40
48
|
| `missing_expected_rev` | 400 | Proposal submitted without `expectedRev` / `rev` | Read the flow and pass its `rev` | GET, REST, MCP |
|
|
41
|
-
| `version_mismatch` | 409 |
|
|
49
|
+
| `version_mismatch` | 409 | Unknown or unavailable `rev` / `expectedRev` snapshot | Re-read flows and resubmit | GET, REST, MCP |
|
|
42
50
|
| `proposal_not_found` | 404 | Unknown proposal id | List proposals and use an existing id | GET, REST, MCP |
|
|
43
|
-
| `proposal_not_pending` | 409 | Action needs
|
|
44
|
-
| `proposal_not_owned` | 403 |
|
|
51
|
+
| `proposal_not_pending` | 409 | Action needs an open proposal | Only pending or conflict proposals can withdraw / decide | GET, REST, MCP |
|
|
52
|
+
| `proposal_not_owned` | 403 | Resolve or withdraw another token’s proposal | Use the creating token; lost-token withdrawal requires a full admin session | GET, REST, MCP |
|
|
45
53
|
| `validation_failed` | 400 | Several validation errors at once (message `N validation errors`) | Fix every entry in errors[] and resubmit. | GET, REST, MCP |
|
|
46
54
|
| `duplicate_node_id` | 400 | The same node id appears more than once in a full tab or batch | Give every node a unique id | GET, REST, MCP |
|
|
47
55
|
| `missing_node_id` | 400 | Node / op without an id | Include a non-empty string id | GET, REST, MCP |
|
|
@@ -72,6 +80,8 @@ Returned in `warnings[]` on a successful propose/submit; they never block.
|
|
|
72
80
|
|
|
73
81
|
| Code | HTTP | Meaning | Hint / next step | Channels |
|
|
74
82
|
| --- | --- | --- | --- | --- |
|
|
83
|
+
| `duplicate_env_keys` | 200 | Changed env arrays contain duplicate names; pairing by name is ambiguous when entries are reordered | Review the env values before approval | GET, REST, MCP, admin |
|
|
84
|
+
| `secret_may_revert` | 200 | Secret spans could not be aligned during proposed conflict resolution; proposed values were kept | Review the warned field against live flows before approval; an editor secret change may be reverted | GET, REST, MCP, admin |
|
|
75
85
|
| `missing_config_node` | 201 | Warning: node has no (or an unresolved) config node yet | Configure before deploy if needed; see `candidates` | GET, REST, MCP |
|
|
76
86
|
| `unwired_node` | 201 | Warning: node has outputs but no wires | Wire it, or ignore while scaffolding | GET, REST, MCP |
|
|
77
87
|
| `incomplete_wires` | 201 | Warning: fewer wire ports than outputs | Add ports if outputs should connect | GET, REST, MCP |
|
|
@@ -83,9 +93,10 @@ Returned in `warnings[]` on a successful propose/submit; they never block.
|
|
|
83
93
|
| `secret_tab_changed` | 201 | Warning: proposal with restored secrets also changes other nodes in the tab | Review every change before approving | GET, REST, MCP |
|
|
84
94
|
| `secret_reference_added` | 201 | Warning: an added/changed node now reads a secret-named env/global/flow value (env.get, $env, ${…}, typed input) | Reviewer must check the value is not sent anywhere unexpected | GET, REST, MCP |
|
|
85
95
|
|
|
86
|
-
##
|
|
96
|
+
## Proposal statuses
|
|
87
97
|
|
|
88
98
|
| Code | HTTP | Meaning | Hint / next step | Channels |
|
|
89
99
|
| --- | --- | --- | --- | --- |
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
100
|
+
| `conflict` | 200 | Status: open proposal has conflicting editor changes; approval blocked | Read the current flow and resolve the proposal | GET, REST, MCP |
|
|
101
|
+
| `expired` | 200 | Status: open proposal expired after the configured TTL (default 7 days) | Re-read the flow and submit a new proposal | GET, REST, MCP |
|
|
102
|
+
| `superseded` | 200 | Legacy closed status; new approvals rebase other open proposals | Re-read the flow and submit a new proposal if still needed | GET, REST, MCP |
|