@nylorun/runtime 0.12.0-beta → 0.14.0-beta
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/CHANGELOG.md +361 -0
- package/README.md +69 -15
- package/dist/adapters/execution/restate.d.ts +7 -2
- package/dist/adapters/execution/restate.js +38 -8
- package/dist/adapters/replication/pgoutput.d.ts +16 -0
- package/dist/adapters/replication/pgoutput.js +169 -0
- package/dist/adapters/streams/s2.js +37 -20
- package/dist/admin-openapi.json +1071 -0
- package/dist/api/a2a/card.d.ts +15 -0
- package/dist/api/a2a/card.js +24 -0
- package/dist/api/a2a/endpoint.d.ts +8 -0
- package/dist/api/a2a/endpoint.js +81 -0
- package/dist/api/a2a/protocol.d.ts +144 -0
- package/dist/api/a2a/protocol.js +188 -0
- package/dist/api/a2a/routes.d.ts +19 -0
- package/dist/api/a2a/routes.js +289 -0
- package/dist/api/a2a/tasks.d.ts +50 -0
- package/dist/api/a2a/tasks.js +186 -0
- package/dist/api/ag-ui/endpoint.d.ts +8 -0
- package/dist/api/ag-ui/endpoint.js +126 -0
- package/dist/api/ag-ui/history.d.ts +7 -0
- package/dist/api/ag-ui/history.js +60 -0
- package/dist/api/ag-ui/routes.d.ts +32 -0
- package/dist/api/ag-ui/routes.js +305 -0
- package/dist/api/ag-ui/session-id.d.ts +1 -0
- package/dist/api/ag-ui/session-id.js +12 -0
- package/dist/api/ag-ui/sse.d.ts +9 -0
- package/dist/api/ag-ui/sse.js +7 -0
- package/dist/api/ag-ui/translate.d.ts +38 -0
- package/dist/api/ag-ui/translate.js +222 -0
- package/dist/api/components.d.ts +7825 -0
- package/dist/api/components.js +83 -0
- package/dist/api/http/app.d.ts +18 -0
- package/dist/api/http/app.js +58 -0
- package/dist/api/http/body.d.ts +4 -0
- package/dist/api/http/body.js +37 -0
- package/dist/api/http/define.d.ts +39 -0
- package/dist/api/http/define.js +130 -0
- package/dist/api/http/respond.d.ts +9 -0
- package/dist/api/http/respond.js +44 -0
- package/dist/api/http/routes/access.d.ts +24 -0
- package/dist/api/http/routes/access.js +259 -0
- package/dist/api/http/routes/actions.d.ts +8 -0
- package/dist/api/http/routes/actions.js +76 -0
- package/dist/api/http/routes/endpoints.d.ts +7 -0
- package/dist/api/http/routes/endpoints.js +100 -0
- package/dist/api/http/routes/sessions.d.ts +3 -0
- package/dist/api/http/routes/sessions.js +205 -0
- package/dist/api/http/routes/tenant.d.ts +10 -0
- package/dist/api/http/routes/tenant.js +172 -0
- package/dist/api/http/routes/vaults.d.ts +8 -0
- package/dist/api/http/routes/vaults.js +167 -0
- package/dist/api/openapi.d.ts +19 -0
- package/dist/api/openapi.js +138 -0
- package/dist/api/route.d.ts +12 -0
- package/dist/api/route.js +4 -0
- package/dist/core/bearer.d.ts +8 -0
- package/dist/core/bearer.js +11 -0
- package/dist/core/flow-host.d.ts +7 -15
- package/dist/core/flow-host.js +19 -41
- package/dist/core/sandbox-routes.d.ts +12 -7
- package/dist/core/sandbox-routes.js +24 -25
- package/dist/execution/memory.d.ts +8 -5
- package/dist/execution/memory.js +33 -13
- package/dist/execution/types.d.ts +38 -6
- package/dist/execution/types.js +12 -3
- package/dist/host/admin-api.d.ts +22 -0
- package/dist/host/admin-api.js +178 -0
- package/dist/host/app.d.ts +34 -0
- package/dist/host/app.js +185 -0
- package/dist/host/config-for.d.ts +2 -0
- package/dist/host/config-for.js +1 -0
- package/dist/host/config.d.ts +11 -0
- package/dist/host/cors.d.ts +27 -0
- package/dist/host/cors.js +64 -0
- package/dist/host/create-host.d.ts +31 -3
- package/dist/host/create-host.js +168 -327
- package/dist/host/http.d.ts +14 -3
- package/dist/host/http.js +64 -23
- package/dist/host/main.js +55 -4
- package/dist/host/stack-config.d.ts +20 -0
- package/dist/host/stack-config.js +55 -0
- package/dist/mcp/pool.d.ts +19 -1
- package/dist/mcp/pool.js +60 -10
- package/dist/model/catalog.d.ts +2 -10
- package/dist/model/classify.d.ts +13 -0
- package/dist/model/classify.js +61 -0
- package/dist/model/models.d.ts +10 -1
- package/dist/model/models.js +14 -6
- package/dist/model/pi-model.d.ts +17 -2
- package/dist/model/pi-model.js +366 -222
- package/dist/openapi.json +25696 -0
- package/dist/sandbox/manager.d.ts +13 -1
- package/dist/sandbox/manager.js +8 -11
- package/dist/sandbox/resolve.d.ts +33 -0
- package/dist/sandbox/resolve.js +81 -0
- package/dist/sandbox/session-sandbox.d.ts +11 -0
- package/dist/sandbox/session-sandbox.js +51 -0
- package/dist/sandbox/share.d.ts +14 -2
- package/dist/sandbox/share.js +33 -3
- package/dist/sandbox/tenant-config.d.ts +37 -0
- package/dist/sandbox/tenant-config.js +51 -0
- package/dist/sandbox/tools.d.ts +7 -9
- package/dist/sandbox/tools.js +11 -3
- package/dist/sandbox/types.d.ts +8 -2
- package/dist/store/event.d.ts +19 -0
- package/dist/store/event.js +49 -0
- package/dist/store/memory.d.ts +20 -8
- package/dist/store/memory.js +265 -100
- package/dist/store/postgres/connect.d.ts +13 -0
- package/dist/store/postgres/connect.js +5 -0
- package/dist/store/postgres/migrations/003_subject_tokens.d.ts +8 -0
- package/dist/store/postgres/migrations/003_subject_tokens.js +42 -0
- package/dist/store/postgres/migrations/004_publishable_keys.d.ts +6 -0
- package/dist/store/postgres/migrations/004_publishable_keys.js +18 -0
- package/dist/store/postgres/migrations/005_action_endpoints.d.ts +6 -0
- package/dist/store/postgres/migrations/005_action_endpoints.js +32 -0
- package/dist/store/postgres/migrations/006_drop_executors.d.ts +11 -0
- package/dist/store/postgres/migrations/006_drop_executors.js +32 -0
- package/dist/store/postgres/migrations/007_event_time.d.ts +7 -0
- package/dist/store/postgres/migrations/007_event_time.js +14 -0
- package/dist/store/postgres/migrations/008_durable_streams.d.ts +13 -0
- package/dist/store/postgres/migrations/008_durable_streams.js +35 -0
- package/dist/store/postgres/migrations/index.d.ts +5 -1
- package/dist/store/postgres/migrations/index.js +22 -2
- package/dist/store/postgres/migrations/shared/index.d.ts +31 -0
- package/dist/store/postgres/migrations/shared/index.js +92 -0
- package/dist/store/postgres/record.d.ts +11 -0
- package/dist/store/postgres/record.js +64 -0
- package/dist/store/postgres/store.js +337 -95
- package/dist/store/postgres/tenants.js +14 -0
- package/dist/store/types.d.ts +176 -81
- package/dist/streams/basin.d.ts +18 -1
- package/dist/streams/basin.js +34 -2
- package/dist/streams/control.d.ts +18 -0
- package/dist/streams/control.js +29 -0
- package/dist/streams/memory.d.ts +3 -1
- package/dist/streams/memory.js +14 -8
- package/dist/streams/relay/core.d.ts +35 -0
- package/dist/streams/relay/core.js +366 -0
- package/dist/streams/relay/memory.d.ts +69 -0
- package/dist/streams/relay/memory.js +176 -0
- package/dist/streams/relay/types.d.ts +77 -0
- package/dist/streams/relay/types.js +7 -0
- package/dist/streams/types.d.ts +66 -36
- package/dist/streams/types.js +18 -53
- package/dist/tenant/access-policy.d.ts +30 -0
- package/dist/tenant/access-policy.js +52 -0
- package/dist/tenant/actions.d.ts +10 -149
- package/dist/tenant/actions.js +5 -167
- package/dist/tenant/advance.js +32 -0
- package/dist/tenant/auth.d.ts +19 -17
- package/dist/tenant/auth.js +90 -93
- package/dist/tenant/browser.d.ts +15 -0
- package/dist/tenant/browser.js +39 -0
- package/dist/tenant/commands.d.ts +18 -2
- package/dist/tenant/commands.js +121 -129
- package/dist/tenant/context.d.ts +75 -18
- package/dist/tenant/context.js +14 -10
- package/dist/tenant/delivery-token.d.ts +41 -0
- package/dist/tenant/delivery-token.js +105 -0
- package/dist/tenant/delivery.d.ts +62 -0
- package/dist/tenant/delivery.js +476 -0
- package/dist/tenant/effects.d.ts +5 -0
- package/dist/tenant/effects.js +138 -48
- package/dist/tenant/endpoints.d.ts +67 -0
- package/dist/tenant/endpoints.js +90 -0
- package/dist/tenant/ephemeral.d.ts +6 -0
- package/dist/tenant/ephemeral.js +60 -36
- package/dist/tenant/http.d.ts +8 -9
- package/dist/tenant/http.js +13 -27
- package/dist/tenant/jwt.d.ts +16 -0
- package/dist/tenant/jwt.js +50 -0
- package/dist/tenant/module.d.ts +3 -0
- package/dist/tenant/module.js +6 -12
- package/dist/tenant/outbound.d.ts +44 -0
- package/dist/tenant/outbound.js +127 -0
- package/dist/tenant/pool.d.ts +0 -2
- package/dist/tenant/pool.js +0 -18
- package/dist/tenant/reset.d.ts +0 -4
- package/dist/tenant/reset.js +0 -5
- package/dist/tenant/runtime.d.ts +10 -20
- package/dist/tenant/runtime.js +59 -42
- package/dist/tenant/scheduler.d.ts +7 -2
- package/dist/tenant/scheduler.js +7 -3
- package/dist/tenant/session-streams.d.ts +125 -0
- package/dist/tenant/session-streams.js +399 -0
- package/dist/tenant/sessions.d.ts +28 -4
- package/dist/tenant/sessions.js +118 -24
- package/dist/tenant/signing-keys.d.ts +55 -0
- package/dist/tenant/signing-keys.js +172 -0
- package/dist/tenant/slim.d.ts +8 -0
- package/dist/tenant/slim.js +21 -0
- package/dist/tenant/status.d.ts +0 -3
- package/dist/tenant/status.js +19 -12
- package/dist/tenant/streams.d.ts +60 -67
- package/dist/tenant/streams.js +140 -208
- package/dist/tenant/subject-limits.d.ts +16 -0
- package/dist/tenant/subject-limits.js +45 -0
- package/dist/tenant/sweep.d.ts +1 -10
- package/dist/tenant/sweep.js +29 -69
- package/dist/tenant/tokens.d.ts +14 -0
- package/dist/tenant/tokens.js +178 -0
- package/dist/tenant/transcript.d.ts +48 -5
- package/dist/tenant/transcript.js +58 -1
- package/dist/tenant/types.d.ts +29 -7
- package/dist/tenant/worker.d.ts +3 -1
- package/dist/tenant/worker.js +9 -3
- package/dist/vault/service.d.ts +18 -2
- package/dist/vault/service.js +57 -13
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +20 -8
- package/dist/core/executors.d.ts +0 -44
- package/dist/core/executors.js +0 -95
- package/dist/streams/relay.d.ts +0 -37
- package/dist/streams/relay.js +0 -254
- package/dist/tenant/live.d.ts +0 -92
- package/dist/tenant/live.js +0 -290
- package/dist/tenant/routes-tenant.d.ts +0 -12
- package/dist/tenant/routes-tenant.js +0 -137
- package/dist/tenant/routes.d.ts +0 -9
- package/dist/tenant/routes.js +0 -151
package/dist/core/flow-host.js
CHANGED
|
@@ -23,8 +23,6 @@
|
|
|
23
23
|
* - `wakeLinkedWorkflow({ t, agentSessionId, turnId, output?, failed?, cancelled?, error?, schedule }): Promise<void>`
|
|
24
24
|
* - `pendingAgentEffects(t): Promise<FlowEffect[]>`
|
|
25
25
|
* - `reconcilePendingAgentEffect({ t, effectId, schedule }): Promise<boolean>`
|
|
26
|
-
* - `claimedFnVerifyActions(t): Promise<Action[]>`
|
|
27
|
-
* - `reofferFnVerifyClaim(t, actionId): Promise<boolean>`
|
|
28
26
|
* - `planCancelCascade({ t, workflowSessionId, turnId }): Promise<CascadeCancelPlan>`
|
|
29
27
|
* - `cancelSiblingWork({ t, workflowSessionId, turnId, siblingPaths?, cancelEffectIds? }): Promise<CancelSiblingResult>`
|
|
30
28
|
* - `fenceWorkflowActions({ t, workflowSessionId, turnId }): Promise<{ cancelled; uncertain }>`
|
|
@@ -61,6 +59,9 @@ export function deriveSessionId(workflowSessionId, path, ...parts) {
|
|
|
61
59
|
* (path, turnId, iterations) so prior verdicts cannot leak across Loop iterations.
|
|
62
60
|
*/
|
|
63
61
|
export function deriveAgentEffectSessionId(workflowSessionId, path, request) {
|
|
62
|
+
// A flow agent used as a tool starts fresh on every call, like any agent used as a tool.
|
|
63
|
+
if (request.context?.role === "delegate")
|
|
64
|
+
return deriveSessionId(workflowSessionId, path, "delegate", String(request.context.delegationId ?? request.turnId));
|
|
64
65
|
if (request.context?.role === "verify-agent") {
|
|
65
66
|
const iterations = request.iterations ?? "-";
|
|
66
67
|
const iterParts = iterations !== "-" && iterations.length > 0 ? iterations.split(".") : [];
|
|
@@ -88,11 +89,12 @@ export function isFlowEffect(request) {
|
|
|
88
89
|
request.kind === "verify" ||
|
|
89
90
|
isFlowToolEffect(request));
|
|
90
91
|
}
|
|
91
|
-
|
|
92
|
+
/** Actions still to be settled: waiting to be delivered, or being delivered. */
|
|
93
|
+
const OPEN_ACTION = ["pending", "delivering"];
|
|
92
94
|
function scheduleAfterCommit(t, schedule, id, wake) {
|
|
93
95
|
t.afterCommit(() => schedule(id, wake));
|
|
94
96
|
}
|
|
95
|
-
/** Active agent turns + pending/
|
|
97
|
+
/** Active agent turns + pending/delivering actions for one workflow turn. */
|
|
96
98
|
export async function countActiveFlowWork(t, workflowSessionId, turnId) {
|
|
97
99
|
const effects = await t.effectsForTurn(workflowSessionId, turnId);
|
|
98
100
|
const openActions = new Set((await t.actionsForSession(workflowSessionId, {
|
|
@@ -251,34 +253,6 @@ export async function reconcilePendingAgentEffect(input) {
|
|
|
251
253
|
});
|
|
252
254
|
return true;
|
|
253
255
|
}
|
|
254
|
-
/** Claimed `fn`/`verify` Actions, for the re-offer after a Tenant opens. */
|
|
255
|
-
export async function claimedFnVerifyActions(t) {
|
|
256
|
-
return t.actionsWithStatus(["claimed"], { kinds: ["fn", "verify"] });
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Re-offer one claimed `fn`/`verify` Action (SD-P7 / WF-C9): they are pure or repeat-safe, so
|
|
260
|
-
* a claim left by an executor of an earlier process is offered again at once rather than
|
|
261
|
-
* after its lease. The next claim bumps the generation, which fences a late result. Locks
|
|
262
|
-
* only the Action's session. Returns true when it re-offered.
|
|
263
|
-
*/
|
|
264
|
-
export async function reofferFnVerifyClaim(t, actionId) {
|
|
265
|
-
const found = await t.get("actions", actionId);
|
|
266
|
-
if (!found)
|
|
267
|
-
return false;
|
|
268
|
-
await t.lockSession(found.sessionId);
|
|
269
|
-
// Read again under the session lock.
|
|
270
|
-
const action = await t.get("actions", actionId);
|
|
271
|
-
if (!action ||
|
|
272
|
-
action.status !== "claimed" ||
|
|
273
|
-
(action.kind !== "fn" && action.kind !== "verify"))
|
|
274
|
-
return false;
|
|
275
|
-
action.status = "pending";
|
|
276
|
-
action.claimId = null;
|
|
277
|
-
action.leaseExpiresAt = null;
|
|
278
|
-
await t.put("actions", action.actionId, action);
|
|
279
|
-
t.signalWork();
|
|
280
|
-
return true;
|
|
281
|
-
}
|
|
282
256
|
/** Path depth for deepest-first cancel ordering. */
|
|
283
257
|
export function pathDepth(path) {
|
|
284
258
|
if (!path)
|
|
@@ -287,14 +261,14 @@ export function pathDepth(path) {
|
|
|
287
261
|
}
|
|
288
262
|
/**
|
|
289
263
|
* Plan a cancel cascade for a workflow session: linked agents deepest first,
|
|
290
|
-
* then pending → cancelled and
|
|
264
|
+
* then pending → cancelled and delivering → uncertain actions (SD-P11 / WF-R53).
|
|
291
265
|
*/
|
|
292
266
|
export async function planCancelCascade(input) {
|
|
293
267
|
const { t } = input;
|
|
294
268
|
const agentEntries = (await t.linkedSessions(input.workflowSessionId)).map((linked) => ({ sessionId: linked.agentSessionId, path: linked.link.path }));
|
|
295
269
|
agentEntries.sort((a, b) => pathDepth(b.path) - pathDepth(a.path));
|
|
296
270
|
const pendingActionIds = [];
|
|
297
|
-
const
|
|
271
|
+
const deliveringActionIds = [];
|
|
298
272
|
const actions = await t.actionsForSession(input.workflowSessionId, {
|
|
299
273
|
...(input.turnId !== null ? { turnId: input.turnId } : {}),
|
|
300
274
|
statuses: OPEN_ACTION,
|
|
@@ -302,19 +276,20 @@ export async function planCancelCascade(input) {
|
|
|
302
276
|
for (const action of actions) {
|
|
303
277
|
if (action.status === "pending")
|
|
304
278
|
pendingActionIds.push(action.actionId);
|
|
305
|
-
|
|
306
|
-
|
|
279
|
+
// Being delivered: the code may already be running.
|
|
280
|
+
else
|
|
281
|
+
deliveringActionIds.push(action.actionId);
|
|
307
282
|
}
|
|
308
283
|
return {
|
|
309
284
|
agentSessionIds: agentEntries.map((e) => e.sessionId),
|
|
310
285
|
pendingActionIds,
|
|
311
|
-
|
|
286
|
+
deliveringActionIds,
|
|
312
287
|
};
|
|
313
288
|
}
|
|
314
289
|
/**
|
|
315
290
|
* Cancel sibling work under a Parallel/Map parent when one branch fails (PAR-R6).
|
|
316
291
|
* Agent turns listed in `agentSessionIds` are returned for the caller to cancel;
|
|
317
|
-
* pending actions → cancelled,
|
|
292
|
+
* pending actions → cancelled, delivering tool actions → uncertain.
|
|
318
293
|
* When `cancelEffectIds` is provided, those effects are marked cancelled and their
|
|
319
294
|
* paths are included in the sibling path set.
|
|
320
295
|
*/
|
|
@@ -360,7 +335,8 @@ export async function cancelSiblingWork(input) {
|
|
|
360
335
|
for (const action of actions) {
|
|
361
336
|
if (!matchesSibling(action.path))
|
|
362
337
|
continue;
|
|
363
|
-
|
|
338
|
+
// Delivered work may already have had an external effect.
|
|
339
|
+
const next = action.status === "pending" ? "cancelled" : "uncertain";
|
|
364
340
|
action.status = next;
|
|
365
341
|
await t.put("actions", action.actionId, action);
|
|
366
342
|
const effect = await t.get("effects", action.actionId);
|
|
@@ -376,7 +352,8 @@ export async function cancelSiblingWork(input) {
|
|
|
376
352
|
return { cancelledActions, uncertainActions, agentSessionIds };
|
|
377
353
|
}
|
|
378
354
|
/**
|
|
379
|
-
* Apply cancel fencing to workflow-session actions (pending→cancelled,
|
|
355
|
+
* Apply cancel fencing to workflow-session actions (pending→cancelled,
|
|
356
|
+
* delivering→uncertain).
|
|
380
357
|
*/
|
|
381
358
|
export async function fenceWorkflowActions(input) {
|
|
382
359
|
const { t } = input;
|
|
@@ -389,7 +366,8 @@ export async function fenceWorkflowActions(input) {
|
|
|
389
366
|
statuses: OPEN_ACTION,
|
|
390
367
|
});
|
|
391
368
|
for (const action of actions) {
|
|
392
|
-
|
|
369
|
+
// Delivered work may already have had an external effect.
|
|
370
|
+
const next = action.status === "pending" ? "cancelled" : "uncertain";
|
|
393
371
|
action.status = next;
|
|
394
372
|
await t.put("actions", action.actionId, action);
|
|
395
373
|
const effect = await t.get("effects", action.actionId);
|
|
@@ -27,18 +27,23 @@ export type SandboxRouteDeps = {
|
|
|
27
27
|
*/
|
|
28
28
|
export declare function handleSessionSandboxTool(deps: SandboxRouteDeps, sessionId: string, toolName: string, body: unknown, signal: AbortSignal): Promise<SandboxToolOutcome>;
|
|
29
29
|
/**
|
|
30
|
-
* `POST /v1/actions/:id/sandbox/:tool` —
|
|
31
|
-
*
|
|
30
|
+
* `POST /v1/actions/:id/sandbox/:tool` — the Action endpoint, authorized by the delivery token
|
|
31
|
+
* of the Action's current delivery (`delivery.generation`). The body is the tool's input.
|
|
32
|
+
*/
|
|
33
|
+
export declare function handleActionSandboxTool(deps: SandboxRouteDeps, actionId: string, toolName: string, body: unknown, signal: AbortSignal, delivery: {
|
|
34
|
+
generation: number;
|
|
35
|
+
}): Promise<SandboxToolOutcome>;
|
|
36
|
+
/**
|
|
37
|
+
* Validate PutSession.sandbox attach: same owner, and a sandbox to share. A session whose
|
|
38
|
+
* definition declares a sandbox must declare the owner's spec; one whose definition declares
|
|
39
|
+
* none inherits it (`inherit`).
|
|
32
40
|
*/
|
|
33
|
-
export declare function handleActionSandboxTool(deps: SandboxRouteDeps, actionId: string, toolName: string, body: unknown, signal: AbortSignal): Promise<SandboxToolOutcome>;
|
|
34
|
-
/** Validate PutSession.sandbox attach: same owner, identical specs. */
|
|
35
41
|
export declare function validateSandboxAttach(body: {
|
|
36
42
|
ownerUserId: string;
|
|
37
|
-
sandbox?:
|
|
38
|
-
session: string;
|
|
39
|
-
};
|
|
43
|
+
sandbox?: unknown;
|
|
40
44
|
}, newManifest: SessionSandboxRef["manifest"], lookup: (id: string) => SessionSandboxRef | undefined,
|
|
41
45
|
/** Acting for a subject: another owner's session is the 404 of a missing one. */
|
|
42
46
|
options?: {
|
|
43
47
|
opaque?: boolean;
|
|
48
|
+
inherit?: boolean;
|
|
44
49
|
}): string | undefined;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Wired from TenantRuntime; keeps route bodies out of the hot runtime file.
|
|
4
4
|
*/
|
|
5
5
|
import { isSandboxToolName, } from "@nylorun/core/define";
|
|
6
|
-
import { capabilityForSandbox, owningSandboxSessionId, sandboxSpecOf, sandboxSpecsEqual, } from "../sandbox/share.js";
|
|
6
|
+
import { capabilityForSandbox, owningSandboxSessionId, sandboxSpecOf, sandboxSpecsEqual, sessionSandboxSpec, } from "../sandbox/share.js";
|
|
7
7
|
export class SandboxRouteError extends Error {
|
|
8
8
|
status;
|
|
9
9
|
constructor(status, message) {
|
|
@@ -15,7 +15,7 @@ export class SandboxRouteError extends Error {
|
|
|
15
15
|
function resolveSpec(session, lookup) {
|
|
16
16
|
const ownerId = owningSandboxSessionId(session, lookup);
|
|
17
17
|
const owner = ownerId === session.id ? session : lookup(ownerId) ?? session;
|
|
18
|
-
const spec =
|
|
18
|
+
const spec = sessionSandboxSpec(owner) ?? sessionSandboxSpec(session);
|
|
19
19
|
if (!spec)
|
|
20
20
|
throw new SandboxRouteError(404, "Session has no sandbox");
|
|
21
21
|
return spec;
|
|
@@ -31,7 +31,7 @@ function toolInput(body) {
|
|
|
31
31
|
if (typeof body !== "object" || Array.isArray(body))
|
|
32
32
|
throw new SandboxRouteError(400, "Sandbox tool body must be an object");
|
|
33
33
|
const record = body;
|
|
34
|
-
const {
|
|
34
|
+
const { requestId: _r, ...input } = record;
|
|
35
35
|
return input;
|
|
36
36
|
}
|
|
37
37
|
async function runTool(deps, { session, lookup }, tool, input, signal) {
|
|
@@ -56,39 +56,36 @@ export async function handleSessionSandboxTool(deps, sessionId, toolName, body,
|
|
|
56
56
|
return runTool(deps, read, tool, toolInput(body), signal);
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
|
-
* `POST /v1/actions/:id/sandbox/:tool` —
|
|
60
|
-
*
|
|
59
|
+
* `POST /v1/actions/:id/sandbox/:tool` — the Action endpoint, authorized by the delivery token
|
|
60
|
+
* of the Action's current delivery (`delivery.generation`). The body is the tool's input.
|
|
61
61
|
*/
|
|
62
|
-
export async function handleActionSandboxTool(deps, actionId, toolName, body, signal) {
|
|
62
|
+
export async function handleActionSandboxTool(deps, actionId, toolName, body, signal, delivery) {
|
|
63
63
|
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
64
64
|
throw new SandboxRouteError(400, "Sandbox tool body must be an object");
|
|
65
|
-
const record = body;
|
|
66
|
-
const claimId = record.claimId;
|
|
67
|
-
const generation = record.generation;
|
|
68
|
-
if (typeof claimId !== "string" || claimId.length === 0)
|
|
69
|
-
throw new SandboxRouteError(400, "claimId is required");
|
|
70
|
-
if (typeof generation !== "number" || !Number.isInteger(generation) || generation < 1)
|
|
71
|
-
throw new SandboxRouteError(400, "generation must be a positive integer");
|
|
72
65
|
const action = await deps.getAction(actionId);
|
|
73
66
|
if (!action)
|
|
74
67
|
throw new SandboxRouteError(404, "Action not found");
|
|
75
|
-
if (action.status !== "
|
|
76
|
-
action.
|
|
77
|
-
action.
|
|
78
|
-
|
|
79
|
-
Date.parse(action.leaseExpiresAt) <= Date.now())
|
|
80
|
-
throw new SandboxRouteError(409, "Stale or expired claim");
|
|
68
|
+
if (action.status !== "delivering" ||
|
|
69
|
+
action.generation !== delivery.generation ||
|
|
70
|
+
Date.parse(action.deadlineAt ?? "") <= Date.now())
|
|
71
|
+
throw new SandboxRouteError(409, "The delivery was cancelled, lost or delivered again");
|
|
81
72
|
const read = await deps.session(action.sessionId);
|
|
82
73
|
if (read.session.activeTurnId !== action.turnId)
|
|
83
74
|
throw new SandboxRouteError(409, "Action unavailable");
|
|
84
|
-
|
|
85
|
-
return runTool(deps, read, tool, toolInput(body), signal);
|
|
75
|
+
return runTool(deps, read, parseTool(toolName), toolInput(body), signal);
|
|
86
76
|
}
|
|
87
|
-
/**
|
|
77
|
+
/**
|
|
78
|
+
* Validate PutSession.sandbox attach: same owner, and a sandbox to share. A session whose
|
|
79
|
+
* definition declares a sandbox must declare the owner's spec; one whose definition declares
|
|
80
|
+
* none inherits it (`inherit`).
|
|
81
|
+
*/
|
|
88
82
|
export function validateSandboxAttach(body, newManifest, lookup,
|
|
89
83
|
/** Acting for a subject: another owner's session is the 404 of a missing one. */
|
|
90
84
|
options = {}) {
|
|
91
|
-
const
|
|
85
|
+
const request = body.sandbox;
|
|
86
|
+
const share = typeof request === "object" && request !== null && "session" in request
|
|
87
|
+
? String(request.session)
|
|
88
|
+
: undefined;
|
|
92
89
|
if (!share)
|
|
93
90
|
return undefined;
|
|
94
91
|
const owner = lookup(share);
|
|
@@ -96,10 +93,12 @@ options = {}) {
|
|
|
96
93
|
throw new SandboxRouteError(404, "Sandbox session not found");
|
|
97
94
|
if (owner.ownerUserId !== body.ownerUserId)
|
|
98
95
|
throw new SandboxRouteError(403, "Sandbox session must belong to the same owner");
|
|
99
|
-
const ownerSpec =
|
|
100
|
-
const newSpec = sandboxSpecOf(newManifest);
|
|
96
|
+
const ownerSpec = sessionSandboxSpec(owner);
|
|
101
97
|
if (!ownerSpec)
|
|
102
98
|
throw new SandboxRouteError(400, "Target session has no sandbox to share");
|
|
99
|
+
if (options.inherit)
|
|
100
|
+
return owningSandboxSessionId(owner, lookup);
|
|
101
|
+
const newSpec = sandboxSpecOf(newManifest);
|
|
103
102
|
if (!newSpec)
|
|
104
103
|
throw new SandboxRouteError(400, "Session must declare a sandbox identical to the shared session");
|
|
105
104
|
if (!sandboxSpecsEqual(ownerSpec, newSpec))
|
|
@@ -2,9 +2,9 @@ import { type DurableExecution, type Wake, type WorkerHandlers } from "./types.j
|
|
|
2
2
|
export interface MemoryExecutionOptions {
|
|
3
3
|
/** Delay between sweep passes of one Tenant. Default 5000. */
|
|
4
4
|
sweepIntervalMs?: number;
|
|
5
|
-
/** First retry delay after `advance` throws; doubles per attempt. Default 50. */
|
|
5
|
+
/** First retry delay after `advance` or `deliver` throws; doubles per attempt. Default 50. */
|
|
6
6
|
retryDelayMs?: number;
|
|
7
|
-
/** Attempts before giving up on a throwing
|
|
7
|
+
/** Attempts before giving up on a throwing handler and reporting it. Default 5. */
|
|
8
8
|
maxAttempts?: number;
|
|
9
9
|
/** How long a dedupe key merges repeated wakes. Default 24 hours. */
|
|
10
10
|
dedupeRetentionMs?: number;
|
|
@@ -32,21 +32,24 @@ export declare class MemoryExecution implements DurableExecution {
|
|
|
32
32
|
private readonly onError;
|
|
33
33
|
constructor(options?: MemoryExecutionOptions);
|
|
34
34
|
wake(tenantId: string, sessionId: string, wake: Wake): Promise<void>;
|
|
35
|
+
deliver(tenantId: string, actionId: string): Promise<void>;
|
|
35
36
|
timer(tenantId: string, key: string, at: Date): Promise<void>;
|
|
36
37
|
armSweep(tenantId: string): Promise<void>;
|
|
37
38
|
disarmSweep(tenantId: string): Promise<void>;
|
|
38
39
|
start(handlers: WorkerHandlers): Promise<void>;
|
|
39
40
|
stop(): Promise<void>;
|
|
40
41
|
/**
|
|
41
|
-
* Resolves once no advance is running or queued and no busy or
|
|
42
|
-
* re-
|
|
42
|
+
* Resolves once no advance or delivery is running or queued and no busy or
|
|
43
|
+
* retry re-run is pending. Timers and sweeps are not waited for. Test helper.
|
|
43
44
|
*/
|
|
44
45
|
idle(): Promise<void>;
|
|
45
46
|
private enqueue;
|
|
46
47
|
private drain;
|
|
47
48
|
/** Drops dedupe keys older than the retention window. */
|
|
48
49
|
private forgetDedupeKeys;
|
|
49
|
-
private
|
|
50
|
+
private runOnce;
|
|
51
|
+
/** Runs the key's handler; resolves to a delay when the key must run again. */
|
|
52
|
+
private call;
|
|
50
53
|
private armTimer;
|
|
51
54
|
private scheduleSweep;
|
|
52
55
|
private later;
|
package/dist/execution/memory.js
CHANGED
|
@@ -41,7 +41,10 @@ export class MemoryExecution {
|
|
|
41
41
|
return;
|
|
42
42
|
this.seen.set(dedupe, now);
|
|
43
43
|
}
|
|
44
|
-
this.enqueue(tenantId, sessionId);
|
|
44
|
+
this.enqueue("advance", tenantId, sessionId);
|
|
45
|
+
}
|
|
46
|
+
async deliver(tenantId, actionId) {
|
|
47
|
+
this.enqueue("deliver", tenantId, actionId);
|
|
45
48
|
}
|
|
46
49
|
async timer(tenantId, key, at) {
|
|
47
50
|
const id = `${tenantId}\u0000${key}`;
|
|
@@ -96,8 +99,8 @@ export class MemoryExecution {
|
|
|
96
99
|
await Promise.allSettled(this.inflight);
|
|
97
100
|
}
|
|
98
101
|
/**
|
|
99
|
-
* Resolves once no advance is running or queued and no busy or
|
|
100
|
-
* re-
|
|
102
|
+
* Resolves once no advance or delivery is running or queued and no busy or
|
|
103
|
+
* retry re-run is pending. Timers and sweeps are not waited for. Test helper.
|
|
101
104
|
*/
|
|
102
105
|
async idle() {
|
|
103
106
|
while (this.delayed.size > 0 ||
|
|
@@ -105,13 +108,14 @@ export class MemoryExecution {
|
|
|
105
108
|
[...this.keys.values()].some((s) => s.running || (s.queued && this.handlers)))
|
|
106
109
|
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
107
110
|
}
|
|
108
|
-
enqueue(tenantId,
|
|
109
|
-
const key =
|
|
111
|
+
enqueue(kind, tenantId, id) {
|
|
112
|
+
const key = stateKey(kind, tenantId, id);
|
|
110
113
|
let state = this.keys.get(key);
|
|
111
114
|
if (!state)
|
|
112
115
|
this.keys.set(key, (state = {
|
|
116
|
+
kind,
|
|
113
117
|
tenantId,
|
|
114
|
-
|
|
118
|
+
id,
|
|
115
119
|
running: false,
|
|
116
120
|
queued: false,
|
|
117
121
|
attempts: 0,
|
|
@@ -126,14 +130,14 @@ export class MemoryExecution {
|
|
|
126
130
|
try {
|
|
127
131
|
while (state.queued && this.handlers) {
|
|
128
132
|
state.queued = false;
|
|
129
|
-
await this.
|
|
133
|
+
await this.runOnce(state, this.handlers);
|
|
130
134
|
}
|
|
131
135
|
}
|
|
132
136
|
finally {
|
|
133
137
|
state.running = false;
|
|
134
138
|
// Idle keys hold nothing (a retry keeps its attempt count); forget them so
|
|
135
139
|
// long-lived processes stay small.
|
|
136
|
-
const key =
|
|
140
|
+
const key = stateKey(state.kind, state.tenantId, state.id);
|
|
137
141
|
if (!state.queued && state.attempts === 0 && this.keys.get(key) === state)
|
|
138
142
|
this.keys.delete(key);
|
|
139
143
|
}
|
|
@@ -147,14 +151,15 @@ export class MemoryExecution {
|
|
|
147
151
|
this.seen.delete(dedupe);
|
|
148
152
|
}
|
|
149
153
|
}
|
|
150
|
-
async
|
|
154
|
+
async runOnce(state, handlers) {
|
|
151
155
|
const controller = new AbortController();
|
|
152
156
|
state.controller = controller;
|
|
157
|
+
const again = () => this.enqueue(state.kind, state.tenantId, state.id);
|
|
153
158
|
try {
|
|
154
|
-
const
|
|
159
|
+
const retryAfterMs = await this.call(state, handlers, controller.signal);
|
|
155
160
|
state.attempts = 0;
|
|
156
|
-
if (
|
|
157
|
-
this.later(
|
|
161
|
+
if (retryAfterMs !== undefined)
|
|
162
|
+
this.later(retryAfterMs, again);
|
|
158
163
|
}
|
|
159
164
|
catch (error) {
|
|
160
165
|
state.attempts += 1;
|
|
@@ -163,12 +168,23 @@ export class MemoryExecution {
|
|
|
163
168
|
this.onError(error);
|
|
164
169
|
}
|
|
165
170
|
else
|
|
166
|
-
this.later(this.retryDelayMs * 2 ** (state.attempts - 1),
|
|
171
|
+
this.later(this.retryDelayMs * 2 ** (state.attempts - 1), again);
|
|
167
172
|
}
|
|
168
173
|
finally {
|
|
169
174
|
state.controller = undefined;
|
|
170
175
|
}
|
|
171
176
|
}
|
|
177
|
+
/** Runs the key's handler; resolves to a delay when the key must run again. */
|
|
178
|
+
async call(state, handlers, signal) {
|
|
179
|
+
if (state.kind === "advance") {
|
|
180
|
+
const result = await handlers.advance(state.tenantId, state.id, signal);
|
|
181
|
+
return result.status === "busy" ? result.retryAfterMs : undefined;
|
|
182
|
+
}
|
|
183
|
+
if (!handlers.deliver)
|
|
184
|
+
throw new Error("WorkerHandlers.deliver is required for deliveries");
|
|
185
|
+
const result = await handlers.deliver(state.tenantId, state.id, signal);
|
|
186
|
+
return result.status === "retry" ? result.retryAfterMs : undefined;
|
|
187
|
+
}
|
|
172
188
|
armTimer(id, timer) {
|
|
173
189
|
timer.handle = setTimeout(() => {
|
|
174
190
|
if (this.timers.get(id) !== timer)
|
|
@@ -215,3 +231,7 @@ export class MemoryExecution {
|
|
|
215
231
|
void promise.finally(() => this.inflight.delete(promise));
|
|
216
232
|
}
|
|
217
233
|
}
|
|
234
|
+
/** Advances and deliveries are queued apart: a session id and an Action id may be equal. */
|
|
235
|
+
function stateKey(kind, tenantId, id) {
|
|
236
|
+
return `${kind}\u0000${sessionKey(tenantId, id)}`;
|
|
237
|
+
}
|
|
@@ -27,21 +27,31 @@
|
|
|
27
27
|
* becomes `uncertain` is settled in the Session Store and `advance` returns
|
|
28
28
|
* `done`.
|
|
29
29
|
* - **Sweeps.** `armSweep` arms one self-re-arming sweep per Tenant; arming an
|
|
30
|
-
* armed Tenant is a no-op. The sweep
|
|
31
|
-
* `runnable` sessions
|
|
30
|
+
* armed Tenant is a no-op. The sweep settles lapsed deliveries and re-wakes
|
|
31
|
+
* orphaned `runnable` sessions.
|
|
32
|
+
* - **One delivery per Action at a time.** `deliver` runs
|
|
33
|
+
* `WorkerHandlers.deliver` for the key `<tenantId>:<actionId>` at least once
|
|
34
|
+
* after it resolves. Two deliveries for one key never overlap; repeated
|
|
35
|
+
* `deliver` calls may each run it, so the handler reads the Action's state
|
|
36
|
+
* and does nothing when there is nothing to deliver.
|
|
37
|
+
* - **Retry after `retry`.** A delivery that returns `retry` is run again for
|
|
38
|
+
* the same key after `retryAfterMs`. This is how an Action endpoint that is
|
|
39
|
+
* down or busy is retried; the handler throws only on infrastructure errors.
|
|
32
40
|
*/
|
|
33
41
|
/** Why a session is woken (§12.3, "Where wakes come from"). */
|
|
34
42
|
export type WakeReason =
|
|
35
43
|
/** Session commands. */
|
|
36
44
|
"message" | "approve" | "respond"
|
|
37
|
-
/** An
|
|
45
|
+
/** An Action's outcome was recorded (a delivery's answer or a background result). */
|
|
38
46
|
| "action_result"
|
|
39
47
|
/** A linked agent turn completed, failed or was cancelled. */
|
|
40
48
|
| "linked"
|
|
41
49
|
/** Queued flow effects, pending agent effects reconciled. */
|
|
42
50
|
| "flow"
|
|
43
51
|
/** The Tenant sweep and `busy` re-wakes. */
|
|
44
|
-
| "recover"
|
|
52
|
+
| "recover"
|
|
53
|
+
/** A long turn ended a segment at a step boundary and continues in the next (Model Calls §10). */
|
|
54
|
+
| "rollover";
|
|
45
55
|
export declare const WAKE_REASONS: readonly WakeReason[];
|
|
46
56
|
export interface Wake {
|
|
47
57
|
reason: WakeReason;
|
|
@@ -59,6 +69,14 @@ export type AdvanceResult = {
|
|
|
59
69
|
status: "busy";
|
|
60
70
|
retryAfterMs: number;
|
|
61
71
|
};
|
|
72
|
+
export type DeliverResult = {
|
|
73
|
+
status: "done";
|
|
74
|
+
}
|
|
75
|
+
/** The Action is still to be delivered; run again for this key after `retryAfterMs`. */
|
|
76
|
+
| {
|
|
77
|
+
status: "retry";
|
|
78
|
+
retryAfterMs: number;
|
|
79
|
+
};
|
|
62
80
|
/** What a Worker runs when Durable Session Execution calls it. */
|
|
63
81
|
export interface WorkerHandlers {
|
|
64
82
|
/**
|
|
@@ -71,6 +89,11 @@ export interface WorkerHandlers {
|
|
|
71
89
|
sweep(tenantId: string): Promise<void>;
|
|
72
90
|
/** A durable timer set with `timer` fired. Required when `timer` is used. */
|
|
73
91
|
fire?(tenantId: string, key: string): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Delivers one Action to its Action endpoint. `signal` aborts when the Worker
|
|
94
|
+
* stops. Throw only on infrastructure errors. Required when `deliver` is used.
|
|
95
|
+
*/
|
|
96
|
+
deliver?(tenantId: string, actionId: string, signal: AbortSignal): Promise<DeliverResult>;
|
|
74
97
|
}
|
|
75
98
|
export interface DurableExecution {
|
|
76
99
|
/** At-least-once. `dedupeKey` merges repeated wakes for the same cause. */
|
|
@@ -81,6 +104,12 @@ export interface DurableExecution {
|
|
|
81
104
|
* implementation can; `fire` must be idempotent either way.
|
|
82
105
|
*/
|
|
83
106
|
timer(tenantId: string, key: string, at: Date): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* At-least-once: `WorkerHandlers.deliver(tenantId, actionId)` runs after this
|
|
109
|
+
* resolves, never overlapping another delivery of the same Action. Send it
|
|
110
|
+
* after the transaction that made the Action pending commits.
|
|
111
|
+
*/
|
|
112
|
+
deliver(tenantId: string, actionId: string): Promise<void>;
|
|
84
113
|
/** Arms the Tenant's self-re-arming sweep. Idempotent. */
|
|
85
114
|
armSweep(tenantId: string): Promise<void>;
|
|
86
115
|
/** Stops re-arming the Tenant's sweep (Tenant deleted). A pass already running finishes. */
|
|
@@ -109,14 +138,17 @@ export interface StuckInvocation {
|
|
|
109
138
|
/** Service name without any prefix, e.g. `NylorunSession`. */
|
|
110
139
|
service: string;
|
|
111
140
|
handler: string;
|
|
112
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* Object key: `<tenantId>:<sessionId>`, `<tenantId>`, `<tenantId>:<timer key>` or
|
|
143
|
+
* `<tenantId>:<actionId>`.
|
|
144
|
+
*/
|
|
113
145
|
key: string;
|
|
114
146
|
tenantId?: string;
|
|
115
147
|
retryCount: number;
|
|
116
148
|
lastFailure?: string;
|
|
117
149
|
modifiedAt?: string;
|
|
118
150
|
}
|
|
119
|
-
/** The key one advance at a time is serialized on. */
|
|
151
|
+
/** The key one advance (or one delivery, with an Action id) at a time is serialized on. */
|
|
120
152
|
export declare function sessionKey(tenantId: string, sessionId: string): string;
|
|
121
153
|
/** Splits a key made by `sessionKey`. Tenant ids never contain `:`. */
|
|
122
154
|
export declare function parseSessionKey(key: string): {
|
package/dist/execution/types.js
CHANGED
|
@@ -27,8 +27,16 @@
|
|
|
27
27
|
* becomes `uncertain` is settled in the Session Store and `advance` returns
|
|
28
28
|
* `done`.
|
|
29
29
|
* - **Sweeps.** `armSweep` arms one self-re-arming sweep per Tenant; arming an
|
|
30
|
-
* armed Tenant is a no-op. The sweep
|
|
31
|
-
* `runnable` sessions
|
|
30
|
+
* armed Tenant is a no-op. The sweep settles lapsed deliveries and re-wakes
|
|
31
|
+
* orphaned `runnable` sessions.
|
|
32
|
+
* - **One delivery per Action at a time.** `deliver` runs
|
|
33
|
+
* `WorkerHandlers.deliver` for the key `<tenantId>:<actionId>` at least once
|
|
34
|
+
* after it resolves. Two deliveries for one key never overlap; repeated
|
|
35
|
+
* `deliver` calls may each run it, so the handler reads the Action's state
|
|
36
|
+
* and does nothing when there is nothing to deliver.
|
|
37
|
+
* - **Retry after `retry`.** A delivery that returns `retry` is run again for
|
|
38
|
+
* the same key after `retryAfterMs`. This is how an Action endpoint that is
|
|
39
|
+
* down or busy is retried; the handler throws only on infrastructure errors.
|
|
32
40
|
*/
|
|
33
41
|
export const WAKE_REASONS = [
|
|
34
42
|
"message",
|
|
@@ -38,8 +46,9 @@ export const WAKE_REASONS = [
|
|
|
38
46
|
"linked",
|
|
39
47
|
"flow",
|
|
40
48
|
"recover",
|
|
49
|
+
"rollover",
|
|
41
50
|
];
|
|
42
|
-
/** The key one advance at a time is serialized on. */
|
|
51
|
+
/** The key one advance (or one delivery, with an Action id) at a time is serialized on. */
|
|
43
52
|
export function sessionKey(tenantId, sessionId) {
|
|
44
53
|
return `${tenantId}:${sessionId}`;
|
|
45
54
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Admin API (`/v1/admin/**`): Tenants and the Host's status, for a caller with the admin
|
|
3
|
+
* key. The Host checks the listener, the protocol and the admin key first (`app.ts`); what
|
|
4
|
+
* gets here is authorized. Routes are declared once, for serving and for the OpenAPI document.
|
|
5
|
+
*/
|
|
6
|
+
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
7
|
+
import { type AdminStatus } from "@nylorun/core/contracts";
|
|
8
|
+
import { type NodeBindings, type TenantModule } from "../tenant/types.js";
|
|
9
|
+
export interface AdminApiOptions {
|
|
10
|
+
module: TenantModule;
|
|
11
|
+
/** `GET /v1/admin/status`. */
|
|
12
|
+
status(): Promise<AdminStatus>;
|
|
13
|
+
/** Stops the Host once the shutdown answer is sent. */
|
|
14
|
+
shutdown(): void;
|
|
15
|
+
/** `GET /v1/admin/openapi.json`: this API's OpenAPI document (`api/openapi.ts`). */
|
|
16
|
+
document?(): unknown;
|
|
17
|
+
}
|
|
18
|
+
type AdminEnv = {
|
|
19
|
+
Bindings: NodeBindings;
|
|
20
|
+
};
|
|
21
|
+
export declare function createAdminApi(options: AdminApiOptions): OpenAPIHono<AdminEnv>;
|
|
22
|
+
export {};
|