agentic-canvas 0.5.0 → 0.6.1

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 CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  A shared whiteboard for diagrams, drawn by people and agents.
9
9
 
10
- > Status: 0.5.0. The board model, the agent operations, the exports, the CLI, the
10
+ > Status: 0.6.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.
@@ -279,12 +279,20 @@ Every name that each entry point exports is listed here. A name marked (type) ex
279
279
  | `FlowGraph` (type), `FlowNode` (type), `FlowEdge` (type), `Placement` (type), `DrawOptions` (type) | Types for a flow and its layout. |
280
280
  | `readBoard`, `BoardData` (type), `NodeData` (type), `EdgeData` (type) | Read a board as plain data. |
281
281
  | `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. |
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. `principalSchema` uses the identity rule below. |
283
283
  | `operatorActor(env?)`, `ActorConfigError` | Read the actor from `CANVAS_ACTOR` as `<kind>:<id>`. It throws `ActorConfigError` when the value is missing or bad. |
284
284
  | `CanvasService` | The board operations that the CLI, the MCP server and the Worker share. It takes a `Store`. |
285
285
  | `createMcpServer(service, actor)`, `runMcp(service, actor)` | Make the MCP server, or run it on stdio. |
286
286
  | `createFileStore`, `openLocalStore`, `FileStoreOptions` (type) | Stores on one local file. See `agentic-canvas/file-store`. |
287
287
 
288
+ ### Actor ids
289
+
290
+ One rule names an actor in every new write, in the SDK, the CLI, the MCP server and the Worker. The kind must be
291
+ one of `PRINCIPAL_KINDS`. The id has 1 to 128 characters from `A-Z`, `a-z`, `0-9`, `.`, `_` and `-`, and starts
292
+ with a letter, digit, `_` or `-`. The rule comes from `parsePrincipal` in `agentic-identity`. A write with another
293
+ id fails with `INVALID`. A board that was stored with an older, looser id (up to 200 characters) still opens. The
294
+ old author stays on its elements. Only new writes follow the strict rule.
295
+
288
296
  ### `agentic-canvas/client`
289
297
 
290
298
  | Name | What it is |
@@ -327,9 +335,9 @@ The stylesheet is a separate export, `agentic-canvas/ui.css`. Load `agentic-desi
327
335
  |---|---|
328
336
  | `createApp(options?)` | Make the Hono app for the Worker. Pass `live` to turn on the live-room socket. |
329
337
  | `default` | The Worker, without live rooms. |
330
- | `authenticate(request, env)` | Read a bearer token from the `Authorization` header, or from a WebSocket sub-protocol. Return the actor and the role, or `null`. |
338
+ | `authenticate(request, env)` | Read a bearer token from the `Authorization` header, or from a WebSocket sub-protocol. Return a promise of the actor and the role, or `null`. |
331
339
  | `sniffImage(bytes)` | Find the image type from the first bytes, or return `null`. The client's claim of a type is not trusted. |
332
- | `WS_PROTOCOL` | The sub-protocol name that carries a token on a WebSocket: `agentic-canvas.bearer`. |
340
+ | `WS_PROTOCOL` | The sub-protocol name for a WebSocket: `agentic-canvas`. The token travels in the entry `agentic-canvas.bearer.<token in base64url>`. |
333
341
  | `WorkerEnv` (type), `AppOptions` (type), `Role` (type) | Types for the Worker bindings, the app options and the role, `editor` or `viewer`. |
334
342
 
335
343
  ### `agentic-canvas/live-worker`
@@ -7,10 +7,10 @@ import {
7
7
  drawFlow,
8
8
  readBoard,
9
9
  toSvg
10
- } from "./chunk-JCYNPYYJ.js";
10
+ } from "./chunk-ZSPZMGHG.js";
11
11
  import {
12
12
  principalSchema
13
- } from "./chunk-SPP6PINW.js";
13
+ } from "./chunk-JOBTIAFJ.js";
14
14
 
15
15
  // src/service.ts
16
16
  import * as Y from "yjs";
@@ -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,17 +1,15 @@
1
1
  import {
2
2
  CanvasService,
3
3
  MAX_LIVE_BYTES
4
- } from "./chunk-G4YTESPS.js";
4
+ } from "./chunk-66FFQMHI.js";
5
5
  import {
6
6
  CanvasError
7
- } from "./chunk-JCYNPYYJ.js";
8
- import {
9
- principalSchema
10
- } from "./chunk-SPP6PINW.js";
7
+ } from "./chunk-ZSPZMGHG.js";
11
8
 
12
9
  // src/worker.ts
13
- import { parseRegion } from "agentic-identity";
10
+ import { hasScope, parsePrincipal, parseRegion } from "agentic-identity";
14
11
  import { createD1Store, createR2Blobs, D1_SCHEMA } from "agentic-identity/cloudflare";
12
+ import { acceptWebSocketProtocol, principalFromWebSocketRequest } from "agentic-identity/http";
15
13
 
16
14
  // node_modules/hono/dist/request/constants.js
17
15
  var GET_MATCH_RESULT = /* @__PURE__ */ Symbol();
@@ -1950,36 +1948,41 @@ function sameText(a, b) {
1950
1948
  for (let i = 0; i < Math.max(x.length, y.length); i++) diff |= (x[i] ?? 0) ^ (y[i] ?? 0);
1951
1949
  return diff === 0;
1952
1950
  }
1953
- var WS_PROTOCOL = "agentic-canvas.bearer";
1954
- function socketToken(request) {
1955
- if (request.headers.get("upgrade") !== "websocket") return void 0;
1956
- const [name, encoded, ...more] = (request.headers.get("sec-websocket-protocol") ?? "").split(",").map((p) => p.trim());
1957
- if (name !== WS_PROTOCOL || !encoded || more.length > 0 || !/^[A-Za-z0-9_-]+$/.test(encoded)) return void 0;
1958
- try {
1959
- return new TextDecoder("utf-8", { fatal: true }).decode(Uint8Array.from(atob(encoded.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0)));
1960
- } catch {
1961
- return void 0;
1962
- }
1951
+ var WS_PROTOCOL = "agentic-canvas";
1952
+ function tokenAuth(env) {
1953
+ return {
1954
+ async authenticate(request) {
1955
+ const token = /^Bearer (.+)$/.exec(request.headers.get("authorization") ?? "")?.[1];
1956
+ if (!token || !env.CANVAS_API_TOKENS) return null;
1957
+ let grants;
1958
+ try {
1959
+ grants = JSON.parse(env.CANVAS_API_TOKENS);
1960
+ } catch {
1961
+ return null;
1962
+ }
1963
+ if (typeof grants !== "object" || grants === null || Array.isArray(grants)) return null;
1964
+ let found;
1965
+ for (const [candidate, grant] of Object.entries(grants)) {
1966
+ if (sameText(candidate, token) && candidate.length >= MIN_TOKEN_LENGTH) found = grant;
1967
+ }
1968
+ if (typeof found !== "object" || found === null) return null;
1969
+ const { actor, kind, role = "editor" } = found;
1970
+ let step;
1971
+ try {
1972
+ step = parsePrincipal({ kind, id: actor });
1973
+ } catch {
1974
+ return null;
1975
+ }
1976
+ if (step.kind === SEEDER.kind && step.id === SEEDER.id) return null;
1977
+ if (role !== "editor" && role !== "viewer") return null;
1978
+ return { principalId: step.id, kind: step.kind, chain: [step], scopes: role === "viewer" ? ["canvas:read"] : ["canvas:read", "canvas:write"] };
1979
+ }
1980
+ };
1963
1981
  }
1964
- function authenticate(request, env) {
1965
- const token = /^Bearer (.+)$/.exec(request.headers.get("authorization") ?? "")?.[1] ?? socketToken(request);
1966
- if (!token || !env.CANVAS_API_TOKENS) return null;
1967
- let grants;
1968
- try {
1969
- grants = JSON.parse(env.CANVAS_API_TOKENS);
1970
- } catch {
1971
- return null;
1972
- }
1973
- if (typeof grants !== "object" || grants === null || Array.isArray(grants)) return null;
1974
- let found;
1975
- for (const [candidate, grant] of Object.entries(grants)) {
1976
- if (sameText(candidate, token) && candidate.length >= MIN_TOKEN_LENGTH) found = grant;
1977
- }
1978
- if (typeof found !== "object" || found === null) return null;
1979
- const { actor, kind, role = "editor" } = found;
1980
- const parsed = principalSchema.safeParse({ kind, id: actor });
1981
- const taken = parsed.success && parsed.data.kind === SEEDER.kind && parsed.data.id === SEEDER.id;
1982
- return parsed.success && !taken && (role === "editor" || role === "viewer") ? { actor: parsed.data, role } : null;
1982
+ async function authenticate(request, env) {
1983
+ const found = await principalFromWebSocketRequest(request, tokenAuth(env), { protocol: WS_PROTOCOL });
1984
+ if (found instanceof Response) return null;
1985
+ return { actor: { kind: found.kind, id: found.principalId }, role: hasScope(found.scopes, "canvas:write") ? "editor" : "viewer" };
1983
1986
  }
1984
1987
  function sniffImage(bytes) {
1985
1988
  const at = (offset, ...sig) => sig.every((b, i) => bytes[offset + i] === b);
@@ -2058,7 +2061,7 @@ function createApp(options = {}) {
2058
2061
  return fail("NOT_CONFIGURED", 'set CANVAS_REGION to "eu", "us", or "unknown"', 503);
2059
2062
  }
2060
2063
  if (!c.env.DB || !c.env.IMAGES) return fail("NOT_CONFIGURED", "bind DB (D1) and IMAGES (R2)", 503);
2061
- const who = authenticate(c.req.raw, c.env);
2064
+ const who = await authenticate(c.req.raw, c.env);
2062
2065
  if (!who) return fail("UNAUTHORIZED", "send a valid bearer token", 401);
2063
2066
  if (who.role === "viewer" && c.req.method !== "GET") return fail("FORBIDDEN", "this token is a viewer; it cannot change a board", 403);
2064
2067
  c.set("actor", who.actor);
@@ -2094,10 +2097,7 @@ function createApp(options = {}) {
2094
2097
  await saveRoom(c, true);
2095
2098
  const { actor, role } = c.var;
2096
2099
  const opened = await options.live(c.env).connect(id, c.req.raw, { principal: { id: actor.id, kind: actor.kind }, role, name: actor.id });
2097
- if (opened.status !== 101 || c.req.header("authorization") || !socketToken(c.req.raw)) return opened;
2098
- const headers = new Headers(opened.headers);
2099
- headers.set("sec-websocket-protocol", WS_PROTOCOL);
2100
- return new Response(null, { status: 101, headers, webSocket: opened.webSocket });
2100
+ return acceptWebSocketProtocol(c.req.raw, opened, WS_PROTOCOL);
2101
2101
  });
2102
2102
  api.get("/boards/:id/state", async (c) => !options.live ? fail("NOT_FOUND", "this server hosts no live rooms", 404) : c.body(new Uint8Array(await c.var.service.state(c.req.param("id"))), 200, { "content-type": "application/octet-stream" }));
2103
2103
  api.post("/boards/:id/checkpoint", async (c) => {
@@ -1,15 +1,14 @@
1
1
  import {
2
2
  CanvasError,
3
3
  SHAPE_TYPES
4
- } from "./chunk-JCYNPYYJ.js";
4
+ } from "./chunk-ZSPZMGHG.js";
5
5
  import {
6
- PRINCIPAL_KINDS,
7
- principalSchema
8
- } from "./chunk-SPP6PINW.js";
6
+ PRINCIPAL_KINDS
7
+ } from "./chunk-JOBTIAFJ.js";
9
8
 
10
9
  // src/version.ts
11
10
  import { createRequire } from "module";
12
- var VERSION = true ? "0.5.0" : readPackageVersion();
11
+ var VERSION = true ? "0.6.1" : readPackageVersion();
13
12
 
14
13
  // src/brand.ts
15
14
  var BRAND = {
@@ -51,17 +50,19 @@ async function svgToPng(svg) {
51
50
  }
52
51
 
53
52
  // src/actor.ts
53
+ import { parsePrincipal } from "agentic-identity";
54
54
  var ActorConfigError = class extends Error {
55
55
  };
56
56
  function operatorActor(env = process.env) {
57
57
  const raw = env.CANVAS_ACTOR;
58
58
  if (!raw) throw new ActorConfigError("the operator must set CANVAS_ACTOR=<kind>:<id>");
59
59
  const at = raw.indexOf(":");
60
- const parsed = principalSchema.safeParse({ kind: raw.slice(0, at), id: raw.slice(at + 1) });
61
- if (at < 0 || !parsed.success) {
60
+ try {
61
+ if (at < 0) throw new Error("no kind");
62
+ return parsePrincipal({ kind: raw.slice(0, at), id: raw.slice(at + 1) });
63
+ } catch {
62
64
  throw new ActorConfigError(`bad CANVAS_ACTOR "${raw}": use <kind>:<id>, kind is one of ${PRINCIPAL_KINDS.join(", ")}`);
63
65
  }
64
- return parsed.data;
65
66
  }
66
67
 
67
68
  // src/mcp.ts
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  isMachineMade,
3
- principalSchema
4
- } from "./chunk-SPP6PINW.js";
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: principalSchema,
62
- editedBy: principalSchema
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 = principalSchema.safeParse(this.meta.get("titleBy"));
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-VHHJIBAB.js";
8
+ } from "./chunk-VFJJGXTK.js";
9
9
  import {
10
10
  openLocalStore
11
11
  } from "./chunk-UKST5A57.js";
12
12
  import {
13
13
  CanvasService
14
- } from "./chunk-G4YTESPS.js";
14
+ } from "./chunk-66FFQMHI.js";
15
15
  import "./chunk-NWQED42L.js";
16
16
  import {
17
17
  CanvasError
18
- } from "./chunk-JCYNPYYJ.js";
19
- import "./chunk-SPP6PINW.js";
18
+ } from "./chunk-ZSPZMGHG.js";
19
+ import "./chunk-JOBTIAFJ.js";
20
20
 
21
21
  // src/cli.ts
22
22
  import { readFileSync, writeFileSync } from "fs";
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-BupIJSPJ.js';
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';
package/dist/embed.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { CanvasClient } from './client.js';
3
3
  import { PrincipalKind } from 'agentic-identity';
4
- import './read-BupIJSPJ.js';
4
+ import './read-CeWoSAAy.js';
5
5
  import 'yjs';
6
6
  import 'zod';
7
7
 
package/dist/embed.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  PRINCIPAL_KINDS
3
- } from "./chunk-SPP6PINW.js";
3
+ } from "./chunk-JOBTIAFJ.js";
4
4
 
5
5
  // ui/embed.tsx
6
6
  import { ToastProvider, TooltipProvider } from "agentic-design/react";
@@ -228,11 +228,11 @@ function Failure({ message, retry }) {
228
228
 
229
229
  // ui/presence.ts
230
230
  import { SyncClient } from "agentic-realtime/client";
231
- var WS_PROTOCOL = "agentic-canvas.bearer";
231
+ var WS_PROTOCOL = "agentic-canvas";
232
232
  var b64url = (text) => btoa(String.fromCharCode(...new TextEncoder().encode(text))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
233
233
  var bearerSocket = (token) => class extends WebSocket {
234
234
  constructor(url) {
235
- super(url, [WS_PROTOCOL, b64url(token)]);
235
+ super(url, [WS_PROTOCOL, `${WS_PROTOCOL}.bearer.${b64url(token)}`]);
236
236
  }
237
237
  };
238
238
  function roomUrl(baseUrl, board) {
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { B as Board, P as Principal } from './read-BupIJSPJ.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-BupIJSPJ.js';
3
- import { B as BoardAccess } from './service-CR4-r1yx.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-CR4-r1yx.js';
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-Ckz-3qhK.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-Ckz-3qhK.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-VHHJIBAB.js";
9
+ } from "./chunk-VFJJGXTK.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-G4YTESPS.js";
16
+ } from "./chunk-66FFQMHI.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-JCYNPYYJ.js";
31
+ } from "./chunk-ZSPZMGHG.js";
32
32
  import {
33
33
  PRINCIPAL_KINDS,
34
34
  isMachineMade,
35
35
  principalSchema
36
- } from "./chunk-SPP6PINW.js";
36
+ } from "./chunk-JOBTIAFJ.js";
37
37
  export {
38
38
  ActorConfigError,
39
39
  BRAND,
@@ -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-BupIJSPJ.js';
8
+ import './read-CeWoSAAy.js';
9
9
  import 'yjs';
10
10
  import 'zod';
11
- import './service-CR4-r1yx.js';
11
+ import './service-Ckz-3qhK.js';
12
12
 
13
13
  type Namespace = Parameters<typeof createDurableObjectRealtime>[0];
14
14
  interface LiveEnv extends WorkerEnv {
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  createApp
3
- } from "./chunk-V74PWJKY.js";
4
- import "./chunk-G4YTESPS.js";
3
+ } from "./chunk-PH67TSYW.js";
4
+ import "./chunk-66FFQMHI.js";
5
5
  import "./chunk-NWQED42L.js";
6
6
  import {
7
7
  CanvasError
8
- } from "./chunk-JCYNPYYJ.js";
9
- import "./chunk-SPP6PINW.js";
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-BupIJSPJ.js';
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-CR4-r1yx.js';
3
+ import { B as BoardAccess, D as DrawOptions } from './service-Ckz-3qhK.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`. */
package/dist/live.js CHANGED
@@ -4,12 +4,11 @@ import {
4
4
  drawFlow,
5
5
  readBoard,
6
6
  toSvg
7
- } from "./chunk-JCYNPYYJ.js";
8
- import {
9
- principalSchema
10
- } from "./chunk-SPP6PINW.js";
7
+ } from "./chunk-ZSPZMGHG.js";
8
+ import "./chunk-JOBTIAFJ.js";
11
9
 
12
10
  // src/live.ts
11
+ import { parsePrincipal } from "agentic-identity";
13
12
  import { SyncClient } from "agentic-realtime/client";
14
13
  var CODES = ["INVALID", "DUPLICATE_ID", "NOT_FOUND", "BAD_REFERENCE", "LIMIT", "UNAVAILABLE", "FORBIDDEN"];
15
14
  var base = (server) => server.replace(/\/$/, "") + "/api/v1/canvas";
@@ -33,9 +32,13 @@ async function whoAmI(server, token) {
33
32
  const response = await call(server, token, "/me");
34
33
  if (!response.ok) throw await refusal(response, "the server refused the token");
35
34
  const { actor, kind, role } = await json(response);
36
- const principal = principalSchema.safeParse({ kind, id: actor });
37
- if (!principal.success || role !== "editor" && role !== "viewer") throw new CanvasError("INVALID", "the server sent an identity this client does not know");
38
- return { actor: principal.data, role };
35
+ let principal;
36
+ try {
37
+ principal = parsePrincipal({ kind, id: actor });
38
+ } catch {
39
+ }
40
+ if (!principal || role !== "editor" && role !== "viewer") throw new CanvasError("INVALID", "the server sent an identity this client does not know");
41
+ return { actor: principal, role };
39
42
  }
40
43
  var LiveBoard = class _LiveBoard {
41
44
  constructor(board, me, client, id, options, refusedAtStart) {
@@ -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
- declare const principalSchema: z.ZodObject<{
5
- kind: z.ZodEnum<["human", "agent_of_human", "agent_of_agent", "agent", "user_token"]>;
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: "human" | "agent_of_human" | "agent_of_agent" | "agent" | "user_token";
13
+ kind: string;
9
14
  id: string;
10
15
  }, {
11
- kind: "human" | "agent_of_human" | "agent_of_agent" | "agent" | "user_token";
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 = z.infer<typeof principalSchema>;
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-BupIJSPJ.js';
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;
package/dist/worker.d.ts CHANGED
@@ -2,8 +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 } from './read-BupIJSPJ.js';
6
- import { C as CanvasService } from './service-CR4-r1yx.js';
5
+ import { P as Principal } from './read-CeWoSAAy.js';
6
+ import { C as CanvasService } from './service-Ckz-3qhK.js';
7
7
  import 'yjs';
8
8
  import 'zod';
9
9
 
@@ -40,13 +40,13 @@ interface AppOptions {
40
40
  /** Makes the live-room server from the Worker bindings. Without it, the ws route answers 404. */
41
41
  live?: (env: WorkerEnv) => Realtime;
42
42
  }
43
- /** A browser cannot set a header on a WebSocket. It offers the token as the sub-protocols `agentic-canvas.bearer` and the token in base64url. */
44
- declare const WS_PROTOCOL = "agentic-canvas.bearer";
43
+ /** A browser cannot set a header on a WebSocket. It offers the sub-protocols `agentic-canvas` and `agentic-canvas.bearer.<token in base64url>`. */
44
+ declare const WS_PROTOCOL = "agentic-canvas";
45
45
  /** The principal and role for a bearer token, or null. The kind is never defaulted: a grant without a valid kind gets null. */
46
- declare function authenticate(request: Request, env: WorkerEnv): {
46
+ declare function authenticate(request: Request, env: WorkerEnv): Promise<{
47
47
  actor: Principal;
48
48
  role: Role;
49
- } | null;
49
+ } | null>;
50
50
  /** The image types we accept, found by their first bytes. The client's claim of a type is not trusted. */
51
51
  declare function sniffImage(bytes: Uint8Array): string | null;
52
52
  declare function createApp(options?: AppOptions): Hono<App>;
package/dist/worker.js CHANGED
@@ -4,11 +4,11 @@ import {
4
4
  createApp,
5
5
  sniffImage,
6
6
  worker_default
7
- } from "./chunk-V74PWJKY.js";
8
- import "./chunk-G4YTESPS.js";
7
+ } from "./chunk-PH67TSYW.js";
8
+ import "./chunk-66FFQMHI.js";
9
9
  import "./chunk-NWQED42L.js";
10
- import "./chunk-JCYNPYYJ.js";
11
- import "./chunk-SPP6PINW.js";
10
+ import "./chunk-ZSPZMGHG.js";
11
+ import "./chunk-JOBTIAFJ.js";
12
12
  export {
13
13
  WS_PROTOCOL,
14
14
  authenticate,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-canvas",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "A shared whiteboard for diagrams, drawn by people and agents.",
5
5
  "keywords": [
6
6
  "whiteboard",
@@ -81,7 +81,7 @@
81
81
  "@dagrejs/dagre": "^3.1.3",
82
82
  "@modelcontextprotocol/sdk": "^1.12.0",
83
83
  "agentic-design": "^0.2.0",
84
- "agentic-identity": "^0.5.0",
84
+ "agentic-identity": "^0.6.0",
85
85
  "agentic-realtime": "^0.3.0",
86
86
  "yjs": "^13.6.0",
87
87
  "zod": "^3.25.0"
@@ -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
- };