nx-ce 0.1.8 → 0.2.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 +8 -57
- package/bin/nx-ce.js +7 -5
- package/package.json +6 -5
- package/src/cli.js +23 -95
- package/src/index.js +6 -4
- package/src/serve.js +30 -6
- package/src/query.js +0 -128
- package/src/skills.js +0 -56
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/nx-ce)
|
|
4
4
|
[](https://github.com/joke-lx/nx-ce/actions/workflows/npm-publish.yml)
|
|
5
5
|
|
|
6
|
-
**nx-ce** is a lightweight Node.js adapter for `@anthropic-ai/claude-agent-sdk`.
|
|
6
|
+
**nx-ce** is a lightweight Node.js adapter for `@anthropic-ai/claude-agent-sdk`. As of **v0.2**, it provides a single mode:
|
|
7
7
|
|
|
8
|
-
- **`nx-ce
|
|
9
|
-
- **`nx-ce serve`** — WebSocket multi-session server (persistent, concurrent clients)
|
|
8
|
+
- **`nx-ce serve`** — WebSocket multi-session server (persistent, concurrent clients). All consumers (CLI scripts, Chrome extensions, native_host) connect via this single WS endpoint.
|
|
10
9
|
|
|
11
|
-
**nx-ce** 是一个轻量级 Node.js 适配器,封装了 `@anthropic-ai/claude-agent-sdk
|
|
12
|
-
|
|
10
|
+
**nx-ce** 是一个轻量级 Node.js 适配器,封装了 `@anthropic-ai/claude-agent-sdk`。
|
|
11
|
+
**v0.2 起只提供一种模式**:`nx-ce serve` 启动 WebSocket 多会话服务器。
|
|
12
|
+
所有调用方(CLI 脚本 / Chrome 扩展 / native_host)都通过这个唯一的 WS 端点与 SDK 通信。
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
@@ -35,47 +35,16 @@ npm install -g nx-ce
|
|
|
35
35
|
## Quick Start / 快速开始
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
#
|
|
39
|
-
nx-ce query "用中文回答:1+1=?" --model claude-haiku-4-5
|
|
40
|
-
|
|
41
|
-
# Start WebSocket server (persistent, multi-session)
|
|
38
|
+
# Start WebSocket server (the only mode)
|
|
42
39
|
nx-ce serve --port 3100
|
|
43
40
|
|
|
44
41
|
# In another terminal, run tests
|
|
45
42
|
node test/serve-test.mjs
|
|
46
43
|
```
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
## `nx-ce query` — One-Shot Cold-Start Query / 一次性冷启动查询
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
nx-ce query "解释这段代码" --model claude-sonnet-4-6
|
|
54
|
-
nx-ce query "继续之前的对话" --resume sess_abc123
|
|
55
|
-
nx-ce query "Analyze" --skill git-workflow,code-review
|
|
56
|
-
nx-ce query "Analyze" --skill all
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
| Flag | Description / 说明 |
|
|
60
|
-
|------|-------------------|
|
|
61
|
-
| `--model <id>` | Model override (default `claude-sonnet-4-6`) / 模型 ID |
|
|
62
|
-
| `--claude-path <path>` | Path to Claude CLI binary / Claude CLI 路径 |
|
|
63
|
-
| `--system-prompt <text>` | System prompt override / 系统提示词覆盖 |
|
|
64
|
-
| `--resume <sessionId>` | Resume a prior session (long conversation) / 续接会话 |
|
|
65
|
-
| `--skill <name>[,<name>...]` | Load specific skills (comma-separated, or `all`) / 加载 Skill |
|
|
66
|
-
| `--include-metadata` | Include skills/tools/slashCommands in output / 附带元数据 |
|
|
67
|
-
| `--no-persist` | Don't persist session / 不持久化 |
|
|
68
|
-
| `--env "KEY=val,KEY2=val"` | Extra environment variables / 额外环境变量 |
|
|
69
|
-
|
|
70
|
-
### JSON output
|
|
45
|
+
> **v0.2 breaking change**: `nx-ce query` and `nx-ce skills` CLI subcommands removed. All consumers must use the WebSocket protocol. See the [Protocol](#websocket-protocol--websocket-协议) section below.
|
|
71
46
|
|
|
72
|
-
|
|
73
|
-
// Default
|
|
74
|
-
{ "text": "2", "sessionId": "sess_abc" }
|
|
75
|
-
|
|
76
|
-
// With --include-metadata
|
|
77
|
-
{ "text": "2", "sessionId": "sess_abc", "metadata": { "skills": [...], "tools": [...], ... } }
|
|
78
|
-
```
|
|
47
|
+
> **v0.2 破坏性变更**:移除 `nx-ce query` 和 `nx-ce skills` 子命令。所有调用方必须使用 WebSocket 协议。
|
|
79
48
|
|
|
80
49
|
---
|
|
81
50
|
|
|
@@ -298,21 +267,6 @@ Each session has its own `agentQuery()`, `MessageChannel`, `MonotonicClock`, and
|
|
|
298
267
|
|
|
299
268
|
---
|
|
300
269
|
|
|
301
|
-
## `nx-ce skills` — List Available Skills / 列出可用 Skill
|
|
302
|
-
|
|
303
|
-
```bash
|
|
304
|
-
nx-ce skills --cwd "D:/project"
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
```json
|
|
308
|
-
{ "skills": ["code-review", "browse", ...],
|
|
309
|
-
"tools": ["Read", "Edit", "Bash", ...],
|
|
310
|
-
"slashCommands": ["code-review", ...],
|
|
311
|
-
"agents": ["Explore", ...] }
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
---
|
|
315
|
-
|
|
316
270
|
## State Persistence / 状态持久化
|
|
317
271
|
|
|
318
272
|
State files at `~/.nx-ce/instances/{key}.json`. Key format: `{name}~{cwd}`.
|
|
@@ -343,9 +297,6 @@ State files at `~/.nx-ce/instances/{key}.json`. Key format: `{name}~{cwd}`.
|
|
|
343
297
|
## Development / 开发
|
|
344
298
|
|
|
345
299
|
```bash
|
|
346
|
-
# One-shot query
|
|
347
|
-
node ./bin/nx-ce.js query "你好"
|
|
348
|
-
|
|
349
300
|
# Start server
|
|
350
301
|
node ./bin/nx-ce.js serve --port 3100
|
|
351
302
|
|
package/bin/nx-ce.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* nx-ce — Claude Engine
|
|
4
|
+
* nx-ce — Claude Engine (v0.2 serve-only)
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* nx-ce
|
|
6
|
+
* Single entry point — all consumers (CLI / Chrome extension / native_host)
|
|
7
|
+
* talk to the WebSocket server via the unified protocol.
|
|
8
|
+
*
|
|
9
|
+
* nx-ce serve [--port 3100] — start WebSocket server
|
|
10
|
+
* nx-ce status — list instance states
|
|
11
|
+
* nx-ce help — show help
|
|
10
12
|
*/
|
|
11
13
|
|
|
12
14
|
import { runCli } from '../src/cli.js';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nx-ce",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Claude Engine —
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Claude Engine — WebSocket adapter for @anthropic-ai/claude-agent-sdk. Single serve mode serves all consumers (CLI / Chrome extension / native_host) over a unified JSON-over-WebSocket protocol.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
19
|
"nx-ce": "node ./bin/nx-ce.js",
|
|
20
|
-
"test": "node test/
|
|
21
|
-
"start": "node ./bin/nx-ce.js"
|
|
20
|
+
"test": "node test/serve-test.mjs",
|
|
21
|
+
"start": "node ./bin/nx-ce.js serve"
|
|
22
22
|
},
|
|
23
23
|
"keywords": [
|
|
24
24
|
"claude",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"anthropic",
|
|
27
27
|
"sdk",
|
|
28
28
|
"native-messaging",
|
|
29
|
-
"chrome-extension"
|
|
29
|
+
"chrome-extension",
|
|
30
|
+
"websocket"
|
|
30
31
|
],
|
|
31
32
|
"license": "MIT",
|
|
32
33
|
"dependencies": {
|
package/src/cli.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CLI —
|
|
2
|
+
* CLI — 子命令路由器(v0.2 起仅 serve 模式)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* 历史:
|
|
5
|
+
* - 旧版有 query(冷启动)/ skills(独立拉元数据)子命令
|
|
6
|
+
* - v0.2 起所有调用统一收敛到 serve:所有元数据通过 getSkills 消息获取
|
|
7
|
+
*
|
|
8
|
+
* 路由: serve | status | help
|
|
6
9
|
*/
|
|
7
10
|
|
|
8
11
|
import { existsSync } from 'node:fs';
|
|
9
|
-
import { runQuery } from './query.js';
|
|
10
12
|
import { startServe } from './serve.js';
|
|
11
|
-
import { listSkills } from './skills.js';
|
|
12
13
|
import { readState, listStates } from './session-store.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -19,11 +20,9 @@ import { readState, listStates } from './session-store.js';
|
|
|
19
20
|
* @returns {{ cmd: string, flags: object, args: string[] }}
|
|
20
21
|
*/
|
|
21
22
|
export function parseArgs(argv = process.argv.slice(2)) {
|
|
22
|
-
const cmd = argv[0];
|
|
23
|
+
const cmd = argv[0];
|
|
23
24
|
const rest = argv.slice(1);
|
|
24
25
|
|
|
25
|
-
// 解析 --key=value 或 --key value 选项
|
|
26
|
-
// positional 只收集非 -- 开头的参数
|
|
27
26
|
const flags = {};
|
|
28
27
|
const positional = [];
|
|
29
28
|
for (let i = 0; i < rest.length; i++) {
|
|
@@ -31,13 +30,10 @@ export function parseArgs(argv = process.argv.slice(2)) {
|
|
|
31
30
|
if (arg.startsWith('--')) {
|
|
32
31
|
const eqIdx = arg.indexOf('=');
|
|
33
32
|
if (eqIdx !== -1) {
|
|
34
|
-
// --key=value 格式
|
|
35
33
|
flags[arg.slice(2, eqIdx)] = arg.slice(eqIdx + 1);
|
|
36
34
|
} else if (rest[i + 1] === undefined || rest[i + 1].startsWith('--')) {
|
|
37
|
-
// --key 后面没有值,或下一个参数也是 flag → boolean flag
|
|
38
35
|
flags[arg.slice(2)] = true;
|
|
39
36
|
} else {
|
|
40
|
-
// --key value 格式(下一个参数作为值)
|
|
41
37
|
flags[arg.slice(2)] = rest[++i] ?? true;
|
|
42
38
|
}
|
|
43
39
|
} else {
|
|
@@ -50,42 +46,14 @@ export function parseArgs(argv = process.argv.slice(2)) {
|
|
|
50
46
|
|
|
51
47
|
/**
|
|
52
48
|
* 运行 CLI 入口。
|
|
53
|
-
* 根据子命令分发到对应的处理函数。
|
|
54
49
|
*/
|
|
55
50
|
export async function runCli() {
|
|
56
|
-
const { cmd, flags
|
|
51
|
+
const { cmd, flags } = parseArgs();
|
|
57
52
|
|
|
58
53
|
switch (cmd) {
|
|
59
|
-
case 'query': {
|
|
60
|
-
// 冷启动查询
|
|
61
|
-
const prompt = args[0] || flags.prompt;
|
|
62
|
-
if (!prompt) {
|
|
63
|
-
throw new Error('用法: nx-ce query <prompt> [--model ...] [--claude-path ...]');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const result = await runQuery({
|
|
67
|
-
prompt,
|
|
68
|
-
model: flags.model,
|
|
69
|
-
cwd: flags.cwd || process.cwd(),
|
|
70
|
-
claudePath: resolveClaudePath(flags['claude-path']),
|
|
71
|
-
systemPrompt: flags['system-prompt'],
|
|
72
|
-
persistSession: flags['no-persist'] ? false : undefined,
|
|
73
|
-
resumeSessionId: flags.resume,
|
|
74
|
-
skills: parseSkills(flags.skill),
|
|
75
|
-
env: flags.env ? parseEnvString(flags.env) : undefined,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// 默认只返回 text + sessionId,加 --include-metadata 才返回 metadata
|
|
79
|
-
if (flags['include-metadata']) {
|
|
80
|
-
return result;
|
|
81
|
-
}
|
|
82
|
-
return { text: result.text, sessionId: result.sessionId };
|
|
83
|
-
}
|
|
84
|
-
|
|
85
54
|
case 'serve': {
|
|
86
|
-
// WebSocket
|
|
55
|
+
// WebSocket 持久化服务模式(唯一数据通路)
|
|
87
56
|
const name = flags.name || 'default';
|
|
88
|
-
|
|
89
57
|
const result = await startServe({
|
|
90
58
|
name,
|
|
91
59
|
claudePath: resolveClaudePath(flags['claude-path']),
|
|
@@ -94,57 +62,41 @@ export async function runCli() {
|
|
|
94
62
|
env: flags.env ? parseEnvString(flags.env) : undefined,
|
|
95
63
|
port: flags.port ? parseInt(flags.port, 10) : undefined,
|
|
96
64
|
});
|
|
97
|
-
|
|
98
65
|
return result;
|
|
99
66
|
}
|
|
100
67
|
|
|
101
68
|
case 'status': {
|
|
102
|
-
// 查询实例状态
|
|
103
69
|
const name = flags.name;
|
|
104
70
|
if (name) {
|
|
105
|
-
return readState(name);
|
|
71
|
+
return readState(name);
|
|
106
72
|
}
|
|
107
|
-
return listStates();
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
case 'skills': {
|
|
111
|
-
const result = await listSkills({
|
|
112
|
-
cwd: flags.cwd || process.cwd(),
|
|
113
|
-
claudePath: resolveClaudePath(flags['claude-path']),
|
|
114
|
-
env: flags.env ? parseEnvString(flags.env) : undefined,
|
|
115
|
-
});
|
|
116
|
-
return result;
|
|
73
|
+
return listStates();
|
|
117
74
|
}
|
|
118
75
|
|
|
119
76
|
case 'help':
|
|
77
|
+
case '--help':
|
|
78
|
+
case '-h':
|
|
120
79
|
default:
|
|
121
|
-
// 显示帮助信息
|
|
122
80
|
console.log(`
|
|
123
|
-
nx-ce — Claude Engine
|
|
81
|
+
nx-ce — Claude Engine (v0.2: serve-only)
|
|
124
82
|
|
|
125
83
|
用法:
|
|
126
|
-
nx-ce
|
|
127
|
-
--model <id> 模型覆盖
|
|
128
|
-
--claude-path <path> Claude CLI 路径
|
|
129
|
-
--system-prompt <text> 系统提示词覆盖
|
|
130
|
-
--resume <sessionId> 续接之前的会话(长对话)
|
|
131
|
-
--skill <name>[,<name>...] 加载指定 Skill(逗号分隔,传 "all" 加载全部)
|
|
132
|
-
--include-metadata 输出中附带 skills/tools/slash_commands 列表
|
|
133
|
-
--no-persist 不持久化会话
|
|
134
|
-
--env "KEY=value,KEY2=val" 额外环境变量
|
|
135
|
-
|
|
136
|
-
nx-ce serve WebSocket 持久化服务器(单一进程 / 多客户端)
|
|
84
|
+
nx-ce serve WebSocket 持久化服务器(唯一入口)
|
|
137
85
|
--name <name> 实例名称(默认: "default")
|
|
138
86
|
--port <port> WebSocket 端口(默认: 3100)
|
|
139
87
|
--model <id> 模型覆盖
|
|
140
88
|
--claude-path <path> Claude CLI 路径
|
|
89
|
+
--cwd <path> 默认工作目录
|
|
141
90
|
--env "KEY=value,..." 额外环境变量
|
|
142
91
|
|
|
143
92
|
nx-ce status [--name <name>] 查看实例状态
|
|
144
93
|
|
|
145
|
-
nx-ce skills [--cwd <path>] 列出 SDK 可用 skill/tool/agent
|
|
146
|
-
|
|
147
94
|
nx-ce help 显示此帮助
|
|
95
|
+
|
|
96
|
+
协议(ws://127.0.0.1:3100):
|
|
97
|
+
C→S: query / getSkills / getStatus / listSessions / closeSession / ping
|
|
98
|
+
S→C: connected / init / turn_start / text / thinking / tool_use / done /
|
|
99
|
+
error / pong / skills / status / session_list / session_closed
|
|
148
100
|
`);
|
|
149
101
|
return null;
|
|
150
102
|
}
|
|
@@ -152,32 +104,18 @@ export async function runCli() {
|
|
|
152
104
|
|
|
153
105
|
/**
|
|
154
106
|
* 解析 Claude CLI 路径。
|
|
155
|
-
* 优先使用命令行参数,然后检查环境变量。
|
|
156
|
-
*
|
|
157
|
-
* @param {string|undefined} flag - 命令行传入的路径
|
|
158
|
-
* @returns {string|undefined} 解析后的路径,未找到则返回 undefined(由 SDK 自动检测)
|
|
159
107
|
*/
|
|
160
108
|
function resolveClaudePath(flag) {
|
|
161
109
|
if (flag) return flag;
|
|
162
|
-
|
|
163
|
-
const candidates = [
|
|
164
|
-
process.env.CLAUDE_PATH,
|
|
165
|
-
process.env.CLAUDE_CLI_PATH,
|
|
166
|
-
// Windows: npx、npm 全局或用户本地安装
|
|
167
|
-
];
|
|
110
|
+
const candidates = [process.env.CLAUDE_PATH, process.env.CLAUDE_CLI_PATH];
|
|
168
111
|
for (const c of candidates) {
|
|
169
112
|
if (c && existsSync(c)) return c;
|
|
170
113
|
}
|
|
171
|
-
// 未找到,让 SDK 自动检测
|
|
172
114
|
return undefined;
|
|
173
115
|
}
|
|
174
116
|
|
|
175
117
|
/**
|
|
176
|
-
*
|
|
177
|
-
* 格式: "KEY=value,KEY2=val2"
|
|
178
|
-
*
|
|
179
|
-
* @param {string} str - 逗号分隔的 KEY=value 对
|
|
180
|
-
* @returns {object}
|
|
118
|
+
* 解析环境变量字符串 "KEY=val,KEY2=val2"
|
|
181
119
|
*/
|
|
182
120
|
function parseEnvString(str) {
|
|
183
121
|
const result = {};
|
|
@@ -189,13 +127,3 @@ function parseEnvString(str) {
|
|
|
189
127
|
}
|
|
190
128
|
return result;
|
|
191
129
|
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* 解析 --skill 参数。
|
|
195
|
-
* 逗号分隔的列表 → 数组;"all" → "all"(由 SDK 处理)。
|
|
196
|
-
*/
|
|
197
|
-
function parseSkills(value) {
|
|
198
|
-
if (value === undefined || value === null || value === '') return undefined;
|
|
199
|
-
if (value === 'all' || value === 'ALL') return 'all';
|
|
200
|
-
return value.split(',').map((s) => s.trim()).filter(Boolean);
|
|
201
|
-
}
|
package/src/index.js
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
* nx-ce — 公开 API 入口
|
|
3
3
|
*
|
|
4
4
|
* 用法:
|
|
5
|
-
* import {
|
|
6
|
-
*
|
|
5
|
+
* import { startServe, readState } from 'nx-ce';
|
|
6
|
+
* await startServe({ name: 'main', port: 3100 });
|
|
7
|
+
*
|
|
8
|
+
* v0.2 起:nx-ce 仅提供 WebSocket serve 模式,不再有冷启动 query。
|
|
9
|
+
* 所有调用方(CLI / Chrome 扩展 / native_host)都通过 WS 协议与 serve 通信。
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
|
-
export {
|
|
10
|
-
export { listSkills } from './skills.js';
|
|
12
|
+
export { startServe } from './serve.js';
|
|
11
13
|
export {
|
|
12
14
|
readState,
|
|
13
15
|
writeState,
|
package/src/serve.js
CHANGED
|
@@ -515,19 +515,43 @@ export async function startServe(options) {
|
|
|
515
515
|
break;
|
|
516
516
|
|
|
517
517
|
case 'getSkills': {
|
|
518
|
-
//
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
518
|
+
// 解析目标 session
|
|
519
|
+
// 带 session/cwd → 取该 session 的元数据
|
|
520
|
+
// 不带 → 取任意已 init 的 session 的元数据(server 级)
|
|
521
|
+
let meta = null;
|
|
522
|
+
if (req.session || req.cwd) {
|
|
523
|
+
const key = sessionKey(sessionName, req.cwd);
|
|
524
|
+
const session = sessionManager.sessions.get(key);
|
|
525
|
+
meta = session?.metadata;
|
|
526
|
+
} else {
|
|
527
|
+
// server 级:找任意一个已 init 的 session
|
|
528
|
+
for (const s of sessionManager.sessions.values()) {
|
|
529
|
+
if (s.metadata) { meta = s.metadata; break; }
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (meta) {
|
|
534
|
+
// 统一返回 type='skills',便于客户端按 type 路由
|
|
535
|
+
ws.send(JSON.stringify({
|
|
536
|
+
type: 'skills',
|
|
537
|
+
sessionId: meta.sessionId,
|
|
538
|
+
model: meta.model,
|
|
539
|
+
skills: meta.skills || [],
|
|
540
|
+
tools: meta.tools || [],
|
|
541
|
+
slashCommands: meta.slashCommands || [],
|
|
542
|
+
agents: meta.agents || [],
|
|
543
|
+
cwd: meta.cwd,
|
|
544
|
+
}));
|
|
523
545
|
} else {
|
|
546
|
+
// 无 init 元数据时:让客户端发一个 query 触发 init,或
|
|
547
|
+
// 等下一个 session 启动后再次 getSkills
|
|
524
548
|
ws.send(JSON.stringify({
|
|
525
549
|
type: 'skills',
|
|
526
550
|
skills: [],
|
|
527
551
|
tools: [],
|
|
528
552
|
slashCommands: [],
|
|
529
553
|
agents: [],
|
|
530
|
-
note: 'session
|
|
554
|
+
note: 'no session has been initialized yet — send a query first',
|
|
531
555
|
}));
|
|
532
556
|
}
|
|
533
557
|
break;
|
package/src/query.js
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 冷启动查询 — 对 @anthropic-ai/claude-agent-sdk 的一次性调用
|
|
3
|
-
*
|
|
4
|
-
* 参考 claudian 的 claudeColdStartQuery.ts(简单的非持久化路径)。
|
|
5
|
-
* 不含 MessageChannel、流式消费循环或 Electron 兼容代码。
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { query as agentQuery } from '@anthropic-ai/claude-agent-sdk';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* 执行一次冷启动查询并返回完整的文本结果。
|
|
12
|
-
*
|
|
13
|
-
* @param {object} options
|
|
14
|
-
* @param {string} options.prompt - 用户提示词
|
|
15
|
-
* @param {string} [options.systemPrompt] - 系统提示词覆盖
|
|
16
|
-
* @param {string} [options.model] - 模型 ID 覆盖
|
|
17
|
-
* @param {string} options.cwd - 工作目录
|
|
18
|
-
* @param {string} options.claudePath - Claude CLI 可执行文件路径
|
|
19
|
-
* @param {object} [options.env] - 额外的环境变量
|
|
20
|
-
* @param {string[]} [options.tools] - 工具白名单(省略则使用 SDK 默认值)
|
|
21
|
-
* @param {boolean} [options.persistSession] - 是否持久化会话(默认 true)
|
|
22
|
-
* @param {string} [options.resumeSessionId] - 恢复之前的会话
|
|
23
|
-
* @param {string[]|'all'} [options.skills] - 加载哪些 Skill(数组或 'all')
|
|
24
|
-
* @param {AbortController} [options.signal] - 中止信号
|
|
25
|
-
* @returns {Promise<{ text: string, sessionId: string | null, metadata: object | null }>}
|
|
26
|
-
*/
|
|
27
|
-
export async function runQuery(options) {
|
|
28
|
-
const {
|
|
29
|
-
prompt,
|
|
30
|
-
systemPrompt,
|
|
31
|
-
model,
|
|
32
|
-
cwd,
|
|
33
|
-
claudePath,
|
|
34
|
-
env = {},
|
|
35
|
-
tools,
|
|
36
|
-
persistSession,
|
|
37
|
-
resumeSessionId,
|
|
38
|
-
skills,
|
|
39
|
-
signal,
|
|
40
|
-
} = options;
|
|
41
|
-
|
|
42
|
-
// 组装 SDK 选项
|
|
43
|
-
const sdkOptions = {
|
|
44
|
-
cwd: cwd || process.cwd(),
|
|
45
|
-
model: model || 'claude-sonnet-4-6',
|
|
46
|
-
pathToClaudeCodeExecutable: claudePath,
|
|
47
|
-
permissionMode: 'bypassPermissions', // 跳过权限确认
|
|
48
|
-
allowDangerouslySkipPermissions: true,
|
|
49
|
-
env: {
|
|
50
|
-
...process.env,
|
|
51
|
-
...env, // 合并额外环境变量
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
// 以下为可选参数的条件注入
|
|
56
|
-
if (systemPrompt) {
|
|
57
|
-
sdkOptions.systemPrompt = systemPrompt;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (tools !== undefined) {
|
|
61
|
-
sdkOptions.tools = tools;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (persistSession === false) {
|
|
65
|
-
sdkOptions.persistSession = false;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (resumeSessionId) {
|
|
69
|
-
sdkOptions.resume = resumeSessionId;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (skills !== undefined) {
|
|
73
|
-
sdkOptions.skills = skills;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (signal) {
|
|
77
|
-
sdkOptions.abortController = signal;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 确保总有一个 abort controller
|
|
81
|
-
const abortController = signal || new AbortController();
|
|
82
|
-
sdkOptions.abortController = abortController;
|
|
83
|
-
|
|
84
|
-
// 发起 SDK 查询(返回异步迭代器)
|
|
85
|
-
const response = agentQuery({ prompt, options: sdkOptions });
|
|
86
|
-
|
|
87
|
-
let text = '';
|
|
88
|
-
let sessionId = null;
|
|
89
|
-
let metadata = null;
|
|
90
|
-
|
|
91
|
-
// 遍历 SDK 返回的流式消息
|
|
92
|
-
for await (const message of response) {
|
|
93
|
-
// 如果已中止,中断查询
|
|
94
|
-
if (abortController.signal.aborted) {
|
|
95
|
-
await response.interrupt();
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// 从 init 消息中捕获 sessionId + 技能/工具/命令 元数据
|
|
100
|
-
if (message.type === 'system' && message.subtype === 'init') {
|
|
101
|
-
sessionId = message.session_id;
|
|
102
|
-
metadata = {
|
|
103
|
-
model: message.model,
|
|
104
|
-
skills: message.skills || [],
|
|
105
|
-
tools: message.tools || [],
|
|
106
|
-
slashCommands: message.slash_commands || [],
|
|
107
|
-
agents: message.agents || [],
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// 提取助手的文本回复内容
|
|
112
|
-
if (message.type === 'assistant' && message.message?.content) {
|
|
113
|
-
const content = message.message.content;
|
|
114
|
-
if (typeof content === 'string') {
|
|
115
|
-
text += content;
|
|
116
|
-
} else if (Array.isArray(content)) {
|
|
117
|
-
// 内容块数组,筛选出 text 块
|
|
118
|
-
for (const block of content) {
|
|
119
|
-
if (block.type === 'text') {
|
|
120
|
-
text += block.text;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return { text, sessionId, metadata };
|
|
128
|
-
}
|
package/src/skills.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* skills — 查询 SDK 可用的 skill / tool / agent 列表
|
|
3
|
-
*
|
|
4
|
-
* 使用一次超轻量 agentQuery() 获取 init 元数据。
|
|
5
|
-
* 不发起真正对话,不持久化 session。
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { query as agentQuery } from '@anthropic-ai/claude-agent-sdk';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* 获取 SDK 可用的 skills/tools/slashCommands/agents 列表。
|
|
12
|
-
*
|
|
13
|
-
* @param {object} [options]
|
|
14
|
-
* @param {string} [options.cwd] - 工作目录
|
|
15
|
-
* @param {string} [options.claudePath] - Claude CLI 路径
|
|
16
|
-
* @param {object} [options.env] - 额外环境变量
|
|
17
|
-
* @returns {Promise<{ skills: string[], tools: string[], slashCommands: string[], agents: string[] }>}
|
|
18
|
-
*/
|
|
19
|
-
export async function listSkills(options = {}) {
|
|
20
|
-
const sdkOptions = {
|
|
21
|
-
cwd: options.cwd || process.cwd(),
|
|
22
|
-
model: 'claude-haiku-4-5',
|
|
23
|
-
permissionMode: 'bypassPermissions',
|
|
24
|
-
allowDangerouslySkipPermissions: true,
|
|
25
|
-
persistSession: false,
|
|
26
|
-
env: { ...process.env, ...options.env },
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
if (options.claudePath) {
|
|
30
|
-
sdkOptions.pathToClaudeCodeExecutable = options.claudePath;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// 用空 prompt 做一次超轻量 init
|
|
34
|
-
const response = agentQuery({ prompt: ' ', options: sdkOptions });
|
|
35
|
-
|
|
36
|
-
const result = {
|
|
37
|
-
skills: [],
|
|
38
|
-
tools: [],
|
|
39
|
-
slashCommands: [],
|
|
40
|
-
agents: [],
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
for await (const message of response) {
|
|
44
|
-
if (message.type === 'system' && message.subtype === 'init') {
|
|
45
|
-
if (Array.isArray(message.skills)) result.skills = message.skills;
|
|
46
|
-
if (Array.isArray(message.tools)) result.tools = message.tools;
|
|
47
|
-
if (Array.isArray(message.slash_commands)) result.slashCommands = message.slash_commands;
|
|
48
|
-
if (Array.isArray(message.agents)) result.agents = message.agents;
|
|
49
|
-
// init 消息后立即中断,不继续消耗资源
|
|
50
|
-
await response.interrupt().catch(() => {});
|
|
51
|
-
break;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return result;
|
|
56
|
-
}
|