@castlemilk/omega 0.1.8 → 0.1.9

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.
Files changed (2) hide show
  1. package/dist/cli.js +102 -42
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -85,7 +85,7 @@ import React2 from "react";
85
85
 
86
86
  // ../../apps/cli/src/commands/tui.tsx
87
87
  import { useEffect, useMemo, useRef, useState } from "react";
88
- import { Box, Text, useApp, useInput } from "ink";
88
+ import { Box, Text, useApp, useInput, useWindowSize } from "ink";
89
89
  import { jsx, jsxs } from "react/jsx-runtime";
90
90
  var API = process.env.HARNESS_API_URL || "http://localhost:4000";
91
91
  function formatTime(d) {
@@ -123,7 +123,7 @@ function useTasks(pollMs = 1e3) {
123
123
  const addLog = (message, level = "info") => {
124
124
  setLogs((prev) => {
125
125
  const next = [...prev, { time: formatTime(/* @__PURE__ */ new Date()), message, level }];
126
- return next.slice(-50);
126
+ return next.slice(-200);
127
127
  });
128
128
  };
129
129
  useEffect(() => {
@@ -153,7 +153,7 @@ function useTasks(pollMs = 1e3) {
153
153
  }
154
154
  }
155
155
  for (const id of Object.keys(previousTasks.current)) {
156
- if (!current[id]) {
156
+ if (!current[id] && !id.startsWith("__")) {
157
157
  addLog(`Task removed: "${previousTasks.current[id].title}"`, "warning");
158
158
  }
159
159
  }
@@ -178,8 +178,12 @@ function useTasks(pollMs = 1e3) {
178
178
  }, [pollMs]);
179
179
  return { tasks, logs, connected };
180
180
  }
181
+ function truncate(str, max) {
182
+ if (str.length <= max) return str;
183
+ return str.slice(0, max - 1) + "\u2026";
184
+ }
181
185
  function Header({ connected }) {
182
- return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", paddingX: 1, children: [
186
+ return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", paddingX: 1, height: 3, flexShrink: 0, children: [
183
187
  /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "Omega Harness Console" }),
184
188
  /* @__PURE__ */ jsx(Box, { flexGrow: 1 }),
185
189
  /* @__PURE__ */ jsx(Text, { color: connected ? "green" : "red", children: connected ? "\u25CF connected" : "\u25CF disconnected" }),
@@ -189,39 +193,6 @@ function Header({ connected }) {
189
193
  ] })
190
194
  ] });
191
195
  }
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
196
  function Stats({ tasks }) {
226
197
  const counts = useMemo(() => {
227
198
  const total = tasks.length;
@@ -231,7 +202,7 @@ function Stats({ tasks }) {
231
202
  const failed = tasks.filter((t) => t.status === "failed").length;
232
203
  return { total, todo, inProgress, done, failed };
233
204
  }, [tasks]);
234
- return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", paddingX: 1, gap: 2, children: [
205
+ return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", paddingX: 1, height: 3, flexShrink: 0, gap: 2, children: [
235
206
  /* @__PURE__ */ jsxs(Text, { children: [
236
207
  "total: ",
237
208
  counts.total
@@ -254,20 +225,109 @@ function Stats({ tasks }) {
254
225
  ] })
255
226
  ] });
256
227
  }
228
+ function TaskList({
229
+ tasks,
230
+ height,
231
+ collapsed,
232
+ toggle,
233
+ columns
234
+ }) {
235
+ const sorted = useMemo(
236
+ () => [...tasks].sort(
237
+ (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
238
+ ),
239
+ [tasks]
240
+ );
241
+ const header = /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
242
+ /* @__PURE__ */ jsxs(Text, { bold: true, underline: true, children: [
243
+ "Tasks (",
244
+ tasks.length,
245
+ ")"
246
+ ] }),
247
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
248
+ "[t] ",
249
+ collapsed ? "expand" : "collapse"
250
+ ] })
251
+ ] });
252
+ if (collapsed) {
253
+ return /* @__PURE__ */ jsx(Box, { borderStyle: "single", paddingX: 1, height: 3, flexShrink: 0, children: header });
254
+ }
255
+ const availableLines = Math.max(0, height - 3);
256
+ const visible = sorted.slice(0, availableLines);
257
+ return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", paddingX: 1, flexDirection: "column", height, flexShrink: 0, children: [
258
+ header,
259
+ visible.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No tasks yet. Create one in the web UI." }),
260
+ visible.map((task) => {
261
+ const providerText = task.provider ? `${task.provider}/${task.model}` : "-";
262
+ const titleMax = Math.max(10, columns - 50);
263
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
264
+ /* @__PURE__ */ jsx(Text, { color: statusColor(task.status), children: statusSymbol(task.status) }),
265
+ /* @__PURE__ */ jsx(Box, { width: 20, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: formatTime(new Date(task.updatedAt)) }) }),
266
+ /* @__PURE__ */ jsx(Box, { width: 12, children: /* @__PURE__ */ jsx(Text, { color: statusColor(task.status), bold: true, children: task.status.toUpperCase() }) }),
267
+ /* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: truncate(providerText, 23) }) }),
268
+ /* @__PURE__ */ jsx(Box, { flexGrow: 1, children: /* @__PURE__ */ jsx(Text, { children: truncate(task.title, titleMax) }) })
269
+ ] }, task.id);
270
+ })
271
+ ] });
272
+ }
273
+ function ConsoleLog({
274
+ logs,
275
+ height,
276
+ columns
277
+ }) {
278
+ const availableLines = Math.max(0, height - 3);
279
+ const visible = logs.slice(-availableLines);
280
+ return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", paddingX: 1, flexDirection: "column", height, flexGrow: 1, children: [
281
+ /* @__PURE__ */ jsx(Text, { bold: true, underline: true, children: "Console" }),
282
+ visible.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Waiting for activity..." }),
283
+ visible.map((log, i) => {
284
+ const timeWidth = 10;
285
+ const msgMax = Math.max(10, columns - timeWidth - 4);
286
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
287
+ /* @__PURE__ */ jsx(Box, { width: timeWidth, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: log.time }) }),
288
+ /* @__PURE__ */ jsx(Box, { flexGrow: 1, children: /* @__PURE__ */ jsx(Text, { color: log.level, children: truncate(log.message, msgMax) }) })
289
+ ] }, i);
290
+ })
291
+ ] });
292
+ }
257
293
  function TuiApp() {
258
294
  const { exit } = useApp();
259
295
  const { tasks, logs, connected } = useTasks();
296
+ const { columns, rows } = useWindowSize();
297
+ const [tasksCollapsed, setTasksCollapsed] = useState(false);
298
+ const [sidebar, setSidebar] = useState(false);
260
299
  useInput((input, key) => {
261
300
  if (input === "q" || key.escape) {
262
301
  exit();
263
302
  }
303
+ if (input === "t") {
304
+ setTasksCollapsed((c) => !c);
305
+ }
306
+ if (input === "s") {
307
+ setSidebar((s) => !s);
308
+ }
264
309
  });
265
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", height: "100%", gap: 1, padding: 1, children: [
310
+ const mainHeight = Math.max(6, rows - 9);
311
+ const taskListWidth = sidebar ? Math.max(30, Math.floor(columns * 0.4)) : void 0;
312
+ const taskListHeight = sidebar ? mainHeight : tasksCollapsed ? 3 : Math.max(3, Math.floor(mainHeight * 0.45));
313
+ const consoleHeight = sidebar ? mainHeight : mainHeight - taskListHeight;
314
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", height: rows, width: columns, gap: 1, padding: 1, children: [
266
315
  /* @__PURE__ */ jsx(Header, { connected }),
267
316
  /* @__PURE__ */ jsx(Stats, { tasks }),
268
- /* @__PURE__ */ jsx(TaskList, { tasks }),
269
- /* @__PURE__ */ jsx(ConsoleLog, { logs }),
270
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Press q or Esc to quit" })
317
+ /* @__PURE__ */ jsxs(Box, { flexDirection: sidebar ? "row" : "column", gap: 1, flexGrow: 1, children: [
318
+ /* @__PURE__ */ jsx(
319
+ TaskList,
320
+ {
321
+ tasks,
322
+ height: taskListHeight,
323
+ collapsed: tasksCollapsed,
324
+ toggle: () => setTasksCollapsed((c) => !c),
325
+ columns: taskListWidth ?? columns
326
+ }
327
+ ),
328
+ /* @__PURE__ */ jsx(ConsoleLog, { logs, height: consoleHeight, columns })
329
+ ] }),
330
+ /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "q/esc quit \u2022 t toggle tasks \u2022 s toggle sidebar" }) })
271
331
  ] });
272
332
  }
273
333
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@castlemilk/omega",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Omega Harness CLI - installable via npx",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",