ankka 0.10.0 → 0.12.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/README.md +1 -0
- package/dist/_proto/ankka/protocol/v1/agent_pb.d.ts +35 -0
- package/dist/_proto/ankka/protocol/v1/agent_pb.js +8 -1
- package/dist/_proto/ankka/protocol/v1/client_pb.d.ts +858 -2
- package/dist/_proto/ankka/protocol/v1/client_pb.js +184 -9
- package/dist/_proto/ankka/protocol/v1/consumer_pb.d.ts +10 -1
- package/dist/_proto/ankka/protocol/v1/consumer_pb.js +1 -1
- package/dist/_proto/ankka/protocol/v1/discovery_pb.d.ts +373 -0
- package/dist/_proto/ankka/protocol/v1/discovery_pb.js +81 -16
- package/dist/_proto/ankka/protocol/v1/endpoint_pb.d.ts +210 -0
- package/dist/_proto/ankka/protocol/v1/endpoint_pb.js +35 -5
- package/dist/_proto/ankka/protocol/v1/event_sourced_pb.d.ts +86 -0
- package/dist/_proto/ankka/protocol/v1/event_sourced_pb.js +11 -1
- package/dist/_proto/ankka/protocol/v1/payload_pb.d.ts +61 -0
- package/dist/_proto/ankka/protocol/v1/payload_pb.js +18 -1
- package/dist/_proto/ankka/protocol/v1/view_pb.d.ts +70 -1
- package/dist/_proto/ankka/protocol/v1/view_pb.js +11 -1
- package/dist/_proto/ankka/protocol/v1/wasm_pb.d.ts +12 -0
- package/dist/_proto/ankka/protocol/v1/wasm_pb.js +1 -1
- package/dist/_proto/ankka/protocol/v1/workflow_pb.d.ts +7 -0
- package/dist/_proto/ankka/protocol/v1/workflow_pb.js +1 -1
- package/dist/agent.d.ts +15 -1
- package/dist/agent.js +20 -0
- package/dist/approvals.d.ts +47 -0
- package/dist/approvals.js +57 -0
- package/dist/autonomous.d.ts +23 -1
- package/dist/autonomous.js +30 -1
- package/dist/client.d.ts +188 -7
- package/dist/client.js +490 -21
- package/dist/consumer.d.ts +42 -3
- package/dist/consumer.js +35 -1
- package/dist/context.d.ts +29 -1
- package/dist/context.js +5 -2
- package/dist/contract.d.ts +40 -0
- package/dist/contract.js +60 -0
- package/dist/effects/common.d.ts +7 -0
- package/dist/effects/common.js +9 -1
- package/dist/effects/eventSourced.d.ts +26 -1
- package/dist/effects/eventSourced.js +58 -5
- package/dist/effects/keyed.d.ts +33 -0
- package/dist/effects/keyed.js +41 -0
- package/dist/endpoint.d.ts +10 -1
- package/dist/endpoint.js +20 -0
- package/dist/eventSourcedEntity.d.ts +8 -3
- package/dist/graph.d.ts +24 -2
- package/dist/graph.js +27 -1
- package/dist/handlers.d.ts +10 -2
- package/dist/handlers.js +6 -2
- package/dist/index.d.ts +15 -6
- package/dist/index.js +12 -3
- package/dist/keyedView.d.ts +59 -0
- package/dist/keyedView.js +73 -0
- package/dist/kinds.js +2 -0
- package/dist/mcp.d.ts +57 -0
- package/dist/mcp.js +64 -0
- package/dist/routes.d.ts +44 -4
- package/dist/routes.js +43 -4
- package/dist/server/agent.js +12 -4
- package/dist/server/discovery.d.ts +17 -1
- package/dist/server/discovery.js +182 -1
- package/dist/server/eventSourced.d.ts +4 -1
- package/dist/server/eventSourced.js +24 -6
- package/dist/server/http.d.ts +2 -1
- package/dist/server/http.js +80 -4
- package/dist/server/stateless.js +42 -3
- package/dist/server/workflow.js +5 -1
- package/dist/service.d.ts +46 -2
- package/dist/service.js +235 -15
- package/dist/services.d.ts +137 -0
- package/dist/services.js +259 -0
- package/dist/socket.d.ts +15 -0
- package/dist/socket.js +25 -0
- package/dist/spec.d.ts +1 -1
- package/dist/spec.js +94 -15
- package/dist/standing.d.ts +18 -0
- package/dist/standing.js +26 -0
- package/dist/startFrom.d.ts +36 -0
- package/dist/startFrom.js +51 -0
- package/dist/testkit/index.d.ts +2 -2
- package/dist/testkit/index.js +1 -1
- package/dist/testkit/integration.js +5 -0
- package/dist/testkit/kinds.d.ts +50 -5
- package/dist/testkit/kinds.js +208 -35
- package/dist/testkit/unit.d.ts +41 -3
- package/dist/testkit/unit.js +87 -10
- package/dist/timedAction.d.ts +17 -2
- package/dist/timedAction.js +33 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/view.d.ts +48 -1
- package/dist/view.js +18 -1
- package/dist/workflow.d.ts +18 -1
- package/dist/workflow.js +39 -0
- package/package.json +3 -2
package/dist/endpoint.js
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
// static readonly routes = { getCart: get("/{cartId}", ShoppingCart, (ep: ShoppingCartEndpoint, req) => ep.getCart(req.params.cartId)) }
|
|
9
9
|
// getCart(cartId: string) { return this.client.of(ShoppingCartEntity, cartId).call(ShoppingCartEntity.handlers.getCart).invoke() }
|
|
10
10
|
// }
|
|
11
|
+
import { secretsFor } from "./client.js";
|
|
11
12
|
import { currentRequest, requestIfAny } from "./context.js";
|
|
13
|
+
import { servicesFor } from "./services.js";
|
|
12
14
|
export class Endpoint {
|
|
13
15
|
#client;
|
|
14
16
|
/** The request being handled: path parameters, query, headers, principal. Request-scoped. */
|
|
@@ -22,6 +24,24 @@ export class Endpoint {
|
|
|
22
24
|
const request = requestIfAny();
|
|
23
25
|
return request ? this.#client.withMetadata(request.metadata) : this.#client;
|
|
24
26
|
}
|
|
27
|
+
#secrets;
|
|
28
|
+
/** The service's secret store: values kept encrypted in the service's own database, never in a journal or a view. */
|
|
29
|
+
get secrets() {
|
|
30
|
+
return this.#secrets ?? secretsFor(this.client);
|
|
31
|
+
}
|
|
32
|
+
/** A unit test's store in place of the runtime's: `component.secrets = new InMemorySecrets()`. */
|
|
33
|
+
set secrets(store) {
|
|
34
|
+
this.#secrets = store;
|
|
35
|
+
}
|
|
36
|
+
#services;
|
|
37
|
+
/** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
|
|
38
|
+
get services() {
|
|
39
|
+
return this.#services ?? servicesFor(this.client);
|
|
40
|
+
}
|
|
41
|
+
/** A unit test's in place of the runtime's: `component.services = new ScriptedServices()`. */
|
|
42
|
+
set services(services) {
|
|
43
|
+
this.#services = services;
|
|
44
|
+
}
|
|
25
45
|
/** @internal */
|
|
26
46
|
_bindClient(client) {
|
|
27
47
|
this.#client = client;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Shape } from "./codec.ts";
|
|
2
|
-
import type { CommandContext } from "./context.ts";
|
|
2
|
+
import type { CommandContext, EventContext } from "./context.ts";
|
|
3
3
|
import type { HandlerTable } from "./handlers.ts";
|
|
4
4
|
import type { ComponentClient } from "./client.ts";
|
|
5
5
|
import { EventSourcedEffects } from "./effects/eventSourced.ts";
|
|
@@ -19,8 +19,11 @@ export declare abstract class EventSourcedEntity<S, E> {
|
|
|
19
19
|
get _kind(): "event-sourced";
|
|
20
20
|
/** The state of an instance with no events yet. `this.entityId` is available here. */
|
|
21
21
|
abstract emptyState(): S;
|
|
22
|
-
/**
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* The fold: the state after `event`, given the state before it. Must not read `this.state`. The third
|
|
24
|
+
* argument, which a two-argument fold may leave out, says where the event was written (1.19).
|
|
25
|
+
*/
|
|
26
|
+
abstract applyEvent(state: S, event: E, context?: EventContext): S;
|
|
24
27
|
/** @internal Bound by the runtime and the testkit before `emptyState`. */
|
|
25
28
|
_bindInstance(entityId: string): void;
|
|
26
29
|
/** @internal Bound by the runtime and the testkit for the duration of one handler call. */
|
|
@@ -37,4 +40,6 @@ export interface EventSourcedEntityClass<S = unknown, E = unknown, C extends Eve
|
|
|
37
40
|
readonly handlers: HandlerTable<C>;
|
|
38
41
|
/** Ask for a snapshot every N events; absent or 0: never. */
|
|
39
42
|
readonly snapshotEvery?: number;
|
|
43
|
+
/** Held at every installation in its replication set and written only at its home (1.19). */
|
|
44
|
+
readonly replicated?: boolean;
|
|
40
45
|
}
|
package/dist/graph.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import type { StartFrom } from "./startFrom.ts";
|
|
1
2
|
import { type Codec, type Shape } from "./codec.ts";
|
|
2
3
|
import type { EffectLike, Metadata } from "./effects/common.ts";
|
|
3
|
-
import type
|
|
4
|
+
import { type ComponentClient, type ComponentRef, type Secrets } from "./client.ts";
|
|
5
|
+
import { type Services } from "./services.ts";
|
|
6
|
+
import type { Standing } from "./standing.ts";
|
|
4
7
|
type MaybePromise<T> = T | Promise<T>;
|
|
5
8
|
/** The contract's name: the manifest and the `ce-type` of every delta. */
|
|
6
9
|
export declare const GRAPH_DELTA_SCHEMA = "ankka.graph-delta.v1";
|
|
@@ -134,24 +137,43 @@ export declare abstract class GraphConsumer<M> {
|
|
|
134
137
|
/** The change's metadata: `ce-subject` is the source instance's id, `ankka.sequence` its sequence number. */
|
|
135
138
|
get metadata(): Metadata;
|
|
136
139
|
get subject(): string;
|
|
140
|
+
/** Of a change from a workflow: where it stood once the effect that recorded the state was applied. `undefined` for a change from an entity or a topic. */
|
|
141
|
+
get standing(): Standing | undefined;
|
|
137
142
|
/** The change's sequence number, which is the version of every element that states none. */
|
|
138
143
|
get sequenceNumber(): bigint | undefined;
|
|
139
144
|
get client(): ComponentClient;
|
|
145
|
+
/** The service's secret store: values kept encrypted in the service's own database, never in a journal or a view. */
|
|
146
|
+
get secrets(): Secrets;
|
|
147
|
+
/** A unit test's store in place of the runtime's: `component.secrets = new InMemorySecrets()`. */
|
|
148
|
+
set secrets(store: Secrets);
|
|
149
|
+
/** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
|
|
150
|
+
get services(): Services;
|
|
151
|
+
/** A unit test's in place of the runtime's: `component.services = new ScriptedServices()`. */
|
|
152
|
+
set services(services: Services);
|
|
140
153
|
/** @internal */
|
|
141
154
|
get _kind(): "consumer";
|
|
142
155
|
abstract onMessage(message: M): MaybePromise<GraphEffect>;
|
|
143
156
|
/** The source was deleted. Ignored unless this says otherwise; publish tombstones here. */
|
|
144
157
|
onDelete(): MaybePromise<GraphEffect>;
|
|
145
158
|
/** @internal */
|
|
146
|
-
_bind(metadata: Metadata, client: ComponentClient): void;
|
|
159
|
+
_bind(metadata: Metadata, client: ComponentClient, standing?: Standing): void;
|
|
147
160
|
}
|
|
148
161
|
export interface GraphConsumerClass<M = unknown, C extends GraphConsumer<M> = GraphConsumer<M>> {
|
|
149
162
|
new (): C;
|
|
150
163
|
readonly componentId: string;
|
|
151
164
|
readonly source?: ComponentRef;
|
|
152
165
|
readonly topic?: string;
|
|
166
|
+
/** Where a topic source starts the first time its group reads the topic. */
|
|
167
|
+
readonly startFrom?: StartFrom;
|
|
168
|
+
/** Raised to read the topic again from the start position, under a group of its own. Absent is 1. */
|
|
169
|
+
readonly version?: number;
|
|
153
170
|
readonly message: Shape<M>;
|
|
154
171
|
/** The delta topic. It must be compacted to hold the graph; the pipeline that reads it creates it so. */
|
|
155
172
|
readonly producesTo: string;
|
|
173
|
+
/**
|
|
174
|
+
* Given a replicated entity's changes written at other installations too (1.19); otherwise each delta
|
|
175
|
+
* is published once, from the installation that wrote the change.
|
|
176
|
+
*/
|
|
177
|
+
readonly mirroredChanges?: boolean;
|
|
156
178
|
}
|
|
157
179
|
export {};
|
package/dist/graph.js
CHANGED
|
@@ -15,8 +15,10 @@
|
|
|
15
15
|
// }
|
|
16
16
|
// }
|
|
17
17
|
import { JSON_CONTENT } from "./codec.js";
|
|
18
|
+
import { secretsFor } from "./client.js";
|
|
18
19
|
import { sequenceNumberOf } from "./consumer.js";
|
|
19
20
|
import { renderDouble, reviver } from "./json.js";
|
|
21
|
+
import { servicesFor } from "./services.js";
|
|
20
22
|
/** The contract's name: the manifest and the `ce-type` of every delta. */
|
|
21
23
|
export const GRAPH_DELTA_SCHEMA = "ankka.graph-delta.v1";
|
|
22
24
|
/** The record key of every delta for the node `id`. */
|
|
@@ -393,6 +395,7 @@ export class GraphConsumer {
|
|
|
393
395
|
graph = new Graph();
|
|
394
396
|
effects = new GraphEffects();
|
|
395
397
|
#metadata = {};
|
|
398
|
+
#standing = undefined;
|
|
396
399
|
#client;
|
|
397
400
|
/** The change's metadata: `ce-subject` is the source instance's id, `ankka.sequence` its sequence number. */
|
|
398
401
|
get metadata() {
|
|
@@ -401,6 +404,10 @@ export class GraphConsumer {
|
|
|
401
404
|
get subject() {
|
|
402
405
|
return this.#metadata["ce-subject"] ?? "";
|
|
403
406
|
}
|
|
407
|
+
/** Of a change from a workflow: where it stood once the effect that recorded the state was applied. `undefined` for a change from an entity or a topic. */
|
|
408
|
+
get standing() {
|
|
409
|
+
return this.#standing;
|
|
410
|
+
}
|
|
404
411
|
/** The change's sequence number, which is the version of every element that states none. */
|
|
405
412
|
get sequenceNumber() {
|
|
406
413
|
return sequenceNumberOf(this.#metadata);
|
|
@@ -410,6 +417,24 @@ export class GraphConsumer {
|
|
|
410
417
|
throw new Error("client is only available inside onMessage or onDelete");
|
|
411
418
|
return this.#client;
|
|
412
419
|
}
|
|
420
|
+
#secrets;
|
|
421
|
+
/** The service's secret store: values kept encrypted in the service's own database, never in a journal or a view. */
|
|
422
|
+
get secrets() {
|
|
423
|
+
return this.#secrets ?? secretsFor(this.client);
|
|
424
|
+
}
|
|
425
|
+
/** A unit test's store in place of the runtime's: `component.secrets = new InMemorySecrets()`. */
|
|
426
|
+
set secrets(store) {
|
|
427
|
+
this.#secrets = store;
|
|
428
|
+
}
|
|
429
|
+
#services;
|
|
430
|
+
/** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
|
|
431
|
+
get services() {
|
|
432
|
+
return this.#services ?? servicesFor(this.client);
|
|
433
|
+
}
|
|
434
|
+
/** A unit test's in place of the runtime's: `component.services = new ScriptedServices()`. */
|
|
435
|
+
set services(services) {
|
|
436
|
+
this.#services = services;
|
|
437
|
+
}
|
|
413
438
|
/** @internal */
|
|
414
439
|
get _kind() {
|
|
415
440
|
return "consumer";
|
|
@@ -419,8 +444,9 @@ export class GraphConsumer {
|
|
|
419
444
|
return this.effects.ignore();
|
|
420
445
|
}
|
|
421
446
|
/** @internal */
|
|
422
|
-
_bind(metadata, client) {
|
|
447
|
+
_bind(metadata, client, standing) {
|
|
423
448
|
this.#metadata = metadata;
|
|
449
|
+
this.#standing = standing;
|
|
424
450
|
this.#client = client;
|
|
425
451
|
}
|
|
426
452
|
}
|
package/dist/handlers.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Approval } from "./approvals.ts";
|
|
1
2
|
import type { Shape } from "./codec.ts";
|
|
2
3
|
import type { EffectLike, ReadOnlyLike } from "./effects/common.ts";
|
|
3
4
|
export type HandlerKind = "command" | "query" | "stream" | "step" | "action";
|
|
@@ -35,14 +36,21 @@ export declare function step<C, E extends EffectLike>(name: string, run: (self:
|
|
|
35
36
|
/** A timed action: `action("remind", s.string, (t, id) => t.remind(id))`. */
|
|
36
37
|
export declare function action<C, I, E extends EffectLike>(name: string, input: Shape<I>, run: (self: C, input: I) => MaybePromise<E>): HandlerRef<C, I, undefined, E>;
|
|
37
38
|
export declare function action<C, E extends EffectLike>(name: string, run: (self: C) => MaybePromise<E>): HandlerRef<C, undefined, undefined, E>;
|
|
38
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* An agent tool: the model sees `description` and the JSON Schema of `input`; the process runs `run`.
|
|
41
|
+
* With `approval`, the tool waits for a person's decision before it runs: the caller is answered with an
|
|
42
|
+
* approval request, and the turn goes on when someone decides it.
|
|
43
|
+
*/
|
|
39
44
|
export interface ToolRef<C = unknown, I = unknown> {
|
|
40
45
|
readonly name: string;
|
|
41
46
|
readonly description: string;
|
|
42
47
|
readonly input: Shape<I>;
|
|
43
48
|
readonly run: (self: C, input: I) => MaybePromise<unknown>;
|
|
49
|
+
readonly approval?: Approval;
|
|
44
50
|
}
|
|
45
|
-
export declare function tool<C, I>(name: string, description: string, input: Shape<I>, run: (self: C, input: I) => MaybePromise<unknown
|
|
51
|
+
export declare function tool<C, I>(name: string, description: string, input: Shape<I>, run: (self: C, input: I) => MaybePromise<unknown>, options?: {
|
|
52
|
+
readonly approval?: Approval;
|
|
53
|
+
}): ToolRef<C, I>;
|
|
46
54
|
export type GuardrailStage = "input" | "output";
|
|
47
55
|
/** An agent guardrail: returns a reason to block, or `null` to let the text through. */
|
|
48
56
|
export interface GuardrailRef {
|
package/dist/handlers.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// and `guardrails` tables. Each carries the wire name, the input and reply schemas, and the function
|
|
3
3
|
// that runs it. The wire name is declared here and nowhere else; renaming the method it calls changes
|
|
4
4
|
// nothing on the wire. These are plain values, so the registry can read them without reflection.
|
|
5
|
+
import { approvalProblem } from "./approvals.js";
|
|
5
6
|
function ref(kind, name, input, reply, run, flags) {
|
|
6
7
|
if (typeof name !== "string" || name.trim() === "")
|
|
7
8
|
throw new TypeError(`a ${kind} needs a wire name`);
|
|
@@ -49,13 +50,16 @@ export function action(name, ...rest) {
|
|
|
49
50
|
const [run] = rest;
|
|
50
51
|
return ref("action", name, undefined, undefined, run, { readOnly: false, streaming: false });
|
|
51
52
|
}
|
|
52
|
-
export function tool(name, description, input, run) {
|
|
53
|
+
export function tool(name, description, input, run, options = {}) {
|
|
53
54
|
if (typeof name !== "string" || name.trim() === "")
|
|
54
55
|
throw new TypeError("a tool needs a name");
|
|
55
56
|
if (typeof description !== "string" || description.trim() === "") {
|
|
56
57
|
throw new TypeError(`tool ${name}: a tool needs a description, or the model cannot choose it`);
|
|
57
58
|
}
|
|
58
|
-
|
|
59
|
+
const problem = approvalProblem(options.approval);
|
|
60
|
+
if (problem)
|
|
61
|
+
throw new TypeError(`tool ${name}: ${problem}`);
|
|
62
|
+
return Object.freeze({ name, description, input, run, ...(options.approval !== undefined ? { approval: options.approval } : {}) });
|
|
59
63
|
}
|
|
60
64
|
export function guardrail(name, check) {
|
|
61
65
|
if (typeof name !== "string" || name.trim() === "")
|
package/dist/index.d.ts
CHANGED
|
@@ -5,27 +5,36 @@ export { EncodingError, DecodingError, renderDouble } from "./json.ts";
|
|
|
5
5
|
export { Instant, Duration, LocalDate, LocalDateTime } from "./time.ts";
|
|
6
6
|
export { ErrorCode, CommandError, Outcome, Retention, httpStatusOf, type ErrorDetail, type Metadata, type EffectLike, type ReadOnlyLike } from "./effects/common.ts";
|
|
7
7
|
export { command, query, stream, step, action, tool, guardrail, type HandlerRef, type ToolRef, type GuardrailRef, type GuardrailStage, type HandlerTable } from "./handlers.ts";
|
|
8
|
-
export { PersistBuilder, EventSourcedEffects, type PersistEffect, type ReadOnlyEffect, type EventSourcedEffect } from "./effects/eventSourced.ts";
|
|
8
|
+
export { PersistBuilder, EventSourcedEffects, type PersistEffect, type ReadOnlyEffect, type EventSourcedEffect, type ReplicationChange } from "./effects/eventSourced.ts";
|
|
9
9
|
export { EventSourcedEntity, type EventSourcedEntityClass } from "./eventSourcedEntity.ts";
|
|
10
10
|
export { UpdateBuilder, KeyValueEffects, type UpdateEffect, type KeyValueReadOnlyEffect, type KeyValueEffect } from "./effects/keyValue.ts";
|
|
11
11
|
export { KeyValueEntity, type KeyValueEntityClass } from "./keyValueEntity.ts";
|
|
12
12
|
export { WorkflowEffects, StepEffects, WorkflowUpdateBuilder, StepUpdateBuilder, workflowSettings, type WorkflowEffect, type WorkflowReadOnlyEffect, type WorkflowCommandEffect, type StepEffect, type StepOutcome, type StepRef, type PauseOptions, type WorkflowSettings, type StepSettings, type Recovery, } from "./effects/workflow.ts";
|
|
13
13
|
export { Workflow, type WorkflowClass } from "./workflow.ts";
|
|
14
14
|
export { ViewEffects, ConsumerEffects, TimedActionEffects, type ViewEffect, type ConsumerEffect, type OutgoingMessage, type TimedActionEffect } from "./effects/stateless.ts";
|
|
15
|
-
export { View, type ViewClass } from "./view.ts";
|
|
15
|
+
export { View, declaredQuery, tableOf, type DeclaredQuery, type ViewClass } from "./view.ts";
|
|
16
|
+
export { KeyedView, on, type KeyedSource, type KeyedViewClass, type ViewRows } from "./keyedView.ts";
|
|
17
|
+
export { KeyedViewEffects, type KeyedViewEffect, type RowChange } from "./effects/keyed.ts";
|
|
16
18
|
export { Consumer, ProtocolVersionError, type ConsumerClass } from "./consumer.ts";
|
|
19
|
+
export { Contract, CONTRACT_NAME_RULE, fingerprintOf, type Publication } from "./contract.ts";
|
|
17
20
|
export { GRAPH_DELTA_SCHEMA, Graph, GraphConsumer, GraphEffects, GraphError, edgeKey, elementKey, graphDeltaCodec, nodeKey, readDelta, type Delta, type Element, type GraphConsumerClass, type GraphEffect, type GraphFault, type Properties, type PropertyValue, type Scalar, type Version, } from "./graph.ts";
|
|
18
21
|
export { TimedAction, type TimedActionClass } from "./timedAction.ts";
|
|
19
22
|
export { AgentEffects, type AgentEffect } from "./effects/agent.ts";
|
|
20
23
|
export { Agent, type AgentClass } from "./agent.ts";
|
|
24
|
+
export { ApprovalAwaited, APPROVALS_SINCE, type AgentOutcome, type Approval, type ApprovalRequest, type DecisionInput } from "./approvals.ts";
|
|
25
|
+
export { mcpServer, resultGuardrail, toolName, type McpServerOptions, type McpServerRef, type ResultGuardrailRef } from "./mcp.ts";
|
|
21
26
|
export { AutonomousAgent, AutonomousAgentCalls, TaskCalls, Tasks, accepted, rejected, taskAcceptance, taskRule, taskType, type Accepted, type AgentState, type Attachment, type AutonomousAgentClass, type AutonomousSettings, type Notification, type Rejected, type TaskAcceptance, type TaskRule, type TaskSnapshot, type TaskType, } from "./autonomous.ts";
|
|
22
27
|
export { Endpoint, type EndpointClass } from "./endpoint.ts";
|
|
23
|
-
export {
|
|
24
|
-
export { type
|
|
25
|
-
export {
|
|
26
|
-
export {
|
|
28
|
+
export { type Socket, SocketClosed } from "./socket.ts";
|
|
29
|
+
export { Acl, Callers, type CallerMatcher, type CallersAcl, HttpProblem, get, post, put, patch, del, sse, sseEvent, sseEvents, type SseEvent, socket, type RouteRef, type RouteOptions, type RouteTable, type Params, type ParamNames, type HttpMethod } from "./routes.ts";
|
|
30
|
+
export { type CommandContext, type EventContext, type RequestContext, type Principal, type Caller, Query, Headers } from "./context.ts";
|
|
31
|
+
export { ComponentClient, Calls, TypedCalls, Invocation, AwaitingInvocation, AWAIT_SINCE, awaitPartJson, Views, Timers, Secrets, InMemorySecrets, noSecrets, WatchEnded, VIEW_STREAMS_SINCE, type AwaitPart, type WatchEvent, type WatchOptions, type Overflow, type TimerTarget, type TimedActionTarget, type ComponentRef } from "./client.ts";
|
|
32
|
+
export { Services, ServiceClient, ScriptedServices, noServices, ServiceError, ServiceUnresolvable, ServiceIdentityMismatch, ServiceUnanswered, ServiceCallFailed, type ServiceResponse, type ServiceRequest, type ServiceHeaders, } from "./services.ts";
|
|
33
|
+
export { Ankka, ServiceBuilder, Registry, RegistrationError, type ServiceOptions, type RegisteredComponent, type RegisteredEndpoint, type Source, type RegisteredEventSourced, type RegisteredKeyValue, type RegisteredWorkflow, type RegisteredView, type RegisteredKeyedView, type RegisteredKeyedSource, type RegisteredConsumer, type RegisteredTimedAction, type RegisteredAgent, } from "./service.ts";
|
|
27
34
|
export { Server, type ServerOptions } from "./server/server.ts";
|
|
28
35
|
export { problems as sidecarProblems } from "./server/discovery.ts";
|
|
29
36
|
export { PROTOCOL_VERSION, SDK_NAME } from "./spec.ts";
|
|
37
|
+
export { StartFrom } from "./startFrom.ts";
|
|
38
|
+
export { isTerminal, isUnknown, type Standing, type StandingStatus } from "./standing.ts";
|
|
30
39
|
export { materialiseEventSourced, materialiseKeyValue, materialiseWorkflowCommand, materialiseStep, type Materialised, type MaterialisedKeyValue, type MaterialisedWorkflowCommand, type MaterialisedStep, } from "./materialise.ts";
|
|
31
40
|
export type { ComponentKind } from "./kinds.ts";
|
package/dist/index.js
CHANGED
|
@@ -23,19 +23,28 @@ export { KeyValueEntity } from "./keyValueEntity.js";
|
|
|
23
23
|
export { WorkflowEffects, StepEffects, WorkflowUpdateBuilder, StepUpdateBuilder, workflowSettings, } from "./effects/workflow.js";
|
|
24
24
|
export { Workflow } from "./workflow.js";
|
|
25
25
|
export { ViewEffects, ConsumerEffects, TimedActionEffects } from "./effects/stateless.js";
|
|
26
|
-
export { View } from "./view.js";
|
|
26
|
+
export { View, declaredQuery, tableOf } from "./view.js";
|
|
27
|
+
export { KeyedView, on } from "./keyedView.js";
|
|
28
|
+
export { KeyedViewEffects } from "./effects/keyed.js";
|
|
27
29
|
export { Consumer, ProtocolVersionError } from "./consumer.js";
|
|
30
|
+
export { Contract, CONTRACT_NAME_RULE, fingerprintOf } from "./contract.js";
|
|
28
31
|
export { GRAPH_DELTA_SCHEMA, Graph, GraphConsumer, GraphEffects, GraphError, edgeKey, elementKey, graphDeltaCodec, nodeKey, readDelta, } from "./graph.js";
|
|
29
32
|
export { TimedAction } from "./timedAction.js";
|
|
30
33
|
export { AgentEffects } from "./effects/agent.js";
|
|
31
34
|
export { Agent } from "./agent.js";
|
|
35
|
+
export { ApprovalAwaited, APPROVALS_SINCE } from "./approvals.js";
|
|
36
|
+
export { mcpServer, resultGuardrail, toolName } from "./mcp.js";
|
|
32
37
|
export { AutonomousAgent, AutonomousAgentCalls, TaskCalls, Tasks, accepted, rejected, taskAcceptance, taskRule, taskType, } from "./autonomous.js";
|
|
33
38
|
export { Endpoint } from "./endpoint.js";
|
|
34
|
-
export {
|
|
39
|
+
export { SocketClosed } from "./socket.js";
|
|
40
|
+
export { Acl, Callers, HttpProblem, get, post, put, patch, del, sse, sseEvent, sseEvents, socket } from "./routes.js";
|
|
35
41
|
export { Query, Headers } from "./context.js";
|
|
36
|
-
export { ComponentClient, Calls, TypedCalls, Invocation, Views, Timers } from "./client.js";
|
|
42
|
+
export { ComponentClient, Calls, TypedCalls, Invocation, AwaitingInvocation, AWAIT_SINCE, awaitPartJson, Views, Timers, Secrets, InMemorySecrets, noSecrets, WatchEnded, VIEW_STREAMS_SINCE } from "./client.js";
|
|
43
|
+
export { Services, ServiceClient, ScriptedServices, noServices, ServiceError, ServiceUnresolvable, ServiceIdentityMismatch, ServiceUnanswered, ServiceCallFailed, } from "./services.js";
|
|
37
44
|
export { Ankka, ServiceBuilder, Registry, RegistrationError, } from "./service.js";
|
|
38
45
|
export { Server } from "./server/server.js";
|
|
39
46
|
export { problems as sidecarProblems } from "./server/discovery.js";
|
|
40
47
|
export { PROTOCOL_VERSION, SDK_NAME } from "./spec.js";
|
|
48
|
+
export { StartFrom } from "./startFrom.js";
|
|
49
|
+
export { isTerminal, isUnknown } from "./standing.js";
|
|
41
50
|
export { materialiseEventSourced, materialiseKeyValue, materialiseWorkflowCommand, materialiseStep, } from "./materialise.js";
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Shape } from "./codec.ts";
|
|
2
|
+
import type { Metadata } from "./effects/common.ts";
|
|
3
|
+
import type { ComponentClient, ComponentRef } from "./client.ts";
|
|
4
|
+
import { KeyedViewEffects, type KeyedViewEffect } from "./effects/keyed.ts";
|
|
5
|
+
import type { DeclaredQuery } from "./view.ts";
|
|
6
|
+
import type { Standing } from "./standing.ts";
|
|
7
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
8
|
+
/** A keyed view's own rows, for the length of one change. It reaches no other view. */
|
|
9
|
+
export interface ViewRows<Row> {
|
|
10
|
+
/** The row under `key`, or `null`. */
|
|
11
|
+
get(key: string): Promise<Row | null>;
|
|
12
|
+
/** The rows of one of this view's declared queries, asked with its values. */
|
|
13
|
+
ask(name: string, values?: Readonly<Record<string, string>>): Promise<Row[]>;
|
|
14
|
+
}
|
|
15
|
+
/** One source of a keyed view, and what the view does with each of its changes and with its entity's deletion. */
|
|
16
|
+
export interface KeyedSource<V = any, E = any> {
|
|
17
|
+
readonly source: ComponentRef;
|
|
18
|
+
readonly events: Shape<E>;
|
|
19
|
+
readonly onChange: (view: V, event: E) => MaybePromise<KeyedViewEffect<any>>;
|
|
20
|
+
readonly deleted?: (view: V) => MaybePromise<KeyedViewEffect<any>>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Declares a source of a keyed view: the entity whose changes it reads, the shape of those changes, and
|
|
24
|
+
* the handler for each. When the entity is deleted the view does nothing unless `deleted` names rows.
|
|
25
|
+
*/
|
|
26
|
+
export declare function on<V, E>(source: ComponentRef, events: Shape<E>, onChange: (view: V, event: E) => MaybePromise<KeyedViewEffect<any>>, options?: {
|
|
27
|
+
readonly deleted?: (view: V) => MaybePromise<KeyedViewEffect<any>>;
|
|
28
|
+
}): KeyedSource<V, E>;
|
|
29
|
+
export declare abstract class KeyedView<Row> {
|
|
30
|
+
#private;
|
|
31
|
+
readonly effects: KeyedViewEffects<Row>;
|
|
32
|
+
/** The change's metadata: `ce-subject` is the source entity's id, `ankka.sequence` its sequence number. */
|
|
33
|
+
get metadata(): Metadata;
|
|
34
|
+
/** The id of the entity the change came from. */
|
|
35
|
+
get subject(): string;
|
|
36
|
+
/** Of a change from a workflow: where it stood once the effect that recorded the state was applied. `undefined` for a change from an entity or a topic. */
|
|
37
|
+
get standing(): Standing | undefined;
|
|
38
|
+
/** This view's own rows, by key or by one of its declared queries. */
|
|
39
|
+
get rows(): ViewRows<Row>;
|
|
40
|
+
get client(): ComponentClient;
|
|
41
|
+
/** @internal */
|
|
42
|
+
get _kind(): "view";
|
|
43
|
+
/** @internal */
|
|
44
|
+
_bind(metadata: Metadata, client: ComponentClient | undefined, rows: ViewRows<Row>, standing?: Standing): void;
|
|
45
|
+
}
|
|
46
|
+
export interface KeyedViewClass<Row = unknown, C extends KeyedView<Row> = KeyedView<Row>> {
|
|
47
|
+
new (): C;
|
|
48
|
+
readonly componentId: string;
|
|
49
|
+
readonly row: Shape<Row>;
|
|
50
|
+
/** One per entity the view reads, declared with `on(...)`. */
|
|
51
|
+
readonly sources: readonly KeyedSource<C, any>[];
|
|
52
|
+
/** Raised to have the view emptied and every source read again from its beginning. Absent is 1. */
|
|
53
|
+
readonly version?: number;
|
|
54
|
+
/** The queries the view can be asked by name, by its own handlers among others. */
|
|
55
|
+
readonly declared?: readonly DeclaredQuery[];
|
|
56
|
+
}
|
|
57
|
+
/** The rows a keyed view reads through the component client, on its own id. */
|
|
58
|
+
export declare function clientRows<Row>(client: ComponentClient, viewId: string, row: Shape<Row>): ViewRows<Row>;
|
|
59
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// A keyed view: several entities' changes, each through a handler of its own, written to whichever rows
|
|
2
|
+
// a change is about, by key. The sidecar runs one projection per source and handles one change at a
|
|
3
|
+
// time across all of them; this process only says which rows each change writes.
|
|
4
|
+
//
|
|
5
|
+
// export class Shipments extends KeyedView<ShipmentRow> {
|
|
6
|
+
// static readonly componentId = "shipments"
|
|
7
|
+
// static readonly row = ShipmentRowShape
|
|
8
|
+
// static readonly sources = [
|
|
9
|
+
// on(ShipmentEntity, ShipmentEvents, (v: Shipments, e) => v.onShipment(e)),
|
|
10
|
+
// on(CustomerEntity, CustomerEvents, (v: Shipments, e) => v.onCustomer(e)),
|
|
11
|
+
// ]
|
|
12
|
+
// static readonly declared = [declaredQuery("of-customer", `SELECT payload FROM ${tableOf("shipments")} WHERE ...`)]
|
|
13
|
+
// async onCustomer(event: CustomerEvent) {
|
|
14
|
+
// const theirs = await this.rows.ask("of-customer", { customer: this.subject })
|
|
15
|
+
// return this.effects.updateRows(theirs.map((r) => [r.shipmentId, { ...r, name: event.name }]))
|
|
16
|
+
// }
|
|
17
|
+
// }
|
|
18
|
+
import { KeyedViewEffects } from "./effects/keyed.js";
|
|
19
|
+
/**
|
|
20
|
+
* Declares a source of a keyed view: the entity whose changes it reads, the shape of those changes, and
|
|
21
|
+
* the handler for each. When the entity is deleted the view does nothing unless `deleted` names rows.
|
|
22
|
+
*/
|
|
23
|
+
export function on(source, events, onChange, options = {}) {
|
|
24
|
+
return Object.freeze({ source, events, onChange, ...(options.deleted ? { deleted: options.deleted } : {}) });
|
|
25
|
+
}
|
|
26
|
+
export class KeyedView {
|
|
27
|
+
effects = new KeyedViewEffects();
|
|
28
|
+
#metadata = {};
|
|
29
|
+
#standing = undefined;
|
|
30
|
+
#client;
|
|
31
|
+
#rows;
|
|
32
|
+
/** The change's metadata: `ce-subject` is the source entity's id, `ankka.sequence` its sequence number. */
|
|
33
|
+
get metadata() {
|
|
34
|
+
return this.#metadata;
|
|
35
|
+
}
|
|
36
|
+
/** The id of the entity the change came from. */
|
|
37
|
+
get subject() {
|
|
38
|
+
return this.#metadata["ce-subject"] ?? "";
|
|
39
|
+
}
|
|
40
|
+
/** Of a change from a workflow: where it stood once the effect that recorded the state was applied. `undefined` for a change from an entity or a topic. */
|
|
41
|
+
get standing() {
|
|
42
|
+
return this.#standing;
|
|
43
|
+
}
|
|
44
|
+
/** This view's own rows, by key or by one of its declared queries. */
|
|
45
|
+
get rows() {
|
|
46
|
+
if (!this.#rows)
|
|
47
|
+
throw new Error("rows are only available inside a keyed view's handler");
|
|
48
|
+
return this.#rows;
|
|
49
|
+
}
|
|
50
|
+
get client() {
|
|
51
|
+
if (!this.#client)
|
|
52
|
+
throw new Error("client is only available inside a keyed view's handler");
|
|
53
|
+
return this.#client;
|
|
54
|
+
}
|
|
55
|
+
/** @internal */
|
|
56
|
+
get _kind() {
|
|
57
|
+
return "view";
|
|
58
|
+
}
|
|
59
|
+
/** @internal */
|
|
60
|
+
_bind(metadata, client, rows, standing) {
|
|
61
|
+
this.#metadata = metadata;
|
|
62
|
+
this.#standing = standing;
|
|
63
|
+
this.#client = client;
|
|
64
|
+
this.#rows = rows;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** The rows a keyed view reads through the component client, on its own id. */
|
|
68
|
+
export function clientRows(client, viewId, row) {
|
|
69
|
+
return {
|
|
70
|
+
get: (key) => client.views.get(viewId, key, row),
|
|
71
|
+
ask: (name, values = {}) => client.views.ask(viewId, name, values, row),
|
|
72
|
+
};
|
|
73
|
+
}
|
package/dist/kinds.js
CHANGED
|
@@ -30,6 +30,8 @@ const CODE_TO_PROTO = Object.freeze({
|
|
|
30
30
|
CONFLICT: ProtoErrorCode.CONFLICT,
|
|
31
31
|
TIMEOUT: ProtoErrorCode.TIMEOUT,
|
|
32
32
|
UNAVAILABLE: ProtoErrorCode.UNAVAILABLE,
|
|
33
|
+
WORKFLOW_FAILED: ProtoErrorCode.WORKFLOW_FAILED,
|
|
34
|
+
NOT_HOME: ProtoErrorCode.NOT_HOME,
|
|
33
35
|
});
|
|
34
36
|
export function errorCodeToProto(code) {
|
|
35
37
|
return CODE_TO_PROTO[code];
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type Approval } from "./approvals.ts";
|
|
2
|
+
export declare const VARIABLE_PREFIX = "ANKKA_MCP_";
|
|
3
|
+
export declare const TOOL_PREFIX = "mcp__";
|
|
4
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
5
|
+
export interface McpServerOptions {
|
|
6
|
+
/** Where the server is; `ANKKA_MCP_<NAME>_URL` replaces it, and is required when neither this nor `service` is given. */
|
|
7
|
+
readonly url?: string;
|
|
8
|
+
/** An ankka service, called as one, at `path` (by default `/mcp`), in `project` (by default this one). */
|
|
9
|
+
readonly service?: string;
|
|
10
|
+
readonly project?: string;
|
|
11
|
+
readonly path?: string;
|
|
12
|
+
/** A header's name and the `ANKKA_MCP_` variable its value is read from. */
|
|
13
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
14
|
+
/** Every tool of the server waits for a person. */
|
|
15
|
+
readonly approval?: Approval;
|
|
16
|
+
}
|
|
17
|
+
export interface McpServerRef extends McpServerOptions {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
}
|
|
20
|
+
/** An MCP server the agent lists under `name`, lower-case letters, digits and hyphens. */
|
|
21
|
+
export declare function mcpServer(name: string, options?: McpServerOptions): McpServerRef;
|
|
22
|
+
/** Checks what an MCP server's tool answered before the model is told it: a reason to withhold it, or `null`. */
|
|
23
|
+
export interface ResultGuardrailRef {
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly check: (tool: string, text: string) => MaybePromise<string | null>;
|
|
26
|
+
}
|
|
27
|
+
export declare function resultGuardrail(name: string, check: (tool: string, text: string) => MaybePromise<string | null>): ResultGuardrailRef;
|
|
28
|
+
/** The name a server's tool is offered to the model under. */
|
|
29
|
+
export declare function toolName(server: string, tool: string): string;
|
|
30
|
+
/** What is wrong with one server, in the sidecar's words. */
|
|
31
|
+
export declare function serverProblems(server: McpServerRef): string[];
|
|
32
|
+
/** The discovery form of a server. */
|
|
33
|
+
export declare function serverToProto(server: McpServerRef): {
|
|
34
|
+
name: string;
|
|
35
|
+
address: {
|
|
36
|
+
case: "url";
|
|
37
|
+
value: string;
|
|
38
|
+
} | {
|
|
39
|
+
case: "service";
|
|
40
|
+
value: {
|
|
41
|
+
name: string;
|
|
42
|
+
path: string;
|
|
43
|
+
project?: string | undefined;
|
|
44
|
+
};
|
|
45
|
+
} | {
|
|
46
|
+
value?: undefined;
|
|
47
|
+
case: undefined;
|
|
48
|
+
};
|
|
49
|
+
headers: {
|
|
50
|
+
name: string;
|
|
51
|
+
variable: string;
|
|
52
|
+
}[];
|
|
53
|
+
approval?: {
|
|
54
|
+
withinMillis?: bigint;
|
|
55
|
+
} | undefined;
|
|
56
|
+
};
|
|
57
|
+
export {};
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// MCP servers an agent offers the model, and the guardrails that check what they answer.
|
|
2
|
+
//
|
|
3
|
+
// The sidecar connects to each server when it starts, reads its tools and offers them to the model as
|
|
4
|
+
// `mcp__<server>__<tool>`. This process is never asked to run one, and never sees a credential: a
|
|
5
|
+
// header's value is read from a variable on the sidecar, and only `ANKKA_MCP_` variables reach it. What a
|
|
6
|
+
// server's tool answers is put through the agent's result guardrails, which this process answers,
|
|
7
|
+
// before the model is told it.
|
|
8
|
+
//
|
|
9
|
+
// static readonly mcpServers = { tickets: mcpServer("tickets", { headers: { Authorization: "ANKKA_MCP_TICKETS_TOKEN" } }) }
|
|
10
|
+
// static readonly resultGuardrails = { noInstructions: resultGuardrail("no-instructions", (tool, text) => null) }
|
|
11
|
+
import { approvalProblem, approvalToProto } from "./approvals.js";
|
|
12
|
+
export const VARIABLE_PREFIX = "ANKKA_MCP_";
|
|
13
|
+
export const TOOL_PREFIX = "mcp__";
|
|
14
|
+
const NAME = /^[a-z0-9-]+$/;
|
|
15
|
+
/** An MCP server the agent lists under `name`, lower-case letters, digits and hyphens. */
|
|
16
|
+
export function mcpServer(name, options = {}) {
|
|
17
|
+
return Object.freeze({ ...options, name });
|
|
18
|
+
}
|
|
19
|
+
export function resultGuardrail(name, check) {
|
|
20
|
+
if (typeof name !== "string" || name.trim() === "")
|
|
21
|
+
throw new TypeError("a result guardrail needs a name");
|
|
22
|
+
return Object.freeze({ name, check });
|
|
23
|
+
}
|
|
24
|
+
/** The name a server's tool is offered to the model under. */
|
|
25
|
+
export function toolName(server, tool) {
|
|
26
|
+
return `${TOOL_PREFIX}${server}__${tool}`;
|
|
27
|
+
}
|
|
28
|
+
/** What is wrong with one server, in the sidecar's words. */
|
|
29
|
+
export function serverProblems(server) {
|
|
30
|
+
const found = [];
|
|
31
|
+
const name = server.name;
|
|
32
|
+
if (!NAME.test(name))
|
|
33
|
+
found.push(`an MCP server's name is lower-case letters, digits and hyphens ([a-z0-9-]), not '${name}'`);
|
|
34
|
+
if (server.url !== undefined && server.service !== undefined)
|
|
35
|
+
found.push(`MCP server '${name}' has both a URL and a service; give one`);
|
|
36
|
+
if (server.url !== undefined && server.url.trim() === "")
|
|
37
|
+
found.push(`MCP server '${name}' needs a URL`);
|
|
38
|
+
for (const [header, variable] of Object.entries(server.headers ?? {})) {
|
|
39
|
+
if (header.trim() === "")
|
|
40
|
+
found.push(`MCP server '${name}' has a header with no name`);
|
|
41
|
+
else if (!variable.startsWith(VARIABLE_PREFIX)) {
|
|
42
|
+
found.push(`MCP server '${name}': the header '${header}' takes its value from '${variable}', which must start ${VARIABLE_PREFIX} — only those variables reach the platform's program, which is what connects to the server`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const approval = approvalProblem(server.approval);
|
|
46
|
+
if (approval)
|
|
47
|
+
found.push(`MCP server '${name}': ${approval}`);
|
|
48
|
+
return found;
|
|
49
|
+
}
|
|
50
|
+
/** The discovery form of a server. */
|
|
51
|
+
export function serverToProto(server) {
|
|
52
|
+
const approval = approvalToProto(server.approval);
|
|
53
|
+
const address = server.url !== undefined
|
|
54
|
+
? { case: "url", value: server.url }
|
|
55
|
+
: server.service !== undefined
|
|
56
|
+
? { case: "service", value: { name: server.service, path: server.path ?? "/mcp", ...(server.project !== undefined ? { project: server.project } : {}) } }
|
|
57
|
+
: { case: undefined };
|
|
58
|
+
return {
|
|
59
|
+
name: server.name,
|
|
60
|
+
address,
|
|
61
|
+
headers: Object.entries(server.headers ?? {}).map(([name, variable]) => ({ name, variable })),
|
|
62
|
+
...(approval !== undefined ? { approval } : {}),
|
|
63
|
+
};
|
|
64
|
+
}
|