@worca/ui 0.21.0 → 0.23.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.
@@ -6,26 +6,33 @@ export const STATES = [
6
6
  'failed',
7
7
  'interrupted',
8
8
  'cancelled',
9
+ 'halted',
10
+ 'setup_failed',
11
+ 'unrecoverable',
9
12
  ];
10
13
 
11
14
  const ACTION_MATRIX = {
12
15
  stop: { running: true },
13
16
  pause: { running: true },
14
- resume: { paused: true, failed: true, interrupted: true },
17
+ resume: { paused: true, failed: true, interrupted: true, halted: true },
15
18
  cancel: {
16
19
  pending: true,
17
20
  running: true,
18
21
  paused: true,
19
22
  failed: true,
20
23
  interrupted: true,
24
+ halted: true,
25
+ setup_failed: true,
21
26
  },
22
27
  archive: {
23
- pending: true,
24
28
  paused: true,
25
29
  completed: true,
26
30
  failed: true,
27
31
  interrupted: true,
28
32
  cancelled: true,
33
+ halted: true,
34
+ setup_failed: true,
35
+ unrecoverable: true,
29
36
  },
30
37
  unarchive: {
31
38
  completed: true,
@@ -40,6 +47,9 @@ const ACTION_MATRIX = {
40
47
  failed: true,
41
48
  interrupted: true,
42
49
  cancelled: true,
50
+ halted: true,
51
+ setup_failed: true,
52
+ unrecoverable: true,
43
53
  },
44
54
  learn: {
45
55
  paused: true,
@@ -47,6 +57,7 @@ const ACTION_MATRIX = {
47
57
  failed: true,
48
58
  interrupted: true,
49
59
  cancelled: true,
60
+ halted: true,
50
61
  },
51
62
  };
52
63
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@worca/ui",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "description": "Pipeline monitoring UI for worca-cc",
5
5
  "license": "MIT",
6
6
  "author": "Sinisha Djukic",
package/server/app.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // server/app.js
2
2
 
3
- import { execFileSync } from 'node:child_process';
3
+ import { execFileSync, spawn } from 'node:child_process';
4
4
  import { createHmac, randomUUID } from 'node:crypto';
5
5
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
6
6
  import { homedir } from 'node:os';
@@ -9,6 +9,7 @@ import { fileURLToPath } from 'node:url';
9
9
  import express from 'express';
10
10
 
11
11
  import { dbExists, getIssue, listIssues } from './beads-reader.js';
12
+ import { createFleetRouter } from './fleet-routes.js';
12
13
  import { RAW_BODY } from './integrations/index.js';
13
14
  import { verify } from './integrations/verify.js';
14
15
  import { LaunchLock } from './launch-lock.js';
@@ -550,6 +551,72 @@ export function createApp(options = {}) {
550
551
  launchLock,
551
552
  }),
552
553
  );
554
+ app.use(
555
+ '/api/fleet-runs',
556
+ createFleetRouter({
557
+ fleetRunsDir: join(homedir(), '.worca', 'fleet-runs'),
558
+ prefsDir,
559
+ // Spawn run_fleet.py in a detached subprocess so the route can return
560
+ // immediately. We pass the pre-generated fleet_id so the in-flight
561
+ // manifest path matches what the route just wrote.
562
+ dispatchFleet: async ({ fleet_id, projects, manifest, resume }) => {
563
+ // Resume path: the /resume route already flipped the manifest to
564
+ // "running"; run_fleet.py --resume reads the manifest, continues
565
+ // paused/interrupted children in place and re-dispatches
566
+ // failed/pending ones. No --projects — the manifest is the source.
567
+ if (resume) {
568
+ const child = spawn(
569
+ 'python3',
570
+ ['-m', 'worca.scripts.run_fleet', '--resume', fleet_id],
571
+ { detached: true, stdio: 'ignore', env: { ...process.env } },
572
+ );
573
+ child.unref();
574
+ return;
575
+ }
576
+ if (!projects || projects.length === 0) return;
577
+ const args = [
578
+ '-m',
579
+ 'worca.scripts.run_fleet',
580
+ '--fleet-id',
581
+ fleet_id,
582
+ '--projects',
583
+ ...projects,
584
+ ];
585
+ if (manifest.work_request?.source) {
586
+ args.push('--source', manifest.work_request.source);
587
+ } else {
588
+ args.push('--prompt', manifest.work_request?.description ?? '');
589
+ }
590
+ if (manifest.head_template) {
591
+ args.push('--head-template', manifest.head_template);
592
+ }
593
+ if (manifest.base_branch) {
594
+ args.push('--base', manifest.base_branch);
595
+ }
596
+ if (manifest.plan?.path) {
597
+ args.push('--plan', manifest.plan.path);
598
+ }
599
+ for (const p of manifest.guide?.paths || []) {
600
+ args.push('--guide', p);
601
+ }
602
+ if (manifest.max_parallel) {
603
+ args.push('--max-parallel', String(manifest.max_parallel));
604
+ }
605
+ if (manifest.fleet_failure_threshold != null) {
606
+ args.push(
607
+ '--fleet-failure-threshold',
608
+ String(manifest.fleet_failure_threshold),
609
+ );
610
+ }
611
+ const child = spawn('python3', args, {
612
+ detached: true,
613
+ stdio: 'ignore',
614
+ env: { ...process.env },
615
+ });
616
+ child.unref();
617
+ },
618
+ }),
619
+ );
553
620
  }
554
621
 
555
622
  // POST /api/integrations/telegram/detect — find chat IDs from recent messages.