pinomad 0.12.0 → 0.14.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.
- package/dist/chunk-0dh2k3er.js +43 -0
- package/dist/{chunk-47ff1d1t.js → chunk-1mjbt2z2.js} +191 -11
- package/dist/{chunk-7wj0jh3t.js → chunk-1rdbnvy6.js} +1 -1
- package/dist/{chunk-ksfxqd8s.js → chunk-35szq73k.js} +2682 -459
- package/dist/{chunk-mnmqyq7d.js → chunk-5j7ybhjj.js} +1984 -333
- package/dist/{chunk-agq4tn9r.js → chunk-5pv0g1nj.js} +12 -10
- package/dist/{chunk-7mn38n4f.js → chunk-63ymn5c9.js} +2 -2
- package/dist/{chunk-nppgxh6c.js → chunk-6j6hkfbj.js} +8 -4
- package/dist/{chunk-pqe7q1s7.js → chunk-6vbp1vva.js} +2 -1
- package/dist/{chunk-g3ahp7b9.js → chunk-9trbbz4r.js} +1 -1
- package/dist/{chunk-gg2dcqbg.js → chunk-b0nwq1dt.js} +29 -12
- package/dist/chunk-dsr19q5h.js +23 -0
- package/dist/{chunk-41q0saz0.js → chunk-e53c0s8z.js} +9 -7
- package/dist/{chunk-np4jd070.js → chunk-e59k3bmz.js} +307 -56
- package/dist/chunk-eerpgvy9.js +315 -0
- package/dist/chunk-eewxy4cp.js +240 -0
- package/dist/{chunk-vqkb70d7.js → chunk-evbv1fkn.js} +3 -3
- package/dist/{chunk-vx7dy07y.js → chunk-ex5yr09h.js} +11 -6
- package/dist/{chunk-t91zt5f0.js → chunk-hg9gs396.js} +1 -1
- package/dist/{chunk-aqj9wfmb.js → chunk-mtkvft88.js} +1 -1
- package/dist/{chunk-t5abqsva.js → chunk-mxwcnnkg.js} +15 -305
- package/dist/{chunk-5qf1grkk.js → chunk-w927q9m1.js} +52 -16
- package/dist/pinomad.js +12 -12
- package/package.json +16 -11
- package/web/assets/{core-DnpzcQzs.js → core-BWHGsFpV.js} +1 -1
- package/web/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/web/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/web/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/web/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/web/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/web/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/web/assets/{highlighted-diff-Dn5ifsvK.js → highlighted-diff-DJ7KZCpS.js} +2 -2
- package/web/assets/{highlights-DnHv6NBM.js → highlights-CyEmo6OI.js} +1 -1
- package/web/assets/{highlights-themes-CUdrlZk3.js → highlights-themes-CSNkBojy.js} +1 -1
- package/web/assets/index-C_qkbyL7.css +1 -0
- package/web/assets/index-DefQtCI2.js +396 -0
- package/web/assets/{shiki-BuPP5HCg.js → shiki-BzCmRG-v.js} +2 -2
- package/web/assets/{shiki-langs-FJk0v0Ga.js → shiki-langs-DOkjyV4d.js} +1 -1
- package/web/assets/{stream-tokenizer-Bl6bisIa.js → stream-tokenizer-CBPqFe4Z.js} +1 -1
- package/web/assets/{theming-themes-BL_CC-Mm.js → theming-themes-wKVca0aB.js} +1 -1
- package/web/assets/{xterm-view-ZsuzJb2C.js → xterm-view-CKa1-QCM.js} +12 -12
- package/web/index.html +3 -2
- package/web/theme-boot.js +11 -0
- package/dist/chunk-42nqtx5r.js +0 -22
- package/dist/chunk-jeqsdcj1.js +0 -96
- package/web/assets/index-BV0ecolo.css +0 -1
- package/web/assets/index-DNsTTgPE.js +0 -396
|
@@ -3,19 +3,26 @@ import {
|
|
|
3
3
|
MAX_ATTACHMENTS_PER_MESSAGE,
|
|
4
4
|
PROTOCOL,
|
|
5
5
|
RTC_DATA_CHANNEL_LABEL,
|
|
6
|
+
TRANSCRIPT_PAGE,
|
|
6
7
|
UNAUTHORIZED_CLOSE_CODE,
|
|
8
|
+
addEarlier,
|
|
7
9
|
checkoutOf,
|
|
10
|
+
classify,
|
|
8
11
|
conversationStream,
|
|
9
12
|
docStream,
|
|
10
13
|
homeOf,
|
|
11
14
|
hostStreams,
|
|
12
15
|
hostSupportsCall,
|
|
13
16
|
hostSupportsFeature,
|
|
17
|
+
isBusy,
|
|
18
|
+
isProcessesHello,
|
|
14
19
|
isRtcHello,
|
|
15
20
|
organize,
|
|
16
21
|
parseRtcMaxMessageSize,
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
processStream,
|
|
23
|
+
rtcSelectedPair,
|
|
24
|
+
withEarlier
|
|
25
|
+
} from "./chunk-1mjbt2z2.js";
|
|
19
26
|
import {
|
|
20
27
|
SECURE_PROLOGUE,
|
|
21
28
|
SecureSession,
|
|
@@ -24,7 +31,7 @@ import {
|
|
|
24
31
|
initiateIK,
|
|
25
32
|
isLoopbackUrl,
|
|
26
33
|
parseEndpointSet
|
|
27
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-6vbp1vva.js";
|
|
28
35
|
|
|
29
36
|
// packages/protocol/src/remote-durable.ts
|
|
30
37
|
import { applyImmutable } from "@earendil-works/chord/delta";
|
|
@@ -293,12 +300,6 @@ function isTerminalsHello(value) {
|
|
|
293
300
|
var terminalsStream = (id) => `terminals:${id}`;
|
|
294
301
|
var terminalStream = (id) => `terminal:${id}`;
|
|
295
302
|
|
|
296
|
-
// packages/protocol/src/transcript.ts
|
|
297
|
-
function isBusy(view) {
|
|
298
|
-
const live = view.docs?.["pi.live"];
|
|
299
|
-
return live?.run !== undefined;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
303
|
// packages/protocol/src/remote-durable.ts
|
|
303
304
|
var DEFAULT_CANDIDATE_TIMEOUTS = {
|
|
304
305
|
socketOpenMs: 5000,
|
|
@@ -396,6 +397,8 @@ class RemoteClient {
|
|
|
396
397
|
#options;
|
|
397
398
|
#listeners = new Set;
|
|
398
399
|
#values = new Map;
|
|
400
|
+
#earlier = new Map;
|
|
401
|
+
#loadingEarlier = new Map;
|
|
399
402
|
#wanted = new Set;
|
|
400
403
|
#fresh = new Set;
|
|
401
404
|
#docs = [];
|
|
@@ -454,6 +457,7 @@ class RemoteClient {
|
|
|
454
457
|
#state;
|
|
455
458
|
#current;
|
|
456
459
|
#side;
|
|
460
|
+
#acquiring = new Map;
|
|
457
461
|
#nextCall = 1;
|
|
458
462
|
#nextNotice = 1;
|
|
459
463
|
#submissions = new Map;
|
|
@@ -466,10 +470,14 @@ class RemoteClient {
|
|
|
466
470
|
#helloDefaults = {};
|
|
467
471
|
#hostVersion;
|
|
468
472
|
#hostTerminals;
|
|
473
|
+
#hostProcesses;
|
|
469
474
|
#terminalWatchers = new Map;
|
|
470
475
|
#terminalSinks = new Map;
|
|
471
476
|
#terminalPositions = new Map;
|
|
472
477
|
#terminalSizes = new Map;
|
|
478
|
+
#processWatchers = new Map;
|
|
479
|
+
#processSinks = new Map;
|
|
480
|
+
#processPositions = new Map;
|
|
473
481
|
#attempt = 0;
|
|
474
482
|
#rtcAttemptNonce = randomHex(8);
|
|
475
483
|
#closed = false;
|
|
@@ -723,6 +731,7 @@ class RemoteClient {
|
|
|
723
731
|
this.#helloDefaults = frame.defaults ?? {};
|
|
724
732
|
this.#hostVersion = frame.hostVersion;
|
|
725
733
|
this.#hostTerminals = isTerminalsHello(frame.terminals) ? frame.terminals : undefined;
|
|
734
|
+
this.#hostProcesses = isProcessesHello(frame.processes) ? frame.processes : undefined;
|
|
726
735
|
this.#fresh.clear();
|
|
727
736
|
const covered = new Set(hostStreams(hostMinor));
|
|
728
737
|
for (const name of HOST_STREAM_NAMES) {
|
|
@@ -741,6 +750,7 @@ class RemoteClient {
|
|
|
741
750
|
for (const stream of this.#conversationStreams(this.#side))
|
|
742
751
|
this.#wanted.add(stream);
|
|
743
752
|
this.#terminalsAvailability();
|
|
753
|
+
this.#processesAvailability();
|
|
744
754
|
for (const stream of this.#wanted)
|
|
745
755
|
this.#subscribe(stream);
|
|
746
756
|
if (frame.endpointSet !== undefined) {
|
|
@@ -823,6 +833,7 @@ class RemoteClient {
|
|
|
823
833
|
providers: null,
|
|
824
834
|
settings: null,
|
|
825
835
|
agents: null,
|
|
836
|
+
upgrade: null,
|
|
826
837
|
host: this.#hostInfo()
|
|
827
838
|
};
|
|
828
839
|
this.#refresh();
|
|
@@ -830,6 +841,7 @@ class RemoteClient {
|
|
|
830
841
|
view: this.#viewSource(),
|
|
831
842
|
controller: this.#controller(),
|
|
832
843
|
terminals: this.#terminals(),
|
|
844
|
+
processes: this.#processes(),
|
|
833
845
|
setConnectionMode: (mode) => this.setConnectionMode(mode),
|
|
834
846
|
rtcDiagnostics: () => this.rtcDiagnostics(),
|
|
835
847
|
close: () => this.#close()
|
|
@@ -1985,9 +1997,13 @@ class RemoteClient {
|
|
|
1985
1997
|
return;
|
|
1986
1998
|
}
|
|
1987
1999
|
this.#values.set(frame.stream, frame.value);
|
|
2000
|
+
this.#earlier.delete(frame.stream);
|
|
2001
|
+
this.#loadingEarlier.delete(frame.stream);
|
|
1988
2002
|
this.#fresh.add(frame.stream);
|
|
1989
2003
|
this.#streamVersions.set(frame.stream, (this.#streamVersions.get(frame.stream) ?? 0) + 1);
|
|
1990
2004
|
this.#refresh();
|
|
2005
|
+
if (this.#processWatchers.has(frame.stream))
|
|
2006
|
+
this.#notifyProcesses(frame.stream);
|
|
1991
2007
|
for (const waiter of this.#snapshotWaiters.get(frame.stream)?.splice(0) ?? [])
|
|
1992
2008
|
waiter.resolve();
|
|
1993
2009
|
return;
|
|
@@ -1996,12 +2012,18 @@ class RemoteClient {
|
|
|
1996
2012
|
return;
|
|
1997
2013
|
this.#values.set(frame.stream, applyImmutable(this.#values.get(frame.stream), frame.ops));
|
|
1998
2014
|
this.#refresh();
|
|
2015
|
+
if (this.#processWatchers.has(frame.stream))
|
|
2016
|
+
this.#notifyProcesses(frame.stream);
|
|
1999
2017
|
return;
|
|
2000
2018
|
case "output": {
|
|
2001
|
-
const
|
|
2019
|
+
const processSink = this.#processSinks.get(frame.stream);
|
|
2020
|
+
const sink = processSink ?? this.#terminalSinks.get(frame.stream);
|
|
2002
2021
|
if (sink === undefined || !this.#wanted.has(frame.stream))
|
|
2003
2022
|
return;
|
|
2004
|
-
|
|
2023
|
+
if (processSink === undefined)
|
|
2024
|
+
this.#terminalPositions.set(frame.stream, frame.seq);
|
|
2025
|
+
else
|
|
2026
|
+
this.#processPositions.set(frame.stream, frame.seq);
|
|
2005
2027
|
sink.output(frame.data, frame.reset === true);
|
|
2006
2028
|
return;
|
|
2007
2029
|
}
|
|
@@ -2010,8 +2032,14 @@ class RemoteClient {
|
|
|
2010
2032
|
this.#endTerminalStream(frame.stream, frame.reason);
|
|
2011
2033
|
return;
|
|
2012
2034
|
}
|
|
2035
|
+
if (frame.stream.startsWith("process:")) {
|
|
2036
|
+
this.#endProcessStream(frame.stream, frame.reason);
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2013
2039
|
this.#wanted.delete(frame.stream);
|
|
2014
2040
|
this.#values.delete(frame.stream);
|
|
2041
|
+
this.#earlier.delete(frame.stream);
|
|
2042
|
+
this.#loadingEarlier.delete(frame.stream);
|
|
2015
2043
|
this.#fresh.delete(frame.stream);
|
|
2016
2044
|
const ended = new Error(`Stream ${frame.stream} ended: ${frame.reason}`);
|
|
2017
2045
|
for (const waiter of this.#snapshotWaiters.get(frame.stream)?.splice(0) ?? []) {
|
|
@@ -2026,6 +2054,8 @@ class RemoteClient {
|
|
|
2026
2054
|
this.#side = undefined;
|
|
2027
2055
|
}
|
|
2028
2056
|
this.#refresh();
|
|
2057
|
+
if (this.#processWatchers.has(frame.stream))
|
|
2058
|
+
this.#notifyProcesses(frame.stream);
|
|
2029
2059
|
return;
|
|
2030
2060
|
}
|
|
2031
2061
|
case "result": {
|
|
@@ -2079,40 +2109,100 @@ class RemoteClient {
|
|
|
2079
2109
|
return [conversationStream(id), ...this.#docs.map((doc) => docStream(doc.kind, id))];
|
|
2080
2110
|
}
|
|
2081
2111
|
async#acquire(id) {
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
this.#wanted.
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2112
|
+
this.#acquiring.set(id, (this.#acquiring.get(id) ?? 0) + 1);
|
|
2113
|
+
try {
|
|
2114
|
+
const missing = this.#conversationStreams(id).filter((stream) => !this.#wanted.has(stream));
|
|
2115
|
+
const stale = this.#conversationStreams(id).filter((stream) => this.#wanted.has(stream) && !this.#fresh.has(stream));
|
|
2116
|
+
for (const stream of missing)
|
|
2117
|
+
this.#wanted.add(stream);
|
|
2118
|
+
const shown = Promise.all([...missing, ...stale].map((stream) => this.#snapshot(stream)));
|
|
2119
|
+
for (const stream of missing) {
|
|
2120
|
+
if (this.#helloReceived)
|
|
2121
|
+
this.#subscribe(stream);
|
|
2122
|
+
}
|
|
2123
|
+
await shown;
|
|
2124
|
+
} finally {
|
|
2125
|
+
const left = (this.#acquiring.get(id) ?? 1) - 1;
|
|
2126
|
+
if (left > 0)
|
|
2127
|
+
this.#acquiring.set(id, left);
|
|
2128
|
+
else
|
|
2129
|
+
this.#acquiring.delete(id);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
#streamStillWanted(stream) {
|
|
2133
|
+
if (this.#processWatchers.has(stream))
|
|
2134
|
+
return true;
|
|
2135
|
+
for (const id of [this.#current, this.#side, ...this.#acquiring.keys()]) {
|
|
2136
|
+
if (id !== undefined && this.#conversationStreams(id).includes(stream))
|
|
2137
|
+
return true;
|
|
2138
|
+
}
|
|
2139
|
+
return false;
|
|
2140
|
+
}
|
|
2141
|
+
#releaseStream(stream) {
|
|
2142
|
+
this.#wanted.delete(stream);
|
|
2143
|
+
this.#values.delete(stream);
|
|
2144
|
+
this.#earlier.delete(stream);
|
|
2145
|
+
this.#loadingEarlier.delete(stream);
|
|
2146
|
+
this.#fresh.delete(stream);
|
|
2147
|
+
for (const waiter of this.#snapshotWaiters.get(stream)?.splice(0) ?? [])
|
|
2148
|
+
waiter.resolve();
|
|
2149
|
+
this.#send({ type: "unsubscribe", stream });
|
|
2150
|
+
}
|
|
2151
|
+
#release(id) {
|
|
2095
2152
|
for (const stream of this.#conversationStreams(id)) {
|
|
2096
|
-
if (
|
|
2153
|
+
if (this.#streamStillWanted(stream))
|
|
2097
2154
|
continue;
|
|
2098
|
-
this.#
|
|
2099
|
-
this.#values.delete(stream);
|
|
2100
|
-
this.#fresh.delete(stream);
|
|
2101
|
-
for (const waiter of this.#snapshotWaiters.get(stream)?.splice(0) ?? [])
|
|
2102
|
-
waiter.resolve();
|
|
2103
|
-
this.#send({ type: "unsubscribe", stream });
|
|
2155
|
+
this.#releaseStream(stream);
|
|
2104
2156
|
}
|
|
2105
2157
|
}
|
|
2106
2158
|
#subscribe(stream) {
|
|
2107
2159
|
const refs = stream.startsWith("conversation:") && hostSupportsFeature(this.#hostMinor, "imageRefs");
|
|
2108
|
-
const since = this.#terminalPositions.get(stream);
|
|
2160
|
+
const since = stream.startsWith("process:") ? this.#processPositions.get(stream) : this.#terminalPositions.get(stream);
|
|
2161
|
+
const window = this.#window(stream);
|
|
2109
2162
|
this.#send({
|
|
2110
2163
|
type: "subscribe",
|
|
2111
2164
|
stream,
|
|
2112
2165
|
...refs ? { images: "ref" } : {},
|
|
2113
|
-
...since === undefined ? {} : { since }
|
|
2166
|
+
...since === undefined ? {} : { since },
|
|
2167
|
+
...window === undefined ? {} : { window }
|
|
2114
2168
|
});
|
|
2115
2169
|
}
|
|
2170
|
+
#window(stream) {
|
|
2171
|
+
if (!stream.startsWith("conversation:") || !hostSupportsFeature(this.#hostMinor, "transcriptPaging"))
|
|
2172
|
+
return;
|
|
2173
|
+
const from = this.#shownView(stream)?.entries[0]?.id;
|
|
2174
|
+
return from === undefined ? { limit: TRANSCRIPT_PAGE } : { limit: TRANSCRIPT_PAGE, from };
|
|
2175
|
+
}
|
|
2176
|
+
#shownView(stream) {
|
|
2177
|
+
const view = this.#values.get(stream);
|
|
2178
|
+
return view === undefined ? undefined : withEarlier(view, this.#earlier.get(stream));
|
|
2179
|
+
}
|
|
2180
|
+
#loadEarlier(id) {
|
|
2181
|
+
const stream = conversationStream(id);
|
|
2182
|
+
const inFlight = this.#loadingEarlier.get(stream);
|
|
2183
|
+
if (inFlight !== undefined)
|
|
2184
|
+
return inFlight;
|
|
2185
|
+
const requested = this.#shownView(stream)?.before;
|
|
2186
|
+
if (requested === undefined)
|
|
2187
|
+
return Promise.resolve();
|
|
2188
|
+
const load = this.#call("earlierEntries", { conversationId: id, before: requested, limit: TRANSCRIPT_PAGE }).then((page) => {
|
|
2189
|
+
if (this.#loadingEarlier.get(stream) !== load)
|
|
2190
|
+
return;
|
|
2191
|
+
const shown = this.#shownView(stream);
|
|
2192
|
+
if (shown === undefined)
|
|
2193
|
+
return;
|
|
2194
|
+
const earlier = addEarlier(shown, this.#earlier.get(stream), requested, page);
|
|
2195
|
+
if (earlier === undefined)
|
|
2196
|
+
return;
|
|
2197
|
+
this.#earlier.set(stream, earlier);
|
|
2198
|
+
this.#refresh();
|
|
2199
|
+
}).finally(() => {
|
|
2200
|
+
if (this.#loadingEarlier.get(stream) === load)
|
|
2201
|
+
this.#loadingEarlier.delete(stream);
|
|
2202
|
+
});
|
|
2203
|
+
this.#loadingEarlier.set(stream, load);
|
|
2204
|
+
return load;
|
|
2205
|
+
}
|
|
2116
2206
|
#terminalsUnavailable() {
|
|
2117
2207
|
if (!hostSupportsFeature(this.#hostMinor, "terminals")) {
|
|
2118
2208
|
return "The host is too old for terminals (needs protocol 5.27); run `pinomad upgrade` on the host";
|
|
@@ -2224,6 +2314,122 @@ class RemoteClient {
|
|
|
2224
2314
|
close: (terminalId) => this.#terminalCall("closeTerminal", { terminalId }).then(() => {})
|
|
2225
2315
|
};
|
|
2226
2316
|
}
|
|
2317
|
+
#processesUnavailable() {
|
|
2318
|
+
if (!hostSupportsFeature(this.#hostMinor, "processes")) {
|
|
2319
|
+
return "The host is too old for background processes (needs protocol 5.32); run `pinomad upgrade` on the host";
|
|
2320
|
+
}
|
|
2321
|
+
if (this.#hostProcesses === undefined)
|
|
2322
|
+
return "The host couldn't load its process module; its log says which package is missing";
|
|
2323
|
+
return;
|
|
2324
|
+
}
|
|
2325
|
+
#processDocDeclared() {
|
|
2326
|
+
return this.#docs.some((doc) => doc.kind === "pinomad.processes");
|
|
2327
|
+
}
|
|
2328
|
+
#processesAvailability() {
|
|
2329
|
+
const listable = hostSupportsFeature(this.#hostMinor, "processes") && this.#processDocDeclared();
|
|
2330
|
+
for (const stream of [...this.#processWatchers.keys()]) {
|
|
2331
|
+
if (listable) {
|
|
2332
|
+
this.#wanted.add(stream);
|
|
2333
|
+
} else {
|
|
2334
|
+
this.#wanted.delete(stream);
|
|
2335
|
+
this.#values.delete(stream);
|
|
2336
|
+
for (const waiter of this.#snapshotWaiters.get(stream)?.splice(0) ?? [])
|
|
2337
|
+
waiter.resolve();
|
|
2338
|
+
}
|
|
2339
|
+
this.#notifyProcesses(stream);
|
|
2340
|
+
}
|
|
2341
|
+
const reason = this.#processesUnavailable();
|
|
2342
|
+
if (reason === undefined) {
|
|
2343
|
+
for (const stream of this.#processSinks.keys())
|
|
2344
|
+
this.#wanted.add(stream);
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
for (const stream of [...this.#processSinks.keys()])
|
|
2348
|
+
this.#endProcessStream(stream, reason);
|
|
2349
|
+
}
|
|
2350
|
+
#notifyProcesses(stream) {
|
|
2351
|
+
const state = this.#processState(stream);
|
|
2352
|
+
for (const listener of [...this.#processWatchers.get(stream) ?? []])
|
|
2353
|
+
listener(state);
|
|
2354
|
+
}
|
|
2355
|
+
#processState(stream) {
|
|
2356
|
+
const value = this.#values.get(stream);
|
|
2357
|
+
const presentation = this.#docs.find((doc) => doc.kind === "pinomad.processes")?.presentation ?? "pinomad.processes";
|
|
2358
|
+
const classified = classify(presentation, value);
|
|
2359
|
+
const processes = classified.type === "pinomad.processes" ? classified.value.processes : [];
|
|
2360
|
+
const reason = this.#processesUnavailable();
|
|
2361
|
+
return {
|
|
2362
|
+
processes: [...processes],
|
|
2363
|
+
...reason === undefined ? {} : { unavailable: reason }
|
|
2364
|
+
};
|
|
2365
|
+
}
|
|
2366
|
+
#endProcessStream(stream, reason) {
|
|
2367
|
+
this.#wanted.delete(stream);
|
|
2368
|
+
const sink = this.#processSinks.get(stream);
|
|
2369
|
+
this.#processSinks.delete(stream);
|
|
2370
|
+
this.#processPositions.delete(stream);
|
|
2371
|
+
sink?.ended(reason);
|
|
2372
|
+
}
|
|
2373
|
+
#processCall(method, args) {
|
|
2374
|
+
const reason = this.#processesUnavailable();
|
|
2375
|
+
return reason === undefined ? this.#call(method, args) : Promise.reject(new Error(reason));
|
|
2376
|
+
}
|
|
2377
|
+
#processes() {
|
|
2378
|
+
return {
|
|
2379
|
+
watch: (conversationId, listener) => {
|
|
2380
|
+
const stream = docStream("pinomad.processes", conversationId);
|
|
2381
|
+
let listeners = this.#processWatchers.get(stream);
|
|
2382
|
+
const first = listeners === undefined;
|
|
2383
|
+
if (listeners === undefined)
|
|
2384
|
+
this.#processWatchers.set(stream, listeners = new Set);
|
|
2385
|
+
listeners.add(listener);
|
|
2386
|
+
listener(this.#processState(stream));
|
|
2387
|
+
if (first && hostSupportsFeature(this.#hostMinor, "processes") && this.#processDocDeclared()) {
|
|
2388
|
+
this.#wanted.add(stream);
|
|
2389
|
+
if (this.#helloReceived)
|
|
2390
|
+
this.#subscribe(stream);
|
|
2391
|
+
}
|
|
2392
|
+
return () => {
|
|
2393
|
+
if (!listeners.delete(listener) || listeners.size > 0)
|
|
2394
|
+
return;
|
|
2395
|
+
this.#processWatchers.delete(stream);
|
|
2396
|
+
if (this.#streamStillWanted(stream))
|
|
2397
|
+
return;
|
|
2398
|
+
if (!this.#wanted.has(stream))
|
|
2399
|
+
return;
|
|
2400
|
+
this.#releaseStream(stream);
|
|
2401
|
+
};
|
|
2402
|
+
},
|
|
2403
|
+
attach: (processId, since, sink) => {
|
|
2404
|
+
const stream = processStream(processId);
|
|
2405
|
+
const reason = this.#processesUnavailable();
|
|
2406
|
+
if (reason !== undefined) {
|
|
2407
|
+
sink.ended(reason);
|
|
2408
|
+
return () => {};
|
|
2409
|
+
}
|
|
2410
|
+
if (this.#processSinks.has(stream) && this.#wanted.has(stream))
|
|
2411
|
+
this.#send({ type: "unsubscribe", stream });
|
|
2412
|
+
this.#processSinks.set(stream, sink);
|
|
2413
|
+
if (since === undefined)
|
|
2414
|
+
this.#processPositions.delete(stream);
|
|
2415
|
+
else
|
|
2416
|
+
this.#processPositions.set(stream, since);
|
|
2417
|
+
this.#wanted.add(stream);
|
|
2418
|
+
if (this.#helloReceived)
|
|
2419
|
+
this.#subscribe(stream);
|
|
2420
|
+
return () => {
|
|
2421
|
+
if (this.#processSinks.get(stream) !== sink)
|
|
2422
|
+
return;
|
|
2423
|
+
this.#processSinks.delete(stream);
|
|
2424
|
+
this.#processPositions.delete(stream);
|
|
2425
|
+
if (this.#wanted.delete(stream))
|
|
2426
|
+
this.#send({ type: "unsubscribe", stream });
|
|
2427
|
+
};
|
|
2428
|
+
},
|
|
2429
|
+
stop: (conversationId, processId) => this.#processCall("stopProcess", { conversationId, id: processId }).then(({ state }) => state),
|
|
2430
|
+
unavailable: () => this.#processesUnavailable()
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2227
2433
|
#host(name) {
|
|
2228
2434
|
return this.#values.get(name);
|
|
2229
2435
|
}
|
|
@@ -2324,22 +2530,33 @@ class RemoteClient {
|
|
|
2324
2530
|
record.error = undefined;
|
|
2325
2531
|
this.#clearSubmissionTimer(record);
|
|
2326
2532
|
}
|
|
2327
|
-
#entriesOf(id) {
|
|
2328
|
-
if (id === undefined)
|
|
2329
|
-
return [];
|
|
2330
|
-
const view = this.#values.get(conversationStream(id));
|
|
2331
|
-
return view?.entries ?? [];
|
|
2332
|
-
}
|
|
2333
2533
|
#entryPresent(record) {
|
|
2334
|
-
if (record.entry === undefined)
|
|
2534
|
+
if (record.entry === undefined || record.conversationId === undefined)
|
|
2535
|
+
return false;
|
|
2536
|
+
const shown = this.#shownView(conversationStream(record.conversationId));
|
|
2537
|
+
if (shown === undefined)
|
|
2335
2538
|
return false;
|
|
2336
|
-
|
|
2539
|
+
if (shown.before !== undefined && Number(record.entry) <= Number(shown.before))
|
|
2540
|
+
return true;
|
|
2541
|
+
return shown.entries.some((entry) => String(entry.id) === String(record.entry));
|
|
2542
|
+
}
|
|
2543
|
+
#inboxOf(id) {
|
|
2544
|
+
if (id === undefined)
|
|
2545
|
+
return;
|
|
2546
|
+
const view = this.#values.get(conversationStream(id));
|
|
2547
|
+
return view?.docs?.["pi.inbox"];
|
|
2337
2548
|
}
|
|
2338
2549
|
#inboxHas(record) {
|
|
2339
|
-
if (record.
|
|
2550
|
+
if (record.submissionId === undefined)
|
|
2340
2551
|
return false;
|
|
2341
|
-
const
|
|
2342
|
-
return
|
|
2552
|
+
const items = this.#inboxOf(record.conversationId)?.items ?? [];
|
|
2553
|
+
return items.some((item) => String(item.id) === String(record.submissionId));
|
|
2554
|
+
}
|
|
2555
|
+
#markChecked(record) {
|
|
2556
|
+
if (record.conversationId === undefined)
|
|
2557
|
+
return;
|
|
2558
|
+
record.checkedVersion = this.#streamVersions.get(conversationStream(record.conversationId)) ?? 0;
|
|
2559
|
+
record.checkedInbox = this.#inboxOf(record.conversationId);
|
|
2343
2560
|
}
|
|
2344
2561
|
#applySubmissionRecord(record, found) {
|
|
2345
2562
|
record.admitted = true;
|
|
@@ -2370,11 +2587,10 @@ class RemoteClient {
|
|
|
2370
2587
|
this.#settleSubmission(record);
|
|
2371
2588
|
continue;
|
|
2372
2589
|
}
|
|
2373
|
-
if (record.conversationId !== undefined && record.
|
|
2590
|
+
if (record.conversationId !== undefined && !record.confirming && record.admitted && (record.entry !== undefined || !this.#inboxHas(record))) {
|
|
2374
2591
|
const stream = conversationStream(record.conversationId);
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
record.checkedVersion = version;
|
|
2592
|
+
if (this.#fresh.has(stream) && (record.checkedVersion !== (this.#streamVersions.get(stream) ?? 0) || record.checkedInbox !== this.#inboxOf(record.conversationId))) {
|
|
2593
|
+
this.#markChecked(record);
|
|
2378
2594
|
this.#confirmSubmission(record);
|
|
2379
2595
|
}
|
|
2380
2596
|
}
|
|
@@ -2408,6 +2624,8 @@ class RemoteClient {
|
|
|
2408
2624
|
} catch {} finally {
|
|
2409
2625
|
record.confirming = false;
|
|
2410
2626
|
}
|
|
2627
|
+
if (!this.#closed && !this.#submissionSettled(record))
|
|
2628
|
+
this.#reconcileSubmissions();
|
|
2411
2629
|
}
|
|
2412
2630
|
#submissionArgs(record) {
|
|
2413
2631
|
const conversationId = record.conversationId;
|
|
@@ -2451,11 +2669,11 @@ class RemoteClient {
|
|
|
2451
2669
|
record.phase = "inFlight";
|
|
2452
2670
|
if (this.#entryPresent(record))
|
|
2453
2671
|
this.#settleSubmission(record);
|
|
2672
|
+
} else if (this.#inboxHas(record)) {
|
|
2673
|
+
this.#settleSubmission(record);
|
|
2454
2674
|
} else {
|
|
2455
2675
|
record.phase = "inFlight";
|
|
2456
|
-
|
|
2457
|
-
record.checkedVersion = (this.#streamVersions.get(conversationStream(record.conversationId)) ?? 0) + (this.#streamVersions.get(docStream("pi.inbox", record.conversationId)) ?? 0);
|
|
2458
|
-
}
|
|
2676
|
+
this.#markChecked(record);
|
|
2459
2677
|
this.#confirmSubmission(record);
|
|
2460
2678
|
}
|
|
2461
2679
|
this.#touchSubmissions();
|
|
@@ -2701,7 +2919,7 @@ class RemoteClient {
|
|
|
2701
2919
|
this.#reconcileSubmissions();
|
|
2702
2920
|
const current = this.#current;
|
|
2703
2921
|
const side = this.#side;
|
|
2704
|
-
const conversation = current === undefined ? undefined : this.#
|
|
2922
|
+
const conversation = current === undefined ? undefined : this.#shownView(conversationStream(current));
|
|
2705
2923
|
const summaries = this.#host("conversations") ?? [];
|
|
2706
2924
|
const index = this.#host("index") ?? { projects: [], conversations: [] };
|
|
2707
2925
|
const devices = this.#host("devices")?.devices ?? [];
|
|
@@ -2717,7 +2935,7 @@ class RemoteClient {
|
|
|
2717
2935
|
submissions: this.#activeSubmissions(),
|
|
2718
2936
|
side: side === undefined ? undefined : {
|
|
2719
2937
|
id: side,
|
|
2720
|
-
conversation: this.#
|
|
2938
|
+
conversation: this.#shownView(conversationStream(side)),
|
|
2721
2939
|
docs: this.#docsOf(side)
|
|
2722
2940
|
},
|
|
2723
2941
|
devices,
|
|
@@ -2726,6 +2944,7 @@ class RemoteClient {
|
|
|
2726
2944
|
providers: this.#host("providers") ?? null,
|
|
2727
2945
|
settings: this.#host("settings") ?? null,
|
|
2728
2946
|
agents: this.#host("agents") ?? null,
|
|
2947
|
+
upgrade: this.#host("upgrade") ?? null,
|
|
2729
2948
|
defaults: this.#effectiveDefaults(this.#helloDefaults)
|
|
2730
2949
|
});
|
|
2731
2950
|
}
|
|
@@ -2790,6 +3009,11 @@ class RemoteClient {
|
|
|
2790
3009
|
}
|
|
2791
3010
|
});
|
|
2792
3011
|
}
|
|
3012
|
+
#requireUpgrade() {
|
|
3013
|
+
if (!hostSupportsCall(this.#hostMinor, "startUpgrade")) {
|
|
3014
|
+
throw new Error("The host is too old for this (needs protocol 5.30); run `pinomad upgrade` on the host");
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
2793
3017
|
#command(operation) {
|
|
2794
3018
|
this.#commands = this.#commands.then(operation).then(() => {}, (error) => this.#notice("error", error instanceof Error ? error.message : String(error)));
|
|
2795
3019
|
return this.#commands;
|
|
@@ -2949,6 +3173,17 @@ class RemoteClient {
|
|
|
2949
3173
|
});
|
|
2950
3174
|
return outcome;
|
|
2951
3175
|
}),
|
|
3176
|
+
reorderQueued: (submissionId, before, target) => this.#request(async () => {
|
|
3177
|
+
if (!hostSupportsCall(this.#hostMinor, "reorderQueued")) {
|
|
3178
|
+
throw new Error("The host is too old for this (needs protocol 5.29); run `pinomad upgrade` on the host");
|
|
3179
|
+
}
|
|
3180
|
+
const { outcome } = await this.#call("reorderQueued", {
|
|
3181
|
+
conversationId: target ?? conversationId(),
|
|
3182
|
+
submissionId,
|
|
3183
|
+
...before === undefined ? {} : { before }
|
|
3184
|
+
});
|
|
3185
|
+
return outcome;
|
|
3186
|
+
}),
|
|
2952
3187
|
setThinkingLevel: (level) => this.#command(() => this.#call("setThinkingLevel", { conversationId: conversationId(), level })),
|
|
2953
3188
|
setModel: (model) => this.#command(() => this.#call("setModel", { conversationId: conversationId(), model })),
|
|
2954
3189
|
setAgent: (agent) => this.#command(() => {
|
|
@@ -3017,6 +3252,7 @@ class RemoteClient {
|
|
|
3017
3252
|
}
|
|
3018
3253
|
return this.#call("listSkills", { scope });
|
|
3019
3254
|
},
|
|
3255
|
+
loadEarlier: (id) => this.#loadEarlier(id),
|
|
3020
3256
|
listCommands: (id) => {
|
|
3021
3257
|
if (!hostSupportsCall(this.#hostMinor, "listCommands")) {
|
|
3022
3258
|
return Promise.reject(new Error("The host is too old for this (needs protocol 5.17); run `pinomad upgrade` on the host"));
|
|
@@ -3065,8 +3301,23 @@ class RemoteClient {
|
|
|
3065
3301
|
if (patch.hostName !== undefined && !hostSupportsFeature(this.#hostMinor, "hostName")) {
|
|
3066
3302
|
throw new Error("The host is too old for this (needs protocol 5.12); run `pinomad upgrade` on the host");
|
|
3067
3303
|
}
|
|
3304
|
+
if (patch.autoUpgrade !== undefined && !hostSupportsFeature(this.#hostMinor, "autoUpgrade")) {
|
|
3305
|
+
throw new Error("The host is too old for this (needs protocol 5.30); run `pinomad upgrade` on the host");
|
|
3306
|
+
}
|
|
3068
3307
|
return this.#call("updateSettings", { patch });
|
|
3069
3308
|
}),
|
|
3309
|
+
startUpgrade: async (when) => {
|
|
3310
|
+
this.#requireUpgrade();
|
|
3311
|
+
await this.#call("startUpgrade", { when });
|
|
3312
|
+
},
|
|
3313
|
+
cancelUpgrade: async () => {
|
|
3314
|
+
this.#requireUpgrade();
|
|
3315
|
+
await this.#call("cancelUpgrade", {});
|
|
3316
|
+
},
|
|
3317
|
+
checkUpgrade: async () => {
|
|
3318
|
+
this.#requireUpgrade();
|
|
3319
|
+
await this.#call("checkUpgrade", {});
|
|
3320
|
+
},
|
|
3070
3321
|
createAgent: (role) => this.#request(async () => {
|
|
3071
3322
|
if (!hostSupportsCall(this.#hostMinor, "createAgent")) {
|
|
3072
3323
|
throw new Error("The host is too old for this (needs protocol 5.6); run `pinomad upgrade` on the host");
|
|
@@ -3103,7 +3354,7 @@ class RemoteClient {
|
|
|
3103
3354
|
await this.#acquire(id);
|
|
3104
3355
|
this.#current = id;
|
|
3105
3356
|
if (previous !== undefined)
|
|
3106
|
-
this.#release(previous
|
|
3357
|
+
this.#release(previous);
|
|
3107
3358
|
this.#refresh();
|
|
3108
3359
|
}
|
|
3109
3360
|
#unshow() {
|
|
@@ -3111,7 +3362,7 @@ class RemoteClient {
|
|
|
3111
3362
|
if (previous === undefined)
|
|
3112
3363
|
return;
|
|
3113
3364
|
this.#current = undefined;
|
|
3114
|
-
this.#release(previous
|
|
3365
|
+
this.#release(previous);
|
|
3115
3366
|
this.#refresh();
|
|
3116
3367
|
}
|
|
3117
3368
|
async#clear() {
|
|
@@ -3126,7 +3377,7 @@ class RemoteClient {
|
|
|
3126
3377
|
await this.#acquire(id);
|
|
3127
3378
|
this.#side = id;
|
|
3128
3379
|
if (previous !== undefined)
|
|
3129
|
-
this.#release(previous
|
|
3380
|
+
this.#release(previous);
|
|
3130
3381
|
this.#refresh();
|
|
3131
3382
|
}
|
|
3132
3383
|
#close() {
|