@fieldwangai/agentflow 0.1.92 → 0.1.94
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/bin/lib/ui-server.mjs +3002 -186
- package/builtin/web-ui/dist/assets/index-CYwUDlvT.js +346 -0
- package/builtin/web-ui/dist/assets/index-DV-QkHHi.css +1 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/skills/agentflow-cli/SKILL.md +133 -0
- package/skills/agentflow-cli/agents/openai.yaml +4 -0
- package/skills/agentflow-cli/scripts/agentflow-cli.mjs +341 -0
- package/builtin/web-ui/dist/assets/index-CSBbCP8S.css +0 -1
- package/builtin/web-ui/dist/assets/index-Dk3al84f.js +0 -341
|
@@ -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-CYwUDlvT.js"></script>
|
|
19
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DV-QkHHi.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.94",
|
|
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,133 @@
|
|
|
1
|
+
---
|
|
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 a flow run, inspect run status/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
|
+
---
|
|
5
|
+
|
|
6
|
+
# AgentFlow CLI
|
|
7
|
+
|
|
8
|
+
Use this skill when the task is to operate AgentFlow itself from an AI agent. Do not configure or call MCP for this skill. Use the bundled CLI script instead:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
node skills/agentflow-cli/scripts/agentflow-cli.mjs <command> [options]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
If the skill is installed outside this repository, resolve the script path relative to this `SKILL.md`.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Install from SkillHub before using this skill in another agent environment:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
skillhub install agentflow-cli --global --agent codex
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
For a project-local install:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
skillhub install agentflow-cli --dir .agents/skills
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
For other SkillHub-supported agents, change the agent name:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
skillhub install agentflow-cli --global --agent claude-code
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
After installation, configure only the direct API token. Do not add an MCP server for this skill.
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
The CLI reads configuration in this order:
|
|
41
|
+
|
|
42
|
+
1. CLI flags: `--base-url`, `--token`
|
|
43
|
+
2. Environment variables: `AGENTFLOW_BASE_URL`, `AGENTFLOW_TOKEN`, `AGENTFLOW_SESSION_TOKEN`
|
|
44
|
+
3. Env files: `AGENTFLOW_ENV_FILE`, then `.env`, `.agentflow.env`, then `~/.agentflow.env`
|
|
45
|
+
|
|
46
|
+
Default base URL: `http://ai.mengma.bigo.inner/`.
|
|
47
|
+
|
|
48
|
+
Required token: `AGENTFLOW_TOKEN` or `AGENTFLOW_SESSION_TOKEN`. Never print the token in the final answer or logs.
|
|
49
|
+
|
|
50
|
+
Example `.env`:
|
|
51
|
+
|
|
52
|
+
```dotenv
|
|
53
|
+
AGENTFLOW_TOKEN=replace-with-token
|
|
54
|
+
# Optional for local debug only:
|
|
55
|
+
# AGENTFLOW_BASE_URL=http://127.0.0.1:8875
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Commands
|
|
59
|
+
|
|
60
|
+
Prefer JSON output and let the CLI handle auth headers.
|
|
61
|
+
|
|
62
|
+
List knowledge/workspace entries:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
node skills/agentflow-cli/scripts/agentflow-cli.mjs list-workspace
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
List flows:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
node skills/agentflow-cli/scripts/agentflow-cli.mjs list-flows
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Read one flow graph:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
node skills/agentflow-cli/scripts/agentflow-cli.mjs get-graph --flow-id TestNodes --flow-source user
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Run a flow:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
node skills/agentflow-cli/scripts/agentflow-cli.mjs run --flow-id TestNodes --flow-source user
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Run a specific run node with inputs:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
node skills/agentflow-cli/scripts/agentflow-cli.mjs run \
|
|
90
|
+
--flow-id TestNodes \
|
|
91
|
+
--flow-source user \
|
|
92
|
+
--run-node-id workspace_run_1 \
|
|
93
|
+
--input topic=hello \
|
|
94
|
+
--input date=today
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
List runs for a flow or workspace alias:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
node skills/agentflow-cli/scripts/agentflow-cli.mjs list-run-by-workspace --workspace TestNodes --limit 20
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Read run logs:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
node skills/agentflow-cli/scripts/agentflow-cli.mjs logs --run-id workspace-123
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Get active run status:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
node skills/agentflow-cli/scripts/agentflow-cli.mjs status --flow-id TestNodes --flow-source user
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Extract display outputs from a flow:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
node skills/agentflow-cli/scripts/agentflow-cli.mjs display-outputs --flow-id TestNodes --flow-source user
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Workflow
|
|
122
|
+
|
|
123
|
+
1. Check token availability with `config`.
|
|
124
|
+
2. Use `list-workspace` or `list-flows` to discover targets.
|
|
125
|
+
3. Use `run` to start the flow. If the task needs the generated page/text, inspect returned `displayOutputs` or call `display-outputs`.
|
|
126
|
+
4. Use `status`, `list-run-by-workspace`, and `logs` when a run is active, failed, or needs debugging.
|
|
127
|
+
|
|
128
|
+
## Failure Handling
|
|
129
|
+
|
|
130
|
+
- If the CLI says the token is missing, ask the user to set `AGENTFLOW_TOKEN` in env or `.env`.
|
|
131
|
+
- If the API returns 401/403, do not retry with a printed token. Ask the user to refresh the token.
|
|
132
|
+
- If `run` fails because a flow is already running, call `status` and `list-run-by-workspace` before retrying.
|
|
133
|
+
- If local debugging is needed, override `AGENTFLOW_BASE_URL`; otherwise keep the default internal URL.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "AgentFlow CLI"
|
|
3
|
+
short_description: "Directly call AgentFlow APIs with a token-backed CLI."
|
|
4
|
+
default_prompt: "Use the bundled AgentFlow CLI to list workspaces or flows, run a flow, inspect run logs, and fetch display outputs. Use AGENTFLOW_TOKEN from env or .env and default the base URL to the internal AgentFlow service."
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_BASE_URL = "http://ai.mengma.bigo.inner/";
|
|
7
|
+
const DISPLAY_DEFINITION_KINDS = new Map([
|
|
8
|
+
["display_markdown", "markdown"],
|
|
9
|
+
["display_mermaid", "mermaid"],
|
|
10
|
+
["display_ascii", "ascii"],
|
|
11
|
+
["display_html", "html"],
|
|
12
|
+
["display_react_app", "react"],
|
|
13
|
+
["display_image", "image"],
|
|
14
|
+
["display_chart", "chart"],
|
|
15
|
+
["display_table", "table"],
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
function usage() {
|
|
19
|
+
return `AgentFlow direct API CLI
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
agentflow-cli <command> [options]
|
|
23
|
+
|
|
24
|
+
Config:
|
|
25
|
+
--base-url <url> Override AGENTFLOW_BASE_URL
|
|
26
|
+
--token <token> Override AGENTFLOW_TOKEN
|
|
27
|
+
AGENTFLOW_BASE_URL Defaults to ${DEFAULT_BASE_URL}
|
|
28
|
+
AGENTFLOW_TOKEN Required unless AGENTFLOW_SESSION_TOKEN is set
|
|
29
|
+
AGENTFLOW_ENV_FILE Optional dotenv file path
|
|
30
|
+
|
|
31
|
+
Commands:
|
|
32
|
+
config
|
|
33
|
+
list-workspace | list-workspaces
|
|
34
|
+
list-flows
|
|
35
|
+
get-graph --flow-id <id> [--flow-source user]
|
|
36
|
+
run --flow-id <id> [--flow-source user] [--run-node-id <id>] [--input k=v]
|
|
37
|
+
status --flow-id <id> [--flow-source user]
|
|
38
|
+
list-run-by-workspace | list-runs-by-workspace --workspace <flowId> [--limit 20]
|
|
39
|
+
list-runs [--flow-id <id>] [--flow-source user] [--limit 20]
|
|
40
|
+
logs --run-id <id>
|
|
41
|
+
display-outputs --flow-id <id> [--flow-source user]
|
|
42
|
+
sync-workspace --workspace <id>
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function parseDotenvValue(raw) {
|
|
47
|
+
let value = String(raw ?? "").trim();
|
|
48
|
+
if (!value) return "";
|
|
49
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
50
|
+
value = value.slice(1, -1);
|
|
51
|
+
}
|
|
52
|
+
return value.replace(/\\n/g, "\n");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function loadEnvFile(filePath) {
|
|
56
|
+
if (!filePath || !fs.existsSync(filePath)) return;
|
|
57
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
58
|
+
for (const line of content.split(/\r?\n/)) {
|
|
59
|
+
const trimmed = line.trim();
|
|
60
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
61
|
+
const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
|
|
62
|
+
if (!match) continue;
|
|
63
|
+
const key = match[1];
|
|
64
|
+
if (process.env[key] !== undefined) continue;
|
|
65
|
+
process.env[key] = parseDotenvValue(match[2]);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function loadEnvFiles() {
|
|
70
|
+
const cwd = process.cwd();
|
|
71
|
+
const candidates = [
|
|
72
|
+
process.env.AGENTFLOW_ENV_FILE,
|
|
73
|
+
path.join(cwd, ".env"),
|
|
74
|
+
path.join(cwd, ".agentflow.env"),
|
|
75
|
+
path.join(os.homedir(), ".agentflow.env"),
|
|
76
|
+
].filter(Boolean);
|
|
77
|
+
for (const candidate of candidates) {
|
|
78
|
+
loadEnvFile(path.resolve(candidate));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function parseArgv(argv) {
|
|
83
|
+
const out = { _: [] };
|
|
84
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
85
|
+
const item = argv[i];
|
|
86
|
+
if (!item.startsWith("--")) {
|
|
87
|
+
out._.push(item);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const eq = item.indexOf("=");
|
|
91
|
+
let key = item.slice(2);
|
|
92
|
+
let value = true;
|
|
93
|
+
if (eq >= 0) {
|
|
94
|
+
key = item.slice(2, eq);
|
|
95
|
+
value = item.slice(eq + 1);
|
|
96
|
+
} else if (argv[i + 1] && !argv[i + 1].startsWith("--")) {
|
|
97
|
+
value = argv[i + 1];
|
|
98
|
+
i += 1;
|
|
99
|
+
}
|
|
100
|
+
if (out[key] !== undefined) {
|
|
101
|
+
if (!Array.isArray(out[key])) out[key] = [out[key]];
|
|
102
|
+
out[key].push(value);
|
|
103
|
+
} else {
|
|
104
|
+
out[key] = value;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function option(args, name, fallback = "") {
|
|
111
|
+
const value = args[name];
|
|
112
|
+
if (Array.isArray(value)) return value[value.length - 1] ?? fallback;
|
|
113
|
+
if (value === true || value === undefined || value === null || value === "") return fallback;
|
|
114
|
+
return String(value);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function normalizedBaseUrl(args) {
|
|
118
|
+
const raw = option(args, "base-url") || process.env.AGENTFLOW_BASE_URL || DEFAULT_BASE_URL;
|
|
119
|
+
return String(raw).replace(/\/+$/, "");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function authToken(args, required = true) {
|
|
123
|
+
const token = option(args, "token") || process.env.AGENTFLOW_TOKEN || process.env.AGENTFLOW_SESSION_TOKEN || "";
|
|
124
|
+
const trimmed = String(token).trim();
|
|
125
|
+
if (required && !trimmed) {
|
|
126
|
+
throw new Error("Missing AGENTFLOW_TOKEN. Set it in env, .env, .agentflow.env, or pass --token.");
|
|
127
|
+
}
|
|
128
|
+
return trimmed;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function query(params = {}) {
|
|
132
|
+
const search = new URLSearchParams();
|
|
133
|
+
for (const [key, value] of Object.entries(params)) {
|
|
134
|
+
if (value === undefined || value === null || value === "") continue;
|
|
135
|
+
search.set(key, String(value));
|
|
136
|
+
}
|
|
137
|
+
const text = search.toString();
|
|
138
|
+
return text ? `?${text}` : "";
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function httpJson(args, pathname, { method = "GET", body, tokenRequired = true } = {}) {
|
|
142
|
+
const token = authToken(args, tokenRequired);
|
|
143
|
+
const url = new URL(pathname, normalizedBaseUrl(args));
|
|
144
|
+
const headers = { Accept: "application/json" };
|
|
145
|
+
if (body !== undefined) headers["Content-Type"] = "application/json";
|
|
146
|
+
if (token) {
|
|
147
|
+
headers.Authorization = `Bearer ${token}`;
|
|
148
|
+
headers.Cookie = `af_session=${encodeURIComponent(token)}`;
|
|
149
|
+
}
|
|
150
|
+
const response = await fetch(url, {
|
|
151
|
+
method,
|
|
152
|
+
headers,
|
|
153
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
154
|
+
});
|
|
155
|
+
const text = await response.text();
|
|
156
|
+
let data = null;
|
|
157
|
+
try {
|
|
158
|
+
data = text ? JSON.parse(text) : null;
|
|
159
|
+
} catch {
|
|
160
|
+
data = { text };
|
|
161
|
+
}
|
|
162
|
+
if (!response.ok) {
|
|
163
|
+
const message = data?.error || data?.message || text || `HTTP ${response.status}`;
|
|
164
|
+
throw new Error(`${method} ${url.pathname} failed: ${message}`);
|
|
165
|
+
}
|
|
166
|
+
return data;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function asArray(value) {
|
|
170
|
+
if (value === undefined) return [];
|
|
171
|
+
return Array.isArray(value) ? value : [value];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function parseInputs(args) {
|
|
175
|
+
const inputs = {};
|
|
176
|
+
for (const item of asArray(args.input)) {
|
|
177
|
+
const text = String(item);
|
|
178
|
+
const index = text.indexOf("=");
|
|
179
|
+
if (index <= 0) {
|
|
180
|
+
throw new Error(`Invalid --input "${text}". Expected key=value.`);
|
|
181
|
+
}
|
|
182
|
+
inputs[text.slice(0, index)] = text.slice(index + 1);
|
|
183
|
+
}
|
|
184
|
+
return inputs;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function slotText(slots, names = []) {
|
|
188
|
+
const wanted = new Set(names.map(String));
|
|
189
|
+
for (const slot of Array.isArray(slots) ? slots : []) {
|
|
190
|
+
const name = String(slot?.name || "");
|
|
191
|
+
if (wanted.size && !wanted.has(name)) continue;
|
|
192
|
+
const value = slot?.value ?? slot?.default ?? "";
|
|
193
|
+
if (String(value || "").trim()) return String(value);
|
|
194
|
+
}
|
|
195
|
+
return "";
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function extractDisplayOutputs(graph) {
|
|
199
|
+
const instances = graph?.instances && typeof graph.instances === "object" ? graph.instances : {};
|
|
200
|
+
const outputs = [];
|
|
201
|
+
for (const [nodeId, instance] of Object.entries(instances)) {
|
|
202
|
+
const kind = DISPLAY_DEFINITION_KINDS.get(String(instance?.definitionId || ""));
|
|
203
|
+
if (!kind) continue;
|
|
204
|
+
const primary = kind === "image" ? "src" : "content";
|
|
205
|
+
const content = String(instance?.body || "") ||
|
|
206
|
+
slotText(instance?.input, [primary, "content", "markdown", "html", "src"]) ||
|
|
207
|
+
slotText(instance?.output, [primary, "content", "markdown", "html", "src"]);
|
|
208
|
+
outputs.push({
|
|
209
|
+
nodeId,
|
|
210
|
+
label: String(instance?.label || instance?.displayName || nodeId),
|
|
211
|
+
definitionId: String(instance?.definitionId || ""),
|
|
212
|
+
kind,
|
|
213
|
+
content,
|
|
214
|
+
hasContent: Boolean(String(content || "").trim()),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return outputs;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function printJson(data) {
|
|
221
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function requireFlowId(args) {
|
|
225
|
+
const flowId = option(args, "flow-id") || option(args, "flow");
|
|
226
|
+
if (!flowId) throw new Error("Missing --flow-id.");
|
|
227
|
+
return flowId;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function main() {
|
|
231
|
+
loadEnvFiles();
|
|
232
|
+
const args = parseArgv(process.argv.slice(2));
|
|
233
|
+
const command = args._[0] || "help";
|
|
234
|
+
if (command === "help" || command === "--help" || command === "-h") {
|
|
235
|
+
process.stdout.write(usage());
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (command === "config") {
|
|
240
|
+
printJson({
|
|
241
|
+
baseUrl: normalizedBaseUrl(args),
|
|
242
|
+
hasToken: Boolean(authToken(args, false)),
|
|
243
|
+
tokenSource: option(args, "token") ? "flag" : process.env.AGENTFLOW_TOKEN ? "AGENTFLOW_TOKEN" : process.env.AGENTFLOW_SESSION_TOKEN ? "AGENTFLOW_SESSION_TOKEN" : "",
|
|
244
|
+
});
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (command === "list-workspace" || command === "list-workspaces") {
|
|
249
|
+
printJson(await httpJson(args, "/api/workspaces"));
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (command === "sync-workspace") {
|
|
254
|
+
const id = option(args, "workspace") || option(args, "id");
|
|
255
|
+
if (!id) throw new Error("Missing --workspace.");
|
|
256
|
+
printJson(await httpJson(args, "/api/workspaces/sync", { method: "POST", body: { id } }));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (command === "list-flows" || command === "list-flow") {
|
|
261
|
+
printJson(await httpJson(args, "/api/flows"));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (command === "get-graph") {
|
|
266
|
+
const flowId = requireFlowId(args);
|
|
267
|
+
const flowSource = option(args, "flow-source") || "user";
|
|
268
|
+
printJson(await httpJson(args, `/api/workspace/graph${query({ flowId, flowSource })}`));
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (command === "run") {
|
|
273
|
+
const flowId = requireFlowId(args);
|
|
274
|
+
const flowSource = option(args, "flow-source") || "user";
|
|
275
|
+
const runNodeId = option(args, "run-node-id") || "";
|
|
276
|
+
const graphPayload = await httpJson(args, `/api/workspace/graph${query({ flowId, flowSource })}`);
|
|
277
|
+
const result = await httpJson(args, "/api/workspace/run", {
|
|
278
|
+
method: "POST",
|
|
279
|
+
body: {
|
|
280
|
+
flowId,
|
|
281
|
+
flowSource,
|
|
282
|
+
runNodeId,
|
|
283
|
+
runAlias: option(args, "run-alias") || "",
|
|
284
|
+
graph: graphPayload.graph,
|
|
285
|
+
inputs: parseInputs(args),
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
printJson({
|
|
289
|
+
...result,
|
|
290
|
+
displayOutputs: extractDisplayOutputs(result?.graph),
|
|
291
|
+
});
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (command === "status") {
|
|
296
|
+
const flowId = requireFlowId(args);
|
|
297
|
+
const flowSource = option(args, "flow-source") || "user";
|
|
298
|
+
printJson(await httpJson(args, `/api/workspace/run/status${query({ flowId, flowSource })}`));
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (command === "list-run-by-workspace" || command === "list-runs-by-workspace") {
|
|
303
|
+
const workspace = option(args, "workspace") || option(args, "flow-id") || option(args, "flow");
|
|
304
|
+
const flowSource = option(args, "flow-source") || "user";
|
|
305
|
+
const limit = Number(option(args, "limit") || 20);
|
|
306
|
+
printJson(await httpJson(args, `/api/workspace/run-logs${query({ flowId: workspace, flowSource, limit })}`));
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (command === "list-runs") {
|
|
311
|
+
const flowId = option(args, "flow-id") || "";
|
|
312
|
+
const flowSource = option(args, "flow-source") || "";
|
|
313
|
+
const scheduleNodeId = option(args, "schedule-node-id") || "";
|
|
314
|
+
const runNodeId = option(args, "run-node-id") || "";
|
|
315
|
+
const limit = Number(option(args, "limit") || 20);
|
|
316
|
+
printJson(await httpJson(args, `/api/workspace/run-logs${query({ flowId, flowSource, scheduleNodeId, runNodeId, limit })}`));
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (command === "logs") {
|
|
321
|
+
const runId = option(args, "run-id");
|
|
322
|
+
if (!runId) throw new Error("Missing --run-id.");
|
|
323
|
+
printJson(await httpJson(args, `/api/workspace/run-logs/${encodeURIComponent(runId)}`));
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (command === "display-outputs") {
|
|
328
|
+
const flowId = requireFlowId(args);
|
|
329
|
+
const flowSource = option(args, "flow-source") || "user";
|
|
330
|
+
const graphPayload = await httpJson(args, `/api/workspace/graph${query({ flowId, flowSource })}`);
|
|
331
|
+
printJson({ flowId, flowSource, displayOutputs: extractDisplayOutputs(graphPayload.graph) });
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
throw new Error(`Unknown command: ${command}\n\n${usage()}`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
main().catch((error) => {
|
|
339
|
+
process.stderr.write(`${error?.message || String(error)}\n`);
|
|
340
|
+
process.exit(1);
|
|
341
|
+
});
|