agentic-canvas 0.0.0-stage → 0.2.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.
@@ -0,0 +1,164 @@
1
+ import {
2
+ Board,
3
+ CanvasError,
4
+ drawFlow,
5
+ principalSchema,
6
+ readBoard,
7
+ toSvg
8
+ } from "./chunk-GGQPE65T.js";
9
+ import {
10
+ FileStoreError
11
+ } from "./chunk-NWQED42L.js";
12
+
13
+ // src/service.ts
14
+ import * as Y from "yjs";
15
+ import { assertId, StoreConflictError } from "agentic-identity";
16
+ var COLLECTION = "boards";
17
+ var MAX_LIVE_BYTES = 1572864;
18
+ var BOARD_ID = /^[A-Za-z0-9_-]{1,64}$/;
19
+ function toBase64(bytes) {
20
+ let text = "";
21
+ for (let i = 0; i < bytes.length; i += 8192) text += String.fromCharCode(...bytes.subarray(i, i + 8192));
22
+ return btoa(text);
23
+ }
24
+ var fromBase64 = (text) => Uint8Array.from(atob(text), (c) => c.charCodeAt(0));
25
+ function boardKey(id) {
26
+ try {
27
+ if (!BOARD_ID.test(id)) throw new Error();
28
+ assertId(id);
29
+ } catch {
30
+ throw new CanvasError("INVALID", "board id must be 1 to 64 of A-Z, a-z, 0-9, _ and -, and not like __x__");
31
+ }
32
+ return id;
33
+ }
34
+ function storeFailure(error) {
35
+ if (error instanceof StoreConflictError) throw new CanvasError("UNAVAILABLE", "the board changed at the same time. Try again");
36
+ if (error instanceof FileStoreError) throw new CanvasError(error.code === "LOCKED" ? "UNAVAILABLE" : "INVALID", error.message);
37
+ throw error;
38
+ }
39
+ var CanvasService = class {
40
+ store;
41
+ constructor(store) {
42
+ this.store = { ...store, transaction: (fn) => store.transaction(fn).catch(storeFailure) };
43
+ }
44
+ async create(actor, options = {}) {
45
+ const id = boardKey(options.id ?? crypto.randomUUID());
46
+ const board = new Board();
47
+ if (options.title !== void 0) board.setTitle(options.title, actor);
48
+ return this.store.transaction(async (tx) => {
49
+ if (await tx.get(COLLECTION, id)) throw new CanvasError("DUPLICATE_ID", `board "${id}" already exists`);
50
+ await tx.put(COLLECTION, id, { update: toBase64(board.encode()) });
51
+ return { id, title: board.title };
52
+ });
53
+ }
54
+ /** Throws NOT_FOUND unless the board exists. A live room opens only for a board that exists. */
55
+ async assertExists(id) {
56
+ boardKey(id);
57
+ if (!await this.store.transaction((tx) => tx.get(COLLECTION, id))) throw new CanvasError("NOT_FOUND", `no board "${id}"`);
58
+ }
59
+ async read(id) {
60
+ boardKey(id);
61
+ return this.store.transaction(async (tx) => ({ id, ...readBoard(await this.load(tx.get(COLLECTION, id), id)) }));
62
+ }
63
+ /** Run `change` on the board and save it, all inside one transaction. */
64
+ async change(id, change) {
65
+ boardKey(id);
66
+ return this.store.transaction(async (tx) => {
67
+ const board = await this.load(tx.get(COLLECTION, id), id);
68
+ const result = change(board);
69
+ await tx.put(COLLECTION, id, { update: toBase64(board.encode()) });
70
+ return result;
71
+ });
72
+ }
73
+ add(id, shape, actor) {
74
+ return this.change(id, (b) => b.addShape(shape, actor));
75
+ }
76
+ connect(id, connector, actor) {
77
+ return this.change(id, (b) => b.addConnector(connector, actor));
78
+ }
79
+ move(id, elementId, dx, dy, actor) {
80
+ return this.change(id, (b) => ({ moved: b.move(elementId, dx, dy, actor) }));
81
+ }
82
+ remove(id, elementId) {
83
+ return this.change(id, (b) => ({ removed: b.remove(elementId) }));
84
+ }
85
+ flow(id, text, actor, options) {
86
+ return this.change(id, (b) => drawFlow(b, text, actor, options));
87
+ }
88
+ async exportSvg(id) {
89
+ boardKey(id);
90
+ return this.store.transaction(async (tx) => toSvg(await this.load(tx.get(COLLECTION, id), id)));
91
+ }
92
+ /** The stored board as a Yjs update. A client applies it to a room to seed it: the room does not read it. */
93
+ async state(id) {
94
+ boardKey(id);
95
+ return this.store.transaction(async (tx) => (await this.load(tx.get(COLLECTION, id), id)).encode());
96
+ }
97
+ /**
98
+ * Merge a live room's state into the stored board. `authors` are the principals the room saw send updates.
99
+ * An element stamped by anyone else is deleted, and a changed author is put back (see `Board.restamp`),
100
+ * so a client that writes raw Yjs cannot put another author on its work. Broken entries are repaired and
101
+ * a board past the element cap is refused. Returns the ids the checks fixed.
102
+ *
103
+ * Limits: the save corrects the stored board only. The room keeps what its clients wrote, so a client that sees
104
+ * a non-empty `fixed` must join again, or its later edits are lost at the next save. The room remembers its
105
+ * authors for good, so a client may stamp an element with the name of anyone who once sent an update in that room.
106
+ * A client that adds a root type of its own makes every later save of that room fail, because the room keeps it.
107
+ */
108
+ async checkpoint(id, update, actor, authors = []) {
109
+ boardKey(id);
110
+ const by = principalSchema.safeParse(actor);
111
+ if (!by.success) throw new CanvasError("INVALID", "the actor must be a valid principal");
112
+ const trusted = (who) => [by.data, ...authors].some((a) => a.kind === who.kind && a.id === who.id);
113
+ return this.store.transaction(async (tx) => {
114
+ const prior = await this.load(tx.get(COLLECTION, id), id);
115
+ const next = prior.clone();
116
+ try {
117
+ Y.applyUpdate(next.doc, update);
118
+ } catch {
119
+ throw new CanvasError("INVALID", "the update is not a Yjs update");
120
+ }
121
+ if (next.doc.store.pendingStructs || next.doc.store.pendingDs) throw new CanvasError("INVALID", "the update is not complete: it needs changes that the board does not have");
122
+ const strange = [...next.doc.share.keys()].filter((key) => key !== "elements" && key !== "meta");
123
+ if (strange.length > 0) throw new CanvasError("INVALID", "the update holds data that is not part of a board");
124
+ const fixed = /* @__PURE__ */ new Set([...next.restamp(prior, trusted), ...next.repair()]);
125
+ const saved = next.encode();
126
+ if (saved.length > MAX_LIVE_BYTES) throw new CanvasError("LIMIT", `the board would hold over ${MAX_LIVE_BYTES} bytes`);
127
+ await tx.put(COLLECTION, id, { update: toBase64(saved) });
128
+ return { elements: next.list().length, fixed: [...fixed].sort() };
129
+ });
130
+ }
131
+ /** Owner only. Repair a board and delete the named elements. Only a human actor may call it. */
132
+ async repair(id, actor, drop = []) {
133
+ const by = principalSchema.safeParse(actor);
134
+ if (!by.success) throw new CanvasError("INVALID", "the actor must be a valid principal");
135
+ if (by.data.kind !== "human") throw new CanvasError("FORBIDDEN", "only a human owner can repair a board");
136
+ boardKey(id);
137
+ return this.store.transaction(async (tx) => {
138
+ const doc = await tx.get(COLLECTION, id);
139
+ if (!doc) throw new CanvasError("NOT_FOUND", `no board "${id}"`);
140
+ const board = decodeStored(doc, id, Board.open);
141
+ const fixed = board.repair(drop);
142
+ await tx.put(COLLECTION, id, { update: toBase64(board.encode()) });
143
+ return { id, fixed, elements: board.list().length };
144
+ });
145
+ }
146
+ async load(read, id) {
147
+ const doc = await read;
148
+ if (!doc) throw new CanvasError("NOT_FOUND", `no board "${id}"`);
149
+ return decodeStored(doc, id, Board.decode);
150
+ }
151
+ };
152
+ function decodeStored(doc, id, decode) {
153
+ try {
154
+ return decode(fromBase64(String(doc.update)));
155
+ } catch (error) {
156
+ if (error instanceof CanvasError) throw error;
157
+ throw new CanvasError("INVALID", `stored board "${id}" is damaged`);
158
+ }
159
+ }
160
+
161
+ export {
162
+ MAX_LIVE_BYTES,
163
+ CanvasService
164
+ };
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ActorConfigError,
4
+ BRAND,
5
+ operatorActor,
6
+ runMcp,
7
+ svgToPng
8
+ } from "./chunk-KEMOXKBY.js";
9
+ import {
10
+ openLocalStore
11
+ } from "./chunk-UKST5A57.js";
12
+ import {
13
+ CanvasService
14
+ } from "./chunk-W7NRP3EX.js";
15
+ import {
16
+ CanvasError
17
+ } from "./chunk-GGQPE65T.js";
18
+ import "./chunk-NWQED42L.js";
19
+
20
+ // src/cli.ts
21
+ import { readFileSync, writeFileSync } from "fs";
22
+ import { parseArgs } from "util";
23
+ var UsageError = class extends Error {
24
+ };
25
+ var HELP = `${BRAND.command} ${BRAND.version}
26
+ ${BRAND.description}
27
+
28
+ Usage: ${BRAND.command} [--dir <path>] <command> [flags]
29
+
30
+ Commands:
31
+ new [--id <id>] [--title <t>] Make a board.
32
+ read <board> Print the board as JSON.
33
+ add <board> --type <t> --x <n> --y <n> Add a shape. Also: --w --h --text --fill --stroke --src --id.
34
+ connect <board> <from> <to> [--text <t>] Join two shapes with an arrow.
35
+ move <board> <element> --dx <n> --dy <n> Move an element or a group.
36
+ rm <board> <element> Delete an element.
37
+ flow <board> --text <t> | --file <p> Draw a flowchart from text ("A -> B: label"). Use --file - for stdin.
38
+ export <board> [--format svg|png] [--out <path>] Print SVG, or write PNG to --out.
39
+ repair <board> [--drop <element>]... Owner only: fix a broken or over-cap board. Needs CANVAS_ACTOR=human:<id>.
40
+ mcp Serve these as MCP tools on stdio. Needs CANVAS_ACTOR.
41
+
42
+ Live mode: set CANVAS_SERVER=<worker url> and CANVAS_TOKEN=<bearer token>. Every command then runs in the live room of
43
+ the board on that server, so a person and an agent can edit one board at once. The token sets the actor, the kind,
44
+ and the role, so CANVAS_ACTOR is not needed. If it is set, it must match the token. --dir, CANVAS_DIR, and repair are local only.
45
+
46
+ Write commands need an actor in CANVAS_ACTOR as <kind>:<id>, such as "agent:helper".
47
+ The operator sets it. A caller cannot pick its own kind: there is no --actor flag.
48
+ Kinds: human, agent_of_human, agent_of_agent, agent, user_token.
49
+ The store folder is --dir, or CANVAS_DIR, or ./.agentic-canvas.`;
50
+ var flags = {
51
+ dir: { type: "string" },
52
+ id: { type: "string" },
53
+ title: { type: "string" },
54
+ type: { type: "string" },
55
+ x: { type: "string" },
56
+ y: { type: "string" },
57
+ w: { type: "string" },
58
+ h: { type: "string" },
59
+ text: { type: "string" },
60
+ fill: { type: "string" },
61
+ stroke: { type: "string" },
62
+ src: { type: "string" },
63
+ dx: { type: "string" },
64
+ dy: { type: "string" },
65
+ file: { type: "string" },
66
+ prefix: { type: "string" },
67
+ format: { type: "string" },
68
+ out: { type: "string" },
69
+ drop: { type: "string", multiple: true },
70
+ help: { type: "boolean" },
71
+ version: { type: "boolean" }
72
+ };
73
+ var num = (v) => v === void 0 ? void 0 : v.trim() === "" ? Number.NaN : Number(v);
74
+ function joinNegatives(argv) {
75
+ const out = [];
76
+ for (let i = 0; i < argv.length; i++) {
77
+ const next = argv[i + 1] ?? "";
78
+ if (argv[i].startsWith("--") && !argv[i].includes("=") && /^-\d/.test(next)) out.push(`${argv[i]}=${argv[++i]}`);
79
+ else out.push(argv[i]);
80
+ }
81
+ return out;
82
+ }
83
+ async function run(argv) {
84
+ const { values: v, positionals } = parseArgs({ args: joinNegatives(argv), options: flags, allowPositionals: true });
85
+ if (v.version) return void console.log(BRAND.version);
86
+ const [command, board, ...rest] = positionals;
87
+ if (v.help || command === void 0) return void console.log(HELP);
88
+ const need = (count, shape) => {
89
+ if (positionals.length !== count + 1) throw new UsageError(`usage: ${BRAND.command} ${command} ${shape}`);
90
+ };
91
+ const server = process.env.CANVAS_SERVER;
92
+ let live;
93
+ let service;
94
+ if (server) {
95
+ if (v.dir !== void 0 || process.env.CANVAS_DIR) throw new UsageError("--dir and CANVAS_DIR are local only: CANVAS_SERVER is set, so the board lives on the server");
96
+ const token = process.env.CANVAS_TOKEN;
97
+ if (!token) throw new UsageError("CANVAS_SERVER needs CANVAS_TOKEN");
98
+ if (command === "repair") throw new UsageError("repair runs on the local store only. Unset CANVAS_SERVER.");
99
+ const { LiveService, whoAmI } = await import("./live.js");
100
+ service = new LiveService({ server, token });
101
+ const claimed = process.env.CANVAS_ACTOR;
102
+ if (claimed || command !== "read" && command !== "export") live = (await whoAmI(server, token)).actor;
103
+ if (live && claimed && claimed !== `${live.kind}:${live.id}`) throw new UsageError(`CANVAS_ACTOR "${claimed}" does not match the token (${live.kind}:${live.id}). Unset CANVAS_ACTOR.`);
104
+ } else {
105
+ service = new CanvasService(openLocalStore(v.dir ?? process.env.CANVAS_DIR ?? ".agentic-canvas"));
106
+ }
107
+ const actor = () => {
108
+ if (live) return live;
109
+ try {
110
+ return operatorActor();
111
+ } catch (error) {
112
+ if (error instanceof ActorConfigError) throw new UsageError(`"${command}" needs an actor: ${error.message}`);
113
+ throw error;
114
+ }
115
+ };
116
+ const print = (value) => console.log(JSON.stringify(value));
117
+ switch (command) {
118
+ case "new":
119
+ need(0, "[--id <id>] [--title <t>]");
120
+ return print(await service.create(actor(), { id: v.id, title: v.title }));
121
+ case "read":
122
+ need(1, "<board>");
123
+ return print(await service.read(board));
124
+ case "add":
125
+ need(1, "<board> --type <t> --x <n> --y <n>");
126
+ return print(await service.add(board, { id: v.id, type: v.type, x: num(v.x), y: num(v.y), w: num(v.w), h: num(v.h), text: v.text, fill: v.fill, stroke: v.stroke, src: v.src }, actor()));
127
+ case "connect":
128
+ need(3, "<board> <from> <to> [--text <t>]");
129
+ return print(await service.connect(board, { id: v.id, from: { id: rest[0] }, to: { id: rest[1] }, text: v.text }, actor()));
130
+ case "move":
131
+ need(2, "<board> <element> --dx <n> --dy <n>");
132
+ return print(await service.move(board, rest[0], num(v.dx) ?? Number.NaN, num(v.dy) ?? Number.NaN, actor()));
133
+ case "rm":
134
+ need(2, "<board> <element>");
135
+ return print(await service.remove(board, rest[0]));
136
+ case "flow": {
137
+ need(1, "<board> --text <t> | --file <path>");
138
+ if (v.text === void 0 === (v.file === void 0)) throw new UsageError("flow needs exactly one of --text and --file");
139
+ const text = v.text ?? readFileSync(v.file === "-" ? 0 : v.file, "utf8");
140
+ return print(await service.flow(board, text, actor(), { idPrefix: v.prefix }));
141
+ }
142
+ case "export": {
143
+ need(1, "<board> [--format svg|png] [--out <path>]");
144
+ const format = v.format ?? "svg";
145
+ if (format !== "svg" && format !== "png") throw new UsageError(`format must be svg or png, got "${format}"`);
146
+ if (format === "png" && !v.out) throw new UsageError("png is binary: pass --out <path>");
147
+ const data = format === "svg" ? await service.exportSvg(board) : await svgToPng(await service.exportSvg(board));
148
+ if (!v.out) return void process.stdout.write(`${data}
149
+ `);
150
+ writeFileSync(v.out, data);
151
+ return print({ out: v.out, format, bytes: data.length });
152
+ }
153
+ case "repair":
154
+ need(1, "<board> [--drop <element>]...");
155
+ return print(await service.repair(board, actor(), v.drop ?? []));
156
+ case "mcp":
157
+ need(0, "");
158
+ return runMcp(service, actor());
159
+ default:
160
+ throw new UsageError(`unknown command "${command}"`);
161
+ }
162
+ }
163
+ run(process.argv.slice(2)).catch((error) => {
164
+ const code = error.code ?? "";
165
+ if (error instanceof UsageError || code.startsWith("ERR_PARSE_ARGS")) {
166
+ console.error(`${error.message}
167
+ Run "${BRAND.command} --help" for usage.`);
168
+ process.exitCode = 2;
169
+ } else if (error instanceof CanvasError) {
170
+ console.error(`${error.code}: ${error.message}`);
171
+ process.exitCode = 1;
172
+ } else {
173
+ console.error(error instanceof Error ? error.message : String(error));
174
+ process.exitCode = 1;
175
+ }
176
+ });
@@ -0,0 +1,46 @@
1
+ import { B as Board, P as Principal, a as BoardAccess } from './service-CJTe-uBI.js';
2
+ export { b as BoardData, d as CanvasElement, e as CanvasError, c as CanvasService, C as Connector, D as DrawOptions, E as EdgeData, f as Endpoint, g as ErrorCode, F as FlowEdge, h as FlowGraph, i as FlowNode, G as Group, M as MAX_ELEMENTS, j as MAX_FLOW_EDGES, k as MAX_FLOW_NODES, l as MAX_TEXT, N as NodeData, m as Placement, n as SHAPE_TYPES, S as Shape, o as ShapeType, p as drawFlow, q as layoutFlow, r as parseFlow, s as principalSchema, t as readBoard } from './service-CJTe-uBI.js';
3
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ export { FileStoreOptions, createFileStore, openLocalStore } from './store-file.js';
5
+ export { PRINCIPAL_KINDS, PrincipalKind, isMachineMade } from 'agentic-identity';
6
+ import 'yjs';
7
+ import 'zod';
8
+
9
+ declare const VERSION: string;
10
+
11
+ declare const BRAND: {
12
+ readonly command: "agentic-canvas";
13
+ readonly description: "A shared whiteboard for diagrams, drawn by people and agents.";
14
+ readonly version: string;
15
+ };
16
+
17
+ /** Draw the board, back to front, on a white background. */
18
+ declare function toSvg(board: Board): string;
19
+
20
+ /**
21
+ * Draw an SVG to PNG in a headless Chromium, through the optional
22
+ * `playwright-core` package. The page runs with JavaScript off and service
23
+ * workers blocked, and every network request is aborted, so a script planted in
24
+ * the SVG cannot run and nothing leaves the machine. When the package or the
25
+ * browser is missing, this throws.
26
+ */
27
+ declare function svgToPng(svg: string): Promise<Uint8Array>;
28
+
29
+ /** The operator set CANVAS_ACTOR wrong or not at all. */
30
+ declare class ActorConfigError extends Error {
31
+ }
32
+ /**
33
+ * The actor for every write, from the operator's CANVAS_ACTOR as <kind>:<id>.
34
+ * A caller never picks its own actor or kind. Unset, empty, or bad is an error.
35
+ */
36
+ declare function operatorActor(env?: Record<string, string | undefined>): Principal;
37
+
38
+ /**
39
+ * The tools mirror the CLI. The actor is fixed here by the operator. No tool takes an actor,
40
+ * author, or kind, so a caller cannot choose who it writes as.
41
+ */
42
+ declare function createMcpServer(service: BoardAccess, actor: Principal): McpServer;
43
+ /** Serve the tools on stdin and stdout. Stdout carries protocol frames only. */
44
+ declare function runMcp(service: BoardAccess, actor: Principal): Promise<void>;
45
+
46
+ export { ActorConfigError, BRAND, Board, Principal, VERSION, createMcpServer, operatorActor, runMcp, svgToPng, toSvg };
package/dist/index.js ADDED
@@ -0,0 +1,61 @@
1
+ import {
2
+ ActorConfigError,
3
+ BRAND,
4
+ VERSION,
5
+ createMcpServer,
6
+ operatorActor,
7
+ runMcp,
8
+ svgToPng
9
+ } from "./chunk-KEMOXKBY.js";
10
+ import {
11
+ createFileStore,
12
+ openLocalStore
13
+ } from "./chunk-UKST5A57.js";
14
+ import {
15
+ CanvasService
16
+ } from "./chunk-W7NRP3EX.js";
17
+ import {
18
+ Board,
19
+ CanvasError,
20
+ MAX_ELEMENTS,
21
+ MAX_FLOW_EDGES,
22
+ MAX_FLOW_NODES,
23
+ MAX_TEXT,
24
+ PRINCIPAL_KINDS,
25
+ SHAPE_TYPES,
26
+ drawFlow,
27
+ isMachineMade,
28
+ layoutFlow,
29
+ parseFlow,
30
+ principalSchema,
31
+ readBoard,
32
+ toSvg
33
+ } from "./chunk-GGQPE65T.js";
34
+ import "./chunk-NWQED42L.js";
35
+ export {
36
+ ActorConfigError,
37
+ BRAND,
38
+ Board,
39
+ CanvasError,
40
+ CanvasService,
41
+ MAX_ELEMENTS,
42
+ MAX_FLOW_EDGES,
43
+ MAX_FLOW_NODES,
44
+ MAX_TEXT,
45
+ PRINCIPAL_KINDS,
46
+ SHAPE_TYPES,
47
+ VERSION,
48
+ createFileStore,
49
+ createMcpServer,
50
+ drawFlow,
51
+ isMachineMade,
52
+ layoutFlow,
53
+ openLocalStore,
54
+ operatorActor,
55
+ parseFlow,
56
+ principalSchema,
57
+ readBoard,
58
+ runMcp,
59
+ svgToPng,
60
+ toSvg
61
+ };
@@ -0,0 +1,21 @@
1
+ import { createDurableObjectRealtime } from 'agentic-realtime/worker';
2
+ export { SyncRoom } from 'agentic-realtime/worker';
3
+ import { WorkerEnv } from './worker.js';
4
+ import 'agentic-identity';
5
+ import 'agentic-realtime';
6
+ import 'agentic-identity/cloudflare';
7
+ import 'hono';
8
+ import './service-CJTe-uBI.js';
9
+ import 'yjs';
10
+ import 'zod';
11
+
12
+ type Namespace = Parameters<typeof createDurableObjectRealtime>[0];
13
+ interface LiveEnv extends WorkerEnv {
14
+ /** Durable Object namespace of the live rooms. Bind `SyncRoom` to it. */
15
+ SYNC_ROOM?: Namespace;
16
+ }
17
+ declare const _default: {
18
+ fetch: (request: Request, env: LiveEnv, ctx: unknown) => Response | Promise<Response>;
19
+ };
20
+
21
+ export { type LiveEnv, _default as default };
@@ -0,0 +1,25 @@
1
+ import {
2
+ createApp
3
+ } from "./chunk-V477KSZQ.js";
4
+ import "./chunk-W7NRP3EX.js";
5
+ import {
6
+ CanvasError
7
+ } from "./chunk-GGQPE65T.js";
8
+ import "./chunk-NWQED42L.js";
9
+
10
+ // src/live-worker.ts
11
+ import { parseRegion } from "agentic-identity";
12
+ import { createDurableObjectRealtime } from "agentic-realtime/worker";
13
+ import { SyncRoom } from "agentic-realtime/worker";
14
+ var app = createApp({
15
+ live: (env) => {
16
+ const rooms = env.SYNC_ROOM;
17
+ if (!rooms) throw new CanvasError("UNAVAILABLE", "bind a Durable Object namespace as SYNC_ROOM");
18
+ return createDurableObjectRealtime(rooms, parseRegion(env.CANVAS_REGION ?? "") === "eu" ? "eu" : void 0);
19
+ }
20
+ });
21
+ var live_worker_default = { fetch: (request, env, ctx) => app.fetch(request, env, ctx) };
22
+ export {
23
+ SyncRoom,
24
+ live_worker_default as default
25
+ };
package/dist/live.d.ts ADDED
@@ -0,0 +1,101 @@
1
+ import { B as Board, P as Principal, a as BoardAccess, b as BoardData, S as Shape, C as Connector, D as DrawOptions } from './service-CJTe-uBI.js';
2
+ import { Peer } from 'agentic-realtime/client';
3
+ import 'yjs';
4
+ import 'zod';
5
+ import 'agentic-identity';
6
+
7
+ interface LiveOptions {
8
+ /** The Worker address, such as `https://canvas.example.com`. */
9
+ server: string;
10
+ /** Board id. */
11
+ board: string;
12
+ /** A bearer token. The server maps it to the actor, the kind, and the role. */
13
+ token: string;
14
+ /** The name other people see in presence. Defaults to the actor id. */
15
+ name?: string;
16
+ /** Reconnect after a drop. Edits made offline merge when the socket returns. */
17
+ reconnect?: {
18
+ initialMs?: number;
19
+ maxMs?: number;
20
+ };
21
+ }
22
+ /** Who the server says the token is. */
23
+ interface Me {
24
+ actor: Principal;
25
+ role: "editor" | "viewer";
26
+ }
27
+ declare function whoAmI(server: string, token: string): Promise<Me>;
28
+ /**
29
+ * One board, open in a live room. The actor of every edit is the one the server reads from the token, so an agent
30
+ * token always writes agent edits. `save` writes the room to the stored board. The server checks the stamps then.
31
+ */
32
+ declare class LiveBoard {
33
+ readonly board: Board;
34
+ readonly me: Me;
35
+ private readonly client;
36
+ private readonly id;
37
+ private readonly options;
38
+ private readonly refusedAtStart;
39
+ private constructor();
40
+ private changed;
41
+ /** The ids of the elements that the last `edit` added, changed, or removed. */
42
+ get touched(): ReadonlySet<string>;
43
+ static join(options: LiveOptions): Promise<LiveBoard>;
44
+ /** The other people and agents in the room. `machine` is true for an agent. */
45
+ get peers(): Peer[];
46
+ /** Why the room dropped an update, in order. */
47
+ get denied(): string[];
48
+ /**
49
+ * Run one change as the token's actor and wait until the room has it. A viewer fails before the change.
50
+ * After a refusal the local copy still holds the change: leave and join again.
51
+ */
52
+ edit<T>(run: (board: Board, actor: Principal) => T): Promise<T>;
53
+ /** Wait until the room has every change made so far. */
54
+ flush(): Promise<void>;
55
+ /**
56
+ * Save the room to the stored board. `fixed` lists the elements the server's checks changed. After that the
57
+ * room still holds the client's version, so leave and join again.
58
+ */
59
+ save(): Promise<{
60
+ elements: number;
61
+ fixed: string[];
62
+ }>;
63
+ leave(): Promise<void>;
64
+ }
65
+ /**
66
+ * The commands of the CLI and the MCP server, run in a live room. Each call joins the room, runs one change as the
67
+ * token's actor, saves to the stored board, and leaves. The `actor` argument is ignored: the server decides who writes.
68
+ * A read shows the room, which the server checks only when it saves: a forged stamp can show until then.
69
+ */
70
+ declare class LiveService implements BoardAccess {
71
+ private readonly options;
72
+ constructor(options: Omit<LiveOptions, "board">);
73
+ private join;
74
+ create(_actor: Principal, options?: {
75
+ id?: string;
76
+ title?: string;
77
+ }): Promise<{
78
+ id: string;
79
+ title: string;
80
+ }>;
81
+ read(id: string): Promise<BoardData & {
82
+ id: string;
83
+ }>;
84
+ exportSvg(id: string): Promise<string>;
85
+ add(id: string, shape: unknown, _actor?: Principal): Promise<Shape>;
86
+ connect(id: string, connector: unknown, _actor?: Principal): Promise<Connector>;
87
+ move(id: string, element: string, dx: number, dy: number, _actor?: Principal): Promise<{
88
+ moved: string[];
89
+ }>;
90
+ remove(id: string, element: string): Promise<{
91
+ removed: string[];
92
+ }>;
93
+ flow(id: string, text: string, _actor?: Principal, options?: DrawOptions): Promise<{
94
+ nodes: string[];
95
+ edges: string[];
96
+ }>;
97
+ private use;
98
+ private write;
99
+ }
100
+
101
+ export { LiveBoard, type LiveOptions, LiveService, type Me, whoAmI };