@drawsy/companion 0.1.33 → 0.1.34

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.
@@ -8,7 +8,7 @@ import { LocalConversationStore } from "./local-conversation-store.js";
8
8
  import { normalizeFolder, pickFolder } from "./folder-picker.js";
9
9
  import { readLocalEngineStatus } from "./engine-status.js";
10
10
  import { createCanvasImageAsset, createCanvasImageAssetFromBytes, createCanvasImageFileFromBytes, inspectCanvasImage } from "./image-asset.js";
11
- import { CANVAS_REQUEST_TIMEOUT_MS, MAX_CANVAS_ASSET_BYTES, MAX_BODY_BYTES, isRecord, parseCanvasContextReference, parseCanvasContextRequest, parseCanvasConnectorRequest, parseCanvasDiagramCodeRequest, parseCanvasLabelRequest, parseCanvasImageRequest, parseLivePreviewRequest, parseCanvasOperations, parseAgentConnectorTurn, parseAgentResourceTurn, parseDrawsyTabId, parseDrawsyTabUrl, parseGenerationMode, isConnectorCapability, } from "./protocol.js";
11
+ import { CANVAS_REQUEST_TIMEOUT_MS, MAX_CANVAS_ASSET_BYTES, MAX_BODY_BYTES, isRecord, parseCanvasContextReference, parseCanvasContextRequest, parseCanvasConnectorRequest, parseCanvasDiagramCodeRequest, parseCanvasLabelRequest, parseCanvasLibraryRequest, parseCanvasImageRequest, parseLivePreviewRequest, parseCanvasOperations, parseAgentConnectorTurn, parseAgentResourceTurn, parseDrawsyTabId, parseDrawsyTabUrl, parseGenerationMode, isConnectorCapability, } from "./protocol.js";
12
12
  import { createConnectorBackendResolver } from "./connector-backend.js";
13
13
  const MAX_DRAW_DOCUMENT_BYTES = 512 * 1024;
14
14
  const readDrawDocument = async (folder) => {
@@ -70,7 +70,8 @@ const assertCanvasActionAllowed = (session, action) => {
70
70
  "context",
71
71
  "replace-image",
72
72
  "replaceImage",
73
- "preview"
73
+ "preview",
74
+ "library"
74
75
  ].includes(action)) {
75
76
  throw new BridgeRequestError(403, "fast_mode_action_blocked", "Fast Mode does not allow this canvas action.");
76
77
  }
@@ -892,7 +893,7 @@ export const createDrawsyBridge = (options = {}) => {
892
893
  response.end();
893
894
  return;
894
895
  }
895
- const internalCanvas = url.pathname.match(/^\/internal\/sessions\/([^/]+)\/canvas\/(read|capabilities|connector|label|diagram|apply|inspect|image|context|replace-image|preview)$/);
896
+ const internalCanvas = url.pathname.match(/^\/internal\/sessions\/([^/]+)\/canvas\/(read|capabilities|connector|label|diagram|apply|inspect|image|context|replace-image|preview|library)$/);
896
897
  if (request.method === "POST" && internalCanvas) {
897
898
  const session = internalSession(request, response, decodeURIComponent(internalCanvas[1]));
898
899
  if (!session)
@@ -916,30 +917,34 @@ export const createDrawsyBridge = (options = {}) => {
916
917
  ? await requestCanvas(session, "preview", {
917
918
  previewRequest: parsedPreview
918
919
  })
919
- : action === "diagram"
920
- ? await requestCanvas(session, "diagram", {
921
- diagramRequest: parsedDiagram
920
+ : action === "library"
921
+ ? await requestCanvas(session, "library", {
922
+ libraryRequest: parseCanvasLibraryRequest(body)
922
923
  })
923
- : action === "context"
924
- ? resolveContextCaptures(session, [
925
- parseCanvasContextReference(await requestCanvas(session, "capture", {
926
- contextRequest: parseCanvasContextRequest(body)
927
- }))
928
- ])[0]
929
- : await requestCanvas(session, action, action === "apply"
930
- ? {
931
- operations: {
932
- ...parseCanvasOperations(body),
933
- ...(session.generationMode === "fast"
934
- ? { existingElementsOnly: true }
935
- : {})
924
+ : action === "diagram"
925
+ ? await requestCanvas(session, "diagram", {
926
+ diagramRequest: parsedDiagram
927
+ })
928
+ : action === "context"
929
+ ? resolveContextCaptures(session, [
930
+ parseCanvasContextReference(await requestCanvas(session, "capture", {
931
+ contextRequest: parseCanvasContextRequest(body)
932
+ }))
933
+ ])[0]
934
+ : await requestCanvas(session, action, action === "apply"
935
+ ? {
936
+ operations: {
937
+ ...parseCanvasOperations(body),
938
+ ...(session.generationMode === "fast"
939
+ ? { existingElementsOnly: true }
940
+ : {})
941
+ }
936
942
  }
937
- }
938
- : action === "connector"
939
- ? { connectorRequest: parseCanvasConnectorRequest(body) }
940
- : action === "label"
941
- ? { labelRequest: parseCanvasLabelRequest(body) }
942
- : undefined);
943
+ : action === "connector"
944
+ ? { connectorRequest: parseCanvasConnectorRequest(body) }
945
+ : action === "label"
946
+ ? { labelRequest: parseCanvasLabelRequest(body) }
947
+ : undefined);
943
948
  json(response, 200, result);
944
949
  return;
945
950
  }
@@ -1021,7 +1026,8 @@ export const createDrawsyBridge = (options = {}) => {
1021
1026
  if (request.method === "GET" && url.pathname === "/v1/engines") {
1022
1027
  json(response, 200, {
1023
1028
  engines: readLocalEngineStatus(),
1024
- generationModes: ["draw", "fast"]
1029
+ generationModes: ["draw", "fast"],
1030
+ library: true
1025
1031
  });
1026
1032
  return;
1027
1033
  }
@@ -41,199 +41,205 @@ const describeToolItem = (item) => {
41
41
  const server = typeof item.server === "string" ? item.server : "MCP";
42
42
  const drawsyMessages = item.tool === "read_current_canvas"
43
43
  ? { started: "Reading current canvas", completed: "Canvas read" }
44
- : item.tool === "get_canvas_capabilities"
45
- ? { started: "Checking canvas tools", completed: "Canvas tools ready" }
46
- : item.tool === "create_or_update_connector"
47
- ? { started: "Connecting canvas objects", completed: "Objects connected" }
48
- : item.tool === "set_container_label"
49
- ? { started: "Setting container label", completed: "Label set" }
50
- : item.tool === "apply_canvas_changes"
51
- ? { started: "Updating canvas", completed: "Canvas updated" }
52
- : item.tool === "inspect_current_canvas_layout"
53
- ? { started: "Inspecting canvas layout", completed: "Layout checked" }
54
- : item.tool === "add_image_from_file"
55
- ? { started: "Adding image to canvas", completed: "Image added" }
56
- : item.tool === "capture_canvas_context"
57
- ? {
58
- started: "Capturing canvas context",
59
- completed: "Context captured",
60
- }
61
- : item.tool === "replace_canvas_image_from_file"
62
- ? {
63
- started: "Replacing canvas image",
64
- completed: "Image replaced",
65
- }
66
- : item.tool === "list_connected_sources"
67
- ? {
68
- started: "Checking connected sources",
69
- completed: "Sources ready",
70
- }
71
- : item.tool === "search_connected_source"
44
+ : item.tool === "search_library"
45
+ ? { started: "Searching library", completed: "Library searched" }
46
+ : item.tool === "read_library_item"
47
+ ? { started: "Reading library item", completed: "Library item read" }
48
+ : item.tool === "insert_library_item"
49
+ ? { started: "Inserting library item", completed: "Library item inserted" }
50
+ : item.tool === "get_canvas_capabilities"
51
+ ? { started: "Checking canvas tools", completed: "Canvas tools ready" }
52
+ : item.tool === "create_or_update_connector"
53
+ ? { started: "Connecting canvas objects", completed: "Objects connected" }
54
+ : item.tool === "set_container_label"
55
+ ? { started: "Setting container label", completed: "Label set" }
56
+ : item.tool === "apply_canvas_changes"
57
+ ? { started: "Updating canvas", completed: "Canvas updated" }
58
+ : item.tool === "inspect_current_canvas_layout"
59
+ ? { started: "Inspecting canvas layout", completed: "Layout checked" }
60
+ : item.tool === "add_image_from_file"
61
+ ? { started: "Adding image to canvas", completed: "Image added" }
62
+ : item.tool === "capture_canvas_context"
72
63
  ? {
73
- started: "Searching connected source",
74
- completed: "Source searched",
64
+ started: "Capturing canvas context",
65
+ completed: "Context captured",
75
66
  }
76
- : item.tool === "list_connected_meeting_tools"
67
+ : item.tool === "replace_canvas_image_from_file"
77
68
  ? {
78
- started: "Checking meeting tools",
79
- completed: "Meeting tools ready",
69
+ started: "Replacing canvas image",
70
+ completed: "Image replaced",
80
71
  }
81
- : item.tool === "call_connected_meeting_tool"
72
+ : item.tool === "list_connected_sources"
82
73
  ? {
83
- started: "Reading meeting source",
84
- completed: "Meeting source ready",
74
+ started: "Checking connected sources",
75
+ completed: "Sources ready",
85
76
  }
86
- : item.tool === "list_aws_regions"
77
+ : item.tool === "search_connected_source"
87
78
  ? {
88
- started: "Checking AWS regions",
89
- completed: "AWS regions ready",
79
+ started: "Searching connected source",
80
+ completed: "Source searched",
90
81
  }
91
- : item.tool === "search_aws_resources"
82
+ : item.tool === "list_connected_meeting_tools"
92
83
  ? {
93
- started: "Searching AWS infrastructure",
94
- completed: "AWS resources ready",
84
+ started: "Checking meeting tools",
85
+ completed: "Meeting tools ready",
95
86
  }
96
- : item.tool === "list_aws_cloudformation_stacks"
87
+ : item.tool === "call_connected_meeting_tool"
97
88
  ? {
98
- started: "Checking CloudFormation",
99
- completed: "CloudFormation stacks ready",
89
+ started: "Reading meeting source",
90
+ completed: "Meeting source ready",
100
91
  }
101
- : item.tool === "list_mail_messages"
102
- ? { started: "Checking mail", completed: "Mail ready" }
103
- : item.tool === "list_calendars"
92
+ : item.tool === "list_aws_regions"
93
+ ? {
94
+ started: "Checking AWS regions",
95
+ completed: "AWS regions ready",
96
+ }
97
+ : item.tool === "search_aws_resources"
104
98
  ? {
105
- started: "Checking calendars",
106
- completed: "Calendars ready",
99
+ started: "Searching AWS infrastructure",
100
+ completed: "AWS resources ready",
107
101
  }
108
- : item.tool === "list_calendar_events"
102
+ : item.tool === "list_aws_cloudformation_stacks"
109
103
  ? {
110
- started: "Checking calendar",
111
- completed: "Events ready",
104
+ started: "Checking CloudFormation",
105
+ completed: "CloudFormation stacks ready",
112
106
  }
113
- : item.tool === "list_drive_files"
114
- ? {
115
- started: "Checking Drive",
116
- completed: "Drive files ready",
117
- }
118
- : item.tool === "list_github_repositories"
107
+ : item.tool === "list_mail_messages"
108
+ ? { started: "Checking mail", completed: "Mail ready" }
109
+ : item.tool === "list_calendars"
119
110
  ? {
120
- started: "Checking repositories",
121
- completed: "Repositories ready",
111
+ started: "Checking calendars",
112
+ completed: "Calendars ready",
122
113
  }
123
- : item.tool === "list_github_repository_contents"
114
+ : item.tool === "list_calendar_events"
124
115
  ? {
125
- started: "Browsing repository",
126
- completed: "Repository contents ready",
116
+ started: "Checking calendar",
117
+ completed: "Events ready",
127
118
  }
128
- : item.tool === "list_github_issues"
119
+ : item.tool === "list_drive_files"
129
120
  ? {
130
- started: "Checking issues",
131
- completed: "Issues ready",
121
+ started: "Checking Drive",
122
+ completed: "Drive files ready",
132
123
  }
133
- : item.tool === "list_github_pull_requests"
124
+ : item.tool === "list_github_repositories"
134
125
  ? {
135
- started: "Checking pull requests",
136
- completed: "Pull requests ready",
126
+ started: "Checking repositories",
127
+ completed: "Repositories ready",
137
128
  }
138
- : item.tool === "list_notion_content"
129
+ : item.tool === "list_github_repository_contents"
139
130
  ? {
140
- started: "Checking Notion",
141
- completed: "Notion content ready",
131
+ started: "Browsing repository",
132
+ completed: "Repository contents ready",
142
133
  }
143
- : item.tool === "list_slack_channels"
134
+ : item.tool === "list_github_issues"
144
135
  ? {
145
- started: "Checking Slack channels",
146
- completed: "Channels ready",
136
+ started: "Checking issues",
137
+ completed: "Issues ready",
147
138
  }
148
- : item.tool === "list_slack_messages"
139
+ : item.tool === "list_github_pull_requests"
149
140
  ? {
150
- started: "Checking Slack",
151
- completed: "Slack messages ready",
141
+ started: "Checking pull requests",
142
+ completed: "Pull requests ready",
152
143
  }
153
- : item.tool === "read_connected_item"
144
+ : item.tool === "list_notion_content"
154
145
  ? {
155
- started: "Reading connected item",
156
- completed: "Source read",
146
+ started: "Checking Notion",
147
+ completed: "Notion content ready",
157
148
  }
158
- : item.tool === "list_kanban_boards"
149
+ : item.tool === "list_slack_channels"
159
150
  ? {
160
- started: "Checking Kanban boards",
161
- completed: "Boards ready",
151
+ started: "Checking Slack channels",
152
+ completed: "Channels ready",
162
153
  }
163
- : item.tool === "read_kanban_board"
154
+ : item.tool === "list_slack_messages"
164
155
  ? {
165
- started: "Reading Kanban board",
166
- completed: "Board ready",
156
+ started: "Checking Slack",
157
+ completed: "Slack messages ready",
167
158
  }
168
- : item.tool === "create_kanban_card"
159
+ : item.tool === "read_connected_item"
169
160
  ? {
170
- started: "Creating Kanban card",
171
- completed: "Card created",
161
+ started: "Reading connected item",
162
+ completed: "Source read",
172
163
  }
173
- : item.tool === "update_kanban_card"
164
+ : item.tool === "list_kanban_boards"
174
165
  ? {
175
- started: "Updating Kanban card",
176
- completed: "Card updated",
166
+ started: "Checking Kanban boards",
167
+ completed: "Boards ready",
177
168
  }
178
- : item.tool === "move_kanban_card"
169
+ : item.tool === "read_kanban_board"
179
170
  ? {
180
- started: "Moving Kanban card",
181
- completed: "Card moved",
171
+ started: "Reading Kanban board",
172
+ completed: "Board ready",
182
173
  }
183
- : item.tool === "create_kanban_checklist_item"
174
+ : item.tool === "create_kanban_card"
184
175
  ? {
185
- started: "Adding checklist item",
186
- completed: "Checklist updated",
176
+ started: "Creating Kanban card",
177
+ completed: "Card created",
187
178
  }
188
- : item.tool === "update_kanban_checklist_item"
179
+ : item.tool === "update_kanban_card"
189
180
  ? {
190
- started: "Updating checklist",
191
- completed: "Checklist updated",
181
+ started: "Updating Kanban card",
182
+ completed: "Card updated",
192
183
  }
193
- : item.tool === "link_current_canvas_to_kanban_card"
184
+ : item.tool === "move_kanban_card"
194
185
  ? {
195
- started: "Linking current canvas",
196
- completed: "Canvas linked",
186
+ started: "Moving Kanban card",
187
+ completed: "Card moved",
197
188
  }
198
- : item.tool === "list_jira_connections"
189
+ : item.tool === "create_kanban_checklist_item"
199
190
  ? {
200
- started: "Checking Jira connections",
201
- completed: "Jira ready",
191
+ started: "Adding checklist item",
192
+ completed: "Checklist updated",
202
193
  }
203
- : item.tool === "list_jira_projects"
194
+ : item.tool === "update_kanban_checklist_item"
204
195
  ? {
205
- started: "Checking Jira projects",
206
- completed: "Projects ready",
196
+ started: "Updating checklist",
197
+ completed: "Checklist updated",
207
198
  }
208
- : item.tool === "search_jira_issues"
199
+ : item.tool === "link_current_canvas_to_kanban_card"
209
200
  ? {
210
- started: "Searching Jira issues",
211
- completed: "Issues ready",
201
+ started: "Linking current canvas",
202
+ completed: "Canvas linked",
212
203
  }
213
- : item.tool === "read_jira_issue"
204
+ : item.tool === "list_jira_connections"
214
205
  ? {
215
- started: "Reading Jira issue",
216
- completed: "Issue ready",
206
+ started: "Checking Jira connections",
207
+ completed: "Jira ready",
217
208
  }
218
- : item.tool === "list_jira_boards"
209
+ : item.tool === "list_jira_projects"
219
210
  ? {
220
- started: "Checking Jira boards",
221
- completed: "Boards ready",
211
+ started: "Checking Jira projects",
212
+ completed: "Projects ready",
222
213
  }
223
- : item.tool === "list_jira_sprints"
214
+ : item.tool === "search_jira_issues"
224
215
  ? {
225
- started: "Checking Jira sprints",
226
- completed: "Sprints ready",
216
+ started: "Searching Jira issues",
217
+ completed: "Issues ready",
227
218
  }
228
- : item.tool === "list_jira_backlog"
219
+ : item.tool === "read_jira_issue"
229
220
  ? {
230
- started: "Checking Jira backlog",
231
- completed: "Backlog ready",
221
+ started: "Reading Jira issue",
222
+ completed: "Issue ready",
232
223
  }
233
- : {
234
- started: "Working on the canvas",
235
- completed: "Canvas tool finished",
236
- };
224
+ : item.tool === "list_jira_boards"
225
+ ? {
226
+ started: "Checking Jira boards",
227
+ completed: "Boards ready",
228
+ }
229
+ : item.tool === "list_jira_sprints"
230
+ ? {
231
+ started: "Checking Jira sprints",
232
+ completed: "Sprints ready",
233
+ }
234
+ : item.tool === "list_jira_backlog"
235
+ ? {
236
+ started: "Checking Jira backlog",
237
+ completed: "Backlog ready",
238
+ }
239
+ : {
240
+ started: "Working on the canvas",
241
+ completed: "Canvas tool finished",
242
+ };
237
243
  return {
238
244
  tool: server === "drawsy" ? item.tool : `${server}/${item.tool}`,
239
245
  startedMessage: server === "drawsy" ? drawsyMessages.started : `Using ${item.tool}`,
@@ -1145,6 +1151,9 @@ export class CodexAppServer {
1145
1151
  default_tools_approval_mode: "auto",
1146
1152
  tools: {
1147
1153
  apply_canvas_changes: { approval_mode: "approve" },
1154
+ search_library: { approval_mode: "approve" },
1155
+ read_library_item: { approval_mode: "approve" },
1156
+ insert_library_item: { approval_mode: "approve" },
1148
1157
  render_diagram_code: { approval_mode: "approve" },
1149
1158
  create_or_update_connector: { approval_mode: "approve" },
1150
1159
  set_container_label: { approval_mode: "approve" },
@@ -29934,6 +29934,65 @@ var StdioServerTransport = class {
29934
29934
  var MAX_BODY_BYTES = 12 * 1024 * 1024;
29935
29935
  var MAX_CANVAS_ASSET_BYTES = 8 * 1024 * 1024;
29936
29936
  var MAX_DIAGRAM_SOURCE_BYTES = 256 * 1024;
29937
+ var parseCanvasLibraryRequest = (value) => {
29938
+ if (!isRecord(value)) throw new Error("Library request must be an object.");
29939
+ const boundedString = (key, max, required2 = false) => {
29940
+ const entry = value[key];
29941
+ if (entry === void 0 && !required2) return void 0;
29942
+ if (typeof entry !== "string" || !entry.trim() || entry.length > max)
29943
+ throw new Error(`Library ${key} is invalid.`);
29944
+ return entry.trim();
29945
+ };
29946
+ const pagination = () => {
29947
+ const cursor = boundedString("cursor", 4096);
29948
+ const limit = value.limit;
29949
+ if (limit !== void 0 && (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1 || limit > 50))
29950
+ throw new Error("Library limit must be an integer from 1 to 50.");
29951
+ return {
29952
+ ...cursor ? { cursor } : {},
29953
+ ...limit !== void 0 ? { limit } : {}
29954
+ };
29955
+ };
29956
+ const source = value.source;
29957
+ if (source !== void 0 && source !== "public")
29958
+ throw new Error("Only the public library catalog is available.");
29959
+ if (value.action === "search") {
29960
+ const query = boundedString("query", 256);
29961
+ return {
29962
+ action: "search",
29963
+ ...query ? { query } : {},
29964
+ source: "public",
29965
+ ...pagination()
29966
+ };
29967
+ }
29968
+ if (value.action === "read" || value.action === "insert") {
29969
+ const packId = boundedString("packId", 256, true);
29970
+ const itemId = boundedString("itemId", 256, value.action === "insert");
29971
+ if (value.action === "read") {
29972
+ return {
29973
+ action: "read",
29974
+ source: "public",
29975
+ packId,
29976
+ ...itemId ? { itemId } : {},
29977
+ ...pagination()
29978
+ };
29979
+ }
29980
+ const { x, y } = value;
29981
+ if (typeof x !== "number" || !Number.isFinite(x) || Math.abs(x) > 1e6 || typeof y !== "number" || !Number.isFinite(y) || Math.abs(y) > 1e6)
29982
+ throw new Error("Library insertion coordinates are invalid.");
29983
+ const validOperationId = boundedString("operationId", 128, true);
29984
+ return {
29985
+ action: "insert",
29986
+ source: "public",
29987
+ packId,
29988
+ itemId,
29989
+ x,
29990
+ y,
29991
+ operationId: validOperationId
29992
+ };
29993
+ }
29994
+ throw new Error("Library action is invalid.");
29995
+ };
29937
29996
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
29938
29997
  var renderedElementType = (element) => {
29939
29998
  const type = typeof element.type === "string" ? element.type : "unknown";
@@ -29950,6 +30009,9 @@ var renderedElementType = (element) => {
29950
30009
  return rounded ? "rounded diamond" : "diamond";
29951
30010
  }
29952
30011
  if (type === "rectangle") {
30012
+ if (typeof element.shapeKind === "string" && element.shapeKind.trim()) {
30013
+ return element.shapeKind;
30014
+ }
29953
30015
  return rounded ? "rounded rectangle" : "rectangle";
29954
30016
  }
29955
30017
  return type;
@@ -30582,6 +30644,73 @@ if (surfaceKind === "canvas" || surfaceKind === "presentation") {
30582
30644
  }
30583
30645
  );
30584
30646
  }
30647
+ if (surfaceKind === "canvas" || surfaceKind === "presentation") {
30648
+ const libraryCall = async (input) => {
30649
+ try {
30650
+ const parsed = parseCanvasLibraryRequest(input);
30651
+ const result = JSON.parse(await callBridge("library", parsed));
30652
+ return {
30653
+ content: [{ type: "text", text: JSON.stringify(result) }]
30654
+ };
30655
+ } catch (error48) {
30656
+ return {
30657
+ isError: true,
30658
+ content: [
30659
+ {
30660
+ type: "text",
30661
+ text: error48 instanceof Error ? error48.message : "Library request failed."
30662
+ }
30663
+ ]
30664
+ };
30665
+ }
30666
+ };
30667
+ const optionalPage = {
30668
+ cursor: external_exports3.string().trim().min(1).max(4096).optional(),
30669
+ limit: external_exports3.number().int().min(1).max(50).optional()
30670
+ };
30671
+ server.registerTool(
30672
+ "search_library",
30673
+ {
30674
+ description: "Search the public catalog for packs and items.",
30675
+ inputSchema: external_exports3.object({
30676
+ query: external_exports3.string().trim().min(1).max(256).optional(),
30677
+ ...optionalPage
30678
+ }).strict(),
30679
+ annotations: { readOnlyHint: true, destructiveHint: false }
30680
+ },
30681
+ (input) => libraryCall({ ...input, action: "search" })
30682
+ );
30683
+ server.registerTool(
30684
+ "read_library_item",
30685
+ {
30686
+ description: "Read public library items or list a pack\u2019s items.",
30687
+ inputSchema: external_exports3.object({
30688
+ packId: external_exports3.string().trim().min(1).max(256),
30689
+ itemId: external_exports3.string().trim().min(1).max(256).optional(),
30690
+ ...optionalPage
30691
+ }).strict(),
30692
+ annotations: { readOnlyHint: true, destructiveHint: false }
30693
+ },
30694
+ (input) => libraryCall({ ...input, action: "read" })
30695
+ );
30696
+ server.registerTool(
30697
+ "insert_library_item",
30698
+ {
30699
+ description: "Insert a library item at canvas coordinates x and y, measured from its top-left corner. Returns element IDs usable with the canvas connector tool.",
30700
+ inputSchema: external_exports3.object({
30701
+ packId: external_exports3.string().trim().min(1).max(256),
30702
+ itemId: external_exports3.string().trim().min(1).max(256),
30703
+ x: external_exports3.number().finite().min(-1e6).max(1e6),
30704
+ y: external_exports3.number().finite().min(-1e6).max(1e6),
30705
+ operationId: external_exports3.string().trim().min(1).max(128).describe(
30706
+ "Insertion identifier; reuse for retries of the same insertion."
30707
+ )
30708
+ }).strict(),
30709
+ annotations: { readOnlyHint: false, destructiveHint: false }
30710
+ },
30711
+ (input) => libraryCall({ ...input, action: "insert" })
30712
+ );
30713
+ }
30585
30714
  server.registerTool(
30586
30715
  "list_connected_sources",
30587
30716
  {
@@ -10,6 +10,76 @@ export const parseGenerationMode = (value) => {
10
10
  throw new Error("generationMode must be 'fast' or 'draw'.");
11
11
  };
12
12
  export const surfaceSupportsLivePreview = (surfaceKind) => surfaceKind === "canvas" || surfaceKind === "presentation";
13
+ export const parseCanvasLibraryRequest = (value) => {
14
+ if (!isRecord(value))
15
+ throw new Error("Library request must be an object.");
16
+ const boundedString = (key, max, required = false) => {
17
+ const entry = value[key];
18
+ if (entry === undefined && !required)
19
+ return undefined;
20
+ if (typeof entry !== "string" || !entry.trim() || entry.length > max)
21
+ throw new Error(`Library ${key} is invalid.`);
22
+ return entry.trim();
23
+ };
24
+ const pagination = () => {
25
+ const cursor = boundedString("cursor", 4_096);
26
+ const limit = value.limit;
27
+ if (limit !== undefined &&
28
+ (typeof limit !== "number" ||
29
+ !Number.isInteger(limit) ||
30
+ limit < 1 ||
31
+ limit > 50))
32
+ throw new Error("Library limit must be an integer from 1 to 50.");
33
+ return {
34
+ ...(cursor ? { cursor } : {}),
35
+ ...(limit !== undefined ? { limit } : {})
36
+ };
37
+ };
38
+ const source = value.source;
39
+ if (source !== undefined && source !== "public")
40
+ throw new Error("Only the public library catalog is available.");
41
+ if (value.action === "search") {
42
+ const query = boundedString("query", 256);
43
+ return {
44
+ action: "search",
45
+ ...(query ? { query } : {}),
46
+ source: "public",
47
+ ...pagination()
48
+ };
49
+ }
50
+ if (value.action === "read" || value.action === "insert") {
51
+ const packId = boundedString("packId", 256, true);
52
+ const itemId = boundedString("itemId", 256, value.action === "insert");
53
+ if (value.action === "read") {
54
+ return {
55
+ action: "read",
56
+ source: "public",
57
+ packId,
58
+ ...(itemId ? { itemId } : {}),
59
+ ...pagination()
60
+ };
61
+ }
62
+ const { x, y } = value;
63
+ if (typeof x !== "number" ||
64
+ !Number.isFinite(x) ||
65
+ Math.abs(x) > 1_000_000 ||
66
+ typeof y !== "number" ||
67
+ !Number.isFinite(y) ||
68
+ Math.abs(y) > 1_000_000)
69
+ throw new Error("Library insertion coordinates are invalid.");
70
+ const validOperationId = boundedString("operationId", 128, true);
71
+ return {
72
+ action: "insert",
73
+ source: "public",
74
+ packId,
75
+ itemId: itemId,
76
+ x,
77
+ y,
78
+ operationId: validOperationId
79
+ };
80
+ }
81
+ throw new Error("Library action is invalid.");
82
+ };
13
83
  export const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
14
84
  export const parseDrawsyTabId = (value) => {
15
85
  if (value === undefined || value === null) {
@@ -61,6 +131,9 @@ const renderedElementType = (element) => {
61
131
  return rounded ? "rounded diamond" : "diamond";
62
132
  }
63
133
  if (type === "rectangle") {
134
+ if (typeof element.shapeKind === "string" && element.shapeKind.trim()) {
135
+ return element.shapeKind;
136
+ }
64
137
  return rounded ? "rounded rectangle" : "rectangle";
65
138
  }
66
139
  return type;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawsy/companion",
3
- "version": "0.1.33",
3
+ "version": "0.1.34",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Drawsy's local Companion server and background command-line controls",