agentic-canvas 0.1.0 → 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,166 @@
1
+ import {
2
+ CanvasError,
3
+ PRINCIPAL_KINDS,
4
+ SHAPE_TYPES,
5
+ principalSchema
6
+ } from "./chunk-GGQPE65T.js";
7
+
8
+ // src/version.ts
9
+ import { createRequire } from "module";
10
+ var VERSION = true ? "0.2.0" : readPackageVersion();
11
+
12
+ // src/brand.ts
13
+ var BRAND = {
14
+ command: "agentic-canvas",
15
+ description: "A shared whiteboard for diagrams, drawn by people and agents.",
16
+ version: VERSION
17
+ };
18
+
19
+ // src/actor.ts
20
+ var ActorConfigError = class extends Error {
21
+ };
22
+ function operatorActor(env = process.env) {
23
+ const raw = env.CANVAS_ACTOR;
24
+ if (!raw) throw new ActorConfigError("the operator must set CANVAS_ACTOR=<kind>:<id>");
25
+ const at = raw.indexOf(":");
26
+ const parsed = principalSchema.safeParse({ kind: raw.slice(0, at), id: raw.slice(at + 1) });
27
+ if (at < 0 || !parsed.success) {
28
+ throw new ActorConfigError(`bad CANVAS_ACTOR "${raw}": use <kind>:<id>, kind is one of ${PRINCIPAL_KINDS.join(", ")}`);
29
+ }
30
+ return parsed.data;
31
+ }
32
+
33
+ // src/png.ts
34
+ var MAX_SIDE = 16384;
35
+ var MAX_PIXELS = 16e6;
36
+ var TIMEOUT_MS = 2e4;
37
+ async function svgToPng(svg) {
38
+ const size = /^<svg [^>]*?width="(\d+)" height="(\d+)"/.exec(svg);
39
+ const width = Number(size?.[1]);
40
+ const height = Number(size?.[2]);
41
+ if (!(width > 0 && height > 0) || width > MAX_SIDE || height > MAX_SIDE || width * height > MAX_PIXELS) {
42
+ throw new CanvasError("LIMIT", `the board is too large for PNG: up to ${MAX_SIDE} px a side and ${MAX_PIXELS / 1e6} million pixels`);
43
+ }
44
+ let chromium;
45
+ try {
46
+ ({ chromium } = await import("playwright-core"));
47
+ } catch {
48
+ throw new CanvasError("UNAVAILABLE", "PNG export needs the playwright-core package. Install it, or export SVG.");
49
+ }
50
+ const browser = await chromium.launch().catch(() => {
51
+ throw new CanvasError("UNAVAILABLE", "PNG export needs Chromium. Run: npx playwright-core install chromium");
52
+ });
53
+ try {
54
+ const context = await browser.newContext({ viewport: { width, height }, javaScriptEnabled: false, serviceWorkers: "block" });
55
+ context.setDefaultTimeout(TIMEOUT_MS);
56
+ await context.route("**/*", (route) => route.abort());
57
+ const page = await context.newPage();
58
+ await page.setContent(`<!doctype html><body style="margin:0">${svg}</body>`);
59
+ return await page.screenshot({ clip: { x: 0, y: 0, width, height } });
60
+ } finally {
61
+ await browser.close();
62
+ }
63
+ }
64
+
65
+ // src/mcp.ts
66
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
67
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
68
+ import { z } from "zod";
69
+ var json = (value) => ({ content: [{ type: "text", text: JSON.stringify(value) }] });
70
+ function failure(error) {
71
+ if (!(error instanceof CanvasError)) console.error(error instanceof Error ? error.message : String(error));
72
+ const body = error instanceof CanvasError ? { code: error.code, message: error.message } : { code: "INTERNAL", message: "the board operation failed" };
73
+ return { isError: true, content: [{ type: "text", text: JSON.stringify(body) }] };
74
+ }
75
+ function tool(run) {
76
+ return async (args) => {
77
+ try {
78
+ return await run(args);
79
+ } catch (error) {
80
+ return failure(error);
81
+ }
82
+ };
83
+ }
84
+ var board = z.string().describe("Board id.");
85
+ var num = z.number();
86
+ function createMcpServer(service, actor) {
87
+ const server = new McpServer({ name: BRAND.command, version: BRAND.version });
88
+ const read = { readOnlyHint: true };
89
+ server.registerTool(
90
+ "create_board",
91
+ { description: "Make a board.", inputSchema: { id: z.string().optional(), title: z.string().optional() } },
92
+ tool(async (a) => json(await service.create(actor, a)))
93
+ );
94
+ server.registerTool(
95
+ "read_board",
96
+ { description: "Read a board as data: nodes, edges, groups, text, and who made what.", inputSchema: { board }, annotations: read },
97
+ tool(async (a) => json(await service.read(a.board)))
98
+ );
99
+ server.registerTool(
100
+ "add_shape",
101
+ {
102
+ description: "Add a shape. Image shapes need src.",
103
+ inputSchema: {
104
+ board,
105
+ id: z.string().optional(),
106
+ type: z.enum(SHAPE_TYPES),
107
+ x: num,
108
+ y: num,
109
+ w: num.optional(),
110
+ h: num.optional(),
111
+ text: z.string().optional(),
112
+ fill: z.string().optional(),
113
+ stroke: z.string().optional(),
114
+ src: z.string().optional()
115
+ }
116
+ },
117
+ tool(async ({ board: b, ...shape }) => json(await service.add(b, shape, actor)))
118
+ );
119
+ server.registerTool(
120
+ "connect",
121
+ { description: "Join two shapes with an arrow.", inputSchema: { board, from: z.string(), to: z.string(), id: z.string().optional(), text: z.string().optional() } },
122
+ tool(async (a) => json(await service.connect(a.board, { id: a.id, from: { id: a.from }, to: { id: a.to }, text: a.text }, actor)))
123
+ );
124
+ server.registerTool(
125
+ "move",
126
+ { description: "Move an element, or a whole group.", inputSchema: { board, element: z.string(), dx: num, dy: num } },
127
+ tool(async (a) => json(await service.move(a.board, a.element, a.dx, a.dy, actor)))
128
+ );
129
+ server.registerTool(
130
+ "remove",
131
+ { description: "Delete an element. A shape takes its connectors, a group its members.", inputSchema: { board, element: z.string() }, annotations: { destructiveHint: true } },
132
+ tool(async (a) => json(await service.remove(a.board, a.element)))
133
+ );
134
+ server.registerTool(
135
+ "draw_flow",
136
+ {
137
+ description: 'Draw a flowchart from text, one statement per line: "A[Box] -> B{Choice}: label". [] box, () ellipse, {} diamond.',
138
+ inputSchema: { board, text: z.string(), idPrefix: z.string().optional() }
139
+ },
140
+ tool(async (a) => json(await service.flow(a.board, a.text, actor, { idPrefix: a.idPrefix })))
141
+ );
142
+ server.registerTool(
143
+ "export_svg",
144
+ { description: "Export the board as SVG text.", inputSchema: { board }, annotations: read },
145
+ tool(async (a) => ({ content: [{ type: "text", text: await service.exportSvg(a.board) }] }))
146
+ );
147
+ server.registerTool(
148
+ "export_png",
149
+ { description: "Export the board as a PNG image. Needs the optional playwright-core and a Chromium.", inputSchema: { board }, annotations: read },
150
+ tool(async (a) => ({ content: [{ type: "image", data: Buffer.from(await svgToPng(await service.exportSvg(a.board))).toString("base64"), mimeType: "image/png" }] }))
151
+ );
152
+ return server;
153
+ }
154
+ async function runMcp(service, actor) {
155
+ await createMcpServer(service, actor).connect(new StdioServerTransport());
156
+ }
157
+
158
+ export {
159
+ VERSION,
160
+ BRAND,
161
+ ActorConfigError,
162
+ operatorActor,
163
+ svgToPng,
164
+ createMcpServer,
165
+ runMcp
166
+ };
@@ -0,0 +1,13 @@
1
+ // src/store-errors.ts
2
+ var FileStoreError = class extends Error {
3
+ constructor(code, message) {
4
+ super(message);
5
+ this.code = code;
6
+ this.name = "FileStoreError";
7
+ }
8
+ code;
9
+ };
10
+
11
+ export {
12
+ FileStoreError
13
+ };
@@ -1,20 +1,12 @@
1
+ import {
2
+ FileStoreError
3
+ } from "./chunk-NWQED42L.js";
4
+
1
5
  // src/store-file.ts
2
6
  import { randomUUID } from "crypto";
3
7
  import { mkdirSync } from "fs";
4
8
  import { link, open, readFile, rename, unlink } from "fs/promises";
5
9
  import { join } from "path";
6
-
7
- // src/store-errors.ts
8
- var FileStoreError = class extends Error {
9
- constructor(code, message) {
10
- super(message);
11
- this.code = code;
12
- this.name = "FileStoreError";
13
- }
14
- code;
15
- };
16
-
17
- // src/store-file.ts
18
10
  import { assertCollection, assertId, createMemoryStore, parseRegion, StoreConflictError } from "agentic-identity";
19
11
  var MAX_TRANSACTION_RUNS = 50;
20
12
  var clone = (value) => JSON.parse(JSON.stringify(value));
@@ -230,7 +222,6 @@ function openLocalStore(dir) {
230
222
  var STORE_FILE = "canvas.json";
231
223
 
232
224
  export {
233
- FileStoreError,
234
225
  createFileStore,
235
226
  openLocalStore,
236
227
  STORE_FILE