bytespost-canvas 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,44 @@
1
+ export type FlagType = 'boolean' | 'string' | 'number' | 'json' | 'file';
2
+ export interface FlagSpec {
3
+ name: string;
4
+ alias?: string;
5
+ type: FlagType;
6
+ valueName?: string;
7
+ required?: boolean | 'when-multiple-tabs';
8
+ description: string;
9
+ }
10
+ export interface ExampleSpec {
11
+ command: string;
12
+ description?: string;
13
+ }
14
+ export interface AiSpec {
15
+ supported: boolean;
16
+ safety: 'read' | 'write' | 'destructive' | 'daemon';
17
+ operation: string;
18
+ context: string[];
19
+ }
20
+ export interface CommandSpec {
21
+ path: string[];
22
+ summary: string;
23
+ description: string;
24
+ usage: string;
25
+ args?: Array<{
26
+ name: string;
27
+ required?: boolean;
28
+ description: string;
29
+ }>;
30
+ flags?: FlagSpec[];
31
+ examples?: ExampleSpec[];
32
+ output?: string;
33
+ ai?: AiSpec;
34
+ }
35
+ export declare const GLOBAL_FLAGS: FlagSpec[];
36
+ export declare const COMMAND_SPECS: CommandSpec[];
37
+ export declare function findCommandSpec(positionals: string[]): {
38
+ spec: CommandSpec | null;
39
+ consumed: number;
40
+ };
41
+ export declare function getTopLevelCommands(): Array<{
42
+ name: string;
43
+ summary: string;
44
+ }>;
@@ -0,0 +1,301 @@
1
+ export const GLOBAL_FLAGS = [
2
+ { name: 'help', alias: 'h', type: 'boolean', description: 'Show command help and exit.' },
3
+ { name: 'json', type: 'boolean', description: 'Print stable machine-readable JSON output.' },
4
+ { name: 'ai', type: 'string', valueName: 'mode', description: 'Plan with the AI command compiler. Modes: plan, auto, explain.' },
5
+ { name: 'prompt', type: 'string', valueName: 'text', description: 'Natural-language instruction for --ai. Use -- for long free-form text.' },
6
+ { name: 'prompt-file', type: 'file', valueName: 'path', description: 'Read --ai instruction text from a UTF-8 file. Use - for stdin.' },
7
+ { name: 'dry-run', type: 'boolean', description: 'Resolve the command without mutating the canvas.' },
8
+ { name: 'yes', type: 'boolean', description: 'Confirm high-risk AI plans when supported.' },
9
+ ];
10
+ const TAB_FLAG = {
11
+ name: 'tab',
12
+ type: 'string',
13
+ valueName: 'id',
14
+ required: 'when-multiple-tabs',
15
+ description: 'Target canvas tab. Optional only when exactly one tab is connected.',
16
+ };
17
+ const OUTPUT_FLAG = {
18
+ name: 'output',
19
+ alias: 'o',
20
+ type: 'file',
21
+ valueName: 'path',
22
+ required: true,
23
+ description: 'Write binary output to this file.',
24
+ };
25
+ const HEX_FLAG = (name, description) => ({
26
+ name,
27
+ type: 'string',
28
+ valueName: 'hex',
29
+ description,
30
+ });
31
+ const NUMBER_FLAG = (name, description, required = false) => ({
32
+ name,
33
+ type: 'number',
34
+ valueName: 'n',
35
+ required,
36
+ description,
37
+ });
38
+ export const COMMAND_SPECS = [
39
+ {
40
+ path: ['start'],
41
+ summary: 'Start the local canvas daemon.',
42
+ description: 'Starts the localhost daemon that browser tabs connect to over WebSocket.',
43
+ usage: 'canvas start',
44
+ examples: [{ command: 'canvasd start' }, { command: 'canvas start' }],
45
+ output: 'Logs the daemon URL and keeps running.',
46
+ ai: { supported: false, safety: 'daemon', operation: 'daemon.start', context: [] },
47
+ },
48
+ {
49
+ path: ['serve'],
50
+ summary: 'Alias for start.',
51
+ description: 'Compatibility alias for canvas start.',
52
+ usage: 'canvas serve',
53
+ examples: [{ command: 'canvas-bridge serve' }],
54
+ output: 'Logs the daemon URL and keeps running.',
55
+ ai: { supported: false, safety: 'daemon', operation: 'daemon.start', context: [] },
56
+ },
57
+ {
58
+ path: ['mcp'],
59
+ summary: 'Run the MCP stdio server.',
60
+ description: 'Exposes the same local canvas control plane as MCP tools over stdio.',
61
+ usage: 'canvas mcp',
62
+ examples: [{ command: 'canvas mcp' }],
63
+ output: 'MCP stdio stream.',
64
+ ai: { supported: false, safety: 'daemon', operation: 'daemon.mcp', context: [] },
65
+ },
66
+ {
67
+ path: ['status'],
68
+ summary: 'Show daemon and tab connection health.',
69
+ description: 'Checks the localhost daemon and reports how many canvas tabs are connected.',
70
+ usage: 'canvas status [--json]',
71
+ examples: [{ command: 'canvas status --json' }],
72
+ output: '{ canvasConnected, tabCount, version }',
73
+ ai: { supported: true, safety: 'read', operation: 'daemon.health', context: ['tabs'] },
74
+ },
75
+ {
76
+ path: ['health'],
77
+ summary: 'Alias for status.',
78
+ description: 'Compatibility alias for canvas status.',
79
+ usage: 'canvas health [--json]',
80
+ examples: [{ command: 'canvas health --json' }],
81
+ output: '{ canvasConnected, tabCount, version }',
82
+ ai: { supported: true, safety: 'read', operation: 'daemon.health', context: ['tabs'] },
83
+ },
84
+ {
85
+ path: ['ls'],
86
+ summary: 'List open canvas tabs.',
87
+ description: 'Lists every browser editor tab connected to the local daemon.',
88
+ usage: 'canvas ls [--json]',
89
+ examples: [{ command: 'canvas ls' }, { command: 'canvas ls --json' }],
90
+ output: '{ tabs: CanvasTabInfo[] }',
91
+ ai: { supported: true, safety: 'read', operation: 'tabs.list', context: ['tabs'] },
92
+ },
93
+ {
94
+ path: ['tab', 'info'],
95
+ summary: 'Show one tab.',
96
+ description: 'Prints metadata for a connected canvas tab.',
97
+ usage: 'canvas tab info <tab> [--json]',
98
+ args: [{ name: 'tab', required: true, description: 'Connected tab id from canvas ls.' }],
99
+ examples: [{ command: 'canvas tab info tab_abc123 --json' }],
100
+ output: '{ tab: CanvasTabInfo }',
101
+ ai: { supported: true, safety: 'read', operation: 'tabs.info', context: ['tabs'] },
102
+ },
103
+ {
104
+ path: ['document', 'get'],
105
+ summary: 'Read the active document.',
106
+ description: 'Returns the full active document from one canvas tab.',
107
+ usage: 'canvas document get --tab <tab> [--json]',
108
+ flags: [TAB_FLAG],
109
+ examples: [{ command: 'canvas document get --tab tab_abc123 --json' }],
110
+ output: '{ document }',
111
+ ai: { supported: true, safety: 'read', operation: 'document.get', context: ['document', 'selection', 'history'] },
112
+ },
113
+ {
114
+ path: ['document', 'nodes'],
115
+ summary: 'List document nodes.',
116
+ description: 'Returns a flattened node list with ids, types, names, parents, pages, and depth.',
117
+ usage: 'canvas document nodes --tab <tab> [--json]',
118
+ flags: [TAB_FLAG],
119
+ examples: [{ command: 'canvas document nodes --tab tab_abc123 --json' }],
120
+ output: '{ nodes: Array<{ id, type, name, parentId, pageId, depth }> }',
121
+ ai: { supported: true, safety: 'read', operation: 'document.nodes', context: ['document', 'selection', 'history'] },
122
+ },
123
+ {
124
+ path: ['draw'],
125
+ summary: 'Create nodes from a JSON spec.',
126
+ description: 'Creates one or more 2D nodes from inline JSON or a JSON file.',
127
+ usage: 'canvas draw --tab <tab> <json-or-file> [--json]',
128
+ args: [{ name: 'json-or-file', required: true, description: 'Inline JSON or path to a JSON file.' }],
129
+ flags: [TAB_FLAG],
130
+ examples: [{ command: "canvas draw --tab tab_abc123 '[{\"type\":\"rect\",\"x\":100,\"y\":100,\"w\":240,\"h\":120}]'" }],
131
+ output: '{ nodeIds: string[] }',
132
+ ai: { supported: true, safety: 'write', operation: 'node.create.batch', context: ['document', 'selection', 'viewport', 'history'] },
133
+ },
134
+ {
135
+ path: ['draw', 'rect'],
136
+ summary: 'Create a rectangle.',
137
+ description: 'Creates a rectangle in canvas coordinates.',
138
+ usage: 'canvas draw rect --tab <tab> --x <n> --y <n> --w <n> --h <n> [flags]',
139
+ flags: [
140
+ TAB_FLAG,
141
+ NUMBER_FLAG('x', 'Left position in canvas coordinates.', true),
142
+ NUMBER_FLAG('y', 'Top position in canvas coordinates.', true),
143
+ NUMBER_FLAG('w', 'Width.', true),
144
+ NUMBER_FLAG('h', 'Height.', true),
145
+ HEX_FLAG('fill', 'Fill color, e.g. #38bdf8.'),
146
+ HEX_FLAG('stroke', 'Stroke color.'),
147
+ NUMBER_FLAG('stroke-width', 'Stroke width.'),
148
+ NUMBER_FLAG('radius', 'Corner radius.'),
149
+ { name: 'name', type: 'string', valueName: 'text', description: 'Node name.' },
150
+ ],
151
+ examples: [{ command: "canvas draw rect --tab tab_abc123 --x 100 --y 100 --w 240 --h 120 --fill '#38bdf8'" }],
152
+ output: '{ nodeIds: string[] }',
153
+ ai: { supported: true, safety: 'write', operation: 'node.create.rect', context: ['document', 'selection', 'viewport', 'history'] },
154
+ },
155
+ {
156
+ path: ['draw', 'frame'],
157
+ summary: 'Create a frame.',
158
+ description: 'Creates a frame in canvas coordinates.',
159
+ usage: 'canvas draw frame --tab <tab> --x <n> --y <n> --w <n> --h <n> [flags]',
160
+ flags: [TAB_FLAG, NUMBER_FLAG('x', 'Left position.', true), NUMBER_FLAG('y', 'Top position.', true), NUMBER_FLAG('w', 'Width.', true), NUMBER_FLAG('h', 'Height.', true), HEX_FLAG('fill', 'Fill color.'), { name: 'name', type: 'string', valueName: 'text', description: 'Node name.' }],
161
+ examples: [{ command: "canvas draw frame --tab tab_abc123 --x 80 --y 80 --w 960 --h 540 --fill '#ffffff'" }],
162
+ output: '{ nodeIds: string[] }',
163
+ ai: { supported: true, safety: 'write', operation: 'node.create.frame', context: ['document', 'selection', 'viewport', 'history'] },
164
+ },
165
+ {
166
+ path: ['draw', 'text'],
167
+ summary: 'Create text.',
168
+ description: 'Creates a text node in canvas coordinates.',
169
+ usage: 'canvas draw text --tab <tab> --x <n> --y <n> --text <text> [flags]',
170
+ flags: [TAB_FLAG, NUMBER_FLAG('x', 'Left position.', true), NUMBER_FLAG('y', 'Top position.', true), { name: 'text', type: 'string', valueName: 'text', required: true, description: 'Text content.' }, NUMBER_FLAG('font-size', 'Font size.'), HEX_FLAG('color', 'Text color.'), NUMBER_FLAG('w', 'Optional fixed box width.'), { name: 'name', type: 'string', valueName: 'text', description: 'Node name.' }],
171
+ examples: [{ command: "canvas draw text --tab tab_abc123 --x 120 --y 140 --text 'Hello' --font-size 32" }],
172
+ output: '{ nodeIds: string[] }',
173
+ ai: { supported: true, safety: 'write', operation: 'node.create.text', context: ['document', 'selection', 'viewport', 'history'] },
174
+ },
175
+ {
176
+ path: ['draw', 'line'],
177
+ summary: 'Create a line.',
178
+ description: 'Creates a line in canvas coordinates.',
179
+ usage: 'canvas draw line --tab <tab> --x1 <n> --y1 <n> --x2 <n> --y2 <n> [flags]',
180
+ flags: [TAB_FLAG, NUMBER_FLAG('x1', 'Start x.', true), NUMBER_FLAG('y1', 'Start y.', true), NUMBER_FLAG('x2', 'End x.', true), NUMBER_FLAG('y2', 'End y.', true), HEX_FLAG('stroke', 'Stroke color.'), NUMBER_FLAG('width', 'Stroke width.'), { name: 'name', type: 'string', valueName: 'text', description: 'Node name.' }],
181
+ examples: [{ command: "canvas draw line --tab tab_abc123 --x1 0 --y1 0 --x2 200 --y2 120 --stroke '#111827'" }],
182
+ output: '{ nodeIds: string[] }',
183
+ ai: { supported: true, safety: 'write', operation: 'node.create.line', context: ['document', 'selection', 'viewport', 'history'] },
184
+ },
185
+ {
186
+ path: ['node', 'get'],
187
+ summary: 'Read one node.',
188
+ description: 'Gets one node by id.',
189
+ usage: 'canvas node get <nodeId> --tab <tab> [--json]',
190
+ args: [{ name: 'nodeId', required: true, description: 'Node id.' }],
191
+ flags: [TAB_FLAG],
192
+ output: '{ node }',
193
+ ai: { supported: true, safety: 'read', operation: 'node.get', context: ['document', 'selection', 'history'] },
194
+ },
195
+ {
196
+ path: ['node', 'update'],
197
+ summary: 'Patch one node.',
198
+ description: 'Applies a shallow JSON patch to one node.',
199
+ usage: 'canvas node update <nodeId> --tab <tab> --patch <json> [--json]',
200
+ args: [{ name: 'nodeId', required: true, description: 'Node id.' }],
201
+ flags: [TAB_FLAG, { name: 'patch', type: 'json', valueName: 'json', required: true, description: 'Shallow JSON patch.' }, { name: 'data', type: 'json', valueName: 'json', description: 'Alias for --patch.' }],
202
+ output: '{ updated, nodeId }',
203
+ ai: { supported: true, safety: 'write', operation: 'node.update', context: ['document', 'selection', 'history'] },
204
+ },
205
+ {
206
+ path: ['node', 'delete'],
207
+ summary: 'Delete one node.',
208
+ description: 'Deletes one node by id.',
209
+ usage: 'canvas node delete <nodeId> --tab <tab> [--json]',
210
+ args: [{ name: 'nodeId', required: true, description: 'Node id.' }],
211
+ flags: [TAB_FLAG],
212
+ output: '{ deleted, nodeId }',
213
+ ai: { supported: true, safety: 'destructive', operation: 'node.delete', context: ['document', 'selection', 'history'] },
214
+ },
215
+ {
216
+ path: ['select', 'set'],
217
+ summary: 'Set selection.',
218
+ description: 'Replaces the current selection with one node id.',
219
+ usage: 'canvas select set <nodeId> --tab <tab> [--json]',
220
+ args: [{ name: 'nodeId', required: true, description: 'Node id.' }],
221
+ flags: [TAB_FLAG],
222
+ output: '{ selectedNodeIds }',
223
+ ai: { supported: true, safety: 'write', operation: 'selection.set', context: ['document', 'selection', 'viewport'] },
224
+ },
225
+ {
226
+ path: ['select', 'clear'],
227
+ summary: 'Clear selection.',
228
+ description: 'Clears the current selection.',
229
+ usage: 'canvas select clear --tab <tab> [--json]',
230
+ flags: [TAB_FLAG],
231
+ output: '{ selectedNodeIds: [] }',
232
+ ai: { supported: true, safety: 'write', operation: 'selection.clear', context: ['selection'] },
233
+ },
234
+ {
235
+ path: ['viewport', 'screenshot'],
236
+ summary: 'Capture a PNG screenshot.',
237
+ description: 'Writes a PNG screenshot of one canvas tab.',
238
+ usage: 'canvas viewport screenshot --tab <tab> -o <file>',
239
+ flags: [TAB_FLAG, OUTPUT_FLAG],
240
+ output: 'PNG bytes written to --output.',
241
+ ai: { supported: true, safety: 'read', operation: 'viewport.screenshot', context: ['viewport'] },
242
+ },
243
+ {
244
+ path: ['screenshot'],
245
+ summary: 'Alias for viewport screenshot.',
246
+ description: 'Writes a PNG screenshot of one canvas tab.',
247
+ usage: 'canvas screenshot --tab <tab> -o <file>',
248
+ flags: [TAB_FLAG, OUTPUT_FLAG],
249
+ output: 'PNG bytes written to --output.',
250
+ ai: { supported: true, safety: 'read', operation: 'viewport.screenshot', context: ['viewport'] },
251
+ },
252
+ {
253
+ path: ['viewport', 'fit'],
254
+ summary: 'Fit viewport to document.',
255
+ description: 'Frames the document in the viewport.',
256
+ usage: 'canvas viewport fit --tab <tab> [--json]',
257
+ flags: [TAB_FLAG],
258
+ output: '{ ok: true }',
259
+ ai: { supported: true, safety: 'write', operation: 'viewport.fit', context: ['document', 'viewport'] },
260
+ },
261
+ {
262
+ path: ['viewport', 'zoom'],
263
+ summary: 'Set viewport zoom.',
264
+ description: 'Sets the 2D viewport zoom.',
265
+ usage: 'canvas viewport zoom --tab <tab> --to <n> [--json]',
266
+ flags: [TAB_FLAG, NUMBER_FLAG('to', 'Target zoom.', true)],
267
+ output: '{ zoom }',
268
+ ai: { supported: true, safety: 'write', operation: 'viewport.zoom', context: ['viewport'] },
269
+ },
270
+ {
271
+ path: ['building'],
272
+ summary: 'Create building-domain elements.',
273
+ description: 'Calls building design creation tools such as wall, room, slab, roof, door, window, zone, or furniture.',
274
+ usage: 'canvas building <wall|room|slab|roof|door|window|zone|furniture> --tab <tab> [flags]',
275
+ args: [{ name: 'buildingType', required: true, description: 'Building element type.' }],
276
+ flags: [TAB_FLAG, NUMBER_FLAG('start-x', 'Wall start x.'), NUMBER_FLAG('start-z', 'Wall start z.'), NUMBER_FLAG('end-x', 'Wall end x.'), NUMBER_FLAG('end-z', 'Wall end z.'), NUMBER_FLAG('x', 'X position.'), NUMBER_FLAG('y', 'Y position.'), NUMBER_FLAG('z', 'Z position.'), NUMBER_FLAG('w', 'Width.'), NUMBER_FLAG('d', 'Depth.'), { name: 'label', type: 'string', valueName: 'text', description: 'Label.' }, { name: 'type', type: 'string', valueName: 'text', description: 'Furniture type.' }, HEX_FLAG('color', 'Color.')],
277
+ examples: [
278
+ { command: 'canvas building wall --tab tab_abc123 --start-x 0 --start-z 0 --end-x 400 --end-z 0' },
279
+ { command: 'canvas building room --tab tab_abc123 --x 0 --z 0 --w 500 --d 400 --label Kitchen' },
280
+ ],
281
+ output: '{ result }',
282
+ ai: { supported: true, safety: 'write', operation: 'building.create', context: ['document', 'selection', 'viewport', 'history', 'building'] },
283
+ },
284
+ ];
285
+ export function findCommandSpec(positionals) {
286
+ const matches = COMMAND_SPECS
287
+ .filter((spec) => spec.path.every((part, index) => positionals[index] === part))
288
+ .sort((a, b) => b.path.length - a.path.length);
289
+ const spec = matches[0] ?? null;
290
+ return { spec, consumed: spec?.path.length ?? 0 };
291
+ }
292
+ export function getTopLevelCommands() {
293
+ const seen = new Map();
294
+ for (const spec of COMMAND_SPECS) {
295
+ const [name] = spec.path;
296
+ if (name && !seen.has(name)) {
297
+ seen.set(name, spec.summary);
298
+ }
299
+ }
300
+ return Array.from(seen, ([name, summary]) => ({ name, summary })).sort((a, b) => a.name.localeCompare(b.name));
301
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Agent-bridge daemon (SLICE 1).
3
+ *
4
+ * Standalone Node process. It does two things:
5
+ * 1. Serves a tiny localhost-only HTTP API (health + screenshot).
6
+ * 2. Hosts a WebSocket hub at `/agent` that the browser canvas connects OUT
7
+ * to at boot (a tab cannot listen for sockets, so it dials us).
8
+ *
9
+ * Each HTTP request is relayed to the connected canvas as a `BridgeCommand`
10
+ * over WS; we await the id-correlated `BridgeReply` and turn it into the HTTP
11
+ * response. localhost-only and opt-in by design.
12
+ */
13
+ export interface DaemonHandle {
14
+ port: number;
15
+ close: () => Promise<void>;
16
+ }
17
+ export declare function resolvePort(): number;
18
+ /**
19
+ * Start the daemon. Resolves once HTTP is listening. The returned handle can
20
+ * close both the HTTP and WS servers.
21
+ */
22
+ export declare function startDaemon(port?: number): Promise<DaemonHandle>;