@castlemilk/omega 0.1.9 → 0.3.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/dist/cli.js +130 -46
- package/dist/proto/tasks.proto +37 -0
- package/dist/server.js +190 -39
- package/dist/web/dist/App.d.ts.map +1 -1
- package/dist/web/dist/App.js +5 -3
- package/dist/web/dist/App.js.map +1 -1
- package/dist/web/dist/assets/index-CcDem7e3.js +40 -0
- package/dist/web/dist/components/ProjectSidebar.js +1 -1
- package/dist/web/dist/components/ProjectSidebar.js.map +1 -1
- package/dist/web/dist/components/ProviderSettings.js +1 -1
- package/dist/web/dist/components/ProviderSettings.js.map +1 -1
- package/dist/web/dist/components/RouterPanel.js +1 -1
- package/dist/web/dist/components/RouterPanel.js.map +1 -1
- package/dist/web/dist/components/TaskBoard.js +3 -3
- package/dist/web/dist/components/TaskBoard.js.map +1 -1
- package/dist/web/dist/index.html +1 -1
- package/dist/web/dist/lib/api.d.ts +18 -12
- package/dist/web/dist/lib/api.d.ts.map +1 -1
- package/dist/web/dist/lib/api.js +13 -4
- package/dist/web/dist/lib/api.js.map +1 -1
- package/dist/web/dist/main.js +5 -1
- package/dist/web/dist/main.js.map +1 -1
- package/package.json +14 -5
package/dist/cli.js
CHANGED
|
@@ -10,14 +10,15 @@ import { Command } from "commander";
|
|
|
10
10
|
import { program } from "commander";
|
|
11
11
|
function getApiUrl() {
|
|
12
12
|
const opts = program.opts();
|
|
13
|
-
return opts.api
|
|
13
|
+
return opts.api ?? "http://localhost:4000";
|
|
14
14
|
}
|
|
15
|
-
async function apiFetch(
|
|
16
|
-
const url = `${getApiUrl()}${
|
|
15
|
+
async function apiFetch(path4, init) {
|
|
16
|
+
const url = `${getApiUrl()}${path4}`;
|
|
17
17
|
const res = await fetch(url, init);
|
|
18
18
|
const data = await res.json().catch(() => ({}));
|
|
19
19
|
if (!res.ok) {
|
|
20
|
-
|
|
20
|
+
const message = typeof data === "object" && data !== null && "error" in data && typeof data.error === "string" ? data.error : `HTTP ${res.status.toString()}`;
|
|
21
|
+
throw new Error(message);
|
|
21
22
|
}
|
|
22
23
|
return data;
|
|
23
24
|
}
|
|
@@ -47,8 +48,85 @@ projectCmd.command("remove").description("Remove a project").argument("<id>", "p
|
|
|
47
48
|
});
|
|
48
49
|
|
|
49
50
|
// ../../apps/cli/src/commands/task.ts
|
|
51
|
+
import { Command as Command3 } from "commander";
|
|
52
|
+
|
|
53
|
+
// ../../apps/cli/src/commands/task-feed.ts
|
|
50
54
|
import { Command as Command2 } from "commander";
|
|
51
|
-
|
|
55
|
+
import * as grpc from "@grpc/grpc-js";
|
|
56
|
+
import * as protoLoader from "@grpc/proto-loader";
|
|
57
|
+
|
|
58
|
+
// ../../apps/cli/src/lib/proto-path.ts
|
|
59
|
+
import { existsSync } from "fs";
|
|
60
|
+
import path from "path";
|
|
61
|
+
import { fileURLToPath } from "url";
|
|
62
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
63
|
+
var __dirname = path.dirname(__filename);
|
|
64
|
+
function findProtoPath() {
|
|
65
|
+
const candidates = [
|
|
66
|
+
// Bundled / installed package layout: dist/proto/tasks.proto
|
|
67
|
+
path.join(__dirname, "proto/tasks.proto"),
|
|
68
|
+
// Development layout from apps/cli/src/lib: ../../../../proto/tasks.proto
|
|
69
|
+
path.resolve(__dirname, "../../../../proto/tasks.proto")
|
|
70
|
+
];
|
|
71
|
+
for (const candidate of candidates) {
|
|
72
|
+
if (existsSync(candidate)) return candidate;
|
|
73
|
+
}
|
|
74
|
+
return candidates[0] ?? "proto/tasks.proto";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ../../apps/cli/src/commands/task-feed.ts
|
|
78
|
+
var GRPC_TARGET = process.env.HARNESS_GRPC_TARGET ?? "localhost:50051";
|
|
79
|
+
var packageDefinition = protoLoader.loadSync(findProtoPath(), {
|
|
80
|
+
keepCase: true,
|
|
81
|
+
longs: String,
|
|
82
|
+
enums: String,
|
|
83
|
+
defaults: true,
|
|
84
|
+
oneofs: true
|
|
85
|
+
});
|
|
86
|
+
var proto = grpc.loadPackageDefinition(packageDefinition);
|
|
87
|
+
var taskFeedCmd = new Command2("feed").description("Feed a task into the harness via gRPC").requiredOption("--project <id>", "project id").requiredOption("--title <title>", "task title").option("--description <text>", "task description").option("--complexity <level>", "simple | medium | complex", "simple").option("--tags <tags>", "comma-separated tags").option("--auto-run", "run the task immediately after submission", false).option("--stream", "stream task updates after submission", false).action(
|
|
88
|
+
(opts) => {
|
|
89
|
+
const client = new proto.omega.TaskIngestion(
|
|
90
|
+
GRPC_TARGET,
|
|
91
|
+
grpc.credentials.createInsecure()
|
|
92
|
+
);
|
|
93
|
+
const request = {
|
|
94
|
+
project_id: opts.project,
|
|
95
|
+
title: opts.title,
|
|
96
|
+
description: opts.description ?? "",
|
|
97
|
+
complexity: opts.complexity,
|
|
98
|
+
tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : [],
|
|
99
|
+
auto_run: opts.autoRun
|
|
100
|
+
};
|
|
101
|
+
if (opts.stream) {
|
|
102
|
+
const call = client.streamTasks();
|
|
103
|
+
call.on("data", (event) => {
|
|
104
|
+
console.log(JSON.stringify(event, null, 2));
|
|
105
|
+
});
|
|
106
|
+
call.on("error", (err) => {
|
|
107
|
+
console.error("Stream error:", err.message);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
});
|
|
110
|
+
call.on("end", () => {
|
|
111
|
+
process.exit(0);
|
|
112
|
+
});
|
|
113
|
+
call.write({ project_id: opts.project });
|
|
114
|
+
}
|
|
115
|
+
client.submitTask(request, (err, response) => {
|
|
116
|
+
if (err) {
|
|
117
|
+
console.error("gRPC error:", err.message);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
console.log(JSON.stringify(response, null, 2));
|
|
121
|
+
if (!opts.stream) {
|
|
122
|
+
client.close();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// ../../apps/cli/src/commands/task.ts
|
|
129
|
+
var taskCmd = new Command3("task").description("Manage tasks");
|
|
52
130
|
taskCmd.command("create").description("Create a task").requiredOption("--project <id>", "project id").requiredOption("--title <title>", "task title").option("--description <text>", "task description").option("--complexity <level>", "simple | medium | complex", "simple").option("--tags <tags>", "comma-separated tags").action(async (opts) => {
|
|
53
131
|
const task = await apiFetch("/tasks", {
|
|
54
132
|
method: "POST",
|
|
@@ -72,13 +150,14 @@ taskCmd.command("run").description("Run a task through the router").argument("<i
|
|
|
72
150
|
const result = await apiFetch(`/tasks/${id}/run`, { method: "POST" });
|
|
73
151
|
console.log(JSON.stringify(result, null, 2));
|
|
74
152
|
});
|
|
153
|
+
taskCmd.addCommand(taskFeedCmd);
|
|
75
154
|
|
|
76
155
|
// ../../apps/cli/src/commands/ui.ts
|
|
77
|
-
import { Command as
|
|
156
|
+
import { Command as Command4 } from "commander";
|
|
78
157
|
import { spawn } from "child_process";
|
|
79
|
-
import { existsSync } from "fs";
|
|
80
|
-
import
|
|
81
|
-
import { fileURLToPath } from "url";
|
|
158
|
+
import { existsSync as existsSync2 } from "fs";
|
|
159
|
+
import path2 from "path";
|
|
160
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
82
161
|
import open from "open";
|
|
83
162
|
import { render } from "ink";
|
|
84
163
|
import React2 from "react";
|
|
@@ -87,7 +166,7 @@ import React2 from "react";
|
|
|
87
166
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
88
167
|
import { Box, Text, useApp, useInput, useWindowSize } from "ink";
|
|
89
168
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
90
|
-
var API = process.env.HARNESS_API_URL
|
|
169
|
+
var API = process.env.HARNESS_API_URL ?? "http://localhost:4000";
|
|
91
170
|
function formatTime(d) {
|
|
92
171
|
return d.toLocaleTimeString("en-US", { hour12: false });
|
|
93
172
|
}
|
|
@@ -131,7 +210,7 @@ function useTasks(pollMs = 1e3) {
|
|
|
131
210
|
const tick = async () => {
|
|
132
211
|
try {
|
|
133
212
|
const res = await fetch(`${API}/tasks`);
|
|
134
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
213
|
+
if (!res.ok) throw new Error(`HTTP ${res.status.toString()}`);
|
|
135
214
|
const data = await res.json();
|
|
136
215
|
setConnected(true);
|
|
137
216
|
const current = {};
|
|
@@ -142,8 +221,8 @@ function useTasks(pollMs = 1e3) {
|
|
|
142
221
|
addLog(`New task queued: "${task.title}"`, "info");
|
|
143
222
|
} else if (prev.status !== task.status) {
|
|
144
223
|
if (task.status === "in_progress") {
|
|
145
|
-
const provider = task.provider
|
|
146
|
-
const model = task.model
|
|
224
|
+
const provider = task.provider ?? "unknown";
|
|
225
|
+
const model = task.model ?? "unknown";
|
|
147
226
|
addLog(`LLM picked up "${task.title}" \u2192 ${provider}/${model}`, "info");
|
|
148
227
|
} else if (task.status === "done") {
|
|
149
228
|
addLog(`Completed: "${task.title}"`, "success");
|
|
@@ -153,8 +232,9 @@ function useTasks(pollMs = 1e3) {
|
|
|
153
232
|
}
|
|
154
233
|
}
|
|
155
234
|
for (const id of Object.keys(previousTasks.current)) {
|
|
156
|
-
|
|
157
|
-
|
|
235
|
+
const removed = previousTasks.current[id];
|
|
236
|
+
if (!current[id] && !id.startsWith("__") && removed) {
|
|
237
|
+
addLog(`Task removed: "${removed.title}"`, "warning");
|
|
158
238
|
}
|
|
159
239
|
}
|
|
160
240
|
previousTasks.current = current;
|
|
@@ -162,16 +242,18 @@ function useTasks(pollMs = 1e3) {
|
|
|
162
242
|
} catch (err) {
|
|
163
243
|
setConnected(false);
|
|
164
244
|
const message = err instanceof Error ? err.message : String(err);
|
|
165
|
-
if (message !== previousTasks.current
|
|
245
|
+
if (message !== previousTasks.current.__last_error?.title) {
|
|
166
246
|
addLog(`Connection lost: ${message}`, "error");
|
|
167
|
-
previousTasks.current
|
|
247
|
+
previousTasks.current.__last_error = { title: message };
|
|
168
248
|
}
|
|
169
249
|
}
|
|
170
250
|
if (!cancelled) {
|
|
171
|
-
setTimeout(
|
|
251
|
+
setTimeout(() => {
|
|
252
|
+
void tick();
|
|
253
|
+
}, pollMs);
|
|
172
254
|
}
|
|
173
255
|
};
|
|
174
|
-
tick();
|
|
256
|
+
void tick();
|
|
175
257
|
return () => {
|
|
176
258
|
cancelled = true;
|
|
177
259
|
};
|
|
@@ -229,7 +311,7 @@ function TaskList({
|
|
|
229
311
|
tasks,
|
|
230
312
|
height,
|
|
231
313
|
collapsed,
|
|
232
|
-
toggle,
|
|
314
|
+
toggle: _toggle,
|
|
233
315
|
columns
|
|
234
316
|
}) {
|
|
235
317
|
const sorted = useMemo(
|
|
@@ -258,7 +340,7 @@ function TaskList({
|
|
|
258
340
|
header,
|
|
259
341
|
visible.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No tasks yet. Create one in the web UI." }),
|
|
260
342
|
visible.map((task) => {
|
|
261
|
-
const providerText = task.provider ? `${task.provider}/${task.model}` : "-";
|
|
343
|
+
const providerText = task.provider ? `${task.provider}/${task.model ?? ""}` : "-";
|
|
262
344
|
const titleMax = Math.max(10, columns - 50);
|
|
263
345
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
264
346
|
/* @__PURE__ */ jsx(Text, { color: statusColor(task.status), children: statusSymbol(task.status) }),
|
|
@@ -321,7 +403,9 @@ function TuiApp() {
|
|
|
321
403
|
tasks,
|
|
322
404
|
height: taskListHeight,
|
|
323
405
|
collapsed: tasksCollapsed,
|
|
324
|
-
toggle: () =>
|
|
406
|
+
toggle: () => {
|
|
407
|
+
setTasksCollapsed((c) => !c);
|
|
408
|
+
},
|
|
325
409
|
columns: taskListWidth ?? columns
|
|
326
410
|
}
|
|
327
411
|
),
|
|
@@ -332,12 +416,12 @@ function TuiApp() {
|
|
|
332
416
|
}
|
|
333
417
|
|
|
334
418
|
// ../../apps/cli/src/commands/ui.ts
|
|
335
|
-
var
|
|
336
|
-
var
|
|
337
|
-
var API2 = process.env.HARNESS_API_URL
|
|
419
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
420
|
+
var __dirname2 = path2.dirname(__filename2);
|
|
421
|
+
var API2 = process.env.HARNESS_API_URL ?? "http://localhost:4000";
|
|
338
422
|
function startBundledServer() {
|
|
339
|
-
const serverPath =
|
|
340
|
-
if (
|
|
423
|
+
const serverPath = path2.resolve(__dirname2, "server.js");
|
|
424
|
+
if (existsSync2(serverPath)) {
|
|
341
425
|
return spawn(process.execPath, [serverPath], { stdio: "inherit" });
|
|
342
426
|
}
|
|
343
427
|
return void 0;
|
|
@@ -360,7 +444,7 @@ async function waitForApi(maxMs = 15e3) {
|
|
|
360
444
|
}
|
|
361
445
|
async function ensureProject() {
|
|
362
446
|
const cwd = process.cwd();
|
|
363
|
-
const name =
|
|
447
|
+
const name = path2.basename(cwd);
|
|
364
448
|
try {
|
|
365
449
|
const listRes = await fetch(`${API2}/projects`);
|
|
366
450
|
const projects = await listRes.json();
|
|
@@ -383,10 +467,10 @@ async function ensureProject() {
|
|
|
383
467
|
console.warn("Could not auto-detect project:", err);
|
|
384
468
|
}
|
|
385
469
|
}
|
|
386
|
-
var uiCmd = new
|
|
470
|
+
var uiCmd = new Command4("ui").description("Open the harness web UI").option("--no-tui", "Do not open the terminal console alongside the web UI").action(async (options) => {
|
|
387
471
|
let server = void 0;
|
|
388
472
|
const alreadyRunning = await isApiReady();
|
|
389
|
-
const useTui = options.tui
|
|
473
|
+
const useTui = options.tui;
|
|
390
474
|
if (!alreadyRunning) {
|
|
391
475
|
server = startBundledServer() ?? spawn("pnpm", ["--filter", "@omega/server", "dev"], {
|
|
392
476
|
stdio: "inherit",
|
|
@@ -421,10 +505,10 @@ var uiCmd = new Command3("ui").description("Open the harness web UI").option("--
|
|
|
421
505
|
});
|
|
422
506
|
|
|
423
507
|
// ../../apps/cli/src/commands/console.ts
|
|
424
|
-
import { Command as
|
|
508
|
+
import { Command as Command5 } from "commander";
|
|
425
509
|
import { render as render2 } from "ink";
|
|
426
510
|
import React3 from "react";
|
|
427
|
-
var consoleCmd = new
|
|
511
|
+
var consoleCmd = new Command5("console").description("Open the harness TUI console").action(() => {
|
|
428
512
|
if (!process.stdin.isTTY) {
|
|
429
513
|
console.error("The harness console requires an interactive terminal (TTY).");
|
|
430
514
|
process.exit(1);
|
|
@@ -433,9 +517,9 @@ var consoleCmd = new Command4("console").description("Open the harness TUI conso
|
|
|
433
517
|
});
|
|
434
518
|
|
|
435
519
|
// ../../apps/cli/src/commands/skill.ts
|
|
436
|
-
import { Command as
|
|
437
|
-
import
|
|
438
|
-
import { fileURLToPath as
|
|
520
|
+
import { Command as Command6 } from "commander";
|
|
521
|
+
import path3 from "path";
|
|
522
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
439
523
|
|
|
440
524
|
// ../skills/src/parse.ts
|
|
441
525
|
import matter from "gray-matter";
|
|
@@ -456,14 +540,14 @@ function parseSkill(md) {
|
|
|
456
540
|
}
|
|
457
541
|
args = frontmatter.args.map((entry, index) => {
|
|
458
542
|
if (typeof entry !== "object" || entry === null) {
|
|
459
|
-
throw new Error(`SKILL.md arg at index ${index} must be an object`);
|
|
543
|
+
throw new Error(`SKILL.md arg at index ${index.toString()} must be an object`);
|
|
460
544
|
}
|
|
461
545
|
const arg = entry;
|
|
462
546
|
if (typeof arg.name !== "string" || arg.name.trim() === "") {
|
|
463
|
-
throw new Error(`SKILL.md arg at index ${index} must have a non-empty "name"`);
|
|
547
|
+
throw new Error(`SKILL.md arg at index ${index.toString()} must have a non-empty "name"`);
|
|
464
548
|
}
|
|
465
549
|
if (typeof arg.type !== "string" || arg.type.trim() === "") {
|
|
466
|
-
throw new Error(`SKILL.md arg at index ${index} must have a non-empty "type"`);
|
|
550
|
+
throw new Error(`SKILL.md arg at index ${index.toString()} must have a non-empty "type"`);
|
|
467
551
|
}
|
|
468
552
|
return {
|
|
469
553
|
name: arg.name.trim(),
|
|
@@ -536,22 +620,22 @@ async function registerSkill(sourcePath, outputDir) {
|
|
|
536
620
|
// ../../apps/cli/src/commands/skill.ts
|
|
537
621
|
function getOutputDir() {
|
|
538
622
|
if (process.env.OMEGA_HARNESS_ROOT) {
|
|
539
|
-
return
|
|
623
|
+
return path3.join(path3.resolve(process.env.OMEGA_HARNESS_ROOT), "packages/skills/src");
|
|
540
624
|
}
|
|
541
625
|
try {
|
|
542
|
-
const
|
|
543
|
-
const
|
|
544
|
-
return
|
|
626
|
+
const __filename3 = fileURLToPath3(import.meta.url);
|
|
627
|
+
const __dirname3 = path3.dirname(__filename3);
|
|
628
|
+
return path3.join(path3.resolve(__dirname3, "../../../.."), "packages/skills/src");
|
|
545
629
|
} catch {
|
|
546
|
-
return
|
|
630
|
+
return path3.join(process.cwd(), "harness-skills");
|
|
547
631
|
}
|
|
548
632
|
}
|
|
549
|
-
var skillCmd = new
|
|
633
|
+
var skillCmd = new Command6("skill").description("Manage skill artifacts");
|
|
550
634
|
skillCmd.command("generate").description("Generate a harness adapter from a SKILL.md file").argument("<source>", "path to SKILL.md").action(async (source) => {
|
|
551
635
|
const outputDir = getOutputDir();
|
|
552
|
-
const manifest = await registerSkill(
|
|
636
|
+
const manifest = await registerSkill(path3.resolve(source), outputDir);
|
|
553
637
|
console.log(`Generated adapter for skill: ${manifest.name}`);
|
|
554
|
-
console.log(`Output directory: ${
|
|
638
|
+
console.log(`Output directory: ${path3.join(outputDir, "generated")}`);
|
|
555
639
|
});
|
|
556
640
|
|
|
557
641
|
// ../../apps/cli/src/index.ts
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
|
|
3
|
+
package omega;
|
|
4
|
+
|
|
5
|
+
service TaskIngestion {
|
|
6
|
+
rpc SubmitTask (TaskRequest) returns (TaskResponse);
|
|
7
|
+
rpc StreamTasks (TaskStreamRequest) returns (stream TaskEvent);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
message TaskRequest {
|
|
11
|
+
string project_id = 1;
|
|
12
|
+
string title = 2;
|
|
13
|
+
string description = 3;
|
|
14
|
+
string complexity = 4;
|
|
15
|
+
repeated string tags = 5;
|
|
16
|
+
bool auto_run = 6;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
message TaskResponse {
|
|
20
|
+
string id = 1;
|
|
21
|
+
string status = 2;
|
|
22
|
+
string error = 3;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
message TaskStreamRequest {
|
|
26
|
+
string project_id = 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
message TaskEvent {
|
|
30
|
+
string id = 1;
|
|
31
|
+
string title = 2;
|
|
32
|
+
string status = 3;
|
|
33
|
+
string provider = 4;
|
|
34
|
+
string model = 5;
|
|
35
|
+
string result = 6;
|
|
36
|
+
string error = 7;
|
|
37
|
+
}
|