agentic-canvas 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -2
- package/dist/{chunk-KEMOXKBY.js → chunk-AGODNBN7.js} +1 -1
- package/dist/cli.js +1 -1
- package/dist/client.d.ts +108 -0
- package/dist/client.js +78 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -1
- package/dist/live-worker.d.ts +2 -1
- package/dist/live.d.ts +2 -1
- package/dist/{service-CJTe-uBI.d.ts → read-BY6f-mmy.d.ts} +1 -107
- package/dist/service-Cpl6wKeY.d.ts +109 -0
- package/dist/worker.d.ts +2 -1
- package/llms.txt +1 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
A shared whiteboard for diagrams, drawn by people and agents.
|
|
9
9
|
|
|
10
|
-
> Status: 0.
|
|
10
|
+
> Status: 0.3.0. The board model, the agent operations, the exports, the CLI, the
|
|
11
11
|
> MCP server, a small Worker API, and live co-editing work. The CLI, the MCP server,
|
|
12
|
-
> and the SDK can join a live room. The web UI is not built yet.
|
|
12
|
+
> and the SDK can join a live room. A remote client calls the Worker API. The web UI is not built yet.
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
@@ -84,6 +84,33 @@ The flow text takes one statement per line: `A -> B: label`. Use `[text]` for a
|
|
|
84
84
|
box, `(text)` for an ellipse, and `{text}` for a diamond. `direction LR` sets
|
|
85
85
|
the direction. Lines that start with `#` or `//` are comments.
|
|
86
86
|
|
|
87
|
+
### Remote client
|
|
88
|
+
|
|
89
|
+
Call a running Worker from a browser, a Worker, or Node 22. The client uses only `fetch`.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { createClient, CanvasApiError } from "agentic-canvas/client";
|
|
93
|
+
|
|
94
|
+
const canvas = createClient({ baseUrl: "https://canvas.example.com/api/v1/canvas", token });
|
|
95
|
+
await canvas.createBoard({ id: "plan", title: "Plan" });
|
|
96
|
+
await canvas.addShape("plan", { type: "rect", x: 0, y: 0, text: "Start" });
|
|
97
|
+
await canvas.drawFlow("plan", "A -> B: go");
|
|
98
|
+
const board = await canvas.readBoard("plan");
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
await canvas.readBoard("missing");
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (error instanceof CanvasApiError) console.log(error.code, error.status); // NOT_FOUND 404
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
There is one method per route: `me`, `createBoard`, `readBoard`, `exportSvg`, `addShape`, `addConnector`,
|
|
108
|
+
`drawFlow`, `move`, `remove`, `uploadImage`, `getImage`, `state`, and `checkpoint`. Each takes
|
|
109
|
+
`{ signal }` last, so an `AbortSignal` can stop a call. The token sets the actor. A failed call throws
|
|
110
|
+
`CanvasApiError` with the API `code` and the HTTP `status`. A call that gets no reply has code `NETWORK`
|
|
111
|
+
and status 0. The client never retries. A 503 means "try again", and the caller decides, because a
|
|
112
|
+
repeated write can change a board twice. Pass `fetch` to use your own.
|
|
113
|
+
|
|
87
114
|
## Features
|
|
88
115
|
|
|
89
116
|
- Shapes: rectangle, ellipse, diamond, text, sticky note, frame, image.
|
package/dist/cli.js
CHANGED
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { E as ErrorCode, B as BoardData, S as ShapeType, a as Shape, b as Endpoint, C as Connector } from './read-BY6f-mmy.js';
|
|
2
|
+
import { PrincipalKind } from 'agentic-identity';
|
|
3
|
+
import 'yjs';
|
|
4
|
+
import 'zod';
|
|
5
|
+
|
|
6
|
+
interface ClientOptions {
|
|
7
|
+
/** The API root, for example `https://canvas.example.com/api/v1/canvas`. */
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
/** A bearer token. The token sets the actor, the kind, and the role. */
|
|
10
|
+
token: string;
|
|
11
|
+
/** A custom `fetch`. Defaults to the global one. */
|
|
12
|
+
fetch?: typeof fetch;
|
|
13
|
+
}
|
|
14
|
+
/** Options for every call. */
|
|
15
|
+
interface CallOptions {
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
}
|
|
18
|
+
interface ShapeInput {
|
|
19
|
+
id?: string;
|
|
20
|
+
type: ShapeType;
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
w?: number;
|
|
24
|
+
h?: number;
|
|
25
|
+
text?: string;
|
|
26
|
+
fill?: string;
|
|
27
|
+
stroke?: string;
|
|
28
|
+
/** A `data:` URL or a `blob:` ref, for an image. */
|
|
29
|
+
src?: string;
|
|
30
|
+
}
|
|
31
|
+
interface ConnectorInput {
|
|
32
|
+
id?: string;
|
|
33
|
+
from: Endpoint;
|
|
34
|
+
to: Endpoint;
|
|
35
|
+
text?: string;
|
|
36
|
+
startArrow?: boolean;
|
|
37
|
+
endArrow?: boolean;
|
|
38
|
+
stroke?: string;
|
|
39
|
+
}
|
|
40
|
+
interface ImageInfo {
|
|
41
|
+
id: string;
|
|
42
|
+
ref: string;
|
|
43
|
+
type: string;
|
|
44
|
+
size: number;
|
|
45
|
+
/** False when the same bytes were stored before. */
|
|
46
|
+
created: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A failed call. `code` is the API error code, such as `NOT_FOUND`, `UNAUTHORIZED`, or `INVALID`. `status` is the HTTP status.
|
|
50
|
+
* A reply that is not the API's error shape has code `HTTP_ERROR`. A call that never got a reply has code `NETWORK` and status 0.
|
|
51
|
+
*/
|
|
52
|
+
type ApiErrorCode = ErrorCode | "UNAUTHORIZED" | "NOT_CONFIGURED" | "TOO_LARGE" | "UNSUPPORTED_TYPE" | "UPGRADE_REQUIRED" | "INTERNAL" | "NETWORK" | "HTTP_ERROR" | (string & {});
|
|
53
|
+
declare class CanvasApiError extends Error {
|
|
54
|
+
readonly code: ApiErrorCode;
|
|
55
|
+
readonly status: number;
|
|
56
|
+
constructor(code: ApiErrorCode, message: string, status: number);
|
|
57
|
+
}
|
|
58
|
+
interface CanvasClient {
|
|
59
|
+
/** Who the token stands for. */
|
|
60
|
+
me(options?: CallOptions): Promise<{
|
|
61
|
+
actor: string;
|
|
62
|
+
kind: PrincipalKind;
|
|
63
|
+
role: "editor" | "viewer";
|
|
64
|
+
}>;
|
|
65
|
+
createBoard(input?: {
|
|
66
|
+
id?: string;
|
|
67
|
+
title?: string;
|
|
68
|
+
}, options?: CallOptions): Promise<{
|
|
69
|
+
id: string;
|
|
70
|
+
title: string;
|
|
71
|
+
}>;
|
|
72
|
+
readBoard(id: string, options?: CallOptions): Promise<BoardData & {
|
|
73
|
+
id: string;
|
|
74
|
+
}>;
|
|
75
|
+
/** The board as an SVG document. */
|
|
76
|
+
exportSvg(id: string, options?: CallOptions): Promise<string>;
|
|
77
|
+
addShape(id: string, shape: ShapeInput, options?: CallOptions): Promise<Shape>;
|
|
78
|
+
addConnector(id: string, connector: ConnectorInput, options?: CallOptions): Promise<Connector>;
|
|
79
|
+
/** Draw a flowchart from text such as `A -> B: label`. Returns the new ids. */
|
|
80
|
+
drawFlow(id: string, text: string, options?: CallOptions): Promise<{
|
|
81
|
+
nodes: string[];
|
|
82
|
+
edges: string[];
|
|
83
|
+
}>;
|
|
84
|
+
/** Move an element. Returns the ids that moved: a shape takes its connectors with it. */
|
|
85
|
+
move(id: string, element: string, dx: number, dy: number, options?: CallOptions): Promise<{
|
|
86
|
+
moved: string[];
|
|
87
|
+
}>;
|
|
88
|
+
/** Remove an element. Returns the ids removed. */
|
|
89
|
+
remove(id: string, element: string, options?: CallOptions): Promise<{
|
|
90
|
+
removed: string[];
|
|
91
|
+
}>;
|
|
92
|
+
uploadImage(bytes: Uint8Array | ArrayBuffer | Blob, options?: CallOptions): Promise<ImageInfo>;
|
|
93
|
+
/** The bytes of a stored image, and its content type. */
|
|
94
|
+
getImage(id: string, options?: CallOptions): Promise<{
|
|
95
|
+
bytes: Uint8Array;
|
|
96
|
+
type: string;
|
|
97
|
+
}>;
|
|
98
|
+
/** The stored board as a Yjs update. Only a server with live rooms serves it. */
|
|
99
|
+
state(id: string, options?: CallOptions): Promise<Uint8Array>;
|
|
100
|
+
/** Merge a live room's Yjs update into the stored board. Only a server with live rooms serves it. */
|
|
101
|
+
checkpoint(id: string, update: Uint8Array, options?: CallOptions): Promise<{
|
|
102
|
+
elements: number;
|
|
103
|
+
fixed: string[];
|
|
104
|
+
}>;
|
|
105
|
+
}
|
|
106
|
+
declare function createClient(options: ClientOptions): CanvasClient;
|
|
107
|
+
|
|
108
|
+
export { type ApiErrorCode, type CallOptions, CanvasApiError, type CanvasClient, type ClientOptions, type ConnectorInput, type ImageInfo, type ShapeInput, createClient };
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
var CanvasApiError = class extends Error {
|
|
3
|
+
constructor(code, message, status) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.status = status;
|
|
7
|
+
this.name = "CanvasApiError";
|
|
8
|
+
}
|
|
9
|
+
code;
|
|
10
|
+
status;
|
|
11
|
+
};
|
|
12
|
+
function createClient(options) {
|
|
13
|
+
const { token } = options;
|
|
14
|
+
const base = options.baseUrl.replace(/\/+$/, "");
|
|
15
|
+
if (!token) throw new TypeError("token is required");
|
|
16
|
+
const doFetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
17
|
+
const seg = encodeURIComponent;
|
|
18
|
+
async function send(method, path, init, call) {
|
|
19
|
+
const headers = { authorization: `Bearer ${token}` };
|
|
20
|
+
if (init.json !== void 0) headers["content-type"] = "application/json";
|
|
21
|
+
if (init.contentType) headers["content-type"] = init.contentType;
|
|
22
|
+
let response;
|
|
23
|
+
try {
|
|
24
|
+
response = await doFetch(`${base}${path}`, {
|
|
25
|
+
method,
|
|
26
|
+
headers,
|
|
27
|
+
body: init.json === void 0 ? init.body : JSON.stringify(init.json),
|
|
28
|
+
signal: call?.signal
|
|
29
|
+
});
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (call?.signal?.aborted) throw error;
|
|
32
|
+
throw new CanvasApiError("NETWORK", error instanceof Error ? error.message : "the request failed", 0);
|
|
33
|
+
}
|
|
34
|
+
if (response.ok) return response;
|
|
35
|
+
let code = "HTTP_ERROR";
|
|
36
|
+
let message = `the server answered ${response.status}`;
|
|
37
|
+
try {
|
|
38
|
+
const { error } = await response.json();
|
|
39
|
+
if (typeof error?.code === "string") code = error.code;
|
|
40
|
+
if (typeof error?.message === "string") message = error.message;
|
|
41
|
+
} catch {
|
|
42
|
+
if (call?.signal?.aborted) throw call.signal.reason;
|
|
43
|
+
}
|
|
44
|
+
throw new CanvasApiError(code, message, response.status);
|
|
45
|
+
}
|
|
46
|
+
const parse = async (response, call) => {
|
|
47
|
+
try {
|
|
48
|
+
return await response.json();
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (call?.signal?.aborted) throw error;
|
|
51
|
+
throw new CanvasApiError("HTTP_ERROR", "the server did not answer with JSON", response.status);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const json = async (method, path, body, call) => parse(await send(method, path, { json: body }, call), call);
|
|
55
|
+
const bytes = async (response) => new Uint8Array(await response.arrayBuffer());
|
|
56
|
+
return {
|
|
57
|
+
me: (o) => json("GET", "/me", void 0, o),
|
|
58
|
+
createBoard: (input = {}, o) => json("POST", "/boards", input, o),
|
|
59
|
+
readBoard: (id, o) => json("GET", `/boards/${seg(id)}`, void 0, o),
|
|
60
|
+
exportSvg: async (id, o) => (await send("GET", `/boards/${seg(id)}/svg`, {}, o)).text(),
|
|
61
|
+
addShape: (id, shape, o) => json("POST", `/boards/${seg(id)}/shapes`, shape, o),
|
|
62
|
+
addConnector: (id, connector, o) => json("POST", `/boards/${seg(id)}/connectors`, connector, o),
|
|
63
|
+
drawFlow: (id, text, o) => json("POST", `/boards/${seg(id)}/flow`, { text }, o),
|
|
64
|
+
move: (id, element, dx, dy, o) => json("POST", `/boards/${seg(id)}/move`, { element, dx, dy }, o),
|
|
65
|
+
remove: (id, element, o) => json("DELETE", `/boards/${seg(id)}/elements/${seg(element)}`, void 0, o),
|
|
66
|
+
uploadImage: async (data, o) => parse(await send("POST", "/images", { body: data, contentType: "application/octet-stream" }, o), o),
|
|
67
|
+
getImage: async (id, o) => {
|
|
68
|
+
const response = await send("GET", `/images/${seg(id)}`, {}, o);
|
|
69
|
+
return { bytes: await bytes(response), type: response.headers.get("content-type") ?? "application/octet-stream" };
|
|
70
|
+
},
|
|
71
|
+
state: async (id, o) => bytes(await send("GET", `/boards/${seg(id)}/state`, {}, o)),
|
|
72
|
+
checkpoint: async (id, update, o) => parse(await send("POST", `/boards/${seg(id)}/checkpoint`, { body: update, contentType: "application/octet-stream" }, o), o)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
CanvasApiError,
|
|
77
|
+
createClient
|
|
78
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { c as Board, P as Principal } from './read-BY6f-mmy.js';
|
|
2
|
+
export { B as BoardData, d as CanvasElement, e as CanvasError, C as Connector, f as EdgeData, b as Endpoint, E as ErrorCode, G as Group, M as MAX_ELEMENTS, g as MAX_TEXT, N as NodeData, h as SHAPE_TYPES, a as Shape, S as ShapeType, p as principalSchema, r as readBoard } from './read-BY6f-mmy.js';
|
|
3
|
+
import { B as BoardAccess } from './service-Cpl6wKeY.js';
|
|
4
|
+
export { C as CanvasService, D as DrawOptions, F as FlowEdge, a as FlowGraph, b as FlowNode, M as MAX_FLOW_EDGES, c as MAX_FLOW_NODES, P as Placement, d as drawFlow, l as layoutFlow, p as parseFlow } from './service-Cpl6wKeY.js';
|
|
3
5
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
6
|
export { FileStoreOptions, createFileStore, openLocalStore } from './store-file.js';
|
|
5
7
|
export { PRINCIPAL_KINDS, PrincipalKind, isMachineMade } from 'agentic-identity';
|
package/dist/index.js
CHANGED
package/dist/live-worker.d.ts
CHANGED
|
@@ -5,9 +5,10 @@ import 'agentic-identity';
|
|
|
5
5
|
import 'agentic-realtime';
|
|
6
6
|
import 'agentic-identity/cloudflare';
|
|
7
7
|
import 'hono';
|
|
8
|
-
import './
|
|
8
|
+
import './read-BY6f-mmy.js';
|
|
9
9
|
import 'yjs';
|
|
10
10
|
import 'zod';
|
|
11
|
+
import './service-Cpl6wKeY.js';
|
|
11
12
|
|
|
12
13
|
type Namespace = Parameters<typeof createDurableObjectRealtime>[0];
|
|
13
14
|
interface LiveEnv extends WorkerEnv {
|
package/dist/live.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as Board, P as Principal, B as BoardData, a as Shape, C as Connector } from './read-BY6f-mmy.js';
|
|
2
2
|
import { Peer } from 'agentic-realtime/client';
|
|
3
|
+
import { B as BoardAccess, D as DrawOptions } from './service-Cpl6wKeY.js';
|
|
3
4
|
import 'yjs';
|
|
4
5
|
import 'zod';
|
|
5
6
|
import 'agentic-identity';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as Y from 'yjs';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { Store } from 'agentic-identity';
|
|
4
3
|
|
|
5
4
|
declare const principalSchema: z.ZodObject<{
|
|
6
5
|
kind: z.ZodEnum<["human", "agent_of_human", "agent_of_agent", "agent", "user_token"]>;
|
|
@@ -143,52 +142,6 @@ declare class Board {
|
|
|
143
142
|
private write;
|
|
144
143
|
}
|
|
145
144
|
|
|
146
|
-
declare const MAX_FLOW_NODES = 500;
|
|
147
|
-
declare const MAX_FLOW_EDGES = 2000;
|
|
148
|
-
interface FlowNode {
|
|
149
|
-
id: string;
|
|
150
|
-
text: string;
|
|
151
|
-
shape: Extract<ShapeType, "rect" | "ellipse" | "diamond">;
|
|
152
|
-
}
|
|
153
|
-
interface FlowEdge {
|
|
154
|
-
from: string;
|
|
155
|
-
to: string;
|
|
156
|
-
text: string;
|
|
157
|
-
}
|
|
158
|
-
interface FlowGraph {
|
|
159
|
-
direction: "TB" | "LR";
|
|
160
|
-
nodes: FlowNode[];
|
|
161
|
-
edges: FlowEdge[];
|
|
162
|
-
}
|
|
163
|
-
declare function parseFlow(source: string): FlowGraph;
|
|
164
|
-
interface Placement {
|
|
165
|
-
id: string;
|
|
166
|
-
x: number;
|
|
167
|
-
y: number;
|
|
168
|
-
w: number;
|
|
169
|
-
h: number;
|
|
170
|
-
}
|
|
171
|
-
/** Layered layout. The top-left of the result is (0, 0). The layout breaks cycles and handles self-loops. */
|
|
172
|
-
declare function layoutFlow(graph: FlowGraph): Placement[];
|
|
173
|
-
interface DrawOptions {
|
|
174
|
-
/**
|
|
175
|
-
* Put in front of every id. Default: a new random prefix for each call, so two agents that draw at
|
|
176
|
-
* the same time never write the same id. A prefix you pass is used as given, and it makes ids
|
|
177
|
-
* repeatable. Up to 16 of A-Z, a-z, 0-9, _ and -.
|
|
178
|
-
*/
|
|
179
|
-
idPrefix?: string;
|
|
180
|
-
/** Top-left of the drawing. Default: below the shapes already on the board. */
|
|
181
|
-
origin?: {
|
|
182
|
-
x: number;
|
|
183
|
-
y: number;
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
/** Draw a flow on the board. All or nothing: on any error the board stays as it was. */
|
|
187
|
-
declare function drawFlow(board: Board, source: string, actor: Principal, options?: DrawOptions): {
|
|
188
|
-
nodes: string[];
|
|
189
|
-
edges: string[];
|
|
190
|
-
};
|
|
191
|
-
|
|
192
145
|
interface Labels {
|
|
193
146
|
/** The author is an agent or acts for one. */
|
|
194
147
|
machineMade: boolean;
|
|
@@ -211,63 +164,4 @@ interface BoardData {
|
|
|
211
164
|
/** The board as plain data for an agent. Reads only. Embedded image bytes are left out. */
|
|
212
165
|
declare function readBoard(board: Board): BoardData;
|
|
213
166
|
|
|
214
|
-
|
|
215
|
-
declare class CanvasService {
|
|
216
|
-
private readonly store;
|
|
217
|
-
constructor(store: Store);
|
|
218
|
-
create(actor: Principal, options?: {
|
|
219
|
-
id?: string;
|
|
220
|
-
title?: string;
|
|
221
|
-
}): Promise<{
|
|
222
|
-
id: string;
|
|
223
|
-
title: string;
|
|
224
|
-
}>;
|
|
225
|
-
/** Throws NOT_FOUND unless the board exists. A live room opens only for a board that exists. */
|
|
226
|
-
assertExists(id: string): Promise<void>;
|
|
227
|
-
read(id: string): Promise<BoardData & {
|
|
228
|
-
id: string;
|
|
229
|
-
}>;
|
|
230
|
-
/** Run `change` on the board and save it, all inside one transaction. */
|
|
231
|
-
change<R>(id: string, change: (board: Board) => R): Promise<R>;
|
|
232
|
-
add(id: string, shape: unknown, actor: Principal): Promise<Shape>;
|
|
233
|
-
connect(id: string, connector: unknown, actor: Principal): Promise<Connector>;
|
|
234
|
-
move(id: string, elementId: string, dx: number, dy: number, actor: Principal): Promise<{
|
|
235
|
-
moved: string[];
|
|
236
|
-
}>;
|
|
237
|
-
remove(id: string, elementId: string): Promise<{
|
|
238
|
-
removed: string[];
|
|
239
|
-
}>;
|
|
240
|
-
flow(id: string, text: string, actor: Principal, options?: DrawOptions): Promise<{
|
|
241
|
-
nodes: string[];
|
|
242
|
-
edges: string[];
|
|
243
|
-
}>;
|
|
244
|
-
exportSvg(id: string): Promise<string>;
|
|
245
|
-
/** The stored board as a Yjs update. A client applies it to a room to seed it: the room does not read it. */
|
|
246
|
-
state(id: string): Promise<Uint8Array>;
|
|
247
|
-
/**
|
|
248
|
-
* Merge a live room's state into the stored board. `authors` are the principals the room saw send updates.
|
|
249
|
-
* An element stamped by anyone else is deleted, and a changed author is put back (see `Board.restamp`),
|
|
250
|
-
* so a client that writes raw Yjs cannot put another author on its work. Broken entries are repaired and
|
|
251
|
-
* a board past the element cap is refused. Returns the ids the checks fixed.
|
|
252
|
-
*
|
|
253
|
-
* Limits: the save corrects the stored board only. The room keeps what its clients wrote, so a client that sees
|
|
254
|
-
* a non-empty `fixed` must join again, or its later edits are lost at the next save. The room remembers its
|
|
255
|
-
* authors for good, so a client may stamp an element with the name of anyone who once sent an update in that room.
|
|
256
|
-
* A client that adds a root type of its own makes every later save of that room fail, because the room keeps it.
|
|
257
|
-
*/
|
|
258
|
-
checkpoint(id: string, update: Uint8Array, actor: Principal, authors?: Principal[]): Promise<{
|
|
259
|
-
elements: number;
|
|
260
|
-
fixed: string[];
|
|
261
|
-
}>;
|
|
262
|
-
/** Owner only. Repair a board and delete the named elements. Only a human actor may call it. */
|
|
263
|
-
repair(id: string, actor: Principal, drop?: string[]): Promise<{
|
|
264
|
-
id: string;
|
|
265
|
-
fixed: string[];
|
|
266
|
-
elements: number;
|
|
267
|
-
}>;
|
|
268
|
-
private load;
|
|
269
|
-
}
|
|
270
|
-
/** What the CLI and the MCP server need. `CanvasService` runs it on a store, and `LiveService` runs it in a live room. */
|
|
271
|
-
type BoardAccess = Pick<CanvasService, "create" | "read" | "add" | "connect" | "move" | "remove" | "flow" | "exportSvg">;
|
|
272
|
-
|
|
273
|
-
export { Board as B, type Connector as C, type DrawOptions as D, type EdgeData as E, type FlowEdge as F, type Group as G, MAX_ELEMENTS as M, type NodeData as N, type Principal as P, type Shape as S, type BoardAccess as a, type BoardData as b, CanvasService as c, type CanvasElement as d, CanvasError as e, type Endpoint as f, type ErrorCode as g, type FlowGraph as h, type FlowNode as i, MAX_FLOW_EDGES as j, MAX_FLOW_NODES as k, MAX_TEXT as l, type Placement as m, SHAPE_TYPES as n, type ShapeType as o, drawFlow as p, layoutFlow as q, parseFlow as r, principalSchema as s, readBoard as t };
|
|
167
|
+
export { type BoardData as B, type Connector as C, type ErrorCode as E, type Group as G, MAX_ELEMENTS as M, type NodeData as N, type Principal as P, type ShapeType as S, type Shape as a, type Endpoint as b, Board as c, type CanvasElement as d, CanvasError as e, type EdgeData as f, MAX_TEXT as g, SHAPE_TYPES as h, principalSchema as p, readBoard as r };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { S as ShapeType, c as Board, P as Principal, B as BoardData, a as Shape, C as Connector } from './read-BY6f-mmy.js';
|
|
2
|
+
import { Store } from 'agentic-identity';
|
|
3
|
+
|
|
4
|
+
declare const MAX_FLOW_NODES = 500;
|
|
5
|
+
declare const MAX_FLOW_EDGES = 2000;
|
|
6
|
+
interface FlowNode {
|
|
7
|
+
id: string;
|
|
8
|
+
text: string;
|
|
9
|
+
shape: Extract<ShapeType, "rect" | "ellipse" | "diamond">;
|
|
10
|
+
}
|
|
11
|
+
interface FlowEdge {
|
|
12
|
+
from: string;
|
|
13
|
+
to: string;
|
|
14
|
+
text: string;
|
|
15
|
+
}
|
|
16
|
+
interface FlowGraph {
|
|
17
|
+
direction: "TB" | "LR";
|
|
18
|
+
nodes: FlowNode[];
|
|
19
|
+
edges: FlowEdge[];
|
|
20
|
+
}
|
|
21
|
+
declare function parseFlow(source: string): FlowGraph;
|
|
22
|
+
interface Placement {
|
|
23
|
+
id: string;
|
|
24
|
+
x: number;
|
|
25
|
+
y: number;
|
|
26
|
+
w: number;
|
|
27
|
+
h: number;
|
|
28
|
+
}
|
|
29
|
+
/** Layered layout. The top-left of the result is (0, 0). The layout breaks cycles and handles self-loops. */
|
|
30
|
+
declare function layoutFlow(graph: FlowGraph): Placement[];
|
|
31
|
+
interface DrawOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Put in front of every id. Default: a new random prefix for each call, so two agents that draw at
|
|
34
|
+
* the same time never write the same id. A prefix you pass is used as given, and it makes ids
|
|
35
|
+
* repeatable. Up to 16 of A-Z, a-z, 0-9, _ and -.
|
|
36
|
+
*/
|
|
37
|
+
idPrefix?: string;
|
|
38
|
+
/** Top-left of the drawing. Default: below the shapes already on the board. */
|
|
39
|
+
origin?: {
|
|
40
|
+
x: number;
|
|
41
|
+
y: number;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Draw a flow on the board. All or nothing: on any error the board stays as it was. */
|
|
45
|
+
declare function drawFlow(board: Board, source: string, actor: Principal, options?: DrawOptions): {
|
|
46
|
+
nodes: string[];
|
|
47
|
+
edges: string[];
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** One place for every board operation. The CLI, the MCP server, and the Worker call it. */
|
|
51
|
+
declare class CanvasService {
|
|
52
|
+
private readonly store;
|
|
53
|
+
constructor(store: Store);
|
|
54
|
+
create(actor: Principal, options?: {
|
|
55
|
+
id?: string;
|
|
56
|
+
title?: string;
|
|
57
|
+
}): Promise<{
|
|
58
|
+
id: string;
|
|
59
|
+
title: string;
|
|
60
|
+
}>;
|
|
61
|
+
/** Throws NOT_FOUND unless the board exists. A live room opens only for a board that exists. */
|
|
62
|
+
assertExists(id: string): Promise<void>;
|
|
63
|
+
read(id: string): Promise<BoardData & {
|
|
64
|
+
id: string;
|
|
65
|
+
}>;
|
|
66
|
+
/** Run `change` on the board and save it, all inside one transaction. */
|
|
67
|
+
change<R>(id: string, change: (board: Board) => R): Promise<R>;
|
|
68
|
+
add(id: string, shape: unknown, actor: Principal): Promise<Shape>;
|
|
69
|
+
connect(id: string, connector: unknown, actor: Principal): Promise<Connector>;
|
|
70
|
+
move(id: string, elementId: string, dx: number, dy: number, actor: Principal): Promise<{
|
|
71
|
+
moved: string[];
|
|
72
|
+
}>;
|
|
73
|
+
remove(id: string, elementId: string): Promise<{
|
|
74
|
+
removed: string[];
|
|
75
|
+
}>;
|
|
76
|
+
flow(id: string, text: string, actor: Principal, options?: DrawOptions): Promise<{
|
|
77
|
+
nodes: string[];
|
|
78
|
+
edges: string[];
|
|
79
|
+
}>;
|
|
80
|
+
exportSvg(id: string): Promise<string>;
|
|
81
|
+
/** The stored board as a Yjs update. A client applies it to a room to seed it: the room does not read it. */
|
|
82
|
+
state(id: string): Promise<Uint8Array>;
|
|
83
|
+
/**
|
|
84
|
+
* Merge a live room's state into the stored board. `authors` are the principals the room saw send updates.
|
|
85
|
+
* An element stamped by anyone else is deleted, and a changed author is put back (see `Board.restamp`),
|
|
86
|
+
* so a client that writes raw Yjs cannot put another author on its work. Broken entries are repaired and
|
|
87
|
+
* a board past the element cap is refused. Returns the ids the checks fixed.
|
|
88
|
+
*
|
|
89
|
+
* Limits: the save corrects the stored board only. The room keeps what its clients wrote, so a client that sees
|
|
90
|
+
* a non-empty `fixed` must join again, or its later edits are lost at the next save. The room remembers its
|
|
91
|
+
* authors for good, so a client may stamp an element with the name of anyone who once sent an update in that room.
|
|
92
|
+
* A client that adds a root type of its own makes every later save of that room fail, because the room keeps it.
|
|
93
|
+
*/
|
|
94
|
+
checkpoint(id: string, update: Uint8Array, actor: Principal, authors?: Principal[]): Promise<{
|
|
95
|
+
elements: number;
|
|
96
|
+
fixed: string[];
|
|
97
|
+
}>;
|
|
98
|
+
/** Owner only. Repair a board and delete the named elements. Only a human actor may call it. */
|
|
99
|
+
repair(id: string, actor: Principal, drop?: string[]): Promise<{
|
|
100
|
+
id: string;
|
|
101
|
+
fixed: string[];
|
|
102
|
+
elements: number;
|
|
103
|
+
}>;
|
|
104
|
+
private load;
|
|
105
|
+
}
|
|
106
|
+
/** What the CLI and the MCP server need. `CanvasService` runs it on a store, and `LiveService` runs it in a live room. */
|
|
107
|
+
type BoardAccess = Pick<CanvasService, "create" | "read" | "add" | "connect" | "move" | "remove" | "flow" | "exportSvg">;
|
|
108
|
+
|
|
109
|
+
export { type BoardAccess as B, CanvasService as C, type DrawOptions as D, type FlowEdge as F, MAX_FLOW_EDGES as M, type Placement as P, type FlowGraph as a, type FlowNode as b, MAX_FLOW_NODES as c, drawFlow as d, layoutFlow as l, parseFlow as p };
|
package/dist/worker.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { Blobs } from 'agentic-identity';
|
|
|
2
2
|
import { Realtime } from 'agentic-realtime';
|
|
3
3
|
import { D1Like, R2Like } from 'agentic-identity/cloudflare';
|
|
4
4
|
import { Hono } from 'hono';
|
|
5
|
-
import { P as Principal
|
|
5
|
+
import { P as Principal } from './read-BY6f-mmy.js';
|
|
6
|
+
import { C as CanvasService } from './service-Cpl6wKeY.js';
|
|
6
7
|
import 'yjs';
|
|
7
8
|
import 'zod';
|
|
8
9
|
|
package/llms.txt
CHANGED
|
@@ -8,6 +8,7 @@ Keywords: whiteboard,canvas,diagrams,drawing,collaboration,agents,mcp,cli.
|
|
|
8
8
|
- CLI: agentic-canvas new|read|add|connect|move|rm|flow|export.
|
|
9
9
|
- Live: set CANVAS_SERVER and CANVAS_TOKEN, and the CLI and `agentic-canvas mcp` edit the board in its live room.
|
|
10
10
|
- SDK: import { LiveBoard, LiveService } from "agentic-canvas/live".
|
|
11
|
+
- SDK: import { createClient, CanvasApiError } from "agentic-canvas/client". It calls the Worker API with fetch. Methods: me, createBoard, readBoard, exportSvg, addShape, addConnector, drawFlow, move, remove, uploadImage, getImage, state, checkpoint. A failure throws CanvasApiError with `code` and `status`. No retries.
|
|
11
12
|
|
|
12
13
|
## Docs
|
|
13
14
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-canvas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A shared whiteboard for diagrams, drawn by people and agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"whiteboard",
|
|
@@ -51,6 +51,10 @@
|
|
|
51
51
|
"types": "./dist/live-worker.d.ts",
|
|
52
52
|
"import": "./dist/live-worker.js"
|
|
53
53
|
},
|
|
54
|
+
"./client": {
|
|
55
|
+
"types": "./dist/client.d.ts",
|
|
56
|
+
"import": "./dist/client.js"
|
|
57
|
+
},
|
|
54
58
|
"./file-store": {
|
|
55
59
|
"types": "./dist/store-file.d.ts",
|
|
56
60
|
"import": "./dist/store-file.js"
|