ankka 0.12.0 → 0.14.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/_proto/ankka/protocol/v1/client_pb.d.ts +594 -0
- package/dist/_proto/ankka/protocol/v1/client_pb.js +172 -30
- package/dist/_proto/ankka/protocol/v1/consumer_pb.js +1 -1
- package/dist/_proto/ankka/protocol/v1/discovery_pb.d.ts +134 -0
- package/dist/_proto/ankka/protocol/v1/discovery_pb.js +51 -36
- package/dist/_proto/ankka/protocol/v1/event_sourced_pb.d.ts +24 -0
- package/dist/_proto/ankka/protocol/v1/event_sourced_pb.js +1 -1
- package/dist/_proto/ankka/protocol/v1/mcp_pb.d.ts +120 -0
- package/dist/_proto/ankka/protocol/v1/mcp_pb.js +29 -0
- package/dist/_proto/ankka/protocol/v1/timed_action_pb.js +1 -1
- package/dist/_proto/ankka/protocol/v1/view_pb.js +1 -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/agent.d.ts +10 -0
- package/dist/agent.js +20 -0
- package/dist/autonomous.d.ts +5 -0
- package/dist/autonomous.js +10 -0
- package/dist/client-keys.d.ts +89 -0
- package/dist/client-keys.js +182 -0
- package/dist/consumer.d.ts +5 -0
- package/dist/consumer.js +10 -0
- package/dist/context.d.ts +11 -3
- package/dist/context.js +20 -4
- package/dist/endpoint.d.ts +10 -0
- package/dist/endpoint.js +20 -0
- package/dist/graph.d.ts +5 -0
- package/dist/graph.js +10 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +5 -0
- package/dist/mcp-endpoint.d.ts +144 -0
- package/dist/mcp-endpoint.js +256 -0
- package/dist/routes.d.ts +25 -2
- package/dist/routes.js +29 -2
- package/dist/server/discovery.d.ts +7 -0
- package/dist/server/discovery.js +26 -2
- package/dist/server/eventSourced.js +3 -3
- package/dist/server/http.d.ts +8 -0
- package/dist/server/http.js +10 -4
- package/dist/server/mcp.d.ts +17 -0
- package/dist/server/mcp.js +157 -0
- package/dist/server/server.d.ts +11 -1
- package/dist/server/server.js +19 -0
- package/dist/server/storageEndpoint.d.ts +14 -0
- package/dist/server/storageEndpoint.js +82 -0
- package/dist/service.d.ts +6 -1
- package/dist/service.js +47 -4
- package/dist/sigv4.d.ts +47 -0
- package/dist/sigv4.js +149 -0
- package/dist/spec.d.ts +8 -2
- package/dist/spec.js +53 -3
- package/dist/storage.d.ts +288 -0
- package/dist/storage.js +1081 -0
- package/dist/testkit/integration.d.ts +9 -0
- package/dist/testkit/integration.js +5 -1
- package/dist/testkit/kinds.d.ts +16 -4
- package/dist/testkit/kinds.js +36 -14
- package/dist/testkit/unit.d.ts +4 -1
- package/dist/testkit/unit.js +8 -4
- package/dist/timedAction.d.ts +10 -0
- package/dist/timedAction.js +20 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workflow.d.ts +8 -0
- package/dist/workflow.js +15 -0
- package/package.json +2 -2
package/dist/autonomous.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
// request's tools run again — so a tool with a side effect should tolerate a repeat.
|
|
17
17
|
import { requestFromJson } from "./approvals.js";
|
|
18
18
|
import { secretsFor } from "./client.js";
|
|
19
|
+
import { processStorage } from "./storage.js";
|
|
19
20
|
import { decodeJsonValue, reviver } from "./json.js";
|
|
20
21
|
import { toJsonSchema } from "./schema.js";
|
|
21
22
|
import { servicesFor } from "./services.js";
|
|
@@ -68,6 +69,15 @@ export class AutonomousAgent {
|
|
|
68
69
|
set secrets(store) {
|
|
69
70
|
this.#secrets = store;
|
|
70
71
|
}
|
|
72
|
+
#storage;
|
|
73
|
+
/** The service's buckets: `this.storage.default`, or `this.storage.bucket("photos")`. */
|
|
74
|
+
get storage() {
|
|
75
|
+
return this.#storage ?? processStorage();
|
|
76
|
+
}
|
|
77
|
+
/** A unit test's store in place of the runtime's: `component.storage = new InMemoryBuckets()`. */
|
|
78
|
+
set storage(buckets) {
|
|
79
|
+
this.#storage = buckets;
|
|
80
|
+
}
|
|
71
81
|
#services;
|
|
72
82
|
/** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
|
|
73
83
|
get services() {
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type ComponentClient } from "./client.ts";
|
|
2
|
+
import { Duration, Instant } from "./time.ts";
|
|
3
|
+
/** The first protocol whose runtime keeps client keys; an older one answers `UNIMPLEMENTED`. */
|
|
4
|
+
export declare const CLIENT_KEYS_SINCE = "1.20";
|
|
5
|
+
/** When a client key stops being admitted. There is no default: the issuer says, every time. */
|
|
6
|
+
export type Expiry = {
|
|
7
|
+
readonly kind: "after";
|
|
8
|
+
readonly millis: number;
|
|
9
|
+
} | {
|
|
10
|
+
readonly kind: "at";
|
|
11
|
+
readonly epochMillis: number;
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: "never";
|
|
14
|
+
};
|
|
15
|
+
export declare const Expiry: Readonly<{
|
|
16
|
+
/** After a while from now: `Expiry.after({ days: 365 })`, or a `Duration`. */
|
|
17
|
+
after(by: Duration | {
|
|
18
|
+
readonly days?: number;
|
|
19
|
+
readonly hours?: number;
|
|
20
|
+
readonly minutes?: number;
|
|
21
|
+
readonly seconds?: number;
|
|
22
|
+
readonly millis?: number;
|
|
23
|
+
}): Expiry;
|
|
24
|
+
/** At an instant. */
|
|
25
|
+
at(when: Instant | Date): Expiry;
|
|
26
|
+
/** Never: admitted until it is revoked. */
|
|
27
|
+
never: Expiry;
|
|
28
|
+
}>;
|
|
29
|
+
/** What to issue: the subject a tool is told, how long, and what for. */
|
|
30
|
+
export interface IssueClientKey {
|
|
31
|
+
readonly subject: string;
|
|
32
|
+
readonly expiry: Expiry;
|
|
33
|
+
/** The MCP endpoint's path; needed only when the service has several. */
|
|
34
|
+
readonly endpoint?: string;
|
|
35
|
+
/** The tools the key may call; every tool when empty. */
|
|
36
|
+
readonly scope?: Iterable<string>;
|
|
37
|
+
/** What a tool reads as the principal's claims: at most 16, each name and value at most 256 characters. */
|
|
38
|
+
readonly attributes?: Readonly<Record<string, string>>;
|
|
39
|
+
readonly label?: string;
|
|
40
|
+
}
|
|
41
|
+
/** An issued key: its id, which lists and revokes it, and the key, shown here once. */
|
|
42
|
+
export interface IssuedClientKey {
|
|
43
|
+
readonly id: string;
|
|
44
|
+
readonly clientKey: string;
|
|
45
|
+
}
|
|
46
|
+
/** One client key as a listing shows it: never the key, nor anything it could be derived from. */
|
|
47
|
+
export interface ClientKeySummary {
|
|
48
|
+
readonly id: string;
|
|
49
|
+
readonly subject: string;
|
|
50
|
+
readonly attributes: Readonly<Record<string, string>>;
|
|
51
|
+
readonly label: string | null;
|
|
52
|
+
readonly endpoint: string;
|
|
53
|
+
readonly scope: readonly string[];
|
|
54
|
+
readonly issuedAt: Instant;
|
|
55
|
+
readonly expiresAt: Instant | null;
|
|
56
|
+
readonly lastUsed: Instant | null;
|
|
57
|
+
readonly revokedAt: Instant | null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The service's client keys, kept by the runtime. A refusal is a `CommandError` with the runtime's
|
|
61
|
+
* code: `BAD_REQUEST` for a request that breaks a rule, `UNAVAILABLE` when the service's MCP
|
|
62
|
+
* endpoints admit no client key (there is then nothing to keep them), `NOT_FOUND` for an unknown id.
|
|
63
|
+
*/
|
|
64
|
+
export declare class ClientKeys {
|
|
65
|
+
#private;
|
|
66
|
+
/** The keys reached through `client`'s sidecar; with none, every call throws. */
|
|
67
|
+
constructor(client?: ComponentClient);
|
|
68
|
+
/** Issues a key; the answer is the one place the key is ever shown. */
|
|
69
|
+
issue(request: IssueClientKey): Promise<IssuedClientKey>;
|
|
70
|
+
/** Every client key of the service, revoked ones included; never a key itself. */
|
|
71
|
+
list(): Promise<ClientKeySummary[]>;
|
|
72
|
+
/** Revokes a key: refused on its next request on every instance. Revoking twice is not an error. */
|
|
73
|
+
revoke(id: string): Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
/** Client keys for unit tests: a map, issuing keys that look like the runtime's and applying its rules. */
|
|
76
|
+
export declare class InMemoryClientKeys extends ClientKeys {
|
|
77
|
+
#private;
|
|
78
|
+
constructor();
|
|
79
|
+
/** The key issued under `id`, which a test may present; the runtime shows it only once. */
|
|
80
|
+
keyOf(id: string): string | undefined;
|
|
81
|
+
issue(request: IssueClientKey): Promise<IssuedClientKey>;
|
|
82
|
+
list(): Promise<ClientKeySummary[]>;
|
|
83
|
+
revoke(id: string): Promise<void>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* @internal The client keys reached through `client`'s sidecar. Not a member of the client, which an
|
|
87
|
+
* entity holds too: only the component kinds that may issue keys call this.
|
|
88
|
+
*/
|
|
89
|
+
export declare function clientKeysFor(client: ComponentClient): ClientKeys;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// A service's client keys (protocol 1.20): credentials it issues for its MCP endpoint, each for one
|
|
2
|
+
// subject, with an expiry, an optional scope of tools and attributes a tool reads as the principal's
|
|
3
|
+
// claims. The runtime keeps them; this process sees a key once, in the answer to `issue`, and never
|
|
4
|
+
// again. Offered to endpoints, MCP endpoints, timed actions and agents, as the secret store is.
|
|
5
|
+
//
|
|
6
|
+
// const issued = await this.clientKeys.issue({ subject: "acme", expiry: Expiry.after({ days: 365 }), scope: ["list_jobs"] })
|
|
7
|
+
// issued.clientKey // shown here, and nowhere again
|
|
8
|
+
import { Code, ConnectError } from "@connectrpc/connect";
|
|
9
|
+
import { channelOf } from "./client.js";
|
|
10
|
+
import { metadataToProto } from "./context.js";
|
|
11
|
+
import { CommandError, ErrorCode } from "./effects/common.js";
|
|
12
|
+
import { errorCodeFromProto } from "./kinds.js";
|
|
13
|
+
import { PROTOCOL_VERSION } from "./spec.js";
|
|
14
|
+
import { Duration, Instant } from "./time.js";
|
|
15
|
+
/** The first protocol whose runtime keeps client keys; an older one answers `UNIMPLEMENTED`. */
|
|
16
|
+
export const CLIENT_KEYS_SINCE = "1.20";
|
|
17
|
+
export const Expiry = Object.freeze({
|
|
18
|
+
/** After a while from now: `Expiry.after({ days: 365 })`, or a `Duration`. */
|
|
19
|
+
after(by) {
|
|
20
|
+
const millis = by instanceof Duration
|
|
21
|
+
? by.toMillis()
|
|
22
|
+
: (by.days ?? 0) * 86_400_000 + (by.hours ?? 0) * 3_600_000 + (by.minutes ?? 0) * 60_000 + (by.seconds ?? 0) * 1_000 + (by.millis ?? 0);
|
|
23
|
+
if (!Number.isFinite(millis) || millis <= 0)
|
|
24
|
+
throw new TypeError(`a client key's expiry is a positive while, not ${millis}ms`);
|
|
25
|
+
return Object.freeze({ kind: "after", millis });
|
|
26
|
+
},
|
|
27
|
+
/** At an instant. */
|
|
28
|
+
at(when) {
|
|
29
|
+
const epochMillis = when instanceof Instant ? when.toEpochMilli() : when.getTime();
|
|
30
|
+
return Object.freeze({ kind: "at", epochMillis });
|
|
31
|
+
},
|
|
32
|
+
/** Never: admitted until it is revoked. */
|
|
33
|
+
never: Object.freeze({ kind: "never" }),
|
|
34
|
+
});
|
|
35
|
+
function refusalOf(error) {
|
|
36
|
+
return new CommandError({ message: error.message, code: errorCodeFromProto(error.code), details: { ...(error.details ?? {}) } });
|
|
37
|
+
}
|
|
38
|
+
function tooOld(failure) {
|
|
39
|
+
if (failure instanceof ConnectError && failure.code === Code.Unimplemented) {
|
|
40
|
+
throw new CommandError({
|
|
41
|
+
message: `the runtime beside this process is too old for client keys, which needs protocol ${CLIENT_KEYS_SINCE}; this process speaks ${PROTOCOL_VERSION}: ${failure.rawMessage}`,
|
|
42
|
+
code: ErrorCode.Internal,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
throw failure;
|
|
46
|
+
}
|
|
47
|
+
function instant(millis) {
|
|
48
|
+
return millis === undefined ? null : Instant.ofEpochMilli(Number(millis));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The service's client keys, kept by the runtime. A refusal is a `CommandError` with the runtime's
|
|
52
|
+
* code: `BAD_REQUEST` for a request that breaks a rule, `UNAVAILABLE` when the service's MCP
|
|
53
|
+
* endpoints admit no client key (there is then nothing to keep them), `NOT_FOUND` for an unknown id.
|
|
54
|
+
*/
|
|
55
|
+
export class ClientKeys {
|
|
56
|
+
#client;
|
|
57
|
+
/** The keys reached through `client`'s sidecar; with none, every call throws. */
|
|
58
|
+
constructor(client) {
|
|
59
|
+
this.#client = client;
|
|
60
|
+
}
|
|
61
|
+
#channel() {
|
|
62
|
+
const channel = this.#client ? channelOf(this.#client) : undefined;
|
|
63
|
+
if (!channel)
|
|
64
|
+
throw new Error("these client keys are not connected to a runtime; a unit test passes an InMemoryClientKeys");
|
|
65
|
+
return channel;
|
|
66
|
+
}
|
|
67
|
+
/** Issues a key; the answer is the one place the key is ever shown. */
|
|
68
|
+
async issue(request) {
|
|
69
|
+
const { stub, metadata } = this.#channel();
|
|
70
|
+
const expiry = request.expiry;
|
|
71
|
+
const reply = await stub()
|
|
72
|
+
.issueClientKey({
|
|
73
|
+
subject: request.subject,
|
|
74
|
+
expiry: expiry.kind === "after"
|
|
75
|
+
? { case: "afterMillis", value: BigInt(expiry.millis) }
|
|
76
|
+
: expiry.kind === "at"
|
|
77
|
+
? { case: "atEpochMillis", value: BigInt(expiry.epochMillis) }
|
|
78
|
+
: { case: "never", value: true },
|
|
79
|
+
...(request.endpoint !== undefined ? { endpoint: request.endpoint } : {}),
|
|
80
|
+
scope: [...(request.scope ?? [])],
|
|
81
|
+
attributes: { ...(request.attributes ?? {}) },
|
|
82
|
+
...(request.label !== undefined ? { label: request.label } : {}),
|
|
83
|
+
metadata: metadataToProto(metadata),
|
|
84
|
+
})
|
|
85
|
+
.catch(tooOld);
|
|
86
|
+
switch (reply.reply.case) {
|
|
87
|
+
case "issued":
|
|
88
|
+
return Object.freeze({ id: reply.reply.value.id, clientKey: reply.reply.value.clientKey });
|
|
89
|
+
case "error":
|
|
90
|
+
throw refusalOf(reply.reply.value);
|
|
91
|
+
default:
|
|
92
|
+
throw new CommandError({ message: "the runtime answered an issue with nothing", code: ErrorCode.Internal });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Every client key of the service, revoked ones included; never a key itself. */
|
|
96
|
+
async list() {
|
|
97
|
+
const { stub, metadata } = this.#channel();
|
|
98
|
+
const reply = await stub().listClientKeys({ metadata: metadataToProto(metadata) }).catch(tooOld);
|
|
99
|
+
if (reply.error)
|
|
100
|
+
throw refusalOf(reply.error);
|
|
101
|
+
return reply.keys.map((k) => Object.freeze({
|
|
102
|
+
id: k.id,
|
|
103
|
+
subject: k.subject,
|
|
104
|
+
attributes: Object.freeze({ ...k.attributes }),
|
|
105
|
+
label: k.label ?? null,
|
|
106
|
+
endpoint: k.endpoint,
|
|
107
|
+
scope: Object.freeze([...k.scope]),
|
|
108
|
+
issuedAt: Instant.ofEpochMilli(Number(k.issuedAtMillis)),
|
|
109
|
+
expiresAt: instant(k.expiresAtMillis),
|
|
110
|
+
lastUsed: instant(k.lastUsedMillis),
|
|
111
|
+
revokedAt: instant(k.revokedAtMillis),
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
/** Revokes a key: refused on its next request on every instance. Revoking twice is not an error. */
|
|
115
|
+
async revoke(id) {
|
|
116
|
+
const { stub, metadata } = this.#channel();
|
|
117
|
+
const reply = await stub().revokeClientKey({ id, metadata: metadataToProto(metadata) }).catch(tooOld);
|
|
118
|
+
if (reply.error)
|
|
119
|
+
throw refusalOf(reply.error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** Client keys for unit tests: a map, issuing keys that look like the runtime's and applying its rules. */
|
|
123
|
+
export class InMemoryClientKeys extends ClientKeys {
|
|
124
|
+
#keys = new Map();
|
|
125
|
+
#next = 0;
|
|
126
|
+
constructor() {
|
|
127
|
+
super(undefined);
|
|
128
|
+
}
|
|
129
|
+
/** The key issued under `id`, which a test may present; the runtime shows it only once. */
|
|
130
|
+
keyOf(id) {
|
|
131
|
+
return this.#keys.get(id)?.clientKey;
|
|
132
|
+
}
|
|
133
|
+
async issue(request) {
|
|
134
|
+
if (typeof request.subject !== "string" || request.subject.trim() === "") {
|
|
135
|
+
throw new CommandError({ message: "a client key needs a subject", code: ErrorCode.BadRequest });
|
|
136
|
+
}
|
|
137
|
+
const attributes = request.attributes ?? {};
|
|
138
|
+
if (Object.keys(attributes).length > 16)
|
|
139
|
+
throw new CommandError({ message: "a client key carries at most 16 attributes", code: ErrorCode.BadRequest });
|
|
140
|
+
for (const [name, value] of Object.entries(attributes)) {
|
|
141
|
+
if (name.length > 256 || value.length > 256)
|
|
142
|
+
throw new CommandError({ message: `attribute '${name.slice(0, 40)}' is longer than 256 characters`, code: ErrorCode.BadRequest });
|
|
143
|
+
}
|
|
144
|
+
this.#next += 1;
|
|
145
|
+
const id = `ck_${this.#next}`;
|
|
146
|
+
const clientKey = `ak_test_${id}_${Math.random().toString(36).slice(2)}`;
|
|
147
|
+
const now = Instant.now();
|
|
148
|
+
const expiry = request.expiry;
|
|
149
|
+
const expiresAt = expiry.kind === "after" ? Instant.ofEpochMilli(now.toEpochMilli() + expiry.millis) : expiry.kind === "at" ? Instant.ofEpochMilli(expiry.epochMillis) : null;
|
|
150
|
+
const summary = Object.freeze({
|
|
151
|
+
id,
|
|
152
|
+
subject: request.subject,
|
|
153
|
+
attributes: Object.freeze({ ...attributes }),
|
|
154
|
+
label: request.label ?? null,
|
|
155
|
+
endpoint: request.endpoint ?? "",
|
|
156
|
+
scope: Object.freeze([...(request.scope ?? [])]),
|
|
157
|
+
issuedAt: now,
|
|
158
|
+
expiresAt,
|
|
159
|
+
lastUsed: null,
|
|
160
|
+
revokedAt: null,
|
|
161
|
+
});
|
|
162
|
+
this.#keys.set(id, { summary, clientKey });
|
|
163
|
+
return Object.freeze({ id, clientKey });
|
|
164
|
+
}
|
|
165
|
+
async list() {
|
|
166
|
+
return [...this.#keys.values()].map((k) => k.summary);
|
|
167
|
+
}
|
|
168
|
+
async revoke(id) {
|
|
169
|
+
const held = this.#keys.get(id);
|
|
170
|
+
if (!held)
|
|
171
|
+
throw new CommandError({ message: `no client key '${id}'`, code: ErrorCode.NotFound });
|
|
172
|
+
if (held.summary.revokedAt === null)
|
|
173
|
+
this.#keys.set(id, { ...held, summary: Object.freeze({ ...held.summary, revokedAt: Instant.now() }) });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* @internal The client keys reached through `client`'s sidecar. Not a member of the client, which an
|
|
178
|
+
* entity holds too: only the component kinds that may issue keys call this.
|
|
179
|
+
*/
|
|
180
|
+
export function clientKeysFor(client) {
|
|
181
|
+
return new ClientKeys(client);
|
|
182
|
+
}
|
package/dist/consumer.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { Contract, Publication } from "./contract.ts";
|
|
|
3
3
|
import type { Shape } from "./codec.ts";
|
|
4
4
|
import type { Metadata } from "./effects/common.ts";
|
|
5
5
|
import { type ComponentClient, type ComponentRef, type Secrets } from "./client.ts";
|
|
6
|
+
import { type Buckets } from "./storage.ts";
|
|
6
7
|
import { ConsumerEffects, type ConsumerEffect } from "./effects/stateless.ts";
|
|
7
8
|
import { type Services } from "./services.ts";
|
|
8
9
|
import type { Standing } from "./standing.ts";
|
|
@@ -40,6 +41,10 @@ export declare abstract class Consumer<M, Out = never> {
|
|
|
40
41
|
get secrets(): Secrets;
|
|
41
42
|
/** A unit test's store in place of the runtime's: `component.secrets = new InMemorySecrets()`. */
|
|
42
43
|
set secrets(store: Secrets);
|
|
44
|
+
/** The service's buckets: `this.storage.default`, or `this.storage.bucket("photos")`. */
|
|
45
|
+
get storage(): Buckets;
|
|
46
|
+
/** A unit test's store in place of the runtime's: `component.storage = new InMemoryBuckets()`. */
|
|
47
|
+
set storage(buckets: Buckets);
|
|
43
48
|
/** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
|
|
44
49
|
get services(): Services;
|
|
45
50
|
/** A unit test's in place of the runtime's: `component.services = new ScriptedServices()`. */
|
package/dist/consumer.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// A consumer: reacts to a source's changes, optionally producing onward to a topic.
|
|
2
2
|
import { secretsFor } from "./client.js";
|
|
3
|
+
import { processStorage } from "./storage.js";
|
|
3
4
|
import { ConsumerEffects } from "./effects/stateless.js";
|
|
4
5
|
import { servicesFor } from "./services.js";
|
|
5
6
|
/** The metadata entry a runtime sets on a consumer's request to say what it speaks: its protocol version. */
|
|
@@ -70,6 +71,15 @@ export class Consumer {
|
|
|
70
71
|
set secrets(store) {
|
|
71
72
|
this.#secrets = store;
|
|
72
73
|
}
|
|
74
|
+
#storage;
|
|
75
|
+
/** The service's buckets: `this.storage.default`, or `this.storage.bucket("photos")`. */
|
|
76
|
+
get storage() {
|
|
77
|
+
return this.#storage ?? processStorage();
|
|
78
|
+
}
|
|
79
|
+
/** A unit test's store in place of the runtime's: `component.storage = new InMemoryBuckets()`. */
|
|
80
|
+
set storage(buckets) {
|
|
81
|
+
this.#storage = buckets;
|
|
82
|
+
}
|
|
73
83
|
#services;
|
|
74
84
|
/** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
|
|
75
85
|
get services() {
|
package/dist/context.d.ts
CHANGED
|
@@ -17,19 +17,27 @@ export interface CommandContext {
|
|
|
17
17
|
* the home, so this is `false` only for a query a follower answers; `true` for an entity not replicated.
|
|
18
18
|
*/
|
|
19
19
|
readonly isHome: boolean;
|
|
20
|
+
/** The subject of the principal who made the original call (1.22); absent when none named one. */
|
|
21
|
+
readonly attribution?: string;
|
|
22
|
+
/** The installation this command was forwarded from to its home (1.22); absent when it was not. */
|
|
23
|
+
readonly forwardedFrom?: string;
|
|
20
24
|
}
|
|
21
|
-
export declare function commandContext(componentId: string, entityId: string, sequenceNumber: bigint, metadata: Metadata, now?: () => Instant, installation?: string | null, isHome?: boolean): CommandContext;
|
|
25
|
+
export declare function commandContext(componentId: string, entityId: string, sequenceNumber: bigint, metadata: Metadata, now?: () => Instant, installation?: string | null, isHome?: boolean, attribution?: string, forwardedFrom?: string): CommandContext;
|
|
22
26
|
/**
|
|
23
27
|
* Where the event `applyEvent` is applying was written (1.19): the installation (`null` when none is
|
|
24
28
|
* named; an event from before replication reads as written at the home), whether that was this
|
|
25
|
-
* installation, and its sequence number
|
|
29
|
+
* installation, and its sequence number; who acted on the command that wrote it and where that
|
|
30
|
+
* command was forwarded from, as recorded (1.22; absent when nothing was). The third argument of
|
|
31
|
+
* `applyEvent`.
|
|
26
32
|
*/
|
|
27
33
|
export interface EventContext {
|
|
28
34
|
readonly installation: string | null;
|
|
29
35
|
readonly isLocal: boolean;
|
|
30
36
|
readonly sequence: bigint;
|
|
37
|
+
readonly attribution?: string;
|
|
38
|
+
readonly forwardedFrom?: string;
|
|
31
39
|
}
|
|
32
|
-
export declare function eventContext(installation: string | null, isLocal: boolean, sequence: bigint): EventContext;
|
|
40
|
+
export declare function eventContext(installation: string | null, isLocal: boolean, sequence: bigint, attribution?: string, forwardedFrom?: string): EventContext;
|
|
33
41
|
export declare function metadataFromProto(proto: ProtoMetadata | undefined): Metadata;
|
|
34
42
|
export declare function metadataToProto(metadata: Metadata | undefined): {
|
|
35
43
|
entries: {
|
package/dist/context.js
CHANGED
|
@@ -4,11 +4,27 @@
|
|
|
4
4
|
// request per asynchronous context, and work handed to another context cannot see it.
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { Instant } from "./time.js";
|
|
7
|
-
export function commandContext(componentId, entityId, sequenceNumber, metadata, now = Instant.now, installation = null, isHome = true) {
|
|
8
|
-
return Object.freeze({
|
|
7
|
+
export function commandContext(componentId, entityId, sequenceNumber, metadata, now = Instant.now, installation = null, isHome = true, attribution, forwardedFrom) {
|
|
8
|
+
return Object.freeze({
|
|
9
|
+
componentId,
|
|
10
|
+
entityId,
|
|
11
|
+
sequenceNumber,
|
|
12
|
+
metadata,
|
|
13
|
+
now,
|
|
14
|
+
installation,
|
|
15
|
+
isHome,
|
|
16
|
+
...(attribution ? { attribution } : {}),
|
|
17
|
+
...(forwardedFrom ? { forwardedFrom } : {}),
|
|
18
|
+
});
|
|
9
19
|
}
|
|
10
|
-
export function eventContext(installation, isLocal, sequence) {
|
|
11
|
-
return Object.freeze({
|
|
20
|
+
export function eventContext(installation, isLocal, sequence, attribution, forwardedFrom) {
|
|
21
|
+
return Object.freeze({
|
|
22
|
+
installation,
|
|
23
|
+
isLocal,
|
|
24
|
+
sequence,
|
|
25
|
+
...(attribution ? { attribution } : {}),
|
|
26
|
+
...(forwardedFrom ? { forwardedFrom } : {}),
|
|
27
|
+
});
|
|
12
28
|
}
|
|
13
29
|
export function metadataFromProto(proto) {
|
|
14
30
|
const out = {};
|
package/dist/endpoint.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type ClientKeys } from "./client-keys.ts";
|
|
1
2
|
import { type ComponentClient, type Secrets } from "./client.ts";
|
|
3
|
+
import { type Buckets } from "./storage.ts";
|
|
2
4
|
import { type RequestContext } from "./context.ts";
|
|
3
5
|
import type { Acl, RouteTable } from "./routes.ts";
|
|
4
6
|
import { type Services } from "./services.ts";
|
|
@@ -12,10 +14,18 @@ export declare abstract class Endpoint {
|
|
|
12
14
|
get secrets(): Secrets;
|
|
13
15
|
/** A unit test's store in place of the runtime's: `component.secrets = new InMemorySecrets()`. */
|
|
14
16
|
set secrets(store: Secrets);
|
|
17
|
+
/** The service's buckets: `this.storage.default`, or `this.storage.bucket("photos")`. */
|
|
18
|
+
get storage(): Buckets;
|
|
19
|
+
/** A unit test's store in place of the runtime's: `component.storage = new InMemoryBuckets()`. */
|
|
20
|
+
set storage(buckets: Buckets);
|
|
15
21
|
/** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
|
|
16
22
|
get services(): Services;
|
|
17
23
|
/** A unit test's in place of the runtime's: `component.services = new ScriptedServices()`. */
|
|
18
24
|
set services(services: Services);
|
|
25
|
+
/** The service's client keys (1.20): issued for its MCP endpoint, kept by the runtime, each shown once. */
|
|
26
|
+
get clientKeys(): ClientKeys;
|
|
27
|
+
/** A unit test's in place of the runtime's: `component.clientKeys = new InMemoryClientKeys()`. */
|
|
28
|
+
set clientKeys(keys: ClientKeys);
|
|
19
29
|
/** @internal */
|
|
20
30
|
_bindClient(client: ComponentClient | undefined): void;
|
|
21
31
|
}
|
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 { clientKeysFor } from "./client-keys.js";
|
|
11
12
|
import { secretsFor } from "./client.js";
|
|
13
|
+
import { processStorage } from "./storage.js";
|
|
12
14
|
import { currentRequest, requestIfAny } from "./context.js";
|
|
13
15
|
import { servicesFor } from "./services.js";
|
|
14
16
|
export class Endpoint {
|
|
@@ -33,6 +35,15 @@ export class Endpoint {
|
|
|
33
35
|
set secrets(store) {
|
|
34
36
|
this.#secrets = store;
|
|
35
37
|
}
|
|
38
|
+
#storage;
|
|
39
|
+
/** The service's buckets: `this.storage.default`, or `this.storage.bucket("photos")`. */
|
|
40
|
+
get storage() {
|
|
41
|
+
return this.#storage ?? processStorage();
|
|
42
|
+
}
|
|
43
|
+
/** A unit test's store in place of the runtime's: `component.storage = new InMemoryBuckets()`. */
|
|
44
|
+
set storage(buckets) {
|
|
45
|
+
this.#storage = buckets;
|
|
46
|
+
}
|
|
36
47
|
#services;
|
|
37
48
|
/** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
|
|
38
49
|
get services() {
|
|
@@ -42,6 +53,15 @@ export class Endpoint {
|
|
|
42
53
|
set services(services) {
|
|
43
54
|
this.#services = services;
|
|
44
55
|
}
|
|
56
|
+
#clientKeys;
|
|
57
|
+
/** The service's client keys (1.20): issued for its MCP endpoint, kept by the runtime, each shown once. */
|
|
58
|
+
get clientKeys() {
|
|
59
|
+
return this.#clientKeys ?? clientKeysFor(this.client);
|
|
60
|
+
}
|
|
61
|
+
/** A unit test's in place of the runtime's: `component.clientKeys = new InMemoryClientKeys()`. */
|
|
62
|
+
set clientKeys(keys) {
|
|
63
|
+
this.#clientKeys = keys;
|
|
64
|
+
}
|
|
45
65
|
/** @internal */
|
|
46
66
|
_bindClient(client) {
|
|
47
67
|
this.#client = client;
|
package/dist/graph.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { StartFrom } from "./startFrom.ts";
|
|
|
2
2
|
import { type Codec, type Shape } from "./codec.ts";
|
|
3
3
|
import type { EffectLike, Metadata } from "./effects/common.ts";
|
|
4
4
|
import { type ComponentClient, type ComponentRef, type Secrets } from "./client.ts";
|
|
5
|
+
import { type Buckets } from "./storage.ts";
|
|
5
6
|
import { type Services } from "./services.ts";
|
|
6
7
|
import type { Standing } from "./standing.ts";
|
|
7
8
|
type MaybePromise<T> = T | Promise<T>;
|
|
@@ -146,6 +147,10 @@ export declare abstract class GraphConsumer<M> {
|
|
|
146
147
|
get secrets(): Secrets;
|
|
147
148
|
/** A unit test's store in place of the runtime's: `component.secrets = new InMemorySecrets()`. */
|
|
148
149
|
set secrets(store: Secrets);
|
|
150
|
+
/** The service's buckets: `this.storage.default`, or `this.storage.bucket("photos")`. */
|
|
151
|
+
get storage(): Buckets;
|
|
152
|
+
/** A unit test's store in place of the runtime's: `component.storage = new InMemoryBuckets()`. */
|
|
153
|
+
set storage(buckets: Buckets);
|
|
149
154
|
/** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
|
|
150
155
|
get services(): Services;
|
|
151
156
|
/** A unit test's in place of the runtime's: `component.services = new ScriptedServices()`. */
|
package/dist/graph.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
// }
|
|
17
17
|
import { JSON_CONTENT } from "./codec.js";
|
|
18
18
|
import { secretsFor } from "./client.js";
|
|
19
|
+
import { processStorage } from "./storage.js";
|
|
19
20
|
import { sequenceNumberOf } from "./consumer.js";
|
|
20
21
|
import { renderDouble, reviver } from "./json.js";
|
|
21
22
|
import { servicesFor } from "./services.js";
|
|
@@ -426,6 +427,15 @@ export class GraphConsumer {
|
|
|
426
427
|
set secrets(store) {
|
|
427
428
|
this.#secrets = store;
|
|
428
429
|
}
|
|
430
|
+
#storage;
|
|
431
|
+
/** The service's buckets: `this.storage.default`, or `this.storage.bucket("photos")`. */
|
|
432
|
+
get storage() {
|
|
433
|
+
return this.#storage ?? processStorage();
|
|
434
|
+
}
|
|
435
|
+
/** A unit test's store in place of the runtime's: `component.storage = new InMemoryBuckets()`. */
|
|
436
|
+
set storage(buckets) {
|
|
437
|
+
this.#storage = buckets;
|
|
438
|
+
}
|
|
429
439
|
#services;
|
|
430
440
|
/** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
|
|
431
441
|
get services() {
|
package/dist/index.d.ts
CHANGED
|
@@ -25,14 +25,19 @@ export { ApprovalAwaited, APPROVALS_SINCE, type AgentOutcome, type Approval, typ
|
|
|
25
25
|
export { mcpServer, resultGuardrail, toolName, type McpServerOptions, type McpServerRef, type ResultGuardrailRef } from "./mcp.ts";
|
|
26
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";
|
|
27
27
|
export { Endpoint, type EndpointClass } from "./endpoint.ts";
|
|
28
|
+
export { McpEndpoint, ToolTable, tools, MCP_ENDPOINTS_SINCE, type McpEndpointClass, type DeclaredTool, type ToolOptions, type ReferenceOptions, type RegisteredMcpEndpoint, type RegisteredMcpTool, type ToolRun, } from "./mcp-endpoint.ts";
|
|
29
|
+
export { ClientKeys, InMemoryClientKeys, Expiry, CLIENT_KEYS_SINCE, type IssueClientKey, type IssuedClientKey, type ClientKeySummary, } from "./client-keys.ts";
|
|
28
30
|
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";
|
|
31
|
+
export { Acl, Callers, type CallerMatcher, type CallersAcl, type AnyOfAcl, 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
32
|
export { type CommandContext, type EventContext, type RequestContext, type Principal, type Caller, Query, Headers } from "./context.ts";
|
|
31
33
|
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";
|
|
34
|
+
export { ObjectRules, StorageRefusals, InMemoryBuckets, DirectoryBuckets, S3Buckets, S3Bucket, noStorage, chooseStorage, declaredBuckets, processStorage, s3Settings, bucketNameOf, bucketVariableSuffix, localAddress, STORAGE_PATH, type Buckets, type Bucket, type BucketExposure, type SignedAccess, type StoredObject, type ObjectInfo, type ObjectEntry, type PutOptions, type InMemoryBucketsOptions, type DirectoryBucketsOptions, type DeclaredBuckets, type S3Settings, type S3Target, type NamedTarget, type StorageBackend, type ChosenStorage, type StorageChoiceOptions, } from "./storage.ts";
|
|
35
|
+
export { Credentials, SignedRequest, sign as signRequest, presign as presignUrl } from "./sigv4.ts";
|
|
32
36
|
export { Services, ServiceClient, ScriptedServices, noServices, ServiceError, ServiceUnresolvable, ServiceIdentityMismatch, ServiceUnanswered, ServiceCallFailed, type ServiceResponse, type ServiceRequest, type ServiceHeaders, } from "./services.ts";
|
|
33
37
|
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";
|
|
34
38
|
export { Server, type ServerOptions } from "./server/server.ts";
|
|
35
39
|
export { problems as sidecarProblems } from "./server/discovery.ts";
|
|
40
|
+
export { createMcpDispatcher, type McpDispatcher, type ToolAnswer } from "./server/mcp.ts";
|
|
36
41
|
export { PROTOCOL_VERSION, SDK_NAME } from "./spec.ts";
|
|
37
42
|
export { StartFrom } from "./startFrom.ts";
|
|
38
43
|
export { isTerminal, isUnknown, type Standing, type StandingStatus } from "./standing.ts";
|
package/dist/index.js
CHANGED
|
@@ -36,14 +36,19 @@ export { ApprovalAwaited, APPROVALS_SINCE } from "./approvals.js";
|
|
|
36
36
|
export { mcpServer, resultGuardrail, toolName } from "./mcp.js";
|
|
37
37
|
export { AutonomousAgent, AutonomousAgentCalls, TaskCalls, Tasks, accepted, rejected, taskAcceptance, taskRule, taskType, } from "./autonomous.js";
|
|
38
38
|
export { Endpoint } from "./endpoint.js";
|
|
39
|
+
export { McpEndpoint, ToolTable, tools, MCP_ENDPOINTS_SINCE, } from "./mcp-endpoint.js";
|
|
40
|
+
export { ClientKeys, InMemoryClientKeys, Expiry, CLIENT_KEYS_SINCE, } from "./client-keys.js";
|
|
39
41
|
export { SocketClosed } from "./socket.js";
|
|
40
42
|
export { Acl, Callers, HttpProblem, get, post, put, patch, del, sse, sseEvent, sseEvents, socket } from "./routes.js";
|
|
41
43
|
export { Query, Headers } from "./context.js";
|
|
42
44
|
export { ComponentClient, Calls, TypedCalls, Invocation, AwaitingInvocation, AWAIT_SINCE, awaitPartJson, Views, Timers, Secrets, InMemorySecrets, noSecrets, WatchEnded, VIEW_STREAMS_SINCE } from "./client.js";
|
|
45
|
+
export { ObjectRules, StorageRefusals, InMemoryBuckets, DirectoryBuckets, S3Buckets, S3Bucket, noStorage, chooseStorage, declaredBuckets, processStorage, s3Settings, bucketNameOf, bucketVariableSuffix, localAddress, STORAGE_PATH, } from "./storage.js";
|
|
46
|
+
export { Credentials, SignedRequest, sign as signRequest, presign as presignUrl } from "./sigv4.js";
|
|
43
47
|
export { Services, ServiceClient, ScriptedServices, noServices, ServiceError, ServiceUnresolvable, ServiceIdentityMismatch, ServiceUnanswered, ServiceCallFailed, } from "./services.js";
|
|
44
48
|
export { Ankka, ServiceBuilder, Registry, RegistrationError, } from "./service.js";
|
|
45
49
|
export { Server } from "./server/server.js";
|
|
46
50
|
export { problems as sidecarProblems } from "./server/discovery.js";
|
|
51
|
+
export { createMcpDispatcher } from "./server/mcp.js";
|
|
47
52
|
export { PROTOCOL_VERSION, SDK_NAME } from "./spec.js";
|
|
48
53
|
export { StartFrom } from "./startFrom.js";
|
|
49
54
|
export { isTerminal, isUnknown } from "./standing.js";
|