chatlab-cli 0.22.1 → 0.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +53 -30
  2. package/dist/{chunk-OC2W5Q5T.mjs → chunk-3MX7VF2B.mjs} +2 -2
  3. package/dist/chunk-5ADKYYNA.mjs +28 -0
  4. package/dist/chunk-5ADKYYNA.mjs.map +1 -0
  5. package/dist/{chunk-QRC3RSER.mjs → chunk-643S2N2U.mjs} +2 -2
  6. package/dist/{chunk-LZ3W6NS5.mjs → chunk-AHQQQ5HZ.mjs} +101 -69
  7. package/dist/chunk-AHQQQ5HZ.mjs.map +1 -0
  8. package/dist/{chunk-6NB475AA.mjs → chunk-FJ35V3TE.mjs} +2 -17
  9. package/dist/chunk-FJ35V3TE.mjs.map +1 -0
  10. package/dist/{chunk-SQKT45MM.mjs → chunk-G7Q7EQ3V.mjs} +546 -84
  11. package/dist/chunk-G7Q7EQ3V.mjs.map +1 -0
  12. package/dist/{chunk-XPBPKH6B.mjs → chunk-L37WJ33K.mjs} +134 -37
  13. package/dist/chunk-L37WJ33K.mjs.map +1 -0
  14. package/dist/{chunk-Z2LYMKCC.mjs → chunk-WTQ7LAZR.mjs} +2 -2
  15. package/dist/cli.mjs +4 -3
  16. package/dist/{http-O5P4BYXU.mjs → http-JDORENHT.mjs} +8 -7
  17. package/dist/{import-BDLL3DG7.mjs → import-LCBSBLYF.mjs} +4 -4
  18. package/dist/index.mjs +10 -9
  19. package/dist/{mcp-L2TPZFE7.mjs → mcp-AR7HBIK4.mjs} +8 -6
  20. package/dist/{mcp-L2TPZFE7.mjs.map → mcp-AR7HBIK4.mjs.map} +1 -1
  21. package/dist/service-WCGGRZTA.mjs +258 -0
  22. package/dist/service-WCGGRZTA.mjs.map +1 -0
  23. package/dist/{src-FVJL5ENA.mjs → src-EGXULYFQ.mjs} +2 -2
  24. package/dist/{stream-import-ZTEW3YVL.mjs → stream-import-QM5R5FDG.mjs} +3 -3
  25. package/dist/{update-checker-MQMQ6A4B.mjs → update-checker-JG5K67O2.mjs} +3 -2
  26. package/dist/{update-checker-MQMQ6A4B.mjs.map → update-checker-JG5K67O2.mjs.map} +1 -1
  27. package/dist-web/assets/{DebugToolsPanel-DkbsB6lr.css → DebugToolsPanel-Yzfz2ntn.css} +1 -1
  28. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-DXxg55kL.js +161 -0
  29. package/dist-web/assets/{EChartWordcloud-Ubal_aYg.js → EChartWordcloud-Cenbzcyj.js} +1 -1
  30. package/dist-web/assets/MessageExportModal-DMHA4X0b.js +1 -0
  31. package/dist-web/assets/electron-DpE69lO4.js +1 -0
  32. package/dist-web/assets/electron-SyPZX9OU.js +1 -0
  33. package/dist-web/assets/{fetch-BS4SlQjA.js → fetch-B7OBmANJ.js} +1 -1
  34. package/dist-web/assets/{fetch-o5NJ2uWE.js → fetch-BikSxuED.js} +1 -1
  35. package/dist-web/assets/fetch-BwMZFwbF.js +1 -0
  36. package/dist-web/assets/fetch-BxE8RQYE.js +1 -0
  37. package/dist-web/assets/fetch-CvfdN4gM.js +17 -0
  38. package/dist-web/assets/{fetch-W43zfemj.js → fetch-LBHcBBkv.js} +1 -1
  39. package/dist-web/assets/http-C741fIwb.js +1 -0
  40. package/dist-web/assets/index-BUfibEyG.css +1 -0
  41. package/dist-web/assets/{index-DOwQB7In.js → index-C2439065.js} +6 -6
  42. package/dist-web/assets/index-CD5Fz-JY.js +7 -0
  43. package/dist-web/assets/index-DC0LjKV6.js +2 -0
  44. package/dist-web/assets/index-Dl-X-izi.css +1 -0
  45. package/dist-web/assets/index-DywUneNT.css +1 -0
  46. package/dist-web/assets/index-JvmxkAGk.js +173 -0
  47. package/dist-web/assets/index-vEVpfw-R.js +163 -0
  48. package/dist-web/assets/model-catalog-DUuKjIVU.js +1 -0
  49. package/dist-web/assets/{vendor-nuxt-ui-7OAuiz_i.js → vendor-nuxt-ui-3XQAyMd0.js} +2 -2
  50. package/dist-web/assets/vendor-reka-ui-BrTYuIfH.js +11 -0
  51. package/dist-web/assets/{web-Cx5APyCN.js → web-Bm94rL5B.js} +1 -1
  52. package/dist-web/index.html +4 -4
  53. package/package.json +3 -2
  54. package/dist/chunk-6NB475AA.mjs.map +0 -1
  55. package/dist/chunk-LZ3W6NS5.mjs.map +0 -1
  56. package/dist/chunk-SQKT45MM.mjs.map +0 -1
  57. package/dist/chunk-XPBPKH6B.mjs.map +0 -1
  58. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-CqacsKDU.js +0 -147
  59. package/dist-web/assets/MessageExportModal-D1GY6WSV.js +0 -1
  60. package/dist-web/assets/electron-8qi0d92j.js +0 -1
  61. package/dist-web/assets/electron-CsUM2O_Y.js +0 -1
  62. package/dist-web/assets/fetch-BOogCPfg.js +0 -1
  63. package/dist-web/assets/fetch-CclRWOHf.js +0 -1
  64. package/dist-web/assets/fetch-CxwnEEqk.js +0 -17
  65. package/dist-web/assets/http-CeXMt-Fv.js +0 -1
  66. package/dist-web/assets/index-BE64tUWq.css +0 -1
  67. package/dist-web/assets/index-CM29Kc4p.js +0 -173
  68. package/dist-web/assets/index-CbQMfheg.css +0 -1
  69. package/dist-web/assets/index-D183wYuz.js +0 -7
  70. package/dist-web/assets/index-DSpvXged.css +0 -1
  71. package/dist-web/assets/index-DTxRy2FH.js +0 -163
  72. package/dist-web/assets/index-grds3iG-.js +0 -2
  73. package/dist-web/assets/vendor-reka-ui-CW2cIgMu.js +0 -11
  74. /package/dist/{chunk-OC2W5Q5T.mjs.map → chunk-3MX7VF2B.mjs.map} +0 -0
  75. /package/dist/{chunk-QRC3RSER.mjs.map → chunk-643S2N2U.mjs.map} +0 -0
  76. /package/dist/{chunk-Z2LYMKCC.mjs.map → chunk-WTQ7LAZR.mjs.map} +0 -0
  77. /package/dist/{http-O5P4BYXU.mjs.map → http-JDORENHT.mjs.map} +0 -0
  78. /package/dist/{import-BDLL3DG7.mjs.map → import-LCBSBLYF.mjs.map} +0 -0
  79. /package/dist/{src-FVJL5ENA.mjs.map → src-EGXULYFQ.mjs.map} +0 -0
  80. /package/dist/{stream-import-ZTEW3YVL.mjs.map → stream-import-QM5R5FDG.mjs.map} +0 -0
package/README.md CHANGED
@@ -8,7 +8,7 @@ Your chat history, finally yours.
8
8
 
9
9
  English | [简体中文](./README.zh-CN.md)
10
10
 
11
- [Official Website](https://chatlab.fun/) · [Documentation](https://docs.chatlab.fun/en/usage/) · [Roadmap](https://chatlab.fun/cn/roadmap/tasks)
11
+ [Official Website](https://chatlab.fun/) · [Docs](https://docs.chatlab.fun/) · [Quick Start](https://docs.chatlab.fun/usage/quick-start) · [Roadmap](https://chatlab.fun/roadmap/tasks) · [Releases](https://github.com/ChatLab/ChatLab/releases)
12
12
 
13
13
  </div>
14
14
 
@@ -16,20 +16,56 @@ ChatLab is an open-source desktop app for understanding your social conversation
16
16
 
17
17
  Currently supported: **WhatsApp, LINE, WeChat, QQ, Discord, Instagram, and Telegram**. Coming next: **iMessage, Messenger, and KakaoTalk**.
18
18
 
19
+ > New install? Start here: [Getting started](https://docs.chatlab.fun/usage/quick-start)
20
+
19
21
  ## Core Features
20
22
 
21
23
  - 🚀 **Built for large histories**: Stream parsing and multi-worker processing keep imports and analysis responsive, even at million-message scale.
22
24
  - 🔒 **Private by default**: Your chat data and settings stay local. No mandatory cloud upload of raw conversations.
23
- - 🤖 **AI that can actually operate on data**: Agent + Function Calling workflows can search, summarize, and analyze chat records with context.
25
+ - 🤖 **AI that can actually operate on data**: Agent + Function Calling workflows (24+ tools) can search, summarize, and analyze chat records with context.
24
26
  - 📊 **Insight-rich visual views**: See trends, time patterns, interaction frequency, rankings, and more in one place.
25
27
  - 🧩 **Cross-platform normalization**: Different export formats are mapped into a unified model so you can analyze them consistently.
26
28
 
29
+ ## Installation
30
+
31
+ ### Desktop App
32
+
33
+ Download the installer for your OS from the [official website](https://chatlab.fun/?type=download) or [GitHub Releases](https://github.com/ChatLab/ChatLab/releases), then double-click to install.
34
+
35
+ ### CLI
36
+
37
+ Requires Node.js ≥ 20.
38
+
39
+ ```bash
40
+ npm i chatlab-cli -g
41
+ ```
42
+
43
+ Start ChatLab:
44
+
45
+ ```bash
46
+ chatlab start # Start API + Web UI, auto-open in browser
47
+ chatlab start --no-open # Start API + Web UI, skip auto-open
48
+ chatlab start --headless # API only, no Web UI (for scripts / AI Agents)
49
+ ```
50
+
51
+ Common options: `--port <port>` (default 3110), `--host <address>`, `--token <token>`.
52
+
53
+ To run as a persistent background service (auto-start on login + auto-restart on crash):
54
+
55
+ ```bash
56
+ chatlab start --daemon # Install as system service (macOS / Linux)
57
+ chatlab status # Check service status
58
+ chatlab stop # Stop and uninstall service
59
+ ```
60
+
61
+ For a full walkthrough, see the [Quick Start guide](https://docs.chatlab.fun/usage/quick-start).
62
+
27
63
  ## Usage Guides
28
64
 
29
65
  - [Download Guide](https://chatlab.fun/?type=download)
30
- - [Chat Record Export Guide](https://docs.chatlab.fun/en/usage/how-to-export)
31
- - [Standardized Format Specification](https://docs.chatlab.fun/en/standard/chatlab-format)
32
- - [Troubleshooting Guide](https://docs.chatlab.fun/en/usage/troubleshooting)
66
+ - [Chat Record Export Guide](https://docs.chatlab.fun/usage/how-to-export)
67
+ - [Standardized Format Specification](https://docs.chatlab.fun/standard/chatlab-format)
68
+ - [Troubleshooting Guide](https://docs.chatlab.fun/usage/troubleshooting)
33
69
 
34
70
  ## Preview
35
71
 
@@ -37,7 +73,13 @@ For more previews, please visit the official website: [chatlab.fun](https://chat
37
73
 
38
74
  ![Preview Interface](/public/images/intro_en.png)
39
75
 
40
- ## System Architecture
76
+ ## Architecture Overview
77
+
78
+ ChatLab is a pnpm monorepo built on Electron + Vue 3 + Nuxt UI + Tailwind CSS. Core business logic lives in shared packages (`@openchatlab/core`, `@openchatlab/node-runtime`, `@openchatlab/tools`), consumed by both the desktop app and the CLI service — so they stay in sync.
79
+
80
+ Data flows in five stages: **format detection → stream parsing → local persistence → SQL + AI query → visualization**.
81
+
82
+ For a deep dive, see the [architecture documentation](https://docs.chatlab.fun/intro).
41
83
 
42
84
  ### Architecture Principles
43
85
 
@@ -46,27 +88,6 @@ For more previews, please visit the official website: [chatlab.fun](https://chat
46
88
  - **Composable intelligence**: AI features are assembled through Agent + Tool Calling, not hard-coded into one model path.
47
89
  - **Schema-first evolution**: Import, query, analysis, and visualization share a consistent data model that scales with new features.
48
90
 
49
- ### Runtime Architecture
50
-
51
- - **Main Process (control plane)**: `apps/desktop/main/index.ts` handles lifecycle and windows. `apps/desktop/main/ipc/` defines domain-scoped IPC. Core AI, query, NLP, import, and merge logic lives in shared packages (`packages/core`, `packages/node-runtime`, `packages/tools`); `apps/desktop/main/ai/` and `apps/desktop/main/i18n/` are thin Electron adaptation layers.
52
- - **Worker Layer (compute plane)**: `apps/desktop/main/worker/` runs import, indexing, and query tasks via `workerManager`, delegating core logic to `@openchatlab/core` and `@openchatlab/node-runtime`.
53
- - **Renderer Layer (interaction plane)**: Vue 3 + Nuxt UI + Tailwind CSS drive management, private chat, group chat, and analysis interfaces. `apps/desktop/preload/index.ts` exposes tightly scoped APIs for secure process boundaries.
54
-
55
- ### Data Pipeline
56
-
57
- 1. **Ingestion**: `parser/` detects file format and dispatches to the matching parser module.
58
- 2. **Persistence**: Stream-based writes populate core local entities: sessions, members, and messages.
59
- 3. **Indexing**: Session- and time-oriented indexes are built for timeline navigation and retrieval.
60
- 4. **Query & Analysis**: `worker/query/*` powers activity metrics, interaction analysis, SQL Lab, and AI-assisted exploration.
61
- 5. **Presentation**: The renderer turns query output into charts, rankings, timelines, and conversational analysis flows.
62
-
63
- ### Extensibility & Reliability
64
-
65
- - **Pluggable parser architecture**: Adding a new import source is mostly an extension in `parser/formats/*`, without reworking downstream query logic.
66
- - **Full + incremental import paths**: `streamImport.ts` and `incrementalImport.ts` support both first-time onboarding and ongoing updates.
67
- - **Modular IPC boundaries**: Domain-based IPC segmentation reduces cross-layer coupling and limits permission spread.
68
- - **Unified i18n evolution**: Main and renderer processes share an i18n system that can evolve with product scope.
69
-
70
91
  ---
71
92
 
72
93
  ## Local Development
@@ -79,11 +100,14 @@ For more previews, please visit the official website: [chatlab.fun](https://chat
79
100
  ### Setup
80
101
 
81
102
  ```bash
82
- # install dependencies
103
+ # Install dependencies
83
104
  pnpm install
84
105
 
85
- # run electron app in dev mode
106
+ # Run desktop app in dev mode
86
107
  pnpm dev
108
+
109
+ # Run CLI in dev mode (apps/cli)
110
+ cd apps/cli && pnpm dev
87
111
  ```
88
112
 
89
113
  If Electron encounters exceptions during startup, you can try using `electron-fix`:
@@ -91,7 +115,6 @@ If Electron encounters exceptions during startup, you can try using `electron-fi
91
115
  ```bash
92
116
  npm install electron-fix -g
93
117
  electron-fix start
94
-
95
118
  ```
96
119
 
97
120
  ## Privacy Policy & User Agreement
@@ -11,7 +11,7 @@ import {
11
11
  generateMessageKey,
12
12
  openBetterSqliteDatabase,
13
13
  writeParseResultToDb
14
- } from "./chunk-SQKT45MM.mjs";
14
+ } from "./chunk-G7Q7EQ3V.mjs";
15
15
 
16
16
  // src/import/chatlab-reader.ts
17
17
  import * as fs from "fs";
@@ -392,4 +392,4 @@ export {
392
392
  parseFile,
393
393
  importData
394
394
  };
395
- //# sourceMappingURL=chunk-OC2W5Q5T.mjs.map
395
+ //# sourceMappingURL=chunk-3MX7VF2B.mjs.map
@@ -0,0 +1,28 @@
1
+ import { createRequire as __createRequire } from 'module';
2
+ import { dirname as __pathDirname } from 'path';
3
+ import { fileURLToPath as __fileURLToPath } from 'url';
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ import {
8
+ resolveCliPath
9
+ } from "./chunk-FJ35V3TE.mjs";
10
+
11
+ // src/version.ts
12
+ import { readFileSync } from "fs";
13
+ var cached;
14
+ function getVersion() {
15
+ if (cached) return cached;
16
+ try {
17
+ const pkgPath = resolveCliPath("package.json");
18
+ cached = JSON.parse(readFileSync(pkgPath, "utf-8")).version;
19
+ } catch {
20
+ cached = "0.0.0-dev";
21
+ }
22
+ return cached;
23
+ }
24
+
25
+ export {
26
+ getVersion
27
+ };
28
+ //# sourceMappingURL=chunk-5ADKYYNA.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/version.ts"],"sourcesContent":["import { readFileSync } from 'fs'\nimport { resolveCliPath } from './paths'\n\nlet cached: string | undefined\n\n/**\n * Read version from apps/cli/package.json at runtime.\n * Works for both tsx dev (src/) and bundled (dist/).\n */\nexport function getVersion(): string {\n if (cached) return cached\n try {\n const pkgPath = resolveCliPath('package.json')\n cached = JSON.parse(readFileSync(pkgPath, 'utf-8')).version\n } catch {\n cached = '0.0.0-dev'\n }\n return cached!\n}\n"],"mappings":";;;;;;;;;;;AAAA,SAAS,oBAAoB;AAG7B,IAAI;AAMG,SAAS,aAAqB;AACnC,MAAI,OAAQ,QAAO;AACnB,MAAI;AACF,UAAM,UAAU,eAAe,cAAc;AAC7C,aAAS,KAAK,MAAM,aAAa,SAAS,OAAO,CAAC,EAAE;AAAA,EACtD,QAAQ;AACN,aAAS;AAAA,EACX;AACA,SAAO;AACT;","names":[]}
@@ -13,7 +13,7 @@ import {
13
13
  incrementalImport,
14
14
  openBetterSqliteDatabase,
15
15
  streamingImport
16
- } from "./chunk-SQKT45MM.mjs";
16
+ } from "./chunk-G7Q7EQ3V.mjs";
17
17
 
18
18
  // src/import/stream-import.ts
19
19
  import * as fs from "fs";
@@ -141,4 +141,4 @@ export {
141
141
  analyzeIncrementalImport2 as analyzeIncrementalImport,
142
142
  analyzeNewImport2 as analyzeNewImport
143
143
  };
144
- //# sourceMappingURL=chunk-QRC3RSER.mjs.map
144
+ //# sourceMappingURL=chunk-643S2N2U.mjs.map
@@ -5,9 +5,8 @@ const require = __createRequire(import.meta.url);
5
5
  const __filename = __fileURLToPath(import.meta.url);
6
6
  const __dirname = __pathDirname(__filename);
7
7
  import {
8
- getVersion,
9
- resolveCliPath
10
- } from "./chunk-6NB475AA.mjs";
8
+ getVersion
9
+ } from "./chunk-5ADKYYNA.mjs";
11
10
  import {
12
11
  DEFAULT_API_PORT,
13
12
  DatabaseManager,
@@ -22,17 +21,49 @@ import {
22
21
  loadConfig,
23
22
  searchMessagesLike,
24
23
  verifyCliDataPath
25
- } from "./chunk-SQKT45MM.mjs";
24
+ } from "./chunk-G7Q7EQ3V.mjs";
25
+ import {
26
+ resolveCliPath
27
+ } from "./chunk-FJ35V3TE.mjs";
26
28
 
27
29
  // src/cli.ts
28
30
  import * as fs from "fs";
29
- import * as path from "path";
30
31
  import { execSync } from "child_process";
31
32
  import { Command } from "commander";
33
+
34
+ // src/http/port.ts
35
+ import * as net from "net";
36
+ function isPortAvailable(port, host) {
37
+ return new Promise((resolve, reject) => {
38
+ const server = net.createServer();
39
+ server.once("error", (err) => {
40
+ if (err.code === "EADDRINUSE") {
41
+ resolve(false);
42
+ } else {
43
+ reject(err);
44
+ }
45
+ });
46
+ server.once("listening", () => server.close(() => resolve(true)));
47
+ server.listen(port, host);
48
+ });
49
+ }
50
+ function formatPortInUseError(port) {
51
+ return [
52
+ ``,
53
+ ` \u2716 Error: port ${port} is already in use.`,
54
+ ``,
55
+ ` Another process (possibly a ChatLab instance) is using this port. You can:`,
56
+ ` \u2022 Use another port: chatlab start --port <port>`,
57
+ ` \u2022 Find the process: lsof -iTCP:${port} -sTCP:LISTEN`,
58
+ ``
59
+ ].join("\n");
60
+ }
61
+
62
+ // src/cli.ts
32
63
  var program = new Command();
33
64
  program.name("chatlab").description("ChatLab - Chat history analysis tool").version(getVersion(), "-v, --version");
34
65
  program.hook("preAction", async () => {
35
- const { checkForUpdatesInteractive } = await import("./update-checker-MQMQ6A4B.mjs");
66
+ const { checkForUpdatesInteractive } = await import("./update-checker-JG5K67O2.mjs");
36
67
  await checkForUpdatesInteractive();
37
68
  });
38
69
  program.command("sessions").description("List all imported chat sessions").option("--format <format>", "Output format (table|json)", "table").action((options) => {
@@ -173,7 +204,7 @@ program.command("import <file>").description("Import a chat history file (14+ fo
173
204
  console.error(`File not found: ${file}`);
174
205
  process.exit(1);
175
206
  }
176
- const { streamImport, detectFormat } = await import("./import-BDLL3DG7.mjs");
207
+ const { streamImport, detectFormat } = await import("./import-LCBSBLYF.mjs");
177
208
  const { dbManager } = initRuntime();
178
209
  const nativeBinding = resolveNativeBinding();
179
210
  const format = detectFormat(file);
@@ -215,7 +246,7 @@ Import error: ${err instanceof Error ? err.message : err}`);
215
246
  }
216
247
  });
217
248
  program.command("formats").description("List all supported chat history formats").action(async () => {
218
- const { getSupportedFormats } = await import("./import-BDLL3DG7.mjs");
249
+ const { getSupportedFormats } = await import("./import-LCBSBLYF.mjs");
219
250
  const formats = getSupportedFormats();
220
251
  console.log(`${formats.length} supported format(s):
221
252
  `);
@@ -224,91 +255,67 @@ program.command("formats").description("List all supported chat history formats"
224
255
  }
225
256
  });
226
257
  program.command("mcp").description("Start MCP Server (stdio transport, for ClaudeCode / Cursor / AI agents)").action(async () => {
227
- const { startCliMcpServer } = await import("./mcp-L2TPZFE7.mjs");
258
+ const { startCliMcpServer } = await import("./mcp-AR7HBIK4.mjs");
228
259
  await startCliMcpServer();
229
260
  });
230
- program.command("serve").description("Start the HTTP API server").option("--port <port>", "Server port", String(DEFAULT_API_PORT)).option("--host <host>", "Listen address", "127.0.0.1").option("--token <token>", "Custom Bearer Token (reads from config or auto-generates if omitted)").option("--web [dir]", "Serve Web frontend static assets (defaults to dist-web/)").action(async (options) => {
231
- const { startHttpServer } = await import("./http-O5P4BYXU.mjs");
261
+ program.command("start").description("Start ChatLab (HTTP API + Web UI)").option("--port <port>", "Server port", String(DEFAULT_API_PORT)).option("--host <host>", "Listen address", "127.0.0.1").option("--token <token>", "Custom Bearer Token (reads from config or auto-generates if omitted)").option("--headless", "API-only mode, do not serve the Web UI").option("--no-open", "Do not auto-open the browser").option("--daemon", "Run as a resident system service (auto-start on login, macOS/Linux)").action(async (options) => {
262
+ if (options.daemon) {
263
+ const { serviceInstall } = await import("./service-WCGGRZTA.mjs");
264
+ serviceInstall({
265
+ port: parseInt(options.port, 10),
266
+ host: options.host,
267
+ token: options.token || void 0,
268
+ headless: options.headless
269
+ });
270
+ return;
271
+ }
272
+ const { startHttpServer } = await import("./http-JDORENHT.mjs");
232
273
  const port = parseInt(options.port, 10);
233
274
  let webRoot;
234
- if (options.web !== void 0) {
235
- const webDir = typeof options.web === "string" ? options.web : resolveCliPath("dist-web");
275
+ if (!options.headless) {
276
+ const webDir = resolveCliPath("dist-web");
236
277
  if (fs.existsSync(webDir)) {
237
- webRoot = path.resolve(webDir);
278
+ webRoot = webDir;
238
279
  } else {
239
- console.warn(`Warning: Web directory not found: ${webDir}, starting in API-only mode`);
280
+ console.warn("Warning: dist-web/ not found, starting in API-only mode");
240
281
  }
241
282
  }
242
283
  try {
284
+ if (!await isPortAvailable(port, options.host)) {
285
+ console.error(formatPortInUseError(port));
286
+ process.exit(1);
287
+ }
243
288
  const info = await startHttpServer({
244
289
  port,
245
290
  host: options.host,
246
291
  token: options.token || void 0,
247
292
  webRoot
248
293
  });
249
- const { startPeriodicUpdateCheck } = await import("./update-checker-MQMQ6A4B.mjs");
294
+ const { startPeriodicUpdateCheck } = await import("./update-checker-JG5K67O2.mjs");
250
295
  startPeriodicUpdateCheck();
251
- console.log(`
252
- ChatLab HTTP API started`);
253
- console.log(` Address: http://${info.host}:${info.port}`);
254
- console.log(` Token: ${info.token}`);
255
- if (webRoot) {
256
- console.log(` Web UI: http://${info.host}:${info.port}/`);
257
- }
258
- console.log(`
259
- Example:`);
260
- console.log(` curl -H "Authorization: Bearer ${info.token}" http://${info.host}:${info.port}/api/v1/status`);
261
- console.log(`
262
- Press Ctrl+C to stop.
263
- `);
264
- const shutdown = async () => {
265
- console.log("\nShutting down...");
266
- const { stopHttpServer } = await import("./http-O5P4BYXU.mjs");
267
- await stopHttpServer();
268
- process.exit(0);
269
- };
270
- process.on("SIGINT", shutdown);
271
- process.on("SIGTERM", shutdown);
272
- } catch (err) {
273
- const message = err instanceof Error ? err.message : String(err);
274
- if (message.includes("EADDRINUSE")) {
275
- console.error(`Error: port ${port} is already in use`);
276
- } else {
277
- console.error(`Startup failed: ${message}`);
278
- }
279
- process.exit(1);
280
- }
281
- });
282
- program.command("web").description("Launch Web UI with one click (HTTP API + browser auto-open)").option("--port <port>", "Server port", String(DEFAULT_API_PORT)).option("--host <host>", "Listen address", "127.0.0.1").option("--no-open", "Do not auto-open browser").action(async (options) => {
283
- const { startHttpServer } = await import("./http-O5P4BYXU.mjs");
284
- const port = parseInt(options.port, 10);
285
- const webDir = resolveCliPath("dist-web");
286
- let webRoot;
287
- if (fs.existsSync(webDir)) {
288
- webRoot = webDir;
289
- } else {
290
- console.warn("Warning: dist-web/ not found, Web UI will not be available (API-only mode)");
291
- }
292
- try {
293
- const info = await startHttpServer({ port, host: options.host, webRoot });
294
- const { startPeriodicUpdateCheck } = await import("./update-checker-MQMQ6A4B.mjs");
295
- startPeriodicUpdateCheck();
296
- const url = `http://${info.host === "0.0.0.0" ? "127.0.0.1" : info.host}:${info.port}`;
296
+ const displayHost = info.host === "0.0.0.0" ? "127.0.0.1" : info.host;
297
+ const url = `http://${displayHost}:${info.port}`;
297
298
  console.log(`
298
299
  ChatLab v${getVersion()}`);
299
- console.log(` Web UI: ${url}`);
300
+ if (webRoot) console.log(` Web UI: ${url}/`);
301
+ console.log(` API: ${url}`);
300
302
  console.log(` Token: ${info.token}`);
301
- if (options.open && webRoot) {
303
+ console.log(`
304
+ Example:`);
305
+ console.log(` curl -H "Authorization: Bearer ${info.token}" ${url}/api/v1/status`);
306
+ if (webRoot && options.open) {
302
307
  openBrowser(url);
303
308
  console.log(`
304
- Browser opened. Press Ctrl+C to stop.`);
309
+ Browser opened. Press Ctrl+C to stop.
310
+ `);
305
311
  } else {
306
312
  console.log(`
307
- Press Ctrl+C to stop.`);
313
+ Press Ctrl+C to stop.
314
+ `);
308
315
  }
309
316
  const shutdown = async () => {
310
317
  console.log("\nShutting down...");
311
- const { stopHttpServer } = await import("./http-O5P4BYXU.mjs");
318
+ const { stopHttpServer } = await import("./http-JDORENHT.mjs");
312
319
  await stopHttpServer();
313
320
  process.exit(0);
314
321
  };
@@ -317,7 +324,7 @@ Press Ctrl+C to stop.`);
317
324
  } catch (err) {
318
325
  const message = err instanceof Error ? err.message : String(err);
319
326
  if (message.includes("EADDRINUSE")) {
320
- console.error(`Error: port ${port} is already in use`);
327
+ console.error(formatPortInUseError(port));
321
328
  } else {
322
329
  console.error(`Startup failed: ${message}`);
323
330
  }
@@ -332,6 +339,31 @@ configCmd.command("show").description("Show current configuration").action(() =>
332
339
  const config = loadConfig();
333
340
  console.log(JSON.stringify(config, null, 2));
334
341
  });
342
+ program.command("stop").description("Stop the resident service and remove auto-start (reverse of start --daemon)").action(async () => {
343
+ const { serviceUninstall } = await import("./service-WCGGRZTA.mjs");
344
+ serviceUninstall();
345
+ });
346
+ program.command("status").description("Show resident service status").action(async () => {
347
+ const { getServiceStatus } = await import("./service-WCGGRZTA.mjs");
348
+ const svc = getServiceStatus();
349
+ console.log("\nChatLab Status");
350
+ console.log("\u2500".repeat(36));
351
+ if (svc.installed) {
352
+ const portStr = svc.port ? `http://${svc.host ?? "127.0.0.1"}:${svc.port}` : "";
353
+ console.log(` Service: ${svc.running ? "running" : "installed (not running)"}`);
354
+ if (portStr) console.log(` Address: ${portStr}`);
355
+ console.log(` Auto-start: enabled`);
356
+ console.log(`
357
+ Use \`chatlab stop\` to remove the service.
358
+ `);
359
+ } else {
360
+ console.log(` Service: not installed`);
361
+ console.log(` Auto-start: disabled`);
362
+ console.log(`
363
+ Use \`chatlab start --daemon\` to install as a system service.
364
+ `);
365
+ }
366
+ });
335
367
  function openBrowser(url) {
336
368
  try {
337
369
  const cmd = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
@@ -417,4 +449,4 @@ if (isDirectRun) {
417
449
  export {
418
450
  run
419
451
  };
420
- //# sourceMappingURL=chunk-LZ3W6NS5.mjs.map
452
+ //# sourceMappingURL=chunk-AHQQQ5HZ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/http/port.ts"],"sourcesContent":["/**\n * ChatLab CLI entry point\n *\n * Dev: pnpm --filter chatlab-cli run cli -- sessions\n */\n\nimport * as fs from 'fs'\nimport { execSync } from 'child_process'\nimport { Command } from 'commander'\nimport { DEFAULT_API_PORT, loadConfig, getConfigPath } from '@openchatlab/config'\nimport {\n NodePathProvider,\n DatabaseManager,\n hasPendingElectronDataWarning,\n verifyCliDataPath,\n} from '@openchatlab/node-runtime'\nimport {\n getSessionMeta,\n getSessionOverview,\n getMemberActivity,\n searchMessagesLike,\n getMembers,\n executeReadonlySql,\n} from '@openchatlab/core'\nimport { getVersion } from './version'\nimport { resolveCliPath } from './paths'\nimport { isPortAvailable, formatPortInUseError } from './http/port'\n\nconst program = new Command()\n\nprogram.name('chatlab').description('ChatLab - Chat history analysis tool').version(getVersion(), '-v, --version')\n\nprogram.hook('preAction', async () => {\n const { checkForUpdatesInteractive } = await import('./update-checker')\n await checkForUpdatesInteractive()\n})\n\nprogram\n .command('sessions')\n .description('List all imported chat sessions')\n .option('--format <format>', 'Output format (table|json)', 'table')\n .action((options) => {\n const { dbManager } = initRuntime()\n const sessionIds = dbManager.listSessionIds()\n\n if (sessionIds.length === 0) {\n console.log('No chat sessions found.')\n console.log(`Data directory: ${dbManager['pathProvider'].getUserDataDir()}`)\n dbManager.closeAll()\n return\n }\n\n const sessions = sessionIds\n .map((id) => {\n const db = dbManager.open(id)\n if (!db) return null\n const meta = getSessionMeta(db)\n if (!meta) return null\n const overview = getSessionOverview(db)\n return { id, ...meta, ...overview }\n })\n .filter(Boolean)\n\n if (options.format === 'json') {\n console.log(JSON.stringify(sessions, null, 2))\n } else {\n console.log(`${sessions.length} session(s) found:\\n`)\n for (const s of sessions) {\n if (!s) continue\n const timeRange = formatTimeRange(s.firstMessageTs, s.lastMessageTs)\n console.log(` ${s.name}`)\n console.log(` ID: ${s.id}`)\n console.log(\n ` Platform: ${s.platform} | Type: ${s.type} | Members: ${s.totalMembers} | Messages: ${s.totalMessages}`\n )\n if (timeRange) console.log(` Range: ${timeRange}`)\n console.log()\n }\n }\n\n dbManager.closeAll()\n })\n\nprogram\n .command('stats <session-id>')\n .description('Show session statistics overview')\n .option('--format <format>', 'Output format (table|json)', 'table')\n .option('--top <n>', 'Show top N active members', '10')\n .action((sessionId, options) => {\n const { dbManager } = initRuntime()\n const db = dbManager.open(sessionId)\n if (!db) {\n console.error(`Session ${sessionId} not found`)\n process.exit(1)\n }\n\n const meta = getSessionMeta(db)\n const overview = getSessionOverview(db)\n const topMembers = getMemberActivity(db).slice(0, parseInt(options.top))\n\n if (options.format === 'json') {\n console.log(JSON.stringify({ meta, overview, topMembers }, null, 2))\n } else {\n console.log(`\\nSession: ${meta?.name}`)\n console.log(`Platform: ${meta?.platform} | Type: ${meta?.type}`)\n console.log(`Total messages: ${overview.totalMessages}`)\n console.log(`Total members: ${overview.totalMembers}`)\n console.log(`Time range: ${formatTimeRange(overview.firstMessageTs, overview.lastMessageTs)}`)\n\n if (topMembers.length > 0) {\n console.log(`\\nActivity ranking (Top ${options.top}):`)\n for (const [i, m] of topMembers.entries()) {\n console.log(` ${i + 1}. ${m.name} - ${m.messageCount} messages (${m.percentage}%)`)\n }\n }\n }\n\n dbManager.closeAll()\n })\n\nprogram\n .command('search <session-id> <keyword>')\n .description('Search messages by keyword')\n .option('--limit <n>', 'Max results to return', '20')\n .option('--format <format>', 'Output format (table|json)', 'table')\n .action((sessionId, keyword, options) => {\n const { dbManager } = initRuntime()\n const db = dbManager.open(sessionId)\n if (!db) {\n console.error(`Session ${sessionId} not found`)\n process.exit(1)\n }\n\n const limit = parseInt(options.limit)\n const result = searchMessagesLike(db, keyword, { limit })\n\n if (options.format === 'json') {\n console.log(JSON.stringify(result, null, 2))\n } else {\n console.log(\n `Search \"${keyword}\" - ${result.total} result(s)${result.hasMore ? ' (showing first ' + limit + ')' : ''}:\\n`\n )\n for (const msg of result.messages) {\n const time = new Date(msg.timestamp * 1000).toLocaleString()\n console.log(` [${time}] ${msg.senderName}: ${msg.content}`)\n }\n }\n\n dbManager.closeAll()\n })\n\nprogram\n .command('members <session-id>')\n .description('List session members')\n .option('--format <format>', 'Output format (table|json)', 'table')\n .action((sessionId, options) => {\n const { dbManager } = initRuntime()\n const db = dbManager.open(sessionId)\n if (!db) {\n console.error(`Session ${sessionId} not found`)\n process.exit(1)\n }\n\n const members = getMembers(db)\n\n if (options.format === 'json') {\n console.log(JSON.stringify(members, null, 2))\n } else {\n console.log(`${members.length} member(s):\\n`)\n for (const [i, m] of members.entries()) {\n console.log(` ${i + 1}. ${m.name} (${m.platformId}) - ${m.messageCount} messages`)\n }\n }\n\n dbManager.closeAll()\n })\n\nprogram\n .command('query <session-id>')\n .description('Execute a read-only SQL query on a session database')\n .requiredOption('--sql <sql>', 'SQL query statement')\n .option('--format <format>', 'Output format (table|json)', 'table')\n .action((sessionId, options) => {\n const { dbManager } = initRuntime()\n const db = dbManager.open(sessionId)\n if (!db) {\n console.error(`Session ${sessionId} not found`)\n process.exit(1)\n }\n\n try {\n const result = executeReadonlySql(db, options.sql)\n if (options.format === 'json') {\n console.log(JSON.stringify(result, null, 2))\n } else {\n if (result.rows.length === 0) {\n console.log('No results.')\n } else {\n printTable(result.columns, result.rows)\n console.log(`\\n${result.rowCount} row(s)${result.truncated ? ' (truncated)' : ''}`)\n }\n }\n } catch (err) {\n console.error(`SQL error: ${err instanceof Error ? err.message : err}`)\n process.exit(1)\n }\n\n dbManager.closeAll()\n })\n\nprogram\n .command('import <file>')\n .description('Import a chat history file (14+ formats: QQ/WeChat/Telegram/WhatsApp/LINE/Discord/Instagram, etc.)')\n .option('--session-id <id>', 'Specify session ID (auto-generated if omitted)')\n .option('--format <id>', 'Specify format ID (skip auto-detection)')\n .action(async (file, options) => {\n if (!fs.existsSync(file)) {\n console.error(`File not found: ${file}`)\n process.exit(1)\n }\n\n const { streamImport, detectFormat } = await import('./import')\n const { dbManager } = initRuntime()\n const nativeBinding = resolveNativeBinding()\n\n const format = detectFormat(file)\n if (!format && !options.format) {\n console.error(`Unrecognized file format: ${file}`)\n console.error('Use --format <id> to specify manually, or run \"chatlab formats\" to see supported formats')\n process.exit(1)\n }\n\n console.log(`Importing: ${file}`)\n if (format) console.log(` Format: ${format.name} (${format.platform})`)\n\n try {\n const result = await streamImport(dbManager, file, {\n formatId: options.format,\n nativeBinding,\n onProgress: (p) => {\n process.stdout.write(`\\r ${p.stage}: ${p.progress}%`)\n },\n })\n\n if (result.success) {\n console.log(`\\n\\nImport succeeded!`)\n console.log(` Session ID: ${result.sessionId}`)\n console.log(` Messages written: ${result.diagnostics?.messagesWritten ?? 0}`)\n console.log(` Messages skipped: ${result.diagnostics?.messagesSkipped ?? 0}`)\n } else {\n console.error(`\\n\\nImport failed: ${result.error}`)\n process.exit(1)\n }\n } catch (err) {\n console.error(`\\n\\nImport error: ${err instanceof Error ? err.message : err}`)\n process.exit(1)\n } finally {\n dbManager.closeAll()\n }\n })\n\nprogram\n .command('formats')\n .description('List all supported chat history formats')\n .action(async () => {\n const { getSupportedFormats } = await import('./import')\n const formats = getSupportedFormats()\n console.log(`${formats.length} supported format(s):\\n`)\n for (const f of formats) {\n console.log(` ${f.id.padEnd(30)} ${f.name} (${f.platform}) [${f.extensions.join(', ')}]`)\n }\n })\n\nprogram\n .command('mcp')\n .description('Start MCP Server (stdio transport, for ClaudeCode / Cursor / AI agents)')\n .action(async () => {\n const { startCliMcpServer } = await import('./mcp')\n await startCliMcpServer()\n })\n\nprogram\n .command('start')\n .description('Start ChatLab (HTTP API + Web UI)')\n .option('--port <port>', 'Server port', String(DEFAULT_API_PORT))\n .option('--host <host>', 'Listen address', '127.0.0.1')\n .option('--token <token>', 'Custom Bearer Token (reads from config or auto-generates if omitted)')\n .option('--headless', 'API-only mode, do not serve the Web UI')\n .option('--no-open', 'Do not auto-open the browser')\n .option('--daemon', 'Run as a resident system service (auto-start on login, macOS/Linux)')\n .action(async (options) => {\n // --daemon: install as system service and exit\n if (options.daemon) {\n const { serviceInstall } = await import('./daemon/service')\n serviceInstall({\n port: parseInt(options.port, 10),\n host: options.host,\n token: options.token || undefined,\n headless: options.headless,\n })\n return\n }\n\n const { startHttpServer } = await import('./http')\n const port = parseInt(options.port, 10)\n\n let webRoot: string | undefined\n if (!options.headless) {\n const webDir = resolveCliPath('dist-web')\n if (fs.existsSync(webDir)) {\n webRoot = webDir\n } else {\n console.warn('Warning: dist-web/ not found, starting in API-only mode')\n }\n }\n\n try {\n // 启动前预检端口,快速失败,避免无谓的初始化后再报 EADDRINUSE;\n // 置于 try 内确保非 EADDRINUSE 错误(EACCES/EADDRNOTAVAIL 等)\n // 也能走到统一的 Startup failed 错误处理路径。\n if (!(await isPortAvailable(port, options.host))) {\n console.error(formatPortInUseError(port))\n process.exit(1)\n }\n\n const info = await startHttpServer({\n port,\n host: options.host,\n token: options.token || undefined,\n webRoot,\n })\n\n const { startPeriodicUpdateCheck } = await import('./update-checker')\n startPeriodicUpdateCheck()\n\n const displayHost = info.host === '0.0.0.0' ? '127.0.0.1' : info.host\n const url = `http://${displayHost}:${info.port}`\n\n console.log(`\\nChatLab v${getVersion()}`)\n if (webRoot) console.log(` Web UI: ${url}/`)\n console.log(` API: ${url}`)\n console.log(` Token: ${info.token}`)\n console.log(`\\nExample:`)\n console.log(` curl -H \"Authorization: Bearer ${info.token}\" ${url}/api/v1/status`)\n\n if (webRoot && options.open) {\n openBrowser(url)\n console.log(`\\nBrowser opened. Press Ctrl+C to stop.\\n`)\n } else {\n console.log(`\\nPress Ctrl+C to stop.\\n`)\n }\n\n const shutdown = async () => {\n console.log('\\nShutting down...')\n const { stopHttpServer } = await import('./http')\n await stopHttpServer()\n process.exit(0)\n }\n\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err)\n if (message.includes('EADDRINUSE')) {\n // 极低概率的 TOCTOU 竞态(预检通过后端口被占),复用统一文案\n console.error(formatPortInUseError(port))\n } else {\n console.error(`Startup failed: ${message}`)\n }\n process.exit(1)\n }\n })\n\nconst configCmd = program.command('config').description('Configuration management')\n\nconfigCmd\n .command('path')\n .description('Show config file path')\n .action(() => {\n console.log(getConfigPath())\n })\n\nconfigCmd\n .command('show')\n .description('Show current configuration')\n .action(() => {\n const config = loadConfig()\n console.log(JSON.stringify(config, null, 2))\n })\n\nprogram\n .command('stop')\n .description('Stop the resident service and remove auto-start (reverse of start --daemon)')\n .action(async () => {\n const { serviceUninstall } = await import('./daemon/service')\n serviceUninstall()\n })\n\nprogram\n .command('status')\n .description('Show resident service status')\n .action(async () => {\n const { getServiceStatus } = await import('./daemon/service')\n const svc = getServiceStatus()\n\n console.log('\\nChatLab Status')\n console.log('─'.repeat(36))\n\n if (svc.installed) {\n const portStr = svc.port ? `http://${svc.host ?? '127.0.0.1'}:${svc.port}` : ''\n console.log(` Service: ${svc.running ? 'running' : 'installed (not running)'}`)\n if (portStr) console.log(` Address: ${portStr}`)\n console.log(` Auto-start: enabled`)\n console.log(`\\n Use \\`chatlab stop\\` to remove the service.\\n`)\n } else {\n console.log(` Service: not installed`)\n console.log(` Auto-start: disabled`)\n console.log(`\\n Use \\`chatlab start --daemon\\` to install as a system service.\\n`)\n }\n })\n\n// --- 工具函数 ---\n\nfunction openBrowser(url: string): void {\n try {\n const cmd =\n process.platform === 'darwin'\n ? `open \"${url}\"`\n : process.platform === 'win32'\n ? `start \"\" \"${url}\"`\n : `xdg-open \"${url}\"`\n execSync(cmd, { stdio: 'ignore' })\n } catch {\n console.log(` Open manually: ${url}`)\n }\n}\n\n/**\n * Resolve standalone better-sqlite3 native module path.\n * Used in non-Electron environments to avoid electron-rebuild conflicts.\n */\nfunction resolveNativeBinding(): string | undefined {\n if (process.versions.electron) return undefined\n const nativePath = resolveCliPath('native/better_sqlite3.node')\n if (fs.existsSync(nativePath)) return nativePath\n return undefined\n}\n\nfunction initRuntime() {\n const config = loadConfig()\n const userDataDir = config.data.user_data_dir || undefined\n const pathProvider = new NodePathProvider(userDataDir)\n pathProvider.ensureAllDirs()\n\n if (hasPendingElectronDataWarning() || !verifyCliDataPath(pathProvider.getDatabaseDir())) {\n printElectronDataError()\n process.exit(1)\n }\n\n const nativeBinding = resolveNativeBinding()\n const dbManager = new DatabaseManager(pathProvider, { nativeBinding })\n return { config, pathProvider, dbManager }\n}\n\nfunction printElectronDataError(): void {\n console.error('\\n' + '='.repeat(68))\n console.error(' ChatLab: Electron desktop data not found')\n console.error('='.repeat(68))\n console.error('')\n console.error(' Detected that ChatLab desktop app was installed on this machine,')\n console.error(' but could not locate your chat databases.')\n console.error('')\n console.error(' This usually means you changed the data directory in desktop settings.')\n console.error('')\n console.error(' To fix this, choose one of:')\n console.error('')\n console.error(' 1. Open ChatLab desktop app — it will auto-migrate your data')\n console.error(' 2. Set the data directory manually:')\n console.error(' export CHATLAB_DATA_DIR=\"/path/to/your/data\"')\n console.error(' 3. Edit ~/.chatlab/config.toml:')\n console.error(' [data]')\n console.error(' user_data_dir = \"/path/to/your/data\"')\n console.error('')\n console.error('='.repeat(68) + '\\n')\n}\n\nfunction formatTimeRange(first: number | null, last: number | null): string {\n if (!first || !last) return 'Unknown'\n const from = new Date(first * 1000).toLocaleDateString()\n const to = new Date(last * 1000).toLocaleDateString()\n return `${from} ~ ${to}`\n}\n\nfunction printTable(columns: string[], rows: Record<string, unknown>[]): void {\n const widths = columns.map((col) => {\n const maxData = rows.reduce((max, row) => {\n const val = String(row[col] ?? '')\n return Math.max(max, val.length)\n }, 0)\n return Math.max(col.length, Math.min(maxData, 40))\n })\n\n const header = columns.map((col, i) => col.padEnd(widths[i])).join(' | ')\n const separator = widths.map((w) => '-'.repeat(w)).join('-+-')\n console.log(header)\n console.log(separator)\n\n for (const row of rows) {\n const line = columns\n .map((col, i) => {\n const val = String(row[col] ?? '')\n return val.length > 40 ? val.slice(0, 37) + '...' : val.padEnd(widths[i])\n })\n .join(' | ')\n console.log(line)\n }\n}\n\n/** CLI entry function */\nexport function run(argv?: string[]): void {\n program.parse(argv)\n}\n\n// Auto-execute when run directly as a script\nconst isDirectRun = process.argv[1]?.endsWith('cli.ts') || process.argv[1]?.endsWith('cli.js')\nif (isDirectRun) {\n run()\n}\n","/**\n * 端口可用性检测与错误文案\n */\n\nimport * as net from 'net'\n\n/**\n * 检测指定端口在给定 host 上是否可用。\n * 使用 net.createServer() 试探性绑定,无副作用(成功后立即释放)。\n */\nexport function isPortAvailable(port: number, host: string): Promise<boolean> {\n return new Promise((resolve, reject) => {\n const server = net.createServer()\n server.once('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n resolve(false)\n } else {\n reject(err)\n }\n })\n server.once('listening', () => server.close(() => resolve(true)))\n server.listen(port, host)\n })\n}\n\n/**\n * 返回端口被占用时统一的友好报错文案。\n */\nexport function formatPortInUseError(port: number): string {\n return [\n ``,\n ` ✖ Error: port ${port} is already in use.`,\n ``,\n ` Another process (possibly a ChatLab instance) is using this port. You can:`,\n ` • Use another port: chatlab start --port <port>`,\n ` • Find the process: lsof -iTCP:${port} -sTCP:LISTEN`,\n ``,\n ].join('\\n')\n}\n"],"mappings":"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","names":[]}
@@ -14,22 +14,7 @@ function resolveCliPath(...segments) {
14
14
  return path.resolve(packageRoot, ...segments);
15
15
  }
16
16
 
17
- // src/version.ts
18
- import { readFileSync } from "fs";
19
- var cached;
20
- function getVersion() {
21
- if (cached) return cached;
22
- try {
23
- const pkgPath = resolveCliPath("package.json");
24
- cached = JSON.parse(readFileSync(pkgPath, "utf-8")).version;
25
- } catch {
26
- cached = "0.0.0-dev";
27
- }
28
- return cached;
29
- }
30
-
31
17
  export {
32
- resolveCliPath,
33
- getVersion
18
+ resolveCliPath
34
19
  };
35
- //# sourceMappingURL=chunk-6NB475AA.mjs.map
20
+ //# sourceMappingURL=chunk-FJ35V3TE.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/paths.ts"],"sourcesContent":["import path from 'path'\nimport { fileURLToPath } from 'url'\n\nexport function resolveCliPath(...segments: string[]): string {\n const moduleDir = path.dirname(fileURLToPath(import.meta.url))\n const packageRoot = path.basename(moduleDir) === 'dist' ? path.dirname(moduleDir) : path.resolve(moduleDir, '..')\n return path.resolve(packageRoot, ...segments)\n}\n"],"mappings":";;;;;;;;AAAA,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAEvB,SAAS,kBAAkB,UAA4B;AAC5D,QAAM,YAAY,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAC7D,QAAM,cAAc,KAAK,SAAS,SAAS,MAAM,SAAS,KAAK,QAAQ,SAAS,IAAI,KAAK,QAAQ,WAAW,IAAI;AAChH,SAAO,KAAK,QAAQ,aAAa,GAAG,QAAQ;AAC9C;","names":[]}