@siteoshq/cli 2.5.1 → 2.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/dist/cli.js CHANGED
@@ -1506,7 +1506,7 @@ var OverviewSchema = z4.object({
1506
1506
  });
1507
1507
  function createProjectApi(input) {
1508
1508
  const origin = resolveSiteOSAuthBaseUrl(input.env);
1509
- async function request(path29, schema, body, method) {
1509
+ async function request(path30, schema, body, method) {
1510
1510
  const scope = body === void 0 ? "projects:workspace:read" : "projects:workspace:write";
1511
1511
  const grant = await input.grants.acquire({
1512
1512
  audience: "siteos-projects",
@@ -1520,7 +1520,7 @@ function createProjectApi(input) {
1520
1520
  message: "SiteOS API access is unavailable."
1521
1521
  });
1522
1522
  const response = await input.fetchImpl(
1523
- `${origin}/api/projects/v1/projects${path29}`,
1523
+ `${origin}/api/projects/v1/projects${path30}`,
1524
1524
  {
1525
1525
  method: method ?? (body === void 0 ? "GET" : "POST"),
1526
1526
  headers: {
@@ -8418,11 +8418,301 @@ async function runServiceCommand(service, options) {
8418
8418
  }
8419
8419
  }
8420
8420
 
8421
- // src/services/analytics-command.ts
8421
+ // src/services/analytics-google-csv.ts
8422
+ import { z as z14 } from "zod";
8423
+ var table = z14.object({
8424
+ dimensions: z14.array(z14.string()),
8425
+ metrics: z14.array(z14.object({ name: z14.string() })),
8426
+ rows: z14.array(
8427
+ z14.object({ dimensions: z14.array(z14.string()), metrics: z14.array(z14.string()) })
8428
+ )
8429
+ });
8430
+ function googleReportCsv(value, fetchedAt) {
8431
+ const report = z14.object({
8432
+ request: z14.object({
8433
+ kind: z14.string(),
8434
+ propertyId: z14.string(),
8435
+ streamId: z14.string(),
8436
+ startDate: z14.string(),
8437
+ endDate: z14.string(),
8438
+ filters: z14.array(z14.unknown()),
8439
+ minuteRange: z14.unknown().optional(),
8440
+ comparison: z14.object({ startDate: z14.string(), endDate: z14.string() }).optional()
8441
+ }).passthrough(),
8442
+ timeZone: z14.string(),
8443
+ limitations: z14.array(z14.string()),
8444
+ quality: z14.unknown(),
8445
+ table,
8446
+ previous: table.optional()
8447
+ }).parse(value);
8448
+ const escape = (v) => `"${(/^[\s]*[=+\-@\t\r]/u.test(v) ? "'" : "") + v.replaceAll('"', '""')}"`;
8449
+ const rows = [
8450
+ ["Report", report.request.kind],
8451
+ ["Definition", JSON.stringify(report.request)],
8452
+ ["Property", report.request.propertyId],
8453
+ ["Stream", report.request.streamId],
8454
+ ["From", report.request.startDate],
8455
+ ["To", report.request.endDate],
8456
+ ["Timezone", report.timeZone],
8457
+ ["Fetched at", fetchedAt ?? ""],
8458
+ ["Filters", JSON.stringify(report.request.filters)],
8459
+ ["Minute boundaries", JSON.stringify(report.request.minuteRange ?? null)],
8460
+ ["Limitations", report.limitations.join(", ")],
8461
+ ["Quality", JSON.stringify(report.quality)],
8462
+ []
8463
+ ];
8464
+ const add = (t, label) => {
8465
+ rows.push(
8466
+ [label],
8467
+ [...t.dimensions, ...t.metrics.map((m) => m.name)],
8468
+ ...t.rows.map((r) => [...r.dimensions, ...r.metrics])
8469
+ );
8470
+ };
8471
+ add(report.table, "Selected period");
8472
+ if (report.previous) {
8473
+ rows.push(
8474
+ ["Comparison from", report.request.comparison?.startDate ?? ""],
8475
+ ["Comparison to", report.request.comparison?.endDate ?? ""]
8476
+ );
8477
+ add(report.previous, "Comparison period");
8478
+ }
8479
+ return rows.map((r) => r.map(escape).join(",")).join("\r\n");
8480
+ }
8481
+
8482
+ // src/services/analytics-google-command.ts
8422
8483
  import { readFile as readFile16, stat as stat2 } from "fs/promises";
8423
8484
  import path25 from "path";
8424
8485
  import { parseArgs as parseArgs4 } from "util";
8425
- import { z as z14 } from "zod";
8486
+ import { z as z15 } from "zod";
8487
+ var GOOGLE_ANALYTICS_HELP = `Read GA4 reports in the selected Project environment.
8488
+
8489
+ Usage:
8490
+ siteos analytics ga4 report [--period <today|yesterday|24h|7d|28d|month|last-month|custom>] [--from <date>] [--to <date>] [--compare <previous|year|none>] [--filters <json>] [--section <totals|trend|events|channels|sources|pages|countries|devices|quality>] [--offset <number>] [--limit <1..100>] [--refresh] [--environment <slug>] [--json]
8491
+ siteos analytics ga4 realtime [--filters <json>] [--environment <slug>] [--json]
8492
+ siteos analytics ga4 explore --file <selection.json> [--refresh] [--environment <slug>] [--json]
8493
+ siteos analytics ga4 export --file <selection.json> [--environment <slug>]
8494
+ siteos analytics ga4 saved list [--environment <slug>] [--json]
8495
+ siteos analytics ga4 saved save --file <saved-report.json> [--environment <slug>] [--json]
8496
+ siteos analytics ga4 saved remove <id> --revision <number> [--environment <slug>] [--json]
8497
+
8498
+ A missing or stale report is queued in the shared cache; rerun to read completion.
8499
+ Exploration selections support campaigns, registered event dimensions, funnels and cohorts.
8500
+ Export writes CSV to stdout from a completed cached snapshot. It does not wait for Google.
8501
+ Saved report writes require an owner/admin and analytics:reports:write.
8502
+ No command connects Google, changes tracking or enables notifications.`;
8503
+ async function runAnalyticsGoogleCommand(options) {
8504
+ const json = options.args.includes("--json");
8505
+ if (options.args.length <= 1 || options.args.includes("--help"))
8506
+ return { exitCode: 0, stdout: GOOGLE_ANALYTICS_HELP };
8507
+ try {
8508
+ const { values, positionals } = parseArgs4({
8509
+ args: options.args.slice(1),
8510
+ allowPositionals: true,
8511
+ strict: true,
8512
+ options: {
8513
+ json: { type: "boolean" },
8514
+ environment: { type: "string" },
8515
+ period: { type: "string" },
8516
+ from: { type: "string" },
8517
+ to: { type: "string" },
8518
+ compare: { type: "string" },
8519
+ filters: { type: "string" },
8520
+ section: { type: "string" },
8521
+ offset: { type: "string" },
8522
+ limit: { type: "string" },
8523
+ file: { type: "string" },
8524
+ revision: { type: "string" },
8525
+ refresh: { type: "boolean" }
8526
+ }
8527
+ });
8528
+ const action = positionals[0];
8529
+ const sub = positionals[1];
8530
+ const operations = {
8531
+ report: {
8532
+ args: 1,
8533
+ flags: [
8534
+ "period",
8535
+ "from",
8536
+ "to",
8537
+ "compare",
8538
+ "filters",
8539
+ "refresh",
8540
+ "section",
8541
+ "offset",
8542
+ "limit"
8543
+ ]
8544
+ },
8545
+ realtime: { args: 1, flags: ["filters"] },
8546
+ explore: { args: 1, flags: ["file", "refresh"] },
8547
+ export: { args: 1, flags: ["file"] },
8548
+ "saved list": { args: 2, flags: [] },
8549
+ "saved save": { args: 2, flags: ["file"], write: true },
8550
+ "saved remove": { args: 3, flags: ["revision"], write: true }
8551
+ };
8552
+ const operation = operations[action === "saved" ? `${action} ${sub}` : action ?? ""];
8553
+ if (!operation || positionals.length !== operation.args || Object.keys(values).some(
8554
+ (k) => !["json", "environment", ...operation.flags].includes(k)
8555
+ ))
8556
+ throw new Error(
8557
+ "Invalid GA4 operation or flags. Run siteos analytics ga4 --help."
8558
+ );
8559
+ const context = await commonServiceContext(
8560
+ options,
8561
+ "analytics",
8562
+ values.environment
8563
+ );
8564
+ if (!context) throw new Error("Select a SiteOS Project first.");
8565
+ const runtime = commonProjectRuntime(options);
8566
+ const scope = operation.write ? "analytics:reports:write" : "analytics:workspace:read";
8567
+ const grant = await runtime.grants.acquire({
8568
+ audience: "siteos-analytics",
8569
+ scopes: [scope]
8570
+ });
8571
+ if (grant.grant.audience !== "siteos-analytics" || grant.grant.organizationId !== context.overview.project.organizationId || grant.grant.scopes.length !== 1 || grant.grant.scopes[0] !== scope)
8572
+ throw new Error(
8573
+ "The Analytics grant does not match this Project and operation."
8574
+ );
8575
+ if (!options.fetchImpl)
8576
+ throw new Error("SiteOS API access is unavailable.");
8577
+ const send = async (suffix, method = "GET", body) => {
8578
+ const response = await options.fetchImpl(
8579
+ `${runtime.api.origin}/api/analytics/v1/resources/${encodeURIComponent(context.resourceId)}/ga4${suffix}`,
8580
+ {
8581
+ method,
8582
+ headers: {
8583
+ Accept: "application/json",
8584
+ Authorization: `Bearer ${grant.accessToken}`,
8585
+ ...body ? { "Content-Type": "application/json" } : {}
8586
+ },
8587
+ ...body ? { body: JSON.stringify(body) } : {},
8588
+ signal: AbortSignal.timeout(3e4)
8589
+ }
8590
+ );
8591
+ const value = await response.json();
8592
+ if (!response.ok) {
8593
+ const error = z15.object({
8594
+ error: z15.object({ code: z15.string(), message: z15.string().max(500) })
8595
+ }).safeParse(value);
8596
+ throw new SiteOSAuthApiError({
8597
+ code: error.success ? error.data.error.code : "ANALYTICS_REQUEST_FAILED",
8598
+ message: error.success ? error.data.error.message : "The GA4 request failed.",
8599
+ status: response.status
8600
+ });
8601
+ }
8602
+ return z15.object({
8603
+ contractVersion: z15.literal(1),
8604
+ resourceId: z15.literal(context.resourceId)
8605
+ }).passthrough().parse(value);
8606
+ };
8607
+ const file = async () => {
8608
+ if (!values.file)
8609
+ throw new Error("Provide --file with a report definition.");
8610
+ const name = path25.resolve(options.cwd ?? process.cwd(), values.file);
8611
+ if ((await stat2(name)).size > 16384)
8612
+ throw new Error("Definition exceeds 16 KiB.");
8613
+ const text = await readFile16(name, "utf8");
8614
+ if (Buffer.byteLength(text) > 16384)
8615
+ throw new Error("Definition exceeds 16 KiB.");
8616
+ return z15.record(z15.unknown()).parse(JSON.parse(text));
8617
+ };
8618
+ let result;
8619
+ if (action === "saved") {
8620
+ result = sub === "list" ? await send("/saved") : sub === "save" ? await send("/saved", "POST", await file()) : await send("/saved", "DELETE", {
8621
+ id: z15.string().uuid().parse(positionals[2]),
8622
+ revision: z15.number().int().positive().parse(Number(values.revision))
8623
+ });
8624
+ } else {
8625
+ const base = await send("");
8626
+ const binding = z15.object({ revision: z15.number().int().positive(), state: z15.string() }).parse(base.binding);
8627
+ if (binding.state === "disconnected")
8628
+ throw new Error("Connect GA4 in Analytics Setup first.");
8629
+ if (action === "report") {
8630
+ const params = new URLSearchParams({ period: values.period ?? "7d" });
8631
+ for (const k of [
8632
+ "from",
8633
+ "to",
8634
+ "compare",
8635
+ "filters",
8636
+ "section",
8637
+ "offset",
8638
+ "limit"
8639
+ ])
8640
+ if (values[k] && values[k] !== "none") params.set(k, values[k]);
8641
+ if (!values.compare) params.set("compare", "previous");
8642
+ result = await send(`?${params}`);
8643
+ const cached = result.query;
8644
+ if (result.range && ((values.section ? !result.report || result.stale : !cached || cached.state === "ready" && cached.stale) || values.refresh)) {
8645
+ await send("/report", "POST", {
8646
+ period: 7,
8647
+ revision: binding.revision,
8648
+ filters: values.filters ? JSON.parse(values.filters) : [],
8649
+ range: Object.fromEntries(
8650
+ [...params].filter(
8651
+ ([k]) => ["period", "from", "to", "compare"].includes(k)
8652
+ )
8653
+ ),
8654
+ refresh: values.refresh ?? false
8655
+ });
8656
+ result = await send(`?${params}`);
8657
+ }
8658
+ } else if (action === "realtime") {
8659
+ const filters2 = values.filters ? JSON.parse(values.filters) : [];
8660
+ const params = new URLSearchParams({
8661
+ filters: JSON.stringify(filters2)
8662
+ });
8663
+ result = await send(`/realtime?${params}`);
8664
+ const live = result.realtime;
8665
+ if (!live?.report || live.stale) {
8666
+ await send("/realtime", "POST", {
8667
+ revision: binding.revision,
8668
+ filters: filters2
8669
+ });
8670
+ result = await send(`/realtime?${params}`);
8671
+ }
8672
+ } else {
8673
+ const selection = await file();
8674
+ const suffix = `/explore?${new URLSearchParams({ selection: JSON.stringify(selection) })}`;
8675
+ result = await send(suffix);
8676
+ const query = result.query;
8677
+ if (action === "export") {
8678
+ if (!query?.report)
8679
+ throw new Error(
8680
+ "No cached report. Run ga4 explore first, then export after it completes."
8681
+ );
8682
+ const csv = googleReportCsv(query.report, query.fetchedAt ?? null);
8683
+ return {
8684
+ exitCode: 0,
8685
+ stdout: csv
8686
+ };
8687
+ }
8688
+ if (!query || query.state === "ready" && query.stale || values.refresh) {
8689
+ await send("/explore", "POST", {
8690
+ selection,
8691
+ revision: binding.revision,
8692
+ refresh: values.refresh ?? false
8693
+ });
8694
+ result = await send(suffix);
8695
+ }
8696
+ }
8697
+ }
8698
+ return { exitCode: 0, stdout: JSON.stringify(result, null, 2) };
8699
+ } catch (cause) {
8700
+ const error = {
8701
+ code: cause instanceof SiteOSAuthApiError ? cause.code : "ANALYTICS_COMMAND_FAILED",
8702
+ message: cause instanceof z15.ZodError ? "The input or server response does not match the GA4 contract." : cause instanceof Error ? cause.message : "GA4 request failed."
8703
+ };
8704
+ return {
8705
+ exitCode: cause instanceof SiteOSAuthApiError ? 1 : 2,
8706
+ ...json ? { stdout: JSON.stringify({ error }) } : { stderr: error.message }
8707
+ };
8708
+ }
8709
+ }
8710
+
8711
+ // src/services/analytics-command.ts
8712
+ import { readFile as readFile17, stat as stat3 } from "fs/promises";
8713
+ import path26 from "path";
8714
+ import { parseArgs as parseArgs5 } from "util";
8715
+ import { z as z16 } from "zod";
8426
8716
  var ANALYTICS_HELP = `Set up website Analytics in the selected Project environment.
8427
8717
 
8428
8718
  Usage:
@@ -8444,6 +8734,7 @@ Usage:
8444
8734
  siteos analytics funnels create --file <funnel.json> [--environment <slug>] [--json]
8445
8735
  siteos analytics funnels archive <id> [--environment <slug>] [--json]
8446
8736
  siteos analytics monitoring prepare [--environment <slug>] [--json]
8737
+ ${GOOGLE_ANALYTICS_HELP.split("Usage:\n")[1].split("\n\n")[0]}
8447
8738
 
8448
8739
  Connect first: siteos project connect analytics. Cookie and Trace are optional.
8449
8740
  Use project environment use <slug> to select an environment; no resource fallback is allowed.
@@ -8453,27 +8744,27 @@ Settings require an owner/admin, the current revision and at least one change.
8453
8744
  Catalog files are bounded JSON using the documented event/campaign/goal/funnel contracts.
8454
8745
  Monitoring preparation creates a Trace draft only; publication remains a separate operation.
8455
8746
  Analytics is Unlimited during early access. No synthetic events are sent by these commands.`;
8456
- var EventInput = z14.object({
8457
- name: z14.string().regex(/^[a-z][a-z0-9_]{0,63}$/u).refine((v) => v !== "pageview" && !v.startsWith("cookie_")),
8458
- label: z14.string().trim().min(1).max(80),
8459
- properties: z14.record(
8460
- z14.string().regex(/^[a-z][a-z0-9_]{0,39}$/u),
8461
- z14.array(
8462
- z14.string().min(1).max(80).regex(/^[a-zA-Z0-9 _./:-]+$/u)
8747
+ var EventInput = z16.object({
8748
+ name: z16.string().regex(/^[a-z][a-z0-9_]{0,63}$/u).refine((v) => v !== "pageview" && !v.startsWith("cookie_")),
8749
+ label: z16.string().trim().min(1).max(80),
8750
+ properties: z16.record(
8751
+ z16.string().regex(/^[a-z][a-z0-9_]{0,39}$/u),
8752
+ z16.array(
8753
+ z16.string().min(1).max(80).regex(/^[a-zA-Z0-9 _./:-]+$/u)
8463
8754
  ).min(1).max(20)
8464
8755
  ).refine((v) => Object.keys(v).length <= 8)
8465
8756
  }).strict();
8466
- var Resource = z14.object({
8467
- id: z14.string(),
8468
- organizationId: z14.string(),
8469
- name: z14.string(),
8470
- slug: z14.string(),
8471
- origin: z14.string().url(),
8472
- publicKey: z14.string(),
8473
- revision: z14.number().int().positive(),
8474
- enabled: z14.boolean(),
8475
- cookieEvents: z14.boolean(),
8476
- minimalRealtime: z14.boolean()
8757
+ var Resource = z16.object({
8758
+ id: z16.string(),
8759
+ organizationId: z16.string(),
8760
+ name: z16.string(),
8761
+ slug: z16.string(),
8762
+ origin: z16.string().url(),
8763
+ publicKey: z16.string(),
8764
+ revision: z16.number().int().positive(),
8765
+ enabled: z16.boolean(),
8766
+ cookieEvents: z16.boolean(),
8767
+ minimalRealtime: z16.boolean()
8477
8768
  });
8478
8769
  var readKeys = [
8479
8770
  "days",
@@ -8518,10 +8809,14 @@ var pick = (value, keys) => Object.fromEntries(
8518
8809
  );
8519
8810
  async function runAnalyticsCommand(options) {
8520
8811
  if (!options.args.length || options.args.some((v) => ["--help", "-h"].includes(v)))
8521
- return { exitCode: 0, stdout: ANALYTICS_HELP };
8812
+ return {
8813
+ exitCode: 0,
8814
+ stdout: ANALYTICS_HELP + "\n\n" + GOOGLE_ANALYTICS_HELP
8815
+ };
8816
+ if (options.args[0] === "ga4") return runAnalyticsGoogleCommand(options);
8522
8817
  const json = options.args.includes("--json");
8523
8818
  try {
8524
- const { positionals, values } = parseArgs4({
8819
+ const { positionals, values } = parseArgs5({
8525
8820
  args: options.args,
8526
8821
  strict: true,
8527
8822
  allowPositionals: true,
@@ -8603,7 +8898,7 @@ async function runAnalyticsCommand(options) {
8603
8898
  throw new Error("Use --days 1, 7 or 28.");
8604
8899
  if (values.country && !/^(?:[A-Z]{2}|unknown)$/u.test(values.country))
8605
8900
  throw new Error("Use an uppercase ISO country code or unknown.");
8606
- if (values.campaign) z14.string().uuid().parse(values.campaign);
8901
+ if (values.campaign) z16.string().uuid().parse(values.campaign);
8607
8902
  const setting = route === "settings set", creating = positionals[1] === "create", archive = positionals[1] === "archive";
8608
8903
  let body;
8609
8904
  if (setting) {
@@ -8627,10 +8922,10 @@ async function runAnalyticsCommand(options) {
8627
8922
  } else if (creating) {
8628
8923
  if (!values.file)
8629
8924
  throw new Error("Creation requires --file with a JSON definition.");
8630
- const filename = path25.resolve(options.cwd ?? process.cwd(), values.file);
8631
- if ((await stat2(filename)).size > 16384)
8925
+ const filename = path26.resolve(options.cwd ?? process.cwd(), values.file);
8926
+ if ((await stat3(filename)).size > 16384)
8632
8927
  throw new Error("The definition must not exceed 16 KiB.");
8633
- const text = await readFile16(filename, "utf8");
8928
+ const text = await readFile17(filename, "utf8");
8634
8929
  if (Buffer.byteLength(text) > 16384)
8635
8930
  throw new Error("The definition must not exceed 16 KiB.");
8636
8931
  let definition;
@@ -8639,9 +8934,9 @@ async function runAnalyticsCommand(options) {
8639
8934
  } catch {
8640
8935
  throw new Error("The definition must contain valid JSON.");
8641
8936
  }
8642
- body = z14.record(z14.unknown()).parse(definition);
8937
+ body = z16.record(z16.unknown()).parse(definition);
8643
8938
  if (action === "events") body = EventInput.parse(body);
8644
- } else if (archive) body = { id: z14.string().uuid().parse(positionals[2]) };
8939
+ } else if (archive) body = { id: z16.string().uuid().parse(positionals[2]) };
8645
8940
  const context = await commonServiceContext(
8646
8941
  options,
8647
8942
  "analytics",
@@ -8688,8 +8983,8 @@ async function runAnalyticsCommand(options) {
8688
8983
  );
8689
8984
  const value = await response.json();
8690
8985
  if (!response.ok) {
8691
- const error = z14.object({
8692
- error: z14.object({ code: z14.string(), message: z14.string().max(500) })
8986
+ const error = z16.object({
8987
+ error: z16.object({ code: z16.string(), message: z16.string().max(500) })
8693
8988
  }).safeParse(value);
8694
8989
  throw new SiteOSAuthApiError({
8695
8990
  code: error.success ? error.data.error.code : "ANALYTICS_REQUEST_FAILED",
@@ -8697,9 +8992,9 @@ async function runAnalyticsCommand(options) {
8697
8992
  status: response.status
8698
8993
  });
8699
8994
  }
8700
- const record = z14.object({
8701
- contractVersion: z14.literal(1),
8702
- resourceId: z14.literal(context.resourceId)
8995
+ const record = z16.object({
8996
+ contractVersion: z16.literal(1),
8997
+ resourceId: z16.literal(context.resourceId)
8703
8998
  }).passthrough().parse(value);
8704
8999
  let output;
8705
9000
  if (setting) {
@@ -8718,16 +9013,16 @@ async function runAnalyticsCommand(options) {
8718
9013
  throw new Error("The saved event does not match this definition.");
8719
9014
  } else
8720
9015
  output = {
8721
- id: z14.string().uuid().parse(record.id),
9016
+ id: z16.string().uuid().parse(record.id),
8722
9017
  ...pick(
8723
9018
  record,
8724
9019
  action === "campaigns" ? ["label", "source", "medium", "campaign"] : action === "goals" ? ["label", "match"] : ["label", "steps"]
8725
9020
  )
8726
9021
  };
8727
9022
  } else if (archive)
8728
- output = { archived: z14.literal(true).parse(record.archived) };
9023
+ output = { archived: z16.literal(true).parse(record.archived) };
8729
9024
  else if (route === "monitoring prepare")
8730
- output = { state: z14.literal("prepared").parse(record.state) };
9025
+ output = { state: z16.literal("prepared").parse(record.state) };
8731
9026
  else if (action === "realtime") output = pick(record, liveKeys);
8732
9027
  else {
8733
9028
  const resource = Resource.parse(record.resource);
@@ -8739,8 +9034,8 @@ async function runAnalyticsCommand(options) {
8739
9034
  output = {
8740
9035
  resource,
8741
9036
  lastReceivedAt: record.lastReceivedAt,
8742
- consentSource: z14.object({
8743
- consentSource: z14.enum([
9037
+ consentSource: z16.object({
9038
+ consentSource: z16.enum([
8744
9039
  "cookie",
8745
9040
  "external",
8746
9041
  "waiting_for_cookie",
@@ -8752,11 +9047,11 @@ async function runAnalyticsCommand(options) {
8752
9047
  else if (action === "installation")
8753
9048
  output = {
8754
9049
  resource,
8755
- installation: z14.string().parse(record.installation),
9050
+ installation: z16.string().parse(record.installation),
8756
9051
  configuration: record.configuration
8757
9052
  };
8758
9053
  else if (action === "events") {
8759
- const definitions = z14.array(EventInput).parse(record.definitions);
9054
+ const definitions = z16.array(EventInput).parse(record.definitions);
8760
9055
  if (route === "events snippet") {
8761
9056
  const definition = definitions.find((d) => d.name === positionals[2]);
8762
9057
  if (!definition)
@@ -8770,13 +9065,13 @@ async function runAnalyticsCommand(options) {
8770
9065
  };
8771
9066
  } else output = { definitions, eventCounts: record.eventCounts };
8772
9067
  } else
8773
- output = action === "report" ? pick(record, readKeys) : { [action]: z14.array(z14.unknown()).parse(record[action]) };
9068
+ output = action === "report" ? pick(record, readKeys) : { [action]: z16.array(z16.unknown()).parse(record[action]) };
8774
9069
  }
8775
9070
  return { exitCode: 0, stdout: JSON.stringify(output, null, 2) };
8776
9071
  } catch (cause) {
8777
9072
  const error = cause instanceof SiteOSAuthApiError ? { code: cause.code, message: cause.message } : {
8778
9073
  code: "ANALYTICS_COMMAND_FAILED",
8779
- message: cause instanceof z14.ZodError ? "The Analytics input or response does not match the supported contract." : cause instanceof Error ? cause.message : "The Analytics command failed."
9074
+ message: cause instanceof z16.ZodError ? "The Analytics input or response does not match the supported contract." : cause instanceof Error ? cause.message : "The Analytics command failed."
8780
9075
  };
8781
9076
  return {
8782
9077
  exitCode: cause instanceof SiteOSAuthApiError ? 1 : 2,
@@ -8786,13 +9081,13 @@ async function runAnalyticsCommand(options) {
8786
9081
  }
8787
9082
 
8788
9083
  // src/services/seo-repair-command.ts
8789
- import { parseArgs as parseArgs5 } from "util";
8790
- import { z as z16 } from "zod";
9084
+ import { parseArgs as parseArgs6 } from "util";
9085
+ import { z as z18 } from "zod";
8791
9086
 
8792
9087
  // src/services/seo-report-client.ts
8793
9088
  import { open, writeFile as writeFile8 } from "fs/promises";
8794
- import path26 from "path";
8795
- import { z as z15 } from "zod";
9089
+ import path27 from "path";
9090
+ import { z as z17 } from "zod";
8796
9091
  async function seoReportClient(options, environment) {
8797
9092
  const context = await commonServiceContext(options, "seo", environment);
8798
9093
  if (!context)
@@ -8827,8 +9122,8 @@ async function seoReportClient(options, environment) {
8827
9122
  }
8828
9123
  );
8829
9124
  if (!response.ok) {
8830
- const result = z15.object({
8831
- error: z15.object({ code: z15.string(), message: z15.string().max(500) })
9125
+ const result = z17.object({
9126
+ error: z17.object({ code: z17.string(), message: z17.string().max(500) })
8832
9127
  }).safeParse(await response.json());
8833
9128
  throw new SiteOSAuthApiError({
8834
9129
  code: result.success ? result.data.error.code : "SEO_REQUEST_FAILED",
@@ -8842,12 +9137,12 @@ async function seoReportClient(options, environment) {
8842
9137
  }
8843
9138
  };
8844
9139
  }
8845
- var versionedReport = z15.object({ contractVersion: z15.literal(1) }).passthrough();
9140
+ var versionedReport = z17.object({ contractVersion: z17.literal(1) }).passthrough();
8846
9141
  async function readSeoInput(cwd, filename) {
8847
- const file = await open(path26.resolve(cwd ?? process.cwd(), filename), "r");
9142
+ const file = await open(path27.resolve(cwd ?? process.cwd(), filename), "r");
8848
9143
  try {
8849
- const stat3 = await file.stat();
8850
- if (!stat3.isFile() || stat3.size > 32e3)
9144
+ const stat4 = await file.stat();
9145
+ if (!stat4.isFile() || stat4.size > 32e3)
8851
9146
  throw new Error("Use a JSON request file of at most 32,000 bytes.");
8852
9147
  const buffer = Buffer.alloc(32001);
8853
9148
  let bytes = 0;
@@ -8868,14 +9163,14 @@ async function readSeoInput(cwd, filename) {
8868
9163
  }
8869
9164
  }
8870
9165
  async function writeSeoReport(options, filename, content) {
8871
- const output = path26.resolve(options.cwd ?? process.cwd(), filename);
9166
+ const output = path27.resolve(options.cwd ?? process.cwd(), filename);
8872
9167
  await writeFile8(output, content, { flag: "wx", mode: 384 });
8873
9168
  return output;
8874
9169
  }
8875
9170
  function seoReportFailure(cause, json, idempotencyKey) {
8876
9171
  const error = {
8877
9172
  code: cause instanceof SiteOSAuthApiError ? cause.code : "SEO_COMMAND_FAILED",
8878
- message: cause instanceof z15.ZodError ? "The SEO service returned an invalid response." : cause instanceof Error ? cause.message : "The SEO command failed."
9173
+ message: cause instanceof z17.ZodError ? "The SEO service returned an invalid response." : cause instanceof Error ? cause.message : "The SEO command failed."
8879
9174
  };
8880
9175
  return {
8881
9176
  exitCode: cause instanceof SiteOSAuthApiError ? 1 : 2,
@@ -8894,7 +9189,7 @@ function seoReportFailure(cause, json, idempotencyKey) {
8894
9189
  async function runSeoRepairCommand(options) {
8895
9190
  const json = options.args.includes("--json");
8896
9191
  try {
8897
- const { positionals, values } = parseArgs5({
9192
+ const { positionals, values } = parseArgs6({
8898
9193
  args: options.args,
8899
9194
  allowPositionals: true,
8900
9195
  strict: true,
@@ -8939,32 +9234,32 @@ async function runSeoRepairCommand(options) {
8939
9234
  });
8940
9235
  const record = versionedReport.parse(await response.json());
8941
9236
  if (recheck) {
8942
- z16.object({
8943
- id: z16.string(),
8944
- resourceId: z16.literal(client.resourceId),
8945
- organizationId: z16.literal(client.organizationId),
8946
- state: z16.literal("queued")
9237
+ z18.object({
9238
+ id: z18.string(),
9239
+ resourceId: z18.literal(client.resourceId),
9240
+ organizationId: z18.literal(client.organizationId),
9241
+ state: z18.literal("queued")
8947
9242
  }).parse(record.audit);
8948
9243
  return { exitCode: 0, stdout: JSON.stringify(record, null, 2) };
8949
9244
  }
8950
- const brief = z16.object({
8951
- schemaVersion: z16.literal(1),
8952
- context: z16.object({
8953
- auditId: z16.literal(values.audit),
8954
- resourceId: z16.literal(client.resourceId),
8955
- organizationId: z16.literal(client.organizationId),
8956
- selectedUrls: z16.array(z16.string()).min(1).max(20),
8957
- ruleId: z16.literal(values.rule ?? null)
9245
+ const brief = z18.object({
9246
+ schemaVersion: z18.literal(1),
9247
+ context: z18.object({
9248
+ auditId: z18.literal(values.audit),
9249
+ resourceId: z18.literal(client.resourceId),
9250
+ organizationId: z18.literal(client.organizationId),
9251
+ selectedUrls: z18.array(z18.string()).min(1).max(20),
9252
+ ruleId: z18.literal(values.rule ?? null)
8958
9253
  }),
8959
- evidence: z16.array(
8960
- z16.object({
8961
- url: z16.string(),
8962
- findings: z16.array(z16.unknown()).max(12)
9254
+ evidence: z18.array(
9255
+ z18.object({
9256
+ url: z18.string(),
9257
+ findings: z18.array(z18.unknown()).max(12)
8963
9258
  })
8964
9259
  ).min(1).max(20),
8965
- suggestedGroups: z16.array(z16.unknown()),
8966
- verificationCommand: z16.string().min(1),
8967
- text: z16.string().min(1)
9260
+ suggestedGroups: z18.array(z18.unknown()),
9261
+ verificationCommand: z18.string().min(1),
9262
+ text: z18.string().min(1)
8968
9263
  }).parse(record);
8969
9264
  const canonical = (value) => {
8970
9265
  const url = new URL(value);
@@ -8985,15 +9280,15 @@ async function runSeoRepairCommand(options) {
8985
9280
 
8986
9281
  // src/services/seo-command.ts
8987
9282
  import { writeFile as writeFile10 } from "fs/promises";
8988
- import path28 from "path";
8989
- import { parseArgs as parseArgs9 } from "util";
8990
- import { z as z20 } from "zod";
9283
+ import path29 from "path";
9284
+ import { parseArgs as parseArgs10 } from "util";
9285
+ import { z as z22 } from "zod";
8991
9286
 
8992
9287
  // src/services/seo-research-command.ts
8993
9288
  import { randomUUID as randomUUID6 } from "crypto";
8994
9289
  import { setTimeout as setTimeout2 } from "timers/promises";
8995
- import { isDeepStrictEqual, parseArgs as parseArgs6 } from "util";
8996
- import { z as z17 } from "zod";
9290
+ import { isDeepStrictEqual, parseArgs as parseArgs7 } from "util";
9291
+ import { z as z19 } from "zod";
8997
9292
  var RESEARCH_HELP = `
8998
9293
  siteos seo research summary [--environment <slug>] [--json]
8999
9294
  siteos seo research status --kind <kind> [--environment <slug>] [--json]
@@ -9040,11 +9335,11 @@ var kinds = [
9040
9335
  ];
9041
9336
  var sections = [...kinds, "ai-rankings"];
9042
9337
  var sectionOf = (request) => request.kind === "ai-visibility" && request.category ? "ai-rankings" : request.kind;
9043
- var requestSchema = z17.object({
9044
- kind: z17.enum(["domain", ...kinds]).transform((kind) => kind === "domain" ? "competitors" : kind),
9045
- target: z17.string().min(1).max(253)
9338
+ var requestSchema = z19.object({
9339
+ kind: z19.enum(["domain", ...kinds]).transform((kind) => kind === "domain" ? "competitors" : kind),
9340
+ target: z19.string().min(1).max(253)
9046
9341
  }).passthrough();
9047
- var runStates = z17.enum([
9342
+ var runStates = z19.enum([
9048
9343
  "queued",
9049
9344
  "running",
9050
9345
  "completed",
@@ -9060,7 +9355,7 @@ var csvCell = (value) => {
9060
9355
  async function runSeoResearchCommand(options) {
9061
9356
  let idempotencyKey;
9062
9357
  try {
9063
- const { values, positionals } = parseArgs6({
9358
+ const { values, positionals } = parseArgs7({
9064
9359
  args: options.args.slice(1),
9065
9360
  strict: true,
9066
9361
  allowPositionals: true,
@@ -9142,26 +9437,26 @@ async function runSeoResearchCommand(options) {
9142
9437
  );
9143
9438
  }
9144
9439
  const client = await seoReportClient(options, values.environment);
9145
- const resourceSchema = z17.object({
9146
- id: z17.literal(client.resourceId),
9147
- organizationId: z17.literal(client.organizationId)
9440
+ const resourceSchema = z19.object({
9441
+ id: z19.literal(client.resourceId),
9442
+ organizationId: z19.literal(client.organizationId)
9148
9443
  }).passthrough();
9149
- const runSchema = z17.object({
9150
- id: z17.string(),
9151
- resourceId: z17.literal(client.resourceId),
9152
- organizationId: z17.literal(client.organizationId),
9153
- websiteUrl: z17.string(),
9444
+ const runSchema = z19.object({
9445
+ id: z19.string(),
9446
+ resourceId: z19.literal(client.resourceId),
9447
+ organizationId: z19.literal(client.organizationId),
9448
+ websiteUrl: z19.string(),
9154
9449
  request: requestSchema,
9155
9450
  state: runStates,
9156
- createdAt: z17.string(),
9157
- finishedAt: z17.string().nullable(),
9158
- parts: z17.array(
9159
- z17.object({
9160
- key: z17.string(),
9161
- state: z17.enum(["completed", "failed"]),
9162
- error: z17.string().nullable(),
9163
- dataset: z17.record(z17.unknown()).nullable(),
9164
- observedAt: z17.string()
9451
+ createdAt: z19.string(),
9452
+ finishedAt: z19.string().nullable(),
9453
+ parts: z19.array(
9454
+ z19.object({
9455
+ key: z19.string(),
9456
+ state: z19.enum(["completed", "failed"]),
9457
+ error: z19.string().nullable(),
9458
+ dataset: z19.record(z19.unknown()).nullable(),
9459
+ observedAt: z19.string()
9165
9460
  }).passthrough()
9166
9461
  )
9167
9462
  }).passthrough();
@@ -9175,36 +9470,36 @@ async function runSeoResearchCommand(options) {
9175
9470
  const query = `?kind=${encodeURIComponent(values.kind ?? "rankings")}`;
9176
9471
  let record;
9177
9472
  if (action.startsWith("serp ")) {
9178
- const snapshotSchema = z17.object({
9179
- id: z17.string(),
9180
- organizationId: z17.literal(client.organizationId),
9181
- resourceId: z17.literal(client.resourceId),
9182
- sourceRunId: z17.literal(id),
9473
+ const snapshotSchema = z19.object({
9474
+ id: z19.string(),
9475
+ organizationId: z19.literal(client.organizationId),
9476
+ resourceId: z19.literal(client.resourceId),
9477
+ sourceRunId: z19.literal(id),
9183
9478
  request: requestSchema,
9184
- keywordKey: z17.literal(
9479
+ keywordKey: z19.literal(
9185
9480
  values.keyword.normalize("NFKC").trim().toLowerCase().replace(/\s+/gu, " ")
9186
9481
  ),
9187
- state: z17.enum([
9482
+ state: z19.enum([
9188
9483
  "queued",
9189
9484
  "running",
9190
9485
  "completed",
9191
9486
  "failed",
9192
9487
  "cancelled"
9193
9488
  ]),
9194
- dataset: z17.object({
9195
- type: z17.literal("serp"),
9196
- keyword: z17.string(),
9197
- country: z17.string(),
9198
- language: z17.string(),
9199
- device: z17.enum(["desktop", "mobile"]),
9200
- observedAt: z17.string(),
9201
- rows: z17.array(
9202
- z17.object({
9203
- position: z17.number(),
9204
- title: z17.string(),
9205
- url: z17.string(),
9206
- domain: z17.string(),
9207
- description: z17.string()
9489
+ dataset: z19.object({
9490
+ type: z19.literal("serp"),
9491
+ keyword: z19.string(),
9492
+ country: z19.string(),
9493
+ language: z19.string(),
9494
+ device: z19.enum(["desktop", "mobile"]),
9495
+ observedAt: z19.string(),
9496
+ rows: z19.array(
9497
+ z19.object({
9498
+ position: z19.number(),
9499
+ title: z19.string(),
9500
+ url: z19.string(),
9501
+ domain: z19.string(),
9502
+ description: z19.string()
9208
9503
  })
9209
9504
  )
9210
9505
  }).nullable()
@@ -9314,9 +9609,9 @@ async function runSeoResearchCommand(options) {
9314
9609
  name: values.name.trim(),
9315
9610
  ...values.id ? { id: values.id } : {}
9316
9611
  });
9317
- const saved = z17.object({
9318
- id: z17.string(),
9319
- name: z17.literal(values.name.trim()),
9612
+ const saved = z19.object({
9613
+ id: z19.string(),
9614
+ name: z19.literal(values.name.trim()),
9320
9615
  request: requestSchema
9321
9616
  }).parse(record.saved);
9322
9617
  if (values.id && saved.id !== values.id || !isDeepStrictEqual(saved.request, plan.request))
@@ -9330,23 +9625,23 @@ async function runSeoResearchCommand(options) {
9330
9625
  action === "cancel" ? "run" : "write",
9331
9626
  {}
9332
9627
  );
9333
- z17.literal(true).parse(
9628
+ z19.literal(true).parse(
9334
9629
  record[action === "cancel" ? "cancelled" : "removed"]
9335
9630
  );
9336
9631
  } else if (action === "summary") {
9337
9632
  record = await get("/summary");
9338
9633
  resourceSchema.parse(record.resource);
9339
- z17.array(z17.object({ kind: z17.enum(sections), summary: z17.unknown() })).parse(
9634
+ z19.array(z19.object({ kind: z19.enum(sections), summary: z19.unknown() })).parse(
9340
9635
  record.checks
9341
9636
  );
9342
9637
  } else if (["status", "history", "saved list"].includes(action)) {
9343
9638
  const view = await get(query);
9344
9639
  resourceSchema.parse(view.resource);
9345
- const runs = z17.array(runSchema).parse(view.runs);
9346
- const saved = z17.array(
9347
- z17.object({
9348
- id: z17.string(),
9349
- name: z17.string(),
9640
+ const runs = z19.array(runSchema).parse(view.runs);
9641
+ const saved = z19.array(
9642
+ z19.object({
9643
+ id: z19.string(),
9644
+ name: z19.string(),
9350
9645
  request: requestSchema
9351
9646
  }).passthrough()
9352
9647
  ).parse(view.saved);
@@ -9481,8 +9776,8 @@ async function runSeoResearchCommand(options) {
9481
9776
  }
9482
9777
 
9483
9778
  // src/services/seo-gsc-command.ts
9484
- import { parseArgs as parseArgs7 } from "util";
9485
- import { z as z18 } from "zod";
9779
+ import { parseArgs as parseArgs8 } from "util";
9780
+ import { z as z20 } from "zod";
9486
9781
  var GSC_HELP = `
9487
9782
  siteos seo gsc status [--environment <slug>] [--json]
9488
9783
  siteos seo gsc report [--dataset <pages|queries>] [--query <text>] [--filter <all|issues|declining>] [--sort <clicks|change|impressions>] [--page <number>] [--url <page-url>] [--environment <slug>] [--json]
@@ -9500,7 +9795,7 @@ Bind/disconnect require a current revision and separate settings authority. Disc
9500
9795
  `;
9501
9796
  async function runSeoGscCommand(options) {
9502
9797
  try {
9503
- const { values, positionals } = parseArgs7({
9798
+ const { values, positionals } = parseArgs8({
9504
9799
  args: options.args.slice(1),
9505
9800
  strict: true,
9506
9801
  allowPositionals: true,
@@ -9582,13 +9877,13 @@ async function runSeoGscCommand(options) {
9582
9877
  `seo:search:${action === "sync" ? "sync" : writing ? "write" : "read"}`,
9583
9878
  body
9584
9879
  );
9585
- const runSchema = z18.object({
9586
- id: z18.string(),
9587
- resourceId: z18.literal(client.resourceId),
9588
- organizationId: z18.literal(client.organizationId),
9589
- websiteUrl: z18.string(),
9590
- siteUrl: z18.string(),
9591
- state: z18.enum([
9880
+ const runSchema = z20.object({
9881
+ id: z20.string(),
9882
+ resourceId: z20.literal(client.resourceId),
9883
+ organizationId: z20.literal(client.organizationId),
9884
+ websiteUrl: z20.string(),
9885
+ siteUrl: z20.string(),
9886
+ state: z20.enum([
9592
9887
  "queued",
9593
9888
  "running",
9594
9889
  "completed",
@@ -9596,32 +9891,32 @@ async function runSeoGscCommand(options) {
9596
9891
  "failed",
9597
9892
  "cancelled"
9598
9893
  ]),
9599
- dates: z18.unknown()
9894
+ dates: z20.unknown()
9600
9895
  }).passthrough();
9601
- const bindingSchema = z18.object({
9602
- resourceId: z18.literal(client.resourceId),
9603
- organizationId: z18.literal(client.organizationId),
9604
- websiteUrl: z18.string(),
9605
- siteUrl: z18.string(),
9606
- revision: z18.number().int().positive()
9896
+ const bindingSchema = z20.object({
9897
+ resourceId: z20.literal(client.resourceId),
9898
+ organizationId: z20.literal(client.organizationId),
9899
+ websiteUrl: z20.string(),
9900
+ siteUrl: z20.string(),
9901
+ revision: z20.number().int().positive()
9607
9902
  }).passthrough();
9608
- const metrics = z18.object({
9609
- clicks: z18.number(),
9610
- impressions: z18.number(),
9611
- ctr: z18.number(),
9612
- position: z18.number()
9903
+ const metrics = z20.object({
9904
+ clicks: z20.number(),
9905
+ impressions: z20.number(),
9906
+ ctr: z20.number(),
9907
+ position: z20.number()
9613
9908
  }).passthrough().nullable();
9614
9909
  const validateView = (data) => {
9615
9910
  const record2 = versionedReport.parse(data);
9616
- z18.literal(client.resourceId).parse(record2.resourceId);
9911
+ z20.literal(client.resourceId).parse(record2.resourceId);
9617
9912
  bindingSchema.nullable().parse(record2.binding);
9618
9913
  runSchema.nullable().parse(record2.latest);
9619
9914
  runSchema.nullable().parse(record2.report);
9620
- z18.array(
9621
- z18.object({ key: z18.string(), current: metrics, previous: metrics }).passthrough()
9915
+ z20.array(
9916
+ z20.object({ key: z20.string(), current: metrics, previous: metrics }).passthrough()
9622
9917
  ).parse(record2.rows);
9623
- z18.number().int().nonnegative().parse(record2.total);
9624
- z18.literal(values.dataset ?? "pages").parse(record2.dataset);
9918
+ z20.number().int().nonnegative().parse(record2.total);
9919
+ z20.literal(values.dataset ?? "pages").parse(record2.dataset);
9625
9920
  return record2;
9626
9921
  };
9627
9922
  if (action === "export") {
@@ -9637,9 +9932,9 @@ async function runSeoGscCommand(options) {
9637
9932
  const text = await response.text();
9638
9933
  if (values.format === "json") {
9639
9934
  const report = validateView(JSON.parse(text));
9640
- z18.literal(reportId).parse(runSchema.parse(report.report).id);
9641
- z18.array(z18.unknown()).length(rows).parse(report.rows);
9642
- z18.literal(truncated === "true").parse(report.truncated);
9935
+ z20.literal(reportId).parse(runSchema.parse(report.report).id);
9936
+ z20.array(z20.unknown()).length(rows).parse(report.rows);
9937
+ z20.literal(truncated === "true").parse(report.truncated);
9643
9938
  }
9644
9939
  const output = await writeSeoReport(options, values.output, text);
9645
9940
  return {
@@ -9660,7 +9955,7 @@ async function runSeoGscCommand(options) {
9660
9955
  };
9661
9956
  }
9662
9957
  const record = versionedReport.parse(await response.json());
9663
- if (action === "disconnect") z18.literal(true).parse(record.unbound);
9958
+ if (action === "disconnect") z20.literal(true).parse(record.unbound);
9664
9959
  else if (writing) {
9665
9960
  runSchema.parse(record.run);
9666
9961
  if (action === "bind") {
@@ -9680,10 +9975,10 @@ async function runSeoGscCommand(options) {
9680
9975
  // src/services/seo-performance-command.ts
9681
9976
  import { randomUUID as randomUUID7 } from "crypto";
9682
9977
  import { writeFile as writeFile9 } from "fs/promises";
9683
- import path27 from "path";
9978
+ import path28 from "path";
9684
9979
  import { setTimeout as setTimeout3 } from "timers/promises";
9685
- import { parseArgs as parseArgs8 } from "util";
9686
- import { z as z19 } from "zod";
9980
+ import { parseArgs as parseArgs9 } from "util";
9981
+ import { z as z21 } from "zod";
9687
9982
  var PERFORMANCE_HELP = `
9688
9983
  siteos seo performance run --audit <id> --url <url> [--url <url>...] [--device <mobile|desktop>] [--idempotency-key <key>] [--environment <slug>] [--json]
9689
9984
  siteos seo performance list [--device <mobile|desktop>] [--environment <slug>] [--json]
@@ -9700,7 +9995,7 @@ Reuse the returned idempotency key after an uncertain run response. Wait default
9700
9995
  async function runSeoPerformanceCommand(options) {
9701
9996
  let idempotencyKey;
9702
9997
  try {
9703
- const { values, positionals } = parseArgs8({
9998
+ const { values, positionals } = parseArgs9({
9704
9999
  args: options.args.slice(1),
9705
10000
  strict: true,
9706
10001
  allowPositionals: true,
@@ -9771,12 +10066,12 @@ async function runSeoPerformanceCommand(options) {
9771
10066
  const runtime = commonProjectRuntime(options);
9772
10067
  const writing = ["run", "cancel"].includes(action);
9773
10068
  const scope = writing ? "seo:audits:write" : "seo:workspace:read";
9774
- const batchSchema = z19.object({
9775
- id: z19.string(),
9776
- resourceId: z19.literal(context.resourceId),
9777
- organizationId: z19.literal(context.overview.project.organizationId),
9778
- sourceAuditId: z19.string(),
9779
- state: z19.enum([
10069
+ const batchSchema = z21.object({
10070
+ id: z21.string(),
10071
+ resourceId: z21.literal(context.resourceId),
10072
+ organizationId: z21.literal(context.overview.project.organizationId),
10073
+ sourceAuditId: z21.string(),
10074
+ state: z21.enum([
9780
10075
  "queued",
9781
10076
  "running",
9782
10077
  "completed",
@@ -9784,8 +10079,8 @@ async function runSeoPerformanceCommand(options) {
9784
10079
  "failed",
9785
10080
  "cancelled"
9786
10081
  ]),
9787
- device: z19.enum(["mobile", "desktop"]),
9788
- urls: z19.array(z19.string()).min(1).max(10)
10082
+ device: z21.enum(["mobile", "desktop"]),
10083
+ urls: z21.array(z21.string()).min(1).max(10)
9789
10084
  }).passthrough();
9790
10085
  const query = new URLSearchParams();
9791
10086
  if (values.device) query.set("device", values.device);
@@ -9833,17 +10128,17 @@ async function runSeoPerformanceCommand(options) {
9833
10128
  );
9834
10129
  const text = await response.text();
9835
10130
  if (values.format === "json") {
9836
- const parsed = z19.object({
9837
- contractVersion: z19.literal(1),
10131
+ const parsed = z21.object({
10132
+ contractVersion: z21.literal(1),
9838
10133
  batch: batchSchema,
9839
- pages: z19.array(z19.unknown())
10134
+ pages: z21.array(z21.unknown())
9840
10135
  }).parse(JSON.parse(text));
9841
10136
  if (parsed.batch.id !== id)
9842
10137
  throw new Error(
9843
10138
  "The export response does not match the selected check."
9844
10139
  );
9845
10140
  }
9846
- const output = path27.resolve(
10141
+ const output = path28.resolve(
9847
10142
  options.cwd ?? process.cwd(),
9848
10143
  values.output
9849
10144
  );
@@ -9859,8 +10154,8 @@ async function runSeoPerformanceCommand(options) {
9859
10154
  }
9860
10155
  const data = await response.json();
9861
10156
  if (!response.ok) {
9862
- const error = z19.object({
9863
- error: z19.object({ code: z19.string(), message: z19.string().max(500) })
10157
+ const error = z21.object({
10158
+ error: z21.object({ code: z21.string(), message: z21.string().max(500) })
9864
10159
  }).safeParse(data);
9865
10160
  throw new SiteOSAuthApiError({
9866
10161
  code: error.success ? error.data.error.code : "SEO_REQUEST_FAILED",
@@ -9868,8 +10163,8 @@ async function runSeoPerformanceCommand(options) {
9868
10163
  status: response.status
9869
10164
  });
9870
10165
  }
9871
- const record = z19.object({ contractVersion: z19.literal(1) }).passthrough().parse(data);
9872
- if (action === "cancel") z19.literal(true).parse(record.cancelled);
10166
+ const record = z21.object({ contractVersion: z21.literal(1) }).passthrough().parse(data);
10167
+ if (action === "cancel") z21.literal(true).parse(record.cancelled);
9873
10168
  else if (action === "run") {
9874
10169
  const accepted = batchSchema.parse(record.batch);
9875
10170
  const normalize = (url) => {
@@ -9882,8 +10177,8 @@ async function runSeoPerformanceCommand(options) {
9882
10177
  "The queued check does not match the requested source, device and URLs."
9883
10178
  );
9884
10179
  } else {
9885
- z19.literal(context.resourceId).parse(record.resourceId);
9886
- z19.array(batchSchema).parse(record.batches);
10180
+ z21.literal(context.resourceId).parse(record.resourceId);
10181
+ z21.array(batchSchema).parse(record.batches);
9887
10182
  const selected = batchSchema.nullable().parse(record.batch);
9888
10183
  if (id && selected?.id !== id)
9889
10184
  throw new Error(
@@ -9910,7 +10205,7 @@ async function runSeoPerformanceCommand(options) {
9910
10205
  } catch (cause) {
9911
10206
  const error = {
9912
10207
  code: cause instanceof SiteOSAuthApiError ? cause.code : "SEO_COMMAND_FAILED",
9913
- message: cause instanceof z19.ZodError ? "The SEO service returned an invalid response." : cause instanceof Error ? cause.message : "The performance command failed."
10208
+ message: cause instanceof z21.ZodError ? "The SEO service returned an invalid response." : cause instanceof Error ? cause.message : "The performance command failed."
9914
10209
  };
9915
10210
  return {
9916
10211
  exitCode: cause instanceof SiteOSAuthApiError ? 1 : 2,
@@ -9982,7 +10277,7 @@ async function runSeoCommand(options) {
9982
10277
  return runSeoRepairCommand(options);
9983
10278
  const json = options.args.includes("--json");
9984
10279
  try {
9985
- const { positionals, values } = parseArgs9({
10280
+ const { positionals, values } = parseArgs10({
9986
10281
  args: options.args,
9987
10282
  strict: true,
9988
10283
  allowPositionals: true,
@@ -10188,17 +10483,17 @@ async function runSeoCommand(options) {
10188
10483
  );
10189
10484
  const text = await response.text();
10190
10485
  if (values.format === "json")
10191
- z20.object({
10192
- contractVersion: z20.literal(1),
10193
- audit: z20.object({
10194
- id: z20.literal(values.audit),
10195
- resourceId: z20.literal(context.resourceId)
10486
+ z22.object({
10487
+ contractVersion: z22.literal(1),
10488
+ audit: z22.object({
10489
+ id: z22.literal(values.audit),
10490
+ resourceId: z22.literal(context.resourceId)
10196
10491
  }),
10197
- kind: z20.literal(values.kind),
10198
- totalRows: z20.literal(rows),
10199
- rows: z20.array(z20.unknown()).length(rows)
10492
+ kind: z22.literal(values.kind),
10493
+ totalRows: z22.literal(rows),
10494
+ rows: z22.array(z22.unknown()).length(rows)
10200
10495
  }).parse(JSON.parse(text));
10201
- const output = path28.resolve(options.cwd ?? process.cwd(), values.output);
10496
+ const output = path29.resolve(options.cwd ?? process.cwd(), values.output);
10202
10497
  await writeFile10(output, text, { flag: "wx", mode: 384 });
10203
10498
  return {
10204
10499
  exitCode: 0,
@@ -10217,8 +10512,8 @@ async function runSeoCommand(options) {
10217
10512
  }
10218
10513
  const data = await response.json();
10219
10514
  if (!response.ok) {
10220
- const result = z20.object({
10221
- error: z20.object({ code: z20.string(), message: z20.string().max(500) })
10515
+ const result = z22.object({
10516
+ error: z22.object({ code: z22.string(), message: z22.string().max(500) })
10222
10517
  }).safeParse(data);
10223
10518
  throw new SiteOSAuthApiError({
10224
10519
  code: result.success ? result.data.error.code : "SEO_REQUEST_FAILED",
@@ -10226,34 +10521,34 @@ async function runSeoCommand(options) {
10226
10521
  status: response.status
10227
10522
  });
10228
10523
  }
10229
- const record = z20.object({ contractVersion: z20.literal(1) }).passthrough().parse(data);
10524
+ const record = z22.object({ contractVersion: z22.literal(1) }).passthrough().parse(data);
10230
10525
  if (automation) {
10231
- z20.literal(context.resourceId).parse(record.resourceId);
10232
- const schedule = z20.object({
10233
- enabled: z20.boolean(),
10234
- weekday: z20.number().int().min(1).max(7),
10235
- time: z20.string(),
10236
- timeZone: z20.string(),
10237
- revision: z20.number().int().min(0),
10238
- nextRunAt: z20.string().nullable()
10526
+ z22.literal(context.resourceId).parse(record.resourceId);
10527
+ const schedule = z22.object({
10528
+ enabled: z22.boolean(),
10529
+ weekday: z22.number().int().min(1).max(7),
10530
+ time: z22.string(),
10531
+ timeZone: z22.string(),
10532
+ revision: z22.number().int().min(0),
10533
+ nextRunAt: z22.string().nullable()
10239
10534
  });
10240
- const notificationRoute = z20.object({
10241
- enabled: z20.boolean(),
10242
- minimumSeverity: z20.enum(["error", "warning"]),
10243
- includeFailures: z20.boolean(),
10244
- revision: z20.number().int().min(0),
10245
- destinationId: z20.string().nullable()
10535
+ const notificationRoute = z22.object({
10536
+ enabled: z22.boolean(),
10537
+ minimumSeverity: z22.enum(["error", "warning"]),
10538
+ includeFailures: z22.boolean(),
10539
+ revision: z22.number().int().min(0),
10540
+ destinationId: z22.string().nullable()
10246
10541
  });
10247
10542
  if (retryNotification) {
10248
- z20.literal(true).parse(record.retryQueued);
10249
- z20.literal(positionals[2]).parse(record.notificationId);
10543
+ z22.literal(true).parse(record.retryQueued);
10544
+ z22.literal(positionals[2]).parse(record.notificationId);
10250
10545
  } else if (route === "notifications destinations")
10251
- z20.object({
10252
- candidates: z20.array(
10253
- z20.object({
10254
- candidateId: z20.string(),
10255
- label: z20.string(),
10256
- availability: z20.literal("available")
10546
+ z22.object({
10547
+ candidates: z22.array(
10548
+ z22.object({
10549
+ candidateId: z22.string(),
10550
+ label: z22.string(),
10551
+ availability: z22.literal("available")
10257
10552
  })
10258
10553
  )
10259
10554
  }).parse(record);
@@ -10266,46 +10561,46 @@ async function runSeoCommand(options) {
10266
10561
  notificationRoute.parse(record.route);
10267
10562
  }
10268
10563
  } else if (!writing) {
10269
- const validated = z20.object({
10270
- resource: z20.object({
10271
- id: z20.literal(context.resourceId),
10272
- organizationId: z20.literal(context.overview.project.organizationId)
10564
+ const validated = z22.object({
10565
+ resource: z22.object({
10566
+ id: z22.literal(context.resourceId),
10567
+ organizationId: z22.literal(context.overview.project.organizationId)
10273
10568
  }),
10274
- audits: z20.array(z20.object({ id: z20.string() }).passthrough()),
10275
- audit: z20.object({
10276
- id: z20.string(),
10277
- resourceId: z20.literal(context.resourceId)
10569
+ audits: z22.array(z22.object({ id: z22.string() }).passthrough()),
10570
+ audit: z22.object({
10571
+ id: z22.string(),
10572
+ resourceId: z22.literal(context.resourceId)
10278
10573
  }).passthrough().nullable(),
10279
- pages: z20.array(z20.unknown()),
10280
- issues: z20.array(z20.unknown()),
10281
- changes: z20.array(z20.unknown()),
10282
- totalChanges: z20.number(),
10283
- dispositions: z20.array(z20.unknown())
10574
+ pages: z22.array(z22.unknown()),
10575
+ issues: z22.array(z22.unknown()),
10576
+ changes: z22.array(z22.unknown()),
10577
+ totalChanges: z22.number(),
10578
+ dispositions: z22.array(z22.unknown())
10284
10579
  }).passthrough().parse(record);
10285
10580
  const selected = query.get("audit");
10286
10581
  if (selected && validated.audit?.id !== selected)
10287
10582
  throw new Error("The SEO response does not match the requested audit.");
10288
10583
  } else if (record.audit)
10289
- z20.object({
10290
- id: z20.string(),
10291
- resourceId: z20.literal(context.resourceId),
10292
- organizationId: z20.literal(context.overview.project.organizationId),
10293
- state: z20.literal("queued")
10584
+ z22.object({
10585
+ id: z22.string(),
10586
+ resourceId: z22.literal(context.resourceId),
10587
+ organizationId: z22.literal(context.overview.project.organizationId),
10588
+ state: z22.literal("queued")
10294
10589
  }).parse(record.audit);
10295
- else if (route === "audit cancel") z20.literal(true).parse(record.cancelled);
10590
+ else if (route === "audit cancel") z22.literal(true).parse(record.cancelled);
10296
10591
  else if (action === "issue")
10297
- z20.object({
10298
- url: z20.literal(values.url),
10299
- ruleId: z20.literal(values.rule),
10300
- ignored: z20.literal(positionals[1] === "ignore"),
10301
- revision: z20.literal(Number(values.revision) + 1)
10592
+ z22.object({
10593
+ url: z22.literal(values.url),
10594
+ ruleId: z22.literal(values.rule),
10595
+ ignored: z22.literal(positionals[1] === "ignore"),
10596
+ revision: z22.literal(Number(values.revision) + 1)
10302
10597
  }).parse(record.disposition);
10303
10598
  else throw new Error("The SEO service returned an invalid response.");
10304
10599
  return { exitCode: 0, stdout: JSON.stringify(record, null, 2) };
10305
10600
  } catch (cause) {
10306
10601
  const error = {
10307
10602
  code: cause instanceof SiteOSAuthApiError ? cause.code : "SEO_COMMAND_FAILED",
10308
- message: cause instanceof z20.ZodError ? "The SEO service returned an invalid response." : cause instanceof Error ? cause.message : "The SEO command failed."
10603
+ message: cause instanceof z22.ZodError ? "The SEO service returned an invalid response." : cause instanceof Error ? cause.message : "The SEO command failed."
10309
10604
  };
10310
10605
  return {
10311
10606
  exitCode: cause instanceof SiteOSAuthApiError ? 1 : 2,