@rolino/cli 0.4.0 → 0.5.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "./chunk-VQOMONSS.js";
4
+ } from "./chunk-TFNGSKT3.js";
5
5
 
6
6
  // src/bin.ts
7
7
  void runCli().then(
@@ -277,7 +277,7 @@ async function setupMcp(options) {
277
277
  // src/cli.ts
278
278
  import { randomUUID } from "crypto";
279
279
  import { openAsBlob } from "fs";
280
- import { stat } from "fs/promises";
280
+ import { readFile as readFile2, stat } from "fs/promises";
281
281
  import { basename, extname, resolve as resolve2 } from "path";
282
282
  import { createInterface } from "readline/promises";
283
283
  import { Command, CommanderError, InvalidArgumentError } from "commander";
@@ -285,7 +285,7 @@ import { Command, CommanderError, InvalidArgumentError } from "commander";
285
285
  // package.json
286
286
  var package_default = {
287
287
  name: "@rolino/cli",
288
- version: "0.4.0",
288
+ version: "0.5.0-beta.0",
289
289
  description: "Agent-friendly command-line interface for Rolino",
290
290
  type: "module",
291
291
  license: "MIT",
@@ -342,9 +342,9 @@ var package_default = {
342
342
  dev: "tsx src/bin.ts"
343
343
  },
344
344
  dependencies: {
345
- "@rolino/contracts": "0.4.0",
346
- "@rolino/local-auth": "0.4.0",
347
- "@rolino/sdk": "0.4.0",
345
+ "@rolino/contracts": "0.5.0-beta.0",
346
+ "@rolino/local-auth": "0.5.0-beta.0",
347
+ "@rolino/sdk": "0.5.0-beta.0",
348
348
  commander: "^15.0.0",
349
349
  open: "^11.0.0"
350
350
  },
@@ -359,10 +359,14 @@ var package_default = {
359
359
  // src/cli.ts
360
360
  import {
361
361
  PostStatusSchema,
362
+ AgentBlogDraftUpdateSchema,
362
363
  ProjectCreateInputSchema,
363
364
  ProjectTypeSchema,
364
365
  ProviderDeliveryOptionsProviderSchema,
365
366
  PublishingProviderSchema,
367
+ SeoOpportunityKindSchema,
368
+ SeoExpectedImpactSchema,
369
+ SeoReportCompletenessSchema,
366
370
  TikTokDraftSettingsSchema,
367
371
  YouTubePostSettingsSchema
368
372
  } from "@rolino/contracts";
@@ -718,7 +722,6 @@ function formatPosts(data) {
718
722
  return ["ID STATUS SCHEDULED CAPTION", ...rows].join("\n");
719
723
  }
720
724
  function formatPost(post) {
721
- const queued = post.status === "PUBLISHING";
722
725
  return [
723
726
  `Post ${post.id}`,
724
727
  `Project: ${post.projectId}`,
@@ -727,11 +730,11 @@ function formatPost(post) {
727
730
  `Caption: ${post.caption || "-"}`,
728
731
  ...post.campaign ? [`Campaign: ${post.campaign.title} (${post.campaign.id})`] : [],
729
732
  ...post.scheduledAt ? [
730
- `${queued ? "Queued for publishing" : "Scheduled"}: ${post.scheduledAt}${post.timezone ? ` (${post.timezone})` : ""}`
733
+ `Scheduled: ${post.scheduledAt}${post.timezone ? ` (${post.timezone})` : ""}`
731
734
  ] : [],
732
735
  ...post.publishedAt ? [`Published: ${post.publishedAt}`] : [],
733
736
  `Media: ${post.media.length}`,
734
- `Destinations: ${post.destinations.map((destination) => `${destination.provider}:${queued && destination.status === "SCHEDULED" ? "QUEUED" : destination.status}`).join(", ") || "none"}`,
737
+ `Destinations: ${post.destinations.map((destination) => `${destination.provider}:${destination.deliveryMode}:${destination.stage}`).join(", ") || "none"}`,
735
738
  ...post.providerSettings?.TIKTOK ? [
736
739
  `TikTok: ${post.providerSettings.TIKTOK.postMode}, ${post.providerSettings.TIKTOK.privacyLevel ?? "visibility not set"}, settings ${post.providerSettings.TIKTOK.consentedAt ? "reviewed" : "not reviewed"}`
737
740
  ] : [],
@@ -742,7 +745,8 @@ function formatPost(post) {
742
745
  }
743
746
  function formatPostSchedulePending(pending) {
744
747
  return [
745
- `YouTube schedule change pending for post ${pending.postId}.`,
748
+ `YouTube publish-time confirmation pending for post ${pending.postId}.`,
749
+ "Video upload: complete (the video remains private).",
746
750
  pending.message,
747
751
  `Mutation: ${pending.mutationId}`
748
752
  ].join("\n");
@@ -788,8 +792,14 @@ function formatPostSchedulePreview(preview) {
788
792
  return [
789
793
  `Schedule preview for post ${preview.post.id}`,
790
794
  `Current version: ${preview.post.version}`,
795
+ `Delivery mode: ${preview.deliveryMode}`,
791
796
  `Publish at: ${preview.schedule.scheduledAt} (${preview.schedule.timezone})`,
792
797
  `Destinations: ${preview.destinations.join(", ")}`,
798
+ ...preview.youtube ? [
799
+ "YouTube preparation: starts immediately as PRIVATE.",
800
+ `YouTube scheduled visibility: ${preview.youtube.publishVisibility}.`,
801
+ `YouTube confirmation: ${preview.youtube.confirmationMeaning}`
802
+ ] : [],
793
803
  ...preview.providerReconciliation ? [`Provider reconciliation: ${preview.providerReconciliation.message}`] : [],
794
804
  `Confirmation expires: ${preview.confirmation.expiresAt}`,
795
805
  `Confirmation token: ${preview.confirmation.token}`,
@@ -801,9 +811,10 @@ function formatPostPublishPreview(preview) {
801
811
  return [
802
812
  `Immediate publish preview for post ${preview.post.id}`,
803
813
  `Current version: ${preview.post.version}`,
814
+ `Delivery mode: ${preview.deliveryMode}`,
804
815
  `Destinations: ${preview.destinations.join(", ")}`,
805
816
  "Effect: confirmed execution queues these destinations for immediate publishing.",
806
- ...preview.destinations.includes("YOUTUBE") ? ["YouTube effect: execution starts a resumable upload; PREPARING remains visible until upload, processing, and configured visibility are provider-confirmed."] : [],
817
+ ...preview.youtube ? [`YouTube effect: execution starts a resumable upload using ${preview.youtube.visibility}; it does not create a schedule. PREPARING remains visible until upload, processing, and visibility are provider-confirmed.`] : [],
807
818
  `Confirmation expires: ${preview.confirmation.expiresAt}`,
808
819
  `Confirmation token: ${preview.confirmation.token}`,
809
820
  ...attention.length ? ["Readiness notes:", ...attention.map((check) => `${check.status.toUpperCase()} ${check.provider ?? "POST"} ${check.code} ${check.message}`)] : ["All required readiness checks passed."]
@@ -816,6 +827,65 @@ function formatCalendarEvents(data) {
816
827
  const rows = data.items.map((event) => `${event.postId} ${event.scheduledAt} ${event.status} ${event.destinations.map((item) => item.provider).join(",") || "-"} ${event.title}`);
817
828
  return ["POST ID SCHEDULED STATUS DESTINATIONS TITLE", ...rows].join("\n");
818
829
  }
830
+ function formatSeoOpportunities(data) {
831
+ if (!data.items.length) return "No active SEO tasks found.";
832
+ return [
833
+ "ID DECISION FORMAT SCORE TOPIC RELEVANCE ACTION READINESS TITLE",
834
+ ...data.items.map((item) => `${item.id} ${item.task.decision} ${item.task.primaryFormat} ${item.score} ${item.confidence.topicRelevance.label} ${item.confidence.actionReadiness.label} ${item.title}`)
835
+ ].join("\n");
836
+ }
837
+ function formatSeoOpportunity(opportunity) {
838
+ return [
839
+ `${opportunity.title} (${opportunity.id})`,
840
+ `Project: ${opportunity.projectId}`,
841
+ `Decision: ${opportunity.task.decision}`,
842
+ `Primary format: ${opportunity.task.primaryFormat}`,
843
+ `Objective: ${opportunity.task.objective}`,
844
+ `Next step: ${opportunity.task.nextStep}`,
845
+ `Priority: ${opportunity.score}/100`,
846
+ `Topic relevance: ${opportunity.confidence.topicRelevance.label}${opportunity.confidence.topicRelevance.score === null ? "" : ` (${opportunity.confidence.topicRelevance.score}/100)`}`,
847
+ `Topic relevance explanation: ${opportunity.confidence.topicRelevance.explanation}`,
848
+ `Action readiness: ${opportunity.confidence.actionReadiness.label}${opportunity.confidence.actionReadiness.score === null ? "" : ` (${opportunity.confidence.actionReadiness.score}/100)`}`,
849
+ `Action readiness explanation: ${opportunity.confidence.actionReadiness.explanation}`,
850
+ "",
851
+ opportunity.brief
852
+ ].join("\n");
853
+ }
854
+ function formatSeoReports(data) {
855
+ if (!data.items.length) return "No weekly SEO reports found.";
856
+ return [
857
+ "ID PERIOD START PERIOD END STATUS READY RESEARCH REJECTED LEGACY",
858
+ ...data.items.map((item) => `${item.id} ${item.periodStart} ${item.periodEnd} ${item.completeness} ${item.readyTaskCount} ${item.researchTaskCount} ${item.rejectedFindingCount} ${item.legacy}`)
859
+ ].join("\n");
860
+ }
861
+ function formatSeoReport(report) {
862
+ return [
863
+ `Weekly SEO report ${report.id}`,
864
+ `Project: ${report.projectId}`,
865
+ `Period: ${report.periodStart} to ${report.periodEnd}`,
866
+ `Schedule: day ${report.deliveryDay} in ${report.timeZone}`,
867
+ `Status: ${report.completeness}`,
868
+ ...report.partialLabel ? [`Note: ${report.partialLabel}`] : [],
869
+ `Ready tasks: ${report.readyTaskCount}`,
870
+ `Research tasks: ${report.researchTaskCount}`,
871
+ `Rejected or filtered findings: ${report.rejectedFindingCount}`,
872
+ `Legacy report: ${report.legacy}`,
873
+ "",
874
+ ...report.opportunities.flatMap((item, index) => [
875
+ `${index + 1}. ${item.title}`,
876
+ ` Decision: ${item.task.decision}`,
877
+ ` Format: ${item.task.primaryFormat}`,
878
+ ` Objective: ${item.task.objective}`,
879
+ ` Next step: ${item.task.nextStep}`,
880
+ ` Topic relevance: ${item.confidence.topicRelevance.label}${item.confidence.topicRelevance.score === null ? "" : ` (${item.confidence.topicRelevance.score}/100)`}`,
881
+ ` Action readiness: ${item.confidence.actionReadiness.label}${item.confidence.actionReadiness.score === null ? "" : ` (${item.confidence.actionReadiness.score}/100)`}`
882
+ ]),
883
+ ...report.rejectedFindings.flatMap((item, index) => [
884
+ `Rejected ${index + 1}: ${item.query}`,
885
+ ` Reason: ${item.reason}`
886
+ ])
887
+ ].join("\n");
888
+ }
819
889
 
820
890
  // src/cli.ts
821
891
  var ROLINO_CLI_VERSION = package_default.version;
@@ -848,6 +918,21 @@ function postStatus(value) {
848
918
  `Status must be one of ${PostStatusSchema.options.join(", ")}.`
849
919
  );
850
920
  }
921
+ function seoOpportunityKind(value) {
922
+ const parsed = SeoOpportunityKindSchema.safeParse(value.toUpperCase());
923
+ if (parsed.success) return parsed.data;
924
+ throw new InvalidArgumentError(`SEO kind must be one of ${SeoOpportunityKindSchema.options.join(", ")}.`);
925
+ }
926
+ function seoExpectedImpact(value) {
927
+ const parsed = SeoExpectedImpactSchema.safeParse(value.toUpperCase());
928
+ if (parsed.success) return parsed.data;
929
+ throw new InvalidArgumentError("SEO impact must be HIGH, MEDIUM, or LOW.");
930
+ }
931
+ function seoReportCompleteness(value) {
932
+ const parsed = SeoReportCompletenessSchema.safeParse(value.toUpperCase());
933
+ if (parsed.success) return parsed.data;
934
+ throw new InvalidArgumentError("Report status must be COMPLETE or PARTIAL.");
935
+ }
851
936
  function projectType(value) {
852
937
  const parsed = ProjectTypeSchema.safeParse(value.toUpperCase());
853
938
  if (parsed.success) return parsed.data;
@@ -1865,6 +1950,274 @@ async function runCli(argv = process.argv, overrides = {}) {
1865
1950
  }
1866
1951
  });
1867
1952
  });
1953
+ const blog = program.command("blog").description("Read and manage Blog Studio");
1954
+ const blogPublishing = blog.command("destinations").description("Inspect Blog publishing providers, destinations, and delivery attempts");
1955
+ blogPublishing.command("providers").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
1956
+ const global = program.opts();
1957
+ commandExitCode = await execute({ command: "blog destinations providers", global, runtime, async action(context, client) {
1958
+ const data = await client.blog.publishing.providers(local.project, { requestId: context.requestId });
1959
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
1960
+ } });
1961
+ });
1962
+ blogPublishing.command("list").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
1963
+ const global = program.opts();
1964
+ commandExitCode = await execute({ command: "blog destinations list", global, runtime, async action(context, client) {
1965
+ const data = await client.blog.publishing.destinations(local.project, { requestId: context.requestId });
1966
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
1967
+ } });
1968
+ });
1969
+ blogPublishing.command("deliveries").requiredOption("--project <project-id>", "exact Rolino project ID").option("--article <article-id>", "filter by exact Blog article ID").action(async (local) => {
1970
+ const global = program.opts();
1971
+ commandExitCode = await execute({ command: "blog destinations deliveries", global, runtime, async action(context, client) {
1972
+ const data = await client.blog.publishing.deliveries(local.project, local.article, { requestId: context.requestId });
1973
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
1974
+ } });
1975
+ });
1976
+ const blogSetup = blog.command("setup").description("Inspect agent-first Blog setup readiness");
1977
+ blogSetup.command("status").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
1978
+ const global = program.opts();
1979
+ commandExitCode = await execute({ command: "blog setup status", global, runtime, async action(context, client) {
1980
+ const data = await client.blog.setup.status(local.project, { requestId: context.requestId });
1981
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
1982
+ } });
1983
+ });
1984
+ const blogSite = blog.command("site").description("Initialize or retry the Blog website import");
1985
+ blogSite.command("import").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--idempotency-key <key>", "stable retry key").action(async (local) => {
1986
+ const global = program.opts();
1987
+ commandExitCode = await execute({ command: "blog site import", global, runtime, async action(context, client) {
1988
+ const data = await client.blog.site.import(local.project, local.idempotencyKey, { requestId: context.requestId });
1989
+ writeSuccess(context, data, JSON.stringify(data, null, 2), [`rolino blog job ${data.id} --project ${local.project} --agent`]);
1990
+ } });
1991
+ });
1992
+ blogSite.command("retry").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--idempotency-key <key>", "stable retry key").action(async (local) => {
1993
+ const global = program.opts();
1994
+ commandExitCode = await execute({ command: "blog site retry", global, runtime, async action(context, client) {
1995
+ const data = await client.blog.site.retry(local.project, local.idempotencyKey, { requestId: context.requestId });
1996
+ writeSuccess(context, data, JSON.stringify(data, null, 2), [`rolino blog job ${data.id} --project ${local.project} --agent`]);
1997
+ } });
1998
+ });
1999
+ blog.command("plans").description("List Blog Studio plans").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
2000
+ const global = program.opts();
2001
+ commandExitCode = await execute({ command: "blog plans", global, runtime, async action(context, client) {
2002
+ const data = await client.blog.plans.list(local.project, { requestId: context.requestId });
2003
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2004
+ } });
2005
+ });
2006
+ const blogPlan = blog.command("plan").description("Create, retry, and review a Blog cadence plan");
2007
+ blogPlan.command("create").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--start <date>", "local start date as YYYY-MM-DD").requiredOption("--weekdays <days>", "comma-separated weekday names such as mon,wed,fri").option("--time-zone <zone>", "IANA time zone", "UTC").requiredOption("--idempotency-key <key>", "stable retry key").action(async (local) => {
2008
+ const weekdayMap = { sun: 0, mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6 };
2009
+ const weekdays = local.weekdays.split(",").map((day) => weekdayMap[day.trim().toLowerCase()]).filter((day) => day !== void 0);
2010
+ if (!weekdays.length || weekdays.length !== local.weekdays.split(",").length) throw new TypeError("--weekdays must contain comma-separated sun,mon,tue,wed,thu,fri,sat values.");
2011
+ const global = program.opts();
2012
+ commandExitCode = await execute({ command: "blog plan create", global, runtime, async action(context, client) {
2013
+ const data = await client.blog.plans.create(local.project, { startsOn: local.start, weekdays, timeZone: local.timeZone }, local.idempotencyKey, { requestId: context.requestId });
2014
+ writeSuccess(context, data, JSON.stringify(data, null, 2), [`rolino blog job ${data.job.id} --project ${local.project} --agent`]);
2015
+ } });
2016
+ });
2017
+ blogPlan.command("retry").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--plan <plan-id>", "exact Blog plan ID").requiredOption("--idempotency-key <key>", "stable retry key").action(async (local) => {
2018
+ const global = program.opts();
2019
+ commandExitCode = await execute({ command: "blog plan retry", global, runtime, async action(context, client) {
2020
+ const data = await client.blog.plans.retry(local.project, local.plan, local.idempotencyKey, { requestId: context.requestId });
2021
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2022
+ } });
2023
+ });
2024
+ blogPlan.command("items").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--plan <plan-id>", "exact Blog plan ID").action(async (local) => {
2025
+ const global = program.opts();
2026
+ commandExitCode = await execute({ command: "blog plan items", global, runtime, async action(context, client) {
2027
+ const data = await client.blog.plans.items.list(local.project, local.plan, { requestId: context.requestId });
2028
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2029
+ } });
2030
+ });
2031
+ const blogPlanItem = blogPlan.command("item").description("Accept or dismiss a reviewed Blog plan item");
2032
+ for (const state of ["accept", "dismiss"]) {
2033
+ blogPlanItem.command(state).requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--plan <plan-id>", "exact Blog plan ID").requiredOption("--item <item-id>", "exact plan item ID").requiredOption("--expected-state <state>", "state returned by the latest item read").requiredOption("--expected-version <version>", "version returned by the latest item read", Number).requiredOption("--idempotency-key <key>", "stable retry key").action(async (local) => {
2034
+ const global = program.opts();
2035
+ commandExitCode = await execute({ command: `blog plan item ${state}`, global, runtime, async action(context, client) {
2036
+ const data = await client.blog.plans.items.setState(local.project, local.plan, local.item, { state: state === "accept" ? "ACCEPTED" : "DISMISSED", expectedState: local.expectedState, expectedVersion: local.expectedVersion, idempotencyKey: local.idempotencyKey }, { requestId: context.requestId });
2037
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2038
+ } });
2039
+ });
2040
+ }
2041
+ const blogArticle = blog.command("article").description("Create an editable Blog article from an accepted item");
2042
+ blogArticle.command("create").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--plan <plan-id>", "exact Blog plan ID").requiredOption("--item <item-id>", "exact accepted item ID").requiredOption("--expected-version <version>", "version returned by the latest item read", Number).requiredOption("--idempotency-key <key>", "stable retry key").action(async (local) => {
2043
+ const global = program.opts();
2044
+ commandExitCode = await execute({ command: "blog article create", global, runtime, async action(context, client) {
2045
+ const data = await client.blog.plans.items.createArticle(local.project, local.plan, local.item, { expectedState: "ACCEPTED", expectedVersion: local.expectedVersion, idempotencyKey: local.idempotencyKey }, { requestId: context.requestId });
2046
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2047
+ } });
2048
+ });
2049
+ const blogConnection = blog.command("connection").description("Configure and verify the server-only Next.js Blog connection");
2050
+ blogConnection.command("status").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
2051
+ const global = program.opts();
2052
+ commandExitCode = await execute({ command: "blog connection status", global, runtime, async action(context, client) {
2053
+ const data = await client.blog.connection.status(local.project, { requestId: context.requestId });
2054
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2055
+ } });
2056
+ });
2057
+ const addConnectionOptions = (command, execute2) => {
2058
+ command.requiredOption("--project <project-id>", "exact Rolino project ID").option("--endpoint <url>", "same-host signed revalidation endpoint").option("--credential-action <action>", "KEEP, CREATE, ROTATE, or REVOKE", "KEEP").option("--credential-id <id>", "credential to rotate or revoke").option("--secret-action <action>", "KEEP, CREATE, or ROTATE", "KEEP");
2059
+ if (execute2) command.requiredOption("--confirmation-token <token>", "short-lived token returned by preview").requiredOption("--idempotency-key <key>", "stable retry key").option("--yes", "confirm the exact connection change");
2060
+ return command;
2061
+ };
2062
+ addConnectionOptions(blogConnection.command("preview"), false).action(async (local) => {
2063
+ const input = { endpoint: local.endpoint ?? null, credentialAction: local.credentialAction.toUpperCase(), credentialId: local.credentialId ?? null, secretAction: local.secretAction.toUpperCase() };
2064
+ const global = program.opts();
2065
+ commandExitCode = await execute({ command: "blog connection preview", global, runtime, async action(context, client) {
2066
+ const data = await client.blog.connection.preview(local.project, input, { requestId: context.requestId });
2067
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2068
+ } });
2069
+ });
2070
+ addConnectionOptions(blogConnection.command("execute"), true).action(async (local) => {
2071
+ const input = { endpoint: local.endpoint ?? null, credentialAction: local.credentialAction.toUpperCase(), credentialId: local.credentialId ?? null, secretAction: local.secretAction.toUpperCase() };
2072
+ const global = program.opts();
2073
+ commandExitCode = await execute({ command: "blog connection execute", global, runtime, async action(context, client) {
2074
+ await requireWriteConsent({ yes: local.yes, global, runtime, preview: `Apply the previewed Blog connection change for project ${local.project}? One-time secrets may be returned and must stay server-only.` });
2075
+ const data = await client.blog.connection.execute(local.project, { ...input, confirmationToken: local.confirmationToken, idempotencyKey: local.idempotencyKey }, { requestId: context.requestId });
2076
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2077
+ } });
2078
+ });
2079
+ blogConnection.command("test").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--idempotency-key <key>", "stable retry key").action(async (local) => {
2080
+ const global = program.opts();
2081
+ commandExitCode = await execute({ command: "blog connection test", global, runtime, async action(context, client) {
2082
+ const data = await client.blog.connection.test(local.project, local.idempotencyKey, { requestId: context.requestId });
2083
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2084
+ } });
2085
+ });
2086
+ const blogArticles = blog.command("articles").description("Read and edit Blog Studio articles");
2087
+ blogArticles.command("list").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
2088
+ const global = program.opts();
2089
+ commandExitCode = await execute({ command: "blog articles list", global, runtime, async action(context, client) {
2090
+ const data = await client.blog.articles.list(local.project, { requestId: context.requestId });
2091
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2092
+ } });
2093
+ });
2094
+ blogArticles.command("show").argument("<article-id>").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (articleId, local) => {
2095
+ const global = program.opts();
2096
+ commandExitCode = await execute({ command: "blog articles show", global, runtime, async action(context, client) {
2097
+ const data = await client.blog.articles.get(local.project, articleId, { requestId: context.requestId });
2098
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2099
+ } });
2100
+ });
2101
+ blogArticles.command("update").argument("<article-id>").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--input <json-file>", "complete Blog draft JSON file").option("--yes", "confirm the draft save").action(async (articleId, local) => {
2102
+ const global = program.opts();
2103
+ commandExitCode = await execute({ command: "blog articles update", global, runtime, async action(context, client) {
2104
+ const draft = AgentBlogDraftUpdateSchema.parse(JSON.parse(await readFile2(resolve2(runtime.cwd, local.input), "utf8")));
2105
+ await requireWriteConsent({ yes: local.yes, global, runtime, preview: `Save a new immutable Blog draft revision?
2106
+ Project: ${local.project}
2107
+ Article: ${articleId}
2108
+ This will not publish.` });
2109
+ const data = await client.blog.articles.updateDraft(local.project, articleId, draft, { requestId: context.requestId });
2110
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2111
+ } });
2112
+ });
2113
+ blogArticles.command("generate").argument("<article-id>").requiredOption("--project <project-id>", "exact Rolino project ID").option("--idempotency-key <key>", "stable retry key; defaults to request ID").option("--yes", "confirm the generation request").action(async (articleId, local) => {
2114
+ const global = program.opts();
2115
+ commandExitCode = await execute({ command: "blog articles generate", global, runtime, async action(context, client) {
2116
+ await requireWriteConsent({ yes: local.yes, global, runtime, preview: `Queue durable Blog article generation?
2117
+ Project: ${local.project}
2118
+ Article: ${articleId}` });
2119
+ const data = await client.blog.articles.generate(local.project, articleId, local.idempotencyKey ?? context.requestId, { requestId: context.requestId });
2120
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2121
+ } });
2122
+ });
2123
+ blog.command("job").argument("<job-id>").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (jobId, local) => {
2124
+ const global = program.opts();
2125
+ commandExitCode = await execute({ command: "blog job", global, runtime, async action(context, client) {
2126
+ const data = await client.blog.jobs.get(local.project, jobId, { requestId: context.requestId });
2127
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2128
+ } });
2129
+ });
2130
+ const blogPublish = blog.command("publish").description("Preview or execute exact approved Blog publication");
2131
+ blogPublish.command("preview").argument("<article-id>").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--revision <revision-id>", "exact user-approved revision ID").option("--destination <destination-id...>", "exact destination IDs; defaults to the current primary").action(async (articleId, local) => {
2132
+ const global = program.opts();
2133
+ commandExitCode = await execute({ command: "blog publish preview", global, runtime, async action(context, client) {
2134
+ const data = await client.blog.articles.previewPublish(local.project, articleId, local.revision, { requestId: context.requestId }, local.destination);
2135
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2136
+ } });
2137
+ });
2138
+ blogPublish.command("execute").argument("<article-id>").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--revision <revision-id>", "same exact revision used for preview").requiredOption("--confirmation-token <token>", "short-lived token returned by preview").option("--destination <destination-id...>", "same exact destination IDs used for preview").option("--idempotency-key <key>", "stable retry key; defaults to request ID").option("--yes", "confirm publication").action(async (articleId, local) => {
2139
+ const global = program.opts();
2140
+ commandExitCode = await execute({ command: "blog publish execute", global, runtime, async action(context, client) {
2141
+ await requireWriteConsent({ yes: local.yes, global, runtime, preview: `Publish the exact approved Blog revision?
2142
+ Project: ${local.project}
2143
+ Article: ${articleId}
2144
+ Revision: ${local.revision}` });
2145
+ const data = await client.blog.articles.executePublish(local.project, articleId, { revisionId: local.revision, destinationIds: local.destination, confirmationToken: local.confirmationToken, idempotencyKey: local.idempotencyKey ?? context.requestId }, { requestId: context.requestId });
2146
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2147
+ } });
2148
+ });
2149
+ const seo = program.command("seo").description("Read authorized SEO opportunities and weekly reports");
2150
+ const seoOpportunities = seo.command("opportunities").description("Read accepted SEO opportunities");
2151
+ seoOpportunities.command("list").description("List bounded SEO opportunities").requiredOption("--project <project-id>", "exact Rolino project ID").option("--limit <number>", "maximum opportunities to return", (value) => {
2152
+ const parsed = Number(value);
2153
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 50) {
2154
+ throw new InvalidArgumentError("Limit must be an integer from 1 to 50.");
2155
+ }
2156
+ return parsed;
2157
+ }).option("--cursor <cursor>", "continue from an SEO opportunity cursor").option("--kind <kind>", "filter by exact SEO opportunity kind", seoOpportunityKind).option("--impact <impact>", "filter by HIGH, MEDIUM, or LOW impact", seoExpectedImpact).action(async (local) => {
2158
+ const global = program.opts();
2159
+ commandExitCode = await execute({
2160
+ command: "seo opportunities list",
2161
+ global,
2162
+ runtime,
2163
+ async action(context, client) {
2164
+ const data = await client.seo.opportunities.list(local.project, {
2165
+ limit: local.limit,
2166
+ cursor: local.cursor,
2167
+ kind: local.kind,
2168
+ expectedImpact: local.impact
2169
+ }, { requestId: context.requestId });
2170
+ writeSuccess(context, data, formatSeoOpportunities(data), data.items.slice(0, 3).map((item) => `rolino seo opportunities show ${item.id} --project ${local.project} --agent`));
2171
+ }
2172
+ });
2173
+ });
2174
+ seoOpportunities.command("show").description("Show one canonical SEO opportunity task").argument("<opportunity-id>").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (opportunityId, local) => {
2175
+ const global = program.opts();
2176
+ commandExitCode = await execute({
2177
+ command: "seo opportunities show",
2178
+ global,
2179
+ runtime,
2180
+ async action(context, client) {
2181
+ const opportunity = await client.seo.opportunities.get(local.project, opportunityId, { requestId: context.requestId });
2182
+ writeSuccess(context, opportunity, formatSeoOpportunity(opportunity));
2183
+ }
2184
+ });
2185
+ });
2186
+ const seoReports = seo.command("reports").description("Read immutable weekly SEO report snapshots");
2187
+ seoReports.command("list").description("List bounded weekly SEO reports").requiredOption("--project <project-id>", "exact Rolino project ID").option("--limit <number>", "maximum reports to return", (value) => {
2188
+ const parsed = Number(value);
2189
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 26) {
2190
+ throw new InvalidArgumentError("Limit must be an integer from 1 to 26.");
2191
+ }
2192
+ return parsed;
2193
+ }).option("--cursor <cursor>", "continue from an SEO report cursor").option("--status <status>", "filter by COMPLETE or PARTIAL", seoReportCompleteness).action(async (local) => {
2194
+ const global = program.opts();
2195
+ commandExitCode = await execute({
2196
+ command: "seo reports list",
2197
+ global,
2198
+ runtime,
2199
+ async action(context, client) {
2200
+ const data = await client.seo.reports.list(local.project, {
2201
+ limit: local.limit,
2202
+ cursor: local.cursor,
2203
+ completeness: local.status
2204
+ }, { requestId: context.requestId });
2205
+ writeSuccess(context, data, formatSeoReports(data), data.items.slice(0, 3).map((item) => `rolino seo reports show ${item.id} --project ${local.project} --agent`));
2206
+ }
2207
+ });
2208
+ });
2209
+ seoReports.command("show").description("Show one weekly SEO report snapshot").argument("<report-id>").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (reportId, local) => {
2210
+ const global = program.opts();
2211
+ commandExitCode = await execute({
2212
+ command: "seo reports show",
2213
+ global,
2214
+ runtime,
2215
+ async action(context, client) {
2216
+ const report = await client.seo.reports.get(local.project, reportId, { requestId: context.requestId });
2217
+ writeSuccess(context, report, formatSeoReport(report));
2218
+ }
2219
+ });
2220
+ });
1868
2221
  program.command("doctor").description("Check configuration, compatibility, and authentication").action(async () => {
1869
2222
  const global = program.opts();
1870
2223
  commandExitCode = await execute({
@@ -1958,4 +2311,4 @@ export {
1958
2311
  ROLINO_CLI_VERSION,
1959
2312
  runCli
1960
2313
  };
1961
- //# sourceMappingURL=chunk-VQOMONSS.js.map
2314
+ //# sourceMappingURL=chunk-TFNGSKT3.js.map