@hydra-acp/cli 0.1.31 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/cli.js +381 -111
- package/dist/index.js +15 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -362,7 +362,9 @@ The service token lives in its own file (`~/.hydra-acp/auth-token`, mode 0600) a
|
|
|
362
362
|
|
|
363
363
|
`daemon.sessionRecentMinutes` (default 30) controls how far back `hydra-acp session` (and the `/v1/sessions` REST endpoint without `?all=true`) looks for cold (disk-only) sessions. Set to `0` to never list cold sessions.
|
|
364
364
|
|
|
365
|
-
`tui.mouse` (default `
|
|
365
|
+
`tui.mouse` (default `false`) controls whether the TUI captures mouse events. With capture off (the default), plain click-drag selects text via your terminal emulator, but wheel-driven scrollback stops working — use `PgUp` / `PgDn` instead. Set to `true` to enable capture, which lets the scroll wheel drive scrollback at the cost of requiring `shift+drag` to select text.
|
|
366
|
+
|
|
367
|
+
`tui.defaultEnterAction` (default `"amend"`) controls what the unmodified Enter key does in the prompt composer. With `"amend"` (the default), Enter amends the in-flight turn and `Shift+Enter` enqueues a new prompt; with no turn in flight either key just enqueues, since there's nothing to amend. Set to `"enqueue"` to flip the two: Enter enqueues (sends immediately when idle, queues behind an in-flight turn) and `Shift+Enter` amends.
|
|
366
368
|
|
|
367
369
|
### Extensions
|
|
368
370
|
|
package/dist/cli.js
CHANGED
|
@@ -271,12 +271,12 @@ var init_config = __esm({
|
|
|
271
271
|
// buffer. Oldest lines are dropped on overflow. The on-disk session
|
|
272
272
|
// history is unaffected; this only bounds the TUI's local view buffer.
|
|
273
273
|
maxScrollbackLines: z.number().int().positive().default(1e4),
|
|
274
|
-
// When true
|
|
275
|
-
//
|
|
276
|
-
//
|
|
277
|
-
// false
|
|
278
|
-
//
|
|
279
|
-
mouse: z.boolean().default(
|
|
274
|
+
// When true, the TUI captures mouse events so the wheel can drive
|
|
275
|
+
// scrollback. The cost: terminals route clicks to the app, so text
|
|
276
|
+
// selection requires shift+drag to bypass mouse reporting. Default
|
|
277
|
+
// false — wheel scrollback stops working, but plain click-drag
|
|
278
|
+
// selects text via the terminal emulator. Set true to opt back in.
|
|
279
|
+
mouse: z.boolean().default(false),
|
|
280
280
|
// Size at which the TUI's session/update debug log (tui.log) rotates
|
|
281
281
|
// to tui.log.0 and resets. Bounds on-disk use at ~2x this value.
|
|
282
282
|
logMaxBytes: z.number().int().positive().default(5 * 1024 * 1024),
|
|
@@ -291,13 +291,13 @@ var init_config = __esm({
|
|
|
291
291
|
// just don't want it.
|
|
292
292
|
progressIndicator: z.boolean().default(true),
|
|
293
293
|
// What the unmodified Enter key does in the prompt composer.
|
|
294
|
-
// "
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
// "
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
defaultEnterAction: z.enum(["enqueue", "amend"]).default("
|
|
294
|
+
// "amend" (default) — Enter amends the in-flight turn; Shift+Enter
|
|
295
|
+
// enqueues. With no turn in flight either key just enqueues,
|
|
296
|
+
// since there's nothing to amend.
|
|
297
|
+
// "enqueue" — flips the two: Enter enqueues the prompt (sends
|
|
298
|
+
// immediately when idle, queues behind an in-flight turn);
|
|
299
|
+
// Shift+Enter amends the in-flight turn.
|
|
300
|
+
defaultEnterAction: z.enum(["enqueue", "amend"]).default("amend")
|
|
301
301
|
});
|
|
302
302
|
ExtensionName = z.string().min(1).regex(/^[A-Za-z0-9._-]+$/, "extension name must be filename-safe");
|
|
303
303
|
ExtensionBody = z.object({
|
|
@@ -338,11 +338,11 @@ var init_config = __esm({
|
|
|
338
338
|
tui: TuiConfig.default({
|
|
339
339
|
repaintThrottleMs: 1e3,
|
|
340
340
|
maxScrollbackLines: 1e4,
|
|
341
|
-
mouse:
|
|
341
|
+
mouse: false,
|
|
342
342
|
logMaxBytes: 5 * 1024 * 1024,
|
|
343
343
|
cwdColumnMaxWidth: 24,
|
|
344
344
|
progressIndicator: true,
|
|
345
|
-
defaultEnterAction: "
|
|
345
|
+
defaultEnterAction: "amend"
|
|
346
346
|
})
|
|
347
347
|
});
|
|
348
348
|
}
|
|
@@ -819,9 +819,9 @@ var init_connection = __esm({
|
|
|
819
819
|
}
|
|
820
820
|
const id = nanoid();
|
|
821
821
|
const message = { jsonrpc: "2.0", id, method, params };
|
|
822
|
-
const response = new Promise((
|
|
822
|
+
const response = new Promise((resolve6, reject) => {
|
|
823
823
|
this.pending.set(id, {
|
|
824
|
-
resolve: (result) =>
|
|
824
|
+
resolve: (result) => resolve6(result),
|
|
825
825
|
reject
|
|
826
826
|
});
|
|
827
827
|
this.stream.send(message).catch((err) => {
|
|
@@ -3074,7 +3074,7 @@ _(switched from \`${oldAgentId}\` to \`${newAgentId}\`)_
|
|
|
3074
3074
|
}
|
|
3075
3075
|
const clientParams = this.rewriteForClient(params);
|
|
3076
3076
|
const toolCallId = extractToolCallId(clientParams);
|
|
3077
|
-
return new Promise((
|
|
3077
|
+
return new Promise((resolve6, reject) => {
|
|
3078
3078
|
let settled = false;
|
|
3079
3079
|
const outbound = [];
|
|
3080
3080
|
const entry = { addClient: sendTo };
|
|
@@ -3113,7 +3113,7 @@ _(switched from \`${oldAgentId}\` to \`${newAgentId}\`)_
|
|
|
3113
3113
|
update
|
|
3114
3114
|
}).catch(() => void 0);
|
|
3115
3115
|
}
|
|
3116
|
-
|
|
3116
|
+
resolve6(result);
|
|
3117
3117
|
});
|
|
3118
3118
|
}).catch((err) => {
|
|
3119
3119
|
settle(() => reject(err));
|
|
@@ -3129,14 +3129,14 @@ _(switched from \`${oldAgentId}\` to \`${newAgentId}\`)_
|
|
|
3129
3129
|
// in flight, but doesn't emit prompt_queue_* broadcasts — clients
|
|
3130
3130
|
// shouldn't see hydra's housekeeping in their chip list.
|
|
3131
3131
|
async enqueuePrompt(task) {
|
|
3132
|
-
return new Promise((
|
|
3132
|
+
return new Promise((resolve6, reject) => {
|
|
3133
3133
|
const entry = {
|
|
3134
3134
|
kind: "internal",
|
|
3135
3135
|
messageId: generateMessageId(),
|
|
3136
3136
|
enqueuedAt: Date.now(),
|
|
3137
3137
|
cancelled: false,
|
|
3138
3138
|
task,
|
|
3139
|
-
resolve:
|
|
3139
|
+
resolve: resolve6,
|
|
3140
3140
|
reject
|
|
3141
3141
|
};
|
|
3142
3142
|
this.promptQueue.push(entry);
|
|
@@ -3155,7 +3155,7 @@ _(switched from \`${oldAgentId}\` to \`${newAgentId}\`)_
|
|
|
3155
3155
|
if (client.clientInfo?.name) originator.name = client.clientInfo.name;
|
|
3156
3156
|
if (client.clientInfo?.version)
|
|
3157
3157
|
originator.version = client.clientInfo.version;
|
|
3158
|
-
return new Promise((
|
|
3158
|
+
return new Promise((resolve6, reject) => {
|
|
3159
3159
|
const entry = {
|
|
3160
3160
|
kind: "user",
|
|
3161
3161
|
messageId,
|
|
@@ -3164,7 +3164,7 @@ _(switched from \`${oldAgentId}\` to \`${newAgentId}\`)_
|
|
|
3164
3164
|
prompt: promptArray,
|
|
3165
3165
|
enqueuedAt: Date.now(),
|
|
3166
3166
|
cancelled: false,
|
|
3167
|
-
resolve:
|
|
3167
|
+
resolve: resolve6,
|
|
3168
3168
|
reject
|
|
3169
3169
|
};
|
|
3170
3170
|
this.promptQueue.push(entry);
|
|
@@ -4352,13 +4352,13 @@ function wsToMessageStream(ws) {
|
|
|
4352
4352
|
throw new Error("ws is closed");
|
|
4353
4353
|
}
|
|
4354
4354
|
const text = JSON.stringify(message);
|
|
4355
|
-
await new Promise((
|
|
4355
|
+
await new Promise((resolve6, reject) => {
|
|
4356
4356
|
ws.send(text, (err) => {
|
|
4357
4357
|
if (err) {
|
|
4358
4358
|
reject(err);
|
|
4359
4359
|
return;
|
|
4360
4360
|
}
|
|
4361
|
-
|
|
4361
|
+
resolve6();
|
|
4362
4362
|
});
|
|
4363
4363
|
});
|
|
4364
4364
|
},
|
|
@@ -4846,8 +4846,8 @@ async function runSessionsTranscript(idOrFile, outPath) {
|
|
|
4846
4846
|
}
|
|
4847
4847
|
async function readBundleFileIfExists(arg) {
|
|
4848
4848
|
try {
|
|
4849
|
-
const
|
|
4850
|
-
if (!
|
|
4849
|
+
const stat5 = await fs17.stat(arg);
|
|
4850
|
+
if (!stat5.isFile()) {
|
|
4851
4851
|
return null;
|
|
4852
4852
|
}
|
|
4853
4853
|
} catch {
|
|
@@ -4882,8 +4882,8 @@ async function runSessionsImport(file, opts = {}) {
|
|
|
4882
4882
|
if (opts.cwd !== void 0) {
|
|
4883
4883
|
const resolved = path11.resolve(opts.cwd);
|
|
4884
4884
|
try {
|
|
4885
|
-
const
|
|
4886
|
-
if (!
|
|
4885
|
+
const stat5 = await fs17.stat(resolved);
|
|
4886
|
+
if (!stat5.isDirectory()) {
|
|
4887
4887
|
process.stderr.write(`--cwd ${resolved} is not a directory
|
|
4888
4888
|
`);
|
|
4889
4889
|
process.exit(1);
|
|
@@ -5029,11 +5029,11 @@ function isResponse(msg) {
|
|
|
5029
5029
|
return !("method" in msg) && "id" in msg && msg.id !== void 0;
|
|
5030
5030
|
}
|
|
5031
5031
|
async function openWs(url, subprotocols) {
|
|
5032
|
-
return new Promise((
|
|
5032
|
+
return new Promise((resolve6, reject) => {
|
|
5033
5033
|
const ws = new WebSocket(url, subprotocols);
|
|
5034
5034
|
const onOpen = () => {
|
|
5035
5035
|
ws.off("error", onError);
|
|
5036
|
-
|
|
5036
|
+
resolve6(wsToMessageStream(ws));
|
|
5037
5037
|
};
|
|
5038
5038
|
const onError = (err) => {
|
|
5039
5039
|
ws.off("open", onOpen);
|
|
@@ -5104,8 +5104,8 @@ var init_resilient_ws = __esm({
|
|
|
5104
5104
|
throw new Error("resilient ws stream not connected");
|
|
5105
5105
|
}
|
|
5106
5106
|
const id = message.id;
|
|
5107
|
-
const promise = new Promise((
|
|
5108
|
-
this.pendingRequests.set(id, { resolve:
|
|
5107
|
+
const promise = new Promise((resolve6, reject) => {
|
|
5108
|
+
this.pendingRequests.set(id, { resolve: resolve6, reject });
|
|
5109
5109
|
});
|
|
5110
5110
|
try {
|
|
5111
5111
|
await this.current.send(message);
|
|
@@ -5133,8 +5133,8 @@ var init_resilient_ws = __esm({
|
|
|
5133
5133
|
this.bindStream(stream);
|
|
5134
5134
|
const wasFirst = this.firstConnect;
|
|
5135
5135
|
this.firstConnect = false;
|
|
5136
|
-
this.connectGate = new Promise((
|
|
5137
|
-
this.releaseConnectGate =
|
|
5136
|
+
this.connectGate = new Promise((resolve6) => {
|
|
5137
|
+
this.releaseConnectGate = resolve6;
|
|
5138
5138
|
});
|
|
5139
5139
|
try {
|
|
5140
5140
|
if (this.opts.onConnect) {
|
|
@@ -5667,7 +5667,7 @@ async function pickSession(term, opts) {
|
|
|
5667
5667
|
};
|
|
5668
5668
|
renderFromScratch();
|
|
5669
5669
|
term.hideCursor();
|
|
5670
|
-
return await new Promise((
|
|
5670
|
+
return await new Promise((resolve6) => {
|
|
5671
5671
|
let resolved = false;
|
|
5672
5672
|
const onResize = () => {
|
|
5673
5673
|
if (resolved) {
|
|
@@ -5807,7 +5807,7 @@ async function pickSession(term, opts) {
|
|
|
5807
5807
|
if (mode === "help") {
|
|
5808
5808
|
if (name === "CTRL_C") {
|
|
5809
5809
|
cleanup();
|
|
5810
|
-
|
|
5810
|
+
resolve6({ kind: "abort" });
|
|
5811
5811
|
return;
|
|
5812
5812
|
}
|
|
5813
5813
|
mode = "normal";
|
|
@@ -5921,12 +5921,12 @@ async function pickSession(term, opts) {
|
|
|
5921
5921
|
}
|
|
5922
5922
|
if (name === "c" || name === "C") {
|
|
5923
5923
|
cleanup();
|
|
5924
|
-
|
|
5924
|
+
resolve6({ kind: "new" });
|
|
5925
5925
|
return;
|
|
5926
5926
|
}
|
|
5927
5927
|
if (name === "q" || name === "Q") {
|
|
5928
5928
|
cleanup();
|
|
5929
|
-
|
|
5929
|
+
resolve6({ kind: "abort" });
|
|
5930
5930
|
return;
|
|
5931
5931
|
}
|
|
5932
5932
|
if (name === "o" || name === "O") {
|
|
@@ -5976,7 +5976,7 @@ async function pickSession(term, opts) {
|
|
|
5976
5976
|
if (session.agentId !== void 0) {
|
|
5977
5977
|
result.agentId = session.agentId;
|
|
5978
5978
|
}
|
|
5979
|
-
|
|
5979
|
+
resolve6(result);
|
|
5980
5980
|
return;
|
|
5981
5981
|
}
|
|
5982
5982
|
if ((name === "k" || name === "K") && selectedIdx > 0) {
|
|
@@ -6062,12 +6062,12 @@ async function pickSession(term, opts) {
|
|
|
6062
6062
|
case "KP_ENTER": {
|
|
6063
6063
|
cleanup();
|
|
6064
6064
|
if (selectedIdx === 0) {
|
|
6065
|
-
|
|
6065
|
+
resolve6({ kind: "new" });
|
|
6066
6066
|
return;
|
|
6067
6067
|
}
|
|
6068
6068
|
const session = visible[selectedIdx - 1];
|
|
6069
6069
|
if (!session) {
|
|
6070
|
-
|
|
6070
|
+
resolve6({ kind: "abort" });
|
|
6071
6071
|
return;
|
|
6072
6072
|
}
|
|
6073
6073
|
const result = {
|
|
@@ -6077,14 +6077,14 @@ async function pickSession(term, opts) {
|
|
|
6077
6077
|
if (session.agentId !== void 0) {
|
|
6078
6078
|
result.agentId = session.agentId;
|
|
6079
6079
|
}
|
|
6080
|
-
|
|
6080
|
+
resolve6(result);
|
|
6081
6081
|
return;
|
|
6082
6082
|
}
|
|
6083
6083
|
case "ESCAPE":
|
|
6084
6084
|
case "CTRL_C":
|
|
6085
6085
|
case "CTRL_D":
|
|
6086
6086
|
cleanup();
|
|
6087
|
-
|
|
6087
|
+
resolve6({ kind: "abort" });
|
|
6088
6088
|
return;
|
|
6089
6089
|
}
|
|
6090
6090
|
};
|
|
@@ -6185,10 +6185,184 @@ var init_picker = __esm({
|
|
|
6185
6185
|
}
|
|
6186
6186
|
});
|
|
6187
6187
|
|
|
6188
|
+
// src/core/cwd.ts
|
|
6189
|
+
import * as fs18 from "fs/promises";
|
|
6190
|
+
import * as path12 from "path";
|
|
6191
|
+
async function validateLocalCwd(input) {
|
|
6192
|
+
const trimmed = input.trim();
|
|
6193
|
+
if (trimmed.length === 0) {
|
|
6194
|
+
return { ok: false, reason: "path is empty" };
|
|
6195
|
+
}
|
|
6196
|
+
const resolved = path12.resolve(expandHome(trimmed));
|
|
6197
|
+
let stat5;
|
|
6198
|
+
try {
|
|
6199
|
+
stat5 = await fs18.stat(resolved);
|
|
6200
|
+
} catch {
|
|
6201
|
+
return { ok: false, reason: `${resolved} does not exist` };
|
|
6202
|
+
}
|
|
6203
|
+
if (!stat5.isDirectory()) {
|
|
6204
|
+
return { ok: false, reason: `${resolved} is not a directory` };
|
|
6205
|
+
}
|
|
6206
|
+
return { ok: true, path: resolved };
|
|
6207
|
+
}
|
|
6208
|
+
var init_cwd = __esm({
|
|
6209
|
+
"src/core/cwd.ts"() {
|
|
6210
|
+
"use strict";
|
|
6211
|
+
init_config();
|
|
6212
|
+
}
|
|
6213
|
+
});
|
|
6214
|
+
|
|
6215
|
+
// src/tui/import-cwd-prompt.ts
|
|
6216
|
+
import * as os4 from "os";
|
|
6217
|
+
async function promptForImportCwd(term, session, opts = {}) {
|
|
6218
|
+
const defaultCwd = opts.defaultCwd ?? os4.homedir();
|
|
6219
|
+
process.stdout.write("\x1B[<u");
|
|
6220
|
+
process.stdout.write("\x1B[?2004l");
|
|
6221
|
+
process.stdout.write("\x1B[>4;0m");
|
|
6222
|
+
process.stdout.write("\x1B[>5;0m");
|
|
6223
|
+
process.stdout.write("\x1B[?1000l");
|
|
6224
|
+
process.stdout.write("\x1B[?1002l");
|
|
6225
|
+
process.stdout.write("\x1B[?1006l");
|
|
6226
|
+
process.stdout.write("\x1B[?1l");
|
|
6227
|
+
process.stdout.write("\x1B>");
|
|
6228
|
+
const shortId2 = stripHydraSessionPrefix(session.sessionId);
|
|
6229
|
+
const fromMachine = session.importedFromMachine ?? "another machine";
|
|
6230
|
+
const originalCwd = session.cwd;
|
|
6231
|
+
let buffer = defaultCwd;
|
|
6232
|
+
let errorLine = null;
|
|
6233
|
+
let busy = false;
|
|
6234
|
+
const render = () => {
|
|
6235
|
+
term("\n");
|
|
6236
|
+
term.bold.cyan("Imported session: ");
|
|
6237
|
+
term(`${shortId2}
|
|
6238
|
+
`);
|
|
6239
|
+
term.dim(` from machine: `);
|
|
6240
|
+
term(`${fromMachine}
|
|
6241
|
+
`);
|
|
6242
|
+
term.dim(` original cwd: `);
|
|
6243
|
+
term(`${shortenHomePath(originalCwd)}
|
|
6244
|
+
`);
|
|
6245
|
+
term("\n");
|
|
6246
|
+
term(
|
|
6247
|
+
"This session has never been launched on this machine. Pick a local\n"
|
|
6248
|
+
);
|
|
6249
|
+
term("cwd for the agent (Enter to accept, Esc to cancel):\n\n");
|
|
6250
|
+
paintInput();
|
|
6251
|
+
if (errorLine) {
|
|
6252
|
+
term("\n");
|
|
6253
|
+
term.red(` ${errorLine}
|
|
6254
|
+
`);
|
|
6255
|
+
}
|
|
6256
|
+
};
|
|
6257
|
+
const paintInput = () => {
|
|
6258
|
+
term.bold("cwd: ");
|
|
6259
|
+
term(buffer);
|
|
6260
|
+
if (!busy) {
|
|
6261
|
+
term.bgWhite(" ");
|
|
6262
|
+
}
|
|
6263
|
+
};
|
|
6264
|
+
const repaintInput = () => {
|
|
6265
|
+
term.column(1);
|
|
6266
|
+
term.eraseLine();
|
|
6267
|
+
paintInput();
|
|
6268
|
+
if (errorLine !== null) {
|
|
6269
|
+
term("\n");
|
|
6270
|
+
term.eraseLine();
|
|
6271
|
+
term.red(` ${errorLine}`);
|
|
6272
|
+
term.up(1);
|
|
6273
|
+
term.column(1);
|
|
6274
|
+
}
|
|
6275
|
+
};
|
|
6276
|
+
render();
|
|
6277
|
+
return await new Promise((resolve6) => {
|
|
6278
|
+
let resolved = false;
|
|
6279
|
+
const cleanup = () => {
|
|
6280
|
+
if (resolved) {
|
|
6281
|
+
return;
|
|
6282
|
+
}
|
|
6283
|
+
resolved = true;
|
|
6284
|
+
term.off("key", onKey);
|
|
6285
|
+
term.grabInput(false);
|
|
6286
|
+
term.hideCursor(false);
|
|
6287
|
+
term("\n\n");
|
|
6288
|
+
};
|
|
6289
|
+
const finish = (value) => {
|
|
6290
|
+
cleanup();
|
|
6291
|
+
resolve6(value);
|
|
6292
|
+
};
|
|
6293
|
+
const onKey = (name, _matches, data) => {
|
|
6294
|
+
if (busy) {
|
|
6295
|
+
return;
|
|
6296
|
+
}
|
|
6297
|
+
if (name === "ENTER" || name === "KP_ENTER") {
|
|
6298
|
+
const candidate = buffer;
|
|
6299
|
+
busy = true;
|
|
6300
|
+
errorLine = null;
|
|
6301
|
+
repaintInput();
|
|
6302
|
+
void validateLocalCwd(candidate).then((result) => {
|
|
6303
|
+
busy = false;
|
|
6304
|
+
if (result.ok) {
|
|
6305
|
+
finish(result.path);
|
|
6306
|
+
return;
|
|
6307
|
+
}
|
|
6308
|
+
errorLine = result.reason;
|
|
6309
|
+
repaintInput();
|
|
6310
|
+
});
|
|
6311
|
+
return;
|
|
6312
|
+
}
|
|
6313
|
+
if (name === "ESCAPE" || name === "CTRL_C" || name === "CTRL_D") {
|
|
6314
|
+
finish(null);
|
|
6315
|
+
return;
|
|
6316
|
+
}
|
|
6317
|
+
if (name === "BACKSPACE") {
|
|
6318
|
+
if (buffer.length > 0) {
|
|
6319
|
+
buffer = buffer.slice(0, -1);
|
|
6320
|
+
errorLine = null;
|
|
6321
|
+
repaintInput();
|
|
6322
|
+
}
|
|
6323
|
+
return;
|
|
6324
|
+
}
|
|
6325
|
+
if (name === "CTRL_U") {
|
|
6326
|
+
buffer = "";
|
|
6327
|
+
errorLine = null;
|
|
6328
|
+
repaintInput();
|
|
6329
|
+
return;
|
|
6330
|
+
}
|
|
6331
|
+
if (name === "CTRL_W") {
|
|
6332
|
+
const trimmedRight = buffer.replace(/[/\s]+$/, "");
|
|
6333
|
+
const lastSep = Math.max(
|
|
6334
|
+
trimmedRight.lastIndexOf("/"),
|
|
6335
|
+
trimmedRight.lastIndexOf(" ")
|
|
6336
|
+
);
|
|
6337
|
+
buffer = lastSep >= 0 ? trimmedRight.slice(0, lastSep + 1) : "";
|
|
6338
|
+
errorLine = null;
|
|
6339
|
+
repaintInput();
|
|
6340
|
+
return;
|
|
6341
|
+
}
|
|
6342
|
+
if (data?.isCharacter) {
|
|
6343
|
+
buffer += name;
|
|
6344
|
+
errorLine = null;
|
|
6345
|
+
repaintInput();
|
|
6346
|
+
return;
|
|
6347
|
+
}
|
|
6348
|
+
};
|
|
6349
|
+
term.grabInput({});
|
|
6350
|
+
term.on("key", onKey);
|
|
6351
|
+
});
|
|
6352
|
+
}
|
|
6353
|
+
var init_import_cwd_prompt = __esm({
|
|
6354
|
+
"src/tui/import-cwd-prompt.ts"() {
|
|
6355
|
+
"use strict";
|
|
6356
|
+
init_paths();
|
|
6357
|
+
init_session();
|
|
6358
|
+
init_cwd();
|
|
6359
|
+
}
|
|
6360
|
+
});
|
|
6361
|
+
|
|
6188
6362
|
// src/tui/attachments.ts
|
|
6189
|
-
import
|
|
6363
|
+
import path13 from "path";
|
|
6190
6364
|
function mimeFromExtension(p) {
|
|
6191
|
-
return EXTENSION_TO_MIME[
|
|
6365
|
+
return EXTENSION_TO_MIME[path13.extname(p).toLowerCase()] ?? null;
|
|
6192
6366
|
}
|
|
6193
6367
|
function isSupportedImagePath(p) {
|
|
6194
6368
|
return mimeFromExtension(p) !== null;
|
|
@@ -6832,6 +7006,8 @@ function mapKeyName(name) {
|
|
|
6832
7006
|
return "ctrl-v";
|
|
6833
7007
|
case "CTRL_W":
|
|
6834
7008
|
return "ctrl-w";
|
|
7009
|
+
case "CTRL_X":
|
|
7010
|
+
return "ctrl-x";
|
|
6835
7011
|
case "CTRL_Y":
|
|
6836
7012
|
return "ctrl-y";
|
|
6837
7013
|
case "ESCAPE":
|
|
@@ -7097,7 +7273,7 @@ var init_screen = __esm({
|
|
|
7097
7273
|
this.onKey = opts.onKey;
|
|
7098
7274
|
this.contentRepaintThrottleMs = opts.repaintThrottleMs ?? DEFAULT_CONTENT_REPAINT_THROTTLE_MS;
|
|
7099
7275
|
this.maxScrollbackLines = opts.maxScrollbackLines ?? DEFAULT_MAX_SCROLLBACK_LINES;
|
|
7100
|
-
this.mouseEnabled = opts.mouse ??
|
|
7276
|
+
this.mouseEnabled = opts.mouse ?? false;
|
|
7101
7277
|
this.progressIndicatorEnabled = opts.progressIndicator ?? true;
|
|
7102
7278
|
this.readonly = opts.readonly ?? false;
|
|
7103
7279
|
this.resizeHandler = () => this.repaint();
|
|
@@ -7614,6 +7790,57 @@ uncaught: ${err.stack ?? err.message}
|
|
|
7614
7790
|
this.drawBanner();
|
|
7615
7791
|
this.placeCursor();
|
|
7616
7792
|
}
|
|
7793
|
+
// Runtime toggle for terminal mouse capture. With capture on, the
|
|
7794
|
+
// wheel drives scrollback but text selection requires shift+drag
|
|
7795
|
+
// (terminals route mouse events to the app). With capture off, plain
|
|
7796
|
+
// click-drag selects text but the wheel does nothing in the app —
|
|
7797
|
+
// use PgUp/PgDn for scrollback instead. Bound to ^X so users can
|
|
7798
|
+
// flip on demand without a config reload + restart. Idempotent.
|
|
7799
|
+
//
|
|
7800
|
+
// Re-issuing grabInput() reinstalls terminal-kit's own stdin "data"
|
|
7801
|
+
// listener, so we have to redo the same listener swap that
|
|
7802
|
+
// installBracketedPaste() did at startup — otherwise our raw handler
|
|
7803
|
+
// and terminal-kit's both fire for every keystroke (each character
|
|
7804
|
+
// appears twice in the prompt).
|
|
7805
|
+
setMouseEnabled(enabled) {
|
|
7806
|
+
if (this.mouseEnabled === enabled) {
|
|
7807
|
+
return;
|
|
7808
|
+
}
|
|
7809
|
+
this.mouseEnabled = enabled;
|
|
7810
|
+
if (!this.started) {
|
|
7811
|
+
return;
|
|
7812
|
+
}
|
|
7813
|
+
if (enabled) {
|
|
7814
|
+
this.term.grabInput({ mouse: "button" });
|
|
7815
|
+
this.term.on("mouse", this.mouseHandler);
|
|
7816
|
+
} else {
|
|
7817
|
+
this.term.off("mouse", this.mouseHandler);
|
|
7818
|
+
this.term.grabInput(true);
|
|
7819
|
+
}
|
|
7820
|
+
this.reclaimStdinAfterGrabInput();
|
|
7821
|
+
}
|
|
7822
|
+
// After a grabInput() re-issue, terminal-kit has put its own "data"
|
|
7823
|
+
// listener back on stdin. Pull it back off and reinstall hydra's
|
|
7824
|
+
// rawStdinHandler — keeping the captured terminal-kit handler so our
|
|
7825
|
+
// bracketed-paste extractor can still delegate non-paste bytes to it.
|
|
7826
|
+
// No-op if installBracketedPaste() hasn't run yet (start() does it
|
|
7827
|
+
// before any toggle path can reach here).
|
|
7828
|
+
reclaimStdinAfterGrabInput() {
|
|
7829
|
+
if (this.terminalKitStdinHandler === null) {
|
|
7830
|
+
return;
|
|
7831
|
+
}
|
|
7832
|
+
const t = this.term;
|
|
7833
|
+
if (!t.stdin || typeof t.onStdin !== "function") {
|
|
7834
|
+
return;
|
|
7835
|
+
}
|
|
7836
|
+
this.terminalKitStdinHandler = t.onStdin;
|
|
7837
|
+
t.stdin.removeListener("data", t.onStdin);
|
|
7838
|
+
t.stdin.removeListener("data", this.rawStdinHandler);
|
|
7839
|
+
t.stdin.on("data", this.rawStdinHandler);
|
|
7840
|
+
}
|
|
7841
|
+
isMouseEnabled() {
|
|
7842
|
+
return this.mouseEnabled;
|
|
7843
|
+
}
|
|
7617
7844
|
// Pushed by the app each onKey tick to reflect prompt-history
|
|
7618
7845
|
// reverse-search state in the banner — the only place that mode's
|
|
7619
7846
|
// query is visible. Pass null when not searching.
|
|
@@ -9101,6 +9328,8 @@ var init_input = __esm({
|
|
|
9101
9328
|
case "ctrl-w":
|
|
9102
9329
|
this.killWord();
|
|
9103
9330
|
return [];
|
|
9331
|
+
case "ctrl-x":
|
|
9332
|
+
return [{ type: "toggle-mouse" }];
|
|
9104
9333
|
case "ctrl-y":
|
|
9105
9334
|
this.yank();
|
|
9106
9335
|
return [];
|
|
@@ -9683,9 +9912,9 @@ var init_input = __esm({
|
|
|
9683
9912
|
|
|
9684
9913
|
// src/tui/clipboard.ts
|
|
9685
9914
|
import { spawn as nodeSpawn } from "child_process";
|
|
9686
|
-
import
|
|
9687
|
-
import
|
|
9688
|
-
import
|
|
9915
|
+
import fs19 from "fs/promises";
|
|
9916
|
+
import os5 from "os";
|
|
9917
|
+
import path14 from "path";
|
|
9689
9918
|
async function readClipboard(envIn = {}) {
|
|
9690
9919
|
const env = { ...defaultEnv, ...envIn };
|
|
9691
9920
|
if (env.platform === "darwin") {
|
|
@@ -9700,7 +9929,7 @@ async function readClipboard(envIn = {}) {
|
|
|
9700
9929
|
};
|
|
9701
9930
|
}
|
|
9702
9931
|
async function readMacOS(env) {
|
|
9703
|
-
const tmpPath =
|
|
9932
|
+
const tmpPath = path14.join(
|
|
9704
9933
|
env.tmpdir(),
|
|
9705
9934
|
`hydra-clipboard-${Date.now()}-${process.pid}.png`
|
|
9706
9935
|
);
|
|
@@ -9724,7 +9953,7 @@ async function readMacOS(env) {
|
|
|
9724
9953
|
return img;
|
|
9725
9954
|
}
|
|
9726
9955
|
} catch {
|
|
9727
|
-
await
|
|
9956
|
+
await fs19.unlink(tmpPath).catch(() => void 0);
|
|
9728
9957
|
}
|
|
9729
9958
|
try {
|
|
9730
9959
|
const buf = await runCapture(env.spawn, "pbpaste", []);
|
|
@@ -9839,9 +10068,9 @@ async function which(env, cmd) {
|
|
|
9839
10068
|
}
|
|
9840
10069
|
async function readFileAsAttachment(p, unlinkAfter) {
|
|
9841
10070
|
try {
|
|
9842
|
-
const buf = await
|
|
10071
|
+
const buf = await fs19.readFile(p);
|
|
9843
10072
|
if (unlinkAfter) {
|
|
9844
|
-
await
|
|
10073
|
+
await fs19.unlink(p).catch(() => void 0);
|
|
9845
10074
|
}
|
|
9846
10075
|
if (buf.length === 0) {
|
|
9847
10076
|
return { ok: false, reason: "no image on clipboard" };
|
|
@@ -9867,14 +10096,14 @@ async function readFileAsAttachment(p, unlinkAfter) {
|
|
|
9867
10096
|
}
|
|
9868
10097
|
}
|
|
9869
10098
|
function run2(spawn6, cmd, args) {
|
|
9870
|
-
return new Promise((
|
|
10099
|
+
return new Promise((resolve6, reject) => {
|
|
9871
10100
|
const proc = spawn6(cmd, args);
|
|
9872
10101
|
proc.stdout?.on("data", () => void 0);
|
|
9873
10102
|
proc.stderr?.on("data", () => void 0);
|
|
9874
10103
|
proc.on("error", reject);
|
|
9875
10104
|
proc.on("close", (code) => {
|
|
9876
10105
|
if (code === 0) {
|
|
9877
|
-
|
|
10106
|
+
resolve6();
|
|
9878
10107
|
} else {
|
|
9879
10108
|
reject(new Error(`${cmd} exited ${code}`));
|
|
9880
10109
|
}
|
|
@@ -9882,7 +10111,7 @@ function run2(spawn6, cmd, args) {
|
|
|
9882
10111
|
});
|
|
9883
10112
|
}
|
|
9884
10113
|
function runCapture(spawn6, cmd, args) {
|
|
9885
|
-
return new Promise((
|
|
10114
|
+
return new Promise((resolve6, reject) => {
|
|
9886
10115
|
const proc = spawn6(cmd, args);
|
|
9887
10116
|
const chunks = [];
|
|
9888
10117
|
let stdoutEnded = proc.stdout === null;
|
|
@@ -9894,7 +10123,7 @@ function runCapture(spawn6, cmd, args) {
|
|
|
9894
10123
|
}
|
|
9895
10124
|
settled = true;
|
|
9896
10125
|
if (closedCode === 0) {
|
|
9897
|
-
|
|
10126
|
+
resolve6(Buffer.concat(chunks));
|
|
9898
10127
|
} else {
|
|
9899
10128
|
reject(new Error(`${cmd} exited ${closedCode}`));
|
|
9900
10129
|
}
|
|
@@ -9929,7 +10158,7 @@ var init_clipboard = __esm({
|
|
|
9929
10158
|
platform: process.platform,
|
|
9930
10159
|
env: process.env,
|
|
9931
10160
|
spawn: nodeSpawn,
|
|
9932
|
-
tmpdir:
|
|
10161
|
+
tmpdir: os5.tmpdir
|
|
9933
10162
|
};
|
|
9934
10163
|
SUPPORTED_IMAGE_MIMES = [
|
|
9935
10164
|
"image/png",
|
|
@@ -10446,8 +10675,8 @@ var init_format = __esm({
|
|
|
10446
10675
|
import { appendFileSync, statSync, renameSync } from "fs";
|
|
10447
10676
|
import { nanoid as nanoid3 } from "nanoid";
|
|
10448
10677
|
import termkit from "terminal-kit";
|
|
10449
|
-
import
|
|
10450
|
-
import
|
|
10678
|
+
import fs20 from "fs/promises";
|
|
10679
|
+
import path15 from "path";
|
|
10451
10680
|
function isReadonlyForbiddenEffect(effect) {
|
|
10452
10681
|
switch (effect.type) {
|
|
10453
10682
|
case "send":
|
|
@@ -10775,10 +11004,10 @@ async function runSession(term, config, serviceToken, opts, exitHint) {
|
|
|
10775
11004
|
if (pendingPermission.toolCallId && toolCallId && pendingPermission.toolCallId !== toolCallId) {
|
|
10776
11005
|
return;
|
|
10777
11006
|
}
|
|
10778
|
-
const
|
|
11007
|
+
const resolve6 = pendingPermission.resolve;
|
|
10779
11008
|
pendingPermission = null;
|
|
10780
11009
|
screen.setPermissionPrompt(null);
|
|
10781
|
-
|
|
11010
|
+
resolve6(result ?? { outcome: { outcome: "cancelled" } });
|
|
10782
11011
|
};
|
|
10783
11012
|
const maybeDismissPermissionByToolUpdate = (update) => {
|
|
10784
11013
|
if (!pendingPermission?.toolCallId) {
|
|
@@ -10811,14 +11040,14 @@ async function runSession(term, config, serviceToken, opts, exitHint) {
|
|
|
10811
11040
|
if (!pendingPermission) {
|
|
10812
11041
|
return;
|
|
10813
11042
|
}
|
|
10814
|
-
const { options, resolve:
|
|
11043
|
+
const { options, resolve: resolve6 } = pendingPermission;
|
|
10815
11044
|
pendingPermission = null;
|
|
10816
11045
|
screen.setPermissionPrompt(null);
|
|
10817
11046
|
if (optionId === null) {
|
|
10818
|
-
|
|
11047
|
+
resolve6({ outcome: { outcome: "cancelled" } });
|
|
10819
11048
|
return;
|
|
10820
11049
|
}
|
|
10821
|
-
|
|
11050
|
+
resolve6({ outcome: { outcome: "selected", optionId } });
|
|
10822
11051
|
void options;
|
|
10823
11052
|
};
|
|
10824
11053
|
conn.onRequest("session/request_permission", async (params) => {
|
|
@@ -10845,12 +11074,12 @@ async function runSession(term, config, serviceToken, opts, exitHint) {
|
|
|
10845
11074
|
]);
|
|
10846
11075
|
return { outcome: { outcome: "cancelled" } };
|
|
10847
11076
|
}
|
|
10848
|
-
return new Promise((
|
|
11077
|
+
return new Promise((resolve6) => {
|
|
10849
11078
|
pendingPermission = {
|
|
10850
11079
|
title,
|
|
10851
11080
|
options,
|
|
10852
11081
|
selectedIndex: 0,
|
|
10853
|
-
resolve:
|
|
11082
|
+
resolve: resolve6,
|
|
10854
11083
|
toolCallId
|
|
10855
11084
|
};
|
|
10856
11085
|
refreshPermissionPrompt();
|
|
@@ -10939,7 +11168,23 @@ async function runSession(term, config, serviceToken, opts, exitHint) {
|
|
|
10939
11168
|
sessionId: ctx.sessionId,
|
|
10940
11169
|
historyPolicy: "full",
|
|
10941
11170
|
clientInfo: { name: "hydra-acp-tui", version: HYDRA_VERSION },
|
|
10942
|
-
...opts.readonly === true ? { readonly: true } : {}
|
|
11171
|
+
...opts.readonly === true ? { readonly: true } : {},
|
|
11172
|
+
// Forward the user-chosen cwd for first-launch imported sessions
|
|
11173
|
+
// via a full resume hint. upstreamSessionId is empty so the
|
|
11174
|
+
// daemon routes through doResurrectFromImport (session-manager.ts)
|
|
11175
|
+
// with the user-supplied cwd instead of silently falling back to
|
|
11176
|
+
// $HOME in resolveImportCwd.
|
|
11177
|
+
...ctx.importAttachHint !== void 0 ? {
|
|
11178
|
+
_meta: {
|
|
11179
|
+
[HYDRA_META_KEY]: {
|
|
11180
|
+
resume: {
|
|
11181
|
+
upstreamSessionId: "",
|
|
11182
|
+
agentId: ctx.importAttachHint.agentId,
|
|
11183
|
+
cwd: ctx.importAttachHint.cwd
|
|
11184
|
+
}
|
|
11185
|
+
}
|
|
11186
|
+
}
|
|
11187
|
+
} : {}
|
|
10943
11188
|
});
|
|
10944
11189
|
resolvedSessionId = attached.sessionId;
|
|
10945
11190
|
if (attached.clientId) {
|
|
@@ -11200,8 +11445,8 @@ async function runSession(term, config, serviceToken, opts, exitHint) {
|
|
|
11200
11445
|
}
|
|
11201
11446
|
});
|
|
11202
11447
|
let finishSession = null;
|
|
11203
|
-
const sessionDone = new Promise((
|
|
11204
|
-
finishSession =
|
|
11448
|
+
const sessionDone = new Promise((resolve6) => {
|
|
11449
|
+
finishSession = resolve6;
|
|
11205
11450
|
});
|
|
11206
11451
|
const cancelRemoteTurn = () => {
|
|
11207
11452
|
conn.notify("session/cancel", { sessionId: resolvedSessionId }).catch(() => void 0);
|
|
@@ -11518,6 +11763,14 @@ async function runSession(term, config, serviceToken, opts, exitHint) {
|
|
|
11518
11763
|
toolsExpanded = !toolsExpanded;
|
|
11519
11764
|
renderToolsBlock();
|
|
11520
11765
|
return;
|
|
11766
|
+
case "toggle-mouse": {
|
|
11767
|
+
const next = !screen.isMouseEnabled();
|
|
11768
|
+
screen.setMouseEnabled(next);
|
|
11769
|
+
screen.notify(
|
|
11770
|
+
next ? "mouse capture on \u2014 wheel scrolls; shift+drag to select text" : "mouse capture off \u2014 click-drag selects text; PgUp/PgDn scrolls"
|
|
11771
|
+
);
|
|
11772
|
+
return;
|
|
11773
|
+
}
|
|
11521
11774
|
case "show-help":
|
|
11522
11775
|
toggleHelpModal();
|
|
11523
11776
|
return;
|
|
@@ -11560,11 +11813,11 @@ async function runSession(term, config, serviceToken, opts, exitHint) {
|
|
|
11560
11813
|
}
|
|
11561
11814
|
const mimeType = mimeFromExtension(token);
|
|
11562
11815
|
if (!mimeType) {
|
|
11563
|
-
screen.notify(`unsupported image type: ${
|
|
11816
|
+
screen.notify(`unsupported image type: ${path15.basename(token)}`);
|
|
11564
11817
|
continue;
|
|
11565
11818
|
}
|
|
11566
11819
|
try {
|
|
11567
|
-
const buf = await
|
|
11820
|
+
const buf = await fs20.readFile(token);
|
|
11568
11821
|
if (buf.length > MAX_ATTACHMENT_BYTES) {
|
|
11569
11822
|
screen.notify(
|
|
11570
11823
|
`image too large (${formatSize(buf.length)}, max ${formatSize(MAX_ATTACHMENT_BYTES)})`
|
|
@@ -11574,13 +11827,13 @@ async function runSession(term, config, serviceToken, opts, exitHint) {
|
|
|
11574
11827
|
dispatcher.addAttachment({
|
|
11575
11828
|
mimeType,
|
|
11576
11829
|
data: buf.toString("base64"),
|
|
11577
|
-
name:
|
|
11830
|
+
name: path15.basename(token),
|
|
11578
11831
|
sizeBytes: buf.length
|
|
11579
11832
|
});
|
|
11580
11833
|
added++;
|
|
11581
11834
|
} catch (err) {
|
|
11582
11835
|
screen.notify(
|
|
11583
|
-
`cannot read ${
|
|
11836
|
+
`cannot read ${path15.basename(token)}: ${err.message}`
|
|
11584
11837
|
);
|
|
11585
11838
|
}
|
|
11586
11839
|
}
|
|
@@ -12297,10 +12550,10 @@ async function runSession(term, config, serviceToken, opts, exitHint) {
|
|
|
12297
12550
|
}
|
|
12298
12551
|
const resetInFlightUiState = () => {
|
|
12299
12552
|
if (pendingPermission) {
|
|
12300
|
-
const
|
|
12553
|
+
const resolve6 = pendingPermission.resolve;
|
|
12301
12554
|
pendingPermission = null;
|
|
12302
12555
|
screen.setPermissionPrompt(null);
|
|
12303
|
-
|
|
12556
|
+
resolve6({ outcome: { outcome: "cancelled" } });
|
|
12304
12557
|
}
|
|
12305
12558
|
closeAgentText();
|
|
12306
12559
|
};
|
|
@@ -12465,6 +12718,21 @@ async function resolveSession(term, config, serviceToken, opts) {
|
|
|
12465
12718
|
return newCtx(opts, cwd, config);
|
|
12466
12719
|
}
|
|
12467
12720
|
opts.readonly = choice.readonly === true;
|
|
12721
|
+
const chosen = sessions.find((s) => s.sessionId === choice.sessionId);
|
|
12722
|
+
const isImportedFirstLaunch = chosen !== void 0 && !!chosen.importedFromMachine && !chosen.upstreamSessionId && !opts.readonly;
|
|
12723
|
+
if (isImportedFirstLaunch) {
|
|
12724
|
+
const promptedCwd = await promptForImportCwd(term, chosen);
|
|
12725
|
+
if (promptedCwd === null) {
|
|
12726
|
+
return null;
|
|
12727
|
+
}
|
|
12728
|
+
const agentId = choice.agentId ?? chosen.agentId ?? "";
|
|
12729
|
+
return {
|
|
12730
|
+
sessionId: choice.sessionId,
|
|
12731
|
+
agentId,
|
|
12732
|
+
cwd: promptedCwd,
|
|
12733
|
+
importAttachHint: { agentId, cwd: promptedCwd }
|
|
12734
|
+
};
|
|
12735
|
+
}
|
|
12468
12736
|
return {
|
|
12469
12737
|
sessionId: choice.sessionId,
|
|
12470
12738
|
agentId: choice.agentId ?? "",
|
|
@@ -12593,8 +12861,8 @@ function createInstallStatusLine(term, baseLabel) {
|
|
|
12593
12861
|
}
|
|
12594
12862
|
function rotateIfBig(target) {
|
|
12595
12863
|
try {
|
|
12596
|
-
const
|
|
12597
|
-
if (
|
|
12864
|
+
const stat5 = statSync(target);
|
|
12865
|
+
if (stat5.size < logMaxBytes) {
|
|
12598
12866
|
return;
|
|
12599
12867
|
}
|
|
12600
12868
|
renameSync(target, `${target}.0`);
|
|
@@ -12618,6 +12886,7 @@ var init_app = __esm({
|
|
|
12618
12886
|
init_history();
|
|
12619
12887
|
init_discovery();
|
|
12620
12888
|
init_picker();
|
|
12889
|
+
init_import_cwd_prompt();
|
|
12621
12890
|
init_screen();
|
|
12622
12891
|
init_input();
|
|
12623
12892
|
init_attachments();
|
|
@@ -12647,6 +12916,7 @@ var init_app = __esm({
|
|
|
12647
12916
|
["^R / ^S", "history reverse / forward search"],
|
|
12648
12917
|
["PgUp / PgDn", "scroll scrollback"],
|
|
12649
12918
|
["Mouse wheel", "scroll scrollback (when mouse capture is on)"],
|
|
12919
|
+
["^X", "toggle mouse capture (wheel scroll vs. text selection)"],
|
|
12650
12920
|
null,
|
|
12651
12921
|
["^C", "cancel turn (twice to exit)"],
|
|
12652
12922
|
["Esc", "cancel turn and prefill draft"],
|
|
@@ -12673,7 +12943,7 @@ var init_tui = __esm({
|
|
|
12673
12943
|
// src/cli.ts
|
|
12674
12944
|
import { readFileSync as readFileSync2 } from "fs";
|
|
12675
12945
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12676
|
-
import { dirname as dirname6, resolve as
|
|
12946
|
+
import { dirname as dirname6, resolve as resolve5 } from "path";
|
|
12677
12947
|
|
|
12678
12948
|
// src/cli/parse-args.ts
|
|
12679
12949
|
var KNOWN_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
@@ -12971,10 +13241,10 @@ async function downloadTo(args) {
|
|
|
12971
13241
|
logSink(formatProgress(args.agentId, received, total));
|
|
12972
13242
|
}
|
|
12973
13243
|
});
|
|
12974
|
-
await new Promise((
|
|
13244
|
+
await new Promise((resolve6, reject) => {
|
|
12975
13245
|
nodeStream.on("error", reject);
|
|
12976
13246
|
out.on("error", reject);
|
|
12977
|
-
out.on("finish", () =>
|
|
13247
|
+
out.on("finish", () => resolve6());
|
|
12978
13248
|
nodeStream.pipe(out);
|
|
12979
13249
|
});
|
|
12980
13250
|
logSink(formatProgress(
|
|
@@ -13026,14 +13296,14 @@ async function extract(archivePath, dest) {
|
|
|
13026
13296
|
throw new Error(`Unsupported archive format: ${archivePath}`);
|
|
13027
13297
|
}
|
|
13028
13298
|
function run(cmd, args) {
|
|
13029
|
-
return new Promise((
|
|
13299
|
+
return new Promise((resolve6, reject) => {
|
|
13030
13300
|
const child = spawn(cmd, args, {
|
|
13031
13301
|
stdio: ["ignore", "ignore", "inherit"]
|
|
13032
13302
|
});
|
|
13033
13303
|
child.on("error", reject);
|
|
13034
13304
|
child.on("exit", (code, signal) => {
|
|
13035
13305
|
if (code === 0) {
|
|
13036
|
-
|
|
13306
|
+
resolve6();
|
|
13037
13307
|
return;
|
|
13038
13308
|
}
|
|
13039
13309
|
reject(
|
|
@@ -13045,11 +13315,11 @@ function run(cmd, args) {
|
|
|
13045
13315
|
});
|
|
13046
13316
|
}
|
|
13047
13317
|
async function hasCommand(name) {
|
|
13048
|
-
return new Promise((
|
|
13318
|
+
return new Promise((resolve6) => {
|
|
13049
13319
|
const finder = process.platform === "win32" ? "where" : "which";
|
|
13050
13320
|
const child = spawn(finder, [name], { stdio: "ignore" });
|
|
13051
|
-
child.on("error", () =>
|
|
13052
|
-
child.on("exit", (code) =>
|
|
13321
|
+
child.on("error", () => resolve6(false));
|
|
13322
|
+
child.on("exit", (code) => resolve6(code === 0));
|
|
13053
13323
|
});
|
|
13054
13324
|
}
|
|
13055
13325
|
async function fileExists(p) {
|
|
@@ -13167,7 +13437,7 @@ function runNpmInstall(args) {
|
|
|
13167
13437
|
}
|
|
13168
13438
|
async function runNpmInstallOnce(args, attempt) {
|
|
13169
13439
|
try {
|
|
13170
|
-
await new Promise((
|
|
13440
|
+
await new Promise((resolve6, reject) => {
|
|
13171
13441
|
const registryArgs = args.registry ? ["--registry", args.registry] : [];
|
|
13172
13442
|
let child;
|
|
13173
13443
|
try {
|
|
@@ -13209,7 +13479,7 @@ async function runNpmInstallOnce(args, attempt) {
|
|
|
13209
13479
|
});
|
|
13210
13480
|
child.on("exit", (code, signal) => {
|
|
13211
13481
|
if (code === 0) {
|
|
13212
|
-
|
|
13482
|
+
resolve6();
|
|
13213
13483
|
return;
|
|
13214
13484
|
}
|
|
13215
13485
|
const reason = code !== null ? `exit code ${code}` : `signal ${signal ?? "unknown"}`;
|
|
@@ -13537,13 +13807,13 @@ function ndjsonStreamFromStdio(stdout, stdin) {
|
|
|
13537
13807
|
throw new Error("stream is closed");
|
|
13538
13808
|
}
|
|
13539
13809
|
const line = JSON.stringify(message) + "\n";
|
|
13540
|
-
await new Promise((
|
|
13810
|
+
await new Promise((resolve6, reject) => {
|
|
13541
13811
|
stdin.write(line, (err) => {
|
|
13542
13812
|
if (err) {
|
|
13543
13813
|
reject(err);
|
|
13544
13814
|
return;
|
|
13545
13815
|
}
|
|
13546
|
-
|
|
13816
|
+
resolve6();
|
|
13547
13817
|
});
|
|
13548
13818
|
});
|
|
13549
13819
|
},
|
|
@@ -14058,8 +14328,8 @@ var SessionManager = class {
|
|
|
14058
14328
|
}
|
|
14059
14329
|
async resolveImportCwd(cwd) {
|
|
14060
14330
|
try {
|
|
14061
|
-
const
|
|
14062
|
-
if (
|
|
14331
|
+
const stat5 = await fs11.stat(cwd);
|
|
14332
|
+
if (stat5.isDirectory()) {
|
|
14063
14333
|
return cwd;
|
|
14064
14334
|
}
|
|
14065
14335
|
} catch {
|
|
@@ -14982,9 +15252,9 @@ var ExtensionManager = class {
|
|
|
14982
15252
|
} catch {
|
|
14983
15253
|
}
|
|
14984
15254
|
tasks.push(
|
|
14985
|
-
new Promise((
|
|
15255
|
+
new Promise((resolve6) => {
|
|
14986
15256
|
if (child.exitCode !== null || child.signalCode !== null) {
|
|
14987
|
-
|
|
15257
|
+
resolve6();
|
|
14988
15258
|
return;
|
|
14989
15259
|
}
|
|
14990
15260
|
const timer = setTimeout(() => {
|
|
@@ -14992,11 +15262,11 @@ var ExtensionManager = class {
|
|
|
14992
15262
|
child.kill("SIGKILL");
|
|
14993
15263
|
} catch {
|
|
14994
15264
|
}
|
|
14995
|
-
|
|
15265
|
+
resolve6();
|
|
14996
15266
|
}, STOP_GRACE_MS);
|
|
14997
15267
|
child.on("exit", () => {
|
|
14998
15268
|
clearTimeout(timer);
|
|
14999
|
-
|
|
15269
|
+
resolve6();
|
|
15000
15270
|
});
|
|
15001
15271
|
})
|
|
15002
15272
|
);
|
|
@@ -15104,8 +15374,8 @@ var ExtensionManager = class {
|
|
|
15104
15374
|
if (child.exitCode !== null || child.signalCode !== null) {
|
|
15105
15375
|
return;
|
|
15106
15376
|
}
|
|
15107
|
-
const exited = new Promise((
|
|
15108
|
-
entry.exitWaiters.push(
|
|
15377
|
+
const exited = new Promise((resolve6) => {
|
|
15378
|
+
entry.exitWaiters.push(resolve6);
|
|
15109
15379
|
});
|
|
15110
15380
|
try {
|
|
15111
15381
|
child.kill("SIGTERM");
|
|
@@ -15302,8 +15572,8 @@ var ExtensionManager = class {
|
|
|
15302
15572
|
entry.pid = void 0;
|
|
15303
15573
|
entry.lastExitCode = typeof code === "number" ? code : void 0;
|
|
15304
15574
|
const waiters = entry.exitWaiters.splice(0);
|
|
15305
|
-
for (const
|
|
15306
|
-
|
|
15575
|
+
for (const resolve6 of waiters) {
|
|
15576
|
+
resolve6();
|
|
15307
15577
|
}
|
|
15308
15578
|
if (this.stopping || entry.manuallyStopped) {
|
|
15309
15579
|
try {
|
|
@@ -17174,9 +17444,9 @@ import * as fs16 from "fs";
|
|
|
17174
17444
|
import * as fsp6 from "fs/promises";
|
|
17175
17445
|
async function runLogTail(logPath, argv, notFoundMessage) {
|
|
17176
17446
|
const opts = parseLogTailFlags(argv);
|
|
17177
|
-
let
|
|
17447
|
+
let stat5;
|
|
17178
17448
|
try {
|
|
17179
|
-
|
|
17449
|
+
stat5 = await fsp6.stat(logPath);
|
|
17180
17450
|
} catch (err) {
|
|
17181
17451
|
const e = err;
|
|
17182
17452
|
if (e.code === "ENOENT") {
|
|
@@ -17187,7 +17457,7 @@ async function runLogTail(logPath, argv, notFoundMessage) {
|
|
|
17187
17457
|
}
|
|
17188
17458
|
throw err;
|
|
17189
17459
|
}
|
|
17190
|
-
let position = await printTail(logPath,
|
|
17460
|
+
let position = await printTail(logPath, stat5.size, opts.tail);
|
|
17191
17461
|
if (!opts.follow) {
|
|
17192
17462
|
return;
|
|
17193
17463
|
}
|
|
@@ -17222,10 +17492,10 @@ async function runLogTail(logPath, argv, notFoundMessage) {
|
|
|
17222
17492
|
}
|
|
17223
17493
|
});
|
|
17224
17494
|
});
|
|
17225
|
-
await new Promise((
|
|
17495
|
+
await new Promise((resolve6) => {
|
|
17226
17496
|
const finish = () => {
|
|
17227
17497
|
watcher.close();
|
|
17228
|
-
|
|
17498
|
+
resolve6();
|
|
17229
17499
|
};
|
|
17230
17500
|
process.once("SIGINT", finish);
|
|
17231
17501
|
process.once("SIGTERM", finish);
|
|
@@ -18031,7 +18301,7 @@ async function promptPassword(prompt) {
|
|
|
18031
18301
|
if (!process.stdin.isTTY) {
|
|
18032
18302
|
return readLineFromStdin();
|
|
18033
18303
|
}
|
|
18034
|
-
return new Promise((
|
|
18304
|
+
return new Promise((resolve6, reject) => {
|
|
18035
18305
|
const stdin = process.stdin;
|
|
18036
18306
|
const wasRaw = stdin.isRaw === true;
|
|
18037
18307
|
let buffer = "";
|
|
@@ -18048,7 +18318,7 @@ async function promptPassword(prompt) {
|
|
|
18048
18318
|
if (byte === 10 || byte === 13) {
|
|
18049
18319
|
process.stdout.write("\n");
|
|
18050
18320
|
cleanup();
|
|
18051
|
-
|
|
18321
|
+
resolve6(buffer);
|
|
18052
18322
|
return;
|
|
18053
18323
|
}
|
|
18054
18324
|
if (byte === 3) {
|
|
@@ -18074,7 +18344,7 @@ async function promptPassword(prompt) {
|
|
|
18074
18344
|
});
|
|
18075
18345
|
}
|
|
18076
18346
|
function readLineFromStdin() {
|
|
18077
|
-
return new Promise((
|
|
18347
|
+
return new Promise((resolve6, reject) => {
|
|
18078
18348
|
let buffer = "";
|
|
18079
18349
|
process.stdin.setEncoding("utf8");
|
|
18080
18350
|
const onData = (chunk) => {
|
|
@@ -18083,7 +18353,7 @@ function readLineFromStdin() {
|
|
|
18083
18353
|
if (nl !== -1) {
|
|
18084
18354
|
process.stdin.removeListener("data", onData);
|
|
18085
18355
|
process.stdin.removeListener("error", onError);
|
|
18086
|
-
|
|
18356
|
+
resolve6(buffer.slice(0, nl).replace(/\r$/, ""));
|
|
18087
18357
|
}
|
|
18088
18358
|
};
|
|
18089
18359
|
const onError = (err) => {
|
|
@@ -18916,7 +19186,7 @@ function readVersion() {
|
|
|
18916
19186
|
try {
|
|
18917
19187
|
const here = dirname6(fileURLToPath2(import.meta.url));
|
|
18918
19188
|
const pkg = JSON.parse(
|
|
18919
|
-
readFileSync2(
|
|
19189
|
+
readFileSync2(resolve5(here, "../package.json"), "utf8")
|
|
18920
19190
|
);
|
|
18921
19191
|
return pkg.version ?? "unknown";
|
|
18922
19192
|
} catch {
|
package/dist/index.js
CHANGED
|
@@ -168,12 +168,12 @@ var TuiConfig = z.object({
|
|
|
168
168
|
// buffer. Oldest lines are dropped on overflow. The on-disk session
|
|
169
169
|
// history is unaffected; this only bounds the TUI's local view buffer.
|
|
170
170
|
maxScrollbackLines: z.number().int().positive().default(1e4),
|
|
171
|
-
// When true
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
// false
|
|
175
|
-
//
|
|
176
|
-
mouse: z.boolean().default(
|
|
171
|
+
// When true, the TUI captures mouse events so the wheel can drive
|
|
172
|
+
// scrollback. The cost: terminals route clicks to the app, so text
|
|
173
|
+
// selection requires shift+drag to bypass mouse reporting. Default
|
|
174
|
+
// false — wheel scrollback stops working, but plain click-drag
|
|
175
|
+
// selects text via the terminal emulator. Set true to opt back in.
|
|
176
|
+
mouse: z.boolean().default(false),
|
|
177
177
|
// Size at which the TUI's session/update debug log (tui.log) rotates
|
|
178
178
|
// to tui.log.0 and resets. Bounds on-disk use at ~2x this value.
|
|
179
179
|
logMaxBytes: z.number().int().positive().default(5 * 1024 * 1024),
|
|
@@ -188,13 +188,13 @@ var TuiConfig = z.object({
|
|
|
188
188
|
// just don't want it.
|
|
189
189
|
progressIndicator: z.boolean().default(true),
|
|
190
190
|
// What the unmodified Enter key does in the prompt composer.
|
|
191
|
-
// "
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
// "
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
defaultEnterAction: z.enum(["enqueue", "amend"]).default("
|
|
191
|
+
// "amend" (default) — Enter amends the in-flight turn; Shift+Enter
|
|
192
|
+
// enqueues. With no turn in flight either key just enqueues,
|
|
193
|
+
// since there's nothing to amend.
|
|
194
|
+
// "enqueue" — flips the two: Enter enqueues the prompt (sends
|
|
195
|
+
// immediately when idle, queues behind an in-flight turn);
|
|
196
|
+
// Shift+Enter amends the in-flight turn.
|
|
197
|
+
defaultEnterAction: z.enum(["enqueue", "amend"]).default("amend")
|
|
198
198
|
});
|
|
199
199
|
var ExtensionName = z.string().min(1).regex(/^[A-Za-z0-9._-]+$/, "extension name must be filename-safe");
|
|
200
200
|
var ExtensionBody = z.object({
|
|
@@ -235,11 +235,11 @@ var HydraConfig = z.object({
|
|
|
235
235
|
tui: TuiConfig.default({
|
|
236
236
|
repaintThrottleMs: 1e3,
|
|
237
237
|
maxScrollbackLines: 1e4,
|
|
238
|
-
mouse:
|
|
238
|
+
mouse: false,
|
|
239
239
|
logMaxBytes: 5 * 1024 * 1024,
|
|
240
240
|
cwdColumnMaxWidth: 24,
|
|
241
241
|
progressIndicator: true,
|
|
242
|
-
defaultEnterAction: "
|
|
242
|
+
defaultEnterAction: "amend"
|
|
243
243
|
})
|
|
244
244
|
});
|
|
245
245
|
function extensionList(config) {
|