@almadar/shell-runtime 0.1.0 → 1.0.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-57SBCUSM.js +62 -0
- package/dist/chunk-625EP7W2.js +108 -0
- package/dist/{chunk-NATGCIVM.js → chunk-7WB6VWRK.js} +344 -129
- 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-I3TKINSO.js +398 -0
- package/dist/{chunk-DYOJGLIX.js → chunk-LKWYCYX5.js} +2 -5
- package/dist/chunk-LYFIQPXX.js +1 -0
- package/dist/{chunk-VNMG7NKY.js → chunk-MU335ZA4.js} +20 -26
- 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-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-VNGXRYFF.js +9 -0
- package/dist/{chunk-HNOAZWFH.js → chunk-WGCWJOT2.js} +2 -0
- package/dist/chunk-X6W44WHO.js +26 -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 +8 -10
- package/dist/kernel/decode.js +1 -2
- package/dist/kernel/engine.d.ts +132 -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 +127 -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 +50 -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 +14 -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
498
|
if (os === void 0) return { kind: "os", result: { error: { kind: "denied", message: `${request.op}: no os host configured` } } };
|
|
308
|
-
const { op,
|
|
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,36 @@ 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) };
|
|
370
575
|
}
|
|
371
576
|
async function secret(ports, request) {
|
|
372
|
-
const store = ports.
|
|
373
|
-
if (store === void 0) return { kind: "secret", result: { error:
|
|
577
|
+
const store = ports.secretPorts?.secrets;
|
|
578
|
+
if (store === void 0) return { kind: "secret", result: { error: SECRET_PORTS_UNAVAILABLE } };
|
|
374
579
|
return { kind: "secret", result: await settle(async () => await store.get(request.name, request.principal) ?? null) };
|
|
375
580
|
}
|
|
376
581
|
async function secretSet(ports, request) {
|
|
377
|
-
const store = ports.
|
|
378
|
-
if (store === void 0) return { kind: "secretSet", result: { error:
|
|
582
|
+
const store = ports.secretPorts?.secrets;
|
|
583
|
+
if (store === void 0) return { kind: "secretSet", result: { error: SECRET_PORTS_UNAVAILABLE } };
|
|
379
584
|
return { kind: "secretSet", result: await settle(async () => {
|
|
380
585
|
await store.set(request.name, request.value, request.ttlMs, request.principal);
|
|
381
586
|
return null;
|
|
382
587
|
}) };
|
|
383
588
|
}
|
|
384
589
|
async function secretRotate(ports, request) {
|
|
385
|
-
const store = ports.
|
|
386
|
-
if (store === void 0) return { kind: "secretRotate", result: { error:
|
|
590
|
+
const store = ports.secretPorts?.secrets;
|
|
591
|
+
if (store === void 0) return { kind: "secretRotate", result: { error: SECRET_PORTS_UNAVAILABLE } };
|
|
387
592
|
const rotate = store.rotate;
|
|
388
593
|
if (rotate === void 0) return { kind: "secretRotate", result: { error: "this host keeps no rotatable credentials" } };
|
|
389
594
|
return { kind: "secretRotate", result: await settle(async () => {
|
|
@@ -392,7 +597,7 @@ async function secretRotate(ports, request) {
|
|
|
392
597
|
}) };
|
|
393
598
|
}
|
|
394
599
|
function random(ports, request) {
|
|
395
|
-
const source = ports.
|
|
600
|
+
const source = ports.secretPorts?.random ?? systemRandom;
|
|
396
601
|
try {
|
|
397
602
|
return { kind: "random", result: { ok: base64Standard(source(request.bytes)) } };
|
|
398
603
|
} catch (error) {
|
|
@@ -400,8 +605,8 @@ function random(ports, request) {
|
|
|
400
605
|
}
|
|
401
606
|
}
|
|
402
607
|
async function credentialStatus(ports, request) {
|
|
403
|
-
const store = ports.
|
|
404
|
-
if (store === void 0) return { kind: "credentialStatus", result: { error:
|
|
608
|
+
const store = ports.secretPorts?.secrets;
|
|
609
|
+
if (store === void 0) return { kind: "credentialStatus", result: { error: SECRET_PORTS_UNAVAILABLE } };
|
|
405
610
|
return { kind: "credentialStatus", result: await settle(async () => {
|
|
406
611
|
const connected = credentialStatusConnected(await store.get(request.name, request.principal), request.principal);
|
|
407
612
|
const label = connected ? await store.label?.(request.name, request.principal) : void 0;
|
|
@@ -409,23 +614,32 @@ async function credentialStatus(ports, request) {
|
|
|
409
614
|
}) };
|
|
410
615
|
}
|
|
411
616
|
async function credentialForget(ports, request) {
|
|
412
|
-
const store = ports.
|
|
413
|
-
if (store === void 0) return { kind: "credentialForget", result: { error:
|
|
617
|
+
const store = ports.secretPorts?.secrets;
|
|
618
|
+
if (store === void 0) return { kind: "credentialForget", result: { error: SECRET_PORTS_UNAVAILABLE } };
|
|
414
619
|
return { kind: "credentialForget", result: await settle(async () => {
|
|
415
620
|
await store.forget(request.name, request.principal);
|
|
416
621
|
return null;
|
|
417
622
|
}) };
|
|
418
623
|
}
|
|
624
|
+
async function credentialConnect(ports, request) {
|
|
625
|
+
const connect = ports.secretPorts?.connect;
|
|
626
|
+
if (connect === void 0) return { kind: "credentialConnect", result: { error: { kind: "unavailable", message: CREDENTIAL_CONNECT_UNAVAILABLE } } };
|
|
627
|
+
try {
|
|
628
|
+
return { kind: "credentialConnect", result: await connect(request.grant) };
|
|
629
|
+
} catch (error) {
|
|
630
|
+
return { kind: "credentialConnect", result: { error: { kind: "unavailable", message: failureMessage(error) } } };
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
async function credentialEnvTails(ports, request) {
|
|
634
|
+
const env = ports.secretPorts?.env;
|
|
635
|
+
if (env === void 0) return { kind: "credentialEnvTails", result: { ok: { kind: "unsupported" } } };
|
|
636
|
+
return { kind: "credentialEnvTails", result: await settle(async () => ({ kind: "held", tails: env(request.names) })) };
|
|
637
|
+
}
|
|
419
638
|
async function credentialAdmin(ports, work) {
|
|
420
|
-
const port = ports.
|
|
421
|
-
if (port === void 0) return { error:
|
|
639
|
+
const port = ports.secretPorts?.credentials;
|
|
640
|
+
if (port === void 0) return { error: SECRET_PORTS_UNAVAILABLE };
|
|
422
641
|
return settle(() => work(port));
|
|
423
642
|
}
|
|
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
643
|
function rowData(value) {
|
|
430
644
|
if (!isJsonObject(value)) throw new Error("persist: the row is not an object");
|
|
431
645
|
return value;
|
|
@@ -528,18 +742,13 @@ async function answerRequest(ports, request, scope) {
|
|
|
528
742
|
return { kind: "cancelled", result: { ok: null } };
|
|
529
743
|
case "substrate":
|
|
530
744
|
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
745
|
case "pageFacts":
|
|
537
746
|
return pageFacts(ports, request);
|
|
538
747
|
case "prompt":
|
|
539
748
|
case "embed":
|
|
540
|
-
return onDeviceModel(ports, request
|
|
749
|
+
return onDeviceModel(ports, request);
|
|
541
750
|
case "tab":
|
|
542
|
-
return tab(ports, request
|
|
751
|
+
return tab(ports, request);
|
|
543
752
|
case "secret":
|
|
544
753
|
return secret(ports, request);
|
|
545
754
|
case "secretSet":
|
|
@@ -552,14 +761,22 @@ async function answerRequest(ports, request, scope) {
|
|
|
552
761
|
return osAnswer(ports, request, scope);
|
|
553
762
|
case "clock":
|
|
554
763
|
return { kind: "clock", result: { ok: (ports.now ?? Date.now)() } };
|
|
764
|
+
case "sealed":
|
|
765
|
+
return { kind: "sealed", result: { error: `${request.op}: a sealed operator is answered by the kernel, never by a host` } };
|
|
555
766
|
case "deliver":
|
|
556
767
|
return { kind: "deliver", result: { error: { kind: "down", id: request.envelope.id } } };
|
|
768
|
+
case "guest":
|
|
769
|
+
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
770
|
case "credentialStatus":
|
|
558
771
|
return credentialStatus(ports, request);
|
|
559
772
|
case "credentialForget":
|
|
560
773
|
return credentialForget(ports, request);
|
|
774
|
+
case "credentialConnect":
|
|
775
|
+
return credentialConnect(ports, request);
|
|
561
776
|
case "credentialStoreList":
|
|
562
777
|
return { kind: request.kind, result: await credentialAdmin(ports, (port) => port.list()) };
|
|
778
|
+
case "credentialEnvTails":
|
|
779
|
+
return credentialEnvTails(ports, request);
|
|
563
780
|
case "credentialStoreRead":
|
|
564
781
|
return { kind: request.kind, result: await credentialAdmin(ports, (port) => port.read(request.name)) };
|
|
565
782
|
case "credentialStoreWrite":
|
|
@@ -575,9 +792,7 @@ async function answerRequest(ports, request, scope) {
|
|
|
575
792
|
return { kind: request.kind, result: await credentialAdmin(ports, (port) => port.mode()) };
|
|
576
793
|
case "credentialModeWrite":
|
|
577
794
|
return { kind: request.kind, result: await credentialAdmin(ports, (port) => port.setMode(request.mode)) };
|
|
578
|
-
case "oauth":
|
|
579
|
-
return oauth(ports, request);
|
|
580
795
|
}
|
|
581
796
|
}
|
|
582
797
|
|
|
583
|
-
export { answerRequest, declareCapabilities, writeRows };
|
|
798
|
+
export { HostAskError, answerRequest, completeTls, consentFromRedirect, declareCapabilities, finishTls, hostAskReturn, kernelEventRequestParser, parseConsentRequest, parseSignInRequest, performHostAnswer, performHostAsk, writeRows };
|