linkshell-cli 0.9.0 → 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 (153) 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 +2 -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/setup.js +4 -2
  9. package/dist/cli/src/commands/setup.js.map +1 -1
  10. package/dist/cli/src/index.js +12 -334
  11. package/dist/cli/src/index.js.map +1 -1
  12. package/dist/cli/src/utils/daemon.d.ts +0 -7
  13. package/dist/cli/src/utils/daemon.js +1 -18
  14. package/dist/cli/src/utils/daemon.js.map +1 -1
  15. package/dist/cli/tsconfig.tsbuildinfo +1 -1
  16. package/dist/gateway/src/accounts.d.ts +6 -0
  17. package/dist/gateway/src/accounts.js +32 -0
  18. package/dist/gateway/src/accounts.js.map +1 -0
  19. package/dist/gateway/src/index.d.ts +4 -0
  20. package/dist/gateway/src/index.js +5 -0
  21. package/dist/gateway/src/index.js.map +1 -0
  22. package/dist/gateway/src/main.d.ts +1 -0
  23. package/dist/gateway/src/main.js +57 -0
  24. package/dist/gateway/src/main.js.map +1 -0
  25. package/dist/gateway/src/rate-limit.d.ts +22 -0
  26. package/dist/gateway/src/rate-limit.js +66 -0
  27. package/dist/gateway/src/rate-limit.js.map +1 -0
  28. package/dist/gateway/src/relay.d.ts +84 -0
  29. package/dist/gateway/src/relay.js +434 -0
  30. package/dist/gateway/src/relay.js.map +1 -0
  31. package/dist/gateway/src/serve.d.ts +34 -0
  32. package/dist/gateway/src/serve.js +102 -0
  33. package/dist/gateway/src/serve.js.map +1 -0
  34. package/dist/gateway/src/store.d.ts +35 -0
  35. package/dist/gateway/src/store.js +109 -0
  36. package/dist/gateway/src/store.js.map +1 -0
  37. package/dist/gateway/src/subscription.d.ts +18 -0
  38. package/dist/gateway/src/subscription.js +46 -0
  39. package/dist/gateway/src/subscription.js.map +1 -0
  40. package/dist/host/src/drivers/claude/transcript.d.ts +23 -23
  41. package/dist/host/src/fs.d.ts +3 -3
  42. package/dist/host/src/input.d.ts +4 -4
  43. package/dist/host/src/terminals.d.ts +1 -1
  44. package/dist/wire/src/model.d.ts +6 -6
  45. package/dist/wire/src/relay.d.ts +8 -8
  46. package/dist/wire/src/rpc.d.ts +818 -818
  47. package/dist/wire/src/updates.d.ts +18 -18
  48. package/package.json +4 -13
  49. package/src/commands/gateway.ts +31 -0
  50. package/src/commands/host.ts +13 -2
  51. package/src/commands/setup.ts +4 -2
  52. package/src/index.ts +14 -429
  53. package/src/utils/daemon.ts +2 -27
  54. package/dist/cli/src/config.d.ts +0 -14
  55. package/dist/cli/src/config.js +0 -23
  56. package/dist/cli/src/config.js.map +0 -1
  57. package/dist/cli/src/machine-id.d.ts +0 -6
  58. package/dist/cli/src/machine-id.js +0 -50
  59. package/dist/cli/src/machine-id.js.map +0 -1
  60. package/dist/cli/src/providers.d.ts +0 -13
  61. package/dist/cli/src/providers.js +0 -77
  62. package/dist/cli/src/providers.js.map +0 -1
  63. package/dist/cli/src/runtime/acp/acp-client.d.ts +0 -128
  64. package/dist/cli/src/runtime/acp/acp-client.js +0 -430
  65. package/dist/cli/src/runtime/acp/acp-client.js.map +0 -1
  66. package/dist/cli/src/runtime/acp/agent-process-discovery.d.ts +0 -48
  67. package/dist/cli/src/runtime/acp/agent-process-discovery.js +0 -278
  68. package/dist/cli/src/runtime/acp/agent-process-discovery.js.map +0 -1
  69. package/dist/cli/src/runtime/acp/agent-session.d.ts +0 -62
  70. package/dist/cli/src/runtime/acp/agent-session.js +0 -1081
  71. package/dist/cli/src/runtime/acp/agent-session.js.map +0 -1
  72. package/dist/cli/src/runtime/acp/agent-workspace.d.ts +0 -311
  73. package/dist/cli/src/runtime/acp/agent-workspace.js +0 -6083
  74. package/dist/cli/src/runtime/acp/agent-workspace.js.map +0 -1
  75. package/dist/cli/src/runtime/acp/claude-sdk-client.d.ts +0 -121
  76. package/dist/cli/src/runtime/acp/claude-sdk-client.js +0 -1147
  77. package/dist/cli/src/runtime/acp/claude-sdk-client.js.map +0 -1
  78. package/dist/cli/src/runtime/acp/claude-stream-json-client.d.ts +0 -53
  79. package/dist/cli/src/runtime/acp/claude-stream-json-client.js +0 -552
  80. package/dist/cli/src/runtime/acp/claude-stream-json-client.js.map +0 -1
  81. package/dist/cli/src/runtime/acp/json-rpc.d.ts +0 -22
  82. package/dist/cli/src/runtime/acp/json-rpc.js +0 -160
  83. package/dist/cli/src/runtime/acp/json-rpc.js.map +0 -1
  84. package/dist/cli/src/runtime/acp/provider-resolver.d.ts +0 -16
  85. package/dist/cli/src/runtime/acp/provider-resolver.js +0 -132
  86. package/dist/cli/src/runtime/acp/provider-resolver.js.map +0 -1
  87. package/dist/cli/src/runtime/bridge-session.d.ts +0 -97
  88. package/dist/cli/src/runtime/bridge-session.js +0 -1364
  89. package/dist/cli/src/runtime/bridge-session.js.map +0 -1
  90. package/dist/cli/src/runtime/hook-command.d.ts +0 -9
  91. package/dist/cli/src/runtime/hook-command.js +0 -80
  92. package/dist/cli/src/runtime/hook-command.js.map +0 -1
  93. package/dist/cli/src/runtime/screen-fallback.d.ts +0 -22
  94. package/dist/cli/src/runtime/screen-fallback.js +0 -202
  95. package/dist/cli/src/runtime/screen-fallback.js.map +0 -1
  96. package/dist/cli/src/runtime/screen-share.d.ts +0 -34
  97. package/dist/cli/src/runtime/screen-share.js +0 -376
  98. package/dist/cli/src/runtime/screen-share.js.map +0 -1
  99. package/dist/cli/src/runtime/scrollback.d.ts +0 -11
  100. package/dist/cli/src/runtime/scrollback.js +0 -33
  101. package/dist/cli/src/runtime/scrollback.js.map +0 -1
  102. package/dist/cli/src/runtime/usage-report.d.ts +0 -34
  103. package/dist/cli/src/runtime/usage-report.js +0 -332
  104. package/dist/cli/src/runtime/usage-report.js.map +0 -1
  105. package/dist/cli/src/utils/keep-awake.d.ts +0 -6
  106. package/dist/cli/src/utils/keep-awake.js +0 -48
  107. package/dist/cli/src/utils/keep-awake.js.map +0 -1
  108. package/dist/cli/src/utils/lan-ip.d.ts +0 -9
  109. package/dist/cli/src/utils/lan-ip.js +0 -61
  110. package/dist/cli/src/utils/lan-ip.js.map +0 -1
  111. package/dist/host/src/input-helper.d.ts +0 -1
  112. package/dist/host/src/input-helper.js +0 -564
  113. package/dist/host/src/input-helper.js.map +0 -1
  114. package/dist/shared-protocol/src/index.d.ts +0 -17029
  115. package/dist/shared-protocol/src/index.js +0 -1064
  116. package/dist/shared-protocol/src/index.js.map +0 -1
  117. package/scripts/copy-web.mjs +0 -34
  118. package/src/config.ts +0 -36
  119. package/src/machine-id.ts +0 -61
  120. package/src/providers.ts +0 -97
  121. package/src/runtime/acp/acp-client.ts +0 -521
  122. package/src/runtime/acp/agent-process-discovery.ts +0 -308
  123. package/src/runtime/acp/agent-session.ts +0 -1186
  124. package/src/runtime/acp/agent-workspace.ts +0 -6590
  125. package/src/runtime/acp/claude-sdk-client.ts +0 -1221
  126. package/src/runtime/acp/claude-stream-json-client.ts +0 -633
  127. package/src/runtime/acp/json-rpc.ts +0 -184
  128. package/src/runtime/acp/provider-resolver.ts +0 -154
  129. package/src/runtime/bridge-session.ts +0 -1549
  130. package/src/runtime/hook-command.ts +0 -73
  131. package/src/runtime/screen-fallback.ts +0 -235
  132. package/src/runtime/screen-share.ts +0 -426
  133. package/src/runtime/scrollback.ts +0 -43
  134. package/src/runtime/usage-report.ts +0 -342
  135. package/src/utils/keep-awake.ts +0 -61
  136. package/src/utils/lan-ip.ts +0 -61
  137. package/web/assets/FileBrowser-wOAfY-xj.js +0 -2
  138. package/web/assets/PortPreview-Cbvv7yzA.js +0 -2
  139. package/web/assets/TerminalPanel-CTSklkct.js +0 -1
  140. package/web/assets/UsageDashboard-CJ1AsNmd.js +0 -1
  141. package/web/assets/index-BsnDaeCt.css +0 -33
  142. package/web/assets/index-q85psYZ5.js +0 -36
  143. package/web/assets/vendor-highlight-NXWAAyIv.js +0 -5
  144. package/web/assets/vendor-react-M7xG3qiw.js +0 -49
  145. package/web/assets/vendor-xterm-D15THcI4.js +0 -9
  146. package/web/index.html +0 -37
  147. package/web/logos/claude.png +0 -0
  148. package/web/logos/codex.png +0 -0
  149. package/web/logos/copilot.png +0 -0
  150. package/web/logos/gemini.png +0 -0
  151. package/web/logos/linkshell-1024.png +0 -0
  152. package/web/logos/linkshell-splash.png +0 -0
  153. 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
- }