@guandata/guanwf 0.1.820 → 0.1.821
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -1
- package/README.md +7 -0
- package/bin/postinstall.js +58 -0
- package/bin/run.js +32 -1
- package/binaries/guanwf-darwin-arm64 +0 -0
- package/binaries/guanwf-darwin-x64 +0 -0
- package/binaries/guanwf-linux-arm64 +0 -0
- package/binaries/guanwf-linux-x64 +0 -0
- package/binaries/guanwf-win32-x64.exe +0 -0
- package/package.json +2 -1
- package/skills/guanwf/references/ETL_AI_DEVELOP.md +13 -8
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -43,8 +43,15 @@ DatasetNode 刷新目标,应使用 SubWorkflowNode 调用产出工作流。参
|
|
|
43
43
|
guanwf install-skill
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
> `npm install -g` / `npm link` 全局安装时会通过 postinstall 自动执行一次 skill 安装/刷新;上述命令用于手动重装或排查。CI 等无需 skill 的环境可设 `GUAN_SKIP_INSTALL_SKILL=1` 跳过。
|
|
47
|
+
|
|
46
48
|
## 版本更新
|
|
47
49
|
|
|
50
|
+
### @guandata/guanwf 0.1.821
|
|
51
|
+
|
|
52
|
+
- 保持 8.2.0 专用兼容能力不变;全局安装或升级后自动刷新 AI Skill。
|
|
53
|
+
- 随包提供完整的工作流使用说明和参考资料。
|
|
54
|
+
|
|
48
55
|
### @guandata/guanwf 0.1.820
|
|
49
56
|
|
|
50
57
|
- 8.2.0 专用兼容版本;请勿与面向 master 的 CLI 版本混用。
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Best-effort post-install hook: refresh the installed AI skill right after
|
|
5
|
+
* a global npm install/upgrade, so users cannot forget to run
|
|
6
|
+
* `guanwf install-skill` and end up with a stale SKILL.md.
|
|
7
|
+
*
|
|
8
|
+
* Constraints:
|
|
9
|
+
* - MUST never fail the npm install: every failure path still exits 0.
|
|
10
|
+
* - Only runs for global installs (`npm install -g` / `npm link`); a local
|
|
11
|
+
* `npm install` inside a project never touches the user's home dirs.
|
|
12
|
+
* - Escape hatch: GUAN_SKIP_INSTALL_SKILL=1 skips entirely (e.g. CI images
|
|
13
|
+
* that install the CLI but never run an AI agent are skipped by default).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
"use strict";
|
|
17
|
+
|
|
18
|
+
const { spawnSync } = require("child_process");
|
|
19
|
+
const path = require("path");
|
|
20
|
+
|
|
21
|
+
const CLI_NAME = "guanwf";
|
|
22
|
+
|
|
23
|
+
function skipReason() {
|
|
24
|
+
if (process.env.GUAN_SKIP_INSTALL_SKILL === "1") return "GUAN_SKIP_INSTALL_SKILL=1";
|
|
25
|
+
if (process.env.npm_config_global !== "true") return "not a global install";
|
|
26
|
+
if (process.env.CI) return "CI environment";
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function main() {
|
|
31
|
+
const reason = skipReason();
|
|
32
|
+
if (reason) {
|
|
33
|
+
console.log(`[${CLI_NAME}] postinstall: skipping automatic install-skill (${reason}).`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
console.log(`[${CLI_NAME}] postinstall: refreshing AI skill (${CLI_NAME} install-skill)...`);
|
|
37
|
+
const result = spawnSync(
|
|
38
|
+
process.execPath,
|
|
39
|
+
[path.join(__dirname, "run.js"), "install-skill"],
|
|
40
|
+
{ stdio: "inherit", env: process.env, timeout: 180000 }
|
|
41
|
+
);
|
|
42
|
+
if (result.error || result.status !== 0) {
|
|
43
|
+
console.warn(
|
|
44
|
+
`[${CLI_NAME}] postinstall: automatic skill install did not complete` +
|
|
45
|
+
(result.error ? ` (${result.error.message})` : ` (exit ${result.status})`) +
|
|
46
|
+
`; run \`${CLI_NAME} install-skill\` manually to refresh SKILL.md.`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
main();
|
|
53
|
+
} catch (err) {
|
|
54
|
+
console.warn(
|
|
55
|
+
`[${CLI_NAME}] postinstall: ${err.message}; run \`${CLI_NAME} install-skill\` manually.`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
process.exit(0);
|
package/bin/run.js
CHANGED
|
@@ -86,10 +86,36 @@ if (process.argv[2] === "version" && process.argv.length === 3) {
|
|
|
86
86
|
process.exit(0);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
|
|
90
|
+
// 定位 npm 自带的 npx-cli.js 并用 node 直接执行(与 guanskill 保持一致),
|
|
91
|
+
// 避免 Windows 上 spawn npx.cmd 的两类问题:shell:true 不做参数 quoting
|
|
92
|
+
// (全局安装路径含空格时会被拆参),以及新版 Node 禁止无 shell 的 .cmd
|
|
93
|
+
// spawn(CVE-2024-27980)。postinstall 场景下 npm_execpath 必然可用。
|
|
94
|
+
function resolveNpxInvocation() {
|
|
95
|
+
const executableDir = path.dirname(process.execPath);
|
|
96
|
+
const candidates = [];
|
|
97
|
+
if (process.env.npm_execpath) {
|
|
98
|
+
candidates.push(path.join(path.dirname(process.env.npm_execpath), "npx-cli.js"));
|
|
99
|
+
}
|
|
100
|
+
candidates.push(path.join(executableDir, "node_modules", "npm", "bin", "npx-cli.js"));
|
|
101
|
+
candidates.push(path.resolve(executableDir, "..", "node_modules", "npm", "bin", "npx-cli.js"));
|
|
102
|
+
candidates.push(path.resolve(executableDir, "..", "lib", "node_modules", "npm", "bin", "npx-cli.js"));
|
|
103
|
+
const npxCliPath = candidates.find((candidate) => fs.existsSync(candidate));
|
|
104
|
+
if (npxCliPath) {
|
|
105
|
+
return { command: process.execPath, argsPrefix: [npxCliPath] };
|
|
106
|
+
}
|
|
107
|
+
if (process.platform === "win32") {
|
|
108
|
+
throw new Error("Cannot locate npm/bin/npx-cli.js for safe Windows execution");
|
|
109
|
+
}
|
|
110
|
+
return { command: "npx", argsPrefix: [] };
|
|
111
|
+
}
|
|
112
|
+
|
|
89
113
|
if (process.argv[2] === "install-skill") {
|
|
90
114
|
const pkgRoot = path.join(__dirname, "..");
|
|
91
115
|
const extraArgs = process.argv.slice(3);
|
|
92
116
|
const args = [
|
|
117
|
+
// --yes: postinstall 等非交互环境下 npx 需要免确认下载 skills CLI
|
|
118
|
+
"--yes",
|
|
93
119
|
"skills",
|
|
94
120
|
"add",
|
|
95
121
|
pkgRoot,
|
|
@@ -100,7 +126,12 @@ if (process.argv[2] === "install-skill") {
|
|
|
100
126
|
...extraArgs,
|
|
101
127
|
];
|
|
102
128
|
console.log("Installing guanwf to AI coding assistants...");
|
|
103
|
-
const
|
|
129
|
+
const npxInvocation = resolveNpxInvocation();
|
|
130
|
+
const result = spawnSync(npxInvocation.command, [...npxInvocation.argsPrefix, ...args], {
|
|
131
|
+
stdio: "inherit",
|
|
132
|
+
env: process.env,
|
|
133
|
+
shell: false,
|
|
134
|
+
});
|
|
104
135
|
if (result.error) throw result.error;
|
|
105
136
|
const status = result.status || 0;
|
|
106
137
|
if (status === 0) installBuddySkills(pkgRoot, "guanwf");
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guandata/guanwf",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.821",
|
|
4
4
|
"description": "观远工作流数据流编辑工具 - 创建、编辑、导出、预览、保存数据流",
|
|
5
5
|
"bin": {
|
|
6
6
|
"guanwf": "bin/run.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
+
"postinstall": "node bin/postinstall.js",
|
|
9
10
|
"build": "node scripts/build.js && node scripts/sync-skill.js",
|
|
10
11
|
"changelog": "node ../../scripts/generate-release-changelog.js .",
|
|
11
12
|
"check-changelog": "node ../../scripts/check-release-changelog.js .",
|
|
@@ -58,7 +58,7 @@ orders := BasicInputDataset("id_1001", "订单", "ds_orders", []Field{
|
|
|
58
58
|
}, Position{X: 100, Y: 100})
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
- `
|
|
61
|
+
- `guanetl export` 会把这里的字段带到最终 JSON。
|
|
62
62
|
- `meta.json` 主要保留 ETL 名称等元信息。
|
|
63
63
|
- 字段相关报错时,优先检查 `[]Field`,不要先怀疑 `meta.json`。
|
|
64
64
|
|
|
@@ -338,25 +338,30 @@ func DefineETL() []Node {
|
|
|
338
338
|
|
|
339
339
|
始终按这个顺序:
|
|
340
340
|
|
|
341
|
-
1. `
|
|
342
|
-
2. 需要看结果时:`
|
|
343
|
-
3. 保存前先看影响:`
|
|
344
|
-
4. 确认无误后:`
|
|
341
|
+
1. `guanetl export --dir <work_dir>`
|
|
342
|
+
2. 需要看结果时:`guanetl preview <node_id> --dir <work_dir>`;保存前要求非空时追加 `--require-nonempty`,零行会返回错误并阻止命令链继续执行
|
|
343
|
+
3. 保存前先看影响:`guanetl save --dir <work_dir> --dry-run`
|
|
344
|
+
4. 确认无误后:`guanetl save --dir <work_dir>`
|
|
345
345
|
|
|
346
346
|
如果 `export` 没过,不要直接 `save`。
|
|
347
347
|
|
|
348
348
|
## save 输出数据集冲突
|
|
349
349
|
|
|
350
|
-
`save` 会先拉取服务端当前 ETL,再把本地 `_exported.json` 合并进去。修改已保存 ETL
|
|
350
|
+
`save` 会先拉取服务端当前 ETL,再把本地 `_exported.json` 合并进去。修改已保存 ETL 时,输出节点必须复用服务端已有输出数据集绑定,否则 direct-save 可能把它当成“创建新输出数据集”,触发“输出数据集目录中存在同名文件”。
|
|
351
|
+
|
|
352
|
+
新建 ETL 首次保存时,服务端 edit API 可能返回 ETL 不存在。若当前本地 base 与工作区 ETL ID 一致且尚无 actions,CLI 会将其识别为正常首次保存回退并输出 `save.first_save_fallback`;其他 edit 故障使用 `save.edit_fallback` warning,不应静默当作首次保存。
|
|
353
|
+
|
|
354
|
+
当本地输出节点 id 被重新生成,但 `outputDsName + parentDirId` 与一个服务端已绑定输出唯一匹配时,`save` 会在请求副本中自动恢复原节点 id 和 dsId,并在影响报告中输出 `save.output_binding_reconciled`。源 `_exported.json` 不会被改写。跨目录、双方重名歧义、删除旧输出或无法唯一匹配时不会自动协调。
|
|
351
355
|
|
|
352
356
|
- 再次修改已保存 ETL,优先重新执行 `guanetl edit <etl_id> --dir <新目录>`,不要长期复用旧工作目录。
|
|
353
357
|
- 只追加输出列时,可以原地 `save`:新增列,不改名、不删已有列、不改已有列类型,并保持原 `OUTPUT_DATASET` 节点 id 和 `outputDsName`。
|
|
354
358
|
- 修改已有输出 schema 时,保持原 `OUTPUT_DATASET` 节点 id;改列名、删列、改已有列类型、换输入数据集或重接输出链路都可能影响下游绑定,保存前必须重新 `preview` 并评估下游。
|
|
355
359
|
- 如果用 `guands dataset rename` 改过 ETL 输出数据集名称,必须同步 `etl.go` 中 `BasicOutputDataset(..., outputDsName, ...)` 或 `BasicOutputDatasetInDir(..., outputDsName, ...)` 的名称。
|
|
356
360
|
- 不要为了绕过同名错误手动删除旧输出数据集;旧输出通常仍被 ETL 依赖。
|
|
357
|
-
-
|
|
361
|
+
- 如果只是在保留旧输出的同时增加新输出,使用新的节点 id 和不同的 `outputDsName` 或 `parentDirId` 即可。
|
|
362
|
+
- 如果确实要移除已绑定输出并创建新输出数据集,必须使用新的节点 id、`outputDsName` 或 `parentDirId`,并显式加 `--allow-output-replacement`。该选项不会迁移下游 dsId 引用,调用方必须自行完成依赖迁移。
|
|
358
363
|
- `save` 如果在 direct-save 前提示输出绑定风险,先修本地 `etl.go` / `meta.json`,再重新 `export -> preview -> save`。
|
|
359
|
-
- `save --dry-run` 只生成保存影响报告,不调用 direct-save;需要机器可读结果时加 `--format json
|
|
364
|
+
- `save --dry-run` 只生成保存影响报告,不调用 direct-save;需要机器可读结果时加 `--format json`。报告里出现阻断级输出绑定风险时,必须先修本地定义,或在确认主动替换且已规划下游迁移后显式使用 `--allow-output-replacement`。
|
|
360
365
|
|
|
361
366
|
## Appendix: Framework Surface
|
|
362
367
|
|