@nanobpm/urban 0.83.0 → 0.85.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/runtime/core/host.d.ts +87 -1
- package/dist/runtime/core/host.js +4 -0
- package/dist/runtime/core/modules/api.d.ts +5 -0
- package/dist/runtime/core/modules/api.js +2 -2
- package/dist/runtime/core/modules/mcp.d.ts +57 -0
- package/dist/runtime/core/modules/mcp.js +709 -0
- package/dist/runtime/core/modules/surfaces.js +11 -0
- package/dist/runtime/engine/nanosdk.d.ts +41 -1
- package/dist/runtime/engine/nanosdk.js +118 -0
- package/dist/runtime/index.d.ts +1 -1
- package/package.json +2 -1
- package/src/runtime/core/host.ts +89 -0
- package/src/runtime/core/modules/actions.test.ts +4 -0
- package/src/runtime/core/modules/agent.test.ts +4 -0
- package/src/runtime/core/modules/api.test.ts +4 -0
- package/src/runtime/core/modules/api.ts +2 -2
- package/src/runtime/core/modules/cancel.test.ts +4 -0
- package/src/runtime/core/modules/connectors.test.ts +4 -0
- package/src/runtime/core/modules/datasource.migrations.test.ts +4 -0
- package/src/runtime/core/modules/instance-tracking.test.ts +4 -0
- package/src/runtime/core/modules/mcp.test.ts +687 -0
- package/src/runtime/core/modules/mcp.ts +814 -0
- package/src/runtime/core/modules/pages.test.ts +4 -0
- package/src/runtime/core/modules/surfaces.test.ts +24 -0
- package/src/runtime/core/modules/surfaces.ts +11 -0
- package/src/runtime/core/modules/triggers.test.ts +4 -0
- package/src/runtime/core/modules/workers.test.ts +4 -0
- package/src/runtime/core/runtime.extensions.test.ts +6 -0
- package/src/runtime/core/runtime.test.ts +6 -0
- package/src/runtime/engine/nanosdk.test.ts +103 -0
- package/src/runtime/engine/nanosdk.ts +152 -0
- package/src/runtime/index.ts +4 -0
|
@@ -318,6 +318,51 @@ export interface ElementInstanceWaitStateFilter {
|
|
|
318
318
|
elementId?: string;
|
|
319
319
|
waitStateType?: WaitStateType;
|
|
320
320
|
}
|
|
321
|
+
/**
|
|
322
|
+
* The engine's own incident-state enum — the lifecycle states an engine actually reports and
|
|
323
|
+
* that are valid as a search selector. An `ACTIVE` incident is an open fault a human/agent can
|
|
324
|
+
* act on (resolve/retry); the other values are terminal or transitional. This is the single
|
|
325
|
+
* source of truth for real engine states; {@link IncidentState} derives from it.
|
|
326
|
+
*/
|
|
327
|
+
export type EngineIncidentState = "ACTIVE" | "MIGRATED" | "PENDING" | "RESOLVED";
|
|
328
|
+
/**
|
|
329
|
+
* The lifecycle state of an incident, as {@link EngineClient.searchIncidents} reports it.
|
|
330
|
+
* Extends {@link EngineIncidentState} with the client-side `"UNKNOWN"` sentinel — a fallback
|
|
331
|
+
* used when the engine returns an unrecognized/absent value, so an incident is surfaced rather
|
|
332
|
+
* than dropped for an odd state. Because `"UNKNOWN"` is not a real engine state, it is not a
|
|
333
|
+
* valid {@link IncidentFilter} selector. A caller narrows on this union rather than probing a
|
|
334
|
+
* raw string.
|
|
335
|
+
*/
|
|
336
|
+
export type IncidentState = EngineIncidentState | "UNKNOWN";
|
|
337
|
+
/**
|
|
338
|
+
* Selectors for an incident search: which process instance and/or lifecycle state to match.
|
|
339
|
+
* Used by {@link EngineClient.searchIncidents}. An eventually consistent (zero-wait) read; an
|
|
340
|
+
* unset selector matches every incident.
|
|
341
|
+
*/
|
|
342
|
+
export interface IncidentFilter {
|
|
343
|
+
processInstanceKey?: string;
|
|
344
|
+
state?: EngineIncidentState;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* A single incident (a stuck token — a job out of retries, an unhandled error, a failed
|
|
348
|
+
* gateway evaluation) as {@link EngineClient.searchIncidents} reports it. Carries the
|
|
349
|
+
* `incidentKey` a caller passes to {@link EngineClient.resolveIncident}, the owning
|
|
350
|
+
* `processInstanceKey`, and — when the incident is job-backed — the `jobKey` a caller passes
|
|
351
|
+
* to {@link EngineClient.updateJobRetries} to make the job retriable before resolving.
|
|
352
|
+
* `errorType`/`errorMessage` are the engine's fault description; they are free-form across
|
|
353
|
+
* engines (the live SDK reports a Camunda error-type enum, the in-process engine a coarser
|
|
354
|
+
* `kind`), so treat them as diagnostic text, not a stable taxonomy.
|
|
355
|
+
*/
|
|
356
|
+
export interface IncidentSummary {
|
|
357
|
+
readonly incidentKey: string;
|
|
358
|
+
readonly processInstanceKey: string;
|
|
359
|
+
readonly elementId?: string;
|
|
360
|
+
readonly elementInstanceKey?: string;
|
|
361
|
+
readonly jobKey?: string;
|
|
362
|
+
readonly errorType?: string;
|
|
363
|
+
readonly errorMessage?: string;
|
|
364
|
+
readonly state: IncidentState;
|
|
365
|
+
}
|
|
321
366
|
/**
|
|
322
367
|
* A deployed form's form-js schema plus its identifying metadata, as
|
|
323
368
|
* {@link EngineClient.getForm} returns it. `schema` is the parsed form-js document
|
|
@@ -447,6 +492,47 @@ export interface EngineClient {
|
|
|
447
492
|
* {@link searchElementInstances}.
|
|
448
493
|
*/
|
|
449
494
|
getElementInstance(elementInstanceKey: string): Promise<ElementInstanceSummary | null>;
|
|
495
|
+
/**
|
|
496
|
+
* Search incidents by owning process instance and/or lifecycle state
|
|
497
|
+
* (`POST /v2/incidents/search`) — the open faults blocking one or more instances (a job out
|
|
498
|
+
* of retries, an unhandled error, a failed gateway evaluation). An eventually consistent
|
|
499
|
+
* (zero-wait) read; each {@link IncidentSummary} carries the `incidentKey` to pass to
|
|
500
|
+
* {@link resolveIncident} and, for a job-backed incident, the `jobKey` to pass to
|
|
501
|
+
* {@link updateJobRetries}. The debugging counterpart to the read accessors above: where
|
|
502
|
+
* they answer "how far has this instance progressed", this answers "why is it stuck".
|
|
503
|
+
*/
|
|
504
|
+
searchIncidents(filter?: IncidentFilter): Promise<IncidentSummary[]>;
|
|
505
|
+
/**
|
|
506
|
+
* Resolve an open incident by key (`POST /v2/incidents/{incidentKey}/resolution`), unblocking
|
|
507
|
+
* the parked token: a job incident returns the job to the activatable pool (so it must have
|
|
508
|
+
* retries left first — see {@link updateJobRetries}), a gateway incident re-evaluates, an
|
|
509
|
+
* uncaught-error incident re-creates the service-task job. A mutating operation.
|
|
510
|
+
*/
|
|
511
|
+
resolveIncident(input: {
|
|
512
|
+
incidentKey: string;
|
|
513
|
+
}): Promise<void>;
|
|
514
|
+
/**
|
|
515
|
+
* Update the remaining retries of a job (`PATCH /v2/jobs/{jobKey}`) — the "retry a failed
|
|
516
|
+
* job" operation. Setting a failed job's retries back above zero makes it activatable again;
|
|
517
|
+
* paired with {@link resolveIncident}, this is how a stuck `jobNoRetries` incident is cleared
|
|
518
|
+
* (bump retries, then resolve). A mutating operation.
|
|
519
|
+
*/
|
|
520
|
+
updateJobRetries(input: {
|
|
521
|
+
jobKey: string;
|
|
522
|
+
retries: number;
|
|
523
|
+
}): Promise<void>;
|
|
524
|
+
/**
|
|
525
|
+
* Set (merge) variables into a scope (`PUT /v2/element-instances/{scopeKey}/variables`).
|
|
526
|
+
* `scopeKey` is a process-instance key or an element-instance key; when `local` is `true` the
|
|
527
|
+
* variables are merged strictly into that local scope, otherwise (the default) they propagate
|
|
528
|
+
* to the outermost scope. A mutating operation used to repair state before resolving an
|
|
529
|
+
* incident or to steer an instance during debugging.
|
|
530
|
+
*/
|
|
531
|
+
setVariables(input: {
|
|
532
|
+
scopeKey: string;
|
|
533
|
+
variables: Record<string, unknown>;
|
|
534
|
+
local?: boolean;
|
|
535
|
+
}): Promise<void>;
|
|
450
536
|
/** Register a push worker for a job type. Draining is handled by the adapter. */
|
|
451
537
|
registerWorker(jobType: string, handler: JobHandler, options?: {
|
|
452
538
|
workerName?: string;
|
|
@@ -470,4 +556,4 @@ export interface EngineClient {
|
|
|
470
556
|
* and `getForm` before it) that a purely structural `implements EngineClient` check no
|
|
471
557
|
* longer catches once the fake is published/compiled against an older `@nanobpm/urban`.
|
|
472
558
|
*/
|
|
473
|
-
export declare const ENGINE_CLIENT_METHODS: readonly ["deployResources", "createInstance", "cancelInstance", "publishMessage", "searchUserTasks", "openUserTasks", "getForm", "completeUserTask", "searchProcessInstances", "searchElementInstances", "searchElementInstanceWaitStates", "getElementInstance", "registerWorker", "close"];
|
|
559
|
+
export declare const ENGINE_CLIENT_METHODS: readonly ["deployResources", "createInstance", "cancelInstance", "publishMessage", "searchUserTasks", "openUserTasks", "getForm", "completeUserTask", "searchProcessInstances", "searchElementInstances", "searchElementInstanceWaitStates", "getElementInstance", "searchIncidents", "resolveIncident", "updateJobRetries", "setVariables", "registerWorker", "close"];
|
|
@@ -69,6 +69,10 @@ export const ENGINE_CLIENT_METHODS = [
|
|
|
69
69
|
"searchElementInstances",
|
|
70
70
|
"searchElementInstanceWaitStates",
|
|
71
71
|
"getElementInstance",
|
|
72
|
+
"searchIncidents",
|
|
73
|
+
"resolveIncident",
|
|
74
|
+
"updateJobRetries",
|
|
75
|
+
"setVariables",
|
|
72
76
|
"registerWorker",
|
|
73
77
|
"close",
|
|
74
78
|
];
|
|
@@ -13,6 +13,10 @@ export interface ApiBinding {
|
|
|
13
13
|
* for free — the UI reads the SAME OpenAPI document the runtime routes + validates from. */
|
|
14
14
|
docs?: boolean | string;
|
|
15
15
|
}
|
|
16
|
+
/** The canonical, non-configurable mount prefix for an app's OpenAPI operations. Operations always
|
|
17
|
+
* live under `/app/api` so page actions, links, and any `callRoute` can name an operation path as a
|
|
18
|
+
* stable framework constant (`/app/api/<op path>`) — there is no per-app `base` knob to drift. */
|
|
19
|
+
export declare const API_BASE = "/app/api";
|
|
16
20
|
/**
|
|
17
21
|
* Derive the ABSOLUTE spec + operations-server URLs the Swagger UI page should use, from the docs
|
|
18
22
|
* page's own address (`pageHref`), the mount-root-relative docs path (`docsBase`, e.g.
|
|
@@ -97,6 +101,7 @@ export declare class NotImplemented extends Error {
|
|
|
97
101
|
export declare function defineOperation<Req extends OperationContract = DefaultContract, Res = unknown>(_id: string, handler: OperationHandler<Req, Res>): OperationHandler<Req, Res>;
|
|
98
102
|
/** Resolve an operation delegate from a loaded module: `default` (when a function) else `handler`. */
|
|
99
103
|
export declare function resolveOperationHandler(mod: Record<string, unknown>): OperationHandler | undefined;
|
|
104
|
+
export declare function readApiBinding(manifest: unknown): ApiBinding | undefined;
|
|
100
105
|
/** The Swagger UI route for an app's `api` binding, or `undefined` when there is no api surface
|
|
101
106
|
* or docs are disabled. Lets other surfaces (e.g. the pages shell's "API docs" badge) link to
|
|
102
107
|
* the docs without re-deriving the path. */
|
|
@@ -19,7 +19,7 @@ import { collectOperations, isSafeOperationId, operationsWithoutId, operationsWi
|
|
|
19
19
|
/** The canonical, non-configurable mount prefix for an app's OpenAPI operations. Operations always
|
|
20
20
|
* live under `/app/api` so page actions, links, and any `callRoute` can name an operation path as a
|
|
21
21
|
* stable framework constant (`/app/api/<op path>`) — there is no per-app `base` knob to drift. */
|
|
22
|
-
const API_BASE = "/app/api";
|
|
22
|
+
export const API_BASE = "/app/api";
|
|
23
23
|
/** Pinned Swagger UI dist served from a CDN, so the docs UI adds zero bundle/runtime deps to an
|
|
24
24
|
* Urban app (matching the deps-free spirit of the rest of the surface — validators are standalone,
|
|
25
25
|
* ADR 0058 Consequences). Pinned (not `latest`) for reproducibility. */
|
|
@@ -147,7 +147,7 @@ export function resolveOperationHandler(mod) {
|
|
|
147
147
|
return mod.handler;
|
|
148
148
|
return undefined;
|
|
149
149
|
}
|
|
150
|
-
function readApiBinding(manifest) {
|
|
150
|
+
export function readApiBinding(manifest) {
|
|
151
151
|
const raw = manifest && typeof manifest === "object" ? Reflect.get(manifest, "api") : undefined;
|
|
152
152
|
if (!raw || typeof raw !== "object")
|
|
153
153
|
return undefined;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { AppApi, RuntimeContext } from "../context.ts";
|
|
2
|
+
import type { HttpRequest } from "../host.ts";
|
|
3
|
+
import { type Route } from "../router.ts";
|
|
4
|
+
export interface McpHandle {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
routes: Route[];
|
|
7
|
+
/** Whether the surface actually answers vs. 404s (config-enabled). The route is always mounted
|
|
8
|
+
* for a stable address, so callers deriving an "active surfaces" list must gate on this. */
|
|
9
|
+
readonly enabled: boolean;
|
|
10
|
+
describe(): Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
/** Mint a fresh MCP session id, host-agnostically. Prefers the Web Crypto `randomUUID` (a global,
|
|
13
|
+
* not `node:crypto`) where present, with a time+random fallback so a host without `crypto` never
|
|
14
|
+
* throws — mirroring how core treats Web Crypto as optional (see `mintRootRequestKey`). */
|
|
15
|
+
export declare function newSessionId(): string;
|
|
16
|
+
/** Bound the live-session map to `max` by evicting least-recently-used entries (the `Map`'s leading
|
|
17
|
+
* keys, given `touchSession` re-inserts on use), closing each victim best-effort so its transport /
|
|
18
|
+
* server releases. This is the categorical fix for the unbounded-growth vector: no client can pin
|
|
19
|
+
* more than `max` server/transport pairs, whatever its `initialize` cadence. Exported for a direct
|
|
20
|
+
* unit test of the LRU eviction contract. */
|
|
21
|
+
export declare function evictExcessSessions<S>(sessions: Map<string, S>, max: number, close: (session: S) => Promise<void>): Promise<void>;
|
|
22
|
+
/** The resolved MCP access policy for this app. */
|
|
23
|
+
interface McpConfig {
|
|
24
|
+
/** Whether the MCP surface answers at all. Defaults ON. */
|
|
25
|
+
readonly enabled: boolean;
|
|
26
|
+
/** Whether non-loopback callers are allowed. Defaults OFF (loopback-only). */
|
|
27
|
+
readonly allowRemote: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the MCP access policy from the manifest (`mcp: { enabled?, allowRemote? }`, read
|
|
31
|
+
* reflectively until the app schema folds the field in — mirroring how `readApiBinding` reads
|
|
32
|
+
* `api`) and the environment (`URBAN_MCP_ENABLED`, `URBAN_MCP_ALLOW_REMOTE`). The env flags win so
|
|
33
|
+
* an operator can force the surface off (or open it to a non-loopback bind) without editing the
|
|
34
|
+
* manifest. Enabled defaults ON and access defaults to loopback-only ("defaults ON for loopback").
|
|
35
|
+
*/
|
|
36
|
+
export declare function readMcpConfig(manifest: unknown, env: (name: string) => string | undefined): McpConfig;
|
|
37
|
+
/** Whether the request targets a loopback host — the best-effort loopback signal available at the
|
|
38
|
+
* host seam (which carries no peer address). A missing Host header is treated as loopback (a direct
|
|
39
|
+
* local client that sent none). */
|
|
40
|
+
export declare function isLoopbackRequest(req: HttpRequest): boolean;
|
|
41
|
+
/** The `required` keys a tool's `inputSchema` declares but the call omitted. Presence is decided
|
|
42
|
+
* solely by {@link isBlankArg} — the single source of truth also used for path args — so a value is
|
|
43
|
+
* "provided" unless it is absent (`undefined`/`null`) or a blank/whitespace-only string. A required
|
|
44
|
+
* non-string input (e.g. a numeric/boolean/object arg) that is present is therefore accepted, not
|
|
45
|
+
* wrongly reported missing. Derived from the schema's own `required` array so declaring a field
|
|
46
|
+
* required is enough to have missing (or whitespace-only) calls rejected rather than silently
|
|
47
|
+
* substituted with an empty string. */
|
|
48
|
+
export declare function missingRequiredArgs(inputSchema: Record<string, unknown>, args: Record<string, unknown>): string[];
|
|
49
|
+
/**
|
|
50
|
+
* Mount the runtime-served MCP surface at `/app/mcp` (ADR 0067). Returns a single `*`-method route
|
|
51
|
+
* (mounted unconditionally, like `/app/agent`); the handler bridges the host request/response seam
|
|
52
|
+
* to the SDK's Web-Standard Streamable-HTTP transport, maintaining one MCP session per
|
|
53
|
+
* `mcp-session-id`. The tool projection reuses the SAME OpenAPI enumeration and dispatch path as
|
|
54
|
+
* `mountApi`, so a tool call is equivalent to the corresponding HTTP call.
|
|
55
|
+
*/
|
|
56
|
+
export declare function mountMcp(ctx: RuntimeContext, app: AppApi, apiRoutes: Route[]): McpHandle;
|
|
57
|
+
export {};
|