@rolino/cli 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "./chunk-3WBZCTM5.js";
4
+ } from "./chunk-PEJ2WA66.js";
5
5
 
6
6
  // src/bin.ts
7
7
  void runCli().then(
@@ -123,12 +123,18 @@ function removeExistingCodexEntry(source, newline) {
123
123
  return kept.join(newline).trimEnd();
124
124
  }
125
125
  function codexBlock(server, newline) {
126
+ const configuration = server.transport === "http" ? [
127
+ `url = ${tomlString(server.url)}`,
128
+ `auth = ${tomlString(server.auth)}`
129
+ ] : [
130
+ `command = ${tomlString(server.command)}`,
131
+ `args = [${server.args.map(tomlString).join(", ")}]`,
132
+ `env = { ROLINO_URL = ${tomlString(server.env.ROLINO_URL)} }`
133
+ ];
126
134
  return [
127
135
  CODEX_BEGIN,
128
136
  "[mcp_servers.rolino]",
129
- `command = ${tomlString(server.command)}`,
130
- `args = [${server.args.map(tomlString).join(", ")}]`,
131
- `env = { ROLINO_URL = ${tomlString(server.env.ROLINO_URL)} }`,
137
+ ...configuration,
132
138
  CODEX_END
133
139
  ].join(newline);
134
140
  }
@@ -168,7 +174,12 @@ function updateClaudeProjectConfig(source, server) {
168
174
  ...parsed,
169
175
  mcpServers: {
170
176
  ...currentServers,
171
- rolino: { type: "stdio", ...server }
177
+ rolino: server.transport === "http" ? { type: "http", url: server.url } : {
178
+ type: "stdio",
179
+ command: server.command,
180
+ args: server.args,
181
+ env: server.env
182
+ }
172
183
  }
173
184
  }, null, 2)}
174
185
  `;
@@ -200,6 +211,7 @@ async function setupCodex(options, server) {
200
211
  backupPath,
201
212
  changed,
202
213
  dryRun: options.dryRun ?? false,
214
+ transport: server.transport,
203
215
  server
204
216
  };
205
217
  }
@@ -220,6 +232,7 @@ async function setupClaudeCode(options, server) {
220
232
  backupPath,
221
233
  changed,
222
234
  dryRun: options.dryRun ?? false,
235
+ transport: server.transport,
223
236
  server
224
237
  };
225
238
  }
@@ -232,6 +245,7 @@ async function setupClaudeCode(options, server) {
232
245
  backupPath: null,
233
246
  changed: true,
234
247
  dryRun: true,
248
+ transport: server.transport,
235
249
  server
236
250
  };
237
251
  }
@@ -245,7 +259,12 @@ async function setupClaudeCode(options, server) {
245
259
  "mcp",
246
260
  "add-json",
247
261
  "rolino",
248
- JSON.stringify({ type: "stdio", ...server }),
262
+ JSON.stringify(server.transport === "http" ? { type: "http", url: server.url } : {
263
+ type: "stdio",
264
+ command: server.command,
265
+ args: server.args,
266
+ env: server.env
267
+ }),
249
268
  "--scope",
250
269
  "user"
251
270
  ], options);
@@ -261,14 +280,51 @@ async function setupClaudeCode(options, server) {
261
280
  backupPath: null,
262
281
  changed: true,
263
282
  dryRun: false,
283
+ transport: server.transport,
264
284
  server
265
285
  };
266
286
  }
287
+ function remoteMcpUrl(baseUrl) {
288
+ return new URL("mcp", `${baseUrl.replace(/\/$/, "")}/`).toString();
289
+ }
290
+ async function advertisedRemoteMcp(options) {
291
+ const fetchImplementation = options.fetch ?? globalThis.fetch;
292
+ try {
293
+ const response = await fetchImplementation(
294
+ new URL("api/v1/meta", `${options.baseUrl.replace(/\/$/, "")}/`),
295
+ {
296
+ headers: { accept: "application/json" },
297
+ signal: AbortSignal.timeout(5e3)
298
+ }
299
+ );
300
+ if (!response.ok) return false;
301
+ const payload = await response.json();
302
+ return payload.data?.mcp?.streamableHttp === true;
303
+ } catch {
304
+ return false;
305
+ }
306
+ }
307
+ async function resolveTransport(options) {
308
+ const requested = options.transport ?? "stdio";
309
+ if (requested === "stdio") return "stdio";
310
+ if (await advertisedRemoteMcp(options)) return "http";
311
+ if (requested === "http") {
312
+ throw new TypeError(
313
+ "This Rolino instance does not advertise Streamable HTTP MCP. Use --transport stdio, or enable and verify remote MCP on the server."
314
+ );
315
+ }
316
+ return "stdio";
317
+ }
267
318
  async function setupMcp(options) {
268
- const serverPath = await resolveServerPath(options);
269
- const server = {
319
+ const transport = await resolveTransport(options);
320
+ const server = transport === "http" ? {
321
+ transport: "http",
322
+ url: remoteMcpUrl(options.baseUrl),
323
+ auth: "oauth"
324
+ } : {
325
+ transport: "stdio",
270
326
  command: options.nodePath,
271
- args: [serverPath],
327
+ args: [await resolveServerPath(options)],
272
328
  env: { ROLINO_URL: options.baseUrl }
273
329
  };
274
330
  return options.client === "codex" ? setupCodex(options, server) : setupClaudeCode(options, server);
@@ -285,7 +341,7 @@ import { Command, CommanderError, InvalidArgumentError } from "commander";
285
341
  // package.json
286
342
  var package_default = {
287
343
  name: "@rolino/cli",
288
- version: "0.6.0",
344
+ version: "0.7.0",
289
345
  description: "Agent-friendly command-line interface for Rolino",
290
346
  type: "module",
291
347
  license: "MIT",
@@ -342,9 +398,9 @@ var package_default = {
342
398
  dev: "tsx src/bin.ts"
343
399
  },
344
400
  dependencies: {
345
- "@rolino/contracts": "0.6.0",
346
- "@rolino/local-auth": "0.6.0",
347
- "@rolino/sdk": "0.6.0",
401
+ "@rolino/contracts": "0.7.0",
402
+ "@rolino/local-auth": "0.7.0",
403
+ "@rolino/sdk": "0.7.0",
348
404
  commander: "^15.0.0",
349
405
  open: "^11.0.0"
350
406
  },
@@ -360,6 +416,7 @@ var package_default = {
360
416
  import {
361
417
  PostStatusSchema,
362
418
  AgentBlogDraftUpdateSchema,
419
+ BacklinkProspectStageSchema,
363
420
  ProjectCreateInputSchema,
364
421
  ProjectTypeSchema,
365
422
  ProviderDeliveryOptionsProviderSchema,
@@ -943,6 +1000,10 @@ function mcpScope(value) {
943
1000
  if (value === "user" || value === "project") return value;
944
1001
  throw new InvalidArgumentError("MCP setup scope must be user or project.");
945
1002
  }
1003
+ function mcpTransport(value) {
1004
+ if (value === "auto" || value === "http" || value === "stdio") return value;
1005
+ throw new InvalidArgumentError("MCP transport must be auto, http, or stdio.");
1006
+ }
946
1007
  function isoDateTime(value) {
947
1008
  const date = new Date(value);
948
1009
  if (Number.isNaN(date.getTime())) {
@@ -1003,13 +1064,21 @@ function requireBlogExecutionConsent(options) {
1003
1064
  throw new TypeError("This Blog execute command requires explicit consent. Review the confirmed operation and pass --yes.");
1004
1065
  }
1005
1066
  function formatMcpSetupPreview(result) {
1067
+ const connection = result.server.transport === "http" ? [
1068
+ `Transport: Streamable HTTP`,
1069
+ `Endpoint: ${result.server.url}`,
1070
+ "Authentication: OAuth in the MCP client"
1071
+ ] : [
1072
+ "Transport: local STDIO",
1073
+ `Command: ${result.server.command}`,
1074
+ `Arguments: ${result.server.args.join(" ")}`,
1075
+ `Rolino URL: ${result.server.env.ROLINO_URL}`
1076
+ ];
1006
1077
  return [
1007
1078
  `Client: ${result.client === "codex" ? "Codex" : "Claude Code"}`,
1008
1079
  `Scope: ${result.scope}`,
1009
1080
  `Target: ${result.target}`,
1010
- `Command: ${result.server.command}`,
1011
- `Arguments: ${result.server.args.join(" ")}`,
1012
- `Rolino URL: ${result.server.env.ROLINO_URL}`,
1081
+ ...connection,
1013
1082
  "No token will be written to MCP configuration."
1014
1083
  ].join("\n");
1015
1084
  }
@@ -1472,7 +1541,7 @@ async function runCli(argv = process.argv, overrides = {}) {
1472
1541
  });
1473
1542
  });
1474
1543
  const setup = program.command("setup").description("Configure local agent tools for Rolino");
1475
- setup.command("mcp").description("Configure the Rolino stdio MCP server for a supported client").requiredOption("--client <client>", "codex or claude-code", mcpClient).option("--scope <scope>", "user or project", mcpScope, "user").option("--server-path <path>", "absolute or working-directory-relative MCP server path").option("--dry-run", "show the intended configuration without writing it").option("--yes", "apply without an interactive confirmation").option("--force", "replace an existing Claude Code user-scoped Rolino server").action(async (local) => {
1544
+ setup.command("mcp").description("Configure Rolino MCP with Streamable HTTP or local STDIO").requiredOption("--client <client>", "codex or claude-code", mcpClient).option("--scope <scope>", "user or project", mcpScope, "user").option("--transport <transport>", "auto, http, or stdio", mcpTransport, "auto").option("--server-path <path>", "absolute or working-directory-relative MCP server path").option("--dry-run", "show the intended configuration without writing it").option("--yes", "apply without an interactive confirmation").option("--force", "replace an existing Claude Code user-scoped Rolino server").action(async (local) => {
1476
1545
  const global = program.opts();
1477
1546
  commandExitCode = await execute({
1478
1547
  command: "setup mcp",
@@ -1485,7 +1554,8 @@ async function runCli(argv = process.argv, overrides = {}) {
1485
1554
  cwd: runtime.cwd,
1486
1555
  env: runtime.env,
1487
1556
  nodePath: runtime.nodePath,
1488
- cliEntryPath: runtime.cliEntryPath
1557
+ cliEntryPath: runtime.cliEntryPath,
1558
+ fetch: runtime.fetch
1489
1559
  };
1490
1560
  const preview = await setupMcp({ ...setupOptions, dryRun: true });
1491
1561
  let result = preview;
@@ -1512,11 +1582,12 @@ async function runCli(argv = process.argv, overrides = {}) {
1512
1582
  `${state} Rolino MCP for ${clientLabel}.`,
1513
1583
  `Scope: ${result.scope}`,
1514
1584
  `Target: ${result.target}`,
1585
+ `Transport: ${result.transport === "http" ? "Streamable HTTP" : "local STDIO"}`,
1515
1586
  ...result.backupPath && !result.dryRun ? [`Backup: ${result.backupPath}`] : [],
1516
1587
  `Rolino URL: ${client.baseUrl}`,
1517
1588
  "No token was written to MCP configuration."
1518
1589
  ].join("\n"),
1519
- result.client === "codex" ? ["codex mcp list", "rolino auth login"] : ["claude mcp get rolino", "rolino auth login"]
1590
+ result.client === "codex" ? result.transport === "http" ? ["codex mcp login rolino", "codex mcp list"] : ["rolino auth login", "codex mcp list"] : result.transport === "http" ? ["claude mcp get rolino", "Complete OAuth when Claude prompts you"] : ["rolino auth login", "claude mcp get rolino"]
1520
1591
  );
1521
1592
  }
1522
1593
  });
@@ -2255,6 +2326,106 @@ Revision: ${local.revision}` });
2255
2326
  writeSuccess(context, data, JSON.stringify(data, null, 2));
2256
2327
  } });
2257
2328
  });
2329
+ const backlinks = program.command("backlinks").description("Review backlink prospects and public contact drafts. Rolino never sends email.");
2330
+ const backlinkTargets = backlinks.command("targets");
2331
+ backlinkTargets.command("list").requiredOption("--project <project-id>").action(async (local) => {
2332
+ const global = program.opts();
2333
+ commandExitCode = await execute({ command: "backlinks targets list", global, runtime, async action(context, client) {
2334
+ const data = await client.backlinks.targets.list(local.project, { requestId: context.requestId });
2335
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2336
+ } });
2337
+ });
2338
+ backlinkTargets.command("add").requiredOption("--project <project-id>").requiredOption("--url <url>").requiredOption("--label <label>").action(async (local) => {
2339
+ const global = program.opts();
2340
+ commandExitCode = await execute({ command: "backlinks targets add", global, runtime, async action(context, client) {
2341
+ const data = await client.backlinks.targets.add(local.project, { url: local.url, label: local.label }, context.requestId, { requestId: context.requestId });
2342
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2343
+ } });
2344
+ });
2345
+ backlinks.command("discover").requiredOption("--project <project-id>").option("--limit <number>", "maximum saved prospects", Number, 20).option("--idempotency-key <key>").action(async (local) => {
2346
+ const global = program.opts();
2347
+ commandExitCode = await execute({ command: "backlinks discover", global, runtime, async action(context, client) {
2348
+ const data = await client.backlinks.discoveries.start(local.project, { limit: local.limit }, local.idempotencyKey ?? context.requestId, { requestId: context.requestId });
2349
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2350
+ } });
2351
+ });
2352
+ const backlinkRuns = backlinks.command("runs");
2353
+ backlinkRuns.command("get").requiredOption("--project <project-id>").requiredOption("--run <run-id>").action(async (local) => {
2354
+ const global = program.opts();
2355
+ commandExitCode = await execute({ command: "backlinks runs get", global, runtime, async action(context, client) {
2356
+ const data = await client.backlinks.discoveries.get(local.project, local.run, { requestId: context.requestId });
2357
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2358
+ } });
2359
+ });
2360
+ const backlinkProspects = backlinks.command("prospects");
2361
+ backlinkProspects.command("list").requiredOption("--project <project-id>").option("--stage <stage>").action(async (local) => {
2362
+ const global = program.opts();
2363
+ commandExitCode = await execute({ command: "backlinks prospects list", global, runtime, async action(context, client) {
2364
+ const stage = local.stage ? BacklinkProspectStageSchema.parse(local.stage.toUpperCase()) : void 0;
2365
+ const data = await client.backlinks.prospects.list(local.project, { stage }, { requestId: context.requestId });
2366
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2367
+ } });
2368
+ });
2369
+ backlinkProspects.command("get").requiredOption("--project <project-id>").requiredOption("--prospect <prospect-id>").action(async (local) => {
2370
+ const global = program.opts();
2371
+ commandExitCode = await execute({ command: "backlinks prospects get", global, runtime, async action(context, client) {
2372
+ const data = await client.backlinks.prospects.get(local.project, local.prospect, { requestId: context.requestId });
2373
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2374
+ } });
2375
+ });
2376
+ backlinkProspects.command("approve").requiredOption("--project <project-id>").requiredOption("--prospect <prospect-id>").requiredOption("--expected-version <number>", "current optimistic version", Number).action(async (local) => {
2377
+ const global = program.opts();
2378
+ commandExitCode = await execute({ command: "backlinks prospects approve", global, runtime, async action(context, client) {
2379
+ const data = await client.backlinks.prospects.updateStage(local.project, local.prospect, { stage: "APPROVED", expectedVersion: local.expectedVersion }, context.requestId, { requestId: context.requestId });
2380
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2381
+ } });
2382
+ });
2383
+ const backlinkContacts = backlinks.command("contacts");
2384
+ backlinkContacts.command("research").requiredOption("--project <project-id>").requiredOption("--prospect <prospect-id>").action(async (local) => {
2385
+ const global = program.opts();
2386
+ commandExitCode = await execute({ command: "backlinks contacts research", global, runtime, async action(context, client) {
2387
+ const data = await client.backlinks.prospects.researchContact(local.project, local.prospect, context.requestId, { requestId: context.requestId });
2388
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2389
+ } });
2390
+ });
2391
+ backlinkContacts.command("list").requiredOption("--project <project-id>").action(async (local) => {
2392
+ const global = program.opts();
2393
+ commandExitCode = await execute({ command: "backlinks contacts list", global, runtime, async action(context, client) {
2394
+ const data = await client.backlinks.contacts.list(local.project, { limit: 50 }, { requestId: context.requestId });
2395
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2396
+ } });
2397
+ });
2398
+ backlinkContacts.command("export").requiredOption("--project <project-id>").option("--format <format>", "json or csv", "csv").action(async (local) => {
2399
+ const global = program.opts();
2400
+ commandExitCode = await execute({ command: "backlinks contacts export", global, runtime, async action(context, client) {
2401
+ const data = await client.backlinks.contacts.list(local.project, { limit: 50 }, { requestId: context.requestId });
2402
+ if (local.format !== "csv") return writeSuccess(context, data, JSON.stringify(data, null, 2));
2403
+ const cell = (value) => {
2404
+ let text = String(value ?? "").replace(/[\r\n]+/g, " ");
2405
+ if (/^[=+\-@\t]/.test(text)) text = `'${text}`;
2406
+ return `"${text.replaceAll('"', '""')}"`;
2407
+ };
2408
+ const csv = ["prospectId,name,role,email,sourceUrl,checkedAt", ...data.items.map((item) => [item.prospectId, item.name, item.role, item.email, item.sourceUrl, item.checkedAt].map(cell).join(","))].join("\n");
2409
+ context.stdout.write(`${csv}
2410
+ `);
2411
+ } });
2412
+ });
2413
+ const backlinkOutreach = backlinks.command("outreach");
2414
+ backlinkOutreach.command("update").requiredOption("--project <project-id>").requiredOption("--prospect <prospect-id>").requiredOption("--file <file>").requiredOption("--expected-version <number>", "current draft version", Number).action(async (local) => {
2415
+ const global = program.opts();
2416
+ commandExitCode = await execute({ command: "backlinks outreach update", global, runtime, async action(context, client) {
2417
+ const payload = JSON.parse(await readFile2(resolve2(runtime.cwd, local.file), "utf8"));
2418
+ const data = await client.backlinks.prospects.updateOutreach(local.project, local.prospect, { ...payload, expectedVersion: local.expectedVersion }, context.requestId, { requestId: context.requestId });
2419
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2420
+ } });
2421
+ });
2422
+ backlinks.command("verify").requiredOption("--project <project-id>").requiredOption("--prospect <prospect-id>").requiredOption("--url <url>").requiredOption("--expected-version <number>", "current prospect version", Number).action(async (local) => {
2423
+ const global = program.opts();
2424
+ commandExitCode = await execute({ command: "backlinks verify", global, runtime, async action(context, client) {
2425
+ const data = await client.backlinks.prospects.verify(local.project, local.prospect, { url: local.url, expectedVersion: local.expectedVersion }, context.requestId, { requestId: context.requestId });
2426
+ writeSuccess(context, data, JSON.stringify(data, null, 2));
2427
+ } });
2428
+ });
2258
2429
  const seo = program.command("seo").description("Read authorized SEO opportunities and weekly reports");
2259
2430
  const seoOpportunities = seo.command("opportunities").description("Read accepted SEO opportunities");
2260
2431
  seoOpportunities.command("list").description("List bounded SEO opportunities").requiredOption("--project <project-id>", "exact Rolino project ID").option("--limit <number>", "maximum opportunities to return", (value) => {
@@ -2420,4 +2591,4 @@ export {
2420
2591
  ROLINO_CLI_VERSION,
2421
2592
  runCli
2422
2593
  };
2423
- //# sourceMappingURL=chunk-3WBZCTM5.js.map
2594
+ //# sourceMappingURL=chunk-PEJ2WA66.js.map