@yu_robotics/remote-cli 1.6.53 → 1.6.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -8
- package/dist/client/MessageHandler.d.ts +7 -0
- package/dist/client/MessageHandler.d.ts.map +1 -1
- package/dist/client/MessageHandler.js +96 -3
- package/dist/client/MessageHandler.js.map +1 -1
- package/dist/client/TaskRecovery.d.ts +7 -3
- package/dist/client/TaskRecovery.d.ts.map +1 -1
- package/dist/client/TaskRecovery.js +80 -27
- package/dist/client/TaskRecovery.js.map +1 -1
- package/dist/client/WebSocketClient.d.ts.map +1 -1
- package/dist/client/WebSocketClient.js +2 -10
- package/dist/client/WebSocketClient.js.map +1 -1
- package/dist/commands/start.d.ts +3 -11
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +22 -99
- package/dist/commands/start.js.map +1 -1
- package/dist/executor/ClaudePersistentExecutor.d.ts +40 -2
- package/dist/executor/ClaudePersistentExecutor.d.ts.map +1 -1
- package/dist/executor/ClaudePersistentExecutor.js +358 -9
- package/dist/executor/ClaudePersistentExecutor.js.map +1 -1
- package/dist/executor/CodexAppServerExecutor.d.ts +13 -0
- package/dist/executor/CodexAppServerExecutor.d.ts.map +1 -1
- package/dist/executor/CodexAppServerExecutor.js +208 -5
- package/dist/executor/CodexAppServerExecutor.js.map +1 -1
- package/dist/executor/CodexSandbox.d.ts +23 -0
- package/dist/executor/CodexSandbox.d.ts.map +1 -0
- package/dist/executor/CodexSandbox.js +211 -0
- package/dist/executor/CodexSandbox.js.map +1 -0
- package/dist/executor/IExecutor.d.ts +5 -1
- package/dist/executor/IExecutor.d.ts.map +1 -1
- package/dist/executor/claude/ClaudeFilePolicy.d.ts +7 -0
- package/dist/executor/claude/ClaudeFilePolicy.d.ts.map +1 -0
- package/dist/executor/claude/ClaudeFilePolicy.js +88 -0
- package/dist/executor/claude/ClaudeFilePolicy.js.map +1 -0
- package/dist/executor/claude/ClaudeSandbox.d.ts +32 -0
- package/dist/executor/claude/ClaudeSandbox.d.ts.map +1 -0
- package/dist/executor/claude/ClaudeSandbox.js +245 -0
- package/dist/executor/claude/ClaudeSandbox.js.map +1 -0
- package/dist/executor/claude/approvalMcpServer.d.ts +2 -0
- package/dist/executor/claude/approvalMcpServer.d.ts.map +1 -0
- package/dist/executor/claude/approvalMcpServer.js +160 -0
- package/dist/executor/claude/approvalMcpServer.js.map +1 -0
- package/dist/executor/claude/filePolicyHook.d.ts +2 -0
- package/dist/executor/claude/filePolicyHook.d.ts.map +1 -0
- package/dist/executor/claude/filePolicyHook.js +63 -0
- package/dist/executor/claude/filePolicyHook.js.map +1 -0
- package/dist/executor/index.d.ts.map +1 -1
- package/dist/executor/index.js +4 -3
- package/dist/executor/index.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/thread/ThreadManager.d.ts.map +1 -1
- package/dist/thread/ThreadManager.js +2 -0
- package/dist/thread/ThreadManager.js.map +1 -1
- package/dist/types/config.d.ts +32 -2
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js.map +1 -1
- package/dist/types/index.d.ts +39 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/update/AutomaticUpdater.d.ts +7 -1
- package/dist/update/AutomaticUpdater.d.ts.map +1 -1
- package/dist/update/AutomaticUpdater.js +25 -2
- package/dist/update/AutomaticUpdater.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,15 +5,16 @@ Remote control Claude Code, AGY CLI, Codex CLI, OpenCode CLI, Kimi Code CLI, ZCo
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Remote Control**: Control your local development environment from anywhere via mobile
|
|
8
|
-
- **
|
|
8
|
+
- **Access Controls**: Working-directory allowlist and user-device binding
|
|
9
9
|
- **Mobile-Optimized**: Simplified commands and rich text formatting for Feishu
|
|
10
10
|
- **Readable Code Changes**: Edit operations show collapsible, line-aware diff previews inside the existing progress card
|
|
11
11
|
- **Multi-Backend Support**: Supports Claude Code, AGY CLI (Antigravity), Codex CLI (OpenAI), OpenCode CLI, Kimi Code CLI, ZCode, and Pi
|
|
12
|
-
- **Image Input and Output**: Forward
|
|
12
|
+
- **Image Input and Output**: Forward Feishu images to supported backends and return Codex native image output. All backends can return local images through file paths or Markdown image links; files must be inside an allowed directory and no larger than 2 MiB. AGY does not accept image input.
|
|
13
13
|
- **Multi-session (Threads)**: Create independent chat threads to parallelize tasks
|
|
14
14
|
- **Remote Machine Management**: Control remote servers or Docker via SSH
|
|
15
15
|
- **Persistent Process**: Long-running AI process with bidirectional streaming
|
|
16
|
-
- **
|
|
16
|
+
- **Automatic Updates**: Install the newer Router's exact version when idle. Manual clients keep running and use the update on their next start; supervised non-interactive clients exit for an immediate restart.
|
|
17
|
+
- **Task Recovery**: With a compatible Router, running tasks resume output on a usable surviving card or a new card with an output-gap notice. Recovery retries use bounded backoff; disconnected output is not buffered or replayed, even after recovery is paused.
|
|
17
18
|
|
|
18
19
|
## Prerequisites
|
|
19
20
|
|
|
@@ -63,6 +64,8 @@ remote-cli start
|
|
|
63
64
|
Help me fix TypeScript errors in ~/projects/my-app
|
|
64
65
|
```
|
|
65
66
|
|
|
67
|
+
Manual starts automatically install updates when tasks and queues are idle, without exiting the running client. Updates take effect on the next start. Use `remote-cli service install` for automatic startup and supervised restarts after updates. If the Router rejects the running protocol, restart the manual client after installation to reconnect. See [Automatic Client Startup](../../README.md#automatic-client-startup) for update behavior and recovery.
|
|
68
|
+
|
|
66
69
|
## Commands
|
|
67
70
|
|
|
68
71
|
| Command | Description |
|
|
@@ -98,12 +101,13 @@ Linux users upgrading from version 1.6.23 or earlier should run `remote-cli serv
|
|
|
98
101
|
| `/compact` | Compress history to save tokens |
|
|
99
102
|
| `/cd <dir>` | Change working directory for this thread |
|
|
100
103
|
| `/model [name]` | List models for the active backend or set this thread's model |
|
|
101
|
-
| `/effort [auto|level]` | Show or set Codex/AGY/OpenCode/Kimi reasoning effort |
|
|
104
|
+
| `/effort [auto|level]` | Show or set Codex/AGY/OpenCode/Kimi/ZCode/Pi reasoning effort |
|
|
105
|
+
| `/sandbox [on/off/read-only/default]` | Configure the current Codex or Claude Code thread sandbox, directory grants, and networking |
|
|
102
106
|
| `/backend` | List backends and show the current thread's effective backend |
|
|
103
107
|
| `/backend <index>` | Switch all threads and clear per-thread backend overrides |
|
|
104
108
|
| `/backend <index> @` | Switch only the current thread |
|
|
105
109
|
| `/backend default @` | Clear the current thread override and follow the global backend |
|
|
106
|
-
| `/bind
|
|
110
|
+
| `/bind <code>` | Bind a new device |
|
|
107
111
|
| `/unbind` | Unbind all devices |
|
|
108
112
|
| `/device` | List and switch between bound devices |
|
|
109
113
|
|
|
@@ -153,14 +157,20 @@ When diagnosing a task, check `/status`, then `/context`, then `/queue`. Use `/a
|
|
|
153
157
|
|
|
154
158
|
## Security
|
|
155
159
|
|
|
156
|
-
|
|
157
|
-
|
|
160
|
+
Codex permission requests use interactive cards when both CLI and Router support them. Choose Allow, Deny, or (for explicit directory grants) Allow and remember directory. Buttons stay bound to the original request and are invalidated when it finishes. Pending approvals get new cards after reconnecting; older routers and card delivery failures retain text replies.
|
|
161
|
+
|
|
162
|
+
Codex supports an opt-in native sandbox through `/sandbox on`, `/sandbox read-only`, and `/sandbox off`. Use `/sandbox allow <directory>` to save extra writable directories for the current thread, `remove <directory>` to revoke them, and `network on|off` to control networking. The workspace-write default allows cross-project reads, networking, and writes to the current project, dedicated temporary/download directories, and common package caches. Grants survive conversation resets and backend switches. See [Codex sandbox configuration](../../README.md#optional-codex-sandbox) for boundaries, approval choices, and configuration defaults. Other backends retain their existing behavior.
|
|
163
|
+
|
|
164
|
+
Claude Code also supports opt-in `/sandbox` policies. Workspace-write mode automatically permits ordinary file edits inside the workspace and explicitly authorized writable directories through a process-scoped native hook. Real file destinations are checked, and native deny/ask rules remain effective. Outside writes, protected files, and all read-only-mode file modifications still require approval. Startup verifies the hook before sending a task and fails if native hook restrictions prevent it from running. Missing Linux dependencies (`bubblewrap` and `socat` on `PATH`) stop execution instead of falling back to full access. Permission cards use text replies (`yes` / `no`, or a request ID when several approvals are pending) if cards are unavailable. Pending approvals expire when the task ends or the process stops. Sandboxed commands can access network domains without per-domain approval by default; `/sandbox network on` restores this behavior while preserving filesystem isolation, native domain deny rules, and administrator-managed restrictions. Previously saved network-off settings remain disabled until explicitly changed. `/sandbox network off` overrides saved domain allowlists for sandboxed commands; it does not block Claude API traffic or in-process web tools. See [Claude sandbox configuration](../../README.md#optional-claude-code-sandbox).
|
|
165
|
+
|
|
166
|
+
- **Directory whitelisting**: Controls which working directory a thread can select; it is not a process sandbox
|
|
167
|
+
- **Backend permissions**: AI processes inherit the operating-system user's permissions. remote-cli does not install a global Claude security hook or automatically filter backend shell commands.
|
|
158
168
|
- **Device authentication**: Each device has a unique hardware-based ID
|
|
159
169
|
- **Binding codes**: Expire after 5 minutes
|
|
160
170
|
|
|
161
171
|
## Documentation
|
|
162
172
|
|
|
163
|
-
For full documentation including router server deployment, see the [project README](https://github.com/
|
|
173
|
+
For full documentation including router server deployment, see the [project README](https://github.com/Yu-Robotics/remote-cli#readme).
|
|
164
174
|
|
|
165
175
|
## License
|
|
166
176
|
|
|
@@ -36,6 +36,8 @@ export declare class MessageHandler {
|
|
|
36
36
|
private readonly activeThreadOperations;
|
|
37
37
|
private readonly abortOperations;
|
|
38
38
|
private automaticUpdateInProgress;
|
|
39
|
+
private approvalCardsSupported;
|
|
40
|
+
private readonly pendingApprovalCards;
|
|
39
41
|
constructor(wsClient: WebSocketClient, threadPool: ThreadExecutorPool, threadManager: ThreadManager, directoryGuard: DirectoryGuard, config: ConfigManager);
|
|
40
42
|
/**
|
|
41
43
|
* Handle message (supports new IncomingMessage format)
|
|
@@ -153,6 +155,11 @@ export declare class MessageHandler {
|
|
|
153
155
|
private isUnavailableCodexModelError;
|
|
154
156
|
private getMessageOpenId;
|
|
155
157
|
private trackTask;
|
|
158
|
+
private forwardApproval;
|
|
159
|
+
private sendApprovalMessage;
|
|
160
|
+
private showApprovalFallback;
|
|
161
|
+
private resolveApprovalCard;
|
|
162
|
+
private handleApprovalResponse;
|
|
156
163
|
private sendStreamChunk;
|
|
157
164
|
private sendToolUse;
|
|
158
165
|
private sendToolResult;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../../src/client/MessageHandler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../../src/client/MessageHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -87,6 +87,8 @@ class MessageHandler {
|
|
|
87
87
|
activeThreadOperations = new Map();
|
|
88
88
|
abortOperations = new Map();
|
|
89
89
|
automaticUpdateInProgress = false;
|
|
90
|
+
approvalCardsSupported = false;
|
|
91
|
+
pendingApprovalCards = new Map();
|
|
90
92
|
constructor(wsClient, threadPool, threadManager, directoryGuard, config) {
|
|
91
93
|
this.wsClient = wsClient;
|
|
92
94
|
this.threadPool = threadPool;
|
|
@@ -121,7 +123,24 @@ class MessageHandler {
|
|
|
121
123
|
return;
|
|
122
124
|
case 'heartbeat':
|
|
123
125
|
return;
|
|
124
|
-
case 'binding_confirm':
|
|
126
|
+
case 'binding_confirm': {
|
|
127
|
+
const data = message.data;
|
|
128
|
+
if (data?.success !== true)
|
|
129
|
+
return;
|
|
130
|
+
this.approvalCardsSupported = data.capabilities?.approvalCards === true;
|
|
131
|
+
for (const pending of this.pendingApprovalCards.values()) {
|
|
132
|
+
if (this.approvalCardsSupported)
|
|
133
|
+
this.sendApprovalMessage(pending.request);
|
|
134
|
+
else
|
|
135
|
+
this.showApprovalFallback(pending.request.messageId);
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
case 'approval_response':
|
|
140
|
+
this.handleApprovalResponse(message);
|
|
141
|
+
return;
|
|
142
|
+
case 'approval_unavailable':
|
|
143
|
+
this.showApprovalFallback(message.messageId);
|
|
125
144
|
return;
|
|
126
145
|
default:
|
|
127
146
|
this.sendResponse(message.messageId, undefined, {
|
|
@@ -233,7 +252,9 @@ class MessageHandler {
|
|
|
233
252
|
this.sendResponse(messageId, resolvedThreadId, {
|
|
234
253
|
success: sent,
|
|
235
254
|
output: sent ? `✅ Sent: "${input}"` : undefined,
|
|
236
|
-
error: sent ? undefined :
|
|
255
|
+
error: sent ? undefined : ex.isWaitingInput()
|
|
256
|
+
? '❌ Input was not accepted. Check the pending request and reply again.'
|
|
257
|
+
: '❌ Failed to send input - executor is no longer waiting',
|
|
237
258
|
});
|
|
238
259
|
}
|
|
239
260
|
else {
|
|
@@ -286,7 +307,7 @@ class MessageHandler {
|
|
|
286
307
|
}
|
|
287
308
|
return;
|
|
288
309
|
}
|
|
289
|
-
const queueSensitiveCommand = /^\/(?:clear|new|compact|cd|model|effort)(?:\s|$)/.test(content?.trim() ?? '')
|
|
310
|
+
const queueSensitiveCommand = /^\/(?:clear|new|compact|cd|model|effort|sandbox)(?:\s|$)/.test(content?.trim() ?? '')
|
|
290
311
|
|| /^\/thread\s+delete(?:\s|$)/.test(content?.trim() ?? '');
|
|
291
312
|
if (queueSensitiveCommand && this.hasThreadQueueState(resolvedThreadId)) {
|
|
292
313
|
this.sendResponse(messageId, resolvedThreadId, {
|
|
@@ -432,6 +453,18 @@ class MessageHandler {
|
|
|
432
453
|
*/
|
|
433
454
|
async handleBuiltInCommand(messageId, threadId, content, executor) {
|
|
434
455
|
const trimmed = content.trim();
|
|
456
|
+
if (trimmed === '/sandbox' || trimmed.startsWith('/sandbox ')) {
|
|
457
|
+
if (!('getSandboxStatus' in executor) || !('configureSandbox' in executor)) {
|
|
458
|
+
this.sendResponse(messageId, threadId, { success: false, error: '/sandbox is currently supported by the Codex and Claude Code backends.' });
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
const control = executor;
|
|
462
|
+
const result = trimmed === '/sandbox'
|
|
463
|
+
? { success: true, output: control.getSandboxStatus() }
|
|
464
|
+
: await control.configureSandbox(trimmed.slice('/sandbox'.length).trim());
|
|
465
|
+
this.sendResponse(messageId, threadId, result);
|
|
466
|
+
return true;
|
|
467
|
+
}
|
|
435
468
|
if (trimmed === '/status') {
|
|
436
469
|
const cwd = executor.getCurrentWorkingDirectory();
|
|
437
470
|
const allowedDirs = this.directoryGuard.getAllowedDirectories();
|
|
@@ -521,6 +554,7 @@ Use /compact to reduce conversation context or /clear to start a fresh context.`
|
|
|
521
554
|
- /cd <directory> - Change working directory for this thread
|
|
522
555
|
- /model [name] - Show models for the active backend, or set this thread's model
|
|
523
556
|
- /effort [auto|level] - Show or set effort for Codex/AGY/OpenCode/Kimi/ZCode/Pi (Claude Code is unsupported)
|
|
557
|
+
- /sandbox [on|off|read-only|default|allow <directory>|remove <directory>|network on|off] - Configure this thread's sandbox (Codex, Claude Code)
|
|
524
558
|
- /backend - List backends and show the current thread's effective backend
|
|
525
559
|
- /backend <index> - Switch all threads and clear per-thread backend overrides
|
|
526
560
|
- /backend <index> @ - Switch only the current thread
|
|
@@ -1554,6 +1588,8 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
1554
1588
|
pendingLocalImageEmissions.add(pending);
|
|
1555
1589
|
};
|
|
1556
1590
|
const executeOptions = {
|
|
1591
|
+
onApprovalRequest: (approval) => this.forwardApproval(approval, executor, messageId, threadId),
|
|
1592
|
+
onApprovalResolved: (requestId, status) => this.resolveApprovalCard(requestId, status),
|
|
1557
1593
|
onTaskNotification,
|
|
1558
1594
|
onStream: (chunk) => {
|
|
1559
1595
|
streamedOutput += chunk;
|
|
@@ -1646,6 +1682,62 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
1646
1682
|
backend: this.threadPool.getBackendKey(threadId),
|
|
1647
1683
|
cwd: this.threadPool.getExecutor(threadId).getCurrentWorkingDirectory(), preview: content });
|
|
1648
1684
|
}
|
|
1685
|
+
forwardApproval(approval, executor, taskMessageId, threadId) {
|
|
1686
|
+
const openId = this.getMessageOpenId(taskMessageId);
|
|
1687
|
+
if (!openId)
|
|
1688
|
+
return false;
|
|
1689
|
+
const request = { type: 'approval_request', messageId: approval.requestId,
|
|
1690
|
+
taskMessageId, openId, threadId, threadName: this.threadManager.getThread(threadId)?.name ?? threadId,
|
|
1691
|
+
cwd: executor.getCurrentWorkingDirectory(), approval, timestamp: Date.now() };
|
|
1692
|
+
this.pendingApprovalCards.set(approval.requestId, { request, executor });
|
|
1693
|
+
if (this.approvalCardsSupported)
|
|
1694
|
+
this.sendApprovalMessage(request);
|
|
1695
|
+
return this.approvalCardsSupported;
|
|
1696
|
+
}
|
|
1697
|
+
sendApprovalMessage(message) {
|
|
1698
|
+
try {
|
|
1699
|
+
this.wsClient.send(message);
|
|
1700
|
+
}
|
|
1701
|
+
catch { /* Pending approvals are resent after registration. */ }
|
|
1702
|
+
}
|
|
1703
|
+
showApprovalFallback(requestId) {
|
|
1704
|
+
const pending = this.pendingApprovalCards.get(requestId);
|
|
1705
|
+
if (!pending)
|
|
1706
|
+
return;
|
|
1707
|
+
const { request } = pending;
|
|
1708
|
+
this.sendStreamChunk(request.taskMessageId, request.threadId, `\nApproval required: ${request.approval.description}\nReply yes, no${request.approval.canRemember ? ', or remember to save these directories' : ''}.\n`);
|
|
1709
|
+
}
|
|
1710
|
+
resolveApprovalCard(requestId, status) {
|
|
1711
|
+
const pending = this.pendingApprovalCards.get(requestId);
|
|
1712
|
+
if (!pending)
|
|
1713
|
+
return;
|
|
1714
|
+
this.pendingApprovalCards.delete(requestId);
|
|
1715
|
+
if (this.approvalCardsSupported)
|
|
1716
|
+
this.sendApprovalMessage({ type: 'approval_resolved', messageId: requestId,
|
|
1717
|
+
openId: pending.request.openId, threadId: pending.request.threadId, status, timestamp: Date.now() });
|
|
1718
|
+
}
|
|
1719
|
+
handleApprovalResponse(message) {
|
|
1720
|
+
const pending = this.pendingApprovalCards.get(message.messageId);
|
|
1721
|
+
if (!pending || pending.request.threadId !== message.threadId || pending.request.openId !== message.openId
|
|
1722
|
+
|| pending.request.taskMessageId !== message.taskMessageId) {
|
|
1723
|
+
this.sendApprovalMessage({ type: 'approval_resolved', messageId: message.messageId,
|
|
1724
|
+
openId: message.openId, threadId: message.threadId, status: 'expired', timestamp: Date.now() });
|
|
1725
|
+
return;
|
|
1726
|
+
}
|
|
1727
|
+
let accepted = false;
|
|
1728
|
+
try {
|
|
1729
|
+
accepted = pending.executor.respondToApproval?.(message.messageId, message.action) === true;
|
|
1730
|
+
}
|
|
1731
|
+
catch { /* Report failure without approving another request. */ }
|
|
1732
|
+
if (accepted) {
|
|
1733
|
+
this.resolveApprovalCard(message.messageId, message.action === 'remember' ? 'remembered' : message.action === 'deny' ? 'denied' : 'approved');
|
|
1734
|
+
}
|
|
1735
|
+
else if (this.pendingApprovalCards.has(message.messageId)) {
|
|
1736
|
+
this.sendApprovalMessage({ type: 'approval_resolved', messageId: message.messageId,
|
|
1737
|
+
openId: message.openId, threadId: message.threadId, status: 'pending',
|
|
1738
|
+
error: 'Approval could not be applied. Check the task output or choose another action.', timestamp: Date.now() });
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1649
1741
|
sendStreamChunk(messageId, threadId, chunk) {
|
|
1650
1742
|
try {
|
|
1651
1743
|
this.wsClient.send({
|
|
@@ -1922,6 +2014,7 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
1922
2014
|
this.notificationAdapter.unregister();
|
|
1923
2015
|
try {
|
|
1924
2016
|
await this.threadPool.destroyAll({ deleteData: false });
|
|
2017
|
+
this.pendingApprovalCards.clear();
|
|
1925
2018
|
}
|
|
1926
2019
|
catch (err) {
|
|
1927
2020
|
console.error('Error destroying thread executors:', err);
|