@siteoshq/cli 1.5.0 → 1.6.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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # SiteOS CLI
2
2
 
3
3
  `@siteoshq/cli` exposes one `siteos` binary for Auth, common Projects, Pulse, Cookie, Forms,
4
- Search, Trace, SEO and Integrations. This source prepares version 1.3.0; publishing is a separate release.
4
+ Search, Trace, SEO and Integrations. This source prepares version 1.6.0; publishing is a separate release.
5
5
  Node.js 22 or newer is required.
6
6
 
7
7
  ## Install and authenticate
package/dist/cli.js CHANGED
@@ -1499,7 +1499,7 @@ var OverviewSchema = z4.object({
1499
1499
  });
1500
1500
  function createProjectApi(input) {
1501
1501
  const origin = resolveSiteOSAuthBaseUrl(input.env);
1502
- async function request(path30, schema, body, method) {
1502
+ async function request(path31, schema, body, method) {
1503
1503
  const scope = body === void 0 ? "projects:workspace:read" : "projects:workspace:write";
1504
1504
  const grant = await input.grants.acquire({
1505
1505
  audience: "siteos-projects",
@@ -1513,7 +1513,7 @@ function createProjectApi(input) {
1513
1513
  message: "SiteOS API access is unavailable."
1514
1514
  });
1515
1515
  const response = await input.fetchImpl(
1516
- `${origin}/api/projects/v1/projects${path30}`,
1516
+ `${origin}/api/projects/v1/projects${path31}`,
1517
1517
  {
1518
1518
  method: method ?? (body === void 0 ? "GET" : "POST"),
1519
1519
  headers: {
@@ -9837,10 +9837,261 @@ async function runServiceCommand(service, options) {
9837
9837
  }
9838
9838
 
9839
9839
  // src/services/seo-command.ts
9840
+ import { writeFile as writeFile9 } from "fs/promises";
9841
+ import path30 from "path";
9842
+ import { parseArgs as parseArgs5 } from "util";
9843
+ import { z as z17 } from "zod";
9844
+
9845
+ // src/services/seo-performance-command.ts
9846
+ import { randomUUID as randomUUID6 } from "crypto";
9840
9847
  import { writeFile as writeFile8 } from "fs/promises";
9841
9848
  import path29 from "path";
9849
+ import { setTimeout as setTimeout2 } from "timers/promises";
9842
9850
  import { parseArgs as parseArgs4 } from "util";
9843
9851
  import { z as z16 } from "zod";
9852
+ var PERFORMANCE_HELP = `
9853
+ siteos seo performance run --audit <id> --url <url> [--url <url>...] [--device <mobile|desktop>] [--idempotency-key <key>] [--environment <slug>] [--json]
9854
+ siteos seo performance list [--device <mobile|desktop>] [--environment <slug>] [--json]
9855
+ siteos seo performance show <id> [--url <url>] [--environment <slug>] [--json]
9856
+ siteos seo performance wait <id> [--timeout <seconds>] [--environment <slug>] [--json]
9857
+ siteos seo performance cancel <id> [--environment <slug>] [--json]
9858
+ siteos seo performance history --url <url> [--device <mobile|desktop>] [--environment <slug>] [--json]
9859
+ siteos seo performance export <id> --format <csv|json> --output <new-file> [--environment <slug>] [--json]
9860
+
9861
+ Performance checks use up to 10 explicitly selected URLs from a saved full HTML audit.
9862
+ Mobile is the default for a new run. Scores are Lighthouse lab data, not real-user Core Web Vitals.
9863
+ Reuse the returned idempotency key after an uncertain run response. Wait defaults to 120 seconds (maximum 900).
9864
+ `;
9865
+ async function runSeoPerformanceCommand(options) {
9866
+ let idempotencyKey;
9867
+ try {
9868
+ const { values, positionals } = parseArgs4({
9869
+ args: options.args.slice(1),
9870
+ strict: true,
9871
+ allowPositionals: true,
9872
+ options: {
9873
+ json: { type: "boolean" },
9874
+ environment: { type: "string" },
9875
+ audit: { type: "string" },
9876
+ url: { type: "string", multiple: true },
9877
+ device: { type: "string" },
9878
+ "idempotency-key": { type: "string" },
9879
+ timeout: { type: "string" },
9880
+ format: { type: "string" },
9881
+ output: { type: "string" }
9882
+ }
9883
+ });
9884
+ const action = positionals[0] ?? "";
9885
+ const id = positionals[1];
9886
+ const operations = {
9887
+ run: { args: 1, flags: ["audit", "url", "device", "idempotency-key"] },
9888
+ list: { args: 1, flags: ["device"] },
9889
+ show: { args: 2, flags: ["url"] },
9890
+ wait: { args: 2, flags: ["timeout"] },
9891
+ cancel: { args: 2, flags: [] },
9892
+ history: { args: 1, flags: ["url", "device"] },
9893
+ export: { args: 2, flags: ["format", "output"] }
9894
+ };
9895
+ const operation = operations[action];
9896
+ if (!operation || positionals.length !== operation.args || Object.keys(values).some(
9897
+ (key) => !["environment", "json", ...operation.flags].includes(key)
9898
+ ))
9899
+ throw new Error(
9900
+ "Invalid performance operation or flags. Run siteos seo --help."
9901
+ );
9902
+ if (values.device && !["mobile", "desktop"].includes(values.device))
9903
+ throw new Error("Use --device mobile or desktop.");
9904
+ if (action === "run" && (!values.audit || !values.url?.length || values.url.length > 10))
9905
+ throw new Error(
9906
+ "Choose a source --audit and between 1 and 10 --url values."
9907
+ );
9908
+ if (action === "history" && values.url?.length !== 1 || action === "show" && values.url && values.url.length !== 1)
9909
+ throw new Error("Use one --url for page evidence or history.");
9910
+ for (const value of values.url ?? []) {
9911
+ const url = new URL(value);
9912
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.port || value.length > 2048)
9913
+ throw new Error("Use eligible public HTTP(S) URLs.");
9914
+ }
9915
+ const timeout = Number(values.timeout ?? 120);
9916
+ if (!Number.isInteger(timeout) || timeout < 1 || timeout > 900)
9917
+ throw new Error("Use a wait timeout between 1 and 900 seconds.");
9918
+ if (action === "export" && (!values.output || !["csv", "json"].includes(values.format ?? "")))
9919
+ throw new Error(
9920
+ "Export requires --format csv|json and --output for a new file."
9921
+ );
9922
+ if (action === "run") {
9923
+ idempotencyKey = values["idempotency-key"] ?? randomUUID6();
9924
+ if (!/^[a-zA-Z0-9_-]{16,100}$/u.test(idempotencyKey))
9925
+ throw new Error(
9926
+ "Use an idempotency key of 16 to 100 letters, digits, underscores or hyphens."
9927
+ );
9928
+ }
9929
+ const context = await commonServiceContext(
9930
+ options,
9931
+ "seo",
9932
+ values.environment
9933
+ );
9934
+ if (!context)
9935
+ throw new Error("Select a SiteOS Project with siteos project use first.");
9936
+ const runtime = commonProjectRuntime(options);
9937
+ const writing = ["run", "cancel"].includes(action);
9938
+ const scope = writing ? "seo:audits:write" : "seo:workspace:read";
9939
+ const batchSchema = z16.object({
9940
+ id: z16.string(),
9941
+ resourceId: z16.literal(context.resourceId),
9942
+ organizationId: z16.literal(context.overview.project.organizationId),
9943
+ sourceAuditId: z16.string(),
9944
+ state: z16.enum([
9945
+ "queued",
9946
+ "running",
9947
+ "completed",
9948
+ "partial",
9949
+ "failed",
9950
+ "cancelled"
9951
+ ]),
9952
+ device: z16.enum(["mobile", "desktop"]),
9953
+ urls: z16.array(z16.string()).min(1).max(10)
9954
+ }).passthrough();
9955
+ const query = new URLSearchParams();
9956
+ if (values.device) query.set("device", values.device);
9957
+ if (!writing && values.url?.[0]) query.set("pageUrl", values.url[0]);
9958
+ const suffix = id ? `/${encodeURIComponent(id)}${action === "cancel" ? "/cancel" : action === "export" ? "/export" : ""}` : "";
9959
+ if (action === "export") query.set("format", values.format);
9960
+ const deadline = Date.now() + timeout * 1e3;
9961
+ while (true) {
9962
+ const grant = await runtime.grants.acquire({
9963
+ audience: "siteos-seo",
9964
+ scopes: [scope]
9965
+ });
9966
+ if (grant.grant.audience !== "siteos-seo" || grant.grant.organizationId !== context.overview.project.organizationId || grant.grant.scopes.length !== 1 || grant.grant.scopes[0] !== scope)
9967
+ throw new Error(
9968
+ "The SEO grant does not match this Project and operation."
9969
+ );
9970
+ if (!options.fetchImpl)
9971
+ throw new Error("SiteOS API access is unavailable.");
9972
+ const response = await options.fetchImpl(
9973
+ `${runtime.api.origin}/api/seo/v1/resources/${encodeURIComponent(context.resourceId)}/browser-checks${suffix}?${query}`,
9974
+ {
9975
+ method: writing ? "POST" : "GET",
9976
+ headers: {
9977
+ Accept: "application/json",
9978
+ Authorization: `Bearer ${grant.accessToken}`,
9979
+ ...action === "run" ? { "Content-Type": "application/json" } : {}
9980
+ },
9981
+ ...action === "run" ? {
9982
+ body: JSON.stringify({
9983
+ auditId: values.audit,
9984
+ urls: values.url,
9985
+ device: values.device ?? "mobile",
9986
+ idempotencyKey
9987
+ })
9988
+ } : {},
9989
+ signal: AbortSignal.timeout(3e4)
9990
+ }
9991
+ );
9992
+ if (action === "export" && response.ok) {
9993
+ if (!(response instanceof Response) || response.headers.get("X-SEO-Browser-Id") !== id || !response.headers.get("Content-Type")?.startsWith(
9994
+ values.format === "csv" ? "text/csv" : "application/json"
9995
+ ))
9996
+ throw new Error(
9997
+ "The export response does not match the selected check."
9998
+ );
9999
+ const text = await response.text();
10000
+ if (values.format === "json") {
10001
+ const parsed = z16.object({
10002
+ contractVersion: z16.literal(1),
10003
+ batch: batchSchema,
10004
+ pages: z16.array(z16.unknown())
10005
+ }).parse(JSON.parse(text));
10006
+ if (parsed.batch.id !== id)
10007
+ throw new Error(
10008
+ "The export response does not match the selected check."
10009
+ );
10010
+ }
10011
+ const output = path29.resolve(
10012
+ options.cwd ?? process.cwd(),
10013
+ values.output
10014
+ );
10015
+ await writeFile8(output, text, { flag: "wx", mode: 384 });
10016
+ return {
10017
+ exitCode: 0,
10018
+ stdout: JSON.stringify(
10019
+ { batchId: id, output, format: values.format },
10020
+ null,
10021
+ 2
10022
+ )
10023
+ };
10024
+ }
10025
+ const data = await response.json();
10026
+ if (!response.ok) {
10027
+ const error = z16.object({
10028
+ error: z16.object({ code: z16.string(), message: z16.string().max(500) })
10029
+ }).safeParse(data);
10030
+ throw new SiteOSAuthApiError({
10031
+ code: error.success ? error.data.error.code : "SEO_REQUEST_FAILED",
10032
+ message: error.success ? error.data.error.message : "The performance request failed.",
10033
+ status: response.status
10034
+ });
10035
+ }
10036
+ const record = z16.object({ contractVersion: z16.literal(1) }).passthrough().parse(data);
10037
+ if (action === "cancel") z16.literal(true).parse(record.cancelled);
10038
+ else if (action === "run") {
10039
+ const accepted = batchSchema.parse(record.batch);
10040
+ const normalize = (url) => {
10041
+ const value = new URL(url);
10042
+ value.hash = "";
10043
+ return value.href;
10044
+ };
10045
+ if (accepted.sourceAuditId !== values.audit || accepted.device !== (values.device ?? "mobile") || JSON.stringify([...new Set(accepted.urls.map(normalize))].sort()) !== JSON.stringify([...new Set(values.url.map(normalize))].sort()))
10046
+ throw new Error(
10047
+ "The queued check does not match the requested source, device and URLs."
10048
+ );
10049
+ } else {
10050
+ z16.literal(context.resourceId).parse(record.resourceId);
10051
+ z16.array(batchSchema).parse(record.batches);
10052
+ const selected = batchSchema.nullable().parse(record.batch);
10053
+ if (id && selected?.id !== id)
10054
+ throw new Error(
10055
+ "The response does not match the selected performance check."
10056
+ );
10057
+ }
10058
+ const batch = record.batch ? batchSchema.parse(record.batch) : null;
10059
+ if (action !== "wait" || !batch || !["queued", "running"].includes(batch.state))
10060
+ return {
10061
+ exitCode: 0,
10062
+ stdout: JSON.stringify(
10063
+ { ...record, ...idempotencyKey ? { idempotencyKey } : {} },
10064
+ null,
10065
+ 2
10066
+ )
10067
+ };
10068
+ if (Date.now() >= deadline)
10069
+ return {
10070
+ exitCode: 3,
10071
+ stdout: JSON.stringify({ ...record, timedOut: true }, null, 2)
10072
+ };
10073
+ await setTimeout2(Math.min(3e3, Math.max(0, deadline - Date.now())));
10074
+ }
10075
+ } catch (cause) {
10076
+ const error = {
10077
+ code: cause instanceof SiteOSAuthApiError ? cause.code : "SEO_COMMAND_FAILED",
10078
+ message: cause instanceof z16.ZodError ? "The SEO service returned an invalid response." : cause instanceof Error ? cause.message : "The performance command failed."
10079
+ };
10080
+ return {
10081
+ exitCode: cause instanceof SiteOSAuthApiError ? 1 : 2,
10082
+ ...options.args.includes("--json") ? {
10083
+ stdout: JSON.stringify({
10084
+ error,
10085
+ ...idempotencyKey ? { idempotencyKey } : {}
10086
+ })
10087
+ } : {
10088
+ stderr: `${error.message}${idempotencyKey ? ` Retry key: ${idempotencyKey}` : ""}`
10089
+ }
10090
+ };
10091
+ }
10092
+ }
10093
+
10094
+ // src/services/seo-command.ts
9844
10095
  var SEO_HELP = `Audit public HTML in the selected Project environment.
9845
10096
 
9846
10097
  Usage:
@@ -9861,19 +10112,23 @@ Usage:
9861
10112
  siteos seo notifications destinations [--environment <slug>] [--json]
9862
10113
  siteos seo notifications set --enabled <true|false> [--destination <candidate-id>] --severity <error|warning> --failures <true|false> --revision <number> [--environment <slug>] [--json]
9863
10114
  siteos seo export --audit <id> --kind <pages|issues|changes> --format <csv|json> --output <new-file> [--query <text>] [--rule <id>] [--severity <error|warning|notice>] [--state <page-or-change-state>] [--environment <slug>] [--json]
10115
+ ${PERFORMANCE_HELP.trim().split("\n\n")[0]}
9864
10116
 
9865
10117
  Schedule and notification writes require an owner/admin and the saved revision (initially 0).
9866
10118
  Export writes all matching rows to a new file; existing files are never overwritten.
9867
10119
  Runs are queued. Read audit show until terminal; an accepted run is not a completed check.
9868
10120
  Recheck accepts a URL observed in the source audit. Cross-page rules require a full audit.
9869
10121
  Read the current disposition revision before ignore/restore; use 0 if no decision exists.
9870
- Setup: siteos project connect seo. No crawl runs during setup.`;
10122
+ Setup: siteos project connect seo. No crawl runs during setup.
10123
+ ${PERFORMANCE_HELP.trim().split("\n\n").slice(1).join("\n\n")}`;
9871
10124
  async function runSeoCommand(options) {
9872
10125
  if (!options.args.length || options.args.some((arg) => ["--help", "-h"].includes(arg)))
9873
10126
  return { exitCode: 0, stdout: SEO_HELP };
10127
+ if (options.args[0] === "performance")
10128
+ return runSeoPerformanceCommand(options);
9874
10129
  const json = options.args.includes("--json");
9875
10130
  try {
9876
- const { positionals, values } = parseArgs4({
10131
+ const { positionals, values } = parseArgs5({
9877
10132
  args: options.args,
9878
10133
  strict: true,
9879
10134
  allowPositionals: true,
@@ -10086,18 +10341,18 @@ async function runSeoCommand(options) {
10086
10341
  );
10087
10342
  const text = await response.text();
10088
10343
  if (values.format === "json")
10089
- z16.object({
10090
- contractVersion: z16.literal(1),
10091
- audit: z16.object({
10092
- id: z16.literal(values.audit),
10093
- resourceId: z16.literal(context.resourceId)
10344
+ z17.object({
10345
+ contractVersion: z17.literal(1),
10346
+ audit: z17.object({
10347
+ id: z17.literal(values.audit),
10348
+ resourceId: z17.literal(context.resourceId)
10094
10349
  }),
10095
- kind: z16.literal(values.kind),
10096
- totalRows: z16.literal(rows),
10097
- rows: z16.array(z16.unknown()).length(rows)
10350
+ kind: z17.literal(values.kind),
10351
+ totalRows: z17.literal(rows),
10352
+ rows: z17.array(z17.unknown()).length(rows)
10098
10353
  }).parse(JSON.parse(text));
10099
- const output = path29.resolve(options.cwd ?? process.cwd(), values.output);
10100
- await writeFile8(output, text, { flag: "wx", mode: 384 });
10354
+ const output = path30.resolve(options.cwd ?? process.cwd(), values.output);
10355
+ await writeFile9(output, text, { flag: "wx", mode: 384 });
10101
10356
  return {
10102
10357
  exitCode: 0,
10103
10358
  stdout: JSON.stringify(
@@ -10115,8 +10370,8 @@ async function runSeoCommand(options) {
10115
10370
  }
10116
10371
  const data = await response.json();
10117
10372
  if (!response.ok) {
10118
- const result = z16.object({
10119
- error: z16.object({ code: z16.string(), message: z16.string().max(500) })
10373
+ const result = z17.object({
10374
+ error: z17.object({ code: z17.string(), message: z17.string().max(500) })
10120
10375
  }).safeParse(data);
10121
10376
  throw new SiteOSAuthApiError({
10122
10377
  code: result.success ? result.data.error.code : "SEO_REQUEST_FAILED",
@@ -10124,34 +10379,34 @@ async function runSeoCommand(options) {
10124
10379
  status: response.status
10125
10380
  });
10126
10381
  }
10127
- const record = z16.object({ contractVersion: z16.literal(1) }).passthrough().parse(data);
10382
+ const record = z17.object({ contractVersion: z17.literal(1) }).passthrough().parse(data);
10128
10383
  if (automation) {
10129
- z16.literal(context.resourceId).parse(record.resourceId);
10130
- const schedule = z16.object({
10131
- enabled: z16.boolean(),
10132
- weekday: z16.number().int().min(1).max(7),
10133
- time: z16.string(),
10134
- timeZone: z16.string(),
10135
- revision: z16.number().int().min(0),
10136
- nextRunAt: z16.string().nullable()
10384
+ z17.literal(context.resourceId).parse(record.resourceId);
10385
+ const schedule = z17.object({
10386
+ enabled: z17.boolean(),
10387
+ weekday: z17.number().int().min(1).max(7),
10388
+ time: z17.string(),
10389
+ timeZone: z17.string(),
10390
+ revision: z17.number().int().min(0),
10391
+ nextRunAt: z17.string().nullable()
10137
10392
  });
10138
- const notificationRoute = z16.object({
10139
- enabled: z16.boolean(),
10140
- minimumSeverity: z16.enum(["error", "warning"]),
10141
- includeFailures: z16.boolean(),
10142
- revision: z16.number().int().min(0),
10143
- destinationId: z16.string().nullable()
10393
+ const notificationRoute = z17.object({
10394
+ enabled: z17.boolean(),
10395
+ minimumSeverity: z17.enum(["error", "warning"]),
10396
+ includeFailures: z17.boolean(),
10397
+ revision: z17.number().int().min(0),
10398
+ destinationId: z17.string().nullable()
10144
10399
  });
10145
10400
  if (retryNotification) {
10146
- z16.literal(true).parse(record.retryQueued);
10147
- z16.literal(positionals[2]).parse(record.notificationId);
10401
+ z17.literal(true).parse(record.retryQueued);
10402
+ z17.literal(positionals[2]).parse(record.notificationId);
10148
10403
  } else if (route === "notifications destinations")
10149
- z16.object({
10150
- candidates: z16.array(
10151
- z16.object({
10152
- candidateId: z16.string(),
10153
- label: z16.string(),
10154
- availability: z16.literal("available")
10404
+ z17.object({
10405
+ candidates: z17.array(
10406
+ z17.object({
10407
+ candidateId: z17.string(),
10408
+ label: z17.string(),
10409
+ availability: z17.literal("available")
10155
10410
  })
10156
10411
  )
10157
10412
  }).parse(record);
@@ -10164,46 +10419,46 @@ async function runSeoCommand(options) {
10164
10419
  notificationRoute.parse(record.route);
10165
10420
  }
10166
10421
  } else if (!writing) {
10167
- const validated = z16.object({
10168
- resource: z16.object({
10169
- id: z16.literal(context.resourceId),
10170
- organizationId: z16.literal(context.overview.project.organizationId)
10422
+ const validated = z17.object({
10423
+ resource: z17.object({
10424
+ id: z17.literal(context.resourceId),
10425
+ organizationId: z17.literal(context.overview.project.organizationId)
10171
10426
  }),
10172
- audits: z16.array(z16.object({ id: z16.string() }).passthrough()),
10173
- audit: z16.object({
10174
- id: z16.string(),
10175
- resourceId: z16.literal(context.resourceId)
10427
+ audits: z17.array(z17.object({ id: z17.string() }).passthrough()),
10428
+ audit: z17.object({
10429
+ id: z17.string(),
10430
+ resourceId: z17.literal(context.resourceId)
10176
10431
  }).passthrough().nullable(),
10177
- pages: z16.array(z16.unknown()),
10178
- issues: z16.array(z16.unknown()),
10179
- changes: z16.array(z16.unknown()),
10180
- totalChanges: z16.number(),
10181
- dispositions: z16.array(z16.unknown())
10432
+ pages: z17.array(z17.unknown()),
10433
+ issues: z17.array(z17.unknown()),
10434
+ changes: z17.array(z17.unknown()),
10435
+ totalChanges: z17.number(),
10436
+ dispositions: z17.array(z17.unknown())
10182
10437
  }).passthrough().parse(record);
10183
10438
  const selected = query.get("audit");
10184
10439
  if (selected && validated.audit?.id !== selected)
10185
10440
  throw new Error("The SEO response does not match the requested audit.");
10186
10441
  } else if (record.audit)
10187
- z16.object({
10188
- id: z16.string(),
10189
- resourceId: z16.literal(context.resourceId),
10190
- organizationId: z16.literal(context.overview.project.organizationId),
10191
- state: z16.literal("queued")
10442
+ z17.object({
10443
+ id: z17.string(),
10444
+ resourceId: z17.literal(context.resourceId),
10445
+ organizationId: z17.literal(context.overview.project.organizationId),
10446
+ state: z17.literal("queued")
10192
10447
  }).parse(record.audit);
10193
- else if (route === "audit cancel") z16.literal(true).parse(record.cancelled);
10448
+ else if (route === "audit cancel") z17.literal(true).parse(record.cancelled);
10194
10449
  else if (action === "issue")
10195
- z16.object({
10196
- url: z16.literal(values.url),
10197
- ruleId: z16.literal(values.rule),
10198
- ignored: z16.literal(positionals[1] === "ignore"),
10199
- revision: z16.literal(Number(values.revision) + 1)
10450
+ z17.object({
10451
+ url: z17.literal(values.url),
10452
+ ruleId: z17.literal(values.rule),
10453
+ ignored: z17.literal(positionals[1] === "ignore"),
10454
+ revision: z17.literal(Number(values.revision) + 1)
10200
10455
  }).parse(record.disposition);
10201
10456
  else throw new Error("The SEO service returned an invalid response.");
10202
10457
  return { exitCode: 0, stdout: JSON.stringify(record, null, 2) };
10203
10458
  } catch (cause) {
10204
10459
  const error = {
10205
10460
  code: cause instanceof SiteOSAuthApiError ? cause.code : "SEO_COMMAND_FAILED",
10206
- message: cause instanceof z16.ZodError ? "The SEO service returned an invalid response." : cause instanceof Error ? cause.message : "The SEO command failed."
10461
+ message: cause instanceof z17.ZodError ? "The SEO service returned an invalid response." : cause instanceof Error ? cause.message : "The SEO command failed."
10207
10462
  };
10208
10463
  return {
10209
10464
  exitCode: cause instanceof SiteOSAuthApiError ? 1 : 2,