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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-interview-cockpit",
3
- "version": "0.17.3",
3
+ "version": "0.18.0",
4
4
  "description": "Scaffold a personal AI-powered interview prep cockpit",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,6 +9,7 @@ import DocRefModal from "./components/DocRefModal";
9
9
  import AiSettingsModal from "./components/AiSettingsModal";
10
10
  import CodeRunnerModal from "./components/CodeRunnerModal";
11
11
  import InfraLabModal from "./components/InfraLabModal";
12
+ import GithubActionsLabModal from "./components/GithubActionsLabModal";
12
13
  import DeploymentLabModal from "./components/DeploymentLabModal";
13
14
  import CanvasLabModal from "./components/CanvasLabModal";
14
15
  import {
@@ -44,6 +45,7 @@ export default function App() {
44
45
  closeSettings,
45
46
  showCodeRunner,
46
47
  showInfraLab,
48
+ showGhaLab,
47
49
  showDeploymentLab,
48
50
  showCanvasLab,
49
51
  closeCodeRunner,
@@ -191,6 +193,7 @@ export default function App() {
191
193
  {showSettings && <AiSettingsModal />}
192
194
  {showCodeRunner && <CodeRunnerModal />}
193
195
  {showInfraLab && <InfraLabModal />}
196
+ {showGhaLab && <GithubActionsLabModal />}
194
197
  {showDeploymentLab && <DeploymentLabModal />}
195
198
  {showCanvasLab && <CanvasLabModal />}
196
199
  </div>
@@ -5,6 +5,7 @@ import type {
5
5
  ContextFileOrigin,
6
6
  WorkspacesRegistry,
7
7
  InfraLabWorkspace,
8
+ GithubActionsLabWorkspace,
8
9
  } from "./types";
9
10
 
10
11
  const BASE = "/api";
@@ -79,8 +80,8 @@ export interface InfraRunListItem {
79
80
  startedAt: string;
80
81
  completedAt: string;
81
82
  durationMs: number;
82
- provider: "aws";
83
- executionMode: "plan-only" | "localstack";
83
+ provider: "aws" | "docker";
84
+ executionMode: "plan-only" | "localstack" | "docker";
84
85
  diagnostics: InfraDiagnostic[];
85
86
  planSummary?: InfraPlanSummary;
86
87
  error?: string;
@@ -243,6 +244,19 @@ export async function updateQuestion(
243
244
  return res.json();
244
245
  }
245
246
 
247
+ export async function copyQuestion(
248
+ id: string,
249
+ data: { parentQuestionId?: string | null; targetTopicId?: string } = {},
250
+ ): Promise<Question[]> {
251
+ const res = await fetch(`${BASE}/questions/${id}/copy`, {
252
+ method: "POST",
253
+ headers: { "Content-Type": "application/json" },
254
+ body: JSON.stringify(data),
255
+ });
256
+ if (!res.ok) throw new Error(await res.text());
257
+ return res.json();
258
+ }
259
+
246
260
  export async function deleteQuestion(id: string): Promise<void> {
247
261
  await fetch(`${BASE}/questions/${id}`, { method: "DELETE" });
248
262
  }
@@ -470,6 +484,60 @@ export async function streamInfraCommand(
470
484
  }
471
485
  }
472
486
 
487
+ // ─── GitHub Actions Lab ──────────────────────────────────────────────────
488
+
489
+ export type GhaStreamMessage =
490
+ | { type: "output"; kind: "stdout" | "stderr" | "info"; text: string }
491
+ | { type: "complete"; runId: string; exitCode: number; durationMs: number }
492
+ | { type: "error"; error: string };
493
+
494
+ export async function streamGhaCommand(
495
+ input: {
496
+ questionId?: string;
497
+ fileId?: string;
498
+ label?: string;
499
+ command: string;
500
+ workspace: GithubActionsLabWorkspace;
501
+ },
502
+ onMessage: (message: GhaStreamMessage) => void,
503
+ ): Promise<void> {
504
+ const res = await fetch(`${BASE}/gha/run-stream`, {
505
+ method: "POST",
506
+ headers: { "Content-Type": "application/json" },
507
+ body: JSON.stringify(input),
508
+ });
509
+ if (!res.ok || !res.body) {
510
+ const err = await res.text().catch(() => "act run failed");
511
+ throw new Error(err || "act run failed");
512
+ }
513
+ const reader = res.body.getReader();
514
+ const decoder = new TextDecoder();
515
+ let buffer = "";
516
+
517
+ const flushBuffer = () => {
518
+ const events = buffer.replace(/\r/g, "").split("\n\n");
519
+ buffer = events.pop() ?? "";
520
+ for (const event of events) {
521
+ if (!event.trim()) continue;
522
+ const payload = event
523
+ .split("\n")
524
+ .filter((line) => line.startsWith("data:"))
525
+ .map((line) => line.slice(5).trimStart())
526
+ .join("\n");
527
+ if (!payload) continue;
528
+ onMessage(JSON.parse(payload) as GhaStreamMessage);
529
+ }
530
+ };
531
+
532
+ while (true) {
533
+ const { value, done } = await reader.read();
534
+ buffer += decoder.decode(value ?? new Uint8Array(), { stream: !done });
535
+ flushBuffer();
536
+ if (done) break;
537
+ }
538
+ if (buffer.trim()) flushBuffer();
539
+ }
540
+
473
541
  export async function fetchInfraRun(runId: string): Promise<InfraRunDetails> {
474
542
  const res = await fetch(`${BASE}/infra/runs/${encodeURIComponent(runId)}`);
475
543
  if (!res.ok) throw new Error(await res.text());
@@ -664,12 +732,19 @@ export async function activateWorkspaceApi(
664
732
  return res.json();
665
733
  }
666
734
 
667
- export async function syncWorkspaceApi(id: string): Promise<{
668
- topicsUpserted: number;
669
- filesImported: number;
670
- filesSkipped: number;
671
- errors: string[];
672
- }> {
735
+ export type SyncWorkspaceResult =
736
+ | { needsAuth: true; authUrl: string }
737
+ | {
738
+ needsAuth?: false;
739
+ topicsUpserted: number;
740
+ filesImported: number;
741
+ filesSkipped: number;
742
+ errors: string[];
743
+ };
744
+
745
+ export async function syncWorkspaceApi(
746
+ id: string,
747
+ ): Promise<SyncWorkspaceResult> {
673
748
  const res = await fetch(`${BASE}/workspaces/${id}/sync`, { method: "POST" });
674
749
  return res.json();
675
750
  }