claude-remote-cli 1.7.0 → 1.7.2
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 +30 -16
- package/dist/server/index.js +7 -2
- package/dist/server/sessions.js +2 -2
- package/package.json +1 -1
- package/public/app.js +54 -0
package/README.md
CHANGED
|
@@ -17,14 +17,14 @@ claude-remote-cli
|
|
|
17
17
|
git clone https://github.com/donovan-yohan/claude-remote-cli.git
|
|
18
18
|
cd claude-remote-cli
|
|
19
19
|
npm install
|
|
20
|
-
|
|
20
|
+
npm start
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
On first launch you'll be prompted to set a PIN. Then open `http://localhost:3456` in your browser.
|
|
24
24
|
|
|
25
25
|
## Prerequisites
|
|
26
26
|
|
|
27
|
-
- **Node.js
|
|
27
|
+
- **Node.js 24+**
|
|
28
28
|
- **Claude Code CLI** installed and available in your PATH (or configure `claudeCommand` in config)
|
|
29
29
|
|
|
30
30
|
## Platform Support
|
|
@@ -38,6 +38,7 @@ Usage: claude-remote-cli [options]
|
|
|
38
38
|
claude-remote-cli <command>
|
|
39
39
|
|
|
40
40
|
Commands:
|
|
41
|
+
update Update to the latest version from npm
|
|
41
42
|
install Install as a background service (survives reboot)
|
|
42
43
|
uninstall Stop and remove the background service
|
|
43
44
|
status Show whether the service is running
|
|
@@ -106,32 +107,45 @@ The PIN hash is stored in config under `pinHash`. To reset:
|
|
|
106
107
|
|
|
107
108
|
- **PIN-protected access** with rate limiting
|
|
108
109
|
- **Worktree isolation** — each session runs in its own Claude Code `--worktree`
|
|
109
|
-
- **Resume sessions** — click inactive worktrees to reconnect
|
|
110
|
+
- **Resume sessions** — click inactive worktrees to reconnect with `--continue`
|
|
111
|
+
- **Persistent session names** — display names and timestamps survive server restarts
|
|
112
|
+
- **Clipboard image paste** — paste screenshots directly into remote terminal sessions (macOS clipboard + xclip on Linux)
|
|
113
|
+
- **Yolo mode** — skip permission prompts with `--dangerously-skip-permissions` (per-session checkbox or context menu)
|
|
114
|
+
- **Worktree cleanup** — delete inactive worktrees from the context menu (removes worktree, prunes refs, deletes branch)
|
|
110
115
|
- **Sidebar filters** — filter by root directory, repo, or text search
|
|
111
|
-
- **Inline rename** — rename sessions with the pencil icon
|
|
116
|
+
- **Inline rename** — rename sessions with the pencil icon
|
|
112
117
|
- **Scrollback buffer** — reconnect to a session and see prior output
|
|
113
|
-
- **Touch toolbar** — mobile-friendly buttons for special keys (
|
|
118
|
+
- **Touch toolbar** — mobile-friendly buttons for special keys (hidden on desktop)
|
|
114
119
|
- **Responsive layout** — works on desktop and mobile with slide-out sidebar
|
|
115
120
|
- **Real-time updates** — worktree changes on disk are pushed to the browser instantly via WebSocket
|
|
121
|
+
- **Update notifications** — toast notification when a new version is available, with one-click update
|
|
122
|
+
- **CLI self-update** — `claude-remote-cli update` to update from npm
|
|
116
123
|
|
|
117
124
|
## Architecture
|
|
118
125
|
|
|
126
|
+
TypeScript + ESM backend compiled to `dist/`. Vanilla JS frontend (no build step).
|
|
127
|
+
|
|
119
128
|
```
|
|
120
129
|
claude-remote-cli/
|
|
121
130
|
├── bin/
|
|
122
|
-
│ └── claude-remote-cli.
|
|
131
|
+
│ └── claude-remote-cli.ts # CLI entry point
|
|
123
132
|
├── server/
|
|
124
|
-
│ ├── index.
|
|
125
|
-
│ ├── sessions.
|
|
126
|
-
│ ├── ws.
|
|
127
|
-
│ ├── watcher.
|
|
128
|
-
│ ├── auth.
|
|
129
|
-
│
|
|
133
|
+
│ ├── index.ts # Express server, REST API routes
|
|
134
|
+
│ ├── sessions.ts # PTY session manager (node-pty)
|
|
135
|
+
│ ├── ws.ts # WebSocket relay (PTY ↔ browser)
|
|
136
|
+
│ ├── watcher.ts # File watcher for .claude/worktrees/ changes
|
|
137
|
+
│ ├── auth.ts # PIN hashing, verification, rate limiting
|
|
138
|
+
│ ├── config.ts # Config loading/saving, worktree metadata
|
|
139
|
+
│ ├── clipboard.ts # System clipboard operations (image paste)
|
|
140
|
+
│ ├── service.ts # Background service management (launchd/systemd)
|
|
141
|
+
│ └── types.ts # Shared TypeScript interfaces
|
|
130
142
|
├── public/
|
|
131
|
-
│ ├── index.html
|
|
132
|
-
│ ├── app.js
|
|
133
|
-
│ ├── style.css
|
|
134
|
-
│ └── vendor/
|
|
143
|
+
│ ├── index.html # Single-page app
|
|
144
|
+
│ ├── app.js # Frontend logic (ES5, no build step)
|
|
145
|
+
│ ├── style.css # Styles (dark theme)
|
|
146
|
+
│ └── vendor/ # Self-hosted xterm.js + addon-fit
|
|
147
|
+
├── test/ # Unit tests (node:test)
|
|
148
|
+
├── dist/ # Compiled output (gitignored)
|
|
135
149
|
├── config.example.json
|
|
136
150
|
└── package.json
|
|
137
151
|
```
|
package/dist/server/index.js
CHANGED
|
@@ -332,21 +332,26 @@ async function main() {
|
|
|
332
332
|
let args;
|
|
333
333
|
let cwd;
|
|
334
334
|
let worktreeName;
|
|
335
|
+
let sessionRepoPath;
|
|
335
336
|
if (worktreePath) {
|
|
336
337
|
// Resume existing worktree — run claude --continue inside the worktree directory
|
|
337
338
|
args = ['--continue', ...baseArgs];
|
|
338
339
|
cwd = worktreePath;
|
|
340
|
+
sessionRepoPath = worktreePath;
|
|
339
341
|
worktreeName = worktreePath.split('/').pop() || '';
|
|
340
342
|
}
|
|
341
343
|
else {
|
|
342
|
-
// New worktree
|
|
344
|
+
// New worktree — PTY spawns in the repo root (so `claude --worktree X` works),
|
|
345
|
+
// but repoPath points to the expected worktree dir for identity/metadata matching
|
|
343
346
|
worktreeName = 'mobile-' + name + '-' + Date.now().toString(36);
|
|
344
347
|
args = ['--worktree', worktreeName, ...baseArgs];
|
|
345
348
|
cwd = repoPath;
|
|
349
|
+
sessionRepoPath = path.join(repoPath, '.claude', 'worktrees', worktreeName);
|
|
346
350
|
}
|
|
347
351
|
const session = sessions.create({
|
|
348
352
|
repoName: name,
|
|
349
|
-
repoPath:
|
|
353
|
+
repoPath: sessionRepoPath,
|
|
354
|
+
cwd,
|
|
350
355
|
root,
|
|
351
356
|
worktreeName,
|
|
352
357
|
displayName: worktreeName,
|
package/dist/server/sessions.js
CHANGED
|
@@ -6,7 +6,7 @@ import path from 'node:path';
|
|
|
6
6
|
import { readMeta, writeMeta } from './config.js';
|
|
7
7
|
// In-memory registry: id -> Session
|
|
8
8
|
const sessions = new Map();
|
|
9
|
-
function create({ repoName, repoPath, root, worktreeName, displayName, command, args = [], cols = 80, rows = 24, configPath }) {
|
|
9
|
+
function create({ repoName, repoPath, cwd, root, worktreeName, displayName, command, args = [], cols = 80, rows = 24, configPath }) {
|
|
10
10
|
const id = crypto.randomBytes(8).toString('hex');
|
|
11
11
|
const createdAt = new Date().toISOString();
|
|
12
12
|
// Strip CLAUDECODE env var to allow spawning claude inside a claude-managed server
|
|
@@ -16,7 +16,7 @@ function create({ repoName, repoPath, root, worktreeName, displayName, command,
|
|
|
16
16
|
name: 'xterm-256color',
|
|
17
17
|
cols,
|
|
18
18
|
rows,
|
|
19
|
-
cwd: repoPath,
|
|
19
|
+
cwd: cwd || repoPath,
|
|
20
20
|
env,
|
|
21
21
|
});
|
|
22
22
|
// Scrollback buffer: stores all PTY output so we can replay on WebSocket (re)connect
|
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -155,6 +155,60 @@
|
|
|
155
155
|
}
|
|
156
156
|
});
|
|
157
157
|
|
|
158
|
+
// On Windows/Linux, Ctrl+V is the paste shortcut but xterm.js intercepts it
|
|
159
|
+
// internally without firing a native paste event, so our image paste handler
|
|
160
|
+
// on terminalContainer never runs. Intercept Ctrl+V here to check for images.
|
|
161
|
+
// On macOS, Ctrl+V sends a raw \x16 to the terminal (used by vim etc.), so
|
|
162
|
+
// we only intercept on non-Mac platforms.
|
|
163
|
+
var isMac = /Mac|iPhone|iPad|iPod/.test(navigator.platform || '');
|
|
164
|
+
if (!isMac) {
|
|
165
|
+
term.attachCustomKeyEventHandler(function (e) {
|
|
166
|
+
if (e.type === 'keydown' && e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey &&
|
|
167
|
+
(e.key === 'v' || e.key === 'V')) {
|
|
168
|
+
if (navigator.clipboard && navigator.clipboard.read) {
|
|
169
|
+
navigator.clipboard.read().then(function (clipboardItems) {
|
|
170
|
+
var imageBlob = null;
|
|
171
|
+
var imageType = null;
|
|
172
|
+
|
|
173
|
+
for (var i = 0; i < clipboardItems.length; i++) {
|
|
174
|
+
var types = clipboardItems[i].types;
|
|
175
|
+
for (var j = 0; j < types.length; j++) {
|
|
176
|
+
if (types[j].indexOf('image/') === 0) {
|
|
177
|
+
imageType = types[j];
|
|
178
|
+
imageBlob = clipboardItems[i];
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (imageBlob) break;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (imageBlob) {
|
|
186
|
+
imageBlob.getType(imageType).then(function (blob) {
|
|
187
|
+
uploadImage(blob, imageType);
|
|
188
|
+
});
|
|
189
|
+
} else {
|
|
190
|
+
navigator.clipboard.readText().then(function (text) {
|
|
191
|
+
if (text) term.paste(text);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}).catch(function () {
|
|
195
|
+
// Clipboard read failed (permission denied, etc.) — fall back to text.
|
|
196
|
+
// If readText also fails, paste is lost for this keypress; this only
|
|
197
|
+
// happens when clipboard permission is fully denied, which is rare
|
|
198
|
+
// for user-gesture-triggered reads on HTTPS origins.
|
|
199
|
+
if (navigator.clipboard.readText) {
|
|
200
|
+
navigator.clipboard.readText().then(function (text) {
|
|
201
|
+
if (text) term.paste(text);
|
|
202
|
+
}).catch(function () {});
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
return false; // Prevent xterm from handling Ctrl+V
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return true; // Let xterm handle all other keys
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
158
212
|
var resizeObserver = new ResizeObserver(function () {
|
|
159
213
|
fitAddon.fit();
|
|
160
214
|
sendResize();
|