claude-remote-cli 2.11.0 → 2.12.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/dist/frontend/assets/index-CEJznk5F.js +47 -0
- package/dist/frontend/index.html +1 -1
- package/dist/server/config.js +3 -0
- package/dist/server/index.js +63 -2
- package/dist/server/sessions.js +47 -6
- package/dist/test/config.test.js +11 -0
- package/dist/test/sessions.test.js +71 -0
- package/package.json +1 -1
- package/dist/frontend/assets/index-DLO1cUOf.js +0 -47
package/dist/frontend/index.html
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
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-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-CEJznk5F.js"></script>
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/index-nIPDa7NP.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
package/dist/server/config.js
CHANGED
package/dist/server/index.js
CHANGED
|
@@ -193,6 +193,30 @@ async function main() {
|
|
|
193
193
|
}
|
|
194
194
|
next();
|
|
195
195
|
};
|
|
196
|
+
function boolConfigEndpoints(name, defaultValue, onEnable) {
|
|
197
|
+
app.get(`/config/${name}`, requireAuth, (_req, res) => {
|
|
198
|
+
res.json({ [name]: config[name] ?? defaultValue });
|
|
199
|
+
});
|
|
200
|
+
app.patch(`/config/${name}`, requireAuth, async (req, res) => {
|
|
201
|
+
const value = req.body[name];
|
|
202
|
+
if (typeof value !== 'boolean') {
|
|
203
|
+
res.status(400).json({ error: `${name} must be a boolean` });
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (value && onEnable) {
|
|
207
|
+
try {
|
|
208
|
+
await onEnable();
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
res.status(400).json({ error: `Validation failed for ${name}` });
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
config[name] = value;
|
|
216
|
+
saveConfig(CONFIG_PATH, config);
|
|
217
|
+
res.json({ [name]: value });
|
|
218
|
+
});
|
|
219
|
+
}
|
|
196
220
|
const watcher = new WorktreeWatcher();
|
|
197
221
|
watcher.rebuild(config.rootDirs || []);
|
|
198
222
|
const server = http.createServer(app);
|
|
@@ -506,6 +530,11 @@ async function main() {
|
|
|
506
530
|
saveConfig(CONFIG_PATH, config);
|
|
507
531
|
res.json({ defaultAgent: config.defaultAgent });
|
|
508
532
|
});
|
|
533
|
+
boolConfigEndpoints('defaultContinue', true);
|
|
534
|
+
boolConfigEndpoints('defaultYolo', false);
|
|
535
|
+
boolConfigEndpoints('launchInTmux', false, async () => {
|
|
536
|
+
await execFileAsync('tmux', ['-V']);
|
|
537
|
+
});
|
|
509
538
|
// DELETE /worktrees — remove a worktree, prune, and delete its branch
|
|
510
539
|
app.delete('/worktrees', requireAuth, async (req, res) => {
|
|
511
540
|
const { worktreePath, repoPath } = req.body;
|
|
@@ -580,7 +609,7 @@ async function main() {
|
|
|
580
609
|
});
|
|
581
610
|
// POST /sessions
|
|
582
611
|
app.post('/sessions', requireAuth, async (req, res) => {
|
|
583
|
-
const { repoPath, repoName, worktreePath, branchName, claudeArgs, yolo, agent } = req.body;
|
|
612
|
+
const { repoPath, repoName, worktreePath, branchName, claudeArgs, yolo, agent, useTmux } = req.body;
|
|
584
613
|
if (!repoPath) {
|
|
585
614
|
res.status(400).json({ error: 'repoPath is required' });
|
|
586
615
|
return;
|
|
@@ -666,6 +695,7 @@ async function main() {
|
|
|
666
695
|
root,
|
|
667
696
|
displayName: name,
|
|
668
697
|
args: baseArgs,
|
|
698
|
+
useTmux: useTmux ?? config.launchInTmux,
|
|
669
699
|
});
|
|
670
700
|
res.status(201).json(repoSession);
|
|
671
701
|
return;
|
|
@@ -689,6 +719,7 @@ async function main() {
|
|
|
689
719
|
displayName: displayNameVal,
|
|
690
720
|
args,
|
|
691
721
|
configPath: CONFIG_PATH,
|
|
722
|
+
useTmux: useTmux ?? config.launchInTmux,
|
|
692
723
|
});
|
|
693
724
|
writeMeta(CONFIG_PATH, {
|
|
694
725
|
worktreePath: sessionRepoPath,
|
|
@@ -731,6 +762,7 @@ async function main() {
|
|
|
731
762
|
displayName,
|
|
732
763
|
args,
|
|
733
764
|
configPath: CONFIG_PATH,
|
|
765
|
+
useTmux: useTmux ?? config.launchInTmux,
|
|
734
766
|
});
|
|
735
767
|
if (!worktreePath) {
|
|
736
768
|
writeMeta(CONFIG_PATH, {
|
|
@@ -744,7 +776,7 @@ async function main() {
|
|
|
744
776
|
});
|
|
745
777
|
// POST /sessions/repo — start a session in the repo root (no worktree)
|
|
746
778
|
app.post('/sessions/repo', requireAuth, (req, res) => {
|
|
747
|
-
const { repoPath, repoName, continue: continueSession, claudeArgs, yolo, agent } = req.body;
|
|
779
|
+
const { repoPath, repoName, continue: continueSession, claudeArgs, yolo, agent, useTmux } = req.body;
|
|
748
780
|
if (!repoPath) {
|
|
749
781
|
res.status(400).json({ error: 'repoPath is required' });
|
|
750
782
|
return;
|
|
@@ -774,6 +806,7 @@ async function main() {
|
|
|
774
806
|
root,
|
|
775
807
|
displayName: name,
|
|
776
808
|
args,
|
|
809
|
+
useTmux: useTmux ?? config.launchInTmux,
|
|
777
810
|
});
|
|
778
811
|
res.status(201).json(session);
|
|
779
812
|
});
|
|
@@ -889,6 +922,34 @@ async function main() {
|
|
|
889
922
|
res.status(500).json({ ok: false, error: message });
|
|
890
923
|
}
|
|
891
924
|
});
|
|
925
|
+
// Clean up orphaned tmux sessions from previous runs
|
|
926
|
+
try {
|
|
927
|
+
const { stdout } = await execFileAsync('tmux', ['list-sessions', '-F', '#{session_name}']);
|
|
928
|
+
const crcSessions = stdout.trim().split('\n').filter(name => name.startsWith('crc-'));
|
|
929
|
+
for (const name of crcSessions) {
|
|
930
|
+
execFileAsync('tmux', ['kill-session', '-t', name]).catch(() => { });
|
|
931
|
+
}
|
|
932
|
+
if (crcSessions.length > 0) {
|
|
933
|
+
console.log(`Cleaned up ${crcSessions.length} orphaned tmux session(s).`);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
catch {
|
|
937
|
+
// tmux not installed or no sessions — ignore
|
|
938
|
+
}
|
|
939
|
+
function gracefulShutdown() {
|
|
940
|
+
server.close();
|
|
941
|
+
// Kill all active sessions (PTY + tmux)
|
|
942
|
+
for (const s of sessions.list()) {
|
|
943
|
+
try {
|
|
944
|
+
sessions.kill(s.id);
|
|
945
|
+
}
|
|
946
|
+
catch { /* already exiting */ }
|
|
947
|
+
}
|
|
948
|
+
// Brief delay to let async tmux kill-session calls fire
|
|
949
|
+
setTimeout(() => process.exit(0), 200);
|
|
950
|
+
}
|
|
951
|
+
process.on('SIGTERM', gracefulShutdown);
|
|
952
|
+
process.on('SIGINT', gracefulShutdown);
|
|
892
953
|
server.listen(config.port, config.host, () => {
|
|
893
954
|
console.log(`claude-remote-cli listening on ${config.host}:${config.port}`);
|
|
894
955
|
});
|
package/dist/server/sessions.js
CHANGED
|
@@ -3,6 +3,7 @@ import crypto from 'node:crypto';
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
+
import { execFile } from 'node:child_process';
|
|
6
7
|
import { readMeta, writeMeta } from './config.js';
|
|
7
8
|
const AGENT_COMMANDS = {
|
|
8
9
|
claude: 'claude',
|
|
@@ -16,6 +17,16 @@ const AGENT_YOLO_ARGS = {
|
|
|
16
17
|
claude: ['--dangerously-skip-permissions'],
|
|
17
18
|
codex: ['--full-auto'],
|
|
18
19
|
};
|
|
20
|
+
function generateTmuxSessionName(displayName, id) {
|
|
21
|
+
const sanitized = displayName.replace(/[^a-zA-Z0-9-]/g, '-').replace(/-+/g, '-').slice(0, 30);
|
|
22
|
+
return `crc-${sanitized}-${id.slice(0, 8)}`;
|
|
23
|
+
}
|
|
24
|
+
function resolveTmuxSpawn(command, args, tmuxSessionName) {
|
|
25
|
+
return {
|
|
26
|
+
command: 'tmux',
|
|
27
|
+
args: ['new-session', '-s', tmuxSessionName, '--', command, ...args],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
19
30
|
// In-memory registry: id -> Session
|
|
20
31
|
const sessions = new Map();
|
|
21
32
|
const IDLE_TIMEOUT_MS = 5000;
|
|
@@ -24,14 +35,23 @@ let idleChangeCallback = null;
|
|
|
24
35
|
function onIdleChange(cb) {
|
|
25
36
|
idleChangeCallback = cb;
|
|
26
37
|
}
|
|
27
|
-
function create({ type, agent = 'claude', repoName, repoPath, cwd, root, worktreeName, branchName, displayName, command, args = [], cols = 80, rows = 24, configPath }) {
|
|
38
|
+
function create({ type, agent = 'claude', repoName, repoPath, cwd, root, worktreeName, branchName, displayName, command, args = [], cols = 80, rows = 24, configPath, useTmux: paramUseTmux }) {
|
|
28
39
|
const id = crypto.randomBytes(8).toString('hex');
|
|
29
40
|
const createdAt = new Date().toISOString();
|
|
30
41
|
const resolvedCommand = command || AGENT_COMMANDS[agent];
|
|
31
42
|
// Strip CLAUDECODE env var to allow spawning claude inside a claude-managed server
|
|
32
43
|
const env = Object.assign({}, process.env);
|
|
33
44
|
delete env.CLAUDECODE;
|
|
34
|
-
const
|
|
45
|
+
const useTmux = !command && !!paramUseTmux;
|
|
46
|
+
let spawnCommand = resolvedCommand;
|
|
47
|
+
let spawnArgs = args;
|
|
48
|
+
const tmuxSessionName = useTmux ? generateTmuxSessionName(displayName || repoName || 'session', id) : '';
|
|
49
|
+
if (useTmux) {
|
|
50
|
+
const tmux = resolveTmuxSpawn(resolvedCommand, args, tmuxSessionName);
|
|
51
|
+
spawnCommand = tmux.command;
|
|
52
|
+
spawnArgs = tmux.args;
|
|
53
|
+
}
|
|
54
|
+
const ptyProcess = pty.spawn(spawnCommand, spawnArgs, {
|
|
35
55
|
name: 'xterm-256color',
|
|
36
56
|
cols,
|
|
37
57
|
rows,
|
|
@@ -57,6 +77,8 @@ function create({ type, agent = 'claude', repoName, repoPath, cwd, root, worktre
|
|
|
57
77
|
lastActivity: createdAt,
|
|
58
78
|
scrollback,
|
|
59
79
|
idle: false,
|
|
80
|
+
useTmux,
|
|
81
|
+
tmuxSessionName,
|
|
60
82
|
};
|
|
61
83
|
sessions.set(id, session);
|
|
62
84
|
// Load existing metadata to preserve a previously-set displayName
|
|
@@ -110,7 +132,14 @@ function create({ type, agent = 'claude', repoName, repoPath, cwd, root, worktre
|
|
|
110
132
|
const retryArgs = args.filter(a => !continueArgs.includes(a));
|
|
111
133
|
scrollback.length = 0;
|
|
112
134
|
scrollbackBytes = 0;
|
|
113
|
-
|
|
135
|
+
let retryCommand = resolvedCommand;
|
|
136
|
+
let retrySpawnArgs = retryArgs;
|
|
137
|
+
if (useTmux && tmuxSessionName) {
|
|
138
|
+
const tmux = resolveTmuxSpawn(resolvedCommand, retryArgs, tmuxSessionName);
|
|
139
|
+
retryCommand = tmux.command;
|
|
140
|
+
retrySpawnArgs = tmux.args;
|
|
141
|
+
}
|
|
142
|
+
const retryPty = pty.spawn(retryCommand, retrySpawnArgs, {
|
|
114
143
|
name: 'xterm-256color',
|
|
115
144
|
cols,
|
|
116
145
|
rows,
|
|
@@ -134,14 +163,14 @@ function create({ type, agent = 'claude', repoName, repoPath, cwd, root, worktre
|
|
|
134
163
|
});
|
|
135
164
|
}
|
|
136
165
|
attachHandlers(ptyProcess, continueArgs.some(a => args.includes(a)));
|
|
137
|
-
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 };
|
|
166
|
+
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, useTmux, tmuxSessionName };
|
|
138
167
|
}
|
|
139
168
|
function get(id) {
|
|
140
169
|
return sessions.get(id);
|
|
141
170
|
}
|
|
142
171
|
function list() {
|
|
143
172
|
return Array.from(sessions.values())
|
|
144
|
-
.map(({ id, type, agent, root, repoName, repoPath, worktreeName, branchName, displayName, createdAt, lastActivity, idle }) => ({
|
|
173
|
+
.map(({ id, type, agent, root, repoName, repoPath, worktreeName, branchName, displayName, createdAt, lastActivity, idle, useTmux, tmuxSessionName }) => ({
|
|
145
174
|
id,
|
|
146
175
|
type,
|
|
147
176
|
agent,
|
|
@@ -154,6 +183,8 @@ function list() {
|
|
|
154
183
|
createdAt,
|
|
155
184
|
lastActivity,
|
|
156
185
|
idle,
|
|
186
|
+
useTmux,
|
|
187
|
+
tmuxSessionName,
|
|
157
188
|
}))
|
|
158
189
|
.sort((a, b) => b.lastActivity.localeCompare(a.lastActivity));
|
|
159
190
|
}
|
|
@@ -170,8 +201,18 @@ function kill(id) {
|
|
|
170
201
|
throw new Error(`Session not found: ${id}`);
|
|
171
202
|
}
|
|
172
203
|
session.pty.kill('SIGTERM');
|
|
204
|
+
if (session.tmuxSessionName) {
|
|
205
|
+
execFile('tmux', ['kill-session', '-t', session.tmuxSessionName], () => { });
|
|
206
|
+
}
|
|
173
207
|
sessions.delete(id);
|
|
174
208
|
}
|
|
209
|
+
function killAllTmuxSessions() {
|
|
210
|
+
for (const session of sessions.values()) {
|
|
211
|
+
if (session.tmuxSessionName) {
|
|
212
|
+
execFile('tmux', ['kill-session', '-t', session.tmuxSessionName], () => { });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
175
216
|
function resize(id, cols, rows) {
|
|
176
217
|
const session = sessions.get(id);
|
|
177
218
|
if (!session) {
|
|
@@ -192,4 +233,4 @@ function findRepoSession(repoPath) {
|
|
|
192
233
|
function nextTerminalName() {
|
|
193
234
|
return `Terminal ${++terminalCounter}`;
|
|
194
235
|
}
|
|
195
|
-
export { create, get, list, kill, resize, updateDisplayName, write, onIdleChange, findRepoSession, nextTerminalName, AGENT_COMMANDS, AGENT_CONTINUE_ARGS, AGENT_YOLO_ARGS };
|
|
236
|
+
export { create, get, list, kill, killAllTmuxSessions, resize, updateDisplayName, write, onIdleChange, findRepoSession, nextTerminalName, AGENT_COMMANDS, AGENT_CONTINUE_ARGS, AGENT_YOLO_ARGS, resolveTmuxSpawn, generateTmuxSessionName };
|
package/dist/test/config.test.js
CHANGED
|
@@ -61,6 +61,17 @@ test('DEFAULTS has expected keys and values', () => {
|
|
|
61
61
|
assert.equal(DEFAULTS.claudeCommand, 'claude');
|
|
62
62
|
assert.deepEqual(DEFAULTS.claudeArgs, []);
|
|
63
63
|
assert.equal(DEFAULTS.defaultAgent, 'claude');
|
|
64
|
+
assert.equal(DEFAULTS.defaultContinue, true);
|
|
65
|
+
assert.equal(DEFAULTS.defaultYolo, false);
|
|
66
|
+
assert.equal(DEFAULTS.launchInTmux, false);
|
|
67
|
+
});
|
|
68
|
+
test('loadConfig returns correct defaults for defaultContinue, defaultYolo, and launchInTmux', () => {
|
|
69
|
+
const configPath = path.join(tmpDir, 'config.json');
|
|
70
|
+
fs.writeFileSync(configPath, JSON.stringify({ port: 3456 }), 'utf8');
|
|
71
|
+
const config = loadConfig(configPath);
|
|
72
|
+
assert.equal(config.defaultContinue, true);
|
|
73
|
+
assert.equal(config.defaultYolo, false);
|
|
74
|
+
assert.equal(config.launchInTmux, false);
|
|
64
75
|
});
|
|
65
76
|
test('ensureMetaDir creates worktree-meta directory', () => {
|
|
66
77
|
const configPath = path.join(tmpDir, 'config.json');
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, it, afterEach } from 'node:test';
|
|
2
2
|
import assert from 'node:assert';
|
|
3
3
|
import * as sessions from '../server/sessions.js';
|
|
4
|
+
import { resolveTmuxSpawn, generateTmuxSessionName } from '../server/sessions.js';
|
|
4
5
|
// Track created session IDs so we can clean up after each test
|
|
5
6
|
const createdIds = [];
|
|
6
7
|
afterEach(() => {
|
|
@@ -264,6 +265,38 @@ describe('sessions', () => {
|
|
|
264
265
|
createdIds.push(result.id);
|
|
265
266
|
assert.strictEqual(result.branchName, '');
|
|
266
267
|
});
|
|
268
|
+
it('resolveTmuxSpawn returns correct tmux command and args', () => {
|
|
269
|
+
const result = resolveTmuxSpawn('claude', ['--continue'], 'test-session');
|
|
270
|
+
assert.deepStrictEqual(result, {
|
|
271
|
+
command: 'tmux',
|
|
272
|
+
args: ['new-session', '-s', 'test-session', '--', 'claude', '--continue'],
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
it('generateTmuxSessionName has crc- prefix', () => {
|
|
276
|
+
const name = generateTmuxSessionName('my-session', 'abcdef1234567890');
|
|
277
|
+
assert.ok(name.startsWith('crc-'), `expected crc- prefix, got: ${name}`);
|
|
278
|
+
});
|
|
279
|
+
it('generateTmuxSessionName sanitizes special characters', () => {
|
|
280
|
+
const name = generateTmuxSessionName('feat/auth-flow', 'abcdef1234567890');
|
|
281
|
+
assert.ok(name.startsWith('crc-feat-auth-flow-'), `expected sanitized name, got: ${name}`);
|
|
282
|
+
});
|
|
283
|
+
it('generateTmuxSessionName limits display name to 30 chars', () => {
|
|
284
|
+
const longName = 'a-very-long-display-name-that-exceeds-thirty-characters';
|
|
285
|
+
const id = 'abcdef1234567890';
|
|
286
|
+
const name = generateTmuxSessionName(longName, id);
|
|
287
|
+
// Format is crc-<sanitized up to 30>-<8 char id>
|
|
288
|
+
// The sanitized portion should be at most 30 chars
|
|
289
|
+
const withoutPrefix = name.slice('crc-'.length);
|
|
290
|
+
const parts = withoutPrefix.split('-');
|
|
291
|
+
const idPart = parts[parts.length - 1];
|
|
292
|
+
const displayPart = withoutPrefix.slice(0, withoutPrefix.length - idPart.length - 1);
|
|
293
|
+
assert.ok(displayPart.length <= 30, `display portion should be <= 30 chars, got: ${displayPart.length}`);
|
|
294
|
+
});
|
|
295
|
+
it('generateTmuxSessionName uses 8 chars from the provided id', () => {
|
|
296
|
+
const id = 'abcdef1234567890';
|
|
297
|
+
const name = generateTmuxSessionName('my-session', id);
|
|
298
|
+
assert.ok(name.endsWith(id.slice(0, 8)), `expected name to end with ${id.slice(0, 8)}, got: ${name}`);
|
|
299
|
+
});
|
|
267
300
|
it('agent defaults to claude when not specified', () => {
|
|
268
301
|
const result = sessions.create({
|
|
269
302
|
repoName: 'test-repo',
|
|
@@ -299,4 +332,42 @@ describe('sessions', () => {
|
|
|
299
332
|
assert.ok(session);
|
|
300
333
|
assert.strictEqual(session.agent, 'codex');
|
|
301
334
|
});
|
|
335
|
+
it('useTmux defaults to false when not specified', () => {
|
|
336
|
+
const result = sessions.create({
|
|
337
|
+
repoName: 'test-repo',
|
|
338
|
+
repoPath: '/tmp',
|
|
339
|
+
command: '/bin/echo',
|
|
340
|
+
args: ['hello'],
|
|
341
|
+
});
|
|
342
|
+
createdIds.push(result.id);
|
|
343
|
+
assert.strictEqual(result.useTmux, false);
|
|
344
|
+
assert.strictEqual(result.tmuxSessionName, '');
|
|
345
|
+
});
|
|
346
|
+
it('useTmux is disabled when custom command is provided even if useTmux is true', () => {
|
|
347
|
+
const result = sessions.create({
|
|
348
|
+
repoName: 'test-repo',
|
|
349
|
+
repoPath: '/tmp',
|
|
350
|
+
command: '/bin/echo',
|
|
351
|
+
args: ['hello'],
|
|
352
|
+
useTmux: true,
|
|
353
|
+
});
|
|
354
|
+
createdIds.push(result.id);
|
|
355
|
+
// Custom command sessions should never use tmux
|
|
356
|
+
assert.strictEqual(result.useTmux, false);
|
|
357
|
+
assert.strictEqual(result.tmuxSessionName, '');
|
|
358
|
+
});
|
|
359
|
+
it('list includes useTmux and tmuxSessionName fields', () => {
|
|
360
|
+
const result = sessions.create({
|
|
361
|
+
repoName: 'test-repo',
|
|
362
|
+
repoPath: '/tmp',
|
|
363
|
+
command: '/bin/echo',
|
|
364
|
+
args: ['hello'],
|
|
365
|
+
});
|
|
366
|
+
createdIds.push(result.id);
|
|
367
|
+
const list = sessions.list();
|
|
368
|
+
const session = list.find(s => s.id === result.id);
|
|
369
|
+
assert.ok(session);
|
|
370
|
+
assert.strictEqual(session.useTmux, false);
|
|
371
|
+
assert.strictEqual(session.tmuxSessionName, '');
|
|
372
|
+
});
|
|
302
373
|
});
|