@kdonev/termscape 0.1.0 → 0.1.1

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 (78) hide show
  1. package/README.md +176 -13
  2. package/dist/agents/detect.d.ts +27 -0
  3. package/dist/agents/detect.d.ts.map +1 -0
  4. package/dist/agents/detect.js +210 -0
  5. package/dist/agents/detect.js.map +1 -0
  6. package/dist/agents/profiles.d.ts +110 -0
  7. package/dist/agents/profiles.d.ts.map +1 -1
  8. package/dist/agents/profiles.js +215 -0
  9. package/dist/agents/profiles.js.map +1 -1
  10. package/dist/agents/templates.d.ts +85 -0
  11. package/dist/agents/templates.d.ts.map +1 -0
  12. package/dist/agents/templates.js +133 -0
  13. package/dist/agents/templates.js.map +1 -0
  14. package/dist/agents/wiring.d.ts +30 -1
  15. package/dist/agents/wiring.d.ts.map +1 -1
  16. package/dist/agents/wiring.js +179 -10
  17. package/dist/agents/wiring.js.map +1 -1
  18. package/dist/cli.js +41 -14
  19. package/dist/cli.js.map +1 -1
  20. package/dist/db/migrations.d.ts.map +1 -1
  21. package/dist/db/migrations.js +55 -0
  22. package/dist/db/migrations.js.map +1 -1
  23. package/dist/db/store.d.ts +41 -0
  24. package/dist/db/store.d.ts.map +1 -1
  25. package/dist/db/store.js +75 -2
  26. package/dist/db/store.js.map +1 -1
  27. package/dist/hub.d.ts +227 -6
  28. package/dist/hub.d.ts.map +1 -1
  29. package/dist/hub.js +504 -33
  30. package/dist/hub.js.map +1 -1
  31. package/dist/hub.tgz +0 -0
  32. package/dist/mcp/server.d.ts +2 -0
  33. package/dist/mcp/server.d.ts.map +1 -1
  34. package/dist/mcp/server.js +19 -2
  35. package/dist/mcp/server.js.map +1 -1
  36. package/dist/protocol/domain.d.ts +87 -0
  37. package/dist/protocol/domain.d.ts.map +1 -1
  38. package/dist/protocol/domain.js +104 -0
  39. package/dist/protocol/domain.js.map +1 -1
  40. package/dist/protocol/mcp-tools.d.ts +30 -0
  41. package/dist/protocol/mcp-tools.d.ts.map +1 -1
  42. package/dist/protocol/mcp-tools.js +50 -0
  43. package/dist/protocol/mcp-tools.js.map +1 -1
  44. package/dist/protocol/peer.d.ts +36 -0
  45. package/dist/protocol/peer.d.ts.map +1 -1
  46. package/dist/protocol/peer.js +35 -1
  47. package/dist/protocol/peer.js.map +1 -1
  48. package/dist/protocol/ws.d.ts +254 -0
  49. package/dist/protocol/ws.d.ts.map +1 -1
  50. package/dist/protocol/ws.js +160 -5
  51. package/dist/protocol/ws.js.map +1 -1
  52. package/dist/remote/lan.d.ts +45 -0
  53. package/dist/remote/lan.d.ts.map +1 -1
  54. package/dist/remote/lan.js +44 -0
  55. package/dist/remote/lan.js.map +1 -1
  56. package/dist/remote/peer-serve.d.ts.map +1 -1
  57. package/dist/remote/peer-serve.js +31 -3
  58. package/dist/remote/peer-serve.js.map +1 -1
  59. package/dist/remote/peer.d.ts +7 -0
  60. package/dist/remote/peer.d.ts.map +1 -1
  61. package/dist/remote/peer.js +22 -0
  62. package/dist/remote/peer.js.map +1 -1
  63. package/dist/remote/registry.d.ts +69 -2
  64. package/dist/remote/registry.d.ts.map +1 -1
  65. package/dist/remote/registry.js +134 -10
  66. package/dist/remote/registry.js.map +1 -1
  67. package/dist/server.d.ts +22 -3
  68. package/dist/server.d.ts.map +1 -1
  69. package/dist/server.js +237 -117
  70. package/dist/server.js.map +1 -1
  71. package/dist/session/manager.d.ts +44 -4
  72. package/dist/session/manager.d.ts.map +1 -1
  73. package/dist/session/manager.js +161 -34
  74. package/dist/session/manager.js.map +1 -1
  75. package/package.json +1 -1
  76. package/web/assets/{index-BOqZOoep.js → index-Bq_Y0JcC.js} +6 -6
  77. package/web/assets/{index-B2JtCtHa.css → index-DQYDkOwJ.css} +1 -1
  78. package/web/index.html +2 -2
package/README.md CHANGED
@@ -8,9 +8,10 @@ hub exposes. Agents can look each other up, send each other messages, spawn
8
8
  helpers into their workspace, and check on each other's terminals. A message
9
9
  from one agent is delivered by typing it into the other's terminal, immediately.
10
10
 
11
- Local-first: the hub runs on your machine, binds loopback only, and the UI is a
12
- browser tab. Other machines run the same hub as a daemon and appear on the same
13
- canvas.
11
+ Local-first: the hub runs on your machine and the UI is a browser tab. It binds
12
+ every interface, so the canvas opens on your phone or a second screen as well
13
+ with its token; `--listen loopback` keeps it to this machine. Other machines run
14
+ the same hub as a daemon and appear on the same canvas.
14
15
 
15
16
  ## Getting started
16
17
 
@@ -23,9 +24,21 @@ if you would rather it did not; the URL is printed either way. Nothing is
23
24
  installed system-wide: state lives in `~/.termscape`, and deleting that
24
25
  directory is the uninstall.
25
26
 
27
+ The hub is reachable from your network, so the same URL — token and all — opens
28
+ the canvas on a phone or a second screen. Attaching another machine is a
29
+ separate opt-in; see [Adding another machine](#adding-another-machine). To keep
30
+ the hub to this machine entirely:
31
+
32
+ ```bash
33
+ npx @kdonev/termscape --listen loopback
34
+ ```
35
+
26
36
  Slide out the **machines** panel: every machine, the workspaces on it, and
27
37
  the agents in each. Point a workspace at a folder there, and start an agent
28
- in it.
38
+ in it. Adding, editing and removing all open a dialog over the canvas, so the
39
+ node you acted on stays where it was and a refusal — a folder that is not
40
+ there, a rename the addresses will not allow — arrives in the dialog next to
41
+ the field, with what you typed still in it.
29
42
 
30
43
  - **Scroll** to pan, **Ctrl/⌘ + scroll** to zoom, **Ctrl/⌘ + 1** to fit,
31
44
  **Ctrl/⌘ + 2** to zoom to one terminal
@@ -35,6 +48,8 @@ in it.
35
48
  ordinary pace is left alone
36
49
  - Below 60% zoom terminals become preview cards — zoom in to interact
37
50
  - Clicking an agent in the panel brings the canvas to it
51
+ - Clicking the canvas closes the panel, as does **Escape** — which closes the
52
+ panel first and clears the selection only once it is shut
38
53
 
39
54
  ## Requirements
40
55
 
@@ -66,8 +81,11 @@ next window.
66
81
 
67
82
  ## Adding another machine
68
83
 
69
- Start the hub so the other machine can see it, then let that machine come to
70
- you:
84
+ The hub is already reachable from your network, but it does not hand out join
85
+ links until asked. `/join` is the one page served without your token — it has
86
+ to be typed by hand on a machine that has nothing yet — so being reachable and
87
+ being enrollable are kept as two separate permissions. Turn the second one on,
88
+ then let the other machine come to you:
71
89
 
72
90
  ```bash
73
91
  npx @kdonev/termscape --listen lan
@@ -134,9 +152,10 @@ have actually changed, and checks that tree really loads before trusting it.
134
152
 
135
153
  Two things worth knowing:
136
154
 
137
- - `--listen` is opt-in and off by default. With it, your hub is reachable on
138
- that network, and anyone who can load the join page can attach a machine to
139
- your canvas. Every other route still requires the token.
155
+ - The **join page** is opt-in and off by default; `--listen lan` is what turns
156
+ it on. With it, anyone who can reach your hub can load that page and attach a
157
+ machine to your canvas. Every other route still requires the token, and being
158
+ reachable — which a default install already is — grants none of this.
140
159
  - Each download carries a single-use key that expires in 15 minutes. Once a
141
160
  machine has joined it keeps a durable token in `~/.termscape/host-token` and
142
161
  rejoins by itself after a reboot or a dropped link — its agents keep running
@@ -158,15 +177,61 @@ tunnel. Same protocol, opposite direction.
158
177
  | `read_screen` | look at another agent's terminal without interrupting it |
159
178
  | `set_status` | label your own window so the human can see what you are doing |
160
179
  | `stop_agent` | stop an agent you spawned |
180
+ | `propose_template` | ask the human to save a way of starting an agent, under a name |
181
+
182
+ `propose_template` is the only one that asks rather than does. A template
183
+ changes how *future* agents are launched, on every machine, with nobody
184
+ necessarily watching — so the canvas shows it to a human, who can edit it
185
+ before accepting or decline it outright. The tool returns as soon as they have
186
+ been shown it rather than blocking until they answer, and the decision is typed
187
+ back into the agent's terminal. Nothing is stored unless someone says yes.
161
188
 
162
189
  Every agent is also given a brief explaining its address, its peers, and that
163
190
  text arriving as `[from <address>] ...` is a colleague rather than the human.
164
191
 
165
192
  ## Agent profiles
166
193
 
167
- An agent CLI is configuration, not code. Built-ins are `claude` and `shell`
168
- (a plain terminal, no agent wiring). Override or add profiles in
169
- `~/.termscape/agents.toml`:
194
+ An agent CLI is configuration, not code. Built-ins are `claude`, `codex`,
195
+ `gemini`, `opencode` and `shell`.
196
+
197
+ Every one of them except `shell` is wired to the hub's MCP endpoint: each gets an
198
+ address, a brief and the `send_message` tool set. They arrive there by four
199
+ different routes, because no two of these CLIs configure an MCP server the same
200
+ way — and **none of them writes to a file you own**, so there is nothing left
201
+ behind when a session ends or when the hub is killed rather than stopped.
202
+
203
+ | agent | how it reaches the hub | brief | resume |
204
+ | --- | --- | --- | --- |
205
+ | `claude` | `--mcp-config` on a generated file | `--append-system-prompt-file` | `--resume <uuid>` |
206
+ | `codex` | `-c mcp_servers.…` overrides, one run only | typed in at startup | restarts clean |
207
+ | `gemini` | `GEMINI_CLI_SYSTEM_SETTINGS_PATH` at a generated file | typed in at startup | restarts clean |
208
+ | `opencode` | `OPENCODE_CONFIG_CONTENT`, no file anywhere | typed in at startup | restarts clean |
209
+
210
+ The last three are typed at rather than handed a brief because none of them can
211
+ *append* to its system prompt — Codex's `base_instructions` and Gemini's
212
+ `GEMINI_SYSTEM_MD` each replace the whole thing, which would cost the agent its
213
+ own tool instructions. None of the three is resumable either: Codex mints a
214
+ session id it will not accept from us, and Gemini accepts one but resumes by
215
+ list index instead. They restart clean, and are briefed again when they do.
216
+
217
+ Two details worth knowing, because both are easy to get wrong:
218
+
219
+ - **Codex's bearer token goes in the environment, never `-c`.** Config
220
+ overrides land in the command line, where any other user on the machine can
221
+ read them. `bearer_token_env_var` exists precisely for this.
222
+ - **opencode is configured entirely from the environment.** Its config is
223
+ handed over as a string, merged with your own rather than replacing it, so
224
+ your models, themes and your own MCP servers survive the session. Note that
225
+ `opencode mcp add` is *not* how this is done: that command writes to
226
+ `~/.config/opencode/opencode.json` and ignores `OPENCODE_CONFIG` while doing
227
+ it. Setting the variable also stops opencode writing its default config file
228
+ on start, so a session leaves nothing behind at all.
229
+
230
+ `shell` is the one profile that is not an agent, and it is not one in a way no
231
+ flag can fix: it runs a shell, so text typed at it is executed rather than read.
232
+ It gets no brief, and its profile says so rather than relying on a default.
233
+
234
+ Override or add profiles in `~/.termscape/agents.toml`:
170
235
 
171
236
  ```toml
172
237
  [my-agent]
@@ -175,8 +240,100 @@ args = ["--mcp-config", "{{mcp_config_path}}"]
175
240
  status = "heuristic" # or "hooks", for exact turn boundaries
176
241
  ready_hint = "[$#>%] ?$" # prompt regex, for the idle indicator
177
242
  inject = "bracketed" # bracketed paste, or "raw"
243
+ brief = "typed" # "flag" if it can append a system prompt,
244
+ # "none" for a terminal that would execute one
245
+ version_args = ["--version"] # how to ask its version, for the panel
246
+ models_args = ["models"] # optional: one model per line on stdout
247
+ models = ["opus", "sonnet"] # the answer when it has no listing command
248
+ model_args = ["--model", "{{model}}"] # how it spells a model, if it takes one
249
+ effort_args = ["--effort", "{{effort}}"] # and an effort
250
+ efforts = ["low", "high"] # the levels it documents
178
251
  ```
179
252
 
253
+ ### Templates
254
+
255
+ The picker offers **templates**, not CLIs. A template is an agent plus a model,
256
+ an effort and an opening instruction — a saved answer to all four, picked once
257
+ instead of typed every time. Every agent gets a bare template under its own
258
+ name, so `claude` and `shell` are still there and nothing that worked stops
259
+ working.
260
+
261
+ Templates are a root of their own in the panel, next to the machines — they are
262
+ config rather than a place, and one template is used on every machine, so it
263
+ does not live under one. **+ template** makes one, `edit` changes it, `×`
264
+ removes it. The list sits below the machines and starts collapsed: machines are
265
+ what you work in every day, templates are what you set up once and then forget.
266
+
267
+ They can also be written by hand, and a hand-written one wins:
268
+
269
+ ```toml
270
+ [template.reviewer]
271
+ agent = "claude"
272
+ model = "opus"
273
+ effort = "high"
274
+ prompt = "Review the diff on this branch for correctness bugs. Report, do not fix."
275
+ ```
276
+
277
+ - Templates made in the panel are stored in `state.db`, the same place
278
+ workspaces and hosts already live. `agents.toml` is a second, read-only
279
+ source: the hub never writes it, so a formatter cannot eat the comments and
280
+ ordering of a file you edit by hand.
281
+ - When both declare the same name **the file wins**, and the dialog refuses the
282
+ name rather than storing a row that would never appear. Someone who wrote a
283
+ template by hand meant it.
284
+ - An agent's own bare template is derived, not stored. Editing one makes a
285
+ stored template that shadows it; removing that reveals the bare one again
286
+ rather than leaving a gap.
287
+ - Removing a template takes nothing from the agents it already started. They
288
+ keep their model, their effort and their ability to resume, because a session
289
+ records what its template resolved to rather than looking it up again.
290
+
291
+ Three words, kept apart deliberately: an **agent** is the CLI program, a
292
+ **template** is what you pick from the list, and a **session** is one running
293
+ instance with an address and a window.
294
+
295
+ - **A template holds values, not arguments.** Claude Code takes `--model` and
296
+ `--effort`; opencode takes `-m provider/model` and has no effort setting on
297
+ its TUI at all; Codex takes `-m` but spells effort as a config override,
298
+ `-c model_reasoning_effort=…`, because it has no `--effort` flag. So the
299
+ template says *which* model, and the agent declares how to spell it. An agent that declares nothing takes nothing, and a template
300
+ asking for a model or an effort it cannot spell is a configuration error
301
+ reported when the file loads — visible in the dialog, not a flag silently
302
+ dropped at launch.
303
+ - **The first instruction is typed in once the CLI is up**, not passed as an
304
+ argument, and it does not repeat when a session is resumed. It is how the
305
+ session started, not what it is.
306
+ - **A resumed session comes back on the model it left with.** What the template
307
+ resolved to is recorded on the session, because resume rebuilds the command
308
+ line rather than replaying it — and because a template can be edited
309
+ afterwards.
310
+ - **Starting an agent on another machine sends values, not a template name.**
311
+ The two machines do not share config, so the name is resolved here first.
312
+
313
+ ### What is actually installed
314
+
315
+ Each machine probes its own `PATH` and reports back, so the panel shows, per
316
+ machine, which agents are there, what version each is, and the models it
317
+ offers. That is per machine on purpose: a host has its own `PATH`, and starting
318
+ an agent it does not have used to fail at launch inside a terminal window,
319
+ where the error reads like the hub is broken.
320
+
321
+ - A declared agent that is **not** installed stays in the list, greyed out,
322
+ naming the command that was not found — rather than vanishing, which looks
323
+ like the config was ignored.
324
+ - Models are enumerated where the CLI can be asked (`opencode models` returns
325
+ a few hundred) and declared in the profile where it cannot. Claude Code has
326
+ no listing command; its `--help` documents the aliases instead, and it takes
327
+ a full model name as readily as an alias. Codex is declared too, for a
328
+ different reason: `codex debug models` does render the real catalog, but it
329
+ is a debug command answering with half a megabyte of JSON rather than the
330
+ one-per-line stdout the profile reads, so the profile carries the slugs that
331
+ catalog marks visible. In every case a full model name outside the list is
332
+ still accepted — the list is what the dropdown suggests, never a limit.
333
+ - Probing runs after the hub is already serving and never blocks it. The first
334
+ page load usually shows *checking…*, and fills in a moment later. **check
335
+ again** in the start-an-agent dialog re-probes every machine.
336
+
180
337
  ## State and restart
181
338
 
182
339
  SQLite at `~/.termscape/state.db` holds everything needed to redraw the canvas
@@ -213,7 +370,13 @@ Agents can type into each other's terminals and spawn more agents. That is the
213
370
  feature, and it is the risk surface: an agent that reads a hostile repository
214
371
  could be talked into sending an attacker's text to a peer.
215
372
 
216
- - The hub binds `127.0.0.1` only, never a network interface. Remote hubs too.
373
+ - The hub binds every interface, so the canvas is reachable from your network —
374
+ but only with its token, which is minted per run and never printed anywhere
375
+ the network can read. `--listen loopback` narrows it to this machine.
376
+ - A hub running `--headless` — one that joined a canvas, or was deployed over
377
+ ssh and is reached through its tunnel — stays on `127.0.0.1` regardless.
378
+ - `/join` is the only route served without the token, and it is off unless you
379
+ passed `--listen lan`.
217
380
  - Every agent gets its own bearer token. The sender of a message is taken from
218
381
  that token, never from the arguments, so attribution cannot be forged.
219
382
  - Messages are length-capped, rate-limited per sender, and always arrive with a
@@ -0,0 +1,27 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import type { AgentProfileInfo } from '../protocol/index.js';
3
+ import type { ProfileRegistry } from './profiles.js';
4
+ export declare class AgentDetector extends EventEmitter {
5
+ private readonly profiles;
6
+ private known;
7
+ private readonly cache;
8
+ private running;
9
+ constructor(profiles: ProfileRegistry);
10
+ /**
11
+ * What is known right now, without probing anything.
12
+ *
13
+ * Never async, and never empty: a profile that has not been probed yet is
14
+ * reported with `available: null`, which the panel draws as "checking".
15
+ * Detection must not be able to hold up a hub starting or a page loading.
16
+ */
17
+ snapshot(): AgentProfileInfo[];
18
+ /**
19
+ * Probe every profile. Safe to call from anywhere, including on a timer or
20
+ * from the browser; concurrent calls share the one run in flight rather
21
+ * than spawning every CLI twice.
22
+ */
23
+ refresh(): Promise<AgentProfileInfo[]>;
24
+ private run;
25
+ private probe;
26
+ }
27
+ //# sourceMappingURL=detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/agents/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,eAAe,CAAC;AAkDnE,qBAAa,aAAc,SAAQ,YAAY;IAKjC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJrC,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IACvD,OAAO,CAAC,OAAO,CAA4C;gBAE9B,QAAQ,EAAE,eAAe;IAItD;;;;;;OAMG;IACH,QAAQ,IAAI,gBAAgB,EAAE;IAI9B;;;;OAIG;IACH,OAAO,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;YAQxB,GAAG;YAWH,KAAK;CA8DpB"}
@@ -0,0 +1,210 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { execFile } from 'node:child_process';
3
+ import { extname } from 'node:path';
4
+ import { platform } from 'node:process';
5
+ import { which } from './resolve.js';
6
+ /**
7
+ * What is actually installed on this machine, and what each one can be
8
+ * pointed at.
9
+ *
10
+ * The picker used to offer whatever agents.toml declared, whether or not the
11
+ * command existed, and said nothing about models. Two things make that worse
12
+ * than it sounds. A declared agent that is not installed fails at launch,
13
+ * inside a terminal window on the canvas, where the error reads like the hub
14
+ * is broken. And a model has to be typed from memory, which is how you find
15
+ * out that the CLI spells it differently than you thought.
16
+ *
17
+ * This answers both, per machine - and per machine is the point, because a
18
+ * host has its own PATH and this hub's answer says nothing about it.
19
+ */
20
+ /**
21
+ * How long any one probe gets before it is killed.
22
+ *
23
+ * Generous on purpose, and measured rather than guessed. `opencode models` was
24
+ * about 16 seconds on Windows through the .cmd shim npm installs when this was
25
+ * written; on opencode 1.18.29 it is nearer 38, which is how the old 30-second
26
+ * bound came to report a CLI with no models at all rather than its 367.
27
+ *
28
+ * That is the argument for erring long, not for chasing the number: a tighter
29
+ * bound does not make anything faster, it just turns a working CLI into one
30
+ * that reports no version and no models. Nothing waits on detection - it
31
+ * starts after the hub is already serving and the browser is told when each
32
+ * answer lands - so the only thing a long timeout costs is a late answer from
33
+ * a CLI that was never going to reply.
34
+ */
35
+ const PROBE_TIMEOUT_MS = 90_000;
36
+ /** Nobody scrolls a dropdown past this, and 395 lines is a real answer. */
37
+ const MAX_MODELS = 500;
38
+ export class AgentDetector extends EventEmitter {
39
+ profiles;
40
+ known = new Map();
41
+ cache = new Map();
42
+ running = null;
43
+ constructor(profiles) {
44
+ super();
45
+ this.profiles = profiles;
46
+ }
47
+ /**
48
+ * What is known right now, without probing anything.
49
+ *
50
+ * Never async, and never empty: a profile that has not been probed yet is
51
+ * reported with `available: null`, which the panel draws as "checking".
52
+ * Detection must not be able to hold up a hub starting or a page loading.
53
+ */
54
+ snapshot() {
55
+ return this.profiles.list().map((p) => this.known.get(p.id) ?? unprobed(p));
56
+ }
57
+ /**
58
+ * Probe every profile. Safe to call from anywhere, including on a timer or
59
+ * from the browser; concurrent calls share the one run in flight rather
60
+ * than spawning every CLI twice.
61
+ */
62
+ refresh() {
63
+ if (this.running)
64
+ return this.running;
65
+ this.running = this.run().finally(() => {
66
+ this.running = null;
67
+ });
68
+ return this.running;
69
+ }
70
+ async run() {
71
+ // In parallel: each of these is a process spawn that mostly sits waiting,
72
+ // and doing them in series makes the first load as slow as their sum.
73
+ const found = await Promise.all(this.profiles.list().map((p) => this.probe(p)));
74
+ const next = new Map(found.map((info) => [info.id, info]));
75
+ const changed = !same(this.known, next);
76
+ this.known = next;
77
+ if (changed)
78
+ this.emit('changed', found);
79
+ return found;
80
+ }
81
+ async probe(profile) {
82
+ const base = unprobed(profile);
83
+ const path = which(profile.command);
84
+ if (!path) {
85
+ // Stays in the list rather than vanishing: a declared agent that
86
+ // silently disappeared looks like the config was ignored, and the
87
+ // command that was not found is the thing worth showing.
88
+ return {
89
+ ...base,
90
+ available: false,
91
+ detail: `not found on PATH: ${profile.command}`,
92
+ };
93
+ }
94
+ const version = profile.versionArgs
95
+ ? await run(path, profile.versionArgs, profile.probeEnv).catch(() => null)
96
+ : null;
97
+ const key = `${path}|${version ?? ''}`;
98
+ const cached = this.cache.get(profile.id);
99
+ if (cached?.key === key) {
100
+ return {
101
+ ...base,
102
+ available: true,
103
+ commandPath: path,
104
+ version,
105
+ models: cached.models,
106
+ modelSource: cached.source,
107
+ };
108
+ }
109
+ const listed = profile.modelsArgs
110
+ ? await run(path, profile.modelsArgs, profile.probeEnv).catch(() => null)
111
+ : null;
112
+ let models = [];
113
+ let source = 'none';
114
+ if (listed !== null) {
115
+ models = listed
116
+ .split(/\r?\n/)
117
+ .map((l) => l.trim())
118
+ .filter((l) => l.length > 0)
119
+ .slice(0, MAX_MODELS);
120
+ source = 'listed';
121
+ }
122
+ if (models.length === 0 && profile.models?.length) {
123
+ // Either the CLI has no listing command, or its command failed. The
124
+ // declared list is the answer in both cases.
125
+ models = profile.models;
126
+ source = 'declared';
127
+ }
128
+ this.cache.set(profile.id, { key, models, source });
129
+ return {
130
+ ...base,
131
+ available: true,
132
+ commandPath: path,
133
+ version,
134
+ models,
135
+ modelSource: source,
136
+ };
137
+ }
138
+ }
139
+ /**
140
+ * A profile before anything has been asked of it.
141
+ *
142
+ * `shell` is the exception that is born answered: it is whatever COMSPEC or
143
+ * SHELL points at, it is always there, and there is nothing to probe.
144
+ */
145
+ function unprobed(p) {
146
+ const detectable = p.versionArgs !== undefined || p.modelsArgs !== undefined;
147
+ return {
148
+ id: p.id,
149
+ description: p.description,
150
+ mcp: p.mcp,
151
+ resumable: !!p.resumeArgs,
152
+ command: p.command,
153
+ commandPath: null,
154
+ available: detectable ? null : true,
155
+ version: null,
156
+ detail: null,
157
+ models: p.models ?? [],
158
+ modelSource: p.models?.length ? 'declared' : 'none',
159
+ takesModel: p.modelArgs !== undefined,
160
+ takesEffort: p.effortArgs !== undefined,
161
+ efforts: p.efforts ?? [],
162
+ };
163
+ }
164
+ /**
165
+ * Run one probe and return its stdout.
166
+ *
167
+ * Timed and killed rather than awaited indefinitely: the whole promise of
168
+ * this file is that a CLI which hangs on `--version` costs a slow dropdown
169
+ * and not a hub that will not boot. Windows shims (.cmd/.bat) are not
170
+ * executable images, so they go through cmd.exe exactly as spawning an agent
171
+ * does - `shell: true` would be the short version and would also hand the
172
+ * command line to a shell that reinterprets it.
173
+ */
174
+ function run(path, args, env) {
175
+ const ext = extname(path).toLowerCase();
176
+ const viaCmd = platform === 'win32' && (ext === '.cmd' || ext === '.bat');
177
+ const file = viaCmd ? (process.env.COMSPEC ?? 'cmd.exe') : path;
178
+ const argv = viaCmd ? ['/c', path, ...args] : args;
179
+ return new Promise((resolve, reject) => {
180
+ execFile(file, argv, {
181
+ timeout: PROBE_TIMEOUT_MS,
182
+ windowsHide: true,
183
+ maxBuffer: 4 * 1024 * 1024,
184
+ // Inherited and then overlaid: a probe still needs PATH and HOME to
185
+ // find and run anything at all.
186
+ env: env ? { ...process.env, ...env } : process.env,
187
+ }, (err, stdout) => {
188
+ // Some CLIs print their version and exit non-zero, or write it to
189
+ // stderr and nothing to stdout. Stdout with content wins over the
190
+ // exit code; nothing on stdout is a failure whatever the code said.
191
+ const out = stdout.trim();
192
+ if (out)
193
+ resolve(out);
194
+ else
195
+ reject(err ?? new Error('no output'));
196
+ });
197
+ });
198
+ }
199
+ /** Whether two detection results are the same, so nothing is broadcast. */
200
+ function same(a, b) {
201
+ if (a.size !== b.size)
202
+ return false;
203
+ for (const [id, x] of a) {
204
+ const y = b.get(id);
205
+ if (!y || JSON.stringify(x) !== JSON.stringify(y))
206
+ return false;
207
+ }
208
+ return true;
209
+ }
210
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/agents/detect.ts"],"names":[],"mappings":"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"}
@@ -16,19 +16,129 @@ export interface AgentProfile {
16
16
  /** Regex matched against the last non-empty line when status = heuristic. */
17
17
  readyHint?: string;
18
18
  inject: InjectMode;
19
+ /**
20
+ * How the brief reaches the agent.
21
+ *
22
+ * `flag` - the profile's own args point at `{{brief_path}}`, the way Claude
23
+ * Code's `--append-system-prompt-file` does. The brief is part of the
24
+ * system prompt and the agent never sees it as a turn.
25
+ *
26
+ * `typed` - the CLI has no way to *append* to its system prompt, so the
27
+ * brief is typed into the terminal once the CLI is up, ahead of any opening
28
+ * instruction. Both of the other wired CLIs are in this position, and for
29
+ * the same reason: Codex's `base_instructions` and Gemini's
30
+ * `GEMINI_SYSTEM_MD` each *replace* the whole system prompt rather than add
31
+ * to it, so using either would cost the agent its own tool instructions -
32
+ * a far worse trade than a first turn that arrives as text.
33
+ *
34
+ * `none` - nothing is sent. `shell` is the reason this value exists and it
35
+ * is not a value any agent should want: a shell *executes* what is typed at
36
+ * it, so a brief there is not context, it is a series of commands that fail
37
+ * loudly.
38
+ *
39
+ * Absent means `flag` for a wired agent - what every profile written before
40
+ * this did - and `none` for an unwired one, because `mcp: false` has always
41
+ * meant "a plain terminal" and a profile that declared one should not start
42
+ * having text typed into it. An unwired agent that *is* an agent says
43
+ * `brief = "typed"` and gets the shorter brief: no tool list, because it has
44
+ * no tools, but it is still told its address and still told that a
45
+ * `[from ...]` line is a colleague rather than the human.
46
+ */
47
+ brief?: 'flag' | 'typed' | 'none';
19
48
  /**
20
49
  * Argument template used to bring a session back with its prior
21
50
  * conversation. `{{uuid}}` is replaced with agent_session_uuid. When absent,
22
51
  * the profile is not resumable and restarts clean.
23
52
  */
24
53
  resumeArgs?: string[];
54
+ /**
55
+ * Environment for the *detection* probes only, not for a session.
56
+ *
57
+ * Detection runs these CLIs on a machine whose owner may never have run them
58
+ * by hand, so a probe that leaves something behind is the hub writing to
59
+ * somebody's home directory uninvited. `opencode models` does exactly that -
60
+ * it creates ~/.config/opencode/opencode.jsonc - and this is how it is told
61
+ * not to.
62
+ */
63
+ probeEnv?: Record<string, string>;
64
+ /**
65
+ * How to ask this CLI its version. Its stdout is shown to the user and is
66
+ * also the cache key for the model list, so a CLI that updates underneath
67
+ * us re-lists rather than serving last week's answer.
68
+ */
69
+ versionArgs?: string[];
70
+ /**
71
+ * How to ask this CLI what models it can be pointed at: one
72
+ * `provider/model` (or bare name) per line on stdout. Absent when the CLI
73
+ * has no such command, which is the common case.
74
+ */
75
+ modelsArgs?: string[];
76
+ /**
77
+ * The answer for a CLI that cannot be asked. Claude Code is the example:
78
+ * it has no listing command, and its --help documents the aliases instead.
79
+ * Used only when `modelsArgs` is absent or its command fails.
80
+ */
81
+ models?: string[];
82
+ /**
83
+ * How this agent spells a model on the command line. `{{model}}` carries
84
+ * the value a template chose.
85
+ *
86
+ * A template holds *values*, not argv, because no two of these agents agree:
87
+ * Claude Code takes `--model`, opencode takes `-m provider/model`. So the
88
+ * template says which model and the agent says how to write it down.
89
+ *
90
+ * Absent means the agent takes no model here, and a template that names one
91
+ * for it is a configuration error reported at load rather than a flag
92
+ * silently dropped at launch. The whole fragment disappears when a template
93
+ * leaves the value unset - it does not expand to an empty string.
94
+ */
95
+ modelArgs?: string[];
96
+ /** The same for effort, with `{{effort}}`. */
97
+ effortArgs?: string[];
98
+ /** The effort levels this agent documents, for the dialog to offer. */
99
+ efforts?: string[];
25
100
  }
26
101
  /**
27
102
  * Built-in profiles. All Claude Code flags here are verified against the CLI:
28
103
  * --mcp-config, --strict-mcp-config, --session-id, --settings,
29
104
  * --append-system-prompt-file, --resume.
105
+ *
106
+ * Codex and Gemini are wired too, and every flag and key below was verified
107
+ * against the CLI actually installed - codex-cli 0.153.4 and gemini 0.58.0 -
108
+ * rather than written from memory. Both reach the hub the same way Claude
109
+ * Code does, over one streamable-HTTP MCP server carrying a bearer token, and
110
+ * neither needs a byte written to config the user owns:
111
+ *
112
+ * - **Codex** takes `-c <dotted.key>=<toml>` on any invocation, so the server
113
+ * is declared in argv for the one run. The token is *not* in argv - it goes
114
+ * in the environment, and `bearer_token_env_var` names the variable to read
115
+ * it from, which keeps it out of every process listing on the machine.
116
+ * - **Gemini** has no per-run config flag, but
117
+ * `GEMINI_CLI_SYSTEM_SETTINGS_PATH` repoints its system settings layer at a
118
+ * file of our choosing, which the hub generates per session.
119
+ *
120
+ * - **opencode** turned out to be the least invasive of the three and was
121
+ * assumed to be the hardest. It reads its entire config from
122
+ * `OPENCODE_CONFIG_CONTENT`, so nothing is written anywhere at all, and what
123
+ * it is given is merged with the user's own config rather than replacing it.
124
+ * The assumption that it had no per-run route came from reading
125
+ * `opencode mcp add` - which does write to the user's file, and ignores
126
+ * `OPENCODE_CONFIG` when it does - and mistaking that command for the only
127
+ * way in.
128
+ *
129
+ * `shell` is the only profile left that is not an agent, and it is not one in
130
+ * a way no flag can fix: it runs a shell, so text typed at it is executed
131
+ * rather than read.
30
132
  */
31
133
  export declare const BUILTIN_PROFILES: Record<string, AgentProfile>;
134
+ /**
135
+ * How this profile's brief travels, with the defaults applied.
136
+ *
137
+ * One place, because three callers need the same answer and the interesting
138
+ * part is the default rather than the field: absent means `flag` for a wired
139
+ * agent and `none` for an unwired one.
140
+ */
141
+ export declare function briefMode(p: AgentProfile): 'flag' | 'typed' | 'none';
32
142
  export declare class ProfileRegistry {
33
143
  private profiles;
34
144
  constructor(profiles?: Record<string, AgentProfile>);
@@ -1 +1 @@
1
- {"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../src/agents/profiles.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGtD;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,0EAA0E;IAC1E,GAAG,EAAE,OAAO,CAAC;IACb,mCAAmC;IACnC,MAAM,EAAE,OAAO,GAAG,WAAW,CAAC;IAC9B,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,UAAU,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAOD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CA8CzD,CAAC;AAEF,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAA+B;gBAEnC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC;IAInD,oEAAoE;IACpE,MAAM,CAAC,IAAI,IAAI,eAAe;IA8B9B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAIpC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY;IAMjC,IAAI,IAAI,YAAY,EAAE;IAItB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;CAIjC;AAED,+EAA+E;AAC/E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAI5E;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,EAAE,CAElF"}
1
+ {"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../src/agents/profiles.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGtD;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,0EAA0E;IAC1E,GAAG,EAAE,OAAO,CAAC;IACb,mCAAmC;IACnC,MAAM,EAAE,OAAO,GAAG,WAAW,CAAC;IAC9B,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,UAAU,CAAC;IACnB;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAClC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAItB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAIlB;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAOD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAoNzD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAEpE;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAA+B;gBAEnC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC;IAInD,oEAAoE;IACpE,MAAM,CAAC,IAAI,IAAI,eAAe;IAyC9B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAIpC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY;IAMjC,IAAI,IAAI,YAAY,EAAE;IAItB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;CAIjC;AAED,+EAA+E;AAC/E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAI5E;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,EAAE,CAElF"}