@dazitech/cli 3.0.1 → 3.0.3

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.
@@ -6,9 +6,16 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
9
12
  var __commonJS = (cb, mod) => function __require() {
10
13
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
14
  };
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
12
19
  var __copyProps = (to, from, except, desc) => {
13
20
  if (from && typeof from === "object" || typeof from === "function") {
14
21
  for (let key of __getOwnPropNames(from))
@@ -25,6 +32,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
32
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
33
  mod
27
34
  ));
35
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
36
 
29
37
  // node_modules/.pnpm/commander@12.1.0/node_modules/commander/lib/error.js
30
38
  var require_error = __commonJS({
@@ -959,8 +967,8 @@ var require_command = __commonJS({
959
967
  "node_modules/.pnpm/commander@12.1.0/node_modules/commander/lib/command.js"(exports2) {
960
968
  var EventEmitter = require("node:events").EventEmitter;
961
969
  var childProcess = require("node:child_process");
962
- var path21 = require("node:path");
963
- var fs17 = require("node:fs");
970
+ var path19 = require("node:path");
971
+ var fs16 = require("node:fs");
964
972
  var process2 = require("node:process");
965
973
  var { Argument: Argument2, humanReadableArgName } = require_argument();
966
974
  var { CommanderError: CommanderError2 } = require_error();
@@ -1892,11 +1900,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1892
1900
  let launchWithNode = false;
1893
1901
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1894
1902
  function findFile(baseDir, baseName) {
1895
- const localBin = path21.resolve(baseDir, baseName);
1896
- if (fs17.existsSync(localBin)) return localBin;
1897
- if (sourceExt.includes(path21.extname(baseName))) return void 0;
1903
+ const localBin = path19.resolve(baseDir, baseName);
1904
+ if (fs16.existsSync(localBin)) return localBin;
1905
+ if (sourceExt.includes(path19.extname(baseName))) return void 0;
1898
1906
  const foundExt = sourceExt.find(
1899
- (ext) => fs17.existsSync(`${localBin}${ext}`)
1907
+ (ext) => fs16.existsSync(`${localBin}${ext}`)
1900
1908
  );
1901
1909
  if (foundExt) return `${localBin}${foundExt}`;
1902
1910
  return void 0;
@@ -1908,21 +1916,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
1908
1916
  if (this._scriptPath) {
1909
1917
  let resolvedScriptPath;
1910
1918
  try {
1911
- resolvedScriptPath = fs17.realpathSync(this._scriptPath);
1919
+ resolvedScriptPath = fs16.realpathSync(this._scriptPath);
1912
1920
  } catch (err) {
1913
1921
  resolvedScriptPath = this._scriptPath;
1914
1922
  }
1915
- executableDir = path21.resolve(
1916
- path21.dirname(resolvedScriptPath),
1923
+ executableDir = path19.resolve(
1924
+ path19.dirname(resolvedScriptPath),
1917
1925
  executableDir
1918
1926
  );
1919
1927
  }
1920
1928
  if (executableDir) {
1921
1929
  let localFile = findFile(executableDir, executableFile);
1922
1930
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
1923
- const legacyName = path21.basename(
1931
+ const legacyName = path19.basename(
1924
1932
  this._scriptPath,
1925
- path21.extname(this._scriptPath)
1933
+ path19.extname(this._scriptPath)
1926
1934
  );
1927
1935
  if (legacyName !== this._name) {
1928
1936
  localFile = findFile(
@@ -1933,7 +1941,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1933
1941
  }
1934
1942
  executableFile = localFile || executableFile;
1935
1943
  }
1936
- launchWithNode = sourceExt.includes(path21.extname(executableFile));
1944
+ launchWithNode = sourceExt.includes(path19.extname(executableFile));
1937
1945
  let proc;
1938
1946
  if (process2.platform !== "win32") {
1939
1947
  if (launchWithNode) {
@@ -2773,7 +2781,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2773
2781
  * @return {Command}
2774
2782
  */
2775
2783
  nameFromFilename(filename) {
2776
- this._name = path21.basename(filename, path21.extname(filename));
2784
+ this._name = path19.basename(filename, path19.extname(filename));
2777
2785
  return this;
2778
2786
  }
2779
2787
  /**
@@ -2787,9 +2795,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2787
2795
  * @param {string} [path]
2788
2796
  * @return {(string|null|Command)}
2789
2797
  */
2790
- executableDir(path22) {
2791
- if (path22 === void 0) return this._executableDir;
2792
- this._executableDir = path22;
2798
+ executableDir(path20) {
2799
+ if (path20 === void 0) return this._executableDir;
2800
+ this._executableDir = path20;
2793
2801
  return this;
2794
2802
  }
2795
2803
  /**
@@ -3019,1482 +3027,2371 @@ var require_commander = __commonJS({
3019
3027
  }
3020
3028
  });
3021
3029
 
3022
- // node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
3023
- var import_index = __toESM(require_commander(), 1);
3024
- var {
3025
- program,
3026
- createCommand,
3027
- createArgument,
3028
- createOption,
3029
- CommanderError,
3030
- InvalidArgumentError,
3031
- InvalidOptionArgumentError,
3032
- // deprecated old name
3033
- Command,
3034
- Argument,
3035
- Option,
3036
- Help
3037
- } = import_index.default;
3038
-
3039
- // cli/dazi-flow/src/commands/flows.ts
3040
- var import_path3 = __toESM(require("path"), 1);
3041
- var import_fs2 = __toESM(require("fs"), 1);
3030
+ // src/shared/flowNodeCatalog.ts
3031
+ var flowNodeCatalog_exports = {};
3032
+ __export(flowNodeCatalog_exports, {
3033
+ FLOW_NODE_CATALOG: () => FLOW_NODE_CATALOG,
3034
+ FLOW_NODE_CATALOG_DESIGNER: () => FLOW_NODE_CATALOG_DESIGNER,
3035
+ allFlowNodeTypes: () => allFlowNodeTypes,
3036
+ buildFlowNodeCatalogQuickStartSection: () => buildFlowNodeCatalogQuickStartSection,
3037
+ flowNodeByType: () => flowNodeByType
3038
+ });
3039
+ function allFlowNodeTypes() {
3040
+ return FLOW_NODE_CATALOG.flatMap((g) => g.items);
3041
+ }
3042
+ function flowNodeByType(type) {
3043
+ return allFlowNodeTypes().find((n) => n.type === type);
3044
+ }
3045
+ function buildFlowNodeCatalogQuickStartSection(detailLink) {
3046
+ const lines = [
3047
+ "## \u53EF\u7528\u6D41\u7A0B\u8282\u70B9\uFF08`data.type`\uFF09",
3048
+ "",
3049
+ "> \u753B\u5E03\u8282\u70B9 **`type` \u6052\u4E3A `custom`**\uFF0C\u4E1A\u52A1\u7C7B\u578B\u5728 **`data.type`**\u3002\u65B0\u5EFA\uFF1A`flow node new --type <\u4E0B\u8868 type>`\u3002",
3050
+ "> **\u7981\u6B62**\u7F16\u9020\u672A\u5728\u4E0B\u8868\u4E2D\u7684 type\u3002",
3051
+ ""
3052
+ ];
3053
+ for (const { group, items } of FLOW_NODE_CATALOG) {
3054
+ lines.push(`### ${group}`, "");
3055
+ lines.push("| type | \u540D\u79F0 | \u4EE3\u7801 | \u7528\u9014 | \u53D8\u91CF |");
3056
+ lines.push("|------|------|:----:|------|------|");
3057
+ for (const n of items) {
3058
+ const code = n.hasCode ? n.codeFile ?? "code.*" : "\u2014";
3059
+ const vars = [n.consumes ? `\u5165:${shortVar(n.consumes)}` : "", n.produces ? `\u51FA:${shortVar(n.produces)}` : ""].filter(Boolean).join(" \xB7 ") || "\u2014";
3060
+ lines.push(`| \`${n.type}\` | ${n.label} | ${code} | ${n.summary} | ${vars} |`);
3061
+ }
3062
+ lines.push("");
3063
+ }
3064
+ lines.push(
3065
+ "**\u9009\u578B\u63D0\u793A**",
3066
+ "",
3067
+ "- **Excel\uFF08\u6709 `managed_file_id` / `file_id`\uFF09\u2192 \u4F18\u5148 `excel-python`**\uFF0C\u52FF\u7528 `file-source`\uFF08\u4E0D\u89E3\u6790 Excel\uFF09",
3068
+ "- \u4EC5\u6781\u7B80\u5355\u5355 Sheet\u3001\u96F6\u4EE3\u7801\u65F6\u624D\u7528 `excel-import`\uFF1B\u591A Sheet / \u590D\u6742\u8868\u5934 / \u4E0D\u786E\u5B9A\u7ED3\u6784 \u2192 `excel-python`",
3069
+ "- **`excel-python`**\uFF1A\u753B\u5E03\u914D **`managed_file_id`\uFF08UUID\uFF09**\uFF1B`code.py` \u7528 **`excel_source_path`**\uFF0C\u7981\u6B62\u5199\u6587\u4EF6\u540D/\u672C\u5730\u8DEF\u5F84",
3070
+ "- \u975E Excel \u539F\u59CB\u6587\u4EF6 \u2192 `file-source`\uFF1B\u8BFB\u5916\u90E8\u5E93 \u2192 `database-source`\uFF1B\u8BFB\u6570\u636E\u7A7A\u95F4 \u2192 `dataspace-source`",
3071
+ "- \u5185\u5B58\u52A0\u5DE5 \u2192 `sql-query` / `python-script`\uFF1B\u5206\u652F \u2192 `condition`\uFF08\u51FA\u8FB9 `true`/`false`\uFF09",
3072
+ "- \u5199\u5916\u90E8\u5E93 \u2192 `database-sink`\uFF1B\u5199\u6570\u636E\u7A7A\u95F4 \u2192 `dataspace-sink`",
3073
+ ""
3074
+ );
3075
+ if (detailLink) {
3076
+ lines.push(`\u5404\u8282\u70B9\u914D\u7F6E\u4E0E\u793A\u4F8B \u2192 [flows-guide \xA7\u6D41\u7A0B\u8282\u70B9\u7EC4\u4EF6](${detailLink}#\u6D41\u7A0B\u8282\u70B9\u7EC4\u4EF6)\u3002`, "");
3077
+ } else {
3078
+ lines.push("\u5404\u8282\u70B9\u914D\u7F6E\u4E0E\u793A\u4F8B \u2192 \u5E2E\u52A9\u6587\u6863 **flow/flows-guide** \xA7\u6D41\u7A0B\u8282\u70B9\u7EC4\u4EF6\u3002", "");
3079
+ }
3080
+ return lines;
3081
+ }
3082
+ function shortVar(s) {
3083
+ return s.replace(/`/g, "").slice(0, 40);
3084
+ }
3085
+ var FLOW_NODE_CATALOG, FLOW_NODE_CATALOG_DESIGNER;
3086
+ var init_flowNodeCatalog = __esm({
3087
+ "src/shared/flowNodeCatalog.ts"() {
3088
+ "use strict";
3089
+ FLOW_NODE_CATALOG = [
3090
+ {
3091
+ group: "\u6D41\u7A0B\u63A7\u5236",
3092
+ items: [
3093
+ {
3094
+ type: "condition",
3095
+ label: "\u6761\u4EF6\u5224\u65AD",
3096
+ hasCode: true,
3097
+ codeFile: "code.py",
3098
+ summary: "\u5BF9\u4E0A\u6E38\u8868\u6C42\u5E03\u5C14\u8868\u8FBE\u5F0F\uFF0C\u8D70 True/False \u5206\u652F",
3099
+ consumes: "\u5165\u8FB9\u4E0A\u6E38\u8868 \u2192 \u8FD0\u884C\u65F6\u6CE8\u5165 `df`",
3100
+ produces: "\u65E0\u65B0\u8868\u53D8\u91CF\uFF08\u4EC5\u8DEF\u7531\uFF09",
3101
+ keyConfig: ["label", "pythonCode\uFF08\u4EE3\u7801\u5728 code.py\uFF09"]
3102
+ },
3103
+ {
3104
+ type: "delay",
3105
+ label: "\u5EF6\u65F6\u7B49\u5F85",
3106
+ hasCode: false,
3107
+ summary: "\u6682\u505C\u6307\u5B9A\u79D2\u6570\u540E\u7EE7\u7EED\u4E0B\u6E38",
3108
+ keyConfig: ["delaySeconds \u6216 delay\uFF08\u79D2\uFF09"]
3109
+ }
3110
+ ]
3111
+ },
3112
+ {
3113
+ group: "\u6570\u636E\u5904\u7406",
3114
+ items: [
3115
+ {
3116
+ type: "file-source",
3117
+ label: "\u6587\u4EF6\u8F93\u5165",
3118
+ hasCode: false,
3119
+ summary: "\u975E Excel \u539F\u59CB\u6587\u4EF6\u900F\u4F20\uFF08.xlsx/.xls \u52FF\u7528\uFF0C\u6539 excel-python\uFF09",
3120
+ produces: "`output_variable_name`",
3121
+ keyConfig: ["file_id / managed_file_id", "output_variable_name"]
3122
+ },
3123
+ {
3124
+ type: "excel-import",
3125
+ label: "Excel \u5BFC\u5165",
3126
+ hasCode: false,
3127
+ summary: "\u4EC5\u6781\u7B80\u5355\u5355 Sheet \u96F6\u4EE3\u7801\uFF1B\u6709 file_id \u7684 Excel \u9ED8\u8BA4\u4ECD\u7528 excel-python",
3128
+ produces: "`output_variable_name`",
3129
+ keyConfig: ["file_id / source_excel_id", "sheetName\uFF08\u53EF\u9009\uFF09", "output_variable_name"]
3130
+ },
3131
+ {
3132
+ type: "excel-python",
3133
+ label: "Excel \u5F00\u53D1",
3134
+ hasCode: true,
3135
+ codeFile: "code.py",
3136
+ summary: "\u591A Sheet / \u590D\u6742\u8868\u5934\uFF1APython \u89E3\u6790 Excel\uFF08\u753B\u5E03\u914D managed_file_id\uFF0C\u4EE3\u7801\u7528 excel_source_path\uFF09",
3137
+ produces: "`set_table_output(\u4E0E output_variable_name \u540C\u540D, df)`",
3138
+ keyConfig: ["managed_file_id\uFF08UUID\uFF0C\u975E\u6587\u4EF6\u540D\uFF09", "output_variable_name"]
3139
+ },
3140
+ {
3141
+ type: "sql-query",
3142
+ label: "SQL \u67E5\u8BE2",
3143
+ hasCode: true,
3144
+ codeFile: "code.sql",
3145
+ summary: "DuckDB \u5185\u5B58 SQL\uFF0C\u4E0A\u6E38\u53D8\u91CF\u540D\u4F5C\u8868\u540D",
3146
+ consumes: "\u4E0A\u6E38 `output_variable_name` \u4F5C SQL \u8868\u540D",
3147
+ produces: "`output_variable_name`",
3148
+ keyConfig: ["output_variable_name"]
3149
+ },
3150
+ {
3151
+ type: "python-script",
3152
+ label: "Python \u811A\u672C",
3153
+ hasCode: true,
3154
+ codeFile: "code.py",
3155
+ summary: "pandas \u8F6C\u6362\uFF1B\u6700\u5E38\u7528\u52A0\u5DE5\u8282\u70B9",
3156
+ consumes: '`get_variable("\u4E0A\u6E38\u53D8\u91CF\u540D")` \u6216\u5165\u8FB9 `df`',
3157
+ produces: "`result_df` \u2192 `output_variable_name`",
3158
+ keyConfig: ["output_variable_name"]
3159
+ },
3160
+ {
3161
+ type: "database-source",
3162
+ label: "\u6570\u636E\u5E93\u8BFB\u53D6",
3163
+ hasCode: true,
3164
+ codeFile: "code.sql",
3165
+ summary: "\u4ECE\u5916\u90E8\u6570\u636E\u5E93\uFF08connectionId\uFF09\u53EA\u8BFB\u67E5\u8BE2",
3166
+ produces: "`output_variable_name`",
3167
+ keyConfig: ["connectionId", "output_variable_name"]
3168
+ },
3169
+ {
3170
+ type: "database-sink",
3171
+ label: "\u6570\u636E\u5E93\u5199\u5165",
3172
+ hasCode: false,
3173
+ summary: "\u5C06\u8868\u53D8\u91CF\u5199\u5165\u5916\u90E8\u6570\u636E\u5E93\u8868",
3174
+ consumes: "`input_variable_name` \u6216\u5165\u8FB9 Parquet",
3175
+ keyConfig: ["connectionId", "tableName", "mode", "input_variable_name"]
3176
+ },
3177
+ {
3178
+ type: "dataspace-source",
3179
+ label: "\u6570\u636E\u7A7A\u95F4\u8BFB\u53D6",
3180
+ hasCode: true,
3181
+ codeFile: "code.sql",
3182
+ summary: "\u4ECE\u642D\u5B50\u6570\u636E\u7A7A\u95F4\uFF08spaceId\uFF09\u53EA\u8BFB SQL",
3183
+ produces: "`output_variable_name`",
3184
+ keyConfig: ["spaceId", "output_variable_name", "limit\uFF08\u53EF\u9009\uFF09"]
3185
+ },
3186
+ {
3187
+ type: "dataspace-sink",
3188
+ label: "\u6570\u636E\u7A7A\u95F4\u5199\u5165",
3189
+ hasCode: false,
3190
+ summary: "\u5C06\u8868\u53D8\u91CF\u5199\u5165\u6570\u636E\u7A7A\u95F4\u8868",
3191
+ consumes: "`input_variable_name` \u6216\u5165\u8FB9 Parquet",
3192
+ keyConfig: ["spaceId", "tableName", "input_variable_name", "syncMetadata\uFF08\u53EF\u9009\uFF09"]
3193
+ },
3194
+ {
3195
+ type: "data-quality-check",
3196
+ label: "\u6570\u636E\u8D28\u91CF\u68C0\u67E5",
3197
+ hasCode: true,
3198
+ codeFile: "code.py",
3199
+ summary: "\u8D28\u68C0\u89C4\u5219\u6216\u81EA\u5B9A\u4E49 Python\uFF08dqPythonCode\uFF09",
3200
+ consumes: "\u4E0A\u6E38\u8868 / `get_variable`",
3201
+ produces: "\u8D28\u68C0\u62A5\u544A\u53D8\u91CF\uFF08\u4F9D\u914D\u7F6E\uFF09",
3202
+ keyConfig: ["output_variable_name", "qualityConfig", "dqPythonCode\uFF08\u4F18\u5148\u4E8E\u89C4\u5219\uFF09"]
3203
+ },
3204
+ {
3205
+ type: "folder-resource-import",
3206
+ label: "\u6587\u4EF6\u5939\u8D44\u6E90\u5BFC\u5165",
3207
+ hasCode: false,
3208
+ summary: "\u4ECE\u6587\u4EF6\u5939\u8D44\u6E90\u6279\u91CF\u5BFC\u5165\u6587\u4EF6",
3209
+ produces: "`output_variable_name`",
3210
+ keyConfig: ["folderId", "resourceId", "pageSize", "output_variable_name"]
3211
+ },
3212
+ {
3213
+ type: "folder-resource-register",
3214
+ label: "\u8D44\u6E90\u6CE8\u518C",
3215
+ hasCode: false,
3216
+ summary: "\u5C06\u5BFC\u5165\u7ED3\u679C\u6CE8\u518C\u5230\u8FDE\u63A5/\u8D44\u6E90\u76EE\u5F55",
3217
+ keyConfig: ["connectionName", "targetFolderId"]
3218
+ }
3219
+ ]
3220
+ }
3221
+ ];
3222
+ FLOW_NODE_CATALOG_DESIGNER = FLOW_NODE_CATALOG.map((g) => ({
3223
+ group: g.group,
3224
+ items: g.items.map(({ type, label, hasCode }) => ({
3225
+ type,
3226
+ label,
3227
+ ...hasCode ? { code: true } : {}
3228
+ }))
3229
+ }));
3230
+ }
3231
+ });
3042
3232
 
3043
- // cli/shared/src/auth.js
3044
- var import_os = __toESM(require("os"), 1);
3045
- var import_path = __toESM(require("path"), 1);
3046
- var import_fs = __toESM(require("fs"), 1);
3233
+ // src/shared/flowCliText.ts
3234
+ var flowCliText_exports = {};
3235
+ __export(flowCliText_exports, {
3236
+ DAZI_CLI: () => DAZI_CLI,
3237
+ DAZI_PS1: () => DAZI_PS1,
3238
+ mdDaziCmd: () => mdDaziCmd,
3239
+ mdFlowCmd: () => mdFlowCmd,
3240
+ normalizeCliExampleLine: () => normalizeCliExampleLine,
3241
+ psDaziLine: () => psDaziLine,
3242
+ psFlowBlock: () => psFlowBlock,
3243
+ psFlowLine: () => psFlowLine
3244
+ });
3245
+ function psFlowLine(subcommand) {
3246
+ return `${DAZI_CLI} flow ${subcommand}`;
3247
+ }
3248
+ function psDaziLine(args) {
3249
+ return `${DAZI_CLI} ${args}`;
3250
+ }
3251
+ function mdFlowCmd(subcommand) {
3252
+ return `\`${DAZI_CLI} flow ${subcommand}\``;
3253
+ }
3254
+ function mdDaziCmd(args) {
3255
+ return `\`${DAZI_CLI} ${args}\``;
3256
+ }
3257
+ function normalizeCliExampleLine(line) {
3258
+ return line.replace(/\.\\+scripts\\+dazi\.ps1\s+/g, `${DAZI_CLI} `).replace(/\.\/scripts\/dazi\.ps1\s+/g, `${DAZI_CLI} `);
3259
+ }
3260
+ function psFlowBlock(lines) {
3261
+ return ["```powershell", ...lines.map(normalizeCliExampleLine), "```"].join("\n");
3262
+ }
3263
+ var DAZI_CLI, DAZI_PS1;
3264
+ var init_flowCliText = __esm({
3265
+ "src/shared/flowCliText.ts"() {
3266
+ "use strict";
3267
+ DAZI_CLI = "dazi";
3268
+ DAZI_PS1 = DAZI_CLI;
3269
+ }
3270
+ });
3047
3271
 
3048
- // cli/shared/src/errors.js
3049
- var DaziError = class extends Error {
3050
- constructor(message, code = "ERR_DAZI", exitCode = 1) {
3051
- super(message);
3052
- this.code = code;
3053
- this.exitCode = exitCode;
3054
- this.name = "DaziError";
3272
+ // src/shared/flowWorkspaceAudit.ts
3273
+ var flowWorkspaceAudit_exports = {};
3274
+ __export(flowWorkspaceAudit_exports, {
3275
+ auditFlowWorkspace: () => auditFlowWorkspace,
3276
+ buildFlowConsistencyMarkdown: () => buildFlowConsistencyMarkdown,
3277
+ repairFlowMeta: () => repairFlowMeta
3278
+ });
3279
+ function sha12(text) {
3280
+ return import_crypto2.default.createHash("sha1").update(text, "utf8").digest("hex");
3281
+ }
3282
+ function resolveNodeType2(node) {
3283
+ if (node.type === "custom") {
3284
+ return String(node.data?.type ?? "custom");
3055
3285
  }
3056
- };
3057
- var AuthError = class extends DaziError {
3058
- constructor(message = "\u672A\u767B\u5F55\uFF0C\u8BF7\u5148\u8FD0\u884C: dazi auth login") {
3059
- super(message, "ERR_AUTH", 2);
3060
- this.name = "AuthError";
3286
+ return String(node.type ?? "custom");
3287
+ }
3288
+ function codeLanguageForNodeType2(nodeType) {
3289
+ if (["python-script", "excel-python", "condition", "data-quality-check"].includes(nodeType)) {
3290
+ return "python";
3061
3291
  }
3062
- };
3063
- var NetworkError = class extends DaziError {
3064
- constructor(message, status) {
3065
- super(message, "ERR_NETWORK", 3);
3066
- this.status = status;
3067
- this.name = "NetworkError";
3292
+ if (["database-source", "dataspace-source", "sql-query"].includes(nodeType)) {
3293
+ return "sql";
3068
3294
  }
3069
- };
3070
-
3071
- // cli/shared/src/auth.js
3072
- function authFilePath() {
3073
- return import_path.default.join(import_os.default.homedir(), ".dazi", "auth.json");
3295
+ return void 0;
3074
3296
  }
3075
- function loadAuth() {
3076
- const p = authFilePath();
3077
- if (!import_fs.default.existsSync(p))
3078
- throw new AuthError();
3297
+ function codeFileExt2(language) {
3298
+ if (language === "python") return "py";
3299
+ if (language === "sql") return "sql";
3300
+ return "txt";
3301
+ }
3302
+ function sanitizeName2(name) {
3303
+ const cleaned = (name || "").replace(/[<>:"/\\|?*\x00-\x1f]/g, "_").trim().replace(/\s+/g, "_");
3304
+ return cleaned || "node";
3305
+ }
3306
+ function readFlowJson2(flowDir) {
3307
+ const raw = JSON.parse(import_fs6.default.readFileSync(import_path7.default.join(flowDir, FLOW_JSON2), "utf8"));
3308
+ return {
3309
+ nodes: Array.isArray(raw.nodes) ? raw.nodes : [],
3310
+ edges: Array.isArray(raw.edges) ? raw.edges : []
3311
+ };
3312
+ }
3313
+ function readMeta2(flowDir) {
3314
+ const p = import_path7.default.join(flowDir, FLOW_META_JSON2);
3315
+ if (!import_fs6.default.existsSync(p)) return void 0;
3079
3316
  try {
3080
- return JSON.parse(import_fs.default.readFileSync(p, "utf-8"));
3317
+ return JSON.parse(import_fs6.default.readFileSync(p, "utf8"));
3081
3318
  } catch {
3082
- throw new AuthError("auth.json \u683C\u5F0F\u635F\u574F\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55");
3319
+ return void 0;
3083
3320
  }
3084
3321
  }
3085
-
3086
- // cli/shared/src/httpClient.js
3087
- async function apiRequest(path21, opts = {}) {
3088
- const auth = opts.token || opts.serverUrl ? { token: opts.token ?? "", serverUrl: opts.serverUrl ?? "" } : loadAuth();
3089
- const url = `${auth.serverUrl.replace(/\/$/, "")}${path21}`;
3090
- const headers = {
3091
- "Content-Type": "application/json",
3092
- Authorization: `Bearer ${auth.token}`,
3093
- ...opts.headers
3094
- };
3095
- const res = await fetch(url, {
3096
- method: opts.method ?? "GET",
3097
- headers,
3098
- body: opts.body != null ? JSON.stringify(opts.body) : void 0
3099
- });
3100
- if (!res.ok) {
3101
- let msg = `HTTP ${res.status} ${res.statusText}`;
3322
+ function isCodeNodeType(nodeType) {
3323
+ return Boolean(NODE_TYPE_CODE_DATA_KEY2[nodeType]);
3324
+ }
3325
+ function listNodeDirs(flowDir) {
3326
+ const nodesRoot = import_path7.default.join(flowDir, NODES_DIR2);
3327
+ if (!import_fs6.default.existsSync(nodesRoot)) return [];
3328
+ return import_fs6.default.readdirSync(nodesRoot).filter((n) => {
3102
3329
  try {
3103
- const j = await res.json();
3104
- if (j.message)
3105
- msg = j.message;
3330
+ return import_fs6.default.statSync(import_path7.default.join(nodesRoot, n)).isDirectory();
3106
3331
  } catch {
3332
+ return false;
3107
3333
  }
3108
- throw new NetworkError(msg, res.status);
3109
- }
3110
- return res.json();
3111
- }
3112
-
3113
- // cli/shared/src/output.js
3114
- function printJsonSummary(summary) {
3115
- console.log(`__JSON_SUMMARY__${JSON.stringify(summary)}`);
3116
- }
3117
- function ok(data) {
3118
- printJsonSummary({ ok: true, data });
3334
+ });
3119
3335
  }
3120
- function fail(code, message) {
3121
- printJsonSummary({ ok: false, error: { code, message } });
3336
+ function findCodeFileInDir(dirAbs) {
3337
+ if (!import_fs6.default.existsSync(dirAbs)) return void 0;
3338
+ for (const f of import_fs6.default.readdirSync(dirAbs)) {
3339
+ if (/^code\.(py|sql|txt)$/i.test(f)) return f;
3340
+ }
3341
+ return void 0;
3122
3342
  }
3123
- function handleError(err) {
3124
- if (err instanceof Error) {
3125
- const code = err.code ?? "ERR_UNKNOWN";
3126
- console.error(`\u9519\u8BEF: ${err.message}`);
3127
- fail(code, err.message);
3128
- } else {
3129
- console.error(`\u672A\u77E5\u9519\u8BEF`, err);
3130
- fail("ERR_UNKNOWN", String(err));
3343
+ function findDirForUuid(flowDir, uuid) {
3344
+ const nodesRoot = import_path7.default.join(flowDir, NODES_DIR2);
3345
+ for (const dirName of listNodeDirs(flowDir)) {
3346
+ const infoPath = import_path7.default.join(nodesRoot, dirName, NODE_INFO_JSON2);
3347
+ if (import_fs6.default.existsSync(infoPath)) {
3348
+ try {
3349
+ const info = JSON.parse(import_fs6.default.readFileSync(infoPath, "utf8"));
3350
+ if (String(info.node_uuid ?? "") === uuid) return import_path7.default.posix.join(NODES_DIR2, dirName);
3351
+ } catch {
3352
+ }
3353
+ }
3131
3354
  }
3132
- process.exit(1);
3355
+ return void 0;
3133
3356
  }
3134
-
3135
- // cli/shared/src/workspaceLayout.js
3136
- var import_path2 = __toESM(require("path"), 1);
3137
- var WORKSPACE_RESOURCE_DIR = "\u8D44\u6E90";
3138
- function resolveWorkspace(cwd = process.cwd()) {
3139
- const resources = import_path2.default.join(cwd, WORKSPACE_RESOURCE_DIR);
3140
- return {
3141
- root: cwd,
3142
- resources,
3143
- dazi: import_path2.default.join(cwd, ".dazi"),
3144
- onto: import_path2.default.join(cwd, "onto"),
3145
- flows: import_path2.default.join(cwd, "flows"),
3146
- apps: import_path2.default.join(cwd, "apps"),
3147
- data: import_path2.default.join(cwd, "data"),
3148
- docs: import_path2.default.join(resources, "docs"),
3149
- prompts: import_path2.default.join(resources, "prompts"),
3150
- examples: import_path2.default.join(resources, "examples"),
3151
- dataspaces: import_path2.default.join(resources, "dataspaces"),
3152
- datasources: import_path2.default.join(resources, "datasources"),
3153
- files: import_path2.default.join(resources, "files")
3357
+ function auditFlowWorkspace(flowDir) {
3358
+ const warnings = [];
3359
+ const doc = readFlowJson2(flowDir);
3360
+ const meta = readMeta2(flowDir) ?? { nodes: {} };
3361
+ const canvasNodeCount = doc.nodes.length;
3362
+ let canvasCodeNodeCount = 0;
3363
+ const missingInMeta = [];
3364
+ const missingCodeFiles = [];
3365
+ const flowUuids = /* @__PURE__ */ new Set();
3366
+ for (const node of doc.nodes) {
3367
+ const uuid = String(node.node_uuid ?? "").trim();
3368
+ if (!uuid) continue;
3369
+ flowUuids.add(uuid);
3370
+ const nodeType = resolveNodeType2(node);
3371
+ if (!isCodeNodeType(nodeType)) continue;
3372
+ canvasCodeNodeCount++;
3373
+ const label = String(node.data?.label ?? node.id ?? "");
3374
+ const m = meta.nodes[uuid];
3375
+ if (!m?.dir || !m.codeFile) {
3376
+ missingInMeta.push({ uuid, nodeId: String(node.id ?? ""), nodeType, label });
3377
+ continue;
3378
+ }
3379
+ const codePath = import_path7.default.join(flowDir, m.dir, m.codeFile);
3380
+ if (!import_fs6.default.existsSync(codePath)) {
3381
+ missingCodeFiles.push({ uuid, dir: m.dir, codeFile: m.codeFile });
3382
+ }
3383
+ }
3384
+ const orphanInMeta = Object.keys(meta.nodes).filter((u) => !flowUuids.has(u));
3385
+ let metaCodeIndexedCount = 0;
3386
+ for (const m of Object.values(meta.nodes)) {
3387
+ if (m.dir && m.codeFile) metaCodeIndexedCount++;
3388
+ }
3389
+ const unindexedDirs = [];
3390
+ for (const dirName of listNodeDirs(flowDir)) {
3391
+ const dirAbs = import_path7.default.join(flowDir, NODES_DIR2, dirName);
3392
+ if (!findCodeFileInDir(dirAbs)) continue;
3393
+ const rel = import_path7.default.posix.join(NODES_DIR2, dirName);
3394
+ const indexed = Object.values(meta.nodes).some((m) => m.dir?.replace(/\\/g, "/") === rel);
3395
+ if (!indexed) unindexedDirs.push(rel);
3396
+ }
3397
+ if (canvasCodeNodeCount !== metaCodeIndexedCount) {
3398
+ warnings.push(
3399
+ `\u753B\u5E03\u4EE3\u7801\u8282\u70B9 ${canvasCodeNodeCount} \u4E2A\uFF0Cmeta \u5DF2\u7D22\u5F15 ${metaCodeIndexedCount} \u4E2A\uFF08\u4E0D\u4E00\u81F4\u4F1A\u5BFC\u81F4 node push / \u8BBE\u8BA1\u5668\u6253\u5F00\u4EE3\u7801\u5931\u8D25\uFF09`
3400
+ );
3401
+ }
3402
+ if (missingInMeta.length) {
3403
+ warnings.push("\u90E8\u5206 flow.json \u4EE3\u7801\u8282\u70B9\u672A\u5199\u5165 flow.meta.json\uFF0C\u8BF7\u6267\u884C project repair-meta");
3404
+ }
3405
+ if (unindexedDirs.length) {
3406
+ warnings.push(`\u5B58\u5728\u672A\u7D22\u5F15\u7684\u8282\u70B9\u76EE\u5F55\uFF1A${unindexedDirs.join(", ")}`);
3407
+ }
3408
+ const ok3 = missingInMeta.length === 0 && orphanInMeta.length === 0 && missingCodeFiles.length === 0 && unindexedDirs.length === 0 && canvasCodeNodeCount === metaCodeIndexedCount;
3409
+ return {
3410
+ ok: ok3,
3411
+ canvasNodeCount,
3412
+ canvasCodeNodeCount,
3413
+ metaIndexedCount: Object.keys(meta.nodes).length,
3414
+ metaCodeIndexedCount,
3415
+ missingInMeta,
3416
+ orphanInMeta,
3417
+ missingCodeFiles,
3418
+ unindexedDirs,
3419
+ warnings
3154
3420
  };
3155
3421
  }
3422
+ function writeNodeInfo(flowDir, node, uuid, nodeType, relDir, codeFile, language, strippedData) {
3423
+ const abs = import_path7.default.join(flowDir, relDir);
3424
+ import_fs6.default.writeFileSync(
3425
+ import_path7.default.join(abs, NODE_INFO_JSON2),
3426
+ JSON.stringify(
3427
+ {
3428
+ _readonly: `\u6B64\u6587\u4EF6\u7531 dazi-flow \u751F\u6210\uFF0C\u4EC5\u4F9B\u67E5\u770B\uFF1B\u7F16\u8F91\u4EE3\u7801\u8BF7\u6539 ${codeFile}\uFF0C\u914D\u7F6E\u6539 flow.json`,
3429
+ node_uuid: uuid,
3430
+ nodeId: String(node.id ?? ""),
3431
+ nodeType,
3432
+ label: String(node.data?.label ?? ""),
3433
+ codeFile,
3434
+ codeLanguage: language,
3435
+ position: node.position,
3436
+ data: strippedData
3437
+ },
3438
+ null,
3439
+ 2
3440
+ ),
3441
+ "utf8"
3442
+ );
3443
+ }
3444
+ function repairFlowMeta(flowDir) {
3445
+ const warnings = [];
3446
+ const repairedUuids = [];
3447
+ const wroteNodeInfo = [];
3448
+ const doc = readFlowJson2(flowDir);
3449
+ const meta = readMeta2(flowDir) ?? { flowId: null, flowName: import_path7.default.basename(flowDir), nodes: {} };
3450
+ if (!meta.nodes) meta.nodes = {};
3451
+ const usedDirNames = new Set(
3452
+ Object.values(meta.nodes).map((m) => m.dir?.split("/").pop()).filter(Boolean)
3453
+ );
3454
+ for (const node of doc.nodes) {
3455
+ const uuid = String(node.node_uuid ?? "").trim();
3456
+ if (!uuid) continue;
3457
+ const nodeType = resolveNodeType2(node);
3458
+ const codeKey = NODE_TYPE_CODE_DATA_KEY2[nodeType];
3459
+ const semanticId = String(node.id ?? "");
3460
+ const label = String(node.data?.label ?? semanticId ?? nodeType);
3461
+ let entry = meta.nodes[uuid];
3462
+ if (!entry) {
3463
+ entry = { nodeId: semanticId, nodeType };
3464
+ meta.nodes[uuid] = entry;
3465
+ repairedUuids.push(uuid);
3466
+ } else {
3467
+ entry.nodeId = semanticId || entry.nodeId;
3468
+ entry.nodeType = nodeType;
3469
+ }
3470
+ if (!codeKey) continue;
3471
+ let relDir = entry.dir?.replace(/\\/g, "/");
3472
+ if (relDir && findCodeFileInDir(import_path7.default.join(flowDir, relDir))) {
3473
+ } else {
3474
+ relDir = findDirForUuid(flowDir, uuid);
3475
+ }
3476
+ if (!relDir) {
3477
+ const byLabel = import_path7.default.posix.join(NODES_DIR2, sanitizeName2(label));
3478
+ if (import_fs6.default.existsSync(import_path7.default.join(flowDir, byLabel)) && findCodeFileInDir(import_path7.default.join(flowDir, byLabel))) {
3479
+ relDir = byLabel;
3480
+ }
3481
+ }
3482
+ if (!relDir) {
3483
+ let dirName = sanitizeName2(label);
3484
+ if (usedDirNames.has(dirName)) dirName = `${dirName}_${uuid.slice(0, 6)}`;
3485
+ usedDirNames.add(dirName);
3486
+ relDir = import_path7.default.posix.join(NODES_DIR2, dirName);
3487
+ import_fs6.default.mkdirSync(import_path7.default.join(flowDir, relDir), { recursive: true });
3488
+ const lang = codeLanguageForNodeType2(nodeType);
3489
+ const codeFile2 = `code.${codeFileExt2(lang)}`;
3490
+ const template = nodeType === "excel-python" ? "# -*- coding: utf-8 -*-\n# Excel \u8282\u70B9\uFF1A\u753B\u5E03\u914D\u7F6E managed_file_id\uFF1B\u4EE3\u7801\u4F7F\u7528 excel_source_path\n" : lang === "sql" ? "-- \u8282\u70B9 SQL\nSELECT 1;\n" : "# -*- coding: utf-8 -*-\n";
3491
+ if (!findCodeFileInDir(import_path7.default.join(flowDir, relDir))) {
3492
+ import_fs6.default.writeFileSync(import_path7.default.join(flowDir, relDir, codeFile2), template, "utf8");
3493
+ warnings.push(`\u5DF2\u4E3A ${uuid} \u521B\u5EFA\u7A7A\u4EE3\u7801\u6A21\u677F ${relDir}/${codeFile2}`);
3494
+ }
3495
+ }
3496
+ const dirAbs = import_path7.default.join(flowDir, relDir);
3497
+ const codeFile = findCodeFileInDir(dirAbs);
3498
+ if (!codeFile) {
3499
+ warnings.push(`\u8282\u70B9 ${uuid} \u76EE\u5F55 ${relDir} \u65E0 code.*\uFF0C\u8DF3\u8FC7\u4EE3\u7801\u7D22\u5F15`);
3500
+ continue;
3501
+ }
3502
+ const language = codeLanguageForNodeType2(nodeType);
3503
+ const code = import_fs6.default.readFileSync(import_path7.default.join(dirAbs, codeFile), "utf8");
3504
+ entry.dir = relDir;
3505
+ entry.codeFile = codeFile;
3506
+ entry.codeLanguage = language;
3507
+ entry.codeHash = sha12(code);
3508
+ const strippedData = { ...node.data ?? {} };
3509
+ delete strippedData[codeKey];
3510
+ const infoPath = import_path7.default.join(dirAbs, NODE_INFO_JSON2);
3511
+ if (!import_fs6.default.existsSync(infoPath)) {
3512
+ writeNodeInfo(flowDir, node, uuid, nodeType, relDir, codeFile, language, strippedData);
3513
+ wroteNodeInfo.push(relDir);
3514
+ }
3515
+ if (!repairedUuids.includes(uuid)) repairedUuids.push(uuid);
3516
+ }
3517
+ meta.graphFingerprint = "sha1:" + sha12(JSON.stringify({ nodes: doc.nodes, edges: doc.edges }));
3518
+ import_fs6.default.writeFileSync(import_path7.default.join(flowDir, FLOW_META_JSON2), JSON.stringify(meta, null, 2), "utf8");
3519
+ const okAfter = auditFlowWorkspace(flowDir).ok;
3520
+ return {
3521
+ ok: okAfter,
3522
+ repairedUuids: [...new Set(repairedUuids)],
3523
+ wroteNodeInfo,
3524
+ warnings
3525
+ };
3526
+ }
3527
+ function buildFlowConsistencyMarkdown(audit, localFilesSpecLink = "../../\u8D44\u6E90/docs/flow/local-files-spec.md") {
3528
+ const lines = [
3529
+ "## \u672C\u5730\u6587\u4EF6\u4E00\u81F4\u6027",
3530
+ "",
3531
+ "| \u6307\u6807 | \u6570\u91CF |",
3532
+ "|------|------|",
3533
+ `| \u753B\u5E03\u8282\u70B9\uFF08flow.json\uFF09 | ${audit.canvasNodeCount} |`,
3534
+ `| \u753B\u5E03\u4EE3\u7801\u8282\u70B9\uFF08flow.json\uFF09 | ${audit.canvasCodeNodeCount} |`,
3535
+ `| meta \u5DF2\u767B\u8BB0\u8282\u70B9 | ${audit.metaIndexedCount} |`,
3536
+ `| meta \u5DF2\u7D22\u5F15\u4EE3\u7801\uFF08dir+codeFile\uFF09 | ${audit.metaCodeIndexedCount} |`,
3537
+ ""
3538
+ ];
3539
+ if (audit.ok) {
3540
+ lines.push("\u2705 **flow.json / flow.meta.json / \u8282\u70B9/ \u4E00\u81F4**\u3002\u8BBE\u8BA1\u5668\u53EF\u6B63\u5E38\u300C\u6253\u5F00\u4EE3\u7801\u300D\uFF0C`node push` \u53EF\u8BC6\u522B\u810F\u8282\u70B9\u3002", "");
3541
+ } else {
3542
+ lines.push("\u26A0\uFE0F **\u76EE\u5F55\u4E0D\u4E00\u81F4**\uFF08\u4F1A\u5BFC\u81F4\u8BBE\u8BA1\u5668\u627E\u4E0D\u5230\u4EE3\u7801\u3001\u6216 `node push` \u8DF3\u8FC7\uFF09\uFF1A", "");
3543
+ if (audit.missingInMeta.length) {
3544
+ lines.push("| uuid | \u8282\u70B9 | \u7C7B\u578B |");
3545
+ lines.push("|------|------|------|");
3546
+ for (const m of audit.missingInMeta) {
3547
+ lines.push(`| \`${m.uuid.slice(0, 8)}\u2026\` | ${m.label || m.nodeId} | ${m.nodeType} |`);
3548
+ }
3549
+ lines.push("");
3550
+ }
3551
+ if (audit.unindexedDirs.length) {
3552
+ lines.push(`- \u672A\u7D22\u5F15\u76EE\u5F55\uFF1A${audit.unindexedDirs.map((d) => `\`${d}\``).join(" ")}`);
3553
+ }
3554
+ if (audit.orphanInMeta.length) {
3555
+ lines.push(`- meta \u591A\u4F59 uuid\uFF1A${audit.orphanInMeta.length} \u4E2A\uFF08flow.json \u4E2D\u5DF2\u4E0D\u5B58\u5728\uFF09`);
3556
+ }
3557
+ for (const w of audit.warnings) lines.push(`- ${w}`);
3558
+ lines.push(
3559
+ "",
3560
+ "\u4FEE\u590D\uFF08\u5728\u6D41\u7A0B\u76EE\u5F55\uFF09\uFF1A",
3561
+ "",
3562
+ "```powershell",
3563
+ "dazi flow project repair-meta --dir .",
3564
+ "dazi flow project doctor --dir .",
3565
+ "```",
3566
+ "",
3567
+ `\u8BE6\u89C1 [\u6D41\u7A0B\u672C\u5730\u6587\u4EF6\u89C4\u8303](${localFilesSpecLink})\u3002`,
3568
+ ""
3569
+ );
3570
+ }
3571
+ lines.push(
3572
+ "## \u53D8\u66F4\u63D0\u4EA4\u547D\u4EE4\u77E9\u9635",
3573
+ "",
3574
+ "| \u6539\u4E86\u4EC0\u4E48 | \u547D\u4EE4 |",
3575
+ "|----------|------|",
3576
+ "| `managed_file_id`\u3001`output_variable_name`\u3001\u8FDE\u7EBF\u3001\u589E\u5220\u8282\u70B9 | `dazi flow project push --dir . --canvas` |",
3577
+ "| `\u8282\u70B9/<\u540D>/code.py` \u6216 `code.sql` | `dazi flow node push --node <uuid> --dir .` |",
3578
+ '| **\u65B0\u589E\u4EE3\u7801\u8282\u70B9** | `dazi flow node new --type <type> --dir . --label "<\u540D>"` \u2192 \u6539\u914D\u7F6E \u2192 `push --canvas` \u2192 \u5199 code \u2192 `node push` |',
3579
+ "",
3580
+ "> \u7981\u6B62\u53EA\u6539 `flow.json` \u800C\u4E0D\u66F4\u65B0 `flow.meta.json`\uFF08\u987B `node new` / `pull` / `repair-meta`\uFF09\u3002",
3581
+ ""
3582
+ );
3583
+ return lines;
3584
+ }
3585
+ var import_crypto2, import_fs6, import_path7, NODES_DIR2, FLOW_JSON2, FLOW_META_JSON2, NODE_INFO_JSON2, NODE_TYPE_CODE_DATA_KEY2;
3586
+ var init_flowWorkspaceAudit = __esm({
3587
+ "src/shared/flowWorkspaceAudit.ts"() {
3588
+ "use strict";
3589
+ import_crypto2 = __toESM(require("crypto"));
3590
+ import_fs6 = __toESM(require("fs"));
3591
+ import_path7 = __toESM(require("path"));
3592
+ NODES_DIR2 = "\u8282\u70B9";
3593
+ FLOW_JSON2 = "flow.json";
3594
+ FLOW_META_JSON2 = "flow.meta.json";
3595
+ NODE_INFO_JSON2 = "node.info.json";
3596
+ NODE_TYPE_CODE_DATA_KEY2 = {
3597
+ "database-source": "sql",
3598
+ "dataspace-source": "sql",
3599
+ "python-script": "pythonCode",
3600
+ "excel-python": "pythonCode",
3601
+ "sql-query": "sql",
3602
+ condition: "pythonCode",
3603
+ "data-quality-check": "dqPythonCode"
3604
+ };
3605
+ }
3606
+ });
3156
3607
 
3157
- // cli/dazi-flow/src/commands/flows.ts
3158
- function makeFlowsCommand() {
3159
- const cmd = new Command("flows").description("Flow \u7BA1\u7406");
3160
- cmd.command("list").description("\u5217\u51FA\u6240\u6709 Flow").option("--space <spaceId>", "\u8FC7\u6EE4\u7A7A\u95F4").option("--status <status>", "\u8FC7\u6EE4\u72B6\u6001\uFF08active/draft/archived\uFF09").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
3161
- try {
3162
- const qs = new URLSearchParams();
3163
- if (opts.space) qs.set("spaceId", opts.space);
3164
- if (opts.status) qs.set("status", opts.status);
3165
- const qsStr = qs.toString() ? `?${qs}` : "";
3166
- const endpoint = `/api/data-pipelines/v1/flows${qsStr}`;
3167
- const flows = await apiRequest(endpoint);
3168
- if (opts.json) {
3169
- console.log(JSON.stringify(flows, null, 2));
3170
- ok({ flows });
3171
- return;
3608
+ // src/shared/flowScaffoldDocs.js
3609
+ var require_flowScaffoldDocs = __commonJS({
3610
+ "src/shared/flowScaffoldDocs.js"(exports2) {
3611
+ "use strict";
3612
+ var __importDefault = exports2 && exports2.__importDefault || function(mod) {
3613
+ return mod && mod.__esModule ? mod : { "default": mod };
3614
+ };
3615
+ Object.defineProperty(exports2, "__esModule", { value: true });
3616
+ exports2.writeScaffoldIfAllowed = exports2.buildFlowProjectReadmeHelpSection = exports2.removeLegacyFlowDirScaffoldReadme = exports2.resolveFlowRootFromFlowDir = exports2.removeLegacyFlowRootQuickStart = exports2.resolveFlowDirQuickStartPath = exports2.writeFlowDirQuickStartScaffold = exports2.removeLegacyFlowDirQuickStartFiles = exports2.buildFlowDirReadmeMarkdown = exports2.buildFlowDirQuickStartMarkdown = exports2.buildFlowsDirReadmeMarkdown = exports2.buildFlowQuickStartMarkdown = exports2.buildFlowRootReadmeMarkdown = exports2.isLegacyFlowScaffoldFile = exports2.isFlowScaffoldFile = exports2.flowDirQuickStartFilename = exports2.sanitizeFlowNameForFilename = exports2.FLOW_DIR_QUICKSTART_FILENAME = exports2.LEGACY_FLOW_DIR_QUICKSTART_FILENAME = exports2.LEGACY_FLOW_ROOT_QUICKSTART_FILENAME = exports2.FLOW_ROOT_README_FILENAME = exports2.flowDocsRel = void 0;
3617
+ var path_1 = __importDefault(require("path"));
3618
+ var flowNodeCatalog_1 = (init_flowNodeCatalog(), __toCommonJS(flowNodeCatalog_exports));
3619
+ var flowCliText_1 = (init_flowCliText(), __toCommonJS(flowCliText_exports));
3620
+ var flowWorkspaceAudit_1 = (init_flowWorkspaceAudit(), __toCommonJS(flowWorkspaceAudit_exports));
3621
+ var DEPTH = {
3622
+ "project-root": 2,
3623
+ "flows-dir": 3,
3624
+ "flow-dir": 4
3625
+ };
3626
+ function flowDocsRel(anchor, docPath) {
3627
+ const prefix = "../".repeat(DEPTH[anchor]);
3628
+ return `${prefix}\u8D44\u6E90/docs/${docPath}`;
3629
+ }
3630
+ exports2.flowDocsRel = flowDocsRel;
3631
+ var SCAFFOLD_MARKER = "<!-- dazi-flow-scaffold -->";
3632
+ exports2.FLOW_ROOT_README_FILENAME = "README.md";
3633
+ exports2.LEGACY_FLOW_ROOT_QUICKSTART_FILENAME = "\u5FEB\u901F\u542F\u52A8.md";
3634
+ exports2.LEGACY_FLOW_DIR_QUICKSTART_FILENAME = "\u5FEB\u901F\u542F\u52A8.md";
3635
+ exports2.FLOW_DIR_QUICKSTART_FILENAME = exports2.LEGACY_FLOW_DIR_QUICKSTART_FILENAME;
3636
+ function sanitizeFlowNameForFilename(flowName) {
3637
+ const safe = flowName.trim().replace(/[\\/:*?"<>|]/g, "_").replace(/\s+/g, "_").replace(/_+/g, "_").replace(/^\.+/, "").slice(0, 80);
3638
+ return safe || "flow";
3639
+ }
3640
+ exports2.sanitizeFlowNameForFilename = sanitizeFlowNameForFilename;
3641
+ function flowDirQuickStartFilename(flowName) {
3642
+ return `\u5FEB\u901F\u542F\u52A8_${sanitizeFlowNameForFilename(flowName)}.md`;
3643
+ }
3644
+ exports2.flowDirQuickStartFilename = flowDirQuickStartFilename;
3645
+ function isFlowScaffoldFile(content) {
3646
+ return content.includes(SCAFFOLD_MARKER);
3647
+ }
3648
+ exports2.isFlowScaffoldFile = isFlowScaffoldFile;
3649
+ function isLegacyFlowScaffoldFile(content) {
3650
+ if (isFlowScaffoldFile(content))
3651
+ return false;
3652
+ return content.includes("dazi-flow project pull") || content.includes("dazi-flow node push") || content.includes("\u89C1 \xA7335") || content.includes("# \u6D41\u7A0B") && content.includes("dazi-flow project pull");
3653
+ }
3654
+ exports2.isLegacyFlowScaffoldFile = isLegacyFlowScaffoldFile;
3655
+ function buildDocsTable(anchor) {
3656
+ const rows = [
3657
+ ["Flow \u6587\u6863\u7D22\u5F15\uFF08\u4ECE\u8FD9\u91CC\u5F00\u59CB\uFF09", "flow/flows-guide.md"],
3658
+ ["\u6570\u636E\u6D41\u7A0B\u9879\u76EE\u5F00\u53D1\u6307\u5357", "flow/flow-project-guide.md"],
3659
+ ["\u6D41\u7A0B\u53D8\u91CF\u7CFB\u7EDF\u6307\u5357", "flow/variables-guide.md"],
3660
+ ["\u8282\u70B9\u4EE3\u7801\u7F16\u5199\u6307\u5357\uFF08\u542B SQL/Python \u793A\u4F8B\uFF09", "flow/node-code-guide.md"],
3661
+ ["Flow \u8FD0\u884C\u4E0E\u6D4B\u8BD5", "flow/run-guide.md"],
3662
+ ["\u6570\u636E\u6E90\u4E0E connectionId", "flow/source-guide.md"],
3663
+ ["**\u6D41\u7A0B\u672C\u5730\u6587\u4EF6\u89C4\u8303\uFF08AI \u5FC5\u8BFB\uFF09**", "flow/local-files-spec.md"],
3664
+ ["\u6D41\u7A0B AI \u5DE5\u4F5C\u624B\u518C", "flow/ai-workflow-playbook.md"],
3665
+ ["CLI \u8C03\u7528\u7EA6\u5B9A\uFF08`dazi` / `dazi.ps1`\uFF09", "guides/cli-invocation.md"]
3666
+ ];
3667
+ return [
3668
+ "## \u5E2E\u52A9\u6587\u6863",
3669
+ "",
3670
+ "\u5DE5\u4F5C\u533A\u6587\u6863\u4F4D\u4E8E **`\u8D44\u6E90/docs/`**\uFF08\u6269\u5C55\u4FA7\u680F **\u5E2E\u52A9 \u2192 \u4E0B\u8F7D\u6240\u6709\u6587\u6863**\uFF0C\u6216\u5728\u5DE5\u4F5C\u533A\u6839\u6267\u884C `dazi docs sync`\uFF09\uFF1A",
3671
+ "",
3672
+ "| \u6587\u6863 | \u94FE\u63A5 |",
3673
+ "|------|------|",
3674
+ ...rows.map(([title, p]) => `| ${title} | [${p.split("/").pop()}](${flowDocsRel(anchor, p)}) |`),
3675
+ "",
3676
+ "\u7EC8\u7AEF\u6253\u5F00\u6587\u6863\uFF08**dazi-work \u6839**\u6216\u5DF2 `cd` \u5230\u6D41\u7A0B\u76EE\u5F55\uFF09\uFF1A",
3677
+ "",
3678
+ "```powershell",
3679
+ (0, flowCliText_1.psDaziLine)("docs open flow/flow-project-guide"),
3680
+ (0, flowCliText_1.psDaziLine)("docs open flow/variables-guide"),
3681
+ (0, flowCliText_1.psDaziLine)("docs open flow/node-code-guide"),
3682
+ (0, flowCliText_1.psDaziLine)("docs open flow/flows-guide"),
3683
+ "```",
3684
+ "",
3685
+ "**\u4EE3\u7801\u793A\u4F8B**\uFF1A\u53D8\u91CF\u8BFB\u5199\u3001`python-script` / `sql-query` \u6A21\u677F\u89C1 [\u8282\u70B9\u4EE3\u7801\u7F16\u5199\u6307\u5357](" + flowDocsRel(anchor, "flow/node-code-guide.md") + ") \u4E0E [\u6D41\u7A0B\u53D8\u91CF\u7CFB\u7EDF\u6307\u5357](" + flowDocsRel(anchor, "flow/variables-guide.md") + ")\u3002",
3686
+ ""
3687
+ ];
3688
+ }
3689
+ function psPath(p) {
3690
+ return p.replace(/\//g, "\\");
3691
+ }
3692
+ function buildFlowDirRelPath(projectFolder, flowName) {
3693
+ const folder = projectFolder ?? "<\u4E1A\u52A1\u9879\u76EE\u540D>";
3694
+ return `\u9879\u76EE/${folder}/\u6D41\u7A0B/flows/${flowName}`;
3695
+ }
3696
+ function buildFlowCliDirSection(opts) {
3697
+ const relPs = psPath(buildFlowDirRelPath(opts.projectFolder, opts.flowName));
3698
+ const absDir = opts.flowDir ? psPath(path_1.default.resolve(opts.flowDir)) : `D:\\path\\to\\dazi-work\\${relPs}`;
3699
+ return [
3700
+ "## CLI \u8C03\u7528\u7EA6\u5B9A\uFF08\u5FC5\u8BFB\uFF09",
3701
+ "",
3702
+ "> **`--dir` \u5FC5\u987B\u6307\u5411\u542B `flow.json` \u7684\u6D41\u7A0B\u76EE\u5F55**\uFF08\u5373\u672C\u76EE\u5F55\uFF09\u3002",
3703
+ "> **\u7981\u6B62**\u5728 `dazi-work` **\u5DE5\u4F5C\u533A\u6839**\u6267\u884C `dazi flow project status/push/pull ... --dir .`",
3704
+ "> \uFF08\u6839\u76EE\u5F55\u53EF\u80FD\u6B8B\u7559\u9519\u8BEF\u7684 `flow.meta.json`\uFF0C\u6216 Agent \u8BEF\u8BFB\u5176\u4ED6\u6D41\u7A0B\u7684 meta\uFF0C\u5BFC\u81F4 **flowId \u4E0E\u6D41\u7A0B\u540D\u4E0D\u4E00\u81F4**\uFF09\u3002",
3705
+ "",
3706
+ "### \u63A8\u8350\uFF1A\u7EDD\u5BF9\u8DEF\u5F84\uFF08Trae / Agent \u9996\u9009\uFF09",
3707
+ "",
3708
+ "\u4E0D\u4F9D\u8D56\u7EC8\u7AEF\u5F53\u524D\u76EE\u5F55\uFF0C\u907F\u514D `cd` \u5230\u9519\u8BEF\u6D41\u7A0B\uFF1A",
3709
+ "",
3710
+ "```powershell",
3711
+ (0, flowCliText_1.psFlowLine)(`project status --dir "${absDir}"`),
3712
+ (0, flowCliText_1.psFlowLine)(`project pull --flow ${opts.flowId} --dir "${absDir}"`),
3713
+ (0, flowCliText_1.psFlowLine)(`project push --dir "${absDir}" --canvas`),
3714
+ (0, flowCliText_1.psFlowLine)(`run node-exec --node <node_uuid> --dir "${absDir}"`),
3715
+ (0, flowCliText_1.psFlowLine)(`run flow-exec --dir "${absDir}" --type debug`),
3716
+ (0, flowCliText_1.psFlowLine)(`variable sync --dir "${absDir}"`),
3717
+ "```",
3718
+ "",
3719
+ "### \u53EF\u9009\uFF1A\u5148 cd \u518D `--dir .`",
3720
+ "",
3721
+ "\u4EC5\u5F53\u7EC8\u7AEF **\u5DF2\u786E\u8BA4** \u5728\u5F53\u524D\u6D41\u7A0B\u76EE\u5F55\u65F6\uFF1A",
3722
+ "",
3723
+ "```powershell",
3724
+ `cd "${absDir}"`,
3725
+ (0, flowCliText_1.psFlowLine)("project status --dir ."),
3726
+ (0, flowCliText_1.psFlowLine)(`project pull --flow ${opts.flowId} --dir .`),
3727
+ "```",
3728
+ "",
3729
+ "**\u81EA\u68C0**\uFF1A`status` \u8F93\u51FA\u987B\u540C\u65F6\u5339\u914D\u672C\u9875 **flowId** \u4E0E **\u6D41\u7A0B\u540D**\uFF1B\u82E5\u51FA\u73B0\u5176\u4ED6\u6D41\u7A0B\u540D\uFF0C\u8BF4\u660E `--dir` \u6216 `cd` \u9519\u8BEF\u3002",
3730
+ ""
3731
+ ];
3732
+ }
3733
+ function cliPrefix() {
3734
+ return `\`${flowCliText_1.DAZI_CLI} flow\`\uFF08\u5168\u5C40 CLI\uFF1B\`--dir\` \u6307\u5411 **\u6D41\u7A0B\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84**\uFF1B\u672A\u88C5\u5168\u5C40\u65F6\u53EF\u7528 \`.scriptsdazi.ps1 flow\`\uFF09`;
3735
+ }
3736
+ function buildFlowRootReadmeMarkdown2(opts) {
3737
+ const folder = opts.projectFolder;
3738
+ return [
3739
+ SCAFFOLD_MARKER,
3740
+ "# \u6D41\u7A0B",
3741
+ "",
3742
+ `\u672C\u76EE\u5F55\u4E3A\u4E1A\u52A1\u9879\u76EE **\`${folder}\`** \u7684\u6570\u636E\u6D41\u7A0B\u5DE5\u4F5C\u533A\u3002`,
3743
+ "",
3744
+ "## 1. \u76EE\u5F55\u7ED3\u6784",
3745
+ "",
3746
+ "| \u8DEF\u5F84 | \u8BF4\u660E |",
3747
+ "|------|------|",
3748
+ "| `\u89C4\u5212/` | \u6D41\u7A0B\u8BBE\u8BA1\u6587\u6863\uFF08\u6570\u636E\u6E90\u3001\u62D3\u6251\u8349\u6848\u7B49\uFF09 |",
3749
+ "| `flows/` | \u5404\u6D41\u7A0B\u5B9E\u4F8B\u76EE\u5F55\uFF08`flow.json`\u3001`\u8282\u70B9/`\u3001`\u53D8\u91CF/`\uFF09 |",
3750
+ "",
3751
+ "\u5355\u4E2A\u6D41\u7A0B\u4F4D\u4E8E `flows/<\u6D41\u7A0B\u540D>/`\uFF1A",
3752
+ "",
3753
+ "| \u8DEF\u5F84 | \u8BF4\u660E |",
3754
+ "|------|------|",
3755
+ "| `flow.json` | \u753B\u5E03\u771F\u7406\u6E90\uFF08\u8282\u70B9\u914D\u7F6E + \u8FDE\u7EBF\uFF0C\u4EE3\u7801\u5DF2\u5265\u79BB\uFF09 |",
3756
+ "| `flow.meta.json` | flowId\u3001node_uuid \u6620\u5C04\uFF08CLI \u7EF4\u62A4\uFF0C\u52FF\u624B\u6539\uFF09 |",
3757
+ "| `\u8282\u70B9/<\u540D>/code.*` | SQL / Python \u8282\u70B9\u4EE3\u7801 |",
3758
+ "| `\u53D8\u91CF/<\u540D>.json` | \u8C03\u8BD5 Run \u53D8\u91CF\u9884\u89C8\uFF08\u53EA\u8BFB\uFF0Cpull/sync \u751F\u6210\uFF09 |",
3759
+ "| `_run/` | \u6D4B\u8BD5/\u8FD0\u884C\u4EA7\u7269\uFF08`*.last-error.md` \u7B49\uFF09 |",
3760
+ "",
3761
+ "> \u6D41\u7A0B**\u4E0D\u7ED1\u5B9A\u6570\u636E\u7A7A\u95F4**\uFF1B`connectionId` \u4EC5\u4E3A\u6570\u636E\u5E93\u8282\u70B9\u7684\u914D\u7F6E\u5B57\u6BB5\u3002",
3762
+ "",
3763
+ "## 2. \u65B0\u5EFA / \u62C9\u53D6\u6D41\u7A0B",
3764
+ "",
3765
+ "\u8D44\u6E90\u7BA1\u7406\u5668\uFF08\u63A8\u8350 **\u8868\u5355**\uFF09\uFF1A",
3766
+ "",
3767
+ "| \u64CD\u4F5C | \u83DC\u5355 |",
3768
+ "|------|------|",
3769
+ "| \u65B0\u5EFA\u6D41\u7A0B | \u53F3\u952E **`\u6D41\u7A0B/flows/`**\uFF08\u6216\u5176\u5B50\u76EE\u5F55\uFF09\u2192 **\u65B0\u5EFA\u6D41\u7A0B** |",
3770
+ "| \u62C9\u53D6\u5E73\u53F0\u6D41\u7A0B | \u53F3\u952E **`\u6D41\u7A0B/flows/`**\uFF08\u6216\u5176\u5B50\u76EE\u5F55\uFF09\u2192 **\u62C9\u53D6\u5E73\u53F0\u6D41\u7A0B** |",
3771
+ "",
3772
+ "\u62C9\u53D6\u8868\u5355\u53EF\u4ECE\u5E73\u53F0 Flow \u5217\u8868\u9009\u62E9\uFF0C\u6216\u624B\u52A8\u586B\u5199 Flow ID\uFF1B\u65B0\u5EFA\u8868\u5355\u586B\u5199\u540D\u79F0\u4E0E\u8BF4\u660E\u540E\u81EA\u52A8\u521B\u5EFA\u5E76\u62C9\u53D6\u3002",
3773
+ "",
3774
+ "> **\u7981\u6B62**\u5728 `dazi-work` \u5DE5\u4F5C\u533A\u6839\u5BF9\u6D41\u7A0B\u547D\u4EE4\u4F7F\u7528 `--dir .`\uFF1B`--dir` \u987B\u6307\u5411 `flows/<\u6D41\u7A0B\u540D>/`\uFF08\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09\u3002",
3775
+ "",
3776
+ "\u547D\u4EE4\u884C\uFF08`--dir` \u4F7F\u7528\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9 `dazi-work` \u6839\uFF09\uFF1A",
3777
+ "",
3778
+ "```powershell",
3779
+ (0, flowCliText_1.psFlowLine)('project pull --flow <flowId> --dir "D:\\path\\to\\dazi-work\\\u9879\u76EE\\' + folder + '\\\u6D41\u7A0B\\flows\\<\u6D41\u7A0B\u540D>"'),
3780
+ "```",
3781
+ "",
3782
+ "\u62C9\u53D6\u540E\u6BCF\u4E2A\u6D41\u7A0B\u76EE\u5F55\u4F1A\u751F\u6210 **`\u5FEB\u901F\u542F\u52A8_<\u6D41\u7A0B\u540D>.md`**\uFF08\u542B flowId \u4E0E\u672C\u6D41\u7A0B\u5E38\u7528\u547D\u4EE4\uFF0C\u4FBF\u4E8E @ \u9644\u52A0\u7ED9 AI\uFF09\u3002",
3783
+ "",
3784
+ "## 3. \u5F00\u53D1\u4E0E\u63D0\u4EA4",
3785
+ "",
3786
+ "1. \u7F16\u8F91 `\u8282\u70B9/<\u540D>/code.sql` \u6216 `code.py`",
3787
+ "2. \u53F3\u952E **\u6D4B\u8BD5\u8FD0\u884C\u8282\u70B9** \u6216 `flow run node-exec --node <uuid>`",
3788
+ "3. \u53F3\u952E **\u63D0\u4EA4\u8282\u70B9** / **\u63D0\u4EA4\u6D41\u7A0B** \u6216 `flow node push` / `flow project push --canvas`",
3789
+ "4. \u67E5\u770B\u8868\u53D8\u91CF\uFF1A\u8BBE\u8BA1\u5668 **output_variable_name** \u65C1 \u{1F4CA}\uFF0C\u6216 `flow variable pull --name <\u540D>`",
3790
+ "",
3791
+ "\u547D\u4EE4\u524D\u7F00\uFF1A" + cliPrefix(),
3792
+ "",
3793
+ "## 4. \u63A8\u8350\u5F00\u53D1\u5FAA\u73AF",
3794
+ "",
3795
+ "\u8BE6\u89C1 [\u6570\u636E\u6D41\u7A0B\u9879\u76EE\u5F00\u53D1\u6307\u5357](" + flowDocsRel("project-root", "flow/flow-project-guide.md") + ") \xA74\u3002",
3796
+ "",
3797
+ "## 5. AI \u63D0\u793A\u8BCD\u63A8\u8350",
3798
+ "",
3799
+ "| \u573A\u666F | \u63D0\u793A\u8BCD ID |",
3800
+ "|------|-----------|",
3801
+ "| \u8BBE\u8BA1/\u65B0\u589E\u6D41\u7A0B | `flow/flow-design` |",
3802
+ "| **\u8FD0\u884C\u5931\u8D25\u3001\u81EA\u4E3B\u6539\u9519** | **`flow/run-fix-loop`**\uFF08\u2B50 \u9996\u9009\uFF09 |",
3803
+ "| \u5206\u6790\u7528\u6237\u7C98\u8D34\u7684\u9519\u8BEF | `flow/run-debug` |",
3804
+ "",
3805
+ "\u4FA7\u680F **\u5E2E\u52A9 \u2192 \u{1F916} \u63D0\u793A\u8BCD** \u4E2D \u2B50 \u4E3A\u6D41\u7A0B\u63A8\u8350\u3002\u53F3\u952E **`flow.json`** \u2192 **\u6253\u5F00 AI \u6539\u9519\u63D0\u793A\u8BCD**\u3002",
3806
+ "\u7ED9 AI \u7684\u4EFB\u52A1\u793A\u4F8B\uFF1A\u300C\u6539 XX \u8282\u70B9\uFF0C\u81EA\u5DF1\u8DD1\u6D4B\u8BD5\uFF0C\u5931\u8D25\u6309 last-error \u6539\u5230\u901A\u8FC7\u518D push\u300D\u3002",
3807
+ "",
3808
+ ...buildDocsTable("project-root")
3809
+ ].join("\n");
3810
+ }
3811
+ exports2.buildFlowRootReadmeMarkdown = buildFlowRootReadmeMarkdown2;
3812
+ function buildFlowQuickStartMarkdown(opts) {
3813
+ return buildFlowRootReadmeMarkdown2(opts);
3814
+ }
3815
+ exports2.buildFlowQuickStartMarkdown = buildFlowQuickStartMarkdown;
3816
+ function buildFlowsDirReadmeMarkdown2(opts) {
3817
+ return [
3818
+ SCAFFOLD_MARKER,
3819
+ "# flows",
3820
+ "",
3821
+ "\u6BCF\u4E2A\u5B50\u76EE\u5F55\u662F\u4E00\u4E2A\u6D41\u7A0B\u5B9E\u4F8B\uFF08\u542B `flow.json`\u3001`flow.meta.json`\uFF09\u3002",
3822
+ "",
3823
+ "## \u64CD\u4F5C",
3824
+ "",
3825
+ "| \u64CD\u4F5C | \u65B9\u5F0F |",
3826
+ "|------|------|",
3827
+ "| \u65B0\u5EFA\u6D41\u7A0B | \u53F3\u952E\u672C\u76EE\u5F55 \u2192 **\u65B0\u5EFA\u6D41\u7A0B**\uFF08\u8868\u5355\uFF1A\u540D\u79F0\u3001\u8BF4\u660E\u3001\u8DEF\u5F84\u9884\u89C8\uFF09 |",
3828
+ "| \u62C9\u53D6\u5E73\u53F0\u6D41\u7A0B | \u53F3\u952E \u2192 **\u62C9\u53D6\u5E73\u53F0\u6D41\u7A0B**\uFF08\u8868\u5355\uFF1A\u5E73\u53F0\u5217\u8868 / Flow ID\u3001\u672C\u5730\u76EE\u5F55\u540D\uFF09 |",
3829
+ "| \u6253\u5F00\u8BBE\u8BA1\u5668 | \u53F3\u952E `flow.json` \u2192 **\u6253\u5F00\u6D41\u7A0B\u8BBE\u8BA1\u5668** |",
3830
+ "",
3831
+ "> **\u7981\u6B62**\u5728 `dazi-work` \u6839\u6267\u884C `project status/push/pull --dir .`\u3002",
3832
+ "",
3833
+ "```powershell",
3834
+ "# \u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84",
3835
+ (0, flowCliText_1.psFlowLine)(`project pull --flow <id> --dir "D:\\path\\to\\dazi-work\\\u9879\u76EE\\${opts.projectFolder}\\\u6D41\u7A0B\\flows\\<\u6D41\u7A0B\u540D>"`),
3836
+ "```",
3837
+ "",
3838
+ "\u62C9\u53D6\u5B8C\u6210\u540E\u8BF7\u9605\u8BFB\u5404\u6D41\u7A0B\u5B50\u76EE\u5F55\u4E0B\u7684 **`\u5FEB\u901F\u542F\u52A8_<\u6D41\u7A0B\u540D>.md`**\u3002",
3839
+ "",
3840
+ ...buildDocsTable("flows-dir")
3841
+ ].join("\n");
3842
+ }
3843
+ exports2.buildFlowsDirReadmeMarkdown = buildFlowsDirReadmeMarkdown2;
3844
+ function buildFlowDirQuickStartMarkdown2(opts) {
3845
+ const name = opts.flowName;
3846
+ const relDir = buildFlowDirRelPath(opts.projectFolder, name);
3847
+ let audit;
3848
+ if (opts.flowDir) {
3849
+ try {
3850
+ audit = (0, flowWorkspaceAudit_1.auditFlowWorkspace)(opts.flowDir);
3851
+ } catch {
3852
+ }
3172
3853
  }
3173
- if (!flows.length) {
3174
- console.log("\uFF08\u6682\u65E0 Flow\uFF09");
3175
- ok({ flows: [] });
3854
+ const canvasN = audit?.canvasNodeCount ?? opts.nodeCount;
3855
+ const codeN = audit?.canvasCodeNodeCount ?? opts.codeNodeCount;
3856
+ const metaCodeN = audit?.metaCodeIndexedCount;
3857
+ const statsParts = [];
3858
+ if (canvasN != null) {
3859
+ let line = `
3860
+ | \u753B\u5E03\u8282\u70B9\uFF08flow.json\uFF09 | ${canvasN}`;
3861
+ if (codeN != null)
3862
+ line += `\uFF0C\u5176\u4E2D\u4EE3\u7801\u8282\u70B9 ${codeN}`;
3863
+ line += " |";
3864
+ statsParts.push(line);
3865
+ }
3866
+ if (metaCodeN != null && codeN != null && metaCodeN !== codeN) {
3867
+ statsParts.push(`
3868
+ | meta \u5DF2\u7D22\u5F15\u4EE3\u7801 | ${metaCodeN} \u26A0\uFE0F \u4E0E\u753B\u5E03\u4E0D\u4E00\u81F4 |`);
3869
+ } else if (metaCodeN != null) {
3870
+ statsParts.push(`
3871
+ | meta \u5DF2\u7D22\u5F15\u4EE3\u7801 | ${metaCodeN} |`);
3872
+ }
3873
+ const pulled = opts.pulledAt ? `
3874
+ | \u4E0A\u6B21\u62C9\u53D6 | ${opts.pulledAt} |` : "";
3875
+ const consistencySection = audit ? (0, flowWorkspaceAudit_1.buildFlowConsistencyMarkdown)(audit, flowDocsRel("flow-dir", "flow/local-files-spec.md")) : [];
3876
+ return [
3877
+ SCAFFOLD_MARKER,
3878
+ "# \u5FEB\u901F\u542F\u52A8",
3879
+ "",
3880
+ `**\u6D41\u7A0B**\uFF1A${name} \xB7 \u5E73\u53F0 flowId \`${opts.flowId}\``,
3881
+ "",
3882
+ "## \u6D41\u7A0B\u4FE1\u606F",
3883
+ "",
3884
+ "| \u5B57\u6BB5 | \u503C |",
3885
+ "|------|-----|",
3886
+ `| flowId | \`${opts.flowId}\` |`,
3887
+ `| \u672C\u5730\u76EE\u5F55 | \`${relDir}/\` |${statsParts.join("")}${pulled}`,
3888
+ "",
3889
+ ...consistencySection,
3890
+ ...buildFlowCliDirSection({
3891
+ flowDir: opts.flowDir,
3892
+ projectFolder: opts.projectFolder,
3893
+ flowName: name,
3894
+ flowId: opts.flowId
3895
+ }),
3896
+ "## \u5E38\u7528\u547D\u4EE4\u901F\u67E5",
3897
+ "",
3898
+ "\u9700\u5DF2\u5B89\u88C5\u5168\u5C40 CLI\uFF08`pnpm add -g @dazitech/cli`\uFF09\u6216\u672A\u88C5\u65F6\u7528 `.scriptsdazi.ps1 flow`\uFF1A",
3899
+ "",
3900
+ "```powershell",
3901
+ "# \u5C06 <FLOW_DIR> \u66FF\u6362\u4E3A\u4E0A\u4E00\u8282\u7EDD\u5BF9\u8DEF\u5F84",
3902
+ (0, flowCliText_1.psFlowLine)('project status --dir "<FLOW_DIR>"'),
3903
+ (0, flowCliText_1.psFlowLine)(`project pull --flow ${opts.flowId} --dir "<FLOW_DIR>"`),
3904
+ (0, flowCliText_1.psFlowLine)('project push --dir "<FLOW_DIR>" --canvas'),
3905
+ (0, flowCliText_1.psFlowLine)('run node-exec --node <node_uuid> --dir "<FLOW_DIR>"'),
3906
+ (0, flowCliText_1.psFlowLine)('run flow-exec --dir "<FLOW_DIR>" --type debug'),
3907
+ (0, flowCliText_1.psFlowLine)('variable sync --dir "<FLOW_DIR>"'),
3908
+ "```",
3909
+ "",
3910
+ ...(0, flowNodeCatalog_1.buildFlowNodeCatalogQuickStartSection)(flowDocsRel("flow-dir", "flow/flows-guide.md")),
3911
+ "## AI \u4FEE\u6539\u73B0\u6709\u6D41\u7A0B\uFF08\u5355\u6587\u4EF6\u5165\u53E3\uFF09",
3912
+ "",
3913
+ "> \u63A8\u8350\u53EA\u628A\u672C\u6587\u4EF6\u9644\u52A0\u7ED9 AI\uFF1B\u82E5\u4FE1\u606F\u4E0D\u8DB3\uFF0C\u518D\u6309\u9700\u6253\u5F00\u300C\u5E2E\u52A9\u6587\u6863\u300D\u4E2D\u7684\u4E13\u9898\u3002",
3914
+ "",
3915
+ "### \u573A\u666F A\uFF1A\u65B0\u589E\u8282\u70B9\uFF08\u5DF2\u6709\u6D41\u7A0B\u7EE7\u7EED\u6539\uFF09",
3916
+ "",
3917
+ "1. \u5148\u8BFB `flow.json` \u4E0E `flow.meta.json`\uFF0C\u6267\u884C `dazi flow project doctor --dir .`",
3918
+ `2. **\u5FC5\u987B**\u7528 CLI \u65B0\u5EFA\u8282\u70B9\uFF08\u7981\u6B62\u624B\u6413 uuid\uFF09\uFF1A${(0, flowCliText_1.mdFlowCmd)('node new --type <node_type> --dir . --label "<\u8282\u70B9\u540D>"')}`,
3919
+ "3. \u5728 `flow.json` \u91CC\u8865 `nodes/edges`\uFF08\u9075\u5B88\u951A\u70B9\uFF1A`sourceHandle` \u4EC5 `r/b/true/false`\uFF0C`targetHandle` \u4EC5 `l/t`\uFF09",
3920
+ "4. \u7F16\u8F91 `\u8282\u70B9/<\u540D>/code.sql|py`\uFF08`node new` \u4F1A\u521B\u5EFA\u76EE\u5F55\u4E0E meta \u7D22\u5F15\uFF09",
3921
+ `5. \u5355\u8282\u70B9\u6D4B\u8BD5\uFF1A${(0, flowCliText_1.mdFlowCmd)("run node-exec --node <node_uuid> --dir .")}`,
3922
+ `6. \u63D0\u4EA4\u753B\u5E03\uFF08\u542B\u8FDE\u7EBF/\u914D\u7F6E\uFF09\uFF1A${(0, flowCliText_1.mdFlowCmd)("project push --dir . --canvas")}`,
3923
+ "",
3924
+ "### \u573A\u666F B\uFF1A\u4EC5\u6539\u8282\u70B9\u4EE3\u7801",
3925
+ "",
3926
+ "1. \u7F16\u8F91 `\u8282\u70B9/<\u540D>/code.sql|py`\uFF08\u4E0D\u8981\u628A\u6B63\u6587\u5199\u56DE `flow.json`\uFF09",
3927
+ `2. \u8FD0\u884C\u8282\u70B9\uFF1A${(0, flowCliText_1.mdFlowCmd)("run node-exec --node <node_uuid> --dir .")}`,
3928
+ `3. \u63D0\u4EA4\u4EE3\u7801\uFF1A${(0, flowCliText_1.mdFlowCmd)("node push --node <node_uuid> --dir .")}`,
3929
+ "",
3930
+ "### \u573A\u666F C\uFF1A\u753B\u5E03\u548C\u4EE3\u7801\u4E0D\u540C\u6B65",
3931
+ "",
3932
+ `1. \u5148 ${(0, flowCliText_1.mdFlowCmd)("project status --dir .")} \u770B\u672C\u5730\u810F\u6539\u52A8`,
3933
+ `2. \u82E5\u6539\u4E86\u62D3\u6251/\u8FDE\u7EBF/\u914D\u7F6E\uFF1A\u6267\u884C ${(0, flowCliText_1.mdFlowCmd)("project push --dir . --canvas")}`,
3934
+ `3. \u82E5\u53EA\u6539\u4E86\u4EE3\u7801\uFF1A\u6267\u884C ${(0, flowCliText_1.mdFlowCmd)("node push --node <node_uuid> --dir .")}`,
3935
+ `4. \u5FC5\u8981\u65F6\u91CD\u65B0\u62C9\u53D6\u6821\u5BF9\uFF1A${(0, flowCliText_1.mdFlowCmd)(`project pull --flow ${opts.flowId} --dir .`)}`,
3936
+ "",
3937
+ "## AI \u81EA\u68C0\u6E05\u5355\uFF08\u63D0\u4EA4\u524D\uFF09",
3938
+ "",
3939
+ `- \u547D\u4EE4\u524D\u7F00\u662F\u5426\u7EDF\u4E00\u4E3A ${(0, flowCliText_1.mdFlowCmd)("...")}\uFF08\u800C\u4E0D\u662F\u88F8 \`dazi-flow\`\uFF09`,
3940
+ "- `flow.json` \u662F\u5426\u53EA\u627F\u8F7D\u753B\u5E03\u62D3\u6251\u4E0E\u8282\u70B9\u914D\u7F6E\uFF08\u4E0D\u5D4C\u5165\u5927\u6BB5 SQL/Python\uFF09",
3941
+ "- `code.*` \u662F\u5426\u4E0E\u8282\u70B9\u4E1A\u52A1\u7C7B\u578B\u4E00\u81F4\uFF08SQL \u8282\u70B9 `code.sql`\uFF0CPython \u8282\u70B9 `code.py`\uFF09",
3942
+ "- \u6761\u4EF6\u8282\u70B9\u51FA\u8FB9\u662F\u5426\u4EC5\u4F7F\u7528 `true/false`\uFF0C\u672A\u8BEF\u7528 `r/b`",
3943
+ "- \u82E5\u6539\u52A8\u753B\u5E03\uFF0C\u662F\u5426\u6267\u884C\u4E86 `flow project push --canvas`",
3944
+ "",
3945
+ "## \u5E38\u89C1\u9519\u8BEF\u4E0E\u4FEE\u590D",
3946
+ "",
3947
+ "| \u73B0\u8C61 | \u5E38\u89C1\u539F\u56E0 | \u4FEE\u590D |",
3948
+ "|------|----------|------|",
3949
+ `| \u547D\u4EE4\u627E\u4E0D\u5230 | \u672A\u88C5\u5168\u5C40 CLI \u6216\u4E0D\u5728 dazi-work | \`pnpm add -g @dazitech/cli\` \u6216 ${(0, flowCliText_1.mdFlowCmd)("...")} / \`.scriptsdazi.ps1 flow ...\` |`,
3950
+ "| \u8282\u70B9\u6D4B\u8BD5\u62A5\u4E0A\u6E38\u53D8\u91CF\u4E0D\u5B58\u5728 | \u672A\u5148\u8FD0\u884C\u4E0A\u6E38\u8282\u70B9\u4EA7\u51FA\u53D8\u91CF | \u5148\u8DD1\u4E0A\u6E38\uFF0C\u6216\u6574\u6D41\u7A0B `flow run flow-exec --type debug` \u540E\u518D\u6D4B |",
3951
+ "| excel-python \u627E\u4E0D\u5230\u6587\u4EF6 | code.py \u5199\u4E86\u6587\u4EF6\u540D/\u672C\u5730\u8DEF\u5F84 | \u753B\u5E03\u914D `managed_file_id`\uFF08UUID\uFF09\uFF1B\u4EE3\u7801\u7528 `excel_source_path` |",
3952
+ "| Excel \u7528\u4E86 file-source | file-source \u4E0D\u89E3\u6790 xlsx | \u6539\u4E3A **`excel-python`** + `managed_file_id` |",
3953
+ "| excel-python \u65E0\u4E3B\u8F93\u51FA | \u672A `set_table_output` \u6216\u540D\u79F0\u4E0E `output_variable_name` \u4E0D\u4E00\u81F4 | \u89C1 node-code-guide \xA75 |",
3954
+ "| \u753B\u5E03\u663E\u793A\u5BF9\u4F46\u5E73\u53F0\u4E0D\u751F\u6548 | \u53EA push \u4E86\u4EE3\u7801\uFF0C\u672A push \u753B\u5E03 | \u6267\u884C `flow project push --dir . --canvas` |",
3955
+ "| \u4EE3\u7801\u6539\u4E86\u4F46\u5E73\u53F0\u8FD8\u662F\u65E7\u4EE3\u7801 | \u6539\u4E86\u672C\u5730 `code.*` \u4F46\u672A node push | \u6267\u884C `flow node push --node <node_uuid> --dir .` |",
3956
+ "| **flowId \u4E0E\u5FEB\u901F\u542F\u52A8\u4E0D\u4E00\u81F4** | \u5728 **dazi-work \u6839**\u6216**\u9519\u8BEF\u6D41\u7A0B\u76EE\u5F55**\u6267\u884C `--dir .` | \u6539\u7528\u4E0A\u6587 **\u7EDD\u5BF9\u8DEF\u5F84**\uFF1B`status` \u987B\u5339\u914D\u672C\u9875 flowId/\u6D41\u7A0B\u540D |",
3957
+ "",
3958
+ "## AI \u81EA\u4E3B\u8FD0\u884C\u4E0E\u6539\u9519\u95ED\u73AF\uFF08Agent \u5FC5\u8BFB\uFF09",
3959
+ "",
3960
+ "> **\u6269\u5C55/\u83DC\u5355\u4E0D\u4F1A\u81EA\u52A8\u6539\u4EE3\u7801**\uFF08\u5E73\u53F0 D6\uFF09\uFF1B\u7528\u6237\u59D4\u6258\u4F60\u6539\u6D41\u7A0B\u65F6\uFF0C\u4F60**\u5FC5\u987B\u4E3B\u52A8**\u6267\u884C\u300C\u6539 \u2192 \u8DD1 \u2192 \u8BFB\u9519 \u2192 \u518D\u6539 \u2192 \u518D\u8DD1\u300D\uFF0C\u76F4\u5230\u901A\u8FC7\u6216\u8FBE\u5230\u91CD\u8BD5\u4E0A\u9650\u3002",
3961
+ "",
3962
+ "### \u9519\u8BEF\u843D\u5728\u54EA\u91CC\uFF08\u8DD1\u5B8C\u5FC5\u67E5\uFF09",
3963
+ "",
3964
+ "| \u8FD0\u884C\u65B9\u5F0F | \u5931\u8D25\u65F6\u8BFB | \u6210\u529F/\u6B65\u9AA4\u6458\u8981 |",
3965
+ "|----------|----------|---------------|",
3966
+ "| \u5355\u8282\u70B9 `run node-exec` | `_run/<\u8282\u70B9\u540D>.last-error.md` | \u65E0 error \u6587\u4EF6\u5373\u901A\u8FC7 |",
3967
+ "| \u6574\u6D41\u7A0B `run flow-exec` | `_run/flow.last-error.md` | `_run/flow.last-run.md`\uFF08\u6B65\u9AA4+\u65E5\u5FD7\uFF09 |",
3968
+ "",
3969
+ "CLI \u5E26 `--json` \u65F6\uFF1A\u770B\u8FD4\u56DE `success: false` \u6216 `errorFile` \u5B57\u6BB5\uFF0C**\u518D\u6253\u5F00\u5BF9\u5E94 md \u6587\u4EF6**\uFF0C\u4E0D\u8981\u53EA\u770B\u7EC8\u7AEF\u4E00\u884C\u62A5\u9519\u3002",
3970
+ "",
3971
+ "### \u6807\u51C6\u6539\u9519\u5FAA\u73AF\uFF08\u9ED8\u8BA4\u6700\u591A 3 \u8F6E\uFF09",
3972
+ "",
3973
+ "1. **\u5B9A\u4F4D**\uFF1A\u8BFB `flow.json` + \u76EE\u6807 `\u8282\u70B9/<\u540D>/code.*` + `flow.meta.json`\uFF08\u53D6 `node_uuid`\uFF09",
3974
+ "2. **\u4FEE\u6539**\uFF1A\u53EA\u6539\u5FC5\u8981\u6587\u4EF6\uFF08\u4EE3\u7801 \u2192 `code.*`\uFF1B\u8FDE\u7EBF/\u914D\u7F6E \u2192 `flow.json`\uFF09",
3975
+ "3. **\u9A8C\u8BC1**\uFF08\u5148\u5C0F\u540E\u5927\uFF09\uFF1A",
3976
+ ` - \u5355\u8282\u70B9\uFF1A${(0, flowCliText_1.mdFlowCmd)("run node-exec --node <node_uuid> --dir .")}`,
3977
+ ` - \u6574\u6D41\u7A0B\uFF1A${(0, flowCliText_1.mdFlowCmd)("run flow-exec --dir . --type debug")}`,
3978
+ "4. **\u5224\u9519**\uFF1A\u9000\u51FA\u7801\u975E 0 / JSON `success:false` \u2192 \u6253\u5F00 `_run/*.last-error.md`\uFF0C\u6309\u5176\u4E2D**\u9519\u8BEF\u5206\u7C7B**\u4E0E traceback \u4FEE\u590D",
3979
+ "5. **\u91CD\u8BD5**\uFF1A\u56DE\u5230\u6B65\u9AA4 2\uFF1B\u82E5 3 \u8F6E\u4ECD\u5931\u8D25\uFF0C\u6C47\u603B\u5DF2\u5C1D\u8BD5\u4FEE\u590D\u70B9\u5E76\u8BF7\u6C42\u7528\u6237\u4ECB\u5165",
3980
+ "6. **\u63D0\u4EA4**\uFF08\u4EC5\u901A\u8FC7\u540E\uFF09\uFF1A\u4EE3\u7801 `node push`\uFF1B\u753B\u5E03 `project push --canvas`",
3981
+ "",
3982
+ "### \u5E38\u89C1\u5931\u8D25 \u2192 \u4F18\u5148\u52A8\u4F5C",
3983
+ "",
3984
+ "| last-error \u5206\u7C7B | \u4F18\u5148\u68C0\u67E5 |",
3985
+ "|-----------------|----------|",
3986
+ "| \u7F3A\u4E0A\u6E38\u53D8\u91CF | \u5148\u8DD1\u4E0A\u6E38\u8282\u70B9\u6216\u6574\u6D41\u7A0B debug\uFF0C\u518D `variable pull` \u770B schema |",
3987
+ "| \u914D\u7F6E\u7F3A\u5931 | `flow.json` \u8BE5\u8282\u70B9 `data`\uFF08connectionId / output_variable_name \u7B49\uFF09 |",
3988
+ "| \u4EE3\u7801\u9519\u8BEF | `code.*` \u4E0E traceback\uFF1B\u6539\u540E\u53EA `node push` |",
3989
+ "| \u8FDE\u63A5/\u6570\u636E\u6E90 | connectionId / spaceId \u662F\u5426\u6709\u6548 |",
3990
+ "",
3991
+ "### Agent \u7981\u6B62\u9879",
3992
+ "",
3993
+ "- **\u7981\u6B62**\u672A\u5B9E\u9645\u8FD0\u884C\u5C31\u58F0\u79F0\u300C\u5DF2\u4FEE\u590D/\u5DF2\u901A\u8FC7\u300D",
3994
+ "- **\u7981\u6B62**\u4E0D\u8BFB `_run/*.last-error.md` \u5C31\u731C\u6D4B\u539F\u56E0",
3995
+ "- **\u7981\u6B62**\u4FEE\u590D\u540E\u8DF3\u8FC7\u9A8C\u8BC1\u76F4\u63A5 `push`",
3996
+ "- **\u7981\u6B62**\u628A SQL/Python \u6B63\u6587\u5199\u56DE `flow.json`",
3997
+ "",
3998
+ "\u4FA7\u680F\u63D0\u793A\u8BCD\u53EF\u9009\u7528 **`flow/run-fix-loop`**\uFF08\u81EA\u4E3B\u6539\u9519\u4E13\u7528\uFF09\u3002",
3999
+ "",
4000
+ "\u6269\u5C55\uFF1A\u53F3\u952E **`flow.json`** / **\u8282\u70B9/** / **`code.*`** \u53EF\u6D4B\u8BD5\u3001\u63D0\u4EA4\u3001\u6253\u5F00\u8BBE\u8BA1\u5668\u3002",
4001
+ "",
4002
+ "## \u7F16\u5199\u8282\u70B9\u4EE3\u7801",
4003
+ "",
4004
+ "- **SQL \u8282\u70B9**\uFF08`sql-query` / `database-source`\uFF09\uFF1A\u7F16\u8F91 `\u8282\u70B9/<\u540D>/code.sql`\uFF1B`sql-query` \u4E2D\u7528 **\u4E0A\u6E38\u53D8\u91CF\u540D\u4F5C\u8868\u540D**\uFF08`FROM \u4E0A\u6E38\u53D8\u91CF\u540D`\uFF09",
4005
+ '- **Python \u8282\u70B9**\uFF08`python-script`\uFF09\uFF1A\u7F16\u8F91 `code.py`\uFF1B\u5355\u8282\u70B9\u6D4B\u8BD5\u7528 **`get_variable("\u4E0A\u6E38\u53D8\u91CF\u540D")`**\uFF0C\u8F93\u51FA\u8D4B\u503C **`result_df`**',
4006
+ "- **`excel-python`\uFF08Excel \u9ED8\u8BA4\u9996\u9009\uFF09**\uFF1A",
4007
+ " - \u6709 **`managed_file_id` \u7684 Excel \u2192 \u4F18\u5148 `excel-python`**\uFF0C**\u52FF\u7528 `file-source`**\uFF08file-source \u4E0D\u89E3\u6790 Excel\uFF09",
4008
+ " - **\u753B\u5E03**\u914D **`managed_file_id`**\uFF08UUID\uFF09\uFF1B**`code.py` \u53EA\u7528 `excel_source_path`**",
4009
+ ' - \u4E3B\u8F93\u51FA **`set_table_output("<\u4E0E output_variable_name \u540C\u540D>", df)`**',
4010
+ " - \u5B8C\u6574\u793A\u4F8B \u2192 [node-code-guide \xA75](" + flowDocsRel("flow-dir", "flow/node-code-guide.md") + "#5-excel-pythonexcel-\u5F00\u53D1--\u9ED8\u8BA4\u9996\u9009)",
4011
+ "- \u53D8\u91CF\u7EA6\u5B9A\u4E0E\u5B8C\u6574\u793A\u4F8B \u2192 [\u6D41\u7A0B\u53D8\u91CF\u7CFB\u7EDF\u6307\u5357](" + flowDocsRel("flow-dir", "flow/variables-guide.md") + ") \xB7 [\u8282\u70B9\u4EE3\u7801\u7F16\u5199\u6307\u5357](" + flowDocsRel("flow-dir", "flow/node-code-guide.md") + ")",
4012
+ "",
4013
+ ...buildDocsTable("flow-dir")
4014
+ ].join("\n");
4015
+ }
4016
+ exports2.buildFlowDirQuickStartMarkdown = buildFlowDirQuickStartMarkdown2;
4017
+ function buildFlowDirReadmeMarkdown(opts) {
4018
+ return buildFlowDirQuickStartMarkdown2(opts);
4019
+ }
4020
+ exports2.buildFlowDirReadmeMarkdown = buildFlowDirReadmeMarkdown;
4021
+ function removeLegacyFlowDirQuickStartFiles(flowDir, currentFilename, fsImpl) {
4022
+ const legacy = path_1.default.join(flowDir, exports2.LEGACY_FLOW_DIR_QUICKSTART_FILENAME);
4023
+ if (fsImpl.existsSync(legacy)) {
4024
+ try {
4025
+ if (isFlowScaffoldFile(fsImpl.readFileSync(legacy, "utf8"))) {
4026
+ fsImpl.unlinkSync(legacy);
4027
+ }
4028
+ } catch {
4029
+ }
4030
+ }
4031
+ for (const entry of fsImpl.readdirSync(flowDir)) {
4032
+ if (entry === currentFilename)
4033
+ continue;
4034
+ if (!/^快速启动_.+\.md$/.test(entry))
4035
+ continue;
4036
+ const full = path_1.default.join(flowDir, entry);
4037
+ try {
4038
+ if (isFlowScaffoldFile(fsImpl.readFileSync(full, "utf8"))) {
4039
+ fsImpl.unlinkSync(full);
4040
+ }
4041
+ } catch {
4042
+ }
4043
+ }
4044
+ }
4045
+ exports2.removeLegacyFlowDirQuickStartFiles = removeLegacyFlowDirQuickStartFiles;
4046
+ function writeFlowDirQuickStartScaffold2(flowDir, flowName, content, fsImpl) {
4047
+ const filename = flowDirQuickStartFilename(flowName);
4048
+ writeScaffoldIfAllowed2(path_1.default.join(flowDir, filename), content, fsImpl);
4049
+ removeLegacyFlowDirQuickStartFiles(flowDir, filename, fsImpl);
4050
+ return filename;
4051
+ }
4052
+ exports2.writeFlowDirQuickStartScaffold = writeFlowDirQuickStartScaffold2;
4053
+ function resolveFlowDirQuickStartPath(flowDir, fsImpl) {
4054
+ let flowName;
4055
+ const metaPath = path_1.default.join(flowDir, "flow.meta.json");
4056
+ if (fsImpl.existsSync(metaPath)) {
4057
+ try {
4058
+ const meta = JSON.parse(fsImpl.readFileSync(metaPath, "utf8"));
4059
+ flowName = meta.flowName?.trim() || void 0;
4060
+ } catch {
4061
+ }
4062
+ }
4063
+ if (!flowName)
4064
+ flowName = path_1.default.basename(flowDir);
4065
+ const preferred = path_1.default.join(flowDir, flowDirQuickStartFilename(flowName));
4066
+ if (fsImpl.existsSync(preferred))
4067
+ return preferred;
4068
+ const legacy = path_1.default.join(flowDir, exports2.LEGACY_FLOW_DIR_QUICKSTART_FILENAME);
4069
+ if (fsImpl.existsSync(legacy))
4070
+ return legacy;
4071
+ const candidates = fsImpl.readdirSync(flowDir).filter((f) => /^快速启动_.+\.md$/.test(f));
4072
+ if (!candidates.length)
4073
+ return null;
4074
+ if (candidates.length === 1)
4075
+ return path_1.default.join(flowDir, candidates[0]);
4076
+ if (fsImpl.statSync) {
4077
+ candidates.sort((a, b) => fsImpl.statSync(path_1.default.join(flowDir, b)).mtimeMs - fsImpl.statSync(path_1.default.join(flowDir, a)).mtimeMs);
4078
+ }
4079
+ return path_1.default.join(flowDir, candidates[0]);
4080
+ }
4081
+ exports2.resolveFlowDirQuickStartPath = resolveFlowDirQuickStartPath;
4082
+ function removeLegacyFlowRootQuickStart2(flowRoot, fsImpl) {
4083
+ const legacy = path_1.default.join(flowRoot, exports2.LEGACY_FLOW_ROOT_QUICKSTART_FILENAME);
4084
+ if (!fsImpl.existsSync(legacy))
3176
4085
  return;
4086
+ try {
4087
+ if (isFlowScaffoldFile(fsImpl.readFileSync(legacy, "utf8"))) {
4088
+ fsImpl.unlinkSync(legacy);
4089
+ }
4090
+ } catch {
3177
4091
  }
3178
- for (const f of flows) {
3179
- console.log(` ${String(f.id).padEnd(8)} ${(f.name ?? "").padEnd(40)} [${f.status ?? "\u2014"}]`);
4092
+ }
4093
+ exports2.removeLegacyFlowRootQuickStart = removeLegacyFlowRootQuickStart2;
4094
+ function resolveFlowRootFromFlowDir2(flowDir) {
4095
+ const flowsDir = path_1.default.dirname(flowDir);
4096
+ if (path_1.default.basename(flowsDir) === "flows") {
4097
+ return path_1.default.dirname(flowsDir);
3180
4098
  }
3181
- ok({ flows });
3182
- } catch (err) {
3183
- handleError(err);
4099
+ if (path_1.default.basename(flowsDir) === "\u6D41\u7A0B") {
4100
+ return flowsDir;
4101
+ }
4102
+ return void 0;
3184
4103
  }
3185
- });
3186
- cmd.command("get <flowId>").alias("show").description("\u67E5\u770B Flow \u8BE6\u60C5").option("--json", "\u8F93\u51FA JSON").action(async (flowId, opts) => {
3187
- try {
3188
- const flow = await apiRequest(`/api/data-pipelines/v1/flows/${flowId}`);
3189
- if (opts.json || true) console.log(JSON.stringify(flow, null, 2));
3190
- ok({ flow });
3191
- } catch (err) {
3192
- handleError(err);
4104
+ exports2.resolveFlowRootFromFlowDir = resolveFlowRootFromFlowDir2;
4105
+ function removeLegacyFlowDirScaffoldReadme2(flowDir, fsImpl) {
4106
+ const legacy = path_1.default.join(flowDir, "README.md");
4107
+ if (!fsImpl.existsSync(legacy))
4108
+ return;
4109
+ try {
4110
+ if (isFlowScaffoldFile(fsImpl.readFileSync(legacy, "utf8"))) {
4111
+ fsImpl.unlinkSync(legacy);
4112
+ }
4113
+ } catch {
4114
+ }
3193
4115
  }
3194
- });
3195
- cmd.command("create").alias("new").description("\u65B0\u5EFA Flow").requiredOption("--name <name>", "Flow \u540D\u79F0").option("--space <spaceId>", "\u7A7A\u95F4 ID").option("--description <desc>", "\u63CF\u8FF0").option("--file <path>", "\u4ECE\u672C\u5730 JSON \u6587\u4EF6\u8BFB\u53D6\u5B9A\u4E49").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
3196
- try {
3197
- let body;
3198
- if (opts.file) {
3199
- body = JSON.parse(import_fs2.default.readFileSync(import_path3.default.resolve(opts.file), "utf-8"));
3200
- } else {
3201
- body = { name: opts.name, spaceId: opts.space, description: opts.description };
4116
+ exports2.removeLegacyFlowDirScaffoldReadme = removeLegacyFlowDirScaffoldReadme2;
4117
+ function buildFlowProjectReadmeHelpSection() {
4118
+ return [
4119
+ "",
4120
+ "## \u5FEB\u901F\u5165\u95E8",
4121
+ "",
4122
+ "- \u9605\u8BFB\u672C\u76EE\u5F55 [README.md](./README.md)",
4123
+ "- \u6D41\u7A0B\u4E13\u9898\u6587\u6863\uFF1A[\u8D44\u6E90/docs/flow/flows-guide.md](" + flowDocsRel("project-root", "flow/flows-guide.md") + ")",
4124
+ ""
4125
+ ];
4126
+ }
4127
+ exports2.buildFlowProjectReadmeHelpSection = buildFlowProjectReadmeHelpSection;
4128
+ function writeScaffoldIfAllowed2(filePath, content, fsImpl, opts) {
4129
+ if (fsImpl.existsSync(filePath)) {
4130
+ const existing = fsImpl.readFileSync(filePath, "utf8");
4131
+ if (!isFlowScaffoldFile(existing)) {
4132
+ if (!(opts?.upgradeLegacy && isLegacyFlowScaffoldFile(existing)))
4133
+ return false;
4134
+ }
3202
4135
  }
3203
- const flow = await apiRequest("/api/data-pipelines/v1/flows", { method: "POST", body });
3204
- if (opts.json) {
3205
- console.log(JSON.stringify(flow, null, 2));
3206
- } else {
3207
- console.log(`\u2705 \u5DF2\u521B\u5EFA Flow: ${flow.id} ${flow.name}`);
4136
+ fsImpl.writeFileSync(filePath, content, "utf8");
4137
+ return true;
4138
+ }
4139
+ exports2.writeScaffoldIfAllowed = writeScaffoldIfAllowed2;
4140
+ }
4141
+ });
4142
+
4143
+ // src/shared/flowWorkspaceAudit.js
4144
+ var require_flowWorkspaceAudit = __commonJS({
4145
+ "src/shared/flowWorkspaceAudit.js"(exports2) {
4146
+ "use strict";
4147
+ var __importDefault = exports2 && exports2.__importDefault || function(mod) {
4148
+ return mod && mod.__esModule ? mod : { "default": mod };
4149
+ };
4150
+ Object.defineProperty(exports2, "__esModule", { value: true });
4151
+ exports2.buildFlowConsistencyMarkdown = exports2.repairFlowMeta = exports2.auditFlowWorkspace = void 0;
4152
+ var crypto_1 = __importDefault(require("crypto"));
4153
+ var fs_1 = __importDefault(require("fs"));
4154
+ var path_1 = __importDefault(require("path"));
4155
+ var NODES_DIR3 = "\u8282\u70B9";
4156
+ var FLOW_JSON3 = "flow.json";
4157
+ var FLOW_META_JSON3 = "flow.meta.json";
4158
+ var NODE_INFO_JSON3 = "node.info.json";
4159
+ var NODE_TYPE_CODE_DATA_KEY3 = {
4160
+ "database-source": "sql",
4161
+ "dataspace-source": "sql",
4162
+ "python-script": "pythonCode",
4163
+ "excel-python": "pythonCode",
4164
+ "sql-query": "sql",
4165
+ condition: "pythonCode",
4166
+ "data-quality-check": "dqPythonCode"
4167
+ };
4168
+ function sha13(text) {
4169
+ return crypto_1.default.createHash("sha1").update(text, "utf8").digest("hex");
4170
+ }
4171
+ function resolveNodeType3(node) {
4172
+ if (node.type === "custom") {
4173
+ return String(node.data?.type ?? "custom");
3208
4174
  }
3209
- ok({ flow });
3210
- } catch (err) {
3211
- handleError(err);
4175
+ return String(node.type ?? "custom");
3212
4176
  }
3213
- });
3214
- cmd.command("validate <flowId>").description("\u9A8C\u8BC1 Flow \u5B9A\u4E49").option("--file <path>", "\u4ECE\u672C\u5730\u5FEB\u7167\u6587\u4EF6\u9A8C\u8BC1\uFF08\u79BB\u7EBF\uFF09").action(async (flowId, opts) => {
3215
- try {
3216
- let result;
3217
- if (opts.file) {
3218
- const body = JSON.parse(import_fs2.default.readFileSync(import_path3.default.resolve(opts.file), "utf-8"));
3219
- result = await apiRequest(`/api/data-pipelines/v1/flows/compile-plan`, { method: "POST", body });
3220
- } else {
3221
- result = await apiRequest(`/api/data-pipelines/v1/flows/${flowId}/validate`, { method: "POST" });
4177
+ function codeLanguageForNodeType3(nodeType) {
4178
+ if (["python-script", "excel-python", "condition", "data-quality-check"].includes(nodeType)) {
4179
+ return "python";
3222
4180
  }
3223
- console.log("\u2705 \u9A8C\u8BC1\u901A\u8FC7");
3224
- ok({ flowId, result });
3225
- } catch (err) {
3226
- handleError(err);
4181
+ if (["database-source", "dataspace-source", "sql-query"].includes(nodeType)) {
4182
+ return "sql";
4183
+ }
4184
+ return void 0;
3227
4185
  }
3228
- });
3229
- cmd.command("patch <flowId>").description("\u90E8\u5206\u66F4\u65B0 Flow \u5C5E\u6027").option("--name <name>", "\u65B0\u540D\u79F0").option("--description <desc>", "\u65B0\u63CF\u8FF0").option("--status <status>", "\u65B0\u72B6\u6001").option("--file <path>", "\u4ECE JSON \u6587\u4EF6\u8BFB\u53D6 patch body").action(async (flowId, opts) => {
3230
- try {
3231
- let patch;
3232
- if (opts.file) {
3233
- patch = JSON.parse(import_fs2.default.readFileSync(import_path3.default.resolve(opts.file), "utf-8"));
3234
- } else {
3235
- patch = {};
3236
- if (opts.name) patch.name = opts.name;
3237
- if (opts.description) patch.description = opts.description;
3238
- if (opts.status) patch.status = opts.status;
4186
+ function codeFileExt3(language) {
4187
+ if (language === "python")
4188
+ return "py";
4189
+ if (language === "sql")
4190
+ return "sql";
4191
+ return "txt";
4192
+ }
4193
+ function sanitizeName3(name) {
4194
+ const cleaned = (name || "").replace(/[<>:"/\\|?*\x00-\x1f]/g, "_").trim().replace(/\s+/g, "_");
4195
+ return cleaned || "node";
4196
+ }
4197
+ function readFlowJson3(flowDir) {
4198
+ const raw = JSON.parse(fs_1.default.readFileSync(path_1.default.join(flowDir, FLOW_JSON3), "utf8"));
4199
+ return {
4200
+ nodes: Array.isArray(raw.nodes) ? raw.nodes : [],
4201
+ edges: Array.isArray(raw.edges) ? raw.edges : []
4202
+ };
4203
+ }
4204
+ function readMeta3(flowDir) {
4205
+ const p = path_1.default.join(flowDir, FLOW_META_JSON3);
4206
+ if (!fs_1.default.existsSync(p))
4207
+ return void 0;
4208
+ try {
4209
+ return JSON.parse(fs_1.default.readFileSync(p, "utf8"));
4210
+ } catch {
4211
+ return void 0;
3239
4212
  }
3240
- const flow = await apiRequest(`/api/data-pipelines/v1/flows/${flowId}`, { method: "PUT", body: patch });
3241
- console.log(`\u2705 Flow ${flowId} \u5DF2\u66F4\u65B0`);
3242
- ok({ flow });
3243
- } catch (err) {
3244
- handleError(err);
3245
4213
  }
3246
- });
3247
- const nodeCmd = new Command("flow-node").description("Flow \u8282\u70B9\u7BA1\u7406");
3248
- nodeCmd.command("get <flowId> <nodeId>").description("\u67E5\u770B Flow \u8282\u70B9\u8BE6\u60C5").option("--json", "\u8F93\u51FA JSON").action(async (flowId, nodeId, opts) => {
3249
- try {
3250
- const node = await apiRequest(`/api/data-pipelines/v1/flows/${flowId}/flow-nodes/${nodeId}`);
3251
- console.log(JSON.stringify(node, null, 2));
3252
- ok({ node });
3253
- } catch (err) {
3254
- handleError(err);
4214
+ function isCodeNodeType2(nodeType) {
4215
+ return Boolean(NODE_TYPE_CODE_DATA_KEY3[nodeType]);
3255
4216
  }
3256
- });
3257
- nodeCmd.command("patch <flowId> <nodeId>").description("\u90E8\u5206\u66F4\u65B0 Flow \u8282\u70B9\u914D\u7F6E").option("--file <path>", "\u4ECE JSON \u6587\u4EF6\u8BFB\u53D6 patch body").option("--config <json>", "config patch JSON \u5B57\u7B26\u4E32").action(async (flowId, nodeId, opts) => {
3258
- try {
3259
- let patch;
3260
- if (opts.file) {
3261
- patch = JSON.parse(import_fs2.default.readFileSync(import_path3.default.resolve(opts.file), "utf-8"));
3262
- } else if (opts.config) {
3263
- patch = { config: JSON.parse(opts.config) };
3264
- } else {
3265
- console.error("--file \u6216 --config \u5FC5\u987B\u63D0\u4F9B");
3266
- process.exit(1);
4217
+ function listNodeDirs2(flowDir) {
4218
+ const nodesRoot = path_1.default.join(flowDir, NODES_DIR3);
4219
+ if (!fs_1.default.existsSync(nodesRoot))
4220
+ return [];
4221
+ return fs_1.default.readdirSync(nodesRoot).filter((n) => {
4222
+ try {
4223
+ return fs_1.default.statSync(path_1.default.join(nodesRoot, n)).isDirectory();
4224
+ } catch {
4225
+ return false;
4226
+ }
4227
+ });
4228
+ }
4229
+ function findCodeFileInDir2(dirAbs) {
4230
+ if (!fs_1.default.existsSync(dirAbs))
4231
+ return void 0;
4232
+ for (const f of fs_1.default.readdirSync(dirAbs)) {
4233
+ if (/^code\.(py|sql|txt)$/i.test(f))
4234
+ return f;
4235
+ }
4236
+ return void 0;
4237
+ }
4238
+ function findDirForUuid2(flowDir, uuid) {
4239
+ const nodesRoot = path_1.default.join(flowDir, NODES_DIR3);
4240
+ for (const dirName of listNodeDirs2(flowDir)) {
4241
+ const infoPath = path_1.default.join(nodesRoot, dirName, NODE_INFO_JSON3);
4242
+ if (fs_1.default.existsSync(infoPath)) {
4243
+ try {
4244
+ const info = JSON.parse(fs_1.default.readFileSync(infoPath, "utf8"));
4245
+ if (String(info.node_uuid ?? "") === uuid)
4246
+ return path_1.default.posix.join(NODES_DIR3, dirName);
4247
+ } catch {
4248
+ }
4249
+ }
3267
4250
  }
3268
- const node = await apiRequest(
3269
- `/api/data-pipelines/v1/flows/${flowId}/flow-nodes/${nodeId}`,
3270
- { method: "PATCH", body: patch }
3271
- );
3272
- console.log(`\u2705 \u8282\u70B9 ${nodeId} \u5DF2\u66F4\u65B0`);
3273
- ok({ node });
3274
- } catch (err) {
3275
- handleError(err);
4251
+ return void 0;
3276
4252
  }
3277
- });
3278
- cmd.addCommand(nodeCmd);
3279
- cmd.command("workspace-init <flowId>").description("\u521D\u59CB\u5316 Flow \u672C\u5730\u5DE5\u4F5C\u76EE\u5F55 flows/<flowId>/").action(async (flowId) => {
3280
- try {
3281
- const ws = resolveWorkspace();
3282
- const flowDir = import_path3.default.join(ws.flows, flowId);
3283
- const dirs = [flowDir, import_path3.default.join(flowDir, "plans"), import_path3.default.join(flowDir, "data")];
3284
- for (const d of dirs) {
3285
- if (!import_fs2.default.existsSync(d)) import_fs2.default.mkdirSync(d, { recursive: true });
4253
+ function auditFlowWorkspace3(flowDir) {
4254
+ const warnings = [];
4255
+ const doc = readFlowJson3(flowDir);
4256
+ const meta = readMeta3(flowDir) ?? { nodes: {} };
4257
+ const canvasNodeCount = doc.nodes.length;
4258
+ let canvasCodeNodeCount = 0;
4259
+ const missingInMeta = [];
4260
+ const missingCodeFiles = [];
4261
+ const flowUuids = /* @__PURE__ */ new Set();
4262
+ for (const node of doc.nodes) {
4263
+ const uuid = String(node.node_uuid ?? "").trim();
4264
+ if (!uuid)
4265
+ continue;
4266
+ flowUuids.add(uuid);
4267
+ const nodeType = resolveNodeType3(node);
4268
+ if (!isCodeNodeType2(nodeType))
4269
+ continue;
4270
+ canvasCodeNodeCount++;
4271
+ const label = String(node.data?.label ?? node.id ?? "");
4272
+ const m = meta.nodes[uuid];
4273
+ if (!m?.dir || !m.codeFile) {
4274
+ missingInMeta.push({ uuid, nodeId: String(node.id ?? ""), nodeType, label });
4275
+ continue;
4276
+ }
4277
+ const codePath = path_1.default.join(flowDir, m.dir, m.codeFile);
4278
+ if (!fs_1.default.existsSync(codePath)) {
4279
+ missingCodeFiles.push({ uuid, dir: m.dir, codeFile: m.codeFile });
4280
+ }
3286
4281
  }
3287
- const metaPath = import_path3.default.join(flowDir, ".dazi-flow.json");
3288
- if (!import_fs2.default.existsSync(metaPath)) {
3289
- import_fs2.default.writeFileSync(metaPath, JSON.stringify({ flowId, initializedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), "utf-8");
4282
+ const orphanInMeta = Object.keys(meta.nodes).filter((u) => !flowUuids.has(u));
4283
+ let metaCodeIndexedCount = 0;
4284
+ for (const m of Object.values(meta.nodes)) {
4285
+ if (m.dir && m.codeFile)
4286
+ metaCodeIndexedCount++;
3290
4287
  }
3291
- console.log(`\u2705 Flow \u5DE5\u4F5C\u76EE\u5F55\u5DF2\u521D\u59CB\u5316: ${flowDir}`);
3292
- ok({ flowId, dir: flowDir });
3293
- } catch (err) {
3294
- handleError(err);
4288
+ const unindexedDirs = [];
4289
+ for (const dirName of listNodeDirs2(flowDir)) {
4290
+ const dirAbs = path_1.default.join(flowDir, NODES_DIR3, dirName);
4291
+ if (!findCodeFileInDir2(dirAbs))
4292
+ continue;
4293
+ const rel = path_1.default.posix.join(NODES_DIR3, dirName);
4294
+ const indexed = Object.values(meta.nodes).some((m) => m.dir?.replace(/\\/g, "/") === rel);
4295
+ if (!indexed)
4296
+ unindexedDirs.push(rel);
4297
+ }
4298
+ if (canvasCodeNodeCount !== metaCodeIndexedCount) {
4299
+ warnings.push(`\u753B\u5E03\u4EE3\u7801\u8282\u70B9 ${canvasCodeNodeCount} \u4E2A\uFF0Cmeta \u5DF2\u7D22\u5F15 ${metaCodeIndexedCount} \u4E2A\uFF08\u4E0D\u4E00\u81F4\u4F1A\u5BFC\u81F4 node push / \u8BBE\u8BA1\u5668\u6253\u5F00\u4EE3\u7801\u5931\u8D25\uFF09`);
4300
+ }
4301
+ if (missingInMeta.length) {
4302
+ warnings.push("\u90E8\u5206 flow.json \u4EE3\u7801\u8282\u70B9\u672A\u5199\u5165 flow.meta.json\uFF0C\u8BF7\u6267\u884C project repair-meta");
4303
+ }
4304
+ if (unindexedDirs.length) {
4305
+ warnings.push(`\u5B58\u5728\u672A\u7D22\u5F15\u7684\u8282\u70B9\u76EE\u5F55\uFF1A${unindexedDirs.join(", ")}`);
4306
+ }
4307
+ const ok3 = missingInMeta.length === 0 && orphanInMeta.length === 0 && missingCodeFiles.length === 0 && unindexedDirs.length === 0 && canvasCodeNodeCount === metaCodeIndexedCount;
4308
+ return {
4309
+ ok: ok3,
4310
+ canvasNodeCount,
4311
+ canvasCodeNodeCount,
4312
+ metaIndexedCount: Object.keys(meta.nodes).length,
4313
+ metaCodeIndexedCount,
4314
+ missingInMeta,
4315
+ orphanInMeta,
4316
+ missingCodeFiles,
4317
+ unindexedDirs,
4318
+ warnings
4319
+ };
3295
4320
  }
3296
- });
3297
- return cmd;
3298
- }
3299
-
3300
- // cli/dazi-flow/src/commands/snapshot.ts
3301
- var import_path6 = __toESM(require("path"), 1);
3302
- var import_fs5 = __toESM(require("fs"), 1);
3303
-
3304
- // cli/dazi-flow/src/lib/flowLocal.ts
3305
- var import_crypto = __toESM(require("crypto"), 1);
3306
- var import_fs3 = __toESM(require("fs"), 1);
3307
- var import_path4 = __toESM(require("path"), 1);
3308
- var NODE_TYPE_CODE_DATA_KEY = {
3309
- "database-source": "sql",
3310
- "dataspace-source": "sql",
3311
- "python-script": "pythonCode",
3312
- "excel-python": "pythonCode",
3313
- "sql-query": "sql",
3314
- condition: "pythonCode",
3315
- "data-quality-check": "dqPythonCode"
3316
- };
3317
- function codeLanguageForNodeType(nodeType) {
3318
- if (["python-script", "excel-python", "condition", "data-quality-check"].includes(nodeType)) {
3319
- return "python";
3320
- }
3321
- if (["database-source", "dataspace-source", "sql-query"].includes(nodeType)) {
3322
- return "sql";
3323
- }
3324
- return void 0;
3325
- }
3326
- function codeFileExt(language) {
3327
- if (language === "python") return "py";
3328
- if (language === "sql") return "sql";
3329
- return "txt";
3330
- }
3331
- var NODES_DIR = "\u8282\u70B9";
3332
- var VARIABLES_DIR = "\u53D8\u91CF";
3333
- var RUN_DIR = "_run";
3334
- var FLOW_JSON = "flow.json";
3335
- var FLOW_META_JSON = "flow.meta.json";
3336
- var NODE_INFO_JSON = "node.info.json";
3337
- function resolveNodeType(node) {
3338
- const nt = node.type;
3339
- if (nt === "custom") {
3340
- const data = node.data ?? {};
3341
- return String(data.type ?? "custom");
3342
- }
3343
- return String(nt ?? "custom");
3344
- }
3345
- function sha1(text) {
3346
- return import_crypto.default.createHash("sha1").update(text, "utf8").digest("hex");
3347
- }
3348
- function sanitizeName(name) {
3349
- const cleaned = (name || "").replace(/[<>:"/\\|?*\x00-\x1f]/g, "_").trim().replace(/\s+/g, "_");
3350
- return cleaned || "node";
3351
- }
3352
- function readMeta(flowDir) {
3353
- const p = import_path4.default.join(flowDir, FLOW_META_JSON);
3354
- if (!import_fs3.default.existsSync(p)) return void 0;
3355
- try {
3356
- return JSON.parse(import_fs3.default.readFileSync(p, "utf8"));
3357
- } catch {
3358
- return void 0;
3359
- }
3360
- }
3361
- function writeMeta(flowDir, meta) {
3362
- import_fs3.default.writeFileSync(import_path4.default.join(flowDir, FLOW_META_JSON), JSON.stringify(meta, null, 2), "utf8");
3363
- }
3364
- function extractDocument(snapshot) {
3365
- const doc = snapshot.document ?? snapshot;
3366
- const nodes = Array.isArray(doc.nodes) ? doc.nodes : [];
3367
- const edges = Array.isArray(doc.edges) ? doc.edges : [];
3368
- return { nodes, edges };
3369
- }
3370
- function splitDocumentToFiles(flowDir, doc) {
3371
- const nodesDir = import_path4.default.join(flowDir, NODES_DIR);
3372
- const strippedNodes = [];
3373
- const nodeMetas = {};
3374
- const usedDirNames = /* @__PURE__ */ new Set();
3375
- for (const node of doc.nodes) {
3376
- if (!node || typeof node !== "object") continue;
3377
- const nodeUuid = String(node.node_uuid ?? "").trim();
3378
- const nodeType = resolveNodeType(node);
3379
- const semanticId = String(node.id ?? "");
3380
- const codeKey = NODE_TYPE_CODE_DATA_KEY[nodeType];
3381
- const stripped = JSON.parse(JSON.stringify(node));
3382
- const meta = { nodeId: semanticId, nodeType };
3383
- if (codeKey && nodeUuid) {
3384
- const data = stripped.data ?? {};
3385
- const rawCode = data[codeKey];
3386
- const codeBody = typeof rawCode === "string" ? rawCode : rawCode != null ? JSON.stringify(rawCode) : "";
3387
- delete data[codeKey];
3388
- stripped.data = data;
3389
- const language = codeLanguageForNodeType(nodeType);
3390
- const ext = codeFileExt(language);
3391
- const label = String(node.data?.label ?? "") || semanticId || nodeType;
3392
- let dirName = sanitizeName(label);
3393
- if (usedDirNames.has(dirName)) {
3394
- dirName = `${dirName}_${nodeUuid.slice(0, 6)}`;
3395
- }
3396
- usedDirNames.add(dirName);
3397
- const nodeDirAbs = import_path4.default.join(nodesDir, dirName);
3398
- import_fs3.default.mkdirSync(nodeDirAbs, { recursive: true });
3399
- const codeFile = `code.${ext}`;
3400
- import_fs3.default.writeFileSync(import_path4.default.join(nodeDirAbs, codeFile), codeBody, "utf8");
3401
- import_fs3.default.writeFileSync(
3402
- import_path4.default.join(nodeDirAbs, NODE_INFO_JSON),
3403
- JSON.stringify(
3404
- {
3405
- _readonly: "\u6B64\u6587\u4EF6\u7531 dazi-flow \u751F\u6210\uFF0C\u4EC5\u4F9B\u67E5\u770B\uFF1B\u7F16\u8F91\u4EE3\u7801\u8BF7\u6539 " + codeFile + "\uFF0C\u914D\u7F6E\u6539 flow.json",
3406
- node_uuid: nodeUuid,
3407
- nodeId: semanticId,
3408
- nodeType,
3409
- label,
3410
- codeFile,
3411
- codeLanguage: language,
3412
- position: node.position,
3413
- data: stripped.data
3414
- },
3415
- null,
3416
- 2
3417
- ),
3418
- "utf8"
3419
- );
3420
- meta.dir = import_path4.default.posix.join(NODES_DIR, dirName);
3421
- meta.codeFile = codeFile;
3422
- meta.codeLanguage = language;
3423
- meta.codeHash = sha1(codeBody);
4321
+ exports2.auditFlowWorkspace = auditFlowWorkspace3;
4322
+ function writeNodeInfo2(flowDir, node, uuid, nodeType, relDir, codeFile, language, strippedData) {
4323
+ const abs = path_1.default.join(flowDir, relDir);
4324
+ fs_1.default.writeFileSync(path_1.default.join(abs, NODE_INFO_JSON3), JSON.stringify({
4325
+ _readonly: `\u6B64\u6587\u4EF6\u7531 dazi-flow \u751F\u6210\uFF0C\u4EC5\u4F9B\u67E5\u770B\uFF1B\u7F16\u8F91\u4EE3\u7801\u8BF7\u6539 ${codeFile}\uFF0C\u914D\u7F6E\u6539 flow.json`,
4326
+ node_uuid: uuid,
4327
+ nodeId: String(node.id ?? ""),
4328
+ nodeType,
4329
+ label: String(node.data?.label ?? ""),
4330
+ codeFile,
4331
+ codeLanguage: language,
4332
+ position: node.position,
4333
+ data: strippedData
4334
+ }, null, 2), "utf8");
4335
+ }
4336
+ function repairFlowMeta3(flowDir) {
4337
+ const warnings = [];
4338
+ const repairedUuids = [];
4339
+ const wroteNodeInfo = [];
4340
+ const doc = readFlowJson3(flowDir);
4341
+ const meta = readMeta3(flowDir) ?? { flowId: null, flowName: path_1.default.basename(flowDir), nodes: {} };
4342
+ if (!meta.nodes)
4343
+ meta.nodes = {};
4344
+ const usedDirNames = new Set(Object.values(meta.nodes).map((m) => m.dir?.split("/").pop()).filter(Boolean));
4345
+ for (const node of doc.nodes) {
4346
+ const uuid = String(node.node_uuid ?? "").trim();
4347
+ if (!uuid)
4348
+ continue;
4349
+ const nodeType = resolveNodeType3(node);
4350
+ const codeKey = NODE_TYPE_CODE_DATA_KEY3[nodeType];
4351
+ const semanticId = String(node.id ?? "");
4352
+ const label = String(node.data?.label ?? semanticId ?? nodeType);
4353
+ let entry = meta.nodes[uuid];
4354
+ if (!entry) {
4355
+ entry = { nodeId: semanticId, nodeType };
4356
+ meta.nodes[uuid] = entry;
4357
+ repairedUuids.push(uuid);
4358
+ } else {
4359
+ entry.nodeId = semanticId || entry.nodeId;
4360
+ entry.nodeType = nodeType;
4361
+ }
4362
+ if (!codeKey)
4363
+ continue;
4364
+ let relDir = entry.dir?.replace(/\\/g, "/");
4365
+ if (relDir && findCodeFileInDir2(path_1.default.join(flowDir, relDir))) {
4366
+ } else {
4367
+ relDir = findDirForUuid2(flowDir, uuid);
4368
+ }
4369
+ if (!relDir) {
4370
+ const byLabel = path_1.default.posix.join(NODES_DIR3, sanitizeName3(label));
4371
+ if (fs_1.default.existsSync(path_1.default.join(flowDir, byLabel)) && findCodeFileInDir2(path_1.default.join(flowDir, byLabel))) {
4372
+ relDir = byLabel;
4373
+ }
4374
+ }
4375
+ if (!relDir) {
4376
+ let dirName = sanitizeName3(label);
4377
+ if (usedDirNames.has(dirName))
4378
+ dirName = `${dirName}_${uuid.slice(0, 6)}`;
4379
+ usedDirNames.add(dirName);
4380
+ relDir = path_1.default.posix.join(NODES_DIR3, dirName);
4381
+ fs_1.default.mkdirSync(path_1.default.join(flowDir, relDir), { recursive: true });
4382
+ const lang = codeLanguageForNodeType3(nodeType);
4383
+ const codeFile2 = `code.${codeFileExt3(lang)}`;
4384
+ const template = nodeType === "excel-python" ? "# -*- coding: utf-8 -*-\n# Excel \u8282\u70B9\uFF1A\u753B\u5E03\u914D\u7F6E managed_file_id\uFF1B\u4EE3\u7801\u4F7F\u7528 excel_source_path\n" : lang === "sql" ? "-- \u8282\u70B9 SQL\nSELECT 1;\n" : "# -*- coding: utf-8 -*-\n";
4385
+ if (!findCodeFileInDir2(path_1.default.join(flowDir, relDir))) {
4386
+ fs_1.default.writeFileSync(path_1.default.join(flowDir, relDir, codeFile2), template, "utf8");
4387
+ warnings.push(`\u5DF2\u4E3A ${uuid} \u521B\u5EFA\u7A7A\u4EE3\u7801\u6A21\u677F ${relDir}/${codeFile2}`);
4388
+ }
4389
+ }
4390
+ const dirAbs = path_1.default.join(flowDir, relDir);
4391
+ const codeFile = findCodeFileInDir2(dirAbs);
4392
+ if (!codeFile) {
4393
+ warnings.push(`\u8282\u70B9 ${uuid} \u76EE\u5F55 ${relDir} \u65E0 code.*\uFF0C\u8DF3\u8FC7\u4EE3\u7801\u7D22\u5F15`);
4394
+ continue;
4395
+ }
4396
+ const language = codeLanguageForNodeType3(nodeType);
4397
+ const code = fs_1.default.readFileSync(path_1.default.join(dirAbs, codeFile), "utf8");
4398
+ entry.dir = relDir;
4399
+ entry.codeFile = codeFile;
4400
+ entry.codeLanguage = language;
4401
+ entry.codeHash = sha13(code);
4402
+ const strippedData = { ...node.data ?? {} };
4403
+ delete strippedData[codeKey];
4404
+ const infoPath = path_1.default.join(dirAbs, NODE_INFO_JSON3);
4405
+ if (!fs_1.default.existsSync(infoPath)) {
4406
+ writeNodeInfo2(flowDir, node, uuid, nodeType, relDir, codeFile, language, strippedData);
4407
+ wroteNodeInfo.push(relDir);
4408
+ }
4409
+ if (!repairedUuids.includes(uuid))
4410
+ repairedUuids.push(uuid);
4411
+ }
4412
+ meta.graphFingerprint = "sha1:" + sha13(JSON.stringify({ nodes: doc.nodes, edges: doc.edges }));
4413
+ fs_1.default.writeFileSync(path_1.default.join(flowDir, FLOW_META_JSON3), JSON.stringify(meta, null, 2), "utf8");
4414
+ const okAfter = auditFlowWorkspace3(flowDir).ok;
4415
+ return {
4416
+ ok: okAfter,
4417
+ repairedUuids: [...new Set(repairedUuids)],
4418
+ wroteNodeInfo,
4419
+ warnings
4420
+ };
3424
4421
  }
3425
- if (nodeUuid) {
3426
- nodeMetas[nodeUuid] = meta;
4422
+ exports2.repairFlowMeta = repairFlowMeta3;
4423
+ function buildFlowConsistencyMarkdown2(audit, localFilesSpecLink = "../../\u8D44\u6E90/docs/flow/local-files-spec.md") {
4424
+ const lines = [
4425
+ "## \u672C\u5730\u6587\u4EF6\u4E00\u81F4\u6027",
4426
+ "",
4427
+ "| \u6307\u6807 | \u6570\u91CF |",
4428
+ "|------|------|",
4429
+ `| \u753B\u5E03\u8282\u70B9\uFF08flow.json\uFF09 | ${audit.canvasNodeCount} |`,
4430
+ `| \u753B\u5E03\u4EE3\u7801\u8282\u70B9\uFF08flow.json\uFF09 | ${audit.canvasCodeNodeCount} |`,
4431
+ `| meta \u5DF2\u767B\u8BB0\u8282\u70B9 | ${audit.metaIndexedCount} |`,
4432
+ `| meta \u5DF2\u7D22\u5F15\u4EE3\u7801\uFF08dir+codeFile\uFF09 | ${audit.metaCodeIndexedCount} |`,
4433
+ ""
4434
+ ];
4435
+ if (audit.ok) {
4436
+ lines.push("\u2705 **flow.json / flow.meta.json / \u8282\u70B9/ \u4E00\u81F4**\u3002\u8BBE\u8BA1\u5668\u53EF\u6B63\u5E38\u300C\u6253\u5F00\u4EE3\u7801\u300D\uFF0C`node push` \u53EF\u8BC6\u522B\u810F\u8282\u70B9\u3002", "");
4437
+ } else {
4438
+ lines.push("\u26A0\uFE0F **\u76EE\u5F55\u4E0D\u4E00\u81F4**\uFF08\u4F1A\u5BFC\u81F4\u8BBE\u8BA1\u5668\u627E\u4E0D\u5230\u4EE3\u7801\u3001\u6216 `node push` \u8DF3\u8FC7\uFF09\uFF1A", "");
4439
+ if (audit.missingInMeta.length) {
4440
+ lines.push("| uuid | \u8282\u70B9 | \u7C7B\u578B |");
4441
+ lines.push("|------|------|------|");
4442
+ for (const m of audit.missingInMeta) {
4443
+ lines.push(`| \`${m.uuid.slice(0, 8)}\u2026\` | ${m.label || m.nodeId} | ${m.nodeType} |`);
4444
+ }
4445
+ lines.push("");
4446
+ }
4447
+ if (audit.unindexedDirs.length) {
4448
+ lines.push(`- \u672A\u7D22\u5F15\u76EE\u5F55\uFF1A${audit.unindexedDirs.map((d) => `\`${d}\``).join(" ")}`);
4449
+ }
4450
+ if (audit.orphanInMeta.length) {
4451
+ lines.push(`- meta \u591A\u4F59 uuid\uFF1A${audit.orphanInMeta.length} \u4E2A\uFF08flow.json \u4E2D\u5DF2\u4E0D\u5B58\u5728\uFF09`);
4452
+ }
4453
+ for (const w of audit.warnings)
4454
+ lines.push(`- ${w}`);
4455
+ lines.push("", "\u4FEE\u590D\uFF08\u5728\u6D41\u7A0B\u76EE\u5F55\uFF09\uFF1A", "", "```powershell", "dazi flow project repair-meta --dir .", "dazi flow project doctor --dir .", "```", "", `\u8BE6\u89C1 [\u6D41\u7A0B\u672C\u5730\u6587\u4EF6\u89C4\u8303](${localFilesSpecLink})\u3002`, "");
4456
+ }
4457
+ lines.push("## \u53D8\u66F4\u63D0\u4EA4\u547D\u4EE4\u77E9\u9635", "", "| \u6539\u4E86\u4EC0\u4E48 | \u547D\u4EE4 |", "|----------|------|", "| `managed_file_id`\u3001`output_variable_name`\u3001\u8FDE\u7EBF\u3001\u589E\u5220\u8282\u70B9 | `dazi flow project push --dir . --canvas` |", "| `\u8282\u70B9/<\u540D>/code.py` \u6216 `code.sql` | `dazi flow node push --node <uuid> --dir .` |", '| **\u65B0\u589E\u4EE3\u7801\u8282\u70B9** | `dazi flow node new --type <type> --dir . --label "<\u540D>"` \u2192 \u6539\u914D\u7F6E \u2192 `push --canvas` \u2192 \u5199 code \u2192 `node push` |', "", "> \u7981\u6B62\u53EA\u6539 `flow.json` \u800C\u4E0D\u66F4\u65B0 `flow.meta.json`\uFF08\u987B `node new` / `pull` / `repair-meta`\uFF09\u3002", "");
4458
+ return lines;
3427
4459
  }
3428
- strippedNodes.push(stripped);
4460
+ exports2.buildFlowConsistencyMarkdown = buildFlowConsistencyMarkdown2;
3429
4461
  }
3430
- const strippedDoc = { nodes: strippedNodes, edges: doc.edges };
3431
- import_fs3.default.writeFileSync(import_path4.default.join(flowDir, FLOW_JSON), JSON.stringify(strippedDoc, null, 2), "utf8");
3432
- return { strippedDoc, nodeMetas };
3433
- }
3434
- function fingerprintDoc(doc) {
3435
- return "sha1:" + sha1(JSON.stringify({ nodes: doc.nodes, edges: doc.edges }));
3436
- }
3437
- function readFlowJson(flowDir) {
3438
- const p = import_path4.default.join(flowDir, FLOW_JSON);
3439
- const raw = JSON.parse(import_fs3.default.readFileSync(p, "utf8"));
3440
- return {
3441
- nodes: Array.isArray(raw.nodes) ? raw.nodes : [],
3442
- edges: Array.isArray(raw.edges) ? raw.edges : []
3443
- };
3444
- }
3445
- function readNodeCode(flowDir, meta) {
3446
- if (!meta.dir || !meta.codeFile) return void 0;
3447
- const p = import_path4.default.join(flowDir, meta.dir, meta.codeFile);
3448
- if (!import_fs3.default.existsSync(p)) return void 0;
3449
- return import_fs3.default.readFileSync(p, "utf8");
3450
- }
3451
- function assembleFullDocument(flowDir, meta) {
3452
- const doc = readFlowJson(flowDir);
3453
- for (const node of doc.nodes) {
3454
- const uuid = String(node.node_uuid ?? "");
3455
- const nodeMeta = meta.nodes[uuid];
3456
- if (!nodeMeta) continue;
3457
- const codeKey = NODE_TYPE_CODE_DATA_KEY[nodeMeta.nodeType];
3458
- if (!codeKey) continue;
3459
- const code = readNodeCode(flowDir, nodeMeta);
3460
- if (code != null) {
3461
- node.data = { ...node.data ?? {}, [codeKey]: code };
4462
+ });
4463
+
4464
+ // src/shared/flowDirResolve.js
4465
+ var require_flowDirResolve = __commonJS({
4466
+ "src/shared/flowDirResolve.js"(exports2) {
4467
+ "use strict";
4468
+ var __importDefault = exports2 && exports2.__importDefault || function(mod) {
4469
+ return mod && mod.__esModule ? mod : { "default": mod };
4470
+ };
4471
+ Object.defineProperty(exports2, "__esModule", { value: true });
4472
+ exports2.resolveFlowDir = exports2.resolveFlowDirForCli = exports2.findFlowDirForPath = exports2.inferBusinessProjectFolder = exports2.FLOW_META_JSON = exports2.FLOW_JSON = void 0;
4473
+ var fs_1 = __importDefault(require("fs"));
4474
+ var path_1 = __importDefault(require("path"));
4475
+ exports2.FLOW_JSON = "flow.json";
4476
+ exports2.FLOW_META_JSON = "flow.meta.json";
4477
+ function inferBusinessProjectFolder2(flowDir) {
4478
+ const norm = path_1.default.resolve(flowDir).replace(/\\/g, "/");
4479
+ let m = norm.match(/(?:^|\/)项目\/([^/]+)\/流程\/flows\/[^/]+(?:\/|$)/);
4480
+ if (m)
4481
+ return m[1];
4482
+ m = norm.match(/(?:^|\/)项目\/([^/]+)\/流程\/([^/]+)(?:\/|$)/);
4483
+ if (m && m[2] !== "flows" && m[2] !== "\u89C4\u5212")
4484
+ return m[1];
4485
+ m = norm.match(/(?:^|\/)项目\/(flow_[^/]+)\/流程(?:\/|$)/);
4486
+ if (m)
4487
+ return m[1];
4488
+ return void 0;
3462
4489
  }
3463
- }
3464
- return doc;
3465
- }
3466
- function listDirtyCodeNodes(flowDir, meta) {
3467
- const dirty = [];
3468
- for (const [uuid, nodeMeta] of Object.entries(meta.nodes)) {
3469
- if (!nodeMeta.codeFile) continue;
3470
- const code = readNodeCode(flowDir, nodeMeta);
3471
- if (code == null) continue;
3472
- if (sha1(code) !== (nodeMeta.codeHash ?? "")) {
3473
- dirty.push(uuid);
4490
+ exports2.inferBusinessProjectFolder = inferBusinessProjectFolder2;
4491
+ function findFlowDirForPath(target) {
4492
+ let cur = path_1.default.resolve(target);
4493
+ if (fs_1.default.existsSync(cur)) {
4494
+ try {
4495
+ if (fs_1.default.statSync(cur).isFile())
4496
+ cur = path_1.default.dirname(cur);
4497
+ } catch {
4498
+ return void 0;
4499
+ }
4500
+ }
4501
+ for (let i = 0; i < 8 && cur; i++) {
4502
+ if (fs_1.default.existsSync(path_1.default.join(cur, exports2.FLOW_JSON)))
4503
+ return cur;
4504
+ const parent = path_1.default.dirname(cur);
4505
+ if (parent === cur)
4506
+ break;
4507
+ cur = parent;
4508
+ }
4509
+ return void 0;
4510
+ }
4511
+ exports2.findFlowDirForPath = findFlowDirForPath;
4512
+ function isWorkspaceRootCandidate(dir) {
4513
+ return fs_1.default.existsSync(path_1.default.join(dir, "\u9879\u76EE")) || fs_1.default.existsSync(path_1.default.join(dir, "scripts", "dazi.ps1"));
3474
4514
  }
4515
+ function resolveFlowDirForCli(dir) {
4516
+ const requested = path_1.default.resolve(dir ?? process.cwd());
4517
+ const warnings = [];
4518
+ if (fs_1.default.existsSync(path_1.default.join(requested, exports2.FLOW_JSON))) {
4519
+ return { flowDir: requested, resolvedFromParent: false, warnings };
4520
+ }
4521
+ const found = findFlowDirForPath(requested);
4522
+ if (found) {
4523
+ if (found !== requested) {
4524
+ warnings.push(`--dir \u672A\u76F4\u63A5\u6307\u5411\u6D41\u7A0B\u76EE\u5F55\uFF0C\u5DF2\u5411\u4E0A\u89E3\u6790\u4E3A: ${found}`);
4525
+ }
4526
+ return { flowDir: found, resolvedFromParent: found !== requested, warnings };
4527
+ }
4528
+ const hasMetaOnly = fs_1.default.existsSync(path_1.default.join(requested, exports2.FLOW_META_JSON)) && !fs_1.default.existsSync(path_1.default.join(requested, exports2.FLOW_JSON));
4529
+ if (hasMetaOnly || isWorkspaceRootCandidate(requested) && !fs_1.default.existsSync(path_1.default.join(requested, exports2.FLOW_JSON))) {
4530
+ return {
4531
+ flowDir: requested,
4532
+ resolvedFromParent: false,
4533
+ warnings,
4534
+ error: "\u5F53\u524D\u76EE\u5F55\u4E0D\u662F\u6D41\u7A0B\u76EE\u5F55\uFF08\u7F3A\u5C11 flow.json\uFF09\u3002\u8BF7\u4F7F\u7528 --dir \u6307\u5411 \u9879\u76EE/<\u4E1A\u52A1\u540D>/\u6D41\u7A0B/flows/<\u6D41\u7A0B\u540D> \u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF1B\u7981\u6B62\u5728 dazi-work \u5DE5\u4F5C\u533A\u6839\u6267\u884C project status/push/pull --dir ."
4535
+ };
4536
+ }
4537
+ return {
4538
+ flowDir: requested,
4539
+ resolvedFromParent: false,
4540
+ warnings,
4541
+ error: `\u76EE\u5F55\u7F3A\u5C11 flow.json: ${requested}\u3002\u8BF7 --dir \u6307\u5411\u542B flow.json \u7684\u6D41\u7A0B\u76EE\u5F55\uFF08\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09\u3002`
4542
+ };
4543
+ }
4544
+ exports2.resolveFlowDirForCli = resolveFlowDirForCli;
4545
+ function resolveFlowDir5(dir, opts) {
4546
+ const result = resolveFlowDirForCli(dir);
4547
+ if (!opts?.json) {
4548
+ for (const w of result.warnings)
4549
+ console.warn(`\u26A0\uFE0F ${w}`);
4550
+ }
4551
+ if (result.error) {
4552
+ console.error(`\u9519\u8BEF: ${result.error}`);
4553
+ process.exit(1);
4554
+ }
4555
+ return result.flowDir;
4556
+ }
4557
+ exports2.resolveFlowDir = resolveFlowDir5;
3475
4558
  }
3476
- return dirty;
3477
- }
4559
+ });
3478
4560
 
3479
- // cli/dazi-flow/src/lib/flowApi.ts
3480
- var import_path5 = __toESM(require("path"), 1);
3481
- var import_fs4 = __toESM(require("fs"), 1);
3482
- var FLOW_API = "/api/data-pipelines/v1/flows";
3483
- async function pushCodeNode(flowId, flowDir, meta, uuid, opts = {}) {
3484
- const nodeMeta = meta.nodes[uuid];
3485
- if (!nodeMeta) return { uuid, status: "skipped" };
3486
- const localCode = readNodeCode(flowDir, nodeMeta);
3487
- if (localCode == null) return { uuid, status: "skipped" };
3488
- const nodeLabel = nodeMeta.nodeId || uuid;
3489
- const remote = await apiRequest(`${FLOW_API}/${flowId}/flow-nodes/${uuid}`, {
3490
- label: `\u8BFB\u53D6\u8FDC\u7AEF\u8282\u70B9\u4EE3\u7801 (nodeId=${nodeLabel}, uuid=${uuid})`
3491
- });
3492
- const remoteHash = sha1(remote.code_body ?? "");
3493
- const baseHash = nodeMeta.codeHash ?? "";
3494
- if (!opts.force && baseHash && remoteHash !== baseHash) {
3495
- return { uuid, status: "conflict", version: remote.version };
3496
- }
3497
- if (opts.dryRun) return { uuid, status: "dry-run", version: remote.version };
3498
- const updated = await apiRequest(`${FLOW_API}/${flowId}/flow-nodes/${uuid}`, {
3499
- method: "PATCH",
3500
- body: { code_body: localCode, expected_version: opts.force ? void 0 : remote.version },
3501
- label: `\u63D0\u4EA4\u8282\u70B9\u4EE3\u7801 (nodeId=${nodeLabel}, uuid=${uuid})`
3502
- });
3503
- nodeMeta.codeHash = sha1(localCode);
3504
- nodeMeta.codeVersion = updated.version;
3505
- return { uuid, status: "pushed", version: updated.version };
3506
- }
3507
- async function pullCodeNode(flowId, flowDir, meta, uuid) {
3508
- const nodeMeta = meta.nodes[uuid];
3509
- if (!nodeMeta?.dir || !nodeMeta.codeFile) {
3510
- throw new Error(`\u8282\u70B9 ${uuid} \u4E0D\u662F\u4EE3\u7801\u8282\u70B9\u6216\u7F3A\u5C11\u672C\u5730\u6620\u5C04`);
4561
+ // src/shared/managedFilesTypes.js
4562
+ var require_managedFilesTypes = __commonJS({
4563
+ "src/shared/managedFilesTypes.js"(exports2) {
4564
+ "use strict";
4565
+ Object.defineProperty(exports2, "__esModule", { value: true });
4566
+ exports2.MANAGED_BUNDLE_FILES = exports2.MANAGED_FS_API = exports2.MANAGED_FILES_API = void 0;
4567
+ exports2.MANAGED_FILES_API = "/api/data-pipelines/v1/managed-files";
4568
+ exports2.MANAGED_FS_API = "/api/data-pipelines/v1/managed-fs";
4569
+ exports2.MANAGED_BUNDLE_FILES = {
4570
+ manifest: "\u6587\u4EF6\u4FE1\u606F.json",
4571
+ originalPrefix: "\u539F\u6587\u4EF6",
4572
+ nativeDigest: "\u539F\u751F\u89E3\u6790.md",
4573
+ aiStructure: "\u8868\u7ED3\u6784.json",
4574
+ reportLayout: "\u62A5\u8868\u5E03\u5C40.json"
4575
+ };
3511
4576
  }
3512
- const remote = await apiRequest(`${FLOW_API}/${flowId}/flow-nodes/${uuid}`, {
3513
- label: `\u62C9\u53D6\u8282\u70B9\u4EE3\u7801 (uuid=${uuid})`
3514
- });
3515
- const code = remote.code_body ?? "";
3516
- const target = import_path5.default.join(flowDir, nodeMeta.dir, nodeMeta.codeFile);
3517
- import_fs4.default.mkdirSync(import_path5.default.dirname(target), { recursive: true });
3518
- import_fs4.default.writeFileSync(target, code, "utf8");
3519
- nodeMeta.codeHash = sha1(code);
3520
- nodeMeta.codeVersion = remote.version;
3521
- return { uuid, version: remote.version };
3522
- }
3523
- async function pushCanvasDocument(flowId, document) {
3524
- const nodeCount = document.nodes?.length ?? 0;
3525
- const edgeCount = document.edges?.length ?? 0;
3526
- await apiRequest(`${FLOW_API}/${flowId}`, {
3527
- method: "PUT",
3528
- body: { data: document },
3529
- label: `\u63D0\u4EA4\u753B\u5E03 flow.json (flowId=${flowId}, nodes=${nodeCount}, edges=${edgeCount})`
3530
- });
3531
- }
4577
+ });
3532
4578
 
3533
- // cli/dazi-flow/src/commands/snapshot.ts
3534
- function makeSnapshotCommand() {
3535
- const cmd = new Command("snapshot").description("Flow \u5FEB\u7167\u7BA1\u7406");
3536
- cmd.command("pull").description("\u4ECE\u5E73\u53F0\u62C9\u53D6 Flow \u5FEB\u7167\u5230\u672C\u5730 flows/<flowId>/").requiredOption("--flow <flowId>", "Flow ID\uFF08\u77ED\u9009\u9879 -f\uFF09").alias("-f").option("--out <dir>", "\u8F93\u51FA\u76EE\u5F55\uFF08\u9ED8\u8BA4 flows/<flowId>/\uFF09").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
3537
- try {
3538
- console.log(`\u6B63\u5728\u62C9\u53D6\u5FEB\u7167: ${opts.flow} ...`);
3539
- const snapshot = await apiRequest(
3540
- `/api/data-pipelines/v1/flows/${opts.flow}/snapshot`
3541
- );
3542
- const ws = resolveWorkspace();
3543
- const outDir = opts.out ?? import_path6.default.join(ws.flows, opts.flow);
3544
- if (!import_fs5.default.existsSync(outDir)) import_fs5.default.mkdirSync(outDir, { recursive: true });
3545
- const outFile = import_path6.default.join(outDir, "snapshot.json");
3546
- import_fs5.default.writeFileSync(outFile, JSON.stringify(snapshot, null, 2), "utf-8");
3547
- if (opts.json) {
3548
- console.log(JSON.stringify(snapshot, null, 2));
3549
- } else {
3550
- console.log(`\u2705 \u5FEB\u7167\u5DF2\u4FDD\u5B58: ${outFile}`);
4579
+ // src/shared/managedFilesBundle.js
4580
+ var require_managedFilesBundle = __commonJS({
4581
+ "src/shared/managedFilesBundle.js"(exports2) {
4582
+ "use strict";
4583
+ var __importDefault = exports2 && exports2.__importDefault || function(mod) {
4584
+ return mod && mod.__esModule ? mod : { "default": mod };
4585
+ };
4586
+ Object.defineProperty(exports2, "__esModule", { value: true });
4587
+ exports2.findManagedFileInBrowse = exports2.pullManagedFileBundle = exports2.isExcelManagedFile = exports2.describeManagedFileLocalState = exports2.isManagedFilePulledLocally = exports2.managedFileLocalDir = exports2.managedFileLocalFolderName = void 0;
4588
+ var fs_1 = __importDefault(require("fs"));
4589
+ var path_1 = __importDefault(require("path"));
4590
+ var managedFilesTypes_js_1 = require_managedFilesTypes();
4591
+ function apiBase(auth) {
4592
+ return auth.serverUrl.replace(/\/$/, "");
4593
+ }
4594
+ function sanitizeDirPart(raw) {
4595
+ return raw.trim().replace(/\s+/g, "_").replace(/[<>:"|?*/\\]/g, "_").slice(0, 80) || "file";
4596
+ }
4597
+ function managedFileLocalFolderName(meta) {
4598
+ const base = sanitizeDirPart(meta.display_name ?? meta.file_id);
4599
+ const shortId = meta.file_id.slice(0, 8);
4600
+ return `${base}_${shortId}`;
4601
+ }
4602
+ exports2.managedFileLocalFolderName = managedFileLocalFolderName;
4603
+ function managedFileLocalDir(filesRoot, meta) {
4604
+ return path_1.default.join(filesRoot, managedFileLocalFolderName(meta));
4605
+ }
4606
+ exports2.managedFileLocalDir = managedFileLocalDir;
4607
+ function isManagedFilePulledLocally(filesRoot, meta) {
4608
+ return fs_1.default.existsSync(path_1.default.join(managedFileLocalDir(filesRoot, meta), managedFilesTypes_js_1.MANAGED_BUNDLE_FILES.manifest));
4609
+ }
4610
+ exports2.isManagedFilePulledLocally = isManagedFilePulledLocally;
4611
+ function describeManagedFileLocalState(wsRoot, meta, filesSubdir = "files") {
4612
+ if (!wsRoot || !meta.file_id)
4613
+ return void 0;
4614
+ const filesRoot = path_1.default.join(wsRoot, "\u8D44\u6E90", filesSubdir);
4615
+ if (isManagedFilePulledLocally(filesRoot, meta))
4616
+ return "\u5DF2\u62C9\u53D6";
4617
+ return void 0;
4618
+ }
4619
+ exports2.describeManagedFileLocalState = describeManagedFileLocalState;
4620
+ function isExcelManagedFile(meta) {
4621
+ const ext = (meta.file_ext ?? path_1.default.extname(meta.display_name ?? "")).toLowerCase();
4622
+ if (ext === ".xlsx" || ext === ".xls")
4623
+ return true;
4624
+ const mime = (meta.mime_type ?? "").toLowerCase();
4625
+ return mime.includes("spreadsheet") || mime.endsWith(".sheet");
4626
+ }
4627
+ exports2.isExcelManagedFile = isExcelManagedFile;
4628
+ async function fetchJson(auth, urlPath, method = "GET") {
4629
+ const res = await fetch(`${apiBase(auth)}${urlPath}`, {
4630
+ method,
4631
+ headers: {
4632
+ Authorization: `Bearer ${auth.token}`,
4633
+ Accept: "application/json",
4634
+ ...method === "POST" ? { "Content-Type": "application/json" } : {}
4635
+ }
4636
+ });
4637
+ if (!res.ok) {
4638
+ const text = await res.text().catch(() => "");
4639
+ throw new Error(`HTTP ${res.status}: ${text || res.statusText}`);
3551
4640
  }
3552
- ok({ flowId: opts.flow, file: outFile });
3553
- } catch (err) {
3554
- handleError(err);
4641
+ return res.json();
3555
4642
  }
3556
- });
3557
- cmd.command("push-graph").description("\u5C06\u672C\u5730 flows/<flowId>/snapshot.json \u56FE\u7ED3\u6784\u63A8\u9001\u5230\u5E73\u53F0").requiredOption("--flow <flowId>", "Flow ID").option("--dir <dir>", "\u5FEB\u7167\u76EE\u5F55\uFF08\u9ED8\u8BA4 flows/<flowId>/\uFF09").option("--dry-run", "\u4EC5\u9A8C\u8BC1\uFF0C\u4E0D\u63A8\u9001").action(async (opts) => {
3558
- try {
3559
- const ws = resolveWorkspace();
3560
- const dir = opts.dir ?? import_path6.default.join(ws.flows, opts.flow);
3561
- const file = import_path6.default.join(dir, "snapshot.json");
3562
- if (!import_fs5.default.existsSync(file)) {
3563
- console.error(`\u9519\u8BEF: \u627E\u4E0D\u5230 ${file}\uFF0C\u8BF7\u5148\u8FD0\u884C snapshot pull`);
3564
- process.exit(1);
4643
+ async function downloadBinary(auth, urlPath, destPath) {
4644
+ const res = await fetch(`${apiBase(auth)}${urlPath}`, {
4645
+ headers: { Authorization: `Bearer ${auth.token}` }
4646
+ });
4647
+ if (!res.ok) {
4648
+ const text = await res.text().catch(() => "");
4649
+ throw new Error(`\u4E0B\u8F7D\u5931\u8D25 HTTP ${res.status}: ${text || res.statusText}`);
3565
4650
  }
3566
- const snapshot = JSON.parse(import_fs5.default.readFileSync(file, "utf-8"));
3567
- if (opts.dryRun) {
3568
- console.log("[dry-run] \u5C06\u63A8\u9001\u56FE\u5FEB\u7167:");
3569
- console.log(` \u8282\u70B9\u6570: ${snapshot.nodes?.length ?? "?"}`);
3570
- console.log(` \u8FB9\u6570: ${snapshot.edges?.length ?? "?"}`);
3571
- ok({ flowId: opts.flow, dryRun: true });
3572
- return;
4651
+ const buf = Buffer.from(await res.arrayBuffer());
4652
+ fs_1.default.mkdirSync(path_1.default.dirname(destPath), { recursive: true });
4653
+ fs_1.default.writeFileSync(destPath, buf);
4654
+ }
4655
+ async function resolveNativeDigest(auth, fileId, parseIfMissing) {
4656
+ if (!parseIfMissing) {
4657
+ try {
4658
+ return await fetchJson(auth, `${managedFilesTypes_js_1.MANAGED_FILES_API}/${encodeURIComponent(fileId)}/excel-native-digest`);
4659
+ } catch {
4660
+ return null;
4661
+ }
4662
+ }
4663
+ try {
4664
+ let resp = await fetchJson(auth, `${managedFilesTypes_js_1.MANAGED_FILES_API}/${encodeURIComponent(fileId)}/excel-native-digest`);
4665
+ if (resp.markdown)
4666
+ return resp;
4667
+ } catch {
4668
+ }
4669
+ try {
4670
+ return await fetchJson(auth, `${managedFilesTypes_js_1.MANAGED_FILES_API}/${encodeURIComponent(fileId)}/excel-native-parse`, "POST");
4671
+ } catch {
4672
+ return null;
3573
4673
  }
3574
- const doc = extractDocument(snapshot);
3575
- await pushCanvasDocument(opts.flow, doc);
3576
- console.log(`\u2705 \u56FE\u5FEB\u7167\u5DF2\u63A8\u9001`);
3577
- ok({ flowId: opts.flow, nodeCount: doc.nodes.length, edgeCount: doc.edges.length });
3578
- } catch (err) {
3579
- handleError(err);
3580
4674
  }
3581
- });
3582
- return cmd;
3583
- }
3584
-
3585
- // cli/dazi-flow/src/commands/project.ts
3586
- var import_path10 = __toESM(require("path"), 1);
3587
- var import_fs8 = __toESM(require("fs"), 1);
3588
-
3589
- // src/shared/flowScaffoldDocs.ts
3590
- var import_path8 = __toESM(require("path"));
3591
-
3592
- // src/shared/flowNodeCatalog.ts
3593
- var FLOW_NODE_CATALOG = [
3594
- {
3595
- group: "\u6D41\u7A0B\u63A7\u5236",
3596
- items: [
3597
- {
3598
- type: "condition",
3599
- label: "\u6761\u4EF6\u5224\u65AD",
3600
- hasCode: true,
3601
- codeFile: "code.py",
3602
- summary: "\u5BF9\u4E0A\u6E38\u8868\u6C42\u5E03\u5C14\u8868\u8FBE\u5F0F\uFF0C\u8D70 True/False \u5206\u652F",
3603
- consumes: "\u5165\u8FB9\u4E0A\u6E38\u8868 \u2192 \u8FD0\u884C\u65F6\u6CE8\u5165 `df`",
3604
- produces: "\u65E0\u65B0\u8868\u53D8\u91CF\uFF08\u4EC5\u8DEF\u7531\uFF09",
3605
- keyConfig: ["label", "pythonCode\uFF08\u4EE3\u7801\u5728 code.py\uFF09"]
3606
- },
3607
- {
3608
- type: "delay",
3609
- label: "\u5EF6\u65F6\u7B49\u5F85",
3610
- hasCode: false,
3611
- summary: "\u6682\u505C\u6307\u5B9A\u79D2\u6570\u540E\u7EE7\u7EED\u4E0B\u6E38",
3612
- keyConfig: ["delaySeconds \u6216 delay\uFF08\u79D2\uFF09"]
3613
- }
3614
- ]
3615
- },
3616
- {
3617
- group: "\u6570\u636E\u5904\u7406",
3618
- items: [
3619
- {
3620
- type: "file-source",
3621
- label: "\u6587\u4EF6\u8F93\u5165",
3622
- hasCode: false,
3623
- summary: "\u975E Excel \u539F\u59CB\u6587\u4EF6\u900F\u4F20\uFF08.xlsx/.xls \u52FF\u7528\uFF0C\u6539 excel-python\uFF09",
3624
- produces: "`output_variable_name`",
3625
- keyConfig: ["file_id / managed_file_id", "output_variable_name"]
3626
- },
3627
- {
3628
- type: "excel-import",
3629
- label: "Excel \u5BFC\u5165",
3630
- hasCode: false,
3631
- summary: "\u4EC5\u6781\u7B80\u5355\u5355 Sheet \u96F6\u4EE3\u7801\uFF1B\u6709 file_id \u7684 Excel \u9ED8\u8BA4\u4ECD\u7528 excel-python",
3632
- produces: "`output_variable_name`",
3633
- keyConfig: ["file_id / source_excel_id", "sheetName\uFF08\u53EF\u9009\uFF09", "output_variable_name"]
3634
- },
3635
- {
3636
- type: "excel-python",
3637
- label: "Excel \u5F00\u53D1",
3638
- hasCode: true,
3639
- codeFile: "code.py",
3640
- summary: "\u591A Sheet / \u590D\u6742\u8868\u5934\uFF1APython \u89E3\u6790 Excel\uFF08\u753B\u5E03\u914D managed_file_id\uFF0C\u4EE3\u7801\u7528 excel_source_path\uFF09",
3641
- produces: "`set_table_output(\u4E0E output_variable_name \u540C\u540D, df)`",
3642
- keyConfig: ["managed_file_id\uFF08UUID\uFF0C\u975E\u6587\u4EF6\u540D\uFF09", "output_variable_name"]
3643
- },
3644
- {
3645
- type: "sql-query",
3646
- label: "SQL \u67E5\u8BE2",
3647
- hasCode: true,
3648
- codeFile: "code.sql",
3649
- summary: "DuckDB \u5185\u5B58 SQL\uFF0C\u4E0A\u6E38\u53D8\u91CF\u540D\u4F5C\u8868\u540D",
3650
- consumes: "\u4E0A\u6E38 `output_variable_name` \u4F5C SQL \u8868\u540D",
3651
- produces: "`output_variable_name`",
3652
- keyConfig: ["output_variable_name"]
3653
- },
3654
- {
3655
- type: "python-script",
3656
- label: "Python \u811A\u672C",
3657
- hasCode: true,
3658
- codeFile: "code.py",
3659
- summary: "pandas \u8F6C\u6362\uFF1B\u6700\u5E38\u7528\u52A0\u5DE5\u8282\u70B9",
3660
- consumes: '`get_variable("\u4E0A\u6E38\u53D8\u91CF\u540D")` \u6216\u5165\u8FB9 `df`',
3661
- produces: "`result_df` \u2192 `output_variable_name`",
3662
- keyConfig: ["output_variable_name"]
3663
- },
3664
- {
3665
- type: "database-source",
3666
- label: "\u6570\u636E\u5E93\u8BFB\u53D6",
3667
- hasCode: true,
3668
- codeFile: "code.sql",
3669
- summary: "\u4ECE\u5916\u90E8\u6570\u636E\u5E93\uFF08connectionId\uFF09\u53EA\u8BFB\u67E5\u8BE2",
3670
- produces: "`output_variable_name`",
3671
- keyConfig: ["connectionId", "output_variable_name"]
3672
- },
3673
- {
3674
- type: "database-sink",
3675
- label: "\u6570\u636E\u5E93\u5199\u5165",
3676
- hasCode: false,
3677
- summary: "\u5C06\u8868\u53D8\u91CF\u5199\u5165\u5916\u90E8\u6570\u636E\u5E93\u8868",
3678
- consumes: "`input_variable_name` \u6216\u5165\u8FB9 Parquet",
3679
- keyConfig: ["connectionId", "tableName", "mode", "input_variable_name"]
3680
- },
3681
- {
3682
- type: "dataspace-source",
3683
- label: "\u6570\u636E\u7A7A\u95F4\u8BFB\u53D6",
3684
- hasCode: true,
3685
- codeFile: "code.sql",
3686
- summary: "\u4ECE\u642D\u5B50\u6570\u636E\u7A7A\u95F4\uFF08spaceId\uFF09\u53EA\u8BFB SQL",
3687
- produces: "`output_variable_name`",
3688
- keyConfig: ["spaceId", "output_variable_name", "limit\uFF08\u53EF\u9009\uFF09"]
3689
- },
3690
- {
3691
- type: "dataspace-sink",
3692
- label: "\u6570\u636E\u7A7A\u95F4\u5199\u5165",
3693
- hasCode: false,
3694
- summary: "\u5C06\u8868\u53D8\u91CF\u5199\u5165\u6570\u636E\u7A7A\u95F4\u8868",
3695
- consumes: "`input_variable_name` \u6216\u5165\u8FB9 Parquet",
3696
- keyConfig: ["spaceId", "tableName", "input_variable_name", "syncMetadata\uFF08\u53EF\u9009\uFF09"]
3697
- },
3698
- {
3699
- type: "data-quality-check",
3700
- label: "\u6570\u636E\u8D28\u91CF\u68C0\u67E5",
3701
- hasCode: true,
3702
- codeFile: "code.py",
3703
- summary: "\u8D28\u68C0\u89C4\u5219\u6216\u81EA\u5B9A\u4E49 Python\uFF08dqPythonCode\uFF09",
3704
- consumes: "\u4E0A\u6E38\u8868 / `get_variable`",
3705
- produces: "\u8D28\u68C0\u62A5\u544A\u53D8\u91CF\uFF08\u4F9D\u914D\u7F6E\uFF09",
3706
- keyConfig: ["output_variable_name", "qualityConfig", "dqPythonCode\uFF08\u4F18\u5148\u4E8E\u89C4\u5219\uFF09"]
3707
- },
3708
- {
3709
- type: "folder-resource-import",
3710
- label: "\u6587\u4EF6\u5939\u8D44\u6E90\u5BFC\u5165",
3711
- hasCode: false,
3712
- summary: "\u4ECE\u6587\u4EF6\u5939\u8D44\u6E90\u6279\u91CF\u5BFC\u5165\u6587\u4EF6",
3713
- produces: "`output_variable_name`",
3714
- keyConfig: ["folderId", "resourceId", "pageSize", "output_variable_name"]
3715
- },
3716
- {
3717
- type: "folder-resource-register",
3718
- label: "\u8D44\u6E90\u6CE8\u518C",
3719
- hasCode: false,
3720
- summary: "\u5C06\u5BFC\u5165\u7ED3\u679C\u6CE8\u518C\u5230\u8FDE\u63A5/\u8D44\u6E90\u76EE\u5F55",
3721
- keyConfig: ["connectionName", "targetFolderId"]
4675
+ async function resolveAiStructure(auth, fileId) {
4676
+ try {
4677
+ return await fetchJson(auth, `${managedFilesTypes_js_1.MANAGED_FILES_API}/${encodeURIComponent(fileId)}/excel-ai-structure`);
4678
+ } catch {
4679
+ return null;
3722
4680
  }
3723
- ]
3724
- }
3725
- ];
3726
- var FLOW_NODE_CATALOG_DESIGNER = FLOW_NODE_CATALOG.map((g) => ({
3727
- group: g.group,
3728
- items: g.items.map(({ type, label, hasCode }) => ({
3729
- type,
3730
- label,
3731
- ...hasCode ? { code: true } : {}
3732
- }))
3733
- }));
3734
- function buildFlowNodeCatalogQuickStartSection(detailLink) {
3735
- const lines = [
3736
- "## \u53EF\u7528\u6D41\u7A0B\u8282\u70B9\uFF08`data.type`\uFF09",
3737
- "",
3738
- "> \u753B\u5E03\u8282\u70B9 **`type` \u6052\u4E3A `custom`**\uFF0C\u4E1A\u52A1\u7C7B\u578B\u5728 **`data.type`**\u3002\u65B0\u5EFA\uFF1A`flow node new --type <\u4E0B\u8868 type>`\u3002",
3739
- "> **\u7981\u6B62**\u7F16\u9020\u672A\u5728\u4E0B\u8868\u4E2D\u7684 type\u3002",
3740
- ""
3741
- ];
3742
- for (const { group, items } of FLOW_NODE_CATALOG) {
3743
- lines.push(`### ${group}`, "");
3744
- lines.push("| type | \u540D\u79F0 | \u4EE3\u7801 | \u7528\u9014 | \u53D8\u91CF |");
3745
- lines.push("|------|------|:----:|------|------|");
3746
- for (const n of items) {
3747
- const code = n.hasCode ? n.codeFile ?? "code.*" : "\u2014";
3748
- const vars = [n.consumes ? `\u5165:${shortVar(n.consumes)}` : "", n.produces ? `\u51FA:${shortVar(n.produces)}` : ""].filter(Boolean).join(" \xB7 ") || "\u2014";
3749
- lines.push(`| \`${n.type}\` | ${n.label} | ${code} | ${n.summary} | ${vars} |`);
3750
4681
  }
3751
- lines.push("");
3752
- }
3753
- lines.push(
3754
- "**\u9009\u578B\u63D0\u793A**",
3755
- "",
3756
- "- **Excel\uFF08\u6709 `managed_file_id` / `file_id`\uFF09\u2192 \u4F18\u5148 `excel-python`**\uFF0C\u52FF\u7528 `file-source`\uFF08\u4E0D\u89E3\u6790 Excel\uFF09",
3757
- "- \u4EC5\u6781\u7B80\u5355\u5355 Sheet\u3001\u96F6\u4EE3\u7801\u65F6\u624D\u7528 `excel-import`\uFF1B\u591A Sheet / \u590D\u6742\u8868\u5934 / \u4E0D\u786E\u5B9A\u7ED3\u6784 \u2192 `excel-python`",
3758
- "- **`excel-python`**\uFF1A\u753B\u5E03\u914D **`managed_file_id`\uFF08UUID\uFF09**\uFF1B`code.py` \u7528 **`excel_source_path`**\uFF0C\u7981\u6B62\u5199\u6587\u4EF6\u540D/\u672C\u5730\u8DEF\u5F84",
3759
- "- \u975E Excel \u539F\u59CB\u6587\u4EF6 \u2192 `file-source`\uFF1B\u8BFB\u5916\u90E8\u5E93 \u2192 `database-source`\uFF1B\u8BFB\u6570\u636E\u7A7A\u95F4 \u2192 `dataspace-source`",
3760
- "- \u5185\u5B58\u52A0\u5DE5 \u2192 `sql-query` / `python-script`\uFF1B\u5206\u652F \u2192 `condition`\uFF08\u51FA\u8FB9 `true`/`false`\uFF09",
3761
- "- \u5199\u5916\u90E8\u5E93 \u2192 `database-sink`\uFF1B\u5199\u6570\u636E\u7A7A\u95F4 \u2192 `dataspace-sink`",
3762
- ""
3763
- );
3764
- if (detailLink) {
3765
- lines.push(`\u5404\u8282\u70B9\u914D\u7F6E\u4E0E\u793A\u4F8B \u2192 [flows-guide \xA7\u6D41\u7A0B\u8282\u70B9\u7EC4\u4EF6](${detailLink}#\u6D41\u7A0B\u8282\u70B9\u7EC4\u4EF6)\u3002`, "");
3766
- } else {
3767
- lines.push("\u5404\u8282\u70B9\u914D\u7F6E\u4E0E\u793A\u4F8B \u2192 \u5E2E\u52A9\u6587\u6863 **flow/flows-guide** \xA7\u6D41\u7A0B\u8282\u70B9\u7EC4\u4EF6\u3002", "");
4682
+ async function resolveReportLayout(auth, fileId) {
4683
+ try {
4684
+ return await fetchJson(auth, `${managedFilesTypes_js_1.MANAGED_FILES_API}/${encodeURIComponent(fileId)}/excel-report-layout`);
4685
+ } catch {
4686
+ return null;
4687
+ }
4688
+ }
4689
+ async function pullManagedFileBundle2(opts) {
4690
+ const { auth, meta } = opts;
4691
+ const fileId = meta.file_id?.trim();
4692
+ if (!fileId)
4693
+ throw new Error("\u7F3A\u5C11 file_id\uFF0C\u4EC5\u652F\u6301\u5DF2\u767B\u8BB0\u6587\u4EF6");
4694
+ const dir = managedFileLocalDir(opts.filesRootDir, meta);
4695
+ fs_1.default.mkdirSync(dir, { recursive: true });
4696
+ const written = [];
4697
+ const notes = [];
4698
+ const pulledAt = (/* @__PURE__ */ new Date()).toISOString();
4699
+ const ext = (meta.file_ext && meta.file_ext.startsWith(".") ? meta.file_ext : meta.file_ext ? `.${meta.file_ext}` : "") || path_1.default.extname(meta.display_name ?? meta.stored_filename ?? "") || "";
4700
+ const originalName = `${managedFilesTypes_js_1.MANAGED_BUNDLE_FILES.originalPrefix}${ext || ""}`;
4701
+ const originalPath = path_1.default.join(dir, originalName);
4702
+ await downloadBinary(auth, `${managedFilesTypes_js_1.MANAGED_FILES_API}/${encodeURIComponent(fileId)}/content`, originalPath);
4703
+ written.push(originalName);
4704
+ if (isExcelManagedFile(meta)) {
4705
+ const digest = await resolveNativeDigest(auth, fileId, opts.parseNativeIfMissing !== false);
4706
+ if (digest?.markdown) {
4707
+ fs_1.default.writeFileSync(path_1.default.join(dir, managedFilesTypes_js_1.MANAGED_BUNDLE_FILES.nativeDigest), digest.markdown, "utf8");
4708
+ written.push(managedFilesTypes_js_1.MANAGED_BUNDLE_FILES.nativeDigest);
4709
+ } else {
4710
+ notes.push("\u539F\u751F\u89E3\u6790\u672A\u751F\u6210\uFF1A\u8BF7\u5728\u5E73\u53F0\u300C\u6587\u4EF6\u4E0A\u4F20\u7BA1\u7406\u300D\u4E2D\u5BF9 Excel \u6267\u884C\u300C\u539F\u751F\u89E3\u6790\u300D\u540E\u91CD\u8BD5\u62C9\u53D6");
4711
+ }
4712
+ const structureResp = await resolveAiStructure(auth, fileId);
4713
+ if (structureResp?.structure) {
4714
+ fs_1.default.writeFileSync(path_1.default.join(dir, managedFilesTypes_js_1.MANAGED_BUNDLE_FILES.aiStructure), JSON.stringify(structureResp.structure, null, 2), "utf8");
4715
+ written.push(managedFilesTypes_js_1.MANAGED_BUNDLE_FILES.aiStructure);
4716
+ } else {
4717
+ notes.push("\u8868\u7ED3\u6784\u672A\u751F\u6210\uFF1A\u8BF7\u5148\u5728\u5E73\u53F0\u5B8C\u6210\u300C\u539F\u751F\u89E3\u6790\u300D\u4E0E\u300CAI \u8868\u7ED3\u6784\u300D\u540E\u518D\u62C9\u53D6\uFF0C\u6216\u4EC5\u4F7F\u7528\u539F\u751F\u89E3\u6790.md \u4EA4\u7ED9 AI");
4718
+ }
4719
+ const layoutResp = await resolveReportLayout(auth, fileId);
4720
+ if (layoutResp?.layout) {
4721
+ fs_1.default.writeFileSync(path_1.default.join(dir, managedFilesTypes_js_1.MANAGED_BUNDLE_FILES.reportLayout), JSON.stringify(layoutResp.layout, null, 2), "utf8");
4722
+ written.push(managedFilesTypes_js_1.MANAGED_BUNDLE_FILES.reportLayout);
4723
+ } else {
4724
+ notes.push("\u62A5\u8868\u5E03\u5C40\u672A\u751F\u6210\uFF1A\u8BF7\u5728\u5E73\u53F0\u300C\u62A5\u8868\u5E03\u5C40\u300D\u9875\u7B7E\u6267\u884C\u89C4\u5219\u89E3\u6790\u6216 AI \u62A5\u8868\u5E03\u5C40\u540E\u518D\u62C9\u53D6");
4725
+ }
4726
+ } else {
4727
+ notes.push("\u975E Excel \u6587\u4EF6\uFF1A\u4EC5\u62C9\u53D6\u539F\u6587\u4EF6\u4E0E\u6587\u4EF6\u4FE1\u606F\uFF08\u65E0\u539F\u751F\u89E3\u6790/\u8868\u7ED3\u6784\uFF09");
4728
+ }
4729
+ const manifest = {
4730
+ pulledAt,
4731
+ file_id: fileId,
4732
+ display_name: meta.display_name,
4733
+ relative_dir: meta.relative_dir,
4734
+ localDir: dir,
4735
+ files: written,
4736
+ notes: notes.length ? notes : void 0
4737
+ };
4738
+ manifest.files = [...written, managedFilesTypes_js_1.MANAGED_BUNDLE_FILES.manifest];
4739
+ fs_1.default.writeFileSync(path_1.default.join(dir, managedFilesTypes_js_1.MANAGED_BUNDLE_FILES.manifest), JSON.stringify({ ...meta, ...manifest }, null, 2), "utf8");
4740
+ return { dir, manifest };
4741
+ }
4742
+ exports2.pullManagedFileBundle = pullManagedFileBundle2;
4743
+ function findManagedFileInBrowse2(items, fileId) {
4744
+ return items.find((i) => i.file_id === fileId);
4745
+ }
4746
+ exports2.findManagedFileInBrowse = findManagedFileInBrowse2;
3768
4747
  }
3769
- return lines;
3770
- }
3771
- function shortVar(s) {
3772
- return s.replace(/`/g, "").slice(0, 40);
3773
- }
4748
+ });
3774
4749
 
3775
- // src/shared/flowCliText.ts
3776
- var DAZI_CLI = "dazi";
3777
- function psFlowLine(subcommand) {
3778
- return `${DAZI_CLI} flow ${subcommand}`;
3779
- }
3780
- function psDaziLine(args) {
3781
- return `${DAZI_CLI} ${args}`;
3782
- }
3783
- function mdFlowCmd(subcommand) {
3784
- return `\`${DAZI_CLI} flow ${subcommand}\``;
3785
- }
4750
+ // node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
4751
+ var import_index = __toESM(require_commander(), 1);
4752
+ var {
4753
+ program,
4754
+ createCommand,
4755
+ createArgument,
4756
+ createOption,
4757
+ CommanderError,
4758
+ InvalidArgumentError,
4759
+ InvalidOptionArgumentError,
4760
+ // deprecated old name
4761
+ Command,
4762
+ Argument,
4763
+ Option,
4764
+ Help
4765
+ } = import_index.default;
3786
4766
 
3787
- // src/shared/flowWorkspaceAudit.ts
3788
- var import_crypto2 = __toESM(require("crypto"));
3789
- var import_fs6 = __toESM(require("fs"));
3790
- var import_path7 = __toESM(require("path"));
3791
- var NODES_DIR2 = "\u8282\u70B9";
3792
- var FLOW_JSON2 = "flow.json";
3793
- var FLOW_META_JSON2 = "flow.meta.json";
3794
- var NODE_INFO_JSON2 = "node.info.json";
3795
- var NODE_TYPE_CODE_DATA_KEY2 = {
3796
- "database-source": "sql",
3797
- "dataspace-source": "sql",
3798
- "python-script": "pythonCode",
3799
- "excel-python": "pythonCode",
3800
- "sql-query": "sql",
3801
- condition: "pythonCode",
3802
- "data-quality-check": "dqPythonCode"
4767
+ // cli/dazi-flow/src/commands/flows.ts
4768
+ var import_path3 = __toESM(require("path"), 1);
4769
+ var import_fs2 = __toESM(require("fs"), 1);
4770
+
4771
+ // cli/shared/src/auth.js
4772
+ var import_os = __toESM(require("os"), 1);
4773
+ var import_path = __toESM(require("path"), 1);
4774
+ var import_fs = __toESM(require("fs"), 1);
4775
+
4776
+ // cli/shared/src/errors.js
4777
+ var DaziError = class extends Error {
4778
+ constructor(message, code = "ERR_DAZI", exitCode = 1) {
4779
+ super(message);
4780
+ this.code = code;
4781
+ this.exitCode = exitCode;
4782
+ this.name = "DaziError";
4783
+ }
3803
4784
  };
3804
- function sha12(text) {
3805
- return import_crypto2.default.createHash("sha1").update(text, "utf8").digest("hex");
3806
- }
3807
- function resolveNodeType2(node) {
3808
- if (node.type === "custom") {
3809
- return String(node.data?.type ?? "custom");
4785
+ var AuthError = class extends DaziError {
4786
+ constructor(message = "\u672A\u767B\u5F55\uFF0C\u8BF7\u5148\u8FD0\u884C: dazi auth login") {
4787
+ super(message, "ERR_AUTH", 2);
4788
+ this.name = "AuthError";
3810
4789
  }
3811
- return String(node.type ?? "custom");
3812
- }
3813
- function codeLanguageForNodeType2(nodeType) {
3814
- if (["python-script", "excel-python", "condition", "data-quality-check"].includes(nodeType)) {
3815
- return "python";
4790
+ };
4791
+ var NetworkError = class extends DaziError {
4792
+ constructor(message, status, request) {
4793
+ super(message, "ERR_NETWORK", 3);
4794
+ this.status = status;
4795
+ this.request = request;
4796
+ this.name = "NetworkError";
3816
4797
  }
3817
- if (["database-source", "dataspace-source", "sql-query"].includes(nodeType)) {
3818
- return "sql";
4798
+ /** 多行人类可读详情,写入 stderr / OutputChannel */
4799
+ formatDetailLines() {
4800
+ const lines = [`\u9519\u8BEF: ${this.message}`];
4801
+ const r = this.request;
4802
+ if (!r)
4803
+ return lines;
4804
+ if (r.label)
4805
+ lines.push(`\u6B65\u9AA4: ${r.label}`);
4806
+ lines.push(`\u8BF7\u6C42: ${r.method} ${r.path}`);
4807
+ lines.push(`\u5B8C\u6574 URL: ${r.url}`);
4808
+ lines.push(`HTTP \u72B6\u6001: ${r.status}`);
4809
+ if (r.responseSnippet)
4810
+ lines.push(`\u54CD\u5E94\u6458\u8981: ${r.responseSnippet}`);
4811
+ return lines;
3819
4812
  }
3820
- return void 0;
3821
- }
3822
- function codeFileExt2(language) {
3823
- if (language === "python") return "py";
3824
- if (language === "sql") return "sql";
3825
- return "txt";
3826
- }
3827
- function sanitizeName2(name) {
3828
- const cleaned = (name || "").replace(/[<>:"/\\|?*\x00-\x1f]/g, "_").trim().replace(/\s+/g, "_");
3829
- return cleaned || "node";
3830
- }
3831
- function readFlowJson2(flowDir) {
3832
- const raw = JSON.parse(import_fs6.default.readFileSync(import_path7.default.join(flowDir, FLOW_JSON2), "utf8"));
3833
- return {
3834
- nodes: Array.isArray(raw.nodes) ? raw.nodes : [],
3835
- edges: Array.isArray(raw.edges) ? raw.edges : []
3836
- };
4813
+ };
4814
+
4815
+ // cli/shared/src/auth.js
4816
+ function authFilePath() {
4817
+ return import_path.default.join(import_os.default.homedir(), ".dazi", "auth.json");
3837
4818
  }
3838
- function readMeta2(flowDir) {
3839
- const p = import_path7.default.join(flowDir, FLOW_META_JSON2);
3840
- if (!import_fs6.default.existsSync(p)) return void 0;
4819
+ function loadAuth() {
4820
+ const p = authFilePath();
4821
+ if (!import_fs.default.existsSync(p))
4822
+ throw new AuthError();
3841
4823
  try {
3842
- return JSON.parse(import_fs6.default.readFileSync(p, "utf8"));
4824
+ return JSON.parse(import_fs.default.readFileSync(p, "utf-8"));
3843
4825
  } catch {
3844
- return void 0;
4826
+ throw new AuthError("auth.json \u683C\u5F0F\u635F\u574F\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55");
3845
4827
  }
3846
4828
  }
3847
- function isCodeNodeType(nodeType) {
3848
- return Boolean(NODE_TYPE_CODE_DATA_KEY2[nodeType]);
3849
- }
3850
- function listNodeDirs(flowDir) {
3851
- const nodesRoot = import_path7.default.join(flowDir, NODES_DIR2);
3852
- if (!import_fs6.default.existsSync(nodesRoot)) return [];
3853
- return import_fs6.default.readdirSync(nodesRoot).filter((n) => {
3854
- try {
3855
- return import_fs6.default.statSync(import_path7.default.join(nodesRoot, n)).isDirectory();
3856
- } catch {
3857
- return false;
3858
- }
3859
- });
4829
+
4830
+ // cli/shared/src/httpClient.js
4831
+ var SNIPPET_MAX = 280;
4832
+ function truncateSnippet(text) {
4833
+ const oneLine = text.replace(/\s+/g, " ").trim();
4834
+ if (oneLine.length <= SNIPPET_MAX)
4835
+ return oneLine;
4836
+ return `${oneLine.slice(0, SNIPPET_MAX)}\u2026`;
3860
4837
  }
3861
- function findCodeFileInDir(dirAbs) {
3862
- if (!import_fs6.default.existsSync(dirAbs)) return void 0;
3863
- for (const f of import_fs6.default.readdirSync(dirAbs)) {
3864
- if (/^code\.(py|sql|txt)$/i.test(f)) return f;
3865
- }
3866
- return void 0;
4838
+ function buildHttpErrorMessage(ctx, detail) {
4839
+ const parts = [
4840
+ ctx.label ? `${ctx.label}\u5931\u8D25` : "HTTP \u8BF7\u6C42\u5931\u8D25",
4841
+ `[${ctx.status} ${ctx.method} ${ctx.path}]`,
4842
+ detail
4843
+ ].filter(Boolean);
4844
+ return parts.join(" \u2014 ");
3867
4845
  }
3868
- function findDirForUuid(flowDir, uuid) {
3869
- const nodesRoot = import_path7.default.join(flowDir, NODES_DIR2);
3870
- for (const dirName of listNodeDirs(flowDir)) {
3871
- const infoPath = import_path7.default.join(nodesRoot, dirName, NODE_INFO_JSON2);
3872
- if (import_fs6.default.existsSync(infoPath)) {
4846
+ async function apiRequest(path19, opts = {}) {
4847
+ const auth = opts.token || opts.serverUrl ? { token: opts.token ?? "", serverUrl: opts.serverUrl ?? "" } : loadAuth();
4848
+ const method = (opts.method ?? "GET").toUpperCase();
4849
+ const serverBase = auth.serverUrl.replace(/\/$/, "");
4850
+ const url = `${serverBase}${path19}`;
4851
+ const headers = {
4852
+ "Content-Type": "application/json",
4853
+ Authorization: `Bearer ${auth.token}`,
4854
+ ...opts.headers
4855
+ };
4856
+ const res = await fetch(url, {
4857
+ method,
4858
+ headers,
4859
+ body: opts.body != null ? JSON.stringify(opts.body) : void 0
4860
+ });
4861
+ if (!res.ok) {
4862
+ let detail = `HTTP ${res.status} ${res.statusText}`;
4863
+ let responseSnippet;
4864
+ const rawText = await res.text();
4865
+ if (rawText) {
4866
+ responseSnippet = truncateSnippet(rawText);
3873
4867
  try {
3874
- const info = JSON.parse(import_fs6.default.readFileSync(infoPath, "utf8"));
3875
- if (String(info.node_uuid ?? "") === uuid) return import_path7.default.posix.join(NODES_DIR2, dirName);
4868
+ const j = JSON.parse(rawText);
4869
+ if (typeof j.detail === "string" && j.detail.trim()) {
4870
+ detail = j.detail;
4871
+ } else if (Array.isArray(j.detail) && j.detail.length) {
4872
+ detail = j.detail.map((d) => d.msg ?? JSON.stringify(d)).join("; ");
4873
+ } else if (j.message) {
4874
+ detail = j.message;
4875
+ }
3876
4876
  } catch {
4877
+ if (rawText.trim() && detail.startsWith("HTTP ")) {
4878
+ detail = `\u975E JSON \u54CD\u5E94\uFF08\u53EF\u80FD\u662F\u7F51\u5173/\u524D\u7AEF 404\uFF09`;
4879
+ }
3877
4880
  }
3878
4881
  }
4882
+ const ctx = {
4883
+ method,
4884
+ path: path19,
4885
+ url,
4886
+ status: res.status,
4887
+ label: opts.label,
4888
+ responseSnippet
4889
+ };
4890
+ throw new NetworkError(buildHttpErrorMessage(ctx, detail), res.status, ctx);
3879
4891
  }
3880
- return void 0;
4892
+ if (res.status === 204 || res.headers.get("content-length") === "0") {
4893
+ return void 0;
4894
+ }
4895
+ const text = await res.text();
4896
+ if (!text.trim())
4897
+ return void 0;
4898
+ return JSON.parse(text);
3881
4899
  }
3882
- function auditFlowWorkspace(flowDir) {
3883
- const warnings = [];
3884
- const doc = readFlowJson2(flowDir);
3885
- const meta = readMeta2(flowDir) ?? { nodes: {} };
3886
- const canvasNodeCount = doc.nodes.length;
3887
- let canvasCodeNodeCount = 0;
3888
- const missingInMeta = [];
3889
- const missingCodeFiles = [];
3890
- const flowUuids = /* @__PURE__ */ new Set();
3891
- for (const node of doc.nodes) {
3892
- const uuid = String(node.node_uuid ?? "").trim();
3893
- if (!uuid) continue;
3894
- flowUuids.add(uuid);
3895
- const nodeType = resolveNodeType2(node);
3896
- if (!isCodeNodeType(nodeType)) continue;
3897
- canvasCodeNodeCount++;
3898
- const label = String(node.data?.label ?? node.id ?? "");
3899
- const m = meta.nodes[uuid];
3900
- if (!m?.dir || !m.codeFile) {
3901
- missingInMeta.push({ uuid, nodeId: String(node.id ?? ""), nodeType, label });
3902
- continue;
3903
- }
3904
- const codePath = import_path7.default.join(flowDir, m.dir, m.codeFile);
3905
- if (!import_fs6.default.existsSync(codePath)) {
3906
- missingCodeFiles.push({ uuid, dir: m.dir, codeFile: m.codeFile });
4900
+
4901
+ // cli/shared/src/output.js
4902
+ function printJsonSummary(summary) {
4903
+ console.log(`__JSON_SUMMARY__${JSON.stringify(summary)}`);
4904
+ }
4905
+ function ok(data) {
4906
+ printJsonSummary({ ok: true, data });
4907
+ }
4908
+ function fail(code, message) {
4909
+ printJsonSummary({ ok: false, error: { code, message } });
4910
+ }
4911
+ function handleError(err) {
4912
+ if (err instanceof NetworkError) {
4913
+ for (const line of err.formatDetailLines()) {
4914
+ console.error(line);
3907
4915
  }
4916
+ fail(err.code, err.message);
4917
+ } else if (err instanceof Error) {
4918
+ const code = err.code ?? "ERR_UNKNOWN";
4919
+ console.error(`\u9519\u8BEF: ${err.message}`);
4920
+ fail(code, err.message);
4921
+ } else {
4922
+ console.error(`\u672A\u77E5\u9519\u8BEF`, err);
4923
+ fail("ERR_UNKNOWN", String(err));
3908
4924
  }
3909
- const orphanInMeta = Object.keys(meta.nodes).filter((u) => !flowUuids.has(u));
3910
- let metaCodeIndexedCount = 0;
3911
- for (const m of Object.values(meta.nodes)) {
3912
- if (m.dir && m.codeFile) metaCodeIndexedCount++;
3913
- }
3914
- const unindexedDirs = [];
3915
- for (const dirName of listNodeDirs(flowDir)) {
3916
- const dirAbs = import_path7.default.join(flowDir, NODES_DIR2, dirName);
3917
- if (!findCodeFileInDir(dirAbs)) continue;
3918
- const rel = import_path7.default.posix.join(NODES_DIR2, dirName);
3919
- const indexed = Object.values(meta.nodes).some((m) => m.dir?.replace(/\\/g, "/") === rel);
3920
- if (!indexed) unindexedDirs.push(rel);
3921
- }
3922
- if (canvasCodeNodeCount !== metaCodeIndexedCount) {
3923
- warnings.push(
3924
- `\u753B\u5E03\u4EE3\u7801\u8282\u70B9 ${canvasCodeNodeCount} \u4E2A\uFF0Cmeta \u5DF2\u7D22\u5F15 ${metaCodeIndexedCount} \u4E2A\uFF08\u4E0D\u4E00\u81F4\u4F1A\u5BFC\u81F4 node push / \u8BBE\u8BA1\u5668\u6253\u5F00\u4EE3\u7801\u5931\u8D25\uFF09`
3925
- );
3926
- }
3927
- if (missingInMeta.length) {
3928
- warnings.push("\u90E8\u5206 flow.json \u4EE3\u7801\u8282\u70B9\u672A\u5199\u5165 flow.meta.json\uFF0C\u8BF7\u6267\u884C project repair-meta");
3929
- }
3930
- if (unindexedDirs.length) {
3931
- warnings.push(`\u5B58\u5728\u672A\u7D22\u5F15\u7684\u8282\u70B9\u76EE\u5F55\uFF1A${unindexedDirs.join(", ")}`);
3932
- }
3933
- const ok3 = missingInMeta.length === 0 && orphanInMeta.length === 0 && missingCodeFiles.length === 0 && unindexedDirs.length === 0 && canvasCodeNodeCount === metaCodeIndexedCount;
4925
+ process.exit(1);
4926
+ }
4927
+
4928
+ // cli/shared/src/workspaceLayout.js
4929
+ var import_path2 = __toESM(require("path"), 1);
4930
+ var WORKSPACE_RESOURCE_DIR = "\u8D44\u6E90";
4931
+ function resolveWorkspace(cwd = process.cwd()) {
4932
+ const resources = import_path2.default.join(cwd, WORKSPACE_RESOURCE_DIR);
3934
4933
  return {
3935
- ok: ok3,
3936
- canvasNodeCount,
3937
- canvasCodeNodeCount,
3938
- metaIndexedCount: Object.keys(meta.nodes).length,
3939
- metaCodeIndexedCount,
3940
- missingInMeta,
3941
- orphanInMeta,
3942
- missingCodeFiles,
3943
- unindexedDirs,
3944
- warnings
4934
+ root: cwd,
4935
+ resources,
4936
+ dazi: import_path2.default.join(cwd, ".dazi"),
4937
+ onto: import_path2.default.join(cwd, "onto"),
4938
+ flows: import_path2.default.join(cwd, "flows"),
4939
+ apps: import_path2.default.join(cwd, "apps"),
4940
+ data: import_path2.default.join(cwd, "data"),
4941
+ docs: import_path2.default.join(resources, "docs"),
4942
+ prompts: import_path2.default.join(resources, "prompts"),
4943
+ examples: import_path2.default.join(resources, "examples"),
4944
+ dataspaces: import_path2.default.join(resources, "dataspaces"),
4945
+ datasources: import_path2.default.join(resources, "datasources"),
4946
+ files: import_path2.default.join(resources, "files")
3945
4947
  };
3946
4948
  }
3947
- function writeNodeInfo(flowDir, node, uuid, nodeType, relDir, codeFile, language, strippedData) {
3948
- const abs = import_path7.default.join(flowDir, relDir);
3949
- import_fs6.default.writeFileSync(
3950
- import_path7.default.join(abs, NODE_INFO_JSON2),
3951
- JSON.stringify(
3952
- {
3953
- _readonly: `\u6B64\u6587\u4EF6\u7531 dazi-flow \u751F\u6210\uFF0C\u4EC5\u4F9B\u67E5\u770B\uFF1B\u7F16\u8F91\u4EE3\u7801\u8BF7\u6539 ${codeFile}\uFF0C\u914D\u7F6E\u6539 flow.json`,
3954
- node_uuid: uuid,
3955
- nodeId: String(node.id ?? ""),
3956
- nodeType,
3957
- label: String(node.data?.label ?? ""),
3958
- codeFile,
3959
- codeLanguage: language,
3960
- position: node.position,
3961
- data: strippedData
3962
- },
3963
- null,
3964
- 2
3965
- ),
3966
- "utf8"
3967
- );
3968
- }
3969
- function repairFlowMeta(flowDir) {
3970
- const warnings = [];
3971
- const repairedUuids = [];
3972
- const wroteNodeInfo = [];
3973
- const doc = readFlowJson2(flowDir);
3974
- const meta = readMeta2(flowDir) ?? { flowId: null, flowName: import_path7.default.basename(flowDir), nodes: {} };
3975
- if (!meta.nodes) meta.nodes = {};
3976
- const usedDirNames = new Set(
3977
- Object.values(meta.nodes).map((m) => m.dir?.split("/").pop()).filter(Boolean)
3978
- );
3979
- for (const node of doc.nodes) {
3980
- const uuid = String(node.node_uuid ?? "").trim();
3981
- if (!uuid) continue;
3982
- const nodeType = resolveNodeType2(node);
3983
- const codeKey = NODE_TYPE_CODE_DATA_KEY2[nodeType];
3984
- const semanticId = String(node.id ?? "");
3985
- const label = String(node.data?.label ?? semanticId ?? nodeType);
3986
- let entry = meta.nodes[uuid];
3987
- if (!entry) {
3988
- entry = { nodeId: semanticId, nodeType };
3989
- meta.nodes[uuid] = entry;
3990
- repairedUuids.push(uuid);
3991
- } else {
3992
- entry.nodeId = semanticId || entry.nodeId;
3993
- entry.nodeType = nodeType;
4949
+
4950
+ // cli/dazi-flow/src/commands/flows.ts
4951
+ function makeFlowsCommand() {
4952
+ const cmd = new Command("flows").description("Flow \u7BA1\u7406");
4953
+ cmd.command("list").description("\u5217\u51FA\u6240\u6709 Flow").option("--space <spaceId>", "\u8FC7\u6EE4\u7A7A\u95F4").option("--status <status>", "\u8FC7\u6EE4\u72B6\u6001\uFF08active/draft/archived\uFF09").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
4954
+ try {
4955
+ const qs = new URLSearchParams();
4956
+ if (opts.space) qs.set("spaceId", opts.space);
4957
+ if (opts.status) qs.set("status", opts.status);
4958
+ const qsStr = qs.toString() ? `?${qs}` : "";
4959
+ const endpoint = `/api/data-pipelines/v1/flows${qsStr}`;
4960
+ const flows = await apiRequest(endpoint);
4961
+ if (opts.json) {
4962
+ console.log(JSON.stringify(flows, null, 2));
4963
+ ok({ flows });
4964
+ return;
4965
+ }
4966
+ if (!flows.length) {
4967
+ console.log("\uFF08\u6682\u65E0 Flow\uFF09");
4968
+ ok({ flows: [] });
4969
+ return;
4970
+ }
4971
+ for (const f of flows) {
4972
+ console.log(` ${String(f.id).padEnd(8)} ${(f.name ?? "").padEnd(40)} [${f.status ?? "\u2014"}]`);
4973
+ }
4974
+ ok({ flows });
4975
+ } catch (err) {
4976
+ handleError(err);
3994
4977
  }
3995
- if (!codeKey) continue;
3996
- let relDir = entry.dir?.replace(/\\/g, "/");
3997
- if (relDir && findCodeFileInDir(import_path7.default.join(flowDir, relDir))) {
3998
- } else {
3999
- relDir = findDirForUuid(flowDir, uuid);
4978
+ });
4979
+ cmd.command("get <flowId>").alias("show").description("\u67E5\u770B Flow \u8BE6\u60C5").option("--json", "\u8F93\u51FA JSON").action(async (flowId, opts) => {
4980
+ try {
4981
+ const flow = await apiRequest(`/api/data-pipelines/v1/flows/${flowId}`);
4982
+ if (opts.json || true) console.log(JSON.stringify(flow, null, 2));
4983
+ ok({ flow });
4984
+ } catch (err) {
4985
+ handleError(err);
4000
4986
  }
4001
- if (!relDir) {
4002
- const byLabel = import_path7.default.posix.join(NODES_DIR2, sanitizeName2(label));
4003
- if (import_fs6.default.existsSync(import_path7.default.join(flowDir, byLabel)) && findCodeFileInDir(import_path7.default.join(flowDir, byLabel))) {
4004
- relDir = byLabel;
4987
+ });
4988
+ cmd.command("create").alias("new").description("\u65B0\u5EFA Flow").requiredOption("--name <name>", "Flow \u540D\u79F0").option("--space <spaceId>", "\u7A7A\u95F4 ID").option("--description <desc>", "\u63CF\u8FF0").option("--file <path>", "\u4ECE\u672C\u5730 JSON \u6587\u4EF6\u8BFB\u53D6\u5B9A\u4E49").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
4989
+ try {
4990
+ let body;
4991
+ if (opts.file) {
4992
+ body = JSON.parse(import_fs2.default.readFileSync(import_path3.default.resolve(opts.file), "utf-8"));
4993
+ } else {
4994
+ body = { name: opts.name, spaceId: opts.space, description: opts.description };
4005
4995
  }
4996
+ const flow = await apiRequest("/api/data-pipelines/v1/flows", { method: "POST", body });
4997
+ if (opts.json) {
4998
+ console.log(JSON.stringify(flow, null, 2));
4999
+ } else {
5000
+ console.log(`\u2705 \u5DF2\u521B\u5EFA Flow: ${flow.id} ${flow.name}`);
5001
+ }
5002
+ ok({ flow });
5003
+ } catch (err) {
5004
+ handleError(err);
4006
5005
  }
4007
- if (!relDir) {
4008
- let dirName = sanitizeName2(label);
4009
- if (usedDirNames.has(dirName)) dirName = `${dirName}_${uuid.slice(0, 6)}`;
4010
- usedDirNames.add(dirName);
4011
- relDir = import_path7.default.posix.join(NODES_DIR2, dirName);
4012
- import_fs6.default.mkdirSync(import_path7.default.join(flowDir, relDir), { recursive: true });
4013
- const lang = codeLanguageForNodeType2(nodeType);
4014
- const codeFile2 = `code.${codeFileExt2(lang)}`;
4015
- const template = nodeType === "excel-python" ? "# -*- coding: utf-8 -*-\n# Excel \u8282\u70B9\uFF1A\u753B\u5E03\u914D\u7F6E managed_file_id\uFF1B\u4EE3\u7801\u4F7F\u7528 excel_source_path\n" : lang === "sql" ? "-- \u8282\u70B9 SQL\nSELECT 1;\n" : "# -*- coding: utf-8 -*-\n";
4016
- if (!findCodeFileInDir(import_path7.default.join(flowDir, relDir))) {
4017
- import_fs6.default.writeFileSync(import_path7.default.join(flowDir, relDir, codeFile2), template, "utf8");
4018
- warnings.push(`\u5DF2\u4E3A ${uuid} \u521B\u5EFA\u7A7A\u4EE3\u7801\u6A21\u677F ${relDir}/${codeFile2}`);
5006
+ });
5007
+ cmd.command("validate <flowId>").description("\u9A8C\u8BC1 Flow \u5B9A\u4E49").option("--file <path>", "\u4ECE\u672C\u5730\u5FEB\u7167\u6587\u4EF6\u9A8C\u8BC1\uFF08\u79BB\u7EBF\uFF09").action(async (flowId, opts) => {
5008
+ try {
5009
+ let result;
5010
+ if (opts.file) {
5011
+ const body = JSON.parse(import_fs2.default.readFileSync(import_path3.default.resolve(opts.file), "utf-8"));
5012
+ result = await apiRequest(`/api/data-pipelines/v1/flows/compile-plan`, { method: "POST", body });
5013
+ } else {
5014
+ result = await apiRequest(`/api/data-pipelines/v1/flows/${flowId}/validate`, { method: "POST" });
4019
5015
  }
5016
+ console.log("\u2705 \u9A8C\u8BC1\u901A\u8FC7");
5017
+ ok({ flowId, result });
5018
+ } catch (err) {
5019
+ handleError(err);
4020
5020
  }
4021
- const dirAbs = import_path7.default.join(flowDir, relDir);
4022
- const codeFile = findCodeFileInDir(dirAbs);
4023
- if (!codeFile) {
4024
- warnings.push(`\u8282\u70B9 ${uuid} \u76EE\u5F55 ${relDir} \u65E0 code.*\uFF0C\u8DF3\u8FC7\u4EE3\u7801\u7D22\u5F15`);
4025
- continue;
5021
+ });
5022
+ cmd.command("patch <flowId>").description("\u90E8\u5206\u66F4\u65B0 Flow \u5C5E\u6027").option("--name <name>", "\u65B0\u540D\u79F0").option("--description <desc>", "\u65B0\u63CF\u8FF0").option("--status <status>", "\u65B0\u72B6\u6001").option("--file <path>", "\u4ECE JSON \u6587\u4EF6\u8BFB\u53D6 patch body").action(async (flowId, opts) => {
5023
+ try {
5024
+ let patch;
5025
+ if (opts.file) {
5026
+ patch = JSON.parse(import_fs2.default.readFileSync(import_path3.default.resolve(opts.file), "utf-8"));
5027
+ } else {
5028
+ patch = {};
5029
+ if (opts.name) patch.name = opts.name;
5030
+ if (opts.description) patch.description = opts.description;
5031
+ if (opts.status) patch.status = opts.status;
5032
+ }
5033
+ const flow = await apiRequest(`/api/data-pipelines/v1/flows/${flowId}`, { method: "PUT", body: patch });
5034
+ console.log(`\u2705 Flow ${flowId} \u5DF2\u66F4\u65B0`);
5035
+ ok({ flow });
5036
+ } catch (err) {
5037
+ handleError(err);
4026
5038
  }
4027
- const language = codeLanguageForNodeType2(nodeType);
4028
- const code = import_fs6.default.readFileSync(import_path7.default.join(dirAbs, codeFile), "utf8");
4029
- entry.dir = relDir;
4030
- entry.codeFile = codeFile;
4031
- entry.codeLanguage = language;
4032
- entry.codeHash = sha12(code);
4033
- const strippedData = { ...node.data ?? {} };
4034
- delete strippedData[codeKey];
4035
- const infoPath = import_path7.default.join(dirAbs, NODE_INFO_JSON2);
4036
- if (!import_fs6.default.existsSync(infoPath)) {
4037
- writeNodeInfo(flowDir, node, uuid, nodeType, relDir, codeFile, language, strippedData);
4038
- wroteNodeInfo.push(relDir);
5039
+ });
5040
+ const nodeCmd = new Command("flow-node").description("Flow \u8282\u70B9\u7BA1\u7406");
5041
+ nodeCmd.command("get <flowId> <nodeId>").description("\u67E5\u770B Flow \u8282\u70B9\u8BE6\u60C5").option("--json", "\u8F93\u51FA JSON").action(async (flowId, nodeId, opts) => {
5042
+ try {
5043
+ const node = await apiRequest(`/api/data-pipelines/v1/flows/${flowId}/flow-nodes/${nodeId}`);
5044
+ console.log(JSON.stringify(node, null, 2));
5045
+ ok({ node });
5046
+ } catch (err) {
5047
+ handleError(err);
4039
5048
  }
4040
- if (!repairedUuids.includes(uuid)) repairedUuids.push(uuid);
4041
- }
4042
- meta.graphFingerprint = "sha1:" + sha12(JSON.stringify({ nodes: doc.nodes, edges: doc.edges }));
4043
- import_fs6.default.writeFileSync(import_path7.default.join(flowDir, FLOW_META_JSON2), JSON.stringify(meta, null, 2), "utf8");
4044
- const okAfter = auditFlowWorkspace(flowDir).ok;
4045
- return {
4046
- ok: okAfter,
4047
- repairedUuids: [...new Set(repairedUuids)],
4048
- wroteNodeInfo,
4049
- warnings
4050
- };
4051
- }
4052
- function buildFlowConsistencyMarkdown(audit, localFilesSpecLink = "../../\u8D44\u6E90/docs/flow/local-files-spec.md") {
4053
- const lines = [
4054
- "## \u672C\u5730\u6587\u4EF6\u4E00\u81F4\u6027",
4055
- "",
4056
- "| \u6307\u6807 | \u6570\u91CF |",
4057
- "|------|------|",
4058
- `| \u753B\u5E03\u8282\u70B9\uFF08flow.json\uFF09 | ${audit.canvasNodeCount} |`,
4059
- `| \u753B\u5E03\u4EE3\u7801\u8282\u70B9\uFF08flow.json\uFF09 | ${audit.canvasCodeNodeCount} |`,
4060
- `| meta \u5DF2\u767B\u8BB0\u8282\u70B9 | ${audit.metaIndexedCount} |`,
4061
- `| meta \u5DF2\u7D22\u5F15\u4EE3\u7801\uFF08dir+codeFile\uFF09 | ${audit.metaCodeIndexedCount} |`,
4062
- ""
4063
- ];
4064
- if (audit.ok) {
4065
- lines.push("\u2705 **flow.json / flow.meta.json / \u8282\u70B9/ \u4E00\u81F4**\u3002\u8BBE\u8BA1\u5668\u53EF\u6B63\u5E38\u300C\u6253\u5F00\u4EE3\u7801\u300D\uFF0C`node push` \u53EF\u8BC6\u522B\u810F\u8282\u70B9\u3002", "");
4066
- } else {
4067
- lines.push("\u26A0\uFE0F **\u76EE\u5F55\u4E0D\u4E00\u81F4**\uFF08\u4F1A\u5BFC\u81F4\u8BBE\u8BA1\u5668\u627E\u4E0D\u5230\u4EE3\u7801\u3001\u6216 `node push` \u8DF3\u8FC7\uFF09\uFF1A", "");
4068
- if (audit.missingInMeta.length) {
4069
- lines.push("| uuid | \u8282\u70B9 | \u7C7B\u578B |");
4070
- lines.push("|------|------|------|");
4071
- for (const m of audit.missingInMeta) {
4072
- lines.push(`| \`${m.uuid.slice(0, 8)}\u2026\` | ${m.label || m.nodeId} | ${m.nodeType} |`);
5049
+ });
5050
+ nodeCmd.command("patch <flowId> <nodeId>").description("\u90E8\u5206\u66F4\u65B0 Flow \u8282\u70B9\u914D\u7F6E").option("--file <path>", "\u4ECE JSON \u6587\u4EF6\u8BFB\u53D6 patch body").option("--config <json>", "config patch JSON \u5B57\u7B26\u4E32").action(async (flowId, nodeId, opts) => {
5051
+ try {
5052
+ let patch;
5053
+ if (opts.file) {
5054
+ patch = JSON.parse(import_fs2.default.readFileSync(import_path3.default.resolve(opts.file), "utf-8"));
5055
+ } else if (opts.config) {
5056
+ patch = { config: JSON.parse(opts.config) };
5057
+ } else {
5058
+ console.error("--file \u6216 --config \u5FC5\u987B\u63D0\u4F9B");
5059
+ process.exit(1);
4073
5060
  }
4074
- lines.push("");
4075
- }
4076
- if (audit.unindexedDirs.length) {
4077
- lines.push(`- \u672A\u7D22\u5F15\u76EE\u5F55\uFF1A${audit.unindexedDirs.map((d) => `\`${d}\``).join(" ")}`);
5061
+ const node = await apiRequest(
5062
+ `/api/data-pipelines/v1/flows/${flowId}/flow-nodes/${nodeId}`,
5063
+ { method: "PATCH", body: patch }
5064
+ );
5065
+ console.log(`\u2705 \u8282\u70B9 ${nodeId} \u5DF2\u66F4\u65B0`);
5066
+ ok({ node });
5067
+ } catch (err) {
5068
+ handleError(err);
4078
5069
  }
4079
- if (audit.orphanInMeta.length) {
4080
- lines.push(`- meta \u591A\u4F59 uuid\uFF1A${audit.orphanInMeta.length} \u4E2A\uFF08flow.json \u4E2D\u5DF2\u4E0D\u5B58\u5728\uFF09`);
5070
+ });
5071
+ cmd.addCommand(nodeCmd);
5072
+ cmd.command("workspace-init <flowId>").description("\u521D\u59CB\u5316 Flow \u672C\u5730\u5DE5\u4F5C\u76EE\u5F55 flows/<flowId>/").action(async (flowId) => {
5073
+ try {
5074
+ const ws = resolveWorkspace();
5075
+ const flowDir = import_path3.default.join(ws.flows, flowId);
5076
+ const dirs = [flowDir, import_path3.default.join(flowDir, "plans"), import_path3.default.join(flowDir, "data")];
5077
+ for (const d of dirs) {
5078
+ if (!import_fs2.default.existsSync(d)) import_fs2.default.mkdirSync(d, { recursive: true });
5079
+ }
5080
+ const metaPath = import_path3.default.join(flowDir, ".dazi-flow.json");
5081
+ if (!import_fs2.default.existsSync(metaPath)) {
5082
+ import_fs2.default.writeFileSync(metaPath, JSON.stringify({ flowId, initializedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), "utf-8");
5083
+ }
5084
+ console.log(`\u2705 Flow \u5DE5\u4F5C\u76EE\u5F55\u5DF2\u521D\u59CB\u5316: ${flowDir}`);
5085
+ ok({ flowId, dir: flowDir });
5086
+ } catch (err) {
5087
+ handleError(err);
4081
5088
  }
4082
- for (const w of audit.warnings) lines.push(`- ${w}`);
4083
- lines.push(
4084
- "",
4085
- "\u4FEE\u590D\uFF08\u5728\u6D41\u7A0B\u76EE\u5F55\uFF09\uFF1A",
4086
- "",
4087
- "```powershell",
4088
- "dazi flow project repair-meta --dir .",
4089
- "dazi flow project doctor --dir .",
4090
- "```",
4091
- "",
4092
- `\u8BE6\u89C1 [\u6D41\u7A0B\u672C\u5730\u6587\u4EF6\u89C4\u8303](${localFilesSpecLink})\u3002`,
4093
- ""
4094
- );
4095
- }
4096
- lines.push(
4097
- "## \u53D8\u66F4\u63D0\u4EA4\u547D\u4EE4\u77E9\u9635",
4098
- "",
4099
- "| \u6539\u4E86\u4EC0\u4E48 | \u547D\u4EE4 |",
4100
- "|----------|------|",
4101
- "| `managed_file_id`\u3001`output_variable_name`\u3001\u8FDE\u7EBF\u3001\u589E\u5220\u8282\u70B9 | `dazi flow project push --dir . --canvas` |",
4102
- "| `\u8282\u70B9/<\u540D>/code.py` \u6216 `code.sql` | `dazi flow node push --node <uuid> --dir .` |",
4103
- '| **\u65B0\u589E\u4EE3\u7801\u8282\u70B9** | `dazi flow node new --type <type> --dir . --label "<\u540D>"` \u2192 \u6539\u914D\u7F6E \u2192 `push --canvas` \u2192 \u5199 code \u2192 `node push` |',
4104
- "",
4105
- "> \u7981\u6B62\u53EA\u6539 `flow.json` \u800C\u4E0D\u66F4\u65B0 `flow.meta.json`\uFF08\u987B `node new` / `pull` / `repair-meta`\uFF09\u3002",
4106
- ""
4107
- );
4108
- return lines;
5089
+ });
5090
+ return cmd;
4109
5091
  }
4110
5092
 
4111
- // src/shared/flowScaffoldDocs.ts
4112
- var DEPTH = {
4113
- "project-root": 2,
4114
- "flows-dir": 3,
4115
- "flow-dir": 4
5093
+ // cli/dazi-flow/src/commands/snapshot.ts
5094
+ var import_path6 = __toESM(require("path"), 1);
5095
+ var import_fs5 = __toESM(require("fs"), 1);
5096
+
5097
+ // cli/dazi-flow/src/lib/flowLocal.ts
5098
+ var import_crypto = __toESM(require("crypto"), 1);
5099
+ var import_fs3 = __toESM(require("fs"), 1);
5100
+ var import_path4 = __toESM(require("path"), 1);
5101
+ var NODE_TYPE_CODE_DATA_KEY = {
5102
+ "database-source": "sql",
5103
+ "dataspace-source": "sql",
5104
+ "python-script": "pythonCode",
5105
+ "excel-python": "pythonCode",
5106
+ "sql-query": "sql",
5107
+ condition: "pythonCode",
5108
+ "data-quality-check": "dqPythonCode"
4116
5109
  };
4117
- function flowDocsRel(anchor, docPath) {
4118
- const prefix = "../".repeat(DEPTH[anchor]);
4119
- return `${prefix}\u8D44\u6E90/docs/${docPath}`;
5110
+ function codeLanguageForNodeType(nodeType) {
5111
+ if (["python-script", "excel-python", "condition", "data-quality-check"].includes(nodeType)) {
5112
+ return "python";
5113
+ }
5114
+ if (["database-source", "dataspace-source", "sql-query"].includes(nodeType)) {
5115
+ return "sql";
5116
+ }
5117
+ return void 0;
4120
5118
  }
4121
- var SCAFFOLD_MARKER = "<!-- dazi-flow-scaffold -->";
4122
- var LEGACY_FLOW_DIR_QUICKSTART_FILENAME = "\u5FEB\u901F\u542F\u52A8.md";
4123
- function sanitizeFlowNameForFilename(flowName) {
4124
- const safe = flowName.trim().replace(/[\\/:*?"<>|]/g, "_").replace(/\s+/g, "_").replace(/_+/g, "_").replace(/^\.+/, "").slice(0, 80);
4125
- return safe || "flow";
5119
+ function codeFileExt(language) {
5120
+ if (language === "python") return "py";
5121
+ if (language === "sql") return "sql";
5122
+ return "txt";
4126
5123
  }
4127
- function flowDirQuickStartFilename(flowName) {
4128
- return `\u5FEB\u901F\u542F\u52A8_${sanitizeFlowNameForFilename(flowName)}.md`;
5124
+ var NODES_DIR = "\u8282\u70B9";
5125
+ var VARIABLES_DIR = "\u53D8\u91CF";
5126
+ var RUN_DIR = "_run";
5127
+ var FLOW_JSON = "flow.json";
5128
+ var FLOW_META_JSON = "flow.meta.json";
5129
+ var NODE_INFO_JSON = "node.info.json";
5130
+ function resolveNodeType(node) {
5131
+ const nt = node.type;
5132
+ if (nt === "custom") {
5133
+ const data = node.data ?? {};
5134
+ return String(data.type ?? "custom");
5135
+ }
5136
+ return String(nt ?? "custom");
4129
5137
  }
4130
- function isFlowScaffoldFile(content) {
4131
- return content.includes(SCAFFOLD_MARKER);
5138
+ function sha1(text) {
5139
+ return import_crypto.default.createHash("sha1").update(text, "utf8").digest("hex");
4132
5140
  }
4133
- function isLegacyFlowScaffoldFile(content) {
4134
- if (isFlowScaffoldFile(content)) return false;
4135
- return content.includes("dazi-flow project pull") || content.includes("dazi-flow node push") || content.includes("\u89C1 \xA7335") || content.includes("# \u6D41\u7A0B") && content.includes("dazi-flow project pull");
5141
+ function sanitizeName(name) {
5142
+ const cleaned = (name || "").replace(/[<>:"/\\|?*\x00-\x1f]/g, "_").trim().replace(/\s+/g, "_");
5143
+ return cleaned || "node";
4136
5144
  }
4137
- function buildDocsTable(anchor) {
4138
- const rows = [
4139
- ["Flow \u6587\u6863\u7D22\u5F15\uFF08\u4ECE\u8FD9\u91CC\u5F00\u59CB\uFF09", "flow/flows-guide.md"],
4140
- ["\u6570\u636E\u6D41\u7A0B\u9879\u76EE\u5F00\u53D1\u6307\u5357", "flow/flow-project-guide.md"],
4141
- ["\u6D41\u7A0B\u53D8\u91CF\u7CFB\u7EDF\u6307\u5357", "flow/variables-guide.md"],
4142
- ["\u8282\u70B9\u4EE3\u7801\u7F16\u5199\u6307\u5357\uFF08\u542B SQL/Python \u793A\u4F8B\uFF09", "flow/node-code-guide.md"],
4143
- ["Flow \u8FD0\u884C\u4E0E\u6D4B\u8BD5", "flow/run-guide.md"],
4144
- ["\u6570\u636E\u6E90\u4E0E connectionId", "flow/source-guide.md"],
4145
- ["**\u6D41\u7A0B\u672C\u5730\u6587\u4EF6\u89C4\u8303\uFF08AI \u5FC5\u8BFB\uFF09**", "flow/local-files-spec.md"],
4146
- ["\u6D41\u7A0B AI \u5DE5\u4F5C\u624B\u518C", "flow/ai-workflow-playbook.md"],
4147
- ["CLI \u8C03\u7528\u7EA6\u5B9A\uFF08`dazi` / `dazi.ps1`\uFF09", "guides/cli-invocation.md"]
4148
- ];
4149
- return [
4150
- "## \u5E2E\u52A9\u6587\u6863",
4151
- "",
4152
- "\u5DE5\u4F5C\u533A\u6587\u6863\u4F4D\u4E8E **`\u8D44\u6E90/docs/`**\uFF08\u6269\u5C55\u4FA7\u680F **\u5E2E\u52A9 \u2192 \u4E0B\u8F7D\u6240\u6709\u6587\u6863**\uFF0C\u6216\u5728\u5DE5\u4F5C\u533A\u6839\u6267\u884C `dazi docs sync`\uFF09\uFF1A",
4153
- "",
4154
- "| \u6587\u6863 | \u94FE\u63A5 |",
4155
- "|------|------|",
4156
- ...rows.map(([title, p]) => `| ${title} | [${p.split("/").pop()}](${flowDocsRel(anchor, p)}) |`),
4157
- "",
4158
- "\u7EC8\u7AEF\u6253\u5F00\u6587\u6863\uFF08**dazi-work \u6839**\u6216\u5DF2 `cd` \u5230\u6D41\u7A0B\u76EE\u5F55\uFF09\uFF1A",
4159
- "",
4160
- "```powershell",
4161
- psDaziLine("docs open flow/flow-project-guide"),
4162
- psDaziLine("docs open flow/variables-guide"),
4163
- psDaziLine("docs open flow/node-code-guide"),
4164
- psDaziLine("docs open flow/flows-guide"),
4165
- "```",
4166
- "",
4167
- "**\u4EE3\u7801\u793A\u4F8B**\uFF1A\u53D8\u91CF\u8BFB\u5199\u3001`python-script` / `sql-query` \u6A21\u677F\u89C1 [\u8282\u70B9\u4EE3\u7801\u7F16\u5199\u6307\u5357](" + flowDocsRel(anchor, "flow/node-code-guide.md") + ") \u4E0E [\u6D41\u7A0B\u53D8\u91CF\u7CFB\u7EDF\u6307\u5357](" + flowDocsRel(anchor, "flow/variables-guide.md") + ")\u3002",
4168
- ""
4169
- ];
5145
+ function readMeta(flowDir) {
5146
+ const p = import_path4.default.join(flowDir, FLOW_META_JSON);
5147
+ if (!import_fs3.default.existsSync(p)) return void 0;
5148
+ try {
5149
+ return JSON.parse(import_fs3.default.readFileSync(p, "utf8"));
5150
+ } catch {
5151
+ return void 0;
5152
+ }
4170
5153
  }
4171
- function cliPrefix() {
4172
- return `\`${DAZI_CLI} flow\`\uFF08\u5168\u5C40 CLI\uFF1B\u5728 **dazi-work \u6839** \u6216\u6D41\u7A0B\u76EE\u5F55\u52A0 \`--dir .\`\uFF1B\u672A\u88C5\u5168\u5C40\u65F6\u53EF\u7528 \`.scriptsdazi.ps1 flow\`\uFF09`;
5154
+ function writeMeta(flowDir, meta) {
5155
+ import_fs3.default.writeFileSync(import_path4.default.join(flowDir, FLOW_META_JSON), JSON.stringify(meta, null, 2), "utf8");
4173
5156
  }
4174
- function buildFlowQuickStartMarkdown(opts) {
4175
- const folder = opts.projectFolder;
4176
- return [
4177
- SCAFFOLD_MARKER,
4178
- "# \u5FEB\u901F\u542F\u52A8",
4179
- "",
4180
- `\u6B22\u8FCE\u5F00\u59CB **\u6570\u636E\u6D41\u7A0B\u9879\u76EE** \u5F00\u53D1\uFF08\`${folder}\`\uFF09\u3002`,
4181
- "",
4182
- "## 1. \u6D41\u7A0B\u76EE\u5F55\u7ED3\u6784",
4183
- "",
4184
- "\u6BCF\u4E2A\u6D41\u7A0B\u662F `\u6D41\u7A0B/<\u6D41\u7A0B\u540D>/` \u76EE\u5F55\uFF1A",
4185
- "",
4186
- "| \u8DEF\u5F84 | \u8BF4\u660E |",
4187
- "|------|------|",
4188
- "| `flow.json` | \u753B\u5E03\u771F\u7406\u6E90\uFF08\u8282\u70B9\u914D\u7F6E + \u8FDE\u7EBF\uFF0C\u4EE3\u7801\u5DF2\u5265\u79BB\uFF09 |",
4189
- "| `flow.meta.json` | flowId\u3001node_uuid \u6620\u5C04\uFF08CLI \u7EF4\u62A4\uFF0C\u52FF\u624B\u6539\uFF09 |",
4190
- "| `\u8282\u70B9/<\u540D>/code.*` | SQL / Python \u8282\u70B9\u4EE3\u7801 |",
4191
- "| `\u53D8\u91CF/<\u540D>.json` | \u8C03\u8BD5 Run \u53D8\u91CF\u9884\u89C8\uFF08\u53EA\u8BFB\uFF0Cpull/sync \u751F\u6210\uFF09 |",
4192
- "| `_run/` | \u6D4B\u8BD5/\u8FD0\u884C\u4EA7\u7269\uFF08`*.last-error.md` \u7B49\uFF09 |",
4193
- "",
4194
- "> \u6D41\u7A0B**\u4E0D\u7ED1\u5B9A\u6570\u636E\u7A7A\u95F4**\uFF1B`connectionId` \u4EC5\u4E3A\u6570\u636E\u5E93\u8282\u70B9\u7684\u914D\u7F6E\u5B57\u6BB5\u3002",
4195
- "",
4196
- "## 2. \u65B0\u5EFA / \u62C9\u53D6\u6D41\u7A0B",
4197
- "",
4198
- "\u8D44\u6E90\u7BA1\u7406\u5668\uFF08\u63A8\u8350 **\u8868\u5355**\uFF09\uFF1A",
4199
- "",
4200
- "| \u64CD\u4F5C | \u83DC\u5355 |",
4201
- "|------|------|",
4202
- "| \u65B0\u5EFA\u6D41\u7A0B | \u53F3\u952E **`\u6D41\u7A0B/`** \u2192 **\u65B0\u5EFA\u6D41\u7A0B** |",
4203
- "| \u62C9\u53D6\u5E73\u53F0\u6D41\u7A0B | \u53F3\u952E **`\u6D41\u7A0B/`** \u6216\u9879\u76EE\u6839 \u2192 **\u62C9\u53D6\u5E73\u53F0\u6D41\u7A0B** |",
4204
- "",
4205
- "\u62C9\u53D6\u8868\u5355\u53EF\u4ECE\u5E73\u53F0 Flow \u5217\u8868\u9009\u62E9\uFF0C\u6216\u624B\u52A8\u586B\u5199 Flow ID\uFF1B\u65B0\u5EFA\u8868\u5355\u586B\u5199\u540D\u79F0\u4E0E\u8BF4\u660E\u540E\u81EA\u52A8\u521B\u5EFA\u5E76\u62C9\u53D6\u3002",
4206
- "",
4207
- "\u547D\u4EE4\u884C\uFF08`dazi-work` \u6839\uFF09\uFF1A",
4208
- "",
4209
- "```powershell",
4210
- psFlowLine(`project pull --flow <flowId> --dir "\u9879\u76EE\\${folder}\\\u6D41\u7A0B\\<\u6D41\u7A0B\u540D>"`),
4211
- "```",
4212
- "",
4213
- "\u62C9\u53D6\u540E\u6BCF\u4E2A\u6D41\u7A0B\u76EE\u5F55\u4F1A\u751F\u6210 **`\u5FEB\u901F\u542F\u52A8_<\u6D41\u7A0B\u540D>.md`**\uFF08\u542B flowId \u4E0E\u672C\u6D41\u7A0B\u5E38\u7528\u547D\u4EE4\uFF0C\u4FBF\u4E8E @ \u9644\u52A0\u7ED9 AI\uFF09\u3002",
4214
- "",
4215
- "## 3. \u5F00\u53D1\u4E0E\u63D0\u4EA4",
4216
- "",
4217
- "1. \u7F16\u8F91 `\u8282\u70B9/<\u540D>/code.sql` \u6216 `code.py`",
4218
- "2. \u53F3\u952E **\u6D4B\u8BD5\u8FD0\u884C\u8282\u70B9** \u6216 `flow run node-exec --node <uuid>`",
4219
- "3. \u53F3\u952E **\u63D0\u4EA4\u8282\u70B9** / **\u63D0\u4EA4\u6D41\u7A0B** \u6216 `flow node push` / `flow project push --canvas`",
4220
- "4. \u67E5\u770B\u8868\u53D8\u91CF\uFF1A\u8BBE\u8BA1\u5668 **output_variable_name** \u65C1 \u{1F4CA}\uFF0C\u6216 `flow variable pull --name <\u540D>`",
4221
- "",
4222
- "\u547D\u4EE4\u524D\u7F00\uFF1A" + cliPrefix(),
4223
- "",
4224
- "## 4. \u63A8\u8350\u5F00\u53D1\u5FAA\u73AF",
4225
- "",
4226
- "\u8BE6\u89C1 [\u6570\u636E\u6D41\u7A0B\u9879\u76EE\u5F00\u53D1\u6307\u5357](" + flowDocsRel("project-root", "flow/flow-project-guide.md") + ") \xA74\u3002",
4227
- "",
4228
- "## 5. AI \u63D0\u793A\u8BCD\u63A8\u8350",
4229
- "",
4230
- "| \u573A\u666F | \u63D0\u793A\u8BCD ID |",
4231
- "|------|-----------|",
4232
- "| \u8BBE\u8BA1/\u65B0\u589E\u6D41\u7A0B | `flow/flow-design` |",
4233
- "| **\u8FD0\u884C\u5931\u8D25\u3001\u81EA\u4E3B\u6539\u9519** | **`flow/run-fix-loop`**\uFF08\u2B50 \u9996\u9009\uFF09 |",
4234
- "| \u5206\u6790\u7528\u6237\u7C98\u8D34\u7684\u9519\u8BEF | `flow/run-debug` |",
4235
- "",
4236
- "\u4FA7\u680F **\u5E2E\u52A9 \u2192 \u{1F916} \u63D0\u793A\u8BCD** \u4E2D \u2B50 \u4E3A\u6D41\u7A0B\u63A8\u8350\u3002\u53F3\u952E **`flow.json`** \u2192 **\u6253\u5F00 AI \u6539\u9519\u63D0\u793A\u8BCD**\u3002",
4237
- "\u7ED9 AI \u7684\u4EFB\u52A1\u793A\u4F8B\uFF1A\u300C\u6539 XX \u8282\u70B9\uFF0C\u81EA\u5DF1\u8DD1\u6D4B\u8BD5\uFF0C\u5931\u8D25\u6309 last-error \u6539\u5230\u901A\u8FC7\u518D push\u300D\u3002",
4238
- "",
4239
- ...buildDocsTable("project-root")
4240
- ].join("\n");
5157
+ function extractDocument(snapshot) {
5158
+ const doc = snapshot.document ?? snapshot;
5159
+ const nodes = Array.isArray(doc.nodes) ? doc.nodes : [];
5160
+ const edges = Array.isArray(doc.edges) ? doc.edges : [];
5161
+ return { nodes, edges };
4241
5162
  }
4242
- function buildFlowsDirReadmeMarkdown(opts) {
4243
- return [
4244
- SCAFFOLD_MARKER,
4245
- "# \u6D41\u7A0B",
4246
- "",
4247
- "\u6BCF\u4E2A\u5B50\u76EE\u5F55\u662F\u4E00\u4E2A\u6D41\u7A0B\uFF08\u542B `flow.json`\u3001`flow.meta.json`\uFF09\u3002",
4248
- "",
4249
- "## \u64CD\u4F5C",
4250
- "",
4251
- "| \u64CD\u4F5C | \u65B9\u5F0F |",
4252
- "|------|------|",
4253
- "| \u65B0\u5EFA\u6D41\u7A0B | \u53F3\u952E\u672C\u76EE\u5F55 \u2192 **\u65B0\u5EFA\u6D41\u7A0B**\uFF08\u8868\u5355\uFF1A\u540D\u79F0\u3001\u8BF4\u660E\u3001\u8DEF\u5F84\u9884\u89C8\uFF09 |",
4254
- "| \u62C9\u53D6\u5E73\u53F0\u6D41\u7A0B | \u53F3\u952E \u2192 **\u62C9\u53D6\u5E73\u53F0\u6D41\u7A0B**\uFF08\u8868\u5355\uFF1A\u5E73\u53F0\u5217\u8868 / Flow ID\u3001\u672C\u5730\u76EE\u5F55\u540D\uFF09 |",
4255
- "| \u6253\u5F00\u8BBE\u8BA1\u5668 | \u53F3\u952E `flow.json` \u2192 **\u6253\u5F00\u6D41\u7A0B\u8BBE\u8BA1\u5668** |",
4256
- "",
4257
- "```powershell",
4258
- "# \u547D\u4EE4\u884C\u62C9\u53D6\uFF08dazi-work \u6839\uFF09",
4259
- psFlowLine(`project pull --flow <id> --dir "\u9879\u76EE\\${opts.projectFolder}\\\u6D41\u7A0B\\<\u6D41\u7A0B\u540D>"`),
4260
- "```",
4261
- "",
4262
- "\u62C9\u53D6\u5B8C\u6210\u540E\u8BF7\u9605\u8BFB\u5404\u6D41\u7A0B\u5B50\u76EE\u5F55\u4E0B\u7684 **`\u5FEB\u901F\u542F\u52A8_<\u6D41\u7A0B\u540D>.md`**\u3002",
4263
- "",
4264
- ...buildDocsTable("flows-dir")
4265
- ].join("\n");
5163
+ function splitDocumentToFiles(flowDir, doc) {
5164
+ const nodesDir = import_path4.default.join(flowDir, NODES_DIR);
5165
+ const strippedNodes = [];
5166
+ const nodeMetas = {};
5167
+ const usedDirNames = /* @__PURE__ */ new Set();
5168
+ for (const node of doc.nodes) {
5169
+ if (!node || typeof node !== "object") continue;
5170
+ const nodeUuid = String(node.node_uuid ?? "").trim();
5171
+ const nodeType = resolveNodeType(node);
5172
+ const semanticId = String(node.id ?? "");
5173
+ const codeKey = NODE_TYPE_CODE_DATA_KEY[nodeType];
5174
+ const stripped = JSON.parse(JSON.stringify(node));
5175
+ const meta = { nodeId: semanticId, nodeType };
5176
+ if (codeKey && nodeUuid) {
5177
+ const data = stripped.data ?? {};
5178
+ const rawCode = data[codeKey];
5179
+ const codeBody = typeof rawCode === "string" ? rawCode : rawCode != null ? JSON.stringify(rawCode) : "";
5180
+ delete data[codeKey];
5181
+ stripped.data = data;
5182
+ const language = codeLanguageForNodeType(nodeType);
5183
+ const ext = codeFileExt(language);
5184
+ const label = String(node.data?.label ?? "") || semanticId || nodeType;
5185
+ let dirName = sanitizeName(label);
5186
+ if (usedDirNames.has(dirName)) {
5187
+ dirName = `${dirName}_${nodeUuid.slice(0, 6)}`;
5188
+ }
5189
+ usedDirNames.add(dirName);
5190
+ const nodeDirAbs = import_path4.default.join(nodesDir, dirName);
5191
+ import_fs3.default.mkdirSync(nodeDirAbs, { recursive: true });
5192
+ const codeFile = `code.${ext}`;
5193
+ import_fs3.default.writeFileSync(import_path4.default.join(nodeDirAbs, codeFile), codeBody, "utf8");
5194
+ import_fs3.default.writeFileSync(
5195
+ import_path4.default.join(nodeDirAbs, NODE_INFO_JSON),
5196
+ JSON.stringify(
5197
+ {
5198
+ _readonly: "\u6B64\u6587\u4EF6\u7531 dazi-flow \u751F\u6210\uFF0C\u4EC5\u4F9B\u67E5\u770B\uFF1B\u7F16\u8F91\u4EE3\u7801\u8BF7\u6539 " + codeFile + "\uFF0C\u914D\u7F6E\u6539 flow.json",
5199
+ node_uuid: nodeUuid,
5200
+ nodeId: semanticId,
5201
+ nodeType,
5202
+ label,
5203
+ codeFile,
5204
+ codeLanguage: language,
5205
+ position: node.position,
5206
+ data: stripped.data
5207
+ },
5208
+ null,
5209
+ 2
5210
+ ),
5211
+ "utf8"
5212
+ );
5213
+ meta.dir = import_path4.default.posix.join(NODES_DIR, dirName);
5214
+ meta.codeFile = codeFile;
5215
+ meta.codeLanguage = language;
5216
+ meta.codeHash = sha1(codeBody);
5217
+ }
5218
+ if (nodeUuid) {
5219
+ nodeMetas[nodeUuid] = meta;
5220
+ }
5221
+ strippedNodes.push(stripped);
5222
+ }
5223
+ const strippedDoc = { nodes: strippedNodes, edges: doc.edges };
5224
+ import_fs3.default.writeFileSync(import_path4.default.join(flowDir, FLOW_JSON), JSON.stringify(strippedDoc, null, 2), "utf8");
5225
+ return { strippedDoc, nodeMetas };
4266
5226
  }
4267
- function buildFlowDirQuickStartMarkdown(opts) {
4268
- const name = opts.flowName;
4269
- let audit;
4270
- if (opts.flowDir) {
4271
- try {
4272
- audit = auditFlowWorkspace(opts.flowDir);
4273
- } catch {
5227
+ function fingerprintDoc(doc) {
5228
+ return "sha1:" + sha1(JSON.stringify({ nodes: doc.nodes, edges: doc.edges }));
5229
+ }
5230
+ function readFlowJson(flowDir) {
5231
+ const p = import_path4.default.join(flowDir, FLOW_JSON);
5232
+ const raw = JSON.parse(import_fs3.default.readFileSync(p, "utf8"));
5233
+ return {
5234
+ nodes: Array.isArray(raw.nodes) ? raw.nodes : [],
5235
+ edges: Array.isArray(raw.edges) ? raw.edges : []
5236
+ };
5237
+ }
5238
+ function readNodeCode(flowDir, meta) {
5239
+ if (!meta.dir || !meta.codeFile) return void 0;
5240
+ const p = import_path4.default.join(flowDir, meta.dir, meta.codeFile);
5241
+ if (!import_fs3.default.existsSync(p)) return void 0;
5242
+ return import_fs3.default.readFileSync(p, "utf8");
5243
+ }
5244
+ function assembleFullDocument(flowDir, meta) {
5245
+ const doc = readFlowJson(flowDir);
5246
+ for (const node of doc.nodes) {
5247
+ const uuid = String(node.node_uuid ?? "");
5248
+ const nodeMeta = meta.nodes[uuid];
5249
+ if (!nodeMeta) continue;
5250
+ const codeKey = NODE_TYPE_CODE_DATA_KEY[nodeMeta.nodeType];
5251
+ if (!codeKey) continue;
5252
+ const code = readNodeCode(flowDir, nodeMeta);
5253
+ if (code != null) {
5254
+ node.data = { ...node.data ?? {}, [codeKey]: code };
5255
+ }
5256
+ }
5257
+ return doc;
5258
+ }
5259
+ function listDirtyCodeNodes(flowDir, meta) {
5260
+ const dirty = [];
5261
+ for (const [uuid, nodeMeta] of Object.entries(meta.nodes)) {
5262
+ if (!nodeMeta.codeFile) continue;
5263
+ const code = readNodeCode(flowDir, nodeMeta);
5264
+ if (code == null) continue;
5265
+ if (sha1(code) !== (nodeMeta.codeHash ?? "")) {
5266
+ dirty.push(uuid);
4274
5267
  }
4275
5268
  }
4276
- const canvasN = audit?.canvasNodeCount ?? opts.nodeCount;
4277
- const codeN = audit?.canvasCodeNodeCount ?? opts.codeNodeCount;
4278
- const metaCodeN = audit?.metaCodeIndexedCount;
4279
- const statsParts = [];
4280
- if (canvasN != null) {
4281
- let line = `
4282
- | \u753B\u5E03\u8282\u70B9\uFF08flow.json\uFF09 | ${canvasN}`;
4283
- if (codeN != null) line += `\uFF0C\u5176\u4E2D\u4EE3\u7801\u8282\u70B9 ${codeN}`;
4284
- line += " |";
4285
- statsParts.push(line);
5269
+ return dirty;
5270
+ }
5271
+
5272
+ // cli/dazi-flow/src/lib/flowApi.ts
5273
+ var import_path5 = __toESM(require("path"), 1);
5274
+ var import_fs4 = __toESM(require("fs"), 1);
5275
+ var FLOW_API = "/api/data-pipelines/v1/flows";
5276
+ async function pushCodeNode(flowId, flowDir, meta, uuid, opts = {}) {
5277
+ const nodeMeta = meta.nodes[uuid];
5278
+ if (!nodeMeta) return { uuid, status: "skipped" };
5279
+ const localCode = readNodeCode(flowDir, nodeMeta);
5280
+ if (localCode == null) return { uuid, status: "skipped" };
5281
+ const nodeLabel = nodeMeta.nodeId || uuid;
5282
+ const remote = await apiRequest(`${FLOW_API}/${flowId}/flow-nodes/${uuid}`, {
5283
+ label: `\u8BFB\u53D6\u8FDC\u7AEF\u8282\u70B9\u4EE3\u7801 (nodeId=${nodeLabel}, uuid=${uuid})`
5284
+ });
5285
+ const remoteHash = sha1(remote.code_body ?? "");
5286
+ const baseHash = nodeMeta.codeHash ?? "";
5287
+ if (!opts.force && baseHash && remoteHash !== baseHash) {
5288
+ return { uuid, status: "conflict", version: remote.version };
4286
5289
  }
4287
- if (metaCodeN != null && codeN != null && metaCodeN !== codeN) {
4288
- statsParts.push(`
4289
- | meta \u5DF2\u7D22\u5F15\u4EE3\u7801 | ${metaCodeN} \u26A0\uFE0F \u4E0E\u753B\u5E03\u4E0D\u4E00\u81F4 |`);
4290
- } else if (metaCodeN != null) {
4291
- statsParts.push(`
4292
- | meta \u5DF2\u7D22\u5F15\u4EE3\u7801 | ${metaCodeN} |`);
5290
+ if (opts.dryRun) return { uuid, status: "dry-run", version: remote.version };
5291
+ const updated = await apiRequest(`${FLOW_API}/${flowId}/flow-nodes/${uuid}`, {
5292
+ method: "PATCH",
5293
+ body: { code_body: localCode, expected_version: opts.force ? void 0 : remote.version },
5294
+ label: `\u63D0\u4EA4\u8282\u70B9\u4EE3\u7801 (nodeId=${nodeLabel}, uuid=${uuid})`
5295
+ });
5296
+ nodeMeta.codeHash = sha1(localCode);
5297
+ nodeMeta.codeVersion = updated.version;
5298
+ return { uuid, status: "pushed", version: updated.version };
5299
+ }
5300
+ async function pullCodeNode(flowId, flowDir, meta, uuid) {
5301
+ const nodeMeta = meta.nodes[uuid];
5302
+ if (!nodeMeta?.dir || !nodeMeta.codeFile) {
5303
+ throw new Error(`\u8282\u70B9 ${uuid} \u4E0D\u662F\u4EE3\u7801\u8282\u70B9\u6216\u7F3A\u5C11\u672C\u5730\u6620\u5C04`);
4293
5304
  }
4294
- const pulled = opts.pulledAt ? `
4295
- | \u4E0A\u6B21\u62C9\u53D6 | ${opts.pulledAt} |` : "";
4296
- const consistencySection = audit ? buildFlowConsistencyMarkdown(audit, flowDocsRel("flow-dir", "flow/local-files-spec.md")) : [];
4297
- return [
4298
- SCAFFOLD_MARKER,
4299
- "# \u5FEB\u901F\u542F\u52A8",
4300
- "",
4301
- `**\u6D41\u7A0B**\uFF1A${name} \xB7 \u5E73\u53F0 flowId \`${opts.flowId}\``,
4302
- "",
4303
- "## \u6D41\u7A0B\u4FE1\u606F",
4304
- "",
4305
- "| \u5B57\u6BB5 | \u503C |",
4306
- "|------|-----|",
4307
- `| flowId | \`${opts.flowId}\` |`,
4308
- `| \u672C\u5730\u76EE\u5F55 | \`\u6D41\u7A0B/${name}/\` |${statsParts.join("")}${pulled}`,
4309
- "",
4310
- ...consistencySection,
4311
- "## \u5E38\u7528\u547D\u4EE4",
4312
- "",
4313
- "\u5728 **dazi-work \u6839**\uFF08\u6216\u5C06 `--dir` \u6307\u5411\u672C\u76EE\u5F55\uFF09\uFF1B\u9700\u5DF2\u5B89\u88C5\u5168\u5C40 CLI\uFF08`pnpm add -g @dazitech/cli`\uFF09\uFF1A",
4314
- "",
4315
- "```powershell",
4316
- `cd "\u9879\u76EE\\${opts.projectFolder ?? "flow_<\u9879\u76EE\u540D>"}\\\u6D41\u7A0B\\${name}"`,
4317
- "",
4318
- "# \u72B6\u6001 / \u62C9\u53D6 / \u63D0\u4EA4",
4319
- psFlowLine("project status --dir ."),
4320
- psFlowLine(`project pull --flow ${opts.flowId} --dir .`),
4321
- psFlowLine("project push --dir . --canvas"),
4322
- "",
4323
- "# \u5355\u8282\u70B9\u6D4B\u8BD5\uFF08uuid \u89C1 flow.meta.json \u6216 node.info.json\uFF09",
4324
- psFlowLine("run node-exec --node <node_uuid> --dir ."),
4325
- "",
4326
- "# \u6574\u6D41\u7A0B\u8C03\u8BD5 + \u53D8\u91CF",
4327
- psFlowLine("run flow-exec --dir . --type debug"),
4328
- psFlowLine("variable sync --dir ."),
4329
- psFlowLine("variable pull --name <output_variable_name> --dir ."),
4330
- "```",
4331
- "",
4332
- ...buildFlowNodeCatalogQuickStartSection(flowDocsRel("flow-dir", "flow/flows-guide.md")),
4333
- "## AI \u4FEE\u6539\u73B0\u6709\u6D41\u7A0B\uFF08\u5355\u6587\u4EF6\u5165\u53E3\uFF09",
4334
- "",
4335
- "> \u63A8\u8350\u53EA\u628A\u672C\u6587\u4EF6\u9644\u52A0\u7ED9 AI\uFF1B\u82E5\u4FE1\u606F\u4E0D\u8DB3\uFF0C\u518D\u6309\u9700\u6253\u5F00\u300C\u5E2E\u52A9\u6587\u6863\u300D\u4E2D\u7684\u4E13\u9898\u3002",
4336
- "",
4337
- "### \u573A\u666F A\uFF1A\u65B0\u589E\u8282\u70B9\uFF08\u5DF2\u6709\u6D41\u7A0B\u7EE7\u7EED\u6539\uFF09",
4338
- "",
4339
- "1. \u5148\u8BFB `flow.json` \u4E0E `flow.meta.json`\uFF0C\u6267\u884C `dazi flow project doctor --dir .`",
4340
- `2. **\u5FC5\u987B**\u7528 CLI \u65B0\u5EFA\u8282\u70B9\uFF08\u7981\u6B62\u624B\u6413 uuid\uFF09\uFF1A${mdFlowCmd('node new --type <node_type> --dir . --label "<\u8282\u70B9\u540D>"')}`,
4341
- "3. \u5728 `flow.json` \u91CC\u8865 `nodes/edges`\uFF08\u9075\u5B88\u951A\u70B9\uFF1A`sourceHandle` \u4EC5 `r/b/true/false`\uFF0C`targetHandle` \u4EC5 `l/t`\uFF09",
4342
- "4. \u7F16\u8F91 `\u8282\u70B9/<\u540D>/code.sql|py`\uFF08`node new` \u4F1A\u521B\u5EFA\u76EE\u5F55\u4E0E meta \u7D22\u5F15\uFF09",
4343
- `5. \u5355\u8282\u70B9\u6D4B\u8BD5\uFF1A${mdFlowCmd("run node-exec --node <node_uuid> --dir .")}`,
4344
- `6. \u63D0\u4EA4\u753B\u5E03\uFF08\u542B\u8FDE\u7EBF/\u914D\u7F6E\uFF09\uFF1A${mdFlowCmd("project push --dir . --canvas")}`,
4345
- "",
4346
- "### \u573A\u666F B\uFF1A\u4EC5\u6539\u8282\u70B9\u4EE3\u7801",
4347
- "",
4348
- "1. \u7F16\u8F91 `\u8282\u70B9/<\u540D>/code.sql|py`\uFF08\u4E0D\u8981\u628A\u6B63\u6587\u5199\u56DE `flow.json`\uFF09",
4349
- `2. \u8FD0\u884C\u8282\u70B9\uFF1A${mdFlowCmd("run node-exec --node <node_uuid> --dir .")}`,
4350
- `3. \u63D0\u4EA4\u4EE3\u7801\uFF1A${mdFlowCmd("node push --node <node_uuid> --dir .")}`,
4351
- "",
4352
- "### \u573A\u666F C\uFF1A\u753B\u5E03\u548C\u4EE3\u7801\u4E0D\u540C\u6B65",
4353
- "",
4354
- `1. \u5148 ${mdFlowCmd("project status --dir .")} \u770B\u672C\u5730\u810F\u6539\u52A8`,
4355
- `2. \u82E5\u6539\u4E86\u62D3\u6251/\u8FDE\u7EBF/\u914D\u7F6E\uFF1A\u6267\u884C ${mdFlowCmd("project push --dir . --canvas")}`,
4356
- `3. \u82E5\u53EA\u6539\u4E86\u4EE3\u7801\uFF1A\u6267\u884C ${mdFlowCmd("node push --node <node_uuid> --dir .")}`,
4357
- `4. \u5FC5\u8981\u65F6\u91CD\u65B0\u62C9\u53D6\u6821\u5BF9\uFF1A${mdFlowCmd(`project pull --flow ${opts.flowId} --dir .`)}`,
4358
- "",
4359
- "## AI \u81EA\u68C0\u6E05\u5355\uFF08\u63D0\u4EA4\u524D\uFF09",
4360
- "",
4361
- `- \u547D\u4EE4\u524D\u7F00\u662F\u5426\u7EDF\u4E00\u4E3A ${mdFlowCmd("...")}\uFF08\u800C\u4E0D\u662F\u88F8 \`dazi-flow\`\uFF09`,
4362
- "- `flow.json` \u662F\u5426\u53EA\u627F\u8F7D\u753B\u5E03\u62D3\u6251\u4E0E\u8282\u70B9\u914D\u7F6E\uFF08\u4E0D\u5D4C\u5165\u5927\u6BB5 SQL/Python\uFF09",
4363
- "- `code.*` \u662F\u5426\u4E0E\u8282\u70B9\u4E1A\u52A1\u7C7B\u578B\u4E00\u81F4\uFF08SQL \u8282\u70B9 `code.sql`\uFF0CPython \u8282\u70B9 `code.py`\uFF09",
4364
- "- \u6761\u4EF6\u8282\u70B9\u51FA\u8FB9\u662F\u5426\u4EC5\u4F7F\u7528 `true/false`\uFF0C\u672A\u8BEF\u7528 `r/b`",
4365
- "- \u82E5\u6539\u52A8\u753B\u5E03\uFF0C\u662F\u5426\u6267\u884C\u4E86 `flow project push --canvas`",
4366
- "",
4367
- "## \u5E38\u89C1\u9519\u8BEF\u4E0E\u4FEE\u590D",
4368
- "",
4369
- "| \u73B0\u8C61 | \u5E38\u89C1\u539F\u56E0 | \u4FEE\u590D |",
4370
- "|------|----------|------|",
4371
- `| \u547D\u4EE4\u627E\u4E0D\u5230 | \u672A\u88C5\u5168\u5C40 CLI \u6216\u4E0D\u5728 dazi-work | \`pnpm add -g @dazitech/cli\` \u6216 ${mdFlowCmd("...")} / \`.scriptsdazi.ps1 flow ...\` |`,
4372
- "| \u8282\u70B9\u6D4B\u8BD5\u62A5\u4E0A\u6E38\u53D8\u91CF\u4E0D\u5B58\u5728 | \u672A\u5148\u8FD0\u884C\u4E0A\u6E38\u8282\u70B9\u4EA7\u51FA\u53D8\u91CF | \u5148\u8DD1\u4E0A\u6E38\uFF0C\u6216\u6574\u6D41\u7A0B `flow run flow-exec --type debug` \u540E\u518D\u6D4B |",
4373
- "| excel-python \u627E\u4E0D\u5230\u6587\u4EF6 | code.py \u5199\u4E86\u6587\u4EF6\u540D/\u672C\u5730\u8DEF\u5F84 | \u753B\u5E03\u914D `managed_file_id`\uFF08UUID\uFF09\uFF1B\u4EE3\u7801\u7528 `excel_source_path` |",
4374
- "| Excel \u7528\u4E86 file-source | file-source \u4E0D\u89E3\u6790 xlsx | \u6539\u4E3A **`excel-python`** + `managed_file_id` |",
4375
- "| excel-python \u65E0\u4E3B\u8F93\u51FA | \u672A `set_table_output` \u6216\u540D\u79F0\u4E0E `output_variable_name` \u4E0D\u4E00\u81F4 | \u89C1 node-code-guide \xA75 |",
4376
- "| \u753B\u5E03\u663E\u793A\u5BF9\u4F46\u5E73\u53F0\u4E0D\u751F\u6548 | \u53EA push \u4E86\u4EE3\u7801\uFF0C\u672A push \u753B\u5E03 | \u6267\u884C `flow project push --dir . --canvas` |",
4377
- "| \u4EE3\u7801\u6539\u4E86\u4F46\u5E73\u53F0\u8FD8\u662F\u65E7\u4EE3\u7801 | \u6539\u4E86\u672C\u5730 `code.*` \u4F46\u672A node push | \u6267\u884C `flow node push --node <node_uuid> --dir .` |",
4378
- "",
4379
- "## AI \u81EA\u4E3B\u8FD0\u884C\u4E0E\u6539\u9519\u95ED\u73AF\uFF08Agent \u5FC5\u8BFB\uFF09",
4380
- "",
4381
- "> **\u6269\u5C55/\u83DC\u5355\u4E0D\u4F1A\u81EA\u52A8\u6539\u4EE3\u7801**\uFF08\u5E73\u53F0 D6\uFF09\uFF1B\u7528\u6237\u59D4\u6258\u4F60\u6539\u6D41\u7A0B\u65F6\uFF0C\u4F60**\u5FC5\u987B\u4E3B\u52A8**\u6267\u884C\u300C\u6539 \u2192 \u8DD1 \u2192 \u8BFB\u9519 \u2192 \u518D\u6539 \u2192 \u518D\u8DD1\u300D\uFF0C\u76F4\u5230\u901A\u8FC7\u6216\u8FBE\u5230\u91CD\u8BD5\u4E0A\u9650\u3002",
4382
- "",
4383
- "### \u9519\u8BEF\u843D\u5728\u54EA\u91CC\uFF08\u8DD1\u5B8C\u5FC5\u67E5\uFF09",
4384
- "",
4385
- "| \u8FD0\u884C\u65B9\u5F0F | \u5931\u8D25\u65F6\u8BFB | \u6210\u529F/\u6B65\u9AA4\u6458\u8981 |",
4386
- "|----------|----------|---------------|",
4387
- "| \u5355\u8282\u70B9 `run node-exec` | `_run/<\u8282\u70B9\u540D>.last-error.md` | \u65E0 error \u6587\u4EF6\u5373\u901A\u8FC7 |",
4388
- "| \u6574\u6D41\u7A0B `run flow-exec` | `_run/flow.last-error.md` | `_run/flow.last-run.md`\uFF08\u6B65\u9AA4+\u65E5\u5FD7\uFF09 |",
4389
- "",
4390
- "CLI \u5E26 `--json` \u65F6\uFF1A\u770B\u8FD4\u56DE `success: false` \u6216 `errorFile` \u5B57\u6BB5\uFF0C**\u518D\u6253\u5F00\u5BF9\u5E94 md \u6587\u4EF6**\uFF0C\u4E0D\u8981\u53EA\u770B\u7EC8\u7AEF\u4E00\u884C\u62A5\u9519\u3002",
4391
- "",
4392
- "### \u6807\u51C6\u6539\u9519\u5FAA\u73AF\uFF08\u9ED8\u8BA4\u6700\u591A 3 \u8F6E\uFF09",
4393
- "",
4394
- "1. **\u5B9A\u4F4D**\uFF1A\u8BFB `flow.json` + \u76EE\u6807 `\u8282\u70B9/<\u540D>/code.*` + `flow.meta.json`\uFF08\u53D6 `node_uuid`\uFF09",
4395
- "2. **\u4FEE\u6539**\uFF1A\u53EA\u6539\u5FC5\u8981\u6587\u4EF6\uFF08\u4EE3\u7801 \u2192 `code.*`\uFF1B\u8FDE\u7EBF/\u914D\u7F6E \u2192 `flow.json`\uFF09",
4396
- "3. **\u9A8C\u8BC1**\uFF08\u5148\u5C0F\u540E\u5927\uFF09\uFF1A",
4397
- ` - \u5355\u8282\u70B9\uFF1A${mdFlowCmd("run node-exec --node <node_uuid> --dir .")}`,
4398
- ` - \u6574\u6D41\u7A0B\uFF1A${mdFlowCmd("run flow-exec --dir . --type debug")}`,
4399
- "4. **\u5224\u9519**\uFF1A\u9000\u51FA\u7801\u975E 0 / JSON `success:false` \u2192 \u6253\u5F00 `_run/*.last-error.md`\uFF0C\u6309\u5176\u4E2D**\u9519\u8BEF\u5206\u7C7B**\u4E0E traceback \u4FEE\u590D",
4400
- "5. **\u91CD\u8BD5**\uFF1A\u56DE\u5230\u6B65\u9AA4 2\uFF1B\u82E5 3 \u8F6E\u4ECD\u5931\u8D25\uFF0C\u6C47\u603B\u5DF2\u5C1D\u8BD5\u4FEE\u590D\u70B9\u5E76\u8BF7\u6C42\u7528\u6237\u4ECB\u5165",
4401
- "6. **\u63D0\u4EA4**\uFF08\u4EC5\u901A\u8FC7\u540E\uFF09\uFF1A\u4EE3\u7801 `node push`\uFF1B\u753B\u5E03 `project push --canvas`",
4402
- "",
4403
- "### \u5E38\u89C1\u5931\u8D25 \u2192 \u4F18\u5148\u52A8\u4F5C",
4404
- "",
4405
- "| last-error \u5206\u7C7B | \u4F18\u5148\u68C0\u67E5 |",
4406
- "|-----------------|----------|",
4407
- "| \u7F3A\u4E0A\u6E38\u53D8\u91CF | \u5148\u8DD1\u4E0A\u6E38\u8282\u70B9\u6216\u6574\u6D41\u7A0B debug\uFF0C\u518D `variable pull` \u770B schema |",
4408
- "| \u914D\u7F6E\u7F3A\u5931 | `flow.json` \u8BE5\u8282\u70B9 `data`\uFF08connectionId / output_variable_name \u7B49\uFF09 |",
4409
- "| \u4EE3\u7801\u9519\u8BEF | `code.*` \u4E0E traceback\uFF1B\u6539\u540E\u53EA `node push` |",
4410
- "| \u8FDE\u63A5/\u6570\u636E\u6E90 | connectionId / spaceId \u662F\u5426\u6709\u6548 |",
4411
- "",
4412
- "### Agent \u7981\u6B62\u9879",
4413
- "",
4414
- "- **\u7981\u6B62**\u672A\u5B9E\u9645\u8FD0\u884C\u5C31\u58F0\u79F0\u300C\u5DF2\u4FEE\u590D/\u5DF2\u901A\u8FC7\u300D",
4415
- "- **\u7981\u6B62**\u4E0D\u8BFB `_run/*.last-error.md` \u5C31\u731C\u6D4B\u539F\u56E0",
4416
- "- **\u7981\u6B62**\u4FEE\u590D\u540E\u8DF3\u8FC7\u9A8C\u8BC1\u76F4\u63A5 `push`",
4417
- "- **\u7981\u6B62**\u628A SQL/Python \u6B63\u6587\u5199\u56DE `flow.json`",
4418
- "",
4419
- "\u4FA7\u680F\u63D0\u793A\u8BCD\u53EF\u9009\u7528 **`flow/run-fix-loop`**\uFF08\u81EA\u4E3B\u6539\u9519\u4E13\u7528\uFF09\u3002",
4420
- "",
4421
- "\u6269\u5C55\uFF1A\u53F3\u952E **`flow.json`** / **\u8282\u70B9/** / **`code.*`** \u53EF\u6D4B\u8BD5\u3001\u63D0\u4EA4\u3001\u6253\u5F00\u8BBE\u8BA1\u5668\u3002",
4422
- "",
4423
- "## \u7F16\u5199\u8282\u70B9\u4EE3\u7801",
4424
- "",
4425
- "- **SQL \u8282\u70B9**\uFF08`sql-query` / `database-source`\uFF09\uFF1A\u7F16\u8F91 `\u8282\u70B9/<\u540D>/code.sql`\uFF1B`sql-query` \u4E2D\u7528 **\u4E0A\u6E38\u53D8\u91CF\u540D\u4F5C\u8868\u540D**\uFF08`FROM \u4E0A\u6E38\u53D8\u91CF\u540D`\uFF09",
4426
- '- **Python \u8282\u70B9**\uFF08`python-script`\uFF09\uFF1A\u7F16\u8F91 `code.py`\uFF1B\u5355\u8282\u70B9\u6D4B\u8BD5\u7528 **`get_variable("\u4E0A\u6E38\u53D8\u91CF\u540D")`**\uFF0C\u8F93\u51FA\u8D4B\u503C **`result_df`**',
4427
- "- **`excel-python`\uFF08Excel \u9ED8\u8BA4\u9996\u9009\uFF09**\uFF1A",
4428
- " - \u6709 **`managed_file_id` \u7684 Excel \u2192 \u4F18\u5148 `excel-python`**\uFF0C**\u52FF\u7528 `file-source`**\uFF08file-source \u4E0D\u89E3\u6790 Excel\uFF09",
4429
- " - **\u753B\u5E03**\u914D **`managed_file_id`**\uFF08UUID\uFF09\uFF1B**`code.py` \u53EA\u7528 `excel_source_path`**",
4430
- ' - \u4E3B\u8F93\u51FA **`set_table_output("<\u4E0E output_variable_name \u540C\u540D>", df)`**',
4431
- " - \u5B8C\u6574\u793A\u4F8B \u2192 [node-code-guide \xA75](" + flowDocsRel("flow-dir", "flow/node-code-guide.md") + "#5-excel-pythonexcel-\u5F00\u53D1--\u9ED8\u8BA4\u9996\u9009)",
4432
- "- \u53D8\u91CF\u7EA6\u5B9A\u4E0E\u5B8C\u6574\u793A\u4F8B \u2192 [\u6D41\u7A0B\u53D8\u91CF\u7CFB\u7EDF\u6307\u5357](" + flowDocsRel("flow-dir", "flow/variables-guide.md") + ") \xB7 [\u8282\u70B9\u4EE3\u7801\u7F16\u5199\u6307\u5357](" + flowDocsRel("flow-dir", "flow/node-code-guide.md") + ")",
4433
- "",
4434
- ...buildDocsTable("flow-dir")
4435
- ].join("\n");
5305
+ const remote = await apiRequest(`${FLOW_API}/${flowId}/flow-nodes/${uuid}`, {
5306
+ label: `\u62C9\u53D6\u8282\u70B9\u4EE3\u7801 (uuid=${uuid})`
5307
+ });
5308
+ const code = remote.code_body ?? "";
5309
+ const target = import_path5.default.join(flowDir, nodeMeta.dir, nodeMeta.codeFile);
5310
+ import_fs4.default.mkdirSync(import_path5.default.dirname(target), { recursive: true });
5311
+ import_fs4.default.writeFileSync(target, code, "utf8");
5312
+ nodeMeta.codeHash = sha1(code);
5313
+ nodeMeta.codeVersion = remote.version;
5314
+ return { uuid, version: remote.version };
5315
+ }
5316
+ async function pushCanvasDocument(flowId, document) {
5317
+ const nodeCount = document.nodes?.length ?? 0;
5318
+ const edgeCount = document.edges?.length ?? 0;
5319
+ await apiRequest(`${FLOW_API}/${flowId}`, {
5320
+ method: "PUT",
5321
+ body: { data: document },
5322
+ label: `\u63D0\u4EA4\u753B\u5E03 flow.json (flowId=${flowId}, nodes=${nodeCount}, edges=${edgeCount})`
5323
+ });
4436
5324
  }
4437
- function removeLegacyFlowDirQuickStartFiles(flowDir, currentFilename, fsImpl) {
4438
- const legacy = import_path8.default.join(flowDir, LEGACY_FLOW_DIR_QUICKSTART_FILENAME);
4439
- if (fsImpl.existsSync(legacy)) {
5325
+
5326
+ // cli/dazi-flow/src/commands/snapshot.ts
5327
+ function makeSnapshotCommand() {
5328
+ const cmd = new Command("snapshot").description("Flow \u5FEB\u7167\u7BA1\u7406");
5329
+ cmd.command("pull").description("\u4ECE\u5E73\u53F0\u62C9\u53D6 Flow \u5FEB\u7167\u5230\u672C\u5730 flows/<flowId>/").requiredOption("--flow <flowId>", "Flow ID\uFF08\u77ED\u9009\u9879 -f\uFF09").alias("-f").option("--out <dir>", "\u8F93\u51FA\u76EE\u5F55\uFF08\u9ED8\u8BA4 flows/<flowId>/\uFF09").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
4440
5330
  try {
4441
- if (isFlowScaffoldFile(fsImpl.readFileSync(legacy, "utf8"))) {
4442
- fsImpl.unlinkSync(legacy);
5331
+ console.log(`\u6B63\u5728\u62C9\u53D6\u5FEB\u7167: ${opts.flow} ...`);
5332
+ const snapshot = await apiRequest(
5333
+ `/api/data-pipelines/v1/flows/${opts.flow}/snapshot`
5334
+ );
5335
+ const ws = resolveWorkspace();
5336
+ const outDir = opts.out ?? import_path6.default.join(ws.flows, opts.flow);
5337
+ if (!import_fs5.default.existsSync(outDir)) import_fs5.default.mkdirSync(outDir, { recursive: true });
5338
+ const outFile = import_path6.default.join(outDir, "snapshot.json");
5339
+ import_fs5.default.writeFileSync(outFile, JSON.stringify(snapshot, null, 2), "utf-8");
5340
+ if (opts.json) {
5341
+ console.log(JSON.stringify(snapshot, null, 2));
5342
+ } else {
5343
+ console.log(`\u2705 \u5FEB\u7167\u5DF2\u4FDD\u5B58: ${outFile}`);
4443
5344
  }
4444
- } catch {
5345
+ ok({ flowId: opts.flow, file: outFile });
5346
+ } catch (err) {
5347
+ handleError(err);
4445
5348
  }
4446
- }
4447
- for (const entry of fsImpl.readdirSync(flowDir)) {
4448
- if (entry === currentFilename) continue;
4449
- if (!/^快速启动_.+\.md$/.test(entry)) continue;
4450
- const full = import_path8.default.join(flowDir, entry);
5349
+ });
5350
+ cmd.command("push-graph").description("\u5C06\u672C\u5730 flows/<flowId>/snapshot.json \u56FE\u7ED3\u6784\u63A8\u9001\u5230\u5E73\u53F0").requiredOption("--flow <flowId>", "Flow ID").option("--dir <dir>", "\u5FEB\u7167\u76EE\u5F55\uFF08\u9ED8\u8BA4 flows/<flowId>/\uFF09").option("--dry-run", "\u4EC5\u9A8C\u8BC1\uFF0C\u4E0D\u63A8\u9001").action(async (opts) => {
4451
5351
  try {
4452
- if (isFlowScaffoldFile(fsImpl.readFileSync(full, "utf8"))) {
4453
- fsImpl.unlinkSync(full);
5352
+ const ws = resolveWorkspace();
5353
+ const dir = opts.dir ?? import_path6.default.join(ws.flows, opts.flow);
5354
+ const file = import_path6.default.join(dir, "snapshot.json");
5355
+ if (!import_fs5.default.existsSync(file)) {
5356
+ console.error(`\u9519\u8BEF: \u627E\u4E0D\u5230 ${file}\uFF0C\u8BF7\u5148\u8FD0\u884C snapshot pull`);
5357
+ process.exit(1);
4454
5358
  }
4455
- } catch {
4456
- }
4457
- }
4458
- }
4459
- function writeFlowDirQuickStartScaffold(flowDir, flowName, content, fsImpl) {
4460
- const filename = flowDirQuickStartFilename(flowName);
4461
- writeScaffoldIfAllowed(import_path8.default.join(flowDir, filename), content, fsImpl);
4462
- removeLegacyFlowDirQuickStartFiles(flowDir, filename, fsImpl);
4463
- return filename;
4464
- }
4465
- function removeLegacyFlowDirScaffoldReadme(flowDir, fsImpl) {
4466
- const legacy = import_path8.default.join(flowDir, "README.md");
4467
- if (!fsImpl.existsSync(legacy)) return;
4468
- try {
4469
- if (isFlowScaffoldFile(fsImpl.readFileSync(legacy, "utf8"))) {
4470
- fsImpl.unlinkSync(legacy);
4471
- }
4472
- } catch {
4473
- }
4474
- }
4475
- function writeScaffoldIfAllowed(filePath, content, fsImpl, opts) {
4476
- if (fsImpl.existsSync(filePath)) {
4477
- const existing = fsImpl.readFileSync(filePath, "utf8");
4478
- if (!isFlowScaffoldFile(existing)) {
4479
- if (!(opts?.upgradeLegacy && isLegacyFlowScaffoldFile(existing))) return false;
5359
+ const snapshot = JSON.parse(import_fs5.default.readFileSync(file, "utf-8"));
5360
+ if (opts.dryRun) {
5361
+ console.log("[dry-run] \u5C06\u63A8\u9001\u56FE\u5FEB\u7167:");
5362
+ console.log(` \u8282\u70B9\u6570: ${snapshot.nodes?.length ?? "?"}`);
5363
+ console.log(` \u8FB9\u6570: ${snapshot.edges?.length ?? "?"}`);
5364
+ ok({ flowId: opts.flow, dryRun: true });
5365
+ return;
5366
+ }
5367
+ const doc = extractDocument(snapshot);
5368
+ await pushCanvasDocument(opts.flow, doc);
5369
+ console.log(`\u2705 \u56FE\u5FEB\u7167\u5DF2\u63A8\u9001`);
5370
+ ok({ flowId: opts.flow, nodeCount: doc.nodes.length, edgeCount: doc.edges.length });
5371
+ } catch (err) {
5372
+ handleError(err);
4480
5373
  }
4481
- }
4482
- fsImpl.writeFileSync(filePath, content, "utf8");
4483
- return true;
5374
+ });
5375
+ return cmd;
4484
5376
  }
4485
5377
 
5378
+ // cli/dazi-flow/src/commands/project.ts
5379
+ var import_path9 = __toESM(require("path"), 1);
5380
+ var import_fs8 = __toESM(require("fs"), 1);
5381
+ var import_flowScaffoldDocs = __toESM(require_flowScaffoldDocs(), 1);
5382
+
4486
5383
  // cli/dazi-flow/src/lib/flowVariables.ts
4487
5384
  var import_fs7 = __toESM(require("fs"), 1);
4488
- var import_path9 = __toESM(require("path"), 1);
5385
+ var import_path8 = __toESM(require("path"), 1);
4489
5386
  async function ensureFlowDebugRun(flowId) {
4490
5387
  return apiRequest(`${FLOW_API}/${flowId}/debug-run`);
4491
5388
  }
4492
5389
  function variablesDir(flowDir) {
4493
- return import_path9.default.join(flowDir, VARIABLES_DIR);
5390
+ return import_path8.default.join(flowDir, VARIABLES_DIR);
4494
5391
  }
4495
5392
  function variableFilePath(flowDir, varName) {
4496
5393
  const safe = sanitizeName(varName) || "variable";
4497
- return import_path9.default.join(variablesDir(flowDir), `${safe}.json`);
5394
+ return import_path8.default.join(variablesDir(flowDir), `${safe}.json`);
4498
5395
  }
4499
5396
  function collectDesignTimeVariableNames(flowDir) {
4500
5397
  const doc = readFlowJson(flowDir);
@@ -4633,11 +5530,10 @@ async function syncAllVariables(flowId, flowDir, previewLimit = 10) {
4633
5530
  }
4634
5531
 
4635
5532
  // cli/dazi-flow/src/commands/project.ts
4636
- function resolveFlowDir(dir) {
4637
- return import_path10.default.resolve(dir ?? process.cwd());
4638
- }
5533
+ var import_flowWorkspaceAudit = __toESM(require_flowWorkspaceAudit(), 1);
5534
+ var import_flowDirResolve = __toESM(require_flowDirResolve(), 1);
4639
5535
  function readLocalCanvasNodeCount(flowDir) {
4640
- const p = import_path10.default.join(flowDir, "flow.json");
5536
+ const p = import_path9.default.join(flowDir, "flow.json");
4641
5537
  if (!import_fs8.default.existsSync(p)) return 0;
4642
5538
  try {
4643
5539
  const doc = JSON.parse(import_fs8.default.readFileSync(p, "utf8"));
@@ -4646,15 +5542,10 @@ function readLocalCanvasNodeCount(flowDir) {
4646
5542
  return 0;
4647
5543
  }
4648
5544
  }
4649
- function inferProjectFolder(flowDir) {
4650
- const norm = flowDir.replace(/\\/g, "/");
4651
- const m = norm.match(/(?:^|\/)项目\/(flow_[^/]+)\/流程(?:\/|$)/);
4652
- return m?.[1];
4653
- }
4654
5545
  function writeFlowPullScaffold(flowDir, meta) {
4655
- const projectFolder = inferProjectFolder(flowDir) ?? "flow_<\u9879\u76EE\u540D>";
4656
- const flowName = meta.flowName || import_path10.default.basename(flowDir);
4657
- const quickStart = buildFlowDirQuickStartMarkdown({
5546
+ const projectFolder = (0, import_flowDirResolve.inferBusinessProjectFolder)(flowDir) ?? "<\u4E1A\u52A1\u9879\u76EE\u540D>";
5547
+ const flowName = meta.flowName || import_path9.default.basename(flowDir);
5548
+ const quickStart = (0, import_flowScaffoldDocs.buildFlowDirQuickStartMarkdown)({
4658
5549
  flowName,
4659
5550
  flowId: meta.flowId,
4660
5551
  projectFolder,
@@ -4663,62 +5554,76 @@ function writeFlowPullScaffold(flowDir, meta) {
4663
5554
  pulledAt: (/* @__PURE__ */ new Date()).toISOString(),
4664
5555
  flowDir
4665
5556
  });
4666
- writeFlowDirQuickStartScaffold(flowDir, flowName, quickStart, import_fs8.default);
4667
- removeLegacyFlowDirScaffoldReadme(flowDir, import_fs8.default);
4668
- const flowsDir = import_path10.default.dirname(flowDir);
4669
- if (import_path10.default.basename(flowsDir) === "\u6D41\u7A0B") {
4670
- const flowsReadme = buildFlowsDirReadmeMarkdown({ projectFolder });
4671
- writeScaffoldIfAllowed(import_path10.default.join(flowsDir, "README.md"), flowsReadme, import_fs8.default, { upgradeLegacy: true });
4672
- const projectRoot = import_path10.default.dirname(flowsDir);
4673
- const quickStart2 = buildFlowQuickStartMarkdown({ projectFolder });
4674
- writeScaffoldIfAllowed(import_path10.default.join(projectRoot, "\u5FEB\u901F\u542F\u52A8.md"), quickStart2, import_fs8.default, { upgradeLegacy: true });
5557
+ (0, import_flowScaffoldDocs.writeFlowDirQuickStartScaffold)(flowDir, flowName, quickStart, import_fs8.default);
5558
+ (0, import_flowScaffoldDocs.removeLegacyFlowDirScaffoldReadme)(flowDir, import_fs8.default);
5559
+ const flowRoot = (0, import_flowScaffoldDocs.resolveFlowRootFromFlowDir)(flowDir);
5560
+ if (flowRoot) {
5561
+ const flowsContainer = import_path9.default.join(flowRoot, "flows");
5562
+ const flowsReadme = (0, import_flowScaffoldDocs.buildFlowsDirReadmeMarkdown)({ projectFolder });
5563
+ if (import_fs8.default.existsSync(flowsContainer)) {
5564
+ (0, import_flowScaffoldDocs.writeScaffoldIfAllowed)(import_path9.default.join(flowsContainer, "README.md"), flowsReadme, import_fs8.default, {
5565
+ upgradeLegacy: true
5566
+ });
5567
+ } else if (import_path9.default.basename(import_path9.default.dirname(flowDir)) === "\u6D41\u7A0B") {
5568
+ (0, import_flowScaffoldDocs.writeScaffoldIfAllowed)(import_path9.default.join(flowRoot, "README.md"), flowsReadme, import_fs8.default, { upgradeLegacy: true });
5569
+ }
5570
+ const rootReadme = (0, import_flowScaffoldDocs.buildFlowRootReadmeMarkdown)({ projectFolder });
5571
+ (0, import_flowScaffoldDocs.writeScaffoldIfAllowed)(import_path9.default.join(flowRoot, "README.md"), rootReadme, import_fs8.default, { upgradeLegacy: true });
5572
+ (0, import_flowScaffoldDocs.removeLegacyFlowRootQuickStart)(flowRoot, import_fs8.default);
4675
5573
  }
4676
5574
  }
4677
5575
  function makeProjectCommand() {
4678
5576
  const cmd = new Command("project").description("\u6D41\u7A0B\u9879\u76EE\u672C\u5730\u6587\u4EF6\u5316\uFF08\u62C9\u53D6/\u63D0\u4EA4/\u72B6\u6001\uFF09");
4679
- cmd.command("init <name>").description("\u5728 \u9879\u76EE/flow_<name>/ \u521D\u59CB\u5316\u6D41\u7A0B\u9879\u76EE\u9AA8\u67B6").option("--root <dir>", "\u5DE5\u4F5C\u533A\u6839\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--json", "\u8F93\u51FA JSON").action((name, opts) => {
5577
+ cmd.command("init <name>").description("\u5728 \u9879\u76EE/<\u4E1A\u52A1\u540D>/ \u521D\u59CB\u5316\u4E1A\u52A1\u9879\u76EE\u6D41\u7A0B\u9AA8\u67B6\uFF08\u542B \u6D41\u7A0B/flows/\uFF09").option("--root <dir>", "\u5DE5\u4F5C\u533A\u6839\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--json", "\u8F93\u51FA JSON").action((name, opts) => {
4680
5578
  try {
4681
- const root = import_path10.default.resolve(opts.root ?? process.cwd());
4682
- const folder = name.startsWith("flow_") ? name : `flow_${name}`;
4683
- const projectRoot = import_path10.default.join(root, "\u9879\u76EE", folder);
5579
+ const root = import_path9.default.resolve(opts.root ?? process.cwd());
5580
+ const folder = name.trim();
5581
+ if (!folder) {
5582
+ console.error("\u9519\u8BEF: \u4E1A\u52A1\u9879\u76EE\u540D\u4E0D\u80FD\u4E3A\u7A7A");
5583
+ process.exit(1);
5584
+ }
5585
+ const projectRoot = import_path9.default.join(root, "\u9879\u76EE", folder);
4684
5586
  if (import_fs8.default.existsSync(projectRoot)) {
4685
5587
  console.error(`\u9519\u8BEF: \u9879\u76EE\u76EE\u5F55\u5DF2\u5B58\u5728: ${projectRoot}`);
4686
5588
  process.exit(1);
4687
5589
  }
4688
- import_fs8.default.mkdirSync(import_path10.default.join(projectRoot, "\u6D41\u7A0B"), { recursive: true });
4689
- import_fs8.default.mkdirSync(import_path10.default.join(projectRoot, "\u89C4\u5212"), { recursive: true });
5590
+ import_fs8.default.mkdirSync(import_path9.default.join(projectRoot, "\u6D41\u7A0B", "flows"), { recursive: true });
5591
+ import_fs8.default.mkdirSync(import_path9.default.join(projectRoot, "\u6D41\u7A0B", "\u89C4\u5212"), { recursive: true });
4690
5592
  import_fs8.default.writeFileSync(
4691
- import_path10.default.join(projectRoot, "README.md"),
4692
- `# ${name}
5593
+ import_path9.default.join(projectRoot, "README.md"),
5594
+ `# ${folder}
4693
5595
 
4694
- \u6570\u636E\u6D41\u7A0B\u9879\u76EE\uFF08\u4E0D\u7ED1\u5B9A\u6570\u636E\u7A7A\u95F4/\u8FDE\u63A5\uFF09\u3002\u6D41\u7A0B\u4F4D\u4E8E \`\u6D41\u7A0B/\` \u76EE\u5F55\u3002
5596
+ \u4E1A\u52A1\u9879\u76EE\uFF08\u672C\u4F53 / \u6D41\u7A0B / \u5E94\u7528\uFF09\u3002\u6D41\u7A0B\u4F4D\u4E8E \`\u6D41\u7A0B/flows/\`\u3002
4695
5597
 
4696
- \u8BE6\u89C1 \`\u5FEB\u901F\u542F\u52A8.md\` \u4E0E \`\u8D44\u6E90/docs/flow/\`\u3002
5598
+ \u8BE6\u89C1 \`\u6D41\u7A0B/README.md\` \u4E0E \`\u8D44\u6E90/docs/flow/\`\u3002
4697
5599
  `,
4698
5600
  "utf8"
4699
5601
  );
4700
- writeScaffoldIfAllowed(
4701
- import_path10.default.join(projectRoot, "\u5FEB\u901F\u542F\u52A8.md"),
4702
- buildFlowQuickStartMarkdown({ projectFolder: folder }),
5602
+ const flowRoot = import_path9.default.join(projectRoot, "\u6D41\u7A0B");
5603
+ import_fs8.default.mkdirSync(import_path9.default.join(flowRoot, "flows"), { recursive: true });
5604
+ (0, import_flowScaffoldDocs.writeScaffoldIfAllowed)(
5605
+ import_path9.default.join(flowRoot, "README.md"),
5606
+ (0, import_flowScaffoldDocs.buildFlowRootReadmeMarkdown)({ projectFolder: folder }),
4703
5607
  import_fs8.default
4704
5608
  );
4705
- writeScaffoldIfAllowed(
4706
- import_path10.default.join(projectRoot, "\u6D41\u7A0B", "README.md"),
4707
- buildFlowsDirReadmeMarkdown({ projectFolder: folder }),
5609
+ (0, import_flowScaffoldDocs.writeScaffoldIfAllowed)(
5610
+ import_path9.default.join(flowRoot, "flows", "README.md"),
5611
+ (0, import_flowScaffoldDocs.buildFlowsDirReadmeMarkdown)({ projectFolder: folder }),
4708
5612
  import_fs8.default
4709
5613
  );
5614
+ (0, import_flowScaffoldDocs.removeLegacyFlowRootQuickStart)(flowRoot, import_fs8.default);
4710
5615
  console.log(`\u2705 \u6D41\u7A0B\u9879\u76EE\u5DF2\u521D\u59CB\u5316: ${projectRoot}`);
4711
5616
  ok({ projectRoot, folder });
4712
5617
  } catch (err) {
4713
5618
  handleError(err);
4714
5619
  }
4715
5620
  });
4716
- cmd.command("pull").description("\u4ECE\u5E73\u53F0\u62C9\u53D6\u6D41\u7A0B\u5FEB\u7167\u5E76\u62C6\u5206\u4E3A\u672C\u5730\u6587\u4EF6\uFF08flow.json + \u8282\u70B9/<\u540D>/code.*\uFF09").requiredOption("--flow <flowId>", "Flow ID").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
5621
+ cmd.command("pull").description("\u4ECE\u5E73\u53F0\u62C9\u53D6\u6D41\u7A0B\u5FEB\u7167\u5E76\u62C6\u5206\u4E3A\u672C\u5730\u6587\u4EF6\uFF08flow.json + \u8282\u70B9/<\u540D>/code.*\uFF09").requiredOption("--flow <flowId>", "Flow ID").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u987B\u542B flow.json\uFF1B\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
4717
5622
  try {
4718
- const flowDir = resolveFlowDir(opts.dir);
5623
+ const flowDir = (0, import_flowDirResolve.resolveFlowDir)(opts.dir, { json: opts.json });
4719
5624
  import_fs8.default.mkdirSync(flowDir, { recursive: true });
4720
5625
  const localCount = readLocalCanvasNodeCount(flowDir);
4721
- const preAudit = localCount > 0 ? auditFlowWorkspace(flowDir) : null;
5626
+ const preAudit = localCount > 0 ? (0, import_flowWorkspaceAudit.auditFlowWorkspace)(flowDir) : null;
4722
5627
  if (preAudit && !preAudit.ok) {
4723
5628
  console.warn("\u26A0\uFE0F flow.json \u4E0E flow.meta.json \u4E0D\u4E00\u81F4\uFF1Bpull \u5C06\u7528\u5E73\u53F0\u5FEB\u7167\u8986\u76D6 flow.json\u3002\u624B\u6539\u753B\u5E03\u672A push \u65F6\u53EF\u80FD\u4E22\u5931\uFF0C\u53EF\u5148 repair-meta \u6216 push --canvas\u3002");
4724
5629
  }
@@ -4743,7 +5648,7 @@ function makeProjectCommand() {
4743
5648
  const codeNodes = Object.values(nodeMetas).filter((m) => m.codeFile).length;
4744
5649
  writeFlowPullScaffold(flowDir, {
4745
5650
  flowId: opts.flow,
4746
- flowName: String(flowMeta.name ?? import_path10.default.basename(flowDir)),
5651
+ flowName: String(flowMeta.name ?? import_path9.default.basename(flowDir)),
4747
5652
  nodeCount: strippedDoc.nodes.length,
4748
5653
  codeNodeCount: codeNodes
4749
5654
  });
@@ -4762,9 +5667,9 @@ function makeProjectCommand() {
4762
5667
  handleError(err);
4763
5668
  }
4764
5669
  });
4765
- cmd.command("push").description("\u63D0\u4EA4\u672C\u5730\u6539\u52A8\u5230\u5E73\u53F0\uFF08\u9ED8\u8BA4\u4EC5\u589E\u91CF\u63A8\u9001\u5DF2\u6539\u4EE3\u7801\u8282\u70B9\uFF09").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--canvas", "\u540C\u65F6\u5168\u91CF\u63A8\u9001\u753B\u5E03\uFF08flow.json\uFF0CPUT /flows/{id} \u6574\u56FE replace\uFF09").option("--force", "\u5FFD\u7565\u8FDC\u7AEF\u7248\u672C\u51B2\u7A81\uFF08\u8986\u76D6\uFF09").option("--dry-run", "\u4EC5\u663E\u793A\u5C06\u63A8\u9001\u7684\u5185\u5BB9").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
5670
+ cmd.command("push").description("\u63D0\u4EA4\u672C\u5730\u6539\u52A8\u5230\u5E73\u53F0\uFF08\u9ED8\u8BA4\u4EC5\u589E\u91CF\u63A8\u9001\u5DF2\u6539\u4EE3\u7801\u8282\u70B9\uFF09").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u987B\u542B flow.json\uFF1B\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09").option("--canvas", "\u540C\u65F6\u5168\u91CF\u63A8\u9001\u753B\u5E03\uFF08flow.json\uFF0CPUT /flows/{id} \u6574\u56FE replace\uFF09").option("--force", "\u5FFD\u7565\u8FDC\u7AEF\u7248\u672C\u51B2\u7A81\uFF08\u8986\u76D6\uFF09").option("--dry-run", "\u4EC5\u663E\u793A\u5C06\u63A8\u9001\u7684\u5185\u5BB9").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
4766
5671
  try {
4767
- const flowDir = resolveFlowDir(opts.dir);
5672
+ const flowDir = (0, import_flowDirResolve.resolveFlowDir)(opts.dir, { json: opts.json });
4768
5673
  const meta = readMeta(flowDir);
4769
5674
  if (!meta?.flowId) {
4770
5675
  console.error("\u9519\u8BEF: \u7F3A\u5C11 flow.meta.json \u6216\u672A\u5173\u8054 flowId\uFF0C\u8BF7\u5148 project pull");
@@ -4812,9 +5717,9 @@ function makeProjectCommand() {
4812
5717
  handleError(err);
4813
5718
  }
4814
5719
  });
4815
- cmd.command("status").description("\u663E\u793A\u672C\u5730\u76F8\u5BF9\u4E0A\u6B21\u62C9\u53D6\u7684\u6539\u52A8\uFF08\u4EE3\u7801\u8282\u70B9\uFF09").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--json", "\u8F93\u51FA JSON").action((opts) => {
5720
+ cmd.command("status").description("\u663E\u793A\u672C\u5730\u76F8\u5BF9\u4E0A\u6B21\u62C9\u53D6\u7684\u6539\u52A8\uFF08\u4EE3\u7801\u8282\u70B9\uFF09").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u987B\u542B flow.json\uFF1B\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09").option("--json", "\u8F93\u51FA JSON").action((opts) => {
4816
5721
  try {
4817
- const flowDir = resolveFlowDir(opts.dir);
5722
+ const flowDir = (0, import_flowDirResolve.resolveFlowDir)(opts.dir, { json: opts.json });
4818
5723
  const meta = readMeta(flowDir);
4819
5724
  if (!meta) {
4820
5725
  console.error("\u9519\u8BEF: \u7F3A\u5C11 flow.meta.json\uFF0C\u8BF7\u5148 project pull");
@@ -4839,10 +5744,10 @@ function makeProjectCommand() {
4839
5744
  handleError(err);
4840
5745
  }
4841
5746
  });
4842
- cmd.command("doctor").description("\u68C0\u67E5 flow.json / flow.meta.json / \u8282\u70B9/ \u4E00\u81F4\u6027").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--json", "\u8F93\u51FA JSON").action((opts) => {
5747
+ cmd.command("doctor").description("\u68C0\u67E5 flow.json / flow.meta.json / \u8282\u70B9/ \u4E00\u81F4\u6027").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u987B\u542B flow.json\uFF1B\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09").option("--json", "\u8F93\u51FA JSON").action((opts) => {
4843
5748
  try {
4844
- const flowDir = resolveFlowDir(opts.dir);
4845
- const audit = auditFlowWorkspace(flowDir);
5749
+ const flowDir = (0, import_flowDirResolve.resolveFlowDir)(opts.dir, { json: opts.json });
5750
+ const audit = (0, import_flowWorkspaceAudit.auditFlowWorkspace)(flowDir);
4846
5751
  if (!opts.json) {
4847
5752
  console.log(`\u6D41\u7A0B\u76EE\u5F55: ${flowDir}`);
4848
5753
  console.log(audit.ok ? "\u2705 \u672C\u5730\u6587\u4EF6\u4E00\u81F4" : "\u26A0\uFE0F \u5B58\u5728\u4E0D\u4E00\u81F4\u9879");
@@ -4857,16 +5762,16 @@ function makeProjectCommand() {
4857
5762
  handleError(err);
4858
5763
  }
4859
5764
  });
4860
- cmd.command("repair-meta").description("\u6839\u636E flow.json \u4E0E \u8282\u70B9/ \u76EE\u5F55\u4FEE\u590D flow.meta.json\uFF08\u53CA node.info.json\uFF09").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--json", "\u8F93\u51FA JSON").action((opts) => {
5765
+ cmd.command("repair-meta").description("\u6839\u636E flow.json \u4E0E \u8282\u70B9/ \u76EE\u5F55\u4FEE\u590D flow.meta.json\uFF08\u53CA node.info.json\uFF09").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u987B\u542B flow.json\uFF1B\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09").option("--json", "\u8F93\u51FA JSON").action((opts) => {
4861
5766
  try {
4862
- const flowDir = resolveFlowDir(opts.dir);
4863
- const result = repairFlowMeta(flowDir);
5767
+ const flowDir = (0, import_flowDirResolve.resolveFlowDir)(opts.dir, { json: opts.json });
5768
+ const result = (0, import_flowWorkspaceAudit.repairFlowMeta)(flowDir);
4864
5769
  const meta = readMeta(flowDir);
4865
5770
  if (meta?.flowId) {
4866
- const audit = auditFlowWorkspace(flowDir);
5771
+ const audit = (0, import_flowWorkspaceAudit.auditFlowWorkspace)(flowDir);
4867
5772
  writeFlowPullScaffold(flowDir, {
4868
5773
  flowId: String(meta.flowId),
4869
- flowName: meta.flowName || import_path10.default.basename(flowDir),
5774
+ flowName: meta.flowName || import_path9.default.basename(flowDir),
4870
5775
  nodeCount: audit.canvasNodeCount,
4871
5776
  codeNodeCount: audit.canvasCodeNodeCount,
4872
5777
  pulledAt: meta.lastPulledAt,
@@ -4883,7 +5788,7 @@ function makeProjectCommand() {
4883
5788
  }
4884
5789
  for (const p of result.wroteNodeInfo) console.log(` + node.info.json \u2192 ${p}`);
4885
5790
  for (const w of result.warnings) console.log(` \u26A0 ${w}`);
4886
- const audit = auditFlowWorkspace(flowDir);
5791
+ const audit = (0, import_flowWorkspaceAudit.auditFlowWorkspace)(flowDir);
4887
5792
  console.log(audit.ok ? "\u2705 doctor \u68C0\u67E5\u901A\u8FC7" : "\u26A0\uFE0F \u4ECD\u6709\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u67E5\u770B doctor \u8F93\u51FA");
4888
5793
  }
4889
5794
  ok(result);
@@ -4895,8 +5800,9 @@ function makeProjectCommand() {
4895
5800
  }
4896
5801
 
4897
5802
  // cli/dazi-flow/src/commands/node.ts
4898
- var import_path11 = __toESM(require("path"), 1);
5803
+ var import_path10 = __toESM(require("path"), 1);
4899
5804
  var import_fs9 = __toESM(require("fs"), 1);
5805
+ var import_flowDirResolve2 = __toESM(require_flowDirResolve(), 1);
4900
5806
  var NODE_TYPE_REQUIRED = {
4901
5807
  "database-source": ["connectionId", "sql"],
4902
5808
  "database-sink": ["connectionId", "tableName"],
@@ -4927,9 +5833,6 @@ var NODE_TYPE_LABELS = {
4927
5833
  "folder-resource-import": "\u6587\u4EF6\u5939\u8D44\u6E90\u5BFC\u5165",
4928
5834
  "folder-resource-register": "\u8D44\u6E90\u6CE8\u518C"
4929
5835
  };
4930
- function resolveFlowDir2(dir) {
4931
- return import_path11.default.resolve(dir ?? process.cwd());
4932
- }
4933
5836
  function requireMeta(flowDir) {
4934
5837
  const meta = readMeta(flowDir);
4935
5838
  if (!meta?.flowId) {
@@ -4963,9 +5866,9 @@ function makeNodeCommand() {
4963
5866
  }
4964
5867
  ok({ types });
4965
5868
  });
4966
- cmd.command("pull").description("\u62C9\u53D6\u5355\u4E2A\u4EE3\u7801\u8282\u70B9\uFF08\u8986\u76D6\u672C\u5730 code.*\uFF09").requiredOption("--node <uuid>", "\u8282\u70B9 node_uuid").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
5869
+ cmd.command("pull").description("\u62C9\u53D6\u5355\u4E2A\u4EE3\u7801\u8282\u70B9\uFF08\u8986\u76D6\u672C\u5730 code.*\uFF09").requiredOption("--node <uuid>", "\u8282\u70B9 node_uuid").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u987B\u542B flow.json\uFF1B\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
4967
5870
  try {
4968
- const flowDir = resolveFlowDir2(opts.dir);
5871
+ const flowDir = (0, import_flowDirResolve2.resolveFlowDir)(opts.dir, { json: opts.json });
4969
5872
  const { meta, flowId } = requireMeta(flowDir);
4970
5873
  const r = await pullCodeNode(flowId, flowDir, meta, opts.node);
4971
5874
  writeMeta(flowDir, meta);
@@ -4975,9 +5878,9 @@ function makeNodeCommand() {
4975
5878
  handleError(err);
4976
5879
  }
4977
5880
  });
4978
- cmd.command("push").description("\u63D0\u4EA4\u5355\u4E2A\u4EE3\u7801\u8282\u70B9\uFF08\u4E50\u89C2\u9501\uFF1B\u8FDC\u7AEF\u51B2\u7A81\u9700 --force\uFF09").requiredOption("--node <uuid>", "\u8282\u70B9 node_uuid").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--force", "\u5FFD\u7565\u8FDC\u7AEF\u7248\u672C\u51B2\u7A81\uFF08\u8986\u76D6\uFF09").option("--dry-run", "\u4EC5\u68C0\u6D4B\uFF0C\u4E0D\u63D0\u4EA4").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
5881
+ cmd.command("push").description("\u63D0\u4EA4\u5355\u4E2A\u4EE3\u7801\u8282\u70B9\uFF08\u4E50\u89C2\u9501\uFF1B\u8FDC\u7AEF\u51B2\u7A81\u9700 --force\uFF09").requiredOption("--node <uuid>", "\u8282\u70B9 node_uuid").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u987B\u542B flow.json\uFF1B\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09").option("--force", "\u5FFD\u7565\u8FDC\u7AEF\u7248\u672C\u51B2\u7A81\uFF08\u8986\u76D6\uFF09").option("--dry-run", "\u4EC5\u68C0\u6D4B\uFF0C\u4E0D\u63D0\u4EA4").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
4979
5882
  try {
4980
- const flowDir = resolveFlowDir2(opts.dir);
5883
+ const flowDir = (0, import_flowDirResolve2.resolveFlowDir)(opts.dir, { json: opts.json });
4981
5884
  const { meta, flowId } = requireMeta(flowDir);
4982
5885
  const r = await pushCodeNode(flowId, flowDir, meta, opts.node, opts);
4983
5886
  writeMeta(flowDir, meta);
@@ -4991,9 +5894,9 @@ function makeNodeCommand() {
4991
5894
  handleError(err);
4992
5895
  }
4993
5896
  });
4994
- cmd.command("new").description("\u65B0\u5EFA\u8282\u70B9\uFF08POST \u5E73\u53F0\uFF0C\u5E76\u5199\u5165\u672C\u5730 flow.json \u4E0E\u4EE3\u7801\u6A21\u677F\uFF09").requiredOption("--type <nodeType>", "\u8282\u70B9\u7C7B\u578B\uFF08\u89C1 node types\uFF09").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--label <label>", "\u8282\u70B9\u663E\u793A\u540D").option("--id <nodeId>", "\u8BED\u4E49 id\uFF08\u7F3A\u7701\u7531\u5E73\u53F0\u751F\u6210\uFF09").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
5897
+ cmd.command("new").description("\u65B0\u5EFA\u8282\u70B9\uFF08POST \u5E73\u53F0\uFF0C\u5E76\u5199\u5165\u672C\u5730 flow.json \u4E0E\u4EE3\u7801\u6A21\u677F\uFF09").requiredOption("--type <nodeType>", "\u8282\u70B9\u7C7B\u578B\uFF08\u89C1 node types\uFF09").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u987B\u542B flow.json\uFF1B\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09").option("--label <label>", "\u8282\u70B9\u663E\u793A\u540D").option("--id <nodeId>", "\u8BED\u4E49 id\uFF08\u7F3A\u7701\u7531\u5E73\u53F0\u751F\u6210\uFF09").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
4995
5898
  try {
4996
- const flowDir = resolveFlowDir2(opts.dir);
5899
+ const flowDir = (0, import_flowDirResolve2.resolveFlowDir)(opts.dir, { json: opts.json });
4997
5900
  const { meta, flowId } = requireMeta(flowDir);
4998
5901
  const created = await apiRequest(`${FLOW_API}/${flowId}/nodes`, {
4999
5902
  method: "POST",
@@ -5014,19 +5917,19 @@ function makeNodeCommand() {
5014
5917
  data: created.data ?? {}
5015
5918
  };
5016
5919
  doc.nodes.push(stripped);
5017
- import_fs9.default.writeFileSync(import_path11.default.join(flowDir, FLOW_JSON), JSON.stringify(doc, null, 2), "utf8");
5920
+ import_fs9.default.writeFileSync(import_path10.default.join(flowDir, FLOW_JSON), JSON.stringify(doc, null, 2), "utf8");
5018
5921
  const nodeMeta = { nodeId: String(created.id ?? ""), nodeType: opts.type };
5019
5922
  const codeKey = NODE_TYPE_CODE_DATA_KEY[opts.type];
5020
5923
  if (codeKey) {
5021
5924
  const language = codeLanguageForNodeType(opts.type);
5022
5925
  const ext = codeFileExt(language);
5023
5926
  const dirName = sanitizeName(opts.label || String(created.id ?? "") || opts.type);
5024
- const nodeDirAbs = import_path11.default.join(flowDir, NODES_DIR, dirName);
5927
+ const nodeDirAbs = import_path10.default.join(flowDir, NODES_DIR, dirName);
5025
5928
  import_fs9.default.mkdirSync(nodeDirAbs, { recursive: true });
5026
5929
  const codeFile = `code.${ext}`;
5027
5930
  const template = CODE_TEMPLATES[language ?? ""] ?? "";
5028
- import_fs9.default.writeFileSync(import_path11.default.join(nodeDirAbs, codeFile), template, "utf8");
5029
- nodeMeta.dir = import_path11.default.posix.join(NODES_DIR, dirName);
5931
+ import_fs9.default.writeFileSync(import_path10.default.join(nodeDirAbs, codeFile), template, "utf8");
5932
+ nodeMeta.dir = import_path10.default.posix.join(NODES_DIR, dirName);
5030
5933
  nodeMeta.codeFile = codeFile;
5031
5934
  nodeMeta.codeLanguage = language;
5032
5935
  nodeMeta.codeHash = sha1(template);
@@ -5046,11 +5949,12 @@ function makeNodeCommand() {
5046
5949
  }
5047
5950
 
5048
5951
  // cli/dazi-flow/src/commands/run.ts
5049
- var import_path13 = __toESM(require("path"), 1);
5952
+ var import_path12 = __toESM(require("path"), 1);
5953
+ var import_flowDirResolve3 = __toESM(require_flowDirResolve(), 1);
5050
5954
 
5051
5955
  // cli/dazi-flow/src/lib/flowRun.ts
5052
5956
  var import_fs10 = __toESM(require("fs"), 1);
5053
- var import_path12 = __toESM(require("path"), 1);
5957
+ var import_path11 = __toESM(require("path"), 1);
5054
5958
  function classifyError(text) {
5055
5959
  const t = (text || "").toLowerCase();
5056
5960
  if (/(变量|variable).*(不存在|未找到|missing|not found|undefined)|缺少.*输入|no.*input|上游/.test(t)) {
@@ -5080,7 +5984,7 @@ function classifyError(text) {
5080
5984
  return { category: "\u672A\u77E5", hint: "\u8BF7\u67E5\u770B\u5B8C\u6574 traceback \u4E0E\u65E5\u5FD7\u3002" };
5081
5985
  }
5082
5986
  function runDir(flowDir) {
5083
- const d = import_path12.default.join(flowDir, RUN_DIR);
5987
+ const d = import_path11.default.join(flowDir, RUN_DIR);
5084
5988
  import_fs10.default.mkdirSync(d, { recursive: true });
5085
5989
  return d;
5086
5990
  }
@@ -5088,7 +5992,7 @@ function safeBase(name) {
5088
5992
  return (name || "run").replace(/[<>:"/\\|?*\x00-\x1f]/g, "_").trim() || "run";
5089
5993
  }
5090
5994
  function writeLastRun(flowDir, base, payload) {
5091
- const p = import_path12.default.join(runDir(flowDir), `${safeBase(base)}.last-run.json`);
5995
+ const p = import_path11.default.join(runDir(flowDir), `${safeBase(base)}.last-run.json`);
5092
5996
  import_fs10.default.writeFileSync(p, JSON.stringify(payload, null, 2), "utf8");
5093
5997
  return p;
5094
5998
  }
@@ -5126,7 +6030,7 @@ function writeErrorMarkdown(flowDir, base, input) {
5126
6030
  if (input.failureBundle != null) {
5127
6031
  lines.push("## FailureBundle", "", "```json", JSON.stringify(input.failureBundle, null, 2), "```", "");
5128
6032
  }
5129
- const p = import_path12.default.join(runDir(flowDir), `${safeBase(base)}.last-error.md`);
6033
+ const p = import_path11.default.join(runDir(flowDir), `${safeBase(base)}.last-error.md`);
5130
6034
  import_fs10.default.writeFileSync(p, lines.join("\n"), "utf8");
5131
6035
  return p;
5132
6036
  }
@@ -5260,16 +6164,13 @@ function writeRunProgressMarkdown(flowDir, base, snap, runId) {
5260
6164
  }
5261
6165
  }
5262
6166
  }
5263
- const p = import_path12.default.join(runDir(flowDir), `${safeBase(base)}.last-run.md`);
6167
+ const p = import_path11.default.join(runDir(flowDir), `${safeBase(base)}.last-run.md`);
5264
6168
  import_fs10.default.writeFileSync(p, lines.join("\n"), "utf8");
5265
6169
  return p;
5266
6170
  }
5267
6171
 
5268
6172
  // cli/dazi-flow/src/commands/run.ts
5269
6173
  var FLOW_API2 = "/api/data-pipelines/v1/flows";
5270
- function resolveFlowDir3(dir) {
5271
- return import_path13.default.resolve(dir ?? process.cwd());
5272
- }
5273
6174
  function sleep(ms) {
5274
6175
  return new Promise((r) => setTimeout(r, ms));
5275
6176
  }
@@ -5428,9 +6329,9 @@ function makeRunCommand() {
5428
6329
  handleError(err);
5429
6330
  }
5430
6331
  });
5431
- cmd.command("node-exec").description("\u6D4B\u8BD5\u8FD0\u884C\u5355\u4E2A\u8282\u70B9\uFF08\u6309 node_uuid\uFF0C\u7ED3\u679C\u843D\u76D8 _run/\uFF09").requiredOption("--node <uuid>", "\u8282\u70B9 node_uuid").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
6332
+ cmd.command("node-exec").description("\u6D4B\u8BD5\u8FD0\u884C\u5355\u4E2A\u8282\u70B9\uFF08\u6309 node_uuid\uFF0C\u7ED3\u679C\u843D\u76D8 _run/\uFF09").requiredOption("--node <uuid>", "\u8282\u70B9 node_uuid").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u987B\u542B flow.json\uFF1B\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
5432
6333
  try {
5433
- const flowDir = resolveFlowDir3(opts.dir);
6334
+ const flowDir = (0, import_flowDirResolve3.resolveFlowDir)(opts.dir, { json: opts.json });
5434
6335
  const meta = readMeta(flowDir);
5435
6336
  if (!meta?.flowId) {
5436
6337
  console.error("\u9519\u8BEF: \u7F3A\u5C11 flow.meta.json\uFF0C\u8BF7\u5148 project pull");
@@ -5443,7 +6344,7 @@ function makeRunCommand() {
5443
6344
  console.error(`\u9519\u8BEF: \u672A\u627E\u5230\u8282\u70B9 ${opts.node} \u7684\u8BED\u4E49 id\uFF08meta.nodeId\uFF09`);
5444
6345
  process.exit(1);
5445
6346
  }
5446
- const base = nodeMeta?.dir ? import_path13.default.basename(nodeMeta.dir) : nodeId;
6347
+ const base = nodeMeta?.dir ? import_path12.default.basename(nodeMeta.dir) : nodeId;
5447
6348
  await apiRequest(`${FLOW_API2}/${flowId}/debug-run`);
5448
6349
  const result = await apiRequest(
5449
6350
  `${FLOW_API2}/${flowId}/nodes/${encodeURIComponent(nodeId)}/run`,
@@ -5483,9 +6384,9 @@ function makeRunCommand() {
5483
6384
  handleError(err);
5484
6385
  }
5485
6386
  });
5486
- cmd.command("flow-exec").description("\u8FD0\u884C\u6574\u6D41\u7A0B\u5E76\u7B49\u5F85\u7ED3\u679C\uFF08\u5931\u8D25\u65F6\u843D\u76D8 _run/flow.last-error.md\uFF09").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--flow <flowId>", "Flow ID\uFF08\u7F3A\u7701\u8BFB meta\uFF09").option("--type <type>", "debug | preview", "debug").option("--timeout <sec>", "\u7B49\u5F85\u8D85\u65F6\u79D2\u6570", "120").option("--json", "\u8F93\u51FA JSON").option("--designer-progress", "\u8F93\u51FA NDJSON \u8FDB\u5EA6\u4E8B\u4EF6\uFF08\u4F9B VS Code \u8BBE\u8BA1\u5668\u89E3\u6790\uFF09").action(async (opts) => {
6387
+ cmd.command("flow-exec").description("\u8FD0\u884C\u6574\u6D41\u7A0B\u5E76\u7B49\u5F85\u7ED3\u679C\uFF08\u5931\u8D25\u65F6\u843D\u76D8 _run/flow.last-error.md\uFF09").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u987B\u542B flow.json\uFF1B\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09").option("--flow <flowId>", "Flow ID\uFF08\u7F3A\u7701\u8BFB meta\uFF09").option("--type <type>", "debug | preview", "debug").option("--timeout <sec>", "\u7B49\u5F85\u8D85\u65F6\u79D2\u6570", "120").option("--json", "\u8F93\u51FA JSON").option("--designer-progress", "\u8F93\u51FA NDJSON \u8FDB\u5EA6\u4E8B\u4EF6\uFF08\u4F9B VS Code \u8BBE\u8BA1\u5668\u89E3\u6790\uFF09").action(async (opts) => {
5487
6388
  try {
5488
- const flowDir = resolveFlowDir3(opts.dir);
6389
+ const flowDir = (0, import_flowDirResolve3.resolveFlowDir)(opts.dir, { json: opts.json });
5489
6390
  const meta = readMeta(flowDir);
5490
6391
  const flowId = opts.flow ?? (meta?.flowId != null ? String(meta.flowId) : "");
5491
6392
  if (!flowId) {
@@ -5589,9 +6490,9 @@ function makeRunCommand() {
5589
6490
  handleError(err);
5590
6491
  }
5591
6492
  });
5592
- cmd.command("failure").description("\u62C9\u53D6\u67D0\u6B21 Run \u7684 failure-bundle \u5E76\u843D\u76D8 _run/flow.last-error.md").requiredOption("--run <runId>", "Run ID").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
6493
+ cmd.command("failure").description("\u62C9\u53D6\u67D0\u6B21 Run \u7684 failure-bundle \u5E76\u843D\u76D8 _run/flow.last-error.md").requiredOption("--run <runId>", "Run ID").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u987B\u542B flow.json\uFF1B\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
5593
6494
  try {
5594
- const flowDir = resolveFlowDir3(opts.dir);
6495
+ const flowDir = (0, import_flowDirResolve3.resolveFlowDir)(opts.dir, { json: opts.json });
5595
6496
  const bundle = await apiRequest(`${FLOW_API2}/runs/${opts.run}/failure-bundle`);
5596
6497
  const errorFile = writeErrorMarkdown(flowDir, "flow", {
5597
6498
  title: `\u5931\u8D25\u5206\u6790\uFF08Run ${opts.run}\uFF09`,
@@ -5608,7 +6509,7 @@ function makeRunCommand() {
5608
6509
  }
5609
6510
 
5610
6511
  // cli/dazi-flow/src/commands/source.ts
5611
- var import_path14 = __toESM(require("path"), 1);
6512
+ var import_path13 = __toESM(require("path"), 1);
5612
6513
  var import_fs11 = __toESM(require("fs"), 1);
5613
6514
  function makeSourceCommand() {
5614
6515
  const cmd = new Command("source").description("\u6570\u636E\u6E90\u7BA1\u7406");
@@ -5683,9 +6584,9 @@ function makeSourceCommand() {
5683
6584
  `/api/connections/${sourceId}/tables${qs}`
5684
6585
  );
5685
6586
  const ws = resolveWorkspace();
5686
- const dataDir = import_path14.default.join(ws.flows, opts.flow, "data");
6587
+ const dataDir = import_path13.default.join(ws.flows, opts.flow, "data");
5687
6588
  if (!import_fs11.default.existsSync(dataDir)) import_fs11.default.mkdirSync(dataDir, { recursive: true });
5688
- const outFile = import_path14.default.join(dataDir, `${tableName}.schema.json`);
6589
+ const outFile = import_path13.default.join(dataDir, `${tableName}.schema.json`);
5689
6590
  import_fs11.default.writeFileSync(outFile, JSON.stringify({
5690
6591
  sourceId,
5691
6592
  tableName,
@@ -5760,7 +6661,7 @@ function makeDataspaceCommand() {
5760
6661
  }
5761
6662
 
5762
6663
  // cli/dazi-flow/src/commands/plan.ts
5763
- var import_path15 = __toESM(require("path"), 1);
6664
+ var import_path14 = __toESM(require("path"), 1);
5764
6665
  var import_fs12 = __toESM(require("fs"), 1);
5765
6666
  function makePlanCommand() {
5766
6667
  const cmd = new Command("plan").description("Flow \u6267\u884C\u8BA1\u5212");
@@ -5772,8 +6673,8 @@ function makePlanCommand() {
5772
6673
  { method: "POST", body: { input } }
5773
6674
  );
5774
6675
  const ws = resolveWorkspace();
5775
- const outFile = opts.out ?? import_path15.default.join(ws.flows, flowId, "plans", "plan.json");
5776
- const outDir = import_path15.default.dirname(outFile);
6676
+ const outFile = opts.out ?? import_path14.default.join(ws.flows, flowId, "plans", "plan.json");
6677
+ const outDir = import_path14.default.dirname(outFile);
5777
6678
  if (!import_fs12.default.existsSync(outDir)) import_fs12.default.mkdirSync(outDir, { recursive: true });
5778
6679
  import_fs12.default.writeFileSync(outFile, JSON.stringify(plan, null, 2), "utf-8");
5779
6680
  if (opts.json) {
@@ -5789,7 +6690,7 @@ function makePlanCommand() {
5789
6690
  cmd.command("apply <flowId>").description("\u5C06\u672C\u5730\u8BA1\u5212\u6587\u4EF6\u63A8\u9001\u5230\u5E73\u53F0\u5E76\u5E94\u7528").option("--file <path>", "\u8BA1\u5212\u6587\u4EF6\uFF08\u9ED8\u8BA4 flows/<flowId>/plans/plan.json\uFF09").option("--dry-run", "\u4EC5\u9A8C\u8BC1\uFF0C\u4E0D\u5B9E\u9645\u5E94\u7528").action(async (flowId, opts) => {
5790
6691
  try {
5791
6692
  const ws = resolveWorkspace();
5792
- const planFile = opts.file ?? import_path15.default.join(ws.flows, flowId, "plans", "plan.json");
6693
+ const planFile = opts.file ?? import_path14.default.join(ws.flows, flowId, "plans", "plan.json");
5793
6694
  if (!import_fs12.default.existsSync(planFile)) {
5794
6695
  console.error(`\u8BA1\u5212\u6587\u4EF6\u4E0D\u5B58\u5728: ${planFile}\uFF0C\u8BF7\u5148\u6267\u884C plan compile`);
5795
6696
  process.exit(1);
@@ -5809,8 +6710,8 @@ function makePlanCommand() {
5809
6710
  `/api/data-pipelines/v1/flows/${flowId}/snapshot`
5810
6711
  );
5811
6712
  const ws = resolveWorkspace();
5812
- const outFile = opts.out ?? import_path15.default.join(ws.flows, flowId, "plans", `plan-${opts.type}.md`);
5813
- const outDir = import_path15.default.dirname(outFile);
6713
+ const outFile = opts.out ?? import_path14.default.join(ws.flows, flowId, "plans", `plan-${opts.type}.md`);
6714
+ const outDir = import_path14.default.dirname(outFile);
5814
6715
  if (!import_fs12.default.existsSync(outDir)) import_fs12.default.mkdirSync(outDir, { recursive: true });
5815
6716
  import_fs12.default.writeFileSync(outFile, doc.content, "utf-8");
5816
6717
  console.log(`\u2705 Markdown \u6587\u6863\u5DF2\u4FDD\u5B58: ${outFile}`);
@@ -5825,8 +6726,8 @@ function makePlanCommand() {
5825
6726
  `/api/data-pipelines/v1/dq-ai-code-prompt`
5826
6727
  );
5827
6728
  const ws = resolveWorkspace();
5828
- const outFile = opts.out ?? import_path15.default.join(ws.flows, flowId, "plans", "llm-guide.md");
5829
- const outDir = import_path15.default.dirname(outFile);
6729
+ const outFile = opts.out ?? import_path14.default.join(ws.flows, flowId, "plans", "llm-guide.md");
6730
+ const outDir = import_path14.default.dirname(outFile);
5830
6731
  if (!import_fs12.default.existsSync(outDir)) import_fs12.default.mkdirSync(outDir, { recursive: true });
5831
6732
  import_fs12.default.writeFileSync(outFile, guide.content, "utf-8");
5832
6733
  console.log(`\u2705 LLM \u5F15\u5BFC\u6587\u6863\u5DF2\u4FDD\u5B58: ${outFile}`);
@@ -5841,10 +6742,10 @@ function makePlanCommand() {
5841
6742
  `/api/data-pipelines/v1/compile-plan`
5842
6743
  );
5843
6744
  const ws = resolveWorkspace();
5844
- const outDir = opts.out ?? import_path15.default.join(ws.flows, flowId, "plans", "db");
6745
+ const outDir = opts.out ?? import_path14.default.join(ws.flows, flowId, "plans", "db");
5845
6746
  if (!import_fs12.default.existsSync(outDir)) import_fs12.default.mkdirSync(outDir, { recursive: true });
5846
6747
  for (const f of scaffold.files ?? []) {
5847
- const outFile = import_path15.default.join(outDir, f.name);
6748
+ const outFile = import_path14.default.join(outDir, f.name);
5848
6749
  import_fs12.default.writeFileSync(outFile, f.content, "utf-8");
5849
6750
  console.log(` \u2192 ${outFile}`);
5850
6751
  }
@@ -5858,7 +6759,7 @@ function makePlanCommand() {
5858
6759
  }
5859
6760
 
5860
6761
  // cli/dazi-flow/src/commands/file.ts
5861
- var import_path16 = __toESM(require("path"), 1);
6762
+ var import_path15 = __toESM(require("path"), 1);
5862
6763
  var import_fs13 = __toESM(require("fs"), 1);
5863
6764
  function makeFileCommand() {
5864
6765
  const cmd = new Command("file").description("\u5E73\u53F0\u6258\u7BA1\u6587\u4EF6\u7BA1\u7406");
@@ -5904,13 +6805,13 @@ function makeFileCommand() {
5904
6805
  });
5905
6806
  cmd.command("upload <localFile>").description("\u4E0A\u4F20\u672C\u5730\u6587\u4EF6\u5230\u5E73\u53F0").option("--space <spaceId>", "\u7A7A\u95F4 ID").option("--remote-path <path>", "\u8FDC\u7AEF\u8DEF\u5F84\uFF08\u9ED8\u8BA4\u4E0E\u6587\u4EF6\u540D\u76F8\u540C\uFF09").option("--overwrite", "\u8986\u76D6\u5DF2\u5B58\u5728\u6587\u4EF6").action(async (localFile, opts) => {
5906
6807
  try {
5907
- const filePath = import_path16.default.resolve(localFile);
6808
+ const filePath = import_path15.default.resolve(localFile);
5908
6809
  if (!import_fs13.default.existsSync(filePath)) {
5909
6810
  console.error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`);
5910
6811
  process.exit(1);
5911
6812
  }
5912
6813
  const content = import_fs13.default.readFileSync(filePath);
5913
- const remotePath = opts.remotePath ?? import_path16.default.basename(filePath);
6814
+ const remotePath = opts.remotePath ?? import_path15.default.basename(filePath);
5914
6815
  const endpoint = opts.space ? `/api/v1/spaces/${opts.space}/files/upload` : "/api/v1/files/upload";
5915
6816
  const result = await apiRequest(endpoint, {
5916
6817
  method: "POST",
@@ -5936,14 +6837,14 @@ function makeFileCommand() {
5936
6837
  const content = data.encoding === "base64" ? Buffer.from(data.content, "base64") : Buffer.from(data.content, "utf-8");
5937
6838
  let outFile;
5938
6839
  if (opts.out) {
5939
- outFile = import_path16.default.resolve(opts.out);
6840
+ outFile = import_path15.default.resolve(opts.out);
5940
6841
  } else if (opts.flow) {
5941
6842
  const ws = resolveWorkspace();
5942
- const dataDir = import_path16.default.join(ws.flows, opts.flow, "data");
6843
+ const dataDir = import_path15.default.join(ws.flows, opts.flow, "data");
5943
6844
  if (!import_fs13.default.existsSync(dataDir)) import_fs13.default.mkdirSync(dataDir, { recursive: true });
5944
- outFile = import_path16.default.join(dataDir, import_path16.default.basename(remotePath));
6845
+ outFile = import_path15.default.join(dataDir, import_path15.default.basename(remotePath));
5945
6846
  } else {
5946
- outFile = import_path16.default.join(process.cwd(), import_path16.default.basename(remotePath));
6847
+ outFile = import_path15.default.join(process.cwd(), import_path15.default.basename(remotePath));
5947
6848
  }
5948
6849
  import_fs13.default.writeFileSync(outFile, content);
5949
6850
  console.log(`\u2705 \u5DF2\u4E0B\u8F7D: ${outFile}`);
@@ -5956,18 +6857,18 @@ function makeFileCommand() {
5956
6857
  }
5957
6858
 
5958
6859
  // cli/dazi-flow/src/commands/data.ts
5959
- var import_path17 = __toESM(require("path"), 1);
6860
+ var import_path16 = __toESM(require("path"), 1);
5960
6861
  var import_fs14 = __toESM(require("fs"), 1);
5961
6862
  function makeDataCommand() {
5962
6863
  const cmd = new Command("data").description("Flow \u6570\u636E\u4E0A\u4F20");
5963
6864
  cmd.command("upload <localFile>").description("\u5C06\u672C\u5730\u6570\u636E\u6587\u4EF6\u4E0A\u4F20\u5230\u5E73\u53F0\uFF08CSV / JSON / Parquet\uFF09").option("--space <spaceId>", "\u76EE\u6807\u7A7A\u95F4 ID").option("--flow <flowId>", "\u5173\u8054 Flow ID\uFF08\u5199\u5165 flows/<flowId> \u5143\u6570\u636E\uFF09").option("--table <name>", "\u76EE\u6807\u8868\u540D\uFF08\u82E5\u5E73\u53F0\u652F\u6301\u76F4\u63A5\u5199\u8868\uFF09").option("--overwrite", "\u8986\u76D6\u5DF2\u6709\u6570\u636E").option("--json", "\u8F93\u51FA JSON").action(async (localFile, opts) => {
5964
6865
  try {
5965
- const filePath = import_path17.default.resolve(localFile);
6866
+ const filePath = import_path16.default.resolve(localFile);
5966
6867
  if (!import_fs14.default.existsSync(filePath)) {
5967
6868
  console.error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`);
5968
6869
  process.exit(1);
5969
6870
  }
5970
- const ext = import_path17.default.extname(filePath).toLowerCase();
6871
+ const ext = import_path16.default.extname(filePath).toLowerCase();
5971
6872
  const supportedExts = [".csv", ".json", ".jsonl", ".parquet", ".tsv"];
5972
6873
  if (!supportedExts.includes(ext)) {
5973
6874
  console.error(`\u4E0D\u652F\u6301\u7684\u6587\u4EF6\u683C\u5F0F: ${ext}\uFF08\u652F\u6301: ${supportedExts.join(", ")}\uFF09`);
@@ -5977,7 +6878,7 @@ function makeDataCommand() {
5977
6878
  const body = {
5978
6879
  content: content.toString("base64"),
5979
6880
  encoding: "base64",
5980
- fileName: import_path17.default.basename(filePath),
6881
+ fileName: import_path16.default.basename(filePath),
5981
6882
  format: ext.slice(1),
5982
6883
  overwrite: opts.overwrite ?? false
5983
6884
  };
@@ -6005,10 +6906,8 @@ function makeDataCommand() {
6005
6906
  }
6006
6907
 
6007
6908
  // cli/dazi-flow/src/commands/variable.ts
6008
- var import_path18 = __toESM(require("path"), 1);
6009
- function resolveFlowDir4(dir) {
6010
- return import_path18.default.resolve(dir ?? process.cwd());
6011
- }
6909
+ var import_path17 = __toESM(require("path"), 1);
6910
+ var import_flowDirResolve4 = __toESM(require_flowDirResolve(), 1);
6012
6911
  function requireFlowId(flowDir) {
6013
6912
  const meta = readMeta(flowDir);
6014
6913
  if (!meta?.flowId) {
@@ -6019,22 +6918,22 @@ function requireFlowId(flowDir) {
6019
6918
  }
6020
6919
  function makeVariableCommand() {
6021
6920
  const cmd = new Command("variable").description("\u6D41\u7A0B\u8FD0\u884C\u671F\u53D8\u91CF\uFF08\u540C\u6B65\u5230\u672C\u5730 \u53D8\u91CF/ \u76EE\u5F55\uFF09");
6022
- cmd.command("sync").description("\u4ECE\u8C03\u8BD5 Run \u540C\u6B65\u5168\u90E8\u53D8\u91CF\u5230 \u53D8\u91CF/<name>.json\uFF08\u542B schema \u4E0E\u524D N \u884C\u9884\u89C8\uFF09").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--limit <n>", "\u8868\u53D8\u91CF\u9884\u89C8\u884C\u6570", "10").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
6921
+ cmd.command("sync").description("\u4ECE\u8C03\u8BD5 Run \u540C\u6B65\u5168\u90E8\u53D8\u91CF\u5230 \u53D8\u91CF/<name>.json\uFF08\u542B schema \u4E0E\u524D N \u884C\u9884\u89C8\uFF09").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u987B\u542B flow.json\uFF1B\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09").option("--limit <n>", "\u8868\u53D8\u91CF\u9884\u89C8\u884C\u6570", "10").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
6023
6922
  try {
6024
- const flowDir = resolveFlowDir4(opts.dir);
6923
+ const flowDir = (0, import_flowDirResolve4.resolveFlowDir)(opts.dir, { json: opts.json });
6025
6924
  const flowId = requireFlowId(flowDir);
6026
6925
  const previewLimit = Math.max(1, Math.min(100, parseInt(opts.limit, 10) || 10));
6027
6926
  if (!opts.json) console.log(`\u6B63\u5728\u540C\u6B65\u53D8\u91CF\uFF08flowId=${flowId}\uFF09\u2026`);
6028
6927
  const result = await syncAllVariables(flowId, flowDir, previewLimit);
6029
- if (!opts.json) console.log(`\u2705 \u5DF2\u540C\u6B65 ${result.count} \u4E2A\u53D8\u91CF \u2192 ${import_path18.default.join(flowDir, "\u53D8\u91CF")}`);
6928
+ if (!opts.json) console.log(`\u2705 \u5DF2\u540C\u6B65 ${result.count} \u4E2A\u53D8\u91CF \u2192 ${import_path17.default.join(flowDir, "\u53D8\u91CF")}`);
6030
6929
  ok({ flowId, flowDir, ...result });
6031
6930
  } catch (err) {
6032
6931
  handleError(err);
6033
6932
  }
6034
6933
  });
6035
- cmd.command("pull").description("\u62C9\u53D6\u5355\u4E2A\u53D8\u91CF\u5230 \u53D8\u91CF/<name>.json").requiredOption("--name <varName>", "\u53D8\u91CF\u540D\uFF08output_variable_name\uFF09").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u9ED8\u8BA4 cwd\uFF09").option("--limit <n>", "\u8868\u53D8\u91CF\u9884\u89C8\u884C\u6570", "10").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
6934
+ cmd.command("pull").description("\u62C9\u53D6\u5355\u4E2A\u53D8\u91CF\u5230 \u53D8\u91CF/<name>.json").requiredOption("--name <varName>", "\u53D8\u91CF\u540D\uFF08output_variable_name\uFF09").option("--dir <dir>", "\u6D41\u7A0B\u76EE\u5F55\uFF08\u987B\u542B flow.json\uFF1B\u63A8\u8350\u7EDD\u5BF9\u8DEF\u5F84\uFF09").option("--limit <n>", "\u8868\u53D8\u91CF\u9884\u89C8\u884C\u6570", "10").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
6036
6935
  try {
6037
- const flowDir = resolveFlowDir4(opts.dir);
6936
+ const flowDir = (0, import_flowDirResolve4.resolveFlowDir)(opts.dir, { json: opts.json });
6038
6937
  const flowId = requireFlowId(flowDir);
6039
6938
  const previewLimit = Math.max(1, Math.min(100, parseInt(opts.limit, 10) || 10));
6040
6939
  const result = await syncVariableByName(flowId, flowDir, opts.name, previewLimit);
@@ -6241,201 +7140,17 @@ function makeMcpCommand() {
6241
7140
  }
6242
7141
 
6243
7142
  // cli/dazi-flow/src/commands/managedFiles.ts
6244
- var import_path20 = __toESM(require("path"), 1);
6245
- var import_fs16 = __toESM(require("fs"), 1);
6246
-
6247
- // src/shared/managedFilesBundle.ts
6248
- var import_fs15 = __toESM(require("fs"));
6249
- var import_path19 = __toESM(require("path"));
6250
-
6251
- // src/shared/managedFilesTypes.ts
6252
- var MANAGED_FILES_API = "/api/data-pipelines/v1/managed-files";
6253
- var MANAGED_FS_API = "/api/data-pipelines/v1/managed-fs";
6254
- var MANAGED_BUNDLE_FILES = {
6255
- manifest: "\u6587\u4EF6\u4FE1\u606F.json",
6256
- originalPrefix: "\u539F\u6587\u4EF6",
6257
- nativeDigest: "\u539F\u751F\u89E3\u6790.md",
6258
- aiStructure: "\u8868\u7ED3\u6784.json",
6259
- reportLayout: "\u62A5\u8868\u5E03\u5C40.json"
6260
- };
6261
-
6262
- // src/shared/managedFilesBundle.ts
6263
- function apiBase(auth) {
6264
- return auth.serverUrl.replace(/\/$/, "");
6265
- }
6266
- function sanitizeDirPart(raw) {
6267
- return raw.trim().replace(/\s+/g, "_").replace(/[<>:"|?*/\\]/g, "_").slice(0, 80) || "file";
6268
- }
6269
- function managedFileLocalFolderName(meta) {
6270
- const base = sanitizeDirPart(meta.display_name ?? meta.file_id);
6271
- const shortId = meta.file_id.slice(0, 8);
6272
- return `${base}_${shortId}`;
6273
- }
6274
- function managedFileLocalDir(filesRoot, meta) {
6275
- return import_path19.default.join(filesRoot, managedFileLocalFolderName(meta));
6276
- }
6277
- function isExcelManagedFile(meta) {
6278
- const ext = (meta.file_ext ?? import_path19.default.extname(meta.display_name ?? "")).toLowerCase();
6279
- if (ext === ".xlsx" || ext === ".xls") return true;
6280
- const mime = (meta.mime_type ?? "").toLowerCase();
6281
- return mime.includes("spreadsheet") || mime.endsWith(".sheet");
6282
- }
6283
- async function fetchJson(auth, urlPath, method = "GET") {
6284
- const res = await fetch(`${apiBase(auth)}${urlPath}`, {
6285
- method,
6286
- headers: {
6287
- Authorization: `Bearer ${auth.token}`,
6288
- Accept: "application/json",
6289
- ...method === "POST" ? { "Content-Type": "application/json" } : {}
6290
- }
6291
- });
6292
- if (!res.ok) {
6293
- const text = await res.text().catch(() => "");
6294
- throw new Error(`HTTP ${res.status}: ${text || res.statusText}`);
6295
- }
6296
- return res.json();
6297
- }
6298
- async function downloadBinary(auth, urlPath, destPath) {
6299
- const res = await fetch(`${apiBase(auth)}${urlPath}`, {
6300
- headers: { Authorization: `Bearer ${auth.token}` }
6301
- });
6302
- if (!res.ok) {
6303
- const text = await res.text().catch(() => "");
6304
- throw new Error(`\u4E0B\u8F7D\u5931\u8D25 HTTP ${res.status}: ${text || res.statusText}`);
6305
- }
6306
- const buf = Buffer.from(await res.arrayBuffer());
6307
- import_fs15.default.mkdirSync(import_path19.default.dirname(destPath), { recursive: true });
6308
- import_fs15.default.writeFileSync(destPath, buf);
6309
- }
6310
- async function resolveNativeDigest(auth, fileId, parseIfMissing) {
6311
- if (!parseIfMissing) {
6312
- try {
6313
- return await fetchJson(
6314
- auth,
6315
- `${MANAGED_FILES_API}/${encodeURIComponent(fileId)}/excel-native-digest`
6316
- );
6317
- } catch {
6318
- return null;
6319
- }
6320
- }
6321
- try {
6322
- let resp = await fetchJson(
6323
- auth,
6324
- `${MANAGED_FILES_API}/${encodeURIComponent(fileId)}/excel-native-digest`
6325
- );
6326
- if (resp.markdown) return resp;
6327
- } catch {
6328
- }
6329
- try {
6330
- return await fetchJson(
6331
- auth,
6332
- `${MANAGED_FILES_API}/${encodeURIComponent(fileId)}/excel-native-parse`,
6333
- "POST"
6334
- );
6335
- } catch {
6336
- return null;
6337
- }
6338
- }
6339
- async function resolveAiStructure(auth, fileId) {
6340
- try {
6341
- return await fetchJson(
6342
- auth,
6343
- `${MANAGED_FILES_API}/${encodeURIComponent(fileId)}/excel-ai-structure`
6344
- );
6345
- } catch {
6346
- return null;
6347
- }
6348
- }
6349
- async function resolveReportLayout(auth, fileId) {
6350
- try {
6351
- return await fetchJson(
6352
- auth,
6353
- `${MANAGED_FILES_API}/${encodeURIComponent(fileId)}/excel-report-layout`
6354
- );
6355
- } catch {
6356
- return null;
6357
- }
6358
- }
6359
- async function pullManagedFileBundle(opts) {
6360
- const { auth, meta } = opts;
6361
- const fileId = meta.file_id?.trim();
6362
- if (!fileId) throw new Error("\u7F3A\u5C11 file_id\uFF0C\u4EC5\u652F\u6301\u5DF2\u767B\u8BB0\u6587\u4EF6");
6363
- const dir = managedFileLocalDir(opts.filesRootDir, meta);
6364
- import_fs15.default.mkdirSync(dir, { recursive: true });
6365
- const written = [];
6366
- const notes = [];
6367
- const pulledAt = (/* @__PURE__ */ new Date()).toISOString();
6368
- const ext = (meta.file_ext && meta.file_ext.startsWith(".") ? meta.file_ext : meta.file_ext ? `.${meta.file_ext}` : "") || import_path19.default.extname(meta.display_name ?? meta.stored_filename ?? "") || "";
6369
- const originalName = `${MANAGED_BUNDLE_FILES.originalPrefix}${ext || ""}`;
6370
- const originalPath = import_path19.default.join(dir, originalName);
6371
- await downloadBinary(
6372
- auth,
6373
- `${MANAGED_FILES_API}/${encodeURIComponent(fileId)}/content`,
6374
- originalPath
6375
- );
6376
- written.push(originalName);
6377
- if (isExcelManagedFile(meta)) {
6378
- const digest = await resolveNativeDigest(auth, fileId, opts.parseNativeIfMissing !== false);
6379
- if (digest?.markdown) {
6380
- import_fs15.default.writeFileSync(import_path19.default.join(dir, MANAGED_BUNDLE_FILES.nativeDigest), digest.markdown, "utf8");
6381
- written.push(MANAGED_BUNDLE_FILES.nativeDigest);
6382
- } else {
6383
- notes.push("\u539F\u751F\u89E3\u6790\u672A\u751F\u6210\uFF1A\u8BF7\u5728\u5E73\u53F0\u300C\u6587\u4EF6\u4E0A\u4F20\u7BA1\u7406\u300D\u4E2D\u5BF9 Excel \u6267\u884C\u300C\u539F\u751F\u89E3\u6790\u300D\u540E\u91CD\u8BD5\u62C9\u53D6");
6384
- }
6385
- const structureResp = await resolveAiStructure(auth, fileId);
6386
- if (structureResp?.structure) {
6387
- import_fs15.default.writeFileSync(
6388
- import_path19.default.join(dir, MANAGED_BUNDLE_FILES.aiStructure),
6389
- JSON.stringify(structureResp.structure, null, 2),
6390
- "utf8"
6391
- );
6392
- written.push(MANAGED_BUNDLE_FILES.aiStructure);
6393
- } else {
6394
- notes.push("\u8868\u7ED3\u6784\u672A\u751F\u6210\uFF1A\u8BF7\u5148\u5728\u5E73\u53F0\u5B8C\u6210\u300C\u539F\u751F\u89E3\u6790\u300D\u4E0E\u300CAI \u8868\u7ED3\u6784\u300D\u540E\u518D\u62C9\u53D6\uFF0C\u6216\u4EC5\u4F7F\u7528\u539F\u751F\u89E3\u6790.md \u4EA4\u7ED9 AI");
6395
- }
6396
- const layoutResp = await resolveReportLayout(auth, fileId);
6397
- if (layoutResp?.layout) {
6398
- import_fs15.default.writeFileSync(
6399
- import_path19.default.join(dir, MANAGED_BUNDLE_FILES.reportLayout),
6400
- JSON.stringify(layoutResp.layout, null, 2),
6401
- "utf8"
6402
- );
6403
- written.push(MANAGED_BUNDLE_FILES.reportLayout);
6404
- } else {
6405
- notes.push("\u62A5\u8868\u5E03\u5C40\u672A\u751F\u6210\uFF1A\u8BF7\u5728\u5E73\u53F0\u300C\u62A5\u8868\u5E03\u5C40\u300D\u9875\u7B7E\u6267\u884C\u89C4\u5219\u89E3\u6790\u6216 AI \u62A5\u8868\u5E03\u5C40\u540E\u518D\u62C9\u53D6");
6406
- }
6407
- } else {
6408
- notes.push("\u975E Excel \u6587\u4EF6\uFF1A\u4EC5\u62C9\u53D6\u539F\u6587\u4EF6\u4E0E\u6587\u4EF6\u4FE1\u606F\uFF08\u65E0\u539F\u751F\u89E3\u6790/\u8868\u7ED3\u6784\uFF09");
6409
- }
6410
- const manifest = {
6411
- pulledAt,
6412
- file_id: fileId,
6413
- display_name: meta.display_name,
6414
- relative_dir: meta.relative_dir,
6415
- localDir: dir,
6416
- files: written,
6417
- notes: notes.length ? notes : void 0
6418
- };
6419
- manifest.files = [...written, MANAGED_BUNDLE_FILES.manifest];
6420
- import_fs15.default.writeFileSync(
6421
- import_path19.default.join(dir, MANAGED_BUNDLE_FILES.manifest),
6422
- JSON.stringify({ ...meta, ...manifest }, null, 2),
6423
- "utf8"
6424
- );
6425
- return { dir, manifest };
6426
- }
6427
- function findManagedFileInBrowse(items, fileId) {
6428
- return items.find((i) => i.file_id === fileId);
6429
- }
6430
-
6431
- // cli/dazi-flow/src/commands/managedFiles.ts
7143
+ var import_path18 = __toESM(require("path"), 1);
7144
+ var import_fs15 = __toESM(require("fs"), 1);
7145
+ var import_managedFilesBundle = __toESM(require_managedFilesBundle(), 1);
7146
+ var import_managedFilesTypes = __toESM(require_managedFilesTypes(), 1);
6432
7147
  function makeManagedFilesCommand() {
6433
7148
  const cmd = new Command("managed-files").description("\u6587\u4EF6\u4E0A\u4F20\u7BA1\u7406\uFF08managed_files\uFF09");
6434
7149
  cmd.command("dirs").description("\u5217\u51FA\u7BA1\u7406\u76EE\u5F55\u5B50\u6587\u4EF6\u5939").option("--path <relativePath>", "\u76F8\u5BF9\u8DEF\u5F84\uFF08\u9ED8\u8BA4\u6839\uFF09", "").option("--json", "\u8F93\u51FA JSON").action(async (opts) => {
6435
7150
  try {
6436
7151
  const qs = new URLSearchParams();
6437
7152
  qs.set("path", opts.path ?? "");
6438
- const data = await apiRequest(`${MANAGED_FS_API}/children?${qs}`);
7153
+ const data = await apiRequest(`${import_managedFilesTypes.MANAGED_FS_API}/children?${qs}`);
6439
7154
  if (!opts.json) {
6440
7155
  for (const c of data.children ?? []) {
6441
7156
  console.log(` ${c.path || "/"} ${c.name}`);
@@ -6451,7 +7166,7 @@ function makeManagedFilesCommand() {
6451
7166
  const qs = new URLSearchParams();
6452
7167
  qs.set("relative_dir", opts.relativeDir ?? "");
6453
7168
  qs.set("include_unregistered", "false");
6454
- const data = await apiRequest(`${MANAGED_FILES_API}/browse?${qs}`);
7169
+ const data = await apiRequest(`${import_managedFilesTypes.MANAGED_FILES_API}/browse?${qs}`);
6455
7170
  if (!opts.json) {
6456
7171
  for (const it of data.items ?? []) {
6457
7172
  if (!it.file_id) continue;
@@ -6470,17 +7185,17 @@ function makeManagedFilesCommand() {
6470
7185
  const qs = new URLSearchParams();
6471
7186
  qs.set("relative_dir", opts.relativeDir ?? "");
6472
7187
  qs.set("include_unregistered", "false");
6473
- const browse = await apiRequest(`${MANAGED_FILES_API}/browse?${qs}`);
6474
- let meta = findManagedFileInBrowse(browse.items ?? [], opts.fileId);
7188
+ const browse = await apiRequest(`${import_managedFilesTypes.MANAGED_FILES_API}/browse?${qs}`);
7189
+ let meta = (0, import_managedFilesBundle.findManagedFileInBrowse)(browse.items ?? [], opts.fileId);
6475
7190
  if (!meta) {
6476
7191
  console.error(`\u9519\u8BEF: \u5728 relative_dir="${opts.relativeDir ?? ""}" \u672A\u627E\u5230 file_id=${opts.fileId}`);
6477
7192
  console.error("\u63D0\u793A: \u6307\u5B9A\u6B63\u786E\u7684 --relative-dir\uFF0C\u6216\u4ECE\u4FA7\u680F\u6811\u53F3\u952E\u62C9\u53D6");
6478
7193
  process.exit(1);
6479
7194
  }
6480
7195
  const ws = resolveWorkspace();
6481
- const filesRoot = import_path20.default.resolve(opts.filesRoot ?? ws.files);
6482
- import_fs16.default.mkdirSync(filesRoot, { recursive: true });
6483
- const result = await pullManagedFileBundle({
7196
+ const filesRoot = import_path18.default.resolve(opts.filesRoot ?? ws.files);
7197
+ import_fs15.default.mkdirSync(filesRoot, { recursive: true });
7198
+ const result = await (0, import_managedFilesBundle.pullManagedFileBundle)({
6484
7199
  auth: { serverUrl: auth.serverUrl, token: auth.token },
6485
7200
  meta,
6486
7201
  filesRootDir: filesRoot,
@@ -6503,7 +7218,7 @@ function makeManagedFilesCommand() {
6503
7218
 
6504
7219
  // cli/dazi-flow/src/index.ts
6505
7220
  var program2 = new Command();
6506
- program2.name("dazi-flow").description("\u642D\u5B50 Flow CLI \u2014 \u5DE5\u4F5C\u6D41\u7BA1\u7406").version("3.0.0", "-v, --version");
7221
+ program2.name("dazi-flow").description("\u642D\u5B50 Flow CLI \u2014 \u5DE5\u4F5C\u6D41\u7BA1\u7406").version("3.0.3", "-v, --version");
6507
7222
  program2.addCommand(makeFlowsCommand());
6508
7223
  program2.addCommand(makeSnapshotCommand());
6509
7224
  program2.addCommand(makeProjectCommand());