@napuketto/cli 0.0.3 → 0.0.4
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 +1 -1
- package/dist/index.mjs +99 -116
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -12,4 +12,4 @@ napuketto config list # 列出全局配置与账号配置
|
|
|
12
12
|
napuketto config apply <file> # 应用外部配置(校验后写回)
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
常用选项:`-d, --data-dir <dir>` 数据根目录(缺省
|
|
15
|
+
常用选项:`-d, --data-dir <dir>` 数据根目录(缺省 `<项目根>/.napuketto`);`--stub-dir <dir>` stub QQNT.dll 目录。
|
package/dist/index.mjs
CHANGED
|
@@ -142,26 +142,27 @@ async function runSingleAccount(opts = {}) {
|
|
|
142
142
|
/**
|
|
143
143
|
* 主配置类型与手写校验器(从 config-cmds.ts 拆分,2026-08-08 FTA 优化)
|
|
144
144
|
*
|
|
145
|
-
* 主配置 parse(不引入 zod):数据根 / 自动重启 / 重启延迟 /
|
|
146
|
-
*
|
|
145
|
+
* 主配置 parse(不引入 zod):数据根 / 自动重启 / 重启延迟 / 账号列表。
|
|
146
|
+
* 2026-08-08 结构拍板:**一个 QQ 账号一个 [[accounts]] 段,协议与通信配置嵌在
|
|
147
|
+
* 账号内**([accounts.onebot11] / [accounts.satori]);账号必填(至少一个)。
|
|
148
|
+
* 协议段为宽松对象,装配时由对应协议包的 zod schema 严格校验。
|
|
147
149
|
*/
|
|
148
150
|
/** 默认自动重启。 */
|
|
149
151
|
const DEFAULT_AUTO_RESTART = true;
|
|
150
152
|
/** 默认重启延迟(毫秒)。 */
|
|
151
153
|
const DEFAULT_RESTART_DELAY_MS$1 = 2e3;
|
|
152
|
-
/**
|
|
153
|
-
function
|
|
154
|
-
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
/** 解析 dataDir(缺省用当前解析的数据根)。 */
|
|
154
|
+
/** 解析账号内协议段(宽松对象,装配时由 zod schema 严格校验)。 */
|
|
155
|
+
function parseProtocolSection(raw, key) {
|
|
156
|
+
const section = raw[key];
|
|
157
|
+
if (section === void 0) return;
|
|
158
|
+
if (typeof section !== "object" || section === null) throw kernelError(`主配置账号 ${key} 段必须是对象`, "INVALID_PARAM");
|
|
159
|
+
return section;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* 解析 dataDir(缺省用当前解析的数据根 = 项目根/.napuketto)。
|
|
163
|
+
* 支持绝对路径、"~/" 前缀(用户主目录)、相对路径(相对启动目录),
|
|
164
|
+
* 消费端 resolveDataRoot 统一展开为绝对路径。
|
|
165
|
+
*/
|
|
165
166
|
function parseDataDir(raw) {
|
|
166
167
|
if (raw["dataDir"] === void 0) return resolveDataRoot();
|
|
167
168
|
if (typeof raw["dataDir"] !== "string" || raw["dataDir"] === "") throw kernelError("主配置 dataDir 必须是非空字符串", "INVALID_PARAM");
|
|
@@ -179,15 +180,15 @@ function parseRestartDelayMs(raw) {
|
|
|
179
180
|
if (typeof raw["restartDelayMs"] !== "number" || !Number.isInteger(raw["restartDelayMs"]) || raw["restartDelayMs"] <= 0) throw kernelError("主配置 restartDelayMs 必须是正整数", "INVALID_PARAM");
|
|
180
181
|
return raw["restartDelayMs"];
|
|
181
182
|
}
|
|
182
|
-
/** 解析 accounts
|
|
183
|
+
/** 解析 accounts(必须至少一个账号,qq 必填)。 */
|
|
183
184
|
function parseAccounts(raw) {
|
|
184
|
-
if (raw["accounts"] === void 0)
|
|
185
|
-
if (!Array.isArray(raw["accounts"])) throw kernelError("主配置 accounts
|
|
185
|
+
if (raw["accounts"] === void 0) throw kernelError("主配置缺少 accounts(必须至少一个账号);首次启动已生成模板,请编辑后重试", "INVALID_PARAM");
|
|
186
|
+
if (!Array.isArray(raw["accounts"]) || raw["accounts"].length === 0) throw kernelError("主配置 accounts 必须是非空数组(至少一个 QQ 账号);首次启动已生成模板,请编辑后重试", "INVALID_PARAM");
|
|
186
187
|
const accounts = [];
|
|
187
188
|
for (const item of raw["accounts"]) accounts.push(parseAccount(item));
|
|
188
189
|
return accounts;
|
|
189
190
|
}
|
|
190
|
-
/**
|
|
191
|
+
/** 解析单个账号项(qq 必填;协议段缺省 = 不启用)。 */
|
|
191
192
|
function parseAccount(item) {
|
|
192
193
|
if (typeof item !== "object" || item === null) throw kernelError("主配置 accounts 元素必须是对象", "INVALID_PARAM");
|
|
193
194
|
const { qq, enabled } = item;
|
|
@@ -197,52 +198,53 @@ function parseAccount(item) {
|
|
|
197
198
|
if (typeof enabled !== "boolean") throw kernelError("主配置账号 enabled 必须是布尔值", "INVALID_PARAM");
|
|
198
199
|
out.enabled = enabled;
|
|
199
200
|
}
|
|
201
|
+
const raw = item;
|
|
202
|
+
const onebot11 = parseProtocolSection(raw, "onebot11");
|
|
203
|
+
if (onebot11 !== void 0) out.onebot11 = onebot11;
|
|
204
|
+
const satori = parseProtocolSection(raw, "satori");
|
|
205
|
+
if (satori !== void 0) out.satori = satori;
|
|
200
206
|
return out;
|
|
201
207
|
}
|
|
202
208
|
/** 手写校验器:主配置 parse(不引入 zod)。 */
|
|
203
209
|
function parseCliConfig(input) {
|
|
204
210
|
if (typeof input !== "object" || input === null) throw kernelError("主配置必须是对象", "INVALID_PARAM");
|
|
205
211
|
const raw = input;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const satori = parseSatori(raw);
|
|
212
|
-
const out = {
|
|
213
|
-
dataDir,
|
|
214
|
-
autoRestart,
|
|
215
|
-
restartDelayMs,
|
|
216
|
-
accounts
|
|
212
|
+
return {
|
|
213
|
+
dataDir: parseDataDir(raw),
|
|
214
|
+
autoRestart: parseAutoRestart(raw),
|
|
215
|
+
restartDelayMs: parseRestartDelayMs(raw),
|
|
216
|
+
accounts: parseAccounts(raw)
|
|
217
217
|
};
|
|
218
|
-
if (onebot11 !== void 0) out.onebot11 = onebot11;
|
|
219
|
-
if (satori !== void 0) out.satori = satori;
|
|
220
|
-
return out;
|
|
221
218
|
}
|
|
222
219
|
//#endregion
|
|
223
220
|
//#region src/config-template.ts
|
|
224
221
|
/**
|
|
225
222
|
* 配置模板(TOML 注释版,从 config-cmds.ts 拆分,2026-08-08 FTA 优化)。
|
|
226
|
-
* `config init` 与首次启动(loadCliConfig)缺失时生成。
|
|
223
|
+
* `config init` 与首次启动(loadCliConfig)缺失时生成。
|
|
224
|
+
*
|
|
225
|
+
* 2026-08-08 结构拍板:**一个 QQ 账号一个 [[accounts]] 段,协议与通信配置嵌在
|
|
226
|
+
* 账号内**([accounts.onebot11] / [accounts.satori]);账号必填(至少一个)。
|
|
227
|
+
* dataDir 不再写死(缺省 = 项目根/.napuketto,跨平台),模板保持可移植。
|
|
227
228
|
*/
|
|
228
|
-
function configTemplate(
|
|
229
|
+
function configTemplate() {
|
|
229
230
|
return `# ============================================================
|
|
230
231
|
# NapukettoQQ 全局配置文件
|
|
231
232
|
# ============================================================
|
|
232
233
|
# 本文件由 \`napuketto config init\` 生成(或首次启动自动生成)。
|
|
234
|
+
# 本机配置不入库(.gitignore);修改后重启生效。
|
|
233
235
|
#
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
#
|
|
237
|
-
#
|
|
238
|
-
# 单账号启动每次读取本文件;修改后重启生效。
|
|
236
|
+
# 配置组织:一个 QQ 账号一个 [[accounts]] 段,协议与通信配置嵌在账号内
|
|
237
|
+
# ([accounts.onebot11] / [accounts.satori])。账号没有写某协议段 = 该账号
|
|
238
|
+
# 不启用该协议。账号必填(至少一个,qq 必填)。
|
|
239
239
|
|
|
240
240
|
# ------------------------------------------------------------
|
|
241
|
-
#
|
|
241
|
+
# 全局配置(跨账号)
|
|
242
242
|
# ------------------------------------------------------------
|
|
243
243
|
|
|
244
|
-
# 数据根目录:账号目录/日志/缓存/QQ
|
|
245
|
-
|
|
244
|
+
# 数据根目录:账号目录/日志/缓存/QQ 数据存放位置。
|
|
245
|
+
# 缺省 = <项目根>/.napuketto(部署场景,跨平台,无需修改)。
|
|
246
|
+
# 支持绝对路径、"~/" 前缀(用户主目录)、相对路径(相对启动目录)。
|
|
247
|
+
# dataDir = ".napuketto"
|
|
246
248
|
|
|
247
249
|
# 多账号(supervisor)模式下,账号进程崩溃是否自动重启
|
|
248
250
|
autoRestart = true
|
|
@@ -250,15 +252,15 @@ autoRestart = true
|
|
|
250
252
|
# 崩溃后自动重启的延迟(毫秒)
|
|
251
253
|
restartDelayMs = 2000
|
|
252
254
|
|
|
253
|
-
# 多账号列表(留空 = 单账号模式:启动后自动快速登录/扫码)
|
|
254
|
-
# [[accounts]]
|
|
255
|
-
# qq = "123456" # QQ 号(必填)
|
|
256
|
-
# enabled = true # 是否启用(可选,缺省 true)
|
|
257
|
-
|
|
258
255
|
# ------------------------------------------------------------
|
|
259
|
-
#
|
|
256
|
+
# 账号(至少一个,qq 必填)
|
|
260
257
|
# ------------------------------------------------------------
|
|
261
|
-
|
|
258
|
+
|
|
259
|
+
[[accounts]]
|
|
260
|
+
qq = "123456" # QQ 号(必填)
|
|
261
|
+
enabled = true # 是否启用(可选,缺省 true)
|
|
262
|
+
|
|
263
|
+
[accounts.onebot11] # 该账号的 OneBot 11 配置(无此段 = 不启用 OB11)
|
|
262
264
|
|
|
263
265
|
# 鉴权 token(全局默认):HTTP/WS 连接必须携带。
|
|
264
266
|
# 校验方式:Authorization: Bearer <token> / 裸 token / URL 参数 ?access_token=<token>
|
|
@@ -276,21 +278,21 @@ reportSelfMessage = false
|
|
|
276
278
|
messagePostFormat = "array"
|
|
277
279
|
|
|
278
280
|
# ---- HTTP 反向服务器(接收第三方 POST 指令),可配置多个 ----
|
|
279
|
-
[[onebot11.httpServers]]
|
|
281
|
+
[[accounts.onebot11.httpServers]]
|
|
280
282
|
enabled = true
|
|
281
283
|
host = "127.0.0.1" # 监听地址;0.0.0.0 = 监听所有网卡(局域网可连)
|
|
282
284
|
port = 3000
|
|
283
285
|
# token = "abc" # 可选:覆盖全局 token
|
|
284
286
|
|
|
285
287
|
# ---- HTTP 正向上报(Webhook:把事件 POST 到第三方地址),可配置多个 ----
|
|
286
|
-
# [[onebot11.httpPostUrls]]
|
|
288
|
+
# [[accounts.onebot11.httpPostUrls]]
|
|
287
289
|
# enabled = true
|
|
288
290
|
# url = "http://127.0.0.1:8080/onebot" # 第三方接收地址
|
|
289
291
|
# token = "abc" # 可选:覆盖全局 token
|
|
290
292
|
# timeoutMs = 5000 # 可选:上报超时(毫秒),缺省不超时
|
|
291
293
|
|
|
292
294
|
# ---- WS 反向服务器(外部 WS 客户端主动连入),可配置多个 ----
|
|
293
|
-
[[onebot11.wsServers]]
|
|
295
|
+
[[accounts.onebot11.wsServers]]
|
|
294
296
|
enabled = true
|
|
295
297
|
host = "127.0.0.1"
|
|
296
298
|
port = 3001
|
|
@@ -298,7 +300,7 @@ port = 3001
|
|
|
298
300
|
# heartbeatInterval = 30000 # 可选:WS ping 间隔(毫秒)
|
|
299
301
|
|
|
300
302
|
# ---- WS 正向客户端(主动连接外部 WS,双向),可配置多个 ----
|
|
301
|
-
# [[onebot11.wsReverseUrls]]
|
|
303
|
+
# [[accounts.onebot11.wsReverseUrls]]
|
|
302
304
|
# enabled = true
|
|
303
305
|
# url = "ws://127.0.0.1:8081/ws" # 第三方 WS 地址(ws:// 或 wss://)
|
|
304
306
|
# token = "abc" # 可选:覆盖全局 token
|
|
@@ -307,54 +309,40 @@ port = 3001
|
|
|
307
309
|
# rejectUnauthorized = true # 可选:wss:// 是否校验证书(自签证书设 false)
|
|
308
310
|
# heartbeatInterval = 30000 # 可选:WS ping 间隔(毫秒)
|
|
309
311
|
|
|
310
|
-
#
|
|
311
|
-
#
|
|
312
|
-
#
|
|
313
|
-
[satori]
|
|
314
|
-
|
|
315
|
-
#
|
|
316
|
-
#
|
|
317
|
-
#
|
|
318
|
-
|
|
319
|
-
#
|
|
320
|
-
#
|
|
321
|
-
|
|
322
|
-
#
|
|
323
|
-
[[satori.httpServers]]
|
|
324
|
-
enabled = true
|
|
325
|
-
host = "127.0.0.1" # 监听地址;0.0.0.0 = 监听所有网卡(局域网可连)
|
|
326
|
-
port = 5500
|
|
327
|
-
# token = "abc" # 可选:覆盖全局 token
|
|
328
|
-
|
|
329
|
-
# ---- WS 事件服务(/v1/events,Satori SDK/应用连入),可配置多个 ----
|
|
330
|
-
[[satori.wsServers]]
|
|
331
|
-
enabled = true
|
|
332
|
-
host = "127.0.0.1"
|
|
333
|
-
port = 5501
|
|
334
|
-
# token = "abc" # 可选:覆盖全局 token
|
|
312
|
+
# 第二个账号示例(只开 Satori):
|
|
313
|
+
# [[accounts]]
|
|
314
|
+
# qq = "654321"
|
|
315
|
+
# [accounts.satori]
|
|
316
|
+
# # token = "" # 可选:Satori 鉴权 token
|
|
317
|
+
# [[accounts.satori.httpServers]] # HTTP RPC(POST /v1/{resource}.{method})
|
|
318
|
+
# enabled = true
|
|
319
|
+
# host = "127.0.0.1"
|
|
320
|
+
# port = 5500
|
|
321
|
+
# [[accounts.satori.wsServers]] # WS 事件服务(/v1/events)
|
|
322
|
+
# enabled = true
|
|
323
|
+
# host = "127.0.0.1"
|
|
324
|
+
# port = 5501
|
|
335
325
|
`;
|
|
336
326
|
}
|
|
337
327
|
//#endregion
|
|
338
328
|
//#region src/config-cmds.ts
|
|
339
|
-
/** 主配置默认值(dataRoot
|
|
329
|
+
/** 主配置默认值(dataRoot 为当前解析的数据根 = 项目根/.napuketto)。 */
|
|
340
330
|
function defaultCliConfig(dataRoot) {
|
|
341
331
|
return {
|
|
342
332
|
dataDir: dataRoot,
|
|
343
333
|
autoRestart: true,
|
|
344
334
|
restartDelayMs: 2e3,
|
|
345
|
-
accounts: []
|
|
346
|
-
onebot11: {},
|
|
347
|
-
satori: {}
|
|
335
|
+
accounts: []
|
|
348
336
|
};
|
|
349
337
|
}
|
|
350
338
|
/** 配置文件缺失 → 生成带注释模板(首次启动自动生成默认配置)。 */
|
|
351
|
-
async function ensureConfigTemplate(path
|
|
339
|
+
async function ensureConfigTemplate(path) {
|
|
352
340
|
try {
|
|
353
341
|
await access(path);
|
|
354
342
|
} catch {
|
|
355
343
|
mkdirSync(dirname(path), { recursive: true });
|
|
356
|
-
await writeFile(path, `${configTemplate(
|
|
357
|
-
logger.info({ path }, "
|
|
344
|
+
await writeFile(path, `${configTemplate()}\n`, "utf8");
|
|
345
|
+
logger.info({ path }, "配置文件不存在,已生成默认模板(请编辑 accounts 段填入 QQ 号)");
|
|
358
346
|
}
|
|
359
347
|
}
|
|
360
348
|
/** 主配置 store(读/写/校验;.toml 后缀 → smol-toml 序列化)。
|
|
@@ -368,10 +356,10 @@ var CliConfigStore = class extends ConfigBase {
|
|
|
368
356
|
});
|
|
369
357
|
}
|
|
370
358
|
};
|
|
371
|
-
/**
|
|
359
|
+
/** 读取主配置(缺失自动落模板)。 */
|
|
372
360
|
async function loadCliConfig(dataRoot) {
|
|
373
361
|
const store = new CliConfigStore(dataRoot);
|
|
374
|
-
await ensureConfigTemplate(store.path
|
|
362
|
+
await ensureConfigTemplate(store.path);
|
|
375
363
|
return await store.load();
|
|
376
364
|
}
|
|
377
365
|
/** CliConfig → 普通对象(smol-toml stringify 入参)。 */
|
|
@@ -379,7 +367,8 @@ function toRecord(config) {
|
|
|
379
367
|
return config;
|
|
380
368
|
}
|
|
381
369
|
/** config init:生成全局 TOML 配置文件(项目根)+ 数据根目录。
|
|
382
|
-
* 文件不存在 → 写带注释模板;已存在 → 跳过(不覆盖用户配置)。
|
|
370
|
+
* 文件不存在 → 写带注释模板;已存在 → 跳过(不覆盖用户配置)。
|
|
371
|
+
* 输出原始模板内容(账号未填时解析会校验失败,init 只负责生成不校验)。 */
|
|
383
372
|
async function cmdConfigInit(opts) {
|
|
384
373
|
const dataRoot = resolveDataRoot(opts.dataDir);
|
|
385
374
|
mkdirSync(dataRoot, { recursive: true });
|
|
@@ -389,14 +378,14 @@ async function cmdConfigInit(opts) {
|
|
|
389
378
|
await access(store.path);
|
|
390
379
|
} catch {
|
|
391
380
|
mkdirSync(dirname(store.path), { recursive: true });
|
|
392
|
-
await writeFile(store.path, `${configTemplate(
|
|
381
|
+
await writeFile(store.path, `${configTemplate()}\n`, "utf8");
|
|
393
382
|
created = true;
|
|
394
383
|
}
|
|
395
|
-
const
|
|
384
|
+
const raw = await readFile(store.path, "utf8");
|
|
396
385
|
logger.info({ dataRoot }, "数据根");
|
|
397
|
-
if (created) logger.info({ path: store.path }, "
|
|
386
|
+
if (created) logger.info({ path: store.path }, "全局配置已生成(请编辑 accounts 段填入 QQ 号)");
|
|
398
387
|
else logger.info({ path: store.path }, "全局配置已存在(跳过生成,避免覆盖用户配置)");
|
|
399
|
-
process.stdout.write(
|
|
388
|
+
process.stdout.write(raw);
|
|
400
389
|
}
|
|
401
390
|
/** config list:列出全局配置与各账号目录。 */
|
|
402
391
|
async function cmdConfigList(opts) {
|
|
@@ -578,11 +567,13 @@ async function runSupervisor(opts = {}) {
|
|
|
578
567
|
//#endregion
|
|
579
568
|
//#region src/index.ts
|
|
580
569
|
/**
|
|
581
|
-
* @napuketto/cli 入口(P2-6 + P6,2026-08-05;2026-08-07
|
|
570
|
+
* @napuketto/cli 入口(P2-6 + P6,2026-08-05;2026-08-07 用户拍板:只保留自建宿主;
|
|
571
|
+
* 2026-08-08 配置结构拍板:账号必填,协议配置嵌在账号内)
|
|
582
572
|
*
|
|
583
573
|
* commander 参数解析:
|
|
584
574
|
* - 单账号:-q <uin> → runSingleAccount(自建宿主:标准 node + stub QQNT.dll,不拉起 QQ)
|
|
585
575
|
* - 多账号:-q A -q B 或 supervisor 子命令 → runSupervisor(子进程编排)
|
|
576
|
+
* - 无 -q:读全局配置 accounts(至少一个,账号必填)→ supervisor 拉起全部启用账号
|
|
586
577
|
* - config 子命令:init / list / apply(主配置管理)
|
|
587
578
|
*
|
|
588
579
|
* 路线 B(拉起 QQ + 注入)已淘汰(2026-08-07 用户拍板),仅 launchQqWithLoader 历史回退。
|
|
@@ -668,32 +659,24 @@ async function runSingleAccountBranch(opts, qqs) {
|
|
|
668
659
|
if (opts.stubDir !== void 0) bootOptions.stubDir = opts.stubDir;
|
|
669
660
|
await runSingleAccount(bootOptions);
|
|
670
661
|
}
|
|
671
|
-
/** 无 -q 时:读全局配置 accounts
|
|
662
|
+
/** 无 -q 时:读全局配置 accounts(账号必填,2026-08-08 拍板),supervisor 拉起全部启用账号。
|
|
663
|
+
* 不再有「配置为空 → 交互式单账号登录」老路径:缺账号直接报错提示编辑模板。 */
|
|
672
664
|
async function autoStart(opts) {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
});
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
} catch {}
|
|
682
|
-
const bootOptions = {};
|
|
683
|
-
if (opts.dataDir !== void 0) bootOptions.dataDir = opts.dataDir;
|
|
684
|
-
if (opts.stubDir !== void 0) bootOptions.stubDir = opts.stubDir;
|
|
685
|
-
logger.info("未指定 -q,自动快速登录(无历史账号则二维码登录)");
|
|
686
|
-
await runSingleAccount(bootOptions);
|
|
665
|
+
if ((await loadCliConfig(resolveDataRoot(opts.dataDir))).accounts.length === 0) throw new Error("主配置没有账号,请编辑 napuketto.toml 填写 [[accounts]] 段");
|
|
666
|
+
await runSupervisor({
|
|
667
|
+
...opts.dataDir !== void 0 ? { dataDir: opts.dataDir } : {},
|
|
668
|
+
...opts.stubDir !== void 0 ? { stubDir: opts.stubDir } : {}
|
|
669
|
+
});
|
|
687
670
|
}
|
|
688
|
-
/** 注册主命令 action(-q 单账号 / 多 -q supervisor / 无 -q
|
|
671
|
+
/** 注册主命令 action(-q 单账号 / 多 -q supervisor / 无 -q 读配置 supervisor 拉起)。 */
|
|
689
672
|
function registerMainAction(program) {
|
|
690
673
|
program.action(async (opts) => {
|
|
691
|
-
const qqs = opts.qq ?? [];
|
|
692
|
-
if (qqs.length === 0) {
|
|
693
|
-
await autoStart(opts);
|
|
694
|
-
return;
|
|
695
|
-
}
|
|
696
674
|
try {
|
|
675
|
+
const qqs = opts.qq ?? [];
|
|
676
|
+
if (qqs.length === 0) {
|
|
677
|
+
await autoStart(opts);
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
697
680
|
if (qqs.length === 1) await runSingleAccountBranch(opts, qqs);
|
|
698
681
|
else {
|
|
699
682
|
const supOpts = { qqs };
|
|
@@ -712,7 +695,7 @@ function main() {
|
|
|
712
695
|
process.stdout.write(`${BANNER}\n`);
|
|
713
696
|
const program = new Command();
|
|
714
697
|
program.name("napuketto").version("0.0.1").description("NapukettoQQ 机器人框架(OneBot 11 兼容)");
|
|
715
|
-
program.option("-q, --qq <uin>", "QQ 号(可重复;多个时 supervisor 拉起多账号子进程)", collectQq, []).option("-d, --data-dir <dir>", "数据根目录(缺省
|
|
698
|
+
program.option("-q, --qq <uin>", "QQ 号(可重复;多个时 supervisor 拉起多账号子进程)", collectQq, []).option("-d, --data-dir <dir>", "数据根目录(缺省 <项目根>/.napuketto)").option("--qq-path <path>", "QQ 安装路径(联调覆盖)").option("--stub-dir <dir>", "stub QQNT.dll 目录(自建宿主 PATH 前置,缺省 loader/native/stub-test-env,Git Submodule)");
|
|
716
699
|
registerConfigCommands(program);
|
|
717
700
|
registerSupervisorCommand(program);
|
|
718
701
|
registerMainAction(program);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["DEFAULT_RESTART_DELAY_MS"],"sources":["../src/logger.ts","../src/boot.ts","../src/config-parse.ts","../src/config-template.ts","../src/config-cmds.ts","../src/supervisor.ts","../src/index.ts"],"sourcesContent":["/**\n * cli 进程级 logger(ADR-007 日志规范,2026-08-07 统一改造)。\n *\n * 复用 kernel `createLogger`(console pretty + redact,格式与 kernel 子进程完全一致:\n * `[时间] LEVEL (name/pid): 消息` + 缩进元数据)。只打 console 不写文件——文件日志由\n * kernel 装配(NapukettoCore.create → `<数据根>/logs/napuketto.log`)负责。\n *\n * 无全局单例(ADR-015 推论):cli 每进程一份,模块级单例即可;`base.name` 用 pino\n * 保留字段 name(logger name)标注来源——pino-pretty 自动渲染为 `(cli/pid)` 元数据头,\n * 不占额外属性行,文件/JSON 侧仍保留 name 字段可过滤。\n *\n * 级别:`NAPKETTO_LOG_LEVEL` 环境变量可覆盖(缺省 info,非法值忽略)。\n */\nimport process from \"node:process\";\nimport { createLogger, type LogLevel } from \"@napuketto/kernel\";\n\n/** 合法级别集合(pino 标准级别)。 */\nconst LOG_LEVELS: readonly LogLevel[] = [\n \"trace\",\n \"debug\",\n \"info\",\n \"warn\",\n \"error\",\n \"fatal\",\n \"silent\",\n];\n\n/** 解析日志级别(env 覆盖,非法/缺失回退 info)。 */\nfunction resolveLevel(): LogLevel {\n const raw = process.env[\"NAPKETTO_LOG_LEVEL\"];\n if (raw !== undefined && (LOG_LEVELS as readonly string[]).includes(raw)) {\n return raw as LogLevel;\n }\n return \"info\";\n}\n\n/**\n * 窄接口注解(避免 TS2883:createLogger 返回 pino.Logger,cli 不直接依赖 pino,\n * declaration emit 无法命名该类型;pino.Logger 结构兼容本接口)。\n */\nexport interface CliLogger {\n info(obj: unknown, msg?: string): void;\n warn(obj: unknown, msg?: string): void;\n error(obj: unknown, msg?: string): void;\n debug(obj: unknown, msg?: string): void;\n trace(obj: unknown, msg?: string): void;\n fatal(obj: unknown, msg?: string): void;\n}\n\n/** cli 进程日志(name 渲染为 (cli/pid) 元数据头,文件 JSON 保留 name 字段可过滤)。 */\nexport const logger: CliLogger = createLogger({\n level: resolveLevel(),\n console: true,\n base: { service: \"cli\" },\n});\n","/**\n * cli boot:单账号启动序列(2026-08-07 用户拍板:只保留自建宿主)\n *\n * locate QQ(取版本/wrapper 路径)→ 解析各包 dist 入口 → launchSelfHost\n * (标准 node + stub QQNT.dll 直接 dlopen,不拉起 QQ / 不注入)→ 常驻。\n *\n * 不写业务逻辑:kernel 装配 + 登录 + 协议装配全部在 self-host.cjs → boot-bootstrap.js 完成。\n * 路线 B(拉起 QQ + 注入)已淘汰(launchQqWithLoader 仅历史回退,cli 不再调用)。\n */\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport { createInterface } from \"node:readline\";\nimport { fileURLToPath } from \"node:url\";\nimport { resolveConfigPath, resolveDataRoot } from \"@napuketto/kernel\";\nimport {\n checkInstanceLock,\n defaultStubDir,\n launchSelfHost,\n type QqInstallInfo,\n resolveQqInstall,\n} from \"@napuketto/loader\";\nimport { logger } from \"./logger.js\";\n\n/** 单账号启动选项。 */\nexport interface BootOptions {\n /** QQ 号(数据目录账号隔离,ADR-016)。 */\n qq?: string;\n /** 数据根目录(缺省环境变量/用户目录)。 */\n dataDir?: string;\n /** 覆盖 QQ 安装路径(联调)。 */\n qqPath?: string;\n /** stub QQNT.dll 目录(缺省 loader 包内闭源 submodule native/stub-test-env)。 */\n stubDir?: string;\n}\n\n/** 解析 workspace 包的 dist 入口(ESM 解析:包是 ESM-only,exports 无 require 条件)。 */\nasync function packageEntry(pkg: string): Promise<string> {\n const url = await import.meta.resolve(pkg);\n return fileURLToPath(url);\n}\n\n/**\n * 原生噪音行(wrapper.node 加载后直写 fd 的 C++ 日志,JS 层无法拦截,只能过滤):\n * - `<MMKV` / `<MemoryFile_Win32` / `<MMKV_IO`:MMKV 存储库刷屏(每次初始化打 ~6 行)\n * - `loadSymbolFromShell` / `getNodeGetJsListApi` / `get symbol failed`:\n * 标准 node 无腾讯私有符号(NodeContextifyContextMetrics 等),GetProcAddress\n * 失败的加载警告(无害,纯噪音)\n * - `loaded [mmkv.*] with N key-values`:MMKV 初始化完成行(野生日志,风格三,\n * 无统一前缀/时间戳,过滤)\n */\nconst NATIVE_NOISE =\n /<MMKV|<MemoryFile_Win32|<MMKV_IO|loadSymbolFromShell|getNodeGetJsListApi|get symbol failed|loaded \\[mmkv/i;\n\n/**\n * 逐行转发子进程输出到父进程,过滤原生噪音。\n * readline 按 UTF-8 解码 pipe 字节流,再经 process.stdout(TTY 路径,\n * WriteConsoleW UTF-16)输出——顺带修复 pino 中文在 cmd.exe/管道 936 转码\n * 链路下的乱码(原生 printf 字节流无法从 JS 侧改编码,转 pipe 后统一解码)。\n */\nfunction forwardFiltered(input: NodeJS.ReadableStream, out: NodeJS.WritableStream): void {\n const lines = createInterface({ input });\n lines.on(\"line\", (line) => {\n if (NATIVE_NOISE.test(line)) {\n return;\n }\n out.write(`${line}\\n`);\n });\n}\n\n/** 启动单个账号(自建宿主 + 常驻)。 */\nexport async function runSingleAccount(opts: BootOptions = {}): Promise<void> {\n const dataRoot = resolveDataRoot(opts.dataDir);\n const qq: QqInstallInfo = resolveQqInstall(opts.qqPath);\n const cfgDir = path.join(dataRoot, opts.qq ?? \"default\");\n\n // 单实例锁预检(2026-08-07 根治「多实例抢数据目录锁挂起」):同一账号数据\n // 目录只允许一个实例(QQ 原生层 MMKV/登录单例有锁,第二个实例抢不到会卡死)。\n // 占用 → 快速失败并提示占用 PID;残留(PID 已死)→ self-host 会自动接管。\n // 真正的锁由 self-host 子进程获取/释放(它才是数据目录的实际持有者),\n // cli 这里只做 spawn 前预检,给用户友好提示(supervisor 等批量拉起同用)。\n const { occupied, pid: holderPid } = checkInstanceLock(cfgDir);\n if (occupied) {\n logger.error(\n { pid: holderPid, dataDir: cfgDir },\n \"数据目录已被其他实例占用,拒绝启动(同一账号数据目录仅允许一个实例;\" +\n \"如确认无实例在跑,请删除该目录下的 instance.lock)\",\n );\n process.exitCode = 1;\n return;\n }\n\n const kernelEntry = await packageEntry(\"@napuketto/kernel\");\n const adapterEntry = await packageEntry(\"@napuketto/adapter\");\n const networkEntry = await packageEntry(\"@napuketto/network\");\n\n const stubDir = opts.stubDir ?? process.env[\"NAPUTO_STUB_DIR\"] ?? defaultStubDir();\n\n // 启动信息走结构化日志(时间戳 + 级别 + pid + 元数据,与 kernel 子进程格式一致)\n logger.info({ qqVersion: qq.version, qqPath: qq.qqPath }, \"QQ 安装信息\");\n logger.info({ dataDir: cfgDir }, \"数据目录\");\n logger.info(\"自建宿主引导(标准 node + stub QQNT.dll)\");\n\n // 唯一启动路径:自建宿主(2026-08-07 用户拍板,路线 B 淘汰)\n // stdio 接管 stdout/stderr:过滤 MMKV / 符号查找失败等原生噪音,其余转发\n // configPath:全局配置文件(项目根 napuketto.toml),注入 NAPKETTO_CONFIG 供装配链读取\n const { child } = launchSelfHost({\n qq,\n kernelEntry,\n adapterEntry,\n networkEntry,\n cfgDir,\n // 子进程 cwd 指向数据根:QQ 原生层 fallback 落盘(guild1.db 等)\n // 落在专门的数据目录,不污染项目根(实测 08-07)。\n cwd: dataRoot,\n configPath: resolveConfigPath({ dataRoot }),\n selfHost: true,\n stdio: [\"inherit\", \"pipe\", \"pipe\"],\n ...(opts.qq !== undefined ? { quickUin: opts.qq } : {}),\n ...(stubDir !== undefined ? { stubDir } : {}),\n });\n\n if (child.stdout !== null) {\n forwardFiltered(child.stdout, process.stdout);\n }\n if (child.stderr !== null) {\n forwardFiltered(child.stderr, process.stderr);\n }\n\n // 常驻:等待自建宿主进程退出\n await new Promise<void>((resolve) => {\n child.on(\"exit\", (code) => {\n logger.info({ code }, \"自建宿主进程退出\");\n resolve();\n });\n child.on(\"error\", (err) => {\n logger.error({ err }, \"自建宿主进程启动失败\");\n resolve();\n });\n });\n}\n","/**\n * 主配置类型与手写校验器(从 config-cmds.ts 拆分,2026-08-08 FTA 优化)\n *\n * 主配置 parse(不引入 zod):数据根 / 自动重启 / 重启延迟 / 账号列表 /\n * onebot11 段 / satori 段。协议段为宽松对象,装配时由对应协议包的 zod schema 严格校验。\n */\nimport { kernelError, resolveDataRoot } from \"@napuketto/kernel\";\n\n/** 默认自动重启。 */\nconst DEFAULT_AUTO_RESTART = true;\n/** 默认重启延迟(毫秒)。 */\nconst DEFAULT_RESTART_DELAY_MS = 2000;\n\n/** 账号配置项。 */\nexport interface CliAccountConfig {\n qq: string;\n enabled?: boolean;\n}\n\n/** 主配置(跨账号,全局单文件 TOML)。 */\nexport interface CliConfig {\n /** 数据根目录(绝对路径)。 */\n dataDir: string;\n /** supervisor 是否自动重启崩溃账号。 */\n autoRestart: boolean;\n /** 崩溃后重启延迟(毫秒)。 */\n restartDelayMs: number;\n /** 账号列表。 */\n accounts: CliAccountConfig[];\n /** onebot11 协议段(与 ob11ConfigSchema 对应,宽松对象,装配时 zod 校验)。 */\n onebot11?: Record<string, unknown>;\n /** satori 协议段(与 satoriConfigSchema 对应,宽松对象,装配时 zod 校验)。 */\n satori?: Record<string, unknown>;\n}\n\n/** 解析 onebot11 段(宽松对象,装配时由 zod schema 严格校验)。 */\nfunction parseOnebot11(raw: Record<string, unknown>): Record<string, unknown> | undefined {\n if (raw[\"onebot11\"] === undefined) {\n return;\n }\n if (typeof raw[\"onebot11\"] !== \"object\" || raw[\"onebot11\"] === null) {\n throw kernelError(\"主配置 onebot11 段必须是对象\", \"INVALID_PARAM\");\n }\n return raw[\"onebot11\"] as Record<string, unknown>;\n}\n\n/** 解析 satori 段(宽松对象,装配时由 zod schema 严格校验)。 */\nfunction parseSatori(raw: Record<string, unknown>): Record<string, unknown> | undefined {\n if (raw[\"satori\"] === undefined) {\n return;\n }\n if (typeof raw[\"satori\"] !== \"object\" || raw[\"satori\"] === null) {\n throw kernelError(\"主配置 satori 段必须是对象\", \"INVALID_PARAM\");\n }\n return raw[\"satori\"] as Record<string, unknown>;\n}\n\n/** 解析 dataDir(缺省用当前解析的数据根)。 */\nfunction parseDataDir(raw: Record<string, unknown>): string {\n if (raw[\"dataDir\"] === undefined) {\n return resolveDataRoot();\n }\n if (typeof raw[\"dataDir\"] !== \"string\" || raw[\"dataDir\"] === \"\") {\n throw kernelError(\"主配置 dataDir 必须是非空字符串\", \"INVALID_PARAM\");\n }\n return raw[\"dataDir\"];\n}\n\n/** 解析 autoRestart。 */\nfunction parseAutoRestart(raw: Record<string, unknown>): boolean {\n if (raw[\"autoRestart\"] === undefined) {\n return DEFAULT_AUTO_RESTART;\n }\n if (typeof raw[\"autoRestart\"] !== \"boolean\") {\n throw kernelError(\"主配置 autoRestart 必须是布尔值\", \"INVALID_PARAM\");\n }\n return raw[\"autoRestart\"];\n}\n\n/** 解析 restartDelayMs(毫秒)。 */\nfunction parseRestartDelayMs(raw: Record<string, unknown>): number {\n if (raw[\"restartDelayMs\"] === undefined) {\n return DEFAULT_RESTART_DELAY_MS;\n }\n if (\n typeof raw[\"restartDelayMs\"] !== \"number\" ||\n !Number.isInteger(raw[\"restartDelayMs\"]) ||\n raw[\"restartDelayMs\"] <= 0\n ) {\n throw kernelError(\"主配置 restartDelayMs 必须是正整数\", \"INVALID_PARAM\");\n }\n return raw[\"restartDelayMs\"];\n}\n\n/** 解析 accounts。 */\nfunction parseAccounts(raw: Record<string, unknown>): CliAccountConfig[] {\n if (raw[\"accounts\"] === undefined) {\n return [];\n }\n if (!Array.isArray(raw[\"accounts\"])) {\n throw kernelError(\"主配置 accounts 必须是数组\", \"INVALID_PARAM\");\n }\n const accounts: CliAccountConfig[] = [];\n for (const item of raw[\"accounts\"]) {\n accounts.push(parseAccount(item));\n }\n return accounts;\n}\n\n/** 解析单个账号项。 */\nfunction parseAccount(item: unknown): CliAccountConfig {\n if (typeof item !== \"object\" || item === null) {\n throw kernelError(\"主配置 accounts 元素必须是对象\", \"INVALID_PARAM\");\n }\n const { qq, enabled } = item as { qq?: unknown; enabled?: unknown };\n if (typeof qq !== \"string\" || qq === \"\") {\n throw kernelError(\"主配置账号缺少非空 qq\", \"INVALID_PARAM\");\n }\n const out: CliAccountConfig = { qq };\n if (enabled !== undefined) {\n if (typeof enabled !== \"boolean\") {\n throw kernelError(\"主配置账号 enabled 必须是布尔值\", \"INVALID_PARAM\");\n }\n out.enabled = enabled;\n }\n return out;\n}\n\n/** 手写校验器:主配置 parse(不引入 zod)。 */\nexport function parseCliConfig(input: unknown): CliConfig {\n if (typeof input !== \"object\" || input === null) {\n throw kernelError(\"主配置必须是对象\", \"INVALID_PARAM\");\n }\n const raw = input as Record<string, unknown>;\n const dataDir = parseDataDir(raw);\n const autoRestart = parseAutoRestart(raw);\n const restartDelayMs = parseRestartDelayMs(raw);\n const accounts = parseAccounts(raw);\n const onebot11 = parseOnebot11(raw);\n const satori = parseSatori(raw);\n const out: CliConfig = { dataDir, autoRestart, restartDelayMs, accounts };\n if (onebot11 !== undefined) {\n out.onebot11 = onebot11;\n }\n if (satori !== undefined) {\n out.satori = satori;\n }\n return out;\n}\n","/**\n * 配置模板(TOML 注释版,从 config-cmds.ts 拆分,2026-08-08 FTA 优化)。\n * `config init` 与首次启动(loadCliConfig)缺失时生成。dataDir 按当前数据根插值。\n */\nexport function configTemplate(dataRoot: string): string {\n // JSON.stringify 转义反斜杠 → TOML 基本字符串合法写法(\"C:\\\\Users\\\\...\")\n const dataDirLiteral = JSON.stringify(dataRoot);\n return `# ============================================================\n# NapukettoQQ 全局配置文件\n# ============================================================\n# 本文件由 \\`napuketto config init\\` 生成(或首次启动自动生成)。\n#\n# 数据(账号目录/日志/缓存/QQ 数据)不在此文件,按数据根组织:\n# 优先级:--data-dir <dir> > NAPKETTO_DATA 环境变量 > ~/.napuketto\n# 配置文件路径解析:NAPKETTO_CONFIG > 项目根探测 > cwd > 数据根兜底。\n#\n# 单账号启动每次读取本文件;修改后重启生效。\n\n# ------------------------------------------------------------\n# 基础配置\n# ------------------------------------------------------------\n\n# 数据根目录:账号目录/日志/缓存/QQ 数据存放位置(绝对路径)\ndataDir = ${dataDirLiteral}\n\n# 多账号(supervisor)模式下,账号进程崩溃是否自动重启\nautoRestart = true\n\n# 崩溃后自动重启的延迟(毫秒)\nrestartDelayMs = 2000\n\n# 多账号列表(留空 = 单账号模式:启动后自动快速登录/扫码)\n# [[accounts]]\n# qq = \"123456\" # QQ 号(必填)\n# enabled = true # 是否启用(可选,缺省 true)\n\n# ------------------------------------------------------------\n# OneBot 11 协议段\n# ------------------------------------------------------------\n[onebot11]\n\n# 鉴权 token(全局默认):HTTP/WS 连接必须携带。\n# 校验方式:Authorization: Bearer <token> / 裸 token / URL 参数 ?access_token=<token>\n# 每个网络实例可单独覆盖(见下文各实例的 token 字段)。\n# 留空 = 不鉴权(仅本机调试推荐)。\n# token = \"\"\n\n# 心跳 meta 事件间隔(毫秒);0 = 关闭心跳事件\nheartbeatInterval = 3000\n\n# 是否上报机器人自己发的消息(OB11 规范默认不上报)\nreportSelfMessage = false\n\n# 消息内容格式:array = 消息段数组(标准 OneBot 11),string = CQ 码字符串\nmessagePostFormat = \"array\"\n\n# ---- HTTP 反向服务器(接收第三方 POST 指令),可配置多个 ----\n[[onebot11.httpServers]]\nenabled = true\nhost = \"127.0.0.1\" # 监听地址;0.0.0.0 = 监听所有网卡(局域网可连)\nport = 3000\n# token = \"abc\" # 可选:覆盖全局 token\n\n# ---- HTTP 正向上报(Webhook:把事件 POST 到第三方地址),可配置多个 ----\n# [[onebot11.httpPostUrls]]\n# enabled = true\n# url = \"http://127.0.0.1:8080/onebot\" # 第三方接收地址\n# token = \"abc\" # 可选:覆盖全局 token\n# timeoutMs = 5000 # 可选:上报超时(毫秒),缺省不超时\n\n# ---- WS 反向服务器(外部 WS 客户端主动连入),可配置多个 ----\n[[onebot11.wsServers]]\nenabled = true\nhost = \"127.0.0.1\"\nport = 3001\n# token = \"abc\" # 可选:覆盖全局 token\n# heartbeatInterval = 30000 # 可选:WS ping 间隔(毫秒)\n\n# ---- WS 正向客户端(主动连接外部 WS,双向),可配置多个 ----\n# [[onebot11.wsReverseUrls]]\n# enabled = true\n# url = \"ws://127.0.0.1:8081/ws\" # 第三方 WS 地址(ws:// 或 wss://)\n# token = \"abc\" # 可选:覆盖全局 token\n# reconnectDelayMs = 5000 # 可选:断线重连延迟(毫秒)\n# maxReconnectAttempts = 10 # 可选:最大重连次数(缺省无限)\n# rejectUnauthorized = true # 可选:wss:// 是否校验证书(自签证书设 false)\n# heartbeatInterval = 30000 # 可选:WS ping 间隔(毫秒)\n\n# ------------------------------------------------------------\n# Satori 协议段\n# ------------------------------------------------------------\n[satori]\n\n# 鉴权 token(全局默认):HTTP RPC 走 Authorization: Bearer <token>;\n# WS 事件服务走 IDENTIFY 信令的 token 字段。留空 = 不鉴权。\n# token = \"\"\n\n# 媒体资源(img/audio/video/file)下载缓存目录(发方向 http(s) src 落盘)\n# cacheDir = \"\"\n\n# ---- HTTP RPC 服务器(POST /v1/{resource}.{method}),可配置多个 ----\n[[satori.httpServers]]\nenabled = true\nhost = \"127.0.0.1\" # 监听地址;0.0.0.0 = 监听所有网卡(局域网可连)\nport = 5500\n# token = \"abc\" # 可选:覆盖全局 token\n\n# ---- WS 事件服务(/v1/events,Satori SDK/应用连入),可配置多个 ----\n[[satori.wsServers]]\nenabled = true\nhost = \"127.0.0.1\"\nport = 5501\n# token = \"abc\" # 可选:覆盖全局 token\n`;\n}\n","/**\n * cli config 子命令:init / list / apply(P6,2026-08-05;2026-08-07 配置文件移到项目根)\n *\n * **全局单配置文件**:`<项目根>/napuketto.toml`(2026-08-07 用户拍板:配置文件放项目根,\n * 数据仍按数据根组织),本项目所有配置都在里面管理(TOML + smol-toml,配合 zod 校验,\n * 用户 2026-08-05 拍板:JSON 门槛太高):\n *\n * dataDir = \"C:\\\\...\\\\.napuketto\" # 数据根(账号目录/日志/缓存/QQ 数据),与配置文件位置解耦\n * autoRestart = true\n * restartDelayMs = 2000\n * [[accounts]]\n * qq = \"123456\"\n * enabled = true\n * [onebot11] # 协议段,与 ob11ConfigSchema 对应\n * heartbeatInterval = 3000\n * [onebot11.http] # 嵌套表\n * enabled = false\n * host = \"127.0.0.1\"\n * port = 3000\n * [satori] # 协议段,与 satoriConfigSchema 对应\n * token = \"\"\n * [[satori.httpServers]] # HTTP RPC 服务器\n * enabled = false\n * port = 5500\n * [[satori.wsServers]] # WS 事件服务(/v1/events)\n * enabled = true\n * port = 5501\n *\n * 配置文件路径解析见 kernel `resolveConfigPath`(NAPKETTO_CONFIG 显式 > 项目根探测 > cwd > 数据根兜底);\n * 校验器为手写 parse(适配 kernel ConfigBase 的 ConfigSchema 形状);\n * 协议段由对应协议包的 zod schema 校验(boot.cjs 装配时经 seed 传入)。\n */\nimport type { Dirent } from \"node:fs\";\nimport { mkdirSync, readdirSync } from \"node:fs\";\nimport { access, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, extname, join } from \"node:path\";\nimport process from \"node:process\";\nimport type { ConfigSchema } from \"@napuketto/kernel\";\nimport {\n ConfigBase,\n kernelError,\n parseToml,\n resolveConfigPath,\n resolveDataRoot,\n stringifyToml,\n} from \"@napuketto/kernel\";\nimport { type CliConfig, parseCliConfig } from \"./config-parse.js\";\nimport { configTemplate } from \"./config-template.js\";\nimport { logger } from \"./logger.js\";\n\n/** 主配置默认值(dataRoot 为当前解析的数据根)。 */\nfunction defaultCliConfig(dataRoot: string): CliConfig {\n return {\n dataDir: dataRoot,\n autoRestart: true,\n restartDelayMs: 2000,\n accounts: [],\n onebot11: {},\n satori: {},\n };\n}\n\n/** 配置文件缺失 → 生成带注释模板(首次启动自动生成默认配置)。 */\nasync function ensureConfigTemplate(path: string, dataRoot: string): Promise<void> {\n try {\n await access(path);\n } catch {\n mkdirSync(dirname(path), { recursive: true });\n await writeFile(path, `${configTemplate(dataRoot)}\\n`, \"utf8\");\n logger.info({ path }, \"配置文件不存在,已生成默认模板(带注释,可参考修改)\");\n }\n}\n\n/** 主配置 store(读/写/校验;.toml 后缀 → smol-toml 序列化)。\n * 配置文件在项目根(resolveConfigPath),dataRoot 仅作兜底与 dataDir 默认值。 */\nclass CliConfigStore extends ConfigBase<CliConfig> {\n constructor(dataRoot: string) {\n super({\n path: resolveConfigPath({ dataRoot }),\n schema: { parse: parseCliConfig } satisfies ConfigSchema<CliConfig>,\n defaults: defaultCliConfig(dataRoot),\n });\n }\n}\n\n/** 读取主配置(缺失自动落默认)。 */\nexport async function loadCliConfig(dataRoot: string): Promise<CliConfig> {\n const store = new CliConfigStore(dataRoot);\n await ensureConfigTemplate(store.path, dataRoot);\n return await store.load();\n}\n\n/** CliConfig → 普通对象(smol-toml stringify 入参)。 */\nfunction toRecord(config: CliConfig): Record<string, unknown> {\n return config as unknown as Record<string, unknown>;\n}\n\n/** config init:生成全局 TOML 配置文件(项目根)+ 数据根目录。\n * 文件不存在 → 写带注释模板;已存在 → 跳过(不覆盖用户配置)。 */\nexport async function cmdConfigInit(opts: { dataDir?: string }): Promise<void> {\n const dataRoot = resolveDataRoot(opts.dataDir);\n mkdirSync(dataRoot, { recursive: true });\n const store = new CliConfigStore(dataRoot);\n let created = false;\n try {\n await access(store.path);\n } catch {\n mkdirSync(dirname(store.path), { recursive: true });\n await writeFile(store.path, `${configTemplate(dataRoot)}\\n`, \"utf8\");\n created = true;\n }\n const config = await store.load();\n logger.info({ dataRoot }, \"数据根\");\n if (created) {\n logger.info({ path: store.path }, \"全局配置已生成(默认模板,带注释)\");\n } else {\n logger.info({ path: store.path }, \"全局配置已存在(跳过生成,避免覆盖用户配置)\");\n }\n // TOML 内容为功能输出(机器可读),保持原样 stdout,不做日志包装\n process.stdout.write(`${stringifyToml(toRecord(config))}\\n`);\n}\n\n/** config list:列出全局配置与各账号目录。 */\nexport async function cmdConfigList(opts: { dataDir?: string }): Promise<void> {\n const dataRoot = resolveDataRoot(opts.dataDir);\n const store = new CliConfigStore(dataRoot);\n const config = await store.load();\n logger.info({ dataRoot }, \"数据根\");\n logger.info({ path: store.path }, \"全局配置\");\n process.stdout.write(`${stringifyToml(toRecord(config))}\\n`);\n // 账号目录(数据根下的子目录)\n let entries: Dirent[];\n try {\n entries = readdirSync(dataRoot, { withFileTypes: true });\n } catch {\n entries = [];\n }\n for (const entry of entries) {\n if (entry.isDirectory()) {\n printAccountConfig(entry.name, dataRoot);\n }\n }\n}\n\n/** 打印账号 config 目录文件清单。 */\nfunction printAccountConfig(account: string, dataRoot: string): void {\n const configDir = join(dataRoot, account, \"config\");\n let files: string[] = [];\n try {\n files = readdirSync(configDir).filter((f) => f.endsWith(\".json\") || f.endsWith(\".toml\"));\n } catch {\n files = [];\n }\n let fileList = \"(未初始化)\";\n if (files.length > 0) {\n fileList = files.join(\", \");\n }\n // 注意:字段名用 account 而非 name——name 是 pino 保留键(logger name),\n // 会被 pino-pretty 渲染成 `(name/pid)` 元数据头而非普通字段。\n logger.info({ account, files: fileList }, \"账号配置\");\n}\n\n/** 解析外部配置文本(按扩展名推断 TOML/JSON)。 */\nfunction parseExternalConfig(file: string, rawText: string): unknown {\n if (extname(file).toLowerCase() === \".toml\") {\n return parseToml(rawText);\n }\n try {\n return JSON.parse(rawText) as unknown;\n } catch {\n throw kernelError(`外部配置 ${file} 不是合法 JSON`, \"INVALID_PARAM\");\n }\n}\n\n/** config apply:应用外部配置(顶层覆盖合并后写回全局配置)。 */\nexport async function cmdConfigApply(file: string, opts: { dataDir?: string }): Promise<void> {\n const dataRoot = resolveDataRoot(opts.dataDir);\n const store = new CliConfigStore(dataRoot);\n const existing = await store.load();\n let rawText: string;\n try {\n rawText = await readFile(file, \"utf8\");\n } catch (err) {\n let message = String(err);\n if (err instanceof Error) {\n const { message: errMessage } = err;\n message = errMessage;\n }\n throw kernelError(`读取外部配置失败 ${file}: ${message}`, \"INVALID_PARAM\", { cause: err });\n }\n const partial = parseExternalConfig(file, rawText);\n if (typeof partial !== \"object\" || partial === null) {\n throw kernelError(\"外部配置必须是对象\", \"INVALID_PARAM\");\n }\n const patch = partial as Record<string, unknown>;\n const merged: Record<string, unknown> = { ...existing, ...patch };\n const next = parseCliConfig(merged);\n await store.save(next);\n logger.info({ path: store.path }, \"全局配置已更新\");\n // TOML 内容为功能输出(机器可读),保持原样 stdout,不做日志包装\n process.stdout.write(`${stringifyToml(toRecord(next))}\\n`);\n}\n","/**\n * supervisor:多账号子进程编排(P6,2026-08-05;2026-08-07 自建宿主唯一)\n *\n * 多账号走多进程(ADR-015):每账号一个独立子进程(node <入口> -q <uin>\n * --data-dir <dataRoot> [--stub-dir <dir>],复用单账号 runSingleAccount 零改动),\n * 本模块作为父进程:\n * - 拉起:主配置 accounts(supervisor 子命令)或显式 -q 列表\n * - 守护:账号异常退出且 autoRestart → 延迟重启\n * - 信号转发:SIGINT/SIGTERM → 停止全部子进程 → 父进程退出\n */\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport process from \"node:process\";\nimport { resolveDataRoot } from \"@napuketto/kernel\";\nimport { loadCliConfig } from \"./config-cmds.js\";\nimport type { CliAccountConfig, CliConfig } from \"./config-parse.js\";\nimport { logger } from \"./logger.js\";\n\n/** 默认重启延迟(毫秒)。 */\nconst DEFAULT_RESTART_DELAY_MS = 2000;\n/** 退出轮询间隔(毫秒)。 */\nconst EXIT_POLL_MS = 100;\n/** 子进程强制退出时限(毫秒,超时未退则父进程强制退出)。 */\nconst FORCE_EXIT_MS = 5000;\n\n/** supervisor 选项。 */\nexport interface SupervisorOptions {\n /** 数据根目录。 */\n dataDir?: string;\n /** 显式账号列表(-q A -q B),优先于主配置 accounts。 */\n qqs?: string[];\n /** stub QQNT.dll 目录(自建宿主 PATH 前置,透传给子进程 --stub-dir)。 */\n stubDir?: string;\n}\n\n/** 解析账号列表(显式 -q 优先,否则主配置 accounts 过滤启用项)。 */\nfunction resolveAccounts(opts: SupervisorOptions, config: CliConfig): CliAccountConfig[] {\n if (opts.qqs !== undefined && opts.qqs.length > 0) {\n return opts.qqs.map((qq) => ({ qq }));\n }\n return config.accounts.filter((acct) => acct.enabled !== false);\n}\n\n/** 解析重启延迟(显式 -q 用默认,主配置场景用配置值)。 */\nfunction resolveRestartDelay(opts: SupervisorOptions, config: CliConfig): number {\n if (opts.qqs === undefined) {\n return config.restartDelayMs;\n }\n return DEFAULT_RESTART_DELAY_MS;\n}\n\n/** 监督上下文(startAccount / stopAll 共享,避免闭包过长)。 */\ninterface SupervisorCtx {\n dataRoot: string;\n entry: string;\n restartDelayMs: number;\n autoRestart: boolean;\n stubDir?: string;\n children: Map<string, ChildProcess>;\n isStopping: () => boolean;\n}\n\n/** 启动一个账号子进程(exit 后按 autoRestart 守护重启)。 */\nfunction startAccount(ctx: SupervisorCtx, acct: CliAccountConfig): void {\n if (ctx.isStopping()) {\n return;\n }\n logger.info({ qq: acct.qq }, \"启动账号\");\n const args = [ctx.entry, \"-q\", acct.qq, \"--data-dir\", ctx.dataRoot];\n if (ctx.stubDir !== undefined) {\n args.push(\"--stub-dir\", ctx.stubDir);\n }\n const child = spawn(process.execPath, args, {\n stdio: \"inherit\",\n });\n ctx.children.set(acct.qq, child);\n child.on(\"exit\", (code, signal) => {\n ctx.children.delete(acct.qq);\n logger.info({ qq: acct.qq, code, signal: signal ?? \"none\" }, \"账号退出\");\n if (!ctx.isStopping() && ctx.autoRestart) {\n // 守护:延迟重启(关闭信号到达前无限重试,由 SIGINT/SIGTERM 终止)\n setTimeout(() => {\n startAccount(ctx, acct);\n }, ctx.restartDelayMs);\n }\n });\n child.on(\"error\", (err) => {\n const { message } = err;\n logger.error({ qq: acct.qq, err }, `账号启动失败: ${message}`);\n ctx.children.delete(acct.qq);\n });\n}\n\n/** 停止全部子进程,全部退出后父进程退出(2026-08-07:超时强杀兜底)。 */\nfunction stopAll(ctx: SupervisorCtx): void {\n logger.info(\"收到退出信号,停止全部账号\");\n for (const child of ctx.children.values()) {\n child.kill();\n }\n // 超时强杀(2026-08-07 修复):子进程不响应 SIGTERM(自建宿主卡死等)时\n // 父进程死等 setInterval 永不退出——限时后强制退出。\n const forceTimer = setTimeout(() => {\n logger.warn(\"子进程未在限时内退出,强制退出\");\n process.exit(1);\n }, FORCE_EXIT_MS);\n const wait = setInterval(() => {\n if (ctx.children.size === 0) {\n clearInterval(wait);\n clearTimeout(forceTimer);\n process.exit(0);\n }\n }, EXIT_POLL_MS);\n}\n\n/** 运行 supervisor(多账号编排)。 */\nexport async function runSupervisor(opts: SupervisorOptions = {}): Promise<void> {\n const dataRoot = resolveDataRoot(opts.dataDir);\n const config = await loadCliConfig(dataRoot);\n const accounts = resolveAccounts(opts, config);\n if (accounts.length === 0) {\n logger.warn(\"supervisor:没有启用的账号(主配置 accounts 为空或全部禁用)\");\n return;\n }\n const restartDelayMs = resolveRestartDelay(opts, config);\n const [, entry = \"\"] = process.argv;\n const children = new Map<string, ChildProcess>();\n let stopping = false;\n const ctx: SupervisorCtx = {\n dataRoot,\n entry,\n restartDelayMs,\n autoRestart: config.autoRestart,\n ...(opts.stubDir !== undefined ? { stubDir: opts.stubDir } : {}),\n children,\n isStopping: () => stopping,\n };\n\n for (const acct of accounts) {\n startAccount(ctx, acct);\n }\n\n // 信号转发:停止全部子进程 → 父进程退出\n const shutdown = (): void => {\n if (stopping) {\n return;\n }\n stopping = true;\n stopAll(ctx);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n // 常驻:直到收到退出信号(stopAll 内 process.exit)\n await new Promise<void>(() => {\n // 永不 resolve,由 stopAll 的 process.exit 结束进程\n });\n}\n","#!/usr/bin/env node\n\n/**\n * @napuketto/cli 入口(P2-6 + P6,2026-08-05;2026-08-07 用户拍板:只保留自建宿主)\n *\n * commander 参数解析:\n * - 单账号:-q <uin> → runSingleAccount(自建宿主:标准 node + stub QQNT.dll,不拉起 QQ)\n * - 多账号:-q A -q B 或 supervisor 子命令 → runSupervisor(子进程编排)\n * - config 子命令:init / list / apply(主配置管理)\n *\n * 路线 B(拉起 QQ + 注入)已淘汰(2026-08-07 用户拍板),仅 launchQqWithLoader 历史回退。\n */\n\nimport process from \"node:process\";\nimport { resolveDataRoot } from \"@napuketto/kernel\";\nimport { Command } from \"commander\";\nimport { runSingleAccount } from \"./boot.js\";\nimport { cmdConfigApply, cmdConfigInit, cmdConfigList, loadCliConfig } from \"./config-cmds.js\";\nimport { logger } from \"./logger.js\";\nimport { runSupervisor } from \"./supervisor.js\";\n\n/** commander collect:累积 -q 多值。 */\nfunction collectQq(value: string, prev: string[]): string[] {\n prev.push(value);\n return prev;\n}\n\n/** 启动字符画(NapukettoQQ 品牌标识,内嵌;art 含反引号/反斜杠,用数组逐行转义)。 */\nconst BANNER = [\n \" _ __ __ __ __ ____ ____ \",\n \" / | / /___ _____ __ __/ /_____ / /_/ /_____ / __ \\\\/ __ \\\\\",\n \" / |/ / __ `/ __ \\\\/ / / / //_/ _ \\\\/ __/ __/ __ \\\\/ / / / / / /\",\n \" / /| / /_/ / /_/ / /_/ / ,< / __/ /_/ /_/ /_/ / /_/ / /_/ / \",\n \"/_/ |_/\\\\__,_/ .___/\\\\__,_/_/|_|\\\\___/\\\\__/\\\\__/\\\\____/\\\\___\\\\_\\\\___\\\\_\\\\ \",\n \" /_/ \",\n].join(\"\\n\");\n\n/** 统一错误输出(结构化日志,错误对象经 pino serializer 带堆栈)。 */\nfunction reportError(err: unknown): void {\n let message = String(err);\n if (err instanceof Error) {\n const { message: errMessage } = err;\n message = errMessage;\n }\n logger.error({ err }, message);\n}\n\n/** 构造可选 dataDir 参数(exactOptionalPropertyTypes 不允许显式 undefined)。 */\nfunction withDataDir(dataDir: string | undefined): { dataDir?: string } {\n if (dataDir === undefined) {\n return {};\n }\n return { dataDir };\n}\n\n/** 读主命令已解析的 data-dir(-d 定义在主命令,子命令经此共享)。 */\nfunction rootDataDir(program: Command): { dataDir?: string } {\n return withDataDir(program.opts<{ dataDir?: string }>().dataDir);\n}\n\n/** 注册 config 子命令(init/list/apply,P6;-d 复用主命令 option)。 */\nfunction registerConfigCommands(program: Command): void {\n const configCmd = program.command(\"config\").description(\"全局配置管理(napuketto.toml)\");\n configCmd\n .command(\"init\")\n .description(\"生成默认全局配置\")\n .action(async () => {\n try {\n await cmdConfigInit(rootDataDir(program));\n } catch (err) {\n reportError(err);\n process.exitCode = 1;\n }\n });\n configCmd\n .command(\"list\")\n .description(\"列出全局配置与账号配置\")\n .action(async () => {\n try {\n await cmdConfigList(rootDataDir(program));\n } catch (err) {\n reportError(err);\n process.exitCode = 1;\n }\n });\n configCmd\n .command(\"apply <file>\")\n .description(\"应用外部配置(TOML/JSON,校验后写回全局配置)\")\n .action(async (file: string) => {\n try {\n await cmdConfigApply(file, rootDataDir(program));\n } catch (err) {\n reportError(err);\n process.exitCode = 1;\n }\n });\n}\n\n/** 注册 supervisor 子命令(读主配置 accounts 批量拉起,P6;-d 复用主命令 option)。 */\nfunction registerSupervisorCommand(program: Command): void {\n program\n .command(\"supervisor\")\n .description(\"多账号编排(读全局配置 accounts 拉起子进程)\")\n .action(async () => {\n try {\n await runSupervisor(rootDataDir(program));\n } catch (err) {\n reportError(err);\n process.exitCode = 1;\n }\n });\n}\n\n/** 单账号启动分支(-q 单值;构造 bootOptions 后走 runSingleAccount)。 */\nasync function runSingleAccountBranch(\n opts: { dataDir?: string; qqPath?: string; stubDir?: string },\n qqs: string[],\n): Promise<void> {\n const [only] = qqs;\n if (only === undefined) {\n return;\n }\n const bootOptions: {\n qq?: string;\n dataDir?: string;\n qqPath?: string;\n stubDir?: string;\n } = { qq: only };\n if (opts.dataDir !== undefined) {\n bootOptions.dataDir = opts.dataDir;\n }\n if (opts.qqPath !== undefined) {\n bootOptions.qqPath = opts.qqPath;\n }\n if (opts.stubDir !== undefined) {\n bootOptions.stubDir = opts.stubDir;\n }\n await runSingleAccount(bootOptions);\n}\n\n/** 无 -q 时:读全局配置 accounts → 有则 supervisor 拉起;无则单账号启动(自建宿主自动快速登录/QR)。 */\nasync function autoStart(opts: { dataDir?: string; stubDir?: string }): Promise<void> {\n try {\n const dataRoot = resolveDataRoot(opts.dataDir);\n const config = await loadCliConfig(dataRoot);\n if (config.accounts.length > 0) {\n await runSupervisor({\n ...(opts.dataDir !== undefined ? { dataDir: opts.dataDir } : {}),\n ...(opts.stubDir !== undefined ? { stubDir: opts.stubDir } : {}),\n });\n return;\n }\n } catch {\n // 配置读取失败忽略,直接单账号启动\n }\n const bootOptions: { dataDir?: string; stubDir?: string } = {};\n if (opts.dataDir !== undefined) {\n bootOptions.dataDir = opts.dataDir;\n }\n if (opts.stubDir !== undefined) {\n bootOptions.stubDir = opts.stubDir;\n }\n logger.info(\"未指定 -q,自动快速登录(无历史账号则二维码登录)\");\n await runSingleAccount(bootOptions);\n}\n\n/** 注册主命令 action(-q 单账号 / 多 -q supervisor / 无 -q 自动读配置或单账号启动)。 */\nfunction registerMainAction(program: Command): void {\n program.action(\n async (opts: { qq?: string[]; dataDir?: string; qqPath?: string; stubDir?: string }) => {\n const qqs = opts.qq ?? [];\n if (qqs.length === 0) {\n await autoStart(opts);\n return;\n }\n try {\n if (qqs.length === 1) {\n await runSingleAccountBranch(opts, qqs);\n } else {\n const supOpts: { dataDir?: string; qqs: string[]; stubDir?: string } = { qqs };\n if (opts.dataDir !== undefined) {\n supOpts.dataDir = opts.dataDir;\n }\n if (opts.stubDir !== undefined) {\n supOpts.stubDir = opts.stubDir;\n }\n await runSupervisor(supOpts);\n }\n } catch (err) {\n reportError(err);\n process.exitCode = 1;\n }\n },\n );\n}\n\n/** 入口:注册全部子命令与主命令后解析。 */\nfunction main(): void {\n // 启动字符画(每次 cli 启动打印)\n process.stdout.write(`${BANNER}\\n`);\n const program = new Command();\n program\n .name(\"napuketto\")\n .version(\"0.0.1\")\n .description(\"NapukettoQQ 机器人框架(OneBot 11 兼容)\");\n\n program\n .option(\n \"-q, --qq <uin>\",\n \"QQ 号(可重复;多个时 supervisor 拉起多账号子进程)\",\n collectQq,\n [],\n )\n .option(\"-d, --data-dir <dir>\", \"数据根目录(缺省 ~/.napuketto)\")\n .option(\"--qq-path <path>\", \"QQ 安装路径(联调覆盖)\")\n .option(\n \"--stub-dir <dir>\",\n \"stub QQNT.dll 目录(自建宿主 PATH 前置,缺省 loader/native/stub-test-env,Git Submodule)\",\n );\n\n registerConfigCommands(program);\n registerSupervisorCommand(program);\n registerMainAction(program);\n\n program.parse(process.argv);\n}\n\nmain();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["DEFAULT_RESTART_DELAY_MS"],"sources":["../src/logger.ts","../src/boot.ts","../src/config-parse.ts","../src/config-template.ts","../src/config-cmds.ts","../src/supervisor.ts","../src/index.ts"],"sourcesContent":["/**\n * cli 进程级 logger(ADR-007 日志规范,2026-08-07 统一改造)。\n *\n * 复用 kernel `createLogger`(console pretty + redact,格式与 kernel 子进程完全一致:\n * `[时间] LEVEL (name/pid): 消息` + 缩进元数据)。只打 console 不写文件——文件日志由\n * kernel 装配(NapukettoCore.create → `<数据根>/logs/napuketto.log`)负责。\n *\n * 无全局单例(ADR-015 推论):cli 每进程一份,模块级单例即可;`base.name` 用 pino\n * 保留字段 name(logger name)标注来源——pino-pretty 自动渲染为 `(cli/pid)` 元数据头,\n * 不占额外属性行,文件/JSON 侧仍保留 name 字段可过滤。\n *\n * 级别:`NAPKETTO_LOG_LEVEL` 环境变量可覆盖(缺省 info,非法值忽略)。\n */\nimport process from \"node:process\";\nimport { createLogger, type LogLevel } from \"@napuketto/kernel\";\n\n/** 合法级别集合(pino 标准级别)。 */\nconst LOG_LEVELS: readonly LogLevel[] = [\n \"trace\",\n \"debug\",\n \"info\",\n \"warn\",\n \"error\",\n \"fatal\",\n \"silent\",\n];\n\n/** 解析日志级别(env 覆盖,非法/缺失回退 info)。 */\nfunction resolveLevel(): LogLevel {\n const raw = process.env[\"NAPKETTO_LOG_LEVEL\"];\n if (raw !== undefined && (LOG_LEVELS as readonly string[]).includes(raw)) {\n return raw as LogLevel;\n }\n return \"info\";\n}\n\n/**\n * 窄接口注解(避免 TS2883:createLogger 返回 pino.Logger,cli 不直接依赖 pino,\n * declaration emit 无法命名该类型;pino.Logger 结构兼容本接口)。\n */\nexport interface CliLogger {\n info(obj: unknown, msg?: string): void;\n warn(obj: unknown, msg?: string): void;\n error(obj: unknown, msg?: string): void;\n debug(obj: unknown, msg?: string): void;\n trace(obj: unknown, msg?: string): void;\n fatal(obj: unknown, msg?: string): void;\n}\n\n/** cli 进程日志(name 渲染为 (cli/pid) 元数据头,文件 JSON 保留 name 字段可过滤)。 */\nexport const logger: CliLogger = createLogger({\n level: resolveLevel(),\n console: true,\n base: { service: \"cli\" },\n});\n","/**\n * cli boot:单账号启动序列(2026-08-07 用户拍板:只保留自建宿主)\n *\n * locate QQ(取版本/wrapper 路径)→ 解析各包 dist 入口 → launchSelfHost\n * (标准 node + stub QQNT.dll 直接 dlopen,不拉起 QQ / 不注入)→ 常驻。\n *\n * 不写业务逻辑:kernel 装配 + 登录 + 协议装配全部在 self-host.cjs → boot-bootstrap.js 完成。\n * 路线 B(拉起 QQ + 注入)已淘汰(launchQqWithLoader 仅历史回退,cli 不再调用)。\n */\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport { createInterface } from \"node:readline\";\nimport { fileURLToPath } from \"node:url\";\nimport { resolveConfigPath, resolveDataRoot } from \"@napuketto/kernel\";\nimport {\n checkInstanceLock,\n defaultStubDir,\n launchSelfHost,\n type QqInstallInfo,\n resolveQqInstall,\n} from \"@napuketto/loader\";\nimport { logger } from \"./logger.js\";\n\n/** 单账号启动选项。 */\nexport interface BootOptions {\n /** QQ 号(数据目录账号隔离,ADR-016)。 */\n qq?: string;\n /** 数据根目录(缺省环境变量/用户目录)。 */\n dataDir?: string;\n /** 覆盖 QQ 安装路径(联调)。 */\n qqPath?: string;\n /** stub QQNT.dll 目录(缺省 loader 包内闭源 submodule native/stub-test-env)。 */\n stubDir?: string;\n}\n\n/** 解析 workspace 包的 dist 入口(ESM 解析:包是 ESM-only,exports 无 require 条件)。 */\nasync function packageEntry(pkg: string): Promise<string> {\n const url = await import.meta.resolve(pkg);\n return fileURLToPath(url);\n}\n\n/**\n * 原生噪音行(wrapper.node 加载后直写 fd 的 C++ 日志,JS 层无法拦截,只能过滤):\n * - `<MMKV` / `<MemoryFile_Win32` / `<MMKV_IO`:MMKV 存储库刷屏(每次初始化打 ~6 行)\n * - `loadSymbolFromShell` / `getNodeGetJsListApi` / `get symbol failed`:\n * 标准 node 无腾讯私有符号(NodeContextifyContextMetrics 等),GetProcAddress\n * 失败的加载警告(无害,纯噪音)\n * - `loaded [mmkv.*] with N key-values`:MMKV 初始化完成行(野生日志,风格三,\n * 无统一前缀/时间戳,过滤)\n */\nconst NATIVE_NOISE =\n /<MMKV|<MemoryFile_Win32|<MMKV_IO|loadSymbolFromShell|getNodeGetJsListApi|get symbol failed|loaded \\[mmkv/i;\n\n/**\n * 逐行转发子进程输出到父进程,过滤原生噪音。\n * readline 按 UTF-8 解码 pipe 字节流,再经 process.stdout(TTY 路径,\n * WriteConsoleW UTF-16)输出——顺带修复 pino 中文在 cmd.exe/管道 936 转码\n * 链路下的乱码(原生 printf 字节流无法从 JS 侧改编码,转 pipe 后统一解码)。\n */\nfunction forwardFiltered(input: NodeJS.ReadableStream, out: NodeJS.WritableStream): void {\n const lines = createInterface({ input });\n lines.on(\"line\", (line) => {\n if (NATIVE_NOISE.test(line)) {\n return;\n }\n out.write(`${line}\\n`);\n });\n}\n\n/** 启动单个账号(自建宿主 + 常驻)。 */\nexport async function runSingleAccount(opts: BootOptions = {}): Promise<void> {\n const dataRoot = resolveDataRoot(opts.dataDir);\n const qq: QqInstallInfo = resolveQqInstall(opts.qqPath);\n const cfgDir = path.join(dataRoot, opts.qq ?? \"default\");\n\n // 单实例锁预检(2026-08-07 根治「多实例抢数据目录锁挂起」):同一账号数据\n // 目录只允许一个实例(QQ 原生层 MMKV/登录单例有锁,第二个实例抢不到会卡死)。\n // 占用 → 快速失败并提示占用 PID;残留(PID 已死)→ self-host 会自动接管。\n // 真正的锁由 self-host 子进程获取/释放(它才是数据目录的实际持有者),\n // cli 这里只做 spawn 前预检,给用户友好提示(supervisor 等批量拉起同用)。\n const { occupied, pid: holderPid } = checkInstanceLock(cfgDir);\n if (occupied) {\n logger.error(\n { pid: holderPid, dataDir: cfgDir },\n \"数据目录已被其他实例占用,拒绝启动(同一账号数据目录仅允许一个实例;\" +\n \"如确认无实例在跑,请删除该目录下的 instance.lock)\",\n );\n process.exitCode = 1;\n return;\n }\n\n const kernelEntry = await packageEntry(\"@napuketto/kernel\");\n const adapterEntry = await packageEntry(\"@napuketto/adapter\");\n const networkEntry = await packageEntry(\"@napuketto/network\");\n\n const stubDir = opts.stubDir ?? process.env[\"NAPUTO_STUB_DIR\"] ?? defaultStubDir();\n\n // 启动信息走结构化日志(时间戳 + 级别 + pid + 元数据,与 kernel 子进程格式一致)\n logger.info({ qqVersion: qq.version, qqPath: qq.qqPath }, \"QQ 安装信息\");\n logger.info({ dataDir: cfgDir }, \"数据目录\");\n logger.info(\"自建宿主引导(标准 node + stub QQNT.dll)\");\n\n // 唯一启动路径:自建宿主(2026-08-07 用户拍板,路线 B 淘汰)\n // stdio 接管 stdout/stderr:过滤 MMKV / 符号查找失败等原生噪音,其余转发\n // configPath:全局配置文件(项目根 napuketto.toml),注入 NAPKETTO_CONFIG 供装配链读取\n const { child } = launchSelfHost({\n qq,\n kernelEntry,\n adapterEntry,\n networkEntry,\n cfgDir,\n // 子进程 cwd 指向数据根:QQ 原生层 fallback 落盘(guild1.db 等)\n // 落在专门的数据目录,不污染项目根(实测 08-07)。\n cwd: dataRoot,\n configPath: resolveConfigPath({ dataRoot }),\n selfHost: true,\n stdio: [\"inherit\", \"pipe\", \"pipe\"],\n ...(opts.qq !== undefined ? { quickUin: opts.qq } : {}),\n ...(stubDir !== undefined ? { stubDir } : {}),\n });\n\n if (child.stdout !== null) {\n forwardFiltered(child.stdout, process.stdout);\n }\n if (child.stderr !== null) {\n forwardFiltered(child.stderr, process.stderr);\n }\n\n // 常驻:等待自建宿主进程退出\n await new Promise<void>((resolve) => {\n child.on(\"exit\", (code) => {\n logger.info({ code }, \"自建宿主进程退出\");\n resolve();\n });\n child.on(\"error\", (err) => {\n logger.error({ err }, \"自建宿主进程启动失败\");\n resolve();\n });\n });\n}\n","/**\n * 主配置类型与手写校验器(从 config-cmds.ts 拆分,2026-08-08 FTA 优化)\n *\n * 主配置 parse(不引入 zod):数据根 / 自动重启 / 重启延迟 / 账号列表。\n * 2026-08-08 结构拍板:**一个 QQ 账号一个 [[accounts]] 段,协议与通信配置嵌在\n * 账号内**([accounts.onebot11] / [accounts.satori]);账号必填(至少一个)。\n * 协议段为宽松对象,装配时由对应协议包的 zod schema 严格校验。\n */\nimport { kernelError, resolveDataRoot } from \"@napuketto/kernel\";\n\n/** 默认自动重启。 */\nconst DEFAULT_AUTO_RESTART = true;\n/** 默认重启延迟(毫秒)。 */\nconst DEFAULT_RESTART_DELAY_MS = 2000;\n\n/** 协议段键(宽松对象,装配时 zod 严格校验)。 */\ntype ProtocolKey = \"onebot11\" | \"satori\";\n\n/** 账号配置项(qq 必填;协议段缺省 = 该账号不启用对应协议)。 */\nexport interface CliAccountConfig {\n qq: string;\n enabled?: boolean;\n /** OneBot 11 协议段(与 ob11ConfigSchema 对应,宽松对象,装配时 zod 校验)。 */\n onebot11?: Record<string, unknown>;\n /** Satori 协议段(与 satoriConfigSchema 对应,宽松对象,装配时 zod 校验)。 */\n satori?: Record<string, unknown>;\n}\n\n/** 主配置(跨账号,全局单文件 TOML)。 */\nexport interface CliConfig {\n /** 数据根目录(绝对路径)。 */\n dataDir: string;\n /** supervisor 是否自动重启崩溃账号。 */\n autoRestart: boolean;\n /** 崩溃后重启延迟(毫秒)。 */\n restartDelayMs: number;\n /** 账号列表(至少一个,qq 必填)。 */\n accounts: CliAccountConfig[];\n}\n\n/** 解析账号内协议段(宽松对象,装配时由 zod schema 严格校验)。 */\nfunction parseProtocolSection(\n raw: Record<string, unknown>,\n key: ProtocolKey,\n): Record<string, unknown> | undefined {\n const section = raw[key];\n if (section === undefined) {\n return;\n }\n if (typeof section !== \"object\" || section === null) {\n throw kernelError(`主配置账号 ${key} 段必须是对象`, \"INVALID_PARAM\");\n }\n return section as Record<string, unknown>;\n}\n\n/**\n * 解析 dataDir(缺省用当前解析的数据根 = 项目根/.napuketto)。\n * 支持绝对路径、\"~/\" 前缀(用户主目录)、相对路径(相对启动目录),\n * 消费端 resolveDataRoot 统一展开为绝对路径。\n */\nfunction parseDataDir(raw: Record<string, unknown>): string {\n if (raw[\"dataDir\"] === undefined) {\n return resolveDataRoot();\n }\n if (typeof raw[\"dataDir\"] !== \"string\" || raw[\"dataDir\"] === \"\") {\n throw kernelError(\"主配置 dataDir 必须是非空字符串\", \"INVALID_PARAM\");\n }\n return raw[\"dataDir\"];\n}\n\n/** 解析 autoRestart。 */\nfunction parseAutoRestart(raw: Record<string, unknown>): boolean {\n if (raw[\"autoRestart\"] === undefined) {\n return DEFAULT_AUTO_RESTART;\n }\n if (typeof raw[\"autoRestart\"] !== \"boolean\") {\n throw kernelError(\"主配置 autoRestart 必须是布尔值\", \"INVALID_PARAM\");\n }\n return raw[\"autoRestart\"];\n}\n\n/** 解析 restartDelayMs(毫秒)。 */\nfunction parseRestartDelayMs(raw: Record<string, unknown>): number {\n if (raw[\"restartDelayMs\"] === undefined) {\n return DEFAULT_RESTART_DELAY_MS;\n }\n if (\n typeof raw[\"restartDelayMs\"] !== \"number\" ||\n !Number.isInteger(raw[\"restartDelayMs\"]) ||\n raw[\"restartDelayMs\"] <= 0\n ) {\n throw kernelError(\"主配置 restartDelayMs 必须是正整数\", \"INVALID_PARAM\");\n }\n return raw[\"restartDelayMs\"];\n}\n\n/** 解析 accounts(必须至少一个账号,qq 必填)。 */\nfunction parseAccounts(raw: Record<string, unknown>): CliAccountConfig[] {\n if (raw[\"accounts\"] === undefined) {\n throw kernelError(\n \"主配置缺少 accounts(必须至少一个账号);首次启动已生成模板,请编辑后重试\",\n \"INVALID_PARAM\",\n );\n }\n if (!Array.isArray(raw[\"accounts\"]) || raw[\"accounts\"].length === 0) {\n throw kernelError(\n \"主配置 accounts 必须是非空数组(至少一个 QQ 账号);首次启动已生成模板,请编辑后重试\",\n \"INVALID_PARAM\",\n );\n }\n const accounts: CliAccountConfig[] = [];\n for (const item of raw[\"accounts\"]) {\n accounts.push(parseAccount(item));\n }\n return accounts;\n}\n\n/** 解析单个账号项(qq 必填;协议段缺省 = 不启用)。 */\nfunction parseAccount(item: unknown): CliAccountConfig {\n if (typeof item !== \"object\" || item === null) {\n throw kernelError(\"主配置 accounts 元素必须是对象\", \"INVALID_PARAM\");\n }\n const { qq, enabled } = item as { qq?: unknown; enabled?: unknown };\n if (typeof qq !== \"string\" || qq === \"\") {\n throw kernelError(\"主配置账号缺少非空 qq\", \"INVALID_PARAM\");\n }\n const out: CliAccountConfig = { qq };\n if (enabled !== undefined) {\n if (typeof enabled !== \"boolean\") {\n throw kernelError(\"主配置账号 enabled 必须是布尔值\", \"INVALID_PARAM\");\n }\n out.enabled = enabled;\n }\n const raw = item as Record<string, unknown>;\n const onebot11 = parseProtocolSection(raw, \"onebot11\");\n if (onebot11 !== undefined) {\n out.onebot11 = onebot11;\n }\n const satori = parseProtocolSection(raw, \"satori\");\n if (satori !== undefined) {\n out.satori = satori;\n }\n return out;\n}\n\n/** 手写校验器:主配置 parse(不引入 zod)。 */\nexport function parseCliConfig(input: unknown): CliConfig {\n if (typeof input !== \"object\" || input === null) {\n throw kernelError(\"主配置必须是对象\", \"INVALID_PARAM\");\n }\n const raw = input as Record<string, unknown>;\n const dataDir = parseDataDir(raw);\n const autoRestart = parseAutoRestart(raw);\n const restartDelayMs = parseRestartDelayMs(raw);\n const accounts = parseAccounts(raw);\n return { dataDir, autoRestart, restartDelayMs, accounts };\n}\n","/**\n * 配置模板(TOML 注释版,从 config-cmds.ts 拆分,2026-08-08 FTA 优化)。\n * `config init` 与首次启动(loadCliConfig)缺失时生成。\n *\n * 2026-08-08 结构拍板:**一个 QQ 账号一个 [[accounts]] 段,协议与通信配置嵌在\n * 账号内**([accounts.onebot11] / [accounts.satori]);账号必填(至少一个)。\n * dataDir 不再写死(缺省 = 项目根/.napuketto,跨平台),模板保持可移植。\n */\nexport function configTemplate(): string {\n return `# ============================================================\n# NapukettoQQ 全局配置文件\n# ============================================================\n# 本文件由 \\`napuketto config init\\` 生成(或首次启动自动生成)。\n# 本机配置不入库(.gitignore);修改后重启生效。\n#\n# 配置组织:一个 QQ 账号一个 [[accounts]] 段,协议与通信配置嵌在账号内\n# ([accounts.onebot11] / [accounts.satori])。账号没有写某协议段 = 该账号\n# 不启用该协议。账号必填(至少一个,qq 必填)。\n\n# ------------------------------------------------------------\n# 全局配置(跨账号)\n# ------------------------------------------------------------\n\n# 数据根目录:账号目录/日志/缓存/QQ 数据存放位置。\n# 缺省 = <项目根>/.napuketto(部署场景,跨平台,无需修改)。\n# 支持绝对路径、\"~/\" 前缀(用户主目录)、相对路径(相对启动目录)。\n# dataDir = \".napuketto\"\n\n# 多账号(supervisor)模式下,账号进程崩溃是否自动重启\nautoRestart = true\n\n# 崩溃后自动重启的延迟(毫秒)\nrestartDelayMs = 2000\n\n# ------------------------------------------------------------\n# 账号(至少一个,qq 必填)\n# ------------------------------------------------------------\n\n[[accounts]]\nqq = \"123456\" # QQ 号(必填)\nenabled = true # 是否启用(可选,缺省 true)\n\n[accounts.onebot11] # 该账号的 OneBot 11 配置(无此段 = 不启用 OB11)\n\n# 鉴权 token(全局默认):HTTP/WS 连接必须携带。\n# 校验方式:Authorization: Bearer <token> / 裸 token / URL 参数 ?access_token=<token>\n# 每个网络实例可单独覆盖(见下文各实例的 token 字段)。\n# 留空 = 不鉴权(仅本机调试推荐)。\n# token = \"\"\n\n# 心跳 meta 事件间隔(毫秒);0 = 关闭心跳事件\nheartbeatInterval = 3000\n\n# 是否上报机器人自己发的消息(OB11 规范默认不上报)\nreportSelfMessage = false\n\n# 消息内容格式:array = 消息段数组(标准 OneBot 11),string = CQ 码字符串\nmessagePostFormat = \"array\"\n\n# ---- HTTP 反向服务器(接收第三方 POST 指令),可配置多个 ----\n[[accounts.onebot11.httpServers]]\nenabled = true\nhost = \"127.0.0.1\" # 监听地址;0.0.0.0 = 监听所有网卡(局域网可连)\nport = 3000\n# token = \"abc\" # 可选:覆盖全局 token\n\n# ---- HTTP 正向上报(Webhook:把事件 POST 到第三方地址),可配置多个 ----\n# [[accounts.onebot11.httpPostUrls]]\n# enabled = true\n# url = \"http://127.0.0.1:8080/onebot\" # 第三方接收地址\n# token = \"abc\" # 可选:覆盖全局 token\n# timeoutMs = 5000 # 可选:上报超时(毫秒),缺省不超时\n\n# ---- WS 反向服务器(外部 WS 客户端主动连入),可配置多个 ----\n[[accounts.onebot11.wsServers]]\nenabled = true\nhost = \"127.0.0.1\"\nport = 3001\n# token = \"abc\" # 可选:覆盖全局 token\n# heartbeatInterval = 30000 # 可选:WS ping 间隔(毫秒)\n\n# ---- WS 正向客户端(主动连接外部 WS,双向),可配置多个 ----\n# [[accounts.onebot11.wsReverseUrls]]\n# enabled = true\n# url = \"ws://127.0.0.1:8081/ws\" # 第三方 WS 地址(ws:// 或 wss://)\n# token = \"abc\" # 可选:覆盖全局 token\n# reconnectDelayMs = 5000 # 可选:断线重连延迟(毫秒)\n# maxReconnectAttempts = 10 # 可选:最大重连次数(缺省无限)\n# rejectUnauthorized = true # 可选:wss:// 是否校验证书(自签证书设 false)\n# heartbeatInterval = 30000 # 可选:WS ping 间隔(毫秒)\n\n# 第二个账号示例(只开 Satori):\n# [[accounts]]\n# qq = \"654321\"\n# [accounts.satori]\n# # token = \"\" # 可选:Satori 鉴权 token\n# [[accounts.satori.httpServers]] # HTTP RPC(POST /v1/{resource}.{method})\n# enabled = true\n# host = \"127.0.0.1\"\n# port = 5500\n# [[accounts.satori.wsServers]] # WS 事件服务(/v1/events)\n# enabled = true\n# host = \"127.0.0.1\"\n# port = 5501\n`;\n}\n","/**\n * cli config 子命令:init / list / apply(P6,2026-08-05;2026-08-07 配置文件移到项目根;\n * 2026-08-08 结构拍板:账号内嵌协议段)\n *\n * **全局单配置文件**:`<项目根>/napuketto.toml`(2026-08-07 用户拍板:配置文件放项目根,\n * 数据仍按数据根组织),本项目所有配置都在里面管理(TOML + smol-toml,配合 zod 校验,\n * 用户 2026-08-05 拍板:JSON 门槛太高)。本机配置不入库(.gitignore)。\n *\n * 2026-08-08 结构(用户拍板):**一个 QQ 账号一个 [[accounts]] 段,协议与通信配置嵌在\n * 账号内**,账号必填(至少一个,qq 必填):\n *\n * # dataDir = \".napuketto\" # 可选;缺省 = 项目根/.napuketto(跨平台)\n * autoRestart = true\n * restartDelayMs = 2000\n * [[accounts]]\n * qq = \"123456\" # QQ 号(必填)\n * enabled = true\n * [accounts.onebot11] # 该账号的 OB11 段(无 = 不启用)\n * token = \"\"\n * [[accounts.onebot11.httpServers]]\n * enabled = true\n * port = 3000\n * [accounts.satori] # 该账号的 Satori 段(无 = 不启用)\n * [[accounts.satori.httpServers]]\n * enabled = true\n * port = 5500\n *\n * 配置文件路径解析见 kernel `resolveConfigPath`(NAPKETTO_CONFIG 显式 > 项目根探测 > 数据根兜底);\n * 校验器为手写 parse(适配 kernel ConfigBase 的 ConfigSchema 形状);\n * 协议段由对应协议包的 zod schema 校验(boot 装配时按登录账号 uin 从 accounts 取段作 seed)。\n */\nimport type { Dirent } from \"node:fs\";\nimport { mkdirSync, readdirSync } from \"node:fs\";\nimport { access, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, extname, join } from \"node:path\";\nimport process from \"node:process\";\nimport type { ConfigSchema } from \"@napuketto/kernel\";\nimport {\n ConfigBase,\n kernelError,\n parseToml,\n resolveConfigPath,\n resolveDataRoot,\n stringifyToml,\n} from \"@napuketto/kernel\";\nimport { type CliConfig, parseCliConfig } from \"./config-parse.js\";\nimport { configTemplate } from \"./config-template.js\";\nimport { logger } from \"./logger.js\";\n\n/** 主配置默认值(dataRoot 为当前解析的数据根 = 项目根/.napuketto)。 */\nfunction defaultCliConfig(dataRoot: string): CliConfig {\n return {\n dataDir: dataRoot,\n autoRestart: true,\n restartDelayMs: 2000,\n accounts: [],\n };\n}\n\n/** 配置文件缺失 → 生成带注释模板(首次启动自动生成默认配置)。 */\nasync function ensureConfigTemplate(path: string): Promise<void> {\n try {\n await access(path);\n } catch {\n mkdirSync(dirname(path), { recursive: true });\n await writeFile(path, `${configTemplate()}\\n`, \"utf8\");\n logger.info({ path }, \"配置文件不存在,已生成默认模板(请编辑 accounts 段填入 QQ 号)\");\n }\n}\n\n/** 主配置 store(读/写/校验;.toml 后缀 → smol-toml 序列化)。\n * 配置文件在项目根(resolveConfigPath),dataRoot 仅作兜底与 dataDir 默认值。 */\nclass CliConfigStore extends ConfigBase<CliConfig> {\n constructor(dataRoot: string) {\n super({\n path: resolveConfigPath({ dataRoot }),\n schema: { parse: parseCliConfig } satisfies ConfigSchema<CliConfig>,\n defaults: defaultCliConfig(dataRoot),\n });\n }\n}\n\n/** 读取主配置(缺失自动落模板)。 */\nexport async function loadCliConfig(dataRoot: string): Promise<CliConfig> {\n const store = new CliConfigStore(dataRoot);\n await ensureConfigTemplate(store.path);\n return await store.load();\n}\n\n/** CliConfig → 普通对象(smol-toml stringify 入参)。 */\nfunction toRecord(config: CliConfig): Record<string, unknown> {\n return config as unknown as Record<string, unknown>;\n}\n\n/** config init:生成全局 TOML 配置文件(项目根)+ 数据根目录。\n * 文件不存在 → 写带注释模板;已存在 → 跳过(不覆盖用户配置)。\n * 输出原始模板内容(账号未填时解析会校验失败,init 只负责生成不校验)。 */\nexport async function cmdConfigInit(opts: { dataDir?: string }): Promise<void> {\n const dataRoot = resolveDataRoot(opts.dataDir);\n mkdirSync(dataRoot, { recursive: true });\n const store = new CliConfigStore(dataRoot);\n let created = false;\n try {\n await access(store.path);\n } catch {\n mkdirSync(dirname(store.path), { recursive: true });\n await writeFile(store.path, `${configTemplate()}\\n`, \"utf8\");\n created = true;\n }\n const raw = await readFile(store.path, \"utf8\");\n logger.info({ dataRoot }, \"数据根\");\n if (created) {\n logger.info({ path: store.path }, \"全局配置已生成(请编辑 accounts 段填入 QQ 号)\");\n } else {\n logger.info({ path: store.path }, \"全局配置已存在(跳过生成,避免覆盖用户配置)\");\n }\n // TOML 内容为功能输出(机器可读),保持原样 stdout,不做日志包装\n process.stdout.write(raw);\n}\n\n/** config list:列出全局配置与各账号目录。 */\nexport async function cmdConfigList(opts: { dataDir?: string }): Promise<void> {\n const dataRoot = resolveDataRoot(opts.dataDir);\n const store = new CliConfigStore(dataRoot);\n const config = await store.load();\n logger.info({ dataRoot }, \"数据根\");\n logger.info({ path: store.path }, \"全局配置\");\n process.stdout.write(`${stringifyToml(toRecord(config))}\\n`);\n // 账号目录(数据根下的子目录)\n let entries: Dirent[];\n try {\n entries = readdirSync(dataRoot, { withFileTypes: true });\n } catch {\n entries = [];\n }\n for (const entry of entries) {\n if (entry.isDirectory()) {\n printAccountConfig(entry.name, dataRoot);\n }\n }\n}\n\n/** 打印账号 config 目录文件清单。 */\nfunction printAccountConfig(account: string, dataRoot: string): void {\n const configDir = join(dataRoot, account, \"config\");\n let files: string[] = [];\n try {\n files = readdirSync(configDir).filter((f) => f.endsWith(\".json\") || f.endsWith(\".toml\"));\n } catch {\n files = [];\n }\n let fileList = \"(未初始化)\";\n if (files.length > 0) {\n fileList = files.join(\", \");\n }\n // 注意:字段名用 account 而非 name——name 是 pino 保留键(logger name),\n // 会被 pino-pretty 渲染成 `(name/pid)` 元数据头而非普通字段。\n logger.info({ account, files: fileList }, \"账号配置\");\n}\n\n/** 解析外部配置文本(按扩展名推断 TOML/JSON)。 */\nfunction parseExternalConfig(file: string, rawText: string): unknown {\n if (extname(file).toLowerCase() === \".toml\") {\n return parseToml(rawText);\n }\n try {\n return JSON.parse(rawText) as unknown;\n } catch {\n throw kernelError(`外部配置 ${file} 不是合法 JSON`, \"INVALID_PARAM\");\n }\n}\n\n/** config apply:应用外部配置(顶层覆盖合并后写回全局配置)。 */\nexport async function cmdConfigApply(file: string, opts: { dataDir?: string }): Promise<void> {\n const dataRoot = resolveDataRoot(opts.dataDir);\n const store = new CliConfigStore(dataRoot);\n const existing = await store.load();\n let rawText: string;\n try {\n rawText = await readFile(file, \"utf8\");\n } catch (err) {\n let message = String(err);\n if (err instanceof Error) {\n const { message: errMessage } = err;\n message = errMessage;\n }\n throw kernelError(`读取外部配置失败 ${file}: ${message}`, \"INVALID_PARAM\", { cause: err });\n }\n const partial = parseExternalConfig(file, rawText);\n if (typeof partial !== \"object\" || partial === null) {\n throw kernelError(\"外部配置必须是对象\", \"INVALID_PARAM\");\n }\n const patch = partial as Record<string, unknown>;\n const merged: Record<string, unknown> = { ...existing, ...patch };\n const next = parseCliConfig(merged);\n await store.save(next);\n logger.info({ path: store.path }, \"全局配置已更新\");\n // TOML 内容为功能输出(机器可读),保持原样 stdout,不做日志包装\n process.stdout.write(`${stringifyToml(toRecord(next))}\\n`);\n}\n","/**\n * supervisor:多账号子进程编排(P6,2026-08-05;2026-08-07 自建宿主唯一)\n *\n * 多账号走多进程(ADR-015):每账号一个独立子进程(node <入口> -q <uin>\n * --data-dir <dataRoot> [--stub-dir <dir>],复用单账号 runSingleAccount 零改动),\n * 本模块作为父进程:\n * - 拉起:主配置 accounts(supervisor 子命令)或显式 -q 列表\n * - 守护:账号异常退出且 autoRestart → 延迟重启\n * - 信号转发:SIGINT/SIGTERM → 停止全部子进程 → 父进程退出\n */\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport process from \"node:process\";\nimport { resolveDataRoot } from \"@napuketto/kernel\";\nimport { loadCliConfig } from \"./config-cmds.js\";\nimport type { CliAccountConfig, CliConfig } from \"./config-parse.js\";\nimport { logger } from \"./logger.js\";\n\n/** 默认重启延迟(毫秒)。 */\nconst DEFAULT_RESTART_DELAY_MS = 2000;\n/** 退出轮询间隔(毫秒)。 */\nconst EXIT_POLL_MS = 100;\n/** 子进程强制退出时限(毫秒,超时未退则父进程强制退出)。 */\nconst FORCE_EXIT_MS = 5000;\n\n/** supervisor 选项。 */\nexport interface SupervisorOptions {\n /** 数据根目录。 */\n dataDir?: string;\n /** 显式账号列表(-q A -q B),优先于主配置 accounts。 */\n qqs?: string[];\n /** stub QQNT.dll 目录(自建宿主 PATH 前置,透传给子进程 --stub-dir)。 */\n stubDir?: string;\n}\n\n/** 解析账号列表(显式 -q 优先,否则主配置 accounts 过滤启用项)。 */\nfunction resolveAccounts(opts: SupervisorOptions, config: CliConfig): CliAccountConfig[] {\n if (opts.qqs !== undefined && opts.qqs.length > 0) {\n return opts.qqs.map((qq) => ({ qq }));\n }\n return config.accounts.filter((acct) => acct.enabled !== false);\n}\n\n/** 解析重启延迟(显式 -q 用默认,主配置场景用配置值)。 */\nfunction resolveRestartDelay(opts: SupervisorOptions, config: CliConfig): number {\n if (opts.qqs === undefined) {\n return config.restartDelayMs;\n }\n return DEFAULT_RESTART_DELAY_MS;\n}\n\n/** 监督上下文(startAccount / stopAll 共享,避免闭包过长)。 */\ninterface SupervisorCtx {\n dataRoot: string;\n entry: string;\n restartDelayMs: number;\n autoRestart: boolean;\n stubDir?: string;\n children: Map<string, ChildProcess>;\n isStopping: () => boolean;\n}\n\n/** 启动一个账号子进程(exit 后按 autoRestart 守护重启)。 */\nfunction startAccount(ctx: SupervisorCtx, acct: CliAccountConfig): void {\n if (ctx.isStopping()) {\n return;\n }\n logger.info({ qq: acct.qq }, \"启动账号\");\n const args = [ctx.entry, \"-q\", acct.qq, \"--data-dir\", ctx.dataRoot];\n if (ctx.stubDir !== undefined) {\n args.push(\"--stub-dir\", ctx.stubDir);\n }\n const child = spawn(process.execPath, args, {\n stdio: \"inherit\",\n });\n ctx.children.set(acct.qq, child);\n child.on(\"exit\", (code, signal) => {\n ctx.children.delete(acct.qq);\n logger.info({ qq: acct.qq, code, signal: signal ?? \"none\" }, \"账号退出\");\n if (!ctx.isStopping() && ctx.autoRestart) {\n // 守护:延迟重启(关闭信号到达前无限重试,由 SIGINT/SIGTERM 终止)\n setTimeout(() => {\n startAccount(ctx, acct);\n }, ctx.restartDelayMs);\n }\n });\n child.on(\"error\", (err) => {\n const { message } = err;\n logger.error({ qq: acct.qq, err }, `账号启动失败: ${message}`);\n ctx.children.delete(acct.qq);\n });\n}\n\n/** 停止全部子进程,全部退出后父进程退出(2026-08-07:超时强杀兜底)。 */\nfunction stopAll(ctx: SupervisorCtx): void {\n logger.info(\"收到退出信号,停止全部账号\");\n for (const child of ctx.children.values()) {\n child.kill();\n }\n // 超时强杀(2026-08-07 修复):子进程不响应 SIGTERM(自建宿主卡死等)时\n // 父进程死等 setInterval 永不退出——限时后强制退出。\n const forceTimer = setTimeout(() => {\n logger.warn(\"子进程未在限时内退出,强制退出\");\n process.exit(1);\n }, FORCE_EXIT_MS);\n const wait = setInterval(() => {\n if (ctx.children.size === 0) {\n clearInterval(wait);\n clearTimeout(forceTimer);\n process.exit(0);\n }\n }, EXIT_POLL_MS);\n}\n\n/** 运行 supervisor(多账号编排)。 */\nexport async function runSupervisor(opts: SupervisorOptions = {}): Promise<void> {\n const dataRoot = resolveDataRoot(opts.dataDir);\n const config = await loadCliConfig(dataRoot);\n const accounts = resolveAccounts(opts, config);\n if (accounts.length === 0) {\n logger.warn(\"supervisor:没有启用的账号(主配置 accounts 为空或全部禁用)\");\n return;\n }\n const restartDelayMs = resolveRestartDelay(opts, config);\n const [, entry = \"\"] = process.argv;\n const children = new Map<string, ChildProcess>();\n let stopping = false;\n const ctx: SupervisorCtx = {\n dataRoot,\n entry,\n restartDelayMs,\n autoRestart: config.autoRestart,\n ...(opts.stubDir !== undefined ? { stubDir: opts.stubDir } : {}),\n children,\n isStopping: () => stopping,\n };\n\n for (const acct of accounts) {\n startAccount(ctx, acct);\n }\n\n // 信号转发:停止全部子进程 → 父进程退出\n const shutdown = (): void => {\n if (stopping) {\n return;\n }\n stopping = true;\n stopAll(ctx);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n // 常驻:直到收到退出信号(stopAll 内 process.exit)\n await new Promise<void>(() => {\n // 永不 resolve,由 stopAll 的 process.exit 结束进程\n });\n}\n","#!/usr/bin/env node\n\n/**\n * @napuketto/cli 入口(P2-6 + P6,2026-08-05;2026-08-07 用户拍板:只保留自建宿主;\n * 2026-08-08 配置结构拍板:账号必填,协议配置嵌在账号内)\n *\n * commander 参数解析:\n * - 单账号:-q <uin> → runSingleAccount(自建宿主:标准 node + stub QQNT.dll,不拉起 QQ)\n * - 多账号:-q A -q B 或 supervisor 子命令 → runSupervisor(子进程编排)\n * - 无 -q:读全局配置 accounts(至少一个,账号必填)→ supervisor 拉起全部启用账号\n * - config 子命令:init / list / apply(主配置管理)\n *\n * 路线 B(拉起 QQ + 注入)已淘汰(2026-08-07 用户拍板),仅 launchQqWithLoader 历史回退。\n */\n\nimport process from \"node:process\";\nimport { resolveDataRoot } from \"@napuketto/kernel\";\nimport { Command } from \"commander\";\nimport { runSingleAccount } from \"./boot.js\";\nimport { cmdConfigApply, cmdConfigInit, cmdConfigList, loadCliConfig } from \"./config-cmds.js\";\nimport { logger } from \"./logger.js\";\nimport { runSupervisor } from \"./supervisor.js\";\n\n/** commander collect:累积 -q 多值。 */\nfunction collectQq(value: string, prev: string[]): string[] {\n prev.push(value);\n return prev;\n}\n\n/** 启动字符画(NapukettoQQ 品牌标识,内嵌;art 含反引号/反斜杠,用数组逐行转义)。 */\nconst BANNER = [\n \" _ __ __ __ __ ____ ____ \",\n \" / | / /___ _____ __ __/ /_____ / /_/ /_____ / __ \\\\/ __ \\\\\",\n \" / |/ / __ `/ __ \\\\/ / / / //_/ _ \\\\/ __/ __/ __ \\\\/ / / / / / /\",\n \" / /| / /_/ / /_/ / /_/ / ,< / __/ /_/ /_/ /_/ / /_/ / /_/ / \",\n \"/_/ |_/\\\\__,_/ .___/\\\\__,_/_/|_|\\\\___/\\\\__/\\\\__/\\\\____/\\\\___\\\\_\\\\___\\\\_\\\\ \",\n \" /_/ \",\n].join(\"\\n\");\n\n/** 统一错误输出(结构化日志,错误对象经 pino serializer 带堆栈)。 */\nfunction reportError(err: unknown): void {\n let message = String(err);\n if (err instanceof Error) {\n const { message: errMessage } = err;\n message = errMessage;\n }\n logger.error({ err }, message);\n}\n\n/** 构造可选 dataDir 参数(exactOptionalPropertyTypes 不允许显式 undefined)。 */\nfunction withDataDir(dataDir: string | undefined): { dataDir?: string } {\n if (dataDir === undefined) {\n return {};\n }\n return { dataDir };\n}\n\n/** 读主命令已解析的 data-dir(-d 定义在主命令,子命令经此共享)。 */\nfunction rootDataDir(program: Command): { dataDir?: string } {\n return withDataDir(program.opts<{ dataDir?: string }>().dataDir);\n}\n\n/** 注册 config 子命令(init/list/apply,P6;-d 复用主命令 option)。 */\nfunction registerConfigCommands(program: Command): void {\n const configCmd = program.command(\"config\").description(\"全局配置管理(napuketto.toml)\");\n configCmd\n .command(\"init\")\n .description(\"生成默认全局配置\")\n .action(async () => {\n try {\n await cmdConfigInit(rootDataDir(program));\n } catch (err) {\n reportError(err);\n process.exitCode = 1;\n }\n });\n configCmd\n .command(\"list\")\n .description(\"列出全局配置与账号配置\")\n .action(async () => {\n try {\n await cmdConfigList(rootDataDir(program));\n } catch (err) {\n reportError(err);\n process.exitCode = 1;\n }\n });\n configCmd\n .command(\"apply <file>\")\n .description(\"应用外部配置(TOML/JSON,校验后写回全局配置)\")\n .action(async (file: string) => {\n try {\n await cmdConfigApply(file, rootDataDir(program));\n } catch (err) {\n reportError(err);\n process.exitCode = 1;\n }\n });\n}\n\n/** 注册 supervisor 子命令(读主配置 accounts 批量拉起,P6;-d 复用主命令 option)。 */\nfunction registerSupervisorCommand(program: Command): void {\n program\n .command(\"supervisor\")\n .description(\"多账号编排(读全局配置 accounts 拉起子进程)\")\n .action(async () => {\n try {\n await runSupervisor(rootDataDir(program));\n } catch (err) {\n reportError(err);\n process.exitCode = 1;\n }\n });\n}\n\n/** 单账号启动分支(-q 单值;构造 bootOptions 后走 runSingleAccount)。 */\nasync function runSingleAccountBranch(\n opts: { dataDir?: string; qqPath?: string; stubDir?: string },\n qqs: string[],\n): Promise<void> {\n const [only] = qqs;\n if (only === undefined) {\n return;\n }\n const bootOptions: {\n qq?: string;\n dataDir?: string;\n qqPath?: string;\n stubDir?: string;\n } = { qq: only };\n if (opts.dataDir !== undefined) {\n bootOptions.dataDir = opts.dataDir;\n }\n if (opts.qqPath !== undefined) {\n bootOptions.qqPath = opts.qqPath;\n }\n if (opts.stubDir !== undefined) {\n bootOptions.stubDir = opts.stubDir;\n }\n await runSingleAccount(bootOptions);\n}\n\n/** 无 -q 时:读全局配置 accounts(账号必填,2026-08-08 拍板),supervisor 拉起全部启用账号。\n * 不再有「配置为空 → 交互式单账号登录」老路径:缺账号直接报错提示编辑模板。 */\nasync function autoStart(opts: { dataDir?: string; stubDir?: string }): Promise<void> {\n const dataRoot = resolveDataRoot(opts.dataDir);\n const config = await loadCliConfig(dataRoot);\n if (config.accounts.length === 0) {\n // parseCliConfig 已强制至少一个,此处防御\n throw new Error(\"主配置没有账号,请编辑 napuketto.toml 填写 [[accounts]] 段\");\n }\n await runSupervisor({\n ...(opts.dataDir !== undefined ? { dataDir: opts.dataDir } : {}),\n ...(opts.stubDir !== undefined ? { stubDir: opts.stubDir } : {}),\n });\n}\n\n/** 注册主命令 action(-q 单账号 / 多 -q supervisor / 无 -q 读配置 supervisor 拉起)。 */\nfunction registerMainAction(program: Command): void {\n program.action(\n async (opts: { qq?: string[]; dataDir?: string; qqPath?: string; stubDir?: string }) => {\n try {\n const qqs = opts.qq ?? [];\n if (qqs.length === 0) {\n await autoStart(opts);\n return;\n }\n if (qqs.length === 1) {\n await runSingleAccountBranch(opts, qqs);\n } else {\n const supOpts: { dataDir?: string; qqs: string[]; stubDir?: string } = { qqs };\n if (opts.dataDir !== undefined) {\n supOpts.dataDir = opts.dataDir;\n }\n if (opts.stubDir !== undefined) {\n supOpts.stubDir = opts.stubDir;\n }\n await runSupervisor(supOpts);\n }\n } catch (err) {\n reportError(err);\n process.exitCode = 1;\n }\n },\n );\n}\n\n/** 入口:注册全部子命令与主命令后解析。 */\nfunction main(): void {\n // 启动字符画(每次 cli 启动打印)\n process.stdout.write(`${BANNER}\\n`);\n const program = new Command();\n program\n .name(\"napuketto\")\n .version(\"0.0.1\")\n .description(\"NapukettoQQ 机器人框架(OneBot 11 兼容)\");\n\n program\n .option(\n \"-q, --qq <uin>\",\n \"QQ 号(可重复;多个时 supervisor 拉起多账号子进程)\",\n collectQq,\n [],\n )\n .option(\"-d, --data-dir <dir>\", \"数据根目录(缺省 <项目根>/.napuketto)\")\n .option(\"--qq-path <path>\", \"QQ 安装路径(联调覆盖)\")\n .option(\n \"--stub-dir <dir>\",\n \"stub QQNT.dll 目录(自建宿主 PATH 前置,缺省 loader/native/stub-test-env,Git Submodule)\",\n );\n\n registerConfigCommands(program);\n registerSupervisorCommand(program);\n registerMainAction(program);\n\n program.parse(process.argv);\n}\n\nmain();\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@napuketto/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Command line interface for NapukettoQQ",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
"commander": "^15.0.0",
|
|
27
27
|
"picocolors": "^1.1.1",
|
|
28
28
|
"qrcode": "^1.5.4",
|
|
29
|
-
"@napuketto/kernel": "0.0.
|
|
29
|
+
"@napuketto/kernel": "0.0.2",
|
|
30
|
+
"@napuketto/loader": "0.0.3",
|
|
30
31
|
"@napuketto/network": "0.0.1",
|
|
31
|
-
"@napuketto/
|
|
32
|
-
"@napuketto/adapter": "0.0.2"
|
|
32
|
+
"@napuketto/adapter": "0.0.3"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"dev": "tsdown --watch",
|