@iqiyi-intl-nadoupro/nadoupro-cli 1.0.0-test.2
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/README.md +25 -0
- package/package.json +42 -0
- package/scripts/bootstrap.js +393 -0
- package/scripts/install.js +99 -0
- package/scripts/run.js +65 -0
- package/scripts/skill-sync.js +18 -0
- package/skills/nadou/SKILL.md +68 -0
- package/skills/nadou/commands/audio.md +125 -0
- package/skills/nadou/commands/auth.md +91 -0
- package/skills/nadou/commands/canvas-attachment.md +71 -0
- package/skills/nadou/commands/canvas-edge.md +65 -0
- package/skills/nadou/commands/canvas-graph.md +191 -0
- package/skills/nadou/commands/canvas-management.md +77 -0
- package/skills/nadou/commands/canvas-media.md +377 -0
- package/skills/nadou/commands/canvas-node.md +228 -0
- package/skills/nadou/commands/canvas.md +123 -0
- package/skills/nadou/commands/evidence.md +30 -0
- package/skills/nadou/commands/image-tools.md +88 -0
- package/skills/nadou/commands/image.md +273 -0
- package/skills/nadou/commands/material.md +59 -0
- package/skills/nadou/commands/media.md +156 -0
- package/skills/nadou/commands/model.md +103 -0
- package/skills/nadou/commands/points.md +43 -0
- package/skills/nadou/commands/schedule.md +112 -0
- package/skills/nadou/commands/space.md +253 -0
- package/skills/nadou/commands/system.md +50 -0
- package/skills/nadou/commands/task-request.md +188 -0
- package/skills/nadou/commands/task.md +229 -0
- package/skills/nadou/commands/text.md +81 -0
- package/skills/nadou/commands/video-advanced.md +96 -0
- package/skills/nadou/commands/video.md +247 -0
- package/skills/nadou/guides/artifact-presentation.md +146 -0
- package/skills/nadou/guides/canvas-dependency-graph.md +101 -0
- package/skills/nadou/guides/canvas-first-complex-workflow.md +83 -0
- package/skills/nadou/guides/execution-interaction.md +54 -0
- package/skills/nadou/guides/failure-cost-presentation.md +42 -0
- package/skills/nadou/guides/intent-templates.md +53 -0
- package/skills/nadou/guides/model-rules.md +7 -0
- package/skills/nadou/guides/utf8-input-safety.md +13 -0
- package/skills/nadou/node-types/audio.md +66 -0
- package/skills/nadou/node-types/clip.md +43 -0
- package/skills/nadou/node-types/director3d.md +41 -0
- package/skills/nadou/node-types/group.md +59 -0
- package/skills/nadou/node-types/image.md +137 -0
- package/skills/nadou/node-types/index.md +77 -0
- package/skills/nadou/node-types/json-contract.md +275 -0
- package/skills/nadou/node-types/model3d.md +35 -0
- package/skills/nadou/node-types/storyboard.md +43 -0
- package/skills/nadou/node-types/text.md +93 -0
- package/skills/nadou/node-types/video.md +119 -0
- package/skills/nadou/policies/core-safety.md +35 -0
- package/skills/nadou/policies/cost-and-write.md +37 -0
- package/skills/nadou/policies/result-and-recovery.md +41 -0
- package/skills/nadou/recipes/atomic-image.md +77 -0
- package/skills/nadou/recipes/canvas-image.md +92 -0
- package/skills/nadou/recipes/one-click-film.md +234 -0
- package/skills/nadou/recipes/quality-review.md +95 -0
- package/skills/nadou/recipes/typical-flows-host-adaptation.md +113 -0
- package/skills/nadou/workflows/atomic.md +61 -0
- package/skills/nadou/workflows/canvas.md +59 -0
- package/skills/nadou/workflows/local.md +41 -0
- package/skills/nadou/workflows/task-recovery.md +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Nadou Pro CLI
|
|
2
|
+
|
|
3
|
+
Nadou Pro's official command-line client.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Node.js 16 or later
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @iqiyi-intl-nadoupro/nadoupro-cli
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Verify
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
nadou version --json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Prerelease testers can install the `test` channel explicitly:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install -g @iqiyi-intl-nadoupro/nadoupro-cli@test
|
|
25
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@iqiyi-intl-nadoupro/nadoupro-cli",
|
|
3
|
+
"version": "1.0.0-test.2",
|
|
4
|
+
"description": "Nadou Pro command-line client.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"nadou": "scripts/run.js",
|
|
7
|
+
"nadoupro-cli": "scripts/run.js",
|
|
8
|
+
"nadou-install": "scripts/bootstrap.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"postinstall": "node scripts/install.js"
|
|
12
|
+
},
|
|
13
|
+
"os": [
|
|
14
|
+
"darwin",
|
|
15
|
+
"linux",
|
|
16
|
+
"win32"
|
|
17
|
+
],
|
|
18
|
+
"cpu": [
|
|
19
|
+
"x64",
|
|
20
|
+
"arm64"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=16"
|
|
24
|
+
},
|
|
25
|
+
"optionalDependencies": {
|
|
26
|
+
"@iqiyi-intl-nadoupro/nadoupro-cli-darwin-x64": "1.0.0-test.2",
|
|
27
|
+
"@iqiyi-intl-nadoupro/nadoupro-cli-darwin-arm64": "1.0.0-test.2",
|
|
28
|
+
"@iqiyi-intl-nadoupro/nadoupro-cli-linux-x64": "1.0.0-test.2",
|
|
29
|
+
"@iqiyi-intl-nadoupro/nadoupro-cli-linux-arm64": "1.0.0-test.2",
|
|
30
|
+
"@iqiyi-intl-nadoupro/nadoupro-cli-win32-x64": "1.0.0-test.2"
|
|
31
|
+
},
|
|
32
|
+
"license": "UNLICENSED",
|
|
33
|
+
"private": false,
|
|
34
|
+
"files": [
|
|
35
|
+
"scripts/bootstrap.js",
|
|
36
|
+
"scripts/install.js",
|
|
37
|
+
"scripts/run.js",
|
|
38
|
+
"scripts/skill-sync.js",
|
|
39
|
+
"skills/nadou/**",
|
|
40
|
+
"README.md"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// 通用安装入口:既服务干净环境,也负责从历史 npm 包迁移。
|
|
5
|
+
//
|
|
6
|
+
// 推荐通过 npm exec 临时运行本脚本。安装器先在隔离目录验证完整平台包,再暂存
|
|
7
|
+
// 受管的全局 nadou shim、安装当前发布版本,并在失败时恢复原 shim。
|
|
8
|
+
|
|
9
|
+
const fs = require("fs");
|
|
10
|
+
const os = require("os");
|
|
11
|
+
const path = require("path");
|
|
12
|
+
const { spawnSync } = require("child_process");
|
|
13
|
+
const { syncSkills } = require("./skill-sync.js");
|
|
14
|
+
|
|
15
|
+
const CURRENT_PACKAGE = require(path.join(__dirname, "..", "package.json")).name;
|
|
16
|
+
const LEGACY_PACKAGE = "@nadouai/cli-auth-poc";
|
|
17
|
+
|
|
18
|
+
function npmCommand(platform = process.platform) {
|
|
19
|
+
return platform === "win32" ? "npm.cmd" : "npm";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function shimCandidates(prefix, platform = process.platform) {
|
|
23
|
+
if (platform === "win32") {
|
|
24
|
+
return ["nadou", "nadou.cmd", "nadou.ps1"].map((name) =>
|
|
25
|
+
path.join(prefix, name)
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return [path.join(prefix, "bin", "nadou")];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function containsPackageMarker(value, packageName) {
|
|
32
|
+
return value
|
|
33
|
+
.replaceAll("\\", "/")
|
|
34
|
+
.toLowerCase()
|
|
35
|
+
.includes(`/${packageName.toLowerCase()}/`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function containsLegacyMarker(value) {
|
|
39
|
+
return containsPackageMarker(value, LEGACY_PACKAGE);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function pathExists(candidate) {
|
|
43
|
+
try {
|
|
44
|
+
fs.lstatSync(candidate);
|
|
45
|
+
return true;
|
|
46
|
+
} catch (err) {
|
|
47
|
+
if (err.code === "ENOENT") return false;
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isPackageShim(candidate, packageName) {
|
|
53
|
+
let stat;
|
|
54
|
+
try {
|
|
55
|
+
stat = fs.lstatSync(candidate);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
if (err.code === "ENOENT") return false;
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (stat.isSymbolicLink()) {
|
|
62
|
+
return containsPackageMarker(fs.readlinkSync(candidate), packageName);
|
|
63
|
+
}
|
|
64
|
+
if (!stat.isFile() || stat.size > 64 * 1024) return false;
|
|
65
|
+
return containsPackageMarker(
|
|
66
|
+
fs.readFileSync(candidate, "utf8"),
|
|
67
|
+
packageName
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isLegacyShim(candidate) {
|
|
72
|
+
return isPackageShim(candidate, LEGACY_PACKAGE);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function isManagedShim(candidate) {
|
|
76
|
+
return (
|
|
77
|
+
isPackageShim(candidate, CURRENT_PACKAGE) ||
|
|
78
|
+
isPackageShim(candidate, LEGACY_PACKAGE)
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function assertManagedShimOwnership(prefix, platform = process.platform) {
|
|
83
|
+
for (const candidate of shimCandidates(prefix, platform)) {
|
|
84
|
+
if (pathExists(candidate) && !isManagedShim(candidate)) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`发现不属于 Nadou npm 包的同名启动文件,拒绝覆盖: ${candidate}`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function removeLegacyShims(prefix, platform = process.platform) {
|
|
93
|
+
const removed = [];
|
|
94
|
+
for (const candidate of shimCandidates(prefix, platform)) {
|
|
95
|
+
if (!isLegacyShim(candidate)) continue;
|
|
96
|
+
fs.rmSync(candidate, { force: true });
|
|
97
|
+
removed.push(candidate);
|
|
98
|
+
}
|
|
99
|
+
return removed;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function backupLegacyShims(prefix, platform = process.platform) {
|
|
103
|
+
return backupMatchingShims(prefix, isLegacyShim, platform);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function backupManagedShims(prefix, platform = process.platform) {
|
|
107
|
+
return backupMatchingShims(prefix, isManagedShim, platform);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function backupMatchingShims(prefix, matches, platform) {
|
|
111
|
+
const backups = [];
|
|
112
|
+
try {
|
|
113
|
+
for (const candidate of shimCandidates(prefix, platform)) {
|
|
114
|
+
if (!matches(candidate)) continue;
|
|
115
|
+
const backup = `${candidate}.nadou-managed-backup-${process.pid}`;
|
|
116
|
+
fs.rmSync(backup, { force: true });
|
|
117
|
+
fs.renameSync(candidate, backup);
|
|
118
|
+
backups.push({ candidate, backup });
|
|
119
|
+
}
|
|
120
|
+
} catch (err) {
|
|
121
|
+
for (const { candidate, backup } of backups.reverse()) {
|
|
122
|
+
if (pathExists(backup) && !pathExists(candidate)) {
|
|
123
|
+
fs.renameSync(backup, candidate);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
throw err;
|
|
127
|
+
}
|
|
128
|
+
return backups;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function discardShimBackups(backups) {
|
|
132
|
+
for (const { backup } of backups) fs.rmSync(backup, { force: true });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function existingShimPaths(prefix, platform = process.platform) {
|
|
136
|
+
return new Set(
|
|
137
|
+
shimCandidates(prefix, platform).filter((candidate) =>
|
|
138
|
+
pathExists(candidate)
|
|
139
|
+
)
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function restoreManagedShims(
|
|
144
|
+
prefix,
|
|
145
|
+
backups,
|
|
146
|
+
preexisting = new Set(),
|
|
147
|
+
platform = process.platform
|
|
148
|
+
) {
|
|
149
|
+
const backedUp = new Set(backups.map(({ candidate }) => candidate));
|
|
150
|
+
for (const candidate of shimCandidates(prefix, platform)) {
|
|
151
|
+
const createdByAttempt =
|
|
152
|
+
!preexisting.has(candidate) || backedUp.has(candidate);
|
|
153
|
+
if (createdByAttempt && isPackageShim(candidate, CURRENT_PACKAGE)) {
|
|
154
|
+
fs.rmSync(candidate, { force: true });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
for (const { candidate, backup } of backups) {
|
|
158
|
+
if (!pathExists(backup)) continue;
|
|
159
|
+
if (pathExists(candidate)) {
|
|
160
|
+
throw new Error(`无法恢复原启动文件,目标已被占用: ${candidate}`);
|
|
161
|
+
}
|
|
162
|
+
fs.renameSync(backup, candidate);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const restoreLegacyShims = restoreManagedShims;
|
|
167
|
+
|
|
168
|
+
function commandInvocation(command, args, platform = process.platform) {
|
|
169
|
+
if (platform === "win32" && command.toLowerCase().endsWith(".cmd")) {
|
|
170
|
+
return {
|
|
171
|
+
command: process.env.ComSpec || "cmd.exe",
|
|
172
|
+
args: ["/d", "/s", "/c", command, ...args],
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return { command, args };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function run(command, args, options = {}) {
|
|
179
|
+
const invocation = commandInvocation(command, args);
|
|
180
|
+
const result = spawnSync(invocation.command, invocation.args, {
|
|
181
|
+
stdio: "inherit",
|
|
182
|
+
...options,
|
|
183
|
+
});
|
|
184
|
+
if (result.error) throw result.error;
|
|
185
|
+
if (result.status !== 0) {
|
|
186
|
+
throw new Error(`${command} ${args.join(" ")} 退出码 ${result.status}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function capture(command, args) {
|
|
191
|
+
const invocation = commandInvocation(command, args);
|
|
192
|
+
const result = spawnSync(invocation.command, invocation.args, {
|
|
193
|
+
encoding: "utf8",
|
|
194
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
195
|
+
});
|
|
196
|
+
if (result.error) throw result.error;
|
|
197
|
+
if (result.status !== 0) {
|
|
198
|
+
throw new Error(`${command} ${args.join(" ")} 退出码 ${result.status}`);
|
|
199
|
+
}
|
|
200
|
+
return result.stdout.trim();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function installedCommand(prefix, platform = process.platform) {
|
|
204
|
+
return platform === "win32"
|
|
205
|
+
? path.join(prefix, "nadou.cmd")
|
|
206
|
+
: path.join(prefix, "bin", "nadou");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function stagingInstallArgs(prefix, spec) {
|
|
210
|
+
return [
|
|
211
|
+
"install",
|
|
212
|
+
"--prefix",
|
|
213
|
+
prefix,
|
|
214
|
+
"--no-save",
|
|
215
|
+
"--no-package-lock",
|
|
216
|
+
"--include=optional",
|
|
217
|
+
spec,
|
|
218
|
+
];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function globalInstallArgs(spec) {
|
|
222
|
+
return [
|
|
223
|
+
"install",
|
|
224
|
+
"-g",
|
|
225
|
+
"--include=optional",
|
|
226
|
+
"--force",
|
|
227
|
+
spec,
|
|
228
|
+
];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function assertVersionEnvelope(stdout, expectedVersion, label) {
|
|
232
|
+
let envelope;
|
|
233
|
+
try {
|
|
234
|
+
envelope = JSON.parse(stdout);
|
|
235
|
+
} catch (err) {
|
|
236
|
+
throw new Error(`${label} 版本输出不是有效 JSON: ${err.message}`);
|
|
237
|
+
}
|
|
238
|
+
const actualVersion = envelope?.data?.version;
|
|
239
|
+
if (envelope?.ok !== true || actualVersion !== expectedVersion) {
|
|
240
|
+
throw new Error(
|
|
241
|
+
`${label} 版本不匹配: 期望 ${expectedVersion}, 实际 ${actualVersion || "未知"}`
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function verifyLauncher(launcher, expectedVersion, label) {
|
|
247
|
+
const result = spawnSync(
|
|
248
|
+
process.execPath,
|
|
249
|
+
[launcher, "version", "--json"],
|
|
250
|
+
{ encoding: "utf8" }
|
|
251
|
+
);
|
|
252
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
253
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
254
|
+
if (result.error) throw result.error;
|
|
255
|
+
if (result.status !== 0) {
|
|
256
|
+
throw new Error(`${label} 版本验证退出码 ${result.status}`);
|
|
257
|
+
}
|
|
258
|
+
assertVersionEnvelope(result.stdout, expectedVersion, label);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function main() {
|
|
262
|
+
const pkg = require("../package.json");
|
|
263
|
+
const version = pkg.version;
|
|
264
|
+
if (!version || version === "0.0.0-dev") {
|
|
265
|
+
throw new Error("安装器只能从已发布的 npm 包运行");
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const npm = npmCommand();
|
|
269
|
+
const prefix = capture(npm, ["prefix", "-g"]);
|
|
270
|
+
const npmRoot = capture(npm, ["root", "-g"]);
|
|
271
|
+
const legacyRoot = path.join(npmRoot, ...LEGACY_PACKAGE.split("/"));
|
|
272
|
+
const hadLegacyPackage = fs.existsSync(legacyRoot);
|
|
273
|
+
const spec = `${CURRENT_PACKAGE}@${version}`;
|
|
274
|
+
const stagingRoot = fs.mkdtempSync(
|
|
275
|
+
path.join(os.tmpdir(), "nadou-install-")
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
// 独立暂存安装可修复“全局主包存在但平台 optional 子包缺失”的状态,
|
|
280
|
+
// 且在触碰任何全局 shim 前验证 registry 包与当前平台二进制。
|
|
281
|
+
console.log(`[nadou-install] 预检 ${spec}`);
|
|
282
|
+
run(npm, stagingInstallArgs(stagingRoot, spec));
|
|
283
|
+
const stagedLauncher = path.join(
|
|
284
|
+
stagingRoot,
|
|
285
|
+
"node_modules",
|
|
286
|
+
...CURRENT_PACKAGE.split("/"),
|
|
287
|
+
"scripts",
|
|
288
|
+
"run.js"
|
|
289
|
+
);
|
|
290
|
+
verifyLauncher(stagedLauncher, version, "预检安装");
|
|
291
|
+
|
|
292
|
+
assertManagedShimOwnership(prefix);
|
|
293
|
+
const preexistingShims = existingShimPaths(prefix);
|
|
294
|
+
const backups = backupManagedShims(prefix);
|
|
295
|
+
for (const { candidate } of backups) {
|
|
296
|
+
console.log(`[nadou-install] 已暂存原启动文件 ${candidate}`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
console.log(`[nadou-install] 安装 ${spec}`);
|
|
301
|
+
run(npm, globalInstallArgs(spec));
|
|
302
|
+
|
|
303
|
+
console.log("[nadou-install] 验证安装结果");
|
|
304
|
+
fs.accessSync(installedCommand(prefix));
|
|
305
|
+
const launcher = path.join(
|
|
306
|
+
npmRoot,
|
|
307
|
+
...CURRENT_PACKAGE.split("/"),
|
|
308
|
+
"scripts",
|
|
309
|
+
"run.js"
|
|
310
|
+
);
|
|
311
|
+
verifyLauncher(launcher, version, "全局安装");
|
|
312
|
+
|
|
313
|
+
const skillSync = syncSkills(launcher);
|
|
314
|
+
if (skillSync.ok) {
|
|
315
|
+
console.log("[nadou-install] 已执行 Agent Skills 同步");
|
|
316
|
+
} else {
|
|
317
|
+
console.warn(`[nadou-install] ${skillSync.message}`);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// 只有新版本通过验证后才清理旧包;清理失败不回退已经可用的新版本。
|
|
321
|
+
try {
|
|
322
|
+
fs.rmSync(legacyRoot, { recursive: true, force: true });
|
|
323
|
+
discardShimBackups(backups);
|
|
324
|
+
if (backups.length > 0 || hadLegacyPackage) {
|
|
325
|
+
console.log(`[nadou-install] 已移除历史包 ${LEGACY_PACKAGE}`);
|
|
326
|
+
}
|
|
327
|
+
} catch (cleanupErr) {
|
|
328
|
+
console.warn(
|
|
329
|
+
`[nadou-install] 新版本已可用,但历史文件清理未完成:${cleanupErr.message}\n` +
|
|
330
|
+
`可稍后重新运行 nadou-install。`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
} catch (err) {
|
|
334
|
+
if (backups.length > 0) {
|
|
335
|
+
try {
|
|
336
|
+
restoreManagedShims(prefix, backups, preexistingShims);
|
|
337
|
+
} catch (restoreErr) {
|
|
338
|
+
throw new Error(
|
|
339
|
+
`${err.message}; 恢复原启动文件失败: ${restoreErr.message}`
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
throw err;
|
|
344
|
+
}
|
|
345
|
+
} finally {
|
|
346
|
+
try {
|
|
347
|
+
fs.rmSync(stagingRoot, { recursive: true, force: true });
|
|
348
|
+
} catch (cleanupErr) {
|
|
349
|
+
console.warn(
|
|
350
|
+
`[nadou-install] 临时目录清理失败,可手工删除 ${stagingRoot}: ${cleanupErr.message}`
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (require.main === module) {
|
|
357
|
+
try {
|
|
358
|
+
main();
|
|
359
|
+
} catch (err) {
|
|
360
|
+
console.error(
|
|
361
|
+
`[nadou-install] 安装失败:${err.message}\n` +
|
|
362
|
+
`可修复后重试同一条安装命令;不会修改 Nadou Profile 或登录凭证。`
|
|
363
|
+
);
|
|
364
|
+
process.exit(1);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
module.exports = {
|
|
369
|
+
assertManagedShimOwnership,
|
|
370
|
+
assertVersionEnvelope,
|
|
371
|
+
backupLegacyShims,
|
|
372
|
+
backupManagedShims,
|
|
373
|
+
capture,
|
|
374
|
+
commandInvocation,
|
|
375
|
+
containsLegacyMarker,
|
|
376
|
+
containsPackageMarker,
|
|
377
|
+
discardShimBackups,
|
|
378
|
+
existingShimPaths,
|
|
379
|
+
globalInstallArgs,
|
|
380
|
+
installedCommand,
|
|
381
|
+
isLegacyShim,
|
|
382
|
+
isManagedShim,
|
|
383
|
+
isPackageShim,
|
|
384
|
+
npmCommand,
|
|
385
|
+
pathExists,
|
|
386
|
+
removeLegacyShims,
|
|
387
|
+
restoreLegacyShims,
|
|
388
|
+
restoreManagedShims,
|
|
389
|
+
shimCandidates,
|
|
390
|
+
stagingInstallArgs,
|
|
391
|
+
syncSkills,
|
|
392
|
+
verifyLauncher,
|
|
393
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// 平台子包解析 + 安装期离线预检。
|
|
2
|
+
//
|
|
3
|
+
// 二进制不由本脚本下载:它随同名平台子包
|
|
4
|
+
// 通过主包的 optionalDependencies 分发。npm 依据子包声明的 os/cpu
|
|
5
|
+
// 只安装匹配当前平台的那一个;完整性由 npm registry 的包 integrity 保证。
|
|
6
|
+
//
|
|
7
|
+
// postinstall 先做离线预检;仅在 npm 全局安装成功后同步随包 Skill。
|
|
8
|
+
// 预检或同步失败只告警不阻断安装(非目标平台、显式 --omit=optional 等场景)。
|
|
9
|
+
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const path = require("path");
|
|
12
|
+
const { syncSkills } = require("./skill-sync.js");
|
|
13
|
+
|
|
14
|
+
function packageName() {
|
|
15
|
+
// 安装器由自身 package.json 决定 scope,确保平台子包与当前发布通道一致。
|
|
16
|
+
return JSON.parse(
|
|
17
|
+
fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8")
|
|
18
|
+
).name;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const SUPPORTED = new Set([
|
|
22
|
+
"darwin-x64",
|
|
23
|
+
"darwin-arm64",
|
|
24
|
+
"linux-x64",
|
|
25
|
+
"linux-arm64",
|
|
26
|
+
"win32-x64",
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
// 返回当前平台对应的子包名,不支持则返回 null。
|
|
30
|
+
function platformPackage() {
|
|
31
|
+
const key = `${process.platform}-${process.arch}`;
|
|
32
|
+
if (!SUPPORTED.has(key)) return null;
|
|
33
|
+
return `${packageName()}-${key}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 解析当前平台二进制的绝对路径。定位不到时抛错,由调用方决定如何处理。
|
|
37
|
+
function resolveBinary() {
|
|
38
|
+
const pkg = platformPackage();
|
|
39
|
+
if (!pkg) {
|
|
40
|
+
throw new Error(`不支持的平台: ${process.platform}-${process.arch}`);
|
|
41
|
+
}
|
|
42
|
+
const binName = process.platform === "win32" ? "nadou.exe" : "nadou";
|
|
43
|
+
// 用 package.json 定位子包根目录,避免子包 exports 限制导致的解析失败。
|
|
44
|
+
const pkgJsonPath = require.resolve(`${pkg}/package.json`);
|
|
45
|
+
return path.join(path.dirname(pkgJsonPath), "bin", binName);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isGlobalInstall(env = process.env) {
|
|
49
|
+
return env.npm_config_global === "true" || env.npm_config_location === "global";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function main({
|
|
53
|
+
env = process.env,
|
|
54
|
+
sync = syncSkills,
|
|
55
|
+
resolve = resolveBinary,
|
|
56
|
+
access = fs.accessSync,
|
|
57
|
+
} = {}) {
|
|
58
|
+
// npx exec 触发的 postinstall 无需预检。
|
|
59
|
+
if (env.npm_command === "exec") return { action: "skipped_exec" };
|
|
60
|
+
|
|
61
|
+
const pkg = platformPackage();
|
|
62
|
+
if (!pkg) {
|
|
63
|
+
console.warn(
|
|
64
|
+
`[nadou] 当前平台 ${process.platform}-${process.arch} 无预编译二进制,跳过预检`
|
|
65
|
+
);
|
|
66
|
+
return { action: "skipped_platform" };
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const bin = resolve();
|
|
70
|
+
access(bin);
|
|
71
|
+
console.log(`nadou 平台二进制就绪: ${pkg}`);
|
|
72
|
+
if (!isGlobalInstall(env)) return { action: "preflight_only" };
|
|
73
|
+
|
|
74
|
+
const skillSync = sync(path.join(__dirname, "run.js"));
|
|
75
|
+
if (skillSync.ok) {
|
|
76
|
+
console.log("[nadou] 已执行 Agent Skills 同步");
|
|
77
|
+
} else {
|
|
78
|
+
console.warn(`[nadou] ${skillSync.message}`);
|
|
79
|
+
}
|
|
80
|
+
return { action: skillSync.ok ? "synchronized" : "sync_failed" };
|
|
81
|
+
} catch (err) {
|
|
82
|
+
console.warn(
|
|
83
|
+
`[nadou] 预检未通过:${err.message}\n` +
|
|
84
|
+
`若运行 nadou 时报错,请重装并保留可选依赖(勿用 --omit=optional / --no-optional):\n` +
|
|
85
|
+
` nadou-install`
|
|
86
|
+
);
|
|
87
|
+
return { action: "preflight_failed" };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (require.main === module) main();
|
|
92
|
+
|
|
93
|
+
module.exports = {
|
|
94
|
+
isGlobalInstall,
|
|
95
|
+
main,
|
|
96
|
+
packageName,
|
|
97
|
+
resolveBinary,
|
|
98
|
+
platformPackage,
|
|
99
|
+
};
|
package/scripts/run.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin 入口:定位平台子包中的 nadou 原生二进制,透传参数与退出码。
|
|
3
|
+
// 二进制随同名平台子包安装(optionalDependencies)。
|
|
4
|
+
|
|
5
|
+
const { spawnSync } = require("child_process");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const { resolveBinary } = require("./install.js");
|
|
9
|
+
|
|
10
|
+
function bundledNpxPath(nodePath = process.execPath, platform = process.platform) {
|
|
11
|
+
const name = platform === "win32" ? "npx.cmd" : "npx";
|
|
12
|
+
const candidate = path.join(path.dirname(nodePath), name);
|
|
13
|
+
return fs.existsSync(candidate) ? candidate : "";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function nodePathEnv(pathValue, nodePath = process.execPath) {
|
|
17
|
+
const directory = path.dirname(nodePath);
|
|
18
|
+
const entries = String(pathValue || "").split(path.delimiter).filter(Boolean);
|
|
19
|
+
return entries.includes(directory)
|
|
20
|
+
? entries.join(path.delimiter)
|
|
21
|
+
: [directory, ...entries].join(path.delimiter);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function launcherEnv(env = process.env, nodePath = process.execPath) {
|
|
25
|
+
return {
|
|
26
|
+
...env,
|
|
27
|
+
NADOU_SKILL_SOURCE: path.join(__dirname, "..", "skills", "nadou"),
|
|
28
|
+
// npm exec may launch Node with a reduced PATH. Preserve an absolute npx
|
|
29
|
+
// companion path so the native Skill workflow can still invoke skills.
|
|
30
|
+
NADOU_NPX_PATH: bundledNpxPath(nodePath) || env.NADOU_NPX_PATH || "",
|
|
31
|
+
NADOU_NODE_PATH: nodePath,
|
|
32
|
+
PATH: nodePathEnv(env.PATH, nodePath),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function main() {
|
|
37
|
+
let bin;
|
|
38
|
+
try {
|
|
39
|
+
bin = resolveBinary();
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.error(
|
|
42
|
+
`${err.message}\n` +
|
|
43
|
+
`未找到平台二进制包。请确认安装未禁用可选依赖,或重装:\n` +
|
|
44
|
+
` nadou-install`
|
|
45
|
+
);
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const result = spawnSync(bin, process.argv.slice(2), {
|
|
51
|
+
stdio: "inherit",
|
|
52
|
+
env: launcherEnv(),
|
|
53
|
+
});
|
|
54
|
+
if (result.error) {
|
|
55
|
+
console.error(`执行 nadou 失败 (${bin}): ${result.error.message}`);
|
|
56
|
+
process.exitCode = 1;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
// 被信号中断时 status 为 null,统一按失败退出。
|
|
60
|
+
process.exitCode = result.status === null ? 1 : result.status;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (require.main === module) main();
|
|
64
|
+
|
|
65
|
+
module.exports = { bundledNpxPath, launcherEnv, nodePathEnv };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require("child_process");
|
|
4
|
+
|
|
5
|
+
// Let the verified CLI own Skill synchronization so installation, update, and
|
|
6
|
+
// a direct `nadou skill install` all use the same native implementation.
|
|
7
|
+
function syncSkills(launcher, spawn = spawnSync) {
|
|
8
|
+
const result = spawn(process.execPath, [launcher, "skill", "install", "--json"], {
|
|
9
|
+
stdio: "inherit",
|
|
10
|
+
});
|
|
11
|
+
if (!result.error && result.status === 0) return { ok: true };
|
|
12
|
+
return {
|
|
13
|
+
ok: false,
|
|
14
|
+
message: "Skill 未自动同步;CLI 已安装。请检查后运行: nadou skill doctor --json",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = { syncSkills };
|