create-interview-cockpit 0.5.0 → 0.7.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 (30) hide show
  1. package/package.json +1 -1
  2. package/template/client/package-lock.json +734 -1
  3. package/template/client/package.json +1 -0
  4. package/template/client/src/App.tsx +3 -0
  5. package/template/client/src/api.ts +384 -4
  6. package/template/client/src/components/AiSettingsModal.tsx +818 -425
  7. package/template/client/src/components/ChatMessage.tsx +34 -12
  8. package/template/client/src/components/ChatView.tsx +298 -121
  9. package/template/client/src/components/CodeContextPanel.tsx +530 -2
  10. package/template/client/src/components/CodeRunnerModal.tsx +1895 -120
  11. package/template/client/src/components/DocRefModal.tsx +55 -6
  12. package/template/client/src/components/FileAttachments.tsx +20 -4
  13. package/template/client/src/components/InfraLabModal.tsx +1706 -0
  14. package/template/client/src/components/LinkedConvosPicker.tsx +128 -0
  15. package/template/client/src/components/MarkdownRenderer.tsx +22 -8
  16. package/template/client/src/components/NotesModal.tsx +977 -0
  17. package/template/client/src/components/PlotEmbed.tsx +173 -0
  18. package/template/client/src/components/Sidebar.tsx +184 -0
  19. package/template/client/src/components/VizCraftEmbed.tsx +257 -13
  20. package/template/client/src/components/WorkspaceSwitcher.tsx +4 -0
  21. package/template/client/src/infraLab.ts +124 -0
  22. package/template/client/src/reactLab.ts +960 -0
  23. package/template/client/src/store.ts +250 -6
  24. package/template/client/src/types.ts +36 -3
  25. package/template/client/tsconfig.tsbuildinfo +1 -1
  26. package/template/cockpit.json +1 -1
  27. package/template/server/src/google-drive.ts +39 -3
  28. package/template/server/src/index.ts +954 -52
  29. package/template/server/src/infra-runner.ts +1104 -0
  30. package/template/server/src/storage.ts +22 -3
@@ -1,8 +1,36 @@
1
1
  import { create } from "zustand";
2
- import type { Topic, Question, CodeSnippet, WorkspaceMeta } from "./types";
2
+ import type {
3
+ Topic,
4
+ Question,
5
+ CodeSnippet,
6
+ WorkspaceMeta,
7
+ InfraLabWorkspace,
8
+ FrontendLabWorkspace,
9
+ } from "./types";
3
10
  import type { AiSettings } from "./api";
4
11
  import * as api from "./api";
5
12
 
13
+ // Default Express server used when opening a React/Next.js sandbox with no prior server code
14
+ const DEFAULT_SERVER_CODE = `import express from 'express';
15
+ const app = express();
16
+ app.use(express.json());
17
+ // Allow requests from the React preview iframe
18
+ app.use((_req, res, next) => {
19
+ res.setHeader('Access-Control-Allow-Origin', '*');
20
+ res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
21
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
22
+ if (_req.method === 'OPTIONS') { res.sendStatus(204); return; }
23
+ next();
24
+ });
25
+
26
+ app.get('/api/hello', (_req, res) => {
27
+ res.json({ message: 'Hello from Express!', time: Date.now() });
28
+ });
29
+
30
+ const port = Number(process.env.PORT);
31
+ app.listen(port, () => console.log('Server on :' + port));
32
+ `;
33
+
6
34
  const DEFAULT_AI_SETTINGS: AiSettings = {
7
35
  systemPrompt: "",
8
36
  responseProfiles: {
@@ -11,6 +39,7 @@ const DEFAULT_AI_SETTINGS: AiSettings = {
11
39
  normal: { maxOutputTokens: 3000, maxSteps: 5 },
12
40
  },
13
41
  vizGuide: "",
42
+ plotGuide: "",
14
43
  alwaysSendPrefsDefault: false,
15
44
  thinkingBudget: 0,
16
45
  promptGroups: {
@@ -52,6 +81,19 @@ const DEFAULT_AI_SETTINGS: AiSettings = {
52
81
  "When using technical terms or abbreviations, immediately expand their meaning in square brackets right after the term — e.g. 'TCP [Transmission Control Protocol — a connection-oriented transport protocol]' or 'idempotent [an operation that produces the same result no matter how many times it is applied]'. Do this throughout your response so I never need to look anything up.",
53
82
  },
54
83
  },
84
+ "diagram-use": {
85
+ label: "Diagram Usage",
86
+ description: "Which visual formats to use and how often.",
87
+ default: "none",
88
+ options: {
89
+ none: "",
90
+ vizcraft:
91
+ "Prioritize using vizcraft (viz) diagrams as much as possible. *NB:* Character limits do not apply to these diagrams",
92
+ mermaid:
93
+ "Prioritize using mermaid diagrams as much as possible. *NB:* Character limits do not apply to these diagrams",
94
+ plot: "Prioritize using plot blocks for graphs, curves, distributions, and data charts when they would make the explanation clearer. *NB:* Character limits do not apply to these plots",
95
+ },
96
+ },
55
97
  },
56
98
  };
57
99
 
@@ -108,19 +150,30 @@ interface Store {
108
150
  createDriveSubfolder: (
109
151
  id: string,
110
152
  name: string,
111
- ) => Promise<import("./api").DriveFolder>;
153
+ ) => Promise<
154
+ import("./api").DriveFolder | { needsAuth: true; authUrl: string }
155
+ >;
112
156
 
113
157
  fetchTopics: () => Promise<void>;
114
158
  fetchQuestions: (topicId: string) => Promise<void>;
115
159
  addTopic: (name: string) => Promise<void>;
116
160
  removeTopic: (id: string) => Promise<void>;
117
161
  renameTopic: (id: string, name: string) => Promise<void>;
162
+ updateTopicSystemContext: (
163
+ id: string,
164
+ systemContext: string,
165
+ ) => Promise<void>;
118
166
  addQuestion: (topicId: string, title: string) => Promise<void>;
119
167
  addChildQuestion: (
120
168
  topicId: string,
121
169
  parentQuestionId: string,
122
170
  title: string,
123
171
  ) => Promise<void>;
172
+ moveQuestion: (
173
+ questionId: string,
174
+ topicId: string,
175
+ parentQuestionId: string | null,
176
+ ) => Promise<void>;
124
177
  removeQuestion: (questionId: string, topicId: string) => Promise<void>;
125
178
  renameQuestion: (
126
179
  questionId: string,
@@ -159,10 +212,21 @@ interface Store {
159
212
  code: string,
160
213
  language: string,
161
214
  label: string,
162
- origin: "user" | "ai" | "sandbox",
215
+ origin:
216
+ | "user"
217
+ | "ai"
218
+ | "sandbox"
219
+ | "infra"
220
+ | "react"
221
+ | "nextjs"
222
+ | "module-federation",
163
223
  ) => Promise<import("./types").ContextFile>;
164
224
  clearMessages: (questionId: string) => Promise<void>;
165
225
 
226
+ // ── Linked Conversations ─────────────────────────────────────
227
+ linkConversation: (questionId: string, linkedId: string) => Promise<void>;
228
+ unlinkConversation: (questionId: string, linkedId: string) => Promise<void>;
229
+
166
230
  // ── Workspace Context Files ──────────────────────────────────
167
231
  workspaceFiles: import("./types").ContextFile[];
168
232
  fetchWorkspaceFiles: () => Promise<void>;
@@ -215,14 +279,25 @@ interface Store {
215
279
  clientCode: string;
216
280
  clientLang: string;
217
281
  fileId?: string;
282
+ /** If set, the client panel opens in React or Next.js preview mode instead of script mode */
283
+ clientType?: "script" | "react" | "nextjs" | "module-federation";
284
+ reactFiles?: Record<string, string> | null;
285
+ reactActiveFile?: string | null;
218
286
  } | null;
219
- openCodeRunner: (code?: string, language?: string) => void;
287
+ /** When set, the script runner opens with this file pre-loaded — Save will overwrite it */
288
+ runnerInitialFileId: string | null;
289
+ openCodeRunner: (code?: string, language?: string, fileId?: string) => void;
220
290
  openSandbox: (
221
291
  serverCode: string,
222
292
  serverLang: string,
223
293
  clientCode: string,
224
294
  clientLang: string,
225
295
  fileId?: string,
296
+ opts?: {
297
+ clientType?: "script" | "react" | "nextjs" | "module-federation";
298
+ reactFiles?: Record<string, string>;
299
+ reactActiveFile?: string;
300
+ },
226
301
  ) => void;
227
302
  overwriteContextFileContent: (
228
303
  questionId: string,
@@ -235,6 +310,33 @@ interface Store {
235
310
  label: string,
236
311
  ) => Promise<void>;
237
312
  closeCodeRunner: () => void;
313
+
314
+ // ── Infra Lab ────────────────────────────────────────────────
315
+ showInfraLab: boolean;
316
+ runnerInitialInfra: InfraLabWorkspace | null;
317
+ runnerInitialInfraFileId: string | null;
318
+ openInfraLab: (workspace?: InfraLabWorkspace, fileId?: string) => void;
319
+ closeInfraLab: () => void;
320
+
321
+ // ── Frontend Labs (React / Next.js / Module Federation) — open inside the sandbox ──
322
+ openReactLab: (
323
+ workspace?: FrontendLabWorkspace,
324
+ fileId?: string,
325
+ serverCode?: string,
326
+ serverLang?: string,
327
+ ) => void;
328
+ openNextLab: (
329
+ workspace?: FrontendLabWorkspace,
330
+ fileId?: string,
331
+ serverCode?: string,
332
+ serverLang?: string,
333
+ ) => void;
334
+ openModuleFederationLab: (
335
+ workspace?: FrontendLabWorkspace,
336
+ fileId?: string,
337
+ serverCode?: string,
338
+ serverLang?: string,
339
+ ) => void;
238
340
  }
239
341
 
240
342
  export const useStore = create<Store>((set, get) => ({
@@ -259,6 +361,10 @@ export const useStore = create<Store>((set, get) => ({
259
361
  runnerInitialCode: "",
260
362
  runnerInitialLanguage: "typescript",
261
363
  runnerInitialSandbox: null,
364
+ runnerInitialFileId: null,
365
+ showInfraLab: false,
366
+ runnerInitialInfra: null,
367
+ runnerInitialInfraFileId: null,
262
368
 
263
369
  // ── Workspaces ───────────────────────────────────────────────
264
370
  workspaces: [],
@@ -448,6 +554,13 @@ export const useStore = create<Store>((set, get) => ({
448
554
  }));
449
555
  },
450
556
 
557
+ updateTopicSystemContext: async (id, systemContext) => {
558
+ await api.updateTopic(id, { systemContext });
559
+ set((s) => ({
560
+ topics: s.topics.map((t) => (t.id === id ? { ...t, systemContext } : t)),
561
+ }));
562
+ },
563
+
451
564
  addQuestion: async (topicId, title) => {
452
565
  const question = await api.createQuestion(topicId, title);
453
566
  set((s) => ({
@@ -468,6 +581,29 @@ export const useStore = create<Store>((set, get) => ({
468
581
  }));
469
582
  },
470
583
 
584
+ moveQuestion: async (questionId, topicId, parentQuestionId) => {
585
+ await api.updateQuestion(questionId, {
586
+ parentQuestionId,
587
+ });
588
+ set((s) => ({
589
+ questionsByTopic: {
590
+ ...s.questionsByTopic,
591
+ [topicId]: (s.questionsByTopic[topicId] || []).map((q) =>
592
+ q.id === questionId
593
+ ? { ...q, parentQuestionId: parentQuestionId ?? undefined }
594
+ : q,
595
+ ),
596
+ },
597
+ currentQuestion:
598
+ s.currentQuestion?.id === questionId
599
+ ? {
600
+ ...s.currentQuestion,
601
+ parentQuestionId: parentQuestionId ?? undefined,
602
+ }
603
+ : s.currentQuestion,
604
+ }));
605
+ },
606
+
471
607
  removeQuestion: async (questionId, topicId) => {
472
608
  await api.deleteQuestion(questionId);
473
609
  set((s) => ({
@@ -682,6 +818,38 @@ export const useStore = create<Store>((set, get) => ({
682
818
  }));
683
819
  },
684
820
 
821
+ linkConversation: async (questionId, linkedId) => {
822
+ const q = get().currentQuestion;
823
+ const current =
824
+ q?.id === questionId ? q : await api.fetchQuestion(questionId);
825
+ const existing = current.linkedConversationIds ?? [];
826
+ if (existing.includes(linkedId)) return;
827
+ const updated = [...existing, linkedId];
828
+ await api.updateQuestion(questionId, { linkedConversationIds: updated });
829
+ set((s) => ({
830
+ currentQuestion:
831
+ s.currentQuestion?.id === questionId
832
+ ? { ...s.currentQuestion, linkedConversationIds: updated }
833
+ : s.currentQuestion,
834
+ }));
835
+ },
836
+
837
+ unlinkConversation: async (questionId, linkedId) => {
838
+ const q = get().currentQuestion;
839
+ const current =
840
+ q?.id === questionId ? q : await api.fetchQuestion(questionId);
841
+ const updated = (current.linkedConversationIds ?? []).filter(
842
+ (id) => id !== linkedId,
843
+ );
844
+ await api.updateQuestion(questionId, { linkedConversationIds: updated });
845
+ set((s) => ({
846
+ currentQuestion:
847
+ s.currentQuestion?.id === questionId
848
+ ? { ...s.currentQuestion, linkedConversationIds: updated }
849
+ : s.currentQuestion,
850
+ }));
851
+ },
852
+
685
853
  fetchWorkspaceFiles: async () => {
686
854
  const files = await api.fetchWorkspaceFiles();
687
855
  set({ workspaceFiles: files });
@@ -727,16 +895,26 @@ export const useStore = create<Store>((set, get) => ({
727
895
  openDocViewer: (fileId, quote, fileName) =>
728
896
  set({ viewingDoc: { fileId, quote, fileName } }),
729
897
  closeDocViewer: () => set({ viewingDoc: null }),
730
- openCodeRunner: (code = "", language = "typescript") =>
898
+ openCodeRunner: (code = "", language = "typescript", fileId?) =>
731
899
  set({
732
900
  showCodeRunner: true,
901
+ showInfraLab: false,
733
902
  runnerInitialCode: code,
734
903
  runnerInitialLanguage: language,
735
904
  runnerInitialSandbox: null,
905
+ runnerInitialFileId: fileId ?? null,
736
906
  }),
737
- openSandbox: (serverCode, serverLang, clientCode, clientLang, fileId?) =>
907
+ openSandbox: (
908
+ serverCode,
909
+ serverLang,
910
+ clientCode,
911
+ clientLang,
912
+ fileId?,
913
+ opts?,
914
+ ) =>
738
915
  set({
739
916
  showCodeRunner: true,
917
+ showInfraLab: false,
740
918
  runnerInitialCode: "",
741
919
  runnerInitialLanguage: "typescript",
742
920
  runnerInitialSandbox: {
@@ -745,7 +923,72 @@ export const useStore = create<Store>((set, get) => ({
745
923
  clientCode,
746
924
  clientLang,
747
925
  fileId,
926
+ clientType: opts?.clientType,
927
+ reactFiles: opts?.reactFiles,
928
+ reactActiveFile: opts?.reactActiveFile,
929
+ },
930
+ runnerInitialFileId: null,
931
+ }),
932
+ openInfraLab: (workspace, fileId?) =>
933
+ set({
934
+ showInfraLab: true,
935
+ showCodeRunner: false,
936
+ runnerInitialInfra: workspace ?? null,
937
+ runnerInitialInfraFileId: fileId ?? null,
938
+ }),
939
+ openReactLab: (workspace?, fileId?, serverCode?, serverLang?) =>
940
+ set({
941
+ showCodeRunner: true,
942
+ showInfraLab: false,
943
+ runnerInitialCode: "",
944
+ runnerInitialLanguage: "typescript",
945
+ runnerInitialSandbox: {
946
+ serverCode: serverCode ?? DEFAULT_SERVER_CODE,
947
+ serverLang: serverLang ?? "typescript",
948
+ clientCode: "",
949
+ clientLang: "javascript",
950
+ fileId,
951
+ clientType: "react",
952
+ reactFiles: workspace?.files ?? null,
953
+ reactActiveFile: workspace?.activeFile ?? null,
954
+ },
955
+ runnerInitialFileId: null,
956
+ }),
957
+ openNextLab: (workspace?, fileId?, serverCode?, serverLang?) =>
958
+ set({
959
+ showCodeRunner: true,
960
+ showInfraLab: false,
961
+ runnerInitialCode: "",
962
+ runnerInitialLanguage: "typescript",
963
+ runnerInitialSandbox: {
964
+ serverCode: serverCode ?? DEFAULT_SERVER_CODE,
965
+ serverLang: serverLang ?? "typescript",
966
+ clientCode: "",
967
+ clientLang: "javascript",
968
+ fileId,
969
+ clientType: "nextjs",
970
+ reactFiles: workspace?.files ?? null,
971
+ reactActiveFile: workspace?.activeFile ?? null,
972
+ },
973
+ runnerInitialFileId: null,
974
+ }),
975
+ openModuleFederationLab: (workspace?, fileId?, serverCode?, serverLang?) =>
976
+ set({
977
+ showCodeRunner: true,
978
+ showInfraLab: false,
979
+ runnerInitialCode: "",
980
+ runnerInitialLanguage: "typescript",
981
+ runnerInitialSandbox: {
982
+ serverCode: serverCode ?? DEFAULT_SERVER_CODE,
983
+ serverLang: serverLang ?? "typescript",
984
+ clientCode: "",
985
+ clientLang: "javascript",
986
+ fileId,
987
+ clientType: "module-federation",
988
+ reactFiles: workspace?.files ?? null,
989
+ reactActiveFile: workspace?.activeFile ?? null,
748
990
  },
991
+ runnerInitialFileId: null,
749
992
  }),
750
993
 
751
994
  overwriteContextFileContent: async (questionId, fileId, content) => {
@@ -767,6 +1010,7 @@ export const useStore = create<Store>((set, get) => ({
767
1010
  }));
768
1011
  },
769
1012
  closeCodeRunner: () => set({ showCodeRunner: false }),
1013
+ closeInfraLab: () => set({ showInfraLab: false }),
770
1014
 
771
1015
  fetchAiSettings: async () => {
772
1016
  const settings = await api.fetchAiSettings();
@@ -1,3 +1,13 @@
1
+ export type ContextFileOrigin =
2
+ | "user"
3
+ | "ai"
4
+ | "upload"
5
+ | "sandbox"
6
+ | "infra"
7
+ | "react"
8
+ | "nextjs"
9
+ | "module-federation";
10
+
1
11
  export interface ContextFile {
2
12
  id: string;
3
13
  name: string;
@@ -6,14 +16,33 @@ export interface ContextFile {
6
16
  createdAt: string;
7
17
  /** Distinguishes how this file was added. 'upload' = user-uploaded doc,
8
18
  * 'user' = code saved from Code Runner, 'ai' = AI-generated code block,
9
- * 'sandbox' = paired server+client sandbox saved as JSON. */
10
- origin?: "user" | "ai" | "upload" | "sandbox";
19
+ * 'sandbox' = paired server+client sandbox saved as JSON,
20
+ * 'infra' = Terraform-style infra lab workspace saved as JSON. */
21
+ origin?: ContextFileOrigin;
11
22
  /** Language hint for code snippets (e.g. 'typescript', 'javascript'). */
12
23
  language?: string;
13
24
  /** Short display label for code snippets. */
14
25
  label?: string;
15
26
  }
16
27
 
28
+ export interface FrontendLabWorkspace {
29
+ version: 1;
30
+ label: string;
31
+ /** Determines defaults, entry file, and file-tree conventions. */
32
+ type: "react" | "nextjs" | "module-federation";
33
+ activeFile: string;
34
+ files: Record<string, string>;
35
+ }
36
+
37
+ export interface InfraLabWorkspace {
38
+ version: 1;
39
+ label: string;
40
+ provider: "aws";
41
+ executionMode: "plan-only" | "localstack";
42
+ activeFile: string;
43
+ files: Record<string, string>;
44
+ }
45
+
17
46
  export interface WorkspaceMeta {
18
47
  id: string;
19
48
  name: string;
@@ -37,6 +66,8 @@ export interface Topic {
37
66
  id: string;
38
67
  name: string;
39
68
  contextFiles: ContextFile[];
69
+ /** Topic-wide system prompt prepended to every question's context in this topic. */
70
+ systemContext?: string;
40
71
  createdAt: string;
41
72
  }
42
73
 
@@ -82,7 +113,7 @@ export interface ReadingBookmark {
82
113
  export interface Question {
83
114
  id: string;
84
115
  topicId: string;
85
- parentQuestionId?: string;
116
+ parentQuestionId?: string | null;
86
117
  title: string;
87
118
  systemContext: string;
88
119
  codeContextFiles: string[];
@@ -90,5 +121,7 @@ export interface Question {
90
121
  messages: Message[];
91
122
  annotations?: Annotation[];
92
123
  readingBookmark?: ReadingBookmark;
124
+ /** IDs of other questions in the same topic whose conversation history is injected as context. */
125
+ linkedConversationIds?: string[];
93
126
  createdAt: string;
94
127
  }
@@ -1 +1 @@
1
- {"root":["./src/app.tsx","./src/api.ts","./src/main.tsx","./src/store.ts","./src/types.ts","./src/vite-env.d.ts","./src/components/chatmessage.tsx","./src/components/chatview.tsx","./src/components/codecontextpanel.tsx","./src/components/fileattachments.tsx","./src/components/fileviewermodal.tsx","./src/components/markdownrenderer.tsx","./src/components/mermaiddiagram.tsx","./src/components/sidebar.tsx","./src/components/textannotator.tsx"],"version":"5.9.3"}
1
+ {"root":["./src/app.tsx","./src/api.ts","./src/main.tsx","./src/store.ts","./src/types.ts","./src/vite-env.d.ts","./src/components/aisettingsmodal.tsx","./src/components/annotationdialog.tsx","./src/components/chatmessage.tsx","./src/components/chatview.tsx","./src/components/codecontextpanel.tsx","./src/components/codelineannotationpopup.tsx","./src/components/coderunnermodal.tsx","./src/components/docrefmodal.tsx","./src/components/fileattachments.tsx","./src/components/filepickermodal.tsx","./src/components/fileviewermodal.tsx","./src/components/linkedconvospicker.tsx","./src/components/markdownrenderer.tsx","./src/components/mermaiddiagram.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.4.0"
2
+ "version": "0.5.0"
3
3
  }
@@ -357,7 +357,11 @@ export async function syncWorkspace(
357
357
  cs.content &&
358
358
  (cs.origin === "user" ||
359
359
  cs.origin === "ai" ||
360
- cs.origin === "sandbox")
360
+ cs.origin === "sandbox" ||
361
+ cs.origin === "react" ||
362
+ cs.origin === "nextjs" ||
363
+ cs.origin === "module-federation" ||
364
+ cs.origin === "infra")
361
365
  ) {
362
366
  try {
363
367
  await fs.mkdir(ctxDir, { recursive: true });
@@ -562,7 +566,35 @@ async function getExportDriveClient(): Promise<drive_v3.Drive> {
562
566
  /* ok */
563
567
  }
564
568
  });
565
- return google.drive({ version: "v3", auth: client });
569
+
570
+ // Wrap the drive client so any invalid_grant errors clear the stored token
571
+ // and surface a friendlier error that the caller can detect.
572
+ const drive = google.drive({ version: "v3", auth: client });
573
+ const originalFiles = drive.files as any;
574
+ const patchMethod = (obj: any, method: string) => {
575
+ const orig = obj[method].bind(obj);
576
+ obj[method] = async (...args: any[]) => {
577
+ try {
578
+ return await orig(...args);
579
+ } catch (err: any) {
580
+ if (err?.message?.includes("invalid_grant") || err?.code === 400) {
581
+ try {
582
+ await fs.unlink(EXPORT_TOKENS_FILE);
583
+ } catch {
584
+ /* already gone */
585
+ }
586
+ const e = new Error("NEEDS_REAUTH");
587
+ (e as any).needsReauth = true;
588
+ throw e;
589
+ }
590
+ throw err;
591
+ }
592
+ };
593
+ };
594
+ ["create", "update", "delete", "list", "get"].forEach((m) =>
595
+ patchMethod(originalFiles, m),
596
+ );
597
+ return drive;
566
598
  }
567
599
 
568
600
  export interface ExportResult {
@@ -736,7 +768,11 @@ export async function exportWorkspace(
736
768
  if (
737
769
  cf.origin === "user" ||
738
770
  cf.origin === "ai" ||
739
- cf.origin === "sandbox"
771
+ cf.origin === "sandbox" ||
772
+ cf.origin === "react" ||
773
+ cf.origin === "nextjs" ||
774
+ cf.origin === "module-federation" ||
775
+ cf.origin === "infra"
740
776
  ) {
741
777
  try {
742
778
  const content = await fs.readFile(