@rolino/cli 0.7.0 → 0.8.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 CHANGED
@@ -1,5 +1,23 @@
1
1
  # @rolino/cli
2
2
 
3
+ ## 0.8.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [4e123ef]
8
+ - @rolino/contracts@0.8.0
9
+ - @rolino/sdk@0.8.0
10
+ - @rolino/local-auth@0.8.0
11
+
12
+ ## 0.7.1
13
+
14
+ ### Patch Changes
15
+
16
+ - c340405: Request the complete opt-in capability set during browser authorization so users can grant draft and scheduling access to local agents. Remote MCP setup now uses host-native configuration and standard OAuth client discovery instead of a fixed Rolino-managed client.
17
+ - @rolino/contracts@0.7.1
18
+ - @rolino/sdk@0.7.1
19
+ - @rolino/local-auth@0.7.1
20
+
3
21
  ## 0.7.0
4
22
 
5
23
  ### Minor Changes
package/README.md CHANGED
@@ -116,8 +116,10 @@ page cannot restart the closed loopback listener.
116
116
 
117
117
  `setup mcp` checks `/api/v1/meta` and prefers hosted Streamable HTTP when it is
118
118
  available. Remote setup writes the `/mcp` URL and OAuth mode, does not start a
119
- local MCP process, and does not reuse `rolino auth login`. Run
120
- `codex mcp login rolino` after remote Codex setup. Use `--transport http` to
119
+ local MCP process, and does not reuse `rolino auth login`. For Codex, it leaves
120
+ OAuth client registration and the loopback callback to Codex so Codex can use
121
+ Rolino's advertised Client ID Metadata Document support. Run `codex mcp login
122
+ rolino` after remote Codex setup. Use `--transport http` to
121
123
  require remote MCP or `--transport stdio` to require the local package and its
122
124
  shared CLI OAuth session. The command previews the exact token-free
123
125
  configuration and asks before changing anything. User scope is the default.
@@ -125,8 +127,10 @@ Codex configuration is written to `CODEX_HOME/config.toml` or
125
127
  `~/.codex/config.toml`; Claude Code user scope is applied through its official
126
128
  CLI. Project scope writes `.codex/config.toml` or `.mcp.json` in the current
127
129
  directory. Existing file-backed configuration is backed up with a
128
- `.rolino-backup` suffix. Use `--dry-run` to preview in scripts and `--yes` to
129
- provide explicit non-interactive consent; `--agent` never implies consent.
130
+ `.rolino-backup` suffix. Use `--dry-run` to preview in scripts and `--yes` to
131
+ provide explicit non-interactive consent; `--agent` never implies consent.
132
+ An orphan Rolino-managed marker left by an earlier removal is repaired without
133
+ changing unrelated Codex settings.
130
134
 
131
135
  Draft creation and update never schedule or publish. Updates change only the
132
136
  fields supplied; omitted media, destinations, captions, and provider settings
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.7.0",
37
+ version: "0.8.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.7.0",
95
- "@rolino/local-auth": "0.7.0",
96
- "@rolino/sdk": "0.7.0",
94
+ "@rolino/contracts": "0.8.0",
95
+ "@rolino/local-auth": "0.8.0",
96
+ "@rolino/sdk": "0.8.0",
97
97
  commander: "^15.0.0",
98
98
  open: "^11.0.0"
99
99
  },
@@ -106,24 +106,21 @@ var package_default = {
106
106
  };
107
107
 
108
108
  // src/cli.ts
109
- var import_contracts = require("@rolino/contracts");
109
+ var import_contracts2 = require("@rolino/contracts");
110
110
  var import_sdk = require("@rolino/sdk");
111
111
 
112
112
  // src/browser-login.ts
113
113
  var import_node_crypto = require("crypto");
114
114
  var import_node_http = require("http");
115
+ var import_contracts = require("@rolino/contracts");
115
116
  var import_local_auth = require("@rolino/local-auth");
116
117
  var import_open = __toESM(require("open"), 1);
117
118
  var OAUTH_CALLBACK_PORT = 48391;
118
119
  var OAUTH_CALLBACK_PATH = "/oauth/callback";
119
120
  var OAUTH_TIMEOUT_MS = 5 * 60 * 1e3;
120
- var DEFAULT_SCOPES = [
121
+ var AUTHORIZATION_REQUEST_SCOPES = [
121
122
  "offline_access",
122
- "identity:read",
123
- "projects:read",
124
- "posts:read",
125
- "integrations:read",
126
- "calendar:read"
123
+ ...import_contracts.AGENT_CAPABILITIES
127
124
  ];
128
125
  function escapeHtml(value) {
129
126
  return value.replace(/[&<>"']/g, (character) => ({
@@ -163,7 +160,7 @@ function createBrowserAuthorizationRequest(baseUrl) {
163
160
  url.searchParams.set("response_type", "code");
164
161
  url.searchParams.set("client_id", configuration.clientId);
165
162
  url.searchParams.set("redirect_uri", redirectUri);
166
- url.searchParams.set("scope", DEFAULT_SCOPES.join(" "));
163
+ url.searchParams.set("scope", AUTHORIZATION_REQUEST_SCOPES.join(" "));
167
164
  url.searchParams.set("resource", configuration.resource);
168
165
  url.searchParams.set("state", state);
169
166
  url.searchParams.set("code_challenge", codeChallenge);
@@ -202,7 +199,7 @@ async function exchangeAuthorizationCode(options) {
202
199
  accessTokenExpiresAt: new Date(Date.now() + expiresIn * 1e3).toISOString(),
203
200
  refreshToken: payload.refresh_token,
204
201
  refreshTokenExpiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1e3).toISOString(),
205
- scopes: typeof payload.scope === "string" ? payload.scope.split(/\s+/).filter(Boolean) : [...DEFAULT_SCOPES]
202
+ scopes: typeof payload.scope === "string" ? payload.scope.split(/\s+/).filter(Boolean) : [...AUTHORIZATION_REQUEST_SCOPES]
206
203
  };
207
204
  }
208
205
  async function loginWithBrowser(options) {
@@ -669,13 +666,12 @@ function updateCodexConfig(source, server) {
669
666
  const block = codexBlock(server, newline);
670
667
  const begin = source.indexOf(CODEX_BEGIN);
671
668
  const end = source.indexOf(CODEX_END);
672
- if (begin === -1 !== (end === -1) || begin !== -1 && end < begin) {
673
- throw new TypeError("Codex config contains an incomplete Rolino-managed block.");
674
- }
675
669
  let unmanagedSource = source;
676
- if (begin !== -1) {
670
+ if (begin !== -1 && end !== -1 && end > begin) {
677
671
  const after = end + CODEX_END.length;
678
672
  unmanagedSource = `${source.slice(0, begin)}${source.slice(after)}`;
673
+ } else if (begin !== -1 || end !== -1) {
674
+ unmanagedSource = source.split(/\r?\n/).filter((line) => line.trim() !== CODEX_BEGIN && line.trim() !== CODEX_END).join(newline);
679
675
  }
680
676
  const withoutRolino = removeExistingCodexEntry(unmanagedSource, newline);
681
677
  return `${withoutRolino ? `${withoutRolino}${newline}${newline}` : ""}${block}${newline}`;
@@ -881,40 +877,40 @@ function outputFormat(value) {
881
877
  throw new import_commander.InvalidArgumentError("Output must be human, json, or jsonl.");
882
878
  }
883
879
  function postStatus(value) {
884
- const parsed = import_contracts.PostStatusSchema.safeParse(value.toUpperCase());
880
+ const parsed = import_contracts2.PostStatusSchema.safeParse(value.toUpperCase());
885
881
  if (parsed.success) return parsed.data;
886
882
  throw new import_commander.InvalidArgumentError(
887
- `Status must be one of ${import_contracts.PostStatusSchema.options.join(", ")}.`
883
+ `Status must be one of ${import_contracts2.PostStatusSchema.options.join(", ")}.`
888
884
  );
889
885
  }
890
886
  function seoOpportunityKind(value) {
891
- const parsed = import_contracts.SeoOpportunityKindSchema.safeParse(value.toUpperCase());
887
+ const parsed = import_contracts2.SeoOpportunityKindSchema.safeParse(value.toUpperCase());
892
888
  if (parsed.success) return parsed.data;
893
- throw new import_commander.InvalidArgumentError(`SEO kind must be one of ${import_contracts.SeoOpportunityKindSchema.options.join(", ")}.`);
889
+ throw new import_commander.InvalidArgumentError(`SEO kind must be one of ${import_contracts2.SeoOpportunityKindSchema.options.join(", ")}.`);
894
890
  }
895
891
  function seoExpectedImpact(value) {
896
- const parsed = import_contracts.SeoExpectedImpactSchema.safeParse(value.toUpperCase());
892
+ const parsed = import_contracts2.SeoExpectedImpactSchema.safeParse(value.toUpperCase());
897
893
  if (parsed.success) return parsed.data;
898
894
  throw new import_commander.InvalidArgumentError("SEO impact must be HIGH, MEDIUM, or LOW.");
899
895
  }
900
896
  function seoReportCompleteness(value) {
901
- const parsed = import_contracts.SeoReportCompletenessSchema.safeParse(value.toUpperCase());
897
+ const parsed = import_contracts2.SeoReportCompletenessSchema.safeParse(value.toUpperCase());
902
898
  if (parsed.success) return parsed.data;
903
899
  throw new import_commander.InvalidArgumentError("Report status must be COMPLETE or PARTIAL.");
904
900
  }
905
901
  function projectType(value) {
906
- const parsed = import_contracts.ProjectTypeSchema.safeParse(value.toUpperCase());
902
+ const parsed = import_contracts2.ProjectTypeSchema.safeParse(value.toUpperCase());
907
903
  if (parsed.success) return parsed.data;
908
904
  throw new import_commander.InvalidArgumentError(
909
- `Type must be one of ${import_contracts.ProjectTypeSchema.options.join(", ")}.`
905
+ `Type must be one of ${import_contracts2.ProjectTypeSchema.options.join(", ")}.`
910
906
  );
911
907
  }
912
908
  function publishingProvider(value) {
913
909
  const normalized = value.toUpperCase();
914
- const parsed = import_contracts.PublishingProviderSchema.safeParse(normalized);
910
+ const parsed = import_contracts2.PublishingProviderSchema.safeParse(normalized);
915
911
  if (parsed.success) return parsed.data;
916
912
  throw new import_commander.InvalidArgumentError(
917
- `Platform must be one of ${import_contracts.PublishingProviderSchema.options.join(", ")}.`
913
+ `Platform must be one of ${import_contracts2.PublishingProviderSchema.options.join(", ")}.`
918
914
  );
919
915
  }
920
916
  function collectPublishingProvider(value, previous) {
@@ -925,10 +921,10 @@ function collectString(value, previous) {
925
921
  }
926
922
  function deliveryOptionsProvider(value) {
927
923
  const normalized = value.toUpperCase();
928
- const parsed = import_contracts.ProviderDeliveryOptionsProviderSchema.safeParse(normalized);
924
+ const parsed = import_contracts2.ProviderDeliveryOptionsProviderSchema.safeParse(normalized);
929
925
  if (parsed.success) return parsed.data;
930
926
  throw new import_commander.InvalidArgumentError(
931
- `Provider must be one of ${import_contracts.ProviderDeliveryOptionsProviderSchema.options.join(", ")}.`
927
+ `Provider must be one of ${import_contracts2.ProviderDeliveryOptionsProviderSchema.options.join(", ")}.`
932
928
  );
933
929
  }
934
930
  function tiktokPostMode(value) {
@@ -966,7 +962,7 @@ function yesOrNo(value) {
966
962
  function youtubeSettings(options) {
967
963
  const hasYouTubeOptions = options.youtubeTitle !== void 0 || options.youtubeCategoryId !== void 0 || options.youtubePrivacy !== void 0 || options.youtubeMadeForKids !== void 0 || options.youtubeSyntheticMedia === true || options.youtubeNotifySubscribers === false || (options.youtubeTag?.length ?? 0) > 0;
968
964
  if (!hasYouTubeOptions) return null;
969
- return import_contracts.YouTubePostSettingsSchema.parse({
965
+ return import_contracts2.YouTubePostSettingsSchema.parse({
970
966
  title: options.youtubeTitle,
971
967
  categoryId: options.youtubeCategoryId,
972
968
  privacyStatus: options.youtubePrivacy,
@@ -1155,7 +1151,7 @@ function errorForOutput(error) {
1155
1151
  function tiktokSettings(options) {
1156
1152
  const hasTikTokOptions = options.tiktokMode !== void 0 || options.tiktokVisibility !== void 0 || options.tiktokComments !== void 0 || options.tiktokDuet !== void 0 || options.tiktokStitch !== void 0 || options.tiktokCommercialContent !== void 0 || options.tiktokPromotesOwnBrand !== void 0 || options.tiktokPromotesThirdParty !== void 0 || options.tiktokAiGenerated !== void 0 || options.tiktokCoverTimestampMs !== void 0 || options.tiktokSettingsReviewed === true;
1157
1153
  if (!hasTikTokOptions) return void 0;
1158
- return import_contracts.TikTokDraftSettingsSchema.parse({
1154
+ return import_contracts2.TikTokDraftSettingsSchema.parse({
1159
1155
  postMode: options.tiktokMode,
1160
1156
  privacyLevel: options.tiktokVisibility,
1161
1157
  allowComment: options.tiktokComments,
@@ -1299,7 +1295,7 @@ async function runCli(argv = process.argv, overrides = {}) {
1299
1295
  global,
1300
1296
  runtime,
1301
1297
  async action(context, client) {
1302
- const parsed = import_contracts.ProjectCreateInputSchema.safeParse({
1298
+ const parsed = import_contracts2.ProjectCreateInputSchema.safeParse({
1303
1299
  name: local.name,
1304
1300
  type: local.type,
1305
1301
  websiteUrl: local.website,
@@ -2175,7 +2171,7 @@ This moves eligible editorial dates only. It does not schedule, publish, or unpu
2175
2171
  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) => {
2176
2172
  const global = program.opts();
2177
2173
  commandExitCode = await execute({ command: "blog articles update", global, runtime, async action(context, client) {
2178
- const draft = import_contracts.AgentBlogDraftUpdateSchema.parse(JSON.parse(await (0, import_promises2.readFile)((0, import_node_path2.resolve)(runtime.cwd, local.input), "utf8")));
2174
+ const draft = import_contracts2.AgentBlogDraftUpdateSchema.parse(JSON.parse(await (0, import_promises2.readFile)((0, import_node_path2.resolve)(runtime.cwd, local.input), "utf8")));
2179
2175
  await requireWriteConsent({ yes: local.yes, global, runtime, preview: `Save a new immutable Blog draft revision?
2180
2176
  Project: ${local.project}
2181
2177
  Article: ${articleId}
@@ -2352,7 +2348,7 @@ Revision: ${local.revision}` });
2352
2348
  backlinkProspects.command("list").requiredOption("--project <project-id>").option("--stage <stage>").action(async (local) => {
2353
2349
  const global = program.opts();
2354
2350
  commandExitCode = await execute({ command: "backlinks prospects list", global, runtime, async action(context, client) {
2355
- const stage = local.stage ? import_contracts.BacklinkProspectStageSchema.parse(local.stage.toUpperCase()) : void 0;
2351
+ const stage = local.stage ? import_contracts2.BacklinkProspectStageSchema.parse(local.stage.toUpperCase()) : void 0;
2356
2352
  const data = await client.backlinks.prospects.list(local.project, { stage }, { requestId: context.requestId });
2357
2353
  writeSuccess(context, data, JSON.stringify(data, null, 2));
2358
2354
  } });