@castlemilk/omega 0.1.8 → 0.2.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 +125 -59
- package/dist/server.js +70 -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 +8 -2
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
15
|
async function apiFetch(path3, init) {
|
|
16
16
|
const url = `${getApiUrl()}${path3}`;
|
|
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
|
}
|
|
@@ -85,9 +86,9 @@ import React2 from "react";
|
|
|
85
86
|
|
|
86
87
|
// ../../apps/cli/src/commands/tui.tsx
|
|
87
88
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
88
|
-
import { Box, Text, useApp, useInput } from "ink";
|
|
89
|
+
import { Box, Text, useApp, useInput, useWindowSize } from "ink";
|
|
89
90
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
90
|
-
var API = process.env.HARNESS_API_URL
|
|
91
|
+
var API = process.env.HARNESS_API_URL ?? "http://localhost:4000";
|
|
91
92
|
function formatTime(d) {
|
|
92
93
|
return d.toLocaleTimeString("en-US", { hour12: false });
|
|
93
94
|
}
|
|
@@ -123,7 +124,7 @@ function useTasks(pollMs = 1e3) {
|
|
|
123
124
|
const addLog = (message, level = "info") => {
|
|
124
125
|
setLogs((prev) => {
|
|
125
126
|
const next = [...prev, { time: formatTime(/* @__PURE__ */ new Date()), message, level }];
|
|
126
|
-
return next.slice(-
|
|
127
|
+
return next.slice(-200);
|
|
127
128
|
});
|
|
128
129
|
};
|
|
129
130
|
useEffect(() => {
|
|
@@ -131,7 +132,7 @@ function useTasks(pollMs = 1e3) {
|
|
|
131
132
|
const tick = async () => {
|
|
132
133
|
try {
|
|
133
134
|
const res = await fetch(`${API}/tasks`);
|
|
134
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
135
|
+
if (!res.ok) throw new Error(`HTTP ${res.status.toString()}`);
|
|
135
136
|
const data = await res.json();
|
|
136
137
|
setConnected(true);
|
|
137
138
|
const current = {};
|
|
@@ -142,8 +143,8 @@ function useTasks(pollMs = 1e3) {
|
|
|
142
143
|
addLog(`New task queued: "${task.title}"`, "info");
|
|
143
144
|
} else if (prev.status !== task.status) {
|
|
144
145
|
if (task.status === "in_progress") {
|
|
145
|
-
const provider = task.provider
|
|
146
|
-
const model = task.model
|
|
146
|
+
const provider = task.provider ?? "unknown";
|
|
147
|
+
const model = task.model ?? "unknown";
|
|
147
148
|
addLog(`LLM picked up "${task.title}" \u2192 ${provider}/${model}`, "info");
|
|
148
149
|
} else if (task.status === "done") {
|
|
149
150
|
addLog(`Completed: "${task.title}"`, "success");
|
|
@@ -153,8 +154,9 @@ function useTasks(pollMs = 1e3) {
|
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
for (const id of Object.keys(previousTasks.current)) {
|
|
156
|
-
|
|
157
|
-
|
|
157
|
+
const removed = previousTasks.current[id];
|
|
158
|
+
if (!current[id] && !id.startsWith("__") && removed) {
|
|
159
|
+
addLog(`Task removed: "${removed.title}"`, "warning");
|
|
158
160
|
}
|
|
159
161
|
}
|
|
160
162
|
previousTasks.current = current;
|
|
@@ -162,24 +164,30 @@ function useTasks(pollMs = 1e3) {
|
|
|
162
164
|
} catch (err) {
|
|
163
165
|
setConnected(false);
|
|
164
166
|
const message = err instanceof Error ? err.message : String(err);
|
|
165
|
-
if (message !== previousTasks.current
|
|
167
|
+
if (message !== previousTasks.current.__last_error?.title) {
|
|
166
168
|
addLog(`Connection lost: ${message}`, "error");
|
|
167
|
-
previousTasks.current
|
|
169
|
+
previousTasks.current.__last_error = { title: message };
|
|
168
170
|
}
|
|
169
171
|
}
|
|
170
172
|
if (!cancelled) {
|
|
171
|
-
setTimeout(
|
|
173
|
+
setTimeout(() => {
|
|
174
|
+
void tick();
|
|
175
|
+
}, pollMs);
|
|
172
176
|
}
|
|
173
177
|
};
|
|
174
|
-
tick();
|
|
178
|
+
void tick();
|
|
175
179
|
return () => {
|
|
176
180
|
cancelled = true;
|
|
177
181
|
};
|
|
178
182
|
}, [pollMs]);
|
|
179
183
|
return { tasks, logs, connected };
|
|
180
184
|
}
|
|
185
|
+
function truncate(str, max) {
|
|
186
|
+
if (str.length <= max) return str;
|
|
187
|
+
return str.slice(0, max - 1) + "\u2026";
|
|
188
|
+
}
|
|
181
189
|
function Header({ connected }) {
|
|
182
|
-
return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", paddingX: 1, children: [
|
|
190
|
+
return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", paddingX: 1, height: 3, flexShrink: 0, children: [
|
|
183
191
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "Omega Harness Console" }),
|
|
184
192
|
/* @__PURE__ */ jsx(Box, { flexGrow: 1 }),
|
|
185
193
|
/* @__PURE__ */ jsx(Text, { color: connected ? "green" : "red", children: connected ? "\u25CF connected" : "\u25CF disconnected" }),
|
|
@@ -189,39 +197,6 @@ function Header({ connected }) {
|
|
|
189
197
|
] })
|
|
190
198
|
] });
|
|
191
199
|
}
|
|
192
|
-
function TaskList({ tasks }) {
|
|
193
|
-
const sorted = useMemo(
|
|
194
|
-
() => [...tasks].sort(
|
|
195
|
-
(a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
|
|
196
|
-
),
|
|
197
|
-
[tasks]
|
|
198
|
-
);
|
|
199
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "single", paddingX: 1, minHeight: 10, children: [
|
|
200
|
-
/* @__PURE__ */ jsxs(Text, { bold: true, underline: true, children: [
|
|
201
|
-
"Tasks (",
|
|
202
|
-
tasks.length,
|
|
203
|
-
")"
|
|
204
|
-
] }),
|
|
205
|
-
sorted.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No tasks yet. Create one in the web UI." }),
|
|
206
|
-
sorted.map((task) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
207
|
-
/* @__PURE__ */ jsx(Text, { color: statusColor(task.status), children: statusSymbol(task.status) }),
|
|
208
|
-
/* @__PURE__ */ jsx(Box, { width: 20, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: formatTime(new Date(task.updatedAt)) }) }),
|
|
209
|
-
/* @__PURE__ */ jsx(Box, { width: 12, children: /* @__PURE__ */ jsx(Text, { color: statusColor(task.status), bold: true, children: task.status.toUpperCase() }) }),
|
|
210
|
-
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: task.provider ? `${task.provider}/${task.model}` : "-" }) }),
|
|
211
|
-
/* @__PURE__ */ jsx(Box, { flexGrow: 1, children: /* @__PURE__ */ jsx(Text, { children: task.title }) })
|
|
212
|
-
] }, task.id))
|
|
213
|
-
] });
|
|
214
|
-
}
|
|
215
|
-
function ConsoleLog({ logs }) {
|
|
216
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "single", paddingX: 1, flexGrow: 1, children: [
|
|
217
|
-
/* @__PURE__ */ jsx(Text, { bold: true, underline: true, children: "Console" }),
|
|
218
|
-
logs.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Waiting for activity..." }),
|
|
219
|
-
logs.map((log, i) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
220
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: log.time }),
|
|
221
|
-
/* @__PURE__ */ jsx(Text, { color: log.level, children: log.message })
|
|
222
|
-
] }, i))
|
|
223
|
-
] });
|
|
224
|
-
}
|
|
225
200
|
function Stats({ tasks }) {
|
|
226
201
|
const counts = useMemo(() => {
|
|
227
202
|
const total = tasks.length;
|
|
@@ -231,7 +206,7 @@ function Stats({ tasks }) {
|
|
|
231
206
|
const failed = tasks.filter((t) => t.status === "failed").length;
|
|
232
207
|
return { total, todo, inProgress, done, failed };
|
|
233
208
|
}, [tasks]);
|
|
234
|
-
return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", paddingX: 1, gap: 2, children: [
|
|
209
|
+
return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", paddingX: 1, height: 3, flexShrink: 0, gap: 2, children: [
|
|
235
210
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
236
211
|
"total: ",
|
|
237
212
|
counts.total
|
|
@@ -254,27 +229,118 @@ function Stats({ tasks }) {
|
|
|
254
229
|
] })
|
|
255
230
|
] });
|
|
256
231
|
}
|
|
232
|
+
function TaskList({
|
|
233
|
+
tasks,
|
|
234
|
+
height,
|
|
235
|
+
collapsed,
|
|
236
|
+
toggle: _toggle,
|
|
237
|
+
columns
|
|
238
|
+
}) {
|
|
239
|
+
const sorted = useMemo(
|
|
240
|
+
() => [...tasks].sort(
|
|
241
|
+
(a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
|
|
242
|
+
),
|
|
243
|
+
[tasks]
|
|
244
|
+
);
|
|
245
|
+
const header = /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
246
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, underline: true, children: [
|
|
247
|
+
"Tasks (",
|
|
248
|
+
tasks.length,
|
|
249
|
+
")"
|
|
250
|
+
] }),
|
|
251
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
252
|
+
"[t] ",
|
|
253
|
+
collapsed ? "expand" : "collapse"
|
|
254
|
+
] })
|
|
255
|
+
] });
|
|
256
|
+
if (collapsed) {
|
|
257
|
+
return /* @__PURE__ */ jsx(Box, { borderStyle: "single", paddingX: 1, height: 3, flexShrink: 0, children: header });
|
|
258
|
+
}
|
|
259
|
+
const availableLines = Math.max(0, height - 3);
|
|
260
|
+
const visible = sorted.slice(0, availableLines);
|
|
261
|
+
return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", paddingX: 1, flexDirection: "column", height, flexShrink: 0, children: [
|
|
262
|
+
header,
|
|
263
|
+
visible.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No tasks yet. Create one in the web UI." }),
|
|
264
|
+
visible.map((task) => {
|
|
265
|
+
const providerText = task.provider ? `${task.provider}/${task.model ?? ""}` : "-";
|
|
266
|
+
const titleMax = Math.max(10, columns - 50);
|
|
267
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
268
|
+
/* @__PURE__ */ jsx(Text, { color: statusColor(task.status), children: statusSymbol(task.status) }),
|
|
269
|
+
/* @__PURE__ */ jsx(Box, { width: 20, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: formatTime(new Date(task.updatedAt)) }) }),
|
|
270
|
+
/* @__PURE__ */ jsx(Box, { width: 12, children: /* @__PURE__ */ jsx(Text, { color: statusColor(task.status), bold: true, children: task.status.toUpperCase() }) }),
|
|
271
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: truncate(providerText, 23) }) }),
|
|
272
|
+
/* @__PURE__ */ jsx(Box, { flexGrow: 1, children: /* @__PURE__ */ jsx(Text, { children: truncate(task.title, titleMax) }) })
|
|
273
|
+
] }, task.id);
|
|
274
|
+
})
|
|
275
|
+
] });
|
|
276
|
+
}
|
|
277
|
+
function ConsoleLog({
|
|
278
|
+
logs,
|
|
279
|
+
height,
|
|
280
|
+
columns
|
|
281
|
+
}) {
|
|
282
|
+
const availableLines = Math.max(0, height - 3);
|
|
283
|
+
const visible = logs.slice(-availableLines);
|
|
284
|
+
return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", paddingX: 1, flexDirection: "column", height, flexGrow: 1, children: [
|
|
285
|
+
/* @__PURE__ */ jsx(Text, { bold: true, underline: true, children: "Console" }),
|
|
286
|
+
visible.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Waiting for activity..." }),
|
|
287
|
+
visible.map((log, i) => {
|
|
288
|
+
const timeWidth = 10;
|
|
289
|
+
const msgMax = Math.max(10, columns - timeWidth - 4);
|
|
290
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
291
|
+
/* @__PURE__ */ jsx(Box, { width: timeWidth, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: log.time }) }),
|
|
292
|
+
/* @__PURE__ */ jsx(Box, { flexGrow: 1, children: /* @__PURE__ */ jsx(Text, { color: log.level, children: truncate(log.message, msgMax) }) })
|
|
293
|
+
] }, i);
|
|
294
|
+
})
|
|
295
|
+
] });
|
|
296
|
+
}
|
|
257
297
|
function TuiApp() {
|
|
258
298
|
const { exit } = useApp();
|
|
259
299
|
const { tasks, logs, connected } = useTasks();
|
|
300
|
+
const { columns, rows } = useWindowSize();
|
|
301
|
+
const [tasksCollapsed, setTasksCollapsed] = useState(false);
|
|
302
|
+
const [sidebar, setSidebar] = useState(false);
|
|
260
303
|
useInput((input, key) => {
|
|
261
304
|
if (input === "q" || key.escape) {
|
|
262
305
|
exit();
|
|
263
306
|
}
|
|
307
|
+
if (input === "t") {
|
|
308
|
+
setTasksCollapsed((c) => !c);
|
|
309
|
+
}
|
|
310
|
+
if (input === "s") {
|
|
311
|
+
setSidebar((s) => !s);
|
|
312
|
+
}
|
|
264
313
|
});
|
|
265
|
-
|
|
314
|
+
const mainHeight = Math.max(6, rows - 9);
|
|
315
|
+
const taskListWidth = sidebar ? Math.max(30, Math.floor(columns * 0.4)) : void 0;
|
|
316
|
+
const taskListHeight = sidebar ? mainHeight : tasksCollapsed ? 3 : Math.max(3, Math.floor(mainHeight * 0.45));
|
|
317
|
+
const consoleHeight = sidebar ? mainHeight : mainHeight - taskListHeight;
|
|
318
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", height: rows, width: columns, gap: 1, padding: 1, children: [
|
|
266
319
|
/* @__PURE__ */ jsx(Header, { connected }),
|
|
267
320
|
/* @__PURE__ */ jsx(Stats, { tasks }),
|
|
268
|
-
/* @__PURE__ */
|
|
269
|
-
|
|
270
|
-
|
|
321
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: sidebar ? "row" : "column", gap: 1, flexGrow: 1, children: [
|
|
322
|
+
/* @__PURE__ */ jsx(
|
|
323
|
+
TaskList,
|
|
324
|
+
{
|
|
325
|
+
tasks,
|
|
326
|
+
height: taskListHeight,
|
|
327
|
+
collapsed: tasksCollapsed,
|
|
328
|
+
toggle: () => {
|
|
329
|
+
setTasksCollapsed((c) => !c);
|
|
330
|
+
},
|
|
331
|
+
columns: taskListWidth ?? columns
|
|
332
|
+
}
|
|
333
|
+
),
|
|
334
|
+
/* @__PURE__ */ jsx(ConsoleLog, { logs, height: consoleHeight, columns })
|
|
335
|
+
] }),
|
|
336
|
+
/* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "q/esc quit \u2022 t toggle tasks \u2022 s toggle sidebar" }) })
|
|
271
337
|
] });
|
|
272
338
|
}
|
|
273
339
|
|
|
274
340
|
// ../../apps/cli/src/commands/ui.ts
|
|
275
341
|
var __filename = fileURLToPath(import.meta.url);
|
|
276
342
|
var __dirname = path.dirname(__filename);
|
|
277
|
-
var API2 = process.env.HARNESS_API_URL
|
|
343
|
+
var API2 = process.env.HARNESS_API_URL ?? "http://localhost:4000";
|
|
278
344
|
function startBundledServer() {
|
|
279
345
|
const serverPath = path.resolve(__dirname, "server.js");
|
|
280
346
|
if (existsSync(serverPath)) {
|
|
@@ -326,7 +392,7 @@ async function ensureProject() {
|
|
|
326
392
|
var uiCmd = new Command3("ui").description("Open the harness web UI").option("--no-tui", "Do not open the terminal console alongside the web UI").action(async (options) => {
|
|
327
393
|
let server = void 0;
|
|
328
394
|
const alreadyRunning = await isApiReady();
|
|
329
|
-
const useTui = options.tui
|
|
395
|
+
const useTui = options.tui;
|
|
330
396
|
if (!alreadyRunning) {
|
|
331
397
|
server = startBundledServer() ?? spawn("pnpm", ["--filter", "@omega/server", "dev"], {
|
|
332
398
|
stdio: "inherit",
|
|
@@ -364,7 +430,7 @@ var uiCmd = new Command3("ui").description("Open the harness web UI").option("--
|
|
|
364
430
|
import { Command as Command4 } from "commander";
|
|
365
431
|
import { render as render2 } from "ink";
|
|
366
432
|
import React3 from "react";
|
|
367
|
-
var consoleCmd = new Command4("console").description("Open the harness TUI console").action(
|
|
433
|
+
var consoleCmd = new Command4("console").description("Open the harness TUI console").action(() => {
|
|
368
434
|
if (!process.stdin.isTTY) {
|
|
369
435
|
console.error("The harness console requires an interactive terminal (TTY).");
|
|
370
436
|
process.exit(1);
|
|
@@ -396,14 +462,14 @@ function parseSkill(md) {
|
|
|
396
462
|
}
|
|
397
463
|
args = frontmatter.args.map((entry, index) => {
|
|
398
464
|
if (typeof entry !== "object" || entry === null) {
|
|
399
|
-
throw new Error(`SKILL.md arg at index ${index} must be an object`);
|
|
465
|
+
throw new Error(`SKILL.md arg at index ${index.toString()} must be an object`);
|
|
400
466
|
}
|
|
401
467
|
const arg = entry;
|
|
402
468
|
if (typeof arg.name !== "string" || arg.name.trim() === "") {
|
|
403
|
-
throw new Error(`SKILL.md arg at index ${index} must have a non-empty "name"`);
|
|
469
|
+
throw new Error(`SKILL.md arg at index ${index.toString()} must have a non-empty "name"`);
|
|
404
470
|
}
|
|
405
471
|
if (typeof arg.type !== "string" || arg.type.trim() === "") {
|
|
406
|
-
throw new Error(`SKILL.md arg at index ${index} must have a non-empty "type"`);
|
|
472
|
+
throw new Error(`SKILL.md arg at index ${index.toString()} must have a non-empty "type"`);
|
|
407
473
|
}
|
|
408
474
|
return {
|
|
409
475
|
name: arg.name.trim(),
|
package/dist/server.js
CHANGED
|
@@ -5,6 +5,7 @@ import express from "express";
|
|
|
5
5
|
import cors from "cors";
|
|
6
6
|
import path from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
|
+
import { z } from "zod";
|
|
8
9
|
|
|
9
10
|
// ../providers/src/openai.ts
|
|
10
11
|
var DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
@@ -41,7 +42,7 @@ var OpenAIProvider = class {
|
|
|
41
42
|
})
|
|
42
43
|
});
|
|
43
44
|
if (!res.ok) {
|
|
44
|
-
throw new Error(`OpenAI request failed: ${res.status} ${res.statusText}`);
|
|
45
|
+
throw new Error(`OpenAI request failed: ${res.status.toString()} ${res.statusText}`);
|
|
45
46
|
}
|
|
46
47
|
const data = await res.json();
|
|
47
48
|
return data.choices?.[0]?.message?.content ?? "";
|
|
@@ -86,7 +87,7 @@ var AnthropicProvider = class {
|
|
|
86
87
|
body: JSON.stringify(body)
|
|
87
88
|
});
|
|
88
89
|
if (!res.ok) {
|
|
89
|
-
throw new Error(`Anthropic request failed: ${res.status} ${res.statusText}`);
|
|
90
|
+
throw new Error(`Anthropic request failed: ${res.status.toString()} ${res.statusText}`);
|
|
90
91
|
}
|
|
91
92
|
const data = await res.json();
|
|
92
93
|
return data.content?.find((c) => c.type === "text")?.text ?? "";
|
|
@@ -130,7 +131,7 @@ var OllamaProvider = class {
|
|
|
130
131
|
})
|
|
131
132
|
});
|
|
132
133
|
if (!res.ok) {
|
|
133
|
-
throw new Error(`Ollama request failed: ${res.status} ${res.statusText}`);
|
|
134
|
+
throw new Error(`Ollama request failed: ${res.status.toString()} ${res.statusText}`);
|
|
134
135
|
}
|
|
135
136
|
const data = await res.json();
|
|
136
137
|
return data.message?.content ?? "";
|
|
@@ -174,7 +175,7 @@ var GeminiProvider = class {
|
|
|
174
175
|
body: JSON.stringify(body)
|
|
175
176
|
});
|
|
176
177
|
if (!res.ok) {
|
|
177
|
-
throw new Error(`Gemini request failed: ${res.status} ${res.statusText}`);
|
|
178
|
+
throw new Error(`Gemini request failed: ${res.status.toString()} ${res.statusText}`);
|
|
178
179
|
}
|
|
179
180
|
const data = await res.json();
|
|
180
181
|
return data.candidates?.[0]?.content?.parts?.map((p) => p.text).join("") ?? "";
|
|
@@ -186,7 +187,7 @@ var KimiProvider = class extends OpenAIProvider {
|
|
|
186
187
|
constructor(config) {
|
|
187
188
|
super({
|
|
188
189
|
...config,
|
|
189
|
-
baseUrl: config.baseUrl
|
|
190
|
+
baseUrl: config.baseUrl ?? "https://api.moonshot.ai/v1"
|
|
190
191
|
});
|
|
191
192
|
this.config = config;
|
|
192
193
|
}
|
|
@@ -212,7 +213,7 @@ function createProvider(config) {
|
|
|
212
213
|
case "generic":
|
|
213
214
|
return new GenericProvider(config);
|
|
214
215
|
default:
|
|
215
|
-
throw new Error(`Unknown provider kind: ${config.kind}`);
|
|
216
|
+
throw new Error(`Unknown provider kind: ${String(config.kind)}`);
|
|
216
217
|
}
|
|
217
218
|
}
|
|
218
219
|
|
|
@@ -255,9 +256,11 @@ function selectProvider(configs, rules2, task) {
|
|
|
255
256
|
return true;
|
|
256
257
|
}).sort((a, b) => b.priority - a.priority);
|
|
257
258
|
for (const rule of matchingRules) {
|
|
258
|
-
const provider = rule.then.provider ? enabled.find(
|
|
259
|
+
const provider = rule.then.provider ? enabled.find(
|
|
260
|
+
(cfg) => cfg.id === rule.then.provider || cfg.name === rule.then.provider
|
|
261
|
+
) : enabled[0];
|
|
259
262
|
if (provider) {
|
|
260
|
-
return { provider, model: rule.then.model
|
|
263
|
+
return { provider, model: rule.then.model ?? provider.defaultModel };
|
|
261
264
|
}
|
|
262
265
|
}
|
|
263
266
|
let best;
|
|
@@ -275,6 +278,11 @@ function selectProvider(configs, rules2, task) {
|
|
|
275
278
|
// src/server.ts
|
|
276
279
|
var __filename = fileURLToPath(import.meta.url);
|
|
277
280
|
var __dirname = path.dirname(__filename);
|
|
281
|
+
function asyncHandler(fn) {
|
|
282
|
+
return (req, res, next) => {
|
|
283
|
+
Promise.resolve(fn(req, res, next)).catch(next);
|
|
284
|
+
};
|
|
285
|
+
}
|
|
278
286
|
var app = express();
|
|
279
287
|
app.use(cors());
|
|
280
288
|
app.use(express.json());
|
|
@@ -305,11 +313,44 @@ if (process.env.KIMI_API_KEY) {
|
|
|
305
313
|
}
|
|
306
314
|
var rules = [];
|
|
307
315
|
function newId() {
|
|
308
|
-
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
316
|
+
return `${Date.now().toString()}-${Math.random().toString(36).slice(2)}`;
|
|
309
317
|
}
|
|
310
318
|
function now() {
|
|
311
319
|
return /* @__PURE__ */ new Date();
|
|
312
320
|
}
|
|
321
|
+
function parseCapabilities(value) {
|
|
322
|
+
if (Array.isArray(value)) return value;
|
|
323
|
+
if (typeof value === "string") return JSON.parse(value);
|
|
324
|
+
return [];
|
|
325
|
+
}
|
|
326
|
+
var projectCreateSchema = z.object({
|
|
327
|
+
name: z.string().min(1),
|
|
328
|
+
path: z.string().min(1),
|
|
329
|
+
repoUrl: z.string().optional(),
|
|
330
|
+
description: z.string().optional(),
|
|
331
|
+
env: z.record(z.string()).optional()
|
|
332
|
+
});
|
|
333
|
+
var taskCreateSchema = z.object({
|
|
334
|
+
projectId: z.string().min(1),
|
|
335
|
+
title: z.string().min(1),
|
|
336
|
+
description: z.string().optional(),
|
|
337
|
+
complexity: z.enum(["simple", "medium", "complex"]).default("simple"),
|
|
338
|
+
tags: z.array(z.string()).default([])
|
|
339
|
+
});
|
|
340
|
+
var providerCreateSchema = z.object({
|
|
341
|
+
name: z.string().min(1),
|
|
342
|
+
kind: z.enum(["openai", "anthropic", "ollama", "gemini", "kimi", "generic"]),
|
|
343
|
+
baseUrl: z.string().optional(),
|
|
344
|
+
apiKey: z.string().optional(),
|
|
345
|
+
defaultModel: z.string().min(1),
|
|
346
|
+
capabilities: z.union([z.string(), z.array(z.any())]).optional(),
|
|
347
|
+
enabled: z.boolean().optional()
|
|
348
|
+
});
|
|
349
|
+
var routerSelectSchema = z.object({
|
|
350
|
+
title: z.string().min(1),
|
|
351
|
+
complexity: z.enum(["simple", "medium", "complex"]).default("simple"),
|
|
352
|
+
tags: z.array(z.string()).default([])
|
|
353
|
+
});
|
|
313
354
|
app.get("/projects", (_req, res) => {
|
|
314
355
|
res.json(
|
|
315
356
|
projects.map((p) => ({
|
|
@@ -319,14 +360,10 @@ app.get("/projects", (_req, res) => {
|
|
|
319
360
|
);
|
|
320
361
|
});
|
|
321
362
|
app.post("/projects", (req, res) => {
|
|
322
|
-
const body = req.body;
|
|
363
|
+
const body = projectCreateSchema.parse(req.body);
|
|
323
364
|
const project = {
|
|
324
365
|
id: newId(),
|
|
325
|
-
|
|
326
|
-
path: body.path,
|
|
327
|
-
repoUrl: body.repoUrl,
|
|
328
|
-
description: body.description,
|
|
329
|
-
env: body.env,
|
|
366
|
+
...body,
|
|
330
367
|
createdAt: now()
|
|
331
368
|
};
|
|
332
369
|
projects.push(project);
|
|
@@ -338,19 +375,15 @@ app.delete("/projects/:id", (req, res) => {
|
|
|
338
375
|
res.status(204).send();
|
|
339
376
|
});
|
|
340
377
|
app.get("/tasks", (req, res) => {
|
|
341
|
-
const projectId = req.query.projectId;
|
|
378
|
+
const projectId = typeof req.query.projectId === "string" ? req.query.projectId : void 0;
|
|
342
379
|
res.json(tasks.filter((t) => projectId ? t.projectId === projectId : true));
|
|
343
380
|
});
|
|
344
381
|
app.post("/tasks", (req, res) => {
|
|
345
|
-
const body = req.body;
|
|
382
|
+
const body = taskCreateSchema.parse(req.body);
|
|
346
383
|
const task = {
|
|
347
384
|
id: newId(),
|
|
348
|
-
|
|
349
|
-
title: body.title,
|
|
350
|
-
description: body.description,
|
|
385
|
+
...body,
|
|
351
386
|
status: "todo",
|
|
352
|
-
complexity: body.complexity || "simple",
|
|
353
|
-
tags: body.tags || [],
|
|
354
387
|
createdAt: now(),
|
|
355
388
|
updatedAt: now()
|
|
356
389
|
};
|
|
@@ -363,9 +396,12 @@ app.patch("/tasks/:id", (req, res) => {
|
|
|
363
396
|
Object.assign(task, req.body, { updatedAt: now() });
|
|
364
397
|
res.json(task);
|
|
365
398
|
});
|
|
366
|
-
app.post("/tasks/:id/run", async (req, res) => {
|
|
399
|
+
app.post("/tasks/:id/run", asyncHandler(async (req, res) => {
|
|
367
400
|
const task = tasks.find((t) => t.id === req.params.id);
|
|
368
|
-
if (!task)
|
|
401
|
+
if (!task) {
|
|
402
|
+
res.status(404).json({ error: "Task not found" });
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
369
405
|
task.status = "in_progress";
|
|
370
406
|
task.error = void 0;
|
|
371
407
|
task.result = void 0;
|
|
@@ -373,7 +409,8 @@ app.post("/tasks/:id/run", async (req, res) => {
|
|
|
373
409
|
if (!selection) {
|
|
374
410
|
task.status = "failed";
|
|
375
411
|
task.error = "No provider available for this task";
|
|
376
|
-
|
|
412
|
+
res.json(task);
|
|
413
|
+
return;
|
|
377
414
|
}
|
|
378
415
|
try {
|
|
379
416
|
const provider = createProvider(selection.provider);
|
|
@@ -387,20 +424,16 @@ app.post("/tasks/:id/run", async (req, res) => {
|
|
|
387
424
|
task.error = err instanceof Error ? err.message : String(err);
|
|
388
425
|
}
|
|
389
426
|
res.json(task);
|
|
390
|
-
});
|
|
427
|
+
}));
|
|
391
428
|
app.get("/providers", (_req, res) => {
|
|
392
429
|
res.json(providerConfigs);
|
|
393
430
|
});
|
|
394
431
|
app.post("/providers", (req, res) => {
|
|
395
|
-
const body = req.body;
|
|
432
|
+
const body = providerCreateSchema.parse(req.body);
|
|
396
433
|
const cfg = {
|
|
397
434
|
id: newId(),
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
baseUrl: body.baseUrl,
|
|
401
|
-
apiKey: body.apiKey,
|
|
402
|
-
defaultModel: body.defaultModel,
|
|
403
|
-
capabilities: Array.isArray(body.capabilities) ? body.capabilities : JSON.parse(body.capabilities || "[]"),
|
|
435
|
+
...body,
|
|
436
|
+
capabilities: parseCapabilities(body.capabilities),
|
|
404
437
|
enabled: body.enabled ?? true
|
|
405
438
|
};
|
|
406
439
|
providerConfigs.push(cfg);
|
|
@@ -417,14 +450,12 @@ app.delete("/providers/:id", (req, res) => {
|
|
|
417
450
|
res.status(204).send();
|
|
418
451
|
});
|
|
419
452
|
app.post("/router/select", (req, res) => {
|
|
420
|
-
const body = req.body;
|
|
453
|
+
const body = routerSelectSchema.parse(req.body);
|
|
421
454
|
const task = {
|
|
422
455
|
id: "preview",
|
|
423
456
|
projectId: "preview",
|
|
424
|
-
|
|
457
|
+
...body,
|
|
425
458
|
status: "todo",
|
|
426
|
-
complexity: body.complexity || "simple",
|
|
427
|
-
tags: body.tags || [],
|
|
428
459
|
createdAt: now(),
|
|
429
460
|
updatedAt: now()
|
|
430
461
|
};
|
|
@@ -437,7 +468,7 @@ app.use(express.static(webDir));
|
|
|
437
468
|
app.get("*", (_req, res) => {
|
|
438
469
|
res.sendFile(path.join(webDir, "index.html"));
|
|
439
470
|
});
|
|
440
|
-
var PORT = process.env.PORT
|
|
471
|
+
var PORT = Number(process.env.PORT ?? 4e3);
|
|
441
472
|
app.listen(PORT, () => {
|
|
442
|
-
console.log(`Omega harness server on http://localhost:${PORT}`);
|
|
473
|
+
console.log(`Omega harness server on http://localhost:${PORT.toString()}`);
|
|
443
474
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":"AAOA,iBAAS,GAAG,
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":"AAOA,iBAAS,GAAG,gCAsDX;AAED,eAAe,GAAG,CAAC"}
|
package/dist/web/dist/App.js
CHANGED
|
@@ -30,10 +30,12 @@ function App() {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
useEffect(() => {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
void (async () => {
|
|
34
|
+
await loadProjects();
|
|
35
|
+
await loadProviders();
|
|
36
|
+
})();
|
|
35
37
|
}, []);
|
|
36
|
-
return (_jsxs("div", { className: "flex h-screen w-screen overflow-hidden", children: [_jsx(ProjectSidebar, { projects: projects, selectedId: selectedProjectId, onSelect: setSelectedProjectId, onChange: loadProjects }), _jsx(TaskBoard, { projectId: selectedProjectId }), _jsxs("aside", { className: "w-80 bg-white border-l border-gray-200 h-screen overflow-y-auto", children: [error && (_jsx("div", { className: "p-3 bg-red-50 text-red-700 text-xs border-b border-red-100", children: error })), _jsx(ProviderSettings, { providers: providers, onChange: loadProviders }), _jsx(RouterPanel, {})] })] }));
|
|
38
|
+
return (_jsxs("div", { className: "flex h-screen w-screen overflow-hidden", children: [_jsx(ProjectSidebar, { projects: projects, selectedId: selectedProjectId, onSelect: setSelectedProjectId, onChange: () => { void loadProjects(); } }), _jsx(TaskBoard, { projectId: selectedProjectId }), _jsxs("aside", { className: "w-80 bg-white border-l border-gray-200 h-screen overflow-y-auto", children: [error && (_jsx("div", { className: "p-3 bg-red-50 text-red-700 text-xs border-b border-red-100", children: error })), _jsx(ProviderSettings, { providers: providers, onChange: () => { void loadProviders(); } }), _jsx(RouterPanel, {})] })] }));
|
|
37
39
|
}
|
|
38
40
|
export default App;
|
|
39
41
|
//# sourceMappingURL=App.js.map
|
package/dist/web/dist/App.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.js","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,cAAc,EAAgB,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAiB,MAAM,kCAAkC,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE1D,SAAS,GAAG;IACV,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAY,EAAE,CAAC,CAAC;IACxD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAa,EAAE,CAAC,CAAC;IAC3D,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,QAAQ,EAAU,CAAC;IACrE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAEvC,KAAK,UAAU,YAAY;QACzB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;YACrC,WAAW,CAAC,IAAI,CAAC,CAAC;YAClB,QAAQ,CAAC,EAAE,CAAC,CAAC;QACf,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,KAAK,UAAU,aAAa;QAC1B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,YAAY,EAAE,CAAC;YACtC,YAAY,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,SAAS,CAAC,GAAG,EAAE;QACb,YAAY,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"App.js","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,cAAc,EAAgB,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAiB,MAAM,kCAAkC,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE1D,SAAS,GAAG;IACV,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAY,EAAE,CAAC,CAAC;IACxD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAa,EAAE,CAAC,CAAC;IAC3D,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,QAAQ,EAAU,CAAC;IACrE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAEvC,KAAK,UAAU,YAAY;QACzB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;YACrC,WAAW,CAAC,IAAI,CAAC,CAAC;YAClB,QAAQ,CAAC,EAAE,CAAC,CAAC;QACf,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,KAAK,UAAU,aAAa;QAC1B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,YAAY,EAAE,CAAC;YACtC,YAAY,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,MAAM,YAAY,EAAE,CAAC;YACrB,MAAM,aAAa,EAAE,CAAC;QACxB,CAAC,CAAC,EAAE,CAAC;IACP,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,CACL,eAAK,SAAS,EAAC,wCAAwC,aACrD,KAAC,cAAc,IACb,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,iBAAiB,EAC7B,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,GAAG,EAAE,GAAG,KAAK,YAAY,EAAE,CAAC,CAAC,CAAC,GACxC,EAEF,KAAC,SAAS,IAAC,SAAS,EAAE,iBAAiB,GAAI,EAE3C,iBAAO,SAAS,EAAC,iEAAiE,aAC/E,KAAK,IAAI,CACR,cAAK,SAAS,EAAC,4DAA4D,YACxE,KAAK,GACF,CACP,EACD,KAAC,gBAAgB,IAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,KAAK,aAAa,EAAE,CAAC,CAAC,CAAC,GAAI,EACrF,KAAC,WAAW,KAAG,IACT,IACJ,CACP,CAAC;AACJ,CAAC;AAED,eAAe,GAAG,CAAC"}
|