@dexto/tui 1.15.0-beta.0 → 1.15.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/components/Footer.cjs +0 -31
- package/dist/components/Footer.d.ts.map +1 -1
- package/dist/components/Footer.js +0 -31
- package/dist/components/Footer.test.cjs +0 -44
- package/dist/components/Footer.test.js +0 -44
- package/dist/components/overlays/InsufficientCreditsOverlay.cjs +4 -19
- package/dist/components/overlays/InsufficientCreditsOverlay.d.ts +0 -2
- package/dist/components/overlays/InsufficientCreditsOverlay.d.ts.map +1 -1
- package/dist/components/overlays/InsufficientCreditsOverlay.js +4 -19
- package/dist/components/overlays/LoginOverlay.cjs +61 -25
- package/dist/components/overlays/LoginOverlay.d.ts +3 -1
- package/dist/components/overlays/LoginOverlay.d.ts.map +1 -1
- package/dist/components/overlays/LoginOverlay.js +66 -26
- package/dist/components/overlays/LogoutOverlay.cjs +17 -8
- package/dist/components/overlays/LogoutOverlay.d.ts +0 -1
- package/dist/components/overlays/LogoutOverlay.d.ts.map +1 -1
- package/dist/components/overlays/LogoutOverlay.js +31 -10
- package/dist/components/overlays/custom-model-wizard/provider-config.cjs +4 -4
- package/dist/components/overlays/custom-model-wizard/provider-config.js +4 -4
- package/dist/containers/OverlayContainer.cjs +40 -30
- package/dist/containers/OverlayContainer.d.ts.map +1 -1
- package/dist/containers/OverlayContainer.js +40 -30
- package/dist/host/index.cjs +49 -22
- package/dist/host/index.d.ts +72 -18
- package/dist/host/index.d.ts.map +1 -1
- package/dist/host/index.js +42 -18
- package/dist/host/index.test.cjs +35 -21
- package/dist/host/index.test.js +40 -22
- package/dist/index.d.cts +52 -13
- package/dist/services/processStream.cjs +2 -2
- package/dist/services/processStream.js +2 -2
- package/dist/services/processStream.test.cjs +1 -1
- package/dist/services/processStream.test.js +1 -1
- package/dist/utils/llm-provider-display.cjs +1 -1
- package/dist/utils/llm-provider-display.js +1 -1
- package/package.json +5 -5
|
@@ -40,7 +40,6 @@ var import_core = require("@dexto/core");
|
|
|
40
40
|
var import_llm_provider_display = require("../utils/llm-provider-display.js");
|
|
41
41
|
var import_chatgpt_rate_limit = require("../utils/chatgpt-rate-limit.js");
|
|
42
42
|
var import_agent_backend = require("../agent-backend.js");
|
|
43
|
-
var import_host = require("../host/index.js");
|
|
44
43
|
function Footer({
|
|
45
44
|
agent,
|
|
46
45
|
sessionId,
|
|
@@ -126,31 +125,6 @@ function Footer({
|
|
|
126
125
|
controller.abort();
|
|
127
126
|
};
|
|
128
127
|
}, [agent, sessionId]);
|
|
129
|
-
const [creditBalance, setCreditBalance] = (0, import_react.useState)(null);
|
|
130
|
-
const usesDextoCredits = provider === "dexto-nova";
|
|
131
|
-
(0, import_react.useEffect)(() => {
|
|
132
|
-
setCreditBalance(null);
|
|
133
|
-
if (!usesDextoCredits) return;
|
|
134
|
-
let cancelled = false;
|
|
135
|
-
const refresh = () => {
|
|
136
|
-
(0, import_host.dextoCreditBalance)().then((balance) => {
|
|
137
|
-
if (!cancelled) setCreditBalance(balance);
|
|
138
|
-
}).catch(() => void 0);
|
|
139
|
-
};
|
|
140
|
-
refresh();
|
|
141
|
-
const controller = new AbortController();
|
|
142
|
-
agent.on(
|
|
143
|
-
"llm:response",
|
|
144
|
-
(payload) => {
|
|
145
|
-
if (!payload.sessionId || payload.sessionId === sessionId) refresh();
|
|
146
|
-
},
|
|
147
|
-
{ signal: controller.signal }
|
|
148
|
-
);
|
|
149
|
-
return () => {
|
|
150
|
-
cancelled = true;
|
|
151
|
-
controller.abort();
|
|
152
|
-
};
|
|
153
|
-
}, [agent, sessionId, usesDextoCredits]);
|
|
154
128
|
const pathColor = isShellMode ? "yellow" : "blue";
|
|
155
129
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { flexDirection: "column", paddingX: 1, children: [
|
|
156
130
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { flexDirection: "row", justifyContent: "space-between", children: [
|
|
@@ -179,11 +153,6 @@ function Footer({
|
|
|
179
153
|
contextLeft.percentLeft,
|
|
180
154
|
"% context left"
|
|
181
155
|
] }) }),
|
|
182
|
-
creditBalance !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "gray", children: [
|
|
183
|
-
"$",
|
|
184
|
-
creditBalance.toFixed(2),
|
|
185
|
-
" credits"
|
|
186
|
-
] }) }),
|
|
187
156
|
chatGPTRateLimitHint && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: chatGPTRateLimitColor, children: chatGPTRateLimitHint }) }),
|
|
188
157
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "gray", children: [
|
|
189
158
|
"approvals: ",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Footer.d.ts","sourceRoot":"","sources":["../../src/components/Footer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAqB,KAAK,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAM7E,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"Footer.d.ts","sourceRoot":"","sources":["../../src/components/Footer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAqB,KAAK,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAM7E,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEjF,UAAU,WAAW;IACjB,KAAK,EAAE,eAAe,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kEAAkE;IAClE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sBAAsB,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;CAC1D;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,EACnB,KAAK,EACL,SAAS,EACT,SAAS,EACT,GAAG,EACH,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,WAAW,EACX,sBAAsB,GACzB,EAAE,WAAW,2CAkLb"}
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
shouldShowChatGPTRateLimitHint
|
|
11
11
|
} from "../utils/chatgpt-rate-limit.js";
|
|
12
12
|
import { supportsContextStats } from "../agent-backend.js";
|
|
13
|
-
import { dextoCreditBalance } from "../host/index.js";
|
|
14
13
|
function Footer({
|
|
15
14
|
agent,
|
|
16
15
|
sessionId,
|
|
@@ -96,31 +95,6 @@ function Footer({
|
|
|
96
95
|
controller.abort();
|
|
97
96
|
};
|
|
98
97
|
}, [agent, sessionId]);
|
|
99
|
-
const [creditBalance, setCreditBalance] = useState(null);
|
|
100
|
-
const usesDextoCredits = provider === "dexto-nova";
|
|
101
|
-
useEffect(() => {
|
|
102
|
-
setCreditBalance(null);
|
|
103
|
-
if (!usesDextoCredits) return;
|
|
104
|
-
let cancelled = false;
|
|
105
|
-
const refresh = () => {
|
|
106
|
-
dextoCreditBalance().then((balance) => {
|
|
107
|
-
if (!cancelled) setCreditBalance(balance);
|
|
108
|
-
}).catch(() => void 0);
|
|
109
|
-
};
|
|
110
|
-
refresh();
|
|
111
|
-
const controller = new AbortController();
|
|
112
|
-
agent.on(
|
|
113
|
-
"llm:response",
|
|
114
|
-
(payload) => {
|
|
115
|
-
if (!payload.sessionId || payload.sessionId === sessionId) refresh();
|
|
116
|
-
},
|
|
117
|
-
{ signal: controller.signal }
|
|
118
|
-
);
|
|
119
|
-
return () => {
|
|
120
|
-
cancelled = true;
|
|
121
|
-
controller.abort();
|
|
122
|
-
};
|
|
123
|
-
}, [agent, sessionId, usesDextoCredits]);
|
|
124
98
|
const pathColor = isShellMode ? "yellow" : "blue";
|
|
125
99
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
|
|
126
100
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [
|
|
@@ -149,11 +123,6 @@ function Footer({
|
|
|
149
123
|
contextLeft.percentLeft,
|
|
150
124
|
"% context left"
|
|
151
125
|
] }) }),
|
|
152
|
-
creditBalance !== null && /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
153
|
-
"$",
|
|
154
|
-
creditBalance.toFixed(2),
|
|
155
|
-
" credits"
|
|
156
|
-
] }) }),
|
|
157
126
|
chatGPTRateLimitHint && /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { color: chatGPTRateLimitColor, children: chatGPTRateLimitHint }) }),
|
|
158
127
|
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
159
128
|
"approvals: ",
|
|
@@ -4,7 +4,6 @@ var import_node_stream = require("node:stream");
|
|
|
4
4
|
var import_ink = require("ink");
|
|
5
5
|
var import_vitest = require("vitest");
|
|
6
6
|
var import_Footer = require("./Footer.js");
|
|
7
|
-
var import_host = require("../host/index.js");
|
|
8
7
|
(0, import_vitest.describe)("configured approval policy in the TUI footer", () => {
|
|
9
8
|
import_vitest.it.each(["manual", "auto-approve"])(
|
|
10
9
|
"shows %s independently of session shortcuts",
|
|
@@ -46,46 +45,3 @@ var import_host = require("../host/index.js");
|
|
|
46
45
|
}
|
|
47
46
|
);
|
|
48
47
|
});
|
|
49
|
-
(0, import_vitest.describe)("Dexto credits in the TUI footer", () => {
|
|
50
|
-
async function renderFooter(provider, balance) {
|
|
51
|
-
(0, import_host.setTuiRuntimeServices)({ dextoCreditBalance: async () => balance });
|
|
52
|
-
const stdout = Object.assign(new import_node_stream.PassThrough(), { columns: 120, rows: 40, isTTY: false });
|
|
53
|
-
let output = "";
|
|
54
|
-
stdout.on("data", (chunk) => {
|
|
55
|
-
output += String(chunk);
|
|
56
|
-
});
|
|
57
|
-
const agent = {
|
|
58
|
-
capabilities: { contextStats: false },
|
|
59
|
-
getEffectiveConfig: () => ({ permissions: { mode: "manual" } }),
|
|
60
|
-
getCurrentLLMConfig: () => ({ provider, model: "openai/gpt-5" }),
|
|
61
|
-
on: () => void 0
|
|
62
|
-
};
|
|
63
|
-
const view = (0, import_ink.render)(
|
|
64
|
-
(0, import_react.createElement)(import_Footer.Footer, { agent, sessionId: "session-1", modelName: "openai/gpt-5" }),
|
|
65
|
-
{
|
|
66
|
-
stdout,
|
|
67
|
-
debug: true,
|
|
68
|
-
patchConsole: false,
|
|
69
|
-
exitOnCtrlC: false
|
|
70
|
-
}
|
|
71
|
-
);
|
|
72
|
-
try {
|
|
73
|
-
await import_vitest.vi.waitFor(() => (0, import_vitest.expect)(output).toContain("approvals: manual"));
|
|
74
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
75
|
-
return output;
|
|
76
|
-
} finally {
|
|
77
|
-
view.unmount();
|
|
78
|
-
stdout.destroy();
|
|
79
|
-
(0, import_host.setTuiRuntimeServices)({});
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
(0, import_vitest.it)("shows the balance while a Dexto model runs, under the name Dexto", async () => {
|
|
83
|
-
const output = await renderFooter("dexto-nova", 12.5);
|
|
84
|
-
(0, import_vitest.expect)(output).toContain("$12.50 credits");
|
|
85
|
-
(0, import_vitest.expect)(output).toContain("(Dexto)");
|
|
86
|
-
});
|
|
87
|
-
(0, import_vitest.it)("shows no balance for other providers", async () => {
|
|
88
|
-
const output = await renderFooter("openai", 12.5);
|
|
89
|
-
(0, import_vitest.expect)(output).not.toContain("credits");
|
|
90
|
-
});
|
|
91
|
-
});
|
|
@@ -3,7 +3,6 @@ import { PassThrough } from "node:stream";
|
|
|
3
3
|
import { render } from "ink";
|
|
4
4
|
import { describe, expect, it, vi } from "vitest";
|
|
5
5
|
import { Footer } from "./Footer.js";
|
|
6
|
-
import { setTuiRuntimeServices } from "../host/index.js";
|
|
7
6
|
describe("configured approval policy in the TUI footer", () => {
|
|
8
7
|
it.each(["manual", "auto-approve"])(
|
|
9
8
|
"shows %s independently of session shortcuts",
|
|
@@ -45,46 +44,3 @@ describe("configured approval policy in the TUI footer", () => {
|
|
|
45
44
|
}
|
|
46
45
|
);
|
|
47
46
|
});
|
|
48
|
-
describe("Dexto credits in the TUI footer", () => {
|
|
49
|
-
async function renderFooter(provider, balance) {
|
|
50
|
-
setTuiRuntimeServices({ dextoCreditBalance: async () => balance });
|
|
51
|
-
const stdout = Object.assign(new PassThrough(), { columns: 120, rows: 40, isTTY: false });
|
|
52
|
-
let output = "";
|
|
53
|
-
stdout.on("data", (chunk) => {
|
|
54
|
-
output += String(chunk);
|
|
55
|
-
});
|
|
56
|
-
const agent = {
|
|
57
|
-
capabilities: { contextStats: false },
|
|
58
|
-
getEffectiveConfig: () => ({ permissions: { mode: "manual" } }),
|
|
59
|
-
getCurrentLLMConfig: () => ({ provider, model: "openai/gpt-5" }),
|
|
60
|
-
on: () => void 0
|
|
61
|
-
};
|
|
62
|
-
const view = render(
|
|
63
|
-
createElement(Footer, { agent, sessionId: "session-1", modelName: "openai/gpt-5" }),
|
|
64
|
-
{
|
|
65
|
-
stdout,
|
|
66
|
-
debug: true,
|
|
67
|
-
patchConsole: false,
|
|
68
|
-
exitOnCtrlC: false
|
|
69
|
-
}
|
|
70
|
-
);
|
|
71
|
-
try {
|
|
72
|
-
await vi.waitFor(() => expect(output).toContain("approvals: manual"));
|
|
73
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
74
|
-
return output;
|
|
75
|
-
} finally {
|
|
76
|
-
view.unmount();
|
|
77
|
-
stdout.destroy();
|
|
78
|
-
setTuiRuntimeServices({});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
it("shows the balance while a Dexto model runs, under the name Dexto", async () => {
|
|
82
|
-
const output = await renderFooter("dexto-nova", 12.5);
|
|
83
|
-
expect(output).toContain("$12.50 credits");
|
|
84
|
-
expect(output).toContain("(Dexto)");
|
|
85
|
-
});
|
|
86
|
-
it("shows no balance for other providers", async () => {
|
|
87
|
-
const output = await renderFooter("openai", 12.5);
|
|
88
|
-
expect(output).not.toContain("credits");
|
|
89
|
-
});
|
|
90
|
-
});
|
|
@@ -49,11 +49,6 @@ const TOP_UP_OPTIONS = [
|
|
|
49
49
|
id: "open-billing",
|
|
50
50
|
label: "Open billing page",
|
|
51
51
|
description: "View full billing dashboard"
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
id: "own-key",
|
|
55
|
-
label: "Use your own model key",
|
|
56
|
-
description: "Pick a model from a provider you have a key for"
|
|
57
52
|
}
|
|
58
53
|
];
|
|
59
54
|
const WAITING_OPTIONS = [
|
|
@@ -82,7 +77,7 @@ function formatBalance(balanceUsd) {
|
|
|
82
77
|
function getErrorMessage(error) {
|
|
83
78
|
return error instanceof Error ? error.message : String(error);
|
|
84
79
|
}
|
|
85
|
-
const InsufficientCreditsOverlay = (0, import_react.forwardRef)(function InsufficientCreditsOverlay2({ isVisible, initialBalanceUsd, onResolved, onClose
|
|
80
|
+
const InsufficientCreditsOverlay = (0, import_react.forwardRef)(function InsufficientCreditsOverlay2({ isVisible, initialBalanceUsd, onResolved, onClose }, ref) {
|
|
86
81
|
const selectorRef = (0, import_react.useRef)(null);
|
|
87
82
|
const isActiveRef = (0, import_react.useRef)(false);
|
|
88
83
|
const [step, setStep] = (0, import_react.useState)("select");
|
|
@@ -195,9 +190,6 @@ const InsufficientCreditsOverlay = (0, import_react.forwardRef)(function Insuffi
|
|
|
195
190
|
case "open-billing":
|
|
196
191
|
void openBillingDashboard();
|
|
197
192
|
return;
|
|
198
|
-
case "own-key":
|
|
199
|
-
onChooseOwnModel();
|
|
200
|
-
return;
|
|
201
193
|
case "reopen":
|
|
202
194
|
void reopenBilling();
|
|
203
195
|
return;
|
|
@@ -209,14 +201,7 @@ const InsufficientCreditsOverlay = (0, import_react.forwardRef)(function Insuffi
|
|
|
209
201
|
return;
|
|
210
202
|
}
|
|
211
203
|
},
|
|
212
|
-
[
|
|
213
|
-
onChooseOwnModel,
|
|
214
|
-
onResolved,
|
|
215
|
-
openBillingDashboard,
|
|
216
|
-
openPrefilledBilling,
|
|
217
|
-
reopenBilling,
|
|
218
|
-
resetToSelection
|
|
219
|
-
]
|
|
204
|
+
[onResolved, openBillingDashboard, openPrefilledBilling, reopenBilling, resetToSelection]
|
|
220
205
|
);
|
|
221
206
|
const formatItem = (0, import_react.useCallback)((option, isSelected) => {
|
|
222
207
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
@@ -243,9 +228,9 @@ const InsufficientCreditsOverlay = (0, import_react.forwardRef)(function Insuffi
|
|
|
243
228
|
return null;
|
|
244
229
|
}
|
|
245
230
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { flexDirection: "column", children: [
|
|
246
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "yellow", bold: true, children: "Out of Dexto credits" }) }),
|
|
231
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "yellow", bold: true, children: "Out of Dexto Nova credits" }) }),
|
|
247
232
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { flexDirection: "column", marginTop: 1, children: [
|
|
248
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", children: "This request stopped because your Dexto
|
|
233
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", children: "This request stopped because your Dexto Nova balance ran out." }),
|
|
249
234
|
formatBalance(balanceUsd) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "gray", children: [
|
|
250
235
|
"Current balance: ",
|
|
251
236
|
formatBalance(balanceUsd)
|
|
@@ -5,8 +5,6 @@ export interface InsufficientCreditsOverlayProps {
|
|
|
5
5
|
initialBalanceUsd: number | null;
|
|
6
6
|
onResolved: () => void;
|
|
7
7
|
onClose: () => void;
|
|
8
|
-
/** Leaves credits for a model on the person's own key. */
|
|
9
|
-
onChooseOwnModel: () => void;
|
|
10
8
|
}
|
|
11
9
|
export interface InsufficientCreditsOverlayHandle {
|
|
12
10
|
handleInput: (input: string, key: Key) => boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InsufficientCreditsOverlay.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/InsufficientCreditsOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"InsufficientCreditsOverlay.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/InsufficientCreditsOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AA6E/D,MAAM,WAAW,+BAA+B;IAC5C,SAAS,EAAE,OAAO,CAAC;IACnB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,gCAAgC;IAC7C,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,QAAA,MAAM,0BAA0B,0HAiO9B,CAAC;AAEH,eAAe,0BAA0B,CAAC"}
|
|
@@ -34,11 +34,6 @@ const TOP_UP_OPTIONS = [
|
|
|
34
34
|
id: "open-billing",
|
|
35
35
|
label: "Open billing page",
|
|
36
36
|
description: "View full billing dashboard"
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
id: "own-key",
|
|
40
|
-
label: "Use your own model key",
|
|
41
|
-
description: "Pick a model from a provider you have a key for"
|
|
42
37
|
}
|
|
43
38
|
];
|
|
44
39
|
const WAITING_OPTIONS = [
|
|
@@ -67,7 +62,7 @@ function formatBalance(balanceUsd) {
|
|
|
67
62
|
function getErrorMessage(error) {
|
|
68
63
|
return error instanceof Error ? error.message : String(error);
|
|
69
64
|
}
|
|
70
|
-
const InsufficientCreditsOverlay = forwardRef(function InsufficientCreditsOverlay2({ isVisible, initialBalanceUsd, onResolved, onClose
|
|
65
|
+
const InsufficientCreditsOverlay = forwardRef(function InsufficientCreditsOverlay2({ isVisible, initialBalanceUsd, onResolved, onClose }, ref) {
|
|
71
66
|
const selectorRef = useRef(null);
|
|
72
67
|
const isActiveRef = useRef(false);
|
|
73
68
|
const [step, setStep] = useState("select");
|
|
@@ -180,9 +175,6 @@ const InsufficientCreditsOverlay = forwardRef(function InsufficientCreditsOverla
|
|
|
180
175
|
case "open-billing":
|
|
181
176
|
void openBillingDashboard();
|
|
182
177
|
return;
|
|
183
|
-
case "own-key":
|
|
184
|
-
onChooseOwnModel();
|
|
185
|
-
return;
|
|
186
178
|
case "reopen":
|
|
187
179
|
void reopenBilling();
|
|
188
180
|
return;
|
|
@@ -194,14 +186,7 @@ const InsufficientCreditsOverlay = forwardRef(function InsufficientCreditsOverla
|
|
|
194
186
|
return;
|
|
195
187
|
}
|
|
196
188
|
},
|
|
197
|
-
[
|
|
198
|
-
onChooseOwnModel,
|
|
199
|
-
onResolved,
|
|
200
|
-
openBillingDashboard,
|
|
201
|
-
openPrefilledBilling,
|
|
202
|
-
reopenBilling,
|
|
203
|
-
resetToSelection
|
|
204
|
-
]
|
|
189
|
+
[onResolved, openBillingDashboard, openPrefilledBilling, reopenBilling, resetToSelection]
|
|
205
190
|
);
|
|
206
191
|
const formatItem = useCallback((option, isSelected) => {
|
|
207
192
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -228,9 +213,9 @@ const InsufficientCreditsOverlay = forwardRef(function InsufficientCreditsOverla
|
|
|
228
213
|
return null;
|
|
229
214
|
}
|
|
230
215
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
231
|
-
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { color: "yellow", bold: true, children: "Out of Dexto credits" }) }),
|
|
216
|
+
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { color: "yellow", bold: true, children: "Out of Dexto Nova credits" }) }),
|
|
232
217
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
233
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "This request stopped because your Dexto
|
|
218
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: "This request stopped because your Dexto Nova balance ran out." }),
|
|
234
219
|
formatBalance(balanceUsd) && /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
235
220
|
"Current balance: ",
|
|
236
221
|
formatBalance(balanceUsd)
|
|
@@ -28,8 +28,9 @@ var import_host = require("../../host/index.js");
|
|
|
28
28
|
const LoginOverlay = (0, import_react.forwardRef)(function LoginOverlay2({ isVisible, onDone }, ref) {
|
|
29
29
|
const [step, setStep] = (0, import_react.useState)("checking");
|
|
30
30
|
const [existingUser, setExistingUser] = (0, import_react.useState)(null);
|
|
31
|
-
const [
|
|
32
|
-
const [
|
|
31
|
+
const [authUrl, setAuthUrl] = (0, import_react.useState)(null);
|
|
32
|
+
const [devicePrompt, setDevicePrompt] = (0, import_react.useState)(null);
|
|
33
|
+
const [status, setStatus] = (0, import_react.useState)("Preparing login...");
|
|
33
34
|
const [error, setError] = (0, import_react.useState)(null);
|
|
34
35
|
const abortControllerRef = (0, import_react.useRef)(null);
|
|
35
36
|
const isActiveRef = (0, import_react.useRef)(false);
|
|
@@ -41,24 +42,47 @@ const LoginOverlay = (0, import_react.forwardRef)(function LoginOverlay2({ isVis
|
|
|
41
42
|
abortControllerRef.current?.abort(new Error("Authentication cancelled"));
|
|
42
43
|
abortControllerRef.current = null;
|
|
43
44
|
}, []);
|
|
45
|
+
const runDeviceLogin = (0, import_react.useCallback)(
|
|
46
|
+
async (abortController) => {
|
|
47
|
+
setStep("starting");
|
|
48
|
+
safeSetStatus("Starting device code login...");
|
|
49
|
+
setAuthUrl(null);
|
|
50
|
+
setDevicePrompt(null);
|
|
51
|
+
return (0, import_host.performDeviceCodeLogin)({
|
|
52
|
+
signal: abortController.signal,
|
|
53
|
+
onPrompt: (prompt) => {
|
|
54
|
+
if (!isActiveRef.current || abortController.signal.aborted) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
setDevicePrompt(prompt);
|
|
58
|
+
setAuthUrl(prompt.verificationUrlComplete ?? prompt.verificationUrl);
|
|
59
|
+
setStep("waiting");
|
|
60
|
+
safeSetStatus("Open the URL below and approve login.");
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
[safeSetStatus]
|
|
65
|
+
);
|
|
44
66
|
const startLogin = (0, import_react.useCallback)(async () => {
|
|
45
67
|
cancelInFlight();
|
|
46
68
|
setError(null);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
safeSetStatus("Signing in...");
|
|
69
|
+
setAuthUrl(null);
|
|
70
|
+
setDevicePrompt(null);
|
|
50
71
|
const abortController = new AbortController();
|
|
51
72
|
abortControllerRef.current = abortController;
|
|
52
73
|
try {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
});
|
|
74
|
+
const result = await runDeviceLogin(abortController);
|
|
75
|
+
if (!result || !isActiveRef.current || abortController.signal.aborted) return;
|
|
76
|
+
setStep("finalizing");
|
|
77
|
+
safeSetStatus("Finalizing login...");
|
|
78
|
+
const persisted = await (0, import_host.persistDeviceApiKeyLoginResult)(result);
|
|
60
79
|
if (!isActiveRef.current || abortController.signal.aborted) return;
|
|
61
|
-
onDone({
|
|
80
|
+
onDone({
|
|
81
|
+
outcome: "success",
|
|
82
|
+
email: persisted.email,
|
|
83
|
+
keyId: persisted.keyId,
|
|
84
|
+
hasDextoApiKey: persisted.hasDextoApiKey
|
|
85
|
+
});
|
|
62
86
|
} catch (err) {
|
|
63
87
|
if (abortController.signal.aborted) {
|
|
64
88
|
return;
|
|
@@ -66,27 +90,28 @@ const LoginOverlay = (0, import_react.forwardRef)(function LoginOverlay2({ isVis
|
|
|
66
90
|
setStep("error");
|
|
67
91
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
68
92
|
setError(errorMessage);
|
|
69
|
-
safeSetStatus("
|
|
93
|
+
safeSetStatus("Login failed");
|
|
70
94
|
} finally {
|
|
71
95
|
abortControllerRef.current = null;
|
|
72
96
|
}
|
|
73
|
-
}, [cancelInFlight, onDone, safeSetStatus]);
|
|
97
|
+
}, [cancelInFlight, onDone, runDeviceLogin, safeSetStatus]);
|
|
74
98
|
(0, import_react.useEffect)(() => {
|
|
75
99
|
if (!isVisible) return;
|
|
76
100
|
isActiveRef.current = true;
|
|
77
101
|
setStep("checking");
|
|
78
102
|
setExistingUser(null);
|
|
79
|
-
|
|
80
|
-
|
|
103
|
+
setAuthUrl(null);
|
|
104
|
+
setDevicePrompt(null);
|
|
105
|
+
setStatus("Preparing login...");
|
|
81
106
|
setError(null);
|
|
82
107
|
void (async () => {
|
|
83
108
|
try {
|
|
84
|
-
const
|
|
109
|
+
const auth = await (0, import_host.loadAuth)();
|
|
85
110
|
if (!isActiveRef.current) return;
|
|
86
|
-
if (
|
|
87
|
-
setExistingUser(
|
|
111
|
+
if (auth) {
|
|
112
|
+
setExistingUser(auth.email || auth.userId || "user");
|
|
88
113
|
setStep("already-authenticated");
|
|
89
|
-
setStatus("You are already
|
|
114
|
+
setStatus("You are already logged in");
|
|
90
115
|
return;
|
|
91
116
|
}
|
|
92
117
|
await startLogin();
|
|
@@ -126,7 +151,7 @@ const LoginOverlay = (0, import_react.forwardRef)(function LoginOverlay2({ isVis
|
|
|
126
151
|
[cancelInFlight, isVisible, onDone, startLogin, step]
|
|
127
152
|
);
|
|
128
153
|
if (!isVisible) return null;
|
|
129
|
-
const hint = step === "already-authenticated" ? "Enter to
|
|
154
|
+
const hint = step === "already-authenticated" ? "Enter to login again \u2022 Esc to close" : step === "error" ? "Enter to retry \u2022 Esc to close" : "Esc to cancel";
|
|
130
155
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
131
156
|
import_ink.Box,
|
|
132
157
|
{
|
|
@@ -136,16 +161,27 @@ const LoginOverlay = (0, import_react.forwardRef)(function LoginOverlay2({ isVis
|
|
|
136
161
|
paddingX: 1,
|
|
137
162
|
marginTop: 1,
|
|
138
163
|
children: [
|
|
139
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { bold: true, color: "cyan", children: "
|
|
164
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { bold: true, color: "cyan", children: "Login to Dexto" }) }),
|
|
140
165
|
existingUser && step === "already-authenticated" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "green", children: [
|
|
141
|
-
"
|
|
166
|
+
"\u2705 Already logged in as: ",
|
|
142
167
|
existingUser
|
|
143
168
|
] }) }),
|
|
144
169
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { marginBottom: 1, children: [
|
|
145
170
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", children: "Status: " }),
|
|
146
171
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { children: status })
|
|
147
172
|
] }),
|
|
148
|
-
|
|
173
|
+
devicePrompt && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
174
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", children: "Open this URL on any device:" }),
|
|
175
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "yellowBright", children: devicePrompt.verificationUrlComplete ?? devicePrompt.verificationUrl }),
|
|
176
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "gray", children: [
|
|
177
|
+
"Code: ",
|
|
178
|
+
devicePrompt.userCode
|
|
179
|
+
] })
|
|
180
|
+
] }),
|
|
181
|
+
authUrl && !devicePrompt && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
182
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", children: "If your browser didn't open, use this URL:" }),
|
|
183
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "yellowBright", children: authUrl })
|
|
184
|
+
] }),
|
|
149
185
|
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "red", children: [
|
|
150
186
|
"Error: ",
|
|
151
187
|
error
|
|
@@ -2,7 +2,9 @@ import React from 'react';
|
|
|
2
2
|
import type { Key } from '../../hooks/useInputOrchestrator.js';
|
|
3
3
|
export type LoginOverlayOutcome = {
|
|
4
4
|
outcome: 'success';
|
|
5
|
-
|
|
5
|
+
email?: string | undefined;
|
|
6
|
+
keyId?: string | undefined;
|
|
7
|
+
hasDextoApiKey: boolean;
|
|
6
8
|
} | {
|
|
7
9
|
outcome: 'cancelled';
|
|
8
10
|
} | {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoginOverlay.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/LoginOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAON,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"LoginOverlay.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/LoginOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAON,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAS/D,MAAM,MAAM,mBAAmB,GACzB;IACI,OAAO,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,cAAc,EAAE,OAAO,CAAC;CAC3B,GACD;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,GACxB;IAAE,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC;AAE5B,MAAM,WAAW,iBAAiB;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAClD;AAED,MAAM,WAAW,kBAAkB;IAC/B,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAUD,QAAA,MAAM,YAAY,8FA0NhB,CAAC;AAEH,eAAe,YAAY,CAAC"}
|