xiaotime 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/approval_menu.test.d.ts +15 -0
- package/dist/__tests__/approval_menu.test.d.ts.map +1 -0
- package/dist/__tests__/approval_menu.test.js +107 -0
- package/dist/__tests__/approval_menu.test.js.map +1 -0
- package/dist/__tests__/server_tool_render.test.d.ts +17 -0
- package/dist/__tests__/server_tool_render.test.d.ts.map +1 -0
- package/dist/__tests__/server_tool_render.test.js +74 -0
- package/dist/__tests__/server_tool_render.test.js.map +1 -0
- package/dist/__tests__/settings.test.d.ts +16 -0
- package/dist/__tests__/settings.test.d.ts.map +1 -0
- package/dist/__tests__/settings.test.js +289 -0
- package/dist/__tests__/settings.test.js.map +1 -0
- package/dist/__tests__/tools.test.d.ts +17 -0
- package/dist/__tests__/tools.test.d.ts.map +1 -0
- package/dist/__tests__/tools.test.js +381 -0
- package/dist/__tests__/tools.test.js.map +1 -0
- package/dist/display.d.ts +19 -0
- package/dist/display.d.ts.map +1 -1
- package/dist/display.js +23 -0
- package/dist/display.js.map +1 -1
- package/dist/settings.d.ts +109 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +332 -0
- package/dist/settings.js.map +1 -0
- package/dist/tools.d.ts +11 -3
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +96 -33
- package/dist/tools.js.map +1 -1
- package/dist/ui_state.d.ts +134 -0
- package/dist/ui_state.d.ts.map +1 -0
- package/dist/ui_state.js +460 -0
- package/dist/ui_state.js.map +1 -0
- package/dist/ws.d.ts +5 -0
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +99 -66
- package/dist/ws.js.map +1 -1
- package/package.json +5 -2
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-UI state machine for the xiaotime dev CLI.
|
|
3
|
+
*
|
|
4
|
+
* SPRINT-CLI-STATE-MACHINE-1 (#6163 / closes #6127) shipped the original
|
|
5
|
+
* state machine + raw-mode approval prompt. That fix eliminated the
|
|
6
|
+
* defaults-to-N half of the "yy" double-echo bug but the visible-yy half
|
|
7
|
+
* persisted because `readline.createInterface({ terminal: true })` attaches
|
|
8
|
+
* its own keypress handler to stdin that echoes characters via its own
|
|
9
|
+
* stdout writes; `rl.pause()` stops `line` events but does NOT detach that
|
|
10
|
+
* echo path, and `setRawMode(true)` only suppresses kernel-level echo, not
|
|
11
|
+
* readline's.
|
|
12
|
+
*
|
|
13
|
+
* SPRINT-CLI-APPROVAL-MENU-1 (#6509) replaces `promptApproval`'s `[y/N]`
|
|
14
|
+
* one-shot prompt with `promptApprovalMenu` — a three-option numbered menu
|
|
15
|
+
* (Run / Run + allowlist / Reject + feedback). The rl is explicitly
|
|
16
|
+
* **closed and recreated** around the menu render so its keypress echo
|
|
17
|
+
* physically can't fire (the rl object is gone for the duration of the
|
|
18
|
+
* menu). Persistent close listeners survive the recreation via
|
|
19
|
+
* `onRlClose()` — callers that need a stable close hook register through
|
|
20
|
+
* that instead of `getRl().on("close", ...)`.
|
|
21
|
+
*
|
|
22
|
+
* The state machine has four states:
|
|
23
|
+
*
|
|
24
|
+
* IDLE — at the `you:` prompt, awaiting user input.
|
|
25
|
+
* GENERATING — model is streaming tokens (or about to). Spinner.
|
|
26
|
+
* RUNNING_TOOL — a server-side tool is running. Spinner.
|
|
27
|
+
* AWAITING_APPROVAL — user must navigate the approval menu for a
|
|
28
|
+
* client-side tool. Main rl is closed; raw stdin
|
|
29
|
+
* captures keystrokes.
|
|
30
|
+
*
|
|
31
|
+
* The WS handler in `ws.ts` is the sole driver. Transitions:
|
|
32
|
+
*
|
|
33
|
+
* IDLE -- user line received -> GENERATING
|
|
34
|
+
* GENERATING -- text_delta -> GENERATING (refresh)
|
|
35
|
+
* GENERATING -- tool_call (server) -> RUNNING_TOOL
|
|
36
|
+
* GENERATING -- tool_call (client+approval) -> AWAITING_APPROVAL
|
|
37
|
+
* AWAITING_APPROVAL -- menu selection -> RUNNING_TOOL
|
|
38
|
+
* RUNNING_TOOL -- tool_result sent -> GENERATING
|
|
39
|
+
* GENERATING -- message_stop -> IDLE
|
|
40
|
+
* ANY -- error -> IDLE
|
|
41
|
+
*/
|
|
42
|
+
import * as readline from "readline";
|
|
43
|
+
export type CliState = "idle" | "generating" | "running_tool" | "awaiting_approval";
|
|
44
|
+
export type ApprovalMenuResult = {
|
|
45
|
+
approved: true;
|
|
46
|
+
allowlistRequested: boolean;
|
|
47
|
+
} | {
|
|
48
|
+
approved: false;
|
|
49
|
+
rejectReason: string;
|
|
50
|
+
};
|
|
51
|
+
export declare class SessionUI {
|
|
52
|
+
private state;
|
|
53
|
+
private spinnerTimer;
|
|
54
|
+
private spinnerFrame;
|
|
55
|
+
private spinnerStartedAt;
|
|
56
|
+
private spinnerLabel;
|
|
57
|
+
private spinnerLineDirty;
|
|
58
|
+
private rl;
|
|
59
|
+
private lineListener;
|
|
60
|
+
/**
|
|
61
|
+
* Close-listener callbacks that should survive rl recreations. The
|
|
62
|
+
* approval-menu path closes + recreates `rl` to physically detach
|
|
63
|
+
* readline's keypress echo. ws.ts registers its "Session paused"
|
|
64
|
+
* resume hint here so it stays attached across recreations.
|
|
65
|
+
*/
|
|
66
|
+
private closeListeners;
|
|
67
|
+
constructor();
|
|
68
|
+
/** Hand the underlying readline to callers that need it for cleanup. */
|
|
69
|
+
getRl(): readline.Interface;
|
|
70
|
+
/**
|
|
71
|
+
* Register a callback to fire when the rl reports a non-suppressed
|
|
72
|
+
* `close` event (the only one users observe is Ctrl+C at the main
|
|
73
|
+
* prompt — close-during-approval is suppressed via removeAllListeners
|
|
74
|
+
* before `rl.close()`). Callbacks are re-attached on every rl
|
|
75
|
+
* recreation so they outlive `promptApprovalMenu`.
|
|
76
|
+
*/
|
|
77
|
+
onRlClose(listener: () => void): void;
|
|
78
|
+
/** Set the explicit current state. Drives spinner start/stop. */
|
|
79
|
+
setState(next: CliState): void;
|
|
80
|
+
getState(): CliState;
|
|
81
|
+
/** Wait for one line of user input on the main rl. */
|
|
82
|
+
promptLine(): Promise<string>;
|
|
83
|
+
/**
|
|
84
|
+
* Three-option numbered approval menu. Replaces the pre-#6509
|
|
85
|
+
* `[y/N]` one-shot prompt.
|
|
86
|
+
*
|
|
87
|
+
* Visual:
|
|
88
|
+
* ❯ 1. Run
|
|
89
|
+
* 2. Run and don't ask again
|
|
90
|
+
* 3. Reject and tell Claude why
|
|
91
|
+
* (1-3, ↑↓ Enter, Esc to reject)
|
|
92
|
+
*
|
|
93
|
+
* Controls:
|
|
94
|
+
* - Number key 1/2/3 — one-shot select
|
|
95
|
+
* - Arrow Up/Down — move focus
|
|
96
|
+
* - Enter — select focused
|
|
97
|
+
* - Esc — equivalent to option 3 (reject)
|
|
98
|
+
* - Ctrl+C — reject + propagate SIGINT (cancels session)
|
|
99
|
+
*
|
|
100
|
+
* The rl is closed before the menu render and recreated after. This
|
|
101
|
+
* physically removes readline's keypress echo from stdin for the
|
|
102
|
+
* duration of the menu — the bug fix the #6163 patch attempted via
|
|
103
|
+
* `rl.pause()` (which only stops `line` events, not the keypress
|
|
104
|
+
* echo). Persistent close listeners are stashed in
|
|
105
|
+
* `this.closeListeners` and re-attached on recreate; the close event
|
|
106
|
+
* triggered by THIS close() is suppressed via removeAllListeners
|
|
107
|
+
* before the call so it doesn't fire the "Session paused" hint.
|
|
108
|
+
*
|
|
109
|
+
* For option 3, after the menu resolves the recreated rl prompts
|
|
110
|
+
* once for a free-form rejection reason (line-mode). Empty input is
|
|
111
|
+
* allowed.
|
|
112
|
+
*
|
|
113
|
+
* The returned `allowlistRequested: true` (option 2) is a signal to
|
|
114
|
+
* the caller — PR2 wires it to write the pattern to
|
|
115
|
+
* `~/.xiaotime/settings.json`. PR1 callers may ignore it.
|
|
116
|
+
*/
|
|
117
|
+
promptApprovalMenu(toolHeader?: string): Promise<ApprovalMenuResult>;
|
|
118
|
+
private _makeRl;
|
|
119
|
+
private _renderMenuAndGetSelection;
|
|
120
|
+
/** Total lines the menu occupies: one per option + one hint line. */
|
|
121
|
+
private _menuLineCount;
|
|
122
|
+
private _renderMenu;
|
|
123
|
+
private _rerenderMenu;
|
|
124
|
+
private _clearMenu;
|
|
125
|
+
private startSpinner;
|
|
126
|
+
private renderSpinnerFrame;
|
|
127
|
+
private stopSpinner;
|
|
128
|
+
/** Called by the text_delta renderer the first time a real model
|
|
129
|
+
* token arrives during a `generating` state. Clears the spinner so
|
|
130
|
+
* the token prints cleanly, but leaves the state at `generating`
|
|
131
|
+
* (token streaming continues without re-spinning). */
|
|
132
|
+
onFirstToken(): void;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=ui_state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui_state.d.ts","sourceRoot":"","sources":["../src/ui_state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAGrC,MAAM,MAAM,QAAQ,GAChB,MAAM,GACN,YAAY,GACZ,cAAc,GACd,mBAAmB,CAAC;AAExB,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,kBAAkB,EAAE,OAAO,CAAA;CAAE,GAC/C;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAa9C,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,YAAY,CAAM;IAK1B,OAAO,CAAC,gBAAgB,CAAS;IAEjC,OAAO,CAAC,EAAE,CAAqB;IAC/B,OAAO,CAAC,YAAY,CAAyC;IAC7D;;;;;OAKG;IACH,OAAO,CAAC,cAAc,CAAyB;;IAM/C,wEAAwE;IACxE,KAAK,IAAI,QAAQ,CAAC,SAAS;IAI3B;;;;;;OAMG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAKrC,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IAoB9B,QAAQ,IAAI,QAAQ;IAIpB,sDAAsD;IACtD,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAe7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACG,kBAAkB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAgD1E,OAAO,CAAC,OAAO;YAYD,0BAA0B;IAmIxC,qEAAqE;IACrE,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,kBAAkB;IAkB1B,OAAO,CAAC,WAAW;IAYnB;;;0DAGsD;IACtD,YAAY,IAAI,IAAI;CAUrB"}
|
package/dist/ui_state.js
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session-UI state machine for the xiaotime dev CLI.
|
|
4
|
+
*
|
|
5
|
+
* SPRINT-CLI-STATE-MACHINE-1 (#6163 / closes #6127) shipped the original
|
|
6
|
+
* state machine + raw-mode approval prompt. That fix eliminated the
|
|
7
|
+
* defaults-to-N half of the "yy" double-echo bug but the visible-yy half
|
|
8
|
+
* persisted because `readline.createInterface({ terminal: true })` attaches
|
|
9
|
+
* its own keypress handler to stdin that echoes characters via its own
|
|
10
|
+
* stdout writes; `rl.pause()` stops `line` events but does NOT detach that
|
|
11
|
+
* echo path, and `setRawMode(true)` only suppresses kernel-level echo, not
|
|
12
|
+
* readline's.
|
|
13
|
+
*
|
|
14
|
+
* SPRINT-CLI-APPROVAL-MENU-1 (#6509) replaces `promptApproval`'s `[y/N]`
|
|
15
|
+
* one-shot prompt with `promptApprovalMenu` — a three-option numbered menu
|
|
16
|
+
* (Run / Run + allowlist / Reject + feedback). The rl is explicitly
|
|
17
|
+
* **closed and recreated** around the menu render so its keypress echo
|
|
18
|
+
* physically can't fire (the rl object is gone for the duration of the
|
|
19
|
+
* menu). Persistent close listeners survive the recreation via
|
|
20
|
+
* `onRlClose()` — callers that need a stable close hook register through
|
|
21
|
+
* that instead of `getRl().on("close", ...)`.
|
|
22
|
+
*
|
|
23
|
+
* The state machine has four states:
|
|
24
|
+
*
|
|
25
|
+
* IDLE — at the `you:` prompt, awaiting user input.
|
|
26
|
+
* GENERATING — model is streaming tokens (or about to). Spinner.
|
|
27
|
+
* RUNNING_TOOL — a server-side tool is running. Spinner.
|
|
28
|
+
* AWAITING_APPROVAL — user must navigate the approval menu for a
|
|
29
|
+
* client-side tool. Main rl is closed; raw stdin
|
|
30
|
+
* captures keystrokes.
|
|
31
|
+
*
|
|
32
|
+
* The WS handler in `ws.ts` is the sole driver. Transitions:
|
|
33
|
+
*
|
|
34
|
+
* IDLE -- user line received -> GENERATING
|
|
35
|
+
* GENERATING -- text_delta -> GENERATING (refresh)
|
|
36
|
+
* GENERATING -- tool_call (server) -> RUNNING_TOOL
|
|
37
|
+
* GENERATING -- tool_call (client+approval) -> AWAITING_APPROVAL
|
|
38
|
+
* AWAITING_APPROVAL -- menu selection -> RUNNING_TOOL
|
|
39
|
+
* RUNNING_TOOL -- tool_result sent -> GENERATING
|
|
40
|
+
* GENERATING -- message_stop -> IDLE
|
|
41
|
+
* ANY -- error -> IDLE
|
|
42
|
+
*/
|
|
43
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
44
|
+
if (k2 === undefined) k2 = k;
|
|
45
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
46
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
47
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
48
|
+
}
|
|
49
|
+
Object.defineProperty(o, k2, desc);
|
|
50
|
+
}) : (function(o, m, k, k2) {
|
|
51
|
+
if (k2 === undefined) k2 = k;
|
|
52
|
+
o[k2] = m[k];
|
|
53
|
+
}));
|
|
54
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
55
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
56
|
+
}) : function(o, v) {
|
|
57
|
+
o["default"] = v;
|
|
58
|
+
});
|
|
59
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
60
|
+
var ownKeys = function(o) {
|
|
61
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
62
|
+
var ar = [];
|
|
63
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
64
|
+
return ar;
|
|
65
|
+
};
|
|
66
|
+
return ownKeys(o);
|
|
67
|
+
};
|
|
68
|
+
return function (mod) {
|
|
69
|
+
if (mod && mod.__esModule) return mod;
|
|
70
|
+
var result = {};
|
|
71
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
72
|
+
__setModuleDefault(result, mod);
|
|
73
|
+
return result;
|
|
74
|
+
};
|
|
75
|
+
})();
|
|
76
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
77
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
78
|
+
};
|
|
79
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
80
|
+
exports.SessionUI = void 0;
|
|
81
|
+
const readline = __importStar(require("readline"));
|
|
82
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
83
|
+
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
84
|
+
const SPINNER_INTERVAL_MS = 100;
|
|
85
|
+
const ELAPSED_THRESHOLD_MS = 3000; // show elapsed time after this
|
|
86
|
+
const MENU_OPTIONS = [
|
|
87
|
+
"Run",
|
|
88
|
+
"Run and don't ask again",
|
|
89
|
+
"Reject and tell Claude why",
|
|
90
|
+
];
|
|
91
|
+
const MENU_HINT = "(1-3, ↑↓ Enter, Esc to reject)";
|
|
92
|
+
class SessionUI {
|
|
93
|
+
state = "idle";
|
|
94
|
+
spinnerTimer = null;
|
|
95
|
+
spinnerFrame = 0;
|
|
96
|
+
spinnerStartedAt = 0;
|
|
97
|
+
spinnerLabel = "";
|
|
98
|
+
// True only while the spinner has written something to the line and
|
|
99
|
+
// we owe a clear. Set on first frame, cleared on stopSpinner. Lets
|
|
100
|
+
// text_delta callers know they need to clear the spinner line
|
|
101
|
+
// before printing the first token.
|
|
102
|
+
spinnerLineDirty = false;
|
|
103
|
+
rl;
|
|
104
|
+
lineListener = null;
|
|
105
|
+
/**
|
|
106
|
+
* Close-listener callbacks that should survive rl recreations. The
|
|
107
|
+
* approval-menu path closes + recreates `rl` to physically detach
|
|
108
|
+
* readline's keypress echo. ws.ts registers its "Session paused"
|
|
109
|
+
* resume hint here so it stays attached across recreations.
|
|
110
|
+
*/
|
|
111
|
+
closeListeners = [];
|
|
112
|
+
constructor() {
|
|
113
|
+
this.rl = this._makeRl();
|
|
114
|
+
}
|
|
115
|
+
/** Hand the underlying readline to callers that need it for cleanup. */
|
|
116
|
+
getRl() {
|
|
117
|
+
return this.rl;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Register a callback to fire when the rl reports a non-suppressed
|
|
121
|
+
* `close` event (the only one users observe is Ctrl+C at the main
|
|
122
|
+
* prompt — close-during-approval is suppressed via removeAllListeners
|
|
123
|
+
* before `rl.close()`). Callbacks are re-attached on every rl
|
|
124
|
+
* recreation so they outlive `promptApprovalMenu`.
|
|
125
|
+
*/
|
|
126
|
+
onRlClose(listener) {
|
|
127
|
+
this.closeListeners.push(listener);
|
|
128
|
+
this.rl.on("close", listener);
|
|
129
|
+
}
|
|
130
|
+
/** Set the explicit current state. Drives spinner start/stop. */
|
|
131
|
+
setState(next) {
|
|
132
|
+
if (next === this.state)
|
|
133
|
+
return;
|
|
134
|
+
const prev = this.state;
|
|
135
|
+
this.state = next;
|
|
136
|
+
// Transition out of any spinner state.
|
|
137
|
+
if (prev === "generating" || prev === "running_tool") {
|
|
138
|
+
this.stopSpinner();
|
|
139
|
+
}
|
|
140
|
+
// Transition in.
|
|
141
|
+
if (next === "generating") {
|
|
142
|
+
this.startSpinner("thinking");
|
|
143
|
+
}
|
|
144
|
+
else if (next === "running_tool") {
|
|
145
|
+
this.startSpinner("running tool");
|
|
146
|
+
}
|
|
147
|
+
// `awaiting_approval` / `idle` => no spinner. Approval-menu path
|
|
148
|
+
// takes over stdin in `promptApprovalMenu`.
|
|
149
|
+
}
|
|
150
|
+
getState() {
|
|
151
|
+
return this.state;
|
|
152
|
+
}
|
|
153
|
+
/** Wait for one line of user input on the main rl. */
|
|
154
|
+
promptLine() {
|
|
155
|
+
return new Promise((resolve) => {
|
|
156
|
+
// Drop any previous one-shot listener that didn't fire.
|
|
157
|
+
if (this.lineListener) {
|
|
158
|
+
this.rl.off("line", this.lineListener);
|
|
159
|
+
}
|
|
160
|
+
const listener = (line) => {
|
|
161
|
+
this.lineListener = null;
|
|
162
|
+
resolve(line.trim());
|
|
163
|
+
};
|
|
164
|
+
this.lineListener = listener;
|
|
165
|
+
this.rl.once("line", listener);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Three-option numbered approval menu. Replaces the pre-#6509
|
|
170
|
+
* `[y/N]` one-shot prompt.
|
|
171
|
+
*
|
|
172
|
+
* Visual:
|
|
173
|
+
* ❯ 1. Run
|
|
174
|
+
* 2. Run and don't ask again
|
|
175
|
+
* 3. Reject and tell Claude why
|
|
176
|
+
* (1-3, ↑↓ Enter, Esc to reject)
|
|
177
|
+
*
|
|
178
|
+
* Controls:
|
|
179
|
+
* - Number key 1/2/3 — one-shot select
|
|
180
|
+
* - Arrow Up/Down — move focus
|
|
181
|
+
* - Enter — select focused
|
|
182
|
+
* - Esc — equivalent to option 3 (reject)
|
|
183
|
+
* - Ctrl+C — reject + propagate SIGINT (cancels session)
|
|
184
|
+
*
|
|
185
|
+
* The rl is closed before the menu render and recreated after. This
|
|
186
|
+
* physically removes readline's keypress echo from stdin for the
|
|
187
|
+
* duration of the menu — the bug fix the #6163 patch attempted via
|
|
188
|
+
* `rl.pause()` (which only stops `line` events, not the keypress
|
|
189
|
+
* echo). Persistent close listeners are stashed in
|
|
190
|
+
* `this.closeListeners` and re-attached on recreate; the close event
|
|
191
|
+
* triggered by THIS close() is suppressed via removeAllListeners
|
|
192
|
+
* before the call so it doesn't fire the "Session paused" hint.
|
|
193
|
+
*
|
|
194
|
+
* For option 3, after the menu resolves the recreated rl prompts
|
|
195
|
+
* once for a free-form rejection reason (line-mode). Empty input is
|
|
196
|
+
* allowed.
|
|
197
|
+
*
|
|
198
|
+
* The returned `allowlistRequested: true` (option 2) is a signal to
|
|
199
|
+
* the caller — PR2 wires it to write the pattern to
|
|
200
|
+
* `~/.xiaotime/settings.json`. PR1 callers may ignore it.
|
|
201
|
+
*/
|
|
202
|
+
async promptApprovalMenu(toolHeader) {
|
|
203
|
+
this.setState("awaiting_approval");
|
|
204
|
+
if (toolHeader) {
|
|
205
|
+
process.stdout.write(toolHeader);
|
|
206
|
+
}
|
|
207
|
+
// Recreate the rl in `finally` so it ALWAYS lands — even if the
|
|
208
|
+
// no-TTY bail-out fires inside `_renderMenuAndGetSelection`. The
|
|
209
|
+
// next `promptLine` call needs a live rl regardless of how the
|
|
210
|
+
// menu resolved.
|
|
211
|
+
let selectedIdx;
|
|
212
|
+
try {
|
|
213
|
+
selectedIdx = await this._renderMenuAndGetSelection();
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
this.rl = this._makeRl();
|
|
217
|
+
}
|
|
218
|
+
// -1 → bail-out from `_renderMenuAndGetSelection` because stdin is
|
|
219
|
+
// not a TTY (CI / piped invocation / test runner). Skip the
|
|
220
|
+
// rejection-reason readline prompt because nothing would feed it.
|
|
221
|
+
if (selectedIdx === -1) {
|
|
222
|
+
return { approved: false, rejectReason: "(no TTY)" };
|
|
223
|
+
}
|
|
224
|
+
if (selectedIdx === 0) {
|
|
225
|
+
process.stdout.write(chalk_1.default.green(" ✓ Approved\n"));
|
|
226
|
+
return { approved: true, allowlistRequested: false };
|
|
227
|
+
}
|
|
228
|
+
if (selectedIdx === 1) {
|
|
229
|
+
process.stdout.write(chalk_1.default.green(" ✓ Approved (allowlist requested)\n"));
|
|
230
|
+
return { approved: true, allowlistRequested: true };
|
|
231
|
+
}
|
|
232
|
+
// selectedIdx === 2 — rejection. Prompt for free-form reason via
|
|
233
|
+
// the recreated rl (line-mode, single line, empty allowed).
|
|
234
|
+
process.stdout.write(chalk_1.default.yellow(" Reason (Enter to send empty): "));
|
|
235
|
+
const reason = await new Promise((resolve) => {
|
|
236
|
+
this.rl.once("line", (line) => resolve(line.trim()));
|
|
237
|
+
});
|
|
238
|
+
return { approved: false, rejectReason: reason };
|
|
239
|
+
}
|
|
240
|
+
// ── Menu internals ───────────────────────────────────────────────
|
|
241
|
+
_makeRl() {
|
|
242
|
+
const rl = readline.createInterface({
|
|
243
|
+
input: process.stdin,
|
|
244
|
+
output: process.stdout,
|
|
245
|
+
terminal: true,
|
|
246
|
+
});
|
|
247
|
+
for (const l of this.closeListeners) {
|
|
248
|
+
rl.on("close", l);
|
|
249
|
+
}
|
|
250
|
+
return rl;
|
|
251
|
+
}
|
|
252
|
+
async _renderMenuAndGetSelection() {
|
|
253
|
+
// Suppress the close-listener side effects for this internal
|
|
254
|
+
// close — we don't want the "Session paused" hint firing
|
|
255
|
+
// mid-approval. Stash the listeners on this.closeListeners so
|
|
256
|
+
// _makeRl can re-attach them after the menu resolves.
|
|
257
|
+
this.rl.removeAllListeners("close");
|
|
258
|
+
this.rl.close();
|
|
259
|
+
const stdin = process.stdin;
|
|
260
|
+
// Bail BEFORE rendering the menu if stdin isn't a TTY. setRawMode
|
|
261
|
+
// can silently no-op on non-TTY stdin in some environments
|
|
262
|
+
// (vitest, piped invocation) — in that case the data listener
|
|
263
|
+
// would attach but never receive bytes, hanging the caller
|
|
264
|
+
// indefinitely. -1 sentinel tells promptApprovalMenu to skip the
|
|
265
|
+
// rejection-reason prompt (which would also hang).
|
|
266
|
+
if (!stdin.isTTY) {
|
|
267
|
+
process.stdout.write(chalk_1.default.red("Rejected (no TTY)\n"));
|
|
268
|
+
return -1;
|
|
269
|
+
}
|
|
270
|
+
const wasRaw = stdin.isRaw === true;
|
|
271
|
+
try {
|
|
272
|
+
stdin.setRawMode(true);
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// Belt + suspenders — isTTY guard above should cover this, but a
|
|
276
|
+
// mid-session TTY loss could surface here.
|
|
277
|
+
process.stdout.write(chalk_1.default.red("Rejected (no TTY)\n"));
|
|
278
|
+
return -1;
|
|
279
|
+
}
|
|
280
|
+
stdin.resume();
|
|
281
|
+
let focusedIdx = 0;
|
|
282
|
+
this._renderMenu(focusedIdx);
|
|
283
|
+
// #6532 CR-r1: function-level finally guarantees onData removal +
|
|
284
|
+
// setRawMode restore even if the Promise body throws or the
|
|
285
|
+
// process is signalled. The previous design relied on every
|
|
286
|
+
// `finish()` call doing the cleanup, leaving leak windows when
|
|
287
|
+
// onData itself threw or when an awaiter rejected without
|
|
288
|
+
// routing through finish().
|
|
289
|
+
let onData = null;
|
|
290
|
+
try {
|
|
291
|
+
return await new Promise((resolve, reject) => {
|
|
292
|
+
const finish = (idx) => {
|
|
293
|
+
this._clearMenu();
|
|
294
|
+
resolve(idx);
|
|
295
|
+
};
|
|
296
|
+
onData = (chunk) => {
|
|
297
|
+
try {
|
|
298
|
+
const s = chunk.toString();
|
|
299
|
+
// Ctrl+C → reject + propagate SIGINT so the WS handler's
|
|
300
|
+
// close path runs and the session ends cleanly.
|
|
301
|
+
if (s.includes("\x03")) {
|
|
302
|
+
finish(2);
|
|
303
|
+
process.kill(process.pid, "SIGINT");
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
// Allowlisted escape-prefixed sequences. Esc alone (no
|
|
307
|
+
// `[`) → option 3 (reject). Arrow up/down rotate focus.
|
|
308
|
+
// Any OTHER `\x1b`-prefixed input is explicitly rejected
|
|
309
|
+
// (logged to stderr at debug level + ignored) per #6532
|
|
310
|
+
// CR-r1: previously such sequences silently no-op'd,
|
|
311
|
+
// which is currently safe due to the no-echo policy but
|
|
312
|
+
// would become a terminal-injection vector if a future
|
|
313
|
+
// diagnostic change started echoing unknown input.
|
|
314
|
+
if (s.charCodeAt(0) === 0x1b) {
|
|
315
|
+
if (s === "\x1b") {
|
|
316
|
+
finish(2);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (s === "\x1b[A") {
|
|
320
|
+
focusedIdx =
|
|
321
|
+
(focusedIdx - 1 + MENU_OPTIONS.length) % MENU_OPTIONS.length;
|
|
322
|
+
this._rerenderMenu(focusedIdx);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (s === "\x1b[B") {
|
|
326
|
+
focusedIdx = (focusedIdx + 1) % MENU_OPTIONS.length;
|
|
327
|
+
this._rerenderMenu(focusedIdx);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
// Unrecognized escape sequence — log + ignore. Use the
|
|
331
|
+
// hex form so a malicious paste of bell/CSI bytes is
|
|
332
|
+
// not re-rendered through stderr.
|
|
333
|
+
const hex = Array.from(chunk)
|
|
334
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
335
|
+
.join(" ");
|
|
336
|
+
process.stderr.write(chalk_1.default.dim(`\n[approval-menu] ignored unrecognized escape: ${hex}\n`));
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
// Number-key one-shot selectors.
|
|
340
|
+
if (s === "1" || s === "2" || s === "3") {
|
|
341
|
+
finish(Number(s) - 1);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
// Enter on focused row.
|
|
345
|
+
if (s === "\r" || s === "\n") {
|
|
346
|
+
finish(focusedIdx);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
// Anything else — ignore. No echo, no bounce. The visible
|
|
350
|
+
// menu stays put so the user can try again.
|
|
351
|
+
}
|
|
352
|
+
catch (err) {
|
|
353
|
+
reject(err);
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
stdin.on("data", onData);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
finally {
|
|
360
|
+
if (onData) {
|
|
361
|
+
stdin.removeListener("data", onData);
|
|
362
|
+
}
|
|
363
|
+
try {
|
|
364
|
+
stdin.setRawMode(wasRaw);
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
/* setRawMode can throw if stdin lost its TTY mid-session. */
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/** Total lines the menu occupies: one per option + one hint line. */
|
|
372
|
+
_menuLineCount() {
|
|
373
|
+
return MENU_OPTIONS.length + 1;
|
|
374
|
+
}
|
|
375
|
+
_renderMenu(focusedIdx) {
|
|
376
|
+
for (let i = 0; i < MENU_OPTIONS.length; i++) {
|
|
377
|
+
const marker = i === focusedIdx ? chalk_1.default.cyan("❯") : " ";
|
|
378
|
+
const numText = chalk_1.default.cyan(`${i + 1}.`);
|
|
379
|
+
process.stdout.write(` ${marker} ${numText} ${MENU_OPTIONS[i]}\n`);
|
|
380
|
+
}
|
|
381
|
+
process.stdout.write(chalk_1.default.dim(` ${MENU_HINT}\n`));
|
|
382
|
+
}
|
|
383
|
+
_rerenderMenu(focusedIdx) {
|
|
384
|
+
// Move cursor up to top of menu, clear each line, redraw.
|
|
385
|
+
const lines = this._menuLineCount();
|
|
386
|
+
process.stdout.write(`\x1b[${lines}A`);
|
|
387
|
+
for (let i = 0; i < MENU_OPTIONS.length; i++) {
|
|
388
|
+
process.stdout.write("\x1b[K");
|
|
389
|
+
const marker = i === focusedIdx ? chalk_1.default.cyan("❯") : " ";
|
|
390
|
+
const numText = chalk_1.default.cyan(`${i + 1}.`);
|
|
391
|
+
process.stdout.write(` ${marker} ${numText} ${MENU_OPTIONS[i]}\n`);
|
|
392
|
+
}
|
|
393
|
+
process.stdout.write("\x1b[K");
|
|
394
|
+
process.stdout.write(chalk_1.default.dim(` ${MENU_HINT}\n`));
|
|
395
|
+
}
|
|
396
|
+
_clearMenu() {
|
|
397
|
+
const lines = this._menuLineCount();
|
|
398
|
+
process.stdout.write(`\x1b[${lines}A`);
|
|
399
|
+
for (let i = 0; i < lines; i++) {
|
|
400
|
+
process.stdout.write("\x1b[K");
|
|
401
|
+
if (i < lines - 1)
|
|
402
|
+
process.stdout.write("\n");
|
|
403
|
+
}
|
|
404
|
+
// Cursor left at start of the (now-cleared) first menu line so
|
|
405
|
+
// the post-selection echo lands there cleanly.
|
|
406
|
+
process.stdout.write(`\x1b[${lines - 1}A\r`);
|
|
407
|
+
}
|
|
408
|
+
// ── Spinner internals ────────────────────────────────────────────
|
|
409
|
+
startSpinner(label) {
|
|
410
|
+
this.spinnerLabel = label;
|
|
411
|
+
this.spinnerFrame = 0;
|
|
412
|
+
this.spinnerStartedAt = Date.now();
|
|
413
|
+
this.spinnerLineDirty = false;
|
|
414
|
+
this.spinnerTimer = setInterval(() => this.renderSpinnerFrame(), SPINNER_INTERVAL_MS);
|
|
415
|
+
// Render the first frame immediately so there's no perceptible
|
|
416
|
+
// delay between state-set and visible feedback.
|
|
417
|
+
this.renderSpinnerFrame();
|
|
418
|
+
}
|
|
419
|
+
renderSpinnerFrame() {
|
|
420
|
+
const frame = SPINNER_FRAMES[this.spinnerFrame % SPINNER_FRAMES.length];
|
|
421
|
+
const elapsed = Date.now() - this.spinnerStartedAt;
|
|
422
|
+
const elapsedStr = elapsed >= ELAPSED_THRESHOLD_MS
|
|
423
|
+
? chalk_1.default.dim(` (${Math.floor(elapsed / 1000)}s)`)
|
|
424
|
+
: "";
|
|
425
|
+
// Clear-to-end-of-line (`\x1b[K`) after the spinner content
|
|
426
|
+
// rather than trailing spaces — robust against terminals where
|
|
427
|
+
// the last frame was longer than the current one (the trailing
|
|
428
|
+
// spaces approach left orphaned chars on shrinking labels).
|
|
429
|
+
process.stdout.write(`\r${chalk_1.default.cyan(frame)} ${chalk_1.default.dim(this.spinnerLabel)}${elapsedStr}\x1b[K`);
|
|
430
|
+
this.spinnerLineDirty = true;
|
|
431
|
+
this.spinnerFrame++;
|
|
432
|
+
}
|
|
433
|
+
stopSpinner() {
|
|
434
|
+
if (this.spinnerTimer) {
|
|
435
|
+
clearInterval(this.spinnerTimer);
|
|
436
|
+
this.spinnerTimer = null;
|
|
437
|
+
}
|
|
438
|
+
if (this.spinnerLineDirty) {
|
|
439
|
+
// Clear the spinner line so the next print doesn't overlap.
|
|
440
|
+
process.stdout.write("\r\x1b[K");
|
|
441
|
+
this.spinnerLineDirty = false;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
/** Called by the text_delta renderer the first time a real model
|
|
445
|
+
* token arrives during a `generating` state. Clears the spinner so
|
|
446
|
+
* the token prints cleanly, but leaves the state at `generating`
|
|
447
|
+
* (token streaming continues without re-spinning). */
|
|
448
|
+
onFirstToken() {
|
|
449
|
+
if (this.spinnerLineDirty) {
|
|
450
|
+
process.stdout.write("\r\x1b[K");
|
|
451
|
+
this.spinnerLineDirty = false;
|
|
452
|
+
}
|
|
453
|
+
if (this.spinnerTimer) {
|
|
454
|
+
clearInterval(this.spinnerTimer);
|
|
455
|
+
this.spinnerTimer = null;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
exports.SessionUI = SessionUI;
|
|
460
|
+
//# sourceMappingURL=ui_state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui_state.js","sourceRoot":"","sources":["../src/ui_state.ts"],"names":[],"mappings":"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"}
|
package/dist/ws.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WebSocket connection and main message loop.
|
|
3
|
+
*
|
|
4
|
+
* SPRINT-CLI-STATE-MACHINE-1 (#6163 / closes #6127) — drives the
|
|
5
|
+
* SessionUI state machine from incoming WS events so the user gets
|
|
6
|
+
* a spinner while the model is generating / a tool is running, and
|
|
7
|
+
* approval prompts don't race the main rl on keystrokes.
|
|
3
8
|
*/
|
|
4
9
|
export declare function runSession(sessionId: string): Promise<void>;
|
|
5
10
|
//# sourceMappingURL=ws.d.ts.map
|
package/dist/ws.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAgBH,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgMjE"}
|