@layorixinc/axterm 0.5.0 → 0.6.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/cli.js +903 -506
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -46,6 +46,154 @@ var __export = (target, all) => {
|
|
|
46
46
|
};
|
|
47
47
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
48
48
|
|
|
49
|
+
// packages/core/src/agent-registry.ts
|
|
50
|
+
function agentProfile(id) {
|
|
51
|
+
const known = PROFILES[id];
|
|
52
|
+
if (known)
|
|
53
|
+
return known;
|
|
54
|
+
return {
|
|
55
|
+
id,
|
|
56
|
+
displayName: id,
|
|
57
|
+
title: {
|
|
58
|
+
workingLabel: id,
|
|
59
|
+
permissionLabel: `${id} - action required`,
|
|
60
|
+
idleLabel: `${id} ready`
|
|
61
|
+
},
|
|
62
|
+
hostHookSource: id
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function renderAgentTitle(profile, state, frame) {
|
|
66
|
+
if (state === undefined)
|
|
67
|
+
return;
|
|
68
|
+
const { title } = profile;
|
|
69
|
+
if (state === "working") {
|
|
70
|
+
if (title.synthesizeWorking === false)
|
|
71
|
+
return;
|
|
72
|
+
const spinner = SPINNER_FRAMES[(frame % SPINNER_FRAMES.length + SPINNER_FRAMES.length) % SPINNER_FRAMES.length];
|
|
73
|
+
return `${spinner} ${title.workingLabel}`;
|
|
74
|
+
}
|
|
75
|
+
if (title.synthesizeTerminal === false)
|
|
76
|
+
return;
|
|
77
|
+
return state === "done" ? title.idleLabel : title.permissionLabel;
|
|
78
|
+
}
|
|
79
|
+
function oscTitle(title) {
|
|
80
|
+
const safe = title.replace(/[\x00-\x1f\x7f]/g, "").slice(0, MAX_TITLE_LENGTH);
|
|
81
|
+
return `\x1B]0;${safe}\x07`;
|
|
82
|
+
}
|
|
83
|
+
var SPINNER_FRAMES, MENU_APPROVE = "\r", PROFILES, REGISTERED_AGENTS, MAX_TITLE_LENGTH = 200;
|
|
84
|
+
var init_agent_registry = __esm(() => {
|
|
85
|
+
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
86
|
+
PROFILES = {
|
|
87
|
+
omp: {
|
|
88
|
+
id: "omp",
|
|
89
|
+
displayName: "OMP",
|
|
90
|
+
title: { workingLabel: "OMP", permissionLabel: "OMP - action required", idleLabel: "OMP ready" },
|
|
91
|
+
hostHookSource: "omp",
|
|
92
|
+
approvalBypassArgs: ["--approval-mode", "yolo"],
|
|
93
|
+
quota: "gauge",
|
|
94
|
+
resume: { argv: ["--resume", "{ref}"], ref: "sessionFile" }
|
|
95
|
+
},
|
|
96
|
+
pi: {
|
|
97
|
+
id: "pi",
|
|
98
|
+
displayName: "Pi",
|
|
99
|
+
title: { workingLabel: "Pi", permissionLabel: "Pi - action required", idleLabel: "Pi ready" },
|
|
100
|
+
hostHookSource: "pi",
|
|
101
|
+
resume: { argv: ["--session", "{ref}"], ref: "sessionId" }
|
|
102
|
+
},
|
|
103
|
+
codex: {
|
|
104
|
+
id: "codex",
|
|
105
|
+
displayName: "Codex",
|
|
106
|
+
title: {
|
|
107
|
+
workingLabel: "Codex",
|
|
108
|
+
permissionLabel: "Codex - action required",
|
|
109
|
+
idleLabel: "Codex ready",
|
|
110
|
+
synthesizeWorking: false
|
|
111
|
+
},
|
|
112
|
+
hostHookSource: "codex",
|
|
113
|
+
quota: "gauge",
|
|
114
|
+
approvalBypassArgs: ["--dangerously-bypass-approvals-and-sandbox"],
|
|
115
|
+
structuredApprovals: true,
|
|
116
|
+
resume: { argv: ["resume", "{ref}"], ref: "sessionId", form: "subcommand" }
|
|
117
|
+
},
|
|
118
|
+
opencode: {
|
|
119
|
+
id: "opencode",
|
|
120
|
+
displayName: "OpenCode",
|
|
121
|
+
title: {
|
|
122
|
+
workingLabel: "OpenCode",
|
|
123
|
+
permissionLabel: "OpenCode - action required",
|
|
124
|
+
idleLabel: "OpenCode ready",
|
|
125
|
+
synthesizeTerminal: false
|
|
126
|
+
},
|
|
127
|
+
hostHookSource: "opencode",
|
|
128
|
+
resume: { argv: ["--session", "{ref}"], ref: "sessionId" }
|
|
129
|
+
},
|
|
130
|
+
cursor: {
|
|
131
|
+
id: "cursor",
|
|
132
|
+
displayName: "Cursor Agent",
|
|
133
|
+
title: {
|
|
134
|
+
workingLabel: "Cursor Agent",
|
|
135
|
+
permissionLabel: "Cursor - action required",
|
|
136
|
+
idleLabel: "Cursor ready"
|
|
137
|
+
},
|
|
138
|
+
hostHookSource: "cursor",
|
|
139
|
+
resume: { argv: ["--resume", "{ref}"], ref: "sessionId" }
|
|
140
|
+
},
|
|
141
|
+
droid: {
|
|
142
|
+
id: "droid",
|
|
143
|
+
displayName: "Droid",
|
|
144
|
+
title: { workingLabel: "Droid", permissionLabel: "Droid - action required", idleLabel: "Droid ready" },
|
|
145
|
+
hostHookSource: "droid",
|
|
146
|
+
resume: { argv: ["--resume", "{ref}"], ref: "sessionId" }
|
|
147
|
+
},
|
|
148
|
+
claude: {
|
|
149
|
+
id: "claude",
|
|
150
|
+
displayName: "Claude Code",
|
|
151
|
+
title: {
|
|
152
|
+
workingLabel: "Claude",
|
|
153
|
+
permissionLabel: "Claude - action required",
|
|
154
|
+
idleLabel: "Claude ready",
|
|
155
|
+
synthesizeWorking: false,
|
|
156
|
+
synthesizeTerminal: false
|
|
157
|
+
},
|
|
158
|
+
hostHookSource: "claude",
|
|
159
|
+
quota: "event",
|
|
160
|
+
approvalBypassArgs: ["--permission-mode", "bypassPermissions"],
|
|
161
|
+
resume: { argv: ["--resume", "{ref}"], ref: "sessionId", selfNamed: "uuid" }
|
|
162
|
+
},
|
|
163
|
+
gemini: {
|
|
164
|
+
id: "gemini",
|
|
165
|
+
displayName: "Gemini CLI",
|
|
166
|
+
title: {
|
|
167
|
+
workingLabel: "Gemini",
|
|
168
|
+
permissionLabel: "Gemini - action required",
|
|
169
|
+
idleLabel: "Gemini ready",
|
|
170
|
+
synthesizeWorking: false,
|
|
171
|
+
synthesizeTerminal: false
|
|
172
|
+
},
|
|
173
|
+
hostHookSource: "gemini",
|
|
174
|
+
approvalBypassArgs: ["--approval-mode", "yolo"],
|
|
175
|
+
hookAck: "{}",
|
|
176
|
+
approvalKeys: { allow: MENU_APPROVE, deny: "\x1B" },
|
|
177
|
+
resume: { argv: ["--resume", "{ref}"], ref: "sessionId", selfNamed: "bare" }
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
REGISTERED_AGENTS = Object.keys(PROFILES);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// packages/core/src/session-id.ts
|
|
184
|
+
function uuidFromSessionId(sessionId) {
|
|
185
|
+
const hex = sessionId.replace(/-/g, "");
|
|
186
|
+
if (hex.length !== 32)
|
|
187
|
+
throw new Error(`session id is not 32 hex characters: ${sessionId}`);
|
|
188
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
189
|
+
}
|
|
190
|
+
function sessionIdFromUuid(ref) {
|
|
191
|
+
if (!ref.includes("-"))
|
|
192
|
+
return;
|
|
193
|
+
const stripped = ref.replace(/-/g, "");
|
|
194
|
+
return /^[0-9a-f]{4,32}$/i.test(stripped) ? stripped.toLowerCase() : undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
49
197
|
// packages/core/src/mcp-config.ts
|
|
50
198
|
function mcpServerEntry(launch, contextPath) {
|
|
51
199
|
return {
|
|
@@ -76,18 +224,6 @@ function codexMcpArgs(launch, contextPath) {
|
|
|
76
224
|
var MCP_SERVER_NAME = "axterm", MCP_CONTEXT_ENV = "AXTERM_MCP_CONTEXT";
|
|
77
225
|
|
|
78
226
|
// packages/core/src/agent-launch.ts
|
|
79
|
-
function uuidFromSessionId(sessionId) {
|
|
80
|
-
const hex = sessionId.replace(/-/g, "");
|
|
81
|
-
if (hex.length !== 32)
|
|
82
|
-
throw new Error(`session id is not 32 hex characters: ${sessionId}`);
|
|
83
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
84
|
-
}
|
|
85
|
-
function sessionIdFromUuid(ref) {
|
|
86
|
-
if (!ref.includes("-"))
|
|
87
|
-
return;
|
|
88
|
-
const stripped = ref.replace(/-/g, "");
|
|
89
|
-
return /^[0-9a-f]{4,32}$/i.test(stripped) ? stripped.toLowerCase() : undefined;
|
|
90
|
-
}
|
|
91
227
|
function withAppServer(argv, url) {
|
|
92
228
|
const out = [];
|
|
93
229
|
for (let i = 0;i < argv.length; i++) {
|
|
@@ -102,7 +238,13 @@ function withAppServer(argv, url) {
|
|
|
102
238
|
return [out[0], "--remote", url, ...out.slice(1)];
|
|
103
239
|
}
|
|
104
240
|
function resumesAConversation(args) {
|
|
105
|
-
return args.some((arg) =>
|
|
241
|
+
return args.some((arg) => RESUME_FLAGS.has(arg) || arg.startsWith("--resume=") || arg.startsWith("--continue="));
|
|
242
|
+
}
|
|
243
|
+
function namingFlag(agent, sessionId, extra) {
|
|
244
|
+
const named = agentProfile(agent).resume?.selfNamed;
|
|
245
|
+
if (named === undefined || resumesAConversation(extra))
|
|
246
|
+
return [];
|
|
247
|
+
return ["--session-id", named === "uuid" ? uuidFromSessionId(sessionId) : sessionId];
|
|
106
248
|
}
|
|
107
249
|
function buildAgentCommand(input) {
|
|
108
250
|
const extra = [...input.extraArgs ?? []];
|
|
@@ -112,17 +254,14 @@ function buildAgentCommand(input) {
|
|
|
112
254
|
"--settings",
|
|
113
255
|
input.claudeSettingsPath,
|
|
114
256
|
...input.mcpConfigPath === undefined ? [] : ["--mcp-config", input.mcpConfigPath],
|
|
115
|
-
"
|
|
116
|
-
uuidFromSessionId(input.sessionId),
|
|
117
|
-
...resumesAConversation(extra) ? ["--fork-session"] : [],
|
|
257
|
+
...namingFlag("claude", input.sessionId, extra),
|
|
118
258
|
...extra
|
|
119
259
|
];
|
|
120
260
|
}
|
|
121
261
|
if (input.agent === "gemini") {
|
|
122
262
|
return [
|
|
123
263
|
"gemini",
|
|
124
|
-
"
|
|
125
|
-
input.sessionId,
|
|
264
|
+
...namingFlag("gemini", input.sessionId, extra),
|
|
126
265
|
...extra
|
|
127
266
|
];
|
|
128
267
|
}
|
|
@@ -159,61 +298,16 @@ function resolveAgentArgs(input) {
|
|
|
159
298
|
});
|
|
160
299
|
return groups.filter((group, index) => group.flag === undefined || lastIndex.get(group.flag) === index).flatMap((group) => group.tokens);
|
|
161
300
|
}
|
|
162
|
-
function operatorArgsFrom(storedArgv) {
|
|
163
|
-
if (storedArgv === undefined || storedArgv.length === 0)
|
|
164
|
-
return [];
|
|
165
|
-
const kept = [];
|
|
166
|
-
for (let i = 1;i < storedArgv.length; i++) {
|
|
167
|
-
const token = storedArgv[i];
|
|
168
|
-
const width = AXTERM_OWNED_FLAGS[token];
|
|
169
|
-
if (width !== undefined) {
|
|
170
|
-
i += width - 1;
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
if (/^--(resume|continue)=/.test(token))
|
|
174
|
-
continue;
|
|
175
|
-
kept.push(token);
|
|
176
|
-
}
|
|
177
|
-
return kept;
|
|
178
|
-
}
|
|
179
|
-
function buildAgentResumeCommand(input) {
|
|
180
|
-
const operator = input.extraArgs ?? operatorArgsFrom(input.storedArgv);
|
|
181
|
-
if (input.agent === "claude") {
|
|
182
|
-
return [
|
|
183
|
-
"claude",
|
|
184
|
-
"--settings",
|
|
185
|
-
input.claudeSettingsPath,
|
|
186
|
-
...input.mcpConfigPath === undefined ? [] : ["--mcp-config", input.mcpConfigPath],
|
|
187
|
-
...operator,
|
|
188
|
-
"--resume",
|
|
189
|
-
uuidFromSessionId(input.sessionId)
|
|
190
|
-
];
|
|
191
|
-
}
|
|
192
|
-
if (input.agent === "gemini") {
|
|
193
|
-
return ["gemini", ...operator, "--resume", input.sessionId];
|
|
194
|
-
}
|
|
195
|
-
return buildAgentCommand({ ...input, extraArgs: operator });
|
|
196
|
-
}
|
|
197
301
|
function isAgentId(value) {
|
|
198
302
|
return AGENT_IDS.includes(value);
|
|
199
303
|
}
|
|
200
304
|
function supportedAgents() {
|
|
201
305
|
return AGENT_IDS;
|
|
202
306
|
}
|
|
203
|
-
var
|
|
307
|
+
var RESUME_FLAGS, AGENT_IDS;
|
|
204
308
|
var init_agent_launch = __esm(() => {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
"--settings": 2,
|
|
208
|
-
"--mcp-config": 2,
|
|
209
|
-
"--session-id": 2,
|
|
210
|
-
"--extension": 2,
|
|
211
|
-
"--fork-session": 1,
|
|
212
|
-
"--resume": 2,
|
|
213
|
-
"-r": 2,
|
|
214
|
-
"--continue": 1,
|
|
215
|
-
"-c": 1
|
|
216
|
-
};
|
|
309
|
+
init_agent_registry();
|
|
310
|
+
RESUME_FLAGS = new Set(["--resume", "-r", "--continue", "-c"]);
|
|
217
311
|
AGENT_IDS = ["omp", "claude", "codex", "gemini"];
|
|
218
312
|
});
|
|
219
313
|
|
|
@@ -944,6 +1038,7 @@ locale: set AXTERM_LANG=en|ko, or \`axterm config set locale ko\`
|
|
|
944
1038
|
"cli.session.sent": "sent",
|
|
945
1039
|
"cli.session.sentKey": "sent {key}",
|
|
946
1040
|
"cli.session.selected": "selected option {index}",
|
|
1041
|
+
"cli.session.selectedDialog": "answered the agent's first-run dialog",
|
|
947
1042
|
"cli.resume.done": "resumed session {id} ({command})",
|
|
948
1043
|
"cli.update.current": "axterm {version} is the latest version",
|
|
949
1044
|
"cli.update.available": "axterm {latest} is available (you have {current})",
|
|
@@ -1013,7 +1108,7 @@ locale: set AXTERM_LANG=en|ko, or \`axterm config set locale ko\`
|
|
|
1013
1108
|
"err.telegram.noToken": "no bot token is configured \u2014 run `axterm setup` or pass --token",
|
|
1014
1109
|
"tg.help.general.header": "axterm commands (General topic):",
|
|
1015
1110
|
"tg.help.general.projects": "/projects \u2014 registered projects",
|
|
1016
|
-
"tg.help.general.new": "/new <project> [name] \u2014 new
|
|
1111
|
+
"tg.help.general.new": "/new <project> [name] [-- agent flags] \u2014 new session (e.g. --resume <conversation>)",
|
|
1017
1112
|
"tg.help.general.wt": "/wt <project> <branch> \u2014 worktree session",
|
|
1018
1113
|
"tg.help.general.fanout": "/fanout <project> <count|branches> <prompt> \u2014 run in parallel",
|
|
1019
1114
|
"tg.help.general.worktrees": "/worktrees <project> \xB7 /rm <project> <branch> [force]",
|
|
@@ -1035,6 +1130,9 @@ locale: set AXTERM_LANG=en|ko, or \`axterm config set locale ko\`
|
|
|
1035
1130
|
"tg.prompt.confirm": "the session is waiting on a confirmation:",
|
|
1036
1131
|
"tg.prompt.secretAsk": "the session is asking for a secret. Reply with it and the message will be deleted:",
|
|
1037
1132
|
"tg.prompt.secretDisabled": "the session is waiting for a password. Answering from chat is off (channels.telegram.secretPrompts); attach and type it, or use an SSH key",
|
|
1133
|
+
"tg.prompt.secretSubmitted": "the secret reached the session",
|
|
1134
|
+
"tg.prompt.secretStale": "that prompt is no longer waiting, so nothing was typed into the session",
|
|
1135
|
+
"tg.prompt.secretUndeleted": "\u26A0\uFE0F Telegram refused to delete that message \u2014 the secret is still in the chat; delete it yourself",
|
|
1038
1136
|
"tg.session.screenFile": "terminal screen",
|
|
1039
1137
|
"tg.session.none": "no sessions",
|
|
1040
1138
|
"tg.prompt.failed": "\u26A0\uFE0F Delivery failed: {error}",
|
|
@@ -1154,6 +1252,7 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1154
1252
|
"err.agent.unknown": "unknown agent '{agent}' \u2014 supported: {supported}",
|
|
1155
1253
|
"err.staging.dir": "could not create the session's staging directory {dir} ({error}) \u2014 fix the permissions, or set defaults.staging to runtime",
|
|
1156
1254
|
"err.staging.notIgnored": "git can see inside {dir}, so a staged file would show up in `git add -A` \u2014 let axterm write its own {file}, or set defaults.staging to runtime",
|
|
1255
|
+
"err.claude.conversationBusy": "session {session} is already in conversation {id} \u2014 two sessions writing one transcript would corrupt it; kill that one first",
|
|
1157
1256
|
"err.claude.noConversation": "claude has no conversation {id} in its store on this machine \u2014 a conversation lives on the machine it ran on, so this one cannot be resumed here",
|
|
1158
1257
|
"codex.approval.denied": "Denied from the axterm channel",
|
|
1159
1258
|
"err.codex.noAuth": "codex is not logged in ({authFile} not found) \u2014 run `codex login` once, then start the session again",
|
|
@@ -1245,6 +1344,7 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1245
1344
|
"cli.session.sent": "\uC804\uB2EC\uB428",
|
|
1246
1345
|
"cli.session.sentKey": "{key} \uC804\uB2EC\uB428",
|
|
1247
1346
|
"cli.session.selected": "{index}\uBC88 \uD56D\uBAA9 \uC120\uD0DD\uB428",
|
|
1347
|
+
"cli.session.selectedDialog": "\uC5D0\uC774\uC804\uD2B8\uC758 \uCCAB \uC2E4\uD589 \uB300\uD654\uC0C1\uC790\uC5D0 \uC751\uB2F5\uD588\uC2B5\uB2C8\uB2E4",
|
|
1248
1348
|
"cli.resume.done": "\uC138\uC158 {id} \uC7AC\uAC1C\uB428 ({command})",
|
|
1249
1349
|
"cli.update.current": "axterm {version} \uC774 \uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4",
|
|
1250
1350
|
"cli.update.available": "axterm {latest} \uC744(\uB97C) \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4 (\uD604\uC7AC {current})",
|
|
@@ -1307,7 +1407,7 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1307
1407
|
"cli.telegram.missingSettings": "telegram link: \uD1A0\uD070/\uADF8\uB8F9/\uC0AC\uC6A9\uC790 \uC815\uBCF4\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. --token/--group/--user \uD50C\uB798\uADF8, TELEGRAM_* \uD658\uACBD\uBCC0\uC218, \uB610\uB294 \uD604\uC7AC \uD3F4\uB354\uC758 .env \uD30C\uC77C \uC911 \uD558\uB098\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
|
|
1308
1408
|
"tg.help.general.header": "axterm \uBA85\uB839 (General \uD1A0\uD53D):",
|
|
1309
1409
|
"tg.help.general.projects": "/projects \u2014 \uB4F1\uB85D\uB41C \uD504\uB85C\uC81D\uD2B8",
|
|
1310
|
-
"tg.help.general.new": "/new <\uD504\uB85C\uC81D\uD2B8> [\uC774\uB984] \u2014 \uC0C8
|
|
1410
|
+
"tg.help.general.new": "/new <\uD504\uB85C\uC81D\uD2B8> [\uC774\uB984] [\uC5D0\uC774\uC804\uD2B8 \uD50C\uB798\uADF8] \u2014 \uC0C8 \uC138\uC158 (\uC608: --resume <\uB300\uD654>)",
|
|
1311
1411
|
"tg.help.general.wt": "/wt <\uD504\uB85C\uC81D\uD2B8> <\uBE0C\uB79C\uCE58> \u2014 worktree \uC138\uC158",
|
|
1312
1412
|
"tg.help.general.fanout": "/fanout <\uD504\uB85C\uC81D\uD2B8> <\uAC1C\uC218|\uBE0C\uB79C\uCE58\uB4E4> <\uD504\uB86C\uD504\uD2B8> \u2014 \uBCD1\uB82C \uC2E4\uD589",
|
|
1313
1413
|
"tg.help.general.worktrees": "/worktrees <\uD504\uB85C\uC81D\uD2B8> \xB7 /rm <\uD504\uB85C\uC81D\uD2B8> <\uBE0C\uB79C\uCE58> [force]",
|
|
@@ -1336,6 +1436,9 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1336
1436
|
"tg.prompt.confirm": "\uC138\uC158\uC774 \uD655\uC778\uC744 \uAE30\uB2E4\uB9AC\uACE0 \uC788\uC2B5\uB2C8\uB2E4:",
|
|
1337
1437
|
"tg.prompt.secretAsk": "\uC138\uC158\uC774 \uBE44\uBC00\uAC12\uC744 \uC694\uAD6C\uD569\uB2C8\uB2E4. \uB2F5\uC7A5\uD558\uBA74 \uADF8 \uBA54\uC2DC\uC9C0\uB294 \uC0AD\uC81C\uB429\uB2C8\uB2E4:",
|
|
1338
1438
|
"tg.prompt.secretDisabled": "\uC138\uC158\uC774 \uBE44\uBC00\uBC88\uD638\uB97C \uAE30\uB2E4\uB9AC\uACE0 \uC788\uC2B5\uB2C8\uB2E4. \uCC44\uD305 \uC785\uB825\uC740 \uAEBC\uC838 \uC788\uC2B5\uB2C8\uB2E4 (channels.telegram.secretPrompts). attach \uD574\uC11C \uC785\uB825\uD558\uAC70\uB098 SSH \uD0A4\uB97C \uC4F0\uC138\uC694",
|
|
1439
|
+
"tg.prompt.secretSubmitted": "\uBE44\uBC00\uAC12\uC774 \uC138\uC158\uC5D0 \uC804\uB2EC\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
|
|
1440
|
+
"tg.prompt.secretStale": "\uADF8 \uD504\uB86C\uD504\uD2B8\uB294 \uB354 \uC774\uC0C1 \uAE30\uB2E4\uB9AC\uACE0 \uC788\uC9C0 \uC54A\uC544 \uC138\uC158\uC5D0 \uC544\uBB34\uAC83\uB3C4 \uC785\uB825\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4",
|
|
1441
|
+
"tg.prompt.secretUndeleted": "\u26A0\uFE0F Telegram\uC774 \uBA54\uC2DC\uC9C0 \uC0AD\uC81C\uB97C \uAC70\uBD80\uD588\uC2B5\uB2C8\uB2E4 \u2014 \uBE44\uBC00\uAC12\uC774 \uC544\uC9C1 \uCC44\uD305\uC5D0 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4. \uC9C1\uC811 \uC0AD\uC81C\uD558\uC138\uC694",
|
|
1339
1442
|
"tg.session.screenFile": "\uD130\uBBF8\uB110 \uD654\uBA74",
|
|
1340
1443
|
"tg.session.none": "\uC138\uC158 \uC5C6\uC74C",
|
|
1341
1444
|
"tg.prompt.failed": "\u26A0\uFE0F \uC804\uB2EC \uC2E4\uD328: {error}",
|
|
@@ -1455,6 +1558,7 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1455
1558
|
"err.agent.unknown": "\uC54C \uC218 \uC5C6\uB294 \uC5D0\uC774\uC804\uD2B8 '{agent}' \uC785\uB2C8\uB2E4. \uC9C0\uC6D0: {supported}",
|
|
1456
1559
|
"err.staging.dir": "\uC138\uC158\uC758 \uC2A4\uD14C\uC774\uC9D5 \uB514\uB809\uD130\uB9AC {dir} \uC744(\uB97C) \uB9CC\uB4E4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 ({error}). \uAD8C\uD55C\uC744 \uACE0\uCE58\uAC70\uB098 defaults.staging \uC744 runtime \uC73C\uB85C \uC124\uC815\uD558\uC138\uC694",
|
|
1457
1560
|
"err.staging.notIgnored": "git\uC774 {dir} \uC548\uC744 \uBCFC \uC218 \uC788\uC5B4\uC11C, \uC2A4\uD14C\uC774\uC9D5\uD55C \uD30C\uC77C\uC774 `git add -A` \uC5D0 \uC7A1\uD799\uB2C8\uB2E4. axterm\uC774 \uC790\uCCB4 {file} \uC744(\uB97C) \uC4F0\uAC8C \uD558\uAC70\uB098 defaults.staging \uC744 runtime \uC73C\uB85C \uC124\uC815\uD558\uC138\uC694",
|
|
1561
|
+
"err.claude.conversationBusy": "\uC138\uC158 {session} \uC774(\uAC00) \uC774\uBBF8 \uB300\uD654 {id} \uC5D0 \uB4E4\uC5B4\uAC00 \uC788\uC2B5\uB2C8\uB2E4. \uD55C transcript\uC5D0 \uB450 \uC138\uC158\uC774 \uC4F0\uBA74 \uC190\uC0C1\uB418\uBBC0\uB85C \uBA3C\uC800 \uADF8 \uC138\uC158\uC744 \uC885\uB8CC\uD558\uC138\uC694",
|
|
1458
1562
|
"err.claude.noConversation": "\uC774 \uBA38\uC2E0\uC758 claude \uC800\uC7A5\uC18C\uC5D0 \uB300\uD654 {id} \uC774(\uAC00) \uC5C6\uC2B5\uB2C8\uB2E4. \uB300\uD654\uB294 \uC2E4\uD589\uB41C \uBA38\uC2E0\uC5D0 \uB0A8\uC73C\uBBC0\uB85C \uC5EC\uAE30\uC11C\uB294 \uC774\uC5B4\uAC08 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
|
|
1459
1563
|
"codex.approval.denied": "axterm \uCC44\uB110\uC5D0\uC11C \uAC70\uBD80\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
|
|
1460
1564
|
"err.codex.noAuth": "codex\uC5D0 \uB85C\uADF8\uC778\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 ({authFile} \uC5C6\uC74C). `codex login` \uC744 \uD55C \uBC88 \uC2E4\uD589\uD55C \uB4A4 \uC138\uC158\uC744 \uB2E4\uC2DC \uC2DC\uC791\uD558\uC138\uC694",
|
|
@@ -1645,144 +1749,16 @@ var init_setup_wizard = __esm(() => {
|
|
|
1645
1749
|
wizardConfig = new WeakMap;
|
|
1646
1750
|
});
|
|
1647
1751
|
|
|
1648
|
-
// packages/core/src/agent-registry.ts
|
|
1649
|
-
function agentProfile(id) {
|
|
1650
|
-
const known = PROFILES[id];
|
|
1651
|
-
if (known)
|
|
1652
|
-
return known;
|
|
1653
|
-
return {
|
|
1654
|
-
id,
|
|
1655
|
-
displayName: id,
|
|
1656
|
-
title: {
|
|
1657
|
-
workingLabel: id,
|
|
1658
|
-
permissionLabel: `${id} - action required`,
|
|
1659
|
-
idleLabel: `${id} ready`
|
|
1660
|
-
},
|
|
1661
|
-
hostHookSource: id
|
|
1662
|
-
};
|
|
1663
|
-
}
|
|
1664
|
-
function renderAgentTitle(profile, state, frame) {
|
|
1665
|
-
if (state === undefined)
|
|
1666
|
-
return;
|
|
1667
|
-
const { title } = profile;
|
|
1668
|
-
if (state === "working") {
|
|
1669
|
-
if (title.synthesizeWorking === false)
|
|
1670
|
-
return;
|
|
1671
|
-
const spinner = SPINNER_FRAMES[(frame % SPINNER_FRAMES.length + SPINNER_FRAMES.length) % SPINNER_FRAMES.length];
|
|
1672
|
-
return `${spinner} ${title.workingLabel}`;
|
|
1673
|
-
}
|
|
1674
|
-
if (title.synthesizeTerminal === false)
|
|
1675
|
-
return;
|
|
1676
|
-
return state === "done" ? title.idleLabel : title.permissionLabel;
|
|
1677
|
-
}
|
|
1678
|
-
function oscTitle(title) {
|
|
1679
|
-
const safe = title.replace(/[\x00-\x1f\x7f]/g, "").slice(0, MAX_TITLE_LENGTH);
|
|
1680
|
-
return `\x1B]0;${safe}\x07`;
|
|
1681
|
-
}
|
|
1682
|
-
var SPINNER_FRAMES, MENU_APPROVE = "\r", PROFILES, REGISTERED_AGENTS, MAX_TITLE_LENGTH = 200;
|
|
1683
|
-
var init_agent_registry = __esm(() => {
|
|
1684
|
-
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1685
|
-
PROFILES = {
|
|
1686
|
-
omp: {
|
|
1687
|
-
id: "omp",
|
|
1688
|
-
displayName: "OMP",
|
|
1689
|
-
title: { workingLabel: "OMP", permissionLabel: "OMP - action required", idleLabel: "OMP ready" },
|
|
1690
|
-
hostHookSource: "omp",
|
|
1691
|
-
approvalBypassArgs: ["--approval-mode", "yolo"],
|
|
1692
|
-
quota: "gauge",
|
|
1693
|
-
resume: { argv: ["--resume", "{ref}"], ref: "sessionFile" }
|
|
1694
|
-
},
|
|
1695
|
-
pi: {
|
|
1696
|
-
id: "pi",
|
|
1697
|
-
displayName: "Pi",
|
|
1698
|
-
title: { workingLabel: "Pi", permissionLabel: "Pi - action required", idleLabel: "Pi ready" },
|
|
1699
|
-
hostHookSource: "pi",
|
|
1700
|
-
resume: { argv: ["--session", "{ref}"], ref: "sessionId" }
|
|
1701
|
-
},
|
|
1702
|
-
codex: {
|
|
1703
|
-
id: "codex",
|
|
1704
|
-
displayName: "Codex",
|
|
1705
|
-
title: {
|
|
1706
|
-
workingLabel: "Codex",
|
|
1707
|
-
permissionLabel: "Codex - action required",
|
|
1708
|
-
idleLabel: "Codex ready",
|
|
1709
|
-
synthesizeWorking: false
|
|
1710
|
-
},
|
|
1711
|
-
hostHookSource: "codex",
|
|
1712
|
-
quota: "gauge",
|
|
1713
|
-
approvalBypassArgs: ["--dangerously-bypass-approvals-and-sandbox"],
|
|
1714
|
-
structuredApprovals: true,
|
|
1715
|
-
resume: { argv: ["resume", "{ref}"], ref: "sessionId", form: "subcommand" }
|
|
1716
|
-
},
|
|
1717
|
-
opencode: {
|
|
1718
|
-
id: "opencode",
|
|
1719
|
-
displayName: "OpenCode",
|
|
1720
|
-
title: {
|
|
1721
|
-
workingLabel: "OpenCode",
|
|
1722
|
-
permissionLabel: "OpenCode - action required",
|
|
1723
|
-
idleLabel: "OpenCode ready",
|
|
1724
|
-
synthesizeTerminal: false
|
|
1725
|
-
},
|
|
1726
|
-
hostHookSource: "opencode",
|
|
1727
|
-
resume: { argv: ["--session", "{ref}"], ref: "sessionId" }
|
|
1728
|
-
},
|
|
1729
|
-
cursor: {
|
|
1730
|
-
id: "cursor",
|
|
1731
|
-
displayName: "Cursor Agent",
|
|
1732
|
-
title: {
|
|
1733
|
-
workingLabel: "Cursor Agent",
|
|
1734
|
-
permissionLabel: "Cursor - action required",
|
|
1735
|
-
idleLabel: "Cursor ready"
|
|
1736
|
-
},
|
|
1737
|
-
hostHookSource: "cursor",
|
|
1738
|
-
resume: { argv: ["--resume", "{ref}"], ref: "sessionId" }
|
|
1739
|
-
},
|
|
1740
|
-
droid: {
|
|
1741
|
-
id: "droid",
|
|
1742
|
-
displayName: "Droid",
|
|
1743
|
-
title: { workingLabel: "Droid", permissionLabel: "Droid - action required", idleLabel: "Droid ready" },
|
|
1744
|
-
hostHookSource: "droid",
|
|
1745
|
-
resume: { argv: ["--resume", "{ref}"], ref: "sessionId" }
|
|
1746
|
-
},
|
|
1747
|
-
claude: {
|
|
1748
|
-
id: "claude",
|
|
1749
|
-
displayName: "Claude Code",
|
|
1750
|
-
title: {
|
|
1751
|
-
workingLabel: "Claude",
|
|
1752
|
-
permissionLabel: "Claude - action required",
|
|
1753
|
-
idleLabel: "Claude ready",
|
|
1754
|
-
synthesizeWorking: false,
|
|
1755
|
-
synthesizeTerminal: false
|
|
1756
|
-
},
|
|
1757
|
-
hostHookSource: "claude",
|
|
1758
|
-
quota: "event",
|
|
1759
|
-
approvalBypassArgs: ["--permission-mode", "bypassPermissions"],
|
|
1760
|
-
resume: { argv: ["--resume", "{ref}"], ref: "sessionId" }
|
|
1761
|
-
},
|
|
1762
|
-
gemini: {
|
|
1763
|
-
id: "gemini",
|
|
1764
|
-
displayName: "Gemini CLI",
|
|
1765
|
-
title: {
|
|
1766
|
-
workingLabel: "Gemini",
|
|
1767
|
-
permissionLabel: "Gemini - action required",
|
|
1768
|
-
idleLabel: "Gemini ready",
|
|
1769
|
-
synthesizeWorking: false,
|
|
1770
|
-
synthesizeTerminal: false
|
|
1771
|
-
},
|
|
1772
|
-
hostHookSource: "gemini",
|
|
1773
|
-
approvalBypassArgs: ["--approval-mode", "yolo"],
|
|
1774
|
-
hookAck: "{}",
|
|
1775
|
-
approvalKeys: { allow: MENU_APPROVE, deny: "\x1B" },
|
|
1776
|
-
resume: { argv: ["--resume", "{ref}"], ref: "sessionId" }
|
|
1777
|
-
}
|
|
1778
|
-
};
|
|
1779
|
-
REGISTERED_AGENTS = Object.keys(PROFILES);
|
|
1780
|
-
});
|
|
1781
|
-
|
|
1782
1752
|
// packages/core/src/agent-resume.ts
|
|
1783
1753
|
function resumeReference(profile, source) {
|
|
1784
|
-
const
|
|
1785
|
-
|
|
1754
|
+
const reported = profile.resume?.ref === "sessionId" ? source.agentSessionId : source.agentSessionFile;
|
|
1755
|
+
if (reported !== undefined && reported.length > 0)
|
|
1756
|
+
return reported;
|
|
1757
|
+
const named = profile.resume?.selfNamed;
|
|
1758
|
+
const own = source.ownSessionId;
|
|
1759
|
+
if (named === undefined || own === undefined || own.length === 0)
|
|
1760
|
+
return;
|
|
1761
|
+
return named === "uuid" ? uuidFromSessionId(own) : own;
|
|
1786
1762
|
}
|
|
1787
1763
|
function agentIdFromCommand(command) {
|
|
1788
1764
|
const head = command[0] ?? "";
|
|
@@ -1809,17 +1785,23 @@ function withoutExistingResume(command, fragment) {
|
|
|
1809
1785
|
}
|
|
1810
1786
|
return command;
|
|
1811
1787
|
}
|
|
1788
|
+
function withoutNamingFlag(command) {
|
|
1789
|
+
const at = command.indexOf("--session-id");
|
|
1790
|
+
return at < 0 ? command : [...command.slice(0, at), ...command.slice(at + 2)];
|
|
1791
|
+
}
|
|
1812
1792
|
function buildResumeCommand(command, profile, reference) {
|
|
1813
1793
|
const resume = profile.resume;
|
|
1814
1794
|
const binary = command[0];
|
|
1815
1795
|
if (!resume || binary === undefined || reference.length === 0)
|
|
1816
1796
|
return;
|
|
1817
1797
|
const template = resume.argv;
|
|
1818
|
-
const
|
|
1798
|
+
const stripped = withoutExistingResume(command, template);
|
|
1799
|
+
const base = resume.selfNamed === undefined ? stripped : withoutNamingFlag(stripped);
|
|
1819
1800
|
const fragment = template.map((token) => token.replaceAll(REF, reference));
|
|
1820
1801
|
return resume.form === "subcommand" ? [base[0], ...fragment, ...base.slice(1)] : [...base, ...fragment];
|
|
1821
1802
|
}
|
|
1822
1803
|
var REF = "{ref}";
|
|
1804
|
+
var init_agent_resume = () => {};
|
|
1823
1805
|
|
|
1824
1806
|
// packages/core/src/self-spawn.ts
|
|
1825
1807
|
function daemonLaunch(input) {
|
|
@@ -1848,9 +1830,9 @@ function ptyHostLaunch(input) {
|
|
|
1848
1830
|
function isPtyHostInvocation(args) {
|
|
1849
1831
|
return args[0] === PTY_HOST_ARGV_FLAG;
|
|
1850
1832
|
}
|
|
1851
|
-
function hookPostTarget(env, agent) {
|
|
1852
|
-
const port = Number(env["AXTERM_HOOK_PORT"]);
|
|
1853
|
-
const token = env["AXTERM_HOOK_TOKEN"];
|
|
1833
|
+
function hookPostTarget(env, agent, override) {
|
|
1834
|
+
const port = override?.port ?? Number(env["AXTERM_HOOK_PORT"]);
|
|
1835
|
+
const token = override?.token ?? env["AXTERM_HOOK_TOKEN"];
|
|
1854
1836
|
const sessionId = env["AXTERM_SESSION_ID"];
|
|
1855
1837
|
const launchToken = env["AXTERM_LAUNCH_TOKEN"];
|
|
1856
1838
|
if (!Number.isSafeInteger(port) || port <= 0)
|
|
@@ -1867,6 +1849,13 @@ function hookPostTarget(env, agent) {
|
|
|
1867
1849
|
}
|
|
1868
1850
|
};
|
|
1869
1851
|
}
|
|
1852
|
+
function parseHookEndpoint(text) {
|
|
1853
|
+
const port = Number(/^AXTERM_HOOK_PORT=(\d+)$/m.exec(text)?.[1]);
|
|
1854
|
+
const token = /^AXTERM_HOOK_TOKEN=(\S+)$/m.exec(text)?.[1];
|
|
1855
|
+
if (!Number.isSafeInteger(port) || port <= 0 || !token)
|
|
1856
|
+
return;
|
|
1857
|
+
return { port, token };
|
|
1858
|
+
}
|
|
1870
1859
|
function isDaemonInvocation(args) {
|
|
1871
1860
|
return args[0] === DAEMON_ARGV_FLAG;
|
|
1872
1861
|
}
|
|
@@ -1992,6 +1981,7 @@ function planUpdateRestart(input) {
|
|
|
1992
1981
|
var SEMVER;
|
|
1993
1982
|
var init_update = __esm(() => {
|
|
1994
1983
|
init_agent_registry();
|
|
1984
|
+
init_agent_resume();
|
|
1995
1985
|
SEMVER = /^\d+\.\d+\.\d+/;
|
|
1996
1986
|
});
|
|
1997
1987
|
|
|
@@ -2192,6 +2182,30 @@ function buildMenuSelectKeys(index, normalizePresses = MENU_NORMALIZE_PRESSES) {
|
|
|
2192
2182
|
}
|
|
2193
2183
|
var MENU_UP = "\x1B[A", MENU_DOWN = "\x1B[B", MENU_SELECT = "\r", MENU_NORMALIZE_PRESSES = 24;
|
|
2194
2184
|
|
|
2185
|
+
// packages/core/src/adopt.ts
|
|
2186
|
+
function resolveSessionCwd(input) {
|
|
2187
|
+
if (!input.preferConversation)
|
|
2188
|
+
return input.requested;
|
|
2189
|
+
const own = input.conversationCwd;
|
|
2190
|
+
return own !== undefined && own.length > 0 ? own : input.requested;
|
|
2191
|
+
}
|
|
2192
|
+
function conversationHeldBy(sessions, conversationId) {
|
|
2193
|
+
if (conversationId.length === 0)
|
|
2194
|
+
return;
|
|
2195
|
+
return sessions.find((session) => {
|
|
2196
|
+
if (session.state !== "running")
|
|
2197
|
+
return false;
|
|
2198
|
+
if (session.agentSessionId === conversationId)
|
|
2199
|
+
return true;
|
|
2200
|
+
const argv = session.agentCommand;
|
|
2201
|
+
return argv.some((token, index) => {
|
|
2202
|
+
if (token === "--resume" || token === "-r" || token === "--session-id")
|
|
2203
|
+
return argv[index + 1] === conversationId;
|
|
2204
|
+
return token === `--resume=${conversationId}`;
|
|
2205
|
+
});
|
|
2206
|
+
});
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2195
2209
|
// packages/core/src/agent-status.ts
|
|
2196
2210
|
function next(prev, state, now) {
|
|
2197
2211
|
return {
|
|
@@ -2201,17 +2215,29 @@ function next(prev, state, now) {
|
|
|
2201
2215
|
stateStartedAt: prev?.state === state ? prev.stateStartedAt : now
|
|
2202
2216
|
};
|
|
2203
2217
|
}
|
|
2204
|
-
function
|
|
2218
|
+
function withConversationId(events, identity) {
|
|
2219
|
+
const id = identity.sessionId;
|
|
2220
|
+
const subagent = identity.subagentId;
|
|
2221
|
+
if (typeof id !== "string" || id === "" || typeof subagent === "string" && subagent !== "") {
|
|
2222
|
+
return [...events];
|
|
2223
|
+
}
|
|
2224
|
+
return events.map((event) => ({ session_id: id, ...event }));
|
|
2225
|
+
}
|
|
2226
|
+
function reduceAgentState(prev, payload, now) {
|
|
2227
|
+
const result = reduceState(prev, payload, now);
|
|
2228
|
+
const identity = {
|
|
2229
|
+
...payload.session_id !== undefined ? { agentSessionId: payload.session_id } : {},
|
|
2230
|
+
...payload.session_file !== undefined ? { agentSessionFile: payload.session_file } : {},
|
|
2231
|
+
...payload.session_name !== undefined ? { sessionName: payload.session_name } : {}
|
|
2232
|
+
};
|
|
2233
|
+
if (Object.keys(identity).length === 0)
|
|
2234
|
+
return result;
|
|
2235
|
+
return { ...result, meta: { ...identity, ...result.meta } };
|
|
2236
|
+
}
|
|
2237
|
+
function reduceState(prev, payload, now) {
|
|
2205
2238
|
switch (payload.event) {
|
|
2206
2239
|
case "session_start":
|
|
2207
|
-
return {
|
|
2208
|
-
entry: prev,
|
|
2209
|
-
meta: {
|
|
2210
|
-
agentSessionId: payload.session_id,
|
|
2211
|
-
agentSessionFile: payload.session_file,
|
|
2212
|
-
sessionName: payload.session_name
|
|
2213
|
-
}
|
|
2214
|
-
};
|
|
2240
|
+
return { entry: prev };
|
|
2215
2241
|
case "before_agent_start": {
|
|
2216
2242
|
const entry = next(prev, prev?.state ?? "working", now);
|
|
2217
2243
|
entry.prompt = payload.prompt ?? "";
|
|
@@ -2378,7 +2404,18 @@ var init_pty_protocol = __esm(() => {
|
|
|
2378
2404
|
Data: 2
|
|
2379
2405
|
};
|
|
2380
2406
|
MAX_FRAME_BYTES = 8 * 1024 * 1024;
|
|
2381
|
-
KNOWN_TYPES = new Set([
|
|
2407
|
+
KNOWN_TYPES = new Set([
|
|
2408
|
+
"hello",
|
|
2409
|
+
"spawn",
|
|
2410
|
+
"subscribe",
|
|
2411
|
+
"resize",
|
|
2412
|
+
"kill",
|
|
2413
|
+
"welcome",
|
|
2414
|
+
"spawned",
|
|
2415
|
+
"subscribed",
|
|
2416
|
+
"exit",
|
|
2417
|
+
"error"
|
|
2418
|
+
]);
|
|
2382
2419
|
});
|
|
2383
2420
|
|
|
2384
2421
|
// packages/core/src/workspace.ts
|
|
@@ -2573,6 +2610,8 @@ var init_onboarding = __esm(() => {
|
|
|
2573
2610
|
{ kind: "authMethod", question: "How would you like to authenticate for this project?" }
|
|
2574
2611
|
],
|
|
2575
2612
|
claude: [
|
|
2613
|
+
{ kind: "folderTrust", question: "Quick safety check: Is this a project you created or one you trust?" },
|
|
2614
|
+
{ kind: "autoMode", question: "Set up auto mode for your environment?" },
|
|
2576
2615
|
{ kind: "apiKey", question: "Do you want to use this API key?" },
|
|
2577
2616
|
{ kind: "loginMethod", question: "Select login method:" }
|
|
2578
2617
|
],
|
|
@@ -3614,6 +3653,9 @@ function sessionsByRecency(live, stored) {
|
|
|
3614
3653
|
var asGiven = (target) => target;
|
|
3615
3654
|
var init_session_scope = () => {};
|
|
3616
3655
|
|
|
3656
|
+
// packages/core/src/test-scratch.ts
|
|
3657
|
+
var init_test_scratch = () => {};
|
|
3658
|
+
|
|
3617
3659
|
// packages/core/src/index.ts
|
|
3618
3660
|
function isRpcEvent(f) {
|
|
3619
3661
|
return typeof f.ev === "string";
|
|
@@ -3633,6 +3675,7 @@ var init_src = __esm(() => {
|
|
|
3633
3675
|
init_config_paths();
|
|
3634
3676
|
init_setup_wizard();
|
|
3635
3677
|
init_agent_registry();
|
|
3678
|
+
init_agent_resume();
|
|
3636
3679
|
init_self_spawn();
|
|
3637
3680
|
init_channel_spi();
|
|
3638
3681
|
init_update();
|
|
@@ -3660,19 +3703,20 @@ var init_src = __esm(() => {
|
|
|
3660
3703
|
init_dotenv_scrub();
|
|
3661
3704
|
init_prompt_detect();
|
|
3662
3705
|
init_session_scope();
|
|
3706
|
+
init_test_scratch();
|
|
3663
3707
|
SUBMIT_DELAY_MS = process.platform === "win32" ? 1500 : 500;
|
|
3664
3708
|
PASTE_CHUNK_BYTES = 16 * 1024;
|
|
3665
3709
|
});
|
|
3666
3710
|
|
|
3667
3711
|
// packages/daemon/src/config-store.ts
|
|
3668
|
-
import
|
|
3712
|
+
import fs6 from "fs";
|
|
3669
3713
|
import path6 from "path";
|
|
3670
3714
|
function writeAtomic(target, contents, mode) {
|
|
3671
3715
|
const tmp = `${target}.tmp`;
|
|
3672
|
-
|
|
3673
|
-
|
|
3716
|
+
fs6.writeFileSync(tmp, contents, { mode });
|
|
3717
|
+
fs6.renameSync(tmp, target);
|
|
3674
3718
|
if (process.platform !== "win32")
|
|
3675
|
-
|
|
3719
|
+
fs6.chmodSync(target, mode);
|
|
3676
3720
|
}
|
|
3677
3721
|
|
|
3678
3722
|
class ConfigStore {
|
|
@@ -3688,16 +3732,16 @@ class ConfigStore {
|
|
|
3688
3732
|
}
|
|
3689
3733
|
load() {
|
|
3690
3734
|
this.loadError = undefined;
|
|
3691
|
-
|
|
3735
|
+
fs6.mkdirSync(this.paths.configDir, { recursive: true, mode: DIR_MODE });
|
|
3692
3736
|
if (process.platform !== "win32") {
|
|
3693
3737
|
try {
|
|
3694
|
-
|
|
3738
|
+
fs6.chmodSync(this.paths.configDir, DIR_MODE);
|
|
3695
3739
|
} catch {}
|
|
3696
3740
|
}
|
|
3697
3741
|
this.config = structuredClone(DEFAULT_CONFIG);
|
|
3698
|
-
if (
|
|
3742
|
+
if (fs6.existsSync(this.paths.configFile)) {
|
|
3699
3743
|
try {
|
|
3700
|
-
const parsed = parseConfig(JSON.parse(
|
|
3744
|
+
const parsed = parseConfig(JSON.parse(fs6.readFileSync(this.paths.configFile, "utf8")));
|
|
3701
3745
|
if ("errors" in parsed)
|
|
3702
3746
|
this.loadError = parsed.errors.join("; ");
|
|
3703
3747
|
else
|
|
@@ -3707,10 +3751,10 @@ class ConfigStore {
|
|
|
3707
3751
|
}
|
|
3708
3752
|
}
|
|
3709
3753
|
this.secrets = {};
|
|
3710
|
-
if (
|
|
3754
|
+
if (fs6.existsSync(this.paths.secretsFile)) {
|
|
3711
3755
|
this.tightenPermissions(this.paths.secretsFile);
|
|
3712
3756
|
try {
|
|
3713
|
-
const raw = JSON.parse(
|
|
3757
|
+
const raw = JSON.parse(fs6.readFileSync(this.paths.secretsFile, "utf8"));
|
|
3714
3758
|
if (raw !== null && typeof raw === "object") {
|
|
3715
3759
|
for (const [key, value] of Object.entries(raw)) {
|
|
3716
3760
|
if (typeof value === "string")
|
|
@@ -3726,8 +3770,8 @@ class ConfigStore {
|
|
|
3726
3770
|
if (process.platform === "win32")
|
|
3727
3771
|
return;
|
|
3728
3772
|
try {
|
|
3729
|
-
if ((
|
|
3730
|
-
|
|
3773
|
+
if ((fs6.statSync(file).mode & 63) !== 0)
|
|
3774
|
+
fs6.chmodSync(file, FILE_MODE);
|
|
3731
3775
|
} catch {}
|
|
3732
3776
|
}
|
|
3733
3777
|
get() {
|
|
@@ -3735,7 +3779,7 @@ class ConfigStore {
|
|
|
3735
3779
|
}
|
|
3736
3780
|
health() {
|
|
3737
3781
|
return configHealth({
|
|
3738
|
-
fileExists:
|
|
3782
|
+
fileExists: fs6.existsSync(this.paths.configFile),
|
|
3739
3783
|
parseError: this.loadError,
|
|
3740
3784
|
config: this.resolved()
|
|
3741
3785
|
});
|
|
@@ -3780,7 +3824,7 @@ class ConfigStore {
|
|
|
3780
3824
|
delete this.secrets[dottedPath];
|
|
3781
3825
|
else
|
|
3782
3826
|
this.secrets[dottedPath] = value;
|
|
3783
|
-
if (!
|
|
3827
|
+
if (!fs6.existsSync(this.paths.configFile)) {
|
|
3784
3828
|
writeAtomic(this.paths.configFile, `${JSON.stringify(this.config, null, 2)}
|
|
3785
3829
|
`, FILE_MODE);
|
|
3786
3830
|
}
|
|
@@ -3810,7 +3854,7 @@ class ConfigStore {
|
|
|
3810
3854
|
fingerprint() {
|
|
3811
3855
|
const stamp = (file) => {
|
|
3812
3856
|
try {
|
|
3813
|
-
return String(
|
|
3857
|
+
return String(fs6.statSync(file).mtimeMs);
|
|
3814
3858
|
} catch {
|
|
3815
3859
|
return "-";
|
|
3816
3860
|
}
|
|
@@ -3852,7 +3896,7 @@ var init_config_store = __esm(() => {
|
|
|
3852
3896
|
});
|
|
3853
3897
|
|
|
3854
3898
|
// packages/daemon/src/shell.ts
|
|
3855
|
-
import
|
|
3899
|
+
import fs7 from "fs";
|
|
3856
3900
|
import path7 from "path";
|
|
3857
3901
|
function findExecutable(name, env) {
|
|
3858
3902
|
const isWindows = env.platform === "win32";
|
|
@@ -3876,7 +3920,7 @@ function lookupOnPath(name) {
|
|
|
3876
3920
|
platform: process.platform,
|
|
3877
3921
|
pathEnv: process.env["PATH"] ?? "",
|
|
3878
3922
|
pathExt: process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD",
|
|
3879
|
-
exists: (candidate) =>
|
|
3923
|
+
exists: (candidate) => fs7.existsSync(candidate)
|
|
3880
3924
|
});
|
|
3881
3925
|
}
|
|
3882
3926
|
function resolveShell(kind) {
|
|
@@ -3913,7 +3957,7 @@ function resolvePosixShell(kind) {
|
|
|
3913
3957
|
candidates.push({ kind: "bash", exe: login });
|
|
3914
3958
|
candidates.push({ kind: "bash", exe: "/bin/bash" }, { kind: "bash", exe: "/usr/bin/bash" });
|
|
3915
3959
|
}
|
|
3916
|
-
const found = candidates.find((c) =>
|
|
3960
|
+
const found = candidates.find((c) => fs7.existsSync(c.exe));
|
|
3917
3961
|
if (found)
|
|
3918
3962
|
return found;
|
|
3919
3963
|
return { kind: "sh", exe: "/bin/sh" };
|
|
@@ -3964,18 +4008,18 @@ var cachedPwsh;
|
|
|
3964
4008
|
var init_shell = () => {};
|
|
3965
4009
|
|
|
3966
4010
|
// packages/daemon/src/paths.ts
|
|
3967
|
-
import
|
|
4011
|
+
import fs8 from "fs";
|
|
3968
4012
|
import os3 from "os";
|
|
3969
4013
|
import path8 from "path";
|
|
3970
4014
|
function ensureDataDirs() {
|
|
3971
4015
|
for (const dir of [PATHS.configDir, PATHS.stateDir, PATHS.runtimeDir, SCROLLBACK_DIR, LOG_DIR, EXT_DIR]) {
|
|
3972
|
-
|
|
4016
|
+
fs8.mkdirSync(dir, { recursive: true, mode: DIR_MODE2 });
|
|
3973
4017
|
}
|
|
3974
4018
|
if (process.platform === "win32")
|
|
3975
4019
|
return;
|
|
3976
4020
|
for (const dir of [PATHS.configDir, PATHS.stateDir, PATHS.runtimeDir]) {
|
|
3977
4021
|
try {
|
|
3978
|
-
|
|
4022
|
+
fs8.chmodSync(dir, DIR_MODE2);
|
|
3979
4023
|
} catch {}
|
|
3980
4024
|
}
|
|
3981
4025
|
}
|
|
@@ -3983,14 +4027,14 @@ function restrictFile(file) {
|
|
|
3983
4027
|
if (process.platform === "win32")
|
|
3984
4028
|
return;
|
|
3985
4029
|
try {
|
|
3986
|
-
|
|
4030
|
+
fs8.chmodSync(file, 384);
|
|
3987
4031
|
} catch {}
|
|
3988
4032
|
}
|
|
3989
4033
|
function log(message) {
|
|
3990
4034
|
const line = `${new Date().toISOString()} ${message}
|
|
3991
4035
|
`;
|
|
3992
4036
|
try {
|
|
3993
|
-
|
|
4037
|
+
fs8.appendFileSync(LOG_FILE, line);
|
|
3994
4038
|
} catch {}
|
|
3995
4039
|
}
|
|
3996
4040
|
var PATHS, CONFIG_DIR, STATE_DIR, RUNTIME_DIR, SCROLLBACK_DIR, LOG_DIR, EXT_DIR, DB_PATH, RUNTIME_FILE2, HOOK_ENDPOINT_FILE, DIR_MODE2 = 448, PIPE_PATH, LOG_FILE;
|
|
@@ -4014,7 +4058,7 @@ var init_paths = __esm(() => {
|
|
|
4014
4058
|
});
|
|
4015
4059
|
|
|
4016
4060
|
// packages/daemon/src/store.ts
|
|
4017
|
-
import
|
|
4061
|
+
import fs9 from "fs";
|
|
4018
4062
|
import path9 from "path";
|
|
4019
4063
|
import { Database } from "bun:sqlite";
|
|
4020
4064
|
function rowToInfo(row) {
|
|
@@ -4046,7 +4090,7 @@ function rowToInfo(row) {
|
|
|
4046
4090
|
class Store {
|
|
4047
4091
|
db;
|
|
4048
4092
|
constructor(dbPath = DB_PATH) {
|
|
4049
|
-
|
|
4093
|
+
fs9.mkdirSync(path9.dirname(dbPath), { recursive: true });
|
|
4050
4094
|
this.db = new Database(dbPath, { strict: true });
|
|
4051
4095
|
this.db.run("PRAGMA journal_mode = WAL");
|
|
4052
4096
|
this.db.run(`
|
|
@@ -4125,8 +4169,10 @@ class Store {
|
|
|
4125
4169
|
this.db.prepare(`UPDATE sessions SET state = 'running', exit_code = NULL, exited_at = NULL,
|
|
4126
4170
|
agent_command = ?, pty_pid = ? WHERE id = ?`).run(JSON.stringify(agentCommand), ptyPid ?? null, id);
|
|
4127
4171
|
}
|
|
4128
|
-
markStaleRunningAsExited() {
|
|
4129
|
-
|
|
4172
|
+
markStaleRunningAsExited(keep = []) {
|
|
4173
|
+
const holes = keep.map(() => "?").join(",");
|
|
4174
|
+
const sql = keep.length === 0 ? `UPDATE sessions SET state = 'exited', exited_at = ? WHERE state != 'exited'` : `UPDATE sessions SET state = 'exited', exited_at = ? WHERE state != 'exited' AND id NOT IN (${holes})`;
|
|
4175
|
+
return this.db.prepare(sql).run(Date.now(), ...keep).changes;
|
|
4130
4176
|
}
|
|
4131
4177
|
listSessions() {
|
|
4132
4178
|
const rows = this.db.prepare(`SELECT * FROM sessions ORDER BY created_at DESC`).all();
|
|
@@ -4212,11 +4258,11 @@ var init_store = __esm(() => {
|
|
|
4212
4258
|
});
|
|
4213
4259
|
|
|
4214
4260
|
// packages/daemon/src/config-migrate.ts
|
|
4215
|
-
import
|
|
4261
|
+
import fs10 from "fs";
|
|
4216
4262
|
import path10 from "path";
|
|
4217
4263
|
import { Database as Database2 } from "bun:sqlite";
|
|
4218
4264
|
function checkpointWal(dbFile) {
|
|
4219
|
-
if (!
|
|
4265
|
+
if (!fs10.existsSync(dbFile))
|
|
4220
4266
|
return;
|
|
4221
4267
|
try {
|
|
4222
4268
|
const db = new Database2(dbFile);
|
|
@@ -4226,30 +4272,30 @@ function checkpointWal(dbFile) {
|
|
|
4226
4272
|
}
|
|
4227
4273
|
function relocate(source, target) {
|
|
4228
4274
|
try {
|
|
4229
|
-
|
|
4275
|
+
fs10.renameSync(source, target);
|
|
4230
4276
|
} catch {
|
|
4231
|
-
|
|
4277
|
+
fs10.cpSync(source, target, { recursive: true });
|
|
4232
4278
|
}
|
|
4233
4279
|
}
|
|
4234
4280
|
function moveInto(from, to, notes) {
|
|
4235
4281
|
checkpointWal(path10.join(from, "axterm.db"));
|
|
4236
|
-
|
|
4282
|
+
fs10.mkdirSync(to, { recursive: true });
|
|
4237
4283
|
for (const entry of STATE_ENTRIES) {
|
|
4238
4284
|
const source = path10.join(from, entry);
|
|
4239
4285
|
const target = path10.join(to, entry);
|
|
4240
|
-
if (!
|
|
4286
|
+
if (!fs10.existsSync(source))
|
|
4241
4287
|
continue;
|
|
4242
|
-
if (!
|
|
4288
|
+
if (!fs10.existsSync(target)) {
|
|
4243
4289
|
relocate(source, target);
|
|
4244
4290
|
notes.push(`moved ${entry}`);
|
|
4245
4291
|
continue;
|
|
4246
4292
|
}
|
|
4247
|
-
if (!
|
|
4293
|
+
if (!fs10.statSync(source).isDirectory() || !fs10.statSync(target).isDirectory())
|
|
4248
4294
|
continue;
|
|
4249
4295
|
let moved = 0;
|
|
4250
|
-
for (const child of
|
|
4296
|
+
for (const child of fs10.readdirSync(source)) {
|
|
4251
4297
|
const childTarget = path10.join(target, child);
|
|
4252
|
-
if (
|
|
4298
|
+
if (fs10.existsSync(childTarget))
|
|
4253
4299
|
continue;
|
|
4254
4300
|
relocate(path10.join(source, child), childTarget);
|
|
4255
4301
|
moved += 1;
|
|
@@ -4261,29 +4307,29 @@ function moveInto(from, to, notes) {
|
|
|
4261
4307
|
function migrateLegacyConfig(input) {
|
|
4262
4308
|
const notes = [];
|
|
4263
4309
|
const storePaths = defaultConfigStorePaths(input.configDir);
|
|
4264
|
-
if (
|
|
4310
|
+
if (fs10.existsSync(storePaths.configFile))
|
|
4265
4311
|
return { migrated: false, notes };
|
|
4266
|
-
const configSource = input.legacyRoots.find((dir) =>
|
|
4312
|
+
const configSource = input.legacyRoots.find((dir) => fs10.existsSync(path10.join(dir, "config.json")));
|
|
4267
4313
|
if (configSource !== undefined) {
|
|
4268
|
-
|
|
4269
|
-
|
|
4314
|
+
fs10.mkdirSync(input.configDir, { recursive: true });
|
|
4315
|
+
fs10.copyFileSync(path10.join(configSource, "config.json"), storePaths.configFile);
|
|
4270
4316
|
const secrets = path10.join(configSource, "secrets.json");
|
|
4271
|
-
if (
|
|
4272
|
-
|
|
4317
|
+
if (fs10.existsSync(secrets)) {
|
|
4318
|
+
fs10.copyFileSync(secrets, storePaths.secretsFile);
|
|
4273
4319
|
try {
|
|
4274
|
-
|
|
4320
|
+
fs10.chmodSync(storePaths.secretsFile, 384);
|
|
4275
4321
|
} catch {}
|
|
4276
4322
|
}
|
|
4277
|
-
|
|
4323
|
+
fs10.writeFileSync(path10.join(configSource, "MIGRATED.txt"), `axterm now reads its configuration from:
|
|
4278
4324
|
${input.configDir}
|
|
4279
4325
|
` + `These files are no longer used. secrets.json still holds your bot token \u2014 delete this directory once you are satisfied.
|
|
4280
4326
|
`);
|
|
4281
4327
|
notes.push(`adopted configuration from ${configSource}`);
|
|
4282
4328
|
}
|
|
4283
|
-
const legacy = input.legacyRoots.find((dir) =>
|
|
4329
|
+
const legacy = input.legacyRoots.find((dir) => fs10.existsSync(path10.join(dir, "axterm.db")));
|
|
4284
4330
|
if (legacy !== undefined) {
|
|
4285
4331
|
moveInto(legacy, input.stateDir, notes);
|
|
4286
|
-
|
|
4332
|
+
fs10.writeFileSync(path10.join(legacy, "MIGRATED.txt"), `axterm now stores state in:
|
|
4287
4333
|
${input.stateDir}
|
|
4288
4334
|
and configuration in:
|
|
4289
4335
|
${input.configDir}
|
|
@@ -4293,7 +4339,7 @@ and configuration in:
|
|
|
4293
4339
|
if (configSource !== undefined)
|
|
4294
4340
|
return { migrated: true, ...legacy ? { from: legacy } : {}, notes };
|
|
4295
4341
|
const dbFile = path10.join(input.stateDir, "axterm.db");
|
|
4296
|
-
if (!
|
|
4342
|
+
if (!fs10.existsSync(dbFile))
|
|
4297
4343
|
return { migrated: notes.length > 0, ...legacy ? { from: legacy } : {}, notes };
|
|
4298
4344
|
const store = new Store(dbFile);
|
|
4299
4345
|
const machine2 = store.getConfig("axterm_machine");
|
|
@@ -5131,13 +5177,13 @@ export default function axtermAgentStatus(pi: PiLike): void {\r
|
|
|
5131
5177
|
var init_axterm_agent_status = () => {};
|
|
5132
5178
|
|
|
5133
5179
|
// packages/daemon/src/ext-install.ts
|
|
5134
|
-
import
|
|
5180
|
+
import fs11 from "fs";
|
|
5135
5181
|
import path11 from "path";
|
|
5136
5182
|
function installExtensionAsset(targetDir) {
|
|
5137
|
-
|
|
5183
|
+
fs11.mkdirSync(targetDir, { recursive: true });
|
|
5138
5184
|
const target = path11.join(targetDir, "axterm-agent-status.mjs");
|
|
5139
5185
|
const js = new Bun.Transpiler({ loader: "ts" }).transformSync(extensionSource);
|
|
5140
|
-
|
|
5186
|
+
fs11.writeFileSync(target, MANAGED_HEADER + js);
|
|
5141
5187
|
return target;
|
|
5142
5188
|
}
|
|
5143
5189
|
var extensionSource, MANAGED_HEADER = `// @axterm-managed-omp-extension
|
|
@@ -5150,7 +5196,7 @@ var init_ext_install = __esm(() => {
|
|
|
5150
5196
|
|
|
5151
5197
|
// packages/daemon/src/workspace-local.ts
|
|
5152
5198
|
import { spawn as spawn2 } from "child_process";
|
|
5153
|
-
import
|
|
5199
|
+
import fs12 from "fs/promises";
|
|
5154
5200
|
import path12 from "path";
|
|
5155
5201
|
function isAbsent(error) {
|
|
5156
5202
|
const code = error?.code;
|
|
@@ -5164,10 +5210,10 @@ class LocalWorkspace {
|
|
|
5164
5210
|
this.stagingDir = stagingDir;
|
|
5165
5211
|
}
|
|
5166
5212
|
async list(dir) {
|
|
5167
|
-
const dirents = await
|
|
5213
|
+
const dirents = await fs12.readdir(dir, { withFileTypes: true });
|
|
5168
5214
|
const stats = await Promise.all(dirents.map(async (dirent) => {
|
|
5169
5215
|
try {
|
|
5170
|
-
return await
|
|
5216
|
+
return await fs12.lstat(path12.join(dir, dirent.name));
|
|
5171
5217
|
} catch (error) {
|
|
5172
5218
|
if (isAbsent(error))
|
|
5173
5219
|
return;
|
|
@@ -5191,7 +5237,7 @@ class LocalWorkspace {
|
|
|
5191
5237
|
}
|
|
5192
5238
|
async stat(target) {
|
|
5193
5239
|
try {
|
|
5194
|
-
const stat = await
|
|
5240
|
+
const stat = await fs12.stat(target);
|
|
5195
5241
|
const kind = stat.isFile() ? "file" : stat.isDirectory() ? "dir" : "other";
|
|
5196
5242
|
return { kind, size: stat.size, mtimeMs: stat.mtimeMs };
|
|
5197
5243
|
} catch (error) {
|
|
@@ -5202,8 +5248,8 @@ class LocalWorkspace {
|
|
|
5202
5248
|
}
|
|
5203
5249
|
async read(file, limit) {
|
|
5204
5250
|
if (limit === undefined)
|
|
5205
|
-
return await
|
|
5206
|
-
const handle = await
|
|
5251
|
+
return await fs12.readFile(file);
|
|
5252
|
+
const handle = await fs12.open(file, "r");
|
|
5207
5253
|
try {
|
|
5208
5254
|
const buffer = Buffer.allocUnsafe(Math.min(limit, (await handle.stat()).size));
|
|
5209
5255
|
let filled = 0;
|
|
@@ -5222,10 +5268,10 @@ class LocalWorkspace {
|
|
|
5222
5268
|
return Promise.resolve(file);
|
|
5223
5269
|
}
|
|
5224
5270
|
async mkdirp(dir, mode) {
|
|
5225
|
-
await
|
|
5271
|
+
await fs12.mkdir(dir, mode === undefined ? { recursive: true } : { recursive: true, mode });
|
|
5226
5272
|
}
|
|
5227
5273
|
async write(file, bytes, mode) {
|
|
5228
|
-
await
|
|
5274
|
+
await fs12.writeFile(file, bytes, mode === undefined ? undefined : { mode });
|
|
5229
5275
|
}
|
|
5230
5276
|
exec(argv, cwd) {
|
|
5231
5277
|
const [command, ...args] = argv;
|
|
@@ -5254,7 +5300,7 @@ var init_workspace_local = () => {};
|
|
|
5254
5300
|
|
|
5255
5301
|
// packages/daemon/src/hook-host.ts
|
|
5256
5302
|
import { randomBytes } from "crypto";
|
|
5257
|
-
import
|
|
5303
|
+
import fs13 from "fs";
|
|
5258
5304
|
import http from "http";
|
|
5259
5305
|
import path13 from "path";
|
|
5260
5306
|
function header(req, name) {
|
|
@@ -5270,6 +5316,7 @@ class HookHost {
|
|
|
5270
5316
|
}
|
|
5271
5317
|
sessions = new Map;
|
|
5272
5318
|
pendingAsks = new Map;
|
|
5319
|
+
refusedHooks = new Set;
|
|
5273
5320
|
endpointFile;
|
|
5274
5321
|
server;
|
|
5275
5322
|
askOpenHandler;
|
|
@@ -5296,16 +5343,19 @@ class HookHost {
|
|
|
5296
5343
|
return promise;
|
|
5297
5344
|
}
|
|
5298
5345
|
writeEndpointFile(port) {
|
|
5299
|
-
|
|
5346
|
+
fs13.mkdirSync(path13.dirname(this.endpointFile), { recursive: true });
|
|
5300
5347
|
const tmp = `${this.endpointFile}.tmp`;
|
|
5301
|
-
|
|
5348
|
+
fs13.writeFileSync(tmp, `AXTERM_HOOK_PORT=${port}
|
|
5302
5349
|
AXTERM_HOOK_TOKEN=${this.token}
|
|
5303
5350
|
`);
|
|
5304
|
-
|
|
5351
|
+
fs13.renameSync(tmp, this.endpointFile);
|
|
5305
5352
|
}
|
|
5306
5353
|
registerSession(sessionId, launchToken, onEvent) {
|
|
5307
5354
|
this.sessions.set(sessionId, { launchToken, onEvent });
|
|
5308
5355
|
}
|
|
5356
|
+
launchMatches(session, presented) {
|
|
5357
|
+
return session.launchToken === undefined || session.launchToken === presented;
|
|
5358
|
+
}
|
|
5309
5359
|
unregisterSession(sessionId) {
|
|
5310
5360
|
this.sessions.delete(sessionId);
|
|
5311
5361
|
for (const [askId, pending] of this.pendingAsks) {
|
|
@@ -5394,7 +5444,7 @@ AXTERM_HOOK_TOKEN=${this.token}
|
|
|
5394
5444
|
res.writeHead(404).end();
|
|
5395
5445
|
return;
|
|
5396
5446
|
}
|
|
5397
|
-
if (
|
|
5447
|
+
if (!this.launchMatches(session, body.launchToken)) {
|
|
5398
5448
|
res.writeHead(403).end();
|
|
5399
5449
|
return;
|
|
5400
5450
|
}
|
|
@@ -5429,7 +5479,9 @@ AXTERM_HOOK_TOKEN=${this.token}
|
|
|
5429
5479
|
return;
|
|
5430
5480
|
}
|
|
5431
5481
|
res.writeHead(200).end();
|
|
5432
|
-
|
|
5482
|
+
const named = input;
|
|
5483
|
+
const events = withConversationId(map(input), { sessionId: named.session_id, subagentId: named.agent_id });
|
|
5484
|
+
this.dispatchHookPayloads(auth.session, events, agent);
|
|
5433
5485
|
}
|
|
5434
5486
|
handleCodex(req, raw, res) {
|
|
5435
5487
|
const auth = this.agentHookSession(req);
|
|
@@ -5485,8 +5537,15 @@ AXTERM_HOOK_TOKEN=${this.token}
|
|
|
5485
5537
|
const sessionId = header(req, "x-axterm-session");
|
|
5486
5538
|
const launchToken = header(req, "x-axterm-launch");
|
|
5487
5539
|
const session = sessionId === undefined ? undefined : this.sessions.get(sessionId);
|
|
5488
|
-
if (sessionId === undefined || !session || session
|
|
5540
|
+
if (sessionId === undefined || !session || !this.launchMatches(session, launchToken)) {
|
|
5541
|
+
const why = sessionId === undefined ? "no session header" : !session ? "unknown session" : "launch token mismatch";
|
|
5542
|
+
const key = `${req.url}\x00${sessionId ?? ""}\x00${why}`;
|
|
5543
|
+
if (!this.refusedHooks.has(key)) {
|
|
5544
|
+
this.refusedHooks.add(key);
|
|
5545
|
+
log(`refused hook post to ${req.url}: ${why} (session ${sessionId ?? "-"}) \u2014 reported once per session`);
|
|
5546
|
+
}
|
|
5489
5547
|
return;
|
|
5548
|
+
}
|
|
5490
5549
|
return { sessionId, session };
|
|
5491
5550
|
}
|
|
5492
5551
|
dispatchHookPayloads(session, payloads, agent) {
|
|
@@ -5576,7 +5635,7 @@ var init_hook_host = __esm(() => {
|
|
|
5576
5635
|
});
|
|
5577
5636
|
|
|
5578
5637
|
// packages/daemon/src/rpc-server.ts
|
|
5579
|
-
import
|
|
5638
|
+
import fs14 from "fs";
|
|
5580
5639
|
import net2 from "net";
|
|
5581
5640
|
function writeFrame(socket, frame) {
|
|
5582
5641
|
if (!socket.destroyed)
|
|
@@ -5686,6 +5745,8 @@ function startRpcServer(options) {
|
|
|
5686
5745
|
session.write(Buffer.from(p["dataB64"], "base64").toString("utf8"));
|
|
5687
5746
|
return {};
|
|
5688
5747
|
}
|
|
5748
|
+
case "session.choose":
|
|
5749
|
+
return { answered: manager.answerScreenChoice(p["id"], p["index"]) };
|
|
5689
5750
|
case "attach": {
|
|
5690
5751
|
const session = manager.resolveLive(p["id"]);
|
|
5691
5752
|
cleanup();
|
|
@@ -5812,7 +5873,7 @@ function startRpcServer(options) {
|
|
|
5812
5873
|
});
|
|
5813
5874
|
if (process.platform !== "win32") {
|
|
5814
5875
|
try {
|
|
5815
|
-
|
|
5876
|
+
fs14.unlinkSync(pipePath);
|
|
5816
5877
|
} catch {}
|
|
5817
5878
|
}
|
|
5818
5879
|
server.listen(pipePath, () => {
|
|
@@ -5827,18 +5888,35 @@ var init_rpc_server = __esm(() => {
|
|
|
5827
5888
|
});
|
|
5828
5889
|
|
|
5829
5890
|
// packages/daemon/src/codex-home.ts
|
|
5830
|
-
import
|
|
5891
|
+
import fs15 from "fs";
|
|
5831
5892
|
import path14 from "path";
|
|
5832
5893
|
function codexTrustKeyPath(home) {
|
|
5833
5894
|
const resolved = (() => {
|
|
5834
5895
|
try {
|
|
5835
|
-
return
|
|
5896
|
+
return fs15.realpathSync.native(home);
|
|
5836
5897
|
} catch {
|
|
5837
5898
|
return path14.resolve(home);
|
|
5838
5899
|
}
|
|
5839
5900
|
})();
|
|
5840
5901
|
return path14.join(resolved, "hooks.json");
|
|
5841
5902
|
}
|
|
5903
|
+
function referenceCredential(source, target, ops) {
|
|
5904
|
+
const symlink = ops?.symlink ?? ((s, t2) => fs15.symlinkSync(s, t2, "file"));
|
|
5905
|
+
const link = ops?.link ?? fs15.linkSync;
|
|
5906
|
+
try {
|
|
5907
|
+
symlink(source, target);
|
|
5908
|
+
return "symlink";
|
|
5909
|
+
} catch {
|
|
5910
|
+
try {
|
|
5911
|
+
link(source, target);
|
|
5912
|
+
return "hardlink";
|
|
5913
|
+
} catch {
|
|
5914
|
+
fs15.copyFileSync(source, target);
|
|
5915
|
+
fs15.chmodSync(target, 384);
|
|
5916
|
+
return "copy";
|
|
5917
|
+
}
|
|
5918
|
+
}
|
|
5919
|
+
}
|
|
5842
5920
|
var init_codex_home = () => {};
|
|
5843
5921
|
|
|
5844
5922
|
// packages/daemon/src/host-env.ts
|
|
@@ -5853,15 +5931,36 @@ function stripHostAttribution(env) {
|
|
|
5853
5931
|
}
|
|
5854
5932
|
var ATTRIBUTION_PREFIXES, ATTRIBUTION_KEEP;
|
|
5855
5933
|
var init_host_env = __esm(() => {
|
|
5856
|
-
ATTRIBUTION_PREFIXES = ["ORCA_"];
|
|
5934
|
+
ATTRIBUTION_PREFIXES = ["ORCA_", "AXTERM_"];
|
|
5857
5935
|
ATTRIBUTION_KEEP = new Set([
|
|
5858
5936
|
"ORCA_CODEX_HOME",
|
|
5859
5937
|
"ORCA_OPENCODE_CONFIG_DIR",
|
|
5860
5938
|
"ORCA_USER_DATA_PATH",
|
|
5861
|
-
"ORCA_APP_VERSION"
|
|
5939
|
+
"ORCA_APP_VERSION",
|
|
5940
|
+
"AXTERM_CONFIG_DIR",
|
|
5941
|
+
"AXTERM_STATE_DIR",
|
|
5942
|
+
"AXTERM_RUNTIME_DIR",
|
|
5943
|
+
"AXTERM_LANG",
|
|
5944
|
+
"AXTERM_PTY_HOST",
|
|
5945
|
+
"AXTERM_PTY_HOST_MAIN"
|
|
5862
5946
|
]);
|
|
5863
5947
|
});
|
|
5864
5948
|
|
|
5949
|
+
// packages/daemon/src/session-env.ts
|
|
5950
|
+
function sessionEnv(base, identity) {
|
|
5951
|
+
return {
|
|
5952
|
+
...stripHostAttribution(base),
|
|
5953
|
+
AXTERM_SESSION_ID: identity.sessionId,
|
|
5954
|
+
AXTERM_LAUNCH_TOKEN: identity.launchToken,
|
|
5955
|
+
AXTERM_HOOK_PORT: String(identity.hookPort),
|
|
5956
|
+
AXTERM_HOOK_TOKEN: identity.hookToken,
|
|
5957
|
+
AXTERM_HOOK_ENDPOINT: identity.endpointFile
|
|
5958
|
+
};
|
|
5959
|
+
}
|
|
5960
|
+
var init_session_env = __esm(() => {
|
|
5961
|
+
init_host_env();
|
|
5962
|
+
});
|
|
5963
|
+
|
|
5865
5964
|
// packages/daemon/src/git-porcelain.ts
|
|
5866
5965
|
function parseWorktreeList(porcelain) {
|
|
5867
5966
|
const entries = [];
|
|
@@ -5900,7 +5999,7 @@ function slugifyBranch(branch) {
|
|
|
5900
5999
|
var MAX_SLUG_LENGTH = 60;
|
|
5901
6000
|
|
|
5902
6001
|
// packages/daemon/src/git.ts
|
|
5903
|
-
import
|
|
6002
|
+
import fs16 from "fs";
|
|
5904
6003
|
import path15 from "path";
|
|
5905
6004
|
async function git(ws, cwd, args) {
|
|
5906
6005
|
const run = await ws.exec(["git", ...args], cwd);
|
|
@@ -5920,7 +6019,7 @@ async function gitOrThrow(ws, cwd, args) {
|
|
|
5920
6019
|
function canonicalPath(target) {
|
|
5921
6020
|
let resolved = path15.resolve(target);
|
|
5922
6021
|
try {
|
|
5923
|
-
resolved =
|
|
6022
|
+
resolved = fs16.realpathSync.native(resolved);
|
|
5924
6023
|
} catch {}
|
|
5925
6024
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
5926
6025
|
}
|
|
@@ -5944,7 +6043,7 @@ async function createWorktree(ws, options) {
|
|
|
5944
6043
|
const existing = (await listWorktrees(ws, repoRoot)).find((w) => canonicalPath(w.path) === canonicalPath(target));
|
|
5945
6044
|
if (existing)
|
|
5946
6045
|
return { path: target, branch, createdBranch: false };
|
|
5947
|
-
|
|
6046
|
+
fs16.mkdirSync(path15.dirname(target), { recursive: true });
|
|
5948
6047
|
const exists = (await git(ws, repoRoot, ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`])).ok;
|
|
5949
6048
|
const args = exists ? ["worktree", "add", target, branch] : ["worktree", "add", "-b", branch, target, ...base !== undefined ? [base] : []];
|
|
5950
6049
|
await gitOrThrow(ws, repoRoot, args);
|
|
@@ -6035,7 +6134,7 @@ var init_git = __esm(() => {
|
|
|
6035
6134
|
});
|
|
6036
6135
|
|
|
6037
6136
|
// packages/daemon/src/claude-store.ts
|
|
6038
|
-
import
|
|
6137
|
+
import fs17 from "fs";
|
|
6039
6138
|
import os4 from "os";
|
|
6040
6139
|
import path16 from "path";
|
|
6041
6140
|
function claudeConfigDir(env = process.env) {
|
|
@@ -6061,19 +6160,55 @@ function claudeConversationExists(configDir, conversationId) {
|
|
|
6061
6160
|
const projects = path16.join(configDir, "projects");
|
|
6062
6161
|
let entries;
|
|
6063
6162
|
try {
|
|
6064
|
-
entries =
|
|
6163
|
+
entries = fs17.readdirSync(projects);
|
|
6065
6164
|
} catch {
|
|
6066
6165
|
return;
|
|
6067
6166
|
}
|
|
6068
6167
|
const file = `${conversationId}.jsonl`;
|
|
6069
6168
|
for (const entry of entries) {
|
|
6070
6169
|
try {
|
|
6071
|
-
if (
|
|
6170
|
+
if (fs17.existsSync(path16.join(projects, entry, file)))
|
|
6072
6171
|
return true;
|
|
6073
6172
|
} catch {}
|
|
6074
6173
|
}
|
|
6075
6174
|
return false;
|
|
6076
6175
|
}
|
|
6176
|
+
function claudeConversationCwd(configDir, conversationId) {
|
|
6177
|
+
const projects = path16.join(configDir, "projects");
|
|
6178
|
+
let entries;
|
|
6179
|
+
try {
|
|
6180
|
+
entries = fs17.readdirSync(projects);
|
|
6181
|
+
} catch {
|
|
6182
|
+
return;
|
|
6183
|
+
}
|
|
6184
|
+
for (const entry of entries) {
|
|
6185
|
+
const file = path16.join(projects, entry, `${conversationId}.jsonl`);
|
|
6186
|
+
let head;
|
|
6187
|
+
try {
|
|
6188
|
+
const handle = fs17.openSync(file, "r");
|
|
6189
|
+
try {
|
|
6190
|
+
const buffer = Buffer.alloc(64 * 1024);
|
|
6191
|
+
const read = fs17.readSync(handle, buffer, 0, buffer.byteLength, 0);
|
|
6192
|
+
head = buffer.subarray(0, read).toString("utf8");
|
|
6193
|
+
} finally {
|
|
6194
|
+
fs17.closeSync(handle);
|
|
6195
|
+
}
|
|
6196
|
+
} catch {
|
|
6197
|
+
continue;
|
|
6198
|
+
}
|
|
6199
|
+
for (const line of head.split(/\r?\n/)) {
|
|
6200
|
+
if (line.length === 0)
|
|
6201
|
+
continue;
|
|
6202
|
+
try {
|
|
6203
|
+
const record = JSON.parse(line);
|
|
6204
|
+
const cwd = record.cwd;
|
|
6205
|
+
if (typeof cwd === "string" && cwd.length > 0)
|
|
6206
|
+
return cwd;
|
|
6207
|
+
} catch {}
|
|
6208
|
+
}
|
|
6209
|
+
}
|
|
6210
|
+
return;
|
|
6211
|
+
}
|
|
6077
6212
|
var CONVERSATION_ID;
|
|
6078
6213
|
var init_claude_store = __esm(() => {
|
|
6079
6214
|
CONVERSATION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -6194,6 +6329,7 @@ function connectPtyHost(options) {
|
|
|
6194
6329
|
const decoder = new FrameDecoder;
|
|
6195
6330
|
const ptys = new Map;
|
|
6196
6331
|
const pending = new Map;
|
|
6332
|
+
const subscriptions = new Map;
|
|
6197
6333
|
const dataListeners = [];
|
|
6198
6334
|
const rawDecoders = new Map;
|
|
6199
6335
|
let welcomed = false;
|
|
@@ -6207,6 +6343,9 @@ function connectPtyHost(options) {
|
|
|
6207
6343
|
for (const waiter of pending.values())
|
|
6208
6344
|
waiter.reject(error);
|
|
6209
6345
|
pending.clear();
|
|
6346
|
+
for (const waiter of subscriptions.values())
|
|
6347
|
+
waiter.reject(error);
|
|
6348
|
+
subscriptions.clear();
|
|
6210
6349
|
socket.destroy();
|
|
6211
6350
|
};
|
|
6212
6351
|
const timer = setTimeout(() => fail(new Error(`pty host did not greet within ${options.timeoutMs ?? 5000}ms`)), options.timeoutMs ?? 5000);
|
|
@@ -6273,6 +6412,15 @@ function connectPtyHost(options) {
|
|
|
6273
6412
|
term: spawnOptions.term
|
|
6274
6413
|
}));
|
|
6275
6414
|
}),
|
|
6415
|
+
adopt: (session) => new Promise((done, failed) => {
|
|
6416
|
+
const pty = ptys.get(session.id) ?? new RemotePty(session.id, session.pid, send);
|
|
6417
|
+
ptys.set(session.id, pty);
|
|
6418
|
+
subscriptions.set(session.id, {
|
|
6419
|
+
resolve: () => done(pty),
|
|
6420
|
+
reject: failed
|
|
6421
|
+
});
|
|
6422
|
+
send(controlFrame({ t: "subscribe", id: session.id, replay: true }));
|
|
6423
|
+
}),
|
|
6276
6424
|
onData: (listener) => dataListeners.push(listener),
|
|
6277
6425
|
close: () => socket.destroy()
|
|
6278
6426
|
});
|
|
@@ -6288,10 +6436,17 @@ function connectPtyHost(options) {
|
|
|
6288
6436
|
pending.delete(message.id);
|
|
6289
6437
|
continue;
|
|
6290
6438
|
}
|
|
6439
|
+
if (message.t === "subscribed") {
|
|
6440
|
+
subscriptions.get(message.id)?.resolve();
|
|
6441
|
+
subscriptions.delete(message.id);
|
|
6442
|
+
continue;
|
|
6443
|
+
}
|
|
6291
6444
|
if (message.t === "exit") {
|
|
6292
6445
|
const pty = ptys.get(message.id);
|
|
6293
6446
|
ptys.delete(message.id);
|
|
6294
6447
|
rawDecoders.delete(message.id);
|
|
6448
|
+
subscriptions.get(message.id)?.reject(new Error(`session ${message.id} exited`));
|
|
6449
|
+
subscriptions.delete(message.id);
|
|
6295
6450
|
pty?.finish(message.code);
|
|
6296
6451
|
}
|
|
6297
6452
|
}
|
|
@@ -6304,15 +6459,18 @@ var init_pty_host_client = __esm(() => {
|
|
|
6304
6459
|
|
|
6305
6460
|
// packages/daemon/src/pty-host-launch.ts
|
|
6306
6461
|
import { spawn as spawn3 } from "child_process";
|
|
6307
|
-
import
|
|
6462
|
+
import fs18 from "fs";
|
|
6308
6463
|
import os5 from "os";
|
|
6309
6464
|
import path17 from "path";
|
|
6310
6465
|
function hostFile(runtimeDir) {
|
|
6311
6466
|
return path17.join(runtimeDir, "pty-host.json");
|
|
6312
6467
|
}
|
|
6468
|
+
function readPtyHostIn(runtimeDir) {
|
|
6469
|
+
return readPtyHostFile(hostFile(runtimeDir));
|
|
6470
|
+
}
|
|
6313
6471
|
function readPtyHostFile(file) {
|
|
6314
6472
|
try {
|
|
6315
|
-
const parsed = JSON.parse(
|
|
6473
|
+
const parsed = JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
6316
6474
|
if (typeof parsed !== "object" || parsed === null)
|
|
6317
6475
|
return;
|
|
6318
6476
|
const { pid, endpoint, token, version: version2 } = parsed;
|
|
@@ -6344,7 +6502,7 @@ async function ensurePtyHost(location) {
|
|
|
6344
6502
|
log(`pty host: handoff file names a dead pid ${existing.pid}, starting a new host`);
|
|
6345
6503
|
const endpoint = hostEndpoint(location.runtimeDir);
|
|
6346
6504
|
if (process.platform !== "win32")
|
|
6347
|
-
|
|
6505
|
+
fs18.rmSync(endpoint, { force: true });
|
|
6348
6506
|
const token = shortHash(`${location.runtimeDir}:${Date.now()}:${Math.random()}`) + shortHash(String(process.pid));
|
|
6349
6507
|
const launch = ptyHostLaunch({ execPath: process.execPath, mainScript: location.mainScript ?? Bun.main });
|
|
6350
6508
|
const child = spawn3(launch.command, launch.args, {
|
|
@@ -6373,8 +6531,8 @@ async function ensurePtyHost(location) {
|
|
|
6373
6531
|
}
|
|
6374
6532
|
}
|
|
6375
6533
|
function writePtyHostFile(file, info) {
|
|
6376
|
-
|
|
6377
|
-
|
|
6534
|
+
fs18.mkdirSync(path17.dirname(file), { recursive: true, mode: 448 });
|
|
6535
|
+
fs18.writeFileSync(file, `${JSON.stringify(info, null, 2)}
|
|
6378
6536
|
`, { mode: 384 });
|
|
6379
6537
|
restrictFile(file);
|
|
6380
6538
|
}
|
|
@@ -6494,7 +6652,7 @@ var init_pty = () => {};
|
|
|
6494
6652
|
|
|
6495
6653
|
// packages/daemon/src/occupancy.ts
|
|
6496
6654
|
import { spawn as spawn4 } from "child_process";
|
|
6497
|
-
import
|
|
6655
|
+
import fs19 from "fs/promises";
|
|
6498
6656
|
function shellOccupied(shellPid) {
|
|
6499
6657
|
return process.platform === "win32" ? occupiedFromSnapshot(shellPid) : occupiedFromPidProbe(shellPid);
|
|
6500
6658
|
}
|
|
@@ -6539,11 +6697,11 @@ async function occupiedFromSnapshot(shellPid) {
|
|
|
6539
6697
|
}
|
|
6540
6698
|
async function childrenViaProc(pid) {
|
|
6541
6699
|
try {
|
|
6542
|
-
const children = await
|
|
6700
|
+
const children = await fs19.readFile(`/proc/${pid}/task/${pid}/children`, "utf8");
|
|
6543
6701
|
return children.trim().length > 0;
|
|
6544
6702
|
} catch {
|
|
6545
6703
|
try {
|
|
6546
|
-
await
|
|
6704
|
+
await fs19.access(`/proc/${pid}`);
|
|
6547
6705
|
return;
|
|
6548
6706
|
} catch {
|
|
6549
6707
|
return false;
|
|
@@ -6607,20 +6765,20 @@ var init_occupancy = __esm(() => {
|
|
|
6607
6765
|
});
|
|
6608
6766
|
|
|
6609
6767
|
// packages/daemon/src/agent-store.ts
|
|
6610
|
-
import
|
|
6768
|
+
import fs20 from "fs";
|
|
6611
6769
|
import os6 from "os";
|
|
6612
6770
|
import path19 from "path";
|
|
6613
6771
|
import { Database as Database3 } from "bun:sqlite";
|
|
6614
6772
|
function readTailLines(file, maxBytes) {
|
|
6615
6773
|
let fd;
|
|
6616
6774
|
try {
|
|
6617
|
-
fd =
|
|
6618
|
-
const size =
|
|
6775
|
+
fd = fs20.openSync(file, "r");
|
|
6776
|
+
const size = fs20.fstatSync(fd).size;
|
|
6619
6777
|
if (size === 0)
|
|
6620
6778
|
return [];
|
|
6621
6779
|
const length = Math.min(size, maxBytes);
|
|
6622
6780
|
const buffer = Buffer.allocUnsafe(length);
|
|
6623
|
-
|
|
6781
|
+
fs20.readSync(fd, buffer, 0, length, size - length);
|
|
6624
6782
|
const text = buffer.toString("utf8");
|
|
6625
6783
|
const lines = text.split(`
|
|
6626
6784
|
`);
|
|
@@ -6633,7 +6791,7 @@ function readTailLines(file, maxBytes) {
|
|
|
6633
6791
|
return [];
|
|
6634
6792
|
} finally {
|
|
6635
6793
|
if (fd !== undefined)
|
|
6636
|
-
|
|
6794
|
+
fs20.closeSync(fd);
|
|
6637
6795
|
}
|
|
6638
6796
|
}
|
|
6639
6797
|
function claudeConfigDir2() {
|
|
@@ -6651,7 +6809,7 @@ function findCodexRollout(sessionId) {
|
|
|
6651
6809
|
const dir = path19.join(root, ...shard2.split("/"));
|
|
6652
6810
|
let entries;
|
|
6653
6811
|
try {
|
|
6654
|
-
entries =
|
|
6812
|
+
entries = fs20.readdirSync(dir);
|
|
6655
6813
|
} catch {
|
|
6656
6814
|
continue;
|
|
6657
6815
|
}
|
|
@@ -6667,7 +6825,7 @@ function newestCodexRollout() {
|
|
|
6667
6825
|
const walk = (dir, depth) => {
|
|
6668
6826
|
let entries;
|
|
6669
6827
|
try {
|
|
6670
|
-
entries =
|
|
6828
|
+
entries = fs20.readdirSync(dir, { withFileTypes: true });
|
|
6671
6829
|
} catch {
|
|
6672
6830
|
return;
|
|
6673
6831
|
}
|
|
@@ -6681,7 +6839,7 @@ function newestCodexRollout() {
|
|
|
6681
6839
|
if (!entry.name.endsWith(".jsonl"))
|
|
6682
6840
|
continue;
|
|
6683
6841
|
try {
|
|
6684
|
-
const { mtimeMs } =
|
|
6842
|
+
const { mtimeMs } = fs20.statSync(full);
|
|
6685
6843
|
if (!best || mtimeMs > best.mtimeMs)
|
|
6686
6844
|
best = { file: full, mtimeMs };
|
|
6687
6845
|
} catch {}
|
|
@@ -6692,7 +6850,7 @@ function newestCodexRollout() {
|
|
|
6692
6850
|
}
|
|
6693
6851
|
function ompQuotaFacts() {
|
|
6694
6852
|
const file = ompAgentDb();
|
|
6695
|
-
if (!
|
|
6853
|
+
if (!fs20.existsSync(file))
|
|
6696
6854
|
return { agent: "omp", kind: "gauge" };
|
|
6697
6855
|
let db;
|
|
6698
6856
|
try {
|
|
@@ -6760,7 +6918,7 @@ function storeQuietForMs(agent, cwd, agentSessionId, nowMs) {
|
|
|
6760
6918
|
if (file === undefined)
|
|
6761
6919
|
return;
|
|
6762
6920
|
try {
|
|
6763
|
-
return Math.max(0, nowMs -
|
|
6921
|
+
return Math.max(0, nowMs - fs20.statSync(file).mtimeMs);
|
|
6764
6922
|
} catch {
|
|
6765
6923
|
return;
|
|
6766
6924
|
}
|
|
@@ -6770,15 +6928,15 @@ function readFileRange(file, from, to) {
|
|
|
6770
6928
|
return "";
|
|
6771
6929
|
let fd;
|
|
6772
6930
|
try {
|
|
6773
|
-
fd =
|
|
6931
|
+
fd = fs20.openSync(file, "r");
|
|
6774
6932
|
const buffer = Buffer.allocUnsafe(to - from);
|
|
6775
|
-
const read =
|
|
6933
|
+
const read = fs20.readSync(fd, buffer, 0, to - from, from);
|
|
6776
6934
|
return buffer.subarray(0, read).toString("utf8");
|
|
6777
6935
|
} catch {
|
|
6778
6936
|
return "";
|
|
6779
6937
|
} finally {
|
|
6780
6938
|
if (fd !== undefined)
|
|
6781
|
-
|
|
6939
|
+
fs20.closeSync(fd);
|
|
6782
6940
|
}
|
|
6783
6941
|
}
|
|
6784
6942
|
var QUOTA_TAIL_BYTES;
|
|
@@ -11229,15 +11387,55 @@ var require_addon_serialize = __commonJS((exports, module) => {
|
|
|
11229
11387
|
})());
|
|
11230
11388
|
});
|
|
11231
11389
|
|
|
11390
|
+
// packages/daemon/src/terminal-mirror.ts
|
|
11391
|
+
class TerminalMirror {
|
|
11392
|
+
scrollbackLines;
|
|
11393
|
+
term;
|
|
11394
|
+
serializeAddon;
|
|
11395
|
+
constructor(cols, rows, scrollbackLines) {
|
|
11396
|
+
this.scrollbackLines = scrollbackLines;
|
|
11397
|
+
this.term = new import_headless.Terminal({ cols, rows, scrollback: scrollbackLines, allowProposedApi: true });
|
|
11398
|
+
this.serializeAddon = new import_addon_serialize.SerializeAddon;
|
|
11399
|
+
this.term.loadAddon(this.serializeAddon);
|
|
11400
|
+
}
|
|
11401
|
+
write(data) {
|
|
11402
|
+
this.term.write(data);
|
|
11403
|
+
}
|
|
11404
|
+
resize(cols, rows) {
|
|
11405
|
+
this.term.resize(cols, rows);
|
|
11406
|
+
}
|
|
11407
|
+
serialize(maxChars = Number.POSITIVE_INFINITY) {
|
|
11408
|
+
const ansi = this.serializeAddon.serialize({ scrollback: this.scrollbackLines });
|
|
11409
|
+
return ansi.length > maxChars ? ansi.slice(-maxChars) : ansi;
|
|
11410
|
+
}
|
|
11411
|
+
text(maxLines) {
|
|
11412
|
+
const buffer = this.term.buffer.active;
|
|
11413
|
+
let last = buffer.length - 1;
|
|
11414
|
+
while (last >= 0 && (buffer.getLine(last)?.translateToString(true).trim() ?? "") === "")
|
|
11415
|
+
last--;
|
|
11416
|
+
const start = Math.max(0, last - maxLines + 1);
|
|
11417
|
+
const lines = [];
|
|
11418
|
+
for (let y = start;y <= last; y++) {
|
|
11419
|
+
lines.push(buffer.getLine(y)?.translateToString(true).trimEnd() ?? "");
|
|
11420
|
+
}
|
|
11421
|
+
return lines.join(`
|
|
11422
|
+
`);
|
|
11423
|
+
}
|
|
11424
|
+
}
|
|
11425
|
+
var import_headless, import_addon_serialize;
|
|
11426
|
+
var init_terminal_mirror = __esm(() => {
|
|
11427
|
+
import_headless = __toESM(require_xterm_headless(), 1);
|
|
11428
|
+
import_addon_serialize = __toESM(require_addon_serialize(), 1);
|
|
11429
|
+
});
|
|
11430
|
+
|
|
11232
11431
|
// packages/daemon/src/pty-session.ts
|
|
11233
|
-
import
|
|
11432
|
+
import fs21 from "fs";
|
|
11234
11433
|
import path20 from "path";
|
|
11235
11434
|
|
|
11236
11435
|
class PtySession {
|
|
11237
11436
|
info;
|
|
11238
11437
|
pty;
|
|
11239
|
-
|
|
11240
|
-
serializeAddon;
|
|
11438
|
+
mirror;
|
|
11241
11439
|
dataListeners = new Set;
|
|
11242
11440
|
exitListeners = new Set;
|
|
11243
11441
|
saveTimer;
|
|
@@ -11254,26 +11452,23 @@ class PtySession {
|
|
|
11254
11452
|
cols: spawn5.cols,
|
|
11255
11453
|
rows: spawn5.rows,
|
|
11256
11454
|
env: spawn5.env,
|
|
11257
|
-
term: "xterm-256color"
|
|
11455
|
+
term: "xterm-256color",
|
|
11456
|
+
id: info.id
|
|
11258
11457
|
});
|
|
11259
11458
|
return new PtySession(info, spawn5, pty);
|
|
11260
11459
|
}
|
|
11460
|
+
static adopt(info, pty, cols, rows) {
|
|
11461
|
+
return new PtySession(info, { exe: "", argv: [], cwd: info.cwd, cols, rows, env: {} }, pty);
|
|
11462
|
+
}
|
|
11261
11463
|
constructor(info, spawn5, pty) {
|
|
11262
11464
|
this.info = info;
|
|
11263
|
-
this.
|
|
11264
|
-
cols: spawn5.cols,
|
|
11265
|
-
rows: spawn5.rows,
|
|
11266
|
-
scrollback: SCROLLBACK_LINES,
|
|
11267
|
-
allowProposedApi: true
|
|
11268
|
-
});
|
|
11269
|
-
this.serializeAddon = new import_addon_serialize.SerializeAddon;
|
|
11270
|
-
this.term.loadAddon(this.serializeAddon);
|
|
11465
|
+
this.mirror = new TerminalMirror(spawn5.cols, spawn5.rows, SCROLLBACK_LINES);
|
|
11271
11466
|
this.pty = pty;
|
|
11272
11467
|
info.ptyPid = this.pty.pid;
|
|
11273
11468
|
this.pty.onData((data) => {
|
|
11274
11469
|
this.seq += 1;
|
|
11275
11470
|
this.lastOutputAt = Date.now();
|
|
11276
|
-
this.
|
|
11471
|
+
this.mirror.write(data);
|
|
11277
11472
|
if (!this.pasteReady) {
|
|
11278
11473
|
const window2 = this.readyScanTail + data;
|
|
11279
11474
|
if (window2.includes("\x1B[?2004h"))
|
|
@@ -11318,27 +11513,16 @@ class PtySession {
|
|
|
11318
11513
|
this.info.rows = rows;
|
|
11319
11514
|
try {
|
|
11320
11515
|
this.pty.resize(cols, rows);
|
|
11321
|
-
this.
|
|
11516
|
+
this.mirror.resize(cols, rows);
|
|
11322
11517
|
} catch (err) {
|
|
11323
11518
|
log(`resize failed for ${this.info.shortId}: ${String(err)}`);
|
|
11324
11519
|
}
|
|
11325
11520
|
}
|
|
11326
11521
|
screenText(maxLines = 60) {
|
|
11327
|
-
|
|
11328
|
-
let last = buffer.length - 1;
|
|
11329
|
-
while (last >= 0 && (buffer.getLine(last)?.translateToString(true).trim() ?? "") === "")
|
|
11330
|
-
last--;
|
|
11331
|
-
const start = Math.max(0, last - maxLines + 1);
|
|
11332
|
-
const lines = [];
|
|
11333
|
-
for (let y = start;y <= last; y++) {
|
|
11334
|
-
lines.push(buffer.getLine(y)?.translateToString(true).trimEnd() ?? "");
|
|
11335
|
-
}
|
|
11336
|
-
return lines.join(`
|
|
11337
|
-
`);
|
|
11522
|
+
return this.mirror.text(maxLines);
|
|
11338
11523
|
}
|
|
11339
11524
|
serializeBuffer() {
|
|
11340
|
-
|
|
11341
|
-
return ansi.length > SCROLLBACK_MAX_CHARS ? ansi.slice(-SCROLLBACK_MAX_CHARS) : ansi;
|
|
11525
|
+
return this.mirror.serialize(SCROLLBACK_MAX_CHARS);
|
|
11342
11526
|
}
|
|
11343
11527
|
scheduleSave() {
|
|
11344
11528
|
if (this.exited || this.saveTimer)
|
|
@@ -11353,8 +11537,8 @@ class PtySession {
|
|
|
11353
11537
|
try {
|
|
11354
11538
|
const file = path20.join(SCROLLBACK_DIR, `${this.info.id}.bin`);
|
|
11355
11539
|
const tmp = `${file}.tmp`;
|
|
11356
|
-
|
|
11357
|
-
|
|
11540
|
+
fs21.writeFileSync(tmp, this.serializeBuffer(), "utf8");
|
|
11541
|
+
fs21.renameSync(tmp, file);
|
|
11358
11542
|
} catch (err) {
|
|
11359
11543
|
log(`scrollback save failed for ${this.info.shortId}: ${String(err)}`);
|
|
11360
11544
|
}
|
|
@@ -11365,18 +11549,18 @@ class PtySession {
|
|
|
11365
11549
|
this.pty.kill();
|
|
11366
11550
|
}
|
|
11367
11551
|
}
|
|
11368
|
-
var
|
|
11552
|
+
var SCROLLBACK_LINES = 5000, SAVE_DEBOUNCE_MS = 5000, SCROLLBACK_MAX_CHARS;
|
|
11369
11553
|
var init_pty_session = __esm(() => {
|
|
11370
11554
|
init_paths();
|
|
11371
11555
|
init_pty();
|
|
11372
|
-
|
|
11373
|
-
import_addon_serialize = __toESM(require_addon_serialize(), 1);
|
|
11556
|
+
init_terminal_mirror();
|
|
11374
11557
|
SCROLLBACK_MAX_CHARS = 2 * 1024 * 1024;
|
|
11375
11558
|
});
|
|
11376
11559
|
|
|
11377
11560
|
// packages/daemon/src/session-manager.ts
|
|
11378
11561
|
import { randomBytes as randomBytes2, randomUUID } from "crypto";
|
|
11379
|
-
import
|
|
11562
|
+
import { spawn as spawn5 } from "child_process";
|
|
11563
|
+
import fs22 from "fs";
|
|
11380
11564
|
import os7 from "os";
|
|
11381
11565
|
import path21 from "path";
|
|
11382
11566
|
function sessionScratchRoot(info) {
|
|
@@ -11420,6 +11604,14 @@ async function sendableNow(runtime, info, probe, now) {
|
|
|
11420
11604
|
occupied
|
|
11421
11605
|
}, now);
|
|
11422
11606
|
}
|
|
11607
|
+
function pidIsRunning(pid) {
|
|
11608
|
+
try {
|
|
11609
|
+
process.kill(pid, 0);
|
|
11610
|
+
return true;
|
|
11611
|
+
} catch (err) {
|
|
11612
|
+
return err.code === "EPERM";
|
|
11613
|
+
}
|
|
11614
|
+
}
|
|
11423
11615
|
|
|
11424
11616
|
class SessionManager {
|
|
11425
11617
|
store;
|
|
@@ -11436,14 +11628,58 @@ class SessionManager {
|
|
|
11436
11628
|
this.store = store;
|
|
11437
11629
|
this.hooks = hooks;
|
|
11438
11630
|
this.configuredDefaults = configuredDefaults;
|
|
11439
|
-
const stale = store.markStaleRunningAsExited();
|
|
11440
|
-
if (stale > 0)
|
|
11441
|
-
log(`boot: marked ${stale} stale session(s) as exited`);
|
|
11442
11631
|
this.sessionWatch = setInterval(() => this.tickSessions(), SESSION_TICK_MS);
|
|
11443
11632
|
this.sessionWatch.unref();
|
|
11444
11633
|
}
|
|
11634
|
+
async recover() {
|
|
11635
|
+
const running = this.store.listSessions().filter((info) => info.state !== "exited");
|
|
11636
|
+
const held = await this.hostSessions();
|
|
11637
|
+
const adoptedIds = [];
|
|
11638
|
+
for (const info of running) {
|
|
11639
|
+
const description = held.get(info.id);
|
|
11640
|
+
if (description === undefined)
|
|
11641
|
+
continue;
|
|
11642
|
+
try {
|
|
11643
|
+
const handle = await this.ptyHost.adopt(description);
|
|
11644
|
+
info.state = "running";
|
|
11645
|
+
delete info.exitCode;
|
|
11646
|
+
delete info.exitedAt;
|
|
11647
|
+
const session = PtySession.adopt(info, handle, description.cols, description.rows);
|
|
11648
|
+
this.wire(session, undefined);
|
|
11649
|
+
this.hostOwned.add(info.id);
|
|
11650
|
+
this.store.markResumed(info.id, info.agentCommand, info.ptyPid);
|
|
11651
|
+
if (agentIdFromCommand(info.agentCommand) === "codex")
|
|
11652
|
+
this.adoptCodexAppServer(info.id);
|
|
11653
|
+
if (agentIdFromCommand(info.agentCommand) === "gemini")
|
|
11654
|
+
this.skipGeminiTelemetryHistory(info.id);
|
|
11655
|
+
adoptedIds.push(info.id);
|
|
11656
|
+
log(`boot: adopted session ${info.shortId} (pty ${description.pid}) from the terminal host`);
|
|
11657
|
+
} catch (err) {
|
|
11658
|
+
log(`boot: could not adopt ${info.shortId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11659
|
+
}
|
|
11660
|
+
}
|
|
11661
|
+
const sweptExited = this.store.markStaleRunningAsExited(adoptedIds);
|
|
11662
|
+
if (sweptExited > 0)
|
|
11663
|
+
log(`boot: marked ${sweptExited} stale session(s) as exited`);
|
|
11664
|
+
return { adopted: adoptedIds.length, sweptExited };
|
|
11665
|
+
}
|
|
11666
|
+
async hostSessions() {
|
|
11667
|
+
if (process.env["AXTERM_PTY_HOST"] !== "1")
|
|
11668
|
+
return new Map;
|
|
11669
|
+
const existing = readPtyHostIn(RUNTIME_DIR);
|
|
11670
|
+
if (existing === undefined)
|
|
11671
|
+
return new Map;
|
|
11672
|
+
try {
|
|
11673
|
+
this.ptyHost ??= await connectPtyHost({ endpoint: existing.endpoint, token: existing.token });
|
|
11674
|
+
return new Map(this.ptyHost.sessions.map((s) => [s.id, s]));
|
|
11675
|
+
} catch (err) {
|
|
11676
|
+
log(`boot: the terminal host named in pty-host.json did not answer: ${err instanceof Error ? err.message : String(err)}`);
|
|
11677
|
+
return new Map;
|
|
11678
|
+
}
|
|
11679
|
+
}
|
|
11445
11680
|
sessionWatch;
|
|
11446
11681
|
ptyHost;
|
|
11682
|
+
hostOwned = new Set;
|
|
11447
11683
|
ptyHostUnavailable = false;
|
|
11448
11684
|
codexServers = new Map;
|
|
11449
11685
|
workspaceFor(_sessionId) {
|
|
@@ -11523,7 +11759,7 @@ class SessionManager {
|
|
|
11523
11759
|
}
|
|
11524
11760
|
async create(params) {
|
|
11525
11761
|
const requestedCwd = params.cwd ? path21.resolve(params.cwd) : os7.homedir();
|
|
11526
|
-
if (!
|
|
11762
|
+
if (!fs22.existsSync(requestedCwd)) {
|
|
11527
11763
|
throw new AxtermError("err.cwd.missing", { cwd: requestedCwd });
|
|
11528
11764
|
}
|
|
11529
11765
|
let worktree;
|
|
@@ -11567,11 +11803,27 @@ class SessionManager {
|
|
|
11567
11803
|
const missing = unresolvableAgent(params.commandOverride, agentCommand, lookupOnPath);
|
|
11568
11804
|
if (missing !== undefined)
|
|
11569
11805
|
throw new AxtermError("err.agent.notInstalled", { command: missing });
|
|
11806
|
+
const adopted = agent === "claude" ? resumedConversationId(agentCommand) : undefined;
|
|
11807
|
+
let sessionCwd = cwd;
|
|
11808
|
+
if (adopted !== undefined) {
|
|
11809
|
+
const holder = conversationHeldBy(this.allSessions(), adopted);
|
|
11810
|
+
if (holder !== undefined) {
|
|
11811
|
+
throw new AxtermError("err.claude.conversationBusy", { id: adopted, session: holder.shortId });
|
|
11812
|
+
}
|
|
11813
|
+
if (worktree === undefined) {
|
|
11814
|
+
const recorded = claudeConversationCwd(claudeConfigDir(), adopted);
|
|
11815
|
+
sessionCwd = resolveSessionCwd({
|
|
11816
|
+
requested: cwd,
|
|
11817
|
+
preferConversation: params.preferConversationCwd === true,
|
|
11818
|
+
...recorded !== undefined && fs22.existsSync(recorded) ? { conversationCwd: recorded } : {}
|
|
11819
|
+
});
|
|
11820
|
+
}
|
|
11821
|
+
}
|
|
11570
11822
|
const info = {
|
|
11571
11823
|
id,
|
|
11572
11824
|
shortId: id.slice(0, 6),
|
|
11573
|
-
name: params.name ?? worktree?.branch ?? path21.basename(
|
|
11574
|
-
cwd,
|
|
11825
|
+
name: params.name ?? worktree?.branch ?? path21.basename(sessionCwd),
|
|
11826
|
+
cwd: sessionCwd,
|
|
11575
11827
|
shell: shell.kind,
|
|
11576
11828
|
agentCommand,
|
|
11577
11829
|
state: "running",
|
|
@@ -11580,8 +11832,7 @@ class SessionManager {
|
|
|
11580
11832
|
createdAt: Date.now(),
|
|
11581
11833
|
...worktree !== undefined ? { worktreePath: worktree.path, branch: worktree.branch, repoRoot: worktree.repoRoot } : {}
|
|
11582
11834
|
};
|
|
11583
|
-
|
|
11584
|
-
await this.launch(info, shell, agentCommand, appServerUrl);
|
|
11835
|
+
await this.launch(info, shell, agentCommand);
|
|
11585
11836
|
this.store.insertSession(info);
|
|
11586
11837
|
log(`session ${info.shortId} created shell=${shell.kind} cmd=${agentCommand.join(" ") || "(shell only)"}`);
|
|
11587
11838
|
this.emit({ t: "session.created", info });
|
|
@@ -11595,7 +11846,7 @@ class SessionManager {
|
|
|
11595
11846
|
launchToken
|
|
11596
11847
|
});
|
|
11597
11848
|
const file = this.claudeSettingsPath(id);
|
|
11598
|
-
|
|
11849
|
+
fs22.writeFileSync(file, JSON.stringify(settings, null, 2), { mode: 384 });
|
|
11599
11850
|
}
|
|
11600
11851
|
writeGeminiSettings(sessionId) {
|
|
11601
11852
|
try {
|
|
@@ -11608,69 +11859,104 @@ class SessionManager {
|
|
|
11608
11859
|
});
|
|
11609
11860
|
const file = this.geminiSettingsPath();
|
|
11610
11861
|
const tmp = `${file}.tmp`;
|
|
11611
|
-
|
|
11612
|
-
|
|
11862
|
+
fs22.writeFileSync(tmp, JSON.stringify(settings, null, 2));
|
|
11863
|
+
fs22.renameSync(tmp, file);
|
|
11613
11864
|
} catch (err) {
|
|
11614
11865
|
log(`gemini settings write failed; session starts without hooks: ${String(err)}`);
|
|
11615
11866
|
}
|
|
11616
11867
|
}
|
|
11617
|
-
async startCodexAppServer(id, codexHome2, cwd) {
|
|
11868
|
+
async startCodexAppServer(id, codexHome2, cwd, identity) {
|
|
11618
11869
|
try {
|
|
11870
|
+
const inherited = this.adoptCodexAppServer(id);
|
|
11871
|
+
if (inherited !== undefined)
|
|
11872
|
+
return inherited;
|
|
11619
11873
|
const port = await freeLoopbackPort();
|
|
11620
11874
|
const url = `ws://127.0.0.1:${port}`;
|
|
11621
|
-
const
|
|
11875
|
+
const child = spawn5("codex", ["app-server", "--listen", url], {
|
|
11622
11876
|
cwd,
|
|
11623
|
-
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
11877
|
+
detached: true,
|
|
11878
|
+
windowsHide: true,
|
|
11879
|
+
stdio: "ignore",
|
|
11880
|
+
env: { ...sessionEnv(process.env, identity), CODEX_HOME: codexHome2 }
|
|
11627
11881
|
});
|
|
11882
|
+
child.unref();
|
|
11883
|
+
const pid = child.pid;
|
|
11884
|
+
if (pid === undefined)
|
|
11885
|
+
throw new Error("the app-server was not given a pid");
|
|
11628
11886
|
if (!await waitForPort(port)) {
|
|
11629
|
-
|
|
11887
|
+
this.killCodexAppServer(pid);
|
|
11630
11888
|
log(`codex app-server did not open ${port}; session ${id.slice(0, 6)} keeps its hooks`);
|
|
11631
11889
|
return;
|
|
11632
11890
|
}
|
|
11633
|
-
|
|
11634
|
-
|
|
11635
|
-
|
|
11636
|
-
stop: () => {
|
|
11637
|
-
client.close();
|
|
11638
|
-
try {
|
|
11639
|
-
killTree(proc.pid, "codex.exe");
|
|
11640
|
-
} catch {}
|
|
11641
|
-
try {
|
|
11642
|
-
proc.kill();
|
|
11643
|
-
} catch {}
|
|
11644
|
-
}
|
|
11645
|
-
});
|
|
11646
|
-
log(`codex app-server on ${url} for session ${id.slice(0, 6)} in ${cwd}`);
|
|
11891
|
+
this.watchCodexAppServer(id, url, pid);
|
|
11892
|
+
fs22.writeFileSync(this.codexAppServerFile(id), JSON.stringify({ pid, url }), { mode: 384 });
|
|
11893
|
+
log(`codex app-server on ${url} (pid ${pid}) for session ${id.slice(0, 6)} in ${cwd}`);
|
|
11647
11894
|
return url;
|
|
11648
11895
|
} catch (err) {
|
|
11649
11896
|
log(`codex app-server unavailable (${String(err)}); session ${id.slice(0, 6)} keeps its hooks`);
|
|
11650
11897
|
return;
|
|
11651
11898
|
}
|
|
11652
11899
|
}
|
|
11900
|
+
codexAppServerFile(id) {
|
|
11901
|
+
return path21.join(RUNTIME_DIR, `codex-app-${id}.json`);
|
|
11902
|
+
}
|
|
11903
|
+
adoptCodexAppServer(id) {
|
|
11904
|
+
const file = this.codexAppServerFile(id);
|
|
11905
|
+
let record;
|
|
11906
|
+
try {
|
|
11907
|
+
record = JSON.parse(fs22.readFileSync(file, "utf8"));
|
|
11908
|
+
} catch {
|
|
11909
|
+
return;
|
|
11910
|
+
}
|
|
11911
|
+
const pid = record.pid;
|
|
11912
|
+
const url = record.url;
|
|
11913
|
+
if (typeof pid !== "number" || typeof url !== "string")
|
|
11914
|
+
return;
|
|
11915
|
+
if (!pidIsRunning(pid)) {
|
|
11916
|
+
fs22.rmSync(file, { force: true });
|
|
11917
|
+
return;
|
|
11918
|
+
}
|
|
11919
|
+
this.watchCodexAppServer(id, url, pid);
|
|
11920
|
+
log(`codex app-server: reconnected to ${url} (pid ${pid}) for session ${id.slice(0, 6)}`);
|
|
11921
|
+
return url;
|
|
11922
|
+
}
|
|
11923
|
+
watchCodexAppServer(id, url, pid) {
|
|
11924
|
+
const client = new CodexAppServerClient(url, (payload) => this.ingestHookEvent(id, payload));
|
|
11925
|
+
client.connect();
|
|
11926
|
+
this.codexServers.set(id, {
|
|
11927
|
+
stop: () => {
|
|
11928
|
+
client.close();
|
|
11929
|
+
this.killCodexAppServer(pid);
|
|
11930
|
+
fs22.rmSync(this.codexAppServerFile(id), { force: true });
|
|
11931
|
+
}
|
|
11932
|
+
});
|
|
11933
|
+
}
|
|
11934
|
+
killCodexAppServer(pid) {
|
|
11935
|
+
try {
|
|
11936
|
+
killTree(pid, "codex.exe");
|
|
11937
|
+
} catch {}
|
|
11938
|
+
}
|
|
11653
11939
|
stopCodexAppServer(id) {
|
|
11654
11940
|
this.codexServers.get(id)?.stop();
|
|
11655
11941
|
this.codexServers.delete(id);
|
|
11656
11942
|
}
|
|
11657
11943
|
ensureCodexHome() {
|
|
11658
11944
|
const home = path21.join(STATE_DIR, "codex-home");
|
|
11659
|
-
|
|
11945
|
+
fs22.mkdirSync(home, { recursive: true, mode: 448 });
|
|
11660
11946
|
this.linkCodexAuth(home);
|
|
11661
11947
|
const bridge = hookLaunch({ execPath: process.execPath, mainScript: Bun.main, agent: "codex" });
|
|
11662
11948
|
const hooksFile = buildCodexHooks({ command: bridge.command, args: bridge.args });
|
|
11663
11949
|
const hooksPath = codexTrustKeyPath(home);
|
|
11664
11950
|
const tmp = `${hooksPath}.tmp`;
|
|
11665
|
-
|
|
11666
|
-
|
|
11951
|
+
fs22.writeFileSync(tmp, JSON.stringify(hooksFile, null, 2));
|
|
11952
|
+
fs22.renameSync(tmp, hooksPath);
|
|
11667
11953
|
const entries = codexHookTrust(hooksPath, hooksFile);
|
|
11668
11954
|
const configPath = path21.join(home, "config.toml");
|
|
11669
|
-
const existing =
|
|
11955
|
+
const existing = fs22.existsSync(configPath) ? fs22.readFileSync(configPath, "utf8") : "";
|
|
11670
11956
|
const missing = entries.filter((e) => !existing.includes(`"${e.key.replaceAll("\\", "\\\\")}"`));
|
|
11671
11957
|
if (missing.length > 0) {
|
|
11672
11958
|
const block = codexTrustStateToml(missing);
|
|
11673
|
-
|
|
11959
|
+
fs22.appendFileSync(configPath, existing !== "" && !existing.endsWith(`
|
|
11674
11960
|
`) ? `
|
|
11675
11961
|
${block}` : block);
|
|
11676
11962
|
}
|
|
@@ -11684,20 +11970,20 @@ ${block}` : block);
|
|
|
11684
11970
|
}
|
|
11685
11971
|
linkCodexAuth(home) {
|
|
11686
11972
|
const target = path21.join(home, "auth.json");
|
|
11687
|
-
if (
|
|
11973
|
+
if (fs22.existsSync(target))
|
|
11688
11974
|
return;
|
|
11689
11975
|
const operatorHome = process.env["CODEX_HOME"] ?? path21.join(os7.homedir(), ".codex");
|
|
11690
11976
|
const source = path21.join(operatorHome, "auth.json");
|
|
11691
|
-
if (!
|
|
11977
|
+
if (!fs22.existsSync(source))
|
|
11692
11978
|
throw new AxtermError("err.codex.noAuth", { authFile: source });
|
|
11979
|
+
let how;
|
|
11693
11980
|
try {
|
|
11694
|
-
|
|
11695
|
-
} catch {
|
|
11696
|
-
|
|
11697
|
-
|
|
11698
|
-
|
|
11699
|
-
|
|
11700
|
-
}
|
|
11981
|
+
how = referenceCredential(source, target);
|
|
11982
|
+
} catch (err) {
|
|
11983
|
+
throw new AxtermError("err.codex.authLink", { reason: err instanceof Error ? err.message : String(err) });
|
|
11984
|
+
}
|
|
11985
|
+
if (how === "copy") {
|
|
11986
|
+
log(`codex auth: neither a symlink nor a hard link to ${source} was possible (state dir on another volume?) \u2014 ` + `copied instead, so a token refresh will not follow; delete ${target} to re-reference it`);
|
|
11701
11987
|
}
|
|
11702
11988
|
}
|
|
11703
11989
|
doctorReport() {
|
|
@@ -11709,24 +11995,24 @@ ${block}` : block);
|
|
|
11709
11995
|
const configPath = path21.join(home, "config.toml");
|
|
11710
11996
|
const read = (file) => {
|
|
11711
11997
|
try {
|
|
11712
|
-
return
|
|
11998
|
+
return fs22.existsSync(file) ? fs22.readFileSync(file, "utf8") : undefined;
|
|
11713
11999
|
} catch {
|
|
11714
12000
|
return;
|
|
11715
12001
|
}
|
|
11716
12002
|
};
|
|
11717
12003
|
return [
|
|
11718
|
-
...evaluateBridgeDoctor({ command: bridge.command, exists:
|
|
12004
|
+
...evaluateBridgeDoctor({ command: bridge.command, exists: fs22.existsSync(bridge.command) }),
|
|
11719
12005
|
...evaluateOmpDoctor({
|
|
11720
12006
|
binary: lookupOnPath("omp"),
|
|
11721
12007
|
extensionPath: this.hooks.extensionPath,
|
|
11722
|
-
extensionExists:
|
|
12008
|
+
extensionExists: fs22.existsSync(this.hooks.extensionPath)
|
|
11723
12009
|
}),
|
|
11724
12010
|
...evaluateClaudeDoctor({ binary: lookupOnPath("claude") }),
|
|
11725
12011
|
...evaluateCodexDoctor({
|
|
11726
12012
|
binary: lookupOnPath("codex"),
|
|
11727
12013
|
authSourcePath: authSource,
|
|
11728
|
-
authSourceExists:
|
|
11729
|
-
homeExists:
|
|
12014
|
+
authSourceExists: fs22.existsSync(authSource),
|
|
12015
|
+
homeExists: fs22.existsSync(home),
|
|
11730
12016
|
expected: codexHookTrust(codexTrustKeyPath(home), buildCodexHooks({ command: bridge.command, args: bridge.args })),
|
|
11731
12017
|
configToml: read(configPath)
|
|
11732
12018
|
}),
|
|
@@ -11825,6 +12111,18 @@ ${block}` : block);
|
|
|
11825
12111
|
session.write(digit);
|
|
11826
12112
|
return true;
|
|
11827
12113
|
}
|
|
12114
|
+
answerScreenChoice(idOrPrefix, optionIndex) {
|
|
12115
|
+
const session = this.resolveLive(idOrPrefix);
|
|
12116
|
+
const dialog = detectOnboarding(agentIdFromCommand(session.info.agentCommand), session.screenText(30));
|
|
12117
|
+
const digit = dialog?.options[optionIndex]?.digit;
|
|
12118
|
+
if (digit === undefined) {
|
|
12119
|
+
session.write(buildMenuSelectKeys(optionIndex));
|
|
12120
|
+
return "menu";
|
|
12121
|
+
}
|
|
12122
|
+
this.pendingPrompt.delete(session.info.id);
|
|
12123
|
+
session.write(digit);
|
|
12124
|
+
return "onboarding";
|
|
12125
|
+
}
|
|
11828
12126
|
sinceLastCapture(id) {
|
|
11829
12127
|
const at = this.lastCaptureAt.get(id);
|
|
11830
12128
|
return at === undefined ? undefined : Date.now() - at;
|
|
@@ -11852,12 +12150,12 @@ ${block}` : block);
|
|
|
11852
12150
|
writeScreenFile(idOrPrefix, lines) {
|
|
11853
12151
|
const session = this.resolveLive(idOrPrefix);
|
|
11854
12152
|
const file = path21.join(path21.dirname(this.hooks.endpointFile), `screen-${session.info.id}.txt`);
|
|
11855
|
-
|
|
12153
|
+
fs22.writeFileSync(file, session.screenText(lines), { mode: 384 });
|
|
11856
12154
|
return file;
|
|
11857
12155
|
}
|
|
11858
12156
|
removeScreenFile(id) {
|
|
11859
12157
|
try {
|
|
11860
|
-
|
|
12158
|
+
fs22.rmSync(path21.join(path21.dirname(this.hooks.endpointFile), `screen-${id}.txt`), { force: true });
|
|
11861
12159
|
} catch {}
|
|
11862
12160
|
}
|
|
11863
12161
|
async stageInboundFile(idOrPrefix, suggestedName, bytes) {
|
|
@@ -11877,8 +12175,8 @@ ${block}` : block);
|
|
|
11877
12175
|
}
|
|
11878
12176
|
async stageInRuntimeDir(info, suggestedName, bytes) {
|
|
11879
12177
|
const file = path21.resolve(planMaterialisedPath(MATERIALISED_DIR, info.id, suggestedName));
|
|
11880
|
-
await
|
|
11881
|
-
await
|
|
12178
|
+
await fs22.promises.mkdir(path21.dirname(file), { recursive: true, mode: 448 });
|
|
12179
|
+
await fs22.promises.writeFile(file, bytes, { mode: 384 });
|
|
11882
12180
|
log(`session ${info.shortId} staged ${path21.basename(file)} (${bytes.byteLength} bytes, runtime dir)`);
|
|
11883
12181
|
return file;
|
|
11884
12182
|
}
|
|
@@ -11903,24 +12201,24 @@ ${block}` : block);
|
|
|
11903
12201
|
}
|
|
11904
12202
|
removeStagedFiles(id, info) {
|
|
11905
12203
|
try {
|
|
11906
|
-
|
|
12204
|
+
fs22.rmSync(path21.join(MATERIALISED_DIR, id), { recursive: true, force: true });
|
|
11907
12205
|
} catch {}
|
|
11908
12206
|
if (info === undefined)
|
|
11909
12207
|
return;
|
|
11910
12208
|
const scratch = path21.resolve(scratchDir(sessionScratchRoot(info)));
|
|
11911
12209
|
try {
|
|
11912
|
-
|
|
11913
|
-
|
|
11914
|
-
|
|
11915
|
-
|
|
12210
|
+
fs22.rmSync(path21.resolve(inboundStagingDir(sessionScratchRoot(info)), id), { recursive: true, force: true });
|
|
12211
|
+
fs22.rmdirSync(path21.join(scratch, "inbound"));
|
|
12212
|
+
fs22.rmSync(path21.join(scratch, SCRATCH_IGNORE_NAME), { force: true });
|
|
12213
|
+
fs22.rmdirSync(scratch);
|
|
11916
12214
|
} catch {}
|
|
11917
12215
|
}
|
|
11918
12216
|
removeClaudeSettings(id) {
|
|
11919
12217
|
try {
|
|
11920
|
-
|
|
11921
|
-
|
|
11922
|
-
|
|
11923
|
-
|
|
12218
|
+
fs22.rmSync(this.claudeSettingsPath(id), { force: true });
|
|
12219
|
+
fs22.rmSync(this.mcpConfigPath(id), { force: true });
|
|
12220
|
+
fs22.rmSync(this.mcpContextPath(id), { force: true });
|
|
12221
|
+
fs22.rmSync(this.geminiTelemetryPath(id), { force: true });
|
|
11924
12222
|
this.stopCodexAppServer(id);
|
|
11925
12223
|
} catch {}
|
|
11926
12224
|
}
|
|
@@ -11936,7 +12234,7 @@ ${block}` : block);
|
|
|
11936
12234
|
writeMcpContext(id, launchToken) {
|
|
11937
12235
|
const file = this.mcpContextPath(id);
|
|
11938
12236
|
try {
|
|
11939
|
-
|
|
12237
|
+
fs22.writeFileSync(file, JSON.stringify({ port: this.hooks.port, token: this.hooks.host.token, sessionId: id, launchToken }), { mode: 384 });
|
|
11940
12238
|
return file;
|
|
11941
12239
|
} catch (err) {
|
|
11942
12240
|
log(`mcp context write failed; session starts without axterm's tools: ${String(err)}`);
|
|
@@ -11949,7 +12247,7 @@ ${block}` : block);
|
|
|
11949
12247
|
writeMcpConfig(id, contextPath) {
|
|
11950
12248
|
const file = this.mcpConfigPath(id);
|
|
11951
12249
|
try {
|
|
11952
|
-
|
|
12250
|
+
fs22.writeFileSync(file, claudeMcpConfig(this.mcpServerLaunch(), contextPath), { mode: 384 });
|
|
11953
12251
|
return file;
|
|
11954
12252
|
} catch (err) {
|
|
11955
12253
|
log(`mcp config write failed; session starts without axterm's tools: ${String(err)}`);
|
|
@@ -11964,7 +12262,7 @@ ${block}` : block);
|
|
|
11964
12262
|
}
|
|
11965
12263
|
async ptySpawner() {
|
|
11966
12264
|
if (process.env["AXTERM_PTY_HOST"] !== "1" || this.ptyHostUnavailable)
|
|
11967
|
-
return spawnPty;
|
|
12265
|
+
return { spawn: spawnPty, hostOwned: false };
|
|
11968
12266
|
try {
|
|
11969
12267
|
if (this.ptyHost === undefined) {
|
|
11970
12268
|
const override = process.env["AXTERM_PTY_HOST_MAIN"];
|
|
@@ -11973,11 +12271,11 @@ ${block}` : block);
|
|
|
11973
12271
|
log(`pty host: connected (pid ${info.pid})`);
|
|
11974
12272
|
}
|
|
11975
12273
|
const host = this.ptyHost;
|
|
11976
|
-
return (options) => host.spawn(options);
|
|
12274
|
+
return { spawn: (options) => host.spawn(options), hostOwned: true };
|
|
11977
12275
|
} catch (err) {
|
|
11978
12276
|
this.ptyHostUnavailable = true;
|
|
11979
12277
|
log(`pty host unavailable, owning ptys in this process: ${err instanceof Error ? err.message : String(err)}`);
|
|
11980
|
-
return spawnPty;
|
|
12278
|
+
return { spawn: spawnPty, hostOwned: false };
|
|
11981
12279
|
}
|
|
11982
12280
|
}
|
|
11983
12281
|
requireClaudeConversation(agentCommand) {
|
|
@@ -11988,7 +12286,7 @@ ${block}` : block);
|
|
|
11988
12286
|
throw new AxtermError("err.claude.noConversation", { id: conversation });
|
|
11989
12287
|
}
|
|
11990
12288
|
}
|
|
11991
|
-
async launch(info, shell, agentCommand
|
|
12289
|
+
async launch(info, shell, agentCommand) {
|
|
11992
12290
|
const id = info.id;
|
|
11993
12291
|
const launchToken = randomBytes2(16).toString("hex");
|
|
11994
12292
|
const agentId = agentIdFromCommand(info.agentCommand);
|
|
@@ -11996,11 +12294,22 @@ ${block}` : block);
|
|
|
11996
12294
|
this.writeClaudeSettings(id, launchToken);
|
|
11997
12295
|
if (agentId !== "omp")
|
|
11998
12296
|
this.writeMcpContext(id, launchToken);
|
|
12297
|
+
if (agentId === "claude")
|
|
12298
|
+
this.writeMcpConfig(id, this.mcpContextPath(id));
|
|
11999
12299
|
if (agentId === "gemini")
|
|
12000
12300
|
this.writeGeminiSettings(id);
|
|
12001
12301
|
const codexHome2 = agentId === "codex" ? this.ensureCodexHome() : undefined;
|
|
12002
12302
|
if (agentId === "claude")
|
|
12003
12303
|
this.requireClaudeConversation(agentCommand);
|
|
12304
|
+
const identity = {
|
|
12305
|
+
sessionId: id,
|
|
12306
|
+
launchToken,
|
|
12307
|
+
hookPort: this.hooks.port,
|
|
12308
|
+
hookToken: this.hooks.host.token,
|
|
12309
|
+
endpointFile: this.hooks.endpointFile
|
|
12310
|
+
};
|
|
12311
|
+
const appServerUrl = codexHome2 === undefined ? undefined : await this.startCodexAppServer(id, codexHome2, info.cwd, identity);
|
|
12312
|
+
const spawner = await this.ptySpawner();
|
|
12004
12313
|
const session = await PtySession.open(info, {
|
|
12005
12314
|
exe: shell.exe,
|
|
12006
12315
|
argv: buildShellArgv(shell, withAppServer(agentCommand, appServerUrl)),
|
|
@@ -12008,12 +12317,7 @@ ${block}` : block);
|
|
|
12008
12317
|
cols: info.cols,
|
|
12009
12318
|
rows: info.rows,
|
|
12010
12319
|
env: {
|
|
12011
|
-
...
|
|
12012
|
-
AXTERM_SESSION_ID: id,
|
|
12013
|
-
AXTERM_LAUNCH_TOKEN: launchToken,
|
|
12014
|
-
AXTERM_HOOK_PORT: String(this.hooks.port),
|
|
12015
|
-
AXTERM_HOOK_TOKEN: this.hooks.host.token,
|
|
12016
|
-
AXTERM_HOOK_ENDPOINT: this.hooks.endpointFile,
|
|
12320
|
+
...sessionEnv(process.env, identity),
|
|
12017
12321
|
...agentId === "gemini" ? {
|
|
12018
12322
|
GEMINI_CLI_SYSTEM_DEFAULTS_PATH: this.geminiSettingsPath(),
|
|
12019
12323
|
GEMINI_TELEMETRY_ENABLED: "true",
|
|
@@ -12024,7 +12328,15 @@ ${block}` : block);
|
|
|
12024
12328
|
} : {},
|
|
12025
12329
|
...codexHome2 !== undefined ? { CODEX_HOME: codexHome2 } : {}
|
|
12026
12330
|
}
|
|
12027
|
-
},
|
|
12331
|
+
}, spawner.spawn);
|
|
12332
|
+
if (spawner.hostOwned)
|
|
12333
|
+
this.hostOwned.add(id);
|
|
12334
|
+
this.wire(session, launchToken);
|
|
12335
|
+
return session;
|
|
12336
|
+
}
|
|
12337
|
+
wire(session, launchToken) {
|
|
12338
|
+
const id = session.info.id;
|
|
12339
|
+
const info = session.info;
|
|
12028
12340
|
this.agentRuntime.set(id, { agentExited: false, choiceSeq: 0, dialogOpen: false });
|
|
12029
12341
|
this.hooks.host.registerSession(id, launchToken, (payload) => this.ingestHookEvent(id, payload));
|
|
12030
12342
|
session.onData(() => {
|
|
@@ -12051,11 +12363,11 @@ ${block}` : block);
|
|
|
12051
12363
|
this.pendingPrompt.delete(id);
|
|
12052
12364
|
this.live.delete(id);
|
|
12053
12365
|
this.agentRuntime.delete(id);
|
|
12366
|
+
this.hostOwned.delete(id);
|
|
12054
12367
|
log(`session ${info.shortId} exited code=${code}`);
|
|
12055
12368
|
this.emit({ t: "session.exit", sessionId: id, code });
|
|
12056
12369
|
});
|
|
12057
12370
|
this.live.set(id, session);
|
|
12058
|
-
return session;
|
|
12059
12371
|
}
|
|
12060
12372
|
emitActivity(id, payload) {
|
|
12061
12373
|
const dropped = payload.dropped;
|
|
@@ -12226,38 +12538,14 @@ ${block}` : block);
|
|
|
12226
12538
|
allSessions() {
|
|
12227
12539
|
return sessionsByRecency([...this.live.values()].map((s) => s.info), this.store.listSessions());
|
|
12228
12540
|
}
|
|
12229
|
-
async resumeNamed(stored, agent) {
|
|
12230
|
-
const mcpConfigPath = agent === "claude" ? this.writeMcpConfig(stored.id, this.mcpContextPath(stored.id)) : undefined;
|
|
12231
|
-
const agentCommand = buildAgentResumeCommand({
|
|
12232
|
-
agent,
|
|
12233
|
-
sessionId: stored.id,
|
|
12234
|
-
extensionPath: this.hooks.extensionPath,
|
|
12235
|
-
claudeSettingsPath: this.claudeSettingsPath(stored.id),
|
|
12236
|
-
...mcpConfigPath === undefined ? {} : { mcpConfigPath },
|
|
12237
|
-
storedArgv: stored.agentCommand
|
|
12238
|
-
});
|
|
12239
|
-
const info = { ...stored, agentCommand, state: "running" };
|
|
12240
|
-
delete info.exitCode;
|
|
12241
|
-
delete info.exitedAt;
|
|
12242
|
-
delete info.agentState;
|
|
12243
|
-
delete info.agentStateSource;
|
|
12244
|
-
await this.launch(info, resolveShell(info.shell), agentCommand);
|
|
12245
|
-
this.store.markResumed(info.id, agentCommand, info.ptyPid);
|
|
12246
|
-
log(`session ${info.shortId} resumed cmd=${agentCommand.join(" ")}`);
|
|
12247
|
-
this.emit({ t: "session.created", info });
|
|
12248
|
-
return info;
|
|
12249
|
-
}
|
|
12250
12541
|
async resume(idOrPrefix) {
|
|
12251
12542
|
const stored = this.store.findByPrefix(idOrPrefix);
|
|
12252
12543
|
if (!stored)
|
|
12253
12544
|
throw new AxtermError("err.session.unknown", { id: idOrPrefix });
|
|
12254
12545
|
if (this.live.has(stored.id))
|
|
12255
12546
|
throw new AxtermError("err.session.running", { id: stored.shortId });
|
|
12256
|
-
const storedAgent = agentIdFromCommand(stored.agentCommand);
|
|
12257
|
-
if (storedAgent === "claude" || storedAgent === "gemini")
|
|
12258
|
-
return this.resumeNamed(stored, storedAgent);
|
|
12259
12547
|
const profile = agentProfile(agentIdFromCommand(stored.agentCommand));
|
|
12260
|
-
const reference = resumeReference(profile, stored);
|
|
12548
|
+
const reference = resumeReference(profile, { ...stored, ownSessionId: stored.id });
|
|
12261
12549
|
if (reference === undefined) {
|
|
12262
12550
|
throw new AxtermError("err.session.noConversation", {
|
|
12263
12551
|
id: stored.shortId,
|
|
@@ -12268,7 +12556,7 @@ ${block}` : block);
|
|
|
12268
12556
|
if (agentCommand === undefined) {
|
|
12269
12557
|
throw new AxtermError("err.session.noResumeSupport", { agent: profile.displayName });
|
|
12270
12558
|
}
|
|
12271
|
-
if (!
|
|
12559
|
+
if (!fs22.existsSync(stored.cwd))
|
|
12272
12560
|
throw new AxtermError("err.session.cwdGone", { cwd: stored.cwd });
|
|
12273
12561
|
const info = { ...stored, agentCommand, state: "running" };
|
|
12274
12562
|
delete info.exitCode;
|
|
@@ -12276,8 +12564,7 @@ ${block}` : block);
|
|
|
12276
12564
|
delete info.agentState;
|
|
12277
12565
|
delete info.agentStateSource;
|
|
12278
12566
|
const shell = resolveShell(info.shell);
|
|
12279
|
-
|
|
12280
|
-
await this.launch(info, shell, agentCommand, resumedAppServer);
|
|
12567
|
+
await this.launch(info, shell, agentCommand);
|
|
12281
12568
|
this.store.markResumed(info.id, agentCommand, info.ptyPid);
|
|
12282
12569
|
log(`session ${info.shortId} resumed cmd=${agentCommand.join(" ")}`);
|
|
12283
12570
|
this.emit({ t: "session.created", info });
|
|
@@ -12343,7 +12630,7 @@ ${block}` : block);
|
|
|
12343
12630
|
removeScrollback(id) {
|
|
12344
12631
|
for (const file of [`${id}.bin`, `${id}.bin.tmp`]) {
|
|
12345
12632
|
try {
|
|
12346
|
-
|
|
12633
|
+
fs22.rmSync(path21.join(SCROLLBACK_DIR, file), { force: true });
|
|
12347
12634
|
} catch {}
|
|
12348
12635
|
}
|
|
12349
12636
|
}
|
|
@@ -12351,6 +12638,14 @@ ${block}` : block);
|
|
|
12351
12638
|
this.drainGeminiTelemetry();
|
|
12352
12639
|
this.watchForStalls();
|
|
12353
12640
|
}
|
|
12641
|
+
skipGeminiTelemetryHistory(id) {
|
|
12642
|
+
const runtime = this.agentRuntime.get(id);
|
|
12643
|
+
if (runtime === undefined)
|
|
12644
|
+
return;
|
|
12645
|
+
try {
|
|
12646
|
+
runtime.telemetryOffset = fs22.statSync(this.geminiTelemetryPath(id)).size;
|
|
12647
|
+
} catch {}
|
|
12648
|
+
}
|
|
12354
12649
|
drainGeminiTelemetry() {
|
|
12355
12650
|
for (const [id, session] of this.live) {
|
|
12356
12651
|
if (agentIdFromCommand(session.info.agentCommand) !== "gemini")
|
|
@@ -12361,7 +12656,7 @@ ${block}` : block);
|
|
|
12361
12656
|
const file = this.geminiTelemetryPath(id);
|
|
12362
12657
|
let size = 0;
|
|
12363
12658
|
try {
|
|
12364
|
-
size =
|
|
12659
|
+
size = fs22.statSync(file).size;
|
|
12365
12660
|
} catch {
|
|
12366
12661
|
continue;
|
|
12367
12662
|
}
|
|
@@ -12423,10 +12718,17 @@ ${block}` : block);
|
|
|
12423
12718
|
clearInterval(this.sessionWatch);
|
|
12424
12719
|
for (const id of [...this.codexServers.keys()])
|
|
12425
12720
|
this.stopCodexAppServer(id);
|
|
12426
|
-
|
|
12721
|
+
let handedOn = 0;
|
|
12722
|
+
for (const [id, session] of this.live) {
|
|
12427
12723
|
session.saveScrollback();
|
|
12724
|
+
if (this.hostOwned.has(id)) {
|
|
12725
|
+
handedOn += 1;
|
|
12726
|
+
continue;
|
|
12727
|
+
}
|
|
12428
12728
|
session.kill();
|
|
12429
12729
|
}
|
|
12730
|
+
if (handedOn > 0)
|
|
12731
|
+
log(`left ${handedOn} session(s) running in the terminal host for the next daemon`);
|
|
12430
12732
|
this.live.clear();
|
|
12431
12733
|
this.agentRuntime.clear();
|
|
12432
12734
|
}
|
|
@@ -12435,7 +12737,7 @@ var DEFAULT_COLS = 120, DEFAULT_ROWS = 30, MATERIALISED_DIR, ACTIVITY_ARG_CHARS
|
|
|
12435
12737
|
var init_session_manager = __esm(() => {
|
|
12436
12738
|
init_src();
|
|
12437
12739
|
init_codex_home();
|
|
12438
|
-
|
|
12740
|
+
init_session_env();
|
|
12439
12741
|
init_git();
|
|
12440
12742
|
init_claude_store();
|
|
12441
12743
|
init_injector();
|
|
@@ -12481,6 +12783,9 @@ class FetchTelegramApi {
|
|
|
12481
12783
|
throw new Error(`telegram file download failed: ${res.status}`);
|
|
12482
12784
|
return new Uint8Array(await res.arrayBuffer());
|
|
12483
12785
|
}
|
|
12786
|
+
async deleteMessage(chatId, messageId) {
|
|
12787
|
+
await this.call("deleteMessage", { chat_id: chatId, message_id: messageId });
|
|
12788
|
+
}
|
|
12484
12789
|
async call(method, params = {}) {
|
|
12485
12790
|
const attempt = async () => {
|
|
12486
12791
|
const res = await fetch(`https://api.telegram.org/bot${this.botToken}/${method}`, {
|
|
@@ -12599,11 +12904,11 @@ var init_acknowledge = __esm(() => {
|
|
|
12599
12904
|
});
|
|
12600
12905
|
|
|
12601
12906
|
// packages/channel-telegram/src/attach.ts
|
|
12602
|
-
import
|
|
12907
|
+
import fs23 from "fs";
|
|
12603
12908
|
import path22 from "path";
|
|
12604
12909
|
function buildAttachmentUpload(filePath, chatId, topicId, caption, limits = TELEGRAM_LIMITS) {
|
|
12605
12910
|
const name = path22.basename(filePath);
|
|
12606
|
-
const bytes =
|
|
12911
|
+
const bytes = fs23.statSync(filePath).size;
|
|
12607
12912
|
const plan = planAttachment({ name, bytes }, limits);
|
|
12608
12913
|
if (plan.kind === "rejected")
|
|
12609
12914
|
throw new Error(plan.reason);
|
|
@@ -12613,7 +12918,7 @@ function buildAttachmentUpload(filePath, chatId, topicId, caption, limits = TELE
|
|
|
12613
12918
|
form.append("message_thread_id", String(topicId));
|
|
12614
12919
|
if (caption !== undefined && caption.length > 0)
|
|
12615
12920
|
form.append("caption", caption);
|
|
12616
|
-
form.append(field, new Blob([
|
|
12921
|
+
form.append(field, new Blob([fs23.readFileSync(filePath)]), name);
|
|
12617
12922
|
return { method: plan.kind === "photo" ? "sendPhoto" : "sendDocument", field, form };
|
|
12618
12923
|
}
|
|
12619
12924
|
function isPhotoRejection(error) {
|
|
@@ -12627,7 +12932,7 @@ function asDocumentUpload(filePath, chatId, topicId, caption) {
|
|
|
12627
12932
|
form.append("message_thread_id", String(topicId));
|
|
12628
12933
|
if (caption !== undefined && caption.length > 0)
|
|
12629
12934
|
form.append("caption", caption);
|
|
12630
|
-
form.append("document", new Blob([
|
|
12935
|
+
form.append("document", new Blob([fs23.readFileSync(filePath)]), name);
|
|
12631
12936
|
return { method: "sendDocument", field: "document", form };
|
|
12632
12937
|
}
|
|
12633
12938
|
var TELEGRAM_LIMITS;
|
|
@@ -13523,6 +13828,26 @@ ${code(prompt.line)}`,
|
|
|
13523
13828
|
});
|
|
13524
13829
|
}
|
|
13525
13830
|
awaitingSecret = new Map;
|
|
13831
|
+
async captureSecret(sessionId, msg, text) {
|
|
13832
|
+
const seq = this.awaitingSecret.get(sessionId);
|
|
13833
|
+
if (seq === undefined)
|
|
13834
|
+
return false;
|
|
13835
|
+
this.awaitingSecret.delete(sessionId);
|
|
13836
|
+
const accepted = this.control.answerShellPrompt(sessionId, seq, text);
|
|
13837
|
+
let deleted = false;
|
|
13838
|
+
if (this.api.deleteMessage && msg.chat && msg.message_id !== undefined) {
|
|
13839
|
+
try {
|
|
13840
|
+
await this.api.deleteMessage(msg.chat.id, msg.message_id);
|
|
13841
|
+
deleted = true;
|
|
13842
|
+
} catch (err) {
|
|
13843
|
+
this.log(`secret reply message ${msg.message_id} not deleted: ${String(err)}`);
|
|
13844
|
+
}
|
|
13845
|
+
}
|
|
13846
|
+
await this.sendToSession(sessionId, tHtml(accepted ? "tg.prompt.secretSubmitted" : "tg.prompt.secretStale"));
|
|
13847
|
+
if (!deleted)
|
|
13848
|
+
await this.sendToSession(sessionId, tHtml("tg.prompt.secretUndeleted"));
|
|
13849
|
+
return true;
|
|
13850
|
+
}
|
|
13526
13851
|
async onChoiceRequest(sessionId, seq, questions, askId, onboarding2 = false) {
|
|
13527
13852
|
const binding = this.control.findBindingBySession(sessionId);
|
|
13528
13853
|
if (!binding || questions.length === 0)
|
|
@@ -13686,6 +14011,8 @@ ${code(prompt.line)}`,
|
|
|
13686
14011
|
}
|
|
13687
14012
|
if (!binding)
|
|
13688
14013
|
return;
|
|
14014
|
+
if (await this.captureSecret(binding.sessionId, msg, text))
|
|
14015
|
+
return;
|
|
13689
14016
|
if (await this.captureFreeText(binding.sessionId, text))
|
|
13690
14017
|
return;
|
|
13691
14018
|
const chatId = msg.chat.id;
|
|
@@ -13916,8 +14243,15 @@ ${code(prompt.line)}`,
|
|
|
13916
14243
|
const project = await resolveProject(args[0]);
|
|
13917
14244
|
if (!project)
|
|
13918
14245
|
return;
|
|
13919
|
-
const
|
|
13920
|
-
|
|
14246
|
+
const tail = args.slice(1);
|
|
14247
|
+
const flagAt = tail.findIndex((token) => token.startsWith("-"));
|
|
14248
|
+
const name = (flagAt < 0 ? tail : tail.slice(0, flagAt)).join(" ") || undefined;
|
|
14249
|
+
const agentArgs = flagAt < 0 ? [] : tail.slice(flagAt);
|
|
14250
|
+
await this.control.createSession({
|
|
14251
|
+
cwd: project.path,
|
|
14252
|
+
...name !== undefined ? { name } : {},
|
|
14253
|
+
...agentArgs.length > 0 ? { agentArgs } : {}
|
|
14254
|
+
});
|
|
13921
14255
|
return;
|
|
13922
14256
|
}
|
|
13923
14257
|
case "/wt": {
|
|
@@ -14205,13 +14539,15 @@ class TelegramService {
|
|
|
14205
14539
|
config;
|
|
14206
14540
|
store;
|
|
14207
14541
|
manager;
|
|
14542
|
+
apiFactory;
|
|
14208
14543
|
channel;
|
|
14209
14544
|
unsubscribe;
|
|
14210
14545
|
runningConfig;
|
|
14211
|
-
constructor(config, store, manager) {
|
|
14546
|
+
constructor(config, store, manager, apiFactory = (token) => new FetchTelegramApi(token)) {
|
|
14212
14547
|
this.config = config;
|
|
14213
14548
|
this.store = store;
|
|
14214
14549
|
this.manager = manager;
|
|
14550
|
+
this.apiFactory = apiFactory;
|
|
14215
14551
|
}
|
|
14216
14552
|
readSettings() {
|
|
14217
14553
|
const telegram = this.config.resolved().channels.telegram;
|
|
@@ -14260,7 +14596,7 @@ class TelegramService {
|
|
|
14260
14596
|
const token = botToken ?? this.readSettings().botToken;
|
|
14261
14597
|
if (!token)
|
|
14262
14598
|
throw new AxtermError("err.telegram.noToken");
|
|
14263
|
-
const api =
|
|
14599
|
+
const api = this.apiFactory(token);
|
|
14264
14600
|
const updates = await api.call("getUpdates", { timeout: 0 });
|
|
14265
14601
|
return discoverTelegramTargets(updates);
|
|
14266
14602
|
}
|
|
@@ -14275,7 +14611,7 @@ class TelegramService {
|
|
|
14275
14611
|
log("telegram: not configured, channel not started");
|
|
14276
14612
|
return false;
|
|
14277
14613
|
}
|
|
14278
|
-
const api =
|
|
14614
|
+
const api = this.apiFactory(s.botToken);
|
|
14279
14615
|
const config = {
|
|
14280
14616
|
groupId: s.groupId,
|
|
14281
14617
|
allowedUserIds: [s.allowUserId],
|
|
@@ -14423,7 +14759,7 @@ var init_telegram_service = __esm(() => {
|
|
|
14423
14759
|
// packages/daemon/src/index.ts
|
|
14424
14760
|
var exports_src = {};
|
|
14425
14761
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
14426
|
-
import
|
|
14762
|
+
import fs24 from "fs";
|
|
14427
14763
|
import net5 from "net";
|
|
14428
14764
|
function isPidAlive(pid) {
|
|
14429
14765
|
try {
|
|
@@ -14435,9 +14771,9 @@ function isPidAlive(pid) {
|
|
|
14435
14771
|
}
|
|
14436
14772
|
async function main() {
|
|
14437
14773
|
ensureDataDirs();
|
|
14438
|
-
if (
|
|
14774
|
+
if (fs24.existsSync(RUNTIME_FILE2)) {
|
|
14439
14775
|
try {
|
|
14440
|
-
const existing = JSON.parse(
|
|
14776
|
+
const existing = JSON.parse(fs24.readFileSync(RUNTIME_FILE2, "utf8"));
|
|
14441
14777
|
if (existing.pid !== process.pid && isPidAlive(existing.pid)) {
|
|
14442
14778
|
log(`daemon already running (pid ${existing.pid}); exiting`);
|
|
14443
14779
|
process.exit(0);
|
|
@@ -14460,7 +14796,7 @@ async function main() {
|
|
|
14460
14796
|
version: productVersion(),
|
|
14461
14797
|
startedAt: Date.now()
|
|
14462
14798
|
};
|
|
14463
|
-
|
|
14799
|
+
fs24.writeFileSync(RUNTIME_FILE2, JSON.stringify(runtime, null, 2), { mode: 384 });
|
|
14464
14800
|
restrictFile(RUNTIME_FILE2);
|
|
14465
14801
|
const config = new ConfigStore(defaultConfigStorePaths(CONFIG_DIR));
|
|
14466
14802
|
if (config.loadError !== undefined)
|
|
@@ -14512,12 +14848,15 @@ async function main() {
|
|
|
14512
14848
|
hookHost.stop();
|
|
14513
14849
|
store.close();
|
|
14514
14850
|
try {
|
|
14515
|
-
const current = JSON.parse(
|
|
14851
|
+
const current = JSON.parse(fs24.readFileSync(RUNTIME_FILE2, "utf8"));
|
|
14516
14852
|
if (current.pid === process.pid)
|
|
14517
|
-
|
|
14853
|
+
fs24.rmSync(RUNTIME_FILE2);
|
|
14518
14854
|
} catch {}
|
|
14519
14855
|
process.exit(0);
|
|
14520
14856
|
};
|
|
14857
|
+
const recovered = await manager.recover();
|
|
14858
|
+
if (recovered.adopted > 0)
|
|
14859
|
+
log(`boot: ${recovered.adopted} session(s) adopted from the terminal host`);
|
|
14521
14860
|
startRpcServer({
|
|
14522
14861
|
pipePath: PIPE_PATH,
|
|
14523
14862
|
token,
|
|
@@ -14603,7 +14942,7 @@ function startPtyHost(options) {
|
|
|
14603
14942
|
send(socket, {
|
|
14604
14943
|
t: "welcome",
|
|
14605
14944
|
v: PTY_PROTOCOL_VERSION,
|
|
14606
|
-
sessions: [...sessions.entries()].filter(([, s]) => !s.exited).map(([id, s]) => ({ id, pid: s.handle.pid }))
|
|
14945
|
+
sessions: [...sessions.entries()].filter(([, s]) => !s.exited).map(([id, s]) => ({ id, pid: s.handle.pid, cols: s.cols, rows: s.rows }))
|
|
14607
14946
|
});
|
|
14608
14947
|
continue;
|
|
14609
14948
|
}
|
|
@@ -14620,9 +14959,18 @@ function startPtyHost(options) {
|
|
|
14620
14959
|
env: message.env,
|
|
14621
14960
|
term: message.term
|
|
14622
14961
|
});
|
|
14623
|
-
const session = {
|
|
14962
|
+
const session = {
|
|
14963
|
+
handle,
|
|
14964
|
+
cols: message.cols,
|
|
14965
|
+
rows: message.rows,
|
|
14966
|
+
exited: false,
|
|
14967
|
+
mirror: new TerminalMirror(message.cols, message.rows, REPLAY_SCROLLBACK_LINES)
|
|
14968
|
+
};
|
|
14624
14969
|
sessions.set(message.id, session);
|
|
14625
|
-
handle.onData((text) =>
|
|
14970
|
+
handle.onData((text) => {
|
|
14971
|
+
session.mirror.write(text);
|
|
14972
|
+
broadcast(encodeFrame(FrameKind.Data, message.id, new TextEncoder().encode(text)));
|
|
14973
|
+
});
|
|
14626
14974
|
handle.onExit((code2) => {
|
|
14627
14975
|
session.exited = true;
|
|
14628
14976
|
for (const client of clients)
|
|
@@ -14637,6 +14985,21 @@ function startPtyHost(options) {
|
|
|
14637
14985
|
}
|
|
14638
14986
|
continue;
|
|
14639
14987
|
}
|
|
14988
|
+
if (message.t === "subscribe") {
|
|
14989
|
+
const session = sessions.get(message.id);
|
|
14990
|
+
if (session === undefined) {
|
|
14991
|
+
send(socket, { t: "error", id: message.id, message: "no such session" });
|
|
14992
|
+
continue;
|
|
14993
|
+
}
|
|
14994
|
+
if (message.replay) {
|
|
14995
|
+
const ansi = session.mirror.serialize(REPLAY_MAX_CHARS);
|
|
14996
|
+
if (ansi.length > 0 && !socket.destroyed) {
|
|
14997
|
+
socket.write(encodeFrame(FrameKind.Data, message.id, new TextEncoder().encode(ansi)));
|
|
14998
|
+
}
|
|
14999
|
+
}
|
|
15000
|
+
send(socket, { t: "subscribed", id: message.id });
|
|
15001
|
+
continue;
|
|
15002
|
+
}
|
|
14640
15003
|
if (message.t === "resize") {
|
|
14641
15004
|
const session = sessions.get(message.id);
|
|
14642
15005
|
if (session === undefined || session.exited)
|
|
@@ -14644,6 +15007,7 @@ function startPtyHost(options) {
|
|
|
14644
15007
|
session.cols = message.cols;
|
|
14645
15008
|
session.rows = message.rows;
|
|
14646
15009
|
session.handle.resize(message.cols, message.rows);
|
|
15010
|
+
session.mirror.resize(message.cols, message.rows);
|
|
14647
15011
|
continue;
|
|
14648
15012
|
}
|
|
14649
15013
|
if (message.t === "kill") {
|
|
@@ -14674,10 +15038,13 @@ function startPtyHost(options) {
|
|
|
14674
15038
|
});
|
|
14675
15039
|
});
|
|
14676
15040
|
}
|
|
15041
|
+
var REPLAY_MAX_CHARS, REPLAY_SCROLLBACK_LINES = 5000;
|
|
14677
15042
|
var init_pty_host = __esm(() => {
|
|
14678
15043
|
init_src();
|
|
14679
15044
|
init_paths();
|
|
14680
15045
|
init_pty();
|
|
15046
|
+
init_terminal_mirror();
|
|
15047
|
+
REPLAY_MAX_CHARS = 2 * 1024 * 1024;
|
|
14681
15048
|
});
|
|
14682
15049
|
|
|
14683
15050
|
// packages/daemon/src/pty-host-main.ts
|
|
@@ -14730,7 +15097,7 @@ var init_pty_host_main = __esm(() => {
|
|
|
14730
15097
|
|
|
14731
15098
|
// packages/cli/src/index.ts
|
|
14732
15099
|
init_src();
|
|
14733
|
-
import
|
|
15100
|
+
import fs25 from "fs";
|
|
14734
15101
|
import path23 from "path";
|
|
14735
15102
|
|
|
14736
15103
|
// packages/cli/src/attach.ts
|
|
@@ -15333,6 +15700,7 @@ async function ensureDaemon() {
|
|
|
15333
15700
|
|
|
15334
15701
|
// packages/cli/src/hook-bridge.ts
|
|
15335
15702
|
init_src();
|
|
15703
|
+
import fs3 from "fs";
|
|
15336
15704
|
var POST_TIMEOUT_MS = 1500;
|
|
15337
15705
|
function isCodexApproval(agent, body) {
|
|
15338
15706
|
if (agent !== "codex")
|
|
@@ -15357,23 +15725,43 @@ async function relayHookEvent(agent) {
|
|
|
15357
15725
|
if (body === "")
|
|
15358
15726
|
return;
|
|
15359
15727
|
const approval = isCodexApproval(agent, body);
|
|
15360
|
-
const
|
|
15361
|
-
|
|
15362
|
-
|
|
15363
|
-
|
|
15364
|
-
|
|
15365
|
-
}
|
|
15366
|
-
if (
|
|
15728
|
+
const timeout = approval ? CODEX_APPROVAL_POST_TIMEOUT_MS : POST_TIMEOUT_MS;
|
|
15729
|
+
const post = (to) => fetch(to.url, { method: "POST", headers: to.headers, body, signal: AbortSignal.timeout(timeout) });
|
|
15730
|
+
let res;
|
|
15731
|
+
try {
|
|
15732
|
+
res = await post(target);
|
|
15733
|
+
} catch {}
|
|
15734
|
+
if (res === undefined || res.status === 401 || res.status === 404) {
|
|
15735
|
+
if (approval && res === undefined)
|
|
15736
|
+
return;
|
|
15737
|
+
const fresh = currentEndpoint();
|
|
15738
|
+
const retarget = fresh === undefined ? undefined : hookPostTarget(process.env, agent, fresh);
|
|
15739
|
+
if (retarget !== undefined && retarget.url !== target.url)
|
|
15740
|
+
res = await post(retarget);
|
|
15741
|
+
else if (res === undefined)
|
|
15742
|
+
return;
|
|
15743
|
+
}
|
|
15744
|
+
if (approval && res !== undefined && res.ok) {
|
|
15367
15745
|
const decision = await res.text();
|
|
15368
15746
|
if (decision !== "")
|
|
15369
15747
|
process.stdout.write(decision);
|
|
15370
15748
|
}
|
|
15371
15749
|
} catch {}
|
|
15372
15750
|
}
|
|
15751
|
+
function currentEndpoint() {
|
|
15752
|
+
const file = process.env["AXTERM_HOOK_ENDPOINT"];
|
|
15753
|
+
if (file === undefined)
|
|
15754
|
+
return;
|
|
15755
|
+
try {
|
|
15756
|
+
return parseHookEndpoint(fs3.readFileSync(file, "utf8"));
|
|
15757
|
+
} catch {
|
|
15758
|
+
return;
|
|
15759
|
+
}
|
|
15760
|
+
}
|
|
15373
15761
|
|
|
15374
15762
|
// packages/cli/src/mcp-server.ts
|
|
15375
15763
|
init_src();
|
|
15376
|
-
import
|
|
15764
|
+
import fs4 from "fs";
|
|
15377
15765
|
import path4 from "path";
|
|
15378
15766
|
var ASK_TIMEOUT_MS = 10 * 60000;
|
|
15379
15767
|
var POST_TIMEOUT_MS2 = 5000;
|
|
@@ -15490,7 +15878,7 @@ class McpServer {
|
|
|
15490
15878
|
const contextFile = this.io.env[MCP_CONTEXT_ENV];
|
|
15491
15879
|
if (contextFile !== undefined) {
|
|
15492
15880
|
try {
|
|
15493
|
-
const context = JSON.parse(
|
|
15881
|
+
const context = JSON.parse(fs4.readFileSync(contextFile, "utf8"));
|
|
15494
15882
|
if (context.port && context.token && context.sessionId) {
|
|
15495
15883
|
return {
|
|
15496
15884
|
url: `http://127.0.0.1:${context.port}`,
|
|
@@ -15647,13 +16035,13 @@ function runMcpServer() {
|
|
|
15647
16035
|
init_src();
|
|
15648
16036
|
import { spawnSync } from "child_process";
|
|
15649
16037
|
import { createHash as createHash2 } from "crypto";
|
|
15650
|
-
import
|
|
16038
|
+
import fs5 from "fs";
|
|
15651
16039
|
import path5 from "path";
|
|
15652
16040
|
import { stdin as stdin2, stdout as stdout2 } from "process";
|
|
15653
16041
|
// packages/cli/package.json
|
|
15654
16042
|
var package_default = {
|
|
15655
16043
|
name: "@layorixinc/axterm",
|
|
15656
|
-
version: "0.
|
|
16044
|
+
version: "0.6.0",
|
|
15657
16045
|
type: "module",
|
|
15658
16046
|
bin: {
|
|
15659
16047
|
axterm: "dist/cli.js"
|
|
@@ -15727,7 +16115,7 @@ function assetName(platform, arch, isMusl) {
|
|
|
15727
16115
|
function usesMusl() {
|
|
15728
16116
|
if (process.platform !== "linux")
|
|
15729
16117
|
return false;
|
|
15730
|
-
if (
|
|
16118
|
+
if (fs5.existsSync("/etc/alpine-release"))
|
|
15731
16119
|
return true;
|
|
15732
16120
|
const ldd = spawnSync("ldd", ["--version"], { encoding: "utf8" });
|
|
15733
16121
|
return `${ldd.stdout ?? ""}${ldd.stderr ?? ""}`.toLowerCase().includes("musl");
|
|
@@ -15741,7 +16129,7 @@ function firstLine(command, args) {
|
|
|
15741
16129
|
function detectOrigin(execPath) {
|
|
15742
16130
|
const resolved = (() => {
|
|
15743
16131
|
try {
|
|
15744
|
-
return
|
|
16132
|
+
return fs5.realpathSync(execPath);
|
|
15745
16133
|
} catch {
|
|
15746
16134
|
return execPath;
|
|
15747
16135
|
}
|
|
@@ -15783,7 +16171,7 @@ function checksumFor(sums, asset) {
|
|
|
15783
16171
|
return;
|
|
15784
16172
|
}
|
|
15785
16173
|
function sha256(file) {
|
|
15786
|
-
return createHash2("sha256").update(
|
|
16174
|
+
return createHash2("sha256").update(fs5.readFileSync(file)).digest("hex");
|
|
15787
16175
|
}
|
|
15788
16176
|
function ask2(question) {
|
|
15789
16177
|
if (stdin2.isTTY !== true)
|
|
@@ -15830,7 +16218,7 @@ async function runUpdate(client, current, options) {
|
|
|
15830
16218
|
}
|
|
15831
16219
|
const asset = assetName(process.platform, process.arch, usesMusl());
|
|
15832
16220
|
const base = `${RELEASES}/v${latest.version}`;
|
|
15833
|
-
const staging =
|
|
16221
|
+
const staging = fs5.mkdtempSync(`${process.execPath}.update-`);
|
|
15834
16222
|
const downloaded = path5.join(staging, asset);
|
|
15835
16223
|
try {
|
|
15836
16224
|
console.log(t("cli.update.downloading", { asset }));
|
|
@@ -15844,7 +16232,7 @@ async function runUpdate(client, current, options) {
|
|
|
15844
16232
|
return;
|
|
15845
16233
|
}
|
|
15846
16234
|
if (process.platform !== "win32")
|
|
15847
|
-
|
|
16235
|
+
fs5.chmodSync(downloaded, 493);
|
|
15848
16236
|
const reported = spawnSync(downloaded, ["--version"], { encoding: "utf8" }).stdout?.trim();
|
|
15849
16237
|
if (reported !== latest.version) {
|
|
15850
16238
|
console.error(t("cli.update.wrongVersion", { got: reported ?? "nothing", want: latest.version }));
|
|
@@ -15884,7 +16272,7 @@ async function runUpdate(client, current, options) {
|
|
|
15884
16272
|
console.log(t("cli.update.restoreFailed", { id, reason: (result.stderr ?? "").trim() || "unknown" }));
|
|
15885
16273
|
}
|
|
15886
16274
|
} finally {
|
|
15887
|
-
|
|
16275
|
+
fs5.rmSync(staging, { recursive: true, force: true });
|
|
15888
16276
|
}
|
|
15889
16277
|
}
|
|
15890
16278
|
function applySwap(target, downloaded) {
|
|
@@ -15893,16 +16281,16 @@ function applySwap(target, downloaded) {
|
|
|
15893
16281
|
try {
|
|
15894
16282
|
for (const step of steps) {
|
|
15895
16283
|
if (step.op === "chmod")
|
|
15896
|
-
|
|
16284
|
+
fs5.chmodSync(step.from, 493);
|
|
15897
16285
|
else if (step.op === "backup") {
|
|
15898
|
-
|
|
16286
|
+
fs5.renameSync(step.from, step.to);
|
|
15899
16287
|
backup = step.to;
|
|
15900
16288
|
} else
|
|
15901
|
-
|
|
16289
|
+
fs5.renameSync(step.from, step.to);
|
|
15902
16290
|
}
|
|
15903
16291
|
} catch (err) {
|
|
15904
|
-
if (backup !== undefined && !
|
|
15905
|
-
|
|
16292
|
+
if (backup !== undefined && !fs5.existsSync(target))
|
|
16293
|
+
fs5.renameSync(backup, target);
|
|
15906
16294
|
throw err;
|
|
15907
16295
|
}
|
|
15908
16296
|
}
|
|
@@ -15975,7 +16363,7 @@ function printSessionTable(sessions) {
|
|
|
15975
16363
|
console.log(row.map((cell, col) => cell.padEnd(widths[col])).join(" "));
|
|
15976
16364
|
}
|
|
15977
16365
|
}
|
|
15978
|
-
var realPath = (target) =>
|
|
16366
|
+
var realPath = (target) => fs25.realpathSync.native(target);
|
|
15979
16367
|
function scopedHere(sessions) {
|
|
15980
16368
|
return sessionsInScope(sessions, process.cwd(), realPath);
|
|
15981
16369
|
}
|
|
@@ -15988,7 +16376,7 @@ var help = () => t("cli.help");
|
|
|
15988
16376
|
function readDotEnv(file = ".env") {
|
|
15989
16377
|
const out = {};
|
|
15990
16378
|
try {
|
|
15991
|
-
for (const line of
|
|
16379
|
+
for (const line of fs25.readFileSync(file, "utf8").split(/\r?\n/)) {
|
|
15992
16380
|
const m = line.match(/^([A-Z0-9_]+)=(.*)$/);
|
|
15993
16381
|
if (m)
|
|
15994
16382
|
out[m[1]] = m[2].trim();
|
|
@@ -15999,7 +16387,7 @@ function readDotEnv(file = ".env") {
|
|
|
15999
16387
|
function configuredLocale() {
|
|
16000
16388
|
try {
|
|
16001
16389
|
const file = resolvePaths(currentPathEnv()).configFile;
|
|
16002
|
-
const raw = JSON.parse(
|
|
16390
|
+
const raw = JSON.parse(fs25.readFileSync(file, "utf8"));
|
|
16003
16391
|
return typeof raw.locale === "string" ? raw.locale : undefined;
|
|
16004
16392
|
} catch {
|
|
16005
16393
|
return;
|
|
@@ -16055,6 +16443,7 @@ async function main2() {
|
|
|
16055
16443
|
const commandOverride = typeof flags.get("cmd") === "string" ? flags.get("cmd").split(/\s+/).filter(Boolean) : undefined;
|
|
16056
16444
|
const info = await client.call("session.create", {
|
|
16057
16445
|
cwd: flags.get("cwd") ?? process.cwd(),
|
|
16446
|
+
preferConversationCwd: flags.get("cwd") === undefined,
|
|
16058
16447
|
name: flags.get("name"),
|
|
16059
16448
|
shell: flags.get("shell"),
|
|
16060
16449
|
agent: flags.get("agent"),
|
|
@@ -16148,9 +16537,17 @@ async function main2() {
|
|
|
16148
16537
|
const [id, indexArg] = positional;
|
|
16149
16538
|
if (!id || indexArg === undefined)
|
|
16150
16539
|
throw new Error(t("cli.usage.choose"));
|
|
16151
|
-
const
|
|
16152
|
-
await client.call("
|
|
16540
|
+
const index = Number(indexArg);
|
|
16541
|
+
const answered = await client.call("session.choose", { id, index }).catch(async (err) => {
|
|
16542
|
+
if (!/unknown method/i.test(String(err)))
|
|
16543
|
+
throw err;
|
|
16544
|
+
const keys2 = buildMenuSelectKeys(index);
|
|
16545
|
+
await client.call("terminal.write", { id, dataB64: Buffer.from(keys2, "utf8").toString("base64") });
|
|
16546
|
+
return { answered: "menu" };
|
|
16547
|
+
});
|
|
16153
16548
|
console.log(t("cli.session.selected", { index: indexArg }));
|
|
16549
|
+
if (answered.answered === "onboarding")
|
|
16550
|
+
console.log(t("cli.session.selectedDialog"));
|
|
16154
16551
|
break;
|
|
16155
16552
|
}
|
|
16156
16553
|
case "setup": {
|