@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/CHANGELOG.md +39 -0
- package/README.md +79 -23
- package/dist/bin.cjs +358 -9
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-VQOMONSS.js → chunk-TFNGSKT3.js} +364 -11
- package/dist/chunk-TFNGSKT3.js.map +1 -0
- package/dist/index.cjs +358 -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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# @rolino/cli
|
|
2
2
|
|
|
3
|
+
## 0.5.0-beta.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 4f9e790: Add the optional `blog:manage` capability and synchronized agent-first Blog
|
|
8
|
+
setup, import, cadence planning, plan review, article creation, confirmed
|
|
9
|
+
connection provisioning, and safe verification operations.
|
|
10
|
+
- 1edd1e7: Add published Blog Studio delivery contracts and the server-only Next.js 16 Blog Studio package.
|
|
11
|
+
- 74a13ea: Expose immediate versus scheduled delivery and destination lifecycle stages,
|
|
12
|
+
preserve YouTube visibility for immediate publishing, and clarify remote
|
|
13
|
+
schedule confirmation across public clients.
|
|
14
|
+
- 4a893dd: Add provider-neutral Blog publishing destinations, safe delivery inspection,
|
|
15
|
+
and exact destination binding across public agent interfaces.
|
|
16
|
+
- 843a1db: Add the exact optional `seo:read` capability and bounded read-only SEO
|
|
17
|
+
opportunity and weekly-report parity across public agent contracts, clients,
|
|
18
|
+
CLI commands, and closed-world MCP tools. SEO reads now use the version 3 task
|
|
19
|
+
contract with the exact Ready, Research, or Rejected decision, selected format,
|
|
20
|
+
complete deliverables, bounded evidence, safe URLs, limitations, and no-publish
|
|
21
|
+
rules. Public output uses readable topic names and separate topic-relevance and
|
|
22
|
+
action-readiness confidence. Research tasks compare all supported formats and
|
|
23
|
+
must validate or reject the finding before a separate draft action is allowed.
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- Updated dependencies [4f9e790]
|
|
28
|
+
- Updated dependencies [1edd1e7]
|
|
29
|
+
- Updated dependencies [74a13ea]
|
|
30
|
+
- Updated dependencies [4a893dd]
|
|
31
|
+
- Updated dependencies [843a1db]
|
|
32
|
+
- @rolino/contracts@0.5.0-beta.0
|
|
33
|
+
- @rolino/sdk@0.5.0-beta.0
|
|
34
|
+
- @rolino/local-auth@0.5.0-beta.0
|
|
35
|
+
|
|
36
|
+
## Unreleased
|
|
37
|
+
|
|
38
|
+
### Minor Changes
|
|
39
|
+
|
|
40
|
+
- Add Blog destination discovery, delivery inspection, and exact destination selection for publication commands.
|
|
41
|
+
|
|
3
42
|
## 0.4.0
|
|
4
43
|
|
|
5
44
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -51,16 +51,34 @@ rolino posts publish execute POST_ID --project PROJECT_ID --expected-version VER
|
|
|
51
51
|
rolino integrations health --project PROJECT_ID
|
|
52
52
|
rolino integrations delivery-options --project PROJECT_ID --provider TIKTOK|LINKEDIN
|
|
53
53
|
rolino calendar list --project PROJECT_ID [--from ISO_DATE --to ISO_DATE] [--limit 50] [--cursor CURSOR]
|
|
54
|
-
rolino
|
|
54
|
+
rolino seo opportunities list --project PROJECT_ID [--limit 20] [--cursor CURSOR] [--kind KIND] [--impact HIGH|MEDIUM|LOW]
|
|
55
|
+
rolino seo opportunities show OPPORTUNITY_ID --project PROJECT_ID
|
|
56
|
+
rolino seo reports list --project PROJECT_ID [--limit 10] [--cursor CURSOR] [--status COMPLETE|PARTIAL]
|
|
57
|
+
rolino seo reports show REPORT_ID --project PROJECT_ID
|
|
58
|
+
rolino blog destinations providers --project PROJECT_ID
|
|
59
|
+
rolino blog destinations list --project PROJECT_ID
|
|
60
|
+
rolino blog destinations deliveries --project PROJECT_ID [--article ARTICLE_ID]
|
|
61
|
+
rolino doctor
|
|
55
62
|
```
|
|
56
63
|
|
|
57
64
|
Interactive login opens the configured Rolino instance in a browser, obtains
|
|
58
65
|
explicit workspace approval, and exchanges a single-use code with PKCE S256.
|
|
59
66
|
The approval screen always includes identity and workspace reads and lets the
|
|
60
67
|
user independently opt into project creation, draft writing, confirmed
|
|
61
|
-
scheduling,
|
|
68
|
+
scheduling, confirmed immediate publishing, and read-only SEO Intelligence.
|
|
69
|
+
The SEO option explains that it permits reading opportunities, evidence,
|
|
70
|
+
complete tasks, and weekly reports. It cannot run analysis or change SEO settings.
|
|
71
|
+
Opportunity detail prints the exact decision, selected format, target,
|
|
72
|
+
deliverables, research, safe URLs, separate topic-relevance and action-readiness
|
|
73
|
+
confidence, limitations, and no-publish rules. A Ready
|
|
74
|
+
task can request complete final deliverables for review. A Research task must
|
|
75
|
+
select only `RESEARCH_TASK`, compare all reasonable formats without a preferred
|
|
76
|
+
final format, and return `VALIDATED` with a complete next task or `REJECTED`
|
|
77
|
+
with evidence. It cannot create a final draft in the same action.
|
|
78
|
+
Optional permissions start off,
|
|
62
79
|
and existing credentials never gain a newly selected permission. Revoke and
|
|
63
|
-
reauthorize an existing connection to grant project creation
|
|
80
|
+
reauthorize an existing connection to grant project creation or `seo:read`.
|
|
81
|
+
Existing credentials never receive `seo:read` automatically. The bearer
|
|
64
82
|
credential never passes through a browser URL. Saved credentials are scoped by
|
|
65
83
|
base URL and stored in the operating-system configuration directory;
|
|
66
84
|
`ROLINO_CONFIG_DIR` overrides that location. `auth logout` revokes the calling
|
|
@@ -107,9 +125,11 @@ rolino posts create --project PROJECT_ID --caption "Launch description" --platfo
|
|
|
107
125
|
```
|
|
108
126
|
|
|
109
127
|
Google may restrict uploads from unaudited API projects to Private. Rolino does
|
|
110
|
-
not infer audit approval from configuration.
|
|
111
|
-
|
|
112
|
-
|
|
128
|
+
not infer audit approval from configuration. `posts publish` preserves the
|
|
129
|
+
chosen Public, Unlisted, or Private visibility and does not create a schedule.
|
|
130
|
+
A future `posts schedule` delivery must target Public; confirmed scheduling
|
|
131
|
+
starts its private resumable upload immediately so processing can finish before
|
|
132
|
+
the target time.
|
|
113
133
|
|
|
114
134
|
TikTok drafts require account-specific choices. Refresh them immediately before
|
|
115
135
|
preparing the draft:
|
|
@@ -134,23 +154,23 @@ images. Use `--bluesky-caption` for a destination-specific override of up to
|
|
|
134
154
|
rolino posts create --project PROJECT_ID --caption "Shared launch" --platform BLUESKY --bluesky-caption "Launch day 🦋" --yes
|
|
135
155
|
```
|
|
136
156
|
|
|
137
|
-
LinkedIn drafts can be text-only, include up to ten stored JPEG/PNG images, or
|
|
138
|
-
contain exactly one stored MP4 without mixing images and video. Native video
|
|
139
|
-
must be 75 KB to 500 MB and 3 seconds to 30 minutes. Refresh the connected
|
|
140
|
-
member and server policy before preparing the draft, and use
|
|
141
|
-
`--linkedin-caption` for a destination override:
|
|
157
|
+
LinkedIn drafts can be text-only, include up to ten stored JPEG/PNG images, or
|
|
158
|
+
contain exactly one stored MP4 without mixing images and video. Native video
|
|
159
|
+
must be 75 KB to 500 MB and 3 seconds to 30 minutes. Refresh the connected
|
|
160
|
+
member and server policy before preparing the draft, and use
|
|
161
|
+
`--linkedin-caption` for a destination override:
|
|
142
162
|
|
|
143
163
|
```text
|
|
144
|
-
rolino integrations delivery-options --project PROJECT_ID --provider LINKEDIN
|
|
145
|
-
rolino posts create --project PROJECT_ID --caption "Shared launch" --platform LINKEDIN --linkedin-caption "Launch day for our LinkedIn network" --media IMAGE_ASSET_ID --yes
|
|
146
|
-
rolino posts create --project PROJECT_ID --caption "Product demo" --platform LINKEDIN --media VIDEO_ASSET_ID --yes
|
|
164
|
+
rolino integrations delivery-options --project PROJECT_ID --provider LINKEDIN
|
|
165
|
+
rolino posts create --project PROJECT_ID --caption "Shared launch" --platform LINKEDIN --linkedin-caption "Launch day for our LinkedIn network" --media IMAGE_ASSET_ID --yes
|
|
166
|
+
rolino posts create --project PROJECT_ID --caption "Product demo" --platform LINKEDIN --media VIDEO_ASSET_ID --yes
|
|
147
167
|
```
|
|
148
168
|
|
|
149
|
-
The connected OAuth member is the author; the developer app's associated
|
|
150
|
-
Company Page is not a publishing target. Native video publish and schedule
|
|
151
|
-
execution remains in preparation until LinkedIn confirms upload and processing.
|
|
152
|
-
If final post creation has an unknown outcome, inspect that member's LinkedIn
|
|
153
|
-
feed before retrying.
|
|
169
|
+
The connected OAuth member is the author; the developer app's associated
|
|
170
|
+
Company Page is not a publishing target. Native video publish and schedule
|
|
171
|
+
execution remains in preparation until LinkedIn confirms upload and processing.
|
|
172
|
+
If final post creation has an unknown outcome, inspect that member's LinkedIn
|
|
173
|
+
feed before retrying.
|
|
154
174
|
|
|
155
175
|
Project creation also requires interactive confirmation or `--yes`. It
|
|
156
176
|
atomically saves a project and its paired brand, but does not start research,
|
|
@@ -170,8 +190,10 @@ server confirmation.
|
|
|
170
190
|
|
|
171
191
|
When YouTube is selected, the preview states whether confirmed execution starts
|
|
172
192
|
or resumes early private preparation. A schedule execution may return
|
|
173
|
-
`PENDING`
|
|
174
|
-
|
|
193
|
+
`PENDING` with phase `REMOTE_SCHEDULE_CONFIRMATION` after the upload completes
|
|
194
|
+
while YouTube acknowledges the requested Public publish time. The video remains
|
|
195
|
+
private, no second publish action is required, and the same idempotency key is
|
|
196
|
+
safe to replay.
|
|
175
197
|
|
|
176
198
|
Immediate publishing uses the same server-enforced pattern. `posts publish
|
|
177
199
|
preview` validates the current version, exact destination set, readiness, live
|
|
@@ -182,8 +204,9 @@ delivery; Rolino's worker performs provider calls and reconciles ambiguous
|
|
|
182
204
|
network results so an unsafe retry cannot duplicate a post. Use
|
|
183
205
|
`ROLINO_CONFIRMATION_TOKEN` for scripts instead of putting the token in process
|
|
184
206
|
arguments. YouTube remains `PREPARING` while its resumable upload and provider
|
|
185
|
-
processing are incomplete
|
|
186
|
-
|
|
207
|
+
processing are incomplete. The preview identifies the delivery as `IMMEDIATE`,
|
|
208
|
+
shows the exact visibility, and confirms that no schedule will be created; only
|
|
209
|
+
provider-confirmed visibility becomes published.
|
|
187
210
|
|
|
188
211
|
The CLI cannot enable YouTube, Bluesky, or LinkedIn, or infer provider approval.
|
|
189
212
|
Those are server-side deployment controls. Self-hosted operators should
|
|
@@ -229,3 +252,36 @@ issues through the repository's
|
|
|
229
252
|
[security policy](https://github.com/deifos/rolino/security/policy).
|
|
230
253
|
|
|
231
254
|
`@rolino/cli` is available under the MIT License.
|
|
255
|
+
## Agent-first Blog Studio setup
|
|
256
|
+
|
|
257
|
+
Authorize the optional `blog:manage` capability separately from Blog reading,
|
|
258
|
+
draft writing, and approved-revision publishing. Existing credentials never
|
|
259
|
+
gain it automatically. Start every setup with:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
rolino --agent blog setup status --project PROJECT_ID
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The `blog site`, `blog plan`, `blog article`, `blog connection`, and
|
|
266
|
+
`blog destinations` command groups mirror the versioned API. Use these safe
|
|
267
|
+
publishing reads before setup or publication:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
rolino --agent blog destinations providers --project PROJECT_ID
|
|
271
|
+
rolino --agent blog destinations list --project PROJECT_ID
|
|
272
|
+
rolino --agent blog destinations deliveries --project PROJECT_ID
|
|
273
|
+
rolino --agent blog destinations deliveries --project PROJECT_ID --article ARTICLE_ID
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The provider catalog requires `blog:manage`. It returns only providers enabled
|
|
277
|
+
for the workspace, safe capabilities, and official documentation. Destination
|
|
278
|
+
and delivery-attempt reads require `blog:read`. They omit draft content,
|
|
279
|
+
prompts, evidence, secrets, and raw provider responses. A returned remote URL
|
|
280
|
+
is not proof that a queued or failed delivery is live.
|
|
281
|
+
|
|
282
|
+
Mutations require a stable idempotency key.
|
|
283
|
+
Connection provisioning also requires preview, explicit confirmation, and
|
|
284
|
+
execute. A first successful execute can return a delivery token or webhook
|
|
285
|
+
secret once; save it only in server-side environment configuration. Replays
|
|
286
|
+
do not reveal it again. Blog publication remains the separate approved exact
|
|
287
|
+
revision preview/execute workflow.
|
package/dist/bin.cjs
CHANGED
|
@@ -34,7 +34,7 @@ var import_commander = require("commander");
|
|
|
34
34
|
// package.json
|
|
35
35
|
var package_default = {
|
|
36
36
|
name: "@rolino/cli",
|
|
37
|
-
version: "0.
|
|
37
|
+
version: "0.5.0-beta.0",
|
|
38
38
|
description: "Agent-friendly command-line interface for Rolino",
|
|
39
39
|
type: "module",
|
|
40
40
|
license: "MIT",
|
|
@@ -91,9 +91,9 @@ var package_default = {
|
|
|
91
91
|
dev: "tsx src/bin.ts"
|
|
92
92
|
},
|
|
93
93
|
dependencies: {
|
|
94
|
-
"@rolino/contracts": "0.
|
|
95
|
-
"@rolino/local-auth": "0.
|
|
96
|
-
"@rolino/sdk": "0.
|
|
94
|
+
"@rolino/contracts": "0.5.0-beta.0",
|
|
95
|
+
"@rolino/local-auth": "0.5.0-beta.0",
|
|
96
|
+
"@rolino/sdk": "0.5.0-beta.0",
|
|
97
97
|
commander: "^15.0.0",
|
|
98
98
|
open: "^11.0.0"
|
|
99
99
|
},
|
|
@@ -464,7 +464,6 @@ function formatPosts(data) {
|
|
|
464
464
|
return ["ID STATUS SCHEDULED CAPTION", ...rows].join("\n");
|
|
465
465
|
}
|
|
466
466
|
function formatPost(post) {
|
|
467
|
-
const queued = post.status === "PUBLISHING";
|
|
468
467
|
return [
|
|
469
468
|
`Post ${post.id}`,
|
|
470
469
|
`Project: ${post.projectId}`,
|
|
@@ -473,11 +472,11 @@ function formatPost(post) {
|
|
|
473
472
|
`Caption: ${post.caption || "-"}`,
|
|
474
473
|
...post.campaign ? [`Campaign: ${post.campaign.title} (${post.campaign.id})`] : [],
|
|
475
474
|
...post.scheduledAt ? [
|
|
476
|
-
|
|
475
|
+
`Scheduled: ${post.scheduledAt}${post.timezone ? ` (${post.timezone})` : ""}`
|
|
477
476
|
] : [],
|
|
478
477
|
...post.publishedAt ? [`Published: ${post.publishedAt}`] : [],
|
|
479
478
|
`Media: ${post.media.length}`,
|
|
480
|
-
`Destinations: ${post.destinations.map((destination) => `${destination.provider}:${
|
|
479
|
+
`Destinations: ${post.destinations.map((destination) => `${destination.provider}:${destination.deliveryMode}:${destination.stage}`).join(", ") || "none"}`,
|
|
481
480
|
...post.providerSettings?.TIKTOK ? [
|
|
482
481
|
`TikTok: ${post.providerSettings.TIKTOK.postMode}, ${post.providerSettings.TIKTOK.privacyLevel ?? "visibility not set"}, settings ${post.providerSettings.TIKTOK.consentedAt ? "reviewed" : "not reviewed"}`
|
|
483
482
|
] : [],
|
|
@@ -488,7 +487,8 @@ function formatPost(post) {
|
|
|
488
487
|
}
|
|
489
488
|
function formatPostSchedulePending(pending) {
|
|
490
489
|
return [
|
|
491
|
-
`YouTube
|
|
490
|
+
`YouTube publish-time confirmation pending for post ${pending.postId}.`,
|
|
491
|
+
"Video upload: complete (the video remains private).",
|
|
492
492
|
pending.message,
|
|
493
493
|
`Mutation: ${pending.mutationId}`
|
|
494
494
|
].join("\n");
|
|
@@ -534,8 +534,14 @@ function formatPostSchedulePreview(preview) {
|
|
|
534
534
|
return [
|
|
535
535
|
`Schedule preview for post ${preview.post.id}`,
|
|
536
536
|
`Current version: ${preview.post.version}`,
|
|
537
|
+
`Delivery mode: ${preview.deliveryMode}`,
|
|
537
538
|
`Publish at: ${preview.schedule.scheduledAt} (${preview.schedule.timezone})`,
|
|
538
539
|
`Destinations: ${preview.destinations.join(", ")}`,
|
|
540
|
+
...preview.youtube ? [
|
|
541
|
+
"YouTube preparation: starts immediately as PRIVATE.",
|
|
542
|
+
`YouTube scheduled visibility: ${preview.youtube.publishVisibility}.`,
|
|
543
|
+
`YouTube confirmation: ${preview.youtube.confirmationMeaning}`
|
|
544
|
+
] : [],
|
|
539
545
|
...preview.providerReconciliation ? [`Provider reconciliation: ${preview.providerReconciliation.message}`] : [],
|
|
540
546
|
`Confirmation expires: ${preview.confirmation.expiresAt}`,
|
|
541
547
|
`Confirmation token: ${preview.confirmation.token}`,
|
|
@@ -547,9 +553,10 @@ function formatPostPublishPreview(preview) {
|
|
|
547
553
|
return [
|
|
548
554
|
`Immediate publish preview for post ${preview.post.id}`,
|
|
549
555
|
`Current version: ${preview.post.version}`,
|
|
556
|
+
`Delivery mode: ${preview.deliveryMode}`,
|
|
550
557
|
`Destinations: ${preview.destinations.join(", ")}`,
|
|
551
558
|
"Effect: confirmed execution queues these destinations for immediate publishing.",
|
|
552
|
-
...preview.
|
|
559
|
+
...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.`] : [],
|
|
553
560
|
`Confirmation expires: ${preview.confirmation.expiresAt}`,
|
|
554
561
|
`Confirmation token: ${preview.confirmation.token}`,
|
|
555
562
|
...attention.length ? ["Readiness notes:", ...attention.map((check) => `${check.status.toUpperCase()} ${check.provider ?? "POST"} ${check.code} ${check.message}`)] : ["All required readiness checks passed."]
|
|
@@ -562,6 +569,65 @@ function formatCalendarEvents(data) {
|
|
|
562
569
|
const rows = data.items.map((event) => `${event.postId} ${event.scheduledAt} ${event.status} ${event.destinations.map((item) => item.provider).join(",") || "-"} ${event.title}`);
|
|
563
570
|
return ["POST ID SCHEDULED STATUS DESTINATIONS TITLE", ...rows].join("\n");
|
|
564
571
|
}
|
|
572
|
+
function formatSeoOpportunities(data) {
|
|
573
|
+
if (!data.items.length) return "No active SEO tasks found.";
|
|
574
|
+
return [
|
|
575
|
+
"ID DECISION FORMAT SCORE TOPIC RELEVANCE ACTION READINESS TITLE",
|
|
576
|
+
...data.items.map((item) => `${item.id} ${item.task.decision} ${item.task.primaryFormat} ${item.score} ${item.confidence.topicRelevance.label} ${item.confidence.actionReadiness.label} ${item.title}`)
|
|
577
|
+
].join("\n");
|
|
578
|
+
}
|
|
579
|
+
function formatSeoOpportunity(opportunity) {
|
|
580
|
+
return [
|
|
581
|
+
`${opportunity.title} (${opportunity.id})`,
|
|
582
|
+
`Project: ${opportunity.projectId}`,
|
|
583
|
+
`Decision: ${opportunity.task.decision}`,
|
|
584
|
+
`Primary format: ${opportunity.task.primaryFormat}`,
|
|
585
|
+
`Objective: ${opportunity.task.objective}`,
|
|
586
|
+
`Next step: ${opportunity.task.nextStep}`,
|
|
587
|
+
`Priority: ${opportunity.score}/100`,
|
|
588
|
+
`Topic relevance: ${opportunity.confidence.topicRelevance.label}${opportunity.confidence.topicRelevance.score === null ? "" : ` (${opportunity.confidence.topicRelevance.score}/100)`}`,
|
|
589
|
+
`Topic relevance explanation: ${opportunity.confidence.topicRelevance.explanation}`,
|
|
590
|
+
`Action readiness: ${opportunity.confidence.actionReadiness.label}${opportunity.confidence.actionReadiness.score === null ? "" : ` (${opportunity.confidence.actionReadiness.score}/100)`}`,
|
|
591
|
+
`Action readiness explanation: ${opportunity.confidence.actionReadiness.explanation}`,
|
|
592
|
+
"",
|
|
593
|
+
opportunity.brief
|
|
594
|
+
].join("\n");
|
|
595
|
+
}
|
|
596
|
+
function formatSeoReports(data) {
|
|
597
|
+
if (!data.items.length) return "No weekly SEO reports found.";
|
|
598
|
+
return [
|
|
599
|
+
"ID PERIOD START PERIOD END STATUS READY RESEARCH REJECTED LEGACY",
|
|
600
|
+
...data.items.map((item) => `${item.id} ${item.periodStart} ${item.periodEnd} ${item.completeness} ${item.readyTaskCount} ${item.researchTaskCount} ${item.rejectedFindingCount} ${item.legacy}`)
|
|
601
|
+
].join("\n");
|
|
602
|
+
}
|
|
603
|
+
function formatSeoReport(report) {
|
|
604
|
+
return [
|
|
605
|
+
`Weekly SEO report ${report.id}`,
|
|
606
|
+
`Project: ${report.projectId}`,
|
|
607
|
+
`Period: ${report.periodStart} to ${report.periodEnd}`,
|
|
608
|
+
`Schedule: day ${report.deliveryDay} in ${report.timeZone}`,
|
|
609
|
+
`Status: ${report.completeness}`,
|
|
610
|
+
...report.partialLabel ? [`Note: ${report.partialLabel}`] : [],
|
|
611
|
+
`Ready tasks: ${report.readyTaskCount}`,
|
|
612
|
+
`Research tasks: ${report.researchTaskCount}`,
|
|
613
|
+
`Rejected or filtered findings: ${report.rejectedFindingCount}`,
|
|
614
|
+
`Legacy report: ${report.legacy}`,
|
|
615
|
+
"",
|
|
616
|
+
...report.opportunities.flatMap((item, index) => [
|
|
617
|
+
`${index + 1}. ${item.title}`,
|
|
618
|
+
` Decision: ${item.task.decision}`,
|
|
619
|
+
` Format: ${item.task.primaryFormat}`,
|
|
620
|
+
` Objective: ${item.task.objective}`,
|
|
621
|
+
` Next step: ${item.task.nextStep}`,
|
|
622
|
+
` Topic relevance: ${item.confidence.topicRelevance.label}${item.confidence.topicRelevance.score === null ? "" : ` (${item.confidence.topicRelevance.score}/100)`}`,
|
|
623
|
+
` Action readiness: ${item.confidence.actionReadiness.label}${item.confidence.actionReadiness.score === null ? "" : ` (${item.confidence.actionReadiness.score}/100)`}`
|
|
624
|
+
]),
|
|
625
|
+
...report.rejectedFindings.flatMap((item, index) => [
|
|
626
|
+
`Rejected ${index + 1}: ${item.query}`,
|
|
627
|
+
` Reason: ${item.reason}`
|
|
628
|
+
])
|
|
629
|
+
].join("\n");
|
|
630
|
+
}
|
|
565
631
|
|
|
566
632
|
// src/mcp-setup.ts
|
|
567
633
|
var import_node_child_process = require("child_process");
|
|
@@ -845,6 +911,21 @@ function postStatus(value) {
|
|
|
845
911
|
`Status must be one of ${import_contracts2.PostStatusSchema.options.join(", ")}.`
|
|
846
912
|
);
|
|
847
913
|
}
|
|
914
|
+
function seoOpportunityKind(value) {
|
|
915
|
+
const parsed = import_contracts2.SeoOpportunityKindSchema.safeParse(value.toUpperCase());
|
|
916
|
+
if (parsed.success) return parsed.data;
|
|
917
|
+
throw new import_commander.InvalidArgumentError(`SEO kind must be one of ${import_contracts2.SeoOpportunityKindSchema.options.join(", ")}.`);
|
|
918
|
+
}
|
|
919
|
+
function seoExpectedImpact(value) {
|
|
920
|
+
const parsed = import_contracts2.SeoExpectedImpactSchema.safeParse(value.toUpperCase());
|
|
921
|
+
if (parsed.success) return parsed.data;
|
|
922
|
+
throw new import_commander.InvalidArgumentError("SEO impact must be HIGH, MEDIUM, or LOW.");
|
|
923
|
+
}
|
|
924
|
+
function seoReportCompleteness(value) {
|
|
925
|
+
const parsed = import_contracts2.SeoReportCompletenessSchema.safeParse(value.toUpperCase());
|
|
926
|
+
if (parsed.success) return parsed.data;
|
|
927
|
+
throw new import_commander.InvalidArgumentError("Report status must be COMPLETE or PARTIAL.");
|
|
928
|
+
}
|
|
848
929
|
function projectType(value) {
|
|
849
930
|
const parsed = import_contracts2.ProjectTypeSchema.safeParse(value.toUpperCase());
|
|
850
931
|
if (parsed.success) return parsed.data;
|
|
@@ -1862,6 +1943,274 @@ async function runCli(argv = process.argv, overrides = {}) {
|
|
|
1862
1943
|
}
|
|
1863
1944
|
});
|
|
1864
1945
|
});
|
|
1946
|
+
const blog = program.command("blog").description("Read and manage Blog Studio");
|
|
1947
|
+
const blogPublishing = blog.command("destinations").description("Inspect Blog publishing providers, destinations, and delivery attempts");
|
|
1948
|
+
blogPublishing.command("providers").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
|
|
1949
|
+
const global = program.opts();
|
|
1950
|
+
commandExitCode = await execute({ command: "blog destinations providers", global, runtime, async action(context, client) {
|
|
1951
|
+
const data = await client.blog.publishing.providers(local.project, { requestId: context.requestId });
|
|
1952
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
1953
|
+
} });
|
|
1954
|
+
});
|
|
1955
|
+
blogPublishing.command("list").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
|
|
1956
|
+
const global = program.opts();
|
|
1957
|
+
commandExitCode = await execute({ command: "blog destinations list", global, runtime, async action(context, client) {
|
|
1958
|
+
const data = await client.blog.publishing.destinations(local.project, { requestId: context.requestId });
|
|
1959
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
1960
|
+
} });
|
|
1961
|
+
});
|
|
1962
|
+
blogPublishing.command("deliveries").requiredOption("--project <project-id>", "exact Rolino project ID").option("--article <article-id>", "filter by exact Blog article ID").action(async (local) => {
|
|
1963
|
+
const global = program.opts();
|
|
1964
|
+
commandExitCode = await execute({ command: "blog destinations deliveries", global, runtime, async action(context, client) {
|
|
1965
|
+
const data = await client.blog.publishing.deliveries(local.project, local.article, { requestId: context.requestId });
|
|
1966
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
1967
|
+
} });
|
|
1968
|
+
});
|
|
1969
|
+
const blogSetup = blog.command("setup").description("Inspect agent-first Blog setup readiness");
|
|
1970
|
+
blogSetup.command("status").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
|
|
1971
|
+
const global = program.opts();
|
|
1972
|
+
commandExitCode = await execute({ command: "blog setup status", global, runtime, async action(context, client) {
|
|
1973
|
+
const data = await client.blog.setup.status(local.project, { requestId: context.requestId });
|
|
1974
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
1975
|
+
} });
|
|
1976
|
+
});
|
|
1977
|
+
const blogSite = blog.command("site").description("Initialize or retry the Blog website import");
|
|
1978
|
+
blogSite.command("import").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--idempotency-key <key>", "stable retry key").action(async (local) => {
|
|
1979
|
+
const global = program.opts();
|
|
1980
|
+
commandExitCode = await execute({ command: "blog site import", global, runtime, async action(context, client) {
|
|
1981
|
+
const data = await client.blog.site.import(local.project, local.idempotencyKey, { requestId: context.requestId });
|
|
1982
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2), [`rolino blog job ${data.id} --project ${local.project} --agent`]);
|
|
1983
|
+
} });
|
|
1984
|
+
});
|
|
1985
|
+
blogSite.command("retry").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 retry", global, runtime, async action(context, client) {
|
|
1988
|
+
const data = await client.blog.site.retry(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
|
+
blog.command("plans").description("List Blog Studio plans").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
|
|
1993
|
+
const global = program.opts();
|
|
1994
|
+
commandExitCode = await execute({ command: "blog plans", global, runtime, async action(context, client) {
|
|
1995
|
+
const data = await client.blog.plans.list(local.project, { requestId: context.requestId });
|
|
1996
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
1997
|
+
} });
|
|
1998
|
+
});
|
|
1999
|
+
const blogPlan = blog.command("plan").description("Create, retry, and review a Blog cadence plan");
|
|
2000
|
+
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) => {
|
|
2001
|
+
const weekdayMap = { sun: 0, mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6 };
|
|
2002
|
+
const weekdays = local.weekdays.split(",").map((day) => weekdayMap[day.trim().toLowerCase()]).filter((day) => day !== void 0);
|
|
2003
|
+
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.");
|
|
2004
|
+
const global = program.opts();
|
|
2005
|
+
commandExitCode = await execute({ command: "blog plan create", global, runtime, async action(context, client) {
|
|
2006
|
+
const data = await client.blog.plans.create(local.project, { startsOn: local.start, weekdays, timeZone: local.timeZone }, local.idempotencyKey, { requestId: context.requestId });
|
|
2007
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2), [`rolino blog job ${data.job.id} --project ${local.project} --agent`]);
|
|
2008
|
+
} });
|
|
2009
|
+
});
|
|
2010
|
+
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) => {
|
|
2011
|
+
const global = program.opts();
|
|
2012
|
+
commandExitCode = await execute({ command: "blog plan retry", global, runtime, async action(context, client) {
|
|
2013
|
+
const data = await client.blog.plans.retry(local.project, local.plan, local.idempotencyKey, { requestId: context.requestId });
|
|
2014
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2015
|
+
} });
|
|
2016
|
+
});
|
|
2017
|
+
blogPlan.command("items").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--plan <plan-id>", "exact Blog plan ID").action(async (local) => {
|
|
2018
|
+
const global = program.opts();
|
|
2019
|
+
commandExitCode = await execute({ command: "blog plan items", global, runtime, async action(context, client) {
|
|
2020
|
+
const data = await client.blog.plans.items.list(local.project, local.plan, { requestId: context.requestId });
|
|
2021
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2022
|
+
} });
|
|
2023
|
+
});
|
|
2024
|
+
const blogPlanItem = blogPlan.command("item").description("Accept or dismiss a reviewed Blog plan item");
|
|
2025
|
+
for (const state of ["accept", "dismiss"]) {
|
|
2026
|
+
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) => {
|
|
2027
|
+
const global = program.opts();
|
|
2028
|
+
commandExitCode = await execute({ command: `blog plan item ${state}`, global, runtime, async action(context, client) {
|
|
2029
|
+
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 });
|
|
2030
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2031
|
+
} });
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
const blogArticle = blog.command("article").description("Create an editable Blog article from an accepted item");
|
|
2035
|
+
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) => {
|
|
2036
|
+
const global = program.opts();
|
|
2037
|
+
commandExitCode = await execute({ command: "blog article create", global, runtime, async action(context, client) {
|
|
2038
|
+
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 });
|
|
2039
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2040
|
+
} });
|
|
2041
|
+
});
|
|
2042
|
+
const blogConnection = blog.command("connection").description("Configure and verify the server-only Next.js Blog connection");
|
|
2043
|
+
blogConnection.command("status").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
|
|
2044
|
+
const global = program.opts();
|
|
2045
|
+
commandExitCode = await execute({ command: "blog connection status", global, runtime, async action(context, client) {
|
|
2046
|
+
const data = await client.blog.connection.status(local.project, { requestId: context.requestId });
|
|
2047
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2048
|
+
} });
|
|
2049
|
+
});
|
|
2050
|
+
const addConnectionOptions = (command, execute2) => {
|
|
2051
|
+
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");
|
|
2052
|
+
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");
|
|
2053
|
+
return command;
|
|
2054
|
+
};
|
|
2055
|
+
addConnectionOptions(blogConnection.command("preview"), false).action(async (local) => {
|
|
2056
|
+
const input = { endpoint: local.endpoint ?? null, credentialAction: local.credentialAction.toUpperCase(), credentialId: local.credentialId ?? null, secretAction: local.secretAction.toUpperCase() };
|
|
2057
|
+
const global = program.opts();
|
|
2058
|
+
commandExitCode = await execute({ command: "blog connection preview", global, runtime, async action(context, client) {
|
|
2059
|
+
const data = await client.blog.connection.preview(local.project, input, { requestId: context.requestId });
|
|
2060
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2061
|
+
} });
|
|
2062
|
+
});
|
|
2063
|
+
addConnectionOptions(blogConnection.command("execute"), true).action(async (local) => {
|
|
2064
|
+
const input = { endpoint: local.endpoint ?? null, credentialAction: local.credentialAction.toUpperCase(), credentialId: local.credentialId ?? null, secretAction: local.secretAction.toUpperCase() };
|
|
2065
|
+
const global = program.opts();
|
|
2066
|
+
commandExitCode = await execute({ command: "blog connection execute", global, runtime, async action(context, client) {
|
|
2067
|
+
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.` });
|
|
2068
|
+
const data = await client.blog.connection.execute(local.project, { ...input, confirmationToken: local.confirmationToken, idempotencyKey: local.idempotencyKey }, { requestId: context.requestId });
|
|
2069
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2070
|
+
} });
|
|
2071
|
+
});
|
|
2072
|
+
blogConnection.command("test").requiredOption("--project <project-id>", "exact Rolino project ID").requiredOption("--idempotency-key <key>", "stable retry key").action(async (local) => {
|
|
2073
|
+
const global = program.opts();
|
|
2074
|
+
commandExitCode = await execute({ command: "blog connection test", global, runtime, async action(context, client) {
|
|
2075
|
+
const data = await client.blog.connection.test(local.project, local.idempotencyKey, { requestId: context.requestId });
|
|
2076
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2077
|
+
} });
|
|
2078
|
+
});
|
|
2079
|
+
const blogArticles = blog.command("articles").description("Read and edit Blog Studio articles");
|
|
2080
|
+
blogArticles.command("list").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (local) => {
|
|
2081
|
+
const global = program.opts();
|
|
2082
|
+
commandExitCode = await execute({ command: "blog articles list", global, runtime, async action(context, client) {
|
|
2083
|
+
const data = await client.blog.articles.list(local.project, { requestId: context.requestId });
|
|
2084
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2085
|
+
} });
|
|
2086
|
+
});
|
|
2087
|
+
blogArticles.command("show").argument("<article-id>").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (articleId, local) => {
|
|
2088
|
+
const global = program.opts();
|
|
2089
|
+
commandExitCode = await execute({ command: "blog articles show", global, runtime, async action(context, client) {
|
|
2090
|
+
const data = await client.blog.articles.get(local.project, articleId, { requestId: context.requestId });
|
|
2091
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2092
|
+
} });
|
|
2093
|
+
});
|
|
2094
|
+
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) => {
|
|
2095
|
+
const global = program.opts();
|
|
2096
|
+
commandExitCode = await execute({ command: "blog articles update", global, runtime, async action(context, client) {
|
|
2097
|
+
const draft = import_contracts2.AgentBlogDraftUpdateSchema.parse(JSON.parse(await (0, import_promises2.readFile)((0, import_node_path2.resolve)(runtime.cwd, local.input), "utf8")));
|
|
2098
|
+
await requireWriteConsent({ yes: local.yes, global, runtime, preview: `Save a new immutable Blog draft revision?
|
|
2099
|
+
Project: ${local.project}
|
|
2100
|
+
Article: ${articleId}
|
|
2101
|
+
This will not publish.` });
|
|
2102
|
+
const data = await client.blog.articles.updateDraft(local.project, articleId, draft, { requestId: context.requestId });
|
|
2103
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2104
|
+
} });
|
|
2105
|
+
});
|
|
2106
|
+
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) => {
|
|
2107
|
+
const global = program.opts();
|
|
2108
|
+
commandExitCode = await execute({ command: "blog articles generate", global, runtime, async action(context, client) {
|
|
2109
|
+
await requireWriteConsent({ yes: local.yes, global, runtime, preview: `Queue durable Blog article generation?
|
|
2110
|
+
Project: ${local.project}
|
|
2111
|
+
Article: ${articleId}` });
|
|
2112
|
+
const data = await client.blog.articles.generate(local.project, articleId, local.idempotencyKey ?? context.requestId, { requestId: context.requestId });
|
|
2113
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2114
|
+
} });
|
|
2115
|
+
});
|
|
2116
|
+
blog.command("job").argument("<job-id>").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (jobId, local) => {
|
|
2117
|
+
const global = program.opts();
|
|
2118
|
+
commandExitCode = await execute({ command: "blog job", global, runtime, async action(context, client) {
|
|
2119
|
+
const data = await client.blog.jobs.get(local.project, jobId, { requestId: context.requestId });
|
|
2120
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2121
|
+
} });
|
|
2122
|
+
});
|
|
2123
|
+
const blogPublish = blog.command("publish").description("Preview or execute exact approved Blog publication");
|
|
2124
|
+
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) => {
|
|
2125
|
+
const global = program.opts();
|
|
2126
|
+
commandExitCode = await execute({ command: "blog publish preview", global, runtime, async action(context, client) {
|
|
2127
|
+
const data = await client.blog.articles.previewPublish(local.project, articleId, local.revision, { requestId: context.requestId }, local.destination);
|
|
2128
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2129
|
+
} });
|
|
2130
|
+
});
|
|
2131
|
+
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) => {
|
|
2132
|
+
const global = program.opts();
|
|
2133
|
+
commandExitCode = await execute({ command: "blog publish execute", global, runtime, async action(context, client) {
|
|
2134
|
+
await requireWriteConsent({ yes: local.yes, global, runtime, preview: `Publish the exact approved Blog revision?
|
|
2135
|
+
Project: ${local.project}
|
|
2136
|
+
Article: ${articleId}
|
|
2137
|
+
Revision: ${local.revision}` });
|
|
2138
|
+
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 });
|
|
2139
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2140
|
+
} });
|
|
2141
|
+
});
|
|
2142
|
+
const seo = program.command("seo").description("Read authorized SEO opportunities and weekly reports");
|
|
2143
|
+
const seoOpportunities = seo.command("opportunities").description("Read accepted SEO opportunities");
|
|
2144
|
+
seoOpportunities.command("list").description("List bounded SEO opportunities").requiredOption("--project <project-id>", "exact Rolino project ID").option("--limit <number>", "maximum opportunities to return", (value) => {
|
|
2145
|
+
const parsed = Number(value);
|
|
2146
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 50) {
|
|
2147
|
+
throw new import_commander.InvalidArgumentError("Limit must be an integer from 1 to 50.");
|
|
2148
|
+
}
|
|
2149
|
+
return parsed;
|
|
2150
|
+
}).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) => {
|
|
2151
|
+
const global = program.opts();
|
|
2152
|
+
commandExitCode = await execute({
|
|
2153
|
+
command: "seo opportunities list",
|
|
2154
|
+
global,
|
|
2155
|
+
runtime,
|
|
2156
|
+
async action(context, client) {
|
|
2157
|
+
const data = await client.seo.opportunities.list(local.project, {
|
|
2158
|
+
limit: local.limit,
|
|
2159
|
+
cursor: local.cursor,
|
|
2160
|
+
kind: local.kind,
|
|
2161
|
+
expectedImpact: local.impact
|
|
2162
|
+
}, { requestId: context.requestId });
|
|
2163
|
+
writeSuccess(context, data, formatSeoOpportunities(data), data.items.slice(0, 3).map((item) => `rolino seo opportunities show ${item.id} --project ${local.project} --agent`));
|
|
2164
|
+
}
|
|
2165
|
+
});
|
|
2166
|
+
});
|
|
2167
|
+
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) => {
|
|
2168
|
+
const global = program.opts();
|
|
2169
|
+
commandExitCode = await execute({
|
|
2170
|
+
command: "seo opportunities show",
|
|
2171
|
+
global,
|
|
2172
|
+
runtime,
|
|
2173
|
+
async action(context, client) {
|
|
2174
|
+
const opportunity = await client.seo.opportunities.get(local.project, opportunityId, { requestId: context.requestId });
|
|
2175
|
+
writeSuccess(context, opportunity, formatSeoOpportunity(opportunity));
|
|
2176
|
+
}
|
|
2177
|
+
});
|
|
2178
|
+
});
|
|
2179
|
+
const seoReports = seo.command("reports").description("Read immutable weekly SEO report snapshots");
|
|
2180
|
+
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) => {
|
|
2181
|
+
const parsed = Number(value);
|
|
2182
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 26) {
|
|
2183
|
+
throw new import_commander.InvalidArgumentError("Limit must be an integer from 1 to 26.");
|
|
2184
|
+
}
|
|
2185
|
+
return parsed;
|
|
2186
|
+
}).option("--cursor <cursor>", "continue from an SEO report cursor").option("--status <status>", "filter by COMPLETE or PARTIAL", seoReportCompleteness).action(async (local) => {
|
|
2187
|
+
const global = program.opts();
|
|
2188
|
+
commandExitCode = await execute({
|
|
2189
|
+
command: "seo reports list",
|
|
2190
|
+
global,
|
|
2191
|
+
runtime,
|
|
2192
|
+
async action(context, client) {
|
|
2193
|
+
const data = await client.seo.reports.list(local.project, {
|
|
2194
|
+
limit: local.limit,
|
|
2195
|
+
cursor: local.cursor,
|
|
2196
|
+
completeness: local.status
|
|
2197
|
+
}, { requestId: context.requestId });
|
|
2198
|
+
writeSuccess(context, data, formatSeoReports(data), data.items.slice(0, 3).map((item) => `rolino seo reports show ${item.id} --project ${local.project} --agent`));
|
|
2199
|
+
}
|
|
2200
|
+
});
|
|
2201
|
+
});
|
|
2202
|
+
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) => {
|
|
2203
|
+
const global = program.opts();
|
|
2204
|
+
commandExitCode = await execute({
|
|
2205
|
+
command: "seo reports show",
|
|
2206
|
+
global,
|
|
2207
|
+
runtime,
|
|
2208
|
+
async action(context, client) {
|
|
2209
|
+
const report = await client.seo.reports.get(local.project, reportId, { requestId: context.requestId });
|
|
2210
|
+
writeSuccess(context, report, formatSeoReport(report));
|
|
2211
|
+
}
|
|
2212
|
+
});
|
|
2213
|
+
});
|
|
1865
2214
|
program.command("doctor").description("Check configuration, compatibility, and authentication").action(async () => {
|
|
1866
2215
|
const global = program.opts();
|
|
1867
2216
|
commandExitCode = await execute({
|