@chrysb/alphaclaw 0.4.6-beta.7 → 0.4.6-beta.9

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 (33) hide show
  1. package/bin/alphaclaw.js +2 -32
  2. package/lib/public/css/theme.css +19 -0
  3. package/lib/public/js/app.js +1 -1
  4. package/lib/public/js/components/doctor/helpers.js +71 -5
  5. package/lib/public/js/components/doctor/index.js +89 -28
  6. package/lib/public/js/components/envars.js +0 -1
  7. package/lib/public/js/components/onboarding/welcome-config.js +39 -17
  8. package/lib/public/js/components/onboarding/welcome-form-step.js +142 -47
  9. package/lib/public/js/components/onboarding/welcome-import-step.js +306 -0
  10. package/lib/public/js/components/onboarding/welcome-placeholder-review-step.js +99 -0
  11. package/lib/public/js/components/onboarding/welcome-secret-review-step.js +191 -0
  12. package/lib/public/js/components/segmented-control.js +7 -1
  13. package/lib/public/js/components/welcome/index.js +112 -0
  14. package/lib/public/js/components/welcome/use-welcome.js +561 -0
  15. package/lib/public/js/lib/api.js +221 -161
  16. package/lib/server/commands.js +1 -0
  17. package/lib/server/constants.js +0 -1
  18. package/lib/server/doctor/bootstrap-context.js +191 -0
  19. package/lib/server/doctor/prompt.js +20 -4
  20. package/lib/server/doctor/service.js +18 -4
  21. package/lib/server/gateway.js +15 -40
  22. package/lib/server/onboarding/github.js +120 -19
  23. package/lib/server/onboarding/import/import-applier.js +321 -0
  24. package/lib/server/onboarding/import/import-config.js +69 -0
  25. package/lib/server/onboarding/import/import-scanner.js +469 -0
  26. package/lib/server/onboarding/import/import-temp.js +63 -0
  27. package/lib/server/onboarding/import/secret-detector.js +289 -0
  28. package/lib/server/onboarding/index.js +256 -29
  29. package/lib/server/onboarding/workspace.js +38 -6
  30. package/lib/server/routes/onboarding.js +281 -12
  31. package/lib/server.js +12 -3
  32. package/package.json +1 -1
  33. package/lib/public/js/components/welcome.js +0 -318
@@ -23,26 +23,26 @@ export const authFetch = async (url, opts = {}) => {
23
23
  try {
24
24
  window.localStorage?.clear?.();
25
25
  } catch {}
26
- window.location.href = '/setup';
27
- throw new Error('Unauthorized');
26
+ window.location.href = "/setup";
27
+ throw new Error("Unauthorized");
28
28
  }
29
29
  return res;
30
30
  };
31
31
 
32
32
  export async function fetchStatus() {
33
- const res = await authFetch('/api/status');
33
+ const res = await authFetch("/api/status");
34
34
  return res.json();
35
35
  }
36
36
 
37
37
  export async function fetchPairings() {
38
- const res = await authFetch('/api/pairings');
38
+ const res = await authFetch("/api/pairings");
39
39
  return res.json();
40
40
  }
41
41
 
42
42
  export async function approvePairing(id, channel) {
43
43
  const res = await authFetch(`/api/pairings/${id}/approve`, {
44
- method: 'POST',
45
- headers: { 'Content-Type': 'application/json' },
44
+ method: "POST",
45
+ headers: { "Content-Type": "application/json" },
46
46
  body: JSON.stringify({ channel }),
47
47
  });
48
48
  return res.json();
@@ -50,15 +50,15 @@ export async function approvePairing(id, channel) {
50
50
 
51
51
  export async function rejectPairing(id, channel) {
52
52
  const res = await authFetch(`/api/pairings/${id}/reject`, {
53
- method: 'POST',
54
- headers: { 'Content-Type': 'application/json' },
53
+ method: "POST",
54
+ headers: { "Content-Type": "application/json" },
55
55
  body: JSON.stringify({ channel }),
56
56
  });
57
57
  return res.json();
58
58
  }
59
59
 
60
60
  export async function fetchGoogleAccounts() {
61
- const res = await authFetch('/api/google/accounts');
61
+ const res = await authFetch("/api/google/accounts");
62
62
  return res.json();
63
63
  }
64
64
 
@@ -99,9 +99,9 @@ export async function saveGoogleCredentials({
99
99
  personal = false,
100
100
  accountId = "",
101
101
  }) {
102
- const res = await authFetch('/api/google/credentials', {
103
- method: 'POST',
104
- headers: { 'Content-Type': 'application/json' },
102
+ const res = await authFetch("/api/google/credentials", {
103
+ method: "POST",
104
+ headers: { "Content-Type": "application/json" },
105
105
  body: JSON.stringify({
106
106
  clientId,
107
107
  clientSecret,
@@ -122,18 +122,18 @@ export async function saveGoogleAccount({
122
122
  personal = false,
123
123
  accountId = "",
124
124
  }) {
125
- const res = await authFetch('/api/google/accounts', {
126
- method: 'POST',
127
- headers: { 'Content-Type': 'application/json' },
125
+ const res = await authFetch("/api/google/accounts", {
126
+ method: "POST",
127
+ headers: { "Content-Type": "application/json" },
128
128
  body: JSON.stringify({ email, services, client, personal, accountId }),
129
129
  });
130
130
  return res.json();
131
131
  }
132
132
 
133
133
  export async function disconnectGoogle(accountId = "") {
134
- const res = await authFetch('/api/google/disconnect', {
135
- method: 'POST',
136
- headers: { 'Content-Type': 'application/json' },
134
+ const res = await authFetch("/api/google/disconnect", {
135
+ method: "POST",
136
+ headers: { "Content-Type": "application/json" },
137
137
  body: JSON.stringify({ accountId }),
138
138
  });
139
139
  return res.json();
@@ -188,7 +188,10 @@ export const renewGmailWatch = async ({
188
188
  const res = await authFetch("/api/gmail/watch/renew", {
189
189
  method: "POST",
190
190
  headers: { "Content-Type": "application/json" },
191
- body: JSON.stringify({ accountId: String(accountId || ""), force: Boolean(force) }),
191
+ body: JSON.stringify({
192
+ accountId: String(accountId || ""),
193
+ force: Boolean(force),
194
+ }),
192
195
  });
193
196
  return parseJsonOrThrow(res, "Could not renew Gmail watch");
194
197
  };
@@ -236,7 +239,9 @@ export const fetchDoctorCards = async ({ runId = "all" } = {}) => {
236
239
  };
237
240
 
238
241
  export const fetchDoctorRun = async (runId) => {
239
- const res = await authFetch(`/api/doctor/runs/${encodeURIComponent(String(runId || ""))}`);
242
+ const res = await authFetch(
243
+ `/api/doctor/runs/${encodeURIComponent(String(runId || ""))}`,
244
+ );
240
245
  return parseJsonOrThrow(res, "Could not load Doctor run");
241
246
  };
242
247
 
@@ -248,11 +253,14 @@ export const fetchDoctorRunCards = async (runId) => {
248
253
  };
249
254
 
250
255
  export const updateDoctorCardStatus = async ({ cardId, status }) => {
251
- const res = await authFetch(`/api/doctor/cards/${encodeURIComponent(String(cardId || ""))}/status`, {
252
- method: "POST",
253
- headers: { "Content-Type": "application/json" },
254
- body: JSON.stringify({ status: String(status || "") }),
255
- });
256
+ const res = await authFetch(
257
+ `/api/doctor/cards/${encodeURIComponent(String(cardId || ""))}/status`,
258
+ {
259
+ method: "POST",
260
+ headers: { "Content-Type": "application/json" },
261
+ body: JSON.stringify({ status: String(status || "") }),
262
+ },
263
+ );
256
264
  return parseJsonOrThrow(res, "Could not update Doctor card status");
257
265
  };
258
266
 
@@ -295,114 +303,122 @@ export const sendAgentMessage = async ({
295
303
  };
296
304
 
297
305
  export async function restartGateway() {
298
- const res = await authFetch('/api/gateway/restart', { method: 'POST' });
299
- return parseJsonOrThrow(res, 'Could not restart gateway');
306
+ const res = await authFetch("/api/gateway/restart", { method: "POST" });
307
+ return parseJsonOrThrow(res, "Could not restart gateway");
300
308
  }
301
309
 
302
310
  export async function fetchRestartStatus() {
303
- const res = await authFetch('/api/restart-status');
304
- return parseJsonOrThrow(res, 'Could not load restart status');
311
+ const res = await authFetch("/api/restart-status");
312
+ return parseJsonOrThrow(res, "Could not load restart status");
305
313
  }
306
314
 
307
315
  export async function fetchWatchdogStatus() {
308
- const res = await authFetch('/api/watchdog/status');
309
- return parseJsonOrThrow(res, 'Could not load watchdog status');
316
+ const res = await authFetch("/api/watchdog/status");
317
+ return parseJsonOrThrow(res, "Could not load watchdog status");
310
318
  }
311
319
 
312
320
  export async function fetchUsageSummary(days = 30) {
313
321
  const params = new URLSearchParams({ days: String(days) });
314
322
  const res = await authFetch(`/api/usage/summary?${params.toString()}`);
315
- return parseJsonOrThrow(res, 'Could not load usage summary');
323
+ return parseJsonOrThrow(res, "Could not load usage summary");
316
324
  }
317
325
 
318
326
  export async function fetchUsageSessions(limit = 50) {
319
327
  const params = new URLSearchParams({ limit: String(limit) });
320
328
  const res = await authFetch(`/api/usage/sessions?${params.toString()}`);
321
- return parseJsonOrThrow(res, 'Could not load usage sessions');
329
+ return parseJsonOrThrow(res, "Could not load usage sessions");
322
330
  }
323
331
 
324
332
  export async function fetchUsageSessionDetail(sessionId) {
325
- const res = await authFetch(`/api/usage/sessions/${encodeURIComponent(String(sessionId || ''))}`);
326
- return parseJsonOrThrow(res, 'Could not load usage session detail');
333
+ const res = await authFetch(
334
+ `/api/usage/sessions/${encodeURIComponent(String(sessionId || ""))}`,
335
+ );
336
+ return parseJsonOrThrow(res, "Could not load usage session detail");
327
337
  }
328
338
 
329
339
  export async function fetchUsageSessionTimeSeries(sessionId, maxPoints = 100) {
330
340
  const params = new URLSearchParams({ maxPoints: String(maxPoints) });
331
- const safeSessionId = encodeURIComponent(String(sessionId || ''));
332
- const res = await authFetch(`/api/usage/sessions/${safeSessionId}/timeseries?${params.toString()}`);
333
- return parseJsonOrThrow(res, 'Could not load usage time series');
341
+ const safeSessionId = encodeURIComponent(String(sessionId || ""));
342
+ const res = await authFetch(
343
+ `/api/usage/sessions/${safeSessionId}/timeseries?${params.toString()}`,
344
+ );
345
+ return parseJsonOrThrow(res, "Could not load usage time series");
334
346
  }
335
347
 
336
348
  export async function fetchWatchdogEvents(limit = 20) {
337
- const res = await authFetch(`/api/watchdog/events?limit=${encodeURIComponent(String(limit))}`);
338
- return parseJsonOrThrow(res, 'Could not load watchdog events');
349
+ const res = await authFetch(
350
+ `/api/watchdog/events?limit=${encodeURIComponent(String(limit))}`,
351
+ );
352
+ return parseJsonOrThrow(res, "Could not load watchdog events");
339
353
  }
340
354
 
341
355
  export async function fetchWatchdogLogs(tail = 65536) {
342
- const res = await authFetch(`/api/watchdog/logs?tail=${encodeURIComponent(String(tail))}`);
343
- if (!res.ok) throw new Error('Could not load watchdog logs');
356
+ const res = await authFetch(
357
+ `/api/watchdog/logs?tail=${encodeURIComponent(String(tail))}`,
358
+ );
359
+ if (!res.ok) throw new Error("Could not load watchdog logs");
344
360
  return res.text();
345
361
  }
346
362
 
347
363
  export async function triggerWatchdogRepair() {
348
- const res = await authFetch('/api/watchdog/repair', { method: 'POST' });
349
- return parseJsonOrThrow(res, 'Could not trigger watchdog repair');
364
+ const res = await authFetch("/api/watchdog/repair", { method: "POST" });
365
+ return parseJsonOrThrow(res, "Could not trigger watchdog repair");
350
366
  }
351
367
 
352
368
  export async function fetchWatchdogResources() {
353
- const res = await authFetch('/api/watchdog/resources');
354
- return parseJsonOrThrow(res, 'Could not load system resources');
369
+ const res = await authFetch("/api/watchdog/resources");
370
+ return parseJsonOrThrow(res, "Could not load system resources");
355
371
  }
356
372
 
357
373
  export async function fetchWatchdogSettings() {
358
- const res = await authFetch('/api/watchdog/settings');
359
- return parseJsonOrThrow(res, 'Could not load watchdog settings');
374
+ const res = await authFetch("/api/watchdog/settings");
375
+ return parseJsonOrThrow(res, "Could not load watchdog settings");
360
376
  }
361
377
 
362
378
  export async function updateWatchdogSettings(settings) {
363
- const res = await authFetch('/api/watchdog/settings', {
364
- method: 'PUT',
365
- headers: { 'Content-Type': 'application/json' },
379
+ const res = await authFetch("/api/watchdog/settings", {
380
+ method: "PUT",
381
+ headers: { "Content-Type": "application/json" },
366
382
  body: JSON.stringify(settings || {}),
367
383
  });
368
- return parseJsonOrThrow(res, 'Could not update watchdog settings');
384
+ return parseJsonOrThrow(res, "Could not update watchdog settings");
369
385
  }
370
386
 
371
387
  export async function fetchDashboardUrl() {
372
- const res = await authFetch('/api/gateway/dashboard');
388
+ const res = await authFetch("/api/gateway/dashboard");
373
389
  return res.json();
374
390
  }
375
391
 
376
392
  export async function fetchOpenclawVersion(refresh = false) {
377
- const query = refresh ? '?refresh=1' : '';
393
+ const query = refresh ? "?refresh=1" : "";
378
394
  const res = await authFetch(`/api/openclaw/version${query}`);
379
395
  return res.json();
380
396
  }
381
397
 
382
398
  export async function updateOpenclaw() {
383
- const res = await authFetch('/api/openclaw/update', { method: 'POST' });
399
+ const res = await authFetch("/api/openclaw/update", { method: "POST" });
384
400
  return res.json();
385
401
  }
386
402
 
387
403
  export async function fetchAlphaclawVersion(refresh = false) {
388
- const query = refresh ? '?refresh=1' : '';
404
+ const query = refresh ? "?refresh=1" : "";
389
405
  const res = await authFetch(`/api/alphaclaw/version${query}`);
390
406
  return res.json();
391
407
  }
392
408
 
393
409
  export async function updateAlphaclaw() {
394
- const res = await authFetch('/api/alphaclaw/update', { method: 'POST' });
410
+ const res = await authFetch("/api/alphaclaw/update", { method: "POST" });
395
411
  return res.json();
396
412
  }
397
413
 
398
414
  export async function fetchSyncCron() {
399
- const res = await authFetch('/api/sync-cron');
415
+ const res = await authFetch("/api/sync-cron");
400
416
  const text = await res.text();
401
417
  let data;
402
418
  try {
403
419
  data = text ? JSON.parse(text) : {};
404
420
  } catch {
405
- throw new Error(text || 'Could not parse sync cron response');
421
+ throw new Error(text || "Could not parse sync cron response");
406
422
  }
407
423
  if (!res.ok) {
408
424
  throw new Error(data.error || text || `HTTP ${res.status}`);
@@ -411,9 +427,9 @@ export async function fetchSyncCron() {
411
427
  }
412
428
 
413
429
  export async function updateSyncCron(payload) {
414
- const res = await authFetch('/api/sync-cron', {
415
- method: 'PUT',
416
- headers: { 'Content-Type': 'application/json' },
430
+ const res = await authFetch("/api/sync-cron", {
431
+ method: "PUT",
432
+ headers: { "Content-Type": "application/json" },
417
433
  body: JSON.stringify(payload),
418
434
  });
419
435
  const text = await res.text();
@@ -421,7 +437,7 @@ export async function updateSyncCron(payload) {
421
437
  try {
422
438
  data = text ? JSON.parse(text) : {};
423
439
  } catch {
424
- throw new Error(text || 'Could not parse sync cron response');
440
+ throw new Error(text || "Could not parse sync cron response");
425
441
  }
426
442
  if (!res.ok) {
427
443
  throw new Error(data.error || text || `HTTP ${res.status}`);
@@ -430,135 +446,176 @@ export async function updateSyncCron(payload) {
430
446
  }
431
447
 
432
448
  export async function fetchDevicePairings() {
433
- const res = await authFetch('/api/devices');
449
+ const res = await authFetch("/api/devices");
434
450
  return res.json();
435
451
  }
436
452
 
437
453
  export async function approveDevice(id) {
438
- const res = await authFetch(`/api/devices/${id}/approve`, { method: 'POST' });
454
+ const res = await authFetch(`/api/devices/${id}/approve`, { method: "POST" });
439
455
  return res.json();
440
456
  }
441
457
 
442
458
  export async function rejectDevice(id) {
443
- const res = await authFetch(`/api/devices/${id}/reject`, { method: 'POST' });
459
+ const res = await authFetch(`/api/devices/${id}/reject`, { method: "POST" });
444
460
  return res.json();
445
461
  }
446
462
 
447
463
  export const fetchAuthStatus = async () => {
448
- const res = await authFetch('/api/auth/status');
464
+ const res = await authFetch("/api/auth/status");
449
465
  return res.json();
450
466
  };
451
467
 
452
468
  export const logout = async () => {
453
- const res = await authFetch('/api/auth/logout', { method: 'POST' });
469
+ const res = await authFetch("/api/auth/logout", { method: "POST" });
454
470
  return res.json();
455
471
  };
456
472
 
457
473
  export async function fetchOnboardStatus() {
458
- const res = await authFetch('/api/onboard/status');
474
+ const res = await authFetch("/api/onboard/status");
475
+ return res.json();
476
+ }
477
+
478
+ export async function runOnboard(vars, modelKey, { importMode = false } = {}) {
479
+ const res = await authFetch("/api/onboard", {
480
+ method: "POST",
481
+ headers: { "Content-Type": "application/json" },
482
+ body: JSON.stringify({ vars, modelKey, importMode }),
483
+ });
484
+ return res.json();
485
+ }
486
+
487
+ export async function verifyGithubOnboardingRepo(repo, token, mode = "new") {
488
+ const res = await authFetch("/api/onboard/github/verify", {
489
+ method: "POST",
490
+ headers: { "Content-Type": "application/json" },
491
+ body: JSON.stringify({ repo, token, mode }),
492
+ });
459
493
  return res.json();
460
494
  }
461
495
 
462
- export async function runOnboard(vars, modelKey) {
463
- const res = await authFetch('/api/onboard', {
464
- method: 'POST',
465
- headers: { 'Content-Type': 'application/json' },
466
- body: JSON.stringify({ vars, modelKey }),
496
+ export async function scanImportRepo(tempDir) {
497
+ const res = await authFetch("/api/onboard/import/scan", {
498
+ method: "POST",
499
+ headers: { "Content-Type": "application/json" },
500
+ body: JSON.stringify({ tempDir }),
467
501
  });
468
502
  return res.json();
469
503
  }
470
504
 
471
- export async function verifyGithubOnboardingRepo(repo, token) {
472
- const res = await authFetch('/api/onboard/github/verify', {
473
- method: 'POST',
474
- headers: { 'Content-Type': 'application/json' },
475
- body: JSON.stringify({ repo, token }),
505
+ export async function applyImport({
506
+ tempDir,
507
+ approvedSecrets = [],
508
+ skipSecretExtraction = false,
509
+ githubRepo = "",
510
+ githubToken = "",
511
+ }) {
512
+ const res = await authFetch("/api/onboard/import/apply", {
513
+ method: "POST",
514
+ headers: { "Content-Type": "application/json" },
515
+ body: JSON.stringify({
516
+ tempDir,
517
+ approvedSecrets,
518
+ skipSecretExtraction,
519
+ githubRepo,
520
+ githubToken,
521
+ }),
476
522
  });
477
523
  return res.json();
478
524
  }
479
525
 
480
526
  export const fetchModels = async () => {
481
- const res = await authFetch('/api/models');
527
+ const res = await authFetch("/api/models");
482
528
  return res.json();
483
529
  };
484
530
 
485
531
  export const fetchModelStatus = async () => {
486
- const res = await authFetch('/api/models/status');
532
+ const res = await authFetch("/api/models/status");
487
533
  return res.json();
488
534
  };
489
535
 
490
536
  export const setPrimaryModel = async (modelKey) => {
491
- const res = await authFetch('/api/models/set', {
492
- method: 'POST',
493
- headers: { 'Content-Type': 'application/json' },
537
+ const res = await authFetch("/api/models/set", {
538
+ method: "POST",
539
+ headers: { "Content-Type": "application/json" },
494
540
  body: JSON.stringify({ modelKey }),
495
541
  });
496
542
  return res.json();
497
543
  };
498
544
 
499
545
  export const fetchModelsConfig = async () => {
500
- const res = await authFetch('/api/models/config');
546
+ const res = await authFetch("/api/models/config");
501
547
  return res.json();
502
548
  };
503
549
 
504
- export const saveModelsConfig = async ({ primary, configuredModels, profiles, authOrder }) => {
505
- const res = await authFetch('/api/models/config', {
506
- method: 'PUT',
507
- headers: { 'Content-Type': 'application/json' },
550
+ export const saveModelsConfig = async ({
551
+ primary,
552
+ configuredModels,
553
+ profiles,
554
+ authOrder,
555
+ }) => {
556
+ const res = await authFetch("/api/models/config", {
557
+ method: "PUT",
558
+ headers: { "Content-Type": "application/json" },
508
559
  body: JSON.stringify({ primary, configuredModels, profiles, authOrder }),
509
560
  });
510
561
  return res.json();
511
562
  };
512
563
 
513
564
  export const fetchAuthProfiles = async () => {
514
- const res = await authFetch('/api/models/auth');
565
+ const res = await authFetch("/api/models/auth");
515
566
  return res.json();
516
567
  };
517
568
 
518
569
  export const upsertAuthProfile = async (profileId, credential) => {
519
- const res = await authFetch(`/api/models/auth/${encodeURIComponent(profileId)}`, {
520
- method: 'PUT',
521
- headers: { 'Content-Type': 'application/json' },
522
- body: JSON.stringify(credential),
523
- });
570
+ const res = await authFetch(
571
+ `/api/models/auth/${encodeURIComponent(profileId)}`,
572
+ {
573
+ method: "PUT",
574
+ headers: { "Content-Type": "application/json" },
575
+ body: JSON.stringify(credential),
576
+ },
577
+ );
524
578
  return res.json();
525
579
  };
526
580
 
527
581
  export const deleteAuthProfile = async (profileId) => {
528
- const res = await authFetch(`/api/models/auth/${encodeURIComponent(profileId)}`, {
529
- method: 'DELETE',
530
- });
582
+ const res = await authFetch(
583
+ `/api/models/auth/${encodeURIComponent(profileId)}`,
584
+ {
585
+ method: "DELETE",
586
+ },
587
+ );
531
588
  return res.json();
532
589
  };
533
590
 
534
591
  export const fetchCodexStatus = async () => {
535
- const res = await authFetch('/api/codex/status');
592
+ const res = await authFetch("/api/codex/status");
536
593
  return res.json();
537
594
  };
538
595
 
539
596
  export const disconnectCodex = async () => {
540
- const res = await authFetch('/api/codex/disconnect', { method: 'POST' });
597
+ const res = await authFetch("/api/codex/disconnect", { method: "POST" });
541
598
  return res.json();
542
599
  };
543
600
 
544
601
  export const exchangeCodexOAuth = async (input) => {
545
- const res = await authFetch('/api/codex/exchange', {
546
- method: 'POST',
547
- headers: { 'Content-Type': 'application/json' },
602
+ const res = await authFetch("/api/codex/exchange", {
603
+ method: "POST",
604
+ headers: { "Content-Type": "application/json" },
548
605
  body: JSON.stringify({ input }),
549
606
  });
550
607
  return res.json();
551
608
  };
552
609
 
553
610
  export async function fetchEnvVars() {
554
- const res = await authFetch('/api/env');
611
+ const res = await authFetch("/api/env");
555
612
  return res.json();
556
613
  }
557
614
 
558
615
  export async function saveEnvVars(vars) {
559
- const res = await authFetch('/api/env', {
560
- method: 'PUT',
561
- headers: { 'Content-Type': 'application/json' },
616
+ const res = await authFetch("/api/env", {
617
+ method: "PUT",
618
+ headers: { "Content-Type": "application/json" },
562
619
  body: JSON.stringify({ vars }),
563
620
  });
564
621
  const text = await res.text();
@@ -566,7 +623,7 @@ export async function saveEnvVars(vars) {
566
623
  try {
567
624
  data = text ? JSON.parse(text) : {};
568
625
  } catch {
569
- throw new Error(text || 'Could not parse env save response');
626
+ throw new Error(text || "Could not parse env save response");
570
627
  }
571
628
  if (!res.ok) {
572
629
  throw new Error(data.error || text || `HTTP ${res.status}`);
@@ -589,107 +646,110 @@ const parseJsonOrThrow = async (res, fallbackError) => {
589
646
  };
590
647
 
591
648
  export async function fetchWebhooks() {
592
- const res = await authFetch('/api/webhooks');
593
- return parseJsonOrThrow(res, 'Could not load webhooks');
649
+ const res = await authFetch("/api/webhooks");
650
+ return parseJsonOrThrow(res, "Could not load webhooks");
594
651
  }
595
652
 
596
653
  export async function fetchWebhookDetail(name) {
597
654
  const res = await authFetch(`/api/webhooks/${encodeURIComponent(name)}`);
598
- return parseJsonOrThrow(res, 'Could not load webhook detail');
655
+ return parseJsonOrThrow(res, "Could not load webhook detail");
599
656
  }
600
657
 
601
658
  export async function createWebhook(name) {
602
- const res = await authFetch('/api/webhooks', {
603
- method: 'POST',
604
- headers: { 'Content-Type': 'application/json' },
659
+ const res = await authFetch("/api/webhooks", {
660
+ method: "POST",
661
+ headers: { "Content-Type": "application/json" },
605
662
  body: JSON.stringify({ name }),
606
663
  });
607
- return parseJsonOrThrow(res, 'Could not create webhook');
664
+ return parseJsonOrThrow(res, "Could not create webhook");
608
665
  }
609
666
 
610
667
  export async function deleteWebhook(name, { deleteTransformDir = false } = {}) {
611
668
  const res = await authFetch(`/api/webhooks/${encodeURIComponent(name)}`, {
612
- method: 'DELETE',
613
- headers: { 'Content-Type': 'application/json' },
669
+ method: "DELETE",
670
+ headers: { "Content-Type": "application/json" },
614
671
  body: JSON.stringify({ deleteTransformDir: !!deleteTransformDir }),
615
672
  });
616
- return parseJsonOrThrow(res, 'Could not delete webhook');
673
+ return parseJsonOrThrow(res, "Could not delete webhook");
617
674
  }
618
675
 
619
- export async function fetchWebhookRequests(name, { limit = 50, offset = 0, status = 'all' } = {}) {
676
+ export async function fetchWebhookRequests(
677
+ name,
678
+ { limit = 50, offset = 0, status = "all" } = {},
679
+ ) {
620
680
  const params = new URLSearchParams({
621
681
  limit: String(limit),
622
682
  offset: String(offset),
623
- status: String(status || 'all'),
683
+ status: String(status || "all"),
624
684
  });
625
685
  const res = await authFetch(
626
686
  `/api/webhooks/${encodeURIComponent(name)}/requests?${params.toString()}`,
627
687
  );
628
- return parseJsonOrThrow(res, 'Could not load webhook requests');
688
+ return parseJsonOrThrow(res, "Could not load webhook requests");
629
689
  }
630
690
 
631
691
  export async function fetchWebhookRequest(name, id) {
632
692
  const res = await authFetch(
633
693
  `/api/webhooks/${encodeURIComponent(name)}/requests/${encodeURIComponent(String(id))}`,
634
694
  );
635
- return parseJsonOrThrow(res, 'Could not load webhook request');
695
+ return parseJsonOrThrow(res, "Could not load webhook request");
636
696
  }
637
697
 
638
698
  export const fetchBrowseTree = async (depth = 10) => {
639
699
  const params = new URLSearchParams({ depth: String(depth) });
640
700
  const res = await authFetch(`/api/browse/tree?${params.toString()}`);
641
- return parseJsonOrThrow(res, 'Could not load file tree');
701
+ return parseJsonOrThrow(res, "Could not load file tree");
642
702
  };
643
703
 
644
704
  export const fetchFileContent = async (filePath) => {
645
- const params = new URLSearchParams({ path: String(filePath || '') });
705
+ const params = new URLSearchParams({ path: String(filePath || "") });
646
706
  const res = await authFetch(`/api/browse/read?${params.toString()}`);
647
- return parseJsonOrThrow(res, 'Could not load file content');
707
+ return parseJsonOrThrow(res, "Could not load file content");
648
708
  };
649
709
 
650
710
  export const saveFileContent = async (filePath, content) => {
651
- const res = await authFetch('/api/browse/write', {
652
- method: 'PUT',
653
- headers: { 'Content-Type': 'application/json' },
711
+ const res = await authFetch("/api/browse/write", {
712
+ method: "PUT",
713
+ headers: { "Content-Type": "application/json" },
654
714
  body: JSON.stringify({ path: filePath, content }),
655
715
  });
656
- return parseJsonOrThrow(res, 'Could not save file');
716
+ return parseJsonOrThrow(res, "Could not save file");
657
717
  };
658
718
 
659
719
  export const createBrowseFile = async (filePath) => {
660
- const res = await authFetch('/api/browse/create-file', {
661
- method: 'POST',
662
- headers: { 'Content-Type': 'application/json' },
663
- body: JSON.stringify({ path: String(filePath || '') }),
720
+ const res = await authFetch("/api/browse/create-file", {
721
+ method: "POST",
722
+ headers: { "Content-Type": "application/json" },
723
+ body: JSON.stringify({ path: String(filePath || "") }),
664
724
  });
665
- return parseJsonOrThrow(res, 'Could not create file');
725
+ return parseJsonOrThrow(res, "Could not create file");
666
726
  };
667
727
 
668
728
  export const createBrowseFolder = async (folderPath) => {
669
- const res = await authFetch('/api/browse/create-folder', {
670
- method: 'POST',
671
- headers: { 'Content-Type': 'application/json' },
672
- body: JSON.stringify({ path: String(folderPath || '') }),
729
+ const res = await authFetch("/api/browse/create-folder", {
730
+ method: "POST",
731
+ headers: { "Content-Type": "application/json" },
732
+ body: JSON.stringify({ path: String(folderPath || "") }),
673
733
  });
674
- return parseJsonOrThrow(res, 'Could not create folder');
734
+ return parseJsonOrThrow(res, "Could not create folder");
675
735
  };
676
736
 
677
737
  export const moveBrowsePath = async (from, to) => {
678
- const res = await authFetch('/api/browse/move', {
679
- method: 'POST',
680
- headers: { 'Content-Type': 'application/json' },
681
- body: JSON.stringify({ from: String(from || ''), to: String(to || '') }),
738
+ const res = await authFetch("/api/browse/move", {
739
+ method: "POST",
740
+ headers: { "Content-Type": "application/json" },
741
+ body: JSON.stringify({ from: String(from || ""), to: String(to || "") }),
682
742
  });
683
- return parseJsonOrThrow(res, 'Could not move path');
743
+ return parseJsonOrThrow(res, "Could not move path");
684
744
  };
685
745
 
686
746
  export const deleteBrowseFile = async (filePath) => {
687
- const res = await authFetch('/api/browse/delete', {
688
- method: 'DELETE',
689
- headers: { 'Content-Type': 'application/json' },
690
- body: JSON.stringify({ path: String(filePath || '') }),
747
+ const res = await authFetch("/api/browse/delete", {
748
+ method: "DELETE",
749
+ headers: { "Content-Type": "application/json" },
750
+ body: JSON.stringify({ path: String(filePath || "") }),
691
751
  });
692
- return parseJsonOrThrow(res, 'Could not delete file');
752
+ return parseJsonOrThrow(res, "Could not delete file");
693
753
  };
694
754
 
695
755
  export const downloadBrowseFile = async (filePath) => {
@@ -724,23 +784,23 @@ export const downloadBrowseFile = async (filePath) => {
724
784
  };
725
785
 
726
786
  export const restoreBrowseFile = async (filePath) => {
727
- const res = await authFetch('/api/browse/restore', {
728
- method: 'POST',
729
- headers: { 'Content-Type': 'application/json' },
730
- body: JSON.stringify({ path: String(filePath || '') }),
787
+ const res = await authFetch("/api/browse/restore", {
788
+ method: "POST",
789
+ headers: { "Content-Type": "application/json" },
790
+ body: JSON.stringify({ path: String(filePath || "") }),
731
791
  });
732
- return parseJsonOrThrow(res, 'Could not restore file');
792
+ return parseJsonOrThrow(res, "Could not restore file");
733
793
  };
734
794
 
735
795
  export const fetchBrowseGitSummary = async () => {
736
- const res = await authFetch('/api/browse/git-summary');
737
- return parseJsonOrThrow(res, 'Could not load git summary');
796
+ const res = await authFetch("/api/browse/git-summary");
797
+ return parseJsonOrThrow(res, "Could not load git summary");
738
798
  };
739
799
 
740
800
  export const fetchBrowseFileDiff = async (filePath) => {
741
801
  const params = new URLSearchParams({ path: String(filePath || "") });
742
802
  const res = await authFetch(`/api/browse/git-diff?${params.toString()}`);
743
- return parseJsonOrThrow(res, 'Could not load file diff');
803
+ return parseJsonOrThrow(res, "Could not load file diff");
744
804
  };
745
805
 
746
806
  export const fetchBrowseSqliteTable = async ({
@@ -760,10 +820,10 @@ export const fetchBrowseSqliteTable = async ({
760
820
  };
761
821
 
762
822
  export const syncBrowseChanges = async (message = "") => {
763
- const res = await authFetch('/api/browse/git-sync', {
764
- method: 'POST',
765
- headers: { 'Content-Type': 'application/json' },
823
+ const res = await authFetch("/api/browse/git-sync", {
824
+ method: "POST",
825
+ headers: { "Content-Type": "application/json" },
766
826
  body: JSON.stringify({ message: String(message || "") }),
767
827
  });
768
- return parseJsonOrThrow(res, 'Could not sync changes');
828
+ return parseJsonOrThrow(res, "Could not sync changes");
769
829
  };