pinomad 0.6.0 → 0.8.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 (30) hide show
  1. package/README.md +6 -59
  2. package/dist/{chunk-wqvp1fwh.js → chunk-1r8a2434.js} +38 -15
  3. package/dist/{chunk-ywmm63ks.js → chunk-3p1eysm9.js} +4 -2
  4. package/dist/{chunk-d5ypmmr7.js → chunk-73vxwjs7.js} +2 -2
  5. package/dist/chunk-a4b61tx5.js +2984 -0
  6. package/dist/{chunk-k2s1hfq5.js → chunk-cytnycs9.js} +9 -9
  7. package/dist/{chunk-3dnnaz4j.js → chunk-ddh6wfv2.js} +8 -8
  8. package/dist/{chunk-jn7y815x.js → chunk-fes7wjw1.js} +5 -5
  9. package/dist/chunk-jgd38rna.js +622 -0
  10. package/dist/{chunk-4btj08we.js → chunk-jjtr7fr5.js} +5608 -2777
  11. package/dist/{chunk-7qb9tq54.js → chunk-khcpv54k.js} +64 -310
  12. package/dist/{chunk-7s1f4z4g.js → chunk-mv94ksvb.js} +22 -5
  13. package/dist/{chunk-367n9je8.js → chunk-p15hqqjz.js} +1 -1
  14. package/dist/{chunk-c8nxym2z.js → chunk-p56xf1r8.js} +1 -1
  15. package/dist/{chunk-ggb1taqy.js → chunk-ptjyk94f.js} +5 -5
  16. package/dist/{chunk-0xh6cy9v.js → chunk-r2ssa2ex.js} +1 -1
  17. package/dist/chunk-sygeb9qb.js +430 -0
  18. package/dist/{chunk-b1h374jr.js → chunk-v414b5pp.js} +8 -2
  19. package/dist/pinomad.js +10 -10
  20. package/package.json +2 -1
  21. package/web/assets/index-5DfEA9Zb.js +398 -0
  22. package/web/assets/index-D6qIPhPy.css +1 -0
  23. package/web/index.html +2 -2
  24. package/dist/chunk-d93kvxvc.js +0 -610
  25. package/dist/chunk-n4tjhcb7.js +0 -257
  26. package/dist/chunk-znhn8af7.js +0 -33
  27. package/web/assets/index-C_aU_ZiV.js +0 -383
  28. package/web/assets/index-DaPcXZPM.css +0 -1
  29. package/web/assets/montserrat-latin-ext-wght-normal-BsZE-iaG.woff2 +0 -0
  30. package/web/assets/montserrat-latin-wght-normal-l_AIctKy.woff2 +0 -0
@@ -1,610 +0,0 @@
1
- import {
2
- HOST_STREAM_NAMES,
3
- INVALID_FRAME_CLOSE_CODE,
4
- PROTOCOL,
5
- UNAUTHORIZED_CLOSE_CODE,
6
- checkoutOf,
7
- conversationStream,
8
- docStream,
9
- homeOf,
10
- hostStreams,
11
- hostSupportsCall,
12
- hostSupportsFeature,
13
- organize
14
- } from "./chunk-n4tjhcb7.js";
15
-
16
- // packages/protocol/src/remote-durable.ts
17
- import { applyImmutable } from "@earendil-works/chord/delta";
18
-
19
- // packages/protocol/src/transport.ts
20
- function webSocketTransport(url, token) {
21
- return {
22
- label: url,
23
- open(handlers) {
24
- const address = new URL(url);
25
- address.searchParams.set("token", token);
26
- const socket = new WebSocket(address);
27
- socket.onmessage = (event) => handlers.message(String(event.data));
28
- socket.onclose = (event) => handlers.closed(event.code);
29
- return { send: (data) => socket.send(data), close: () => socket.close() };
30
- }
31
- };
32
- }
33
-
34
- // packages/protocol/src/remote-durable.ts
35
- function connectRemoteDurable(options) {
36
- return new RemoteClient(options).ready;
37
- }
38
-
39
- class UnauthorizedError extends Error {
40
- constructor() {
41
- super("Unauthorized: this client is not allowed (wrong token, or the device is not paired or was revoked)");
42
- this.name = "UnauthorizedError";
43
- }
44
- }
45
-
46
- class ProtocolMismatchError extends Error {
47
- hostMajor;
48
- clientMajor;
49
- hostVersion;
50
- direction;
51
- constructor(hostMajor, clientMajor, hostVersion) {
52
- const direction = hostMajor > clientMajor ? "client-older" : "host-older";
53
- super(direction === "client-older" ? `Client out of date: the host speaks protocol major ${hostMajor}, this client speaks ${clientMajor}` : `Host out of date: this client speaks protocol major ${clientMajor}, the host speaks ${hostMajor}`);
54
- this.name = "ProtocolMismatchError";
55
- this.hostMajor = hostMajor;
56
- this.clientMajor = clientMajor;
57
- if (hostVersion !== undefined)
58
- this.hostVersion = hostVersion;
59
- this.direction = direction;
60
- }
61
- }
62
-
63
- class RemoteClient {
64
- ready;
65
- #options;
66
- #listeners = new Set;
67
- #values = new Map;
68
- #wanted = new Set;
69
- #fresh = new Set;
70
- #docs = [];
71
- #toolPresentations = {};
72
- #snapshotWaiters = new Map;
73
- #pending = new Map;
74
- #transport;
75
- #connection;
76
- #state;
77
- #current;
78
- #side;
79
- #nextCall = 1;
80
- #nextNotice = 1;
81
- #hostMinor = 0;
82
- #helloModels = [];
83
- #helloDefaults = {};
84
- #hostVersion;
85
- #attempt = 0;
86
- #closed = false;
87
- #notifying = false;
88
- #commands = Promise.resolve();
89
- #resolveReady;
90
- #rejectReady;
91
- constructor(options) {
92
- this.#options = options;
93
- this.#transport = "transport" in options ? options.transport : webSocketTransport(options.url, options.token);
94
- this.ready = new Promise((resolve, reject) => {
95
- this.#resolveReady = resolve;
96
- this.#rejectReady = reject;
97
- });
98
- this.#open();
99
- }
100
- #open() {
101
- const connection = this.#transport.open({
102
- message: (data) => {
103
- if (this.#connection === connection)
104
- this.#receive(JSON.parse(data));
105
- },
106
- closed: (code, reason) => this.#lost(connection, code, reason)
107
- });
108
- this.#connection = connection;
109
- }
110
- #lost(connection, code, reason) {
111
- if (this.#connection !== connection)
112
- return;
113
- this.#connection = undefined;
114
- for (const pending of this.#pending.values())
115
- pending.reject(new Error("Disconnected from host"));
116
- this.#pending.clear();
117
- if (this.#state === undefined) {
118
- this.#closed = true;
119
- this.#rejectReady(code === UNAUTHORIZED_CLOSE_CODE ? new UnauthorizedError : new Error(reason ?? `Could not connect to ${this.#transport.label}`));
120
- return;
121
- }
122
- if (this.#closed)
123
- return;
124
- if (code === UNAUTHORIZED_CLOSE_CODE) {
125
- this.#closed = true;
126
- this.#update({ connection: "closed", unauthorized: true });
127
- this.#notice("error", new UnauthorizedError().message);
128
- return;
129
- }
130
- if (code === INVALID_FRAME_CLOSE_CODE) {
131
- this.#closed = true;
132
- this.#update({ connection: "closed" });
133
- this.#notice("error", `The host rejected a malformed frame from this client${reason === undefined ? "" : `: ${reason}`}`);
134
- return;
135
- }
136
- this.#update({ connection: "reconnecting" });
137
- const { min, max } = this.#options.reconnectDelayMs ?? { min: 200, max: 2000 };
138
- const delay = Math.min(max, min * 2 ** this.#attempt++);
139
- setTimeout(() => {
140
- if (!this.#closed)
141
- this.#open();
142
- }, delay);
143
- }
144
- #send(frame) {
145
- this.#connection?.send(JSON.stringify(frame));
146
- }
147
- #receive(frame) {
148
- if (frame === null || typeof frame !== "object")
149
- return;
150
- switch (frame.type) {
151
- case "hello": {
152
- const announced = frame.protocol;
153
- const hostMajor = typeof announced === "number" ? announced : announced !== null && typeof announced === "object" ? announced.major : undefined;
154
- if (hostMajor !== PROTOCOL.major) {
155
- const mismatch = new ProtocolMismatchError(typeof hostMajor === "number" ? hostMajor : Number.NaN, PROTOCOL.major, frame.hostVersion);
156
- this.#closed = true;
157
- if (this.#state === undefined) {
158
- this.#rejectReady(mismatch);
159
- } else {
160
- this.#update({
161
- connection: "outdated",
162
- protocolMismatch: {
163
- hostMajor: mismatch.hostMajor,
164
- clientMajor: mismatch.clientMajor,
165
- ...mismatch.hostVersion === undefined ? {} : { hostVersion: mismatch.hostVersion },
166
- direction: mismatch.direction
167
- }
168
- });
169
- this.#notice("error", mismatch.message);
170
- }
171
- this.#connection?.close();
172
- return;
173
- }
174
- const hostMinor = announced !== null && typeof announced === "object" && typeof announced.minor === "number" ? announced.minor : 0;
175
- this.#hostMinor = hostMinor;
176
- this.#attempt = 0;
177
- this.#docs = frame.docs;
178
- this.#toolPresentations = frame.toolPresentations;
179
- this.#helloModels = frame.models;
180
- this.#helloDefaults = frame.defaults ?? {};
181
- this.#hostVersion = frame.hostVersion;
182
- this.#fresh.clear();
183
- const covered = new Set(hostStreams(hostMinor));
184
- for (const name of HOST_STREAM_NAMES) {
185
- if (covered.has(name)) {
186
- this.#wanted.add(name);
187
- } else {
188
- this.#wanted.delete(name);
189
- this.#values.delete(name);
190
- this.#fresh.delete(name);
191
- }
192
- }
193
- if (this.#current !== undefined)
194
- for (const stream of this.#conversationStreams(this.#current))
195
- this.#wanted.add(stream);
196
- if (this.#side !== undefined)
197
- for (const stream of this.#conversationStreams(this.#side))
198
- this.#wanted.add(stream);
199
- for (const stream of this.#wanted)
200
- this.#send({ type: "subscribe", stream });
201
- if (this.#state !== undefined) {
202
- this.#update({
203
- session: frame.session,
204
- toolPresentations: frame.toolPresentations,
205
- connection: "connected",
206
- host: this.#hostInfo()
207
- });
208
- this.#refresh();
209
- } else
210
- this.#awaitFirstView(frame, hostMinor);
211
- return;
212
- }
213
- case "snapshot":
214
- if (!this.#wanted.has(frame.stream))
215
- return;
216
- this.#values.set(frame.stream, frame.value);
217
- this.#fresh.add(frame.stream);
218
- this.#refresh();
219
- for (const waiter of this.#snapshotWaiters.get(frame.stream)?.splice(0) ?? [])
220
- waiter.resolve();
221
- return;
222
- case "ops":
223
- if (!this.#wanted.has(frame.stream) || !this.#values.has(frame.stream))
224
- return;
225
- this.#values.set(frame.stream, applyImmutable(this.#values.get(frame.stream), frame.ops));
226
- this.#refresh();
227
- return;
228
- case "ended":
229
- this.#wanted.delete(frame.stream);
230
- this.#values.delete(frame.stream);
231
- this.#fresh.delete(frame.stream);
232
- for (const waiter of this.#snapshotWaiters.get(frame.stream)?.splice(0) ?? []) {
233
- waiter.reject(new Error(`Stream ${frame.stream} ended: ${frame.reason}`));
234
- }
235
- this.#refresh();
236
- return;
237
- case "result": {
238
- const pending = this.#pending.get(frame.id);
239
- this.#pending.delete(frame.id);
240
- if (frame.ok)
241
- pending?.resolve(frame.value);
242
- else
243
- pending?.reject(new Error(frame.error));
244
- return;
245
- }
246
- case "notice":
247
- this.#notice(frame.level, frame.message);
248
- return;
249
- }
250
- }
251
- #awaitFirstView(hello, hostMinor) {
252
- Promise.all(hostStreams(hostMinor).map((stream) => this.#snapshot(stream))).then(() => {
253
- this.#state = {
254
- session: hello.session,
255
- organized: { chats: [], projects: [], archived: [] },
256
- models: this.#host("models") ?? hello.models,
257
- defaults: this.#effectiveDefaults(hello.defaults ?? {}),
258
- notices: [],
259
- connection: "connected",
260
- docs: [],
261
- toolPresentations: hello.toolPresentations,
262
- devices: [],
263
- mcp: null,
264
- auth: null,
265
- settings: null,
266
- agents: null,
267
- host: this.#hostInfo()
268
- };
269
- this.#refresh();
270
- this.#resolveReady({ view: this.#viewSource(), controller: this.#controller(), close: () => this.#close() });
271
- });
272
- }
273
- #snapshot(stream) {
274
- return new Promise((resolve, reject) => {
275
- const waiters = this.#snapshotWaiters.get(stream) ?? [];
276
- waiters.push({ resolve, reject });
277
- this.#snapshotWaiters.set(stream, waiters);
278
- });
279
- }
280
- #conversationStreams(id) {
281
- return [conversationStream(id), ...this.#docs.map((doc) => docStream(doc.kind, id))];
282
- }
283
- async#acquire(id) {
284
- const missing = this.#conversationStreams(id).filter((stream) => !this.#wanted.has(stream));
285
- const stale = this.#conversationStreams(id).filter((stream) => this.#wanted.has(stream) && !this.#fresh.has(stream));
286
- const shown = Promise.all([...missing, ...stale].map((stream) => this.#snapshot(stream)));
287
- for (const stream of missing) {
288
- this.#wanted.add(stream);
289
- this.#send({ type: "subscribe", stream });
290
- }
291
- await shown;
292
- }
293
- #release(id, other) {
294
- const keep = new Set(other === undefined ? [] : this.#conversationStreams(other));
295
- for (const stream of this.#conversationStreams(id)) {
296
- if (keep.has(stream))
297
- continue;
298
- this.#wanted.delete(stream);
299
- this.#values.delete(stream);
300
- this.#fresh.delete(stream);
301
- this.#send({ type: "unsubscribe", stream });
302
- }
303
- }
304
- #host(name) {
305
- return this.#values.get(name);
306
- }
307
- #hostInfo() {
308
- return {
309
- protocol: { major: PROTOCOL.major, minor: this.#hostMinor },
310
- ...this.#hostVersion === undefined ? {} : { version: this.#hostVersion }
311
- };
312
- }
313
- #effectiveDefaults(fallback) {
314
- const settings = this.#host("settings");
315
- if (settings === undefined)
316
- return fallback;
317
- return {
318
- ...settings.defaultModel === null ? {} : { model: settings.defaultModel },
319
- ...settings.defaultThinkingLevel === null ? {} : { thinkingLevel: settings.defaultThinkingLevel }
320
- };
321
- }
322
- #descendsFrom(id, ancestor) {
323
- const summaries = this.#host("conversations") ?? [];
324
- const parentOf = new Map(summaries.map((summary) => [summary.id, summary.parent]));
325
- let cursor = id;
326
- for (let depth = 0;cursor !== undefined && depth < 8; depth++) {
327
- if (cursor === ancestor)
328
- return true;
329
- cursor = parentOf.get(cursor);
330
- }
331
- return false;
332
- }
333
- #docsOf(id) {
334
- return this.#docs.flatMap((doc) => {
335
- const stream = docStream(doc.kind, id);
336
- return this.#values.has(stream) ? [{ kind: doc.kind, presentation: doc.presentation, value: this.#values.get(stream) }] : [];
337
- });
338
- }
339
- #refresh() {
340
- if (this.#state === undefined)
341
- return;
342
- const current = this.#current;
343
- const side = this.#side;
344
- const conversation = current === undefined ? undefined : this.#values.get(conversationStream(current));
345
- const summaries = this.#host("conversations") ?? [];
346
- const index = this.#host("index") ?? { projects: [], conversations: [] };
347
- const devices = this.#host("devices")?.devices ?? [];
348
- const tasks = this.#wanted.has("tasks") ? this.#values.get("tasks") : undefined;
349
- this.#update({
350
- conversation,
351
- models: this.#host("models") ?? this.#helloModels,
352
- organized: organize(index, summaries),
353
- home: current === undefined ? undefined : homeOf(index, summaries, current),
354
- checkout: current === undefined ? undefined : checkoutOf(index, summaries, current),
355
- tasks,
356
- docs: current === undefined ? [] : this.#docsOf(current),
357
- side: side === undefined ? undefined : {
358
- id: side,
359
- conversation: this.#values.get(conversationStream(side)),
360
- docs: this.#docsOf(side)
361
- },
362
- devices,
363
- mcp: this.#host("mcp") ?? null,
364
- auth: this.#host("auth") ?? null,
365
- settings: this.#host("settings") ?? null,
366
- agents: this.#host("agents") ?? null,
367
- defaults: this.#effectiveDefaults(this.#helloDefaults)
368
- });
369
- }
370
- #update(patch) {
371
- if (this.#state === undefined)
372
- return;
373
- this.#state = { ...this.#state, ...patch };
374
- if (this.#notifying)
375
- return;
376
- this.#notifying = true;
377
- queueMicrotask(() => {
378
- this.#notifying = false;
379
- for (const listener of this.#listeners)
380
- listener();
381
- });
382
- }
383
- #notice(level, message) {
384
- if (this.#state === undefined)
385
- return;
386
- this.#update({ notices: [...this.#state.notices, { id: this.#nextNotice++, level, message }].slice(-20) });
387
- }
388
- #call(method, args) {
389
- if (this.#connection === undefined || this.#state?.connection !== "connected") {
390
- return Promise.reject(new Error("Not connected to the host"));
391
- }
392
- const id = this.#nextCall++;
393
- return new Promise((resolve, reject) => {
394
- this.#pending.set(id, { resolve, reject });
395
- this.#send({ type: "call", id, method, args });
396
- });
397
- }
398
- #command(operation) {
399
- this.#commands = this.#commands.then(operation).then(() => {}, (error) => this.#notice("error", error instanceof Error ? error.message : String(error)));
400
- return this.#commands;
401
- }
402
- #request(operation) {
403
- const request = this.#commands.then(operation);
404
- this.#commands = request.then(() => {}, () => {});
405
- return request;
406
- }
407
- #viewSource() {
408
- return {
409
- current: () => this.#state,
410
- subscribe: (listener) => {
411
- this.#listeners.add(listener);
412
- return () => this.#listeners.delete(listener);
413
- }
414
- };
415
- }
416
- #controller() {
417
- const newRequestId = () => Array.from(crypto.getRandomValues(new Uint8Array(16)), (byte) => byte.toString(16).padStart(2, "0")).join("");
418
- const conversationId = () => {
419
- if (this.#current === undefined)
420
- throw new Error("No conversation selected");
421
- return this.#current;
422
- };
423
- return {
424
- addProject: (path) => this.#request(() => this.#call("addProject", { path })),
425
- listFolders: (path) => this.#request(() => {
426
- if (!hostSupportsCall(this.#hostMinor, "listFolders")) {
427
- throw new Error("The host is too old for this (needs protocol 5.3); run `pinomad upgrade` on the host");
428
- }
429
- return this.#call("listFolders", path === undefined ? {} : { path });
430
- }),
431
- removeProject: (path) => this.#command(() => this.#call("removeProject", { path })),
432
- renameConversation: (id, title) => this.#request(async () => {
433
- if (!hostSupportsCall(this.#hostMinor, "renameConversation")) {
434
- throw new Error("The host is too old for this (needs protocol 5.4); run `pinomad upgrade` on the host");
435
- }
436
- await this.#call("renameConversation", { conversationId: id, title });
437
- }),
438
- createConversation: (home, text, options) => this.#command(async () => {
439
- const images = options?.images ?? [];
440
- if (images.length > 0 && !hostSupportsFeature(this.#hostMinor, "images")) {
441
- throw new Error("The host is too old for images (needs protocol 5.5); run `pinomad upgrade` on the host");
442
- }
443
- if (options?.agent !== undefined && !hostSupportsFeature(this.#hostMinor, "agentConversations")) {
444
- throw new Error("The host is too old for named agents (needs protocol 5.10); run `pinomad upgrade` on the host");
445
- }
446
- const { conversationId: conversationId2 } = await this.#call("createConversation", {
447
- home,
448
- text,
449
- requestId: newRequestId(),
450
- ...options?.checkout === undefined ? {} : { checkout: options.checkout },
451
- ...options?.model === undefined ? {} : { model: options.model },
452
- ...options?.thinkingLevel === undefined ? {} : { thinkingLevel: options.thinkingLevel },
453
- ...options?.agent === undefined ? {} : { agent: options.agent },
454
- ...images.length === 0 ? {} : { images }
455
- });
456
- await this.#switch(conversationId2);
457
- }),
458
- archive: (id, archived) => this.#command(async () => {
459
- await this.#call("archive", { conversationId: id, archived });
460
- if (archived && id === this.#current)
461
- this.#unshow();
462
- if (archived && this.#side !== undefined && this.#descendsFrom(this.#side, id))
463
- await this.#showSide(undefined);
464
- }),
465
- submit: (text, whenBusy, options) => this.#command(() => {
466
- const images = options?.images ?? [];
467
- if (images.length > 0 && !hostSupportsFeature(this.#hostMinor, "images")) {
468
- throw new Error("The host is too old for images (needs protocol 5.5); run `pinomad upgrade` on the host");
469
- }
470
- return this.#call("submit", {
471
- conversationId: options?.target ?? conversationId(),
472
- text,
473
- whenBusy,
474
- requestId: options?.requestId ?? newRequestId(),
475
- ...images.length === 0 ? {} : { images }
476
- });
477
- }),
478
- compact: (instructions) => this.#command(() => this.#call("compact", { conversationId: conversationId(), ...instructions === undefined ? {} : { instructions } })),
479
- abort: (target) => {
480
- const targetConversation = target ?? this.#current;
481
- if (targetConversation === undefined) {
482
- this.#notice("error", "No conversation selected");
483
- return Promise.resolve();
484
- }
485
- return this.#call("abort", { conversationId: targetConversation }).then(() => {}, (error) => this.#notice("error", error instanceof Error ? error.message : String(error)));
486
- },
487
- setThinkingLevel: (level) => this.#command(() => this.#call("setThinkingLevel", { conversationId: conversationId(), level })),
488
- setModel: (model) => this.#command(() => this.#call("setModel", { conversationId: conversationId(), model })),
489
- toggleTasks: () => this.#command(async () => {
490
- if (this.#wanted.delete("tasks")) {
491
- this.#send({ type: "unsubscribe", stream: "tasks" });
492
- this.#values.delete("tasks");
493
- this.#refresh();
494
- return;
495
- }
496
- this.#wanted.add("tasks");
497
- const shown = this.#snapshot("tasks");
498
- this.#send({ type: "subscribe", stream: "tasks" });
499
- await shown;
500
- }),
501
- switchConversation: (id) => this.#command(() => this.#switch(id)),
502
- showSide: (id) => this.#command(() => this.#showSide(id)),
503
- fork: (entryId, prompt, removeTools, options) => this.#command(async () => {
504
- const { conversationId: forked } = await this.#call("fork", {
505
- conversationId: conversationId(),
506
- entryId,
507
- ...removeTools === undefined ? {} : { removeTools }
508
- });
509
- if (options?.show === "side")
510
- await this.#showSide(forked);
511
- else
512
- await this.#switch(forked);
513
- await this.#call("submit", { conversationId: forked, text: prompt, whenBusy: "followUp", requestId: newRequestId() });
514
- }),
515
- answer: (kind, requestId, answers, target) => this.#command(() => this.#call("answer", { conversationId: target ?? conversationId(), kind, requestId, answers })),
516
- changes: () => this.#request(() => this.#call("changes", { conversationId: conversationId() })),
517
- listFiles: (scope, query) => {
518
- if (!hostSupportsCall(this.#hostMinor, "listFiles")) {
519
- return Promise.reject(new Error("The host is too old for this (needs protocol 5.10); run `pinomad upgrade` on the host"));
520
- }
521
- return this.#call("listFiles", { scope, query });
522
- },
523
- createPairing: () => this.#request(() => this.#call("createPairing", {})),
524
- revokeDevice: (publicKey) => this.#command(() => this.#call("revokeDevice", { publicKey })),
525
- logout: (providerId) => this.#command(() => {
526
- if (!hostSupportsCall(this.#hostMinor, "logout")) {
527
- throw new Error("The host is too old for this (needs protocol 5.1); run `pinomad upgrade` on the host");
528
- }
529
- return this.#call("logout", { providerId });
530
- }),
531
- updateSettings: (patch) => this.#command(() => {
532
- if (!hostSupportsCall(this.#hostMinor, "updateSettings")) {
533
- throw new Error("The host is too old for this (needs protocol 5.2); run `pinomad upgrade` on the host");
534
- }
535
- if (patch.acceptAccountDevices !== undefined && !hostSupportsFeature(this.#hostMinor, "acceptAccountDevices")) {
536
- throw new Error("The host is too old for this (needs protocol 5.8); run `pinomad upgrade` on the host");
537
- }
538
- if (patch.hostName !== undefined && !hostSupportsFeature(this.#hostMinor, "hostName")) {
539
- throw new Error("The host is too old for this (needs protocol 5.12); run `pinomad upgrade` on the host");
540
- }
541
- return this.#call("updateSettings", { patch });
542
- }),
543
- createAgent: (role) => this.#request(async () => {
544
- if (!hostSupportsCall(this.#hostMinor, "createAgent")) {
545
- throw new Error("The host is too old for this (needs protocol 5.6); run `pinomad upgrade` on the host");
546
- }
547
- await this.#call("createAgent", { role });
548
- }),
549
- updateAgent: (role) => this.#request(async () => {
550
- if (!hostSupportsCall(this.#hostMinor, "updateAgent")) {
551
- throw new Error("The host is too old for this (needs protocol 5.6); run `pinomad upgrade` on the host");
552
- }
553
- await this.#call("updateAgent", { role });
554
- }),
555
- deleteAgent: (name) => this.#request(async () => {
556
- if (!hostSupportsCall(this.#hostMinor, "deleteAgent")) {
557
- throw new Error("The host is too old for this (needs protocol 5.6); run `pinomad upgrade` on the host");
558
- }
559
- await this.#call("deleteAgent", { name });
560
- }),
561
- setPushToken: (token) => this.#request(async () => {
562
- if (!hostSupportsCall(this.#hostMinor, "setPushToken")) {
563
- throw new Error("The host is too old for this (needs protocol 5.7); run `pinomad upgrade` on the host");
564
- }
565
- if (token !== null && typeof token !== "string" && !hostSupportsFeature(this.#hostMinor, "apnsPush")) {
566
- throw new Error("The host is too old for this (needs protocol 5.9); run `pinomad upgrade` on the host");
567
- }
568
- await this.#call("setPushToken", { token });
569
- })
570
- };
571
- }
572
- async#switch(id) {
573
- const previous = this.#current;
574
- if (id === previous)
575
- return;
576
- await this.#acquire(id);
577
- this.#current = id;
578
- if (previous !== undefined)
579
- this.#release(previous, this.#side);
580
- this.#refresh();
581
- }
582
- #unshow() {
583
- const previous = this.#current;
584
- if (previous === undefined)
585
- return;
586
- this.#current = undefined;
587
- this.#release(previous, this.#side);
588
- this.#refresh();
589
- }
590
- async#showSide(id) {
591
- const previous = this.#side;
592
- if (id === previous)
593
- return;
594
- if (id !== undefined)
595
- await this.#acquire(id);
596
- this.#side = id;
597
- if (previous !== undefined)
598
- this.#release(previous, this.#current);
599
- this.#refresh();
600
- }
601
- #close() {
602
- if (this.#closed)
603
- return;
604
- this.#closed = true;
605
- this.#update({ connection: "closed" });
606
- this.#connection?.close();
607
- }
608
- }
609
-
610
- export { connectRemoteDurable, ProtocolMismatchError };