@opencode-cockpit/shell 0.1.4 → 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.
Files changed (77) hide show
  1. package/README.md +100 -10
  2. package/dist/agent/plugin.js +180 -0
  3. package/dist/agent/tools/index.js +23 -0
  4. package/{src/tools/keys.ts → dist/agent/tools/keys.js} +11 -14
  5. package/dist/agent/tools/list.js +68 -0
  6. package/dist/agent/tools/read.js +59 -0
  7. package/dist/agent/tools/restart.js +44 -0
  8. package/dist/agent/tools/send.js +71 -0
  9. package/dist/agent/tools/shared.js +94 -0
  10. package/dist/agent/tools/start.js +140 -0
  11. package/dist/agent/tools/stop.js +42 -0
  12. package/dist/agent/tools/wait.js +79 -0
  13. package/dist/agent/tools/watch-args.js +12 -0
  14. package/dist/agent/tools/watch.js +74 -0
  15. package/dist/connect.js +31 -0
  16. package/dist/core/config.js +97 -0
  17. package/dist/core/find.js +69 -0
  18. package/dist/core/format.js +63 -0
  19. package/dist/core/kind.js +31 -0
  20. package/dist/server.js +2 -0
  21. package/dist/tui/components/badge.js +29 -0
  22. package/dist/tui/components/console.js +759 -0
  23. package/dist/tui/components/dock.js +270 -0
  24. package/dist/tui/components/sidebar.js +136 -0
  25. package/dist/tui/dialogs.js +163 -0
  26. package/dist/tui/index.js +205 -0
  27. package/dist/tui/lib/details.js +23 -0
  28. package/dist/tui/lib/keys.js +43 -0
  29. package/dist/tui/lib/search.js +39 -0
  30. package/dist/tui/lib/update.js +58 -0
  31. package/dist/tui/lib/view.js +191 -0
  32. package/dist/tui/state/store.js +158 -0
  33. package/package.json +21 -12
  34. package/types/agent/plugin.d.ts +12 -0
  35. package/types/agent/tools/index.d.ts +5 -0
  36. package/types/agent/tools/keys.d.ts +3 -0
  37. package/types/agent/tools/list.d.ts +3 -0
  38. package/types/agent/tools/read.d.ts +3 -0
  39. package/types/agent/tools/restart.d.ts +3 -0
  40. package/types/agent/tools/send.d.ts +3 -0
  41. package/types/agent/tools/shared.d.ts +40 -0
  42. package/types/agent/tools/start.d.ts +3 -0
  43. package/types/agent/tools/stop.d.ts +3 -0
  44. package/types/agent/tools/wait.d.ts +3 -0
  45. package/types/agent/tools/watch-args.d.ts +10 -0
  46. package/types/agent/tools/watch.d.ts +3 -0
  47. package/types/connect.d.ts +9 -0
  48. package/types/core/config.d.ts +59 -0
  49. package/types/core/find.d.ts +37 -0
  50. package/types/core/format.d.ts +8 -0
  51. package/types/core/kind.d.ts +9 -0
  52. package/types/server.d.ts +2 -0
  53. package/types/tui/components/badge.d.ts +9 -0
  54. package/types/tui/components/console.d.ts +22 -0
  55. package/types/tui/components/dock.d.ts +14 -0
  56. package/types/tui/components/sidebar.d.ts +14 -0
  57. package/types/tui/dialogs.d.ts +10 -0
  58. package/types/tui/index.d.ts +13 -0
  59. package/types/tui/lib/details.d.ts +3 -0
  60. package/types/tui/lib/keys.d.ts +8 -0
  61. package/types/tui/lib/search.d.ts +7 -0
  62. package/types/tui/lib/update.d.ts +25 -0
  63. package/types/tui/lib/view.d.ts +80 -0
  64. package/types/tui/state/store.d.ts +55 -0
  65. package/src/connect.ts +0 -24
  66. package/src/server.ts +0 -156
  67. package/src/tools/find.ts +0 -80
  68. package/src/tools/format.ts +0 -78
  69. package/src/tools/index.ts +0 -467
  70. package/src/tui/badge.tsx +0 -17
  71. package/src/tui/console.tsx +0 -438
  72. package/src/tui/dock.tsx +0 -130
  73. package/src/tui/index.tsx +0 -286
  74. package/src/tui/keys.ts +0 -52
  75. package/src/tui/sidebar.tsx +0 -44
  76. package/src/tui/store.ts +0 -185
  77. package/src/tui/view.ts +0 -161
@@ -0,0 +1,63 @@
1
+ const MAX_LINE = 2000;
2
+
3
+ /** Compact log lines for a model: numbered, long lines cut, consecutive repeats collapsed. */
4
+ export function formatLines(lines) {
5
+ const out = [];
6
+ let i = 0;
7
+ while (i < lines.length) {
8
+ const line = lines[i];
9
+ let j = i + 1;
10
+ while (j < lines.length && lines[j].text === line.text) j++;
11
+ const repeats = j - i;
12
+ const text = line.text.length > MAX_LINE ? `${line.text.slice(0, MAX_LINE)}… [${line.text.length - MAX_LINE} chars cut]` : line.text;
13
+ out.push(repeats > 1 ? `${line.n}| ${text} (×${repeats}, lines ${line.n}-${line.n + repeats - 1})` : `${line.n}| ${text}`);
14
+ i = j;
15
+ }
16
+ return out.join("\n");
17
+ }
18
+ export function describeStatus(info) {
19
+ switch (info.status) {
20
+ case "running":
21
+ return `running (pid ${info.pid}, up ${duration(Date.now() - info.startedAt)})`;
22
+ case "exited":
23
+ return `exited with code ${info.exitCode ?? "?"} after ${duration((info.endedAt ?? Date.now()) - info.startedAt)}`;
24
+ case "killed":
25
+ return `killed${info.signal ? ` by ${info.signal}` : ""} after ${duration((info.endedAt ?? Date.now()) - info.startedAt)}`;
26
+ case "failed":
27
+ return `failed to start: ${info.error ?? "unknown error"}`;
28
+ }
29
+ }
30
+ export function header(info) {
31
+ const run = info.run > 1 ? ` run=${info.run}` : "";
32
+ return `<shell id="${info.id}" title="${info.title.replaceAll('"', "'")}" status="${info.status}"${run}>`;
33
+ }
34
+ export function formatRead(info, page, empty = "(no output yet)") {
35
+ const parts = [header(info), `status: ${describeStatus(info)}`];
36
+ if (page.truncated) parts.push(`(older lines were dropped from the buffer; oldest kept is ${page.firstLine})`);
37
+ parts.push(page.lines.length > 0 ? formatLines(page.lines) : empty);
38
+ if (page.hasMore) parts.push(`(more lines available: call shell_read with after=${page.nextCursor})`);
39
+ parts.push("</shell>", `cursor: ${page.nextCursor}`);
40
+ return parts.join("\n");
41
+ }
42
+ export function formatWait(result, timeoutSeconds) {
43
+ switch (result.reason) {
44
+ case "pattern":
45
+ return `condition met: pattern matched on line ${result.match?.n}: ${result.match?.text}`;
46
+ case "port":
47
+ return "condition met: port is accepting connections";
48
+ case "idle":
49
+ return "condition met: no output for the idle window (the program may be waiting for input)";
50
+ case "exit":
51
+ return `process ended: ${describeStatus(result.info)}`;
52
+ case "timeout":
53
+ return `timed out after ${timeoutSeconds}s without the condition being met; the shell is still ${result.info.status}`;
54
+ }
55
+ }
56
+ export function duration(ms) {
57
+ const s = Math.max(0, Math.round(ms / 1000));
58
+ if (s < 60) return `${s}s`;
59
+ const m = Math.floor(s / 60);
60
+ if (m < 60) return `${m}m${s % 60 ? `${s % 60}s` : ""}`;
61
+ const h = Math.floor(m / 60);
62
+ return `${h}h${m % 60 ? `${m % 60}m` : ""}`;
63
+ }
@@ -0,0 +1,31 @@
1
+ import { commandOf } from "./find.js";
2
+
3
+ /** What a shell is, derived from its command — never a label anyone has to maintain by hand. */
4
+
5
+ const TESTS = /\b(test|tests|vitest|jest|mocha|pytest|rspec|phpunit|playwright|cypress|karma)\b/i;
6
+ const SERVER = /\b(dev|serve|server|start|preview|nodemon|uvicorn|gunicorn|rails s|php -S|docker[\s-]compose\s+up|ngrok|tunnel)\b/i;
7
+ const WATCHER = /(--watch|\bwatch\b|watchexec|fswatch|chokidar|\btsc\s+-w\b|--hot|\bentr\b)/i;
8
+ const BUILD = /\b(build|assemble|compile|bundle|tsc|webpack|rollup|esbuild|tsup|make|gradlew?|mvn|cargo build)\b/i;
9
+
10
+ /**
11
+ * Order matters: a test runner in watch mode is still about tests, and `vite build` is a build even
12
+ * though `vite` usually serves.
13
+ */
14
+ export function classify(command, custom) {
15
+ // Config patterns win: only the user knows their own toolchain.
16
+ for (const [name, pattern] of Object.entries(custom ?? {})) {
17
+ try {
18
+ if (new RegExp(pattern, "i").test(command)) return name;
19
+ } catch {
20
+ // a bad pattern in config is ignored, like any other unusable setting
21
+ }
22
+ }
23
+ if (TESTS.test(command)) return "tests";
24
+ if (BUILD.test(command) && !SERVER.test(command)) return "build";
25
+ if (SERVER.test(command)) return "server";
26
+ if (WATCHER.test(command)) return "watcher";
27
+ return "task";
28
+ }
29
+ export function kindOfShell(s, custom) {
30
+ return classify(commandOf(s), custom);
31
+ }
package/dist/server.js ADDED
@@ -0,0 +1,2 @@
1
+ /** Published entry point: `@opencode-cockpit/shell/server`. */
2
+ export { createShellServer, default, SHELL_PACKAGE } from "./agent/plugin.js";
@@ -0,0 +1,29 @@
1
+ import { effect as _$effect } from "@opentui/solid";
2
+ import { insertNode as _$insertNode } from "@opentui/solid";
3
+ import { insert as _$insert } from "@opentui/solid";
4
+ import { setProp as _$setProp } from "@opentui/solid";
5
+ import { createElement as _$createElement } from "@opentui/solid";
6
+ /** @jsxImportSource @opentui/solid */
7
+
8
+ import { badgeText, kindColor, kindOf } from "../lib/view.js";
9
+
10
+ /** Status pill: label on a coloured background, readable in any font and colour scheme. */
11
+ export function Badge(props) {
12
+ const theme = () => props.api.theme.current;
13
+ const kind = () => kindOf(props.shell);
14
+ return (() => {
15
+ var _el$ = _$createElement("text"),
16
+ _el$2 = _$createElement("span"),
17
+ _el$3 = _$createElement("b");
18
+ _$insertNode(_el$, _el$2);
19
+ _$setProp(_el$, "flexShrink", 0);
20
+ _$setProp(_el$, "wrapMode", "none");
21
+ _$insertNode(_el$2, _el$3);
22
+ _$insert(_el$3, () => badgeText(kind(), props.frame));
23
+ _$effect(_$p => _$setProp(_el$2, "style", {
24
+ fg: theme().background,
25
+ bg: kindColor(theme(), kind())
26
+ }, _$p));
27
+ return _el$;
28
+ })();
29
+ }