@worca/ui 0.8.1 → 0.9.0-rc.2
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/app/main.bundle.js +1424 -755
- package/app/main.bundle.js.map +4 -4
- package/app/styles.css +399 -23
- package/package.json +5 -4
- package/server/app.js +341 -6
- package/server/dispatch-events-aggregator.js +161 -0
- package/server/ensure-webhook.js +66 -0
- package/server/index.js +22 -0
- package/server/integrations/adapter.js +91 -0
- package/server/integrations/adapters/discord.js +109 -0
- package/server/integrations/adapters/slack.js +106 -0
- package/server/integrations/adapters/telegram.js +228 -0
- package/server/integrations/adapters/webhook_out.js +253 -0
- package/server/integrations/allowlist.js +19 -0
- package/server/integrations/chat_context.js +68 -0
- package/server/integrations/commands/control.js +120 -0
- package/server/integrations/commands/global.js +239 -0
- package/server/integrations/commands/parser.js +29 -0
- package/server/integrations/commands/project.js +394 -0
- package/server/integrations/config-loader.js +40 -0
- package/server/integrations/index.js +390 -0
- package/server/integrations/markdown.js +220 -0
- package/server/integrations/rate_limiter.js +131 -0
- package/server/integrations/renderers.js +191 -0
- package/server/integrations/rest_client.js +17 -0
- package/server/integrations/verify.js +23 -0
- package/server/process-manager.js +61 -2
- package/server/project-registry.js +37 -0
- package/server/project-routes.js +175 -6
- package/server/settings-validator.js +279 -2
- package/server/subagents-discovery.js +116 -0
- package/server/version-check.js +35 -0
- package/server/watcher.js +37 -10
- package/server/worca-setup.js +15 -1
- package/server/ws-modular.js +6 -2
package/server/watcher.js
CHANGED
|
@@ -2,6 +2,25 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { existsSync, readdirSync, readFileSync, watch } from 'node:fs';
|
|
3
3
|
import { readdir, readFile } from 'node:fs/promises';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
+
import {
|
|
6
|
+
assignEventsToIterations,
|
|
7
|
+
readDispatchEventsFromJsonl,
|
|
8
|
+
} from './dispatch-events-aggregator.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Enrich a status object with dispatch events read from events.jsonl in the
|
|
12
|
+
* same run directory. Mutates `status.stages` by adding `dispatch_events` to
|
|
13
|
+
* matching iterations. No-op when events.jsonl is missing (e.g. a run that
|
|
14
|
+
* started before the emit was wired, or a run with no dispatches).
|
|
15
|
+
*/
|
|
16
|
+
function enrichWithDispatchEvents(status, runDir) {
|
|
17
|
+
if (!status?.stages) return status;
|
|
18
|
+
const eventsPath = join(runDir, 'events.jsonl');
|
|
19
|
+
const events = readDispatchEventsFromJsonl(eventsPath);
|
|
20
|
+
if (events.length === 0) return status;
|
|
21
|
+
status.stages = assignEventsToIterations(events, status.stages);
|
|
22
|
+
return status;
|
|
23
|
+
}
|
|
5
24
|
|
|
6
25
|
export function createRunId(status) {
|
|
7
26
|
// Prefer run_id from status (new per-run format)
|
|
@@ -35,9 +54,11 @@ export function discoverRuns(worcaDir) {
|
|
|
35
54
|
if (existsSync(activeRunPath)) {
|
|
36
55
|
try {
|
|
37
56
|
const activeId = readFileSync(activeRunPath, 'utf8').trim();
|
|
38
|
-
const
|
|
57
|
+
const runDir = join(worcaDir, 'runs', activeId);
|
|
58
|
+
const candidate = join(runDir, 'status.json');
|
|
39
59
|
if (existsSync(candidate)) {
|
|
40
|
-
|
|
60
|
+
let status = JSON.parse(readFileSync(candidate, 'utf8'));
|
|
61
|
+
status = enrichWithDispatchEvents(status, runDir);
|
|
41
62
|
const active =
|
|
42
63
|
!isTerminal(status) && status.pipeline_status === 'running';
|
|
43
64
|
const id = createRunId(status);
|
|
@@ -53,10 +74,12 @@ export function discoverRuns(worcaDir) {
|
|
|
53
74
|
const runsDir = join(worcaDir, 'runs');
|
|
54
75
|
if (existsSync(runsDir)) {
|
|
55
76
|
for (const entry of readdirSync(runsDir)) {
|
|
56
|
-
const
|
|
77
|
+
const runDir = join(runsDir, entry);
|
|
78
|
+
const statusPath = join(runDir, 'status.json');
|
|
57
79
|
if (!existsSync(statusPath)) continue;
|
|
58
80
|
try {
|
|
59
|
-
|
|
81
|
+
let status = JSON.parse(readFileSync(statusPath, 'utf8'));
|
|
82
|
+
status = enrichWithDispatchEvents(status, runDir);
|
|
60
83
|
const id = createRunId(status);
|
|
61
84
|
if (seenIds.has(id)) continue;
|
|
62
85
|
seenIds.add(id);
|
|
@@ -140,8 +163,10 @@ export async function discoverRunsAsync(worcaDir) {
|
|
|
140
163
|
const activeRunPath = join(worcaDir, 'active_run');
|
|
141
164
|
try {
|
|
142
165
|
const activeId = (await readFile(activeRunPath, 'utf8')).trim();
|
|
143
|
-
const
|
|
144
|
-
const
|
|
166
|
+
const runDir = join(worcaDir, 'runs', activeId);
|
|
167
|
+
const candidate = join(runDir, 'status.json');
|
|
168
|
+
let status = JSON.parse(await readFile(candidate, 'utf8'));
|
|
169
|
+
status = enrichWithDispatchEvents(status, runDir);
|
|
145
170
|
const active = !isTerminal(status) && status.pipeline_status === 'running';
|
|
146
171
|
const id = createRunId(status);
|
|
147
172
|
runs.push({ id, active, ...status });
|
|
@@ -156,15 +181,17 @@ export async function discoverRunsAsync(worcaDir) {
|
|
|
156
181
|
const entries = await readdir(runsDir);
|
|
157
182
|
const readPromises = entries.map(async (entry) => {
|
|
158
183
|
try {
|
|
159
|
-
const
|
|
184
|
+
const runDir = join(runsDir, entry);
|
|
185
|
+
const statusPath = join(runDir, 'status.json');
|
|
160
186
|
const status = JSON.parse(await readFile(statusPath, 'utf8'));
|
|
161
|
-
return status;
|
|
187
|
+
return { status, runDir };
|
|
162
188
|
} catch {
|
|
163
189
|
return null;
|
|
164
190
|
}
|
|
165
191
|
});
|
|
166
|
-
for (const
|
|
167
|
-
if (!
|
|
192
|
+
for (const result of await Promise.all(readPromises)) {
|
|
193
|
+
if (!result) continue;
|
|
194
|
+
const status = enrichWithDispatchEvents(result.status, result.runDir);
|
|
168
195
|
const id = createRunId(status);
|
|
169
196
|
if (seenIds.has(id)) continue;
|
|
170
197
|
seenIds.add(id);
|
package/server/worca-setup.js
CHANGED
|
@@ -20,10 +20,24 @@ export function checkWorcaInstalled(projectPath) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Read the worca-cc version from a project's
|
|
23
|
+
* Read the worca-cc version from a project's worca installation.
|
|
24
|
+
* Tries .claude/worca/version.json first, then falls back to __init__.py.
|
|
24
25
|
* Returns the version string or null if not found.
|
|
25
26
|
*/
|
|
26
27
|
export function readProjectWorcaVersion(projectPath) {
|
|
28
|
+
// Try version.json first (preferred format)
|
|
29
|
+
try {
|
|
30
|
+
const versionJson = JSON.parse(
|
|
31
|
+
readFileSync(
|
|
32
|
+
join(projectPath, '.claude', 'worca', 'version.json'),
|
|
33
|
+
'utf8',
|
|
34
|
+
),
|
|
35
|
+
);
|
|
36
|
+
if (versionJson.version) return versionJson.version;
|
|
37
|
+
} catch {
|
|
38
|
+
// fall through to __init__.py
|
|
39
|
+
}
|
|
40
|
+
// Fall back to __init__.py
|
|
27
41
|
try {
|
|
28
42
|
const initPy = readFileSync(
|
|
29
43
|
join(projectPath, '.claude', 'worca', '__init__.py'),
|
package/server/ws-modular.js
CHANGED
|
@@ -11,6 +11,7 @@ import { join } from 'node:path';
|
|
|
11
11
|
import { WebSocketServer } from 'ws';
|
|
12
12
|
import { readProjects, synthesizeDefaultProject } from './project-registry.js';
|
|
13
13
|
import { TIER_FULL, TIER_POLLING, WatcherSet } from './watcher-set.js';
|
|
14
|
+
import { readProjectWorcaVersion } from './worca-setup.js';
|
|
14
15
|
import { createBroadcaster } from './ws-broadcaster.js';
|
|
15
16
|
import { createClientManager } from './ws-client-manager.js';
|
|
16
17
|
import { createMessageRouter } from './ws-message-router.js';
|
|
@@ -157,9 +158,12 @@ export function attachWsServer(httpServer, config) {
|
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
// Broadcast projects-updated to all clients
|
|
161
|
+
// Shape must match GET /api/projects so frontend state stays consistent
|
|
162
|
+
// (include worcaVersion — without it, clients would show "unknown" after
|
|
163
|
+
// the WS event clobbers the enriched REST response on add/remove)
|
|
160
164
|
const projectList = freshProjects.map((p) => ({
|
|
161
|
-
|
|
162
|
-
|
|
165
|
+
...p,
|
|
166
|
+
worcaVersion: readProjectWorcaVersion(p.path),
|
|
163
167
|
}));
|
|
164
168
|
broadcaster.broadcast('projects-updated', { projects: projectList });
|
|
165
169
|
}
|