@wrongstack/tui 1.0.27 → 1.0.29
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/app-key-handler.d.ts +33 -2
- package/dist/auth-panel-model.d.ts +7 -0
- package/dist/index.js +69 -15
- package/package.json +7 -7
|
@@ -1,8 +1,39 @@
|
|
|
1
1
|
import { type KeyEvent } from './components/input.js';
|
|
2
2
|
import type { AppKeyHandlerOptions } from './key-handler-context.js';
|
|
3
3
|
export type { AppKeyHandlerOptions } from './key-handler-context.js';
|
|
4
|
-
/**
|
|
5
|
-
export
|
|
4
|
+
/** One delivered input event, as the host passes it to `handleKey`. */
|
|
5
|
+
export interface NextStepsTakeoverEvent {
|
|
6
|
+
/** Text payload of the event — empty for special keys and pointer reports. */
|
|
7
|
+
input: string;
|
|
8
|
+
key: KeyEvent;
|
|
9
|
+
/**
|
|
10
|
+
* Current composer buffer. The armed submit clears the composer when it
|
|
11
|
+
* fires, so text sitting here is what makes ANY event a takeover.
|
|
12
|
+
*/
|
|
13
|
+
draft: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* True when this event means the user has taken control of an armed automatic
|
|
17
|
+
* turn, i.e. the armed next-steps submit must be cancelled.
|
|
18
|
+
*
|
|
19
|
+
* Pointer reports are NOT keyboard activity: the input layer emits mouse
|
|
20
|
+
* press/release/wheel/move as `KeyEvent`s carrying `mouse` (see
|
|
21
|
+
* `components/input.tsx`, which builds them as `EMPTY_KEY` + `mouse`). Treating
|
|
22
|
+
* them as input made a plain click, a wheel scroll, or a drag through the
|
|
23
|
+
* transcript look like typing and silently killed the armed countdown, so the
|
|
24
|
+
* automatic turn never advanced.
|
|
25
|
+
*
|
|
26
|
+
* The same reasoning covers keyboard navigation: `PgUp`/`PgDn`, the caret
|
|
27
|
+
* keys, and the Ctrl+U/Ctrl+D paging chord move through the transcript exactly
|
|
28
|
+
* like the wheel does, so on an empty composer they leave the countdown armed
|
|
29
|
+
* too. Everything that can put text in the composer — typing, editing, pasting,
|
|
30
|
+
* ↑/↓ history recall, Tab accepting the suggestion, Enter — still cancels, and
|
|
31
|
+
* so does any event at all once the composer holds text, because the armed
|
|
32
|
+
* submit calls `clearDraft()` when it fires.
|
|
33
|
+
*/
|
|
34
|
+
export declare function shouldStopNextStepsAutoSubmit(event: NextStepsTakeoverEvent): boolean;
|
|
35
|
+
/** Cancel an armed automatic turn when this event is a real user takeover. */
|
|
36
|
+
export declare function stopNextStepsAutoSubmitOnKey(cancel: () => void, event: NextStepsTakeoverEvent): void;
|
|
6
37
|
/** Creates the terminal key host around focused overlay/input routers. */
|
|
7
38
|
export declare function createAppKeyHandler(options: AppKeyHandlerOptions): (input: string, key: KeyEvent) => Promise<void>;
|
|
8
39
|
//# sourceMappingURL=app-key-handler.d.ts.map
|
|
@@ -42,6 +42,13 @@ export interface AuthOAuthStrategyRow {
|
|
|
42
42
|
providerId: string;
|
|
43
43
|
label: string;
|
|
44
44
|
description?: string | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Extended guidance, one entry per paragraph (mirrors
|
|
47
|
+
* `ProviderAuthStrategyMetadata.notes`). Unlike `description` this may be
|
|
48
|
+
* several sentences, so the oauth view renders it wrapped in the
|
|
49
|
+
* focused-strategy detail area, not inline on the row.
|
|
50
|
+
*/
|
|
51
|
+
notes?: readonly string[] | undefined;
|
|
45
52
|
}
|
|
46
53
|
/** Strategy ids are runtime-extensible. */
|
|
47
54
|
export type AuthOAuthKind = string;
|
package/dist/index.js
CHANGED
|
@@ -42278,7 +42278,7 @@ function KanbanPanel({
|
|
|
42278
42278
|
setPrompt({ ...prompt, buffer: prompt.buffer.slice(0, -1) });
|
|
42279
42279
|
return;
|
|
42280
42280
|
}
|
|
42281
|
-
if (input && !key2.ctrl && !key2.meta && input >= " ") {
|
|
42281
|
+
if (input && !key2.ctrl && !key2.meta && input >= " " && !isLeakedMouseInput(input)) {
|
|
42282
42282
|
setPrompt({ ...prompt, buffer: `${prompt.buffer}${input}` });
|
|
42283
42283
|
}
|
|
42284
42284
|
return;
|
|
@@ -47456,6 +47456,23 @@ function keySummary(keyRow) {
|
|
|
47456
47456
|
const created = keyRow.createdAt ? ` ${keyRow.createdAt.slice(0, 10)}` : "";
|
|
47457
47457
|
return `${keyRow.masked}${method}${created}`;
|
|
47458
47458
|
}
|
|
47459
|
+
var MAX_OAUTH_NOTE_LINES = 8;
|
|
47460
|
+
function wrapNoteText(text, width) {
|
|
47461
|
+
const words = text.split(/\s+/).filter((w) => w.length > 0);
|
|
47462
|
+
if (words.length === 0) return [];
|
|
47463
|
+
const lines = [];
|
|
47464
|
+
let cur = words[0] ?? "";
|
|
47465
|
+
for (const word of words.slice(1)) {
|
|
47466
|
+
if (cur.length + 1 + word.length <= width) {
|
|
47467
|
+
cur += ` ${word}`;
|
|
47468
|
+
continue;
|
|
47469
|
+
}
|
|
47470
|
+
lines.push(cur);
|
|
47471
|
+
cur = word.length > width ? word.slice(0, width) : word;
|
|
47472
|
+
}
|
|
47473
|
+
lines.push(cur);
|
|
47474
|
+
return lines;
|
|
47475
|
+
}
|
|
47459
47476
|
function renderRow(row, focused, i) {
|
|
47460
47477
|
const marker = focused ? "\u203A" : " ";
|
|
47461
47478
|
const rowColor = focused ? UI_COLORS.focused : void 0;
|
|
@@ -47646,7 +47663,21 @@ function AuthPanel({ panel, maxRows, columns }) {
|
|
|
47646
47663
|
const size = useTerminalSize();
|
|
47647
47664
|
const budget = maxRows ?? Math.max(8, size.rows - 7);
|
|
47648
47665
|
const compact2 = budget < 14;
|
|
47649
|
-
const
|
|
47666
|
+
const noteWidth = Math.max(20, (columns ?? size.columns) - 4);
|
|
47667
|
+
const oauthFocus = panel.view === "oauth" ? panel.oauthStrategies?.[panel.selected] : void 0;
|
|
47668
|
+
const oauthNoteLines = !compact2 && oauthFocus?.notes ? oauthFocus.notes.flatMap(
|
|
47669
|
+
(note, i) => (
|
|
47670
|
+
// Blank separator between paragraphs; a space (not '') so Ink
|
|
47671
|
+
// emits the row and the reserve below matches the render.
|
|
47672
|
+
(i > 0 ? [" "] : []).concat(wrapNoteText(note, noteWidth))
|
|
47673
|
+
)
|
|
47674
|
+
).slice(
|
|
47675
|
+
0,
|
|
47676
|
+
// 8 keeps at least one list row visible on any non-compact
|
|
47677
|
+
// budget: total = 8 border/title/legend/warning + N + 1 list.
|
|
47678
|
+
Math.min(MAX_OAUTH_NOTE_LINES, Math.max(0, budget - 8))
|
|
47679
|
+
) : [];
|
|
47680
|
+
const extraRows = (panel.hint ? 1 : 0) + (panel.confirm ? compact2 ? 1 : 2 : 0) + (panel.input ? compact2 ? 1 : 2 : 0) + (panel.view === "catalog" ? compact2 ? 1 : 2 : 0) + (!compact2 && panel.view === "provider" ? 2 : 0) + (!compact2 && panel.view === "oauth" ? 3 + oauthNoteLines.length : 0) + (!compact2 && panel.view === "list" && panel.providers.length === 0 ? 2 : 0) + (!compact2 && panel.view === "catalog" && panel.busy ? 2 : 0);
|
|
47650
47681
|
const maxVisible = Math.max(
|
|
47651
47682
|
1,
|
|
47652
47683
|
budget - 4 - extraRows - (panel.view === "flow" ? compact2 ? 0 : 1 : 2)
|
|
@@ -47698,6 +47729,7 @@ function AuthPanel({ panel, maxRows, columns }) {
|
|
|
47698
47729
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text2, { color: UI_COLORS.warning, wrap: "truncate-end", children: "\u26A0 Subscription tokens used outside official clients may violate provider Terms \u2014" }),
|
|
47699
47730
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text2, { color: UI_COLORS.warning, wrap: "truncate-end", children: "your account could be rate-limited or banned. An API key is the sanctioned path." })
|
|
47700
47731
|
] }) : null,
|
|
47732
|
+
oauthNoteLines.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box2, { flexDirection: "column", children: oauthNoteLines.map((line, ni) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text2, { dimColor: true, wrap: "truncate-end", children: line }, `on-${ni}`)) }) : null,
|
|
47701
47733
|
panel.view === "catalog" ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box2, { marginTop: compact2 ? 0 : 1, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text2, { wrap: "truncate-end", children: [
|
|
47702
47734
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text2, { dimColor: true, children: "filter: " }),
|
|
47703
47735
|
panel.filter.length > 0 ? panel.filter : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text2, { dimColor: true, children: "(type to search)" }),
|
|
@@ -48670,6 +48702,7 @@ function isClearConfirmation(value) {
|
|
|
48670
48702
|
return value === "YES";
|
|
48671
48703
|
}
|
|
48672
48704
|
function clearConfirmationKeyResult(value, input, key2) {
|
|
48705
|
+
if (input && isLeakedMouseInput(input)) return { decision: null, value };
|
|
48673
48706
|
if (key2.escape) return { decision: false, value };
|
|
48674
48707
|
if (key2.ctrl || key2.meta) return { decision: null, value };
|
|
48675
48708
|
if (key2.return) {
|
|
@@ -54856,6 +54889,7 @@ function RefineCountdownPanel({
|
|
|
54856
54889
|
}, []);
|
|
54857
54890
|
use_input_default((input, key2) => {
|
|
54858
54891
|
if (resolvedRef.current) return;
|
|
54892
|
+
if (input && isLeakedMouseInput(input)) return;
|
|
54859
54893
|
resolvedRef.current = true;
|
|
54860
54894
|
const isBackspace = key2.backspace || key2.delete || input === "\x7F" || input === "\b";
|
|
54861
54895
|
if (key2.escape || isBackspace) onDecision("cancel");
|
|
@@ -83620,9 +83654,9 @@ function useResourceSlashCommands(deps) {
|
|
|
83620
83654
|
// src/hooks/use-session-slash-commands.ts
|
|
83621
83655
|
var import_react157 = __toESM(require_react(), 1);
|
|
83622
83656
|
import {
|
|
83623
|
-
areSubagentsAllowed,
|
|
83624
83657
|
isSubagentPolicyLocked,
|
|
83625
|
-
|
|
83658
|
+
setSessionSubagentPolicy,
|
|
83659
|
+
subagentPolicyMode
|
|
83626
83660
|
} from "@wrongstack/core/coordination";
|
|
83627
83661
|
import { toErrorMessage as toErrorMessage12 } from "@wrongstack/core/utils";
|
|
83628
83662
|
var RESUME_PICKER_SESSIONS = 500;
|
|
@@ -83632,22 +83666,26 @@ function useSessionSlashCommands(deps, part) {
|
|
|
83632
83666
|
if (part !== "head") return;
|
|
83633
83667
|
const cmd = {
|
|
83634
83668
|
name: "solo",
|
|
83635
|
-
description: "Control session-only subagents before the first message: /solo on|off|status.",
|
|
83669
|
+
description: "Control session-only subagents before the first message: /solo on|companions|off|status.",
|
|
83636
83670
|
async run(args) {
|
|
83637
83671
|
const action = (args ?? "").trim().toLowerCase() || "status";
|
|
83638
|
-
const
|
|
83672
|
+
const mode = subagentPolicyMode(agent.ctx);
|
|
83639
83673
|
if (action === "status") {
|
|
83674
|
+
const state = mode === "all" ? "off" : mode === "companions" ? "on (companions allowed)" : "on";
|
|
83640
83675
|
return {
|
|
83641
|
-
message: `Solo session is ${
|
|
83676
|
+
message: `Solo session is ${state}${isSubagentPolicyLocked(agent.ctx) ? " (locked)" : ""}.`
|
|
83642
83677
|
};
|
|
83643
83678
|
}
|
|
83644
|
-
if (action !== "on" && action !== "off") {
|
|
83645
|
-
return { message: "Usage: /solo on|off|status" };
|
|
83679
|
+
if (action !== "on" && action !== "off" && action !== "companions") {
|
|
83680
|
+
return { message: "Usage: /solo on|companions|off|status" };
|
|
83646
83681
|
}
|
|
83647
83682
|
try {
|
|
83648
|
-
await
|
|
83683
|
+
await setSessionSubagentPolicy(
|
|
83684
|
+
agent.ctx,
|
|
83685
|
+
action === "off" ? "all" : action === "companions" ? "companions" : "none"
|
|
83686
|
+
);
|
|
83649
83687
|
return {
|
|
83650
|
-
message: action === "on" ? "Solo session enabled. Chimera, delegation, and background subagents are blocked." : "Solo session disabled. Subagents are allowed for this session."
|
|
83688
|
+
message: action === "on" ? "Solo session enabled. Chimera, delegation, background subagents, and companions are blocked." : action === "companions" ? "Solo session enabled with companions. Chimera, delegation, and background subagents are blocked; the read-only memory and explore companions still run." : "Solo session disabled. Subagents are allowed for this session."
|
|
83651
83689
|
};
|
|
83652
83690
|
} catch (err) {
|
|
83653
83691
|
return { message: toErrorMessage12(err) };
|
|
@@ -85646,8 +85684,19 @@ async function routePointerEvents(ctx, input, key2) {
|
|
|
85646
85684
|
}
|
|
85647
85685
|
|
|
85648
85686
|
// src/app-key-handler.ts
|
|
85649
|
-
function
|
|
85650
|
-
|
|
85687
|
+
function isComposerEmptyNavigation(input, key2) {
|
|
85688
|
+
if (key2.pageUp || key2.pageDown || key2.home || key2.end) return true;
|
|
85689
|
+
if (key2.leftArrow || key2.rightArrow) return true;
|
|
85690
|
+
return key2.ctrl && (input === "u" || input === "d");
|
|
85691
|
+
}
|
|
85692
|
+
function shouldStopNextStepsAutoSubmit(event) {
|
|
85693
|
+
const { input, key: key2, draft } = event;
|
|
85694
|
+
if (draft !== "") return true;
|
|
85695
|
+
if (key2.mouse) return false;
|
|
85696
|
+
return !isComposerEmptyNavigation(input, key2);
|
|
85697
|
+
}
|
|
85698
|
+
function stopNextStepsAutoSubmitOnKey(cancel, event) {
|
|
85699
|
+
if (shouldStopNextStepsAutoSubmit(event)) cancel();
|
|
85651
85700
|
}
|
|
85652
85701
|
function createAppKeyHandler(options) {
|
|
85653
85702
|
const {
|
|
@@ -85660,7 +85709,8 @@ function createAppKeyHandler(options) {
|
|
|
85660
85709
|
terminalColumns,
|
|
85661
85710
|
terminalRows,
|
|
85662
85711
|
mainColumnWidth,
|
|
85663
|
-
cancelNextStepsCountdown
|
|
85712
|
+
cancelNextStepsCountdown,
|
|
85713
|
+
draftRef
|
|
85664
85714
|
} = options;
|
|
85665
85715
|
const stdout = { columns: terminalColumns, rows: terminalRows };
|
|
85666
85716
|
const historyWidth = mainColumnWidth > 0 ? mainColumnWidth : stdout?.columns ?? 80;
|
|
@@ -85679,7 +85729,11 @@ function createAppKeyHandler(options) {
|
|
|
85679
85729
|
);
|
|
85680
85730
|
const handleKey = async (input, key2) => {
|
|
85681
85731
|
if (!key2.escape) ctx.lastEscAtRef.current = 0;
|
|
85682
|
-
stopNextStepsAutoSubmitOnKey(cancelNextStepsCountdown
|
|
85732
|
+
stopNextStepsAutoSubmitOnKey(cancelNextStepsCountdown, {
|
|
85733
|
+
input,
|
|
85734
|
+
key: key2,
|
|
85735
|
+
draft: draftRef.current.buffer
|
|
85736
|
+
});
|
|
85683
85737
|
if (routeCtrlCEscalation(ctx, input, key2)) return;
|
|
85684
85738
|
if (routeModalOverlay(ctx, input, key2)) {
|
|
85685
85739
|
ctx.lastEscAtRef.current = 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/tui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.29",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack terminal UI: Ink-based interactive renderer with history, pickers, and slash menus.",
|
|
6
6
|
"repository": {
|
|
@@ -26,12 +26,12 @@
|
|
|
26
26
|
"!dist/**/*.map"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@wrongstack/core": "1.0.
|
|
30
|
-
"@wrongstack/kanban": "1.0.
|
|
31
|
-
"@wrongstack/runtime": "1.0.
|
|
32
|
-
"@wrongstack/sage": "1.0.
|
|
33
|
-
"@wrongstack/sdd": "1.0.
|
|
34
|
-
"@wrongstack/tools": "1.0.
|
|
29
|
+
"@wrongstack/core": "1.0.29",
|
|
30
|
+
"@wrongstack/kanban": "1.0.29",
|
|
31
|
+
"@wrongstack/runtime": "1.0.29",
|
|
32
|
+
"@wrongstack/sage": "1.0.29",
|
|
33
|
+
"@wrongstack/sdd": "1.0.29",
|
|
34
|
+
"@wrongstack/tools": "1.0.29",
|
|
35
35
|
"string-width": "8.2.2",
|
|
36
36
|
"wrap-ansi": "^10.0.1"
|
|
37
37
|
},
|