agentic-canvas 0.9.0 → 0.11.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 +68 -6
- package/dist/{chunk-JQCUVXXD.js → chunk-5II2WZIL.js} +2 -2
- package/dist/chunk-EDQNA2SU.js +290 -0
- package/dist/{chunk-33ANMQEY.js → chunk-EE725BJ3.js} +38 -10
- package/dist/{chunk-ZSPZMGHG.js → chunk-MVWUIBGV.js} +50 -320
- package/dist/{chunk-M74ENUWR.js → chunk-Z6KLGAFP.js} +6 -6
- package/dist/cli.js +130 -6
- package/dist/client-Dyd30StF.d.ts +153 -0
- package/dist/client.d.ts +118 -4
- package/dist/client.js +12 -1
- package/dist/embed.d.ts +31 -6
- package/dist/embed.js +94 -19
- package/dist/index.d.ts +9 -7
- package/dist/index.js +11 -11
- package/dist/live-worker.d.ts +2 -2
- package/dist/live-worker.js +4 -4
- package/dist/live.d.ts +2 -2
- package/dist/live.js +6 -5
- package/dist/{read--Jd3kE13.d.ts → read-UIJlrkwx.d.ts} +1 -1
- package/dist/{service-OiTtNadu.d.ts → service-ojpxVNVl.d.ts} +1 -1
- package/dist/worker.d.ts +2 -2
- package/dist/worker.js +4 -4
- package/llms.txt +3 -3
- package/package.json +3 -3
- package/dist/chunk-JOBTIAFJ.js +0 -19
package/dist/cli.js
CHANGED
|
@@ -5,22 +5,112 @@ import {
|
|
|
5
5
|
operatorActor,
|
|
6
6
|
runMcp,
|
|
7
7
|
svgToPng
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-EE725BJ3.js";
|
|
9
9
|
import {
|
|
10
10
|
openLocalStore
|
|
11
11
|
} from "./chunk-NRLF6MGR.js";
|
|
12
12
|
import {
|
|
13
13
|
CanvasService
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-Z6KLGAFP.js";
|
|
15
15
|
import "./chunk-NWQED42L.js";
|
|
16
|
+
import "./chunk-EDQNA2SU.js";
|
|
16
17
|
import {
|
|
17
18
|
CanvasError
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import "./chunk-JOBTIAFJ.js";
|
|
19
|
+
} from "./chunk-MVWUIBGV.js";
|
|
20
20
|
|
|
21
21
|
// src/cli.ts
|
|
22
22
|
import { readFileSync, writeFileSync } from "fs";
|
|
23
23
|
import { parseArgs } from "util";
|
|
24
|
+
|
|
25
|
+
// src/client.ts
|
|
26
|
+
var CanvasApiError = class extends Error {
|
|
27
|
+
constructor(code, message, status) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.code = code;
|
|
30
|
+
this.status = status;
|
|
31
|
+
this.name = "CanvasApiError";
|
|
32
|
+
}
|
|
33
|
+
code;
|
|
34
|
+
status;
|
|
35
|
+
};
|
|
36
|
+
function parseSubject(text) {
|
|
37
|
+
const at = text.indexOf(":");
|
|
38
|
+
const kind = text.slice(0, at);
|
|
39
|
+
if (at < 1 || kind !== "human" && kind !== "agent" || at === text.length - 1) throw new TypeError(`"${text}" is not human:<id> or agent:<id>`);
|
|
40
|
+
return { subjectKind: kind, subjectId: text.slice(at + 1) };
|
|
41
|
+
}
|
|
42
|
+
function createClient(options) {
|
|
43
|
+
const { token } = options;
|
|
44
|
+
const base = options.baseUrl.replace(/\/+$/, "");
|
|
45
|
+
if (!token) throw new TypeError("token is required");
|
|
46
|
+
const doFetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
47
|
+
const seg = encodeURIComponent;
|
|
48
|
+
async function send(method, path, init, call) {
|
|
49
|
+
const headers = { authorization: `Bearer ${token}` };
|
|
50
|
+
if (init.json !== void 0) headers["content-type"] = "application/json";
|
|
51
|
+
if (init.contentType) headers["content-type"] = init.contentType;
|
|
52
|
+
let response;
|
|
53
|
+
try {
|
|
54
|
+
response = await doFetch(`${base}${path}`, {
|
|
55
|
+
method,
|
|
56
|
+
headers,
|
|
57
|
+
body: init.json === void 0 ? init.body : JSON.stringify(init.json),
|
|
58
|
+
signal: call?.signal
|
|
59
|
+
});
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (call?.signal?.aborted) throw error;
|
|
62
|
+
throw new CanvasApiError("NETWORK", error instanceof Error ? error.message : "the request failed", 0);
|
|
63
|
+
}
|
|
64
|
+
if (response.ok) return response;
|
|
65
|
+
let code = "HTTP_ERROR";
|
|
66
|
+
let message = `the server answered ${response.status}`;
|
|
67
|
+
try {
|
|
68
|
+
const { error } = await response.json();
|
|
69
|
+
if (typeof error?.code === "string") code = error.code;
|
|
70
|
+
if (typeof error?.message === "string") message = error.message;
|
|
71
|
+
} catch {
|
|
72
|
+
if (call?.signal?.aborted) throw call.signal.reason;
|
|
73
|
+
}
|
|
74
|
+
throw new CanvasApiError(code, message, response.status);
|
|
75
|
+
}
|
|
76
|
+
const parse = async (response, call) => {
|
|
77
|
+
try {
|
|
78
|
+
return await response.json();
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (call?.signal?.aborted) throw error;
|
|
81
|
+
throw new CanvasApiError("HTTP_ERROR", "the server did not answer with JSON", response.status);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const json = async (method, path, body, call) => parse(await send(method, path, { json: body }, call), call);
|
|
85
|
+
const bytes = async (response) => new Uint8Array(await response.arrayBuffer());
|
|
86
|
+
return {
|
|
87
|
+
me: (o) => json("GET", "/me", void 0, o),
|
|
88
|
+
listBoards: (o) => json("GET", "/boards", void 0, o),
|
|
89
|
+
createBoard: (input = {}, o) => json("POST", "/boards", input, o),
|
|
90
|
+
readBoard: (id, o) => json("GET", `/boards/${seg(id)}`, void 0, o),
|
|
91
|
+
deleteBoard: (id, o) => json("DELETE", `/boards/${seg(id)}`, void 0, o),
|
|
92
|
+
exportSvg: async (id, o) => (await send("GET", `/boards/${seg(id)}/svg`, {}, o)).text(),
|
|
93
|
+
addShape: (id, shape, o) => json("POST", `/boards/${seg(id)}/shapes`, shape, o),
|
|
94
|
+
addConnector: (id, connector, o) => json("POST", `/boards/${seg(id)}/connectors`, connector, o),
|
|
95
|
+
drawFlow: (id, text, o) => json("POST", `/boards/${seg(id)}/flow`, { text }, o),
|
|
96
|
+
update: (id, element, patch, o) => json("PATCH", `/boards/${seg(id)}/elements/${seg(element)}`, patch, o),
|
|
97
|
+
move: (id, element, dx, dy, o) => json("POST", `/boards/${seg(id)}/move`, { element, dx, dy }, o),
|
|
98
|
+
remove: (id, element, o) => json("DELETE", `/boards/${seg(id)}/elements/${seg(element)}`, void 0, o),
|
|
99
|
+
access: (id, o) => json("GET", `/boards/${seg(id)}/access`, void 0, o),
|
|
100
|
+
grant: (id, input, o) => json("POST", `/boards/${seg(id)}/grants`, input, o),
|
|
101
|
+
revoke: (id, { subjectKind, subjectId }, o) => json("DELETE", `/boards/${seg(id)}/grants?subjectKind=${seg(subjectKind)}&subjectId=${seg(subjectId)}`, void 0, o),
|
|
102
|
+
invite: (id, input, o) => json("POST", `/boards/${seg(id)}/invites`, input, o),
|
|
103
|
+
uploadImage: async (data, o) => parse(await send("POST", "/images", { body: data, contentType: "application/octet-stream" }, o), o),
|
|
104
|
+
getImage: async (id, o) => {
|
|
105
|
+
const response = await send("GET", `/images/${seg(id)}`, {}, o);
|
|
106
|
+
return { bytes: await bytes(response), type: response.headers.get("content-type") ?? "application/octet-stream" };
|
|
107
|
+
},
|
|
108
|
+
state: async (id, o) => bytes(await send("GET", `/boards/${seg(id)}/state`, {}, o)),
|
|
109
|
+
checkpoint: async (id, update, o) => parse(await send("POST", `/boards/${seg(id)}/checkpoint`, { body: update, contentType: "application/octet-stream" }, o), o)
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/cli.ts
|
|
24
114
|
var UsageError = class extends Error {
|
|
25
115
|
};
|
|
26
116
|
var HELP = `${BRAND.command} ${BRAND.version}
|
|
@@ -41,11 +131,16 @@ Commands:
|
|
|
41
131
|
flow <board> --text <t> | --file <p> Draw a flowchart from text ("A -> B: label"). Use --file - for stdin.
|
|
42
132
|
export <board> [--format svg|png] [--out <path>] Print SVG, or write PNG to --out.
|
|
43
133
|
repair <board> [--drop <element>]... Owner only: fix a broken or over-cap board. Needs CANVAS_ACTOR=human:<id>.
|
|
134
|
+
access <board> Server only. List the grants and open invites. Owner.
|
|
135
|
+
grant <board> <human|agent>:<id> --role <viewer|commenter|editor|owner> Server only. Give a role. Owner.
|
|
136
|
+
revoke <board> <human|agent>:<id> Server only. Take a role back. Owner.
|
|
137
|
+
invite <board> --role <viewer|commenter|editor> --email <e> | --handle <h> [--expires-in-ms <n>] Server only. Owner.
|
|
44
138
|
mcp Serve these as MCP tools on stdio. Needs CANVAS_ACTOR.
|
|
45
139
|
|
|
46
140
|
Live mode: set CANVAS_SERVER=<worker url> and CANVAS_TOKEN=<bearer token>. Every command then runs in the live room of
|
|
47
141
|
the board on that server, so a person and an agent can edit one board at once. The token sets the actor, the kind,
|
|
48
142
|
and the role, so CANVAS_ACTOR is not needed. If it is set, it must match the token. --dir, CANVAS_DIR, and repair are local only.
|
|
143
|
+
The sharing commands need CANVAS_SERVER: a local store has no owners.
|
|
49
144
|
|
|
50
145
|
Write commands need an actor in CANVAS_ACTOR as <kind>:<id>, such as "agent:helper".
|
|
51
146
|
The operator sets it. A caller cannot pick its own kind: there is no --actor flag.
|
|
@@ -69,6 +164,10 @@ var flags = {
|
|
|
69
164
|
file: { type: "string" },
|
|
70
165
|
prefix: { type: "string" },
|
|
71
166
|
format: { type: "string" },
|
|
167
|
+
role: { type: "string" },
|
|
168
|
+
email: { type: "string" },
|
|
169
|
+
handle: { type: "string" },
|
|
170
|
+
"expires-in-ms": { type: "string" },
|
|
72
171
|
out: { type: "string" },
|
|
73
172
|
drop: { type: "string", multiple: true },
|
|
74
173
|
help: { type: "boolean" },
|
|
@@ -117,6 +216,17 @@ async function run(argv) {
|
|
|
117
216
|
throw error;
|
|
118
217
|
}
|
|
119
218
|
};
|
|
219
|
+
const sharing = () => {
|
|
220
|
+
if (!server) throw new UsageError(`"${command}" needs a server: set CANVAS_SERVER and CANVAS_TOKEN`);
|
|
221
|
+
return createClient({ baseUrl: `${server.replace(/\/$/, "")}/api/v1/canvas`, token: process.env.CANVAS_TOKEN });
|
|
222
|
+
};
|
|
223
|
+
const subject = (text) => {
|
|
224
|
+
try {
|
|
225
|
+
return parseSubject(text ?? "");
|
|
226
|
+
} catch (error) {
|
|
227
|
+
throw new UsageError(error.message);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
120
230
|
const print = (value) => console.log(JSON.stringify(value));
|
|
121
231
|
switch (command) {
|
|
122
232
|
case "new":
|
|
@@ -169,9 +279,23 @@ async function run(argv) {
|
|
|
169
279
|
case "repair":
|
|
170
280
|
need(1, "<board> [--drop <element>]...");
|
|
171
281
|
return print(await service.repair(board, actor(), v.drop ?? []));
|
|
282
|
+
case "access":
|
|
283
|
+
need(1, "<board>");
|
|
284
|
+
return print(await sharing().access(board));
|
|
285
|
+
case "grant":
|
|
286
|
+
need(2, "<board> <human|agent>:<id> --role <role>");
|
|
287
|
+
return print(await sharing().grant(board, { ...subject(rest[0]), role: v.role }));
|
|
288
|
+
case "revoke":
|
|
289
|
+
need(2, "<board> <human|agent>:<id>");
|
|
290
|
+
return print(await sharing().revoke(board, subject(rest[0])));
|
|
291
|
+
case "invite": {
|
|
292
|
+
need(1, "<board> --role <role> --email <e> | --handle <h> [--expires-in-ms <n>]");
|
|
293
|
+
const expiresInMs = num(v["expires-in-ms"]);
|
|
294
|
+
return print(await sharing().invite(board, { role: v.role, email: v.email, handle: v.handle, ...expiresInMs === void 0 ? {} : { expiresInMs } }));
|
|
295
|
+
}
|
|
172
296
|
case "mcp":
|
|
173
297
|
need(0, "");
|
|
174
|
-
return runMcp(service, actor());
|
|
298
|
+
return runMcp(service, actor(), server ? sharing() : void 0);
|
|
175
299
|
default:
|
|
176
300
|
throw new UsageError(`unknown command "${command}"`);
|
|
177
301
|
}
|
|
@@ -182,7 +306,7 @@ run(process.argv.slice(2)).catch((error) => {
|
|
|
182
306
|
console.error(`${error.message}
|
|
183
307
|
Run "${BRAND.command} --help" for usage.`);
|
|
184
308
|
process.exitCode = 2;
|
|
185
|
-
} else if (error instanceof CanvasError) {
|
|
309
|
+
} else if (error instanceof CanvasError || error instanceof CanvasApiError) {
|
|
186
310
|
console.error(`${error.code}: ${error.message}`);
|
|
187
311
|
process.exitCode = 1;
|
|
188
312
|
} else {
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { a as BoardData, c as ShapeType, S as Shape, E as Endpoint, C as Connector } from './read-UIJlrkwx.js';
|
|
2
|
+
import { PrincipalKind } from 'agentic-identity';
|
|
3
|
+
|
|
4
|
+
/** Options for every call. */
|
|
5
|
+
interface CallOptions {
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
}
|
|
8
|
+
interface ShapeInput {
|
|
9
|
+
id?: string;
|
|
10
|
+
type: ShapeType;
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
w?: number;
|
|
14
|
+
h?: number;
|
|
15
|
+
text?: string;
|
|
16
|
+
fill?: string;
|
|
17
|
+
stroke?: string;
|
|
18
|
+
/** A `data:` URL or a `blob:` ref, for an image. */
|
|
19
|
+
src?: string;
|
|
20
|
+
}
|
|
21
|
+
interface ConnectorInput {
|
|
22
|
+
id?: string;
|
|
23
|
+
from: Endpoint;
|
|
24
|
+
to: Endpoint;
|
|
25
|
+
text?: string;
|
|
26
|
+
startArrow?: boolean;
|
|
27
|
+
endArrow?: boolean;
|
|
28
|
+
stroke?: string;
|
|
29
|
+
}
|
|
30
|
+
type ShapePatch = Partial<Pick<ShapeInput, "x" | "y" | "w" | "h" | "text" | "fill" | "stroke" | "src">>;
|
|
31
|
+
type ConnectorPatch = Partial<Pick<ConnectorInput, "from" | "to" | "text" | "startArrow" | "endArrow" | "stroke">>;
|
|
32
|
+
/** A role on a board, from least to most. */
|
|
33
|
+
type BoardRole = "viewer" | "commenter" | "editor" | "owner";
|
|
34
|
+
/** Who holds a grant. A person and an agent may share one id and hold separate roles. */
|
|
35
|
+
type SubjectKind = "human" | "agent";
|
|
36
|
+
interface Subject {
|
|
37
|
+
subjectKind: SubjectKind;
|
|
38
|
+
subjectId: string;
|
|
39
|
+
}
|
|
40
|
+
interface BoardGrant {
|
|
41
|
+
subjectKind: SubjectKind;
|
|
42
|
+
subjectId: string;
|
|
43
|
+
role: BoardRole;
|
|
44
|
+
[field: string]: unknown;
|
|
45
|
+
}
|
|
46
|
+
/** An open invite. It never shows the email. */
|
|
47
|
+
interface BoardInvite {
|
|
48
|
+
id: string;
|
|
49
|
+
role: BoardRole;
|
|
50
|
+
[field: string]: unknown;
|
|
51
|
+
}
|
|
52
|
+
interface InviteInput {
|
|
53
|
+
/** Not `owner`: only a grant makes an owner. */
|
|
54
|
+
role: Exclude<BoardRole, "owner">;
|
|
55
|
+
/** The person to invite. Give this or `handle`. */
|
|
56
|
+
email?: string;
|
|
57
|
+
/** The agent to invite. Give this or `email`. */
|
|
58
|
+
handle?: string;
|
|
59
|
+
/** How long the invite lives, in milliseconds. */
|
|
60
|
+
expiresInMs?: number;
|
|
61
|
+
}
|
|
62
|
+
interface ImageInfo {
|
|
63
|
+
id: string;
|
|
64
|
+
ref: string;
|
|
65
|
+
type: string;
|
|
66
|
+
size: number;
|
|
67
|
+
/** False when the same bytes were stored before. */
|
|
68
|
+
created: boolean;
|
|
69
|
+
}
|
|
70
|
+
interface CanvasClient {
|
|
71
|
+
/** Who the token stands for. */
|
|
72
|
+
me(options?: CallOptions): Promise<{
|
|
73
|
+
actor: string;
|
|
74
|
+
kind: PrincipalKind;
|
|
75
|
+
role: "editor" | "viewer";
|
|
76
|
+
}>;
|
|
77
|
+
/** The first 100 boards, with titles. */
|
|
78
|
+
listBoards(options?: CallOptions): Promise<{
|
|
79
|
+
boards: {
|
|
80
|
+
id: string;
|
|
81
|
+
title: string;
|
|
82
|
+
}[];
|
|
83
|
+
}>;
|
|
84
|
+
createBoard(input?: {
|
|
85
|
+
id?: string;
|
|
86
|
+
title?: string;
|
|
87
|
+
}, options?: CallOptions): Promise<{
|
|
88
|
+
id: string;
|
|
89
|
+
title: string;
|
|
90
|
+
}>;
|
|
91
|
+
readBoard(id: string, options?: CallOptions): Promise<BoardData & {
|
|
92
|
+
id: string;
|
|
93
|
+
}>;
|
|
94
|
+
/** Delete a board for good, with its live room. Returns the id. */
|
|
95
|
+
deleteBoard(id: string, options?: CallOptions): Promise<{
|
|
96
|
+
deleted: string;
|
|
97
|
+
}>;
|
|
98
|
+
/** The board as an SVG document. */
|
|
99
|
+
exportSvg(id: string, options?: CallOptions): Promise<string>;
|
|
100
|
+
addShape(id: string, shape: ShapeInput, options?: CallOptions): Promise<Shape>;
|
|
101
|
+
addConnector(id: string, connector: ConnectorInput, options?: CallOptions): Promise<Connector>;
|
|
102
|
+
/** Draw a flowchart from text such as `A -> B: label`. Returns the new ids. */
|
|
103
|
+
drawFlow(id: string, text: string, options?: CallOptions): Promise<{
|
|
104
|
+
nodes: string[];
|
|
105
|
+
edges: string[];
|
|
106
|
+
}>;
|
|
107
|
+
/** Change fields of one shape or connector, such as its size or text. The patch holds at least one field. */
|
|
108
|
+
update(id: string, element: string, patch: ShapePatch | ConnectorPatch, options?: CallOptions): Promise<Shape | Connector>;
|
|
109
|
+
/** Move an element. Returns the ids that moved: a shape takes its connectors with it. */
|
|
110
|
+
move(id: string, element: string, dx: number, dy: number, options?: CallOptions): Promise<{
|
|
111
|
+
moved: string[];
|
|
112
|
+
}>;
|
|
113
|
+
/** Remove an element. Returns the ids removed. */
|
|
114
|
+
remove(id: string, element: string, options?: CallOptions): Promise<{
|
|
115
|
+
removed: string[];
|
|
116
|
+
}>;
|
|
117
|
+
/** The grants and the open invites of a board. Needs the owner role. */
|
|
118
|
+
access(id: string, options?: CallOptions): Promise<{
|
|
119
|
+
grants: BoardGrant[];
|
|
120
|
+
invites: BoardInvite[];
|
|
121
|
+
}>;
|
|
122
|
+
/** Give a person or an agent a role on a board. Needs the owner role. */
|
|
123
|
+
grant(id: string, input: Subject & {
|
|
124
|
+
role: BoardRole;
|
|
125
|
+
}, options?: CallOptions): Promise<{
|
|
126
|
+
ok: true;
|
|
127
|
+
}>;
|
|
128
|
+
/** Take a role back. The last owner cannot be removed. Needs the owner role. */
|
|
129
|
+
revoke(id: string, input: Subject, options?: CallOptions): Promise<{
|
|
130
|
+
ok: true;
|
|
131
|
+
}>;
|
|
132
|
+
/** Invite a person by email or an agent by handle. Returns the invite id. Needs the owner role. */
|
|
133
|
+
invite(id: string, input: InviteInput, options?: CallOptions): Promise<{
|
|
134
|
+
id: string;
|
|
135
|
+
}>;
|
|
136
|
+
uploadImage(bytes: Uint8Array | ArrayBuffer | Blob, options?: CallOptions): Promise<ImageInfo>;
|
|
137
|
+
/** The bytes of a stored image, and its content type. */
|
|
138
|
+
getImage(id: string, options?: CallOptions): Promise<{
|
|
139
|
+
bytes: Uint8Array;
|
|
140
|
+
type: string;
|
|
141
|
+
}>;
|
|
142
|
+
/** The stored board as a Yjs update. Only a server with live rooms serves it. */
|
|
143
|
+
state(id: string, options?: CallOptions): Promise<Uint8Array>;
|
|
144
|
+
/** Merge a live room's Yjs update into the stored board. Only a server with live rooms serves it. */
|
|
145
|
+
checkpoint(id: string, update: Uint8Array, options?: CallOptions): Promise<{
|
|
146
|
+
elements: number;
|
|
147
|
+
fixed: string[];
|
|
148
|
+
}>;
|
|
149
|
+
}
|
|
150
|
+
/** What the CLI and the MCP server need to share a board. Sharing lives on the server, so it needs a client. */
|
|
151
|
+
type BoardSharing = Pick<CanvasClient, "access" | "grant" | "revoke" | "invite">;
|
|
152
|
+
|
|
153
|
+
export type { BoardSharing as B, CanvasClient as C };
|
package/dist/client.d.ts
CHANGED
|
@@ -1,7 +1,68 @@
|
|
|
1
|
-
import { E as ErrorCode, a as BoardData, c as ShapeType, S as Shape, d as Endpoint, C as Connector } from './read--Jd3kE13.js';
|
|
2
1
|
import { PrincipalKind } from 'agentic-identity';
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
|
|
3
|
+
type Principal = {
|
|
4
|
+
kind: PrincipalKind;
|
|
5
|
+
id: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type ErrorCode = "INVALID" | "DUPLICATE_ID" | "NOT_FOUND" | "BAD_REFERENCE" | "LIMIT" | "UNAVAILABLE" | "FORBIDDEN";
|
|
9
|
+
declare const SHAPE_TYPES: readonly ["rect", "ellipse", "diamond", "text", "sticky", "frame", "image"];
|
|
10
|
+
type ShapeType = (typeof SHAPE_TYPES)[number];
|
|
11
|
+
type Endpoint = {
|
|
12
|
+
id: string;
|
|
13
|
+
} | {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
};
|
|
17
|
+
interface Stamp {
|
|
18
|
+
id: string;
|
|
19
|
+
z: number;
|
|
20
|
+
groupId?: string;
|
|
21
|
+
/** Who made the element. Never changes. */
|
|
22
|
+
author: Principal;
|
|
23
|
+
/** Who made the last edit. */
|
|
24
|
+
editedBy: Principal;
|
|
25
|
+
}
|
|
26
|
+
interface Shape extends Stamp {
|
|
27
|
+
type: ShapeType;
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
w: number;
|
|
31
|
+
h: number;
|
|
32
|
+
text: string;
|
|
33
|
+
fill?: string;
|
|
34
|
+
stroke?: string;
|
|
35
|
+
src?: string;
|
|
36
|
+
}
|
|
37
|
+
interface Connector extends Stamp {
|
|
38
|
+
type: "connector";
|
|
39
|
+
from: Endpoint;
|
|
40
|
+
to: Endpoint;
|
|
41
|
+
text: string;
|
|
42
|
+
startArrow: boolean;
|
|
43
|
+
endArrow: boolean;
|
|
44
|
+
stroke?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface Labels {
|
|
48
|
+
/** The author is an agent or acts for one. */
|
|
49
|
+
machineMade: boolean;
|
|
50
|
+
/** The last edit came from an agent or one acting for one. */
|
|
51
|
+
lastEditMachineMade: boolean;
|
|
52
|
+
}
|
|
53
|
+
type NodeData = Shape & Labels;
|
|
54
|
+
type EdgeData = Connector & Labels;
|
|
55
|
+
interface BoardData {
|
|
56
|
+
title: string;
|
|
57
|
+
nodes: NodeData[];
|
|
58
|
+
edges: EdgeData[];
|
|
59
|
+
groups: {
|
|
60
|
+
id: string;
|
|
61
|
+
members: string[];
|
|
62
|
+
}[];
|
|
63
|
+
/** Every non-empty text, back to front. */
|
|
64
|
+
texts: string[];
|
|
65
|
+
}
|
|
5
66
|
|
|
6
67
|
interface ClientOptions {
|
|
7
68
|
/** The API root, for example `https://canvas.example.com/api/v1/canvas`. */
|
|
@@ -39,6 +100,36 @@ interface ConnectorInput {
|
|
|
39
100
|
}
|
|
40
101
|
type ShapePatch = Partial<Pick<ShapeInput, "x" | "y" | "w" | "h" | "text" | "fill" | "stroke" | "src">>;
|
|
41
102
|
type ConnectorPatch = Partial<Pick<ConnectorInput, "from" | "to" | "text" | "startArrow" | "endArrow" | "stroke">>;
|
|
103
|
+
/** A role on a board, from least to most. */
|
|
104
|
+
type BoardRole = "viewer" | "commenter" | "editor" | "owner";
|
|
105
|
+
/** Who holds a grant. A person and an agent may share one id and hold separate roles. */
|
|
106
|
+
type SubjectKind = "human" | "agent";
|
|
107
|
+
interface Subject {
|
|
108
|
+
subjectKind: SubjectKind;
|
|
109
|
+
subjectId: string;
|
|
110
|
+
}
|
|
111
|
+
interface BoardGrant {
|
|
112
|
+
subjectKind: SubjectKind;
|
|
113
|
+
subjectId: string;
|
|
114
|
+
role: BoardRole;
|
|
115
|
+
[field: string]: unknown;
|
|
116
|
+
}
|
|
117
|
+
/** An open invite. It never shows the email. */
|
|
118
|
+
interface BoardInvite {
|
|
119
|
+
id: string;
|
|
120
|
+
role: BoardRole;
|
|
121
|
+
[field: string]: unknown;
|
|
122
|
+
}
|
|
123
|
+
interface InviteInput {
|
|
124
|
+
/** Not `owner`: only a grant makes an owner. */
|
|
125
|
+
role: Exclude<BoardRole, "owner">;
|
|
126
|
+
/** The person to invite. Give this or `handle`. */
|
|
127
|
+
email?: string;
|
|
128
|
+
/** The agent to invite. Give this or `email`. */
|
|
129
|
+
handle?: string;
|
|
130
|
+
/** How long the invite lives, in milliseconds. */
|
|
131
|
+
expiresInMs?: number;
|
|
132
|
+
}
|
|
42
133
|
interface ImageInfo {
|
|
43
134
|
id: string;
|
|
44
135
|
ref: string;
|
|
@@ -104,6 +195,25 @@ interface CanvasClient {
|
|
|
104
195
|
remove(id: string, element: string, options?: CallOptions): Promise<{
|
|
105
196
|
removed: string[];
|
|
106
197
|
}>;
|
|
198
|
+
/** The grants and the open invites of a board. Needs the owner role. */
|
|
199
|
+
access(id: string, options?: CallOptions): Promise<{
|
|
200
|
+
grants: BoardGrant[];
|
|
201
|
+
invites: BoardInvite[];
|
|
202
|
+
}>;
|
|
203
|
+
/** Give a person or an agent a role on a board. Needs the owner role. */
|
|
204
|
+
grant(id: string, input: Subject & {
|
|
205
|
+
role: BoardRole;
|
|
206
|
+
}, options?: CallOptions): Promise<{
|
|
207
|
+
ok: true;
|
|
208
|
+
}>;
|
|
209
|
+
/** Take a role back. The last owner cannot be removed. Needs the owner role. */
|
|
210
|
+
revoke(id: string, input: Subject, options?: CallOptions): Promise<{
|
|
211
|
+
ok: true;
|
|
212
|
+
}>;
|
|
213
|
+
/** Invite a person by email or an agent by handle. Returns the invite id. Needs the owner role. */
|
|
214
|
+
invite(id: string, input: InviteInput, options?: CallOptions): Promise<{
|
|
215
|
+
id: string;
|
|
216
|
+
}>;
|
|
107
217
|
uploadImage(bytes: Uint8Array | ArrayBuffer | Blob, options?: CallOptions): Promise<ImageInfo>;
|
|
108
218
|
/** The bytes of a stored image, and its content type. */
|
|
109
219
|
getImage(id: string, options?: CallOptions): Promise<{
|
|
@@ -118,6 +228,10 @@ interface CanvasClient {
|
|
|
118
228
|
fixed: string[];
|
|
119
229
|
}>;
|
|
120
230
|
}
|
|
231
|
+
/** What the CLI and the MCP server need to share a board. Sharing lives on the server, so it needs a client. */
|
|
232
|
+
type BoardSharing = Pick<CanvasClient, "access" | "grant" | "revoke" | "invite">;
|
|
233
|
+
/** `kind:id` of a person or an agent, as the CLI and the tools write it. */
|
|
234
|
+
declare function parseSubject(text: string): Subject;
|
|
121
235
|
declare function createClient(options: ClientOptions): CanvasClient;
|
|
122
236
|
|
|
123
|
-
export { type ApiErrorCode, type CallOptions, CanvasApiError, type CanvasClient, type ClientOptions, type ConnectorInput, type ConnectorPatch, type ImageInfo, type ShapeInput, type ShapePatch, createClient };
|
|
237
|
+
export { type ApiErrorCode, type BoardGrant, type BoardInvite, type BoardRole, type BoardSharing, type CallOptions, CanvasApiError, type CanvasClient, type ClientOptions, type ConnectorInput, type ConnectorPatch, type ImageInfo, type InviteInput, type ShapeInput, type ShapePatch, type Subject, type SubjectKind, createClient, parseSubject };
|
package/dist/client.js
CHANGED
|
@@ -9,6 +9,12 @@ var CanvasApiError = class extends Error {
|
|
|
9
9
|
code;
|
|
10
10
|
status;
|
|
11
11
|
};
|
|
12
|
+
function parseSubject(text) {
|
|
13
|
+
const at = text.indexOf(":");
|
|
14
|
+
const kind = text.slice(0, at);
|
|
15
|
+
if (at < 1 || kind !== "human" && kind !== "agent" || at === text.length - 1) throw new TypeError(`"${text}" is not human:<id> or agent:<id>`);
|
|
16
|
+
return { subjectKind: kind, subjectId: text.slice(at + 1) };
|
|
17
|
+
}
|
|
12
18
|
function createClient(options) {
|
|
13
19
|
const { token } = options;
|
|
14
20
|
const base = options.baseUrl.replace(/\/+$/, "");
|
|
@@ -66,6 +72,10 @@ function createClient(options) {
|
|
|
66
72
|
update: (id, element, patch, o) => json("PATCH", `/boards/${seg(id)}/elements/${seg(element)}`, patch, o),
|
|
67
73
|
move: (id, element, dx, dy, o) => json("POST", `/boards/${seg(id)}/move`, { element, dx, dy }, o),
|
|
68
74
|
remove: (id, element, o) => json("DELETE", `/boards/${seg(id)}/elements/${seg(element)}`, void 0, o),
|
|
75
|
+
access: (id, o) => json("GET", `/boards/${seg(id)}/access`, void 0, o),
|
|
76
|
+
grant: (id, input, o) => json("POST", `/boards/${seg(id)}/grants`, input, o),
|
|
77
|
+
revoke: (id, { subjectKind, subjectId }, o) => json("DELETE", `/boards/${seg(id)}/grants?subjectKind=${seg(subjectKind)}&subjectId=${seg(subjectId)}`, void 0, o),
|
|
78
|
+
invite: (id, input, o) => json("POST", `/boards/${seg(id)}/invites`, input, o),
|
|
69
79
|
uploadImage: async (data, o) => parse(await send("POST", "/images", { body: data, contentType: "application/octet-stream" }, o), o),
|
|
70
80
|
getImage: async (id, o) => {
|
|
71
81
|
const response = await send("GET", `/images/${seg(id)}`, {}, o);
|
|
@@ -77,5 +87,6 @@ function createClient(options) {
|
|
|
77
87
|
}
|
|
78
88
|
export {
|
|
79
89
|
CanvasApiError,
|
|
80
|
-
createClient
|
|
90
|
+
createClient,
|
|
91
|
+
parseSubject
|
|
81
92
|
};
|
package/dist/embed.d.ts
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { CanvasClient } from './client.js';
|
|
2
|
+
import { C as CanvasClient } from './client-Dyd30StF.js';
|
|
3
3
|
import { PrincipalKind } from 'agentic-identity';
|
|
4
|
-
import '
|
|
5
|
-
import 'yjs';
|
|
4
|
+
import { SyncClient, Status } from 'agentic-realtime/client';
|
|
5
|
+
import * as Y from 'yjs';
|
|
6
|
+
import './read-UIJlrkwx.js';
|
|
6
7
|
import 'zod';
|
|
7
8
|
|
|
9
|
+
/** The presence of a room: who is here. A `SyncClient` of agentic-realtime has these. */
|
|
10
|
+
type RoomPresence = Pick<SyncClient, "peers" | "onPeers">;
|
|
11
|
+
/**
|
|
12
|
+
* A board that the app keeps itself, in its own Yjs room. The UI reads and writes it, and does not call a client, poll, or
|
|
13
|
+
* open a live room. The app keeps the document and its room.
|
|
14
|
+
*/
|
|
15
|
+
interface HostBoard {
|
|
16
|
+
/** The Yjs document. The UI opens it as a `Board`: its `elements` and `meta` maps, as this package stores them. */
|
|
17
|
+
doc: Y.Doc;
|
|
18
|
+
/** The presence of the app's room. With it, the UI shows who else is here. */
|
|
19
|
+
presence?: RoomPresence;
|
|
20
|
+
/** The connection state now. With neither `status` nor `onStatus`, the UI shows the board as live. With only `onStatus`, it shows not live until the room reports "connected". */
|
|
21
|
+
status?: () => Status;
|
|
22
|
+
/** A way to hear when the connection state changes. It returns a function that stops the listener. The UI reads it once each time it opens `doc`. */
|
|
23
|
+
onStatus?: (listener: (status: Status) => void) => () => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
8
26
|
interface RoomOptions {
|
|
9
27
|
/** The API root, for example `https://canvas.example.com/api/v1/canvas`. */
|
|
10
28
|
baseUrl: string;
|
|
@@ -16,14 +34,21 @@ interface RoomOptions {
|
|
|
16
34
|
interface BoardPrincipal {
|
|
17
35
|
kind: PrincipalKind;
|
|
18
36
|
name: string;
|
|
37
|
+
/** Stamped on each element the principal writes in host mode. The default is `name`, which must then be a valid id. A client mode board takes the actor from its token. */
|
|
38
|
+
id?: string;
|
|
19
39
|
/** Only `editor` can change the board. Any other value, or no value, opens it read only. */
|
|
20
40
|
role?: "editor" | "viewer";
|
|
21
41
|
}
|
|
22
42
|
interface BoardEditorOptions {
|
|
23
43
|
/** The board id. */
|
|
24
44
|
boardId: string;
|
|
25
|
-
/** Loads and changes the board. A client from `createClient` in `agentic-canvas/client` works. */
|
|
26
|
-
client
|
|
45
|
+
/** Loads and changes the board. A client from `createClient` in `agentic-canvas/client` works. Needed unless `host` is given. */
|
|
46
|
+
client?: CanvasClient;
|
|
47
|
+
/**
|
|
48
|
+
* The board in your own Yjs document. The UI then reads and writes that document, and calls no client, does not poll, and
|
|
49
|
+
* opens no live room. `client` or `room` together with `host` throws a `TypeError`.
|
|
50
|
+
*/
|
|
51
|
+
host?: HostBoard;
|
|
27
52
|
principal: BoardPrincipal;
|
|
28
53
|
/** Open the board read only, even for an editor. */
|
|
29
54
|
readOnly?: boolean;
|
|
@@ -43,4 +68,4 @@ interface MountedBoard {
|
|
|
43
68
|
/** Render the board into an element, for an app that does not use React itself. */
|
|
44
69
|
declare function mount(element: HTMLElement, options: BoardEditorOptions): MountedBoard;
|
|
45
70
|
|
|
46
|
-
export { BoardEditor, type BoardEditorOptions, type BoardPrincipal, CanvasClient, type MountedBoard, type RoomOptions, mount };
|
|
71
|
+
export { BoardEditor, type BoardEditorOptions, type BoardPrincipal, CanvasClient, type HostBoard, type MountedBoard, type RoomOptions, mount };
|