@wu529778790/open-im 1.7.0-beta.1 → 1.7.0-beta.3

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 (42) hide show
  1. package/dist/adapters/claude-sdk-adapter.d.ts +7 -7
  2. package/dist/adapters/claude-sdk-adapter.js +127 -65
  3. package/dist/commands/handler.d.ts +0 -6
  4. package/dist/commands/handler.js +0 -62
  5. package/dist/config-web.js +3 -15
  6. package/dist/config.d.ts +0 -10
  7. package/dist/config.js +1 -54
  8. package/dist/dingtalk/event-handler.js +2 -4
  9. package/dist/dingtalk/message-sender.d.ts +0 -2
  10. package/dist/dingtalk/message-sender.js +1 -10
  11. package/dist/feishu/event-handler.js +4 -157
  12. package/dist/feishu/message-sender.d.ts +0 -20
  13. package/dist/feishu/message-sender.js +0 -155
  14. package/dist/index.js +0 -13
  15. package/dist/manager.js +5 -2
  16. package/dist/qq/event-handler.js +2 -4
  17. package/dist/qq/event-handler.test.js +0 -1
  18. package/dist/qq/message-sender.d.ts +0 -1
  19. package/dist/qq/message-sender.js +1 -6
  20. package/dist/setup.js +3 -7
  21. package/dist/shared/ai-task.js +6 -25
  22. package/dist/shared/system-messages.d.ts +0 -2
  23. package/dist/shared/system-messages.js +0 -32
  24. package/dist/shared/system-messages.test.js +1 -8
  25. package/dist/telegram/event-handler.js +2 -24
  26. package/dist/telegram/message-sender.d.ts +0 -1
  27. package/dist/telegram/message-sender.js +0 -14
  28. package/dist/wechat/event-handler.js +2 -28
  29. package/dist/wechat/message-sender.d.ts +0 -2
  30. package/dist/wechat/message-sender.js +0 -31
  31. package/dist/wework/event-handler.js +2 -4
  32. package/dist/wework/message-sender.d.ts +0 -2
  33. package/dist/wework/message-sender.js +1 -23
  34. package/package.json +1 -1
  35. package/dist/hook/permission-server.d.ts +0 -38
  36. package/dist/hook/permission-server.js +0 -301
  37. package/dist/hook/permission-server.test.d.ts +0 -1
  38. package/dist/hook/permission-server.test.js +0 -12
  39. package/dist/permission-mode/session-mode.d.ts +0 -7
  40. package/dist/permission-mode/session-mode.js +0 -59
  41. package/dist/permission-mode/types.d.ts +0 -11
  42. package/dist/permission-mode/types.js +0 -29
@@ -1,18 +1,18 @@
1
1
  /**
2
- * Claude SDK Adapter - 使用 Agent SDK 实现持久会话,无需每次 spawn 进程
2
+ * Claude SDK Adapter - 使用 Agent SDK V2 Session API 实现真正的多轮对话
3
3
  *
4
- * 优势:
5
- * 1. 进程内执行 - 无 fork/exec 开销,响应更快
6
- * 2. 会话复用 - resume 保留上下文,无需重新加载历史
7
- * 3. 流式输出 - includePartialMessages 支持 text_delta、thinking_delta
4
+ * V2 API 优势:
5
+ * 1. 进程内执行 - 无 fork/exec 开销
6
+ * 2. 持久会话 - SDKSession 对象保持会话状态,支持真正的多轮对话
7
+ * 3. 流式输出 - 支持实时增量更新
8
8
  *
9
- * 认证:ANTHROPIC_API_KEY 或 CLAUDE_CODE_OAUTH_TOKEN(claude setup-token)
9
+ * 认证:ANTHROPIC_API_KEY 或 CLAUDE_CODE_OAUTH_TOKEN
10
10
  */
11
11
  import type { ToolAdapter, RunCallbacks, RunOptions, RunHandle } from './tool-adapter.interface.js';
12
12
  export declare class ClaudeSDKAdapter implements ToolAdapter {
13
13
  readonly toolId = "claude-sdk";
14
14
  /**
15
- * 清理所有活跃的 SDK 查询
15
+ * 清理所有活跃的 SDK 会话和流
16
16
  */
17
17
  static destroy(): void;
18
18
  run(prompt: string, sessionId: string | undefined, workDir: string, callbacks: RunCallbacks, options?: RunOptions): RunHandle;
@@ -1,18 +1,21 @@
1
1
  /**
2
- * Claude SDK Adapter - 使用 Agent SDK 实现持久会话,无需每次 spawn 进程
2
+ * Claude SDK Adapter - 使用 Agent SDK V2 Session API 实现真正的多轮对话
3
3
  *
4
- * 优势:
5
- * 1. 进程内执行 - 无 fork/exec 开销,响应更快
6
- * 2. 会话复用 - resume 保留上下文,无需重新加载历史
7
- * 3. 流式输出 - includePartialMessages 支持 text_delta、thinking_delta
4
+ * V2 API 优势:
5
+ * 1. 进程内执行 - 无 fork/exec 开销
6
+ * 2. 持久会话 - SDKSession 对象保持会话状态,支持真正的多轮对话
7
+ * 3. 流式输出 - 支持实时增量更新
8
8
  *
9
- * 认证:ANTHROPIC_API_KEY 或 CLAUDE_CODE_OAUTH_TOKEN(claude setup-token)
9
+ * 认证:ANTHROPIC_API_KEY 或 CLAUDE_CODE_OAUTH_TOKEN
10
10
  */
11
- import { query } from '@anthropic-ai/claude-agent-sdk';
11
+ import { unstable_v2_createSession, unstable_v2_resumeSession } from '@anthropic-ai/claude-agent-sdk';
12
12
  import { createLogger } from '../logger.js';
13
13
  const log = createLogger('ClaudeSDK');
14
- // 存储所有活跃的查询,用于清理
15
- const activeQueries = new Set();
14
+ // 存储所有活跃的 SDKSession 对象,key 为 sessionId
15
+ // 使用 Map 而不是 Set,因为我们需要通过 sessionId 获取 session
16
+ const activeSessions = new Map();
17
+ // 存储正在进行的流式迭代器,用于中断
18
+ const activeStreams = new Set();
16
19
  function isStreamEvent(msg) {
17
20
  return msg.type === 'stream_event';
18
21
  }
@@ -20,33 +23,82 @@ function isSystemInit(msg) {
20
23
  const m = msg;
21
24
  return m.type === 'system' && m.subtype === 'init';
22
25
  }
26
+ function isAssistant(msg) {
27
+ return msg.type === 'assistant';
28
+ }
23
29
  function isResult(msg) {
24
30
  return msg.type === 'result';
25
31
  }
26
- function isAssistant(msg) {
27
- return msg.type === 'assistant';
32
+ /**
33
+ * 获取或创建 SDKSession
34
+ * @param sessionId 已有的 sessionId,如果为 undefined 则创建新会话
35
+ * @param workDir 工作目录
36
+ * @param model 模型名称
37
+ * @param permissionMode 权限模式
38
+ * @returns SDKSession 对象和实际的 sessionId
39
+ */
40
+ async function getOrCreateSession(sessionId, _workDir, // 保留参数以备将来使用
41
+ model, permissionMode) {
42
+ const sessionOptions = {
43
+ model: model || 'claude-opus-4-5',
44
+ permissionMode,
45
+ // 可以添加其他选项,如 hooks, allowedTools 等
46
+ };
47
+ let session;
48
+ if (sessionId) {
49
+ // 尝试恢复已有会话
50
+ try {
51
+ log.info(`Attempting to resume session: ${sessionId}`);
52
+ session = unstable_v2_resumeSession(sessionId, sessionOptions);
53
+ activeSessions.set(sessionId, session);
54
+ log.info(`Successfully resumed session: ${sessionId}`);
55
+ return { session, sessionId };
56
+ }
57
+ catch (err) {
58
+ log.warn(`Failed to resume session ${sessionId}, creating new one: ${err}`);
59
+ // 恢复失败,创建新会话
60
+ }
61
+ }
62
+ // 创建新会话
63
+ session = unstable_v2_createSession(sessionOptions);
64
+ // 新会话的 sessionId 需要从第一个消息中获取
65
+ // 暂时返回 undefined,稍后在 init 消息中获取
66
+ const tempId = `pending-${Date.now()}`;
67
+ activeSessions.set(tempId, session);
68
+ log.info(`Created new session (tempId: ${tempId})`);
69
+ return { session, sessionId: tempId };
28
70
  }
29
71
  export class ClaudeSDKAdapter {
30
72
  toolId = 'claude-sdk';
31
73
  /**
32
- * 清理所有活跃的 SDK 查询
74
+ * 清理所有活跃的 SDK 会话和流
33
75
  */
34
76
  static destroy() {
35
- for (const q of activeQueries) {
77
+ for (const stream of activeStreams) {
36
78
  try {
37
- if (q && typeof q.return === 'function') {
38
- q.return();
79
+ if (stream && typeof stream.return === 'function') {
80
+ stream.return();
39
81
  }
40
82
  }
41
83
  catch {
42
84
  /* ignore */
43
85
  }
44
86
  }
45
- activeQueries.clear();
87
+ activeStreams.clear();
88
+ for (const session of activeSessions.values()) {
89
+ try {
90
+ session.close();
91
+ }
92
+ catch {
93
+ /* ignore */
94
+ }
95
+ }
96
+ activeSessions.clear();
46
97
  }
47
98
  run(prompt, sessionId, workDir, callbacks, options) {
48
99
  const abortController = new AbortController();
49
- let queryClosed = false;
100
+ let streamClosed = false;
101
+ let actualSessionId;
50
102
  const permissionMode = options?.skipPermissions
51
103
  ? 'bypassPermissions'
52
104
  : options?.permissionMode === 'acceptEdits'
@@ -54,41 +106,47 @@ export class ClaudeSDKAdapter {
54
106
  : options?.permissionMode === 'plan'
55
107
  ? 'plan'
56
108
  : 'default';
57
- const runQuery = async () => {
109
+ const runSession = async () => {
58
110
  try {
59
- // 调试:检查关键环境变量
111
+ // 检查环境变量
60
112
  const hasApiKey = !!process.env.ANTHROPIC_API_KEY;
61
113
  const hasAuthToken = !!process.env.ANTHROPIC_AUTH_TOKEN;
62
- const hasBaseUrl = !!process.env.ANTHROPIC_BASE_URL;
63
- if (!hasApiKey && !hasAuthToken && !hasBaseUrl) {
114
+ if (!hasApiKey && !hasAuthToken) {
64
115
  log.warn('Claude SDK: No API credentials found in environment variables');
65
116
  }
66
- const opts = {
67
- cwd: workDir,
68
- resume: sessionId,
69
- includePartialMessages: true,
70
- permissionMode,
71
- model: options?.model,
72
- abortController,
73
- allowDangerouslySkipPermissions: permissionMode === 'bypassPermissions',
74
- };
75
- const q = query({
76
- prompt,
77
- options: opts,
78
- });
79
- // 将查询添加到活跃列表
80
- activeQueries.add(q);
117
+ log.info(`[V2] Session: ${sessionId ?? 'new'}, prompt="${prompt.slice(0, 50)}..."`);
118
+ // 获取或创建会话
119
+ const { session } = await getOrCreateSession(sessionId, workDir, options?.model, permissionMode);
120
+ // 发送用户消息
121
+ await session.send(prompt);
122
+ // 获取响应流
123
+ const stream = session.stream();
124
+ activeStreams.add(stream);
81
125
  let accumulated = '';
82
126
  let accumulatedThinking = '';
83
127
  const toolStats = {};
84
128
  try {
85
- for await (const msg of q) {
86
- if (abortController.signal.aborted)
129
+ for await (const msg of stream) {
130
+ if (abortController.signal.aborted) {
131
+ log.info('Stream aborted by user');
87
132
  break;
133
+ }
134
+ // 获取实际的 sessionId(从 init 消息中)
88
135
  if (isSystemInit(msg)) {
89
- callbacks.onSessionId?.(msg.session_id);
136
+ const newSessionId = msg.session_id;
137
+ if (newSessionId && newSessionId !== actualSessionId) {
138
+ // 更新 sessionId 映射
139
+ if (actualSessionId && actualSessionId.startsWith('pending-')) {
140
+ activeSessions.delete(actualSessionId);
141
+ }
142
+ activeSessions.set(newSessionId, session);
143
+ actualSessionId = newSessionId;
144
+ log.info(`[V2] Got actual sessionId: ${newSessionId}`);
145
+ callbacks.onSessionId?.(newSessionId);
146
+ }
90
147
  continue;
91
148
  }
149
+ // 处理流式事件
92
150
  if (isStreamEvent(msg)) {
93
151
  const ev = msg.event;
94
152
  if (ev?.type === 'content_block_delta' && ev.delta) {
@@ -103,6 +161,7 @@ export class ClaudeSDKAdapter {
103
161
  }
104
162
  continue;
105
163
  }
164
+ // 处理助手消息(工具调用)
106
165
  if (isAssistant(msg)) {
107
166
  const content = msg.message?.content;
108
167
  for (const block of content ?? []) {
@@ -113,16 +172,22 @@ export class ClaudeSDKAdapter {
113
172
  }
114
173
  continue;
115
174
  }
175
+ // 处理结果消息
116
176
  if (isResult(msg)) {
117
- queryClosed = true;
177
+ streamClosed = true;
118
178
  const m = msg;
119
179
  const success = m.subtype === 'success';
120
180
  const errs = m.errors ?? [];
121
- const noConvErr = errs.find((e) => e.includes('No conversation found with session ID'));
122
- if (!success && noConvErr) {
123
- log.warn(`SDK session invalid: ${noConvErr}`);
124
- callbacks.onSessionInvalid?.();
125
- callbacks.onError('会话已过期,请发送 /new 开始新会话');
181
+ log.info(`[V2] Result: subtype=${m.subtype}, num_turns=${m.num_turns}, sessionId=${actualSessionId ?? 'unknown'}`);
182
+ // 检查会话错误
183
+ if (!success) {
184
+ const noConvErr = errs.find((e) => e.includes('No conversation found') || e.includes('session not found'));
185
+ if (noConvErr) {
186
+ log.warn(`Session ${actualSessionId} not found, may need to create new one`);
187
+ callbacks.onSessionInvalid?.();
188
+ }
189
+ const errMsg = errs[0] || '未知错误';
190
+ callbacks.onError(errMsg);
126
191
  return;
127
192
  }
128
193
  const resultText = m.result ?? '';
@@ -135,58 +200,55 @@ export class ClaudeSDKAdapter {
135
200
  numTurns: m.num_turns ?? 0,
136
201
  toolStats,
137
202
  };
138
- if (!result.accumulated && result.result)
203
+ if (!result.accumulated && result.result) {
139
204
  result.accumulated = result.result;
205
+ }
140
206
  if (!result.accumulated && !result.result && accumulated) {
141
- log.debug(`Result event had no text but accumulated=${accumulated.length} chars, using accumulated`);
142
207
  result.accumulated = accumulated;
143
208
  result.result = accumulated;
144
209
  }
145
- if (!result.accumulated && !result.result) {
146
- const errMsg = errs[0] ?? '未知错误';
147
- log.warn(`SDK result empty: subtype=${m.subtype}, errors=${JSON.stringify(errs)}`);
148
- callbacks.onError(errMsg);
149
- return;
150
- }
151
210
  callbacks.onComplete(result);
152
211
  return;
153
212
  }
154
213
  }
155
- if (!queryClosed) {
214
+ // 如果流正常结束但没有收到 result 消息
215
+ if (!streamClosed && accumulated) {
216
+ log.info('Stream ended without result message, using accumulated text');
156
217
  callbacks.onComplete({
157
218
  success: true,
158
219
  result: accumulated,
159
220
  accumulated,
160
221
  cost: 0,
161
222
  durationMs: 0,
162
- numTurns: 0,
223
+ numTurns: 1,
163
224
  toolStats,
164
225
  });
165
226
  }
166
227
  }
167
228
  finally {
168
- q.close();
169
- // 从活跃列表中移除
170
- activeQueries.delete(q);
229
+ // 从活跃列表中移除流
230
+ activeStreams.delete(stream);
171
231
  }
172
232
  }
173
233
  catch (err) {
174
- if (abortController.signal.aborted)
234
+ if (abortController.signal.aborted) {
235
+ log.info('Session run aborted');
175
236
  return;
237
+ }
176
238
  const errorObj = err;
177
239
  const msg = errorObj.message || String(err);
178
- const stack = errorObj.stack || '';
179
- // 输出详细的错误信息用于调试
180
- log.error(`Claude SDK error: ${msg}`);
181
- if (stack) {
182
- log.error(`Error stack: ${stack}`);
240
+ log.error(`Claude SDK V2 error: ${msg}`);
241
+ if (errorObj.stack) {
242
+ log.error(`Error stack: ${errorObj.stack}`);
183
243
  }
184
244
  callbacks.onError(msg);
185
245
  }
186
246
  };
187
- runQuery();
247
+ // 启动会话(不等待)
248
+ runSession();
188
249
  return {
189
250
  abort: () => {
251
+ log.info('Aborting session run');
190
252
  abortController.abort();
191
253
  },
192
254
  };
@@ -1,14 +1,11 @@
1
1
  import { type Config } from '../config.js';
2
2
  import type { SessionManager } from '../session/session-manager.js';
3
3
  import type { RequestQueue } from '../queue/request-queue.js';
4
- import { type PermissionMode } from '../permission-mode/types.js';
5
4
  import type { ThreadContext } from '../shared/types.js';
6
5
  export type { ThreadContext };
7
6
  export interface MessageSender {
8
7
  sendTextReply(chatId: string, text: string, threadCtx?: ThreadContext): Promise<void>;
9
8
  sendDirectorySelection?(chatId: string, currentDir: string, userId: string): Promise<void>;
10
- sendModeCard?(chatId: string, userId: string, currentMode: PermissionMode): Promise<void>;
11
- sendModeKeyboard?(chatId: string, userId: string, currentMode: PermissionMode): Promise<void>;
12
9
  }
13
10
  export interface CommandHandlerDeps {
14
11
  config: Config;
@@ -22,15 +19,12 @@ export declare class CommandHandler {
22
19
  private deps;
23
20
  constructor(deps: CommandHandlerDeps);
24
21
  dispatch(text: string, chatId: string, userId: string, platform: 'dingtalk' | 'feishu' | 'qq' | 'telegram' | 'wechat' | 'wework', handleClaudeRequest: ClaudeRequestHandler): Promise<boolean>;
25
- private handleMode;
26
22
  private getClearHistoryHint;
27
23
  private handleHelp;
28
24
  private handleNew;
29
25
  private handlePwd;
30
26
  private handleStatus;
31
27
  private handleCd;
32
- private handleAllow;
33
- private handleDeny;
34
28
  private getAiVersion;
35
29
  }
36
30
  /**
@@ -1,8 +1,5 @@
1
1
  import { resolvePlatformAiCommand } from '../config.js';
2
- import { resolveLatestPermission, getPendingCount } from '../hook/permission-server.js';
3
2
  import { escapePathForMarkdown } from '../shared/utils.js';
4
- import { getPermissionMode, setPermissionMode } from '../permission-mode/session-mode.js';
5
- import { MODE_LABELS, MODE_DESCRIPTIONS, parsePermissionMode } from '../permission-mode/types.js';
6
3
  import { TERMINAL_ONLY_COMMANDS } from '../constants.js';
7
4
  import { execFile } from 'node:child_process';
8
5
  import { readdirSync } from 'node:fs';
@@ -20,18 +17,12 @@ export class CommandHandler {
20
17
  }
21
18
  if (t === '/help')
22
19
  return this.handleHelp(chatId, platform);
23
- if (t === '/mode' || t.startsWith('/mode '))
24
- return this.handleMode(chatId, userId, platform, t.slice(6).trim());
25
20
  if (t === '/new')
26
21
  return this.handleNew(chatId, userId, platform);
27
22
  if (t === '/pwd')
28
23
  return this.handlePwd(chatId, userId);
29
24
  if (t === '/status')
30
25
  return this.handleStatus(chatId, userId, platform);
31
- if (t === '/allow' || t === '/y')
32
- return this.handleAllow(chatId);
33
- if (t === '/deny' || t === '/n')
34
- return this.handleDeny(chatId);
35
26
  if (t === '/cd' || t.startsWith('/cd ')) {
36
27
  return this.handleCd(chatId, userId, t.slice(3).trim(), platform);
37
28
  }
@@ -42,35 +33,6 @@ export class CommandHandler {
42
33
  }
43
34
  return false;
44
35
  }
45
- async handleMode(chatId, userId, platform, arg) {
46
- const defaultMode = this.deps.config.defaultPermissionMode;
47
- const currentMode = getPermissionMode(userId, defaultMode);
48
- if (arg) {
49
- const parsed = parsePermissionMode(arg);
50
- if (parsed) {
51
- setPermissionMode(userId, parsed);
52
- await this.deps.sender.sendTextReply(chatId, `✅ 权限模式已切换为 **${MODE_LABELS[parsed]}**\n${MODE_DESCRIPTIONS[parsed]}`);
53
- return true;
54
- }
55
- await this.deps.sender.sendTextReply(chatId, `无效模式: ${arg}\n可用: ask, accept-edits, plan, yolo`);
56
- return true;
57
- }
58
- if (platform === 'feishu' && this.deps.sender.sendModeCard) {
59
- await this.deps.sender.sendModeCard(chatId, userId, currentMode);
60
- return true;
61
- }
62
- if (platform === 'telegram' && this.deps.sender.sendModeKeyboard) {
63
- await this.deps.sender.sendModeKeyboard(chatId, userId, currentMode);
64
- return true;
65
- }
66
- const lines = [
67
- `🔐 **权限模式** (当前: ${MODE_LABELS[currentMode]})`,
68
- '',
69
- ...['ask', 'accept-edits', 'plan', 'yolo'].map((m) => `• \`/mode ${m}\` - ${MODE_LABELS[m]}: ${MODE_DESCRIPTIONS[m]}`),
70
- ];
71
- await this.deps.sender.sendTextReply(chatId, lines.join('\n'));
72
- return true;
73
- }
74
36
  getClearHistoryHint(platform) {
75
37
  return platform === 'feishu'
76
38
  ? '💡 提示:如需清除本对话的历史消息,请点击飞书聊天右上角「...」→ 清除聊天记录'
@@ -85,13 +47,10 @@ export class CommandHandler {
85
47
  '📋 可用命令:',
86
48
  '',
87
49
  '/help - 显示帮助',
88
- '/mode - 切换权限模式(安全/编辑放行/只读/YOLO)',
89
50
  '/new - 开始新会话(AI 上下文重置)',
90
51
  '/status - 显示状态',
91
52
  '/cd <路径> - 切换工作目录',
92
53
  '/pwd - 当前工作目录',
93
- '/allow (/y) - 允许权限请求',
94
- '/deny (/n) - 拒绝权限请求',
95
54
  '',
96
55
  this.getClearHistoryHint(platform),
97
56
  ].join('\n');
@@ -150,27 +109,6 @@ export class CommandHandler {
150
109
  }
151
110
  return true;
152
111
  }
153
- async handleAllow(chatId) {
154
- const reqId = resolveLatestPermission(chatId, 'allow');
155
- if (reqId) {
156
- const remaining = getPendingCount(chatId);
157
- await this.deps.sender.sendTextReply(chatId, `✅ 权限已允许${remaining > 0 ? `(还有 ${remaining} 个待确认)` : ''}`);
158
- }
159
- else {
160
- await this.deps.sender.sendTextReply(chatId, 'ℹ️ 没有待确认的权限请求');
161
- }
162
- return true;
163
- }
164
- async handleDeny(chatId) {
165
- const reqId = resolveLatestPermission(chatId, 'deny');
166
- if (reqId) {
167
- await this.deps.sender.sendTextReply(chatId, '❌ 权限已拒绝');
168
- }
169
- else {
170
- await this.deps.sender.sendTextReply(chatId, 'ℹ️ 没有待确认的权限请求');
171
- }
172
- return true;
173
- }
174
112
  getAiVersion(aiCommand) {
175
113
  if (aiCommand === 'claude') {
176
114
  // Claude 使用 SDK,返回 SDK 版本
@@ -134,7 +134,6 @@ function buildInitialPayload(file) {
134
134
  ai: {
135
135
  aiCommand: file.aiCommand ?? "claude",
136
136
  claudeWorkDir: file.tools?.claude?.workDir ?? process.cwd(),
137
- claudeSkipPermissions: file.tools?.claude?.skipPermissions ?? true,
138
137
  claudeTimeoutMs: file.tools?.claude?.timeoutMs ?? 600000,
139
138
  claudeConfigPath: process.platform === 'win32'
140
139
  ? getClaudeConfigHome() + "\\.claude\\settings.json"
@@ -148,8 +147,6 @@ function buildInitialPayload(file) {
148
147
  codexCliPath: file.tools?.codex?.cliPath ?? "codex",
149
148
  codebuddyCliPath: file.tools?.codebuddy?.cliPath ?? "codebuddy",
150
149
  codexProxy: file.tools?.codex?.proxy ?? "",
151
- defaultPermissionMode: file.defaultPermissionMode ?? "ask",
152
- hookPort: file.hookPort ?? 35801,
153
150
  logDir: file.logDir ?? "",
154
151
  logLevel: file.logLevel ?? "default",
155
152
  },
@@ -186,8 +183,6 @@ function validatePayload(payload) {
186
183
  errors.push("Codex timeout must be positive.");
187
184
  if (!Number.isFinite(payload.ai.codebuddyTimeoutMs) || payload.ai.codebuddyTimeoutMs <= 0)
188
185
  errors.push("CodeBuddy timeout must be positive.");
189
- if (!Number.isFinite(payload.ai.hookPort) || payload.ai.hookPort <= 0)
190
- errors.push("Hook port must be positive.");
191
186
  return errors;
192
187
  }
193
188
  function validateConfigForPlatform(platform, config) {
@@ -266,12 +261,9 @@ function createProbeConfig(values) {
266
261
  aiCommand: "claude",
267
262
  codexCliPath: "codex",
268
263
  claudeWorkDir: process.cwd(),
269
- claudeSkipPermissions: true,
270
- defaultPermissionMode: "ask",
271
264
  claudeTimeoutMs: 600000,
272
265
  codexTimeoutMs: 600000,
273
266
  codebuddyTimeoutMs: 600000,
274
- hookPort: 35801,
275
267
  logDir: "",
276
268
  logLevel: "INFO",
277
269
  codebuddyCliPath: "codebuddy",
@@ -397,15 +389,12 @@ function toFileConfig(payload, existing) {
397
389
  return {
398
390
  ...existing,
399
391
  aiCommand: payload.ai.aiCommand,
400
- defaultPermissionMode: payload.ai.defaultPermissionMode ?? existing.defaultPermissionMode ?? "ask",
401
- hookPort: payload.ai.hookPort,
402
392
  logDir: payload.ai.logDir === undefined ? existing.logDir : clean(payload.ai.logDir),
403
393
  logLevel: payload.ai.logLevel === "default" ? undefined : payload.ai.logLevel,
404
394
  tools: {
405
395
  claude: {
406
396
  ...existing.tools?.claude,
407
397
  workDir: clean(payload.ai.claudeWorkDir) ?? process.cwd(),
408
- skipPermissions: payload.ai.claudeSkipPermissions,
409
398
  timeoutMs: payload.ai.claudeTimeoutMs,
410
399
  proxy: clean(payload.ai.claudeProxy),
411
400
  // model is now saved to ~/.claude/settings.json as ANTHROPIC_MODEL
@@ -414,14 +403,12 @@ function toFileConfig(payload, existing) {
414
403
  ...existing.tools?.codex,
415
404
  cliPath: clean(payload.ai.codexCliPath) ?? "codex",
416
405
  workDir: clean(payload.ai.claudeWorkDir) ?? process.cwd(),
417
- skipPermissions: existing.tools?.codex?.skipPermissions ?? payload.ai.claudeSkipPermissions,
418
406
  timeoutMs: payload.ai.codexTimeoutMs,
419
407
  proxy: clean(payload.ai.codexProxy),
420
408
  },
421
409
  codebuddy: {
422
410
  ...existing.tools?.codebuddy,
423
411
  cliPath: clean(payload.ai.codebuddyCliPath) ?? "codebuddy",
424
- skipPermissions: existing.tools?.codebuddy?.skipPermissions ?? payload.ai.claudeSkipPermissions,
425
412
  timeoutMs: payload.ai.codebuddyTimeoutMs,
426
413
  },
427
414
  },
@@ -623,8 +610,9 @@ export async function startWebConfigServer(options) {
623
610
  }
624
611
  if (request.method === "POST" && requestUrl.pathname === "/api/service/start") {
625
612
  try {
626
- loadConfig();
627
- const started = startBackgroundService(options.cwd);
613
+ const config = loadConfig();
614
+ const workDir = config.claudeWorkDir ?? options.cwd;
615
+ const started = startBackgroundService(workDir);
628
616
  json(response, 200, { message: `Bridge started with pid ${started.pid}.`, pid: started.pid });
629
617
  if (!options.persistent) {
630
618
  setTimeout(() => finishFlow("saved"), 120);
package/dist/config.d.ts CHANGED
@@ -38,10 +38,7 @@ export interface Config {
38
38
  codexTimeoutMs: number;
39
39
  codebuddyTimeoutMs: number;
40
40
  claudeWorkDir: string;
41
- claudeSkipPermissions: boolean;
42
- defaultPermissionMode: 'ask' | 'accept-edits' | 'plan' | 'yolo';
43
41
  claudeModel?: string;
44
- hookPort: number;
45
42
  logDir: string;
46
43
  logLevel: LogLevel;
47
44
  platforms: {
@@ -137,7 +134,6 @@ export interface FilePlatformDingtalk {
137
134
  }
138
135
  export interface FileToolClaude {
139
136
  workDir?: string;
140
- skipPermissions?: boolean;
141
137
  timeoutMs?: number;
142
138
  model?: string;
143
139
  proxy?: string;
@@ -146,16 +142,12 @@ export interface FileToolCodex {
146
142
  cliPath?: string;
147
143
  workDir?: string;
148
144
  timeoutMs?: number;
149
- /** 是否跳过权限确认(默认 true) */
150
- skipPermissions?: boolean;
151
145
  /** HTTP/HTTPS 代理,用于访问 chatgpt.com(如 http://127.0.0.1:7890) */
152
146
  proxy?: string;
153
147
  }
154
148
  export interface FileToolCodeBuddy {
155
149
  cliPath?: string;
156
150
  timeoutMs?: number;
157
- /** 是否跳过权限确认(默认 true) */
158
- skipPermissions?: boolean;
159
151
  }
160
152
  export interface FileConfig {
161
153
  telegramBotToken?: string;
@@ -177,8 +169,6 @@ export interface FileConfig {
177
169
  codex?: FileToolCodex;
178
170
  codebuddy?: FileToolCodeBuddy;
179
171
  };
180
- defaultPermissionMode?: 'ask' | 'accept-edits' | 'plan' | 'yolo';
181
- hookPort?: number;
182
172
  logDir?: string;
183
173
  logLevel?: LogLevel;
184
174
  }