@cotal-ai/core 0.1.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.
Files changed (50) hide show
  1. package/LICENSE +202 -0
  2. package/dist/agent-file.d.ts +24 -0
  3. package/dist/agent-file.d.ts.map +1 -0
  4. package/dist/agent-file.js +106 -0
  5. package/dist/agent-file.js.map +1 -0
  6. package/dist/command.d.ts +15 -0
  7. package/dist/command.d.ts.map +1 -0
  8. package/dist/command.js +2 -0
  9. package/dist/command.js.map +1 -0
  10. package/dist/connector.d.ts +43 -0
  11. package/dist/connector.d.ts.map +1 -0
  12. package/dist/connector.js +2 -0
  13. package/dist/connector.js.map +1 -0
  14. package/dist/endpoint.d.ts +193 -0
  15. package/dist/endpoint.d.ts.map +1 -0
  16. package/dist/endpoint.js +628 -0
  17. package/dist/endpoint.js.map +1 -0
  18. package/dist/identity.d.ts +26 -0
  19. package/dist/identity.d.ts.map +1 -0
  20. package/dist/identity.js +29 -0
  21. package/dist/identity.js.map +1 -0
  22. package/dist/index.d.ts +12 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +12 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/link.d.ts +37 -0
  27. package/dist/link.d.ts.map +1 -0
  28. package/dist/link.js +39 -0
  29. package/dist/link.js.map +1 -0
  30. package/dist/provision.d.ts +68 -0
  31. package/dist/provision.d.ts.map +1 -0
  32. package/dist/provision.js +204 -0
  33. package/dist/provision.js.map +1 -0
  34. package/dist/registry.d.ts +24 -0
  35. package/dist/registry.d.ts.map +1 -0
  36. package/dist/registry.js +26 -0
  37. package/dist/registry.js.map +1 -0
  38. package/dist/streams.d.ts +47 -0
  39. package/dist/streams.d.ts.map +1 -0
  40. package/dist/streams.js +97 -0
  41. package/dist/streams.js.map +1 -0
  42. package/dist/subjects.d.ts +83 -0
  43. package/dist/subjects.d.ts.map +1 -0
  44. package/dist/subjects.js +186 -0
  45. package/dist/subjects.js.map +1 -0
  46. package/dist/types.d.ts +117 -0
  47. package/dist/types.d.ts.map +1 -0
  48. package/dist/types.js +9 -0
  49. package/dist/types.js.map +1 -0
  50. package/package.json +31 -0
@@ -0,0 +1,26 @@
1
+ /**
2
+ * A locally-generated agent identity (an nkey user keypair).
3
+ *
4
+ * The public key is the **stable id** used identically everywhere — `card.id`, the
5
+ * subject-encoded sender token, the JWT subject, and the DM durable name — so the
6
+ * server's ACLs and the wire layout stay in lockstep. The seed is the private half:
7
+ * it never goes on the wire and (from the provisioning step on) is signed into a
8
+ * creds file the endpoint loads to authenticate as this id.
9
+ */
10
+ export interface Identity {
11
+ /** User nkey public key (`U…`). The stable agent id. */
12
+ id: string;
13
+ /** User nkey seed (`SU…`). Private — kept off the wire. */
14
+ seed: string;
15
+ }
16
+ /** Generate a fresh user nkey identity locally. The seed is derived here and never
17
+ * leaves the generating process except as a creds file handed to its own agent. */
18
+ export declare function newIdentity(): Identity;
19
+ /** The stable id carried by a creds file: the agent's nkey public key. Derived from the
20
+ * seed block (format-independent) and cross-checked against the JWT subject — a mismatch
21
+ * means a corrupt or spliced creds file (a seed paired with someone else's JWT), which
22
+ * would otherwise auth as one identity while the subject token claims another. Lets an
23
+ * endpoint that authenticates with creds adopt the matching `card.id`, keeping one id
24
+ * everywhere. */
25
+ export declare function idFromCreds(creds: string): string;
26
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,WAAW,QAAQ;IACvB,wDAAwD;IACxD,EAAE,EAAE,MAAM,CAAC;IACX,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;CACd;AAED;oFACoF;AACpF,wBAAgB,WAAW,IAAI,QAAQ,CAItC;AAED;;;;;kBAKkB;AAClB,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWjD"}
@@ -0,0 +1,29 @@
1
+ import { createUser, fromSeed } from "@nats-io/nkeys";
2
+ /** Generate a fresh user nkey identity locally. The seed is derived here and never
3
+ * leaves the generating process except as a creds file handed to its own agent. */
4
+ export function newIdentity() {
5
+ const kp = createUser();
6
+ const seed = new TextDecoder().decode(kp.getSeed());
7
+ return { id: kp.getPublicKey(), seed };
8
+ }
9
+ /** The stable id carried by a creds file: the agent's nkey public key. Derived from the
10
+ * seed block (format-independent) and cross-checked against the JWT subject — a mismatch
11
+ * means a corrupt or spliced creds file (a seed paired with someone else's JWT), which
12
+ * would otherwise auth as one identity while the subject token claims another. Lets an
13
+ * endpoint that authenticates with creds adopt the matching `card.id`, keeping one id
14
+ * everywhere. */
15
+ export function idFromCreds(creds) {
16
+ const seedM = creds.match(/BEGIN USER NKEY SEED-----\s*([\s\S]*?)\s*------END USER NKEY SEED/);
17
+ if (!seedM)
18
+ throw new Error("creds: no user nkey seed block found");
19
+ const id = fromSeed(new TextEncoder().encode(seedM[1].trim())).getPublicKey();
20
+ const jwtM = creds.match(/BEGIN NATS USER JWT-----\s*([\s\S]*?)\s*------END NATS USER JWT/);
21
+ const payload = jwtM?.[1].trim().split(".")[1];
22
+ const sub = payload
23
+ ? JSON.parse(Buffer.from(payload, "base64url").toString("utf8")).sub
24
+ : undefined;
25
+ if (sub && sub !== id)
26
+ throw new Error(`creds: seed identity ${id} != JWT subject ${sub}`);
27
+ return id;
28
+ }
29
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAkBtD;oFACoF;AACpF,MAAM,UAAU,WAAW;IACzB,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IACxB,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IACpD,OAAO,EAAE,EAAE,EAAE,EAAE,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,CAAC;AACzC,CAAC;AAED;;;;;kBAKkB;AAClB,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,mEAAmE,CAAC,CAAC;IAC/F,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IACpE,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC;IAC9E,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,iEAAiE,CAAC,CAAC;IAC5F,MAAM,OAAO,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAG,OAAO;QACjB,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAsB,CAAC,GAAG;QAC1F,CAAC,CAAC,SAAS,CAAC;IACd,IAAI,GAAG,IAAI,GAAG,KAAK,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,EAAE,mBAAmB,GAAG,EAAE,CAAC,CAAC;IAC3F,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,12 @@
1
+ export * from "./types.js";
2
+ export * from "./subjects.js";
3
+ export * from "./link.js";
4
+ export * from "./identity.js";
5
+ export * from "./provision.js";
6
+ export * from "./streams.js";
7
+ export * from "./agent-file.js";
8
+ export * from "./endpoint.js";
9
+ export * from "./connector.js";
10
+ export * from "./command.js";
11
+ export * from "./registry.js";
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ export * from "./types.js";
2
+ export * from "./subjects.js";
3
+ export * from "./link.js";
4
+ export * from "./identity.js";
5
+ export * from "./provision.js";
6
+ export * from "./streams.js";
7
+ export * from "./agent-file.js";
8
+ export * from "./endpoint.js";
9
+ export * from "./connector.js";
10
+ export * from "./command.js";
11
+ export * from "./registry.js";
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC"}
package/dist/link.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The Cotal join link — the onboarding half of the wire contract (v0).
3
+ *
4
+ * cotal://[token@]host[:port]/space[?channel=a,b] plaintext
5
+ * cotals://[token@]host[:port]/space[?channel=a,b] TLS required
6
+ * cotal://user:pass@host/space user/password auth
7
+ *
8
+ * One copy-pasteable string carries server + credentials + space, so a peer
9
+ * joins with a single value. The nats.js client does NOT read credentials from
10
+ * a URL, so we parse them out here and hand them to connect() as options.
11
+ */
12
+ export interface JoinLink {
13
+ /** nats:// server URL (auth is carried separately, never in this string). */
14
+ servers: string;
15
+ space: string;
16
+ /** Channels from the link, if any (?channel=a,b). */
17
+ channels?: string[];
18
+ /** Whether a TLS connection is required. */
19
+ tls: boolean;
20
+ /** Bare token (userinfo without a password). */
21
+ token?: string;
22
+ /** Username (userinfo with a password). */
23
+ user?: string;
24
+ pass?: string;
25
+ }
26
+ /** Parse a `cotal://` / `cotals://` join link. Throws on anything malformed. */
27
+ export declare function parseJoinLink(link: string): JoinLink;
28
+ /** Build a join link from its parts (for an operator handing one out). */
29
+ export declare function formatJoinLink(opts: {
30
+ host: string;
31
+ port?: number;
32
+ space: string;
33
+ token?: string;
34
+ tls?: boolean;
35
+ channels?: string[];
36
+ }): string;
37
+ //# sourceMappingURL=link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../src/link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,QAAQ;IACvB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,4CAA4C;IAC5C,GAAG,EAAE,OAAO,CAAC;IACb,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CA+BpD;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB,GAAG,MAAM,CAMT"}
package/dist/link.js ADDED
@@ -0,0 +1,39 @@
1
+ /** Parse a `cotal://` / `cotals://` join link. Throws on anything malformed. */
2
+ export function parseJoinLink(link) {
3
+ const tls = link.startsWith("cotals://");
4
+ if (!tls && !link.startsWith("cotal://"))
5
+ throw new Error(`not a Cotal link (expected cotal:// or cotals://): ${link}`);
6
+ // Reparse under a "special" scheme so the WHATWG URL parser populates the
7
+ // authority (credentials/host/port) reliably — non-special schemes don't.
8
+ const u = new URL(link.replace(/^cotals?:\/\//, tls ? "https://" : "http://"));
9
+ if (!u.hostname)
10
+ throw new Error(`Cotal link has no host: ${link}`);
11
+ const space = decodeURIComponent(u.pathname.replace(/^\/+/, "").split("/")[0] ?? "");
12
+ if (!space)
13
+ throw new Error(`Cotal link has no space (cotal://host/<space>): ${link}`);
14
+ const chParam = u.searchParams.get("channels") ?? u.searchParams.get("channel");
15
+ const channels = chParam
16
+ ? chParam.split(",").map((s) => s.trim()).filter(Boolean)
17
+ : undefined;
18
+ const user = u.username ? decodeURIComponent(u.username) : undefined;
19
+ const pass = u.password ? decodeURIComponent(u.password) : undefined;
20
+ return {
21
+ servers: `nats://${u.hostname}:${u.port || "4222"}`,
22
+ space,
23
+ channels,
24
+ tls,
25
+ // userinfo with no ':' is a bare token; with ':' it's user/password.
26
+ token: user && !pass ? user : undefined,
27
+ user: pass ? user : undefined,
28
+ pass: pass || undefined,
29
+ };
30
+ }
31
+ /** Build a join link from its parts (for an operator handing one out). */
32
+ export function formatJoinLink(opts) {
33
+ const scheme = opts.tls ? "cotals" : "cotal";
34
+ const cred = opts.token ? `${encodeURIComponent(opts.token)}@` : "";
35
+ const port = opts.port ? `:${opts.port}` : "";
36
+ const query = opts.channels?.length ? `?channel=${opts.channels.join(",")}` : "";
37
+ return `${scheme}://${cred}${opts.host}${port}/${encodeURIComponent(opts.space)}${query}`;
38
+ }
39
+ //# sourceMappingURL=link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.js","sourceRoot":"","sources":["../src/link.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import type { Identity } from "./identity.js";
2
+ /** Cred profiles (per the plan's class table). Demo-1 mints all permissively; steps 5–7
3
+ * scope each one — at which point the manager MUST already hold its own privileged
4
+ * profile (broad: pre-create others' DM durables, serve ctl), not "agent", or it
5
+ * silently loses those powers the moment "agent" is tightened. */
6
+ export type Profile = "agent" | "observer" | "admin" | "manager";
7
+ /** A space's persisted trust material. The `signingSeed` is the sensitive provisioner
8
+ * secret; everything else is public (JWTs) or recoverable. */
9
+ export interface SpaceAuth {
10
+ space: string;
11
+ operator: {
12
+ seed: string;
13
+ jwt: string;
14
+ };
15
+ account: {
16
+ pub: string;
17
+ seed: string;
18
+ jwt: string;
19
+ signingSeed: string;
20
+ signingPub: string;
21
+ };
22
+ sys: {
23
+ pub: string;
24
+ jwt: string;
25
+ };
26
+ }
27
+ /** Generate a fresh operator → account(+signing key) → system-account chain for a space. */
28
+ export declare function createSpaceAuth(space: string): Promise<SpaceAuth>;
29
+ /** Options shaping a minted user's permissions. */
30
+ export interface MintOpts {
31
+ /** Channels an "agent" may publish to (the agent file's `publish:` allow-list, already
32
+ * resolved by the caller). Each is run through the chat-subject builder so a wildcard
33
+ * subtree like `team.>` becomes `chat.<id>.team.>`. Defaults to `["general"]`. */
34
+ channels?: string[];
35
+ /** The agent's role — scopes its TASK-queue consumer to svc_<role>. */
36
+ role?: string;
37
+ /** Control service the agent may address. Defaults to `"manager"`. */
38
+ manager?: string;
39
+ }
40
+ /** The privileged onboarding ops a launcher needs — implemented by a connected, permissive
41
+ * endpoint (the manager, or a short-lived provisioner that `cotal spawn` opens). */
42
+ export interface DurableProvisioner {
43
+ provisionDmInbox(id: string): Promise<void>;
44
+ provisionTaskQueue(role: string): Promise<void>;
45
+ }
46
+ /** Onboard an agent for launch (auth mode): pre-create its bind-only DM (+ role TASK) durables
47
+ * and mint its scoped creds. The single shared step so every launcher — the manager and
48
+ * `cotal spawn` alike — provisions identically (manager not special). */
49
+ export declare function provisionAgent(provisioner: DurableProvisioner, auth: SpaceAuth, identity: Identity, opts?: MintOpts): Promise<string>;
50
+ /** Mint a user creds file for an agent {@link Identity} (its stable id+seed from
51
+ * {@link newIdentity}). The account signing key signs over ONLY the public key
52
+ * (`fromPublic`) — the agent seed is never part of the signature, it's only folded into
53
+ * the resulting creds file. The "agent" profile is scoped to publish only as itself and
54
+ * only to its declared channels (the channel-restriction enforcement); "manager" and
55
+ * "observer" stay permissive here and are scoped in steps 6–7. */
56
+ export declare function mintCreds(auth: SpaceAuth, identity: Identity, profile: Profile, opts?: MintOpts): Promise<string>;
57
+ /** Render the `nats-server` config that trusts this space's operator and serves its
58
+ * accounts via the in-config MEMORY resolver. */
59
+ export declare function serverConfig(auth: SpaceAuth, opts: {
60
+ port?: number;
61
+ storeDir: string;
62
+ }): string;
63
+ export declare function authDir(root: string): string;
64
+ /** Persist the space trust material. The file holds the signing seed — treat as a secret. */
65
+ export declare function saveSpaceAuth(dir: string, auth: SpaceAuth): void;
66
+ /** Load the space trust material, or undefined if auth was never set up here. */
67
+ export declare function loadSpaceAuth(dir: string): SpaceAuth | undefined;
68
+ //# sourceMappingURL=provision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provision.d.ts","sourceRoot":"","sources":["../src/provision.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C;;;mEAGmE;AACnE,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AAEjE;+DAC+D;AAC/D,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7F,GAAG,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CACnC;AAYD,4FAA4F;AAC5F,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CA6BvE;AAED,mDAAmD;AACnD,MAAM,WAAW,QAAQ;IACvB;;uFAEmF;IACnF,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;qFACqF;AACrF,MAAM,WAAW,kBAAkB;IACjC,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAED;;0EAE0E;AAC1E,wBAAsB,cAAc,CAClC,WAAW,EAAE,kBAAkB,EAC/B,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,QAAQ,EAClB,IAAI,GAAE,QAAa,GAClB,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED;;;;;mEAKmE;AACnE,wBAAsB,SAAS,CAC7B,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,OAAO,EAChB,IAAI,GAAE,QAAa,GAClB,OAAO,CAAC,MAAM,CAAC,CAYjB;AAmHD;kDACkD;AAClD,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAa/F;AAMD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,6FAA6F;AAC7F,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,CAGhE;AAED,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAIhE"}
@@ -0,0 +1,204 @@
1
+ /**
2
+ * The provisioner — the signer capability for a space.
3
+ *
4
+ * A space is one NATS *account*; every agent is a *user* in it. This module mints the
5
+ * decentralized-JWT trust chain (operator → account → user) programmatically with
6
+ * `@nats-io/jwt`, so there is no dependency on the external `nsc` CLI and the signing
7
+ * key stays in one place (whoever holds {@link SpaceAuth.account.signingSeed}).
8
+ *
9
+ * Demo-1 stage: out-of-band mint. `cotal up` creates the space's trust material
10
+ * once and writes a `nats-server` config (operator + system account + MEMORY resolver);
11
+ * `cotal mint` and the manager load that material and mint per-agent creds files. There
12
+ * is no connect-time token exchange yet (that's the later auth-callout stage).
13
+ *
14
+ * NOT yet provided (our job, not nsc's): credential revocation and an issuance audit
15
+ * trail. Revocation is deferred past Demo 1; minted creds currently have no TTL.
16
+ */
17
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
18
+ import { join } from "node:path";
19
+ import { encodeOperator, encodeAccount, encodeUser, fmtCreds, } from "@nats-io/jwt";
20
+ import { createOperator, createAccount, fromPublic, fromSeed } from "@nats-io/nkeys";
21
+ import { token, spacePrefix, chatSubject, unicastSubject, anycastSubject, controlServiceSubject, chatStream, dmStream, taskStream, chatDurable, dmDurable, taskDurable, presenceBucket, } from "./subjects.js";
22
+ // Unlimited account limits — without explicit limits a JWT account defaults to 0 conns
23
+ // (every connect denied). JetStream needs storage on the data account but MUST stay off
24
+ // the system account (the server refuses to start otherwise).
25
+ const BASE_LIMITS = {
26
+ subs: -1, conn: -1, leaf: -1, imports: -1, exports: -1,
27
+ data: -1, payload: -1, wildcards: true,
28
+ };
29
+ const DATA_LIMITS = { ...BASE_LIMITS, mem_storage: -1, disk_storage: -1 };
30
+ const SYS_LIMITS = { ...BASE_LIMITS, mem_storage: 0, disk_storage: 0 };
31
+ /** Generate a fresh operator → account(+signing key) → system-account chain for a space. */
32
+ export async function createSpaceAuth(space) {
33
+ const okp = createOperator();
34
+ const akp = createAccount();
35
+ const askp = createAccount(); // account signing key — what mints users
36
+ const syskp = createAccount();
37
+ const sysPub = syskp.getPublicKey();
38
+ const operatorJwt = await encodeOperator(`cotal-${token(space)}`, okp, { system_account: sysPub });
39
+ const accountJwt = await encodeAccount(token(space), akp, { signing_keys: [askp.getPublicKey()], limits: DATA_LIMITS }, { signer: okp });
40
+ const sysJwt = await encodeAccount("SYS", syskp, { limits: SYS_LIMITS }, { signer: okp });
41
+ const dec = (u) => new TextDecoder().decode(u);
42
+ return {
43
+ space,
44
+ operator: { seed: dec(okp.getSeed()), jwt: operatorJwt },
45
+ account: {
46
+ pub: akp.getPublicKey(),
47
+ seed: dec(akp.getSeed()),
48
+ jwt: accountJwt,
49
+ signingSeed: dec(askp.getSeed()),
50
+ signingPub: askp.getPublicKey(),
51
+ },
52
+ sys: { pub: sysPub, jwt: sysJwt },
53
+ };
54
+ }
55
+ /** Onboard an agent for launch (auth mode): pre-create its bind-only DM (+ role TASK) durables
56
+ * and mint its scoped creds. The single shared step so every launcher — the manager and
57
+ * `cotal spawn` alike — provisions identically (manager not special). */
58
+ export async function provisionAgent(provisioner, auth, identity, opts = {}) {
59
+ await provisioner.provisionDmInbox(identity.id);
60
+ if (opts.role)
61
+ await provisioner.provisionTaskQueue(opts.role);
62
+ return mintCreds(auth, identity, "agent", opts);
63
+ }
64
+ /** Mint a user creds file for an agent {@link Identity} (its stable id+seed from
65
+ * {@link newIdentity}). The account signing key signs over ONLY the public key
66
+ * (`fromPublic`) — the agent seed is never part of the signature, it's only folded into
67
+ * the resulting creds file. The "agent" profile is scoped to publish only as itself and
68
+ * only to its declared channels (the channel-restriction enforcement); "manager" and
69
+ * "observer" stay permissive here and are scoped in steps 6–7. */
70
+ export async function mintCreds(auth, identity, profile, opts = {}) {
71
+ const signer = fromSeed(new TextEncoder().encode(auth.account.signingSeed));
72
+ const perms = permissionsFor(profile, auth.space, identity.id, opts);
73
+ const userJwt = await encodeUser(profile, fromPublic(identity.id), fromPublic(auth.account.pub), perms, { signer });
74
+ const creds = fmtCreds(userJwt, fromSeed(new TextEncoder().encode(identity.seed)));
75
+ return new TextDecoder().decode(creds);
76
+ }
77
+ /** Build the NATS user permission object for a profile: a default-deny allow-list scoped to
78
+ * exactly what each profile does. "manager" stays permissive (the privileged provisioner
79
+ * host). Subject/stream/durable names come from the shared builders so the ACLs can't drift
80
+ * from the wire layout. */
81
+ function permissionsFor(profile, space, id, opts) {
82
+ if (profile === "manager")
83
+ return {}; // privileged: allow-all defaults
84
+ const CHAT = chatStream(space), DM = dmStream(space), TASK = taskStream(space);
85
+ const KV = `KV_${presenceBucket(space)}`;
86
+ const inbox = `_INBOX_${id}.>`;
87
+ if (profile === "observer" || profile === "admin") {
88
+ // Read-only: live feed via tap, history + presence via ephemeral/ordered consumers it
89
+ // creates on CHAT + the presence KV. No chat/inst/svc/ctl publish → can't post.
90
+ // observer — sub chat.> only; DM_<space>/svc never named → DMs + anycast structurally
91
+ // invisible (step-6 inbox scoping means it can't sniff deliveries either).
92
+ // admin — sub widened to the whole space so the dashboard's tap also sees DMs (inst.>)
93
+ // and anycast (svc.>) live, PLUS DM-stream read verbs so it can backfill DM history.
94
+ // A deliberate god-view: DMs are plaintext + ACL-gated, so mint this only for a trusted
95
+ // audit dashboard. CONSUMER.CREATE on DM_<space> is the DM-confidentiality surface —
96
+ // granted here ONLY for this elevated read-only profile, never to agents.
97
+ const sub = profile === "admin"
98
+ ? [`${spacePrefix(space)}.>`, inbox]
99
+ : [`${spacePrefix(space)}.chat.>`, inbox];
100
+ const allow = [
101
+ "$JS.API.INFO",
102
+ `$JS.API.STREAM.INFO.${CHAT}`,
103
+ `$JS.API.STREAM.INFO.${KV}`,
104
+ // ephemeral backlog consumer (channelHistory): a multi-filter create can't encode its
105
+ // filter in the subject → bare form; the .> form covers named consumers.
106
+ `$JS.API.CONSUMER.CREATE.${CHAT}`,
107
+ `$JS.API.CONSUMER.CREATE.${CHAT}.>`,
108
+ `$JS.API.CONSUMER.INFO.${CHAT}.>`,
109
+ `$JS.API.CONSUMER.MSG.NEXT.${CHAT}.>`,
110
+ `$JS.ACK.${CHAT}.>`,
111
+ `$JS.API.CONSUMER.CREATE.${KV}.>`, // kv.watch ordered consumer (roster is public)
112
+ `$JS.API.CONSUMER.INFO.${KV}.>`,
113
+ "$JS.FC.>", // ordered-consumer flow control
114
+ ];
115
+ if (profile === "admin") {
116
+ // DM history backfill (dmHistory): same bare-form gotcha as CHAT — filter_subjects is
117
+ // plural so the create lands on the bare subject; the .> form covers named consumers.
118
+ allow.push(`$JS.API.STREAM.INFO.${DM}`, `$JS.API.CONSUMER.CREATE.${DM}`, `$JS.API.CONSUMER.CREATE.${DM}.>`, `$JS.API.CONSUMER.INFO.${DM}.>`, `$JS.API.CONSUMER.MSG.NEXT.${DM}.>`, `$JS.ACK.${DM}.>`);
119
+ }
120
+ return { sub: { allow: sub }, pub: { allow } };
121
+ }
122
+ // ---- agent ----
123
+ const channels = opts.channels?.length ? opts.channels : ["general"];
124
+ const manager = opts.manager ?? "manager";
125
+ const chatD = chatDurable(id), dmD = dmDurable(id);
126
+ const svcD = opts.role ? taskDurable(opts.role) : undefined;
127
+ const pubAllow = [
128
+ // peer subjects — identity + channel scope (step 5), built from the real builders.
129
+ ...channels.map((ch) => chatSubject(space, id, ch)),
130
+ unicastSubject(space, "*", id), // inst.*.<id> — DM any instance, as me
131
+ anycastSubject(space, "*", id), // svc.*.<id> — anycast any role, as me
132
+ controlServiceSubject(space, manager, id), // ctl.<mgr>.<id>
133
+ // JetStream control plane — scoped to this agent's own streams/durables.
134
+ "$JS.API.INFO",
135
+ `$JS.API.STREAM.INFO.${CHAT}`, `$JS.API.STREAM.INFO.${DM}`, `$JS.API.STREAM.INFO.${TASK}`, `$JS.API.STREAM.INFO.${KV}`,
136
+ // CHAT consumer: self-create (chat is world-readable, so name-scope is enough).
137
+ `$JS.API.CONSUMER.DURABLE.CREATE.${CHAT}.${chatD}`,
138
+ `$JS.API.CONSUMER.INFO.${CHAT}.${chatD}`,
139
+ `$JS.API.CONSUMER.MSG.NEXT.${CHAT}.${chatD}`,
140
+ `$JS.ACK.${CHAT}.${chatD}.>`,
141
+ // DM consumer: BIND ONLY — info/fetch/ack its own pre-created durable, never create.
142
+ `$JS.API.CONSUMER.INFO.${DM}.${dmD}`,
143
+ `$JS.API.CONSUMER.MSG.NEXT.${DM}.${dmD}`,
144
+ `$JS.ACK.${DM}.${dmD}.>`,
145
+ // Presence: watch (read, public roster) + flow control + PUT OWN KEY ONLY.
146
+ `$JS.API.CONSUMER.CREATE.${KV}.>`,
147
+ `$JS.API.CONSUMER.INFO.${KV}.>`,
148
+ "$JS.FC.>",
149
+ `$KV.${presenceBucket(space)}.${id}`, // own presence key only — can't spoof peers
150
+ ];
151
+ if (svcD) {
152
+ // TASK consumer: BIND ONLY its own role's pre-created durable (svc_<role>). Like DM, the
153
+ // create-time filter_subject isn't reliably ACL-constrainable, so no create path is
154
+ // allowed — the privileged provisioner pre-creates svc_<role> filtered to svc.<role>.*.
155
+ pubAllow.push(`$JS.API.CONSUMER.INFO.${TASK}.${svcD}`, `$JS.API.CONSUMER.MSG.NEXT.${TASK}.${svcD}`, `$JS.ACK.${TASK}.${svcD}.>`);
156
+ }
157
+ // Explicit create-deny (defense-in-depth over default-deny) on the two streams whose
158
+ // create-time filter_subject is the attack surface — DM (private content) and TASK
159
+ // (cross-role work-stealing). Covers the bare ephemeral form (no trailing token), the
160
+ // named/new-API form, and the old durable form. No create path on either stream.
161
+ const pubDeny = [
162
+ `$JS.API.CONSUMER.CREATE.${DM}`,
163
+ `$JS.API.CONSUMER.CREATE.${DM}.>`,
164
+ `$JS.API.CONSUMER.DURABLE.CREATE.${DM}.>`,
165
+ `$JS.API.CONSUMER.CREATE.${TASK}`,
166
+ `$JS.API.CONSUMER.CREATE.${TASK}.>`,
167
+ `$JS.API.CONSUMER.DURABLE.CREATE.${TASK}.>`,
168
+ ];
169
+ return { pub: { allow: pubAllow, deny: pubDeny }, sub: { allow: [inbox] } };
170
+ }
171
+ /** Render the `nats-server` config that trusts this space's operator and serves its
172
+ * accounts via the in-config MEMORY resolver. */
173
+ export function serverConfig(auth, opts) {
174
+ const port = opts.port ?? 4222;
175
+ return `# Generated by \`cotal up\` — do not edit by hand.
176
+ port: ${port}
177
+ jetstream { store_dir: ${JSON.stringify(opts.storeDir)} }
178
+ operator: ${auth.operator.jwt}
179
+ system_account: ${auth.sys.pub}
180
+ resolver: MEMORY
181
+ resolver_preload: {
182
+ ${auth.account.pub}: ${auth.account.jwt}
183
+ ${auth.sys.pub}: ${auth.sys.jwt}
184
+ }
185
+ `;
186
+ }
187
+ // ---- persistence (.cotal/auth) ------------------------------------------------
188
+ const AUTH_FILE = "auth.json";
189
+ export function authDir(root) {
190
+ return join(root, ".cotal", "auth");
191
+ }
192
+ /** Persist the space trust material. The file holds the signing seed — treat as a secret. */
193
+ export function saveSpaceAuth(dir, auth) {
194
+ mkdirSync(dir, { recursive: true });
195
+ writeFileSync(join(dir, AUTH_FILE), JSON.stringify(auth, null, 2), { mode: 0o600 });
196
+ }
197
+ /** Load the space trust material, or undefined if auth was never set up here. */
198
+ export function loadSpaceAuth(dir) {
199
+ const f = join(dir, AUTH_FILE);
200
+ if (!existsSync(f))
201
+ return undefined;
202
+ return JSON.parse(readFileSync(f, "utf8"));
203
+ }
204
+ //# sourceMappingURL=provision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provision.js","sourceRoot":"","sources":["../src/provision.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A typed registry of extensions, keyed by `kind:name`. Implementations
3
+ * **self-register** their extensions (connectors, commands …) as a side-effect of
4
+ * import; composition roots just import the packages they want and then resolve.
5
+ *
6
+ * Core stays ignorant of which extensions exist: they plug into the registry, not
7
+ * the other way round. An unknown extension throws — no silent fallback.
8
+ */
9
+ export interface Extension {
10
+ readonly kind: string;
11
+ readonly name: string;
12
+ }
13
+ export declare class Registry {
14
+ #private;
15
+ /** Register one or more extensions. A duplicate `kind:name` throws. */
16
+ register(...exts: Extension[]): void;
17
+ /** Resolve one extension by kind + name. Unknown throws. */
18
+ resolve<T extends Extension>(kind: T["kind"], name: string): T;
19
+ /** Every registered extension of a kind (e.g. all commands, for CLI dispatch). */
20
+ all<T extends Extension>(kind: T["kind"]): T[];
21
+ }
22
+ /** The process-wide registry. Implementations self-register into it on import. */
23
+ export declare const registry: Registry;
24
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,QAAQ;;IAGnB,uEAAuE;IACvE,QAAQ,CAAC,GAAG,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI;IAQpC,4DAA4D;IAC5D,OAAO,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC;IAM9D,kFAAkF;IAClF,GAAG,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;CAG/C;AAED,kFAAkF;AAClF,eAAO,MAAM,QAAQ,UAAiB,CAAC"}
@@ -0,0 +1,26 @@
1
+ export class Registry {
2
+ #byKey = new Map();
3
+ /** Register one or more extensions. A duplicate `kind:name` throws. */
4
+ register(...exts) {
5
+ for (const ext of exts) {
6
+ const key = `${ext.kind}:${ext.name}`;
7
+ if (this.#byKey.has(key))
8
+ throw new Error(`extension already registered: ${key}`);
9
+ this.#byKey.set(key, ext);
10
+ }
11
+ }
12
+ /** Resolve one extension by kind + name. Unknown throws. */
13
+ resolve(kind, name) {
14
+ const ext = this.#byKey.get(`${kind}:${name}`);
15
+ if (!ext)
16
+ throw new Error(`no ${kind} registered for "${name}"`);
17
+ return ext;
18
+ }
19
+ /** Every registered extension of a kind (e.g. all commands, for CLI dispatch). */
20
+ all(kind) {
21
+ return [...this.#byKey.values()].filter((e) => e.kind === kind);
22
+ }
23
+ }
24
+ /** The process-wide registry. Implementations self-register into it on import. */
25
+ export const registry = new Registry();
26
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAaA,MAAM,OAAO,QAAQ;IACnB,MAAM,GAAG,IAAI,GAAG,EAAqB,CAAC;IAEtC,uEAAuE;IACvE,QAAQ,CAAC,GAAG,IAAiB;QAC3B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;YACtC,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,EAAE,CAAC,CAAC;YAClF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,OAAO,CAAsB,IAAe,EAAE,IAAY;QACxD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,oBAAoB,IAAI,GAAG,CAAC,CAAC;QACjE,OAAO,GAAQ,CAAC;IAClB,CAAC;IAED,kFAAkF;IAClF,GAAG,CAAsB,IAAe;QACtC,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAQ,CAAC;IACzE,CAAC;CACF;AAED,kFAAkF;AAClF,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { type ConsumerConfig, type JetStreamManager } from "@nats-io/jetstream";
2
+ /**
3
+ * Create (idempotently) the three backing streams for a space — CHAT (multicast backlog +
4
+ * history), DM (per-instance inboxes), TASK (anycast work queue).
5
+ *
6
+ * This is **privileged**: under auth mode `STREAM.CREATE` is denied to regular agents
7
+ * (streams are space infrastructure, not per-agent), so it runs once at setup
8
+ * (`cotal up`) or from a permissive endpoint. The single source of the stream
9
+ * definitions, shared by the endpoint and the setup path so they can't diverge.
10
+ */
11
+ export declare function createSpaceStreams(jsm: JetStreamManager, space: string): Promise<void>;
12
+ /**
13
+ * The DM inbox durable for an instance — ONE definition, used both by the privileged
14
+ * pre-create (manager/provisioner, auth mode) and the endpoint's open-mode self-create, so
15
+ * an idempotent re-add can never error on a config delta. The `filter_subject` binds the
16
+ * durable to inst.<id>.* — only the privileged creator sets it, which is the whole point:
17
+ * an agent can't create a durable filtered to someone else's inbox.
18
+ *
19
+ * `inactive_threshold` is set ONLY when the caller passes one — i.e. the open-mode
20
+ * self-create, where the agent owns the durable and a threshold cleanly retires its inbox
21
+ * after it departs. The privileged auth pre-create OMITS it: the agent BINDS-only and is
22
+ * denied CONSUMER.CREATE, so a threshold would retire the durable before a late/relaunched
23
+ * agent binds it, and the bind would then fail permanently ("consumer not found"). Persisting
24
+ * it is the price of bind-only; explicit cleanup on agent-stop is a follow-up.
25
+ */
26
+ export declare function dmDurableConfig(space: string, id: string, opts?: {
27
+ ackWaitMs?: number;
28
+ inactiveThresholdMs?: number;
29
+ }): Partial<ConsumerConfig>;
30
+ /**
31
+ * The TASK work-queue durable for a role — ONE definition, shared by the privileged
32
+ * pre-create (auth mode) and the endpoint's open-mode self-create. The durable is shared
33
+ * across all instances of a role (queue group); the privileged creator sets the
34
+ * filter_subject to svc.<role>.* so an agent can't bind a consumer filtered to another
35
+ * role's queue (the same create-time-filter attack surface as DM). Idempotent per role.
36
+ */
37
+ export declare function taskDurableConfig(space: string, role: string, opts?: {
38
+ ackWaitMs?: number;
39
+ }): Partial<ConsumerConfig>;
40
+ /** Connect with the given (privileged) creds, create the space's streams, and disconnect.
41
+ * Used by `cotal up` to pre-create streams once at setup. */
42
+ export declare function setupSpaceStreams(opts: {
43
+ servers: string;
44
+ space: string;
45
+ creds: string;
46
+ }): Promise<void>;
47
+ //# sourceMappingURL=streams.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streams.d.ts","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAkB5B;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,gBAAgB,EACrB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAsBf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9D,OAAO,CAAC,cAAc,CAAC,CAUzB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAChC,OAAO,CAAC,cAAc,CAAC,CAOzB;AAED;8DAC8D;AAC9D,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,IAAI,CAAC,CAahB"}