@astralyn/pi 0.84.6 → 0.84.8
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 +27 -0
- package/dist/core/compaction/compaction.d.ts +2 -0
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +7 -7
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/extensions/background/index.d.ts.map +1 -1
- package/dist/extensions/background/index.js +48 -72
- package/dist/extensions/background/index.js.map +1 -1
- package/dist/extensions/background/manager.d.ts.map +1 -1
- package/dist/extensions/background/manager.js +5 -3
- package/dist/extensions/background/manager.js.map +1 -1
- package/dist/extensions/background/registry.d.ts +11 -13
- package/dist/extensions/background/registry.d.ts.map +1 -1
- package/dist/extensions/background/registry.js +71 -80
- package/dist/extensions/background/registry.js.map +1 -1
- package/dist/extensions/background/render.d.ts.map +1 -1
- package/dist/extensions/background/render.js +17 -4
- package/dist/extensions/background/render.js.map +1 -1
- package/dist/extensions/deepwiki/constants.d.ts +2 -2
- package/dist/extensions/deepwiki/constants.d.ts.map +1 -1
- package/dist/extensions/deepwiki/constants.js +5 -5
- package/dist/extensions/deepwiki/constants.js.map +1 -1
- package/dist/extensions/deepwiki/schema.js +1 -1
- package/dist/extensions/deepwiki/schema.js.map +1 -1
- package/dist/extensions/question/constants.d.ts +2 -2
- package/dist/extensions/question/constants.d.ts.map +1 -1
- package/dist/extensions/question/constants.js +5 -2
- package/dist/extensions/question/constants.js.map +1 -1
- package/dist/extensions/router/dialog-host.d.ts +60 -0
- package/dist/extensions/router/dialog-host.d.ts.map +1 -0
- package/dist/extensions/router/dialog-host.js +196 -0
- package/dist/extensions/router/dialog-host.js.map +1 -0
- package/dist/extensions/router/probe.d.ts.map +1 -1
- package/dist/extensions/router/probe.js +2 -0
- package/dist/extensions/router/probe.js.map +1 -1
- package/dist/extensions/router/ui.d.ts.map +1 -1
- package/dist/extensions/router/ui.js +117 -67
- package/dist/extensions/router/ui.js.map +1 -1
- package/dist/extensions/subagent/activity.d.ts +9 -0
- package/dist/extensions/subagent/activity.d.ts.map +1 -1
- package/dist/extensions/subagent/activity.js +21 -0
- package/dist/extensions/subagent/activity.js.map +1 -1
- package/dist/extensions/subagent/agents-command.d.ts.map +1 -1
- package/dist/extensions/subagent/agents-command.js +355 -71
- package/dist/extensions/subagent/agents-command.js.map +1 -1
- package/dist/extensions/subagent/agents.d.ts.map +1 -1
- package/dist/extensions/subagent/agents.js +7 -3
- package/dist/extensions/subagent/agents.js.map +1 -1
- package/dist/extensions/subagent/constants.d.ts +1 -0
- package/dist/extensions/subagent/constants.d.ts.map +1 -1
- package/dist/extensions/subagent/constants.js +4 -0
- package/dist/extensions/subagent/constants.js.map +1 -1
- package/dist/extensions/subagent/index.js +2 -2
- package/dist/extensions/subagent/index.js.map +1 -1
- package/dist/extensions/subagent/render.d.ts.map +1 -1
- package/dist/extensions/subagent/render.js +1 -7
- package/dist/extensions/subagent/render.js.map +1 -1
- package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
- package/dist/extensions/subagent/sdk-runner.js +19 -0
- package/dist/extensions/subagent/sdk-runner.js.map +1 -1
- package/dist/extensions/subagent/settings.d.ts.map +1 -1
- package/dist/extensions/subagent/settings.js +11 -4
- package/dist/extensions/subagent/settings.js.map +1 -1
- package/dist/extensions/subagent/state.d.ts +9 -0
- package/dist/extensions/subagent/state.d.ts.map +1 -1
- package/dist/extensions/subagent/state.js +33 -2
- package/dist/extensions/subagent/state.js.map +1 -1
- package/dist/extensions/subagent/ui/model-list.d.ts +1 -0
- package/dist/extensions/subagent/ui/model-list.d.ts.map +1 -1
- package/dist/extensions/subagent/ui/model-list.js +5 -0
- package/dist/extensions/subagent/ui/model-list.js.map +1 -1
- package/dist/extensions/todo/constants.d.ts +1 -1
- package/dist/extensions/todo/constants.d.ts.map +1 -1
- package/dist/extensions/todo/constants.js +10 -27
- package/dist/extensions/todo/constants.js.map +1 -1
- package/dist/extensions/todo/state.d.ts.map +1 -1
- package/dist/extensions/todo/state.js +27 -16
- package/dist/extensions/todo/state.js.map +1 -1
- package/dist/extensions/todo/view.d.ts.map +1 -1
- package/dist/extensions/todo/view.js +16 -5
- package/dist/extensions/todo/view.js.map +1 -1
- package/docs/bundled/extensions/background.md +3 -3
- package/docs/bundled/extensions/todo.md +2 -2
- package/docs/compaction.md +2 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/** Extension-owned dialog host that keeps every /router TUI page in one modal lifecycle. */
|
|
2
|
+
import { Container, isFocusable, Spacer, Text } from "@earendil-works/pi-tui";
|
|
3
|
+
import { DynamicBorder } from "../../modes/interactive/components/dynamic-border.js";
|
|
4
|
+
import { ExtensionInputComponent } from "../../modes/interactive/components/extension-input.js";
|
|
5
|
+
import { ExtensionSelectorComponent } from "../../modes/interactive/components/extension-selector.js";
|
|
6
|
+
import { keyLabel } from "../../modes/interactive/components/keybinding-hints.js";
|
|
7
|
+
export class RouterTuiSessionClosedError extends Error {
|
|
8
|
+
constructor() {
|
|
9
|
+
super("Router TUI session is closed.");
|
|
10
|
+
this.name = "RouterTuiSessionClosedError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function createNativeRouterDialogs(ctx) {
|
|
14
|
+
return {
|
|
15
|
+
kind: "native",
|
|
16
|
+
select: (title, options) => ctx.ui.select(title, options),
|
|
17
|
+
input: (title, placeholder) => ctx.ui.input(title, placeholder),
|
|
18
|
+
confirm: (title, message) => ctx.ui.confirm(title, message),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Persistent root mounted by one ctx.ui.custom() call. Child dialogs resolve
|
|
23
|
+
* without closing this root, so async flow continuations can replace them
|
|
24
|
+
* without exposing the main editor between pages.
|
|
25
|
+
*/
|
|
26
|
+
export class RouterTuiSession extends Container {
|
|
27
|
+
constructor(tui, theme, keybindings, onTransitionClose) {
|
|
28
|
+
super();
|
|
29
|
+
this.kind = "tui";
|
|
30
|
+
this.acceptsInput = false;
|
|
31
|
+
this.closeRequested = false;
|
|
32
|
+
this.disposed = false;
|
|
33
|
+
this._focused = false;
|
|
34
|
+
this.tui = tui;
|
|
35
|
+
this.theme = theme;
|
|
36
|
+
this.keybindings = keybindings;
|
|
37
|
+
this.onTransitionClose = onTransitionClose;
|
|
38
|
+
const loading = new Container();
|
|
39
|
+
loading.addChild(new DynamicBorder((text) => theme.fg("border", text)));
|
|
40
|
+
loading.addChild(new Spacer(1));
|
|
41
|
+
loading.addChild(new Text(theme.fg("muted", "Loading router…"), 1, 0));
|
|
42
|
+
loading.addChild(new Spacer(1));
|
|
43
|
+
loading.addChild(new DynamicBorder((text) => theme.fg("border", text)));
|
|
44
|
+
this.activeScreen = loading;
|
|
45
|
+
this.addChild(loading);
|
|
46
|
+
}
|
|
47
|
+
get focused() {
|
|
48
|
+
return this._focused;
|
|
49
|
+
}
|
|
50
|
+
set focused(value) {
|
|
51
|
+
this._focused = value;
|
|
52
|
+
if (this.activeScreen && isFocusable(this.activeScreen)) {
|
|
53
|
+
this.activeScreen.focused = value && this.acceptsInput;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
show(factory) {
|
|
57
|
+
this.clearTransitionTimer();
|
|
58
|
+
if (this.disposed || this.closeRequested)
|
|
59
|
+
return Promise.reject(new RouterTuiSessionClosedError());
|
|
60
|
+
if (this.pendingReject)
|
|
61
|
+
return Promise.reject(new Error("Router TUI session already has an active dialog."));
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
let settled = false;
|
|
64
|
+
const fail = (error) => {
|
|
65
|
+
if (settled || this.disposed)
|
|
66
|
+
return;
|
|
67
|
+
settled = true;
|
|
68
|
+
this.acceptsInput = false;
|
|
69
|
+
if (this.pendingReject === reject)
|
|
70
|
+
this.pendingReject = undefined;
|
|
71
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
72
|
+
};
|
|
73
|
+
const done = (result) => {
|
|
74
|
+
if (settled || this.disposed)
|
|
75
|
+
return;
|
|
76
|
+
settled = true;
|
|
77
|
+
this.acceptsInput = false;
|
|
78
|
+
if (this.activeScreen && isFocusable(this.activeScreen))
|
|
79
|
+
this.activeScreen.focused = false;
|
|
80
|
+
if (this.pendingReject === reject)
|
|
81
|
+
this.pendingReject = undefined;
|
|
82
|
+
this.tui.setFocus(this);
|
|
83
|
+
this.beginTransition();
|
|
84
|
+
resolve(result);
|
|
85
|
+
};
|
|
86
|
+
this.pendingReject = reject;
|
|
87
|
+
let pending;
|
|
88
|
+
try {
|
|
89
|
+
pending = factory(this.tui, this.theme, this.keybindings, done);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
fail(error);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
void Promise.resolve(pending).then((component) => {
|
|
96
|
+
if (settled || this.disposed) {
|
|
97
|
+
this.disposeScreen(component);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
this.setActiveScreen(component);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
this.disposeScreen(component);
|
|
105
|
+
fail(error);
|
|
106
|
+
}
|
|
107
|
+
}, fail);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
input(title, placeholder) {
|
|
111
|
+
return this.show((tui, _theme, _keybindings, done) => new ExtensionInputComponent(title, placeholder, done, () => done(undefined), { tui }));
|
|
112
|
+
}
|
|
113
|
+
async confirm(title, message) {
|
|
114
|
+
const selected = await this.show((tui, _theme, _keybindings, done) => new ExtensionSelectorComponent(title, ["Yes", "No"], done, () => done(undefined), {
|
|
115
|
+
tui,
|
|
116
|
+
subtitle: message,
|
|
117
|
+
}));
|
|
118
|
+
return selected === "Yes";
|
|
119
|
+
}
|
|
120
|
+
handleInput(data) {
|
|
121
|
+
if (this.acceptsInput) {
|
|
122
|
+
this.activeScreen?.handleInput?.(data);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (this.closeRequested || !this.keybindings.matches(data, "tui.select.cancel"))
|
|
126
|
+
return;
|
|
127
|
+
this.closeRequested = true;
|
|
128
|
+
this.onTransitionClose();
|
|
129
|
+
}
|
|
130
|
+
dispose() {
|
|
131
|
+
if (this.disposed)
|
|
132
|
+
return;
|
|
133
|
+
this.disposed = true;
|
|
134
|
+
this.acceptsInput = false;
|
|
135
|
+
this.clearTransitionTimer();
|
|
136
|
+
const reject = this.pendingReject;
|
|
137
|
+
this.pendingReject = undefined;
|
|
138
|
+
this.disposeActiveScreen();
|
|
139
|
+
this.clear();
|
|
140
|
+
reject?.(new RouterTuiSessionClosedError());
|
|
141
|
+
}
|
|
142
|
+
beginTransition() {
|
|
143
|
+
this.clearTransitionTimer();
|
|
144
|
+
this.transitionTimer = setTimeout(() => {
|
|
145
|
+
this.transitionTimer = undefined;
|
|
146
|
+
if (this.disposed || this.closeRequested || this.acceptsInput)
|
|
147
|
+
return;
|
|
148
|
+
const working = new Container();
|
|
149
|
+
working.addChild(new DynamicBorder((text) => this.theme.fg("border", text)));
|
|
150
|
+
working.addChild(new Spacer(1));
|
|
151
|
+
const cancelKey = keyLabel("tui.select.cancel", { keybindings: this.keybindings });
|
|
152
|
+
const closeHint = cancelKey ? `${cancelKey} closes /router` : "Close /router to continue";
|
|
153
|
+
working.addChild(new Text(this.theme.fg("muted", `Working… ${closeHint}`), 1, 0));
|
|
154
|
+
working.addChild(new Spacer(1));
|
|
155
|
+
working.addChild(new DynamicBorder((text) => this.theme.fg("border", text)));
|
|
156
|
+
this.disposeActiveScreen();
|
|
157
|
+
this.clear();
|
|
158
|
+
this.activeScreen = working;
|
|
159
|
+
this.addChild(working);
|
|
160
|
+
this.tui.requestRender();
|
|
161
|
+
}, 150);
|
|
162
|
+
}
|
|
163
|
+
clearTransitionTimer() {
|
|
164
|
+
if (this.transitionTimer === undefined)
|
|
165
|
+
return;
|
|
166
|
+
clearTimeout(this.transitionTimer);
|
|
167
|
+
this.transitionTimer = undefined;
|
|
168
|
+
}
|
|
169
|
+
setActiveScreen(component) {
|
|
170
|
+
this.disposeActiveScreen();
|
|
171
|
+
this.clear();
|
|
172
|
+
this.activeScreen = component;
|
|
173
|
+
this.addChild(component);
|
|
174
|
+
this.acceptsInput = true;
|
|
175
|
+
this.tui.setFocus(component);
|
|
176
|
+
this.tui.requestRender();
|
|
177
|
+
}
|
|
178
|
+
disposeActiveScreen() {
|
|
179
|
+
const screen = this.activeScreen;
|
|
180
|
+
this.activeScreen = undefined;
|
|
181
|
+
if (screen)
|
|
182
|
+
this.disposeScreen(screen);
|
|
183
|
+
}
|
|
184
|
+
disposeScreen(screen) {
|
|
185
|
+
const dispose = screen.dispose;
|
|
186
|
+
if (isFocusable(screen))
|
|
187
|
+
screen.focused = false;
|
|
188
|
+
try {
|
|
189
|
+
dispose?.call(screen);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// A child cleanup failure must not strand the outer custom lifecycle.
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=dialog-host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialog-host.js","sourceRoot":"","sources":["../../../src/extensions/router/dialog-host.ts"],"names":[],"mappings":"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","sourcesContent":["/** Extension-owned dialog host that keeps every /router TUI page in one modal lifecycle. */\n\nimport { type Component, Container, type Focusable, isFocusable, Spacer, Text, type TUI } from \"@earendil-works/pi-tui\";\nimport type { ExtensionCommandContext } from \"../../core/extensions/types.ts\";\nimport type { KeybindingsManager } from \"../../core/keybindings.ts\";\nimport { DynamicBorder } from \"../../modes/interactive/components/dynamic-border.ts\";\nimport { ExtensionInputComponent } from \"../../modes/interactive/components/extension-input.ts\";\nimport { ExtensionSelectorComponent } from \"../../modes/interactive/components/extension-selector.ts\";\nimport { keyLabel } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\n\nexport type RouterComponent = Component & { dispose?(): void };\n\nexport type RouterComponentFactory<T> = (\n\ttui: TUI,\n\ttheme: Theme,\n\tkeybindings: KeybindingsManager,\n\tdone: (result: T) => void,\n) => RouterComponent | Promise<RouterComponent>;\n\ninterface RouterDialogBase {\n\tinput(title: string, placeholder?: string): Promise<string | undefined>;\n\tconfirm(title: string, message: string): Promise<boolean>;\n}\n\nexport interface RouterNativeDialogs extends RouterDialogBase {\n\treadonly kind: \"native\";\n\tselect(title: string, options: string[]): Promise<string | undefined>;\n}\n\nexport interface RouterTuiDialogs extends RouterDialogBase {\n\treadonly kind: \"tui\";\n\tshow<T>(factory: RouterComponentFactory<T>): Promise<T>;\n}\n\nexport type RouterDialogs = RouterNativeDialogs | RouterTuiDialogs;\n\nexport class RouterTuiSessionClosedError extends Error {\n\tconstructor() {\n\t\tsuper(\"Router TUI session is closed.\");\n\t\tthis.name = \"RouterTuiSessionClosedError\";\n\t}\n}\n\nexport function createNativeRouterDialogs(ctx: ExtensionCommandContext): RouterNativeDialogs {\n\treturn {\n\t\tkind: \"native\",\n\t\tselect: (title, options) => ctx.ui.select(title, options),\n\t\tinput: (title, placeholder) => ctx.ui.input(title, placeholder),\n\t\tconfirm: (title, message) => ctx.ui.confirm(title, message),\n\t};\n}\n\n/**\n * Persistent root mounted by one ctx.ui.custom() call. Child dialogs resolve\n * without closing this root, so async flow continuations can replace them\n * without exposing the main editor between pages.\n */\nexport class RouterTuiSession extends Container implements Focusable, RouterTuiDialogs {\n\treadonly kind = \"tui\" as const;\n\n\tprivate readonly tui: TUI;\n\tprivate readonly theme: Theme;\n\tprivate readonly keybindings: KeybindingsManager;\n\tprivate readonly onTransitionClose: () => void;\n\tprivate activeScreen: RouterComponent | undefined;\n\tprivate pendingReject: ((error: Error) => void) | undefined;\n\tprivate transitionTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate acceptsInput = false;\n\tprivate closeRequested = false;\n\tprivate disposed = false;\n\tprivate _focused = false;\n\n\tconstructor(tui: TUI, theme: Theme, keybindings: KeybindingsManager, onTransitionClose: () => void) {\n\t\tsuper();\n\t\tthis.tui = tui;\n\t\tthis.theme = theme;\n\t\tthis.keybindings = keybindings;\n\t\tthis.onTransitionClose = onTransitionClose;\n\n\t\tconst loading = new Container();\n\t\tloading.addChild(new DynamicBorder((text) => theme.fg(\"border\", text)));\n\t\tloading.addChild(new Spacer(1));\n\t\tloading.addChild(new Text(theme.fg(\"muted\", \"Loading router…\"), 1, 0));\n\t\tloading.addChild(new Spacer(1));\n\t\tloading.addChild(new DynamicBorder((text) => theme.fg(\"border\", text)));\n\t\tthis.activeScreen = loading;\n\t\tthis.addChild(loading);\n\t}\n\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tif (this.activeScreen && isFocusable(this.activeScreen)) {\n\t\t\tthis.activeScreen.focused = value && this.acceptsInput;\n\t\t}\n\t}\n\n\tshow<T>(factory: RouterComponentFactory<T>): Promise<T> {\n\t\tthis.clearTransitionTimer();\n\t\tif (this.disposed || this.closeRequested) return Promise.reject(new RouterTuiSessionClosedError());\n\t\tif (this.pendingReject) return Promise.reject(new Error(\"Router TUI session already has an active dialog.\"));\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tlet settled = false;\n\t\t\tconst fail = (error: unknown) => {\n\t\t\t\tif (settled || this.disposed) return;\n\t\t\t\tsettled = true;\n\t\t\t\tthis.acceptsInput = false;\n\t\t\t\tif (this.pendingReject === reject) this.pendingReject = undefined;\n\t\t\t\treject(error instanceof Error ? error : new Error(String(error)));\n\t\t\t};\n\t\t\tconst done = (result: T) => {\n\t\t\t\tif (settled || this.disposed) return;\n\t\t\t\tsettled = true;\n\t\t\t\tthis.acceptsInput = false;\n\t\t\t\tif (this.activeScreen && isFocusable(this.activeScreen)) this.activeScreen.focused = false;\n\t\t\t\tif (this.pendingReject === reject) this.pendingReject = undefined;\n\t\t\t\tthis.tui.setFocus(this);\n\t\t\t\tthis.beginTransition();\n\t\t\t\tresolve(result);\n\t\t\t};\n\t\t\tthis.pendingReject = reject;\n\t\t\tlet pending: RouterComponent | Promise<RouterComponent>;\n\t\t\ttry {\n\t\t\t\tpending = factory(this.tui, this.theme, this.keybindings, done);\n\t\t\t} catch (error) {\n\t\t\t\tfail(error);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvoid Promise.resolve(pending).then((component) => {\n\t\t\t\tif (settled || this.disposed) {\n\t\t\t\t\tthis.disposeScreen(component);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tthis.setActiveScreen(component);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.disposeScreen(component);\n\t\t\t\t\tfail(error);\n\t\t\t\t}\n\t\t\t}, fail);\n\t\t});\n\t}\n\n\tinput(title: string, placeholder?: string): Promise<string | undefined> {\n\t\treturn this.show<string | undefined>(\n\t\t\t(tui, _theme, _keybindings, done) =>\n\t\t\t\tnew ExtensionInputComponent(title, placeholder, done, () => done(undefined), { tui }),\n\t\t);\n\t}\n\n\tasync confirm(title: string, message: string): Promise<boolean> {\n\t\tconst selected = await this.show<string | undefined>(\n\t\t\t(tui, _theme, _keybindings, done) =>\n\t\t\t\tnew ExtensionSelectorComponent(title, [\"Yes\", \"No\"], done, () => done(undefined), {\n\t\t\t\t\ttui,\n\t\t\t\t\tsubtitle: message,\n\t\t\t\t}),\n\t\t);\n\t\treturn selected === \"Yes\";\n\t}\n\n\thandleInput(data: string): void {\n\t\tif (this.acceptsInput) {\n\t\t\tthis.activeScreen?.handleInput?.(data);\n\t\t\treturn;\n\t\t}\n\t\tif (this.closeRequested || !this.keybindings.matches(data, \"tui.select.cancel\")) return;\n\t\tthis.closeRequested = true;\n\t\tthis.onTransitionClose();\n\t}\n\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tthis.acceptsInput = false;\n\t\tthis.clearTransitionTimer();\n\t\tconst reject = this.pendingReject;\n\t\tthis.pendingReject = undefined;\n\t\tthis.disposeActiveScreen();\n\t\tthis.clear();\n\t\treject?.(new RouterTuiSessionClosedError());\n\t}\n\n\tprivate beginTransition(): void {\n\t\tthis.clearTransitionTimer();\n\t\tthis.transitionTimer = setTimeout(() => {\n\t\t\tthis.transitionTimer = undefined;\n\t\t\tif (this.disposed || this.closeRequested || this.acceptsInput) return;\n\t\t\tconst working = new Container();\n\t\t\tworking.addChild(new DynamicBorder((text) => this.theme.fg(\"border\", text)));\n\t\t\tworking.addChild(new Spacer(1));\n\t\t\tconst cancelKey = keyLabel(\"tui.select.cancel\", { keybindings: this.keybindings });\n\t\t\tconst closeHint = cancelKey ? `${cancelKey} closes /router` : \"Close /router to continue\";\n\t\t\tworking.addChild(new Text(this.theme.fg(\"muted\", `Working… ${closeHint}`), 1, 0));\n\t\t\tworking.addChild(new Spacer(1));\n\t\t\tworking.addChild(new DynamicBorder((text) => this.theme.fg(\"border\", text)));\n\t\t\tthis.disposeActiveScreen();\n\t\t\tthis.clear();\n\t\t\tthis.activeScreen = working;\n\t\t\tthis.addChild(working);\n\t\t\tthis.tui.requestRender();\n\t\t}, 150);\n\t}\n\n\tprivate clearTransitionTimer(): void {\n\t\tif (this.transitionTimer === undefined) return;\n\t\tclearTimeout(this.transitionTimer);\n\t\tthis.transitionTimer = undefined;\n\t}\n\n\tprivate setActiveScreen(component: RouterComponent): void {\n\t\tthis.disposeActiveScreen();\n\t\tthis.clear();\n\t\tthis.activeScreen = component;\n\t\tthis.addChild(component);\n\t\tthis.acceptsInput = true;\n\t\tthis.tui.setFocus(component);\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate disposeActiveScreen(): void {\n\t\tconst screen = this.activeScreen;\n\t\tthis.activeScreen = undefined;\n\t\tif (screen) this.disposeScreen(screen);\n\t}\n\n\tprivate disposeScreen(screen: RouterComponent): void {\n\t\tconst dispose = screen.dispose;\n\t\tif (isFocusable(screen)) screen.focused = false;\n\t\ttry {\n\t\t\tdispose?.call(screen);\n\t\t} catch {\n\t\t\t// A child cleanup failure must not strand the outer custom lifecycle.\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../../src/extensions/router/probe.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAIlE,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,UAAU,EAAE,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../../src/extensions/router/probe.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAIlE,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,UAAU,EAAE,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,WAAW,CAAC,CA0DvB"}
|
|
@@ -11,6 +11,8 @@ export async function probeRelayModels(opts) {
|
|
|
11
11
|
if (baseUrl.protocol !== "http:" && baseUrl.protocol !== "https:") {
|
|
12
12
|
return { ok: false, error: `Unsupported protocol: ${baseUrl.protocol}` };
|
|
13
13
|
}
|
|
14
|
+
if (opts.signal?.aborted)
|
|
15
|
+
return { ok: false, error: "Cancelled." };
|
|
14
16
|
const controller = new AbortController();
|
|
15
17
|
const timeoutMs = opts.timeoutMs ?? DEFAULTS.probeTimeoutMs;
|
|
16
18
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"probe.js","sourceRoot":"","sources":["../../../src/extensions/router/probe.ts"],"names":[],"mappings":"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","sourcesContent":["/** Fetch OpenAI-compatible model catalog from a relay baseUrl. */\n\nimport { DEFAULTS, formatError } from \"./constants.ts\";\n\nexport interface ProbeModel {\n\tid: string;\n\tname?: string;\n}\n\nexport type ProbeResult = { ok: true; models: ProbeModel[]; truncated: boolean } | { ok: false; error: string };\n\nexport async function probeRelayModels(opts: {\n\tbaseUrl: string;\n\tapiKey?: string;\n\tsignal?: AbortSignal;\n\ttimeoutMs?: number;\n}): Promise<ProbeResult> {\n\tlet baseUrl: URL;\n\ttry {\n\t\tbaseUrl = new URL(opts.baseUrl);\n\t} catch {\n\t\treturn { ok: false, error: \"Base URL is not a valid URL.\" };\n\t}\n\tif (baseUrl.protocol !== \"http:\" && baseUrl.protocol !== \"https:\") {\n\t\treturn { ok: false, error: `Unsupported protocol: ${baseUrl.protocol}` };\n\t}\n\n\tconst controller = new AbortController();\n\tconst timeoutMs = opts.timeoutMs ?? DEFAULTS.probeTimeoutMs;\n\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\tconst onOuterAbort = () => controller.abort();\n\topts.signal?.addEventListener(\"abort\", onOuterAbort, { once: true });\n\n\ttry {\n\t\tconst url = appendPath(baseUrl, \"models\");\n\t\tconst headers: Record<string, string> = { Accept: \"application/json\" };\n\t\tif (opts.apiKey) headers.Authorization = `Bearer ${opts.apiKey}`;\n\n\t\tconst response = await fetch(url, { headers, signal: controller.signal });\n\t\tif (!response.ok) {\n\t\t\tconst body = await readTextBounded(response, 4_096);\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `HTTP ${response.status}${body ? `: ${body.slice(0, 400)}` : \"\"}`,\n\t\t\t};\n\t\t}\n\t\tconst text = await readTextBounded(response, DEFAULTS.probeBodyBytes);\n\t\tlet json: unknown;\n\t\ttry {\n\t\t\tjson = JSON.parse(text);\n\t\t} catch {\n\t\t\treturn { ok: false, error: \"Model catalog response is not JSON.\" };\n\t\t}\n\t\tconst models = parseOpenAIModels(json);\n\t\tif (models === null) {\n\t\t\treturn { ok: false, error: \"JSON has no OpenAI-style `data` array of model ids.\" };\n\t\t}\n\t\tconst sorted = dedupeSort(models);\n\t\tconst truncated = sorted.length > DEFAULTS.probeMaxModels;\n\t\treturn {\n\t\t\tok: true,\n\t\t\tmodels: sorted.slice(0, DEFAULTS.probeMaxModels),\n\t\t\ttruncated,\n\t\t};\n\t} catch (error) {\n\t\tif (controller.signal.aborted) {\n\t\t\treturn { ok: false, error: opts.signal?.aborted ? \"Cancelled.\" : `Timed out after ${timeoutMs}ms.` };\n\t\t}\n\t\treturn { ok: false, error: formatError(error) };\n\t} finally {\n\t\tclearTimeout(timer);\n\t\topts.signal?.removeEventListener(\"abort\", onOuterAbort);\n\t}\n}\n\nfunction appendPath(base: URL, segment: string): URL {\n\tconst url = new URL(base.href);\n\tconst path = url.pathname.replace(/\\/+$/, \"\");\n\turl.pathname = `${path}/${segment}`;\n\treturn url;\n}\n\nfunction parseOpenAIModels(json: unknown): ProbeModel[] | null {\n\tif (!json || typeof json !== \"object\") return null;\n\tconst data = (json as { data?: unknown }).data;\n\tif (!Array.isArray(data)) return null;\n\tconst models: ProbeModel[] = [];\n\tfor (const item of data) {\n\t\tif (!item || typeof item !== \"object\") continue;\n\t\tconst record = item as Record<string, unknown>;\n\t\tconst id = typeof record.id === \"string\" ? record.id.trim() : \"\";\n\t\tif (!id) continue;\n\t\tconst name =\n\t\t\ttypeof record.name === \"string\" && record.name.trim() && record.name.trim() !== id\n\t\t\t\t? record.name.trim()\n\t\t\t\t: undefined;\n\t\tmodels.push(name ? { id, name } : { id });\n\t}\n\treturn models;\n}\n\nfunction dedupeSort(models: ProbeModel[]): ProbeModel[] {\n\tconst map = new Map<string, ProbeModel>();\n\tfor (const model of models) {\n\t\tif (!map.has(model.id)) map.set(model.id, model);\n\t}\n\treturn [...map.values()].sort((a, b) => a.id.localeCompare(b.id));\n}\n\nasync function readTextBounded(response: Response, maxBytes: number): Promise<string> {\n\tconst reader = response.body?.getReader();\n\tif (!reader) return await response.text();\n\tconst chunks: Uint8Array[] = [];\n\tlet total = 0;\n\twhile (true) {\n\t\tconst { done, value } = await reader.read();\n\t\tif (done) break;\n\t\tif (!value) continue;\n\t\ttotal += value.byteLength;\n\t\tif (total > maxBytes) {\n\t\t\tawait reader.cancel();\n\t\t\tthrow new Error(`Response exceeds ${maxBytes} bytes.`);\n\t\t}\n\t\tchunks.push(value);\n\t}\n\tconst merged = new Uint8Array(total);\n\tlet offset = 0;\n\tfor (const chunk of chunks) {\n\t\tmerged.set(chunk, offset);\n\t\toffset += chunk.byteLength;\n\t}\n\treturn new TextDecoder().decode(merged);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"probe.js","sourceRoot":"","sources":["../../../src/extensions/router/probe.ts"],"names":[],"mappings":"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","sourcesContent":["/** Fetch OpenAI-compatible model catalog from a relay baseUrl. */\n\nimport { DEFAULTS, formatError } from \"./constants.ts\";\n\nexport interface ProbeModel {\n\tid: string;\n\tname?: string;\n}\n\nexport type ProbeResult = { ok: true; models: ProbeModel[]; truncated: boolean } | { ok: false; error: string };\n\nexport async function probeRelayModels(opts: {\n\tbaseUrl: string;\n\tapiKey?: string;\n\tsignal?: AbortSignal;\n\ttimeoutMs?: number;\n}): Promise<ProbeResult> {\n\tlet baseUrl: URL;\n\ttry {\n\t\tbaseUrl = new URL(opts.baseUrl);\n\t} catch {\n\t\treturn { ok: false, error: \"Base URL is not a valid URL.\" };\n\t}\n\tif (baseUrl.protocol !== \"http:\" && baseUrl.protocol !== \"https:\") {\n\t\treturn { ok: false, error: `Unsupported protocol: ${baseUrl.protocol}` };\n\t}\n\n\tif (opts.signal?.aborted) return { ok: false, error: \"Cancelled.\" };\n\tconst controller = new AbortController();\n\tconst timeoutMs = opts.timeoutMs ?? DEFAULTS.probeTimeoutMs;\n\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\tconst onOuterAbort = () => controller.abort();\n\topts.signal?.addEventListener(\"abort\", onOuterAbort, { once: true });\n\n\ttry {\n\t\tconst url = appendPath(baseUrl, \"models\");\n\t\tconst headers: Record<string, string> = { Accept: \"application/json\" };\n\t\tif (opts.apiKey) headers.Authorization = `Bearer ${opts.apiKey}`;\n\n\t\tconst response = await fetch(url, { headers, signal: controller.signal });\n\t\tif (!response.ok) {\n\t\t\tconst body = await readTextBounded(response, 4_096);\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `HTTP ${response.status}${body ? `: ${body.slice(0, 400)}` : \"\"}`,\n\t\t\t};\n\t\t}\n\t\tconst text = await readTextBounded(response, DEFAULTS.probeBodyBytes);\n\t\tlet json: unknown;\n\t\ttry {\n\t\t\tjson = JSON.parse(text);\n\t\t} catch {\n\t\t\treturn { ok: false, error: \"Model catalog response is not JSON.\" };\n\t\t}\n\t\tconst models = parseOpenAIModels(json);\n\t\tif (models === null) {\n\t\t\treturn { ok: false, error: \"JSON has no OpenAI-style `data` array of model ids.\" };\n\t\t}\n\t\tconst sorted = dedupeSort(models);\n\t\tconst truncated = sorted.length > DEFAULTS.probeMaxModels;\n\t\treturn {\n\t\t\tok: true,\n\t\t\tmodels: sorted.slice(0, DEFAULTS.probeMaxModels),\n\t\t\ttruncated,\n\t\t};\n\t} catch (error) {\n\t\tif (controller.signal.aborted) {\n\t\t\treturn { ok: false, error: opts.signal?.aborted ? \"Cancelled.\" : `Timed out after ${timeoutMs}ms.` };\n\t\t}\n\t\treturn { ok: false, error: formatError(error) };\n\t} finally {\n\t\tclearTimeout(timer);\n\t\topts.signal?.removeEventListener(\"abort\", onOuterAbort);\n\t}\n}\n\nfunction appendPath(base: URL, segment: string): URL {\n\tconst url = new URL(base.href);\n\tconst path = url.pathname.replace(/\\/+$/, \"\");\n\turl.pathname = `${path}/${segment}`;\n\treturn url;\n}\n\nfunction parseOpenAIModels(json: unknown): ProbeModel[] | null {\n\tif (!json || typeof json !== \"object\") return null;\n\tconst data = (json as { data?: unknown }).data;\n\tif (!Array.isArray(data)) return null;\n\tconst models: ProbeModel[] = [];\n\tfor (const item of data) {\n\t\tif (!item || typeof item !== \"object\") continue;\n\t\tconst record = item as Record<string, unknown>;\n\t\tconst id = typeof record.id === \"string\" ? record.id.trim() : \"\";\n\t\tif (!id) continue;\n\t\tconst name =\n\t\t\ttypeof record.name === \"string\" && record.name.trim() && record.name.trim() !== id\n\t\t\t\t? record.name.trim()\n\t\t\t\t: undefined;\n\t\tmodels.push(name ? { id, name } : { id });\n\t}\n\treturn models;\n}\n\nfunction dedupeSort(models: ProbeModel[]): ProbeModel[] {\n\tconst map = new Map<string, ProbeModel>();\n\tfor (const model of models) {\n\t\tif (!map.has(model.id)) map.set(model.id, model);\n\t}\n\treturn [...map.values()].sort((a, b) => a.id.localeCompare(b.id));\n}\n\nasync function readTextBounded(response: Response, maxBytes: number): Promise<string> {\n\tconst reader = response.body?.getReader();\n\tif (!reader) return await response.text();\n\tconst chunks: Uint8Array[] = [];\n\tlet total = 0;\n\twhile (true) {\n\t\tconst { done, value } = await reader.read();\n\t\tif (done) break;\n\t\tif (!value) continue;\n\t\ttotal += value.byteLength;\n\t\tif (total > maxBytes) {\n\t\t\tawait reader.cancel();\n\t\t\tthrow new Error(`Response exceeds ${maxBytes} bytes.`);\n\t\t}\n\t\tchunks.push(value);\n\t}\n\tconst merged = new Uint8Array(total);\n\tlet offset = 0;\n\tfor (const chunk of chunks) {\n\t\tmerged.set(chunk, offset);\n\t\toffset += chunk.byteLength;\n\t}\n\treturn new TextDecoder().decode(merged);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../../src/extensions/router/ui.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../../src/extensions/router/ui.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AA6F5F,wBAAgB,oBAAoB,CACnC,OAAO,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,EACrD,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACb,OAAO,CAET;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,uBAAuB,EAAE,EAAE,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBlH;AAqtBD,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAiBrF"}
|