cursor-feedback 2.2.0 → 2.3.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [2.3.0](https://github.com/jianger666/cursor-feedback-extension/compare/v2.2.0...v2.3.0) (2026-07-04)
6
+
7
+
8
+ ### Features
9
+
10
+ * 面板消息排队与撤回、扫码建应用、摘要历史与多项 UI 体验优化 ([ffe731c](https://github.com/jianger666/cursor-feedback-extension/commit/ffe731c767975f2045fc4b8701500b6a60f48373))
11
+
5
12
  ## [2.2.0](https://github.com/jianger666/cursor-feedback-extension/compare/v2.1.2...v2.2.0) (2026-07-03)
6
13
 
7
14
 
package/dist/extension.js CHANGED
@@ -179,6 +179,9 @@ class FeedbackViewProvider {
179
179
  this._feishuAck = true;
180
180
  // 忙时消息排队子开关(飞书通知子项;AI 正忙时用户消息入队,下一轮 feedback 自动送达)
181
181
  this._feishuQueue = true;
182
+ // 最近一次下发给 webview 的队列快照(签名去重避免每秒刷屏;webview 重建后凭此恢复列表)
183
+ this._lastQueueSig = '';
184
+ this._lastQueueItems = [];
182
185
  this._debugInfo = {
183
186
  portRange: '',
184
187
  workspacePath: '',
@@ -186,6 +189,9 @@ class FeedbackViewProvider {
186
189
  activePort: null,
187
190
  lastStatus: ''
188
191
  };
192
+ // ---------- 扫码一键创建飞书应用 ----------
193
+ this._registerPollTimer = null;
194
+ this._registerPort = null;
189
195
  this._basePort = port;
190
196
  this._debugInfo.portRange = `${port}-${port + this._portScanRange - 1}`;
191
197
  this._i18n = (0, i18n_1.loadMessages)(this._extensionUri.fsPath);
@@ -228,11 +234,22 @@ class FeedbackViewProvider {
228
234
  case 'submitFeedback':
229
235
  await this._handleFeedbackSubmit(data.payload);
230
236
  break;
237
+ case 'queueMessage':
238
+ await this._handleQueueMessage(data.payload);
239
+ break;
240
+ case 'removeQueued':
241
+ await this._handleRemoveQueued(data.payload);
242
+ break;
231
243
  case 'ready':
232
244
  this._webviewReady = true;
233
245
  // 同步超时续期开关状态到 UI
234
246
  this._postAutoRetryState();
235
247
  this._postFeishuState();
248
+ // 重建后的 webview 恢复队列列表显示(poll 之后每秒还会校准)
249
+ this._view?.webview.postMessage({
250
+ type: 'queueState',
251
+ payload: { items: this._lastQueueItems }
252
+ });
236
253
  // WebView 准备就绪后,检查是否有待处理的请求
237
254
  // (插件通知关闭时保持静默,不显示缓存的请求)
238
255
  if (this._currentRequest && this._isPluginNotifyEnabled()) {
@@ -265,9 +282,6 @@ class FeedbackViewProvider {
265
282
  case 'saveFeishuConfig':
266
283
  await this._handleSaveFeishuConfig(data.payload);
267
284
  break;
268
- case 'openFeishuGuide':
269
- await this._handleOpenFeishuGuide();
270
- break;
271
285
  case 'toggleFeishuEnabled':
272
286
  await this._handleToggleFeishuEnabled(!!data.payload?.enabled);
273
287
  break;
@@ -283,6 +297,15 @@ class FeedbackViewProvider {
283
297
  case 'toggleFeishuQueue':
284
298
  await this._handleToggleFeishuQueue(!!data.payload?.enabled);
285
299
  break;
300
+ case 'feishuRegisterStart':
301
+ await this._handleFeishuRegisterStart();
302
+ break;
303
+ case 'feishuRegisterCancel':
304
+ this._handleFeishuRegisterCancel();
305
+ break;
306
+ case 'openLink':
307
+ this._handleOpenLink(data.payload?.url);
308
+ break;
286
309
  case 'testNotification':
287
310
  this._sendTestNotification();
288
311
  break;
@@ -358,6 +381,8 @@ class FeedbackViewProvider {
358
381
  // 端口仍然有效,保持使用
359
382
  this._debugInfo.connectedPorts = [this._activePort];
360
383
  this._debugInfo.activePort = this._activePort;
384
+ // 同步忙时队列快照到面板(签名去重,无变化不发);打上端口标,撤回时按它路由
385
+ this._postQueueState((result.queued || []).map(q => ({ ...q, port: this._activePort })));
361
386
  // 只挡「已提交/已结束」的请求;_handleNewRequest 内部按当前显示态去重,重复调用无害
362
387
  if (result.request && !this._resolvedRequestIds.has(result.request.id)) {
363
388
  this._debugInfo.lastStatus = this._t('statusListening', { port: this._activePort });
@@ -390,6 +415,12 @@ class FeedbackViewProvider {
390
415
  const results = await Promise.all(ports.map(port => this._checkPortForRequest(port)));
391
416
  // 更新已连接的端口列表
392
417
  this._debugInfo.connectedPorts = results.filter(r => r.connected).map(r => r.port);
418
+ // 汇总各 server 返回的本工作区忙时队列(正常只有归属本窗口的 server 有内容),按时间排序下发;
419
+ // 打上各自 server 的端口标,撤回时按它路由
420
+ const queuedAll = results
421
+ .flatMap(r => (r.connected && Array.isArray(r.queued) ? r.queued.map(q => ({ ...q, port: r.port })) : []))
422
+ .sort((a, b) => a.at - b.at);
423
+ this._postQueueState(queuedAll);
393
424
  // 找出属于当前工作区的请求(只排除已提交/已结束的;「见过但没提交」的仍要能回到面板)。
394
425
  // 不再用 server 进程级 ownerWorkspace 二次过滤:request 已在 _checkPortForRequest
395
426
  // 按本窗口工作区过滤过(isPathInWorkspace);多窗口共用一个 MCP 进程时 owner 只反映
@@ -457,11 +488,14 @@ class FeedbackViewProvider {
457
488
  let request;
458
489
  let ownerWorkspace = null;
459
490
  let feishuResolvedId = null;
491
+ let queued = [];
460
492
  if (parsed && typeof parsed === 'object' && 'startTime' in parsed) {
461
493
  // 新格式
462
494
  request = parsed.request;
463
495
  ownerWorkspace = parsed.ownerWorkspace;
464
496
  feishuResolvedId = parsed.feishuResolvedId ?? null;
497
+ if (Array.isArray(parsed.queued))
498
+ queued = parsed.queued;
465
499
  this._maybeSyncFeishu(port, parsed.feishu);
466
500
  this._maybeSyncAutoRetry(parsed.autoRetry);
467
501
  this._maybeSyncPause(parsed.pause);
@@ -475,10 +509,10 @@ class FeedbackViewProvider {
475
509
  const isMatch = isPathInWorkspace(request.projectDir);
476
510
  if (!isMatch) {
477
511
  // 请求不属于当前工作区,返回特殊标记
478
- return { connected: true, request: null, port, mismatch: true, ownerWorkspace };
512
+ return { connected: true, request: null, port, mismatch: true, ownerWorkspace, queued };
479
513
  }
480
514
  }
481
- return { connected: true, request, port, ownerWorkspace, feishuResolvedId };
515
+ return { connected: true, request, port, ownerWorkspace, feishuResolvedId, queued };
482
516
  }
483
517
  catch {
484
518
  return { connected: false, request: null, port };
@@ -766,6 +800,103 @@ class FeedbackViewProvider {
766
800
  vscode.window.showErrorMessage(this._i18n.submitFailed + ': ' + this._i18n.cannotConnectMCP);
767
801
  }
768
802
  }
803
+ /**
804
+ * 把忙时队列快照推给 webview(签名去重:队列无变化时每秒轮询不重复发)
805
+ */
806
+ _postQueueState(items) {
807
+ const sig = JSON.stringify(items);
808
+ if (sig === this._lastQueueSig)
809
+ return;
810
+ this._lastQueueSig = sig;
811
+ this._lastQueueItems = items;
812
+ this._view?.webview.postMessage({ type: 'queueState', payload: { items } });
813
+ }
814
+ /**
815
+ * 面板忙时排队:AI 正忙(没有等待中的反馈请求)时用户在面板点了「排队发送」。
816
+ * 先定位归属本工作区的 server(AI 的下一轮 interactive_feedback 会走同一个 MCP 进程,
817
+ * 队列必须排在那里才能被消费),再 POST 入队。找不到归属 server = AI 没在本项目工作,
818
+ * 明确告知失败,绝不静默吞消息。
819
+ */
820
+ async _handleQueueMessage(payload) {
821
+ const workspacePaths = getWorkspacePaths();
822
+ const workspacePath = workspacePaths.length > 0 ? workspacePaths[0] : '';
823
+ const fail = (reason) => {
824
+ this._view?.webview.postMessage({ type: 'queueSubmitted', payload: { success: false, reason } });
825
+ };
826
+ if (!workspacePath) {
827
+ fail('no-server');
828
+ return;
829
+ }
830
+ const port = await this._findOwnerPort(workspacePath);
831
+ if (!port) {
832
+ fail('no-server');
833
+ return;
834
+ }
835
+ try {
836
+ const response = await this._httpPost(`http://127.0.0.1:${port}/api/feedback/enqueue`, JSON.stringify({
837
+ text: payload.interactive_feedback,
838
+ images: payload.images || [],
839
+ attachedFiles: payload.attachedFiles || [],
840
+ projectDir: workspacePath
841
+ }));
842
+ const result = JSON.parse(response);
843
+ if (result.queued) {
844
+ this._view?.webview.postMessage({ type: 'queueSubmitted', payload: { success: true } });
845
+ }
846
+ else {
847
+ fail(result.reason === 'pending' || result.reason === 'disabled' ? result.reason : 'no-server');
848
+ }
849
+ }
850
+ catch {
851
+ fail('no-server');
852
+ }
853
+ }
854
+ /**
855
+ * 撤回一条排队消息:按队列项携带的端口路由到所在 server。
856
+ * webview 已做乐观移除;失败也无需提示——下一秒轮询会把仍在队列里的消息补回列表。
857
+ */
858
+ async _handleRemoveQueued(payload) {
859
+ if (!payload || !payload.id || typeof payload.port !== 'number')
860
+ return;
861
+ try {
862
+ await this._httpPost(`http://127.0.0.1:${payload.port}/api/feedback/queue/remove`, JSON.stringify({ id: payload.id }));
863
+ }
864
+ catch {
865
+ // 静默:轮询会校准列表
866
+ }
867
+ }
868
+ /**
869
+ * 定位归属指定工作区的 MCP server 端口(ownerWorkspace 与工作区路径互为前缀)。
870
+ * 优先试当前已知端口,避免全端口扫描;都不中再并行扫描全范围。
871
+ */
872
+ async _findOwnerPort(workspacePath) {
873
+ const normalizedWs = normalizePath(workspacePath);
874
+ const isOwner = async (port) => {
875
+ try {
876
+ const response = await this._httpGet(`http://127.0.0.1:${port}/api/feedback/current?workspace=${encodeURIComponent(workspacePath)}`);
877
+ const parsed = JSON.parse(response);
878
+ const owner = parsed && parsed.ownerWorkspace ? normalizePath(parsed.ownerWorkspace) : '';
879
+ return !!owner && pathsRelated(owner, normalizedWs);
880
+ }
881
+ catch {
882
+ return false;
883
+ }
884
+ };
885
+ const preferred = [this._currentRequestPort, this._activePort]
886
+ .filter((p) => typeof p === 'number');
887
+ for (const p of preferred) {
888
+ if (await isOwner(p))
889
+ return p;
890
+ }
891
+ const ports = [];
892
+ for (let i = 0; i < this._portScanRange; i++) {
893
+ const p = this._basePort + i;
894
+ if (!preferred.includes(p))
895
+ ports.push(p);
896
+ }
897
+ const hits = await Promise.all(ports.map(async (p) => ((await isOwner(p)) ? p : null)));
898
+ return hits.find((p) => p !== null) ?? null;
899
+ }
769
900
  /**
770
901
  * 处理选择文件/文件夹
771
902
  */
@@ -1059,24 +1190,103 @@ class FeedbackViewProvider {
1059
1190
  this._httpPost(`http://127.0.0.1:${port}/api/feishu/config`, body).catch(() => { });
1060
1191
  }
1061
1192
  }
1193
+ _postRegisterState(payload) {
1194
+ this._view?.webview.postMessage({ type: 'feishuRegisterState', payload });
1195
+ }
1062
1196
  /**
1063
- * 打开「如何配置飞书机器人」指引
1197
+ * 发起扫码创建:找一个活跃的 MCP server(凭证写磁盘全局共享,任一进程均可),
1198
+ * 拿到验证链接后本地生成二维码 dataURL 推给面板,随后轮询直到成功/失败。
1064
1199
  */
1065
- async _handleOpenFeishuGuide() {
1066
- const onlineUrl = 'https://github.com/jianger666/cursor-feedback-extension/blob/main/docs/feishu-setup.md';
1067
- const guideUri = vscode.Uri.joinPath(this._extensionUri, 'docs', 'feishu-setup.md');
1200
+ async _handleFeishuRegisterStart() {
1201
+ this._stopRegisterPolling();
1202
+ const port = await this._findAnyServerPort();
1203
+ if (!port) {
1204
+ this._postRegisterState({ status: 'error', error: this._i18n.registerNoServer });
1205
+ return;
1206
+ }
1207
+ this._registerPort = port;
1068
1208
  try {
1069
- // stat 确认打进包的本地文档在、再站内预览。
1070
- // markdown.showPreview 对缺失文件不报错、只渲染「找不到 feishu-setup.md」、catch 也不触发——
1071
- // 所以必须自己先校验存在、否则一旦打包又漏了 docs 就退回原 bug。
1072
- await vscode.workspace.fs.stat(guideUri);
1073
- await vscode.commands.executeCommand('markdown.showPreview', guideUri);
1209
+ // register/start 要等二维码就绪(服务端最多 10s)才响应,超时给足余量。
1210
+ // 二维码 dataURL server 生成(插件 VSIX 不带 node_modules,装不进二维码库)
1211
+ const raw = await this._httpPost(`http://127.0.0.1:${port}/api/feishu/register/start`, '{}', 15000);
1212
+ const state = JSON.parse(raw);
1213
+ if (state.status === 'waiting' && state.url) {
1214
+ this._postRegisterState({ status: 'waiting', url: state.url, expireIn: state.expireIn, qr: state.qr });
1215
+ this._startRegisterPolling(port);
1216
+ }
1217
+ else {
1218
+ this._postRegisterState({ status: 'error', error: state.error || 'failed' });
1219
+ }
1074
1220
  }
1075
1221
  catch {
1076
- // 本地没有(打包漏了 / 环境异常)→ 兜底开 GitHub 在线文档、绝不让用户再撞「找不到」
1077
- await vscode.env.openExternal(vscode.Uri.parse(onlineUrl));
1222
+ this._postRegisterState({ status: 'error', error: this._i18n.registerNoServer });
1078
1223
  }
1079
1224
  }
1225
+ _handleFeishuRegisterCancel() {
1226
+ this._stopRegisterPolling();
1227
+ if (this._registerPort) {
1228
+ this._httpPost(`http://127.0.0.1:${this._registerPort}/api/feishu/register/cancel`, '{}').catch(() => { });
1229
+ this._registerPort = null;
1230
+ }
1231
+ }
1232
+ _startRegisterPolling(port) {
1233
+ this._stopRegisterPolling();
1234
+ this._registerPollTimer = setInterval(async () => {
1235
+ try {
1236
+ const raw = await this._httpGet(`http://127.0.0.1:${port}/api/feishu/register/status`);
1237
+ const st = JSON.parse(raw);
1238
+ if (st.status === 'success') {
1239
+ this._stopRegisterPolling();
1240
+ this._postRegisterState({ status: 'success', appId: st.appId });
1241
+ }
1242
+ else if (st.status === 'error' || st.status === 'idle') {
1243
+ this._stopRegisterPolling();
1244
+ this._postRegisterState({ status: 'error', error: st.error || 'failed' });
1245
+ }
1246
+ }
1247
+ catch {
1248
+ // server 暂时不可达:下一轮再试
1249
+ }
1250
+ }, 2000);
1251
+ }
1252
+ _stopRegisterPolling() {
1253
+ if (this._registerPollTimer) {
1254
+ clearInterval(this._registerPollTimer);
1255
+ this._registerPollTimer = null;
1256
+ }
1257
+ }
1258
+ /** 找任意一个可达的 MCP server 端口(优先当前活跃端口,避免全端口扫描) */
1259
+ async _findAnyServerPort() {
1260
+ const alive = async (port) => {
1261
+ try {
1262
+ const raw = await this._httpGet(`http://127.0.0.1:${port}/api/health`);
1263
+ return JSON.parse(raw).status === 'ok';
1264
+ }
1265
+ catch {
1266
+ return false;
1267
+ }
1268
+ };
1269
+ const preferred = [this._activePort, this._currentRequestPort, ...(this._debugInfo.connectedPorts || [])]
1270
+ .filter((p) => typeof p === 'number');
1271
+ for (const p of preferred) {
1272
+ if (await alive(p))
1273
+ return p;
1274
+ }
1275
+ const ports = [];
1276
+ for (let i = 0; i < this._portScanRange; i++) {
1277
+ const p = this._basePort + i;
1278
+ if (!preferred.includes(p))
1279
+ ports.push(p);
1280
+ }
1281
+ const hits = await Promise.all(ports.map(async (p) => ((await alive(p)) ? p : null)));
1282
+ return hits.find((p) => p !== null) ?? null;
1283
+ }
1284
+ /** 打开外部链接(webview 内点「打开链接」时用;仅放行 http/https) */
1285
+ _handleOpenLink(url) {
1286
+ if (!url || !/^https?:\/\//i.test(url))
1287
+ return;
1288
+ vscode.env.openExternal(vscode.Uri.parse(url));
1289
+ }
1080
1290
  /**
1081
1291
  * 切换飞书通知开关(关了即使已配置也不推飞书;长连接仍保留以便绑定/回复)
1082
1292
  */
@@ -1142,7 +1352,7 @@ class FeedbackViewProvider {
1142
1352
  /**
1143
1353
  * HTTP POST 请求
1144
1354
  */
1145
- _httpPost(url, body) {
1355
+ _httpPost(url, body, timeoutMs = 5000) {
1146
1356
  return new Promise((resolve, reject) => {
1147
1357
  const urlObj = new URL(url);
1148
1358
  const options = {
@@ -1154,7 +1364,7 @@ class FeedbackViewProvider {
1154
1364
  'Content-Type': 'application/json',
1155
1365
  'Content-Length': Buffer.byteLength(body)
1156
1366
  },
1157
- timeout: 5000
1367
+ timeout: timeoutMs
1158
1368
  };
1159
1369
  const req = http.request(options, (res) => {
1160
1370
  let data = '';
package/dist/feishu.js CHANGED
@@ -82,6 +82,9 @@ class FeishuBridge {
82
82
  this.onReply = null;
83
83
  /** 绑定的 chat_id 变化时的回调(由 mcp-server 注入,用于通知 extension 持久化) */
84
84
  this.onBindChange = null;
85
+ // ---------- 扫码一键创建应用(registerApp / Device Grant) ----------
86
+ this.registerState = { status: 'idle' };
87
+ this.registerAbort = null;
85
88
  /** 按 chatId 暂存待合并的入站消息 */
86
89
  this.inboundBuffer = new Map();
87
90
  }
@@ -109,6 +112,116 @@ class FeishuBridge {
109
112
  isConfigured() {
110
113
  return !!this.config;
111
114
  }
115
+ getRegisterState() {
116
+ return this.registerState;
117
+ }
118
+ /**
119
+ * 启动「扫码一键创建应用」:返回待扫码的验证链接(等 onQRCodeReady 就绪后才返回)。
120
+ * 用户在飞书里确认后 SDK resolve 出凭证 → 与手动在面板保存等价(写磁盘 touched + configure 立即生效)。
121
+ * 已有等待中的流程直接复用当前二维码,不重复发起。
122
+ */
123
+ async startRegister() {
124
+ if (this.registerState.status === 'waiting' && this.registerAbort) {
125
+ return this.registerState;
126
+ }
127
+ if (!this.lark) {
128
+ try {
129
+ this.lark = await Promise.resolve().then(() => __importStar(require('@larksuiteoapi/node-sdk')));
130
+ }
131
+ catch (e) {
132
+ flog('飞书 SDK 加载失败: ' + e);
133
+ this.registerState = { status: 'error', error: 'sdk_load_failed' };
134
+ return this.registerState;
135
+ }
136
+ }
137
+ if (typeof this.lark.registerApp !== 'function') {
138
+ this.registerState = { status: 'error', error: 'sdk_too_old' };
139
+ return this.registerState;
140
+ }
141
+ const abort = new AbortController();
142
+ this.registerAbort = abort;
143
+ const state = { status: 'waiting' };
144
+ this.registerState = state;
145
+ let urlResolve = () => { };
146
+ const urlReady = new Promise((resolve) => {
147
+ urlResolve = resolve;
148
+ setTimeout(resolve, 10000); // 兜底:10s 拿不到二维码就按错误返回
149
+ });
150
+ this.lark
151
+ .registerApp({
152
+ signal: abort.signal,
153
+ onQRCodeReady: (info) => {
154
+ state.url = info.url;
155
+ state.expireIn = info.expireIn;
156
+ urlResolve();
157
+ },
158
+ appPreset: {
159
+ name: 'Cursor Feedback',
160
+ desc: 'Cursor AI 交互反馈通知机器人(由 cursor-feedback 插件扫码创建)',
161
+ },
162
+ // 平台基础模板已含收发消息 / reaction / 长连接事件;这里增量补齐本插件用到的资源权限。
163
+ // 平台不认识的名字会在确认页被静默丢弃,不会导致流程失败。
164
+ addons: {
165
+ scopes: { tenant: ['im:message', 'im:message:send_as_bot', 'im:resource'] },
166
+ events: { items: { tenant: ['im.message.receive_v1'] } },
167
+ },
168
+ // 只允许创建新应用:避免用户误选已有应用、其配置被覆盖
169
+ createOnly: true,
170
+ })
171
+ .then(async (result) => {
172
+ if (this.registerState !== state)
173
+ return; // 已被取消/新流程覆盖
174
+ const cfg = {
175
+ appId: result.client_id,
176
+ appSecret: result.client_secret,
177
+ enabled: this.enabled,
178
+ ackReaction: this.ackReaction,
179
+ queueWhenBusy: this.queueWhenBusy,
180
+ };
181
+ this.writePersistedConfig({ ...cfg, touched: true });
182
+ await this.configure(cfg);
183
+ state.status = 'success';
184
+ state.appId = result.client_id;
185
+ state.url = undefined;
186
+ flog(`扫码创建应用成功: ${result.client_id}`);
187
+ })
188
+ .catch((e) => {
189
+ if (this.registerState !== state)
190
+ return;
191
+ state.status = 'error';
192
+ state.url = undefined;
193
+ state.error = [e?.code, e?.description].filter(Boolean).join(': ') || String(e);
194
+ flog(`扫码创建应用失败: ${state.error}`);
195
+ });
196
+ await urlReady;
197
+ if (state.status === 'waiting' && !state.url) {
198
+ abort.abort();
199
+ this.registerAbort = null;
200
+ this.registerState = { status: 'error', error: 'qr_timeout' };
201
+ return this.registerState;
202
+ }
203
+ // 二维码在 server 侧生成(插件 VSIX 不带 node_modules);失败则面板退化为只显示链接按钮
204
+ if (state.status === 'waiting' && state.url) {
205
+ try {
206
+ const QRCode = await Promise.resolve().then(() => __importStar(require('qrcode')));
207
+ state.qr = await QRCode.toDataURL(state.url, { margin: 1, width: 220 });
208
+ }
209
+ catch (e) {
210
+ flog('二维码生成失败(退化为链接): ' + e);
211
+ }
212
+ }
213
+ return this.registerState;
214
+ }
215
+ /** 取消进行中的扫码创建流程(用户关闭弹窗 / 主动取消) */
216
+ cancelRegister() {
217
+ if (this.registerAbort) {
218
+ this.registerAbort.abort();
219
+ this.registerAbort = null;
220
+ }
221
+ if (this.registerState.status === 'waiting') {
222
+ this.registerState = { status: 'idle' };
223
+ }
224
+ }
112
225
  /** 读取当前 appId 对应的绑定 chat_id(来自磁盘,跨进程共享) */
113
226
  getBoundChatId() {
114
227
  if (!this.config?.appId)
package/dist/i18n/en.json CHANGED
@@ -58,7 +58,6 @@
58
58
  "feishuAppSecret": "App Secret",
59
59
  "feishuAppIdPlaceholder": "cli_xxxxxxxx",
60
60
  "feishuSecretPlaceholder": "Enter App Secret",
61
- "feishuGuide": "How to set up a Feishu bot?",
62
61
  "feishuClose": "Close",
63
62
  "feishuStatusUnconfigured": "Not configured",
64
63
  "feishuStatusConfigured": "Configured · send your bot a message to bind",
@@ -73,8 +72,29 @@
73
72
  "osNotifyDesc": "Sends a system notification when the IDE is in the background.",
74
73
  "feishuAckLabel": "Get emoji acknowledgement",
75
74
  "feishuAckDesc": "After you reply in Feishu, the bot adds a Get emoji as acknowledgement.",
76
- "feishuQueueLabel": "Queue messages while AI is busy",
77
- "feishuQueueDesc": "Messages sent while the AI is busy are queued and auto-delivered on its next feedback round, marked as appended during the task.",
75
+ "queueWhenBusyLabel": "Queue messages while AI is busy",
76
+ "queueWhenBusyDesc": "While the AI is busy, messages from the panel and Feishu are queued and auto-delivered in order on its next feedback round, marked as appended during the task.",
77
+ "queueSend": "Queue Message",
78
+ "queuedTitle": "Queued messages",
79
+ "queueModeHint": "AI is busy — messages sent now are queued and delivered when it next asks for feedback",
80
+ "toastPanelQueued": "✓ Queued — the AI will receive it after finishing this task",
81
+ "queueFailedPending": "AI is waiting for your feedback — submit it directly instead",
82
+ "queueFailedDisabled": "Busy-time queuing is turned off in settings — the message was not queued",
83
+ "queueFailedNoServer": "No active AI session found — the message was not queued",
84
+ "sourceFeishu": "Feishu",
85
+ "sourcePanel": "Panel",
86
+ "queueMetaImages": "img",
87
+ "queueMetaFiles": "file",
88
+ "summaryPrevTip": "Older summary",
89
+ "summaryNextTip": "Newer summary",
90
+ "feishuRegisterBtn": "Create app by QR code",
91
+ "registerLoading": "Getting QR code…",
92
+ "registerScanHint": "Scan with Feishu to confirm — permissions are preset and credentials fill in automatically",
93
+ "registerOpenLink": "Don't want to scan? Create via link",
94
+ "registerRetry": "Retry",
95
+ "registerSuccess": "Created! Credentials configured automatically",
96
+ "registerFailed": "Creation failed",
97
+ "registerNoServer": "MCP Server is not running — cannot start creation",
78
98
  "quickReplyDefault1": "Keep waiting for my feedback",
79
99
  "quickReplyDefault2": "End the task",
80
100
  "quickReplyEdit": "Edit quick replies",
@@ -149,7 +149,6 @@ function getDefaultMessages() {
149
149
  feishuAppSecret: "App Secret",
150
150
  feishuAppIdPlaceholder: "cli_xxxxxxxx",
151
151
  feishuSecretPlaceholder: "Enter App Secret",
152
- feishuGuide: "How to set up a Feishu bot?",
153
152
  feishuClose: "Close",
154
153
  feishuStatusUnconfigured: "Not configured",
155
154
  feishuStatusConfigured: "Configured · send your bot a message to bind",
@@ -164,6 +163,29 @@ function getDefaultMessages() {
164
163
  osNotifyDesc: "Sends a system notification when the IDE is in the background.",
165
164
  feishuAckLabel: "Get emoji acknowledgement",
166
165
  feishuAckDesc: "After you reply in Feishu, the bot adds a Get emoji as acknowledgement.",
166
+ queueWhenBusyLabel: "Queue messages while AI is busy",
167
+ queueWhenBusyDesc: "While the AI is busy, messages from the panel and Feishu are queued and auto-delivered in order on its next feedback round, marked as appended during the task.",
168
+ queueSend: "Queue Message",
169
+ queuedTitle: "Queued messages",
170
+ queueModeHint: "AI is busy — messages sent now are queued and delivered when it next asks for feedback",
171
+ toastPanelQueued: "✓ Queued — the AI will receive it after finishing this task",
172
+ queueFailedPending: "AI is waiting for your feedback — submit it directly instead",
173
+ queueFailedDisabled: "Busy-time queuing is turned off in settings — the message was not queued",
174
+ queueFailedNoServer: "No active AI session found — the message was not queued",
175
+ sourceFeishu: "Feishu",
176
+ sourcePanel: "Panel",
177
+ queueMetaImages: "img",
178
+ queueMetaFiles: "file",
179
+ summaryPrevTip: "Older summary",
180
+ summaryNextTip: "Newer summary",
181
+ feishuRegisterBtn: "Create app by QR code",
182
+ registerLoading: "Getting QR code…",
183
+ registerScanHint: "Scan with Feishu to confirm — permissions are preset and credentials fill in automatically",
184
+ registerOpenLink: "Don't want to scan? Create via link",
185
+ registerRetry: "Retry",
186
+ registerSuccess: "Created! Credentials configured automatically",
187
+ registerFailed: "Creation failed",
188
+ registerNoServer: "MCP Server is not running — cannot start creation",
167
189
  quickReplyDefault1: "Keep waiting for my feedback",
168
190
  quickReplyDefault2: "End the task",
169
191
  quickReplyEdit: "Edit quick replies",
@@ -58,7 +58,6 @@
58
58
  "feishuAppSecret": "App Secret",
59
59
  "feishuAppIdPlaceholder": "cli_xxxxxxxx",
60
60
  "feishuSecretPlaceholder": "请输入 App Secret",
61
- "feishuGuide": "如何配置飞书机器人?",
62
61
  "feishuClose": "关闭",
63
62
  "feishuStatusUnconfigured": "未配置",
64
63
  "feishuStatusConfigured": "已配置 · 请给机器人发条消息以绑定接收方",
@@ -73,8 +72,29 @@
73
72
  "osNotifyDesc": "IDE 切到后台时,用系统通知提醒你。",
74
73
  "feishuAckLabel": "Get 表情回执",
75
74
  "feishuAckDesc": "你在飞书回复后,机器人加个 Get 表情表示「已收到」。",
76
- "feishuQueueLabel": "忙时消息排队",
77
- "feishuQueueDesc": "AI 正忙时你发的消息先排队,等它下一轮询问时自动送达,并附「任务期间追加」提示。",
75
+ "queueWhenBusyLabel": "忙时消息排队",
76
+ "queueWhenBusyDesc": "AI 正忙时,面板与飞书发送的消息先排队,等它下一轮询问时按序自动送达,并附「任务期间追加」提示。",
77
+ "queueSend": "排队发送",
78
+ "queuedTitle": "排队中的消息",
79
+ "queueModeHint": "AI 正忙,现在发送的消息会排队,等它下一轮询问时自动送达",
80
+ "toastPanelQueued": "✓ 已排队,AI 完成当前任务后自动收到",
81
+ "queueFailedPending": "AI 正在等待你的反馈,请直接提交",
82
+ "queueFailedDisabled": "忙时排队已在设置中关闭,消息未排队",
83
+ "queueFailedNoServer": "没有找到正在工作的 AI 会话,消息未排队",
84
+ "sourceFeishu": "飞书",
85
+ "sourcePanel": "面板",
86
+ "queueMetaImages": "图片",
87
+ "queueMetaFiles": "文件",
88
+ "summaryPrevTip": "更早的摘要",
89
+ "summaryNextTip": "更新的摘要",
90
+ "feishuRegisterBtn": "扫码快速创建应用",
91
+ "registerLoading": "正在获取二维码…",
92
+ "registerScanHint": "用飞书「扫一扫」确认创建,权限已预置,凭证会自动填好",
93
+ "registerOpenLink": "不想扫码?点击链接创建",
94
+ "registerRetry": "重试",
95
+ "registerSuccess": "创建成功!凭证已自动配置",
96
+ "registerFailed": "创建失败",
97
+ "registerNoServer": "MCP Server 未运行,无法发起创建",
78
98
  "quickReplyDefault1": "继续等待我的反馈",
79
99
  "quickReplyDefault2": "结束任务",
80
100
  "quickReplyEdit": "编辑快捷短语",