@fieldwangai/agentflow 0.1.143 → 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.
@@ -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-c8AUUsSF.js"></script>
19
- <link rel="stylesheet" crossorigin href="/assets/index-CtG65J3e.css">
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.143",
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 AgentFlow workspaces or 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/.
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
@@ -132,13 +143,15 @@ The only admin write exception is audited version-membership repair. Read its st
132
143
  ## Workflow
133
144
 
134
145
  1. Check token availability with `config`.
135
- 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.
136
147
  3. Use `run` to start the flow. If the task needs the generated page/text, inspect returned `displayOutputs` or call `display-outputs`.
137
148
  4. Use `status`, `list-run-by-workspace`, and `logs` when a run is active, failed, or needs debugging.
138
149
 
139
150
  ## Failure Handling
140
151
 
141
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.
142
155
  - If the API returns 401/403, do not retry with a printed token. Ask the user to refresh the token.
143
156
  - If `run` fails because a flow is already running, call `status` and `list-run-by-workspace` before retrying.
144
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]
@@ -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
- throw new Error(`${method} ${url.pathname} failed: ${message}`);
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";
@@ -93,6 +93,7 @@ For local Markdown or other content that must become a browser URL, publish it f
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
94
  - An admin obtaining the strict revision for that repair must explicitly read state with
95
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.
96
97
  - `observation.state` replaces the complete previous observation for the same `clientId`.
97
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.
98
99
  - Reusing an `action.key` updates the same semantic stage. Do not create a new key for refreshes or retries.
@@ -316,6 +316,7 @@ Authorization: Bearer <AGENTFLOW_TOKEN>
316
316
  | `expectedRevision` | string | 兼容字段 | 仅在没有 `expectedVersions` 时启用的整 Workflow 严格锁;新接入不要使用 |
317
317
  | `idempotencyKey` | string | 强烈建议 | 一次业务语义操作的稳定身份,不使用时间戳或随机 UUID |
318
318
  | `adminOperation` | string | 管理员特例 | 仅 `repair-version-membership`;详见 5.3 |
319
+ | `adminReason` | string | 管理员 UI 建议必填 | 版本归属修复原因,最多 500 字符;写入审计事件 |
319
320
  | `observation` | object | 条件必填 | 同一 `clientId` 的完整生产方观察 |
320
321
  | `action` | object | 条件必填 | 一条关键业务阶段 |
321
322
  | `artifacts` | array | 条件必填 | Action 证据或全局证据 |
@@ -358,6 +359,7 @@ Authorization: Bearer <AGENTFLOW_TOKEN>
358
359
  "workflow": "tapd:1013667",
359
360
  "source": "prd-flow",
360
361
  "adminOperation": "repair-version-membership",
362
+ "adminReason": "清理测试版本并归属到正式迭代",
361
363
  "projections": {
362
364
  "timeline": [{
363
365
  "kind": "version",
@@ -380,6 +382,7 @@ Authorization: Bearer <AGENTFLOW_TOKEN>
380
382
  - 所有新投影必须是 `kind=version`;当前 source 的非版本投影和其他 source 的全部投影原子保留。
381
383
  - `expectedRevision` 与 `idempotencyKey` 必填。并发变化返回 `409`,失败时不产生部分写入。
382
384
  - 运行态事件写入 `administrativeRepair.kind=version-attribution`、管理员 actor 和时间,供审计追踪。
385
+ - 管理 UI 必须提交 `adminReason`;旧 CLI 请求可暂时省略以保持兼容。
383
386
  - 空 `timeline` 表示清空该 source 的版本归属,但仍保留该 source 的 Sprint/Milestone 等非版本条目。
384
387
 
385
388
  ## 6. POST /api/workflow-artifacts/publish:发布 Markdown 预览