@exayard/cli 0.2.0 → 0.5.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/LICENSE +21 -0
- package/README.md +60 -8
- package/dist/bin.js +3824 -221
- package/dist/index.d.ts +20 -5
- package/dist/index.js +3659 -211
- package/package.json +10 -7
package/dist/bin.js
CHANGED
|
@@ -26,12 +26,2505 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
mod
|
|
27
27
|
));
|
|
28
28
|
|
|
29
|
+
// ../sdk/dist/_generated/operations.js
|
|
30
|
+
var require_operations = __commonJS({
|
|
31
|
+
"../sdk/dist/_generated/operations.js"(exports2) {
|
|
32
|
+
"use strict";
|
|
33
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
34
|
+
exports2.OPERATION_METHODS = exports2.OPERATIONS = void 0;
|
|
35
|
+
exports2.OPERATIONS = {
|
|
36
|
+
add_symbol_line_item: {
|
|
37
|
+
method: "POST",
|
|
38
|
+
path: "/symbols/{id}/line-items",
|
|
39
|
+
tag: "Takeoff",
|
|
40
|
+
summary: "Add a line item to a symbol",
|
|
41
|
+
body: true,
|
|
42
|
+
response: "json",
|
|
43
|
+
params: [
|
|
44
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex takeoff symbol ID" },
|
|
45
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Convex project ID" },
|
|
46
|
+
{ name: "lineItem", in: "body", required: true, kind: "object" }
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
archive_conversation: {
|
|
50
|
+
method: "DELETE",
|
|
51
|
+
path: "/conversations/{id}",
|
|
52
|
+
tag: "Conversations",
|
|
53
|
+
summary: "Archive a conversation",
|
|
54
|
+
body: false,
|
|
55
|
+
response: "empty",
|
|
56
|
+
params: [
|
|
57
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex conversation ID" },
|
|
58
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
archive_labor_crew: {
|
|
62
|
+
method: "DELETE",
|
|
63
|
+
path: "/labor/crews/{id}",
|
|
64
|
+
tag: "Labor",
|
|
65
|
+
summary: "Archive a labor crew",
|
|
66
|
+
body: false,
|
|
67
|
+
response: "empty",
|
|
68
|
+
params: [
|
|
69
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
70
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
archive_labor_equipment: {
|
|
74
|
+
method: "DELETE",
|
|
75
|
+
path: "/labor/equipment/{id}",
|
|
76
|
+
tag: "Labor",
|
|
77
|
+
summary: "Archive a labor equipment entry",
|
|
78
|
+
body: false,
|
|
79
|
+
response: "empty",
|
|
80
|
+
params: [
|
|
81
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
82
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
archive_labor_rate: {
|
|
86
|
+
method: "DELETE",
|
|
87
|
+
path: "/labor/rates/{id}",
|
|
88
|
+
tag: "Labor",
|
|
89
|
+
summary: "Archive a labor rate",
|
|
90
|
+
body: false,
|
|
91
|
+
response: "empty",
|
|
92
|
+
params: [
|
|
93
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
94
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
archive_list: {
|
|
98
|
+
method: "DELETE",
|
|
99
|
+
path: "/lists/{id}",
|
|
100
|
+
tag: "Lists",
|
|
101
|
+
summary: "Archive (soft-delete) a list",
|
|
102
|
+
body: false,
|
|
103
|
+
response: "empty",
|
|
104
|
+
params: [
|
|
105
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex product list ID" },
|
|
106
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
archive_page: {
|
|
110
|
+
method: "DELETE",
|
|
111
|
+
path: "/pages/{id}",
|
|
112
|
+
tag: "Pages",
|
|
113
|
+
summary: "Archive (soft-delete) a page",
|
|
114
|
+
body: false,
|
|
115
|
+
response: "empty",
|
|
116
|
+
params: [
|
|
117
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex page ID" }
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
archive_product: {
|
|
121
|
+
method: "DELETE",
|
|
122
|
+
path: "/products/{id}",
|
|
123
|
+
tag: "Products",
|
|
124
|
+
summary: "Archive (soft-delete) a product",
|
|
125
|
+
body: false,
|
|
126
|
+
response: "empty",
|
|
127
|
+
params: [
|
|
128
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex product ID" },
|
|
129
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
archive_project: {
|
|
133
|
+
method: "DELETE",
|
|
134
|
+
path: "/projects/{id}",
|
|
135
|
+
tag: "Projects",
|
|
136
|
+
summary: "Archive a project",
|
|
137
|
+
body: false,
|
|
138
|
+
response: "empty",
|
|
139
|
+
params: [
|
|
140
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
141
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
archive_vendor: {
|
|
145
|
+
method: "DELETE",
|
|
146
|
+
path: "/vendors/{id}",
|
|
147
|
+
tag: "Vendors",
|
|
148
|
+
summary: "Archive (soft-delete) a vendor",
|
|
149
|
+
body: false,
|
|
150
|
+
response: "empty",
|
|
151
|
+
params: [
|
|
152
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex vendor ID" },
|
|
153
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
attach_file_to_project: {
|
|
157
|
+
method: "POST",
|
|
158
|
+
path: "/projects/{id}/files/{fileId}/attach",
|
|
159
|
+
tag: "Files",
|
|
160
|
+
summary: "Attach an existing file to a project",
|
|
161
|
+
body: true,
|
|
162
|
+
response: "empty",
|
|
163
|
+
params: [
|
|
164
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
165
|
+
{ name: "fileId", in: "path", required: true, kind: "string", description: "Convex file ID" },
|
|
166
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
batch_create_products: {
|
|
170
|
+
method: "POST",
|
|
171
|
+
path: "/products/batch",
|
|
172
|
+
tag: "Products",
|
|
173
|
+
summary: "Batch-create products",
|
|
174
|
+
body: true,
|
|
175
|
+
response: "json",
|
|
176
|
+
params: [
|
|
177
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
178
|
+
{ name: "listId", in: "body", required: true, kind: "string", description: "Convex product list ID" },
|
|
179
|
+
{ name: "products", in: "body", required: true, kind: "array" }
|
|
180
|
+
]
|
|
181
|
+
},
|
|
182
|
+
cancel_assessment: {
|
|
183
|
+
method: "POST",
|
|
184
|
+
path: "/assessments/{id}/cancel",
|
|
185
|
+
tag: "Takeoff",
|
|
186
|
+
summary: "Cancel a running assessment",
|
|
187
|
+
body: true,
|
|
188
|
+
response: "empty",
|
|
189
|
+
params: [
|
|
190
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex takeoff assessment ID" },
|
|
191
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
192
|
+
]
|
|
193
|
+
},
|
|
194
|
+
confirm_file_upload: {
|
|
195
|
+
method: "POST",
|
|
196
|
+
path: "/files/{id}/confirm",
|
|
197
|
+
tag: "Files",
|
|
198
|
+
summary: "Confirm a file upload after the client PUT succeeds",
|
|
199
|
+
confirm: "This operation uses AI usage.",
|
|
200
|
+
body: true,
|
|
201
|
+
response: "json",
|
|
202
|
+
params: [
|
|
203
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex file ID" },
|
|
204
|
+
{ name: "r2Key", in: "body", required: true, kind: "string" }
|
|
205
|
+
]
|
|
206
|
+
},
|
|
207
|
+
create_agent_session: {
|
|
208
|
+
method: "POST",
|
|
209
|
+
path: "/ai-sessions",
|
|
210
|
+
tag: "Account",
|
|
211
|
+
summary: "Start a computer-use agent session on a project page",
|
|
212
|
+
confirm: "This operation uses AI usage.",
|
|
213
|
+
body: true,
|
|
214
|
+
response: "json",
|
|
215
|
+
params: [
|
|
216
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Convex project ID" },
|
|
217
|
+
{ name: "pageId", in: "body", required: true, kind: "string", description: "Convex page ID the agent will operate on." },
|
|
218
|
+
{ name: "vendor", in: "body", required: true, kind: "string", enum: ["claude", "openai", "gemini"] }
|
|
219
|
+
]
|
|
220
|
+
},
|
|
221
|
+
create_assessment: {
|
|
222
|
+
method: "POST",
|
|
223
|
+
path: "/projects/{id}/assessments",
|
|
224
|
+
tag: "Takeoff",
|
|
225
|
+
summary: "Start a new AI assessment",
|
|
226
|
+
confirm: "This operation uses AI usage.",
|
|
227
|
+
body: true,
|
|
228
|
+
response: "json",
|
|
229
|
+
params: [
|
|
230
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
231
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
232
|
+
{ name: "conversationId", in: "body", required: false, kind: "string" },
|
|
233
|
+
{ name: "fileIds", in: "body", required: false, kind: "array" },
|
|
234
|
+
{ name: "pageIds", in: "body", required: false, kind: "array" },
|
|
235
|
+
{ name: "imageUrls", in: "body", required: false, kind: "array" },
|
|
236
|
+
{ name: "types", in: "body", required: false, kind: "array" }
|
|
237
|
+
]
|
|
238
|
+
},
|
|
239
|
+
create_bid: {
|
|
240
|
+
method: "POST",
|
|
241
|
+
path: "/projects/{id}/bids",
|
|
242
|
+
tag: "Estimates",
|
|
243
|
+
summary: "Generate a project-scoped bid document (blocking)",
|
|
244
|
+
confirm: "This operation uses AI usage.",
|
|
245
|
+
body: true,
|
|
246
|
+
response: "json",
|
|
247
|
+
params: [
|
|
248
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
249
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
250
|
+
{ name: "notes", in: "body", required: false, kind: "string", description: "Additional context appended to the prompt" },
|
|
251
|
+
{ name: "measurementContext", in: "body", required: false, kind: "string", description: "Pre-serialized takeoff measurements + line items (markdown). If omitted, the endpoint auto-loads the project takeoff from Convex and serializes it server-side." },
|
|
252
|
+
{ name: "ignoreMissingVariables", in: "body", required: false, kind: "boolean", description: "When auto-loading takeoff data, skip line items with missing formula variables instead of failing with 422. Ignored if measurementContext is provided." },
|
|
253
|
+
{ name: "share", in: "body", required: false, kind: "boolean", description: "Publish the saved bid on a public link that anyone holding the URL can read. Defaults to false \u2014 the document stays private to your organization and documentUrl points at the signed-in path. Set true only when the customer asked for a shareable link; you can also share or unshare later via PATCH /v1/documents/{id}/share." }
|
|
254
|
+
]
|
|
255
|
+
},
|
|
256
|
+
create_conversation: {
|
|
257
|
+
method: "POST",
|
|
258
|
+
path: "/conversations",
|
|
259
|
+
tag: "Conversations",
|
|
260
|
+
summary: "Create a conversation",
|
|
261
|
+
body: true,
|
|
262
|
+
response: "json",
|
|
263
|
+
params: [
|
|
264
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
265
|
+
{ name: "projectId", in: "body", required: false, kind: "string", description: "Convex project ID" },
|
|
266
|
+
{ name: "title", in: "body", required: false, kind: "string" },
|
|
267
|
+
{ name: "type", in: "body", required: false, kind: "string", enum: ["ai_chat", "project_activity"] },
|
|
268
|
+
{ name: "model", in: "body", required: false, kind: "string" },
|
|
269
|
+
{ name: "visibility", in: "body", required: false, kind: "string", enum: ["private", "shared"] }
|
|
270
|
+
]
|
|
271
|
+
},
|
|
272
|
+
create_custom_property: {
|
|
273
|
+
method: "POST",
|
|
274
|
+
path: "/custom-properties",
|
|
275
|
+
tag: "Custom properties",
|
|
276
|
+
summary: "Create a custom property",
|
|
277
|
+
body: true,
|
|
278
|
+
response: "json",
|
|
279
|
+
params: [
|
|
280
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
281
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
282
|
+
{ name: "type", in: "body", required: true, kind: "string", enum: ["text", "textarea", "number", "date", "checkbox", "multiCheckbox", "select", "url", "email", "phone", "currency", "percentage"] },
|
|
283
|
+
{ name: "recordTypes", in: "body", required: true, kind: "array" },
|
|
284
|
+
{ name: "options", in: "body", required: false, kind: "array" },
|
|
285
|
+
{ name: "presetId", in: "body", required: false, kind: "string" }
|
|
286
|
+
]
|
|
287
|
+
},
|
|
288
|
+
create_estimate: {
|
|
289
|
+
method: "POST",
|
|
290
|
+
path: "/projects/{id}/estimates",
|
|
291
|
+
tag: "Estimates",
|
|
292
|
+
summary: "Generate an estimate and persist it as a document (blocking)",
|
|
293
|
+
confirm: "This operation uses AI usage.",
|
|
294
|
+
body: true,
|
|
295
|
+
response: "json",
|
|
296
|
+
params: [
|
|
297
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
298
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
299
|
+
{ name: "query", in: "body", required: true, kind: "string", description: "Natural-language prompt describing the scope of the estimate" },
|
|
300
|
+
{ name: "notes", in: "body", required: false, kind: "string", description: "Additional notes appended to the prompt" },
|
|
301
|
+
{ name: "measurementContext", in: "body", required: false, kind: "string", description: "Pre-serialized takeoff data to ground the estimator (see GET /v1/projects/{id}/takeoff-summary)" },
|
|
302
|
+
{ name: "share", in: "body", required: false, kind: "boolean", description: "Publish the saved estimate on a public link that anyone holding the URL can read. Defaults to false \u2014 the document stays private to your organization and documentUrl points at the signed-in path. Set true only when the customer asked for a shareable link; you can also share or unshare later via PATCH /v1/documents/{id}/share." }
|
|
303
|
+
]
|
|
304
|
+
},
|
|
305
|
+
create_folder: {
|
|
306
|
+
method: "POST",
|
|
307
|
+
path: "/folders",
|
|
308
|
+
tag: "Folders",
|
|
309
|
+
summary: "Create a folder",
|
|
310
|
+
body: true,
|
|
311
|
+
response: "json",
|
|
312
|
+
params: [
|
|
313
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Convex project ID" },
|
|
314
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
315
|
+
{ name: "parentId", in: "body", required: true, kind: "string", description: "Convex folder ID" }
|
|
316
|
+
]
|
|
317
|
+
},
|
|
318
|
+
create_group: {
|
|
319
|
+
method: "POST",
|
|
320
|
+
path: "/projects/{id}/groups",
|
|
321
|
+
tag: "Takeoff",
|
|
322
|
+
summary: "Create a takeoff group",
|
|
323
|
+
body: true,
|
|
324
|
+
response: "json",
|
|
325
|
+
params: [
|
|
326
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
327
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
328
|
+
{ name: "parentId", in: "body", required: false, kind: "string", description: "Convex takeoff group ID" }
|
|
329
|
+
]
|
|
330
|
+
},
|
|
331
|
+
create_labor_crew: {
|
|
332
|
+
method: "POST",
|
|
333
|
+
path: "/labor/crews",
|
|
334
|
+
tag: "Labor",
|
|
335
|
+
summary: "Create a labor crew",
|
|
336
|
+
body: true,
|
|
337
|
+
response: "json",
|
|
338
|
+
params: [
|
|
339
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
340
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
341
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
342
|
+
{ name: "members", in: "body", required: true, kind: "array" },
|
|
343
|
+
{ name: "equipmentIds", in: "body", required: false, kind: "array" },
|
|
344
|
+
{ name: "dailyCapacity", in: "body", required: false, kind: "object" }
|
|
345
|
+
]
|
|
346
|
+
},
|
|
347
|
+
create_labor_equipment: {
|
|
348
|
+
method: "POST",
|
|
349
|
+
path: "/labor/equipment",
|
|
350
|
+
tag: "Labor",
|
|
351
|
+
summary: "Create a labor equipment entry",
|
|
352
|
+
body: true,
|
|
353
|
+
response: "json",
|
|
354
|
+
params: [
|
|
355
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
356
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
357
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
358
|
+
{ name: "category", in: "body", required: true, kind: "string" },
|
|
359
|
+
{ name: "dailyRate", in: "body", required: true, kind: "number" },
|
|
360
|
+
{ name: "hourlyRate", in: "body", required: false, kind: "number" },
|
|
361
|
+
{ name: "currency", in: "body", required: false, kind: "string", enum: ["USD", "CAD", "MXN", "BRL", "ARS", "CLP", "COP", "PEN", "UYU", "PYG", "BOB", "VES", "GYD", "SRD", "GTQ", "HNL", "NIO", "CRC", "PAB", "BZD", "JMD", "HTG", "DOP", "CUP", "BSD", "BBD", "TTD", "XCD", "AWG", "ANG", "KYD", "BMD", "FKP", "EUR", "GBP", "CHF", "NOK", "SEK", "DKK", "PLN", "CZK", "HUF", "RON", "BGN", "HRK", "RSD", "UAH", "RUB", "TRY", "ISK", "BAM", "MKD", "ALL", "MDL", "BYN", "GEL", "AMD", "AZN", "GIP", "ILS", "AED", "SAR", "QAR", "KWD", "BHD", "OMR", "JOD", "LBP", "EGP", "IQD", "IRR", "SYP", "YER", "ZAR", "NAD", "LSL", "SZL", "BWP", "NGN", "KES", "GHS", "MAD", "TND", "DZD", "LYD", "ETB", "ERN", "DJF", "SOS", "SDG", "SSP", "UGX", "TZS", "RWF", "BIF", "MWK", "ZMW", "ZWG", "MZN", "AOA", "CDF", "MGA", "MUR", "SCR", "KMF", "CVE", "GMD", "GNF", "LRD", "SLE", "MRU", "STN", "XOF", "XAF", "SHP", "JPY", "CNY", "HKD", "MOP", "TWD", "KRW", "KPW", "MNT", "SGD", "INR", "BTN", "PKR", "BDT", "LKR", "NPR", "MVR", "AFN", "IDR", "MYR", "PHP", "THB", "VND", "MMK", "KHR", "LAK", "BND", "KZT", "UZS", "KGS", "TJS", "TMT", "AUD", "NZD", "FJD", "PGK", "SBD", "VUV", "WST", "TOP", "XPF"], description: "ISO 4217 currency code" },
|
|
362
|
+
{ name: "isOwned", in: "body", required: false, kind: "boolean" }
|
|
363
|
+
]
|
|
364
|
+
},
|
|
365
|
+
create_labor_rate: {
|
|
366
|
+
method: "POST",
|
|
367
|
+
path: "/labor/rates",
|
|
368
|
+
tag: "Labor",
|
|
369
|
+
summary: "Create a labor rate",
|
|
370
|
+
body: true,
|
|
371
|
+
response: "json",
|
|
372
|
+
params: [
|
|
373
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
374
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
375
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
376
|
+
{ name: "hourlyRate", in: "body", required: true, kind: "number" },
|
|
377
|
+
{ name: "burdenRate", in: "body", required: false, kind: "number" },
|
|
378
|
+
{ name: "currency", in: "body", required: false, kind: "string", enum: ["USD", "CAD", "MXN", "BRL", "ARS", "CLP", "COP", "PEN", "UYU", "PYG", "BOB", "VES", "GYD", "SRD", "GTQ", "HNL", "NIO", "CRC", "PAB", "BZD", "JMD", "HTG", "DOP", "CUP", "BSD", "BBD", "TTD", "XCD", "AWG", "ANG", "KYD", "BMD", "FKP", "EUR", "GBP", "CHF", "NOK", "SEK", "DKK", "PLN", "CZK", "HUF", "RON", "BGN", "HRK", "RSD", "UAH", "RUB", "TRY", "ISK", "BAM", "MKD", "ALL", "MDL", "BYN", "GEL", "AMD", "AZN", "GIP", "ILS", "AED", "SAR", "QAR", "KWD", "BHD", "OMR", "JOD", "LBP", "EGP", "IQD", "IRR", "SYP", "YER", "ZAR", "NAD", "LSL", "SZL", "BWP", "NGN", "KES", "GHS", "MAD", "TND", "DZD", "LYD", "ETB", "ERN", "DJF", "SOS", "SDG", "SSP", "UGX", "TZS", "RWF", "BIF", "MWK", "ZMW", "ZWG", "MZN", "AOA", "CDF", "MGA", "MUR", "SCR", "KMF", "CVE", "GMD", "GNF", "LRD", "SLE", "MRU", "STN", "XOF", "XAF", "SHP", "JPY", "CNY", "HKD", "MOP", "TWD", "KRW", "KPW", "MNT", "SGD", "INR", "BTN", "PKR", "BDT", "LKR", "NPR", "MVR", "AFN", "IDR", "MYR", "PHP", "THB", "VND", "MMK", "KHR", "LAK", "BND", "KZT", "UZS", "KGS", "TJS", "TMT", "AUD", "NZD", "FJD", "PGK", "SBD", "VUV", "WST", "TOP", "XPF"], description: "ISO 4217 currency code" },
|
|
379
|
+
{ name: "effectiveDate", in: "body", required: false, kind: "number" },
|
|
380
|
+
{ name: "isDefault", in: "body", required: false, kind: "boolean" }
|
|
381
|
+
]
|
|
382
|
+
},
|
|
383
|
+
create_layer: {
|
|
384
|
+
method: "POST",
|
|
385
|
+
path: "/projects/{id}/layers",
|
|
386
|
+
tag: "Takeoff",
|
|
387
|
+
summary: "Create a takeoff layer",
|
|
388
|
+
body: true,
|
|
389
|
+
response: "json",
|
|
390
|
+
params: [
|
|
391
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
392
|
+
{ name: "name", in: "body", required: true, kind: "string" }
|
|
393
|
+
]
|
|
394
|
+
},
|
|
395
|
+
create_list: {
|
|
396
|
+
method: "POST",
|
|
397
|
+
path: "/lists",
|
|
398
|
+
tag: "Lists",
|
|
399
|
+
summary: "Create a list",
|
|
400
|
+
body: true,
|
|
401
|
+
response: "json",
|
|
402
|
+
params: [
|
|
403
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
404
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
405
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
406
|
+
{ name: "type", in: "body", required: true, kind: "string", enum: ["product", "takeoffTemplate"] },
|
|
407
|
+
{ name: "privacy", in: "body", required: false, kind: "string", enum: ["private", "organization", "public", "system"] },
|
|
408
|
+
{ name: "category", in: "body", required: false, kind: "string" },
|
|
409
|
+
{ name: "locale", in: "body", required: true, kind: "string", enum: ["af-ZA", "id-ID", "ms-MY", "ca-ES", "cs-CZ", "da-DK", "de-DE", "en-US", "es-ES", "es-419", "fr-CA", "fr-FR", "hr-HR", "it-IT", "hu-HU", "nl-NL", "no-NO", "pl-PL", "pt-BR", "pt-PT", "ro-RO", "sk-SK", "fi-FI", "sv-SE", "vi-VN", "tr-TR", "el-GR", "ru-RU", "sr-SP", "uk-UA", "hy-AM", "he-IL", "ur-PK", "ar-SA", "hi-IN", "th-TH", "ko-KR", "ja-JP", "zh-CN", "zh-TW", null], description: "Supported BCP-47 locale code" }
|
|
410
|
+
]
|
|
411
|
+
},
|
|
412
|
+
create_measurement: {
|
|
413
|
+
method: "POST",
|
|
414
|
+
path: "/pages/{id}/measurements",
|
|
415
|
+
tag: "Takeoff",
|
|
416
|
+
summary: "Create a measurement on a page",
|
|
417
|
+
body: true,
|
|
418
|
+
response: "json",
|
|
419
|
+
params: [
|
|
420
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex page ID" },
|
|
421
|
+
{ name: "symbolId", in: "body", required: true, kind: "string", description: "Convex takeoff symbol ID" },
|
|
422
|
+
{ name: "shapeId", in: "body", required: true, kind: "string", description: "Canvas shape ID" },
|
|
423
|
+
{ name: "value", in: "body", required: true, kind: "number", description: "The measured quantity in RAW PAGE PIXELS \u2014 the same coordinate space as the geometry points, NEVER feet, metres, or any other real-world unit. Linear measurements are a length in px, areas are px\xB2, counts are a tally. Exayard converts to real units at display time using the page scale, so a real-world number sent here is converted a second time and reads wrong. When geometry is supplied the server measures it and rejects a value that disagrees by 10\xD7 or more, naming both numbers. To send a real-world quantity, convert it to page pixels first (multiply by the page's pixels-per-unit; square that factor for an area)." },
|
|
424
|
+
{ name: "geometry", in: "body", required: false, kind: "object", description: 'Measurement geometry: { "points": [{ "x": number, "y": number, "bulge"?: number }, ...], "isClosed": boolean, "holes"?: Point[][], "pathD"?: string }. When geometry is provided, "points" and "isClosed" are required; other keys (e.g. { "length": 280.9 }) are rejected.' },
|
|
425
|
+
{ name: "notes", in: "body", required: false, kind: "string" },
|
|
426
|
+
{ name: "layerId", in: "body", required: false, kind: "string", description: "Convex takeoff layer ID" }
|
|
427
|
+
]
|
|
428
|
+
},
|
|
429
|
+
create_message: {
|
|
430
|
+
method: "POST",
|
|
431
|
+
path: "/projects/{id}/messages",
|
|
432
|
+
tag: "Messages",
|
|
433
|
+
summary: "Post a message to a project",
|
|
434
|
+
body: true,
|
|
435
|
+
response: "json",
|
|
436
|
+
params: [
|
|
437
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
438
|
+
{ name: "content", in: "body", required: true, kind: "string" },
|
|
439
|
+
{ name: "parentMessageId", in: "body", required: false, kind: "string", description: "Convex chat message ID" },
|
|
440
|
+
{ name: "pageId", in: "body", required: false, kind: "string", description: "Convex page ID" },
|
|
441
|
+
{ name: "location", in: "body", required: false, kind: "object" },
|
|
442
|
+
{ name: "mentions", in: "body", required: false, kind: "array" }
|
|
443
|
+
]
|
|
444
|
+
},
|
|
445
|
+
create_organization: {
|
|
446
|
+
method: "POST",
|
|
447
|
+
path: "/organizations",
|
|
448
|
+
tag: "Account",
|
|
449
|
+
summary: "Create a company your app's company owns",
|
|
450
|
+
body: true,
|
|
451
|
+
response: "json",
|
|
452
|
+
params: [
|
|
453
|
+
{ name: "name", in: "body", required: true, kind: "string", description: "The company name, 1 to 200 characters." },
|
|
454
|
+
{ name: "country", in: "body", required: false, kind: "string", description: "ISO 3166-1 alpha-2 country code. It sets the company's starting tax, units and currency." },
|
|
455
|
+
{ name: "currency", in: "body", required: false, kind: "string", enum: ["USD", "CAD", "MXN", "BRL", "ARS", "CLP", "COP", "PEN", "UYU", "PYG", "BOB", "VES", "GYD", "SRD", "GTQ", "HNL", "NIO", "CRC", "PAB", "BZD", "JMD", "HTG", "DOP", "CUP", "BSD", "BBD", "TTD", "XCD", "AWG", "ANG", "KYD", "BMD", "FKP", "EUR", "GBP", "CHF", "NOK", "SEK", "DKK", "PLN", "CZK", "HUF", "RON", "BGN", "HRK", "RSD", "UAH", "RUB", "TRY", "ISK", "BAM", "MKD", "ALL", "MDL", "BYN", "GEL", "AMD", "AZN", "GIP", "ILS", "AED", "SAR", "QAR", "KWD", "BHD", "OMR", "JOD", "LBP", "EGP", "IQD", "IRR", "SYP", "YER", "ZAR", "NAD", "LSL", "SZL", "BWP", "NGN", "KES", "GHS", "MAD", "TND", "DZD", "LYD", "ETB", "ERN", "DJF", "SOS", "SDG", "SSP", "UGX", "TZS", "RWF", "BIF", "MWK", "ZMW", "ZWG", "MZN", "AOA", "CDF", "MGA", "MUR", "SCR", "KMF", "CVE", "GMD", "GNF", "LRD", "SLE", "MRU", "STN", "XOF", "XAF", "SHP", "JPY", "CNY", "HKD", "MOP", "TWD", "KRW", "KPW", "MNT", "SGD", "INR", "BTN", "PKR", "BDT", "LKR", "NPR", "MVR", "AFN", "IDR", "MYR", "PHP", "THB", "VND", "MMK", "KHR", "LAK", "BND", "KZT", "UZS", "KGS", "TJS", "TMT", "AUD", "NZD", "FJD", "PGK", "SBD", "VUV", "WST", "TOP", "XPF"], description: "ISO 4217 currency code" },
|
|
456
|
+
{ name: "measurementSystem", in: "body", required: false, kind: "string", enum: ["imperial", "metric"] }
|
|
457
|
+
]
|
|
458
|
+
},
|
|
459
|
+
create_product: {
|
|
460
|
+
method: "POST",
|
|
461
|
+
path: "/products",
|
|
462
|
+
tag: "Products",
|
|
463
|
+
summary: "Create a product",
|
|
464
|
+
body: true,
|
|
465
|
+
response: "json",
|
|
466
|
+
params: [
|
|
467
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
468
|
+
{ name: "listId", in: "body", required: true, kind: "string", description: "Convex product list ID" },
|
|
469
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
470
|
+
{ name: "sku", in: "body", required: false, kind: "string" },
|
|
471
|
+
{ name: "unitOfMeasure", in: "body", required: false, kind: "string" },
|
|
472
|
+
{ name: "costPerUnit", in: "body", required: false, kind: "number" },
|
|
473
|
+
{ name: "currency", in: "body", required: false, kind: "string", enum: ["USD", "CAD", "MXN", "BRL", "ARS", "CLP", "COP", "PEN", "UYU", "PYG", "BOB", "VES", "GYD", "SRD", "GTQ", "HNL", "NIO", "CRC", "PAB", "BZD", "JMD", "HTG", "DOP", "CUP", "BSD", "BBD", "TTD", "XCD", "AWG", "ANG", "KYD", "BMD", "FKP", "EUR", "GBP", "CHF", "NOK", "SEK", "DKK", "PLN", "CZK", "HUF", "RON", "BGN", "HRK", "RSD", "UAH", "RUB", "TRY", "ISK", "BAM", "MKD", "ALL", "MDL", "BYN", "GEL", "AMD", "AZN", "GIP", "ILS", "AED", "SAR", "QAR", "KWD", "BHD", "OMR", "JOD", "LBP", "EGP", "IQD", "IRR", "SYP", "YER", "ZAR", "NAD", "LSL", "SZL", "BWP", "NGN", "KES", "GHS", "MAD", "TND", "DZD", "LYD", "ETB", "ERN", "DJF", "SOS", "SDG", "SSP", "UGX", "TZS", "RWF", "BIF", "MWK", "ZMW", "ZWG", "MZN", "AOA", "CDF", "MGA", "MUR", "SCR", "KMF", "CVE", "GMD", "GNF", "LRD", "SLE", "MRU", "STN", "XOF", "XAF", "SHP", "JPY", "CNY", "HKD", "MOP", "TWD", "KRW", "KPW", "MNT", "SGD", "INR", "BTN", "PKR", "BDT", "LKR", "NPR", "MVR", "AFN", "IDR", "MYR", "PHP", "THB", "VND", "MMK", "KHR", "LAK", "BND", "KZT", "UZS", "KGS", "TJS", "TMT", "AUD", "NZD", "FJD", "PGK", "SBD", "VUV", "WST", "TOP", "XPF"], description: "ISO 4217 currency code" },
|
|
474
|
+
{ name: "markupPercent", in: "body", required: false, kind: "number" },
|
|
475
|
+
{ name: "customProperties", in: "body", required: false, kind: "object" },
|
|
476
|
+
{ name: "position", in: "body", required: false, kind: "integer" }
|
|
477
|
+
]
|
|
478
|
+
},
|
|
479
|
+
create_project: {
|
|
480
|
+
method: "POST",
|
|
481
|
+
path: "/projects",
|
|
482
|
+
tag: "Projects",
|
|
483
|
+
summary: "Create a project",
|
|
484
|
+
body: true,
|
|
485
|
+
response: "json",
|
|
486
|
+
params: [
|
|
487
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
488
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
489
|
+
{ name: "customProperties", in: "body", required: false, kind: "object" }
|
|
490
|
+
]
|
|
491
|
+
},
|
|
492
|
+
create_symbol: {
|
|
493
|
+
method: "POST",
|
|
494
|
+
path: "/projects/{id}/symbols",
|
|
495
|
+
tag: "Takeoff",
|
|
496
|
+
summary: "Create a takeoff symbol",
|
|
497
|
+
body: true,
|
|
498
|
+
response: "json",
|
|
499
|
+
params: [
|
|
500
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
501
|
+
{ name: "isTemplate", in: "body", required: false, kind: "boolean" },
|
|
502
|
+
{ name: "listId", in: "body", required: false, kind: "string", description: "Convex product list ID" },
|
|
503
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
504
|
+
{ name: "type", in: "body", required: true, kind: "string", enum: ["count", "linear", "area"] },
|
|
505
|
+
{ name: "color", in: "body", required: true, kind: "string" },
|
|
506
|
+
{ name: "category", in: "body", required: false, kind: "string" },
|
|
507
|
+
{ name: "subcategory", in: "body", required: false, kind: "string" },
|
|
508
|
+
{ name: "groupId", in: "body", required: false, kind: "string", description: "Convex takeoff group ID" },
|
|
509
|
+
{ name: "customProperties", in: "body", required: false, kind: "object" }
|
|
510
|
+
]
|
|
511
|
+
},
|
|
512
|
+
create_vendor: {
|
|
513
|
+
method: "POST",
|
|
514
|
+
path: "/vendors",
|
|
515
|
+
tag: "Vendors",
|
|
516
|
+
summary: "Create a vendor",
|
|
517
|
+
body: true,
|
|
518
|
+
response: "json",
|
|
519
|
+
params: [
|
|
520
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
521
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
522
|
+
{ name: "type", in: "body", required: true, kind: "string", enum: ["supplier", "subcontractor", "rental"] },
|
|
523
|
+
{ name: "kind", in: "body", required: false, kind: "string", enum: ["sub", "supplier", "surety", "insurer", "agency", "owner", "architect", "engineer", "other", null] },
|
|
524
|
+
{ name: "contactName", in: "body", required: false, kind: "string" },
|
|
525
|
+
{ name: "email", in: "body", required: false, kind: "string" },
|
|
526
|
+
{ name: "phone", in: "body", required: false, kind: "string" },
|
|
527
|
+
{ name: "website", in: "body", required: false, kind: "string" },
|
|
528
|
+
{ name: "address", in: "body", required: false, kind: "object" },
|
|
529
|
+
{ name: "paymentTerms", in: "body", required: false, kind: "string" },
|
|
530
|
+
{ name: "accountNumber", in: "body", required: false, kind: "string" },
|
|
531
|
+
{ name: "notes", in: "body", required: false, kind: "string" },
|
|
532
|
+
{ name: "tags", in: "body", required: false, kind: "array" },
|
|
533
|
+
{ name: "isPreferred", in: "body", required: false, kind: "boolean" }
|
|
534
|
+
]
|
|
535
|
+
},
|
|
536
|
+
create_vendor_quote: {
|
|
537
|
+
method: "POST",
|
|
538
|
+
path: "/projects/{id}/quotes",
|
|
539
|
+
tag: "Vendor quotes",
|
|
540
|
+
summary: "Create a vendor quote request",
|
|
541
|
+
body: true,
|
|
542
|
+
response: "json",
|
|
543
|
+
params: [
|
|
544
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
545
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
546
|
+
{ name: "vendorId", in: "body", required: true, kind: "string" },
|
|
547
|
+
{ name: "quoteNumber", in: "body", required: true, kind: "string" },
|
|
548
|
+
{ name: "lineItems", in: "body", required: false, kind: "array" },
|
|
549
|
+
{ name: "subtotal", in: "body", required: false, kind: "number", description: "Sum of the line items' totalPrice when lineItems are sent; absent counts as 0" },
|
|
550
|
+
{ name: "tax", in: "body", required: false, kind: "number" },
|
|
551
|
+
{ name: "shipping", in: "body", required: false, kind: "number" },
|
|
552
|
+
{ name: "total", in: "body", required: false, kind: "number", description: "Must equal subtotal + tax + shipping (an absent amount counts as 0); absent counts as 0" },
|
|
553
|
+
{ name: "validUntil", in: "body", required: false, kind: "number", description: "Quote expiry as epoch milliseconds" },
|
|
554
|
+
{ name: "notes", in: "body", required: false, kind: "string" }
|
|
555
|
+
]
|
|
556
|
+
},
|
|
557
|
+
create_webhook_endpoint: {
|
|
558
|
+
method: "POST",
|
|
559
|
+
path: "/webhook_endpoints",
|
|
560
|
+
tag: "Webhooks",
|
|
561
|
+
summary: "Create a webhook endpoint",
|
|
562
|
+
body: true,
|
|
563
|
+
response: "json",
|
|
564
|
+
params: [
|
|
565
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
566
|
+
{ name: "url", in: "body", required: true, kind: "string" },
|
|
567
|
+
{ name: "events", in: "body", required: true, kind: "array" },
|
|
568
|
+
{ name: "description", in: "body", required: false, kind: "string" }
|
|
569
|
+
]
|
|
570
|
+
},
|
|
571
|
+
create_widget: {
|
|
572
|
+
method: "POST",
|
|
573
|
+
path: "/widgets",
|
|
574
|
+
tag: "Widgets",
|
|
575
|
+
summary: "Create a widget",
|
|
576
|
+
body: true,
|
|
577
|
+
response: "json",
|
|
578
|
+
params: [
|
|
579
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
580
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
581
|
+
{ name: "config", in: "body", required: false, kind: "object" }
|
|
582
|
+
]
|
|
583
|
+
},
|
|
584
|
+
delete_organization: {
|
|
585
|
+
method: "DELETE",
|
|
586
|
+
path: "/organizations/{id}",
|
|
587
|
+
tag: "Account",
|
|
588
|
+
summary: "Release a company your app's company owns",
|
|
589
|
+
body: false,
|
|
590
|
+
response: "json",
|
|
591
|
+
params: [
|
|
592
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "The id of a company your app's company owns, from GET /v1/organizations or its creation." }
|
|
593
|
+
]
|
|
594
|
+
},
|
|
595
|
+
delete_webhook_endpoint: {
|
|
596
|
+
method: "DELETE",
|
|
597
|
+
path: "/webhook_endpoints/{id}",
|
|
598
|
+
tag: "Webhooks",
|
|
599
|
+
summary: "Delete a webhook endpoint",
|
|
600
|
+
body: false,
|
|
601
|
+
response: "empty",
|
|
602
|
+
params: [
|
|
603
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
604
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
605
|
+
]
|
|
606
|
+
},
|
|
607
|
+
disconnect_integration: {
|
|
608
|
+
method: "DELETE",
|
|
609
|
+
path: "/integrations/{id}",
|
|
610
|
+
tag: "Integrations",
|
|
611
|
+
summary: "Disconnect an integration",
|
|
612
|
+
body: false,
|
|
613
|
+
response: "empty",
|
|
614
|
+
params: [
|
|
615
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
616
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
617
|
+
]
|
|
618
|
+
},
|
|
619
|
+
export_project: {
|
|
620
|
+
method: "GET",
|
|
621
|
+
path: "/projects/{id}/export",
|
|
622
|
+
tag: "Projects",
|
|
623
|
+
summary: "Export a project as structured JSON",
|
|
624
|
+
body: false,
|
|
625
|
+
response: "json",
|
|
626
|
+
params: [
|
|
627
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
628
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
629
|
+
]
|
|
630
|
+
},
|
|
631
|
+
find_symbol_elements: {
|
|
632
|
+
method: "POST",
|
|
633
|
+
path: "/pages/{id}/elements/find",
|
|
634
|
+
tag: "Takeoff",
|
|
635
|
+
summary: "Detect instances of an element on a page via Claude Vision",
|
|
636
|
+
body: true,
|
|
637
|
+
response: "json",
|
|
638
|
+
params: [
|
|
639
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex page ID" },
|
|
640
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
641
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Convex project ID" },
|
|
642
|
+
{ name: "elementDescription", in: "body", required: true, kind: "string", description: 'Natural-language description of what to find (e.g. "electrical outlets", "doors")' },
|
|
643
|
+
{ name: "measurementType", in: "body", required: true, kind: "string", enum: ["count", "linear", "area"], description: "How detections should be shaped (count marker / linear line / area rectangle)" },
|
|
644
|
+
{ name: "persist", in: "body", required: false, kind: "boolean", description: "When true, writes the detected shapes to the realtime canvas via the sync-worker. Returns persistedShapeIds on success." }
|
|
645
|
+
]
|
|
646
|
+
},
|
|
647
|
+
generate_estimate: {
|
|
648
|
+
method: "POST",
|
|
649
|
+
path: "/projects/{id}/estimates/generate",
|
|
650
|
+
tag: "Estimates",
|
|
651
|
+
summary: "Stream an AI-generated cost estimate",
|
|
652
|
+
body: true,
|
|
653
|
+
response: "stream",
|
|
654
|
+
params: [
|
|
655
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
656
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
657
|
+
{ name: "query", in: "body", required: true, kind: "string", description: "Natural-language prompt describing the scope of the estimate" },
|
|
658
|
+
{ name: "notes", in: "body", required: false, kind: "string", description: "Additional notes appended to the prompt" },
|
|
659
|
+
{ name: "measurementContext", in: "body", required: false, kind: "string", description: "Pre-serialized takeoff data to ground the estimator (see GET /v1/projects/{id}/takeoff-summary)" }
|
|
660
|
+
]
|
|
661
|
+
},
|
|
662
|
+
get_assessment: {
|
|
663
|
+
method: "GET",
|
|
664
|
+
path: "/assessments/{id}",
|
|
665
|
+
tag: "Takeoff",
|
|
666
|
+
summary: "Get an assessment by ID",
|
|
667
|
+
body: false,
|
|
668
|
+
response: "json",
|
|
669
|
+
params: [
|
|
670
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex takeoff assessment ID" }
|
|
671
|
+
]
|
|
672
|
+
},
|
|
673
|
+
get_best_price_for_product: {
|
|
674
|
+
method: "GET",
|
|
675
|
+
path: "/products/{id}/best-price",
|
|
676
|
+
tag: "Vendor prices",
|
|
677
|
+
summary: "Best vendor price for a product",
|
|
678
|
+
body: false,
|
|
679
|
+
response: "json",
|
|
680
|
+
params: [
|
|
681
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex product ID" },
|
|
682
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
683
|
+
{ name: "quantity", in: "query", required: false, kind: "number" }
|
|
684
|
+
]
|
|
685
|
+
},
|
|
686
|
+
get_contact: {
|
|
687
|
+
method: "GET",
|
|
688
|
+
path: "/contacts/{id}",
|
|
689
|
+
tag: "Contacts",
|
|
690
|
+
summary: "Get a contact by ID",
|
|
691
|
+
body: false,
|
|
692
|
+
response: "json",
|
|
693
|
+
params: [
|
|
694
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex contact ID" },
|
|
695
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
696
|
+
]
|
|
697
|
+
},
|
|
698
|
+
get_conversation: {
|
|
699
|
+
method: "GET",
|
|
700
|
+
path: "/conversations/{id}",
|
|
701
|
+
tag: "Conversations",
|
|
702
|
+
summary: "Get a conversation by ID",
|
|
703
|
+
body: false,
|
|
704
|
+
response: "json",
|
|
705
|
+
params: [
|
|
706
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex conversation ID" },
|
|
707
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
708
|
+
]
|
|
709
|
+
},
|
|
710
|
+
get_credit_balance: {
|
|
711
|
+
method: "GET",
|
|
712
|
+
path: "/credits/balance",
|
|
713
|
+
tag: "Credits",
|
|
714
|
+
summary: "Get credit balance for an organization",
|
|
715
|
+
body: false,
|
|
716
|
+
response: "json",
|
|
717
|
+
params: [
|
|
718
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
719
|
+
]
|
|
720
|
+
},
|
|
721
|
+
get_credit_usage_history: {
|
|
722
|
+
method: "GET",
|
|
723
|
+
path: "/credits/usage",
|
|
724
|
+
tag: "Credits",
|
|
725
|
+
summary: "Get credit usage history",
|
|
726
|
+
body: false,
|
|
727
|
+
response: "json",
|
|
728
|
+
params: [
|
|
729
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
730
|
+
{ name: "limit", in: "query", required: false, kind: "integer" },
|
|
731
|
+
{ name: "cursor", in: "query", required: false, kind: "number" },
|
|
732
|
+
{ name: "dateFrom", in: "query", required: false, kind: "number" },
|
|
733
|
+
{ name: "dateTo", in: "query", required: false, kind: "number" },
|
|
734
|
+
{ name: "type", in: "query", required: false, kind: "string", enum: ["issuance", "consumption"] },
|
|
735
|
+
{ name: "kind", in: "query", required: false, kind: "string", enum: ["monthly", "gifted", "purchased"] }
|
|
736
|
+
]
|
|
737
|
+
},
|
|
738
|
+
get_credit_usage_stats: {
|
|
739
|
+
method: "GET",
|
|
740
|
+
path: "/credits/stats",
|
|
741
|
+
tag: "Credits",
|
|
742
|
+
summary: "Get credit usage stats",
|
|
743
|
+
body: false,
|
|
744
|
+
response: "json",
|
|
745
|
+
params: [
|
|
746
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
747
|
+
{ name: "dateFrom", in: "query", required: false, kind: "number" },
|
|
748
|
+
{ name: "dateTo", in: "query", required: false, kind: "number" }
|
|
749
|
+
]
|
|
750
|
+
},
|
|
751
|
+
get_custom_property: {
|
|
752
|
+
method: "GET",
|
|
753
|
+
path: "/custom-properties/{id}",
|
|
754
|
+
tag: "Custom properties",
|
|
755
|
+
summary: "Get a custom property",
|
|
756
|
+
body: false,
|
|
757
|
+
response: "json",
|
|
758
|
+
params: [
|
|
759
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex custom property ID" },
|
|
760
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
761
|
+
]
|
|
762
|
+
},
|
|
763
|
+
get_document: {
|
|
764
|
+
method: "GET",
|
|
765
|
+
path: "/documents/{id}",
|
|
766
|
+
tag: "Documents",
|
|
767
|
+
summary: "Get a single document",
|
|
768
|
+
body: false,
|
|
769
|
+
response: "json",
|
|
770
|
+
params: [
|
|
771
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex document ID" },
|
|
772
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
773
|
+
]
|
|
774
|
+
},
|
|
775
|
+
get_file: {
|
|
776
|
+
method: "GET",
|
|
777
|
+
path: "/files/{id}",
|
|
778
|
+
tag: "Files",
|
|
779
|
+
summary: "Get a file with its pages",
|
|
780
|
+
body: false,
|
|
781
|
+
response: "json",
|
|
782
|
+
params: [
|
|
783
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex file ID" },
|
|
784
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
785
|
+
]
|
|
786
|
+
},
|
|
787
|
+
get_file_url: {
|
|
788
|
+
method: "GET",
|
|
789
|
+
path: "/files/{id}/url",
|
|
790
|
+
tag: "Files",
|
|
791
|
+
summary: "Get a presigned download URL for a file",
|
|
792
|
+
body: false,
|
|
793
|
+
response: "json",
|
|
794
|
+
params: [
|
|
795
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex file ID" },
|
|
796
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
797
|
+
]
|
|
798
|
+
},
|
|
799
|
+
get_folder: {
|
|
800
|
+
method: "GET",
|
|
801
|
+
path: "/folders/{id}",
|
|
802
|
+
tag: "Folders",
|
|
803
|
+
summary: "Get a folder by ID",
|
|
804
|
+
body: false,
|
|
805
|
+
response: "json",
|
|
806
|
+
params: [
|
|
807
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex folder ID" }
|
|
808
|
+
]
|
|
809
|
+
},
|
|
810
|
+
get_labor_crew: {
|
|
811
|
+
method: "GET",
|
|
812
|
+
path: "/labor/crews/{id}",
|
|
813
|
+
tag: "Labor",
|
|
814
|
+
summary: "Get a labor crew",
|
|
815
|
+
body: false,
|
|
816
|
+
response: "json",
|
|
817
|
+
params: [
|
|
818
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
819
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
820
|
+
]
|
|
821
|
+
},
|
|
822
|
+
get_labor_equipment: {
|
|
823
|
+
method: "GET",
|
|
824
|
+
path: "/labor/equipment/{id}",
|
|
825
|
+
tag: "Labor",
|
|
826
|
+
summary: "Get a labor equipment entry",
|
|
827
|
+
body: false,
|
|
828
|
+
response: "json",
|
|
829
|
+
params: [
|
|
830
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
831
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
832
|
+
]
|
|
833
|
+
},
|
|
834
|
+
get_labor_rate: {
|
|
835
|
+
method: "GET",
|
|
836
|
+
path: "/labor/rates/{id}",
|
|
837
|
+
tag: "Labor",
|
|
838
|
+
summary: "Get a labor rate",
|
|
839
|
+
body: false,
|
|
840
|
+
response: "json",
|
|
841
|
+
params: [
|
|
842
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
843
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
844
|
+
]
|
|
845
|
+
},
|
|
846
|
+
get_latest_assessment: {
|
|
847
|
+
method: "GET",
|
|
848
|
+
path: "/projects/{id}/assessments/latest",
|
|
849
|
+
tag: "Takeoff",
|
|
850
|
+
summary: "Get the most recent assessment with elements",
|
|
851
|
+
body: false,
|
|
852
|
+
response: "json",
|
|
853
|
+
params: [
|
|
854
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" }
|
|
855
|
+
]
|
|
856
|
+
},
|
|
857
|
+
get_list: {
|
|
858
|
+
method: "GET",
|
|
859
|
+
path: "/lists/{id}",
|
|
860
|
+
tag: "Lists",
|
|
861
|
+
summary: "Get a single list",
|
|
862
|
+
body: false,
|
|
863
|
+
response: "json",
|
|
864
|
+
params: [
|
|
865
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex product list ID" },
|
|
866
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
867
|
+
]
|
|
868
|
+
},
|
|
869
|
+
get_me: {
|
|
870
|
+
method: "GET",
|
|
871
|
+
path: "/me",
|
|
872
|
+
tag: "Account",
|
|
873
|
+
summary: "Get the authenticated caller",
|
|
874
|
+
body: false,
|
|
875
|
+
response: "json",
|
|
876
|
+
params: []
|
|
877
|
+
},
|
|
878
|
+
get_measurement: {
|
|
879
|
+
method: "GET",
|
|
880
|
+
path: "/pages/{pageId}/measurements/{shapeId}",
|
|
881
|
+
tag: "Takeoff",
|
|
882
|
+
summary: "Get a measurement by shape ID",
|
|
883
|
+
body: false,
|
|
884
|
+
response: "json",
|
|
885
|
+
params: [
|
|
886
|
+
{ name: "pageId", in: "path", required: true, kind: "string", description: "Convex page ID" },
|
|
887
|
+
{ name: "shapeId", in: "path", required: true, kind: "string" }
|
|
888
|
+
]
|
|
889
|
+
},
|
|
890
|
+
get_page: {
|
|
891
|
+
method: "GET",
|
|
892
|
+
path: "/pages/{id}",
|
|
893
|
+
tag: "Pages",
|
|
894
|
+
summary: "Get a single page",
|
|
895
|
+
body: false,
|
|
896
|
+
response: "json",
|
|
897
|
+
params: [
|
|
898
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex page ID" }
|
|
899
|
+
]
|
|
900
|
+
},
|
|
901
|
+
get_page_content: {
|
|
902
|
+
method: "GET",
|
|
903
|
+
path: "/pages/{id}/content",
|
|
904
|
+
tag: "Pages",
|
|
905
|
+
summary: "Extracted text and AI description for a page",
|
|
906
|
+
body: false,
|
|
907
|
+
response: "json",
|
|
908
|
+
params: [
|
|
909
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex page ID" }
|
|
910
|
+
]
|
|
911
|
+
},
|
|
912
|
+
get_prices_for_product: {
|
|
913
|
+
method: "GET",
|
|
914
|
+
path: "/products/{id}/prices",
|
|
915
|
+
tag: "Vendor prices",
|
|
916
|
+
summary: "List vendor prices for a product",
|
|
917
|
+
body: false,
|
|
918
|
+
response: "json",
|
|
919
|
+
params: [
|
|
920
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex product ID" },
|
|
921
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
922
|
+
]
|
|
923
|
+
},
|
|
924
|
+
get_product: {
|
|
925
|
+
method: "GET",
|
|
926
|
+
path: "/products/{id}",
|
|
927
|
+
tag: "Products",
|
|
928
|
+
summary: "Get a single product",
|
|
929
|
+
body: false,
|
|
930
|
+
response: "json",
|
|
931
|
+
params: [
|
|
932
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex product ID" },
|
|
933
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
934
|
+
]
|
|
935
|
+
},
|
|
936
|
+
get_project: {
|
|
937
|
+
method: "GET",
|
|
938
|
+
path: "/projects/{id}",
|
|
939
|
+
tag: "Projects",
|
|
940
|
+
summary: "Get a single project",
|
|
941
|
+
body: false,
|
|
942
|
+
response: "json",
|
|
943
|
+
params: [
|
|
944
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
945
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
946
|
+
]
|
|
947
|
+
},
|
|
948
|
+
get_project_relations: {
|
|
949
|
+
method: "GET",
|
|
950
|
+
path: "/projects/{id}/relations",
|
|
951
|
+
tag: "Projects",
|
|
952
|
+
summary: "Get a project with page/estimate counts",
|
|
953
|
+
body: false,
|
|
954
|
+
response: "json",
|
|
955
|
+
params: [
|
|
956
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
957
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
958
|
+
]
|
|
959
|
+
},
|
|
960
|
+
get_spreadsheet: {
|
|
961
|
+
method: "GET",
|
|
962
|
+
path: "/spreadsheets/{id}",
|
|
963
|
+
tag: "Spreadsheets",
|
|
964
|
+
summary: "Get a single spreadsheet",
|
|
965
|
+
body: false,
|
|
966
|
+
response: "json",
|
|
967
|
+
params: [
|
|
968
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex spreadsheet ID" },
|
|
969
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
970
|
+
]
|
|
971
|
+
},
|
|
972
|
+
get_symbol: {
|
|
973
|
+
method: "GET",
|
|
974
|
+
path: "/symbols/{id}",
|
|
975
|
+
tag: "Takeoff",
|
|
976
|
+
summary: "Get a takeoff symbol",
|
|
977
|
+
body: false,
|
|
978
|
+
response: "json",
|
|
979
|
+
params: [
|
|
980
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex takeoff symbol ID" },
|
|
981
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
982
|
+
]
|
|
983
|
+
},
|
|
984
|
+
get_symbol_line_item: {
|
|
985
|
+
method: "GET",
|
|
986
|
+
path: "/symbols/{symbolId}/line-items/{id}",
|
|
987
|
+
tag: "Takeoff",
|
|
988
|
+
summary: "Get a single line item on a symbol",
|
|
989
|
+
body: false,
|
|
990
|
+
response: "json",
|
|
991
|
+
params: [
|
|
992
|
+
{ name: "symbolId", in: "path", required: true, kind: "string", description: "Convex takeoff symbol ID" },
|
|
993
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
994
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
995
|
+
]
|
|
996
|
+
},
|
|
997
|
+
get_takeoff_summary: {
|
|
998
|
+
method: "GET",
|
|
999
|
+
path: "/projects/{id}/takeoff-summary",
|
|
1000
|
+
tag: "Takeoff",
|
|
1001
|
+
summary: "Aggregated totals across takeoff symbols in a project",
|
|
1002
|
+
body: false,
|
|
1003
|
+
response: "json",
|
|
1004
|
+
params: [
|
|
1005
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" }
|
|
1006
|
+
]
|
|
1007
|
+
},
|
|
1008
|
+
get_unread_notification_count: {
|
|
1009
|
+
method: "GET",
|
|
1010
|
+
path: "/notifications/unread-count",
|
|
1011
|
+
tag: "Notifications",
|
|
1012
|
+
summary: "Get unread notification count",
|
|
1013
|
+
body: false,
|
|
1014
|
+
response: "json",
|
|
1015
|
+
params: []
|
|
1016
|
+
},
|
|
1017
|
+
get_usage: {
|
|
1018
|
+
method: "GET",
|
|
1019
|
+
path: "/usage",
|
|
1020
|
+
tag: "Credits",
|
|
1021
|
+
summary: "Get AI usage by app, key, end user or operation",
|
|
1022
|
+
body: false,
|
|
1023
|
+
response: "json",
|
|
1024
|
+
params: [
|
|
1025
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1026
|
+
{ name: "groupBy", in: "query", required: true, kind: "string", enum: ["app", "key", "end_user", "operation"], description: "What to group by: app, key, end user (the Exayard-End-User header) or operation." },
|
|
1027
|
+
{ name: "dateFrom", in: "query", required: false, kind: "number", description: "Start of the window, epoch ms, inclusive. Default: this billing month." },
|
|
1028
|
+
{ name: "dateTo", in: "query", required: false, kind: "number", description: "End of the window, epoch ms, exclusive. Default: now when dateFrom is given, else the end of this billing month." },
|
|
1029
|
+
{ name: "appId", in: "query", required: false, kind: "string", description: "Only the usage of this app." },
|
|
1030
|
+
{ name: "appKeyId", in: "query", required: false, kind: "string", description: "Only the usage of this key." },
|
|
1031
|
+
{ name: "endUser", in: "query", required: false, kind: "string", description: "Only the usage of this end user." }
|
|
1032
|
+
]
|
|
1033
|
+
},
|
|
1034
|
+
get_vendor: {
|
|
1035
|
+
method: "GET",
|
|
1036
|
+
path: "/vendors/{id}",
|
|
1037
|
+
tag: "Vendors",
|
|
1038
|
+
summary: "Get a single vendor",
|
|
1039
|
+
body: false,
|
|
1040
|
+
response: "json",
|
|
1041
|
+
params: [
|
|
1042
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex vendor ID" },
|
|
1043
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1044
|
+
]
|
|
1045
|
+
},
|
|
1046
|
+
get_vendor_quote: {
|
|
1047
|
+
method: "GET",
|
|
1048
|
+
path: "/quotes/{id}",
|
|
1049
|
+
tag: "Vendor quotes",
|
|
1050
|
+
summary: "Get a vendor quote",
|
|
1051
|
+
body: false,
|
|
1052
|
+
response: "json",
|
|
1053
|
+
params: [
|
|
1054
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1055
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1056
|
+
]
|
|
1057
|
+
},
|
|
1058
|
+
get_widget: {
|
|
1059
|
+
method: "GET",
|
|
1060
|
+
path: "/widgets/{id}",
|
|
1061
|
+
tag: "Widgets",
|
|
1062
|
+
summary: "Get a widget",
|
|
1063
|
+
body: false,
|
|
1064
|
+
response: "json",
|
|
1065
|
+
params: [
|
|
1066
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex widget ID" },
|
|
1067
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1068
|
+
]
|
|
1069
|
+
},
|
|
1070
|
+
list_assessments: {
|
|
1071
|
+
method: "GET",
|
|
1072
|
+
path: "/projects/{id}/assessments",
|
|
1073
|
+
tag: "Takeoff",
|
|
1074
|
+
summary: "List assessments for a project",
|
|
1075
|
+
body: false,
|
|
1076
|
+
response: "json",
|
|
1077
|
+
params: [
|
|
1078
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" }
|
|
1079
|
+
]
|
|
1080
|
+
},
|
|
1081
|
+
list_contacts: {
|
|
1082
|
+
method: "GET",
|
|
1083
|
+
path: "/contacts",
|
|
1084
|
+
tag: "Contacts",
|
|
1085
|
+
summary: "List contacts captured by widgets",
|
|
1086
|
+
body: false,
|
|
1087
|
+
response: "json",
|
|
1088
|
+
params: [
|
|
1089
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1090
|
+
{ name: "widgetId", in: "query", required: false, kind: "string" },
|
|
1091
|
+
{ name: "search", in: "query", required: false, kind: "string" },
|
|
1092
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1093
|
+
]
|
|
1094
|
+
},
|
|
1095
|
+
list_conversation_files: {
|
|
1096
|
+
method: "GET",
|
|
1097
|
+
path: "/conversations/{id}/files",
|
|
1098
|
+
tag: "Conversations",
|
|
1099
|
+
summary: "List files uploaded in a conversation",
|
|
1100
|
+
body: false,
|
|
1101
|
+
response: "json",
|
|
1102
|
+
params: [
|
|
1103
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex conversation ID" },
|
|
1104
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1105
|
+
{ name: "imagesOnly", in: "query", required: false, kind: "boolean" }
|
|
1106
|
+
]
|
|
1107
|
+
},
|
|
1108
|
+
list_conversations: {
|
|
1109
|
+
method: "GET",
|
|
1110
|
+
path: "/conversations",
|
|
1111
|
+
tag: "Conversations",
|
|
1112
|
+
summary: "List conversations for the caller",
|
|
1113
|
+
body: false,
|
|
1114
|
+
response: "json",
|
|
1115
|
+
params: [
|
|
1116
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1117
|
+
{ name: "type", in: "query", required: false, kind: "string", enum: ["ai_chat", "project_activity", "whatsapp_chat"] },
|
|
1118
|
+
{ name: "channel", in: "query", required: false, kind: "string", enum: ["app", "email", "whatsapp"] },
|
|
1119
|
+
{ name: "limit", in: "query", required: false, kind: "integer" },
|
|
1120
|
+
{ name: "cursor", in: "query", required: false, kind: "number" }
|
|
1121
|
+
]
|
|
1122
|
+
},
|
|
1123
|
+
list_custom_properties: {
|
|
1124
|
+
method: "GET",
|
|
1125
|
+
path: "/custom-properties",
|
|
1126
|
+
tag: "Custom properties",
|
|
1127
|
+
summary: "List custom properties in an organization",
|
|
1128
|
+
body: false,
|
|
1129
|
+
response: "json",
|
|
1130
|
+
params: [
|
|
1131
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1132
|
+
{ name: "recordType", in: "query", required: false, kind: "string", enum: ["project", "countTakeoff", "linearTakeoff", "areaTakeoff", "product"] }
|
|
1133
|
+
]
|
|
1134
|
+
},
|
|
1135
|
+
list_documents: {
|
|
1136
|
+
method: "GET",
|
|
1137
|
+
path: "/projects/{id}/documents",
|
|
1138
|
+
tag: "Documents",
|
|
1139
|
+
summary: "List documents in a project",
|
|
1140
|
+
body: false,
|
|
1141
|
+
response: "json",
|
|
1142
|
+
params: [
|
|
1143
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
1144
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1145
|
+
]
|
|
1146
|
+
},
|
|
1147
|
+
list_files: {
|
|
1148
|
+
method: "GET",
|
|
1149
|
+
path: "/files",
|
|
1150
|
+
tag: "Files",
|
|
1151
|
+
summary: "List files in a project",
|
|
1152
|
+
body: false,
|
|
1153
|
+
response: "json",
|
|
1154
|
+
params: [
|
|
1155
|
+
{ name: "projectId", in: "query", required: true, kind: "string" },
|
|
1156
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1157
|
+
]
|
|
1158
|
+
},
|
|
1159
|
+
list_folder_contents: {
|
|
1160
|
+
method: "GET",
|
|
1161
|
+
path: "/folders/contents",
|
|
1162
|
+
tag: "Folders",
|
|
1163
|
+
summary: "List files and folders inside a folder",
|
|
1164
|
+
body: false,
|
|
1165
|
+
response: "json",
|
|
1166
|
+
params: [
|
|
1167
|
+
{ name: "projectId", in: "query", required: true, kind: "string", description: "Convex project ID" },
|
|
1168
|
+
{ name: "parentId", in: "query", required: false, kind: "string" }
|
|
1169
|
+
]
|
|
1170
|
+
},
|
|
1171
|
+
list_folders: {
|
|
1172
|
+
method: "GET",
|
|
1173
|
+
path: "/folders",
|
|
1174
|
+
tag: "Folders",
|
|
1175
|
+
summary: "List folders in a project",
|
|
1176
|
+
body: false,
|
|
1177
|
+
response: "json",
|
|
1178
|
+
params: [
|
|
1179
|
+
{ name: "projectId", in: "query", required: true, kind: "string", description: "Convex project ID" }
|
|
1180
|
+
]
|
|
1181
|
+
},
|
|
1182
|
+
list_groups: {
|
|
1183
|
+
method: "GET",
|
|
1184
|
+
path: "/projects/{id}/groups",
|
|
1185
|
+
tag: "Takeoff",
|
|
1186
|
+
summary: "List takeoff groups in a project",
|
|
1187
|
+
body: false,
|
|
1188
|
+
response: "json",
|
|
1189
|
+
params: [
|
|
1190
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" }
|
|
1191
|
+
]
|
|
1192
|
+
},
|
|
1193
|
+
list_installations: {
|
|
1194
|
+
method: "GET",
|
|
1195
|
+
path: "/installations",
|
|
1196
|
+
tag: "Account",
|
|
1197
|
+
summary: "List the companies where the calling app is installed",
|
|
1198
|
+
body: false,
|
|
1199
|
+
response: "json",
|
|
1200
|
+
params: [
|
|
1201
|
+
{ name: "limit", in: "query", required: false, kind: "integer", description: "Companies per page (1 to 100, default 50)." },
|
|
1202
|
+
{ name: "cursor", in: "query", required: false, kind: "string", description: "`nextCursor` from the previous page." }
|
|
1203
|
+
]
|
|
1204
|
+
},
|
|
1205
|
+
list_integrations: {
|
|
1206
|
+
method: "GET",
|
|
1207
|
+
path: "/integrations",
|
|
1208
|
+
tag: "Integrations",
|
|
1209
|
+
summary: "List integrations connected to an organization",
|
|
1210
|
+
body: false,
|
|
1211
|
+
response: "json",
|
|
1212
|
+
params: [
|
|
1213
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1214
|
+
]
|
|
1215
|
+
},
|
|
1216
|
+
list_labor_crews: {
|
|
1217
|
+
method: "GET",
|
|
1218
|
+
path: "/labor/crews",
|
|
1219
|
+
tag: "Labor",
|
|
1220
|
+
summary: "List labor crews",
|
|
1221
|
+
body: false,
|
|
1222
|
+
response: "json",
|
|
1223
|
+
params: [
|
|
1224
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1225
|
+
{ name: "includeInactive", in: "query", required: false, kind: "boolean" }
|
|
1226
|
+
]
|
|
1227
|
+
},
|
|
1228
|
+
list_labor_equipment: {
|
|
1229
|
+
method: "GET",
|
|
1230
|
+
path: "/labor/equipment",
|
|
1231
|
+
tag: "Labor",
|
|
1232
|
+
summary: "List labor equipment",
|
|
1233
|
+
body: false,
|
|
1234
|
+
response: "json",
|
|
1235
|
+
params: [
|
|
1236
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1237
|
+
{ name: "category", in: "query", required: false, kind: "string" },
|
|
1238
|
+
{ name: "includeInactive", in: "query", required: false, kind: "boolean" }
|
|
1239
|
+
]
|
|
1240
|
+
},
|
|
1241
|
+
list_labor_rates: {
|
|
1242
|
+
method: "GET",
|
|
1243
|
+
path: "/labor/rates",
|
|
1244
|
+
tag: "Labor",
|
|
1245
|
+
summary: "List labor rates",
|
|
1246
|
+
body: false,
|
|
1247
|
+
response: "json",
|
|
1248
|
+
params: [
|
|
1249
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1250
|
+
{ name: "includeInactive", in: "query", required: false, kind: "boolean" }
|
|
1251
|
+
]
|
|
1252
|
+
},
|
|
1253
|
+
list_layers: {
|
|
1254
|
+
method: "GET",
|
|
1255
|
+
path: "/projects/{id}/layers",
|
|
1256
|
+
tag: "Takeoff",
|
|
1257
|
+
summary: "List takeoff layers in a project",
|
|
1258
|
+
body: false,
|
|
1259
|
+
response: "json",
|
|
1260
|
+
params: [
|
|
1261
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
1262
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1263
|
+
]
|
|
1264
|
+
},
|
|
1265
|
+
list_lists: {
|
|
1266
|
+
method: "GET",
|
|
1267
|
+
path: "/lists",
|
|
1268
|
+
tag: "Lists",
|
|
1269
|
+
summary: "List product lists and takeoff templates",
|
|
1270
|
+
body: false,
|
|
1271
|
+
response: "json",
|
|
1272
|
+
params: [
|
|
1273
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1274
|
+
{ name: "type", in: "query", required: false, kind: "string", enum: ["product", "takeoffTemplate"] },
|
|
1275
|
+
{ name: "category", in: "query", required: false, kind: "string" },
|
|
1276
|
+
{ name: "includeInactive", in: "query", required: false, kind: "boolean" }
|
|
1277
|
+
]
|
|
1278
|
+
},
|
|
1279
|
+
list_measurements_by_page: {
|
|
1280
|
+
method: "GET",
|
|
1281
|
+
path: "/pages/{id}/measurements",
|
|
1282
|
+
tag: "Takeoff",
|
|
1283
|
+
summary: "List measurements on a page",
|
|
1284
|
+
body: false,
|
|
1285
|
+
response: "json",
|
|
1286
|
+
params: [
|
|
1287
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex page ID" }
|
|
1288
|
+
]
|
|
1289
|
+
},
|
|
1290
|
+
list_measurements_by_symbol: {
|
|
1291
|
+
method: "GET",
|
|
1292
|
+
path: "/symbols/{id}/measurements",
|
|
1293
|
+
tag: "Takeoff",
|
|
1294
|
+
summary: "List measurements for a symbol",
|
|
1295
|
+
body: false,
|
|
1296
|
+
response: "json",
|
|
1297
|
+
params: [
|
|
1298
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex takeoff symbol ID" },
|
|
1299
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
1300
|
+
]
|
|
1301
|
+
},
|
|
1302
|
+
list_messages: {
|
|
1303
|
+
method: "GET",
|
|
1304
|
+
path: "/projects/{id}/messages",
|
|
1305
|
+
tag: "Messages",
|
|
1306
|
+
summary: "List project activity messages",
|
|
1307
|
+
body: false,
|
|
1308
|
+
response: "json",
|
|
1309
|
+
params: [
|
|
1310
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
1311
|
+
{ name: "pageId", in: "query", required: false, kind: "string" }
|
|
1312
|
+
]
|
|
1313
|
+
},
|
|
1314
|
+
list_notifications: {
|
|
1315
|
+
method: "GET",
|
|
1316
|
+
path: "/notifications",
|
|
1317
|
+
tag: "Notifications",
|
|
1318
|
+
summary: "List notifications for the caller",
|
|
1319
|
+
body: false,
|
|
1320
|
+
response: "json",
|
|
1321
|
+
params: [
|
|
1322
|
+
{ name: "limit", in: "query", required: false, kind: "integer" },
|
|
1323
|
+
{ name: "cursor", in: "query", required: false, kind: "string" }
|
|
1324
|
+
]
|
|
1325
|
+
},
|
|
1326
|
+
list_org_documents: {
|
|
1327
|
+
method: "GET",
|
|
1328
|
+
path: "/documents",
|
|
1329
|
+
tag: "Documents",
|
|
1330
|
+
summary: "List documents in the organization",
|
|
1331
|
+
body: false,
|
|
1332
|
+
response: "json",
|
|
1333
|
+
params: [
|
|
1334
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1335
|
+
{ name: "type", in: "query", required: false, kind: "string" },
|
|
1336
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1337
|
+
]
|
|
1338
|
+
},
|
|
1339
|
+
list_organizations: {
|
|
1340
|
+
method: "GET",
|
|
1341
|
+
path: "/organizations",
|
|
1342
|
+
tag: "Account",
|
|
1343
|
+
summary: "List the companies your app's company owns",
|
|
1344
|
+
body: false,
|
|
1345
|
+
response: "json",
|
|
1346
|
+
params: [
|
|
1347
|
+
{ name: "limit", in: "query", required: false, kind: "integer", description: "Companies per page (1 to 100, default 50)." },
|
|
1348
|
+
{ name: "cursor", in: "query", required: false, kind: "string", description: "`nextCursor` from the previous page." }
|
|
1349
|
+
]
|
|
1350
|
+
},
|
|
1351
|
+
list_pages: {
|
|
1352
|
+
method: "GET",
|
|
1353
|
+
path: "/projects/{id}/pages",
|
|
1354
|
+
tag: "Pages",
|
|
1355
|
+
summary: "List pages in a project, grouped by file",
|
|
1356
|
+
body: false,
|
|
1357
|
+
response: "json",
|
|
1358
|
+
params: [
|
|
1359
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
1360
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1361
|
+
]
|
|
1362
|
+
},
|
|
1363
|
+
list_products: {
|
|
1364
|
+
method: "GET",
|
|
1365
|
+
path: "/products",
|
|
1366
|
+
tag: "Products",
|
|
1367
|
+
summary: "List products in an organization",
|
|
1368
|
+
body: false,
|
|
1369
|
+
response: "json",
|
|
1370
|
+
params: [
|
|
1371
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1372
|
+
{ name: "search", in: "query", required: false, kind: "string" },
|
|
1373
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1374
|
+
]
|
|
1375
|
+
},
|
|
1376
|
+
list_products_in_list: {
|
|
1377
|
+
method: "GET",
|
|
1378
|
+
path: "/lists/{id}/products",
|
|
1379
|
+
tag: "Products",
|
|
1380
|
+
summary: "List products in a list",
|
|
1381
|
+
body: false,
|
|
1382
|
+
response: "json",
|
|
1383
|
+
params: [
|
|
1384
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex product list ID" },
|
|
1385
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1386
|
+
]
|
|
1387
|
+
},
|
|
1388
|
+
list_project_conversations: {
|
|
1389
|
+
method: "GET",
|
|
1390
|
+
path: "/projects/{id}/conversations",
|
|
1391
|
+
tag: "Conversations",
|
|
1392
|
+
summary: "List conversations in a project",
|
|
1393
|
+
body: false,
|
|
1394
|
+
response: "json",
|
|
1395
|
+
params: [
|
|
1396
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
1397
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1398
|
+
{ name: "type", in: "query", required: false, kind: "string", enum: ["ai_chat", "project_activity", "widget_chat"] }
|
|
1399
|
+
]
|
|
1400
|
+
},
|
|
1401
|
+
list_projects: {
|
|
1402
|
+
method: "GET",
|
|
1403
|
+
path: "/projects",
|
|
1404
|
+
tag: "Projects",
|
|
1405
|
+
summary: "List projects in an organization",
|
|
1406
|
+
body: false,
|
|
1407
|
+
response: "json",
|
|
1408
|
+
params: [
|
|
1409
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1410
|
+
{ name: "status", in: "query", required: false, kind: "string", enum: ["lead", "contacted", "qualified", "active", "archived"] },
|
|
1411
|
+
{ name: "search", in: "query", required: false, kind: "string" }
|
|
1412
|
+
]
|
|
1413
|
+
},
|
|
1414
|
+
list_spreadsheets: {
|
|
1415
|
+
method: "GET",
|
|
1416
|
+
path: "/projects/{id}/spreadsheets",
|
|
1417
|
+
tag: "Spreadsheets",
|
|
1418
|
+
summary: "List spreadsheets in a project",
|
|
1419
|
+
body: false,
|
|
1420
|
+
response: "json",
|
|
1421
|
+
params: [
|
|
1422
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
1423
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1424
|
+
]
|
|
1425
|
+
},
|
|
1426
|
+
list_symbol_line_items: {
|
|
1427
|
+
method: "GET",
|
|
1428
|
+
path: "/symbols/{id}/line-items",
|
|
1429
|
+
tag: "Takeoff",
|
|
1430
|
+
summary: "List all line items on a takeoff symbol",
|
|
1431
|
+
body: false,
|
|
1432
|
+
response: "json",
|
|
1433
|
+
params: [
|
|
1434
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex takeoff symbol ID" },
|
|
1435
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
1436
|
+
]
|
|
1437
|
+
},
|
|
1438
|
+
list_symbols: {
|
|
1439
|
+
method: "GET",
|
|
1440
|
+
path: "/projects/{id}/symbols",
|
|
1441
|
+
tag: "Takeoff",
|
|
1442
|
+
summary: "List takeoff symbols in a project",
|
|
1443
|
+
body: false,
|
|
1444
|
+
response: "json",
|
|
1445
|
+
params: [
|
|
1446
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
1447
|
+
{ name: "type", in: "query", required: false, kind: "string", enum: ["count", "linear", "area"] },
|
|
1448
|
+
{ name: "source", in: "query", required: false, kind: "string", enum: ["ai", "manual"] },
|
|
1449
|
+
{ name: "category", in: "query", required: false, kind: "string" }
|
|
1450
|
+
]
|
|
1451
|
+
},
|
|
1452
|
+
list_vendor_quotes: {
|
|
1453
|
+
method: "GET",
|
|
1454
|
+
path: "/projects/{id}/quotes",
|
|
1455
|
+
tag: "Vendor quotes",
|
|
1456
|
+
summary: "List vendor quotes on a project",
|
|
1457
|
+
body: false,
|
|
1458
|
+
response: "json",
|
|
1459
|
+
params: [
|
|
1460
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
1461
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1462
|
+
{ name: "status", in: "query", required: false, kind: "string", enum: ["requested", "received", "accepted", "rejected", "expired"] }
|
|
1463
|
+
]
|
|
1464
|
+
},
|
|
1465
|
+
list_vendors: {
|
|
1466
|
+
method: "GET",
|
|
1467
|
+
path: "/vendors",
|
|
1468
|
+
tag: "Vendors",
|
|
1469
|
+
summary: "List vendors in an organization",
|
|
1470
|
+
body: false,
|
|
1471
|
+
response: "json",
|
|
1472
|
+
params: [
|
|
1473
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1474
|
+
{ name: "type", in: "query", required: false, kind: "string", enum: ["supplier", "subcontractor", "rental"] },
|
|
1475
|
+
{ name: "kind", in: "query", required: false, kind: "string", enum: ["sub", "supplier", "surety", "insurer", "agency", "owner", "architect", "engineer", "other"] },
|
|
1476
|
+
{ name: "preferredOnly", in: "query", required: false, kind: "boolean" },
|
|
1477
|
+
{ name: "search", in: "query", required: false, kind: "string" },
|
|
1478
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1479
|
+
]
|
|
1480
|
+
},
|
|
1481
|
+
list_webhook_deliveries: {
|
|
1482
|
+
method: "GET",
|
|
1483
|
+
path: "/webhook_endpoints/{id}/deliveries",
|
|
1484
|
+
tag: "Webhooks",
|
|
1485
|
+
summary: "List deliveries for a webhook endpoint",
|
|
1486
|
+
body: false,
|
|
1487
|
+
response: "json",
|
|
1488
|
+
params: [
|
|
1489
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1490
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1491
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1492
|
+
]
|
|
1493
|
+
},
|
|
1494
|
+
list_webhook_endpoints: {
|
|
1495
|
+
method: "GET",
|
|
1496
|
+
path: "/webhook_endpoints",
|
|
1497
|
+
tag: "Webhooks",
|
|
1498
|
+
summary: "List webhook endpoints in an organization",
|
|
1499
|
+
body: false,
|
|
1500
|
+
response: "json",
|
|
1501
|
+
params: [
|
|
1502
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1503
|
+
]
|
|
1504
|
+
},
|
|
1505
|
+
list_widget_conversations: {
|
|
1506
|
+
method: "GET",
|
|
1507
|
+
path: "/widgets/{id}/conversations",
|
|
1508
|
+
tag: "Conversations",
|
|
1509
|
+
summary: "List a widget\u2019s inbox conversations",
|
|
1510
|
+
body: false,
|
|
1511
|
+
response: "json",
|
|
1512
|
+
params: [
|
|
1513
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex widget ID" },
|
|
1514
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1515
|
+
{ name: "status", in: "query", required: false, kind: "string", enum: ["bot", "open", "snoozed", "closed"] },
|
|
1516
|
+
{ name: "search", in: "query", required: false, kind: "string" },
|
|
1517
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1518
|
+
]
|
|
1519
|
+
},
|
|
1520
|
+
list_widgets: {
|
|
1521
|
+
method: "GET",
|
|
1522
|
+
path: "/widgets",
|
|
1523
|
+
tag: "Widgets",
|
|
1524
|
+
summary: "List widgets in an organization",
|
|
1525
|
+
body: false,
|
|
1526
|
+
response: "json",
|
|
1527
|
+
params: [
|
|
1528
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1529
|
+
]
|
|
1530
|
+
},
|
|
1531
|
+
mark_all_notifications_read: {
|
|
1532
|
+
method: "POST",
|
|
1533
|
+
path: "/notifications/mark-all-read",
|
|
1534
|
+
tag: "Notifications",
|
|
1535
|
+
summary: "Mark all notifications as read",
|
|
1536
|
+
body: false,
|
|
1537
|
+
response: "empty",
|
|
1538
|
+
params: []
|
|
1539
|
+
},
|
|
1540
|
+
mark_notification_read: {
|
|
1541
|
+
method: "POST",
|
|
1542
|
+
path: "/notifications/{id}/read",
|
|
1543
|
+
tag: "Notifications",
|
|
1544
|
+
summary: "Mark a notification as read",
|
|
1545
|
+
body: false,
|
|
1546
|
+
response: "empty",
|
|
1547
|
+
params: [
|
|
1548
|
+
{ name: "id", in: "path", required: true, kind: "string" }
|
|
1549
|
+
]
|
|
1550
|
+
},
|
|
1551
|
+
patch_symbol_line_item: {
|
|
1552
|
+
method: "PATCH",
|
|
1553
|
+
path: "/symbols/{symbolId}/line-items/{id}",
|
|
1554
|
+
tag: "Takeoff",
|
|
1555
|
+
summary: "Update a line item on a symbol",
|
|
1556
|
+
body: true,
|
|
1557
|
+
response: "empty",
|
|
1558
|
+
params: [
|
|
1559
|
+
{ name: "symbolId", in: "path", required: true, kind: "string", description: "Convex takeoff symbol ID" },
|
|
1560
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1561
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Convex project ID" },
|
|
1562
|
+
{ name: "patch", in: "body", required: true, kind: "object", description: "Only the fields to change" }
|
|
1563
|
+
]
|
|
1564
|
+
},
|
|
1565
|
+
presign_file_upload: {
|
|
1566
|
+
method: "POST",
|
|
1567
|
+
path: "/files",
|
|
1568
|
+
tag: "Files",
|
|
1569
|
+
summary: "Start a file upload \u2014 returns a presigned R2 URL",
|
|
1570
|
+
body: true,
|
|
1571
|
+
response: "json",
|
|
1572
|
+
params: [
|
|
1573
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Convex project ID" },
|
|
1574
|
+
{ name: "filename", in: "body", required: true, kind: "string" },
|
|
1575
|
+
{ name: "mimeType", in: "body", required: true, kind: "string" },
|
|
1576
|
+
{ name: "fileSize", in: "body", required: true, kind: "integer", description: "File size in bytes (max 500 MB)" },
|
|
1577
|
+
{ name: "folderId", in: "body", required: false, kind: "string", description: "Convex folder ID" },
|
|
1578
|
+
{ name: "dimensions", in: "body", required: false, kind: "object" }
|
|
1579
|
+
]
|
|
1580
|
+
},
|
|
1581
|
+
propose_analysis: {
|
|
1582
|
+
method: "POST",
|
|
1583
|
+
path: "/projects/{id}/analysis/propose",
|
|
1584
|
+
tag: "Takeoff",
|
|
1585
|
+
summary: "Propose an AI-generated analysis from a natural-language prompt",
|
|
1586
|
+
body: true,
|
|
1587
|
+
response: "json",
|
|
1588
|
+
params: [
|
|
1589
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
1590
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1591
|
+
{ name: "prompt", in: "body", required: true, kind: "string", description: "Natural-language description of what to measure" },
|
|
1592
|
+
{ name: "pageIds", in: "body", required: false, kind: "array", description: "Page IDs to propose against. Defaults to the project's first page." },
|
|
1593
|
+
{ name: "fileIds", in: "body", required: false, kind: "array", description: "File IDs to scope the proposal to. When set, only pages belonging to these files are considered \u2014 pass the IDs from the [Attached files: ...] note in the user's message so a fresh upload doesn't pull in older project files. Wins over pageIds." }
|
|
1594
|
+
]
|
|
1595
|
+
},
|
|
1596
|
+
provision_cma: {
|
|
1597
|
+
method: "POST",
|
|
1598
|
+
path: "/cma/provision",
|
|
1599
|
+
tag: "Account",
|
|
1600
|
+
summary: "Provision or rotate the caller\u2019s Managed Agents vault",
|
|
1601
|
+
body: false,
|
|
1602
|
+
response: "json",
|
|
1603
|
+
params: []
|
|
1604
|
+
},
|
|
1605
|
+
receive_vendor_quote: {
|
|
1606
|
+
method: "PATCH",
|
|
1607
|
+
path: "/quotes/{id}/receive",
|
|
1608
|
+
tag: "Vendor quotes",
|
|
1609
|
+
summary: "Record a vendor quote response",
|
|
1610
|
+
body: true,
|
|
1611
|
+
response: "json",
|
|
1612
|
+
params: [
|
|
1613
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1614
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1615
|
+
{ name: "lineItems", in: "body", required: true, kind: "array" },
|
|
1616
|
+
{ name: "subtotal", in: "body", required: true, kind: "number", description: "Sum of the line items' totalPrice when lineItems are sent" },
|
|
1617
|
+
{ name: "tax", in: "body", required: false, kind: "number" },
|
|
1618
|
+
{ name: "shipping", in: "body", required: false, kind: "number" },
|
|
1619
|
+
{ name: "total", in: "body", required: true, kind: "number", description: "Must equal subtotal + tax + shipping (an absent amount counts as 0)" },
|
|
1620
|
+
{ name: "validUntil", in: "body", required: false, kind: "number" }
|
|
1621
|
+
]
|
|
1622
|
+
},
|
|
1623
|
+
remove_custom_property: {
|
|
1624
|
+
method: "DELETE",
|
|
1625
|
+
path: "/custom-properties/{id}",
|
|
1626
|
+
tag: "Custom properties",
|
|
1627
|
+
summary: "Archive a custom property",
|
|
1628
|
+
body: false,
|
|
1629
|
+
response: "empty",
|
|
1630
|
+
params: [
|
|
1631
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex custom property ID" },
|
|
1632
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1633
|
+
]
|
|
1634
|
+
},
|
|
1635
|
+
remove_file: {
|
|
1636
|
+
method: "DELETE",
|
|
1637
|
+
path: "/files/{id}",
|
|
1638
|
+
tag: "Files",
|
|
1639
|
+
summary: "Soft-delete a file",
|
|
1640
|
+
body: false,
|
|
1641
|
+
response: "empty",
|
|
1642
|
+
params: [
|
|
1643
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex file ID" }
|
|
1644
|
+
]
|
|
1645
|
+
},
|
|
1646
|
+
remove_folder: {
|
|
1647
|
+
method: "DELETE",
|
|
1648
|
+
path: "/folders/{id}",
|
|
1649
|
+
tag: "Folders",
|
|
1650
|
+
summary: "Delete a folder",
|
|
1651
|
+
body: false,
|
|
1652
|
+
response: "empty",
|
|
1653
|
+
params: [
|
|
1654
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex folder ID" }
|
|
1655
|
+
]
|
|
1656
|
+
},
|
|
1657
|
+
remove_group: {
|
|
1658
|
+
method: "DELETE",
|
|
1659
|
+
path: "/groups/{id}",
|
|
1660
|
+
tag: "Takeoff",
|
|
1661
|
+
summary: "Delete a takeoff group",
|
|
1662
|
+
body: false,
|
|
1663
|
+
response: "empty",
|
|
1664
|
+
params: [
|
|
1665
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex takeoff group ID" },
|
|
1666
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
1667
|
+
]
|
|
1668
|
+
},
|
|
1669
|
+
remove_layer: {
|
|
1670
|
+
method: "DELETE",
|
|
1671
|
+
path: "/layers/{id}",
|
|
1672
|
+
tag: "Takeoff",
|
|
1673
|
+
summary: "Delete a takeoff layer",
|
|
1674
|
+
body: false,
|
|
1675
|
+
response: "empty",
|
|
1676
|
+
params: [
|
|
1677
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex takeoff layer ID" },
|
|
1678
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
1679
|
+
]
|
|
1680
|
+
},
|
|
1681
|
+
remove_measurement: {
|
|
1682
|
+
method: "DELETE",
|
|
1683
|
+
path: "/pages/{pageId}/measurements/{shapeId}",
|
|
1684
|
+
tag: "Takeoff",
|
|
1685
|
+
summary: "Delete a measurement by shape ID",
|
|
1686
|
+
body: false,
|
|
1687
|
+
response: "empty",
|
|
1688
|
+
params: [
|
|
1689
|
+
{ name: "pageId", in: "path", required: true, kind: "string", description: "Convex page ID" },
|
|
1690
|
+
{ name: "shapeId", in: "path", required: true, kind: "string" }
|
|
1691
|
+
]
|
|
1692
|
+
},
|
|
1693
|
+
remove_message: {
|
|
1694
|
+
method: "DELETE",
|
|
1695
|
+
path: "/messages/{id}",
|
|
1696
|
+
tag: "Messages",
|
|
1697
|
+
summary: "Delete a message",
|
|
1698
|
+
body: false,
|
|
1699
|
+
response: "empty",
|
|
1700
|
+
params: [
|
|
1701
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex chat message ID" }
|
|
1702
|
+
]
|
|
1703
|
+
},
|
|
1704
|
+
remove_symbol: {
|
|
1705
|
+
method: "DELETE",
|
|
1706
|
+
path: "/symbols/{id}",
|
|
1707
|
+
tag: "Takeoff",
|
|
1708
|
+
summary: "Delete a takeoff symbol",
|
|
1709
|
+
body: false,
|
|
1710
|
+
response: "empty",
|
|
1711
|
+
params: [
|
|
1712
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex takeoff symbol ID" },
|
|
1713
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
1714
|
+
]
|
|
1715
|
+
},
|
|
1716
|
+
remove_symbol_line_item: {
|
|
1717
|
+
method: "DELETE",
|
|
1718
|
+
path: "/symbols/{symbolId}/line-items/{id}",
|
|
1719
|
+
tag: "Takeoff",
|
|
1720
|
+
summary: "Remove a line item from a symbol",
|
|
1721
|
+
body: false,
|
|
1722
|
+
response: "empty",
|
|
1723
|
+
params: [
|
|
1724
|
+
{ name: "symbolId", in: "path", required: true, kind: "string", description: "Convex takeoff symbol ID" },
|
|
1725
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1726
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
1727
|
+
]
|
|
1728
|
+
},
|
|
1729
|
+
remove_vendor_price: {
|
|
1730
|
+
method: "DELETE",
|
|
1731
|
+
path: "/products/{productId}/prices/{vendorId}",
|
|
1732
|
+
tag: "Vendor prices",
|
|
1733
|
+
summary: "Remove a vendor price",
|
|
1734
|
+
body: false,
|
|
1735
|
+
response: "empty",
|
|
1736
|
+
params: [
|
|
1737
|
+
{ name: "productId", in: "path", required: true, kind: "string", description: "Convex product ID" },
|
|
1738
|
+
{ name: "vendorId", in: "path", required: true, kind: "string", description: "Convex vendor ID" },
|
|
1739
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1740
|
+
]
|
|
1741
|
+
},
|
|
1742
|
+
remove_widget: {
|
|
1743
|
+
method: "DELETE",
|
|
1744
|
+
path: "/widgets/{id}",
|
|
1745
|
+
tag: "Widgets",
|
|
1746
|
+
summary: "Delete a widget",
|
|
1747
|
+
body: false,
|
|
1748
|
+
response: "empty",
|
|
1749
|
+
params: [
|
|
1750
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex widget ID" },
|
|
1751
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1752
|
+
]
|
|
1753
|
+
},
|
|
1754
|
+
rename_file: {
|
|
1755
|
+
method: "PATCH",
|
|
1756
|
+
path: "/files/{id}",
|
|
1757
|
+
tag: "Files",
|
|
1758
|
+
summary: "Rename or move a file",
|
|
1759
|
+
body: true,
|
|
1760
|
+
response: "empty",
|
|
1761
|
+
params: [
|
|
1762
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex file ID" },
|
|
1763
|
+
{ name: "filename", in: "body", required: false, kind: "string" },
|
|
1764
|
+
{ name: "folderId", in: "body", required: false, kind: "string", description: "Convex folder ID" }
|
|
1765
|
+
]
|
|
1766
|
+
},
|
|
1767
|
+
rename_folder: {
|
|
1768
|
+
method: "PATCH",
|
|
1769
|
+
path: "/folders/{id}",
|
|
1770
|
+
tag: "Folders",
|
|
1771
|
+
summary: "Rename or move a folder",
|
|
1772
|
+
body: true,
|
|
1773
|
+
response: "empty",
|
|
1774
|
+
params: [
|
|
1775
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex folder ID" },
|
|
1776
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
1777
|
+
{ name: "parentId", in: "body", required: false, kind: "string", description: "Convex folder ID" }
|
|
1778
|
+
]
|
|
1779
|
+
},
|
|
1780
|
+
resend_webhook_delivery: {
|
|
1781
|
+
method: "POST",
|
|
1782
|
+
path: "/webhook_deliveries/{id}/resend",
|
|
1783
|
+
tag: "Webhooks",
|
|
1784
|
+
summary: "Resend a webhook delivery",
|
|
1785
|
+
body: true,
|
|
1786
|
+
response: "json",
|
|
1787
|
+
params: [
|
|
1788
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1789
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1790
|
+
]
|
|
1791
|
+
},
|
|
1792
|
+
restore_list: {
|
|
1793
|
+
method: "POST",
|
|
1794
|
+
path: "/lists/{id}/restore",
|
|
1795
|
+
tag: "Lists",
|
|
1796
|
+
summary: "Restore an archived list",
|
|
1797
|
+
body: true,
|
|
1798
|
+
response: "empty",
|
|
1799
|
+
params: [
|
|
1800
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex product list ID" },
|
|
1801
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1802
|
+
]
|
|
1803
|
+
},
|
|
1804
|
+
restore_page: {
|
|
1805
|
+
method: "POST",
|
|
1806
|
+
path: "/pages/{id}/restore",
|
|
1807
|
+
tag: "Pages",
|
|
1808
|
+
summary: "Restore an archived page",
|
|
1809
|
+
body: false,
|
|
1810
|
+
response: "empty",
|
|
1811
|
+
params: [
|
|
1812
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex page ID" }
|
|
1813
|
+
]
|
|
1814
|
+
},
|
|
1815
|
+
restore_project: {
|
|
1816
|
+
method: "POST",
|
|
1817
|
+
path: "/projects/{id}/restore",
|
|
1818
|
+
tag: "Projects",
|
|
1819
|
+
summary: "Restore an archived project",
|
|
1820
|
+
body: true,
|
|
1821
|
+
response: "empty",
|
|
1822
|
+
params: [
|
|
1823
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
1824
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
1825
|
+
]
|
|
1826
|
+
},
|
|
1827
|
+
run_approved_analysis: {
|
|
1828
|
+
method: "POST",
|
|
1829
|
+
path: "/projects/{id}/analysis/run",
|
|
1830
|
+
tag: "Takeoff",
|
|
1831
|
+
summary: "Run an approved analysis (kicks off a background AI task)",
|
|
1832
|
+
confirm: "This operation uses AI usage.",
|
|
1833
|
+
body: true,
|
|
1834
|
+
response: "json",
|
|
1835
|
+
params: [
|
|
1836
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
1837
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1838
|
+
{ name: "pageIds", in: "body", required: true, kind: "array" },
|
|
1839
|
+
{ name: "elements", in: "body", required: true, kind: "array" },
|
|
1840
|
+
{ name: "layerName", in: "body", required: false, kind: "string" }
|
|
1841
|
+
]
|
|
1842
|
+
},
|
|
1843
|
+
search_help_articles: {
|
|
1844
|
+
method: "POST",
|
|
1845
|
+
path: "/help/search",
|
|
1846
|
+
tag: "Help",
|
|
1847
|
+
summary: "Search help articles by keyword",
|
|
1848
|
+
body: true,
|
|
1849
|
+
response: "json",
|
|
1850
|
+
params: [
|
|
1851
|
+
{ name: "query", in: "body", required: true, kind: "string" },
|
|
1852
|
+
{ name: "limit", in: "body", required: false, kind: "integer" },
|
|
1853
|
+
{ name: "section", in: "body", required: false, kind: "string" }
|
|
1854
|
+
]
|
|
1855
|
+
},
|
|
1856
|
+
search_products: {
|
|
1857
|
+
method: "GET",
|
|
1858
|
+
path: "/products/search",
|
|
1859
|
+
tag: "Products",
|
|
1860
|
+
summary: "Full-text search products",
|
|
1861
|
+
body: false,
|
|
1862
|
+
response: "json",
|
|
1863
|
+
params: [
|
|
1864
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1865
|
+
{ name: "query", in: "query", required: true, kind: "string" },
|
|
1866
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1867
|
+
]
|
|
1868
|
+
},
|
|
1869
|
+
search_project_files: {
|
|
1870
|
+
method: "GET",
|
|
1871
|
+
path: "/projects/{id}/files/search",
|
|
1872
|
+
tag: "Files",
|
|
1873
|
+
summary: "Search pages in a project by name or indexed content",
|
|
1874
|
+
body: false,
|
|
1875
|
+
response: "json",
|
|
1876
|
+
params: [
|
|
1877
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
1878
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1879
|
+
{ name: "query", in: "query", required: false, kind: "string" }
|
|
1880
|
+
]
|
|
1881
|
+
},
|
|
1882
|
+
search_project_line_items: {
|
|
1883
|
+
method: "GET",
|
|
1884
|
+
path: "/projects/{id}/line-items",
|
|
1885
|
+
tag: "Takeoff",
|
|
1886
|
+
summary: "Search line items across all symbols in a project",
|
|
1887
|
+
body: false,
|
|
1888
|
+
response: "json",
|
|
1889
|
+
params: [
|
|
1890
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
1891
|
+
{ name: "query", in: "query", required: false, kind: "string" },
|
|
1892
|
+
{ name: "takeoffType", in: "query", required: false, kind: "string", enum: ["count", "linear", "area"] },
|
|
1893
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1894
|
+
]
|
|
1895
|
+
},
|
|
1896
|
+
search_vendors: {
|
|
1897
|
+
method: "GET",
|
|
1898
|
+
path: "/vendors/search",
|
|
1899
|
+
tag: "Vendors",
|
|
1900
|
+
summary: "Full-text search vendors",
|
|
1901
|
+
body: false,
|
|
1902
|
+
response: "json",
|
|
1903
|
+
params: [
|
|
1904
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1905
|
+
{ name: "query", in: "query", required: true, kind: "string" },
|
|
1906
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1907
|
+
]
|
|
1908
|
+
},
|
|
1909
|
+
send_cma_message: {
|
|
1910
|
+
method: "POST",
|
|
1911
|
+
path: "/cma/chat",
|
|
1912
|
+
tag: "Account",
|
|
1913
|
+
summary: "Send a message to the Managed Agent and return its reply",
|
|
1914
|
+
body: true,
|
|
1915
|
+
response: "json",
|
|
1916
|
+
params: [
|
|
1917
|
+
{ name: "message", in: "body", required: true, kind: "string" },
|
|
1918
|
+
{ name: "sessionId", in: "body", required: false, kind: "string" },
|
|
1919
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "The organization this message is for and is billed to (memberships[].orgId in GET /v1/me). You must be a member of it; on the session of one of your Exayard conversations it must be that conversation's organization. Required unless sessionId is the session of one of your Exayard conversations (else 400). An organization without enough credits is answered 402." }
|
|
1920
|
+
]
|
|
1921
|
+
},
|
|
1922
|
+
send_webhook_listener_test: {
|
|
1923
|
+
method: "POST",
|
|
1924
|
+
path: "/webhook_listeners/{id}/test",
|
|
1925
|
+
tag: "Webhooks",
|
|
1926
|
+
summary: "Send a test event to a webhook listener",
|
|
1927
|
+
body: true,
|
|
1928
|
+
response: "json",
|
|
1929
|
+
params: [
|
|
1930
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1931
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1932
|
+
{ name: "type", in: "body", required: true, kind: "string", enum: ["project.created", "project.updated", "project.archived", "assessment.started", "assessment.completed", "assessment.cancelled", "assessment.failed", "estimate.generated", "bid.generated", "bid.submitted", "bid.won", "bid.lost", "bid.accepted", "bid.declined", "bid.sent", "bid.viewed", "bid.commented", "file.processed", "addendum.uploaded", "quote.requested", "quote.received", "quote.accepted", "quote.rejected", "quote.expired", "conversation.started", "conversation.handoff_requested", "conversation.closed", "lead.captured", "contact.created", "appointment.booked", "appointment.rescheduled", "appointment.cancelled", "call.completed", "call.missed", "review.requested", "job.written_back", "payment.captured", "payment.failed"] }
|
|
1933
|
+
]
|
|
1934
|
+
},
|
|
1935
|
+
send_webhook_test_event: {
|
|
1936
|
+
method: "POST",
|
|
1937
|
+
path: "/webhook_endpoints/{id}/test",
|
|
1938
|
+
tag: "Webhooks",
|
|
1939
|
+
summary: "Send a test event to a webhook endpoint",
|
|
1940
|
+
body: true,
|
|
1941
|
+
response: "json",
|
|
1942
|
+
params: [
|
|
1943
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1944
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1945
|
+
{ name: "type", in: "body", required: true, kind: "string", enum: ["project.created", "project.updated", "project.archived", "assessment.started", "assessment.completed", "assessment.cancelled", "assessment.failed", "estimate.generated", "bid.generated", "bid.submitted", "bid.won", "bid.lost", "bid.accepted", "bid.declined", "bid.sent", "bid.viewed", "bid.commented", "file.processed", "addendum.uploaded", "quote.requested", "quote.received", "quote.accepted", "quote.rejected", "quote.expired", "conversation.started", "conversation.handoff_requested", "conversation.closed", "lead.captured", "contact.created", "appointment.booked", "appointment.rescheduled", "appointment.cancelled", "call.completed", "call.missed", "review.requested", "job.written_back", "payment.captured", "payment.failed"] }
|
|
1946
|
+
]
|
|
1947
|
+
},
|
|
1948
|
+
set_document_sharing: {
|
|
1949
|
+
method: "PATCH",
|
|
1950
|
+
path: "/documents/{id}/share",
|
|
1951
|
+
tag: "Documents",
|
|
1952
|
+
summary: "Turn document sharing on or off",
|
|
1953
|
+
body: true,
|
|
1954
|
+
response: "json",
|
|
1955
|
+
params: [
|
|
1956
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex document ID" },
|
|
1957
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1958
|
+
{ name: "enabled", in: "body", required: true, kind: "boolean", description: "true mints (or reuses) the public share link and turns sharing on; false revokes access while keeping the secret for later re-enabling." }
|
|
1959
|
+
]
|
|
1960
|
+
},
|
|
1961
|
+
set_vendor_price: {
|
|
1962
|
+
method: "PUT",
|
|
1963
|
+
path: "/products/{productId}/prices/{vendorId}",
|
|
1964
|
+
tag: "Vendor prices",
|
|
1965
|
+
summary: "Create or update a vendor price",
|
|
1966
|
+
body: true,
|
|
1967
|
+
response: "json",
|
|
1968
|
+
params: [
|
|
1969
|
+
{ name: "productId", in: "path", required: true, kind: "string", description: "Convex product ID" },
|
|
1970
|
+
{ name: "vendorId", in: "path", required: true, kind: "string", description: "Convex vendor ID" },
|
|
1971
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1972
|
+
{ name: "pricePerUnit", in: "body", required: true, kind: "number" },
|
|
1973
|
+
{ name: "minOrderQuantity", in: "body", required: false, kind: "number" },
|
|
1974
|
+
{ name: "volumeDiscounts", in: "body", required: false, kind: "array" },
|
|
1975
|
+
{ name: "leadTimeDays", in: "body", required: false, kind: "integer" },
|
|
1976
|
+
{ name: "expirationDate", in: "body", required: false, kind: "number" },
|
|
1977
|
+
{ name: "notes", in: "body", required: false, kind: "string" }
|
|
1978
|
+
]
|
|
1979
|
+
},
|
|
1980
|
+
start_webhook_listener: {
|
|
1981
|
+
method: "POST",
|
|
1982
|
+
path: "/webhook_listeners",
|
|
1983
|
+
tag: "Webhooks",
|
|
1984
|
+
summary: "Start a webhook listener (exayard listen)",
|
|
1985
|
+
body: true,
|
|
1986
|
+
response: "json",
|
|
1987
|
+
params: [
|
|
1988
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
1989
|
+
{ name: "events", in: "body", required: false, kind: "array" },
|
|
1990
|
+
{ name: "name", in: "body", required: false, kind: "string" }
|
|
1991
|
+
]
|
|
1992
|
+
},
|
|
1993
|
+
stop_webhook_listener: {
|
|
1994
|
+
method: "DELETE",
|
|
1995
|
+
path: "/webhook_listeners/{id}",
|
|
1996
|
+
tag: "Webhooks",
|
|
1997
|
+
summary: "Stop a webhook listener",
|
|
1998
|
+
body: false,
|
|
1999
|
+
response: "empty",
|
|
2000
|
+
params: [
|
|
2001
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
2002
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." }
|
|
2003
|
+
]
|
|
2004
|
+
},
|
|
2005
|
+
stream_webhook_listener_events: {
|
|
2006
|
+
method: "GET",
|
|
2007
|
+
path: "/webhook_listeners/{id}/events",
|
|
2008
|
+
tag: "Webhooks",
|
|
2009
|
+
summary: "Stream a webhook listener's events",
|
|
2010
|
+
body: false,
|
|
2011
|
+
response: "stream",
|
|
2012
|
+
params: [
|
|
2013
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
2014
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2015
|
+
{ name: "after", in: "query", required: false, kind: "integer" }
|
|
2016
|
+
]
|
|
2017
|
+
},
|
|
2018
|
+
update_contact: {
|
|
2019
|
+
method: "PATCH",
|
|
2020
|
+
path: "/contacts/{id}",
|
|
2021
|
+
tag: "Contacts",
|
|
2022
|
+
summary: "Update a contact\u2019s details or CRM attributes",
|
|
2023
|
+
body: true,
|
|
2024
|
+
response: "empty",
|
|
2025
|
+
params: [
|
|
2026
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex contact ID" },
|
|
2027
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2028
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2029
|
+
{ name: "email", in: "body", required: false, kind: "string" },
|
|
2030
|
+
{ name: "phone", in: "body", required: false, kind: "string" },
|
|
2031
|
+
{ name: "attributes", in: "body", required: false, kind: "object" }
|
|
2032
|
+
]
|
|
2033
|
+
},
|
|
2034
|
+
update_conversation_title: {
|
|
2035
|
+
method: "PATCH",
|
|
2036
|
+
path: "/conversations/{id}",
|
|
2037
|
+
tag: "Conversations",
|
|
2038
|
+
summary: "Update conversation title or linked project",
|
|
2039
|
+
body: true,
|
|
2040
|
+
response: "empty",
|
|
2041
|
+
params: [
|
|
2042
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex conversation ID" },
|
|
2043
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2044
|
+
{ name: "title", in: "body", required: false, kind: "string" },
|
|
2045
|
+
{ name: "projectId", in: "body", required: false, kind: "string", description: "Convex project ID" }
|
|
2046
|
+
]
|
|
2047
|
+
},
|
|
2048
|
+
update_custom_property: {
|
|
2049
|
+
method: "PATCH",
|
|
2050
|
+
path: "/custom-properties/{id}",
|
|
2051
|
+
tag: "Custom properties",
|
|
2052
|
+
summary: "Update a custom property",
|
|
2053
|
+
body: true,
|
|
2054
|
+
response: "empty",
|
|
2055
|
+
params: [
|
|
2056
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex custom property ID" },
|
|
2057
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2058
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2059
|
+
{ name: "type", in: "body", required: false, kind: "string", enum: ["text", "textarea", "number", "date", "checkbox", "multiCheckbox", "select", "url", "email", "phone", "currency", "percentage"] },
|
|
2060
|
+
{ name: "recordTypes", in: "body", required: false, kind: "array" },
|
|
2061
|
+
{ name: "options", in: "body", required: false, kind: "array" }
|
|
2062
|
+
]
|
|
2063
|
+
},
|
|
2064
|
+
update_group: {
|
|
2065
|
+
method: "PATCH",
|
|
2066
|
+
path: "/groups/{id}",
|
|
2067
|
+
tag: "Takeoff",
|
|
2068
|
+
summary: "Update a takeoff group",
|
|
2069
|
+
body: true,
|
|
2070
|
+
response: "empty",
|
|
2071
|
+
params: [
|
|
2072
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex takeoff group ID" },
|
|
2073
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Convex project ID" },
|
|
2074
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2075
|
+
{ name: "position", in: "body", required: false, kind: "number" },
|
|
2076
|
+
{ name: "parentId", in: "body", required: false, kind: "string", description: "Convex takeoff group ID" }
|
|
2077
|
+
]
|
|
2078
|
+
},
|
|
2079
|
+
update_labor_crew: {
|
|
2080
|
+
method: "PATCH",
|
|
2081
|
+
path: "/labor/crews/{id}",
|
|
2082
|
+
tag: "Labor",
|
|
2083
|
+
summary: "Update a labor crew",
|
|
2084
|
+
body: true,
|
|
2085
|
+
response: "empty",
|
|
2086
|
+
params: [
|
|
2087
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
2088
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2089
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2090
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
2091
|
+
{ name: "members", in: "body", required: false, kind: "array" },
|
|
2092
|
+
{ name: "equipmentIds", in: "body", required: false, kind: "array" },
|
|
2093
|
+
{ name: "dailyCapacity", in: "body", required: false, kind: "object" }
|
|
2094
|
+
]
|
|
2095
|
+
},
|
|
2096
|
+
update_labor_equipment: {
|
|
2097
|
+
method: "PATCH",
|
|
2098
|
+
path: "/labor/equipment/{id}",
|
|
2099
|
+
tag: "Labor",
|
|
2100
|
+
summary: "Update a labor equipment entry",
|
|
2101
|
+
body: true,
|
|
2102
|
+
response: "empty",
|
|
2103
|
+
params: [
|
|
2104
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
2105
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2106
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2107
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
2108
|
+
{ name: "category", in: "body", required: false, kind: "string" },
|
|
2109
|
+
{ name: "dailyRate", in: "body", required: false, kind: "number" },
|
|
2110
|
+
{ name: "hourlyRate", in: "body", required: false, kind: "number" },
|
|
2111
|
+
{ name: "currency", in: "body", required: false, kind: "string", enum: ["USD", "CAD", "MXN", "BRL", "ARS", "CLP", "COP", "PEN", "UYU", "PYG", "BOB", "VES", "GYD", "SRD", "GTQ", "HNL", "NIO", "CRC", "PAB", "BZD", "JMD", "HTG", "DOP", "CUP", "BSD", "BBD", "TTD", "XCD", "AWG", "ANG", "KYD", "BMD", "FKP", "EUR", "GBP", "CHF", "NOK", "SEK", "DKK", "PLN", "CZK", "HUF", "RON", "BGN", "HRK", "RSD", "UAH", "RUB", "TRY", "ISK", "BAM", "MKD", "ALL", "MDL", "BYN", "GEL", "AMD", "AZN", "GIP", "ILS", "AED", "SAR", "QAR", "KWD", "BHD", "OMR", "JOD", "LBP", "EGP", "IQD", "IRR", "SYP", "YER", "ZAR", "NAD", "LSL", "SZL", "BWP", "NGN", "KES", "GHS", "MAD", "TND", "DZD", "LYD", "ETB", "ERN", "DJF", "SOS", "SDG", "SSP", "UGX", "TZS", "RWF", "BIF", "MWK", "ZMW", "ZWG", "MZN", "AOA", "CDF", "MGA", "MUR", "SCR", "KMF", "CVE", "GMD", "GNF", "LRD", "SLE", "MRU", "STN", "XOF", "XAF", "SHP", "JPY", "CNY", "HKD", "MOP", "TWD", "KRW", "KPW", "MNT", "SGD", "INR", "BTN", "PKR", "BDT", "LKR", "NPR", "MVR", "AFN", "IDR", "MYR", "PHP", "THB", "VND", "MMK", "KHR", "LAK", "BND", "KZT", "UZS", "KGS", "TJS", "TMT", "AUD", "NZD", "FJD", "PGK", "SBD", "VUV", "WST", "TOP", "XPF"], description: "ISO 4217 currency code" },
|
|
2112
|
+
{ name: "isOwned", in: "body", required: false, kind: "boolean" }
|
|
2113
|
+
]
|
|
2114
|
+
},
|
|
2115
|
+
update_labor_rate: {
|
|
2116
|
+
method: "PATCH",
|
|
2117
|
+
path: "/labor/rates/{id}",
|
|
2118
|
+
tag: "Labor",
|
|
2119
|
+
summary: "Update a labor rate",
|
|
2120
|
+
body: true,
|
|
2121
|
+
response: "empty",
|
|
2122
|
+
params: [
|
|
2123
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
2124
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2125
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2126
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
2127
|
+
{ name: "hourlyRate", in: "body", required: false, kind: "number" },
|
|
2128
|
+
{ name: "burdenRate", in: "body", required: false, kind: "number" },
|
|
2129
|
+
{ name: "currency", in: "body", required: false, kind: "string", enum: ["USD", "CAD", "MXN", "BRL", "ARS", "CLP", "COP", "PEN", "UYU", "PYG", "BOB", "VES", "GYD", "SRD", "GTQ", "HNL", "NIO", "CRC", "PAB", "BZD", "JMD", "HTG", "DOP", "CUP", "BSD", "BBD", "TTD", "XCD", "AWG", "ANG", "KYD", "BMD", "FKP", "EUR", "GBP", "CHF", "NOK", "SEK", "DKK", "PLN", "CZK", "HUF", "RON", "BGN", "HRK", "RSD", "UAH", "RUB", "TRY", "ISK", "BAM", "MKD", "ALL", "MDL", "BYN", "GEL", "AMD", "AZN", "GIP", "ILS", "AED", "SAR", "QAR", "KWD", "BHD", "OMR", "JOD", "LBP", "EGP", "IQD", "IRR", "SYP", "YER", "ZAR", "NAD", "LSL", "SZL", "BWP", "NGN", "KES", "GHS", "MAD", "TND", "DZD", "LYD", "ETB", "ERN", "DJF", "SOS", "SDG", "SSP", "UGX", "TZS", "RWF", "BIF", "MWK", "ZMW", "ZWG", "MZN", "AOA", "CDF", "MGA", "MUR", "SCR", "KMF", "CVE", "GMD", "GNF", "LRD", "SLE", "MRU", "STN", "XOF", "XAF", "SHP", "JPY", "CNY", "HKD", "MOP", "TWD", "KRW", "KPW", "MNT", "SGD", "INR", "BTN", "PKR", "BDT", "LKR", "NPR", "MVR", "AFN", "IDR", "MYR", "PHP", "THB", "VND", "MMK", "KHR", "LAK", "BND", "KZT", "UZS", "KGS", "TJS", "TMT", "AUD", "NZD", "FJD", "PGK", "SBD", "VUV", "WST", "TOP", "XPF"], description: "ISO 4217 currency code" },
|
|
2130
|
+
{ name: "effectiveDate", in: "body", required: false, kind: "number" },
|
|
2131
|
+
{ name: "isDefault", in: "body", required: false, kind: "boolean" }
|
|
2132
|
+
]
|
|
2133
|
+
},
|
|
2134
|
+
update_layer: {
|
|
2135
|
+
method: "PATCH",
|
|
2136
|
+
path: "/layers/{id}",
|
|
2137
|
+
tag: "Takeoff",
|
|
2138
|
+
summary: "Rename a takeoff layer",
|
|
2139
|
+
body: true,
|
|
2140
|
+
response: "empty",
|
|
2141
|
+
params: [
|
|
2142
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex takeoff layer ID" },
|
|
2143
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Convex project ID" },
|
|
2144
|
+
{ name: "name", in: "body", required: true, kind: "string" }
|
|
2145
|
+
]
|
|
2146
|
+
},
|
|
2147
|
+
update_list: {
|
|
2148
|
+
method: "PATCH",
|
|
2149
|
+
path: "/lists/{id}",
|
|
2150
|
+
tag: "Lists",
|
|
2151
|
+
summary: "Update list name or metadata",
|
|
2152
|
+
body: true,
|
|
2153
|
+
response: "json",
|
|
2154
|
+
params: [
|
|
2155
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex product list ID" },
|
|
2156
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2157
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2158
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
2159
|
+
{ name: "privacy", in: "body", required: false, kind: "string", enum: ["private", "organization", "public", "system"] },
|
|
2160
|
+
{ name: "category", in: "body", required: false, kind: "string" }
|
|
2161
|
+
]
|
|
2162
|
+
},
|
|
2163
|
+
update_measurement: {
|
|
2164
|
+
method: "PATCH",
|
|
2165
|
+
path: "/measurements/{id}",
|
|
2166
|
+
tag: "Takeoff",
|
|
2167
|
+
summary: "Update a measurement",
|
|
2168
|
+
body: true,
|
|
2169
|
+
response: "empty",
|
|
2170
|
+
params: [
|
|
2171
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex takeoff measurement ID" },
|
|
2172
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Convex project ID" },
|
|
2173
|
+
{ name: "value", in: "body", required: false, kind: "number", description: "The measured quantity in RAW PAGE PIXELS \u2014 the same coordinate space as the geometry points, NEVER feet, metres, or any other real-world unit. Linear measurements are a length in px, areas are px\xB2, counts are a tally. Exayard converts to real units at display time using the page scale, so a real-world number sent here is converted a second time and reads wrong. When geometry is supplied the server measures it and rejects a value that disagrees by 10\xD7 or more, naming both numbers. To send a real-world quantity, convert it to page pixels first (multiply by the page's pixels-per-unit; square that factor for an area)." },
|
|
2174
|
+
{ name: "manualValue", in: "body", required: false, kind: "number", description: "A user's override of the measured quantity, in RAW PAGE PIXELS \u2014 the same coordinate space as the geometry points, NEVER feet, metres, or any other real-world unit. Linear measurements are a length in px, areas are px\xB2, counts are a tally. Exayard converts to real units at display time using the page scale, so a real-world number sent here is converted a second time and reads wrong. When geometry is supplied the server measures it and rejects a value that disagrees by 10\xD7 or more, naming both numbers. To send a real-world quantity, convert it to page pixels first (multiply by the page's pixels-per-unit; square that factor for an area). Send null to clear the override and fall back to the measured value." },
|
|
2175
|
+
{ name: "geometry", in: "body", required: false, kind: "object", description: 'Measurement geometry: { "points": [{ "x": number, "y": number, "bulge"?: number }, ...], "isClosed": boolean, "holes"?: Point[][], "pathD"?: string }. When geometry is provided, "points" and "isClosed" are required; other keys (e.g. { "length": 280.9 }) are rejected.' },
|
|
2176
|
+
{ name: "notes", in: "body", required: false, kind: "string" },
|
|
2177
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2178
|
+
{ name: "layerId", in: "body", required: false, kind: "string", description: "Convex takeoff layer ID" }
|
|
2179
|
+
]
|
|
2180
|
+
},
|
|
2181
|
+
update_message: {
|
|
2182
|
+
method: "PATCH",
|
|
2183
|
+
path: "/messages/{id}",
|
|
2184
|
+
tag: "Messages",
|
|
2185
|
+
summary: "Update a message",
|
|
2186
|
+
body: true,
|
|
2187
|
+
response: "empty",
|
|
2188
|
+
params: [
|
|
2189
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex chat message ID" },
|
|
2190
|
+
{ name: "content", in: "body", required: true, kind: "string" }
|
|
2191
|
+
]
|
|
2192
|
+
},
|
|
2193
|
+
update_page: {
|
|
2194
|
+
method: "PATCH",
|
|
2195
|
+
path: "/pages/{id}",
|
|
2196
|
+
tag: "Pages",
|
|
2197
|
+
summary: "Update page name or rotation",
|
|
2198
|
+
body: true,
|
|
2199
|
+
response: "json",
|
|
2200
|
+
params: [
|
|
2201
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex page ID" },
|
|
2202
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2203
|
+
{ name: "rotation", in: "body", required: false, kind: "number" }
|
|
2204
|
+
]
|
|
2205
|
+
},
|
|
2206
|
+
update_product: {
|
|
2207
|
+
method: "PATCH",
|
|
2208
|
+
path: "/products/{id}",
|
|
2209
|
+
tag: "Products",
|
|
2210
|
+
summary: "Update a product",
|
|
2211
|
+
body: true,
|
|
2212
|
+
response: "json",
|
|
2213
|
+
params: [
|
|
2214
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex product ID" },
|
|
2215
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2216
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2217
|
+
{ name: "sku", in: "body", required: false, kind: "string" },
|
|
2218
|
+
{ name: "unitOfMeasure", in: "body", required: false, kind: "string" },
|
|
2219
|
+
{ name: "costPerUnit", in: "body", required: false, kind: "number" },
|
|
2220
|
+
{ name: "currency", in: "body", required: false, kind: "string", enum: ["USD", "CAD", "MXN", "BRL", "ARS", "CLP", "COP", "PEN", "UYU", "PYG", "BOB", "VES", "GYD", "SRD", "GTQ", "HNL", "NIO", "CRC", "PAB", "BZD", "JMD", "HTG", "DOP", "CUP", "BSD", "BBD", "TTD", "XCD", "AWG", "ANG", "KYD", "BMD", "FKP", "EUR", "GBP", "CHF", "NOK", "SEK", "DKK", "PLN", "CZK", "HUF", "RON", "BGN", "HRK", "RSD", "UAH", "RUB", "TRY", "ISK", "BAM", "MKD", "ALL", "MDL", "BYN", "GEL", "AMD", "AZN", "GIP", "ILS", "AED", "SAR", "QAR", "KWD", "BHD", "OMR", "JOD", "LBP", "EGP", "IQD", "IRR", "SYP", "YER", "ZAR", "NAD", "LSL", "SZL", "BWP", "NGN", "KES", "GHS", "MAD", "TND", "DZD", "LYD", "ETB", "ERN", "DJF", "SOS", "SDG", "SSP", "UGX", "TZS", "RWF", "BIF", "MWK", "ZMW", "ZWG", "MZN", "AOA", "CDF", "MGA", "MUR", "SCR", "KMF", "CVE", "GMD", "GNF", "LRD", "SLE", "MRU", "STN", "XOF", "XAF", "SHP", "JPY", "CNY", "HKD", "MOP", "TWD", "KRW", "KPW", "MNT", "SGD", "INR", "BTN", "PKR", "BDT", "LKR", "NPR", "MVR", "AFN", "IDR", "MYR", "PHP", "THB", "VND", "MMK", "KHR", "LAK", "BND", "KZT", "UZS", "KGS", "TJS", "TMT", "AUD", "NZD", "FJD", "PGK", "SBD", "VUV", "WST", "TOP", "XPF"], description: "ISO 4217 currency code" },
|
|
2221
|
+
{ name: "markupPercent", in: "body", required: false, kind: "number" },
|
|
2222
|
+
{ name: "customProperties", in: "body", required: false, kind: "object" }
|
|
2223
|
+
]
|
|
2224
|
+
},
|
|
2225
|
+
update_project: {
|
|
2226
|
+
method: "PATCH",
|
|
2227
|
+
path: "/projects/{id}",
|
|
2228
|
+
tag: "Projects",
|
|
2229
|
+
summary: "Update a project",
|
|
2230
|
+
body: true,
|
|
2231
|
+
response: "json",
|
|
2232
|
+
params: [
|
|
2233
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
2234
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2235
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2236
|
+
{ name: "status", in: "body", required: false, kind: "string", enum: ["lead", "contacted", "qualified", "active", "archived"] },
|
|
2237
|
+
{ name: "settings", in: "body", required: false, kind: "object" },
|
|
2238
|
+
{ name: "tags", in: "body", required: false, kind: "array" },
|
|
2239
|
+
{ name: "customProperties", in: "body", required: false, kind: "object" }
|
|
2240
|
+
]
|
|
2241
|
+
},
|
|
2242
|
+
update_project_privacy: {
|
|
2243
|
+
method: "POST",
|
|
2244
|
+
path: "/projects/{id}/privacy",
|
|
2245
|
+
tag: "Projects",
|
|
2246
|
+
summary: "Change a project\u2019s privacy setting",
|
|
2247
|
+
body: true,
|
|
2248
|
+
response: "empty",
|
|
2249
|
+
params: [
|
|
2250
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex project ID" },
|
|
2251
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2252
|
+
{ name: "privacy", in: "body", required: true, kind: "string", enum: ["private", "unlisted", "public"] }
|
|
2253
|
+
]
|
|
2254
|
+
},
|
|
2255
|
+
update_symbol: {
|
|
2256
|
+
method: "PATCH",
|
|
2257
|
+
path: "/symbols/{id}",
|
|
2258
|
+
tag: "Takeoff",
|
|
2259
|
+
summary: "Update a takeoff symbol",
|
|
2260
|
+
body: true,
|
|
2261
|
+
response: "empty",
|
|
2262
|
+
params: [
|
|
2263
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex takeoff symbol ID" },
|
|
2264
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Convex project ID" },
|
|
2265
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2266
|
+
{ name: "color", in: "body", required: false, kind: "string" },
|
|
2267
|
+
{ name: "type", in: "body", required: false, kind: "string", enum: ["count", "linear", "area"] },
|
|
2268
|
+
{ name: "category", in: "body", required: false, kind: "string" },
|
|
2269
|
+
{ name: "subcategory", in: "body", required: false, kind: "string" },
|
|
2270
|
+
{ name: "customProperties", in: "body", required: false, kind: "object" }
|
|
2271
|
+
]
|
|
2272
|
+
},
|
|
2273
|
+
update_vendor: {
|
|
2274
|
+
method: "PATCH",
|
|
2275
|
+
path: "/vendors/{id}",
|
|
2276
|
+
tag: "Vendors",
|
|
2277
|
+
summary: "Update a vendor",
|
|
2278
|
+
body: true,
|
|
2279
|
+
response: "json",
|
|
2280
|
+
params: [
|
|
2281
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex vendor ID" },
|
|
2282
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2283
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2284
|
+
{ name: "type", in: "body", required: false, kind: "string", enum: ["supplier", "subcontractor", "rental"] },
|
|
2285
|
+
{ name: "kind", in: "body", required: false, kind: "string", enum: ["sub", "supplier", "surety", "insurer", "agency", "owner", "architect", "engineer", "other", null] },
|
|
2286
|
+
{ name: "contactName", in: "body", required: false, kind: "string" },
|
|
2287
|
+
{ name: "email", in: "body", required: false, kind: "string" },
|
|
2288
|
+
{ name: "phone", in: "body", required: false, kind: "string" },
|
|
2289
|
+
{ name: "website", in: "body", required: false, kind: "string" },
|
|
2290
|
+
{ name: "address", in: "body", required: false, kind: "object" },
|
|
2291
|
+
{ name: "paymentTerms", in: "body", required: false, kind: "string" },
|
|
2292
|
+
{ name: "accountNumber", in: "body", required: false, kind: "string" },
|
|
2293
|
+
{ name: "rating", in: "body", required: false, kind: "number" },
|
|
2294
|
+
{ name: "notes", in: "body", required: false, kind: "string" },
|
|
2295
|
+
{ name: "tags", in: "body", required: false, kind: "array" },
|
|
2296
|
+
{ name: "isPreferred", in: "body", required: false, kind: "boolean" }
|
|
2297
|
+
]
|
|
2298
|
+
},
|
|
2299
|
+
update_vendor_quote_status: {
|
|
2300
|
+
method: "PATCH",
|
|
2301
|
+
path: "/quotes/{id}/status",
|
|
2302
|
+
tag: "Vendor quotes",
|
|
2303
|
+
summary: "Accept, reject, or expire a vendor quote",
|
|
2304
|
+
body: true,
|
|
2305
|
+
response: "json",
|
|
2306
|
+
params: [
|
|
2307
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
2308
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2309
|
+
{ name: "status", in: "body", required: true, kind: "string", enum: ["accepted", "rejected", "expired"] }
|
|
2310
|
+
]
|
|
2311
|
+
},
|
|
2312
|
+
update_webhook_endpoint: {
|
|
2313
|
+
method: "PATCH",
|
|
2314
|
+
path: "/webhook_endpoints/{id}",
|
|
2315
|
+
tag: "Webhooks",
|
|
2316
|
+
summary: "Update a webhook endpoint",
|
|
2317
|
+
body: true,
|
|
2318
|
+
response: "empty",
|
|
2319
|
+
params: [
|
|
2320
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
2321
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2322
|
+
{ name: "url", in: "body", required: false, kind: "string" },
|
|
2323
|
+
{ name: "events", in: "body", required: false, kind: "array" },
|
|
2324
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
2325
|
+
{ name: "status", in: "body", required: false, kind: "string", enum: ["active", "paused"] }
|
|
2326
|
+
]
|
|
2327
|
+
},
|
|
2328
|
+
update_widget: {
|
|
2329
|
+
method: "PATCH",
|
|
2330
|
+
path: "/widgets/{id}",
|
|
2331
|
+
tag: "Widgets",
|
|
2332
|
+
summary: "Update a widget",
|
|
2333
|
+
body: true,
|
|
2334
|
+
response: "empty",
|
|
2335
|
+
params: [
|
|
2336
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Convex widget ID" },
|
|
2337
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Convex organization ID \u2014 get it from GET /v1/me or the get_me tool (memberships[].orgId), not the organization name. Optional with a company key or an app's key: the key's company is used." },
|
|
2338
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2339
|
+
{ name: "isActive", in: "body", required: false, kind: "boolean" },
|
|
2340
|
+
{ name: "config", in: "body", required: false, kind: "object" }
|
|
2341
|
+
]
|
|
2342
|
+
}
|
|
2343
|
+
};
|
|
2344
|
+
exports2.OPERATION_METHODS = {
|
|
2345
|
+
addSymbolLineItem: "add_symbol_line_item",
|
|
2346
|
+
archiveConversation: "archive_conversation",
|
|
2347
|
+
archiveLaborCrew: "archive_labor_crew",
|
|
2348
|
+
archiveLaborEquipment: "archive_labor_equipment",
|
|
2349
|
+
archiveLaborRate: "archive_labor_rate",
|
|
2350
|
+
archiveList: "archive_list",
|
|
2351
|
+
archivePage: "archive_page",
|
|
2352
|
+
archiveProduct: "archive_product",
|
|
2353
|
+
archiveProject: "archive_project",
|
|
2354
|
+
archiveVendor: "archive_vendor",
|
|
2355
|
+
attachFileToProject: "attach_file_to_project",
|
|
2356
|
+
batchCreateProducts: "batch_create_products",
|
|
2357
|
+
cancelAssessment: "cancel_assessment",
|
|
2358
|
+
confirmFileUpload: "confirm_file_upload",
|
|
2359
|
+
createAgentSession: "create_agent_session",
|
|
2360
|
+
createAssessment: "create_assessment",
|
|
2361
|
+
createBid: "create_bid",
|
|
2362
|
+
createConversation: "create_conversation",
|
|
2363
|
+
createCustomProperty: "create_custom_property",
|
|
2364
|
+
createEstimate: "create_estimate",
|
|
2365
|
+
createFolder: "create_folder",
|
|
2366
|
+
createGroup: "create_group",
|
|
2367
|
+
createLaborCrew: "create_labor_crew",
|
|
2368
|
+
createLaborEquipment: "create_labor_equipment",
|
|
2369
|
+
createLaborRate: "create_labor_rate",
|
|
2370
|
+
createLayer: "create_layer",
|
|
2371
|
+
createList: "create_list",
|
|
2372
|
+
createMeasurement: "create_measurement",
|
|
2373
|
+
createMessage: "create_message",
|
|
2374
|
+
createOrganization: "create_organization",
|
|
2375
|
+
createProduct: "create_product",
|
|
2376
|
+
createProject: "create_project",
|
|
2377
|
+
createSymbol: "create_symbol",
|
|
2378
|
+
createVendor: "create_vendor",
|
|
2379
|
+
createVendorQuote: "create_vendor_quote",
|
|
2380
|
+
createWebhookEndpoint: "create_webhook_endpoint",
|
|
2381
|
+
createWidget: "create_widget",
|
|
2382
|
+
deleteOrganization: "delete_organization",
|
|
2383
|
+
deleteWebhookEndpoint: "delete_webhook_endpoint",
|
|
2384
|
+
disconnectIntegration: "disconnect_integration",
|
|
2385
|
+
exportProject: "export_project",
|
|
2386
|
+
findSymbolElements: "find_symbol_elements",
|
|
2387
|
+
generateEstimate: "generate_estimate",
|
|
2388
|
+
getAssessment: "get_assessment",
|
|
2389
|
+
getBestPriceForProduct: "get_best_price_for_product",
|
|
2390
|
+
getContact: "get_contact",
|
|
2391
|
+
getConversation: "get_conversation",
|
|
2392
|
+
getCreditBalance: "get_credit_balance",
|
|
2393
|
+
getCreditUsageHistory: "get_credit_usage_history",
|
|
2394
|
+
getCreditUsageStats: "get_credit_usage_stats",
|
|
2395
|
+
getCustomProperty: "get_custom_property",
|
|
2396
|
+
getDocument: "get_document",
|
|
2397
|
+
getFile: "get_file",
|
|
2398
|
+
getFileUrl: "get_file_url",
|
|
2399
|
+
getFolder: "get_folder",
|
|
2400
|
+
getLaborCrew: "get_labor_crew",
|
|
2401
|
+
getLaborEquipment: "get_labor_equipment",
|
|
2402
|
+
getLaborRate: "get_labor_rate",
|
|
2403
|
+
getLatestAssessment: "get_latest_assessment",
|
|
2404
|
+
getList: "get_list",
|
|
2405
|
+
getMe: "get_me",
|
|
2406
|
+
getMeasurement: "get_measurement",
|
|
2407
|
+
getPage: "get_page",
|
|
2408
|
+
getPageContent: "get_page_content",
|
|
2409
|
+
getPricesForProduct: "get_prices_for_product",
|
|
2410
|
+
getProduct: "get_product",
|
|
2411
|
+
getProject: "get_project",
|
|
2412
|
+
getProjectRelations: "get_project_relations",
|
|
2413
|
+
getSpreadsheet: "get_spreadsheet",
|
|
2414
|
+
getSymbol: "get_symbol",
|
|
2415
|
+
getSymbolLineItem: "get_symbol_line_item",
|
|
2416
|
+
getTakeoffSummary: "get_takeoff_summary",
|
|
2417
|
+
getUnreadNotificationCount: "get_unread_notification_count",
|
|
2418
|
+
getUsage: "get_usage",
|
|
2419
|
+
getVendor: "get_vendor",
|
|
2420
|
+
getVendorQuote: "get_vendor_quote",
|
|
2421
|
+
getWidget: "get_widget",
|
|
2422
|
+
listAssessments: "list_assessments",
|
|
2423
|
+
listContacts: "list_contacts",
|
|
2424
|
+
listConversationFiles: "list_conversation_files",
|
|
2425
|
+
listConversations: "list_conversations",
|
|
2426
|
+
listCustomProperties: "list_custom_properties",
|
|
2427
|
+
listDocuments: "list_documents",
|
|
2428
|
+
listFiles: "list_files",
|
|
2429
|
+
listFolderContents: "list_folder_contents",
|
|
2430
|
+
listFolders: "list_folders",
|
|
2431
|
+
listGroups: "list_groups",
|
|
2432
|
+
listInstallations: "list_installations",
|
|
2433
|
+
listIntegrations: "list_integrations",
|
|
2434
|
+
listLaborCrews: "list_labor_crews",
|
|
2435
|
+
listLaborEquipment: "list_labor_equipment",
|
|
2436
|
+
listLaborRates: "list_labor_rates",
|
|
2437
|
+
listLayers: "list_layers",
|
|
2438
|
+
listLists: "list_lists",
|
|
2439
|
+
listMeasurementsByPage: "list_measurements_by_page",
|
|
2440
|
+
listMeasurementsBySymbol: "list_measurements_by_symbol",
|
|
2441
|
+
listMessages: "list_messages",
|
|
2442
|
+
listNotifications: "list_notifications",
|
|
2443
|
+
listOrgDocuments: "list_org_documents",
|
|
2444
|
+
listOrganizations: "list_organizations",
|
|
2445
|
+
listPages: "list_pages",
|
|
2446
|
+
listProducts: "list_products",
|
|
2447
|
+
listProductsInList: "list_products_in_list",
|
|
2448
|
+
listProjectConversations: "list_project_conversations",
|
|
2449
|
+
listProjects: "list_projects",
|
|
2450
|
+
listSpreadsheets: "list_spreadsheets",
|
|
2451
|
+
listSymbolLineItems: "list_symbol_line_items",
|
|
2452
|
+
listSymbols: "list_symbols",
|
|
2453
|
+
listVendorQuotes: "list_vendor_quotes",
|
|
2454
|
+
listVendors: "list_vendors",
|
|
2455
|
+
listWebhookDeliveries: "list_webhook_deliveries",
|
|
2456
|
+
listWebhookEndpoints: "list_webhook_endpoints",
|
|
2457
|
+
listWidgetConversations: "list_widget_conversations",
|
|
2458
|
+
listWidgets: "list_widgets",
|
|
2459
|
+
markAllNotificationsRead: "mark_all_notifications_read",
|
|
2460
|
+
markNotificationRead: "mark_notification_read",
|
|
2461
|
+
patchSymbolLineItem: "patch_symbol_line_item",
|
|
2462
|
+
presignFileUpload: "presign_file_upload",
|
|
2463
|
+
proposeAnalysis: "propose_analysis",
|
|
2464
|
+
provisionCma: "provision_cma",
|
|
2465
|
+
receiveVendorQuote: "receive_vendor_quote",
|
|
2466
|
+
removeCustomProperty: "remove_custom_property",
|
|
2467
|
+
removeFile: "remove_file",
|
|
2468
|
+
removeFolder: "remove_folder",
|
|
2469
|
+
removeGroup: "remove_group",
|
|
2470
|
+
removeLayer: "remove_layer",
|
|
2471
|
+
removeMeasurement: "remove_measurement",
|
|
2472
|
+
removeMessage: "remove_message",
|
|
2473
|
+
removeSymbol: "remove_symbol",
|
|
2474
|
+
removeSymbolLineItem: "remove_symbol_line_item",
|
|
2475
|
+
removeVendorPrice: "remove_vendor_price",
|
|
2476
|
+
removeWidget: "remove_widget",
|
|
2477
|
+
renameFile: "rename_file",
|
|
2478
|
+
renameFolder: "rename_folder",
|
|
2479
|
+
resendWebhookDelivery: "resend_webhook_delivery",
|
|
2480
|
+
restoreList: "restore_list",
|
|
2481
|
+
restorePage: "restore_page",
|
|
2482
|
+
restoreProject: "restore_project",
|
|
2483
|
+
runApprovedAnalysis: "run_approved_analysis",
|
|
2484
|
+
searchHelpArticles: "search_help_articles",
|
|
2485
|
+
searchProducts: "search_products",
|
|
2486
|
+
searchProjectFiles: "search_project_files",
|
|
2487
|
+
searchProjectLineItems: "search_project_line_items",
|
|
2488
|
+
searchVendors: "search_vendors",
|
|
2489
|
+
sendCmaMessage: "send_cma_message",
|
|
2490
|
+
sendWebhookListenerTest: "send_webhook_listener_test",
|
|
2491
|
+
sendWebhookTestEvent: "send_webhook_test_event",
|
|
2492
|
+
setDocumentSharing: "set_document_sharing",
|
|
2493
|
+
setVendorPrice: "set_vendor_price",
|
|
2494
|
+
startWebhookListener: "start_webhook_listener",
|
|
2495
|
+
stopWebhookListener: "stop_webhook_listener",
|
|
2496
|
+
streamWebhookListenerEvents: "stream_webhook_listener_events",
|
|
2497
|
+
updateContact: "update_contact",
|
|
2498
|
+
updateConversationTitle: "update_conversation_title",
|
|
2499
|
+
updateCustomProperty: "update_custom_property",
|
|
2500
|
+
updateGroup: "update_group",
|
|
2501
|
+
updateLaborCrew: "update_labor_crew",
|
|
2502
|
+
updateLaborEquipment: "update_labor_equipment",
|
|
2503
|
+
updateLaborRate: "update_labor_rate",
|
|
2504
|
+
updateLayer: "update_layer",
|
|
2505
|
+
updateList: "update_list",
|
|
2506
|
+
updateMeasurement: "update_measurement",
|
|
2507
|
+
updateMessage: "update_message",
|
|
2508
|
+
updatePage: "update_page",
|
|
2509
|
+
updateProduct: "update_product",
|
|
2510
|
+
updateProject: "update_project",
|
|
2511
|
+
updateProjectPrivacy: "update_project_privacy",
|
|
2512
|
+
updateSymbol: "update_symbol",
|
|
2513
|
+
updateVendor: "update_vendor",
|
|
2514
|
+
updateVendorQuoteStatus: "update_vendor_quote_status",
|
|
2515
|
+
updateWebhookEndpoint: "update_webhook_endpoint",
|
|
2516
|
+
updateWidget: "update_widget"
|
|
2517
|
+
};
|
|
2518
|
+
}
|
|
2519
|
+
});
|
|
2520
|
+
|
|
29
2521
|
// ../sdk/dist/error.js
|
|
30
2522
|
var require_error = __commonJS({
|
|
31
2523
|
"../sdk/dist/error.js"(exports2) {
|
|
32
2524
|
"use strict";
|
|
33
2525
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
34
|
-
exports2.ExayardError = void 0;
|
|
2526
|
+
exports2.errorFromProblem = exports2.APIConnectionError = exports2.InternalServerError = exports2.RateLimitError = exports2.UnprocessableEntityError = exports2.ConflictError = exports2.NotFoundError = exports2.PermissionDeniedError = exports2.PaymentRequiredError = exports2.AuthenticationError = exports2.BadRequestError = exports2.ExayardError = void 0;
|
|
2527
|
+
var KNOWN_FIELDS = /* @__PURE__ */ new Set(["type", "title", "status", "detail", "instance", "code", "param", "doc_url", "request_id"]);
|
|
35
2528
|
var ExayardError2 = class extends Error {
|
|
36
2529
|
type;
|
|
37
2530
|
title;
|
|
@@ -42,6 +2535,8 @@ var require_error = __commonJS({
|
|
|
42
2535
|
param;
|
|
43
2536
|
docUrl;
|
|
44
2537
|
requestId;
|
|
2538
|
+
/** Problem extension members, such as `limit` on usage_limit_reached. */
|
|
2539
|
+
extensions;
|
|
45
2540
|
constructor(body) {
|
|
46
2541
|
const title = body.title ?? "error";
|
|
47
2542
|
const status = typeof body.status === "number" ? body.status : 0;
|
|
@@ -49,7 +2544,7 @@ var require_error = __commonJS({
|
|
|
49
2544
|
const detail = body.detail ?? "";
|
|
50
2545
|
const message = detail ? `${title} (${code}): ${detail}` : `HTTP ${status || "unknown"}: ${title}`;
|
|
51
2546
|
super(message);
|
|
52
|
-
this.name =
|
|
2547
|
+
this.name = new.target.name;
|
|
53
2548
|
this.type = body.type ?? "about:blank";
|
|
54
2549
|
this.title = title;
|
|
55
2550
|
this.status = status;
|
|
@@ -59,13 +2554,14 @@ var require_error = __commonJS({
|
|
|
59
2554
|
this.param = body.param;
|
|
60
2555
|
this.docUrl = body.doc_url;
|
|
61
2556
|
this.requestId = body.request_id;
|
|
2557
|
+
this.extensions = Object.fromEntries(Object.entries(body).filter(([key]) => !KNOWN_FIELDS.has(key)));
|
|
62
2558
|
}
|
|
63
2559
|
// Convenience guards for the most common branching points.
|
|
64
2560
|
isRateLimited() {
|
|
65
2561
|
return this.code === "rate_limited" || this.status === 429;
|
|
66
2562
|
}
|
|
67
2563
|
isUnauthenticated() {
|
|
68
|
-
return this.code === "unauthenticated" || this.status === 401;
|
|
2564
|
+
return this.code === "unauthenticated" || this.code === "api_key_expired" || this.status === 401;
|
|
69
2565
|
}
|
|
70
2566
|
isNotFound() {
|
|
71
2567
|
return this.code === "not_found" || this.status === 404;
|
|
@@ -78,6 +2574,341 @@ var require_error = __commonJS({
|
|
|
78
2574
|
}
|
|
79
2575
|
};
|
|
80
2576
|
exports2.ExayardError = ExayardError2;
|
|
2577
|
+
var BadRequestError = class extends ExayardError2 {
|
|
2578
|
+
};
|
|
2579
|
+
exports2.BadRequestError = BadRequestError;
|
|
2580
|
+
var AuthenticationError = class extends ExayardError2 {
|
|
2581
|
+
};
|
|
2582
|
+
exports2.AuthenticationError = AuthenticationError;
|
|
2583
|
+
var PaymentRequiredError = class extends ExayardError2 {
|
|
2584
|
+
};
|
|
2585
|
+
exports2.PaymentRequiredError = PaymentRequiredError;
|
|
2586
|
+
var PermissionDeniedError = class extends ExayardError2 {
|
|
2587
|
+
};
|
|
2588
|
+
exports2.PermissionDeniedError = PermissionDeniedError;
|
|
2589
|
+
var NotFoundError = class extends ExayardError2 {
|
|
2590
|
+
};
|
|
2591
|
+
exports2.NotFoundError = NotFoundError;
|
|
2592
|
+
var ConflictError = class extends ExayardError2 {
|
|
2593
|
+
};
|
|
2594
|
+
exports2.ConflictError = ConflictError;
|
|
2595
|
+
var UnprocessableEntityError = class extends ExayardError2 {
|
|
2596
|
+
};
|
|
2597
|
+
exports2.UnprocessableEntityError = UnprocessableEntityError;
|
|
2598
|
+
var RateLimitError = class extends ExayardError2 {
|
|
2599
|
+
retryAfter;
|
|
2600
|
+
constructor(body, retryAfter) {
|
|
2601
|
+
super(body);
|
|
2602
|
+
this.retryAfter = retryAfter;
|
|
2603
|
+
}
|
|
2604
|
+
};
|
|
2605
|
+
exports2.RateLimitError = RateLimitError;
|
|
2606
|
+
var InternalServerError = class extends ExayardError2 {
|
|
2607
|
+
};
|
|
2608
|
+
exports2.InternalServerError = InternalServerError;
|
|
2609
|
+
var APIConnectionError = class extends ExayardError2 {
|
|
2610
|
+
};
|
|
2611
|
+
exports2.APIConnectionError = APIConnectionError;
|
|
2612
|
+
var BY_CODE = {
|
|
2613
|
+
invalid_request: BadRequestError,
|
|
2614
|
+
invalid_end_user: BadRequestError,
|
|
2615
|
+
company_required: BadRequestError,
|
|
2616
|
+
company_mismatch: BadRequestError,
|
|
2617
|
+
unauthenticated: AuthenticationError,
|
|
2618
|
+
api_key_expired: AuthenticationError,
|
|
2619
|
+
insufficient_credits: PaymentRequiredError,
|
|
2620
|
+
subscription_required: PaymentRequiredError,
|
|
2621
|
+
upgrade_required: PaymentRequiredError,
|
|
2622
|
+
paid_plan_required: PaymentRequiredError,
|
|
2623
|
+
project_limit_reached: PaymentRequiredError,
|
|
2624
|
+
usage_limit_reached: PaymentRequiredError,
|
|
2625
|
+
forbidden: PermissionDeniedError,
|
|
2626
|
+
insufficient_scope: PermissionDeniedError,
|
|
2627
|
+
app_not_installed: PermissionDeniedError,
|
|
2628
|
+
app_suspended: PermissionDeniedError,
|
|
2629
|
+
partner_company_limit_reached: PermissionDeniedError,
|
|
2630
|
+
not_found: NotFoundError,
|
|
2631
|
+
conflict: ConflictError,
|
|
2632
|
+
idempotency_key_reused: ConflictError,
|
|
2633
|
+
rate_limited: RateLimitError,
|
|
2634
|
+
internal_error: InternalServerError
|
|
2635
|
+
};
|
|
2636
|
+
var BY_STATUS = {
|
|
2637
|
+
400: BadRequestError,
|
|
2638
|
+
401: AuthenticationError,
|
|
2639
|
+
402: PaymentRequiredError,
|
|
2640
|
+
403: PermissionDeniedError,
|
|
2641
|
+
404: NotFoundError,
|
|
2642
|
+
409: ConflictError,
|
|
2643
|
+
422: UnprocessableEntityError,
|
|
2644
|
+
429: RateLimitError
|
|
2645
|
+
};
|
|
2646
|
+
var errorFromProblem = (body, retryAfter) => {
|
|
2647
|
+
const status = typeof body.status === "number" ? body.status : 0;
|
|
2648
|
+
const cls = BY_CODE[body.code] ?? BY_STATUS[status] ?? (status >= 500 ? InternalServerError : ExayardError2);
|
|
2649
|
+
if (cls === RateLimitError)
|
|
2650
|
+
return new RateLimitError(body, retryAfter);
|
|
2651
|
+
return new cls(body);
|
|
2652
|
+
};
|
|
2653
|
+
exports2.errorFromProblem = errorFromProblem;
|
|
2654
|
+
}
|
|
2655
|
+
});
|
|
2656
|
+
|
|
2657
|
+
// ../sdk/dist/retry.js
|
|
2658
|
+
var require_retry = __commonJS({
|
|
2659
|
+
"../sdk/dist/retry.js"(exports2) {
|
|
2660
|
+
"use strict";
|
|
2661
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2662
|
+
exports2.sleep = exports2.retryDelayMs = exports2.parseRetryAfter = exports2.isRetryableStatus = exports2.MAX_RETRY_AFTER_SECONDS = exports2.DEFAULT_MAX_RETRIES = void 0;
|
|
2663
|
+
exports2.DEFAULT_MAX_RETRIES = 2;
|
|
2664
|
+
exports2.MAX_RETRY_AFTER_SECONDS = 60;
|
|
2665
|
+
var BASE_DELAY_MS = 500;
|
|
2666
|
+
var MAX_BACKOFF_MS = 8e3;
|
|
2667
|
+
var isRetryableStatus = (status) => status === 429 || status >= 500;
|
|
2668
|
+
exports2.isRetryableStatus = isRetryableStatus;
|
|
2669
|
+
var parseRetryAfter = (value, now = Date.now()) => {
|
|
2670
|
+
if (value === null || value === void 0)
|
|
2671
|
+
return void 0;
|
|
2672
|
+
const trimmed = value.trim();
|
|
2673
|
+
if (trimmed === "")
|
|
2674
|
+
return void 0;
|
|
2675
|
+
if (/^\d+(\.\d+)?$/.test(trimmed))
|
|
2676
|
+
return Number(trimmed);
|
|
2677
|
+
const at = Date.parse(trimmed);
|
|
2678
|
+
if (Number.isNaN(at))
|
|
2679
|
+
return void 0;
|
|
2680
|
+
return Math.max(0, (at - now) / 1e3);
|
|
2681
|
+
};
|
|
2682
|
+
exports2.parseRetryAfter = parseRetryAfter;
|
|
2683
|
+
var retryDelayMs = (attempt, retryAfterSeconds, random = Math.random) => {
|
|
2684
|
+
if (retryAfterSeconds !== void 0)
|
|
2685
|
+
return Math.ceil(retryAfterSeconds * 1e3);
|
|
2686
|
+
const backoff = Math.min(MAX_BACKOFF_MS, BASE_DELAY_MS * 2 ** (attempt - 1));
|
|
2687
|
+
return Math.round(backoff * (1 - 0.25 * random()));
|
|
2688
|
+
};
|
|
2689
|
+
exports2.retryDelayMs = retryDelayMs;
|
|
2690
|
+
var sleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
2691
|
+
if (signal?.aborted) {
|
|
2692
|
+
reject(signal.reason ?? new Error("Aborted"));
|
|
2693
|
+
return;
|
|
2694
|
+
}
|
|
2695
|
+
const timer = setTimeout(() => {
|
|
2696
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2697
|
+
resolve();
|
|
2698
|
+
}, ms);
|
|
2699
|
+
const onAbort = () => {
|
|
2700
|
+
clearTimeout(timer);
|
|
2701
|
+
reject(signal?.reason ?? new Error("Aborted"));
|
|
2702
|
+
};
|
|
2703
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2704
|
+
});
|
|
2705
|
+
exports2.sleep = sleep;
|
|
2706
|
+
}
|
|
2707
|
+
});
|
|
2708
|
+
|
|
2709
|
+
// ../sdk/dist/listeners.js
|
|
2710
|
+
var require_listeners = __commonJS({
|
|
2711
|
+
"../sdk/dist/listeners.js"(exports2) {
|
|
2712
|
+
"use strict";
|
|
2713
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2714
|
+
exports2.WebhookListeners = void 0;
|
|
2715
|
+
exports2.parseServerSentEvents = parseServerSentEvents;
|
|
2716
|
+
var error_1 = require_error();
|
|
2717
|
+
var retry_1 = require_retry();
|
|
2718
|
+
async function* parseServerSentEvents(body) {
|
|
2719
|
+
const reader = body.getReader();
|
|
2720
|
+
const decoder = new TextDecoder();
|
|
2721
|
+
let buffer = "";
|
|
2722
|
+
let current = { data: [] };
|
|
2723
|
+
const flush = () => {
|
|
2724
|
+
const out = current;
|
|
2725
|
+
current = { data: [] };
|
|
2726
|
+
if (out.data.length === 0 && out.retry === void 0 && out.event === void 0)
|
|
2727
|
+
return null;
|
|
2728
|
+
return {
|
|
2729
|
+
...out.id !== void 0 ? { id: out.id } : {},
|
|
2730
|
+
...out.event !== void 0 ? { event: out.event } : {},
|
|
2731
|
+
data: out.data.join("\n"),
|
|
2732
|
+
...out.retry !== void 0 ? { retry: out.retry } : {}
|
|
2733
|
+
};
|
|
2734
|
+
};
|
|
2735
|
+
const take = function* (line) {
|
|
2736
|
+
if (line === "") {
|
|
2737
|
+
const event = flush();
|
|
2738
|
+
if (event)
|
|
2739
|
+
yield event;
|
|
2740
|
+
return;
|
|
2741
|
+
}
|
|
2742
|
+
if (line.startsWith(":"))
|
|
2743
|
+
return;
|
|
2744
|
+
const colon = line.indexOf(":");
|
|
2745
|
+
const field = colon === -1 ? line : line.slice(0, colon);
|
|
2746
|
+
let value = colon === -1 ? "" : line.slice(colon + 1);
|
|
2747
|
+
if (value.startsWith(" "))
|
|
2748
|
+
value = value.slice(1);
|
|
2749
|
+
if (field === "data")
|
|
2750
|
+
current.data.push(value);
|
|
2751
|
+
else if (field === "event")
|
|
2752
|
+
current.event = value;
|
|
2753
|
+
else if (field === "id")
|
|
2754
|
+
current.id = value;
|
|
2755
|
+
else if (field === "retry" && /^\d+$/.test(value))
|
|
2756
|
+
current.retry = Number(value);
|
|
2757
|
+
};
|
|
2758
|
+
try {
|
|
2759
|
+
for (; ; ) {
|
|
2760
|
+
const { done, value } = await reader.read();
|
|
2761
|
+
if (done)
|
|
2762
|
+
break;
|
|
2763
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2764
|
+
let newline = buffer.search(/\r\n|\r|\n/);
|
|
2765
|
+
while (newline !== -1) {
|
|
2766
|
+
const line = buffer.slice(0, newline);
|
|
2767
|
+
const width = buffer.startsWith("\r\n", newline) ? 2 : 1;
|
|
2768
|
+
if (width === 1 && buffer[newline] === "\r" && newline === buffer.length - 1)
|
|
2769
|
+
break;
|
|
2770
|
+
buffer = buffer.slice(newline + width);
|
|
2771
|
+
yield* take(line);
|
|
2772
|
+
newline = buffer.search(/\r\n|\r|\n/);
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
buffer += decoder.decode();
|
|
2776
|
+
if (buffer !== "")
|
|
2777
|
+
yield* take(buffer);
|
|
2778
|
+
} finally {
|
|
2779
|
+
reader.releaseLock();
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
var DEFAULT_RETRY_MS = 1e3;
|
|
2783
|
+
var DEFAULT_MAX_RECONNECTS = 10;
|
|
2784
|
+
var WebhookListeners = class {
|
|
2785
|
+
transport;
|
|
2786
|
+
constructor(transport) {
|
|
2787
|
+
this.transport = transport;
|
|
2788
|
+
}
|
|
2789
|
+
/** Start a listener. `events` defaults to every event (`["*"]`). Needs `write:webhooks`. */
|
|
2790
|
+
start(body = {}, opts = {}) {
|
|
2791
|
+
return this.transport.request({
|
|
2792
|
+
method: "POST",
|
|
2793
|
+
path: "/webhook_listeners",
|
|
2794
|
+
body: { ...body, organizationId: this.transport.organizationId(body.organizationId) },
|
|
2795
|
+
options: opts
|
|
2796
|
+
});
|
|
2797
|
+
}
|
|
2798
|
+
/** Send a test event of a concrete type to the listener; it arrives on the stream with `test: true`. */
|
|
2799
|
+
sendTest(id, body, opts = {}) {
|
|
2800
|
+
return this.transport.request({
|
|
2801
|
+
method: "POST",
|
|
2802
|
+
path: `/webhook_listeners/${encodeURIComponent(id)}/test`,
|
|
2803
|
+
body: { ...body, organizationId: this.transport.organizationId(body.organizationId) },
|
|
2804
|
+
options: opts
|
|
2805
|
+
});
|
|
2806
|
+
}
|
|
2807
|
+
/** Stop the listener and delete its events. */
|
|
2808
|
+
async stop(id, query = {}, opts = {}) {
|
|
2809
|
+
await this.transport.request({
|
|
2810
|
+
method: "DELETE",
|
|
2811
|
+
path: `/webhook_listeners/${encodeURIComponent(id)}`,
|
|
2812
|
+
query: { organizationId: this.transport.organizationId(query.organizationId) },
|
|
2813
|
+
options: opts
|
|
2814
|
+
});
|
|
2815
|
+
}
|
|
2816
|
+
/** Open the stream once and return the raw Response (text/event-stream). Most callers want `events()`. */
|
|
2817
|
+
open(id, opts = {}) {
|
|
2818
|
+
return this.transport.request({
|
|
2819
|
+
method: "GET",
|
|
2820
|
+
path: `/webhook_listeners/${encodeURIComponent(id)}/events`,
|
|
2821
|
+
query: { organizationId: this.transport.organizationId(opts.organizationId) },
|
|
2822
|
+
headers: {
|
|
2823
|
+
Accept: "text/event-stream",
|
|
2824
|
+
...opts.after !== void 0 ? { "Last-Event-ID": String(opts.after) } : {}
|
|
2825
|
+
},
|
|
2826
|
+
raw: true,
|
|
2827
|
+
// The stream stays open for minutes: no per-attempt timeout once the answer has started.
|
|
2828
|
+
options: { signal: opts.signal, timeoutMs: 3e4 }
|
|
2829
|
+
});
|
|
2830
|
+
}
|
|
2831
|
+
/**
|
|
2832
|
+
* The listener's events, in order, for as long as the listener lives: reconnects on `event: reconnect` and on a
|
|
2833
|
+
* dropped stream, resuming after the last event it yielded. Ends when `signal` aborts. Throws NotFoundError when
|
|
2834
|
+
* the listener was stopped or swept, and the last error after `maxReconnects` failed reconnects in a row.
|
|
2835
|
+
*/
|
|
2836
|
+
async *events(id, opts = {}) {
|
|
2837
|
+
let after = opts.after;
|
|
2838
|
+
let retryMs = DEFAULT_RETRY_MS;
|
|
2839
|
+
let failures = 0;
|
|
2840
|
+
let first = true;
|
|
2841
|
+
const maxReconnects = opts.maxReconnects ?? DEFAULT_MAX_RECONNECTS;
|
|
2842
|
+
while (!opts.signal?.aborted) {
|
|
2843
|
+
if (!first)
|
|
2844
|
+
opts.onReconnect?.(after);
|
|
2845
|
+
first = false;
|
|
2846
|
+
let res;
|
|
2847
|
+
try {
|
|
2848
|
+
res = await this.open(id, { after, organizationId: opts.organizationId, signal: opts.signal });
|
|
2849
|
+
} catch (err) {
|
|
2850
|
+
if (opts.signal?.aborted)
|
|
2851
|
+
return;
|
|
2852
|
+
if (err instanceof error_1.NotFoundError || err instanceof error_1.ExayardError && err.status >= 400 && err.status < 500 && err.status !== 429)
|
|
2853
|
+
throw err;
|
|
2854
|
+
failures += 1;
|
|
2855
|
+
if (failures > maxReconnects)
|
|
2856
|
+
throw err;
|
|
2857
|
+
await sleepUnlessAborted(Math.max(retryMs, (0, retry_1.retryDelayMs)(failures)), opts.signal);
|
|
2858
|
+
continue;
|
|
2859
|
+
}
|
|
2860
|
+
if (!res.body)
|
|
2861
|
+
throw new Error("Exayard: the listener stream answered with no body.");
|
|
2862
|
+
failures = 0;
|
|
2863
|
+
let reconnectAtOnce = false;
|
|
2864
|
+
try {
|
|
2865
|
+
for await (const message of parseServerSentEvents(res.body)) {
|
|
2866
|
+
if (message.retry !== void 0)
|
|
2867
|
+
retryMs = message.retry;
|
|
2868
|
+
if (message.event === "reconnect") {
|
|
2869
|
+
reconnectAtOnce = true;
|
|
2870
|
+
break;
|
|
2871
|
+
}
|
|
2872
|
+
if (message.event !== "webhook" || message.data === "")
|
|
2873
|
+
continue;
|
|
2874
|
+
const event = JSON.parse(message.data);
|
|
2875
|
+
after = event.seq;
|
|
2876
|
+
yield event;
|
|
2877
|
+
}
|
|
2878
|
+
} catch (err) {
|
|
2879
|
+
if (opts.signal?.aborted)
|
|
2880
|
+
return;
|
|
2881
|
+
if (err instanceof SyntaxError)
|
|
2882
|
+
throw err;
|
|
2883
|
+
} finally {
|
|
2884
|
+
await res.body.cancel().catch(() => void 0);
|
|
2885
|
+
}
|
|
2886
|
+
if (!reconnectAtOnce)
|
|
2887
|
+
await sleepUnlessAborted(retryMs, opts.signal);
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
};
|
|
2891
|
+
exports2.WebhookListeners = WebhookListeners;
|
|
2892
|
+
var sleepUnlessAborted = async (ms, signal) => {
|
|
2893
|
+
try {
|
|
2894
|
+
await (0, retry_1.sleep)(ms, signal);
|
|
2895
|
+
} catch {
|
|
2896
|
+
}
|
|
2897
|
+
};
|
|
2898
|
+
}
|
|
2899
|
+
});
|
|
2900
|
+
|
|
2901
|
+
// ../sdk/dist/options.js
|
|
2902
|
+
var require_options = __commonJS({
|
|
2903
|
+
"../sdk/dist/options.js"(exports2) {
|
|
2904
|
+
"use strict";
|
|
2905
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2906
|
+
exports2.isValidEndUser = exports2.END_USER_MAX_LENGTH = exports2.END_USER_HEADER = void 0;
|
|
2907
|
+
exports2.END_USER_HEADER = "Exayard-End-User";
|
|
2908
|
+
exports2.END_USER_MAX_LENGTH = 128;
|
|
2909
|
+
var END_USER_PATTERN = new RegExp(`^[\\x20-\\x7E]{1,${exports2.END_USER_MAX_LENGTH}}$`);
|
|
2910
|
+
var isValidEndUser = (value) => END_USER_PATTERN.test(value);
|
|
2911
|
+
exports2.isValidEndUser = isValidEndUser;
|
|
81
2912
|
}
|
|
82
2913
|
});
|
|
83
2914
|
|
|
@@ -86,7 +2917,48 @@ var require_webhooks = __commonJS({
|
|
|
86
2917
|
"../sdk/dist/webhooks.js"(exports2) {
|
|
87
2918
|
"use strict";
|
|
88
2919
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
89
|
-
exports2.constructWebhookEvent = exports2.WebhookSignatureError = void 0;
|
|
2920
|
+
exports2.constructWebhookEvent = exports2.signWebhookPayload = exports2.DEFAULT_WEBHOOK_TOLERANCE_SECONDS = exports2.WebhookSignatureError = exports2.WEBHOOK_SIGNATURE_HEADER = exports2.WEBHOOK_EVENT_TYPES = void 0;
|
|
2921
|
+
exports2.WEBHOOK_EVENT_TYPES = [
|
|
2922
|
+
"project.created",
|
|
2923
|
+
"project.updated",
|
|
2924
|
+
"project.archived",
|
|
2925
|
+
"assessment.started",
|
|
2926
|
+
"assessment.completed",
|
|
2927
|
+
"assessment.cancelled",
|
|
2928
|
+
"assessment.failed",
|
|
2929
|
+
"estimate.generated",
|
|
2930
|
+
"bid.generated",
|
|
2931
|
+
"bid.submitted",
|
|
2932
|
+
"bid.won",
|
|
2933
|
+
"bid.lost",
|
|
2934
|
+
"bid.accepted",
|
|
2935
|
+
"bid.declined",
|
|
2936
|
+
"bid.sent",
|
|
2937
|
+
"bid.viewed",
|
|
2938
|
+
"bid.commented",
|
|
2939
|
+
"file.processed",
|
|
2940
|
+
"addendum.uploaded",
|
|
2941
|
+
"quote.requested",
|
|
2942
|
+
"quote.received",
|
|
2943
|
+
"quote.accepted",
|
|
2944
|
+
"quote.rejected",
|
|
2945
|
+
"quote.expired",
|
|
2946
|
+
"conversation.started",
|
|
2947
|
+
"conversation.handoff_requested",
|
|
2948
|
+
"conversation.closed",
|
|
2949
|
+
"lead.captured",
|
|
2950
|
+
"contact.created",
|
|
2951
|
+
"appointment.booked",
|
|
2952
|
+
"appointment.rescheduled",
|
|
2953
|
+
"appointment.cancelled",
|
|
2954
|
+
"call.completed",
|
|
2955
|
+
"call.missed",
|
|
2956
|
+
"review.requested",
|
|
2957
|
+
"job.written_back",
|
|
2958
|
+
"payment.captured",
|
|
2959
|
+
"payment.failed"
|
|
2960
|
+
];
|
|
2961
|
+
exports2.WEBHOOK_SIGNATURE_HEADER = "Exayard-Signature";
|
|
90
2962
|
var WebhookSignatureError2 = class extends Error {
|
|
91
2963
|
code;
|
|
92
2964
|
constructor(code, message) {
|
|
@@ -96,27 +2968,27 @@ var require_webhooks = __commonJS({
|
|
|
96
2968
|
}
|
|
97
2969
|
};
|
|
98
2970
|
exports2.WebhookSignatureError = WebhookSignatureError2;
|
|
99
|
-
|
|
2971
|
+
exports2.DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 5 * 60;
|
|
100
2972
|
var parseSignatureHeader = (header) => {
|
|
101
|
-
const parts = header.split(",").map((s) => s.trim());
|
|
102
2973
|
let t;
|
|
103
|
-
|
|
104
|
-
for (const part of
|
|
2974
|
+
const v1 = [];
|
|
2975
|
+
for (const part of header.split(",").map((s) => s.trim())) {
|
|
105
2976
|
const eq = part.indexOf("=");
|
|
106
2977
|
if (eq === -1)
|
|
107
2978
|
continue;
|
|
108
2979
|
const k = part.slice(0, eq);
|
|
109
2980
|
const v = part.slice(eq + 1);
|
|
110
|
-
if (k === "t")
|
|
111
|
-
t =
|
|
112
|
-
if (k === "v1")
|
|
113
|
-
v1
|
|
2981
|
+
if (k === "t" && /^\d+$/.test(v))
|
|
2982
|
+
t = Number(v);
|
|
2983
|
+
if (k === "v1" && v)
|
|
2984
|
+
v1.push(v);
|
|
114
2985
|
}
|
|
115
|
-
if (t === void 0 ||
|
|
116
|
-
throw new WebhookSignatureError2("malformed_header",
|
|
2986
|
+
if (t === void 0 || v1.length === 0) {
|
|
2987
|
+
throw new WebhookSignatureError2("malformed_header", 'Exayard-Signature must be "t=<unix seconds>,v1=<digest>".');
|
|
117
2988
|
}
|
|
118
2989
|
return { t, v1 };
|
|
119
2990
|
};
|
|
2991
|
+
var toText = (payload) => typeof payload === "string" ? payload : new TextDecoder().decode(payload);
|
|
120
2992
|
var hmacSha256Base64 = async (secret, payload) => {
|
|
121
2993
|
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
122
2994
|
const digest = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(payload));
|
|
@@ -134,17 +3006,27 @@ var require_webhooks = __commonJS({
|
|
|
134
3006
|
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
135
3007
|
return result === 0;
|
|
136
3008
|
};
|
|
137
|
-
var
|
|
3009
|
+
var signWebhookPayload2 = async (payload, secret, options = {}) => {
|
|
3010
|
+
const t = options.timestamp ?? Math.floor(Date.now() / 1e3);
|
|
3011
|
+
const v1 = await hmacSha256Base64(secret, `${t}.${toText(payload)}`);
|
|
3012
|
+
return `t=${t},v1=${v1}`;
|
|
3013
|
+
};
|
|
3014
|
+
exports2.signWebhookPayload = signWebhookPayload2;
|
|
3015
|
+
var constructWebhookEvent2 = async (payload, signatureHeader, secret, options = {}) => {
|
|
3016
|
+
if (!signatureHeader)
|
|
3017
|
+
throw new WebhookSignatureError2("malformed_header", "The Exayard-Signature header is missing.");
|
|
138
3018
|
const { t, v1 } = parseSignatureHeader(signatureHeader);
|
|
3019
|
+
const tolerance = options.toleranceSeconds ?? exports2.DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
|
|
139
3020
|
const nowSec = Math.floor((options.now ?? Date.now()) / 1e3);
|
|
140
|
-
if (Math.abs(nowSec - t) >
|
|
141
|
-
throw new WebhookSignatureError2("replay_window_exceeded", `Signature timestamp ${t} is outside the ${
|
|
3021
|
+
if (Math.abs(nowSec - t) > tolerance) {
|
|
3022
|
+
throw new WebhookSignatureError2("replay_window_exceeded", `Signature timestamp ${t} is outside the ${tolerance}-second window (now=${nowSec}).`);
|
|
142
3023
|
}
|
|
143
|
-
const
|
|
144
|
-
|
|
3024
|
+
const text = toText(payload);
|
|
3025
|
+
const expected = await hmacSha256Base64(secret, `${t}.${text}`);
|
|
3026
|
+
if (!v1.some((candidate) => safeCompare(expected, candidate))) {
|
|
145
3027
|
throw new WebhookSignatureError2("invalid_signature", "HMAC digest mismatch. Check the endpoint secret.");
|
|
146
3028
|
}
|
|
147
|
-
return JSON.parse(
|
|
3029
|
+
return JSON.parse(text);
|
|
148
3030
|
};
|
|
149
3031
|
exports2.constructWebhookEvent = constructWebhookEvent2;
|
|
150
3032
|
}
|
|
@@ -156,7 +3038,11 @@ var require_client = __commonJS({
|
|
|
156
3038
|
"use strict";
|
|
157
3039
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
158
3040
|
exports2.Exayard = void 0;
|
|
3041
|
+
var operations_1 = require_operations();
|
|
159
3042
|
var error_1 = require_error();
|
|
3043
|
+
var listeners_1 = require_listeners();
|
|
3044
|
+
var options_1 = require_options();
|
|
3045
|
+
var retry_1 = require_retry();
|
|
160
3046
|
var webhooks_1 = require_webhooks();
|
|
161
3047
|
var UNSAFE_METHODS = /* @__PURE__ */ new Set(["POST", "PATCH", "PUT", "DELETE"]);
|
|
162
3048
|
var randomIdempotencyKey = () => {
|
|
@@ -165,24 +3051,91 @@ var require_client = __commonJS({
|
|
|
165
3051
|
}
|
|
166
3052
|
return `idem_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
167
3053
|
};
|
|
3054
|
+
var envApiKey = () => {
|
|
3055
|
+
if (typeof process === "undefined" || !process.env)
|
|
3056
|
+
return void 0;
|
|
3057
|
+
const value = process.env.EXAYARD_API_KEY;
|
|
3058
|
+
return value && value.trim() !== "" ? value.trim() : void 0;
|
|
3059
|
+
};
|
|
3060
|
+
var checkEndUser = (value) => {
|
|
3061
|
+
if (value === void 0)
|
|
3062
|
+
return void 0;
|
|
3063
|
+
if (!(0, options_1.isValidEndUser)(value)) {
|
|
3064
|
+
throw new TypeError(`Exayard: endUser must be 1 to ${options_1.END_USER_MAX_LENGTH} printable characters.`);
|
|
3065
|
+
}
|
|
3066
|
+
return value;
|
|
3067
|
+
};
|
|
3068
|
+
var transportProblem = (code, status, detail, requestId) => ({
|
|
3069
|
+
type: `https://developers.exayard.com/concepts/errors#${code}`,
|
|
3070
|
+
title: code === "transport_error" ? "Transport Error" : code === "timeout" ? "Timeout" : "Connection Error",
|
|
3071
|
+
status,
|
|
3072
|
+
detail,
|
|
3073
|
+
code,
|
|
3074
|
+
doc_url: `https://developers.exayard.com/concepts/errors#${code}`,
|
|
3075
|
+
...requestId ? { request_id: requestId } : {}
|
|
3076
|
+
});
|
|
168
3077
|
var Exayard2 = class {
|
|
169
|
-
|
|
170
|
-
bearerToken;
|
|
3078
|
+
credential;
|
|
171
3079
|
baseUrl;
|
|
172
3080
|
timeoutMs;
|
|
3081
|
+
maxRetries;
|
|
173
3082
|
fetchImpl;
|
|
3083
|
+
defaultOrganizationId;
|
|
3084
|
+
defaultEndUser;
|
|
174
3085
|
constructor(opts = {}) {
|
|
175
|
-
|
|
176
|
-
|
|
3086
|
+
const credential = opts.apiKey ?? opts.bearerToken ?? envApiKey();
|
|
3087
|
+
if (!credential) {
|
|
3088
|
+
throw new Error("Exayard: pass `apiKey` (or set EXAYARD_API_KEY) or `bearerToken` \u2014 see https://developers.exayard.com/api-reference.");
|
|
177
3089
|
}
|
|
178
|
-
this.
|
|
179
|
-
this.bearerToken = opts.bearerToken;
|
|
3090
|
+
this.credential = credential;
|
|
180
3091
|
this.baseUrl = (opts.baseUrl ?? "https://api.exayard.com/v1").replace(/\/$/, "");
|
|
181
3092
|
this.timeoutMs = opts.timeoutMs ?? 6e4;
|
|
182
|
-
this.
|
|
3093
|
+
this.maxRetries = Math.max(0, opts.maxRetries ?? retry_1.DEFAULT_MAX_RETRIES);
|
|
3094
|
+
this.fetchImpl = opts.fetch ?? ((input, init) => fetch(input, init));
|
|
3095
|
+
this.defaultOrganizationId = opts.organizationId;
|
|
3096
|
+
this.defaultEndUser = checkEndUser(opts.endUser);
|
|
3097
|
+
}
|
|
3098
|
+
/** The company a call names, else the client's default; undefined lets the key imply it. */
|
|
3099
|
+
organizationFor(named) {
|
|
3100
|
+
return named ?? this.defaultOrganizationId;
|
|
3101
|
+
}
|
|
3102
|
+
// One attempt: the per-attempt timeout covers the wait for the answer's headers only, so a stream body is never
|
|
3103
|
+
// cut by it; the caller's signal aborts both (its listener stays while the body may still be read).
|
|
3104
|
+
async attempt(url, init, timeoutMs, signal) {
|
|
3105
|
+
const ac = new AbortController();
|
|
3106
|
+
let timedOut = false;
|
|
3107
|
+
const onAbort = () => ac.abort(signal?.reason);
|
|
3108
|
+
if (signal?.aborted)
|
|
3109
|
+
ac.abort(signal.reason);
|
|
3110
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
3111
|
+
const timer = timeoutMs > 0 ? setTimeout(() => (timedOut = true, ac.abort()), timeoutMs) : void 0;
|
|
3112
|
+
try {
|
|
3113
|
+
return await this.fetchImpl(url, { ...init, signal: ac.signal });
|
|
3114
|
+
} catch (err) {
|
|
3115
|
+
signal?.removeEventListener("abort", onAbort);
|
|
3116
|
+
if (signal?.aborted)
|
|
3117
|
+
throw err;
|
|
3118
|
+
throw new error_1.APIConnectionError(timedOut ? transportProblem("timeout", 0, `No answer within ${timeoutMs} ms.`) : transportProblem("connection_error", 0, err?.message || "The connection failed."));
|
|
3119
|
+
} finally {
|
|
3120
|
+
if (timer)
|
|
3121
|
+
clearTimeout(timer);
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
async toError(res) {
|
|
3125
|
+
const retryAfter = (0, retry_1.parseRetryAfter)(res.headers.get("Retry-After"));
|
|
3126
|
+
const ct = res.headers.get("Content-Type") ?? "";
|
|
3127
|
+
if (ct.includes("problem+json") || ct.includes("application/json")) {
|
|
3128
|
+
const body2 = await res.json().catch(() => null);
|
|
3129
|
+
if (body2 && typeof body2 === "object")
|
|
3130
|
+
return (0, error_1.errorFromProblem)({ ...body2, status: body2.status ?? res.status }, retryAfter);
|
|
3131
|
+
}
|
|
3132
|
+
const text = await res.text().catch(() => "");
|
|
3133
|
+
const body = transportProblem("transport_error", res.status, text.slice(0, 500) || `HTTP ${res.status}`, res.headers.get("X-Request-Id") ?? void 0);
|
|
3134
|
+
return (0, error_1.errorFromProblem)({ ...body, code: res.status === 429 ? "rate_limited" : body.code }, retryAfter);
|
|
183
3135
|
}
|
|
184
3136
|
// Internal request primitive. Public resource methods compose this.
|
|
185
3137
|
async request(opts) {
|
|
3138
|
+
const call = opts.options ?? {};
|
|
186
3139
|
const url = new URL(`${this.baseUrl}${opts.path}`);
|
|
187
3140
|
if (opts.query) {
|
|
188
3141
|
for (const [k, v] of Object.entries(opts.query)) {
|
|
@@ -192,86 +3145,138 @@ var require_client = __commonJS({
|
|
|
192
3145
|
}
|
|
193
3146
|
const headers = {
|
|
194
3147
|
Accept: "application/json",
|
|
195
|
-
Authorization: `Bearer ${this.
|
|
3148
|
+
Authorization: `Bearer ${this.credential}`,
|
|
3149
|
+
...opts.headers
|
|
196
3150
|
};
|
|
197
3151
|
if (opts.body !== void 0)
|
|
198
3152
|
headers["Content-Type"] = "application/json";
|
|
199
|
-
const
|
|
200
|
-
if (
|
|
201
|
-
headers[
|
|
202
|
-
|
|
3153
|
+
const endUser = checkEndUser(call.endUser) ?? this.defaultEndUser;
|
|
3154
|
+
if (endUser !== void 0)
|
|
3155
|
+
headers[options_1.END_USER_HEADER] = endUser;
|
|
3156
|
+
if (call.idempotencyKey)
|
|
3157
|
+
headers["Idempotency-Key"] = call.idempotencyKey;
|
|
3158
|
+
else if (UNSAFE_METHODS.has(opts.method))
|
|
203
3159
|
headers["Idempotency-Key"] = randomIdempotencyKey();
|
|
3160
|
+
const init = {
|
|
3161
|
+
method: opts.method,
|
|
3162
|
+
headers,
|
|
3163
|
+
body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
|
|
3164
|
+
};
|
|
3165
|
+
const maxRetries = Math.max(0, call.maxRetries ?? this.maxRetries);
|
|
3166
|
+
const timeoutMs = call.timeoutMs ?? this.timeoutMs;
|
|
3167
|
+
for (let attempt = 0; ; attempt++) {
|
|
3168
|
+
let res;
|
|
3169
|
+
try {
|
|
3170
|
+
res = await this.attempt(url.toString(), init, timeoutMs, call.signal);
|
|
3171
|
+
} catch (err) {
|
|
3172
|
+
if (err instanceof error_1.APIConnectionError && attempt < maxRetries) {
|
|
3173
|
+
await (0, retry_1.sleep)((0, retry_1.retryDelayMs)(attempt + 1), call.signal);
|
|
3174
|
+
continue;
|
|
3175
|
+
}
|
|
3176
|
+
throw err;
|
|
3177
|
+
}
|
|
3178
|
+
if (res.ok) {
|
|
3179
|
+
if (opts.raw)
|
|
3180
|
+
return res;
|
|
3181
|
+
if (res.status === 204)
|
|
3182
|
+
return void 0;
|
|
3183
|
+
const text = await res.text();
|
|
3184
|
+
return text === "" ? void 0 : JSON.parse(text);
|
|
3185
|
+
}
|
|
3186
|
+
if ((0, retry_1.isRetryableStatus)(res.status) && attempt < maxRetries) {
|
|
3187
|
+
const retryAfter = (0, retry_1.parseRetryAfter)(res.headers.get("Retry-After"));
|
|
3188
|
+
if (retryAfter === void 0 || retryAfter <= retry_1.MAX_RETRY_AFTER_SECONDS) {
|
|
3189
|
+
await res.body?.cancel().catch(() => void 0);
|
|
3190
|
+
await (0, retry_1.sleep)((0, retry_1.retryDelayMs)(attempt + 1, retryAfter), call.signal);
|
|
3191
|
+
continue;
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
throw await this.toError(res);
|
|
204
3195
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
|
|
3196
|
+
}
|
|
3197
|
+
/**
|
|
3198
|
+
* Call any API operation by its name: the spec's operationId, which is also
|
|
3199
|
+
* its MCP tool name and its CLI command. The input is the operation's path,
|
|
3200
|
+
* query and body fields merged flat, the same input its MCP tool takes.
|
|
3201
|
+
*/
|
|
3202
|
+
call(name, input, opts = {}) {
|
|
3203
|
+
const op = operations_1.OPERATIONS[name];
|
|
3204
|
+
if (!op)
|
|
3205
|
+
throw new Error(`Exayard: no operation named ${String(name)}`);
|
|
3206
|
+
const args = { ...input };
|
|
3207
|
+
const known = new Set(op.params.map((param) => param.name));
|
|
3208
|
+
const unknown = Object.keys(args).filter((field) => !known.has(field));
|
|
3209
|
+
if (unknown.length > 0)
|
|
3210
|
+
throw new Error(`Exayard: ${name} does not take ${unknown.join(", ")}`);
|
|
3211
|
+
if (known.has("organizationId") && args.organizationId === void 0 && this.defaultOrganizationId) {
|
|
3212
|
+
args.organizationId = this.defaultOrganizationId;
|
|
217
3213
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
3214
|
+
let path = op.path;
|
|
3215
|
+
const query = {};
|
|
3216
|
+
const body = {};
|
|
3217
|
+
for (const param of op.params) {
|
|
3218
|
+
const value = args[param.name];
|
|
3219
|
+
if (param.in === "path") {
|
|
3220
|
+
if (value === void 0 || value === null || value === "")
|
|
3221
|
+
throw new Error(`Exayard: ${name} needs ${param.name}`);
|
|
3222
|
+
path = path.replace(`{${param.name}}`, encodeURIComponent(String(value)));
|
|
3223
|
+
} else if (value !== void 0) {
|
|
3224
|
+
if (param.in === "query")
|
|
3225
|
+
query[param.name] = value;
|
|
3226
|
+
else
|
|
3227
|
+
body[param.name] = value;
|
|
223
3228
|
}
|
|
224
|
-
const text = await res.text().catch(() => "");
|
|
225
|
-
throw new error_1.ExayardError({
|
|
226
|
-
type: "https://developers.exayard.com/concepts/errors#transport_error",
|
|
227
|
-
title: "Transport Error",
|
|
228
|
-
status: res.status,
|
|
229
|
-
detail: text.slice(0, 500) || `HTTP ${res.status}`,
|
|
230
|
-
code: "transport_error",
|
|
231
|
-
doc_url: "https://developers.exayard.com/concepts/errors#transport_error",
|
|
232
|
-
request_id: res.headers.get("X-Request-Id") ?? void 0
|
|
233
|
-
});
|
|
234
3229
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
3230
|
+
return this.request({
|
|
3231
|
+
method: op.method,
|
|
3232
|
+
path,
|
|
3233
|
+
query,
|
|
3234
|
+
body: op.body ? body : void 0,
|
|
3235
|
+
raw: op.response === "stream",
|
|
3236
|
+
options: opts
|
|
3237
|
+
});
|
|
238
3238
|
}
|
|
3239
|
+
/**
|
|
3240
|
+
* Every API operation as a typed method, generated from the API spec:
|
|
3241
|
+
* `exa.api.listVendorQuotes({ id: projectId })`.
|
|
3242
|
+
*/
|
|
3243
|
+
api = Object.fromEntries(Object.entries(operations_1.OPERATION_METHODS).map(([method, name]) => [
|
|
3244
|
+
method,
|
|
3245
|
+
(input, opts) => this.call(name, input, opts)
|
|
3246
|
+
]));
|
|
239
3247
|
// ---------------------------------------------------------------------------
|
|
240
|
-
// Resource surface — a curated
|
|
241
|
-
// most often hit.
|
|
242
|
-
//
|
|
3248
|
+
// Resource surface — a curated layer over the generated operations for the
|
|
3249
|
+
// flows agents and integrations most often hit. Each method is a call() of the
|
|
3250
|
+
// operation it names, so its input and output types come from the API spec.
|
|
3251
|
+
// Everything else is on `exa.api` and `exa.call`. `organizationId` is optional
|
|
3252
|
+
// everywhere: the key implies the company, or the client's default fills it.
|
|
243
3253
|
// ---------------------------------------------------------------------------
|
|
244
3254
|
me = {
|
|
245
|
-
get: () => this.
|
|
3255
|
+
get: () => this.call("get_me", {})
|
|
246
3256
|
};
|
|
247
3257
|
projects = {
|
|
248
|
-
// GET /v1/projects returns a raw array
|
|
249
|
-
//
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
archive: (id, query) => this.request({ method: "POST", path: `/projects/${id}/archive`, query })
|
|
3258
|
+
// GET /v1/projects returns a raw array; there is no { items, next_cursor }
|
|
3259
|
+
// envelope and no limit/cursor pagination yet.
|
|
3260
|
+
list: (query) => this.call("list_projects", query),
|
|
3261
|
+
get: (id, query = {}) => this.call("get_project", { id, ...query }),
|
|
3262
|
+
create: (body, opts = {}) => this.call("create_project", body, opts),
|
|
3263
|
+
export: (id, query = {}) => this.call("export_project", { id, ...query }),
|
|
3264
|
+
archive: (id, query = {}) => this.call("archive_project", { id, ...query })
|
|
256
3265
|
};
|
|
257
3266
|
// File upload is a 3-step dance: presign (get an R2 upload URL) → PUT the
|
|
258
3267
|
// bytes straight to R2 → confirm (which also kicks off PDF page extraction).
|
|
259
3268
|
// `upload` wraps all three. A PDF becomes pages asynchronously after confirm —
|
|
260
3269
|
// poll pages.list() until processingStatus is 'complete' before proposing/running.
|
|
3270
|
+
// The project decides the organization, so none of the three takes one.
|
|
261
3271
|
files = {
|
|
262
|
-
presign: (body) => this.
|
|
263
|
-
|
|
264
|
-
path: "/files",
|
|
265
|
-
body
|
|
266
|
-
}),
|
|
267
|
-
confirm: (fileId, r2Key) => this.request({ method: "POST", path: `/files/${fileId}/confirm`, body: { r2Key } }),
|
|
3272
|
+
presign: (body) => this.call("presign_file_upload", body),
|
|
3273
|
+
confirm: (fileId, r2Key) => this.call("confirm_file_upload", { id: fileId, r2Key }),
|
|
268
3274
|
// Convenience: presign → PUT bytes to the presigned R2 URL → confirm.
|
|
269
3275
|
// The PUT goes directly to R2 (a different host, self-authenticated by the
|
|
270
3276
|
// signed URL) — not through the /v1 request primitive.
|
|
271
3277
|
upload: async (body) => {
|
|
272
3278
|
const buf = body.bytes instanceof ArrayBuffer ? new Uint8Array(body.bytes) : body.bytes;
|
|
273
3279
|
const { fileId, uploadUrl, r2Key } = await this.files.presign({
|
|
274
|
-
organizationId: body.organizationId,
|
|
275
3280
|
projectId: body.projectId,
|
|
276
3281
|
filename: body.filename,
|
|
277
3282
|
mimeType: body.mimeType,
|
|
@@ -302,49 +3307,120 @@ var require_client = __commonJS({
|
|
|
302
3307
|
// List a project's pages (grouped by file). Use to discover pageIds + poll
|
|
303
3308
|
// each file's processingStatus until extraction finishes.
|
|
304
3309
|
pages = {
|
|
305
|
-
list: (projectId, query) => this.
|
|
3310
|
+
list: (projectId, query = {}) => this.call("list_pages", { id: projectId, ...query })
|
|
306
3311
|
};
|
|
307
3312
|
// AI takeoff assessments. Reads resolve the org from the project/assessment
|
|
308
|
-
// (no organizationId). `
|
|
3313
|
+
// (no organizationId). `run` kicks off an AI run on an existing project's
|
|
309
3314
|
// pages — pair with the assessment.completed webhook + latest()/takeoffSummary
|
|
310
3315
|
// to pull results once it finishes.
|
|
311
3316
|
assessments = {
|
|
312
|
-
list: (projectId) => this.
|
|
313
|
-
latest: (projectId) => this.
|
|
314
|
-
get: (id) => this.
|
|
315
|
-
takeoffSummary: (projectId) => this.
|
|
3317
|
+
list: (projectId) => this.call("list_assessments", { id: projectId }),
|
|
3318
|
+
latest: (projectId) => this.call("get_latest_assessment", { id: projectId }),
|
|
3319
|
+
get: (id) => this.call("get_assessment", { id }),
|
|
3320
|
+
takeoffSummary: (projectId) => this.call("get_takeoff_summary", { id: projectId }),
|
|
316
3321
|
// Ask the AI what to measure: a natural-language prompt + pageIds → a
|
|
317
3322
|
// proposed `elements` array (id/name/category/hexColor) you can pass
|
|
318
3323
|
// straight to run(). The bridge for callers who don't already know the
|
|
319
|
-
// elements.
|
|
320
|
-
propose: (projectId, body) => this.
|
|
3324
|
+
// elements.
|
|
3325
|
+
propose: (projectId, body) => this.call("propose_analysis", { id: projectId, ...body }),
|
|
321
3326
|
// Run an AI takeoff analysis on an existing project and let it complete
|
|
322
3327
|
// end-to-end (no human approval step). You specify exactly what to detect
|
|
323
3328
|
// via `elements` (each: id, name, category area|linear|count, hexColor) on
|
|
324
|
-
// the given pageIds. Returns { assessmentId,
|
|
325
|
-
//
|
|
3329
|
+
// the given pageIds. Returns { assessmentId, pageIds }; track completion via
|
|
3330
|
+
// the assessment.completed webhook + latest()/takeoffSummary.
|
|
326
3331
|
//
|
|
327
|
-
// (
|
|
328
|
-
//
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
3332
|
+
// (The auto-detect POST /assessments path pauses at awaiting_approval,
|
|
3333
|
+
// which needs a human, so it isn't wrapped here; it is exa.api.createAssessment.)
|
|
3334
|
+
run: (projectId, body, opts = {}) => this.call("run_approved_analysis", { id: projectId, ...body }, opts)
|
|
3335
|
+
};
|
|
3336
|
+
// Generate an estimate from a project's takeoff and persist it as a document.
|
|
3337
|
+
// Blocking: the estimator typically runs 30–60s, which is at the default
|
|
3338
|
+
// 60s client timeout — raise `timeoutMs` when constructing the client.
|
|
3339
|
+
//
|
|
3340
|
+
// The saved document is PRIVATE to your organization. `share: true` also
|
|
3341
|
+
// publishes it on a public link anyone holding the URL can read, and makes
|
|
3342
|
+
// `documentUrl` the public path instead of the signed-in one. Set it only
|
|
3343
|
+
// when the customer asked for a shareable link; `documents.setSharing` can
|
|
3344
|
+
// share or unshare later either way.
|
|
3345
|
+
//
|
|
3346
|
+
// (The streaming sibling POST /v1/projects/{id}/estimates/generate persists
|
|
3347
|
+
// no document and has no `share` flag; it is exa.api.generateEstimate, which
|
|
3348
|
+
// returns the event-stream Response.)
|
|
3349
|
+
estimates = {
|
|
3350
|
+
create: (projectId, body, opts = {}) => this.call("create_estimate", { id: projectId, ...body }, opts)
|
|
3351
|
+
};
|
|
3352
|
+
// Same estimator wrapped with bid-formatting instructions, persisted as a
|
|
3353
|
+
// bid document. Same blocking timing and same private-by-default `share`
|
|
3354
|
+
// semantics as estimates.create. When `measurementContext` is omitted the
|
|
3355
|
+
// takeoff is auto-loaded server-side, which can 422 on line items with
|
|
3356
|
+
// missing formula variables — retry with `ignoreMissingVariables: true` to
|
|
3357
|
+
// skip them (the skipped rows come back in `warnings`).
|
|
3358
|
+
bids = {
|
|
3359
|
+
create: (projectId, body, opts = {}) => this.call("create_bid", { id: projectId, ...body }, opts)
|
|
3360
|
+
};
|
|
3361
|
+
// Documents (estimates, bids, and anything else saved to a project) plus the
|
|
3362
|
+
// sharing switch. Documents are private to the organization until someone
|
|
3363
|
+
// enables sharing; enabling mints a secret and publishes a link readable by
|
|
3364
|
+
// anyone on the internet who has the URL — no sign-in, no expiry.
|
|
3365
|
+
//
|
|
3366
|
+
// Disabling revokes access but KEEPS the secret, so re-enabling later revives
|
|
3367
|
+
// the same URL. There is no rotate operation: a leaked link means the
|
|
3368
|
+
// document is burned, not that a disable/enable cycle gives you a fresh one.
|
|
3369
|
+
documents = {
|
|
3370
|
+
// Both list endpoints return a raw array (no { items, next_cursor }
|
|
3371
|
+
// envelope), like projects.list. They're here because setSharing needs a
|
|
3372
|
+
// document id and only documents you just generated hand you one directly.
|
|
3373
|
+
list: (query) => this.call("list_org_documents", query),
|
|
3374
|
+
listByProject: (projectId, query = {}) => this.call("list_documents", { id: projectId, ...query }),
|
|
3375
|
+
// Current sharing state rides the document body: `shareEnabled` and
|
|
3376
|
+
// `shareSecret` come back on GET.
|
|
3377
|
+
get: (id, query = {}) => this.call("get_document", { id, ...query }),
|
|
3378
|
+
// A document id belonging to another organization answers 404 exactly like
|
|
3379
|
+
// an id that doesn't exist.
|
|
3380
|
+
setSharing: (id, body, opts = {}) => this.call("set_document_sharing", { id, ...body }, opts)
|
|
3381
|
+
};
|
|
3382
|
+
// Vendor quotes. Request a quote from a vendor for a project, advance it to
|
|
3383
|
+
// `received` once the vendor responds (with priced line items), then record a
|
|
3384
|
+
// terminal accepted | rejected | expired decision. Each transition fires a
|
|
3385
|
+
// quote.* webhook. Note: there is no product UI for vendor quotes yet — these
|
|
3386
|
+
// are visible only via this API and the webhooks.
|
|
3387
|
+
quotes = {
|
|
3388
|
+
list: (projectId, query) => this.call("list_vendor_quotes", { id: projectId, ...query }),
|
|
3389
|
+
get: (id, query = {}) => this.call("get_vendor_quote", { id, ...query }),
|
|
3390
|
+
create: (projectId, body, opts = {}) => this.call("create_vendor_quote", { id: projectId, ...body }, opts),
|
|
3391
|
+
receive: (id, body, opts = {}) => this.call("receive_vendor_quote", { id, ...body }, opts),
|
|
3392
|
+
updateStatus: (id, body, opts = {}) => this.call("update_vendor_quote_status", { id, ...body }, opts)
|
|
336
3393
|
};
|
|
337
3394
|
help = {
|
|
338
|
-
search: (body) => this.
|
|
3395
|
+
search: (body) => this.call("search_help_articles", body)
|
|
339
3396
|
};
|
|
340
3397
|
webhooks = {
|
|
341
|
-
listEndpoints: (query) => this.
|
|
342
|
-
createEndpoint: (body) => this.
|
|
343
|
-
deleteEndpoint: (id, query) => this.
|
|
344
|
-
listDeliveries: (id, query) => this.
|
|
3398
|
+
listEndpoints: (query = {}) => this.call("list_webhook_endpoints", query),
|
|
3399
|
+
createEndpoint: (body) => this.call("create_webhook_endpoint", body),
|
|
3400
|
+
deleteEndpoint: (id, query = {}) => this.call("delete_webhook_endpoint", { id, ...query }),
|
|
3401
|
+
listDeliveries: (id, query = {}) => this.call("list_webhook_deliveries", { id, ...query }),
|
|
3402
|
+
/** Send a test event of one type to an endpoint, signed as usual, its body marked `"test": true`. */
|
|
3403
|
+
sendTestEvent: (id, body, opts = {}) => this.request({
|
|
3404
|
+
method: "POST",
|
|
3405
|
+
path: `/webhook_endpoints/${encodeURIComponent(id)}/test`,
|
|
3406
|
+
body: { ...body, organizationId: this.organizationFor(body.organizationId) },
|
|
3407
|
+
options: opts
|
|
3408
|
+
}),
|
|
3409
|
+
/** Send a delivery again, as a new delivery with the same event id and body. */
|
|
3410
|
+
resendDelivery: (deliveryId, body = {}, opts = {}) => this.request({
|
|
3411
|
+
method: "POST",
|
|
3412
|
+
path: `/webhook_deliveries/${encodeURIComponent(deliveryId)}/resend`,
|
|
3413
|
+
body: { organizationId: this.organizationFor(body.organizationId) },
|
|
3414
|
+
options: opts
|
|
3415
|
+
}),
|
|
3416
|
+
/** Receive the company's events over a stream, without a public endpoint (what `exayard listen` runs on). */
|
|
3417
|
+
listeners: new listeners_1.WebhookListeners({
|
|
3418
|
+
request: (opts) => this.request(opts),
|
|
3419
|
+
organizationId: (named) => this.organizationFor(named)
|
|
3420
|
+
}),
|
|
345
3421
|
/**
|
|
346
|
-
* Parse + verify an inbound webhook delivery
|
|
347
|
-
* on any signature failure — always catch and return 400 to let us retry.
|
|
3422
|
+
* Parse + verify an inbound webhook delivery: `constructEvent(rawBody, signatureHeader, secret)`. Throws
|
|
3423
|
+
* WebhookSignatureError on any signature failure — always catch and return 400 to let us retry.
|
|
348
3424
|
*/
|
|
349
3425
|
constructEvent: webhooks_1.constructWebhookEvent
|
|
350
3426
|
};
|
|
@@ -358,36 +3434,118 @@ var require_dist = __commonJS({
|
|
|
358
3434
|
"../sdk/dist/index.js"(exports2) {
|
|
359
3435
|
"use strict";
|
|
360
3436
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
361
|
-
exports2.WebhookSignatureError = exports2.constructWebhookEvent = exports2.ExayardError = exports2.Exayard = void 0;
|
|
3437
|
+
exports2.parseRetryAfter = exports2.DEFAULT_MAX_RETRIES = exports2.WebhookListeners = exports2.parseServerSentEvents = exports2.DEFAULT_WEBHOOK_TOLERANCE_SECONDS = exports2.WEBHOOK_SIGNATURE_HEADER = exports2.WEBHOOK_EVENT_TYPES = exports2.WebhookSignatureError = exports2.signWebhookPayload = exports2.constructWebhookEvent = exports2.UnprocessableEntityError = exports2.RateLimitError = exports2.PermissionDeniedError = exports2.PaymentRequiredError = exports2.NotFoundError = exports2.InternalServerError = exports2.ExayardError = exports2.errorFromProblem = exports2.ConflictError = exports2.BadRequestError = exports2.AuthenticationError = exports2.APIConnectionError = exports2.isValidEndUser = exports2.END_USER_MAX_LENGTH = exports2.END_USER_HEADER = exports2.OPERATIONS = exports2.Exayard = void 0;
|
|
362
3438
|
var client_1 = require_client();
|
|
363
3439
|
Object.defineProperty(exports2, "Exayard", { enumerable: true, get: function() {
|
|
364
3440
|
return client_1.Exayard;
|
|
365
3441
|
} });
|
|
3442
|
+
var operations_1 = require_operations();
|
|
3443
|
+
Object.defineProperty(exports2, "OPERATIONS", { enumerable: true, get: function() {
|
|
3444
|
+
return operations_1.OPERATIONS;
|
|
3445
|
+
} });
|
|
3446
|
+
var options_1 = require_options();
|
|
3447
|
+
Object.defineProperty(exports2, "END_USER_HEADER", { enumerable: true, get: function() {
|
|
3448
|
+
return options_1.END_USER_HEADER;
|
|
3449
|
+
} });
|
|
3450
|
+
Object.defineProperty(exports2, "END_USER_MAX_LENGTH", { enumerable: true, get: function() {
|
|
3451
|
+
return options_1.END_USER_MAX_LENGTH;
|
|
3452
|
+
} });
|
|
3453
|
+
Object.defineProperty(exports2, "isValidEndUser", { enumerable: true, get: function() {
|
|
3454
|
+
return options_1.isValidEndUser;
|
|
3455
|
+
} });
|
|
366
3456
|
var error_1 = require_error();
|
|
3457
|
+
Object.defineProperty(exports2, "APIConnectionError", { enumerable: true, get: function() {
|
|
3458
|
+
return error_1.APIConnectionError;
|
|
3459
|
+
} });
|
|
3460
|
+
Object.defineProperty(exports2, "AuthenticationError", { enumerable: true, get: function() {
|
|
3461
|
+
return error_1.AuthenticationError;
|
|
3462
|
+
} });
|
|
3463
|
+
Object.defineProperty(exports2, "BadRequestError", { enumerable: true, get: function() {
|
|
3464
|
+
return error_1.BadRequestError;
|
|
3465
|
+
} });
|
|
3466
|
+
Object.defineProperty(exports2, "ConflictError", { enumerable: true, get: function() {
|
|
3467
|
+
return error_1.ConflictError;
|
|
3468
|
+
} });
|
|
3469
|
+
Object.defineProperty(exports2, "errorFromProblem", { enumerable: true, get: function() {
|
|
3470
|
+
return error_1.errorFromProblem;
|
|
3471
|
+
} });
|
|
367
3472
|
Object.defineProperty(exports2, "ExayardError", { enumerable: true, get: function() {
|
|
368
3473
|
return error_1.ExayardError;
|
|
369
3474
|
} });
|
|
3475
|
+
Object.defineProperty(exports2, "InternalServerError", { enumerable: true, get: function() {
|
|
3476
|
+
return error_1.InternalServerError;
|
|
3477
|
+
} });
|
|
3478
|
+
Object.defineProperty(exports2, "NotFoundError", { enumerable: true, get: function() {
|
|
3479
|
+
return error_1.NotFoundError;
|
|
3480
|
+
} });
|
|
3481
|
+
Object.defineProperty(exports2, "PaymentRequiredError", { enumerable: true, get: function() {
|
|
3482
|
+
return error_1.PaymentRequiredError;
|
|
3483
|
+
} });
|
|
3484
|
+
Object.defineProperty(exports2, "PermissionDeniedError", { enumerable: true, get: function() {
|
|
3485
|
+
return error_1.PermissionDeniedError;
|
|
3486
|
+
} });
|
|
3487
|
+
Object.defineProperty(exports2, "RateLimitError", { enumerable: true, get: function() {
|
|
3488
|
+
return error_1.RateLimitError;
|
|
3489
|
+
} });
|
|
3490
|
+
Object.defineProperty(exports2, "UnprocessableEntityError", { enumerable: true, get: function() {
|
|
3491
|
+
return error_1.UnprocessableEntityError;
|
|
3492
|
+
} });
|
|
370
3493
|
var webhooks_1 = require_webhooks();
|
|
371
3494
|
Object.defineProperty(exports2, "constructWebhookEvent", { enumerable: true, get: function() {
|
|
372
3495
|
return webhooks_1.constructWebhookEvent;
|
|
373
3496
|
} });
|
|
3497
|
+
Object.defineProperty(exports2, "signWebhookPayload", { enumerable: true, get: function() {
|
|
3498
|
+
return webhooks_1.signWebhookPayload;
|
|
3499
|
+
} });
|
|
374
3500
|
Object.defineProperty(exports2, "WebhookSignatureError", { enumerable: true, get: function() {
|
|
375
3501
|
return webhooks_1.WebhookSignatureError;
|
|
376
3502
|
} });
|
|
3503
|
+
Object.defineProperty(exports2, "WEBHOOK_EVENT_TYPES", { enumerable: true, get: function() {
|
|
3504
|
+
return webhooks_1.WEBHOOK_EVENT_TYPES;
|
|
3505
|
+
} });
|
|
3506
|
+
Object.defineProperty(exports2, "WEBHOOK_SIGNATURE_HEADER", { enumerable: true, get: function() {
|
|
3507
|
+
return webhooks_1.WEBHOOK_SIGNATURE_HEADER;
|
|
3508
|
+
} });
|
|
3509
|
+
Object.defineProperty(exports2, "DEFAULT_WEBHOOK_TOLERANCE_SECONDS", { enumerable: true, get: function() {
|
|
3510
|
+
return webhooks_1.DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
|
|
3511
|
+
} });
|
|
3512
|
+
var listeners_1 = require_listeners();
|
|
3513
|
+
Object.defineProperty(exports2, "parseServerSentEvents", { enumerable: true, get: function() {
|
|
3514
|
+
return listeners_1.parseServerSentEvents;
|
|
3515
|
+
} });
|
|
3516
|
+
Object.defineProperty(exports2, "WebhookListeners", { enumerable: true, get: function() {
|
|
3517
|
+
return listeners_1.WebhookListeners;
|
|
3518
|
+
} });
|
|
3519
|
+
var retry_1 = require_retry();
|
|
3520
|
+
Object.defineProperty(exports2, "DEFAULT_MAX_RETRIES", { enumerable: true, get: function() {
|
|
3521
|
+
return retry_1.DEFAULT_MAX_RETRIES;
|
|
3522
|
+
} });
|
|
3523
|
+
Object.defineProperty(exports2, "parseRetryAfter", { enumerable: true, get: function() {
|
|
3524
|
+
return retry_1.parseRetryAfter;
|
|
3525
|
+
} });
|
|
377
3526
|
}
|
|
378
3527
|
});
|
|
379
3528
|
|
|
380
3529
|
// src/bin.ts
|
|
381
|
-
var
|
|
3530
|
+
var import_sdk4 = __toESM(require_dist());
|
|
382
3531
|
|
|
383
3532
|
// src/commands.ts
|
|
384
|
-
var
|
|
3533
|
+
var import_sdk3 = __toESM(require_dist());
|
|
385
3534
|
|
|
386
3535
|
// src/config.ts
|
|
387
3536
|
var import_node_fs = require("fs");
|
|
388
3537
|
var import_node_os = require("os");
|
|
389
3538
|
var import_node_path = require("path");
|
|
390
3539
|
var configPath = () => (0, import_node_path.join)((0, import_node_os.homedir)(), ".exayard", "config.json");
|
|
3540
|
+
var readConfigFile = () => {
|
|
3541
|
+
const path = configPath();
|
|
3542
|
+
if (!(0, import_node_fs.existsSync)(path)) return {};
|
|
3543
|
+
try {
|
|
3544
|
+
return JSON.parse((0, import_node_fs.readFileSync)(path, "utf8"));
|
|
3545
|
+
} catch {
|
|
3546
|
+
return {};
|
|
3547
|
+
}
|
|
3548
|
+
};
|
|
391
3549
|
var readConfig = () => {
|
|
392
3550
|
const fromEnv = {
|
|
393
3551
|
apiKey: process.env.EXAYARD_API_KEY,
|
|
@@ -396,31 +3554,464 @@ var readConfig = () => {
|
|
|
396
3554
|
};
|
|
397
3555
|
const path = configPath();
|
|
398
3556
|
if (!(0, import_node_fs.existsSync)(path)) return fromEnv;
|
|
3557
|
+
return { ...readConfigFile(), ...Object.fromEntries(Object.entries(fromEnv).filter(([, v]) => v !== void 0 && v !== "")) };
|
|
3558
|
+
};
|
|
3559
|
+
var writeConfig = (config) => {
|
|
3560
|
+
const path = configPath();
|
|
3561
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path), { recursive: true, mode: 448 });
|
|
399
3562
|
try {
|
|
400
|
-
|
|
401
|
-
return { ...raw, ...Object.fromEntries(Object.entries(fromEnv).filter(([, v]) => v !== void 0 && v !== "")) };
|
|
3563
|
+
(0, import_node_fs.chmodSync)((0, import_node_path.dirname)(path), 448);
|
|
402
3564
|
} catch {
|
|
403
|
-
return fromEnv;
|
|
404
3565
|
}
|
|
3566
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
3567
|
+
(0, import_node_fs.writeFileSync)(tmp, JSON.stringify(config, null, 2), { mode: 384 });
|
|
3568
|
+
try {
|
|
3569
|
+
(0, import_node_fs.chmodSync)(tmp, 384);
|
|
3570
|
+
} catch {
|
|
3571
|
+
}
|
|
3572
|
+
(0, import_node_fs.renameSync)(tmp, path);
|
|
405
3573
|
};
|
|
406
|
-
var
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
3574
|
+
var saveOAuthSession = (session) => {
|
|
3575
|
+
writeConfig({ ...readConfigFile(), oauth: session });
|
|
3576
|
+
};
|
|
3577
|
+
|
|
3578
|
+
// src/listen.ts
|
|
3579
|
+
var import_node_os2 = require("os");
|
|
3580
|
+
var import_sdk = __toESM(require_dist());
|
|
3581
|
+
var organizationOf = (payload) => {
|
|
410
3582
|
try {
|
|
411
|
-
|
|
3583
|
+
const parsed = JSON.parse(payload);
|
|
3584
|
+
return typeof parsed.organizationId === "string" ? parsed.organizationId : void 0;
|
|
412
3585
|
} catch {
|
|
3586
|
+
return void 0;
|
|
413
3587
|
}
|
|
414
3588
|
};
|
|
3589
|
+
var forwardEvent = async (event, forwardTo, secret, fetchImpl = fetch) => {
|
|
3590
|
+
const organizationId = organizationOf(event.payload);
|
|
3591
|
+
try {
|
|
3592
|
+
const res = await fetchImpl(forwardTo, {
|
|
3593
|
+
method: "POST",
|
|
3594
|
+
headers: {
|
|
3595
|
+
"Content-Type": "application/json",
|
|
3596
|
+
"User-Agent": "Exayard-CLI-Listen",
|
|
3597
|
+
"Exayard-Signature": await (0, import_sdk.signWebhookPayload)(event.payload, secret),
|
|
3598
|
+
"Exayard-Event-Id": event.eventId,
|
|
3599
|
+
"Exayard-Event-Type": event.type,
|
|
3600
|
+
...organizationId ? { "Exayard-Organization-Id": organizationId } : {}
|
|
3601
|
+
},
|
|
3602
|
+
body: event.payload,
|
|
3603
|
+
redirect: "manual"
|
|
3604
|
+
});
|
|
3605
|
+
await res.body?.cancel().catch(() => void 0);
|
|
3606
|
+
return { status: res.status };
|
|
3607
|
+
} catch (err) {
|
|
3608
|
+
return { error: err.message || "request failed" };
|
|
3609
|
+
}
|
|
3610
|
+
};
|
|
3611
|
+
var runListen = async (exa, opts) => {
|
|
3612
|
+
const listener = await exa.webhooks.listeners.start({
|
|
3613
|
+
organizationId: opts.organizationId,
|
|
3614
|
+
...opts.events && opts.events.length > 0 ? { events: opts.events } : {},
|
|
3615
|
+
name: (opts.name ?? (0, import_node_os2.hostname)()).slice(0, 100)
|
|
3616
|
+
});
|
|
3617
|
+
const target = opts.forwardTo ? ` and forwarding to ${opts.forwardTo}` : "";
|
|
3618
|
+
if (opts.json) opts.write(JSON.stringify({ ready: true, listenerId: listener.id, secret: listener.secret, events: listener.events }));
|
|
3619
|
+
else {
|
|
3620
|
+
opts.writeErr(`Ready. Listening for ${listener.events.join(", ")}${target}. Press Ctrl-C to stop.`);
|
|
3621
|
+
opts.writeErr(`Your webhook signing secret is ${listener.secret}`);
|
|
3622
|
+
}
|
|
3623
|
+
let code = 0;
|
|
3624
|
+
try {
|
|
3625
|
+
for await (const event of exa.webhooks.listeners.events(listener.id, { organizationId: opts.organizationId, signal: opts.signal })) {
|
|
3626
|
+
if (!opts.forwardTo) {
|
|
3627
|
+
opts.write(opts.json ? JSON.stringify({ seq: event.seq, type: event.type, eventId: event.eventId, payload: event.payload }) : event.payload);
|
|
3628
|
+
continue;
|
|
3629
|
+
}
|
|
3630
|
+
if (!opts.json) opts.writeErr(`--> ${event.type}${event.test ? " (test)" : ""} [${event.eventId}]`);
|
|
3631
|
+
const result = await forwardEvent(event, opts.forwardTo, listener.secret, opts.fetch);
|
|
3632
|
+
if (opts.json) opts.write(JSON.stringify({ seq: event.seq, type: event.type, eventId: event.eventId, ...result }));
|
|
3633
|
+
else if ("status" in result) opts.writeErr(`<-- [${result.status}] POST ${opts.forwardTo}`);
|
|
3634
|
+
else opts.writeErr(`<-- failed POST ${opts.forwardTo}: ${result.error}`);
|
|
3635
|
+
}
|
|
3636
|
+
} catch (err) {
|
|
3637
|
+
if (!opts.signal.aborted) {
|
|
3638
|
+
opts.writeErr(`The listener stopped: ${err.message}`);
|
|
3639
|
+
code = 1;
|
|
3640
|
+
}
|
|
3641
|
+
} finally {
|
|
3642
|
+
await exa.webhooks.listeners.stop(listener.id, { organizationId: opts.organizationId }, { maxRetries: 1 }).catch(() => void 0);
|
|
3643
|
+
}
|
|
3644
|
+
if (!opts.json && code === 0) opts.writeErr("Stopped.");
|
|
3645
|
+
return code;
|
|
3646
|
+
};
|
|
3647
|
+
|
|
3648
|
+
// src/oauth.ts
|
|
3649
|
+
var import_node_crypto = require("crypto");
|
|
3650
|
+
var import_node_child_process = require("child_process");
|
|
3651
|
+
var import_node_http = require("http");
|
|
3652
|
+
var OAUTH_SCOPES = ["openid", "profile", "email", "offline_access"];
|
|
3653
|
+
var REFRESH_MARGIN_MS = 6e4;
|
|
3654
|
+
var OAuthLoginError = class extends Error {
|
|
3655
|
+
constructor(message) {
|
|
3656
|
+
super(message);
|
|
3657
|
+
this.name = "OAuthLoginError";
|
|
3658
|
+
}
|
|
3659
|
+
};
|
|
3660
|
+
var base64url = (bytes) => bytes.toString("base64url");
|
|
3661
|
+
var pkcePair = () => {
|
|
3662
|
+
const verifier = base64url((0, import_node_crypto.randomBytes)(32));
|
|
3663
|
+
return { verifier, challenge: base64url((0, import_node_crypto.createHash)("sha256").update(verifier).digest()) };
|
|
3664
|
+
};
|
|
3665
|
+
var getJson = async (fetchImpl, url, what) => {
|
|
3666
|
+
let res;
|
|
3667
|
+
try {
|
|
3668
|
+
res = await fetchImpl(url, { headers: { Accept: "application/json" } });
|
|
3669
|
+
} catch (err) {
|
|
3670
|
+
throw new OAuthLoginError(`Could not reach ${url} (${what}): ${err.message}`);
|
|
3671
|
+
}
|
|
3672
|
+
if (!res.ok) throw new OAuthLoginError(`${url} (${what}) answered ${res.status}.`);
|
|
3673
|
+
return await res.json();
|
|
3674
|
+
};
|
|
3675
|
+
var stringField = (obj, key, where) => {
|
|
3676
|
+
const value = obj[key];
|
|
3677
|
+
if (typeof value !== "string" || value === "") throw new OAuthLoginError(`${where} has no ${key}.`);
|
|
3678
|
+
return value;
|
|
3679
|
+
};
|
|
3680
|
+
var discoverOAuth = async (apiBaseUrl, fetchImpl) => {
|
|
3681
|
+
const origin = new URL(apiBaseUrl).origin;
|
|
3682
|
+
const prm = await getJson(fetchImpl, `${origin}/.well-known/oauth-protected-resource`, "protected resource metadata");
|
|
3683
|
+
const servers = prm.authorization_servers;
|
|
3684
|
+
const issuer = Array.isArray(servers) && typeof servers[0] === "string" ? servers[0] : void 0;
|
|
3685
|
+
if (!issuer) throw new OAuthLoginError(`${origin} names no authorization server.`);
|
|
3686
|
+
const asUrl = `${issuer.replace(/\/$/, "")}/.well-known/oauth-authorization-server`;
|
|
3687
|
+
const meta = await getJson(fetchImpl, asUrl, "authorization server metadata");
|
|
3688
|
+
const methods = meta.code_challenge_methods_supported;
|
|
3689
|
+
if (Array.isArray(methods) && !methods.includes("S256")) throw new OAuthLoginError(`${issuer} does not support PKCE S256.`);
|
|
3690
|
+
return {
|
|
3691
|
+
resource: typeof prm.resource === "string" && prm.resource !== "" ? prm.resource : origin,
|
|
3692
|
+
authorizationEndpoint: stringField(meta, "authorization_endpoint", asUrl),
|
|
3693
|
+
tokenEndpoint: stringField(meta, "token_endpoint", asUrl),
|
|
3694
|
+
registrationEndpoint: stringField(meta, "registration_endpoint", asUrl)
|
|
3695
|
+
};
|
|
3696
|
+
};
|
|
3697
|
+
var registerClient = async (registrationEndpoint, redirectUri, fetchImpl) => {
|
|
3698
|
+
const res = await fetchImpl(registrationEndpoint, {
|
|
3699
|
+
method: "POST",
|
|
3700
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
3701
|
+
body: JSON.stringify({
|
|
3702
|
+
client_name: "Exayard CLI",
|
|
3703
|
+
redirect_uris: [redirectUri],
|
|
3704
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
3705
|
+
response_types: ["code"],
|
|
3706
|
+
token_endpoint_auth_method: "none",
|
|
3707
|
+
scope: OAUTH_SCOPES.join(" ")
|
|
3708
|
+
})
|
|
3709
|
+
});
|
|
3710
|
+
const body = await res.json().catch(() => ({}));
|
|
3711
|
+
if (!res.ok) throw new OAuthLoginError(`Registering the CLI failed (${res.status}): ${oauthErrorText(body)}`);
|
|
3712
|
+
return stringField(body, "client_id", "The registration answer");
|
|
3713
|
+
};
|
|
3714
|
+
var oauthErrorText = (body) => [body.error, body.error_description].filter((v) => typeof v === "string" && v !== "").join(": ") || "no detail";
|
|
3715
|
+
var tokenRequest = async (tokenEndpoint, form, fetchImpl, now) => {
|
|
3716
|
+
const params = new URLSearchParams();
|
|
3717
|
+
for (const [k, v] of Object.entries(form)) if (v !== void 0) params.set(k, v);
|
|
3718
|
+
const res = await fetchImpl(tokenEndpoint, {
|
|
3719
|
+
method: "POST",
|
|
3720
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" },
|
|
3721
|
+
body: params.toString()
|
|
3722
|
+
});
|
|
3723
|
+
const body = await res.json().catch(() => ({}));
|
|
3724
|
+
if (!res.ok) throw new OAuthLoginError(`The token request failed (${res.status}): ${oauthErrorText(body)}`);
|
|
3725
|
+
const accessToken = stringField(body, "access_token", "The token answer");
|
|
3726
|
+
const expiresIn = typeof body.expires_in === "number" ? body.expires_in : Number(body.expires_in);
|
|
3727
|
+
return {
|
|
3728
|
+
accessToken,
|
|
3729
|
+
...typeof body.refresh_token === "string" && body.refresh_token !== "" ? { refreshToken: body.refresh_token } : {},
|
|
3730
|
+
...Number.isFinite(expiresIn) && expiresIn > 0 ? { expiresAt: now() + expiresIn * 1e3 } : {}
|
|
3731
|
+
};
|
|
3732
|
+
};
|
|
3733
|
+
var CALLBACK_PAGE = (title) => `<!doctype html><html><head><meta charset="utf-8"><title>Exayard CLI</title></head><body style="font-family:system-ui,sans-serif;padding:3rem"><h1>${title}</h1><p>You can close this tab and go back to the terminal.</p></body></html>`;
|
|
3734
|
+
var startLoopback = async (port, signal) => {
|
|
3735
|
+
let resolveCallback = () => void 0;
|
|
3736
|
+
let rejectCallback = () => void 0;
|
|
3737
|
+
const callback = new Promise((resolve, reject) => {
|
|
3738
|
+
resolveCallback = resolve;
|
|
3739
|
+
rejectCallback = reject;
|
|
3740
|
+
});
|
|
3741
|
+
const server = (0, import_node_http.createServer)((req, res) => {
|
|
3742
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
3743
|
+
if (url.pathname !== "/callback") {
|
|
3744
|
+
res.writeHead(404, { "Content-Type": "text/plain" }).end("Not found");
|
|
3745
|
+
return;
|
|
3746
|
+
}
|
|
3747
|
+
const failed = url.searchParams.has("error") || !url.searchParams.has("code");
|
|
3748
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(CALLBACK_PAGE(failed ? "Sign-in did not finish" : "You are signed in"));
|
|
3749
|
+
resolveCallback(url.searchParams);
|
|
3750
|
+
});
|
|
3751
|
+
await new Promise((resolve, reject) => {
|
|
3752
|
+
server.once("error", reject);
|
|
3753
|
+
server.listen(port, "127.0.0.1", () => resolve());
|
|
3754
|
+
});
|
|
3755
|
+
const close = () => {
|
|
3756
|
+
server.closeAllConnections();
|
|
3757
|
+
server.close();
|
|
3758
|
+
};
|
|
3759
|
+
const onAbort = () => rejectCallback(new OAuthLoginError("Sign-in cancelled."));
|
|
3760
|
+
if (signal?.aborted) onAbort();
|
|
3761
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
3762
|
+
const { port: bound } = server.address();
|
|
3763
|
+
return { redirectUri: `http://127.0.0.1:${bound}/callback`, callback, close };
|
|
3764
|
+
};
|
|
3765
|
+
var openInBrowser = (url) => {
|
|
3766
|
+
const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", '""', url.replace(/&/g, "^&")]] : ["xdg-open", [url]];
|
|
3767
|
+
try {
|
|
3768
|
+
const child = (0, import_node_child_process.spawn)(cmd, args, { stdio: "ignore", detached: true });
|
|
3769
|
+
child.on("error", () => void 0);
|
|
3770
|
+
child.unref();
|
|
3771
|
+
} catch {
|
|
3772
|
+
}
|
|
3773
|
+
};
|
|
3774
|
+
var browserLogin = async (opts) => {
|
|
3775
|
+
const fetchImpl = opts.fetch ?? ((input, init) => fetch(input, init));
|
|
3776
|
+
const now = opts.now ?? Date.now;
|
|
3777
|
+
const discovery = await discoverOAuth(opts.apiBaseUrl, fetchImpl);
|
|
3778
|
+
const loopback = await startLoopback(opts.port ?? 0, opts.signal);
|
|
3779
|
+
try {
|
|
3780
|
+
const clientId = await registerClient(discovery.registrationEndpoint, loopback.redirectUri, fetchImpl);
|
|
3781
|
+
const { verifier, challenge } = pkcePair();
|
|
3782
|
+
const state = base64url((0, import_node_crypto.randomBytes)(16));
|
|
3783
|
+
const authorize = new URL(discovery.authorizationEndpoint);
|
|
3784
|
+
authorize.search = new URLSearchParams({
|
|
3785
|
+
response_type: "code",
|
|
3786
|
+
client_id: clientId,
|
|
3787
|
+
redirect_uri: loopback.redirectUri,
|
|
3788
|
+
scope: OAUTH_SCOPES.join(" "),
|
|
3789
|
+
state,
|
|
3790
|
+
code_challenge: challenge,
|
|
3791
|
+
code_challenge_method: "S256",
|
|
3792
|
+
resource: discovery.resource
|
|
3793
|
+
}).toString();
|
|
3794
|
+
opts.writeErr("Opening your browser to sign in. If it does not open, visit:");
|
|
3795
|
+
opts.writeErr(authorize.toString());
|
|
3796
|
+
await Promise.resolve(opts.openBrowser ? opts.openBrowser(authorize.toString()) : openInBrowser(authorize.toString())).catch(() => void 0);
|
|
3797
|
+
const query = await loopback.callback;
|
|
3798
|
+
if (query.get("error")) {
|
|
3799
|
+
throw new OAuthLoginError(`Sign-in refused: ${[query.get("error"), query.get("error_description")].filter(Boolean).join(": ")}`);
|
|
3800
|
+
}
|
|
3801
|
+
if (query.get("state") !== state) throw new OAuthLoginError("Sign-in answer did not match this login (state). Run `exayard login` again.");
|
|
3802
|
+
const code = query.get("code");
|
|
3803
|
+
if (!code) throw new OAuthLoginError("Sign-in answer carried no code.");
|
|
3804
|
+
const tokens = await tokenRequest(
|
|
3805
|
+
discovery.tokenEndpoint,
|
|
3806
|
+
{
|
|
3807
|
+
grant_type: "authorization_code",
|
|
3808
|
+
code,
|
|
3809
|
+
redirect_uri: loopback.redirectUri,
|
|
3810
|
+
client_id: clientId,
|
|
3811
|
+
code_verifier: verifier,
|
|
3812
|
+
resource: discovery.resource
|
|
3813
|
+
},
|
|
3814
|
+
fetchImpl,
|
|
3815
|
+
now
|
|
3816
|
+
);
|
|
3817
|
+
return { ...tokens, clientId, tokenEndpoint: discovery.tokenEndpoint, redirectUri: loopback.redirectUri, resource: discovery.resource };
|
|
3818
|
+
} finally {
|
|
3819
|
+
loopback.close();
|
|
3820
|
+
}
|
|
3821
|
+
};
|
|
3822
|
+
var refreshSession = async (session, fetchImpl, now = Date.now) => {
|
|
3823
|
+
if (!session.refreshToken) throw new OAuthLoginError("Your sign-in ended. Run `exayard login` again.");
|
|
3824
|
+
let tokens;
|
|
3825
|
+
try {
|
|
3826
|
+
tokens = await tokenRequest(
|
|
3827
|
+
session.tokenEndpoint,
|
|
3828
|
+
{ grant_type: "refresh_token", refresh_token: session.refreshToken, client_id: session.clientId, resource: session.resource },
|
|
3829
|
+
fetchImpl,
|
|
3830
|
+
now
|
|
3831
|
+
);
|
|
3832
|
+
} catch (err) {
|
|
3833
|
+
throw new OAuthLoginError(`Your sign-in could not be renewed (${err.message}). Run \`exayard login\` again.`);
|
|
3834
|
+
}
|
|
3835
|
+
return { ...session, ...tokens, refreshToken: tokens.refreshToken ?? session.refreshToken };
|
|
3836
|
+
};
|
|
3837
|
+
var needsRefresh = (session, now) => session.expiresAt !== void 0 && session.expiresAt - now < REFRESH_MARGIN_MS;
|
|
3838
|
+
var sessionFetch = (initial, save, fetchImpl = (input, init) => fetch(input, init), now = Date.now) => {
|
|
3839
|
+
let session = initial;
|
|
3840
|
+
let refreshing = null;
|
|
3841
|
+
const current = async () => {
|
|
3842
|
+
if (!needsRefresh(session, now())) return session;
|
|
3843
|
+
refreshing ??= refreshSession(session, fetchImpl, now).then((next) => {
|
|
3844
|
+
session = next;
|
|
3845
|
+
save(next);
|
|
3846
|
+
return next;
|
|
3847
|
+
}).finally(() => {
|
|
3848
|
+
refreshing = null;
|
|
3849
|
+
});
|
|
3850
|
+
return refreshing;
|
|
3851
|
+
};
|
|
3852
|
+
return async (input, init) => {
|
|
3853
|
+
const headers = new Headers(init?.headers);
|
|
3854
|
+
if (!headers.has("Authorization")) return fetchImpl(input, init);
|
|
3855
|
+
let accessToken;
|
|
3856
|
+
try {
|
|
3857
|
+
;
|
|
3858
|
+
({ accessToken } = await current());
|
|
3859
|
+
} catch (err) {
|
|
3860
|
+
return new Response(
|
|
3861
|
+
JSON.stringify({ type: "about:blank", title: "Sign-in ended", status: 401, code: "unauthenticated", detail: err.message }),
|
|
3862
|
+
{ status: 401, headers: { "Content-Type": "application/problem+json" } }
|
|
3863
|
+
);
|
|
3864
|
+
}
|
|
3865
|
+
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
3866
|
+
return fetchImpl(input, { ...init, headers });
|
|
3867
|
+
};
|
|
3868
|
+
};
|
|
3869
|
+
|
|
3870
|
+
// src/operations.ts
|
|
3871
|
+
var import_sdk2 = __toESM(require_dist());
|
|
3872
|
+
var kebab = (name) => name.replace(/_/g, "-");
|
|
3873
|
+
var camelToKebab = (name) => name.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
|
|
3874
|
+
var BY_COMMAND = new Map(
|
|
3875
|
+
Object.keys(import_sdk2.OPERATIONS).map((name) => [kebab(name), name])
|
|
3876
|
+
);
|
|
3877
|
+
var operationForCommand = (command) => BY_COMMAND.get(command);
|
|
3878
|
+
var spec = (name) => import_sdk2.OPERATIONS[name];
|
|
3879
|
+
var confirmation = (op) => op.confirm ?? (op.method === "DELETE" ? "This operation deletes or archives a record." : void 0);
|
|
3880
|
+
var pathParams = (op) => op.params.filter((param) => param.in === "path");
|
|
3881
|
+
var usage = (name) => {
|
|
3882
|
+
const op = spec(name);
|
|
3883
|
+
const positional = pathParams(op).map((param) => `<${param.name}>`);
|
|
3884
|
+
const required = op.params.filter((param) => param.in !== "path" && param.required).map((param) => `--${param.name} <${param.kind}>`);
|
|
3885
|
+
return ["exayard", kebab(name), ...positional, ...required, ...confirmation(op) ? ["--yes"] : []].join(" ");
|
|
3886
|
+
};
|
|
3887
|
+
var operationHelp = (name) => {
|
|
3888
|
+
const op = spec(name);
|
|
3889
|
+
const lines = [`${op.summary}`, "", ` ${usage(name)}`, "", ` ${op.method} /v1${op.path}`];
|
|
3890
|
+
const note = confirmation(op);
|
|
3891
|
+
if (note) lines.push("", ` ${note} Pass --yes to run it.`);
|
|
3892
|
+
lines.push("", "Fields:");
|
|
3893
|
+
for (const param of op.params) {
|
|
3894
|
+
const where = param.in === "path" ? `<${param.name}>` : `--${param.name}`;
|
|
3895
|
+
const kind = param.enum ? param.enum.map(String).join("|") : param.kind;
|
|
3896
|
+
const flags = [param.required ? "required" : "optional", kind].join(", ");
|
|
3897
|
+
const orgNote = param.name === "organizationId" ? " (--org, or the one saved by login)" : "";
|
|
3898
|
+
lines.push(` ${where.padEnd(28)} ${flags}${orgNote}${param.description ? ` \u2014 ${param.description}` : ""}`);
|
|
3899
|
+
}
|
|
3900
|
+
lines.push("", " --input '<json>' the whole input as one JSON object; flags override its fields");
|
|
3901
|
+
return `${lines.join("\n")}
|
|
3902
|
+
`;
|
|
3903
|
+
};
|
|
3904
|
+
var operationsIndex = () => {
|
|
3905
|
+
const byTag = /* @__PURE__ */ new Map();
|
|
3906
|
+
for (const name of Object.keys(import_sdk2.OPERATIONS)) {
|
|
3907
|
+
const tag = spec(name).tag;
|
|
3908
|
+
byTag.set(tag, [...byTag.get(tag) ?? [], name]);
|
|
3909
|
+
}
|
|
3910
|
+
const lines = ["Every API operation is a command. `exayard <command> --help` shows its fields.", ""];
|
|
3911
|
+
for (const [tag, names] of [...byTag].sort(([a], [b]) => a.localeCompare(b))) {
|
|
3912
|
+
lines.push(`${tag}:`);
|
|
3913
|
+
for (const name of names) lines.push(` ${kebab(name).padEnd(36)} ${spec(name).summary}`);
|
|
3914
|
+
lines.push("");
|
|
3915
|
+
}
|
|
3916
|
+
return lines.join("\n");
|
|
3917
|
+
};
|
|
3918
|
+
var UsageError = class extends Error {
|
|
3919
|
+
};
|
|
3920
|
+
var coerce = (param, raw) => {
|
|
3921
|
+
if (param.kind === "boolean") {
|
|
3922
|
+
if (raw === true || raw === "true") return true;
|
|
3923
|
+
if (raw === "false") return false;
|
|
3924
|
+
throw new UsageError(`--${param.name} takes true or false`);
|
|
3925
|
+
}
|
|
3926
|
+
if (typeof raw !== "string") throw new UsageError(`--${param.name} needs a value`);
|
|
3927
|
+
if (param.kind === "number" || param.kind === "integer") {
|
|
3928
|
+
const value = Number(raw);
|
|
3929
|
+
if (raw.trim() === "" || !Number.isFinite(value)) throw new UsageError(`--${param.name} takes a number, not "${raw}"`);
|
|
3930
|
+
return value;
|
|
3931
|
+
}
|
|
3932
|
+
if (param.kind === "array" || param.kind === "object" || param.kind === "unknown") {
|
|
3933
|
+
try {
|
|
3934
|
+
return JSON.parse(raw);
|
|
3935
|
+
} catch {
|
|
3936
|
+
if (param.kind === "array") return raw.split(",").map((item) => item.trim()).filter(Boolean);
|
|
3937
|
+
throw new UsageError(`--${param.name} takes JSON`);
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
return raw;
|
|
3941
|
+
};
|
|
3942
|
+
var GLOBAL_FLAGS = /* @__PURE__ */ new Set(["json", "api-key", "org", "yes", "input", "help", "h"]);
|
|
3943
|
+
var buildInput = (name, ctx, defaultOrg) => {
|
|
3944
|
+
const op = spec(name);
|
|
3945
|
+
let input = {};
|
|
3946
|
+
if (typeof ctx.flags.input === "string") {
|
|
3947
|
+
const parsed = JSON.parse(ctx.flags.input);
|
|
3948
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new UsageError("--input takes a JSON object");
|
|
3949
|
+
input = { ...parsed };
|
|
3950
|
+
}
|
|
3951
|
+
const byFlag = /* @__PURE__ */ new Map();
|
|
3952
|
+
for (const param of op.params) {
|
|
3953
|
+
byFlag.set(param.name, param);
|
|
3954
|
+
byFlag.set(camelToKebab(param.name), param);
|
|
3955
|
+
}
|
|
3956
|
+
for (const [flag, raw] of Object.entries(ctx.flags)) {
|
|
3957
|
+
if (GLOBAL_FLAGS.has(flag) || raw === void 0) continue;
|
|
3958
|
+
const param = byFlag.get(flag);
|
|
3959
|
+
if (!param) throw new UsageError(`${kebab(name)} has no --${flag}`);
|
|
3960
|
+
input[param.name] = coerce(param, raw);
|
|
3961
|
+
}
|
|
3962
|
+
const positional = pathParams(op);
|
|
3963
|
+
if (ctx.positional.length > positional.length) {
|
|
3964
|
+
throw new UsageError(`${kebab(name)} takes ${positional.length} positional argument(s): ${usage(name)}`);
|
|
3965
|
+
}
|
|
3966
|
+
ctx.positional.forEach((value, index) => {
|
|
3967
|
+
input[positional[index].name] = value;
|
|
3968
|
+
});
|
|
3969
|
+
if (op.params.some((param) => param.name === "organizationId") && input.organizationId === void 0) {
|
|
3970
|
+
const org = typeof ctx.flags.org === "string" ? ctx.flags.org : defaultOrg;
|
|
3971
|
+
if (org) input.organizationId = org;
|
|
3972
|
+
}
|
|
3973
|
+
const missing = op.params.filter((param) => param.required && input[param.name] === void 0).map((param) => param.name);
|
|
3974
|
+
if (missing.length > 0) throw new UsageError(`missing ${missing.join(", ")}: ${usage(name)}`);
|
|
3975
|
+
return input;
|
|
3976
|
+
};
|
|
3977
|
+
var runOperation = async (name, ctx, client, defaultOrg) => {
|
|
3978
|
+
const op = spec(name);
|
|
3979
|
+
let input;
|
|
3980
|
+
try {
|
|
3981
|
+
input = buildInput(name, ctx, defaultOrg);
|
|
3982
|
+
} catch (err) {
|
|
3983
|
+
if (!(err instanceof UsageError) && !(err instanceof SyntaxError)) throw err;
|
|
3984
|
+
ctx.writeErr(err.message);
|
|
3985
|
+
return 2;
|
|
3986
|
+
}
|
|
3987
|
+
const note = confirmation(op);
|
|
3988
|
+
if (note && ctx.flags.yes !== true) {
|
|
3989
|
+
ctx.writeErr(`${note} Run it again with --yes to go ahead.`);
|
|
3990
|
+
return 2;
|
|
3991
|
+
}
|
|
3992
|
+
const result = await client.call(name, input);
|
|
3993
|
+
if (op.response === "stream") {
|
|
3994
|
+
const res = result;
|
|
3995
|
+
if (res.body) for await (const chunk of res.body) process.stdout.write(chunk);
|
|
3996
|
+
return 0;
|
|
3997
|
+
}
|
|
3998
|
+
if (result === void 0) {
|
|
3999
|
+
ctx.write(ctx.json ? JSON.stringify({ ok: true }) : "done");
|
|
4000
|
+
return 0;
|
|
4001
|
+
}
|
|
4002
|
+
ctx.write(JSON.stringify(result, null, 2));
|
|
4003
|
+
return 0;
|
|
4004
|
+
};
|
|
415
4005
|
|
|
416
4006
|
// src/commands.ts
|
|
417
4007
|
var makeClient = (ctx) => {
|
|
418
4008
|
const cfg = readConfig();
|
|
419
4009
|
const apiKey = (typeof ctx.flags["api-key"] === "string" ? ctx.flags["api-key"] : void 0) ?? cfg.apiKey;
|
|
420
|
-
if (
|
|
421
|
-
|
|
4010
|
+
if (apiKey) return new import_sdk3.Exayard({ apiKey, baseUrl: cfg.baseUrl });
|
|
4011
|
+
if (cfg.oauth) {
|
|
4012
|
+
return new import_sdk3.Exayard({ bearerToken: cfg.oauth.accessToken, baseUrl: cfg.baseUrl, fetch: sessionFetch(cfg.oauth, saveOAuthSession) });
|
|
422
4013
|
}
|
|
423
|
-
|
|
4014
|
+
throw new Error("Not logged in. Run `exayard login` or set EXAYARD_API_KEY.");
|
|
424
4015
|
};
|
|
425
4016
|
var renderProject = (p) => {
|
|
426
4017
|
const id = String(p._id ?? p.id ?? "???");
|
|
@@ -429,29 +4020,60 @@ var renderProject = (p) => {
|
|
|
429
4020
|
return `${id} ${status.padEnd(10)} ${name}`;
|
|
430
4021
|
};
|
|
431
4022
|
var loginCommand = async (ctx) => {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
ctx.writeErr("Paste an API key from https://exayard.com/settings/api-keys:");
|
|
435
|
-
ctx.writeErr(" exayard login --api-key sk_live_...");
|
|
4023
|
+
if (ctx.flags["api-key"] === true) {
|
|
4024
|
+
ctx.writeErr("Usage: exayard login --api-key exa_live_... (or just `exayard login` to sign in in the browser)");
|
|
436
4025
|
return 2;
|
|
437
4026
|
}
|
|
4027
|
+
const pasted = typeof ctx.flags["api-key"] === "string" ? ctx.flags["api-key"] : void 0;
|
|
438
4028
|
const orgId = typeof ctx.flags.org === "string" ? ctx.flags.org : void 0;
|
|
439
4029
|
const baseUrl = (typeof ctx.flags["base-url"] === "string" ? ctx.flags["base-url"] : void 0) ?? readConfig().baseUrl;
|
|
4030
|
+
if (!pasted) return await browserLoginCommand(ctx, baseUrl, orgId);
|
|
440
4031
|
writeConfig({ apiKey: pasted, organizationId: orgId, baseUrl });
|
|
441
|
-
const exa = new
|
|
4032
|
+
const exa = new import_sdk3.Exayard({ apiKey: pasted, baseUrl });
|
|
442
4033
|
await exa.me.get();
|
|
443
4034
|
ctx.write(ctx.json ? JSON.stringify({ ok: true }) : "Logged in. Token saved to ~/.exayard/config.json.");
|
|
444
4035
|
return 0;
|
|
445
4036
|
};
|
|
4037
|
+
var DEFAULT_BASE_URL = "https://api.exayard.com/v1";
|
|
4038
|
+
var browserLoginCommand = async (ctx, baseUrl, orgId) => {
|
|
4039
|
+
const ac = new AbortController();
|
|
4040
|
+
const onSigint = () => ac.abort();
|
|
4041
|
+
process.once("SIGINT", onSigint);
|
|
4042
|
+
let session;
|
|
4043
|
+
try {
|
|
4044
|
+
session = await browserLogin({ apiBaseUrl: baseUrl ?? DEFAULT_BASE_URL, writeErr: ctx.writeErr, openBrowser: ctx.openBrowser, signal: ac.signal });
|
|
4045
|
+
} catch (err) {
|
|
4046
|
+
if (!(err instanceof OAuthLoginError)) throw err;
|
|
4047
|
+
ctx.writeErr(err.message);
|
|
4048
|
+
return 1;
|
|
4049
|
+
} finally {
|
|
4050
|
+
process.off("SIGINT", onSigint);
|
|
4051
|
+
}
|
|
4052
|
+
const organizationId = orgId ?? readConfigFile().organizationId;
|
|
4053
|
+
writeConfig({ ...baseUrl ? { baseUrl } : {}, ...organizationId ? { organizationId } : {}, oauth: session });
|
|
4054
|
+
const exa = new import_sdk3.Exayard({ bearerToken: session.accessToken, baseUrl, fetch: sessionFetch(session, saveOAuthSession) });
|
|
4055
|
+
const me = await exa.me.get();
|
|
4056
|
+
const companies = me.memberships.map((m) => m.orgId);
|
|
4057
|
+
let saved = organizationId;
|
|
4058
|
+
if (!saved && companies.length === 1) {
|
|
4059
|
+
saved = companies[0];
|
|
4060
|
+
writeConfig({ ...readConfigFile(), organizationId: saved });
|
|
4061
|
+
}
|
|
4062
|
+
if (ctx.json) {
|
|
4063
|
+
ctx.write(JSON.stringify({ ok: true, organizationId: saved ?? null, memberships: me.memberships }));
|
|
4064
|
+
} else {
|
|
4065
|
+
ctx.write(`Logged in${saved ? ` (company ${saved})` : ""}. Saved to ~/.exayard/config.json.`);
|
|
4066
|
+
if (!saved && companies.length > 1) {
|
|
4067
|
+
ctx.writeErr("You belong to several companies. Pass --org <id> on calls, or save one with `exayard login --org <id>`:");
|
|
4068
|
+
for (const m of me.memberships) ctx.writeErr(` ${m.orgId}${m.role ? ` ${m.role}` : ""}`);
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
if (process.env.EXAYARD_API_KEY) ctx.writeErr("EXAYARD_API_KEY is set; calls use that key, not this sign-in, while it is.");
|
|
4072
|
+
return 0;
|
|
4073
|
+
};
|
|
446
4074
|
var projectsListCommand = async (ctx) => {
|
|
447
4075
|
const exa = makeClient(ctx);
|
|
448
|
-
const
|
|
449
|
-
const organizationId = (typeof ctx.flags.org === "string" ? ctx.flags.org : void 0) ?? cfg.organizationId;
|
|
450
|
-
if (!organizationId) {
|
|
451
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
452
|
-
return 2;
|
|
453
|
-
}
|
|
454
|
-
const res = await exa.projects.list({ organizationId });
|
|
4076
|
+
const res = await exa.projects.list({ organizationId: resolveOrgId(ctx) });
|
|
455
4077
|
if (ctx.json) {
|
|
456
4078
|
ctx.write(JSON.stringify(res, null, 2));
|
|
457
4079
|
} else {
|
|
@@ -476,15 +4098,11 @@ var assessmentsRunCommand = async (ctx) => {
|
|
|
476
4098
|
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
477
4099
|
if (!projectId) {
|
|
478
4100
|
ctx.writeErr(
|
|
479
|
-
`Usage: exayard assessments run <project_id> --
|
|
4101
|
+
`Usage: exayard assessments run <project_id> --pages <id1,id2> --elements '[{"id","name","category","hexColor"}]' [--org <org_id>]`
|
|
480
4102
|
);
|
|
481
4103
|
return 2;
|
|
482
4104
|
}
|
|
483
4105
|
const organizationId = resolveOrgId(ctx);
|
|
484
|
-
if (!organizationId) {
|
|
485
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
486
|
-
return 2;
|
|
487
|
-
}
|
|
488
4106
|
const pageIds = typeof ctx.flags.pages === "string" ? ctx.flags.pages.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
489
4107
|
if (pageIds.length === 0) {
|
|
490
4108
|
ctx.writeErr("--pages <pageId1,pageId2> required.");
|
|
@@ -504,12 +4122,7 @@ var filesUploadCommand = async (ctx) => {
|
|
|
504
4122
|
const path = typeof ctx.flags.file === "string" ? ctx.flags.file : ctx.positional[0];
|
|
505
4123
|
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : void 0;
|
|
506
4124
|
if (!path || !projectId) {
|
|
507
|
-
ctx.writeErr("Usage: exayard files upload <path> --project <projectId>
|
|
508
|
-
return 2;
|
|
509
|
-
}
|
|
510
|
-
const organizationId = resolveOrgId(ctx);
|
|
511
|
-
if (!organizationId) {
|
|
512
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
4125
|
+
ctx.writeErr("Usage: exayard files upload <path> --project <projectId>");
|
|
513
4126
|
return 2;
|
|
514
4127
|
}
|
|
515
4128
|
const { readFileSync: readFileSync2 } = await import("fs");
|
|
@@ -519,7 +4132,6 @@ var filesUploadCommand = async (ctx) => {
|
|
|
519
4132
|
const mimeType = ext === ".pdf" ? "application/pdf" : ext === ".png" ? "image/png" : ext === ".jpg" || ext === ".jpeg" ? "image/jpeg" : "application/octet-stream";
|
|
520
4133
|
const exa = makeClient(ctx);
|
|
521
4134
|
const res = await exa.files.upload({
|
|
522
|
-
organizationId,
|
|
523
4135
|
projectId,
|
|
524
4136
|
filename: basename(path),
|
|
525
4137
|
mimeType,
|
|
@@ -533,14 +4145,10 @@ var filesUploadCommand = async (ctx) => {
|
|
|
533
4145
|
var pagesListCommand = async (ctx) => {
|
|
534
4146
|
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
535
4147
|
if (!projectId) {
|
|
536
|
-
ctx.writeErr("Usage: exayard pages list <project_id> [--org
|
|
4148
|
+
ctx.writeErr("Usage: exayard pages list <project_id> [--org <org_id>]");
|
|
537
4149
|
return 2;
|
|
538
4150
|
}
|
|
539
4151
|
const organizationId = resolveOrgId(ctx);
|
|
540
|
-
if (!organizationId) {
|
|
541
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
542
|
-
return 2;
|
|
543
|
-
}
|
|
544
4152
|
const exa = makeClient(ctx);
|
|
545
4153
|
const res = await exa.pages.list(projectId, { organizationId });
|
|
546
4154
|
ctx.write(JSON.stringify(res, null, 2));
|
|
@@ -549,14 +4157,10 @@ var pagesListCommand = async (ctx) => {
|
|
|
549
4157
|
var assessmentsProposeCommand = async (ctx) => {
|
|
550
4158
|
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
551
4159
|
if (!projectId) {
|
|
552
|
-
ctx.writeErr('Usage: exayard assessments propose <project_id> --prompt "..." [--pages id1,id2] [--org
|
|
4160
|
+
ctx.writeErr('Usage: exayard assessments propose <project_id> --prompt "..." [--pages id1,id2] [--org <org_id>]');
|
|
553
4161
|
return 2;
|
|
554
4162
|
}
|
|
555
4163
|
const organizationId = resolveOrgId(ctx);
|
|
556
|
-
if (!organizationId) {
|
|
557
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
558
|
-
return 2;
|
|
559
|
-
}
|
|
560
4164
|
const prompt = typeof ctx.flags.prompt === "string" ? ctx.flags.prompt : ctx.positional[1];
|
|
561
4165
|
if (!prompt) {
|
|
562
4166
|
ctx.writeErr('--prompt "<what to measure>" required.');
|
|
@@ -602,37 +4206,30 @@ var takeoffSummaryCommand = async (ctx) => {
|
|
|
602
4206
|
return 0;
|
|
603
4207
|
};
|
|
604
4208
|
var webhooksListenCommand = async (ctx) => {
|
|
605
|
-
if (process.env.EXAYARD_EXPERIMENTAL !== "1") {
|
|
606
|
-
ctx.writeErr(
|
|
607
|
-
[
|
|
608
|
-
"exayard listen is not yet implemented.",
|
|
609
|
-
"",
|
|
610
|
-
"The tunnel implementation is still in progress. To preview the command",
|
|
611
|
-
"surface, set EXAYARD_EXPERIMENTAL=1 \u2014 but note the tunnel itself will not",
|
|
612
|
-
"forward events yet.",
|
|
613
|
-
"",
|
|
614
|
-
"For now, register a webhook endpoint pointing at a public tunnel",
|
|
615
|
-
"(ngrok, cloudflared) and verify signatures locally with:",
|
|
616
|
-
"",
|
|
617
|
-
' echo "$RAW_BODY" | exayard webhooks verify --signature "$EXAYARD_SIGNATURE" --secret whsec_...'
|
|
618
|
-
].join("\n")
|
|
619
|
-
);
|
|
620
|
-
return 2;
|
|
621
|
-
}
|
|
622
4209
|
const forwardTo = typeof ctx.flags["forward-to"] === "string" ? ctx.flags["forward-to"] : void 0;
|
|
623
|
-
if (
|
|
624
|
-
ctx.writeErr(
|
|
4210
|
+
if (ctx.flags["forward-to"] === true) {
|
|
4211
|
+
ctx.writeErr('Usage: exayard listen [--forward-to http://localhost:3000/webhooks] [--events "bid.won,project.created"]');
|
|
625
4212
|
return 2;
|
|
626
4213
|
}
|
|
627
|
-
ctx.
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
4214
|
+
const events = typeof ctx.flags.events === "string" ? ctx.flags.events.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
4215
|
+
const exa = makeClient(ctx);
|
|
4216
|
+
const ac = new AbortController();
|
|
4217
|
+
const onSigint = () => ac.abort();
|
|
4218
|
+
process.once("SIGINT", onSigint);
|
|
4219
|
+
try {
|
|
4220
|
+
return await runListen(exa, {
|
|
4221
|
+
forwardTo,
|
|
4222
|
+
events,
|
|
4223
|
+
organizationId: resolveOrgId(ctx),
|
|
4224
|
+
name: typeof ctx.flags.name === "string" ? ctx.flags.name : void 0,
|
|
4225
|
+
json: ctx.json,
|
|
4226
|
+
write: ctx.write,
|
|
4227
|
+
writeErr: ctx.writeErr,
|
|
4228
|
+
signal: ac.signal
|
|
4229
|
+
});
|
|
4230
|
+
} finally {
|
|
4231
|
+
process.off("SIGINT", onSigint);
|
|
4232
|
+
}
|
|
636
4233
|
};
|
|
637
4234
|
var webhooksVerifyCommand = async (ctx) => {
|
|
638
4235
|
const signature = typeof ctx.flags.signature === "string" ? ctx.flags.signature : void 0;
|
|
@@ -643,11 +4240,11 @@ var webhooksVerifyCommand = async (ctx) => {
|
|
|
643
4240
|
}
|
|
644
4241
|
const body = await readAllStdin();
|
|
645
4242
|
try {
|
|
646
|
-
const event = await (0,
|
|
4243
|
+
const event = await (0, import_sdk3.constructWebhookEvent)(body, signature, secret);
|
|
647
4244
|
ctx.write(ctx.json ? JSON.stringify(event, null, 2) : `verified: ${event.type} (${event.id})`);
|
|
648
4245
|
return 0;
|
|
649
4246
|
} catch (err) {
|
|
650
|
-
if (err instanceof
|
|
4247
|
+
if (err instanceof import_sdk3.WebhookSignatureError) {
|
|
651
4248
|
ctx.writeErr(`Signature invalid (${err.code}): ${err.message}`);
|
|
652
4249
|
} else {
|
|
653
4250
|
ctx.writeErr(`Verify failed: ${err.message}`);
|
|
@@ -682,35 +4279,27 @@ var helpSearchCommand = async (ctx) => {
|
|
|
682
4279
|
};
|
|
683
4280
|
var resolveOrgId = (ctx) => {
|
|
684
4281
|
const fromFlag = typeof ctx.flags.org === "string" ? ctx.flags.org : void 0;
|
|
685
|
-
return fromFlag ?? readConfig().organizationId ??
|
|
4282
|
+
return fromFlag ?? readConfig().organizationId ?? void 0;
|
|
686
4283
|
};
|
|
687
4284
|
var projectsCreateCommand = async (ctx) => {
|
|
688
4285
|
const name = typeof ctx.flags.name === "string" ? ctx.flags.name : ctx.positional[0];
|
|
689
4286
|
if (!name) {
|
|
690
|
-
ctx.writeErr('Usage: exayard projects create --name "<project name>" [--org
|
|
4287
|
+
ctx.writeErr('Usage: exayard projects create --name "<project name>" [--org <org_id>]');
|
|
691
4288
|
return 2;
|
|
692
4289
|
}
|
|
693
4290
|
const organizationId = resolveOrgId(ctx);
|
|
694
|
-
if (!organizationId) {
|
|
695
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
696
|
-
return 2;
|
|
697
|
-
}
|
|
698
4291
|
const exa = makeClient(ctx);
|
|
699
4292
|
const res = await exa.projects.create({ organizationId, name });
|
|
700
|
-
ctx.write(ctx.json ? JSON.stringify(res, null, 2) : `created ${res.
|
|
4293
|
+
ctx.write(ctx.json ? JSON.stringify(res, null, 2) : `created ${res.id} ${name}`);
|
|
701
4294
|
return 0;
|
|
702
4295
|
};
|
|
703
4296
|
var projectsArchiveCommand = async (ctx) => {
|
|
704
4297
|
const id = typeof ctx.flags.id === "string" ? ctx.flags.id : ctx.positional[0];
|
|
705
4298
|
if (!id) {
|
|
706
|
-
ctx.writeErr("Usage: exayard projects archive <project_id> [--org
|
|
4299
|
+
ctx.writeErr("Usage: exayard projects archive <project_id> [--org <org_id>]");
|
|
707
4300
|
return 2;
|
|
708
4301
|
}
|
|
709
4302
|
const organizationId = resolveOrgId(ctx);
|
|
710
|
-
if (!organizationId) {
|
|
711
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
712
|
-
return 2;
|
|
713
|
-
}
|
|
714
4303
|
const exa = makeClient(ctx);
|
|
715
4304
|
await exa.projects.archive(id, { organizationId });
|
|
716
4305
|
ctx.write(ctx.json ? JSON.stringify({ ok: true, archived: id }) : `archived ${id}`);
|
|
@@ -718,10 +4307,6 @@ var projectsArchiveCommand = async (ctx) => {
|
|
|
718
4307
|
};
|
|
719
4308
|
var webhooksListCommand = async (ctx) => {
|
|
720
4309
|
const organizationId = resolveOrgId(ctx);
|
|
721
|
-
if (!organizationId) {
|
|
722
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
723
|
-
return 2;
|
|
724
|
-
}
|
|
725
4310
|
const exa = makeClient(ctx);
|
|
726
4311
|
const res = await exa.webhooks.listEndpoints({ organizationId });
|
|
727
4312
|
if (ctx.json) {
|
|
@@ -745,10 +4330,6 @@ var webhooksCreateCommand = async (ctx) => {
|
|
|
745
4330
|
return 2;
|
|
746
4331
|
}
|
|
747
4332
|
const organizationId = resolveOrgId(ctx);
|
|
748
|
-
if (!organizationId) {
|
|
749
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
750
|
-
return 2;
|
|
751
|
-
}
|
|
752
4333
|
const events = eventsFlag.split(",").map((s) => s.trim()).filter(Boolean);
|
|
753
4334
|
const exa = makeClient(ctx);
|
|
754
4335
|
const res = await exa.webhooks.createEndpoint({ organizationId, url, events, description });
|
|
@@ -763,14 +4344,10 @@ var webhooksCreateCommand = async (ctx) => {
|
|
|
763
4344
|
var webhooksDeleteCommand = async (ctx) => {
|
|
764
4345
|
const id = typeof ctx.flags.id === "string" ? ctx.flags.id : ctx.positional[0];
|
|
765
4346
|
if (!id) {
|
|
766
|
-
ctx.writeErr("Usage: exayard webhooks delete <endpoint_id> [--org
|
|
4347
|
+
ctx.writeErr("Usage: exayard webhooks delete <endpoint_id> [--org <org_id>]");
|
|
767
4348
|
return 2;
|
|
768
4349
|
}
|
|
769
4350
|
const organizationId = resolveOrgId(ctx);
|
|
770
|
-
if (!organizationId) {
|
|
771
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
772
|
-
return 2;
|
|
773
|
-
}
|
|
774
4351
|
const exa = makeClient(ctx);
|
|
775
4352
|
await exa.webhooks.deleteEndpoint(id, { organizationId });
|
|
776
4353
|
ctx.write(ctx.json ? JSON.stringify({ ok: true, deleted: id }) : `deleted ${id}`);
|
|
@@ -814,25 +4391,33 @@ var COMMANDS = {
|
|
|
814
4391
|
"help-search": helpSearchCommand,
|
|
815
4392
|
init: initCommand
|
|
816
4393
|
};
|
|
4394
|
+
var commandFor = (command) => {
|
|
4395
|
+
const handWritten = COMMANDS[command];
|
|
4396
|
+
if (handWritten) return handWritten;
|
|
4397
|
+
const operation = operationForCommand(command);
|
|
4398
|
+
if (!operation) return void 0;
|
|
4399
|
+
return async (ctx) => runOperation(operation, ctx, makeClient(ctx), readConfig().organizationId);
|
|
4400
|
+
};
|
|
817
4401
|
|
|
818
4402
|
// src/bin.ts
|
|
819
4403
|
var HELP_TEXT = `exayard \u2014 CLI for the Exayard API
|
|
820
4404
|
|
|
821
4405
|
Auth:
|
|
822
|
-
exayard login
|
|
4406
|
+
exayard login Sign in in the browser
|
|
4407
|
+
exayard login --api-key exa_live_... Save an API key instead
|
|
823
4408
|
|
|
824
4409
|
Projects:
|
|
825
|
-
exayard projects list
|
|
4410
|
+
exayard projects list List projects
|
|
826
4411
|
exayard projects create --name "<name>" Create a project
|
|
827
4412
|
exayard projects archive <project_id> Archive a project
|
|
828
4413
|
|
|
829
4414
|
AI takeoff (full flow):
|
|
830
|
-
exayard files upload <path> --project <id>
|
|
4415
|
+
exayard files upload <path> --project <id>
|
|
831
4416
|
Upload a plan PDF (extracts to pages)
|
|
832
|
-
exayard pages list <project_id>
|
|
4417
|
+
exayard pages list <project_id> List pages + extraction status
|
|
833
4418
|
exayard assessments propose <project_id> --prompt "measure walls + roof"
|
|
834
4419
|
Ask AI what to measure -> elements
|
|
835
|
-
exayard assessments run <project_id> --
|
|
4420
|
+
exayard assessments run <project_id> --pages <id1,id2> --elements '<json>'
|
|
836
4421
|
Run an AI takeoff to completion
|
|
837
4422
|
exayard assessments latest <project_id> Most recent assessment + elements
|
|
838
4423
|
exayard assessments list <project_id> All assessments for a project
|
|
@@ -844,7 +4429,14 @@ Webhooks:
|
|
|
844
4429
|
exayard webhooks create --url <https://...> --events "project.created,bid.generated"
|
|
845
4430
|
exayard webhooks delete <endpoint_id> Revoke an endpoint
|
|
846
4431
|
exayard webhooks verify --signature ... --secret ... < body.json
|
|
847
|
-
exayard listen --forward-to http://localhost
|
|
4432
|
+
exayard listen --forward-to http://localhost:3000/webhooks [--events a,b]
|
|
4433
|
+
Forward your events to a local address
|
|
4434
|
+
|
|
4435
|
+
Every API operation:
|
|
4436
|
+
exayard api List every operation as a command
|
|
4437
|
+
exayard <operation> --help Show an operation's fields
|
|
4438
|
+
exayard list-vendor-quotes <project_id> --status received
|
|
4439
|
+
Positionals fill the path, flags the rest
|
|
848
4440
|
|
|
849
4441
|
Help / misc:
|
|
850
4442
|
exayard help-search "how do I run a takeoff" Search help articles
|
|
@@ -853,10 +4445,12 @@ Help / misc:
|
|
|
853
4445
|
Global flags:
|
|
854
4446
|
--json Emit machine-readable output + suppress prose
|
|
855
4447
|
--api-key <key> Override credentials for this call
|
|
856
|
-
--org <id>
|
|
4448
|
+
--org <id> The company to act in (a company key or app key implies its own)
|
|
4449
|
+
--yes Run an operation that deletes, spends, sends or buys
|
|
857
4450
|
|
|
858
4451
|
See https://developers.exayard.com/api-reference for the full API reference.
|
|
859
4452
|
`;
|
|
4453
|
+
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["json", "yes", "help", "h", "force"]);
|
|
860
4454
|
var parseArgs = (argv) => {
|
|
861
4455
|
let command = null;
|
|
862
4456
|
const flags = {};
|
|
@@ -865,7 +4459,7 @@ var parseArgs = (argv) => {
|
|
|
865
4459
|
while (i < argv.length) {
|
|
866
4460
|
const arg = argv[i];
|
|
867
4461
|
if (!command && !arg.startsWith("-")) {
|
|
868
|
-
if (argv[i + 1] && !argv[i + 1].startsWith("-") && !arg.includes(":")) {
|
|
4462
|
+
if (argv[i + 1] && !argv[i + 1].startsWith("-") && !arg.includes(":") && !operationForCommand(arg)) {
|
|
869
4463
|
command = `${arg}:${argv[i + 1]}`;
|
|
870
4464
|
i += 2;
|
|
871
4465
|
continue;
|
|
@@ -882,7 +4476,7 @@ var parseArgs = (argv) => {
|
|
|
882
4476
|
} else {
|
|
883
4477
|
const key = arg.slice(2);
|
|
884
4478
|
const next = argv[i + 1];
|
|
885
|
-
if (next && !next.startsWith("-")) {
|
|
4479
|
+
if (next && !next.startsWith("-") && !BOOLEAN_FLAGS.has(key)) {
|
|
886
4480
|
flags[key] = next;
|
|
887
4481
|
i += 2;
|
|
888
4482
|
} else {
|
|
@@ -899,11 +4493,20 @@ var parseArgs = (argv) => {
|
|
|
899
4493
|
};
|
|
900
4494
|
var main = async (argv) => {
|
|
901
4495
|
const { command, flags, positional } = parseArgs(argv);
|
|
4496
|
+
const operation = command ? operationForCommand(command) : void 0;
|
|
4497
|
+
if (operation && (flags.help || flags.h)) {
|
|
4498
|
+
process.stdout.write(operationHelp(operation));
|
|
4499
|
+
return 0;
|
|
4500
|
+
}
|
|
4501
|
+
if (command === "api") {
|
|
4502
|
+
process.stdout.write(operationsIndex());
|
|
4503
|
+
return 0;
|
|
4504
|
+
}
|
|
902
4505
|
if (!command || flags.help || flags.h || command === "help") {
|
|
903
4506
|
process.stdout.write(HELP_TEXT);
|
|
904
4507
|
return command ? 0 : 2;
|
|
905
4508
|
}
|
|
906
|
-
const impl =
|
|
4509
|
+
const impl = commandFor(command);
|
|
907
4510
|
if (!impl) {
|
|
908
4511
|
process.stderr.write(`Unknown command: ${command}
|
|
909
4512
|
|
|
@@ -922,7 +4525,7 @@ ${HELP_TEXT}`);
|
|
|
922
4525
|
try {
|
|
923
4526
|
return await impl(ctx);
|
|
924
4527
|
} catch (err) {
|
|
925
|
-
if (err instanceof
|
|
4528
|
+
if (err instanceof import_sdk4.ExayardError) {
|
|
926
4529
|
const body = {
|
|
927
4530
|
code: err.code,
|
|
928
4531
|
title: err.title,
|