aicodeman 1.2.2 → 1.3.1
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 +264 -90
- package/dist/attachment-magic.d.ts +13 -0
- package/dist/attachment-magic.d.ts.map +1 -1
- package/dist/attachment-magic.js +47 -0
- package/dist/attachment-magic.js.map +1 -1
- package/dist/attachment-registry.d.ts.map +1 -1
- package/dist/attachment-registry.js +13 -2
- package/dist/attachment-registry.js.map +1 -1
- package/dist/config/buffer-limits.d.ts +1 -2
- package/dist/config/buffer-limits.d.ts.map +1 -1
- package/dist/config/buffer-limits.js +1 -2
- package/dist/config/buffer-limits.js.map +1 -1
- package/dist/config/map-limits.d.ts +11 -0
- package/dist/config/map-limits.d.ts.map +1 -1
- package/dist/config/map-limits.js +11 -0
- package/dist/config/map-limits.js.map +1 -1
- package/dist/config/server-timing.d.ts +6 -0
- package/dist/config/server-timing.d.ts.map +1 -1
- package/dist/config/server-timing.js +9 -0
- package/dist/config/server-timing.js.map +1 -1
- package/dist/config/terminal-history.d.ts +11 -6
- package/dist/config/terminal-history.d.ts.map +1 -1
- package/dist/config/terminal-history.js +18 -8
- package/dist/config/terminal-history.js.map +1 -1
- package/dist/cron/cron-input.d.ts +30 -0
- package/dist/cron/cron-input.d.ts.map +1 -0
- package/dist/cron/cron-input.js +7 -0
- package/dist/cron/cron-input.js.map +1 -0
- package/dist/cron/cron-service.d.ts +92 -0
- package/dist/cron/cron-service.d.ts.map +1 -0
- package/dist/cron/cron-service.js +606 -0
- package/dist/cron/cron-service.js.map +1 -0
- package/dist/cron/cron-time.d.ts +29 -0
- package/dist/cron/cron-time.d.ts.map +1 -0
- package/dist/cron/cron-time.js +87 -0
- package/dist/cron/cron-time.js.map +1 -0
- package/dist/document-thumbnailer.d.ts +1 -1
- package/dist/document-thumbnailer.d.ts.map +1 -1
- package/dist/document-thumbnailer.js +11 -2
- package/dist/document-thumbnailer.js.map +1 -1
- package/dist/generated-artifact-attachments.d.ts +26 -0
- package/dist/generated-artifact-attachments.d.ts.map +1 -0
- package/dist/generated-artifact-attachments.js +61 -0
- package/dist/generated-artifact-attachments.js.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/mux-interface.d.ts +31 -5
- package/dist/mux-interface.d.ts.map +1 -1
- package/dist/remote-hosts.d.ts +66 -0
- package/dist/remote-hosts.d.ts.map +1 -0
- package/dist/remote-hosts.js +198 -0
- package/dist/remote-hosts.js.map +1 -0
- package/dist/services/unified-session-service.d.ts +115 -0
- package/dist/services/unified-session-service.d.ts.map +1 -0
- package/dist/services/unified-session-service.js +173 -0
- package/dist/services/unified-session-service.js.map +1 -0
- package/dist/session-cli-builder.d.ts +3 -1
- package/dist/session-cli-builder.d.ts.map +1 -1
- package/dist/session-cli-builder.js +26 -7
- package/dist/session-cli-builder.js.map +1 -1
- package/dist/session-pty-exit-breaker.d.ts +66 -0
- package/dist/session-pty-exit-breaker.d.ts.map +1 -0
- package/dist/session-pty-exit-breaker.js +77 -0
- package/dist/session-pty-exit-breaker.js.map +1 -0
- package/dist/session.d.ts +24 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +94 -11
- package/dist/session.js.map +1 -1
- package/dist/state-store.d.ts +14 -0
- package/dist/state-store.d.ts.map +1 -1
- package/dist/state-store.js +49 -0
- package/dist/state-store.js.map +1 -1
- package/dist/tmux-manager.d.ts +94 -8
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +222 -26
- package/dist/tmux-manager.js.map +1 -1
- package/dist/types/api.d.ts +11 -0
- package/dist/types/api.d.ts.map +1 -1
- package/dist/types/api.js.map +1 -1
- package/dist/types/app-state.d.ts +5 -0
- package/dist/types/app-state.d.ts.map +1 -1
- package/dist/types/app-state.js.map +1 -1
- package/dist/types/cron.d.ts +88 -0
- package/dist/types/cron.d.ts.map +1 -0
- package/dist/types/cron.js +13 -0
- package/dist/types/cron.js.map +1 -0
- package/dist/types/session.d.ts +57 -0
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js.map +1 -1
- package/dist/web/heic-jpeg-converter.d.ts +24 -0
- package/dist/web/heic-jpeg-converter.d.ts.map +1 -0
- package/dist/web/heic-jpeg-converter.js +76 -0
- package/dist/web/heic-jpeg-converter.js.map +1 -0
- package/dist/web/heic-jpeg-worker.d.ts +44 -0
- package/dist/web/heic-jpeg-worker.d.ts.map +1 -0
- package/dist/web/heic-jpeg-worker.js +78 -0
- package/dist/web/heic-jpeg-worker.js.map +1 -0
- package/dist/web/ports/cron-port.d.ts +9 -0
- package/dist/web/ports/cron-port.d.ts.map +1 -0
- package/dist/web/ports/cron-port.js +6 -0
- package/dist/web/ports/cron-port.js.map +1 -0
- package/dist/web/ports/index.d.ts +1 -0
- package/dist/web/ports/index.d.ts.map +1 -1
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/app.b0534652.js +38 -0
- package/dist/web/public/app.b0534652.js.br +0 -0
- package/dist/web/public/app.b0534652.js.gz +0 -0
- package/dist/web/public/{constants.1c779517.js → constants.5b18ed64.js} +65 -0
- package/dist/web/public/constants.5b18ed64.js.br +0 -0
- package/dist/web/public/constants.5b18ed64.js.gz +0 -0
- package/dist/web/public/cron-ui.js +328 -0
- package/dist/web/public/cron-ui.js.br +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
- package/dist/web/public/index.html +313 -25
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.63794d0b.js +1 -0
- package/dist/web/public/input-cjk.63794d0b.js.br +0 -0
- package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.3f91acef.js.gz +0 -0
- package/dist/web/public/mobile-handlers.b5a667ef.js.gz +0 -0
- package/dist/web/public/mobile.0644e3ac.css +1 -0
- package/dist/web/public/mobile.0644e3ac.css.br +0 -0
- package/dist/web/public/mobile.0644e3ac.css.gz +0 -0
- package/dist/web/public/notification-manager.ca9dc40d.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.772e92c9.js → panels-ui.ca03853f.js} +25 -17
- package/dist/web/public/panels-ui.ca03853f.js.br +0 -0
- package/dist/web/public/panels-ui.ca03853f.js.gz +0 -0
- package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
- package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
- package/dist/web/public/respawn-ui.2d249da9.js.gz +0 -0
- package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
- package/dist/web/public/session-ui.b51814ef.js +49 -0
- package/dist/web/public/session-ui.b51814ef.js.br +0 -0
- package/dist/web/public/session-ui.b51814ef.js.gz +0 -0
- package/dist/web/public/settings-ui.6c67c125.js +67 -0
- package/dist/web/public/settings-ui.6c67c125.js.br +0 -0
- package/dist/web/public/settings-ui.6c67c125.js.gz +0 -0
- package/dist/web/public/styles.be7d626b.css +1 -0
- package/dist/web/public/styles.be7d626b.css.br +0 -0
- package/dist/web/public/styles.be7d626b.css.gz +0 -0
- package/dist/web/public/subagent-windows.69b60c35.js.gz +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/terminal-ui.dbf141fa.js +3 -0
- package/dist/web/public/terminal-ui.dbf141fa.js.br +0 -0
- package/dist/web/public/terminal-ui.dbf141fa.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
- package/dist/web/routes/case-routes.d.ts.map +1 -1
- package/dist/web/routes/case-routes.js +115 -1
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/cron-routes.d.ts +11 -0
- package/dist/web/routes/cron-routes.d.ts.map +1 -0
- package/dist/web/routes/cron-routes.js +70 -0
- package/dist/web/routes/cron-routes.js.map +1 -0
- package/dist/web/routes/index.d.ts +1 -0
- package/dist/web/routes/index.d.ts.map +1 -1
- package/dist/web/routes/index.js +1 -0
- package/dist/web/routes/index.js.map +1 -1
- package/dist/web/routes/respawn-routes.d.ts.map +1 -1
- package/dist/web/routes/respawn-routes.js +3 -0
- package/dist/web/routes/respawn-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +610 -68
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/ws-routes.d.ts.map +1 -1
- package/dist/web/routes/ws-routes.js +56 -17
- package/dist/web/routes/ws-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +113 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +145 -0
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts +8 -2
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +75 -13
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-listener-wiring.d.ts +5 -1
- package/dist/web/session-listener-wiring.d.ts.map +1 -1
- package/dist/web/session-listener-wiring.js +23 -1
- package/dist/web/session-listener-wiring.js.map +1 -1
- package/dist/web/sse-events.d.ts +15 -0
- package/dist/web/sse-events.d.ts.map +1 -1
- package/dist/web/sse-events.js +17 -0
- package/dist/web/sse-events.js.map +1 -1
- package/dist/web/ws-connection-registry.d.ts +77 -0
- package/dist/web/ws-connection-registry.d.ts.map +1 -0
- package/dist/web/ws-connection-registry.js +99 -0
- package/dist/web/ws-connection-registry.js.map +1 -0
- package/package.json +3 -1
- package/dist/web/public/app.a8d73c32.js +0 -38
- package/dist/web/public/app.a8d73c32.js.br +0 -0
- package/dist/web/public/app.a8d73c32.js.gz +0 -0
- package/dist/web/public/constants.1c779517.js.br +0 -0
- package/dist/web/public/constants.1c779517.js.gz +0 -0
- package/dist/web/public/input-cjk.4e950e0a.js +0 -1
- package/dist/web/public/input-cjk.4e950e0a.js.br +0 -0
- package/dist/web/public/input-cjk.4e950e0a.js.gz +0 -0
- package/dist/web/public/mobile.8a9aff7a.css +0 -1
- package/dist/web/public/mobile.8a9aff7a.css.br +0 -0
- package/dist/web/public/mobile.8a9aff7a.css.gz +0 -0
- package/dist/web/public/panels-ui.772e92c9.js.br +0 -0
- package/dist/web/public/panels-ui.772e92c9.js.gz +0 -0
- package/dist/web/public/session-ui.da3e53f2.js +0 -36
- package/dist/web/public/session-ui.da3e53f2.js.br +0 -0
- package/dist/web/public/session-ui.da3e53f2.js.gz +0 -0
- package/dist/web/public/settings-ui.38feb956.js +0 -61
- package/dist/web/public/settings-ui.38feb956.js.br +0 -0
- package/dist/web/public/settings-ui.38feb956.js.gz +0 -0
- package/dist/web/public/styles.1b3e6147.css +0 -1
- package/dist/web/public/styles.1b3e6147.css.br +0 -0
- package/dist/web/public/styles.1b3e6147.css.gz +0 -0
- package/dist/web/public/terminal-ui.865f21f6.js +0 -3
- package/dist/web/public/terminal-ui.865f21f6.js.br +0 -0
- package/dist/web/public/terminal-ui.865f21f6.js.gz +0 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { exec } from 'node:child_process';
|
|
6
|
+
import { promisify } from 'node:util';
|
|
7
|
+
const execAsync = promisify(exec);
|
|
8
|
+
const REMOTE_HOSTS_FILE = 'remote-hosts.json';
|
|
9
|
+
const REMOTE_CASES_FILE = 'remote-cases.json';
|
|
10
|
+
export function remoteHostsPath(configDir) {
|
|
11
|
+
return join(configDir, REMOTE_HOSTS_FILE);
|
|
12
|
+
}
|
|
13
|
+
export function remoteCasesPath(configDir) {
|
|
14
|
+
return join(configDir, REMOTE_CASES_FILE);
|
|
15
|
+
}
|
|
16
|
+
async function readJsonArray(path) {
|
|
17
|
+
try {
|
|
18
|
+
const raw = await fs.readFile(path, 'utf-8');
|
|
19
|
+
const parsed = JSON.parse(raw);
|
|
20
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function writeJsonArray(configDir, path, value) {
|
|
27
|
+
if (!existsSync(configDir))
|
|
28
|
+
mkdirSync(configDir, { recursive: true });
|
|
29
|
+
await fs.writeFile(path, JSON.stringify(value, null, 2));
|
|
30
|
+
}
|
|
31
|
+
export async function readRemoteHosts(configDir) {
|
|
32
|
+
return readJsonArray(remoteHostsPath(configDir));
|
|
33
|
+
}
|
|
34
|
+
export async function writeRemoteHosts(configDir, hosts) {
|
|
35
|
+
await writeJsonArray(configDir, remoteHostsPath(configDir), hosts);
|
|
36
|
+
}
|
|
37
|
+
export async function readRemoteCases(configDir) {
|
|
38
|
+
return readJsonArray(remoteCasesPath(configDir));
|
|
39
|
+
}
|
|
40
|
+
export async function writeRemoteCases(configDir, cases) {
|
|
41
|
+
await writeJsonArray(configDir, remoteCasesPath(configDir), cases);
|
|
42
|
+
}
|
|
43
|
+
export function defaultRemoteCommandForMode(mode) {
|
|
44
|
+
const commands = {
|
|
45
|
+
shell: 'exec bash -l',
|
|
46
|
+
// Mirror the LOCAL claude default so the remote agent runs non-interactively
|
|
47
|
+
// (no trust-folder/permission prompt that nothing on the remote answers). The
|
|
48
|
+
// per-host `commands.claude` override stays the escape hatch.
|
|
49
|
+
claude: 'exec claude --dangerously-skip-permissions',
|
|
50
|
+
opencode: 'exec opencode',
|
|
51
|
+
codex: 'exec codex',
|
|
52
|
+
gemini: 'exec gemini',
|
|
53
|
+
};
|
|
54
|
+
return commands[mode] || commands.shell;
|
|
55
|
+
}
|
|
56
|
+
export function remoteSshTarget(host) {
|
|
57
|
+
return `${host.username}@${host.host}`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* POSIX single-quote shell-escaping (end-quote, escaped-quote, restart-quote).
|
|
61
|
+
* Mirrors the helper in tmux-manager.ts so a value with spaces/metachars stays a
|
|
62
|
+
* single shell token. Used here for identity paths and `-o KEY=VALUE` options.
|
|
63
|
+
*/
|
|
64
|
+
function shellescape(str) {
|
|
65
|
+
return "'" + str.replace(/'/g, "'\\''") + "'";
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Expand a leading `~` or `$HOME` in an identity path to an absolute path.
|
|
69
|
+
*
|
|
70
|
+
* ssh does NOT expand `~` inside `-i` (the shell would, but we shellescape the
|
|
71
|
+
* value into a single quoted token so the shell never sees it). So we expand at
|
|
72
|
+
* build time, before escaping. Non-`~`/`$HOME` paths are returned unchanged.
|
|
73
|
+
*/
|
|
74
|
+
function expandIdentityPath(identityFile) {
|
|
75
|
+
if (identityFile === '~')
|
|
76
|
+
return homedir();
|
|
77
|
+
if (identityFile.startsWith('~/'))
|
|
78
|
+
return join(homedir(), identityFile.slice(2));
|
|
79
|
+
if (identityFile === '$HOME')
|
|
80
|
+
return homedir();
|
|
81
|
+
if (identityFile.startsWith('$HOME/'))
|
|
82
|
+
return join(homedir(), identityFile.slice('$HOME/'.length));
|
|
83
|
+
return identityFile;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* COD-107 — build the ordered, shell-safe ssh CONNECTION tokens shared by both
|
|
87
|
+
* the durable-launch command (`buildRemoteLaunchCommand`) and the tmux
|
|
88
|
+
* prerequisite probe (`buildRemoteTmuxCheckCommand`), so the prereq check and
|
|
89
|
+
* the real launch connect with IDENTICAL options (they can't drift).
|
|
90
|
+
*
|
|
91
|
+
* Returns the leading tokens of an ssh command line (NOT including `-t`, the
|
|
92
|
+
* target, or any remote command). Order:
|
|
93
|
+
* ssh -o BatchMode=yes
|
|
94
|
+
* [-o ConnectTimeout=10] (default; suppressed if extraSshOptions sets it)
|
|
95
|
+
* [-p <port>]
|
|
96
|
+
* [-i <abs-identity>] (~/$HOME expanded, then shellescaped)
|
|
97
|
+
* [-J <jumpHost>] (shellescaped, single token)
|
|
98
|
+
* [-o ProxyCommand=nc -X 5 -x <socks> %h %p] (ONE shellescaped -o token)
|
|
99
|
+
* [-o <KEY=VALUE>] … (each extra option, shellescaped)
|
|
100
|
+
*
|
|
101
|
+
* Escaping notes (the risky part):
|
|
102
|
+
* - The ProxyCommand is emitted as a single shellescaped `-o KEY=VALUE`, so the
|
|
103
|
+
* whole value (spaces + `%h`/`%p`) reaches ssh as one argument and `%h %p`
|
|
104
|
+
* survive verbatim — ssh expands them to the real host/port, not the shell.
|
|
105
|
+
* - A default `-o ConnectTimeout=10` bounds the wait on an unreachable/blackholed
|
|
106
|
+
* host (else the pane hangs on the OS TCP timeout). It is omitted when the
|
|
107
|
+
* operator already set ConnectTimeout via extraSshOptions, so their value wins.
|
|
108
|
+
*/
|
|
109
|
+
export function buildSshConnectionArgs(remote) {
|
|
110
|
+
const parts = ['ssh', '-o BatchMode=yes'];
|
|
111
|
+
const hasConnectTimeout = (remote.extraSshOptions ?? []).some((opt) => /^ConnectTimeout=/i.test(opt));
|
|
112
|
+
if (!hasConnectTimeout)
|
|
113
|
+
parts.push('-o ConnectTimeout=10');
|
|
114
|
+
if (remote.port)
|
|
115
|
+
parts.push(`-p ${remote.port}`);
|
|
116
|
+
if (remote.identityFile)
|
|
117
|
+
parts.push(`-i ${shellescape(expandIdentityPath(remote.identityFile))}`);
|
|
118
|
+
if (remote.jumpHost)
|
|
119
|
+
parts.push(`-J ${shellescape(remote.jumpHost)}`);
|
|
120
|
+
if (remote.socksProxy) {
|
|
121
|
+
parts.push(`-o ${shellescape(`ProxyCommand=nc -X 5 -x ${remote.socksProxy} %h %p`)}`);
|
|
122
|
+
}
|
|
123
|
+
for (const opt of remote.extraSshOptions ?? []) {
|
|
124
|
+
parts.push(`-o ${shellescape(opt)}`);
|
|
125
|
+
}
|
|
126
|
+
return parts;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* COD-104 — build the SSH command that checks the remote host has tmux.
|
|
130
|
+
*
|
|
131
|
+
* Durable remote sessions run the agent inside a tmux server ON the remote host
|
|
132
|
+
* (`tmux -L codeman new-session -A …`), so tmux is now a hard prerequisite there.
|
|
133
|
+
* `command -v tmux` exits 0 (and prints the path) when tmux is installed.
|
|
134
|
+
*
|
|
135
|
+
* COD-107 — connects with the SAME options as the real launch
|
|
136
|
+
* (`buildSshConnectionArgs`) so a proxied/custom-port/identity host that the
|
|
137
|
+
* launch can reach also passes the prereq probe (and vice-versa).
|
|
138
|
+
*/
|
|
139
|
+
export function buildRemoteTmuxCheckCommand(host) {
|
|
140
|
+
// ConnectTimeout is now a default of buildSshConnectionArgs (shared with the launch).
|
|
141
|
+
return [...buildSshConnectionArgs(host), remoteSshTarget(host), "'command -v tmux'"].join(' ');
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* COD-104 — verify the remote host has tmux installed (required for durable
|
|
145
|
+
* remote sessions). Returns a structured result with a clear, user-facing error
|
|
146
|
+
* when tmux is missing or the host is unreachable. Never throws.
|
|
147
|
+
*/
|
|
148
|
+
export async function checkRemoteTmuxAvailable(host) {
|
|
149
|
+
const command = buildRemoteTmuxCheckCommand(host);
|
|
150
|
+
try {
|
|
151
|
+
const { stdout } = await execAsync(command, { timeout: 15_000 });
|
|
152
|
+
const tmuxPath = stdout.trim();
|
|
153
|
+
if (!tmuxPath) {
|
|
154
|
+
return {
|
|
155
|
+
ok: false,
|
|
156
|
+
error: `remote host ${host.host} needs tmux installed for durable remote sessions`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return { ok: true, tmuxPath };
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
const stderr = err && typeof err === 'object' && 'stderr' in err ? String(err.stderr ?? '') : '';
|
|
163
|
+
// `command -v tmux` exits non-zero when tmux is absent (no stderr); a real
|
|
164
|
+
// connection failure surfaces ssh diagnostics on stderr.
|
|
165
|
+
if (stderr.trim()) {
|
|
166
|
+
return {
|
|
167
|
+
ok: false,
|
|
168
|
+
error: `could not verify tmux on remote host ${host.host}: ${stderr.trim()}`,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
ok: false,
|
|
173
|
+
error: `remote host ${host.host} needs tmux installed for durable remote sessions`,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
export function remoteDisplayPath(remote) {
|
|
178
|
+
const path = 'remotePath' in remote ? remote.remotePath : remote.path;
|
|
179
|
+
return `${remote.username}@${remote.host}:${path}`;
|
|
180
|
+
}
|
|
181
|
+
export function toSessionRemote(host, remoteCase) {
|
|
182
|
+
return {
|
|
183
|
+
hostId: host.id,
|
|
184
|
+
label: host.label,
|
|
185
|
+
host: host.host,
|
|
186
|
+
username: host.username,
|
|
187
|
+
port: host.port,
|
|
188
|
+
remotePath: remoteCase.remotePath,
|
|
189
|
+
commands: host.commands,
|
|
190
|
+
// COD-107 — carry the advanced SSH options from host config into the session
|
|
191
|
+
// so the launch/prereq commands connect the same way the operator configured.
|
|
192
|
+
identityFile: host.identityFile,
|
|
193
|
+
socksProxy: host.socksProxy,
|
|
194
|
+
jumpHost: host.jumpHost,
|
|
195
|
+
extraSshOptions: host.extraSshOptions,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=remote-hosts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-hosts.js","sourceRoot":"","sources":["../src/remote-hosts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Pure merge/filter logic for the unified session list (COD-121).
|
|
3
|
+
*
|
|
4
|
+
* Combines four read-only views of a session — live (in-memory `Session`),
|
|
5
|
+
* persisted (`state.json`), transcript history (`~/.claude/projects`), and the
|
|
6
|
+
* lifecycle audit log — plus mux process stats, into one de-duplicated list
|
|
7
|
+
* keyed by sessionId. Transcript-history rows are keyed by the Claude
|
|
8
|
+
* conversation UUID (the `.jsonl` filename stem), which diverges from the
|
|
9
|
+
* Codeman id for resumed sessions — an alias map (claudeSessionId → Codeman id,
|
|
10
|
+
* built from the live/persisted views) folds them into the owning session item.
|
|
11
|
+
* Higher-precedence sources overwrite scalar fields when present
|
|
12
|
+
* (history < lifecycle < persisted < live), while the `sources` array
|
|
13
|
+
* always accumulates every contributing view. A "meaningfulness floor" drops
|
|
14
|
+
* noise (bare lifecycle/mux-only rows with no name and no first prompt).
|
|
15
|
+
*
|
|
16
|
+
* PURE: no fs/IO and no node imports. All IO happens in the route that feeds
|
|
17
|
+
* this module its inputs, which keeps the merge/sort/filter behavior unit-testable.
|
|
18
|
+
*/
|
|
19
|
+
export type UnifiedSessionItem = {
|
|
20
|
+
sessionId: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
mode?: string;
|
|
23
|
+
status?: string;
|
|
24
|
+
isWorking?: boolean;
|
|
25
|
+
workingDir?: string;
|
|
26
|
+
createdAt?: number;
|
|
27
|
+
lastActivityAt?: number;
|
|
28
|
+
claudeSessionId?: string;
|
|
29
|
+
firstPrompt?: string;
|
|
30
|
+
sizeBytes?: number;
|
|
31
|
+
projectKey?: string;
|
|
32
|
+
remote?: boolean;
|
|
33
|
+
sources: string[];
|
|
34
|
+
stats?: {
|
|
35
|
+
memoryMB: number;
|
|
36
|
+
cpuPercent: number;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
/** Live in-memory session view (subset of `Session.toState()`). */
|
|
40
|
+
export type LiveSessionInput = {
|
|
41
|
+
id: string;
|
|
42
|
+
name?: string;
|
|
43
|
+
mode?: string;
|
|
44
|
+
status?: string;
|
|
45
|
+
isWorking?: boolean;
|
|
46
|
+
workingDir?: string;
|
|
47
|
+
createdAt?: number;
|
|
48
|
+
lastActivityAt?: number;
|
|
49
|
+
claudeSessionId?: string;
|
|
50
|
+
};
|
|
51
|
+
/** Persisted session view (subset of `SessionState`). */
|
|
52
|
+
export type PersistedSessionInput = {
|
|
53
|
+
id: string;
|
|
54
|
+
name?: string;
|
|
55
|
+
mode?: string;
|
|
56
|
+
status?: string;
|
|
57
|
+
workingDir?: string;
|
|
58
|
+
createdAt?: number;
|
|
59
|
+
lastActivityAt?: number;
|
|
60
|
+
/** Claude conversation ID this session resumes (`SessionState.resumeSessionId`). */
|
|
61
|
+
claudeSessionId?: string;
|
|
62
|
+
};
|
|
63
|
+
/** Lifecycle audit-log view. Entries are expected NEWEST-first (the order `SessionLifecycleLog.query()` returns). */
|
|
64
|
+
export type LifecycleInput = {
|
|
65
|
+
sessionId: string;
|
|
66
|
+
name?: string;
|
|
67
|
+
mode?: string;
|
|
68
|
+
ts: number;
|
|
69
|
+
event?: string;
|
|
70
|
+
};
|
|
71
|
+
/** Transcript-history view (one `.jsonl` per session). */
|
|
72
|
+
export type HistoryInput = {
|
|
73
|
+
sessionId: string;
|
|
74
|
+
workingDir: string;
|
|
75
|
+
sizeBytes: number;
|
|
76
|
+
lastModified: string;
|
|
77
|
+
firstPrompt?: string;
|
|
78
|
+
projectKey?: string;
|
|
79
|
+
};
|
|
80
|
+
/** Mux process-stat view. */
|
|
81
|
+
export type MuxStatInput = {
|
|
82
|
+
sessionId: string;
|
|
83
|
+
muxName?: string;
|
|
84
|
+
mode?: string;
|
|
85
|
+
stats?: {
|
|
86
|
+
memoryMB: number;
|
|
87
|
+
cpuPercent: number;
|
|
88
|
+
};
|
|
89
|
+
remote?: boolean;
|
|
90
|
+
};
|
|
91
|
+
export type UnifiedSources = {
|
|
92
|
+
live?: LiveSessionInput[];
|
|
93
|
+
persisted?: PersistedSessionInput[];
|
|
94
|
+
lifecycle?: LifecycleInput[];
|
|
95
|
+
history?: HistoryInput[];
|
|
96
|
+
mux?: MuxStatInput[];
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Merge all source views into one list, applying precedence
|
|
100
|
+
* (history → lifecycle → persisted → live) and the meaningfulness floor.
|
|
101
|
+
*/
|
|
102
|
+
export declare function mergeUnifiedSessions(sources: UnifiedSources): UnifiedSessionItem[];
|
|
103
|
+
/**
|
|
104
|
+
* Case-insensitive substring filter (name + firstPrompt + workingDir + sessionId)
|
|
105
|
+
* with offset/limit paging. `total` is the filtered count BEFORE paging.
|
|
106
|
+
*/
|
|
107
|
+
export declare function filterAndPaginate(items: UnifiedSessionItem[], opts: {
|
|
108
|
+
q?: string;
|
|
109
|
+
offset?: number;
|
|
110
|
+
limit?: number;
|
|
111
|
+
}): {
|
|
112
|
+
sessions: UnifiedSessionItem[];
|
|
113
|
+
total: number;
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=unified-session-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unified-session-service.d.ts","sourceRoot":"","sources":["../../src/services/unified-session-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Pure merge/filter logic for the unified session list (COD-121).
|
|
3
|
+
*
|
|
4
|
+
* Combines four read-only views of a session — live (in-memory `Session`),
|
|
5
|
+
* persisted (`state.json`), transcript history (`~/.claude/projects`), and the
|
|
6
|
+
* lifecycle audit log — plus mux process stats, into one de-duplicated list
|
|
7
|
+
* keyed by sessionId. Transcript-history rows are keyed by the Claude
|
|
8
|
+
* conversation UUID (the `.jsonl` filename stem), which diverges from the
|
|
9
|
+
* Codeman id for resumed sessions — an alias map (claudeSessionId → Codeman id,
|
|
10
|
+
* built from the live/persisted views) folds them into the owning session item.
|
|
11
|
+
* Higher-precedence sources overwrite scalar fields when present
|
|
12
|
+
* (history < lifecycle < persisted < live), while the `sources` array
|
|
13
|
+
* always accumulates every contributing view. A "meaningfulness floor" drops
|
|
14
|
+
* noise (bare lifecycle/mux-only rows with no name and no first prompt).
|
|
15
|
+
*
|
|
16
|
+
* PURE: no fs/IO and no node imports. All IO happens in the route that feeds
|
|
17
|
+
* this module its inputs, which keeps the merge/sort/filter behavior unit-testable.
|
|
18
|
+
*/
|
|
19
|
+
/** Push a source tag onto an item exactly once. */
|
|
20
|
+
function addSource(item, source) {
|
|
21
|
+
if (!item.sources.includes(source))
|
|
22
|
+
item.sources.push(source);
|
|
23
|
+
}
|
|
24
|
+
/** Get-or-create the accumulator item for a sessionId. */
|
|
25
|
+
function ensureItem(map, sessionId) {
|
|
26
|
+
let item = map.get(sessionId);
|
|
27
|
+
if (!item) {
|
|
28
|
+
item = { sessionId, sources: [] };
|
|
29
|
+
map.set(sessionId, item);
|
|
30
|
+
}
|
|
31
|
+
return item;
|
|
32
|
+
}
|
|
33
|
+
/** Overwrite a scalar field only when the incoming value is defined. */
|
|
34
|
+
function overwrite(item, key, value) {
|
|
35
|
+
if (value !== undefined)
|
|
36
|
+
item[key] = value;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Merge all source views into one list, applying precedence
|
|
40
|
+
* (history → lifecycle → persisted → live) and the meaningfulness floor.
|
|
41
|
+
*/
|
|
42
|
+
export function mergeUnifiedSessions(sources) {
|
|
43
|
+
const map = new Map();
|
|
44
|
+
// Alias map: Claude conversation UUID → owning Codeman session id. Resumed
|
|
45
|
+
// (claudeSessionId = resumeSessionId != id) and /clear-respawned sessions
|
|
46
|
+
// would otherwise surface twice — once as a live/persisted row and once as a
|
|
47
|
+
// separate history-only row keyed by the conversation UUID. Live wins over
|
|
48
|
+
// persisted on conflicting entries (registered last).
|
|
49
|
+
const aliasToOwner = new Map();
|
|
50
|
+
for (const p of sources.persisted ?? []) {
|
|
51
|
+
if (p.claudeSessionId !== undefined && p.claudeSessionId !== p.id)
|
|
52
|
+
aliasToOwner.set(p.claudeSessionId, p.id);
|
|
53
|
+
}
|
|
54
|
+
for (const v of sources.live ?? []) {
|
|
55
|
+
if (v.claudeSessionId !== undefined && v.claudeSessionId !== v.id)
|
|
56
|
+
aliasToOwner.set(v.claudeSessionId, v.id);
|
|
57
|
+
}
|
|
58
|
+
const resolveId = (sessionId) => aliasToOwner.get(sessionId) ?? sessionId;
|
|
59
|
+
// 1) history (lowest precedence; keys resolve through the alias map)
|
|
60
|
+
for (const h of sources.history ?? []) {
|
|
61
|
+
const item = ensureItem(map, resolveId(h.sessionId));
|
|
62
|
+
addSource(item, 'history');
|
|
63
|
+
overwrite(item, 'workingDir', h.workingDir);
|
|
64
|
+
overwrite(item, 'sizeBytes', h.sizeBytes);
|
|
65
|
+
overwrite(item, 'firstPrompt', h.firstPrompt);
|
|
66
|
+
overwrite(item, 'projectKey', h.projectKey);
|
|
67
|
+
const ms = Date.parse(h.lastModified);
|
|
68
|
+
if (!Number.isNaN(ms) && item.lastActivityAt === undefined)
|
|
69
|
+
item.lastActivityAt = ms;
|
|
70
|
+
}
|
|
71
|
+
// 2) lifecycle — entries arrive NEWEST-first, so first-seen wins for
|
|
72
|
+
// name/mode (mirrors the lastActivityAt guard); unconditional overwrites
|
|
73
|
+
// would leave the OLDEST entry in the window (stale name/mode) standing.
|
|
74
|
+
for (const l of sources.lifecycle ?? []) {
|
|
75
|
+
const item = ensureItem(map, resolveId(l.sessionId));
|
|
76
|
+
addSource(item, 'lifecycle');
|
|
77
|
+
if (item.name === undefined)
|
|
78
|
+
overwrite(item, 'name', l.name);
|
|
79
|
+
if (item.mode === undefined)
|
|
80
|
+
overwrite(item, 'mode', l.mode);
|
|
81
|
+
if (item.lastActivityAt === undefined && typeof l.ts === 'number')
|
|
82
|
+
item.lastActivityAt = l.ts;
|
|
83
|
+
}
|
|
84
|
+
// 3) persisted
|
|
85
|
+
for (const p of sources.persisted ?? []) {
|
|
86
|
+
const item = ensureItem(map, p.id);
|
|
87
|
+
addSource(item, 'persisted');
|
|
88
|
+
overwrite(item, 'name', p.name);
|
|
89
|
+
overwrite(item, 'mode', p.mode);
|
|
90
|
+
overwrite(item, 'status', p.status);
|
|
91
|
+
overwrite(item, 'workingDir', p.workingDir);
|
|
92
|
+
overwrite(item, 'createdAt', p.createdAt);
|
|
93
|
+
overwrite(item, 'lastActivityAt', p.lastActivityAt);
|
|
94
|
+
}
|
|
95
|
+
// 4) live (highest precedence)
|
|
96
|
+
for (const v of sources.live ?? []) {
|
|
97
|
+
const item = ensureItem(map, v.id);
|
|
98
|
+
addSource(item, 'live');
|
|
99
|
+
overwrite(item, 'name', v.name);
|
|
100
|
+
overwrite(item, 'mode', v.mode);
|
|
101
|
+
overwrite(item, 'status', v.status);
|
|
102
|
+
overwrite(item, 'isWorking', v.isWorking);
|
|
103
|
+
overwrite(item, 'workingDir', v.workingDir);
|
|
104
|
+
overwrite(item, 'createdAt', v.createdAt);
|
|
105
|
+
overwrite(item, 'lastActivityAt', v.lastActivityAt);
|
|
106
|
+
overwrite(item, 'claudeSessionId', v.claudeSessionId);
|
|
107
|
+
}
|
|
108
|
+
// 5) mux stats + remote flag (create item if mux-only)
|
|
109
|
+
for (const m of sources.mux ?? []) {
|
|
110
|
+
const item = ensureItem(map, m.sessionId);
|
|
111
|
+
addSource(item, 'mux');
|
|
112
|
+
overwrite(item, 'mode', m.mode);
|
|
113
|
+
if (m.stats)
|
|
114
|
+
item.stats = m.stats;
|
|
115
|
+
if (m.remote !== undefined)
|
|
116
|
+
item.remote = m.remote;
|
|
117
|
+
}
|
|
118
|
+
// Meaningfulness floor: keep real rows, drop bare lifecycle/mux-only noise.
|
|
119
|
+
const kept = [];
|
|
120
|
+
for (const item of map.values()) {
|
|
121
|
+
const isReal = item.sources.includes('live') ||
|
|
122
|
+
item.sources.includes('persisted') ||
|
|
123
|
+
item.sources.includes('history') ||
|
|
124
|
+
(item.firstPrompt !== undefined && item.firstPrompt !== '');
|
|
125
|
+
if (isReal)
|
|
126
|
+
kept.push(item);
|
|
127
|
+
}
|
|
128
|
+
// Stable sort: lastActivityAt desc (undefined last), createdAt desc, sessionId asc.
|
|
129
|
+
kept.sort((a, b) => {
|
|
130
|
+
const la = a.lastActivityAt;
|
|
131
|
+
const lb = b.lastActivityAt;
|
|
132
|
+
if (la !== lb) {
|
|
133
|
+
if (la === undefined)
|
|
134
|
+
return 1;
|
|
135
|
+
if (lb === undefined)
|
|
136
|
+
return -1;
|
|
137
|
+
return lb - la;
|
|
138
|
+
}
|
|
139
|
+
const ca = a.createdAt;
|
|
140
|
+
const cb = b.createdAt;
|
|
141
|
+
if (ca !== cb) {
|
|
142
|
+
if (ca === undefined)
|
|
143
|
+
return 1;
|
|
144
|
+
if (cb === undefined)
|
|
145
|
+
return -1;
|
|
146
|
+
return cb - ca;
|
|
147
|
+
}
|
|
148
|
+
return a.sessionId < b.sessionId ? -1 : a.sessionId > b.sessionId ? 1 : 0;
|
|
149
|
+
});
|
|
150
|
+
return kept;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Case-insensitive substring filter (name + firstPrompt + workingDir + sessionId)
|
|
154
|
+
* with offset/limit paging. `total` is the filtered count BEFORE paging.
|
|
155
|
+
*/
|
|
156
|
+
export function filterAndPaginate(items, opts) {
|
|
157
|
+
const q = (opts.q ?? '').trim().toLowerCase();
|
|
158
|
+
const filtered = q
|
|
159
|
+
? items.filter((it) => {
|
|
160
|
+
const hay = [it.name, it.firstPrompt, it.workingDir, it.sessionId]
|
|
161
|
+
.filter((v) => typeof v === 'string')
|
|
162
|
+
.join(' ')
|
|
163
|
+
.toLowerCase();
|
|
164
|
+
return hay.includes(q);
|
|
165
|
+
})
|
|
166
|
+
: items;
|
|
167
|
+
const total = filtered.length;
|
|
168
|
+
const offset = Math.max(0, Math.floor(opts.offset ?? 0));
|
|
169
|
+
const limit = Math.min(500, Math.max(1, Math.floor(opts.limit ?? 100)));
|
|
170
|
+
const sessions = filtered.slice(offset, offset + limit);
|
|
171
|
+
return { sessions, total };
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=unified-session-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unified-session-service.js","sourceRoot":"","sources":["../../src/services/unified-session-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -58,9 +58,11 @@ export declare function buildClaudeEnv(sessionId: string): Record<string, string
|
|
|
58
58
|
* Lighter than buildClaudeEnv — no PATH augmentation or Codeman vars needed
|
|
59
59
|
* since the mux session already has those set.
|
|
60
60
|
*
|
|
61
|
+
* @param truecolorEnabled - When true, set COLORTERM=truecolor (COD-75 opt-in);
|
|
62
|
+
* otherwise leave COLORTERM unset. Mirrors buildEnvExports() so both paths agree.
|
|
61
63
|
* @returns Environment variables object for pty.spawn
|
|
62
64
|
*/
|
|
63
|
-
export declare function buildMuxAttachEnv(): Record<string, string | undefined>;
|
|
65
|
+
export declare function buildMuxAttachEnv(truecolorEnabled?: boolean): Record<string, string | undefined>;
|
|
64
66
|
/**
|
|
65
67
|
* Build environment variables for a direct shell session (non-mux fallback).
|
|
66
68
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-cli-builder.d.ts","sourceRoot":"","sources":["../src/session-cli-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAyB1D;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,EAAE,CAGjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,UAAU,EACtB,KAAK,CAAC,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE,WAAW,GACnB,MAAM,EAAE,CAKV;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAOxE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"session-cli-builder.d.ts","sourceRoot":"","sources":["../src/session-cli-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAyB1D;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,EAAE,CAGjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,UAAU,EACtB,KAAK,CAAC,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE,WAAW,GACnB,MAAM,EAAE,CAKV;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAOxE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAmBpF;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAsBhG;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAYnF"}
|
|
@@ -91,14 +91,12 @@ export function buildPromptArgs(prompt, model) {
|
|
|
91
91
|
* @returns Environment variables object for pty.spawn
|
|
92
92
|
*/
|
|
93
93
|
export function buildClaudeEnv(sessionId) {
|
|
94
|
-
|
|
94
|
+
const env = {
|
|
95
95
|
...process.env,
|
|
96
96
|
LANG: 'en_US.UTF-8',
|
|
97
97
|
LC_ALL: 'en_US.UTF-8',
|
|
98
98
|
PATH: getAugmentedPath(),
|
|
99
99
|
TERM: 'xterm-256color',
|
|
100
|
-
COLORTERM: undefined,
|
|
101
|
-
CLAUDECODE: undefined,
|
|
102
100
|
// Inform Claude it's running within Codeman (helps prevent self-termination)
|
|
103
101
|
CODEMAN_MUX: '1',
|
|
104
102
|
CODEMAN_SESSION_ID: sessionId,
|
|
@@ -106,23 +104,44 @@ export function buildClaudeEnv(sessionId) {
|
|
|
106
104
|
// Path only (not the secret value) — hook curls cat it at execution time (COD-54)
|
|
107
105
|
CODEMAN_HOOK_SECRET_FILE: dataPath('hook-secret'),
|
|
108
106
|
};
|
|
107
|
+
// COD-115: `delete`, not `= undefined` — node-pty serializes a present-with-undefined
|
|
108
|
+
// key as the literal string "KEY=undefined" (see buildMuxAttachEnv below).
|
|
109
|
+
delete env.COLORTERM;
|
|
110
|
+
delete env.CLAUDECODE;
|
|
111
|
+
return env;
|
|
109
112
|
}
|
|
110
113
|
/**
|
|
111
114
|
* Build environment variables for mux-attached PTY sessions (tmux attach).
|
|
112
115
|
* Lighter than buildClaudeEnv — no PATH augmentation or Codeman vars needed
|
|
113
116
|
* since the mux session already has those set.
|
|
114
117
|
*
|
|
118
|
+
* @param truecolorEnabled - When true, set COLORTERM=truecolor (COD-75 opt-in);
|
|
119
|
+
* otherwise leave COLORTERM unset. Mirrors buildEnvExports() so both paths agree.
|
|
115
120
|
* @returns Environment variables object for pty.spawn
|
|
116
121
|
*/
|
|
117
|
-
export function buildMuxAttachEnv() {
|
|
118
|
-
|
|
122
|
+
export function buildMuxAttachEnv(truecolorEnabled) {
|
|
123
|
+
const env = {
|
|
119
124
|
...process.env,
|
|
120
125
|
LANG: 'en_US.UTF-8',
|
|
121
126
|
LC_ALL: 'en_US.UTF-8',
|
|
122
127
|
TERM: 'xterm-256color',
|
|
123
|
-
COLORTERM: undefined,
|
|
124
|
-
CLAUDECODE: undefined,
|
|
125
128
|
};
|
|
129
|
+
// COD-115: keys to UNSET must be `delete`d, NOT set to `undefined`. On a
|
|
130
|
+
// `{...process.env}` spread the key stays present with value undefined, and node-pty
|
|
131
|
+
// serializes it as the literal string "TMUX=undefined" — a non-empty value that still
|
|
132
|
+
// trips tmux's nesting guard, killing the attach-bridge PTY (exit 1 → respawn loop).
|
|
133
|
+
// The server can be launched from inside tmux; attach clients must never inherit that
|
|
134
|
+
// parent tmux context. (Same fix the working create path uses in tmux-manager.ts.)
|
|
135
|
+
delete env.TMUX;
|
|
136
|
+
delete env.TMUX_PANE;
|
|
137
|
+
delete env.CLAUDECODE;
|
|
138
|
+
if (truecolorEnabled) {
|
|
139
|
+
env.COLORTERM = 'truecolor';
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
delete env.COLORTERM; // COD-75: unset for non-truecolor (was `: undefined`, same node-pty quirk)
|
|
143
|
+
}
|
|
144
|
+
return env;
|
|
126
145
|
}
|
|
127
146
|
/**
|
|
128
147
|
* Build environment variables for a direct shell session (non-mux fallback).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-cli-builder.js","sourceRoot":"","sources":["../src/session-cli-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD;;;GAGG;AACH,SAAS,mBAAmB,CAAC,UAAsB,EAAE,YAAqB;IACxE,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,8BAA8B;YACjC,OAAO,CAAC,gCAAgC,CAAC,CAAC;QAC5C,KAAK,cAAc;YACjB,IAAI,YAAY,EAAE,CAAC;gBACjB,OAAO,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC;YAC1C,CAAC;YACD,iDAAiD;YACjD,OAAO,EAAE,CAAC;QACZ,KAAK,QAAQ,CAAC;QACd;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAoB;IACrD,IAAI,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IACjD,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAC9F,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAClC,SAAiB,EACjB,UAAsB,EACtB,KAAc,EACd,YAAqB,EACrB,MAAoB;IAEpB,MAAM,IAAI,GAAG,CAAC,GAAG,mBAAmB,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;IAC3F,IAAI,KAAK;QAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACvC,IAAI,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC;IACzC,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,KAAc;IAC5D,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,WAAW,EAAE,gCAAgC,EAAE,iBAAiB,EAAE,aAAa,CAAC,CAAC;IACrG,IAAI,KAAK,EAAE,CAAC;QACV,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClB,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,
|
|
1
|
+
{"version":3,"file":"session-cli-builder.js","sourceRoot":"","sources":["../src/session-cli-builder.ts"],"names":[],"mappings":"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"}
|