@fieldwangai/agentflow 0.1.142 → 0.1.144
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/README.md +8 -0
- package/README.zh-CN.md +8 -0
- package/bin/lib/auth.mjs +52 -0
- package/bin/lib/catalog-flows.mjs +27 -2
- package/bin/lib/flow-static-preview.mjs +104 -0
- package/bin/lib/help.mjs +2 -0
- package/bin/lib/main.mjs +64 -0
- package/bin/lib/ui-server.mjs +74 -11
- package/builtin/web-ui/dist/assets/{WorkflowAssistantThread-DGaXikJ9.js → WorkflowAssistantThread-CnqBhGXJ.js} +1 -1
- package/builtin/web-ui/dist/assets/index-5wTTj_jN.css +1 -0
- package/builtin/web-ui/dist/assets/index-BnXKlBQj.js +889 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/skills/agentflow-author-flow/SKILL.md +49 -0
- package/skills/agentflow-author-flow/agents/openai.yaml +4 -0
- package/skills/agentflow-cli/SKILL.md +20 -2
- package/skills/agentflow-cli/scripts/agentflow-cli.mjs +126 -3
- package/skills/agentflow-cli/scripts/workflow-report-client.mjs +2 -1
- package/skills/agentflow-workflow-report/SKILL.md +3 -0
- package/skills/agentflow-workflow-report/references/protocol.md +16 -4
- package/builtin/web-ui/dist/assets/index-CtG65J3e.css +0 -1
- package/builtin/web-ui/dist/assets/index-pLsH0Uyj.js +0 -835
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0"
|
|
16
16
|
rel="stylesheet"
|
|
17
17
|
/>
|
|
18
|
-
<script type="module" crossorigin src="/assets/index-
|
|
19
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
18
|
+
<script type="module" crossorigin src="/assets/index-BnXKlBQj.js"></script>
|
|
19
|
+
<link rel="stylesheet" crossorigin href="/assets/index-5wTTj_jN.css">
|
|
20
20
|
</head>
|
|
21
21
|
<body>
|
|
22
22
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fieldwangai/agentflow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.144",
|
|
4
4
|
"description": "Orchestration system for long-running complex agent tasks using Cursor, OpenCode, Claude Code, or Codex as execution backends",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/agentflow.mjs",
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentflow-author-flow
|
|
3
|
+
description: Generate or revise an AgentFlow flow locally from a natural-language request, validate its flow.yaml, open a single-file platform-style preview, and publish it to personal, workspace, or team scope after user confirmation. Use when a user asks Codex, Cursor, Claude Code, or another coding agent to create, draw, preview, sync, upload, or publish an AgentFlow flow without using AI Composer or manually dragging nodes.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Author an AgentFlow
|
|
7
|
+
|
|
8
|
+
Turn the user's request into a locally reviewable Flow and handle the commands on their behalf. Do not ask the user to run preview or publish commands.
|
|
9
|
+
|
|
10
|
+
## Required references
|
|
11
|
+
|
|
12
|
+
Before creating or changing node schemas or edges, read:
|
|
13
|
+
|
|
14
|
+
- [Builtin node schemas](../agentflow-node-reference/references/builtin-nodes.md)
|
|
15
|
+
- [Control capabilities](../../reference/flow-control-capabilities.md)
|
|
16
|
+
- [Flow layout](../../reference/flow-layout.md)
|
|
17
|
+
|
|
18
|
+
Read [prompt and handler checks](../../reference/flow-prompt-handler-check.md) when the Flow contains agent nodes. Read [standard recipes](../agentflow-flow-recipes/references/recipes.md) when a recipe matches the request.
|
|
19
|
+
|
|
20
|
+
## Workflow
|
|
21
|
+
|
|
22
|
+
1. Convert the request into a short node plan. Ask only for a business choice that materially changes behavior; do not ask for YAML details.
|
|
23
|
+
2. Choose a stable lowercase flow ID using letters, digits, hyphens, or underscores.
|
|
24
|
+
3. Create the draft at `.workspace/agentflow/pipelines/<flow-id>/flow.yaml`. Start from the packaged `builtin/pipelines/new/flow.yaml` when available. Preserve any unrelated workspace files.
|
|
25
|
+
4. Build nodes from authoritative definitions. Never invent `definitionId`, slot order, slot type, handle index, or control semantics. Give every instance a unique position and keep the main path left-to-right.
|
|
26
|
+
5. Validate the draft with `agentflow validate <flow-id> --json`. In this repository, use `node bin/agentflow.mjs validate <flow-id> --json`. Fix all errors before continuing; surface warnings that affect behavior.
|
|
27
|
+
6. Generate and open the static preview with `agentflow flow preview <path-to-flow.yaml>`. In this repository, use `node bin/agentflow.mjs flow preview <path-to-flow.yaml>`. This command must exit after opening the generated `file://` HTML; do not start `agentflow ui` or another server.
|
|
28
|
+
7. Report the draft path and a compact node/edge summary, then wait for the user's visual confirmation. Do not write to the AgentFlow platform before confirmation unless the user explicitly requested direct publish without review.
|
|
29
|
+
8. Ask for `personal`, `workspace`, or `team` only if the user has not already chosen the destination. `team` means a workspace Flow shared as editor with the current account's active team.
|
|
30
|
+
9. Publish through the sibling `agentflow-cli` skill:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
node ../agentflow-cli/scripts/agentflow-cli.mjs publish-flow \
|
|
34
|
+
--flow-id <flow-id> \
|
|
35
|
+
--file <path-to-flow.yaml> \
|
|
36
|
+
--target-space <personal|workspace|team>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Resolve the script relative to this `SKILL.md`. Never expose the token.
|
|
40
|
+
10. If the server reports that the Flow already exists, stop and explain the conflict. Use `--replace` only after the user explicitly confirms updating that exact Flow and destination. The CLI reads the current revision before replacement.
|
|
41
|
+
11. Verify the published graph with `get-graph --flow-id <flow-id> --flow-source <user|workspace>` and report the platform Flow ID, scope, and result.
|
|
42
|
+
|
|
43
|
+
## Safety rules
|
|
44
|
+
|
|
45
|
+
- Treat local generation and preview as reversible; treat platform publish and replacement as external writes.
|
|
46
|
+
- Never silently convert a personal Flow into a shared Flow.
|
|
47
|
+
- Never create a temporary test Flow on the production platform merely to validate tooling. Use an isolated local AgentFlow server or mock API for integration tests.
|
|
48
|
+
- Do not run the Flow unless the user asks to execute it; publishing is not execution.
|
|
49
|
+
- Keep a failed draft on disk so it can be inspected and repaired.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "AgentFlow Flow Authoring"
|
|
3
|
+
short_description: "Generate, preview, confirm, and publish AgentFlow flows"
|
|
4
|
+
default_prompt: "Use $agentflow-author-flow to generate a local AgentFlow, open its platform-style preview, and publish it after I confirm."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agentflow-cli
|
|
3
|
-
description: Direct AgentFlow platform operation through a bundled token-backed CLI, without MCP. Use when Codex needs to list
|
|
3
|
+
description: Direct AgentFlow platform operation through a bundled token-backed CLI, without MCP. Use when Codex needs to list or publish AgentFlow flows, start or inspect runs, read graphs and logs, or fetch display outputs through AgentFlow HTTP APIs using AGENTFLOW_TOKEN from env or .env. Default AgentFlow base URL is http://ai.mengma.bigo.inner/.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# AgentFlow CLI
|
|
@@ -71,6 +71,17 @@ List flows:
|
|
|
71
71
|
node skills/agentflow-cli/scripts/agentflow-cli.mjs list-flows
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
Publish a new local Flow after the user has reviewed it:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
node skills/agentflow-cli/scripts/agentflow-cli.mjs publish-flow \
|
|
78
|
+
--flow-id release-check \
|
|
79
|
+
--file .workspace/agentflow/pipelines/release-check/flow.yaml \
|
|
80
|
+
--target-space personal
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Destinations are `personal`, `workspace`, and `team`. `team` creates a workspace Flow and shares it as editor with the current account's active team. Publishing is create-only by default. If the exact Flow already exists, stop and ask whether to update it; only after explicit confirmation rerun with `--replace`. Replacement first reads the server revision and submits it with the update.
|
|
84
|
+
|
|
74
85
|
Read one flow graph:
|
|
75
86
|
|
|
76
87
|
```bash
|
|
@@ -124,16 +135,23 @@ The reusable transport lives in `scripts/workflow-report-client.mjs`. The CLI ex
|
|
|
124
135
|
|
|
125
136
|
Every write requires the real business adapter `source`. Put the key-level `expectedVersions` map in the JSON file; use `absent` for a new resource key. `--expected-revision` is retained only for legacy whole-Workflow locking and should not be used by new integrations.
|
|
126
137
|
|
|
138
|
+
The only admin write exception is audited version-membership repair. Read its strict revision with
|
|
139
|
+
`workflow-get --runtime-only --admin-operation repair-version-membership`, then send the matching
|
|
140
|
+
`workflow-report --admin-operation repair-version-membership` request as defined by
|
|
141
|
+
`agentflow-workflow-report`. This flag does not grant general Workflow read or write access.
|
|
142
|
+
|
|
127
143
|
## Workflow
|
|
128
144
|
|
|
129
145
|
1. Check token availability with `config`.
|
|
130
|
-
2. Use `list-workspace` or `list-flows` to discover targets.
|
|
146
|
+
2. Use `list-workspace` or `list-flows` to discover targets. Use `publish-flow` only after a local Flow has passed validation and the user has confirmed the preview.
|
|
131
147
|
3. Use `run` to start the flow. If the task needs the generated page/text, inspect returned `displayOutputs` or call `display-outputs`.
|
|
132
148
|
4. Use `status`, `list-run-by-workspace`, and `logs` when a run is active, failed, or needs debugging.
|
|
133
149
|
|
|
134
150
|
## Failure Handling
|
|
135
151
|
|
|
136
152
|
- If the CLI says the token is missing, ask the user to set `AGENTFLOW_TOKEN` in env or `.env`.
|
|
153
|
+
- If `publish-flow` returns 409, do not add `--replace` automatically. Ask the user to confirm updating the existing Flow.
|
|
154
|
+
- If team publishing says no active team is assigned, keep the local draft and ask the user to choose personal/workspace or have an admin assign the account to a team.
|
|
137
155
|
- If the API returns 401/403, do not retry with a printed token. Ask the user to refresh the token.
|
|
138
156
|
- If `run` fails because a flow is already running, call `status` and `list-run-by-workspace` before retrying.
|
|
139
157
|
- If local debugging is needed, override `AGENTFLOW_BASE_URL`; otherwise keep the default internal URL.
|
|
@@ -33,6 +33,7 @@ Commands:
|
|
|
33
33
|
config
|
|
34
34
|
list-workspace | list-workspaces
|
|
35
35
|
list-flows
|
|
36
|
+
publish-flow --flow-id <id> --file <flow.yaml> [--target-space personal|workspace|team] [--replace]
|
|
36
37
|
get-graph --flow-id <id> [--flow-source user]
|
|
37
38
|
run --flow-id <id> [--flow-source user] [--run-node-id <id>] [--input k=v]
|
|
38
39
|
status --flow-id <id> [--flow-source user]
|
|
@@ -41,9 +42,9 @@ Commands:
|
|
|
41
42
|
logs --run-id <id>
|
|
42
43
|
display-outputs --flow-id <id> [--flow-source user]
|
|
43
44
|
sync-workspace --workspace <id>
|
|
44
|
-
workflow-get --workflow tapd:<id> [--flow-id <id>] [--runtime-only]
|
|
45
|
+
workflow-get --workflow tapd:<id> [--flow-id <id>] [--runtime-only] [--admin-operation repair-version-membership]
|
|
45
46
|
workflow-access-sync --workflow tapd:<id> --file <access.json>
|
|
46
|
-
workflow-report --workflow tapd:<id> --file <report.json> [--source <adapter>] [--expected-revision <revision>] [--idempotency-key <key>]
|
|
47
|
+
workflow-report --workflow tapd:<id> --file <report.json> [--source <adapter>] [--expected-revision <revision>] [--idempotency-key <key>] [--admin-operation repair-version-membership]
|
|
47
48
|
workflow-artifact-publish --workflow tapd:<id> --file <artifact.json> [--source <adapter>] [--expected-revision <revision>] [--idempotency-key <key>]
|
|
48
49
|
`;
|
|
49
50
|
}
|
|
@@ -166,7 +167,36 @@ async function httpJson(args, pathname, { method = "GET", body, tokenRequired =
|
|
|
166
167
|
}
|
|
167
168
|
if (!response.ok) {
|
|
168
169
|
const message = data?.error || data?.message || text || `HTTP ${response.status}`;
|
|
169
|
-
|
|
170
|
+
const error = new Error(`${method} ${url.pathname} failed: ${message}`);
|
|
171
|
+
error.status = response.status;
|
|
172
|
+
error.response = data;
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
return data;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function httpMultipart(args, pathname, form) {
|
|
179
|
+
const token = authToken(args);
|
|
180
|
+
const url = new URL(pathname, normalizedBaseUrl(args));
|
|
181
|
+
const headers = { Accept: "application/json" };
|
|
182
|
+
if (token) {
|
|
183
|
+
headers.Authorization = `Bearer ${token}`;
|
|
184
|
+
headers.Cookie = `af_session=${encodeURIComponent(token)}`;
|
|
185
|
+
}
|
|
186
|
+
const response = await fetch(url, { method: "POST", headers, body: form });
|
|
187
|
+
const text = await response.text();
|
|
188
|
+
let data = null;
|
|
189
|
+
try {
|
|
190
|
+
data = text ? JSON.parse(text) : null;
|
|
191
|
+
} catch {
|
|
192
|
+
data = { text };
|
|
193
|
+
}
|
|
194
|
+
if (!response.ok) {
|
|
195
|
+
const message = data?.error || data?.message || text || `HTTP ${response.status}`;
|
|
196
|
+
const error = new Error(`POST ${url.pathname} failed: ${message}`);
|
|
197
|
+
error.status = response.status;
|
|
198
|
+
error.response = data;
|
|
199
|
+
throw error;
|
|
170
200
|
}
|
|
171
201
|
return data;
|
|
172
202
|
}
|
|
@@ -277,6 +307,54 @@ function readJsonFile(filePath) {
|
|
|
277
307
|
}
|
|
278
308
|
}
|
|
279
309
|
|
|
310
|
+
function readFlowYamlFile(filePath) {
|
|
311
|
+
const requested = String(filePath || "").trim();
|
|
312
|
+
if (!requested) throw new Error("Missing --file <flow.yaml>.");
|
|
313
|
+
const resolved = path.resolve(requested);
|
|
314
|
+
let flowYaml;
|
|
315
|
+
try {
|
|
316
|
+
flowYaml = fs.readFileSync(resolved, "utf8");
|
|
317
|
+
} catch (error) {
|
|
318
|
+
throw new Error(`Cannot read flow file ${resolved}: ${error?.message || String(error)}`);
|
|
319
|
+
}
|
|
320
|
+
if (!flowYaml.trim()) throw new Error(`Flow file is empty: ${resolved}`);
|
|
321
|
+
return { resolved, flowYaml };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function targetDestinationFromArgs(args) {
|
|
325
|
+
const requested = (option(args, "target-space") || option(args, "flow-source") || "personal").toLowerCase();
|
|
326
|
+
if (requested === "personal" || requested === "user") return { flowSource: "user", shareWithTeam: false };
|
|
327
|
+
if (requested === "workspace") return { flowSource: "workspace", shareWithTeam: false };
|
|
328
|
+
if (requested === "team") return { flowSource: "workspace", shareWithTeam: true };
|
|
329
|
+
throw new Error("Invalid --target-space. Use personal|workspace|team (alias: user).");
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async function importFlow(args, { flowId, targetSpace, resolved, flowYaml }) {
|
|
333
|
+
const form = new FormData();
|
|
334
|
+
form.set("flowId", flowId);
|
|
335
|
+
form.set("targetSpace", targetSpace);
|
|
336
|
+
form.set("file", new Blob([flowYaml], { type: "application/yaml" }), path.basename(resolved));
|
|
337
|
+
return httpMultipart(args, "/api/flows/import", form);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async function resolvePublishTeam(args, shareWithTeam) {
|
|
341
|
+
if (!shareWithTeam) return null;
|
|
342
|
+
const result = await httpJson(args, "/api/teams/me");
|
|
343
|
+
if (!result?.team?.id) {
|
|
344
|
+
throw new Error("Cannot publish to team: the current AgentFlow account is not assigned to an active team.");
|
|
345
|
+
}
|
|
346
|
+
return result.team;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async function sharePublishedFlowWithTeam(args, { flowId, flowSource, team }) {
|
|
350
|
+
if (!team) return null;
|
|
351
|
+
const result = await httpJson(args, "/api/workspace/collaboration/team-share", {
|
|
352
|
+
method: "POST",
|
|
353
|
+
body: { flowId, flowSource, teamId: team.id, role: "editor" },
|
|
354
|
+
});
|
|
355
|
+
return result?.team || team;
|
|
356
|
+
}
|
|
357
|
+
|
|
280
358
|
async function main() {
|
|
281
359
|
loadEnvFiles();
|
|
282
360
|
const args = parseArgv(process.argv.slice(2));
|
|
@@ -312,6 +390,47 @@ async function main() {
|
|
|
312
390
|
return;
|
|
313
391
|
}
|
|
314
392
|
|
|
393
|
+
if (command === "publish-flow") {
|
|
394
|
+
const flowId = requireFlowId(args);
|
|
395
|
+
const destination = targetDestinationFromArgs(args);
|
|
396
|
+
const targetSpace = destination.flowSource;
|
|
397
|
+
const source = readFlowYamlFile(option(args, "file"));
|
|
398
|
+
const replace = args.replace === true;
|
|
399
|
+
const team = await resolvePublishTeam(args, destination.shareWithTeam);
|
|
400
|
+
|
|
401
|
+
if (!replace) {
|
|
402
|
+
const result = await importFlow(args, { flowId, targetSpace, ...source });
|
|
403
|
+
const sharedTeam = await sharePublishedFlowWithTeam(args, { flowId, flowSource: targetSpace, team });
|
|
404
|
+
printJson({ ...result, action: "created", targetSpace: team ? "team" : targetSpace, team: sharedTeam, file: source.resolved });
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
let current = null;
|
|
409
|
+
try {
|
|
410
|
+
current = await httpJson(args, `/api/flow${query({ flowId, flowSource: targetSpace })}`);
|
|
411
|
+
} catch (error) {
|
|
412
|
+
if (error?.status !== 404) throw error;
|
|
413
|
+
}
|
|
414
|
+
if (!current) {
|
|
415
|
+
const result = await importFlow(args, { flowId, targetSpace, ...source });
|
|
416
|
+
const sharedTeam = await sharePublishedFlowWithTeam(args, { flowId, flowSource: targetSpace, team });
|
|
417
|
+
printJson({ ...result, action: "created", targetSpace: team ? "team" : targetSpace, team: sharedTeam, file: source.resolved });
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const result = await httpJson(args, "/api/flow", {
|
|
421
|
+
method: "POST",
|
|
422
|
+
body: {
|
|
423
|
+
flowId,
|
|
424
|
+
flowSource: targetSpace,
|
|
425
|
+
flowYaml: source.flowYaml,
|
|
426
|
+
baseRevision: current.revision,
|
|
427
|
+
},
|
|
428
|
+
});
|
|
429
|
+
const sharedTeam = await sharePublishedFlowWithTeam(args, { flowId, flowSource: targetSpace, team });
|
|
430
|
+
printJson({ ...result, flowId, flowSource: targetSpace, action: "updated", targetSpace: team ? "team" : targetSpace, team: sharedTeam, file: source.resolved });
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
|
|
315
434
|
if (command === "get-graph") {
|
|
316
435
|
const flowId = requireFlowId(args);
|
|
317
436
|
const flowSource = option(args, "flow-source") || "user";
|
|
@@ -390,12 +509,14 @@ async function main() {
|
|
|
390
509
|
const flowId = option(args, "flow-id") || option(args, "flow");
|
|
391
510
|
const flowSource = option(args, "flow-source") || "user";
|
|
392
511
|
const runtimeOnly = args["runtime-only"] === true || args.cached === true ? "1" : "";
|
|
512
|
+
const adminOperation = option(args, "admin-operation") || "";
|
|
393
513
|
const client = createWorkflowReportClient({ baseUrl: normalizedBaseUrl(args), token: authToken(args) });
|
|
394
514
|
printJson(await client.getState({
|
|
395
515
|
workflow: workflow.key,
|
|
396
516
|
flowId,
|
|
397
517
|
flowSource,
|
|
398
518
|
runtimeOnly: runtimeOnly === "1",
|
|
519
|
+
adminOperation,
|
|
399
520
|
}));
|
|
400
521
|
return;
|
|
401
522
|
}
|
|
@@ -412,11 +533,13 @@ async function main() {
|
|
|
412
533
|
const expectedRevision = option(args, "expected-revision");
|
|
413
534
|
const idempotencyKey = option(args, "idempotency-key");
|
|
414
535
|
const reportSource = option(args, "source");
|
|
536
|
+
const adminOperation = option(args, "admin-operation");
|
|
415
537
|
const flowId = option(args, "flow-id") || option(args, "flow");
|
|
416
538
|
const flowSource = option(args, "flow-source");
|
|
417
539
|
if (expectedRevision) body.expectedRevision = expectedRevision;
|
|
418
540
|
if (idempotencyKey) body.idempotencyKey = idempotencyKey;
|
|
419
541
|
if (reportSource) body.source = reportSource;
|
|
542
|
+
if (adminOperation) body.adminOperation = adminOperation;
|
|
420
543
|
if (!String(body.source || "").trim()) throw new Error("Missing Workflow report source. Pass --source <adapter> or include source in the JSON file.");
|
|
421
544
|
if (flowId) body.flowId = flowId;
|
|
422
545
|
if (flowSource) body.flowSource = flowSource;
|
|
@@ -50,12 +50,13 @@ export function createWorkflowReportClient({ baseUrl, token, fetchImpl = globalT
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
return {
|
|
53
|
-
getState({ workflow, flowId = "", flowSource = "user", runtimeOnly = false } = {}) {
|
|
53
|
+
getState({ workflow, flowId = "", flowSource = "user", runtimeOnly = false, adminOperation = "" } = {}) {
|
|
54
54
|
return request(`/api/workflows/state${workflowQuery({
|
|
55
55
|
workflow,
|
|
56
56
|
flowId,
|
|
57
57
|
flowSource,
|
|
58
58
|
runtimeOnly: runtimeOnly ? "1" : "",
|
|
59
|
+
adminOperation,
|
|
59
60
|
})}`);
|
|
60
61
|
},
|
|
61
62
|
syncAccess(body = {}) {
|
|
@@ -91,6 +91,9 @@ For local Markdown or other content that must become a browser URL, publish it f
|
|
|
91
91
|
|
|
92
92
|
- Treat TAPD personnel as derived authority when the Adapter can read them: TAPD Owner maps to Workflow Owner and registered TAPD participants map to Viewer.
|
|
93
93
|
- Keep explicit grants separate from derived TAPD membership. Allow Owner and explicit Reporter writes. Treat TAPD participant Viewer, explicit Viewer, same-team Viewer, share-link Viewer, and ordinary admin review as read-only. The only admin write exception is an explicit audited `adminOperation=repair-version-membership` Report containing only `kind=version` timeline projections, with required `expectedRevision` and `idempotencyKey`; it must never admit Action, Artifact, Observation, GlobalState, Extension, Checklist, or non-version projection writes. Accept legacy `editor` only as a compatibility alias for Reporter.
|
|
94
|
+
- An admin obtaining the strict revision for that repair must explicitly read state with
|
|
95
|
+
`adminOperation=repair-version-membership` (CLI: `workflow-get --runtime-only --admin-operation repair-version-membership`). The server resolves only that read as `admin-version-repair`; it does not grant ordinary admin CLI access to unrelated Workflows.
|
|
96
|
+
- Admin management UI must provide a concise `adminReason` for the audit event. Keep it optional for legacy CLI compatibility, but never omit it from new interactive management flows.
|
|
94
97
|
- `observation.state` replaces the complete previous observation for the same `clientId`.
|
|
95
98
|
- `globalState.patch` recursively merges objects; arrays and scalars replace; `null` and `remove` delete explicit paths. The first reporting source to write a path owns it; another source cannot overwrite an owned path.
|
|
96
99
|
- Reusing an `action.key` updates the same semantic stage. Do not create a new key for refreshes or retries.
|
|
@@ -240,12 +240,20 @@ GET /api/workflows/state?workflow=tapd%3A1020124&runtimeOnly=1
|
|
|
240
240
|
Authorization: Bearer <AGENTFLOW_TOKEN>
|
|
241
241
|
```
|
|
242
242
|
|
|
243
|
+
管理员为版本归属修复读取严格锁时,必须显式声明同一受限意图:
|
|
244
|
+
|
|
245
|
+
```http
|
|
246
|
+
GET /api/workflows/state?workflow=tapd%3A1013667&runtimeOnly=1&adminOperation=repair-version-membership
|
|
247
|
+
Authorization: Bearer <AGENTFLOW_TOKEN>
|
|
248
|
+
```
|
|
249
|
+
|
|
243
250
|
| Query 参数 | 类型 | 必填 | 含义 |
|
|
244
251
|
| --- | --- | --- | --- |
|
|
245
252
|
| `workflow` | string | 与 namespace/id 二选一 | 规范 key,例如 `tapd:1020124` |
|
|
246
253
|
| `namespace` | string | 与 workflow 二选一 | 当前仅支持 `tapd` |
|
|
247
254
|
| `id` | string | 与 workflow 二选一 | TAPD short ID |
|
|
248
255
|
| `runtimeOnly` | `0 \| 1` | 否 | `1` 只读取已保存运行态,不主动刷新上游;CLI 的 `--runtime-only` 使用它 |
|
|
256
|
+
| `adminOperation` | string | 管理员版本修复时必填 | 仅 `repair-version-membership`;只授予取得严格锁所需的受限读取,不授予普通 Workflow 写权限;服务端强制按 runtime-only 读取 |
|
|
249
257
|
| `flowId` | string | 否 | 关联 AgentFlow 项目时指定项目 ID |
|
|
250
258
|
| `flowSource` | string | 否 | 项目来源,默认 `user` |
|
|
251
259
|
| `workspaceId` | string | 否 | 项目工作区上下文 |
|
|
@@ -308,6 +316,7 @@ Authorization: Bearer <AGENTFLOW_TOKEN>
|
|
|
308
316
|
| `expectedRevision` | string | 兼容字段 | 仅在没有 `expectedVersions` 时启用的整 Workflow 严格锁;新接入不要使用 |
|
|
309
317
|
| `idempotencyKey` | string | 强烈建议 | 一次业务语义操作的稳定身份,不使用时间戳或随机 UUID |
|
|
310
318
|
| `adminOperation` | string | 管理员特例 | 仅 `repair-version-membership`;详见 5.3 |
|
|
319
|
+
| `adminReason` | string | 管理员 UI 建议必填 | 版本归属修复原因,最多 500 字符;写入审计事件 |
|
|
311
320
|
| `observation` | object | 条件必填 | 同一 `clientId` 的完整生产方观察 |
|
|
312
321
|
| `action` | object | 条件必填 | 一条关键业务阶段 |
|
|
313
322
|
| `artifacts` | array | 条件必填 | Action 证据或全局证据 |
|
|
@@ -350,17 +359,19 @@ Authorization: Bearer <AGENTFLOW_TOKEN>
|
|
|
350
359
|
"workflow": "tapd:1013667",
|
|
351
360
|
"source": "prd-flow",
|
|
352
361
|
"adminOperation": "repair-version-membership",
|
|
362
|
+
"adminReason": "清理测试版本并归属到正式迭代",
|
|
353
363
|
"projections": {
|
|
354
364
|
"timeline": [{
|
|
355
365
|
"kind": "version",
|
|
356
|
-
"id": "
|
|
366
|
+
"id": "1133202860001000338",
|
|
367
|
+
"key": "prd-flow:tapd-current-version:1133202860001000338",
|
|
357
368
|
"title": "Likee Android&iOS V5.63",
|
|
358
369
|
"date": "2026-08-11",
|
|
359
370
|
"source": "prd-flow"
|
|
360
371
|
}]
|
|
361
372
|
},
|
|
362
373
|
"expectedRevision": "runtime:<revision-from-get>",
|
|
363
|
-
"idempotencyKey": "admin-version-repair:1013667:
|
|
374
|
+
"idempotencyKey": "admin-version-repair:1013667:version-1133202860001000338"
|
|
364
375
|
}
|
|
365
376
|
```
|
|
366
377
|
|
|
@@ -371,6 +382,7 @@ Authorization: Bearer <AGENTFLOW_TOKEN>
|
|
|
371
382
|
- 所有新投影必须是 `kind=version`;当前 source 的非版本投影和其他 source 的全部投影原子保留。
|
|
372
383
|
- `expectedRevision` 与 `idempotencyKey` 必填。并发变化返回 `409`,失败时不产生部分写入。
|
|
373
384
|
- 运行态事件写入 `administrativeRepair.kind=version-attribution`、管理员 actor 和时间,供审计追踪。
|
|
385
|
+
- 管理 UI 必须提交 `adminReason`;旧 CLI 请求可暂时省略以保持兼容。
|
|
374
386
|
- 空 `timeline` 表示清空该 source 的版本归属,但仍保留该 source 的 Sprint/Milestone 等非版本条目。
|
|
375
387
|
|
|
376
388
|
## 6. POST /api/workflow-artifacts/publish:发布 Markdown 预览
|
|
@@ -708,7 +720,7 @@ timeline-membership:tapd-1020124:version-1133202860001000338:v1
|
|
|
708
720
|
### 10.5 管理员修复版本归属
|
|
709
721
|
|
|
710
722
|
1. 仅在批量治理版本归属时使用;普通业务状态仍由 Owner/Reporter 上报。
|
|
711
|
-
2.
|
|
723
|
+
2. 管理员使用 `adminOperation=repair-version-membership` GET 当前 Workflow,人工或程序核对目标版本自身的稳定 ID,并保存 `runtimeRevision`;不得把 TAPD 需求 ID 当作版本 ID。
|
|
712
724
|
3. 发送只含 `kind=version` timeline 的 `repair-version-membership` 请求。
|
|
713
725
|
4. 验证响应包含 `administrativeRepair`,事件 actor 是操作管理员,且非版本/其他 source 投影未变化。
|
|
714
726
|
5. `409` 时重新读取、重新核对并只重试一次;不得绕过严格锁。
|
|
@@ -748,7 +760,7 @@ prd-flow 只是一个接入实现,不是协议依赖:
|
|
|
748
760
|
- Action 下能看到稳定 key 的 MR、构建或测试产物。
|
|
749
761
|
- Markdown Publish 返回可访问 URL,但不会推进业务状态。
|
|
750
762
|
- 版本改名/改期不产生新迭代节点,版本切换不会删除第三方 Sprint。
|
|
751
|
-
-
|
|
763
|
+
- 管理员只有显式携带版本修复意图时才能 GET 当前 runtimeRevision;修复只能改 `kind=version` 投影,要求 runtimeRevision/幂等键并留下管理员 actor 审计;无意图读取与普通 Report 仍返回 403。
|
|
752
764
|
- 自定义 extension 能保存;注册渲染器后能显示对应文档区 / Issue 区。
|
|
753
765
|
- 不同资源 key 可并发更新;同 key 旧版本返回包含具体 `resourceKey` 的 409。
|
|
754
766
|
- 409 会触发一次 key 级 read → re-merge → retry,且失败请求不会部分落库。
|