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,73 +0,0 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
-
4
- /** Pre-marker CLI: `curl -X POST http://127.0.0.1:<port>/hook --data-binary @-`. */
5
- const LEGACY_LOCAL_HOOK =
6
- /https?:\/\/127\.0\.0\.1:\d+\/hook(?:\?|"|'|\s|$|\\)/;
7
-
8
- function hookEntryText(entry: unknown): string {
9
- try {
10
- return JSON.stringify(entry);
11
- } catch {
12
- return String(entry);
13
- }
14
- }
15
-
16
- /** True for current (`/hook?m=lsh-…`) and legacy localhost `/hook` curl entries. */
17
- export function isLinkShellHookEntry(entry: unknown, marker?: string): boolean {
18
- const raw = hookEntryText(entry);
19
- if (marker && raw.includes(`/hook?m=${marker}`)) return true;
20
- if (raw.includes("/hook?m=lsh-")) return true;
21
- if (raw.includes("/hook?m=") && raw.includes("LINKSHELL_ID")) return true;
22
- return LEGACY_LOCAL_HOOK.test(raw) && raw.includes("--data-binary @-");
23
- }
24
-
25
- function sweepHooksObject(hooks: Record<string, unknown>): boolean {
26
- let changed = false;
27
- for (const [eventName, entries] of Object.entries(hooks)) {
28
- if (!Array.isArray(entries)) continue;
29
- const filtered = entries.filter((entry) => !isLinkShellHookEntry(entry));
30
- if (filtered.length !== entries.length) {
31
- changed = true;
32
- if (filtered.length === 0) delete hooks[eventName];
33
- else hooks[eventName] = filtered;
34
- }
35
- }
36
- return changed;
37
- }
38
-
39
- /** Remove leftover LinkShell hook entries from one JSON settings/hooks file. */
40
- export function sweepLinkShellHookEntries(configPath: string): boolean {
41
- if (!existsSync(configPath)) return false;
42
- try {
43
- const raw = JSON.parse(readFileSync(configPath, "utf8")) as Record<string, unknown>;
44
- const hooks = raw.hooks;
45
- if (!hooks || typeof hooks !== "object" || Array.isArray(hooks)) return false;
46
- const changed = sweepHooksObject(hooks as Record<string, unknown>);
47
- if (!changed) return false;
48
- if (Object.keys(hooks as object).length === 0) delete raw.hooks;
49
- writeFileSync(configPath, `${JSON.stringify(raw, null, 2)}\n`);
50
- return true;
51
- } catch {
52
- return false;
53
- }
54
- }
55
-
56
- /**
57
- * One-shot leftover sweep. LinkShell no longer writes hook configs; this only
58
- * deletes historical curl-hook entries so they cannot fire after upgrade.
59
- */
60
- export function sweepLinkShellHookConfigs(home: string, extraPaths: string[] = []): string[] {
61
- const candidates = [
62
- join(home, ".claude", "settings.json"),
63
- join(home, ".claude", "settings.local.json"),
64
- join(home, ".codex", "hooks.json"),
65
- join(home, ".gemini", "settings.json"),
66
- ...extraPaths,
67
- ];
68
- const swept: string[] = [];
69
- for (const path of candidates) {
70
- if (sweepLinkShellHookEntries(path)) swept.push(path);
71
- }
72
- return swept;
73
- }
@@ -1,235 +0,0 @@
1
- import { execSync, spawn } from "node:child_process";
2
- import { readFileSync, unlinkSync, existsSync } from "node:fs";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
- import { platform } from "node:os";
6
- import { createEnvelope, serializeEnvelope } from "@linkshell/protocol";
7
- import type { Envelope } from "@linkshell/protocol";
8
-
9
- export interface ScreenFallbackOptions {
10
- fps: number;
11
- quality: number;
12
- scale: number;
13
- sessionId: string;
14
- onFrame: (envelope: Envelope) => void;
15
- onStatus: (envelope: Envelope) => void;
16
- }
17
-
18
- const CHUNK_SIZE = 48 * 1024; // 48KB per chunk, leave room for envelope overhead
19
- const TMP_FILE = join(tmpdir(), `linkshell-screen-${process.pid}.jpg`);
20
-
21
- export class ScreenFallback {
22
- private timer: ReturnType<typeof setInterval> | undefined;
23
- private frameId = 0;
24
- private active = false;
25
- private readonly options: ScreenFallbackOptions;
26
-
27
- constructor(options: ScreenFallbackOptions) {
28
- this.options = options;
29
- }
30
-
31
- start(): void {
32
- if (this.active) return;
33
- this.active = true;
34
- this.frameId = 0;
35
-
36
- const interval = Math.max(50, Math.floor(1000 / this.options.fps));
37
-
38
- this.options.onStatus(
39
- createEnvelope({
40
- type: "screen.status",
41
- sessionId: this.options.sessionId,
42
- payload: { active: true, mode: "fallback" as const },
43
- }),
44
- );
45
-
46
- // Capture first frame immediately
47
- this.captureAndSend();
48
-
49
- this.timer = setInterval(() => {
50
- this.captureAndSend();
51
- }, interval);
52
- }
53
-
54
- stop(): void {
55
- if (!this.active) return;
56
- this.active = false;
57
- if (this.timer) {
58
- clearInterval(this.timer);
59
- this.timer = undefined;
60
- }
61
- // Clean up temp file
62
- try { if (existsSync(TMP_FILE)) unlinkSync(TMP_FILE); } catch {}
63
-
64
- this.options.onStatus(
65
- createEnvelope({
66
- type: "screen.status",
67
- sessionId: this.options.sessionId,
68
- payload: { active: false, mode: "off" as const },
69
- }),
70
- );
71
- }
72
-
73
- private captureAndSend(): void {
74
- if (!this.active) return;
75
-
76
- try {
77
- const imgBuffer = this.captureScreen();
78
- if (!imgBuffer || imgBuffer.length === 0) return;
79
-
80
- const base64 = imgBuffer.toString("base64");
81
- const frameId = this.frameId++;
82
-
83
- // Get dimensions (approximate from JPEG header or use defaults)
84
- const dims = this.getJpegDimensions(imgBuffer);
85
-
86
- if (base64.length <= CHUNK_SIZE) {
87
- // Single chunk
88
- this.options.onFrame(
89
- createEnvelope({
90
- type: "screen.frame",
91
- sessionId: this.options.sessionId,
92
- payload: {
93
- data: base64,
94
- width: dims.width,
95
- height: dims.height,
96
- frameId,
97
- chunkIndex: 0,
98
- chunkTotal: 1,
99
- },
100
- }),
101
- );
102
- } else {
103
- // Split into chunks
104
- const chunkTotal = Math.ceil(base64.length / CHUNK_SIZE);
105
- for (let i = 0; i < chunkTotal; i++) {
106
- if (!this.active) return;
107
- const chunk = base64.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE);
108
- this.options.onFrame(
109
- createEnvelope({
110
- type: "screen.frame",
111
- sessionId: this.options.sessionId,
112
- payload: {
113
- data: chunk,
114
- width: dims.width,
115
- height: dims.height,
116
- frameId,
117
- chunkIndex: i,
118
- chunkTotal,
119
- },
120
- }),
121
- );
122
- }
123
- }
124
- } catch {
125
- // Silently skip failed frames
126
- }
127
- }
128
-
129
- private captureScreen(): Buffer | null {
130
- const os = platform();
131
- const q = this.options.quality;
132
- const s = this.options.scale;
133
-
134
- try {
135
- if (os === "darwin") {
136
- // macOS: screencapture
137
- execSync(
138
- `screencapture -x -t jpg -C "${TMP_FILE}"`,
139
- { timeout: 3000, stdio: "pipe" },
140
- );
141
- if (!existsSync(TMP_FILE)) return null;
142
- let buf = readFileSync(TMP_FILE);
143
-
144
- // Resize if scale < 1 and sips is available
145
- if (s < 1) {
146
- try {
147
- const w = Math.floor(this.getJpegDimensions(buf).width * s);
148
- execSync(
149
- `sips --resampleWidth ${w} -s formatOptions ${q} "${TMP_FILE}" --out "${TMP_FILE}"`,
150
- { timeout: 3000, stdio: "pipe" },
151
- );
152
- buf = readFileSync(TMP_FILE);
153
- } catch {
154
- // Use original size if resize fails
155
- }
156
- }
157
- return buf;
158
- }
159
-
160
- if (os === "linux") {
161
- // Linux: try import (ImageMagick) or scrot
162
- try {
163
- const resizeArg = s < 1 ? `-resize ${Math.floor(s * 100)}%` : "";
164
- execSync(
165
- `import -window root -quality ${q} ${resizeArg} jpeg:"${TMP_FILE}"`,
166
- { timeout: 3000, stdio: "pipe" },
167
- );
168
- } catch {
169
- // Fallback to scrot
170
- execSync(
171
- `scrot -q ${q} "${TMP_FILE}"`,
172
- { timeout: 3000, stdio: "pipe" },
173
- );
174
- }
175
- if (!existsSync(TMP_FILE)) return null;
176
- return readFileSync(TMP_FILE);
177
- }
178
-
179
- // Windows: use PowerShell
180
- if (os === "win32") {
181
- execSync(
182
- `powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Screen]::PrimaryScreen | ForEach-Object { $bmp = New-Object System.Drawing.Bitmap($_.Bounds.Width, $_.Bounds.Height); $g = [System.Drawing.Graphics]::FromImage($bmp); $g.CopyFromScreen($_.Bounds.Location, [System.Drawing.Point]::Empty, $_.Bounds.Size); $bmp.Save('${TMP_FILE.replace(/\\/g, "\\\\")}', [System.Drawing.Imaging.ImageFormat]::Jpeg) }"`,
183
- { timeout: 5000, stdio: "pipe" },
184
- );
185
- if (!existsSync(TMP_FILE)) return null;
186
- return readFileSync(TMP_FILE);
187
- }
188
-
189
- return null;
190
- } catch {
191
- return null;
192
- }
193
- }
194
-
195
- private getJpegDimensions(buf: Buffer): { width: number; height: number } {
196
- // Parse JPEG SOF marker for dimensions
197
- try {
198
- let i = 2; // Skip SOI
199
- while (i < buf.length - 8) {
200
- if (buf[i] !== 0xff) break;
201
- const marker = buf[i + 1]!;
202
- // SOF0, SOF1, SOF2
203
- if (marker >= 0xc0 && marker <= 0xc2) {
204
- const height = buf.readUInt16BE(i + 5);
205
- const width = buf.readUInt16BE(i + 7);
206
- return { width, height };
207
- }
208
- const len = buf.readUInt16BE(i + 2);
209
- i += 2 + len;
210
- }
211
- } catch {}
212
- return { width: 1920, height: 1080 }; // Default fallback
213
- }
214
-
215
- static isAvailable(): boolean {
216
- const os = platform();
217
- try {
218
- if (os === "darwin") {
219
- execSync("which screencapture", { stdio: "pipe" });
220
- return true;
221
- }
222
- if (os === "linux") {
223
- try {
224
- execSync("which import", { stdio: "pipe" });
225
- return true;
226
- } catch {
227
- execSync("which scrot", { stdio: "pipe" });
228
- return true;
229
- }
230
- }
231
- if (os === "win32") return true; // PowerShell always available
232
- } catch {}
233
- return false;
234
- }
235
- }