@layorixinc/axterm 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1371 -404
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -46,6 +46,15 @@ var __export = (target, all) => {
|
|
|
46
46
|
};
|
|
47
47
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
48
48
|
|
|
49
|
+
// packages/core/src/menu-keys.ts
|
|
50
|
+
function buildMenuSelectKeys(index, normalizePresses = MENU_NORMALIZE_PRESSES) {
|
|
51
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
52
|
+
throw new RangeError(`menu index must be a non-negative integer, got ${index}`);
|
|
53
|
+
}
|
|
54
|
+
return MENU_UP.repeat(normalizePresses) + MENU_DOWN.repeat(index) + MENU_SELECT;
|
|
55
|
+
}
|
|
56
|
+
var MENU_UP = "\x1B[A", MENU_DOWN = "\x1B[B", MENU_SELECT = "\r", MENU_NORMALIZE_PRESSES = 24;
|
|
57
|
+
|
|
49
58
|
// packages/core/src/agent-registry.ts
|
|
50
59
|
function agentProfile(id) {
|
|
51
60
|
const known = PROFILES[id];
|
|
@@ -62,6 +71,13 @@ function agentProfile(id) {
|
|
|
62
71
|
hostHookSource: id
|
|
63
72
|
};
|
|
64
73
|
}
|
|
74
|
+
function menuChoiceKeys(profile, optionIndex) {
|
|
75
|
+
const keys = profile.approvalKeys;
|
|
76
|
+
if (keys !== undefined) {
|
|
77
|
+
return optionIndex === 0 ? keys.allow : optionIndex === 1 ? keys.deny : undefined;
|
|
78
|
+
}
|
|
79
|
+
return profile.clampingApprovalMenu === true ? buildMenuSelectKeys(optionIndex) : undefined;
|
|
80
|
+
}
|
|
65
81
|
function renderAgentTitle(profile, state, frame) {
|
|
66
82
|
if (state === undefined)
|
|
67
83
|
return;
|
|
@@ -87,8 +103,15 @@ var init_agent_registry = __esm(() => {
|
|
|
87
103
|
omp: {
|
|
88
104
|
id: "omp",
|
|
89
105
|
displayName: "OMP",
|
|
90
|
-
title: {
|
|
106
|
+
title: {
|
|
107
|
+
workingLabel: "OMP",
|
|
108
|
+
permissionLabel: "OMP - action required",
|
|
109
|
+
idleLabel: "OMP ready",
|
|
110
|
+
synthesizeWorking: false,
|
|
111
|
+
synthesizeTerminal: false
|
|
112
|
+
},
|
|
91
113
|
hostHookSource: "omp",
|
|
114
|
+
clampingApprovalMenu: true,
|
|
92
115
|
approvalBypassArgs: ["--approval-mode", "yolo"],
|
|
93
116
|
quota: "gauge",
|
|
94
117
|
resume: { argv: ["--resume", "{ref}"], ref: "sessionFile" }
|
|
@@ -158,6 +181,8 @@ var init_agent_registry = __esm(() => {
|
|
|
158
181
|
hostHookSource: "claude",
|
|
159
182
|
quota: "event",
|
|
160
183
|
approvalBypassArgs: ["--permission-mode", "bypassPermissions"],
|
|
184
|
+
approvalKeys: { allow: MENU_APPROVE, deny: "\x1B" },
|
|
185
|
+
structuredApprovals: true,
|
|
161
186
|
resume: { argv: ["--resume", "{ref}"], ref: "sessionId", selfNamed: "uuid" }
|
|
162
187
|
},
|
|
163
188
|
gemini: {
|
|
@@ -1011,6 +1036,7 @@ usage:
|
|
|
1011
1036
|
axterm rm <id\u2026> [--force] forget a session: its row, scrollback and topic binding
|
|
1012
1037
|
axterm rm --exited [--all] forget every exited session this listing would show
|
|
1013
1038
|
axterm daemon status|stop
|
|
1039
|
+
axterm service install|status|uninstall start at login and restart daemon failures
|
|
1014
1040
|
axterm project add <dir> [alias] | list | rm <alias> register a project for channel use
|
|
1015
1041
|
axterm run [--worktree BRANCH] [--base REF] ... worktree-backed session
|
|
1016
1042
|
axterm fanout --count 3 [--prefix try] "prompt"
|
|
@@ -1039,6 +1065,7 @@ locale: set AXTERM_LANG=en|ko, or \`axterm config set locale ko\`
|
|
|
1039
1065
|
"cli.session.sentKey": "sent {key}",
|
|
1040
1066
|
"cli.session.selected": "selected option {index}",
|
|
1041
1067
|
"cli.session.selectedDialog": "answered the agent's first-run dialog",
|
|
1068
|
+
"cli.choose.daemonTooOld": "this daemon is too old to answer a dialog safely \u2014 its `choose` sent keystrokes that land on the row below on claude and gemini, which is a wider grant. Restart the daemon, or send the keys yourself with `axterm key`",
|
|
1042
1069
|
"cli.resume.done": "resumed session {id} ({command})",
|
|
1043
1070
|
"cli.update.current": "axterm {version} is the latest version",
|
|
1044
1071
|
"cli.update.available": "axterm {latest} is available (you have {current})",
|
|
@@ -1082,6 +1109,7 @@ locale: set AXTERM_LANG=en|ko, or \`axterm config set locale ko\`
|
|
|
1082
1109
|
"cli.usage.worktree": "usage: axterm worktree list|rm <branch> [--force]",
|
|
1083
1110
|
"cli.usage.worktreeRm": "usage: axterm worktree rm <branch> [--force]",
|
|
1084
1111
|
"cli.usage.daemon": "usage: axterm daemon status|stop",
|
|
1112
|
+
"cli.usage.service": "usage: axterm service install|status|uninstall",
|
|
1085
1113
|
"cli.usage.telegram": "usage: axterm telegram link|doctor|status|discover",
|
|
1086
1114
|
"cli.usage.fanout": "usage: axterm fanout --count N <prompt>",
|
|
1087
1115
|
"cli.config.set": "set {path}",
|
|
@@ -1098,6 +1126,13 @@ locale: set AXTERM_LANG=en|ko, or \`axterm config set locale ko\`
|
|
|
1098
1126
|
"cli.worktree.refused": "refused: {reason}",
|
|
1099
1127
|
"cli.daemon.stopping": "daemon stopping",
|
|
1100
1128
|
"cli.daemon.notReady": "daemon did not become ready within 15s (see {log})",
|
|
1129
|
+
"cli.service.installed": "installed {kind} service; the daemon is running under the supervisor",
|
|
1130
|
+
"cli.service.uninstalled": "removed service registration; the current daemon keeps running",
|
|
1131
|
+
"cli.service.status": "service: {kind}; installed={installed}; active={active}",
|
|
1132
|
+
"cli.service.yes": "yes",
|
|
1133
|
+
"cli.service.no": "no",
|
|
1134
|
+
"cli.service.commandFailed": "service command failed ({command}): {error}",
|
|
1135
|
+
"cli.service.notReady": "the service started but the daemon was not ready within 15s",
|
|
1101
1136
|
"cli.telegram.missingSettings": "telegram link: no token/group/user found. Provide --token/--group/--user flags, TELEGRAM_* environment variables, or a .env file in the current directory.",
|
|
1102
1137
|
"cli.telegram.discoverHint": "Create a group with Topics enabled, add your bot to it, then run this again. Being added is enough \u2014 you do not have to send anything.",
|
|
1103
1138
|
"cli.telegram.discoverNone": "the bot cannot see any group yet.",
|
|
@@ -1128,6 +1163,8 @@ locale: set AXTERM_LANG=en|ko, or \`axterm config set locale ko\`
|
|
|
1128
1163
|
"tg.status.inferred": "\u2139\uFE0F Guessed from what is running under the shell \u2014 this agent reports nothing, so there are no approval buttons or replies here (axterm attach shows everything).",
|
|
1129
1164
|
"tg.session.emptyScreen": "(empty screen)",
|
|
1130
1165
|
"tg.prompt.confirm": "the session is waiting on a confirmation:",
|
|
1166
|
+
"tg.poll.conflict": "\u26A0\uFE0F {machine} cannot receive messages: another axterm is polling this bot token, and Telegram allows only one. Stop the other instance, or give this machine its own bot.",
|
|
1167
|
+
"tg.poll.failed": "\u26A0\uFE0F {machine} cannot receive messages right now: {reason}",
|
|
1131
1168
|
"tg.prompt.secretAsk": "the session is asking for a secret. Reply with it and the message will be deleted:",
|
|
1132
1169
|
"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
1170
|
"tg.prompt.secretSubmitted": "the secret reached the session",
|
|
@@ -1177,6 +1214,7 @@ locale: set AXTERM_LANG=en|ko, or \`axterm config set locale ko\`
|
|
|
1177
1214
|
"tg.answer.pickAtLeastOne": "pick at least one",
|
|
1178
1215
|
"tg.answer.confirmed": "confirmed",
|
|
1179
1216
|
"tg.answer.unknownOption": "no such option",
|
|
1217
|
+
"tg.answer.noSafeKey": "axterm cannot press this row safely \u2014 use /key or /type",
|
|
1180
1218
|
"tg.answer.picked": "{label} selected",
|
|
1181
1219
|
"tg.answer.toggledOn": "{label} selected",
|
|
1182
1220
|
"tg.answer.toggledOff": "{label} cleared",
|
|
@@ -1225,6 +1263,7 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1225
1263
|
"doctor.codex.trustStale": "codex hooks: {stale}/{total} trust hashes stale \u2014 codex reports them Modified and silently skips them; re-grant via /hooks in the codex TUI",
|
|
1226
1264
|
"doctor.codex.trustPending": "codex hooks: {pending}/{total} not yet granted \u2014 appended at the next codex launch",
|
|
1227
1265
|
"doctor.telegram.ok": "telegram: connected",
|
|
1266
|
+
"doctor.telegram.pollFailing": "telegram cannot receive: {reason}",
|
|
1228
1267
|
"doctor.telegram.off": "telegram: not running",
|
|
1229
1268
|
"doctor.telegram.problems": "telegram: {problems}",
|
|
1230
1269
|
"quota.gauge": "{agent}: {percent}% of {window} used, resets in {resets}",
|
|
@@ -1250,11 +1289,12 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1250
1289
|
"err.session.dialogOpen": "session {id} is waiting on a dialog ({state}); answer it with the buttons or /esc first",
|
|
1251
1290
|
"err.agent.notInstalled": "the agent command '{command}' was not found on PATH \u2014 install it, or pass --cmd to run something else",
|
|
1252
1291
|
"err.agent.unknown": "unknown agent '{agent}' \u2014 supported: {supported}",
|
|
1292
|
+
"err.choose.noSafeKey": "axterm cannot select row {index} of a {agent} dialog: its menu wraps, so an absolute row cannot be addressed from an unknown cursor position \u2014 use `axterm key` for the keys you want",
|
|
1253
1293
|
"err.staging.dir": "could not create the session's staging directory {dir} ({error}) \u2014 fix the permissions, or set defaults.staging to runtime",
|
|
1254
1294
|
"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
1295
|
"err.claude.conversationBusy": "session {session} is already in conversation {id} \u2014 two sessions writing one transcript would corrupt it; kill that one first",
|
|
1256
1296
|
"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",
|
|
1257
|
-
"
|
|
1297
|
+
"agent.approval.denied": "Denied from the axterm channel",
|
|
1258
1298
|
"err.codex.noAuth": "codex is not logged in ({authFile} not found) \u2014 run `codex login` once, then start the session again",
|
|
1259
1299
|
"err.codex.authLink": "could not link codex credentials into axterm's CODEX_HOME ({reason}) \u2014 refusing to start a logged-out agent",
|
|
1260
1300
|
"health.config.invalid": "the configuration file could not be understood",
|
|
@@ -1317,6 +1357,7 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1317
1357
|
axterm rm <id\u2026> [--force] \uC138\uC158\uC744 \uC78A\uB294\uB2E4: \uBAA9\uB85D\xB7\uC2A4\uD06C\uB864\uBC31\xB7\uD1A0\uD53D \uC5F0\uACB0\uAE4C\uC9C0
|
|
1318
1358
|
axterm rm --exited [--all] \uC774 \uBAA9\uB85D\uC5D0 \uBCF4\uC774\uB294 \uC885\uB8CC\uB41C \uC138\uC158\uC744 \uBAA8\uB450 \uC78A\uB294\uB2E4
|
|
1319
1359
|
axterm daemon status|stop
|
|
1360
|
+
axterm service install|status|uninstall \uB85C\uADF8\uC778 \uC2DC \uC2DC\uC791\uD558\uACE0 \uB370\uBAAC \uC2E4\uD328 \uC2DC \uC7AC\uC2DC\uC791
|
|
1320
1361
|
axterm project add <\uD3F4\uB354> [\uBCC4\uCE6D] | list | rm <\uBCC4\uCE6D> \uCC44\uB110\uC5D0\uC11C \uC4F8 \uD504\uB85C\uC81D\uD2B8 \uB4F1\uB85D
|
|
1321
1362
|
axterm run [--worktree \uBE0C\uB79C\uCE58] [--base REF] ... worktree \uAE30\uBC18 \uC138\uC158
|
|
1322
1363
|
axterm fanout --count 3 [--prefix try] "\uD504\uB86C\uD504\uD2B8"
|
|
@@ -1345,6 +1386,7 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1345
1386
|
"cli.session.sentKey": "{key} \uC804\uB2EC\uB428",
|
|
1346
1387
|
"cli.session.selected": "{index}\uBC88 \uD56D\uBAA9 \uC120\uD0DD\uB428",
|
|
1347
1388
|
"cli.session.selectedDialog": "\uC5D0\uC774\uC804\uD2B8\uC758 \uCCAB \uC2E4\uD589 \uB300\uD654\uC0C1\uC790\uC5D0 \uC751\uB2F5\uD588\uC2B5\uB2C8\uB2E4",
|
|
1389
|
+
"cli.choose.daemonTooOld": "\uC774 \uB370\uBAAC\uC740 \uB300\uD654\uC0C1\uC790\uB97C \uC548\uC804\uD558\uAC8C \uC751\uB2F5\uD560 \uC218 \uC5C6\uB294 \uAD6C\uBC84\uC804\uC785\uB2C8\uB2E4. \uC608\uC804 `choose` \uB294 claude \uC640 gemini \uC5D0\uC11C \uD55C \uD589 \uC544\uB798\uB97C \uC120\uD0DD\uD588\uACE0 \uADF8\uAC83\uC740 \uB354 \uB113\uC740 \uC2B9\uC778\uC785\uB2C8\uB2E4. \uB370\uBAAC\uC744 \uC7AC\uC2DC\uC791\uD558\uAC70\uB098 `axterm key` \uB85C \uC9C1\uC811 \uD0A4\uB97C \uBCF4\uB0B4\uC138\uC694",
|
|
1348
1390
|
"cli.resume.done": "\uC138\uC158 {id} \uC7AC\uAC1C\uB428 ({command})",
|
|
1349
1391
|
"cli.update.current": "axterm {version} \uC774 \uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4",
|
|
1350
1392
|
"cli.update.available": "axterm {latest} \uC744(\uB97C) \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4 (\uD604\uC7AC {current})",
|
|
@@ -1388,6 +1430,7 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1388
1430
|
"cli.usage.worktree": "\uC0AC\uC6A9\uBC95: axterm worktree list|rm <\uBE0C\uB79C\uCE58> [--force]",
|
|
1389
1431
|
"cli.usage.worktreeRm": "\uC0AC\uC6A9\uBC95: axterm worktree rm <\uBE0C\uB79C\uCE58> [--force]",
|
|
1390
1432
|
"cli.usage.daemon": "\uC0AC\uC6A9\uBC95: axterm daemon status|stop",
|
|
1433
|
+
"cli.usage.service": "\uC0AC\uC6A9\uBC95: axterm service install|status|uninstall",
|
|
1391
1434
|
"cli.usage.telegram": "\uC0AC\uC6A9\uBC95: axterm telegram link|doctor|status|discover",
|
|
1392
1435
|
"cli.usage.fanout": "\uC0AC\uC6A9\uBC95: axterm fanout --count N <\uD504\uB86C\uD504\uD2B8>",
|
|
1393
1436
|
"cli.config.set": "{path} \uC124\uC815\uB428",
|
|
@@ -1404,6 +1447,13 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1404
1447
|
"cli.worktree.refused": "\uAC70\uBD80\uB428: {reason}",
|
|
1405
1448
|
"cli.daemon.stopping": "\uB370\uBAAC \uC885\uB8CC \uC911",
|
|
1406
1449
|
"cli.daemon.notReady": "15\uCD08 \uC548\uC5D0 \uB370\uBAAC\uC774 \uC900\uBE44\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4 ({log} \uD655\uC778)",
|
|
1450
|
+
"cli.service.installed": "{kind} \uC11C\uBE44\uC2A4 \uC124\uCE58\uB428. \uB370\uBAAC\uC740 supervisor \uC544\uB798\uC5D0\uC11C \uC2E4\uD589 \uC911\uC785\uB2C8\uB2E4",
|
|
1451
|
+
"cli.service.uninstalled": "\uC11C\uBE44\uC2A4 \uB4F1\uB85D\uC744 \uC81C\uAC70\uD588\uC2B5\uB2C8\uB2E4. \uD604\uC7AC \uB370\uBAAC\uC740 \uACC4\uC18D \uC2E4\uD589\uB429\uB2C8\uB2E4",
|
|
1452
|
+
"cli.service.status": "\uC11C\uBE44\uC2A4: {kind}; \uC124\uCE58={installed}; \uD65C\uC131={active}",
|
|
1453
|
+
"cli.service.yes": "\uC608",
|
|
1454
|
+
"cli.service.no": "\uC544\uB2C8\uC694",
|
|
1455
|
+
"cli.service.commandFailed": "\uC11C\uBE44\uC2A4 \uBA85\uB839 \uC2E4\uD328 ({command}): {error}",
|
|
1456
|
+
"cli.service.notReady": "\uC11C\uBE44\uC2A4\uAC00 \uC2DC\uC791\uB410\uC9C0\uB9CC 15\uCD08 \uC548\uC5D0 \uB370\uBAAC\uC774 \uC900\uBE44\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4",
|
|
1407
1457
|
"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.",
|
|
1408
1458
|
"tg.help.general.header": "axterm \uBA85\uB839 (General \uD1A0\uD53D):",
|
|
1409
1459
|
"tg.help.general.projects": "/projects \u2014 \uB4F1\uB85D\uB41C \uD504\uB85C\uC81D\uD2B8",
|
|
@@ -1434,6 +1484,8 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1434
1484
|
"tg.status.inferred": "\u2139\uFE0F \uC178\uC5D0\uC11C \uC2E4\uD589 \uC911\uC778 \uD504\uB85C\uC138\uC2A4\uB85C \uCD94\uC815\uD55C \uC0C1\uD0DC\uC785\uB2C8\uB2E4 \u2014 \uC774 \uC5D0\uC774\uC804\uD2B8\uB294 \uC0C1\uD0DC\uB97C \uBCF4\uACE0\uD558\uC9C0 \uC54A\uC544 \uC2B9\uC778 \uBC84\uD2BC\uB3C4, \uB2F5\uBCC0\uB3C4 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC804\uCCB4\uB294 axterm attach\uB85C \uD655\uC778\uD558\uC138\uC694).",
|
|
1435
1485
|
"tg.session.emptyScreen": "(\uBE48 \uD654\uBA74)",
|
|
1436
1486
|
"tg.prompt.confirm": "\uC138\uC158\uC774 \uD655\uC778\uC744 \uAE30\uB2E4\uB9AC\uACE0 \uC788\uC2B5\uB2C8\uB2E4:",
|
|
1487
|
+
"tg.poll.conflict": "\u26A0\uFE0F {machine} \uC774 \uBA54\uC2DC\uC9C0\uB97C \uBC1B\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: \uB2E4\uB978 axterm \uC774 \uC774 \uBD07 \uD1A0\uD070\uC744 \uD3F4\uB9C1\uD558\uACE0 \uC788\uACE0 Telegram \uC740 \uD558\uB098\uB9CC \uD5C8\uC6A9\uD569\uB2C8\uB2E4. \uB2E4\uB978 \uC778\uC2A4\uD134\uC2A4\uB97C \uB044\uAC70\uB098 \uC774 \uBA38\uC2E0\uC5D0 \uC804\uC6A9 \uBD07\uC744 \uC8FC\uC138\uC694.",
|
|
1488
|
+
"tg.poll.failed": "\u26A0\uFE0F {machine} \uC774 \uC9C0\uAE08 \uBA54\uC2DC\uC9C0\uB97C \uBC1B\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: {reason}",
|
|
1437
1489
|
"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:",
|
|
1438
1490
|
"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
1491
|
"tg.prompt.secretSubmitted": "\uBE44\uBC00\uAC12\uC774 \uC138\uC158\uC5D0 \uC804\uB2EC\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
|
|
@@ -1483,6 +1535,7 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1483
1535
|
"tg.answer.pickAtLeastOne": "\uCD5C\uC18C 1\uAC1C\uB294 \uC120\uD0DD\uD558\uC138\uC694",
|
|
1484
1536
|
"tg.answer.confirmed": "\uD655\uC815\uB428",
|
|
1485
1537
|
"tg.answer.unknownOption": "\uC5C6\uB294 \uC120\uD0DD\uC9C0",
|
|
1538
|
+
"tg.answer.noSafeKey": "\uC774 \uD589\uC740 \uC548\uC804\uD558\uAC8C \uB204\uB97C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 /key \uB610\uB294 /type\uC744 \uC4F0\uC138\uC694",
|
|
1486
1539
|
"tg.answer.picked": "{label} \uC120\uD0DD\uB428",
|
|
1487
1540
|
"tg.answer.toggledOn": "{label} \uC120\uD0DD",
|
|
1488
1541
|
"tg.answer.toggledOff": "{label} \uD574\uC81C",
|
|
@@ -1531,6 +1584,7 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1531
1584
|
"doctor.codex.trustStale": "codex \uD6C5: {stale}/{total} \uC2E0\uB8B0 \uD574\uC2DC \uBD88\uC77C\uCE58 \u2014 codex\uAC00 Modified\uB85C \uBCF4\uACE0\uD558\uACE0 \uC870\uC6A9\uD788 \uAC74\uB108\uB701\uB2C8\uB2E4; codex TUI\uC758 /hooks\uC5D0\uC11C \uC7AC\uC2B9\uC778\uD558\uC138\uC694",
|
|
1532
1585
|
"doctor.codex.trustPending": "codex \uD6C5: {pending}/{total} \uBBF8\uC2B9\uC778 \u2014 \uB2E4\uC74C codex \uC2E4\uD589 \uC2DC \uAE30\uB85D\uB429\uB2C8\uB2E4",
|
|
1533
1586
|
"doctor.telegram.ok": "\uD154\uB808\uADF8\uB7A8: \uC5F0\uACB0\uB428",
|
|
1587
|
+
"doctor.telegram.pollFailing": "\uD154\uB808\uADF8\uB7A8 \uC218\uC2E0 \uBD88\uAC00: {reason}",
|
|
1534
1588
|
"doctor.telegram.off": "\uD154\uB808\uADF8\uB7A8: \uBBF8\uC2E4\uD589",
|
|
1535
1589
|
"doctor.telegram.problems": "\uD154\uB808\uADF8\uB7A8: {problems}",
|
|
1536
1590
|
"quota.gauge": "{agent}: {window} \uD55C\uB3C4\uC758 {percent}% \uC0AC\uC6A9, {resets} \uD6C4 \uCD08\uAE30\uD654",
|
|
@@ -1556,11 +1610,12 @@ usage: /fanout <project> <count|branches> <prompt>`,
|
|
|
1556
1610
|
"err.session.dialogOpen": "\uC138\uC158 {id} \uC774(\uAC00) \uB2E4\uC774\uC5BC\uB85C\uADF8\uB97C \uAE30\uB2E4\uB9AC\uB294 \uC911\uC785\uB2C8\uB2E4 ({state}). \uBC84\uD2BC\uC73C\uB85C \uB2F5\uD558\uAC70\uB098 /esc \uB97C \uBA3C\uC800 \uBCF4\uB0B4\uC138\uC694",
|
|
1557
1611
|
"err.agent.notInstalled": "\uC5D0\uC774\uC804\uD2B8 \uBA85\uB839 '{command}' \uC744(\uB97C) PATH \uC5D0\uC11C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC124\uCE58\uD558\uAC70\uB098 --cmd \uB85C \uB2E4\uB978 \uBA85\uB839\uC744 \uC9C0\uC815\uD558\uC138\uC694",
|
|
1558
1612
|
"err.agent.unknown": "\uC54C \uC218 \uC5C6\uB294 \uC5D0\uC774\uC804\uD2B8 '{agent}' \uC785\uB2C8\uB2E4. \uC9C0\uC6D0: {supported}",
|
|
1613
|
+
"err.choose.noSafeKey": "{agent} \uB300\uD654\uC0C1\uC790\uC758 {index}\uBC88 \uD589\uC740 \uC120\uD0DD\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA54\uB274\uAC00 \uC21C\uD658\uD558\uBBC0\uB85C \uCEE4\uC11C \uC704\uCE58\uB97C \uBAA8\uB974\uB294 \uC0C1\uD0DC\uC5D0\uC11C \uC808\uB300 \uD589\uC744 \uC9C0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC6D0\uD558\uB294 \uD0A4\uB294 `axterm key`\uB85C \uBCF4\uB0B4\uC138\uC694",
|
|
1559
1614
|
"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",
|
|
1560
1615
|
"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
1616
|
"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",
|
|
1562
1617
|
"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",
|
|
1563
|
-
"
|
|
1618
|
+
"agent.approval.denied": "axterm \uCC44\uB110\uC5D0\uC11C \uAC70\uBD80\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
|
|
1564
1619
|
"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",
|
|
1565
1620
|
"err.codex.authLink": "codex \uC790\uACA9 \uC99D\uBA85\uC744 axterm\uC758 CODEX_HOME\uC5D0 \uC5F0\uACB0\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 ({reason}). \uB85C\uADF8\uC544\uC6C3 \uC0C1\uD0DC\uC758 \uC5D0\uC774\uC804\uD2B8\uB294 \uC2DC\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4",
|
|
1566
1621
|
"health.config.invalid": "\uC124\uC815 \uD30C\uC77C\uC744 \uC774\uD574\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
|
|
@@ -1864,6 +1919,222 @@ var init_self_spawn = __esm(() => {
|
|
|
1864
1919
|
RUNTIME_NAMES = new Set(["bun", "bun.exe", "bun-debug", "bun-debug.exe", "node", "node.exe"]);
|
|
1865
1920
|
});
|
|
1866
1921
|
|
|
1922
|
+
// packages/core/src/service.ts
|
|
1923
|
+
import path2 from "path";
|
|
1924
|
+
function xml(value) {
|
|
1925
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
1926
|
+
}
|
|
1927
|
+
function systemdArgument(value) {
|
|
1928
|
+
return `"${value.replaceAll("\\", "\\\\").replaceAll('"', "\\\"").replaceAll("%", "%%")}"`;
|
|
1929
|
+
}
|
|
1930
|
+
function windowsArgument(value) {
|
|
1931
|
+
if (value !== "" && !/[\s"]/u.test(value))
|
|
1932
|
+
return value;
|
|
1933
|
+
let out = '"';
|
|
1934
|
+
let slashes = 0;
|
|
1935
|
+
for (const char of value) {
|
|
1936
|
+
if (char === "\\") {
|
|
1937
|
+
slashes++;
|
|
1938
|
+
continue;
|
|
1939
|
+
}
|
|
1940
|
+
if (char === '"') {
|
|
1941
|
+
out += "\\".repeat(slashes * 2 + 1) + '"';
|
|
1942
|
+
slashes = 0;
|
|
1943
|
+
continue;
|
|
1944
|
+
}
|
|
1945
|
+
out += "\\".repeat(slashes) + char;
|
|
1946
|
+
slashes = 0;
|
|
1947
|
+
}
|
|
1948
|
+
return out + "\\".repeat(slashes * 2) + '"';
|
|
1949
|
+
}
|
|
1950
|
+
function windowsCommandLine(args) {
|
|
1951
|
+
return args.map(windowsArgument).join(" ");
|
|
1952
|
+
}
|
|
1953
|
+
function environmentEntries(input) {
|
|
1954
|
+
return Object.entries(input.environment ?? {}).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => {
|
|
1955
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(key))
|
|
1956
|
+
throw new Error(`invalid service environment key: ${key}`);
|
|
1957
|
+
if (/[\0\r\n]/u.test(value))
|
|
1958
|
+
throw new Error(`invalid service environment value for ${key}`);
|
|
1959
|
+
return [key, value];
|
|
1960
|
+
});
|
|
1961
|
+
}
|
|
1962
|
+
function systemdEnvironment(input) {
|
|
1963
|
+
return environmentEntries(input).map(([key, value]) => `Environment="${`${key}=${value}`.replaceAll("\\", "\\\\").replaceAll('"', "\\\"").replaceAll("%", "%%")}"`).join(`
|
|
1964
|
+
`);
|
|
1965
|
+
}
|
|
1966
|
+
function launchdEnvironment(input) {
|
|
1967
|
+
const entries = environmentEntries(input);
|
|
1968
|
+
if (entries.length === 0)
|
|
1969
|
+
return "";
|
|
1970
|
+
return ` <key>EnvironmentVariables</key>
|
|
1971
|
+
<dict>
|
|
1972
|
+
${entries.map(([key, value]) => ` <key>${xml(key)}</key>
|
|
1973
|
+
<string>${xml(value)}</string>`).join(`
|
|
1974
|
+
`)}
|
|
1975
|
+
</dict>
|
|
1976
|
+
`;
|
|
1977
|
+
}
|
|
1978
|
+
function systemdDefinition(input) {
|
|
1979
|
+
const argv = [input.launch.command, ...input.launch.args].map(systemdArgument).join(" ");
|
|
1980
|
+
const environment = systemdEnvironment(input);
|
|
1981
|
+
return {
|
|
1982
|
+
kind: "systemd",
|
|
1983
|
+
id: SYSTEMD_SERVICE,
|
|
1984
|
+
file: path2.posix.join(input.homeDir, ".config/systemd/user", SYSTEMD_SERVICE),
|
|
1985
|
+
contents: `[Unit]
|
|
1986
|
+
Description=axterm daemon
|
|
1987
|
+
After=network-online.target
|
|
1988
|
+
Wants=network-online.target
|
|
1989
|
+
|
|
1990
|
+
[Service]
|
|
1991
|
+
Type=simple
|
|
1992
|
+
ExecStart=:${argv}
|
|
1993
|
+
${environment ? `${environment}
|
|
1994
|
+
` : ""}Restart=on-failure
|
|
1995
|
+
RestartSec=2
|
|
1996
|
+
TimeoutStopSec=20
|
|
1997
|
+
# The PTY host is detached but remains in this cgroup. Stop only the daemon.
|
|
1998
|
+
KillMode=process
|
|
1999
|
+
|
|
2000
|
+
[Install]
|
|
2001
|
+
WantedBy=default.target
|
|
2002
|
+
`
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
function launchdDefinition(input) {
|
|
2006
|
+
const argumentsXml = [input.launch.command, ...input.launch.args].map((arg) => ` <string>${xml(arg)}</string>`).join(`
|
|
2007
|
+
`);
|
|
2008
|
+
const logFile = path2.posix.join(input.stateDir, "logs/service.log");
|
|
2009
|
+
return {
|
|
2010
|
+
kind: "launchd",
|
|
2011
|
+
id: LAUNCHD_LABEL,
|
|
2012
|
+
file: path2.posix.join(input.homeDir, "Library/LaunchAgents", `${LAUNCHD_LABEL}.plist`),
|
|
2013
|
+
contents: `<?xml version="1.0" encoding="UTF-8"?>
|
|
2014
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
2015
|
+
<plist version="1.0">
|
|
2016
|
+
<dict>
|
|
2017
|
+
<key>Label</key>
|
|
2018
|
+
<string>${LAUNCHD_LABEL}</string>
|
|
2019
|
+
<key>ProgramArguments</key>
|
|
2020
|
+
<array>
|
|
2021
|
+
${argumentsXml}
|
|
2022
|
+
</array>
|
|
2023
|
+
<key>WorkingDirectory</key>
|
|
2024
|
+
<string>${xml(input.homeDir)}</string>
|
|
2025
|
+
${launchdEnvironment(input)} <key>RunAtLoad</key>
|
|
2026
|
+
<true/>
|
|
2027
|
+
<key>KeepAlive</key>
|
|
2028
|
+
<dict>
|
|
2029
|
+
<key>SuccessfulExit</key>
|
|
2030
|
+
<false/>
|
|
2031
|
+
</dict>
|
|
2032
|
+
<key>ThrottleInterval</key>
|
|
2033
|
+
<integer>2</integer>
|
|
2034
|
+
<key>AbandonProcessGroup</key>
|
|
2035
|
+
<true/>
|
|
2036
|
+
<key>StandardOutPath</key>
|
|
2037
|
+
<string>${xml(logFile)}</string>
|
|
2038
|
+
<key>StandardErrorPath</key>
|
|
2039
|
+
<string>${xml(logFile)}</string>
|
|
2040
|
+
</dict>
|
|
2041
|
+
</plist>
|
|
2042
|
+
`
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
function powershellLiteral(value) {
|
|
2046
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
2047
|
+
}
|
|
2048
|
+
function taskDefinition(input) {
|
|
2049
|
+
if (!input.userId)
|
|
2050
|
+
throw new Error("Task Scheduler requires userId");
|
|
2051
|
+
const entries = environmentEntries(input);
|
|
2052
|
+
let command = input.launch.command;
|
|
2053
|
+
let argumentsLine = windowsCommandLine(input.launch.args);
|
|
2054
|
+
let companionFiles;
|
|
2055
|
+
if (entries.length > 0) {
|
|
2056
|
+
const wrapperFile = path2.win32.join(input.stateDir, "service", "axterm-service.ps1");
|
|
2057
|
+
const environment = entries.map(([key, value]) => `$env:${key} = ${powershellLiteral(value)}`).join(`\r
|
|
2058
|
+
`);
|
|
2059
|
+
const invocation = [input.launch.command, ...input.launch.args].map(powershellLiteral).join(" ");
|
|
2060
|
+
companionFiles = [
|
|
2061
|
+
{
|
|
2062
|
+
file: wrapperFile,
|
|
2063
|
+
contents: `$ErrorActionPreference = 'Stop'\r
|
|
2064
|
+
${environment}\r
|
|
2065
|
+
& ${invocation}\r
|
|
2066
|
+
exit $LASTEXITCODE\r
|
|
2067
|
+
`
|
|
2068
|
+
}
|
|
2069
|
+
];
|
|
2070
|
+
command = "powershell.exe";
|
|
2071
|
+
argumentsLine = windowsCommandLine([
|
|
2072
|
+
"-NoProfile",
|
|
2073
|
+
"-NonInteractive",
|
|
2074
|
+
"-ExecutionPolicy",
|
|
2075
|
+
"Bypass",
|
|
2076
|
+
"-File",
|
|
2077
|
+
wrapperFile
|
|
2078
|
+
]);
|
|
2079
|
+
}
|
|
2080
|
+
return {
|
|
2081
|
+
kind: "task-scheduler",
|
|
2082
|
+
id: WINDOWS_TASK,
|
|
2083
|
+
file: path2.win32.join(input.stateDir, "service", "axterm-task.xml"),
|
|
2084
|
+
contents: `<?xml version="1.0" encoding="UTF-16"?>
|
|
2085
|
+
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
2086
|
+
<RegistrationInfo>
|
|
2087
|
+
<Description>axterm daemon</Description>
|
|
2088
|
+
</RegistrationInfo>
|
|
2089
|
+
<Triggers>
|
|
2090
|
+
<LogonTrigger>
|
|
2091
|
+
<Enabled>true</Enabled>
|
|
2092
|
+
<UserId>${xml(input.userId)}</UserId>
|
|
2093
|
+
</LogonTrigger>
|
|
2094
|
+
</Triggers>
|
|
2095
|
+
<Principals>
|
|
2096
|
+
<Principal id="Author">
|
|
2097
|
+
<UserId>${xml(input.userId)}</UserId>
|
|
2098
|
+
<LogonType>InteractiveToken</LogonType>
|
|
2099
|
+
<RunLevel>LeastPrivilege</RunLevel>
|
|
2100
|
+
</Principal>
|
|
2101
|
+
</Principals>
|
|
2102
|
+
<Settings>
|
|
2103
|
+
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
|
2104
|
+
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
|
2105
|
+
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
|
2106
|
+
<AllowHardTerminate>false</AllowHardTerminate>
|
|
2107
|
+
<StartWhenAvailable>true</StartWhenAvailable>
|
|
2108
|
+
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
|
|
2109
|
+
<RestartOnFailure>
|
|
2110
|
+
<Interval>PT1M</Interval>
|
|
2111
|
+
<Count>255</Count>
|
|
2112
|
+
</RestartOnFailure>
|
|
2113
|
+
</Settings>
|
|
2114
|
+
<Actions Context="Author">
|
|
2115
|
+
<Exec>
|
|
2116
|
+
<Command>${xml(command)}</Command>
|
|
2117
|
+
<Arguments>${xml(argumentsLine)}</Arguments>
|
|
2118
|
+
<WorkingDirectory>${xml(input.homeDir)}</WorkingDirectory>
|
|
2119
|
+
</Exec>
|
|
2120
|
+
</Actions>
|
|
2121
|
+
</Task>
|
|
2122
|
+
`,
|
|
2123
|
+
...companionFiles ? { companionFiles } : {}
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
function serviceDefinition(input) {
|
|
2127
|
+
if (input.platform === "linux")
|
|
2128
|
+
return systemdDefinition(input);
|
|
2129
|
+
if (input.platform === "darwin")
|
|
2130
|
+
return launchdDefinition(input);
|
|
2131
|
+
if (input.platform === "win32")
|
|
2132
|
+
return taskDefinition(input);
|
|
2133
|
+
throw new Error(`service management is not supported on ${input.platform}`);
|
|
2134
|
+
}
|
|
2135
|
+
var SYSTEMD_SERVICE = "axterm.service", LAUNCHD_LABEL = "com.layorix.axterm.daemon", WINDOWS_TASK = "axterm-daemon";
|
|
2136
|
+
var init_service = () => {};
|
|
2137
|
+
|
|
1867
2138
|
// packages/core/src/attachment.ts
|
|
1868
2139
|
function megabytes(bytes) {
|
|
1869
2140
|
return `${Math.round(bytes / (1024 * 1024))} MB`;
|
|
@@ -2054,6 +2325,22 @@ function parseProjectRef(raw) {
|
|
|
2054
2325
|
return { machine: text.slice(0, split), alias: text.slice(split + 1) };
|
|
2055
2326
|
return { alias: text };
|
|
2056
2327
|
}
|
|
2328
|
+
function parseCommandAddress(token) {
|
|
2329
|
+
const at = token.indexOf("@");
|
|
2330
|
+
if (at < 0)
|
|
2331
|
+
return { command: token };
|
|
2332
|
+
const target = token.slice(at + 1);
|
|
2333
|
+
if (!/^[A-Za-z0-9_]+$/.test(target))
|
|
2334
|
+
return { command: token };
|
|
2335
|
+
return { command: token.slice(0, at), target };
|
|
2336
|
+
}
|
|
2337
|
+
function commandIsForBot(target, self) {
|
|
2338
|
+
if (target === undefined)
|
|
2339
|
+
return true;
|
|
2340
|
+
if (self === undefined)
|
|
2341
|
+
return false;
|
|
2342
|
+
return target.toLowerCase() === self.toLowerCase();
|
|
2343
|
+
}
|
|
2057
2344
|
var MAX_NAME_LENGTH = 32;
|
|
2058
2345
|
|
|
2059
2346
|
// packages/core/src/fanout.ts
|
|
@@ -2163,6 +2450,9 @@ function buildApprovalQuestion(toolName) {
|
|
|
2163
2450
|
function isModalState(state) {
|
|
2164
2451
|
return state === "blocked" || state === "waiting";
|
|
2165
2452
|
}
|
|
2453
|
+
function offersKeystrokeApproval(profile, state) {
|
|
2454
|
+
return state === "blocked" && profile.structuredApprovals !== true;
|
|
2455
|
+
}
|
|
2166
2456
|
function dialogTransition(nextState, dialogOpen) {
|
|
2167
2457
|
if (isModalState(nextState))
|
|
2168
2458
|
return { open: true, resolved: false };
|
|
@@ -2173,15 +2463,6 @@ function isPromptInjectable(state) {
|
|
|
2173
2463
|
}
|
|
2174
2464
|
var init_choice = () => {};
|
|
2175
2465
|
|
|
2176
|
-
// packages/core/src/menu-keys.ts
|
|
2177
|
-
function buildMenuSelectKeys(index, normalizePresses = MENU_NORMALIZE_PRESSES) {
|
|
2178
|
-
if (!Number.isInteger(index) || index < 0) {
|
|
2179
|
-
throw new RangeError(`menu index must be a non-negative integer, got ${index}`);
|
|
2180
|
-
}
|
|
2181
|
-
return MENU_UP.repeat(normalizePresses) + MENU_DOWN.repeat(index) + MENU_SELECT;
|
|
2182
|
-
}
|
|
2183
|
-
var MENU_UP = "\x1B[A", MENU_DOWN = "\x1B[B", MENU_SELECT = "\r", MENU_NORMALIZE_PRESSES = 24;
|
|
2184
|
-
|
|
2185
2466
|
// packages/core/src/adopt.ts
|
|
2186
2467
|
function resolveSessionCwd(input) {
|
|
2187
2468
|
if (!input.preferConversation)
|
|
@@ -2548,7 +2829,7 @@ function mapClaudeHook(input) {
|
|
|
2548
2829
|
}
|
|
2549
2830
|
}
|
|
2550
2831
|
function buildClaudeSettings(input) {
|
|
2551
|
-
const handler = {
|
|
2832
|
+
const handler = (timeout) => ({
|
|
2552
2833
|
type: "http",
|
|
2553
2834
|
url: `http://127.0.0.1:${input.port}/hook/claude`,
|
|
2554
2835
|
headers: {
|
|
@@ -2556,16 +2837,36 @@ function buildClaudeSettings(input) {
|
|
|
2556
2837
|
"x-axterm-session": input.sessionId,
|
|
2557
2838
|
"x-axterm-launch": input.launchToken
|
|
2558
2839
|
},
|
|
2559
|
-
timeout
|
|
2560
|
-
};
|
|
2840
|
+
timeout
|
|
2841
|
+
});
|
|
2842
|
+
const observe = handler(HOOK_TIMEOUT_SECONDS);
|
|
2561
2843
|
const hooks = {};
|
|
2562
|
-
for (const event of TOOL_EVENTS)
|
|
2563
|
-
|
|
2844
|
+
for (const event of TOOL_EVENTS) {
|
|
2845
|
+
const single = event === "PermissionRequest" ? handler(APPROVAL_TIMEOUT_SECONDS) : observe;
|
|
2846
|
+
hooks[event] = [{ matcher: "*", hooks: [single] }];
|
|
2847
|
+
}
|
|
2564
2848
|
for (const event of PLAIN_EVENTS)
|
|
2565
|
-
hooks[event] = [{ hooks: [
|
|
2849
|
+
hooks[event] = [{ hooks: [observe] }];
|
|
2566
2850
|
return { hooks };
|
|
2567
2851
|
}
|
|
2568
|
-
|
|
2852
|
+
function hookPermissionDecision(question, answers, denyMessage) {
|
|
2853
|
+
const picked = answers?.find((a) => a.questionIndex === 0)?.selected[0];
|
|
2854
|
+
if (picked === undefined)
|
|
2855
|
+
return;
|
|
2856
|
+
if (picked === question.options[0]?.label) {
|
|
2857
|
+
return { hookSpecificOutput: { hookEventName: "PermissionRequest", decision: { behavior: "allow" } } };
|
|
2858
|
+
}
|
|
2859
|
+
if (picked === question.options[1]?.label) {
|
|
2860
|
+
return {
|
|
2861
|
+
hookSpecificOutput: {
|
|
2862
|
+
hookEventName: "PermissionRequest",
|
|
2863
|
+
decision: { behavior: "deny", ...denyMessage !== undefined ? { message: denyMessage } : {} }
|
|
2864
|
+
}
|
|
2865
|
+
};
|
|
2866
|
+
}
|
|
2867
|
+
return;
|
|
2868
|
+
}
|
|
2869
|
+
var MAX_RESULT_TEXT = 2000, HOOK_TIMEOUT_SECONDS = 5, APPROVAL_TIMEOUT_SECONDS = 120, APPROVAL_HOLD_MS = 110000, TOOL_EVENTS, PLAIN_EVENTS;
|
|
2569
2870
|
var init_claude_hooks = __esm(() => {
|
|
2570
2871
|
TOOL_EVENTS = ["PreToolUse", "PostToolUse", "PostToolUseFailure", "PermissionRequest"];
|
|
2571
2872
|
PLAIN_EVENTS = [
|
|
@@ -2591,18 +2892,25 @@ function detectOnboarding(agent, screen) {
|
|
|
2591
2892
|
if (!dialog)
|
|
2592
2893
|
continue;
|
|
2593
2894
|
const options = [];
|
|
2895
|
+
let lastOption = -1;
|
|
2594
2896
|
const end = Math.min(lines.length, i + 1 + OPTION_WINDOW_LINES + 1);
|
|
2595
2897
|
for (let j = i + 1;j < end; j++) {
|
|
2596
2898
|
const row = OPTION_ROW.exec(lines[j]);
|
|
2597
|
-
if (row)
|
|
2899
|
+
if (row) {
|
|
2598
2900
|
options.push({ digit: row[1], label: row[2].trim() });
|
|
2901
|
+
lastOption = j;
|
|
2902
|
+
}
|
|
2599
2903
|
}
|
|
2600
|
-
if (options.length
|
|
2601
|
-
|
|
2904
|
+
if (options.length < 2)
|
|
2905
|
+
continue;
|
|
2906
|
+
const noise = lines.slice(lastOption + 1).filter((row) => row !== "" && !DIALOG_FOOTER.test(row));
|
|
2907
|
+
if (noise.length > QUIET_AFTER_OPTIONS)
|
|
2908
|
+
continue;
|
|
2909
|
+
return { kind: dialog.kind, question: dialog.question, options };
|
|
2602
2910
|
}
|
|
2603
2911
|
return;
|
|
2604
2912
|
}
|
|
2605
|
-
var DIALOGS, OPTION_ROW, OPTION_WINDOW_LINES = 8;
|
|
2913
|
+
var DIALOGS, OPTION_ROW, OPTION_WINDOW_LINES = 8, QUIET_AFTER_OPTIONS = 3, DIALOG_FOOTER;
|
|
2606
2914
|
var init_onboarding = __esm(() => {
|
|
2607
2915
|
DIALOGS = {
|
|
2608
2916
|
gemini: [
|
|
@@ -2618,6 +2926,97 @@ var init_onboarding = __esm(() => {
|
|
|
2618
2926
|
codex: [{ kind: "folderTrust", question: "Do you trust the contents of this directory?" }]
|
|
2619
2927
|
};
|
|
2620
2928
|
OPTION_ROW = /^[\u25CF\u25CB\u276F>\u203A]?\s*(\d)\.\s+(.+)$/;
|
|
2929
|
+
DIALOG_FOOTER = /^[\s\u2502\u2500\u2570\u256F\u256D\u256E\u250C\u2510\u2514\u2518|]*$|enter to (confirm|continue|select)|esc to cancel|use enter to select|press enter|\u2191\/\u2193|arrow keys|tab to|^\(.*\)$/i;
|
|
2930
|
+
});
|
|
2931
|
+
|
|
2932
|
+
// packages/core/src/diff-view.ts
|
|
2933
|
+
function scanStructure(lines) {
|
|
2934
|
+
const meta = new Set;
|
|
2935
|
+
let proven = false;
|
|
2936
|
+
let inHunk = false;
|
|
2937
|
+
for (let i = 0;i < lines.length; i++) {
|
|
2938
|
+
const line = lines[i] ?? "";
|
|
2939
|
+
if (line.startsWith("@@")) {
|
|
2940
|
+
inHunk = true;
|
|
2941
|
+
proven ||= HUNK_HEADER.test(line);
|
|
2942
|
+
continue;
|
|
2943
|
+
}
|
|
2944
|
+
if (FILE_SECTION_START.test(line)) {
|
|
2945
|
+
inHunk = false;
|
|
2946
|
+
proven ||= line.startsWith("diff --git ");
|
|
2947
|
+
continue;
|
|
2948
|
+
}
|
|
2949
|
+
if (inHunk || !line.startsWith("--- "))
|
|
2950
|
+
continue;
|
|
2951
|
+
if ((lines[i + 1] ?? "").startsWith("+++ ")) {
|
|
2952
|
+
meta.add(i);
|
|
2953
|
+
meta.add(i + 1);
|
|
2954
|
+
i += 1;
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
return { meta, proven };
|
|
2958
|
+
}
|
|
2959
|
+
function countTail(lines) {
|
|
2960
|
+
let added = 0;
|
|
2961
|
+
let removed = 0;
|
|
2962
|
+
for (const line of lines) {
|
|
2963
|
+
if (line.startsWith("+++ ") || line.startsWith("--- "))
|
|
2964
|
+
continue;
|
|
2965
|
+
if (line.startsWith("+"))
|
|
2966
|
+
added += 1;
|
|
2967
|
+
else if (line.startsWith("-"))
|
|
2968
|
+
removed += 1;
|
|
2969
|
+
}
|
|
2970
|
+
return { added, removed };
|
|
2971
|
+
}
|
|
2972
|
+
function summarizePatch(patch, maxChars = DIFF_MAX_CHARS, maxLines = DIFF_MAX_LINES) {
|
|
2973
|
+
const totalChars = patch.length;
|
|
2974
|
+
if (totalChars === 0)
|
|
2975
|
+
return;
|
|
2976
|
+
const clipped = patch.slice(0, maxChars);
|
|
2977
|
+
const all = clipped.split(`
|
|
2978
|
+
`);
|
|
2979
|
+
const cutChars = totalChars > maxChars;
|
|
2980
|
+
const cutLines = all.length > maxLines;
|
|
2981
|
+
const kept = all.slice(0, maxLines);
|
|
2982
|
+
const { meta, proven } = scanStructure(kept);
|
|
2983
|
+
if (!proven)
|
|
2984
|
+
return;
|
|
2985
|
+
let added = 0;
|
|
2986
|
+
let removed = 0;
|
|
2987
|
+
const lines = kept.map((line, i) => {
|
|
2988
|
+
if (meta.has(i) || line.startsWith("@@") || FILE_SECTION_START.test(line)) {
|
|
2989
|
+
return { kind: "meta", text: line };
|
|
2990
|
+
}
|
|
2991
|
+
if (line.startsWith("+")) {
|
|
2992
|
+
added += 1;
|
|
2993
|
+
return { kind: "add", text: line.slice(1) };
|
|
2994
|
+
}
|
|
2995
|
+
if (line.startsWith("-")) {
|
|
2996
|
+
removed += 1;
|
|
2997
|
+
return { kind: "del", text: line.slice(1) };
|
|
2998
|
+
}
|
|
2999
|
+
return { kind: "context", text: line };
|
|
3000
|
+
});
|
|
3001
|
+
if (added + removed === 0)
|
|
3002
|
+
return;
|
|
3003
|
+
const tail = countTail(all.slice(maxLines));
|
|
3004
|
+
added += tail.added;
|
|
3005
|
+
removed += tail.removed;
|
|
3006
|
+
const truncated = cutChars ? "chars" : cutLines ? "lines" : undefined;
|
|
3007
|
+
return {
|
|
3008
|
+
lines: truncated === undefined ? lines : [...lines.slice(0, maxLines - 1), TRUNCATED],
|
|
3009
|
+
added,
|
|
3010
|
+
removed,
|
|
3011
|
+
...truncated === undefined ? {} : { truncated },
|
|
3012
|
+
totalChars
|
|
3013
|
+
};
|
|
3014
|
+
}
|
|
3015
|
+
var DIFF_MAX_CHARS = 8000, DIFF_MAX_LINES = 80, TRUNCATED, HUNK_HEADER, FILE_SECTION_START;
|
|
3016
|
+
var init_diff_view = __esm(() => {
|
|
3017
|
+
TRUNCATED = { kind: "meta", text: "\u2026 diff truncated \u2026" };
|
|
3018
|
+
HUNK_HEADER = /^@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/;
|
|
3019
|
+
FILE_SECTION_START = /^(?:diff |index |old mode |new mode |new file mode |deleted file mode |similarity index |dissimilarity index |rename |copy |Binary files )/;
|
|
2621
3020
|
});
|
|
2622
3021
|
|
|
2623
3022
|
// packages/core/src/doctor.ts
|
|
@@ -2863,7 +3262,7 @@ var SEQUENCES, KEY_NAMES;
|
|
|
2863
3262
|
var init_keys = __esm(() => {
|
|
2864
3263
|
SEQUENCES = {
|
|
2865
3264
|
approve: "\r",
|
|
2866
|
-
deny: "\x1B
|
|
3265
|
+
deny: "\x1B",
|
|
2867
3266
|
enter: "\r",
|
|
2868
3267
|
esc: "\x1B",
|
|
2869
3268
|
up: "\x1B[A",
|
|
@@ -3339,24 +3738,7 @@ trusted_hash = "${e.hash}"
|
|
|
3339
3738
|
}).join(`
|
|
3340
3739
|
`);
|
|
3341
3740
|
}
|
|
3342
|
-
|
|
3343
|
-
const picked = answers?.find((a) => a.questionIndex === 0)?.selected[0];
|
|
3344
|
-
if (picked === undefined)
|
|
3345
|
-
return;
|
|
3346
|
-
if (picked === question.options[0]?.label) {
|
|
3347
|
-
return { hookSpecificOutput: { hookEventName: "PermissionRequest", decision: { behavior: "allow" } } };
|
|
3348
|
-
}
|
|
3349
|
-
if (picked === question.options[1]?.label) {
|
|
3350
|
-
return {
|
|
3351
|
-
hookSpecificOutput: {
|
|
3352
|
-
hookEventName: "PermissionRequest",
|
|
3353
|
-
decision: { behavior: "deny", ...denyMessage !== undefined ? { message: denyMessage } : {} }
|
|
3354
|
-
}
|
|
3355
|
-
};
|
|
3356
|
-
}
|
|
3357
|
-
return;
|
|
3358
|
-
}
|
|
3359
|
-
var OBSERVATION_TIMEOUT_SECONDS = 15, CODEX_PERMISSION_TIMEOUT_SECONDS = 120, CODEX_APPROVAL_HOLD_MS = 110000, CODEX_APPROVAL_POST_TIMEOUT_MS = 115000, SESSION_END_TIMEOUT_SECONDS = 2, OBSERVATION_EVENTS, EVENT_KEY_LABELS;
|
|
3741
|
+
var OBSERVATION_TIMEOUT_SECONDS = 15, CODEX_PERMISSION_TIMEOUT_SECONDS = 120, CODEX_APPROVAL_POST_TIMEOUT_MS = 115000, SESSION_END_TIMEOUT_SECONDS = 2, OBSERVATION_EVENTS, EVENT_KEY_LABELS;
|
|
3360
3742
|
var init_codex_hooks = __esm(() => {
|
|
3361
3743
|
OBSERVATION_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "Stop"];
|
|
3362
3744
|
EVENT_KEY_LABELS = {
|
|
@@ -3610,11 +3992,11 @@ var init_prompt_detect = __esm(() => {
|
|
|
3610
3992
|
});
|
|
3611
3993
|
|
|
3612
3994
|
// packages/core/src/session-scope.ts
|
|
3613
|
-
import
|
|
3995
|
+
import path3 from "path";
|
|
3614
3996
|
function comparable(target, resolve) {
|
|
3615
|
-
let real =
|
|
3997
|
+
let real = path3.resolve(target);
|
|
3616
3998
|
try {
|
|
3617
|
-
real =
|
|
3999
|
+
real = path3.resolve(resolve(real));
|
|
3618
4000
|
} catch {}
|
|
3619
4001
|
const trimmed = real.replace(/[\\/]+$/, "");
|
|
3620
4002
|
return process.platform === "win32" ? trimmed.toLowerCase() : trimmed;
|
|
@@ -3622,7 +4004,7 @@ function comparable(target, resolve) {
|
|
|
3622
4004
|
function pathWithin(parent, child, resolve = asGiven) {
|
|
3623
4005
|
const from = comparable(parent, resolve);
|
|
3624
4006
|
const to = comparable(child, resolve);
|
|
3625
|
-
return to === from || to.startsWith(from +
|
|
4007
|
+
return to === from || to.startsWith(from + path3.sep);
|
|
3626
4008
|
}
|
|
3627
4009
|
function sessionInScope(session, scope, resolve = asGiven) {
|
|
3628
4010
|
if (pathWithin(scope, session.cwd, resolve))
|
|
@@ -3677,6 +4059,7 @@ var init_src = __esm(() => {
|
|
|
3677
4059
|
init_agent_registry();
|
|
3678
4060
|
init_agent_resume();
|
|
3679
4061
|
init_self_spawn();
|
|
4062
|
+
init_service();
|
|
3680
4063
|
init_channel_spi();
|
|
3681
4064
|
init_update();
|
|
3682
4065
|
init_errors();
|
|
@@ -3688,6 +4071,7 @@ var init_src = __esm(() => {
|
|
|
3688
4071
|
init_pty_protocol();
|
|
3689
4072
|
init_claude_hooks();
|
|
3690
4073
|
init_onboarding();
|
|
4074
|
+
init_diff_view();
|
|
3691
4075
|
init_doctor();
|
|
3692
4076
|
init_stall();
|
|
3693
4077
|
init_codex_stream();
|
|
@@ -3709,14 +4093,14 @@ var init_src = __esm(() => {
|
|
|
3709
4093
|
});
|
|
3710
4094
|
|
|
3711
4095
|
// packages/daemon/src/config-store.ts
|
|
3712
|
-
import
|
|
3713
|
-
import
|
|
3714
|
-
function
|
|
4096
|
+
import fs7 from "fs";
|
|
4097
|
+
import path8 from "path";
|
|
4098
|
+
function writeAtomic2(target, contents, mode) {
|
|
3715
4099
|
const tmp = `${target}.tmp`;
|
|
3716
|
-
|
|
3717
|
-
|
|
4100
|
+
fs7.writeFileSync(tmp, contents, { mode });
|
|
4101
|
+
fs7.renameSync(tmp, target);
|
|
3718
4102
|
if (process.platform !== "win32")
|
|
3719
|
-
|
|
4103
|
+
fs7.chmodSync(target, mode);
|
|
3720
4104
|
}
|
|
3721
4105
|
|
|
3722
4106
|
class ConfigStore {
|
|
@@ -3732,16 +4116,16 @@ class ConfigStore {
|
|
|
3732
4116
|
}
|
|
3733
4117
|
load() {
|
|
3734
4118
|
this.loadError = undefined;
|
|
3735
|
-
|
|
4119
|
+
fs7.mkdirSync(this.paths.configDir, { recursive: true, mode: DIR_MODE });
|
|
3736
4120
|
if (process.platform !== "win32") {
|
|
3737
4121
|
try {
|
|
3738
|
-
|
|
4122
|
+
fs7.chmodSync(this.paths.configDir, DIR_MODE);
|
|
3739
4123
|
} catch {}
|
|
3740
4124
|
}
|
|
3741
4125
|
this.config = structuredClone(DEFAULT_CONFIG);
|
|
3742
|
-
if (
|
|
4126
|
+
if (fs7.existsSync(this.paths.configFile)) {
|
|
3743
4127
|
try {
|
|
3744
|
-
const parsed = parseConfig(JSON.parse(
|
|
4128
|
+
const parsed = parseConfig(JSON.parse(fs7.readFileSync(this.paths.configFile, "utf8")));
|
|
3745
4129
|
if ("errors" in parsed)
|
|
3746
4130
|
this.loadError = parsed.errors.join("; ");
|
|
3747
4131
|
else
|
|
@@ -3751,10 +4135,10 @@ class ConfigStore {
|
|
|
3751
4135
|
}
|
|
3752
4136
|
}
|
|
3753
4137
|
this.secrets = {};
|
|
3754
|
-
if (
|
|
4138
|
+
if (fs7.existsSync(this.paths.secretsFile)) {
|
|
3755
4139
|
this.tightenPermissions(this.paths.secretsFile);
|
|
3756
4140
|
try {
|
|
3757
|
-
const raw = JSON.parse(
|
|
4141
|
+
const raw = JSON.parse(fs7.readFileSync(this.paths.secretsFile, "utf8"));
|
|
3758
4142
|
if (raw !== null && typeof raw === "object") {
|
|
3759
4143
|
for (const [key, value] of Object.entries(raw)) {
|
|
3760
4144
|
if (typeof value === "string")
|
|
@@ -3770,8 +4154,8 @@ class ConfigStore {
|
|
|
3770
4154
|
if (process.platform === "win32")
|
|
3771
4155
|
return;
|
|
3772
4156
|
try {
|
|
3773
|
-
if ((
|
|
3774
|
-
|
|
4157
|
+
if ((fs7.statSync(file).mode & 63) !== 0)
|
|
4158
|
+
fs7.chmodSync(file, FILE_MODE);
|
|
3775
4159
|
} catch {}
|
|
3776
4160
|
}
|
|
3777
4161
|
get() {
|
|
@@ -3779,7 +4163,7 @@ class ConfigStore {
|
|
|
3779
4163
|
}
|
|
3780
4164
|
health() {
|
|
3781
4165
|
return configHealth({
|
|
3782
|
-
fileExists:
|
|
4166
|
+
fileExists: fs7.existsSync(this.paths.configFile),
|
|
3783
4167
|
parseError: this.loadError,
|
|
3784
4168
|
config: this.resolved()
|
|
3785
4169
|
});
|
|
@@ -3812,7 +4196,7 @@ class ConfigStore {
|
|
|
3812
4196
|
const parsed = parseConfig(candidate);
|
|
3813
4197
|
if ("errors" in parsed)
|
|
3814
4198
|
throw new Error(parsed.errors.join("; "));
|
|
3815
|
-
|
|
4199
|
+
writeAtomic2(this.paths.configFile, `${JSON.stringify(parsed.config, null, 2)}
|
|
3816
4200
|
`, FILE_MODE);
|
|
3817
4201
|
this.config = parsed.config;
|
|
3818
4202
|
this.loadError = undefined;
|
|
@@ -3824,11 +4208,11 @@ class ConfigStore {
|
|
|
3824
4208
|
delete this.secrets[dottedPath];
|
|
3825
4209
|
else
|
|
3826
4210
|
this.secrets[dottedPath] = value;
|
|
3827
|
-
if (!
|
|
3828
|
-
|
|
4211
|
+
if (!fs7.existsSync(this.paths.configFile)) {
|
|
4212
|
+
writeAtomic2(this.paths.configFile, `${JSON.stringify(this.config, null, 2)}
|
|
3829
4213
|
`, FILE_MODE);
|
|
3830
4214
|
}
|
|
3831
|
-
|
|
4215
|
+
writeAtomic2(this.paths.secretsFile, `${JSON.stringify(this.secrets, null, 2)}
|
|
3832
4216
|
`, FILE_MODE);
|
|
3833
4217
|
}
|
|
3834
4218
|
hasSecret(dottedPath) {
|
|
@@ -3854,7 +4238,7 @@ class ConfigStore {
|
|
|
3854
4238
|
fingerprint() {
|
|
3855
4239
|
const stamp = (file) => {
|
|
3856
4240
|
try {
|
|
3857
|
-
return String(
|
|
4241
|
+
return String(fs7.statSync(file).mtimeMs);
|
|
3858
4242
|
} catch {
|
|
3859
4243
|
return "-";
|
|
3860
4244
|
}
|
|
@@ -3876,8 +4260,8 @@ function applyPath(config, dottedPath, value) {
|
|
|
3876
4260
|
function defaultConfigStorePaths(configDir) {
|
|
3877
4261
|
return {
|
|
3878
4262
|
configDir,
|
|
3879
|
-
configFile:
|
|
3880
|
-
secretsFile:
|
|
4263
|
+
configFile: path8.join(configDir, "config.json"),
|
|
4264
|
+
secretsFile: path8.join(configDir, "secrets.json")
|
|
3881
4265
|
};
|
|
3882
4266
|
}
|
|
3883
4267
|
var ENV_ALIASES, DIR_MODE = 448, FILE_MODE = 384;
|
|
@@ -3896,13 +4280,13 @@ var init_config_store = __esm(() => {
|
|
|
3896
4280
|
});
|
|
3897
4281
|
|
|
3898
4282
|
// packages/daemon/src/shell.ts
|
|
3899
|
-
import
|
|
3900
|
-
import
|
|
4283
|
+
import fs8 from "fs";
|
|
4284
|
+
import path9 from "path";
|
|
3901
4285
|
function findExecutable(name, env) {
|
|
3902
4286
|
const isWindows = env.platform === "win32";
|
|
3903
|
-
const join = isWindows ?
|
|
4287
|
+
const join = isWindows ? path9.win32.join : path9.posix.join;
|
|
3904
4288
|
const separator = isWindows ? ";" : ":";
|
|
3905
|
-
const extensions = isWindows ?
|
|
4289
|
+
const extensions = isWindows ? path9.win32.extname(name).length > 0 ? [""] : env.pathExt.split(";").filter((e) => e.length > 0) : [""];
|
|
3906
4290
|
for (const rawDir of env.pathEnv.split(separator)) {
|
|
3907
4291
|
const dir = rawDir.trim().replace(/^"|"$/g, "");
|
|
3908
4292
|
if (dir.length === 0)
|
|
@@ -3920,16 +4304,16 @@ function lookupOnPath(name) {
|
|
|
3920
4304
|
platform: process.platform,
|
|
3921
4305
|
pathEnv: process.env["PATH"] ?? "",
|
|
3922
4306
|
pathExt: process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD",
|
|
3923
|
-
exists: (candidate) =>
|
|
4307
|
+
exists: (candidate) => fs8.existsSync(candidate)
|
|
3924
4308
|
});
|
|
3925
4309
|
}
|
|
3926
4310
|
function resolveShell(kind) {
|
|
3927
4311
|
if (process.platform !== "win32")
|
|
3928
4312
|
return resolvePosixShell(kind);
|
|
3929
4313
|
const systemRoot = process.env["SystemRoot"] ?? "C:\\Windows";
|
|
3930
|
-
const windowsPowershell =
|
|
4314
|
+
const windowsPowershell = path9.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
3931
4315
|
if (kind === "cmd") {
|
|
3932
|
-
return { kind: "cmd", exe:
|
|
4316
|
+
return { kind: "cmd", exe: path9.join(systemRoot, "System32", "cmd.exe") };
|
|
3933
4317
|
}
|
|
3934
4318
|
if (kind === "powershell") {
|
|
3935
4319
|
return { kind: "powershell", exe: windowsPowershell };
|
|
@@ -3957,7 +4341,7 @@ function resolvePosixShell(kind) {
|
|
|
3957
4341
|
candidates.push({ kind: "bash", exe: login });
|
|
3958
4342
|
candidates.push({ kind: "bash", exe: "/bin/bash" }, { kind: "bash", exe: "/usr/bin/bash" });
|
|
3959
4343
|
}
|
|
3960
|
-
const found = candidates.find((c) =>
|
|
4344
|
+
const found = candidates.find((c) => fs8.existsSync(c.exe));
|
|
3961
4345
|
if (found)
|
|
3962
4346
|
return found;
|
|
3963
4347
|
return { kind: "sh", exe: "/bin/sh" };
|
|
@@ -4002,24 +4386,24 @@ function ipcEndpoint(platform, user, env) {
|
|
|
4002
4386
|
const suffix = `-${shortHash(env.runtimeDir)}`;
|
|
4003
4387
|
return `\\\\.\\pipe\\axterm-${safeUser}${suffix}`;
|
|
4004
4388
|
}
|
|
4005
|
-
return
|
|
4389
|
+
return path9.posix.join(env.runtimeDir, "axterm.sock");
|
|
4006
4390
|
}
|
|
4007
4391
|
var cachedPwsh;
|
|
4008
4392
|
var init_shell = () => {};
|
|
4009
4393
|
|
|
4010
4394
|
// packages/daemon/src/paths.ts
|
|
4011
|
-
import
|
|
4012
|
-
import
|
|
4013
|
-
import
|
|
4395
|
+
import fs9 from "fs";
|
|
4396
|
+
import os4 from "os";
|
|
4397
|
+
import path10 from "path";
|
|
4014
4398
|
function ensureDataDirs() {
|
|
4015
4399
|
for (const dir of [PATHS.configDir, PATHS.stateDir, PATHS.runtimeDir, SCROLLBACK_DIR, LOG_DIR, EXT_DIR]) {
|
|
4016
|
-
|
|
4400
|
+
fs9.mkdirSync(dir, { recursive: true, mode: DIR_MODE2 });
|
|
4017
4401
|
}
|
|
4018
4402
|
if (process.platform === "win32")
|
|
4019
4403
|
return;
|
|
4020
4404
|
for (const dir of [PATHS.configDir, PATHS.stateDir, PATHS.runtimeDir]) {
|
|
4021
4405
|
try {
|
|
4022
|
-
|
|
4406
|
+
fs9.chmodSync(dir, DIR_MODE2);
|
|
4023
4407
|
} catch {}
|
|
4024
4408
|
}
|
|
4025
4409
|
}
|
|
@@ -4027,14 +4411,14 @@ function restrictFile(file) {
|
|
|
4027
4411
|
if (process.platform === "win32")
|
|
4028
4412
|
return;
|
|
4029
4413
|
try {
|
|
4030
|
-
|
|
4414
|
+
fs9.chmodSync(file, 384);
|
|
4031
4415
|
} catch {}
|
|
4032
4416
|
}
|
|
4033
4417
|
function log(message) {
|
|
4034
4418
|
const line = `${new Date().toISOString()} ${message}
|
|
4035
4419
|
`;
|
|
4036
4420
|
try {
|
|
4037
|
-
|
|
4421
|
+
fs9.appendFileSync(LOG_FILE, line);
|
|
4038
4422
|
} catch {}
|
|
4039
4423
|
}
|
|
4040
4424
|
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;
|
|
@@ -4051,15 +4435,15 @@ var init_paths = __esm(() => {
|
|
|
4051
4435
|
DB_PATH = PATHS.dbFile;
|
|
4052
4436
|
RUNTIME_FILE2 = PATHS.runtimeFile;
|
|
4053
4437
|
HOOK_ENDPOINT_FILE = PATHS.hookEndpointFile;
|
|
4054
|
-
PIPE_PATH = ipcEndpoint(process.platform,
|
|
4438
|
+
PIPE_PATH = ipcEndpoint(process.platform, os4.userInfo().username, {
|
|
4055
4439
|
runtimeDir: PATHS.runtimeDir
|
|
4056
4440
|
});
|
|
4057
|
-
LOG_FILE =
|
|
4441
|
+
LOG_FILE = path10.join(LOG_DIR, "daemon.log");
|
|
4058
4442
|
});
|
|
4059
4443
|
|
|
4060
4444
|
// packages/daemon/src/store.ts
|
|
4061
|
-
import
|
|
4062
|
-
import
|
|
4445
|
+
import fs10 from "fs";
|
|
4446
|
+
import path11 from "path";
|
|
4063
4447
|
import { Database } from "bun:sqlite";
|
|
4064
4448
|
function rowToInfo(row) {
|
|
4065
4449
|
return {
|
|
@@ -4090,7 +4474,7 @@ function rowToInfo(row) {
|
|
|
4090
4474
|
class Store {
|
|
4091
4475
|
db;
|
|
4092
4476
|
constructor(dbPath = DB_PATH) {
|
|
4093
|
-
|
|
4477
|
+
fs10.mkdirSync(path11.dirname(dbPath), { recursive: true });
|
|
4094
4478
|
this.db = new Database(dbPath, { strict: true });
|
|
4095
4479
|
this.db.run("PRAGMA journal_mode = WAL");
|
|
4096
4480
|
this.db.run(`
|
|
@@ -4258,11 +4642,11 @@ var init_store = __esm(() => {
|
|
|
4258
4642
|
});
|
|
4259
4643
|
|
|
4260
4644
|
// packages/daemon/src/config-migrate.ts
|
|
4261
|
-
import
|
|
4262
|
-
import
|
|
4645
|
+
import fs11 from "fs";
|
|
4646
|
+
import path12 from "path";
|
|
4263
4647
|
import { Database as Database2 } from "bun:sqlite";
|
|
4264
4648
|
function checkpointWal(dbFile) {
|
|
4265
|
-
if (!
|
|
4649
|
+
if (!fs11.existsSync(dbFile))
|
|
4266
4650
|
return;
|
|
4267
4651
|
try {
|
|
4268
4652
|
const db = new Database2(dbFile);
|
|
@@ -4272,32 +4656,32 @@ function checkpointWal(dbFile) {
|
|
|
4272
4656
|
}
|
|
4273
4657
|
function relocate(source, target) {
|
|
4274
4658
|
try {
|
|
4275
|
-
|
|
4659
|
+
fs11.renameSync(source, target);
|
|
4276
4660
|
} catch {
|
|
4277
|
-
|
|
4661
|
+
fs11.cpSync(source, target, { recursive: true });
|
|
4278
4662
|
}
|
|
4279
4663
|
}
|
|
4280
4664
|
function moveInto(from, to, notes) {
|
|
4281
|
-
checkpointWal(
|
|
4282
|
-
|
|
4665
|
+
checkpointWal(path12.join(from, "axterm.db"));
|
|
4666
|
+
fs11.mkdirSync(to, { recursive: true });
|
|
4283
4667
|
for (const entry of STATE_ENTRIES) {
|
|
4284
|
-
const source =
|
|
4285
|
-
const target =
|
|
4286
|
-
if (!
|
|
4668
|
+
const source = path12.join(from, entry);
|
|
4669
|
+
const target = path12.join(to, entry);
|
|
4670
|
+
if (!fs11.existsSync(source))
|
|
4287
4671
|
continue;
|
|
4288
|
-
if (!
|
|
4672
|
+
if (!fs11.existsSync(target)) {
|
|
4289
4673
|
relocate(source, target);
|
|
4290
4674
|
notes.push(`moved ${entry}`);
|
|
4291
4675
|
continue;
|
|
4292
4676
|
}
|
|
4293
|
-
if (!
|
|
4677
|
+
if (!fs11.statSync(source).isDirectory() || !fs11.statSync(target).isDirectory())
|
|
4294
4678
|
continue;
|
|
4295
4679
|
let moved = 0;
|
|
4296
|
-
for (const child of
|
|
4297
|
-
const childTarget =
|
|
4298
|
-
if (
|
|
4680
|
+
for (const child of fs11.readdirSync(source)) {
|
|
4681
|
+
const childTarget = path12.join(target, child);
|
|
4682
|
+
if (fs11.existsSync(childTarget))
|
|
4299
4683
|
continue;
|
|
4300
|
-
relocate(
|
|
4684
|
+
relocate(path12.join(source, child), childTarget);
|
|
4301
4685
|
moved += 1;
|
|
4302
4686
|
}
|
|
4303
4687
|
if (moved > 0)
|
|
@@ -4307,29 +4691,29 @@ function moveInto(from, to, notes) {
|
|
|
4307
4691
|
function migrateLegacyConfig(input) {
|
|
4308
4692
|
const notes = [];
|
|
4309
4693
|
const storePaths = defaultConfigStorePaths(input.configDir);
|
|
4310
|
-
if (
|
|
4694
|
+
if (fs11.existsSync(storePaths.configFile))
|
|
4311
4695
|
return { migrated: false, notes };
|
|
4312
|
-
const configSource = input.legacyRoots.find((dir) =>
|
|
4696
|
+
const configSource = input.legacyRoots.find((dir) => fs11.existsSync(path12.join(dir, "config.json")));
|
|
4313
4697
|
if (configSource !== undefined) {
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
const secrets =
|
|
4317
|
-
if (
|
|
4318
|
-
|
|
4698
|
+
fs11.mkdirSync(input.configDir, { recursive: true });
|
|
4699
|
+
fs11.copyFileSync(path12.join(configSource, "config.json"), storePaths.configFile);
|
|
4700
|
+
const secrets = path12.join(configSource, "secrets.json");
|
|
4701
|
+
if (fs11.existsSync(secrets)) {
|
|
4702
|
+
fs11.copyFileSync(secrets, storePaths.secretsFile);
|
|
4319
4703
|
try {
|
|
4320
|
-
|
|
4704
|
+
fs11.chmodSync(storePaths.secretsFile, 384);
|
|
4321
4705
|
} catch {}
|
|
4322
4706
|
}
|
|
4323
|
-
|
|
4707
|
+
fs11.writeFileSync(path12.join(configSource, "MIGRATED.txt"), `axterm now reads its configuration from:
|
|
4324
4708
|
${input.configDir}
|
|
4325
4709
|
` + `These files are no longer used. secrets.json still holds your bot token \u2014 delete this directory once you are satisfied.
|
|
4326
4710
|
`);
|
|
4327
4711
|
notes.push(`adopted configuration from ${configSource}`);
|
|
4328
4712
|
}
|
|
4329
|
-
const legacy = input.legacyRoots.find((dir) =>
|
|
4713
|
+
const legacy = input.legacyRoots.find((dir) => fs11.existsSync(path12.join(dir, "axterm.db")));
|
|
4330
4714
|
if (legacy !== undefined) {
|
|
4331
4715
|
moveInto(legacy, input.stateDir, notes);
|
|
4332
|
-
|
|
4716
|
+
fs11.writeFileSync(path12.join(legacy, "MIGRATED.txt"), `axterm now stores state in:
|
|
4333
4717
|
${input.stateDir}
|
|
4334
4718
|
and configuration in:
|
|
4335
4719
|
${input.configDir}
|
|
@@ -4338,8 +4722,8 @@ and configuration in:
|
|
|
4338
4722
|
}
|
|
4339
4723
|
if (configSource !== undefined)
|
|
4340
4724
|
return { migrated: true, ...legacy ? { from: legacy } : {}, notes };
|
|
4341
|
-
const dbFile =
|
|
4342
|
-
if (!
|
|
4725
|
+
const dbFile = path12.join(input.stateDir, "axterm.db");
|
|
4726
|
+
if (!fs11.existsSync(dbFile))
|
|
4343
4727
|
return { migrated: notes.length > 0, ...legacy ? { from: legacy } : {}, notes };
|
|
4344
4728
|
const store = new Store(dbFile);
|
|
4345
4729
|
const machine2 = store.getConfig("axterm_machine");
|
|
@@ -5177,13 +5561,13 @@ export default function axtermAgentStatus(pi: PiLike): void {\r
|
|
|
5177
5561
|
var init_axterm_agent_status = () => {};
|
|
5178
5562
|
|
|
5179
5563
|
// packages/daemon/src/ext-install.ts
|
|
5180
|
-
import
|
|
5181
|
-
import
|
|
5564
|
+
import fs12 from "fs";
|
|
5565
|
+
import path13 from "path";
|
|
5182
5566
|
function installExtensionAsset(targetDir) {
|
|
5183
|
-
|
|
5184
|
-
const target =
|
|
5567
|
+
fs12.mkdirSync(targetDir, { recursive: true });
|
|
5568
|
+
const target = path13.join(targetDir, "axterm-agent-status.mjs");
|
|
5185
5569
|
const js = new Bun.Transpiler({ loader: "ts" }).transformSync(extensionSource);
|
|
5186
|
-
|
|
5570
|
+
fs12.writeFileSync(target, MANAGED_HEADER + js);
|
|
5187
5571
|
return target;
|
|
5188
5572
|
}
|
|
5189
5573
|
var extensionSource, MANAGED_HEADER = `// @axterm-managed-omp-extension
|
|
@@ -5196,8 +5580,8 @@ var init_ext_install = __esm(() => {
|
|
|
5196
5580
|
|
|
5197
5581
|
// packages/daemon/src/workspace-local.ts
|
|
5198
5582
|
import { spawn as spawn2 } from "child_process";
|
|
5199
|
-
import
|
|
5200
|
-
import
|
|
5583
|
+
import fs13 from "fs/promises";
|
|
5584
|
+
import path14 from "path";
|
|
5201
5585
|
function isAbsent(error) {
|
|
5202
5586
|
const code = error?.code;
|
|
5203
5587
|
return code === "ENOENT" || code === "ENOTDIR";
|
|
@@ -5210,10 +5594,10 @@ class LocalWorkspace {
|
|
|
5210
5594
|
this.stagingDir = stagingDir;
|
|
5211
5595
|
}
|
|
5212
5596
|
async list(dir) {
|
|
5213
|
-
const dirents = await
|
|
5597
|
+
const dirents = await fs13.readdir(dir, { withFileTypes: true });
|
|
5214
5598
|
const stats = await Promise.all(dirents.map(async (dirent) => {
|
|
5215
5599
|
try {
|
|
5216
|
-
return await
|
|
5600
|
+
return await fs13.lstat(path14.join(dir, dirent.name));
|
|
5217
5601
|
} catch (error) {
|
|
5218
5602
|
if (isAbsent(error))
|
|
5219
5603
|
return;
|
|
@@ -5237,7 +5621,7 @@ class LocalWorkspace {
|
|
|
5237
5621
|
}
|
|
5238
5622
|
async stat(target) {
|
|
5239
5623
|
try {
|
|
5240
|
-
const stat = await
|
|
5624
|
+
const stat = await fs13.stat(target);
|
|
5241
5625
|
const kind = stat.isFile() ? "file" : stat.isDirectory() ? "dir" : "other";
|
|
5242
5626
|
return { kind, size: stat.size, mtimeMs: stat.mtimeMs };
|
|
5243
5627
|
} catch (error) {
|
|
@@ -5248,8 +5632,8 @@ class LocalWorkspace {
|
|
|
5248
5632
|
}
|
|
5249
5633
|
async read(file, limit) {
|
|
5250
5634
|
if (limit === undefined)
|
|
5251
|
-
return await
|
|
5252
|
-
const handle = await
|
|
5635
|
+
return await fs13.readFile(file);
|
|
5636
|
+
const handle = await fs13.open(file, "r");
|
|
5253
5637
|
try {
|
|
5254
5638
|
const buffer = Buffer.allocUnsafe(Math.min(limit, (await handle.stat()).size));
|
|
5255
5639
|
let filled = 0;
|
|
@@ -5268,10 +5652,10 @@ class LocalWorkspace {
|
|
|
5268
5652
|
return Promise.resolve(file);
|
|
5269
5653
|
}
|
|
5270
5654
|
async mkdirp(dir, mode) {
|
|
5271
|
-
await
|
|
5655
|
+
await fs13.mkdir(dir, mode === undefined ? { recursive: true } : { recursive: true, mode });
|
|
5272
5656
|
}
|
|
5273
5657
|
async write(file, bytes, mode) {
|
|
5274
|
-
await
|
|
5658
|
+
await fs13.writeFile(file, bytes, mode === undefined ? undefined : { mode });
|
|
5275
5659
|
}
|
|
5276
5660
|
exec(argv, cwd) {
|
|
5277
5661
|
const [command, ...args] = argv;
|
|
@@ -5300,9 +5684,9 @@ var init_workspace_local = () => {};
|
|
|
5300
5684
|
|
|
5301
5685
|
// packages/daemon/src/hook-host.ts
|
|
5302
5686
|
import { randomBytes } from "crypto";
|
|
5303
|
-
import
|
|
5687
|
+
import fs14 from "fs";
|
|
5304
5688
|
import http from "http";
|
|
5305
|
-
import
|
|
5689
|
+
import path15 from "path";
|
|
5306
5690
|
function header(req, name) {
|
|
5307
5691
|
const value = req.headers[name];
|
|
5308
5692
|
return Array.isArray(value) ? value[0] : value;
|
|
@@ -5322,12 +5706,12 @@ class HookHost {
|
|
|
5322
5706
|
askOpenHandler;
|
|
5323
5707
|
resolveWorkspace;
|
|
5324
5708
|
local;
|
|
5325
|
-
|
|
5326
|
-
|
|
5709
|
+
approvalHoldMs;
|
|
5710
|
+
approvalSeq = 0;
|
|
5327
5711
|
constructor(options) {
|
|
5328
5712
|
this.endpointFile = options.endpointFile;
|
|
5329
|
-
this.
|
|
5330
|
-
this.resolveWorkspace = options.workspaceFor ?? (() => this.local ??= new LocalWorkspace(
|
|
5713
|
+
this.approvalHoldMs = options.approvalHoldMs ?? APPROVAL_HOLD_MS;
|
|
5714
|
+
this.resolveWorkspace = options.workspaceFor ?? (() => this.local ??= new LocalWorkspace(path15.join(RUNTIME_DIR, "materialised")));
|
|
5331
5715
|
}
|
|
5332
5716
|
start() {
|
|
5333
5717
|
const server = http.createServer((req, res) => this.handle(req, res));
|
|
@@ -5343,12 +5727,12 @@ class HookHost {
|
|
|
5343
5727
|
return promise;
|
|
5344
5728
|
}
|
|
5345
5729
|
writeEndpointFile(port) {
|
|
5346
|
-
|
|
5730
|
+
fs14.mkdirSync(path15.dirname(this.endpointFile), { recursive: true });
|
|
5347
5731
|
const tmp = `${this.endpointFile}.tmp`;
|
|
5348
|
-
|
|
5732
|
+
fs14.writeFileSync(tmp, `AXTERM_HOOK_PORT=${port}
|
|
5349
5733
|
AXTERM_HOOK_TOKEN=${this.token}
|
|
5350
5734
|
`);
|
|
5351
|
-
|
|
5735
|
+
fs14.renameSync(tmp, this.endpointFile);
|
|
5352
5736
|
}
|
|
5353
5737
|
registerSession(sessionId, launchToken, onEvent) {
|
|
5354
5738
|
this.sessions.set(sessionId, { launchToken, onEvent });
|
|
@@ -5417,11 +5801,11 @@ AXTERM_HOOK_TOKEN=${this.token}
|
|
|
5417
5801
|
req.on("end", () => {
|
|
5418
5802
|
const raw = Buffer.concat(chunks).toString("utf8");
|
|
5419
5803
|
if (isClaude) {
|
|
5420
|
-
this.
|
|
5804
|
+
this.handleDecidingAgentHook(req, raw, res, "claude");
|
|
5421
5805
|
return;
|
|
5422
5806
|
}
|
|
5423
5807
|
if (isCodex) {
|
|
5424
|
-
this.
|
|
5808
|
+
this.handleDecidingAgentHook(req, raw, res, "codex");
|
|
5425
5809
|
return;
|
|
5426
5810
|
}
|
|
5427
5811
|
if (isGemini) {
|
|
@@ -5483,7 +5867,7 @@ AXTERM_HOOK_TOKEN=${this.token}
|
|
|
5483
5867
|
const events = withConversationId(map(input), { sessionId: named.session_id, subagentId: named.agent_id });
|
|
5484
5868
|
this.dispatchHookPayloads(auth.session, events, agent);
|
|
5485
5869
|
}
|
|
5486
|
-
|
|
5870
|
+
handleDecidingAgentHook(req, raw, res, agent) {
|
|
5487
5871
|
const auth = this.agentHookSession(req);
|
|
5488
5872
|
if (!auth) {
|
|
5489
5873
|
res.writeHead(200).end();
|
|
@@ -5496,20 +5880,27 @@ AXTERM_HOOK_TOKEN=${this.token}
|
|
|
5496
5880
|
res.writeHead(200).end();
|
|
5497
5881
|
return;
|
|
5498
5882
|
}
|
|
5883
|
+
const dispatch = () => {
|
|
5884
|
+
const events = withConversationId(mapClaudeHook(input), {
|
|
5885
|
+
sessionId: input.session_id,
|
|
5886
|
+
subagentId: input.agent_id
|
|
5887
|
+
});
|
|
5888
|
+
this.dispatchHookPayloads(auth.session, events, agent);
|
|
5889
|
+
};
|
|
5499
5890
|
if (input.hook_event_name !== "PermissionRequest") {
|
|
5500
5891
|
res.writeHead(200).end();
|
|
5501
|
-
|
|
5892
|
+
dispatch();
|
|
5502
5893
|
return;
|
|
5503
5894
|
}
|
|
5504
5895
|
const question = buildApprovalQuestion(input.tool_name);
|
|
5505
|
-
|
|
5506
|
-
const askId =
|
|
5896
|
+
dispatch();
|
|
5897
|
+
const askId = `${agent}-${auth.sessionId}-${this.approvalSeq++}`;
|
|
5507
5898
|
let fence;
|
|
5508
5899
|
const settle = (body) => {
|
|
5509
5900
|
clearTimeout(fence);
|
|
5510
5901
|
if (res.writableEnded)
|
|
5511
5902
|
return;
|
|
5512
|
-
const decision = body.status === "answered" ?
|
|
5903
|
+
const decision = body.status === "answered" ? hookPermissionDecision(question, body.answers, t("agent.approval.denied")) : undefined;
|
|
5513
5904
|
if (decision === undefined) {
|
|
5514
5905
|
res.writeHead(200).end();
|
|
5515
5906
|
return;
|
|
@@ -5530,7 +5921,7 @@ AXTERM_HOOK_TOKEN=${this.token}
|
|
|
5530
5921
|
return;
|
|
5531
5922
|
this.pendingAsks.delete(askId);
|
|
5532
5923
|
settle({ status: "cancelled" });
|
|
5533
|
-
}, this.
|
|
5924
|
+
}, this.approvalHoldMs);
|
|
5534
5925
|
fence.unref?.();
|
|
5535
5926
|
}
|
|
5536
5927
|
agentHookSession(req) {
|
|
@@ -5634,8 +6025,112 @@ var init_hook_host = __esm(() => {
|
|
|
5634
6025
|
MAX_BODY_BYTES = 256 * 1024;
|
|
5635
6026
|
});
|
|
5636
6027
|
|
|
6028
|
+
// packages/daemon/src/process-guards.ts
|
|
6029
|
+
class RejectionLogLimiter {
|
|
6030
|
+
detailLimit;
|
|
6031
|
+
windowMs;
|
|
6032
|
+
windowStartedAt;
|
|
6033
|
+
details = 0;
|
|
6034
|
+
noticeEmitted = false;
|
|
6035
|
+
constructor(detailLimit = DEFAULT_REJECTION_DETAILS, windowMs = DEFAULT_REJECTION_WINDOW_MS) {
|
|
6036
|
+
this.detailLimit = detailLimit;
|
|
6037
|
+
this.windowMs = windowMs;
|
|
6038
|
+
if (!Number.isInteger(detailLimit) || detailLimit < 1)
|
|
6039
|
+
throw new RangeError("detailLimit must be a positive integer");
|
|
6040
|
+
if (!Number.isFinite(windowMs) || windowMs <= 0)
|
|
6041
|
+
throw new RangeError("windowMs must be positive");
|
|
6042
|
+
}
|
|
6043
|
+
decide(now) {
|
|
6044
|
+
if (this.windowStartedAt === undefined || now - this.windowStartedAt >= this.windowMs || now < this.windowStartedAt) {
|
|
6045
|
+
this.windowStartedAt = now;
|
|
6046
|
+
this.details = 0;
|
|
6047
|
+
this.noticeEmitted = false;
|
|
6048
|
+
}
|
|
6049
|
+
if (this.details < this.detailLimit) {
|
|
6050
|
+
this.details++;
|
|
6051
|
+
return "detail";
|
|
6052
|
+
}
|
|
6053
|
+
if (!this.noticeEmitted) {
|
|
6054
|
+
this.noticeEmitted = true;
|
|
6055
|
+
return "notice";
|
|
6056
|
+
}
|
|
6057
|
+
return "drop";
|
|
6058
|
+
}
|
|
6059
|
+
}
|
|
6060
|
+
function thrownText(reason) {
|
|
6061
|
+
let text;
|
|
6062
|
+
if (reason instanceof Error) {
|
|
6063
|
+
text = reason.stack ?? reason.message;
|
|
6064
|
+
} else if (typeof reason === "string") {
|
|
6065
|
+
text = reason;
|
|
6066
|
+
} else {
|
|
6067
|
+
try {
|
|
6068
|
+
text = JSON.stringify(reason) ?? String(reason);
|
|
6069
|
+
} catch {
|
|
6070
|
+
text = String(reason);
|
|
6071
|
+
}
|
|
6072
|
+
}
|
|
6073
|
+
if (text.length <= MAX_BREADCRUMB_CHARS)
|
|
6074
|
+
return text;
|
|
6075
|
+
return `${text.slice(0, MAX_BREADCRUMB_CHARS)}\u2026 [truncated]`;
|
|
6076
|
+
}
|
|
6077
|
+
|
|
6078
|
+
class ProcessCrashGuards {
|
|
6079
|
+
options;
|
|
6080
|
+
limiter;
|
|
6081
|
+
now;
|
|
6082
|
+
fatal = false;
|
|
6083
|
+
uninstallCurrent;
|
|
6084
|
+
constructor(options) {
|
|
6085
|
+
this.options = options;
|
|
6086
|
+
this.now = options.now ?? Date.now;
|
|
6087
|
+
this.limiter = new RejectionLogLimiter(options.rejectionDetailsPerWindow, options.rejectionWindowMs);
|
|
6088
|
+
}
|
|
6089
|
+
install(events) {
|
|
6090
|
+
if (this.uninstallCurrent !== undefined)
|
|
6091
|
+
throw new Error("process crash guards are already installed");
|
|
6092
|
+
const uncaught = (reason) => this.fatalExit("uncaughtException", reason);
|
|
6093
|
+
const rejected = (reason) => this.unhandledRejection(reason);
|
|
6094
|
+
events.on("uncaughtException", uncaught);
|
|
6095
|
+
events.on("unhandledRejection", rejected);
|
|
6096
|
+
let installed = true;
|
|
6097
|
+
const uninstall = () => {
|
|
6098
|
+
if (!installed)
|
|
6099
|
+
return;
|
|
6100
|
+
installed = false;
|
|
6101
|
+
events.off("uncaughtException", uncaught);
|
|
6102
|
+
events.off("unhandledRejection", rejected);
|
|
6103
|
+
if (this.uninstallCurrent === uninstall)
|
|
6104
|
+
this.uninstallCurrent = undefined;
|
|
6105
|
+
};
|
|
6106
|
+
this.uninstallCurrent = uninstall;
|
|
6107
|
+
return uninstall;
|
|
6108
|
+
}
|
|
6109
|
+
startupFailed(reason) {
|
|
6110
|
+
this.fatalExit("startupFailure", reason);
|
|
6111
|
+
}
|
|
6112
|
+
unhandledRejection(reason) {
|
|
6113
|
+
if (this.fatal)
|
|
6114
|
+
return;
|
|
6115
|
+
const decision = this.limiter.decide(this.now());
|
|
6116
|
+
if (decision === "detail") {
|
|
6117
|
+
this.options.log(`unhandledRejection: ${thrownText(reason)}`);
|
|
6118
|
+
} else if (decision === "notice") {
|
|
6119
|
+
this.options.log("unhandledRejection: suppressing further unhandled rejections in this rate-limit window");
|
|
6120
|
+
}
|
|
6121
|
+
}
|
|
6122
|
+
fatalExit(origin, reason) {
|
|
6123
|
+
if (this.fatal)
|
|
6124
|
+
return;
|
|
6125
|
+
this.fatal = true;
|
|
6126
|
+
this.options.log(`${origin}: ${thrownText(reason)}`);
|
|
6127
|
+
this.options.exit(1);
|
|
6128
|
+
}
|
|
6129
|
+
}
|
|
6130
|
+
var DEFAULT_REJECTION_DETAILS = 20, DEFAULT_REJECTION_WINDOW_MS = 60000, MAX_BREADCRUMB_CHARS = 16384;
|
|
6131
|
+
|
|
5637
6132
|
// packages/daemon/src/rpc-server.ts
|
|
5638
|
-
import
|
|
6133
|
+
import fs15 from "fs";
|
|
5639
6134
|
import net2 from "net";
|
|
5640
6135
|
function writeFrame(socket, frame) {
|
|
5641
6136
|
if (!socket.destroyed)
|
|
@@ -5873,7 +6368,7 @@ function startRpcServer(options) {
|
|
|
5873
6368
|
});
|
|
5874
6369
|
if (process.platform !== "win32") {
|
|
5875
6370
|
try {
|
|
5876
|
-
|
|
6371
|
+
fs15.unlinkSync(pipePath);
|
|
5877
6372
|
} catch {}
|
|
5878
6373
|
}
|
|
5879
6374
|
server.listen(pipePath, () => {
|
|
@@ -5888,21 +6383,21 @@ var init_rpc_server = __esm(() => {
|
|
|
5888
6383
|
});
|
|
5889
6384
|
|
|
5890
6385
|
// packages/daemon/src/codex-home.ts
|
|
5891
|
-
import
|
|
5892
|
-
import
|
|
6386
|
+
import fs16 from "fs";
|
|
6387
|
+
import path16 from "path";
|
|
5893
6388
|
function codexTrustKeyPath(home) {
|
|
5894
6389
|
const resolved = (() => {
|
|
5895
6390
|
try {
|
|
5896
|
-
return
|
|
6391
|
+
return fs16.realpathSync.native(home);
|
|
5897
6392
|
} catch {
|
|
5898
|
-
return
|
|
6393
|
+
return path16.resolve(home);
|
|
5899
6394
|
}
|
|
5900
6395
|
})();
|
|
5901
|
-
return
|
|
6396
|
+
return path16.join(resolved, "hooks.json");
|
|
5902
6397
|
}
|
|
5903
6398
|
function referenceCredential(source, target, ops) {
|
|
5904
|
-
const symlink = ops?.symlink ?? ((s, t2) =>
|
|
5905
|
-
const link = ops?.link ??
|
|
6399
|
+
const symlink = ops?.symlink ?? ((s, t2) => fs16.symlinkSync(s, t2, "file"));
|
|
6400
|
+
const link = ops?.link ?? fs16.linkSync;
|
|
5906
6401
|
try {
|
|
5907
6402
|
symlink(source, target);
|
|
5908
6403
|
return "symlink";
|
|
@@ -5911,8 +6406,8 @@ function referenceCredential(source, target, ops) {
|
|
|
5911
6406
|
link(source, target);
|
|
5912
6407
|
return "hardlink";
|
|
5913
6408
|
} catch {
|
|
5914
|
-
|
|
5915
|
-
|
|
6409
|
+
fs16.copyFileSync(source, target);
|
|
6410
|
+
fs16.chmodSync(target, 384);
|
|
5916
6411
|
return "copy";
|
|
5917
6412
|
}
|
|
5918
6413
|
}
|
|
@@ -5999,8 +6494,8 @@ function slugifyBranch(branch) {
|
|
|
5999
6494
|
var MAX_SLUG_LENGTH = 60;
|
|
6000
6495
|
|
|
6001
6496
|
// packages/daemon/src/git.ts
|
|
6002
|
-
import
|
|
6003
|
-
import
|
|
6497
|
+
import fs17 from "fs";
|
|
6498
|
+
import path17 from "path";
|
|
6004
6499
|
async function git(ws, cwd, args) {
|
|
6005
6500
|
const run = await ws.exec(["git", ...args], cwd);
|
|
6006
6501
|
return {
|
|
@@ -6017,15 +6512,15 @@ async function gitOrThrow(ws, cwd, args) {
|
|
|
6017
6512
|
return result.stdout;
|
|
6018
6513
|
}
|
|
6019
6514
|
function canonicalPath(target) {
|
|
6020
|
-
let resolved =
|
|
6515
|
+
let resolved = path17.resolve(target);
|
|
6021
6516
|
try {
|
|
6022
|
-
resolved =
|
|
6517
|
+
resolved = fs17.realpathSync.native(resolved);
|
|
6023
6518
|
} catch {}
|
|
6024
6519
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
6025
6520
|
}
|
|
6026
6521
|
async function resolveRepoRoot(ws, cwd) {
|
|
6027
6522
|
const result = await git(ws, cwd, ["rev-parse", "--show-toplevel"]);
|
|
6028
|
-
return result.ok ?
|
|
6523
|
+
return result.ok ? path17.resolve(result.stdout) : undefined;
|
|
6029
6524
|
}
|
|
6030
6525
|
async function currentBranch(ws, cwd) {
|
|
6031
6526
|
const result = await git(ws, cwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
@@ -6034,8 +6529,8 @@ async function currentBranch(ws, cwd) {
|
|
|
6034
6529
|
async function listWorktrees(ws, repoRoot) {
|
|
6035
6530
|
return parseWorktreeList(await gitOrThrow(ws, repoRoot, ["worktree", "list", "--porcelain"]));
|
|
6036
6531
|
}
|
|
6037
|
-
function worktreePathFor(repoRoot, branch, root =
|
|
6038
|
-
return
|
|
6532
|
+
function worktreePathFor(repoRoot, branch, root = path17.join(STATE_DIR, "worktrees")) {
|
|
6533
|
+
return path17.join(root, slugifyBranch(path17.basename(repoRoot)), slugifyBranch(branch));
|
|
6039
6534
|
}
|
|
6040
6535
|
async function createWorktree(ws, options) {
|
|
6041
6536
|
const { repoRoot, branch, base } = options;
|
|
@@ -6043,7 +6538,7 @@ async function createWorktree(ws, options) {
|
|
|
6043
6538
|
const existing = (await listWorktrees(ws, repoRoot)).find((w) => canonicalPath(w.path) === canonicalPath(target));
|
|
6044
6539
|
if (existing)
|
|
6045
6540
|
return { path: target, branch, createdBranch: false };
|
|
6046
|
-
|
|
6541
|
+
fs17.mkdirSync(path17.dirname(target), { recursive: true });
|
|
6047
6542
|
const exists = (await git(ws, repoRoot, ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`])).ok;
|
|
6048
6543
|
const args = exists ? ["worktree", "add", target, branch] : ["worktree", "add", "-b", branch, target, ...base !== undefined ? [base] : []];
|
|
6049
6544
|
await gitOrThrow(ws, repoRoot, args);
|
|
@@ -6104,7 +6599,7 @@ async function diffStat(ws, worktreePath, base) {
|
|
|
6104
6599
|
const untracked = await untrackedFiles(ws, worktreePath);
|
|
6105
6600
|
let newLines = 0;
|
|
6106
6601
|
for (const file of untracked)
|
|
6107
|
-
newLines += await countLines(ws,
|
|
6602
|
+
newLines += await countLines(ws, path17.join(worktreePath, file));
|
|
6108
6603
|
return {
|
|
6109
6604
|
filesChanged: a.filesChanged + b.filesChanged + untracked.length,
|
|
6110
6605
|
insertions: a.insertions + b.insertions + newLines,
|
|
@@ -6127,21 +6622,84 @@ async function changedFiles(ws, worktreePath, base, limit = 20) {
|
|
|
6127
6622
|
names.add(file);
|
|
6128
6623
|
return [...names].slice(0, limit);
|
|
6129
6624
|
}
|
|
6130
|
-
|
|
6625
|
+
async function diffPatch(ws, worktreePath, base, maxChars = 64 * 1024) {
|
|
6626
|
+
const parts = [];
|
|
6627
|
+
let size = 0;
|
|
6628
|
+
const push = (text) => {
|
|
6629
|
+
if (size >= maxChars)
|
|
6630
|
+
return false;
|
|
6631
|
+
const room = maxChars - size;
|
|
6632
|
+
const piece = text.length > room ? text.slice(0, room) : text;
|
|
6633
|
+
parts.push(piece);
|
|
6634
|
+
size += piece.length;
|
|
6635
|
+
return piece.length === text.length;
|
|
6636
|
+
};
|
|
6637
|
+
for (const args of [
|
|
6638
|
+
["diff", `${base}...HEAD`, "--", "."],
|
|
6639
|
+
["diff", "HEAD"]
|
|
6640
|
+
]) {
|
|
6641
|
+
const out = await git(ws, worktreePath, args);
|
|
6642
|
+
if (out.ok && out.stdout.trim().length > 0 && !push(`${out.stdout.replace(/\s+$/, "")}
|
|
6643
|
+
`)) {
|
|
6644
|
+
return parts.join("");
|
|
6645
|
+
}
|
|
6646
|
+
}
|
|
6647
|
+
for (const file of await untrackedFiles(ws, worktreePath)) {
|
|
6648
|
+
if (size >= maxChars)
|
|
6649
|
+
break;
|
|
6650
|
+
let text;
|
|
6651
|
+
try {
|
|
6652
|
+
const stat = await ws.stat(path17.join(worktreePath, file));
|
|
6653
|
+
if (stat?.kind !== "file" || stat.size > UNTRACKED_LINE_SCAN_LIMIT)
|
|
6654
|
+
continue;
|
|
6655
|
+
text = new TextDecoder().decode(await ws.read(path17.join(worktreePath, file)));
|
|
6656
|
+
} catch {
|
|
6657
|
+
continue;
|
|
6658
|
+
}
|
|
6659
|
+
if (text.slice(0, 8000).includes("\x00")) {
|
|
6660
|
+
if (!push(`diff --git a/${file} b/${file}
|
|
6661
|
+
Binary files /dev/null and b/${file} differ
|
|
6662
|
+
`))
|
|
6663
|
+
break;
|
|
6664
|
+
continue;
|
|
6665
|
+
}
|
|
6666
|
+
const lines = text.split(`
|
|
6667
|
+
`);
|
|
6668
|
+
if (lines.at(-1) === "")
|
|
6669
|
+
lines.pop();
|
|
6670
|
+
const shown = lines.slice(0, UNTRACKED_PATCH_LINES);
|
|
6671
|
+
const body = shown.map((line) => `+${line}`).join(`
|
|
6672
|
+
`);
|
|
6673
|
+
const elided = lines.length > shown.length ? `
|
|
6674
|
+
+\u2026 ${lines.length - shown.length} more lines
|
|
6675
|
+
` : `
|
|
6676
|
+
`;
|
|
6677
|
+
const header2 = `diff --git a/${file} b/${file}
|
|
6678
|
+
new file mode 100644
|
|
6679
|
+
--- /dev/null
|
|
6680
|
+
+++ b/${file}
|
|
6681
|
+
@@ -0,0 +1,${lines.length} @@
|
|
6682
|
+
`;
|
|
6683
|
+
if (!push(`${header2}${body}${elided}`))
|
|
6684
|
+
break;
|
|
6685
|
+
}
|
|
6686
|
+
return parts.join("");
|
|
6687
|
+
}
|
|
6688
|
+
var UNTRACKED_LINE_SCAN_LIMIT, UNTRACKED_PATCH_LINES = 40;
|
|
6131
6689
|
var init_git = __esm(() => {
|
|
6132
6690
|
init_paths();
|
|
6133
6691
|
UNTRACKED_LINE_SCAN_LIMIT = 1024 * 1024;
|
|
6134
6692
|
});
|
|
6135
6693
|
|
|
6136
6694
|
// packages/daemon/src/claude-store.ts
|
|
6137
|
-
import
|
|
6138
|
-
import
|
|
6139
|
-
import
|
|
6695
|
+
import fs18 from "fs";
|
|
6696
|
+
import os5 from "os";
|
|
6697
|
+
import path18 from "path";
|
|
6140
6698
|
function claudeConfigDir(env = process.env) {
|
|
6141
6699
|
const override = env["CLAUDE_CONFIG_DIR"];
|
|
6142
6700
|
if (override !== undefined && override.length > 0)
|
|
6143
6701
|
return override;
|
|
6144
|
-
return
|
|
6702
|
+
return path18.join(os5.homedir(), ".claude");
|
|
6145
6703
|
}
|
|
6146
6704
|
function resumedConversationId(argv) {
|
|
6147
6705
|
for (let i = 0;i < argv.length; i++) {
|
|
@@ -6157,41 +6715,41 @@ function resumedConversationId(argv) {
|
|
|
6157
6715
|
return;
|
|
6158
6716
|
}
|
|
6159
6717
|
function claudeConversationExists(configDir, conversationId) {
|
|
6160
|
-
const projects =
|
|
6718
|
+
const projects = path18.join(configDir, "projects");
|
|
6161
6719
|
let entries;
|
|
6162
6720
|
try {
|
|
6163
|
-
entries =
|
|
6721
|
+
entries = fs18.readdirSync(projects);
|
|
6164
6722
|
} catch {
|
|
6165
6723
|
return;
|
|
6166
6724
|
}
|
|
6167
6725
|
const file = `${conversationId}.jsonl`;
|
|
6168
6726
|
for (const entry of entries) {
|
|
6169
6727
|
try {
|
|
6170
|
-
if (
|
|
6728
|
+
if (fs18.existsSync(path18.join(projects, entry, file)))
|
|
6171
6729
|
return true;
|
|
6172
6730
|
} catch {}
|
|
6173
6731
|
}
|
|
6174
6732
|
return false;
|
|
6175
6733
|
}
|
|
6176
6734
|
function claudeConversationCwd(configDir, conversationId) {
|
|
6177
|
-
const projects =
|
|
6735
|
+
const projects = path18.join(configDir, "projects");
|
|
6178
6736
|
let entries;
|
|
6179
6737
|
try {
|
|
6180
|
-
entries =
|
|
6738
|
+
entries = fs18.readdirSync(projects);
|
|
6181
6739
|
} catch {
|
|
6182
6740
|
return;
|
|
6183
6741
|
}
|
|
6184
6742
|
for (const entry of entries) {
|
|
6185
|
-
const file =
|
|
6743
|
+
const file = path18.join(projects, entry, `${conversationId}.jsonl`);
|
|
6186
6744
|
let head;
|
|
6187
6745
|
try {
|
|
6188
|
-
const handle =
|
|
6746
|
+
const handle = fs18.openSync(file, "r");
|
|
6189
6747
|
try {
|
|
6190
6748
|
const buffer = Buffer.alloc(64 * 1024);
|
|
6191
|
-
const read =
|
|
6749
|
+
const read = fs18.readSync(handle, buffer, 0, buffer.byteLength, 0);
|
|
6192
6750
|
head = buffer.subarray(0, read).toString("utf8");
|
|
6193
6751
|
} finally {
|
|
6194
|
-
|
|
6752
|
+
fs18.closeSync(handle);
|
|
6195
6753
|
}
|
|
6196
6754
|
} catch {
|
|
6197
6755
|
continue;
|
|
@@ -6215,7 +6773,7 @@ var init_claude_store = __esm(() => {
|
|
|
6215
6773
|
});
|
|
6216
6774
|
|
|
6217
6775
|
// packages/daemon/src/injector.ts
|
|
6218
|
-
import { setTimeout as
|
|
6776
|
+
import { setTimeout as delay3 } from "timers/promises";
|
|
6219
6777
|
|
|
6220
6778
|
class Injector {
|
|
6221
6779
|
queues = new Map;
|
|
@@ -6230,14 +6788,14 @@ class Injector {
|
|
|
6230
6788
|
try {
|
|
6231
6789
|
const deadline = Date.now() + 5000;
|
|
6232
6790
|
while (!session.pasteReady && !session.isExited && Date.now() < deadline) {
|
|
6233
|
-
await
|
|
6791
|
+
await delay3(100);
|
|
6234
6792
|
}
|
|
6235
6793
|
session.write(CTRL_U);
|
|
6236
|
-
await
|
|
6794
|
+
await delay3(CLEAR_SETTLE_MS);
|
|
6237
6795
|
for (const chunk of iteratePasteChunks(text)) {
|
|
6238
6796
|
session.write(chunk);
|
|
6239
6797
|
}
|
|
6240
|
-
await
|
|
6798
|
+
await delay3(SUBMIT_DELAY_MS);
|
|
6241
6799
|
session.write(SUBMIT);
|
|
6242
6800
|
} catch (err) {
|
|
6243
6801
|
session.write(BRACKETED_PASTE_END);
|
|
@@ -6249,7 +6807,7 @@ class Injector {
|
|
|
6249
6807
|
interrupt(session) {
|
|
6250
6808
|
return this.enqueue(session.info.id, async () => {
|
|
6251
6809
|
session.write(ESC);
|
|
6252
|
-
await
|
|
6810
|
+
await delay3(500);
|
|
6253
6811
|
});
|
|
6254
6812
|
}
|
|
6255
6813
|
}
|
|
@@ -6459,18 +7017,18 @@ var init_pty_host_client = __esm(() => {
|
|
|
6459
7017
|
|
|
6460
7018
|
// packages/daemon/src/pty-host-launch.ts
|
|
6461
7019
|
import { spawn as spawn3 } from "child_process";
|
|
6462
|
-
import
|
|
6463
|
-
import
|
|
6464
|
-
import
|
|
7020
|
+
import fs19 from "fs";
|
|
7021
|
+
import os6 from "os";
|
|
7022
|
+
import path19 from "path";
|
|
6465
7023
|
function hostFile(runtimeDir) {
|
|
6466
|
-
return
|
|
7024
|
+
return path19.join(runtimeDir, "pty-host.json");
|
|
6467
7025
|
}
|
|
6468
7026
|
function readPtyHostIn(runtimeDir) {
|
|
6469
7027
|
return readPtyHostFile(hostFile(runtimeDir));
|
|
6470
7028
|
}
|
|
6471
7029
|
function readPtyHostFile(file) {
|
|
6472
7030
|
try {
|
|
6473
|
-
const parsed = JSON.parse(
|
|
7031
|
+
const parsed = JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
6474
7032
|
if (typeof parsed !== "object" || parsed === null)
|
|
6475
7033
|
return;
|
|
6476
7034
|
const { pid, endpoint, token, version: version2 } = parsed;
|
|
@@ -6490,8 +7048,8 @@ function isRunning(pid) {
|
|
|
6490
7048
|
}
|
|
6491
7049
|
}
|
|
6492
7050
|
function hostEndpoint(runtimeDir) {
|
|
6493
|
-
const base = ipcEndpoint(process.platform,
|
|
6494
|
-
return process.platform === "win32" ? `${base}-pty` :
|
|
7051
|
+
const base = ipcEndpoint(process.platform, os6.userInfo().username, { runtimeDir });
|
|
7052
|
+
return process.platform === "win32" ? `${base}-pty` : path19.join(runtimeDir, "axterm-pty.sock");
|
|
6495
7053
|
}
|
|
6496
7054
|
async function ensurePtyHost(location) {
|
|
6497
7055
|
const file = hostFile(location.runtimeDir);
|
|
@@ -6502,7 +7060,7 @@ async function ensurePtyHost(location) {
|
|
|
6502
7060
|
log(`pty host: handoff file names a dead pid ${existing.pid}, starting a new host`);
|
|
6503
7061
|
const endpoint = hostEndpoint(location.runtimeDir);
|
|
6504
7062
|
if (process.platform !== "win32")
|
|
6505
|
-
|
|
7063
|
+
fs19.rmSync(endpoint, { force: true });
|
|
6506
7064
|
const token = shortHash(`${location.runtimeDir}:${Date.now()}:${Math.random()}`) + shortHash(String(process.pid));
|
|
6507
7065
|
const launch = ptyHostLaunch({ execPath: process.execPath, mainScript: location.mainScript ?? Bun.main });
|
|
6508
7066
|
const child = spawn3(launch.command, launch.args, {
|
|
@@ -6531,10 +7089,41 @@ async function ensurePtyHost(location) {
|
|
|
6531
7089
|
}
|
|
6532
7090
|
}
|
|
6533
7091
|
function writePtyHostFile(file, info) {
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
7092
|
+
const dir = path19.dirname(file);
|
|
7093
|
+
const tmp = `${file}.tmp`;
|
|
7094
|
+
fs19.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
7095
|
+
let handle;
|
|
7096
|
+
try {
|
|
7097
|
+
handle = fs19.openSync(tmp, "w", 384);
|
|
7098
|
+
fs19.writeFileSync(handle, `${JSON.stringify(info, null, 2)}
|
|
7099
|
+
`, "utf8");
|
|
7100
|
+
fs19.fsyncSync(handle);
|
|
7101
|
+
fs19.closeSync(handle);
|
|
7102
|
+
handle = undefined;
|
|
7103
|
+
restrictFile(tmp);
|
|
7104
|
+
fs19.renameSync(tmp, file);
|
|
7105
|
+
restrictFile(file);
|
|
7106
|
+
if (process.platform !== "win32") {
|
|
7107
|
+
try {
|
|
7108
|
+
const directory = fs19.openSync(dir, "r");
|
|
7109
|
+
try {
|
|
7110
|
+
fs19.fsyncSync(directory);
|
|
7111
|
+
} finally {
|
|
7112
|
+
fs19.closeSync(directory);
|
|
7113
|
+
}
|
|
7114
|
+
} catch {}
|
|
7115
|
+
}
|
|
7116
|
+
} catch (err) {
|
|
7117
|
+
if (handle !== undefined) {
|
|
7118
|
+
try {
|
|
7119
|
+
fs19.closeSync(handle);
|
|
7120
|
+
} catch {}
|
|
7121
|
+
}
|
|
7122
|
+
try {
|
|
7123
|
+
fs19.rmSync(tmp, { force: true });
|
|
7124
|
+
} catch {}
|
|
7125
|
+
throw err;
|
|
7126
|
+
}
|
|
6538
7127
|
}
|
|
6539
7128
|
var init_pty_host_launch = __esm(() => {
|
|
6540
7129
|
init_src();
|
|
@@ -6543,8 +7132,8 @@ var init_pty_host_launch = __esm(() => {
|
|
|
6543
7132
|
});
|
|
6544
7133
|
|
|
6545
7134
|
// packages/daemon/src/pty.ts
|
|
6546
|
-
import { spawnSync as
|
|
6547
|
-
import
|
|
7135
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
7136
|
+
import path20 from "path";
|
|
6548
7137
|
import { StringDecoder as StringDecoder2 } from "string_decoder";
|
|
6549
7138
|
|
|
6550
7139
|
class Fanout {
|
|
@@ -6565,13 +7154,13 @@ class Fanout {
|
|
|
6565
7154
|
}
|
|
6566
7155
|
function killTree(pid, exeName) {
|
|
6567
7156
|
if (process.platform === "win32") {
|
|
6568
|
-
const check =
|
|
7157
|
+
const check = spawnSync3("tasklist", ["/FI", `PID eq ${pid}`, "/NH", "/FO", "CSV"], {
|
|
6569
7158
|
encoding: "utf8",
|
|
6570
7159
|
windowsHide: true
|
|
6571
7160
|
});
|
|
6572
7161
|
const image = check.stdout?.split(",")[0]?.replace(/"/g, "").toLowerCase() ?? "";
|
|
6573
7162
|
if (image === exeName) {
|
|
6574
|
-
|
|
7163
|
+
spawnSync3("taskkill", ["/PID", String(pid), "/T", "/F"], { encoding: "utf8", windowsHide: true });
|
|
6575
7164
|
}
|
|
6576
7165
|
return;
|
|
6577
7166
|
}
|
|
@@ -6644,7 +7233,7 @@ function spawnPty(options) {
|
|
|
6644
7233
|
}
|
|
6645
7234
|
}
|
|
6646
7235
|
});
|
|
6647
|
-
handle = new BunPtyHandle(proc,
|
|
7236
|
+
handle = new BunPtyHandle(proc, path20.basename(options.exe).toLowerCase());
|
|
6648
7237
|
proc.exited.then((code) => handle?.finish(code ?? null));
|
|
6649
7238
|
return handle;
|
|
6650
7239
|
}
|
|
@@ -6652,7 +7241,7 @@ var init_pty = () => {};
|
|
|
6652
7241
|
|
|
6653
7242
|
// packages/daemon/src/occupancy.ts
|
|
6654
7243
|
import { spawn as spawn4 } from "child_process";
|
|
6655
|
-
import
|
|
7244
|
+
import fs20 from "fs/promises";
|
|
6656
7245
|
function shellOccupied(shellPid) {
|
|
6657
7246
|
return process.platform === "win32" ? occupiedFromSnapshot(shellPid) : occupiedFromPidProbe(shellPid);
|
|
6658
7247
|
}
|
|
@@ -6697,11 +7286,11 @@ async function occupiedFromSnapshot(shellPid) {
|
|
|
6697
7286
|
}
|
|
6698
7287
|
async function childrenViaProc(pid) {
|
|
6699
7288
|
try {
|
|
6700
|
-
const children = await
|
|
7289
|
+
const children = await fs20.readFile(`/proc/${pid}/task/${pid}/children`, "utf8");
|
|
6701
7290
|
return children.trim().length > 0;
|
|
6702
7291
|
} catch {
|
|
6703
7292
|
try {
|
|
6704
|
-
await
|
|
7293
|
+
await fs20.access(`/proc/${pid}`);
|
|
6705
7294
|
return;
|
|
6706
7295
|
} catch {
|
|
6707
7296
|
return false;
|
|
@@ -6765,20 +7354,20 @@ var init_occupancy = __esm(() => {
|
|
|
6765
7354
|
});
|
|
6766
7355
|
|
|
6767
7356
|
// packages/daemon/src/agent-store.ts
|
|
6768
|
-
import
|
|
6769
|
-
import
|
|
6770
|
-
import
|
|
7357
|
+
import fs21 from "fs";
|
|
7358
|
+
import os7 from "os";
|
|
7359
|
+
import path21 from "path";
|
|
6771
7360
|
import { Database as Database3 } from "bun:sqlite";
|
|
6772
7361
|
function readTailLines(file, maxBytes) {
|
|
6773
7362
|
let fd;
|
|
6774
7363
|
try {
|
|
6775
|
-
fd =
|
|
6776
|
-
const size =
|
|
7364
|
+
fd = fs21.openSync(file, "r");
|
|
7365
|
+
const size = fs21.fstatSync(fd).size;
|
|
6777
7366
|
if (size === 0)
|
|
6778
7367
|
return [];
|
|
6779
7368
|
const length = Math.min(size, maxBytes);
|
|
6780
7369
|
const buffer = Buffer.allocUnsafe(length);
|
|
6781
|
-
|
|
7370
|
+
fs21.readSync(fd, buffer, 0, length, size - length);
|
|
6782
7371
|
const text = buffer.toString("utf8");
|
|
6783
7372
|
const lines = text.split(`
|
|
6784
7373
|
`);
|
|
@@ -6791,46 +7380,46 @@ function readTailLines(file, maxBytes) {
|
|
|
6791
7380
|
return [];
|
|
6792
7381
|
} finally {
|
|
6793
7382
|
if (fd !== undefined)
|
|
6794
|
-
|
|
7383
|
+
fs21.closeSync(fd);
|
|
6795
7384
|
}
|
|
6796
7385
|
}
|
|
6797
7386
|
function claudeConfigDir2() {
|
|
6798
|
-
return process.env["CLAUDE_CONFIG_DIR"] ??
|
|
7387
|
+
return process.env["CLAUDE_CONFIG_DIR"] ?? path21.join(os7.homedir(), ".claude");
|
|
6799
7388
|
}
|
|
6800
7389
|
function codexHome() {
|
|
6801
|
-
return
|
|
7390
|
+
return path21.join(STATE_DIR, "codex-home");
|
|
6802
7391
|
}
|
|
6803
7392
|
function ompAgentDb() {
|
|
6804
|
-
return
|
|
7393
|
+
return path21.join(os7.homedir(), ".omp", "agent", "agent.db");
|
|
6805
7394
|
}
|
|
6806
7395
|
function findCodexRollout(sessionId) {
|
|
6807
|
-
const root =
|
|
7396
|
+
const root = path21.join(codexHome(), "sessions");
|
|
6808
7397
|
for (const shard2 of codexRolloutShards(sessionId)) {
|
|
6809
|
-
const dir =
|
|
7398
|
+
const dir = path21.join(root, ...shard2.split("/"));
|
|
6810
7399
|
let entries;
|
|
6811
7400
|
try {
|
|
6812
|
-
entries =
|
|
7401
|
+
entries = fs21.readdirSync(dir);
|
|
6813
7402
|
} catch {
|
|
6814
7403
|
continue;
|
|
6815
7404
|
}
|
|
6816
7405
|
const match2 = entries.find((name) => name.endsWith(`-${sessionId}.jsonl`));
|
|
6817
7406
|
if (match2 !== undefined)
|
|
6818
|
-
return
|
|
7407
|
+
return path21.join(dir, match2);
|
|
6819
7408
|
}
|
|
6820
7409
|
return;
|
|
6821
7410
|
}
|
|
6822
7411
|
function newestCodexRollout() {
|
|
6823
|
-
const root =
|
|
7412
|
+
const root = path21.join(codexHome(), "sessions");
|
|
6824
7413
|
let best;
|
|
6825
7414
|
const walk = (dir, depth) => {
|
|
6826
7415
|
let entries;
|
|
6827
7416
|
try {
|
|
6828
|
-
entries =
|
|
7417
|
+
entries = fs21.readdirSync(dir, { withFileTypes: true });
|
|
6829
7418
|
} catch {
|
|
6830
7419
|
return;
|
|
6831
7420
|
}
|
|
6832
7421
|
for (const entry of entries) {
|
|
6833
|
-
const full =
|
|
7422
|
+
const full = path21.join(dir, entry.name);
|
|
6834
7423
|
if (entry.isDirectory()) {
|
|
6835
7424
|
if (depth < 3)
|
|
6836
7425
|
walk(full, depth + 1);
|
|
@@ -6839,7 +7428,7 @@ function newestCodexRollout() {
|
|
|
6839
7428
|
if (!entry.name.endsWith(".jsonl"))
|
|
6840
7429
|
continue;
|
|
6841
7430
|
try {
|
|
6842
|
-
const { mtimeMs } =
|
|
7431
|
+
const { mtimeMs } = fs21.statSync(full);
|
|
6843
7432
|
if (!best || mtimeMs > best.mtimeMs)
|
|
6844
7433
|
best = { file: full, mtimeMs };
|
|
6845
7434
|
} catch {}
|
|
@@ -6850,7 +7439,7 @@ function newestCodexRollout() {
|
|
|
6850
7439
|
}
|
|
6851
7440
|
function ompQuotaFacts() {
|
|
6852
7441
|
const file = ompAgentDb();
|
|
6853
|
-
if (!
|
|
7442
|
+
if (!fs21.existsSync(file))
|
|
6854
7443
|
return { agent: "omp", kind: "gauge" };
|
|
6855
7444
|
let db;
|
|
6856
7445
|
try {
|
|
@@ -6882,7 +7471,7 @@ function claudeQuotaFacts(sessions) {
|
|
|
6882
7471
|
for (const session of sessions) {
|
|
6883
7472
|
if (session.agentSessionId === undefined)
|
|
6884
7473
|
continue;
|
|
6885
|
-
const file =
|
|
7474
|
+
const file = path21.join(dir, claudeTranscriptRelPath(session.cwd, session.agentSessionId));
|
|
6886
7475
|
const event = parseClaudeLimitEvent(readTailLines(file, QUOTA_TAIL_BYTES));
|
|
6887
7476
|
if (event && (!newest || event.at > newest.at))
|
|
6888
7477
|
newest = event;
|
|
@@ -6914,11 +7503,11 @@ function collectQuotaFacts(agents, sessions, onPath) {
|
|
|
6914
7503
|
function storeQuietForMs(agent, cwd, agentSessionId, nowMs) {
|
|
6915
7504
|
if (agentSessionId === undefined)
|
|
6916
7505
|
return;
|
|
6917
|
-
const file = agent === "claude" ?
|
|
7506
|
+
const file = agent === "claude" ? path21.join(claudeConfigDir2(), claudeTranscriptRelPath(cwd, agentSessionId)) : agent === "codex" ? findCodexRollout(agentSessionId) : undefined;
|
|
6918
7507
|
if (file === undefined)
|
|
6919
7508
|
return;
|
|
6920
7509
|
try {
|
|
6921
|
-
return Math.max(0, nowMs -
|
|
7510
|
+
return Math.max(0, nowMs - fs21.statSync(file).mtimeMs);
|
|
6922
7511
|
} catch {
|
|
6923
7512
|
return;
|
|
6924
7513
|
}
|
|
@@ -6928,15 +7517,15 @@ function readFileRange(file, from, to) {
|
|
|
6928
7517
|
return "";
|
|
6929
7518
|
let fd;
|
|
6930
7519
|
try {
|
|
6931
|
-
fd =
|
|
7520
|
+
fd = fs21.openSync(file, "r");
|
|
6932
7521
|
const buffer = Buffer.allocUnsafe(to - from);
|
|
6933
|
-
const read =
|
|
7522
|
+
const read = fs21.readSync(fd, buffer, 0, to - from, from);
|
|
6934
7523
|
return buffer.subarray(0, read).toString("utf8");
|
|
6935
7524
|
} catch {
|
|
6936
7525
|
return "";
|
|
6937
7526
|
} finally {
|
|
6938
7527
|
if (fd !== undefined)
|
|
6939
|
-
|
|
7528
|
+
fs21.closeSync(fd);
|
|
6940
7529
|
}
|
|
6941
7530
|
}
|
|
6942
7531
|
var QUOTA_TAIL_BYTES;
|
|
@@ -11429,8 +12018,8 @@ var init_terminal_mirror = __esm(() => {
|
|
|
11429
12018
|
});
|
|
11430
12019
|
|
|
11431
12020
|
// packages/daemon/src/pty-session.ts
|
|
11432
|
-
import
|
|
11433
|
-
import
|
|
12021
|
+
import fs22 from "fs";
|
|
12022
|
+
import path22 from "path";
|
|
11434
12023
|
|
|
11435
12024
|
class PtySession {
|
|
11436
12025
|
info;
|
|
@@ -11535,10 +12124,10 @@ class PtySession {
|
|
|
11535
12124
|
}
|
|
11536
12125
|
saveScrollback() {
|
|
11537
12126
|
try {
|
|
11538
|
-
const file =
|
|
12127
|
+
const file = path22.join(SCROLLBACK_DIR, `${this.info.id}.bin`);
|
|
11539
12128
|
const tmp = `${file}.tmp`;
|
|
11540
|
-
|
|
11541
|
-
|
|
12129
|
+
fs22.writeFileSync(tmp, this.serializeBuffer(), "utf8");
|
|
12130
|
+
fs22.renameSync(tmp, file);
|
|
11542
12131
|
} catch (err) {
|
|
11543
12132
|
log(`scrollback save failed for ${this.info.shortId}: ${String(err)}`);
|
|
11544
12133
|
}
|
|
@@ -11560,9 +12149,9 @@ var init_pty_session = __esm(() => {
|
|
|
11560
12149
|
// packages/daemon/src/session-manager.ts
|
|
11561
12150
|
import { randomBytes as randomBytes2, randomUUID } from "crypto";
|
|
11562
12151
|
import { spawn as spawn5 } from "child_process";
|
|
11563
|
-
import
|
|
11564
|
-
import
|
|
11565
|
-
import
|
|
12152
|
+
import fs23 from "fs";
|
|
12153
|
+
import os8 from "os";
|
|
12154
|
+
import path23 from "path";
|
|
11566
12155
|
function sessionScratchRoot(info) {
|
|
11567
12156
|
return info.worktreePath ?? info.cwd;
|
|
11568
12157
|
}
|
|
@@ -11612,11 +12201,15 @@ function pidIsRunning(pid) {
|
|
|
11612
12201
|
return err.code === "EPERM";
|
|
11613
12202
|
}
|
|
11614
12203
|
}
|
|
12204
|
+
function ptyHostEnabled(env) {
|
|
12205
|
+
return env["AXTERM_PTY_HOST"] !== "0";
|
|
12206
|
+
}
|
|
11615
12207
|
|
|
11616
12208
|
class SessionManager {
|
|
11617
12209
|
store;
|
|
11618
12210
|
hooks;
|
|
11619
12211
|
configuredDefaults;
|
|
12212
|
+
ptyHostPolicy;
|
|
11620
12213
|
live = new Map;
|
|
11621
12214
|
agentRuntime = new Map;
|
|
11622
12215
|
injector = new Injector;
|
|
@@ -11624,10 +12217,14 @@ class SessionManager {
|
|
|
11624
12217
|
eventListeners = new Set;
|
|
11625
12218
|
rawHookListeners = new Set;
|
|
11626
12219
|
workspace = new LocalWorkspace(MATERIALISED_DIR);
|
|
11627
|
-
constructor(store, hooks, configuredDefaults = () => ({})
|
|
12220
|
+
constructor(store, hooks, configuredDefaults = () => ({}), ptyHostPolicy = {
|
|
12221
|
+
enabled: ptyHostEnabled(process.env),
|
|
12222
|
+
...process.env["AXTERM_PTY_HOST_MAIN"] ? { mainScript: process.env["AXTERM_PTY_HOST_MAIN"] } : {}
|
|
12223
|
+
}) {
|
|
11628
12224
|
this.store = store;
|
|
11629
12225
|
this.hooks = hooks;
|
|
11630
12226
|
this.configuredDefaults = configuredDefaults;
|
|
12227
|
+
this.ptyHostPolicy = ptyHostPolicy;
|
|
11631
12228
|
this.sessionWatch = setInterval(() => this.tickSessions(), SESSION_TICK_MS);
|
|
11632
12229
|
this.sessionWatch.unref();
|
|
11633
12230
|
}
|
|
@@ -11664,7 +12261,7 @@ class SessionManager {
|
|
|
11664
12261
|
return { adopted: adoptedIds.length, sweptExited };
|
|
11665
12262
|
}
|
|
11666
12263
|
async hostSessions() {
|
|
11667
|
-
if (
|
|
12264
|
+
if (!this.ptyHostPolicy.enabled)
|
|
11668
12265
|
return new Map;
|
|
11669
12266
|
const existing = readPtyHostIn(RUNTIME_DIR);
|
|
11670
12267
|
if (existing === undefined)
|
|
@@ -11753,13 +12350,14 @@ class SessionManager {
|
|
|
11753
12350
|
return {
|
|
11754
12351
|
stat: await diffStat(ws, target, compare),
|
|
11755
12352
|
files: await changedFiles(ws, target, compare),
|
|
12353
|
+
patch: await diffPatch(ws, target, compare),
|
|
11756
12354
|
base: compare,
|
|
11757
12355
|
...info.branch !== undefined ? { branch: info.branch } : {}
|
|
11758
12356
|
};
|
|
11759
12357
|
}
|
|
11760
12358
|
async create(params) {
|
|
11761
|
-
const requestedCwd = params.cwd ?
|
|
11762
|
-
if (!
|
|
12359
|
+
const requestedCwd = params.cwd ? path23.resolve(params.cwd) : os8.homedir();
|
|
12360
|
+
if (!fs23.existsSync(requestedCwd)) {
|
|
11763
12361
|
throw new AxtermError("err.cwd.missing", { cwd: requestedCwd });
|
|
11764
12362
|
}
|
|
11765
12363
|
let worktree;
|
|
@@ -11815,14 +12413,14 @@ class SessionManager {
|
|
|
11815
12413
|
sessionCwd = resolveSessionCwd({
|
|
11816
12414
|
requested: cwd,
|
|
11817
12415
|
preferConversation: params.preferConversationCwd === true,
|
|
11818
|
-
...recorded !== undefined &&
|
|
12416
|
+
...recorded !== undefined && fs23.existsSync(recorded) ? { conversationCwd: recorded } : {}
|
|
11819
12417
|
});
|
|
11820
12418
|
}
|
|
11821
12419
|
}
|
|
11822
12420
|
const info = {
|
|
11823
12421
|
id,
|
|
11824
12422
|
shortId: id.slice(0, 6),
|
|
11825
|
-
name: params.name ?? worktree?.branch ??
|
|
12423
|
+
name: params.name ?? worktree?.branch ?? path23.basename(sessionCwd),
|
|
11826
12424
|
cwd: sessionCwd,
|
|
11827
12425
|
shell: shell.kind,
|
|
11828
12426
|
agentCommand,
|
|
@@ -11846,7 +12444,7 @@ class SessionManager {
|
|
|
11846
12444
|
launchToken
|
|
11847
12445
|
});
|
|
11848
12446
|
const file = this.claudeSettingsPath(id);
|
|
11849
|
-
|
|
12447
|
+
fs23.writeFileSync(file, JSON.stringify(settings, null, 2), { mode: 384 });
|
|
11850
12448
|
}
|
|
11851
12449
|
writeGeminiSettings(sessionId) {
|
|
11852
12450
|
try {
|
|
@@ -11859,8 +12457,8 @@ class SessionManager {
|
|
|
11859
12457
|
});
|
|
11860
12458
|
const file = this.geminiSettingsPath();
|
|
11861
12459
|
const tmp = `${file}.tmp`;
|
|
11862
|
-
|
|
11863
|
-
|
|
12460
|
+
fs23.writeFileSync(tmp, JSON.stringify(settings, null, 2));
|
|
12461
|
+
fs23.renameSync(tmp, file);
|
|
11864
12462
|
} catch (err) {
|
|
11865
12463
|
log(`gemini settings write failed; session starts without hooks: ${String(err)}`);
|
|
11866
12464
|
}
|
|
@@ -11889,7 +12487,7 @@ class SessionManager {
|
|
|
11889
12487
|
return;
|
|
11890
12488
|
}
|
|
11891
12489
|
this.watchCodexAppServer(id, url, pid);
|
|
11892
|
-
|
|
12490
|
+
fs23.writeFileSync(this.codexAppServerFile(id), JSON.stringify({ pid, url }), { mode: 384 });
|
|
11893
12491
|
log(`codex app-server on ${url} (pid ${pid}) for session ${id.slice(0, 6)} in ${cwd}`);
|
|
11894
12492
|
return url;
|
|
11895
12493
|
} catch (err) {
|
|
@@ -11898,13 +12496,13 @@ class SessionManager {
|
|
|
11898
12496
|
}
|
|
11899
12497
|
}
|
|
11900
12498
|
codexAppServerFile(id) {
|
|
11901
|
-
return
|
|
12499
|
+
return path23.join(RUNTIME_DIR, `codex-app-${id}.json`);
|
|
11902
12500
|
}
|
|
11903
12501
|
adoptCodexAppServer(id) {
|
|
11904
12502
|
const file = this.codexAppServerFile(id);
|
|
11905
12503
|
let record;
|
|
11906
12504
|
try {
|
|
11907
|
-
record = JSON.parse(
|
|
12505
|
+
record = JSON.parse(fs23.readFileSync(file, "utf8"));
|
|
11908
12506
|
} catch {
|
|
11909
12507
|
return;
|
|
11910
12508
|
}
|
|
@@ -11913,7 +12511,7 @@ class SessionManager {
|
|
|
11913
12511
|
if (typeof pid !== "number" || typeof url !== "string")
|
|
11914
12512
|
return;
|
|
11915
12513
|
if (!pidIsRunning(pid)) {
|
|
11916
|
-
|
|
12514
|
+
fs23.rmSync(file, { force: true });
|
|
11917
12515
|
return;
|
|
11918
12516
|
}
|
|
11919
12517
|
this.watchCodexAppServer(id, url, pid);
|
|
@@ -11927,7 +12525,7 @@ class SessionManager {
|
|
|
11927
12525
|
stop: () => {
|
|
11928
12526
|
client.close();
|
|
11929
12527
|
this.killCodexAppServer(pid);
|
|
11930
|
-
|
|
12528
|
+
fs23.rmSync(this.codexAppServerFile(id), { force: true });
|
|
11931
12529
|
}
|
|
11932
12530
|
});
|
|
11933
12531
|
}
|
|
@@ -11941,22 +12539,22 @@ class SessionManager {
|
|
|
11941
12539
|
this.codexServers.delete(id);
|
|
11942
12540
|
}
|
|
11943
12541
|
ensureCodexHome() {
|
|
11944
|
-
const home =
|
|
11945
|
-
|
|
12542
|
+
const home = path23.join(STATE_DIR, "codex-home");
|
|
12543
|
+
fs23.mkdirSync(home, { recursive: true, mode: 448 });
|
|
11946
12544
|
this.linkCodexAuth(home);
|
|
11947
12545
|
const bridge = hookLaunch({ execPath: process.execPath, mainScript: Bun.main, agent: "codex" });
|
|
11948
12546
|
const hooksFile = buildCodexHooks({ command: bridge.command, args: bridge.args });
|
|
11949
12547
|
const hooksPath = codexTrustKeyPath(home);
|
|
11950
12548
|
const tmp = `${hooksPath}.tmp`;
|
|
11951
|
-
|
|
11952
|
-
|
|
12549
|
+
fs23.writeFileSync(tmp, JSON.stringify(hooksFile, null, 2));
|
|
12550
|
+
fs23.renameSync(tmp, hooksPath);
|
|
11953
12551
|
const entries = codexHookTrust(hooksPath, hooksFile);
|
|
11954
|
-
const configPath =
|
|
11955
|
-
const existing =
|
|
12552
|
+
const configPath = path23.join(home, "config.toml");
|
|
12553
|
+
const existing = fs23.existsSync(configPath) ? fs23.readFileSync(configPath, "utf8") : "";
|
|
11956
12554
|
const missing = entries.filter((e) => !existing.includes(`"${e.key.replaceAll("\\", "\\\\")}"`));
|
|
11957
12555
|
if (missing.length > 0) {
|
|
11958
12556
|
const block = codexTrustStateToml(missing);
|
|
11959
|
-
|
|
12557
|
+
fs23.appendFileSync(configPath, existing !== "" && !existing.endsWith(`
|
|
11960
12558
|
`) ? `
|
|
11961
12559
|
${block}` : block);
|
|
11962
12560
|
}
|
|
@@ -11969,12 +12567,12 @@ ${block}` : block);
|
|
|
11969
12567
|
return home;
|
|
11970
12568
|
}
|
|
11971
12569
|
linkCodexAuth(home) {
|
|
11972
|
-
const target =
|
|
11973
|
-
if (
|
|
12570
|
+
const target = path23.join(home, "auth.json");
|
|
12571
|
+
if (fs23.existsSync(target))
|
|
11974
12572
|
return;
|
|
11975
|
-
const operatorHome = process.env["CODEX_HOME"] ??
|
|
11976
|
-
const source =
|
|
11977
|
-
if (!
|
|
12573
|
+
const operatorHome = process.env["CODEX_HOME"] ?? path23.join(os8.homedir(), ".codex");
|
|
12574
|
+
const source = path23.join(operatorHome, "auth.json");
|
|
12575
|
+
if (!fs23.existsSync(source))
|
|
11978
12576
|
throw new AxtermError("err.codex.noAuth", { authFile: source });
|
|
11979
12577
|
let how;
|
|
11980
12578
|
try {
|
|
@@ -11988,31 +12586,31 @@ ${block}` : block);
|
|
|
11988
12586
|
}
|
|
11989
12587
|
doctorReport() {
|
|
11990
12588
|
const bridge = hookLaunch({ execPath: process.execPath, mainScript: Bun.main, agent: "codex" });
|
|
11991
|
-
const home =
|
|
11992
|
-
const operatorHome = process.env["CODEX_HOME"] ??
|
|
11993
|
-
const authSource =
|
|
12589
|
+
const home = path23.join(STATE_DIR, "codex-home");
|
|
12590
|
+
const operatorHome = process.env["CODEX_HOME"] ?? path23.join(os8.homedir(), ".codex");
|
|
12591
|
+
const authSource = path23.join(operatorHome, "auth.json");
|
|
11994
12592
|
const geminiSettings = this.geminiSettingsPath();
|
|
11995
|
-
const configPath =
|
|
12593
|
+
const configPath = path23.join(home, "config.toml");
|
|
11996
12594
|
const read = (file) => {
|
|
11997
12595
|
try {
|
|
11998
|
-
return
|
|
12596
|
+
return fs23.existsSync(file) ? fs23.readFileSync(file, "utf8") : undefined;
|
|
11999
12597
|
} catch {
|
|
12000
12598
|
return;
|
|
12001
12599
|
}
|
|
12002
12600
|
};
|
|
12003
12601
|
return [
|
|
12004
|
-
...evaluateBridgeDoctor({ command: bridge.command, exists:
|
|
12602
|
+
...evaluateBridgeDoctor({ command: bridge.command, exists: fs23.existsSync(bridge.command) }),
|
|
12005
12603
|
...evaluateOmpDoctor({
|
|
12006
12604
|
binary: lookupOnPath("omp"),
|
|
12007
12605
|
extensionPath: this.hooks.extensionPath,
|
|
12008
|
-
extensionExists:
|
|
12606
|
+
extensionExists: fs23.existsSync(this.hooks.extensionPath)
|
|
12009
12607
|
}),
|
|
12010
12608
|
...evaluateClaudeDoctor({ binary: lookupOnPath("claude") }),
|
|
12011
12609
|
...evaluateCodexDoctor({
|
|
12012
12610
|
binary: lookupOnPath("codex"),
|
|
12013
12611
|
authSourcePath: authSource,
|
|
12014
|
-
authSourceExists:
|
|
12015
|
-
homeExists:
|
|
12612
|
+
authSourceExists: fs23.existsSync(authSource),
|
|
12613
|
+
homeExists: fs23.existsSync(home),
|
|
12016
12614
|
expected: codexHookTrust(codexTrustKeyPath(home), buildCodexHooks({ command: bridge.command, args: bridge.args })),
|
|
12017
12615
|
configToml: read(configPath)
|
|
12018
12616
|
}),
|
|
@@ -12113,10 +12711,14 @@ ${block}` : block);
|
|
|
12113
12711
|
}
|
|
12114
12712
|
answerScreenChoice(idOrPrefix, optionIndex) {
|
|
12115
12713
|
const session = this.resolveLive(idOrPrefix);
|
|
12116
|
-
const
|
|
12714
|
+
const agent = agentIdFromCommand(session.info.agentCommand);
|
|
12715
|
+
const dialog = detectOnboarding(agent, session.screenText(30));
|
|
12117
12716
|
const digit = dialog?.options[optionIndex]?.digit;
|
|
12118
12717
|
if (digit === undefined) {
|
|
12119
|
-
|
|
12718
|
+
const keys2 = menuChoiceKeys(agentProfile(agent), optionIndex);
|
|
12719
|
+
if (keys2 === undefined)
|
|
12720
|
+
throw new AxtermError("err.choose.noSafeKey", { agent, index: optionIndex });
|
|
12721
|
+
session.write(keys2);
|
|
12120
12722
|
return "menu";
|
|
12121
12723
|
}
|
|
12122
12724
|
this.pendingPrompt.delete(session.info.id);
|
|
@@ -12149,13 +12751,13 @@ ${block}` : block);
|
|
|
12149
12751
|
}
|
|
12150
12752
|
writeScreenFile(idOrPrefix, lines) {
|
|
12151
12753
|
const session = this.resolveLive(idOrPrefix);
|
|
12152
|
-
const file =
|
|
12153
|
-
|
|
12754
|
+
const file = path23.join(path23.dirname(this.hooks.endpointFile), `screen-${session.info.id}.txt`);
|
|
12755
|
+
fs23.writeFileSync(file, session.screenText(lines), { mode: 384 });
|
|
12154
12756
|
return file;
|
|
12155
12757
|
}
|
|
12156
12758
|
removeScreenFile(id) {
|
|
12157
12759
|
try {
|
|
12158
|
-
|
|
12760
|
+
fs23.rmSync(path23.join(path23.dirname(this.hooks.endpointFile), `screen-${id}.txt`), { force: true });
|
|
12159
12761
|
} catch {}
|
|
12160
12762
|
}
|
|
12161
12763
|
async stageInboundFile(idOrPrefix, suggestedName, bytes) {
|
|
@@ -12164,20 +12766,20 @@ ${block}` : block);
|
|
|
12164
12766
|
return this.stageInRuntimeDir(info, suggestedName, bytes);
|
|
12165
12767
|
const ws = this.workspaceFor(info.id);
|
|
12166
12768
|
const dir = sessionScratchRoot(info);
|
|
12167
|
-
const file =
|
|
12769
|
+
const file = path23.resolve(planMaterialisedPath(inboundStagingDir(dir), info.id, suggestedName));
|
|
12168
12770
|
await this.prepareScratchDir(ws, dir, file);
|
|
12169
12771
|
await ws.write(file, bytes, 384);
|
|
12170
|
-
log(`session ${info.shortId} staged ${
|
|
12772
|
+
log(`session ${info.shortId} staged ${path23.basename(file)} (${bytes.byteLength} bytes)`);
|
|
12171
12773
|
return file;
|
|
12172
12774
|
}
|
|
12173
12775
|
stagingMode() {
|
|
12174
12776
|
return this.configuredDefaults().staging ?? "project";
|
|
12175
12777
|
}
|
|
12176
12778
|
async stageInRuntimeDir(info, suggestedName, bytes) {
|
|
12177
|
-
const file =
|
|
12178
|
-
await
|
|
12179
|
-
await
|
|
12180
|
-
log(`session ${info.shortId} staged ${
|
|
12779
|
+
const file = path23.resolve(planMaterialisedPath(MATERIALISED_DIR, info.id, suggestedName));
|
|
12780
|
+
await fs23.promises.mkdir(path23.dirname(file), { recursive: true, mode: 448 });
|
|
12781
|
+
await fs23.promises.writeFile(file, bytes, { mode: 384 });
|
|
12782
|
+
log(`session ${info.shortId} staged ${path23.basename(file)} (${bytes.byteLength} bytes, runtime dir)`);
|
|
12181
12783
|
return file;
|
|
12182
12784
|
}
|
|
12183
12785
|
async prepareScratchDir(ws, sessionDir, file) {
|
|
@@ -12194,47 +12796,47 @@ ${block}` : block);
|
|
|
12194
12796
|
throw new AxtermError("err.staging.notIgnored", { dir: scratch, file: SCRATCH_IGNORE_NAME });
|
|
12195
12797
|
}
|
|
12196
12798
|
try {
|
|
12197
|
-
await ws.mkdirp(
|
|
12799
|
+
await ws.mkdirp(path23.dirname(file), 448);
|
|
12198
12800
|
} catch (err) {
|
|
12199
12801
|
throw new AxtermError("err.staging.dir", { dir: scratch, error: err instanceof Error ? err.message : String(err) });
|
|
12200
12802
|
}
|
|
12201
12803
|
}
|
|
12202
12804
|
removeStagedFiles(id, info) {
|
|
12203
12805
|
try {
|
|
12204
|
-
|
|
12806
|
+
fs23.rmSync(path23.join(MATERIALISED_DIR, id), { recursive: true, force: true });
|
|
12205
12807
|
} catch {}
|
|
12206
12808
|
if (info === undefined)
|
|
12207
12809
|
return;
|
|
12208
|
-
const scratch =
|
|
12810
|
+
const scratch = path23.resolve(scratchDir(sessionScratchRoot(info)));
|
|
12209
12811
|
try {
|
|
12210
|
-
|
|
12211
|
-
|
|
12212
|
-
|
|
12213
|
-
|
|
12812
|
+
fs23.rmSync(path23.resolve(inboundStagingDir(sessionScratchRoot(info)), id), { recursive: true, force: true });
|
|
12813
|
+
fs23.rmdirSync(path23.join(scratch, "inbound"));
|
|
12814
|
+
fs23.rmSync(path23.join(scratch, SCRATCH_IGNORE_NAME), { force: true });
|
|
12815
|
+
fs23.rmdirSync(scratch);
|
|
12214
12816
|
} catch {}
|
|
12215
12817
|
}
|
|
12216
12818
|
removeClaudeSettings(id) {
|
|
12217
12819
|
try {
|
|
12218
|
-
|
|
12219
|
-
|
|
12220
|
-
|
|
12221
|
-
|
|
12820
|
+
fs23.rmSync(this.claudeSettingsPath(id), { force: true });
|
|
12821
|
+
fs23.rmSync(this.mcpConfigPath(id), { force: true });
|
|
12822
|
+
fs23.rmSync(this.mcpContextPath(id), { force: true });
|
|
12823
|
+
fs23.rmSync(this.geminiTelemetryPath(id), { force: true });
|
|
12222
12824
|
this.stopCodexAppServer(id);
|
|
12223
12825
|
} catch {}
|
|
12224
12826
|
}
|
|
12225
12827
|
claudeSettingsPath(id) {
|
|
12226
|
-
return
|
|
12828
|
+
return path23.join(path23.dirname(this.hooks.endpointFile), `claude-${id}.json`);
|
|
12227
12829
|
}
|
|
12228
12830
|
mcpConfigPath(id) {
|
|
12229
|
-
return
|
|
12831
|
+
return path23.join(path23.dirname(this.hooks.endpointFile), `mcp-${id}.json`);
|
|
12230
12832
|
}
|
|
12231
12833
|
mcpContextPath(id) {
|
|
12232
|
-
return
|
|
12834
|
+
return path23.join(path23.dirname(this.hooks.endpointFile), `mcp-ctx-${id}.json`);
|
|
12233
12835
|
}
|
|
12234
12836
|
writeMcpContext(id, launchToken) {
|
|
12235
12837
|
const file = this.mcpContextPath(id);
|
|
12236
12838
|
try {
|
|
12237
|
-
|
|
12839
|
+
fs23.writeFileSync(file, JSON.stringify({ port: this.hooks.port, token: this.hooks.host.token, sessionId: id, launchToken }), { mode: 384 });
|
|
12238
12840
|
return file;
|
|
12239
12841
|
} catch (err) {
|
|
12240
12842
|
log(`mcp context write failed; session starts without axterm's tools: ${String(err)}`);
|
|
@@ -12247,7 +12849,7 @@ ${block}` : block);
|
|
|
12247
12849
|
writeMcpConfig(id, contextPath) {
|
|
12248
12850
|
const file = this.mcpConfigPath(id);
|
|
12249
12851
|
try {
|
|
12250
|
-
|
|
12852
|
+
fs23.writeFileSync(file, claudeMcpConfig(this.mcpServerLaunch(), contextPath), { mode: 384 });
|
|
12251
12853
|
return file;
|
|
12252
12854
|
} catch (err) {
|
|
12253
12855
|
log(`mcp config write failed; session starts without axterm's tools: ${String(err)}`);
|
|
@@ -12255,18 +12857,20 @@ ${block}` : block);
|
|
|
12255
12857
|
}
|
|
12256
12858
|
}
|
|
12257
12859
|
geminiTelemetryPath(id) {
|
|
12258
|
-
return
|
|
12860
|
+
return path23.join(path23.dirname(this.hooks.endpointFile), `gemini-telemetry-${id}.log`);
|
|
12259
12861
|
}
|
|
12260
12862
|
geminiSettingsPath() {
|
|
12261
|
-
return
|
|
12863
|
+
return path23.join(path23.dirname(this.hooks.endpointFile), "gemini-defaults.json");
|
|
12262
12864
|
}
|
|
12263
12865
|
async ptySpawner() {
|
|
12264
|
-
if (
|
|
12866
|
+
if (!this.ptyHostPolicy.enabled || this.ptyHostUnavailable)
|
|
12265
12867
|
return { spawn: spawnPty, hostOwned: false };
|
|
12266
12868
|
try {
|
|
12267
12869
|
if (this.ptyHost === undefined) {
|
|
12268
|
-
const
|
|
12269
|
-
|
|
12870
|
+
const info = await ensurePtyHost({
|
|
12871
|
+
runtimeDir: RUNTIME_DIR,
|
|
12872
|
+
...this.ptyHostPolicy.mainScript ? { mainScript: this.ptyHostPolicy.mainScript } : {}
|
|
12873
|
+
});
|
|
12270
12874
|
this.ptyHost = await connectPtyHost({ endpoint: info.endpoint, token: info.token });
|
|
12271
12875
|
log(`pty host: connected (pid ${info.pid})`);
|
|
12272
12876
|
}
|
|
@@ -12431,7 +13035,7 @@ ${block}` : block);
|
|
|
12431
13035
|
this.store.updateAgentState(id, result.entry.state, "hook", now);
|
|
12432
13036
|
log(`session ${info.shortId} agent ${runtime.entry?.state ?? "-"} -> ${result.entry.state}`);
|
|
12433
13037
|
this.emit({ t: "agent.status", sessionId: id, state: result.entry.state, toolName: result.entry.toolName });
|
|
12434
|
-
if (
|
|
13038
|
+
if (offersKeystrokeApproval(agentProfile(agentIdFromCommand(info.agentCommand)), result.entry.state)) {
|
|
12435
13039
|
runtime.choiceSeq += 1;
|
|
12436
13040
|
this.emit({
|
|
12437
13041
|
t: "choice.request",
|
|
@@ -12556,7 +13160,7 @@ ${block}` : block);
|
|
|
12556
13160
|
if (agentCommand === undefined) {
|
|
12557
13161
|
throw new AxtermError("err.session.noResumeSupport", { agent: profile.displayName });
|
|
12558
13162
|
}
|
|
12559
|
-
if (!
|
|
13163
|
+
if (!fs23.existsSync(stored.cwd))
|
|
12560
13164
|
throw new AxtermError("err.session.cwdGone", { cwd: stored.cwd });
|
|
12561
13165
|
const info = { ...stored, agentCommand, state: "running" };
|
|
12562
13166
|
delete info.exitCode;
|
|
@@ -12630,7 +13234,7 @@ ${block}` : block);
|
|
|
12630
13234
|
removeScrollback(id) {
|
|
12631
13235
|
for (const file of [`${id}.bin`, `${id}.bin.tmp`]) {
|
|
12632
13236
|
try {
|
|
12633
|
-
|
|
13237
|
+
fs23.rmSync(path23.join(SCROLLBACK_DIR, file), { force: true });
|
|
12634
13238
|
} catch {}
|
|
12635
13239
|
}
|
|
12636
13240
|
}
|
|
@@ -12643,7 +13247,7 @@ ${block}` : block);
|
|
|
12643
13247
|
if (runtime === undefined)
|
|
12644
13248
|
return;
|
|
12645
13249
|
try {
|
|
12646
|
-
runtime.telemetryOffset =
|
|
13250
|
+
runtime.telemetryOffset = fs23.statSync(this.geminiTelemetryPath(id)).size;
|
|
12647
13251
|
} catch {}
|
|
12648
13252
|
}
|
|
12649
13253
|
drainGeminiTelemetry() {
|
|
@@ -12656,7 +13260,7 @@ ${block}` : block);
|
|
|
12656
13260
|
const file = this.geminiTelemetryPath(id);
|
|
12657
13261
|
let size = 0;
|
|
12658
13262
|
try {
|
|
12659
|
-
size =
|
|
13263
|
+
size = fs23.statSync(file).size;
|
|
12660
13264
|
} catch {
|
|
12661
13265
|
continue;
|
|
12662
13266
|
}
|
|
@@ -12752,7 +13356,7 @@ var init_session_manager = __esm(() => {
|
|
|
12752
13356
|
init_pty_session();
|
|
12753
13357
|
init_shell();
|
|
12754
13358
|
init_workspace_local();
|
|
12755
|
-
MATERIALISED_DIR =
|
|
13359
|
+
MATERIALISED_DIR = path23.join(RUNTIME_DIR, "materialised");
|
|
12756
13360
|
PROMPT_OPTIONS = {
|
|
12757
13361
|
hostKey: [{ label: "yes" }, { label: "no" }],
|
|
12758
13362
|
password: [],
|
|
@@ -12904,11 +13508,11 @@ var init_acknowledge = __esm(() => {
|
|
|
12904
13508
|
});
|
|
12905
13509
|
|
|
12906
13510
|
// packages/channel-telegram/src/attach.ts
|
|
12907
|
-
import
|
|
12908
|
-
import
|
|
13511
|
+
import fs24 from "fs";
|
|
13512
|
+
import path24 from "path";
|
|
12909
13513
|
function buildAttachmentUpload(filePath, chatId, topicId, caption, limits = TELEGRAM_LIMITS) {
|
|
12910
|
-
const name =
|
|
12911
|
-
const bytes =
|
|
13514
|
+
const name = path24.basename(filePath);
|
|
13515
|
+
const bytes = fs24.statSync(filePath).size;
|
|
12912
13516
|
const plan = planAttachment({ name, bytes }, limits);
|
|
12913
13517
|
if (plan.kind === "rejected")
|
|
12914
13518
|
throw new Error(plan.reason);
|
|
@@ -12918,7 +13522,7 @@ function buildAttachmentUpload(filePath, chatId, topicId, caption, limits = TELE
|
|
|
12918
13522
|
form.append("message_thread_id", String(topicId));
|
|
12919
13523
|
if (caption !== undefined && caption.length > 0)
|
|
12920
13524
|
form.append("caption", caption);
|
|
12921
|
-
form.append(field, new Blob([
|
|
13525
|
+
form.append(field, new Blob([fs24.readFileSync(filePath)]), name);
|
|
12922
13526
|
return { method: plan.kind === "photo" ? "sendPhoto" : "sendDocument", field, form };
|
|
12923
13527
|
}
|
|
12924
13528
|
function isPhotoRejection(error) {
|
|
@@ -12926,13 +13530,13 @@ function isPhotoRejection(error) {
|
|
|
12926
13530
|
return /IMAGE_PROCESS_FAILED|PHOTO_INVALID_DIMENSIONS|PHOTO_EXT_INVALID|PHOTO_SAVE_FILE_INVALID/i.test(message);
|
|
12927
13531
|
}
|
|
12928
13532
|
function asDocumentUpload(filePath, chatId, topicId, caption) {
|
|
12929
|
-
const name =
|
|
13533
|
+
const name = path24.basename(filePath);
|
|
12930
13534
|
const form = new FormData;
|
|
12931
13535
|
form.append("chat_id", String(chatId));
|
|
12932
13536
|
form.append("message_thread_id", String(topicId));
|
|
12933
13537
|
if (caption !== undefined && caption.length > 0)
|
|
12934
13538
|
form.append("caption", caption);
|
|
12935
|
-
form.append("document", new Blob([
|
|
13539
|
+
form.append("document", new Blob([fs24.readFileSync(filePath)]), name);
|
|
12936
13540
|
return { method: "sendDocument", field: "document", form };
|
|
12937
13541
|
}
|
|
12938
13542
|
var TELEGRAM_LIMITS;
|
|
@@ -13002,6 +13606,31 @@ function planScreenDelivery(text, maxMessages) {
|
|
|
13002
13606
|
const chunks = preChunks(text);
|
|
13003
13607
|
return chunks.length <= maxMessages ? { kind: "messages", chunks } : { kind: "file", text };
|
|
13004
13608
|
}
|
|
13609
|
+
function diffQuote(summary, limit = TELEGRAM_TEXT_LIMIT, reserved = 0) {
|
|
13610
|
+
const OPEN = "<blockquote expandable><pre>";
|
|
13611
|
+
const CLOSE = "</pre></blockquote>";
|
|
13612
|
+
const budget = limit - reserved - OPEN.length - CLOSE.length;
|
|
13613
|
+
const cut = (total) => escapeHtml(`\u2026 truncated, ${total} characters in the full patch`);
|
|
13614
|
+
const rendered = [];
|
|
13615
|
+
let used = 0;
|
|
13616
|
+
let stopped = false;
|
|
13617
|
+
for (const line of summary.lines) {
|
|
13618
|
+
const marker = line.kind === "add" ? "+" : line.kind === "del" ? "-" : "";
|
|
13619
|
+
const escaped = escapeHtml(`${marker}${line.text}`);
|
|
13620
|
+
if (used + escaped.length + 1 + cut(summary.totalChars).length > budget) {
|
|
13621
|
+
stopped = true;
|
|
13622
|
+
break;
|
|
13623
|
+
}
|
|
13624
|
+
rendered.push(escaped);
|
|
13625
|
+
used += escaped.length + 1;
|
|
13626
|
+
}
|
|
13627
|
+
if (rendered.length === 0)
|
|
13628
|
+
return "";
|
|
13629
|
+
if (stopped || summary.truncated !== undefined)
|
|
13630
|
+
rendered.push(cut(summary.totalChars));
|
|
13631
|
+
return `${OPEN}${rendered.join(`
|
|
13632
|
+
`)}${CLOSE}`;
|
|
13633
|
+
}
|
|
13005
13634
|
function parseScreenLines(args) {
|
|
13006
13635
|
const raw = args[0];
|
|
13007
13636
|
if (raw === undefined || !/^-?\d+$/.test(raw))
|
|
@@ -13476,7 +14105,7 @@ var init_topic_title = __esm(() => {
|
|
|
13476
14105
|
});
|
|
13477
14106
|
|
|
13478
14107
|
// packages/channel-telegram/src/adapter.ts
|
|
13479
|
-
import { setTimeout as
|
|
14108
|
+
import { setTimeout as delay4 } from "timers/promises";
|
|
13480
14109
|
function readMedia(msg) {
|
|
13481
14110
|
const photo = msg.photo?.at(-1);
|
|
13482
14111
|
if (photo) {
|
|
@@ -13512,6 +14141,7 @@ class TelegramChannel {
|
|
|
13512
14141
|
titleFlushes = new Map;
|
|
13513
14142
|
flows = new Map;
|
|
13514
14143
|
outbound = Promise.resolve();
|
|
14144
|
+
username;
|
|
13515
14145
|
polling = false;
|
|
13516
14146
|
stopped = false;
|
|
13517
14147
|
pollAbort;
|
|
@@ -13696,7 +14326,16 @@ class TelegramChannel {
|
|
|
13696
14326
|
`${d.stat.filesChanged} files +${d.stat.insertions} -${d.stat.deletions}`,
|
|
13697
14327
|
...d.files.map((f) => `\u2022 ${f}`)
|
|
13698
14328
|
];
|
|
13699
|
-
|
|
14329
|
+
const messages = htmlLines(lines);
|
|
14330
|
+
const summary = d.patch === undefined ? undefined : summarizePatch(d.patch);
|
|
14331
|
+
const last = messages.at(-1);
|
|
14332
|
+
if (summary !== undefined && last !== undefined) {
|
|
14333
|
+
const quote = diffQuote(summary, TELEGRAM_TEXT_LIMIT, last.length + 1);
|
|
14334
|
+
if (quote !== "")
|
|
14335
|
+
messages[messages.length - 1] = `${last}
|
|
14336
|
+
${quote}`;
|
|
14337
|
+
}
|
|
14338
|
+
for (const message of messages) {
|
|
13700
14339
|
await this.sendToSession(sessionId, message);
|
|
13701
14340
|
}
|
|
13702
14341
|
}
|
|
@@ -14119,8 +14758,8 @@ ${code(prompt.line)}`,
|
|
|
14119
14758
|
throw new Error("this Telegram client cannot download files");
|
|
14120
14759
|
const files = [];
|
|
14121
14760
|
for (const item of batch.items) {
|
|
14122
|
-
const
|
|
14123
|
-
files.push({ path:
|
|
14761
|
+
const path25 = await this.control.stageInboundFile(batch.sessionId, item.name, await download2(item.fileId));
|
|
14762
|
+
files.push({ path: path25, kind: item.kind });
|
|
14124
14763
|
}
|
|
14125
14764
|
await this.control.prompt(batch.sessionId, buildAttachmentPrompt(files, batch.caption));
|
|
14126
14765
|
this.log(`staged ${files.length} inbound file(s) for ${batch.sessionId}`);
|
|
@@ -14135,7 +14774,7 @@ ${code(prompt.line)}`,
|
|
|
14135
14774
|
async settleInboundForTest() {
|
|
14136
14775
|
while (this.albums.size > 0 || this.inboundQueues.size > 0) {
|
|
14137
14776
|
if (this.albums.size > 0)
|
|
14138
|
-
await
|
|
14777
|
+
await delay4(this.albumDebounceMs + 5);
|
|
14139
14778
|
await Promise.all([...this.inboundQueues.values()]);
|
|
14140
14779
|
}
|
|
14141
14780
|
}
|
|
@@ -14144,10 +14783,22 @@ ${code(prompt.line)}`,
|
|
|
14144
14783
|
return;
|
|
14145
14784
|
await this.api.call("setMessageReaction", { chat_id: chatId, message_id: messageId, reaction: reactionPayload(state) }).catch(() => {});
|
|
14146
14785
|
}
|
|
14786
|
+
async selfUsername() {
|
|
14787
|
+
if (this.username !== undefined)
|
|
14788
|
+
return this.username;
|
|
14789
|
+
try {
|
|
14790
|
+
const me = await this.api.call("getMe");
|
|
14791
|
+
if (typeof me.username === "string" && me.username.length > 0)
|
|
14792
|
+
this.username = me.username;
|
|
14793
|
+
} catch {}
|
|
14794
|
+
return this.username;
|
|
14795
|
+
}
|
|
14147
14796
|
async handleCommand(text, binding, msg) {
|
|
14148
14797
|
const [rawCmd = "", ...args] = text.split(/\s+/);
|
|
14149
14798
|
const rawTail = text.slice(rawCmd.length);
|
|
14150
|
-
const cmd = rawCmd
|
|
14799
|
+
const { command: cmd, target } = parseCommandAddress(rawCmd);
|
|
14800
|
+
if (target !== undefined && !commandIsForBot(target, await this.selfUsername()))
|
|
14801
|
+
return;
|
|
14151
14802
|
if (binding) {
|
|
14152
14803
|
switch (cmd) {
|
|
14153
14804
|
case "/esc":
|
|
@@ -14303,13 +14954,13 @@ ${code(prompt.line)}`,
|
|
|
14303
14954
|
const project = await resolveProject(args[0]);
|
|
14304
14955
|
if (!project)
|
|
14305
14956
|
return;
|
|
14306
|
-
const
|
|
14307
|
-
if (
|
|
14957
|
+
const target2 = args[1];
|
|
14958
|
+
if (target2 === undefined) {
|
|
14308
14959
|
await reply(t("tg.usage.rm"));
|
|
14309
14960
|
return;
|
|
14310
14961
|
}
|
|
14311
|
-
const result = await this.control.removeWorktree(project.alias,
|
|
14312
|
-
await reply(result.removed ? t("tg.worktree.removed", { target }) : t("tg.worktree.refused", { reason: String(result.reason) }));
|
|
14962
|
+
const result = await this.control.removeWorktree(project.alias, target2, args[2] === "force");
|
|
14963
|
+
await reply(result.removed ? t("tg.worktree.removed", { target: target2 }) : t("tg.worktree.refused", { reason: String(result.reason) }));
|
|
14313
14964
|
return;
|
|
14314
14965
|
}
|
|
14315
14966
|
case "/sessions": {
|
|
@@ -14407,8 +15058,12 @@ ${code(prompt.line)}`,
|
|
|
14407
15058
|
return;
|
|
14408
15059
|
}
|
|
14409
15060
|
if (flow.askId === undefined) {
|
|
14410
|
-
const keys2 = agentProfile(agentIdFromCommand(session.agentCommand)).
|
|
14411
|
-
|
|
15061
|
+
const keys2 = menuChoiceKeys(agentProfile(agentIdFromCommand(session.agentCommand)), decoded.optionIndex);
|
|
15062
|
+
if (keys2 === undefined) {
|
|
15063
|
+
await answer(t("tg.answer.noSafeKey"));
|
|
15064
|
+
return;
|
|
15065
|
+
}
|
|
15066
|
+
this.control.write(session.id, keys2);
|
|
14412
15067
|
}
|
|
14413
15068
|
flow.answers.push({ questionIndex: flow.cursor, selected: [label] });
|
|
14414
15069
|
flow.cursor += 1;
|
|
@@ -14461,6 +15116,11 @@ ${code(prompt.line)}`,
|
|
|
14461
15116
|
clearTimeout(album.timer);
|
|
14462
15117
|
this.albums.clear();
|
|
14463
15118
|
}
|
|
15119
|
+
pollHealth() {
|
|
15120
|
+
return this.pollError;
|
|
15121
|
+
}
|
|
15122
|
+
pollError;
|
|
15123
|
+
pollWarned = false;
|
|
14464
15124
|
async pollLoop() {
|
|
14465
15125
|
while (this.polling) {
|
|
14466
15126
|
try {
|
|
@@ -14471,6 +15131,10 @@ ${code(prompt.line)}`,
|
|
|
14471
15131
|
});
|
|
14472
15132
|
if (this.stopped)
|
|
14473
15133
|
return;
|
|
15134
|
+
if (this.pollError !== undefined)
|
|
15135
|
+
this.log("polling recovered");
|
|
15136
|
+
this.pollError = undefined;
|
|
15137
|
+
this.pollWarned = false;
|
|
14474
15138
|
for (const update2 of updates) {
|
|
14475
15139
|
this.updateOffset = Math.max(this.updateOffset, update2.update_id + 1);
|
|
14476
15140
|
await this.handleUpdate(update2);
|
|
@@ -14480,11 +15144,29 @@ ${code(prompt.line)}`,
|
|
|
14480
15144
|
} catch (err) {
|
|
14481
15145
|
if (!this.polling)
|
|
14482
15146
|
return;
|
|
14483
|
-
|
|
14484
|
-
|
|
15147
|
+
const reason = String(err);
|
|
15148
|
+
this.pollError = reason;
|
|
15149
|
+
this.log(`poll failed: ${reason}`);
|
|
15150
|
+
if (!this.pollWarned) {
|
|
15151
|
+
this.pollWarned = true;
|
|
15152
|
+
this.warnPollFailure(reason);
|
|
15153
|
+
}
|
|
15154
|
+
await delay4(3000);
|
|
14485
15155
|
}
|
|
14486
15156
|
}
|
|
14487
15157
|
}
|
|
15158
|
+
async warnPollFailure(reason) {
|
|
15159
|
+
const conflict = /\b409\b|terminated by other getUpdates/i.test(reason);
|
|
15160
|
+
const text = conflict ? tHtml("tg.poll.conflict", { machine: this.config.machine.name }) : tHtml("tg.poll.failed", { machine: this.config.machine.name, reason: oneLineReason(reason) });
|
|
15161
|
+
try {
|
|
15162
|
+
await this.api.call("sendMessage", { chat_id: this.config.groupId, text, parse_mode: "HTML" });
|
|
15163
|
+
} catch (err) {
|
|
15164
|
+
this.log(`could not report the poll failure: ${String(err)}`);
|
|
15165
|
+
}
|
|
15166
|
+
}
|
|
15167
|
+
}
|
|
15168
|
+
function oneLineReason(reason) {
|
|
15169
|
+
return reason.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
14488
15170
|
}
|
|
14489
15171
|
var SCREEN_MESSAGE_BUDGET = 2, AGENT_ERROR_CHARS = 1200, RAW_TYPE_LIMIT = 500, RAW_SCREEN_LINES = 12, HELP_KEYS, helpText = () => HELP_KEYS.map((key) => escapeHtml(t(key))).join(`
|
|
14490
15172
|
`), ALBUM_DEBOUNCE_MS = 2000, UNKNOWN_SIZE = 1, REFUSAL_KEYS, megabytes2 = (bytes) => `${Math.round(bytes / (1024 * 1024))} MB`;
|
|
@@ -14521,13 +15203,13 @@ var init_src2 = __esm(() => {
|
|
|
14521
15203
|
});
|
|
14522
15204
|
|
|
14523
15205
|
// packages/daemon/src/machine-identity.ts
|
|
14524
|
-
import
|
|
15206
|
+
import os9 from "os";
|
|
14525
15207
|
function resolveMachineIdentity(override) {
|
|
14526
|
-
const hostname =
|
|
15208
|
+
const hostname = os9.hostname();
|
|
14527
15209
|
return {
|
|
14528
15210
|
name: slugifyMachineName(override && override.trim().length > 0 ? override : hostname),
|
|
14529
15211
|
hostname,
|
|
14530
|
-
addresses: collectAddresses(
|
|
15212
|
+
addresses: collectAddresses(os9.networkInterfaces())
|
|
14531
15213
|
};
|
|
14532
15214
|
}
|
|
14533
15215
|
var init_machine_identity = __esm(() => {
|
|
@@ -14719,6 +15401,9 @@ class TelegramService {
|
|
|
14719
15401
|
problems.push(t("doctor.noUser"));
|
|
14720
15402
|
if (problems.length > 0)
|
|
14721
15403
|
return { ok: false, problems };
|
|
15404
|
+
const pollError = this.channel?.pollHealth();
|
|
15405
|
+
if (pollError !== undefined)
|
|
15406
|
+
problems.push(t("doctor.telegram.pollFailing", { reason: pollError.replace(/\s+/g, " ").slice(0, 160) }));
|
|
14722
15407
|
const api = new FetchTelegramApi(s.botToken);
|
|
14723
15408
|
const report = { ok: true, problems };
|
|
14724
15409
|
try {
|
|
@@ -14759,7 +15444,7 @@ var init_telegram_service = __esm(() => {
|
|
|
14759
15444
|
// packages/daemon/src/index.ts
|
|
14760
15445
|
var exports_src = {};
|
|
14761
15446
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
14762
|
-
import
|
|
15447
|
+
import fs25 from "fs";
|
|
14763
15448
|
import net5 from "net";
|
|
14764
15449
|
function isPidAlive(pid) {
|
|
14765
15450
|
try {
|
|
@@ -14771,9 +15456,9 @@ function isPidAlive(pid) {
|
|
|
14771
15456
|
}
|
|
14772
15457
|
async function main() {
|
|
14773
15458
|
ensureDataDirs();
|
|
14774
|
-
if (
|
|
15459
|
+
if (fs25.existsSync(RUNTIME_FILE2)) {
|
|
14775
15460
|
try {
|
|
14776
|
-
const existing = JSON.parse(
|
|
15461
|
+
const existing = JSON.parse(fs25.readFileSync(RUNTIME_FILE2, "utf8"));
|
|
14777
15462
|
if (existing.pid !== process.pid && isPidAlive(existing.pid)) {
|
|
14778
15463
|
log(`daemon already running (pid ${existing.pid}); exiting`);
|
|
14779
15464
|
process.exit(0);
|
|
@@ -14796,7 +15481,7 @@ async function main() {
|
|
|
14796
15481
|
version: productVersion(),
|
|
14797
15482
|
startedAt: Date.now()
|
|
14798
15483
|
};
|
|
14799
|
-
|
|
15484
|
+
fs25.writeFileSync(RUNTIME_FILE2, JSON.stringify(runtime, null, 2), { mode: 384 });
|
|
14800
15485
|
restrictFile(RUNTIME_FILE2);
|
|
14801
15486
|
const config = new ConfigStore(defaultConfigStorePaths(CONFIG_DIR));
|
|
14802
15487
|
if (config.loadError !== undefined)
|
|
@@ -14848,9 +15533,9 @@ async function main() {
|
|
|
14848
15533
|
hookHost.stop();
|
|
14849
15534
|
store.close();
|
|
14850
15535
|
try {
|
|
14851
|
-
const current = JSON.parse(
|
|
15536
|
+
const current = JSON.parse(fs25.readFileSync(RUNTIME_FILE2, "utf8"));
|
|
14852
15537
|
if (current.pid === process.pid)
|
|
14853
|
-
|
|
15538
|
+
fs25.rmSync(RUNTIME_FILE2);
|
|
14854
15539
|
} catch {}
|
|
14855
15540
|
process.exit(0);
|
|
14856
15541
|
};
|
|
@@ -14869,12 +15554,9 @@ async function main() {
|
|
|
14869
15554
|
});
|
|
14870
15555
|
process.on("SIGINT", () => stop("SIGINT"));
|
|
14871
15556
|
process.on("SIGTERM", () => stop("SIGTERM"));
|
|
14872
|
-
process.on("uncaughtException", (err) => {
|
|
14873
|
-
log(`uncaughtException: ${err.stack ?? String(err)}`);
|
|
14874
|
-
stop("uncaughtException");
|
|
14875
|
-
});
|
|
14876
15557
|
log(`daemon started pid=${process.pid} config=${CONFIG_DIR} state=${STATE_DIR}`);
|
|
14877
15558
|
}
|
|
15559
|
+
var crashGuards;
|
|
14878
15560
|
var init_src3 = __esm(() => {
|
|
14879
15561
|
init_src();
|
|
14880
15562
|
init_config_migrate();
|
|
@@ -14887,7 +15569,12 @@ var init_src3 = __esm(() => {
|
|
|
14887
15569
|
init_telegram_service();
|
|
14888
15570
|
init_store();
|
|
14889
15571
|
net5.setDefaultAutoSelectFamilyAttemptTimeout(3000);
|
|
14890
|
-
|
|
15572
|
+
crashGuards = new ProcessCrashGuards({
|
|
15573
|
+
log,
|
|
15574
|
+
exit: (code2) => process.exit(code2)
|
|
15575
|
+
});
|
|
15576
|
+
crashGuards.install(process);
|
|
15577
|
+
main().catch((reason) => crashGuards.startupFailed(reason));
|
|
14891
15578
|
});
|
|
14892
15579
|
|
|
14893
15580
|
// packages/daemon/src/pty-host.ts
|
|
@@ -15097,8 +15784,8 @@ var init_pty_host_main = __esm(() => {
|
|
|
15097
15784
|
|
|
15098
15785
|
// packages/cli/src/index.ts
|
|
15099
15786
|
init_src();
|
|
15100
|
-
import
|
|
15101
|
-
import
|
|
15787
|
+
import fs26 from "fs";
|
|
15788
|
+
import path25 from "path";
|
|
15102
15789
|
|
|
15103
15790
|
// packages/cli/src/attach.ts
|
|
15104
15791
|
import fs from "fs";
|
|
@@ -15517,11 +16204,11 @@ ${bold(t(state.promptKey))} ${dim("[yes]")} `);
|
|
|
15517
16204
|
const finished = advance({ ...state, kind: "review", step: "review" }, "");
|
|
15518
16205
|
if (!("done" in finished))
|
|
15519
16206
|
return { saved: false };
|
|
15520
|
-
for (const [
|
|
15521
|
-
await client.call("config.set", { path:
|
|
16207
|
+
for (const [path4, value] of finished.done.settings) {
|
|
16208
|
+
await client.call("config.set", { path: path4, value });
|
|
15522
16209
|
}
|
|
15523
|
-
for (const [
|
|
15524
|
-
await client.call("config.set", { path:
|
|
16210
|
+
for (const [path4, value] of finished.done.secrets) {
|
|
16211
|
+
await client.call("config.set", { path: path4, value });
|
|
15525
16212
|
}
|
|
15526
16213
|
stdout.write(`
|
|
15527
16214
|
${green("\u2713")} ${t("wizard.saved")}
|
|
@@ -15559,10 +16246,202 @@ function describeHealth(health) {
|
|
|
15559
16246
|
// packages/cli/src/client.ts
|
|
15560
16247
|
init_src();
|
|
15561
16248
|
import { spawn } from "child_process";
|
|
15562
|
-
import
|
|
16249
|
+
import fs3 from "fs";
|
|
15563
16250
|
import net from "net";
|
|
15564
|
-
import
|
|
16251
|
+
import path5 from "path";
|
|
15565
16252
|
import { setTimeout as delay } from "timers/promises";
|
|
16253
|
+
|
|
16254
|
+
// packages/cli/src/service-manager.ts
|
|
16255
|
+
init_src();
|
|
16256
|
+
import { spawnSync } from "child_process";
|
|
16257
|
+
import fs2 from "fs";
|
|
16258
|
+
import os3 from "os";
|
|
16259
|
+
import path4 from "path";
|
|
16260
|
+
|
|
16261
|
+
class ServiceCommandError extends Error {
|
|
16262
|
+
invocation;
|
|
16263
|
+
result;
|
|
16264
|
+
constructor(invocation, result) {
|
|
16265
|
+
super(result.stderr.trim() || result.stdout.trim() || `${invocation.command} exited ${result.status}`);
|
|
16266
|
+
this.invocation = invocation;
|
|
16267
|
+
this.result = result;
|
|
16268
|
+
this.name = "ServiceCommandError";
|
|
16269
|
+
}
|
|
16270
|
+
}
|
|
16271
|
+
var defaultRun = ({ command, args }) => {
|
|
16272
|
+
const result = spawnSync(command, args, { encoding: "utf8", windowsHide: true });
|
|
16273
|
+
return {
|
|
16274
|
+
status: result.status ?? 1,
|
|
16275
|
+
stdout: result.stdout ?? "",
|
|
16276
|
+
stderr: result.stderr ?? result.error?.message ?? ""
|
|
16277
|
+
};
|
|
16278
|
+
};
|
|
16279
|
+
function writeAtomic(file, contents) {
|
|
16280
|
+
fs2.mkdirSync(path4.dirname(file), { recursive: true, mode: 448 });
|
|
16281
|
+
const tmp = `${file}.tmp`;
|
|
16282
|
+
try {
|
|
16283
|
+
fs2.writeFileSync(tmp, contents, { mode: 384 });
|
|
16284
|
+
fs2.renameSync(tmp, file);
|
|
16285
|
+
if (process.platform !== "win32")
|
|
16286
|
+
fs2.chmodSync(file, 384);
|
|
16287
|
+
} catch (err) {
|
|
16288
|
+
try {
|
|
16289
|
+
fs2.rmSync(tmp, { force: true });
|
|
16290
|
+
} catch {}
|
|
16291
|
+
throw err;
|
|
16292
|
+
}
|
|
16293
|
+
}
|
|
16294
|
+
function utf16Xml(contents) {
|
|
16295
|
+
return Buffer.concat([Buffer.from([255, 254]), Buffer.from(contents, "utf16le")]);
|
|
16296
|
+
}
|
|
16297
|
+
function utf8Script(contents) {
|
|
16298
|
+
return Buffer.concat([Buffer.from([239, 187, 191]), Buffer.from(contents, "utf8")]);
|
|
16299
|
+
}
|
|
16300
|
+
|
|
16301
|
+
class ServiceManager {
|
|
16302
|
+
options;
|
|
16303
|
+
definition;
|
|
16304
|
+
run;
|
|
16305
|
+
uid;
|
|
16306
|
+
constructor(options) {
|
|
16307
|
+
this.options = options;
|
|
16308
|
+
this.run = options.run ?? defaultRun;
|
|
16309
|
+
this.uid = options.uid ?? process.getuid?.();
|
|
16310
|
+
this.definition = serviceDefinition({
|
|
16311
|
+
platform: options.platform,
|
|
16312
|
+
launch: options.launch,
|
|
16313
|
+
homeDir: options.homeDir,
|
|
16314
|
+
stateDir: options.stateDir,
|
|
16315
|
+
...options.userId ? { userId: options.userId } : {},
|
|
16316
|
+
...options.environment ? { environment: options.environment } : {}
|
|
16317
|
+
});
|
|
16318
|
+
}
|
|
16319
|
+
install() {
|
|
16320
|
+
fs2.mkdirSync(path4.join(this.options.stateDir, "logs"), { recursive: true, mode: 448 });
|
|
16321
|
+
for (const companion of this.definition.companionFiles ?? []) {
|
|
16322
|
+
writeAtomic(companion.file, this.definition.kind === "task-scheduler" ? utf8Script(companion.contents) : companion.contents);
|
|
16323
|
+
}
|
|
16324
|
+
writeAtomic(this.definition.file, this.definition.kind === "task-scheduler" ? utf16Xml(this.definition.contents) : this.definition.contents);
|
|
16325
|
+
if (this.definition.kind === "systemd") {
|
|
16326
|
+
this.must({ command: "systemctl", args: ["--user", "daemon-reload"] });
|
|
16327
|
+
this.must({ command: "systemctl", args: ["--user", "enable", SYSTEMD_SERVICE] });
|
|
16328
|
+
} else if (this.definition.kind === "launchd") {
|
|
16329
|
+
this.must({ command: "launchctl", args: ["enable", this.launchdTarget()] });
|
|
16330
|
+
} else {
|
|
16331
|
+
this.must({ command: "schtasks.exe", args: ["/Create", "/TN", WINDOWS_TASK, "/XML", this.definition.file, "/F"] });
|
|
16332
|
+
}
|
|
16333
|
+
}
|
|
16334
|
+
start() {
|
|
16335
|
+
if (this.definition.kind === "systemd") {
|
|
16336
|
+
this.must({ command: "systemctl", args: ["--user", "start", SYSTEMD_SERVICE] });
|
|
16337
|
+
} else if (this.definition.kind === "launchd") {
|
|
16338
|
+
const target = this.launchdTarget();
|
|
16339
|
+
const loaded = this.run({ command: "launchctl", args: ["print", target] }).status === 0;
|
|
16340
|
+
this.must(loaded ? { command: "launchctl", args: ["kickstart", target] } : { command: "launchctl", args: ["bootstrap", `gui/${this.requireUid()}`, this.definition.file] });
|
|
16341
|
+
} else {
|
|
16342
|
+
this.must({ command: "schtasks.exe", args: ["/Run", "/TN", WINDOWS_TASK] });
|
|
16343
|
+
}
|
|
16344
|
+
}
|
|
16345
|
+
status() {
|
|
16346
|
+
if (this.definition.kind === "systemd") {
|
|
16347
|
+
return {
|
|
16348
|
+
kind: this.definition.kind,
|
|
16349
|
+
installed: fs2.existsSync(this.definition.file),
|
|
16350
|
+
active: this.run({ command: "systemctl", args: ["--user", "is-active", "--quiet", SYSTEMD_SERVICE] }).status === 0
|
|
16351
|
+
};
|
|
16352
|
+
}
|
|
16353
|
+
if (this.definition.kind === "launchd") {
|
|
16354
|
+
return {
|
|
16355
|
+
kind: this.definition.kind,
|
|
16356
|
+
installed: fs2.existsSync(this.definition.file),
|
|
16357
|
+
active: this.run({ command: "launchctl", args: ["print", this.launchdTarget()] }).status === 0
|
|
16358
|
+
};
|
|
16359
|
+
}
|
|
16360
|
+
const installed = this.run({ command: "schtasks.exe", args: ["/Query", "/TN", WINDOWS_TASK] }).status === 0;
|
|
16361
|
+
const active = installed && this.run({
|
|
16362
|
+
command: "powershell.exe",
|
|
16363
|
+
args: [
|
|
16364
|
+
"-NoProfile",
|
|
16365
|
+
"-NonInteractive",
|
|
16366
|
+
"-Command",
|
|
16367
|
+
`if ((Get-ScheduledTask -TaskName '${WINDOWS_TASK}').State -eq 'Running') { exit 0 } else { exit 1 }`
|
|
16368
|
+
]
|
|
16369
|
+
}).status === 0;
|
|
16370
|
+
return { kind: this.definition.kind, installed, active };
|
|
16371
|
+
}
|
|
16372
|
+
uninstall() {
|
|
16373
|
+
if (this.definition.kind === "systemd") {
|
|
16374
|
+
this.run({ command: "systemctl", args: ["--user", "disable", SYSTEMD_SERVICE] });
|
|
16375
|
+
fs2.rmSync(this.definition.file, { force: true });
|
|
16376
|
+
this.must({ command: "systemctl", args: ["--user", "daemon-reload"] });
|
|
16377
|
+
} else if (this.definition.kind === "launchd") {
|
|
16378
|
+
this.run({ command: "launchctl", args: ["disable", this.launchdTarget()] });
|
|
16379
|
+
fs2.rmSync(this.definition.file, { force: true });
|
|
16380
|
+
} else {
|
|
16381
|
+
this.run({ command: "schtasks.exe", args: ["/Delete", "/TN", WINDOWS_TASK, "/F"] });
|
|
16382
|
+
fs2.rmSync(this.definition.file, { force: true });
|
|
16383
|
+
}
|
|
16384
|
+
for (const companion of this.definition.companionFiles ?? [])
|
|
16385
|
+
fs2.rmSync(companion.file, { force: true });
|
|
16386
|
+
}
|
|
16387
|
+
must(command) {
|
|
16388
|
+
const result = this.run(command);
|
|
16389
|
+
if (result.status !== 0)
|
|
16390
|
+
throw new ServiceCommandError(command, result);
|
|
16391
|
+
}
|
|
16392
|
+
requireUid() {
|
|
16393
|
+
if (this.uid === undefined)
|
|
16394
|
+
throw new Error("launchd requires the current uid");
|
|
16395
|
+
return this.uid;
|
|
16396
|
+
}
|
|
16397
|
+
launchdTarget() {
|
|
16398
|
+
return `gui/${this.requireUid()}/${LAUNCHD_LABEL}`;
|
|
16399
|
+
}
|
|
16400
|
+
}
|
|
16401
|
+
function windowsUserId(run = defaultRun) {
|
|
16402
|
+
const command = {
|
|
16403
|
+
command: "powershell.exe",
|
|
16404
|
+
args: [
|
|
16405
|
+
"-NoProfile",
|
|
16406
|
+
"-NonInteractive",
|
|
16407
|
+
"-Command",
|
|
16408
|
+
"[System.Security.Principal.WindowsIdentity]::GetCurrent().Name"
|
|
16409
|
+
]
|
|
16410
|
+
};
|
|
16411
|
+
const result = run(command);
|
|
16412
|
+
const value = result.stdout.trim();
|
|
16413
|
+
if (result.status !== 0 || value === "")
|
|
16414
|
+
throw new ServiceCommandError(command, result);
|
|
16415
|
+
return value;
|
|
16416
|
+
}
|
|
16417
|
+
function startInstalledService(manager) {
|
|
16418
|
+
if (!manager.status().installed)
|
|
16419
|
+
return false;
|
|
16420
|
+
manager.start();
|
|
16421
|
+
return true;
|
|
16422
|
+
}
|
|
16423
|
+
function currentServiceManager(run = defaultRun) {
|
|
16424
|
+
const paths = resolvePaths(currentPathEnv());
|
|
16425
|
+
const environment = {};
|
|
16426
|
+
if (process.env["AXTERM_CONFIG_DIR"] === paths.configDir)
|
|
16427
|
+
environment["AXTERM_CONFIG_DIR"] = paths.configDir;
|
|
16428
|
+
if (process.env["AXTERM_STATE_DIR"] === paths.stateDir)
|
|
16429
|
+
environment["AXTERM_STATE_DIR"] = paths.stateDir;
|
|
16430
|
+
if (process.env["AXTERM_RUNTIME_DIR"] === paths.runtimeDir)
|
|
16431
|
+
environment["AXTERM_RUNTIME_DIR"] = paths.runtimeDir;
|
|
16432
|
+
return new ServiceManager({
|
|
16433
|
+
platform: process.platform,
|
|
16434
|
+
homeDir: os3.homedir(),
|
|
16435
|
+
stateDir: paths.stateDir,
|
|
16436
|
+
launch: daemonLaunch({ execPath: process.execPath, mainScript: Bun.main }),
|
|
16437
|
+
...process.getuid ? { uid: process.getuid() } : {},
|
|
16438
|
+
...process.platform === "win32" ? { userId: windowsUserId(run) } : {},
|
|
16439
|
+
...Object.keys(environment).length > 0 ? { environment } : {},
|
|
16440
|
+
run
|
|
16441
|
+
});
|
|
16442
|
+
}
|
|
16443
|
+
|
|
16444
|
+
// packages/cli/src/client.ts
|
|
15566
16445
|
var RUNTIME_FILE = resolvePaths(currentPathEnv()).runtimeFile;
|
|
15567
16446
|
|
|
15568
16447
|
class RpcClient {
|
|
@@ -15648,7 +16527,7 @@ class RpcClient {
|
|
|
15648
16527
|
}
|
|
15649
16528
|
function readRuntime() {
|
|
15650
16529
|
try {
|
|
15651
|
-
const runtime = JSON.parse(
|
|
16530
|
+
const runtime = JSON.parse(fs3.readFileSync(RUNTIME_FILE, "utf8"));
|
|
15652
16531
|
process.kill(runtime.pid, 0);
|
|
15653
16532
|
return runtime;
|
|
15654
16533
|
} catch {
|
|
@@ -15667,11 +16546,14 @@ async function tryConnect() {
|
|
|
15667
16546
|
return;
|
|
15668
16547
|
}
|
|
15669
16548
|
}
|
|
16549
|
+
function connectDaemonIfRunning() {
|
|
16550
|
+
return tryConnect();
|
|
16551
|
+
}
|
|
15670
16552
|
function daemonEnv(cwd, env) {
|
|
15671
16553
|
const files = [];
|
|
15672
16554
|
for (const name of dotenvCandidates(env["NODE_ENV"])) {
|
|
15673
16555
|
try {
|
|
15674
|
-
files.push(parseDotenv(
|
|
16556
|
+
files.push(parseDotenv(fs3.readFileSync(path5.join(cwd, name), "utf8")));
|
|
15675
16557
|
} catch {}
|
|
15676
16558
|
}
|
|
15677
16559
|
return scrubDotenvKeys(env, files);
|
|
@@ -15680,14 +16562,26 @@ async function ensureDaemon() {
|
|
|
15680
16562
|
const existing = await tryConnect();
|
|
15681
16563
|
if (existing)
|
|
15682
16564
|
return existing;
|
|
15683
|
-
|
|
15684
|
-
|
|
15685
|
-
|
|
15686
|
-
|
|
15687
|
-
|
|
15688
|
-
|
|
15689
|
-
|
|
15690
|
-
|
|
16565
|
+
let supervised = false;
|
|
16566
|
+
try {
|
|
16567
|
+
supervised = startInstalledService(currentServiceManager());
|
|
16568
|
+
} catch (err) {
|
|
16569
|
+
if (err instanceof ServiceCommandError) {
|
|
16570
|
+
const command = [err.invocation.command, ...err.invocation.args].join(" ");
|
|
16571
|
+
throw new Error(t("cli.service.commandFailed", { command, error: err.message }));
|
|
16572
|
+
}
|
|
16573
|
+
throw err;
|
|
16574
|
+
}
|
|
16575
|
+
if (!supervised) {
|
|
16576
|
+
const launch = daemonLaunch({ execPath: process.execPath, mainScript: Bun.main });
|
|
16577
|
+
const child = spawn(launch.command, launch.args, {
|
|
16578
|
+
detached: true,
|
|
16579
|
+
stdio: "ignore",
|
|
16580
|
+
windowsHide: true,
|
|
16581
|
+
env: daemonEnv(process.cwd(), process.env)
|
|
16582
|
+
});
|
|
16583
|
+
child.unref();
|
|
16584
|
+
}
|
|
15691
16585
|
const deadline = Date.now() + 15000;
|
|
15692
16586
|
while (Date.now() < deadline) {
|
|
15693
16587
|
await delay(300);
|
|
@@ -15695,12 +16589,12 @@ async function ensureDaemon() {
|
|
|
15695
16589
|
if (client)
|
|
15696
16590
|
return client;
|
|
15697
16591
|
}
|
|
15698
|
-
throw new Error(t("cli.daemon.notReady", { log:
|
|
16592
|
+
throw new Error(t("cli.daemon.notReady", { log: path5.join(resolvePaths(currentPathEnv()).logDir, "daemon.log") }));
|
|
15699
16593
|
}
|
|
15700
16594
|
|
|
15701
16595
|
// packages/cli/src/hook-bridge.ts
|
|
15702
16596
|
init_src();
|
|
15703
|
-
import
|
|
16597
|
+
import fs4 from "fs";
|
|
15704
16598
|
var POST_TIMEOUT_MS = 1500;
|
|
15705
16599
|
function isCodexApproval(agent, body) {
|
|
15706
16600
|
if (agent !== "codex")
|
|
@@ -15753,7 +16647,7 @@ function currentEndpoint() {
|
|
|
15753
16647
|
if (file === undefined)
|
|
15754
16648
|
return;
|
|
15755
16649
|
try {
|
|
15756
|
-
return parseHookEndpoint(
|
|
16650
|
+
return parseHookEndpoint(fs4.readFileSync(file, "utf8"));
|
|
15757
16651
|
} catch {
|
|
15758
16652
|
return;
|
|
15759
16653
|
}
|
|
@@ -15761,8 +16655,8 @@ function currentEndpoint() {
|
|
|
15761
16655
|
|
|
15762
16656
|
// packages/cli/src/mcp-server.ts
|
|
15763
16657
|
init_src();
|
|
15764
|
-
import
|
|
15765
|
-
import
|
|
16658
|
+
import fs5 from "fs";
|
|
16659
|
+
import path6 from "path";
|
|
15766
16660
|
var ASK_TIMEOUT_MS = 10 * 60000;
|
|
15767
16661
|
var POST_TIMEOUT_MS2 = 5000;
|
|
15768
16662
|
var TOOLS = [
|
|
@@ -15878,7 +16772,7 @@ class McpServer {
|
|
|
15878
16772
|
const contextFile = this.io.env[MCP_CONTEXT_ENV];
|
|
15879
16773
|
if (contextFile !== undefined) {
|
|
15880
16774
|
try {
|
|
15881
|
-
const context = JSON.parse(
|
|
16775
|
+
const context = JSON.parse(fs5.readFileSync(contextFile, "utf8"));
|
|
15882
16776
|
if (context.port && context.token && context.sessionId) {
|
|
15883
16777
|
return {
|
|
15884
16778
|
url: `http://127.0.0.1:${context.port}`,
|
|
@@ -15923,7 +16817,7 @@ class McpServer {
|
|
|
15923
16817
|
const target = this.target();
|
|
15924
16818
|
if (!target)
|
|
15925
16819
|
return { text: "axterm is not reachable from this session", isError: true };
|
|
15926
|
-
const resolved =
|
|
16820
|
+
const resolved = path6.isAbsolute(file) ? file : path6.resolve(this.io.cwd, file);
|
|
15927
16821
|
const caption = typeof args["caption"] === "string" ? args["caption"] : undefined;
|
|
15928
16822
|
const res = await this.post("/hook/attach", { sessionId: target.sessionId, launchToken: target.launchToken, path: resolved, caption }, POST_TIMEOUT_MS2);
|
|
15929
16823
|
if (!res)
|
|
@@ -16033,15 +16927,15 @@ function runMcpServer() {
|
|
|
16033
16927
|
|
|
16034
16928
|
// packages/cli/src/update.ts
|
|
16035
16929
|
init_src();
|
|
16036
|
-
import { spawnSync } from "child_process";
|
|
16930
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
16037
16931
|
import { createHash as createHash2 } from "crypto";
|
|
16038
|
-
import
|
|
16039
|
-
import
|
|
16932
|
+
import fs6 from "fs";
|
|
16933
|
+
import path7 from "path";
|
|
16040
16934
|
import { stdin as stdin2, stdout as stdout2 } from "process";
|
|
16041
16935
|
// packages/cli/package.json
|
|
16042
16936
|
var package_default = {
|
|
16043
16937
|
name: "@layorixinc/axterm",
|
|
16044
|
-
version: "0.6.
|
|
16938
|
+
version: "0.6.1",
|
|
16045
16939
|
type: "module",
|
|
16046
16940
|
bin: {
|
|
16047
16941
|
axterm: "dist/cli.js"
|
|
@@ -16115,13 +17009,13 @@ function assetName(platform, arch, isMusl) {
|
|
|
16115
17009
|
function usesMusl() {
|
|
16116
17010
|
if (process.platform !== "linux")
|
|
16117
17011
|
return false;
|
|
16118
|
-
if (
|
|
17012
|
+
if (fs6.existsSync("/etc/alpine-release"))
|
|
16119
17013
|
return true;
|
|
16120
|
-
const ldd =
|
|
17014
|
+
const ldd = spawnSync2("ldd", ["--version"], { encoding: "utf8" });
|
|
16121
17015
|
return `${ldd.stdout ?? ""}${ldd.stderr ?? ""}`.toLowerCase().includes("musl");
|
|
16122
17016
|
}
|
|
16123
17017
|
function firstLine(command, args) {
|
|
16124
|
-
const result =
|
|
17018
|
+
const result = spawnSync2(command, args, { encoding: "utf8", windowsHide: true });
|
|
16125
17019
|
if (result.status !== 0)
|
|
16126
17020
|
return;
|
|
16127
17021
|
return result.stdout?.trim().split(/\r?\n/)[0]?.trim() || undefined;
|
|
@@ -16129,7 +17023,7 @@ function firstLine(command, args) {
|
|
|
16129
17023
|
function detectOrigin(execPath) {
|
|
16130
17024
|
const resolved = (() => {
|
|
16131
17025
|
try {
|
|
16132
|
-
return
|
|
17026
|
+
return fs6.realpathSync(execPath);
|
|
16133
17027
|
} catch {
|
|
16134
17028
|
return execPath;
|
|
16135
17029
|
}
|
|
@@ -16154,7 +17048,7 @@ function npmGlobalBin() {
|
|
|
16154
17048
|
const prefix = firstLine("npm", ["prefix", "-g"]);
|
|
16155
17049
|
if (prefix === undefined)
|
|
16156
17050
|
return;
|
|
16157
|
-
return process.platform === "win32" ? prefix :
|
|
17051
|
+
return process.platform === "win32" ? prefix : path7.join(prefix, "bin");
|
|
16158
17052
|
}
|
|
16159
17053
|
async function download(url, into) {
|
|
16160
17054
|
const response = await fetch(url, { signal: AbortSignal.timeout(600000) });
|
|
@@ -16171,7 +17065,7 @@ function checksumFor(sums, asset) {
|
|
|
16171
17065
|
return;
|
|
16172
17066
|
}
|
|
16173
17067
|
function sha256(file) {
|
|
16174
|
-
return createHash2("sha256").update(
|
|
17068
|
+
return createHash2("sha256").update(fs6.readFileSync(file)).digest("hex");
|
|
16175
17069
|
}
|
|
16176
17070
|
function ask2(question) {
|
|
16177
17071
|
if (stdin2.isTTY !== true)
|
|
@@ -16218,8 +17112,8 @@ async function runUpdate(client, current, options) {
|
|
|
16218
17112
|
}
|
|
16219
17113
|
const asset = assetName(process.platform, process.arch, usesMusl());
|
|
16220
17114
|
const base = `${RELEASES}/v${latest.version}`;
|
|
16221
|
-
const staging =
|
|
16222
|
-
const downloaded =
|
|
17115
|
+
const staging = fs6.mkdtempSync(`${process.execPath}.update-`);
|
|
17116
|
+
const downloaded = path7.join(staging, asset);
|
|
16223
17117
|
try {
|
|
16224
17118
|
console.log(t("cli.update.downloading", { asset }));
|
|
16225
17119
|
await download(`${base}/${asset}`, downloaded);
|
|
@@ -16232,8 +17126,8 @@ async function runUpdate(client, current, options) {
|
|
|
16232
17126
|
return;
|
|
16233
17127
|
}
|
|
16234
17128
|
if (process.platform !== "win32")
|
|
16235
|
-
|
|
16236
|
-
const reported =
|
|
17129
|
+
fs6.chmodSync(downloaded, 493);
|
|
17130
|
+
const reported = spawnSync2(downloaded, ["--version"], { encoding: "utf8" }).stdout?.trim();
|
|
16237
17131
|
if (reported !== latest.version) {
|
|
16238
17132
|
console.error(t("cli.update.wrongVersion", { got: reported ?? "nothing", want: latest.version }));
|
|
16239
17133
|
process.exitCode = 1;
|
|
@@ -16265,14 +17159,14 @@ async function runUpdate(client, current, options) {
|
|
|
16265
17159
|
if (plan.stale > 0)
|
|
16266
17160
|
console.log(t("cli.update.staleDaemon", { count: plan.stale }));
|
|
16267
17161
|
for (const id of plan.resume) {
|
|
16268
|
-
const result =
|
|
17162
|
+
const result = spawnSync2(process.execPath, ["resume", id, "--no-attach", "--no-setup"], { encoding: "utf8" });
|
|
16269
17163
|
if (result.status === 0)
|
|
16270
17164
|
console.log(t("cli.update.restored", { id }));
|
|
16271
17165
|
else
|
|
16272
17166
|
console.log(t("cli.update.restoreFailed", { id, reason: (result.stderr ?? "").trim() || "unknown" }));
|
|
16273
17167
|
}
|
|
16274
17168
|
} finally {
|
|
16275
|
-
|
|
17169
|
+
fs6.rmSync(staging, { recursive: true, force: true });
|
|
16276
17170
|
}
|
|
16277
17171
|
}
|
|
16278
17172
|
function applySwap(target, downloaded) {
|
|
@@ -16281,20 +17175,88 @@ function applySwap(target, downloaded) {
|
|
|
16281
17175
|
try {
|
|
16282
17176
|
for (const step of steps) {
|
|
16283
17177
|
if (step.op === "chmod")
|
|
16284
|
-
|
|
17178
|
+
fs6.chmodSync(step.from, 493);
|
|
16285
17179
|
else if (step.op === "backup") {
|
|
16286
|
-
|
|
17180
|
+
fs6.renameSync(step.from, step.to);
|
|
16287
17181
|
backup = step.to;
|
|
16288
17182
|
} else
|
|
16289
|
-
|
|
17183
|
+
fs6.renameSync(step.from, step.to);
|
|
16290
17184
|
}
|
|
16291
17185
|
} catch (err) {
|
|
16292
|
-
if (backup !== undefined && !
|
|
16293
|
-
|
|
17186
|
+
if (backup !== undefined && !fs6.existsSync(target))
|
|
17187
|
+
fs6.renameSync(backup, target);
|
|
16294
17188
|
throw err;
|
|
16295
17189
|
}
|
|
16296
17190
|
}
|
|
16297
17191
|
|
|
17192
|
+
// packages/cli/src/service-command.ts
|
|
17193
|
+
init_src();
|
|
17194
|
+
import { setTimeout as delay2 } from "timers/promises";
|
|
17195
|
+
var SERVICE_WAIT_MS = 15000;
|
|
17196
|
+
var SERVICE_POLL_MS = 200;
|
|
17197
|
+
async function waitForDaemon(deps, running) {
|
|
17198
|
+
const sleep = deps.sleep ?? ((ms) => delay2(ms));
|
|
17199
|
+
const now = deps.now ?? Date.now;
|
|
17200
|
+
const deadline = now() + SERVICE_WAIT_MS;
|
|
17201
|
+
for (;; ) {
|
|
17202
|
+
const connected = await deps.connect();
|
|
17203
|
+
if (running && connected !== undefined)
|
|
17204
|
+
return connected;
|
|
17205
|
+
connected?.close();
|
|
17206
|
+
if (!running && connected === undefined)
|
|
17207
|
+
return;
|
|
17208
|
+
if (now() >= deadline)
|
|
17209
|
+
throw new Error(t("cli.service.notReady"));
|
|
17210
|
+
await sleep(SERVICE_POLL_MS);
|
|
17211
|
+
}
|
|
17212
|
+
}
|
|
17213
|
+
function localizedStatus(status) {
|
|
17214
|
+
return t("cli.service.status", {
|
|
17215
|
+
kind: status.kind,
|
|
17216
|
+
installed: t(status.installed ? "cli.service.yes" : "cli.service.no"),
|
|
17217
|
+
active: t(status.active ? "cli.service.yes" : "cli.service.no")
|
|
17218
|
+
});
|
|
17219
|
+
}
|
|
17220
|
+
function localizeCommandError(err) {
|
|
17221
|
+
if (err instanceof ServiceCommandError) {
|
|
17222
|
+
const command = [err.invocation.command, ...err.invocation.args].join(" ");
|
|
17223
|
+
throw new Error(t("cli.service.commandFailed", { command, error: err.message }));
|
|
17224
|
+
}
|
|
17225
|
+
throw err;
|
|
17226
|
+
}
|
|
17227
|
+
async function runServiceCommand(subcommand, deps) {
|
|
17228
|
+
const write = deps.write ?? console.log;
|
|
17229
|
+
try {
|
|
17230
|
+
if (subcommand === "status") {
|
|
17231
|
+
write(localizedStatus(deps.manager.status()));
|
|
17232
|
+
return;
|
|
17233
|
+
}
|
|
17234
|
+
if (subcommand === "uninstall") {
|
|
17235
|
+
deps.manager.uninstall();
|
|
17236
|
+
write(t("cli.service.uninstalled"));
|
|
17237
|
+
return;
|
|
17238
|
+
}
|
|
17239
|
+
if (subcommand !== "install")
|
|
17240
|
+
throw new Error(t("cli.usage.service"));
|
|
17241
|
+
deps.manager.install();
|
|
17242
|
+
const existing = await deps.connect();
|
|
17243
|
+
if (existing !== undefined) {
|
|
17244
|
+
try {
|
|
17245
|
+
await existing.call("daemon.stop");
|
|
17246
|
+
} finally {
|
|
17247
|
+
existing.close();
|
|
17248
|
+
}
|
|
17249
|
+
await waitForDaemon(deps, false);
|
|
17250
|
+
}
|
|
17251
|
+
deps.manager.start();
|
|
17252
|
+
const ready = await waitForDaemon(deps, true);
|
|
17253
|
+
ready?.close();
|
|
17254
|
+
write(t("cli.service.installed", { kind: deps.manager.definition.kind }));
|
|
17255
|
+
} catch (err) {
|
|
17256
|
+
localizeCommandError(err);
|
|
17257
|
+
}
|
|
17258
|
+
}
|
|
17259
|
+
|
|
16298
17260
|
// packages/cli/src/index.ts
|
|
16299
17261
|
var VERSION = package_default.version;
|
|
16300
17262
|
var VALUE_FLAGS = new Set([
|
|
@@ -16363,7 +17325,7 @@ function printSessionTable(sessions) {
|
|
|
16363
17325
|
console.log(row.map((cell, col) => cell.padEnd(widths[col])).join(" "));
|
|
16364
17326
|
}
|
|
16365
17327
|
}
|
|
16366
|
-
var realPath = (target) =>
|
|
17328
|
+
var realPath = (target) => fs26.realpathSync.native(target);
|
|
16367
17329
|
function scopedHere(sessions) {
|
|
16368
17330
|
return sessionsInScope(sessions, process.cwd(), realPath);
|
|
16369
17331
|
}
|
|
@@ -16376,7 +17338,7 @@ var help = () => t("cli.help");
|
|
|
16376
17338
|
function readDotEnv(file = ".env") {
|
|
16377
17339
|
const out = {};
|
|
16378
17340
|
try {
|
|
16379
|
-
for (const line of
|
|
17341
|
+
for (const line of fs26.readFileSync(file, "utf8").split(/\r?\n/)) {
|
|
16380
17342
|
const m = line.match(/^([A-Z0-9_]+)=(.*)$/);
|
|
16381
17343
|
if (m)
|
|
16382
17344
|
out[m[1]] = m[2].trim();
|
|
@@ -16387,7 +17349,7 @@ function readDotEnv(file = ".env") {
|
|
|
16387
17349
|
function configuredLocale() {
|
|
16388
17350
|
try {
|
|
16389
17351
|
const file = resolvePaths(currentPathEnv()).configFile;
|
|
16390
|
-
const raw = JSON.parse(
|
|
17352
|
+
const raw = JSON.parse(fs26.readFileSync(file, "utf8"));
|
|
16391
17353
|
return typeof raw.locale === "string" ? raw.locale : undefined;
|
|
16392
17354
|
} catch {
|
|
16393
17355
|
return;
|
|
@@ -16424,6 +17386,13 @@ async function main2() {
|
|
|
16424
17386
|
console.log(help());
|
|
16425
17387
|
return;
|
|
16426
17388
|
}
|
|
17389
|
+
if (command === "service") {
|
|
17390
|
+
await runServiceCommand(positional[0] ?? "status", {
|
|
17391
|
+
manager: currentServiceManager(),
|
|
17392
|
+
connect: connectDaemonIfRunning
|
|
17393
|
+
});
|
|
17394
|
+
return;
|
|
17395
|
+
}
|
|
16427
17396
|
const client = await ensureDaemon();
|
|
16428
17397
|
const health = await client.call("config.health").catch(() => {
|
|
16429
17398
|
return;
|
|
@@ -16538,12 +17507,10 @@ async function main2() {
|
|
|
16538
17507
|
if (!id || indexArg === undefined)
|
|
16539
17508
|
throw new Error(t("cli.usage.choose"));
|
|
16540
17509
|
const index = Number(indexArg);
|
|
16541
|
-
const answered = await client.call("session.choose", { id, index }).catch(
|
|
17510
|
+
const answered = await client.call("session.choose", { id, index }).catch((err) => {
|
|
16542
17511
|
if (!/unknown method/i.test(String(err)))
|
|
16543
17512
|
throw err;
|
|
16544
|
-
|
|
16545
|
-
await client.call("terminal.write", { id, dataB64: Buffer.from(keys2, "utf8").toString("base64") });
|
|
16546
|
-
return { answered: "menu" };
|
|
17513
|
+
throw new Error(t("cli.choose.daemonTooOld"));
|
|
16547
17514
|
});
|
|
16548
17515
|
console.log(t("cli.session.selected", { index: indexArg }));
|
|
16549
17516
|
if (answered.answered === "onboarding")
|
|
@@ -16623,8 +17590,8 @@ async function main2() {
|
|
|
16623
17590
|
case "project": {
|
|
16624
17591
|
const sub = positional[0] ?? "list";
|
|
16625
17592
|
if (sub === "add") {
|
|
16626
|
-
const dir = positional[1] ?
|
|
16627
|
-
const alias = positional[2] ??
|
|
17593
|
+
const dir = positional[1] ? path25.resolve(positional[1]) : process.cwd();
|
|
17594
|
+
const alias = positional[2] ?? path25.basename(dir);
|
|
16628
17595
|
const projects = await client.call("project.add", {
|
|
16629
17596
|
alias,
|
|
16630
17597
|
path: dir
|