@sfmc-bds/cli 0.2.3 → 0.2.5

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/services.ts"],
4
- "sourcesContent": ["/**\n * services.ts \u2014 CLI \u8FDB\u7A0B\u7F16\u6392\u4E0E\u670D\u52A1\u751F\u547D\u5468\u671F\u7BA1\u7406\n *\n * \u6838\u5FC3\u7BA1\u7406\u5BF9\u8C61\uFF08ServiceName\uFF09\uFF1A\n * - `bds`\uFF1AMinecraft \u57FA\u5CA9\u7248\u670D\u52A1\u7AEF\uFF08\u652F\u6301 stdin \u7BA1\u9053\u6307\u4EE4\u4EA4\u4E92\u4E0E\u4F18\u96C5\u505C\u670D\uFF09\n * - `db`\uFF1Adb-server SQLite HTTP \u670D\u52A1\uFF08\u7AEF\u53E3\u5065\u5EB7\u68C0\u67E5\u4E0E loopback \u63A2\u6D3B\uFF09\n * - `tunnel`\uFF1A\u4E91\u7AEF QQ Webhook \u5230\u672C\u673A db-server \u7684 SSH \u53CD\u5411\u96A7\u9053\n * - `qq`\uFF1Aqq-bridge \u6D88\u606F\u7F51\u6865\u670D\u52A1\uFF08\u652F\u6301\u5B98\u65B9 Bot \u4E0E LLBot \u53CC\u540E\u7AEF\uFF09\n * - `llbot`\uFF1ALLBot \u72EC\u7ACB\u673A\u5668\u4EBA\u8FDB\u7A0B\uFF08\u6761\u4EF6\u542F\u52A8\uFF09\n *\n * \u6838\u5FC3\u80FD\u529B\uFF1A\n * - \u8FDB\u7A0B\u751F\u547D\u5468\u671F\uFF1A\u6309\u4F9D\u8D56\u6B21\u5E8F\u7F16\u6392\u670D\u52A1\u542F\u52A8\uFF08db \u2192 qq \u2192 bds\uFF09\u3001\u4F18\u96C5\u505C\u6B62\u4E0E\u5F02\u5E38\u81EA\u52A8\u62C9\u8D77\n * - \u591A\u6E90\u63A2\u6D3B\u611F\u77E5\uFF1A\u81EA\u52A8\u63A2\u6D4B\u6258\u7BA1\u8FDB\u7A0B\u4E0E\u5916\u90E8\u72EC\u7ACB\u8FDB\u7A0B\u72B6\u6001\n * - \u65E5\u5FD7\u6D41\u7EDF\u4E00\u805A\u5408\uFF1A\u6355\u83B7\u5E76\u5F52\u4E00\u5316\u5404\u670D\u52A1\u7684 stdout / stderr \u6570\u636E\u6D41\n */\n\nimport type { BdsUpdaterConfig, DBConfig, QQBackend, QQBridgeConfig } from \"@sfmc-bds/sdk/node/config\";\nimport {\n DEFAULT_BDS_UPDATER_CONFIG,\n DEFAULT_DB_CONFIG,\n DEFAULT_QQ_CONFIG,\n ensureCoreConfigs,\n loadEnsuredConfig,\n qqRuntimeStatusPath,\n readJson,\n type QqRuntimeStatus,\n} from \"@sfmc-bds/sdk/node/config\";\n\nimport { bdsExePath, bdsSpawnEnvExtra, ensureBdsExecutable } from \"@sfmc-bds/bds-tools/host-platform\";\nimport {\n clearBdsPidFile,\n isProcessAlive,\n probeBdsStatus,\n readBdsPidFile,\n writeBdsPidFile,\n} from \"@sfmc-bds/bds-tools/process-probe\";\nimport { postBdsLifecycleEvent } from \"@sfmc-bds/bds-tools/qq-events-notify\";\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { EventEmitter } from \"node:events\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { isDaemonServer } from \"./daemon/role.js\";\nimport { onRemoteServiceStateChange } from \"./daemon/client.js\";\nimport { t } from \"./i18n/index.js\";\nimport { resolveLlbotLaunch } from \"./llbot-launch.js\";\nimport { inferLevel, pushLog as pushUnifiedLog } from \"./logs.js\";\nimport { findNodeServicePids } from \"./node-service-probe.js\";\nimport { ensurePackUpdateConfigFile } from \"./pack-update/index.js\";\nimport { recordBdsVersion } from \"./bds-runtime-version.js\";\nimport { reportBdsPlayerSession } from \"./player-session.js\";\nimport { ROOT, spawnService, type ServiceId } from \"./runtime.js\";\n\nexport { ROOT } from \"./runtime.js\";\n\nexport interface LogLine {\n time: Date;\n text: string;\n stream: \"stdout\" | \"stderr\";\n}\n\n/** \u5B50\u8FDB\u7A0B\u8F93\u51FA\u5206\u884C\uFF1A\u8FC7\u6EE4\u7A7A\u4E32\u4EE5\u53CA Windows CRLF \u62C6\u5206\u540E\u6B8B\u7559\u7684\u7EAF\u7A7A\u767D\u884C\u3002 */\nexport function nonBlankOutputLines(text: string): string[] {\n return text.split(/\\r?\\n/).filter((line) => line.trim().length > 0);\n}\n\nexport type ServiceName = \"bds\" | \"db\" | \"tunnel\" | \"qq\" | \"llbot\";\nexport const SERVICE_NAMES: ServiceName[] = [\"bds\", \"db\", \"tunnel\", \"qq\", \"llbot\"];\n\n/** \u5355\u670D\u52A1 start \u7ED3\u679C\uFF1Aoptional \u670D\u52A1 validate \u5931\u8D25\u8BB0\u4E3A skipped\uFF0C\u4E0D\u629B\u9519 */\nexport type StartOutcome = { status: \"started\" } | { status: \"already\" } | { status: \"skipped\"; reason: string };\n\nexport interface StartAllResult {\n started: ServiceName[];\n skipped: Array<{ name: ServiceName; reason: string }>;\n failed: Array<{ name: ServiceName; reason: string }>;\n}\n\nexport interface ServiceStatus {\n name: ServiceName;\n title: string;\n running: boolean;\n pid: number;\n uptime: string;\n ownership?: \"managed\" | \"external\";\n}\n\n/** \u5F53\u524D db \u5065\u5EB7\u63A2\u6D4B\u7AEF\u53E3\uFF08\u4E0E createServices \u540C\u6B65\uFF09 */\nlet dbHealthPort = 3001;\n/** \u5F53\u524D QQ \u540E\u7AEF\uFF08\u4E0E createServices \u540C\u6B65\uFF0C\u4F9B Tab/\u7A97\u6807\u9898\uFF09 */\nlet qqBackendMode: QQBackend = \"official\";\n\nexport function getQqBackendMode(): QQBackend {\n return qqBackendMode;\n}\n\nasync function probeDbHealth(port: number): Promise<boolean> {\n try {\n const res = await fetch(`http://127.0.0.1:${port}/api/health`, { signal: AbortSignal.timeout(2000) });\n return res.ok;\n } catch {\n return false;\n }\n}\n\n/** \u8BFB `.sfmc/qq.runtime.json` + pid \u5B58\u6D3B\uFF0C\u63A2\u6D4B\u5916\u90E8/\u672C\u673A qq-bridge */\nasync function probeQqRuntime(): Promise<{ alive: boolean; pid: number }> {\n const status = readJson<QqRuntimeStatus>(qqRuntimeStatusPath(ROOT));\n if (!status?.pid || status.pid <= 0) return { alive: false, pid: 0 };\n if (!(await isProcessAlive(status.pid))) return { alive: false, pid: 0 };\n return { alive: true, pid: status.pid };\n}\n\nconst tunnelRuntimePath = path.join(ROOT, \".sfmc\", \"tunnel.runtime.json\");\n\nasync function probeTunnelRuntime(): Promise<number> {\n const pid = readJson<{ pid?: number }>(tunnelRuntimePath)?.pid;\n return typeof pid === \"number\" && pid > 0 && (await isProcessAlive(pid)) ? pid : 0;\n}\n\nfunction writeTunnelRuntime(pid: number): void {\n fs.mkdirSync(path.dirname(tunnelRuntimePath), { recursive: true });\n fs.writeFileSync(tunnelRuntimePath, JSON.stringify({ pid, startedAt: Date.now() }) + \"\\n\");\n}\n\nfunction clearTunnelRuntime(pid: number): void {\n if (readJson<{ pid?: number }>(tunnelRuntimePath)?.pid !== pid) return;\n try {\n fs.unlinkSync(tunnelRuntimePath);\n } catch {\n /* \u8FDB\u7A0B\u9000\u51FA\u65F6\u5141\u8BB8\u6587\u4EF6\u5DF2\u88AB\u79FB\u9664 */\n }\n}\n\ninterface ServiceDef {\n name: ServiceName;\n title: string;\n service?: ServiceId;\n cmd?: string;\n args?: string[];\n cwd: string;\n env?: Record<string, string>;\n stopCommand?: string;\n stopTimeout: number;\n autoRestart: boolean;\n restartDelay: number;\n /**\n * \u53EF\u9009\u670D\u52A1\uFF1Avalidate \u5931\u8D25\u65F6 start \u8FD4\u56DE skipped\uFF08startAll \u4E0D\u5F53\u4F5C\u5931\u8D25\uFF09\u3002\n * \u7528\u4E8E official \u4E0B\u8DF3\u8FC7 llbot\u3001\u4EE5\u53CA qq_enabled=false\u3002\n */\n optional?: boolean;\n validate?: () => string | null;\n /** \u542F\u52A8\u524D\u94A9\u5B50(\u5982 BDS \u88C5\u8F7D\u4E00\u81F4\u6027\u6821\u9A8C);\u5931\u8D25\u5219\u7981\u6B62 spawn */\n beforeStart?: () => Promise<void>;\n /** \u539F\u59CB\u65E5\u5FD7\u884C\u89C2\u5BDF\u5668\uFF0C\u4E0D\u5F97\u963B\u585E\u5B50\u8FDB\u7A0B\u8F93\u51FA\u3002 */\n onLogLine?: (line: string) => void;\n}\n\nclass Service {\n name: ServiceName;\n title: string;\n proc: ChildProcess | null = null;\n running = false;\n pid = 0;\n startTime: Date | null = null;\n logs: LogLine[] = [];\n events = new EventEmitter();\n\n private def: ServiceDef;\n private manualStop = false;\n private updateInProgress = false;\n private restartTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(def: ServiceDef) {\n this.name = def.name;\n this.title = def.title;\n this.def = def;\n if (def.name === \"tunnel\") {\n this.events.on(\"output\", (text: string, level: \"info\" | \"error\") => {\n pushUnifiedLog(text, \"tunnel\", level);\n });\n }\n }\n\n get uptime(): string {\n if (!this.startTime || !this.running) return \"\u2014\";\n const ms = Date.now() - this.startTime.getTime();\n const m = Math.floor(ms / 60000);\n const h = Math.floor(m / 60);\n if (h > 0) return `${h}h ${m % 60}m`;\n if (m > 0) return `${m}m`;\n return `${Math.floor(ms / 1000)}s`;\n }\n\n pushLog(text: string, stream: \"stdout\" | \"stderr\"): void {\n const line: LogLine = { time: new Date(), text, stream };\n this.logs.push(line);\n if (this.logs.length > 2000) this.logs.splice(0, this.logs.length - 2000);\n this.events.emit(\"log\", line);\n // stderr \u89C6\u4E3A error; stdout \u7528 inferLevel \u63A8\u65AD (bare \u5B50\u8FDB\u7A0B\u7EAF text \u65F6\u9ED8\u8BA4 info,\n // BDS \u7B49\u81EA\u5E26 [LEVEL] \u6807\u7B7E\u7684\u4ECD\u53EF\u6B63\u786E\u63A8\u65AD)\n const level = stream === \"stderr\" ? \"error\" : inferLevel(text);\n pushUnifiedLog(text, this.name, level);\n this.def.onLogLine?.(text);\n }\n\n async start(): Promise<StartOutcome> {\n if (this.updateInProgress) throw new Error(\"BDS \u6B63\u5728\u66F4\u65B0\uFF0C\u6682\u4E0D\u53EF\u542F\u52A8\");\n if (this.running) return { status: \"already\" };\n if (this.name === \"bds\") {\n const probe = await probeBdsStatus({ rootDir: ROOT });\n if (probe.state !== \"stopped\") {\n throw new Error(\n t(\"svc.bdsAlreadyRunning\", {\n pid: String(probe.pid),\n kind: t(probe.state === \"managed\" ? \"svc.running\" : \"svc.runningExternal\"),\n })\n );\n }\n }\n if (this.name === \"db\" || this.name === \"qq\") {\n const external = await findNodeServicePids(this.name);\n if (external.length > 0) {\n return { status: \"already\" };\n }\n }\n if (this.name === \"tunnel\" && (await probeTunnelRuntime())) return { status: \"already\" };\n if (this.def.validate) {\n const v = this.def.validate();\n if (v) {\n // optional\uFF1A\u914D\u7F6E\u5C42\u9762\u8DF3\u8FC7\uFF08\u5982 official \u4E0B llbot\uFF09\uFF0C\u4E0D\u5F53\u5931\u8D25\u3001\u4E5F\u4E0D spawn\n if (this.def.optional) return { status: \"skipped\", reason: v };\n throw new Error(v);\n }\n }\n if (this.def.beforeStart) {\n await this.def.beforeStart();\n }\n // beforeStart \u542B\u5F02\u6B65\u68C0\u67E5\uFF1B\u66F4\u65B0\u53EF\u80FD\u5728\u7B49\u5F85\u671F\u95F4\u5F00\u59CB\u3002\n if (this.updateInProgress) throw new Error(\"BDS \u6B63\u5728\u66F4\u65B0\uFF0C\u6682\u4E0D\u53EF\u542F\u52A8\");\n this.manualStop = false;\n /* \u5B50\u8FDB\u7A0B\u59CB\u7EC8\u7531\u5B88\u62A4\u8FDB\u7A0B\u7528\u7BA1\u9053\u6301\u6709\uFF08stdin \u53D1 stop\u3001stdout \u65E5\u5FD7\u3001autoRestart\uFF09\uFF1B\n * \u4E0D\u518D\u5BF9\u5B50\u670D\u52A1\u505A detached/unref\u2014\u2014\u8131\u79BB\u7EC8\u7AEF\u7531 daemon \u8FDB\u7A0B\u81EA\u8EAB\u8D1F\u8D23\u3002\n * windowsHide\uFF1AWindows \u4E0A\u907F\u514D\u4E3A\u6BCF\u4E2A\u670D\u52A1\u5F39\u51FA\u7A7A\u767D\u63A7\u5236\u53F0\uFF08stdout \u5DF2 pipe\uFF0C\u7A97\u5185\u65E0\u8F93\u51FA\uFF09\u3002 */\n const spawnOpts = {\n cwd: this.def.cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"] as Array<\"pipe\">,\n env: this.def.env ? { ...process.env, ...this.def.env } : process.env,\n windowsHide: true,\n };\n const child = this.def.service\n ? spawnService(this.def.service, this.def.args ?? [], spawnOpts)\n : spawn(this.def.cmd as string, this.def.args ?? [], spawnOpts);\n this.proc = child;\n this.pid = child.pid ?? 0;\n this.running = true;\n this.startTime = new Date();\n if (this.name === \"bds\" && this.pid > 0) {\n writeBdsPidFile(this.pid, ROOT);\n }\n if (this.name === \"tunnel\" && this.pid > 0) writeTunnelRuntime(this.pid);\n this.events.emit(\"output\", `started (PID ${this.pid})`, \"info\");\n this.events.emit(\"state\", { name: this.name, running: true, pid: this.pid });\n\n let lifecycleReported = false;\n const reportExit = (code?: number | null) => {\n if (this.name !== \"bds\" || lifecycleReported) return;\n lifecycleReported = true;\n const planned = this.manualStop || this.updateInProgress;\n void postBdsLifecycleEvent(planned ? \"stop\" : \"crash\", planned ? undefined : `code=${code ?? \"?\"}`, {\n port: dbHealthPort,\n });\n };\n if (this.name === \"bds\") {\n child.once(\"spawn\", () => {\n void postBdsLifecycleEvent(\"start\", undefined, { port: dbHealthPort });\n });\n }\n\n child.on(\"error\", (e) => {\n this.events.emit(\"output\", `process error: ${e.message}`, \"error\");\n reportExit();\n this.cleanup();\n this.scheduleRestart();\n });\n\n let versionLineBuffer = \"\";\n child.stdout?.on(\"data\", (d: Buffer) => {\n if (this.name === \"bds\" && this.startTime) {\n versionLineBuffer += d.toString();\n const versionLines = versionLineBuffer.split(/\\r?\\n/);\n versionLineBuffer = (versionLines.pop() ?? \"\").slice(-4096);\n for (const versionLine of versionLines) recordBdsVersion(ROOT, versionLine, this.pid, this.startTime.getTime());\n }\n for (const line of nonBlankOutputLines(d.toString())) {\n this.pushLog(line, \"stdout\");\n }\n });\n child.stderr?.on(\"data\", (d: Buffer) => {\n for (const line of nonBlankOutputLines(d.toString())) {\n this.pushLog(line, \"stderr\");\n }\n });\n\n child.on(\"exit\", (code) => {\n if (this.proc && this.proc !== child) return;\n this.events.emit(\"output\", `exited (code: ${code})`, \"info\");\n reportExit(code);\n this.cleanup();\n this.scheduleRestart();\n });\n return { status: \"started\" };\n }\n\n private scheduleRestart(): void {\n if (this.manualStop || this.updateInProgress || !this.def.autoRestart || this.restartTimer) return;\n this.restartTimer = setTimeout(() => {\n this.restartTimer = null;\n if (!this.updateInProgress && !this.manualStop && !this.running) {\n void this.start().catch((e: Error) => {\n this.events.emit(\"output\", `restart failed: ${e.message}`, \"error\");\n });\n }\n }, this.def.restartDelay);\n }\n\n /** \u66F4\u65B0\u5668\u72EC\u7ACB\u8FDB\u7A0B\u505C\u670D\u65F6\uFF0C\u7981\u6B62\u5C06\u8BA1\u5212\u5185\u9000\u51FA\u5F53\u4F5C\u5D29\u6E83\u5E76\u81EA\u52A8\u62C9\u8D77\u3002 */\n beginUpdate(): void {\n if (this.name !== \"bds\") return;\n if (this.updateInProgress) throw new Error(\"BDS \u66F4\u65B0\u5DF2\u5728\u8FDB\u884C\u4E2D\");\n this.updateInProgress = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n }\n\n endUpdate(): void {\n this.updateInProgress = false;\n }\n\n async stop(): Promise<void> {\n this.manualStop = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n if (!this.proc || !this.running) return;\n this.events.emit(\"output\", \"stopping...\", \"info\");\n\n if (this.def.stopCommand && this.proc.stdin) {\n this.proc.stdin.write(this.def.stopCommand + \"\\n\");\n } else {\n this.proc.kill(\"SIGTERM\");\n }\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n if (this.proc) {\n this.events.emit(\"output\", \"force kill\", \"error\");\n try {\n this.proc.kill(\"SIGKILL\");\n } catch {\n /* ignore */\n }\n }\n resolve();\n }, this.def.stopTimeout);\n\n this.proc?.on(\"exit\", () => {\n clearTimeout(timeout);\n resolve();\n });\n });\n }\n\n forceStop(): void {\n const child = this.proc;\n this.manualStop = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n this.cleanup();\n if (!child) return;\n try {\n child.kill(\"SIGKILL\");\n } catch {\n /* ignore */\n }\n }\n\n async restart(): Promise<StartOutcome> {\n await this.stop();\n return this.start();\n }\n\n getRecentLogs(n: number): LogLine[] {\n return this.logs.slice(-n);\n }\n\n /**\n * \u63A2\u6D4B\u53D1\u73B0\u8FDB\u7A0B\u5DF2\u6B7B\u540E\u56DE\u5199\u672C\u5730\u72B6\u6001\uFF08\u4E0D\u8BBE manualStop\uFF0C\u4EE5\u4FBF exit \u56DE\u8C03\u4ECD\u53EF\u6309\u9700 autoRestart\uFF09\u3002\n */\n markStoppedFromProbe(): void {\n if (!this.running && !this.proc) return;\n this.cleanup();\n }\n\n private cleanup(): void {\n const wasRunning = this.running || this.proc !== null;\n const exitingPid = this.pid;\n this.proc = null;\n this.running = false;\n this.pid = 0;\n this.startTime = null;\n if (this.name === \"bds\" && exitingPid > 0) {\n const filePid = readBdsPidFile(ROOT);\n if (filePid === exitingPid) {\n clearBdsPidFile(ROOT);\n }\n }\n if (this.name === \"tunnel\" && exitingPid > 0) clearTunnelRuntime(exitingPid);\n if (wasRunning) {\n this.events.emit(\"state\", { name: this.name, running: false, pid: 0 });\n }\n }\n}\n\nfunction createServices(): Record<ServiceName, Service> {\n /* \u5404\u670D\u52A1/CLI \u7528 SDK ensureCoreConfigs \u64AD\u79CD\uFF08\u542B $schema\uFF09\uFF0C\u4E0D\u518D\u4ECE configs-default \u62F7\u8D1D\u3002 */\n ensureCoreConfigs(ROOT, [\"bds_updater\", \"qq_config\", \"db_config\"]);\n ensurePackUpdateConfigFile();\n const bdsCfg = loadEnsuredConfig(ROOT, \"bds_updater.json\", \"bds_updater\", { ...DEFAULT_BDS_UPDATER_CONFIG } as Record<\n string,\n unknown\n >) as BdsUpdaterConfig;\n const qqCfg = loadEnsuredConfig(ROOT, \"qq_config.json\", \"qq_config\", { ...DEFAULT_QQ_CONFIG } as Record<\n string,\n unknown\n >) as QQBridgeConfig;\n const dbCfg = loadEnsuredConfig(ROOT, \"db_config.json\", \"db_config\", { ...DEFAULT_DB_CONFIG } as Record<\n string,\n unknown\n >) as DBConfig;\n const bdsPath = bdsCfg.bds_path ?? ROOT;\n const useLlbotBackend = qqCfg.qq_backend === \"llbot\";\n const qqEnabled = qqCfg.qq_enabled !== false;\n // \u5B98\u65B9 Webhook \u7531\u4E91\u7AEF\u63A5\u6536\uFF1B\u4EC5\u5207\u6362\u4F20\u8F93\u65B9\u5F0F\u5373\u53EF\u907F\u514D\u672C\u673A\u518D\u542F\u52A8\u4E00\u4E2A\u6865\u3002\n const qqExternal = !useLlbotBackend && qqCfg.official?.transport === \"webhook\";\n const tunnelCfg = qqCfg.official?.tunnel;\n const tunnelEnabled = qqEnabled && qqExternal && tunnelCfg?.enabled === true;\n const tunnelHost = String(tunnelCfg?.host ?? \"\").trim();\n const tunnelRemotePort = tunnelCfg?.remotePort ?? 13001;\n const llbotEnabled = qqCfg.llbot?.enabled !== false;\n const llbotLaunch = resolveLlbotLaunch(qqCfg.llbot?.path, qqCfg.llbot?.cwd);\n const llbotPath = llbotLaunch.exe;\n const llbotCwd = llbotLaunch.cwd;\n const dbPort = dbCfg.db_port ?? 3001;\n dbHealthPort = dbPort;\n qqBackendMode = useLlbotBackend ? \"llbot\" : \"official\";\n const bdsExe = bdsExePath(path.resolve(bdsPath));\n const qqTitle = useLlbotBackend ? \"QQ (llbot)\" : \"QQ (official)\";\n\n return {\n bds: new Service({\n name: \"bds\",\n title: \"BDS\",\n cmd: bdsExe,\n args: [],\n cwd: bdsPath,\n env: bdsSpawnEnvExtra(bdsPath),\n stopCommand: \"stop\",\n stopTimeout: 30000,\n /* \u5D29\u6E83\u62C9\u8D77\u7531 sfmc daemon \u56FA\u5B9A\u5F00\u542F\uFF1B\u4E0D\u518D\u8BFB bds_updater.crash_restart */\n autoRestart: true,\n restartDelay: 5000,\n validate: () => {\n if (!fs.existsSync(bdsExe)) return `not found: ${bdsExe}`;\n return null;\n },\n beforeStart: async () => {\n ensureBdsExecutable(bdsExe);\n /* \u5148\u88C5\u6536\u4EF6\u7BB1\u7B2C\u4E09\u65B9\u5305\uFF0C\u518D\u68C0\u67E5 CF \u66F4\u65B0\uFF0C\u518D\u8DD1\u6A21\u5757\u805A\u5408\u95F8\u95E8 */\n const { scanAndInstallInbox } = await import(\"./world-packs.js\");\n await scanAndInstallInbox({ interactive: false });\n const { runPackUpdatesOnBdsStart } = await import(\"./pack-update/index.js\");\n await runPackUpdatesOnBdsStart();\n const { ensurePacksReady } = await import(\"./pack-lifecycle.js\");\n await ensurePacksReady();\n },\n onLogLine: (line) =>\n reportBdsPlayerSession(line, {\n port: dbPort,\n ...(dbCfg.http_auth ? { authToken: dbCfg.http_auth } : {}),\n }),\n }),\n\n db: new Service({\n name: \"db\",\n title: \"DB Server\",\n service: \"db\",\n cwd: ROOT,\n stopTimeout: 10000,\n autoRestart: true,\n restartDelay: 3000,\n env: { DB_PORT: String(dbPort) },\n }),\n\n tunnel: new Service({\n name: \"tunnel\",\n title: \"QQ SSH Tunnel\",\n cmd: \"ssh\",\n args: [\n \"-N\", \"-T\", \"-o\", \"BatchMode=yes\", \"-o\", \"ConnectTimeout=10\",\n \"-o\", \"ExitOnForwardFailure=yes\", \"-o\", \"ServerAliveInterval=30\",\n \"-o\", \"ServerAliveCountMax=3\",\n \"-R\", `127.0.0.1:${tunnelRemotePort}:127.0.0.1:${dbPort}`,\n tunnelHost,\n ],\n cwd: ROOT,\n stopTimeout: 10000,\n autoRestart: true,\n restartDelay: 5000,\n optional: !tunnelEnabled,\n validate: () => {\n if (!tunnelEnabled) return \"QQ tunnel disabled (official webhook tunnel.enabled=false)\";\n if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/.test(tunnelHost)) return \"invalid official.tunnel.host\";\n if (!Number.isInteger(tunnelRemotePort) || tunnelRemotePort < 1 || tunnelRemotePort > 65535) {\n return \"invalid official.tunnel.remotePort\";\n }\n return null;\n },\n }),\n\n qq: new Service({\n name: \"qq\",\n title: qqTitle,\n service: \"qq\",\n cwd: ROOT,\n stopTimeout: 10000,\n // \u7F3A\u51ED\u636E\u65F6 validate \u62E6\u4E0B\uFF0C\u4E0D\u4F1A spawn\u2192exit(1)\u2192autoRestart \u6B7B\u5FAA\u73AF\n autoRestart: true,\n restartDelay: 3000,\n optional: !qqEnabled || qqExternal,\n validate: () => {\n if (!qqEnabled) return \"QQ bridge disabled (qq_enabled=false)\";\n if (qqExternal) return \"QQ bridge runs externally (official.transport=webhook)\";\n if (!useLlbotBackend) {\n if (!String(qqCfg.official?.app_id ?? \"\").trim() || !String(qqCfg.official?.app_secret ?? \"\").trim()) {\n return \"missing official.app_id / official.app_secret\";\n }\n }\n return null;\n },\n }),\n\n llbot: new Service({\n name: \"llbot\",\n title: \"LLBot\",\n cmd: llbotPath,\n args: [],\n cwd: llbotCwd,\n stopTimeout: 10000,\n autoRestart: false,\n restartDelay: 5000,\n // official \u6216\u672A\u542F\u7528\u65F6\u771F\u6B63 skip\uFF0C\u4E0D throw\n optional: !useLlbotBackend || !llbotEnabled,\n validate: () => {\n if (!useLlbotBackend) return \"LLBot skipped (qq_backend!=llbot)\";\n if (!llbotEnabled) return \"LLBot disabled (llbot.enabled=false)\";\n if (!fs.existsSync(llbotPath)) return `not found: ${llbotPath}`;\n return null;\n },\n }),\n };\n}\n\nexport let services: Record<ServiceName, Service> = createServices();\n\nexport function refreshServices(): void {\n forceStopAll();\n services = createServices();\n}\n\nexport const START_ORDER: ServiceName[] = [\"db\", \"tunnel\", \"qq\", \"llbot\", \"bds\"];\n\nexport async function startAll(): Promise<StartAllResult> {\n const result: StartAllResult = { started: [], skipped: [], failed: [] };\n for (const name of START_ORDER) {\n const svc = services[name];\n if (!svc) continue;\n try {\n const outcome = await svc.start();\n if (outcome.status === \"skipped\") {\n result.skipped.push({ name, reason: outcome.reason });\n svc.events.emit(\"output\", `skipped: ${outcome.reason}`, \"info\");\n } else {\n result.started.push(name);\n }\n } catch (e) {\n const reason = (e as Error).message;\n result.failed.push({ name, reason });\n svc.events.emit(\"output\", `start error: ${reason}`, \"error\");\n }\n }\n return result;\n}\n\nexport async function stopAll(): Promise<void> {\n const pending = [...START_ORDER]\n .reverse()\n .map((name) => services[name])\n .filter((service): service is Service => Boolean(service))\n .map((service) => service.stop());\n await Promise.allSettled(pending);\n}\n\nexport function forceStopAll(): void {\n for (const service of Object.values(services)) service.forceStop();\n}\n\nexport type ServiceStateEvent = { name: ServiceName; running: boolean; pid: number };\n\n/** \u8BA2\u9605\u4EFB\u610F\u670D\u52A1\u542F\u505C\uFF08\u542B\u63A2\u6D4B\u56DE\u5199\uFF09\uFF1B\u8FD4\u56DE\u53D6\u6D88\u51FD\u6570 */\nexport function onServiceStateChange(fn: (ev: ServiceStateEvent) => void): () => void {\n /* CLI \u8FDB\u7A0B\u4E0D\u6301\u6709 Service \u5B50\u8FDB\u7A0B\uFF1A\u6539\u8BA2\u5B88\u62A4\u8FDB\u7A0B\u63A8\u9001\u7684\u8FDC\u7A0B\u72B6\u6001\u4E8B\u4EF6 */\n if (!isDaemonServer()) {\n return onRemoteServiceStateChange(fn);\n }\n const handler = (ev: ServiceStateEvent): void => {\n fn(ev);\n };\n for (const service of Object.values(services)) {\n service.events.on(\"state\", handler);\n }\n return () => {\n for (const service of Object.values(services)) {\n service.events.off(\"state\", handler);\n }\n };\n}\n\n/** \u82E5\u672C\u5730\u6807\u8BB0 running \u4F46 OS \u8FDB\u7A0B\u5DF2\u6B7B\uFF0C\u56DE\u6536\u5185\u5B58\u6807\u5FD7 */\nasync function reconcileManagedAlive(service: Service): Promise<boolean> {\n if (!service.running) return false;\n if (service.pid > 0 && !(await isProcessAlive(service.pid))) {\n service.markStoppedFromProbe();\n return false;\n }\n return service.running;\n}\n\n/**\n * \u7EDF\u4E00\u8FD0\u884C\u6001\u67E5\u8BE2\uFF08\u6743\u5A01\u5165\u53E3\uFF09\uFF1AOS/\u5065\u5EB7\u63A2\u6D4B + \u56DE\u5199 Service \u5185\u5B58\u6807\u5FD7\u3002\n * status / Tab \u53D1\u9001\u76EE\u6807 / reload \u7B49\u5747\u5E94\u8D70\u6B64\u63A5\u53E3\uFF0C\u52FF\u76F4\u63A5\u8BFB `service.running`\u3002\n * CLI \u8FDB\u7A0B\u7ECF\u5B88\u62A4\u8FDB\u7A0B RPC \u83B7\u53D6\uFF1B\u5B88\u62A4\u8FDB\u7A0B\u5185\u8D70\u672C\u5730\u63A2\u6D3B\u3002\n */\nexport async function queryServicesRuntime(): Promise<ServiceStatus[]> {\n if (!isDaemonServer()) {\n const { queryRuntimeViaDaemon } = await import(\"./daemon/client.js\");\n return queryRuntimeViaDaemon();\n }\n return queryServicesRuntimeLocal();\n}\n\n/** \u5B88\u62A4\u8FDB\u7A0B\u5185\u672C\u5730\u63A2\u6D3B\u5B9E\u73B0\uFF08\u4EA6\u4F9B server status \u7EC4\u88C5 rows\uFF09 */\nexport async function queryServicesRuntimeLocal(): Promise<ServiceStatus[]> {\n return Promise.all(\n SERVICE_NAMES.map(async (name) => {\n const service = services[name];\n let running = false;\n let pid = 0;\n let uptime = \"\u2014\";\n let ownership: \"managed\" | \"external\" | undefined;\n\n if (name === \"bds\") {\n const probe = await probeBdsStatus({\n managedPid: service.pid,\n hasStdin: Boolean(service.proc?.stdin),\n rootDir: ROOT,\n });\n if (probe.state === \"managed\") {\n running = true;\n pid = probe.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n /* \u63A2\u6D4B\u4E3A managed \u4F46\u672C\u5730\u5DF2\u6807\u505C\uFF1A\u4FDD\u6301\u63A2\u6D4B\u7ED3\u679C\uFF0C\u4E0D\u5F3A\u5236\u6539\u5185\u5B58\uFF08stdin \u4ECD\u53EF\u7528\uFF09 */\n } else if (probe.state === \"external\") {\n running = true;\n pid = probe.pid;\n ownership = \"external\";\n /* \u5916\u90E8\u8FDB\u7A0B\uFF1A\u672C\u5730 managed \u53E5\u67C4\u5DF2\u5931\u6548\u5219\u56DE\u6536 */\n if (service.running && service.pid !== probe.pid) {\n service.markStoppedFromProbe();\n } else if (service.running && !(await isProcessAlive(service.pid))) {\n service.markStoppedFromProbe();\n }\n } else {\n running = false;\n if (service.running || service.proc) {\n service.markStoppedFromProbe();\n }\n }\n } else if (name === \"db\") {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n } else if (await probeDbHealth(dbHealthPort)) {\n running = true;\n ownership = \"external\";\n const pids = await findNodeServicePids(\"db\");\n pid = pids[0] ?? 0;\n }\n } else if (name === \"qq\") {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n } else {\n const probe = await probeQqRuntime();\n if (probe.alive) {\n running = true;\n pid = probe.pid;\n ownership = \"external\";\n } else {\n const pids = await findNodeServicePids(\"qq\");\n if (pids.length > 0) {\n running = true;\n pid = pids[0]!;\n ownership = \"external\";\n }\n }\n }\n } else if (name === \"tunnel\") {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n } else {\n pid = await probeTunnelRuntime();\n running = pid > 0;\n if (running) ownership = \"external\";\n }\n } else {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n }\n }\n\n return {\n name,\n title: service.title,\n running,\n pid,\n uptime,\n ...(ownership ? { ownership } : {}),\n };\n })\n );\n}\n\n/** \u5355\u670D\u52A1\u662F\u5426\u5728\u8DD1\uFF08\u542B\u5916\u90E8\u5B9E\u4F8B\uFF09 */\nexport async function isServiceRunning(name: ServiceName): Promise<boolean> {\n const rows = await queryServicesRuntime();\n return rows.some((r) => r.name === name && r.running);\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * services.ts \u2014 CLI \u8FDB\u7A0B\u7F16\u6392\u4E0E\u670D\u52A1\u751F\u547D\u5468\u671F\u7BA1\u7406\n *\n * \u6838\u5FC3\u7BA1\u7406\u5BF9\u8C61\uFF08ServiceName\uFF09\uFF1A\n * - `bds`\uFF1AMinecraft \u57FA\u5CA9\u7248\u670D\u52A1\u7AEF\uFF08\u652F\u6301 stdin \u7BA1\u9053\u6307\u4EE4\u4EA4\u4E92\u4E0E\u4F18\u96C5\u505C\u670D\uFF09\n * - `db`\uFF1Adb-server SQLite HTTP \u670D\u52A1\uFF08\u7AEF\u53E3\u5065\u5EB7\u68C0\u67E5\u4E0E loopback \u63A2\u6D3B\uFF09\n * - `tunnel`\uFF1A\u4E91\u7AEF QQ Webhook \u5230\u672C\u673A db-server \u7684 SSH \u53CD\u5411\u96A7\u9053\n * - `qq`\uFF1Aqq-bridge \u6D88\u606F\u7F51\u6865\u670D\u52A1\uFF08\u652F\u6301\u5B98\u65B9 Bot \u4E0E LLBot \u53CC\u540E\u7AEF\uFF09\n * - `llbot`\uFF1ALLBot \u72EC\u7ACB\u673A\u5668\u4EBA\u8FDB\u7A0B\uFF08\u6761\u4EF6\u542F\u52A8\uFF09\n *\n * \u6838\u5FC3\u80FD\u529B\uFF1A\n * - \u8FDB\u7A0B\u751F\u547D\u5468\u671F\uFF1A\u6309\u4F9D\u8D56\u6B21\u5E8F\u7F16\u6392\u670D\u52A1\u542F\u52A8\uFF08db \u2192 qq \u2192 bds\uFF09\u3001\u4F18\u96C5\u505C\u6B62\u4E0E\u5F02\u5E38\u81EA\u52A8\u62C9\u8D77\n * - \u591A\u6E90\u63A2\u6D3B\u611F\u77E5\uFF1A\u81EA\u52A8\u63A2\u6D4B\u6258\u7BA1\u8FDB\u7A0B\u4E0E\u5916\u90E8\u72EC\u7ACB\u8FDB\u7A0B\u72B6\u6001\n * - \u65E5\u5FD7\u6D41\u7EDF\u4E00\u805A\u5408\uFF1A\u6355\u83B7\u5E76\u5F52\u4E00\u5316\u5404\u670D\u52A1\u7684 stdout / stderr \u6570\u636E\u6D41\n */\n\nimport type { BdsUpdaterConfig, DBConfig, QQBackend, QQBridgeConfig } from \"@sfmc-bds/sdk/node/config\";\nimport {\n DEFAULT_BDS_UPDATER_CONFIG,\n DEFAULT_DB_CONFIG,\n DEFAULT_QQ_CONFIG,\n ensureCoreConfigs,\n loadEnsuredConfig,\n qqRuntimeStatusPath,\n readJson,\n type QqRuntimeStatus,\n} from \"@sfmc-bds/sdk/node/config\";\n\nimport { bdsExePath, bdsSpawnEnvExtra, ensureBdsExecutable } from \"@sfmc-bds/bds-tools/host-platform\";\nimport {\n clearBdsPidFile,\n isProcessAlive,\n probeBdsStatus,\n readBdsPidFile,\n writeBdsPidFile,\n} from \"@sfmc-bds/bds-tools/process-probe\";\nimport { postBdsLifecycleEvent } from \"@sfmc-bds/bds-tools/qq-events-notify\";\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { EventEmitter } from \"node:events\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { isDaemonServer } from \"./daemon/role.js\";\nimport { onRemoteServiceStateChange } from \"./daemon/client.js\";\nimport { t } from \"./i18n/index.js\";\nimport { resolveLlbotLaunch } from \"./llbot-launch.js\";\nimport { inferLevel, pushLog as pushUnifiedLog } from \"./logs.js\";\nimport { findNodeServicePids } from \"./node-service-probe.js\";\nimport { ensurePackUpdateConfigFile } from \"./pack-update/index.js\";\nimport { recordBdsVersion } from \"./bds-runtime-version.js\";\nimport { reportBdsPlayerSession } from \"./player-session.js\";\nimport { startModuleSidecars, stopModuleSidecars } from \"./module-sidecars.js\";\nimport { ROOT, spawnService, type ServiceId } from \"./runtime.js\";\n\nexport { ROOT } from \"./runtime.js\";\n\nexport interface LogLine {\n time: Date;\n text: string;\n stream: \"stdout\" | \"stderr\";\n}\n\n/** \u5B50\u8FDB\u7A0B\u8F93\u51FA\u5206\u884C\uFF1A\u8FC7\u6EE4\u7A7A\u4E32\u4EE5\u53CA Windows CRLF \u62C6\u5206\u540E\u6B8B\u7559\u7684\u7EAF\u7A7A\u767D\u884C\u3002 */\nexport function nonBlankOutputLines(text: string): string[] {\n return text.split(/\\r?\\n/).filter((line) => line.trim().length > 0);\n}\n\nexport type ServiceName = \"bds\" | \"db\" | \"tunnel\" | \"qq\" | \"llbot\";\nexport const SERVICE_NAMES: ServiceName[] = [\"bds\", \"db\", \"tunnel\", \"qq\", \"llbot\"];\n\n/** \u5355\u670D\u52A1 start \u7ED3\u679C\uFF1Aoptional \u670D\u52A1 validate \u5931\u8D25\u8BB0\u4E3A skipped\uFF0C\u4E0D\u629B\u9519 */\nexport type StartOutcome = { status: \"started\" } | { status: \"already\" } | { status: \"skipped\"; reason: string };\n\nexport interface StartAllResult {\n started: ServiceName[];\n skipped: Array<{ name: ServiceName; reason: string }>;\n failed: Array<{ name: ServiceName; reason: string }>;\n}\n\nexport interface ServiceStatus {\n name: ServiceName;\n title: string;\n running: boolean;\n pid: number;\n uptime: string;\n ownership?: \"managed\" | \"external\";\n}\n\n/** \u5F53\u524D db \u5065\u5EB7\u63A2\u6D4B\u7AEF\u53E3\uFF08\u4E0E createServices \u540C\u6B65\uFF09 */\nlet dbHealthPort = 3001;\n/** \u5F53\u524D QQ \u540E\u7AEF\uFF08\u4E0E createServices \u540C\u6B65\uFF0C\u4F9B Tab/\u7A97\u6807\u9898\uFF09 */\nlet qqBackendMode: QQBackend = \"official\";\n\nexport function getQqBackendMode(): QQBackend {\n return qqBackendMode;\n}\n\nasync function probeDbHealth(port: number): Promise<boolean> {\n try {\n const res = await fetch(`http://127.0.0.1:${port}/api/health`, { signal: AbortSignal.timeout(2000) });\n return res.ok;\n } catch {\n return false;\n }\n}\n\n/** \u8BFB `.sfmc/qq.runtime.json` + pid \u5B58\u6D3B\uFF0C\u63A2\u6D4B\u5916\u90E8/\u672C\u673A qq-bridge */\nasync function probeQqRuntime(): Promise<{ alive: boolean; pid: number }> {\n const status = readJson<QqRuntimeStatus>(qqRuntimeStatusPath(ROOT));\n if (!status?.pid || status.pid <= 0) return { alive: false, pid: 0 };\n if (!(await isProcessAlive(status.pid))) return { alive: false, pid: 0 };\n return { alive: true, pid: status.pid };\n}\n\nconst tunnelRuntimePath = path.join(ROOT, \".sfmc\", \"tunnel.runtime.json\");\n\nasync function probeTunnelRuntime(): Promise<number> {\n const pid = readJson<{ pid?: number }>(tunnelRuntimePath)?.pid;\n return typeof pid === \"number\" && pid > 0 && (await isProcessAlive(pid)) ? pid : 0;\n}\n\nfunction writeTunnelRuntime(pid: number): void {\n fs.mkdirSync(path.dirname(tunnelRuntimePath), { recursive: true });\n fs.writeFileSync(tunnelRuntimePath, JSON.stringify({ pid, startedAt: Date.now() }) + \"\\n\");\n}\n\nfunction clearTunnelRuntime(pid: number): void {\n if (readJson<{ pid?: number }>(tunnelRuntimePath)?.pid !== pid) return;\n try {\n fs.unlinkSync(tunnelRuntimePath);\n } catch {\n /* \u8FDB\u7A0B\u9000\u51FA\u65F6\u5141\u8BB8\u6587\u4EF6\u5DF2\u88AB\u79FB\u9664 */\n }\n}\n\ninterface ServiceDef {\n name: ServiceName;\n title: string;\n service?: ServiceId;\n cmd?: string;\n args?: string[];\n cwd: string;\n env?: Record<string, string>;\n stopCommand?: string;\n stopTimeout: number;\n autoRestart: boolean;\n restartDelay: number;\n /**\n * \u53EF\u9009\u670D\u52A1\uFF1Avalidate \u5931\u8D25\u65F6 start \u8FD4\u56DE skipped\uFF08startAll \u4E0D\u5F53\u4F5C\u5931\u8D25\uFF09\u3002\n * \u7528\u4E8E official \u4E0B\u8DF3\u8FC7 llbot\u3001\u4EE5\u53CA qq_enabled=false\u3002\n */\n optional?: boolean;\n validate?: () => string | null;\n /** \u542F\u52A8\u524D\u94A9\u5B50(\u5982 BDS \u88C5\u8F7D\u4E00\u81F4\u6027\u6821\u9A8C);\u5931\u8D25\u5219\u7981\u6B62 spawn */\n beforeStart?: () => Promise<void>;\n /** \u539F\u59CB\u65E5\u5FD7\u884C\u89C2\u5BDF\u5668\uFF0C\u4E0D\u5F97\u963B\u585E\u5B50\u8FDB\u7A0B\u8F93\u51FA\u3002 */\n onLogLine?: (line: string) => void;\n}\n\nclass Service {\n name: ServiceName;\n title: string;\n proc: ChildProcess | null = null;\n running = false;\n pid = 0;\n startTime: Date | null = null;\n logs: LogLine[] = [];\n events = new EventEmitter();\n\n private def: ServiceDef;\n private manualStop = false;\n private updateInProgress = false;\n private restartTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(def: ServiceDef) {\n this.name = def.name;\n this.title = def.title;\n this.def = def;\n if (def.name === \"tunnel\") {\n this.events.on(\"output\", (text: string, level: \"info\" | \"error\") => {\n pushUnifiedLog(text, \"tunnel\", level);\n });\n }\n }\n\n get uptime(): string {\n if (!this.startTime || !this.running) return \"\u2014\";\n const ms = Date.now() - this.startTime.getTime();\n const m = Math.floor(ms / 60000);\n const h = Math.floor(m / 60);\n if (h > 0) return `${h}h ${m % 60}m`;\n if (m > 0) return `${m}m`;\n return `${Math.floor(ms / 1000)}s`;\n }\n\n pushLog(text: string, stream: \"stdout\" | \"stderr\"): void {\n const line: LogLine = { time: new Date(), text, stream };\n this.logs.push(line);\n if (this.logs.length > 2000) this.logs.splice(0, this.logs.length - 2000);\n this.events.emit(\"log\", line);\n // stderr \u89C6\u4E3A error; stdout \u7528 inferLevel \u63A8\u65AD (bare \u5B50\u8FDB\u7A0B\u7EAF text \u65F6\u9ED8\u8BA4 info,\n // BDS \u7B49\u81EA\u5E26 [LEVEL] \u6807\u7B7E\u7684\u4ECD\u53EF\u6B63\u786E\u63A8\u65AD)\n const level = stream === \"stderr\" ? \"error\" : inferLevel(text);\n pushUnifiedLog(text, this.name, level);\n this.def.onLogLine?.(text);\n }\n\n async start(): Promise<StartOutcome> {\n if (this.updateInProgress) throw new Error(\"BDS \u6B63\u5728\u66F4\u65B0\uFF0C\u6682\u4E0D\u53EF\u542F\u52A8\");\n if (this.running) return { status: \"already\" };\n if (this.name === \"bds\") {\n const probe = await probeBdsStatus({ rootDir: ROOT });\n if (probe.state !== \"stopped\") {\n throw new Error(\n t(\"svc.bdsAlreadyRunning\", {\n pid: String(probe.pid),\n kind: t(probe.state === \"managed\" ? \"svc.running\" : \"svc.runningExternal\"),\n })\n );\n }\n }\n if (this.name === \"db\" || this.name === \"qq\") {\n const external = await findNodeServicePids(this.name);\n if (external.length > 0) {\n return { status: \"already\" };\n }\n }\n if (this.name === \"tunnel\" && (await probeTunnelRuntime())) return { status: \"already\" };\n if (this.def.validate) {\n const v = this.def.validate();\n if (v) {\n // optional\uFF1A\u914D\u7F6E\u5C42\u9762\u8DF3\u8FC7\uFF08\u5982 official \u4E0B llbot\uFF09\uFF0C\u4E0D\u5F53\u5931\u8D25\u3001\u4E5F\u4E0D spawn\n if (this.def.optional) return { status: \"skipped\", reason: v };\n throw new Error(v);\n }\n }\n if (this.def.beforeStart) {\n await this.def.beforeStart();\n }\n if (this.name === \"bds\") {\n await startModuleSidecars(ROOT, (line, stream) => this.pushLog(line, stream));\n }\n // beforeStart \u542B\u5F02\u6B65\u68C0\u67E5\uFF1B\u66F4\u65B0\u53EF\u80FD\u5728\u7B49\u5F85\u671F\u95F4\u5F00\u59CB\u3002\n if (this.updateInProgress) throw new Error(\"BDS \u6B63\u5728\u66F4\u65B0\uFF0C\u6682\u4E0D\u53EF\u542F\u52A8\");\n this.manualStop = false;\n /* \u5B50\u8FDB\u7A0B\u59CB\u7EC8\u7531\u5B88\u62A4\u8FDB\u7A0B\u7528\u7BA1\u9053\u6301\u6709\uFF08stdin \u53D1 stop\u3001stdout \u65E5\u5FD7\u3001autoRestart\uFF09\uFF1B\n * \u4E0D\u518D\u5BF9\u5B50\u670D\u52A1\u505A detached/unref\u2014\u2014\u8131\u79BB\u7EC8\u7AEF\u7531 daemon \u8FDB\u7A0B\u81EA\u8EAB\u8D1F\u8D23\u3002\n * windowsHide\uFF1AWindows \u4E0A\u907F\u514D\u4E3A\u6BCF\u4E2A\u670D\u52A1\u5F39\u51FA\u7A7A\u767D\u63A7\u5236\u53F0\uFF08stdout \u5DF2 pipe\uFF0C\u7A97\u5185\u65E0\u8F93\u51FA\uFF09\u3002 */\n const spawnOpts = {\n cwd: this.def.cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"] as Array<\"pipe\">,\n env: this.def.env ? { ...process.env, ...this.def.env } : process.env,\n windowsHide: true,\n };\n const child = this.def.service\n ? spawnService(this.def.service, this.def.args ?? [], spawnOpts)\n : spawn(this.def.cmd as string, this.def.args ?? [], spawnOpts);\n this.proc = child;\n this.pid = child.pid ?? 0;\n this.running = true;\n this.startTime = new Date();\n if (this.name === \"bds\" && this.pid > 0) {\n writeBdsPidFile(this.pid, ROOT);\n }\n if (this.name === \"tunnel\" && this.pid > 0) writeTunnelRuntime(this.pid);\n this.events.emit(\"output\", `started (PID ${this.pid})`, \"info\");\n this.events.emit(\"state\", { name: this.name, running: true, pid: this.pid });\n\n let lifecycleReported = false;\n const reportExit = (code?: number | null) => {\n if (this.name !== \"bds\" || lifecycleReported) return;\n lifecycleReported = true;\n const planned = this.manualStop || this.updateInProgress;\n void postBdsLifecycleEvent(planned ? \"stop\" : \"crash\", planned ? undefined : `code=${code ?? \"?\"}`, {\n port: dbHealthPort,\n });\n };\n if (this.name === \"bds\") {\n child.once(\"spawn\", () => {\n void postBdsLifecycleEvent(\"start\", undefined, { port: dbHealthPort });\n });\n }\n\n child.on(\"error\", (e) => {\n this.events.emit(\"output\", `process error: ${e.message}`, \"error\");\n reportExit();\n this.cleanup();\n this.scheduleRestart();\n });\n\n let versionLineBuffer = \"\";\n child.stdout?.on(\"data\", (d: Buffer) => {\n if (this.name === \"bds\" && this.startTime) {\n versionLineBuffer += d.toString();\n const versionLines = versionLineBuffer.split(/\\r?\\n/);\n versionLineBuffer = (versionLines.pop() ?? \"\").slice(-4096);\n for (const versionLine of versionLines) recordBdsVersion(ROOT, versionLine, this.pid, this.startTime.getTime());\n }\n for (const line of nonBlankOutputLines(d.toString())) {\n this.pushLog(line, \"stdout\");\n }\n });\n child.stderr?.on(\"data\", (d: Buffer) => {\n for (const line of nonBlankOutputLines(d.toString())) {\n this.pushLog(line, \"stderr\");\n }\n });\n\n child.on(\"exit\", (code) => {\n if (this.proc && this.proc !== child) return;\n this.events.emit(\"output\", `exited (code: ${code})`, \"info\");\n reportExit(code);\n this.cleanup();\n this.scheduleRestart();\n });\n return { status: \"started\" };\n }\n\n private scheduleRestart(): void {\n if (this.manualStop || this.updateInProgress || !this.def.autoRestart || this.restartTimer) return;\n this.restartTimer = setTimeout(() => {\n this.restartTimer = null;\n if (!this.updateInProgress && !this.manualStop && !this.running) {\n void this.start().catch((e: Error) => {\n this.events.emit(\"output\", `restart failed: ${e.message}`, \"error\");\n });\n }\n }, this.def.restartDelay);\n }\n\n /** \u66F4\u65B0\u5668\u72EC\u7ACB\u8FDB\u7A0B\u505C\u670D\u65F6\uFF0C\u7981\u6B62\u5C06\u8BA1\u5212\u5185\u9000\u51FA\u5F53\u4F5C\u5D29\u6E83\u5E76\u81EA\u52A8\u62C9\u8D77\u3002 */\n beginUpdate(): void {\n if (this.name !== \"bds\") return;\n if (this.updateInProgress) throw new Error(\"BDS \u66F4\u65B0\u5DF2\u5728\u8FDB\u884C\u4E2D\");\n this.updateInProgress = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n }\n\n endUpdate(): void {\n this.updateInProgress = false;\n }\n\n async stop(): Promise<void> {\n this.manualStop = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n if (!this.proc || !this.running) return;\n this.events.emit(\"output\", \"stopping...\", \"info\");\n\n if (this.def.stopCommand && this.proc.stdin) {\n this.proc.stdin.write(this.def.stopCommand + \"\\n\");\n } else {\n this.proc.kill(\"SIGTERM\");\n }\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n if (this.proc) {\n this.events.emit(\"output\", \"force kill\", \"error\");\n try {\n this.proc.kill(\"SIGKILL\");\n } catch {\n /* ignore */\n }\n }\n resolve();\n }, this.def.stopTimeout);\n\n this.proc?.on(\"exit\", () => {\n clearTimeout(timeout);\n resolve();\n });\n });\n }\n\n forceStop(): void {\n const child = this.proc;\n this.manualStop = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n this.cleanup();\n if (!child) return;\n try {\n child.kill(\"SIGKILL\");\n } catch {\n /* ignore */\n }\n }\n\n async restart(): Promise<StartOutcome> {\n await this.stop();\n return this.start();\n }\n\n getRecentLogs(n: number): LogLine[] {\n return this.logs.slice(-n);\n }\n\n /**\n * \u63A2\u6D4B\u53D1\u73B0\u8FDB\u7A0B\u5DF2\u6B7B\u540E\u56DE\u5199\u672C\u5730\u72B6\u6001\uFF08\u4E0D\u8BBE manualStop\uFF0C\u4EE5\u4FBF exit \u56DE\u8C03\u4ECD\u53EF\u6309\u9700 autoRestart\uFF09\u3002\n */\n markStoppedFromProbe(): void {\n if (!this.running && !this.proc) return;\n this.cleanup();\n }\n\n private cleanup(): void {\n const wasRunning = this.running || this.proc !== null;\n const exitingPid = this.pid;\n this.proc = null;\n this.running = false;\n this.pid = 0;\n this.startTime = null;\n if (this.name === \"bds\" && exitingPid > 0) {\n const filePid = readBdsPidFile(ROOT);\n if (filePid === exitingPid) {\n clearBdsPidFile(ROOT);\n }\n stopModuleSidecars();\n }\n if (this.name === \"tunnel\" && exitingPid > 0) clearTunnelRuntime(exitingPid);\n if (wasRunning) {\n this.events.emit(\"state\", { name: this.name, running: false, pid: 0 });\n }\n }\n}\n\nfunction createServices(): Record<ServiceName, Service> {\n /* \u5404\u670D\u52A1/CLI \u7528 SDK ensureCoreConfigs \u64AD\u79CD\uFF08\u542B $schema\uFF09\uFF0C\u4E0D\u518D\u4ECE configs-default \u62F7\u8D1D\u3002 */\n ensureCoreConfigs(ROOT, [\"bds_updater\", \"qq_config\", \"db_config\"]);\n ensurePackUpdateConfigFile();\n const bdsCfg = loadEnsuredConfig(ROOT, \"bds_updater.json\", \"bds_updater\", { ...DEFAULT_BDS_UPDATER_CONFIG } as Record<\n string,\n unknown\n >) as BdsUpdaterConfig;\n const qqCfg = loadEnsuredConfig(ROOT, \"qq_config.json\", \"qq_config\", { ...DEFAULT_QQ_CONFIG } as Record<\n string,\n unknown\n >) as QQBridgeConfig;\n const dbCfg = loadEnsuredConfig(ROOT, \"db_config.json\", \"db_config\", { ...DEFAULT_DB_CONFIG } as Record<\n string,\n unknown\n >) as DBConfig;\n const rawBdsPath = String(bdsCfg.bds_path ?? \"\").trim();\n if (process.platform !== \"win32\" && /^[A-Za-z]:[\\\\/]/.test(rawBdsPath)) {\n throw new Error(\n `configs/bds_updater.json \u7684 bds_path \u662F Windows \u8DEF\u5F84\uFF08${rawBdsPath}\uFF09\u3002` +\n `\u8BF7\u6539\u6210\u76F8\u5BF9 SFMC_ROOT \u7684\u8DEF\u5F84\uFF0C\u4F8B\u5982 \"BDS\"\u3002`\n );\n }\n const bdsPath = path.resolve(ROOT, rawBdsPath || \".\");\n const useLlbotBackend = qqCfg.qq_backend === \"llbot\";\n const qqEnabled = qqCfg.qq_enabled !== false;\n // \u5B98\u65B9 Webhook \u7531\u4E91\u7AEF\u63A5\u6536\uFF1B\u4EC5\u5207\u6362\u4F20\u8F93\u65B9\u5F0F\u5373\u53EF\u907F\u514D\u672C\u673A\u518D\u542F\u52A8\u4E00\u4E2A\u6865\u3002\n const qqExternal = !useLlbotBackend && qqCfg.official?.transport === \"webhook\";\n const tunnelCfg = qqCfg.official?.tunnel;\n const tunnelEnabled = qqEnabled && qqExternal && tunnelCfg?.enabled === true;\n const tunnelHost = String(tunnelCfg?.host ?? \"\").trim();\n const tunnelRemotePort = tunnelCfg?.remotePort ?? 13001;\n const llbotEnabled = qqCfg.llbot?.enabled !== false;\n const llbotLaunch = resolveLlbotLaunch(qqCfg.llbot?.path, qqCfg.llbot?.cwd);\n const llbotPath = llbotLaunch.exe;\n const llbotCwd = llbotLaunch.cwd;\n const dbPort = dbCfg.db_port ?? 3001;\n dbHealthPort = dbPort;\n qqBackendMode = useLlbotBackend ? \"llbot\" : \"official\";\n const bdsExe = bdsExePath(bdsPath);\n const qqTitle = useLlbotBackend ? \"QQ (llbot)\" : \"QQ (official)\";\n\n return {\n bds: new Service({\n name: \"bds\",\n title: \"BDS\",\n cmd: bdsExe,\n args: [],\n cwd: bdsPath,\n env: bdsSpawnEnvExtra(bdsPath),\n stopCommand: \"stop\",\n stopTimeout: 30000,\n /* \u5D29\u6E83\u62C9\u8D77\u7531 sfmc daemon \u56FA\u5B9A\u5F00\u542F\uFF1B\u4E0D\u518D\u8BFB bds_updater.crash_restart */\n autoRestart: true,\n restartDelay: 5000,\n validate: () => {\n if (!fs.existsSync(bdsExe)) return `not found: ${bdsExe}`;\n return null;\n },\n beforeStart: async () => {\n ensureBdsExecutable(bdsExe);\n /* \u5148\u88C5\u6536\u4EF6\u7BB1\u7B2C\u4E09\u65B9\u5305\uFF0C\u518D\u68C0\u67E5 CF \u66F4\u65B0\uFF0C\u518D\u8DD1\u6A21\u5757\u805A\u5408\u95F8\u95E8 */\n const { scanAndInstallInbox } = await import(\"./world-packs.js\");\n await scanAndInstallInbox({ interactive: false });\n const { runPackUpdatesOnBdsStart } = await import(\"./pack-update/index.js\");\n await runPackUpdatesOnBdsStart();\n const { ensurePacksReady } = await import(\"./pack-lifecycle.js\");\n await ensurePacksReady();\n },\n onLogLine: (line) =>\n reportBdsPlayerSession(line, {\n port: dbPort,\n ...(dbCfg.http_auth ? { authToken: dbCfg.http_auth } : {}),\n }),\n }),\n\n db: new Service({\n name: \"db\",\n title: \"DB Server\",\n service: \"db\",\n cwd: ROOT,\n stopTimeout: 10000,\n autoRestart: true,\n restartDelay: 3000,\n env: { DB_PORT: String(dbPort) },\n }),\n\n tunnel: new Service({\n name: \"tunnel\",\n title: \"QQ SSH Tunnel\",\n cmd: \"ssh\",\n args: [\n \"-N\", \"-T\", \"-o\", \"BatchMode=yes\", \"-o\", \"ConnectTimeout=10\",\n \"-o\", \"ExitOnForwardFailure=yes\", \"-o\", \"ServerAliveInterval=30\",\n \"-o\", \"ServerAliveCountMax=3\",\n \"-R\", `127.0.0.1:${tunnelRemotePort}:127.0.0.1:${dbPort}`,\n tunnelHost,\n ],\n cwd: ROOT,\n stopTimeout: 10000,\n autoRestart: true,\n restartDelay: 5000,\n optional: !tunnelEnabled,\n validate: () => {\n if (!tunnelEnabled) return \"QQ tunnel disabled (official webhook tunnel.enabled=false)\";\n if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/.test(tunnelHost)) return \"invalid official.tunnel.host\";\n if (!Number.isInteger(tunnelRemotePort) || tunnelRemotePort < 1 || tunnelRemotePort > 65535) {\n return \"invalid official.tunnel.remotePort\";\n }\n return null;\n },\n }),\n\n qq: new Service({\n name: \"qq\",\n title: qqTitle,\n service: \"qq\",\n cwd: ROOT,\n stopTimeout: 10000,\n // \u7F3A\u51ED\u636E\u65F6 validate \u62E6\u4E0B\uFF0C\u4E0D\u4F1A spawn\u2192exit(1)\u2192autoRestart \u6B7B\u5FAA\u73AF\n autoRestart: true,\n restartDelay: 3000,\n optional: !qqEnabled || qqExternal,\n validate: () => {\n if (!qqEnabled) return \"QQ bridge disabled (qq_enabled=false)\";\n if (qqExternal) return \"QQ bridge runs externally (official.transport=webhook)\";\n if (!useLlbotBackend) {\n if (!String(qqCfg.official?.app_id ?? \"\").trim() || !String(qqCfg.official?.app_secret ?? \"\").trim()) {\n return \"missing official.app_id / official.app_secret\";\n }\n }\n return null;\n },\n }),\n\n llbot: new Service({\n name: \"llbot\",\n title: \"LLBot\",\n cmd: llbotPath,\n args: [],\n cwd: llbotCwd,\n stopTimeout: 10000,\n autoRestart: false,\n restartDelay: 5000,\n // official \u6216\u672A\u542F\u7528\u65F6\u771F\u6B63 skip\uFF0C\u4E0D throw\n optional: !useLlbotBackend || !llbotEnabled,\n validate: () => {\n if (!useLlbotBackend) return \"LLBot skipped (qq_backend!=llbot)\";\n if (!llbotEnabled) return \"LLBot disabled (llbot.enabled=false)\";\n if (!fs.existsSync(llbotPath)) return `not found: ${llbotPath}`;\n return null;\n },\n }),\n };\n}\n\nexport let services: Record<ServiceName, Service> = createServices();\n\nexport function refreshServices(): void {\n forceStopAll();\n services = createServices();\n}\n\nexport const START_ORDER: ServiceName[] = [\"db\", \"tunnel\", \"qq\", \"llbot\", \"bds\"];\n\nexport async function startAll(): Promise<StartAllResult> {\n const result: StartAllResult = { started: [], skipped: [], failed: [] };\n for (const name of START_ORDER) {\n const svc = services[name];\n if (!svc) continue;\n try {\n const outcome = await svc.start();\n if (outcome.status === \"skipped\") {\n result.skipped.push({ name, reason: outcome.reason });\n svc.events.emit(\"output\", `skipped: ${outcome.reason}`, \"info\");\n } else {\n result.started.push(name);\n }\n } catch (e) {\n const reason = (e as Error).message;\n result.failed.push({ name, reason });\n svc.events.emit(\"output\", `start error: ${reason}`, \"error\");\n }\n }\n return result;\n}\n\nexport async function stopAll(): Promise<void> {\n const pending = [...START_ORDER]\n .reverse()\n .map((name) => services[name])\n .filter((service): service is Service => Boolean(service))\n .map((service) => service.stop());\n await Promise.allSettled(pending);\n}\n\nexport function forceStopAll(): void {\n for (const service of Object.values(services)) service.forceStop();\n}\n\nexport type ServiceStateEvent = { name: ServiceName; running: boolean; pid: number };\n\n/** \u8BA2\u9605\u4EFB\u610F\u670D\u52A1\u542F\u505C\uFF08\u542B\u63A2\u6D4B\u56DE\u5199\uFF09\uFF1B\u8FD4\u56DE\u53D6\u6D88\u51FD\u6570 */\nexport function onServiceStateChange(fn: (ev: ServiceStateEvent) => void): () => void {\n /* CLI \u8FDB\u7A0B\u4E0D\u6301\u6709 Service \u5B50\u8FDB\u7A0B\uFF1A\u6539\u8BA2\u5B88\u62A4\u8FDB\u7A0B\u63A8\u9001\u7684\u8FDC\u7A0B\u72B6\u6001\u4E8B\u4EF6 */\n if (!isDaemonServer()) {\n return onRemoteServiceStateChange(fn);\n }\n const handler = (ev: ServiceStateEvent): void => {\n fn(ev);\n };\n for (const service of Object.values(services)) {\n service.events.on(\"state\", handler);\n }\n return () => {\n for (const service of Object.values(services)) {\n service.events.off(\"state\", handler);\n }\n };\n}\n\n/** \u82E5\u672C\u5730\u6807\u8BB0 running \u4F46 OS \u8FDB\u7A0B\u5DF2\u6B7B\uFF0C\u56DE\u6536\u5185\u5B58\u6807\u5FD7 */\nasync function reconcileManagedAlive(service: Service): Promise<boolean> {\n if (!service.running) return false;\n if (service.pid > 0 && !(await isProcessAlive(service.pid))) {\n service.markStoppedFromProbe();\n return false;\n }\n return service.running;\n}\n\n/**\n * \u7EDF\u4E00\u8FD0\u884C\u6001\u67E5\u8BE2\uFF08\u6743\u5A01\u5165\u53E3\uFF09\uFF1AOS/\u5065\u5EB7\u63A2\u6D4B + \u56DE\u5199 Service \u5185\u5B58\u6807\u5FD7\u3002\n * status / Tab \u53D1\u9001\u76EE\u6807 / reload \u7B49\u5747\u5E94\u8D70\u6B64\u63A5\u53E3\uFF0C\u52FF\u76F4\u63A5\u8BFB `service.running`\u3002\n * CLI \u8FDB\u7A0B\u7ECF\u5B88\u62A4\u8FDB\u7A0B RPC \u83B7\u53D6\uFF1B\u5B88\u62A4\u8FDB\u7A0B\u5185\u8D70\u672C\u5730\u63A2\u6D3B\u3002\n */\nexport async function queryServicesRuntime(): Promise<ServiceStatus[]> {\n if (!isDaemonServer()) {\n const { queryRuntimeViaDaemon } = await import(\"./daemon/client.js\");\n return queryRuntimeViaDaemon();\n }\n return queryServicesRuntimeLocal();\n}\n\n/** \u5B88\u62A4\u8FDB\u7A0B\u5185\u672C\u5730\u63A2\u6D3B\u5B9E\u73B0\uFF08\u4EA6\u4F9B server status \u7EC4\u88C5 rows\uFF09 */\nexport async function queryServicesRuntimeLocal(): Promise<ServiceStatus[]> {\n return Promise.all(\n SERVICE_NAMES.map(async (name) => {\n const service = services[name];\n let running = false;\n let pid = 0;\n let uptime = \"\u2014\";\n let ownership: \"managed\" | \"external\" | undefined;\n\n if (name === \"bds\") {\n const probe = await probeBdsStatus({\n managedPid: service.pid,\n hasStdin: Boolean(service.proc?.stdin),\n rootDir: ROOT,\n });\n if (probe.state === \"managed\") {\n running = true;\n pid = probe.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n /* \u63A2\u6D4B\u4E3A managed \u4F46\u672C\u5730\u5DF2\u6807\u505C\uFF1A\u4FDD\u6301\u63A2\u6D4B\u7ED3\u679C\uFF0C\u4E0D\u5F3A\u5236\u6539\u5185\u5B58\uFF08stdin \u4ECD\u53EF\u7528\uFF09 */\n } else if (probe.state === \"external\") {\n running = true;\n pid = probe.pid;\n ownership = \"external\";\n /* \u5916\u90E8\u8FDB\u7A0B\uFF1A\u672C\u5730 managed \u53E5\u67C4\u5DF2\u5931\u6548\u5219\u56DE\u6536 */\n if (service.running && service.pid !== probe.pid) {\n service.markStoppedFromProbe();\n } else if (service.running && !(await isProcessAlive(service.pid))) {\n service.markStoppedFromProbe();\n }\n } else {\n running = false;\n if (service.running || service.proc) {\n service.markStoppedFromProbe();\n }\n }\n } else if (name === \"db\") {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n } else if (await probeDbHealth(dbHealthPort)) {\n running = true;\n ownership = \"external\";\n const pids = await findNodeServicePids(\"db\");\n pid = pids[0] ?? 0;\n }\n } else if (name === \"qq\") {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n } else {\n const probe = await probeQqRuntime();\n if (probe.alive) {\n running = true;\n pid = probe.pid;\n ownership = \"external\";\n } else {\n const pids = await findNodeServicePids(\"qq\");\n if (pids.length > 0) {\n running = true;\n pid = pids[0]!;\n ownership = \"external\";\n }\n }\n }\n } else if (name === \"tunnel\") {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n } else {\n pid = await probeTunnelRuntime();\n running = pid > 0;\n if (running) ownership = \"external\";\n }\n } else {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n }\n }\n\n return {\n name,\n title: service.title,\n running,\n pid,\n uptime,\n ...(ownership ? { ownership } : {}),\n };\n })\n );\n}\n\n/** \u5355\u670D\u52A1\u662F\u5426\u5728\u8DD1\uFF08\u542B\u5916\u90E8\u5B9E\u4F8B\uFF09 */\nexport async function isServiceRunning(name: ServiceName): Promise<boolean> {\n const rows = await queryServicesRuntime();\n return rows.some((r) => r.name === name && r.running);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["ROOT"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/world-packs.ts"],
4
- "sourcesContent": ["/**\n * world-packs.ts \u2014 \u901A\u7528\u4E16\u754C BP/RP \u7F16\u6392\uFF08\u6536\u4EF6\u7BB1\u5B89\u88C5 + packs CLI\uFF09\n * \u4E0E pack-lifecycle\uFF08\u6A21\u5757\u805A\u5408 BP/RP\uFF09\u804C\u8D23\u5206\u79BB\u3002\n */\nimport { confirm, isCancel, multiselect } from \"@clack/prompts\";\nimport {\n KNOWN_EXPERIMENTS,\n bumpPackPatchVersion,\n checkWorldExperimentsDiagnosis,\n checkWorldScriptPermissions,\n disableInstalledPack,\n enableInstalledPack,\n findInstalledPackById,\n installPackDirectory,\n isPackArchive,\n listInstalledWorldPacks,\n listWorldEnableListResult,\n readPackDependencyUuids,\n readPackManifestInfo,\n repairWorldPacksWiring,\n resolvePackRoots,\n uninstallInstalledPack,\n worldPackParentDir,\n type InstalledWorldPack,\n type PackManifestInfo,\n} from \"@sfmc-bds/bds-tools/world-packs\";\nimport crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { getLocale, t } from \"./i18n/index.js\";\nimport { pushLog, type LogLevel } from \"./logs.js\";\nimport { BP_NAME, RP_NAME, resolveBdsContext } from \"./pack-lifecycle.js\";\nimport {\n bindPackSource,\n bindingLabelForUuid,\n checkPackUpdates,\n formatSourcesList,\n getBinding,\n packSourcesPath,\n probeSourceAfterInstall,\n removeBinding,\n resolvePackTrashDir,\n resolveUninstallTrashDir,\n searchRemote,\n} from \"./pack-update/index.js\";\nimport { ROOT } from \"./runtime.js\";\nimport { c } from \"./theme.js\";\n\nexport const PACKS_CMD_NAMES = [\"packs\", \"addon\"] as const;\nexport const PACKS_SUBCOMMANDS = [\n \"list\",\n \"search\",\n \"enable\",\n \"disable\",\n \"uninstall\",\n \"bump\",\n \"install\",\n \"scan\",\n \"doctor\",\n \"path\",\n \"bind\",\n \"unbind\",\n \"sources\",\n \"check\",\n \"update\",\n] as const;\n\nfunction restartHint(): string {\n return t(\"packs.restartHint\");\n}\nfunction rejoinHint(): string {\n return t(\"packs.rejoinHint\");\n}\n\n/** \u4E16\u754C\u5305\u7F16\u6392\u65E5\u5FD7\uFF1A\u7EDF\u4E00\u8D70 sfmc \u805A\u5408\u5C42\uFF08source=pack\uFF09 */\nfunction logPack(text: string, level: LogLevel = \"info\"): void {\n pushLog(text, \"pack\", level);\n}\n\nexport function isPacksCommand(cmd: string | undefined): cmd is string {\n return !!cmd && (PACKS_CMD_NAMES as readonly string[]).includes(cmd);\n}\n\nexport function packsInboxDir(): string {\n return path.join(ROOT, \"packs\");\n}\n\nfunction doneDir(): string {\n return path.join(packsInboxDir(), \"_done\");\n}\n\nfunction failedDir(): string {\n return path.join(packsInboxDir(), \"_failed\");\n}\n\nfunction statePath(): string {\n return path.join(packsInboxDir(), \"inbox-state.json\");\n}\n\ninterface InboxState {\n installed: Record<string, { uuid: string; at: string; folderName?: string }>;\n}\n\nfunction ensureInboxLayout(): void {\n for (const d of [packsInboxDir(), doneDir(), failedDir(), resolvePackTrashDir()]) {\n fs.mkdirSync(d, { recursive: true });\n }\n}\n\nfunction readState(): InboxState {\n const file = statePath();\n if (!fs.existsSync(file)) return { installed: {} };\n try {\n const raw = JSON.parse(fs.readFileSync(file, \"utf8\")) as InboxState;\n return { installed: raw.installed ?? {} };\n } catch {\n return { installed: {} };\n }\n}\n\nfunction writeState(state: InboxState): void {\n ensureInboxLayout();\n fs.writeFileSync(statePath(), `${JSON.stringify(state, null, 2)}\\n`, \"utf8\");\n}\n\n/** \u6E90\u6307\u7EB9\uFF1A\u8DEF\u5F84 + size + mtime */\nfunction sourceFingerprint(absPath: string): string {\n const st = fs.statSync(absPath);\n const payload = `${absPath}|${st.size}|${st.mtimeMs}`;\n return crypto.createHash(\"sha1\").update(payload).digest(\"hex\");\n}\n\nfunction stampName(name: string): string {\n const safe = name.replace(/[<>:\"/\\\\|?*\\x00-\\x1f]/g, \"_\").slice(0, 80);\n const ts = new Date().toISOString().replace(/[:.]/g, \"-\");\n return `${ts}-${safe || \"pack\"}`;\n}\n\nfunction moveTo(src: string, destParent: string, label: string): string {\n fs.mkdirSync(destParent, { recursive: true });\n const dest = path.join(destParent, stampName(label));\n fs.renameSync(src, dest);\n return dest;\n}\n\nfunction fmtVer(v: [number, number, number]): string {\n return v.join(\".\");\n}\n\nfunction pruneInboxStateByUuid(uuid: string): void {\n const state = readState();\n let changed = false;\n for (const [fp, meta] of Object.entries(state.installed)) {\n if (meta.uuid.toLowerCase() === uuid.toLowerCase()) {\n delete state.installed[fp];\n changed = true;\n }\n }\n if (changed) writeState(state);\n}\n\n/** \u5E73\u53F0\u805A\u5408\u5305\u4E0D\u53EF\u7ECF packs uninstall \u5378\u51FA\uFF08\u7B56\u7565\u96C6\u4E2D\uFF0CCLI \u53EA\u6D88\u8D39\uFF09 */\nexport function isProtectedSfmcPack(pack: Pick<InstalledWorldPack, \"folderName\">): boolean {\n const folder = pack.folderName.toLowerCase();\n return folder === BP_NAME.toLowerCase() || folder === RP_NAME.toLowerCase();\n}\n\ntype UninstallOneOutcome =\n | { status: \"protected\"; folder: string }\n | { status: \"trashed\"; folder: string; dest: string }\n | { status: \"deleted\"; folder: string }\n | { status: \"missing\"; folder: string };\n\nfunction formatUninstallOutcome(r: UninstallOneOutcome): string {\n if (r.status === \"protected\") {\n return c.red(t(\"packs.uninstall.protected\", { folder: r.folder }));\n }\n if (r.status === \"trashed\") {\n return c.green(t(\"packs.uninstall.trashed\", { folder: r.folder, dest: r.dest }));\n }\n if (r.status === \"missing\") {\n return c.yellow(t(\"packs.uninstall.dirMissing\", { folder: r.folder }));\n }\n return c.green(t(\"packs.uninstall.deleted\", { folder: r.folder }));\n}\n\n/**\n * \u5378\u8F7D\u5355\u4E2A\u5DF2\u88C5\u4E16\u754C\u5305\uFF08disable + \u79FB\u56DE\u6536\u7AD9/\u5220\u9664\uFF09\u3002\n * \u4E0D\u8D1F\u8D23\u914D\u5BF9 RP / \u7ED1\u5B9A \u2014\u2014 \u7531\u4E0A\u5C42\u7F16\u6392\u3002\u8FD4\u56DE\u7ED3\u6784\u5316\u7ED3\u679C\uFF08DIP\uFF1A\u5C55\u793A\u4E0E\u7B56\u7565\u5206\u79BB\uFF09\u3002\n */\nasync function uninstallOneInstalledPack(\n pack: InstalledWorldPack,\n opts: { bdsRoot: string; levelName: string; purge: boolean }\n): Promise<UninstallOneOutcome> {\n if (isProtectedSfmcPack(pack)) {\n return { status: \"protected\", folder: pack.folderName };\n }\n const trash = resolveUninstallTrashDir({ purge: opts.purge });\n const r = await uninstallInstalledPack({\n bdsRoot: opts.bdsRoot,\n levelName: opts.levelName,\n pack,\n trashDir: trash,\n });\n pruneInboxStateByUuid(pack.uuid);\n if (r.action === \"trashed\") {\n return { status: \"trashed\", folder: pack.folderName, dest: r.dest };\n }\n if (r.action === \"missing\") {\n return { status: \"missing\", folder: pack.folderName };\n }\n return { status: \"deleted\", folder: pack.folderName };\n}\n\n/** \u89E3\u6790 BP \u7684\u914D\u5BF9 RP\uFF08--no-paired \u65F6\u8DF3\u8FC7\uFF09 */\nfunction resolvePairedResource(\n pack: InstalledWorldPack,\n packs: InstalledWorldPack[],\n noPaired: boolean\n): InstalledWorldPack | null {\n if (pack.kind !== \"behavior\" || noPaired) return null;\n const binding = getBinding(pack.uuid);\n const pairedUuid =\n binding?.pairedResourceUuid ?? (fs.existsSync(pack.dir) ? readPackDependencyUuids(pack.dir)[0] : undefined) ?? null;\n if (!pairedUuid) return null;\n return packs.find((p) => p.kind === \"resource\" && p.uuid.toLowerCase() === pairedUuid.toLowerCase()) ?? null;\n}\n\nfunction formatUninstallPickLabel(pack: InstalledWorldPack): string {\n const kind = pack.kind === \"behavior\" ? \"BP\" : \"RP\";\n const en = pack.enabled ? t(\"packs.list.on\") : t(\"packs.list.off\");\n return `[${kind}] ${pack.name} ${pack.folderName} ${en.trim()} ${pack.uuid.slice(0, 8)}`;\n}\n\n/**\n * \u6309 CLI id \u5217\u8868\u89E3\u6790\u5F85\u5378\u8F7D\u5305\uFF08DIP\uFF1A\u89E3\u6790\u4E0E TTY/\u786E\u8BA4/\u6267\u884C\u5206\u79BB\uFF0C\u4FBF\u4E8E\u5355\u6D4B\uFF09\u3002\n * - \u4EFB\u4E00 id \u627E\u4E0D\u5230 \u2192 not_found\n * - \u4EFB\u4E00\u547D\u4E2D\u53D7\u4FDD\u62A4\u5305 \u2192 protected\uFF08\u4E0E\u5355 id \u4E2D\u6B62\u5951\u7EA6\u4E00\u81F4\uFF0CLSP\uFF09\n */\nexport function resolveUninstallTargets(\n packs: InstalledWorldPack[],\n ids: string[]\n):\n | { status: \"ok\"; selected: InstalledWorldPack[] }\n | { status: \"not_found\"; missing: string[] }\n | { status: \"protected\"; folder: string } {\n const missing: string[] = [];\n const byUuid = new Map<string, InstalledWorldPack>();\n for (const id of ids) {\n const pack = findInstalledPackById(packs, id);\n if (!pack) {\n missing.push(id);\n continue;\n }\n byUuid.set(pack.uuid.toLowerCase(), pack);\n }\n if (missing.length) return { status: \"not_found\", missing };\n const selected = [...byUuid.values()];\n const blocked = selected.find((p) => isProtectedSfmcPack(p));\n if (blocked) return { status: \"protected\", folder: blocked.folderName };\n return { status: \"ok\", selected };\n}\n\n/** TTY \u591A\u9009\u5F85\u5378\u8F7D\u5305\uFF1B\u53D6\u6D88\u8FD4\u56DE null\uFF1B\u65E0\u53EF\u5378\u9879\u8FD4\u56DE [] */\nasync function pickPacksForUninstall(packs: InstalledWorldPack[]): Promise<InstalledWorldPack[] | null> {\n const candidates = packs.filter((p) => !isProtectedSfmcPack(p));\n if (candidates.length === 0) return [];\n const bp = candidates.filter((p) => p.kind === \"behavior\");\n const rp = candidates.filter((p) => p.kind === \"resource\");\n const options = [\n ...bp.map((p) => ({ value: p.uuid, label: formatUninstallPickLabel(p) })),\n ...rp.map((p) => ({ value: p.uuid, label: formatUninstallPickLabel(p) })),\n ];\n const selected = await multiselect({\n message: t(\"packs.uninstall.pick\"),\n options,\n required: true,\n });\n if (isCancel(selected)) return null;\n const set = new Set(selected as string[]);\n return candidates.filter((p) => set.has(p.uuid));\n}\n\n/**\n * \u5C55\u5F00\u914D\u5BF9 RP \u540E\u6279\u91CF\u5378\u8F7D\uFF1B\u53EF\u9009\u4EA4\u4E92\u786E\u8BA4\u3002\n * \u540C\u4E00 uuid \u53EA\u5378\u4E00\u6B21\uFF08\u7528\u6237\u540C\u65F6\u52FE\u9009 BP+\u914D\u5BF9 RP \u65F6\u4E0D\u91CD\u590D\uFF09\u3002\n */\nasync function uninstallPacksBatch(\n selected: InstalledWorldPack[],\n allPacks: InstalledWorldPack[],\n opts: {\n bdsRoot: string;\n levelName: string;\n purge: boolean;\n noPaired: boolean;\n askConfirm: boolean;\n }\n): Promise<string> {\n if (selected.length === 0) return c.yellow(t(\"packs.uninstall.none\"));\n\n type QueueItem = { pack: InstalledWorldPack; pairedOf?: string };\n const queue: QueueItem[] = [];\n const seen = new Set<string>();\n for (const pack of selected) {\n const key = pack.uuid.toLowerCase();\n if (seen.has(key)) continue;\n seen.add(key);\n queue.push({ pack });\n /* LSP\uFF1A\u53D7\u4FDD\u62A4\u4E3B\u5305\u4E0D\u5C55\u5F00\u914D\u5BF9 RP */\n if (isProtectedSfmcPack(pack)) continue;\n const paired = resolvePairedResource(pack, allPacks, opts.noPaired);\n if (paired) {\n const pk = paired.uuid.toLowerCase();\n if (!seen.has(pk)) {\n seen.add(pk);\n queue.push({ pack: paired, pairedOf: pack.folderName });\n }\n }\n }\n\n if (opts.askConfirm && process.stdin.isTTY) {\n const preview = queue\n .map((q) => {\n const kind = q.pack.kind === \"behavior\" ? \"BP\" : \"RP\";\n const extra = q.pairedOf ? ` ${c.dim(t(\"packs.uninstall.pairedOf\", { folder: q.pairedOf }))}` : \"\";\n return ` [${kind}] ${q.pack.folderName}${extra}`;\n })\n .join(\"\\n\");\n logPack(t(\"packs.uninstall.summary\", { count: queue.length }), \"info\");\n logPack(preview, \"info\");\n const ans = await confirm({\n message: t(\"packs.uninstall.confirm\", { count: String(queue.length) }),\n initialValue: false,\n });\n if (isCancel(ans) || !ans) return c.dim(t(\"packs.uninstall.cancelled\"));\n }\n\n const lines: string[] = [];\n for (const item of queue) {\n const outcome = await uninstallOneInstalledPack(item.pack, opts);\n lines.push(formatUninstallOutcome(outcome));\n /* \u4EC5\u4E3B\u5305\u5B9E\u9645\u5378\u8F7D\u540E\u6E05\u7ED1\u5B9A\uFF08\u53D7\u4FDD\u62A4\u5305 status=protected\uFF0C\u4E0D\u78B0 binding\uFF09 */\n if (item.pack.kind === \"behavior\" && outcome.status !== \"protected\") {\n const removed = removeBinding(item.pack.uuid);\n if (removed) {\n lines.push(c.dim(t(\"packs.uninstall.bindingRemoved\", { uuid: item.pack.uuid })));\n }\n }\n }\n lines.push(c.yellow(restartHint()));\n return lines.join(\"\\n\");\n}\n\nfunction printConflict(existing: PackManifestInfo & { dir: string }, incoming: PackManifestInfo): void {\n logPack(c.yellow(t(\"packs.conflict\")), \"warn\");\n logPack(\n t(\"packs.conflict.existing\", {\n name: existing.name,\n version: fmtVer(existing.version),\n uuid: existing.uuid,\n dir: existing.dir,\n }),\n \"info\"\n );\n logPack(\n t(\"packs.conflict.incoming\", {\n name: incoming.name,\n version: fmtVer(incoming.version),\n uuid: incoming.uuid,\n }),\n \"info\"\n );\n}\n\nasync function askOverwrite(): Promise<boolean> {\n if (!process.stdin.isTTY) return false;\n const ans = await confirm({\n message: t(\"packs.overwriteAsk\"),\n initialValue: false,\n });\n if (isCancel(ans)) return false;\n return !!ans;\n}\n\nasync function installOnePackRoot(opts: {\n srcDir: string;\n bdsRoot: string;\n levelName: string;\n folderHint?: string;\n force?: boolean;\n interactive?: boolean;\n}): Promise<{ ok: boolean; skipped?: boolean; reason?: string; info?: PackManifestInfo }> {\n let info: PackManifestInfo | null;\n try {\n info = readPackManifestInfo(opts.srcDir);\n } catch {\n return { ok: false, reason: t(\"packs.badManifest\") };\n }\n if (!info) {\n return { ok: false, reason: t(\"packs.badManifest\") };\n }\n const destParent = worldPackParentDir(opts.bdsRoot, opts.levelName, info.kind);\n let force = !!opts.force;\n\n let result = await installPackDirectory({\n srcDir: opts.srcDir,\n destParent,\n force: false,\n ...(opts.folderHint ? { folderName: opts.folderHint } : {}),\n });\n\n if (result.conflict) {\n printConflict(result.conflict.existing, result.conflict.incoming);\n if (force) {\n /* \u663E\u5F0F --force */\n } else if (opts.interactive && process.stdin.isTTY) {\n force = await askOverwrite();\n if (!force) {\n logPack(c.dim(t(\"packs.skippedNoOverwrite\")), \"info\");\n return { ok: false, skipped: true, reason: \"conflict:skipped\", info };\n }\n } else {\n logPack(c.yellow(t(\"packs.conflictSkip\", { name: info.name })), \"warn\");\n return { ok: false, skipped: true, reason: \"conflict:noninteractive\", info };\n }\n result = await installPackDirectory({\n srcDir: opts.srcDir,\n destParent,\n force: true,\n /* \u8DEF\u5F84\u7531\u540C uuid \u51B3\u7B56\u9501\u5B9A\uFF0C\u4E0D\u4F9D\u8D56 folderHint\uFF0C\u907F\u514D\u65C1\u8DEF\u76EE\u5F55 */\n });\n }\n\n if (!result.ok || !result.info || !result.destDir) {\n return { ok: false, reason: result.reason ?? \"install failed\", info };\n }\n\n await enableInstalledPack({\n bdsRoot: opts.bdsRoot,\n levelName: opts.levelName,\n info: result.info,\n });\n\n /* \u5B89\u88C5\u6210\u529F\u540E\u63A2\u6D4B CF \u66F4\u65B0\u6E90\uFF08\u4EC5 BP\uFF09 */\n try {\n await probeSourceAfterInstall({\n info: result.info,\n packDir: result.destDir,\n ...(result.folderName ? { folderName: result.folderName } : {}),\n ...(opts.interactive !== undefined ? { interactive: opts.interactive } : {}),\n });\n } catch (e) {\n logPack(c.yellow(`probe: ${(e as Error).message}`), \"warn\");\n }\n\n return { ok: true, info: result.info };\n}\n\n/** \u6536\u96C6\u6536\u4EF6\u7BB1\u5019\u9009\uFF08\u8DF3\u8FC7 _done / _failed / \u72B6\u6001\u6587\u4EF6\uFF09 */\nfunction listInboxCandidates(): string[] {\n const inbox = packsInboxDir();\n if (!fs.existsSync(inbox)) return [];\n const out: string[] = [];\n for (const e of fs.readdirSync(inbox, { withFileTypes: true })) {\n if (e.name.startsWith(\"_\") || e.name === \"inbox-state.json\") continue;\n const full = path.join(inbox, e.name);\n if (e.isDirectory()) out.push(full);\n else if (e.isFile() && isPackArchive(full)) out.push(full);\n }\n return out;\n}\n\n/**\n * \u626B\u63CF\u6536\u4EF6\u7BB1\u5E76\u5B89\u88C5\u3002\u7A7A\u6536\u4EF6\u7BB1\u65F6\u4E0D\u6253\u65E5\u5FD7\uFF08\u542F\u52A8\u94A9\u5B50\u53CB\u597D\uFF09\u3002\n */\nexport async function scanAndInstallInbox(opts?: {\n dryRun?: boolean;\n force?: boolean;\n interactive?: boolean;\n}): Promise<{ installed: number; skipped: number; failed: number }> {\n ensureInboxLayout();\n const candidates = listInboxCandidates();\n if (candidates.length === 0) {\n return { installed: 0, skipped: 0, failed: 0 };\n }\n\n let bdsRoot: string;\n let levelName: string;\n try {\n ({ bdsRoot, levelName } = resolveBdsContext());\n } catch (e) {\n logPack(c.yellow(t(\"packs.inboxSkip\", { message: (e as Error).message })), \"warn\");\n return { installed: 0, skipped: 0, failed: candidates.length };\n }\n\n const interactive = opts?.interactive ?? !!process.stdin.isTTY;\n const force = !!opts?.force;\n const dryRun = !!opts?.dryRun;\n const state = readState();\n let installed = 0;\n let skipped = 0;\n let failed = 0;\n\n logPack(c.dim(t(\"packs.inboxFound\", { count: candidates.length, level: levelName })));\n\n for (const src of candidates) {\n const base = path.basename(src);\n const fp = sourceFingerprint(src);\n if (state.installed[fp] && !force) {\n logPack(c.dim(t(\"packs.alreadyDone\", { name: base })));\n skipped++;\n if (!dryRun) {\n try {\n moveTo(src, doneDir(), base);\n } catch {\n /* \u53EF\u80FD\u4ECD\u88AB\u5360\u7528 */\n }\n }\n continue;\n }\n\n let disposeRoots: (() => void) | null = null;\n try {\n let roots: string[] = [];\n if (fs.statSync(src).isDirectory() || isPackArchive(src)) {\n if (dryRun && isPackArchive(src) && fs.statSync(src).isFile()) {\n logPack(c.dim(t(\"packs.dryRunExtract\", { name: base })));\n skipped++;\n continue;\n }\n const resolved = await resolvePackRoots(src);\n disposeRoots = resolved.dispose;\n roots = resolved.roots;\n } else {\n failed++;\n if (!dryRun) moveTo(src, failedDir(), base);\n continue;\n }\n\n if (roots.length === 0) {\n logPack(c.red(t(\"packs.noPackRoot\", { name: base })), \"error\");\n failed++;\n if (!dryRun && fs.statSync(src).isDirectory()) moveTo(src, failedDir(), `${base}-no-root`);\n continue;\n }\n\n if (dryRun) {\n for (const r of roots) {\n try {\n const info = readPackManifestInfo(r);\n logPack(c.dim(`dry-run: ${base} \u2192 ${info?.kind ?? \"?\"} ${info?.name ?? path.basename(r)}`), \"info\");\n } catch {\n logPack(c.red(t(\"packs.badManifest\")), \"error\");\n }\n }\n skipped++;\n continue;\n }\n\n let okCount = 0;\n let skipCount = 0;\n let failCount = 0;\n let lastUuid = \"\";\n for (const root of roots) {\n const folderHint =\n roots.length === 1 && fs.statSync(src).isFile() ? path.basename(src, path.extname(src)) : path.basename(root);\n const r = await installOnePackRoot({\n srcDir: root,\n bdsRoot,\n levelName,\n folderHint,\n force,\n interactive,\n });\n if (r.ok && r.info) {\n okCount++;\n lastUuid = r.info.uuid;\n logPack(\n c.green(\n t(\"packs.installedEnabled\", {\n kind: r.info.kind === \"resource\" ? \"RP\" : \"BP\",\n name: r.info.name,\n version: fmtVer(r.info.version),\n })\n ),\n \"success\"\n );\n } else if (r.skipped) {\n skipCount++;\n } else {\n failCount++;\n logPack(c.red(t(\"packs.installFailed\", { name: path.basename(root), reason: r.reason ?? \"?\" })), \"error\");\n }\n }\n\n installed += okCount > 0 ? 1 : 0;\n skipped += skipCount;\n failed += failCount;\n\n if (okCount > 0) {\n state.installed[fp] = { uuid: lastUuid, at: new Date().toISOString(), folderName: base };\n writeState(state);\n moveTo(src, doneDir(), base);\n logPack(c.yellow(`${restartHint()}`), \"warn\");\n } else if (failCount > 0 && skipCount === 0) {\n moveTo(src, failedDir(), base);\n }\n /* \u4EC5\u51B2\u7A81\u8DF3\u8FC7\uFF1A\u4FDD\u7559\u5728\u6536\u4EF6\u7BB1\uFF0C\u4FBF\u4E8E\u4EA4\u4E92\u65F6\u518D\u5904\u7406 */\n } catch (e) {\n logPack(c.red(`${base}: ${(e as Error).message}`), \"error\");\n failed++;\n if (!dryRun && fs.existsSync(src)) {\n try {\n moveTo(src, failedDir(), base);\n } catch {\n /* ignore */\n }\n }\n } finally {\n if (disposeRoots) disposeRoots();\n }\n }\n\n return { installed, skipped, failed };\n}\n\nfunction filterPacks(packs: InstalledWorldPack[], kind: \"bp\" | \"rp\" | \"all\", search?: string): InstalledWorldPack[] {\n let list = packs;\n if (kind === \"bp\") list = list.filter((p) => p.kind === \"behavior\");\n if (kind === \"rp\") list = list.filter((p) => p.kind === \"resource\");\n if (search) {\n const q = search.toLowerCase();\n list = list.filter(\n (p) =>\n p.name.toLowerCase().includes(q) || p.folderName.toLowerCase().includes(q) || p.uuid.toLowerCase().includes(q)\n );\n }\n return list;\n}\n\n/** \u672A\u7ED1\u5B9A\u6765\u6E90\u7684\u5360\u4F4D\u7B26\u53EA\u7528\u4E8E\u5185\u90E8\u8BED\u4E49\uFF0C\u4E0D\u5E94\u4F5C\u4E3A\u91CD\u590D\u7684\u5217\u8868\u5217\u5C55\u793A\u3002 */\nexport function shouldDisplayPackSourceLabel(sourceLabel: string): boolean {\n return sourceLabel !== \"src=-\";\n}\n\nfunction formatPackList(packs: InstalledWorldPack[]): string {\n if (packs.length === 0) return c.dim(t(\"packs.listEmpty\"));\n const lines: string[] = [];\n const bp = packs.filter((p) => p.kind === \"behavior\");\n const rp = packs.filter((p) => p.kind === \"resource\");\n if (bp.length) {\n lines.push(c.bold(t(\"packs.list.bpHeader\")));\n for (const p of bp) {\n const en = p.enabled ? c.green(t(\"packs.list.on\")) : c.dim(t(\"packs.list.off\"));\n const src = bindingLabelForUuid(p.uuid);\n const sourceColumn = shouldDisplayPackSourceLabel(src) ? ` ${c.dim(src)}` : \"\";\n lines.push(` [${en}] ${p.folderName} ${p.name} v${fmtVer(p.version)} ${c.dim(p.uuid)}${sourceColumn}`);\n }\n }\n if (rp.length) {\n lines.push(c.bold(t(\"packs.list.rpHeader\")));\n for (const p of rp) {\n const en = p.enabled ? c.green(t(\"packs.list.on\")) : c.dim(t(\"packs.list.off\"));\n lines.push(` [${en}] ${p.folderName} ${p.name} v${fmtVer(p.version)} ${c.dim(p.uuid)}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\nfunction parseListFlags(args: string[]): { kind: \"bp\" | \"rp\" | \"all\"; search?: string } {\n let kind: \"bp\" | \"rp\" | \"all\" = \"all\";\n let search: string | undefined;\n for (let i = 0; i < args.length; i++) {\n const a = args[i];\n if (a === \"--kind\" && args[i + 1]) {\n const k = args[++i]!.toLowerCase();\n if (k === \"bp\" || k === \"behavior\") kind = \"bp\";\n else if (k === \"rp\" || k === \"resource\") kind = \"rp\";\n else kind = \"all\";\n } else if (a === \"--search\" && args[i + 1]) {\n search = args[++i];\n } else if (a && !a.startsWith(\"-\")) {\n search = a;\n }\n }\n return { kind, ...(search ? { search } : {}) };\n}\n\nfunction hasFlag(args: string[], name: string): boolean {\n return args.includes(name);\n}\n\nfunction formatExperimentsStatus(levelName: string, knownExperiments: Record<string, boolean>): string {\n const isZh = getLocale() === \"zh-CN\";\n const lines = [t(\"packs.doctor.experimentsStatus\", { level: levelName })];\n for (const exp of KNOWN_EXPERIMENTS) {\n const on = !!knownExperiments[exp.id];\n const marker = on ? c.green(\"\u25CF\") : c.dim(\"\u25CB\");\n const stateText = on ? c.green(isZh ? \"\u5DF2\u5F00\u542F\" : \"enabled\") : c.dim(isZh ? \"\u672A\u5F00\u542F\" : \"disabled\");\n const nameStr = isZh ? `${exp.nameZh} (${exp.nameEn})` : exp.nameEn;\n lines.push(` ${marker} ${nameStr}: ${stateText}`);\n }\n return lines.join(\"\\n\");\n}\n\nasync function cmdDoctor(args: string[] = []): Promise<string> {\n const isFix = hasFlag(args, \"--fix\") || hasFlag(args, \"-y\") || hasFlag(args, \"fix\");\n const isExperimentsOnly = hasFlag(args, \"--experiments\") || hasFlag(args, \"experiments\");\n const isAllExperiments = hasFlag(args, \"--all-experiments\") || hasFlag(args, \"--all-exp\");\n const expArg = args.find((a) => a.startsWith(\"--experiments=\"));\n\n const { bdsRoot, levelName } = resolveBdsContext();\n const packs = listInstalledWorldPacks(bdsRoot, levelName);\n const expDiag = checkWorldExperimentsDiagnosis(bdsRoot, levelName, packs);\n\n // \u4EC5\u67E5\u8BE2\u5B9E\u9A8C\u6027\u73A9\u6CD5\u72B6\u6001\uFF08\u4F8B\u5982 packs doctor --experiments\uFF09\n if (isExperimentsOnly && !isFix && !isAllExperiments && !expArg) {\n if (!expDiag.levelDatExists || !expDiag.experimentsInfo) {\n return c.yellow(`level.dat not found for world \u300C${levelName}\u300D: ${expDiag.levelDatPath}`);\n }\n const statusText = formatExperimentsStatus(levelName, expDiag.experimentsInfo.knownExperiments);\n return `${statusText}\\n\\n ${c.cyan(t(\"packs.doctor.experimentsFixPrompt\"))}`;\n }\n\n // \u5224\u5B9A\u662F\u5426\u9700\u8981\u5F00\u542F\u5B9E\u9A8C\u6027\u529F\u80FD\n let requestedExperiments: string[] | \"all\" | undefined = undefined;\n if (isAllExperiments || expArg === \"--experiments=all\") {\n requestedExperiments = \"all\";\n } else if (expArg) {\n const rawList = expArg\n .slice(\"--experiments=\".length)\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n if (rawList.length > 0) {\n requestedExperiments = rawList;\n }\n }\n\n if (isFix || requestedExperiments !== undefined) {\n const shouldFixBeta = !expDiag.betaApis.hasBetaApis && expDiag.betaApis.requiringPacks.length > 0;\n const repRes = await repairWorldPacksWiring(bdsRoot, levelName, {\n fixBetaApis: shouldFixBeta,\n ...(requestedExperiments !== undefined ? { enableExperiments: requestedExperiments } : {}),\n });\n\n const fixLogs: string[] = [];\n if (repRes.experimentsResult?.attempted) {\n if (repRes.experimentsResult.changed) {\n const isZh = getLocale() === \"zh-CN\";\n const enabledNames = repRes.experimentsResult.enabled\n .map((id) => {\n const def = KNOWN_EXPERIMENTS.find((x) => x.id === id);\n return def ? (isZh ? def.nameZh : def.nameEn) : id;\n })\n .join(\", \");\n fixLogs.push(\n c.green(\n t(\"packs.doctor.experimentsFixed\", {\n level: levelName,\n list: enabledNames || \"all\",\n backup: repRes.experimentsResult.backupPath ?? \"level.dat.bak\",\n })\n )\n );\n } else if (repRes.experimentsResult.error) {\n fixLogs.push(\n c.red(t(\"packs.doctor.experimentsFailed\", { level: levelName, error: repRes.experimentsResult.error }))\n );\n } else {\n fixLogs.push(c.dim(t(\"packs.doctor.experimentsAlreadyOn\", { level: levelName })));\n }\n } else if (repRes.betaApisResult?.attempted) {\n if (repRes.betaApisResult.changed) {\n fixLogs.push(\n c.green(\n t(\"packs.doctor.betaApisFixed\", {\n level: levelName,\n backup: repRes.betaApisResult.backupPath ?? \"level.dat.bak\",\n })\n )\n );\n } else if (repRes.betaApisResult.error) {\n fixLogs.push(c.red(t(\"packs.doctor.betaApisFailed\", { level: levelName, error: repRes.betaApisResult.error })));\n } else {\n fixLogs.push(c.dim(t(\"packs.doctor.betaApisAlreadyOn\", { level: levelName })));\n }\n }\n\n for (const g of repRes.cleanedGhostPacks) {\n fixLogs.push(c.green(t(\"packs.doctor.ghostPackCleaned\", { kind: g.kind, uuid: g.uuid })));\n }\n\n for (const s of repRes.syncedVersions) {\n fixLogs.push(\n c.green(\n t(\"packs.doctor.versionSynced\", {\n kind: s.kind,\n folder: s.folder,\n listVer: s.listVer,\n diskVer: s.diskVer,\n })\n )\n );\n }\n\n if (repRes.fixedPermissions && repRes.fixedPermissions.length > 0) {\n fixLogs.push(\n c.green(\n t(\"packs.doctor.permissionsFixed\", {\n perms: repRes.fixedPermissions.map((x) => `${x.packName} (${x.moduleName})`).join(\", \"),\n })\n )\n );\n }\n\n const totalHealed =\n (repRes.experimentsResult?.changed ? 1 : 0) +\n (!repRes.experimentsResult && repRes.betaApisResult?.changed ? 1 : 0) +\n repRes.cleanedGhostPacks.length +\n repRes.syncedVersions.length +\n (repRes.fixedPermissions?.length ?? 0);\n\n if (totalHealed > 0) {\n return (\n fixLogs.map((x) => ` - ${x}`).join(\"\\n\") +\n \"\\n\" +\n c.green(t(\"packs.doctor.fixedSummary\", { count: totalHealed }))\n );\n } else if (fixLogs.length > 0) {\n return fixLogs.map((x) => ` - ${x}`).join(\"\\n\");\n }\n }\n\n const issues: string[] = [];\n let fixableCount = 0;\n\n for (const kind of [\"behavior\", \"resource\"] as const) {\n // \u7ECF listWorldEnableListResult \u2192 pack-manager\uFF08DRY/Demeter\uFF1B\u4FDD\u7559 parseFail \u4FE1\u53F7 \u2014 LSP\uFF09\n const snap = listWorldEnableListResult(bdsRoot, levelName, kind);\n if (snap.parseFailedFile) {\n issues.push(t(\"packs.doctor.parseFail\", { file: path.basename(snap.parseFailedFile) }));\n }\n const byUuid = new Map(packs.filter((p) => p.kind === kind).map((p) => [p.uuid, p]));\n for (const e of snap.entries) {\n const p = byUuid.get(e.pack_id);\n if (!p) {\n issues.push(t(\"packs.doctor.missingDir\", { kind, uuid: e.pack_id }));\n fixableCount++;\n continue;\n }\n const ev = e.version;\n if (\n Array.isArray(ev) &&\n ev.length >= 3 &&\n (ev[0] !== p.version[0] || ev[1] !== p.version[1] || ev[2] !== p.version[2])\n ) {\n issues.push(\n t(\"packs.doctor.versionMismatch\", {\n kind,\n folder: p.folderName,\n listVer: ev.join(\".\"),\n diskVer: fmtVer(p.version),\n })\n );\n fixableCount++;\n }\n }\n }\n\n // Beta APIs \u6821\u9A8C\n if (expDiag.betaApis.requiringPacks.length > 0 && !expDiag.betaApis.hasBetaApis) {\n const packLines = expDiag.betaApis.requiringPacks\n .map((rp) => {\n const depStr = rp.betaDependencies.map((d) => `${d.module_name ?? d.uuid}@${d.version ?? \"\"}`).join(\", \");\n const name =\n !rp.pack.name || rp.pack.name.startsWith(\"pack.\") || rp.pack.name === \"pack\"\n ? rp.pack.folderName\n : `${rp.pack.name} (${rp.pack.folderName})`;\n return ` * ${name} (v${fmtVer(rp.pack.version)}) -> \u4F9D\u8D56 ${depStr}`;\n })\n .join(\"\\n\");\n issues.push(t(\"packs.doctor.betaApisMissing\", { level: levelName, packs: packLines }));\n fixableCount++;\n }\n\n // \u539F\u751F\u811A\u672C\u6A21\u5757\u6743\u9650\u6821\u9A8C\uFF08\u5982 @minecraft/server-net, @minecraft/diagnostics \u7B49\uFF09\n const permDiag = checkWorldScriptPermissions(bdsRoot, levelName, packs);\n if (permDiag.missingPermissions.length > 0) {\n const list = permDiag.missingPermissions\n .map((m) => {\n const name =\n !m.pack.name || m.pack.name.startsWith(\"pack.\") || m.pack.name === \"pack\"\n ? m.pack.folderName\n : `${m.pack.name} (${m.pack.folderName})`;\n return ` * ${name} -> \u4F9D\u8D56\u6A21\u5757 ${m.moduleName}`;\n })\n .join(\"\\n\");\n issues.push(t(\"packs.doctor.permissionsMissing\", { list }));\n fixableCount++;\n }\n\n // \u683C\u5F0F\u5316\u5F53\u524D\u4E16\u754C\u7684\u5B9E\u9A8C\u6027\u5F00\u5173\u72B6\u6001\n let expStatusSection = \"\";\n if (expDiag.levelDatExists && expDiag.experimentsInfo) {\n expStatusSection = \"\\n\\n\" + formatExperimentsStatus(levelName, expDiag.experimentsInfo.knownExperiments);\n }\n\n if (issues.length === 0) {\n return c.green(t(\"packs.doctor.ok\")) + expStatusSection;\n }\n\n let res = t(\"packs.doctor.found\", {\n count: issues.length,\n list: issues.map((x) => ` - ${x}`).join(\"\\n\"),\n });\n res += expStatusSection;\n if (fixableCount > 0 && !isFix) {\n res += \"\\n\\n \" + c.cyan(t(\"packs.doctor.fixPrompt\"));\n res += \"\\n \" + c.dim(t(\"packs.doctor.experimentsFixPrompt\"));\n }\n return res;\n}\n\nexport async function dispatchPacksCommand(sub: string | undefined, args: string[]): Promise<string> {\n const verb = (sub ?? \"\").toLowerCase();\n if (!verb || verb === \"help\" || verb === \"-h\" || verb === \"--help\") {\n return packsUsage();\n }\n\n try {\n switch (verb) {\n case \"list\": {\n const { bdsRoot, levelName } = resolveBdsContext();\n const flags = parseListFlags(args);\n const packs = filterPacks(listInstalledWorldPacks(bdsRoot, levelName), flags.kind, flags.search);\n return formatPackList(packs);\n }\n case \"search\": {\n const q = args[0];\n if (!q) return c.yellow(t(\"packs.search.usage\"));\n return await searchRemote(q);\n }\n case \"bind\": {\n const packId = args[0];\n const ref = args[1];\n if (!packId || !ref) return c.yellow(t(\"packs.bind.usage\"));\n return await bindPackSource(packId, ref);\n }\n case \"unbind\": {\n const packId = args[0];\n if (!packId) return c.yellow(t(\"packs.unbind.usage\"));\n const { bdsRoot, levelName } = resolveBdsContext();\n const packs = listInstalledWorldPacks(bdsRoot, levelName);\n const pack = findInstalledPackById(packs, packId);\n const uuid = pack?.uuid ?? packId;\n const ok = removeBinding(uuid);\n return ok\n ? c.green(t(\"packs.unbindOk\", { uuid, path: packSourcesPath() }))\n : c.yellow(t(\"packs.unbindMiss\", { id: packId }));\n }\n case \"sources\":\n return formatSourcesList();\n case \"check\": {\n const id = args.find((a) => !a.startsWith(\"-\"));\n return await checkPackUpdates({ ...(id ? { packId: id } : {}), apply: false });\n }\n case \"update\": {\n const all = hasFlag(args, \"--all\");\n const id = args.find((a) => !a.startsWith(\"-\") && a !== \"--all\");\n if (!all && !id) return c.yellow(t(\"packs.update.usage\"));\n return await checkPackUpdates({\n ...(id ? { packId: id } : {}),\n apply: true,\n });\n }\n case \"enable\":\n case \"disable\": {\n const id = args[0];\n if (!id) return c.yellow(t(\"packs.toggle.usage\", { verb }));\n const { bdsRoot, levelName } = resolveBdsContext();\n const packs = listInstalledWorldPacks(bdsRoot, levelName);\n const pack = findInstalledPackById(packs, id);\n if (!pack) return c.red(t(\"packs.notFound\", { id }));\n if (verb === \"enable\") {\n await enableInstalledPack({\n bdsRoot,\n levelName,\n info: {\n name: pack.name,\n uuid: pack.uuid,\n version: pack.version,\n kind: pack.kind,\n },\n });\n } else {\n await disableInstalledPack({\n bdsRoot,\n levelName,\n kind: pack.kind,\n packUuid: pack.uuid,\n version: pack.version,\n });\n }\n return c.green(t(\"packs.toggleDone\", { verb, folder: pack.folderName, hint: restartHint() }));\n }\n case \"uninstall\": {\n const ids = args.filter((a) => !a.startsWith(\"-\"));\n const purge = hasFlag(args, \"--purge\");\n const noPaired = hasFlag(args, \"--no-paired\");\n const { bdsRoot, levelName } = resolveBdsContext();\n const packs = listInstalledWorldPacks(bdsRoot, levelName);\n\n let selected: InstalledWorldPack[] = [];\n if (ids.length === 0) {\n if (!process.stdin.isTTY) return c.yellow(t(\"packs.uninstall.usage\"));\n const picked = await pickPacksForUninstall(packs);\n if (picked === null) return c.dim(t(\"packs.uninstall.cancelled\"));\n if (picked.length === 0) return c.yellow(t(\"packs.uninstall.none\"));\n selected = picked;\n } else {\n /* \u5355/\u591A id \u5171\u7528\u89E3\u6790\u539F\u8BED\uFF08LSP\uFF1A\u4EFB\u4E00\u53D7\u4FDD\u62A4\u5305\u6574\u4F53\u4E2D\u6B62\uFF09 */\n const resolved = resolveUninstallTargets(packs, ids);\n if (resolved.status === \"not_found\") {\n return c.red(t(\"packs.notFound\", { id: resolved.missing.join(\", \") }));\n }\n if (resolved.status === \"protected\") {\n return c.red(t(\"packs.uninstall.protected\", { folder: resolved.folder }));\n }\n selected = resolved.selected;\n }\n\n return await uninstallPacksBatch(selected, packs, {\n bdsRoot,\n levelName,\n purge,\n noPaired,\n /* \u591A\u9009\u6216\u4E00\u6B21\u5378\u591A\u4E2A\u65F6\u786E\u8BA4\uFF1B\u5355 id \u4FDD\u6301\u539F\u300C\u76F4\u63A5\u5378\u300D\u624B\u611F */\n askConfirm: ids.length !== 1,\n });\n }\n case \"bump\": {\n const id = args[0];\n if (!id) return c.yellow(t(\"packs.bump.usage\"));\n const { bdsRoot, levelName } = resolveBdsContext();\n const packs = listInstalledWorldPacks(bdsRoot, levelName);\n const pack = findInstalledPackById(packs, id);\n if (!pack) return c.red(t(\"packs.notFound\", { id }));\n if (pack.kind !== \"resource\") {\n return c.red(t(\"packs.bump.rpOnly\"));\n }\n const next = bumpPackPatchVersion(pack.dir);\n if (pack.enabled) {\n await enableInstalledPack({\n bdsRoot,\n levelName,\n info: {\n name: pack.name,\n uuid: pack.uuid,\n version: next,\n kind: \"resource\",\n },\n });\n }\n return c.green(\n t(\"packs.bumped\", {\n folder: pack.folderName,\n version: fmtVer(next),\n restart: restartHint(),\n rejoin: rejoinHint(),\n })\n );\n }\n case \"install\": {\n const force = hasFlag(args, \"--force\");\n const inbox = hasFlag(args, \"--inbox\") || args.length === 0 || args.every((a) => a.startsWith(\"-\"));\n if (inbox) {\n const r = await scanAndInstallInbox({ force, interactive: true });\n return c.dim(\n t(\"packs.installInbox\", {\n installed: r.installed,\n skipped: r.skipped,\n failed: r.failed,\n })\n );\n }\n const target = args.find((a) => !a.startsWith(\"-\"));\n if (!target) return c.yellow(t(\"packs.install.usage\"));\n const abs = path.resolve(target);\n if (!fs.existsSync(abs)) return c.red(t(\"packs.pathMissing\", { path: abs }));\n const { bdsRoot, levelName } = resolveBdsContext();\n let disposeRoots: (() => void) | null = null;\n try {\n if (!fs.statSync(abs).isDirectory() && !isPackArchive(abs)) {\n return c.red(t(\"packs.notArchive\"));\n }\n const resolved = await resolvePackRoots(abs);\n disposeRoots = resolved.dispose;\n const roots = resolved.roots;\n if (roots.length === 0) return c.red(t(\"packs.noManifestRoot\"));\n const lines: string[] = [];\n for (const root of roots) {\n const r = await installOnePackRoot({\n srcDir: root,\n bdsRoot,\n levelName,\n folderHint: path.basename(root),\n force,\n interactive: true,\n });\n if (r.ok && r.info) {\n lines.push(\n c.green(\n t(\"packs.installedLine\", {\n name: r.info.name,\n version: fmtVer(r.info.version),\n kind: r.info.kind,\n })\n )\n );\n } else {\n lines.push(\n c.yellow(\n t(\"packs.skipFailLine\", {\n name: path.basename(root),\n reason: r.reason ?? \"?\",\n })\n )\n );\n }\n }\n lines.push(c.yellow(restartHint()));\n return lines.join(\"\\n\");\n } finally {\n if (disposeRoots) disposeRoots();\n }\n }\n case \"scan\": {\n const force = hasFlag(args, \"--force\");\n const dryRun = hasFlag(args, \"--dry-run\");\n const r = await scanAndInstallInbox({\n force,\n dryRun,\n interactive: !!process.stdin.isTTY,\n });\n if (r.installed === 0 && r.skipped === 0 && r.failed === 0) {\n return c.dim(t(\"packs.inboxEmpty\"));\n }\n return c.dim(\n t(\"packs.scanResult\", {\n installed: r.installed,\n skipped: r.skipped,\n failed: r.failed,\n })\n );\n }\n case \"doctor\":\n return await cmdDoctor(args);\n case \"path\": {\n ensureInboxLayout();\n const { bdsRoot, levelName } = resolveBdsContext();\n return [\n `bdsRoot: ${bdsRoot}`,\n `level: ${levelName}`,\n `behavior: ${worldPackParentDir(bdsRoot, levelName, \"behavior\")}`,\n `resource: ${worldPackParentDir(bdsRoot, levelName, \"resource\")}`,\n `inbox: ${packsInboxDir()}`,\n `trash: ${resolvePackTrashDir()}`,\n `build: ${path.join(packsInboxDir(), \"_build\")}`,\n `sources: ${packSourcesPath()}`,\n ].join(\"\\n\");\n }\n default:\n return c.red(t(\"packs.unknownSub\", { verb })) + packsUsage();\n }\n } catch (e) {\n return c.red((e as Error).message);\n }\n}\n\nexport function packsUsage(): string {\n return `${t(\"packs.usage.title\", { cmd: c.bold(\"sfmc packs\"), alias: c.green(\"addon\") })}\n\n ${c.green(\"packs list\")} [--kind bp|rp|all] [--search q]\n ${c.green(\"packs search\")} <q> ${t(\"packs.usage.cfSearch\")}\n ${c.green(\"packs bind\")} <id> <project|slug|url>\n ${c.green(\"packs unbind\")} <id>\n ${c.green(\"packs sources\")}\n ${c.green(\"packs check\")} [id]\n ${c.green(\"packs update\")} <id|--all>\n ${c.green(\"packs enable|disable\")} <uuid|folder>\n ${c.green(\"packs uninstall\")} [id...] [--purge] [--no-paired]\n ${c.green(\"packs bump\")} <id> ${t(\"packs.usage.bump\")}\n ${c.green(\"packs install\")} [path|--inbox] [--force]\n ${c.green(\"packs scan\")} [--force] [--dry-run]\n ${c.green(\"packs doctor\")} [--fix] [--all-experiments] [--experiments]\n ${c.green(\"packs path\")}\n\n${t(\"packs.usage.inbox\", { path: packsInboxDir() })}\n${t(\"packs.usage.trash\", { path: resolvePackTrashDir() })}\n${t(\"packs.usage.sources\", { path: packSourcesPath() })}\n${t(\"packs.usage.enableNote\", { hint: restartHint() })}\n${t(\"packs.usage.moduleNote\", { cmd: c.green(\"sfmc mod\") })}`;\n}\n"],
4
+ "sourcesContent": ["/**\n * world-packs.ts \u2014 \u901A\u7528\u4E16\u754C BP/RP \u7F16\u6392\uFF08\u6536\u4EF6\u7BB1\u5B89\u88C5 + packs CLI\uFF09\n * \u4E0E pack-lifecycle\uFF08\u6A21\u5757\u805A\u5408 BP/RP\uFF09\u804C\u8D23\u5206\u79BB\u3002\n */\nimport { confirm, isCancel, multiselect } from \"@clack/prompts\";\nimport {\n KNOWN_EXPERIMENTS,\n bumpPackPatchVersion,\n checkWorldExperimentsDiagnosis,\n checkWorldScriptPermissions,\n disableInstalledPack,\n enableInstalledPack,\n findInstalledPackById,\n installPackDirectory,\n isPackArchive,\n listInstalledWorldPacks,\n listWorldEnableListResult,\n readPackDependencyUuids,\n readPackManifestInfo,\n repairWorldPacksWiring,\n resolvePackRoots,\n uninstallInstalledPack,\n worldPackParentDir,\n type InstalledWorldPack,\n type PackManifestInfo,\n} from \"@sfmc-bds/bds-tools/world-packs\";\nimport crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { getLocale, t } from \"./i18n/index.js\";\nimport { pushLog, type LogLevel } from \"./logs.js\";\nimport { BP_NAME, RP_NAME, resolveBdsContext } from \"./pack-lifecycle.js\";\nimport {\n bindPackSource,\n bindingLabelForUuid,\n checkPackUpdates,\n formatSourcesList,\n getBinding,\n packSourcesPath,\n probeSourceAfterInstall,\n removeBinding,\n resolvePackTrashDir,\n resolveUninstallTrashDir,\n searchRemote,\n} from \"./pack-update/index.js\";\nimport { ROOT } from \"./runtime.js\";\nimport { c } from \"./theme.js\";\n\nexport const PACKS_CMD_NAMES = [\"packs\", \"addon\"] as const;\nexport const PACKS_SUBCOMMANDS = [\n \"list\",\n \"search\",\n \"enable\",\n \"disable\",\n \"uninstall\",\n \"bump\",\n \"install\",\n \"scan\",\n \"doctor\",\n \"path\",\n \"bind\",\n \"unbind\",\n \"sources\",\n \"check\",\n \"update\",\n] as const;\n\nfunction restartHint(): string {\n return t(\"packs.restartHint\");\n}\nfunction rejoinHint(): string {\n return t(\"packs.rejoinHint\");\n}\n\n/** \u8D44\u6E90\u5305\u7F16\u6392\u65E5\u5FD7\uFF1A\u7EDF\u4E00\u8D70 sfmc \u805A\u5408\u5C42\uFF08source=pack\uFF09 */\nfunction logPack(text: string, level: LogLevel = \"info\"): void {\n pushLog(text, \"pack\", level);\n}\n\nexport function isPacksCommand(cmd: string | undefined): cmd is string {\n return !!cmd && (PACKS_CMD_NAMES as readonly string[]).includes(cmd);\n}\n\nexport function packsInboxDir(): string {\n return path.join(ROOT, \"packs\");\n}\n\nfunction doneDir(): string {\n return path.join(packsInboxDir(), \"_done\");\n}\n\nfunction failedDir(): string {\n return path.join(packsInboxDir(), \"_failed\");\n}\n\nfunction statePath(): string {\n return path.join(packsInboxDir(), \"inbox-state.json\");\n}\n\ninterface InboxState {\n installed: Record<string, { uuid: string; at: string; folderName?: string }>;\n}\n\nfunction ensureInboxLayout(): void {\n for (const d of [packsInboxDir(), doneDir(), failedDir(), resolvePackTrashDir()]) {\n fs.mkdirSync(d, { recursive: true });\n }\n}\n\nfunction readState(): InboxState {\n const file = statePath();\n if (!fs.existsSync(file)) return { installed: {} };\n try {\n const raw = JSON.parse(fs.readFileSync(file, \"utf8\")) as InboxState;\n return { installed: raw.installed ?? {} };\n } catch {\n return { installed: {} };\n }\n}\n\nfunction writeState(state: InboxState): void {\n ensureInboxLayout();\n fs.writeFileSync(statePath(), `${JSON.stringify(state, null, 2)}\\n`, \"utf8\");\n}\n\n/** \u6E90\u6307\u7EB9\uFF1A\u8DEF\u5F84 + size + mtime */\nfunction sourceFingerprint(absPath: string): string {\n const st = fs.statSync(absPath);\n const payload = `${absPath}|${st.size}|${st.mtimeMs}`;\n return crypto.createHash(\"sha1\").update(payload).digest(\"hex\");\n}\n\nfunction stampName(name: string): string {\n const safe = name.replace(/[<>:\"/\\\\|?*\\x00-\\x1f]/g, \"_\").slice(0, 80);\n const ts = new Date().toISOString().replace(/[:.]/g, \"-\");\n return `${ts}-${safe || \"pack\"}`;\n}\n\nfunction moveTo(src: string, destParent: string, label: string): string {\n fs.mkdirSync(destParent, { recursive: true });\n const dest = path.join(destParent, stampName(label));\n fs.renameSync(src, dest);\n return dest;\n}\n\nfunction fmtVer(v: [number, number, number]): string {\n return v.join(\".\");\n}\n\nfunction pruneInboxStateByUuid(uuid: string): void {\n const state = readState();\n let changed = false;\n for (const [fp, meta] of Object.entries(state.installed)) {\n if (meta.uuid.toLowerCase() === uuid.toLowerCase()) {\n delete state.installed[fp];\n changed = true;\n }\n }\n if (changed) writeState(state);\n}\n\n/** \u5E73\u53F0\u805A\u5408\u5305\u4E0D\u53EF\u7ECF packs uninstall \u5378\u51FA\uFF08\u7B56\u7565\u96C6\u4E2D\uFF0CCLI \u53EA\u6D88\u8D39\uFF09 */\nexport function isProtectedSfmcPack(pack: Pick<InstalledWorldPack, \"folderName\">): boolean {\n const folder = pack.folderName.toLowerCase();\n return folder === BP_NAME.toLowerCase() || folder === RP_NAME.toLowerCase();\n}\n\ntype UninstallOneOutcome =\n | { status: \"protected\"; folder: string }\n | { status: \"trashed\"; folder: string; dest: string }\n | { status: \"deleted\"; folder: string }\n | { status: \"missing\"; folder: string };\n\nfunction formatUninstallOutcome(r: UninstallOneOutcome): string {\n if (r.status === \"protected\") {\n return c.red(t(\"packs.uninstall.protected\", { folder: r.folder }));\n }\n if (r.status === \"trashed\") {\n return c.green(t(\"packs.uninstall.trashed\", { folder: r.folder, dest: r.dest }));\n }\n if (r.status === \"missing\") {\n return c.yellow(t(\"packs.uninstall.dirMissing\", { folder: r.folder }));\n }\n return c.green(t(\"packs.uninstall.deleted\", { folder: r.folder }));\n}\n\n/**\n * \u5378\u8F7D\u5355\u4E2A\u5DF2\u88C5\u8D44\u6E90\u5305\uFF08disable + \u79FB\u56DE\u6536\u7AD9/\u5220\u9664\uFF09\u3002\n * \u4E0D\u8D1F\u8D23\u914D\u5BF9 RP / \u7ED1\u5B9A \u2014\u2014 \u7531\u4E0A\u5C42\u7F16\u6392\u3002\u8FD4\u56DE\u7ED3\u6784\u5316\u7ED3\u679C\uFF08DIP\uFF1A\u5C55\u793A\u4E0E\u7B56\u7565\u5206\u79BB\uFF09\u3002\n */\nasync function uninstallOneInstalledPack(\n pack: InstalledWorldPack,\n opts: { bdsRoot: string; levelName: string; purge: boolean }\n): Promise<UninstallOneOutcome> {\n if (isProtectedSfmcPack(pack)) {\n return { status: \"protected\", folder: pack.folderName };\n }\n const trash = resolveUninstallTrashDir({ purge: opts.purge });\n const r = await uninstallInstalledPack({\n bdsRoot: opts.bdsRoot,\n levelName: opts.levelName,\n pack,\n trashDir: trash,\n });\n pruneInboxStateByUuid(pack.uuid);\n if (r.action === \"trashed\") {\n return { status: \"trashed\", folder: pack.folderName, dest: r.dest };\n }\n if (r.action === \"missing\") {\n return { status: \"missing\", folder: pack.folderName };\n }\n return { status: \"deleted\", folder: pack.folderName };\n}\n\n/** \u89E3\u6790 BP \u7684\u914D\u5BF9 RP\uFF08--no-paired \u65F6\u8DF3\u8FC7\uFF09 */\nfunction resolvePairedResource(\n pack: InstalledWorldPack,\n packs: InstalledWorldPack[],\n noPaired: boolean\n): InstalledWorldPack | null {\n if (pack.kind !== \"behavior\" || noPaired) return null;\n const binding = getBinding(pack.uuid);\n const pairedUuid =\n binding?.pairedResourceUuid ?? (fs.existsSync(pack.dir) ? readPackDependencyUuids(pack.dir)[0] : undefined) ?? null;\n if (!pairedUuid) return null;\n return packs.find((p) => p.kind === \"resource\" && p.uuid.toLowerCase() === pairedUuid.toLowerCase()) ?? null;\n}\n\nfunction formatUninstallPickLabel(pack: InstalledWorldPack): string {\n const kind = pack.kind === \"behavior\" ? \"BP\" : \"RP\";\n const en = pack.enabled ? t(\"packs.list.on\") : t(\"packs.list.off\");\n return `[${kind}] ${pack.name} ${pack.folderName} ${en.trim()} ${pack.uuid.slice(0, 8)}`;\n}\n\n/**\n * \u6309 CLI id \u5217\u8868\u89E3\u6790\u5F85\u5378\u8F7D\u5305\uFF08DIP\uFF1A\u89E3\u6790\u4E0E TTY/\u786E\u8BA4/\u6267\u884C\u5206\u79BB\uFF0C\u4FBF\u4E8E\u5355\u6D4B\uFF09\u3002\n * - \u4EFB\u4E00 id \u627E\u4E0D\u5230 \u2192 not_found\n * - \u4EFB\u4E00\u547D\u4E2D\u53D7\u4FDD\u62A4\u5305 \u2192 protected\uFF08\u4E0E\u5355 id \u4E2D\u6B62\u5951\u7EA6\u4E00\u81F4\uFF0CLSP\uFF09\n */\nexport function resolveUninstallTargets(\n packs: InstalledWorldPack[],\n ids: string[]\n):\n | { status: \"ok\"; selected: InstalledWorldPack[] }\n | { status: \"not_found\"; missing: string[] }\n | { status: \"protected\"; folder: string } {\n const missing: string[] = [];\n const byUuid = new Map<string, InstalledWorldPack>();\n for (const id of ids) {\n const pack = findInstalledPackById(packs, id);\n if (!pack) {\n missing.push(id);\n continue;\n }\n byUuid.set(pack.uuid.toLowerCase(), pack);\n }\n if (missing.length) return { status: \"not_found\", missing };\n const selected = [...byUuid.values()];\n const blocked = selected.find((p) => isProtectedSfmcPack(p));\n if (blocked) return { status: \"protected\", folder: blocked.folderName };\n return { status: \"ok\", selected };\n}\n\n/** TTY \u591A\u9009\u5F85\u5378\u8F7D\u5305\uFF1B\u53D6\u6D88\u8FD4\u56DE null\uFF1B\u65E0\u53EF\u5378\u9879\u8FD4\u56DE [] */\nasync function pickPacksForUninstall(packs: InstalledWorldPack[]): Promise<InstalledWorldPack[] | null> {\n const candidates = packs.filter((p) => !isProtectedSfmcPack(p));\n if (candidates.length === 0) return [];\n const bp = candidates.filter((p) => p.kind === \"behavior\");\n const rp = candidates.filter((p) => p.kind === \"resource\");\n const options = [\n ...bp.map((p) => ({ value: p.uuid, label: formatUninstallPickLabel(p) })),\n ...rp.map((p) => ({ value: p.uuid, label: formatUninstallPickLabel(p) })),\n ];\n const selected = await multiselect({\n message: t(\"packs.uninstall.pick\"),\n options,\n required: true,\n });\n if (isCancel(selected)) return null;\n const set = new Set(selected as string[]);\n return candidates.filter((p) => set.has(p.uuid));\n}\n\n/**\n * \u5C55\u5F00\u914D\u5BF9 RP \u540E\u6279\u91CF\u5378\u8F7D\uFF1B\u53EF\u9009\u4EA4\u4E92\u786E\u8BA4\u3002\n * \u540C\u4E00 uuid \u53EA\u5378\u4E00\u6B21\uFF08\u7528\u6237\u540C\u65F6\u52FE\u9009 BP+\u914D\u5BF9 RP \u65F6\u4E0D\u91CD\u590D\uFF09\u3002\n */\nasync function uninstallPacksBatch(\n selected: InstalledWorldPack[],\n allPacks: InstalledWorldPack[],\n opts: {\n bdsRoot: string;\n levelName: string;\n purge: boolean;\n noPaired: boolean;\n askConfirm: boolean;\n }\n): Promise<string> {\n if (selected.length === 0) return c.yellow(t(\"packs.uninstall.none\"));\n\n type QueueItem = { pack: InstalledWorldPack; pairedOf?: string };\n const queue: QueueItem[] = [];\n const seen = new Set<string>();\n for (const pack of selected) {\n const key = pack.uuid.toLowerCase();\n if (seen.has(key)) continue;\n seen.add(key);\n queue.push({ pack });\n /* LSP\uFF1A\u53D7\u4FDD\u62A4\u4E3B\u5305\u4E0D\u5C55\u5F00\u914D\u5BF9 RP */\n if (isProtectedSfmcPack(pack)) continue;\n const paired = resolvePairedResource(pack, allPacks, opts.noPaired);\n if (paired) {\n const pk = paired.uuid.toLowerCase();\n if (!seen.has(pk)) {\n seen.add(pk);\n queue.push({ pack: paired, pairedOf: pack.folderName });\n }\n }\n }\n\n if (opts.askConfirm && process.stdin.isTTY) {\n const preview = queue\n .map((q) => {\n const kind = q.pack.kind === \"behavior\" ? \"BP\" : \"RP\";\n const extra = q.pairedOf ? ` ${c.dim(t(\"packs.uninstall.pairedOf\", { folder: q.pairedOf }))}` : \"\";\n return ` [${kind}] ${q.pack.folderName}${extra}`;\n })\n .join(\"\\n\");\n logPack(t(\"packs.uninstall.summary\", { count: queue.length }), \"info\");\n logPack(preview, \"info\");\n const ans = await confirm({\n message: t(\"packs.uninstall.confirm\", { count: String(queue.length) }),\n initialValue: false,\n });\n if (isCancel(ans) || !ans) return c.dim(t(\"packs.uninstall.cancelled\"));\n }\n\n const lines: string[] = [];\n for (const item of queue) {\n const outcome = await uninstallOneInstalledPack(item.pack, opts);\n lines.push(formatUninstallOutcome(outcome));\n /* \u4EC5\u4E3B\u5305\u5B9E\u9645\u5378\u8F7D\u540E\u6E05\u7ED1\u5B9A\uFF08\u53D7\u4FDD\u62A4\u5305 status=protected\uFF0C\u4E0D\u78B0 binding\uFF09 */\n if (item.pack.kind === \"behavior\" && outcome.status !== \"protected\") {\n const removed = removeBinding(item.pack.uuid);\n if (removed) {\n lines.push(c.dim(t(\"packs.uninstall.bindingRemoved\", { uuid: item.pack.uuid })));\n }\n }\n }\n lines.push(c.yellow(restartHint()));\n return lines.join(\"\\n\");\n}\n\nfunction printConflict(existing: PackManifestInfo & { dir: string }, incoming: PackManifestInfo): void {\n logPack(c.yellow(t(\"packs.conflict\")), \"warn\");\n logPack(\n t(\"packs.conflict.existing\", {\n name: existing.name,\n version: fmtVer(existing.version),\n uuid: existing.uuid,\n dir: existing.dir,\n }),\n \"info\"\n );\n logPack(\n t(\"packs.conflict.incoming\", {\n name: incoming.name,\n version: fmtVer(incoming.version),\n uuid: incoming.uuid,\n }),\n \"info\"\n );\n}\n\nasync function askOverwrite(): Promise<boolean> {\n if (!process.stdin.isTTY) return false;\n const ans = await confirm({\n message: t(\"packs.overwriteAsk\"),\n initialValue: false,\n });\n if (isCancel(ans)) return false;\n return !!ans;\n}\n\nasync function installOnePackRoot(opts: {\n srcDir: string;\n bdsRoot: string;\n levelName: string;\n folderHint?: string;\n force?: boolean;\n interactive?: boolean;\n}): Promise<{ ok: boolean; skipped?: boolean; reason?: string; info?: PackManifestInfo }> {\n let info: PackManifestInfo | null;\n try {\n info = readPackManifestInfo(opts.srcDir);\n } catch {\n return { ok: false, reason: t(\"packs.badManifest\") };\n }\n if (!info) {\n return { ok: false, reason: t(\"packs.badManifest\") };\n }\n const destParent = worldPackParentDir(opts.bdsRoot, opts.levelName, info.kind);\n let force = !!opts.force;\n\n let result = await installPackDirectory({\n srcDir: opts.srcDir,\n destParent,\n force: false,\n ...(opts.folderHint ? { folderName: opts.folderHint } : {}),\n });\n\n if (result.conflict) {\n printConflict(result.conflict.existing, result.conflict.incoming);\n if (force) {\n /* \u663E\u5F0F --force */\n } else if (opts.interactive && process.stdin.isTTY) {\n force = await askOverwrite();\n if (!force) {\n logPack(c.dim(t(\"packs.skippedNoOverwrite\")), \"info\");\n return { ok: false, skipped: true, reason: \"conflict:skipped\", info };\n }\n } else {\n logPack(c.yellow(t(\"packs.conflictSkip\", { name: info.name })), \"warn\");\n return { ok: false, skipped: true, reason: \"conflict:noninteractive\", info };\n }\n result = await installPackDirectory({\n srcDir: opts.srcDir,\n destParent,\n force: true,\n /* \u8DEF\u5F84\u7531\u540C uuid \u51B3\u7B56\u9501\u5B9A\uFF0C\u4E0D\u4F9D\u8D56 folderHint\uFF0C\u907F\u514D\u65C1\u8DEF\u76EE\u5F55 */\n });\n }\n\n if (!result.ok || !result.info || !result.destDir) {\n return { ok: false, reason: result.reason ?? \"install failed\", info };\n }\n\n await enableInstalledPack({\n bdsRoot: opts.bdsRoot,\n levelName: opts.levelName,\n info: result.info,\n });\n\n /* \u5B89\u88C5\u6210\u529F\u540E\u63A2\u6D4B CF \u66F4\u65B0\u6E90\uFF08\u4EC5 BP\uFF09 */\n try {\n await probeSourceAfterInstall({\n info: result.info,\n packDir: result.destDir,\n ...(result.folderName ? { folderName: result.folderName } : {}),\n ...(opts.interactive !== undefined ? { interactive: opts.interactive } : {}),\n });\n } catch (e) {\n logPack(c.yellow(`probe: ${(e as Error).message}`), \"warn\");\n }\n\n return { ok: true, info: result.info };\n}\n\n/** \u6536\u96C6\u6536\u4EF6\u7BB1\u5019\u9009\uFF08\u8DF3\u8FC7 _done / _failed / \u72B6\u6001\u6587\u4EF6\uFF09 */\nfunction listInboxCandidates(): string[] {\n const inbox = packsInboxDir();\n if (!fs.existsSync(inbox)) return [];\n const out: string[] = [];\n for (const e of fs.readdirSync(inbox, { withFileTypes: true })) {\n if (e.name.startsWith(\"_\") || e.name === \"inbox-state.json\") continue;\n const full = path.join(inbox, e.name);\n if (e.isDirectory()) out.push(full);\n else if (e.isFile() && isPackArchive(full)) out.push(full);\n }\n return out;\n}\n\n/**\n * \u626B\u63CF\u6536\u4EF6\u7BB1\u5E76\u5B89\u88C5\u3002\u7A7A\u6536\u4EF6\u7BB1\u65F6\u4E0D\u6253\u65E5\u5FD7\uFF08\u542F\u52A8\u94A9\u5B50\u53CB\u597D\uFF09\u3002\n */\nexport async function scanAndInstallInbox(opts?: {\n dryRun?: boolean;\n force?: boolean;\n interactive?: boolean;\n}): Promise<{ installed: number; skipped: number; failed: number }> {\n ensureInboxLayout();\n const candidates = listInboxCandidates();\n if (candidates.length === 0) {\n return { installed: 0, skipped: 0, failed: 0 };\n }\n\n let bdsRoot: string;\n let levelName: string;\n try {\n ({ bdsRoot, levelName } = resolveBdsContext());\n } catch (e) {\n logPack(c.yellow(t(\"packs.inboxSkip\", { message: (e as Error).message })), \"warn\");\n return { installed: 0, skipped: 0, failed: candidates.length };\n }\n\n const interactive = opts?.interactive ?? !!process.stdin.isTTY;\n const force = !!opts?.force;\n const dryRun = !!opts?.dryRun;\n const state = readState();\n let installed = 0;\n let skipped = 0;\n let failed = 0;\n\n logPack(c.dim(t(\"packs.inboxFound\", { count: candidates.length, level: levelName })));\n\n for (const src of candidates) {\n const base = path.basename(src);\n const fp = sourceFingerprint(src);\n if (state.installed[fp] && !force) {\n logPack(c.dim(t(\"packs.alreadyDone\", { name: base })));\n skipped++;\n if (!dryRun) {\n try {\n moveTo(src, doneDir(), base);\n } catch {\n /* \u53EF\u80FD\u4ECD\u88AB\u5360\u7528 */\n }\n }\n continue;\n }\n\n let disposeRoots: (() => void) | null = null;\n try {\n let roots: string[] = [];\n if (fs.statSync(src).isDirectory() || isPackArchive(src)) {\n if (dryRun && isPackArchive(src) && fs.statSync(src).isFile()) {\n logPack(c.dim(t(\"packs.dryRunExtract\", { name: base })));\n skipped++;\n continue;\n }\n const resolved = await resolvePackRoots(src);\n disposeRoots = resolved.dispose;\n roots = resolved.roots;\n } else {\n failed++;\n if (!dryRun) moveTo(src, failedDir(), base);\n continue;\n }\n\n if (roots.length === 0) {\n logPack(c.red(t(\"packs.noPackRoot\", { name: base })), \"error\");\n failed++;\n if (!dryRun && fs.statSync(src).isDirectory()) moveTo(src, failedDir(), `${base}-no-root`);\n continue;\n }\n\n if (dryRun) {\n for (const r of roots) {\n try {\n const info = readPackManifestInfo(r);\n logPack(c.dim(`dry-run: ${base} \u2192 ${info?.kind ?? \"?\"} ${info?.name ?? path.basename(r)}`), \"info\");\n } catch {\n logPack(c.red(t(\"packs.badManifest\")), \"error\");\n }\n }\n skipped++;\n continue;\n }\n\n let okCount = 0;\n let skipCount = 0;\n let failCount = 0;\n let lastUuid = \"\";\n for (const root of roots) {\n const folderHint =\n roots.length === 1 && fs.statSync(src).isFile() ? path.basename(src, path.extname(src)) : path.basename(root);\n const r = await installOnePackRoot({\n srcDir: root,\n bdsRoot,\n levelName,\n folderHint,\n force,\n interactive,\n });\n if (r.ok && r.info) {\n okCount++;\n lastUuid = r.info.uuid;\n logPack(\n c.green(\n t(\"packs.installedEnabled\", {\n kind: r.info.kind === \"resource\" ? \"RP\" : \"BP\",\n name: r.info.name,\n version: fmtVer(r.info.version),\n })\n ),\n \"success\"\n );\n } else if (r.skipped) {\n skipCount++;\n } else {\n failCount++;\n logPack(c.red(t(\"packs.installFailed\", { name: path.basename(root), reason: r.reason ?? \"?\" })), \"error\");\n }\n }\n\n installed += okCount > 0 ? 1 : 0;\n skipped += skipCount;\n failed += failCount;\n\n if (okCount > 0) {\n state.installed[fp] = { uuid: lastUuid, at: new Date().toISOString(), folderName: base };\n writeState(state);\n moveTo(src, doneDir(), base);\n logPack(c.yellow(`${restartHint()}`), \"warn\");\n } else if (failCount > 0 && skipCount === 0) {\n moveTo(src, failedDir(), base);\n }\n /* \u4EC5\u51B2\u7A81\u8DF3\u8FC7\uFF1A\u4FDD\u7559\u5728\u6536\u4EF6\u7BB1\uFF0C\u4FBF\u4E8E\u4EA4\u4E92\u65F6\u518D\u5904\u7406 */\n } catch (e) {\n logPack(c.red(`${base}: ${(e as Error).message}`), \"error\");\n failed++;\n if (!dryRun && fs.existsSync(src)) {\n try {\n moveTo(src, failedDir(), base);\n } catch {\n /* ignore */\n }\n }\n } finally {\n if (disposeRoots) disposeRoots();\n }\n }\n\n return { installed, skipped, failed };\n}\n\nfunction filterPacks(packs: InstalledWorldPack[], kind: \"bp\" | \"rp\" | \"all\", search?: string): InstalledWorldPack[] {\n let list = packs;\n if (kind === \"bp\") list = list.filter((p) => p.kind === \"behavior\");\n if (kind === \"rp\") list = list.filter((p) => p.kind === \"resource\");\n if (search) {\n const q = search.toLowerCase();\n list = list.filter(\n (p) =>\n p.name.toLowerCase().includes(q) || p.folderName.toLowerCase().includes(q) || p.uuid.toLowerCase().includes(q)\n );\n }\n return list;\n}\n\n/** \u672A\u7ED1\u5B9A\u6765\u6E90\u7684\u5360\u4F4D\u7B26\u53EA\u7528\u4E8E\u5185\u90E8\u8BED\u4E49\uFF0C\u4E0D\u5E94\u4F5C\u4E3A\u91CD\u590D\u7684\u5217\u8868\u5217\u5C55\u793A\u3002 */\nexport function shouldDisplayPackSourceLabel(sourceLabel: string): boolean {\n return sourceLabel !== \"src=-\";\n}\n\nfunction formatPackList(packs: InstalledWorldPack[]): string {\n if (packs.length === 0) return c.dim(t(\"packs.listEmpty\"));\n const lines: string[] = [];\n const bp = packs.filter((p) => p.kind === \"behavior\");\n const rp = packs.filter((p) => p.kind === \"resource\");\n if (bp.length) {\n lines.push(c.bold(t(\"packs.list.bpHeader\")));\n for (const p of bp) {\n const en = p.enabled ? c.green(t(\"packs.list.on\")) : c.dim(t(\"packs.list.off\"));\n const src = bindingLabelForUuid(p.uuid);\n const sourceColumn = shouldDisplayPackSourceLabel(src) ? ` ${c.dim(src)}` : \"\";\n lines.push(` [${en}] ${p.folderName} ${p.name} v${fmtVer(p.version)} ${c.dim(p.uuid)}${sourceColumn}`);\n }\n }\n if (rp.length) {\n lines.push(c.bold(t(\"packs.list.rpHeader\")));\n for (const p of rp) {\n const en = p.enabled ? c.green(t(\"packs.list.on\")) : c.dim(t(\"packs.list.off\"));\n lines.push(` [${en}] ${p.folderName} ${p.name} v${fmtVer(p.version)} ${c.dim(p.uuid)}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\nfunction parseListFlags(args: string[]): { kind: \"bp\" | \"rp\" | \"all\"; search?: string } {\n let kind: \"bp\" | \"rp\" | \"all\" = \"all\";\n let search: string | undefined;\n for (let i = 0; i < args.length; i++) {\n const a = args[i];\n if (a === \"--kind\" && args[i + 1]) {\n const k = args[++i]!.toLowerCase();\n if (k === \"bp\" || k === \"behavior\") kind = \"bp\";\n else if (k === \"rp\" || k === \"resource\") kind = \"rp\";\n else kind = \"all\";\n } else if (a === \"--search\" && args[i + 1]) {\n search = args[++i];\n } else if (a && !a.startsWith(\"-\")) {\n search = a;\n }\n }\n return { kind, ...(search ? { search } : {}) };\n}\n\nfunction hasFlag(args: string[], name: string): boolean {\n return args.includes(name);\n}\n\nfunction formatExperimentsStatus(levelName: string, knownExperiments: Record<string, boolean>): string {\n const isZh = getLocale() === \"zh-CN\";\n const lines = [t(\"packs.doctor.experimentsStatus\", { level: levelName })];\n for (const exp of KNOWN_EXPERIMENTS) {\n const on = !!knownExperiments[exp.id];\n const marker = on ? c.green(\"\u25CF\") : c.dim(\"\u25CB\");\n const stateText = on ? c.green(isZh ? \"\u5DF2\u5F00\u542F\" : \"enabled\") : c.dim(isZh ? \"\u672A\u5F00\u542F\" : \"disabled\");\n const nameStr = isZh ? `${exp.nameZh} (${exp.nameEn})` : exp.nameEn;\n lines.push(` ${marker} ${nameStr}: ${stateText}`);\n }\n return lines.join(\"\\n\");\n}\n\nasync function cmdDoctor(args: string[] = []): Promise<string> {\n const isFix = hasFlag(args, \"--fix\") || hasFlag(args, \"-y\") || hasFlag(args, \"fix\");\n const isExperimentsOnly = hasFlag(args, \"--experiments\") || hasFlag(args, \"experiments\");\n const isAllExperiments = hasFlag(args, \"--all-experiments\") || hasFlag(args, \"--all-exp\");\n const expArg = args.find((a) => a.startsWith(\"--experiments=\"));\n\n const { bdsRoot, levelName } = resolveBdsContext();\n const packs = listInstalledWorldPacks(bdsRoot, levelName);\n const expDiag = checkWorldExperimentsDiagnosis(bdsRoot, levelName, packs);\n\n // \u4EC5\u67E5\u8BE2\u5B9E\u9A8C\u6027\u73A9\u6CD5\u72B6\u6001\uFF08\u4F8B\u5982 packs doctor --experiments\uFF09\n if (isExperimentsOnly && !isFix && !isAllExperiments && !expArg) {\n if (!expDiag.levelDatExists || !expDiag.experimentsInfo) {\n return c.yellow(`level.dat not found for world \u300C${levelName}\u300D: ${expDiag.levelDatPath}`);\n }\n const statusText = formatExperimentsStatus(levelName, expDiag.experimentsInfo.knownExperiments);\n return `${statusText}\\n\\n ${c.cyan(t(\"packs.doctor.experimentsFixPrompt\"))}`;\n }\n\n // \u5224\u5B9A\u662F\u5426\u9700\u8981\u5F00\u542F\u5B9E\u9A8C\u6027\u529F\u80FD\n let requestedExperiments: string[] | \"all\" | undefined = undefined;\n if (isAllExperiments || expArg === \"--experiments=all\") {\n requestedExperiments = \"all\";\n } else if (expArg) {\n const rawList = expArg\n .slice(\"--experiments=\".length)\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n if (rawList.length > 0) {\n requestedExperiments = rawList;\n }\n }\n\n if (isFix || requestedExperiments !== undefined) {\n const shouldFixBeta = !expDiag.betaApis.hasBetaApis && expDiag.betaApis.requiringPacks.length > 0;\n const repRes = await repairWorldPacksWiring(bdsRoot, levelName, {\n fixBetaApis: shouldFixBeta,\n ...(requestedExperiments !== undefined ? { enableExperiments: requestedExperiments } : {}),\n });\n\n const fixLogs: string[] = [];\n if (repRes.experimentsResult?.attempted) {\n if (repRes.experimentsResult.changed) {\n const isZh = getLocale() === \"zh-CN\";\n const enabledNames = repRes.experimentsResult.enabled\n .map((id) => {\n const def = KNOWN_EXPERIMENTS.find((x) => x.id === id);\n return def ? (isZh ? def.nameZh : def.nameEn) : id;\n })\n .join(\", \");\n fixLogs.push(\n c.green(\n t(\"packs.doctor.experimentsFixed\", {\n level: levelName,\n list: enabledNames || \"all\",\n backup: repRes.experimentsResult.backupPath ?? \"level.dat.bak\",\n })\n )\n );\n } else if (repRes.experimentsResult.error) {\n fixLogs.push(\n c.red(t(\"packs.doctor.experimentsFailed\", { level: levelName, error: repRes.experimentsResult.error }))\n );\n } else {\n fixLogs.push(c.dim(t(\"packs.doctor.experimentsAlreadyOn\", { level: levelName })));\n }\n } else if (repRes.betaApisResult?.attempted) {\n if (repRes.betaApisResult.changed) {\n fixLogs.push(\n c.green(\n t(\"packs.doctor.betaApisFixed\", {\n level: levelName,\n backup: repRes.betaApisResult.backupPath ?? \"level.dat.bak\",\n })\n )\n );\n } else if (repRes.betaApisResult.error) {\n fixLogs.push(c.red(t(\"packs.doctor.betaApisFailed\", { level: levelName, error: repRes.betaApisResult.error })));\n } else {\n fixLogs.push(c.dim(t(\"packs.doctor.betaApisAlreadyOn\", { level: levelName })));\n }\n }\n\n for (const g of repRes.cleanedGhostPacks) {\n fixLogs.push(c.green(t(\"packs.doctor.ghostPackCleaned\", { kind: g.kind, uuid: g.uuid })));\n }\n\n for (const s of repRes.syncedVersions) {\n fixLogs.push(\n c.green(\n t(\"packs.doctor.versionSynced\", {\n kind: s.kind,\n folder: s.folder,\n listVer: s.listVer,\n diskVer: s.diskVer,\n })\n )\n );\n }\n\n if (repRes.fixedPermissions && repRes.fixedPermissions.length > 0) {\n fixLogs.push(\n c.green(\n t(\"packs.doctor.permissionsFixed\", {\n perms: repRes.fixedPermissions.map((x) => `${x.packName} (${x.moduleName})`).join(\", \"),\n })\n )\n );\n }\n\n const totalHealed =\n (repRes.experimentsResult?.changed ? 1 : 0) +\n (!repRes.experimentsResult && repRes.betaApisResult?.changed ? 1 : 0) +\n repRes.cleanedGhostPacks.length +\n repRes.syncedVersions.length +\n (repRes.fixedPermissions?.length ?? 0);\n\n if (totalHealed > 0) {\n return (\n fixLogs.map((x) => ` - ${x}`).join(\"\\n\") +\n \"\\n\" +\n c.green(t(\"packs.doctor.fixedSummary\", { count: totalHealed }))\n );\n } else if (fixLogs.length > 0) {\n return fixLogs.map((x) => ` - ${x}`).join(\"\\n\");\n }\n }\n\n const issues: string[] = [];\n let fixableCount = 0;\n\n for (const kind of [\"behavior\", \"resource\"] as const) {\n // \u7ECF listWorldEnableListResult \u2192 pack-manager\uFF08DRY/Demeter\uFF1B\u4FDD\u7559 parseFail \u4FE1\u53F7 \u2014 LSP\uFF09\n const snap = listWorldEnableListResult(bdsRoot, levelName, kind);\n if (snap.parseFailedFile) {\n issues.push(t(\"packs.doctor.parseFail\", { file: path.basename(snap.parseFailedFile) }));\n }\n const byUuid = new Map(packs.filter((p) => p.kind === kind).map((p) => [p.uuid, p]));\n for (const e of snap.entries) {\n const p = byUuid.get(e.pack_id);\n if (!p) {\n issues.push(t(\"packs.doctor.missingDir\", { kind, uuid: e.pack_id }));\n fixableCount++;\n continue;\n }\n const ev = e.version;\n if (\n Array.isArray(ev) &&\n ev.length >= 3 &&\n (ev[0] !== p.version[0] || ev[1] !== p.version[1] || ev[2] !== p.version[2])\n ) {\n issues.push(\n t(\"packs.doctor.versionMismatch\", {\n kind,\n folder: p.folderName,\n listVer: ev.join(\".\"),\n diskVer: fmtVer(p.version),\n })\n );\n fixableCount++;\n }\n }\n }\n\n // Beta APIs \u6821\u9A8C\n if (expDiag.betaApis.requiringPacks.length > 0 && !expDiag.betaApis.hasBetaApis) {\n const packLines = expDiag.betaApis.requiringPacks\n .map((rp) => {\n const depStr = rp.betaDependencies.map((d) => `${d.module_name ?? d.uuid}@${d.version ?? \"\"}`).join(\", \");\n const name =\n !rp.pack.name || rp.pack.name.startsWith(\"pack.\") || rp.pack.name === \"pack\"\n ? rp.pack.folderName\n : `${rp.pack.name} (${rp.pack.folderName})`;\n return ` * ${name} (v${fmtVer(rp.pack.version)}) -> \u4F9D\u8D56 ${depStr}`;\n })\n .join(\"\\n\");\n issues.push(t(\"packs.doctor.betaApisMissing\", { level: levelName, packs: packLines }));\n fixableCount++;\n }\n\n // \u539F\u751F\u811A\u672C\u6A21\u5757\u6743\u9650\u6821\u9A8C\uFF08\u5982 @minecraft/server-net, @minecraft/diagnostics \u7B49\uFF09\n const permDiag = checkWorldScriptPermissions(bdsRoot, levelName, packs);\n if (permDiag.missingPermissions.length > 0) {\n const list = permDiag.missingPermissions\n .map((m) => {\n const name =\n !m.pack.name || m.pack.name.startsWith(\"pack.\") || m.pack.name === \"pack\"\n ? m.pack.folderName\n : `${m.pack.name} (${m.pack.folderName})`;\n return ` * ${name} -> \u4F9D\u8D56\u6A21\u5757 ${m.moduleName}`;\n })\n .join(\"\\n\");\n issues.push(t(\"packs.doctor.permissionsMissing\", { list }));\n fixableCount++;\n }\n\n // \u683C\u5F0F\u5316\u5F53\u524D\u4E16\u754C\u7684\u5B9E\u9A8C\u6027\u5F00\u5173\u72B6\u6001\n let expStatusSection = \"\";\n if (expDiag.levelDatExists && expDiag.experimentsInfo) {\n expStatusSection = \"\\n\\n\" + formatExperimentsStatus(levelName, expDiag.experimentsInfo.knownExperiments);\n }\n\n if (issues.length === 0) {\n return c.green(t(\"packs.doctor.ok\")) + expStatusSection;\n }\n\n let res = t(\"packs.doctor.found\", {\n count: issues.length,\n list: issues.map((x) => ` - ${x}`).join(\"\\n\"),\n });\n res += expStatusSection;\n if (fixableCount > 0 && !isFix) {\n res += \"\\n\\n \" + c.cyan(t(\"packs.doctor.fixPrompt\"));\n res += \"\\n \" + c.dim(t(\"packs.doctor.experimentsFixPrompt\"));\n }\n return res;\n}\n\nexport async function dispatchPacksCommand(sub: string | undefined, args: string[]): Promise<string> {\n const verb = (sub ?? \"\").toLowerCase();\n if (!verb || verb === \"help\" || verb === \"-h\" || verb === \"--help\") {\n return packsUsage();\n }\n\n try {\n switch (verb) {\n case \"list\": {\n const { bdsRoot, levelName } = resolveBdsContext();\n const flags = parseListFlags(args);\n const packs = filterPacks(listInstalledWorldPacks(bdsRoot, levelName), flags.kind, flags.search);\n return formatPackList(packs);\n }\n case \"search\": {\n const q = args[0];\n if (!q) return c.yellow(t(\"packs.search.usage\"));\n return await searchRemote(q);\n }\n case \"bind\": {\n const packId = args[0];\n const ref = args[1];\n if (!packId || !ref) return c.yellow(t(\"packs.bind.usage\"));\n return await bindPackSource(packId, ref);\n }\n case \"unbind\": {\n const packId = args[0];\n if (!packId) return c.yellow(t(\"packs.unbind.usage\"));\n const { bdsRoot, levelName } = resolveBdsContext();\n const packs = listInstalledWorldPacks(bdsRoot, levelName);\n const pack = findInstalledPackById(packs, packId);\n const uuid = pack?.uuid ?? packId;\n const ok = removeBinding(uuid);\n return ok\n ? c.green(t(\"packs.unbindOk\", { uuid, path: packSourcesPath() }))\n : c.yellow(t(\"packs.unbindMiss\", { id: packId }));\n }\n case \"sources\":\n return formatSourcesList();\n case \"check\": {\n const id = args.find((a) => !a.startsWith(\"-\"));\n return await checkPackUpdates({ ...(id ? { packId: id } : {}), apply: false });\n }\n case \"update\": {\n const all = hasFlag(args, \"--all\");\n const id = args.find((a) => !a.startsWith(\"-\") && a !== \"--all\");\n if (!all && !id) return c.yellow(t(\"packs.update.usage\"));\n return await checkPackUpdates({\n ...(id ? { packId: id } : {}),\n apply: true,\n });\n }\n case \"enable\":\n case \"disable\": {\n const id = args[0];\n if (!id) return c.yellow(t(\"packs.toggle.usage\", { verb }));\n const { bdsRoot, levelName } = resolveBdsContext();\n const packs = listInstalledWorldPacks(bdsRoot, levelName);\n const pack = findInstalledPackById(packs, id);\n if (!pack) return c.red(t(\"packs.notFound\", { id }));\n if (verb === \"enable\") {\n await enableInstalledPack({\n bdsRoot,\n levelName,\n info: {\n name: pack.name,\n uuid: pack.uuid,\n version: pack.version,\n kind: pack.kind,\n },\n });\n } else {\n await disableInstalledPack({\n bdsRoot,\n levelName,\n kind: pack.kind,\n packUuid: pack.uuid,\n version: pack.version,\n });\n }\n return c.green(t(\"packs.toggleDone\", { verb, folder: pack.folderName, hint: restartHint() }));\n }\n case \"uninstall\": {\n const ids = args.filter((a) => !a.startsWith(\"-\"));\n const purge = hasFlag(args, \"--purge\");\n const noPaired = hasFlag(args, \"--no-paired\");\n const { bdsRoot, levelName } = resolveBdsContext();\n const packs = listInstalledWorldPacks(bdsRoot, levelName);\n\n let selected: InstalledWorldPack[] = [];\n if (ids.length === 0) {\n if (!process.stdin.isTTY) return c.yellow(t(\"packs.uninstall.usage\"));\n const picked = await pickPacksForUninstall(packs);\n if (picked === null) return c.dim(t(\"packs.uninstall.cancelled\"));\n if (picked.length === 0) return c.yellow(t(\"packs.uninstall.none\"));\n selected = picked;\n } else {\n /* \u5355/\u591A id \u5171\u7528\u89E3\u6790\u539F\u8BED\uFF08LSP\uFF1A\u4EFB\u4E00\u53D7\u4FDD\u62A4\u5305\u6574\u4F53\u4E2D\u6B62\uFF09 */\n const resolved = resolveUninstallTargets(packs, ids);\n if (resolved.status === \"not_found\") {\n return c.red(t(\"packs.notFound\", { id: resolved.missing.join(\", \") }));\n }\n if (resolved.status === \"protected\") {\n return c.red(t(\"packs.uninstall.protected\", { folder: resolved.folder }));\n }\n selected = resolved.selected;\n }\n\n return await uninstallPacksBatch(selected, packs, {\n bdsRoot,\n levelName,\n purge,\n noPaired,\n /* \u591A\u9009\u6216\u4E00\u6B21\u5378\u591A\u4E2A\u65F6\u786E\u8BA4\uFF1B\u5355 id \u4FDD\u6301\u539F\u300C\u76F4\u63A5\u5378\u300D\u624B\u611F */\n askConfirm: ids.length !== 1,\n });\n }\n case \"bump\": {\n const id = args[0];\n if (!id) return c.yellow(t(\"packs.bump.usage\"));\n const { bdsRoot, levelName } = resolveBdsContext();\n const packs = listInstalledWorldPacks(bdsRoot, levelName);\n const pack = findInstalledPackById(packs, id);\n if (!pack) return c.red(t(\"packs.notFound\", { id }));\n if (pack.kind !== \"resource\") {\n return c.red(t(\"packs.bump.rpOnly\"));\n }\n const next = bumpPackPatchVersion(pack.dir);\n if (pack.enabled) {\n await enableInstalledPack({\n bdsRoot,\n levelName,\n info: {\n name: pack.name,\n uuid: pack.uuid,\n version: next,\n kind: \"resource\",\n },\n });\n }\n return c.green(\n t(\"packs.bumped\", {\n folder: pack.folderName,\n version: fmtVer(next),\n restart: restartHint(),\n rejoin: rejoinHint(),\n })\n );\n }\n case \"install\": {\n const force = hasFlag(args, \"--force\");\n const inbox = hasFlag(args, \"--inbox\") || args.length === 0 || args.every((a) => a.startsWith(\"-\"));\n if (inbox) {\n const r = await scanAndInstallInbox({ force, interactive: true });\n return c.dim(\n t(\"packs.installInbox\", {\n installed: r.installed,\n skipped: r.skipped,\n failed: r.failed,\n })\n );\n }\n const target = args.find((a) => !a.startsWith(\"-\"));\n if (!target) return c.yellow(t(\"packs.install.usage\"));\n const abs = path.resolve(target);\n if (!fs.existsSync(abs)) return c.red(t(\"packs.pathMissing\", { path: abs }));\n const { bdsRoot, levelName } = resolveBdsContext();\n let disposeRoots: (() => void) | null = null;\n try {\n if (!fs.statSync(abs).isDirectory() && !isPackArchive(abs)) {\n return c.red(t(\"packs.notArchive\"));\n }\n const resolved = await resolvePackRoots(abs);\n disposeRoots = resolved.dispose;\n const roots = resolved.roots;\n if (roots.length === 0) return c.red(t(\"packs.noManifestRoot\"));\n const lines: string[] = [];\n for (const root of roots) {\n const r = await installOnePackRoot({\n srcDir: root,\n bdsRoot,\n levelName,\n folderHint: path.basename(root),\n force,\n interactive: true,\n });\n if (r.ok && r.info) {\n lines.push(\n c.green(\n t(\"packs.installedLine\", {\n name: r.info.name,\n version: fmtVer(r.info.version),\n kind: r.info.kind,\n })\n )\n );\n } else {\n lines.push(\n c.yellow(\n t(\"packs.skipFailLine\", {\n name: path.basename(root),\n reason: r.reason ?? \"?\",\n })\n )\n );\n }\n }\n lines.push(c.yellow(restartHint()));\n return lines.join(\"\\n\");\n } finally {\n if (disposeRoots) disposeRoots();\n }\n }\n case \"scan\": {\n const force = hasFlag(args, \"--force\");\n const dryRun = hasFlag(args, \"--dry-run\");\n const r = await scanAndInstallInbox({\n force,\n dryRun,\n interactive: !!process.stdin.isTTY,\n });\n if (r.installed === 0 && r.skipped === 0 && r.failed === 0) {\n return c.dim(t(\"packs.inboxEmpty\"));\n }\n return c.dim(\n t(\"packs.scanResult\", {\n installed: r.installed,\n skipped: r.skipped,\n failed: r.failed,\n })\n );\n }\n case \"doctor\":\n return await cmdDoctor(args);\n case \"path\": {\n ensureInboxLayout();\n const { bdsRoot, levelName } = resolveBdsContext();\n return [\n `bdsRoot: ${bdsRoot}`,\n `level: ${levelName}`,\n `behavior: ${worldPackParentDir(bdsRoot, levelName, \"behavior\")}`,\n `resource: ${worldPackParentDir(bdsRoot, levelName, \"resource\")}`,\n `inbox: ${packsInboxDir()}`,\n `trash: ${resolvePackTrashDir()}`,\n `build: ${path.join(packsInboxDir(), \"_build\")}`,\n `sources: ${packSourcesPath()}`,\n ].join(\"\\n\");\n }\n default:\n return c.red(t(\"packs.unknownSub\", { verb })) + packsUsage();\n }\n } catch (e) {\n return c.red((e as Error).message);\n }\n}\n\nexport function packsUsage(): string {\n return `${t(\"packs.usage.title\", { cmd: c.bold(\"sfmc packs\"), alias: c.green(\"addon\") })}\n\n ${c.green(\"packs list\")} [--kind bp|rp|all] [--search q]\n ${c.green(\"packs search\")} <q> ${t(\"packs.usage.cfSearch\")}\n ${c.green(\"packs bind\")} <id> <project|slug|url>\n ${c.green(\"packs unbind\")} <id>\n ${c.green(\"packs sources\")}\n ${c.green(\"packs check\")} [id]\n ${c.green(\"packs update\")} <id|--all>\n ${c.green(\"packs enable|disable\")} <uuid|folder>\n ${c.green(\"packs uninstall\")} [id...] [--purge] [--no-paired]\n ${c.green(\"packs bump\")} <id> ${t(\"packs.usage.bump\")}\n ${c.green(\"packs install\")} [path|--inbox] [--force]\n ${c.green(\"packs scan\")} [--force] [--dry-run]\n ${c.green(\"packs doctor\")} [--fix] [--all-experiments] [--experiments]\n ${c.green(\"packs path\")}\n\n${t(\"packs.usage.inbox\", { path: packsInboxDir() })}\n${t(\"packs.usage.trash\", { path: resolvePackTrashDir() })}\n${t(\"packs.usage.sources\", { path: packSourcesPath() })}\n${t(\"packs.usage.enableNote\", { hint: restartHint() })}\n${t(\"packs.usage.moduleNote\", { cmd: c.green(\"sfmc mod\") })}`;\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sfmc-bds/cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "SFMC management CLI — REPL, service supervisor, module install, behavior-pack build/deploy",
5
5
  "author": "Shiroha7z",
6
6
  "license": "AGPL-3.0-only",
@@ -53,11 +53,11 @@
53
53
  "chalk": "^6.0.0",
54
54
  "cross-spawn": "7.0.6",
55
55
  "esbuild": "^0.28.1",
56
- "@sfmc-bds/bds-tools": "0.2.1",
57
- "@sfmc-bds/sdk": "0.2.1"
56
+ "@sfmc-bds/bds-tools": "0.2.2",
57
+ "@sfmc-bds/sdk": "0.2.2"
58
58
  },
59
59
  "devDependencies": {
60
- "@sfmc-bds/tools": "0.2.1"
60
+ "@sfmc-bds/tools": "0.2.2"
61
61
  },
62
62
  "publishConfig": {
63
63
  "access": "public",