@yu_robotics/remote-cli 1.6.111 → 1.6.114

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 (46) hide show
  1. package/dist/client/MessageHandler.d.ts +4 -0
  2. package/dist/client/MessageHandler.d.ts.map +1 -1
  3. package/dist/client/MessageHandler.js +113 -9
  4. package/dist/client/MessageHandler.js.map +1 -1
  5. package/dist/client/WebSocketClient.d.ts +5 -0
  6. package/dist/client/WebSocketClient.d.ts.map +1 -1
  7. package/dist/client/WebSocketClient.js +37 -3
  8. package/dist/client/WebSocketClient.js.map +1 -1
  9. package/dist/commands/files.d.ts +5 -0
  10. package/dist/commands/files.d.ts.map +1 -0
  11. package/dist/commands/files.js +30 -0
  12. package/dist/commands/files.js.map +1 -0
  13. package/dist/commands/start.d.ts.map +1 -1
  14. package/dist/commands/start.js +4 -1
  15. package/dist/commands/start.js.map +1 -1
  16. package/dist/commands/status.d.ts.map +1 -1
  17. package/dist/commands/status.js +4 -1
  18. package/dist/commands/status.js.map +1 -1
  19. package/dist/files/DeviceIdentity.d.ts +10 -0
  20. package/dist/files/DeviceIdentity.d.ts.map +1 -0
  21. package/dist/files/DeviceIdentity.js +82 -0
  22. package/dist/files/DeviceIdentity.js.map +1 -0
  23. package/dist/files/DocumentExtractor.d.ts +4 -0
  24. package/dist/files/DocumentExtractor.d.ts.map +1 -0
  25. package/dist/files/DocumentExtractor.js +59 -0
  26. package/dist/files/DocumentExtractor.js.map +1 -0
  27. package/dist/files/DocumentWorker.d.ts +3 -0
  28. package/dist/files/DocumentWorker.d.ts.map +1 -0
  29. package/dist/files/DocumentWorker.js +359 -0
  30. package/dist/files/DocumentWorker.js.map +1 -0
  31. package/dist/files/FileIO.d.ts +21 -0
  32. package/dist/files/FileIO.d.ts.map +1 -0
  33. package/dist/files/FileIO.js +134 -0
  34. package/dist/files/FileIO.js.map +1 -0
  35. package/dist/files/FileInbox.d.ts +60 -0
  36. package/dist/files/FileInbox.d.ts.map +1 -0
  37. package/dist/files/FileInbox.js +426 -0
  38. package/dist/files/FileInbox.js.map +1 -0
  39. package/dist/index.js +16 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/types/config.d.ts +3 -0
  42. package/dist/types/config.d.ts.map +1 -1
  43. package/dist/types/config.js.map +1 -1
  44. package/dist/types/index.d.ts +5 -3
  45. package/dist/types/index.d.ts.map +1 -1
  46. package/package.json +4 -1
@@ -37,11 +37,13 @@ export declare class MessageHandler {
37
37
  private readonly abortOperations;
38
38
  private automaticUpdateInProgress;
39
39
  private approvalCardsSupported;
40
+ private streamingContextSupported;
40
41
  private delegationProgressSupported;
41
42
  private delegationProgressTextSupported;
42
43
  private readonly pendingApprovalCards;
43
44
  private readonly delegation;
44
45
  private readonly delegationBridges;
46
+ private fileInbox?;
45
47
  constructor(wsClient: WebSocketClient, threadPool: ThreadExecutorPool, threadManager: ThreadManager, directoryGuard: DirectoryGuard, config: ConfigManager);
46
48
  reconcilePendingDelegatedWorkers(): Promise<void>;
47
49
  /**
@@ -165,6 +167,8 @@ export declare class MessageHandler {
165
167
  private showApprovalFallback;
166
168
  private resolveApprovalCard;
167
169
  private handleApprovalResponse;
170
+ /** Publish routing metadata before backend startup or the first output token. */
171
+ private sendStreamContext;
168
172
  private sendStreamChunk;
169
173
  private sendToolUse;
170
174
  private sendToolResult;
@@ -1 +1 @@
1
- {"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../../src/client/MessageHandler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../../src/client/MessageHandler.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,14 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.MessageHandler = void 0;
7
+ const FileInbox_1 = require("../files/FileInbox");
8
+ const DeviceIdentity_1 = require("../files/DeviceIdentity");
9
+ const crypto_1 = require("crypto");
10
+ const DocumentExtractor_1 = require("../files/DocumentExtractor");
11
+ const path_1 = __importDefault(require("path"));
4
12
  const hooks_1 = require("../hooks");
5
13
  const FileReadDetector_1 = require("../utils/FileReadDetector");
6
14
  const LocalImageDetector_1 = require("../utils/LocalImageDetector");
@@ -10,7 +18,7 @@ const child_process_1 = require("child_process");
10
18
  const promises_1 = require("fs/promises");
11
19
  const fs_1 = require("fs");
12
20
  const os_1 = require("os");
13
- const path_1 = require("path");
21
+ const path_2 = require("path");
14
22
  const config_1 = require("../types/config");
15
23
  const uuid_1 = require("uuid");
16
24
  const ZCodeCommand_1 = require("../executor/zcode/ZCodeCommand");
@@ -94,17 +102,20 @@ class MessageHandler {
94
102
  abortOperations = new Map();
95
103
  automaticUpdateInProgress = false;
96
104
  approvalCardsSupported = false;
105
+ streamingContextSupported = false;
97
106
  delegationProgressSupported = false;
98
107
  delegationProgressTextSupported = false;
99
108
  pendingApprovalCards = new Map();
100
109
  delegation;
101
110
  delegationBridges = new Map();
111
+ fileInbox;
102
112
  constructor(wsClient, threadPool, threadManager, directoryGuard, config) {
103
113
  this.wsClient = wsClient;
104
114
  this.threadPool = threadPool;
105
115
  this.threadManager = threadManager;
106
116
  this.directoryGuard = directoryGuard;
107
117
  this.config = config;
118
+ this.wsClient.onClose?.(() => this.fileInbox?.disconnect());
108
119
  this.delegation = new DelegationManager_1.DelegationManager(directoryGuard);
109
120
  this.notificationAdapter = new hooks_1.FeishuNotificationAdapter(wsClient);
110
121
  this.notificationAdapter.setThreadNameResolver((threadId) => this.threadManager.getThread(threadId)?.name);
@@ -140,7 +151,20 @@ class MessageHandler {
140
151
  await this.handleStatusQuery(message.messageId);
141
152
  return;
142
153
  case 'command':
143
- await this.handleCommandMessage(message);
154
+ await this.handleCommandMessage(message, this.fileInbox?.mark());
155
+ return;
156
+ case 'file_pending':
157
+ if (this.fileInbox)
158
+ await this.fileInbox.accept(message);
159
+ else
160
+ this.wsClient.send({ type: 'file_status', fileId: message.fileId, status: 'failed',
161
+ detail: 'Local file staging is unavailable. Check secure enrollment, files.retentionDays and storage permissions.' });
162
+ return;
163
+ case 'file_available':
164
+ this.fileInbox?.available(message);
165
+ return;
166
+ case 'file_failed':
167
+ await this.fileInbox?.failed(message.fileId, message.detail);
144
168
  return;
145
169
  case 'heartbeat':
146
170
  return;
@@ -148,7 +172,24 @@ class MessageHandler {
148
172
  const data = message.data;
149
173
  if (data?.success !== true)
150
174
  return;
175
+ if (data.capabilities?.fileTransferV1 === true && !this.fileInbox) {
176
+ try {
177
+ const fileServer = this.config.get('serverUrl');
178
+ const fileNamespace = (0, crypto_1.createHash)('sha256').update((0, DeviceIdentity_1.fileOrigin)(fileServer)).digest('hex');
179
+ this.fileInbox = new FileInbox_1.FileInbox(path_1.default.join(this.config.getConfigDir(), 'attachments', fileNamespace), fileServer, {
180
+ thread: id => id ? this.threadManager.getThread(id) : this.threadManager.getDefaultThread(),
181
+ send: value => this.wsClient.send(value), extract: DocumentExtractor_1.extractDocument,
182
+ }, this.config.get('files.retentionDays'));
183
+ await this.fileInbox.initialize();
184
+ }
185
+ catch {
186
+ await this.fileInbox?.destroy();
187
+ this.fileInbox = undefined;
188
+ console.error('[Files] Local staging is unavailable. File requests will fail without running a model.');
189
+ }
190
+ }
151
191
  this.approvalCardsSupported = data.capabilities?.approvalCards === true;
192
+ this.streamingContextSupported = data.capabilities?.streamingContext === true;
152
193
  this.delegationProgressSupported = data.capabilities?.delegationProgress === true;
153
194
  this.delegationProgressTextSupported = this.delegationProgressSupported
154
195
  && data.capabilities?.delegationProgressText === true;
@@ -196,7 +237,7 @@ class MessageHandler {
196
237
  /**
197
238
  * Handle command message — route to the correct thread executor.
198
239
  */
199
- async handleCommandMessage(message) {
240
+ async handleCommandMessage(message, fileOrder) {
200
241
  const { messageId, content, attachments, workingDirectory, openId, isSlashCommand, threadId } = message;
201
242
  this.currentOpenId = openId;
202
243
  this.messageOpenIds.set(message.messageId, openId);
@@ -221,6 +262,7 @@ class MessageHandler {
221
262
  return;
222
263
  }
223
264
  const executor = this.threadPool.getExecutor(resolvedThreadId);
265
+ this.sendStreamContext(messageId, resolvedThreadId, executor);
224
266
  // Handle /abort for this specific thread (bypasses busy check)
225
267
  if (content?.trim() === '/abort') {
226
268
  await this.handleAbortCommand(messageId, resolvedThreadId, executor);
@@ -291,12 +333,26 @@ class MessageHandler {
291
333
  return;
292
334
  }
293
335
  }
336
+ const ordinaryMessage = !isSlashCommand && !content?.trim().startsWith('/');
337
+ if (ordinaryMessage) {
338
+ try {
339
+ if (message.fileIds?.length && !this.fileInbox)
340
+ throw new Error('File support was not negotiated. Upgrade and enroll this CLI first.');
341
+ if (this.fileInbox) {
342
+ await this.fileInbox.initialize();
343
+ this.fileInbox.claim(messageId, resolvedThreadId, openId, message.fileIds, fileOrder);
344
+ }
345
+ }
346
+ catch (error) {
347
+ this.sendResponse(messageId, resolvedThreadId, { success: false, error: error instanceof Error ? error.message : 'Attachment is unavailable.' });
348
+ return;
349
+ }
350
+ }
294
351
  const threadBusy = this.threadPool.isThreadBusy(resolvedThreadId);
295
352
  const queuedCount = this.threadQueues.get(resolvedThreadId)?.length ?? 0;
296
353
  if (!threadBusy && !this.hasThreadQueueState(resolvedThreadId))
297
354
  this.pausedQueues.delete(resolvedThreadId);
298
355
  const queuePaused = this.pausedQueues.has(resolvedThreadId);
299
- const ordinaryMessage = !isSlashCommand && !content?.trim().startsWith('/');
300
356
  // An idle executor can still have a paused queue. New work must not overtake it.
301
357
  if (threadBusy || (ordinaryMessage && (queuedCount > 0 || queuePaused))) {
302
358
  // Allow pending replace even when busy
@@ -405,6 +461,7 @@ class MessageHandler {
405
461
  * Handle /abort command for a specific thread executor
406
462
  */
407
463
  async handleAbortCommand(messageId, threadId, executor) {
464
+ this.fileInbox?.cancelThread(threadId);
408
465
  const existingAbort = this.abortOperations.get(threadId);
409
466
  if (existingAbort) {
410
467
  await existingAbort;
@@ -490,6 +547,12 @@ class MessageHandler {
490
547
  */
491
548
  async handleBuiltInCommand(messageId, threadId, content, executor) {
492
549
  const trimmed = content.trim();
550
+ if (trimmed === '/files' || trimmed === '/files clear') {
551
+ this.sendResponse(messageId, threadId, { success: true, output: this.fileInbox
552
+ ? await this.fileInbox.list(threadId, this.getMessageOpenId(messageId), trimmed === '/files clear')
553
+ : 'File reception is unavailable. Upgrade both peers, configure Router TLS, run remote-cli files enable locally, approve its binding code, and reconnect.' });
554
+ return true;
555
+ }
493
556
  if (trimmed === '/delegation' || trimmed.startsWith('/delegation ')) {
494
557
  const argumentsList = trimmed.slice('/delegation'.length).trim().split(/\s+/).filter(Boolean);
495
558
  const action = argumentsList[0];
@@ -917,23 +980,30 @@ You can also use natural language commands to control Claude Code CLI.`,
917
980
  });
918
981
  setTimeout(() => {
919
982
  const pending = this.pendingQueueConfirmations.get(id);
920
- if (pending && pending.info.expiresAt <= Date.now())
983
+ if (pending && pending.info.expiresAt <= Date.now()) {
984
+ this.fileInbox?.release(pending.command.messageId);
921
985
  this.pendingQueueConfirmations.delete(id);
986
+ }
922
987
  }, QUEUE_CONFIRMATION_TTL_MS).unref?.();
923
988
  return info;
924
989
  }
925
990
  removeExpiredQueueConfirmations() {
926
991
  const now = Date.now();
927
992
  for (const [id, pending] of this.pendingQueueConfirmations) {
928
- if (pending.info.expiresAt <= now)
993
+ if (pending.info.expiresAt <= now) {
994
+ this.fileInbox?.release(pending.command.messageId);
929
995
  this.pendingQueueConfirmations.delete(id);
996
+ }
930
997
  }
931
998
  }
932
999
  clearThreadQueue(threadId) {
933
1000
  let cleared = this.threadQueues.get(threadId)?.length ?? 0;
1001
+ for (const command of this.threadQueues.get(threadId) ?? [])
1002
+ this.fileInbox?.release(command.messageId);
934
1003
  this.threadQueues.delete(threadId);
935
1004
  for (const [id, pending] of this.pendingQueueConfirmations) {
936
1005
  if (pending.info.threadId === threadId) {
1006
+ this.fileInbox?.release(pending.command.messageId);
937
1007
  this.pendingQueueConfirmations.delete(id);
938
1008
  cleared += 1;
939
1009
  }
@@ -960,6 +1030,8 @@ You can also use natural language commands to control Claude Code CLI.`,
960
1030
  if (!pending || pending.info.threadId !== threadId || pending.info.expiresAt <= Date.now()
961
1031
  || pending.info.backend !== this.threadPool.getBackendKey(threadId) || pending.info.cwd !== currentCwd
962
1032
  || (pending.command.openId && pending.command.openId !== this.getMessageOpenId(messageId))) {
1033
+ if (pending)
1034
+ this.fileInbox?.release(pending.command.messageId);
963
1035
  this.pendingQueueConfirmations.delete(id);
964
1036
  this.sendResponse(messageId, threadId, { success: false, error: 'Queue confirmation is no longer valid. The message was not queued.' });
965
1037
  return;
@@ -967,9 +1039,11 @@ You can also use natural language commands to control Claude Code CLI.`,
967
1039
  this.pendingQueueConfirmations.delete(id);
968
1040
  const queue = this.threadQueues.get(threadId) ?? [];
969
1041
  if (queue.length >= MAX_THREAD_QUEUE_LENGTH) {
1042
+ this.fileInbox?.release(pending.command.messageId);
970
1043
  this.sendResponse(messageId, threadId, { success: false, error: `This thread queue is full (${MAX_THREAD_QUEUE_LENGTH} messages).` });
971
1044
  return;
972
1045
  }
1046
+ this.fileInbox?.moveClaim(pending.command.messageId, parts[3] || pending.command.messageId);
973
1047
  queue.push({ ...pending.command, messageId: parts[3] || pending.command.messageId });
974
1048
  this.threadQueues.set(threadId, queue);
975
1049
  console.log(`[MessageHandler] Queued message ${parts[3] || pending.command.messageId} in thread ${threadId}: position=${queue.length}, paused=${this.pausedQueues.has(threadId)}`);
@@ -983,6 +1057,7 @@ You can also use natural language commands to control Claude Code CLI.`,
983
1057
  if (action === 'cancel' && id) {
984
1058
  const pending = this.pendingQueueConfirmations.get(id);
985
1059
  if (pending?.info.threadId === threadId) {
1060
+ this.fileInbox?.release(pending.command.messageId);
986
1061
  this.pendingQueueConfirmations.delete(id);
987
1062
  this.sendResponse(messageId, threadId, { success: true, output: '❌ Message was not queued.' });
988
1063
  }
@@ -1276,6 +1351,7 @@ You can also use natural language commands to control Claude Code CLI.`,
1276
1351
  await this.delegation.store.deleteThread(target.id);
1277
1352
  await this.threadPool.destroyThread(target.id);
1278
1353
  await this.threadManager.deleteThread(target.id);
1354
+ await this.fileInbox?.deleteThread(target.id);
1279
1355
  this.sendResponse(messageId, callerThreadId, {
1280
1356
  success: true,
1281
1357
  output: `✅ Thread "${name}" deleted.`,
@@ -1543,8 +1619,8 @@ You can also use natural language commands to control Claude Code CLI.`,
1543
1619
  ]);
1544
1620
  async listLocalSkills(cwd, backend) {
1545
1621
  const roots = backend === 'codex'
1546
- ? [(0, path_1.join)(cwd, '.agents', 'skills'), (0, path_1.join)((0, os_1.homedir)(), '.codex', 'skills'), (0, path_1.join)((0, os_1.homedir)(), '.codex', 'skills', '.system')]
1547
- : [(0, path_1.join)(cwd, '.claude', 'skills'), (0, path_1.join)((0, os_1.homedir)(), '.claude', 'skills')];
1622
+ ? [(0, path_2.join)(cwd, '.agents', 'skills'), (0, path_2.join)((0, os_1.homedir)(), '.codex', 'skills'), (0, path_2.join)((0, os_1.homedir)(), '.codex', 'skills', '.system')]
1623
+ : [(0, path_2.join)(cwd, '.claude', 'skills'), (0, path_2.join)((0, os_1.homedir)(), '.claude', 'skills')];
1548
1624
  const skills = [];
1549
1625
  const seen = new Set();
1550
1626
  for (const root of roots) {
@@ -1558,7 +1634,7 @@ You can also use natural language commands to control Claude Code CLI.`,
1558
1634
  for (const entry of entries) {
1559
1635
  if (!entry.isDirectory() || seen.has(entry.name))
1560
1636
  continue;
1561
- const skillPath = (0, path_1.join)(root, entry.name, 'SKILL.md');
1637
+ const skillPath = (0, path_2.join)(root, entry.name, 'SKILL.md');
1562
1638
  try {
1563
1639
  const content = await (0, promises_1.readFile)(skillPath, 'utf8');
1564
1640
  const description = content.match(/^description:\s*(.+)$/mi)?.[1]?.trim();
@@ -1705,6 +1781,7 @@ You can also use natural language commands to control Claude Code CLI.`,
1705
1781
  * Execute AI command on a specific executor
1706
1782
  */
1707
1783
  async executeCommand(messageId, threadId, content, executor, attachments, fromQueue = false) {
1784
+ this.sendStreamContext(messageId, threadId, executor);
1708
1785
  let delegationScope;
1709
1786
  let delegationBridge;
1710
1787
  const complete = async (success, error) => {
@@ -1736,6 +1813,12 @@ You can also use natural language commands to control Claude Code CLI.`,
1736
1813
  return success;
1737
1814
  };
1738
1815
  try {
1816
+ if (this.fileInbox?.hasClaim(messageId)) {
1817
+ this.sendStreamChunk(messageId, threadId, '📎 Waiting for attached files to be ready...\n');
1818
+ content += await this.fileInbox.prepare(messageId);
1819
+ if (this.isDestroyed || this.abortOperations.has(threadId))
1820
+ throw new Error('Attachment task cancelled.');
1821
+ }
1739
1822
  const openId = this.getMessageOpenId(messageId);
1740
1823
  const thread = this.threadManager.getThread(threadId);
1741
1824
  if (thread?.delegation && executor.configureDelegation) {
@@ -2057,6 +2140,24 @@ You can also use natural language commands to control Claude Code CLI.`,
2057
2140
  error: 'Approval could not be applied. Check the task output or choose another action.', timestamp: Date.now() });
2058
2141
  }
2059
2142
  }
2143
+ /** Publish routing metadata before backend startup or the first output token. */
2144
+ sendStreamContext(messageId, threadId, executor) {
2145
+ if (!this.streamingContextSupported)
2146
+ return;
2147
+ const thread = this.threadManager.getThread(threadId);
2148
+ const openId = this.getMessageOpenId(messageId);
2149
+ if (!thread || !openId)
2150
+ return;
2151
+ try {
2152
+ this.wsClient.send({
2153
+ type: 'stream_context', messageId, openId, threadId,
2154
+ threadName: thread.name, cwd: executor.getCurrentWorkingDirectory(), timestamp: Date.now(),
2155
+ });
2156
+ }
2157
+ catch (error) {
2158
+ console.error('Failed to send stream context:', error);
2159
+ }
2160
+ }
2060
2161
  sendStreamChunk(messageId, threadId, chunk) {
2061
2162
  try {
2062
2163
  this.wsClient.send({
@@ -2186,6 +2287,8 @@ You can also use natural language commands to control Claude Code CLI.`,
2186
2287
  }
2187
2288
  }
2188
2289
  sendResponse(messageId, threadId, result) {
2290
+ if (!result.queueConfirmation)
2291
+ this.fileInbox?.release(messageId, !result.success);
2189
2292
  try {
2190
2293
  // Resolve CWD from thread executor if possible
2191
2294
  let cwd;
@@ -2348,6 +2451,7 @@ You can also use natural language commands to control Claude Code CLI.`,
2348
2451
  */
2349
2452
  async destroy() {
2350
2453
  this.isDestroyed = true;
2454
+ await this.fileInbox?.destroy();
2351
2455
  this.notificationAdapter.unregister();
2352
2456
  try {
2353
2457
  await this.delegation.destroy();