claude-remote-cli 2.8.0 → 2.9.0
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 +5 -2
- package/dist/frontend/assets/index-B3mDW63X.css +32 -0
- package/dist/frontend/assets/index-Dkj00kFR.js +47 -0
- package/dist/frontend/index.html +2 -2
- package/dist/server/config.js +1 -0
- package/dist/server/index.js +27 -9
- package/dist/server/sessions.js +25 -9
- package/dist/test/config.test.js +2 -0
- package/dist/test/sessions.test.js +35 -0
- package/package.json +1 -1
- package/dist/frontend/assets/index-CUkDx_1l.js +0 -47
- package/dist/frontend/assets/index-Not5cXLa.css +0 -32
package/dist/frontend/index.html
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
12
12
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
13
13
|
<meta name="theme-color" content="#1a1a1a" />
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-Dkj00kFR.js"></script>
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-B3mDW63X.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
18
18
|
<div id="app"></div>
|
package/dist/server/config.js
CHANGED
package/dist/server/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import cookieParser from 'cookie-parser';
|
|
|
11
11
|
import { loadConfig, saveConfig, DEFAULTS, readMeta, writeMeta, deleteMeta, ensureMetaDir } from './config.js';
|
|
12
12
|
import * as auth from './auth.js';
|
|
13
13
|
import * as sessions from './sessions.js';
|
|
14
|
+
import { AGENT_CONTINUE_ARGS } from './sessions.js';
|
|
14
15
|
import { setupWebSocket } from './ws.js';
|
|
15
16
|
import { WorktreeWatcher, WORKTREE_DIRS, isValidWorktreePath, parseWorktreeListPorcelain, parseAllWorktrees } from './watcher.js';
|
|
16
17
|
import { isInstalled as serviceIsInstalled } from './service.js';
|
|
@@ -500,6 +501,21 @@ async function main() {
|
|
|
500
501
|
broadcastEvent('worktrees-changed');
|
|
501
502
|
res.json(config.rootDirs);
|
|
502
503
|
});
|
|
504
|
+
// GET /config/defaultAgent — get default coding agent
|
|
505
|
+
app.get('/config/defaultAgent', requireAuth, (_req, res) => {
|
|
506
|
+
res.json({ defaultAgent: config.defaultAgent || 'claude' });
|
|
507
|
+
});
|
|
508
|
+
// PATCH /config/defaultAgent — set default coding agent
|
|
509
|
+
app.patch('/config/defaultAgent', requireAuth, (req, res) => {
|
|
510
|
+
const { defaultAgent } = req.body;
|
|
511
|
+
if (!defaultAgent || (defaultAgent !== 'claude' && defaultAgent !== 'codex')) {
|
|
512
|
+
res.status(400).json({ error: 'defaultAgent must be "claude" or "codex"' });
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
config.defaultAgent = defaultAgent;
|
|
516
|
+
saveConfig(CONFIG_PATH, config);
|
|
517
|
+
res.json({ defaultAgent: config.defaultAgent });
|
|
518
|
+
});
|
|
503
519
|
// DELETE /worktrees — remove a worktree, prune, and delete its branch
|
|
504
520
|
app.delete('/worktrees', requireAuth, async (req, res) => {
|
|
505
521
|
const { worktreePath, repoPath } = req.body;
|
|
@@ -574,11 +590,12 @@ async function main() {
|
|
|
574
590
|
});
|
|
575
591
|
// POST /sessions
|
|
576
592
|
app.post('/sessions', requireAuth, async (req, res) => {
|
|
577
|
-
const { repoPath, repoName, worktreePath, branchName, claudeArgs } = req.body;
|
|
593
|
+
const { repoPath, repoName, worktreePath, branchName, claudeArgs, agent } = req.body;
|
|
578
594
|
if (!repoPath) {
|
|
579
595
|
res.status(400).json({ error: 'repoPath is required' });
|
|
580
596
|
return;
|
|
581
597
|
}
|
|
598
|
+
const resolvedAgent = agent || config.defaultAgent || 'claude';
|
|
582
599
|
const name = repoName || repoPath.split('/').filter(Boolean).pop() || 'session';
|
|
583
600
|
const baseArgs = [...(config.claudeArgs || []), ...(claudeArgs || [])];
|
|
584
601
|
// Compute root by matching repoPath against configured rootDirs
|
|
@@ -591,7 +608,7 @@ async function main() {
|
|
|
591
608
|
let resolvedBranch = '';
|
|
592
609
|
if (worktreePath) {
|
|
593
610
|
// Resume existing worktree
|
|
594
|
-
args = [
|
|
611
|
+
args = [...AGENT_CONTINUE_ARGS[resolvedAgent], ...baseArgs];
|
|
595
612
|
cwd = worktreePath;
|
|
596
613
|
sessionRepoPath = worktreePath;
|
|
597
614
|
worktreeName = worktreePath.split('/').pop() || '';
|
|
@@ -648,12 +665,12 @@ async function main() {
|
|
|
648
665
|
}
|
|
649
666
|
const repoSession = sessions.create({
|
|
650
667
|
type: 'repo',
|
|
668
|
+
agent: resolvedAgent,
|
|
651
669
|
repoName: name,
|
|
652
670
|
repoPath,
|
|
653
671
|
cwd: repoPath,
|
|
654
672
|
root,
|
|
655
673
|
displayName: name,
|
|
656
|
-
command: config.claudeCommand,
|
|
657
674
|
args: baseArgs,
|
|
658
675
|
});
|
|
659
676
|
res.status(201).json(repoSession);
|
|
@@ -664,10 +681,11 @@ async function main() {
|
|
|
664
681
|
cwd = existingWt.path;
|
|
665
682
|
sessionRepoPath = existingWt.path;
|
|
666
683
|
worktreeName = existingWt.path.split('/').pop() || '';
|
|
667
|
-
args = [
|
|
684
|
+
args = [...AGENT_CONTINUE_ARGS[resolvedAgent], ...baseArgs];
|
|
668
685
|
const displayNameVal = branchName || worktreeName;
|
|
669
686
|
const session = sessions.create({
|
|
670
687
|
type: 'worktree',
|
|
688
|
+
agent: resolvedAgent,
|
|
671
689
|
repoName: name,
|
|
672
690
|
repoPath: sessionRepoPath,
|
|
673
691
|
cwd,
|
|
@@ -675,7 +693,6 @@ async function main() {
|
|
|
675
693
|
worktreeName,
|
|
676
694
|
branchName: branchName || worktreeName,
|
|
677
695
|
displayName: displayNameVal,
|
|
678
|
-
command: config.claudeCommand,
|
|
679
696
|
args,
|
|
680
697
|
configPath: CONFIG_PATH,
|
|
681
698
|
});
|
|
@@ -710,6 +727,7 @@ async function main() {
|
|
|
710
727
|
const displayName = branchName || worktreeName;
|
|
711
728
|
const session = sessions.create({
|
|
712
729
|
type: 'worktree',
|
|
730
|
+
agent: resolvedAgent,
|
|
713
731
|
repoName: name,
|
|
714
732
|
repoPath: sessionRepoPath,
|
|
715
733
|
cwd,
|
|
@@ -717,7 +735,6 @@ async function main() {
|
|
|
717
735
|
worktreeName,
|
|
718
736
|
branchName: branchName || worktreeName,
|
|
719
737
|
displayName,
|
|
720
|
-
command: config.claudeCommand,
|
|
721
738
|
args,
|
|
722
739
|
configPath: CONFIG_PATH,
|
|
723
740
|
});
|
|
@@ -733,11 +750,12 @@ async function main() {
|
|
|
733
750
|
});
|
|
734
751
|
// POST /sessions/repo — start a session in the repo root (no worktree)
|
|
735
752
|
app.post('/sessions/repo', requireAuth, (req, res) => {
|
|
736
|
-
const { repoPath, repoName, continue: continueSession, claudeArgs } = req.body;
|
|
753
|
+
const { repoPath, repoName, continue: continueSession, claudeArgs, agent } = req.body;
|
|
737
754
|
if (!repoPath) {
|
|
738
755
|
res.status(400).json({ error: 'repoPath is required' });
|
|
739
756
|
return;
|
|
740
757
|
}
|
|
758
|
+
const resolvedAgent = agent || config.defaultAgent || 'claude';
|
|
741
759
|
// One repo session at a time
|
|
742
760
|
const existing = sessions.findRepoSession(repoPath);
|
|
743
761
|
if (existing) {
|
|
@@ -746,17 +764,17 @@ async function main() {
|
|
|
746
764
|
}
|
|
747
765
|
const name = repoName || repoPath.split('/').filter(Boolean).pop() || 'session';
|
|
748
766
|
const baseArgs = [...(config.claudeArgs || []), ...(claudeArgs || [])];
|
|
749
|
-
const args = continueSession ? [
|
|
767
|
+
const args = continueSession ? [...AGENT_CONTINUE_ARGS[resolvedAgent], ...baseArgs] : [...baseArgs];
|
|
750
768
|
const roots = config.rootDirs || [];
|
|
751
769
|
const root = roots.find(function (r) { return repoPath.startsWith(r); }) || '';
|
|
752
770
|
const session = sessions.create({
|
|
753
771
|
type: 'repo',
|
|
772
|
+
agent: resolvedAgent,
|
|
754
773
|
repoName: name,
|
|
755
774
|
repoPath,
|
|
756
775
|
cwd: repoPath,
|
|
757
776
|
root,
|
|
758
777
|
displayName: name,
|
|
759
|
-
command: config.claudeCommand,
|
|
760
778
|
args,
|
|
761
779
|
});
|
|
762
780
|
res.status(201).json(session);
|
package/dist/server/sessions.js
CHANGED
|
@@ -4,6 +4,18 @@ import fs from 'node:fs';
|
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { readMeta, writeMeta } from './config.js';
|
|
7
|
+
const AGENT_COMMANDS = {
|
|
8
|
+
claude: 'claude',
|
|
9
|
+
codex: 'codex',
|
|
10
|
+
};
|
|
11
|
+
const AGENT_YOLO_ARGS = {
|
|
12
|
+
claude: ['--dangerously-skip-permissions'],
|
|
13
|
+
codex: ['--full-auto'],
|
|
14
|
+
};
|
|
15
|
+
const AGENT_CONTINUE_ARGS = {
|
|
16
|
+
claude: ['--continue'],
|
|
17
|
+
codex: ['resume', '--last'],
|
|
18
|
+
};
|
|
7
19
|
// In-memory registry: id -> Session
|
|
8
20
|
const sessions = new Map();
|
|
9
21
|
const IDLE_TIMEOUT_MS = 5000;
|
|
@@ -11,13 +23,14 @@ let idleChangeCallback = null;
|
|
|
11
23
|
function onIdleChange(cb) {
|
|
12
24
|
idleChangeCallback = cb;
|
|
13
25
|
}
|
|
14
|
-
function create({ type, repoName, repoPath, cwd, root, worktreeName, branchName, displayName, command, args = [], cols = 80, rows = 24, configPath }) {
|
|
26
|
+
function create({ type, agent = 'claude', repoName, repoPath, cwd, root, worktreeName, branchName, displayName, command, args = [], cols = 80, rows = 24, configPath }) {
|
|
15
27
|
const id = crypto.randomBytes(8).toString('hex');
|
|
16
28
|
const createdAt = new Date().toISOString();
|
|
29
|
+
const resolvedCommand = command || AGENT_COMMANDS[agent];
|
|
17
30
|
// Strip CLAUDECODE env var to allow spawning claude inside a claude-managed server
|
|
18
31
|
const env = Object.assign({}, process.env);
|
|
19
32
|
delete env.CLAUDECODE;
|
|
20
|
-
const ptyProcess = pty.spawn(
|
|
33
|
+
const ptyProcess = pty.spawn(resolvedCommand, args, {
|
|
21
34
|
name: 'xterm-256color',
|
|
22
35
|
cols,
|
|
23
36
|
rows,
|
|
@@ -31,6 +44,7 @@ function create({ type, repoName, repoPath, cwd, root, worktreeName, branchName,
|
|
|
31
44
|
const session = {
|
|
32
45
|
id,
|
|
33
46
|
type: type || 'worktree',
|
|
47
|
+
agent,
|
|
34
48
|
root: root || '',
|
|
35
49
|
repoName: repoName || '',
|
|
36
50
|
repoPath,
|
|
@@ -70,6 +84,7 @@ function create({ type, repoName, repoPath, cwd, root, worktreeName, branchName,
|
|
|
70
84
|
}
|
|
71
85
|
}, IDLE_TIMEOUT_MS);
|
|
72
86
|
}
|
|
87
|
+
const continueArgs = AGENT_CONTINUE_ARGS[agent];
|
|
73
88
|
function attachHandlers(proc, canRetry) {
|
|
74
89
|
const spawnTime = Date.now();
|
|
75
90
|
proc.onData((data) => {
|
|
@@ -89,12 +104,12 @@ function create({ type, repoName, repoPath, cwd, root, worktreeName, branchName,
|
|
|
89
104
|
}
|
|
90
105
|
});
|
|
91
106
|
proc.onExit(({ exitCode }) => {
|
|
92
|
-
// If
|
|
107
|
+
// If continue args failed quickly, retry without them
|
|
93
108
|
if (canRetry && (Date.now() - spawnTime) < 3000 && exitCode !== 0) {
|
|
94
|
-
const retryArgs = args.filter(a => a
|
|
109
|
+
const retryArgs = args.filter(a => !continueArgs.includes(a));
|
|
95
110
|
scrollback.length = 0;
|
|
96
111
|
scrollbackBytes = 0;
|
|
97
|
-
const retryPty = pty.spawn(
|
|
112
|
+
const retryPty = pty.spawn(resolvedCommand, retryArgs, {
|
|
98
113
|
name: 'xterm-256color',
|
|
99
114
|
cols,
|
|
100
115
|
rows,
|
|
@@ -117,17 +132,18 @@ function create({ type, repoName, repoPath, cwd, root, worktreeName, branchName,
|
|
|
117
132
|
fs.rm(tmpDir, { recursive: true, force: true }, () => { });
|
|
118
133
|
});
|
|
119
134
|
}
|
|
120
|
-
attachHandlers(ptyProcess, args.includes(
|
|
121
|
-
return { id, type: session.type, root: session.root, repoName: session.repoName, repoPath, worktreeName: session.worktreeName, branchName: session.branchName, displayName: session.displayName, pid: ptyProcess.pid, createdAt, lastActivity: createdAt, idle: false };
|
|
135
|
+
attachHandlers(ptyProcess, continueArgs.some(a => args.includes(a)));
|
|
136
|
+
return { id, type: session.type, agent: session.agent, root: session.root, repoName: session.repoName, repoPath, worktreeName: session.worktreeName, branchName: session.branchName, displayName: session.displayName, pid: ptyProcess.pid, createdAt, lastActivity: createdAt, idle: false };
|
|
122
137
|
}
|
|
123
138
|
function get(id) {
|
|
124
139
|
return sessions.get(id);
|
|
125
140
|
}
|
|
126
141
|
function list() {
|
|
127
142
|
return Array.from(sessions.values())
|
|
128
|
-
.map(({ id, type, root, repoName, repoPath, worktreeName, branchName, displayName, createdAt, lastActivity, idle }) => ({
|
|
143
|
+
.map(({ id, type, agent, root, repoName, repoPath, worktreeName, branchName, displayName, createdAt, lastActivity, idle }) => ({
|
|
129
144
|
id,
|
|
130
145
|
type,
|
|
146
|
+
agent,
|
|
131
147
|
root,
|
|
132
148
|
repoName,
|
|
133
149
|
repoPath,
|
|
@@ -172,4 +188,4 @@ function write(id, data) {
|
|
|
172
188
|
function findRepoSession(repoPath) {
|
|
173
189
|
return list().find((s) => s.type === 'repo' && s.repoPath === repoPath);
|
|
174
190
|
}
|
|
175
|
-
export { create, get, list, kill, resize, updateDisplayName, write, onIdleChange, findRepoSession };
|
|
191
|
+
export { create, get, list, kill, resize, updateDisplayName, write, onIdleChange, findRepoSession, AGENT_COMMANDS, AGENT_YOLO_ARGS, AGENT_CONTINUE_ARGS };
|
package/dist/test/config.test.js
CHANGED
|
@@ -40,6 +40,7 @@ test('loadConfig merges with defaults for missing fields', () => {
|
|
|
40
40
|
assert.deepEqual(config.repos, DEFAULTS.repos);
|
|
41
41
|
assert.equal(config.claudeCommand, DEFAULTS.claudeCommand);
|
|
42
42
|
assert.deepEqual(config.claudeArgs, DEFAULTS.claudeArgs);
|
|
43
|
+
assert.equal(config.defaultAgent, DEFAULTS.defaultAgent);
|
|
43
44
|
});
|
|
44
45
|
test('loadConfig throws if config file not found', () => {
|
|
45
46
|
const configPath = path.join(tmpDir, 'nonexistent.json');
|
|
@@ -59,6 +60,7 @@ test('DEFAULTS has expected keys and values', () => {
|
|
|
59
60
|
assert.deepEqual(DEFAULTS.repos, []);
|
|
60
61
|
assert.equal(DEFAULTS.claudeCommand, 'claude');
|
|
61
62
|
assert.deepEqual(DEFAULTS.claudeArgs, []);
|
|
63
|
+
assert.equal(DEFAULTS.defaultAgent, 'claude');
|
|
62
64
|
});
|
|
63
65
|
test('ensureMetaDir creates worktree-meta directory', () => {
|
|
64
66
|
const configPath = path.join(tmpDir, 'config.json');
|
|
@@ -264,4 +264,39 @@ describe('sessions', () => {
|
|
|
264
264
|
createdIds.push(result.id);
|
|
265
265
|
assert.strictEqual(result.branchName, '');
|
|
266
266
|
});
|
|
267
|
+
it('agent defaults to claude when not specified', () => {
|
|
268
|
+
const result = sessions.create({
|
|
269
|
+
repoName: 'test-repo',
|
|
270
|
+
repoPath: '/tmp',
|
|
271
|
+
command: '/bin/echo',
|
|
272
|
+
args: ['hello'],
|
|
273
|
+
});
|
|
274
|
+
createdIds.push(result.id);
|
|
275
|
+
assert.strictEqual(result.agent, 'claude');
|
|
276
|
+
});
|
|
277
|
+
it('agent is set when specified', () => {
|
|
278
|
+
const result = sessions.create({
|
|
279
|
+
repoName: 'test-repo',
|
|
280
|
+
repoPath: '/tmp',
|
|
281
|
+
agent: 'codex',
|
|
282
|
+
command: '/bin/echo',
|
|
283
|
+
args: ['hello'],
|
|
284
|
+
});
|
|
285
|
+
createdIds.push(result.id);
|
|
286
|
+
assert.strictEqual(result.agent, 'codex');
|
|
287
|
+
});
|
|
288
|
+
it('list includes agent field', () => {
|
|
289
|
+
const result = sessions.create({
|
|
290
|
+
repoName: 'test-repo',
|
|
291
|
+
repoPath: '/tmp',
|
|
292
|
+
agent: 'codex',
|
|
293
|
+
command: '/bin/echo',
|
|
294
|
+
args: ['hello'],
|
|
295
|
+
});
|
|
296
|
+
createdIds.push(result.id);
|
|
297
|
+
const list = sessions.list();
|
|
298
|
+
const session = list.find(s => s.id === result.id);
|
|
299
|
+
assert.ok(session);
|
|
300
|
+
assert.strictEqual(session.agent, 'codex');
|
|
301
|
+
});
|
|
267
302
|
});
|