@nyte-ai/host 0.0.0-stage → 0.0.18

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 (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -2
  3. package/THIRD-PARTY-NOTICES.md +800 -0
  4. package/dist/chunks/canvas-preview-0cmbkp2j.js +65 -0
  5. package/dist/chunks/canvas-preview-0nq75h9k.js +7 -0
  6. package/dist/chunks/canvas-preview-0nrnbt76.js +497 -0
  7. package/dist/chunks/canvas-preview-1sq7z9jq.js +17 -0
  8. package/dist/chunks/canvas-preview-1ta3avah.js +56 -0
  9. package/dist/chunks/canvas-preview-2zdyh9ny.js +26 -0
  10. package/dist/chunks/canvas-preview-31w7xm8z.js +34 -0
  11. package/dist/chunks/canvas-preview-4b6sayhg.js +295 -0
  12. package/dist/chunks/canvas-preview-4e0pxa32.js +63 -0
  13. package/dist/chunks/canvas-preview-518dh8by.js +169 -0
  14. package/dist/chunks/canvas-preview-5hxat7hh.js +239 -0
  15. package/dist/chunks/canvas-preview-5mex725c.js +14 -0
  16. package/dist/chunks/canvas-preview-5z63am63.js +295 -0
  17. package/dist/chunks/canvas-preview-65xqjw7f.js +20 -0
  18. package/dist/chunks/canvas-preview-69fkk9yn.js +95 -0
  19. package/dist/chunks/canvas-preview-75db60xy.js +457 -0
  20. package/dist/chunks/canvas-preview-76fwvd6e.js +272 -0
  21. package/dist/chunks/canvas-preview-8xgt67ka.js +598 -0
  22. package/dist/chunks/canvas-preview-98aj5p34.js +176 -0
  23. package/dist/chunks/canvas-preview-9aqczacs.js +73 -0
  24. package/dist/chunks/canvas-preview-a3yw0j70.js +72 -0
  25. package/dist/chunks/canvas-preview-azg56359.js +68 -0
  26. package/dist/chunks/canvas-preview-d9wwhbtz.js +240 -0
  27. package/dist/chunks/canvas-preview-ehf3vp31.js +411 -0
  28. package/dist/chunks/canvas-preview-evh92ywd.js +369 -0
  29. package/dist/chunks/canvas-preview-ewgspmsd.js +81 -0
  30. package/dist/chunks/canvas-preview-g3qj3y3k.js +156 -0
  31. package/dist/chunks/canvas-preview-h8hshnff.js +63 -0
  32. package/dist/chunks/canvas-preview-j1tnf51w.js +36 -0
  33. package/dist/chunks/canvas-preview-kdqgrstc.js +194 -0
  34. package/dist/chunks/canvas-preview-kn7kvbfj.js +12 -0
  35. package/dist/chunks/canvas-preview-m02v4yy6.js +386 -0
  36. package/dist/chunks/canvas-preview-mgcx2b2p.js +271 -0
  37. package/dist/chunks/canvas-preview-rp4x6y1h.js +144 -0
  38. package/dist/chunks/canvas-preview-rp6fz0w5.js +130 -0
  39. package/dist/chunks/canvas-preview-s94ebwf5.js +112 -0
  40. package/dist/chunks/canvas-preview-senpjwep.js +518 -0
  41. package/dist/chunks/canvas-preview-skzwvw4d.js +23 -0
  42. package/dist/chunks/canvas-preview-srgfpdmf.js +60 -0
  43. package/dist/chunks/canvas-preview-tjhabynd.js +252 -0
  44. package/dist/chunks/canvas-preview-vaatfznm.js +189 -0
  45. package/dist/chunks/canvas-preview-vax9ne4e.js +341 -0
  46. package/dist/chunks/canvas-preview-wg80rbc2.js +46 -0
  47. package/dist/chunks/canvas-preview-xn10d4f8.js +608 -0
  48. package/dist/chunks/canvas-preview-yjz0f7n4.js +50 -0
  49. package/dist/chunks/canvas-preview-ymgz0937.js +587 -0
  50. package/dist/chunks/canvas-preview-yrfz2ak6.js +134 -0
  51. package/dist/chunks/canvas-preview-zaaj99wd.js +244 -0
  52. package/dist/chunks/canvas-preview-zn2d0k9d.js +133 -0
  53. package/dist/src/canvas-preview.d.ts +29 -0
  54. package/dist/src/canvas-preview.js +267 -0
  55. package/dist/src/credentials.d.ts +44 -0
  56. package/dist/src/credentials.js +10 -0
  57. package/dist/src/deploy/cloudflare/worker.d.ts +50 -0
  58. package/dist/src/deploy/cloudflare/worker.js +196 -0
  59. package/dist/src/deploy/vercel/nitro.config.d.ts +2 -0
  60. package/dist/src/deploy/vercel/nitro.config.js +30 -0
  61. package/dist/src/deploy/vercel/server/routes/[...].d.ts +2 -0
  62. package/dist/src/deploy/vercel/server/routes/[...].js +18 -0
  63. package/dist/src/deploy/vercel/server/utils/host.d.ts +13 -0
  64. package/dist/src/deploy/vercel/server/utils/host.js +7 -0
  65. package/dist/src/deploy/vercel/server/utils/repair.d.ts +1 -0
  66. package/dist/src/deploy/vercel/server/utils/repair.js +6 -0
  67. package/dist/src/deploy/vercel/server/utils/step.d.ts +10 -0
  68. package/dist/src/deploy/vercel/server/utils/step.js +6 -0
  69. package/dist/src/deploy/vercel/server/workflows/drive.d.ts +14 -0
  70. package/dist/src/deploy/vercel/server/workflows/drive.js +13 -0
  71. package/dist/src/durable-object.d.ts +14 -0
  72. package/dist/src/durable-object.js +231 -0
  73. package/dist/src/hash.d.ts +1 -0
  74. package/dist/src/hash.js +6 -0
  75. package/dist/src/identity-key.d.ts +11 -0
  76. package/dist/src/identity-key.js +9 -0
  77. package/dist/src/identity.d.ts +12 -0
  78. package/dist/src/identity.js +6 -0
  79. package/dist/src/index.d.ts +21 -0
  80. package/dist/src/index.js +120 -0
  81. package/dist/src/key-file.d.ts +1 -0
  82. package/dist/src/key-file.js +7 -0
  83. package/dist/src/listen-fetch.d.ts +8 -0
  84. package/dist/src/listen-fetch.js +9 -0
  85. package/dist/src/listen.d.ts +4 -0
  86. package/dist/src/listen.js +10 -0
  87. package/dist/src/local/command.d.ts +15 -0
  88. package/dist/src/local/command.js +8 -0
  89. package/dist/src/local/feed.d.ts +7 -0
  90. package/dist/src/local/feed.js +6 -0
  91. package/dist/src/local/files.d.ts +18 -0
  92. package/dist/src/local/files.js +13 -0
  93. package/dist/src/local/git-watch.d.ts +15 -0
  94. package/dist/src/local/git-watch.js +7 -0
  95. package/dist/src/local/git.d.ts +5 -0
  96. package/dist/src/local/git.js +13 -0
  97. package/dist/src/local/github.d.ts +30 -0
  98. package/dist/src/local/github.js +9 -0
  99. package/dist/src/local/identity.d.ts +3 -0
  100. package/dist/src/local/identity.js +7 -0
  101. package/dist/src/local/jobs.d.ts +7 -0
  102. package/dist/src/local/jobs.js +6 -0
  103. package/dist/src/local/legacy-trust.d.ts +10 -0
  104. package/dist/src/local/legacy-trust.js +6 -0
  105. package/dist/src/local/patch.d.ts +16 -0
  106. package/dist/src/local/patch.js +17 -0
  107. package/dist/src/local/provider.d.ts +11 -0
  108. package/dist/src/local/provider.js +20 -0
  109. package/dist/src/local/snapshots.d.ts +7 -0
  110. package/dist/src/local/snapshots.js +11 -0
  111. package/dist/src/local/text.d.ts +1 -0
  112. package/dist/src/local/text.js +6 -0
  113. package/dist/src/local/tree.d.ts +50 -0
  114. package/dist/src/local/tree.js +16 -0
  115. package/dist/src/local/workspace-migration.d.ts +2 -0
  116. package/dist/src/local/workspace-migration.js +7 -0
  117. package/dist/src/local/worktrees.d.ts +46 -0
  118. package/dist/src/local/worktrees.js +14 -0
  119. package/dist/src/mcp/config.d.ts +75 -0
  120. package/dist/src/mcp/config.js +12 -0
  121. package/dist/src/mcp/connect.d.ts +28 -0
  122. package/dist/src/mcp/connect.js +9 -0
  123. package/dist/src/mcp/hub.d.ts +19 -0
  124. package/dist/src/mcp/hub.js +11 -0
  125. package/dist/src/mcp/oauth.d.ts +46 -0
  126. package/dist/src/mcp/oauth.js +12 -0
  127. package/dist/src/mcp/tools.d.ts +29 -0
  128. package/dist/src/mcp/tools.js +12 -0
  129. package/dist/src/open.d.ts +42 -0
  130. package/dist/src/open.js +44 -0
  131. package/dist/src/plugins/loader.d.ts +21 -0
  132. package/dist/src/plugins/loader.js +11 -0
  133. package/dist/src/postgres.d.ts +19 -0
  134. package/dist/src/postgres.js +8 -0
  135. package/dist/src/providers.d.ts +16 -0
  136. package/dist/src/providers.js +112 -0
  137. package/dist/src/secrets.d.ts +12 -0
  138. package/dist/src/secrets.js +8 -0
  139. package/dist/src/serve/binding.d.ts +39 -0
  140. package/dist/src/serve/binding.js +6 -0
  141. package/dist/src/serve/handler.d.ts +15 -0
  142. package/dist/src/serve/handler.js +8 -0
  143. package/dist/src/serve/stream-server.d.ts +6 -0
  144. package/dist/src/serve/stream-server.js +6 -0
  145. package/dist/src/session-index.d.ts +11 -0
  146. package/dist/src/session-index.js +11 -0
  147. package/dist/src/shell-env.d.ts +14 -0
  148. package/dist/src/shell-env.js +108 -0
  149. package/dist/src/sqlite-workers.d.ts +6 -0
  150. package/dist/src/sqlite-workers.js +6 -0
  151. package/dist/src/sqlite.d.ts +7 -0
  152. package/dist/src/sqlite.js +10 -0
  153. package/dist/src/store-owner.d.ts +15 -0
  154. package/dist/src/store-owner.js +9 -0
  155. package/dist/src/store-rows.d.ts +13 -0
  156. package/dist/src/store-rows.js +26 -0
  157. package/dist/src/store-usage.d.ts +25 -0
  158. package/dist/src/store-usage.js +16 -0
  159. package/dist/src/usage-scan.d.ts +24 -0
  160. package/dist/src/usage-scan.js +65 -0
  161. package/dist/src/usage-service.d.ts +22 -0
  162. package/dist/src/usage-service.js +12 -0
  163. package/dist/src/usage-summary.d.ts +9 -0
  164. package/dist/src/usage-summary.js +16 -0
  165. package/dist/src/usage-types.d.ts +86 -0
  166. package/dist/src/usage-types.js +0 -0
  167. package/dist/src/usage-worker.d.ts +1 -0
  168. package/dist/src/usage-worker.js +25 -0
  169. package/dist/src/usage.d.ts +72 -0
  170. package/dist/src/usage.js +13 -0
  171. package/package.json +132 -3
  172. package/src/canvas-preview.ts +402 -0
  173. package/src/credentials.ts +294 -0
  174. package/src/deploy/cloudflare/README.md +3 -0
  175. package/src/deploy/cloudflare/worker.ts +306 -0
  176. package/src/deploy/cloudflare/wrangler.jsonc +12 -0
  177. package/src/deploy/vercel/README.md +3 -0
  178. package/src/deploy/vercel/nitro.config.ts +29 -0
  179. package/src/deploy/vercel/server/routes/[...].ts +10 -0
  180. package/src/deploy/vercel/server/utils/host.ts +104 -0
  181. package/src/deploy/vercel/server/utils/repair.ts +35 -0
  182. package/src/deploy/vercel/server/utils/step.ts +25 -0
  183. package/src/deploy/vercel/server/workflows/drive.ts +46 -0
  184. package/src/deploy/vercel/tsconfig.json +9 -0
  185. package/src/deploy/vercel/types/bun.d.ts +2 -0
  186. package/src/durable-object.ts +386 -0
  187. package/src/hash.ts +5 -0
  188. package/src/identity-key.ts +20 -0
  189. package/src/identity.ts +66 -0
  190. package/src/index.ts +21 -0
  191. package/src/key-file.ts +53 -0
  192. package/src/listen-fetch.ts +409 -0
  193. package/src/listen.ts +228 -0
  194. package/src/local/command.ts +109 -0
  195. package/src/local/feed.ts +70 -0
  196. package/src/local/files.ts +812 -0
  197. package/src/local/git-watch.ts +189 -0
  198. package/src/local/git.ts +914 -0
  199. package/src/local/github.ts +692 -0
  200. package/src/local/identity.ts +21 -0
  201. package/src/local/jobs.ts +587 -0
  202. package/src/local/legacy-trust.ts +105 -0
  203. package/src/local/patch.ts +334 -0
  204. package/src/local/provider.ts +184 -0
  205. package/src/local/snapshots.ts +776 -0
  206. package/src/local/text.ts +9 -0
  207. package/src/local/tree.ts +398 -0
  208. package/src/local/workspace-migration.ts +95 -0
  209. package/src/local/worktrees.ts +239 -0
  210. package/src/mcp/config.ts +330 -0
  211. package/src/mcp/connect.ts +105 -0
  212. package/src/mcp/hub.ts +836 -0
  213. package/src/mcp/oauth.ts +436 -0
  214. package/src/mcp/tools.ts +333 -0
  215. package/src/open.ts +469 -0
  216. package/src/plugins/loader.ts +186 -0
  217. package/src/postgres.ts +492 -0
  218. package/src/providers.ts +135 -0
  219. package/src/secrets.ts +31 -0
  220. package/src/serve/binding.ts +628 -0
  221. package/src/serve/handler.ts +362 -0
  222. package/src/serve/stream-server.ts +176 -0
  223. package/src/session-index.ts +99 -0
  224. package/src/shell-env.ts +153 -0
  225. package/src/sqlite-workers.ts +283 -0
  226. package/src/sqlite.ts +528 -0
  227. package/src/store-owner.ts +98 -0
  228. package/src/store-rows.ts +110 -0
  229. package/src/store-usage.ts +344 -0
  230. package/src/usage-scan.ts +81 -0
  231. package/src/usage-service.ts +73 -0
  232. package/src/usage-summary.ts +79 -0
  233. package/src/usage-types.ts +87 -0
  234. package/src/usage-worker.ts +24 -0
  235. package/src/usage.ts +701 -0
@@ -0,0 +1,628 @@
1
+ import { isJson, SdkError } from "@nyte-ai/schema";
2
+ import type {
3
+ ByteReader,
4
+ Capability,
5
+ CapabilityWire,
6
+ HostView,
7
+ HostViewWire,
8
+ JobsChange,
9
+ JobView,
10
+ Json,
11
+ OutputFrame,
12
+ PageId,
13
+ PageInfo,
14
+ PatchSet,
15
+ PatchSetWire,
16
+ PrincipalId,
17
+ ReceiptId,
18
+ SessionId,
19
+ WorkspaceId,
20
+ } from "@nyte-ai/schema";
21
+ import type {
22
+ BrowserApi,
23
+ Connection,
24
+ FilesApi,
25
+ GitApi,
26
+ GitHubApi,
27
+ Job,
28
+ Login,
29
+ Page,
30
+ Receipt,
31
+ } from "@nyte-ai/core";
32
+ import type { ServerHost } from "@nyte-ai/core/server";
33
+ import { mapped, viewFrames } from "@nyte-ai/protocol";
34
+ import type {
35
+ ByteName,
36
+ CallName,
37
+ FrameName,
38
+ OperationInput,
39
+ OperationOutput,
40
+ } from "@nyte-ai/protocol";
41
+
42
+ /** Returns what a row's binding receives besides its input: the request's body (the bytes of `files.write`, `job.write` and `files.upload`) and the client's abort signal. */
43
+ export type BindingRequest = {
44
+ readonly body: ReadableStream<Uint8Array> | null;
45
+ readonly signal: AbortSignal;
46
+ };
47
+
48
+ /** Returns a byte row's answer: the reader and the headers the response adds (`X-Nyte-Version` for `files.bytes`, §40p). */
49
+ type ByteBody = {
50
+ readonly reader: ByteReader;
51
+ readonly headers?: Readonly<Record<string, string>>;
52
+ };
53
+
54
+ /** Returns the bindings of the rows answered once. */
55
+ export type CallBinding = {
56
+ readonly [N in CallName]: (
57
+ input: OperationInput<N>,
58
+ request: BindingRequest,
59
+ ) => Promise<OperationOutput<N>>;
60
+ };
61
+
62
+ /** Returns the bindings of the rows answered with JSON frames. */
63
+ export type FrameBinding = {
64
+ readonly [N in FrameName]: (
65
+ input: OperationInput<N>,
66
+ request: BindingRequest,
67
+ ) => Promise<AsyncIterable<OperationOutput<N>>>;
68
+ };
69
+
70
+ /** Returns the bindings of the rows answered with bytes. */
71
+ export type ByteBinding = {
72
+ readonly [N in ByteName]: (
73
+ input: OperationInput<N>,
74
+ request: BindingRequest,
75
+ ) => Promise<ByteBody>;
76
+ };
77
+
78
+ /** Returns the binding of `job.output`, whose frames are framed bytes and control frames (§40h). */
79
+ type OutputBinding = {
80
+ readonly "job.output": (
81
+ input: OperationInput<"job.output">,
82
+ request: BindingRequest,
83
+ ) => Promise<AsyncIterable<OutputFrame>>;
84
+ };
85
+
86
+ /** Returns the server side of the table for one connection: each row takes its decoded input and the request's body and abort signal, and answers its output, its frames, or a byte reader; setup failures (authority, capability, unknown ids) reject before any frame. */
87
+ export type Binding = CallBinding & FrameBinding & ByteBinding & OutputBinding;
88
+
89
+ /** Describes what a binding needs beyond the connection: the server port, for the rows that carry a wire-only id (§40o) and `parts.create`. */
90
+ export type BindingOptions = {
91
+ readonly server: ServerHost;
92
+ };
93
+
94
+ const LOGIN_LIFETIME = 10 * 60 * 1000;
95
+
96
+ type Attempt = {
97
+ readonly login: Login;
98
+ readonly timer: ReturnType<typeof setTimeout>;
99
+ steps: AsyncIterator<OperationOutput<"login.steps">> | null;
100
+ };
101
+
102
+ const attempts = new WeakMap<ServerHost, Map<string, Attempt>>();
103
+
104
+ const holds = new WeakMap<ServerHost, Map<string, Page[]>>();
105
+
106
+ /** Returns each row bound to the call it names: ids bind handles on the connection, handles returned become ids, `Set`s and `Map`s become their wire forms; `head.move`, `files.batch` and `browser.open` go through the server port's wire operations with their wire id (§40o, §40x). `head.watch` sends the head's view from the host's fold whole, then each later view as its changes (`viewFrames`), the core watch marking the principal's seen tip (§40a, §54a). Authority is the caller's gate: the server checks each row's before binding. */
107
+ export function bindConnection(connection: Connection, options: BindingOptions): Binding {
108
+ const { wire } = options.server;
109
+ const { principal } = connection;
110
+ const logins = attempts.get(options.server) ?? new Map<string, Attempt>();
111
+ attempts.set(options.server, logins);
112
+ const pages = holds.get(options.server) ?? new Map<string, Page[]>();
113
+ holds.set(options.server, pages);
114
+
115
+ const session = (id: SessionId) => connection.session(id);
116
+ const workspace = (id: WorkspaceId) => connection.workspace(id);
117
+
118
+ const files = async (id: WorkspaceId): Promise<FilesApi> =>
119
+ available(await workspace(id).files(), "files");
120
+
121
+ const git = async (id: WorkspaceId): Promise<GitApi> =>
122
+ available(await workspace(id).git(), "git");
123
+
124
+ const github = async (id: WorkspaceId): Promise<GitHubApi> =>
125
+ available(await workspace(id).github(), "github");
126
+
127
+ const browser = async (id: WorkspaceId): Promise<BrowserApi> =>
128
+ available(await workspace(id).browser(), "browser");
129
+
130
+ const findLogin = (provider: string): Attempt =>
131
+ logins.get(principalKey(principal, provider)) ??
132
+ fail("unknown", `No login for ${provider} is in progress.`);
133
+
134
+ const endLogin = (provider: string, attempt: Attempt): void => {
135
+ clearTimeout(attempt.timer);
136
+ const key = principalKey(principal, provider);
137
+
138
+ if (logins.get(key) === attempt) logins.delete(key);
139
+ void attempt.steps?.return?.();
140
+ };
141
+
142
+ const hold = (page: Page): Page => {
143
+ const key = principalKey(principal, page.id);
144
+ const known = pages.get(key) ?? [];
145
+ pages.set(key, known.includes(page) ? known : [...known, page]);
146
+
147
+ return page;
148
+ };
149
+
150
+ const release = async (page: PageId): Promise<void> => {
151
+ const key = principalKey(principal, page);
152
+ const [last, ...rest] = [...(pages.get(key) ?? [])].reverse();
153
+
154
+ if (rest.length === 0) pages.delete(key);
155
+ else pages.set(key, rest.reverse());
156
+ await last?.close();
157
+ };
158
+
159
+ const findPage = async (at: WorkspaceId, id: PageId): Promise<Page> =>
160
+ pages.get(principalKey(principal, id))?.[0] ??
161
+ wire.page(id) ??
162
+ hold(await (await browser(at)).adopt(id));
163
+
164
+ const describePage = async (page: Page): Promise<PageInfo> => {
165
+ const view = await page.view();
166
+
167
+ return { id: page.id, url: view.url, title: view.title, holders: wire.holders(page.id) };
168
+ };
169
+
170
+ const findReceipt = (at: SessionId | null, id: ReceiptId): Receipt<unknown> | undefined =>
171
+ at === null ? wire.batchReceipt(id) : session(at).receipt(id);
172
+
173
+ const startedJob = (job: Job): OperationOutput<"jobs.start"> => ({
174
+ ...(wire.job(job.id) ?? fail("unknown", `Job ${job.id} is gone.`)),
175
+ key: job.key,
176
+ });
177
+
178
+ return {
179
+ "connection.watch": async () => mapped(connection.watch(), (view) => hostWire(view, principal)),
180
+ "connection.blob": async ({ id }) => ({ reader: await connection.blob(id) }),
181
+
182
+ "models.list": () => connection.models.list(),
183
+ "models.accounts": ({ provider }) => connection.models.accounts(provider),
184
+ "models.selectAccount": async ({ provider, account }) => {
185
+ await connection.models.selectAccount(provider, account);
186
+
187
+ return null;
188
+ },
189
+ "models.usage": () => connection.models.usage(),
190
+ "models.refresh": async ({ provider }) => {
191
+ await connection.models.refresh(provider);
192
+
193
+ return null;
194
+ },
195
+ "models.setDefault": ({ ref }) => connection.models.setDefault(ref),
196
+ "models.verify": ({ provider }) => connection.models.verify(provider),
197
+ "models.login": async (input) => {
198
+ const { provider } = input;
199
+ const login = connection.models.login(input);
200
+ const key = principalKey(principal, provider);
201
+ const previous = logins.get(key);
202
+
203
+ if (previous !== undefined) endLogin(provider, previous);
204
+
205
+ const attempt: Attempt = {
206
+ login,
207
+ steps: null,
208
+ timer: setTimeout(() => endLogin(provider, attempt), LOGIN_LIFETIME),
209
+ };
210
+
211
+ logins.set(key, attempt);
212
+
213
+ return null;
214
+ },
215
+ "models.logout": async ({ provider }) => {
216
+ await connection.models.logout(provider);
217
+
218
+ return null;
219
+ },
220
+ "login.steps": async ({ provider }) => {
221
+ const attempt = findLogin(provider);
222
+
223
+ if (attempt.steps !== null)
224
+ fail("invalid", `The login for ${provider} is already streaming.`);
225
+ const steps = attempt.login.steps()[Symbol.asyncIterator]();
226
+ attempt.steps = steps;
227
+
228
+ return {
229
+ [Symbol.asyncIterator]: () => ({
230
+ async next() {
231
+ const next = await steps.next();
232
+
233
+ if (next.done === true || next.value.kind === "done" || next.value.kind === "failed")
234
+ endLogin(provider, attempt);
235
+
236
+ return next;
237
+ },
238
+ async return() {
239
+ endLogin(provider, attempt);
240
+
241
+ return { done: true, value: undefined };
242
+ },
243
+ }),
244
+ };
245
+ },
246
+ "login.answer": async ({ provider, code }) => {
247
+ await findLogin(provider).login.answer(code);
248
+
249
+ return null;
250
+ },
251
+
252
+ "settings.apply": ({ id, value }) => connection.settings.apply(id, value),
253
+
254
+ "workspaces.list": () => connection.workspaces.list(),
255
+ "workspaces.inspect": ({ target }) => connection.workspaces.inspect(target),
256
+ "workspaces.add": ({ target, options }) => connection.workspaces.add(target, options),
257
+ "workspaces.forget": ({ id }) => connection.workspaces.forget(id),
258
+
259
+ "sessions.watch": async (input) => connection.sessions.watch(input),
260
+ "sessions.start": async (input) => {
261
+ const { session, receipt } = await connection.sessions.start(input);
262
+
263
+ return { session: session.id, receipt: receipt.id, message: receipt.message };
264
+ },
265
+ "sessions.list": (input) => connection.sessions.list(input),
266
+
267
+ "session.info": ({ session: id }) => session(id).info(),
268
+ "session.update": ({ session: id, patch }) => session(id).update(patch),
269
+ "session.remove": ({ session: id }) => session(id).remove(),
270
+ "session.stop": ({ session: id }) => session(id).stop(),
271
+ "session.command": ({ session: id, name, text }) => session(id).command(name, text),
272
+ "session.completeCommand": ({ session: id, name, prefix }) =>
273
+ session(id).completeCommand(name, prefix),
274
+
275
+ "head.send": async ({ session: id, head, content, options }) => {
276
+ const receipt = await session(id).head(head).send(content, options);
277
+
278
+ return { receipt: receipt.id, message: receipt.message };
279
+ },
280
+ "head.configure": ({ session: id, head, patch }) => session(id).head(head).configure(patch),
281
+ "head.view": ({ session: id, head, options }) => session(id).head(head).view(options),
282
+ "head.watch": async ({ session: id, head, options }) => {
283
+ await session(id).info();
284
+
285
+ return viewFrames(session(id).head(head).watch(options));
286
+ },
287
+ "head.history": ({ session: id, head, before, limit }) =>
288
+ session(id).head(head).history({ before, limit }),
289
+ "head.tree": ({ session: id, head }) => session(id).head(head).tree(),
290
+ "head.move": async ({ session: id, head, position, options, id: receipt }) => ({
291
+ receipt: (await wire.move(id, head, position, { ...options, id: receipt })).id,
292
+ }),
293
+ "head.compact": ({ session: id, head, instructions }) =>
294
+ session(id).head(head).compact({ instructions }),
295
+
296
+ "heads.create": ({ session: id, name, input }) => session(id).heads.create(name, input),
297
+ "heads.merge": ({ session: id, name, options }) => session(id).heads.merge(name, options),
298
+ "heads.remove": ({ session: id, name, options }) => session(id).heads.remove(name, options),
299
+
300
+ "run.stop": ({ session: id, run }) => session(id).run(run).stop(),
301
+ "run.settled": ({ session: id, run }, { signal }) => session(id).run(run).settled({ signal }),
302
+
303
+ "receipt.status": async ({ session: id, receipt }) => {
304
+ const status = (await findReceipt(id, receipt)?.status()) ?? { kind: "missing" };
305
+
306
+ return status.kind === "settled"
307
+ ? { kind: "settled", outcome: toJson(status.outcome) }
308
+ : status;
309
+ },
310
+ "receipt.settled": async ({ session: id, receipt }, { signal }) => {
311
+ const handle =
312
+ findReceipt(id, receipt) ?? fail("unknown", `No receipt has the id ${receipt}.`);
313
+
314
+ return toJson(await handle.settled({ signal }));
315
+ },
316
+ "receipt.stop": async ({ session: id, receipt }) => {
317
+ const stopped = (await findReceipt(id, receipt)?.stop()) ?? { kind: "missing" };
318
+
319
+ return stopped.kind === "settled"
320
+ ? { kind: "settled", outcome: toJson(stopped.outcome) }
321
+ : stopped;
322
+ },
323
+ "receipt.landed": async ({ session: id, receipt }) =>
324
+ wire
325
+ .sendReceipt(id ?? fail("invalid", "Only a send's receipt has landed."), receipt)
326
+ .landed(),
327
+
328
+ "parts.create": async ({ session: id, kind, input }) => {
329
+ const definition =
330
+ (await wire.partDefinition(id, kind)) ??
331
+ fail("unknown", `No part definition has the kind ${kind}.`);
332
+
333
+ return { part: (await session(id).parts.create(definition, input)).id };
334
+ },
335
+ "part.update": ({ session: id, part, next, options }) =>
336
+ session(id).part(part).update(next, options),
337
+ "part.act": async ({ session: id, part, action, input }) => {
338
+ const outcome = await session(id).part(part).act(action, input);
339
+
340
+ return outcome.kind === "ok" ? { kind: "ok", output: toJson(outcome.output) } : outcome;
341
+ },
342
+ "part.answer": async ({ session: id, part }, { signal }) => {
343
+ const answer = await session(id).part(part).answer({ signal });
344
+
345
+ return answer === null
346
+ ? null
347
+ : { action: String(answer.action), input: toJson(answer.input) };
348
+ },
349
+ "part.close": async ({ session: id, part }) => {
350
+ await session(id).part(part).close();
351
+
352
+ return null;
353
+ },
354
+
355
+ "workspace.info": ({ workspace: id }) => workspace(id).info(),
356
+ "workspace.catalog": ({ workspace: id }) => workspace(id).catalog(),
357
+ "workspace.command": ({ workspace: id, name, text }) => workspace(id).command(name, text),
358
+ "workspace.completeCommand": ({ workspace: id, name, prefix }) =>
359
+ workspace(id).completeCommand(name, prefix),
360
+ "workspace.watch": async ({ workspace: id }) => workspace(id).watch(),
361
+ "mcp.watch": async ({ workspace: id }) => workspace(id).mcp.watch(),
362
+ "mcp.reconnect": ({ workspace: id, server }) => workspace(id).mcp.reconnect(server),
363
+ "mcp.setEnabled": ({ workspace: id, server, enabled }) =>
364
+ workspace(id).mcp.setEnabled(server, enabled),
365
+ "mcp.login": async ({ workspace: id, server }) => {
366
+ const login = JSON.stringify(["mcp", id, server]);
367
+ const previous = logins.get(principalKey(principal, login));
368
+
369
+ if (previous !== undefined) endLogin(login, previous);
370
+
371
+ const attempt: Attempt = {
372
+ login: workspace(id).mcp.login(server),
373
+ steps: null,
374
+ timer: setTimeout(() => endLogin(login, attempt), LOGIN_LIFETIME),
375
+ };
376
+
377
+ logins.set(principalKey(principal, login), attempt);
378
+
379
+ return { login };
380
+ },
381
+ "mcp.logout": ({ workspace: id, server }) => workspace(id).mcp.logout(server),
382
+ "mcp.add": ({ workspace: id, server, config, scope }) =>
383
+ workspace(id).mcp.add(server, config, scope),
384
+ "mcp.remove": ({ workspace: id, server, scope }) => workspace(id).mcp.remove(server, scope),
385
+ "workspace.update": ({ workspace: id, patch }) => workspace(id).update(patch),
386
+ "workspace.reload": ({ workspace: id }) => workspace(id).reload(),
387
+ "workspace.changes": ({ workspace: id, run }) => workspace(id).changes(run),
388
+ "workspace.patches": async ({ workspace: id, run, paths, options }) =>
389
+ mapped(workspace(id).patches(run, paths, options), patchesWire),
390
+ "workspace.files": async ({ workspace: id }) => capabilityWire(await workspace(id).files()),
391
+ "workspace.git": async ({ workspace: id }) => capabilityWire(await workspace(id).git()),
392
+ "workspace.github": async ({ workspace: id }) => capabilityWire(await workspace(id).github()),
393
+ "workspace.browser": async ({ workspace: id }) => capabilityWire(await workspace(id).browser()),
394
+
395
+ "jobs.start": async ({ workspace: id, command, session, title, pty, id: job }) =>
396
+ startedJob(await workspace(id).jobs.start({ command, session, title, pty, id: job })),
397
+ "jobs.recover": async ({ workspace: id, key }) => {
398
+ const recovered = await workspace(id).jobs.recover(key);
399
+
400
+ return "id" in recovered ? startedJob(recovered) : recovered;
401
+ },
402
+ "jobs.list": ({ workspace: id }) => workspace(id).jobs.list(),
403
+ "jobs.watch": async ({ workspace: id }) => jobChanges(workspace(id).jobs.watch()),
404
+
405
+ "job.write": async ({ workspace: id, job }, { body }) => {
406
+ await workspace(id)
407
+ .job(job)
408
+ .write(await readBytes(body));
409
+
410
+ return null;
411
+ },
412
+ "job.output": async ({ workspace: id, job }) => workspace(id).job(job).output(),
413
+ "job.resize": ({ workspace: id, job, size }) => workspace(id).job(job).resize(size),
414
+ "job.idle": ({ workspace: id, job }) => workspace(id).job(job).idle(),
415
+ "job.stop": ({ workspace: id, job }) => workspace(id).job(job).stop(),
416
+ "job.settled": ({ workspace: id, job }, { signal }) =>
417
+ workspace(id).job(job).settled({ signal }),
418
+
419
+ "files.read": async ({ workspace: id, path }) => (await files(id)).read(path),
420
+ "files.bytes": async ({ workspace: id, path }) => {
421
+ const reader = await (await files(id)).bytes(path);
422
+
423
+ const version =
424
+ "version" in reader && typeof reader.version === "string" ? reader.version : null;
425
+
426
+ return version === null ? { reader } : { reader, headers: { "X-Nyte-Version": version } };
427
+ },
428
+ "files.stat": async ({ workspace: id, path }) => (await files(id)).stat(path),
429
+ "files.write": async ({ workspace: id, path, options }, { body }) =>
430
+ (await files(id)).write(path, await readBytes(body), options),
431
+ "files.remove": async ({ workspace: id, path, options }) =>
432
+ (await files(id)).remove(path, options),
433
+ "files.batch": async ({ workspace: id, entries, id: receipt }) => ({
434
+ receipt: (await wire.batch(id, await files(id), entries, receipt)).id,
435
+ }),
436
+ "files.format": async ({ workspace: id, path, contents }) =>
437
+ (await files(id)).format(path, contents),
438
+ "files.scan": async ({ workspace: id, directory, ignored }) =>
439
+ (await files(id)).scan({ directory, ignored }),
440
+ "files.find": async ({ workspace: id, query, limit }) =>
441
+ (await files(id)).find({ query, limit }),
442
+ "files.search": async ({ workspace: id, ...input }) => (await files(id)).search(input),
443
+ "files.watch": async ({ workspace: id, paths }) => (await files(id)).watch({ paths }),
444
+ "files.upload": async ({ workspace: id }, { body }) =>
445
+ (await files(id)).upload(readChunks(body)),
446
+
447
+ "git.status": async ({ workspace: id }) => (await git(id)).status(),
448
+ "git.watch": async ({ workspace: id }) => (await git(id)).watch(),
449
+ "git.changes": async ({ workspace: id, scope, options }) =>
450
+ (await git(id)).changes(scope, options),
451
+ "git.watchChanges": async ({ workspace: id, scope, options }) =>
452
+ (await git(id)).watchChanges(scope, options),
453
+ "git.patches": async ({ workspace: id, scope, paths, options }) =>
454
+ mapped((await git(id)).patches(scope, paths, options), patchesWire),
455
+ "git.contents": async ({ workspace: id, path, scope }) => (await git(id)).contents(path, scope),
456
+ "git.log": async ({ workspace: id, cursor, limit }) => (await git(id)).log({ cursor, limit }),
457
+ "git.refs": async ({ workspace: id }) => (await git(id)).refs(),
458
+ "git.blame": async ({ workspace: id, path }) => (await git(id)).blame(path),
459
+ "git.stage": async ({ workspace: id, paths, options }) => (await git(id)).stage(paths, options),
460
+ "git.unstage": async ({ workspace: id, paths, options }) =>
461
+ (await git(id)).unstage(paths, options),
462
+ "git.discard": async ({ workspace: id, paths, options }) =>
463
+ (await git(id)).discard(paths, options),
464
+ "git.commit": async ({ workspace: id, input, options }) =>
465
+ (await git(id)).commit(input, options),
466
+ "git.branch": async ({ workspace: id, input, options }) =>
467
+ (await git(id)).branch(input, options),
468
+ "git.push": async ({ workspace: id, input, options }) => (await git(id)).push(input, options),
469
+
470
+ "github.account": () => connection.github.account(),
471
+ "github.signIn": () => connection.github.signIn(),
472
+ "github.signOut": () => connection.github.signOut(),
473
+ "github.state": async ({ workspace: id }) => (await github(id)).state(),
474
+ "github.createPullRequest": async ({ workspace: id, input }) =>
475
+ (await github(id)).createPullRequest(input),
476
+
477
+ "browser.open": async ({ workspace: id, url, id: key }) => {
478
+ const api = await browser(id);
479
+
480
+ return describePage(
481
+ hold(
482
+ await wire.openPage({
483
+ id: key,
484
+ input: { workspace: id, url },
485
+ open: () => api.open({ url }),
486
+ }),
487
+ ),
488
+ );
489
+ },
490
+ "browser.adopt": async ({ workspace: id, id: page }) =>
491
+ describePage(hold(await (await browser(id)).adopt(page))),
492
+ "browser.list": async ({ workspace: id }) => (await browser(id)).list(),
493
+ "browser.watch": async ({ workspace: id }) => (await browser(id)).watch(),
494
+
495
+ "page.view": async ({ workspace: id, page }) => (await findPage(id, page)).view(),
496
+ "page.watch": async ({ workspace: id, page }) => (await findPage(id, page)).watch(),
497
+ "page.snapshot": async ({ workspace: id, page, ref }) =>
498
+ (await findPage(id, page)).snapshot({ ref }),
499
+ "page.navigate": async ({ workspace: id, page, url }) =>
500
+ (await findPage(id, page)).navigate(url),
501
+ "page.back": async ({ workspace: id, page }) => (await findPage(id, page)).back(),
502
+ "page.forward": async ({ workspace: id, page }) => (await findPage(id, page)).forward(),
503
+ "page.reload": async ({ workspace: id, page }) => (await findPage(id, page)).reload(),
504
+ "page.stopLoading": async ({ workspace: id, page }) => (await findPage(id, page)).stopLoading(),
505
+ "page.click": async ({ workspace: id, page, ref, button, double, expect }) =>
506
+ (await findPage(id, page)).click({ ref, button, double, expect }),
507
+ "page.type": async ({ workspace: id, page, ref, text, clear, submit, expect }) =>
508
+ (await findPage(id, page)).type({ ref, text, clear, submit, expect }),
509
+ "page.press": async ({ workspace: id, page, key, ref }) =>
510
+ (await findPage(id, page)).press({ key, ref }),
511
+ "page.scroll": async ({ workspace: id, page, direction, ref, pages: count }) =>
512
+ (await findPage(id, page)).scroll({ direction, ref, pages: count }),
513
+ "page.wait": async ({ workspace: id, page, condition }) =>
514
+ (await findPage(id, page)).wait(condition),
515
+ "page.console": async ({ workspace: id, page, limit, clear }) =>
516
+ (await findPage(id, page)).console({ limit, clear }),
517
+ "page.capture": async ({ workspace: id, page }) => ({
518
+ reader: await (await findPage(id, page)).capture(),
519
+ }),
520
+ "page.evaluate": async ({ workspace: id, page, expression, options }) =>
521
+ (await findPage(id, page)).evaluate(expression, options),
522
+ "page.close": async ({ page }) => {
523
+ await release(page);
524
+
525
+ return null;
526
+ },
527
+
528
+ "plugins.replace": ({ plugins, scope }) => connection.plugins.replace(plugins, scope),
529
+ "plugins.check": ({ plugins }) => connection.plugins.check(plugins),
530
+
531
+ "principals.list": async () =>
532
+ (await connection.principals.list()).map((view) => ({
533
+ ...view,
534
+ authority: [...view.authority],
535
+ })),
536
+ "principals.pair": ({ authority, label }) =>
537
+ connection.principals.pair({ authority: new Set(authority), label }),
538
+ "principals.revoke": async ({ id }) => {
539
+ await connection.principals.revoke(id);
540
+
541
+ return null;
542
+ },
543
+ };
544
+ }
545
+
546
+ /** The wire form of a job list watch: the whole list first, then each job the next list moved (a new view object) or dropped, alone. */
547
+ async function* jobChanges(lists: AsyncIterable<readonly JobView[]>): AsyncIterable<JobsChange> {
548
+ let previous: readonly JobView[] | null = null;
549
+
550
+ for await (const jobs of lists) {
551
+ if (previous === null) yield { kind: "jobs", jobs };
552
+ else {
553
+ const kept = new Set(jobs.map((job) => job.id));
554
+ const unmoved = new Set(previous);
555
+
556
+ for (const job of previous) if (!kept.has(job.id)) yield { kind: "forgotten", id: job.id };
557
+
558
+ for (const job of jobs) if (!unmoved.has(job)) yield { kind: "job", job };
559
+ }
560
+
561
+ previous = jobs;
562
+ }
563
+ }
564
+
565
+ async function* readChunks(body: ReadableStream<Uint8Array> | null): AsyncGenerator<Uint8Array> {
566
+ if (body === null) return;
567
+ const reader = body.getReader();
568
+
569
+ try {
570
+ for (;;) {
571
+ const read = await reader.read();
572
+
573
+ if (read.done) return;
574
+ yield read.value;
575
+ }
576
+ } finally {
577
+ reader.releaseLock();
578
+ }
579
+ }
580
+
581
+ async function readBytes(body: ReadableStream<Uint8Array> | null): Promise<Uint8Array> {
582
+ const chunks: Uint8Array[] = [];
583
+
584
+ for await (const chunk of readChunks(body)) chunks.push(chunk);
585
+ const bytes = new Uint8Array(chunks.reduce((size, chunk) => size + chunk.byteLength, 0));
586
+ let offset = 0;
587
+
588
+ for (const chunk of chunks) {
589
+ bytes.set(chunk, offset);
590
+ offset += chunk.byteLength;
591
+ }
592
+
593
+ return bytes;
594
+ }
595
+
596
+ function hostWire(view: HostView, principal: PrincipalId): HostViewWire {
597
+ return { ...view, authority: [...view.authority], principal };
598
+ }
599
+
600
+ function patchesWire(set: PatchSet): PatchSetWire {
601
+ return { revision: set.revision, entries: Object.fromEntries(set.entries) };
602
+ }
603
+
604
+ function capabilityWire<T>(capability: Capability<T>): CapabilityWire {
605
+ if (capability.kind !== "available") return capability;
606
+
607
+ return { kind: "available" };
608
+ }
609
+
610
+ function available<T>(capability: Capability<T>, what: string): T {
611
+ if (capability.kind === "available") return capability.api;
612
+
613
+ if (capability.kind === "denied") fail("denied", `${what}: ${capability.reason}`);
614
+
615
+ return fail("unavailable", `${what}: ${capability.reason}`);
616
+ }
617
+
618
+ function toJson(value: unknown): Json {
619
+ return isJson(value) ? value : null;
620
+ }
621
+
622
+ function principalKey(principal: PrincipalId, name: string): string {
623
+ return JSON.stringify([principal, name]);
624
+ }
625
+
626
+ function fail(code: "unknown" | "invalid" | "denied" | "unavailable", message: string): never {
627
+ throw new SdkError(code, message);
628
+ }