@hasna/computer 0.1.7 → 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 (42) hide show
  1. package/README.md +67 -0
  2. package/dist/apps/ghostty/applescript.d.ts +36 -0
  3. package/dist/apps/ghostty/applescript.d.ts.map +1 -0
  4. package/dist/apps/ghostty/applescript.test.d.ts +2 -0
  5. package/dist/apps/ghostty/applescript.test.d.ts.map +1 -0
  6. package/dist/apps/ghostty/driver.d.ts +10 -0
  7. package/dist/apps/ghostty/driver.d.ts.map +1 -0
  8. package/dist/apps/registry.d.ts +5 -0
  9. package/dist/apps/registry.d.ts.map +1 -0
  10. package/dist/apps/registry.test.d.ts +2 -0
  11. package/dist/apps/registry.test.d.ts.map +1 -0
  12. package/dist/apps/types.d.ts +47 -0
  13. package/dist/apps/types.d.ts.map +1 -0
  14. package/dist/cli/index.js +5893 -9970
  15. package/dist/cli/storage.d.ts +3 -0
  16. package/dist/cli/storage.d.ts.map +1 -0
  17. package/dist/cli/storage.test.d.ts +2 -0
  18. package/dist/cli/storage.test.d.ts.map +1 -0
  19. package/dist/db/index.d.ts +15 -2
  20. package/dist/db/index.d.ts.map +1 -1
  21. package/dist/db/pg-migrations.d.ts +2 -0
  22. package/dist/db/pg-migrations.d.ts.map +1 -0
  23. package/dist/db/remote-storage.d.ts +11 -0
  24. package/dist/db/remote-storage.d.ts.map +1 -0
  25. package/dist/db/storage-sync.d.ts +57 -0
  26. package/dist/db/storage-sync.d.ts.map +1 -0
  27. package/dist/db/storage-sync.test.d.ts +2 -0
  28. package/dist/db/storage-sync.test.d.ts.map +1 -0
  29. package/dist/index.d.ts +3 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +13473 -17215
  32. package/dist/mcp/index.js +17467 -21724
  33. package/dist/mcp/server.d.ts.map +1 -1
  34. package/dist/server/index.js +14852 -19139
  35. package/dist/storage.d.ts +5 -0
  36. package/dist/storage.d.ts.map +1 -0
  37. package/dist/storage.js +5519 -0
  38. package/package.json +9 -3
  39. package/src/db/migrations/001_initial.sql +2 -1
  40. package/dashboard/dist/assets/index-6UXnbsOB.js +0 -40
  41. package/dashboard/dist/assets/index-CwAxlYtY.css +0 -1
  42. package/dashboard/dist/index.html +0 -13
package/README.md CHANGED
@@ -47,12 +47,63 @@ bun install -g @hasna/computer
47
47
  computer run <task> # Run a computer use task
48
48
  computer run <task> -p openai # Use OpenAI instead of Anthropic
49
49
  computer run <task> -s 30 # Limit to 30 steps
50
+ computer open <app> [options] # Open an app deterministically via its driver
51
+ computer apps # List app drivers + availability
50
52
  computer screenshot # Capture current screen
51
53
  computer sessions # List past sessions
52
54
  computer session <id> # Show session details + action log
53
55
  computer stats # Usage statistics
54
56
  ```
55
57
 
58
+ ## Apps
59
+
60
+ Beyond AI-driven tasks (`computer run`), `computer` ships deterministic **app
61
+ drivers** that open and arrange desktop apps with zero AI in the loop — windows,
62
+ tabs, pane grids, and a command per pane.
63
+
64
+ ```bash
65
+ computer apps # List registered drivers and availability
66
+ computer open <app> [options] # Open/orchestrate an app via its driver
67
+ ```
68
+
69
+ Options for `computer open`:
70
+
71
+ | Option | Description |
72
+ |--------|-------------|
73
+ | `--grid RxC` | Split the window into R rows x C cols (panes fill row-major: left-to-right, top-to-bottom) |
74
+ | `--tabs "spec1,spec2,..."` | Multiple tabs in one window, each with its own grid (e.g. `"2x2,1x2,1x2"`) |
75
+ | `--run <cmd>` | Command for the next pane in order (repeatable); with `--tabs`, commands flow across tabs |
76
+ | `--all` | Run the single `--run` command in every pane |
77
+ | `--dir <path>` | Working directory — every pane `cd`s there first |
78
+ | `--max` | Maximize the new window (not native fullscreen) |
79
+
80
+ Examples:
81
+
82
+ ```bash
83
+ # 2x2 grid, run codewith in all four panes, maximized
84
+ computer open ghostty --grid 2x2 --run "codewith" --all --max
85
+
86
+ # 2x2 grid with a different command per pane
87
+ computer open ghostty --grid 2x2 --run "htop" --run "btop" --run "vim" --run "bun dev"
88
+
89
+ # Three tabs (2x2, then two 1x2), every pane cd'd into the project
90
+ computer open ghostty --tabs "2x2,1x2,1x2" --dir ~/Workspace/myproject
91
+ ```
92
+
93
+ **Drivers:**
94
+
95
+ - **ghostty** — Ghostty terminal (macOS, Ghostty 1.3+). Uses Ghostty's native
96
+ AppleScript dictionary for windows, tabs, and splits. Requires
97
+ `/Applications/Ghostty.app` (or `ghostty` on PATH).
98
+
99
+ Drivers are app-generic: each implements `available()` (with a reason when
100
+ unavailable, e.g. on Linux) and `open(spec)`. New drivers register in
101
+ `src/apps/registry.ts`.
102
+
103
+ The same surface is exposed over MCP via `computer_open_app`
104
+ (params: `app`, `grid`, `tabs`, `run[]`, `all`, `dir`, `max` — falls back to a
105
+ plain macOS app launch for apps without a driver) and `computer_list_apps`.
106
+
56
107
  ## MCP Server
57
108
 
58
109
  Add to your Claude Code config:
@@ -159,6 +210,22 @@ const result = await driver.execute({
159
210
 
160
211
  Sessions and action logs are stored in `~/.hasna/computer/computer.db` (SQLite).
161
212
 
213
+ ## Storage Sync
214
+
215
+ Optional Postgres sync is available through package-local commands:
216
+
217
+ ```bash
218
+ export HASNA_COMPUTER_DATABASE_URL=postgres://...
219
+ computer storage status
220
+ computer storage push
221
+ computer storage pull
222
+ computer storage sync
223
+ ```
224
+
225
+ The MCP server also exposes `storage_status`, `storage_push`, `storage_pull`, and `storage_sync`.
226
+
227
+ `COMPUTER_DATABASE_URL` is accepted as the non-Hasna fallback database URL.
228
+
162
229
  ## License
163
230
 
164
231
  Apache-2.0
@@ -0,0 +1,36 @@
1
+ import type { PaneGrid } from "../types.js";
2
+ /** Parse "RxC" (e.g. "2x2", "3x1") into a PaneGrid. Throws on invalid input. */
3
+ export declare function parseGrid(spec: string): PaneGrid;
4
+ /** Parse a comma-separated tabs spec like "2x2,1x2,1x2" into one grid per tab. */
5
+ export declare function parseTabsSpec(spec: string): PaneGrid[];
6
+ /** Escape a string for embedding inside an AppleScript double-quoted literal. */
7
+ export declare function escapeAppleScript(s: string): string;
8
+ /** Single-quote a string for POSIX shells (used for `cd <dir>`). */
9
+ export declare function shellQuote(s: string): string;
10
+ /**
11
+ * Map run commands onto panes (row-major across tabs in order).
12
+ * - `all` fills every pane with the single command (requires exactly one).
13
+ * - Otherwise commands are assigned in order; extra panes get undefined.
14
+ * Throws when there are more commands than panes.
15
+ */
16
+ export declare function assignPaneCommands(totalPanes: number, run: string[], all: boolean): (string | undefined)[];
17
+ export interface GhosttyScriptOptions {
18
+ /** One grid per tab; the first tab reuses the new window's selected tab. */
19
+ tabs: PaneGrid[];
20
+ /** Command per pane, row-major across tabs in order. undefined = empty shell. */
21
+ commands?: (string | undefined)[];
22
+ /** Working directory — every pane cds here before its command. */
23
+ dir?: string;
24
+ /** Maximize the new window (toggle_maximize, not native fullscreen). */
25
+ max?: boolean;
26
+ }
27
+ /**
28
+ * Build the full AppleScript for opening one Ghostty window with the given
29
+ * tabs/grids/commands. Pure — returns the script string.
30
+ *
31
+ * Grid algorithm (per tab): build column 0 downward (split direction down
32
+ * from the previous row), then split each row rightward C-1 times, then
33
+ * equalize once. Commands inject row-major with `input text` + enter key.
34
+ */
35
+ export declare function buildGhosttyScript(opts: GhosttyScriptOptions): string;
36
+ //# sourceMappingURL=applescript.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applescript.d.ts","sourceRoot":"","sources":["../../../src/apps/ghostty/applescript.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAShD;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAMtD;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,oEAAoE;AACpE,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EAAE,EACb,GAAG,EAAE,OAAO,GACX,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAWxB;AAED,MAAM,WAAW,oBAAoB;IACnC,4EAA4E;IAC5E,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;IAClC,kEAAkE;IAClE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,oBAAoB,GAAG,MAAM,CAiErE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=applescript.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applescript.test.d.ts","sourceRoot":"","sources":["../../../src/apps/ghostty/applescript.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import type { AppAvailability, AppDriver } from "../types.js";
2
+ export interface GhosttyEnvironment {
3
+ platform?: NodeJS.Platform | string;
4
+ hasAppBundle?: boolean;
5
+ hasBinary?: boolean;
6
+ }
7
+ /** Availability check, injectable for tests. */
8
+ export declare function ghosttyAvailability(env?: GhosttyEnvironment): AppAvailability;
9
+ export declare const ghosttyDriver: AppDriver;
10
+ //# sourceMappingURL=driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../../src/apps/ghostty/driver.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAwC,MAAM,aAAa,CAAC;AAKpG,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC;IACpC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,gDAAgD;AAChD,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,kBAAuB,GAAG,eAAe,CAWjF;AAmBD,eAAO,MAAM,aAAa,EAAE,SAiD3B,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { AppDriver } from "./types.js";
2
+ export declare function registerAppDriver(driver: AppDriver): void;
3
+ export declare function getAppDriver(name: string): AppDriver | undefined;
4
+ export declare function listAppDrivers(): AppDriver[];
5
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/apps/registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAK5C,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAEzD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAEhE;AAED,wBAAgB,cAAc,IAAI,SAAS,EAAE,CAE5C"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=registry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.test.d.ts","sourceRoot":"","sources":["../../src/apps/registry.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,47 @@
1
+ /** A pane grid: R rows x C cols. Panes are addressed row-major. */
2
+ export interface PaneGrid {
3
+ rows: number;
4
+ cols: number;
5
+ }
6
+ /** Declarative description of how to open/arrange an app. */
7
+ export interface AppOpenSpec {
8
+ /** Split the window into rows x cols panes (single tab). */
9
+ grid?: PaneGrid;
10
+ /** Multiple tabs in one window, each with its own grid. Overrides `grid`. */
11
+ tabs?: PaneGrid[];
12
+ /** Commands per pane, row-major across tabs in order. */
13
+ run?: string[];
14
+ /** Fill every pane with the single `run` command. */
15
+ all?: boolean;
16
+ /** Working directory — every pane cds here before running its command. */
17
+ dir?: string;
18
+ /** Maximize the new window (not native fullscreen). */
19
+ max?: boolean;
20
+ }
21
+ /** Whether a driver can run on this machine, and why not if it can't. */
22
+ export interface AppAvailability {
23
+ available: boolean;
24
+ /** Human-readable reason when unavailable. */
25
+ reason?: string;
26
+ }
27
+ /** Result of an open() call. */
28
+ export interface AppOpenResult {
29
+ ok: boolean;
30
+ message: string;
31
+ /** Total panes created (when applicable). */
32
+ panes?: number;
33
+ /** Tabs created (when applicable). */
34
+ tabs?: number;
35
+ }
36
+ /** A deterministic driver for one desktop application. */
37
+ export interface AppDriver {
38
+ /** Registry key, e.g. "ghostty". Lowercase. */
39
+ name: string;
40
+ /** One-line human description. */
41
+ description: string;
42
+ /** Check availability on this machine. */
43
+ available(): AppAvailability;
44
+ /** Open/orchestrate the app according to spec. */
45
+ open(spec: AppOpenSpec): Promise<AppOpenResult>;
46
+ }
47
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/apps/types.ts"],"names":[],"mappings":"AAOA,mEAAmE;AACnE,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,6DAA6D;AAC7D,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC;IAClB,yDAAyD;IACzD,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,qDAAqD;IACrD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,yEAAyE;AACzE,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,gCAAgC;AAChC,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,0DAA0D;AAC1D,MAAM,WAAW,SAAS;IACxB,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,SAAS,IAAI,eAAe,CAAC;IAC7B,kDAAkD;IAClD,IAAI,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CACjD"}