refactor: migrate CLI from packages/server to apps/cli

Move the CLI application from packages/server/ to apps/cli/ to align
with the monorepo convention (apps/ for deployable applications,
packages/ for shared libraries). Rename package to @openchatlab/chatlab.

Also fix a bug in ensureToken() that used raw string manipulation to
write config.toml, causing duplicate token fields that corrupted the
TOML file. This led to config parse failures and user_data_dir being
overwritten with defaults. Now uses writeConfigField() for safe writes.
This commit is contained in:
digua committed 2026-05-19 21:33:00 +08:00
1 parent 9db30879d3
commit 78e41a65b5
40 files changed
+74 -69

No files matched your search

+1 -1
View File
@@ -18,7 +18,7 @@ error-log-*.txt
# Project
dist/
dist-web/
packages/server/native/
apps/cli/native/
.history/
out
.obskey
@@ -8,6 +8,6 @@
*/
// 开发阶段占位,实际通过 package.json scripts 的 tsx 运行
console.error('Please run via: npx tsx packages/server/src/cli.ts')
console.error('Or use: pnpm --filter chatlab dev')
console.error('Please run via: npx tsx apps/cli/src/cli.ts')
console.error('Or use: pnpm --filter @openchatlab/chatlab cli')
process.exit(1)
@@ -1,5 +1,5 @@
{
"name": "chatlab",
"name": "@openchatlab/chatlab",
"version": "0.0.1",
"private": true,
"description": "ChatLab CLI & Service - 聊天记录分析工具",
File renamed without changes.
@@ -34,14 +34,14 @@ test('reports invalid native binding load failure', () => {
})
test('reports valid native binding when it can be loaded by current Node', () => {
const nativePath = path.resolve('packages/server/native/better_sqlite3.node')
const nativePath = path.resolve('apps/cli/native/better_sqlite3.node')
const status = getNativeStatus(nativePath)
assert.equal(status.ok, true)
assert.equal(status.reason, 'valid')
})
test('prints status to stderr so CLI stdout stays machine-readable', () => {
const result = spawnSync(process.execPath, ['packages/server/scripts/ensure-native.mjs', '--check'], {
const result = spawnSync(process.execPath, ['apps/cli/scripts/ensure-native.mjs', '--check'], {
encoding: 'utf8',
})
@@ -3,7 +3,7 @@
# 在隔离目录中编译 better-sqlite3 的系统 Node.js 原生模块,
# 避免与 electron-rebuild 产生冲突。
#
# 产物:packages/server/native/better_sqlite3.node
# 产物:apps/cli/native/better_sqlite3.node
#
set -e
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -9,7 +9,7 @@ import * as fs from 'fs'
import * as path from 'path'
import * as crypto from 'crypto'
import type { FastifyInstance } from 'fastify'
import { loadConfig, getConfigDir, MigrationRunner, ALL_MIGRATIONS } from '@openchatlab/config'
import { loadConfig, writeConfigField, MigrationRunner, ALL_MIGRATIONS } from '@openchatlab/config'
import type { ChatLabConfig } from '@openchatlab/config'
import { NodePathProvider, DatabaseManager, AIConversationManager } from '@openchatlab/node-runtime'
import { createServer } from './server'
@@ -45,22 +45,10 @@ function ensureToken(config: ChatLabConfig): string {
if (config.api.token) return config.api.token
const token = `clb_${crypto.randomBytes(32).toString('hex')}`
const configDir = getConfigDir()
const configPath = path.join(configDir, 'config.toml')
try {
let content = ''
if (fs.existsSync(configPath)) {
content = fs.readFileSync(configPath, 'utf-8')
}
if (!content.includes('[api]')) {
content += `\n[api]\ntoken = "${token}"\n`
} else {
content = content.replace(/\[api\]/, `[api]\ntoken = "${token}"`)
}
fs.mkdirSync(configDir, { recursive: true })
fs.writeFileSync(configPath, content, 'utf-8')
writeConfigField('api', 'token', token)
} catch {
// 写入失败时仍使用生成的 token 运行本次会话
// best-effort: token still usable for this session
}
return token
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+17
View File
@@ -0,0 +1,17 @@
{
"extends": "../../tsconfig.node.json",
"compilerOptions": {
"composite": true,
"noEmit": true
},
"include": [
"src/**/*.ts",
"../../packages/shared-types/**/*",
"../../packages/core/**/*",
"../../packages/node-runtime/**/*",
"../../packages/config/**/*",
"../../packages/parser/**/*",
"../../packages/tools/**/*",
"../../packages/sync/**/*"
]
}
+1 -1
View File
@@ -11,7 +11,7 @@
"../../packages/node-runtime/**/*",
"../../packages/config/**/*",
"../../packages/parser/**/*",
"../../packages/server/**/*",
"../../apps/cli/**/*",
"../../packages/tools/**/*",
"../../packages/sync/**/*"
],
+1 -1
View File
@@ -28,7 +28,7 @@
"dev:app": "vite --config vite.web.config.mts",
"dev:serve": "bash scripts/dev-serve.sh",
"dev:web": "pnpm run ensure:server-native && CHATLAB_AUTO_SERVE=1 vite --config vite.web.config.mts",
"ensure:server-native": "pnpm --filter chatlab run ensure-native",
"ensure:server-native": "pnpm --filter @openchatlab/chatlab run ensure-native",
"build:web": "vite build --config vite.web.config.mts",
"format": "prettier --write .",
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
+43 -43
View File
@@ -88,6 +88,49 @@ importers:
specifier: ^3.1.1
version: 3.2.6(typescript@5.9.3)
apps/cli:
dependencies:
'@fastify/multipart':
specifier: ^10.0.0
version: 10.0.0
'@fastify/static':
specifier: ^9.1.3
version: 9.1.3
'@modelcontextprotocol/sdk':
specifier: ^1.12.1
version: 1.29.0(zod@3.25.76)
'@openchatlab/config':
specifier: workspace:*
version: link:../../packages/config
'@openchatlab/core':
specifier: workspace:*
version: link:../../packages/core
'@openchatlab/node-runtime':
specifier: workspace:*
version: link:../../packages/node-runtime
'@openchatlab/parser':
specifier: workspace:*
version: link:../../packages/parser
'@openchatlab/shared-types':
specifier: workspace:*
version: link:../../packages/shared-types
'@openchatlab/sync':
specifier: workspace:*
version: link:../../packages/sync
'@openchatlab/tools':
specifier: workspace:*
version: link:../../packages/tools
commander:
specifier: ^13.1.0
version: 13.1.0
fastify:
specifier: ^5.8.4
version: 5.8.4
devDependencies:
tsx:
specifier: ^4.21.0
version: 4.21.0
apps/desktop:
dependencies:
'@aptabase/electron':
@@ -181,49 +224,6 @@ importers:
specifier: ^1.7.8
version: 1.7.8
packages/server:
dependencies:
'@fastify/multipart':
specifier: ^10.0.0
version: 10.0.0
'@fastify/static':
specifier: ^9.1.3
version: 9.1.3
'@modelcontextprotocol/sdk':
specifier: ^1.12.1
version: 1.29.0(zod@3.25.76)
'@openchatlab/config':
specifier: workspace:*
version: link:../config
'@openchatlab/core':
specifier: workspace:*
version: link:../core
'@openchatlab/node-runtime':
specifier: workspace:*
version: link:../node-runtime
'@openchatlab/parser':
specifier: workspace:*
version: link:../parser
'@openchatlab/shared-types':
specifier: workspace:*
version: link:../shared-types
'@openchatlab/sync':
specifier: workspace:*
version: link:../sync
'@openchatlab/tools':
specifier: workspace:*
version: link:../tools
commander:
specifier: ^13.1.0
version: 13.1.0
fastify:
specifier: ^5.8.4
version: 5.8.4
devDependencies:
tsx:
specifier: ^4.21.0
version: 4.21.0
packages/shared-types: {}
packages/sync: {}
+1 -1
View File
@@ -7,4 +7,4 @@ pnpm run ensure:server-native
exec tsx watch \
--include 'packages/core/src/**' \
--include 'packages/node-runtime/src/**' \
packages/server/src/cli.ts serve "$@"
apps/cli/src/cli.ts serve "$@"
+1 -1
View File
@@ -50,7 +50,7 @@ function chatlabServePlugin(): Plugin {
return
}
const serverDir = resolve(__dirname, 'packages/server')
const serverDir = resolve(__dirname, 'apps/cli')
const coreDir = resolve(__dirname, 'packages/core/src')
const runtimeDir = resolve(__dirname, 'packages/node-runtime/src')
serverProcess = spawn(