linkshell-cli 0.5.42 → 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.
Files changed (135) hide show
  1. package/dist/cli/src/commands/host.d.ts +23 -0
  2. package/dist/cli/src/commands/host.js +264 -0
  3. package/dist/cli/src/commands/host.js.map +1 -0
  4. package/dist/cli/src/commands/pair.d.ts +1 -0
  5. package/dist/cli/src/commands/pair.js +48 -0
  6. package/dist/cli/src/commands/pair.js.map +1 -0
  7. package/dist/cli/src/index.js +68 -0
  8. package/dist/cli/src/index.js.map +1 -1
  9. package/dist/cli/src/utils/daemon.d.ts +1 -1
  10. package/dist/cli/src/utils/daemon.js +3 -2
  11. package/dist/cli/src/utils/daemon.js.map +1 -1
  12. package/dist/cli/tsconfig.tsbuildinfo +1 -1
  13. package/dist/host/src/drivers/acp/connection.d.ts +61 -0
  14. package/dist/host/src/drivers/acp/connection.js +121 -0
  15. package/dist/host/src/drivers/acp/connection.js.map +1 -0
  16. package/dist/host/src/drivers/acp/driver.d.ts +110 -0
  17. package/dist/host/src/drivers/acp/driver.js +438 -0
  18. package/dist/host/src/drivers/acp/driver.js.map +1 -0
  19. package/dist/host/src/drivers/acp/mapper.d.ts +49 -0
  20. package/dist/host/src/drivers/acp/mapper.js +411 -0
  21. package/dist/host/src/drivers/acp/mapper.js.map +1 -0
  22. package/dist/host/src/drivers/auth.d.ts +7 -0
  23. package/dist/host/src/drivers/auth.js +57 -0
  24. package/dist/host/src/drivers/auth.js.map +1 -0
  25. package/dist/host/src/drivers/claude/driver.d.ts +76 -0
  26. package/dist/host/src/drivers/claude/driver.js +331 -0
  27. package/dist/host/src/drivers/claude/driver.js.map +1 -0
  28. package/dist/host/src/drivers/claude/transcript.d.ts +335 -0
  29. package/dist/host/src/drivers/claude/transcript.js +516 -0
  30. package/dist/host/src/drivers/claude/transcript.js.map +1 -0
  31. package/dist/host/src/drivers/codex/app-server.d.ts +36 -0
  32. package/dist/host/src/drivers/codex/app-server.js +144 -0
  33. package/dist/host/src/drivers/codex/app-server.js.map +1 -0
  34. package/dist/host/src/drivers/codex/driver.d.ts +89 -0
  35. package/dist/host/src/drivers/codex/driver.js +452 -0
  36. package/dist/host/src/drivers/codex/driver.js.map +1 -0
  37. package/dist/host/src/drivers/codex/mapper.d.ts +61 -0
  38. package/dist/host/src/drivers/codex/mapper.js +623 -0
  39. package/dist/host/src/drivers/codex/mapper.js.map +1 -0
  40. package/dist/host/src/drivers/codex/settings.d.ts +51 -0
  41. package/dist/host/src/drivers/codex/settings.js +107 -0
  42. package/dist/host/src/drivers/codex/settings.js.map +1 -0
  43. package/dist/host/src/drivers/images.d.ts +11 -0
  44. package/dist/host/src/drivers/images.js +34 -0
  45. package/dist/host/src/drivers/images.js.map +1 -0
  46. package/dist/host/src/drivers/nesting.d.ts +6 -0
  47. package/dist/host/src/drivers/nesting.js +25 -0
  48. package/dist/host/src/drivers/nesting.js.map +1 -0
  49. package/dist/host/src/drivers/registry.d.ts +21 -0
  50. package/dist/host/src/drivers/registry.js +30 -0
  51. package/dist/host/src/drivers/registry.js.map +1 -0
  52. package/dist/host/src/drivers/types.d.ts +105 -0
  53. package/dist/host/src/drivers/types.js +2 -0
  54. package/dist/host/src/drivers/types.js.map +1 -0
  55. package/dist/host/src/fs.d.ts +69 -0
  56. package/dist/host/src/fs.js +198 -0
  57. package/dist/host/src/fs.js.map +1 -0
  58. package/dist/host/src/gateway.d.ts +55 -0
  59. package/dist/host/src/gateway.js +263 -0
  60. package/dist/host/src/gateway.js.map +1 -0
  61. package/dist/host/src/host.d.ts +51 -0
  62. package/dist/host/src/host.js +133 -0
  63. package/dist/host/src/host.js.map +1 -0
  64. package/dist/host/src/hub.d.ts +89 -0
  65. package/dist/host/src/hub.js +592 -0
  66. package/dist/host/src/hub.js.map +1 -0
  67. package/dist/host/src/index.d.ts +10 -0
  68. package/dist/host/src/index.js +10 -0
  69. package/dist/host/src/index.js.map +1 -0
  70. package/dist/host/src/ports.d.ts +24 -0
  71. package/dist/host/src/ports.js +201 -0
  72. package/dist/host/src/ports.js.map +1 -0
  73. package/dist/host/src/rpc/client.d.ts +9 -0
  74. package/dist/host/src/rpc/client.js +42 -0
  75. package/dist/host/src/rpc/client.js.map +1 -0
  76. package/dist/host/src/rpc/server.d.ts +52 -0
  77. package/dist/host/src/rpc/server.js +293 -0
  78. package/dist/host/src/rpc/server.js.map +1 -0
  79. package/dist/host/src/screen.d.ts +30 -0
  80. package/dist/host/src/screen.js +230 -0
  81. package/dist/host/src/screen.js.map +1 -0
  82. package/dist/host/src/shell-env.d.ts +8 -0
  83. package/dist/host/src/shell-env.js +39 -0
  84. package/dist/host/src/shell-env.js.map +1 -0
  85. package/dist/host/src/store.d.ts +75 -0
  86. package/dist/host/src/store.js +265 -0
  87. package/dist/host/src/store.js.map +1 -0
  88. package/dist/host/src/terminals.d.ts +55 -0
  89. package/dist/host/src/terminals.js +367 -0
  90. package/dist/host/src/terminals.js.map +1 -0
  91. package/dist/shared-protocol/src/index.d.ts +3345 -3345
  92. package/dist/wire/src/crypto.d.ts +65 -0
  93. package/dist/wire/src/crypto.js +253 -0
  94. package/dist/wire/src/crypto.js.map +1 -0
  95. package/dist/wire/src/index.d.ts +8 -0
  96. package/dist/wire/src/index.js +9 -0
  97. package/dist/wire/src/index.js.map +1 -0
  98. package/dist/wire/src/model.d.ts +704 -0
  99. package/dist/wire/src/model.js +163 -0
  100. package/dist/wire/src/model.js.map +1 -0
  101. package/dist/wire/src/peer.d.ts +42 -0
  102. package/dist/wire/src/peer.js +143 -0
  103. package/dist/wire/src/peer.js.map +1 -0
  104. package/dist/wire/src/relay-client.d.ts +82 -0
  105. package/dist/wire/src/relay-client.js +259 -0
  106. package/dist/wire/src/relay-client.js.map +1 -0
  107. package/dist/wire/src/relay.d.ts +481 -0
  108. package/dist/wire/src/relay.js +129 -0
  109. package/dist/wire/src/relay.js.map +1 -0
  110. package/dist/wire/src/rpc.d.ts +4170 -0
  111. package/dist/wire/src/rpc.js +332 -0
  112. package/dist/wire/src/rpc.js.map +1 -0
  113. package/dist/wire/src/shell.d.ts +2 -0
  114. package/dist/wire/src/shell.js +62 -0
  115. package/dist/wire/src/shell.js.map +1 -0
  116. package/dist/wire/src/updates.d.ts +3325 -0
  117. package/dist/wire/src/updates.js +210 -0
  118. package/dist/wire/src/updates.js.map +1 -0
  119. package/package.json +8 -3
  120. package/src/commands/host.ts +283 -0
  121. package/src/commands/pair.ts +47 -0
  122. package/src/index.ts +73 -0
  123. package/src/utils/daemon.ts +4 -3
  124. package/dist/cli/src/commands/hook.d.ts +0 -6
  125. package/dist/cli/src/commands/hook.js +0 -120
  126. package/dist/cli/src/commands/hook.js.map +0 -1
  127. package/dist/cli/src/runtime/acp/agent-workspace-git.d.ts +0 -63
  128. package/dist/cli/src/runtime/acp/agent-workspace-git.js +0 -297
  129. package/dist/cli/src/runtime/acp/agent-workspace-git.js.map +0 -1
  130. package/dist/cli/src/runtime/acp-relay.d.ts +0 -23
  131. package/dist/cli/src/runtime/acp-relay.js +0 -73
  132. package/dist/cli/src/runtime/acp-relay.js.map +0 -1
  133. package/dist/cli/src/runtime/hook-permission-server.d.ts +0 -36
  134. package/dist/cli/src/runtime/hook-permission-server.js +0 -147
  135. package/dist/cli/src/runtime/hook-permission-server.js.map +0 -1
@@ -0,0 +1,198 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, open, readdir, stat, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { basename, dirname, extname, join, resolve } from "node:path";
5
+ import { RpcError } from "@linkshell/wire";
6
+ // Browsing and searching the host's directories, to pick where a session runs.
7
+ const PROJECT_MARKERS = [".git", "package.json", "Cargo.toml", "go.mod", "pyproject.toml", "Package.swift", "pom.xml", "build.gradle", "Gemfile", "composer.json", "deno.json", "CMakeLists.txt"];
8
+ /** Directories never worth descending into when searching. */
9
+ const SKIP = new Set([
10
+ "node_modules", ".git", "Library", "Pods", "DerivedData", "build", "dist", ".next", ".expo", "target", "vendor",
11
+ ".Trash", "Applications", "Movies", "Music", "Pictures", ".cache", ".npm", ".pnpm-store", "venv", ".venv", "__pycache__",
12
+ ]);
13
+ function isProject(names) {
14
+ return PROJECT_MARKERS.some((marker) => names.has(marker));
15
+ }
16
+ function expand(path) {
17
+ if (!path || path === "~")
18
+ return homedir();
19
+ if (path.startsWith("~/"))
20
+ return join(homedir(), path.slice(2));
21
+ return resolve(path);
22
+ }
23
+ async function subdirectories(path, hidden) {
24
+ const dirents = await readdir(path, { withFileTypes: true });
25
+ const names = new Set(dirents.map((d) => d.name));
26
+ const dirs = dirents.filter((d) => (d.isDirectory() || d.isSymbolicLink()) && (hidden || !d.name.startsWith(".")));
27
+ const entries = await Promise.all(dirs.map(async (d) => {
28
+ const full = join(path, d.name);
29
+ try {
30
+ if (d.isSymbolicLink() && !(await stat(full)).isDirectory())
31
+ return undefined;
32
+ const children = new Set(await readdir(full).catch(() => []));
33
+ return { name: d.name, path: full, project: isProject(children) || undefined };
34
+ }
35
+ catch {
36
+ return undefined;
37
+ }
38
+ }));
39
+ return {
40
+ entries: entries
41
+ .filter((e) => Boolean(e))
42
+ .sort((a, b) => Number(Boolean(b.project)) - Number(Boolean(a.project)) || a.name.localeCompare(b.name, undefined, { sensitivity: "base" })),
43
+ names,
44
+ };
45
+ }
46
+ export async function listDirectory(input) {
47
+ const path = expand(input.path);
48
+ if (!existsSync(path) || !(await stat(path)).isDirectory())
49
+ throw RpcError.app("not_found", `no such directory: ${path}`);
50
+ const { entries } = await subdirectories(path, input.hidden).catch(() => {
51
+ throw RpcError.app("not_found", `can't read ${path}`);
52
+ });
53
+ const parent = dirname(path);
54
+ return { path, parent: parent === path ? undefined : parent, home: homedir(), entries };
55
+ }
56
+ /**
57
+ * Breadth-first under the home directory, a few levels deep, within a time
58
+ * budget: good enough to find "claude-remote" or "rtranslate" by typing.
59
+ */
60
+ export async function searchDirectories(input, budgetMs = 1500) {
61
+ const query = input.query.toLowerCase().trim();
62
+ const deadline = Date.now() + budgetMs;
63
+ const found = [];
64
+ let queue = [{ path: homedir(), depth: 0 }];
65
+ let truncated = false;
66
+ while (queue.length > 0) {
67
+ if (Date.now() > deadline) {
68
+ truncated = true;
69
+ break;
70
+ }
71
+ const next = [];
72
+ for (const { path, depth } of queue) {
73
+ if (Date.now() > deadline) {
74
+ truncated = true;
75
+ break;
76
+ }
77
+ let dirents;
78
+ try {
79
+ dirents = await readdir(path, { withFileTypes: true });
80
+ }
81
+ catch {
82
+ continue;
83
+ }
84
+ for (const d of dirents) {
85
+ if (!d.isDirectory() || d.name.startsWith(".") || SKIP.has(d.name))
86
+ continue;
87
+ const full = join(path, d.name);
88
+ if (d.name.toLowerCase().includes(query)) {
89
+ const children = new Set(await readdir(full).catch(() => []));
90
+ found.push({ name: d.name, path: full, project: isProject(children) || undefined });
91
+ }
92
+ if (depth < 4)
93
+ next.push({ path: full, depth: depth + 1 });
94
+ }
95
+ }
96
+ queue = next;
97
+ }
98
+ // Code projects first (that's what a session runs in), then how well the name matches.
99
+ const score = (e) => (e.project ? 0 : 10) + (e.name.toLowerCase() === query ? 0 : e.name.toLowerCase().startsWith(query) ? 1 : 2);
100
+ found.sort((a, b) => score(a) - score(b) || a.path.split("/").length - b.path.split("/").length);
101
+ if (found.length > input.limit)
102
+ truncated = true;
103
+ return { entries: found.slice(0, input.limit), truncated };
104
+ }
105
+ const IMAGE_TYPES = {
106
+ png: "image/png",
107
+ jpg: "image/jpeg",
108
+ jpeg: "image/jpeg",
109
+ gif: "image/gif",
110
+ webp: "image/webp",
111
+ svg: "image/svg+xml",
112
+ heic: "image/heic",
113
+ };
114
+ /** Reads a file for the phone's viewer. Text is detected by content, not extension. */
115
+ export async function readFile(input) {
116
+ const path = expand(input.path);
117
+ let info;
118
+ try {
119
+ info = await stat(path);
120
+ }
121
+ catch {
122
+ throw RpcError.app("not_found", `no such file: ${path}`);
123
+ }
124
+ if (!info.isFile())
125
+ throw RpcError.app("invalid_params", `not a file: ${path}`);
126
+ const extension = path.split(".").pop()?.toLowerCase() ?? "";
127
+ const image = IMAGE_TYPES[extension];
128
+ const limit = image ? Math.min(input.maxBytes * 3, 8_000_000) : input.maxBytes;
129
+ const length = Math.min(info.size, limit);
130
+ const handle = await open(path, "r");
131
+ const buffer = Buffer.alloc(length);
132
+ try {
133
+ await handle.read(buffer, 0, length, 0);
134
+ }
135
+ finally {
136
+ await handle.close();
137
+ }
138
+ const base = { path, size: info.size, modifiedAt: info.mtimeMs, truncated: info.size > length };
139
+ if (image && !base.truncated)
140
+ return { ...base, kind: "image", data: buffer.toString("base64"), mimeType: image };
141
+ // Text: no NUL bytes in the first 8KB.
142
+ if (buffer.subarray(0, 8192).includes(0))
143
+ return { ...base, kind: "binary" };
144
+ let text = buffer.toString("utf8");
145
+ // Don't end on half a multi-byte character.
146
+ if (base.truncated)
147
+ text = text.replace(/\uFFFD+$/, "");
148
+ return { ...base, kind: "text", text };
149
+ }
150
+ /** A single path component: no separators, no `.`/`..`, nothing a shell or filesystem would choke on. */
151
+ function safeName(name) {
152
+ const cleaned = name.replace(/[/\\\0]/g, "_").trim();
153
+ if (!cleaned || cleaned === "." || cleaned === "..")
154
+ throw RpcError.app("invalid_params", "invalid name");
155
+ return cleaned;
156
+ }
157
+ async function existingDirectory(path) {
158
+ const dir = resolve(path.replace(/^~(?=$|\/)/, homedir()));
159
+ const info = await stat(dir).catch(() => undefined);
160
+ if (!info?.isDirectory())
161
+ throw RpcError.app("not_found", `no such directory: ${path}`);
162
+ return dir;
163
+ }
164
+ /** Writes an uploaded file into `dir`, never over an existing one. */
165
+ export async function uploadFile(input) {
166
+ const dir = await existingDirectory(input.dir);
167
+ const name = safeName(input.name);
168
+ const bytes = Buffer.from(input.data, "base64");
169
+ const ext = extname(name);
170
+ const stem = basename(name, ext);
171
+ for (let attempt = 1; attempt < 1000; attempt++) {
172
+ const path = join(dir, attempt === 1 ? name : `${stem} ${attempt}${ext}`);
173
+ try {
174
+ // "wx": fails if the file exists, so a race can't overwrite either.
175
+ await writeFile(path, bytes, { flag: "wx" });
176
+ return { path, size: bytes.length };
177
+ }
178
+ catch (error) {
179
+ if (error.code !== "EEXIST")
180
+ throw error;
181
+ }
182
+ }
183
+ throw RpcError.app("busy", "too many files with this name");
184
+ }
185
+ export async function makeDirectory(input) {
186
+ const parent = await existingDirectory(input.parent);
187
+ const path = join(parent, safeName(input.name));
188
+ try {
189
+ await mkdir(path);
190
+ }
191
+ catch (error) {
192
+ if (error.code === "EEXIST")
193
+ throw RpcError.app("invalid_params", "a file or folder with this name already exists");
194
+ throw error;
195
+ }
196
+ return { path };
197
+ }
198
+ //# sourceMappingURL=fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../../../../host/src/fs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import { type GatewayStatus, type Identity } from "@linkshell/wire";
2
+ import type { RpcTransport } from "./rpc/server.js";
3
+ export interface GatewayLinkOptions {
4
+ /** Gateway base URL (ws:// or wss://, or http(s):// which is converted). */
5
+ url: string;
6
+ home: string;
7
+ /** Account token (from `linkshell login`); the machine then belongs to that account. */
8
+ token?: () => Promise<string | undefined> | string | undefined;
9
+ name?: string;
10
+ /** Serves a decrypted channel as a client connection. */
11
+ serve: (transport: RpcTransport) => void;
12
+ onChange?: (status: GatewayStatus) => void;
13
+ onPaired?: (device: {
14
+ id: string;
15
+ name: string;
16
+ }) => void;
17
+ log: (message: string) => void;
18
+ }
19
+ export declare function relayUrl(base: string): string;
20
+ export declare class GatewayLink {
21
+ private readonly options;
22
+ readonly identity: Identity;
23
+ private readonly relay;
24
+ private readonly devicesPath;
25
+ private devices;
26
+ private pairing?;
27
+ private readonly tunnels;
28
+ private readonly online;
29
+ private readonly name;
30
+ constructor(options: GatewayLinkOptions);
31
+ start(): void;
32
+ stop(): void;
33
+ status(): GatewayStatus;
34
+ /** Opens a pairing window; the returned link goes in the QR code. */
35
+ startPairing(): Promise<{
36
+ link: string;
37
+ code: string;
38
+ expiresAt: number;
39
+ gateway: string;
40
+ }>;
41
+ revoke(deviceId: string): Promise<void>;
42
+ private onPairRequest;
43
+ private onFrame;
44
+ /**
45
+ * Whose hello to accept: devices paired here (with the keys stored at
46
+ * pairing, not whatever the gateway says now), or, when this machine is
47
+ * signed in, devices the gateway says share its account.
48
+ */
49
+ private authorize;
50
+ private closeTunnel;
51
+ private refreshPresence;
52
+ private changed;
53
+ private loadDevices;
54
+ private saveDevices;
55
+ }
@@ -0,0 +1,263 @@
1
+ import { chmodSync, existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { hostname, platform } from "node:os";
3
+ import { join } from "node:path";
4
+ import { randomBytes } from "node:crypto";
5
+ import WebSocket from "ws";
6
+ import { answerTunnel, checkPairingProof, codeSecret, createIdentity, encodePairingLink, RELAY_PATH, RelayClient, RpcError, toBase64, } from "@linkshell/wire";
7
+ export function relayUrl(base) {
8
+ const url = new URL(base.replace(/^http/, "ws"));
9
+ if (!url.pathname.endsWith(RELAY_PATH))
10
+ url.pathname = url.pathname.replace(/\/$/, "") + RELAY_PATH;
11
+ return url.toString();
12
+ }
13
+ function loadIdentity(home) {
14
+ const path = join(home, "identity.json");
15
+ try {
16
+ const stored = JSON.parse(readFileSync(path, "utf8"));
17
+ if (stored.id && stored.signSecret && stored.boxSecret)
18
+ return stored;
19
+ }
20
+ catch {
21
+ // First run: create below.
22
+ }
23
+ const identity = createIdentity();
24
+ writeFileSync(path, `${JSON.stringify(identity, null, 2)}\n`, { mode: 0o600 });
25
+ chmodSync(path, 0o600);
26
+ return identity;
27
+ }
28
+ export class GatewayLink {
29
+ options;
30
+ identity;
31
+ relay;
32
+ devicesPath;
33
+ devices;
34
+ pairing;
35
+ tunnels = new Map();
36
+ online = new Set();
37
+ name;
38
+ constructor(options) {
39
+ this.options = options;
40
+ this.identity = loadIdentity(options.home);
41
+ this.devicesPath = join(options.home, "paired-devices.json");
42
+ this.devices = this.loadDevices();
43
+ this.name = options.name ?? hostname().replace(/\.local$/, "");
44
+ this.relay = new RelayClient({
45
+ url: relayUrl(options.url),
46
+ identity: this.identity,
47
+ role: "machine",
48
+ name: this.name,
49
+ platform: platform(),
50
+ token: options.token,
51
+ createSocket: (url) => new WebSocket(url, { perMessageDeflate: false }),
52
+ });
53
+ this.relay.onStatus((status, error) => {
54
+ if (status === "online") {
55
+ this.options.log(`[gateway] online as ${this.identity.id.slice(0, 8)}${this.relay.account ? ` (${this.relay.account.email ?? "account"})` : ""}`);
56
+ void this.refreshPresence();
57
+ }
58
+ else if (error && status === "offline") {
59
+ this.options.log(`[gateway] ${error.code}: ${error.message}`);
60
+ for (const tunnel of this.tunnels.values())
61
+ this.closeTunnel(tunnel, "gateway disconnected");
62
+ }
63
+ this.changed();
64
+ });
65
+ this.relay.onFrame((from, via, frame) => this.onFrame(from, via, frame));
66
+ this.relay.on("pair.request", (request) => void this.onPairRequest(request));
67
+ this.relay.on("presence", ({ id, online }) => {
68
+ if (online)
69
+ this.online.add(id);
70
+ else {
71
+ this.online.delete(id);
72
+ for (const tunnel of this.tunnels.values())
73
+ if (tunnel.deviceId === id)
74
+ this.closeTunnel(tunnel, "device went offline");
75
+ }
76
+ this.changed();
77
+ });
78
+ }
79
+ start() {
80
+ this.relay.start();
81
+ }
82
+ stop() {
83
+ for (const tunnel of this.tunnels.values())
84
+ this.closeTunnel(tunnel, "host stopping");
85
+ this.relay.stop();
86
+ }
87
+ status() {
88
+ const status = this.relay.status;
89
+ return {
90
+ url: this.options.url,
91
+ status: status === "stopped" ? "off" : status,
92
+ error: status === "online" ? undefined : this.relay.lastError,
93
+ machine: { id: this.identity.id, name: this.name },
94
+ account: this.relay.account,
95
+ devices: this.devices.map((device) => ({
96
+ id: device.id,
97
+ name: device.name,
98
+ pairedAt: device.pairedAt,
99
+ online: this.online.has(device.id),
100
+ })),
101
+ };
102
+ }
103
+ /** Opens a pairing window; the returned link goes in the QR code. */
104
+ async startPairing() {
105
+ if (!(await this.relay.waitOnline(8000))) {
106
+ throw RpcError.app("offline", `can't reach the gateway (${this.options.url})${this.relay.lastError ? `: ${this.relay.lastError.message}` : ""}`);
107
+ }
108
+ const offer = await this.relay.request("pair.offer", {});
109
+ const secret = toBase64(randomBytes(16));
110
+ this.pairing = { secret, code: offer.code, expiresAt: offer.expiresAt };
111
+ const link = encodePairingLink({ gateway: this.options.url, signKey: this.identity.signKey, secret, code: offer.code });
112
+ return { link, code: offer.code, expiresAt: offer.expiresAt, gateway: this.options.url };
113
+ }
114
+ async revoke(deviceId) {
115
+ this.devices = this.devices.filter((device) => device.id !== deviceId);
116
+ this.saveDevices();
117
+ for (const tunnel of this.tunnels.values())
118
+ if (tunnel.deviceId === deviceId)
119
+ this.closeTunnel(tunnel, "unpaired");
120
+ await this.relay.request("devices.revoke", { deviceId }).catch(() => { });
121
+ this.changed();
122
+ }
123
+ // ── pairing ───────────────────────────────────────────────────────
124
+ async onPairRequest(request) {
125
+ const pairing = this.pairing;
126
+ const valid = !!pairing &&
127
+ pairing.expiresAt > Date.now() &&
128
+ request.code === pairing.code &&
129
+ (checkPairingProof(pairing.secret, request.device, request.proof) || checkPairingProof(codeSecret(pairing.code), request.device, request.proof));
130
+ if (valid) {
131
+ this.devices = [
132
+ ...this.devices.filter((device) => device.id !== request.device.id),
133
+ { ...request.device, pairedAt: Date.now() },
134
+ ];
135
+ this.saveDevices();
136
+ this.pairing = undefined;
137
+ this.online.add(request.device.id);
138
+ this.options.log(`[gateway] paired with "${request.device.name}"`);
139
+ this.options.onPaired?.({ id: request.device.id, name: request.device.name });
140
+ this.changed();
141
+ }
142
+ else {
143
+ this.options.log(`[gateway] refused a pairing attempt from "${request.device.name}"`);
144
+ }
145
+ await this.relay.request("pair.decide", { requestId: request.requestId, accept: valid }).catch(() => { });
146
+ }
147
+ // ── channels ──────────────────────────────────────────────────────
148
+ onFrame(from, via, frame) {
149
+ const key = `${from.id}:${frame.ch}`;
150
+ switch (frame.k) {
151
+ case "hello": {
152
+ const device = this.authorize(from, via);
153
+ if (!device) {
154
+ this.relay.send(from.id, { k: "refuse", ch: frame.ch, code: "not_paired", message: "this device isn't paired with this computer" });
155
+ return;
156
+ }
157
+ let answer;
158
+ try {
159
+ answer = answerTunnel(this.identity, device, frame);
160
+ }
161
+ catch {
162
+ this.relay.send(from.id, { k: "refuse", ch: frame.ch, code: "bad_hello", message: "handshake failed" });
163
+ return;
164
+ }
165
+ const previous = this.tunnels.get(key);
166
+ if (previous)
167
+ this.closeTunnel(previous, "reopened");
168
+ const tunnel = { channel: answer.channel, deviceId: from.id, ch: frame.ch, closed: false };
169
+ this.tunnels.set(key, tunnel);
170
+ this.relay.send(from.id, answer.welcome);
171
+ this.options.serve({
172
+ local: false,
173
+ send: (text) => {
174
+ if (!tunnel.closed)
175
+ this.relay.send(from.id, { k: "data", ch: frame.ch, box: tunnel.channel.seal(text) });
176
+ },
177
+ close: () => this.closeTunnel(tunnel, "closed by host", true),
178
+ onMessage: (listener) => (tunnel.message = listener),
179
+ onClose: (listener) => (tunnel.close = listener),
180
+ });
181
+ return;
182
+ }
183
+ case "data": {
184
+ const tunnel = this.tunnels.get(key);
185
+ if (!tunnel)
186
+ return;
187
+ let text;
188
+ try {
189
+ text = tunnel.channel.open(frame.box);
190
+ }
191
+ catch {
192
+ // Tampered, replayed or out of order: the channel can't be trusted any more.
193
+ this.closeTunnel(tunnel, "bad frame", true);
194
+ return;
195
+ }
196
+ tunnel.message?.(text);
197
+ return;
198
+ }
199
+ case "close": {
200
+ const tunnel = this.tunnels.get(key);
201
+ if (tunnel)
202
+ this.closeTunnel(tunnel, frame.reason ?? "closed by device");
203
+ return;
204
+ }
205
+ default:
206
+ return;
207
+ }
208
+ }
209
+ /**
210
+ * Whose hello to accept: devices paired here (with the keys stored at
211
+ * pairing, not whatever the gateway says now), or, when this machine is
212
+ * signed in, devices the gateway says share its account.
213
+ */
214
+ authorize(from, via) {
215
+ const paired = this.devices.find((device) => device.id === from.id);
216
+ if (paired)
217
+ return paired.signKey === from.signKey && paired.boxKey === from.boxKey ? paired : undefined;
218
+ if (via === "account" && this.relay.account)
219
+ return from;
220
+ return undefined;
221
+ }
222
+ closeTunnel(tunnel, reason, notify = false) {
223
+ if (tunnel.closed)
224
+ return;
225
+ tunnel.closed = true;
226
+ this.tunnels.delete(`${tunnel.deviceId}:${tunnel.ch}`);
227
+ if (notify)
228
+ this.relay.send(tunnel.deviceId, { k: "close", ch: tunnel.ch, reason });
229
+ tunnel.close?.();
230
+ }
231
+ async refreshPresence() {
232
+ try {
233
+ const { devices } = await this.relay.request("devices.list", {});
234
+ this.online.clear();
235
+ for (const device of devices)
236
+ if (device.online)
237
+ this.online.add(device.id);
238
+ this.changed();
239
+ }
240
+ catch {
241
+ // Presence events will correct it.
242
+ }
243
+ }
244
+ changed() {
245
+ this.options.onChange?.(this.status());
246
+ }
247
+ loadDevices() {
248
+ if (!existsSync(this.devicesPath))
249
+ return [];
250
+ try {
251
+ const parsed = JSON.parse(readFileSync(this.devicesPath, "utf8"));
252
+ return Array.isArray(parsed) ? parsed : [];
253
+ }
254
+ catch {
255
+ return [];
256
+ }
257
+ }
258
+ saveDevices() {
259
+ writeFileSync(this.devicesPath, `${JSON.stringify(this.devices, null, 2)}\n`, { mode: 0o600 });
260
+ chmodSync(this.devicesPath, 0o600);
261
+ }
262
+ }
263
+ //# sourceMappingURL=gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../../../../host/src/gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import type { MachineInfo } from "@linkshell/wire";
2
+ import type { AgentDriver } from "./drivers/types.js";
3
+ import { SessionHub } from "./hub.js";
4
+ import { HostRpcServer } from "./rpc/server.js";
5
+ import { GatewayLink } from "./gateway.js";
6
+ export interface HostPaths {
7
+ home: string;
8
+ runDir: string;
9
+ hostSocket: string;
10
+ codexSocket: string;
11
+ database: string;
12
+ }
13
+ /** `~/.linkshell`, or `$LINKSHELL_HOME` (tests, multiple instances). */
14
+ export declare function defaultHome(): string;
15
+ export declare function hostPaths(home?: string): HostPaths;
16
+ export interface HostOptions {
17
+ home?: string;
18
+ version: string;
19
+ /** Environment agents run with — should be the user's login-shell environment. */
20
+ env?: NodeJS.ProcessEnv;
21
+ codexCommand?: string;
22
+ claudeCommand?: string;
23
+ /** Overrides the bundled Claude ACP adapter. */
24
+ claudeAdapter?: {
25
+ command: string;
26
+ args: string[];
27
+ };
28
+ /** Extra drivers (tests) or a full replacement of the default set. */
29
+ drivers?: (paths: HostPaths) => AgentDriver[];
30
+ tcpPort?: number;
31
+ log?: (message: string) => void;
32
+ /** A v2 gateway to reach this machine through, from anywhere. */
33
+ gateway?: {
34
+ url: string;
35
+ token?: () => Promise<string | undefined> | string | undefined;
36
+ name?: string;
37
+ };
38
+ /** How often to re-list sessions started outside LinkShell; 0 disables. */
39
+ discoveryIntervalMs?: number;
40
+ }
41
+ export interface RunningHost {
42
+ paths: HostPaths;
43
+ hub: SessionHub;
44
+ server: HostRpcServer;
45
+ gateway?: GatewayLink;
46
+ machineInfo(): MachineInfo;
47
+ stop(): Promise<void>;
48
+ }
49
+ /** True when another host already answers on the socket. */
50
+ export declare function isHostRunning(socketPath: string): Promise<boolean>;
51
+ export declare function startHost(options: HostOptions): Promise<RunningHost>;