create-interview-cockpit 0.18.0 → 0.19.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 +1 -1
- package/template/client/src/api.ts +101 -0
- package/template/client/src/components/GhaHistoryPanel.tsx +194 -0
- package/template/client/src/components/GhaJobsPanel.tsx +432 -0
- package/template/client/src/components/GithubActionsLabModal.tsx +376 -74
- package/template/client/src/components/Sidebar.tsx +216 -59
- package/template/client/src/githubActionsLab.ts +7 -0
- package/template/client/src/store.ts +47 -0
- package/template/client/src/types.ts +6 -0
- package/template/client/tsconfig.tsbuildinfo +1 -1
- package/template/cockpit.json +1 -1
- package/template/server/src/gha-runner.ts +325 -0
- package/template/server/src/google-drive.ts +507 -125
- package/template/server/src/index.ts +87 -1
|
@@ -119,7 +119,9 @@ export default function Sidebar() {
|
|
|
119
119
|
selectDriveSubfolder,
|
|
120
120
|
clearDriveSubfolder,
|
|
121
121
|
syncWorkspace,
|
|
122
|
+
syncTopic,
|
|
122
123
|
exportWorkspace,
|
|
124
|
+
exportTopic,
|
|
123
125
|
workspaceFiles,
|
|
124
126
|
uploadWorkspaceFiles,
|
|
125
127
|
removeWorkspaceFile,
|
|
@@ -162,6 +164,7 @@ export default function Sidebar() {
|
|
|
162
164
|
const [openMenuQuestionId, setOpenMenuQuestionId] = useState<string | null>(
|
|
163
165
|
null,
|
|
164
166
|
);
|
|
167
|
+
const [openMenuTopicId, setOpenMenuTopicId] = useState<string | null>(null);
|
|
165
168
|
const [openTopicPrompts, setOpenTopicPrompts] = useState<Set<string>>(
|
|
166
169
|
new Set(),
|
|
167
170
|
);
|
|
@@ -216,6 +219,11 @@ export default function Sidebar() {
|
|
|
216
219
|
const [navigating, setNavigating] = useState(false);
|
|
217
220
|
const [syncing, setSyncing] = useState(false);
|
|
218
221
|
const [pushing, setPushing] = useState(false);
|
|
222
|
+
const [topicSyncingId, setTopicSyncingId] = useState<string | null>(null);
|
|
223
|
+
const [topicPushingId, setTopicPushingId] = useState<string | null>(null);
|
|
224
|
+
const [topicDriveStatus, setTopicDriveStatus] = useState<
|
|
225
|
+
Record<string, string>
|
|
226
|
+
>({});
|
|
219
227
|
const [wsFilesExpanded, setWsFilesExpanded] = useState(true);
|
|
220
228
|
const [driveFileSyncStatus, setDriveFileSyncStatus] = useState<string | null>(
|
|
221
229
|
null,
|
|
@@ -294,6 +302,61 @@ export default function Sidebar() {
|
|
|
294
302
|
}
|
|
295
303
|
};
|
|
296
304
|
|
|
305
|
+
const setTopicStatus = (topicId: string, value: string) => {
|
|
306
|
+
setTopicDriveStatus((prev) => ({ ...prev, [topicId]: value }));
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const handlePullTopicFromDrive = async (topicId: string) => {
|
|
310
|
+
if (!activeWorkspaceId || !activeWs?.driveConfig?.folderId) return;
|
|
311
|
+
setTopicSyncingId(topicId);
|
|
312
|
+
setTopicStatus(topicId, "Pulling topic from Drive…");
|
|
313
|
+
try {
|
|
314
|
+
const result = await syncTopic(activeWorkspaceId, topicId);
|
|
315
|
+
if ("needsAuth" in result && result.needsAuth) {
|
|
316
|
+
window.location.href = result.authUrl;
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const firstError = result.errors[0];
|
|
320
|
+
setTopicStatus(
|
|
321
|
+
topicId,
|
|
322
|
+
result.errors.length > 0
|
|
323
|
+
? `Topic pull finished with ${result.errors.length} error${result.errors.length === 1 ? "" : "s"}. ${firstError ? `First: ${firstError}` : ""}`
|
|
324
|
+
: `Pulled ${result.filesImported} file${result.filesImported === 1 ? "" : "s"} into this topic.`,
|
|
325
|
+
);
|
|
326
|
+
} catch (err: any) {
|
|
327
|
+
setTopicStatus(topicId, err?.message || "Topic pull failed.");
|
|
328
|
+
} finally {
|
|
329
|
+
setTopicSyncingId(null);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const handlePushTopicToDrive = async (topicId: string) => {
|
|
334
|
+
if (!activeWorkspaceId || !activeWs?.driveConfig?.folderId) return;
|
|
335
|
+
setTopicPushingId(topicId);
|
|
336
|
+
setTopicStatus(topicId, "Pushing topic to Drive…");
|
|
337
|
+
try {
|
|
338
|
+
const result = await exportTopic(
|
|
339
|
+
activeWorkspaceId,
|
|
340
|
+
topicId,
|
|
341
|
+
activeWs.driveConfig.subFolderId,
|
|
342
|
+
);
|
|
343
|
+
if ("needsAuth" in result && result.needsAuth) {
|
|
344
|
+
window.location.href = result.authUrl;
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
setTopicStatus(
|
|
348
|
+
topicId,
|
|
349
|
+
result.errors.length > 0
|
|
350
|
+
? `Topic push finished with ${result.errors.length} error${result.errors.length === 1 ? "" : "s"}.`
|
|
351
|
+
: `Pushed ${result.questionsExported} question${result.questionsExported === 1 ? "" : "s"} and ${result.filesExported} file${result.filesExported === 1 ? "" : "s"} to Drive.`,
|
|
352
|
+
);
|
|
353
|
+
} catch (err: any) {
|
|
354
|
+
setTopicStatus(topicId, err?.message || "Topic push failed.");
|
|
355
|
+
} finally {
|
|
356
|
+
setTopicPushingId(null);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
297
360
|
useEffect(() => {
|
|
298
361
|
if (editingTopicId || editingQuestionId) {
|
|
299
362
|
editInputRef.current?.select();
|
|
@@ -1194,6 +1257,9 @@ export default function Sidebar() {
|
|
|
1194
1257
|
sensitivity: "base",
|
|
1195
1258
|
}),
|
|
1196
1259
|
);
|
|
1260
|
+
const isTopicMenuOpen = openMenuTopicId === topic.id;
|
|
1261
|
+
const topicBusy =
|
|
1262
|
+
topicSyncingId === topic.id || topicPushingId === topic.id;
|
|
1197
1263
|
|
|
1198
1264
|
return (
|
|
1199
1265
|
<div key={topic.id}>
|
|
@@ -1238,69 +1304,160 @@ export default function Sidebar() {
|
|
|
1238
1304
|
</span>
|
|
1239
1305
|
</button>
|
|
1240
1306
|
{editingTopicId !== topic.id && (
|
|
1241
|
-
<
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
setEditingTopicId(topic.id);
|
|
1245
|
-
setEditingTopicName(topic.name);
|
|
1246
|
-
}}
|
|
1247
|
-
className="p-0.5 rounded opacity-0 group-hover:opacity-100 text-slate-600 hover:text-cyan-400 transition-all"
|
|
1248
|
-
title="Rename"
|
|
1307
|
+
<div
|
|
1308
|
+
className="relative shrink-0 flex items-center"
|
|
1309
|
+
onClick={(e) => e.stopPropagation()}
|
|
1249
1310
|
>
|
|
1250
|
-
|
|
1251
|
-
|
|
1311
|
+
{topicBusy && !isTopicMenuOpen ? (
|
|
1312
|
+
<Loader2 className="w-3 h-3 animate-spin text-cyan-400" />
|
|
1313
|
+
) : (
|
|
1314
|
+
<button
|
|
1315
|
+
onClick={() =>
|
|
1316
|
+
setOpenMenuTopicId(
|
|
1317
|
+
isTopicMenuOpen ? null : topic.id,
|
|
1318
|
+
)
|
|
1319
|
+
}
|
|
1320
|
+
className={`p-0.5 rounded transition-all ${
|
|
1321
|
+
isTopicMenuOpen
|
|
1322
|
+
? "text-cyan-400"
|
|
1323
|
+
: "opacity-0 group-hover:opacity-100 text-slate-600 hover:text-slate-300"
|
|
1324
|
+
}`}
|
|
1325
|
+
title="Topic options"
|
|
1326
|
+
>
|
|
1327
|
+
<MoreHorizontal className="w-3.5 h-3.5" />
|
|
1328
|
+
</button>
|
|
1329
|
+
)}
|
|
1330
|
+
|
|
1331
|
+
{isTopicMenuOpen && (
|
|
1332
|
+
<>
|
|
1333
|
+
<div
|
|
1334
|
+
className="fixed inset-0 z-40"
|
|
1335
|
+
onClick={() => setOpenMenuTopicId(null)}
|
|
1336
|
+
/>
|
|
1337
|
+
<div className="absolute right-0 top-full mt-0.5 z-50 bg-slate-800 border border-slate-700 rounded-md shadow-xl min-w-[170px] py-0.5">
|
|
1338
|
+
<button
|
|
1339
|
+
onClick={() => {
|
|
1340
|
+
setOpenMenuTopicId(null);
|
|
1341
|
+
setEditingTopicId(topic.id);
|
|
1342
|
+
setEditingTopicName(topic.name);
|
|
1343
|
+
}}
|
|
1344
|
+
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-slate-300 hover:bg-slate-700 hover:text-white transition-colors"
|
|
1345
|
+
>
|
|
1346
|
+
<Pencil className="w-3 h-3" /> Rename
|
|
1347
|
+
</button>
|
|
1348
|
+
<button
|
|
1349
|
+
onClick={() => {
|
|
1350
|
+
setOpenMenuTopicId(null);
|
|
1351
|
+
setAddingQuestionTo(topic.id);
|
|
1352
|
+
setNewQuestionTitle("");
|
|
1353
|
+
if (!isExpanded) toggleTopic(topic.id);
|
|
1354
|
+
}}
|
|
1355
|
+
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-slate-300 hover:bg-slate-700 hover:text-white transition-colors"
|
|
1356
|
+
>
|
|
1357
|
+
<Plus className="w-3 h-3" /> Add question
|
|
1358
|
+
</button>
|
|
1359
|
+
<button
|
|
1360
|
+
onClick={async () => {
|
|
1361
|
+
setOpenMenuTopicId(null);
|
|
1362
|
+
let topicQuestions =
|
|
1363
|
+
questionsByTopic[topic.id] ?? [];
|
|
1364
|
+
try {
|
|
1365
|
+
topicQuestions = await api.fetchQuestions(
|
|
1366
|
+
topic.id,
|
|
1367
|
+
);
|
|
1368
|
+
} catch {
|
|
1369
|
+
// Fall back to the already-loaded sidebar snapshot.
|
|
1370
|
+
}
|
|
1371
|
+
const rootQuestions = topicQuestions.filter(
|
|
1372
|
+
(q) => !q.parentQuestionId,
|
|
1373
|
+
);
|
|
1374
|
+
const exportedQuestions = await Promise.all(
|
|
1375
|
+
rootQuestions.map((q) =>
|
|
1376
|
+
buildQuestionExport(q, topicQuestions),
|
|
1377
|
+
),
|
|
1378
|
+
);
|
|
1379
|
+
downloadJson(
|
|
1380
|
+
{
|
|
1381
|
+
id: topic.id,
|
|
1382
|
+
name: topic.name,
|
|
1383
|
+
systemContext: topic.systemContext ?? "",
|
|
1384
|
+
contextFiles: topic.contextFiles,
|
|
1385
|
+
createdAt: topic.createdAt,
|
|
1386
|
+
questions: exportedQuestions,
|
|
1387
|
+
},
|
|
1388
|
+
`${topic.name.replace(/[^a-z0-9]+/gi, "-")}.json`,
|
|
1389
|
+
);
|
|
1390
|
+
}}
|
|
1391
|
+
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-slate-300 hover:bg-slate-700 hover:text-white transition-colors"
|
|
1392
|
+
>
|
|
1393
|
+
<Download className="w-3 h-3" /> Download
|
|
1394
|
+
</button>
|
|
1395
|
+
|
|
1396
|
+
{canSyncDriveFolder && (
|
|
1397
|
+
<>
|
|
1398
|
+
<div className="border-t border-slate-700 my-0.5" />
|
|
1399
|
+
<button
|
|
1400
|
+
onClick={() => {
|
|
1401
|
+
setOpenMenuTopicId(null);
|
|
1402
|
+
void handlePushTopicToDrive(topic.id);
|
|
1403
|
+
}}
|
|
1404
|
+
disabled={topicBusy || pushing || syncing}
|
|
1405
|
+
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-cyan-300 hover:bg-slate-700 hover:text-cyan-200 disabled:opacity-50 transition-colors"
|
|
1406
|
+
>
|
|
1407
|
+
{topicPushingId === topic.id ? (
|
|
1408
|
+
<Loader2 className="w-3 h-3 animate-spin" />
|
|
1409
|
+
) : (
|
|
1410
|
+
<Upload className="w-3 h-3" />
|
|
1411
|
+
)}
|
|
1412
|
+
Push topic
|
|
1413
|
+
</button>
|
|
1414
|
+
<button
|
|
1415
|
+
onClick={() => {
|
|
1416
|
+
setOpenMenuTopicId(null);
|
|
1417
|
+
void handlePullTopicFromDrive(topic.id);
|
|
1418
|
+
}}
|
|
1419
|
+
disabled={topicBusy || pushing || syncing}
|
|
1420
|
+
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-slate-300 hover:bg-slate-700 hover:text-cyan-300 disabled:opacity-50 transition-colors"
|
|
1421
|
+
>
|
|
1422
|
+
{topicSyncingId === topic.id ? (
|
|
1423
|
+
<Loader2 className="w-3 h-3 animate-spin" />
|
|
1424
|
+
) : (
|
|
1425
|
+
<RefreshCw className="w-3 h-3" />
|
|
1426
|
+
)}
|
|
1427
|
+
Pull topic
|
|
1428
|
+
</button>
|
|
1429
|
+
</>
|
|
1430
|
+
)}
|
|
1431
|
+
|
|
1432
|
+
<div className="border-t border-slate-700 my-0.5" />
|
|
1433
|
+
<button
|
|
1434
|
+
onClick={() => {
|
|
1435
|
+
setOpenMenuTopicId(null);
|
|
1436
|
+
if (
|
|
1437
|
+
confirm(
|
|
1438
|
+
`Delete topic "${topic.name}" and all its questions?`,
|
|
1439
|
+
)
|
|
1440
|
+
) {
|
|
1441
|
+
removeTopic(topic.id);
|
|
1442
|
+
}
|
|
1443
|
+
}}
|
|
1444
|
+
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-red-400 hover:bg-slate-700 hover:text-red-300 transition-colors"
|
|
1445
|
+
>
|
|
1446
|
+
<Trash2 className="w-3 h-3" /> Delete
|
|
1447
|
+
</button>
|
|
1448
|
+
</div>
|
|
1449
|
+
</>
|
|
1450
|
+
)}
|
|
1451
|
+
</div>
|
|
1252
1452
|
)}
|
|
1253
|
-
<button
|
|
1254
|
-
onClick={(e) => {
|
|
1255
|
-
e.stopPropagation();
|
|
1256
|
-
if (
|
|
1257
|
-
confirm(
|
|
1258
|
-
`Delete topic "${topic.name}" and all its questions?`,
|
|
1259
|
-
)
|
|
1260
|
-
) {
|
|
1261
|
-
removeTopic(topic.id);
|
|
1262
|
-
}
|
|
1263
|
-
}}
|
|
1264
|
-
className="p-0.5 rounded opacity-0 group-hover:opacity-100 text-slate-600 hover:text-red-400 transition-all"
|
|
1265
|
-
>
|
|
1266
|
-
<Trash2 className="w-3 h-3" />
|
|
1267
|
-
</button>
|
|
1268
|
-
<button
|
|
1269
|
-
onClick={async (e) => {
|
|
1270
|
-
e.stopPropagation();
|
|
1271
|
-
let topicQuestions = questionsByTopic[topic.id] ?? [];
|
|
1272
|
-
try {
|
|
1273
|
-
topicQuestions = await api.fetchQuestions(topic.id);
|
|
1274
|
-
} catch {
|
|
1275
|
-
// Fall back to the already-loaded sidebar snapshot.
|
|
1276
|
-
}
|
|
1277
|
-
const rootQuestions = topicQuestions.filter(
|
|
1278
|
-
(q) => !q.parentQuestionId,
|
|
1279
|
-
);
|
|
1280
|
-
const exportedQuestions = await Promise.all(
|
|
1281
|
-
rootQuestions.map((q) =>
|
|
1282
|
-
buildQuestionExport(q, topicQuestions),
|
|
1283
|
-
),
|
|
1284
|
-
);
|
|
1285
|
-
downloadJson(
|
|
1286
|
-
{
|
|
1287
|
-
id: topic.id,
|
|
1288
|
-
name: topic.name,
|
|
1289
|
-
systemContext: topic.systemContext ?? "",
|
|
1290
|
-
contextFiles: topic.contextFiles,
|
|
1291
|
-
createdAt: topic.createdAt,
|
|
1292
|
-
questions: exportedQuestions,
|
|
1293
|
-
},
|
|
1294
|
-
`${topic.name.replace(/[^a-z0-9]+/gi, "-")}.json`,
|
|
1295
|
-
);
|
|
1296
|
-
}}
|
|
1297
|
-
className="p-0.5 rounded opacity-0 group-hover:opacity-100 text-slate-600 hover:text-cyan-400 transition-all"
|
|
1298
|
-
title="Download topic as JSON"
|
|
1299
|
-
>
|
|
1300
|
-
<Download className="w-3 h-3" />
|
|
1301
|
-
</button>
|
|
1302
1453
|
</div>
|
|
1303
1454
|
|
|
1455
|
+
{topicDriveStatus[topic.id] && (
|
|
1456
|
+
<p className="px-3 pb-1 text-[10px] leading-relaxed text-slate-500">
|
|
1457
|
+
{topicDriveStatus[topic.id]}
|
|
1458
|
+
</p>
|
|
1459
|
+
)}
|
|
1460
|
+
|
|
1304
1461
|
{/* Questions list */}
|
|
1305
1462
|
{isExpanded && (
|
|
1306
1463
|
<div className="ml-3 border-l border-slate-800">
|
|
@@ -199,6 +199,10 @@ export function cloneGhaLabWorkspace(
|
|
|
199
199
|
f.startsWith(".github/workflows/"),
|
|
200
200
|
) || ".github/workflows/ci.yml",
|
|
201
201
|
files: sourceFiles,
|
|
202
|
+
// Preserve optional UX flags so they round-trip through save/load.
|
|
203
|
+
...(source.includeRunHistoryInContext
|
|
204
|
+
? { includeRunHistoryInContext: true }
|
|
205
|
+
: {}),
|
|
202
206
|
};
|
|
203
207
|
}
|
|
204
208
|
|
|
@@ -280,6 +284,9 @@ export function parseGhaLabWorkspace(
|
|
|
280
284
|
? parsed.defaultWorkflow
|
|
281
285
|
: DEFAULT_GHA_LAB.defaultWorkflow,
|
|
282
286
|
files,
|
|
287
|
+
...(parsed.includeRunHistoryInContext === true
|
|
288
|
+
? { includeRunHistoryInContext: true }
|
|
289
|
+
: {}),
|
|
283
290
|
});
|
|
284
291
|
} catch {
|
|
285
292
|
return null;
|
|
@@ -128,6 +128,10 @@ interface Store {
|
|
|
128
128
|
renameWorkspace: (id: string, name: string) => Promise<void>;
|
|
129
129
|
patchWorkspace: (id: string, data: object) => Promise<void>;
|
|
130
130
|
syncWorkspace: (id: string) => Promise<import("./api").SyncWorkspaceResult>;
|
|
131
|
+
syncTopic: (
|
|
132
|
+
workspaceId: string,
|
|
133
|
+
topicId: string,
|
|
134
|
+
) => Promise<import("./api").SyncWorkspaceResult>;
|
|
131
135
|
linkDriveFolder: (
|
|
132
136
|
workspaceId: string,
|
|
133
137
|
url: string,
|
|
@@ -144,6 +148,11 @@ interface Store {
|
|
|
144
148
|
id: string,
|
|
145
149
|
targetFolderId?: string,
|
|
146
150
|
) => Promise<import("./api").ExportWorkspaceResult>;
|
|
151
|
+
exportTopic: (
|
|
152
|
+
workspaceId: string,
|
|
153
|
+
topicId: string,
|
|
154
|
+
targetFolderId?: string,
|
|
155
|
+
) => Promise<import("./api").ExportWorkspaceResult>;
|
|
147
156
|
fetchDriveSubfolders: (id: string) => Promise<import("./api").DriveFolder[]>;
|
|
148
157
|
createDriveSubfolder: (
|
|
149
158
|
id: string,
|
|
@@ -494,6 +503,40 @@ export const useStore = create<Store>((set, get) => ({
|
|
|
494
503
|
return result;
|
|
495
504
|
},
|
|
496
505
|
|
|
506
|
+
syncTopic: async (workspaceId, topicId) => {
|
|
507
|
+
const result = await api.syncTopicApi(workspaceId, topicId);
|
|
508
|
+
if ("needsAuth" in result && result.needsAuth) {
|
|
509
|
+
return result;
|
|
510
|
+
}
|
|
511
|
+
if (workspaceId === get().activeWorkspaceId) {
|
|
512
|
+
const [topics, questions] = await Promise.all([
|
|
513
|
+
api.fetchTopics(),
|
|
514
|
+
api.fetchQuestions(topicId),
|
|
515
|
+
]);
|
|
516
|
+
set((s) => {
|
|
517
|
+
const selectedStillExists = questions.some(
|
|
518
|
+
(q) => q.id === s.selectedQuestionId,
|
|
519
|
+
);
|
|
520
|
+
const selectedWasInTopic = s.currentQuestion?.topicId === topicId;
|
|
521
|
+
return {
|
|
522
|
+
topics,
|
|
523
|
+
questionsByTopic: { ...s.questionsByTopic, [topicId]: questions },
|
|
524
|
+
selectedQuestionId:
|
|
525
|
+
selectedWasInTopic && !selectedStillExists
|
|
526
|
+
? null
|
|
527
|
+
: s.selectedQuestionId,
|
|
528
|
+
currentQuestion:
|
|
529
|
+
selectedWasInTopic && !selectedStillExists
|
|
530
|
+
? null
|
|
531
|
+
: s.currentQuestion,
|
|
532
|
+
};
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
const registry = await api.fetchWorkspaces();
|
|
536
|
+
set({ workspaces: registry.workspaces });
|
|
537
|
+
return result;
|
|
538
|
+
},
|
|
539
|
+
|
|
497
540
|
linkDriveFolder: async (workspaceId, url) => {
|
|
498
541
|
const { registry, folders } = await api.linkDriveFolder(workspaceId, url);
|
|
499
542
|
set({ workspaces: registry.workspaces, driveRootFolders: folders });
|
|
@@ -556,6 +599,10 @@ export const useStore = create<Store>((set, get) => ({
|
|
|
556
599
|
return api.exportWorkspaceToDrive(id, targetFolderId);
|
|
557
600
|
},
|
|
558
601
|
|
|
602
|
+
exportTopic: async (workspaceId, topicId, targetFolderId) => {
|
|
603
|
+
return api.exportTopicToDrive(workspaceId, topicId, targetFolderId);
|
|
604
|
+
},
|
|
605
|
+
|
|
559
606
|
fetchDriveSubfolders: async (id) => {
|
|
560
607
|
return api.fetchDriveSubfolders(id);
|
|
561
608
|
},
|
|
@@ -58,6 +58,12 @@ export interface GithubActionsLabWorkspace {
|
|
|
58
58
|
defaultEvent?: string;
|
|
59
59
|
/** Optional default workflow file path under .github/workflows. */
|
|
60
60
|
defaultWorkflow?: string;
|
|
61
|
+
/**
|
|
62
|
+
* When true, the most recent act runs for this lab are embedded into the
|
|
63
|
+
* saved snapshot so the chat LLM can reason about real execution results
|
|
64
|
+
* (job statuses, durations, exit codes) instead of just the YAML.
|
|
65
|
+
*/
|
|
66
|
+
includeRunHistoryInContext?: boolean;
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
export interface WorkspaceMeta {
|
|
@@ -1 +1 @@
|
|
|
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
|
+
{"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/ghahistorypanel.tsx","./src/components/ghajobspanel.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"}
|
package/template/cockpit.json
CHANGED