@kdonev/termscape 0.1.8 → 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 +32 -1
- package/dist/agents/profiles.d.ts +10 -0
- package/dist/agents/profiles.d.ts.map +1 -1
- package/dist/agents/profiles.js +12 -0
- package/dist/agents/profiles.js.map +1 -1
- package/dist/agents/router.d.ts +3 -1
- package/dist/agents/router.d.ts.map +1 -1
- package/dist/agents/router.js +11 -3
- package/dist/agents/router.js.map +1 -1
- package/dist/agents/wiring.d.ts.map +1 -1
- package/dist/agents/wiring.js +16 -1
- package/dist/agents/wiring.js.map +1 -1
- package/dist/cli-args.d.ts +10 -0
- package/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +2 -0
- package/dist/cli-args.js.map +1 -1
- package/dist/cli.js +15 -258
- package/dist/cli.js.map +1 -1
- package/dist/debug.d.ts.map +1 -1
- package/dist/debug.js +7 -0
- package/dist/debug.js.map +1 -1
- package/dist/hub.d.ts +40 -1
- package/dist/hub.d.ts.map +1 -1
- package/dist/hub.js +163 -10
- package/dist/hub.js.map +1 -1
- package/dist/hub.tgz +0 -0
- package/dist/main.d.ts +7 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +343 -0
- package/dist/main.js.map +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +5 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/protocol/domain.d.ts +35 -0
- package/dist/protocol/domain.d.ts.map +1 -1
- package/dist/protocol/domain.js +31 -2
- package/dist/protocol/domain.js.map +1 -1
- package/dist/protocol/index.d.ts +1 -0
- package/dist/protocol/index.d.ts.map +1 -1
- package/dist/protocol/index.js +1 -0
- package/dist/protocol/index.js.map +1 -1
- package/dist/protocol/peer.d.ts +12 -0
- package/dist/protocol/peer.d.ts.map +1 -1
- package/dist/protocol/peer.js +30 -1
- package/dist/protocol/peer.js.map +1 -1
- package/dist/protocol/version.d.ts +12 -0
- package/dist/protocol/version.d.ts.map +1 -0
- package/dist/protocol/version.js +37 -0
- package/dist/protocol/version.js.map +1 -0
- package/dist/protocol/ws.d.ts +67 -1
- package/dist/protocol/ws.d.ts.map +1 -1
- package/dist/protocol/ws.js +11 -1
- package/dist/protocol/ws.js.map +1 -1
- package/dist/remote/enroll.d.ts.map +1 -1
- package/dist/remote/enroll.js +22 -1
- package/dist/remote/enroll.js.map +1 -1
- package/dist/remote/join.d.ts.map +1 -1
- package/dist/remote/join.js +10 -1
- package/dist/remote/join.js.map +1 -1
- package/dist/remote/peer-serve.d.ts.map +1 -1
- package/dist/remote/peer-serve.js +14 -0
- package/dist/remote/peer-serve.js.map +1 -1
- package/dist/remote/peer.d.ts +7 -0
- package/dist/remote/peer.d.ts.map +1 -1
- package/dist/remote/peer.js +25 -1
- package/dist/remote/peer.js.map +1 -1
- package/dist/remote/registry.d.ts +24 -2
- package/dist/remote/registry.d.ts.map +1 -1
- package/dist/remote/registry.js +76 -5
- package/dist/remote/registry.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +16 -3
- package/dist/server.js.map +1 -1
- package/dist/session/manager.d.ts +5 -0
- package/dist/session/manager.d.ts.map +1 -1
- package/dist/session/manager.js +7 -2
- package/dist/session/manager.js.map +1 -1
- package/dist/session/pty.d.ts +17 -2
- package/dist/session/pty.d.ts.map +1 -1
- package/dist/session/pty.js +69 -9
- package/dist/session/pty.js.map +1 -1
- package/dist/supervisor.d.ts +44 -0
- package/dist/supervisor.d.ts.map +1 -0
- package/dist/supervisor.js +125 -0
- package/dist/supervisor.js.map +1 -0
- package/dist/update/host.d.ts +19 -0
- package/dist/update/host.d.ts.map +1 -0
- package/dist/update/host.js +56 -0
- package/dist/update/host.js.map +1 -0
- package/dist/update/install.d.ts +38 -0
- package/dist/update/install.d.ts.map +1 -0
- package/dist/update/install.js +81 -0
- package/dist/update/install.js.map +1 -0
- package/dist/update/post-install.d.ts +2 -0
- package/dist/update/post-install.d.ts.map +1 -0
- package/dist/update/post-install.js +39 -0
- package/dist/update/post-install.js.map +1 -0
- package/dist/update/restart.d.ts +31 -0
- package/dist/update/restart.d.ts.map +1 -0
- package/dist/update/restart.js +65 -0
- package/dist/update/restart.js.map +1 -0
- package/dist/update/resume.d.ts +5 -0
- package/dist/update/resume.d.ts.map +1 -0
- package/dist/update/resume.js +47 -0
- package/dist/update/resume.js.map +1 -0
- package/dist/update/updater.d.ts +57 -0
- package/dist/update/updater.d.ts.map +1 -0
- package/dist/update/updater.js +190 -0
- package/dist/update/updater.js.map +1 -0
- package/package.json +1 -1
- package/web/assets/{index-i7fXLKF0.js → index-BoqmaIyh.js} +9 -9
- package/web/assets/index-Czi_vAR-.css +1 -0
- package/web/index.html +2 -2
- package/web/assets/index-gfbbNQK4.css +0 -1
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE7D;;;;;;GAMG;AACH,KAAK,UAAU,GAAG;IAChB,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;QACrC,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QACzB,OAAO;IACT,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC9B,CAAC;AAED,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAClB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/debug.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAmCA;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,qEAAqE;AACrE,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAG1D;AAED,sEAAsE;AACtE,wBAAgB,WAAW,IAAI,MAAM,EAAE,CAGtC"}
|
package/dist/debug.js
CHANGED
|
@@ -11,6 +11,13 @@
|
|
|
11
11
|
* output only the mode changes in a program's output — what it asked the
|
|
12
12
|
* terminal for, which is what decides whose job a wheel is
|
|
13
13
|
* attach attach, detach, resize, and what a replayed snapshot restores
|
|
14
|
+
* deliver messages and first instructions on their way into an agent: the
|
|
15
|
+
* route taken, the wait for a CLI to be ready, the hooks it reports,
|
|
16
|
+
* and every Enter - the first, and any sent again, and why not
|
|
17
|
+
*
|
|
18
|
+
* For a message that sits unsent in a composer, `deliver` on the machine that
|
|
19
|
+
* owns the agent is the one that matters: that hub types it, and that hub
|
|
20
|
+
* decides whether to press Enter again.
|
|
14
21
|
*
|
|
15
22
|
* A remote session is two hubs, and each logs only its own half. To see a
|
|
16
23
|
* wheel all the way to the program, set this on the canvas machine *and* on
|
package/dist/debug.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug.js","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"debug.js","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,MAAM,GAAG,IAAI,GAAG,CACpB,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC;KAChC,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;KAClC,MAAM,CAAC,OAAO,CAAC,CACnB,CAAC;AAEF,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAEvE;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,OAAO,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,KAAK,CAAC,KAAa,EAAE,OAAe;IAClD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO;IAC5B,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,WAAW;IACzB,IAAI,GAAG;QAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IACxB,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;AACrB,CAAC"}
|
package/dist/hub.d.ts
CHANGED
|
@@ -12,12 +12,13 @@ import { SessionManager } from './session/manager.js';
|
|
|
12
12
|
import type { AgentApi } from './mcp/server.js';
|
|
13
13
|
import { PeerRegistry } from './remote/registry.js';
|
|
14
14
|
import { type Uplink } from './remote/peer-serve.js';
|
|
15
|
+
import type { Updater } from './update/updater.js';
|
|
15
16
|
/** One template as list_templates shows it: described, with its env named but not given. */
|
|
16
17
|
type TemplateView = Omit<AgentTemplateInfo, 'env' | 'error'> & {
|
|
17
18
|
envNames: string[];
|
|
18
19
|
error: string | null;
|
|
19
20
|
};
|
|
20
|
-
export declare const HUB_VERSION = "0.
|
|
21
|
+
export declare const HUB_VERSION = "0.2.0";
|
|
21
22
|
/** How many agents one workspace may hold, bounding runaway recursive spawns. */
|
|
22
23
|
export declare const DEFAULT_SPAWN_CAP = 12;
|
|
23
24
|
export interface HubOptions {
|
|
@@ -48,6 +49,12 @@ export declare class Hub extends EventEmitter implements AgentApi {
|
|
|
48
49
|
readonly sessions: SessionManager;
|
|
49
50
|
readonly router: MessageRouter;
|
|
50
51
|
readonly peers: PeerRegistry;
|
|
52
|
+
/**
|
|
53
|
+
* Newer releases and installing them. Set by the command line once it knows
|
|
54
|
+
* how this hub was installed; null in tests and on a joined machine, which
|
|
55
|
+
* takes its canvas's build instead.
|
|
56
|
+
*/
|
|
57
|
+
updater: Updater | null;
|
|
51
58
|
/**
|
|
52
59
|
* The link back to the canvas this hub was attached to, if it was. Null on a
|
|
53
60
|
* hub that owns a canvas — which is what makes the difference between the
|
|
@@ -108,6 +115,13 @@ export declare class Hub extends EventEmitter implements AgentApi {
|
|
|
108
115
|
private everyAgent;
|
|
109
116
|
/** One address's lineage, from the same view, or null for one nobody here knows. */
|
|
110
117
|
private lineageOf;
|
|
118
|
+
/**
|
|
119
|
+
* Whether the window at `address` is a shell rather than an agent, as far
|
|
120
|
+
* as this hub can tell. Profiles are judged against this hub's own
|
|
121
|
+
* registry, which holds every built-in one; a custom profile only another
|
|
122
|
+
* machine declares reads as an agent, which is what it was before.
|
|
123
|
+
*/
|
|
124
|
+
private isShellAt;
|
|
111
125
|
/**
|
|
112
126
|
* Refuse to act on an address the viewer may not see, in exactly the words
|
|
113
127
|
* used for one that does not exist: a hidden agent must not be discoverable
|
|
@@ -297,6 +311,15 @@ export declare class Hub extends EventEmitter implements AgentApi {
|
|
|
297
311
|
* fails on a permission error rather than replacing the files.
|
|
298
312
|
*/
|
|
299
313
|
removeHost(hostId: string): Promise<void>;
|
|
314
|
+
/**
|
|
315
|
+
* Bring a host to this hub's version.
|
|
316
|
+
*
|
|
317
|
+
* An ssh host is redeployed: its hub is stopped first, because a deploy
|
|
318
|
+
* reuses a hub it finds running, and then `connectHost` finds the version
|
|
319
|
+
* wrong and installs this one. An enrolled host is told to fetch this hub's
|
|
320
|
+
* build and restart itself; it comes back on its own, at the new version.
|
|
321
|
+
*/
|
|
322
|
+
upgradeHost(hostId: string): Promise<void>;
|
|
300
323
|
/**
|
|
301
324
|
* Deploy the hub to a host if needed, tunnel to it, and join it to the
|
|
302
325
|
* directory. The peer token is minted per connection and never written to
|
|
@@ -399,6 +422,17 @@ export declare class Hub extends EventEmitter implements AgentApi {
|
|
|
399
422
|
* did at start.
|
|
400
423
|
*/
|
|
401
424
|
clearSession(sessionId: string): Promise<Session>;
|
|
425
|
+
/**
|
|
426
|
+
* Write down which agents on this machine are running, ahead of a restart
|
|
427
|
+
* for an update, so the hub that comes up next can resume them.
|
|
428
|
+
*/
|
|
429
|
+
rememberRunning(): void;
|
|
430
|
+
/**
|
|
431
|
+
* Resume what the hub before this one was running when it went down for an
|
|
432
|
+
* update. One agent that will not come back is reported and skipped, not a
|
|
433
|
+
* reason to leave the others stopped.
|
|
434
|
+
*/
|
|
435
|
+
resumeRemembered(): Promise<string[]>;
|
|
402
436
|
resumeWorkspace(workspaceId: string): Promise<Session[]>;
|
|
403
437
|
moveWindow(sessionId: string, rect: WindowRect): void;
|
|
404
438
|
setViewport(v: Viewport): void;
|
|
@@ -564,6 +598,11 @@ export declare class Hub extends EventEmitter implements AgentApi {
|
|
|
564
598
|
delivered: boolean;
|
|
565
599
|
to: string;
|
|
566
600
|
deliveredAt: number | null;
|
|
601
|
+
} | {
|
|
602
|
+
note: string;
|
|
603
|
+
delivered: boolean;
|
|
604
|
+
to: string;
|
|
605
|
+
deliveredAt: number | null;
|
|
567
606
|
}>;
|
|
568
607
|
/**
|
|
569
608
|
* Tell every attached machine who is on the canvas.
|
package/dist/hub.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../src/hub.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAOL,KAAK,iBAAiB,EACtB,KAAK,QAAQ,EAEb,KAAK,OAAO,EACZ,KAAK,IAAI,EACT,KAAK,IAAI,EAGT,KAAK,OAAO,EACZ,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAU,KAAK,EAAE,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,
|
|
1
|
+
{"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../src/hub.ts"],"names":[],"mappings":"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"}
|
package/dist/hub.js
CHANGED
|
@@ -4,7 +4,7 @@ import { readFileSync } from 'node:fs';
|
|
|
4
4
|
import { encodeInjection, INJECT_SUBMIT, MAX_PENDING_PROPOSALS, parseAddress, slugify, } from './protocol/index.js';
|
|
5
5
|
import { openDb } from './db/index.js';
|
|
6
6
|
import { Store } from './db/store.js';
|
|
7
|
-
import { briefMode, ProfileRegistry } from './agents/profiles.js';
|
|
7
|
+
import { briefMode, isPlainTerminal, ProfileRegistry } from './agents/profiles.js';
|
|
8
8
|
import { AgentDetector } from './agents/detect.js';
|
|
9
9
|
import { TemplateRegistry, overlayCanvasTemplates, validate as validateTemplate, } from './agents/templates.js';
|
|
10
10
|
import { TokenRegistry } from './agents/tokens.js';
|
|
@@ -18,9 +18,11 @@ import { PeerRegistry } from './remote/registry.js';
|
|
|
18
18
|
import { SPAWN_RELAY_TIMEOUT_MS } from './remote/peer-serve.js';
|
|
19
19
|
import { deploy } from './remote/deployer.js';
|
|
20
20
|
import { hubTarballPath } from './remote/tarball.js';
|
|
21
|
+
import { debug } from './debug.js';
|
|
21
22
|
import { paths } from './paths.js';
|
|
22
23
|
import { checkFolder, folderName } from './folders.js';
|
|
23
|
-
|
|
24
|
+
import { saveResumeList, takeResumeList } from './update/resume.js';
|
|
25
|
+
export const HUB_VERSION = '0.2.0';
|
|
24
26
|
/**
|
|
25
27
|
* How long changes are pooled before every attached machine is told the
|
|
26
28
|
* canvas has moved on. A status change is a session change, and agents change
|
|
@@ -89,6 +91,12 @@ export class Hub extends EventEmitter {
|
|
|
89
91
|
sessions;
|
|
90
92
|
router;
|
|
91
93
|
peers;
|
|
94
|
+
/**
|
|
95
|
+
* Newer releases and installing them. Set by the command line once it knows
|
|
96
|
+
* how this hub was installed; null in tests and on a joined machine, which
|
|
97
|
+
* takes its canvas's build instead.
|
|
98
|
+
*/
|
|
99
|
+
updater = null;
|
|
92
100
|
/**
|
|
93
101
|
* The link back to the canvas this hub was attached to, if it was. Null on a
|
|
94
102
|
* hub that owns a canvas — which is what makes the difference between the
|
|
@@ -156,7 +164,7 @@ export class Hub extends EventEmitter {
|
|
|
156
164
|
this.sessions.visiblePeers = (s) => this.everyAgent()
|
|
157
165
|
.filter((o) => o.address !== s.address && canSee(s, o))
|
|
158
166
|
.map((o) => o.address);
|
|
159
|
-
this.peers.on('host', (h) => this.emit('host', h));
|
|
167
|
+
this.peers.on('host', (h) => this.emit('host', this.peers.decorate(h)));
|
|
160
168
|
this.peers.on('peerAgents', (hostId, found) => this.emit('hostAgents', hostId, found));
|
|
161
169
|
this.peers.on('peerSession', (s) => this.emit('session', s));
|
|
162
170
|
this.peers.on('peerSessionRemoved', (addr) => this.emit('removed', addr, addr));
|
|
@@ -274,6 +282,19 @@ export class Hub extends EventEmitter {
|
|
|
274
282
|
this.uplink?.agents().find((a) => a.address === address);
|
|
275
283
|
return found ? { address, parentAddress: found.parentAddress ?? null } : null;
|
|
276
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* Whether the window at `address` is a shell rather than an agent, as far
|
|
287
|
+
* as this hub can tell. Profiles are judged against this hub's own
|
|
288
|
+
* registry, which holds every built-in one; a custom profile only another
|
|
289
|
+
* machine declares reads as an agent, which is what it was before.
|
|
290
|
+
*/
|
|
291
|
+
isShellAt(address) {
|
|
292
|
+
const profile = this.sessions.getByAddress(address)?.profile ??
|
|
293
|
+
this.peers.find(address)?.session.profile ??
|
|
294
|
+
this.uplink?.agents().find((a) => a.address === address)?.profile;
|
|
295
|
+
const p = profile ? this.profiles.get(profile) : null;
|
|
296
|
+
return p ? isPlainTerminal(p) : false;
|
|
297
|
+
}
|
|
277
298
|
/**
|
|
278
299
|
* Refuse to act on an address the viewer may not see, in exactly the words
|
|
279
300
|
* used for one that does not exist: a hidden agent must not be discoverable
|
|
@@ -731,6 +752,30 @@ export class Hub extends EventEmitter {
|
|
|
731
752
|
this.store.removeHost(hostId);
|
|
732
753
|
this.emit('hostRemoved', hostId);
|
|
733
754
|
}
|
|
755
|
+
/**
|
|
756
|
+
* Bring a host to this hub's version.
|
|
757
|
+
*
|
|
758
|
+
* An ssh host is redeployed: its hub is stopped first, because a deploy
|
|
759
|
+
* reuses a hub it finds running, and then `connectHost` finds the version
|
|
760
|
+
* wrong and installs this one. An enrolled host is told to fetch this hub's
|
|
761
|
+
* build and restart itself; it comes back on its own, at the new version.
|
|
762
|
+
*/
|
|
763
|
+
async upgradeHost(hostId) {
|
|
764
|
+
const host = this.store.listHosts().find((h) => h.id === hostId);
|
|
765
|
+
if (!host)
|
|
766
|
+
throw new Error(`unknown host ${hostId}`);
|
|
767
|
+
if (host.kind === 'ssh') {
|
|
768
|
+
// Asked to remember its running agents on the way down: the hub the
|
|
769
|
+
// redeploy starts resumes them.
|
|
770
|
+
await this.peers.requestShutdown(hostId, { resume: true }).catch(() => false);
|
|
771
|
+
this.peers.remove(hostId);
|
|
772
|
+
await this.tunnels.get(hostId)?.dispose().catch(() => { });
|
|
773
|
+
this.tunnels.delete(hostId);
|
|
774
|
+
await this.connectHost(hostId);
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
await this.peers.requestUpdate(hostId);
|
|
778
|
+
}
|
|
734
779
|
/**
|
|
735
780
|
* Deploy the hub to a host if needed, tunnel to it, and join it to the
|
|
736
781
|
* directory. The peer token is minted per connection and never written to
|
|
@@ -951,6 +996,38 @@ export class Hub extends EventEmitter {
|
|
|
951
996
|
this.noteOpening(session.id, opening || null);
|
|
952
997
|
return session;
|
|
953
998
|
}
|
|
999
|
+
/**
|
|
1000
|
+
* Write down which agents on this machine are running, ahead of a restart
|
|
1001
|
+
* for an update, so the hub that comes up next can resume them.
|
|
1002
|
+
*/
|
|
1003
|
+
rememberRunning() {
|
|
1004
|
+
const running = this.sessions
|
|
1005
|
+
.list()
|
|
1006
|
+
.filter((s) => s.state === 'running' || s.state === 'starting')
|
|
1007
|
+
.map((s) => s.id);
|
|
1008
|
+
saveResumeList(running);
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Resume what the hub before this one was running when it went down for an
|
|
1012
|
+
* update. One agent that will not come back is reported and skipped, not a
|
|
1013
|
+
* reason to leave the others stopped.
|
|
1014
|
+
*/
|
|
1015
|
+
async resumeRemembered() {
|
|
1016
|
+
const resumed = [];
|
|
1017
|
+
for (const id of takeResumeList()) {
|
|
1018
|
+
const s = this.sessions.get(id);
|
|
1019
|
+
if (!s || s.state === 'running' || s.state === 'starting')
|
|
1020
|
+
continue;
|
|
1021
|
+
try {
|
|
1022
|
+
await this.resumeSession(id);
|
|
1023
|
+
resumed.push(s.address);
|
|
1024
|
+
}
|
|
1025
|
+
catch (err) {
|
|
1026
|
+
this.emit('error', new Error(`resume ${s.address}: ${err.message}`));
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
return resumed;
|
|
1030
|
+
}
|
|
954
1031
|
async resumeWorkspace(workspaceId) {
|
|
955
1032
|
const out = [];
|
|
956
1033
|
for (const s of this.sessions.list()) {
|
|
@@ -1431,6 +1508,16 @@ export class Hub extends EventEmitter {
|
|
|
1431
1508
|
throw new Error('cannot send a message to yourself');
|
|
1432
1509
|
this.requireVisible(me, to);
|
|
1433
1510
|
const result = await this.deliverFrom(me.address, to, text);
|
|
1511
|
+
// A shell never answers: what it prints stays on its own screen, and
|
|
1512
|
+
// reading that screen is the only way to see it - not polling (issue 30).
|
|
1513
|
+
if (this.isShellAt(to)) {
|
|
1514
|
+
return {
|
|
1515
|
+
...result,
|
|
1516
|
+
note: `${to} is a shell, not an agent: your text was run there as a command, ` +
|
|
1517
|
+
'without a [from ...] prefix, and nothing will be sent back to you. Call ' +
|
|
1518
|
+
'read_screen on it to see the output.',
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1434
1521
|
// Only a successful send counts as a question asked - deliverFrom throws
|
|
1435
1522
|
// on failure, so a message that never arrived does not excuse polling.
|
|
1436
1523
|
this.pollWatch.noteMessage(me.address, to);
|
|
@@ -1506,8 +1593,14 @@ export class Hub extends EventEmitter {
|
|
|
1506
1593
|
// alternative is writing into a CLI that is not reading, which is not a
|
|
1507
1594
|
// delivery at all.
|
|
1508
1595
|
const target = this.sessions.getByAddress(to);
|
|
1509
|
-
if (target)
|
|
1596
|
+
if (target) {
|
|
1597
|
+
const t0 = Date.now();
|
|
1510
1598
|
await this.awaitOpened(target.id);
|
|
1599
|
+
const waited = Date.now() - t0;
|
|
1600
|
+
if (waited > 50) {
|
|
1601
|
+
debug('deliver', `${fromAddr} -> ${to}: held ${waited}ms behind the agent's opening`);
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1511
1604
|
const r = this.router.send(fromAddr, to, text);
|
|
1512
1605
|
if (!r.delivered)
|
|
1513
1606
|
throw new Error(r.error ?? 'delivery failed');
|
|
@@ -1518,6 +1611,10 @@ export class Hub extends EventEmitter {
|
|
|
1518
1611
|
// complete on this side too.
|
|
1519
1612
|
const id = randomUUID();
|
|
1520
1613
|
const sentAt = Date.now();
|
|
1614
|
+
debug('deliver', `message ${id} ${fromAddr} -> ${to} (${text.length} chars): ` +
|
|
1615
|
+
(where === 'remote'
|
|
1616
|
+
? `to the attached machine ${this.peers.hostIdFor(to) ?? '?'}, which types it`
|
|
1617
|
+
: 'up to the canvas, which routes it'));
|
|
1521
1618
|
try {
|
|
1522
1619
|
if (where === 'remote')
|
|
1523
1620
|
await this.peers.deliver(fromAddr, to, text);
|
|
@@ -1531,9 +1628,11 @@ export class Hub extends EventEmitter {
|
|
|
1531
1628
|
};
|
|
1532
1629
|
this.store.insertMessage(m);
|
|
1533
1630
|
this.emit('message', m);
|
|
1631
|
+
debug('deliver', `message ${id}: accepted after ${Date.now() - sentAt}ms`);
|
|
1534
1632
|
return { delivered: true, to, deliveredAt: m.deliveredAt };
|
|
1535
1633
|
}
|
|
1536
1634
|
catch (err) {
|
|
1635
|
+
debug('deliver', `message ${id}: failed - ${err.message}`);
|
|
1537
1636
|
const m = {
|
|
1538
1637
|
id, fromAddr, toAddr: to, body: text,
|
|
1539
1638
|
sentAt, deliveredAt: null, deliveryState: 'failed',
|
|
@@ -1609,7 +1708,13 @@ export class Hub extends EventEmitter {
|
|
|
1609
1708
|
* gets there.
|
|
1610
1709
|
*/
|
|
1611
1710
|
const picked = await this.pickSpawnTemplate(me, opts.profile);
|
|
1612
|
-
|
|
1711
|
+
// Attributed, except in a shell, which would run the prefix (issue 30).
|
|
1712
|
+
const childProfile = this.profiles.get(picked.agent);
|
|
1713
|
+
const instruction = !opts.prompt
|
|
1714
|
+
? null
|
|
1715
|
+
: childProfile && isPlainTerminal(childProfile)
|
|
1716
|
+
? opts.prompt
|
|
1717
|
+
: `[from ${me.address}] ${opts.prompt}`;
|
|
1613
1718
|
const opening = [picked.prompt, instruction].filter(Boolean).join('\n\n') || undefined;
|
|
1614
1719
|
// A `host` naming something beyond this hub's own machine has to go up
|
|
1615
1720
|
// the link: an attached hub holds rows for nothing but itself, and only
|
|
@@ -1797,14 +1902,20 @@ export class Hub extends EventEmitter {
|
|
|
1797
1902
|
* taken as answers to it. Such a screen is waited out until it changes.
|
|
1798
1903
|
*/
|
|
1799
1904
|
async typeWhenReady(sessionId, text) {
|
|
1905
|
+
const t0 = Date.now();
|
|
1906
|
+
debug('deliver', `${sessionId} first instruction (${text.length} chars): waiting for the CLI`);
|
|
1800
1907
|
const ready = await this.awaitReady(sessionId);
|
|
1801
1908
|
const pty = this.sessions.pty(sessionId);
|
|
1802
|
-
if (!ready || !pty?.running)
|
|
1909
|
+
if (!ready || !pty?.running) {
|
|
1910
|
+
debug('deliver', `${sessionId} first instruction dropped after ${Date.now() - t0}ms: ` +
|
|
1911
|
+
(pty?.running ? 'the CLI never drew anything' : 'the CLI exited'));
|
|
1803
1912
|
return;
|
|
1913
|
+
}
|
|
1804
1914
|
const session = this.sessions.get(sessionId);
|
|
1805
1915
|
if (!session)
|
|
1806
1916
|
return;
|
|
1807
1917
|
const mode = this.profiles.get(session.profile)?.inject ?? 'bracketed';
|
|
1918
|
+
debug('deliver', `${sessionId} first instruction: CLI ready after ${Date.now() - t0}ms, typing it`);
|
|
1808
1919
|
try {
|
|
1809
1920
|
// Awaited, so that a caller holding this session's readiness gate is not
|
|
1810
1921
|
// released until the Enter is in: a message injected between the paste
|
|
@@ -1835,9 +1946,21 @@ export class Hub extends EventEmitter {
|
|
|
1835
1946
|
// A question on screen outlasts any cap: the human answers it when they
|
|
1836
1947
|
// get to the window, and the instruction is still wanted after that.
|
|
1837
1948
|
const asking = () => !!hint && hint.test(pty.tailLines(pty.rows));
|
|
1949
|
+
/*
|
|
1950
|
+
* Quiet is not enough for an agent that reports through hooks. On kid7,
|
|
1951
|
+
* starting straight after an update, Claude Code had drawn its prompt and
|
|
1952
|
+
* gone quiet five seconds in - and was not reading yet: the instruction
|
|
1953
|
+
* typed then lost its Enter, and SessionStart only arrived after the paste.
|
|
1954
|
+
* Its first hook is the agent saying it is up, so for such an agent that
|
|
1955
|
+
* is what readiness waits for, on top of the quiet. The cap still applies,
|
|
1956
|
+
* so an agent whose hooks never run is not held forever.
|
|
1957
|
+
*/
|
|
1958
|
+
const wantsHook = !!profile?.mcp && profile.status === 'hooks';
|
|
1959
|
+
const hooked = () => !wantsHook || pty.hooksReported;
|
|
1838
1960
|
return new Promise((res) => {
|
|
1839
1961
|
let settled = false;
|
|
1840
1962
|
let seen = false;
|
|
1963
|
+
let isQuiet = false;
|
|
1841
1964
|
let quiet = null;
|
|
1842
1965
|
const done = (v) => {
|
|
1843
1966
|
if (settled)
|
|
@@ -1848,26 +1971,53 @@ export class Hub extends EventEmitter {
|
|
|
1848
1971
|
clearTimeout(quiet);
|
|
1849
1972
|
pty.off('data', onData);
|
|
1850
1973
|
pty.off('exit', onExit);
|
|
1974
|
+
pty.off('hook', onHook);
|
|
1851
1975
|
res(v);
|
|
1852
1976
|
};
|
|
1977
|
+
const settle = () => {
|
|
1978
|
+
if (asking()) {
|
|
1979
|
+
debug('deliver', `${sessionId} quiet, but a question is on screen; still waiting`);
|
|
1980
|
+
return;
|
|
1981
|
+
}
|
|
1982
|
+
if (!hooked()) {
|
|
1983
|
+
debug('deliver', `${sessionId} quiet, but its hooks have not reported yet; still waiting`);
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
debug('deliver', `${sessionId} ready: no output for ${OPENING_QUIET_MS}ms, hooks reported`);
|
|
1987
|
+
done(true);
|
|
1988
|
+
};
|
|
1853
1989
|
const onData = () => {
|
|
1854
1990
|
seen = true;
|
|
1991
|
+
isQuiet = false;
|
|
1855
1992
|
if (quiet)
|
|
1856
1993
|
clearTimeout(quiet);
|
|
1857
1994
|
quiet = setTimeout(() => {
|
|
1858
1995
|
quiet = null;
|
|
1859
|
-
|
|
1860
|
-
|
|
1996
|
+
isQuiet = true;
|
|
1997
|
+
settle();
|
|
1861
1998
|
}, OPENING_QUIET_MS);
|
|
1862
1999
|
};
|
|
2000
|
+
// A hook that lands after the quiet is what the quiet was waiting for.
|
|
2001
|
+
const onHook = () => {
|
|
2002
|
+
if (isQuiet)
|
|
2003
|
+
settle();
|
|
2004
|
+
};
|
|
1863
2005
|
const onExit = () => done(false);
|
|
1864
2006
|
const cap = setTimeout(() => {
|
|
1865
|
-
if (
|
|
1866
|
-
|
|
2007
|
+
if (asking())
|
|
2008
|
+
return;
|
|
2009
|
+
debug('deliver', `${sessionId} ${OPENING_READY_CAP_MS}ms cap reached ` +
|
|
2010
|
+
(!seen
|
|
2011
|
+
? 'with no output at all'
|
|
2012
|
+
: hooked()
|
|
2013
|
+
? 'while still drawing; typing anyway'
|
|
2014
|
+
: 'and its hooks never reported; typing anyway'));
|
|
2015
|
+
done(seen);
|
|
1867
2016
|
}, OPENING_READY_CAP_MS);
|
|
1868
2017
|
cap.unref?.();
|
|
1869
2018
|
pty.on('data', onData);
|
|
1870
2019
|
pty.on('exit', onExit);
|
|
2020
|
+
pty.on('hook', onHook);
|
|
1871
2021
|
});
|
|
1872
2022
|
}
|
|
1873
2023
|
async readScreen(sessionId, address, lines) {
|
|
@@ -1879,6 +2029,9 @@ export class Hub extends EventEmitter {
|
|
|
1879
2029
|
// readScreenAt: that path also serves peers asking on this hub's behalf
|
|
1880
2030
|
// for a caller it never authenticated, and the count belongs to the
|
|
1881
2031
|
// caller's own hub.
|
|
2032
|
+
// Not for a shell: watching its screen is how its output is read at all.
|
|
2033
|
+
if (this.isShellAt(address))
|
|
2034
|
+
return result;
|
|
1882
2035
|
const note = this.pollWatch.noteRead(me.address, address);
|
|
1883
2036
|
return note ? { ...result, note } : result;
|
|
1884
2037
|
}
|