@arach/lattices 0.1.0 → 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.
- package/README.md +28 -28
- package/app/Sources/ActionRow.swift +61 -0
- package/app/Sources/App.swift +1 -40
- package/app/Sources/AppDelegate.swift +154 -24
- package/app/Sources/CheatSheetHUD.swift +1 -0
- package/app/Sources/CommandModeState.swift +40 -19
- package/app/Sources/CommandModeView.swift +27 -2
- package/app/Sources/DaemonServer.swift +8 -0
- package/app/Sources/DiagnosticLog.swift +19 -1
- package/app/Sources/EventBus.swift +1 -0
- package/app/Sources/HotkeyManager.swift +1 -0
- package/app/Sources/HotkeyStore.swift +9 -1
- package/app/Sources/LatticesApi.swift +210 -0
- package/app/Sources/MainView.swift +46 -86
- package/app/Sources/MainWindow.swift +13 -0
- package/app/Sources/OcrModel.swift +309 -0
- package/app/Sources/OcrStore.swift +295 -0
- package/app/Sources/OmniSearchState.swift +283 -0
- package/app/Sources/OmniSearchView.swift +288 -0
- package/app/Sources/OmniSearchWindow.swift +105 -0
- package/app/Sources/PaletteCommand.swift +11 -1
- package/app/Sources/PermissionChecker.swift +12 -2
- package/app/Sources/Preferences.swift +44 -0
- package/app/Sources/ScreenMapState.swift +7 -17
- package/app/Sources/ScreenMapView.swift +3 -0
- package/app/Sources/SettingsView.swift +534 -122
- package/app/Sources/Theme.swift +39 -0
- package/app/Sources/WindowTiler.swift +59 -56
- package/bin/lattices-app.js +23 -7
- package/bin/lattices.js +123 -0
- package/docs/api.md +390 -249
- package/docs/app.md +75 -28
- package/docs/concepts.md +45 -136
- package/docs/config.md +8 -7
- package/docs/layers.md +16 -18
- package/docs/ocr.md +185 -0
- package/docs/overview.md +39 -34
- package/docs/quickstart.md +34 -35
- package/package.json +6 -2
package/docs/api.md
CHANGED
|
@@ -4,23 +4,8 @@ description: WebSocket API reference for programmatic control of lattices
|
|
|
4
4
|
order: 5
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# Daemon API
|
|
8
|
-
|
|
9
7
|
The lattices menu bar app runs a WebSocket daemon on `ws://127.0.0.1:9399`.
|
|
10
|
-
|
|
11
|
-
can do, agents and scripts can do too.
|
|
12
|
-
|
|
13
|
-
## Who this is for
|
|
14
|
-
|
|
15
|
-
- **AI coding agents** that need to discover projects, launch sessions,
|
|
16
|
-
tile windows, and switch contexts without human interaction
|
|
17
|
-
- **Scripts and automation** — CI, dotfile bootstraps, workspace setup
|
|
18
|
-
- **Custom tools** — build your own launcher, dashboard, or orchestrator
|
|
19
|
-
|
|
20
|
-
> New to lattices? Start with the [Overview](/docs/overview) and
|
|
21
|
-
> [Quickstart](/docs/quickstart). For the `.lattices.json` config format
|
|
22
|
-
> and CLI commands, see [Configuration](/docs/config). For architecture
|
|
23
|
-
> details, see [Concepts](/docs/concepts).
|
|
8
|
+
30 RPC methods and 5 real-time events.
|
|
24
9
|
|
|
25
10
|
## Quick start
|
|
26
11
|
|
|
@@ -39,7 +24,7 @@ lattices daemon status
|
|
|
39
24
|
3. Call a method from Node.js:
|
|
40
25
|
|
|
41
26
|
```js
|
|
42
|
-
import { daemonCall } from 'lattices/daemon-client'
|
|
27
|
+
import { daemonCall } from '@arach/lattices/daemon-client'
|
|
43
28
|
|
|
44
29
|
const windows = await daemonCall('windows.list')
|
|
45
30
|
console.log(windows) // [{ wid, app, title, frame, ... }, ...]
|
|
@@ -88,28 +73,25 @@ lattices uses a JSON-RPC-style protocol over WebSocket on port **9399**.
|
|
|
88
73
|
| `result` | any \| null | Method return value (null on error) |
|
|
89
74
|
| `error` | string \| null | Error message (null on success) |
|
|
90
75
|
|
|
91
|
-
### Event (server-pushed)
|
|
92
|
-
|
|
93
|
-
```json
|
|
94
|
-
{
|
|
95
|
-
"event": "windows.changed",
|
|
96
|
-
"data": { ... }
|
|
97
|
-
}
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Events have no `id` — they are broadcast to all connected clients
|
|
101
|
-
whenever state changes.
|
|
102
|
-
|
|
103
76
|
### Errors
|
|
104
77
|
|
|
105
|
-
Three error types:
|
|
106
|
-
|
|
107
78
|
| Error | Meaning |
|
|
108
79
|
|-----------------|--------------------------------------|
|
|
109
80
|
| Unknown method | The `method` string is not recognized |
|
|
110
81
|
| Missing parameter | A required param was not provided |
|
|
111
82
|
| Not found | The referenced resource doesn't exist |
|
|
112
83
|
|
|
84
|
+
### Connection lifecycle
|
|
85
|
+
|
|
86
|
+
- The daemon starts when the menu bar app launches and stops when it quits.
|
|
87
|
+
- Connections are plain WebSocket. No handshake, no auth, no heartbeat.
|
|
88
|
+
- The Node.js `daemonCall()` client opens a fresh connection per call and
|
|
89
|
+
closes it when the response arrives. For event subscriptions, hold the
|
|
90
|
+
connection open (see [Reactive event pattern](#agent-integration)).
|
|
91
|
+
- If the daemon restarts (e.g. after `lattices app restart`), existing
|
|
92
|
+
connections are dropped. Clients should reconnect and treat the daemon
|
|
93
|
+
as stateless. There is no session resumption.
|
|
94
|
+
|
|
113
95
|
## Node.js client
|
|
114
96
|
|
|
115
97
|
lattices ships a zero-dependency WebSocket client that works with
|
|
@@ -121,7 +103,7 @@ matching internally.
|
|
|
121
103
|
Send an RPC call and await the response.
|
|
122
104
|
|
|
123
105
|
```js
|
|
124
|
-
import { daemonCall } from 'lattices/daemon-client'
|
|
106
|
+
import { daemonCall } from '@arach/lattices/daemon-client'
|
|
125
107
|
|
|
126
108
|
// Read-only
|
|
127
109
|
const status = await daemonCall('daemon.status')
|
|
@@ -132,7 +114,7 @@ const win = await daemonCall('windows.get', { wid: 1234 })
|
|
|
132
114
|
await daemonCall('session.launch', { path: '/Users/you/dev/myapp' })
|
|
133
115
|
await daemonCall('window.tile', { session: 'myapp-a1b2c3', position: 'left' })
|
|
134
116
|
|
|
135
|
-
// Custom timeout (default:
|
|
117
|
+
// Custom timeout (default: 3000ms)
|
|
136
118
|
await daemonCall('projects.scan', null, 10000)
|
|
137
119
|
```
|
|
138
120
|
|
|
@@ -144,7 +126,7 @@ await daemonCall('projects.scan', null, 10000)
|
|
|
144
126
|
Check if the daemon is reachable.
|
|
145
127
|
|
|
146
128
|
```js
|
|
147
|
-
import { isDaemonRunning } from 'lattices/daemon-client'
|
|
129
|
+
import { isDaemonRunning } from '@arach/lattices/daemon-client'
|
|
148
130
|
|
|
149
131
|
if (await isDaemonRunning()) {
|
|
150
132
|
console.log('daemon is up')
|
|
@@ -153,11 +135,36 @@ if (await isDaemonRunning()) {
|
|
|
153
135
|
|
|
154
136
|
Returns `true` if `daemon.status` responds within 1 second.
|
|
155
137
|
|
|
138
|
+
### Error handling
|
|
139
|
+
|
|
140
|
+
`daemonCall` throws on errors — always wrap calls in try/catch:
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
import { daemonCall } from '@arach/lattices/daemon-client'
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
await daemonCall('session.launch', { path: '/nonexistent' })
|
|
147
|
+
} catch (err) {
|
|
148
|
+
// err.message is one of:
|
|
149
|
+
// "Not found" — resource doesn't exist
|
|
150
|
+
// "Missing parameter: ..." — required param missing
|
|
151
|
+
// "Unknown method: ..." — bad method name
|
|
152
|
+
// "Daemon request timed out" — no response within timeout
|
|
153
|
+
// ECONNREFUSED — daemon not running
|
|
154
|
+
console.error('Daemon error:', err.message)
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
156
158
|
---
|
|
157
159
|
|
|
158
|
-
##
|
|
160
|
+
## System
|
|
161
|
+
|
|
162
|
+
| Method | Type | Description |
|
|
163
|
+
|--------|------|-------------|
|
|
164
|
+
| `daemon.status` | read | Health check and stats |
|
|
165
|
+
| `api.schema` | read | Full API schema for self-discovery |
|
|
159
166
|
|
|
160
|
-
|
|
167
|
+
#### `daemon.status`
|
|
161
168
|
|
|
162
169
|
Health check and basic stats.
|
|
163
170
|
|
|
@@ -175,9 +182,28 @@ Health check and basic stats.
|
|
|
175
182
|
}
|
|
176
183
|
```
|
|
177
184
|
|
|
185
|
+
#### `api.schema`
|
|
186
|
+
|
|
187
|
+
Return the full API schema including version, models, and method definitions.
|
|
188
|
+
Useful for agent self-discovery.
|
|
189
|
+
|
|
190
|
+
**Params**: none
|
|
191
|
+
|
|
178
192
|
---
|
|
179
193
|
|
|
180
|
-
|
|
194
|
+
## Windows & Spaces
|
|
195
|
+
|
|
196
|
+
| Method | Type | Description |
|
|
197
|
+
|--------|------|-------------|
|
|
198
|
+
| `windows.list` | read | All visible windows |
|
|
199
|
+
| `windows.get` | read | Single window by ID |
|
|
200
|
+
| `spaces.list` | read | macOS display spaces |
|
|
201
|
+
| `window.tile` | write | Tile a window to a position |
|
|
202
|
+
| `window.focus` | write | Focus a window / switch Spaces |
|
|
203
|
+
| `window.move` | write | Move a window to another Space |
|
|
204
|
+
| `layout.distribute` | write | Distribute windows evenly |
|
|
205
|
+
|
|
206
|
+
#### `windows.list`
|
|
181
207
|
|
|
182
208
|
List all visible windows tracked by the desktop model.
|
|
183
209
|
|
|
@@ -203,9 +229,7 @@ List all visible windows tracked by the desktop model.
|
|
|
203
229
|
The `latticesSession` field is present only on windows that belong to
|
|
204
230
|
a lattices session (matched via the `[lattices:name]` title tag).
|
|
205
231
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
### `windows.get`
|
|
232
|
+
#### `windows.get`
|
|
209
233
|
|
|
210
234
|
Get a single window by its CGWindowID.
|
|
211
235
|
|
|
@@ -216,12 +240,92 @@ Get a single window by its CGWindowID.
|
|
|
216
240
|
| `wid` | number | yes | CGWindowID |
|
|
217
241
|
|
|
218
242
|
**Returns**: a single window object (same shape as `windows.list` items).
|
|
219
|
-
|
|
220
243
|
**Errors**: `Not found` if the window ID doesn't exist.
|
|
221
244
|
|
|
245
|
+
#### `spaces.list`
|
|
246
|
+
|
|
247
|
+
List macOS display spaces (virtual desktops).
|
|
248
|
+
|
|
249
|
+
**Params**: none
|
|
250
|
+
|
|
251
|
+
**Returns**: array of display objects:
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
[
|
|
255
|
+
{
|
|
256
|
+
"displayIndex": 0,
|
|
257
|
+
"displayId": "main",
|
|
258
|
+
"currentSpaceId": 1,
|
|
259
|
+
"spaces": [
|
|
260
|
+
{ "id": 1, "index": 0, "display": 0, "isCurrent": true },
|
|
261
|
+
{ "id": 2, "index": 1, "display": 0, "isCurrent": false }
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
]
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
#### `window.tile`
|
|
268
|
+
|
|
269
|
+
Tile a session's terminal window to a screen position.
|
|
270
|
+
|
|
271
|
+
**Params**:
|
|
272
|
+
|
|
273
|
+
| Field | Type | Required | Description |
|
|
274
|
+
|------------|--------|----------|-------------------------------------|
|
|
275
|
+
| `session` | string | yes | Session name |
|
|
276
|
+
| `position` | string | yes | Tile position (see below) |
|
|
277
|
+
|
|
278
|
+
**Positions**: `left`, `right`, `top`, `bottom`, `top-left`, `top-right`,
|
|
279
|
+
`bottom-left`, `bottom-right`, `left-third`, `center-third`, `right-third`,
|
|
280
|
+
`maximize`, `center`
|
|
281
|
+
|
|
282
|
+
#### `window.focus`
|
|
283
|
+
|
|
284
|
+
Focus a window — bring it to front and switch Spaces if needed.
|
|
285
|
+
|
|
286
|
+
**Params** (one of):
|
|
287
|
+
|
|
288
|
+
| Field | Type | Required | Description |
|
|
289
|
+
|-----------|--------|----------|---------------------------------|
|
|
290
|
+
| `wid` | number | no | CGWindowID (any window) |
|
|
291
|
+
| `session` | string | no | Session name (lattices windows) |
|
|
292
|
+
|
|
293
|
+
Provide either `wid` or `session`. If `wid` is given, it takes priority.
|
|
294
|
+
|
|
295
|
+
#### `window.move`
|
|
296
|
+
|
|
297
|
+
Move a session's window to a different macOS Space.
|
|
298
|
+
|
|
299
|
+
**Params**:
|
|
300
|
+
|
|
301
|
+
| Field | Type | Required | Description |
|
|
302
|
+
|-----------|--------|----------|----------------------------|
|
|
303
|
+
| `session` | string | yes | Session name |
|
|
304
|
+
| `spaceId` | number | yes | Target Space ID (from `spaces.list`) |
|
|
305
|
+
|
|
306
|
+
#### `layout.distribute`
|
|
307
|
+
|
|
308
|
+
Distribute all visible lattices windows evenly across the screen.
|
|
309
|
+
|
|
310
|
+
**Params**: none
|
|
311
|
+
|
|
222
312
|
---
|
|
223
313
|
|
|
224
|
-
|
|
314
|
+
## Sessions
|
|
315
|
+
|
|
316
|
+
| Method | Type | Description |
|
|
317
|
+
|--------|------|-------------|
|
|
318
|
+
| `tmux.sessions` | read | Lattices tmux sessions |
|
|
319
|
+
| `tmux.inventory` | read | All sessions including orphans |
|
|
320
|
+
| `session.launch` | write | Launch a project session |
|
|
321
|
+
| `session.kill` | write | Kill a session |
|
|
322
|
+
| `session.detach` | write | Detach clients from a session |
|
|
323
|
+
| `session.sync` | write | Reconcile session to config |
|
|
324
|
+
| `session.restart` | write | Restart a pane's process |
|
|
325
|
+
|
|
326
|
+
All session methods require tmux to be installed.
|
|
327
|
+
|
|
328
|
+
#### `tmux.sessions`
|
|
225
329
|
|
|
226
330
|
List tmux sessions that belong to lattices.
|
|
227
331
|
|
|
@@ -250,9 +354,7 @@ List tmux sessions that belong to lattices.
|
|
|
250
354
|
]
|
|
251
355
|
```
|
|
252
356
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
### `tmux.inventory`
|
|
357
|
+
#### `tmux.inventory`
|
|
256
358
|
|
|
257
359
|
List all tmux sessions including orphans (sessions not tracked by lattices).
|
|
258
360
|
|
|
@@ -269,9 +371,80 @@ List all tmux sessions including orphans (sessions not tracked by lattices).
|
|
|
269
371
|
|
|
270
372
|
Both arrays contain session objects (same shape as `tmux.sessions`).
|
|
271
373
|
|
|
374
|
+
#### `session.launch`
|
|
375
|
+
|
|
376
|
+
Launch a new tmux session for a project. If a session already exists,
|
|
377
|
+
it will be reattached. The project must be in the scanned project list —
|
|
378
|
+
call `projects.list` to check, or `projects.scan` to refresh.
|
|
379
|
+
|
|
380
|
+
**Params**:
|
|
381
|
+
|
|
382
|
+
| Field | Type | Required | Description |
|
|
383
|
+
|--------|--------|----------|----------------------------------|
|
|
384
|
+
| `path` | string | yes | Absolute path to project directory |
|
|
385
|
+
|
|
386
|
+
**Returns**: `{ "ok": true }`
|
|
387
|
+
**Errors**: `Not found` if the path isn't in the scanned project list.
|
|
388
|
+
|
|
389
|
+
#### `session.kill`
|
|
390
|
+
|
|
391
|
+
Kill a tmux session by name.
|
|
392
|
+
|
|
393
|
+
**Params**:
|
|
394
|
+
|
|
395
|
+
| Field | Type | Required | Description |
|
|
396
|
+
|--------|--------|----------|---------------------|
|
|
397
|
+
| `name` | string | yes | Session name |
|
|
398
|
+
|
|
399
|
+
#### `session.detach`
|
|
400
|
+
|
|
401
|
+
Detach all clients from a session (keeps it running).
|
|
402
|
+
|
|
403
|
+
**Params**:
|
|
404
|
+
|
|
405
|
+
| Field | Type | Required | Description |
|
|
406
|
+
|--------|--------|----------|---------------------|
|
|
407
|
+
| `name` | string | yes | Session name |
|
|
408
|
+
|
|
409
|
+
#### `session.sync`
|
|
410
|
+
|
|
411
|
+
Reconcile a running session to match its declared `.lattices.json` config.
|
|
412
|
+
Recreates missing panes, re-applies layout, restores labels, re-runs
|
|
413
|
+
commands in idle panes.
|
|
414
|
+
|
|
415
|
+
**Params**:
|
|
416
|
+
|
|
417
|
+
| Field | Type | Required | Description |
|
|
418
|
+
|--------|--------|----------|----------------------------------|
|
|
419
|
+
| `path` | string | yes | Absolute path to project directory |
|
|
420
|
+
|
|
421
|
+
**Errors**: `Not found` if the path isn't in the project list.
|
|
422
|
+
|
|
423
|
+
#### `session.restart`
|
|
424
|
+
|
|
425
|
+
Restart a specific pane's process within a session.
|
|
426
|
+
|
|
427
|
+
**Params**:
|
|
428
|
+
|
|
429
|
+
| Field | Type | Required | Description |
|
|
430
|
+
|--------|--------|----------|----------------------------------|
|
|
431
|
+
| `path` | string | yes | Absolute path to project directory |
|
|
432
|
+
| `pane` | string | no | Pane name to restart (defaults to first pane) |
|
|
433
|
+
|
|
272
434
|
---
|
|
273
435
|
|
|
274
|
-
|
|
436
|
+
## Projects & Layers
|
|
437
|
+
|
|
438
|
+
| Method | Type | Description |
|
|
439
|
+
|--------|------|-------------|
|
|
440
|
+
| `projects.list` | read | Discovered projects |
|
|
441
|
+
| `projects.scan` | write | Re-scan project directory |
|
|
442
|
+
| `layers.list` | read | Workspace layers and active index |
|
|
443
|
+
| `layer.switch` | write | Switch workspace layer |
|
|
444
|
+
| `group.launch` | write | Launch a tab group |
|
|
445
|
+
| `group.kill` | write | Kill a tab group |
|
|
446
|
+
|
|
447
|
+
#### `projects.list`
|
|
275
448
|
|
|
276
449
|
List all discovered projects.
|
|
277
450
|
|
|
@@ -297,33 +470,14 @@ List all discovered projects.
|
|
|
297
470
|
|
|
298
471
|
`devCommand` and `packageManager` are present only when detected.
|
|
299
472
|
|
|
300
|
-
|
|
473
|
+
#### `projects.scan`
|
|
301
474
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
List macOS display spaces (virtual desktops).
|
|
475
|
+
Trigger a re-scan of the project directory. Useful after cloning a new
|
|
476
|
+
repo or adding a `.lattices.json` config.
|
|
305
477
|
|
|
306
478
|
**Params**: none
|
|
307
479
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
```json
|
|
311
|
-
[
|
|
312
|
-
{
|
|
313
|
-
"displayIndex": 0,
|
|
314
|
-
"displayId": "main",
|
|
315
|
-
"currentSpaceId": 1,
|
|
316
|
-
"spaces": [
|
|
317
|
-
{ "id": 1, "index": 0, "display": 0, "isCurrent": true },
|
|
318
|
-
{ "id": 2, "index": 1, "display": 0, "isCurrent": false }
|
|
319
|
-
]
|
|
320
|
-
}
|
|
321
|
-
]
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
---
|
|
325
|
-
|
|
326
|
-
### `layers.list`
|
|
480
|
+
#### `layers.list`
|
|
327
481
|
|
|
328
482
|
List configured workspace layers and the active index.
|
|
329
483
|
|
|
@@ -343,203 +497,213 @@ List configured workspace layers and the active index.
|
|
|
343
497
|
|
|
344
498
|
Returns empty `layers` array if no workspace config is loaded.
|
|
345
499
|
|
|
346
|
-
|
|
500
|
+
#### `layer.switch`
|
|
347
501
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
Launch a new tmux session for a project.
|
|
502
|
+
Switch the active workspace layer. Focuses and tiles all windows in the
|
|
503
|
+
target layer, launches any projects that aren't running yet, and posts
|
|
504
|
+
a `layer.switched` event.
|
|
353
505
|
|
|
354
506
|
**Params**:
|
|
355
507
|
|
|
356
|
-
| Field
|
|
357
|
-
|
|
358
|
-
| `
|
|
359
|
-
|
|
360
|
-
**Returns**: `{ "ok": true }`
|
|
361
|
-
|
|
362
|
-
**Errors**: `Not found` if the path isn't in the scanned project list.
|
|
363
|
-
Run `projects.scan` first if needed.
|
|
364
|
-
|
|
365
|
-
**Notes**: If a session already exists for this project, it will be
|
|
366
|
-
reattached. The project must be in the scanned project list — call
|
|
367
|
-
`projects.list` to check, or `projects.scan` to refresh.
|
|
368
|
-
|
|
369
|
-
---
|
|
508
|
+
| Field | Type | Required | Description |
|
|
509
|
+
|---------|--------|----------|--------------------------------|
|
|
510
|
+
| `index` | number | yes | Layer index (0-based) |
|
|
370
511
|
|
|
371
|
-
|
|
512
|
+
#### `group.launch`
|
|
372
513
|
|
|
373
|
-
|
|
514
|
+
Launch a tab group session.
|
|
374
515
|
|
|
375
516
|
**Params**:
|
|
376
517
|
|
|
377
|
-
| Field
|
|
378
|
-
|
|
379
|
-
| `
|
|
380
|
-
|
|
381
|
-
**Returns**: `{ "ok": true }`
|
|
518
|
+
| Field | Type | Required | Description |
|
|
519
|
+
|-------|--------|----------|------------------|
|
|
520
|
+
| `id` | string | yes | Group ID |
|
|
382
521
|
|
|
383
|
-
|
|
522
|
+
**Errors**: `Not found` if the group ID doesn't match any configured group.
|
|
384
523
|
|
|
385
|
-
|
|
524
|
+
#### `group.kill`
|
|
386
525
|
|
|
387
|
-
|
|
526
|
+
Kill a tab group session.
|
|
388
527
|
|
|
389
528
|
**Params**:
|
|
390
529
|
|
|
391
|
-
| Field
|
|
392
|
-
|
|
393
|
-
| `
|
|
394
|
-
|
|
395
|
-
**Returns**: `{ "ok": true }`
|
|
530
|
+
| Field | Type | Required | Description |
|
|
531
|
+
|-------|--------|----------|------------------|
|
|
532
|
+
| `id` | string | yes | Group ID |
|
|
396
533
|
|
|
397
534
|
---
|
|
398
535
|
|
|
399
|
-
|
|
536
|
+
## Processes & Terminals
|
|
400
537
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
538
|
+
| Method | Type | Description |
|
|
539
|
+
|--------|------|-------------|
|
|
540
|
+
| `processes.list` | read | Running developer processes |
|
|
541
|
+
| `processes.tree` | read | Process tree from a PID |
|
|
542
|
+
| `terminals.list` | read | Terminal instances with processes |
|
|
543
|
+
| `terminals.search` | read | Search terminals by criteria |
|
|
404
544
|
|
|
405
|
-
|
|
545
|
+
#### `processes.list`
|
|
406
546
|
|
|
407
|
-
|
|
408
|
-
|--------|--------|----------|----------------------------------|
|
|
409
|
-
| `path` | string | yes | Absolute path to project directory |
|
|
410
|
-
|
|
411
|
-
**Returns**: `{ "ok": true }`
|
|
412
|
-
|
|
413
|
-
**Errors**: `Not found` if the path isn't in the project list.
|
|
414
|
-
|
|
415
|
-
---
|
|
416
|
-
|
|
417
|
-
### `session.restart`
|
|
418
|
-
|
|
419
|
-
Restart a specific pane's process within a session.
|
|
547
|
+
List running processes relevant to development (editors, servers, build tools).
|
|
420
548
|
|
|
421
549
|
**Params**:
|
|
422
550
|
|
|
423
|
-
| Field
|
|
424
|
-
|
|
425
|
-
| `
|
|
426
|
-
| `pane` | string | no | Pane name to restart (defaults to first pane) |
|
|
551
|
+
| Field | Type | Required | Description |
|
|
552
|
+
|-----------|--------|----------|------------------------------------|
|
|
553
|
+
| `command` | string | no | Filter by command name substring |
|
|
427
554
|
|
|
428
|
-
**Returns**:
|
|
429
|
-
|
|
430
|
-
**Errors**: `Not found` if the path isn't in the project list.
|
|
555
|
+
**Returns**: array of process objects:
|
|
431
556
|
|
|
432
|
-
|
|
557
|
+
```json
|
|
558
|
+
[
|
|
559
|
+
{
|
|
560
|
+
"pid": 1234,
|
|
561
|
+
"ppid": 567,
|
|
562
|
+
"command": "node",
|
|
563
|
+
"args": "server.js",
|
|
564
|
+
"cwd": "/Users/you/dev/myapp",
|
|
565
|
+
"tty": "/dev/ttys003",
|
|
566
|
+
"tmuxSession": "myapp-a1b2c3",
|
|
567
|
+
"tmuxPaneId": "%0"
|
|
568
|
+
}
|
|
569
|
+
]
|
|
570
|
+
```
|
|
433
571
|
|
|
434
|
-
|
|
572
|
+
#### `processes.tree`
|
|
435
573
|
|
|
436
|
-
|
|
574
|
+
Get the process tree rooted at a given PID.
|
|
437
575
|
|
|
438
576
|
**Params**:
|
|
439
577
|
|
|
440
|
-
| Field
|
|
441
|
-
|
|
442
|
-
| `
|
|
443
|
-
| `position` | string | yes | Tile position (see below) |
|
|
444
|
-
|
|
445
|
-
**Positions**: `left`, `right`, `top`, `bottom`, `top-left`, `top-right`,
|
|
446
|
-
`bottom-left`, `bottom-right`, `maximize`, `center`
|
|
447
|
-
|
|
448
|
-
**Returns**: `{ "ok": true }`
|
|
449
|
-
|
|
450
|
-
---
|
|
578
|
+
| Field | Type | Required | Description |
|
|
579
|
+
|-------|--------|----------|---------------|
|
|
580
|
+
| `pid` | number | yes | Root PID |
|
|
451
581
|
|
|
452
|
-
|
|
582
|
+
**Returns**: array of process objects (same shape as `processes.list`).
|
|
453
583
|
|
|
454
|
-
|
|
584
|
+
#### `terminals.list`
|
|
455
585
|
|
|
456
|
-
|
|
586
|
+
List all discovered terminal instances with their processes, tabs, and tmux associations.
|
|
457
587
|
|
|
458
|
-
|
|
459
|
-
|-----------|--------|----------|---------------------------------|
|
|
460
|
-
| `wid` | number | no | CGWindowID (any window) |
|
|
461
|
-
| `session` | string | no | Session name (lattices windows) |
|
|
588
|
+
**Params**:
|
|
462
589
|
|
|
463
|
-
|
|
590
|
+
| Field | Type | Required | Description |
|
|
591
|
+
|-----------|---------|----------|--------------------------------------|
|
|
592
|
+
| `refresh` | boolean | no | Force-refresh the terminal tab cache |
|
|
464
593
|
|
|
465
|
-
**Returns**:
|
|
594
|
+
**Returns**: array of terminal instance objects:
|
|
466
595
|
|
|
467
|
-
|
|
596
|
+
```json
|
|
597
|
+
[
|
|
598
|
+
{
|
|
599
|
+
"tty": "/dev/ttys003",
|
|
600
|
+
"app": "Terminal",
|
|
601
|
+
"windowIndex": 0,
|
|
602
|
+
"tabIndex": 0,
|
|
603
|
+
"isActiveTab": true,
|
|
604
|
+
"tabTitle": "myapp",
|
|
605
|
+
"processes": [ ... ],
|
|
606
|
+
"shellPid": 1234,
|
|
607
|
+
"cwd": "/Users/you/dev/myapp",
|
|
608
|
+
"tmuxSession": "myapp-a1b2c3",
|
|
609
|
+
"tmuxPaneId": "%0",
|
|
610
|
+
"hasClaude": true,
|
|
611
|
+
"displayName": "Terminal — myapp"
|
|
612
|
+
}
|
|
613
|
+
]
|
|
614
|
+
```
|
|
468
615
|
|
|
469
|
-
|
|
616
|
+
#### `terminals.search`
|
|
470
617
|
|
|
471
|
-
|
|
618
|
+
Search terminal instances by various criteria.
|
|
472
619
|
|
|
473
620
|
**Params**:
|
|
474
621
|
|
|
475
|
-
| Field
|
|
476
|
-
|
|
477
|
-
| `
|
|
478
|
-
| `
|
|
622
|
+
| Field | Type | Required | Description |
|
|
623
|
+
|------------|---------|----------|--------------------------------------|
|
|
624
|
+
| `command` | string | no | Filter by command name substring |
|
|
625
|
+
| `cwd` | string | no | Filter by working directory substring |
|
|
626
|
+
| `app` | string | no | Filter by terminal app name |
|
|
627
|
+
| `session` | string | no | Filter by tmux session name |
|
|
628
|
+
| `hasClaude`| boolean | no | Filter to only Claude-running TTYs |
|
|
479
629
|
|
|
480
|
-
**Returns**:
|
|
630
|
+
**Returns**: filtered array of terminal instance objects (same shape as `terminals.list`).
|
|
481
631
|
|
|
482
632
|
---
|
|
483
633
|
|
|
484
|
-
|
|
634
|
+
## OCR
|
|
485
635
|
|
|
486
|
-
|
|
636
|
+
| Method | Type | Description |
|
|
637
|
+
|--------|------|-------------|
|
|
638
|
+
| `ocr.snapshot` | read | Current OCR results for all visible windows |
|
|
639
|
+
| `ocr.search` | read | Full-text search across OCR history |
|
|
640
|
+
| `ocr.history` | read | OCR timeline for a specific window |
|
|
641
|
+
| `ocr.scan` | write | Trigger an immediate OCR scan |
|
|
487
642
|
|
|
488
|
-
|
|
643
|
+
See [Screen OCR](/docs/ocr) for configuration, scan schedules, and storage details.
|
|
489
644
|
|
|
490
|
-
|
|
491
|
-
|---------|--------|----------|--------------------------------|
|
|
492
|
-
| `index` | number | yes | Layer index (0-based) |
|
|
645
|
+
#### `ocr.snapshot`
|
|
493
646
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
**Notes**: This focuses and tiles all windows in the target layer,
|
|
497
|
-
launches any projects that aren't running yet, and posts a
|
|
498
|
-
`layer.switched` event.
|
|
647
|
+
Get the current in-memory OCR results for all visible windows.
|
|
499
648
|
|
|
500
|
-
|
|
649
|
+
**Params**: none
|
|
501
650
|
|
|
502
|
-
|
|
651
|
+
**Returns**: array of OCR result objects:
|
|
503
652
|
|
|
504
|
-
|
|
653
|
+
```json
|
|
654
|
+
[
|
|
655
|
+
{
|
|
656
|
+
"wid": 1234,
|
|
657
|
+
"app": "Terminal",
|
|
658
|
+
"title": "zsh",
|
|
659
|
+
"frame": { "x": 0, "y": 25, "w": 960, "h": 1050 },
|
|
660
|
+
"fullText": "~/dev/myapp $ npm run dev\nready on port 3000",
|
|
661
|
+
"blocks": [
|
|
662
|
+
{
|
|
663
|
+
"text": "~/dev/myapp $ npm run dev",
|
|
664
|
+
"confidence": 0.95,
|
|
665
|
+
"x": 0.02, "y": 0.05, "w": 0.6, "h": 0.04
|
|
666
|
+
}
|
|
667
|
+
],
|
|
668
|
+
"timestamp": 1709568000.0
|
|
669
|
+
}
|
|
670
|
+
]
|
|
671
|
+
```
|
|
505
672
|
|
|
506
|
-
|
|
673
|
+
#### `ocr.search`
|
|
507
674
|
|
|
508
|
-
|
|
509
|
-
|-------|--------|----------|------------------|
|
|
510
|
-
| `id` | string | yes | Group ID |
|
|
675
|
+
Full-text search across OCR history using SQLite FTS5.
|
|
511
676
|
|
|
512
|
-
**
|
|
677
|
+
**Params**:
|
|
513
678
|
|
|
514
|
-
|
|
679
|
+
| Field | Type | Required | Description |
|
|
680
|
+
|---------|---------|----------|------------------------------------------|
|
|
681
|
+
| `query` | string | yes | FTS5 search query |
|
|
682
|
+
| `app` | string | no | Filter by application name |
|
|
683
|
+
| `limit` | number | no | Max results (default 50) |
|
|
684
|
+
| `live` | boolean | no | Search live snapshot instead of history (default false) |
|
|
515
685
|
|
|
516
|
-
|
|
686
|
+
**FTS5 query examples**: `error`, `"build failed"`, `error OR warning`, `npm AND dev`, `react*`
|
|
517
687
|
|
|
518
|
-
|
|
688
|
+
#### `ocr.history`
|
|
519
689
|
|
|
520
|
-
|
|
690
|
+
Get the OCR timeline for a specific window, ordered by most recent first.
|
|
521
691
|
|
|
522
692
|
**Params**:
|
|
523
693
|
|
|
524
|
-
| Field
|
|
525
|
-
|
|
526
|
-
| `
|
|
527
|
-
|
|
528
|
-
**Returns**: `{ "ok": true }`
|
|
529
|
-
|
|
530
|
-
**Errors**: `Not found` if the group ID doesn't match any configured group.
|
|
531
|
-
|
|
532
|
-
---
|
|
694
|
+
| Field | Type | Required | Description |
|
|
695
|
+
|---------|--------|----------|----------------------------|
|
|
696
|
+
| `wid` | number | yes | CGWindowID |
|
|
697
|
+
| `limit` | number | no | Max results (default 50) |
|
|
533
698
|
|
|
534
|
-
|
|
699
|
+
#### `ocr.scan`
|
|
535
700
|
|
|
536
|
-
Trigger
|
|
537
|
-
|
|
701
|
+
Trigger an immediate OCR scan of all visible windows, bypassing the
|
|
702
|
+
periodic timer. Results available via `ocr.snapshot` once complete;
|
|
703
|
+
an `ocr.scanComplete` event is broadcast to all clients.
|
|
538
704
|
|
|
539
705
|
**Params**: none
|
|
540
706
|
|
|
541
|
-
**Returns**: `{ "ok": true }`
|
|
542
|
-
|
|
543
707
|
---
|
|
544
708
|
|
|
545
709
|
## Events
|
|
@@ -547,69 +711,47 @@ repo or adding a `.lattices.json` config.
|
|
|
547
711
|
Events are pushed to all connected WebSocket clients when state changes.
|
|
548
712
|
They have no `id` field — listen for messages with an `event` field.
|
|
549
713
|
|
|
550
|
-
|
|
714
|
+
| Event | Trigger |
|
|
715
|
+
|-------|---------|
|
|
716
|
+
| `windows.changed` | Desktop window list changes |
|
|
717
|
+
| `tmux.changed` | Sessions created, killed, or modified |
|
|
718
|
+
| `layer.switched` | Active workspace layer changes |
|
|
719
|
+
| `ocr.scanComplete` | OCR scan cycle finishes |
|
|
720
|
+
| `processes.changed` | Developer processes start or stop |
|
|
551
721
|
|
|
552
|
-
|
|
553
|
-
moved, or resized).
|
|
722
|
+
#### `windows.changed`
|
|
554
723
|
|
|
555
724
|
```json
|
|
556
|
-
{
|
|
557
|
-
"event": "windows.changed",
|
|
558
|
-
"data": {
|
|
559
|
-
"windows": [ ... ],
|
|
560
|
-
"added": [1234],
|
|
561
|
-
"removed": [5678]
|
|
562
|
-
}
|
|
563
|
-
}
|
|
725
|
+
{ "event": "windows.changed", "data": { "windowCount": 12, "added": [1234], "removed": [5678] } }
|
|
564
726
|
```
|
|
565
727
|
|
|
566
|
-
|
|
567
|
-
|-----------|----------|------------------------------------|
|
|
568
|
-
| `windows` | array | Full current window list |
|
|
569
|
-
| `added` | number[] | Window IDs that appeared |
|
|
570
|
-
| `removed` | number[] | Window IDs that disappeared |
|
|
728
|
+
#### `tmux.changed`
|
|
571
729
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
730
|
+
```json
|
|
731
|
+
{ "event": "tmux.changed", "data": { "sessionCount": 3, "sessions": ["myapp-a1b2c3"] } }
|
|
732
|
+
```
|
|
575
733
|
|
|
576
|
-
|
|
734
|
+
#### `layer.switched`
|
|
577
735
|
|
|
578
736
|
```json
|
|
579
|
-
{
|
|
580
|
-
"event": "tmux.changed",
|
|
581
|
-
"data": {
|
|
582
|
-
"sessions": [ ... ]
|
|
583
|
-
}
|
|
584
|
-
}
|
|
737
|
+
{ "event": "layer.switched", "data": { "index": 1 } }
|
|
585
738
|
```
|
|
586
739
|
|
|
587
|
-
|
|
588
|
-
|------------|-------|--------------------------|
|
|
589
|
-
| `sessions` | array | Full current session list |
|
|
740
|
+
#### `ocr.scanComplete`
|
|
590
741
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
742
|
+
```json
|
|
743
|
+
{ "event": "ocr.scanComplete", "data": { "windowCount": 12, "totalBlocks": 342 } }
|
|
744
|
+
```
|
|
594
745
|
|
|
595
|
-
|
|
746
|
+
#### `processes.changed`
|
|
596
747
|
|
|
597
748
|
```json
|
|
598
|
-
{
|
|
599
|
-
"event": "layer.switched",
|
|
600
|
-
"data": {
|
|
601
|
-
"index": 1
|
|
602
|
-
}
|
|
603
|
-
}
|
|
749
|
+
{ "event": "processes.changed", "data": { "interestingCount": 5, "pids": [1234, 5678] } }
|
|
604
750
|
```
|
|
605
751
|
|
|
606
|
-
| Field | Type | Description |
|
|
607
|
-
|---------|--------|------------------------------|
|
|
608
|
-
| `index` | number | Index of the now-active layer |
|
|
609
|
-
|
|
610
752
|
---
|
|
611
753
|
|
|
612
|
-
## Agent integration
|
|
754
|
+
## Agent integration
|
|
613
755
|
|
|
614
756
|
### CLAUDE.md snippet
|
|
615
757
|
|
|
@@ -631,7 +773,7 @@ is available at ws://127.0.0.1:9399.
|
|
|
631
773
|
|
|
632
774
|
### Import
|
|
633
775
|
\```js
|
|
634
|
-
import { daemonCall } from 'lattices/daemon-client'
|
|
776
|
+
import { daemonCall } from '@arach/lattices/daemon-client'
|
|
635
777
|
\```
|
|
636
778
|
```
|
|
637
779
|
|
|
@@ -640,7 +782,7 @@ import { daemonCall } from 'lattices/daemon-client'
|
|
|
640
782
|
An orchestrator agent can set up the full workspace for sub-agents:
|
|
641
783
|
|
|
642
784
|
```js
|
|
643
|
-
import { daemonCall } from 'lattices/daemon-client'
|
|
785
|
+
import { daemonCall } from '@arach/lattices/daemon-client'
|
|
644
786
|
|
|
645
787
|
// Discover what's available
|
|
646
788
|
const projects = await daemonCall('projects.list')
|
|
@@ -660,10 +802,10 @@ await daemonCall('window.tile', { session: api.name, position: 'right' })
|
|
|
660
802
|
|
|
661
803
|
### Reactive event pattern
|
|
662
804
|
|
|
663
|
-
Subscribe to events
|
|
805
|
+
Subscribe to events to react to workspace changes:
|
|
664
806
|
|
|
665
807
|
```js
|
|
666
|
-
import WebSocket from 'ws'
|
|
808
|
+
import WebSocket from 'ws'
|
|
667
809
|
|
|
668
810
|
const ws = new WebSocket('ws://127.0.0.1:9399')
|
|
669
811
|
|
|
@@ -671,12 +813,11 @@ ws.on('message', (raw) => {
|
|
|
671
813
|
const msg = JSON.parse(raw)
|
|
672
814
|
|
|
673
815
|
if (msg.event === 'tmux.changed') {
|
|
674
|
-
console.log('Sessions
|
|
816
|
+
console.log('Sessions:', msg.data.sessions.join(', '))
|
|
675
817
|
}
|
|
676
818
|
|
|
677
819
|
if (msg.event === 'windows.changed') {
|
|
678
|
-
|
|
679
|
-
console.log('Lattices windows:', latticesWindows.length)
|
|
820
|
+
console.log('Windows:', msg.data.windowCount, 'total')
|
|
680
821
|
}
|
|
681
822
|
|
|
682
823
|
if (msg.event === 'layer.switched') {
|
|
@@ -695,7 +836,7 @@ ws.on('open', () => {
|
|
|
695
836
|
Always verify the daemon is running before making calls:
|
|
696
837
|
|
|
697
838
|
```js
|
|
698
|
-
import { isDaemonRunning, daemonCall } from 'lattices/daemon-client'
|
|
839
|
+
import { isDaemonRunning, daemonCall } from '@arach/lattices/daemon-client'
|
|
699
840
|
|
|
700
841
|
if (!(await isDaemonRunning())) {
|
|
701
842
|
console.error('lattices daemon is not running — start it with: lattices app')
|