@castlemilk/omega 0.2.0 → 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 CHANGED
@@ -12,8 +12,8 @@ function getApiUrl() {
12
12
  const opts = program.opts();
13
13
  return opts.api ?? "http://localhost:4000";
14
14
  }
15
- async function apiFetch(path3, init) {
16
- const url = `${getApiUrl()}${path3}`;
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) {
@@ -48,8 +48,85 @@ projectCmd.command("remove").description("Remove a project").argument("<id>", "p
48
48
  });
49
49
 
50
50
  // ../../apps/cli/src/commands/task.ts
51
+ import { Command as Command3 } from "commander";
52
+
53
+ // ../../apps/cli/src/commands/task-feed.ts
51
54
  import { Command as Command2 } from "commander";
52
- var taskCmd = new Command2("task").description("Manage tasks");
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");
53
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) => {
54
131
  const task = await apiFetch("/tasks", {
55
132
  method: "POST",
@@ -73,13 +150,14 @@ taskCmd.command("run").description("Run a task through the router").argument("<i
73
150
  const result = await apiFetch(`/tasks/${id}/run`, { method: "POST" });
74
151
  console.log(JSON.stringify(result, null, 2));
75
152
  });
153
+ taskCmd.addCommand(taskFeedCmd);
76
154
 
77
155
  // ../../apps/cli/src/commands/ui.ts
78
- import { Command as Command3 } from "commander";
156
+ import { Command as Command4 } from "commander";
79
157
  import { spawn } from "child_process";
80
- import { existsSync } from "fs";
81
- import path from "path";
82
- import { fileURLToPath } from "url";
158
+ import { existsSync as existsSync2 } from "fs";
159
+ import path2 from "path";
160
+ import { fileURLToPath as fileURLToPath2 } from "url";
83
161
  import open from "open";
84
162
  import { render } from "ink";
85
163
  import React2 from "react";
@@ -338,12 +416,12 @@ function TuiApp() {
338
416
  }
339
417
 
340
418
  // ../../apps/cli/src/commands/ui.ts
341
- var __filename = fileURLToPath(import.meta.url);
342
- var __dirname = path.dirname(__filename);
419
+ var __filename2 = fileURLToPath2(import.meta.url);
420
+ var __dirname2 = path2.dirname(__filename2);
343
421
  var API2 = process.env.HARNESS_API_URL ?? "http://localhost:4000";
344
422
  function startBundledServer() {
345
- const serverPath = path.resolve(__dirname, "server.js");
346
- if (existsSync(serverPath)) {
423
+ const serverPath = path2.resolve(__dirname2, "server.js");
424
+ if (existsSync2(serverPath)) {
347
425
  return spawn(process.execPath, [serverPath], { stdio: "inherit" });
348
426
  }
349
427
  return void 0;
@@ -366,7 +444,7 @@ async function waitForApi(maxMs = 15e3) {
366
444
  }
367
445
  async function ensureProject() {
368
446
  const cwd = process.cwd();
369
- const name = path.basename(cwd);
447
+ const name = path2.basename(cwd);
370
448
  try {
371
449
  const listRes = await fetch(`${API2}/projects`);
372
450
  const projects = await listRes.json();
@@ -389,7 +467,7 @@ async function ensureProject() {
389
467
  console.warn("Could not auto-detect project:", err);
390
468
  }
391
469
  }
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) => {
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) => {
393
471
  let server = void 0;
394
472
  const alreadyRunning = await isApiReady();
395
473
  const useTui = options.tui;
@@ -427,10 +505,10 @@ var uiCmd = new Command3("ui").description("Open the harness web UI").option("--
427
505
  });
428
506
 
429
507
  // ../../apps/cli/src/commands/console.ts
430
- import { Command as Command4 } from "commander";
508
+ import { Command as Command5 } from "commander";
431
509
  import { render as render2 } from "ink";
432
510
  import React3 from "react";
433
- var consoleCmd = new Command4("console").description("Open the harness TUI console").action(() => {
511
+ var consoleCmd = new Command5("console").description("Open the harness TUI console").action(() => {
434
512
  if (!process.stdin.isTTY) {
435
513
  console.error("The harness console requires an interactive terminal (TTY).");
436
514
  process.exit(1);
@@ -439,9 +517,9 @@ var consoleCmd = new Command4("console").description("Open the harness TUI conso
439
517
  });
440
518
 
441
519
  // ../../apps/cli/src/commands/skill.ts
442
- import { Command as Command5 } from "commander";
443
- import path2 from "path";
444
- import { fileURLToPath as fileURLToPath2 } from "url";
520
+ import { Command as Command6 } from "commander";
521
+ import path3 from "path";
522
+ import { fileURLToPath as fileURLToPath3 } from "url";
445
523
 
446
524
  // ../skills/src/parse.ts
447
525
  import matter from "gray-matter";
@@ -542,22 +620,22 @@ async function registerSkill(sourcePath, outputDir) {
542
620
  // ../../apps/cli/src/commands/skill.ts
543
621
  function getOutputDir() {
544
622
  if (process.env.OMEGA_HARNESS_ROOT) {
545
- return path2.join(path2.resolve(process.env.OMEGA_HARNESS_ROOT), "packages/skills/src");
623
+ return path3.join(path3.resolve(process.env.OMEGA_HARNESS_ROOT), "packages/skills/src");
546
624
  }
547
625
  try {
548
- const __filename2 = fileURLToPath2(import.meta.url);
549
- const __dirname2 = path2.dirname(__filename2);
550
- return path2.join(path2.resolve(__dirname2, "../../../.."), "packages/skills/src");
626
+ const __filename3 = fileURLToPath3(import.meta.url);
627
+ const __dirname3 = path3.dirname(__filename3);
628
+ return path3.join(path3.resolve(__dirname3, "../../../.."), "packages/skills/src");
551
629
  } catch {
552
- return path2.join(process.cwd(), "harness-skills");
630
+ return path3.join(process.cwd(), "harness-skills");
553
631
  }
554
632
  }
555
- var skillCmd = new Command5("skill").description("Manage skill artifacts");
633
+ var skillCmd = new Command6("skill").description("Manage skill artifacts");
556
634
  skillCmd.command("generate").description("Generate a harness adapter from a SKILL.md file").argument("<source>", "path to SKILL.md").action(async (source) => {
557
635
  const outputDir = getOutputDir();
558
- const manifest = await registerSkill(path2.resolve(source), outputDir);
636
+ const manifest = await registerSkill(path3.resolve(source), outputDir);
559
637
  console.log(`Generated adapter for skill: ${manifest.name}`);
560
- console.log(`Output directory: ${path2.join(outputDir, "generated")}`);
638
+ console.log(`Output directory: ${path3.join(outputDir, "generated")}`);
561
639
  });
562
640
 
563
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
+ }
package/dist/server.js CHANGED
@@ -6,6 +6,8 @@ import cors from "cors";
6
6
  import path from "path";
7
7
  import { fileURLToPath } from "url";
8
8
  import { z } from "zod";
9
+ import * as grpc from "@grpc/grpc-js";
10
+ import * as protoLoader from "@grpc/proto-loader";
9
11
 
10
12
  // ../providers/src/openai.ts
11
13
  var DEFAULT_BASE_URL = "https://api.openai.com/v1";
@@ -472,3 +474,121 @@ var PORT = Number(process.env.PORT ?? 4e3);
472
474
  app.listen(PORT, () => {
473
475
  console.log(`Omega harness server on http://localhost:${PORT.toString()}`);
474
476
  });
477
+ var GRPC_PORT = Number(process.env.GRPC_PORT ?? 50051);
478
+ var GRPC_PROTO_PATH = path.join(__dirname, "proto/tasks.proto");
479
+ var grpcPackageDef = protoLoader.loadSync(GRPC_PROTO_PATH, {
480
+ keepCase: true,
481
+ longs: String,
482
+ enums: String,
483
+ defaults: true,
484
+ oneofs: true
485
+ });
486
+ var grpcProto = grpc.loadPackageDefinition(grpcPackageDef);
487
+ function parseGrpcRequest(req) {
488
+ const r = req;
489
+ return {
490
+ project_id: typeof r.project_id === "string" ? r.project_id : "",
491
+ title: typeof r.title === "string" ? r.title : "",
492
+ description: typeof r.description === "string" ? r.description : void 0,
493
+ complexity: typeof r.complexity === "string" ? r.complexity : void 0,
494
+ tags: Array.isArray(r.tags) ? r.tags.filter((t) => typeof t === "string") : void 0,
495
+ auto_run: typeof r.auto_run === "boolean" ? r.auto_run : void 0
496
+ };
497
+ }
498
+ function isValidComplexity(value) {
499
+ return ["simple", "medium", "complex"].includes(value);
500
+ }
501
+ async function executeTask(task) {
502
+ task.status = "in_progress";
503
+ task.error = void 0;
504
+ task.result = void 0;
505
+ const selection = selectProvider(providerConfigs, rules, task);
506
+ if (!selection) {
507
+ task.status = "failed";
508
+ task.error = "No provider available for this task";
509
+ return;
510
+ }
511
+ try {
512
+ const provider = createProvider(selection.provider);
513
+ const prompt = [task.title, task.description].filter(Boolean).join("\n\n");
514
+ const result = await provider.send(prompt, { model: selection.model });
515
+ task.status = "done";
516
+ task.result = result;
517
+ task.assignedModel = { provider: selection.provider.name, model: selection.model };
518
+ } catch (err) {
519
+ task.status = "failed";
520
+ task.error = err instanceof Error ? err.message : String(err);
521
+ }
522
+ }
523
+ var grpcServer = new grpc.Server();
524
+ grpcServer.addService(grpcProto.omega.TaskIngestion.service, {
525
+ submitTask: (call, callback) => {
526
+ void (async () => {
527
+ try {
528
+ const req = parseGrpcRequest(call.request);
529
+ if (!req.project_id || !req.title) {
530
+ callback(
531
+ { code: grpc.status.INVALID_ARGUMENT, message: "project_id and title are required" },
532
+ null
533
+ );
534
+ return;
535
+ }
536
+ const complexity = isValidComplexity(req.complexity ?? "") ? req.complexity : "simple";
537
+ const task = {
538
+ id: newId(),
539
+ projectId: req.project_id,
540
+ title: req.title,
541
+ description: req.description ?? void 0,
542
+ status: "todo",
543
+ complexity,
544
+ tags: req.tags ?? [],
545
+ createdAt: now(),
546
+ updatedAt: now()
547
+ };
548
+ tasks.push(task);
549
+ if (req.auto_run) {
550
+ await executeTask(task);
551
+ }
552
+ callback(null, { id: task.id, status: task.status, error: task.error ?? "" });
553
+ } catch (err) {
554
+ const message = err instanceof Error ? err.message : String(err);
555
+ callback({ code: grpc.status.INTERNAL, message }, null);
556
+ }
557
+ })();
558
+ },
559
+ streamTasks: (call) => {
560
+ const req = call.request;
561
+ const projectId = req.project_id;
562
+ let cancelled = false;
563
+ const interval = setInterval(() => {
564
+ if (cancelled) return;
565
+ const filtered = projectId ? tasks.filter((t) => t.projectId === projectId) : tasks;
566
+ for (const task of filtered.slice(-20)) {
567
+ call.write({
568
+ id: task.id,
569
+ title: task.title,
570
+ status: task.status,
571
+ provider: task.assignedModel?.provider ?? "",
572
+ model: task.assignedModel?.model ?? "",
573
+ result: task.result ?? "",
574
+ error: task.error ?? ""
575
+ });
576
+ }
577
+ }, 1e3);
578
+ call.on("cancelled", () => {
579
+ cancelled = true;
580
+ clearInterval(interval);
581
+ });
582
+ call.on("error", () => {
583
+ cancelled = true;
584
+ clearInterval(interval);
585
+ });
586
+ }
587
+ });
588
+ grpcServer.bindAsync(`0.0.0.0:${GRPC_PORT.toString()}`, grpc.ServerCredentials.createInsecure(), (err) => {
589
+ if (err) {
590
+ console.error("gRPC server failed to start:", err);
591
+ return;
592
+ }
593
+ console.log(`gRPC task ingestion on 0.0.0.0:${GRPC_PORT.toString()}`);
594
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@castlemilk/omega",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Omega Harness CLI - installable via npx",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",
@@ -12,6 +12,8 @@
12
12
  "dist"
13
13
  ],
14
14
  "dependencies": {
15
+ "@grpc/grpc-js": "^1.14.4",
16
+ "@grpc/proto-loader": "^0.8.1",
15
17
  "commander": "^12.1.0",
16
18
  "cors": "^2.8.5",
17
19
  "express": "^4.19.2",
@@ -40,9 +42,10 @@
40
42
  "url": "git+https://github.com/castlemilk/harness.git"
41
43
  },
42
44
  "scripts": {
43
- "build:cli": "esbuild ../../apps/cli/src/index.ts --bundle --platform=node --format=esm --outfile=dist/cli.js --external:commander --external:open --external:gray-matter --external:yaml --external:ink --external:react --alias:@omega/core=../../packages/core/src/index.ts --alias:@omega/skills=../../packages/skills/src/index.ts",
44
- "build:server": "esbuild src/server.ts --bundle --platform=node --format=esm --outfile=dist/server.js --external:express --external:cors --external:zod --alias:@omega/core=../../packages/core/src/index.ts --alias:@omega/providers=../../packages/providers/src/index.ts --alias:@omega/router=../../packages/router/src/index.ts",
45
+ "build:cli": "esbuild ../../apps/cli/src/index.ts --bundle --platform=node --format=esm --outfile=dist/cli.js --external:commander --external:open --external:gray-matter --external:yaml --external:ink --external:react --external:@grpc/grpc-js --external:@grpc/proto-loader --alias:@omega/core=../../packages/core/src/index.ts --alias:@omega/skills=../../packages/skills/src/index.ts",
46
+ "build:server": "esbuild src/server.ts --bundle --platform=node --format=esm --outfile=dist/server.js --external:express --external:cors --external:zod --external:@grpc/grpc-js --external:@grpc/proto-loader --alias:@omega/core=../../packages/core/src/index.ts --alias:@omega/providers=../../packages/providers/src/index.ts --alias:@omega/router=../../packages/router/src/index.ts",
45
47
  "copy:web": "cp -R ../../apps/web/dist dist/web",
46
- "build": "pnpm build:cli && pnpm build:server && pnpm copy:web"
48
+ "copy:proto": "mkdir -p dist/proto && cp ../../proto/tasks.proto dist/proto/tasks.proto",
49
+ "build": "pnpm build:cli && pnpm build:server && pnpm copy:web && pnpm copy:proto"
47
50
  }
48
51
  }