create-interview-cockpit 0.18.0 → 0.20.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.
@@ -36,7 +36,7 @@ import {
36
36
  runInfraAction,
37
37
  streamInfraCommand,
38
38
  } from "./infra-runner.js";
39
- import { streamGhaCommand } from "./gha-runner.js";
39
+ import { streamGhaCommand, listGhaRuns, getGhaRun } from "./gha-runner.js";
40
40
 
41
41
  const app = express();
42
42
  app.use(cors());
@@ -373,6 +373,39 @@ app.post("/api/workspaces/:id/sync", async (req, res) => {
373
373
  }
374
374
  });
375
375
 
376
+ app.post("/api/workspaces/:id/topics/:topicId/sync", async (req, res) => {
377
+ try {
378
+ // Pull only this topic's Drive folder into the selected workspace.
379
+ storage.setActiveWorkspaceId(req.params.id);
380
+ const result = await googleDrive.syncTopic(
381
+ req.params.id,
382
+ req.params.topicId,
383
+ extractText,
384
+ );
385
+ if (result.errors.some((error) => /\b403\b/.test(error))) {
386
+ return res.json({
387
+ ...result,
388
+ needsAuth: true,
389
+ authUrl: googleDrive.getExportAuthUrl(),
390
+ });
391
+ }
392
+ res.json(result);
393
+ } catch (err: any) {
394
+ if (err?.needsReauth || err?.message === "NEEDS_REAUTH") {
395
+ return res.json({
396
+ needsAuth: true,
397
+ authUrl: googleDrive.getExportAuthUrl(),
398
+ topicsUpserted: 0,
399
+ filesImported: 0,
400
+ filesSkipped: 0,
401
+ errors: ["Google Drive authorization is required to pull this topic."],
402
+ });
403
+ }
404
+ console.error("[sync-topic]", err);
405
+ res.status(500).json({ error: err?.message || "Topic sync failed" });
406
+ }
407
+ });
408
+
376
409
  app.get("/api/workspaces/:id/drive-subfolders", async (req, res) => {
377
410
  try {
378
411
  const folders = await googleDrive.listDriveSubfolders(req.params.id);
@@ -452,6 +485,30 @@ app.post("/api/workspaces/:id/export-drive", async (req, res) => {
452
485
  }
453
486
  });
454
487
 
488
+ app.post(
489
+ "/api/workspaces/:id/topics/:topicId/export-drive",
490
+ async (req, res) => {
491
+ try {
492
+ if (!(await googleDrive.isExportAuthed())) {
493
+ return res.json({
494
+ needsAuth: true,
495
+ authUrl: googleDrive.getExportAuthUrl(),
496
+ });
497
+ }
498
+ const { targetFolderId } = req.body as { targetFolderId?: string };
499
+ const result = await googleDrive.exportTopic(
500
+ req.params.id,
501
+ req.params.topicId,
502
+ targetFolderId,
503
+ );
504
+ res.json(result);
505
+ } catch (err: any) {
506
+ console.error("[export-topic-drive]", err);
507
+ res.status(500).json({ error: err?.message || "Topic export failed" });
508
+ }
509
+ },
510
+ );
511
+
455
512
  app.post("/api/drive/repair-permissions", async (req, res) => {
456
513
  try {
457
514
  const { folderId } = req.body as { folderId: string };
@@ -1518,6 +1575,35 @@ app.post("/api/gha/run-stream", async (req, res) => {
1518
1575
  res.end();
1519
1576
  });
1520
1577
 
1578
+ // List historical act runs, optionally scoped to a lab file or question.
1579
+ app.get("/api/gha/runs", async (req, res) => {
1580
+ try {
1581
+ const { questionId, fileId, limit } = req.query as {
1582
+ questionId?: string;
1583
+ fileId?: string;
1584
+ limit?: string;
1585
+ };
1586
+ const parsedLimit = limit ? Number(limit) : undefined;
1587
+ const runs = await listGhaRuns({
1588
+ questionId,
1589
+ fileId,
1590
+ ...(Number.isFinite(parsedLimit) ? { limit: parsedLimit } : {}),
1591
+ });
1592
+ res.json(runs);
1593
+ } catch (err: any) {
1594
+ res.status(500).json({ error: err?.message || "Failed to list gha runs" });
1595
+ }
1596
+ });
1597
+
1598
+ app.get("/api/gha/runs/:runId", async (req, res) => {
1599
+ try {
1600
+ const run = await getGhaRun(req.params.runId);
1601
+ res.json(run);
1602
+ } catch {
1603
+ res.status(404).json({ error: "GHA run not found" });
1604
+ }
1605
+ });
1606
+
1521
1607
  // Link an existing file to a topic without re-uploading
1522
1608
  app.post("/api/topics/:topicId/context-files/link", async (req, res) => {
1523
1609
  const { fileId, originalName } = req.body as {