@lunora/runtime 1.0.0-alpha.26 → 1.0.0-alpha.27
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/index.d.mts
CHANGED
|
@@ -2348,6 +2348,17 @@ interface WorkerOptions {
|
|
|
2348
2348
|
*/
|
|
2349
2349
|
vectorIntrospector?: VectorIntrospector;
|
|
2350
2350
|
/**
|
|
2351
|
+
* Voice-session Durable Object namespaces, keyed by the agent's
|
|
2352
|
+
* `lunora/agents.ts` export name (e.g. `{ support: env.VOICE_SUPPORT }`).
|
|
2353
|
+
* Codegen wires this for every voice-enabled agent. When set, the worker
|
|
2354
|
+
* exposes `/_lunora/voice/<agentExportName>` — a WebSocket upgrade that
|
|
2355
|
+
* resolves the caller's identity, forwards it on the server-minted
|
|
2356
|
+
* `x-lunora-userid` / `x-lunora-identity` headers, and hands the socket to
|
|
2357
|
+
* the agent's `VoiceSessionDO`. Omit it (voice-free apps) and the route does
|
|
2358
|
+
* not exist.
|
|
2359
|
+
*/
|
|
2360
|
+
voiceAgents?: Record<string, ShardNamespaceLike>;
|
|
2361
|
+
/**
|
|
2351
2362
|
* Resolver for the Cloudflare Workflows REST client, built from the
|
|
2352
2363
|
* deployment `env` (its `CLOUDFLARE_ACCOUNT_ID` / `CLOUDFLARE_API_TOKEN`).
|
|
2353
2364
|
* Set by the codegen-emitted worker entry (which depends on
|
package/dist/index.d.ts
CHANGED
|
@@ -2348,6 +2348,17 @@ interface WorkerOptions {
|
|
|
2348
2348
|
*/
|
|
2349
2349
|
vectorIntrospector?: VectorIntrospector;
|
|
2350
2350
|
/**
|
|
2351
|
+
* Voice-session Durable Object namespaces, keyed by the agent's
|
|
2352
|
+
* `lunora/agents.ts` export name (e.g. `{ support: env.VOICE_SUPPORT }`).
|
|
2353
|
+
* Codegen wires this for every voice-enabled agent. When set, the worker
|
|
2354
|
+
* exposes `/_lunora/voice/<agentExportName>` — a WebSocket upgrade that
|
|
2355
|
+
* resolves the caller's identity, forwards it on the server-minted
|
|
2356
|
+
* `x-lunora-userid` / `x-lunora-identity` headers, and hands the socket to
|
|
2357
|
+
* the agent's `VoiceSessionDO`. Omit it (voice-free apps) and the route does
|
|
2358
|
+
* not exist.
|
|
2359
|
+
*/
|
|
2360
|
+
voiceAgents?: Record<string, ShardNamespaceLike>;
|
|
2361
|
+
/**
|
|
2351
2362
|
* Resolver for the Cloudflare Workflows REST client, built from the
|
|
2352
2363
|
* deployment `env` (its `CLOUDFLARE_ACCOUNT_ID` / `CLOUDFLARE_API_TOKEN`).
|
|
2353
2364
|
* Set by the codegen-emitted worker entry (which depends on
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { toAirbyteMessages, toFivetranResponse } from './packem_shared/toAirbyteMessages-DrHdplb4.mjs';
|
|
2
|
-
export { composeWorker, createLunoraHandler, createWorker, defineRpcEnvelope, resolveLunoraOptions, withFrameworkWorker } from './packem_shared/composeWorker-
|
|
2
|
+
export { composeWorker, createLunoraHandler, createWorker, defineRpcEnvelope, resolveLunoraOptions, withFrameworkWorker } from './packem_shared/composeWorker-UZKPkF3Q.mjs';
|
|
3
3
|
export { createCrossShardRelationCapabilities } from './packem_shared/createCrossShardRelationCapabilities-CbcWjkAn.mjs';
|
|
4
4
|
export { DEFAULT_REGISTRY_CACHE_TTL_MS, SHARD_REGISTRY_DO_NAME, createDynamicShardRegistry } from './packem_shared/DEFAULT_REGISTRY_CACHE_TTL_MS-B3pA7aXp.mjs';
|
|
5
5
|
export { LunoraError, toErrorResponse } from './packem_shared/LunoraError-Bpb9EFJ3.mjs';
|
|
@@ -1917,12 +1917,24 @@ const NDJSON_ENCODER = new TextEncoder();
|
|
|
1917
1917
|
const RPC_PATH = "/_lunora/rpc";
|
|
1918
1918
|
const RPC_BATCH_PATH = "/_lunora/rpc-batch";
|
|
1919
1919
|
const WS_PATH = "/_lunora/ws";
|
|
1920
|
+
const VOICE_PATH_PREFIX = "/_lunora/voice/";
|
|
1920
1921
|
const SCHEDULER_DISPATCH_PATH = "/_lunora/scheduler/dispatch";
|
|
1921
1922
|
const CRON_JOBS_RUN_PATH = "/_lunora/admin/cron-jobs/run";
|
|
1922
1923
|
const ADMIN_PATH_PREFIX = "/_lunora/admin/";
|
|
1923
1924
|
const MIGRATE_PATH = "/_lunora/migrate";
|
|
1924
1925
|
const STATUS_PATH = "/_lunora/status";
|
|
1925
1926
|
const isAdminPath = (pathname) => pathname.startsWith(ADMIN_PATH_PREFIX) || pathname === MIGRATE_PATH;
|
|
1927
|
+
const readForwardedIdentity = (request) => {
|
|
1928
|
+
const forwardedUserId = request.headers.get("x-lunora-userid");
|
|
1929
|
+
const forwardedIdentity = request.headers.get("x-lunora-identity");
|
|
1930
|
+
if (forwardedUserId === null && forwardedIdentity === null) {
|
|
1931
|
+
return void 0;
|
|
1932
|
+
}
|
|
1933
|
+
return {
|
|
1934
|
+
...forwardedIdentity === null ? {} : { identity: forwardedIdentity },
|
|
1935
|
+
...forwardedUserId === null ? {} : { userId: forwardedUserId }
|
|
1936
|
+
};
|
|
1937
|
+
};
|
|
1926
1938
|
const DEFAULT_AUTH_BASE_PATH = "/api/auth";
|
|
1927
1939
|
const RECORD_AUTH_EVENT_OP = "__lunora_admin__:recordAuthEvent";
|
|
1928
1940
|
const AUTH_ATTEMPT_SEGMENTS = ["/sign-in", "/sign-up", "/callback"];
|
|
@@ -2240,7 +2252,7 @@ const createWorker = (options) => {
|
|
|
2240
2252
|
resolveForwardContext: resolveAdminForwardContext,
|
|
2241
2253
|
shardDO
|
|
2242
2254
|
});
|
|
2243
|
-
const dispatchToShard = async (functionPath, args, shardKey, mutationId) => {
|
|
2255
|
+
const dispatchToShard = async (functionPath, args, shardKey, mutationId, forwardedIdentity) => {
|
|
2244
2256
|
if (options.authorizeShard) {
|
|
2245
2257
|
const allowed = await options.authorizeShard(null, shardKey);
|
|
2246
2258
|
if (!allowed) {
|
|
@@ -2248,6 +2260,12 @@ const createWorker = (options) => {
|
|
|
2248
2260
|
}
|
|
2249
2261
|
}
|
|
2250
2262
|
const headers = { "content-type": "application/json", "x-lunora-system": "1" };
|
|
2263
|
+
if (forwardedIdentity?.userId !== void 0 && forwardedIdentity.userId.length > 0) {
|
|
2264
|
+
headers["x-lunora-userid"] = forwardedIdentity.userId;
|
|
2265
|
+
}
|
|
2266
|
+
if (forwardedIdentity?.identity !== void 0 && forwardedIdentity.identity.length > 0) {
|
|
2267
|
+
headers["x-lunora-identity"] = forwardedIdentity.identity;
|
|
2268
|
+
}
|
|
2251
2269
|
if (mutationId !== void 0 && mutationId.length > 0) {
|
|
2252
2270
|
headers["x-lunora-mutation-id"] = mutationId;
|
|
2253
2271
|
}
|
|
@@ -2258,16 +2276,17 @@ const createWorker = (options) => {
|
|
|
2258
2276
|
});
|
|
2259
2277
|
return forwardToShard(shardDO, shardKey, forwarded);
|
|
2260
2278
|
};
|
|
2261
|
-
const
|
|
2279
|
+
const startWorkflowInstance = async (binding, args, env, label) => {
|
|
2262
2280
|
const candidate = env?.[binding];
|
|
2263
2281
|
if (!candidate || typeof candidate.create !== "function") {
|
|
2264
|
-
throw new LunoraError(
|
|
2282
|
+
throw new LunoraError(`${label} targets workflow binding "${binding}", which is not bound on env`, {
|
|
2265
2283
|
code: "CRON_JOB_FAILED",
|
|
2266
2284
|
status: 500
|
|
2267
2285
|
});
|
|
2268
2286
|
}
|
|
2269
|
-
await candidate.create({ params:
|
|
2287
|
+
await candidate.create({ params: args });
|
|
2270
2288
|
};
|
|
2289
|
+
const startCronWorkflow = async (binding, job, env) => startWorkflowInstance(binding, job.args ?? {}, env, `cron job "${job.name}"`);
|
|
2271
2290
|
const runOneCronJob = async (job, env) => {
|
|
2272
2291
|
if (job.workflow) {
|
|
2273
2292
|
await startCronWorkflow(job.workflow, job, env);
|
|
@@ -2364,13 +2383,18 @@ const createWorker = (options) => {
|
|
|
2364
2383
|
throw new LunoraError("Scheduler dispatch body must be valid JSON", { code: "BAD_REQUEST", status: 400 });
|
|
2365
2384
|
}
|
|
2366
2385
|
const candidate = body ?? {};
|
|
2386
|
+
const args = candidate.args ?? {};
|
|
2387
|
+
if (typeof candidate.workflow === "string" && candidate.workflow.length > 0) {
|
|
2388
|
+
await startWorkflowInstance(candidate.workflow, args, env, "scheduled workflow");
|
|
2389
|
+
return Response.json({ ok: true }, { status: 200 });
|
|
2390
|
+
}
|
|
2367
2391
|
if (typeof candidate.functionPath !== "string" || candidate.functionPath.length === 0) {
|
|
2368
2392
|
throw new LunoraError("Scheduler dispatch is missing `functionPath`", { code: "BAD_REQUEST", status: 400 });
|
|
2369
2393
|
}
|
|
2370
|
-
const args = candidate.args ?? {};
|
|
2371
2394
|
const shardKey = typeof candidate.shardKey === "string" && candidate.shardKey.length > 0 ? candidate.shardKey : defaultShard;
|
|
2372
2395
|
const mutationId = typeof candidate.id === "string" && candidate.id.length > 0 ? candidate.id : void 0;
|
|
2373
|
-
const
|
|
2396
|
+
const identity = readForwardedIdentity(request);
|
|
2397
|
+
const response = await dispatchToShard(candidate.functionPath, args, shardKey, mutationId, identity);
|
|
2374
2398
|
await releasePoolSlot(candidate);
|
|
2375
2399
|
return response;
|
|
2376
2400
|
};
|
|
@@ -2563,6 +2587,59 @@ const createWorker = (options) => {
|
|
|
2563
2587
|
}
|
|
2564
2588
|
return forwardToShard(shardDO, shardKey, new Request(request, { headers: upgradeHeaders }));
|
|
2565
2589
|
};
|
|
2590
|
+
const handleVoiceUpgrade = async (request, env, url) => {
|
|
2591
|
+
const { voiceAgents } = options;
|
|
2592
|
+
if (voiceAgents === void 0) {
|
|
2593
|
+
return new Response("Not found", { status: 404 });
|
|
2594
|
+
}
|
|
2595
|
+
if (request.headers.get("Upgrade") !== "websocket") {
|
|
2596
|
+
return new Response("Expected a WebSocket upgrade", { headers: { allow: "GET" }, status: 426 });
|
|
2597
|
+
}
|
|
2598
|
+
const blockedUpgrade = enforceWebSocketOrigin(request, resolvedSecurity);
|
|
2599
|
+
if (blockedUpgrade) {
|
|
2600
|
+
return blockedUpgrade;
|
|
2601
|
+
}
|
|
2602
|
+
let agentName;
|
|
2603
|
+
try {
|
|
2604
|
+
agentName = decodeURIComponent(url.pathname.slice(VOICE_PATH_PREFIX.length));
|
|
2605
|
+
} catch {
|
|
2606
|
+
return new Response("Unknown voice agent", { status: 404 });
|
|
2607
|
+
}
|
|
2608
|
+
const namespace = Object.hasOwn(voiceAgents, agentName) ? voiceAgents[agentName] : void 0;
|
|
2609
|
+
if (namespace === void 0) {
|
|
2610
|
+
return new Response("Unknown voice agent", { status: 404 });
|
|
2611
|
+
}
|
|
2612
|
+
const threadKey = url.searchParams.get("threadKey");
|
|
2613
|
+
if (threadKey === null || threadKey.length === 0) {
|
|
2614
|
+
return new Response("Missing threadKey", { status: 400 });
|
|
2615
|
+
}
|
|
2616
|
+
const { headers: forwardedHeaders, identity } = await resolveForwardContext(request, env, publicResolveIdentity);
|
|
2617
|
+
if (options.authorizeShard) {
|
|
2618
|
+
const allowed = await options.authorizeShard(identity, threadKey);
|
|
2619
|
+
if (!allowed) {
|
|
2620
|
+
return new Response("Forbidden", { status: 403 });
|
|
2621
|
+
}
|
|
2622
|
+
} else {
|
|
2623
|
+
guardUnauthenticatedShardAccess("shard");
|
|
2624
|
+
}
|
|
2625
|
+
const upgradeHeaders = new Headers(request.headers);
|
|
2626
|
+
upgradeHeaders.delete("x-lunora-userid");
|
|
2627
|
+
upgradeHeaders.delete("x-lunora-identity");
|
|
2628
|
+
upgradeHeaders.delete("x-lunora-identity-exp");
|
|
2629
|
+
const forwardedUserId = forwardedHeaders["x-lunora-userid"];
|
|
2630
|
+
const forwardedIdentity = forwardedHeaders["x-lunora-identity"];
|
|
2631
|
+
const forwardedExp = forwardedHeaders["x-lunora-identity-exp"];
|
|
2632
|
+
if (forwardedUserId !== void 0) {
|
|
2633
|
+
upgradeHeaders.set("x-lunora-userid", forwardedUserId);
|
|
2634
|
+
}
|
|
2635
|
+
if (forwardedIdentity !== void 0) {
|
|
2636
|
+
upgradeHeaders.set("x-lunora-identity", forwardedIdentity);
|
|
2637
|
+
}
|
|
2638
|
+
if (forwardedExp !== void 0) {
|
|
2639
|
+
upgradeHeaders.set("x-lunora-identity-exp", forwardedExp);
|
|
2640
|
+
}
|
|
2641
|
+
return forwardToShard(namespace, threadKey, new Request(request, { headers: upgradeHeaders }));
|
|
2642
|
+
};
|
|
2566
2643
|
const authorizeFanOutEnvelope = async (fanOut, functionPath, identity) => {
|
|
2567
2644
|
if (options.authorizeFanOut) {
|
|
2568
2645
|
const allowed = await options.authorizeFanOut(identity, fanOut.table, functionPath);
|
|
@@ -3091,6 +3168,9 @@ const createWorker = (options) => {
|
|
|
3091
3168
|
await applyAdminGate(request, url.pathname);
|
|
3092
3169
|
return internalRoute(request, env, url, context);
|
|
3093
3170
|
}
|
|
3171
|
+
if (options.voiceAgents !== void 0 && url.pathname.startsWith(VOICE_PATH_PREFIX)) {
|
|
3172
|
+
return handleVoiceUpgrade(request, env, url);
|
|
3173
|
+
}
|
|
3094
3174
|
const httpRouteResponse = await dispatchHttpRoute(request, env, context);
|
|
3095
3175
|
if (httpRouteResponse) {
|
|
3096
3176
|
return httpRouteResponse;
|