@rolino/cli 0.4.0 → 0.5.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/CHANGELOG.md +80 -0
- package/README.md +115 -22
- package/dist/bin.cjs +459 -9
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-VQOMONSS.js → chunk-MBZL2CK6.js} +465 -11
- package/dist/chunk-MBZL2CK6.js.map +1 -0
- package/dist/index.cjs +459 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +4 -4
- package/dist/chunk-VQOMONSS.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -50,7 +50,7 @@ var import_commander = require("commander");
|
|
|
50
50
|
// package.json
|
|
51
51
|
var package_default = {
|
|
52
52
|
name: "@rolino/cli",
|
|
53
|
-
version: "0.
|
|
53
|
+
version: "0.5.0",
|
|
54
54
|
description: "Agent-friendly command-line interface for Rolino",
|
|
55
55
|
type: "module",
|
|
56
56
|
license: "MIT",
|
|
@@ -107,9 +107,9 @@ var package_default = {
|
|
|
107
107
|
dev: "tsx src/bin.ts"
|
|
108
108
|
},
|
|
109
109
|
dependencies: {
|
|
110
|
-
"@rolino/contracts": "0.
|
|
111
|
-
"@rolino/local-auth": "0.
|
|
112
|
-
"@rolino/sdk": "0.
|
|
110
|
+
"@rolino/contracts": "0.5.0",
|
|
111
|
+
"@rolino/local-auth": "0.5.0",
|
|
112
|
+
"@rolino/sdk": "0.5.0",
|
|
113
113
|
commander: "^15.0.0",
|
|
114
114
|
open: "^11.0.0"
|
|
115
115
|
},
|
|
@@ -480,7 +480,6 @@ function formatPosts(data) {
|
|
|
480
480
|
return ["ID STATUS SCHEDULED CAPTION", ...rows].join("\n");
|
|
481
481
|
}
|
|
482
482
|
function formatPost(post) {
|
|
483
|
-
const queued = post.status === "PUBLISHING";
|
|
484
483
|
return [
|
|
485
484
|
`Post ${post.id}`,
|
|
486
485
|
`Project: ${post.projectId}`,
|
|
@@ -489,11 +488,11 @@ function formatPost(post) {
|
|
|
489
488
|
`Caption: ${post.caption || "-"}`,
|
|
490
489
|
...post.campaign ? [`Campaign: ${post.campaign.title} (${post.campaign.id})`] : [],
|
|
491
490
|
...post.scheduledAt ? [
|
|
492
|
-
|
|
491
|
+
`Scheduled: ${post.scheduledAt}${post.timezone ? ` (${post.timezone})` : ""}`
|
|
493
492
|
] : [],
|
|
494
493
|
...post.publishedAt ? [`Published: ${post.publishedAt}`] : [],
|
|
495
494
|
`Media: ${post.media.length}`,
|
|
496
|
-
`Destinations: ${post.destinations.map((destination) => `${destination.provider}:${
|
|
495
|
+
`Destinations: ${post.destinations.map((destination) => `${destination.provider}:${destination.deliveryMode}:${destination.stage}`).join(", ") || "none"}`,
|
|
497
496
|
...post.providerSettings?.TIKTOK ? [
|
|
498
497
|
`TikTok: ${post.providerSettings.TIKTOK.postMode}, ${post.providerSettings.TIKTOK.privacyLevel ?? "visibility not set"}, settings ${post.providerSettings.TIKTOK.consentedAt ? "reviewed" : "not reviewed"}`
|
|
499
498
|
] : [],
|
|
@@ -504,7 +503,8 @@ function formatPost(post) {
|
|
|
504
503
|
}
|
|
505
504
|
function formatPostSchedulePending(pending) {
|
|
506
505
|
return [
|
|
507
|
-
`YouTube
|
|
506
|
+
`YouTube publish-time confirmation pending for post ${pending.postId}.`,
|
|
507
|
+
"Video upload: complete (the video remains private).",
|
|
508
508
|
pending.message,
|
|
509
509
|
`Mutation: ${pending.mutationId}`
|
|
510
510
|
].join("\n");
|
|
@@ -550,8 +550,14 @@ function formatPostSchedulePreview(preview) {
|
|
|
550
550
|
return [
|
|
551
551
|
`Schedule preview for post ${preview.post.id}`,
|
|
552
552
|
`Current version: ${preview.post.version}`,
|
|
553
|
+
`Delivery mode: ${preview.deliveryMode}`,
|
|
553
554
|
`Publish at: ${preview.schedule.scheduledAt} (${preview.schedule.timezone})`,
|
|
554
555
|
`Destinations: ${preview.destinations.join(", ")}`,
|
|
556
|
+
...preview.youtube ? [
|
|
557
|
+
"YouTube preparation: starts immediately as PRIVATE.",
|
|
558
|
+
`YouTube scheduled visibility: ${preview.youtube.publishVisibility}.`,
|
|
559
|
+
`YouTube confirmation: ${preview.youtube.confirmationMeaning}`
|
|
560
|
+
] : [],
|
|
555
561
|
...preview.providerReconciliation ? [`Provider reconciliation: ${preview.providerReconciliation.message}`] : [],
|
|
556
562
|
`Confirmation expires: ${preview.confirmation.expiresAt}`,
|
|
557
563
|
`Confirmation token: ${preview.confirmation.token}`,
|
|
@@ -563,9 +569,10 @@ function formatPostPublishPreview(preview) {
|
|
|
563
569
|
return [
|
|
564
570
|
`Immediate publish preview for post ${preview.post.id}`,
|
|
565
571
|
`Current version: ${preview.post.version}`,
|
|
572
|
+
`Delivery mode: ${preview.deliveryMode}`,
|
|
566
573
|
`Destinations: ${preview.destinations.join(", ")}`,
|
|
567
574
|
"Effect: confirmed execution queues these destinations for immediate publishing.",
|
|
568
|
-
...preview.
|
|
575
|
+
...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.`] : [],
|
|
569
576
|
`Confirmation expires: ${preview.confirmation.expiresAt}`,
|
|
570
577
|
`Confirmation token: ${preview.confirmation.token}`,
|
|
571
578
|
...attention.length ? ["Readiness notes:", ...attention.map((check) => `${check.status.toUpperCase()} ${check.provider ?? "POST"} ${check.code} ${check.message}`)] : ["All required readiness checks passed."]
|
|
@@ -578,6 +585,65 @@ function formatCalendarEvents(data) {
|
|
|
578
585
|
const rows = data.items.map((event) => `${event.postId} ${event.scheduledAt} ${event.status} ${event.destinations.map((item) => item.provider).join(",") || "-"} ${event.title}`);
|
|
579
586
|
return ["POST ID SCHEDULED STATUS DESTINATIONS TITLE", ...rows].join("\n");
|
|
580
587
|
}
|
|
588
|
+
function formatSeoOpportunities(data) {
|
|
589
|
+
if (!data.items.length) return "No active SEO tasks found.";
|
|
590
|
+
return [
|
|
591
|
+
"ID DECISION FORMAT SCORE TOPIC RELEVANCE ACTION READINESS TITLE",
|
|
592
|
+
...data.items.map((item) => `${item.id} ${item.task.decision} ${item.task.primaryFormat} ${item.score} ${item.confidence.topicRelevance.label} ${item.confidence.actionReadiness.label} ${item.title}`)
|
|
593
|
+
].join("\n");
|
|
594
|
+
}
|
|
595
|
+
function formatSeoOpportunity(opportunity) {
|
|
596
|
+
return [
|
|
597
|
+
`${opportunity.title} (${opportunity.id})`,
|
|
598
|
+
`Project: ${opportunity.projectId}`,
|
|
599
|
+
`Decision: ${opportunity.task.decision}`,
|
|
600
|
+
`Primary format: ${opportunity.task.primaryFormat}`,
|
|
601
|
+
`Objective: ${opportunity.task.objective}`,
|
|
602
|
+
`Next step: ${opportunity.task.nextStep}`,
|
|
603
|
+
`Priority: ${opportunity.score}/100`,
|
|
604
|
+
`Topic relevance: ${opportunity.confidence.topicRelevance.label}${opportunity.confidence.topicRelevance.score === null ? "" : ` (${opportunity.confidence.topicRelevance.score}/100)`}`,
|
|
605
|
+
`Topic relevance explanation: ${opportunity.confidence.topicRelevance.explanation}`,
|
|
606
|
+
`Action readiness: ${opportunity.confidence.actionReadiness.label}${opportunity.confidence.actionReadiness.score === null ? "" : ` (${opportunity.confidence.actionReadiness.score}/100)`}`,
|
|
607
|
+
`Action readiness explanation: ${opportunity.confidence.actionReadiness.explanation}`,
|
|
608
|
+
"",
|
|
609
|
+
opportunity.brief
|
|
610
|
+
].join("\n");
|
|
611
|
+
}
|
|
612
|
+
function formatSeoReports(data) {
|
|
613
|
+
if (!data.items.length) return "No weekly SEO reports found.";
|
|
614
|
+
return [
|
|
615
|
+
"ID PERIOD START PERIOD END STATUS READY RESEARCH REJECTED LEGACY",
|
|
616
|
+
...data.items.map((item) => `${item.id} ${item.periodStart} ${item.periodEnd} ${item.completeness} ${item.readyTaskCount} ${item.researchTaskCount} ${item.rejectedFindingCount} ${item.legacy}`)
|
|
617
|
+
].join("\n");
|
|
618
|
+
}
|
|
619
|
+
function formatSeoReport(report) {
|
|
620
|
+
return [
|
|
621
|
+
`Weekly SEO report ${report.id}`,
|
|
622
|
+
`Project: ${report.projectId}`,
|
|
623
|
+
`Period: ${report.periodStart} to ${report.periodEnd}`,
|
|
624
|
+
`Schedule: day ${report.deliveryDay} in ${report.timeZone}`,
|
|
625
|
+
`Status: ${report.completeness}`,
|
|
626
|
+
...report.partialLabel ? [`Note: ${report.partialLabel}`] : [],
|
|
627
|
+
`Ready tasks: ${report.readyTaskCount}`,
|
|
628
|
+
`Research tasks: ${report.researchTaskCount}`,
|
|
629
|
+
`Rejected or filtered findings: ${report.rejectedFindingCount}`,
|
|
630
|
+
`Legacy report: ${report.legacy}`,
|
|
631
|
+
"",
|
|
632
|
+
...report.opportunities.flatMap((item, index) => [
|
|
633
|
+
`${index + 1}. ${item.title}`,
|
|
634
|
+
` Decision: ${item.task.decision}`,
|
|
635
|
+
` Format: ${item.task.primaryFormat}`,
|
|
636
|
+
` Objective: ${item.task.objective}`,
|
|
637
|
+
` Next step: ${item.task.nextStep}`,
|
|
638
|
+
` Topic relevance: ${item.confidence.topicRelevance.label}${item.confidence.topicRelevance.score === null ? "" : ` (${item.confidence.topicRelevance.score}/100)`}`,
|
|
639
|
+
` Action readiness: ${item.confidence.actionReadiness.label}${item.confidence.actionReadiness.score === null ? "" : ` (${item.confidence.actionReadiness.score}/100)`}`
|
|
640
|
+
]),
|
|
641
|
+
...report.rejectedFindings.flatMap((item, index) => [
|
|
642
|
+
`Rejected ${index + 1}: ${item.query}`,
|
|
643
|
+
` Reason: ${item.reason}`
|
|
644
|
+
])
|
|
645
|
+
].join("\n");
|
|
646
|
+
}
|
|
581
647
|
|
|
582
648
|
// src/mcp-setup.ts
|
|
583
649
|
var import_node_child_process = require("child_process");
|
|
@@ -861,6 +927,21 @@ function postStatus(value) {
|
|
|
861
927
|
`Status must be one of ${import_contracts2.PostStatusSchema.options.join(", ")}.`
|
|
862
928
|
);
|
|
863
929
|
}
|
|
930
|
+
function seoOpportunityKind(value) {
|
|
931
|
+
const parsed = import_contracts2.SeoOpportunityKindSchema.safeParse(value.toUpperCase());
|
|
932
|
+
if (parsed.success) return parsed.data;
|
|
933
|
+
throw new import_commander.InvalidArgumentError(`SEO kind must be one of ${import_contracts2.SeoOpportunityKindSchema.options.join(", ")}.`);
|
|
934
|
+
}
|
|
935
|
+
function seoExpectedImpact(value) {
|
|
936
|
+
const parsed = import_contracts2.SeoExpectedImpactSchema.safeParse(value.toUpperCase());
|
|
937
|
+
if (parsed.success) return parsed.data;
|
|
938
|
+
throw new import_commander.InvalidArgumentError("SEO impact must be HIGH, MEDIUM, or LOW.");
|
|
939
|
+
}
|
|
940
|
+
function seoReportCompleteness(value) {
|
|
941
|
+
const parsed = import_contracts2.SeoReportCompletenessSchema.safeParse(value.toUpperCase());
|
|
942
|
+
if (parsed.success) return parsed.data;
|
|
943
|
+
throw new import_commander.InvalidArgumentError("Report status must be COMPLETE or PARTIAL.");
|
|
944
|
+
}
|
|
864
945
|
function projectType(value) {
|
|
865
946
|
const parsed = import_contracts2.ProjectTypeSchema.safeParse(value.toUpperCase());
|
|
866
947
|
if (parsed.success) return parsed.data;
|
|
@@ -988,6 +1069,10 @@ async function requireWriteConsent(options) {
|
|
|
988
1069
|
);
|
|
989
1070
|
}
|
|
990
1071
|
}
|
|
1072
|
+
function requireBlogExecutionConsent(options) {
|
|
1073
|
+
if (options.yes || resolveFormat(options.global, options.runtime) !== "human") return;
|
|
1074
|
+
throw new TypeError("This Blog execute command requires explicit consent. Review the confirmed operation and pass --yes.");
|
|
1075
|
+
}
|
|
991
1076
|
function formatMcpSetupPreview(result) {
|
|
992
1077
|
return [
|
|
993
1078
|
`Client: ${result.client === "codex" ? "Codex" : "Claude Code"}`,
|
|
@@ -1878,6 +1963,371 @@ async function runCli(argv = process.argv, overrides = {}) {
|
|
|
1878
1963
|
}
|
|
1879
1964
|
});
|
|
1880
1965
|
});
|
|
1966
|
+
const blog = program.command("blog").description("Read and manage Blog Studio");
|
|
1967
|
+
const blogPublishing = blog.command("destinations").description("Inspect Blog publishing providers, destinations, and delivery attempts");
|
|
1968
|
+
blogPublishing.command("providers").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
|
|
1969
|
+
const global = program.opts();
|
|
1970
|
+
commandExitCode = await execute({ command: "blog destinations providers", global, runtime, async action(context, client) {
|
|
1971
|
+
const data = await client.blog.publishing.providers(local.project, { requestId: context.requestId });
|
|
1972
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
1973
|
+
} });
|
|
1974
|
+
});
|
|
1975
|
+
blogPublishing.command("list").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
|
|
1976
|
+
const global = program.opts();
|
|
1977
|
+
commandExitCode = await execute({ command: "blog destinations list", global, runtime, async action(context, client) {
|
|
1978
|
+
const data = await client.blog.publishing.destinations(local.project, { requestId: context.requestId });
|
|
1979
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
1980
|
+
} });
|
|
1981
|
+
});
|
|
1982
|
+
blogPublishing.command("deliveries").requiredOption("--project <project-id>", "exact Rolino project ID").option("--article <article-id>", "filter by exact Blog article ID").action(async (local) => {
|
|
1983
|
+
const global = program.opts();
|
|
1984
|
+
commandExitCode = await execute({ command: "blog destinations deliveries", global, runtime, async action(context, client) {
|
|
1985
|
+
const data = await client.blog.publishing.deliveries(local.project, local.article, { requestId: context.requestId });
|
|
1986
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
1987
|
+
} });
|
|
1988
|
+
});
|
|
1989
|
+
const blogSetup = blog.command("setup").description("Inspect agent-first Blog setup readiness");
|
|
1990
|
+
blogSetup.command("status").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
|
|
1991
|
+
const global = program.opts();
|
|
1992
|
+
commandExitCode = await execute({ command: "blog setup status", global, runtime, async action(context, client) {
|
|
1993
|
+
const data = await client.blog.setup.status(local.project, { requestId: context.requestId });
|
|
1994
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
1995
|
+
} });
|
|
1996
|
+
});
|
|
1997
|
+
const blogSite = blog.command("site").description("Initialize or retry the Blog website import");
|
|
1998
|
+
blogSite.command("import").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--idempotency-key <key>", "stable retry key").action(async (local) => {
|
|
1999
|
+
const global = program.opts();
|
|
2000
|
+
commandExitCode = await execute({ command: "blog site import", global, runtime, async action(context, client) {
|
|
2001
|
+
const data = await client.blog.site.import(local.project, local.idempotencyKey, { requestId: context.requestId });
|
|
2002
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2), [`rolino blog job ${data.id} --project ${local.project} --agent`]);
|
|
2003
|
+
} });
|
|
2004
|
+
});
|
|
2005
|
+
blogSite.command("retry").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--idempotency-key <key>", "stable retry key").action(async (local) => {
|
|
2006
|
+
const global = program.opts();
|
|
2007
|
+
commandExitCode = await execute({ command: "blog site retry", global, runtime, async action(context, client) {
|
|
2008
|
+
const data = await client.blog.site.retry(local.project, local.idempotencyKey, { requestId: context.requestId });
|
|
2009
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2), [`rolino blog job ${data.id} --project ${local.project} --agent`]);
|
|
2010
|
+
} });
|
|
2011
|
+
});
|
|
2012
|
+
blog.command("plans").description("List Blog Studio plans").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
|
|
2013
|
+
const global = program.opts();
|
|
2014
|
+
commandExitCode = await execute({ command: "blog plans", global, runtime, async action(context, client) {
|
|
2015
|
+
const data = await client.blog.plans.list(local.project, { requestId: context.requestId });
|
|
2016
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2017
|
+
} });
|
|
2018
|
+
});
|
|
2019
|
+
const blogPlan = blog.command("plan").description("Create, retry, and review a Blog cadence plan");
|
|
2020
|
+
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) => {
|
|
2021
|
+
const weekdayMap = { sun: 0, mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6 };
|
|
2022
|
+
const weekdays = local.weekdays.split(",").map((day) => weekdayMap[day.trim().toLowerCase()]).filter((day) => day !== void 0);
|
|
2023
|
+
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.");
|
|
2024
|
+
const global = program.opts();
|
|
2025
|
+
commandExitCode = await execute({ command: "blog plan create", global, runtime, async action(context, client) {
|
|
2026
|
+
const data = await client.blog.plans.create(local.project, { startsOn: local.start, weekdays, timeZone: local.timeZone }, local.idempotencyKey, { requestId: context.requestId });
|
|
2027
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2), [`rolino blog job ${data.job.id} --project ${local.project} --agent`]);
|
|
2028
|
+
} });
|
|
2029
|
+
});
|
|
2030
|
+
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) => {
|
|
2031
|
+
const global = program.opts();
|
|
2032
|
+
commandExitCode = await execute({ command: "blog plan retry", global, runtime, async action(context, client) {
|
|
2033
|
+
const data = await client.blog.plans.retry(local.project, local.plan, local.idempotencyKey, { requestId: context.requestId });
|
|
2034
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2035
|
+
} });
|
|
2036
|
+
});
|
|
2037
|
+
blogPlan.command("items").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--plan <plan-id>", "exact Blog plan ID").action(async (local) => {
|
|
2038
|
+
const global = program.opts();
|
|
2039
|
+
commandExitCode = await execute({ command: "blog plan items", global, runtime, async action(context, client) {
|
|
2040
|
+
const data = await client.blog.plans.items.list(local.project, local.plan, { requestId: context.requestId });
|
|
2041
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2042
|
+
} });
|
|
2043
|
+
});
|
|
2044
|
+
const blogPlanItem = blogPlan.command("item").description("Accept or dismiss a reviewed Blog plan item");
|
|
2045
|
+
for (const state of ["accept", "dismiss"]) {
|
|
2046
|
+
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) => {
|
|
2047
|
+
const global = program.opts();
|
|
2048
|
+
commandExitCode = await execute({ command: `blog plan item ${state}`, global, runtime, async action(context, client) {
|
|
2049
|
+
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 });
|
|
2050
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2051
|
+
} });
|
|
2052
|
+
});
|
|
2053
|
+
}
|
|
2054
|
+
const blogArticle = blog.command("article").description("Create an editable Blog article from an accepted item");
|
|
2055
|
+
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) => {
|
|
2056
|
+
const global = program.opts();
|
|
2057
|
+
commandExitCode = await execute({ command: "blog article create", global, runtime, async action(context, client) {
|
|
2058
|
+
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 });
|
|
2059
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2060
|
+
} });
|
|
2061
|
+
});
|
|
2062
|
+
const blogConnection = blog.command("connection").description("Configure and verify the server-only Next.js Blog connection");
|
|
2063
|
+
blogConnection.command("status").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
|
|
2064
|
+
const global = program.opts();
|
|
2065
|
+
commandExitCode = await execute({ command: "blog connection status", global, runtime, async action(context, client) {
|
|
2066
|
+
const data = await client.blog.connection.status(local.project, { requestId: context.requestId });
|
|
2067
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2068
|
+
} });
|
|
2069
|
+
});
|
|
2070
|
+
const addConnectionOptions = (command, execute2) => {
|
|
2071
|
+
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");
|
|
2072
|
+
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");
|
|
2073
|
+
return command;
|
|
2074
|
+
};
|
|
2075
|
+
addConnectionOptions(blogConnection.command("preview"), false).action(async (local) => {
|
|
2076
|
+
const input = { endpoint: local.endpoint ?? null, credentialAction: local.credentialAction.toUpperCase(), credentialId: local.credentialId ?? null, secretAction: local.secretAction.toUpperCase() };
|
|
2077
|
+
const global = program.opts();
|
|
2078
|
+
commandExitCode = await execute({ command: "blog connection preview", global, runtime, async action(context, client) {
|
|
2079
|
+
const data = await client.blog.connection.preview(local.project, input, { requestId: context.requestId });
|
|
2080
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2081
|
+
} });
|
|
2082
|
+
});
|
|
2083
|
+
addConnectionOptions(blogConnection.command("execute"), true).action(async (local) => {
|
|
2084
|
+
const input = { endpoint: local.endpoint ?? null, credentialAction: local.credentialAction.toUpperCase(), credentialId: local.credentialId ?? null, secretAction: local.secretAction.toUpperCase() };
|
|
2085
|
+
const global = program.opts();
|
|
2086
|
+
commandExitCode = await execute({ command: "blog connection execute", global, runtime, async action(context, client) {
|
|
2087
|
+
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.` });
|
|
2088
|
+
const data = await client.blog.connection.execute(local.project, { ...input, confirmationToken: local.confirmationToken, idempotencyKey: local.idempotencyKey }, { requestId: context.requestId });
|
|
2089
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2090
|
+
} });
|
|
2091
|
+
});
|
|
2092
|
+
blogConnection.command("test").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--idempotency-key <key>", "stable retry key").action(async (local) => {
|
|
2093
|
+
const global = program.opts();
|
|
2094
|
+
commandExitCode = await execute({ command: "blog connection test", global, runtime, async action(context, client) {
|
|
2095
|
+
const data = await client.blog.connection.test(local.project, local.idempotencyKey, { requestId: context.requestId });
|
|
2096
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2097
|
+
} });
|
|
2098
|
+
});
|
|
2099
|
+
const blogArticles = blog.command("articles").description("Read and edit Blog Studio articles");
|
|
2100
|
+
blogArticles.command("list").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
|
|
2101
|
+
const global = program.opts();
|
|
2102
|
+
commandExitCode = await execute({ command: "blog articles list", global, runtime, async action(context, client) {
|
|
2103
|
+
const data = await client.blog.articles.list(local.project, { requestId: context.requestId });
|
|
2104
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2105
|
+
} });
|
|
2106
|
+
});
|
|
2107
|
+
blogArticles.command("show").argument("<article-id>").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (articleId, local) => {
|
|
2108
|
+
const global = program.opts();
|
|
2109
|
+
commandExitCode = await execute({ command: "blog articles show", global, runtime, async action(context, client) {
|
|
2110
|
+
const data = await client.blog.articles.get(local.project, articleId, { requestId: context.requestId });
|
|
2111
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2112
|
+
} });
|
|
2113
|
+
});
|
|
2114
|
+
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) => {
|
|
2115
|
+
const global = program.opts();
|
|
2116
|
+
commandExitCode = await execute({ command: "blog articles update", global, runtime, async action(context, client) {
|
|
2117
|
+
const draft = import_contracts2.AgentBlogDraftUpdateSchema.parse(JSON.parse(await (0, import_promises2.readFile)((0, import_node_path2.resolve)(runtime.cwd, local.input), "utf8")));
|
|
2118
|
+
await requireWriteConsent({ yes: local.yes, global, runtime, preview: `Save a new immutable Blog draft revision?
|
|
2119
|
+
Project: ${local.project}
|
|
2120
|
+
Article: ${articleId}
|
|
2121
|
+
This will not publish.` });
|
|
2122
|
+
const data = await client.blog.articles.updateDraft(local.project, articleId, draft, { requestId: context.requestId });
|
|
2123
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2124
|
+
} });
|
|
2125
|
+
});
|
|
2126
|
+
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) => {
|
|
2127
|
+
const global = program.opts();
|
|
2128
|
+
commandExitCode = await execute({ command: "blog articles generate", global, runtime, async action(context, client) {
|
|
2129
|
+
await requireWriteConsent({ yes: local.yes, global, runtime, preview: `Queue durable Blog article generation?
|
|
2130
|
+
Project: ${local.project}
|
|
2131
|
+
Article: ${articleId}` });
|
|
2132
|
+
const data = await client.blog.articles.generate(local.project, articleId, local.idempotencyKey ?? context.requestId, { requestId: context.requestId });
|
|
2133
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2134
|
+
} });
|
|
2135
|
+
});
|
|
2136
|
+
const blogImages = blog.command("images").description("Create and review Blog images with blog:write; cannot approve or publish");
|
|
2137
|
+
blogImages.command("generate").description("Queue one exact-revision featured image with blog:write; cannot approve or publish").argument("<article-id>").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--revision <revision-id>", "exact Blog revision ID").option("--editorial-brief <text>", "bounded visual direction; provider controls stay on the server").option("--idempotency-key <key>", "stable retry key; defaults to request ID").action(async (articleId, local) => {
|
|
2138
|
+
const global = program.opts();
|
|
2139
|
+
commandExitCode = await execute({ command: "blog images generate", global, runtime, async action(context, client) {
|
|
2140
|
+
const data = await client.blog.articles.generateImage(local.project, articleId, { revisionId: local.revision, idempotencyKey: local.idempotencyKey ?? context.requestId, editorialBrief: local.editorialBrief }, { requestId: context.requestId });
|
|
2141
|
+
writeSuccess(context, data, `Blog image generation queued.
|
|
2142
|
+
Image: ${data.image.id}
|
|
2143
|
+
Job: ${data.job.id}`);
|
|
2144
|
+
} });
|
|
2145
|
+
});
|
|
2146
|
+
blogImages.command("upload").description("Upload one JPEG, PNG, or WebP for an exact Blog revision with blog:write; cannot approve or publish").argument("<article-id>").argument("<file-path>").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--revision <revision-id>", "exact Blog revision ID").requiredOption("--alt-text <text>", "reviewable image alt text").action(async (articleId, filePath, local) => {
|
|
2147
|
+
const global = program.opts();
|
|
2148
|
+
commandExitCode = await execute({ command: "blog images upload", global, runtime, async action(context, client) {
|
|
2149
|
+
const absolutePath = (0, import_node_path2.resolve)(runtime.cwd, filePath);
|
|
2150
|
+
const details = await (0, import_promises2.stat)(absolutePath).catch(() => null);
|
|
2151
|
+
if (!details?.isFile()) throw new TypeError("The Blog image path must point to a readable file.");
|
|
2152
|
+
const contentTypes = { ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".png": "image/png", ".webp": "image/webp" };
|
|
2153
|
+
const extension = (0, import_node_path2.extname)(absolutePath).toLowerCase();
|
|
2154
|
+
const contentType = contentTypes[extension];
|
|
2155
|
+
if (!contentType) throw new TypeError("Use a JPEG, PNG, or WebP Blog image.");
|
|
2156
|
+
const body = await (0, import_node_fs2.openAsBlob)(absolutePath, { type: contentType });
|
|
2157
|
+
const data = await client.blog.articles.uploadImage(local.project, articleId, { revisionId: local.revision, fileName: (0, import_node_path2.basename)(absolutePath), contentType, fileSize: details.size, altText: local.altText, body }, { requestId: context.requestId });
|
|
2158
|
+
writeSuccess(context, data, `Blog image uploaded for review.
|
|
2159
|
+
Image: ${data.id}
|
|
2160
|
+
State: ${data.state}`);
|
|
2161
|
+
} });
|
|
2162
|
+
});
|
|
2163
|
+
blogImages.command("review").description("Review one exact Blog image with blog:write; does not approve the article or publish").argument("<article-id>").argument("<image-id>").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--decision <decision>", "APPROVED or REJECTED").requiredOption("--expected-version <version>", "current image version", positiveInteger).option("--alt-text <text>", "final image alt text; required by the server for approval").option("--idempotency-key <key>", "stable retry key; defaults to request ID").action(async (articleId, imageId, local) => {
|
|
2164
|
+
const decision = local.decision.toUpperCase();
|
|
2165
|
+
if (decision !== "APPROVED" && decision !== "REJECTED") throw new TypeError("--decision must be APPROVED or REJECTED.");
|
|
2166
|
+
const global = program.opts();
|
|
2167
|
+
commandExitCode = await execute({ command: "blog images review", global, runtime, async action(context, client) {
|
|
2168
|
+
const data = await client.blog.articles.reviewImage(local.project, articleId, imageId, { decision, expectedVersion: local.expectedVersion, altText: local.altText ?? "", idempotencyKey: local.idempotencyKey ?? context.requestId }, { requestId: context.requestId });
|
|
2169
|
+
writeSuccess(context, data, `Blog image review saved.
|
|
2170
|
+
Image: ${data.id}
|
|
2171
|
+
State: ${data.state}`);
|
|
2172
|
+
} });
|
|
2173
|
+
});
|
|
2174
|
+
const blogApprove = blog.command("approve").description("Approve one exact Blog bundle with blog:approve; cannot publish");
|
|
2175
|
+
blogApprove.command("preview").description("Preview one exact approval bundle with blog:approve; creates a confirmation but cannot publish").argument("<article-id>").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--revision <revision-id>", "exact Blog revision ID").action(async (articleId, local) => {
|
|
2176
|
+
const global = program.opts();
|
|
2177
|
+
commandExitCode = await execute({ command: "blog approve preview", global, runtime, async action(context, client) {
|
|
2178
|
+
const data = await client.blog.articles.previewApproval(local.project, articleId, { revisionId: local.revision }, { requestId: context.requestId });
|
|
2179
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2180
|
+
} });
|
|
2181
|
+
});
|
|
2182
|
+
blogApprove.command("execute").description("Approve the confirmed exact bundle with blog:approve; separate blog:publish access is still required").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").requiredOption("--idempotency-key <key>", "stable retry key").option("--yes", "confirm exact bundle approval").action(async (articleId, local) => {
|
|
2183
|
+
const global = program.opts();
|
|
2184
|
+
commandExitCode = await execute({ command: "blog approve execute", global, runtime, async action(context, client) {
|
|
2185
|
+
requireBlogExecutionConsent({ yes: local.yes, global, runtime });
|
|
2186
|
+
const data = await client.blog.articles.executeApproval(local.project, articleId, { revisionId: local.revision, confirmationToken: local.confirmationToken, idempotencyKey: local.idempotencyKey }, { requestId: context.requestId });
|
|
2187
|
+
writeSuccess(context, data, `Exact Blog bundle approved.
|
|
2188
|
+
Revision: ${data.revisionId}
|
|
2189
|
+
Publishing still requires blog:publish.`);
|
|
2190
|
+
} });
|
|
2191
|
+
});
|
|
2192
|
+
const blogSchedule = blog.command("schedule").description("Schedule exact approved Blog bundles with blog:publish; does not grant approval");
|
|
2193
|
+
const addScheduleOptions = (command, executeSchedule) => {
|
|
2194
|
+
command.argument("<article-id>").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--revision <revision-id>", "exact approved Blog revision ID").requiredOption("--at <iso>", "future ISO date-time with UTC offset").requiredOption("--timezone <iana>", "explicit IANA timezone").option("--destination <destination-id...>", "exact destination IDs; defaults to the current primary");
|
|
2195
|
+
if (executeSchedule) command.requiredOption("--confirmation-token <token>", "short-lived token returned by preview").requiredOption("--idempotency-key <key>", "stable retry key").option("--yes", "confirm future external publication");
|
|
2196
|
+
return command;
|
|
2197
|
+
};
|
|
2198
|
+
addScheduleOptions(blogSchedule.command("preview").description("Preview an exact future Blog schedule with blog:publish; does not publish now"), false).action(async (articleId, local) => {
|
|
2199
|
+
const global = program.opts();
|
|
2200
|
+
commandExitCode = await execute({ command: "blog schedule preview", global, runtime, async action(context, client) {
|
|
2201
|
+
const data = await client.blog.articles.previewSchedule(local.project, articleId, { revisionId: local.revision, scheduledAt: local.at, timezone: local.timezone, destinationIds: local.destination }, { requestId: context.requestId });
|
|
2202
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2203
|
+
} });
|
|
2204
|
+
});
|
|
2205
|
+
addScheduleOptions(blogSchedule.command("execute").description("Execute a confirmed future Blog schedule with blog:publish; causes future external publication"), true).action(async (articleId, local) => {
|
|
2206
|
+
const global = program.opts();
|
|
2207
|
+
commandExitCode = await execute({ command: "blog schedule execute", global, runtime, async action(context, client) {
|
|
2208
|
+
requireBlogExecutionConsent({ yes: local.yes, global, runtime });
|
|
2209
|
+
const data = await client.blog.articles.executeSchedule(local.project, articleId, { revisionId: local.revision, scheduledAt: local.at, timezone: local.timezone, destinationIds: local.destination, confirmationToken: local.confirmationToken, idempotencyKey: local.idempotencyKey }, { requestId: context.requestId });
|
|
2210
|
+
writeSuccess(context, data, `Blog article scheduled.
|
|
2211
|
+
Time: ${data.scheduledAt}
|
|
2212
|
+
Timezone: ${data.timezone}`);
|
|
2213
|
+
} });
|
|
2214
|
+
});
|
|
2215
|
+
const blogScheduleCancel = blogSchedule.command("cancel").description("Cancel one future Blog schedule with blog:publish; never unpublishes a live article");
|
|
2216
|
+
blogScheduleCancel.command("preview").description("Preview exact schedule cancellation with blog:publish; does not unpublish").argument("<article-id>").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (articleId, local) => {
|
|
2217
|
+
const global = program.opts();
|
|
2218
|
+
commandExitCode = await execute({ command: "blog schedule cancel preview", global, runtime, async action(context, client) {
|
|
2219
|
+
const data = await client.blog.articles.previewScheduleCancellation(local.project, articleId, { requestId: context.requestId });
|
|
2220
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2221
|
+
} });
|
|
2222
|
+
});
|
|
2223
|
+
blogScheduleCancel.command("execute").description("Cancel the confirmed future schedule with blog:publish; never unpublishes a live article").argument("<article-id>").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--confirmation-token <token>", "short-lived token returned by preview").requiredOption("--idempotency-key <key>", "stable retry key").option("--yes", "confirm schedule cancellation").action(async (articleId, local) => {
|
|
2224
|
+
const global = program.opts();
|
|
2225
|
+
commandExitCode = await execute({ command: "blog schedule cancel execute", global, runtime, async action(context, client) {
|
|
2226
|
+
requireBlogExecutionConsent({ yes: local.yes, global, runtime });
|
|
2227
|
+
const data = await client.blog.articles.executeScheduleCancellation(local.project, articleId, { confirmationToken: local.confirmationToken, idempotencyKey: local.idempotencyKey }, { requestId: context.requestId });
|
|
2228
|
+
writeSuccess(context, data, `Future Blog schedule canceled.
|
|
2229
|
+
Article: ${data.articleId}
|
|
2230
|
+
A live article was not unpublished.`);
|
|
2231
|
+
} });
|
|
2232
|
+
});
|
|
2233
|
+
blog.command("job").argument("<job-id>").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (jobId, local) => {
|
|
2234
|
+
const global = program.opts();
|
|
2235
|
+
commandExitCode = await execute({ command: "blog job", global, runtime, async action(context, client) {
|
|
2236
|
+
const data = await client.blog.jobs.get(local.project, jobId, { requestId: context.requestId });
|
|
2237
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2238
|
+
} });
|
|
2239
|
+
});
|
|
2240
|
+
const blogPublish = blog.command("publish").description("Preview or execute exact approved Blog publication");
|
|
2241
|
+
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) => {
|
|
2242
|
+
const global = program.opts();
|
|
2243
|
+
commandExitCode = await execute({ command: "blog publish preview", global, runtime, async action(context, client) {
|
|
2244
|
+
const data = await client.blog.articles.previewPublish(local.project, articleId, local.revision, { requestId: context.requestId }, local.destination);
|
|
2245
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2246
|
+
} });
|
|
2247
|
+
});
|
|
2248
|
+
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) => {
|
|
2249
|
+
const global = program.opts();
|
|
2250
|
+
commandExitCode = await execute({ command: "blog publish execute", global, runtime, async action(context, client) {
|
|
2251
|
+
await requireWriteConsent({ yes: local.yes, global, runtime, preview: `Publish the exact approved Blog revision?
|
|
2252
|
+
Project: ${local.project}
|
|
2253
|
+
Article: ${articleId}
|
|
2254
|
+
Revision: ${local.revision}` });
|
|
2255
|
+
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 });
|
|
2256
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2257
|
+
} });
|
|
2258
|
+
});
|
|
2259
|
+
const seo = program.command("seo").description("Read authorized SEO opportunities and weekly reports");
|
|
2260
|
+
const seoOpportunities = seo.command("opportunities").description("Read accepted SEO opportunities");
|
|
2261
|
+
seoOpportunities.command("list").description("List bounded SEO opportunities").requiredOption("--project <project-id>", "exact Rolino project ID").option("--limit <number>", "maximum opportunities to return", (value) => {
|
|
2262
|
+
const parsed = Number(value);
|
|
2263
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 50) {
|
|
2264
|
+
throw new import_commander.InvalidArgumentError("Limit must be an integer from 1 to 50.");
|
|
2265
|
+
}
|
|
2266
|
+
return parsed;
|
|
2267
|
+
}).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) => {
|
|
2268
|
+
const global = program.opts();
|
|
2269
|
+
commandExitCode = await execute({
|
|
2270
|
+
command: "seo opportunities list",
|
|
2271
|
+
global,
|
|
2272
|
+
runtime,
|
|
2273
|
+
async action(context, client) {
|
|
2274
|
+
const data = await client.seo.opportunities.list(local.project, {
|
|
2275
|
+
limit: local.limit,
|
|
2276
|
+
cursor: local.cursor,
|
|
2277
|
+
kind: local.kind,
|
|
2278
|
+
expectedImpact: local.impact
|
|
2279
|
+
}, { requestId: context.requestId });
|
|
2280
|
+
writeSuccess(context, data, formatSeoOpportunities(data), data.items.slice(0, 3).map((item) => `rolino seo opportunities show ${item.id} --project ${local.project} --agent`));
|
|
2281
|
+
}
|
|
2282
|
+
});
|
|
2283
|
+
});
|
|
2284
|
+
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) => {
|
|
2285
|
+
const global = program.opts();
|
|
2286
|
+
commandExitCode = await execute({
|
|
2287
|
+
command: "seo opportunities show",
|
|
2288
|
+
global,
|
|
2289
|
+
runtime,
|
|
2290
|
+
async action(context, client) {
|
|
2291
|
+
const opportunity = await client.seo.opportunities.get(local.project, opportunityId, { requestId: context.requestId });
|
|
2292
|
+
writeSuccess(context, opportunity, formatSeoOpportunity(opportunity));
|
|
2293
|
+
}
|
|
2294
|
+
});
|
|
2295
|
+
});
|
|
2296
|
+
const seoReports = seo.command("reports").description("Read immutable weekly SEO report snapshots");
|
|
2297
|
+
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) => {
|
|
2298
|
+
const parsed = Number(value);
|
|
2299
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 26) {
|
|
2300
|
+
throw new import_commander.InvalidArgumentError("Limit must be an integer from 1 to 26.");
|
|
2301
|
+
}
|
|
2302
|
+
return parsed;
|
|
2303
|
+
}).option("--cursor <cursor>", "continue from an SEO report cursor").option("--status <status>", "filter by COMPLETE or PARTIAL", seoReportCompleteness).action(async (local) => {
|
|
2304
|
+
const global = program.opts();
|
|
2305
|
+
commandExitCode = await execute({
|
|
2306
|
+
command: "seo reports list",
|
|
2307
|
+
global,
|
|
2308
|
+
runtime,
|
|
2309
|
+
async action(context, client) {
|
|
2310
|
+
const data = await client.seo.reports.list(local.project, {
|
|
2311
|
+
limit: local.limit,
|
|
2312
|
+
cursor: local.cursor,
|
|
2313
|
+
completeness: local.status
|
|
2314
|
+
}, { requestId: context.requestId });
|
|
2315
|
+
writeSuccess(context, data, formatSeoReports(data), data.items.slice(0, 3).map((item) => `rolino seo reports show ${item.id} --project ${local.project} --agent`));
|
|
2316
|
+
}
|
|
2317
|
+
});
|
|
2318
|
+
});
|
|
2319
|
+
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) => {
|
|
2320
|
+
const global = program.opts();
|
|
2321
|
+
commandExitCode = await execute({
|
|
2322
|
+
command: "seo reports show",
|
|
2323
|
+
global,
|
|
2324
|
+
runtime,
|
|
2325
|
+
async action(context, client) {
|
|
2326
|
+
const report = await client.seo.reports.get(local.project, reportId, { requestId: context.requestId });
|
|
2327
|
+
writeSuccess(context, report, formatSeoReport(report));
|
|
2328
|
+
}
|
|
2329
|
+
});
|
|
2330
|
+
});
|
|
1881
2331
|
program.command("doctor").description("Check configuration, compatibility, and authentication").action(async () => {
|
|
1882
2332
|
const global = program.opts();
|
|
1883
2333
|
commandExitCode = await execute({
|