@rolino/cli 0.5.0-beta.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/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.5.0-beta.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.5.0-beta.0",
111
- "@rolino/local-auth": "0.5.0-beta.0",
112
- "@rolino/sdk": "0.5.0-beta.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
  },
@@ -1069,6 +1069,10 @@ async function requireWriteConsent(options) {
1069
1069
  );
1070
1070
  }
1071
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
+ }
1072
1076
  function formatMcpSetupPreview(result) {
1073
1077
  return [
1074
1078
  `Client: ${result.client === "codex" ? "Codex" : "Claude Code"}`,
@@ -2129,6 +2133,103 @@ Article: ${articleId}` });
2129
2133
  writeSuccess(context, data, JSON.stringify(data, null, 2));
2130
2134
  } });
2131
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
+ });
2132
2233
  blog.command("job").argument("<job-id>").requiredOption("--project <project-id>", "exact Rolino project ID").action(async (jobId, local) => {
2133
2234
  const global = program.opts();
2134
2235
  commandExitCode = await execute({ command: "blog job", global, runtime, async action(context, client) {