linkshell-cli 0.8.2 → 0.10.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 (173) hide show
  1. package/README.md +11 -6
  2. package/dist/cli/src/commands/gateway.d.ts +2 -0
  3. package/dist/cli/src/commands/gateway.js +29 -0
  4. package/dist/cli/src/commands/gateway.js.map +1 -0
  5. package/dist/cli/src/commands/host.d.ts +4 -0
  6. package/dist/cli/src/commands/host.js +12 -2
  7. package/dist/cli/src/commands/host.js.map +1 -1
  8. package/dist/cli/src/commands/screen.d.ts +10 -0
  9. package/dist/cli/src/commands/screen.js +82 -74
  10. package/dist/cli/src/commands/screen.js.map +1 -1
  11. package/dist/cli/src/commands/setup.d.ts +11 -1
  12. package/dist/cli/src/commands/setup.js +102 -38
  13. package/dist/cli/src/commands/setup.js.map +1 -1
  14. package/dist/cli/src/index.js +28 -338
  15. package/dist/cli/src/index.js.map +1 -1
  16. package/dist/cli/src/utils/daemon.d.ts +0 -7
  17. package/dist/cli/src/utils/daemon.js +1 -18
  18. package/dist/cli/src/utils/daemon.js.map +1 -1
  19. package/dist/cli/tsconfig.tsbuildinfo +1 -1
  20. package/dist/gateway/src/accounts.d.ts +6 -0
  21. package/dist/gateway/src/accounts.js +32 -0
  22. package/dist/gateway/src/accounts.js.map +1 -0
  23. package/dist/gateway/src/index.d.ts +4 -0
  24. package/dist/gateway/src/index.js +5 -0
  25. package/dist/gateway/src/index.js.map +1 -0
  26. package/dist/gateway/src/main.d.ts +1 -0
  27. package/dist/gateway/src/main.js +57 -0
  28. package/dist/gateway/src/main.js.map +1 -0
  29. package/dist/gateway/src/rate-limit.d.ts +22 -0
  30. package/dist/gateway/src/rate-limit.js +66 -0
  31. package/dist/gateway/src/rate-limit.js.map +1 -0
  32. package/dist/gateway/src/relay.d.ts +84 -0
  33. package/dist/gateway/src/relay.js +434 -0
  34. package/dist/gateway/src/relay.js.map +1 -0
  35. package/dist/gateway/src/serve.d.ts +34 -0
  36. package/dist/gateway/src/serve.js +102 -0
  37. package/dist/gateway/src/serve.js.map +1 -0
  38. package/dist/gateway/src/store.d.ts +35 -0
  39. package/dist/gateway/src/store.js +109 -0
  40. package/dist/gateway/src/store.js.map +1 -0
  41. package/dist/gateway/src/subscription.d.ts +18 -0
  42. package/dist/gateway/src/subscription.js +46 -0
  43. package/dist/gateway/src/subscription.js.map +1 -0
  44. package/dist/host/src/drivers/claude/transcript.d.ts +23 -23
  45. package/dist/host/src/fs.d.ts +3 -3
  46. package/dist/host/src/input.d.ts +112 -27
  47. package/dist/host/src/input.js +214 -144
  48. package/dist/host/src/input.js.map +1 -1
  49. package/dist/host/src/rpc/server.js +1 -1
  50. package/dist/host/src/rpc/server.js.map +1 -1
  51. package/dist/host/src/screen-pacer.d.ts +25 -11
  52. package/dist/host/src/screen-pacer.js +28 -6
  53. package/dist/host/src/screen-pacer.js.map +1 -1
  54. package/dist/host/src/screen-viewer.d.ts +5 -0
  55. package/dist/host/src/screen-viewer.js +1231 -55
  56. package/dist/host/src/screen-viewer.js.map +1 -1
  57. package/dist/host/src/screen.d.ts +19 -10
  58. package/dist/host/src/screen.js +314 -108
  59. package/dist/host/src/screen.js.map +1 -1
  60. package/dist/host/src/terminals.d.ts +1 -1
  61. package/dist/host/src/terminals.js +1 -0
  62. package/dist/host/src/terminals.js.map +1 -1
  63. package/dist/wire/src/model.d.ts +6 -6
  64. package/dist/wire/src/relay.d.ts +8 -8
  65. package/dist/wire/src/rpc.d.ts +818 -818
  66. package/dist/wire/src/updates.d.ts +18 -18
  67. package/package.json +6 -15
  68. package/src/commands/gateway.ts +31 -0
  69. package/src/commands/host.ts +15 -2
  70. package/src/commands/screen.ts +78 -72
  71. package/src/commands/setup.ts +102 -60
  72. package/src/index.ts +29 -433
  73. package/src/utils/daemon.ts +2 -27
  74. package/dist/cli/src/config.d.ts +0 -14
  75. package/dist/cli/src/config.js +0 -23
  76. package/dist/cli/src/config.js.map +0 -1
  77. package/dist/cli/src/machine-id.d.ts +0 -6
  78. package/dist/cli/src/machine-id.js +0 -50
  79. package/dist/cli/src/machine-id.js.map +0 -1
  80. package/dist/cli/src/providers.d.ts +0 -13
  81. package/dist/cli/src/providers.js +0 -77
  82. package/dist/cli/src/providers.js.map +0 -1
  83. package/dist/cli/src/runtime/acp/acp-client.d.ts +0 -128
  84. package/dist/cli/src/runtime/acp/acp-client.js +0 -430
  85. package/dist/cli/src/runtime/acp/acp-client.js.map +0 -1
  86. package/dist/cli/src/runtime/acp/agent-process-discovery.d.ts +0 -48
  87. package/dist/cli/src/runtime/acp/agent-process-discovery.js +0 -278
  88. package/dist/cli/src/runtime/acp/agent-process-discovery.js.map +0 -1
  89. package/dist/cli/src/runtime/acp/agent-session.d.ts +0 -62
  90. package/dist/cli/src/runtime/acp/agent-session.js +0 -1081
  91. package/dist/cli/src/runtime/acp/agent-session.js.map +0 -1
  92. package/dist/cli/src/runtime/acp/agent-workspace.d.ts +0 -311
  93. package/dist/cli/src/runtime/acp/agent-workspace.js +0 -6083
  94. package/dist/cli/src/runtime/acp/agent-workspace.js.map +0 -1
  95. package/dist/cli/src/runtime/acp/claude-sdk-client.d.ts +0 -121
  96. package/dist/cli/src/runtime/acp/claude-sdk-client.js +0 -1147
  97. package/dist/cli/src/runtime/acp/claude-sdk-client.js.map +0 -1
  98. package/dist/cli/src/runtime/acp/claude-stream-json-client.d.ts +0 -53
  99. package/dist/cli/src/runtime/acp/claude-stream-json-client.js +0 -552
  100. package/dist/cli/src/runtime/acp/claude-stream-json-client.js.map +0 -1
  101. package/dist/cli/src/runtime/acp/json-rpc.d.ts +0 -22
  102. package/dist/cli/src/runtime/acp/json-rpc.js +0 -160
  103. package/dist/cli/src/runtime/acp/json-rpc.js.map +0 -1
  104. package/dist/cli/src/runtime/acp/provider-resolver.d.ts +0 -16
  105. package/dist/cli/src/runtime/acp/provider-resolver.js +0 -132
  106. package/dist/cli/src/runtime/acp/provider-resolver.js.map +0 -1
  107. package/dist/cli/src/runtime/bridge-session.d.ts +0 -97
  108. package/dist/cli/src/runtime/bridge-session.js +0 -1364
  109. package/dist/cli/src/runtime/bridge-session.js.map +0 -1
  110. package/dist/cli/src/runtime/hook-command.d.ts +0 -9
  111. package/dist/cli/src/runtime/hook-command.js +0 -80
  112. package/dist/cli/src/runtime/hook-command.js.map +0 -1
  113. package/dist/cli/src/runtime/screen-fallback.d.ts +0 -22
  114. package/dist/cli/src/runtime/screen-fallback.js +0 -202
  115. package/dist/cli/src/runtime/screen-fallback.js.map +0 -1
  116. package/dist/cli/src/runtime/screen-share.d.ts +0 -34
  117. package/dist/cli/src/runtime/screen-share.js +0 -376
  118. package/dist/cli/src/runtime/screen-share.js.map +0 -1
  119. package/dist/cli/src/runtime/scrollback.d.ts +0 -11
  120. package/dist/cli/src/runtime/scrollback.js +0 -33
  121. package/dist/cli/src/runtime/scrollback.js.map +0 -1
  122. package/dist/cli/src/runtime/usage-report.d.ts +0 -34
  123. package/dist/cli/src/runtime/usage-report.js +0 -332
  124. package/dist/cli/src/runtime/usage-report.js.map +0 -1
  125. package/dist/cli/src/utils/keep-awake.d.ts +0 -6
  126. package/dist/cli/src/utils/keep-awake.js +0 -48
  127. package/dist/cli/src/utils/keep-awake.js.map +0 -1
  128. package/dist/cli/src/utils/lan-ip.d.ts +0 -9
  129. package/dist/cli/src/utils/lan-ip.js +0 -61
  130. package/dist/cli/src/utils/lan-ip.js.map +0 -1
  131. package/dist/host/src/input-helper.d.ts +0 -1
  132. package/dist/host/src/input-helper.js +0 -564
  133. package/dist/host/src/input-helper.js.map +0 -1
  134. package/dist/shared-protocol/src/index.d.ts +0 -17029
  135. package/dist/shared-protocol/src/index.js +0 -1064
  136. package/dist/shared-protocol/src/index.js.map +0 -1
  137. package/scripts/copy-web.mjs +0 -34
  138. package/src/config.ts +0 -36
  139. package/src/machine-id.ts +0 -61
  140. package/src/providers.ts +0 -97
  141. package/src/runtime/acp/acp-client.ts +0 -521
  142. package/src/runtime/acp/agent-process-discovery.ts +0 -308
  143. package/src/runtime/acp/agent-session.ts +0 -1186
  144. package/src/runtime/acp/agent-workspace.ts +0 -6590
  145. package/src/runtime/acp/claude-sdk-client.ts +0 -1221
  146. package/src/runtime/acp/claude-stream-json-client.ts +0 -633
  147. package/src/runtime/acp/json-rpc.ts +0 -184
  148. package/src/runtime/acp/provider-resolver.ts +0 -154
  149. package/src/runtime/bridge-session.ts +0 -1549
  150. package/src/runtime/hook-command.ts +0 -73
  151. package/src/runtime/screen-fallback.ts +0 -235
  152. package/src/runtime/screen-share.ts +0 -426
  153. package/src/runtime/scrollback.ts +0 -43
  154. package/src/runtime/usage-report.ts +0 -342
  155. package/src/utils/keep-awake.ts +0 -61
  156. package/src/utils/lan-ip.ts +0 -61
  157. package/web/assets/FileBrowser-wOAfY-xj.js +0 -2
  158. package/web/assets/PortPreview-Cbvv7yzA.js +0 -2
  159. package/web/assets/TerminalPanel-CTSklkct.js +0 -1
  160. package/web/assets/UsageDashboard-CJ1AsNmd.js +0 -1
  161. package/web/assets/index-BsnDaeCt.css +0 -33
  162. package/web/assets/index-q85psYZ5.js +0 -36
  163. package/web/assets/vendor-highlight-NXWAAyIv.js +0 -5
  164. package/web/assets/vendor-react-M7xG3qiw.js +0 -49
  165. package/web/assets/vendor-xterm-D15THcI4.js +0 -9
  166. package/web/index.html +0 -37
  167. package/web/logos/claude.png +0 -0
  168. package/web/logos/codex.png +0 -0
  169. package/web/logos/copilot.png +0 -0
  170. package/web/logos/gemini.png +0 -0
  171. package/web/logos/linkshell-1024.png +0 -0
  172. package/web/logos/linkshell-splash.png +0 -0
  173. package/web/logos/linkshell.png +0 -0
@@ -1,308 +0,0 @@
1
- import { execFile, execFileSync } from "node:child_process";
2
- import { promisify } from "node:util";
3
-
4
- const execFileAsync = promisify(execFile);
5
-
6
- /**
7
- * Host-side agent process discovery, modeled on Open Island's
8
- * ActiveAgentProcessDiscovery: `ps` is the authoritative "is it alive?"
9
- * signal. We classify well-known CLIs (Claude, Codex, Gemini, Copilot,
10
- * OpenCode, Cursor Agent, Kimi) and ignore LinkShell's own control-plane
11
- * subprocesses (codex app-server, Claude stream-json / SDK).
12
- *
13
- * This does not invent a remote conversation protocol for those agents.
14
- * Live processes become visible conversations; prompting still requires
15
- * an official adapter (Codex app-server / Claude stream-json or SDK).
16
- */
17
-
18
- export const KNOWN_AGENT_PROVIDERS = [
19
- "codex",
20
- "claude",
21
- "gemini",
22
- "copilot",
23
- "opencode",
24
- "cursor",
25
- "grok",
26
- "kimi",
27
- "custom",
28
- ] as const;
29
-
30
- export type KnownAgentProvider = (typeof KNOWN_AGENT_PROVIDERS)[number];
31
-
32
- export const PROTOCOL_AGENT_PROVIDERS = new Set<string>(["codex", "claude", "gemini", "cursor", "grok"]);
33
-
34
- export interface AgentProcessSnapshot {
35
- provider: string;
36
- pid: string;
37
- command: string;
38
- tty?: string;
39
- cwd?: string;
40
- sessionId?: string;
41
- parentPid?: string;
42
- }
43
-
44
- export interface RunningProcess {
45
- pid: string;
46
- parentPid: string;
47
- tty?: string;
48
- command: string;
49
- }
50
-
51
- export type ProcessCommandRunner = (executable: string, args: string[]) => string | undefined;
52
-
53
- const UUID_RE = /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/;
54
-
55
- export function agentProviderLabel(provider: string): string {
56
- switch (provider) {
57
- case "codex":
58
- return "Codex";
59
- case "claude":
60
- return "Claude";
61
- case "gemini":
62
- return "Gemini";
63
- case "copilot":
64
- return "Copilot";
65
- case "opencode":
66
- return "OpenCode";
67
- case "cursor":
68
- return "Cursor";
69
- case "grok":
70
- return "Grok";
71
- case "kimi":
72
- return "Kimi";
73
- case "custom":
74
- return "Custom";
75
- default:
76
- return provider;
77
- }
78
- }
79
-
80
- export function isProtocolAgentProvider(provider: string): boolean {
81
- return PROTOCOL_AGENT_PROVIDERS.has(provider);
82
- }
83
-
84
- export function firstCommandToken(command: string): string {
85
- return command.trim().split(/\s+/, 1)[0] ?? "";
86
- }
87
-
88
- export function commandBinaryName(command: string): string {
89
- const token = firstCommandToken(command);
90
- const parts = token.split(/[/\\]/);
91
- return (parts[parts.length - 1] ?? token).toLowerCase();
92
- }
93
-
94
- /** LinkShell-owned control planes must not appear as user sessions. */
95
- export function isControlPlaneCommand(command: string): boolean {
96
- const lowered = command.toLowerCase();
97
- if (/\bapp-server\b/.test(lowered)) return true;
98
- if (lowered.includes("claude-agent-sdk")) return true;
99
- if (lowered.includes("stream-json") && (lowered.includes("--print") || lowered.includes("--output-format"))) {
100
- return true;
101
- }
102
- if (/\b--acp\b/.test(lowered)) return true;
103
- if (commandBinaryName(command) === "cursor-agent" && /\sacp(\s|$)/.test(lowered)) return true;
104
- if (commandBinaryName(command) === "grok" && /\bagent\b/.test(lowered) && /\bstdio\b/.test(lowered)) return true;
105
- return false;
106
- }
107
-
108
- export function classifyAgentCommand(command: string): string | undefined {
109
- if (!command.trim() || isControlPlaneCommand(command)) return undefined;
110
- const lowered = command.toLowerCase();
111
- const first = firstCommandToken(lowered);
112
- const binary = commandBinaryName(command);
113
-
114
- if (binary === "codex" || first.endsWith("/codex") || lowered.includes("/codex/codex")) return "codex";
115
- if (binary === "claude" || first.endsWith("/claude") || lowered.includes("/.local/bin/claude")) return "claude";
116
- if (binary === "cursor-agent" || first.endsWith("/cursor-agent")) return "cursor";
117
- if (isOpenCodeCommand(lowered, first)) return "opencode";
118
- if (
119
- binary === "gemini" ||
120
- first.endsWith("/gemini") ||
121
- lowered.includes("/bin/gemini") ||
122
- lowered.includes("/google/gemini-cli") ||
123
- lowered.includes("@google/gemini-cli")
124
- ) {
125
- return "gemini";
126
- }
127
- if (binary === "kimi" && (first === "kimi" || first.endsWith("/kimi"))) return "kimi";
128
- if (binary === "copilot" || first.endsWith("/copilot")) return "copilot";
129
- if (binary === "grok" || first.endsWith("/grok")) return "grok";
130
- return undefined;
131
- }
132
-
133
- function isOpenCodeCommand(lowered: string, first: string): boolean {
134
- if (
135
- first === "opencode" ||
136
- first === "opencode-ai" ||
137
- first.endsWith("/opencode") ||
138
- first.endsWith("/opencode-ai")
139
- ) {
140
- return true;
141
- }
142
- if (!lowered.includes("opencode")) return false;
143
-
144
- const isPackageRunner =
145
- first === "npx" || first.endsWith("/npx") ||
146
- first === "pnpx" || first.endsWith("/pnpx") ||
147
- first === "bunx" || first.endsWith("/bunx") ||
148
- ((first === "npm" || first.endsWith("/npm")) && (lowered.includes(" exec ") || lowered.includes(" run "))) ||
149
- ((first === "pnpm" || first.endsWith("/pnpm")) && (lowered.includes(" dlx ") || lowered.includes(" exec ") || lowered.includes(" run "))) ||
150
- first === "yarn" || first.endsWith("/yarn");
151
-
152
- if (isPackageRunner) {
153
- const tokens = lowered.split(/\s+/);
154
- const packageIndex = tokens.findIndex((token) => {
155
- if (token.startsWith("@opencode-ai/")) return true;
156
- const base = token.startsWith("@") ? token : (token.split("@")[0] ?? token);
157
- return base === "opencode" || base === "opencode-ai";
158
- });
159
- if (packageIndex >= 0) {
160
- const installLike = new Set(["install", "i", "add", "remove", "rm", "uninstall", "update", "upgrade", "up", "unlink"]);
161
- const isInstall = tokens.slice(0, packageIndex).some((token) => installLike.has(token));
162
- if (!isInstall) return true;
163
- }
164
- }
165
-
166
- const isNode = first === "node" || first.endsWith("/node") || first === "bun" || first.endsWith("/bun");
167
- if (
168
- isNode && (
169
- lowered.includes("/opencode-ai/") ||
170
- lowered.includes("/@opencode-ai/") ||
171
- lowered.includes("/node_modules/opencode/") ||
172
- lowered.includes("/node_modules/opencode-ai/") ||
173
- lowered.includes("/node_modules/@opencode-ai/") ||
174
- /\/\.bin\/opencode(-ai)?(\s|$)/.test(lowered)
175
- )
176
- ) {
177
- return true;
178
- }
179
- return false;
180
- }
181
-
182
- export function parsePsOutput(output: string): RunningProcess[] {
183
- const processes: RunningProcess[] = [];
184
- for (const rawLine of output.split(/\r?\n/)) {
185
- const line = rawLine.trim();
186
- if (!line) continue;
187
- const parts = line.split(/\s+/);
188
- if (parts.length < 4) continue;
189
- const pid = parts[0]!;
190
- const parentPid = parts[1]!;
191
- const ttyRaw = parts[2]!;
192
- const command = parts.slice(3).join(" ").trim();
193
- if (!command) continue;
194
- processes.push({
195
- pid,
196
- parentPid,
197
- tty: normalizeTty(ttyRaw),
198
- command,
199
- });
200
- }
201
- return processes;
202
- }
203
-
204
- function normalizeTty(raw: string): string | undefined {
205
- const trimmed = raw.trim();
206
- if (!trimmed || trimmed === "??") return undefined;
207
- return trimmed.startsWith("/dev/") ? trimmed : `/dev/${trimmed}`;
208
- }
209
-
210
- export function sessionIdFromCommand(command: string): string | undefined {
211
- const tokens = command.split(/\s+/);
212
- for (let index = 0; index < tokens.length; index += 1) {
213
- const token = tokens[index]!;
214
- if (token === "--resume" || token === "-r" || token === "--session-id") {
215
- const next = tokens[index + 1];
216
- const match = next?.match(UUID_RE);
217
- if (match) return match[0].toLowerCase();
218
- }
219
- if (token.startsWith("--resume=") || token.startsWith("--session-id=")) {
220
- const match = token.split("=", 2)[1]?.match(UUID_RE);
221
- if (match) return match[0].toLowerCase();
222
- }
223
- }
224
- return undefined;
225
- }
226
-
227
- export function liveConversationId(snapshot: Pick<AgentProcessSnapshot, "provider" | "pid" | "sessionId">): string {
228
- if (snapshot.sessionId) return `agent-live-${snapshot.provider}-${snapshot.sessionId}`;
229
- return `agent-live-${snapshot.provider}-${snapshot.pid}`;
230
- }
231
-
232
- export function isLiveConversationId(conversationId: string): boolean {
233
- return conversationId.startsWith("agent-live-");
234
- }
235
-
236
- export function discoverAgentProcesses(input?: {
237
- run?: ProcessCommandRunner;
238
- processes?: RunningProcess[];
239
- includeCwd?: boolean;
240
- }): AgentProcessSnapshot[] {
241
- const run = input?.run ?? defaultCommandRunner;
242
- const processes = input?.processes ?? parsePsOutput(run("/bin/ps", ["-Ao", "pid=,ppid=,tty=,command="]) ?? "");
243
- const snapshots: AgentProcessSnapshot[] = [];
244
- const claimed = new Set<string>();
245
-
246
- for (const process of processes) {
247
- const provider = classifyAgentCommand(process.command);
248
- if (!provider) continue;
249
- // Open Island lets OpenCode through without a TTY (IDE terminals).
250
- if (!process.tty && provider !== "opencode") continue;
251
-
252
- const sessionId = sessionIdFromCommand(process.command);
253
- const claimKey = sessionId ? `${provider}:${sessionId}` : `${provider}:${process.tty ?? process.pid}`;
254
- if (claimed.has(claimKey)) continue;
255
- claimed.add(claimKey);
256
-
257
- snapshots.push({
258
- provider,
259
- pid: process.pid,
260
- command: process.command,
261
- tty: process.tty,
262
- // lsof is useful but too expensive for the 3s live poll — it stalls the
263
- // PTY/WebSocket loop. Callers that need cwd pass includeCwd.
264
- cwd: input?.includeCwd ? cwdFromLsof(run, process.pid) : undefined,
265
- sessionId,
266
- parentPid: process.parentPid,
267
- });
268
- }
269
- return snapshots;
270
- }
271
-
272
- /** Non-blocking `ps` scan for the live poll. Never calls lsof. */
273
- export async function discoverAgentProcessesAsync(): Promise<AgentProcessSnapshot[]> {
274
- try {
275
- const { stdout } = await execFileAsync("/bin/ps", ["-Ao", "pid=,ppid=,tty=,command="], {
276
- encoding: "utf8",
277
- timeout: 800,
278
- maxBuffer: 2 * 1024 * 1024,
279
- });
280
- return discoverAgentProcesses({ processes: parsePsOutput(stdout) });
281
- } catch {
282
- return [];
283
- }
284
- }
285
-
286
- function cwdFromLsof(run: ProcessCommandRunner, pid: string): string | undefined {
287
- const output = run("/usr/sbin/lsof", ["-a", "-p", pid, "-Fn"]);
288
- if (!output) return undefined;
289
- const lines = output.split(/\r?\n/);
290
- for (let index = 0; index < lines.length; index += 1) {
291
- if (lines[index] !== "fcwd") continue;
292
- const next = lines[index + 1] ?? "";
293
- if (next.startsWith("n/") ) return next.slice(1);
294
- }
295
- return undefined;
296
- }
297
-
298
- function defaultCommandRunner(executable: string, args: string[]): string | undefined {
299
- try {
300
- return execFileSync(executable, args, {
301
- encoding: "utf8",
302
- timeout: 800,
303
- stdio: ["ignore", "pipe", "ignore"],
304
- });
305
- } catch {
306
- return undefined;
307
- }
308
- }