@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.
Files changed (36) hide show
  1. package/dist/components/Footer.cjs +0 -31
  2. package/dist/components/Footer.d.ts.map +1 -1
  3. package/dist/components/Footer.js +0 -31
  4. package/dist/components/Footer.test.cjs +0 -44
  5. package/dist/components/Footer.test.js +0 -44
  6. package/dist/components/overlays/InsufficientCreditsOverlay.cjs +4 -19
  7. package/dist/components/overlays/InsufficientCreditsOverlay.d.ts +0 -2
  8. package/dist/components/overlays/InsufficientCreditsOverlay.d.ts.map +1 -1
  9. package/dist/components/overlays/InsufficientCreditsOverlay.js +4 -19
  10. package/dist/components/overlays/LoginOverlay.cjs +61 -25
  11. package/dist/components/overlays/LoginOverlay.d.ts +3 -1
  12. package/dist/components/overlays/LoginOverlay.d.ts.map +1 -1
  13. package/dist/components/overlays/LoginOverlay.js +66 -26
  14. package/dist/components/overlays/LogoutOverlay.cjs +17 -8
  15. package/dist/components/overlays/LogoutOverlay.d.ts +0 -1
  16. package/dist/components/overlays/LogoutOverlay.d.ts.map +1 -1
  17. package/dist/components/overlays/LogoutOverlay.js +31 -10
  18. package/dist/components/overlays/custom-model-wizard/provider-config.cjs +4 -4
  19. package/dist/components/overlays/custom-model-wizard/provider-config.js +4 -4
  20. package/dist/containers/OverlayContainer.cjs +40 -30
  21. package/dist/containers/OverlayContainer.d.ts.map +1 -1
  22. package/dist/containers/OverlayContainer.js +40 -30
  23. package/dist/host/index.cjs +49 -22
  24. package/dist/host/index.d.ts +72 -18
  25. package/dist/host/index.d.ts.map +1 -1
  26. package/dist/host/index.js +42 -18
  27. package/dist/host/index.test.cjs +35 -21
  28. package/dist/host/index.test.js +40 -22
  29. package/dist/index.d.cts +52 -13
  30. package/dist/services/processStream.cjs +2 -2
  31. package/dist/services/processStream.js +2 -2
  32. package/dist/services/processStream.test.cjs +1 -1
  33. package/dist/services/processStream.test.js +1 -1
  34. package/dist/utils/llm-provider-display.cjs +1 -1
  35. package/dist/utils/llm-provider-display.js +1 -1
  36. 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;AAGjF,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,2CAqNb"}
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, onChooseOwnModel }, ref) {
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 credits ran out." }),
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;AAmF/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;IACpB,0DAA0D;IAC1D,gBAAgB,EAAE,MAAM,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,gCAAgC;IAC7C,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,QAAA,MAAM,0BAA0B,0HA4O9B,CAAC;AAEH,eAAe,0BAA0B,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, onChooseOwnModel }, ref) {
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 credits ran out." }),
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 [signInNote, setSignInNote] = (0, import_react.useState)(null);
32
- const [status, setStatus] = (0, import_react.useState)("Preparing sign-in...");
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
- setSignInNote(null);
48
- setStep("signing-in");
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 summary = await (0, import_host.signIn)({
54
- signal: abortController.signal,
55
- onNote: (text) => {
56
- if (!isActiveRef.current || abortController.signal.aborted) return;
57
- setSignInNote(text);
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({ outcome: "success", summary });
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("Sign-in failed");
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
- setSignInNote(null);
80
- setStatus("Preparing sign-in...");
103
+ setAuthUrl(null);
104
+ setDevicePrompt(null);
105
+ setStatus("Preparing login...");
81
106
  setError(null);
82
107
  void (async () => {
83
108
  try {
84
- const signedInAs = await (0, import_host.currentSignIn)();
109
+ const auth = await (0, import_host.loadAuth)();
85
110
  if (!isActiveRef.current) return;
86
- if (signedInAs !== null) {
87
- setExistingUser(signedInAs);
111
+ if (auth) {
112
+ setExistingUser(auth.email || auth.userId || "user");
88
113
  setStep("already-authenticated");
89
- setStatus("You are already signed in");
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 sign in again \u2022 Esc to close" : step === "error" ? "Enter to retry \u2022 Esc to close" : "Esc to cancel";
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: "Sign in to Dexto" }) }),
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
- "Signed in as ",
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
- signInNote && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { children: signInNote }) }),
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
- summary: string;
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;AAG/D,MAAM,MAAM,mBAAmB,GACzB;IAAE,OAAO,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC;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;AAID,QAAA,MAAM,YAAY,8FAiLhB,CAAC;AAEH,eAAe,YAAY,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"}