claude-remote-cli 3.0.3 → 3.0.4

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.
@@ -1,214 +0,0 @@
1
- import pty from 'node-pty';
2
- import fs from 'node:fs';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
- import { AGENT_COMMANDS, AGENT_CONTINUE_ARGS } from './types.js';
6
- import { readMeta, writeMeta } from './config.js';
7
- const IDLE_TIMEOUT_MS = 5000;
8
- const MAX_SCROLLBACK = 256 * 1024; // 256KB max
9
- export function generateTmuxSessionName(displayName, id) {
10
- const sanitized = displayName.replace(/[^a-zA-Z0-9-]/g, '-').replace(/-+/g, '-').slice(0, 30);
11
- return `crc-${sanitized}-${id.slice(0, 8)}`;
12
- }
13
- export function resolveTmuxSpawn(command, args, tmuxSessionName) {
14
- return {
15
- command: 'tmux',
16
- args: [
17
- '-u', 'new-session', '-s', tmuxSessionName, '--', command, ...args,
18
- ';', 'set', 'set-clipboard', 'on',
19
- ';', 'set', 'allow-passthrough', 'on',
20
- ';', 'set', 'mode-keys', 'vi',
21
- ],
22
- };
23
- }
24
- export function createPtySession(params, sessionsMap, idleChangeCallbacks) {
25
- const { id, type, agent = 'claude', repoName, repoPath, cwd, root, worktreeName, branchName, displayName, command, args = [], cols = 80, rows = 24, configPath, useTmux: paramUseTmux, tmuxSessionName: paramTmuxSessionName, initialScrollback, restored: paramRestored, } = params;
26
- const createdAt = new Date().toISOString();
27
- const resolvedCommand = command || AGENT_COMMANDS[agent];
28
- // Strip CLAUDECODE env var to allow spawning claude inside a claude-managed server
29
- const env = Object.assign({}, process.env);
30
- delete env.CLAUDECODE;
31
- const useTmux = !command && !!paramUseTmux;
32
- let spawnCommand = resolvedCommand;
33
- let spawnArgs = args;
34
- const tmuxSessionName = paramTmuxSessionName || (useTmux ? generateTmuxSessionName(displayName || repoName || 'session', id) : '');
35
- if (useTmux) {
36
- const tmux = resolveTmuxSpawn(resolvedCommand, args, tmuxSessionName);
37
- spawnCommand = tmux.command;
38
- spawnArgs = tmux.args;
39
- }
40
- const ptyProcess = pty.spawn(spawnCommand, spawnArgs, {
41
- name: 'xterm-256color',
42
- cols,
43
- rows,
44
- cwd: cwd || repoPath,
45
- env,
46
- });
47
- // Scrollback buffer: stores all PTY output so we can replay on WebSocket (re)connect
48
- const scrollback = initialScrollback ? [...initialScrollback] : [];
49
- let scrollbackBytes = initialScrollback ? initialScrollback.reduce((sum, s) => sum + s.length, 0) : 0;
50
- const resolvedCwd = cwd || repoPath;
51
- const session = {
52
- id,
53
- type: type || 'worktree',
54
- agent,
55
- mode: 'pty',
56
- root: root || '',
57
- repoName: repoName || '',
58
- repoPath,
59
- worktreeName: worktreeName || '',
60
- branchName: branchName || worktreeName || '',
61
- displayName: displayName || worktreeName || repoName || '',
62
- pty: ptyProcess,
63
- createdAt,
64
- lastActivity: createdAt,
65
- scrollback,
66
- idle: false,
67
- cwd: resolvedCwd,
68
- customCommand: command || null,
69
- useTmux,
70
- tmuxSessionName,
71
- onPtyReplacedCallbacks: [],
72
- status: 'active',
73
- restored: paramRestored || false,
74
- };
75
- sessionsMap.set(id, session);
76
- // Load existing metadata to preserve a previously-set displayName
77
- if (configPath && worktreeName) {
78
- const existing = readMeta(configPath, repoPath);
79
- if (existing && existing.displayName) {
80
- session.displayName = existing.displayName;
81
- }
82
- writeMeta(configPath, { worktreePath: repoPath, displayName: session.displayName, lastActivity: createdAt });
83
- }
84
- let metaFlushTimer = null;
85
- let idleTimer = null;
86
- function resetIdleTimer() {
87
- if (session.idle) {
88
- session.idle = false;
89
- for (const cb of idleChangeCallbacks)
90
- cb(session.id, false);
91
- }
92
- if (idleTimer)
93
- clearTimeout(idleTimer);
94
- idleTimer = setTimeout(() => {
95
- if (!session.idle) {
96
- session.idle = true;
97
- for (const cb of idleChangeCallbacks)
98
- cb(session.id, true);
99
- }
100
- }, IDLE_TIMEOUT_MS);
101
- }
102
- const continueArgs = AGENT_CONTINUE_ARGS[agent];
103
- function attachHandlers(proc, canRetry) {
104
- const spawnTime = Date.now();
105
- // Clear restored flag after 3s of running — means the PTY is healthy
106
- const restoredClearTimer = session.restored ? setTimeout(() => { session.restored = false; }, 3000) : null;
107
- proc.onData((data) => {
108
- session.lastActivity = new Date().toISOString();
109
- resetIdleTimer();
110
- scrollback.push(data);
111
- scrollbackBytes += data.length;
112
- // Trim oldest entries if over limit
113
- while (scrollbackBytes > MAX_SCROLLBACK && scrollback.length > 1) {
114
- scrollbackBytes -= scrollback.shift().length;
115
- }
116
- if (configPath && worktreeName && !metaFlushTimer) {
117
- metaFlushTimer = setTimeout(() => {
118
- metaFlushTimer = null;
119
- writeMeta(configPath, { worktreePath: repoPath, displayName: session.displayName, lastActivity: session.lastActivity });
120
- }, 5000);
121
- }
122
- });
123
- proc.onExit(() => {
124
- if (canRetry && (Date.now() - spawnTime) < 3000) {
125
- const retryArgs = args.filter(a => !continueArgs.includes(a));
126
- const retryNotice = '\r\n[claude-remote-cli] --continue not available; starting new session...\r\n';
127
- scrollback.length = 0;
128
- scrollbackBytes = 0;
129
- scrollback.push(retryNotice);
130
- scrollbackBytes = retryNotice.length;
131
- let retryCommand = resolvedCommand;
132
- let retrySpawnArgs = retryArgs;
133
- if (useTmux && tmuxSessionName) {
134
- const retryTmuxName = tmuxSessionName + '-retry';
135
- session.tmuxSessionName = retryTmuxName;
136
- const tmux = resolveTmuxSpawn(resolvedCommand, retryArgs, retryTmuxName);
137
- retryCommand = tmux.command;
138
- retrySpawnArgs = tmux.args;
139
- }
140
- let retryPty;
141
- try {
142
- retryPty = pty.spawn(retryCommand, retrySpawnArgs, {
143
- name: 'xterm-256color',
144
- cols,
145
- rows,
146
- cwd: cwd || repoPath,
147
- env,
148
- });
149
- }
150
- catch {
151
- // Retry spawn failed — fall through to normal exit cleanup
152
- if (restoredClearTimer)
153
- clearTimeout(restoredClearTimer);
154
- if (idleTimer)
155
- clearTimeout(idleTimer);
156
- if (metaFlushTimer)
157
- clearTimeout(metaFlushTimer);
158
- sessionsMap.delete(id);
159
- return;
160
- }
161
- session.pty = retryPty;
162
- for (const cb of session.onPtyReplacedCallbacks)
163
- cb(retryPty);
164
- attachHandlers(retryPty, false);
165
- return;
166
- }
167
- if (restoredClearTimer)
168
- clearTimeout(restoredClearTimer);
169
- // If PTY exited and this is a restored session, mark disconnected rather than delete
170
- if (session.restored) {
171
- session.status = 'disconnected';
172
- session.restored = false; // clear so user-initiated kills can delete normally
173
- if (idleTimer)
174
- clearTimeout(idleTimer);
175
- if (metaFlushTimer)
176
- clearTimeout(metaFlushTimer);
177
- return;
178
- }
179
- if (idleTimer)
180
- clearTimeout(idleTimer);
181
- if (metaFlushTimer)
182
- clearTimeout(metaFlushTimer);
183
- if (configPath && worktreeName) {
184
- writeMeta(configPath, { worktreePath: repoPath, displayName: session.displayName, lastActivity: session.lastActivity });
185
- }
186
- sessionsMap.delete(id);
187
- const tmpDir = path.join(os.tmpdir(), 'claude-remote-cli', id);
188
- fs.rm(tmpDir, { recursive: true, force: true }, () => { });
189
- });
190
- }
191
- attachHandlers(ptyProcess, continueArgs.some(a => args.includes(a)));
192
- const result = {
193
- id,
194
- type: session.type,
195
- agent: session.agent,
196
- mode: 'pty',
197
- root: session.root,
198
- repoName: session.repoName,
199
- repoPath,
200
- worktreeName: session.worktreeName,
201
- branchName: session.branchName,
202
- displayName: session.displayName,
203
- pid: ptyProcess.pid,
204
- createdAt,
205
- lastActivity: createdAt,
206
- idle: false,
207
- cwd: resolvedCwd,
208
- customCommand: command || null,
209
- useTmux,
210
- tmuxSessionName,
211
- status: 'active',
212
- };
213
- return { session, result };
214
- }