skydive-cli 0.5.0-beta.41 → 0.5.0-beta.46
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/CHANGELOG.md +2 -0
- package/dist/js/{api-DQCaztBg.mjs → api-TwLD7ibI.mjs} +1 -1
- package/dist/js/bin.mjs +27 -25
- package/dist/js/{boot-B1Y1D48W.mjs → boot-BD8HZcOo.mjs} +387 -32
- package/dist/js/{client-DMYwKALl.mjs → client-C2lPer4b.mjs} +49 -4
- package/dist/js/client-CSKr4Swa.mjs +5 -0
- package/dist/js/{daemon-D8WoX21I.mjs → daemon-MZN78SYL.mjs} +94 -5
- package/dist/js/daemon-S0p-4FGa.mjs +7 -0
- package/dist/js/daemon-client-DMx26-Rz.mjs +8 -0
- package/dist/js/{daemon-client-Bp5DMeIi.mjs → daemon-client-qNlw9inL.mjs} +1 -1
- package/dist/js/{forward-zwB55Bls.mjs → forward-Ct8Vd2WW.mjs} +1 -1
- package/dist/js/{install-BkMcGVYS.mjs → install-DNJ6k578.mjs} +3 -7
- package/dist/js/{print-BAmEefvX.mjs → print-BAdeImNJ.mjs} +2 -2
- package/dist/js/{print-CwbdwCeQ.mjs → print-Bg8rzq9t.mjs} +82 -6
- package/dist/js/{print-share-azWHVxpt.mjs → print-share-D20YvVbb.mjs} +2 -2
- package/dist/js/{rest-B5DVQ1LM.mjs → rest-8QGtGZIO.mjs} +2 -2
- package/dist/js/{rest-Bbe-RhMy.mjs → rest-BtHVI0y6.mjs} +13 -4
- package/package.json +5 -5
- package/dist/js/client-DCwVlAal.mjs +0 -5
- package/dist/js/daemon-By-I_HVM.mjs +0 -7
- package/dist/js/daemon-client-_SVAskeO.mjs +0 -8
- /package/dist/js/{http-error-UHH3mVBF.mjs → http-error-BF2NZZE3.mjs} +0 -0
- /package/dist/js/{output-wY0VQDea.mjs → output-C9mb3sUB.mjs} +0 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { t as
|
|
5
|
-
import {
|
|
2
|
+
import { a as isRecord, i as errorMessage, n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-BtHVI0y6.mjs";
|
|
3
|
+
import { A as getSavedTheme, F as recordDefaultAgent, V as resolveWebUrl, W as saveTheme, b as DEFAULT_API_URL, c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, g as specKeyFor, h as parseConnectCard, i as resolveAgent, k as getReviewStateDir, l as parseExternalOauthConnectParams, m as computeSettledLabel, p as composeQuestionAnswer, s as MASK_CHAR, u as parseOauthConnectParams, w as getConfigPath, x as DEFAULT_APP_URL } from "./print-Bg8rzq9t.mjs";
|
|
4
|
+
import { A as installAgentAlias, C as themeVersion, D as machineOsFromPlatform, F as detectShell, L as getActiveWorkspaceId, M as takenAliasNames, O as aliasActivationHint, S as themeModeFromColorFgBg, T as themesForMode, V as setActiveWorkspace, _ as monoTheme, a as WORDMARK, b as themeForMode, d as profilingEnabled, f as record, g as findTheme, h as applyTheme, i as MARK_CELLS, j as slugifyAliasName, k as dedupeAliasName, m as DEFAULT_THEME_ID, n as buildCrashReport, p as writeArtifact, r as writeCrashReport, s as splashFitsWidth, t as installCrashHandler, v as noColorRequested, x as themeMode, y as theme, z as listWorkspaces } from "./install-DNJ6k578.mjs";
|
|
5
|
+
import { t as HttpError } from "./http-error-BF2NZZE3.mjs";
|
|
6
6
|
import { i as billingBlockedOutcomeFromSendResponse } from "./billing-blocked-D3l5kJlX.mjs";
|
|
7
|
-
import { t as PortalClient } from "./client-
|
|
8
|
-
import "./daemon-
|
|
7
|
+
import { t as PortalClient } from "./client-C2lPer4b.mjs";
|
|
8
|
+
import "./daemon-MZN78SYL.mjs";
|
|
9
9
|
import { t as defaultTlsCertSource } from "./tls-cert-CV-pwxVN.mjs";
|
|
10
|
-
import "./api-
|
|
10
|
+
import "./api-TwLD7ibI.mjs";
|
|
11
11
|
import { t as SandboxStream } from "./client-Btq6bMzX.mjs";
|
|
12
|
-
import { t as PortalDaemonClient } from "./daemon-client-
|
|
12
|
+
import { t as PortalDaemonClient } from "./daemon-client-qNlw9inL.mjs";
|
|
13
13
|
import { t as runRawPtyPassthrough } from "./raw-pty-D5PhKZSl.mjs";
|
|
14
14
|
import * as os$1 from "node:os";
|
|
15
15
|
import { homedir, platform, release, tmpdir } from "node:os";
|
|
@@ -2794,26 +2794,80 @@ function CollapseHint() {
|
|
|
2794
2794
|
/** Collapsed line cap for tool error output. */
|
|
2795
2795
|
const maxErrorLines = 8;
|
|
2796
2796
|
/**
|
|
2797
|
+
* Character budget for a collapsed error block, sized so the block still
|
|
2798
|
+
* costs about `maxErrorLines` rows on a conventional 80-column terminal.
|
|
2799
|
+
* Error lines wrap instead of clipping, so without this a single
|
|
2800
|
+
* multi-kilobyte line (a raw JSON body, a minified stack) would wrap into a
|
|
2801
|
+
* wall of scrollback.
|
|
2802
|
+
*/
|
|
2803
|
+
const maxErrorChars = maxErrorLines * 80;
|
|
2804
|
+
/**
|
|
2805
|
+
* Squeeze the vertical dead space out of a block: leading and trailing blank
|
|
2806
|
+
* lines dropped, runs of blank lines collapsed to one. A failing command's
|
|
2807
|
+
* error text arrives as stderr (newline-terminated) plus a blank-line-
|
|
2808
|
+
* separated `Command exited with code N`, which otherwise renders as a
|
|
2809
|
+
* two-row hole in the middle of the block.
|
|
2810
|
+
*/
|
|
2811
|
+
function tightenBlanks(text) {
|
|
2812
|
+
return text.replace(/\n{3,}/g, "\n\n").replace(/^\n+/, "").replace(/\s+$/, "");
|
|
2813
|
+
}
|
|
2814
|
+
/**
|
|
2815
|
+
* Collapsed form of an error block: whole lines, up to the line cap and the
|
|
2816
|
+
* character budget, left for the renderer to word-wrap. Unlike ok output,
|
|
2817
|
+
* errors are not clipped at a fixed column — the reason a command failed is
|
|
2818
|
+
* usually in the tail of the message, so an ellipsis at column 100 hides the
|
|
2819
|
+
* one thing the block exists to say.
|
|
2820
|
+
*/
|
|
2821
|
+
function collapseError(text) {
|
|
2822
|
+
const source = expandLines(text);
|
|
2823
|
+
const lines = [];
|
|
2824
|
+
let room = maxErrorChars;
|
|
2825
|
+
let cut = false;
|
|
2826
|
+
for (const line of source) {
|
|
2827
|
+
if (lines.length === maxErrorLines || room <= 0) break;
|
|
2828
|
+
if (line.length > room) {
|
|
2829
|
+
lines.push(truncate(line, room));
|
|
2830
|
+
cut = true;
|
|
2831
|
+
break;
|
|
2832
|
+
}
|
|
2833
|
+
lines.push(line);
|
|
2834
|
+
room -= line.length;
|
|
2835
|
+
}
|
|
2836
|
+
const hidden = source.length - lines.length;
|
|
2837
|
+
return {
|
|
2838
|
+
lines,
|
|
2839
|
+
hidden,
|
|
2840
|
+
clipped: cut || hidden > 0
|
|
2841
|
+
};
|
|
2842
|
+
}
|
|
2843
|
+
/** Whether a collapsed error block hides any of its text. */
|
|
2844
|
+
function isErrorClipped(text) {
|
|
2845
|
+
return collapseError(tightenBlanks(text)).clipped;
|
|
2846
|
+
}
|
|
2847
|
+
/**
|
|
2797
2848
|
* Tool error output, clamped like ok output: collapsed it clips to
|
|
2798
2849
|
* `maxErrorLines` with the "+N more · click to expand" tail; expanded it
|
|
2799
|
-
* renders in full
|
|
2800
|
-
*
|
|
2801
|
-
*
|
|
2850
|
+
* renders in full. Same click-to-toggle box as the rest of the block — a
|
|
2851
|
+
* failing command's output shouldn't flood the scrollback just because it
|
|
2852
|
+
* arrived on the error channel.
|
|
2802
2853
|
*/
|
|
2803
2854
|
function ErrorLines({ text, expanded }) {
|
|
2855
|
+
const tidy = tightenBlanks(text);
|
|
2804
2856
|
if (expanded) return /* @__PURE__ */ jsx(ExpandedLines, {
|
|
2805
|
-
text,
|
|
2857
|
+
text: tidy,
|
|
2806
2858
|
fg: theme.error
|
|
2807
2859
|
});
|
|
2808
|
-
|
|
2860
|
+
const { lines, hidden, clipped } = collapseError(tidy);
|
|
2861
|
+
return /* @__PURE__ */ jsxs("box", {
|
|
2809
2862
|
style: { flexDirection: "column" },
|
|
2810
|
-
children:
|
|
2811
|
-
maxLines: maxErrorLines,
|
|
2812
|
-
maxLineLen: 100
|
|
2813
|
-
}).map((line, idx) => /* @__PURE__ */ jsx("text", {
|
|
2863
|
+
children: [lines.map((line, idx) => /* @__PURE__ */ jsx("text", {
|
|
2814
2864
|
fg: theme.error,
|
|
2865
|
+
style: { wrapMode: "word" },
|
|
2815
2866
|
children: line
|
|
2816
|
-
}, idx))
|
|
2867
|
+
}, idx)), clipped && /* @__PURE__ */ jsx("text", {
|
|
2868
|
+
fg: theme.error,
|
|
2869
|
+
children: hidden > 0 ? `… +${hidden} more lines · click to expand` : "… click to expand"
|
|
2870
|
+
})]
|
|
2817
2871
|
});
|
|
2818
2872
|
}
|
|
2819
2873
|
/** Indents tool output two columns under its header — no rule, just space. */
|
|
@@ -3790,12 +3844,38 @@ function CardItem({ item }) {
|
|
|
3790
3844
|
children: [
|
|
3791
3845
|
/* @__PURE__ */ jsxs("text", {
|
|
3792
3846
|
fg: theme.fg,
|
|
3793
|
-
children: ["⚿ ", card.title]
|
|
3847
|
+
children: [card.questions.length > 0 ? "? " : "⚿ ", card.title]
|
|
3794
3848
|
}),
|
|
3795
3849
|
card.subtitle ? /* @__PURE__ */ jsx("text", {
|
|
3796
3850
|
fg: theme.muted,
|
|
3797
3851
|
children: card.subtitle
|
|
3798
3852
|
}) : null,
|
|
3853
|
+
card.questions.map((q, qi) => /* @__PURE__ */ jsxs("box", {
|
|
3854
|
+
style: { flexDirection: "column" },
|
|
3855
|
+
children: [
|
|
3856
|
+
card.questions.length > 1 ? /* @__PURE__ */ jsx("text", {
|
|
3857
|
+
fg: theme.fg,
|
|
3858
|
+
children: q.question
|
|
3859
|
+
}) : null,
|
|
3860
|
+
q.options.map((opt, i) => /* @__PURE__ */ jsxs("text", {
|
|
3861
|
+
fg: theme.muted,
|
|
3862
|
+
children: [
|
|
3863
|
+
" ",
|
|
3864
|
+
String.fromCharCode(65 + i),
|
|
3865
|
+
". ",
|
|
3866
|
+
opt.label
|
|
3867
|
+
]
|
|
3868
|
+
}, opt.label)),
|
|
3869
|
+
/* @__PURE__ */ jsxs("text", {
|
|
3870
|
+
fg: theme.dim,
|
|
3871
|
+
children: [
|
|
3872
|
+
" ",
|
|
3873
|
+
String.fromCharCode(65 + q.options.length),
|
|
3874
|
+
". Other…"
|
|
3875
|
+
]
|
|
3876
|
+
})
|
|
3877
|
+
]
|
|
3878
|
+
}, qi)),
|
|
3799
3879
|
card.description ? /* @__PURE__ */ jsx("markdown", {
|
|
3800
3880
|
content: card.description,
|
|
3801
3881
|
syntaxStyle: syntaxStyle(),
|
|
@@ -4412,7 +4492,7 @@ function ToolEdit({ item }) {
|
|
|
4412
4492
|
item.output?.kind === "error" && /* @__PURE__ */ jsxs(Indented, { children: [/* @__PURE__ */ jsx(ErrorLines, {
|
|
4413
4493
|
text: item.output.errorText,
|
|
4414
4494
|
expanded
|
|
4415
|
-
}), expanded &&
|
|
4495
|
+
}), expanded && isErrorClipped(item.output.errorText) && /* @__PURE__ */ jsx(CollapseHint, {})] }),
|
|
4416
4496
|
item.output?.kind === "ok" && pairs === null && inputReady && /* @__PURE__ */ jsx(Indented, { children: /* @__PURE__ */ jsx("text", {
|
|
4417
4497
|
fg: theme.muted,
|
|
4418
4498
|
children: summarize(item.output.value, 60)
|
|
@@ -4595,7 +4675,7 @@ function ToolBash({ item }) {
|
|
|
4595
4675
|
text: item.output.errorText,
|
|
4596
4676
|
expanded
|
|
4597
4677
|
}) }),
|
|
4598
|
-
expanded && (showCommandBlock && isClipped(commandBlockText, maxCommandLines) || output !== null && isClipped(output, maxLines$1) || item.output?.kind === "error" &&
|
|
4678
|
+
expanded && (showCommandBlock && isClipped(commandBlockText, maxCommandLines) || output !== null && isClipped(output, maxLines$1) || item.output?.kind === "error" && isErrorClipped(item.output.errorText)) && /* @__PURE__ */ jsx(Indented, { children: /* @__PURE__ */ jsx(CollapseHint, {}) })
|
|
4599
4679
|
]
|
|
4600
4680
|
});
|
|
4601
4681
|
}
|
|
@@ -4622,7 +4702,11 @@ const maxLines = 6;
|
|
|
4622
4702
|
function ToolRead({ item }) {
|
|
4623
4703
|
const glyph = toolGlyph(item.state);
|
|
4624
4704
|
const input = isRecord(item.input) ? item.input : {};
|
|
4625
|
-
const filePath =
|
|
4705
|
+
const filePath = pickString(input, [
|
|
4706
|
+
"path",
|
|
4707
|
+
"filePath",
|
|
4708
|
+
"file"
|
|
4709
|
+
]);
|
|
4626
4710
|
const offset = typeof input.offset === "number" ? input.offset : null;
|
|
4627
4711
|
const limit = typeof input.limit === "number" ? input.limit : null;
|
|
4628
4712
|
const header = filePath ? offset !== null ? `${filePath}:L${offset}${limit ? `-L${offset + limit - 1}` : ""}` : filePath : "(unknown path)";
|
|
@@ -4666,7 +4750,7 @@ function ToolRead({ item }) {
|
|
|
4666
4750
|
item.output?.kind === "error" && /* @__PURE__ */ jsxs(Indented, { children: [/* @__PURE__ */ jsx(ErrorLines, {
|
|
4667
4751
|
text: item.output.errorText,
|
|
4668
4752
|
expanded
|
|
4669
|
-
}), expanded &&
|
|
4753
|
+
}), expanded && isErrorClipped(item.output.errorText) && /* @__PURE__ */ jsx(CollapseHint, {})] })
|
|
4670
4754
|
]
|
|
4671
4755
|
});
|
|
4672
4756
|
}
|
|
@@ -4750,7 +4834,7 @@ function ToolGrep({ item }) {
|
|
|
4750
4834
|
item.output?.kind === "error" && /* @__PURE__ */ jsxs(Indented, { children: [/* @__PURE__ */ jsx(ErrorLines, {
|
|
4751
4835
|
text: item.output.errorText,
|
|
4752
4836
|
expanded
|
|
4753
|
-
}), expanded &&
|
|
4837
|
+
}), expanded && isErrorClipped(item.output.errorText) && /* @__PURE__ */ jsx(CollapseHint, {})] })
|
|
4754
4838
|
]
|
|
4755
4839
|
});
|
|
4756
4840
|
}
|
|
@@ -7681,7 +7765,7 @@ const keybindGroups = [
|
|
|
7681
7765
|
},
|
|
7682
7766
|
{
|
|
7683
7767
|
keys: "ctrl+r",
|
|
7684
|
-
action: "act on a waiting connect
|
|
7768
|
+
action: "act on a waiting connect / ask card"
|
|
7685
7769
|
},
|
|
7686
7770
|
{
|
|
7687
7771
|
keys: "pgup / pgdn",
|
|
@@ -8433,6 +8517,248 @@ function printResumeHint(write = (text) => process.stdout.write(text)) {
|
|
|
8433
8517
|
lastConversationId = null;
|
|
8434
8518
|
}
|
|
8435
8519
|
|
|
8520
|
+
//#endregion
|
|
8521
|
+
//#region src/chat/tui/screens/ask-picker.tsx
|
|
8522
|
+
/**
|
|
8523
|
+
* In-TUI picker for a `platform ask` form. Modeled on Claude Code's
|
|
8524
|
+
* AskUserQuestion overlay: one question at a time, a highlighted option
|
|
8525
|
+
* list (Other is the last row), enter selects, letters jump, esc cancels.
|
|
8526
|
+
* Multi-question forms advance after each pick and submit once every
|
|
8527
|
+
* question is answered. The composed answer is a normal user message.
|
|
8528
|
+
*/
|
|
8529
|
+
function AskPicker({ questions, onSubmit, onCancel }) {
|
|
8530
|
+
const [active, setActive] = useState(0);
|
|
8531
|
+
const [highlight, setHighlight] = useState(0);
|
|
8532
|
+
const [selected, setSelected] = useState({});
|
|
8533
|
+
const [otherOpen, setOtherOpen] = useState(false);
|
|
8534
|
+
const [otherText, setOtherText] = useState("");
|
|
8535
|
+
const q = questions[active] ?? questions[0];
|
|
8536
|
+
const otherIndex = q ? q.options.length : 0;
|
|
8537
|
+
const doneIndex = q?.multiSelect ? otherIndex + 1 : -1;
|
|
8538
|
+
const rowCount = q?.multiSelect ? otherIndex + 2 : otherIndex + 1;
|
|
8539
|
+
const clamped = Math.min(highlight, Math.max(0, rowCount - 1));
|
|
8540
|
+
const picked = selected[active] ?? [];
|
|
8541
|
+
function answersFor(nextSelected, nextOther) {
|
|
8542
|
+
return questions.map((_, qi) => {
|
|
8543
|
+
const labels = nextSelected[qi] ?? [];
|
|
8544
|
+
if (qi === active && nextOther.trim()) return [...labels, nextOther.trim()];
|
|
8545
|
+
return labels;
|
|
8546
|
+
});
|
|
8547
|
+
}
|
|
8548
|
+
function finish(nextSelected, nextOther) {
|
|
8549
|
+
const answers = answersFor(nextSelected, nextOther);
|
|
8550
|
+
if (answers.some((a) => a.length === 0)) return;
|
|
8551
|
+
onSubmit(composeQuestionAnswer(questions, answers));
|
|
8552
|
+
}
|
|
8553
|
+
function advanceOrSubmit(nextSelected) {
|
|
8554
|
+
const nextUnanswered = questions.findIndex((_, i) => i > active && !(nextSelected[i] ?? []).length);
|
|
8555
|
+
if (nextUnanswered === -1) {
|
|
8556
|
+
finish(nextSelected, "");
|
|
8557
|
+
return;
|
|
8558
|
+
}
|
|
8559
|
+
setSelected(nextSelected);
|
|
8560
|
+
setActive(nextUnanswered);
|
|
8561
|
+
setHighlight(0);
|
|
8562
|
+
setOtherOpen(false);
|
|
8563
|
+
setOtherText("");
|
|
8564
|
+
}
|
|
8565
|
+
function pickOption(idx) {
|
|
8566
|
+
if (!q) return;
|
|
8567
|
+
const opt = q.options[idx];
|
|
8568
|
+
if (!opt) return;
|
|
8569
|
+
if (q.multiSelect) {
|
|
8570
|
+
const cur = selected[active] ?? [];
|
|
8571
|
+
const next = cur.includes(opt.label) ? cur.filter((l) => l !== opt.label) : [...cur, opt.label];
|
|
8572
|
+
setSelected({
|
|
8573
|
+
...selected,
|
|
8574
|
+
[active]: next
|
|
8575
|
+
});
|
|
8576
|
+
return;
|
|
8577
|
+
}
|
|
8578
|
+
advanceOrSubmit({
|
|
8579
|
+
...selected,
|
|
8580
|
+
[active]: [opt.label]
|
|
8581
|
+
});
|
|
8582
|
+
}
|
|
8583
|
+
function goToQuestion(index) {
|
|
8584
|
+
if (index < 0 || index >= questions.length || index === active) return;
|
|
8585
|
+
setActive(index);
|
|
8586
|
+
setHighlight(0);
|
|
8587
|
+
setOtherOpen(false);
|
|
8588
|
+
setOtherText("");
|
|
8589
|
+
}
|
|
8590
|
+
useKeyboard((key) => {
|
|
8591
|
+
if (!q) return;
|
|
8592
|
+
if (key.name === "escape") {
|
|
8593
|
+
if (otherOpen) {
|
|
8594
|
+
setOtherOpen(false);
|
|
8595
|
+
setOtherText("");
|
|
8596
|
+
return;
|
|
8597
|
+
}
|
|
8598
|
+
onCancel();
|
|
8599
|
+
return;
|
|
8600
|
+
}
|
|
8601
|
+
if (otherOpen) {
|
|
8602
|
+
if (key.name === "return" || key.name === "kpenter") {
|
|
8603
|
+
key.preventDefault();
|
|
8604
|
+
const custom = otherText.trim();
|
|
8605
|
+
if (!custom) return;
|
|
8606
|
+
const nextSelected = {
|
|
8607
|
+
...selected,
|
|
8608
|
+
[active]: q.multiSelect ? [...selected[active] ?? [], custom] : [custom]
|
|
8609
|
+
};
|
|
8610
|
+
if (q.multiSelect) {
|
|
8611
|
+
setSelected(nextSelected);
|
|
8612
|
+
setOtherOpen(false);
|
|
8613
|
+
setOtherText("");
|
|
8614
|
+
return;
|
|
8615
|
+
}
|
|
8616
|
+
advanceOrSubmit(nextSelected);
|
|
8617
|
+
return;
|
|
8618
|
+
}
|
|
8619
|
+
if (key.name === "backspace") {
|
|
8620
|
+
setOtherText((t) => t.slice(0, -1));
|
|
8621
|
+
return;
|
|
8622
|
+
}
|
|
8623
|
+
const ch = key.sequence ?? key.name;
|
|
8624
|
+
if (ch && ch.length === 1 && !key.ctrl && !key.meta) setOtherText((t) => t + ch);
|
|
8625
|
+
return;
|
|
8626
|
+
}
|
|
8627
|
+
if (key.name === "up") {
|
|
8628
|
+
setHighlight(Math.max(0, clamped - 1));
|
|
8629
|
+
return;
|
|
8630
|
+
}
|
|
8631
|
+
if (key.name === "down") {
|
|
8632
|
+
setHighlight(Math.min(rowCount - 1, clamped + 1));
|
|
8633
|
+
return;
|
|
8634
|
+
}
|
|
8635
|
+
if (key.name === "left" || (key.name === "tab" || key.sequence === "\x1B[Z") && key.shift) {
|
|
8636
|
+
key.preventDefault();
|
|
8637
|
+
goToQuestion(active - 1);
|
|
8638
|
+
return;
|
|
8639
|
+
}
|
|
8640
|
+
if (key.name === "right" || key.name === "tab" && !key.shift) {
|
|
8641
|
+
key.preventDefault();
|
|
8642
|
+
goToQuestion(active + 1);
|
|
8643
|
+
return;
|
|
8644
|
+
}
|
|
8645
|
+
if (key.name === "space" || key.sequence === " ") {
|
|
8646
|
+
key.preventDefault();
|
|
8647
|
+
if (!q.multiSelect) return;
|
|
8648
|
+
if (clamped === otherIndex) {
|
|
8649
|
+
setOtherOpen(true);
|
|
8650
|
+
return;
|
|
8651
|
+
}
|
|
8652
|
+
if (clamped === doneIndex) {
|
|
8653
|
+
if (picked.length === 0) return;
|
|
8654
|
+
advanceOrSubmit({
|
|
8655
|
+
...selected,
|
|
8656
|
+
[active]: picked
|
|
8657
|
+
});
|
|
8658
|
+
return;
|
|
8659
|
+
}
|
|
8660
|
+
pickOption(clamped);
|
|
8661
|
+
return;
|
|
8662
|
+
}
|
|
8663
|
+
if (key.name === "return" || key.name === "kpenter") {
|
|
8664
|
+
key.preventDefault();
|
|
8665
|
+
if (clamped === otherIndex) {
|
|
8666
|
+
setOtherOpen(true);
|
|
8667
|
+
return;
|
|
8668
|
+
}
|
|
8669
|
+
if (clamped === doneIndex) {
|
|
8670
|
+
if (picked.length === 0) return;
|
|
8671
|
+
advanceOrSubmit({
|
|
8672
|
+
...selected,
|
|
8673
|
+
[active]: picked
|
|
8674
|
+
});
|
|
8675
|
+
return;
|
|
8676
|
+
}
|
|
8677
|
+
pickOption(clamped);
|
|
8678
|
+
return;
|
|
8679
|
+
}
|
|
8680
|
+
const letter = (key.sequence ?? key.name ?? "").toLowerCase();
|
|
8681
|
+
if (letter.length === 1 && /[a-z]/.test(letter)) {
|
|
8682
|
+
const idx = letter.charCodeAt(0) - 97;
|
|
8683
|
+
if (idx === otherIndex || letter === "o") {
|
|
8684
|
+
setHighlight(otherIndex);
|
|
8685
|
+
setOtherOpen(true);
|
|
8686
|
+
return;
|
|
8687
|
+
}
|
|
8688
|
+
if (q.options[idx]) {
|
|
8689
|
+
setHighlight(idx);
|
|
8690
|
+
pickOption(idx);
|
|
8691
|
+
}
|
|
8692
|
+
}
|
|
8693
|
+
});
|
|
8694
|
+
if (!q) return null;
|
|
8695
|
+
const nav = questions.length > 1 ? `${active + 1}/${questions.length}` : null;
|
|
8696
|
+
return /* @__PURE__ */ jsxs("box", {
|
|
8697
|
+
style: {
|
|
8698
|
+
flexDirection: "column",
|
|
8699
|
+
flexGrow: 1
|
|
8700
|
+
},
|
|
8701
|
+
children: [
|
|
8702
|
+
nav ? /* @__PURE__ */ jsx("text", {
|
|
8703
|
+
fg: theme.dim,
|
|
8704
|
+
children: nav
|
|
8705
|
+
}) : null,
|
|
8706
|
+
/* @__PURE__ */ jsx("text", {
|
|
8707
|
+
fg: theme.fg,
|
|
8708
|
+
children: q.question
|
|
8709
|
+
}),
|
|
8710
|
+
/* @__PURE__ */ jsxs("box", {
|
|
8711
|
+
style: {
|
|
8712
|
+
flexDirection: "column",
|
|
8713
|
+
marginTop: 1
|
|
8714
|
+
},
|
|
8715
|
+
children: [
|
|
8716
|
+
q.options.map((opt, i) => {
|
|
8717
|
+
const isHi = !otherOpen && i === clamped;
|
|
8718
|
+
const isPicked = picked.includes(opt.label);
|
|
8719
|
+
return /* @__PURE__ */ jsxs("text", {
|
|
8720
|
+
fg: isHi ? theme.accent : theme.fg,
|
|
8721
|
+
children: [
|
|
8722
|
+
isHi ? "› " : " ",
|
|
8723
|
+
q.multiSelect ? isPicked ? "☑ " : "☐ " : "",
|
|
8724
|
+
String.fromCharCode(65 + i),
|
|
8725
|
+
". ",
|
|
8726
|
+
opt.label
|
|
8727
|
+
]
|
|
8728
|
+
}, opt.label);
|
|
8729
|
+
}),
|
|
8730
|
+
otherOpen ? /* @__PURE__ */ jsxs("text", {
|
|
8731
|
+
fg: theme.accent,
|
|
8732
|
+
children: [
|
|
8733
|
+
"› Other: ",
|
|
8734
|
+
otherText,
|
|
8735
|
+
/* @__PURE__ */ jsx("span", {
|
|
8736
|
+
fg: theme.dim,
|
|
8737
|
+
children: "_"
|
|
8738
|
+
})
|
|
8739
|
+
]
|
|
8740
|
+
}) : /* @__PURE__ */ jsxs("text", {
|
|
8741
|
+
fg: clamped === otherIndex ? theme.accent : theme.muted,
|
|
8742
|
+
children: [
|
|
8743
|
+
clamped === otherIndex ? "› " : " ",
|
|
8744
|
+
String.fromCharCode(65 + otherIndex),
|
|
8745
|
+
". Other"
|
|
8746
|
+
]
|
|
8747
|
+
}),
|
|
8748
|
+
q.multiSelect && !otherOpen ? /* @__PURE__ */ jsxs("text", {
|
|
8749
|
+
fg: clamped === doneIndex ? theme.accent : theme.muted,
|
|
8750
|
+
children: [clamped === doneIndex ? "› " : " ", "Done"]
|
|
8751
|
+
}) : null
|
|
8752
|
+
]
|
|
8753
|
+
}),
|
|
8754
|
+
/* @__PURE__ */ jsx("text", {
|
|
8755
|
+
fg: theme.dim,
|
|
8756
|
+
children: otherOpen ? "↵ submit · esc back" : q.multiSelect ? questions.length > 1 ? "space toggle · ← back · Done to continue · esc cancel" : "space toggle · Done to continue · esc cancel" : questions.length > 1 ? "↵ select · ← back · ↑/↓ move · esc cancel" : "↵ select · ↑/↓ move · esc cancel"
|
|
8757
|
+
})
|
|
8758
|
+
]
|
|
8759
|
+
});
|
|
8760
|
+
}
|
|
8761
|
+
|
|
8436
8762
|
//#endregion
|
|
8437
8763
|
//#region src/chat/tui/screens/card-picker.tsx
|
|
8438
8764
|
/**
|
|
@@ -11969,10 +12295,12 @@ function ChatScreen({ agent, conversation, attachRunId }) {
|
|
|
11969
12295
|
const credPromptOpen = credPrompt !== null;
|
|
11970
12296
|
const [computePrompt, setComputePrompt] = useState(null);
|
|
11971
12297
|
const computePromptOpen = computePrompt !== null;
|
|
12298
|
+
const [askPrompt, setAskPrompt] = useState(null);
|
|
12299
|
+
const askPromptOpen = askPrompt !== null;
|
|
11972
12300
|
const [restartConfirm, setRestartConfirm] = useState(false);
|
|
11973
12301
|
const [menuDismissed, setMenuDismissed] = useState(false);
|
|
11974
12302
|
const [menuHighlight, setMenuHighlight] = useState(0);
|
|
11975
|
-
const commandQuery = grantPrompt || credPrompt || computePrompt || restartConfirm ? null : detectCommandTrigger(input);
|
|
12303
|
+
const commandQuery = grantPrompt || credPrompt || computePrompt || askPrompt || restartConfirm ? null : detectCommandTrigger(input);
|
|
11976
12304
|
const menuCommands = commandQuery === null ? [] : filterSlashCommands(commandQuery);
|
|
11977
12305
|
const menuOpen = commandQuery !== null && !menuDismissed;
|
|
11978
12306
|
const menuHighlightClamped = Math.min(menuHighlight, Math.max(0, menuCommands.length - 1));
|
|
@@ -11990,7 +12318,7 @@ function ChatScreen({ agent, conversation, attachRunId }) {
|
|
|
11990
12318
|
credPromptRef.current = credPrompt;
|
|
11991
12319
|
const seenRunsRef = useRef(/* @__PURE__ */ new Set());
|
|
11992
12320
|
const shellSessionRef = useRef(null);
|
|
11993
|
-
const hostState = grantPrompt || credPrompt || computePrompt || restartConfirm ? "blocked" : run.kind === "idle" ? "idle" : "working";
|
|
12321
|
+
const hostState = grantPrompt || credPrompt || computePrompt || askPrompt || restartConfirm ? "blocked" : run.kind === "idle" ? "idle" : "working";
|
|
11994
12322
|
useEffect(() => {
|
|
11995
12323
|
agentHost.transition({ state: hostState });
|
|
11996
12324
|
}, [agentHost, hostState]);
|
|
@@ -12046,7 +12374,7 @@ function ChatScreen({ agent, conversation, attachRunId }) {
|
|
|
12046
12374
|
const reviewBelowBoxRef = useRef(null);
|
|
12047
12375
|
const paneDragRef = useRef(null);
|
|
12048
12376
|
const composerBoxHeight = composerRows + 2;
|
|
12049
|
-
const pendingVisible = !grantPrompt && !credPrompt && !computePrompt && !nonWebChannel && attachments.length > 0;
|
|
12377
|
+
const pendingVisible = !grantPrompt && !credPrompt && !computePrompt && !askPrompt && !nonWebChannel && attachments.length > 0;
|
|
12050
12378
|
const pendingRows = pendingVisible ? attachments.length + 2 : 0;
|
|
12051
12379
|
const credPromptHeight = 4 + (credPrompt && (credPrompt.error || credPrompt.submitting) ? 1 : 0);
|
|
12052
12380
|
const nonWebNoticeHeight = 5;
|
|
@@ -12112,7 +12440,8 @@ function ChatScreen({ agent, conversation, attachRunId }) {
|
|
|
12112
12440
|
reviewFocused,
|
|
12113
12441
|
reviewPlacement,
|
|
12114
12442
|
credPromptOpen,
|
|
12115
|
-
computePromptOpen
|
|
12443
|
+
computePromptOpen,
|
|
12444
|
+
askPromptOpen
|
|
12116
12445
|
]);
|
|
12117
12446
|
useEffect(() => {
|
|
12118
12447
|
if (!rest || !initialConversationId) return;
|
|
@@ -12514,6 +12843,13 @@ function ChatScreen({ agent, conversation, attachRunId }) {
|
|
|
12514
12843
|
});
|
|
12515
12844
|
return;
|
|
12516
12845
|
}
|
|
12846
|
+
if (action.kind === "answer_question") {
|
|
12847
|
+
setAskPrompt({
|
|
12848
|
+
cardId: item.id,
|
|
12849
|
+
questions: item.card.questions
|
|
12850
|
+
});
|
|
12851
|
+
return;
|
|
12852
|
+
}
|
|
12517
12853
|
updateCard(item.id, {
|
|
12518
12854
|
status: "busy",
|
|
12519
12855
|
detail: null
|
|
@@ -12628,6 +12964,11 @@ function ChatScreen({ agent, conversation, attachRunId }) {
|
|
|
12628
12964
|
const target = items.find((m) => m.kind === "card" && m.id === computePrompt.cardId);
|
|
12629
12965
|
if (!target || target.card.settled !== null || target.status === "done") setComputePrompt(null);
|
|
12630
12966
|
}, [computePrompt, items]);
|
|
12967
|
+
useEffect(() => {
|
|
12968
|
+
if (!askPrompt) return;
|
|
12969
|
+
const target = items.find((m) => m.kind === "card" && m.id === askPrompt.cardId);
|
|
12970
|
+
if (!target || target.card.settled !== null || target.status === "done") setAskPrompt(null);
|
|
12971
|
+
}, [askPrompt, items]);
|
|
12631
12972
|
const promptForCards = useCallback(() => {
|
|
12632
12973
|
const targets = itemsRef.current.filter(isActionableCard);
|
|
12633
12974
|
const [only] = targets;
|
|
@@ -12913,7 +13254,7 @@ function ChatScreen({ agent, conversation, attachRunId }) {
|
|
|
12913
13254
|
stageAttachment(image);
|
|
12914
13255
|
}, [stageAttachment]);
|
|
12915
13256
|
usePaste((event) => {
|
|
12916
|
-
if (cardPickerOpen || modelPickerOpen || themePickerOpen || prPickerOpen || helpOpen || bgTasksOpen || credPromptOpen || grantPrompt || computePromptOpen || restartConfirm || nonWebChannel) return;
|
|
13257
|
+
if (cardPickerOpen || modelPickerOpen || themePickerOpen || prPickerOpen || helpOpen || bgTasksOpen || credPromptOpen || grantPrompt || computePromptOpen || askPromptOpen || restartConfirm || nonWebChannel) return;
|
|
12917
13258
|
const text = event.metadata?.kind === "binary" ? "" : decodePasteBytes(event.bytes);
|
|
12918
13259
|
const route = routePaste({
|
|
12919
13260
|
kind: event.metadata?.kind,
|
|
@@ -13052,7 +13393,7 @@ function ChatScreen({ agent, conversation, attachRunId }) {
|
|
|
13052
13393
|
goTo
|
|
13053
13394
|
]);
|
|
13054
13395
|
const archiveConversation = useCallback(() => {
|
|
13055
|
-
if (grantPrompt || credPrompt || computePrompt || restartConfirm) return;
|
|
13396
|
+
if (grantPrompt || credPrompt || computePrompt || askPrompt || restartConfirm) return;
|
|
13056
13397
|
if (agentRunConversation) {
|
|
13057
13398
|
useStore.getState().showToast({ message: "agent runs can't be archived — ctrl+d in the picker deletes" });
|
|
13058
13399
|
return;
|
|
@@ -13087,6 +13428,7 @@ function ChatScreen({ agent, conversation, attachRunId }) {
|
|
|
13087
13428
|
grantPrompt,
|
|
13088
13429
|
credPrompt,
|
|
13089
13430
|
computePrompt,
|
|
13431
|
+
askPrompt,
|
|
13090
13432
|
restartConfirm,
|
|
13091
13433
|
agentRunConversation,
|
|
13092
13434
|
conversationId,
|
|
@@ -13537,7 +13879,7 @@ function ChatScreen({ agent, conversation, attachRunId }) {
|
|
|
13537
13879
|
}
|
|
13538
13880
|
if (reviewFocused) return;
|
|
13539
13881
|
}
|
|
13540
|
-
if (cardPickerOpen || modelPickerOpen || themePickerOpen || prPickerOpen || helpOpen || bgTasksOpen) return;
|
|
13882
|
+
if (cardPickerOpen || askPromptOpen || modelPickerOpen || themePickerOpen || prPickerOpen || helpOpen || bgTasksOpen) return;
|
|
13541
13883
|
if (grantPrompt) {
|
|
13542
13884
|
if (key.name === "y") {
|
|
13543
13885
|
confirmGrant();
|
|
@@ -13789,6 +14131,19 @@ function ChatScreen({ agent, conversation, attachRunId }) {
|
|
|
13789
14131
|
nonWebChannel
|
|
13790
14132
|
]);
|
|
13791
14133
|
const modelHint = formatModelHint(model, effort);
|
|
14134
|
+
if (askPrompt) return /* @__PURE__ */ jsx(AskPicker, {
|
|
14135
|
+
questions: askPrompt.questions,
|
|
14136
|
+
onSubmit: (text) => {
|
|
14137
|
+
const cardId = askPrompt.cardId;
|
|
14138
|
+
setAskPrompt(null);
|
|
14139
|
+
updateCard(cardId, {
|
|
14140
|
+
status: "done",
|
|
14141
|
+
detail: text
|
|
14142
|
+
});
|
|
14143
|
+
sendContent(text, []);
|
|
14144
|
+
},
|
|
14145
|
+
onCancel: () => setAskPrompt(null)
|
|
14146
|
+
});
|
|
13792
14147
|
if (cardPickerOpen) return /* @__PURE__ */ jsx(CardPicker, {
|
|
13793
14148
|
cards: actionableCards,
|
|
13794
14149
|
onSelect: (card) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as mintPortalDeviceToken, c as unifyLegacyCliGrants, i as grantPortalAccess, l as startTlsForward, n as fetchPortalDevices, r as findThisDevice } from "./api-
|
|
2
|
+
import { a as mintPortalDeviceToken, c as unifyLegacyCliGrants, i as grantPortalAccess, l as startTlsForward, n as fetchPortalDevices, r as findThisDevice } from "./api-TwLD7ibI.mjs";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import * as childProcess from "node:child_process";
|
|
@@ -568,6 +568,8 @@ var PortalClient = class {
|
|
|
568
568
|
legacyGrantsChecked = false;
|
|
569
569
|
wakeWatch = null;
|
|
570
570
|
lastTick = Date.now();
|
|
571
|
+
handingOff = false;
|
|
572
|
+
handoffResolve = null;
|
|
571
573
|
heartbeatConfig;
|
|
572
574
|
constructor(opts) {
|
|
573
575
|
this.opts = opts;
|
|
@@ -641,6 +643,48 @@ var PortalClient = class {
|
|
|
641
643
|
this.jobs = null;
|
|
642
644
|
this.ws?.close();
|
|
643
645
|
this.ws = null;
|
|
646
|
+
this.finishHandoff();
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Begin a make-before-break handoff to a successor daemon. Keep the current
|
|
650
|
+
* portal socket (and thus this machine's presence claim) alive so there is no
|
|
651
|
+
* `registered but not connected` gap, but stop redialing once it closes — the
|
|
652
|
+
* successor's `claim()` supersedes us and the server closes our socket, which
|
|
653
|
+
* is our cue to step down. Resolves when the socket has closed (clean
|
|
654
|
+
* handoff) OR after `timeoutMs` (the successor never came up, so we release
|
|
655
|
+
* the slot rather than hold it forever). Idempotent.
|
|
656
|
+
*/
|
|
657
|
+
beginHandoff(timeoutMs) {
|
|
658
|
+
if (this.disposed) return Promise.resolve();
|
|
659
|
+
if (this.handingOff) return new Promise((resolve) => {
|
|
660
|
+
const prev = this.handoffResolve;
|
|
661
|
+
this.handoffResolve = () => {
|
|
662
|
+
prev?.();
|
|
663
|
+
resolve();
|
|
664
|
+
};
|
|
665
|
+
});
|
|
666
|
+
this.handingOff = true;
|
|
667
|
+
if (!this.ws) return Promise.resolve();
|
|
668
|
+
return new Promise((resolve) => {
|
|
669
|
+
let done = false;
|
|
670
|
+
const finish = () => {
|
|
671
|
+
if (done) return;
|
|
672
|
+
done = true;
|
|
673
|
+
clearTimeout(timer);
|
|
674
|
+
resolve();
|
|
675
|
+
};
|
|
676
|
+
this.handoffResolve = finish;
|
|
677
|
+
const timer = setTimeout(() => {
|
|
678
|
+
this.disable();
|
|
679
|
+
this.finishHandoff();
|
|
680
|
+
}, timeoutMs);
|
|
681
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
finishHandoff() {
|
|
685
|
+
const resolve = this.handoffResolve;
|
|
686
|
+
this.handoffResolve = null;
|
|
687
|
+
resolve?.();
|
|
644
688
|
}
|
|
645
689
|
/**
|
|
646
690
|
* Grant one agent access to this machine (default-deny; user-initiated).
|
|
@@ -702,18 +746,18 @@ var PortalClient = class {
|
|
|
702
746
|
this.opts.onMachineName(identity.machineName, identity.machineNameSource);
|
|
703
747
|
}
|
|
704
748
|
let backoff = this.heartbeatConfig.initialBackoffMs;
|
|
705
|
-
while (this.enabled && !this.disposed) {
|
|
749
|
+
while (this.enabled && !this.disposed && !this.handingOff) {
|
|
706
750
|
this.setStatus("connecting");
|
|
707
751
|
try {
|
|
708
752
|
const token = await this.wsToken();
|
|
709
753
|
if (await this.runConnection(token)) backoff = this.heartbeatConfig.initialBackoffMs;
|
|
710
754
|
else backoff = Math.min(backoff * 2, this.heartbeatConfig.maxBackoffMs);
|
|
711
755
|
} catch (err) {
|
|
712
|
-
if (!this.enabled || this.disposed) break;
|
|
756
|
+
if (!this.enabled || this.disposed || this.handingOff) break;
|
|
713
757
|
this.setStatus("error", errorMessage(err));
|
|
714
758
|
backoff = Math.min(backoff * 2, this.heartbeatConfig.maxBackoffMs);
|
|
715
759
|
}
|
|
716
|
-
if (!this.enabled || this.disposed) break;
|
|
760
|
+
if (!this.enabled || this.disposed || this.handingOff) break;
|
|
717
761
|
await sleep(Math.random() * backoff);
|
|
718
762
|
}
|
|
719
763
|
}
|
|
@@ -772,6 +816,7 @@ var PortalClient = class {
|
|
|
772
816
|
jobs.killAll();
|
|
773
817
|
if (this.jobs === jobs) this.jobs = null;
|
|
774
818
|
if (this.ws === ws) this.ws = null;
|
|
819
|
+
if (this.handingOff) this.finishHandoff();
|
|
775
820
|
settle();
|
|
776
821
|
});
|
|
777
822
|
});
|