feat(cli): add tsup build pipeline for npm publishing

- Rename package to `chatlab-cli`, version 0.1.0
- Add tsup config with ESM output, bundle @openchatlab/* + stream-json
- Update bin/chatlab.js from stub to actual entry point
- Unify version reading via src/version.ts (CLI/MCP/HTTP API)
- Add publishing fields: type, files, engines, publishConfig, keywords
- Move @openchatlab/* to devDependencies (bundled into dist/)
- Add better-sqlite3 to dependencies, @node-rs/jieba to optionalDependencies
- Fix --web default path for bundled dist/ directory structure
This commit is contained in:
digua committed 2026-05-19 21:46:00 +08:00
1 parent 78e41a65b5
commit b1427e2422
9 files changed
+366 -37

No files matched your search

+1 -12
View File
@@ -1,13 +1,2 @@
#!/usr/bin/env node
/**
* ChatLab CLI 入口
*
* 开发阶段:通过 pnpm --filter chatlab dev 运行(使用 tsx)
* 发布阶段:构建后直接作为 Node.js 脚本运行
*/
// 开发阶段占位,实际通过 package.json scripts 的 tsx 运行
console.error('Please run via: npx tsx apps/cli/src/cli.ts')
console.error('Or use: pnpm --filter @openchatlab/chatlab cli')
process.exit(1)
import('../dist/cli.js').then((m) => m.run())
+55 -10
View File
@@ -1,23 +1,70 @@
{
"name": "@openchatlab/chatlab",
"version": "0.0.1",
"private": true,
"name": "chatlab-cli",
"version": "0.1.0",
"description": "ChatLab CLI & Service - 聊天记录分析工具",
"type": "module",
"bin": {
"chatlab": "./bin/chatlab.js"
},
"main": "./src/index.ts",
"types": "./src/index.ts",
"main": "./dist/index.js",
"files": [
"bin/",
"dist/",
"dist-web/",
"LICENSE"
],
"engines": {
"node": ">=20"
},
"publishConfig": {
"access": "public"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/ChatLab/ChatLab.git",
"directory": "apps/cli"
},
"homepage": "https://github.com/ChatLab/ChatLab",
"keywords": [
"chatlab",
"chat",
"analysis",
"wechat",
"qq",
"telegram",
"whatsapp",
"discord",
"mcp",
"cli"
],
"scripts": {
"cli": "node scripts/ensure-native.mjs && tsx src/cli.ts",
"build": "tsup",
"ensure-native": "node scripts/ensure-native.mjs",
"rebuild-native": "bash scripts/rebuild-native.sh",
"rebuild-sqlite": "npm rebuild better-sqlite3"
"rebuild-sqlite": "npm rebuild better-sqlite3",
"prepublishOnly": "pnpm run build"
},
"dependencies": {
"@fastify/multipart": "^10.0.0",
"@fastify/static": "^9.1.3",
"@mariozechner/pi-agent-core": "0.54.2",
"@mariozechner/pi-ai": "0.54.2",
"@modelcontextprotocol/sdk": "^1.12.1",
"better-sqlite3": "^12.4.6",
"commander": "^13.1.0",
"fastify": "^5.8.4",
"gray-matter": "^4.0.3",
"js-tiktoken": "^1.0.21",
"smol-toml": "^1.3.1",
"stream-json": "^1.9.1",
"zod": "^3.24.4"
},
"optionalDependencies": {
"@node-rs/jieba": "^2.0.1"
},
"devDependencies": {
"@openchatlab/config": "workspace:*",
"@openchatlab/core": "workspace:*",
"@openchatlab/node-runtime": "workspace:*",
@@ -25,10 +72,8 @@
"@openchatlab/shared-types": "workspace:*",
"@openchatlab/sync": "workspace:*",
"@openchatlab/tools": "workspace:*",
"commander": "^13.1.0",
"fastify": "^5.8.4"
},
"devDependencies": {
"@types/better-sqlite3": "^7.6.13",
"tsup": "^8.5.0",
"tsx": "^4.21.0"
}
}
+3 -2
View File
@@ -18,10 +18,11 @@ import {
getMembers,
executeReadonlySql,
} from '@openchatlab/core'
import { getVersion } from './version'
const program = new Command()
program.name('chatlab').description('ChatLab - 聊天记录分析工具').version('0.0.1')
program.name('chatlab').description('ChatLab - 聊天记录分析工具').version(getVersion())
// chatlab sessions - 列出所有会话
program
@@ -295,7 +296,7 @@ program
let webRoot: string | undefined
if (options.web !== undefined) {
const webDir = typeof options.web === 'string' ? options.web : path.resolve(__dirname, '../../dist-web')
const webDir = typeof options.web === 'string' ? options.web : path.resolve(__dirname, '../dist-web')
if (fs.existsSync(webDir)) {
webRoot = path.resolve(webDir)
} else {
+2 -4
View File
@@ -8,9 +8,7 @@
import type { FastifyInstance } from 'fastify'
import type { DatabaseManager } from '@openchatlab/node-runtime'
import { successResponse } from '../errors'
// eslint-disable-next-line @typescript-eslint/no-require-imports
const pkg = require('../../../package.json') as { version: string }
import { getVersion } from '../../version'
export function registerSystemRoutes(server: FastifyInstance, dbManager: DatabaseManager): void {
server.get('/api/v1/status', async () => {
@@ -23,7 +21,7 @@ export function registerSystemRoutes(server: FastifyInstance, dbManager: Databas
return successResponse({
name: 'ChatLab API',
version: pkg.version,
version: getVersion(),
uptime: Math.floor(process.uptime()),
sessionCount,
})
+2 -1
View File
@@ -14,6 +14,7 @@ import { NodePathProvider, DatabaseManager } from '@openchatlab/node-runtime'
import { getSessionMeta, getSessionOverview, getDatabaseSchema } from '@openchatlab/core'
import { MCP_TOOL_REGISTRY, CoreDataProvider } from '@openchatlab/tools'
import type { SessionListContext } from '@openchatlab/tools/src/definitions/sessions'
import { getVersion } from './version'
const MCP_TOOL_PREFIX = 'chatlab_'
@@ -73,7 +74,7 @@ export async function startMcpServer(): Promise<void> {
const server = new McpServer({
name: 'chatlab',
version: '0.0.1',
version: getVersion(),
})
// --- 注册 Tools ---
+19
View File
@@ -0,0 +1,19 @@
import { readFileSync } from 'fs'
import { resolve } from 'path'
let cached: string | undefined
/**
* Read version from apps/cli/package.json at runtime.
* Works for both tsx dev (src/) and bundled (dist/).
*/
export function getVersion(): string {
if (cached) return cached
try {
const pkgPath = resolve(__dirname, '../package.json')
cached = JSON.parse(readFileSync(pkgPath, 'utf-8')).version
} catch {
cached = '0.0.0-dev'
}
return cached!
}
+27
View File
@@ -0,0 +1,27 @@
import { defineConfig } from 'tsup'
export default defineConfig({
entry: {
cli: 'src/cli.ts',
index: 'src/index.ts',
},
format: ['esm'],
outDir: 'dist',
splitting: true,
sourcemap: true,
clean: true,
target: 'node20',
platform: 'node',
noExternal: [/^@openchatlab\//, 'stream-json'],
external: ['better-sqlite3', '@node-rs/jieba'],
banner: {
js: [
"import { createRequire as __createRequire } from 'module';",
"import { dirname as __pathDirname } from 'path';",
"import { fileURLToPath as __fileURLToPath } from 'url';",
'const require = __createRequire(import.meta.url);',
'const __filename = __fileURLToPath(import.meta.url);',
'const __dirname = __pathDirname(__filename);',
].join('\n'),
},
})