create-interview-cockpit 0.17.3 → 0.18.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.
@@ -1206,6 +1206,415 @@ export const DEFAULT_NEXTJS_LAB: FrontendLabWorkspace = {
1206
1206
  files: NEXTJS_DEFAULT_FILES,
1207
1207
  };
1208
1208
 
1209
+ const NEXTJS_BFF_AUTH_CLIENT_FILES: Record<string, string> = {
1210
+ "README.md": `# Next.js BFF Auth Client Lab
1211
+
1212
+ This lab is intentionally separate from the Infrastructure Lab.
1213
+
1214
+ ## Flow
1215
+
1216
+ 1. Deploy the infrastructure lab first:
1217
+ - terraform init
1218
+ - terraform plan
1219
+ - terraform apply -auto-approve
1220
+ 2. Start this Next.js lab.
1221
+ 3. Click **Sign in through BFF**.
1222
+ 4. The browser goes to the deployed BFF at http://localhost:4300.
1223
+ 5. The BFF redirects to the local Cognito-like provider.
1224
+ 6. After sign-in, the BFF stores tokens in Redis and redirects back to this Next.js app.
1225
+ 7. This app calls the BFF with credentials included.
1226
+
1227
+ ## Why separate labs?
1228
+
1229
+ - Infra Lab owns deployment.
1230
+ - Next.js Lab owns the frontend shell/client experience.
1231
+ - The integration point is the BFF URL: http://localhost:4300.
1232
+ `,
1233
+ "app/layout.tsx": `import "./globals.css";
1234
+
1235
+ export const metadata = {
1236
+ title: "Enterprise Auth Client Lab",
1237
+ description: "Next.js client talking to a locally deployed BFF",
1238
+ };
1239
+
1240
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
1241
+ return (
1242
+ <html lang="en">
1243
+ <body>{children}</body>
1244
+ </html>
1245
+ );
1246
+ }
1247
+ `,
1248
+ "app/page.tsx": `import { AuthDashboard } from "../components/AuthDashboard";
1249
+
1250
+ export default function HomePage() {
1251
+ return <AuthDashboard />;
1252
+ }
1253
+ `,
1254
+ "app/globals.css": `* {
1255
+ box-sizing: border-box;
1256
+ }
1257
+
1258
+ html,
1259
+ body {
1260
+ margin: 0;
1261
+ min-height: 100%;
1262
+ background:
1263
+ radial-gradient(circle at top left, rgba(6, 182, 212, 0.18), transparent 30rem),
1264
+ radial-gradient(circle at bottom right, rgba(124, 58, 237, 0.16), transparent 28rem),
1265
+ #020617;
1266
+ color: #e2e8f0;
1267
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
1268
+ }
1269
+
1270
+ button,
1271
+ textarea {
1272
+ font: inherit;
1273
+ }
1274
+
1275
+ button {
1276
+ cursor: pointer;
1277
+ }
1278
+
1279
+ .shell {
1280
+ width: min(1120px, calc(100vw - 32px));
1281
+ margin: 0 auto;
1282
+ padding: 48px 0;
1283
+ }
1284
+
1285
+ .hero,
1286
+ .card {
1287
+ border: 1px solid rgba(148, 163, 184, 0.18);
1288
+ background: rgba(15, 23, 42, 0.78);
1289
+ box-shadow: 0 24px 80px rgba(2, 6, 23, 0.45);
1290
+ backdrop-filter: blur(18px);
1291
+ }
1292
+
1293
+ .hero {
1294
+ border-radius: 28px;
1295
+ padding: 40px;
1296
+ }
1297
+
1298
+ .eyebrow {
1299
+ margin: 0 0 12px;
1300
+ color: #22d3ee;
1301
+ font-size: 0.78rem;
1302
+ font-weight: 800;
1303
+ letter-spacing: 0.22em;
1304
+ text-transform: uppercase;
1305
+ }
1306
+
1307
+ h1,
1308
+ h2,
1309
+ p {
1310
+ margin-top: 0;
1311
+ }
1312
+
1313
+ h1 {
1314
+ max-width: 760px;
1315
+ margin-bottom: 12px;
1316
+ color: #f8fafc;
1317
+ font-size: clamp(2.25rem, 6vw, 4.5rem);
1318
+ line-height: 0.95;
1319
+ letter-spacing: -0.06em;
1320
+ }
1321
+
1322
+ .hero p {
1323
+ max-width: 720px;
1324
+ color: #cbd5e1;
1325
+ font-size: 1.05rem;
1326
+ line-height: 1.7;
1327
+ }
1328
+
1329
+ .actions {
1330
+ display: flex;
1331
+ flex-wrap: wrap;
1332
+ gap: 12px;
1333
+ margin-top: 28px;
1334
+ }
1335
+
1336
+ button {
1337
+ border: 1px solid rgba(148, 163, 184, 0.26);
1338
+ border-radius: 999px;
1339
+ background: rgba(15, 23, 42, 0.92);
1340
+ color: #e2e8f0;
1341
+ padding: 11px 16px;
1342
+ font-weight: 800;
1343
+ transition: transform 150ms ease, border-color 150ms ease, background 150ms ease;
1344
+ }
1345
+
1346
+ button:hover:not(:disabled) {
1347
+ transform: translateY(-1px);
1348
+ border-color: rgba(34, 211, 238, 0.7);
1349
+ background: rgba(8, 47, 73, 0.9);
1350
+ }
1351
+
1352
+ button:disabled {
1353
+ cursor: not-allowed;
1354
+ opacity: 0.45;
1355
+ }
1356
+
1357
+ button.primary {
1358
+ border-color: rgba(34, 211, 238, 0.5);
1359
+ background: linear-gradient(135deg, #06b6d4, #8b5cf6);
1360
+ color: #020617;
1361
+ }
1362
+
1363
+ .grid {
1364
+ display: grid;
1365
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1366
+ gap: 18px;
1367
+ margin-top: 18px;
1368
+ }
1369
+
1370
+ .card {
1371
+ border-radius: 22px;
1372
+ padding: 24px;
1373
+ }
1374
+
1375
+ .card.wide {
1376
+ grid-column: 1 / -1;
1377
+ }
1378
+
1379
+ .cardHeader {
1380
+ display: flex;
1381
+ align-items: center;
1382
+ gap: 12px;
1383
+ }
1384
+
1385
+ .cardHeader span {
1386
+ display: inline-grid;
1387
+ width: 30px;
1388
+ height: 30px;
1389
+ place-items: center;
1390
+ border-radius: 999px;
1391
+ background: rgba(34, 211, 238, 0.14);
1392
+ color: #67e8f9;
1393
+ font-weight: 900;
1394
+ }
1395
+
1396
+ h2 {
1397
+ margin-bottom: 0;
1398
+ color: #f8fafc;
1399
+ font-size: 1rem;
1400
+ }
1401
+
1402
+ .hint {
1403
+ margin: 12px 0 16px;
1404
+ color: #94a3b8;
1405
+ line-height: 1.6;
1406
+ }
1407
+
1408
+ pre,
1409
+ textarea {
1410
+ width: 100%;
1411
+ border: 1px solid rgba(51, 65, 85, 0.95);
1412
+ border-radius: 16px;
1413
+ background: rgba(2, 6, 23, 0.8);
1414
+ color: #cbd5e1;
1415
+ }
1416
+
1417
+ pre {
1418
+ min-height: 164px;
1419
+ overflow: auto;
1420
+ padding: 16px;
1421
+ font-size: 0.82rem;
1422
+ line-height: 1.55;
1423
+ }
1424
+
1425
+ textarea {
1426
+ display: block;
1427
+ margin-bottom: 12px;
1428
+ padding: 14px;
1429
+ resize: vertical;
1430
+ }
1431
+
1432
+ @media (max-width: 820px) {
1433
+ .grid {
1434
+ grid-template-columns: 1fr;
1435
+ }
1436
+ }
1437
+ `,
1438
+ "components/AuthDashboard.tsx": `"use client";
1439
+
1440
+ import { useEffect, useMemo, useState } from "react";
1441
+
1442
+ const BFF_BASE_URL = "http://localhost:4300";
1443
+
1444
+ type ApiResult = {
1445
+ status: number;
1446
+ body: unknown;
1447
+ };
1448
+
1449
+ function getCookie(name: string): string | null {
1450
+ const match = document.cookie
1451
+ .split("; ")
1452
+ .find((row) => row.startsWith(name + "="));
1453
+
1454
+ return match ? decodeURIComponent(match.split("=")[1]) : null;
1455
+ }
1456
+
1457
+ async function bffFetch(path: string, init: RequestInit = {}): Promise<ApiResult> {
1458
+ const method = (init.method || "GET").toUpperCase();
1459
+ const headers = new Headers(init.headers || {});
1460
+
1461
+ if (!["GET", "HEAD", "OPTIONS"].includes(method)) {
1462
+ const csrf = getCookie("XSRF-TOKEN");
1463
+ if (csrf) headers.set("x-csrf-token", csrf);
1464
+ }
1465
+
1466
+ const response = await fetch(BFF_BASE_URL + path, {
1467
+ ...init,
1468
+ headers,
1469
+ credentials: "include",
1470
+ });
1471
+
1472
+ const text = await response.text();
1473
+ let body: unknown = text;
1474
+ try {
1475
+ body = text ? JSON.parse(text) : null;
1476
+ } catch {
1477
+ body = text;
1478
+ }
1479
+
1480
+ return { status: response.status, body };
1481
+ }
1482
+
1483
+ export function AuthDashboard() {
1484
+ const [me, setMe] = useState<ApiResult | null>(null);
1485
+ const [claim, setClaim] = useState<ApiResult | null>(null);
1486
+ const [noteResult, setNoteResult] = useState<ApiResult | null>(null);
1487
+ const [note, setNote] = useState("Customer uploaded first notice of loss.");
1488
+ const [loading, setLoading] = useState<string | null>(null);
1489
+
1490
+ const isAuthenticated = useMemo(() => {
1491
+ if (!me || typeof me.body !== "object" || me.body === null) return false;
1492
+ return Boolean((me.body as { authenticated?: boolean }).authenticated);
1493
+ }, [me]);
1494
+
1495
+ async function run(label: string, action: () => Promise<void>) {
1496
+ setLoading(label);
1497
+ try {
1498
+ await action();
1499
+ } finally {
1500
+ setLoading(null);
1501
+ }
1502
+ }
1503
+
1504
+ async function refreshMe() {
1505
+ setMe(await bffFetch("/auth/me"));
1506
+ }
1507
+
1508
+ useEffect(() => {
1509
+ void refreshMe();
1510
+ }, []);
1511
+
1512
+ function login() {
1513
+ const returnTo = encodeURIComponent(window.location.origin);
1514
+ window.location.href = BFF_BASE_URL + "/auth/login?returnTo=" + returnTo;
1515
+ }
1516
+
1517
+ async function logout() {
1518
+ await run("logout", async () => {
1519
+ await bffFetch("/auth/logout", { method: "POST" });
1520
+ await refreshMe();
1521
+ setClaim(null);
1522
+ setNoteResult(null);
1523
+ });
1524
+ }
1525
+
1526
+ async function loadClaim() {
1527
+ await run("claim", async () => {
1528
+ setClaim(await bffFetch("/api/claims/CLM-1001"));
1529
+ });
1530
+ }
1531
+
1532
+ async function addNote() {
1533
+ await run("note", async () => {
1534
+ setNoteResult(
1535
+ await bffFetch("/api/claims/CLM-1001/notes", {
1536
+ method: "POST",
1537
+ headers: { "content-type": "application/json" },
1538
+ body: JSON.stringify({ text: note }),
1539
+ }),
1540
+ );
1541
+ await loadClaim();
1542
+ });
1543
+ }
1544
+
1545
+ return (
1546
+ <main className="shell">
1547
+ <section className="hero">
1548
+ <p className="eyebrow">Next.js shell + deployed BFF</p>
1549
+ <h1>Enterprise BFF Auth Client</h1>
1550
+ <p>
1551
+ This frontend stores no bearer tokens. It redirects through the BFF,
1552
+ then calls BFF endpoints with cookies and CSRF protection.
1553
+ </p>
1554
+ <div className="actions">
1555
+ <button className="primary" onClick={login}>Sign in through BFF</button>
1556
+ <button onClick={() => void refreshMe()}>Refresh /auth/me</button>
1557
+ <button onClick={() => void logout()} disabled={!isAuthenticated || loading === "logout"}>
1558
+ Logout
1559
+ </button>
1560
+ </div>
1561
+ </section>
1562
+
1563
+ <section className="grid">
1564
+ <article className="card">
1565
+ <div className="cardHeader">
1566
+ <span>1</span>
1567
+ <h2>Session</h2>
1568
+ </div>
1569
+ <p className="hint">The browser sees only cookie-backed auth state.</p>
1570
+ <pre>{JSON.stringify(me?.body ?? "Not loaded", null, 2)}</pre>
1571
+ </article>
1572
+
1573
+ <article className="card">
1574
+ <div className="cardHeader">
1575
+ <span>2</span>
1576
+ <h2>Downstream API through BFF</h2>
1577
+ </div>
1578
+ <p className="hint">The client never calls the claims API directly.</p>
1579
+ <button onClick={() => void loadClaim()} disabled={!isAuthenticated || loading === "claim"}>
1580
+ Load claim CLM-1001
1581
+ </button>
1582
+ <pre>{JSON.stringify(claim?.body ?? "No claim loaded", null, 2)}</pre>
1583
+ </article>
1584
+
1585
+ <article className="card wide">
1586
+ <div className="cardHeader">
1587
+ <span>3</span>
1588
+ <h2>Write with CSRF header</h2>
1589
+ </div>
1590
+ <p className="hint">
1591
+ The readable XSRF-TOKEN cookie is copied into x-csrf-token for writes.
1592
+ </p>
1593
+ <textarea value={note} onChange={(event) => setNote(event.target.value)} rows={3} />
1594
+ <button onClick={() => void addNote()} disabled={!isAuthenticated || loading === "note"}>
1595
+ Add note
1596
+ </button>
1597
+ <pre>{JSON.stringify(noteResult?.body ?? "No note submitted", null, 2)}</pre>
1598
+ </article>
1599
+ </section>
1600
+ </main>
1601
+ );
1602
+ }
1603
+ `,
1604
+ "components/AuthDashboard.module.css": `/* Optional scratch file: move styles here if you want CSS modules practice. */
1605
+ `,
1606
+ "app/styles.css": `/* Optional scratch file for experimenting with additional global styles. */
1607
+ `,
1608
+ };
1609
+
1610
+ export const NEXTJS_BFF_AUTH_CLIENT_LAB: FrontendLabWorkspace = {
1611
+ version: 1,
1612
+ label: "Next.js BFF Auth Client",
1613
+ type: "nextjs",
1614
+ activeFile: "components/AuthDashboard.tsx",
1615
+ files: NEXTJS_BFF_AUTH_CLIENT_FILES,
1616
+ };
1617
+
1209
1618
  export const DEFAULT_MODULE_FEDERATION_LAB: FrontendLabWorkspace = {
1210
1619
  version: 1,
1211
1620
  label: "Webpack Module Federation Lab",
@@ -6,6 +6,7 @@ import type {
6
6
  WorkspaceMeta,
7
7
  InfraLabWorkspace,
8
8
  FrontendLabWorkspace,
9
+ GithubActionsLabWorkspace,
9
10
  ContextFileOrigin,
10
11
  } from "./types";
11
12
  import type { AiSettings } from "./api";
@@ -126,12 +127,7 @@ interface Store {
126
127
  deleteWorkspace: (id: string) => Promise<void>;
127
128
  renameWorkspace: (id: string, name: string) => Promise<void>;
128
129
  patchWorkspace: (id: string, data: object) => Promise<void>;
129
- syncWorkspace: (id: string) => Promise<{
130
- topicsUpserted: number;
131
- filesImported: number;
132
- filesSkipped: number;
133
- errors: string[];
134
- }>;
130
+ syncWorkspace: (id: string) => Promise<import("./api").SyncWorkspaceResult>;
135
131
  linkDriveFolder: (
136
132
  workspaceId: string,
137
133
  url: string,
@@ -176,6 +172,12 @@ interface Store {
176
172
  topicId: string,
177
173
  parentQuestionId: string | null,
178
174
  ) => Promise<void>;
175
+ copyQuestion: (
176
+ questionId: string,
177
+ topicId: string,
178
+ parentQuestionId: string | null,
179
+ targetTopicId?: string,
180
+ ) => Promise<void>;
179
181
  removeQuestion: (questionId: string, topicId: string) => Promise<void>;
180
182
  renameQuestion: (
181
183
  questionId: string,
@@ -330,6 +332,13 @@ interface Store {
330
332
  openInfraLab: (workspace?: InfraLabWorkspace, fileId?: string) => void;
331
333
  closeInfraLab: () => void;
332
334
 
335
+ // ── GitHub Actions Lab ───────────────────────────────────────
336
+ showGhaLab: boolean;
337
+ runnerInitialGha: GithubActionsLabWorkspace | null;
338
+ runnerInitialGhaFileId: string | null;
339
+ openGhaLab: (workspace?: GithubActionsLabWorkspace, fileId?: string) => void;
340
+ closeGhaLab: () => void;
341
+
333
342
  // ── Frontend Labs (React / Next.js / Module Federation) — open inside the sandbox ──
334
343
  openReactLab: (
335
344
  workspace?: FrontendLabWorkspace,
@@ -387,6 +396,9 @@ export const useStore = create<Store>((set, get) => ({
387
396
  showInfraLab: false,
388
397
  runnerInitialInfra: null,
389
398
  runnerInitialInfraFileId: null,
399
+ showGhaLab: false,
400
+ runnerInitialGha: null,
401
+ runnerInitialGhaFileId: null,
390
402
  showCanvasLab: false,
391
403
  canvasLabInitialCode: null,
392
404
  canvasLabInitialFileId: null,
@@ -467,9 +479,15 @@ export const useStore = create<Store>((set, get) => ({
467
479
 
468
480
  syncWorkspace: async (id) => {
469
481
  const result = await api.syncWorkspaceApi(id);
482
+ if ("needsAuth" in result && result.needsAuth) {
483
+ return result;
484
+ }
470
485
  if (id === get().activeWorkspaceId) {
471
- const topics = await api.fetchTopics();
472
- set({ topics, questionsByTopic: {} });
486
+ const [topics, workspaceFiles] = await Promise.all([
487
+ api.fetchTopics(),
488
+ api.fetchWorkspaceFiles(),
489
+ ]);
490
+ set({ topics, workspaceFiles, questionsByTopic: {} });
473
491
  }
474
492
  const registry = await api.fetchWorkspaces();
475
493
  set({ workspaces: registry.workspaces });
@@ -491,8 +509,15 @@ export const useStore = create<Store>((set, get) => ({
491
509
  if (workspaceId === get().activeWorkspaceId) {
492
510
  set({ topics: [], questionsByTopic: {} });
493
511
  const result = await api.syncWorkspaceApi(workspaceId);
494
- const topics = await api.fetchTopics();
495
- set({ topics, questionsByTopic: {} });
512
+ if ("needsAuth" in result && result.needsAuth) {
513
+ window.location.href = result.authUrl;
514
+ return;
515
+ }
516
+ const [topics, workspaceFiles] = await Promise.all([
517
+ api.fetchTopics(),
518
+ api.fetchWorkspaceFiles(),
519
+ ]);
520
+ set({ topics, workspaceFiles, questionsByTopic: {} });
496
521
  const reg2 = await api.fetchWorkspaces();
497
522
  set({ workspaces: reg2.workspaces });
498
523
  }
@@ -508,6 +533,7 @@ export const useStore = create<Store>((set, get) => ({
508
533
  selectedTopicId: null,
509
534
  selectedQuestionId: null,
510
535
  currentQuestion: null,
536
+ workspaceFiles: [],
511
537
  });
512
538
  },
513
539
 
@@ -629,6 +655,39 @@ export const useStore = create<Store>((set, get) => ({
629
655
  }));
630
656
  },
631
657
 
658
+ copyQuestion: async (
659
+ questionId,
660
+ topicId,
661
+ parentQuestionId,
662
+ targetTopicId,
663
+ ) => {
664
+ const destinationTopicId = targetTopicId ?? topicId;
665
+ const copied = await api.copyQuestion(questionId, {
666
+ parentQuestionId,
667
+ targetTopicId: destinationTopicId,
668
+ });
669
+ const copiedRoot = copied[0];
670
+ set((s) => ({
671
+ questionsByTopic: {
672
+ ...s.questionsByTopic,
673
+ [destinationTopicId]: [
674
+ ...(s.questionsByTopic[destinationTopicId] || []),
675
+ ...copied,
676
+ ],
677
+ },
678
+ selectedTopicId: copiedRoot?.id ? destinationTopicId : s.selectedTopicId,
679
+ selectedQuestionId: copiedRoot?.id ?? s.selectedQuestionId,
680
+ currentQuestion: copiedRoot ?? s.currentQuestion,
681
+ expandedTopics: s.expandedTopics.includes(destinationTopicId)
682
+ ? s.expandedTopics
683
+ : [...s.expandedTopics, destinationTopicId],
684
+ }));
685
+ if (copiedRoot) {
686
+ sessionStorage.setItem("lastTopicId", destinationTopicId);
687
+ sessionStorage.setItem("lastQuestionId", copiedRoot.id);
688
+ }
689
+ },
690
+
632
691
  removeQuestion: async (questionId, topicId) => {
633
692
  await api.deleteQuestion(questionId);
634
693
  set((s) => ({
@@ -1079,6 +1138,20 @@ export const useStore = create<Store>((set, get) => ({
1079
1138
  openBrowserSecurityLab: () => set({ showBrowserSecurityLab: true }),
1080
1139
  closeBrowserSecurityLab: () => set({ showBrowserSecurityLab: false }),
1081
1140
  closeInfraLab: () => set({ showInfraLab: false }),
1141
+ openGhaLab: (workspace, fileId?) =>
1142
+ set({
1143
+ showGhaLab: true,
1144
+ showInfraLab: false,
1145
+ showCodeRunner: false,
1146
+ runnerInitialGha: workspace ?? null,
1147
+ runnerInitialGhaFileId: fileId ?? null,
1148
+ }),
1149
+ closeGhaLab: () =>
1150
+ set({
1151
+ showGhaLab: false,
1152
+ runnerInitialGha: null,
1153
+ runnerInitialGhaFileId: null,
1154
+ }),
1082
1155
  openCanvasLab: (code?, fileId?) =>
1083
1156
  set({
1084
1157
  showCanvasLab: true,
@@ -8,7 +8,8 @@ export type ContextFileOrigin =
8
8
  | "react"
9
9
  | "nextjs"
10
10
  | "module-federation"
11
- | "canvas";
11
+ | "canvas"
12
+ | "github-actions";
12
13
 
13
14
  export interface ContextFile {
14
15
  id: string;
@@ -42,12 +43,23 @@ export interface FrontendLabWorkspace {
42
43
  export interface InfraLabWorkspace {
43
44
  version: 1;
44
45
  label: string;
45
- provider: "aws";
46
- executionMode: "plan-only" | "localstack";
46
+ provider: "aws" | "docker";
47
+ executionMode: "plan-only" | "localstack" | "docker";
47
48
  activeFile: string;
48
49
  files: Record<string, string>;
49
50
  }
50
51
 
52
+ export interface GithubActionsLabWorkspace {
53
+ version: 1;
54
+ label: string;
55
+ activeFile: string;
56
+ files: Record<string, string>;
57
+ /** Optional default event the run button uses (push, pull_request, workflow_dispatch). */
58
+ defaultEvent?: string;
59
+ /** Optional default workflow file path under .github/workflows. */
60
+ defaultWorkflow?: string;
61
+ }
62
+
51
63
  export interface WorkspaceMeta {
52
64
  id: string;
53
65
  name: string;
@@ -116,6 +128,16 @@ export interface ReadingBookmark {
116
128
  blockIndex: number;
117
129
  }
118
130
 
131
+ export interface StoredCodeAnnotation {
132
+ id: string;
133
+ lineNumber: number;
134
+ lineContent: string;
135
+ prompt: string;
136
+ response: string;
137
+ filePath: string;
138
+ createdAt: string;
139
+ }
140
+
119
141
  export interface Question {
120
142
  id: string;
121
143
  topicId: string;
@@ -127,6 +149,8 @@ export interface Question {
127
149
  messages: Message[];
128
150
  annotations?: Annotation[];
129
151
  readingBookmark?: ReadingBookmark;
152
+ /** Code-line annotations keyed by file path. */
153
+ codeAnnotations?: { [filePath: string]: StoredCodeAnnotation[] };
130
154
  /** IDs of other questions in the same topic whose conversation history is injected as context. */
131
155
  linkedConversationIds?: string[];
132
156
  createdAt: string;
@@ -1 +1 @@
1
- {"root":["./src/app.tsx","./src/api.ts","./src/browsersecuritytemplates.ts","./src/infralab.ts","./src/main.tsx","./src/reactlab.ts","./src/store.ts","./src/types.ts","./src/vite-env.d.ts","./src/components/aisettingsmodal.tsx","./src/components/annotationdialog.tsx","./src/components/browsersecuritylabmodal.tsx","./src/components/canvaslabmodal.tsx","./src/components/chatmessage.tsx","./src/components/chatview.tsx","./src/components/codecontextpanel.tsx","./src/components/codelineannotationpopup.tsx","./src/components/coderunnermodal.tsx","./src/components/deploymentlabmodal.tsx","./src/components/docrefmodal.tsx","./src/components/fileattachments.tsx","./src/components/filepickermodal.tsx","./src/components/fileviewermodal.tsx","./src/components/infralabmodal.tsx","./src/components/labspanel.tsx","./src/components/linkedconvospicker.tsx","./src/components/markdownrenderer.tsx","./src/components/mermaiddiagram.tsx","./src/components/notesmodal.tsx","./src/components/plotembed.tsx","./src/components/sidebar.tsx","./src/components/textannotator.tsx","./src/components/vizcraftembed.tsx","./src/components/workspaceswitcher.tsx"],"version":"5.9.3"}
1
+ {"root":["./src/app.tsx","./src/api.ts","./src/browsersecuritytemplates.ts","./src/enterpriselocallab.ts","./src/githubactionslab.ts","./src/infralab.ts","./src/main.tsx","./src/reactlab.ts","./src/store.ts","./src/types.ts","./src/vite-env.d.ts","./src/components/aisettingsmodal.tsx","./src/components/annotationdialog.tsx","./src/components/browsersecuritylabmodal.tsx","./src/components/canvaslabmodal.tsx","./src/components/chatmessage.tsx","./src/components/chatview.tsx","./src/components/codecontextpanel.tsx","./src/components/codelineannotationpopup.tsx","./src/components/coderunnermodal.tsx","./src/components/deploymentlabmodal.tsx","./src/components/docrefmodal.tsx","./src/components/fileattachments.tsx","./src/components/filepickermodal.tsx","./src/components/fileviewermodal.tsx","./src/components/githubactionslabmodal.tsx","./src/components/infralabmodal.tsx","./src/components/labspanel.tsx","./src/components/linkedconvospicker.tsx","./src/components/markdownrenderer.tsx","./src/components/mermaiddiagram.tsx","./src/components/notesmodal.tsx","./src/components/plotembed.tsx","./src/components/sidebar.tsx","./src/components/textannotator.tsx","./src/components/vizcraftembed.tsx","./src/components/workspaceswitcher.tsx"],"version":"5.9.3"}
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.17.1"
2
+ "version": "0.17.2"
3
3
  }