agentic-canvas 0.6.0 → 0.7.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 +49 -16
- package/dist/{chunk-SIDWRC3P.js → chunk-3QW4UF2R.js} +8 -3
- package/dist/{chunk-BIIC2DPZ.js → chunk-EH7WCMK6.js} +59 -8
- package/dist/{chunk-G4YTESPS.js → chunk-GPSOLW2Y.js} +11 -2
- package/dist/chunk-JOBTIAFJ.js +19 -0
- package/dist/{chunk-JCYNPYYJ.js → chunk-ZSPZMGHG.js} +6 -5
- package/dist/cli.js +9 -4
- package/dist/client.d.ts +5 -1
- package/dist/client.js +1 -0
- package/dist/embed.d.ts +1 -1
- package/dist/embed.js +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/live-worker.d.ts +2 -2
- package/dist/live-worker.js +4 -4
- package/dist/live.d.ts +6 -3
- package/dist/live.js +9 -2
- package/dist/{read-BupIJSPJ.d.ts → read-CeWoSAAy.d.ts} +19 -5
- package/dist/{service-CR4-r1yx.d.ts → service-D9WzMLH1.d.ts} +6 -2
- package/dist/ui.css +2 -2
- package/dist/worker.d.ts +17 -6
- package/dist/worker.js +4 -4
- package/llms.txt +3 -3
- package/package.json +2 -2
- package/dist/chunk-SPP6PINW.js +0 -10
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
A shared whiteboard for diagrams, drawn by people and agents.
|
|
9
9
|
|
|
10
|
-
> Status: 0.
|
|
10
|
+
> Status: 0.7.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
12
|
> and the SDK can join a live room. A remote client calls the Worker API. A small web UI, served by the Worker, is new in 0.4.0.
|
|
13
13
|
> The package exports `agentic-canvas/ui`, a board view you can mount in your own app, new in 0.5.0.
|
|
@@ -38,7 +38,7 @@ npx agentic-canvas export demo --out demo.svg
|
|
|
38
38
|
npx agentic-canvas export demo --format png --out demo.png
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
Other commands: `list`, `add`, `connect`, `update`, `move`, `rm
|
|
41
|
+
Other commands: `list`, `add`, `connect`, `update`, `move`, `rm`, `delete` (a whole board). Run `npx agentic-canvas --help`.
|
|
42
42
|
`repair` is for the owner only. It needs `CANVAS_ACTOR=human:<id>`.
|
|
43
43
|
Boards live in `./.agentic-canvas` (or `--dir`, or `CANVAS_DIR`). Every write
|
|
44
44
|
needs an actor in `CANVAS_ACTOR`, as `<kind>:<id>`. The operator sets it. A write
|
|
@@ -52,7 +52,7 @@ CANVAS_ACTOR=agent:helper npx agentic-canvas --dir ./boards mcp
|
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
The server speaks MCP on stdio. It has one tool for each CLI command: `create_board`,
|
|
55
|
-
`list_boards`, `read_board`, `add_shape`, `connect`, `update_element`, `move`, `remove`, `draw_flow`, `export_svg`, and
|
|
55
|
+
`delete_board`, `list_boards`, `read_board`, `add_shape`, `connect`, `update_element`, `move`, `remove`, `draw_flow`, `export_svg`, and
|
|
56
56
|
`export_png`. `repair` is not a tool. Only a human operator runs it, from the CLI. The server will not start unless `CANVAS_ACTOR` is set to a valid
|
|
57
57
|
`<kind>:<id>`. That actor stamps every write. No tool takes an actor, author, or kind, so a
|
|
58
58
|
caller cannot choose who it writes as. An error comes back as a tool error with a code.
|
|
@@ -105,7 +105,7 @@ try {
|
|
|
105
105
|
}
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
There is one method per route: `me`, `listBoards`, `createBoard`, `readBoard`, `exportSvg`, `addShape`, `addConnector`,
|
|
108
|
+
There is one method per route: `me`, `listBoards`, `createBoard`, `readBoard`, `deleteBoard`, `exportSvg`, `addShape`, `addConnector`,
|
|
109
109
|
`drawFlow`, `update`, `move`, `remove`, `uploadImage`, `getImage`, `state`, and `checkpoint`. Each takes
|
|
110
110
|
`{ signal }` last, so an `AbortSignal` can stop a call. The token sets the actor. A failed call throws
|
|
111
111
|
`CanvasApiError` with the API `code` and the HTTP `status`. A call that gets no reply has code `NETWORK`
|
|
@@ -125,12 +125,37 @@ no CI secret is used:
|
|
|
125
125
|
```sh
|
|
126
126
|
npm ci && npm run build
|
|
127
127
|
WRANGLER_SEND_METRICS=false CLOUDFLARE_API_TOKEN=... npx wrangler deploy -c wrangler.demo.jsonc
|
|
128
|
-
npx wrangler secret put CANVAS_API_TOKENS -c wrangler.demo.jsonc # only to set or rotate tokens
|
|
128
|
+
npx wrangler secret put CANVAS_API_TOKENS -c wrangler.demo.jsonc # only to set or rotate the demo tokens
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
`wrangler.demo.jsonc` holds the config. The
|
|
131
|
+
`wrangler.demo.jsonc` holds the config. The demo is a throwaway: it turns on the dev fallback (see "Auth") and
|
|
132
|
+
signs in with a static token list. The tokens are a Worker secret and are not in the repo. To run your
|
|
132
133
|
own copy, create a D1 database and an R2 bucket (both in the EU), put their names and id in a copy of that
|
|
133
|
-
file, and deploy it the same way.
|
|
134
|
+
file, and deploy it the same way. Remove `CANVAS_UNSAFE_STATIC_TOKENS` from your copy unless it is a throwaway.
|
|
135
|
+
|
|
136
|
+
## Auth
|
|
137
|
+
|
|
138
|
+
The Worker signs requests in with `agentic-identity` (`agentic-identity/http`). A bearer key (an `api_key` or
|
|
139
|
+
`pat` credential) is a credential in the same Store as the boards (D1). A `session` credential works as a
|
|
140
|
+
bearer key too, and as the `session` cookie for reads. A write or a socket that rides on the cookie gets 401,
|
|
141
|
+
because the Worker lists no allowed origins. A credential names a principal, and the principal's scopes set the role:
|
|
142
|
+
|
|
143
|
+
- Any scope that covers `canvas:write`, such as `canvas:write`, `canvas:admin`, `*:write`, or `*:admin`, is an editor.
|
|
144
|
+
- Any scope that covers `canvas:read`, such as `canvas:read` or `*:read`, is a viewer. A viewer reads and cannot change a board, over HTTP or in a live room.
|
|
145
|
+
- A principal with neither scope, an unknown, expired, or revoked credential, or a revoked parent in the
|
|
146
|
+
delegation chain, gets 401.
|
|
147
|
+
|
|
148
|
+
Make principals and credentials in the database of this Worker with `agentic-identity` (its Worker, or
|
|
149
|
+
`issueCredential` from its SDK). The two must share one D1 database and its schema (`D1_SCHEMA`). A credential
|
|
150
|
+
that lives in another database gets 401 here. The author kind of every write is the kind of the principal.
|
|
151
|
+
A browser sends its credential as the WebSocket sub-protocol `agentic-canvas.bearer.<token in base64url>`.
|
|
152
|
+
|
|
153
|
+
**Dev fallback.** `CANVAS_API_TOKENS` is a static list of tokens, as JSON: `{ "<token>": { "actor": "ada", "kind": "human", "role": "viewer" } }`.
|
|
154
|
+
It is read only when `CANVAS_UNSAFE_STATIC_TOKENS=1` is set too. Then it adds to the identity credentials: a
|
|
155
|
+
token that is not a credential is looked up in the list. A static token reaches every board and cannot be
|
|
156
|
+
revoked without a new deploy. It ignores revocation in the Store: a static grant named `ada` still writes as
|
|
157
|
+
`ada` after the identity credential of `ada` is revoked. Use it for local work and throwaway demos only. If `CANVAS_API_TOKENS` is set
|
|
158
|
+
and the switch is not, every API request gets 503 `NOT_CONFIGURED`, so a deploy never trusts the list by accident.
|
|
134
159
|
|
|
135
160
|
## UI
|
|
136
161
|
|
|
@@ -146,12 +171,13 @@ npm ci
|
|
|
146
171
|
npm run build # writes the UI to ui-dist/
|
|
147
172
|
cat > .dev.vars <<'EOF2'
|
|
148
173
|
CANVAS_REGION=unknown
|
|
174
|
+
CANVAS_UNSAFE_STATIC_TOKENS=1
|
|
149
175
|
CANVAS_API_TOKENS={"ada-token-0123456789-0123456789-0123456789":{"actor":"ada","kind":"human"}}
|
|
150
176
|
EOF2
|
|
151
177
|
WRANGLER_SEND_METRICS=false npx wrangler dev
|
|
152
178
|
```
|
|
153
179
|
|
|
154
|
-
Open the address that wrangler prints and paste the token. A token has 32 characters or more. Its
|
|
180
|
+
`CANVAS_UNSAFE_STATIC_TOKENS=1` turns on the dev fallback: see "Auth". Open the address that wrangler prints and paste the token. A static token has 32 characters or more. Its
|
|
155
181
|
grant sets the name, the kind (a person or an agent), and the role. A `viewer` token reads and
|
|
156
182
|
cannot change a board. The token stays in the tab's session storage.
|
|
157
183
|
|
|
@@ -239,11 +265,11 @@ at the same time keep the last write. The board list shows the first 100 boards.
|
|
|
239
265
|
- A CLI over a local file store. Several processes can write at once.
|
|
240
266
|
- An MCP server (stdio) over the same service as the CLI.
|
|
241
267
|
- A Worker (`agentic-canvas/worker`) under `/api/v1/canvas`. Boards live in D1 and images in R2
|
|
242
|
-
(EU jurisdiction), through the shared `Store` and `Blobs` ports. Set `
|
|
243
|
-
|
|
244
|
-
Routes: `POST /boards`, `GET /boards/:id`, `GET /boards/:id/svg`, `POST /boards/:id/shapes`,
|
|
268
|
+
(EU jurisdiction), through the shared `Store` and `Blobs` ports. Set `CANVAS_REGION`. Callers sign in with
|
|
269
|
+
`agentic-identity` credentials (see "Auth" below). `CANVAS_API_TOKENS` is a dev fallback only.
|
|
270
|
+
Routes: `POST /boards`, `GET /boards/:id`, `DELETE /boards/:id`, `GET /boards/:id/svg`, `POST /boards/:id/shapes`,
|
|
245
271
|
`/connectors`, `/flow`, `/move`, `GET /boards`, `PATCH /boards/:id/elements/:element`, `DELETE /boards/:id/elements/:element`, `POST /images`,
|
|
246
|
-
`GET /images/:id`. An image is stored under the SHA-256 of its bytes, and the reply gives
|
|
272
|
+
`GET /images/:id`. An editor may delete a board. The Worker also erases its live room. An image is stored under the SHA-256 of its bytes, and the reply gives
|
|
247
273
|
the `blob:` ref for a shape. A busy database or bucket answers 503: try again.
|
|
248
274
|
- Live rooms (`agentic-canvas/live-worker`, on `agentic-realtime`). One Durable Object holds one
|
|
249
275
|
board. A token may carry `"role": "viewer"`: a viewer reads and cannot write. `GET /boards/:id/ws`
|
|
@@ -279,12 +305,20 @@ Every name that each entry point exports is listed here. A name marked (type) ex
|
|
|
279
305
|
| `FlowGraph` (type), `FlowNode` (type), `FlowEdge` (type), `Placement` (type), `DrawOptions` (type) | Types for a flow and its layout. |
|
|
280
306
|
| `readBoard`, `BoardData` (type), `NodeData` (type), `EdgeData` (type) | Read a board as plain data. |
|
|
281
307
|
| `toSvg(board)`, `svgToPng(svg)` | `toSvg` returns the board as SVG. `svgToPng` turns an SVG string into PNG through Chromium. |
|
|
282
|
-
| `PRINCIPAL_KINDS`, `PrincipalKind` (type), `Principal` (type), `principalSchema`, `isMachineMade` | The principal kinds, the schema of a principal, and the rule that marks machine-made kinds. |
|
|
308
|
+
| `PRINCIPAL_KINDS`, `PrincipalKind` (type), `Principal` (type), `principalSchema`, `isMachineMade` | The principal kinds, the schema of a principal, and the rule that marks machine-made kinds. `principalSchema` uses the identity rule below. |
|
|
283
309
|
| `operatorActor(env?)`, `ActorConfigError` | Read the actor from `CANVAS_ACTOR` as `<kind>:<id>`. It throws `ActorConfigError` when the value is missing or bad. |
|
|
284
310
|
| `CanvasService` | The board operations that the CLI, the MCP server and the Worker share. It takes a `Store`. |
|
|
285
311
|
| `createMcpServer(service, actor)`, `runMcp(service, actor)` | Make the MCP server, or run it on stdio. |
|
|
286
312
|
| `createFileStore`, `openLocalStore`, `FileStoreOptions` (type) | Stores on one local file. See `agentic-canvas/file-store`. |
|
|
287
313
|
|
|
314
|
+
### Actor ids
|
|
315
|
+
|
|
316
|
+
One rule names an actor in every new write, in the SDK, the CLI, the MCP server and the Worker. The kind must be
|
|
317
|
+
one of `PRINCIPAL_KINDS`. The id has 1 to 128 characters from `A-Z`, `a-z`, `0-9`, `.`, `_` and `-`, and starts
|
|
318
|
+
with a letter, digit, `_` or `-`. The rule comes from `parsePrincipal` in `agentic-identity`. A write with another
|
|
319
|
+
id fails with `INVALID`. A board that was stored with an older, looser id (up to 200 characters) still opens. The
|
|
320
|
+
old author stays on its elements. Only new writes follow the strict rule.
|
|
321
|
+
|
|
288
322
|
### `agentic-canvas/client`
|
|
289
323
|
|
|
290
324
|
| Name | What it is |
|
|
@@ -342,9 +376,8 @@ The stylesheet is a separate export, `agentic-canvas/ui.css`. Load `agentic-desi
|
|
|
342
376
|
|
|
343
377
|
## Limits
|
|
344
378
|
|
|
345
|
-
- Canvas takes principal kinds, regions, and the Store and Blobs ports from `agentic-identity` 0.
|
|
346
|
-
It does not use the sharing API yet
|
|
347
|
-
`CANVAS_API_TOKENS`. It has no board delete or erase. When it adopts sharing, grants are keyed
|
|
379
|
+
- Canvas takes principal kinds, regions, and the Store and Blobs ports from `agentic-identity` 0.6.
|
|
380
|
+
It does not use the sharing API yet: every principal with `canvas:write` reaches every board. When it adopts sharing, grants are keyed
|
|
348
381
|
on kind and id, and a delete calls `deleteResource`.
|
|
349
382
|
- A board holds at most 10,000 elements. A flow holds at most 500 nodes and 2,000
|
|
350
383
|
edges.
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CanvasError,
|
|
3
3
|
SHAPE_TYPES
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZSPZMGHG.js";
|
|
5
5
|
import {
|
|
6
6
|
PRINCIPAL_KINDS
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-JOBTIAFJ.js";
|
|
8
8
|
|
|
9
9
|
// src/version.ts
|
|
10
10
|
import { createRequire } from "module";
|
|
11
|
-
var VERSION = true ? "0.
|
|
11
|
+
var VERSION = true ? "0.7.0" : readPackageVersion();
|
|
12
12
|
|
|
13
13
|
// src/brand.ts
|
|
14
14
|
var BRAND = {
|
|
@@ -104,6 +104,11 @@ function createMcpServer(service, actor) {
|
|
|
104
104
|
{ description: "Read a board as data: nodes, edges, groups, text, and who made what.", inputSchema: { board }, annotations: read },
|
|
105
105
|
tool(async (a) => json(await service.read(a.board)))
|
|
106
106
|
);
|
|
107
|
+
server.registerTool(
|
|
108
|
+
"delete_board",
|
|
109
|
+
{ description: "Delete a whole board for good. It cannot be undone.", inputSchema: { board }, annotations: { destructiveHint: true } },
|
|
110
|
+
tool(async (a) => json(await service.delete(a.board)))
|
|
111
|
+
);
|
|
107
112
|
server.registerTool(
|
|
108
113
|
"add_shape",
|
|
109
114
|
{
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CanvasService,
|
|
3
3
|
MAX_LIVE_BYTES
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GPSOLW2Y.js";
|
|
5
5
|
import {
|
|
6
6
|
CanvasError
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-ZSPZMGHG.js";
|
|
8
8
|
|
|
9
9
|
// src/worker.ts
|
|
10
|
-
import { hasScope, parsePrincipal, parseRegion } from "agentic-identity";
|
|
10
|
+
import { createStoreAuth, hasScope, parsePrincipal, parseRegion } from "agentic-identity";
|
|
11
11
|
import { createD1Store, createR2Blobs, D1_SCHEMA } from "agentic-identity/cloudflare";
|
|
12
12
|
import { acceptWebSocketProtocol, principalFromWebSocketRequest } from "agentic-identity/http";
|
|
13
13
|
|
|
@@ -1902,6 +1902,9 @@ function remember(id, mark) {
|
|
|
1902
1902
|
if (saved.size > 500) saved.clear();
|
|
1903
1903
|
saved.set(id, mark);
|
|
1904
1904
|
}
|
|
1905
|
+
function forget(id) {
|
|
1906
|
+
saved.delete(id);
|
|
1907
|
+
}
|
|
1905
1908
|
async function pull(live, service, id, force = false) {
|
|
1906
1909
|
const { updates, compactions } = await live.meta(id);
|
|
1907
1910
|
const mark = `${updates}:${compactions}`;
|
|
@@ -1979,8 +1982,41 @@ function tokenAuth(env) {
|
|
|
1979
1982
|
}
|
|
1980
1983
|
};
|
|
1981
1984
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1985
|
+
var staticTokensOn = (env) => env.CANVAS_UNSAFE_STATIC_TOKENS === "1";
|
|
1986
|
+
function workerAuth(env, store) {
|
|
1987
|
+
const auths = [];
|
|
1988
|
+
if (store) auths.push(createStoreAuth(store));
|
|
1989
|
+
if (staticTokensOn(env)) auths.push(tokenAuth(env));
|
|
1990
|
+
let failure;
|
|
1991
|
+
const auth = {
|
|
1992
|
+
async authenticate(request) {
|
|
1993
|
+
for (const auth2 of auths) {
|
|
1994
|
+
let found;
|
|
1995
|
+
try {
|
|
1996
|
+
found = await auth2.authenticate(request);
|
|
1997
|
+
} catch (error) {
|
|
1998
|
+
failure = error;
|
|
1999
|
+
return null;
|
|
2000
|
+
}
|
|
2001
|
+
if (!found) continue;
|
|
2002
|
+
let step;
|
|
2003
|
+
try {
|
|
2004
|
+
step = parsePrincipal({ kind: found.kind, id: found.principalId });
|
|
2005
|
+
} catch {
|
|
2006
|
+
return null;
|
|
2007
|
+
}
|
|
2008
|
+
if (step.kind === SEEDER.kind && step.id === SEEDER.id) return null;
|
|
2009
|
+
return found;
|
|
2010
|
+
}
|
|
2011
|
+
return null;
|
|
2012
|
+
}
|
|
2013
|
+
};
|
|
2014
|
+
return { auth, failure: () => failure };
|
|
2015
|
+
}
|
|
2016
|
+
async function authenticate(request, env, store) {
|
|
2017
|
+
const { auth, failure } = workerAuth(env, store);
|
|
2018
|
+
const found = await principalFromWebSocketRequest(request, auth, { protocol: WS_PROTOCOL, scope: "canvas:read" });
|
|
2019
|
+
if (failure() !== void 0) throw failure();
|
|
1984
2020
|
if (found instanceof Response) return null;
|
|
1985
2021
|
return { actor: { kind: found.kind, id: found.principalId }, role: hasScope(found.scopes, "canvas:write") ? "editor" : "viewer" };
|
|
1986
2022
|
}
|
|
@@ -2061,13 +2097,15 @@ function createApp(options = {}) {
|
|
|
2061
2097
|
return fail("NOT_CONFIGURED", 'set CANVAS_REGION to "eu", "us", or "unknown"', 503);
|
|
2062
2098
|
}
|
|
2063
2099
|
if (!c.env.DB || !c.env.IMAGES) return fail("NOT_CONFIGURED", "bind DB (D1) and IMAGES (R2)", 503);
|
|
2064
|
-
|
|
2100
|
+
if (c.env.CANVAS_API_TOKENS && !staticTokensOn(c.env)) return fail("NOT_CONFIGURED", "CANVAS_API_TOKENS is a dev fallback. Set CANVAS_UNSAFE_STATIC_TOKENS=1 to use it, or remove it and issue agentic-identity credentials", 503);
|
|
2101
|
+
await ready(c.env.DB);
|
|
2102
|
+
const store = createD1Store(c.env.DB, { region });
|
|
2103
|
+
const who = await authenticate(c.req.raw, c.env, store);
|
|
2065
2104
|
if (!who) return fail("UNAUTHORIZED", "send a valid bearer token", 401);
|
|
2066
2105
|
if (who.role === "viewer" && c.req.method !== "GET") return fail("FORBIDDEN", "this token is a viewer; it cannot change a board", 403);
|
|
2067
2106
|
c.set("actor", who.actor);
|
|
2068
2107
|
c.set("role", who.role);
|
|
2069
|
-
|
|
2070
|
-
c.set("service", new CanvasService(createD1Store(c.env.DB, { region })));
|
|
2108
|
+
c.set("service", new CanvasService(store));
|
|
2071
2109
|
c.set("blobs", createR2Blobs(c.env.IMAGES, { region, urlBase: new URL(c.req.url).origin, urlSecret: `${crypto.randomUUID()}${crypto.randomUUID()}` }));
|
|
2072
2110
|
await next();
|
|
2073
2111
|
});
|
|
@@ -2116,6 +2154,19 @@ function createApp(options = {}) {
|
|
|
2116
2154
|
return c.json(await c.var.service.create(c.var.actor, { id, title }), 201);
|
|
2117
2155
|
});
|
|
2118
2156
|
api.get("/boards/:id", async (c) => c.json(await c.var.service.read(c.req.param("id"))));
|
|
2157
|
+
api.delete("/boards/:id", async (c) => {
|
|
2158
|
+
const id = c.req.param("id");
|
|
2159
|
+
await c.var.service.assertExists(id);
|
|
2160
|
+
const live = options.live?.(c.env);
|
|
2161
|
+
if (live) await live.erase(id);
|
|
2162
|
+
const deleted = await c.var.service.delete(id);
|
|
2163
|
+
if (live) {
|
|
2164
|
+
await live.erase(id);
|
|
2165
|
+
forget(id);
|
|
2166
|
+
}
|
|
2167
|
+
console.log(JSON.stringify({ level: "info", op: "delete-board", actorKind: c.var.actor.kind }));
|
|
2168
|
+
return c.json(deleted);
|
|
2169
|
+
});
|
|
2119
2170
|
api.get("/boards/:id/svg", async (c) => new Response(await c.var.service.exportSvg(c.req.param("id")), { headers: { "content-type": "image/svg+xml", "x-content-type-options": "nosniff", "content-security-policy": "default-src 'none'; style-src 'unsafe-inline'; img-src data:" } }));
|
|
2120
2171
|
api.post("/boards/:id/shapes", async (c) => c.json(await c.var.service.add(c.req.param("id"), await jsonBody(c), c.var.actor), 201));
|
|
2121
2172
|
api.post("/boards/:id/connectors", async (c) => c.json(await c.var.service.connect(c.req.param("id"), await jsonBody(c), c.var.actor), 201));
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
drawFlow,
|
|
8
8
|
readBoard,
|
|
9
9
|
toSvg
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-ZSPZMGHG.js";
|
|
11
11
|
import {
|
|
12
12
|
principalSchema
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-JOBTIAFJ.js";
|
|
14
14
|
|
|
15
15
|
// src/service.ts
|
|
16
16
|
import * as Y from "yjs";
|
|
@@ -65,6 +65,15 @@ var CanvasService = class {
|
|
|
65
65
|
}
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
|
+
/** Delete a board and everything on it. Throws NOT_FOUND unless the board exists. The Worker erases the live room too. */
|
|
69
|
+
async delete(id) {
|
|
70
|
+
boardKey(id);
|
|
71
|
+
return this.store.transaction(async (tx) => {
|
|
72
|
+
if (!await tx.get(COLLECTION, id)) throw new CanvasError("NOT_FOUND", `no board "${id}"`);
|
|
73
|
+
await tx.delete(COLLECTION, id);
|
|
74
|
+
return { deleted: id };
|
|
75
|
+
});
|
|
76
|
+
}
|
|
68
77
|
/** Throws NOT_FOUND unless the board exists. A live room opens only for a board that exists. */
|
|
69
78
|
async assertExists(id) {
|
|
70
79
|
boardKey(id);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/core/principal.ts
|
|
2
|
+
import { PRINCIPAL_KINDS, isMachineMade, parsePrincipal } from "agentic-identity";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
var principalSchema = z.object({ kind: z.string(), id: z.string() }).strict().transform((value, ctx) => {
|
|
5
|
+
try {
|
|
6
|
+
return parsePrincipal(value);
|
|
7
|
+
} catch (error) {
|
|
8
|
+
ctx.addIssue({ code: "custom", message: error instanceof Error ? error.message : "invalid principal" });
|
|
9
|
+
return z.NEVER;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
var storedPrincipalSchema = z.object({ kind: z.enum(PRINCIPAL_KINDS), id: z.string().min(1).max(200) }).strict();
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
PRINCIPAL_KINDS,
|
|
16
|
+
isMachineMade,
|
|
17
|
+
principalSchema,
|
|
18
|
+
storedPrincipalSchema
|
|
19
|
+
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isMachineMade,
|
|
3
|
-
principalSchema
|
|
4
|
-
|
|
3
|
+
principalSchema,
|
|
4
|
+
storedPrincipalSchema
|
|
5
|
+
} from "./chunk-JOBTIAFJ.js";
|
|
5
6
|
|
|
6
7
|
// src/core/board.ts
|
|
7
8
|
import { randomUUID } from "crypto";
|
|
@@ -58,8 +59,8 @@ var stampFields = {
|
|
|
58
59
|
id,
|
|
59
60
|
z: z.number().finite().min(-MAX_Z).max(MAX_Z),
|
|
60
61
|
groupId: id.optional(),
|
|
61
|
-
author:
|
|
62
|
-
editedBy:
|
|
62
|
+
author: storedPrincipalSchema,
|
|
63
|
+
editedBy: storedPrincipalSchema
|
|
63
64
|
};
|
|
64
65
|
var storedShape = z.object({ ...stampFields, type: z.enum(SHAPE_TYPES), x: coord, y: coord, w: size, h: size, text, fill: color.optional(), stroke: color.optional(), src: imageSrc.optional() }).refine((s) => s.type === "image" === (s.src !== void 0), { message: "src is required for image shapes and allowed on no other type" });
|
|
65
66
|
var storedConnector = z.object({
|
|
@@ -129,7 +130,7 @@ var Board = class _Board {
|
|
|
129
130
|
return value.success ? value.data : "";
|
|
130
131
|
}
|
|
131
132
|
get titleBy() {
|
|
132
|
-
const value =
|
|
133
|
+
const value = storedPrincipalSchema.safeParse(this.meta.get("titleBy"));
|
|
133
134
|
return value.success ? value.data : void 0;
|
|
134
135
|
}
|
|
135
136
|
setTitle(title, actor) {
|
package/dist/cli.js
CHANGED
|
@@ -5,18 +5,18 @@ import {
|
|
|
5
5
|
operatorActor,
|
|
6
6
|
runMcp,
|
|
7
7
|
svgToPng
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-3QW4UF2R.js";
|
|
9
9
|
import {
|
|
10
10
|
openLocalStore
|
|
11
11
|
} from "./chunk-UKST5A57.js";
|
|
12
12
|
import {
|
|
13
13
|
CanvasService
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-GPSOLW2Y.js";
|
|
15
15
|
import "./chunk-NWQED42L.js";
|
|
16
16
|
import {
|
|
17
17
|
CanvasError
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import "./chunk-
|
|
18
|
+
} from "./chunk-ZSPZMGHG.js";
|
|
19
|
+
import "./chunk-JOBTIAFJ.js";
|
|
20
20
|
|
|
21
21
|
// src/cli.ts
|
|
22
22
|
import { readFileSync, writeFileSync } from "fs";
|
|
@@ -36,6 +36,7 @@ Commands:
|
|
|
36
36
|
connect <board> <from> <to> [--text <t>] Join two shapes with an arrow.
|
|
37
37
|
update <board> <element> [--x --y --w --h --text --fill --stroke] Change fields of an element. Resize with --w and --h.
|
|
38
38
|
move <board> <element> --dx <n> --dy <n> Move an element or a group.
|
|
39
|
+
delete <board> Delete a whole board for good.
|
|
39
40
|
rm <board> <element> Delete an element.
|
|
40
41
|
flow <board> --text <t> | --file <p> Draw a flowchart from text ("A -> B: label"). Use --file - for stdin.
|
|
41
42
|
export <board> [--format svg|png] [--out <path>] Print SVG, or write PNG to --out.
|
|
@@ -141,6 +142,10 @@ async function run(argv) {
|
|
|
141
142
|
case "move":
|
|
142
143
|
need(2, "<board> <element> --dx <n> --dy <n>");
|
|
143
144
|
return print(await service.move(board, rest[0], num(v.dx) ?? Number.NaN, num(v.dy) ?? Number.NaN, actor()));
|
|
145
|
+
case "delete":
|
|
146
|
+
need(1, "<board>");
|
|
147
|
+
actor();
|
|
148
|
+
return print(await service.delete(board));
|
|
144
149
|
case "rm":
|
|
145
150
|
need(2, "<board> <element>");
|
|
146
151
|
return print(await service.remove(board, rest[0]));
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { E as ErrorCode, a as BoardData, c as ShapeType, S as Shape, d as Endpoint, C as Connector } from './read-
|
|
1
|
+
import { E as ErrorCode, a as BoardData, c as ShapeType, S as Shape, d as Endpoint, C as Connector } from './read-CeWoSAAy.js';
|
|
2
2
|
import { PrincipalKind } from 'agentic-identity';
|
|
3
3
|
import 'yjs';
|
|
4
4
|
import 'zod';
|
|
@@ -81,6 +81,10 @@ interface CanvasClient {
|
|
|
81
81
|
readBoard(id: string, options?: CallOptions): Promise<BoardData & {
|
|
82
82
|
id: string;
|
|
83
83
|
}>;
|
|
84
|
+
/** Delete a board for good, with its live room. Returns the id. */
|
|
85
|
+
deleteBoard(id: string, options?: CallOptions): Promise<{
|
|
86
|
+
deleted: string;
|
|
87
|
+
}>;
|
|
84
88
|
/** The board as an SVG document. */
|
|
85
89
|
exportSvg(id: string, options?: CallOptions): Promise<string>;
|
|
86
90
|
addShape(id: string, shape: ShapeInput, options?: CallOptions): Promise<Shape>;
|
package/dist/client.js
CHANGED
|
@@ -58,6 +58,7 @@ function createClient(options) {
|
|
|
58
58
|
listBoards: (o) => json("GET", "/boards", void 0, o),
|
|
59
59
|
createBoard: (input = {}, o) => json("POST", "/boards", input, o),
|
|
60
60
|
readBoard: (id, o) => json("GET", `/boards/${seg(id)}`, void 0, o),
|
|
61
|
+
deleteBoard: (id, o) => json("DELETE", `/boards/${seg(id)}`, void 0, o),
|
|
61
62
|
exportSvg: async (id, o) => (await send("GET", `/boards/${seg(id)}/svg`, {}, o)).text(),
|
|
62
63
|
addShape: (id, shape, o) => json("POST", `/boards/${seg(id)}/shapes`, shape, o),
|
|
63
64
|
addConnector: (id, connector, o) => json("POST", `/boards/${seg(id)}/connectors`, connector, o),
|
package/dist/embed.d.ts
CHANGED
package/dist/embed.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { B as Board, P as Principal } from './read-
|
|
2
|
-
export { a as BoardData, b as CanvasElement, e as CanvasError, C as Connector, f as EdgeData, d as Endpoint, E as ErrorCode, G as Group, M as MAX_ELEMENTS, g as MAX_TEXT, N as NodeData, h as SHAPE_TYPES, S as Shape, c as ShapeType, p as principalSchema, r as readBoard } from './read-
|
|
3
|
-
import { B as BoardAccess } from './service-
|
|
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-
|
|
1
|
+
import { B as Board, P as Principal } from './read-CeWoSAAy.js';
|
|
2
|
+
export { a as BoardData, b as CanvasElement, e as CanvasError, C as Connector, f as EdgeData, d as Endpoint, E as ErrorCode, G as Group, M as MAX_ELEMENTS, g as MAX_TEXT, N as NodeData, h as SHAPE_TYPES, S as Shape, c as ShapeType, p as principalSchema, r as readBoard } from './read-CeWoSAAy.js';
|
|
3
|
+
import { B as BoardAccess } from './service-D9WzMLH1.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-D9WzMLH1.js';
|
|
5
5
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
6
6
|
export { FileStoreOptions, createFileStore, openLocalStore } from './store-file.js';
|
|
7
7
|
export { PRINCIPAL_KINDS, PrincipalKind, isMachineMade } from 'agentic-identity';
|
package/dist/index.js
CHANGED
|
@@ -6,14 +6,14 @@ import {
|
|
|
6
6
|
operatorActor,
|
|
7
7
|
runMcp,
|
|
8
8
|
svgToPng
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-3QW4UF2R.js";
|
|
10
10
|
import {
|
|
11
11
|
createFileStore,
|
|
12
12
|
openLocalStore
|
|
13
13
|
} from "./chunk-UKST5A57.js";
|
|
14
14
|
import {
|
|
15
15
|
CanvasService
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-GPSOLW2Y.js";
|
|
17
17
|
import "./chunk-NWQED42L.js";
|
|
18
18
|
import {
|
|
19
19
|
Board,
|
|
@@ -28,12 +28,12 @@ import {
|
|
|
28
28
|
parseFlow,
|
|
29
29
|
readBoard,
|
|
30
30
|
toSvg
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-ZSPZMGHG.js";
|
|
32
32
|
import {
|
|
33
33
|
PRINCIPAL_KINDS,
|
|
34
34
|
isMachineMade,
|
|
35
35
|
principalSchema
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-JOBTIAFJ.js";
|
|
37
37
|
export {
|
|
38
38
|
ActorConfigError,
|
|
39
39
|
BRAND,
|
package/dist/live-worker.d.ts
CHANGED
|
@@ -5,10 +5,10 @@ import 'agentic-identity';
|
|
|
5
5
|
import 'agentic-realtime';
|
|
6
6
|
import 'agentic-identity/cloudflare';
|
|
7
7
|
import 'hono';
|
|
8
|
-
import './read-
|
|
8
|
+
import './read-CeWoSAAy.js';
|
|
9
9
|
import 'yjs';
|
|
10
10
|
import 'zod';
|
|
11
|
-
import './service-
|
|
11
|
+
import './service-D9WzMLH1.js';
|
|
12
12
|
|
|
13
13
|
type Namespace = Parameters<typeof createDurableObjectRealtime>[0];
|
|
14
14
|
interface LiveEnv extends WorkerEnv {
|
package/dist/live-worker.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createApp
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-EH7WCMK6.js";
|
|
4
|
+
import "./chunk-GPSOLW2Y.js";
|
|
5
5
|
import "./chunk-NWQED42L.js";
|
|
6
6
|
import {
|
|
7
7
|
CanvasError
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-ZSPZMGHG.js";
|
|
9
|
+
import "./chunk-JOBTIAFJ.js";
|
|
10
10
|
|
|
11
11
|
// src/live-worker.ts
|
|
12
12
|
import { parseRegion } from "agentic-identity";
|
package/dist/live.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { B as Board, P as Principal, a as BoardData, S as Shape, C as Connector, b as CanvasElement } from './read-
|
|
1
|
+
import { B as Board, P as Principal, a as BoardData, S as Shape, C as Connector, b as CanvasElement } from './read-CeWoSAAy.js';
|
|
2
2
|
import { Peer } from 'agentic-realtime/client';
|
|
3
|
-
import { B as BoardAccess, D as DrawOptions } from './service-
|
|
3
|
+
import { B as BoardAccess, D as DrawOptions } from './service-D9WzMLH1.js';
|
|
4
4
|
import 'yjs';
|
|
5
|
-
import 'zod';
|
|
6
5
|
import 'agentic-identity';
|
|
6
|
+
import 'zod';
|
|
7
7
|
|
|
8
8
|
interface LiveOptions {
|
|
9
9
|
/** The Worker address, such as `https://canvas.example.com`. */
|
|
@@ -83,6 +83,9 @@ declare class LiveService implements BoardAccess {
|
|
|
83
83
|
id: string;
|
|
84
84
|
title: string;
|
|
85
85
|
}[]>;
|
|
86
|
+
delete(id: string): Promise<{
|
|
87
|
+
deleted: string;
|
|
88
|
+
}>;
|
|
86
89
|
read(id: string): Promise<BoardData & {
|
|
87
90
|
id: string;
|
|
88
91
|
}>;
|
package/dist/live.js
CHANGED
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
drawFlow,
|
|
5
5
|
readBoard,
|
|
6
6
|
toSvg
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-ZSPZMGHG.js";
|
|
8
|
+
import "./chunk-JOBTIAFJ.js";
|
|
9
9
|
|
|
10
10
|
// src/live.ts
|
|
11
11
|
import { parsePrincipal } from "agentic-identity";
|
|
@@ -145,6 +145,13 @@ var LiveService = class {
|
|
|
145
145
|
if (!Array.isArray(boards) || !boards.every((b) => typeof b?.id === "string" && typeof b?.title === "string")) throw new CanvasError("INVALID", "the server sent a board list this client does not know");
|
|
146
146
|
return boards;
|
|
147
147
|
}
|
|
148
|
+
async delete(id) {
|
|
149
|
+
const response = await call(this.options.server, this.options.token, `/boards/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
150
|
+
if (!response.ok) throw await refusal(response, "the server refused the request");
|
|
151
|
+
const { deleted } = await json(response);
|
|
152
|
+
if (deleted !== id) throw new CanvasError("INVALID", "the server sent a result this client does not know");
|
|
153
|
+
return { deleted };
|
|
154
|
+
}
|
|
148
155
|
async read(id) {
|
|
149
156
|
return this.use(id, async (live) => ({ id, ...readBoard(live.board) }));
|
|
150
157
|
}
|
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
import * as Y from 'yjs';
|
|
2
|
+
import { PrincipalKind } from 'agentic-identity';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The actor of a new write. The rule is the identity kernel's `parsePrincipal`: a known kind, and an id of 1 to 128
|
|
7
|
+
* characters from A-Z, a-z, 0-9, ".", "_" and "-". The Worker, the CLI and the SDK all use it.
|
|
8
|
+
*/
|
|
9
|
+
declare const principalSchema: z.ZodEffects<z.ZodObject<{
|
|
10
|
+
kind: z.ZodString;
|
|
6
11
|
id: z.ZodString;
|
|
7
12
|
}, "strict", z.ZodTypeAny, {
|
|
8
|
-
kind:
|
|
13
|
+
kind: string;
|
|
9
14
|
id: string;
|
|
10
15
|
}, {
|
|
11
|
-
kind:
|
|
16
|
+
kind: string;
|
|
17
|
+
id: string;
|
|
18
|
+
}>, {
|
|
19
|
+
kind: PrincipalKind;
|
|
20
|
+
id: string;
|
|
21
|
+
}, {
|
|
22
|
+
kind: string;
|
|
12
23
|
id: string;
|
|
13
24
|
}>;
|
|
14
|
-
type Principal =
|
|
25
|
+
type Principal = {
|
|
26
|
+
kind: PrincipalKind;
|
|
27
|
+
id: string;
|
|
28
|
+
};
|
|
15
29
|
|
|
16
30
|
type ErrorCode = "INVALID" | "DUPLICATE_ID" | "NOT_FOUND" | "BAD_REFERENCE" | "LIMIT" | "UNAVAILABLE" | "FORBIDDEN";
|
|
17
31
|
declare class CanvasError extends Error {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as ShapeType, B as Board, P as Principal, a as BoardData, S as Shape, C as Connector, b as CanvasElement } from './read-
|
|
1
|
+
import { c as ShapeType, B as Board, P as Principal, a as BoardData, S as Shape, C as Connector, b as CanvasElement } from './read-CeWoSAAy.js';
|
|
2
2
|
import { Store } from 'agentic-identity';
|
|
3
3
|
|
|
4
4
|
declare const MAX_FLOW_NODES = 500;
|
|
@@ -68,6 +68,10 @@ declare class CanvasService {
|
|
|
68
68
|
id: string;
|
|
69
69
|
title: string;
|
|
70
70
|
}[]>;
|
|
71
|
+
/** Delete a board and everything on it. Throws NOT_FOUND unless the board exists. The Worker erases the live room too. */
|
|
72
|
+
delete(id: string): Promise<{
|
|
73
|
+
deleted: string;
|
|
74
|
+
}>;
|
|
71
75
|
/** Throws NOT_FOUND unless the board exists. A live room opens only for a board that exists. */
|
|
72
76
|
assertExists(id: string): Promise<void>;
|
|
73
77
|
read(id: string): Promise<BoardData & {
|
|
@@ -123,6 +127,6 @@ declare class CanvasService {
|
|
|
123
127
|
private load;
|
|
124
128
|
}
|
|
125
129
|
/** What the CLI and the MCP server need. `CanvasService` runs it on a store, and `LiveService` runs it in a live room. */
|
|
126
|
-
type BoardAccess = Pick<CanvasService, "create" | "list" | "read" | "update" | "add" | "connect" | "move" | "remove" | "flow" | "exportSvg">;
|
|
130
|
+
type BoardAccess = Pick<CanvasService, "create" | "list" | "read" | "delete" | "update" | "add" | "connect" | "move" | "remove" | "flow" | "exportSvg">;
|
|
127
131
|
|
|
128
132
|
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/ui.css
CHANGED
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
.shape { fill: var(--ad-color-surface-raised); stroke: var(--ad-color-border-strong); stroke-width: 1.5; }
|
|
16
16
|
.shape.text { fill: transparent; stroke: none; }
|
|
17
17
|
.shape.frame, .shape.image { fill: none; stroke-dasharray: 6 4; pointer-events: visiblePainted; }
|
|
18
|
-
.shape.sticky { fill:
|
|
18
|
+
.shape.sticky { fill: var(--ad-color-warning-subtle); stroke: var(--ad-color-warning); }
|
|
19
19
|
.label { fill: var(--ad-color-text); pointer-events: none; }
|
|
20
|
-
.sticky-label { fill:
|
|
20
|
+
.sticky-label { fill: var(--ad-color-warning-text); }
|
|
21
21
|
.edge { stroke: var(--ad-color-text-muted); stroke-width: 2; fill: none; pointer-events: none; }
|
|
22
22
|
.hit { stroke: transparent; stroke-width: 14; }
|
|
23
23
|
.arrowhead { fill: var(--ad-color-text-muted); }
|
package/dist/worker.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Blobs } from 'agentic-identity';
|
|
1
|
+
import { Store, 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 } from './read-
|
|
6
|
-
import { C as CanvasService } from './service-
|
|
5
|
+
import { P as Principal } from './read-CeWoSAAy.js';
|
|
6
|
+
import { C as CanvasService } from './service-D9WzMLH1.js';
|
|
7
7
|
import 'yjs';
|
|
8
8
|
import 'zod';
|
|
9
9
|
|
|
@@ -11,10 +11,16 @@ interface WorkerEnv {
|
|
|
11
11
|
DB: D1Like;
|
|
12
12
|
IMAGES: R2Like;
|
|
13
13
|
/**
|
|
14
|
+
* Dev fallback. Set it to "1" to let `CANVAS_API_TOKENS` sign requests in. Without it, `CANVAS_API_TOKENS` is not read,
|
|
15
|
+
* and a request that sets it gets 503, so a deploy never trusts a static list by accident. Never set it in production.
|
|
16
|
+
*/
|
|
17
|
+
CANVAS_UNSAFE_STATIC_TOKENS?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Dev fallback, read only when `CANVAS_UNSAFE_STATIC_TOKENS` is "1". It is tried after the identity credentials.
|
|
14
20
|
* JSON: `{ "<token>": { "actor": "ada", "kind": "human", "role": "viewer" } }`. A token has 32 characters or more.
|
|
15
21
|
* A grant needs an actor id and a kind. A token with a missing or bad part gets 401.
|
|
16
22
|
* The role is "editor" (the default) or "viewer". A viewer reads and cannot write, over HTTP or in a live room.
|
|
17
|
-
*
|
|
23
|
+
* Every token reaches every board, and a token cannot be revoked without a new deploy.
|
|
18
24
|
*/
|
|
19
25
|
CANVAS_API_TOKENS?: string;
|
|
20
26
|
/**
|
|
@@ -42,8 +48,13 @@ interface AppOptions {
|
|
|
42
48
|
}
|
|
43
49
|
/** A browser cannot set a header on a WebSocket. It offers the sub-protocols `agentic-canvas` and `agentic-canvas.bearer.<token in base64url>`. */
|
|
44
50
|
declare const WS_PROTOCOL = "agentic-canvas";
|
|
45
|
-
/**
|
|
46
|
-
|
|
51
|
+
/**
|
|
52
|
+
* The principal and role for a request, or null. A credential issued by agentic-identity (a bearer key or an access token in the
|
|
53
|
+
* store) signs in. Its scopes set the role: `canvas:write` is an editor, `canvas:read` a viewer, and none is denied.
|
|
54
|
+
* The static list signs in only with `CANVAS_UNSAFE_STATIC_TOKENS=1`. The kind is never defaulted: a principal without a valid kind gets null.
|
|
55
|
+
* Without `store` only the static list is read.
|
|
56
|
+
*/
|
|
57
|
+
declare function authenticate(request: Request, env: WorkerEnv, store?: Store): Promise<{
|
|
47
58
|
actor: Principal;
|
|
48
59
|
role: Role;
|
|
49
60
|
} | null>;
|
package/dist/worker.js
CHANGED
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
createApp,
|
|
5
5
|
sniffImage,
|
|
6
6
|
worker_default
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-EH7WCMK6.js";
|
|
8
|
+
import "./chunk-GPSOLW2Y.js";
|
|
9
9
|
import "./chunk-NWQED42L.js";
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-ZSPZMGHG.js";
|
|
11
|
+
import "./chunk-JOBTIAFJ.js";
|
|
12
12
|
export {
|
|
13
13
|
WS_PROTOCOL,
|
|
14
14
|
authenticate,
|
package/llms.txt
CHANGED
|
@@ -6,11 +6,11 @@ Keywords: whiteboard,canvas,diagrams,drawing,collaboration,agents,mcp,cli.
|
|
|
6
6
|
|
|
7
7
|
- SDK: import { Board, drawFlow, readBoard, toSvg } from "agentic-canvas".
|
|
8
8
|
- Exports: the README "Exports" section lists every name of each entry point: agentic-canvas, /client, /file-store, /live, /worker and /live-worker.
|
|
9
|
-
- CLI: agentic-canvas new|list|read|add|connect|update|move|rm|flow|export.
|
|
10
|
-
- UI: the Worker serves a web UI at its root (run `npm run build`, then `wrangler dev`). A person signs in with a bearer token.
|
|
9
|
+
- CLI: agentic-canvas new|list|read|add|connect|update|move|rm|delete|flow|export.
|
|
10
|
+
- UI: the Worker serves a web UI at its root (run `npm run build`, then `wrangler dev`). A person signs in with a bearer token: an agentic-identity credential (scope canvas:write or canvas:read). A static CANVAS_API_TOKENS list works only with CANVAS_UNSAFE_STATIC_TOKENS=1 (dev).
|
|
11
11
|
- Live: set CANVAS_SERVER and CANVAS_TOKEN, and the CLI and `agentic-canvas mcp` edit the board in its live room.
|
|
12
12
|
- SDK: import { LiveBoard, LiveService } from "agentic-canvas/live".
|
|
13
|
-
- SDK: import { createClient, CanvasApiError } from "agentic-canvas/client". It calls the Worker API with fetch. Methods: me, listBoards, createBoard, readBoard, exportSvg, addShape, addConnector, drawFlow, update, move, remove, uploadImage, getImage, state, checkpoint. A failure throws CanvasApiError with `code` and `status`. No retries.
|
|
13
|
+
- SDK: import { createClient, CanvasApiError } from "agentic-canvas/client". It calls the Worker API with fetch. Methods: me, listBoards, createBoard, readBoard, deleteBoard, exportSvg, addShape, addConnector, drawFlow, update, move, remove, uploadImage, getImage, state, checkpoint. A failure throws CanvasApiError with `code` and `status`. No retries.
|
|
14
14
|
- UI: import { mount } from "agentic-canvas/ui"; import "agentic-canvas/ui.css" (with agentic-design/styles.css). mount(element, { boardId, client, principal: { kind, name, role }, readOnly?, room?: { baseUrl, token } }) renders the board and returns { update(options), unmount() }. BoardEditor is the same as a React component. React 19 is a peer dependency.
|
|
15
15
|
|
|
16
16
|
## Docs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-canvas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "A shared whiteboard for diagrams, drawn by people and agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"whiteboard",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
83
83
|
"agentic-design": "^0.2.0",
|
|
84
84
|
"agentic-identity": "^0.6.0",
|
|
85
|
-
"agentic-realtime": "^0.
|
|
85
|
+
"agentic-realtime": "^0.4.2",
|
|
86
86
|
"yjs": "^13.6.0",
|
|
87
87
|
"zod": "^3.25.0"
|
|
88
88
|
},
|
package/dist/chunk-SPP6PINW.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// src/core/principal.ts
|
|
2
|
-
import { PRINCIPAL_KINDS, isMachineMade } from "agentic-identity";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
var principalSchema = z.object({ kind: z.enum(PRINCIPAL_KINDS), id: z.string().min(1).max(200) }).strict();
|
|
5
|
-
|
|
6
|
-
export {
|
|
7
|
-
PRINCIPAL_KINDS,
|
|
8
|
-
isMachineMade,
|
|
9
|
-
principalSchema
|
|
10
|
-
};
|