@kevlns/v-cli 0.1.1-beta.1 → 0.2.0-beta.1
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/AGENTS.md +73 -0
- package/README.md +89 -16
- package/dist/cli.mjs +824 -38
- package/package.json +13 -5
- package/schemas/v-cli-plugin.schema.json +144 -0
package/dist/cli.mjs
CHANGED
|
@@ -75,31 +75,665 @@ function defaultHomeDir() {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
// src/core/loader.ts
|
|
78
|
-
import
|
|
79
|
-
import
|
|
78
|
+
import fs4 from "fs";
|
|
79
|
+
import path5 from "path";
|
|
80
80
|
import { pathToFileURL } from "url";
|
|
81
81
|
|
|
82
|
+
// src/core/official.ts
|
|
83
|
+
import { createRequire } from "module";
|
|
84
|
+
import { fileURLToPath } from "url";
|
|
85
|
+
import path3 from "path";
|
|
86
|
+
import fs2 from "fs";
|
|
87
|
+
|
|
88
|
+
// src/core/manifest.ts
|
|
89
|
+
var RESERVED_MANIFEST_COMMAND_NAMES = ["doctor", "plugin", "ts", "agent", "help"];
|
|
90
|
+
var KNOWN_PLATFORMS = ["darwin", "linux", "win32"];
|
|
91
|
+
var OUTPUT_FORMATS = ["json", "stdout", "text"];
|
|
92
|
+
var COMMAND_NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
93
|
+
var PACKAGE_NAME_RE = /^@[a-zA-Z0-9][a-zA-Z0-9._~-]*\/[a-zA-Z0-9][a-zA-Z0-9._~-]*$/;
|
|
94
|
+
function isPlainObject(v) {
|
|
95
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
96
|
+
}
|
|
97
|
+
function isStringArray(v) {
|
|
98
|
+
return Array.isArray(v) && v.every((x) => typeof x === "string");
|
|
99
|
+
}
|
|
100
|
+
function validateManifest(input) {
|
|
101
|
+
if (!isPlainObject(input)) {
|
|
102
|
+
return { ok: false, errors: ["\u6E05\u5355\u5FC5\u987B\u662F JSON \u5BF9\u8C61"] };
|
|
103
|
+
}
|
|
104
|
+
const errors = [];
|
|
105
|
+
const m = input;
|
|
106
|
+
if (typeof m.schemaVersion !== "number" || !Number.isInteger(m.schemaVersion)) {
|
|
107
|
+
errors.push("schemaVersion \u5FC5\u987B\u662F\u6574\u6570");
|
|
108
|
+
} else if (m.schemaVersion === 1) {
|
|
109
|
+
} else if (m.schemaVersion >= 2) {
|
|
110
|
+
errors.push(
|
|
111
|
+
`schemaVersion ${m.schemaVersion} \u4E0D\u53D7\u652F\u6301\uFF1Av-cli 0.2 \u4EC5\u652F\u6301\u5951\u7EA6\u7248\u672C 1 \u7684\u6E05\u5355\uFF0C\u8BF7\u5347\u7EA7\u63D2\u4EF6\u6216 v-cli`
|
|
112
|
+
);
|
|
113
|
+
} else {
|
|
114
|
+
errors.push("schemaVersion \u5FC5\u987B\u4E3A 1");
|
|
115
|
+
}
|
|
116
|
+
const requireString = (key) => {
|
|
117
|
+
const v = m[key];
|
|
118
|
+
if (typeof v !== "string" || v.length === 0) {
|
|
119
|
+
errors.push(`${key} \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
120
|
+
return void 0;
|
|
121
|
+
}
|
|
122
|
+
return v;
|
|
123
|
+
};
|
|
124
|
+
const packageName = requireString("package");
|
|
125
|
+
if (typeof packageName === "string" && !PACKAGE_NAME_RE.test(packageName)) {
|
|
126
|
+
errors.push("package \u5FC5\u987B\u662F @scope/name \u5F62\u5F0F\u7684 npm \u5305\u540D");
|
|
127
|
+
}
|
|
128
|
+
const command = requireString("command");
|
|
129
|
+
if (typeof command === "string") {
|
|
130
|
+
if (!COMMAND_NAME_RE.test(command)) {
|
|
131
|
+
errors.push(`command "${command}" \u4E0D\u5408\u6CD5\uFF1A\u53EA\u80FD\u662F\u5C0F\u5199\u5B57\u6BCD\u5F00\u5934\u7684 [a-z0-9-] \u5B57\u7B26\u4E32`);
|
|
132
|
+
}
|
|
133
|
+
if (RESERVED_MANIFEST_COMMAND_NAMES.includes(command)) {
|
|
134
|
+
errors.push(
|
|
135
|
+
`command "${command}" \u662F v-cli \u4FDD\u7559\u547D\u4EE4\u540D\uFF08${RESERVED_MANIFEST_COMMAND_NAMES.join("/")}\uFF09\uFF0C\u63D2\u4EF6\u4E0D\u80FD\u5360\u7528`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
requireString("bin");
|
|
140
|
+
requireString("description");
|
|
141
|
+
if (!Array.isArray(m.platforms)) {
|
|
142
|
+
errors.push("platforms \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4");
|
|
143
|
+
} else if (m.platforms.length === 0) {
|
|
144
|
+
errors.push("platforms \u81F3\u5C11\u8981\u58F0\u660E\u4E00\u4E2A\u5E73\u53F0");
|
|
145
|
+
} else if (!isStringArray(m.platforms)) {
|
|
146
|
+
errors.push("platforms \u7684\u5143\u7D20\u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
|
|
147
|
+
} else {
|
|
148
|
+
const unknown = m.platforms.filter((p) => !KNOWN_PLATFORMS.includes(p));
|
|
149
|
+
if (unknown.length > 0) {
|
|
150
|
+
errors.push(`platforms \u542B\u672A\u77E5\u5E73\u53F0: ${unknown.join(", ")}\uFF08\u4EC5\u652F\u6301 ${KNOWN_PLATFORMS.join("/")}\uFF09`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (!isPlainObject(m.runtime)) {
|
|
154
|
+
errors.push("runtime \u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
155
|
+
}
|
|
156
|
+
if (!Array.isArray(m.environment)) {
|
|
157
|
+
errors.push("environment \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
158
|
+
} else {
|
|
159
|
+
m.environment.forEach((entry, i) => {
|
|
160
|
+
if (!isPlainObject(entry)) {
|
|
161
|
+
errors.push(`environment[${i}] \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
162
|
+
} else {
|
|
163
|
+
if (typeof entry.name !== "string" || entry.name.length === 0) {
|
|
164
|
+
errors.push(`environment[${i}].name \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
165
|
+
}
|
|
166
|
+
if (typeof entry.description !== "string" || entry.description.length === 0) {
|
|
167
|
+
errors.push(`environment[${i}].description \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
const agent2 = m.agent;
|
|
173
|
+
if (!isPlainObject(agent2)) {
|
|
174
|
+
errors.push("agent \u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
175
|
+
} else {
|
|
176
|
+
if (typeof agent2.whenToUse !== "string") {
|
|
177
|
+
errors.push("agent.whenToUse \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
|
|
178
|
+
}
|
|
179
|
+
if (!Array.isArray(agent2.globalOptions)) {
|
|
180
|
+
errors.push("agent.globalOptions \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
181
|
+
} else {
|
|
182
|
+
agent2.globalOptions.forEach((o, i) => {
|
|
183
|
+
if (!isPlainObject(o)) {
|
|
184
|
+
errors.push(`agent.globalOptions[${i}] \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
185
|
+
} else {
|
|
186
|
+
if (typeof o.flags !== "string" || o.flags.length === 0) {
|
|
187
|
+
errors.push(`agent.globalOptions[${i}].flags \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
188
|
+
}
|
|
189
|
+
if (typeof o.description !== "string" || o.description.length === 0) {
|
|
190
|
+
errors.push(`agent.globalOptions[${i}].description \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
if (!Array.isArray(agent2.commands)) {
|
|
196
|
+
errors.push("agent.commands \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
197
|
+
} else {
|
|
198
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
199
|
+
agent2.commands.forEach((c, i) => {
|
|
200
|
+
const at = `agent.commands[${i}]`;
|
|
201
|
+
if (!isPlainObject(c)) {
|
|
202
|
+
errors.push(`${at} \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (!Array.isArray(c.path) || !isStringArray(c.path) || c.path.length === 0) {
|
|
206
|
+
errors.push(`${at}.path \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u6570\u7EC4`);
|
|
207
|
+
} else {
|
|
208
|
+
const badSegment = c.path.find((seg) => !COMMAND_NAME_RE.test(seg));
|
|
209
|
+
if (badSegment !== void 0) {
|
|
210
|
+
errors.push(`${at}.path \u542B\u975E\u6CD5\u6BB5 "${badSegment}"\uFF08\u6BCF\u6BB5\u987B\u5339\u914D ^[a-z][a-z0-9-]*$\uFF09`);
|
|
211
|
+
}
|
|
212
|
+
const key = c.path.join(" ");
|
|
213
|
+
if (seenPaths.has(key)) {
|
|
214
|
+
errors.push(`${at}.path ${key} \u4E0E\u524D\u9762\u7684\u547D\u4EE4\u91CD\u590D`);
|
|
215
|
+
}
|
|
216
|
+
seenPaths.add(key);
|
|
217
|
+
}
|
|
218
|
+
if (typeof c.usage !== "string" || c.usage.length === 0) {
|
|
219
|
+
errors.push(`${at}.usage \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
220
|
+
}
|
|
221
|
+
if (typeof c.description !== "string" || c.description.length === 0) {
|
|
222
|
+
errors.push(`${at}.description \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
223
|
+
}
|
|
224
|
+
if (!Array.isArray(c.arguments)) {
|
|
225
|
+
errors.push(`${at}.arguments \u5FC5\u987B\u662F\u6570\u7EC4`);
|
|
226
|
+
} else {
|
|
227
|
+
c.arguments.forEach((a, j) => {
|
|
228
|
+
if (!isPlainObject(a)) {
|
|
229
|
+
errors.push(`${at}.arguments[${j}] \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
230
|
+
} else {
|
|
231
|
+
if (typeof a.name !== "string" || a.name.length === 0) {
|
|
232
|
+
errors.push(`${at}.arguments[${j}].name \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
233
|
+
}
|
|
234
|
+
if (typeof a.required !== "boolean") {
|
|
235
|
+
errors.push(`${at}.arguments[${j}].required \u5FC5\u987B\u662F\u5E03\u5C14\u503C`);
|
|
236
|
+
}
|
|
237
|
+
if (typeof a.description !== "string" || a.description.length === 0) {
|
|
238
|
+
errors.push(`${at}.arguments[${j}].description \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
if (!Array.isArray(c.options)) {
|
|
244
|
+
errors.push(`${at}.options \u5FC5\u987B\u662F\u6570\u7EC4`);
|
|
245
|
+
} else {
|
|
246
|
+
c.options.forEach((o, j) => {
|
|
247
|
+
if (!isPlainObject(o)) {
|
|
248
|
+
errors.push(`${at}.options[${j}] \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
249
|
+
} else {
|
|
250
|
+
if (typeof o.flags !== "string" || o.flags.length === 0) {
|
|
251
|
+
errors.push(`${at}.options[${j}].flags \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
252
|
+
}
|
|
253
|
+
if (typeof o.description !== "string" || o.description.length === 0) {
|
|
254
|
+
errors.push(`${at}.options[${j}].description \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
if (!isPlainObject(c.output)) {
|
|
260
|
+
errors.push(`${at}.output \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
261
|
+
} else {
|
|
262
|
+
if (typeof c.output.format !== "string" || c.output.format.length === 0 || !OUTPUT_FORMATS.includes(c.output.format)) {
|
|
263
|
+
errors.push(
|
|
264
|
+
`${at}.output.format \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u4E14\u5C5E\u4E8E [${OUTPUT_FORMATS.join(", ")}]`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
if (typeof c.output.description !== "string" || c.output.description.length === 0) {
|
|
268
|
+
errors.push(`${at}.output.description \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (!isPlainObject(c.exitCodes)) {
|
|
272
|
+
errors.push(`${at}.exitCodes \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
273
|
+
} else {
|
|
274
|
+
const badExit = Object.values(c.exitCodes).find((v) => typeof v !== "string");
|
|
275
|
+
if (badExit !== void 0) {
|
|
276
|
+
errors.push(`${at}.exitCodes \u7684\u503C\u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (!Array.isArray(c.safety) || !isStringArray(c.safety)) {
|
|
280
|
+
errors.push(`${at}.safety \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4`);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (errors.length > 0) {
|
|
286
|
+
return { ok: false, errors };
|
|
287
|
+
}
|
|
288
|
+
return { ok: true, manifest: input };
|
|
289
|
+
}
|
|
290
|
+
function validatePluginIdentity(manifest, pkgJson) {
|
|
291
|
+
const errors = [];
|
|
292
|
+
if (manifest.package !== pkgJson.name) {
|
|
293
|
+
errors.push(
|
|
294
|
+
`\u6E05\u5355 package "${manifest.package}" \u4E0E package.json name "${String(pkgJson.name)}" \u4E0D\u4E00\u81F4`
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
const binKeys = isPlainObject(pkgJson.bin) ? Object.keys(pkgJson.bin) : [];
|
|
298
|
+
if (!binKeys.includes(manifest.bin)) {
|
|
299
|
+
errors.push(`\u6E05\u5355 bin "${manifest.bin}" \u4E0D\u5728 package.json bin (${binKeys.join(", ") || "\u7A7A"}) \u4E2D`);
|
|
300
|
+
}
|
|
301
|
+
return errors;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// src/core/official.ts
|
|
305
|
+
var OFFICIAL_PLUGINS = [
|
|
306
|
+
{
|
|
307
|
+
package: "@kevlns/xlmerge",
|
|
308
|
+
command: "xlmerge",
|
|
309
|
+
description: "Git \u4E2D .xlsx/.xlsm \u51B2\u7A81\u53EF\u89C6\u5316\u89E3\u51B3\u65B9\u6848\uFF08\u4E09\u5411 diff\u3001\u672C\u5730 UI\u3001\u5199\u56DE\u4E0E\u63D0\u4EA4\uFF09"
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
package: "@kevlns/u-cli-mod",
|
|
313
|
+
command: "unity",
|
|
314
|
+
description: "Unity \u5DE5\u7A0B\u7CBE\u786E\u7248\u672C\u8DEF\u7531 + \u5B98\u65B9 CLI \u4E0B\u8F7D + com.unity.pipeline \u9002\u914D\u5305\uFF08Windows-first\uFF09"
|
|
315
|
+
}
|
|
316
|
+
];
|
|
317
|
+
var OFFICIAL_COMMAND_NAMES = new Set(
|
|
318
|
+
OFFICIAL_PLUGINS.map((p) => p.command)
|
|
319
|
+
);
|
|
320
|
+
function defaultResolvePackage(pkg, opts = {}) {
|
|
321
|
+
const baseFile = opts.base ?? fileURLToPath(import.meta.url);
|
|
322
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
323
|
+
const resolveFrom = opts.resolveFrom ?? process.env.V_CLI_PLUGIN_RESOLVE_FROM;
|
|
324
|
+
const candidates = [];
|
|
325
|
+
if (resolveFrom) {
|
|
326
|
+
candidates.push(path3.join(path3.resolve(cwd, resolveFrom), "node_modules", pkg, "package.json"));
|
|
327
|
+
}
|
|
328
|
+
try {
|
|
329
|
+
candidates.push(createRequire(baseFile).resolve(`${pkg}/package.json`));
|
|
330
|
+
} catch {
|
|
331
|
+
}
|
|
332
|
+
try {
|
|
333
|
+
const reqCwd = createRequire(path3.join(cwd, "__v_cli_resolve__.cjs"));
|
|
334
|
+
candidates.push(reqCwd.resolve(`${pkg}/package.json`, { paths: [cwd] }));
|
|
335
|
+
} catch {
|
|
336
|
+
}
|
|
337
|
+
for (const candidate of candidates) {
|
|
338
|
+
try {
|
|
339
|
+
if (fs2.existsSync(candidate)) return candidate;
|
|
340
|
+
} catch {
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return void 0;
|
|
344
|
+
}
|
|
345
|
+
function defaultExists(file) {
|
|
346
|
+
try {
|
|
347
|
+
return fs2.existsSync(file);
|
|
348
|
+
} catch {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function defaultRead(file) {
|
|
353
|
+
return fs2.readFileSync(file, "utf-8");
|
|
354
|
+
}
|
|
355
|
+
function formatErrors(errors) {
|
|
356
|
+
return errors.join("\uFF1B");
|
|
357
|
+
}
|
|
358
|
+
function isPlainObject2(v) {
|
|
359
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
360
|
+
}
|
|
361
|
+
function discoverOfficialPlugin(spec, io = {}) {
|
|
362
|
+
const platform = io.platform ?? process.platform;
|
|
363
|
+
const exists = io.existsFile ?? defaultExists;
|
|
364
|
+
const read = io.readFile ?? defaultRead;
|
|
365
|
+
const resolvePackage = io.resolvePackage ?? defaultResolvePackage;
|
|
366
|
+
const base = {
|
|
367
|
+
source: "official",
|
|
368
|
+
package: spec.package,
|
|
369
|
+
name: spec.command,
|
|
370
|
+
description: spec.description,
|
|
371
|
+
status: "missing",
|
|
372
|
+
platform
|
|
373
|
+
};
|
|
374
|
+
let pkgJsonPath;
|
|
375
|
+
try {
|
|
376
|
+
pkgJsonPath = resolvePackage(spec.package);
|
|
377
|
+
} catch {
|
|
378
|
+
pkgJsonPath = void 0;
|
|
379
|
+
}
|
|
380
|
+
if (!pkgJsonPath || !exists(pkgJsonPath)) {
|
|
381
|
+
return {
|
|
382
|
+
...base,
|
|
383
|
+
error: `\u672A\u627E\u5230\u5B98\u65B9\u63D2\u4EF6\u5305 ${spec.package}\uFF08\u672A\u5B89\u88C5\u6216\u4E0D\u5728\u53EF\u89E3\u6790\u8DEF\u5F84\uFF1B\u8BF7\u5B89\u88C5\u540E\u91CD\u8BD5\uFF09`
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
let pkgJson;
|
|
387
|
+
try {
|
|
388
|
+
pkgJson = JSON.parse(read(pkgJsonPath));
|
|
389
|
+
} catch (err) {
|
|
390
|
+
return {
|
|
391
|
+
...base,
|
|
392
|
+
status: "invalid",
|
|
393
|
+
error: `package.json \u4E0D\u662F\u5408\u6CD5 JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
const vCliMeta = pkgJson.vCli && isPlainObject2(pkgJson.vCli) ? pkgJson.vCli : {};
|
|
397
|
+
const manifestRel = typeof vCliMeta.manifest === "string" ? vCliMeta.manifest : "v-cli.plugin.json";
|
|
398
|
+
const pkgRoot = path3.dirname(pkgJsonPath);
|
|
399
|
+
const manifestPath = path3.join(pkgRoot, manifestRel);
|
|
400
|
+
if (!exists(manifestPath)) {
|
|
401
|
+
return {
|
|
402
|
+
...base,
|
|
403
|
+
error: `\u5305\u5185\u7F3A\u5C11\u63D2\u4EF6\u6E05\u5355 ${manifestRel}\uFF08package.json \u7684 vCli.manifest \u6307\u5411\u5B83\uFF09`
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
let manifestRaw;
|
|
407
|
+
try {
|
|
408
|
+
manifestRaw = read(manifestPath);
|
|
409
|
+
} catch (err) {
|
|
410
|
+
return {
|
|
411
|
+
...base,
|
|
412
|
+
error: `\u8BFB\u53D6\u6E05\u5355\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
let parsed;
|
|
416
|
+
try {
|
|
417
|
+
parsed = JSON.parse(manifestRaw);
|
|
418
|
+
} catch (err) {
|
|
419
|
+
return {
|
|
420
|
+
...base,
|
|
421
|
+
status: "invalid",
|
|
422
|
+
error: `\u6E05\u5355 ${manifestRel} \u4E0D\u662F\u5408\u6CD5 JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
const validated = validateManifest(parsed);
|
|
426
|
+
if (!validated.ok) {
|
|
427
|
+
return { ...base, status: "invalid", error: `\u6E05\u5355\u6821\u9A8C\u5931\u8D25: ${formatErrors(validated.errors)}` };
|
|
428
|
+
}
|
|
429
|
+
const manifest = validated.manifest;
|
|
430
|
+
const identityErrors = validatePluginIdentity(manifest, pkgJson);
|
|
431
|
+
if (identityErrors.length > 0) {
|
|
432
|
+
return {
|
|
433
|
+
...base,
|
|
434
|
+
status: "invalid",
|
|
435
|
+
error: `\u8EAB\u4EFD\u6821\u9A8C\u5931\u8D25: ${formatErrors(identityErrors)}`
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
if (manifest.command !== spec.command) {
|
|
439
|
+
return {
|
|
440
|
+
...base,
|
|
441
|
+
status: "invalid",
|
|
442
|
+
error: `\u6E05\u5355 command "${manifest.command}" \u4E0E\u5B98\u65B9\u767D\u540D\u5355\u547D\u4EE4 "${spec.command}" \u4E0D\u4E00\u81F4`
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
const binMap = pkgJson.bin;
|
|
446
|
+
const binRel = isPlainObject2(binMap) ? binMap[manifest.bin] : void 0;
|
|
447
|
+
if (typeof binRel !== "string" || binRel.length === 0) {
|
|
448
|
+
return {
|
|
449
|
+
...base,
|
|
450
|
+
status: "invalid",
|
|
451
|
+
error: `bin "${manifest.bin}" \u7684\u542F\u52A8\u76EE\u6807\u4E0D\u662F\u5B57\u7B26\u4E32\u8DEF\u5F84`
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
const binPath = path3.resolve(pkgRoot, binRel);
|
|
455
|
+
const rootPrefix = pkgRoot.endsWith(path3.sep) ? pkgRoot : pkgRoot + path3.sep;
|
|
456
|
+
if (!binPath.startsWith(rootPrefix)) {
|
|
457
|
+
return {
|
|
458
|
+
...base,
|
|
459
|
+
status: "invalid",
|
|
460
|
+
error: `bin "${manifest.bin}" \u7684\u76EE\u6807 ${binRel} \u9003\u9038\u4E86\u5305\u76EE\u5F55`
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
const requiredPlatforms = manifest.platforms;
|
|
464
|
+
if (!requiredPlatforms.includes(platform)) {
|
|
465
|
+
return {
|
|
466
|
+
...base,
|
|
467
|
+
status: "platform-mismatch",
|
|
468
|
+
version: typeof pkgJson.version === "string" ? pkgJson.version : void 0,
|
|
469
|
+
requiredPlatforms,
|
|
470
|
+
error: `\u8BE5\u63D2\u4EF6\u4EC5\u652F\u6301\u5E73\u53F0 [${requiredPlatforms.join(", ")}]\uFF0C\u5F53\u524D\u5E73\u53F0\u4E3A ${platform}`
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
if (!exists(binPath)) {
|
|
474
|
+
return {
|
|
475
|
+
...base,
|
|
476
|
+
version: typeof pkgJson.version === "string" ? pkgJson.version : void 0,
|
|
477
|
+
requiredPlatforms,
|
|
478
|
+
error: `bin \u6587\u4EF6\u4E0D\u5B58\u5728: ${binRel}\uFF08manifest.bin=${manifest.bin}\uFF09`
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
return {
|
|
482
|
+
...base,
|
|
483
|
+
status: "available",
|
|
484
|
+
version: typeof pkgJson.version === "string" ? pkgJson.version : void 0,
|
|
485
|
+
requiredPlatforms,
|
|
486
|
+
bin: binPath,
|
|
487
|
+
whenToUse: manifest.agent.whenToUse,
|
|
488
|
+
globalOptions: manifest.agent.globalOptions,
|
|
489
|
+
commands: manifest.agent.commands.map((c) => ({
|
|
490
|
+
path: c.path,
|
|
491
|
+
usage: c.usage,
|
|
492
|
+
description: c.description,
|
|
493
|
+
arguments: c.arguments,
|
|
494
|
+
options: c.options,
|
|
495
|
+
output: c.output,
|
|
496
|
+
exitCodes: c.exitCodes,
|
|
497
|
+
safety: c.safety
|
|
498
|
+
})),
|
|
499
|
+
runtime: manifest.runtime,
|
|
500
|
+
environment: manifest.environment
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function discoverAllOfficialPlugins(io = {}) {
|
|
504
|
+
return OFFICIAL_PLUGINS.map((spec) => discoverOfficialPlugin(spec, io));
|
|
505
|
+
}
|
|
506
|
+
function isOfficialCommand(token) {
|
|
507
|
+
return OFFICIAL_COMMAND_NAMES.has(token);
|
|
508
|
+
}
|
|
509
|
+
|
|
82
510
|
// src/core/command.ts
|
|
83
|
-
|
|
84
|
-
|
|
511
|
+
var RESERVED_COMMAND_NAMES = ["doctor", "plugin", "ts", "agent", "help"];
|
|
512
|
+
var NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
513
|
+
function isStringArray2(v) {
|
|
514
|
+
return Array.isArray(v) && v.every((x) => typeof x === "string");
|
|
515
|
+
}
|
|
516
|
+
function isPlainObject3(v) {
|
|
517
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
518
|
+
}
|
|
519
|
+
function validateAgentMeta(agent2, errors) {
|
|
520
|
+
if (agent2 === void 0 || agent2 === null) return;
|
|
521
|
+
if (!isPlainObject3(agent2)) {
|
|
522
|
+
errors.push("agent \u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (agent2.whenToUse !== void 0 && typeof agent2.whenToUse !== "string") {
|
|
526
|
+
errors.push("agent.whenToUse \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
|
|
527
|
+
}
|
|
528
|
+
if (agent2.globalOptions !== void 0) {
|
|
529
|
+
if (!Array.isArray(agent2.globalOptions)) {
|
|
530
|
+
errors.push("agent.globalOptions \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
531
|
+
} else {
|
|
532
|
+
agent2.globalOptions.forEach((o, i) => {
|
|
533
|
+
if (!isPlainObject3(o) || typeof o.flags !== "string" || typeof o.description !== "string") {
|
|
534
|
+
errors.push(`agent.globalOptions[${i}] \u5FC5\u987B\u662F {flags: string, description: string}`);
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (agent2.commands !== void 0) {
|
|
540
|
+
if (!Array.isArray(agent2.commands)) {
|
|
541
|
+
errors.push("agent.commands \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
542
|
+
} else {
|
|
543
|
+
agent2.commands.forEach((c, i) => {
|
|
544
|
+
const at = `agent.commands[${i}]`;
|
|
545
|
+
if (!isPlainObject3(c) || typeof c.description !== "string") {
|
|
546
|
+
errors.push(`${at} \u5FC5\u987B\u662F\u542B\u5B57\u7B26\u4E32 description \u7684\u5BF9\u8C61`);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
if (!Array.isArray(c.path) || !isStringArray2(c.path) || c.path.length === 0) {
|
|
550
|
+
errors.push(`${at}.path \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u6570\u7EC4\uFF08\u6BCF\u6BB5\u5339\u914D ^[a-z][a-z0-9-]*$\uFF09`);
|
|
551
|
+
} else if (c.path.some((seg) => !NAME_RE.test(seg))) {
|
|
552
|
+
errors.push(`${at}.path \u542B\u975E\u6CD5\u6BB5\uFF08\u6BCF\u6BB5\u987B\u5339\u914D ^[a-z][a-z0-9-]*$\uFF09`);
|
|
553
|
+
}
|
|
554
|
+
if (c.usage !== void 0 && typeof c.usage !== "string") {
|
|
555
|
+
errors.push(`${at}.usage \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function validateLocalCommand(candidate, options = {}) {
|
|
562
|
+
const errors = [];
|
|
563
|
+
if (!isPlainObject3(candidate)) {
|
|
564
|
+
return { ok: false, errors: ["\u63D2\u4EF6\u5BFC\u51FA\u5FC5\u987B\u662F\u5BF9\u8C61"] };
|
|
565
|
+
}
|
|
85
566
|
const cmd = candidate;
|
|
86
|
-
|
|
567
|
+
if (cmd.apiVersion !== 1) {
|
|
568
|
+
const got = cmd.apiVersion === void 0 ? "\u7F3A\u5931" : JSON.stringify(cmd.apiVersion);
|
|
569
|
+
errors.push(
|
|
570
|
+
`apiVersion \u5FC5\u987B\u4E3A 1\uFF08\u5F53\u524D: ${got}\uFF09\u3002v-cli 0.2 \u8D77\u91C7\u7528 apiVersion 1 \u63D2\u4EF6\u5951\u7EA6\uFF0C\u65E7\u7248\u63D2\u4EF6\u8BF7\u8865\u4E0A apiVersion: 1 \u540E\u91CD\u65B0\u52A0\u8F7D`
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
if (typeof cmd.name !== "string" || cmd.name.length === 0) {
|
|
574
|
+
errors.push("name \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
575
|
+
} else {
|
|
576
|
+
if (!NAME_RE.test(cmd.name)) {
|
|
577
|
+
errors.push(`\u547D\u4EE4\u540D "${cmd.name}" \u4E0D\u5408\u6CD5\uFF1A\u53EA\u80FD\u662F\u5C0F\u5199\u5B57\u6BCD\u5F00\u5934\u7684 [a-z0-9-] \u5B57\u7B26\u4E32`);
|
|
578
|
+
}
|
|
579
|
+
const reserved = [...RESERVED_COMMAND_NAMES, ...OFFICIAL_COMMAND_NAMES];
|
|
580
|
+
if (reserved.includes(cmd.name)) {
|
|
581
|
+
const kind = RESERVED_COMMAND_NAMES.includes(cmd.name) ? "v-cli \u5185\u7F6E\u4FDD\u7559\u547D\u4EE4" : "\u5B98\u65B9\u63D2\u4EF6\u547D\u4EE4";
|
|
582
|
+
errors.push(`\u547D\u4EE4\u540D "${cmd.name}" \u662F${kind}\uFF0C\u672C\u5730\u63D2\u4EF6\u4E0D\u80FD\u5360\u7528`);
|
|
583
|
+
}
|
|
584
|
+
if ((options.existingNames ?? []).includes(cmd.name)) {
|
|
585
|
+
errors.push(`\u91CD\u590D\u7684\u547D\u4EE4\u540D "${cmd.name}"\uFF08\u5DF2\u7531\u5176\u4ED6\u672C\u5730\u63D2\u4EF6\u5B9A\u4E49\uFF09`);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (typeof cmd.description !== "string" || cmd.description.length === 0) {
|
|
589
|
+
errors.push("description \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
590
|
+
}
|
|
591
|
+
if (typeof cmd.register !== "function") {
|
|
592
|
+
errors.push("register \u5FC5\u987B\u662F\u51FD\u6570");
|
|
593
|
+
}
|
|
594
|
+
validateAgentMeta(cmd.agent, errors);
|
|
595
|
+
if (errors.length > 0) return { ok: false, errors };
|
|
596
|
+
return { ok: true, command: candidate, errors: [] };
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// src/commands/agent.ts
|
|
600
|
+
function buildAgentIndex(builtin, local, official) {
|
|
601
|
+
const rows = [];
|
|
602
|
+
for (const cmd of builtin) {
|
|
603
|
+
rows.push({
|
|
604
|
+
name: cmd.name,
|
|
605
|
+
type: "builtin",
|
|
606
|
+
description: cmd.description,
|
|
607
|
+
metadataStatus: cmd.agent ? "full" : "minimal",
|
|
608
|
+
whenToUse: cmd.agent?.whenToUse,
|
|
609
|
+
globalOptions: cmd.agent?.globalOptions,
|
|
610
|
+
commands: cmd.agent?.commands
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
for (const item of local) {
|
|
614
|
+
const cmd = item.command;
|
|
615
|
+
if (!cmd) {
|
|
616
|
+
rows.push({
|
|
617
|
+
name: item.file ? item.file.split(/[\\/]/).pop()?.replace(/\.mjs$/, "") ?? "(\u52A0\u8F7D\u5931\u8D25)" : "(\u52A0\u8F7D\u5931\u8D25)",
|
|
618
|
+
type: "local",
|
|
619
|
+
description: item.error ?? "\u52A0\u8F7D\u5931\u8D25",
|
|
620
|
+
metadataStatus: "minimal",
|
|
621
|
+
error: item.error
|
|
622
|
+
});
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
rows.push({
|
|
626
|
+
name: cmd.name,
|
|
627
|
+
type: "local",
|
|
628
|
+
description: cmd.description,
|
|
629
|
+
metadataStatus: cmd.agent ? "full" : "minimal",
|
|
630
|
+
whenToUse: cmd.agent?.whenToUse,
|
|
631
|
+
globalOptions: cmd.agent?.globalOptions,
|
|
632
|
+
commands: cmd.agent?.commands
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
for (const info of official) {
|
|
636
|
+
rows.push({
|
|
637
|
+
name: info.name,
|
|
638
|
+
type: "official",
|
|
639
|
+
description: info.description,
|
|
640
|
+
metadataStatus: info.status === "available" ? "full" : "minimal",
|
|
641
|
+
package: info.package,
|
|
642
|
+
version: info.version,
|
|
643
|
+
status: info.status,
|
|
644
|
+
platform: info.platform,
|
|
645
|
+
requiredPlatforms: info.requiredPlatforms,
|
|
646
|
+
error: info.error,
|
|
647
|
+
whenToUse: info.whenToUse,
|
|
648
|
+
globalOptions: info.globalOptions,
|
|
649
|
+
// 完整清单元数据全量透传(arguments/options/output/exitCodes/safety)
|
|
650
|
+
commands: info.commands,
|
|
651
|
+
runtime: info.runtime,
|
|
652
|
+
environment: info.environment
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
return rows;
|
|
656
|
+
}
|
|
657
|
+
function rowToText(row) {
|
|
658
|
+
const status = row.status ? ` [${row.status}]` : "";
|
|
659
|
+
const meta = row.metadataStatus === "full" ? "\u5B8C\u6574\u5143\u6570\u636E" : "\u6700\u5C0F\u5143\u6570\u636E";
|
|
660
|
+
return `[${row.type}] ${row.name}${status}: ${row.description} (${meta})`;
|
|
87
661
|
}
|
|
662
|
+
var agent = {
|
|
663
|
+
name: "agent",
|
|
664
|
+
description: "agent \u7D22\u5F15\uFF1Aindex \u5217\u51FA\u5168\u90E8\u547D\u4EE4\u53CA\u5143\u6570\u636E\uFF0Cdescribe \u67E5\u770B\u5355\u4E2A\u547D\u4EE4\u8BE6\u60C5",
|
|
665
|
+
apiVersion: 1,
|
|
666
|
+
register(program, ctx) {
|
|
667
|
+
program.command("index").description("\u5217\u51FA\u5168\u90E8\u547D\u4EE4\uFF08builtin/local/official\uFF09\u4E0E agent \u5143\u6570\u636E").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action(async (opts) => {
|
|
668
|
+
const json = ctx.json || opts.json;
|
|
669
|
+
const rows = buildAgentIndex(
|
|
670
|
+
builtinCommands,
|
|
671
|
+
await loadLocalPlugins(ctx),
|
|
672
|
+
discoverAllOfficialPlugins()
|
|
673
|
+
);
|
|
674
|
+
ctx.log.result(json ? rows : rows.map(rowToText).join("\n"));
|
|
675
|
+
});
|
|
676
|
+
program.command("describe").description("\u67E5\u770B\u5355\u4E2A\u547D\u4EE4\u7684\u5B8C\u6574\u8BB0\u5F55\uFF08builtin/local/official\uFF09").argument("<name>", "\u547D\u4EE4\u540D").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action(async (name, opts) => {
|
|
677
|
+
const json = ctx.json || opts.json;
|
|
678
|
+
const row = buildAgentIndex(
|
|
679
|
+
builtinCommands,
|
|
680
|
+
await loadLocalPlugins(ctx),
|
|
681
|
+
discoverAllOfficialPlugins()
|
|
682
|
+
).find((r) => r.name === name);
|
|
683
|
+
if (!row) {
|
|
684
|
+
ctx.log.error(`\u672A\u627E\u5230\u547D\u4EE4: ${name}`);
|
|
685
|
+
process.exitCode = 1;
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
if (json) {
|
|
689
|
+
ctx.log.result(row);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
const lines = [
|
|
693
|
+
`\u547D\u4EE4: ${row.name} (${row.type})`,
|
|
694
|
+
`\u63CF\u8FF0: ${row.description}`,
|
|
695
|
+
`\u5143\u6570\u636E: ${row.metadataStatus}`
|
|
696
|
+
];
|
|
697
|
+
if (row.status) lines.push(`\u72B6\u6001: ${row.status}${row.error ? `\uFF08${row.error}\uFF09` : ""}`);
|
|
698
|
+
if (row.package) lines.push(`\u5305: ${row.package}${row.version ? `@${row.version}` : ""}`);
|
|
699
|
+
if (row.requiredPlatforms) lines.push(`\u652F\u6301\u5E73\u53F0: ${row.requiredPlatforms.join(", ")}`);
|
|
700
|
+
if (row.whenToUse) lines.push(`\u4F55\u65F6\u4F7F\u7528: ${row.whenToUse}`);
|
|
701
|
+
if (row.globalOptions && row.globalOptions.length > 0) {
|
|
702
|
+
lines.push("\u5168\u5C40\u9009\u9879:");
|
|
703
|
+
for (const o of row.globalOptions) lines.push(` ${o.flags} \u2014 ${o.description}`);
|
|
704
|
+
}
|
|
705
|
+
if (row.commands && row.commands.length > 0) {
|
|
706
|
+
lines.push("\u5B50\u547D\u4EE4:");
|
|
707
|
+
for (const c of row.commands) {
|
|
708
|
+
const pathText = Array.isArray(c.path) ? c.path.join(" ") : "";
|
|
709
|
+
const optText = Array.isArray(c.options) && c.options.length > 0 ? ` \u9009\u9879: ${c.options.map((o) => o.flags).join(", ")}` : "";
|
|
710
|
+
const safeText = Array.isArray(c.safety) && c.safety.length > 0 ? ` \u5B89\u5168: ${c.safety.join("\uFF1B")}` : "";
|
|
711
|
+
lines.push(
|
|
712
|
+
` ${pathText} \u2014 ${c.description}${c.usage ? ` (\u7528\u6CD5: ${c.usage})` : ""}${optText}${safeText}`
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
ctx.log.result(lines.join("\n"));
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
};
|
|
88
720
|
|
|
89
721
|
// src/commands/doctor.ts
|
|
90
|
-
import
|
|
722
|
+
import fs3 from "fs";
|
|
91
723
|
|
|
92
724
|
// src/version.ts
|
|
93
|
-
var VERSION = "0.
|
|
725
|
+
var VERSION = "0.2.0-beta.1";
|
|
94
726
|
|
|
95
727
|
// src/commands/doctor.ts
|
|
96
728
|
var doctor = {
|
|
97
729
|
name: "doctor",
|
|
98
730
|
description: "\u4F53\u68C0\uFF1Anode/v-cli \u7248\u672C\u3001homeDir\u3001config\u3001\u63D2\u4EF6\u72B6\u6001",
|
|
731
|
+
apiVersion: 1,
|
|
99
732
|
register(program, ctx) {
|
|
100
|
-
program.action(async () => {
|
|
733
|
+
program.option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action(async (opts) => {
|
|
734
|
+
const json = ctx.json || opts.json;
|
|
101
735
|
const homeDir = ctx.homeDir;
|
|
102
|
-
const exists =
|
|
736
|
+
const exists = fs3.existsSync(homeDir);
|
|
103
737
|
let configWritable = false;
|
|
104
738
|
try {
|
|
105
739
|
ctx.config.set((cfg) => cfg);
|
|
@@ -113,6 +747,14 @@ var doctor = {
|
|
|
113
747
|
file: p.file,
|
|
114
748
|
error: p.error ?? null
|
|
115
749
|
}));
|
|
750
|
+
const officialPlugins = discoverAllOfficialPlugins().map((o) => ({
|
|
751
|
+
package: o.package,
|
|
752
|
+
name: o.name,
|
|
753
|
+
version: o.version,
|
|
754
|
+
status: o.status,
|
|
755
|
+
platform: o.platform,
|
|
756
|
+
error: o.error
|
|
757
|
+
}));
|
|
116
758
|
const report = {
|
|
117
759
|
version: VERSION,
|
|
118
760
|
node: process.version,
|
|
@@ -120,9 +762,10 @@ var doctor = {
|
|
|
120
762
|
homeDirExists: exists,
|
|
121
763
|
configWritable,
|
|
122
764
|
localPluginCount: plugins.length,
|
|
123
|
-
localPlugins: list
|
|
765
|
+
localPlugins: list,
|
|
766
|
+
officialPlugins
|
|
124
767
|
};
|
|
125
|
-
if (
|
|
768
|
+
if (json) {
|
|
126
769
|
ctx.log.result(report);
|
|
127
770
|
} else {
|
|
128
771
|
ctx.log.result(`vcli ${VERSION} (node ${process.version})`);
|
|
@@ -132,18 +775,26 @@ var doctor = {
|
|
|
132
775
|
for (const p of plugins) {
|
|
133
776
|
ctx.log.result(` - ${p.file}${p.error ? ` [\u5931\u8D25: ${p.error}]` : ""}`);
|
|
134
777
|
}
|
|
778
|
+
ctx.log.result(`\u5B98\u65B9\u63D2\u4EF6: ${officialPlugins.length} \u4E2A`);
|
|
779
|
+
for (const o of officialPlugins) {
|
|
780
|
+
ctx.log.result(
|
|
781
|
+
` - ${o.name} (${o.package}${o.version ? `@${o.version}` : ""}) [${o.status}]${o.error ? ` ${o.error}` : ""}`
|
|
782
|
+
);
|
|
783
|
+
}
|
|
135
784
|
}
|
|
136
785
|
});
|
|
137
786
|
}
|
|
138
787
|
};
|
|
139
788
|
|
|
140
789
|
// src/commands/plugin.ts
|
|
141
|
-
import
|
|
790
|
+
import path4 from "path";
|
|
142
791
|
var plugin = {
|
|
143
792
|
name: "plugin",
|
|
144
793
|
description: "\u63D2\u4EF6\u7BA1\u7406\uFF1Alist \u5217\u51FA\u547D\u4EE4\uFF0Cpath \u663E\u793A\u672C\u5730\u63D2\u4EF6\u76EE\u5F55",
|
|
794
|
+
apiVersion: 1,
|
|
145
795
|
register(program, ctx) {
|
|
146
|
-
program.command("list").description("\u5217\u51FA\u5185\u7F6E\u547D\u4EE4\
|
|
796
|
+
program.command("list").description("\u5217\u51FA\u5185\u7F6E\u547D\u4EE4\u3001\u672C\u5730\u63D2\u4EF6\u4E0E\u5B98\u65B9\u63D2\u4EF6\u72B6\u6001").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action(async (opts) => {
|
|
797
|
+
const json = ctx.json || opts.json;
|
|
147
798
|
const all = await loadAllCommands(ctx);
|
|
148
799
|
const rows = all.map((item) => ({
|
|
149
800
|
source: item.source,
|
|
@@ -151,17 +802,32 @@ var plugin = {
|
|
|
151
802
|
description: item.command?.description ?? item.error ?? "",
|
|
152
803
|
file: item.file ?? ""
|
|
153
804
|
}));
|
|
154
|
-
|
|
155
|
-
|
|
805
|
+
const official = discoverAllOfficialPlugins().map((o) => ({
|
|
806
|
+
source: "official",
|
|
807
|
+
name: o.name,
|
|
808
|
+
description: o.description,
|
|
809
|
+
package: o.package,
|
|
810
|
+
version: o.version,
|
|
811
|
+
status: o.status,
|
|
812
|
+
platform: o.platform,
|
|
813
|
+
error: o.error
|
|
814
|
+
}));
|
|
815
|
+
if (json) {
|
|
816
|
+
ctx.log.result([...rows, ...official]);
|
|
156
817
|
} else {
|
|
157
818
|
for (const row of rows) {
|
|
158
819
|
ctx.log.result(`[${row.source}] ${row.name}: ${row.description}`);
|
|
159
820
|
}
|
|
821
|
+
for (const row of official) {
|
|
822
|
+
ctx.log.result(
|
|
823
|
+
`[${row.source}] ${row.name}: ${row.description} [${row.status}]${row.error ? ` ${row.error}` : ""}`
|
|
824
|
+
);
|
|
825
|
+
}
|
|
160
826
|
}
|
|
161
827
|
});
|
|
162
|
-
program.command("path").description("\u6253\u5370\u672C\u5730\u63D2\u4EF6\u76EE\u5F55\uFF08\u653E\u5165 .mjs \u6587\u4EF6\u5373\u751F\u6548\uFF09").action(() => {
|
|
163
|
-
const dir =
|
|
164
|
-
if (ctx.json) {
|
|
828
|
+
program.command("path").description("\u6253\u5370\u672C\u5730\u63D2\u4EF6\u76EE\u5F55\uFF08\u653E\u5165 .mjs \u6587\u4EF6\u5373\u751F\u6548\uFF09").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action((opts) => {
|
|
829
|
+
const dir = path4.join(ctx.homeDir, "commands");
|
|
830
|
+
if (ctx.json || opts.json) {
|
|
165
831
|
ctx.log.result({ dir });
|
|
166
832
|
} else {
|
|
167
833
|
ctx.log.result(dir);
|
|
@@ -192,8 +858,10 @@ function parseInput(input) {
|
|
|
192
858
|
var ts = {
|
|
193
859
|
name: "ts",
|
|
194
860
|
description: "\u65F6\u95F4\u6233\u5DE5\u5177\uFF1A\u65F6\u95F4\u6233 <-> \u53EF\u8BFB\u65F6\u95F4\u4E92\u8F6C",
|
|
861
|
+
apiVersion: 1,
|
|
195
862
|
register(program, ctx) {
|
|
196
|
-
program.argument("[value]", "\u6570\u5B57\u65F6\u95F4\u6233\uFF08\u79D2\u6216\u6BEB\u79D2\uFF0C\u81EA\u52A8\u8BC6\u522B\uFF09\u6216\u65E5\u671F\u5B57\u7B26\u4E32").action((value) => {
|
|
863
|
+
program.argument("[value]", "\u6570\u5B57\u65F6\u95F4\u6233\uFF08\u79D2\u6216\u6BEB\u79D2\uFF0C\u81EA\u52A8\u8BC6\u522B\uFF09\u6216\u65E5\u671F\u5B57\u7B26\u4E32").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action((value, opts) => {
|
|
864
|
+
const json = ctx.json || opts.json;
|
|
197
865
|
if (!value || value === "now") {
|
|
198
866
|
const now = Date.now();
|
|
199
867
|
const report2 = {
|
|
@@ -202,7 +870,7 @@ var ts = {
|
|
|
202
870
|
iso: new Date(now).toISOString(),
|
|
203
871
|
local: new Date(now).toString()
|
|
204
872
|
};
|
|
205
|
-
ctx.log.result(
|
|
873
|
+
ctx.log.result(json ? report2 : report2.iso);
|
|
206
874
|
return;
|
|
207
875
|
}
|
|
208
876
|
const result = parseInput(value);
|
|
@@ -213,36 +881,38 @@ var ts = {
|
|
|
213
881
|
iso: new Date(result.milliseconds).toISOString(),
|
|
214
882
|
local: formatTime(result.milliseconds)
|
|
215
883
|
};
|
|
216
|
-
ctx.log.result(
|
|
884
|
+
ctx.log.result(json ? report : report.local);
|
|
217
885
|
});
|
|
218
886
|
}
|
|
219
887
|
};
|
|
220
888
|
|
|
221
889
|
// src/commands/index.ts
|
|
222
|
-
var builtinCommands = [doctor, plugin, ts];
|
|
890
|
+
var builtinCommands = [doctor, plugin, ts, agent];
|
|
223
891
|
|
|
224
892
|
// src/core/loader.ts
|
|
225
893
|
async function loadBuiltinCommands() {
|
|
226
894
|
return builtinCommands.map((command) => ({ command, source: "builtin" }));
|
|
227
895
|
}
|
|
228
896
|
async function loadLocalPlugins(ctx) {
|
|
229
|
-
const dir =
|
|
897
|
+
const dir = path5.join(ctx.homeDir, "commands");
|
|
230
898
|
const loaded = [];
|
|
231
|
-
if (!
|
|
232
|
-
const files =
|
|
899
|
+
if (!fs4.existsSync(dir)) return loaded;
|
|
900
|
+
const files = fs4.readdirSync(dir).filter((f) => f.endsWith(".mjs")).sort();
|
|
233
901
|
for (const file of files) {
|
|
234
|
-
const abs =
|
|
902
|
+
const abs = path5.join(dir, file);
|
|
235
903
|
try {
|
|
236
904
|
const mod = await import(
|
|
237
905
|
/* @vite-ignore */
|
|
238
906
|
pathToFileURL(abs).href
|
|
239
907
|
);
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
908
|
+
const candidate = mod.default ?? mod;
|
|
909
|
+
const acceptedNames = loaded.map((l) => l.command?.name).filter((n) => typeof n === "string");
|
|
910
|
+
const validation = validateLocalCommand(candidate, { existingNames: acceptedNames });
|
|
911
|
+
if (!validation.ok || !validation.command) {
|
|
912
|
+
loaded.push({ source: "local", file: abs, error: validation.errors.join("\uFF1B") });
|
|
243
913
|
continue;
|
|
244
914
|
}
|
|
245
|
-
loaded.push({ command:
|
|
915
|
+
loaded.push({ command: validation.command, source: "local", file: abs });
|
|
246
916
|
} catch (err) {
|
|
247
917
|
loaded.push({
|
|
248
918
|
source: "local",
|
|
@@ -257,14 +927,120 @@ async function loadAllCommands(ctx) {
|
|
|
257
927
|
return [...await loadBuiltinCommands(), ...await loadLocalPlugins(ctx)];
|
|
258
928
|
}
|
|
259
929
|
|
|
930
|
+
// src/core/runner.ts
|
|
931
|
+
import { spawn } from "child_process";
|
|
932
|
+
var PLUGIN_ENV_KEYS = {
|
|
933
|
+
V_CLI_HOST_VERSION: VERSION,
|
|
934
|
+
V_CLI_PLUGIN_API: "1",
|
|
935
|
+
V_CLI_INVOKED_BY: "v-cli"
|
|
936
|
+
};
|
|
937
|
+
var SIGNAL_NUMBERS = {
|
|
938
|
+
SIGHUP: 1,
|
|
939
|
+
SIGINT: 2,
|
|
940
|
+
SIGQUIT: 3,
|
|
941
|
+
SIGILL: 4,
|
|
942
|
+
SIGTRAP: 5,
|
|
943
|
+
SIGABRT: 6,
|
|
944
|
+
SIGBUS: 7,
|
|
945
|
+
SIGFPE: 8,
|
|
946
|
+
SIGKILL: 9,
|
|
947
|
+
SIGUSR1: 10,
|
|
948
|
+
SIGSEGV: 11,
|
|
949
|
+
SIGUSR2: 12,
|
|
950
|
+
SIGPIPE: 13,
|
|
951
|
+
SIGALRM: 14,
|
|
952
|
+
SIGTERM: 15,
|
|
953
|
+
SIGSTKFLT: 16,
|
|
954
|
+
SIGCHLD: 17,
|
|
955
|
+
SIGCONT: 18,
|
|
956
|
+
SIGSTOP: 19,
|
|
957
|
+
SIGTSTP: 20,
|
|
958
|
+
SIGTTIN: 21,
|
|
959
|
+
SIGTTOU: 22,
|
|
960
|
+
SIGURG: 23,
|
|
961
|
+
SIGXCPU: 24,
|
|
962
|
+
SIGXFSZ: 25,
|
|
963
|
+
SIGVTALRM: 26,
|
|
964
|
+
SIGPROF: 27,
|
|
965
|
+
SIGWINCH: 28,
|
|
966
|
+
SIGIO: 29,
|
|
967
|
+
SIGPWR: 30,
|
|
968
|
+
SIGSYS: 31
|
|
969
|
+
};
|
|
970
|
+
function runPluginBin({ bin, argv, env = {}, cwd }) {
|
|
971
|
+
return new Promise((resolve, reject) => {
|
|
972
|
+
const child = spawn(process.execPath, [bin, ...argv], {
|
|
973
|
+
cwd,
|
|
974
|
+
env: { ...process.env, ...PLUGIN_ENV_KEYS, ...env },
|
|
975
|
+
stdio: "inherit",
|
|
976
|
+
shell: false
|
|
977
|
+
});
|
|
978
|
+
const forward = (signal) => {
|
|
979
|
+
try {
|
|
980
|
+
child.kill(signal);
|
|
981
|
+
} catch {
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
const onSigint = () => forward("SIGINT");
|
|
985
|
+
const onSigterm = () => forward("SIGTERM");
|
|
986
|
+
process.on("SIGINT", onSigint);
|
|
987
|
+
process.on("SIGTERM", onSigterm);
|
|
988
|
+
const cleanup = () => {
|
|
989
|
+
process.removeListener("SIGINT", onSigint);
|
|
990
|
+
process.removeListener("SIGTERM", onSigterm);
|
|
991
|
+
};
|
|
992
|
+
child.on("error", (err) => {
|
|
993
|
+
cleanup();
|
|
994
|
+
reject(err);
|
|
995
|
+
});
|
|
996
|
+
child.on("close", (code, signal) => {
|
|
997
|
+
cleanup();
|
|
998
|
+
resolve({ code, signal });
|
|
999
|
+
});
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
function mapExitCode(result) {
|
|
1003
|
+
if (result.code !== null) return result.code;
|
|
1004
|
+
if (result.signal !== null) {
|
|
1005
|
+
if (process.platform !== "win32") {
|
|
1006
|
+
const signum = SIGNAL_NUMBERS[result.signal];
|
|
1007
|
+
if (signum !== void 0) return 128 + signum;
|
|
1008
|
+
}
|
|
1009
|
+
return 1;
|
|
1010
|
+
}
|
|
1011
|
+
return 1;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
260
1014
|
// src/cli.ts
|
|
1015
|
+
function scanLeadingJson(rawArgs) {
|
|
1016
|
+
let i = 0;
|
|
1017
|
+
while (i < rawArgs.length && rawArgs[i] === "--json") i++;
|
|
1018
|
+
return { json: i > 0, args: rawArgs.slice(i), commandIndex: i };
|
|
1019
|
+
}
|
|
1020
|
+
async function interceptOfficialCommand(rawArgs, commandIndex) {
|
|
1021
|
+
const token = rawArgs[commandIndex];
|
|
1022
|
+
if (!isOfficialCommand(token)) return void 0;
|
|
1023
|
+
const spec = OFFICIAL_PLUGINS.find((p) => p.command === token);
|
|
1024
|
+
if (!spec) return void 0;
|
|
1025
|
+
const info = discoverOfficialPlugin(spec);
|
|
1026
|
+
if (info.status !== "available" || !info.bin) {
|
|
1027
|
+
process.stderr.write(
|
|
1028
|
+
`[v-cli] \u9519\u8BEF: \u5B98\u65B9\u63D2\u4EF6 ${spec.package}\uFF08\u547D\u4EE4 ${spec.command}\uFF09\u4E0D\u53EF\u7528\uFF1A${info.error ?? info.status}
|
|
1029
|
+
`
|
|
1030
|
+
);
|
|
1031
|
+
return 1;
|
|
1032
|
+
}
|
|
1033
|
+
const result = await runPluginBin({ bin: info.bin, argv: rawArgs.slice(commandIndex + 1) });
|
|
1034
|
+
return mapExitCode(result);
|
|
1035
|
+
}
|
|
261
1036
|
async function main() {
|
|
262
1037
|
const rawArgs = process.argv.slice(2);
|
|
263
|
-
const
|
|
264
|
-
const
|
|
265
|
-
const
|
|
1038
|
+
const span = scanLeadingJson(rawArgs);
|
|
1039
|
+
const ctx = createContext(defaultHomeDir(), span.json);
|
|
1040
|
+
const intercepted = await interceptOfficialCommand(rawArgs, span.commandIndex);
|
|
1041
|
+
if (intercepted !== void 0) return intercepted;
|
|
266
1042
|
const program = new Command();
|
|
267
|
-
program.name("v-cli").description("kevlns \u7684\u4E2A\u4EBA\u5DE5\u5177\u7BB1").version(VERSION).
|
|
1043
|
+
program.name("v-cli").description("kevlns \u7684\u4E2A\u4EBA\u5DE5\u5177\u7BB1").version(VERSION).showHelpAfterError();
|
|
268
1044
|
const loaded = await loadAllCommands(ctx);
|
|
269
1045
|
for (const item of loaded) {
|
|
270
1046
|
if (!item.command) {
|
|
@@ -273,12 +1049,22 @@ async function main() {
|
|
|
273
1049
|
}
|
|
274
1050
|
const cmd = item.command;
|
|
275
1051
|
if (cmd.hidden) continue;
|
|
276
|
-
const sub =
|
|
277
|
-
|
|
1052
|
+
const sub = new Command(cmd.name).description(cmd.description);
|
|
1053
|
+
try {
|
|
1054
|
+
cmd.register(sub, ctx);
|
|
1055
|
+
program.addCommand(sub);
|
|
1056
|
+
} catch (err) {
|
|
1057
|
+
ctx.log.warn(
|
|
1058
|
+
`${item.file ?? cmd.name} register \u5931\u8D25: ${err instanceof Error ? err.message : String(err)}\uFF1B\u5DF2\u8DF3\u8FC7`
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
for (const spec of OFFICIAL_PLUGINS) {
|
|
1063
|
+
program.command(spec.command).description(`${spec.description}\uFF08\u5B98\u65B9\u63D2\u4EF6\uFF1A\u5B89\u88C5\u540E\u53EF\u7528\uFF09`);
|
|
278
1064
|
}
|
|
279
1065
|
try {
|
|
280
|
-
await program.parseAsync(args, { from: "user" });
|
|
281
|
-
return 0;
|
|
1066
|
+
await program.parseAsync(span.args, { from: "user" });
|
|
1067
|
+
return typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
282
1068
|
} catch (err) {
|
|
283
1069
|
if (err instanceof CommanderError) {
|
|
284
1070
|
return err.exitCode ?? 1;
|