@fieldwangai/agentflow 0.1.159 → 0.1.162
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/auth.mjs +231 -14
- package/bin/lib/catalog-flows.mjs +12 -1
- package/bin/lib/cli-auth-page.mjs +103 -0
- package/bin/lib/control-while.mjs +336 -0
- package/bin/lib/flow-dsl/codegen.mjs +74 -11
- package/bin/lib/flow-dsl/ir.mjs +33 -2
- package/bin/lib/flow-dsl/lint.mjs +128 -3
- package/bin/lib/flow-dsl/parser.mjs +168 -4
- package/bin/lib/marketplace.mjs +10 -2
- package/bin/lib/node-package-manifest.mjs +7 -2
- package/bin/lib/node-ui-kit.mjs +156 -0
- package/bin/lib/paths.mjs +2 -0
- package/bin/lib/skill-runtime.mjs +6 -0
- package/bin/lib/table.mjs +2 -3
- package/bin/lib/ui-server.mjs +220 -17
- package/bin/lib/workspace-draft.mjs +74 -0
- package/bin/lib/workspace-flow-store.mjs +11 -2
- package/bin/lib/workspace-graph-merge.mjs +3 -0
- package/bin/lib/workspace-routes.mjs +307 -5
- package/bin/lib/workspace-server.mjs +598 -11
- package/bin/pipeline/validate-flow.mjs +3 -3
- package/builtin/nodes/control_parse_json.md +25 -0
- package/builtin/nodes/control_subflow_call.md +27 -0
- package/builtin/nodes/control_while.md +124 -0
- package/builtin/nodes/provide_json.md +14 -0
- package/builtin/nodes/workspace_subflow_input.md +14 -0
- package/builtin/pipelines/new/workspace.flow.js +1 -1
- package/builtin/pipelines/subflow-preview/workspace.flow.js +29 -0
- package/builtin/pipelines/subflow-preview/workspace.layout.json +38 -0
- package/builtin/pipelines/subflow-preview/workspace.nodes.json +19 -0
- package/builtin/pipelines/subflow-preview/workspace.state.json +93 -0
- package/builtin/pipelines/while-subflow-preview/workspace.flow.js +55 -0
- package/builtin/pipelines/while-subflow-preview/workspace.layout.json +66 -0
- package/builtin/pipelines/while-subflow-preview/workspace.nodes.json +49 -0
- package/builtin/pipelines/while-subflow-preview/workspace.state.json +194 -0
- package/builtin/web-ui/dist/assets/{WorkflowAssistantThread-CY19DsYq.js → WorkflowAssistantThread-CTrXOZ00.js} +1 -1
- package/builtin/web-ui/dist/assets/index-5uJFccdX.css +1 -0
- package/builtin/web-ui/dist/assets/index-BeUfNQRL.js +873 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +6 -1
- package/reference/flow-control-capabilities.md +41 -16
- package/shared/slot-types.js +58 -0
- package/skills/agentflow-author-flow/SKILL.md +81 -8
- package/skills/agentflow-author-flow/agents/openai.yaml +3 -3
- package/skills/agentflow-cli/SKILL.md +130 -25
- package/skills/agentflow-cli/agents/openai.yaml +2 -2
- package/skills/agentflow-cli/runtime/bin/lib/skill-runtime.mjs +14765 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/agent_subAgent.md +34 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_agent_toBool.md +26 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_anyOne.md +19 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_cancelled.md +26 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_cd_workspace.md +50 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_delay.md +31 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_end.md +13 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_if.md +21 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_interval_loop.md +55 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_load_mcp.md +24 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_load_skills.md +35 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_parse_json.md +25 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_start.md +13 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_subflow_call.md +27 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_toBool.md +27 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_user_workspace.md +24 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_wait_until.md +34 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_while.md +124 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_ascii.md +24 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_chart.md +32 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_code.md +39 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_html.md +32 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_image.md +36 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_markdown.md +24 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_mermaid.md +24 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_react_app.md +53 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_table.md +32 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/provide_bool.md +14 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/provide_file.md +14 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/provide_json.md +14 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/provide_password.md +14 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/provide_str.md +14 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_display_share_link.md +47 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_get_env.md +20 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_git_checkout.md +70 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_git_worktree_load.md +73 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_git_worktree_unload.md +52 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_gitlab_create_mr.md +114 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_jenkins_build.md +67 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_load_key.md +26 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_nodejs.md +52 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_print.md +21 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_save_key.md +26 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_set_run_env.md +38 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_user_ask.md +27 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_user_check.md +26 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_wecom_send_app_markdown.md +57 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_wecom_send_group_markdown.md +45 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/workspace_one_click_task.md +44 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/workspace_run.md +16 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/workspace_scheduled_run.md +16 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/workspace_subflow_input.md +14 -0
- package/skills/agentflow-cli/runtime/package.json +6 -0
- package/skills/agentflow-cli/scripts/agentflow-auth-store.mjs +113 -0
- package/skills/agentflow-cli/scripts/agentflow-cli.mjs +393 -28
- package/skills/agentflow-cli/scripts/agentflow-runtime.mjs +11 -4
- package/skills/agentflow-flow-add-instances/SKILL.md +2 -2
- package/skills/agentflow-flow-dsl/SKILL.md +83 -10
- package/skills/agentflow-flow-dsl/agents/openai.yaml +2 -2
- package/skills/agentflow-flow-dsl/references/node-calls.md +3 -0
- package/skills/agentflow-flow-dsl/references/subflow-authoring.md +230 -0
- package/skills/agentflow-flow-edit-node-fields/SKILL.md +3 -3
- package/skills/agentflow-flow-recipes/SKILL.md +1 -1
- package/skills/agentflow-flow-recipes/references/recipes.md +1 -2
- package/skills/agentflow-flow-sync-ui/SKILL.md +1 -1
- package/skills/agentflow-node-dsl/SKILL.md +44 -13
- package/skills/agentflow-node-reference/references/builtin-nodes.md +25 -0
- package/skills/agentflow-workspace-markdown/SKILL.md +1 -1
- package/builtin/web-ui/dist/assets/index-B9ppXv7e.css +0 -1
- package/builtin/web-ui/dist/assets/index-CdEQWRrp.js +0 -872
- package/skills/agentflow-workspace-graph/SKILL.md +0 -85
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
function emptyStore() {
|
|
7
|
+
return { version: 1, profiles: {}, pending: {} };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function agentFlowAuthFile() {
|
|
11
|
+
const configured = String(process.env.AGENTFLOW_AUTH_FILE || "").trim();
|
|
12
|
+
return path.resolve(configured || path.join(os.homedir(), ".agentflow", "auth.json"));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function readAgentFlowAuthStore() {
|
|
16
|
+
const file = agentFlowAuthFile();
|
|
17
|
+
try {
|
|
18
|
+
const data = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
19
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return emptyStore();
|
|
20
|
+
return {
|
|
21
|
+
version: 1,
|
|
22
|
+
profiles: data.profiles && typeof data.profiles === "object" && !Array.isArray(data.profiles) ? data.profiles : {},
|
|
23
|
+
pending: data.pending && typeof data.pending === "object" && !Array.isArray(data.pending) ? data.pending : {},
|
|
24
|
+
};
|
|
25
|
+
} catch {
|
|
26
|
+
return emptyStore();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function writeAgentFlowAuthStore(store) {
|
|
31
|
+
const file = agentFlowAuthFile();
|
|
32
|
+
const dir = path.dirname(file);
|
|
33
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
34
|
+
try { fs.chmodSync(dir, 0o700); } catch {}
|
|
35
|
+
const temp = `${file}.${process.pid}.${crypto.randomBytes(6).toString("hex")}.tmp`;
|
|
36
|
+
fs.writeFileSync(temp, `${JSON.stringify(store, null, 2)}\n`, { encoding: "utf-8", mode: 0o600 });
|
|
37
|
+
try { fs.chmodSync(temp, 0o600); } catch {}
|
|
38
|
+
fs.renameSync(temp, file);
|
|
39
|
+
try { fs.chmodSync(file, 0o600); } catch {}
|
|
40
|
+
return file;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function profileKey(baseUrl) {
|
|
44
|
+
return String(baseUrl || "").replace(/\/+$/, "");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function savedAgentFlowProfile(baseUrl) {
|
|
48
|
+
const key = profileKey(baseUrl);
|
|
49
|
+
const profile = readAgentFlowAuthStore().profiles[key];
|
|
50
|
+
if (!profile || typeof profile !== "object") return null;
|
|
51
|
+
if (Number(profile.expiresAt) > 0 && Number(profile.expiresAt) <= Date.now()) return null;
|
|
52
|
+
const token = String(profile.token || "").trim();
|
|
53
|
+
return token ? { ...profile, token } : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function savedAgentFlowPendingAuthorization(baseUrl) {
|
|
57
|
+
const key = profileKey(baseUrl);
|
|
58
|
+
const pending = readAgentFlowAuthStore().pending[key];
|
|
59
|
+
if (!pending || typeof pending !== "object") return null;
|
|
60
|
+
if (Number(pending.expiresAt) > 0 && Number(pending.expiresAt) <= Date.now()) return null;
|
|
61
|
+
return { ...pending };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function saveAgentFlowPendingAuthorization(baseUrl, pending) {
|
|
65
|
+
const key = profileKey(baseUrl);
|
|
66
|
+
const store = readAgentFlowAuthStore();
|
|
67
|
+
store.pending[key] = {
|
|
68
|
+
requestId: String(pending.requestId || ""),
|
|
69
|
+
deviceCode: String(pending.deviceCode || ""),
|
|
70
|
+
userCode: String(pending.userCode || ""),
|
|
71
|
+
verificationUrl: String(pending.verificationUrl || ""),
|
|
72
|
+
expiresAt: Number(pending.expiresAt) || 0,
|
|
73
|
+
pollInterval: Number(pending.pollInterval) || 3,
|
|
74
|
+
createdAt: Date.now(),
|
|
75
|
+
};
|
|
76
|
+
writeAgentFlowAuthStore(store);
|
|
77
|
+
return { ...store.pending[key], deviceCode: undefined };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function saveAgentFlowProfile(baseUrl, profile) {
|
|
81
|
+
const key = profileKey(baseUrl);
|
|
82
|
+
const store = readAgentFlowAuthStore();
|
|
83
|
+
store.profiles[key] = {
|
|
84
|
+
token: String(profile.token || ""),
|
|
85
|
+
tokenType: String(profile.tokenType || "Bearer"),
|
|
86
|
+
expiresAt: Number(profile.expiresAt) || 0,
|
|
87
|
+
user: profile.user && typeof profile.user === "object" ? profile.user : null,
|
|
88
|
+
scopes: Array.isArray(profile.scopes) ? profile.scopes.map(String) : [],
|
|
89
|
+
updatedAt: Date.now(),
|
|
90
|
+
};
|
|
91
|
+
delete store.pending[key];
|
|
92
|
+
const file = writeAgentFlowAuthStore(store);
|
|
93
|
+
return { file, profile: { ...store.profiles[key], token: undefined } };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function clearAgentFlowPendingAuthorization(baseUrl) {
|
|
97
|
+
const key = profileKey(baseUrl);
|
|
98
|
+
const store = readAgentFlowAuthStore();
|
|
99
|
+
if (!store.pending[key]) return false;
|
|
100
|
+
delete store.pending[key];
|
|
101
|
+
writeAgentFlowAuthStore(store);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function clearAgentFlowProfile(baseUrl) {
|
|
106
|
+
const key = profileKey(baseUrl);
|
|
107
|
+
const store = readAgentFlowAuthStore();
|
|
108
|
+
const existed = Boolean(store.profiles[key] || store.pending[key]);
|
|
109
|
+
delete store.profiles[key];
|
|
110
|
+
delete store.pending[key];
|
|
111
|
+
if (existed) writeAgentFlowAuthStore(store);
|
|
112
|
+
return existed;
|
|
113
|
+
}
|
|
@@ -7,6 +7,15 @@ import {
|
|
|
7
7
|
loadAgentFlowRuntime,
|
|
8
8
|
resolveAgentFlowPackageRoot,
|
|
9
9
|
} from "./agentflow-runtime.mjs";
|
|
10
|
+
import {
|
|
11
|
+
agentFlowAuthFile,
|
|
12
|
+
clearAgentFlowPendingAuthorization,
|
|
13
|
+
clearAgentFlowProfile,
|
|
14
|
+
saveAgentFlowPendingAuthorization,
|
|
15
|
+
saveAgentFlowProfile,
|
|
16
|
+
savedAgentFlowPendingAuthorization,
|
|
17
|
+
savedAgentFlowProfile,
|
|
18
|
+
} from "./agentflow-auth-store.mjs";
|
|
10
19
|
import { createWorkflowReportClient } from "./workflow-report-client.mjs";
|
|
11
20
|
|
|
12
21
|
const DEFAULT_BASE_URL = "http://ai.mengma.bigo.inner/";
|
|
@@ -48,13 +57,20 @@ Usage:
|
|
|
48
57
|
Config:
|
|
49
58
|
--base-url <url> Override AGENTFLOW_BASE_URL
|
|
50
59
|
--token <token> Override AGENTFLOW_TOKEN
|
|
51
|
-
--agentflow-package-root <dir>
|
|
60
|
+
--agentflow-package-root <dir> Development override for the bundled Skill runtime
|
|
52
61
|
AGENTFLOW_BASE_URL Defaults to ${DEFAULT_BASE_URL}
|
|
53
|
-
AGENTFLOW_TOKEN
|
|
62
|
+
AGENTFLOW_TOKEN Overrides saved browser authorization
|
|
54
63
|
AGENTFLOW_ENV_FILE Optional dotenv file path
|
|
64
|
+
AGENTFLOW_AUTH_FILE Optional saved authorization path
|
|
55
65
|
|
|
56
66
|
Commands:
|
|
57
67
|
config
|
|
68
|
+
auth start | auth login
|
|
69
|
+
auth complete
|
|
70
|
+
auth status
|
|
71
|
+
auth logout
|
|
72
|
+
dsl-lint --file <flowDir|workspace.flow.js> [--workspace-root <dir>]
|
|
73
|
+
dsl-layout --file <flowDir|workspace.flow.js> [--workspace-root <dir>] [--all]
|
|
58
74
|
list-workspace | list-workspaces
|
|
59
75
|
list-flows
|
|
60
76
|
node-package-list
|
|
@@ -63,17 +79,27 @@ Commands:
|
|
|
63
79
|
node-package-install --node <id>@<version> [--workspace-root <dir>]
|
|
64
80
|
node-package-sync --flow <flowDir|workspace.flow.js> [--workspace-root <dir>]
|
|
65
81
|
pull-flow --flow-id <id> [--flow-source user] [--output <flowDir>] [--workspace-root <dir>] [--replace]
|
|
66
|
-
publish-flow --flow-id <id> --file <flowDir|workspace.flow.js|flow.yaml> [--target-space personal|workspace|team] [--with-dependencies] [--replace]
|
|
82
|
+
publish-flow --flow-id <id> --file <flowDir|workspace.flow.js|flow.yaml> [--target-space personal|workspace|team] [--schedule enabled|disabled|preserve] [--with-dependencies] [--replace]
|
|
67
83
|
get-graph --flow-id <id> [--flow-source user]
|
|
68
84
|
migrate-flow --flow-id <id> [--flow-source user] [--archived] [--allow-loss]
|
|
69
85
|
migrate-all [--include-archived] [--allow-loss] [--dry-run]
|
|
70
86
|
workspace-preview --file <flowDir|workspace.flow.js|workspace.graph.json> [--preview-id <id>] [--ttl-seconds <n>]
|
|
87
|
+
draft-create --file <flowDir|workspace.flow.js|workspace.graph.json> [--draft-id <id>] [--ttl-seconds <n>] [--with-dependencies]
|
|
88
|
+
draft-pull --draft-id <id> [--output <flowDir>] [--workspace-root <dir>] [--replace]
|
|
89
|
+
draft-update --draft-id <id> --base-revision <revision> --file <flowDir|workspace.flow.js|workspace.graph.json> [--ttl-seconds <n>] [--with-dependencies]
|
|
90
|
+
draft-run --draft-id <id> [--run-node-id <id>] [--input k=v]
|
|
91
|
+
draft-publish --draft-id <id> --flow-id <id> [--target-space personal|workspace|team] [--schedule enabled|disabled|preserve]
|
|
71
92
|
run --flow-id <id> [--flow-source user] [--run-node-id <id>] [--input k=v]
|
|
72
93
|
status --flow-id <id> [--flow-source user]
|
|
73
94
|
list-run-by-workspace | list-runs-by-workspace --workspace <flowId> [--limit 20]
|
|
74
95
|
list-runs [--flow-id <id>] [--flow-source user] [--limit 20]
|
|
75
96
|
logs --run-id <id>
|
|
76
97
|
display-outputs --flow-id <id> [--flow-source user]
|
|
98
|
+
schedule-list [--flow-id <id>] [--flow-source user]
|
|
99
|
+
schedule-set --flow-id <id> --schedule-node-id <id> [--enabled true|false] [--cron <expr>] [--timezone <tz>] [--overlap-policy skip]
|
|
100
|
+
schedule-enable --flow-id <id> --schedule-node-id <id>
|
|
101
|
+
schedule-disable --flow-id <id> --schedule-node-id <id>
|
|
102
|
+
schedule-run-now --flow-id <id> --schedule-node-id <id> [--input k=v]
|
|
77
103
|
sync-workspace --workspace <id>
|
|
78
104
|
workflow-get --workflow tapd:<id> [--flow-id <id>] [--runtime-only] [--admin-operation repair-version-membership]
|
|
79
105
|
workflow-access-sync --workflow tapd:<id> --file <access.json>
|
|
@@ -158,11 +184,23 @@ function normalizedBaseUrl(args) {
|
|
|
158
184
|
return String(raw).replace(/\/+$/, "");
|
|
159
185
|
}
|
|
160
186
|
|
|
187
|
+
function resolvedAuth(args) {
|
|
188
|
+
const fromFlag = option(args, "token");
|
|
189
|
+
if (fromFlag) return { token: fromFlag, source: "flag", profile: null };
|
|
190
|
+
const fromTokenEnv = String(process.env.AGENTFLOW_TOKEN || "").trim();
|
|
191
|
+
if (fromTokenEnv) return { token: fromTokenEnv, source: "AGENTFLOW_TOKEN", profile: null };
|
|
192
|
+
const fromSessionEnv = String(process.env.AGENTFLOW_SESSION_TOKEN || "").trim();
|
|
193
|
+
if (fromSessionEnv) return { token: fromSessionEnv, source: "AGENTFLOW_SESSION_TOKEN", profile: null };
|
|
194
|
+
const profile = savedAgentFlowProfile(normalizedBaseUrl(args));
|
|
195
|
+
return profile?.token
|
|
196
|
+
? { token: profile.token, source: "saved-auth", profile }
|
|
197
|
+
: { token: "", source: "", profile: null };
|
|
198
|
+
}
|
|
199
|
+
|
|
161
200
|
function authToken(args, required = true) {
|
|
162
|
-
const
|
|
163
|
-
const trimmed = String(token).trim();
|
|
201
|
+
const trimmed = String(resolvedAuth(args).token || "").trim();
|
|
164
202
|
if (required && !trimmed) {
|
|
165
|
-
throw new Error("
|
|
203
|
+
throw new Error("AgentFlow authorization is missing. Run `auth start`, open the returned URL, then run `auth complete`; or set AGENTFLOW_TOKEN.");
|
|
166
204
|
}
|
|
167
205
|
return trimmed;
|
|
168
206
|
}
|
|
@@ -488,6 +526,7 @@ async function pullFlow(args) {
|
|
|
488
526
|
ok: true,
|
|
489
527
|
flowId,
|
|
490
528
|
flowSource,
|
|
529
|
+
draft: current.draft === true,
|
|
491
530
|
outputDir,
|
|
492
531
|
revision: current.revision || "",
|
|
493
532
|
format: written.format,
|
|
@@ -529,6 +568,60 @@ function parseInputs(args) {
|
|
|
529
568
|
return inputs;
|
|
530
569
|
}
|
|
531
570
|
|
|
571
|
+
function parseBooleanOption(value, name) {
|
|
572
|
+
if (value === true || value === "true" || value === "1") return true;
|
|
573
|
+
if (value === false || value === "false" || value === "0") return false;
|
|
574
|
+
throw new Error(`${name} must be true or false.`);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
async function runRemoteWorkspace(args, { flowId, flowSource = "user", runNodeId = "" }) {
|
|
578
|
+
const graphPayload = await httpJson(args, `/api/workspace/graph${query({ flowId, flowSource })}`);
|
|
579
|
+
const result = await httpJson(args, "/api/workspace/run", {
|
|
580
|
+
method: "POST",
|
|
581
|
+
body: {
|
|
582
|
+
flowId,
|
|
583
|
+
flowSource,
|
|
584
|
+
runNodeId,
|
|
585
|
+
runAlias: option(args, "run-alias") || "",
|
|
586
|
+
graph: graphPayload.graph,
|
|
587
|
+
inputs: parseInputs(args),
|
|
588
|
+
},
|
|
589
|
+
});
|
|
590
|
+
return {
|
|
591
|
+
...result,
|
|
592
|
+
displayOutputs: extractDisplayOutputs(result?.graph),
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
async function prepareDraftGraph(args) {
|
|
597
|
+
const requestedFile = option(args, "file");
|
|
598
|
+
const withDependencies = args["with-dependencies"] === true;
|
|
599
|
+
let publishedNodePackages = [];
|
|
600
|
+
let rewrittenImports = [];
|
|
601
|
+
if (withDependencies) {
|
|
602
|
+
const rawSource = readFlowSourceFile(requestedFile, { allowPackageDependencies: true });
|
|
603
|
+
const source = await prepareFlowWithDependencies(args, requestedFile, rawSource);
|
|
604
|
+
publishedNodePackages = source.publishedNodePackages || [];
|
|
605
|
+
rewrittenImports = source.rewrittenImports || [];
|
|
606
|
+
}
|
|
607
|
+
const graph = await readWorkspaceGraphArg(requestedFile, path.resolve(option(args, "workspace-root") || process.cwd()));
|
|
608
|
+
const nodeDependencies = await graphNodePackageDependencies(graph);
|
|
609
|
+
if (nodeDependencies.length) {
|
|
610
|
+
const remoteByKey = await remoteNodePackageCatalog(args);
|
|
611
|
+
const missing = nodeDependencies.filter((item) => !remoteByKey.has(`${item.id}@${item.version}`));
|
|
612
|
+
if (missing.length) {
|
|
613
|
+
throw new Error(`Cannot create draft: server is missing node packages ${missing.map((item) => item.specifier).join(", ")}.`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
return {
|
|
617
|
+
graph,
|
|
618
|
+
file: path.resolve(String(requestedFile || "")),
|
|
619
|
+
nodeDependencies: nodeDependencies.map((item) => item.specifier),
|
|
620
|
+
publishedNodePackages,
|
|
621
|
+
rewrittenImports,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
|
|
532
625
|
function slotText(slots, names = []) {
|
|
533
626
|
const wanted = new Set(names.map(String));
|
|
534
627
|
for (const slot of Array.isArray(slots) ? slots : []) {
|
|
@@ -566,6 +659,125 @@ function printJson(data) {
|
|
|
566
659
|
process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
|
|
567
660
|
}
|
|
568
661
|
|
|
662
|
+
function authAction(command, args) {
|
|
663
|
+
if (command === "auth") return String(args._[1] || "status").trim().toLowerCase();
|
|
664
|
+
if (command.startsWith("auth-")) return command.slice("auth-".length).toLowerCase();
|
|
665
|
+
return "";
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
async function runAuthCommand(action, args) {
|
|
669
|
+
const baseUrl = normalizedBaseUrl(args);
|
|
670
|
+
if (action === "start" || action === "login") {
|
|
671
|
+
const authorization = await httpJson(args, "/api/auth/cli/device", {
|
|
672
|
+
method: "POST",
|
|
673
|
+
tokenRequired: false,
|
|
674
|
+
body: {
|
|
675
|
+
clientName: option(args, "client-name") || "AgentFlow CLI",
|
|
676
|
+
},
|
|
677
|
+
});
|
|
678
|
+
const saved = saveAgentFlowPendingAuthorization(baseUrl, authorization);
|
|
679
|
+
printJson({
|
|
680
|
+
status: "authorization_required",
|
|
681
|
+
baseUrl,
|
|
682
|
+
requestId: saved.requestId,
|
|
683
|
+
userCode: saved.userCode,
|
|
684
|
+
verificationUrl: saved.verificationUrl,
|
|
685
|
+
expiresAt: saved.expiresAt,
|
|
686
|
+
next: "Open verificationUrl, approve access, then run `auth complete`.",
|
|
687
|
+
});
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (action === "complete") {
|
|
692
|
+
const pending = savedAgentFlowPendingAuthorization(baseUrl);
|
|
693
|
+
if (!pending?.deviceCode) {
|
|
694
|
+
throw new Error("No pending AgentFlow authorization. Run `auth start` first.");
|
|
695
|
+
}
|
|
696
|
+
let result;
|
|
697
|
+
try {
|
|
698
|
+
result = await httpJson(args, "/api/auth/cli/token", {
|
|
699
|
+
method: "POST",
|
|
700
|
+
tokenRequired: false,
|
|
701
|
+
body: { deviceCode: pending.deviceCode },
|
|
702
|
+
});
|
|
703
|
+
} catch (error) {
|
|
704
|
+
const code = String(error?.response?.code || "");
|
|
705
|
+
if (["access_denied", "expired_token", "invalid_grant"].includes(code)) {
|
|
706
|
+
clearAgentFlowPendingAuthorization(baseUrl);
|
|
707
|
+
}
|
|
708
|
+
throw error;
|
|
709
|
+
}
|
|
710
|
+
if (result?.code === "authorization_pending") {
|
|
711
|
+
printJson({
|
|
712
|
+
status: "authorization_pending",
|
|
713
|
+
baseUrl,
|
|
714
|
+
requestId: pending.requestId,
|
|
715
|
+
verificationUrl: pending.verificationUrl,
|
|
716
|
+
expiresAt: pending.expiresAt,
|
|
717
|
+
});
|
|
718
|
+
process.exitCode = 2;
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
if (!result?.token) throw new Error("AgentFlow authorization exchange did not return a token.");
|
|
722
|
+
const saved = saveAgentFlowProfile(baseUrl, result);
|
|
723
|
+
printJson({
|
|
724
|
+
status: "authenticated",
|
|
725
|
+
baseUrl,
|
|
726
|
+
user: result.user || null,
|
|
727
|
+
scopes: Array.isArray(result.scopes) ? result.scopes : [],
|
|
728
|
+
expiresAt: result.expiresAt || 0,
|
|
729
|
+
credentialFile: saved.file,
|
|
730
|
+
});
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (action === "status") {
|
|
735
|
+
const auth = resolvedAuth(args);
|
|
736
|
+
if (!auth.token) {
|
|
737
|
+
printJson({ authenticated: false, baseUrl, tokenSource: "", credentialFile: agentFlowAuthFile() });
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
const me = await httpJson(args, "/api/auth/me", { tokenRequired: false });
|
|
741
|
+
printJson({
|
|
742
|
+
authenticated: Boolean(me?.authenticated),
|
|
743
|
+
baseUrl,
|
|
744
|
+
tokenSource: auth.source,
|
|
745
|
+
user: me?.user || null,
|
|
746
|
+
expiresAt: auth.profile?.expiresAt || me?.user?.sessionExpiresAt || 0,
|
|
747
|
+
credentialFile: auth.source === "saved-auth" ? agentFlowAuthFile() : "",
|
|
748
|
+
});
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
if (action === "logout") {
|
|
753
|
+
const auth = resolvedAuth(args);
|
|
754
|
+
let revoked = false;
|
|
755
|
+
let remoteError = "";
|
|
756
|
+
if (auth.token) {
|
|
757
|
+
try {
|
|
758
|
+
const response = await httpJson(args, "/api/auth/cli/revoke", { method: "POST", body: {} });
|
|
759
|
+
revoked = Boolean(response?.ok);
|
|
760
|
+
} catch (error) {
|
|
761
|
+
remoteError = error?.message || String(error);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
const localCleared = clearAgentFlowProfile(baseUrl);
|
|
765
|
+
printJson({
|
|
766
|
+
authenticated: false,
|
|
767
|
+
baseUrl,
|
|
768
|
+
revoked,
|
|
769
|
+
localCleared,
|
|
770
|
+
tokenSource: auth.source,
|
|
771
|
+
warning: auth.source && auth.source !== "saved-auth"
|
|
772
|
+
? `${auth.source} is still configured outside the CLI credential store.`
|
|
773
|
+
: remoteError || "",
|
|
774
|
+
});
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
throw new Error(`Unknown auth command: ${action}. Use auth start, complete, status, or logout.`);
|
|
779
|
+
}
|
|
780
|
+
|
|
569
781
|
function requireFlowId(args) {
|
|
570
782
|
const flowId = option(args, "flow-id") || option(args, "flow");
|
|
571
783
|
if (!flowId) throw new Error("Missing --flow-id.");
|
|
@@ -755,16 +967,48 @@ function isMissingPublishedFlowError(error) {
|
|
|
755
967
|
return error?.status === 400 && /Pipeline directory not found/i.test(String(error?.message || ""));
|
|
756
968
|
}
|
|
757
969
|
|
|
758
|
-
async function importFlow(args, { flowId, targetSpace, resolved, flowYaml }) {
|
|
970
|
+
async function importFlow(args, { flowId, targetSpace, scheduleMode = "disabled", resolved, flowYaml }) {
|
|
759
971
|
const form = new FormData();
|
|
760
972
|
form.set("flowId", flowId);
|
|
761
973
|
form.set("targetSpace", targetSpace);
|
|
974
|
+
form.set("scheduleMode", scheduleMode);
|
|
762
975
|
const name = path.basename(resolved);
|
|
763
976
|
const mime = /\.m?js$/i.test(name) ? "application/javascript" : "application/yaml";
|
|
764
977
|
form.set("file", new Blob([flowYaml], { type: mime }), name);
|
|
765
978
|
return httpMultipart(args, "/api/flows/import", form);
|
|
766
979
|
}
|
|
767
980
|
|
|
981
|
+
function normalizeScheduleMode(args, fallback = "disabled") {
|
|
982
|
+
const scheduleMode = String(option(args, "schedule") || fallback).trim().toLowerCase();
|
|
983
|
+
if (!["enabled", "disabled", "preserve"].includes(scheduleMode)) {
|
|
984
|
+
throw new Error("--schedule must be enabled, disabled, or preserve.");
|
|
985
|
+
}
|
|
986
|
+
return scheduleMode;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
function graphWithScheduleMode(graph, scheduleMode = "disabled") {
|
|
990
|
+
if (scheduleMode === "preserve") return graph;
|
|
991
|
+
const instances = { ...(graph?.instances || {}) };
|
|
992
|
+
let scheduleCount = 0;
|
|
993
|
+
for (const [nodeId, instance] of Object.entries(instances)) {
|
|
994
|
+
if (String(instance?.definitionId || "") !== "workspace_scheduled_run") continue;
|
|
995
|
+
scheduleCount += 1;
|
|
996
|
+
let config = {};
|
|
997
|
+
try {
|
|
998
|
+
const parsed = JSON.parse(String(instance.body || "{}"));
|
|
999
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) config = parsed;
|
|
1000
|
+
} catch {}
|
|
1001
|
+
instances[nodeId] = {
|
|
1002
|
+
...instance,
|
|
1003
|
+
body: JSON.stringify({ ...config, enabled: scheduleMode === "enabled" }),
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
if (scheduleMode === "enabled" && scheduleCount === 0) {
|
|
1007
|
+
throw new Error("Cannot enable scheduling: the Flow has no Scheduled Run node.");
|
|
1008
|
+
}
|
|
1009
|
+
return { ...graph, instances };
|
|
1010
|
+
}
|
|
1011
|
+
|
|
768
1012
|
async function resolvePublishTeam(args, shareWithTeam) {
|
|
769
1013
|
if (!shareWithTeam) return null;
|
|
770
1014
|
const result = await httpJson(args, "/api/teams/me");
|
|
@@ -793,6 +1037,12 @@ async function main() {
|
|
|
793
1037
|
return;
|
|
794
1038
|
}
|
|
795
1039
|
|
|
1040
|
+
const selectedAuthAction = authAction(command, args);
|
|
1041
|
+
if (selectedAuthAction) {
|
|
1042
|
+
await runAuthCommand(selectedAuthAction, args);
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
796
1046
|
if (command === "config") {
|
|
797
1047
|
let localRuntime = { available: false, root: "", version: "" };
|
|
798
1048
|
try {
|
|
@@ -802,15 +1052,39 @@ async function main() {
|
|
|
802
1052
|
} catch (error) {
|
|
803
1053
|
localRuntime.error = error?.message || String(error);
|
|
804
1054
|
}
|
|
1055
|
+
const auth = resolvedAuth(args);
|
|
805
1056
|
printJson({
|
|
806
1057
|
baseUrl: normalizedBaseUrl(args),
|
|
807
|
-
hasToken: Boolean(
|
|
808
|
-
tokenSource:
|
|
1058
|
+
hasToken: Boolean(auth.token),
|
|
1059
|
+
tokenSource: auth.source,
|
|
1060
|
+
credentialFile: auth.source === "saved-auth" ? agentFlowAuthFile() : "",
|
|
809
1061
|
localRuntime,
|
|
810
1062
|
});
|
|
811
1063
|
return;
|
|
812
1064
|
}
|
|
813
1065
|
|
|
1066
|
+
if (command === "dsl-lint" || command === "dsl-layout") {
|
|
1067
|
+
const target = option(args, "file");
|
|
1068
|
+
if (!target) throw new Error(`${command} requires --file <flowDir|workspace.flow.js>.`);
|
|
1069
|
+
const resolved = path.resolve(target);
|
|
1070
|
+
const stat = fs.statSync(resolved);
|
|
1071
|
+
const flowDir = stat.isDirectory() ? resolved : path.dirname(resolved);
|
|
1072
|
+
const workspaceRoot = path.resolve(option(args, "workspace-root") || process.cwd());
|
|
1073
|
+
const runtime = await loadAgentFlowRuntime();
|
|
1074
|
+
if (command === "dsl-lint") {
|
|
1075
|
+
const result = runtime.lintWorkspaceFlowDir(flowDir, { workspaceRoot });
|
|
1076
|
+
printJson(result);
|
|
1077
|
+
if (result.errors.length) process.exitCode = 1;
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
const result = runtime.layoutWorkspaceFlowDir(flowDir, {
|
|
1081
|
+
all: args.all === true,
|
|
1082
|
+
workspaceRoot,
|
|
1083
|
+
});
|
|
1084
|
+
printJson(result);
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
814
1088
|
if (command === "list-workspace" || command === "list-workspaces") {
|
|
815
1089
|
printJson(await httpJson(args, "/api/workspaces"));
|
|
816
1090
|
return;
|
|
@@ -885,10 +1159,23 @@ async function main() {
|
|
|
885
1159
|
return;
|
|
886
1160
|
}
|
|
887
1161
|
|
|
1162
|
+
if (command === "draft-pull") {
|
|
1163
|
+
const draftId = option(args, "draft-id");
|
|
1164
|
+
if (!draftId) throw new Error("draft-pull requires --draft-id <id>.");
|
|
1165
|
+
args["flow-id"] = draftId;
|
|
1166
|
+
args["flow-source"] = "user";
|
|
1167
|
+
const result = await pullFlow(args);
|
|
1168
|
+
if (result.ok && result.draft !== true) throw new Error(`${draftId} is not a Workspace Draft.`);
|
|
1169
|
+
printJson({ ...result, draftId });
|
|
1170
|
+
if (!result.ok) process.exitCode = 2;
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
888
1174
|
if (command === "publish-flow") {
|
|
889
1175
|
const flowId = requireFlowId(args);
|
|
890
1176
|
const destination = targetDestinationFromArgs(args);
|
|
891
1177
|
const targetSpace = destination.flowSource;
|
|
1178
|
+
const scheduleMode = normalizeScheduleMode(args);
|
|
892
1179
|
const withDependencies = args["with-dependencies"] === true;
|
|
893
1180
|
const requestedFile = option(args, "file");
|
|
894
1181
|
const rawSource = readFlowSourceFile(requestedFile, { allowPackageDependencies: withDependencies });
|
|
@@ -915,7 +1202,7 @@ async function main() {
|
|
|
915
1202
|
const dependencyPreflight = await preflightRemoteFlowDependencies(args, source);
|
|
916
1203
|
|
|
917
1204
|
if (!replace) {
|
|
918
|
-
const result = await importFlow(args, { flowId, targetSpace, ...source });
|
|
1205
|
+
const result = await importFlow(args, { flowId, targetSpace, scheduleMode, ...source });
|
|
919
1206
|
const sharedTeam = await sharePublishedFlowWithTeam(args, { flowId, flowSource: targetSpace, team });
|
|
920
1207
|
printJson({ ...result, action: "created", targetSpace: team ? "team" : targetSpace, team: sharedTeam, file: source.resolved, nodeDependencies: dependencyPreflight.map((item) => item.specifier), rewrittenImports: source.rewrittenImports || [], publishedNodePackages: source.publishedNodePackages || [] });
|
|
921
1208
|
return;
|
|
@@ -924,16 +1211,16 @@ async function main() {
|
|
|
924
1211
|
// 更新走哪条路取决于存储格式:yaml 流程改 /api/flow,代码化流程改 Workspace 图。
|
|
925
1212
|
// /api/flow 只认 flowYaml 字符串,代码化的流程发过去等于把图退回成 yaml。
|
|
926
1213
|
if (!current) {
|
|
927
|
-
const result = await importFlow(args, { flowId, targetSpace, ...source });
|
|
1214
|
+
const result = await importFlow(args, { flowId, targetSpace, scheduleMode, ...source });
|
|
928
1215
|
const sharedTeam = await sharePublishedFlowWithTeam(args, { flowId, flowSource: targetSpace, team });
|
|
929
1216
|
printJson({ ...result, action: "created", targetSpace: team ? "team" : targetSpace, team: sharedTeam, file: source.resolved, nodeDependencies: dependencyPreflight.map((item) => item.specifier), rewrittenImports: source.rewrittenImports || [], publishedNodePackages: source.publishedNodePackages || [] });
|
|
930
1217
|
return;
|
|
931
1218
|
}
|
|
932
1219
|
if (source.isCode) {
|
|
933
|
-
const graph = await readWorkspaceGraphArg(
|
|
1220
|
+
const graph = graphWithScheduleMode(await readWorkspaceGraphArg(
|
|
934
1221
|
option(args, "file"),
|
|
935
1222
|
path.resolve(option(args, "workspace-root") || process.cwd()),
|
|
936
|
-
);
|
|
1223
|
+
), scheduleMode);
|
|
937
1224
|
const updated = await httpJson(args, "/api/workspace/graph", {
|
|
938
1225
|
method: "POST",
|
|
939
1226
|
body: { flowId, flowSource: targetSpace, graph, baseRevision: current.revision },
|
|
@@ -1088,26 +1375,66 @@ async function main() {
|
|
|
1088
1375
|
return;
|
|
1089
1376
|
}
|
|
1090
1377
|
|
|
1091
|
-
if (command === "
|
|
1092
|
-
const
|
|
1093
|
-
|
|
1094
|
-
const
|
|
1095
|
-
|
|
1096
|
-
|
|
1378
|
+
if (command === "draft-create" || command === "draft-update" || command === "workspace-draft") {
|
|
1379
|
+
const draftId = option(args, "draft-id") || "";
|
|
1380
|
+
if (command === "draft-update" && !draftId) throw new Error("draft-update requires --draft-id <id>.");
|
|
1381
|
+
const baseRevision = option(args, "base-revision") || "";
|
|
1382
|
+
if (command === "draft-update" && !baseRevision) {
|
|
1383
|
+
throw new Error("draft-update requires --base-revision <revision>. Pull the Draft first if the revision is unknown.");
|
|
1384
|
+
}
|
|
1385
|
+
const prepared = await prepareDraftGraph(args);
|
|
1386
|
+
const result = await httpJson(args, "/api/workspace/draft", {
|
|
1097
1387
|
method: "POST",
|
|
1098
1388
|
body: {
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1389
|
+
graph: prepared.graph,
|
|
1390
|
+
draftId,
|
|
1391
|
+
baseRevision,
|
|
1392
|
+
title: option(args, "title") || "Workspace Draft",
|
|
1393
|
+
ttlSeconds: option(args, "ttl-seconds") ? Number(option(args, "ttl-seconds")) : undefined,
|
|
1394
|
+
resetRuntime: args["keep-runtime"] !== true,
|
|
1105
1395
|
},
|
|
1106
1396
|
});
|
|
1107
|
-
printJson({
|
|
1108
|
-
|
|
1109
|
-
|
|
1397
|
+
printJson({ ...result, ...prepared });
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
if (command === "draft-run") {
|
|
1402
|
+
const draftId = option(args, "draft-id");
|
|
1403
|
+
if (!draftId) throw new Error("draft-run requires --draft-id <id>.");
|
|
1404
|
+
printJson(await runRemoteWorkspace(args, {
|
|
1405
|
+
flowId: draftId,
|
|
1406
|
+
flowSource: "user",
|
|
1407
|
+
runNodeId: option(args, "run-node-id") || "",
|
|
1408
|
+
}));
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
if (command === "draft-publish" || command === "draft-promote") {
|
|
1413
|
+
const draftId = option(args, "draft-id");
|
|
1414
|
+
if (!draftId) throw new Error("draft-publish requires --draft-id <id>.");
|
|
1415
|
+
const flowId = requireFlowId(args);
|
|
1416
|
+
const destination = targetDestinationFromArgs(args);
|
|
1417
|
+
const targetSpace = destination.shareWithTeam ? "team" : destination.flowSource === "user" ? "personal" : "workspace";
|
|
1418
|
+
const scheduleMode = normalizeScheduleMode(args);
|
|
1419
|
+
const team = await resolvePublishTeam(args, destination.shareWithTeam);
|
|
1420
|
+
const result = await httpJson(args, "/api/workspace/draft/publish", {
|
|
1421
|
+
method: "POST",
|
|
1422
|
+
body: { draftId, flowId, targetSpace, scheduleMode },
|
|
1423
|
+
});
|
|
1424
|
+
const sharedTeam = await sharePublishedFlowWithTeam(args, {
|
|
1425
|
+
flowId,
|
|
1426
|
+
flowSource: destination.flowSource,
|
|
1427
|
+
team,
|
|
1110
1428
|
});
|
|
1429
|
+
printJson({ ...result, team: sharedTeam, targetSpace });
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
if (command === "run") {
|
|
1434
|
+
const flowId = requireFlowId(args);
|
|
1435
|
+
const flowSource = option(args, "flow-source") || "user";
|
|
1436
|
+
const runNodeId = option(args, "run-node-id") || "";
|
|
1437
|
+
printJson(await runRemoteWorkspace(args, { flowId, flowSource, runNodeId }));
|
|
1111
1438
|
return;
|
|
1112
1439
|
}
|
|
1113
1440
|
|
|
@@ -1151,6 +1478,44 @@ async function main() {
|
|
|
1151
1478
|
return;
|
|
1152
1479
|
}
|
|
1153
1480
|
|
|
1481
|
+
if (command === "schedule-list") {
|
|
1482
|
+
const flowId = option(args, "flow-id") || "";
|
|
1483
|
+
const flowSource = option(args, "flow-source") || "user";
|
|
1484
|
+
if (flowId) {
|
|
1485
|
+
printJson(await httpJson(args, `/api/workspace/schedules${query({ flowId, flowSource })}`));
|
|
1486
|
+
} else {
|
|
1487
|
+
printJson(await httpJson(args, "/api/schedules"));
|
|
1488
|
+
}
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
if (["schedule-set", "schedule-enable", "schedule-disable"].includes(command)) {
|
|
1493
|
+
const flowId = requireFlowId(args);
|
|
1494
|
+
const flowSource = option(args, "flow-source") || "user";
|
|
1495
|
+
const scheduleNodeId = option(args, "schedule-node-id");
|
|
1496
|
+
if (!scheduleNodeId) throw new Error(`${command} requires --schedule-node-id <id>.`);
|
|
1497
|
+
const body = { flowId, flowSource, scheduleNodeId };
|
|
1498
|
+
if (command === "schedule-enable") body.enabled = true;
|
|
1499
|
+
if (command === "schedule-disable") body.enabled = false;
|
|
1500
|
+
if (command === "schedule-set" && args.enabled !== undefined) {
|
|
1501
|
+
body.enabled = parseBooleanOption(args.enabled, "--enabled");
|
|
1502
|
+
}
|
|
1503
|
+
if (args.cron !== undefined) body.cron = option(args, "cron");
|
|
1504
|
+
if (args.timezone !== undefined) body.timezone = option(args, "timezone");
|
|
1505
|
+
if (args["overlap-policy"] !== undefined) body.overlapPolicy = option(args, "overlap-policy");
|
|
1506
|
+
printJson(await httpJson(args, "/api/workspace/schedule/config", { method: "POST", body }));
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
if (command === "schedule-run-now") {
|
|
1511
|
+
const flowId = requireFlowId(args);
|
|
1512
|
+
const flowSource = option(args, "flow-source") || "user";
|
|
1513
|
+
const scheduleNodeId = option(args, "schedule-node-id");
|
|
1514
|
+
if (!scheduleNodeId) throw new Error("schedule-run-now requires --schedule-node-id <id>.");
|
|
1515
|
+
printJson(await runRemoteWorkspace(args, { flowId, flowSource, runNodeId: scheduleNodeId }));
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1154
1519
|
if (command === "workflow-get") {
|
|
1155
1520
|
const workflow = workflowReferenceFromArgs(args);
|
|
1156
1521
|
if (workflow.namespace !== "tapd") {
|