lark-remote 0.4.3 → 0.4.5

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 (106) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/binder.d.ts +11 -10
  3. package/dist/binder.js +17 -16
  4. package/dist/bridge/inbound-media.d.ts +1 -27
  5. package/dist/bridge/inbound-media.js +13 -14
  6. package/dist/bridge/index.d.ts +49 -49
  7. package/dist/bridge/index.js +99 -92
  8. package/dist/bridge/queue-manager.d.ts +33 -26
  9. package/dist/bridge/queue-manager.js +117 -117
  10. package/dist/card/bash-renderer.js +4 -4
  11. package/dist/card/card-shared.d.ts +1 -1
  12. package/dist/card/card-shared.js +2 -2
  13. package/dist/card/run-renderer.js +8 -8
  14. package/dist/card/run-state.js +81 -31
  15. package/dist/card/tool-render.js +6 -2
  16. package/dist/cli.js +5 -6
  17. package/dist/clone.d.ts +1 -6
  18. package/dist/clone.js +4 -4
  19. package/dist/common/clamp.d.ts +2 -0
  20. package/dist/common/clamp.js +5 -0
  21. package/dist/common/fs.d.ts +4 -3
  22. package/dist/common/fs.js +5 -4
  23. package/dist/config/dir.d.ts +4 -5
  24. package/dist/config/dir.js +7 -8
  25. package/dist/config/wizard.js +2 -2
  26. package/dist/connector/index.d.ts +0 -2
  27. package/dist/connector/index.js +5 -5
  28. package/dist/inbound/receipt.d.ts +1 -3
  29. package/dist/inbound/receipt.js +2 -2
  30. package/dist/inbound/turn-assembler.d.ts +1 -3
  31. package/dist/inbound/turn-assembler.js +2 -2
  32. package/dist/index.js +30 -22
  33. package/dist/instance-lock.js +4 -4
  34. package/dist/persistence/atomic-write.js +3 -3
  35. package/dist/platform/agent-stopper.d.ts +1 -1
  36. package/dist/platform/terminator-posix.js +5 -5
  37. package/dist/platform/terminator-win32.js +1 -1
  38. package/dist/router/card-helpers.d.ts +1 -3
  39. package/dist/router/card-helpers.js +2 -2
  40. package/dist/router/config/codex.js +1 -1
  41. package/dist/router/config/common/model-patch.d.ts +1 -2
  42. package/dist/router/config/common/model-patch.js +1 -2
  43. package/dist/router/config/opencode.js +1 -1
  44. package/dist/router/config/pi.js +1 -1
  45. package/dist/router/index.d.ts +22 -22
  46. package/dist/router/index.js +71 -52
  47. package/dist/router/order-exec-dispatch.d.ts +3 -10
  48. package/dist/router/order-exec-dispatch.js +3 -3
  49. package/dist/router/{utils.d.ts → usage-format.d.ts} +2 -4
  50. package/dist/router/{utils.js → usage-format.js} +3 -8
  51. package/dist/runner/claude/runner.d.ts +1 -1
  52. package/dist/runner/claude/runner.js +1 -2
  53. package/dist/runner/claude/session.d.ts +2 -4
  54. package/dist/runner/claude/session.js +7 -9
  55. package/dist/runner/common/acp/{protocol-helpers.d.ts → acp-approval.d.ts} +2 -8
  56. package/dist/runner/common/acp/{protocol-helpers.js → acp-approval.js} +4 -3
  57. package/dist/runner/common/acp/base-acp-runner.d.ts +1 -1
  58. package/dist/runner/common/acp/base-acp-runner.js +1 -1
  59. package/dist/runner/common/acp/base-translator.d.ts +43 -0
  60. package/dist/runner/common/acp/base-translator.js +83 -18
  61. package/dist/runner/common/connection-based-runner.d.ts +4 -4
  62. package/dist/runner/common/connection-based-runner.js +3 -3
  63. package/dist/runner/common/jsonrpc/connection-manager.d.ts +12 -12
  64. package/dist/runner/common/jsonrpc/connection-manager.js +36 -36
  65. package/dist/runner/common/{usage.d.ts → result-usage.d.ts} +1 -1
  66. package/dist/runner/common/{usage.js → result-usage.js} +2 -2
  67. package/dist/runner/common/spawning-runner.d.ts +2 -2
  68. package/dist/runner/common/spawning-runner.js +8 -8
  69. package/dist/runner/common/{runner-utils.d.ts → synthetic-events.d.ts} +4 -4
  70. package/dist/runner/common/{runner-utils.js → synthetic-events.js} +5 -5
  71. package/dist/runner/dsh/client.d.ts +1 -1
  72. package/dist/runner/dsh/client.js +1 -1
  73. package/dist/runner/dsh/runner.js +1 -1
  74. package/dist/runner/kimi/acp/runner.d.ts +1 -1
  75. package/dist/runner/kimi/acp/runner.js +1 -1
  76. package/dist/runner/kimi/acp/translator.js +1 -1
  77. package/dist/runner/opencode/acp/runner.d.ts +2 -2
  78. package/dist/runner/opencode/acp/runner.js +1 -1
  79. package/dist/runner/opencode/acp/translator.d.ts +22 -1
  80. package/dist/runner/opencode/acp/translator.js +61 -1
  81. package/dist/runner/pi/rpc/runner.d.ts +1 -1
  82. package/dist/runner/pi/rpc/translator.d.ts +0 -1
  83. package/dist/runner/pi/rpc/translator.js +0 -2
  84. package/dist/runner/registry.d.ts +3 -3
  85. package/dist/runner/registry.js +3 -3
  86. package/dist/session/claude/session-index.d.ts +1 -1
  87. package/dist/session/claude/session-index.js +1 -1
  88. package/dist/session/claude/session-reader.d.ts +1 -1
  89. package/dist/session/claude/session-reader.js +1 -1
  90. package/dist/session/codex/rollout-reader.js +2 -2
  91. package/dist/session/dsh/sessions.d.ts +1 -1
  92. package/dist/session/dsh/sessions.js +1 -1
  93. package/dist/session/kimi/sessions.d.ts +0 -4
  94. package/dist/session/kimi/sessions.js +2 -8
  95. package/dist/session/opencode/sessions.js +3 -3
  96. package/dist/session/registry.d.ts +1 -1
  97. package/dist/session/registry.js +1 -1
  98. package/dist/session/session-store.d.ts +2 -3
  99. package/dist/session/session-store.js +2 -4
  100. package/dist/update/index.d.ts +1 -1
  101. package/dist/update/index.js +1 -1
  102. package/dist/update/version-check.d.ts +5 -3
  103. package/dist/update/version-check.js +10 -8
  104. package/dist/workspace/index.d.ts +4 -4
  105. package/dist/workspace/index.js +5 -5
  106. package/package.json +1 -1
@@ -27,7 +27,7 @@ export class QueueManager {
27
27
  * `updateQueuedTaskMessage`. The ordered array remains the source of truth
28
28
  * (position display depends on order); the index must be kept in sync on
29
29
  * every mutation (push / splice / shift) via the `indexAdd`/`indexRemove`
30
- * helpers. Same messageId is never enqueued twice in one workspace
30
+ * helpers. Same messageId is never enqueued twice in one cwd
31
31
  * (Feishu dedup + internal-key dedup), so no overwrite concern.
32
32
  */
33
33
  taskIndex = new Map();
@@ -41,7 +41,7 @@ export class QueueManager {
41
41
  */
42
42
  /** queue card 的 send promise 表(public:bridge 集成测试注入/断言用)。 */
43
43
  queueCardMessages = new Map();
44
- /** Track number of executing tasks per workspace (for queue card display). */
44
+ /** Track number of executing tasks per cwd (for queue card display). */
45
45
  pendingOrExecutingCount = new Map();
46
46
  /**
47
47
  * Monotonic counter minting a unique slot id per enqueued task. A slot
@@ -84,7 +84,7 @@ export class QueueManager {
84
84
  * before begin); `removeFromQueue` therefore needs no cleanup here.
85
85
  */
86
86
  beganMessageIds = new Set();
87
- /** Callback to check if workspace has an active run */
87
+ /** Callback to check if cwd has an active run */
88
88
  isWorkspaceRunning;
89
89
  /** Callback to send card updates */
90
90
  sendCard;
@@ -96,30 +96,30 @@ export class QueueManager {
96
96
  this.updateCard = updateCard;
97
97
  }
98
98
  /** P3-5: register a queued task in the per-workspace `messageId → task` index. */
99
- indexAdd(workspace, task) {
100
- let idx = this.taskIndex.get(workspace);
99
+ indexAdd(cwd, task) {
100
+ let idx = this.taskIndex.get(cwd);
101
101
  if (!idx) {
102
102
  idx = new Map();
103
- this.taskIndex.set(workspace, idx);
103
+ this.taskIndex.set(cwd, idx);
104
104
  }
105
105
  idx.set(task.messageId, task);
106
106
  }
107
107
  /** P3-5: drop a task from the per-workspace index by messageId. */
108
- indexRemove(workspace, messageId) {
109
- this.taskIndex.get(workspace)?.delete(messageId);
108
+ indexRemove(cwd, messageId) {
109
+ this.taskIndex.get(cwd)?.delete(messageId);
110
110
  }
111
111
  /** P3-5: O(1) lookup of a queued task by messageId via the index. */
112
- indexGet(workspace, messageId) {
113
- return this.taskIndex.get(workspace)?.get(messageId);
112
+ indexGet(cwd, messageId) {
113
+ return this.taskIndex.get(cwd)?.get(messageId);
114
114
  }
115
115
  /**
116
116
  * Enqueue a task into the workspace-level serial queue.
117
- * Each workspace has its own serial queue for parallel execution across workspaces.
117
+ * Each cwd has its own serial queue for parallel execution across workspaces.
118
118
  */
119
- enqueue(workspace, task, opts) {
119
+ enqueue(cwd, task, opts) {
120
120
  // Guard: reject non-function tasks that would poison the queue chain
121
121
  if (typeof task !== 'function') {
122
- getLogger().warn('[queue-manager] enqueue ignored task is not a function, workspace=', workspace);
122
+ getLogger().warn('[queue-manager] enqueue ignored task is not a function, cwd=', cwd);
123
123
  return;
124
124
  }
125
125
  // Mint a unique slot id for this task's execution period (regardless of
@@ -137,15 +137,15 @@ export class QueueManager {
137
137
  chatId: taskMeta.chatId,
138
138
  messageId: taskMeta.messageId,
139
139
  feishuReplyTo: taskMeta.feishuReplyTo,
140
- workspace,
140
+ cwd,
141
141
  timestamp: Date.now(),
142
142
  messagePreview,
143
143
  editable: taskMeta.editable,
144
144
  binding: taskMeta.binding,
145
145
  };
146
146
  // Check if there are tasks waiting BEFORE adding this one
147
- const currentExecutingCount = this.pendingOrExecutingCount.get(workspace) ?? 0;
148
- const currentQueueLength = this.queuedTasks.get(workspace)?.length ?? 0;
147
+ const currentExecutingCount = this.pendingOrExecutingCount.get(cwd) ?? 0;
148
+ const currentQueueLength = this.queuedTasks.get(cwd)?.length ?? 0;
149
149
  const hasWaitingTasks = currentExecutingCount > 0 || currentQueueLength > 0;
150
150
  // P2-5: bound the waiting backlog. When the queue is full, reject the
151
151
  // task with a visible card instead of appending it (which would grow
@@ -153,7 +153,7 @@ export class QueueManager {
153
153
  // fire another queue card). Do NOT increment pendingOrExecutingCount —
154
154
  // a rejected task never executes, so it must not occupy a slot.
155
155
  if (currentQueueLength >= MAX_WAITING_QUEUE) {
156
- getLogger().warn(`[queue-manager] queue full workspace=${workspace} depth=${currentQueueLength} rejecting task messageId=${taskMeta.messageId}`);
156
+ getLogger().warn(`[queue-manager] queue full cwd=${cwd} depth=${currentQueueLength} rejecting task messageId=${taskMeta.messageId}`);
157
157
  void this.sendCard(taskMeta.chatId, {
158
158
  schema: '2.0',
159
159
  config: { wide_screen_mode: true },
@@ -167,7 +167,7 @@ export class QueueManager {
167
167
  tag: 'div',
168
168
  text: {
169
169
  tag: 'lark_md',
170
- content: `当前 workspace 已有 ${MAX_WAITING_QUEUE} 条消息排队等待,为防止积压已拒收本条消息。请等待队列消化后重发,或用 \`/stop\` 清空当前任务。`,
170
+ content: `当前工作目录已有 ${MAX_WAITING_QUEUE} 条消息排队等待,为防止积压已拒收本条消息。请等待队列消化后重发,或用 \`/stop\` 清空当前任务。`,
171
171
  },
172
172
  },
173
173
  ],
@@ -177,38 +177,38 @@ export class QueueManager {
177
177
  });
178
178
  return;
179
179
  }
180
- // Add to workspace queue list
181
- let taskList = this.queuedTasks.get(workspace);
180
+ // Add to cwd queue list
181
+ let taskList = this.queuedTasks.get(cwd);
182
182
  if (!taskList) {
183
183
  taskList = [];
184
- this.queuedTasks.set(workspace, taskList);
184
+ this.queuedTasks.set(cwd, taskList);
185
185
  }
186
186
  taskList.push(queuedTask);
187
- this.indexAdd(workspace, queuedTask);
187
+ this.indexAdd(cwd, queuedTask);
188
188
  // Increment executing count SYNCHRONOUSLY so subsequent enqueues see it
189
- this.pendingOrExecutingCount.set(workspace, currentExecutingCount + 1);
189
+ this.pendingOrExecutingCount.set(cwd, currentExecutingCount + 1);
190
190
  // Only send the queue card if the task actually has to wait.
191
191
  if (hasWaitingTasks) {
192
- void this.sendQueueStatusCard(workspace, taskMeta.chatId, taskMeta.messageId, messagePreview,
192
+ void this.sendQueueStatusCard(cwd, taskMeta.chatId, taskMeta.messageId, messagePreview,
193
193
  // Reply to the real Feishu message id when the queue key is an
194
194
  // internal key (order.exec); otherwise messageId IS the Feishu id.
195
195
  taskMeta.feishuReplyTo);
196
196
  }
197
- getLogger().debug(`[queue-manager] enqueue task queued workspace=${workspace} queueCard=${hasWaitingTasks} executing=${currentExecutingCount + 1} queueLen=${currentQueueLength + 1}`);
197
+ getLogger().debug(`[queue-manager] enqueue task queued cwd=${cwd} queueCard=${hasWaitingTasks} executing=${currentExecutingCount + 1} queueLen=${currentQueueLength + 1}`);
198
198
  }
199
- // Get or create the queue for this workspace
200
- let queue = this.queues.get(workspace);
199
+ // Get or create the queue for this cwd
200
+ let queue = this.queues.get(cwd);
201
201
  if (!queue) {
202
202
  queue = Promise.resolve();
203
- this.queues.set(workspace, queue);
203
+ this.queues.set(cwd, queue);
204
204
  }
205
- getLogger().debug(`[queue-manager] enqueue workspace=${workspace}`);
205
+ getLogger().debug(`[queue-manager] enqueue cwd=${cwd}`);
206
206
  // Capture messageId for cancellation guard
207
207
  const messageId = taskMeta?.messageId;
208
208
  // messagePreview already captured above (outside taskMeta block)
209
209
  const newQueue = queue
210
210
  .then(() => {
211
- getLogger().debug(`[queue-manager] task begin workspace=${workspace} messageId=${messageId}`);
211
+ getLogger().debug(`[queue-manager] task begin cwd=${cwd} messageId=${messageId}`);
212
212
  // Live preview for the executing card: read from the QueuedTask at
213
213
  // begin time, not the enqueue-closure `taskMeta` (which is frozen).
214
214
  // `updateQueuedTaskMessage` (queue.edit/queue.input) mutates the live
@@ -221,19 +221,19 @@ export class QueueManager {
221
221
  // double O(N) scan. The ordered array still drives removal (splice
222
222
  // preserves queue order for position display); the index stays in
223
223
  // sync via indexRemove.
224
- const task = this.indexGet(workspace, messageId);
224
+ const task = this.indexGet(cwd, messageId);
225
225
  if (!task) {
226
- getLogger().debug(`[queue-manager] task skipped (cancelled) workspace=${workspace} messageId=${messageId}`);
226
+ getLogger().debug(`[queue-manager] task skipped (cancelled) cwd=${cwd} messageId=${messageId}`);
227
227
  return;
228
228
  }
229
229
  livePreview = task.messagePreview;
230
230
  // Remove this task's metadata by messageId
231
- const taskList = this.queuedTasks.get(workspace);
231
+ const taskList = this.queuedTasks.get(cwd);
232
232
  const idx = taskList?.findIndex((t) => t.messageId === messageId) ?? -1;
233
233
  if (taskList && idx >= 0) {
234
234
  taskList.splice(idx, 1);
235
235
  }
236
- this.indexRemove(workspace, messageId);
236
+ this.indexRemove(cwd, messageId);
237
237
  // Cancellation check passed and the task has been removed: it is
238
238
  // about to run, so record it as began for queue.immediate feedback.
239
239
  // Bounded: ids are only consulted for the current immediate target,
@@ -252,12 +252,12 @@ export class QueueManager {
252
252
  // so a stale closure can never run on a later task.
253
253
  let replacement;
254
254
  if (messageId) {
255
- const workspaceReplacements = this.taskReplacements.get(workspace);
255
+ const workspaceReplacements = this.taskReplacements.get(cwd);
256
256
  replacement = workspaceReplacements?.get(messageId);
257
257
  if (replacement) {
258
258
  workspaceReplacements?.delete(messageId);
259
259
  if (workspaceReplacements && workspaceReplacements.size === 0) {
260
- this.taskReplacements.delete(workspace);
260
+ this.taskReplacements.delete(cwd);
261
261
  }
262
262
  }
263
263
  }
@@ -265,7 +265,7 @@ export class QueueManager {
265
265
  // This must happen after the cancellation check: a skipped/cancelled
266
266
  // task must keep its "❌ 已撤销" card, not be flipped to executing.
267
267
  if (messageId) {
268
- void this.updateQueueCardToExecuting(workspace, messageId, livePreview, true);
268
+ void this.updateQueueCardToExecuting(cwd, messageId, livePreview, true);
269
269
  }
270
270
  // Re-arm the pending/executing count for every task that is about to
271
271
  // run. `resetExecutingCount` (external interrupt) zeroes the count,
@@ -273,42 +273,42 @@ export class QueueManager {
273
273
  // task enqueued BEFORE the interrupt can begin with count 0. Without
274
274
  // this, a running task is invisible to later enqueues and they
275
275
  // silently skip the "⏳ 消息排队中" card. This applies regardless of
276
- // taskMeta: resetExecutingCount can clear the count for any workspace,
276
+ // taskMeta: resetExecutingCount can clear the count for any cwd,
277
277
  // and a task that resumes after an interrupt must re-arm even when it
278
278
  // carries no metadata.
279
- const currentCount = this.pendingOrExecutingCount.get(workspace) ?? 0;
279
+ const currentCount = this.pendingOrExecutingCount.get(cwd) ?? 0;
280
280
  if (currentCount < 1) {
281
- this.pendingOrExecutingCount.set(workspace, 1);
282
- getLogger().debug(`[queue-manager] re-armed pendingOrExecutingCount workspace=${workspace} (interrupt resume)`);
281
+ this.pendingOrExecutingCount.set(cwd, 1);
282
+ getLogger().debug(`[queue-manager] re-armed pendingOrExecutingCount cwd=${cwd} (interrupt resume)`);
283
283
  }
284
284
  // Mark this task's slot as the current execution period before running
285
285
  // it. `resetExecutingCount` reads this marker to grant the interrupt
286
286
  // credit to exactly this task; the settle removes the marker.
287
- this.executingSlot.set(workspace, slotId);
287
+ this.executingSlot.set(cwd, slotId);
288
288
  return replacement ? replacement() : task();
289
289
  })
290
290
  .then(() => {
291
- getLogger().debug(`[queue-manager] task end workspace=${workspace}`);
291
+ getLogger().debug(`[queue-manager] task end cwd=${cwd}`);
292
292
  // beganMessageIds entry intentionally retained: sticky by design
293
293
  // (A21) — the marker records "has ever begun", not "is currently
294
294
  // running", so a task that began and settled quickly still reports
295
295
  // "已开始执行" to queue.immediate.
296
- this.decrementExecutingCount(workspace, slotId);
296
+ this.decrementExecutingCount(cwd, slotId);
297
297
  })
298
298
  .catch((err) => {
299
299
  getLogger().error('[queue-manager] queue task error:', err);
300
300
  // Same sticky rationale as the success settle: the marker survives
301
301
  // even when the task errors after beginning (A21).
302
- this.decrementExecutingCount(workspace, slotId);
302
+ this.decrementExecutingCount(cwd, slotId);
303
303
  });
304
- this.queues.set(workspace, newQueue);
304
+ this.queues.set(cwd, newQueue);
305
305
  }
306
306
  /**
307
307
  * Execute a task immediately without going through the queue.
308
308
  * Used for / commands that should respond immediately.
309
309
  */
310
- enqueueImmediate(workspace, task) {
311
- getLogger().debug(`[queue-manager] enqueueImmediate workspace=${workspace}`);
310
+ enqueueImmediate(cwd, task) {
311
+ getLogger().debug(`[queue-manager] enqueueImmediate cwd=${cwd}`);
312
312
  void task().catch((err) => getLogger().error('[queue-manager] immediate task error:', err));
313
313
  }
314
314
  /**
@@ -316,22 +316,22 @@ export class QueueManager {
316
316
  * place, preserving its queue position (tasks queued behind it still run
317
317
  * after it). Consumed when the task's slot begins; removed if cancelled.
318
318
  */
319
- setTaskReplacement(workspace, messageId, task) {
320
- let workspaceReplacements = this.taskReplacements.get(workspace);
319
+ setTaskReplacement(cwd, messageId, task) {
320
+ let workspaceReplacements = this.taskReplacements.get(cwd);
321
321
  if (!workspaceReplacements) {
322
322
  workspaceReplacements = new Map();
323
- this.taskReplacements.set(workspace, workspaceReplacements);
323
+ this.taskReplacements.set(cwd, workspaceReplacements);
324
324
  }
325
325
  workspaceReplacements.set(messageId, task);
326
- getLogger().debug(`[queue-manager] set task replacement workspace=${workspace} messageId=${messageId}`);
326
+ getLogger().debug(`[queue-manager] set task replacement cwd=${cwd} messageId=${messageId}`);
327
327
  }
328
328
  /** Send a queue status card showing current queue position and actions. */
329
- async sendQueueStatusCard(workspace, chatId, replyToMessageId, messagePreview,
329
+ async sendQueueStatusCard(cwd, chatId, replyToMessageId, messagePreview,
330
330
  /** Feishu reply target when `replyToMessageId` (the queue key) is an
331
331
  * internal key, not a valid Feishu message id. Defaults to the queue key
332
332
  * for hand-typed messages. */
333
333
  feishuReplyTo) {
334
- const taskList = this.queuedTasks.get(workspace) ?? [];
334
+ const taskList = this.queuedTasks.get(cwd) ?? [];
335
335
  const positionInQueue = taskList.findIndex((t) => t.chatId === chatId && t.messageId === replyToMessageId);
336
336
  const actualPosition = positionInQueue + 1;
337
337
  const tasksAhead = actualPosition - 1;
@@ -343,7 +343,7 @@ export class QueueManager {
343
343
  title: { tag: 'plain_text', content: '⏳ 消息排队中' },
344
344
  },
345
345
  body: {
346
- elements: this.buildQueueStatusCardElements(workspace, actualPosition, tasksAhead, replyToMessageId, messagePreview),
346
+ elements: this.buildQueueStatusCardElements(cwd, actualPosition, tasksAhead, replyToMessageId, messagePreview),
347
347
  },
348
348
  };
349
349
  // The Feishu reply target must be a real Feishu message id. The queue
@@ -367,19 +367,19 @@ export class QueueManager {
367
367
  }
368
368
  return messageId;
369
369
  }
370
- /** Get queue info for a workspace. */
371
- getQueueInfo(workspace) {
372
- const taskList = this.queuedTasks.get(workspace) ?? [];
370
+ /** Get queue info for a cwd. */
371
+ getQueueInfo(cwd) {
372
+ const taskList = this.queuedTasks.get(cwd) ?? [];
373
373
  return {
374
374
  position: taskList.length,
375
375
  tasksAhead: Math.max(0, taskList.length - 1),
376
- isRunning: this.isWorkspaceRunning(workspace),
376
+ isRunning: this.isWorkspaceRunning(cwd),
377
377
  };
378
378
  }
379
379
  /** Build queue status card elements. */
380
- buildQueueStatusCardElements(workspace, actualPosition, tasksAhead, messageId, messagePreview) {
381
- const workspaceName = displayName(workspace);
382
- const isRunning = this.isWorkspaceRunning(workspace);
380
+ buildQueueStatusCardElements(cwd, actualPosition, tasksAhead, messageId, messagePreview) {
381
+ const workspaceName = displayName(cwd);
382
+ const isRunning = this.isWorkspaceRunning(cwd);
383
383
  const elements = [
384
384
  {
385
385
  tag: 'div',
@@ -408,7 +408,7 @@ export class QueueManager {
408
408
  tag: 'div',
409
409
  text: { tag: 'lark_md', content: `📝 \`${messagePreview}\`` },
410
410
  });
411
- const task = this.indexGet(workspace, messageId);
411
+ const task = this.indexGet(cwd, messageId);
412
412
  if (task?.editable !== false) {
413
413
  // Edit button: a pending (queued) task is editable by default. The
414
414
  // executing/cancelled states render via dedicated card builders that
@@ -418,40 +418,40 @@ export class QueueManager {
418
418
  text: { tag: 'plain_text', content: '✏️ 编辑' },
419
419
  type: 'default',
420
420
  size: 'small',
421
- behaviors: [{ type: 'callback', value: { cmd: 'queue.edit', workspace, messageId } }],
421
+ behaviors: [{ type: 'callback', value: { cmd: 'queue.edit', cwd, messageId } }],
422
422
  });
423
423
  }
424
424
  elements.push({ tag: 'hr' });
425
425
  }
426
426
  // Action buttons
427
- elements.push(...this.buildQueueActionButtons(workspace, messageId, false));
427
+ elements.push(...this.buildQueueActionButtons(cwd, messageId, false));
428
428
  return elements;
429
429
  }
430
430
  /**
431
431
  * Build the 撤销/立即执行 action button pair for a queue card.
432
432
  *
433
433
  * Both buttons route to `queue.cancel` / `queue.immediate` callbacks with
434
- * the same workspace/messageId; only the `disabled` flag varies by card
434
+ * the same cwd/messageId; only the `disabled` flag varies by card
435
435
  * state — pending cards enable both, executing/cancelled cards disable
436
436
  * both. Centralizing the pair here eliminates the 3-way duplication
437
437
  * between `buildQueueStatusCardElements`, `updateQueueCardToExecuting`,
438
438
  * and `updateQueueCardToCancelled` (Clean Code P2-1).
439
439
  */
440
- buildQueueActionButtons(workspace, messageId, disabled) {
440
+ buildQueueActionButtons(cwd, messageId, disabled) {
441
441
  return [
442
442
  {
443
443
  tag: 'button',
444
444
  text: { tag: 'plain_text', content: '❌ 撤销' },
445
445
  type: 'danger',
446
446
  disabled,
447
- behaviors: [{ type: 'callback', value: { cmd: 'queue.cancel', workspace, messageId } }],
447
+ behaviors: [{ type: 'callback', value: { cmd: 'queue.cancel', cwd, messageId } }],
448
448
  },
449
449
  {
450
450
  tag: 'button',
451
451
  text: { tag: 'plain_text', content: '⚡ 立即执行' },
452
452
  type: 'primary',
453
453
  disabled,
454
- behaviors: [{ type: 'callback', value: { cmd: 'queue.immediate', workspace, messageId } }],
454
+ behaviors: [{ type: 'callback', value: { cmd: 'queue.immediate', cwd, messageId } }],
455
455
  },
456
456
  ];
457
457
  }
@@ -466,20 +466,20 @@ export class QueueManager {
466
466
  * the card must keep its "❌ 已撤销" state instead of being flipped back to
467
467
  * executing. The mapping is deleted in finally in both cases.
468
468
  */
469
- async updateQueueCardToExecuting(workspace, messageId, messagePreview, started = false) {
469
+ async updateQueueCardToExecuting(cwd, messageId, messagePreview, started = false) {
470
470
  const cardMessageId = await this.queueCardMessages.get(messageId);
471
471
  if (!cardMessageId) {
472
472
  getLogger().debug(`[queue-manager] no queue card to update, messageId=${messageId}`);
473
473
  return;
474
474
  }
475
- const workspaceName = displayName(workspace);
475
+ const workspaceName = displayName(cwd);
476
476
  try {
477
477
  // Prefer the live preview: an edited task's messagePreview is updated in
478
478
  // place, and the passed-in snapshot may be stale if the card send was in
479
479
  // flight while the user edited (A19). The begin path (started=true) already
480
480
  // passes the live preview captured at begin (A12), so the fallback is only
481
481
  // exercised when the task is no longer queued.
482
- const liveTask = this.indexGet(workspace, messageId);
482
+ const liveTask = this.indexGet(cwd, messageId);
483
483
  const stillQueued = liveTask !== undefined;
484
484
  if (!stillQueued && !started) {
485
485
  getLogger().debug(`[queue-manager] queue card update to executing skipped (task no longer queued and not started) messageId=${messageId}`);
@@ -502,7 +502,7 @@ export class QueueManager {
502
502
  { tag: 'hr' },
503
503
  { tag: 'div', text: { tag: 'lark_md', content: `📝 \`${previewForCard}\`` } },
504
504
  { tag: 'hr' },
505
- ...this.buildQueueActionButtons(workspace, messageId, true),
505
+ ...this.buildQueueActionButtons(cwd, messageId, true),
506
506
  ],
507
507
  },
508
508
  };
@@ -517,38 +517,38 @@ export class QueueManager {
517
517
  }
518
518
  }
519
519
  /** Remove a task from the queue by messageId. Returns true if found and removed. */
520
- removeFromQueue(workspace, messageId) {
520
+ removeFromQueue(cwd, messageId) {
521
521
  // Drop any one-shot replacement for this task: a cancelled/removed task
522
522
  // must never leave a closure behind that could execute later.
523
- const workspaceReplacements = this.taskReplacements.get(workspace);
523
+ const workspaceReplacements = this.taskReplacements.get(cwd);
524
524
  if (workspaceReplacements?.delete(messageId) && workspaceReplacements.size === 0) {
525
- this.taskReplacements.delete(workspace);
525
+ this.taskReplacements.delete(cwd);
526
526
  }
527
- const taskList = this.queuedTasks.get(workspace);
527
+ const taskList = this.queuedTasks.get(cwd);
528
528
  if (!taskList)
529
529
  return false;
530
530
  // P3-5: index is the O(1) presence check; array splice keeps order.
531
- if (!this.indexGet(workspace, messageId))
531
+ if (!this.indexGet(cwd, messageId))
532
532
  return false;
533
533
  const index = taskList.findIndex((t) => t.messageId === messageId);
534
534
  if (index >= 0) {
535
535
  taskList.splice(index, 1);
536
536
  }
537
- this.indexRemove(workspace, messageId);
537
+ this.indexRemove(cwd, messageId);
538
538
  // Note: queueCardMessages mapping is NOT deleted here.
539
539
  // The caller (handleQueueCancel) will call updateQueueCardToCancelled
540
540
  // which will clean up the mapping after updating the card.
541
- getLogger().info(`[queue-manager] removed from queue workspace=${workspace} messageId=${messageId}`);
541
+ getLogger().info(`[queue-manager] removed from queue cwd=${cwd} messageId=${messageId}`);
542
542
  return true;
543
543
  }
544
544
  /** Update queue card to "cancelled" status when user clicks 撤销. */
545
- async updateQueueCardToCancelled(workspace, messageId) {
545
+ async updateQueueCardToCancelled(cwd, messageId) {
546
546
  const cardMessageId = await this.queueCardMessages.get(messageId);
547
547
  if (!cardMessageId) {
548
548
  getLogger().debug(`[queue-manager] no queue card to update (cancelled), messageId=${messageId}`);
549
549
  return;
550
550
  }
551
- const workspaceName = displayName(workspace);
551
+ const workspaceName = displayName(cwd);
552
552
  const card = {
553
553
  schema: '2.0',
554
554
  config: { wide_screen_mode: true },
@@ -565,7 +565,7 @@ export class QueueManager {
565
565
  { tag: 'hr' },
566
566
  { tag: 'div', text: { tag: 'lark_md', content: `📝 该消息已从队列中撤销` } },
567
567
  { tag: 'hr' },
568
- ...this.buildQueueActionButtons(workspace, messageId, true),
568
+ ...this.buildQueueActionButtons(cwd, messageId, true),
569
569
  ],
570
570
  },
571
571
  };
@@ -581,9 +581,9 @@ export class QueueManager {
581
581
  }
582
582
  }
583
583
  /** Get task metadata from queue. */
584
- getQueuedTask(workspace, messageId) {
584
+ getQueuedTask(cwd, messageId) {
585
585
  // P3-5: O(1) index lookup instead of array `find`.
586
- return this.indexGet(workspace, messageId);
586
+ return this.indexGet(cwd, messageId);
587
587
  }
588
588
  /**
589
589
  * Whether a task with the given messageId has begun executing (cancellation
@@ -594,19 +594,19 @@ export class QueueManager {
594
594
  hasBegan(messageId) {
595
595
  return this.beganMessageIds.has(messageId);
596
596
  }
597
- /** Get all queued tasks for a workspace. Returns a copy to prevent aliasing bugs. */
598
- getQueuedTasks(workspace) {
599
- return [...(this.queuedTasks.get(workspace) ?? [])];
597
+ /** Get all queued tasks for a cwd. Returns a copy to prevent aliasing bugs. */
598
+ getQueuedTasks(cwd) {
599
+ return [...(this.queuedTasks.get(cwd) ?? [])];
600
600
  }
601
601
  /** Update the messagePreview for a queued task. Returns true if found and updated. */
602
- updateQueuedTaskMessage(workspace, messageId, newMessage) {
602
+ updateQueuedTaskMessage(cwd, messageId, newMessage) {
603
603
  // P3-5: O(1) index lookup instead of array `find`.
604
- const task = this.indexGet(workspace, messageId);
604
+ const task = this.indexGet(cwd, messageId);
605
605
  if (!task)
606
606
  return false;
607
607
  task.messagePreview = newMessage;
608
608
  task.editedMessage = newMessage;
609
- getLogger().info(`[queue-manager] updated messagePreview workspace=${workspace} messageId=${messageId}`);
609
+ getLogger().info(`[queue-manager] updated messagePreview cwd=${cwd} messageId=${messageId}`);
610
610
  return true;
611
611
  }
612
612
  /**
@@ -621,8 +621,8 @@ export class QueueManager {
621
621
  * concurrent PATCH updateCard result. Returning { card } in the callback
622
622
  * response updates the card synchronously with no API race.
623
623
  */
624
- buildQueueCardForEdit(workspace, messageId, newMessagePreview) {
625
- const taskList = this.queuedTasks.get(workspace) ?? [];
624
+ buildQueueCardForEdit(cwd, messageId, newMessagePreview) {
625
+ const taskList = this.queuedTasks.get(cwd) ?? [];
626
626
  const positionInQueue = taskList.findIndex((t) => t.messageId === messageId);
627
627
  if (positionInQueue < 0) {
628
628
  getLogger().info(`[queue-manager] task not in queue (build edit card), messageId=${messageId}`);
@@ -638,12 +638,12 @@ export class QueueManager {
638
638
  title: { tag: 'plain_text', content: '⏳ 消息排队中' },
639
639
  },
640
640
  body: {
641
- elements: this.buildQueueStatusCardElements(workspace, actualPosition, tasksAhead, messageId, newMessagePreview),
641
+ elements: this.buildQueueStatusCardElements(cwd, actualPosition, tasksAhead, messageId, newMessagePreview),
642
642
  },
643
643
  };
644
644
  }
645
645
  /**
646
- * Decrement `pendingOrExecutingCount` for a workspace, honoring per-slot
646
+ * Decrement `pendingOrExecutingCount` for a cwd, honoring per-slot
647
647
  * interrupt bookkeeping.
648
648
  *
649
649
  * Called from the queue chain's `.then()`/`.catch()` settle with the slot
@@ -657,24 +657,24 @@ export class QueueManager {
657
657
  * newer task (enqueued after the reset) is still running.
658
658
  * 3. Otherwise → normal decrement.
659
659
  */
660
- decrementExecutingCount(workspace, slotId) {
661
- if (this.executingSlot.get(workspace) === slotId) {
662
- this.executingSlot.delete(workspace);
660
+ decrementExecutingCount(cwd, slotId) {
661
+ if (this.executingSlot.get(cwd) === slotId) {
662
+ this.executingSlot.delete(cwd);
663
663
  }
664
- const interrupted = this.interruptedSlots.get(workspace);
664
+ const interrupted = this.interruptedSlots.get(cwd);
665
665
  if (interrupted?.has(slotId)) {
666
666
  interrupted.delete(slotId);
667
667
  if (interrupted.size === 0) {
668
- this.interruptedSlots.delete(workspace);
668
+ this.interruptedSlots.delete(cwd);
669
669
  }
670
- getLogger().debug(`[queue-manager] skip decrement (interrupted slot) workspace=${workspace} slot=${slotId}`);
670
+ getLogger().debug(`[queue-manager] skip decrement (interrupted slot) cwd=${cwd} slot=${slotId}`);
671
671
  return;
672
672
  }
673
- const count = this.pendingOrExecutingCount.get(workspace) ?? 1;
674
- this.pendingOrExecutingCount.set(workspace, Math.max(0, count - 1));
673
+ const count = this.pendingOrExecutingCount.get(cwd) ?? 1;
674
+ this.pendingOrExecutingCount.set(cwd, Math.max(0, count - 1));
675
675
  }
676
676
  /**
677
- * Reset the executing task count for a workspace.
677
+ * Reset the executing task count for a cwd.
678
678
  *
679
679
  * This should be called when a running task is interrupted externally
680
680
  * (e.g., via /stop command or "立即执行" button) so that the queue
@@ -699,41 +699,41 @@ export class QueueManager {
699
699
  * running successor and mark ITS slot interrupted, hiding it from the
700
700
  * queue card (A22).
701
701
  */
702
- resetExecutingCount(workspace, expectedSlot) {
703
- const currentSlot = this.executingSlot.get(workspace);
702
+ resetExecutingCount(cwd, expectedSlot) {
703
+ const currentSlot = this.executingSlot.get(cwd);
704
704
  if (currentSlot !== expectedSlot) {
705
- getLogger().debug(`[queue-manager] resetExecutingCount skip (stopped task settled, slot advanced) workspace=${workspace} expectedSlot=${expectedSlot} currentSlot=${currentSlot ?? 'none'}`);
705
+ getLogger().debug(`[queue-manager] resetExecutingCount skip (stopped task settled, slot advanced) cwd=${cwd} expectedSlot=${expectedSlot} currentSlot=${currentSlot ?? 'none'}`);
706
706
  return;
707
707
  }
708
708
  // P3#9: only reset and grant an interrupt slot when there is actually a
709
709
  // pending/executing task.
710
- const currentCount = this.pendingOrExecutingCount.get(workspace) ?? 0;
710
+ const currentCount = this.pendingOrExecutingCount.get(cwd) ?? 0;
711
711
  if (currentCount === 0) {
712
- getLogger().debug(`[queue-manager] resetExecutingCount no-op (count already 0) workspace=${workspace}`);
712
+ getLogger().debug(`[queue-manager] resetExecutingCount no-op (count already 0) cwd=${cwd}`);
713
713
  return;
714
714
  }
715
715
  // Bind the interrupt credit to the currently executing task's slot (if
716
716
  // any; defensive against a count without an executing task). `Set.add`
717
717
  // dedupes repeated resets of the same slot.
718
718
  const slot = currentSlot;
719
- this.pendingOrExecutingCount.set(workspace, 0);
719
+ this.pendingOrExecutingCount.set(cwd, 0);
720
720
  if (slot !== undefined) {
721
- let interrupted = this.interruptedSlots.get(workspace);
721
+ let interrupted = this.interruptedSlots.get(cwd);
722
722
  if (!interrupted) {
723
723
  interrupted = new Set();
724
- this.interruptedSlots.set(workspace, interrupted);
724
+ this.interruptedSlots.set(cwd, interrupted);
725
725
  }
726
726
  interrupted.add(slot);
727
- getLogger().debug(`[queue-manager] reset pendingOrExecutingCount workspace=${workspace} interruptedSlot=${slot}`);
727
+ getLogger().debug(`[queue-manager] reset pendingOrExecutingCount cwd=${cwd} interruptedSlot=${slot}`);
728
728
  return;
729
729
  }
730
- getLogger().debug(`[queue-manager] reset pendingOrExecutingCount workspace=${workspace} (no executing slot)`);
730
+ getLogger().debug(`[queue-manager] reset pendingOrExecutingCount cwd=${cwd} (no executing slot)`);
731
731
  }
732
732
  /**
733
- * The slot id of the task currently executing in this workspace (if any).
733
+ * The slot id of the task currently executing in this cwd (if any).
734
734
  */
735
- getExecutingSlot(workspace) {
736
- return this.executingSlot.get(workspace);
735
+ getExecutingSlot(cwd) {
736
+ return this.executingSlot.get(cwd);
737
737
  }
738
738
  }
739
739
  //# sourceMappingURL=queue-manager.js.map
@@ -71,8 +71,8 @@ function assembleCard(state, elements) {
71
71
  schema: '2.0',
72
72
  config: { wide_screen_mode: true, update_multi: true },
73
73
  header: {
74
- template: bashHeaderTemplate2(state.terminal),
75
- title: { content: bashHeaderTitle2(state.terminal), tag: 'plain_text' },
74
+ template: bashHeaderTemplate(state.terminal),
75
+ title: { content: bashHeaderTitle(state.terminal), tag: 'plain_text' },
76
76
  },
77
77
  body: { elements },
78
78
  };
@@ -161,7 +161,7 @@ function bashStatusTagLabel(terminal, exitCode) {
161
161
  return '出错';
162
162
  }
163
163
  /** CardKit 2.0 header title */
164
- function bashHeaderTitle2(terminal) {
164
+ function bashHeaderTitle(terminal) {
165
165
  if (terminal === 'running')
166
166
  return '💻 执行命令...';
167
167
  if (terminal === 'error')
@@ -171,7 +171,7 @@ function bashHeaderTitle2(terminal) {
171
171
  return '✅ 命令执行完成';
172
172
  }
173
173
  /** CardKit 2.0 header template */
174
- function bashHeaderTemplate2(terminal) {
174
+ function bashHeaderTemplate(terminal) {
175
175
  return terminalToColor(terminal);
176
176
  }
177
177
  //# sourceMappingURL=bash-renderer.js.map