visionos-monorepo 0.1.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 (160) hide show
  1. package/.claude/worktrees/competent-burnell-8d1330/README.md +138 -0
  2. package/.claude/worktrees/competent-burnell-8d1330/cli/package.json +35 -0
  3. package/.claude/worktrees/competent-burnell-8d1330/cli/scripts/copy-web-assets.mjs +12 -0
  4. package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/logout.ts +12 -0
  5. package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/open.ts +19 -0
  6. package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/start.ts +97 -0
  7. package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/status.ts +23 -0
  8. package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/userinfo.ts +47 -0
  9. package/.claude/worktrees/competent-burnell-8d1330/cli/src/index.ts +23 -0
  10. package/.claude/worktrees/competent-burnell-8d1330/cli/src/lib/auth.ts +84 -0
  11. package/.claude/worktrees/competent-burnell-8d1330/cli/src/lib/browser.ts +37 -0
  12. package/.claude/worktrees/competent-burnell-8d1330/cli/src/lib/localState.ts +80 -0
  13. package/.claude/worktrees/competent-burnell-8d1330/cli/src/lib/runtime.ts +203 -0
  14. package/.claude/worktrees/competent-burnell-8d1330/cli/src/runtime/index.ts +36 -0
  15. package/.claude/worktrees/competent-burnell-8d1330/cli/src/types/inquirer.d.ts +9 -0
  16. package/.claude/worktrees/competent-burnell-8d1330/cli/tsconfig.json +19 -0
  17. package/.claude/worktrees/competent-burnell-8d1330/client/index.html +15 -0
  18. package/.claude/worktrees/competent-burnell-8d1330/client/package.json +27 -0
  19. package/.claude/worktrees/competent-burnell-8d1330/client/postcss.config.cjs +7 -0
  20. package/.claude/worktrees/competent-burnell-8d1330/client/src/App.tsx +57 -0
  21. package/.claude/worktrees/competent-burnell-8d1330/client/src/components/CliAuthPage.tsx +385 -0
  22. package/.claude/worktrees/competent-burnell-8d1330/client/src/components/ManifestoPage.tsx +946 -0
  23. package/.claude/worktrees/competent-burnell-8d1330/client/src/components/TrackCard.tsx +19 -0
  24. package/.claude/worktrees/competent-burnell-8d1330/client/src/lib/api.ts +58 -0
  25. package/.claude/worktrees/competent-burnell-8d1330/client/src/main.tsx +11 -0
  26. package/.claude/worktrees/competent-burnell-8d1330/client/src/styles/index.css +33 -0
  27. package/.claude/worktrees/competent-burnell-8d1330/client/src/styles/manifesto.css +1398 -0
  28. package/.claude/worktrees/competent-burnell-8d1330/client/tailwind.config.ts +36 -0
  29. package/.claude/worktrees/competent-burnell-8d1330/client/tsconfig.json +25 -0
  30. package/.claude/worktrees/competent-burnell-8d1330/client/vite.config.ts +20 -0
  31. package/.claude/worktrees/competent-burnell-8d1330/package-lock.json +5278 -0
  32. package/.claude/worktrees/competent-burnell-8d1330/package.json +24 -0
  33. package/.claude/worktrees/competent-burnell-8d1330/server/package.json +25 -0
  34. package/.claude/worktrees/competent-burnell-8d1330/server/src/app.ts +71 -0
  35. package/.claude/worktrees/competent-burnell-8d1330/server/src/config/env.ts +14 -0
  36. package/.claude/worktrees/competent-burnell-8d1330/server/src/features/auth/sessionStore.ts +74 -0
  37. package/.claude/worktrees/competent-burnell-8d1330/server/src/index.ts +8 -0
  38. package/.claude/worktrees/competent-burnell-8d1330/server/src/routes/auth.ts +112 -0
  39. package/.claude/worktrees/competent-burnell-8d1330/server/src/routes/health.ts +14 -0
  40. package/.claude/worktrees/competent-burnell-8d1330/server/tsconfig.json +19 -0
  41. package/.claude/worktrees/competent-burnell-8d1330/shared/package.json +24 -0
  42. package/.claude/worktrees/competent-burnell-8d1330/shared/src/index.ts +91 -0
  43. package/.claude/worktrees/competent-burnell-8d1330/shared/tsconfig.json +16 -0
  44. package/.claude/worktrees/competent-burnell-8d1330/tsconfig.base.json +12 -0
  45. package/.claude/worktrees/competent-burnell-8d1330/visionos-manifesto/index.html +392 -0
  46. package/.claude/worktrees/competent-burnell-8d1330/visionos-manifesto/script.js +146 -0
  47. package/.claude/worktrees/competent-burnell-8d1330/visionos-manifesto/styles.css +1082 -0
  48. package/.claude/worktrees/vigilant-napier-0de76f/README.md +138 -0
  49. package/.claude/worktrees/vigilant-napier-0de76f/cli/package.json +35 -0
  50. package/.claude/worktrees/vigilant-napier-0de76f/cli/scripts/copy-web-assets.mjs +12 -0
  51. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/logout.ts +12 -0
  52. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/open.ts +19 -0
  53. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/start.ts +97 -0
  54. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/status.ts +23 -0
  55. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/userinfo.ts +47 -0
  56. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/index.ts +23 -0
  57. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/lib/auth.ts +84 -0
  58. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/lib/browser.ts +37 -0
  59. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/lib/localState.ts +80 -0
  60. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/lib/runtime.ts +203 -0
  61. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/runtime/index.ts +36 -0
  62. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/types/inquirer.d.ts +9 -0
  63. package/.claude/worktrees/vigilant-napier-0de76f/cli/tsconfig.json +19 -0
  64. package/.claude/worktrees/vigilant-napier-0de76f/client/index.html +15 -0
  65. package/.claude/worktrees/vigilant-napier-0de76f/client/package.json +27 -0
  66. package/.claude/worktrees/vigilant-napier-0de76f/client/postcss.config.cjs +7 -0
  67. package/.claude/worktrees/vigilant-napier-0de76f/client/src/App.tsx +57 -0
  68. package/.claude/worktrees/vigilant-napier-0de76f/client/src/components/CliAuthPage.tsx +385 -0
  69. package/.claude/worktrees/vigilant-napier-0de76f/client/src/components/ManifestoPage.tsx +946 -0
  70. package/.claude/worktrees/vigilant-napier-0de76f/client/src/components/TrackCard.tsx +19 -0
  71. package/.claude/worktrees/vigilant-napier-0de76f/client/src/lib/api.ts +58 -0
  72. package/.claude/worktrees/vigilant-napier-0de76f/client/src/main.tsx +11 -0
  73. package/.claude/worktrees/vigilant-napier-0de76f/client/src/styles/index.css +33 -0
  74. package/.claude/worktrees/vigilant-napier-0de76f/client/src/styles/manifesto.css +1398 -0
  75. package/.claude/worktrees/vigilant-napier-0de76f/client/tailwind.config.ts +36 -0
  76. package/.claude/worktrees/vigilant-napier-0de76f/client/tsconfig.json +25 -0
  77. package/.claude/worktrees/vigilant-napier-0de76f/client/vite.config.ts +20 -0
  78. package/.claude/worktrees/vigilant-napier-0de76f/package-lock.json +5278 -0
  79. package/.claude/worktrees/vigilant-napier-0de76f/package.json +24 -0
  80. package/.claude/worktrees/vigilant-napier-0de76f/server/package.json +25 -0
  81. package/.claude/worktrees/vigilant-napier-0de76f/server/src/app.ts +71 -0
  82. package/.claude/worktrees/vigilant-napier-0de76f/server/src/config/env.ts +14 -0
  83. package/.claude/worktrees/vigilant-napier-0de76f/server/src/features/auth/sessionStore.ts +74 -0
  84. package/.claude/worktrees/vigilant-napier-0de76f/server/src/index.ts +8 -0
  85. package/.claude/worktrees/vigilant-napier-0de76f/server/src/routes/auth.ts +112 -0
  86. package/.claude/worktrees/vigilant-napier-0de76f/server/src/routes/health.ts +14 -0
  87. package/.claude/worktrees/vigilant-napier-0de76f/server/tsconfig.json +19 -0
  88. package/.claude/worktrees/vigilant-napier-0de76f/shared/package.json +24 -0
  89. package/.claude/worktrees/vigilant-napier-0de76f/shared/src/index.ts +91 -0
  90. package/.claude/worktrees/vigilant-napier-0de76f/shared/tsconfig.json +16 -0
  91. package/.claude/worktrees/vigilant-napier-0de76f/tsconfig.base.json +12 -0
  92. package/.claude/worktrees/vigilant-napier-0de76f/visionos-manifesto/index.html +392 -0
  93. package/.claude/worktrees/vigilant-napier-0de76f/visionos-manifesto/script.js +146 -0
  94. package/.claude/worktrees/vigilant-napier-0de76f/visionos-manifesto/styles.css +1082 -0
  95. package/.github/workflows/publish.yml +30 -0
  96. package/README.md +175 -0
  97. package/cli/README.md +165 -0
  98. package/cli/package.json +36 -0
  99. package/cli/scripts/copy-web-assets.mjs +12 -0
  100. package/cli/src/commands/lessons.ts +68 -0
  101. package/cli/src/commands/login.ts +46 -0
  102. package/cli/src/commands/logout.ts +12 -0
  103. package/cli/src/commands/open.ts +29 -0
  104. package/cli/src/commands/start.ts +146 -0
  105. package/cli/src/commands/status.ts +28 -0
  106. package/cli/src/commands/userinfo.ts +59 -0
  107. package/cli/src/index.ts +109 -0
  108. package/cli/src/lib/auth.ts +84 -0
  109. package/cli/src/lib/browser.ts +37 -0
  110. package/cli/src/lib/content.ts +57 -0
  111. package/cli/src/lib/lessonPrinter.ts +38 -0
  112. package/cli/src/lib/lessonRunner.ts +381 -0
  113. package/cli/src/lib/localState.ts +114 -0
  114. package/cli/src/lib/loginFlow.ts +74 -0
  115. package/cli/src/lib/progress.ts +94 -0
  116. package/cli/src/lib/runtime.ts +220 -0
  117. package/cli/src/lib/validator.ts +401 -0
  118. package/cli/src/runtime/index.ts +108 -0
  119. package/cli/src/types/inquirer.d.ts +9 -0
  120. package/cli/tsconfig.json +19 -0
  121. package/client/index.html +15 -0
  122. package/client/package.json +27 -0
  123. package/client/postcss.config.cjs +7 -0
  124. package/client/src/App.tsx +102 -0
  125. package/client/src/components/AccountPage.tsx +79 -0
  126. package/client/src/components/AuthPanel.tsx +312 -0
  127. package/client/src/components/CliAuthPage.tsx +367 -0
  128. package/client/src/components/CreatorPortal.tsx +885 -0
  129. package/client/src/components/ErrorBoundary.tsx +92 -0
  130. package/client/src/components/ManifestoPage.tsx +1126 -0
  131. package/client/src/components/TrackCard.tsx +19 -0
  132. package/client/src/lib/api.ts +215 -0
  133. package/client/src/main.tsx +14 -0
  134. package/client/src/styles/index.css +33 -0
  135. package/client/src/styles/manifesto.css +1828 -0
  136. package/client/tailwind.config.ts +36 -0
  137. package/client/tsconfig.json +25 -0
  138. package/client/vercel.json +8 -0
  139. package/client/vite.config.ts +33 -0
  140. package/package.json +27 -0
  141. package/server/package.json +26 -0
  142. package/server/src/app.ts +132 -0
  143. package/server/src/config/env.ts +135 -0
  144. package/server/src/features/accounts/accountStore.ts +359 -0
  145. package/server/src/features/accounts/contentStore.ts +264 -0
  146. package/server/src/features/accounts/password.ts +26 -0
  147. package/server/src/features/auth/sessionStore.ts +79 -0
  148. package/server/src/index.ts +8 -0
  149. package/server/src/routes/auth.ts +328 -0
  150. package/server/src/routes/content.ts +174 -0
  151. package/server/src/routes/health.ts +14 -0
  152. package/server/src/routes/progress.ts +105 -0
  153. package/server/tsconfig.json +19 -0
  154. package/shared/package.json +24 -0
  155. package/shared/src/index.ts +455 -0
  156. package/shared/tsconfig.json +16 -0
  157. package/tsconfig.base.json +12 -0
  158. package/visionos-manifesto/index.html +392 -0
  159. package/visionos-manifesto/script.js +146 -0
  160. package/visionos-manifesto/styles.css +1082 -0
@@ -0,0 +1,367 @@
1
+ import { useEffect, useState } from "react";
2
+ import {
3
+ LEARNING_TRACKS,
4
+ type CliAuthSession,
5
+ type CliAuthSessionStatus,
6
+ type VisionOsUserProfile
7
+ } from "../../../shared/src/index.js";
8
+ import {
9
+ VisionOsApiError,
10
+ getCliAuthSession,
11
+ getCurrentUser,
12
+ confirmCliAuthSession,
13
+ getRuntimeInfo
14
+ } from "../lib/api.js";
15
+ import { AuthPanel } from "./AuthPanel.js";
16
+
17
+ interface CliAuthPageProps {
18
+ sessionId: string;
19
+ }
20
+
21
+ function formatSessionTime(value: string) {
22
+ return new Date(value).toLocaleString(undefined, {
23
+ dateStyle: "medium",
24
+ timeStyle: "short"
25
+ });
26
+ }
27
+
28
+ function getStatusCopy(status: CliAuthSessionStatus) {
29
+ if (status === "authenticated") return "Authenticated";
30
+ if (status === "expired") return "Expired";
31
+ return "Awaiting confirmation";
32
+ }
33
+
34
+ export function CliAuthPage({ sessionId }: CliAuthPageProps) {
35
+ const [session, setSession] = useState<CliAuthSession | null>(null);
36
+ const [currentUser, setCurrentUser] = useState<VisionOsUserProfile | null>(null);
37
+ const [mainAppUrl, setMainAppUrl] = useState<string | null>(null);
38
+ const [loading, setLoading] = useState(true);
39
+ const [errorMessage, setErrorMessage] = useState<string | null>(null);
40
+
41
+ useEffect(() => {
42
+ let cancelled = false;
43
+
44
+ async function loadData() {
45
+ setLoading(true);
46
+ setErrorMessage(null);
47
+
48
+ try {
49
+ const [sessionPayload, userPayload, runtimePayload] = await Promise.all([
50
+ getCliAuthSession(sessionId),
51
+ getCurrentUser().catch(() => ({ user: null })),
52
+ getRuntimeInfo().catch(() => null)
53
+ ]);
54
+
55
+ if (cancelled) return;
56
+
57
+ setSession(sessionPayload.session);
58
+ setCurrentUser(userPayload.user);
59
+ if (runtimePayload?.runtime?.mainAppUrl) {
60
+ setMainAppUrl(runtimePayload.runtime.mainAppUrl);
61
+ }
62
+ } catch (error) {
63
+ if (cancelled) return;
64
+
65
+ if (error instanceof VisionOsApiError) {
66
+ setErrorMessage(error.message);
67
+ } else if (error instanceof Error) {
68
+ setErrorMessage(error.message);
69
+ } else {
70
+ setErrorMessage("Unable to load CLI authentication details.");
71
+ }
72
+ } finally {
73
+ if (!cancelled) setLoading(false);
74
+ }
75
+ }
76
+
77
+ void loadData();
78
+
79
+ return () => {
80
+ cancelled = true;
81
+ };
82
+ }, [sessionId]);
83
+
84
+ useEffect(() => {
85
+ if (session?.status === "authenticated") {
86
+ const timer = setTimeout(() => {
87
+ const targetUrl = mainAppUrl
88
+ ? `${mainAppUrl.replace(/\/$/, "")}/?token=${session.accountToken}`
89
+ : `/?token=${session.accountToken}`;
90
+ window.location.href = targetUrl;
91
+ }, 2000);
92
+ return () => clearTimeout(timer);
93
+ }
94
+ }, [session?.status, session?.accountToken, mainAppUrl]);
95
+
96
+ const track = session
97
+ ? LEARNING_TRACKS.find((learningTrack) => learningTrack.id === session.trackId)
98
+ : undefined;
99
+
100
+ return (
101
+ <div className="monitor-bezel">
102
+ <div className="monitor-screen" style={{ display: "flex", flexDirection: "column", minHeight: "calc(100vh - 48px)" }}>
103
+ {/* ── Header ─────────────────────── */}
104
+ <header className="m-site-header">
105
+ <div className="header-left">
106
+ <span className="header-brand">VisionOS</span>
107
+ <span className="header-meta">CLI Browser Handoff</span>
108
+ </div>
109
+ <nav className="header-nav">
110
+ <a href="/">Home <span className="arrow">↗</span></a>
111
+ </nav>
112
+ </header>
113
+
114
+ {/* ── Main Content ───────────────── */}
115
+ <main style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: "40px 60px" }}>
116
+ <div style={{ display: "grid", gridTemplateColumns: "1.1fr 0.9fr", gap: 48, width: "100%", maxWidth: 1100, alignItems: "center" }}>
117
+ {/* Left Column */}
118
+ <div>
119
+ <p className="text-label" style={{ fontFamily: "var(--font-mono)" }}>
120
+ ← cli handoff
121
+ </p>
122
+ <h1 className="text-section-title" style={{ marginTop: 12 }}>
123
+ Finish sign-in<br />
124
+ <span style={{ fontWeight: 400, color: "var(--ink-muted)" }}>for your terminal.</span>
125
+ </h1>
126
+ <p className="m-text-body" style={{ marginTop: 20 }}>
127
+ This browser step confirms your identity for the waiting CLI session. Once complete, return to your terminal.
128
+ </p>
129
+
130
+ {/* Session info cards */}
131
+ <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginTop: 32 }}>
132
+ <div style={{ padding: "20px 16px", background: "var(--bg-beige-light)", borderRadius: "var(--radius-lg)", border: "1px solid rgba(0,0,0,0.04)" }}>
133
+ <span className="text-label">Session ID</span>
134
+ <p style={{ marginTop: 8, fontFamily: "var(--font-mono)", fontSize: "0.72rem", color: "var(--ink-soft)", wordBreak: "break-all" }}>
135
+ {sessionId}
136
+ </p>
137
+ </div>
138
+ <div style={{ padding: "20px 16px", background: "var(--bg-beige-light)", borderRadius: "var(--radius-lg)", border: "1px solid rgba(0,0,0,0.04)" }}>
139
+ <span className="text-label">Learning Track</span>
140
+ <p style={{ marginTop: 8, fontSize: "1.05rem", fontWeight: 700, color: "var(--m-ink)" }}>
141
+ {track?.label ?? session?.trackId ?? "Loading…"}
142
+ </p>
143
+ {track && (
144
+ <p style={{ marginTop: 6, fontSize: "0.78rem", color: "var(--ink-muted)", lineHeight: 1.5 }}>
145
+ {track.description}
146
+ </p>
147
+ )}
148
+ </div>
149
+ </div>
150
+ </div>
151
+
152
+ {/* Right Column — Auth Card */}
153
+ <div style={{
154
+ background: "var(--m-white)",
155
+ borderRadius: "var(--radius-lg)",
156
+ padding: 32,
157
+ border: "1px solid rgba(0,0,0,0.06)",
158
+ boxShadow: "0 4px 24px rgba(0,0,0,0.08)",
159
+ position: "relative",
160
+ overflow: "hidden"
161
+ }}>
162
+ {/* Top stripe for visual interest */}
163
+ <div style={{
164
+ position: "absolute",
165
+ top: 0,
166
+ left: 0,
167
+ right: 0,
168
+ height: 4,
169
+ background: session?.status === "authenticated"
170
+ ? "var(--accent-green)"
171
+ : session?.status === "expired"
172
+ ? "var(--accent-red)"
173
+ : "var(--accent-teal)"
174
+ }} />
175
+
176
+ <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
177
+ <div>
178
+ <span className="text-label" style={{ fontFamily: "var(--font-mono)", color: "var(--accent-teal)" }}>
179
+ sign-in status
180
+ </span>
181
+ <h2 style={{ marginTop: 8, fontSize: "1.6rem", fontWeight: 800, letterSpacing: "-0.02em", color: "var(--m-ink)", fontFamily: "var(--font-sans)" }}>
182
+ {loading ? "Loading session…" : getStatusCopy(session?.status ?? "pending")}
183
+ </h2>
184
+ </div>
185
+ </div>
186
+
187
+ {loading && (
188
+ <p style={{ marginTop: 20, fontSize: "0.88rem", color: "var(--ink-muted)", lineHeight: 1.6 }}>
189
+ Fetching the auth session so this browser can confirm the waiting CLI.
190
+ </p>
191
+ )}
192
+
193
+ {!loading && errorMessage && (
194
+ <div style={{
195
+ marginTop: 20,
196
+ padding: "12px 16px",
197
+ background: "rgba(224, 90, 58, 0.08)",
198
+ border: "1px solid rgba(224, 90, 58, 0.2)",
199
+ borderRadius: "var(--radius-md)",
200
+ fontSize: "0.85rem",
201
+ color: "var(--accent-red)"
202
+ }}>
203
+ {errorMessage}
204
+ </div>
205
+ )}
206
+
207
+ {!loading && session && (
208
+ <div style={{ marginTop: 24 }}>
209
+ {/* Timestamps */}
210
+ <div style={{
211
+ display: "grid",
212
+ gridTemplateColumns: "1fr 1fr",
213
+ gap: 12,
214
+ padding: "16px",
215
+ background: "var(--bg-beige-light)",
216
+ borderRadius: "var(--radius-md)",
217
+ marginBottom: 24
218
+ }}>
219
+ <div>
220
+ <span style={{ fontSize: "0.65rem", color: "var(--ink-muted)", textTransform: "uppercase", letterSpacing: "0.05em" }}>Created</span>
221
+ <p style={{ marginTop: 4, fontFamily: "var(--font-mono)", fontSize: "0.78rem", fontWeight: 600, color: "var(--m-ink)" }}>
222
+ {formatSessionTime(session.createdAt)}
223
+ </p>
224
+ </div>
225
+ <div>
226
+ <span style={{ fontSize: "0.65rem", color: "var(--ink-muted)", textTransform: "uppercase", letterSpacing: "0.05em" }}>Expires</span>
227
+ <p style={{ marginTop: 4, fontFamily: "var(--font-mono)", fontSize: "0.78rem", fontWeight: 600, color: "var(--m-ink)" }}>
228
+ {formatSessionTime(session.expiresAt)}
229
+ </p>
230
+ </div>
231
+ </div>
232
+
233
+ {/* Pending → Form or Direct Confirmation */}
234
+ {session.status === "pending" && currentUser ? (
235
+ <div style={{
236
+ padding: 24,
237
+ background: "rgba(14, 116, 144, 0.04)",
238
+ border: "1px dashed var(--accent-teal)",
239
+ borderRadius: "var(--radius-lg)",
240
+ marginBottom: 20,
241
+ display: "flex",
242
+ flexDirection: "column",
243
+ gap: 16
244
+ }}>
245
+ <div>
246
+ <span className="text-label" style={{ color: "var(--accent-teal)", fontSize: "0.62rem" }}>
247
+ ● browser session detected
248
+ </span>
249
+ <h3 style={{ marginTop: 8, fontSize: "1.1rem", fontWeight: 800, color: "var(--m-ink)", fontFamily: "var(--font-sans)" }}>
250
+ Quick Link Terminal
251
+ </h3>
252
+ <p style={{ marginTop: 6, fontSize: "0.82rem", color: "var(--ink-muted)", lineHeight: 1.5 }}>
253
+ You are currently signed in as <strong style={{ color: "var(--m-ink)" }}>{currentUser.name}</strong> ({currentUser.email}). Would you like to confirm terminal login as this user?
254
+ </p>
255
+ </div>
256
+
257
+ <button
258
+ onClick={async () => {
259
+ setLoading(true);
260
+ setErrorMessage(null);
261
+ try {
262
+ const res = await confirmCliAuthSession(sessionId);
263
+ setSession(res.session);
264
+ } catch (err) {
265
+ setErrorMessage(err instanceof Error ? err.message : "Unable to link CLI session.");
266
+ } finally {
267
+ setLoading(false);
268
+ }
269
+ }}
270
+ className="auth-submit"
271
+ style={{ background: "#000000", border: "1px solid var(--bezel-outer)", color: "var(--bg-beige)", width: "100%", fontWeight: 700, cursor: "pointer", transition: "all 0.2s ease", boxShadow: "0 0 12px rgba(168, 168, 160, 0.4)", borderRadius: "var(--radius-md)" }}
272
+ >
273
+ Confirm & Link Terminal
274
+ </button>
275
+
276
+ <div style={{ display: "flex", alignItems: "center", gap: 8, justifyContent: "center" }}>
277
+ <span style={{ height: 1, flex: 1, background: "rgba(0,0,0,0.06)" }} />
278
+ <span style={{ fontSize: "0.65rem", color: "var(--ink-faint)", textTransform: "uppercase" }}>or</span>
279
+ <span style={{ height: 1, flex: 1, background: "rgba(0,0,0,0.06)" }} />
280
+ </div>
281
+
282
+ <button
283
+ onClick={() => {
284
+ setCurrentUser(null);
285
+ }}
286
+ className="auth-google-fallback"
287
+ style={{ minHeight: 38, fontSize: "0.8rem", border: "1px solid rgba(255,255,255,0.25)", background: "#000000", color: "var(--m-ink)", width: "100%", cursor: "pointer", boxShadow: "0 0 8px rgba(255, 255, 255, 0.05)", borderRadius: "var(--radius-md)" }}
288
+ >
289
+ Use a different account
290
+ </button>
291
+ </div>
292
+ ) : session.status === "pending" && (
293
+ <div>
294
+ <AuthPanel
295
+ mode="login"
296
+ sessionId={sessionId}
297
+ onCliSessionAuthenticated={(payload) => setSession(payload.session)}
298
+ />
299
+ <p style={{ fontSize: "0.72rem", color: "var(--ink-muted)", lineHeight: 1.6 }}>
300
+ Your account is saved in MongoDB. After this browser step completes, return to your terminal.
301
+ </p>
302
+ </div>
303
+ )}
304
+
305
+ {/* Authenticated → Success */}
306
+ {session.status === "authenticated" && (
307
+ <div style={{
308
+ padding: 20,
309
+ background: "rgba(45, 138, 86, 0.08)",
310
+ border: "1px solid rgba(45, 138, 86, 0.2)",
311
+ borderRadius: "var(--radius-md)"
312
+ }}>
313
+ <span style={{ display: "block", fontSize: "0.65rem", fontWeight: 600, color: "var(--accent-green)", textTransform: "uppercase", letterSpacing: "0.1em" }}>
314
+ CLI sign-in complete
315
+ </span>
316
+ <p style={{ marginTop: 8, fontSize: "1rem", fontWeight: 700, color: "var(--m-ink)" }}>
317
+ {session.learner?.name} is authenticated for the {track?.label ?? "selected"} track.
318
+ </p>
319
+ <p style={{ marginTop: 8, fontSize: "0.82rem", color: "var(--ink-soft)", lineHeight: 1.6 }}>
320
+ You can return to the terminal. The waiting CLI session will continue automatically.
321
+ </p>
322
+ <p style={{ marginTop: 12, fontSize: "0.78rem", color: "var(--accent-teal)", fontWeight: 600, fontFamily: "var(--font-mono)" }}>
323
+ Redirecting to your progress dashboard...
324
+ </p>
325
+ </div>
326
+ )}
327
+
328
+ {/* Expired */}
329
+ {session.status === "expired" && (
330
+ <div style={{
331
+ padding: 20,
332
+ background: "rgba(224, 90, 58, 0.06)",
333
+ border: "1px solid rgba(224, 90, 58, 0.15)",
334
+ borderRadius: "var(--radius-md)"
335
+ }}>
336
+ <span style={{ display: "block", fontSize: "0.65rem", fontWeight: 600, color: "var(--accent-red)", textTransform: "uppercase", letterSpacing: "0.1em" }}>
337
+ Session expired
338
+ </span>
339
+ <p style={{ marginTop: 8, fontSize: "0.85rem", color: "var(--ink-soft)", lineHeight: 1.6 }}>
340
+ This browser confirmation took too long. Start a new terminal session with <code style={{ fontFamily: "var(--font-mono)", fontWeight: 600 }}>visionos start</code> to generate a fresh auth link.
341
+ </p>
342
+ </div>
343
+ )}
344
+ </div>
345
+ )}
346
+ </div>
347
+ </div>
348
+ </main>
349
+
350
+ {/* ── Footer ─────────────────────── */}
351
+ <footer className="m-site-footer">
352
+ <div className="header-left">
353
+ <span className="header-brand">VisionOS</span>
354
+ <span className="header-meta">CLI Auth · Secure Session</span>
355
+ </div>
356
+ <nav className="header-nav">
357
+ <a href="/">← Back to home</a>
358
+ </nav>
359
+ </footer>
360
+ </div>
361
+
362
+ <div className="bezel-notch">
363
+ <div className="bezel-notch-inner" />
364
+ </div>
365
+ </div>
366
+ );
367
+ }