@yunzai-ng/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +34 -0
  3. package/dist/args.d.ts +44 -0
  4. package/dist/args.d.ts.map +1 -0
  5. package/dist/args.js +116 -0
  6. package/dist/args.js.map +1 -0
  7. package/dist/bin.d.ts +3 -0
  8. package/dist/bin.d.ts.map +1 -0
  9. package/dist/bin.js +40 -0
  10. package/dist/bin.js.map +1 -0
  11. package/dist/commands/doctor.d.ts +16 -0
  12. package/dist/commands/doctor.d.ts.map +1 -0
  13. package/dist/commands/doctor.js +181 -0
  14. package/dist/commands/doctor.js.map +1 -0
  15. package/dist/commands/init.d.ts +12 -0
  16. package/dist/commands/init.d.ts.map +1 -0
  17. package/dist/commands/init.js +61 -0
  18. package/dist/commands/init.js.map +1 -0
  19. package/dist/commands/plugin.d.ts +14 -0
  20. package/dist/commands/plugin.d.ts.map +1 -0
  21. package/dist/commands/plugin.js +115 -0
  22. package/dist/commands/plugin.js.map +1 -0
  23. package/dist/commands/start.d.ts +19 -0
  24. package/dist/commands/start.d.ts.map +1 -0
  25. package/dist/commands/start.js +102 -0
  26. package/dist/commands/start.js.map +1 -0
  27. package/dist/help.d.ts +6 -0
  28. package/dist/help.d.ts.map +1 -0
  29. package/dist/help.js +42 -0
  30. package/dist/help.js.map +1 -0
  31. package/dist/index.d.ts +17 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +17 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/link.d.ts +16 -0
  36. package/dist/link.d.ts.map +1 -0
  37. package/dist/link.js +103 -0
  38. package/dist/link.js.map +1 -0
  39. package/dist/run.d.ts +7 -0
  40. package/dist/run.d.ts.map +1 -0
  41. package/dist/run.js +115 -0
  42. package/dist/run.js.map +1 -0
  43. package/dist/terminal.d.ts +43 -0
  44. package/dist/terminal.d.ts.map +1 -0
  45. package/dist/terminal.js +94 -0
  46. package/dist/terminal.js.map +1 -0
  47. package/package.json +31 -0
  48. package/src/args.test.ts +52 -0
  49. package/src/args.ts +132 -0
  50. package/src/bin.ts +40 -0
  51. package/src/commands/doctor.ts +205 -0
  52. package/src/commands/init.ts +65 -0
  53. package/src/commands/plugin.ts +133 -0
  54. package/src/commands/start.ts +121 -0
  55. package/src/help.ts +42 -0
  56. package/src/index.ts +16 -0
  57. package/src/link.test.ts +53 -0
  58. package/src/link.ts +117 -0
  59. package/src/run.ts +124 -0
  60. package/src/terminal.ts +102 -0
package/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # @yunzai-ng/cli
2
+
3
+ 命令行入口。命令名 `yzng`。
4
+
5
+ ## 命令
6
+
7
+ | 命令 | 作用 |
8
+ |---|---|
9
+ | `yzng start` | 启动 |
10
+ | `yzng dev` | 启动,缺省日志级别更低 |
11
+ | `yzng init` | 只生成主目录与默认配置,不启动 |
12
+ | `yzng doctor` | 体检:Node 版本、目录权限、可选原生模块、端口占用 |
13
+ | `yzng plugin new <名>` | 生成一个插件骨架 |
14
+
15
+ 常用参数:
16
+
17
+ ```bash
18
+ yzng start --home D:\yunzai # 指定主目录(也可用环境变量 YZNG_HOME)
19
+ yzng start --plugins ../a,../b # 额外加载这几个目录(开发时用)
20
+ yzng start --log-level debug
21
+ ```
22
+
23
+ ## `dev` 与 `start` 的唯一区别是缺省日志级别
24
+
25
+ **刻意不提供文件监听自动重载。** Node 的 ESM 模块缓存无法真正清除 —— 每次重载都会永久留下一份
26
+ 旧模块连同其闭包。一个「保存即重载」的开发模式会让人在几十次保存之后面对一个内存里堆着几十份
27
+ 旧闭包的进程,而那时的行为已经无从解释。开发期改了插件,重启进程。
28
+
29
+ ## 主目录怎么定
30
+
31
+ 优先级:`--home` 参数 > `YZNG_HOME` 环境变量 > 便携模式标记 > 系统默认位置。
32
+
33
+ Windows 的默认位置是 `%LOCALAPPDATA%\YunzaiNG`。**首次启动会打印它的实际位置** —— 这一条要紧,
34
+ 因为「我改的配置为什么不生效」几乎总是改错了目录。
package/dist/args.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * 模块职责:命令行参数解析
3
+ * 依赖方向:无依赖
4
+ * 生命周期:纯函数
5
+ * 注意事项:手写实现而非引入 commander / yargs。CLI 的参数总计不足十个,
6
+ * 而这两个库各自会引入 10~30 个传递依赖 —— 对一个需在 Termux 上
7
+ * 执行 `pnpm install` 的框架而言,装机体积与安装时长是真实成本。
8
+ *
9
+ * `--` 之后的内容原样收入 `rest`,不作解析:`yzng plugin new x -- --raw`
10
+ * 这类"将其余参数转交给下游"的场景需要该行为。
11
+ */
12
+ /** 解析结果 */
13
+ export interface ParsedArgs {
14
+ /** 子命令,未提供时为空串 */
15
+ readonly command: string;
16
+ /** 子命令之后的位置参数 */
17
+ readonly positional: readonly string[];
18
+ /** 长短选项;出现但未带值的记为 `true` */
19
+ readonly flags: Readonly<Record<string, string | boolean>>;
20
+ /** `--` 之后的原样内容 */
21
+ readonly rest: readonly string[];
22
+ }
23
+ /**
24
+ * 解析 `process.argv.slice(2)`
25
+ * @param argv 参数数组
26
+ * @returns 解析结果
27
+ */
28
+ export declare function parseArgs(argv: readonly string[]): ParsedArgs;
29
+ /**
30
+ * 取字符串选项
31
+ * @param args 解析结果
32
+ * @param name 选项名
33
+ * @returns 值;未提供或仅为布尔开关时 undefined
34
+ */
35
+ export declare function flagString(args: ParsedArgs, name: string): string | undefined;
36
+ /**
37
+ * 取布尔选项
38
+ * @param args 解析结果
39
+ * @param name 选项名
40
+ * @param fallback 未提供时的取值
41
+ * @returns 布尔值
42
+ */
43
+ export declare function flagBoolean(args: ParsedArgs, name: string, fallback?: boolean): boolean;
44
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,WAAW;AACX,MAAM,WAAW,UAAU;IACzB,kBAAkB;IAClB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,iBAAiB;IACjB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAA;IAC1D,mBAAmB;IACnB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;CACjC;AAoBD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CA2D7D;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG7E;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAKrF"}
package/dist/args.js ADDED
@@ -0,0 +1,116 @@
1
+ /**
2
+ * 模块职责:命令行参数解析
3
+ * 依赖方向:无依赖
4
+ * 生命周期:纯函数
5
+ * 注意事项:手写实现而非引入 commander / yargs。CLI 的参数总计不足十个,
6
+ * 而这两个库各自会引入 10~30 个传递依赖 —— 对一个需在 Termux 上
7
+ * 执行 `pnpm install` 的框架而言,装机体积与安装时长是真实成本。
8
+ *
9
+ * `--` 之后的内容原样收入 `rest`,不作解析:`yzng plugin new x -- --raw`
10
+ * 这类"将其余参数转交给下游"的场景需要该行为。
11
+ */
12
+ /** 单字母短选项到长名的映射 */
13
+ const SHORT = {
14
+ h: "help",
15
+ v: "version",
16
+ d: "debug",
17
+ c: "home"
18
+ };
19
+ /**
20
+ * 会带值的选项
21
+ *
22
+ * 必须显式列出,不可依据"下一个 token 不似选项即视为其值"推断 —— 该规则会使
23
+ * `yzng --debug start` 中的 `start` 被 `--debug` 并入,子命令因此为空,
24
+ * CLI 转而输出帮助,而使用者无从察觉问题所在。清单之外的选项一律为布尔开关;
25
+ * 确需为其传值时应写 `--新选项=值`,等号形式的优先级恒高于本清单。
26
+ */
27
+ const VALUE_FLAGS = new Set(["home", "plugins", "port", "host"]);
28
+ /**
29
+ * 解析 `process.argv.slice(2)`
30
+ * @param argv 参数数组
31
+ * @returns 解析结果
32
+ */
33
+ export function parseArgs(argv) {
34
+ const positional = [];
35
+ const flags = {};
36
+ const rest = [];
37
+ let command = "";
38
+ for (let i = 0; i < argv.length; i++) {
39
+ const token = argv[i] ?? "";
40
+ if (token === "--") {
41
+ rest.push(...argv.slice(i + 1));
42
+ break;
43
+ }
44
+ if (token.startsWith("--")) {
45
+ const body = token.slice(2);
46
+ const eq = body.indexOf("=");
47
+ if (eq >= 0) {
48
+ flags[body.slice(0, eq)] = body.slice(eq + 1);
49
+ continue;
50
+ }
51
+ if (body.startsWith("no-")) {
52
+ flags[body.slice(3)] = false;
53
+ continue;
54
+ }
55
+ // 仅清单内的选项会消耗下一个 token 作为其值。代价是以横杠开头的值
56
+ // (`--home -x`)须写作 `--home=-x`;该代价换取的是子命令绝不会被误并入
57
+ const next = argv[i + 1];
58
+ if (VALUE_FLAGS.has(body) && next !== undefined && !next.startsWith("-")) {
59
+ flags[body] = next;
60
+ i++;
61
+ }
62
+ else {
63
+ flags[body] = true;
64
+ }
65
+ continue;
66
+ }
67
+ if (token.startsWith("-") && token.length > 1) {
68
+ // 允许 `-dv` 形式的合并写法;合并时仅最后一个字母可能带值
69
+ const letters = [...token.slice(1)];
70
+ for (let k = 0; k < letters.length; k++) {
71
+ const name = SHORT[letters[k] ?? ""] ?? letters[k] ?? "";
72
+ const last = k === letters.length - 1;
73
+ const next = argv[i + 1];
74
+ if (last && VALUE_FLAGS.has(name) && next !== undefined && !next.startsWith("-")) {
75
+ flags[name] = next;
76
+ i++;
77
+ }
78
+ else {
79
+ flags[name] = true;
80
+ }
81
+ }
82
+ continue;
83
+ }
84
+ if (command === "")
85
+ command = token;
86
+ else
87
+ positional.push(token);
88
+ }
89
+ return { command, positional, flags, rest };
90
+ }
91
+ /**
92
+ * 取字符串选项
93
+ * @param args 解析结果
94
+ * @param name 选项名
95
+ * @returns 值;未提供或仅为布尔开关时 undefined
96
+ */
97
+ export function flagString(args, name) {
98
+ const value = args.flags[name];
99
+ return typeof value === "string" && value !== "" ? value : undefined;
100
+ }
101
+ /**
102
+ * 取布尔选项
103
+ * @param args 解析结果
104
+ * @param name 选项名
105
+ * @param fallback 未提供时的取值
106
+ * @returns 布尔值
107
+ */
108
+ export function flagBoolean(args, name, fallback = false) {
109
+ const value = args.flags[name];
110
+ if (value === undefined)
111
+ return fallback;
112
+ if (typeof value === "boolean")
113
+ return value;
114
+ return value !== "false" && value !== "0";
115
+ }
116
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
package/dist/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
package/dist/bin.js ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 模块职责:`yzng` 可执行入口 —— 唯一允许调用 `process.exit` 的位置
4
+ * 依赖方向:仅调用 run()
5
+ * 生命周期:进程入口
6
+ * 注意事项:**未捕获异常在此处理,而非交由 Node 缺省处理。** Node 缺省会输出
7
+ * 完整调用栈,其中绝大部分为内核与 fastify 的栈帧;对使用者不具信息量,
8
+ * 反而将真正有用的第一行挤出屏幕。此处仅输出消息,调用栈保留给 `--debug`。
9
+ *
10
+ * `unhandledRejection` 必须显式接管:自 Node 20 起其缺省行为是终止进程,
11
+ * 而"某个插件的 promise 未附加 catch"不应导致整个机器人下线。
12
+ */
13
+ import process from "node:process";
14
+ import { run } from "./run.js";
15
+ /**
16
+ * 输出一条致命错误
17
+ * @param label 场景
18
+ * @param err 错误
19
+ */
20
+ function fatal(label, err) {
21
+ const message = err instanceof Error ? err.message : String(err);
22
+ process.stderr.write(`\n${label}:${message}\n`);
23
+ if (err instanceof Error && err.stack !== undefined && process.argv.includes("--debug")) {
24
+ process.stderr.write(`${err.stack}\n`);
25
+ }
26
+ else {
27
+ process.stderr.write("附加 --debug 可查看完整调用栈\n");
28
+ }
29
+ }
30
+ process.on("unhandledRejection", reason => {
31
+ fatal("未处理的 promise 拒绝", reason);
32
+ });
33
+ try {
34
+ process.exitCode = await run(process.argv.slice(2));
35
+ }
36
+ catch (err) {
37
+ fatal("命令执行失败", err);
38
+ process.exitCode = 1;
39
+ }
40
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG;AACH,OAAO,OAAO,MAAM,cAAc,CAAA;AAClC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAE9B;;;;GAIG;AACH,SAAS,KAAK,CAAC,KAAa,EAAE,GAAY;IACxC,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IAChE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,OAAO,IAAI,CAAC,CAAA;IAC/C,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACxF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,KAAK,IAAI,CAAC,CAAA;IACxC,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAA;IAC/C,CAAC;AACH,CAAC;AAED,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,MAAM,CAAC,EAAE;IACxC,KAAK,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAA;AAClC,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC;IACH,OAAO,CAAC,QAAQ,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;AACrD,CAAC;AAAC,OAAO,GAAG,EAAE,CAAC;IACb,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;IACpB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;AACtB,CAAC"}
@@ -0,0 +1,16 @@
1
+ /** 自检参数 */
2
+ export interface DoctorOptions {
3
+ /** 应用主目录 */
4
+ readonly home?: string | undefined;
5
+ /** 待探测的面板端口,缺省 2536(与配置缺省值一致) */
6
+ readonly port?: number | undefined;
7
+ /** 待探测的监听地址 */
8
+ readonly host?: string | undefined;
9
+ }
10
+ /**
11
+ * 执行一次环境自检
12
+ * @param opts 参数
13
+ * @returns 进程退出码;存在 error 级问题时为 1
14
+ */
15
+ export declare function runDoctor(opts?: DoctorOptions): Promise<number>;
16
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAwJA,WAAW;AACX,MAAM,WAAW,aAAa;IAC5B,YAAY;IACZ,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,iCAAiC;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,eAAe;IACf,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqCzE"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * 模块职责:`yzng doctor` —— 环境自检:运行环境、目录、可选原生依赖、端口占用
3
+ * 依赖方向:依赖 @yunzai-ng/core 的公开入口与 node:net
4
+ * 生命周期:一次性,执行完毕即退出
5
+ * 注意事项:**每一项均须给出后续处置方式,否则本命令不具备存在价值。**
6
+ * 一句"Redis 连接失败"不指明该改哪里,等于把排障推回给使用者;故每条不合格项
7
+ * 都附一条可执行的下一步。
8
+ *
9
+ * 退出码:存在 `error` 级问题时为 1,仅有 `warn` 时仍为 0 ——
10
+ * `yzng doctor && yzng start` 是自然的用法,不应被"尚未配置账号"一类提示阻断。
11
+ */
12
+ import net from "node:net";
13
+ import process from "node:process";
14
+ import { createRequire } from "node:module";
15
+ import { access } from "node:fs/promises";
16
+ import { detectPlatform, resolvePaths, sampleUsage } from "@yunzai-ng/core";
17
+ import { bold, cyan, dim, green, print, printRows, red, yellow } from "../terminal.js";
18
+ /** Node 最低版本,与根 package.json 的 engines 保持一致 */
19
+ const MIN_NODE_MAJOR = 20;
20
+ /**
21
+ * 检查 Node 版本
22
+ * @returns 结论
23
+ */
24
+ function checkNode() {
25
+ const major = Number.parseInt(process.versions.node.split(".")[0] ?? "0", 10);
26
+ if (major >= MIN_NODE_MAJOR)
27
+ return { level: "ok", title: "Node 版本", detail: process.version };
28
+ return {
29
+ level: "error",
30
+ title: "Node 版本",
31
+ detail: `${process.version},低于要求的 ${MIN_NODE_MAJOR}`,
32
+ next: "将 Node 升级至 20.11 或更高版本:内核使用了 import.meta.dirname 与原生 ESM 解析"
33
+ };
34
+ }
35
+ /**
36
+ * 检查一个可选原生依赖能否加载
37
+ *
38
+ * **解析基准必须是内核而非 CLI 自身。** pnpm 的隔离式 node_modules 将
39
+ * `classic-level` 安装于 `packages/core/node_modules` 之下,CLI 的模块图中并不含有
40
+ * 该包 —— 自 CLI 自身解析将一律得到"未安装",而该结论会将使用者引向
41
+ * 一个并不存在的问题(去安装一个实际已安装完毕的包)。
42
+ *
43
+ * 使用 `createRequire` 而非 `await import()`:这两个包均为 CommonJS 原生扩展,
44
+ * 而此处需要区分的是"未安装"与"已安装但 ABI 不匹配"—— 仅 `require` 的报错信息中
45
+ * 携带 `NODE_MODULE_VERSION` 一段,动态 import 会将其重新包装并丢失该细节。
46
+ * @param name 包名
47
+ * @param purpose 该包的用途
48
+ * @param fallback 缺失时的降级说明
49
+ * @returns 结论
50
+ */
51
+ function checkNative(name, purpose, fallback) {
52
+ const self = createRequire(import.meta.url);
53
+ let require = self;
54
+ try {
55
+ require = createRequire(self.resolve("@yunzai-ng/core/package.json"));
56
+ }
57
+ catch {
58
+ // 未找到内核时退回自身解析:此情形下 CLI 属于单独安装,
59
+ // 报告"未安装"至少不构成误导,而真正的问题(内核缺失)由其他检查项覆盖
60
+ }
61
+ try {
62
+ require.resolve(name);
63
+ }
64
+ catch {
65
+ return { level: "warn", title: name, detail: `未安装(${purpose})`, next: `${fallback}。需要时执行:pnpm add -w ${name}` };
66
+ }
67
+ try {
68
+ require(name);
69
+ return { level: "ok", title: name, detail: `可用(${purpose})` };
70
+ }
71
+ catch (err) {
72
+ return {
73
+ level: "warn",
74
+ title: name,
75
+ detail: `已安装但加载失败:${err instanceof Error ? err.message.split("\n")[0] : String(err)}`,
76
+ next: `原生模块与当前 Node ABI 不匹配,重建即可解决:pnpm rebuild ${name}。${fallback}`
77
+ };
78
+ }
79
+ }
80
+ /**
81
+ * 检查目录可达
82
+ * @param label 检查项名称
83
+ * @param dir 路径
84
+ * @returns 结论
85
+ */
86
+ async function checkDir(label, dir) {
87
+ try {
88
+ await access(dir);
89
+ return { level: "ok", title: label, detail: cyan(dir) };
90
+ }
91
+ catch {
92
+ return {
93
+ level: "warn",
94
+ title: label,
95
+ detail: `${cyan(dir)} ${dim("尚不存在")}`,
96
+ next: "首次启动时将自动创建;亦可先执行 yzng init"
97
+ };
98
+ }
99
+ }
100
+ /**
101
+ * 检查端口是否可监听
102
+ *
103
+ * 实际执行一次 bind 而非查询进程列表:其行为跨平台一致,且"能否监听"本身即为
104
+ * 内核关注的问题 —— 端口被同一用户的另一实例占用,与被防火墙策略拦截,
105
+ * 对启动而言结果相同。
106
+ * @param host 监听地址
107
+ * @param port 端口
108
+ * @returns 结论
109
+ */
110
+ function checkPort(host, port) {
111
+ return new Promise(resolve => {
112
+ const probe = net.createServer();
113
+ probe.once("error", (err) => {
114
+ resolve({
115
+ level: "error",
116
+ title: "面板端口",
117
+ detail: `${host}:${port} 不可用(${err.code ?? err.message})`,
118
+ next: err.code === "EADDRINUSE"
119
+ ? "该端口已被其他程序占用(通常为另一个 Yunzai 实例)。请修改 config/yunzai.yaml 中的 server.port,或先停止该程序"
120
+ : "请检查监听地址是否属于本机网卡"
121
+ });
122
+ });
123
+ probe.once("listening", () => {
124
+ probe.close(() => resolve({ level: "ok", title: "面板端口", detail: `${host}:${port} 可用` }));
125
+ });
126
+ probe.listen(port, host);
127
+ });
128
+ }
129
+ /**
130
+ * 输出一条结论
131
+ * @param finding 结论
132
+ */
133
+ function printFinding(finding) {
134
+ const mark = finding.level === "ok" ? green("✓") : finding.level === "warn" ? yellow("!") : red("✗");
135
+ print(` ${mark} ${finding.title} ${finding.detail}`);
136
+ if (finding.next !== undefined)
137
+ print(` ${dim(`→ ${finding.next}`)}`);
138
+ }
139
+ /**
140
+ * 执行一次环境自检
141
+ * @param opts 参数
142
+ * @returns 进程退出码;存在 error 级问题时为 1
143
+ */
144
+ export async function runDoctor(opts = {}) {
145
+ const platform = detectPlatform();
146
+ const paths = resolvePaths({ home: opts.home });
147
+ const usage = sampleUsage();
148
+ print();
149
+ print(` ${bold("环境")}`);
150
+ printRows([
151
+ ["系统", `${platform.os}/${platform.arch}${platform.isTermux ? " · Termux" : ""}${platform.isContainer ? " · 容器" : ""}`],
152
+ ["Node", platform.nodeVersion],
153
+ ["CPU", `${platform.cpus} 核`],
154
+ ["常驻内存", `${(usage.rss / 1024 / 1024).toFixed(1)} MB`]
155
+ ]);
156
+ const findings = [
157
+ checkNode(),
158
+ await checkDir("主目录", paths.home),
159
+ await checkDir("配置目录", paths.config),
160
+ await checkDir("数据目录", paths.data),
161
+ checkNative("classic-level", "内嵌 KV,用于计数与冷却", "缺失时内核降级为 JSON 文件存储,功能不受影响但写入速度较低"),
162
+ checkNative("better-sqlite3", "关系数据,用于插件建表", "缺失时依赖 SQL 的插件将取得一条错误说明,而非静默失效"),
163
+ await checkPort(opts.host ?? "127.0.0.1", opts.port ?? 2536)
164
+ ];
165
+ print();
166
+ print(` ${bold("检查")}`);
167
+ for (const finding of findings)
168
+ printFinding(finding);
169
+ const errors = findings.filter(f => f.level === "error").length;
170
+ const warns = findings.filter(f => f.level === "warn").length;
171
+ print();
172
+ if (errors > 0)
173
+ print(` ${red(`${errors} 项需要处理`)}${warns > 0 ? dim(`,${warns} 项提示`) : ""}`);
174
+ else if (warns > 0)
175
+ print(` ${yellow(`${warns} 项提示`)}${dim(",不影响启动")}`);
176
+ else
177
+ print(` ${green("全部检查项正常")}`);
178
+ print();
179
+ return errors > 0 ? 1 : 0;
180
+ }
181
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /** 初始化参数 */
2
+ export interface InitOptions {
3
+ /** 应用主目录;缺省时按内核的探测顺序确定 */
4
+ readonly home?: string | undefined;
5
+ }
6
+ /**
7
+ * 建立主目录与缺省配置
8
+ * @param opts 参数
9
+ * @returns 进程退出码
10
+ */
11
+ export declare function runInit(opts?: InitOptions): Promise<number>;
12
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAiBA,YAAY;AACZ,MAAM,WAAW,WAAW;IAC1B,0BAA0B;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoCrE"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * 模块职责:`yzng init` —— 在主目录中建立目录布局与缺省配置文件
3
+ * 依赖方向:依赖 @yunzai-ng/core 的公开入口
4
+ * 生命周期:一次性,执行完毕即退出
5
+ * 注意事项:**刻意不经由 `createApp()`。** `createApp` 会一并打开 KV 数据库、
6
+ * 建立 HTTP 客户端连接池、装配插件宿主 —— 在"仅需查看配置文件结构"
7
+ * 的场景中,上述工作均属额外开销,且 KV 无法打开时会直接抛出异常,
8
+ * 使一次本应成功的初始化转为失败。此处仅执行两项必要工作:建立目录、
9
+ * 声明配置(从而将缺省值落盘)。
10
+ *
11
+ * 幂等:已存在的目录与配置文件均不会被覆盖(`ConfigStore` 仅在文件
12
+ * 缺失时写入缺省值),因此对一个已长期运行的实例再次执行 init 是安全的。
13
+ */
14
+ import { ConfigStore, createLoggerHub, defineCoreConfig, ensurePaths, resolvePaths } from "@yunzai-ng/core";
15
+ import { linkFramework } from "../link.js";
16
+ import { bold, cyan, dim, green, print, printErr, printRows, red, yellow } from "../terminal.js";
17
+ /**
18
+ * 建立主目录与缺省配置
19
+ * @param opts 参数
20
+ * @returns 进程退出码
21
+ */
22
+ export async function runInit(opts = {}) {
23
+ // 仅输出至控制台、级别限制为 warn:ConfigStore 自身会输出"已写出缺省配置"一类的
24
+ // info,而本命令需要给出的是一份经过整理的清单,两者叠加将降低可读性
25
+ const hub = createLoggerHub({ level: "warn", console: true });
26
+ try {
27
+ const paths = await ensurePaths(resolvePaths({ home: opts.home }));
28
+ const store = new ConfigStore({ dir: paths.config, logger: hub.root, watch: false });
29
+ const config = await defineCoreConfig(store);
30
+ const link = await linkFramework(paths.home);
31
+ print();
32
+ print(` ${bold(green("已就绪"))} ${dim("目录与缺省配置均已建立")}`);
33
+ print();
34
+ printRows([
35
+ ["主目录", cyan(paths.home)],
36
+ ["配置", cyan(paths.config)],
37
+ ["数据", cyan(paths.data)],
38
+ ["日志", cyan(paths.logs)],
39
+ ["插件", cyan(paths.plugins)],
40
+ ["临时", cyan(paths.temp)]
41
+ ]);
42
+ print();
43
+ if (link.failed.length > 0) {
44
+ print(` ${yellow("!")} 框架包未能链接至主目录,插件将无法 import @yunzai-ng/core`);
45
+ for (const [name, reason] of link.failed)
46
+ print(` ${dim(`${name}:${reason}`)}`);
47
+ print();
48
+ }
49
+ print(` ${dim("下一步")} ${cyan("yzng start")} ${dim(`随后访问 http://${config.get().server.host}:${config.get().server.port}/`)}`);
50
+ print();
51
+ return 0;
52
+ }
53
+ catch (err) {
54
+ printErr(red(`初始化失败:${err instanceof Error ? err.message : String(err)}`));
55
+ return 1;
56
+ }
57
+ finally {
58
+ hub.close();
59
+ }
60
+ }
61
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /** 脚手架参数 */
2
+ export interface PluginNewOptions {
3
+ /** 插件名 */
4
+ readonly name: string;
5
+ /** 应用主目录 */
6
+ readonly home?: string | undefined;
7
+ }
8
+ /**
9
+ * 生成插件骨架
10
+ * @param opts 参数
11
+ * @returns 进程退出码
12
+ */
13
+ export declare function runPluginNew(opts: PluginNewOptions): Promise<number>;
14
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/commands/plugin.ts"],"names":[],"mappings":"AAmBA,YAAY;AACZ,MAAM,WAAW,gBAAgB;IAC/B,UAAU;IACV,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,YAAY;IACZ,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAmED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmC1E"}