@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/server.js CHANGED
@@ -5,6 +5,9 @@ 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";
9
+ import * as grpc from "@grpc/grpc-js";
10
+ import * as protoLoader from "@grpc/proto-loader";
8
11
 
9
12
  // ../providers/src/openai.ts
10
13
  var DEFAULT_BASE_URL = "https://api.openai.com/v1";
@@ -41,7 +44,7 @@ var OpenAIProvider = class {
41
44
  })
42
45
  });
43
46
  if (!res.ok) {
44
- throw new Error(`OpenAI request failed: ${res.status} ${res.statusText}`);
47
+ throw new Error(`OpenAI request failed: ${res.status.toString()} ${res.statusText}`);
45
48
  }
46
49
  const data = await res.json();
47
50
  return data.choices?.[0]?.message?.content ?? "";
@@ -86,7 +89,7 @@ var AnthropicProvider = class {
86
89
  body: JSON.stringify(body)
87
90
  });
88
91
  if (!res.ok) {
89
- throw new Error(`Anthropic request failed: ${res.status} ${res.statusText}`);
92
+ throw new Error(`Anthropic request failed: ${res.status.toString()} ${res.statusText}`);
90
93
  }
91
94
  const data = await res.json();
92
95
  return data.content?.find((c) => c.type === "text")?.text ?? "";
@@ -130,7 +133,7 @@ var OllamaProvider = class {
130
133
  })
131
134
  });
132
135
  if (!res.ok) {
133
- throw new Error(`Ollama request failed: ${res.status} ${res.statusText}`);
136
+ throw new Error(`Ollama request failed: ${res.status.toString()} ${res.statusText}`);
134
137
  }
135
138
  const data = await res.json();
136
139
  return data.message?.content ?? "";
@@ -174,7 +177,7 @@ var GeminiProvider = class {
174
177
  body: JSON.stringify(body)
175
178
  });
176
179
  if (!res.ok) {
177
- throw new Error(`Gemini request failed: ${res.status} ${res.statusText}`);
180
+ throw new Error(`Gemini request failed: ${res.status.toString()} ${res.statusText}`);
178
181
  }
179
182
  const data = await res.json();
180
183
  return data.candidates?.[0]?.content?.parts?.map((p) => p.text).join("") ?? "";
@@ -186,7 +189,7 @@ var KimiProvider = class extends OpenAIProvider {
186
189
  constructor(config) {
187
190
  super({
188
191
  ...config,
189
- baseUrl: config.baseUrl || "https://api.moonshot.ai/v1"
192
+ baseUrl: config.baseUrl ?? "https://api.moonshot.ai/v1"
190
193
  });
191
194
  this.config = config;
192
195
  }
@@ -212,7 +215,7 @@ function createProvider(config) {
212
215
  case "generic":
213
216
  return new GenericProvider(config);
214
217
  default:
215
- throw new Error(`Unknown provider kind: ${config.kind}`);
218
+ throw new Error(`Unknown provider kind: ${String(config.kind)}`);
216
219
  }
217
220
  }
218
221
 
@@ -255,9 +258,11 @@ function selectProvider(configs, rules2, task) {
255
258
  return true;
256
259
  }).sort((a, b) => b.priority - a.priority);
257
260
  for (const rule of matchingRules) {
258
- const provider = rule.then.provider ? enabled.find((cfg) => cfg.id === rule.then.provider || cfg.name === rule.then.provider) : enabled[0];
261
+ const provider = rule.then.provider ? enabled.find(
262
+ (cfg) => cfg.id === rule.then.provider || cfg.name === rule.then.provider
263
+ ) : enabled[0];
259
264
  if (provider) {
260
- return { provider, model: rule.then.model || provider.defaultModel };
265
+ return { provider, model: rule.then.model ?? provider.defaultModel };
261
266
  }
262
267
  }
263
268
  let best;
@@ -275,6 +280,11 @@ function selectProvider(configs, rules2, task) {
275
280
  // src/server.ts
276
281
  var __filename = fileURLToPath(import.meta.url);
277
282
  var __dirname = path.dirname(__filename);
283
+ function asyncHandler(fn) {
284
+ return (req, res, next) => {
285
+ Promise.resolve(fn(req, res, next)).catch(next);
286
+ };
287
+ }
278
288
  var app = express();
279
289
  app.use(cors());
280
290
  app.use(express.json());
@@ -305,11 +315,44 @@ if (process.env.KIMI_API_KEY) {
305
315
  }
306
316
  var rules = [];
307
317
  function newId() {
308
- return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
318
+ return `${Date.now().toString()}-${Math.random().toString(36).slice(2)}`;
309
319
  }
310
320
  function now() {
311
321
  return /* @__PURE__ */ new Date();
312
322
  }
323
+ function parseCapabilities(value) {
324
+ if (Array.isArray(value)) return value;
325
+ if (typeof value === "string") return JSON.parse(value);
326
+ return [];
327
+ }
328
+ var projectCreateSchema = z.object({
329
+ name: z.string().min(1),
330
+ path: z.string().min(1),
331
+ repoUrl: z.string().optional(),
332
+ description: z.string().optional(),
333
+ env: z.record(z.string()).optional()
334
+ });
335
+ var taskCreateSchema = z.object({
336
+ projectId: z.string().min(1),
337
+ title: z.string().min(1),
338
+ description: z.string().optional(),
339
+ complexity: z.enum(["simple", "medium", "complex"]).default("simple"),
340
+ tags: z.array(z.string()).default([])
341
+ });
342
+ var providerCreateSchema = z.object({
343
+ name: z.string().min(1),
344
+ kind: z.enum(["openai", "anthropic", "ollama", "gemini", "kimi", "generic"]),
345
+ baseUrl: z.string().optional(),
346
+ apiKey: z.string().optional(),
347
+ defaultModel: z.string().min(1),
348
+ capabilities: z.union([z.string(), z.array(z.any())]).optional(),
349
+ enabled: z.boolean().optional()
350
+ });
351
+ var routerSelectSchema = z.object({
352
+ title: z.string().min(1),
353
+ complexity: z.enum(["simple", "medium", "complex"]).default("simple"),
354
+ tags: z.array(z.string()).default([])
355
+ });
313
356
  app.get("/projects", (_req, res) => {
314
357
  res.json(
315
358
  projects.map((p) => ({
@@ -319,14 +362,10 @@ app.get("/projects", (_req, res) => {
319
362
  );
320
363
  });
321
364
  app.post("/projects", (req, res) => {
322
- const body = req.body;
365
+ const body = projectCreateSchema.parse(req.body);
323
366
  const project = {
324
367
  id: newId(),
325
- name: body.name,
326
- path: body.path,
327
- repoUrl: body.repoUrl,
328
- description: body.description,
329
- env: body.env,
368
+ ...body,
330
369
  createdAt: now()
331
370
  };
332
371
  projects.push(project);
@@ -338,19 +377,15 @@ app.delete("/projects/:id", (req, res) => {
338
377
  res.status(204).send();
339
378
  });
340
379
  app.get("/tasks", (req, res) => {
341
- const projectId = req.query.projectId;
380
+ const projectId = typeof req.query.projectId === "string" ? req.query.projectId : void 0;
342
381
  res.json(tasks.filter((t) => projectId ? t.projectId === projectId : true));
343
382
  });
344
383
  app.post("/tasks", (req, res) => {
345
- const body = req.body;
384
+ const body = taskCreateSchema.parse(req.body);
346
385
  const task = {
347
386
  id: newId(),
348
- projectId: body.projectId,
349
- title: body.title,
350
- description: body.description,
387
+ ...body,
351
388
  status: "todo",
352
- complexity: body.complexity || "simple",
353
- tags: body.tags || [],
354
389
  createdAt: now(),
355
390
  updatedAt: now()
356
391
  };
@@ -363,9 +398,12 @@ app.patch("/tasks/:id", (req, res) => {
363
398
  Object.assign(task, req.body, { updatedAt: now() });
364
399
  res.json(task);
365
400
  });
366
- app.post("/tasks/:id/run", async (req, res) => {
401
+ app.post("/tasks/:id/run", asyncHandler(async (req, res) => {
367
402
  const task = tasks.find((t) => t.id === req.params.id);
368
- if (!task) return res.status(404).json({ error: "Task not found" });
403
+ if (!task) {
404
+ res.status(404).json({ error: "Task not found" });
405
+ return;
406
+ }
369
407
  task.status = "in_progress";
370
408
  task.error = void 0;
371
409
  task.result = void 0;
@@ -373,7 +411,8 @@ app.post("/tasks/:id/run", async (req, res) => {
373
411
  if (!selection) {
374
412
  task.status = "failed";
375
413
  task.error = "No provider available for this task";
376
- return res.json(task);
414
+ res.json(task);
415
+ return;
377
416
  }
378
417
  try {
379
418
  const provider = createProvider(selection.provider);
@@ -387,20 +426,16 @@ app.post("/tasks/:id/run", async (req, res) => {
387
426
  task.error = err instanceof Error ? err.message : String(err);
388
427
  }
389
428
  res.json(task);
390
- });
429
+ }));
391
430
  app.get("/providers", (_req, res) => {
392
431
  res.json(providerConfigs);
393
432
  });
394
433
  app.post("/providers", (req, res) => {
395
- const body = req.body;
434
+ const body = providerCreateSchema.parse(req.body);
396
435
  const cfg = {
397
436
  id: newId(),
398
- name: body.name,
399
- kind: body.kind,
400
- baseUrl: body.baseUrl,
401
- apiKey: body.apiKey,
402
- defaultModel: body.defaultModel,
403
- capabilities: Array.isArray(body.capabilities) ? body.capabilities : JSON.parse(body.capabilities || "[]"),
437
+ ...body,
438
+ capabilities: parseCapabilities(body.capabilities),
404
439
  enabled: body.enabled ?? true
405
440
  };
406
441
  providerConfigs.push(cfg);
@@ -417,14 +452,12 @@ app.delete("/providers/:id", (req, res) => {
417
452
  res.status(204).send();
418
453
  });
419
454
  app.post("/router/select", (req, res) => {
420
- const body = req.body;
455
+ const body = routerSelectSchema.parse(req.body);
421
456
  const task = {
422
457
  id: "preview",
423
458
  projectId: "preview",
424
- title: body.title,
459
+ ...body,
425
460
  status: "todo",
426
- complexity: body.complexity || "simple",
427
- tags: body.tags || [],
428
461
  createdAt: now(),
429
462
  updatedAt: now()
430
463
  };
@@ -437,7 +470,125 @@ app.use(express.static(webDir));
437
470
  app.get("*", (_req, res) => {
438
471
  res.sendFile(path.join(webDir, "index.html"));
439
472
  });
440
- var PORT = process.env.PORT || 4e3;
473
+ var PORT = Number(process.env.PORT ?? 4e3);
441
474
  app.listen(PORT, () => {
442
- console.log(`Omega harness server on http://localhost:${PORT}`);
475
+ console.log(`Omega harness server on http://localhost:${PORT.toString()}`);
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()}`);
443
594
  });
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":"AAOA,iBAAS,GAAG,gCAoDX;AAED,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":"AAOA,iBAAS,GAAG,gCAsDX;AAED,eAAe,GAAG,CAAC"}
@@ -30,10 +30,12 @@ function App() {
30
30
  }
31
31
  }
32
32
  useEffect(() => {
33
- loadProjects();
34
- loadProviders();
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
@@ -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;QACf,aAAa,EAAE,CAAC;IAClB,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,YAAY,GACtB,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,aAAa,GAAI,EACnE,KAAC,WAAW,KAAG,IACT,IACJ,CACP,CAAC;AACJ,CAAC;AAED,eAAe,GAAG,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"}