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
@@ -0,0 +1,109 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ export class GatewayStore {
3
+ db;
4
+ constructor(path) {
5
+ this.db = new DatabaseSync(path);
6
+ this.db.exec(`
7
+ PRAGMA journal_mode = WAL;
8
+ CREATE TABLE IF NOT EXISTS peers (
9
+ id TEXT PRIMARY KEY,
10
+ role TEXT NOT NULL,
11
+ sign_key TEXT NOT NULL,
12
+ box_key TEXT NOT NULL,
13
+ name TEXT NOT NULL,
14
+ platform TEXT,
15
+ user_id TEXT,
16
+ last_seen INTEGER NOT NULL
17
+ );
18
+ CREATE INDEX IF NOT EXISTS peers_by_user ON peers (user_id);
19
+ CREATE TABLE IF NOT EXISTS links (
20
+ machine_id TEXT NOT NULL,
21
+ device_id TEXT NOT NULL,
22
+ created_at INTEGER NOT NULL,
23
+ PRIMARY KEY (machine_id, device_id)
24
+ ) WITHOUT ROWID;
25
+ CREATE INDEX IF NOT EXISTS links_by_device ON links (device_id);
26
+ `);
27
+ }
28
+ close() {
29
+ this.db.close();
30
+ }
31
+ peer(id) {
32
+ const row = this.db.prepare("SELECT * FROM peers WHERE id = ?").get(id);
33
+ return row ? toPeer(row) : undefined;
34
+ }
35
+ upsertPeer(peer) {
36
+ this.db
37
+ .prepare(`INSERT INTO peers (id, role, sign_key, box_key, name, platform, user_id, last_seen)
38
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
39
+ ON CONFLICT(id) DO UPDATE SET box_key = excluded.box_key, name = excluded.name,
40
+ platform = excluded.platform, user_id = excluded.user_id, last_seen = excluded.last_seen`)
41
+ .run(peer.id, peer.role, peer.signKey, peer.boxKey, peer.name, peer.platform ?? null, peer.userId ?? null, peer.lastSeen);
42
+ }
43
+ touch(id, at = Date.now()) {
44
+ this.db.prepare("UPDATE peers SET last_seen = ? WHERE id = ?").run(at, id);
45
+ }
46
+ link(machineId, deviceId, at = Date.now()) {
47
+ this.db.prepare("INSERT OR IGNORE INTO links (machine_id, device_id, created_at) VALUES (?, ?, ?)").run(machineId, deviceId, at);
48
+ }
49
+ unlink(machineId, deviceId) {
50
+ return this.db.prepare("DELETE FROM links WHERE machine_id = ? AND device_id = ?").run(machineId, deviceId).changes > 0;
51
+ }
52
+ /**
53
+ * Takes a peer off its account: the account's other peers no longer reach it, or list it. Its
54
+ * keys and pairings stay, and signing in again puts it back.
55
+ */
56
+ disown(id) {
57
+ return this.db.prepare("UPDATE peers SET user_id = NULL WHERE id = ? AND user_id IS NOT NULL").run(id).changes > 0;
58
+ }
59
+ linkedAt(machineId, deviceId) {
60
+ const row = this.db.prepare("SELECT created_at FROM links WHERE machine_id = ? AND device_id = ?").get(machineId, deviceId);
61
+ return row?.created_at;
62
+ }
63
+ /** Peers of the other role that `id` may reach: linked, or owned by the same account. */
64
+ reachable(id) {
65
+ const self = this.peer(id);
66
+ if (!self)
67
+ return [];
68
+ const other = self.role === "machine" ? "device" : "machine";
69
+ const linked = (self.role === "machine"
70
+ ? this.db.prepare("SELECT p.*, l.created_at AS paired_at FROM links l JOIN peers p ON p.id = l.device_id WHERE l.machine_id = ?").all(id)
71
+ : this.db.prepare("SELECT p.*, l.created_at AS paired_at FROM links l JOIN peers p ON p.id = l.machine_id WHERE l.device_id = ?").all(id));
72
+ const out = new Map();
73
+ for (const row of linked)
74
+ out.set(String(row.id), { peer: toPeer(row), via: "paired", pairedAt: Number(row.paired_at) });
75
+ if (self.userId) {
76
+ const owned = this.db.prepare("SELECT * FROM peers WHERE user_id = ? AND role = ?").all(self.userId, other);
77
+ for (const row of owned)
78
+ if (!out.has(String(row.id)))
79
+ out.set(String(row.id), { peer: toPeer(row), via: "account" });
80
+ }
81
+ return [...out.values()];
82
+ }
83
+ /** How `from` may reach `to`, or undefined when it may not. */
84
+ route(from, to) {
85
+ const a = this.peer(from);
86
+ const b = this.peer(to);
87
+ if (!a || !b || a.role === b.role)
88
+ return undefined;
89
+ const [machine, device] = a.role === "machine" ? [a, b] : [b, a];
90
+ if (this.linkedAt(machine.id, device.id) !== undefined)
91
+ return "paired";
92
+ if (a.userId && a.userId === b.userId)
93
+ return "account";
94
+ return undefined;
95
+ }
96
+ }
97
+ function toPeer(row) {
98
+ return {
99
+ id: String(row.id),
100
+ role: row.role,
101
+ signKey: String(row.sign_key),
102
+ boxKey: String(row.box_key),
103
+ name: String(row.name),
104
+ platform: row.platform ? String(row.platform) : undefined,
105
+ userId: row.user_id ? String(row.user_id) : undefined,
106
+ lastSeen: Number(row.last_seen),
107
+ };
108
+ }
109
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../../gateway/src/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { GatewayOptions } from "./relay.js";
2
+ export interface SubscriptionCheck {
3
+ /** "unknown": the lookup itself failed, which says nothing about the account. */
4
+ status: "active" | "inactive" | "unknown";
5
+ reason?: string;
6
+ }
7
+ export type CheckSubscription = (userId: string) => Promise<SubscriptionCheck>;
8
+ export declare function supabaseSubscriptions(supabase: {
9
+ url: string;
10
+ anonKey: string;
11
+ serviceRoleKey: string;
12
+ }): CheckSubscription;
13
+ /**
14
+ * The official gateway's rule for who may connect: a computer needs an active
15
+ * Pro account. Devices only reach computers they're paired with or share an
16
+ * account with, so they need nothing more.
17
+ */
18
+ export declare function requirePro(check: CheckSubscription, warn: (message: string) => void): NonNullable<GatewayOptions["admit"]>;
@@ -0,0 +1,46 @@
1
+ export function supabaseSubscriptions(supabase) {
2
+ return async (userId) => {
3
+ if (!supabase.url || !supabase.anonKey)
4
+ return { status: "unknown", reason: "missing_supabase_config" };
5
+ if (!supabase.serviceRoleKey)
6
+ return { status: "unknown", reason: "missing_service_role_key" };
7
+ try {
8
+ const response = await fetch(`${supabase.url}/rest/v1/profiles?id=eq.${userId}&select=plan,plan_expires_at&limit=1`, {
9
+ headers: { Authorization: `Bearer ${supabase.serviceRoleKey}`, apikey: supabase.anonKey },
10
+ signal: AbortSignal.timeout(5_000),
11
+ });
12
+ if (!response.ok)
13
+ return { status: "unknown", reason: `profile_lookup_http_${response.status}` };
14
+ const profiles = (await response.json());
15
+ const profile = profiles[0];
16
+ if (!profile)
17
+ return { status: "unknown", reason: "profile_not_found" };
18
+ const active = profile.plan === "pro" && !!profile.plan_expires_at && new Date(profile.plan_expires_at) > new Date();
19
+ return { status: active ? "active" : "inactive" };
20
+ }
21
+ catch {
22
+ return { status: "unknown", reason: "profile_lookup_failed" };
23
+ }
24
+ };
25
+ }
26
+ /**
27
+ * The official gateway's rule for who may connect: a computer needs an active
28
+ * Pro account. Devices only reach computers they're paired with or share an
29
+ * account with, so they need nothing more.
30
+ */
31
+ export function requirePro(check, warn) {
32
+ return async ({ role, userId }) => {
33
+ if (role !== "machine")
34
+ return undefined;
35
+ if (!userId)
36
+ return "官方网关需要登录 Pro 账号:在电脑上运行 linkshell login";
37
+ const subscription = await check(userId);
38
+ // A lookup failure isn't a lapsed subscription; don't lock people out over it.
39
+ if (subscription.status === "inactive")
40
+ return "官方网关需要 Pro 订阅:https://itool.tech";
41
+ if (subscription.status === "unknown")
42
+ warn(`subscription check unavailable (${subscription.reason}); admitting ${userId}`);
43
+ return undefined;
44
+ };
45
+ }
46
+ //# sourceMappingURL=subscription.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription.js","sourceRoot":"","sources":["../../../../gateway/src/subscription.ts"],"names":[],"mappings":"AAcA,MAAM,UAAU,qBAAqB,CAAC,QAAkE;IACtG,OAAO,KAAK,EAAE,MAAM,EAAE,EAAE;QACtB,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO;YAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,yBAAyB,EAAE,CAAC;QACxG,IAAI,CAAC,QAAQ,CAAC,cAAc;YAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,0BAA0B,EAAE,CAAC;QAC/F,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,CAAC,GAAG,2BAA2B,MAAM,sCAAsC,EAAE;gBACnH,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,OAAO,EAAE;gBACzF,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC;aACnC,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,uBAAuB,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YACjG,MAAM,QAAQ,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAuD,CAAC;YAC/F,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC5B,IAAI,CAAC,OAAO;gBAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;YACxE,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,OAAO,CAAC,eAAe,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;YACrH,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;QACpD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;QAChE,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,KAAwB,EAAE,IAA+B;IAClF,OAAO,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;QAChC,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QACzC,IAAI,CAAC,MAAM;YAAE,OAAO,wCAAwC,CAAC;QAC7D,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;QACzC,+EAA+E;QAC/E,IAAI,YAAY,CAAC,MAAM,KAAK,UAAU;YAAE,OAAO,kCAAkC,CAAC;QAClF,IAAI,YAAY,CAAC,MAAM,KAAK,SAAS;YAAE,IAAI,CAAC,mCAAmC,YAAY,CAAC,MAAM,gBAAgB,MAAM,EAAE,CAAC,CAAC;QAC5H,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC"}
@@ -33,7 +33,6 @@ export declare function transcriptLine(raw: string, options?: {
33
33
  /** Parses a whole transcript file. */
34
34
  /** When each transcript update happened, for history import. */
35
35
  export declare const transcriptTimes: WeakMap<{
36
- sessionUpdate: "user_message_chunk";
37
36
  content: {
38
37
  type: "text";
39
38
  text: string;
@@ -43,15 +42,14 @@ export declare const transcriptTimes: WeakMap<{
43
42
  data?: string | undefined;
44
43
  uri?: string | undefined;
45
44
  } | {
46
- name: string;
47
45
  type: "resource_link";
46
+ name: string;
48
47
  uri: string;
49
48
  kind?: "agent" | "file" | "skill" | undefined;
50
49
  };
50
+ sessionUpdate: "user_message_chunk";
51
51
  messageId?: string | undefined;
52
52
  } | {
53
- sessionUpdate: "agent_message_chunk";
54
- messageId: string;
55
53
  content: {
56
54
  type: "text";
57
55
  text: string;
@@ -61,15 +59,15 @@ export declare const transcriptTimes: WeakMap<{
61
59
  data?: string | undefined;
62
60
  uri?: string | undefined;
63
61
  } | {
64
- name: string;
65
62
  type: "resource_link";
63
+ name: string;
66
64
  uri: string;
67
65
  kind?: "agent" | "file" | "skill" | undefined;
68
66
  };
67
+ sessionUpdate: "agent_message_chunk";
68
+ messageId: string;
69
69
  parentToolCallId?: string | undefined;
70
70
  } | {
71
- sessionUpdate: "agent_thought_chunk";
72
- messageId: string;
73
71
  content: {
74
72
  type: "text";
75
73
  text: string;
@@ -79,16 +77,18 @@ export declare const transcriptTimes: WeakMap<{
79
77
  data?: string | undefined;
80
78
  uri?: string | undefined;
81
79
  } | {
82
- name: string;
83
80
  type: "resource_link";
81
+ name: string;
84
82
  uri: string;
85
83
  kind?: "agent" | "file" | "skill" | undefined;
86
84
  };
85
+ sessionUpdate: "agent_thought_chunk";
86
+ messageId: string;
87
87
  parentToolCallId?: string | undefined;
88
88
  } | {
89
- status: "pending" | "in_progress" | "completed" | "failed";
89
+ status: "failed" | "pending" | "in_progress" | "completed";
90
+ kind: "search" | "read" | "edit" | "delete" | "move" | "execute" | "think" | "fetch" | "other";
90
91
  title: string;
91
- kind: "read" | "edit" | "delete" | "move" | "search" | "execute" | "think" | "fetch" | "other";
92
92
  toolCallId: string;
93
93
  sessionUpdate: "tool_call";
94
94
  detail?: {
@@ -98,12 +98,12 @@ export declare const transcriptTimes: WeakMap<{
98
98
  } | {
99
99
  type: "subagent";
100
100
  action: "message" | "spawn" | "wait" | "stop" | "resume" | "list";
101
- model?: string | undefined;
102
101
  task?: string | undefined;
103
102
  agentType?: string | undefined;
103
+ model?: string | undefined;
104
104
  } | {
105
- name: string;
106
105
  type: "skill";
106
+ name: string;
107
107
  description?: string | undefined;
108
108
  } | {
109
109
  type: "web_search";
@@ -132,8 +132,8 @@ export declare const transcriptTimes: WeakMap<{
132
132
  data?: string | undefined;
133
133
  uri?: string | undefined;
134
134
  } | {
135
- name: string;
136
135
  type: "resource_link";
136
+ name: string;
137
137
  uri: string;
138
138
  kind?: "agent" | "file" | "skill" | undefined;
139
139
  };
@@ -161,6 +161,8 @@ export declare const transcriptTimes: WeakMap<{
161
161
  } | {
162
162
  toolCallId: string;
163
163
  sessionUpdate: "tool_call_update";
164
+ status?: "failed" | "pending" | "in_progress" | "completed" | undefined;
165
+ title?: string | undefined;
164
166
  detail?: {
165
167
  type: "mcp";
166
168
  tool: string;
@@ -168,12 +170,12 @@ export declare const transcriptTimes: WeakMap<{
168
170
  } | {
169
171
  type: "subagent";
170
172
  action: "message" | "spawn" | "wait" | "stop" | "resume" | "list";
171
- model?: string | undefined;
172
173
  task?: string | undefined;
173
174
  agentType?: string | undefined;
175
+ model?: string | undefined;
174
176
  } | {
175
- name: string;
176
177
  type: "skill";
178
+ name: string;
177
179
  description?: string | undefined;
178
180
  } | {
179
181
  type: "web_search";
@@ -191,8 +193,6 @@ export declare const transcriptTimes: WeakMap<{
191
193
  text: string;
192
194
  more?: number | undefined;
193
195
  } | undefined;
194
- status?: "pending" | "in_progress" | "completed" | "failed" | undefined;
195
- title?: string | undefined;
196
196
  content?: ({
197
197
  type: "content";
198
198
  content: {
@@ -204,8 +204,8 @@ export declare const transcriptTimes: WeakMap<{
204
204
  data?: string | undefined;
205
205
  uri?: string | undefined;
206
206
  } | {
207
- name: string;
208
207
  type: "resource_link";
208
+ name: string;
209
209
  uri: string;
210
210
  kind?: "agent" | "file" | "skill" | undefined;
211
211
  };
@@ -259,14 +259,14 @@ export declare const transcriptTimes: WeakMap<{
259
259
  parentToolCallId?: string | undefined;
260
260
  } | {
261
261
  options: {
262
- name: string;
263
262
  values: {
264
- name: string;
265
263
  value: string;
264
+ name: string;
266
265
  description?: string | undefined;
267
266
  }[];
268
267
  id: string;
269
- category: "model" | "other" | "mode" | "effort";
268
+ name: string;
269
+ category: "mode" | "other" | "model" | "effort";
270
270
  current: string;
271
271
  }[];
272
272
  sessionUpdate: "ls_config";
@@ -283,15 +283,15 @@ export declare const transcriptTimes: WeakMap<{
283
283
  stopReason?: "error" | "end_turn" | "cancelled" | "max_tokens" | "refusal" | undefined;
284
284
  } | {
285
285
  options: {
286
+ optionId: string;
286
287
  name: string;
287
288
  kind: "allow_once" | "allow_always" | "reject_once" | "reject_always";
288
- optionId: string;
289
289
  }[];
290
290
  title: string;
291
291
  requestId: string;
292
292
  sessionUpdate: "ls_permission";
293
- detail?: string | undefined;
294
293
  toolCallId?: string | undefined;
294
+ detail?: string | undefined;
295
295
  questions?: {
296
296
  id: string;
297
297
  kind: "text" | "choice" | "choices";
@@ -4,8 +4,8 @@ export declare function listDirectory(input: {
4
4
  files?: boolean;
5
5
  }): Promise<{
6
6
  entries: {
7
- name: string;
8
7
  path: string;
8
+ name: string;
9
9
  file?: boolean | undefined;
10
10
  project?: boolean | undefined;
11
11
  size?: number | undefined;
@@ -15,8 +15,8 @@ export declare function listDirectory(input: {
15
15
  home: string;
16
16
  } | {
17
17
  entries: {
18
- name: string;
19
18
  path: string;
19
+ name: string;
20
20
  file?: boolean | undefined;
21
21
  project?: boolean | undefined;
22
22
  size?: number | undefined;
@@ -35,8 +35,8 @@ export declare function searchDirectories(input: {
35
35
  limit: number;
36
36
  }, budgetMs?: number): Promise<{
37
37
  entries: {
38
- name: string;
39
38
  path: string;
39
+ name: string;
40
40
  file?: boolean | undefined;
41
41
  project?: boolean | undefined;
42
42
  size?: number | undefined;
@@ -107,25 +107,92 @@ export interface HelperStatus {
107
107
  trusted: boolean;
108
108
  recording: boolean;
109
109
  app: string;
110
+ /** The app can send the screen as a video track (it carries the media engine). */
111
+ video: boolean;
110
112
  }
113
+ /** A display as the app sees it. `screen` is its number in everything said to the app. */
114
+ export interface AppDisplay {
115
+ screen: number;
116
+ name: string;
117
+ /** In pixels. */
118
+ w: number;
119
+ h: number;
120
+ main: boolean;
121
+ }
122
+ /** The picture asked of the app for a pipe that loses nothing: rates in bits a second. */
123
+ export interface StreamProfile {
124
+ width: number;
125
+ fps: number;
126
+ bitrate: number;
127
+ ceiling: number;
128
+ }
129
+ /** A stream under way. */
130
+ export interface AppStream {
131
+ /** Another profile, without stopping: a new size starts a new generation, at a keyframe. */
132
+ set(profile: StreamProfile): void;
133
+ /** The next frame a keyframe. */
134
+ key(): void;
135
+ end(): void;
136
+ }
137
+ /** How the app is to offer a screen as a video track. */
138
+ export interface VideoOffer {
139
+ /** Which of the system's displays. */
140
+ screen: number;
141
+ iceServers: {
142
+ urls: string[];
143
+ }[];
144
+ /** Frames a second; the app's own choice when left out. */
145
+ fps?: number;
146
+ }
147
+ /** What a viewer answers the app's offer with, and the candidates it finds. */
148
+ export type VideoSignal = {
149
+ t: "rtc.answer";
150
+ sdp: string;
151
+ } | {
152
+ t: "rtc.ice";
153
+ candidate: string;
154
+ sdpMid: string | null;
155
+ sdpMLineIndex: number | null;
156
+ };
157
+ export declare const videoSignal: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
158
+ t: z.ZodLiteral<"rtc.answer">;
159
+ sdp: z.ZodString;
160
+ }, "strip", z.ZodTypeAny, {
161
+ sdp: string;
162
+ t: "rtc.answer";
163
+ }, {
164
+ sdp: string;
165
+ t: "rtc.answer";
166
+ }>, z.ZodObject<{
167
+ t: z.ZodLiteral<"rtc.ice">;
168
+ candidate: z.ZodString;
169
+ sdpMid: z.ZodNullable<z.ZodString>;
170
+ sdpMLineIndex: z.ZodNullable<z.ZodNumber>;
171
+ }, "strip", z.ZodTypeAny, {
172
+ t: "rtc.ice";
173
+ candidate: string;
174
+ sdpMid: string | null;
175
+ sdpMLineIndex: number | null;
176
+ }, {
177
+ t: "rtc.ice";
178
+ candidate: string;
179
+ sdpMid: string | null;
180
+ sdpMLineIndex: number | null;
181
+ }>]>;
111
182
  /**
112
- * The app as it came in the package, made runnable. Packing a package drops
113
- * the program's permission to run (pnpm stores every file that isn't a `bin`
114
- * as a plain one): it is given back here, and where the installation can't be
115
- * written to (a root-owned one), a copy under the user's own directory is used.
116
- * Neither touches the signature.
183
+ * The app out of the archive it is shipped in, unpacked under the user's own
184
+ * directory. An archive, because a package can carry neither the links a
185
+ * framework is made of nor a program's permission to run; unpacked whole, the
186
+ * app is as it was signed. Always at the same place, so that an upgrade
187
+ * replaces the app instead of adding another: macOS has been seen to ask
188
+ * about recording again for the same app run from a new path. (A host still
189
+ * running the app being replaced keeps the one it has open.)
117
190
  */
118
- export declare function usableApp(packaged: string, copyTo?: string): string | undefined;
119
- /** The signed app this package was made with, if it was. */
191
+ export declare function unpackedApp(archive: string, at?: string): string | undefined;
192
+ /** LinkShell.app, where this installation has it: a Mac with Apple silicon, with `@linkshell/mac` installed beside the host. */
120
193
  export declare function shippedApp(): string | undefined;
121
- /** The helper compiled here; built on first use, once per version of its source. */
122
- export declare function inputHelper(log?: (message: string) => void): Promise<string>;
123
- /**
124
- * What this process itself (so: the host, and the capture it starts) is
125
- * allowed to do, and under which app's name. `ask` has the system put its
126
- * question for that permission, and opens the settings page for it.
127
- */
128
- export declare function hostAccess(log: (message: string) => void, ask?: "recording" | "control"): Promise<HelperStatus>;
194
+ /** What to tell someone whose Mac has no LinkShell.app: it is an Intel Mac (the app is built for Apple silicon only), or the package that carries it wasn't installed. */
195
+ export declare const NO_APP: string;
129
196
  type Route = (message: Record<string, unknown>) => void;
130
197
  declare class HelperApp {
131
198
  private readonly app;
@@ -138,6 +205,7 @@ declare class HelperApp {
138
205
  private readonly routes;
139
206
  private readonly watchers;
140
207
  private waiting;
208
+ private listing;
141
209
  constructor(app: string, dryRun: boolean, log: (message: string) => void);
142
210
  /** The app's end of the socket; opens the app when it isn't there. */
143
211
  private link;
@@ -146,17 +214,34 @@ declare class HelperApp {
146
214
  private write;
147
215
  /** What the app may do. It says so as it connects; asked again, it looks again. */
148
216
  access(): Promise<HelperStatus>;
149
- /** The system's dialog and its settings page, for the app. */
150
- ask(what?: "control" | "recording"): Promise<void>;
217
+ /** The displays there are to watch. */
218
+ displays(): Promise<AppDisplay[]>;
219
+ /**
220
+ * Has the app capture a screen and encode it for a pipe that loses nothing:
221
+ * the socket to a viewer it couldn't reach directly. Whole frames come to
222
+ * `on.frame`, each with the generation of picture size it belongs to.
223
+ */
224
+ stream(screen: number, profile: StreamProfile, on: {
225
+ frame(unit: Buffer, key: boolean, generation: number): void;
226
+ exit(error?: string): void;
227
+ }): Promise<AppStream>;
228
+ /**
229
+ * Opens LinkShell's own window for its permissions, on this computer: it says
230
+ * what each is for, has the system ask, takes the user to the switch, and
231
+ * ticks each off as it is turned on. Nothing appears when both are allowed
232
+ * already, and there is only ever one such window.
233
+ */
234
+ ask(): Promise<void>;
151
235
  /**
152
- * Has the app run a screen capture: as the app's child, the recording is the
153
- * app's to be allowed. The picture comes down a socket of its own; the
154
- * returned function ends the capture.
236
+ * Has the app offer a screen to one viewer as a video track. What the app
237
+ * says of it (`rtc.offer`, `rtc.ice`, `rtc.state`, `rtc.error`, and `gone`
238
+ * when the app itself went) comes to `route`; the viewer's answer goes back
239
+ * with `signal`. The picture and the viewer's hands then travel between the
240
+ * two of them, not through here.
155
241
  */
156
- capture(exec: string, args: string[], on: {
157
- data(chunk: Buffer): void;
158
- exit(code: number, errors: string): void;
159
- }): Promise<() => void>;
242
+ offerVideo(id: string, offer: VideoOffer, route: Route): Promise<void>;
243
+ signal(id: string, signal: VideoSignal): void;
244
+ endVideo(id: string): void;
160
245
  join(id: string, screen: number, route: Route, watcher: (status: HelperStatus) => void): Promise<void>;
161
246
  send(id: string, event: InputEvent): void;
162
247
  leave(id: string, watcher: (status: HelperStatus) => void): void;
@@ -171,19 +256,19 @@ export type { HelperApp };
171
256
  export declare class InputControl {
172
257
  private readonly listener;
173
258
  private readonly log;
174
- /** `app: false` keeps to a child process even when the package has the app (tests). */
259
+ /** `app: false` runs the app's program as this process's child instead of having the system open it (tests). */
175
260
  private readonly options;
176
261
  private child?;
177
262
  private joined?;
178
263
  private stopped;
179
264
  private started;
180
265
  constructor(listener: InputListener, log: (message: string) => void,
181
- /** `app: false` keeps to a child process even when the package has the app (tests). */
266
+ /** `app: false` runs the app's program as this process's child instead of having the system open it (tests). */
182
267
  options?: {
183
268
  dryRun?: boolean;
184
269
  app?: boolean;
185
270
  });
186
- /** Starts the helper for the display ffmpeg calls "Capture screen `screen`". Reports through the listener; never throws. */
271
+ /** Starts control of the display the app numbers `screen`. Reports through the listener; never throws. */
187
272
  start(screen: number): Promise<void>;
188
273
  private joinApp;
189
274
  private spawn;