@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
@@ -8,12 +8,17 @@ import {
8
8
  useState
9
9
  } from "react";
10
10
  import { Box, Text } from "ink";
11
- import { currentSignIn, signIn } from "../../host/index.js";
11
+ import {
12
+ loadAuth,
13
+ performDeviceCodeLogin,
14
+ persistDeviceApiKeyLoginResult
15
+ } from "../../host/index.js";
12
16
  const LoginOverlay = forwardRef(function LoginOverlay2({ isVisible, onDone }, ref) {
13
17
  const [step, setStep] = useState("checking");
14
18
  const [existingUser, setExistingUser] = useState(null);
15
- const [signInNote, setSignInNote] = useState(null);
16
- const [status, setStatus] = useState("Preparing sign-in...");
19
+ const [authUrl, setAuthUrl] = useState(null);
20
+ const [devicePrompt, setDevicePrompt] = useState(null);
21
+ const [status, setStatus] = useState("Preparing login...");
17
22
  const [error, setError] = useState(null);
18
23
  const abortControllerRef = useRef(null);
19
24
  const isActiveRef = useRef(false);
@@ -25,24 +30,47 @@ const LoginOverlay = forwardRef(function LoginOverlay2({ isVisible, onDone }, re
25
30
  abortControllerRef.current?.abort(new Error("Authentication cancelled"));
26
31
  abortControllerRef.current = null;
27
32
  }, []);
33
+ const runDeviceLogin = useCallback(
34
+ async (abortController) => {
35
+ setStep("starting");
36
+ safeSetStatus("Starting device code login...");
37
+ setAuthUrl(null);
38
+ setDevicePrompt(null);
39
+ return performDeviceCodeLogin({
40
+ signal: abortController.signal,
41
+ onPrompt: (prompt) => {
42
+ if (!isActiveRef.current || abortController.signal.aborted) {
43
+ return;
44
+ }
45
+ setDevicePrompt(prompt);
46
+ setAuthUrl(prompt.verificationUrlComplete ?? prompt.verificationUrl);
47
+ setStep("waiting");
48
+ safeSetStatus("Open the URL below and approve login.");
49
+ }
50
+ });
51
+ },
52
+ [safeSetStatus]
53
+ );
28
54
  const startLogin = useCallback(async () => {
29
55
  cancelInFlight();
30
56
  setError(null);
31
- setSignInNote(null);
32
- setStep("signing-in");
33
- safeSetStatus("Signing in...");
57
+ setAuthUrl(null);
58
+ setDevicePrompt(null);
34
59
  const abortController = new AbortController();
35
60
  abortControllerRef.current = abortController;
36
61
  try {
37
- const summary = await signIn({
38
- signal: abortController.signal,
39
- onNote: (text) => {
40
- if (!isActiveRef.current || abortController.signal.aborted) return;
41
- setSignInNote(text);
42
- }
43
- });
62
+ const result = await runDeviceLogin(abortController);
63
+ if (!result || !isActiveRef.current || abortController.signal.aborted) return;
64
+ setStep("finalizing");
65
+ safeSetStatus("Finalizing login...");
66
+ const persisted = await persistDeviceApiKeyLoginResult(result);
44
67
  if (!isActiveRef.current || abortController.signal.aborted) return;
45
- onDone({ outcome: "success", summary });
68
+ onDone({
69
+ outcome: "success",
70
+ email: persisted.email,
71
+ keyId: persisted.keyId,
72
+ hasDextoApiKey: persisted.hasDextoApiKey
73
+ });
46
74
  } catch (err) {
47
75
  if (abortController.signal.aborted) {
48
76
  return;
@@ -50,27 +78,28 @@ const LoginOverlay = forwardRef(function LoginOverlay2({ isVisible, onDone }, re
50
78
  setStep("error");
51
79
  const errorMessage = err instanceof Error ? err.message : String(err);
52
80
  setError(errorMessage);
53
- safeSetStatus("Sign-in failed");
81
+ safeSetStatus("Login failed");
54
82
  } finally {
55
83
  abortControllerRef.current = null;
56
84
  }
57
- }, [cancelInFlight, onDone, safeSetStatus]);
85
+ }, [cancelInFlight, onDone, runDeviceLogin, safeSetStatus]);
58
86
  useEffect(() => {
59
87
  if (!isVisible) return;
60
88
  isActiveRef.current = true;
61
89
  setStep("checking");
62
90
  setExistingUser(null);
63
- setSignInNote(null);
64
- setStatus("Preparing sign-in...");
91
+ setAuthUrl(null);
92
+ setDevicePrompt(null);
93
+ setStatus("Preparing login...");
65
94
  setError(null);
66
95
  void (async () => {
67
96
  try {
68
- const signedInAs = await currentSignIn();
97
+ const auth = await loadAuth();
69
98
  if (!isActiveRef.current) return;
70
- if (signedInAs !== null) {
71
- setExistingUser(signedInAs);
99
+ if (auth) {
100
+ setExistingUser(auth.email || auth.userId || "user");
72
101
  setStep("already-authenticated");
73
- setStatus("You are already signed in");
102
+ setStatus("You are already logged in");
74
103
  return;
75
104
  }
76
105
  await startLogin();
@@ -110,7 +139,7 @@ const LoginOverlay = forwardRef(function LoginOverlay2({ isVisible, onDone }, re
110
139
  [cancelInFlight, isVisible, onDone, startLogin, step]
111
140
  );
112
141
  if (!isVisible) return null;
113
- 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";
142
+ 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";
114
143
  return /* @__PURE__ */ jsxs(
115
144
  Box,
116
145
  {
@@ -120,16 +149,27 @@ const LoginOverlay = forwardRef(function LoginOverlay2({ isVisible, onDone }, re
120
149
  paddingX: 1,
121
150
  marginTop: 1,
122
151
  children: [
123
- /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "Sign in to Dexto" }) }),
152
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "Login to Dexto" }) }),
124
153
  existingUser && step === "already-authenticated" && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: "green", children: [
125
- "Signed in as ",
154
+ "\u2705 Already logged in as: ",
126
155
  existingUser
127
156
  ] }) }),
128
157
  /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
129
158
  /* @__PURE__ */ jsx(Text, { color: "gray", children: "Status: " }),
130
159
  /* @__PURE__ */ jsx(Text, { children: status })
131
160
  ] }),
132
- signInNote && /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { children: signInNote }) }),
161
+ devicePrompt && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
162
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "Open this URL on any device:" }),
163
+ /* @__PURE__ */ jsx(Text, { color: "yellowBright", children: devicePrompt.verificationUrlComplete ?? devicePrompt.verificationUrl }),
164
+ /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
165
+ "Code: ",
166
+ devicePrompt.userCode
167
+ ] })
168
+ ] }),
169
+ authUrl && !devicePrompt && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
170
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "If your browser didn't open, use this URL:" }),
171
+ /* @__PURE__ */ jsx(Text, { color: "yellowBright", children: authUrl })
172
+ ] }),
133
173
  error && /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: "red", children: [
134
174
  "Error: ",
135
175
  error
@@ -30,12 +30,20 @@ const LogoutOverlay = (0, import_react.forwardRef)(function LogoutOverlay2({ isV
30
30
  const [userLabel, setUserLabel] = (0, import_react.useState)(null);
31
31
  const [usingDextoCredits, setUsingDextoCredits] = (0, import_react.useState)(false);
32
32
  const [error, setError] = (0, import_react.useState)(null);
33
+ const provisionedApiKeyRef = (0, import_react.useRef)(null);
33
34
  const performLogout = (0, import_react.useCallback)(async () => {
34
35
  setError(null);
35
36
  setStep("logging-out");
36
37
  try {
37
- const summary = await (0, import_host.signOut)();
38
- onDone({ outcome: "success", summary, wasUsingDextoCredits: usingDextoCredits });
38
+ const auth = await (0, import_host.loadAuth)();
39
+ if (auth?.dextoApiKey && auth.dextoApiKeySource === "provisioned") {
40
+ provisionedApiKeyRef.current = auth.dextoApiKey;
41
+ }
42
+ await (0, import_host.removeAuth)();
43
+ if (provisionedApiKeyRef.current) {
44
+ await (0, import_host.removeDextoApiKeyFromEnv)({ expectedValue: provisionedApiKeyRef.current });
45
+ }
46
+ onDone({ outcome: "success", wasUsingDextoCredits: usingDextoCredits });
39
47
  } catch (err) {
40
48
  setStep("error");
41
49
  setError(err instanceof Error ? err.message : String(err));
@@ -44,23 +52,24 @@ const LogoutOverlay = (0, import_react.forwardRef)(function LogoutOverlay2({ isV
44
52
  (0, import_react.useEffect)(() => {
45
53
  if (!isVisible) return;
46
54
  let cancelled = false;
55
+ provisionedApiKeyRef.current = null;
47
56
  setStep("checking");
48
57
  setUserLabel(null);
49
58
  setUsingDextoCredits(false);
50
59
  setError(null);
51
60
  void (async () => {
52
61
  try {
53
- const signedInAs = await (0, import_host.currentSignIn)();
62
+ const auth = await (0, import_host.loadAuth)();
54
63
  if (cancelled) return;
55
- if (signedInAs === null) {
64
+ if (!auth) {
56
65
  setStep("not-authenticated");
57
66
  return;
58
67
  }
59
- setUserLabel(signedInAs);
68
+ setUserLabel(auth.email || auth.userId || "user");
60
69
  } catch {
61
70
  if (!cancelled) {
62
71
  setStep("error");
63
- setError("Failed to read the sign-in");
72
+ setError("Failed to load authentication state");
64
73
  }
65
74
  return;
66
75
  }
@@ -108,12 +117,12 @@ const LogoutOverlay = (0, import_react.forwardRef)(function LogoutOverlay2({ isV
108
117
  marginTop: 1,
109
118
  children: [
110
119
  /* @__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: "Logout" }) }),
111
- step === "not-authenticated" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "yellow", children: "Not signed in" }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { marginBottom: 1, children: [
120
+ step === "not-authenticated" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "yellow", children: "\u2139\uFE0F Not currently logged in" }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { marginBottom: 1, children: [
112
121
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", children: "Account: " }),
113
122
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { children: userLabel ?? "..." })
114
123
  ] }),
115
124
  usingDextoCredits && step !== "not-authenticated" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { flexDirection: "column", marginBottom: 1, children: [
116
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "yellow", children: "\u26A0\uFE0F You are configured to use Dexto models, which run on this sign-in." }),
125
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "yellow", children: "\u26A0\uFE0F You are configured to use Dexto Nova credits (provider: Dexto Nova)." }),
117
126
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", dimColor: true, children: "After logout, you will need to run `/login` to authenticate again, or `dexto setup` to configure a different provider." })
118
127
  ] }),
119
128
  step === "logging-out" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", children: "Logging out..." }) }),
@@ -2,7 +2,6 @@ import React from 'react';
2
2
  import type { Key } from '../../hooks/useInputOrchestrator.js';
3
3
  export type LogoutOverlayOutcome = {
4
4
  outcome: 'success';
5
- summary: string;
6
5
  wasUsingDextoCredits: boolean;
7
6
  } | {
8
7
  outcome: 'cancelled';
@@ -1 +1 @@
1
- {"version":3,"file":"LogoutOverlay.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/LogoutOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4E,MAAM,OAAO,CAAC;AAEjG,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAG/D,MAAM,MAAM,oBAAoB,GAC1B;IAAE,OAAO,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,OAAO,CAAA;CAAE,GACtE;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,GACxB;IAAE,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC;AAE5B,MAAM,WAAW,kBAAkB;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,mBAAmB;IAChC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAID,QAAA,MAAM,aAAa,gGA4JjB,CAAC;AAEH,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"LogoutOverlay.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/LogoutOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAON,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAQ/D,MAAM,MAAM,oBAAoB,GAC1B;IAAE,OAAO,EAAE,SAAS,CAAC;IAAC,oBAAoB,EAAE,OAAO,CAAA;CAAE,GACrD;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,GACxB;IAAE,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC;AAE5B,MAAM,WAAW,kBAAkB;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,mBAAmB;IAChC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAID,QAAA,MAAM,aAAa,gGAwKjB,CAAC;AAEH,eAAe,aAAa,CAAC"}
@@ -1,18 +1,38 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from "react";
2
+ import {
3
+ forwardRef,
4
+ useCallback,
5
+ useEffect,
6
+ useImperativeHandle,
7
+ useRef,
8
+ useState
9
+ } from "react";
3
10
  import { Box, Text } from "ink";
4
- import { currentSignIn, isUsingDextoCredits, signOut } from "../../host/index.js";
11
+ import {
12
+ loadAuth,
13
+ removeAuth,
14
+ removeDextoApiKeyFromEnv,
15
+ isUsingDextoCredits
16
+ } from "../../host/index.js";
5
17
  const LogoutOverlay = forwardRef(function LogoutOverlay2({ isVisible, onDone }, ref) {
6
18
  const [step, setStep] = useState("checking");
7
19
  const [userLabel, setUserLabel] = useState(null);
8
20
  const [usingDextoCredits, setUsingDextoCredits] = useState(false);
9
21
  const [error, setError] = useState(null);
22
+ const provisionedApiKeyRef = useRef(null);
10
23
  const performLogout = useCallback(async () => {
11
24
  setError(null);
12
25
  setStep("logging-out");
13
26
  try {
14
- const summary = await signOut();
15
- onDone({ outcome: "success", summary, wasUsingDextoCredits: usingDextoCredits });
27
+ const auth = await loadAuth();
28
+ if (auth?.dextoApiKey && auth.dextoApiKeySource === "provisioned") {
29
+ provisionedApiKeyRef.current = auth.dextoApiKey;
30
+ }
31
+ await removeAuth();
32
+ if (provisionedApiKeyRef.current) {
33
+ await removeDextoApiKeyFromEnv({ expectedValue: provisionedApiKeyRef.current });
34
+ }
35
+ onDone({ outcome: "success", wasUsingDextoCredits: usingDextoCredits });
16
36
  } catch (err) {
17
37
  setStep("error");
18
38
  setError(err instanceof Error ? err.message : String(err));
@@ -21,23 +41,24 @@ const LogoutOverlay = forwardRef(function LogoutOverlay2({ isVisible, onDone },
21
41
  useEffect(() => {
22
42
  if (!isVisible) return;
23
43
  let cancelled = false;
44
+ provisionedApiKeyRef.current = null;
24
45
  setStep("checking");
25
46
  setUserLabel(null);
26
47
  setUsingDextoCredits(false);
27
48
  setError(null);
28
49
  void (async () => {
29
50
  try {
30
- const signedInAs = await currentSignIn();
51
+ const auth = await loadAuth();
31
52
  if (cancelled) return;
32
- if (signedInAs === null) {
53
+ if (!auth) {
33
54
  setStep("not-authenticated");
34
55
  return;
35
56
  }
36
- setUserLabel(signedInAs);
57
+ setUserLabel(auth.email || auth.userId || "user");
37
58
  } catch {
38
59
  if (!cancelled) {
39
60
  setStep("error");
40
- setError("Failed to read the sign-in");
61
+ setError("Failed to load authentication state");
41
62
  }
42
63
  return;
43
64
  }
@@ -85,12 +106,12 @@ const LogoutOverlay = forwardRef(function LogoutOverlay2({ isVisible, onDone },
85
106
  marginTop: 1,
86
107
  children: [
87
108
  /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "Logout" }) }),
88
- step === "not-authenticated" ? /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "Not signed in" }) }) : /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
109
+ step === "not-authenticated" ? /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "\u2139\uFE0F Not currently logged in" }) }) : /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
89
110
  /* @__PURE__ */ jsx(Text, { color: "gray", children: "Account: " }),
90
111
  /* @__PURE__ */ jsx(Text, { children: userLabel ?? "..." })
91
112
  ] }),
92
113
  usingDextoCredits && step !== "not-authenticated" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
93
- /* @__PURE__ */ jsx(Text, { color: "yellow", children: "\u26A0\uFE0F You are configured to use Dexto models, which run on this sign-in." }),
114
+ /* @__PURE__ */ jsx(Text, { color: "yellow", children: "\u26A0\uFE0F You are configured to use Dexto Nova credits (provider: Dexto Nova)." }),
94
115
  /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: "After logout, you will need to run `/login` to authenticate again, or `dexto setup` to configure a different provider." })
95
116
  ] }),
96
117
  step === "logging-out" && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: "gray", children: "Logging out..." }) }),
@@ -446,7 +446,7 @@ const PROVIDER_CONFIGS = {
446
446
  }
447
447
  },
448
448
  "dexto-nova": {
449
- displayName: "Dexto",
449
+ displayName: "Dexto Nova",
450
450
  description: "Access 100+ models with Nova credits",
451
451
  steps: [
452
452
  {
@@ -493,14 +493,14 @@ const PROVIDER_CONFIGS = {
493
493
  }
494
494
  }
495
495
  if (status === "invalid") {
496
- return `Model '${modelId}' not found. Dexto uses OpenRouter model IDs - check https://openrouter.ai/models`;
496
+ return `Model '${modelId}' not found. Dexto Nova uses OpenRouter model IDs - check https://openrouter.ai/models`;
497
497
  }
498
498
  return null;
499
499
  }
500
500
  },
501
501
  setupInfo: {
502
- title: "Dexto Models",
503
- description: "Add OpenRouter-format models that use your Dexto credits. Requires a sign-in: run dexto login --scope cloud:write first.",
502
+ title: "Dexto Nova Setup",
503
+ description: "Add OpenRouter-format models that use your Dexto Nova credits. Requires login: run `/login` first.",
504
504
  docsUrl: "https://openrouter.ai/models"
505
505
  }
506
506
  }
@@ -411,7 +411,7 @@ const PROVIDER_CONFIGS = {
411
411
  }
412
412
  },
413
413
  "dexto-nova": {
414
- displayName: "Dexto",
414
+ displayName: "Dexto Nova",
415
415
  description: "Access 100+ models with Nova credits",
416
416
  steps: [
417
417
  {
@@ -458,14 +458,14 @@ const PROVIDER_CONFIGS = {
458
458
  }
459
459
  }
460
460
  if (status === "invalid") {
461
- return `Model '${modelId}' not found. Dexto uses OpenRouter model IDs - check https://openrouter.ai/models`;
461
+ return `Model '${modelId}' not found. Dexto Nova uses OpenRouter model IDs - check https://openrouter.ai/models`;
462
462
  }
463
463
  return null;
464
464
  }
465
465
  },
466
466
  setupInfo: {
467
- title: "Dexto Models",
468
- description: "Add OpenRouter-format models that use your Dexto credits. Requires a sign-in: run dexto login --scope cloud:write first.",
467
+ title: "Dexto Nova Setup",
468
+ description: "Add OpenRouter-format models that use your Dexto Nova credits. Requires login: run `/login` first.",
469
469
  docsUrl: "https://openrouter.ai/models"
470
470
  }
471
471
  }
@@ -85,6 +85,7 @@ var import_idGenerator = require("../utils/idGenerator.js");
85
85
  var import_host = require("../host/index.js");
86
86
  var import_FocusOverlayFrame = require("../components/shared/FocusOverlayFrame.js");
87
87
  var import_overlayPresentation = require("../utils/overlayPresentation.js");
88
+ var import_dexto_auth_refresh = require("../utils/dexto-auth-refresh.js");
88
89
  function isLLMProvider(value) {
89
90
  if (typeof value !== "string") return false;
90
91
  for (const provider of import_llm.LLM_PROVIDERS) {
@@ -385,7 +386,7 @@ const OverlayContainer = (0, import_react.forwardRef)(
385
386
  {
386
387
  id: (0, import_idGenerator.generateMessageId)("system"),
387
388
  role: "system",
388
- content: "Dexto models need a sign-in that may spend credits. Run dexto login --scope cloud:write, or pick another model.",
389
+ content: "Cannot switch to Dexto Nova model - authentication required. Run /login to authenticate.",
389
390
  timestamp: /* @__PURE__ */ new Date()
390
391
  }
391
392
  ]);
@@ -397,7 +398,7 @@ const OverlayContainer = (0, import_react.forwardRef)(
397
398
  {
398
399
  id: (0, import_idGenerator.generateMessageId)("error"),
399
400
  role: "system",
400
- content: `Failed to check the Dexto sign-in: ${error instanceof Error ? error.message : String(error)}`,
401
+ content: `Failed to verify Dexto Nova auth: ${error instanceof Error ? error.message : String(error)}`,
401
402
  timestamp: /* @__PURE__ */ new Date()
402
403
  }
403
404
  ]);
@@ -660,13 +661,6 @@ const OverlayContainer = (0, import_react.forwardRef)(
660
661
  insufficientCredits: null
661
662
  }));
662
663
  }, [setUi]);
663
- const handleInsufficientCreditsChooseOwnModel = (0, import_react.useCallback)(() => {
664
- setUi((prev) => ({
665
- ...prev,
666
- activeOverlay: "model-selector",
667
- insufficientCredits: null
668
- }));
669
- }, [setUi]);
670
664
  const handleInsufficientCreditsResolved = (0, import_react.useCallback)(() => {
671
665
  setUi((prev) => ({
672
666
  ...prev,
@@ -1136,33 +1130,50 @@ ${result.context}`,
1136
1130
  }, [setUi]);
1137
1131
  const handleLoginDone = (0, import_react.useCallback)(
1138
1132
  (outcome) => {
1139
- handleClose();
1140
- if (outcome.outcome === "closed") {
1141
- return;
1142
- }
1143
- if (outcome.outcome === "cancelled") {
1133
+ void (async () => {
1134
+ handleClose();
1135
+ if (outcome.outcome === "closed") {
1136
+ return;
1137
+ }
1138
+ if (outcome.outcome === "cancelled") {
1139
+ setMessages((prev) => [
1140
+ ...prev,
1141
+ {
1142
+ id: (0, import_idGenerator.generateMessageId)("system"),
1143
+ role: "system",
1144
+ content: "Login cancelled.",
1145
+ timestamp: /* @__PURE__ */ new Date()
1146
+ }
1147
+ ]);
1148
+ return;
1149
+ }
1150
+ let refreshWarning = null;
1151
+ if (outcome.hasDextoApiKey) {
1152
+ try {
1153
+ await (0, import_dexto_auth_refresh.refreshDextoNovaAuthAfterLogin)(agent, session.id || void 0);
1154
+ } catch (error) {
1155
+ refreshWarning = `Logged in, but failed to refresh the active Dexto Nova session: ${error instanceof Error ? error.message : String(error)}`;
1156
+ }
1157
+ }
1158
+ const userLabel = outcome.email ? ` as ${outcome.email}` : "";
1159
+ const keyLine = outcome.hasDextoApiKey ? `\u{1F511} DEXTO_API_KEY ready${outcome.keyId ? ` (Key ID: ${outcome.keyId})` : ""}` : "\u26A0\uFE0F Failed to provision DEXTO_API_KEY (you can still use your own API keys)";
1160
+ const content = [
1161
+ `\u2705 Logged in${userLabel}`,
1162
+ keyLine,
1163
+ ...refreshWarning ? [`\u26A0\uFE0F ${refreshWarning}`] : []
1164
+ ].join("\n");
1144
1165
  setMessages((prev) => [
1145
1166
  ...prev,
1146
1167
  {
1147
1168
  id: (0, import_idGenerator.generateMessageId)("system"),
1148
1169
  role: "system",
1149
- content: "Sign-in cancelled.",
1170
+ content,
1150
1171
  timestamp: /* @__PURE__ */ new Date()
1151
1172
  }
1152
1173
  ]);
1153
- return;
1154
- }
1155
- setMessages((prev) => [
1156
- ...prev,
1157
- {
1158
- id: (0, import_idGenerator.generateMessageId)("system"),
1159
- role: "system",
1160
- content: outcome.summary,
1161
- timestamp: /* @__PURE__ */ new Date()
1162
- }
1163
- ]);
1174
+ })();
1164
1175
  },
1165
- [handleClose, setMessages]
1176
+ [agent, handleClose, session.id, setMessages]
1166
1177
  );
1167
1178
  const handleLogoutDone = (0, import_react.useCallback)(
1168
1179
  (outcome) => {
@@ -1188,7 +1199,7 @@ ${result.context}`,
1188
1199
  {
1189
1200
  id: (0, import_idGenerator.generateMessageId)("system"),
1190
1201
  role: "system",
1191
- content: `${outcome.summary}${warning}`,
1202
+ content: `\u2705 Successfully logged out${warning}`,
1192
1203
  timestamp: /* @__PURE__ */ new Date()
1193
1204
  }
1194
1205
  ]);
@@ -2485,8 +2496,7 @@ Use /${data.name} to run it.`,
2485
2496
  isVisible: true,
2486
2497
  initialBalanceUsd: ui.insufficientCredits.balanceUsd,
2487
2498
  onResolved: handleInsufficientCreditsResolved,
2488
- onClose: handleInsufficientCreditsClose,
2489
- onChooseOwnModel: handleInsufficientCreditsChooseOwnModel
2499
+ onClose: handleInsufficientCreditsClose
2490
2500
  }
2491
2501
  ) }),
2492
2502
  ui.activeOverlay === "login" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1 +1 @@
1
- {"version":3,"file":"OverlayContainer.d.ts","sourceRoot":"","sources":["../../src/containers/OverlayContainer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAyE,MAAM,OAAO,CAAC;AAI9F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAE5D,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAGH,KAAK,eAAe,EACvB,MAAM,iCAAiC,CAAC;AAuGzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAuC3D,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAsC3D,MAAM,WAAW,sBAAsB;IACnC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,UAAU,qBAAqB;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3D,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAC1E,KAAK,EAAE,eAAe,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,mDAAmD;IACnD,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,4EAA4E;IAC5E,qBAAqB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,sGA++F5B,CAAC"}
1
+ {"version":3,"file":"OverlayContainer.d.ts","sourceRoot":"","sources":["../../src/containers/OverlayContainer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAyE,MAAM,OAAO,CAAC;AAI9F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAE5D,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAGH,KAAK,eAAe,EACvB,MAAM,iCAAiC,CAAC;AAuGzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAuC3D,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAuC3D,MAAM,WAAW,sBAAsB;IACnC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,UAAU,qBAAqB;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3D,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAC1E,KAAK,EAAE,eAAe,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,mDAAmD;IACnD,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,4EAA4E;IAC5E,qBAAqB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,sGA6/F5B,CAAC"}
@@ -67,6 +67,7 @@ import { generateMessageId } from "../utils/idGenerator.js";
67
67
  import { canUseDextoProvider, captureAnalytics } from "../host/index.js";
68
68
  import { FocusOverlayFrame } from "../components/shared/FocusOverlayFrame.js";
69
69
  import { shouldHideCliChrome } from "../utils/overlayPresentation.js";
70
+ import { refreshDextoNovaAuthAfterLogin } from "../utils/dexto-auth-refresh.js";
70
71
  function isLLMProvider(value) {
71
72
  if (typeof value !== "string") return false;
72
73
  for (const provider of LLM_PROVIDERS) {
@@ -367,7 +368,7 @@ const OverlayContainer = forwardRef(
367
368
  {
368
369
  id: generateMessageId("system"),
369
370
  role: "system",
370
- content: "Dexto models need a sign-in that may spend credits. Run dexto login --scope cloud:write, or pick another model.",
371
+ content: "Cannot switch to Dexto Nova model - authentication required. Run /login to authenticate.",
371
372
  timestamp: /* @__PURE__ */ new Date()
372
373
  }
373
374
  ]);
@@ -379,7 +380,7 @@ const OverlayContainer = forwardRef(
379
380
  {
380
381
  id: generateMessageId("error"),
381
382
  role: "system",
382
- content: `Failed to check the Dexto sign-in: ${error instanceof Error ? error.message : String(error)}`,
383
+ content: `Failed to verify Dexto Nova auth: ${error instanceof Error ? error.message : String(error)}`,
383
384
  timestamp: /* @__PURE__ */ new Date()
384
385
  }
385
386
  ]);
@@ -642,13 +643,6 @@ const OverlayContainer = forwardRef(
642
643
  insufficientCredits: null
643
644
  }));
644
645
  }, [setUi]);
645
- const handleInsufficientCreditsChooseOwnModel = useCallback(() => {
646
- setUi((prev) => ({
647
- ...prev,
648
- activeOverlay: "model-selector",
649
- insufficientCredits: null
650
- }));
651
- }, [setUi]);
652
646
  const handleInsufficientCreditsResolved = useCallback(() => {
653
647
  setUi((prev) => ({
654
648
  ...prev,
@@ -1118,33 +1112,50 @@ ${result.context}`,
1118
1112
  }, [setUi]);
1119
1113
  const handleLoginDone = useCallback(
1120
1114
  (outcome) => {
1121
- handleClose();
1122
- if (outcome.outcome === "closed") {
1123
- return;
1124
- }
1125
- if (outcome.outcome === "cancelled") {
1115
+ void (async () => {
1116
+ handleClose();
1117
+ if (outcome.outcome === "closed") {
1118
+ return;
1119
+ }
1120
+ if (outcome.outcome === "cancelled") {
1121
+ setMessages((prev) => [
1122
+ ...prev,
1123
+ {
1124
+ id: generateMessageId("system"),
1125
+ role: "system",
1126
+ content: "Login cancelled.",
1127
+ timestamp: /* @__PURE__ */ new Date()
1128
+ }
1129
+ ]);
1130
+ return;
1131
+ }
1132
+ let refreshWarning = null;
1133
+ if (outcome.hasDextoApiKey) {
1134
+ try {
1135
+ await refreshDextoNovaAuthAfterLogin(agent, session.id || void 0);
1136
+ } catch (error) {
1137
+ refreshWarning = `Logged in, but failed to refresh the active Dexto Nova session: ${error instanceof Error ? error.message : String(error)}`;
1138
+ }
1139
+ }
1140
+ const userLabel = outcome.email ? ` as ${outcome.email}` : "";
1141
+ const keyLine = outcome.hasDextoApiKey ? `\u{1F511} DEXTO_API_KEY ready${outcome.keyId ? ` (Key ID: ${outcome.keyId})` : ""}` : "\u26A0\uFE0F Failed to provision DEXTO_API_KEY (you can still use your own API keys)";
1142
+ const content = [
1143
+ `\u2705 Logged in${userLabel}`,
1144
+ keyLine,
1145
+ ...refreshWarning ? [`\u26A0\uFE0F ${refreshWarning}`] : []
1146
+ ].join("\n");
1126
1147
  setMessages((prev) => [
1127
1148
  ...prev,
1128
1149
  {
1129
1150
  id: generateMessageId("system"),
1130
1151
  role: "system",
1131
- content: "Sign-in cancelled.",
1152
+ content,
1132
1153
  timestamp: /* @__PURE__ */ new Date()
1133
1154
  }
1134
1155
  ]);
1135
- return;
1136
- }
1137
- setMessages((prev) => [
1138
- ...prev,
1139
- {
1140
- id: generateMessageId("system"),
1141
- role: "system",
1142
- content: outcome.summary,
1143
- timestamp: /* @__PURE__ */ new Date()
1144
- }
1145
- ]);
1156
+ })();
1146
1157
  },
1147
- [handleClose, setMessages]
1158
+ [agent, handleClose, session.id, setMessages]
1148
1159
  );
1149
1160
  const handleLogoutDone = useCallback(
1150
1161
  (outcome) => {
@@ -1170,7 +1181,7 @@ ${result.context}`,
1170
1181
  {
1171
1182
  id: generateMessageId("system"),
1172
1183
  role: "system",
1173
- content: `${outcome.summary}${warning}`,
1184
+ content: `\u2705 Successfully logged out${warning}`,
1174
1185
  timestamp: /* @__PURE__ */ new Date()
1175
1186
  }
1176
1187
  ]);
@@ -2467,8 +2478,7 @@ Use /${data.name} to run it.`,
2467
2478
  isVisible: true,
2468
2479
  initialBalanceUsd: ui.insufficientCredits.balanceUsd,
2469
2480
  onResolved: handleInsufficientCreditsResolved,
2470
- onClose: handleInsufficientCreditsClose,
2471
- onChooseOwnModel: handleInsufficientCreditsChooseOwnModel
2481
+ onClose: handleInsufficientCreditsClose
2472
2482
  }
2473
2483
  ) }),
2474
2484
  ui.activeOverlay === "login" && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(