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.
- package/README.md +53 -30
- package/dist/{chunk-OC2W5Q5T.mjs → chunk-3MX7VF2B.mjs} +2 -2
- package/dist/chunk-5ADKYYNA.mjs +28 -0
- package/dist/chunk-5ADKYYNA.mjs.map +1 -0
- package/dist/{chunk-QRC3RSER.mjs → chunk-643S2N2U.mjs} +2 -2
- package/dist/{chunk-LZ3W6NS5.mjs → chunk-AHQQQ5HZ.mjs} +101 -69
- package/dist/chunk-AHQQQ5HZ.mjs.map +1 -0
- package/dist/{chunk-6NB475AA.mjs → chunk-FJ35V3TE.mjs} +2 -17
- package/dist/chunk-FJ35V3TE.mjs.map +1 -0
- package/dist/{chunk-SQKT45MM.mjs → chunk-G7Q7EQ3V.mjs} +546 -84
- package/dist/chunk-G7Q7EQ3V.mjs.map +1 -0
- package/dist/{chunk-XPBPKH6B.mjs → chunk-L37WJ33K.mjs} +134 -37
- package/dist/chunk-L37WJ33K.mjs.map +1 -0
- package/dist/{chunk-Z2LYMKCC.mjs → chunk-WTQ7LAZR.mjs} +2 -2
- package/dist/cli.mjs +4 -3
- package/dist/{http-O5P4BYXU.mjs → http-JDORENHT.mjs} +8 -7
- package/dist/{import-BDLL3DG7.mjs → import-LCBSBLYF.mjs} +4 -4
- package/dist/index.mjs +10 -9
- package/dist/{mcp-L2TPZFE7.mjs → mcp-AR7HBIK4.mjs} +8 -6
- package/dist/{mcp-L2TPZFE7.mjs.map → mcp-AR7HBIK4.mjs.map} +1 -1
- package/dist/service-WCGGRZTA.mjs +258 -0
- package/dist/service-WCGGRZTA.mjs.map +1 -0
- package/dist/{src-FVJL5ENA.mjs → src-EGXULYFQ.mjs} +2 -2
- package/dist/{stream-import-ZTEW3YVL.mjs → stream-import-QM5R5FDG.mjs} +3 -3
- package/dist/{update-checker-MQMQ6A4B.mjs → update-checker-JG5K67O2.mjs} +3 -2
- package/dist/{update-checker-MQMQ6A4B.mjs.map → update-checker-JG5K67O2.mjs.map} +1 -1
- package/dist-web/assets/{DebugToolsPanel-DkbsB6lr.css → DebugToolsPanel-Yzfz2ntn.css} +1 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-DXxg55kL.js +161 -0
- package/dist-web/assets/{EChartWordcloud-Ubal_aYg.js → EChartWordcloud-Cenbzcyj.js} +1 -1
- package/dist-web/assets/MessageExportModal-DMHA4X0b.js +1 -0
- package/dist-web/assets/electron-DpE69lO4.js +1 -0
- package/dist-web/assets/electron-SyPZX9OU.js +1 -0
- package/dist-web/assets/{fetch-BS4SlQjA.js → fetch-B7OBmANJ.js} +1 -1
- package/dist-web/assets/{fetch-o5NJ2uWE.js → fetch-BikSxuED.js} +1 -1
- package/dist-web/assets/fetch-BwMZFwbF.js +1 -0
- package/dist-web/assets/fetch-BxE8RQYE.js +1 -0
- package/dist-web/assets/fetch-CvfdN4gM.js +17 -0
- package/dist-web/assets/{fetch-W43zfemj.js → fetch-LBHcBBkv.js} +1 -1
- package/dist-web/assets/http-C741fIwb.js +1 -0
- package/dist-web/assets/index-BUfibEyG.css +1 -0
- package/dist-web/assets/{index-DOwQB7In.js → index-C2439065.js} +6 -6
- package/dist-web/assets/index-CD5Fz-JY.js +7 -0
- package/dist-web/assets/index-DC0LjKV6.js +2 -0
- package/dist-web/assets/index-Dl-X-izi.css +1 -0
- package/dist-web/assets/index-DywUneNT.css +1 -0
- package/dist-web/assets/index-JvmxkAGk.js +173 -0
- package/dist-web/assets/index-vEVpfw-R.js +163 -0
- package/dist-web/assets/model-catalog-DUuKjIVU.js +1 -0
- package/dist-web/assets/{vendor-nuxt-ui-7OAuiz_i.js → vendor-nuxt-ui-3XQAyMd0.js} +2 -2
- package/dist-web/assets/vendor-reka-ui-BrTYuIfH.js +11 -0
- package/dist-web/assets/{web-Cx5APyCN.js → web-Bm94rL5B.js} +1 -1
- package/dist-web/index.html +4 -4
- package/package.json +3 -2
- package/dist/chunk-6NB475AA.mjs.map +0 -1
- package/dist/chunk-LZ3W6NS5.mjs.map +0 -1
- package/dist/chunk-SQKT45MM.mjs.map +0 -1
- package/dist/chunk-XPBPKH6B.mjs.map +0 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-CqacsKDU.js +0 -147
- package/dist-web/assets/MessageExportModal-D1GY6WSV.js +0 -1
- package/dist-web/assets/electron-8qi0d92j.js +0 -1
- package/dist-web/assets/electron-CsUM2O_Y.js +0 -1
- package/dist-web/assets/fetch-BOogCPfg.js +0 -1
- package/dist-web/assets/fetch-CclRWOHf.js +0 -1
- package/dist-web/assets/fetch-CxwnEEqk.js +0 -17
- package/dist-web/assets/http-CeXMt-Fv.js +0 -1
- package/dist-web/assets/index-BE64tUWq.css +0 -1
- package/dist-web/assets/index-CM29Kc4p.js +0 -173
- package/dist-web/assets/index-CbQMfheg.css +0 -1
- package/dist-web/assets/index-D183wYuz.js +0 -7
- package/dist-web/assets/index-DSpvXged.css +0 -1
- package/dist-web/assets/index-DTxRy2FH.js +0 -163
- package/dist-web/assets/index-grds3iG-.js +0 -2
- package/dist-web/assets/vendor-reka-ui-CW2cIgMu.js +0 -11
- /package/dist/{chunk-OC2W5Q5T.mjs.map → chunk-3MX7VF2B.mjs.map} +0 -0
- /package/dist/{chunk-QRC3RSER.mjs.map → chunk-643S2N2U.mjs.map} +0 -0
- /package/dist/{chunk-Z2LYMKCC.mjs.map → chunk-WTQ7LAZR.mjs.map} +0 -0
- /package/dist/{http-O5P4BYXU.mjs.map → http-JDORENHT.mjs.map} +0 -0
- /package/dist/{import-BDLL3DG7.mjs.map → import-LCBSBLYF.mjs.map} +0 -0
- /package/dist/{src-FVJL5ENA.mjs.map → src-EGXULYFQ.mjs.map} +0 -0
- /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/) · [
|
|
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/
|
|
31
|
-
- [Standardized Format Specification](https://docs.chatlab.fun/
|
|
32
|
-
- [Troubleshooting Guide](https://docs.chatlab.fun/
|
|
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
|

|
|
39
75
|
|
|
40
|
-
##
|
|
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
|
-
#
|
|
103
|
+
# Install dependencies
|
|
83
104
|
pnpm install
|
|
84
105
|
|
|
85
|
-
#
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
258
|
+
const { startCliMcpServer } = await import("./mcp-AR7HBIK4.mjs");
|
|
228
259
|
await startCliMcpServer();
|
|
229
260
|
});
|
|
230
|
-
program.command("
|
|
231
|
-
|
|
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.
|
|
235
|
-
const webDir =
|
|
275
|
+
if (!options.headless) {
|
|
276
|
+
const webDir = resolveCliPath("dist-web");
|
|
236
277
|
if (fs.existsSync(webDir)) {
|
|
237
|
-
webRoot =
|
|
278
|
+
webRoot = webDir;
|
|
238
279
|
} else {
|
|
239
|
-
console.warn(
|
|
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-
|
|
294
|
+
const { startPeriodicUpdateCheck } = await import("./update-checker-JG5K67O2.mjs");
|
|
250
295
|
startPeriodicUpdateCheck();
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
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-
|
|
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(
|
|
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-
|
|
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-
|
|
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":[]}
|