@almadar/shell-runtime 0.1.0 → 1.1.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-2WCVOMZ2.js +112 -0
- package/dist/chunk-57SBCUSM.js +62 -0
- package/dist/{chunk-Q2H75UYT.js → chunk-A2JJT4GA.js} +1 -37
- package/dist/chunk-BAS26HBN.js +10 -0
- package/dist/{chunk-REPX5QPI.js → chunk-BOVU4TCQ.js} +11 -3
- package/dist/chunk-BY5G4225.js +6 -0
- package/dist/chunk-DFDNHVDW.js +284 -0
- package/dist/{chunk-NATGCIVM.js → chunk-JVSREWQO.js} +378 -130
- package/dist/{chunk-DYOJGLIX.js → chunk-LKWYCYX5.js} +2 -5
- package/dist/chunk-LYFIQPXX.js +1 -0
- package/dist/{chunk-ODJCZ27I.js → chunk-N7HYNKK6.js} +35 -2
- package/dist/{chunk-4J2QEIUV.js → chunk-NTFQ36UN.js} +4 -30
- package/dist/chunk-NU6LVKGS.js +15 -0
- package/dist/{chunk-VNMG7NKY.js → chunk-OQVSN7DP.js} +21 -26
- package/dist/chunk-P2N6K2IV.js +20 -0
- package/dist/{chunk-QQRDUMB2.js → chunk-QCJWPXLH.js} +11 -11
- package/dist/{chunk-QELZ6TEJ.js → chunk-UD2CXYSJ.js} +39 -3
- package/dist/{chunk-UJHOUZAL.js → chunk-V4EMDPCO.js} +2 -5
- package/dist/chunk-VKQA2DJX.js +409 -0
- package/dist/chunk-VNGXRYFF.js +9 -0
- package/dist/{chunk-HNOAZWFH.js → chunk-WGCWJOT2.js} +2 -0
- package/dist/chunk-XZGVOBY6.js +33 -0
- package/dist/chunk-ZUSM4VW4.js +197 -0
- package/dist/client/EventTransport.d.ts +23 -7
- package/dist/client/EventTransport.js +1 -2
- package/dist/client/awaiting.js +0 -1
- package/dist/client/channel-transport.d.ts +6 -6
- package/dist/client/channel-transport.js +0 -1
- package/dist/client/circuit-store.d.ts +16 -5
- package/dist/client/circuit-store.js +2 -3
- package/dist/client/client-kernel.d.ts +36 -15
- package/dist/client/declared-input-dispatch.d.ts +19 -2
- package/dist/client/declared-input-dispatch.js +1 -2
- package/dist/client/inbound-dispatch.d.ts +21 -2
- package/dist/client/inbound-dispatch.js +1 -2
- package/dist/client/local-fold.d.ts +15 -8
- package/dist/client/local-fold.js +1 -2
- package/dist/client/render-sigils.js +0 -1
- package/dist/client/state-book.d.ts +9 -16
- package/dist/client/state-book.js +1 -2
- package/dist/client/trait-mount-error.js +0 -1
- package/dist/credentials/env-vars.d.ts +5 -1
- package/dist/credentials/env-vars.js +1 -3
- package/dist/credentials/scope.d.ts +8 -7
- package/dist/credentials/scope.js +0 -1
- package/dist/credentials/vault.d.ts +142 -0
- package/dist/credentials/vault.js +1 -0
- package/dist/effects/in-flight-calls.js +0 -1
- package/dist/effects/on-device-model.js +0 -1
- package/dist/events/EventBus.d.ts +1 -4
- package/dist/events/EventBus.js +0 -1
- package/dist/events/emit-stamp.js +0 -1
- package/dist/index.d.ts +25 -42
- package/dist/index.js +27 -45
- package/dist/kernel/decode.d.ts +10 -10
- package/dist/kernel/decode.js +1 -2
- package/dist/kernel/engine.d.ts +133 -31
- package/dist/kernel/engine.js +7 -9
- package/dist/kernel/frame-applier.d.ts +8 -7
- package/dist/kernel/frame-applier.js +1 -2
- package/dist/kernel/host.d.ts +148 -16
- package/dist/kernel/host.js +4 -7
- package/dist/kernel/lane.d.ts +1 -22
- package/dist/kernel/lane.js +1 -2
- package/dist/kernel/os-fetch-port.d.ts +7 -0
- package/dist/kernel/os-fetch-port.js +3 -0
- package/dist/kernel/os-port.d.ts +30 -8
- package/dist/kernel/os-port.js +1 -2
- package/dist/kernel/program-effects.d.ts +11 -28
- package/dist/kernel/program-effects.js +1 -2
- package/dist/kernel/secret-ports.d.ts +139 -0
- package/dist/kernel/secret-ports.js +2 -0
- package/dist/kernel/wire.d.ts +113 -12
- package/dist/loader/fetch-loader.d.ts +26 -0
- package/dist/loader/fetch-loader.js +1 -0
- package/dist/loader/lazy-page.js +0 -1
- package/dist/loader/schema-loader.d.ts +1 -17
- package/dist/persistence/native-memory.js +0 -1
- package/dist/runtime/entities/declaredCollections.d.ts +6 -0
- package/dist/runtime/entities/declaredCollections.js +1 -0
- package/dist/runtime/evaluation/local-store-transport.d.ts +16 -23
- package/dist/runtime/evaluation/local-store-transport.js +9 -13
- package/dist/runtime/evaluation/residence-transport.d.ts +3 -3
- package/dist/runtime/evaluation/residence-transport.js +1 -2
- package/dist/runtime/evaluation/routed-persistence.js +0 -1
- package/dist/runtime/events/identity/routing.d.ts +2 -53
- package/dist/runtime/events/identity/routing.js +1 -2
- package/dist/runtime/types.d.ts +5 -205
- package/dist/runtime/types.js +1 -2
- package/dist/service/default-call-service.js +0 -1
- package/dist/service/service-provider.js +0 -1
- package/package.json +8 -5
- package/dist/chunk-2JU7Y4KU.js +0 -21
- package/dist/chunk-46RWK2B5.js +0 -28
- package/dist/chunk-4ZICQ5PM.js +0 -192
- package/dist/chunk-53IPZXDP.js +0 -76
- package/dist/chunk-5D63EMO5.js +0 -16
- package/dist/chunk-CPSQXEJ6.js +0 -55
- package/dist/chunk-CQP5XAX6.js +0 -173
- package/dist/chunk-CROS2GEX.js +0 -49
- package/dist/chunk-D4XJTBLB.js +0 -217
- package/dist/chunk-D5USD2EX.js +0 -232
- package/dist/chunk-EV56K4GB.js +0 -63
- package/dist/chunk-FQ6IZMIU.js +0 -10
- package/dist/chunk-HMK6YMQS.js +0 -135
- package/dist/chunk-IHQLE5M7.js +0 -62
- package/dist/chunk-JFWFDUTR.js +0 -35
- package/dist/chunk-MHYSHSRG.js +0 -54
- package/dist/chunk-MLKGABMK.js +0 -7
- package/dist/chunk-NAECVIAR.js +0 -322
- package/dist/chunk-Q7JK4KY7.js +0 -9
- package/dist/chunk-RBUMDYFS.js +0 -681
- package/dist/chunk-SAS5KKCR.js +0 -43
- package/dist/chunk-SWPDU752.js +0 -28
- package/dist/chunk-T775TUCD.js +0 -18
- package/dist/chunk-VMOBW7RK.js +0 -21
- package/dist/chunk-WNPNNYXY.js +0 -22
- package/dist/chunk-XB5GYODW.js +0 -28
- package/dist/chunk-YL2SO5TP.js +0 -334
- package/dist/chunk-YO4LWS3I.js +0 -606
- package/dist/chunk-ZAQSS47X.js +0 -157
- package/dist/chunk-ZCQUUJIF.js +0 -129
- package/dist/client/ServerBridge.d.ts +0 -126
- package/dist/client/ServerBridge.js +0 -3
- package/dist/credentials/seed.d.ts +0 -21
- package/dist/credentials/seed.js +0 -2
- package/dist/credentials/store.d.ts +0 -110
- package/dist/credentials/store.js +0 -2
- package/dist/index-hT1xCdTq.d.ts +0 -46
- package/dist/kernel/beacon.d.ts +0 -32
- package/dist/kernel/beacon.js +0 -2
- package/dist/kernel/http.d.ts +0 -302
- package/dist/kernel/http.js +0 -6
- package/dist/kernel/oauth-effects.d.ts +0 -25
- package/dist/kernel/oauth-effects.js +0 -7
- package/dist/kernel/oauth-port.d.ts +0 -31
- package/dist/kernel/oauth-port.js +0 -2
- package/dist/loader/http-loader.d.ts +0 -111
- package/dist/loader/http-loader.js +0 -2
- package/dist/runtime/effects/composition/compose-behaviors.d.ts +0 -58
- package/dist/runtime/effects/composition/compose-behaviors.js +0 -4
- package/dist/runtime/effects/composition/event-wiring.d.ts +0 -38
- package/dist/runtime/effects/composition/event-wiring.js +0 -2
- package/dist/runtime/effects/composition/index.d.ts +0 -5
- package/dist/runtime/effects/composition/index.js +0 -6
- package/dist/runtime/effects/composition/layout-strategy.d.ts +0 -35
- package/dist/runtime/effects/composition/layout-strategy.js +0 -2
- package/dist/runtime/effects/composition/pipe.d.ts +0 -25
- package/dist/runtime/effects/composition/pipe.js +0 -2
- package/dist/runtime/entities/mockStoreSeed.d.ts +0 -56
- package/dist/runtime/entities/mockStoreSeed.js +0 -3
- package/dist/runtime/entities/seedBrowserStore.d.ts +0 -28
- package/dist/runtime/entities/seedBrowserStore.js +0 -2
- package/dist/runtime/entities/std-mock-seed.d.ts +0 -87
- package/dist/runtime/entities/std-mock-seed.js +0 -2
- package/dist/runtime/time/TickScheduler.d.ts +0 -77
- package/dist/runtime/time/TickScheduler.js +0 -3
- package/dist/runtime/time/cron.d.ts +0 -25
- package/dist/runtime/time/cron.js +0 -2
- package/dist/runtime/time/duration.d.ts +0 -20
- package/dist/runtime/time/duration.js +0 -2
- package/dist/runtime/traits/OrbitalTraitParsing.d.ts +0 -34
- package/dist/runtime/traits/OrbitalTraitParsing.js +0 -2
- package/dist/runtime/traits/listen-targets.d.ts +0 -16
- package/dist/runtime/traits/listen-targets.js +0 -3
- package/dist/runtime/traits/trait-index.d.ts +0 -35
- package/dist/runtime/traits/trait-index.js +0 -3
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import { isProgramEffect } from './chunk-
|
|
2
|
-
import { systemRandom, base64Standard
|
|
3
|
-
import { untilAborted } from './chunk-MZKIL5IN.js';
|
|
1
|
+
import { isProgramEffect } from './chunk-WGCWJOT2.js';
|
|
2
|
+
import { systemRandom, base64Standard } from './chunk-ZUSM4VW4.js';
|
|
4
3
|
import { OnDeviceModelFailure, promptOnDevice, embedOnDevice } from './chunk-FWA5K7Q2.js';
|
|
5
|
-
import {
|
|
4
|
+
import { untilAborted } from './chunk-MZKIL5IN.js';
|
|
5
|
+
import { isJsonObject, WriteDeclinedError, QueryDeclinedError, TabHostFailure, RowSetRefSchema, toPayloadValue, credentialStatusConnected } from '@almadar/core';
|
|
6
6
|
|
|
7
|
+
function callerViewer(caller) {
|
|
8
|
+
return caller === void 0 ? void 0 : { id: caller.principal, ...caller.role !== void 0 ? { role: caller.role } : {} };
|
|
9
|
+
}
|
|
7
10
|
function issuePayload(issue) {
|
|
8
11
|
const payload = { code: issue.code, message: issue.message };
|
|
9
12
|
if (issue.path !== void 0) payload.path = issue.path;
|
|
@@ -11,8 +14,24 @@ function issuePayload(issue) {
|
|
|
11
14
|
if (issue.column !== void 0) payload.column = issue.column;
|
|
12
15
|
return payload;
|
|
13
16
|
}
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
var HostAskError = class extends Error {
|
|
18
|
+
constructor(kind, message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.kind = kind;
|
|
21
|
+
}
|
|
22
|
+
kind;
|
|
23
|
+
};
|
|
24
|
+
function programDocumentJson(document) {
|
|
25
|
+
return typeof document === "string" ? document : toPayloadValue(document);
|
|
26
|
+
}
|
|
27
|
+
function kernelEventRequestParser(kernel) {
|
|
28
|
+
return (payload, options) => {
|
|
29
|
+
try {
|
|
30
|
+
return { ok: kernel.parseEventRequest(JSON.stringify(payload), JSON.stringify(options)) };
|
|
31
|
+
} catch (error) {
|
|
32
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
33
|
+
}
|
|
34
|
+
};
|
|
16
35
|
}
|
|
17
36
|
async function settle(work) {
|
|
18
37
|
try {
|
|
@@ -39,10 +58,10 @@ async function callService(ports, request, scope) {
|
|
|
39
58
|
ports.deliverLive({ orbital, trait: site.trait, event: onMessage }, payload, scope.originClientId);
|
|
40
59
|
};
|
|
41
60
|
const call = async (signal, outsideRequest) => {
|
|
42
|
-
const host = site !== void 0 ? ports.servicePorts({ orbital, trait: site.trait },
|
|
61
|
+
const host = site !== void 0 ? ports.servicePorts({ orbital, trait: site.trait }, callerViewer(caller), message, outsideRequest === true ? void 0 : scope.assertReentry) : void 0;
|
|
43
62
|
const context = caller !== void 0 || signal !== void 0 || host !== void 0 || outsideRequest !== void 0 ? { ...identity, ...host !== void 0 ? { host } : {}, ...signal !== void 0 ? { signal } : {}, ...outsideRequest !== void 0 ? { outsideRequest } : {} } : void 0;
|
|
44
63
|
const result = await ports.callService(service, method, serviceParams(args), context);
|
|
45
|
-
return result
|
|
64
|
+
return result;
|
|
46
65
|
};
|
|
47
66
|
const deferred = site?.followOn === true ? ports.deferCallOutcome() : void 0;
|
|
48
67
|
if (site !== void 0 && deferred !== void 0) {
|
|
@@ -55,7 +74,7 @@ async function callService(ports, request, scope) {
|
|
|
55
74
|
return outcome.cancelled ? { kind: "serviceCancelled" } : { kind: "service", result: { ok: outcome.value } };
|
|
56
75
|
};
|
|
57
76
|
try {
|
|
58
|
-
return await
|
|
77
|
+
return await run();
|
|
59
78
|
} catch (error) {
|
|
60
79
|
return { kind: "service", result: { error: failureMessage(error) } };
|
|
61
80
|
}
|
|
@@ -69,7 +88,6 @@ function startDeferredCall(defer, site, key, call, context) {
|
|
|
69
88
|
payload,
|
|
70
89
|
entityId: scope.entityId,
|
|
71
90
|
originClientId: scope.originClientId,
|
|
72
|
-
user: scope.user,
|
|
73
91
|
locale: scope.locale
|
|
74
92
|
});
|
|
75
93
|
const outcome = key === void 0 ? call(void 0, true).then((value) => ({ cancelled: false, value })) : ports.inFlightCalls.run(key, (signal) => untilAborted(signal, call(signal, true)));
|
|
@@ -100,7 +118,7 @@ async function substrate(ports, request, scope) {
|
|
|
100
118
|
const source = site.emit.onMessage !== void 0 ? ports.substrateStream?.(request.op, request.args, scope) : void 0;
|
|
101
119
|
if (ports.inFlightCalls.inFlight(key)) return { kind: "substrate", result: { error: { message: source !== void 0 ? `key ${key} already has a stream open` : `key ${key} already has a completion pending`, errors: [] } } };
|
|
102
120
|
const deliver = (fired) => {
|
|
103
|
-
if (fired !== null) sink({ orbital: site.orbital ?? scope.orbital, traitName: site.trait, event: fired.event, payload: isJsonObject(fired.payload) ? fired.payload : void 0, entityId: site.entityId, originClientId: scope.originClientId,
|
|
121
|
+
if (fired !== null) sink({ orbital: site.orbital ?? scope.orbital, traitName: site.trait, event: fired.event, payload: isJsonObject(fired.payload) ? fired.payload : void 0, entityId: site.entityId, originClientId: scope.originClientId, locale: scope.locale });
|
|
104
122
|
};
|
|
105
123
|
const failed = (error) => deliver(complete(site, key, request.op, request.args, { error: { message: failureMessage(error), errors: [] } }));
|
|
106
124
|
if (source !== void 0 && ports.substrateMessage !== void 0) {
|
|
@@ -131,18 +149,168 @@ async function substrate(ports, request, scope) {
|
|
|
131
149
|
}, failed);
|
|
132
150
|
return { kind: "serviceDeferred" };
|
|
133
151
|
}
|
|
152
|
+
function optionalKey(op, args) {
|
|
153
|
+
if (args.length === 0) return { key: void 0 };
|
|
154
|
+
const [key] = args;
|
|
155
|
+
if (args.length === 1 && typeof key === "string" && key !== "") return { key };
|
|
156
|
+
return { error: `${op}: the key must be one non-empty text` };
|
|
157
|
+
}
|
|
158
|
+
async function performHostAsk(actions, op, args) {
|
|
159
|
+
const fail = (message) => ({ error: message });
|
|
160
|
+
if (op === "auth/sign-out") {
|
|
161
|
+
if (args.length !== 0) return fail("auth/sign-out takes no arguments");
|
|
162
|
+
if (actions?.signOut === void 0) return fail("no host sign-out is configured for auth/sign-out");
|
|
163
|
+
try {
|
|
164
|
+
await actions.signOut();
|
|
165
|
+
return { ok: null };
|
|
166
|
+
} catch (error) {
|
|
167
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const audio = actions?.audio;
|
|
171
|
+
if (audio === void 0) return fail(`no host audio is configured for ${op}`);
|
|
172
|
+
let ask;
|
|
173
|
+
if (op === "audio/stop-music") {
|
|
174
|
+
if (args.length !== 0) return fail("audio/stop-music takes no arguments");
|
|
175
|
+
ask = { op };
|
|
176
|
+
} else {
|
|
177
|
+
const parsed = optionalKey(op, args);
|
|
178
|
+
if ("error" in parsed) return fail(parsed.error);
|
|
179
|
+
if (op === "audio/play-sound") {
|
|
180
|
+
if (parsed.key === void 0) return fail("audio/play-sound: the key must be one non-empty text");
|
|
181
|
+
ask = { op, key: parsed.key };
|
|
182
|
+
} else if (op === "audio/stop-sound" || op === "audio/change-music") {
|
|
183
|
+
ask = { op, key: parsed.key };
|
|
184
|
+
} else {
|
|
185
|
+
return fail(`no host action is configured for ${op}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
audio(ask);
|
|
190
|
+
return { ok: null };
|
|
191
|
+
} catch (error) {
|
|
192
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
var SIGN_IN_FIELDS = {
|
|
196
|
+
google: ["method"],
|
|
197
|
+
email: ["method", "email", "password"],
|
|
198
|
+
emailLink: ["method", "email", "continueUrl", "link"],
|
|
199
|
+
customToken: ["method", "token"]
|
|
200
|
+
};
|
|
201
|
+
function parseSignInRequest(args) {
|
|
202
|
+
const [request] = args;
|
|
203
|
+
if (args.length !== 1 || !isJsonObject(request)) return { error: "auth/sign-in takes one request object" };
|
|
204
|
+
const method = request.method;
|
|
205
|
+
const allowed = typeof method === "string" ? SIGN_IN_FIELDS[method] : void 0;
|
|
206
|
+
if (typeof method !== "string" || allowed === void 0) return { error: "auth/sign-in: method must be google, email, emailLink or customToken" };
|
|
207
|
+
const text = (field) => {
|
|
208
|
+
const value = request[field];
|
|
209
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
210
|
+
};
|
|
211
|
+
for (const field of Object.keys(request)) {
|
|
212
|
+
if (!allowed.includes(field)) return { error: `auth/sign-in: ${field} is not a field of the ${method} method` };
|
|
213
|
+
}
|
|
214
|
+
if (method === "google") return { request: { method } };
|
|
215
|
+
if (method === "email") {
|
|
216
|
+
const email2 = text("email");
|
|
217
|
+
const password = text("password");
|
|
218
|
+
return email2 !== void 0 && password !== void 0 ? { request: { method, email: email2, password } } : { error: "auth/sign-in: email takes an email and a password" };
|
|
219
|
+
}
|
|
220
|
+
if (method === "customToken") {
|
|
221
|
+
const token = text("token");
|
|
222
|
+
return token !== void 0 ? { request: { method, token } } : { error: "auth/sign-in: customToken takes a token" };
|
|
223
|
+
}
|
|
224
|
+
const email = text("email");
|
|
225
|
+
const continueUrl = text("continueUrl");
|
|
226
|
+
const link = text("link");
|
|
227
|
+
if (email === void 0 || continueUrl === void 0 === (link === void 0)) {
|
|
228
|
+
return { error: "auth/sign-in: emailLink takes an email and exactly one of continueUrl (send the link) or link (complete the sign-in)" };
|
|
229
|
+
}
|
|
230
|
+
return continueUrl !== void 0 ? { request: { method: "emailLink", email, continueUrl } } : { request: { method: "emailLink", email, link } };
|
|
231
|
+
}
|
|
232
|
+
function parseConsentRequest(args) {
|
|
233
|
+
const [request] = args;
|
|
234
|
+
if (args.length !== 1 || !isJsonObject(request)) return { error: "auth/consent takes one request object" };
|
|
235
|
+
for (const field of Object.keys(request)) {
|
|
236
|
+
if (field !== "authorizeUrl") return { error: `auth/consent: ${field} is not a field of the request` };
|
|
237
|
+
}
|
|
238
|
+
const authorizeUrl = request.authorizeUrl;
|
|
239
|
+
return typeof authorizeUrl === "string" && authorizeUrl !== "" ? { request: { authorizeUrl } } : { error: "auth/consent: authorizeUrl must be one non-empty text" };
|
|
240
|
+
}
|
|
241
|
+
function consentFromRedirect(redirectUrl) {
|
|
242
|
+
let params;
|
|
243
|
+
try {
|
|
244
|
+
params = new URL(redirectUrl).searchParams;
|
|
245
|
+
} catch {
|
|
246
|
+
throw new HostAskError("invalid", "the consent redirect is not a URL");
|
|
247
|
+
}
|
|
248
|
+
const code = params.get("code");
|
|
249
|
+
const state = params.get("state");
|
|
250
|
+
if (code === null || code === "" || state === null || state === "") throw new HostAskError("denied", "the consent redirect carried no code and state");
|
|
251
|
+
return { code, state };
|
|
252
|
+
}
|
|
253
|
+
async function performConsent(actions, args) {
|
|
254
|
+
const parsed = parseConsentRequest(args);
|
|
255
|
+
if ("error" in parsed) return { error: parsed.error, kind: "invalid" };
|
|
256
|
+
if (actions?.consent === void 0) return { error: "no host consent is configured for auth/consent", kind: "unavailable" };
|
|
257
|
+
try {
|
|
258
|
+
const { code, state } = await actions.consent(parsed.request);
|
|
259
|
+
return { ok: { code, state } };
|
|
260
|
+
} catch (error) {
|
|
261
|
+
if (error instanceof HostAskError) return { error: error.message, kind: error.kind };
|
|
262
|
+
return { error: error instanceof Error ? error.message : String(error), kind: "unavailable" };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async function performHostAnswer(actions, op, args) {
|
|
266
|
+
if (op === "auth/consent") return performConsent(actions, args);
|
|
267
|
+
if (op !== "auth/sign-in") {
|
|
268
|
+
const outcome = await performHostAsk(actions, op, args);
|
|
269
|
+
return "error" in outcome ? { error: outcome.error, kind: "unavailable" } : { ok: {} };
|
|
270
|
+
}
|
|
271
|
+
const parsed = parseSignInRequest(args);
|
|
272
|
+
if ("error" in parsed) return { error: parsed.error, kind: "invalid" };
|
|
273
|
+
if (actions?.signIn === void 0) return { error: "no host sign-in is configured for auth/sign-in", kind: "unavailable" };
|
|
274
|
+
try {
|
|
275
|
+
const answer = await actions.signIn(parsed.request);
|
|
276
|
+
const fields = {};
|
|
277
|
+
if ("sent" in answer) fields.sent = true;
|
|
278
|
+
else {
|
|
279
|
+
fields.uid = answer.uid;
|
|
280
|
+
if (answer.email !== void 0) fields.email = answer.email;
|
|
281
|
+
if (answer.displayName !== void 0) fields.displayName = answer.displayName;
|
|
282
|
+
}
|
|
283
|
+
return { ok: fields };
|
|
284
|
+
} catch (error) {
|
|
285
|
+
if (error instanceof HostAskError) return { error: error.message, kind: error.kind };
|
|
286
|
+
return { error: error instanceof Error ? error.message : String(error), kind: "unavailable" };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function hostAskReturn(completion, outcome) {
|
|
290
|
+
const keyed2 = completion.key !== void 0 ? { key: completion.key } : {};
|
|
291
|
+
const event = "ok" in outcome ? completion.success : completion.failure;
|
|
292
|
+
if (event === void 0) return void 0;
|
|
293
|
+
const payload = "ok" in outcome ? { ...outcome.ok, ...keyed2 } : { error: outcome.error, kind: outcome.kind, ...keyed2 };
|
|
294
|
+
return { trait: completion.trait, ...completion.entityId !== void 0 ? { entityId: completion.entityId } : {}, event, payload };
|
|
295
|
+
}
|
|
296
|
+
async function hostAction(ports, request) {
|
|
297
|
+
const outcome = await performHostAsk(ports.hostActions, request.op, request.args);
|
|
298
|
+
return "error" in outcome ? { kind: "substrate", result: { error: { message: outcome.error, errors: [] } } } : { kind: "substrate", result: { ok: null } };
|
|
299
|
+
}
|
|
134
300
|
async function substrateInPlace(ports, request, scope) {
|
|
301
|
+
if (request.op === "auth/sign-in" || request.op === "auth/consent") return { kind: "substrate", result: { error: { message: `${request.op} runs on the client host: a server-role kernel cannot ask it`, errors: [] } } };
|
|
302
|
+
if (request.op.startsWith("audio/") || request.op === "auth/sign-out") return hostAction(ports, request);
|
|
135
303
|
if (request.op === "program/live") return { kind: "substrate", result: { error: { message: "program/live streams to its onMessage route: give it a key and an onMessage route", errors: [] } } };
|
|
136
304
|
const handler = ports.programEffect();
|
|
137
305
|
const failure = (message, errors = []) => {
|
|
138
306
|
const detail = { message, errors };
|
|
139
307
|
return { kind: "substrate", result: { error: detail } };
|
|
140
308
|
};
|
|
141
|
-
if (request.op === "program/dispatch" || request.op === "program/inputs") {
|
|
142
|
-
const lent = ports.servicePorts({ orbital: scope.orbital, trait: "" },
|
|
309
|
+
if (request.op === "program/dispatch" || request.op === "program/inputs" || request.op === "program/resolve-rows") {
|
|
310
|
+
const lent = ports.servicePorts({ orbital: scope.orbital, trait: "" }, void 0, () => {
|
|
143
311
|
}, scope.assertReentry);
|
|
144
312
|
try {
|
|
145
|
-
const delivered = await deliverAgentAsk(request.op, request.args, lent);
|
|
313
|
+
const delivered = await deliverAgentAsk(request.op, request.args, lent, ports, scope.originClientId);
|
|
146
314
|
if (delivered.ok) return { kind: "substrate", result: { ok: delivered.result } };
|
|
147
315
|
return failure(delivered.error, delivered.errors ?? []);
|
|
148
316
|
} catch (error) {
|
|
@@ -152,22 +320,27 @@ async function substrateInPlace(ports, request, scope) {
|
|
|
152
320
|
if (handler === void 0) return failure(`no program host configured for ${request.op}`);
|
|
153
321
|
if (!isProgramEffect(request.op)) return failure(`no program host configured for ${request.op}`);
|
|
154
322
|
try {
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return failure(outcome.error, (outcome.errors ?? []).map((issue) => toJson(issuePayload(issue))));
|
|
323
|
+
const outcome = await handler(request.op, request.args, void 0);
|
|
324
|
+
if (outcome.ok) return { kind: "substrate", result: { ok: programDocumentJson(outcome.result) } };
|
|
325
|
+
return failure(outcome.error, (outcome.errors ?? []).map((issue) => issuePayload(issue)));
|
|
159
326
|
} catch (error) {
|
|
160
327
|
return failure(error instanceof Error ? error.message : String(error));
|
|
161
328
|
}
|
|
162
329
|
}
|
|
163
|
-
function
|
|
164
|
-
if (value === void 0 || value === null || value === "") return false;
|
|
165
|
-
if (Array.isArray(value)) return value.length > 0;
|
|
166
|
-
if (isJsonObject(value)) return Object.keys(value).length > 0;
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
async function deliverAgentAsk(op, args, host) {
|
|
330
|
+
async function deliverAgentAsk(op, args, host, ports, clientId) {
|
|
170
331
|
const [first, second, third] = args;
|
|
332
|
+
if (op === "program/resolve-rows") {
|
|
333
|
+
if (args.length !== 2 || first === void 0 || second === void 0) return { ok: false, error: "program/resolve-rows: the host is asked for a row-set reference and the viewer slot, which is null: a program names no viewer" };
|
|
334
|
+
const reference = RowSetRefSchema.safeParse(first);
|
|
335
|
+
if (!reference.success) {
|
|
336
|
+
const error = `program/resolve-rows: not a row-set reference: ${reference.error.message}`;
|
|
337
|
+
return { ok: false, error, errors: [{ code: "INVALID_REQUEST", message: error, status: 400 }] };
|
|
338
|
+
}
|
|
339
|
+
if (second !== null) return { ok: false, error: "program/resolve-rows: rows resolve for the requesting session's identity (EXEC-IDENTITY-1); a program names no viewer, so the viewer argument is null" };
|
|
340
|
+
if (host.resolveRows === void 0) return { ok: false, error: "program/resolve-rows: this host resolves no row sets" };
|
|
341
|
+
const page = await host.resolveRows(reference.data, clientId);
|
|
342
|
+
return { ok: true, result: toPayloadValue(page) };
|
|
343
|
+
}
|
|
171
344
|
if (op === "program/inputs") {
|
|
172
345
|
const steps = args.length === 1 && isJsonObject(first) ? first["trace"] : void 0;
|
|
173
346
|
if (typeof steps !== "number") return { ok: false, error: "program/inputs: the host is asked for { trace } only" };
|
|
@@ -189,74 +362,25 @@ async function deliverAgentAsk(op, args, host) {
|
|
|
189
362
|
if (host.dispatchRequest === void 0) return { ok: false, error: "program/dispatch: this host does not serve event requests" };
|
|
190
363
|
const stream = third["stream"] === true;
|
|
191
364
|
if (stream && host.supportsDispatchStream !== true) return { ok: false, error: "program/dispatch: this host does not answer stream items" };
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
const traits = third["traits"];
|
|
198
|
-
if (traits !== void 0 && traits !== null) fields["traits"] = traits;
|
|
199
|
-
if (isJsonObject(third["viewer"])) fields["user"] = third["viewer"];
|
|
200
|
-
const parsed = OrbitalEventRequestSchema.safeParse(fields);
|
|
201
|
-
if (!parsed.success) {
|
|
202
|
-
const message = `program/dispatch: not an event request: ${parsed.error.message}`;
|
|
203
|
-
return { ok: false, error: message, errors: [{ code: "INVALID_REQUEST", message, status: 400 }] };
|
|
204
|
-
}
|
|
365
|
+
const parse = ports.parseEventRequest;
|
|
366
|
+
if (parse === void 0) return { ok: false, error: "program/dispatch: this host has no kernel to parse an event request" };
|
|
367
|
+
const parsed = parse(payload, third);
|
|
368
|
+
if ("error" in parsed) return { ok: false, error: parsed.error, errors: [{ code: "INVALID_REQUEST", message: parsed.error, status: 400 }] };
|
|
205
369
|
const orbital = typeof third["orbital"] === "string" ? third["orbital"] : "";
|
|
206
|
-
const answered = await host.dispatchRequest(orbital, parsed.
|
|
370
|
+
const answered = await host.dispatchRequest(orbital, parsed.ok, third["input"] === true, stream);
|
|
207
371
|
if (!answered.ok) return { ok: false, error: answered.error, errors: [{ code: answered.code, message: answered.error, status: answered.status }] };
|
|
208
|
-
if (!stream) return { ok: true, result:
|
|
372
|
+
if (!stream) return { ok: true, result: answered.response };
|
|
209
373
|
if (answered.streamItems === void 0) return { ok: false, error: "program/dispatch: this host does not answer stream items" };
|
|
210
|
-
return { ok: true, result:
|
|
374
|
+
return { ok: true, result: { ...answered.response, items: answered.streamItems } };
|
|
211
375
|
}
|
|
212
376
|
const event = first.lastIndexOf(".");
|
|
213
377
|
const trait = first.lastIndexOf(".", event - 1);
|
|
214
378
|
if (event < 0 || trait < 0) return { ok: false, error: `program/dispatch: '${first}' is not a qualified address` };
|
|
215
379
|
const response = await host.dispatchInput(first.slice(0, trait), { targetTrait: first.slice(trait + 1, event), event: first.slice(event + 1), payload });
|
|
216
|
-
return { ok: true, result:
|
|
217
|
-
}
|
|
218
|
-
var HTTP_UNAVAILABLE = "HTTP_KERNEL_UNAVAILABLE: this host was given no http ports";
|
|
219
|
-
function httpAnswer(exchange) {
|
|
220
|
-
return { kind: "http", result: exchange.kind === "reply" ? { ok: exchange.response } : { error: exchange.failure } };
|
|
221
|
-
}
|
|
222
|
-
async function http(ports, request, scope) {
|
|
223
|
-
const exchangePorts = ports.http;
|
|
224
|
-
if (exchangePorts === void 0) return { kind: "http", result: { error: { kind: "failed", message: HTTP_UNAVAILABLE } } };
|
|
225
|
-
const { key, site } = request;
|
|
226
|
-
const orbital = site?.orbital ?? scope.orbital;
|
|
227
|
-
const onMessage = site?.emit.onMessage;
|
|
228
|
-
const onEvent = (event) => {
|
|
229
|
-
if (site === void 0 || onMessage === void 0) return;
|
|
230
|
-
ports.deliverLive({ orbital, trait: site.trait, event: onMessage }, { ...event }, scope.originClientId);
|
|
231
|
-
};
|
|
232
|
-
const send = (signal) => request.kind === "httpStream" ? streamHttp(exchangePorts, request.request, { framing: request.framing, key: key ?? null }, onEvent, signal) : sendHttp(exchangePorts, request.request, signal, request.delayMs);
|
|
233
|
-
const run = async () => {
|
|
234
|
-
if (key === void 0) return httpAnswer(await send());
|
|
235
|
-
const outcome = await ports.inFlightCalls.run(key, (signal) => untilAborted(signal, send(signal)));
|
|
236
|
-
return outcome.cancelled ? { kind: "serviceCancelled" } : httpAnswer(outcome.value);
|
|
237
|
-
};
|
|
238
|
-
try {
|
|
239
|
-
return await (scope.outsideEventQueue !== void 0 ? scope.outsideEventQueue(run) : run());
|
|
240
|
-
} catch (error) {
|
|
241
|
-
return { kind: "http", result: { error: { kind: "failed", message: failureMessage(error) } } };
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
async function httpBeacon(ports, request, scope) {
|
|
245
|
-
const exchangePorts = ports.http;
|
|
246
|
-
if (exchangePorts === void 0) return { kind: "httpBeacon", result: { ok: { via: "unavailable", message: HTTP_UNAVAILABLE } } };
|
|
247
|
-
const { key } = request;
|
|
248
|
-
const send = async (signal) => ({ kind: "httpBeacon", result: await sendBeaconRequest(exchangePorts, request.request, signal) });
|
|
249
|
-
const run = async () => {
|
|
250
|
-
if (key === void 0) return send();
|
|
251
|
-
const outcome = await ports.inFlightCalls.run(key, (signal) => untilAborted(signal, send(signal)));
|
|
252
|
-
return outcome.cancelled ? { kind: "serviceCancelled" } : outcome.value;
|
|
253
|
-
};
|
|
254
|
-
try {
|
|
255
|
-
return await (scope.outsideEventQueue !== void 0 ? scope.outsideEventQueue(run) : run());
|
|
256
|
-
} catch (error) {
|
|
257
|
-
return { kind: "httpBeacon", result: { error: { kind: "failed", message: failureMessage(error) } } };
|
|
258
|
-
}
|
|
380
|
+
return { ok: true, result: response };
|
|
259
381
|
}
|
|
382
|
+
var CREDENTIAL_CONNECT_UNAVAILABLE = "this host holds no credential store that can connect";
|
|
383
|
+
var SECRET_PORTS_UNAVAILABLE = "SECRET_PORTS_UNAVAILABLE: this host was given no secret ports";
|
|
260
384
|
async function onDeviceModel(ports, request, scope) {
|
|
261
385
|
const { key } = request;
|
|
262
386
|
const ask = async (signal) => request.kind === "prompt" ? { kind: "prompt", result: { ok: await promptOnDevice(ports.onDevice, request.text, request.schema, key ?? null, signal) } } : { kind: "embed", result: { ok: await embedOnDevice(ports.onDevice, request.texts, key ?? null, signal) } };
|
|
@@ -266,7 +390,7 @@ async function onDeviceModel(ports, request, scope) {
|
|
|
266
390
|
return outcome.cancelled ? { kind: "serviceCancelled" } : outcome.value;
|
|
267
391
|
};
|
|
268
392
|
try {
|
|
269
|
-
return await
|
|
393
|
+
return await run();
|
|
270
394
|
} catch (error) {
|
|
271
395
|
const failure = error instanceof OnDeviceModelFailure ? { kind: error.kind, message: error.message } : { kind: "failed", message: failureMessage(error) };
|
|
272
396
|
return request.kind === "prompt" ? { kind: "prompt", result: { error: failure } } : { kind: "embed", result: { error: failure } };
|
|
@@ -280,7 +404,7 @@ async function tab(ports, request, scope) {
|
|
|
280
404
|
const deliver = (payload) => {
|
|
281
405
|
if (ask.kind !== "watch") return;
|
|
282
406
|
const target = ask.deliver;
|
|
283
|
-
const lent = ports.servicePorts({ orbital: target.orbital, trait: target.trait },
|
|
407
|
+
const lent = ports.servicePorts({ orbital: target.orbital, trait: target.trait }, void 0, () => {
|
|
284
408
|
});
|
|
285
409
|
void lent.dispatchInput(target.orbital, { targetTrait: target.trait, event: target.event, payload });
|
|
286
410
|
};
|
|
@@ -297,22 +421,103 @@ async function tab(ports, request, scope) {
|
|
|
297
421
|
const outcome = await ports.inFlightCalls.run(key, (signal) => untilAborted(signal, perform(signal)));
|
|
298
422
|
return outcome.cancelled ? { kind: "serviceCancelled" } : outcome.value;
|
|
299
423
|
};
|
|
300
|
-
return
|
|
424
|
+
return run();
|
|
301
425
|
}
|
|
302
426
|
async function pageFacts(ports, request) {
|
|
303
|
-
return { kind: "pageFacts", result: await settle(async () => ports.pageFacts?.(request.fields) ?? { available: false, hostname: "", referrer: "", screenWidth: null }) };
|
|
427
|
+
return { kind: "pageFacts", result: await settle(async () => ports.pageFacts?.(request.fields) ?? { available: false, hostname: "", referrer: "", screenWidth: null, href: "" }) };
|
|
428
|
+
}
|
|
429
|
+
function finishTls(ports, os, key, raw) {
|
|
430
|
+
return new Promise((resolve) => completeTls(ports, os, key, raw, resolve));
|
|
431
|
+
}
|
|
432
|
+
function completeTls(ports, os, key, raw, finish) {
|
|
433
|
+
const tls = ports.osTls;
|
|
434
|
+
if (tls === void 0) {
|
|
435
|
+
finish(raw);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const step = (outcome, parked) => {
|
|
439
|
+
const next = tls.complete(key, outcome);
|
|
440
|
+
if (next === null) {
|
|
441
|
+
finish(parked ? { error: { kind: "tls", message: "the TLS operation vanished" } } : outcome);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if ("done" in next) {
|
|
445
|
+
finish(next.done);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
void os.answer(next.ask.op, next.ask.args).then((answered) => step(answered, true));
|
|
449
|
+
};
|
|
450
|
+
step(raw, false);
|
|
451
|
+
}
|
|
452
|
+
var CANONICAL_BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
453
|
+
var SEGMENT_ENCODERS = {
|
|
454
|
+
base64: (value) => base64Standard(new TextEncoder().encode(value)),
|
|
455
|
+
uri: (value) => encodeURIComponent(value),
|
|
456
|
+
form: (value) => encodeURIComponent(value).replace(/[!'()~]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`).replace(/%20/g, "+")
|
|
457
|
+
};
|
|
458
|
+
async function withCredentialSegments(ports, op, args, principal) {
|
|
459
|
+
const segments = args[1];
|
|
460
|
+
if (op !== "os/send" || !Array.isArray(segments)) return { args };
|
|
461
|
+
const parts = [];
|
|
462
|
+
for (const [at, segment] of segments.entries()) {
|
|
463
|
+
const invalid = (what) => ({ error: { kind: "invalid", message: `${op}: segment ${at + 1} ${what}` } });
|
|
464
|
+
if (!isJsonObject(segment)) return invalid("is not a { bytes, credential } object");
|
|
465
|
+
const text = segment["bytes"];
|
|
466
|
+
const name = segment["credential"];
|
|
467
|
+
if (typeof text !== "string" || typeof name !== "string") return invalid("needs a bytes text and a credential name");
|
|
468
|
+
if (!CANONICAL_BASE64.test(text)) return invalid("bytes are not standard base64");
|
|
469
|
+
parts.push(Uint8Array.from(atob(text), (c) => c.charCodeAt(0)));
|
|
470
|
+
const requested = segment["encode"];
|
|
471
|
+
const encoder = requested === void 0 ? void 0 : typeof requested === "string" && Object.hasOwn(SEGMENT_ENCODERS, requested) ? SEGMENT_ENCODERS[requested] : null;
|
|
472
|
+
if (encoder === null) return invalid(`encode must be one of ${Object.keys(SEGMENT_ENCODERS).join(", ")}`);
|
|
473
|
+
const requestedScope = segment["scope"];
|
|
474
|
+
if (requestedScope !== void 0 && requestedScope !== "app" && requestedScope !== "user") return invalid("scope must be one of app, user");
|
|
475
|
+
if (name === "") continue;
|
|
476
|
+
if (requestedScope === "user" && principal === void 0) return { error: { kind: "denied", message: `${op}: segment ${at + 1} needs a signed-in viewer` } };
|
|
477
|
+
const store = ports.secretPorts?.secrets;
|
|
478
|
+
if (store === void 0) return { error: { kind: "denied", message: `${op}: no credential source is configured` } };
|
|
479
|
+
let held;
|
|
480
|
+
try {
|
|
481
|
+
held = await store.get(name, requestedScope === "user" ? principal : void 0);
|
|
482
|
+
} catch (error) {
|
|
483
|
+
return { error: { kind: "io", message: `${op}: credential ${name}: ${error instanceof Error ? error.message : String(error)}` } };
|
|
484
|
+
}
|
|
485
|
+
if (held === void 0 || held === null) return { error: { kind: "denied", message: `${op}: credential ${name} is not held` } };
|
|
486
|
+
parts.push(new TextEncoder().encode(encoder === void 0 ? held : encoder(held)));
|
|
487
|
+
}
|
|
488
|
+
const bytes = new Uint8Array(parts.reduce((total, part) => total + part.length, 0));
|
|
489
|
+
let offset = 0;
|
|
490
|
+
for (const part of parts) {
|
|
491
|
+
bytes.set(part, offset);
|
|
492
|
+
offset += part.length;
|
|
493
|
+
}
|
|
494
|
+
return { args: [args[0] ?? null, base64Standard(bytes), ...args.slice(2)] };
|
|
304
495
|
}
|
|
305
496
|
async function osAnswer(ports, request, scope) {
|
|
306
497
|
const { os } = ports;
|
|
307
|
-
if (os === void 0) return { kind: "os", result: { error: { kind: "
|
|
308
|
-
const { op,
|
|
498
|
+
if (os === void 0) return { kind: "os", result: { error: { kind: "unsupported", message: `${request.op}: no os host configured` } } };
|
|
499
|
+
const { op, key, site, orbital: asking } = request;
|
|
309
500
|
const completions = ports.osCompletions;
|
|
310
501
|
const sink = ports.deferCallOutcome();
|
|
311
502
|
const message = ports.substrateMessage;
|
|
312
|
-
|
|
503
|
+
const streaming = key !== void 0 && site !== void 0 && completions !== void 0 && sink !== void 0 && message !== void 0 && os.stream !== void 0 && completions.streams(op);
|
|
504
|
+
const deferring = !streaming && key !== void 0 && site !== void 0 && completions !== void 0 && sink !== void 0 && os.defer !== void 0 && completions.defers(op);
|
|
505
|
+
const lease = (streaming || deferring) && key !== void 0 ? ports.inFlightCalls.reserve(key) : void 0;
|
|
506
|
+
if ((streaming || deferring) && lease === void 0) {
|
|
507
|
+
return { kind: "os", result: { error: { kind: "invalid", message: `${op}: key ${key} already has ${streaming ? "a stream open" : "a completion pending"}` } } };
|
|
508
|
+
}
|
|
509
|
+
const segmented = await withCredentialSegments(ports, op, request.args, request.principal);
|
|
510
|
+
if ("error" in segmented) {
|
|
511
|
+
lease?.release();
|
|
512
|
+
return { kind: "os", result: segmented };
|
|
513
|
+
}
|
|
514
|
+
if (lease?.signal.aborted === true) {
|
|
515
|
+
lease.release();
|
|
516
|
+
return { kind: "serviceCancelled" };
|
|
517
|
+
}
|
|
518
|
+
const { args } = segmented;
|
|
519
|
+
if (lease !== void 0 && streaming && key !== void 0 && site !== void 0 && completions !== void 0 && sink !== void 0 && message !== void 0 && os.stream !== void 0) {
|
|
313
520
|
const orbital = site.orbital ?? scope.orbital;
|
|
314
|
-
const lease = ports.inFlightCalls.reserve(key);
|
|
315
|
-
if (lease === void 0) return { kind: "os", result: { error: { kind: "invalid", message: `${op}: key ${key} already has a stream open` } } };
|
|
316
521
|
const stop = () => {
|
|
317
522
|
os.cancel?.(key);
|
|
318
523
|
lease.release();
|
|
@@ -320,30 +525,28 @@ async function osAnswer(ports, request, scope) {
|
|
|
320
525
|
lease.signal.addEventListener("abort", stop, { once: true });
|
|
321
526
|
const deliver = (fired) => {
|
|
322
527
|
if (fired === null || lease.signal.aborted) return false;
|
|
323
|
-
sink({ orbital, traitName: site.trait, event: fired.event, payload: isJsonObject(fired.payload) ? fired.payload : void 0, entityId: site.entityId, originClientId: scope.originClientId,
|
|
528
|
+
sink({ orbital, traitName: site.trait, event: fired.event, payload: isJsonObject(fired.payload) ? fired.payload : void 0, entityId: site.entityId, originClientId: scope.originClientId, locale: scope.locale });
|
|
324
529
|
return true;
|
|
325
530
|
};
|
|
326
531
|
const started = await os.stream(op, args, key, (value) => deliver(message(site, key, value)), (error) => {
|
|
327
532
|
lease.release();
|
|
328
533
|
lease.signal.removeEventListener("abort", stop);
|
|
329
534
|
deliver(completions.event(site, key, { error }));
|
|
330
|
-
});
|
|
535
|
+
}, asking);
|
|
331
536
|
if ("error" in started) {
|
|
332
537
|
lease.release();
|
|
333
538
|
lease.signal.removeEventListener("abort", stop);
|
|
334
539
|
}
|
|
335
540
|
return { kind: "os", result: started };
|
|
336
541
|
}
|
|
337
|
-
if (key !== void 0 && site !== void 0 && completions !== void 0 && sink !== void 0 && os.defer !== void 0
|
|
542
|
+
if (lease !== void 0 && deferring && key !== void 0 && site !== void 0 && completions !== void 0 && sink !== void 0 && os.defer !== void 0) {
|
|
338
543
|
const orbital = site.orbital ?? scope.orbital;
|
|
339
|
-
const lease = ports.inFlightCalls.reserve(key);
|
|
340
|
-
if (lease === void 0) return { kind: "os", result: { error: { kind: "invalid", message: `${op}: key ${key} already has a completion pending` } } };
|
|
341
544
|
const cancel = () => {
|
|
342
545
|
os.cancel?.(key);
|
|
343
546
|
lease.release();
|
|
344
547
|
};
|
|
345
548
|
lease.signal.addEventListener("abort", cancel, { once: true });
|
|
346
|
-
const
|
|
549
|
+
const finish = (outcome) => {
|
|
347
550
|
lease.release();
|
|
348
551
|
lease.signal.removeEventListener("abort", cancel);
|
|
349
552
|
if (lease.signal.aborted) return;
|
|
@@ -356,34 +559,67 @@ async function osAnswer(ports, request, scope) {
|
|
|
356
559
|
payload: isJsonObject(fired.payload) ? fired.payload : void 0,
|
|
357
560
|
entityId: site.entityId,
|
|
358
561
|
originClientId: scope.originClientId,
|
|
359
|
-
user: scope.user,
|
|
360
562
|
locale: scope.locale
|
|
361
563
|
});
|
|
362
|
-
}
|
|
564
|
+
};
|
|
565
|
+
const started = os.defer(op, args, key, (raw) => {
|
|
566
|
+
completeTls(ports, os, key, raw, finish);
|
|
567
|
+
}, asking);
|
|
363
568
|
if ("error" in started) {
|
|
364
569
|
lease.release();
|
|
365
570
|
lease.signal.removeEventListener("abort", cancel);
|
|
366
571
|
}
|
|
367
572
|
return "error" in started ? { kind: "os", result: started } : { kind: "serviceDeferred" };
|
|
368
573
|
}
|
|
369
|
-
return { kind: "os", result: await os.answer(op, args) };
|
|
574
|
+
return { kind: "os", result: await os.answer(op, args, asking) };
|
|
575
|
+
}
|
|
576
|
+
async function heldGpuAnswer(ports, request, scope) {
|
|
577
|
+
const held = ports.heldGpu;
|
|
578
|
+
if (held === void 0) return { kind: "os", result: { error: { kind: "unsupported", message: `${request.op}: this host installed no gpu answerer` } } };
|
|
579
|
+
const { key } = request;
|
|
580
|
+
const lease = key === void 0 ? void 0 : ports.inFlightCalls.reserve(key);
|
|
581
|
+
if (key !== void 0 && lease === void 0) {
|
|
582
|
+
return { kind: "os", result: { error: { kind: "invalid", message: `${request.op}: key ${key} already has a completion pending` } } };
|
|
583
|
+
}
|
|
584
|
+
const answer = await held.answer(request.ticket, (completion) => {
|
|
585
|
+
const aborted = lease?.signal.aborted === true;
|
|
586
|
+
lease?.release();
|
|
587
|
+
if (completion === null) return;
|
|
588
|
+
const sink = ports.deferCallOutcome();
|
|
589
|
+
if (aborted || sink === void 0) {
|
|
590
|
+
held.drop(completion.heldPayload);
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
sink({
|
|
594
|
+
orbital: completion.orbital ?? scope.orbital,
|
|
595
|
+
traitName: completion.traitName,
|
|
596
|
+
event: completion.event,
|
|
597
|
+
payload: void 0,
|
|
598
|
+
heldPayload: completion.heldPayload,
|
|
599
|
+
entityId: completion.entityId,
|
|
600
|
+
originClientId: scope.originClientId,
|
|
601
|
+
locale: scope.locale
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
if (answer.kind !== "serviceDeferred") lease?.release();
|
|
605
|
+
return answer;
|
|
370
606
|
}
|
|
371
607
|
async function secret(ports, request) {
|
|
372
|
-
const store = ports.
|
|
373
|
-
if (store === void 0) return { kind: "secret", result: { error:
|
|
608
|
+
const store = ports.secretPorts?.secrets;
|
|
609
|
+
if (store === void 0) return { kind: "secret", result: { error: SECRET_PORTS_UNAVAILABLE } };
|
|
374
610
|
return { kind: "secret", result: await settle(async () => await store.get(request.name, request.principal) ?? null) };
|
|
375
611
|
}
|
|
376
612
|
async function secretSet(ports, request) {
|
|
377
|
-
const store = ports.
|
|
378
|
-
if (store === void 0) return { kind: "secretSet", result: { error:
|
|
613
|
+
const store = ports.secretPorts?.secrets;
|
|
614
|
+
if (store === void 0) return { kind: "secretSet", result: { error: SECRET_PORTS_UNAVAILABLE } };
|
|
379
615
|
return { kind: "secretSet", result: await settle(async () => {
|
|
380
616
|
await store.set(request.name, request.value, request.ttlMs, request.principal);
|
|
381
617
|
return null;
|
|
382
618
|
}) };
|
|
383
619
|
}
|
|
384
620
|
async function secretRotate(ports, request) {
|
|
385
|
-
const store = ports.
|
|
386
|
-
if (store === void 0) return { kind: "secretRotate", result: { error:
|
|
621
|
+
const store = ports.secretPorts?.secrets;
|
|
622
|
+
if (store === void 0) return { kind: "secretRotate", result: { error: SECRET_PORTS_UNAVAILABLE } };
|
|
387
623
|
const rotate = store.rotate;
|
|
388
624
|
if (rotate === void 0) return { kind: "secretRotate", result: { error: "this host keeps no rotatable credentials" } };
|
|
389
625
|
return { kind: "secretRotate", result: await settle(async () => {
|
|
@@ -392,7 +628,7 @@ async function secretRotate(ports, request) {
|
|
|
392
628
|
}) };
|
|
393
629
|
}
|
|
394
630
|
function random(ports, request) {
|
|
395
|
-
const source = ports.
|
|
631
|
+
const source = ports.secretPorts?.random ?? systemRandom;
|
|
396
632
|
try {
|
|
397
633
|
return { kind: "random", result: { ok: base64Standard(source(request.bytes)) } };
|
|
398
634
|
} catch (error) {
|
|
@@ -400,8 +636,8 @@ function random(ports, request) {
|
|
|
400
636
|
}
|
|
401
637
|
}
|
|
402
638
|
async function credentialStatus(ports, request) {
|
|
403
|
-
const store = ports.
|
|
404
|
-
if (store === void 0) return { kind: "credentialStatus", result: { error:
|
|
639
|
+
const store = ports.secretPorts?.secrets;
|
|
640
|
+
if (store === void 0) return { kind: "credentialStatus", result: { error: SECRET_PORTS_UNAVAILABLE } };
|
|
405
641
|
return { kind: "credentialStatus", result: await settle(async () => {
|
|
406
642
|
const connected = credentialStatusConnected(await store.get(request.name, request.principal), request.principal);
|
|
407
643
|
const label = connected ? await store.label?.(request.name, request.principal) : void 0;
|
|
@@ -409,23 +645,32 @@ async function credentialStatus(ports, request) {
|
|
|
409
645
|
}) };
|
|
410
646
|
}
|
|
411
647
|
async function credentialForget(ports, request) {
|
|
412
|
-
const store = ports.
|
|
413
|
-
if (store === void 0) return { kind: "credentialForget", result: { error:
|
|
648
|
+
const store = ports.secretPorts?.secrets;
|
|
649
|
+
if (store === void 0) return { kind: "credentialForget", result: { error: SECRET_PORTS_UNAVAILABLE } };
|
|
414
650
|
return { kind: "credentialForget", result: await settle(async () => {
|
|
415
651
|
await store.forget(request.name, request.principal);
|
|
416
652
|
return null;
|
|
417
653
|
}) };
|
|
418
654
|
}
|
|
655
|
+
async function credentialConnect(ports, request) {
|
|
656
|
+
const connect = ports.secretPorts?.connect;
|
|
657
|
+
if (connect === void 0) return { kind: "credentialConnect", result: { error: { kind: "unavailable", message: CREDENTIAL_CONNECT_UNAVAILABLE } } };
|
|
658
|
+
try {
|
|
659
|
+
return { kind: "credentialConnect", result: await connect(request.grant) };
|
|
660
|
+
} catch (error) {
|
|
661
|
+
return { kind: "credentialConnect", result: { error: { kind: "unavailable", message: failureMessage(error) } } };
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
async function credentialEnvTails(ports, request) {
|
|
665
|
+
const env = ports.secretPorts?.env;
|
|
666
|
+
if (env === void 0) return { kind: "credentialEnvTails", result: { ok: { kind: "unsupported" } } };
|
|
667
|
+
return { kind: "credentialEnvTails", result: await settle(async () => ({ kind: "held", tails: env(request.names) })) };
|
|
668
|
+
}
|
|
419
669
|
async function credentialAdmin(ports, work) {
|
|
420
|
-
const port = ports.
|
|
421
|
-
if (port === void 0) return { error:
|
|
670
|
+
const port = ports.secretPorts?.credentials;
|
|
671
|
+
if (port === void 0) return { error: SECRET_PORTS_UNAVAILABLE };
|
|
422
672
|
return settle(() => work(port));
|
|
423
673
|
}
|
|
424
|
-
async function oauth(ports, request) {
|
|
425
|
-
const port = ports.http?.oauth;
|
|
426
|
-
if (port === void 0) return { kind: "oauth", result: { error: "this host keeps no OAuth state" } };
|
|
427
|
-
return { kind: "oauth", result: await settle(async () => checkOAuthReply(await port.answer(request.ask, request.principal))) };
|
|
428
|
-
}
|
|
429
674
|
function rowData(value) {
|
|
430
675
|
if (!isJsonObject(value)) throw new Error("persist: the row is not an object");
|
|
431
676
|
return value;
|
|
@@ -528,18 +773,13 @@ async function answerRequest(ports, request, scope) {
|
|
|
528
773
|
return { kind: "cancelled", result: { ok: null } };
|
|
529
774
|
case "substrate":
|
|
530
775
|
return substrate(ports, request, scope);
|
|
531
|
-
case "http":
|
|
532
|
-
case "httpStream":
|
|
533
|
-
return http(ports, request, scope);
|
|
534
|
-
case "httpBeacon":
|
|
535
|
-
return httpBeacon(ports, request, scope);
|
|
536
776
|
case "pageFacts":
|
|
537
777
|
return pageFacts(ports, request);
|
|
538
778
|
case "prompt":
|
|
539
779
|
case "embed":
|
|
540
|
-
return onDeviceModel(ports, request
|
|
780
|
+
return onDeviceModel(ports, request);
|
|
541
781
|
case "tab":
|
|
542
|
-
return tab(ports, request
|
|
782
|
+
return tab(ports, request);
|
|
543
783
|
case "secret":
|
|
544
784
|
return secret(ports, request);
|
|
545
785
|
case "secretSet":
|
|
@@ -550,16 +790,26 @@ async function answerRequest(ports, request, scope) {
|
|
|
550
790
|
return random(ports, request);
|
|
551
791
|
case "os":
|
|
552
792
|
return osAnswer(ports, request, scope);
|
|
793
|
+
case "gpu":
|
|
794
|
+
return heldGpuAnswer(ports, request, scope);
|
|
553
795
|
case "clock":
|
|
554
796
|
return { kind: "clock", result: { ok: (ports.now ?? Date.now)() } };
|
|
797
|
+
case "sealed":
|
|
798
|
+
return { kind: "sealed", result: { error: `${request.op}: a sealed operator is answered by the kernel, never by a host` } };
|
|
555
799
|
case "deliver":
|
|
556
800
|
return { kind: "deliver", result: { error: { kind: "down", id: request.envelope.id } } };
|
|
801
|
+
case "guest":
|
|
802
|
+
return { kind: "substrate", result: { error: { message: "program/dispatch: this host starts no guest kernels", errors: [{ kind: "unsupported", message: "program/dispatch: this host starts no guest kernels" }] } } };
|
|
557
803
|
case "credentialStatus":
|
|
558
804
|
return credentialStatus(ports, request);
|
|
559
805
|
case "credentialForget":
|
|
560
806
|
return credentialForget(ports, request);
|
|
807
|
+
case "credentialConnect":
|
|
808
|
+
return credentialConnect(ports, request);
|
|
561
809
|
case "credentialStoreList":
|
|
562
810
|
return { kind: request.kind, result: await credentialAdmin(ports, (port) => port.list()) };
|
|
811
|
+
case "credentialEnvTails":
|
|
812
|
+
return credentialEnvTails(ports, request);
|
|
563
813
|
case "credentialStoreRead":
|
|
564
814
|
return { kind: request.kind, result: await credentialAdmin(ports, (port) => port.read(request.name)) };
|
|
565
815
|
case "credentialStoreWrite":
|
|
@@ -575,9 +825,7 @@ async function answerRequest(ports, request, scope) {
|
|
|
575
825
|
return { kind: request.kind, result: await credentialAdmin(ports, (port) => port.mode()) };
|
|
576
826
|
case "credentialModeWrite":
|
|
577
827
|
return { kind: request.kind, result: await credentialAdmin(ports, (port) => port.setMode(request.mode)) };
|
|
578
|
-
case "oauth":
|
|
579
|
-
return oauth(ports, request);
|
|
580
828
|
}
|
|
581
829
|
}
|
|
582
830
|
|
|
583
|
-
export { answerRequest, declareCapabilities, writeRows };
|
|
831
|
+
export { HostAskError, answerRequest, completeTls, consentFromRedirect, declareCapabilities, finishTls, hostAskReturn, kernelEventRequestParser, parseConsentRequest, parseSignInRequest, performHostAnswer, performHostAsk, writeRows };
|