@exayard/cli 0.1.0 → 0.2.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/dist/bin.js +241 -0
- package/dist/index.js +228 -0
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -254,6 +254,86 @@ var require_client = __commonJS({
|
|
|
254
254
|
export: (id, query) => this.request({ method: "GET", path: `/projects/${id}/export`, query }),
|
|
255
255
|
archive: (id, query) => this.request({ method: "POST", path: `/projects/${id}/archive`, query })
|
|
256
256
|
};
|
|
257
|
+
// File upload is a 3-step dance: presign (get an R2 upload URL) → PUT the
|
|
258
|
+
// bytes straight to R2 → confirm (which also kicks off PDF page extraction).
|
|
259
|
+
// `upload` wraps all three. A PDF becomes pages asynchronously after confirm —
|
|
260
|
+
// poll pages.list() until processingStatus is 'complete' before proposing/running.
|
|
261
|
+
files = {
|
|
262
|
+
presign: (body) => this.request({
|
|
263
|
+
method: "POST",
|
|
264
|
+
path: "/files",
|
|
265
|
+
body
|
|
266
|
+
}),
|
|
267
|
+
confirm: (fileId, r2Key) => this.request({ method: "POST", path: `/files/${fileId}/confirm`, body: { r2Key } }),
|
|
268
|
+
// Convenience: presign → PUT bytes to the presigned R2 URL → confirm.
|
|
269
|
+
// The PUT goes directly to R2 (a different host, self-authenticated by the
|
|
270
|
+
// signed URL) — not through the /v1 request primitive.
|
|
271
|
+
upload: async (body) => {
|
|
272
|
+
const buf = body.bytes instanceof ArrayBuffer ? new Uint8Array(body.bytes) : body.bytes;
|
|
273
|
+
const { fileId, uploadUrl, r2Key } = await this.files.presign({
|
|
274
|
+
organizationId: body.organizationId,
|
|
275
|
+
projectId: body.projectId,
|
|
276
|
+
filename: body.filename,
|
|
277
|
+
mimeType: body.mimeType,
|
|
278
|
+
fileSize: buf.byteLength,
|
|
279
|
+
folderId: body.folderId
|
|
280
|
+
});
|
|
281
|
+
const put = await this.fetchImpl(uploadUrl, {
|
|
282
|
+
method: "PUT",
|
|
283
|
+
headers: { "Content-Type": body.mimeType },
|
|
284
|
+
// Node/undici fetch accepts a Uint8Array body at runtime; the DOM
|
|
285
|
+
// BodyInit type doesn't list it, so cast at this boundary.
|
|
286
|
+
body: buf
|
|
287
|
+
});
|
|
288
|
+
if (!put.ok) {
|
|
289
|
+
throw new error_1.ExayardError({
|
|
290
|
+
type: "https://developers.exayard.com/concepts/errors#transport_error",
|
|
291
|
+
title: "Upload Failed",
|
|
292
|
+
status: put.status,
|
|
293
|
+
detail: `PUT to storage failed: HTTP ${put.status}`,
|
|
294
|
+
code: "transport_error",
|
|
295
|
+
doc_url: "https://developers.exayard.com/concepts/errors#transport_error"
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
await this.files.confirm(fileId, r2Key);
|
|
299
|
+
return { fileId };
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
// List a project's pages (grouped by file). Use to discover pageIds + poll
|
|
303
|
+
// each file's processingStatus until extraction finishes.
|
|
304
|
+
pages = {
|
|
305
|
+
list: (projectId, query) => this.request({ method: "GET", path: `/projects/${projectId}/pages`, query })
|
|
306
|
+
};
|
|
307
|
+
// AI takeoff assessments. Reads resolve the org from the project/assessment
|
|
308
|
+
// (no organizationId). `start` kicks off an AI run on an existing project's
|
|
309
|
+
// pages — pair with the assessment.completed webhook + latest()/takeoffSummary
|
|
310
|
+
// to pull results once it finishes.
|
|
311
|
+
assessments = {
|
|
312
|
+
list: (projectId) => this.request({ method: "GET", path: `/projects/${projectId}/assessments` }),
|
|
313
|
+
latest: (projectId) => this.request({ method: "GET", path: `/projects/${projectId}/assessments/latest` }),
|
|
314
|
+
get: (id) => this.request({ method: "GET", path: `/assessments/${id}` }),
|
|
315
|
+
takeoffSummary: (projectId) => this.request({ method: "GET", path: `/projects/${projectId}/takeoff-summary` }),
|
|
316
|
+
// Ask the AI what to measure: a natural-language prompt + pageIds → a
|
|
317
|
+
// proposed `elements` array (id/name/category/hexColor) you can pass
|
|
318
|
+
// straight to run(). The bridge for callers who don't already know the
|
|
319
|
+
// elements. Returns { elements, creditEstimate, ... }.
|
|
320
|
+
propose: (projectId, body) => this.request({ method: "POST", path: `/projects/${projectId}/analysis/propose`, body }),
|
|
321
|
+
// Run an AI takeoff analysis on an existing project and let it complete
|
|
322
|
+
// end-to-end (no human approval step). You specify exactly what to detect
|
|
323
|
+
// via `elements` (each: id, name, category area|linear|count, hexColor) on
|
|
324
|
+
// the given pageIds. Returns { assessmentId, layerId, pageIds }; track
|
|
325
|
+
// completion via the assessment.completed webhook + latest()/takeoffSummary.
|
|
326
|
+
//
|
|
327
|
+
// (This maps to POST /v1/projects/{id}/analysis/run — the pre-approved path
|
|
328
|
+
// that runs to completion. The auto-detect POST /assessments path pauses at
|
|
329
|
+
// awaiting_approval, which needs a human, so it isn't exposed here.)
|
|
330
|
+
run: (projectId, body, opts = {}) => this.request({
|
|
331
|
+
method: "POST",
|
|
332
|
+
path: `/projects/${projectId}/analysis/run`,
|
|
333
|
+
body,
|
|
334
|
+
idempotencyKey: opts.idempotencyKey
|
|
335
|
+
})
|
|
336
|
+
};
|
|
257
337
|
help = {
|
|
258
338
|
search: (body) => this.request({ method: "POST", path: "/help/search", body, autoIdempotencyKey: false })
|
|
259
339
|
};
|
|
@@ -381,6 +461,146 @@ var projectsListCommand = async (ctx) => {
|
|
|
381
461
|
}
|
|
382
462
|
return 0;
|
|
383
463
|
};
|
|
464
|
+
var assessmentsListCommand = async (ctx) => {
|
|
465
|
+
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
466
|
+
if (!projectId) {
|
|
467
|
+
ctx.writeErr("Usage: exayard assessments list <project_id>");
|
|
468
|
+
return 2;
|
|
469
|
+
}
|
|
470
|
+
const exa = makeClient(ctx);
|
|
471
|
+
const res = await exa.assessments.list(projectId);
|
|
472
|
+
ctx.write(JSON.stringify(res, null, 2));
|
|
473
|
+
return 0;
|
|
474
|
+
};
|
|
475
|
+
var assessmentsRunCommand = async (ctx) => {
|
|
476
|
+
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
477
|
+
if (!projectId) {
|
|
478
|
+
ctx.writeErr(
|
|
479
|
+
`Usage: exayard assessments run <project_id> --org org_... --pages <id1,id2> --elements '[{"id","name","category","hexColor"}]'`
|
|
480
|
+
);
|
|
481
|
+
return 2;
|
|
482
|
+
}
|
|
483
|
+
const organizationId = resolveOrgId(ctx);
|
|
484
|
+
if (!organizationId) {
|
|
485
|
+
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
486
|
+
return 2;
|
|
487
|
+
}
|
|
488
|
+
const pageIds = typeof ctx.flags.pages === "string" ? ctx.flags.pages.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
489
|
+
if (pageIds.length === 0) {
|
|
490
|
+
ctx.writeErr("--pages <pageId1,pageId2> required.");
|
|
491
|
+
return 2;
|
|
492
|
+
}
|
|
493
|
+
if (typeof ctx.flags.elements !== "string") {
|
|
494
|
+
ctx.writeErr(`--elements '[{"id","name","category","hexColor"}]' required (what to detect).`);
|
|
495
|
+
return 2;
|
|
496
|
+
}
|
|
497
|
+
const elements = JSON.parse(ctx.flags.elements);
|
|
498
|
+
const exa = makeClient(ctx);
|
|
499
|
+
const res = await exa.assessments.run(projectId, { organizationId, pageIds, elements });
|
|
500
|
+
ctx.write(JSON.stringify(res, null, 2));
|
|
501
|
+
return 0;
|
|
502
|
+
};
|
|
503
|
+
var filesUploadCommand = async (ctx) => {
|
|
504
|
+
const path = typeof ctx.flags.file === "string" ? ctx.flags.file : ctx.positional[0];
|
|
505
|
+
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : void 0;
|
|
506
|
+
if (!path || !projectId) {
|
|
507
|
+
ctx.writeErr("Usage: exayard files upload <path> --project <projectId> [--org org_...]");
|
|
508
|
+
return 2;
|
|
509
|
+
}
|
|
510
|
+
const organizationId = resolveOrgId(ctx);
|
|
511
|
+
if (!organizationId) {
|
|
512
|
+
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
513
|
+
return 2;
|
|
514
|
+
}
|
|
515
|
+
const { readFileSync: readFileSync2 } = await import("fs");
|
|
516
|
+
const { basename, extname } = await import("path");
|
|
517
|
+
const bytes = readFileSync2(path);
|
|
518
|
+
const ext = extname(path).toLowerCase();
|
|
519
|
+
const mimeType = ext === ".pdf" ? "application/pdf" : ext === ".png" ? "image/png" : ext === ".jpg" || ext === ".jpeg" ? "image/jpeg" : "application/octet-stream";
|
|
520
|
+
const exa = makeClient(ctx);
|
|
521
|
+
const res = await exa.files.upload({
|
|
522
|
+
organizationId,
|
|
523
|
+
projectId,
|
|
524
|
+
filename: basename(path),
|
|
525
|
+
mimeType,
|
|
526
|
+
bytes: new Uint8Array(bytes)
|
|
527
|
+
});
|
|
528
|
+
ctx.write(
|
|
529
|
+
ctx.json ? JSON.stringify(res, null, 2) : `uploaded ${res.fileId} ${basename(path)} \u2014 extraction started; run 'exayard pages list ${projectId}' to watch`
|
|
530
|
+
);
|
|
531
|
+
return 0;
|
|
532
|
+
};
|
|
533
|
+
var pagesListCommand = async (ctx) => {
|
|
534
|
+
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
535
|
+
if (!projectId) {
|
|
536
|
+
ctx.writeErr("Usage: exayard pages list <project_id> [--org org_...]");
|
|
537
|
+
return 2;
|
|
538
|
+
}
|
|
539
|
+
const organizationId = resolveOrgId(ctx);
|
|
540
|
+
if (!organizationId) {
|
|
541
|
+
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
542
|
+
return 2;
|
|
543
|
+
}
|
|
544
|
+
const exa = makeClient(ctx);
|
|
545
|
+
const res = await exa.pages.list(projectId, { organizationId });
|
|
546
|
+
ctx.write(JSON.stringify(res, null, 2));
|
|
547
|
+
return 0;
|
|
548
|
+
};
|
|
549
|
+
var assessmentsProposeCommand = async (ctx) => {
|
|
550
|
+
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
551
|
+
if (!projectId) {
|
|
552
|
+
ctx.writeErr('Usage: exayard assessments propose <project_id> --prompt "..." [--pages id1,id2] [--org org_...]');
|
|
553
|
+
return 2;
|
|
554
|
+
}
|
|
555
|
+
const organizationId = resolveOrgId(ctx);
|
|
556
|
+
if (!organizationId) {
|
|
557
|
+
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
558
|
+
return 2;
|
|
559
|
+
}
|
|
560
|
+
const prompt = typeof ctx.flags.prompt === "string" ? ctx.flags.prompt : ctx.positional[1];
|
|
561
|
+
if (!prompt) {
|
|
562
|
+
ctx.writeErr('--prompt "<what to measure>" required.');
|
|
563
|
+
return 2;
|
|
564
|
+
}
|
|
565
|
+
const pageIds = typeof ctx.flags.pages === "string" ? ctx.flags.pages.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
566
|
+
const exa = makeClient(ctx);
|
|
567
|
+
const res = await exa.assessments.propose(projectId, { organizationId, prompt, pageIds });
|
|
568
|
+
ctx.write(JSON.stringify(res, null, 2));
|
|
569
|
+
return 0;
|
|
570
|
+
};
|
|
571
|
+
var assessmentsLatestCommand = async (ctx) => {
|
|
572
|
+
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
573
|
+
if (!projectId) {
|
|
574
|
+
ctx.writeErr("Usage: exayard assessments latest <project_id>");
|
|
575
|
+
return 2;
|
|
576
|
+
}
|
|
577
|
+
const exa = makeClient(ctx);
|
|
578
|
+
const res = await exa.assessments.latest(projectId);
|
|
579
|
+
ctx.write(JSON.stringify(res, null, 2));
|
|
580
|
+
return 0;
|
|
581
|
+
};
|
|
582
|
+
var assessmentsGetCommand = async (ctx) => {
|
|
583
|
+
const id = typeof ctx.flags.id === "string" ? ctx.flags.id : ctx.positional[0];
|
|
584
|
+
if (!id) {
|
|
585
|
+
ctx.writeErr("Usage: exayard assessments get <assessment_id>");
|
|
586
|
+
return 2;
|
|
587
|
+
}
|
|
588
|
+
const exa = makeClient(ctx);
|
|
589
|
+
const res = await exa.assessments.get(id);
|
|
590
|
+
ctx.write(JSON.stringify(res, null, 2));
|
|
591
|
+
return 0;
|
|
592
|
+
};
|
|
593
|
+
var takeoffSummaryCommand = async (ctx) => {
|
|
594
|
+
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
595
|
+
if (!projectId) {
|
|
596
|
+
ctx.writeErr("Usage: exayard takeoff summary <project_id>");
|
|
597
|
+
return 2;
|
|
598
|
+
}
|
|
599
|
+
const exa = makeClient(ctx);
|
|
600
|
+
const res = await exa.assessments.takeoffSummary(projectId);
|
|
601
|
+
ctx.write(JSON.stringify(res, null, 2));
|
|
602
|
+
return 0;
|
|
603
|
+
};
|
|
384
604
|
var webhooksListenCommand = async (ctx) => {
|
|
385
605
|
if (process.env.EXAYARD_EXPERIMENTAL !== "1") {
|
|
386
606
|
ctx.writeErr(
|
|
@@ -578,6 +798,14 @@ var COMMANDS = {
|
|
|
578
798
|
"projects:list": projectsListCommand,
|
|
579
799
|
"projects:create": projectsCreateCommand,
|
|
580
800
|
"projects:archive": projectsArchiveCommand,
|
|
801
|
+
"files:upload": filesUploadCommand,
|
|
802
|
+
"pages:list": pagesListCommand,
|
|
803
|
+
"assessments:list": assessmentsListCommand,
|
|
804
|
+
"assessments:latest": assessmentsLatestCommand,
|
|
805
|
+
"assessments:get": assessmentsGetCommand,
|
|
806
|
+
"assessments:propose": assessmentsProposeCommand,
|
|
807
|
+
"assessments:run": assessmentsRunCommand,
|
|
808
|
+
"takeoff:summary": takeoffSummaryCommand,
|
|
581
809
|
"webhooks:list": webhooksListCommand,
|
|
582
810
|
"webhooks:create": webhooksCreateCommand,
|
|
583
811
|
"webhooks:delete": webhooksDeleteCommand,
|
|
@@ -598,6 +826,19 @@ Projects:
|
|
|
598
826
|
exayard projects create --name "<name>" Create a project
|
|
599
827
|
exayard projects archive <project_id> Archive a project
|
|
600
828
|
|
|
829
|
+
AI takeoff (full flow):
|
|
830
|
+
exayard files upload <path> --project <id> --org org_...
|
|
831
|
+
Upload a plan PDF (extracts to pages)
|
|
832
|
+
exayard pages list <project_id> --org org_... List pages + extraction status
|
|
833
|
+
exayard assessments propose <project_id> --prompt "measure walls + roof"
|
|
834
|
+
Ask AI what to measure -> elements
|
|
835
|
+
exayard assessments run <project_id> --org org_... --pages <id1,id2> --elements '<json>'
|
|
836
|
+
Run an AI takeoff to completion
|
|
837
|
+
exayard assessments latest <project_id> Most recent assessment + elements
|
|
838
|
+
exayard assessments list <project_id> All assessments for a project
|
|
839
|
+
exayard assessments get <assessment_id> One assessment by ID
|
|
840
|
+
exayard takeoff summary <project_id> Aggregated count/linear/area totals
|
|
841
|
+
|
|
601
842
|
Webhooks:
|
|
602
843
|
exayard webhooks list List webhook endpoints
|
|
603
844
|
exayard webhooks create --url <https://...> --events "project.created,bid.generated"
|
package/dist/index.js
CHANGED
|
@@ -258,6 +258,86 @@ var require_client = __commonJS({
|
|
|
258
258
|
export: (id, query) => this.request({ method: "GET", path: `/projects/${id}/export`, query }),
|
|
259
259
|
archive: (id, query) => this.request({ method: "POST", path: `/projects/${id}/archive`, query })
|
|
260
260
|
};
|
|
261
|
+
// File upload is a 3-step dance: presign (get an R2 upload URL) → PUT the
|
|
262
|
+
// bytes straight to R2 → confirm (which also kicks off PDF page extraction).
|
|
263
|
+
// `upload` wraps all three. A PDF becomes pages asynchronously after confirm —
|
|
264
|
+
// poll pages.list() until processingStatus is 'complete' before proposing/running.
|
|
265
|
+
files = {
|
|
266
|
+
presign: (body) => this.request({
|
|
267
|
+
method: "POST",
|
|
268
|
+
path: "/files",
|
|
269
|
+
body
|
|
270
|
+
}),
|
|
271
|
+
confirm: (fileId, r2Key) => this.request({ method: "POST", path: `/files/${fileId}/confirm`, body: { r2Key } }),
|
|
272
|
+
// Convenience: presign → PUT bytes to the presigned R2 URL → confirm.
|
|
273
|
+
// The PUT goes directly to R2 (a different host, self-authenticated by the
|
|
274
|
+
// signed URL) — not through the /v1 request primitive.
|
|
275
|
+
upload: async (body) => {
|
|
276
|
+
const buf = body.bytes instanceof ArrayBuffer ? new Uint8Array(body.bytes) : body.bytes;
|
|
277
|
+
const { fileId, uploadUrl, r2Key } = await this.files.presign({
|
|
278
|
+
organizationId: body.organizationId,
|
|
279
|
+
projectId: body.projectId,
|
|
280
|
+
filename: body.filename,
|
|
281
|
+
mimeType: body.mimeType,
|
|
282
|
+
fileSize: buf.byteLength,
|
|
283
|
+
folderId: body.folderId
|
|
284
|
+
});
|
|
285
|
+
const put = await this.fetchImpl(uploadUrl, {
|
|
286
|
+
method: "PUT",
|
|
287
|
+
headers: { "Content-Type": body.mimeType },
|
|
288
|
+
// Node/undici fetch accepts a Uint8Array body at runtime; the DOM
|
|
289
|
+
// BodyInit type doesn't list it, so cast at this boundary.
|
|
290
|
+
body: buf
|
|
291
|
+
});
|
|
292
|
+
if (!put.ok) {
|
|
293
|
+
throw new error_1.ExayardError({
|
|
294
|
+
type: "https://developers.exayard.com/concepts/errors#transport_error",
|
|
295
|
+
title: "Upload Failed",
|
|
296
|
+
status: put.status,
|
|
297
|
+
detail: `PUT to storage failed: HTTP ${put.status}`,
|
|
298
|
+
code: "transport_error",
|
|
299
|
+
doc_url: "https://developers.exayard.com/concepts/errors#transport_error"
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
await this.files.confirm(fileId, r2Key);
|
|
303
|
+
return { fileId };
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
// List a project's pages (grouped by file). Use to discover pageIds + poll
|
|
307
|
+
// each file's processingStatus until extraction finishes.
|
|
308
|
+
pages = {
|
|
309
|
+
list: (projectId, query) => this.request({ method: "GET", path: `/projects/${projectId}/pages`, query })
|
|
310
|
+
};
|
|
311
|
+
// AI takeoff assessments. Reads resolve the org from the project/assessment
|
|
312
|
+
// (no organizationId). `start` kicks off an AI run on an existing project's
|
|
313
|
+
// pages — pair with the assessment.completed webhook + latest()/takeoffSummary
|
|
314
|
+
// to pull results once it finishes.
|
|
315
|
+
assessments = {
|
|
316
|
+
list: (projectId) => this.request({ method: "GET", path: `/projects/${projectId}/assessments` }),
|
|
317
|
+
latest: (projectId) => this.request({ method: "GET", path: `/projects/${projectId}/assessments/latest` }),
|
|
318
|
+
get: (id) => this.request({ method: "GET", path: `/assessments/${id}` }),
|
|
319
|
+
takeoffSummary: (projectId) => this.request({ method: "GET", path: `/projects/${projectId}/takeoff-summary` }),
|
|
320
|
+
// Ask the AI what to measure: a natural-language prompt + pageIds → a
|
|
321
|
+
// proposed `elements` array (id/name/category/hexColor) you can pass
|
|
322
|
+
// straight to run(). The bridge for callers who don't already know the
|
|
323
|
+
// elements. Returns { elements, creditEstimate, ... }.
|
|
324
|
+
propose: (projectId, body) => this.request({ method: "POST", path: `/projects/${projectId}/analysis/propose`, body }),
|
|
325
|
+
// Run an AI takeoff analysis on an existing project and let it complete
|
|
326
|
+
// end-to-end (no human approval step). You specify exactly what to detect
|
|
327
|
+
// via `elements` (each: id, name, category area|linear|count, hexColor) on
|
|
328
|
+
// the given pageIds. Returns { assessmentId, layerId, pageIds }; track
|
|
329
|
+
// completion via the assessment.completed webhook + latest()/takeoffSummary.
|
|
330
|
+
//
|
|
331
|
+
// (This maps to POST /v1/projects/{id}/analysis/run — the pre-approved path
|
|
332
|
+
// that runs to completion. The auto-detect POST /assessments path pauses at
|
|
333
|
+
// awaiting_approval, which needs a human, so it isn't exposed here.)
|
|
334
|
+
run: (projectId, body, opts = {}) => this.request({
|
|
335
|
+
method: "POST",
|
|
336
|
+
path: `/projects/${projectId}/analysis/run`,
|
|
337
|
+
body,
|
|
338
|
+
idempotencyKey: opts.idempotencyKey
|
|
339
|
+
})
|
|
340
|
+
};
|
|
261
341
|
help = {
|
|
262
342
|
search: (body) => this.request({ method: "POST", path: "/help/search", body, autoIdempotencyKey: false })
|
|
263
343
|
};
|
|
@@ -398,6 +478,146 @@ var projectsListCommand = async (ctx) => {
|
|
|
398
478
|
}
|
|
399
479
|
return 0;
|
|
400
480
|
};
|
|
481
|
+
var assessmentsListCommand = async (ctx) => {
|
|
482
|
+
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
483
|
+
if (!projectId) {
|
|
484
|
+
ctx.writeErr("Usage: exayard assessments list <project_id>");
|
|
485
|
+
return 2;
|
|
486
|
+
}
|
|
487
|
+
const exa = makeClient(ctx);
|
|
488
|
+
const res = await exa.assessments.list(projectId);
|
|
489
|
+
ctx.write(JSON.stringify(res, null, 2));
|
|
490
|
+
return 0;
|
|
491
|
+
};
|
|
492
|
+
var assessmentsRunCommand = async (ctx) => {
|
|
493
|
+
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
494
|
+
if (!projectId) {
|
|
495
|
+
ctx.writeErr(
|
|
496
|
+
`Usage: exayard assessments run <project_id> --org org_... --pages <id1,id2> --elements '[{"id","name","category","hexColor"}]'`
|
|
497
|
+
);
|
|
498
|
+
return 2;
|
|
499
|
+
}
|
|
500
|
+
const organizationId = resolveOrgId(ctx);
|
|
501
|
+
if (!organizationId) {
|
|
502
|
+
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
503
|
+
return 2;
|
|
504
|
+
}
|
|
505
|
+
const pageIds = typeof ctx.flags.pages === "string" ? ctx.flags.pages.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
506
|
+
if (pageIds.length === 0) {
|
|
507
|
+
ctx.writeErr("--pages <pageId1,pageId2> required.");
|
|
508
|
+
return 2;
|
|
509
|
+
}
|
|
510
|
+
if (typeof ctx.flags.elements !== "string") {
|
|
511
|
+
ctx.writeErr(`--elements '[{"id","name","category","hexColor"}]' required (what to detect).`);
|
|
512
|
+
return 2;
|
|
513
|
+
}
|
|
514
|
+
const elements = JSON.parse(ctx.flags.elements);
|
|
515
|
+
const exa = makeClient(ctx);
|
|
516
|
+
const res = await exa.assessments.run(projectId, { organizationId, pageIds, elements });
|
|
517
|
+
ctx.write(JSON.stringify(res, null, 2));
|
|
518
|
+
return 0;
|
|
519
|
+
};
|
|
520
|
+
var filesUploadCommand = async (ctx) => {
|
|
521
|
+
const path = typeof ctx.flags.file === "string" ? ctx.flags.file : ctx.positional[0];
|
|
522
|
+
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : void 0;
|
|
523
|
+
if (!path || !projectId) {
|
|
524
|
+
ctx.writeErr("Usage: exayard files upload <path> --project <projectId> [--org org_...]");
|
|
525
|
+
return 2;
|
|
526
|
+
}
|
|
527
|
+
const organizationId = resolveOrgId(ctx);
|
|
528
|
+
if (!organizationId) {
|
|
529
|
+
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
530
|
+
return 2;
|
|
531
|
+
}
|
|
532
|
+
const { readFileSync: readFileSync2 } = await import("fs");
|
|
533
|
+
const { basename, extname } = await import("path");
|
|
534
|
+
const bytes = readFileSync2(path);
|
|
535
|
+
const ext = extname(path).toLowerCase();
|
|
536
|
+
const mimeType = ext === ".pdf" ? "application/pdf" : ext === ".png" ? "image/png" : ext === ".jpg" || ext === ".jpeg" ? "image/jpeg" : "application/octet-stream";
|
|
537
|
+
const exa = makeClient(ctx);
|
|
538
|
+
const res = await exa.files.upload({
|
|
539
|
+
organizationId,
|
|
540
|
+
projectId,
|
|
541
|
+
filename: basename(path),
|
|
542
|
+
mimeType,
|
|
543
|
+
bytes: new Uint8Array(bytes)
|
|
544
|
+
});
|
|
545
|
+
ctx.write(
|
|
546
|
+
ctx.json ? JSON.stringify(res, null, 2) : `uploaded ${res.fileId} ${basename(path)} \u2014 extraction started; run 'exayard pages list ${projectId}' to watch`
|
|
547
|
+
);
|
|
548
|
+
return 0;
|
|
549
|
+
};
|
|
550
|
+
var pagesListCommand = async (ctx) => {
|
|
551
|
+
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
552
|
+
if (!projectId) {
|
|
553
|
+
ctx.writeErr("Usage: exayard pages list <project_id> [--org org_...]");
|
|
554
|
+
return 2;
|
|
555
|
+
}
|
|
556
|
+
const organizationId = resolveOrgId(ctx);
|
|
557
|
+
if (!organizationId) {
|
|
558
|
+
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
559
|
+
return 2;
|
|
560
|
+
}
|
|
561
|
+
const exa = makeClient(ctx);
|
|
562
|
+
const res = await exa.pages.list(projectId, { organizationId });
|
|
563
|
+
ctx.write(JSON.stringify(res, null, 2));
|
|
564
|
+
return 0;
|
|
565
|
+
};
|
|
566
|
+
var assessmentsProposeCommand = async (ctx) => {
|
|
567
|
+
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
568
|
+
if (!projectId) {
|
|
569
|
+
ctx.writeErr('Usage: exayard assessments propose <project_id> --prompt "..." [--pages id1,id2] [--org org_...]');
|
|
570
|
+
return 2;
|
|
571
|
+
}
|
|
572
|
+
const organizationId = resolveOrgId(ctx);
|
|
573
|
+
if (!organizationId) {
|
|
574
|
+
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
575
|
+
return 2;
|
|
576
|
+
}
|
|
577
|
+
const prompt = typeof ctx.flags.prompt === "string" ? ctx.flags.prompt : ctx.positional[1];
|
|
578
|
+
if (!prompt) {
|
|
579
|
+
ctx.writeErr('--prompt "<what to measure>" required.');
|
|
580
|
+
return 2;
|
|
581
|
+
}
|
|
582
|
+
const pageIds = typeof ctx.flags.pages === "string" ? ctx.flags.pages.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
583
|
+
const exa = makeClient(ctx);
|
|
584
|
+
const res = await exa.assessments.propose(projectId, { organizationId, prompt, pageIds });
|
|
585
|
+
ctx.write(JSON.stringify(res, null, 2));
|
|
586
|
+
return 0;
|
|
587
|
+
};
|
|
588
|
+
var assessmentsLatestCommand = async (ctx) => {
|
|
589
|
+
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
590
|
+
if (!projectId) {
|
|
591
|
+
ctx.writeErr("Usage: exayard assessments latest <project_id>");
|
|
592
|
+
return 2;
|
|
593
|
+
}
|
|
594
|
+
const exa = makeClient(ctx);
|
|
595
|
+
const res = await exa.assessments.latest(projectId);
|
|
596
|
+
ctx.write(JSON.stringify(res, null, 2));
|
|
597
|
+
return 0;
|
|
598
|
+
};
|
|
599
|
+
var assessmentsGetCommand = async (ctx) => {
|
|
600
|
+
const id = typeof ctx.flags.id === "string" ? ctx.flags.id : ctx.positional[0];
|
|
601
|
+
if (!id) {
|
|
602
|
+
ctx.writeErr("Usage: exayard assessments get <assessment_id>");
|
|
603
|
+
return 2;
|
|
604
|
+
}
|
|
605
|
+
const exa = makeClient(ctx);
|
|
606
|
+
const res = await exa.assessments.get(id);
|
|
607
|
+
ctx.write(JSON.stringify(res, null, 2));
|
|
608
|
+
return 0;
|
|
609
|
+
};
|
|
610
|
+
var takeoffSummaryCommand = async (ctx) => {
|
|
611
|
+
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
612
|
+
if (!projectId) {
|
|
613
|
+
ctx.writeErr("Usage: exayard takeoff summary <project_id>");
|
|
614
|
+
return 2;
|
|
615
|
+
}
|
|
616
|
+
const exa = makeClient(ctx);
|
|
617
|
+
const res = await exa.assessments.takeoffSummary(projectId);
|
|
618
|
+
ctx.write(JSON.stringify(res, null, 2));
|
|
619
|
+
return 0;
|
|
620
|
+
};
|
|
401
621
|
var webhooksListenCommand = async (ctx) => {
|
|
402
622
|
if (process.env.EXAYARD_EXPERIMENTAL !== "1") {
|
|
403
623
|
ctx.writeErr(
|
|
@@ -595,6 +815,14 @@ var COMMANDS = {
|
|
|
595
815
|
"projects:list": projectsListCommand,
|
|
596
816
|
"projects:create": projectsCreateCommand,
|
|
597
817
|
"projects:archive": projectsArchiveCommand,
|
|
818
|
+
"files:upload": filesUploadCommand,
|
|
819
|
+
"pages:list": pagesListCommand,
|
|
820
|
+
"assessments:list": assessmentsListCommand,
|
|
821
|
+
"assessments:latest": assessmentsLatestCommand,
|
|
822
|
+
"assessments:get": assessmentsGetCommand,
|
|
823
|
+
"assessments:propose": assessmentsProposeCommand,
|
|
824
|
+
"assessments:run": assessmentsRunCommand,
|
|
825
|
+
"takeoff:summary": takeoffSummaryCommand,
|
|
598
826
|
"webhooks:list": webhooksListCommand,
|
|
599
827
|
"webhooks:create": webhooksCreateCommand,
|
|
600
828
|
"webhooks:delete": webhooksDeleteCommand,
|