@fieldwangai/agentflow 0.1.160 → 0.1.163

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.
Files changed (111) hide show
  1. package/bin/lib/auth.mjs +231 -14
  2. package/bin/lib/catalog-flows.mjs +2 -0
  3. package/bin/lib/cli-auth-page.mjs +103 -0
  4. package/bin/lib/flow-dsl/codegen.mjs +23 -2
  5. package/bin/lib/flow-dsl/lint.mjs +44 -0
  6. package/bin/lib/flow-dsl/parser.mjs +68 -1
  7. package/bin/lib/marketplace-usage.mjs +218 -0
  8. package/bin/lib/marketplace.mjs +183 -3
  9. package/bin/lib/node-package-manifest.mjs +1 -1
  10. package/bin/lib/skill-runtime.mjs +6 -0
  11. package/bin/lib/spaces.mjs +200 -0
  12. package/bin/lib/table.mjs +2 -3
  13. package/bin/lib/ui-server.mjs +471 -84
  14. package/bin/lib/workspace-draft.mjs +74 -0
  15. package/bin/lib/workspace-routes.mjs +484 -4
  16. package/bin/lib/workspace-server.mjs +373 -30
  17. package/bin/lib/workspace-state.mjs +2 -1
  18. package/builtin/nodes/agent_subAgent.md +5 -1
  19. package/builtin/nodes/context_bundle.md +44 -0
  20. package/builtin/nodes/context_knowledge.md +29 -0
  21. package/builtin/nodes/context_skills.md +29 -0
  22. package/builtin/nodes/context_workspace.md +36 -0
  23. package/builtin/nodes/control_while.md +12 -5
  24. package/builtin/nodes/tool_git_worktree_load.md +4 -3
  25. package/builtin/pipelines/new/workspace.flow.js +1 -1
  26. package/builtin/web-ui/dist/assets/{WorkflowAssistantThread-Bfo9Ythw.js → WorkflowAssistantThread-DmhIgoD7.js} +1 -1
  27. package/builtin/web-ui/dist/assets/index-CI9J6Unt.js +873 -0
  28. package/builtin/web-ui/dist/assets/index-DY5vE7v1.css +1 -0
  29. package/builtin/web-ui/dist/index.html +2 -2
  30. package/package.json +5 -1
  31. package/shared/slot-types.js +1 -0
  32. package/skills/agentflow-author-flow/SKILL.md +81 -8
  33. package/skills/agentflow-author-flow/agents/openai.yaml +3 -3
  34. package/skills/agentflow-cli/SKILL.md +179 -27
  35. package/skills/agentflow-cli/agents/openai.yaml +2 -2
  36. package/skills/agentflow-cli/runtime/bin/lib/skill-runtime.mjs +14879 -0
  37. package/skills/agentflow-cli/runtime/builtin/nodes/agent_subAgent.md +38 -0
  38. package/skills/agentflow-cli/runtime/builtin/nodes/context_bundle.md +44 -0
  39. package/skills/agentflow-cli/runtime/builtin/nodes/context_knowledge.md +29 -0
  40. package/skills/agentflow-cli/runtime/builtin/nodes/context_skills.md +29 -0
  41. package/skills/agentflow-cli/runtime/builtin/nodes/context_workspace.md +36 -0
  42. package/skills/agentflow-cli/runtime/builtin/nodes/control_agent_toBool.md +26 -0
  43. package/skills/agentflow-cli/runtime/builtin/nodes/control_anyOne.md +19 -0
  44. package/skills/agentflow-cli/runtime/builtin/nodes/control_cancelled.md +26 -0
  45. package/skills/agentflow-cli/runtime/builtin/nodes/control_cd_workspace.md +50 -0
  46. package/skills/agentflow-cli/runtime/builtin/nodes/control_delay.md +31 -0
  47. package/skills/agentflow-cli/runtime/builtin/nodes/control_end.md +13 -0
  48. package/skills/agentflow-cli/runtime/builtin/nodes/control_if.md +21 -0
  49. package/skills/agentflow-cli/runtime/builtin/nodes/control_interval_loop.md +55 -0
  50. package/skills/agentflow-cli/runtime/builtin/nodes/control_load_mcp.md +24 -0
  51. package/skills/agentflow-cli/runtime/builtin/nodes/control_load_skills.md +35 -0
  52. package/skills/agentflow-cli/runtime/builtin/nodes/control_parse_json.md +25 -0
  53. package/skills/agentflow-cli/runtime/builtin/nodes/control_start.md +13 -0
  54. package/skills/agentflow-cli/runtime/builtin/nodes/control_subflow_call.md +27 -0
  55. package/skills/agentflow-cli/runtime/builtin/nodes/control_toBool.md +27 -0
  56. package/skills/agentflow-cli/runtime/builtin/nodes/control_user_workspace.md +24 -0
  57. package/skills/agentflow-cli/runtime/builtin/nodes/control_wait_until.md +34 -0
  58. package/skills/agentflow-cli/runtime/builtin/nodes/control_while.md +131 -0
  59. package/skills/agentflow-cli/runtime/builtin/nodes/display_ascii.md +24 -0
  60. package/skills/agentflow-cli/runtime/builtin/nodes/display_chart.md +32 -0
  61. package/skills/agentflow-cli/runtime/builtin/nodes/display_code.md +39 -0
  62. package/skills/agentflow-cli/runtime/builtin/nodes/display_html.md +32 -0
  63. package/skills/agentflow-cli/runtime/builtin/nodes/display_image.md +36 -0
  64. package/skills/agentflow-cli/runtime/builtin/nodes/display_markdown.md +24 -0
  65. package/skills/agentflow-cli/runtime/builtin/nodes/display_mermaid.md +24 -0
  66. package/skills/agentflow-cli/runtime/builtin/nodes/display_react_app.md +53 -0
  67. package/skills/agentflow-cli/runtime/builtin/nodes/display_table.md +32 -0
  68. package/skills/agentflow-cli/runtime/builtin/nodes/provide_bool.md +14 -0
  69. package/skills/agentflow-cli/runtime/builtin/nodes/provide_file.md +14 -0
  70. package/skills/agentflow-cli/runtime/builtin/nodes/provide_json.md +14 -0
  71. package/skills/agentflow-cli/runtime/builtin/nodes/provide_password.md +14 -0
  72. package/skills/agentflow-cli/runtime/builtin/nodes/provide_str.md +14 -0
  73. package/skills/agentflow-cli/runtime/builtin/nodes/tool_display_share_link.md +47 -0
  74. package/skills/agentflow-cli/runtime/builtin/nodes/tool_get_env.md +20 -0
  75. package/skills/agentflow-cli/runtime/builtin/nodes/tool_git_checkout.md +70 -0
  76. package/skills/agentflow-cli/runtime/builtin/nodes/tool_git_worktree_load.md +74 -0
  77. package/skills/agentflow-cli/runtime/builtin/nodes/tool_git_worktree_unload.md +52 -0
  78. package/skills/agentflow-cli/runtime/builtin/nodes/tool_gitlab_create_mr.md +114 -0
  79. package/skills/agentflow-cli/runtime/builtin/nodes/tool_jenkins_build.md +67 -0
  80. package/skills/agentflow-cli/runtime/builtin/nodes/tool_load_key.md +26 -0
  81. package/skills/agentflow-cli/runtime/builtin/nodes/tool_nodejs.md +52 -0
  82. package/skills/agentflow-cli/runtime/builtin/nodes/tool_print.md +21 -0
  83. package/skills/agentflow-cli/runtime/builtin/nodes/tool_save_key.md +26 -0
  84. package/skills/agentflow-cli/runtime/builtin/nodes/tool_set_run_env.md +38 -0
  85. package/skills/agentflow-cli/runtime/builtin/nodes/tool_user_ask.md +27 -0
  86. package/skills/agentflow-cli/runtime/builtin/nodes/tool_user_check.md +26 -0
  87. package/skills/agentflow-cli/runtime/builtin/nodes/tool_wecom_send_app_markdown.md +57 -0
  88. package/skills/agentflow-cli/runtime/builtin/nodes/tool_wecom_send_group_markdown.md +45 -0
  89. package/skills/agentflow-cli/runtime/builtin/nodes/workspace_one_click_task.md +44 -0
  90. package/skills/agentflow-cli/runtime/builtin/nodes/workspace_run.md +16 -0
  91. package/skills/agentflow-cli/runtime/builtin/nodes/workspace_scheduled_run.md +16 -0
  92. package/skills/agentflow-cli/runtime/builtin/nodes/workspace_subflow_input.md +14 -0
  93. package/skills/agentflow-cli/runtime/package.json +6 -0
  94. package/skills/agentflow-cli/scripts/agentflow-auth-store.mjs +113 -0
  95. package/skills/agentflow-cli/scripts/agentflow-cli.mjs +457 -28
  96. package/skills/agentflow-cli/scripts/agentflow-runtime.mjs +11 -4
  97. package/skills/agentflow-flow-add-instances/SKILL.md +2 -2
  98. package/skills/agentflow-flow-dsl/SKILL.md +73 -8
  99. package/skills/agentflow-flow-dsl/agents/openai.yaml +2 -2
  100. package/skills/agentflow-flow-dsl/references/node-calls.md +6 -2
  101. package/skills/agentflow-flow-dsl/references/subflow-authoring.md +34 -7
  102. package/skills/agentflow-flow-edit-node-fields/SKILL.md +3 -3
  103. package/skills/agentflow-flow-recipes/SKILL.md +1 -1
  104. package/skills/agentflow-flow-recipes/references/recipes.md +1 -2
  105. package/skills/agentflow-flow-sync-ui/SKILL.md +1 -1
  106. package/skills/agentflow-node-dsl/SKILL.md +5 -13
  107. package/skills/agentflow-node-reference/references/builtin-nodes.md +37 -5
  108. package/skills/agentflow-workspace-markdown/SKILL.md +1 -1
  109. package/builtin/web-ui/dist/assets/index-XbeI5foV.js +0 -872
  110. package/builtin/web-ui/dist/assets/index-e1omCEau.css +0 -1
  111. package/skills/agentflow-workspace-graph/SKILL.md +0 -85
@@ -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> Override the local @fieldwangai/agentflow runtime
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 Required unless AGENTFLOW_SESSION_TOKEN is set
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
@@ -62,18 +78,32 @@ Commands:
62
78
  node-package-publish --file <nodePackageDir>
63
79
  node-package-install --node <id>@<version> [--workspace-root <dir>]
64
80
  node-package-sync --flow <flowDir|workspace.flow.js> [--workspace-root <dir>]
81
+ marketplace-list [--kind flow|node] [--owned] [--query <text>]
82
+ marketplace-flow-publish --flow-id <id> [--flow-source user] [--version 1.0.0] [--visibility public|private]
83
+ marketplace-flow-install --flow <id>@<version> [--flow-id <targetId>]
84
+ marketplace-visibility --kind flow|node --resource <id>@<version> --visibility public|private
65
85
  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]
86
+ 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
87
  get-graph --flow-id <id> [--flow-source user]
68
88
  migrate-flow --flow-id <id> [--flow-source user] [--archived] [--allow-loss]
69
89
  migrate-all [--include-archived] [--allow-loss] [--dry-run]
70
90
  workspace-preview --file <flowDir|workspace.flow.js|workspace.graph.json> [--preview-id <id>] [--ttl-seconds <n>]
91
+ draft-create --file <flowDir|workspace.flow.js|workspace.graph.json> [--draft-id <id>] [--ttl-seconds <n>] [--with-dependencies]
92
+ draft-pull --draft-id <id> [--output <flowDir>] [--workspace-root <dir>] [--replace]
93
+ draft-update --draft-id <id> --base-revision <revision> --file <flowDir|workspace.flow.js|workspace.graph.json> [--ttl-seconds <n>] [--with-dependencies]
94
+ draft-run --draft-id <id> [--run-node-id <id>] [--input k=v]
95
+ draft-publish --draft-id <id> --flow-id <id> [--target-space personal|workspace|team] [--schedule enabled|disabled|preserve]
71
96
  run --flow-id <id> [--flow-source user] [--run-node-id <id>] [--input k=v]
72
97
  status --flow-id <id> [--flow-source user]
73
98
  list-run-by-workspace | list-runs-by-workspace --workspace <flowId> [--limit 20]
74
99
  list-runs [--flow-id <id>] [--flow-source user] [--limit 20]
75
100
  logs --run-id <id>
76
101
  display-outputs --flow-id <id> [--flow-source user]
102
+ schedule-list [--flow-id <id>] [--flow-source user]
103
+ schedule-set --flow-id <id> --schedule-node-id <id> [--enabled true|false] [--cron <expr>] [--timezone <tz>] [--overlap-policy skip]
104
+ schedule-enable --flow-id <id> --schedule-node-id <id>
105
+ schedule-disable --flow-id <id> --schedule-node-id <id>
106
+ schedule-run-now --flow-id <id> --schedule-node-id <id> [--input k=v]
77
107
  sync-workspace --workspace <id>
78
108
  workflow-get --workflow tapd:<id> [--flow-id <id>] [--runtime-only] [--admin-operation repair-version-membership]
79
109
  workflow-access-sync --workflow tapd:<id> --file <access.json>
@@ -158,11 +188,23 @@ function normalizedBaseUrl(args) {
158
188
  return String(raw).replace(/\/+$/, "");
159
189
  }
160
190
 
191
+ function resolvedAuth(args) {
192
+ const fromFlag = option(args, "token");
193
+ if (fromFlag) return { token: fromFlag, source: "flag", profile: null };
194
+ const fromTokenEnv = String(process.env.AGENTFLOW_TOKEN || "").trim();
195
+ if (fromTokenEnv) return { token: fromTokenEnv, source: "AGENTFLOW_TOKEN", profile: null };
196
+ const fromSessionEnv = String(process.env.AGENTFLOW_SESSION_TOKEN || "").trim();
197
+ if (fromSessionEnv) return { token: fromSessionEnv, source: "AGENTFLOW_SESSION_TOKEN", profile: null };
198
+ const profile = savedAgentFlowProfile(normalizedBaseUrl(args));
199
+ return profile?.token
200
+ ? { token: profile.token, source: "saved-auth", profile }
201
+ : { token: "", source: "", profile: null };
202
+ }
203
+
161
204
  function authToken(args, required = true) {
162
- const token = option(args, "token") || process.env.AGENTFLOW_TOKEN || process.env.AGENTFLOW_SESSION_TOKEN || "";
163
- const trimmed = String(token).trim();
205
+ const trimmed = String(resolvedAuth(args).token || "").trim();
164
206
  if (required && !trimmed) {
165
- throw new Error("Missing AGENTFLOW_TOKEN. Set it in env, .env, .agentflow.env, or pass --token.");
207
+ throw new Error("AgentFlow authorization is missing. Run `auth start`, open the returned URL, then run `auth complete`; or set AGENTFLOW_TOKEN.");
166
208
  }
167
209
  return trimmed;
168
210
  }
@@ -333,6 +375,11 @@ function searchNodePackageCatalog(nodes, queryText, limitValue) {
333
375
  inputs: node.inputs || node.input || [],
334
376
  outputs: node.outputs || node.output || [],
335
377
  contentSha256: node.contentSha256 || "",
378
+ visibility: node.visibility || "public",
379
+ useCount: Number(node.useCount || 0),
380
+ installCount: Number(node.installCount || 0),
381
+ uniqueUserCount: Number(node.uniqueUserCount || 0),
382
+ lastUsedAt: node.lastUsedAt || "",
336
383
  installCommand: `node-package-install --node ${node.id}@${node.version}`,
337
384
  }));
338
385
  }
@@ -488,6 +535,7 @@ async function pullFlow(args) {
488
535
  ok: true,
489
536
  flowId,
490
537
  flowSource,
538
+ draft: current.draft === true,
491
539
  outputDir,
492
540
  revision: current.revision || "",
493
541
  format: written.format,
@@ -529,6 +577,60 @@ function parseInputs(args) {
529
577
  return inputs;
530
578
  }
531
579
 
580
+ function parseBooleanOption(value, name) {
581
+ if (value === true || value === "true" || value === "1") return true;
582
+ if (value === false || value === "false" || value === "0") return false;
583
+ throw new Error(`${name} must be true or false.`);
584
+ }
585
+
586
+ async function runRemoteWorkspace(args, { flowId, flowSource = "user", runNodeId = "" }) {
587
+ const graphPayload = await httpJson(args, `/api/workspace/graph${query({ flowId, flowSource })}`);
588
+ const result = await httpJson(args, "/api/workspace/run", {
589
+ method: "POST",
590
+ body: {
591
+ flowId,
592
+ flowSource,
593
+ runNodeId,
594
+ runAlias: option(args, "run-alias") || "",
595
+ graph: graphPayload.graph,
596
+ inputs: parseInputs(args),
597
+ },
598
+ });
599
+ return {
600
+ ...result,
601
+ displayOutputs: extractDisplayOutputs(result?.graph),
602
+ };
603
+ }
604
+
605
+ async function prepareDraftGraph(args) {
606
+ const requestedFile = option(args, "file");
607
+ const withDependencies = args["with-dependencies"] === true;
608
+ let publishedNodePackages = [];
609
+ let rewrittenImports = [];
610
+ if (withDependencies) {
611
+ const rawSource = readFlowSourceFile(requestedFile, { allowPackageDependencies: true });
612
+ const source = await prepareFlowWithDependencies(args, requestedFile, rawSource);
613
+ publishedNodePackages = source.publishedNodePackages || [];
614
+ rewrittenImports = source.rewrittenImports || [];
615
+ }
616
+ const graph = await readWorkspaceGraphArg(requestedFile, path.resolve(option(args, "workspace-root") || process.cwd()));
617
+ const nodeDependencies = await graphNodePackageDependencies(graph);
618
+ if (nodeDependencies.length) {
619
+ const remoteByKey = await remoteNodePackageCatalog(args);
620
+ const missing = nodeDependencies.filter((item) => !remoteByKey.has(`${item.id}@${item.version}`));
621
+ if (missing.length) {
622
+ throw new Error(`Cannot create draft: server is missing node packages ${missing.map((item) => item.specifier).join(", ")}.`);
623
+ }
624
+ }
625
+ return {
626
+ graph,
627
+ file: path.resolve(String(requestedFile || "")),
628
+ nodeDependencies: nodeDependencies.map((item) => item.specifier),
629
+ publishedNodePackages,
630
+ rewrittenImports,
631
+ };
632
+ }
633
+
532
634
  function slotText(slots, names = []) {
533
635
  const wanted = new Set(names.map(String));
534
636
  for (const slot of Array.isArray(slots) ? slots : []) {
@@ -566,6 +668,125 @@ function printJson(data) {
566
668
  process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
567
669
  }
568
670
 
671
+ function authAction(command, args) {
672
+ if (command === "auth") return String(args._[1] || "status").trim().toLowerCase();
673
+ if (command.startsWith("auth-")) return command.slice("auth-".length).toLowerCase();
674
+ return "";
675
+ }
676
+
677
+ async function runAuthCommand(action, args) {
678
+ const baseUrl = normalizedBaseUrl(args);
679
+ if (action === "start" || action === "login") {
680
+ const authorization = await httpJson(args, "/api/auth/cli/device", {
681
+ method: "POST",
682
+ tokenRequired: false,
683
+ body: {
684
+ clientName: option(args, "client-name") || "AgentFlow CLI",
685
+ },
686
+ });
687
+ const saved = saveAgentFlowPendingAuthorization(baseUrl, authorization);
688
+ printJson({
689
+ status: "authorization_required",
690
+ baseUrl,
691
+ requestId: saved.requestId,
692
+ userCode: saved.userCode,
693
+ verificationUrl: saved.verificationUrl,
694
+ expiresAt: saved.expiresAt,
695
+ next: "Open verificationUrl, approve access, then run `auth complete`.",
696
+ });
697
+ return;
698
+ }
699
+
700
+ if (action === "complete") {
701
+ const pending = savedAgentFlowPendingAuthorization(baseUrl);
702
+ if (!pending?.deviceCode) {
703
+ throw new Error("No pending AgentFlow authorization. Run `auth start` first.");
704
+ }
705
+ let result;
706
+ try {
707
+ result = await httpJson(args, "/api/auth/cli/token", {
708
+ method: "POST",
709
+ tokenRequired: false,
710
+ body: { deviceCode: pending.deviceCode },
711
+ });
712
+ } catch (error) {
713
+ const code = String(error?.response?.code || "");
714
+ if (["access_denied", "expired_token", "invalid_grant"].includes(code)) {
715
+ clearAgentFlowPendingAuthorization(baseUrl);
716
+ }
717
+ throw error;
718
+ }
719
+ if (result?.code === "authorization_pending") {
720
+ printJson({
721
+ status: "authorization_pending",
722
+ baseUrl,
723
+ requestId: pending.requestId,
724
+ verificationUrl: pending.verificationUrl,
725
+ expiresAt: pending.expiresAt,
726
+ });
727
+ process.exitCode = 2;
728
+ return;
729
+ }
730
+ if (!result?.token) throw new Error("AgentFlow authorization exchange did not return a token.");
731
+ const saved = saveAgentFlowProfile(baseUrl, result);
732
+ printJson({
733
+ status: "authenticated",
734
+ baseUrl,
735
+ user: result.user || null,
736
+ scopes: Array.isArray(result.scopes) ? result.scopes : [],
737
+ expiresAt: result.expiresAt || 0,
738
+ credentialFile: saved.file,
739
+ });
740
+ return;
741
+ }
742
+
743
+ if (action === "status") {
744
+ const auth = resolvedAuth(args);
745
+ if (!auth.token) {
746
+ printJson({ authenticated: false, baseUrl, tokenSource: "", credentialFile: agentFlowAuthFile() });
747
+ return;
748
+ }
749
+ const me = await httpJson(args, "/api/auth/me", { tokenRequired: false });
750
+ printJson({
751
+ authenticated: Boolean(me?.authenticated),
752
+ baseUrl,
753
+ tokenSource: auth.source,
754
+ user: me?.user || null,
755
+ expiresAt: auth.profile?.expiresAt || me?.user?.sessionExpiresAt || 0,
756
+ credentialFile: auth.source === "saved-auth" ? agentFlowAuthFile() : "",
757
+ });
758
+ return;
759
+ }
760
+
761
+ if (action === "logout") {
762
+ const auth = resolvedAuth(args);
763
+ let revoked = false;
764
+ let remoteError = "";
765
+ if (auth.token) {
766
+ try {
767
+ const response = await httpJson(args, "/api/auth/cli/revoke", { method: "POST", body: {} });
768
+ revoked = Boolean(response?.ok);
769
+ } catch (error) {
770
+ remoteError = error?.message || String(error);
771
+ }
772
+ }
773
+ const localCleared = clearAgentFlowProfile(baseUrl);
774
+ printJson({
775
+ authenticated: false,
776
+ baseUrl,
777
+ revoked,
778
+ localCleared,
779
+ tokenSource: auth.source,
780
+ warning: auth.source && auth.source !== "saved-auth"
781
+ ? `${auth.source} is still configured outside the CLI credential store.`
782
+ : remoteError || "",
783
+ });
784
+ return;
785
+ }
786
+
787
+ throw new Error(`Unknown auth command: ${action}. Use auth start, complete, status, or logout.`);
788
+ }
789
+
569
790
  function requireFlowId(args) {
570
791
  const flowId = option(args, "flow-id") || option(args, "flow");
571
792
  if (!flowId) throw new Error("Missing --flow-id.");
@@ -755,16 +976,48 @@ function isMissingPublishedFlowError(error) {
755
976
  return error?.status === 400 && /Pipeline directory not found/i.test(String(error?.message || ""));
756
977
  }
757
978
 
758
- async function importFlow(args, { flowId, targetSpace, resolved, flowYaml }) {
979
+ async function importFlow(args, { flowId, targetSpace, scheduleMode = "disabled", resolved, flowYaml }) {
759
980
  const form = new FormData();
760
981
  form.set("flowId", flowId);
761
982
  form.set("targetSpace", targetSpace);
983
+ form.set("scheduleMode", scheduleMode);
762
984
  const name = path.basename(resolved);
763
985
  const mime = /\.m?js$/i.test(name) ? "application/javascript" : "application/yaml";
764
986
  form.set("file", new Blob([flowYaml], { type: mime }), name);
765
987
  return httpMultipart(args, "/api/flows/import", form);
766
988
  }
767
989
 
990
+ function normalizeScheduleMode(args, fallback = "disabled") {
991
+ const scheduleMode = String(option(args, "schedule") || fallback).trim().toLowerCase();
992
+ if (!["enabled", "disabled", "preserve"].includes(scheduleMode)) {
993
+ throw new Error("--schedule must be enabled, disabled, or preserve.");
994
+ }
995
+ return scheduleMode;
996
+ }
997
+
998
+ function graphWithScheduleMode(graph, scheduleMode = "disabled") {
999
+ if (scheduleMode === "preserve") return graph;
1000
+ const instances = { ...(graph?.instances || {}) };
1001
+ let scheduleCount = 0;
1002
+ for (const [nodeId, instance] of Object.entries(instances)) {
1003
+ if (String(instance?.definitionId || "") !== "workspace_scheduled_run") continue;
1004
+ scheduleCount += 1;
1005
+ let config = {};
1006
+ try {
1007
+ const parsed = JSON.parse(String(instance.body || "{}"));
1008
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) config = parsed;
1009
+ } catch {}
1010
+ instances[nodeId] = {
1011
+ ...instance,
1012
+ body: JSON.stringify({ ...config, enabled: scheduleMode === "enabled" }),
1013
+ };
1014
+ }
1015
+ if (scheduleMode === "enabled" && scheduleCount === 0) {
1016
+ throw new Error("Cannot enable scheduling: the Flow has no Scheduled Run node.");
1017
+ }
1018
+ return { ...graph, instances };
1019
+ }
1020
+
768
1021
  async function resolvePublishTeam(args, shareWithTeam) {
769
1022
  if (!shareWithTeam) return null;
770
1023
  const result = await httpJson(args, "/api/teams/me");
@@ -793,6 +1046,12 @@ async function main() {
793
1046
  return;
794
1047
  }
795
1048
 
1049
+ const selectedAuthAction = authAction(command, args);
1050
+ if (selectedAuthAction) {
1051
+ await runAuthCommand(selectedAuthAction, args);
1052
+ return;
1053
+ }
1054
+
796
1055
  if (command === "config") {
797
1056
  let localRuntime = { available: false, root: "", version: "" };
798
1057
  try {
@@ -802,15 +1061,39 @@ async function main() {
802
1061
  } catch (error) {
803
1062
  localRuntime.error = error?.message || String(error);
804
1063
  }
1064
+ const auth = resolvedAuth(args);
805
1065
  printJson({
806
1066
  baseUrl: normalizedBaseUrl(args),
807
- hasToken: Boolean(authToken(args, false)),
808
- tokenSource: option(args, "token") ? "flag" : process.env.AGENTFLOW_TOKEN ? "AGENTFLOW_TOKEN" : process.env.AGENTFLOW_SESSION_TOKEN ? "AGENTFLOW_SESSION_TOKEN" : "",
1067
+ hasToken: Boolean(auth.token),
1068
+ tokenSource: auth.source,
1069
+ credentialFile: auth.source === "saved-auth" ? agentFlowAuthFile() : "",
809
1070
  localRuntime,
810
1071
  });
811
1072
  return;
812
1073
  }
813
1074
 
1075
+ if (command === "dsl-lint" || command === "dsl-layout") {
1076
+ const target = option(args, "file");
1077
+ if (!target) throw new Error(`${command} requires --file <flowDir|workspace.flow.js>.`);
1078
+ const resolved = path.resolve(target);
1079
+ const stat = fs.statSync(resolved);
1080
+ const flowDir = stat.isDirectory() ? resolved : path.dirname(resolved);
1081
+ const workspaceRoot = path.resolve(option(args, "workspace-root") || process.cwd());
1082
+ const runtime = await loadAgentFlowRuntime();
1083
+ if (command === "dsl-lint") {
1084
+ const result = runtime.lintWorkspaceFlowDir(flowDir, { workspaceRoot });
1085
+ printJson(result);
1086
+ if (result.errors.length) process.exitCode = 1;
1087
+ return;
1088
+ }
1089
+ const result = runtime.layoutWorkspaceFlowDir(flowDir, {
1090
+ all: args.all === true,
1091
+ workspaceRoot,
1092
+ });
1093
+ printJson(result);
1094
+ return;
1095
+ }
1096
+
814
1097
  if (command === "list-workspace" || command === "list-workspaces") {
815
1098
  printJson(await httpJson(args, "/api/workspaces"));
816
1099
  return;
@@ -878,6 +1161,61 @@ async function main() {
878
1161
  return;
879
1162
  }
880
1163
 
1164
+ if (command === "marketplace-list") {
1165
+ const kind = String(option(args, "kind") || "flow").trim().toLowerCase();
1166
+ if (kind !== "flow" && kind !== "node") throw new Error("--kind must be flow or node.");
1167
+ const result = await httpJson(args, `/api/marketplace/resources${query({
1168
+ kind,
1169
+ scope: args.owned === true ? "owned" : "",
1170
+ q: option(args, "query") || option(args, "q"),
1171
+ sort: "useCount",
1172
+ order: "desc",
1173
+ })}`);
1174
+ printJson(result);
1175
+ return;
1176
+ }
1177
+
1178
+ if (command === "marketplace-flow-publish") {
1179
+ const flowId = requireFlowId(args);
1180
+ const visibility = String(option(args, "visibility") || "public").trim().toLowerCase();
1181
+ if (visibility !== "public" && visibility !== "private") throw new Error("--visibility must be public or private.");
1182
+ printJson(await httpJson(args, "/api/marketplace/flows/publish", {
1183
+ method: "POST",
1184
+ body: {
1185
+ flowId,
1186
+ flowSource: option(args, "flow-source") || "user",
1187
+ id: option(args, "marketplace-id") || flowId,
1188
+ displayName: option(args, "display-name") || flowId,
1189
+ description: option(args, "description") || "",
1190
+ version: option(args, "version") || "1.0.0",
1191
+ visibility,
1192
+ },
1193
+ }));
1194
+ return;
1195
+ }
1196
+
1197
+ if (command === "marketplace-flow-install") {
1198
+ const spec = parseNodePackageSpec(option(args, "flow"));
1199
+ printJson(await httpJson(args, "/api/marketplace/flows/install", {
1200
+ method: "POST",
1201
+ body: { id: spec.id, version: spec.version, flowId: option(args, "flow-id") || spec.id },
1202
+ }));
1203
+ return;
1204
+ }
1205
+
1206
+ if (command === "marketplace-visibility") {
1207
+ const kind = String(option(args, "kind") || "").trim().toLowerCase();
1208
+ if (kind !== "flow" && kind !== "node") throw new Error("--kind must be flow or node.");
1209
+ const spec = parseNodePackageSpec(option(args, "resource"));
1210
+ const visibility = String(option(args, "visibility") || "").trim().toLowerCase();
1211
+ if (visibility !== "public" && visibility !== "private") throw new Error("--visibility must be public or private.");
1212
+ printJson(await httpJson(args, "/api/marketplace/visibility", {
1213
+ method: "PATCH",
1214
+ body: { kind, id: spec.id, version: spec.version, visibility },
1215
+ }));
1216
+ return;
1217
+ }
1218
+
881
1219
  if (command === "pull-flow") {
882
1220
  const result = await pullFlow(args);
883
1221
  printJson(result);
@@ -885,10 +1223,23 @@ async function main() {
885
1223
  return;
886
1224
  }
887
1225
 
1226
+ if (command === "draft-pull") {
1227
+ const draftId = option(args, "draft-id");
1228
+ if (!draftId) throw new Error("draft-pull requires --draft-id <id>.");
1229
+ args["flow-id"] = draftId;
1230
+ args["flow-source"] = "user";
1231
+ const result = await pullFlow(args);
1232
+ if (result.ok && result.draft !== true) throw new Error(`${draftId} is not a Workspace Draft.`);
1233
+ printJson({ ...result, draftId });
1234
+ if (!result.ok) process.exitCode = 2;
1235
+ return;
1236
+ }
1237
+
888
1238
  if (command === "publish-flow") {
889
1239
  const flowId = requireFlowId(args);
890
1240
  const destination = targetDestinationFromArgs(args);
891
1241
  const targetSpace = destination.flowSource;
1242
+ const scheduleMode = normalizeScheduleMode(args);
892
1243
  const withDependencies = args["with-dependencies"] === true;
893
1244
  const requestedFile = option(args, "file");
894
1245
  const rawSource = readFlowSourceFile(requestedFile, { allowPackageDependencies: withDependencies });
@@ -915,7 +1266,7 @@ async function main() {
915
1266
  const dependencyPreflight = await preflightRemoteFlowDependencies(args, source);
916
1267
 
917
1268
  if (!replace) {
918
- const result = await importFlow(args, { flowId, targetSpace, ...source });
1269
+ const result = await importFlow(args, { flowId, targetSpace, scheduleMode, ...source });
919
1270
  const sharedTeam = await sharePublishedFlowWithTeam(args, { flowId, flowSource: targetSpace, team });
920
1271
  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
1272
  return;
@@ -924,16 +1275,16 @@ async function main() {
924
1275
  // 更新走哪条路取决于存储格式:yaml 流程改 /api/flow,代码化流程改 Workspace 图。
925
1276
  // /api/flow 只认 flowYaml 字符串,代码化的流程发过去等于把图退回成 yaml。
926
1277
  if (!current) {
927
- const result = await importFlow(args, { flowId, targetSpace, ...source });
1278
+ const result = await importFlow(args, { flowId, targetSpace, scheduleMode, ...source });
928
1279
  const sharedTeam = await sharePublishedFlowWithTeam(args, { flowId, flowSource: targetSpace, team });
929
1280
  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
1281
  return;
931
1282
  }
932
1283
  if (source.isCode) {
933
- const graph = await readWorkspaceGraphArg(
1284
+ const graph = graphWithScheduleMode(await readWorkspaceGraphArg(
934
1285
  option(args, "file"),
935
1286
  path.resolve(option(args, "workspace-root") || process.cwd()),
936
- );
1287
+ ), scheduleMode);
937
1288
  const updated = await httpJson(args, "/api/workspace/graph", {
938
1289
  method: "POST",
939
1290
  body: { flowId, flowSource: targetSpace, graph, baseRevision: current.revision },
@@ -1088,26 +1439,66 @@ async function main() {
1088
1439
  return;
1089
1440
  }
1090
1441
 
1091
- if (command === "run") {
1092
- const flowId = requireFlowId(args);
1093
- const flowSource = option(args, "flow-source") || "user";
1094
- const runNodeId = option(args, "run-node-id") || "";
1095
- const graphPayload = await httpJson(args, `/api/workspace/graph${query({ flowId, flowSource })}`);
1096
- const result = await httpJson(args, "/api/workspace/run", {
1442
+ if (command === "draft-create" || command === "draft-update" || command === "workspace-draft") {
1443
+ const draftId = option(args, "draft-id") || "";
1444
+ if (command === "draft-update" && !draftId) throw new Error("draft-update requires --draft-id <id>.");
1445
+ const baseRevision = option(args, "base-revision") || "";
1446
+ if (command === "draft-update" && !baseRevision) {
1447
+ throw new Error("draft-update requires --base-revision <revision>. Pull the Draft first if the revision is unknown.");
1448
+ }
1449
+ const prepared = await prepareDraftGraph(args);
1450
+ const result = await httpJson(args, "/api/workspace/draft", {
1097
1451
  method: "POST",
1098
1452
  body: {
1099
- flowId,
1100
- flowSource,
1101
- runNodeId,
1102
- runAlias: option(args, "run-alias") || "",
1103
- graph: graphPayload.graph,
1104
- inputs: parseInputs(args),
1453
+ graph: prepared.graph,
1454
+ draftId,
1455
+ baseRevision,
1456
+ title: option(args, "title") || "Workspace Draft",
1457
+ ttlSeconds: option(args, "ttl-seconds") ? Number(option(args, "ttl-seconds")) : undefined,
1458
+ resetRuntime: args["keep-runtime"] !== true,
1105
1459
  },
1106
1460
  });
1107
- printJson({
1108
- ...result,
1109
- displayOutputs: extractDisplayOutputs(result?.graph),
1461
+ printJson({ ...result, ...prepared });
1462
+ return;
1463
+ }
1464
+
1465
+ if (command === "draft-run") {
1466
+ const draftId = option(args, "draft-id");
1467
+ if (!draftId) throw new Error("draft-run requires --draft-id <id>.");
1468
+ printJson(await runRemoteWorkspace(args, {
1469
+ flowId: draftId,
1470
+ flowSource: "user",
1471
+ runNodeId: option(args, "run-node-id") || "",
1472
+ }));
1473
+ return;
1474
+ }
1475
+
1476
+ if (command === "draft-publish" || command === "draft-promote") {
1477
+ const draftId = option(args, "draft-id");
1478
+ if (!draftId) throw new Error("draft-publish requires --draft-id <id>.");
1479
+ const flowId = requireFlowId(args);
1480
+ const destination = targetDestinationFromArgs(args);
1481
+ const targetSpace = destination.shareWithTeam ? "team" : destination.flowSource === "user" ? "personal" : "workspace";
1482
+ const scheduleMode = normalizeScheduleMode(args);
1483
+ const team = await resolvePublishTeam(args, destination.shareWithTeam);
1484
+ const result = await httpJson(args, "/api/workspace/draft/publish", {
1485
+ method: "POST",
1486
+ body: { draftId, flowId, targetSpace, scheduleMode },
1110
1487
  });
1488
+ const sharedTeam = await sharePublishedFlowWithTeam(args, {
1489
+ flowId,
1490
+ flowSource: destination.flowSource,
1491
+ team,
1492
+ });
1493
+ printJson({ ...result, team: sharedTeam, targetSpace });
1494
+ return;
1495
+ }
1496
+
1497
+ if (command === "run") {
1498
+ const flowId = requireFlowId(args);
1499
+ const flowSource = option(args, "flow-source") || "user";
1500
+ const runNodeId = option(args, "run-node-id") || "";
1501
+ printJson(await runRemoteWorkspace(args, { flowId, flowSource, runNodeId }));
1111
1502
  return;
1112
1503
  }
1113
1504
 
@@ -1151,6 +1542,44 @@ async function main() {
1151
1542
  return;
1152
1543
  }
1153
1544
 
1545
+ if (command === "schedule-list") {
1546
+ const flowId = option(args, "flow-id") || "";
1547
+ const flowSource = option(args, "flow-source") || "user";
1548
+ if (flowId) {
1549
+ printJson(await httpJson(args, `/api/workspace/schedules${query({ flowId, flowSource })}`));
1550
+ } else {
1551
+ printJson(await httpJson(args, "/api/schedules"));
1552
+ }
1553
+ return;
1554
+ }
1555
+
1556
+ if (["schedule-set", "schedule-enable", "schedule-disable"].includes(command)) {
1557
+ const flowId = requireFlowId(args);
1558
+ const flowSource = option(args, "flow-source") || "user";
1559
+ const scheduleNodeId = option(args, "schedule-node-id");
1560
+ if (!scheduleNodeId) throw new Error(`${command} requires --schedule-node-id <id>.`);
1561
+ const body = { flowId, flowSource, scheduleNodeId };
1562
+ if (command === "schedule-enable") body.enabled = true;
1563
+ if (command === "schedule-disable") body.enabled = false;
1564
+ if (command === "schedule-set" && args.enabled !== undefined) {
1565
+ body.enabled = parseBooleanOption(args.enabled, "--enabled");
1566
+ }
1567
+ if (args.cron !== undefined) body.cron = option(args, "cron");
1568
+ if (args.timezone !== undefined) body.timezone = option(args, "timezone");
1569
+ if (args["overlap-policy"] !== undefined) body.overlapPolicy = option(args, "overlap-policy");
1570
+ printJson(await httpJson(args, "/api/workspace/schedule/config", { method: "POST", body }));
1571
+ return;
1572
+ }
1573
+
1574
+ if (command === "schedule-run-now") {
1575
+ const flowId = requireFlowId(args);
1576
+ const flowSource = option(args, "flow-source") || "user";
1577
+ const scheduleNodeId = option(args, "schedule-node-id");
1578
+ if (!scheduleNodeId) throw new Error("schedule-run-now requires --schedule-node-id <id>.");
1579
+ printJson(await runRemoteWorkspace(args, { flowId, flowSource, runNodeId: scheduleNodeId }));
1580
+ return;
1581
+ }
1582
+
1154
1583
  if (command === "workflow-get") {
1155
1584
  const workflow = workflowReferenceFromArgs(args);
1156
1585
  if (workflow.namespace !== "tapd") {