pi-link 0.5.2 → 0.6.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/CHANGELOG.md +20 -0
- package/README.md +126 -82
- package/bin/pi-link.mjs +10 -8
- package/index.ts +286 -223
- package/package.json +1 -1
- package/skills/pi-link-tools/SKILL.md +23 -47
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,26 @@ This changelog is based on the git history from `2026-03-21` (initial commit) th
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## 0.6.0 — 2026-10-25
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **Contract change: `link_compact` no longer waits for the target.** The call now returns as soon as the request is sent, with its request ID: `Compact request sent to "<name>" [<id>]; the result will arrive as a link notification.` That result no longer means the target compacted, or even that it accepted. Callers must wait for the outcome, which arrives later as a notification in the requester's own inbox, delivered like a received message: it starts a turn if the requester is idle, steers it if it is busy, and is held while the requester's own compaction gate stands. Each sent request produces at most one line, `link_compact "<name>" [<id>]: <outcome>`, naming the requested target. The line has no `From` header, because the hub or the requester may have produced it. The outcome is one of:
|
|
14
|
+
- `compacted`;
|
|
15
|
+
- `not done: <reason>`, covering a decline, a hub `not_found`, or a runtime failure or cancellation;
|
|
16
|
+
- `left the link before answering; result unknown`;
|
|
17
|
+
- `link disconnected before an answer; result unknown, the target may still be compacting`, after `/link-disconnect`.
|
|
18
|
+
|
|
19
|
+
A requester is no longer kept busy for up to five minutes, and can have several compactions outstanding at once. Failures that need no answer still return immediately in the result and produce no notification: not connected, self target, a target not in your group's list, an already-aborted call, or a request that could not be sent. Aborting after the request is sent no longer ends anything, since there is no remote cancel. The wire protocol is unchanged. Outstanding requests live only in the requester's memory: a reload or exit loses their outcomes, a response sent while the hub is down is not replayed, and a response after the requester renames goes to its old name; in those two cases no outcome is ever reported, as the next entry explains.
|
|
20
|
+
|
|
21
|
+
- **Contract change: a remote compaction waits until the target is free.** A busy target — an active run, an automatic retry or compaction, a queued continuation, or a reported `compacting` hold — no longer declines with `not done: target busy`. It holds the request with no deadline, checks every 500 ms, and compacts once Pi reports it idle and no compaction gate stands. Waiting raises no gate: the target keeps working and keeps receiving messages, which are held only once compaction starts. Its user sees `"<name>" asked this terminal to compact; waiting until it is idle`; `"<name>" asked this terminal to compact` now appears only when compaction starts at once. Idle is observed, not reserved: work that starts between that check and the compaction can still be interrupted, as with a request served on arrival. A target holds one request at a time; another, from any requester, is declined with `not done: target already has a compaction request waiting or running` (wire code `already_requested`). A completed `/tree` navigation ends a request that has not started, with `not done: target navigated its session tree while the request waited` (`tree_navigated`); disconnecting or ending the target's session drops it, and its requester sees the target leave. A failure is answered once and not retried. The requester's five-minute deadline is gone: an outcome can take as long as the target stays busy, and a lost response — the hub down, the requester renamed, or a target that was the hub leaving — leaves the request outstanding with no notification. The manual-compaction gate keeps its own five-minute backstop. Versions are not negotiated: a target on an earlier version still declines a busy request with `not done: target busy`, and an earlier requester still gives up after 300 seconds, ignores a later answer, and shows the new codes as sent.
|
|
22
|
+
|
|
23
|
+
- **Contract change: `link_compact` declines read as text.** The notification now says `not done: target busy`, `not done: compaction unavailable on target` or `not done: target not found` where it said `not done: busy`, `not done: unsupported` or `not done: not_found`. Runtime errors and unrecognized reasons still follow `not done: ` unchanged, and a response with no reason still reads `not done: failed`. Only the notification text changes: the wire still carries the codes, and the requester translates them, so answers from targets and hubs on earlier versions read the same way.
|
|
24
|
+
|
|
25
|
+
- **Clearer link messages.** Losing the hub warns `Disconnected from the link hub; reconnecting...`, since a reconnect follows. `/link-disconnect` says `Disconnected from the link; automatic reconnect disabled`, or `Already disconnected; automatic reconnect disabled` when nothing was connected. A `link_send` that cannot be handed on now says why: `target not found` on the hub, or `connection to the link hub is not open` on a client, as in `Failed to send to "<name>": target not found`. A `link_compact` request that cannot be sent says `Failed to request compact on "<name>": connection to the link hub is not open`. The target of a remote compaction is notified `"<name>" asked this terminal to compact`. The `not_delivered` error code is unchanged. `pi-link --status` no longer claims more than its query shows: exit `2` says `No link hub answered on :<port>.`, and exit `1` says `Unexpected response from :<port>/status; not a compatible pi-link hub. If it is one, update pi-link and restart terminals.` Exit codes and stdout are unchanged. When `pi-link <name>` finds no such session in this cwd and starts a new one, it says `No session named "<name>" found in this cwd.`, as `--resolve` does. The `/link-name` description reads `Change link name (without an argument, use the session name)`, and the `link_list` view in the TUI says `1 terminal` or `N terminals` instead of `N terminal(s)`. In the TUI, a delivered batch now reads `⚡ [Link: ...]` without the redundant `[link]` label; messages restored from older sessions that carry a sender keep their `⚡ [<sender>]` label. Other texts say "the link" too: `Joined the link as "<name>" (N online)`, `Not connected to the link`, and the `--link` flag's `Connect to the link on startup`; the `--link-name` flag reads `Set the link name on startup (link identity only; does not affect session)`.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
9
29
|
## 0.5.2 — 2026-09-29
|
|
10
30
|
|
|
11
31
|
### Fixed
|
package/README.md
CHANGED
|
@@ -20,8 +20,8 @@ Questions, ideas? There's a [pi-link thread](https://discord.com/channels/145680
|
|
|
20
20
|
- [Walkthrough](#walkthrough)
|
|
21
21
|
- [LLM Tools](#llm-tools)
|
|
22
22
|
- [Slash Commands](#slash-commands)
|
|
23
|
-
- [CLI: `pi-link`](#cli-pi-link)
|
|
24
23
|
- [Configuration](#configuration)
|
|
24
|
+
- [CLI: `pi-link`](#cli-pi-link)
|
|
25
25
|
- [Troubleshooting](#troubleshooting)
|
|
26
26
|
- [Limitations & Design Decisions](#limitations--design-decisions)
|
|
27
27
|
- [Dependencies](#dependencies)
|
|
@@ -42,7 +42,7 @@ Pi's package installation does not check the host version, so `pi install` succe
|
|
|
42
42
|
|
|
43
43
|
### Install
|
|
44
44
|
|
|
45
|
-
The minimum install
|
|
45
|
+
The minimum install enables all in-Pi tools, commands and startup flags. It also restores the saved link name and connection preference when you resume a session:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
pi install npm:pi-link
|
|
@@ -66,7 +66,7 @@ pi install npm:pi-link && npm i -g pi-link
|
|
|
66
66
|
|
|
67
67
|
Pi installs packages into its own npm root (`~/.pi/agent/npm/`), which is not on `PATH`; `npm i -g pi-link` is what puts the `pi-link` command there.
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
You do not need the shell launcher for communication inside Pi: use `/link-connect` and `/link-name <name>`. The launcher adds shell commands for resuming sessions by name, listing saved sessions, and checking live link status.
|
|
70
70
|
|
|
71
71
|
### Uninstall
|
|
72
72
|
|
|
@@ -91,6 +91,8 @@ Already in a session? Use `/link-connect`. Use `/link` any time to check status,
|
|
|
91
91
|
|
|
92
92
|
## Walkthrough
|
|
93
93
|
|
|
94
|
+
A message shares only its text, not the sender's full conversation. Separate conversations do not isolate files: terminals may share a working tree, and pi-link does not coordinate their writes.
|
|
95
|
+
|
|
94
96
|
Here's a concrete example of two terminals collaborating. Open two terminals, each with its name already set:
|
|
95
97
|
|
|
96
98
|
```bash
|
|
@@ -103,7 +105,7 @@ The first one to start becomes the hub; the second joins it. **In Terminal 1**,
|
|
|
103
105
|
```
|
|
104
106
|
> /link
|
|
105
107
|
⚡ Link: builder (hub) · 2 online
|
|
106
|
-
builder: idle (5s) · 45K/272K (17%)
|
|
108
|
+
builder (you): idle (5s) · 45K/272K (17%)
|
|
107
109
|
cwd: ~/my-project
|
|
108
110
|
researcher: idle (12s) · 80K/272K (29%)
|
|
109
111
|
cwd: ~/my-project
|
|
@@ -136,15 +138,15 @@ With two projects open, you may not want their terminals to see each other. Put
|
|
|
136
138
|
|
|
137
139
|
## LLM Tools
|
|
138
140
|
|
|
139
|
-
|
|
141
|
+
The model has three tools: `link_send` to talk to another terminal, `link_list` to see who is there, and `link_compact` to trim another terminal's context. You can ask for these actions in a normal prompt. The bundled [pi-link-tools skill](skills/pi-link-tools/SKILL.md) provides usage guidance for agents.
|
|
140
142
|
|
|
141
|
-
###
|
|
143
|
+
### What the model can do
|
|
142
144
|
|
|
143
145
|
| Tool | Behavior | Returns |
|
|
144
146
|
| -------------- | ------------------------------------------------------- | ------------------------------------------------------- |
|
|
145
147
|
| `link_send` | Send a message to one other terminal | Reports whether sending started, not whether it arrived |
|
|
146
|
-
| `link_list` | List currently connected terminals
|
|
147
|
-
| `link_compact` | Ask another terminal to compact
|
|
148
|
+
| `link_list` | List currently connected terminals | Terminal list with status, cwd, and context |
|
|
149
|
+
| `link_compact` | Ask another terminal to compact, without waiting | Request ID now; the outcome later as a notification |
|
|
148
150
|
|
|
149
151
|
### `link_send`
|
|
150
152
|
|
|
@@ -165,7 +167,7 @@ Targets are pre-validated against the terminals in your group, so a definite typ
|
|
|
165
167
|
|
|
166
168
|
### `link_list`
|
|
167
169
|
|
|
168
|
-
Lists the connected terminals in your [group](#configuration), with
|
|
170
|
+
Lists the connected terminals in your [group](#configuration), with status, working directory, context usage, and self-identification. Takes no parameters. Your own status and context are read when you run `link_list`; for other terminals, you see their most recently reported values, which may be slightly behind.
|
|
169
171
|
|
|
170
172
|
Each terminal reports its current working directory on connect. `link_list` shows the full absolute path.
|
|
171
173
|
|
|
@@ -178,11 +180,11 @@ Each terminal's status is derived automatically from Pi lifecycle events - agent
|
|
|
178
180
|
| `idle (2m)` | Waiting for user input |
|
|
179
181
|
| `thinking (3s)` | Work Pi has not settled yet — the LLM call, plus any automatic retry or compaction |
|
|
180
182
|
| `tool:bash (12s)` | The first still-active call, not a list |
|
|
181
|
-
| `compacting (8s)` |
|
|
183
|
+
| `compacting (8s)` | Incoming messages held for compaction |
|
|
182
184
|
|
|
183
185
|
Pi can run tools in parallel, so `tool:<name>` names the first still-active call Pi reported — one call that really is still running, not a list of every concurrent one — and it advances only when that call ends, staying `tool:<name>` until the last one does.
|
|
184
186
|
|
|
185
|
-
|
|
187
|
+
Both `/compact` and a `link_compact` request that has started can show `compacting` (one still waiting does not); automatic compaction shows `thinking`. While `compacting` is displayed, incoming messages are held. The state reports that hold, not proof that compaction is still running — see [Inbox](#inbox).
|
|
186
188
|
|
|
187
189
|
Durations are computed at render time from a `since` timestamp - no timer traffic over the wire. Terminals that just joined with no status data yet render as blank, not fake idle.
|
|
188
190
|
|
|
@@ -202,21 +204,57 @@ Connected terminals:
|
|
|
202
204
|
|
|
203
205
|
### `link_compact`
|
|
204
206
|
|
|
205
|
-
|
|
207
|
+
Use this tool to ask another terminal in the same group to compact its context — the same operation as `/compact`: summarize older conversation while keeping recent messages. A summary can omit details. Optional instructions guide what it preserves, but do not guarantee what survives or assign the target a task to perform afterwards.
|
|
208
|
+
|
|
209
|
+
For example, ask the model in the requesting terminal:
|
|
210
|
+
|
|
211
|
+
```text
|
|
212
|
+
Ask researcher to compact its context, preserving pending decisions, and tell me the result.
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The tool first reports that the request was sent. **Sent does not mean accepted or finished.** A separate notification reports the outcome later. In this successful example, `<id>` stands for the request ID; the same ID connects the two messages:
|
|
216
|
+
|
|
217
|
+
```text
|
|
218
|
+
Compact request sent to "researcher" [<id>]; the result will arrive as a link notification.
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Later, on success:
|
|
222
|
+
|
|
223
|
+
```text
|
|
224
|
+
link_compact "researcher" [<id>]: compacted
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
The notification appears as a link message in the requesting terminal and reaches its model: it starts a turn if the model is idle, steers an active run, and waits while that terminal reports `compacting`. pi-link generates it automatically; the target's model does not need to send a reply. The requesting model can continue other work while waiting, and several compaction requests can be outstanding at once.
|
|
228
|
+
|
|
229
|
+
The target does not interrupt its own work. A request that arrives while the target is busy waits there, with no deadline, until Pi reports the session idle and the terminal is not reporting `compacting`; an active run, an automatic retry or compaction, a queued continuation, or another compaction's hold all count as busy. The waiting target checks every 500 ms. Waiting holds nothing back: the target keeps working, and messages sent to it are still delivered. A message already delivered into Pi's work keeps the target busy; one still in the target's inbox batching window (about 200 ms) does not block the start, and is delivered after the compaction instead. Once compaction starts, incoming messages are held until it ends. The target's user sees `"<requester>" asked this terminal to compact` when compaction starts at once, or `"<requester>" asked this terminal to compact; waiting until it is idle` when the request waits.
|
|
230
|
+
|
|
231
|
+
Idle is observed, not reserved. Seeing the target idle and starting its compaction are two steps, so work that Pi or another extension starts between them can still be interrupted by the compaction, as with a request served the moment it arrives. With no deadline, the outcome takes as long as the target stays busy; a target that never becomes idle never answers.
|
|
232
|
+
|
|
233
|
+
The tool's parameters are:
|
|
206
234
|
|
|
207
235
|
| Parameter | Type | Description |
|
|
208
236
|
| -------------- | -------- | ------------------------------------------------------ |
|
|
209
237
|
| `to` | `string` | Target terminal name |
|
|
210
|
-
| `instructions` | `string` | Optional
|
|
238
|
+
| `instructions` | `string` | Optional guidance for the compaction summary |
|
|
239
|
+
|
|
240
|
+
**Results you may see:**
|
|
241
|
+
|
|
242
|
+
- **Failure before sending** — the tool reports the error immediately, and no later notification follows. For example, the requesting terminal may be disconnected or the target absent from its group. A terminal cannot request its own compaction with this tool.
|
|
243
|
+
- **Success** — `compacted`. Compaction finished, but this does not reserve the target: it may be busy again by the next request.
|
|
244
|
+
- **Already requested** — `not done: target already has a compaction request waiting or running`. A target holds one request at a time, from any requester; another one is declined rather than queued, merged or allowed to replace it.
|
|
245
|
+
- **Navigation** — `not done: target navigated its session tree while the request waited`. Completing a `/tree` navigation on the target ends a request that has not started. A running compaction is unaffected, and other session changes, such as new messages or a session rename, end nothing.
|
|
246
|
+
- **Other declines or errors** — `not done: compaction unavailable on target` if the target has no compaction available when the request arrives, or `not done: target not found` if the target cannot be found when the request is routed. The wire carries the codes `already_requested`, `tree_navigated`, `unsupported` and `not_found`; the requester shows them as this text. Pi may report a reason such as `Nothing to compact (session too small)` or `Already compacted`; runtime errors and cancellations also appear after `not done: `.
|
|
247
|
+
- **Earlier target** — `not done: target busy`. A target on an earlier pi-link version declines at once while busy instead of waiting.
|
|
248
|
+
- **Departure** — `left the link before answering; result unknown`. The target may or may not have compacted. A target that disconnects or ends its session drops a request it has not started, so this is also how such a request ends.
|
|
249
|
+
- **Disconnect** — `/link-disconnect` in the requesting terminal reports outstanding requests as `link disconnected before an answer; result unknown, the target may still be compacting`.
|
|
250
|
+
|
|
251
|
+
There is no remote cancel. **An unknown result is not a confirmed failure.** Losing the hub connection without explicitly disconnecting keeps requests outstanding, so an answer arriving after reconnection can still be reported.
|
|
252
|
+
|
|
253
|
+
If the requesting terminal reloads or exits, it loses tracking of outstanding requests and their outcomes will not be reported. A response sent while the hub is down or failing over is not replayed. Renaming the requester leaves responses addressed to its old name. With no deadline, a lost response leaves the request outstanding indefinitely: no notification follows, and neither `/link` nor `link_list` shows its outcome. The same happens when the target is the hub and leaves, because its requesters lose the hub instead of seeing the target leave. A requester leaving does not cancel a request the target holds; the target still compacts once it is free.
|
|
211
254
|
|
|
212
|
-
-
|
|
213
|
-
- **Busy decline** — the target accepts only when Pi reports its session idle **and** no manual compaction holds its delivery gate, so an active run, an automatic retry, an automatic compaction and a queued continuation all decline immediately with `reason: "busy"` without interrupting the work in progress.
|
|
214
|
-
- **The other declines** — a runtime offering no compaction capability declines with `reason: "unsupported"` and is never asked to compact; a session under the runtime's threshold declines with `Compact on "<target>" not done: Nothing to compact (session too small)`; a target that has done nothing since its last compaction declines with `Compact on "<target>" not done: Already compacted`.
|
|
215
|
-
- **Self-target rejection** — calling `link_compact` on yourself returns a `self_target` error (`Cannot compact yourself.`).
|
|
216
|
-
- **Flat 300-second timeout** — the timeout bounds the caller's wait only; nothing aborts the target, so a timed-out call may mean the compaction is still running.
|
|
217
|
-
- Any terminal can request compaction on another **in its group**.
|
|
255
|
+
Versions are not negotiated. A target on an earlier pi-link still declines a busy request with `not done: target busy`. A requester on an earlier pi-link still stops waiting after 300 seconds and ignores a later answer, although a current target may still be waiting and compact afterwards; it shows the new codes as sent, such as `not done: already_requested`.
|
|
218
256
|
|
|
219
|
-
|
|
257
|
+
For the tool-result fields and implementation behind these behaviors, see [Remote compaction](#remote-compaction).
|
|
220
258
|
|
|
221
259
|
---
|
|
222
260
|
|
|
@@ -234,7 +272,7 @@ The mechanics behind these outcomes — what an accepted request runs, the deliv
|
|
|
234
272
|
```
|
|
235
273
|
> /link
|
|
236
274
|
⚡ Link: builder (hub) · 3 online
|
|
237
|
-
builder: idle (12s) · 45K/272K (17%)
|
|
275
|
+
builder (you): idle (12s) · 45K/272K (17%)
|
|
238
276
|
cwd: ~/my-project
|
|
239
277
|
worker-1: thinking (3s) · ?/272K
|
|
240
278
|
cwd: ~/my-project
|
|
@@ -248,10 +286,10 @@ The mechanics behind these outcomes — what an accepted request runs, the deliv
|
|
|
248
286
|
✓ Renamed to "my-session"
|
|
249
287
|
|
|
250
288
|
> /link-disconnect
|
|
251
|
-
✓ Disconnected from link
|
|
289
|
+
✓ Disconnected from the link; automatic reconnect disabled
|
|
252
290
|
|
|
253
291
|
> /link-connect
|
|
254
|
-
✓ Joined link as "
|
|
292
|
+
✓ Joined the link as "my-session" (3 online)
|
|
255
293
|
```
|
|
256
294
|
|
|
257
295
|
With no argument, `/link-name` adopts the Pi session name. `/link-connect` joins an existing hub if one is running; otherwise it starts the hub.
|
|
@@ -262,13 +300,43 @@ See [Configuration](#configuration) for details on `--link`, `/link-connect`, an
|
|
|
262
300
|
|
|
263
301
|
---
|
|
264
302
|
|
|
303
|
+
## Configuration
|
|
304
|
+
|
|
305
|
+
Link is **off by default**. Without `--link`, `--link-name`, or `pi-link`, a fresh session is completely silent — no status bar, no connections, no warnings.
|
|
306
|
+
|
|
307
|
+
**Naming concepts**
|
|
308
|
+
|
|
309
|
+
- **link name** — identity used on the network (visible in `link_list`, `/link`, and messages).
|
|
310
|
+
- **group** — the text after the first `@` in a link name, compared exactly. `archon@pi-link` is in `pi-link`, `a@g@h` is in `g@h`, and a name without `@` (or ending in one, like `a@`) belongs to the single implicit group of plain names. Comparison is **case-sensitive**: `a@Team` and `a@team` are different groups. `link_list`, `link_send`, `link_compact`, `/link` and the footer see only your own group; the hub still routes for every group, and `pi-link --status` shows all of them. Renaming with `/link-name` moves you.
|
|
311
|
+
- **Pi session name** — identity Pi gives the session itself; lives in the session JSONL's latest `session_info` entry.
|
|
312
|
+
- **saved link name** — the link name persisted to the session, restored on resume. Set by `/link-name`, `pi-link <name>`, or `pi --link-name <name>`.
|
|
313
|
+
- **`--link-name` flag vs `/link-name` command** — same concept (the link name) at different times (startup vs mid-session).
|
|
314
|
+
|
|
315
|
+
| What you want | Use |
|
|
316
|
+
| ------------------------------------ | ----------------------- |
|
|
317
|
+
| Resume/create a named session | `pi-link <name>` |
|
|
318
|
+
| Stable link identity, normal Pi flow | `pi --link-name <name>` |
|
|
319
|
+
| Quick try, random name | `pi --link` |
|
|
320
|
+
| Already in a session | `/link-connect` |
|
|
321
|
+
| Disconnect mid-session | `/link-disconnect` |
|
|
322
|
+
|
|
323
|
+
`pi-link <name>` resumes/creates a session AND sets your link identity in one step. `pi --link-name <name>` sets only the link identity, leaving Pi's normal session selection (latest in cwd, or fresh) untouched.
|
|
324
|
+
|
|
325
|
+
**Name normalization:** Link names are normalized — leading/trailing whitespace removed and internal whitespace runs collapsed to a single space. `/link-name build lead` saves and shows as `build lead`.
|
|
326
|
+
|
|
327
|
+
**Name precedence:** `pi --link-name` > `pi-link <name>` > saved `/link-name` > Pi session name > random `t-xxxx`. _(The `pi-link` wrapper itself does not accept `--link-name`; pick one or the other.)_
|
|
328
|
+
|
|
329
|
+
`/link-connect` and `/link-disconnect` save their intent to the session — resume later and the connection state is restored without needing the flag. Explicit user intent takes precedence over `--link`.
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
265
333
|
## CLI: `pi-link`
|
|
266
334
|
|
|
267
335
|
`pi-link` is the optional shell launcher installed with `npm i -g pi-link`. It resolves sessions by name, lists them, and asks the running hub who is connected.
|
|
268
336
|
|
|
269
337
|
### Session Resume
|
|
270
338
|
|
|
271
|
-
Pi's `--session` flag
|
|
339
|
+
Pi's `--session` flag accepts a file path or session ID, not a display name. `pi-link` bridges this — it resolves a session by name and launches Pi directly:
|
|
272
340
|
|
|
273
341
|
```bash
|
|
274
342
|
pi-link worker-1 # resume or create session "worker-1"
|
|
@@ -394,8 +462,8 @@ Everything above is what the hub sends. The CLI validates only the fields its ta
|
|
|
394
462
|
| Code | Meaning | Message |
|
|
395
463
|
| --- | --- | --- |
|
|
396
464
|
| `0` | Something answered with a printable payload | — |
|
|
397
|
-
| `2` | No hub answered | `No link hub
|
|
398
|
-
| `1` | Usage error, or an answer the table cannot print | `
|
|
465
|
+
| `2` | No hub answered | `No link hub answered on :9900.` |
|
|
466
|
+
| `1` | Usage error, or an answer the table cannot print | `Unexpected response from :9900/status; not a compatible pi-link hub. If it is one, update pi-link and restart terminals.` |
|
|
399
467
|
|
|
400
468
|
The two failure messages are deliberately distinct, so a script can tell *no usable answer* from *an answer this CLI cannot read* without parsing anything else. Exit `2` covers both nothing listening and a listener that accepts the request but does not answer within two seconds — every timeout is exit `2`. Exit `1` covers a listener that responds with something the table cannot read — for example a pi-link 0.3.0 hub, which answers plain HTTP with `426 Upgrade Required`, so an out-of-date fleet lands here deterministically rather than looking like an outage.
|
|
401
469
|
|
|
@@ -411,45 +479,15 @@ Finally, `--status` and `--json` belong to the wrapper only until a session name
|
|
|
411
479
|
|
|
412
480
|
---
|
|
413
481
|
|
|
414
|
-
## Configuration
|
|
415
|
-
|
|
416
|
-
Link is **off by default**. Without `--link`, `--link-name`, or `pi-link`, a fresh session is completely silent — no status bar, no connections, no warnings.
|
|
417
|
-
|
|
418
|
-
**Naming concepts**
|
|
419
|
-
|
|
420
|
-
- **link name** — identity used on the network (visible in `link_list`, `/link`, and messages).
|
|
421
|
-
- **group** — the text after the first `@` in a link name, compared exactly. `archon@pi-link` is in `pi-link`, `a@g@h` is in `g@h`, and a name without `@` (or ending in one, like `a@`) belongs to the single implicit group of plain names. Comparison is **case-sensitive**: `a@Team` and `a@team` are different groups. `link_list`, `link_send`, `link_compact`, `/link` and the footer see only your own group; the hub still routes for every group, and `pi-link --status` shows all of them. Renaming with `/link-name` moves you.
|
|
422
|
-
- **Pi session name** — identity Pi gives the session itself; lives in the session JSONL's latest `session_info` entry.
|
|
423
|
-
- **saved link name** — the link name persisted to the session, restored on resume. Set by `/link-name`, `pi-link <name>`, or `pi --link-name <name>`.
|
|
424
|
-
- **`--link-name` flag vs `/link-name` command** — same concept (the link name) at different times (startup vs mid-session).
|
|
425
|
-
|
|
426
|
-
| What you want | Use |
|
|
427
|
-
| ------------------------------------ | ----------------------- |
|
|
428
|
-
| Resume/create a named session | `pi-link <name>` |
|
|
429
|
-
| Stable link identity, normal Pi flow | `pi --link-name <name>` |
|
|
430
|
-
| Quick try, random name | `pi --link` |
|
|
431
|
-
| Already in a session | `/link-connect` |
|
|
432
|
-
| Disconnect mid-session | `/link-disconnect` |
|
|
433
|
-
|
|
434
|
-
`pi-link <name>` resumes/creates a session AND sets your link identity in one step. `pi --link-name <name>` sets only the link identity, leaving Pi's normal session selection (latest in cwd, or fresh) untouched.
|
|
435
|
-
|
|
436
|
-
**Name normalization:** Link names are normalized — leading/trailing whitespace removed and internal whitespace runs collapsed to a single space. `/link-name "build lead"` saves and shows as `build lead`.
|
|
437
|
-
|
|
438
|
-
**Name precedence:** `pi --link-name` > `pi-link <name>` > saved `/link-name` > Pi session name > random `t-xxxx`. _(The `pi-link` wrapper itself does not accept `--link-name`; pick one or the other.)_
|
|
439
|
-
|
|
440
|
-
`/link-connect` and `/link-disconnect` save their intent to the session — resume later and the connection state is restored without needing the flag. Explicit user intent takes precedence over `--link`.
|
|
441
|
-
|
|
442
|
-
---
|
|
443
|
-
|
|
444
482
|
## Troubleshooting
|
|
445
483
|
|
|
446
484
|
### Port 9900 is already in use
|
|
447
485
|
|
|
448
486
|
If another process occupies port 9900, the terminal can't become the hub. It tries to connect as a client first, and that attempt fails within 5 seconds even against a listener that accepts the connection and never speaks WebSocket - so the terminal falls back to trying the hub role and then retries after 2-5 seconds, rather than sitting offline indefinitely. Free the port or modify `DEFAULT_PORT` in `index.ts` - see [Limitations](#limitations--design-decisions).
|
|
449
487
|
|
|
450
|
-
### `link_compact`
|
|
488
|
+
### `link_compact` has no outcome yet
|
|
451
489
|
|
|
452
|
-
A `link_compact` request does not interrupt work the target is still doing,
|
|
490
|
+
A `link_compact` request does not interrupt work the target is still doing: it waits there, with no deadline, until that terminal is idle and not reporting `compacting` — see [`link_compact`](#link_compact) for what counts. A busy target answers late, and one that never becomes idle never answers. `/link` or `link_list` shows what the target is doing, not whether it holds your request. If the response was lost — the hub went down, or the requester renamed or reloaded — no outcome arrives and nothing recovers it. `not done: target busy` comes only from a target on an earlier pi-link version, which declines instead of waiting.
|
|
453
491
|
|
|
454
492
|
### I sent a message but got no reply
|
|
455
493
|
|
|
@@ -457,7 +495,7 @@ A successful send is not a confirmation. On a client it means the message was ha
|
|
|
457
495
|
|
|
458
496
|
### `pi-link --status` reports no hub, or an unsupported one
|
|
459
497
|
|
|
460
|
-
The two messages mean different things. `No link hub
|
|
498
|
+
The two messages mean different things. `No link hub answered on :9900.` (exit `2`) means the query received no complete answer, not that no hub is running. Check the effective port and retry: the link may be down, you may have caught the 2–5 second window while a client promotes itself to hub, or a listener may have stalled. `Unexpected response from :9900/status; not a compatible pi-link hub. If it is one, update pi-link and restart terminals.` (exit `1`) means something answered but the response did not pass the CLI's checks — for example, a pi-link 0.3.0 hub, which predates the endpoint. Updating is not enough on its own: the running terminals keep the old hub alive until they restart. See [`--status`: who is connected right now](#--status-who-is-connected-right-now).
|
|
461
499
|
|
|
462
500
|
### Terminals don't see each other
|
|
463
501
|
|
|
@@ -482,8 +520,9 @@ When the hub exits, a surviving client promotes itself in roughly 2–5 seconds;
|
|
|
482
520
|
| 4 | **No offline backlog** | A definitely absent target is rejected and nothing is stored, so a terminal that reconnects receives no messages it missed while offline. |
|
|
483
521
|
| 5 | **Client rename triggers full reconnect** | Changing a client's name requires a new `register` message, so the client disconnects and reconnects. Hub renames are handled in-place. |
|
|
484
522
|
| 6 | **Single-machine / localhost-only** | Link only binds to `127.0.0.1`; terminals on different machines cannot join. |
|
|
485
|
-
| 7 | **Callbacks are conventional** |
|
|
523
|
+
| 7 | **Callbacks are conventional** | A `link_send` callback is an ordinary message, not a protocol response: no request identifier, and it exists only because the receiver chose to send one. |
|
|
486
524
|
| 8 | **Groups isolate attention, not access** | No auth: any process may pick any group, and `pi-link --status` shows all of them; isolation needs one version everywhere, so upgrade and restart together. |
|
|
525
|
+
| 9 | **Compact outcomes are not durable** | Outcomes are correlated by request ID but tracked only in the requester's memory: a reload or exit loses them, and a lost response is never reported. |
|
|
487
526
|
|
|
488
527
|
---
|
|
489
528
|
|
|
@@ -552,7 +591,7 @@ The wire protocol consists of the following message types, all serialized as JSO
|
|
|
552
591
|
| `terminal_joined` | Hub → All | Broadcast when a terminal joins; may include cwd and context |
|
|
553
592
|
| `terminal_left` | Hub → All | Broadcast when a terminal disconnects |
|
|
554
593
|
| `chat` | Any → Any | Message delivered to the receiver's model: steered into a running agent, or starting a turn if idle |
|
|
555
|
-
| `compact_request` | Any → Any | Request a remote terminal to compact its context;
|
|
594
|
+
| `compact_request` | Any → Any | Request a remote terminal to compact its context; answered by one `compact_response` |
|
|
556
595
|
| `compact_response` | Any → Any | Completion/failure response for a compact_request |
|
|
557
596
|
| `status_update` | Any → Hub → All | Terminal broadcasts agent status change; carries updated context |
|
|
558
597
|
| `error` | Hub → Client | Error notification |
|
|
@@ -613,15 +652,17 @@ Default names are random 4-character hex IDs: `t-a1b2`, `t-c3d4`, etc.
|
|
|
613
652
|
| `role` | `"hub" \| "client" \| "disconnected"` | Current network role |
|
|
614
653
|
| `connectionAttempt` | `record \| null` | The one establishment attempt in flight: its shared promise plus any pending socket/server. Object identity marks the owner |
|
|
615
654
|
| `agentRunning` | `boolean` | Between `agent_start` and `agent_settled` — not `agent_end`; drives status only |
|
|
616
|
-
| `compactRunning` | `boolean` | Whether a remote request holds this terminal's delivery gate
|
|
655
|
+
| `compactRunning` | `boolean` | Whether a running remote request holds this terminal's delivery gate |
|
|
656
|
+
| `remoteCompact` | `object \| null` | The one remote request this terminal holds, waiting or running, until it is answered |
|
|
657
|
+
| `compactWaitTimer` | `Timer \| undefined` | Next check whether the held remote request can start; set exactly while it waits |
|
|
617
658
|
| `localCompacting` | `boolean` | Whether a `manual`-reason compaction has raised the delivery gate |
|
|
618
659
|
| `activeTools` | `Map` | `toolCallId` → `toolName` for calls still running; the first drives status |
|
|
619
660
|
| `stateSince` | `number` | Timestamp of last status change (used for duration display) |
|
|
620
661
|
| `currentCwd` | `string` | Current working directory reported to peers on connect |
|
|
621
|
-
| `inbox` | `array` | Queued incoming messages awaiting delivery
|
|
662
|
+
| `inbox` | `array` | Queued incoming messages and compact outcomes awaiting delivery |
|
|
622
663
|
| `flushTimer` | `Timer \| null` | Pending inbox flush; armed by the first queued message and not moved afterwards |
|
|
623
|
-
| `compactDeadline` | `Timer \| undefined` | Backstop releasing the inbox if
|
|
624
|
-
| `pendingCompactResponses` | `Map` |
|
|
664
|
+
| `compactDeadline` | `Timer \| undefined` | Backstop releasing the inbox if no ending pi-link handles arrives |
|
|
665
|
+
| `pendingCompactResponses` | `Map` | Request ID → requested target, for each compact request still awaiting an outcome; no deadline |
|
|
625
666
|
| `disposed` | `boolean` | Set on shutdown; guards WebSocket callbacks against stale context |
|
|
626
667
|
| `startupConnectTimer` | `Timer \| null` | Deferred startup connect so Pi's startup cycle completes first |
|
|
627
668
|
| `manuallyDisconnected` | `boolean` | Set by `/link-disconnect`; suppresses auto-reconnect |
|
|
@@ -630,15 +671,15 @@ Default names are random 4-character hex IDs: `t-a1b2`, `t-c3d4`, etc.
|
|
|
630
671
|
|
|
631
672
|
`routeMessage()` returns a `boolean` indicating delivery status:
|
|
632
673
|
|
|
633
|
-
- **Hub** - delivery is authoritative. If a chat target is not connected, the hub sends a protocol-level error back to a client sender; a local hub sender already receives the failed delivery result. A `compact_request` to an unknown target gets a synthesized `compact_response` (`ok: false`, `reason: "not_found"`), so the
|
|
674
|
+
- **Hub** - delivery is authoritative. If a chat target is not connected, the hub sends a protocol-level error back to a client sender; a local hub sender already receives the failed delivery result. A `compact_request` to an unknown target gets a synthesized `compact_response` (`ok: false`, `reason: "not_found"`), so the requester is notified promptly instead of never.
|
|
634
675
|
- **Client** - delivery is optimistic (`true` means "sent to hub"). The hub handles routing and errors via the protocol.
|
|
635
676
|
|
|
636
677
|
### Connection Lifecycle
|
|
637
678
|
|
|
638
679
|
Internally, teardown is split into two functions:
|
|
639
680
|
|
|
640
|
-
- **`disconnect()`** - closes sockets, clears connection state,
|
|
641
|
-
- **`cleanup()`** -
|
|
681
|
+
- **`disconnect()`** - closes sockets, clears connection state, reports each outstanding compact request as unknown, and drops unanswered a remote request still waiting here. Used by `/link-disconnect` and called internally by `cleanup()`.
|
|
682
|
+
- **`cleanup()`** - sets `disposed = true`, then calls `disconnect()` and clears `ctx`. Used on `session_shutdown`.
|
|
642
683
|
|
|
643
684
|
Three helpers protect WebSocket callbacks from stale extension context:
|
|
644
685
|
|
|
@@ -664,8 +705,8 @@ The extension hooks into Pi's agent lifecycle events:
|
|
|
664
705
|
- **`session_before_compact`** → For a manual compaction, raises the gate that holds inbox delivery. Automatic (threshold/overflow) compaction is left alone: it runs inside the agent run, where Pi already queues and drains steered messages itself.
|
|
665
706
|
- **`session_compact`** → Clears the local compaction gate and force-pushes a `status_update` so peers see the new (post-compaction) context usage immediately.
|
|
666
707
|
- **`model_select`** → Force-pushes a `status_update`: the context window, and so the usage percentage, belongs to the model.
|
|
667
|
-
- **`session_tree`** →
|
|
668
|
-
- **`session_shutdown`** → Full cleanup via `cleanup()`: closes all sockets,
|
|
708
|
+
- **`session_tree`** → Ends a remote request still waiting here, answering `tree_navigated`, and force-pushes a `status_update`: context usage belongs to the active branch.
|
|
709
|
+
- **`session_shutdown`** → Full cleanup via `cleanup()`: closes all sockets, ends tracking of outstanding compact requests, drops a remote request still waiting here, drops the inbox (their outcomes included) and its flush timer so no turn is started, and disposes the extension.
|
|
669
710
|
|
|
670
711
|
The five agent and tool handlers — `agent_start`, `agent_end`, `agent_settled` (when Pi still reports the session idle), `tool_execution_start` and `tool_execution_end` — each recompute the status and hand it to `pushStatus()`, which publishes only when the display identity changed since the last publish: a second concurrent tool starting behind the one already shown is silent, and so is a mutation that leaves a raised compaction gate on display. The session handlers keep the separate paths described in their own bullets, and `session_shutdown` cleans up rather than pushing. `disconnect()` is the only thing that clears the stored baseline, and `pushStatus()` returns immediately while the terminal is disconnected, so handler events in that interval restore nothing; once connected again, either a client's forced `welcome` push or the first of those ordinary handler events restores it. `session_compact`, `model_select` and `session_tree` always push, whether the identity changed or not — including while a remote request's gate still holds the displayed identity.
|
|
671
712
|
|
|
@@ -673,7 +714,7 @@ Status updates are push-based: each terminal broadcasts changes to the hub, whic
|
|
|
673
714
|
|
|
674
715
|
### Inbox
|
|
675
716
|
|
|
676
|
-
An arriving `chat` message goes into a local inbox rather than calling `pi.sendMessage()` immediately, so that arrivals close together can be coalesced into a single delivery.
|
|
717
|
+
An arriving `chat` message goes into a local inbox rather than calling `pi.sendMessage()` immediately, so that arrivals close together can be coalesced into a single delivery. The outcome of a `link_compact` this terminal requested takes the same path: one line per resolved request, `link_compact "<name>" [<id>]: <outcome>`, naming the requested target and its request ID. It counts toward `N` in the `[Link: N message(s) received]` batch but has no `From` header: it is generated by pi-link, not a conversational reply from the target.
|
|
677
718
|
|
|
678
719
|
The flush pipeline:
|
|
679
720
|
|
|
@@ -683,28 +724,31 @@ The flush pipeline:
|
|
|
683
724
|
4. **Deliver** - one `pi.sendMessage()` call with a `[Link: N message(s) received]` block. Pi reads the receiver's state at this point — not at send time — to decide whether the batch steers a running agent or starts a turn.
|
|
684
725
|
5. **Drain** - if the inbox still has items, reschedule.
|
|
685
726
|
|
|
686
|
-
Delivery is held while a `manual`-reason compaction holds this terminal's gate, because a message delivered mid-compaction would be reasoned about against context that is being rebuilt. Two flags gate it: `compactRunning` for a compaction serving a remote `link_compact`, and `localCompacting`, set from `session_before_compact`; a remote request raises `compactRunning` first and adds `localCompacting` only if its compaction reaches that hook, since Pi reports its own reason as `manual` either way. Automatic (threshold/overflow) compaction is deliberately **not** gated: it runs inside the agent run, where Pi already queues steered messages and drains them afterwards. Because a gated flush does not reschedule, the release path is load-bearing: `releaseInbox()` runs after either flag clears and schedules a flush only when neither gate remains, and `compactDeadline` is the backstop for a `localCompacting` left raised, since failure
|
|
727
|
+
Delivery is held while a `manual`-reason compaction holds this terminal's gate, because a message delivered mid-compaction would be reasoned about against context that is being rebuilt. Two flags gate it: `compactRunning` for a compaction serving a remote `link_compact`, and `localCompacting`, set from `session_before_compact`; a remote request raises `compactRunning` first and adds `localCompacting` only if its compaction reaches that hook, since Pi reports its own reason as `manual` either way. Automatic (threshold/overflow) compaction is deliberately **not** gated: it runs inside the agent run, where Pi already queues steered messages and drains them afterwards. Because a gated flush does not reschedule, the release path is load-bearing: `releaseInbox()` runs after either flag clears and schedules a flush only when neither gate remains, and `compactDeadline` is the backstop for a `localCompacting` left raised, since pi-link does not handle Pi's failure event, `session_compact_failed`, and so no ending event it listens to clears the gate after a failure.
|
|
687
728
|
|
|
688
|
-
| Constant
|
|
689
|
-
|
|
|
690
|
-
| `FLUSH_DELAY_MS`
|
|
691
|
-
| `BATCH_MAX_ITEMS`
|
|
692
|
-
| `BATCH_MAX_CHARS`
|
|
693
|
-
| `COMPACT_TIMEOUT_MS`
|
|
729
|
+
| Constant | Value | Purpose |
|
|
730
|
+
| ----------------------- | ------- | ------------------------------------------------------------- |
|
|
731
|
+
| `FLUSH_DELAY_MS` | 200 | Batching window, from the first queued message |
|
|
732
|
+
| `BATCH_MAX_ITEMS` | 20 | Max messages per batch |
|
|
733
|
+
| `BATCH_MAX_CHARS` | 16 000 | Soft cap on batch text size (~4K tokens) |
|
|
734
|
+
| `COMPACT_TIMEOUT_MS` | 300 000 | Backstop releasing a manual compaction's gate |
|
|
735
|
+
| `COMPACT_WAIT_CHECK_MS` | 500 | How often a waiting remote request checks whether to start |
|
|
694
736
|
|
|
695
737
|
### Remote compaction
|
|
696
738
|
|
|
697
|
-
|
|
739
|
+
The tool returns the request ID in `details.id` as well as in its sent text. Failures before sending return immediately without a later outcome: `not_connected`, `self_target` (`Cannot compact yourself.`), `not_found` for a target absent from the requester's group list, `aborted` for an already-aborted call, or `not_delivered` if routing could not hand the request on. These tool-result errors are distinct from a later `not done: <reason>` notification.
|
|
740
|
+
|
|
741
|
+
A `compact_request` is answered at once with `unsupported` if the target has no compaction capability, and with `already_requested` if another request holds the target's one slot. Otherwise it takes that slot, `remoteCompact`, and starts at once if Pi reports the session idle and no compaction gate stands. If not, a single `setTimeout` checks again every `COMPACT_WAIT_CHECK_MS` while it waits; there is no deadline, and waiting raises no gate. Polling is deliberate: Pi emits `session_compact` before it clears its own compacting state, and a gate left by a failed compaction is released by its backstop or the next run, so no event marks every moment the target becomes free. Each check reads local state only. A request is served by calling `ctx.compact()` — the same operation as `/compact`. The requester is answered with a `compact_response` carrying its request ID when the runtime reports a result through `onComplete`/`onError`, once; a failure is not retried. A waiting request is dropped unanswered by `disconnect()` and teardown, and answered with `tree_navigated` after a completed tree navigation; a running one keeps its slot until its compaction ends. Each call targets one terminal, independent requests can be outstanding concurrently, and link participants are cooperating peers: nothing here is privileged.
|
|
698
742
|
|
|
699
743
|
Serving a request raises the inbox gates in order, and not always both. `compactRunning` is set synchronously before `ctx.compact()` is called; `localCompacting` rises only if execution reaches the manual `session_before_compact` hook, so a runtime that refuses early — nothing to compact, already compacted, or another preparation failure — returns through `onError` without the local gate ever going up. When the compaction does succeed, `session_compact` clears the local gate first, and the later `finish()` clears `compactRunning` and calls `releaseInbox()`, which is what drains the inbox.
|
|
700
744
|
|
|
701
|
-
What a failure hides is the ending, not the failure itself. A remote compaction reports its error through `onError`, which runs `finish()` and answers the
|
|
745
|
+
What a failure hides is the ending, not the failure itself. A remote compaction reports its error through `onError`, which runs `finish()` and answers the requester, so `compactRunning` always comes back down. But `session_compact` is a success-only ending and pi-link does not listen to Pi's `session_compact_failed`, so a `localCompacting` already raised by the hook stays up until the target's next agent run (`agent_start`), a later successful compaction, or the `compactDeadline` backstop 300 seconds after that gate rose. A human `/compact` has no result callback at all, so those three are its only release paths.
|
|
702
746
|
|
|
703
|
-
|
|
747
|
+
The 300 seconds of `COMPACT_TIMEOUT_MS` bound only that gate backstop; the requester has no deadline. Aborting the call before the request goes out leaves the target untouched; once it is sent, the request belongs to the extension rather than the call, so a later abort changes nothing — there is no remote cancel, and the target's compaction, once started, runs to its own end.
|
|
704
748
|
|
|
705
749
|
### Rendering
|
|
706
750
|
|
|
707
|
-
Delivered link batches render with a styled
|
|
751
|
+
Delivered link batches render with a styled `⚡` prefix using the theme's accent color, followed by the batch's own `[Link: N message(s) received]` header; a message restored from an older session that carries a sender keeps it as `⚡ [<sender>]`. In batches, sender attribution lives in the `From "name":` blocks inside the message body, not in the prefix, and a `link_compact` outcome line has no sender. The link status text in Pi's footer uses `theme.fg("dim", ...)` to match Pi's standard footer styling.
|
|
708
752
|
|
|
709
753
|
Deliveries sit in the same background panel Pi draws around extension messages by default, indented by your configured output padding, so link messages line up with the rest of the transcript instead of standing outside it.
|
|
710
754
|
|
package/bin/pi-link.mjs
CHANGED
|
@@ -610,13 +610,15 @@ function formatTerminalStatus(entry) {
|
|
|
610
610
|
return `${entry.status} (${formatAge(entry.sinceSeconds)})`;
|
|
611
611
|
}
|
|
612
612
|
|
|
613
|
-
function failUnsupported() {
|
|
614
|
-
console.error(
|
|
613
|
+
function failUnsupported(port) {
|
|
614
|
+
console.error(
|
|
615
|
+
`Unexpected response from :${port}/status; not a compatible pi-link hub. If it is one, update pi-link and restart terminals.`,
|
|
616
|
+
);
|
|
615
617
|
process.exit(1);
|
|
616
618
|
}
|
|
617
619
|
|
|
618
620
|
function failNoHub(port) {
|
|
619
|
-
console.error(`No link hub
|
|
621
|
+
console.error(`No link hub answered on :${port}.`);
|
|
620
622
|
process.exit(2);
|
|
621
623
|
}
|
|
622
624
|
|
|
@@ -673,7 +675,7 @@ async function runStatus(state) {
|
|
|
673
675
|
failNoHub(port);
|
|
674
676
|
}
|
|
675
677
|
|
|
676
|
-
if (!response.ok) failUnsupported();
|
|
678
|
+
if (!response.ok) failUnsupported(port);
|
|
677
679
|
|
|
678
680
|
let body;
|
|
679
681
|
try {
|
|
@@ -682,16 +684,16 @@ async function runStatus(state) {
|
|
|
682
684
|
// A listener that sends headers and then stalls is still an unanswered
|
|
683
685
|
// query, not an old hub: every timeout reports as "no hub".
|
|
684
686
|
if (deadline.aborted) failNoHub(port);
|
|
685
|
-
failUnsupported();
|
|
687
|
+
failUnsupported(port);
|
|
686
688
|
}
|
|
687
689
|
|
|
688
690
|
let payload;
|
|
689
691
|
try {
|
|
690
692
|
payload = JSON.parse(body);
|
|
691
693
|
} catch {
|
|
692
|
-
failUnsupported();
|
|
694
|
+
failUnsupported(port);
|
|
693
695
|
}
|
|
694
|
-
if (!isStatusPayload(payload)) failUnsupported();
|
|
696
|
+
if (!isStatusPayload(payload)) failUnsupported(port);
|
|
695
697
|
|
|
696
698
|
if (state.json) {
|
|
697
699
|
process.stdout.write(body);
|
|
@@ -741,7 +743,7 @@ async function runLauncher(state) {
|
|
|
741
743
|
piArgs.push("--session", matches[0].path);
|
|
742
744
|
} else {
|
|
743
745
|
if (!state.global) {
|
|
744
|
-
console.error(`No "${name}" found in this cwd. Use --global to search other cwds.`);
|
|
746
|
+
console.error(`No session named "${name}" found in this cwd. Use --global to search other cwds.`);
|
|
745
747
|
}
|
|
746
748
|
console.error("Starting new session.");
|
|
747
749
|
}
|