@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.
Files changed (3) hide show
  1. package/dist/bin.js +241 -0
  2. package/dist/index.js +228 -0
  3. 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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exayard/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Official CLI for the Exayard API",
5
5
  "bin": {
6
6
  "exayard": "./dist/bin.js"