@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/CHANGELOG.md +20 -0
- package/README.md +37 -19
- package/dist/bin.cjs +188 -18
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-3WBZCTM5.js → chunk-PEJ2WA66.js} +190 -19
- package/dist/chunk-PEJ2WA66.js.map +1 -0
- package/dist/index.cjs +188 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -10
- package/dist/index.d.ts +21 -10
- package/dist/index.js +1 -1
- package/package.json +4 -4
- package/dist/chunk-3WBZCTM5.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -50,7 +50,7 @@ var import_commander = require("commander");
|
|
|
50
50
|
// package.json
|
|
51
51
|
var package_default = {
|
|
52
52
|
name: "@rolino/cli",
|
|
53
|
-
version: "0.
|
|
53
|
+
version: "0.7.0",
|
|
54
54
|
description: "Agent-friendly command-line interface for Rolino",
|
|
55
55
|
type: "module",
|
|
56
56
|
license: "MIT",
|
|
@@ -107,9 +107,9 @@ var package_default = {
|
|
|
107
107
|
dev: "tsx src/bin.ts"
|
|
108
108
|
},
|
|
109
109
|
dependencies: {
|
|
110
|
-
"@rolino/contracts": "0.
|
|
111
|
-
"@rolino/local-auth": "0.
|
|
112
|
-
"@rolino/sdk": "0.
|
|
110
|
+
"@rolino/contracts": "0.7.0",
|
|
111
|
+
"@rolino/local-auth": "0.7.0",
|
|
112
|
+
"@rolino/sdk": "0.7.0",
|
|
113
113
|
commander: "^15.0.0",
|
|
114
114
|
open: "^11.0.0"
|
|
115
115
|
},
|
|
@@ -665,12 +665,18 @@ function removeExistingCodexEntry(source, newline) {
|
|
|
665
665
|
return kept.join(newline).trimEnd();
|
|
666
666
|
}
|
|
667
667
|
function codexBlock(server, newline) {
|
|
668
|
+
const configuration = server.transport === "http" ? [
|
|
669
|
+
`url = ${tomlString(server.url)}`,
|
|
670
|
+
`auth = ${tomlString(server.auth)}`
|
|
671
|
+
] : [
|
|
672
|
+
`command = ${tomlString(server.command)}`,
|
|
673
|
+
`args = [${server.args.map(tomlString).join(", ")}]`,
|
|
674
|
+
`env = { ROLINO_URL = ${tomlString(server.env.ROLINO_URL)} }`
|
|
675
|
+
];
|
|
668
676
|
return [
|
|
669
677
|
CODEX_BEGIN,
|
|
670
678
|
"[mcp_servers.rolino]",
|
|
671
|
-
|
|
672
|
-
`args = [${server.args.map(tomlString).join(", ")}]`,
|
|
673
|
-
`env = { ROLINO_URL = ${tomlString(server.env.ROLINO_URL)} }`,
|
|
679
|
+
...configuration,
|
|
674
680
|
CODEX_END
|
|
675
681
|
].join(newline);
|
|
676
682
|
}
|
|
@@ -710,7 +716,12 @@ function updateClaudeProjectConfig(source, server) {
|
|
|
710
716
|
...parsed,
|
|
711
717
|
mcpServers: {
|
|
712
718
|
...currentServers,
|
|
713
|
-
rolino: { type: "
|
|
719
|
+
rolino: server.transport === "http" ? { type: "http", url: server.url } : {
|
|
720
|
+
type: "stdio",
|
|
721
|
+
command: server.command,
|
|
722
|
+
args: server.args,
|
|
723
|
+
env: server.env
|
|
724
|
+
}
|
|
714
725
|
}
|
|
715
726
|
}, null, 2)}
|
|
716
727
|
`;
|
|
@@ -742,6 +753,7 @@ async function setupCodex(options, server) {
|
|
|
742
753
|
backupPath,
|
|
743
754
|
changed,
|
|
744
755
|
dryRun: options.dryRun ?? false,
|
|
756
|
+
transport: server.transport,
|
|
745
757
|
server
|
|
746
758
|
};
|
|
747
759
|
}
|
|
@@ -762,6 +774,7 @@ async function setupClaudeCode(options, server) {
|
|
|
762
774
|
backupPath,
|
|
763
775
|
changed,
|
|
764
776
|
dryRun: options.dryRun ?? false,
|
|
777
|
+
transport: server.transport,
|
|
765
778
|
server
|
|
766
779
|
};
|
|
767
780
|
}
|
|
@@ -774,6 +787,7 @@ async function setupClaudeCode(options, server) {
|
|
|
774
787
|
backupPath: null,
|
|
775
788
|
changed: true,
|
|
776
789
|
dryRun: true,
|
|
790
|
+
transport: server.transport,
|
|
777
791
|
server
|
|
778
792
|
};
|
|
779
793
|
}
|
|
@@ -787,7 +801,12 @@ async function setupClaudeCode(options, server) {
|
|
|
787
801
|
"mcp",
|
|
788
802
|
"add-json",
|
|
789
803
|
"rolino",
|
|
790
|
-
JSON.stringify({ type: "
|
|
804
|
+
JSON.stringify(server.transport === "http" ? { type: "http", url: server.url } : {
|
|
805
|
+
type: "stdio",
|
|
806
|
+
command: server.command,
|
|
807
|
+
args: server.args,
|
|
808
|
+
env: server.env
|
|
809
|
+
}),
|
|
791
810
|
"--scope",
|
|
792
811
|
"user"
|
|
793
812
|
], options);
|
|
@@ -803,14 +822,51 @@ async function setupClaudeCode(options, server) {
|
|
|
803
822
|
backupPath: null,
|
|
804
823
|
changed: true,
|
|
805
824
|
dryRun: false,
|
|
825
|
+
transport: server.transport,
|
|
806
826
|
server
|
|
807
827
|
};
|
|
808
828
|
}
|
|
829
|
+
function remoteMcpUrl(baseUrl) {
|
|
830
|
+
return new URL("mcp", `${baseUrl.replace(/\/$/, "")}/`).toString();
|
|
831
|
+
}
|
|
832
|
+
async function advertisedRemoteMcp(options) {
|
|
833
|
+
const fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
834
|
+
try {
|
|
835
|
+
const response = await fetchImplementation(
|
|
836
|
+
new URL("api/v1/meta", `${options.baseUrl.replace(/\/$/, "")}/`),
|
|
837
|
+
{
|
|
838
|
+
headers: { accept: "application/json" },
|
|
839
|
+
signal: AbortSignal.timeout(5e3)
|
|
840
|
+
}
|
|
841
|
+
);
|
|
842
|
+
if (!response.ok) return false;
|
|
843
|
+
const payload = await response.json();
|
|
844
|
+
return payload.data?.mcp?.streamableHttp === true;
|
|
845
|
+
} catch {
|
|
846
|
+
return false;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
async function resolveTransport(options) {
|
|
850
|
+
const requested = options.transport ?? "stdio";
|
|
851
|
+
if (requested === "stdio") return "stdio";
|
|
852
|
+
if (await advertisedRemoteMcp(options)) return "http";
|
|
853
|
+
if (requested === "http") {
|
|
854
|
+
throw new TypeError(
|
|
855
|
+
"This Rolino instance does not advertise Streamable HTTP MCP. Use --transport stdio, or enable and verify remote MCP on the server."
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
return "stdio";
|
|
859
|
+
}
|
|
809
860
|
async function setupMcp(options) {
|
|
810
|
-
const
|
|
811
|
-
const server = {
|
|
861
|
+
const transport = await resolveTransport(options);
|
|
862
|
+
const server = transport === "http" ? {
|
|
863
|
+
transport: "http",
|
|
864
|
+
url: remoteMcpUrl(options.baseUrl),
|
|
865
|
+
auth: "oauth"
|
|
866
|
+
} : {
|
|
867
|
+
transport: "stdio",
|
|
812
868
|
command: options.nodePath,
|
|
813
|
-
args: [
|
|
869
|
+
args: [await resolveServerPath(options)],
|
|
814
870
|
env: { ROLINO_URL: options.baseUrl }
|
|
815
871
|
};
|
|
816
872
|
return options.client === "codex" ? setupCodex(options, server) : setupClaudeCode(options, server);
|
|
@@ -951,6 +1007,10 @@ function mcpScope(value) {
|
|
|
951
1007
|
if (value === "user" || value === "project") return value;
|
|
952
1008
|
throw new import_commander.InvalidArgumentError("MCP setup scope must be user or project.");
|
|
953
1009
|
}
|
|
1010
|
+
function mcpTransport(value) {
|
|
1011
|
+
if (value === "auto" || value === "http" || value === "stdio") return value;
|
|
1012
|
+
throw new import_commander.InvalidArgumentError("MCP transport must be auto, http, or stdio.");
|
|
1013
|
+
}
|
|
954
1014
|
function isoDateTime(value) {
|
|
955
1015
|
const date = new Date(value);
|
|
956
1016
|
if (Number.isNaN(date.getTime())) {
|
|
@@ -1011,13 +1071,21 @@ function requireBlogExecutionConsent(options) {
|
|
|
1011
1071
|
throw new TypeError("This Blog execute command requires explicit consent. Review the confirmed operation and pass --yes.");
|
|
1012
1072
|
}
|
|
1013
1073
|
function formatMcpSetupPreview(result) {
|
|
1074
|
+
const connection = result.server.transport === "http" ? [
|
|
1075
|
+
`Transport: Streamable HTTP`,
|
|
1076
|
+
`Endpoint: ${result.server.url}`,
|
|
1077
|
+
"Authentication: OAuth in the MCP client"
|
|
1078
|
+
] : [
|
|
1079
|
+
"Transport: local STDIO",
|
|
1080
|
+
`Command: ${result.server.command}`,
|
|
1081
|
+
`Arguments: ${result.server.args.join(" ")}`,
|
|
1082
|
+
`Rolino URL: ${result.server.env.ROLINO_URL}`
|
|
1083
|
+
];
|
|
1014
1084
|
return [
|
|
1015
1085
|
`Client: ${result.client === "codex" ? "Codex" : "Claude Code"}`,
|
|
1016
1086
|
`Scope: ${result.scope}`,
|
|
1017
1087
|
`Target: ${result.target}`,
|
|
1018
|
-
|
|
1019
|
-
`Arguments: ${result.server.args.join(" ")}`,
|
|
1020
|
-
`Rolino URL: ${result.server.env.ROLINO_URL}`,
|
|
1088
|
+
...connection,
|
|
1021
1089
|
"No token will be written to MCP configuration."
|
|
1022
1090
|
].join("\n");
|
|
1023
1091
|
}
|
|
@@ -1480,7 +1548,7 @@ async function runCli(argv = process.argv, overrides = {}) {
|
|
|
1480
1548
|
});
|
|
1481
1549
|
});
|
|
1482
1550
|
const setup = program.command("setup").description("Configure local agent tools for Rolino");
|
|
1483
|
-
setup.command("mcp").description("Configure
|
|
1551
|
+
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) => {
|
|
1484
1552
|
const global = program.opts();
|
|
1485
1553
|
commandExitCode = await execute({
|
|
1486
1554
|
command: "setup mcp",
|
|
@@ -1493,7 +1561,8 @@ async function runCli(argv = process.argv, overrides = {}) {
|
|
|
1493
1561
|
cwd: runtime.cwd,
|
|
1494
1562
|
env: runtime.env,
|
|
1495
1563
|
nodePath: runtime.nodePath,
|
|
1496
|
-
cliEntryPath: runtime.cliEntryPath
|
|
1564
|
+
cliEntryPath: runtime.cliEntryPath,
|
|
1565
|
+
fetch: runtime.fetch
|
|
1497
1566
|
};
|
|
1498
1567
|
const preview = await setupMcp({ ...setupOptions, dryRun: true });
|
|
1499
1568
|
let result = preview;
|
|
@@ -1520,11 +1589,12 @@ async function runCli(argv = process.argv, overrides = {}) {
|
|
|
1520
1589
|
`${state} Rolino MCP for ${clientLabel}.`,
|
|
1521
1590
|
`Scope: ${result.scope}`,
|
|
1522
1591
|
`Target: ${result.target}`,
|
|
1592
|
+
`Transport: ${result.transport === "http" ? "Streamable HTTP" : "local STDIO"}`,
|
|
1523
1593
|
...result.backupPath && !result.dryRun ? [`Backup: ${result.backupPath}`] : [],
|
|
1524
1594
|
`Rolino URL: ${client.baseUrl}`,
|
|
1525
1595
|
"No token was written to MCP configuration."
|
|
1526
1596
|
].join("\n"),
|
|
1527
|
-
result.client === "codex" ? ["codex mcp
|
|
1597
|
+
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"]
|
|
1528
1598
|
);
|
|
1529
1599
|
}
|
|
1530
1600
|
});
|
|
@@ -2263,6 +2333,106 @@ Revision: ${local.revision}` });
|
|
|
2263
2333
|
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2264
2334
|
} });
|
|
2265
2335
|
});
|
|
2336
|
+
const backlinks = program.command("backlinks").description("Review backlink prospects and public contact drafts. Rolino never sends email.");
|
|
2337
|
+
const backlinkTargets = backlinks.command("targets");
|
|
2338
|
+
backlinkTargets.command("list").requiredOption("--project <project-id>").action(async (local) => {
|
|
2339
|
+
const global = program.opts();
|
|
2340
|
+
commandExitCode = await execute({ command: "backlinks targets list", global, runtime, async action(context, client) {
|
|
2341
|
+
const data = await client.backlinks.targets.list(local.project, { requestId: context.requestId });
|
|
2342
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2343
|
+
} });
|
|
2344
|
+
});
|
|
2345
|
+
backlinkTargets.command("add").requiredOption("--project <project-id>").requiredOption("--url <url>").requiredOption("--label <label>").action(async (local) => {
|
|
2346
|
+
const global = program.opts();
|
|
2347
|
+
commandExitCode = await execute({ command: "backlinks targets add", global, runtime, async action(context, client) {
|
|
2348
|
+
const data = await client.backlinks.targets.add(local.project, { url: local.url, label: local.label }, context.requestId, { requestId: context.requestId });
|
|
2349
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2350
|
+
} });
|
|
2351
|
+
});
|
|
2352
|
+
backlinks.command("discover").requiredOption("--project <project-id>").option("--limit <number>", "maximum saved prospects", Number, 20).option("--idempotency-key <key>").action(async (local) => {
|
|
2353
|
+
const global = program.opts();
|
|
2354
|
+
commandExitCode = await execute({ command: "backlinks discover", global, runtime, async action(context, client) {
|
|
2355
|
+
const data = await client.backlinks.discoveries.start(local.project, { limit: local.limit }, local.idempotencyKey ?? context.requestId, { requestId: context.requestId });
|
|
2356
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2357
|
+
} });
|
|
2358
|
+
});
|
|
2359
|
+
const backlinkRuns = backlinks.command("runs");
|
|
2360
|
+
backlinkRuns.command("get").requiredOption("--project <project-id>").requiredOption("--run <run-id>").action(async (local) => {
|
|
2361
|
+
const global = program.opts();
|
|
2362
|
+
commandExitCode = await execute({ command: "backlinks runs get", global, runtime, async action(context, client) {
|
|
2363
|
+
const data = await client.backlinks.discoveries.get(local.project, local.run, { requestId: context.requestId });
|
|
2364
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2365
|
+
} });
|
|
2366
|
+
});
|
|
2367
|
+
const backlinkProspects = backlinks.command("prospects");
|
|
2368
|
+
backlinkProspects.command("list").requiredOption("--project <project-id>").option("--stage <stage>").action(async (local) => {
|
|
2369
|
+
const global = program.opts();
|
|
2370
|
+
commandExitCode = await execute({ command: "backlinks prospects list", global, runtime, async action(context, client) {
|
|
2371
|
+
const stage = local.stage ? import_contracts.BacklinkProspectStageSchema.parse(local.stage.toUpperCase()) : void 0;
|
|
2372
|
+
const data = await client.backlinks.prospects.list(local.project, { stage }, { requestId: context.requestId });
|
|
2373
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2374
|
+
} });
|
|
2375
|
+
});
|
|
2376
|
+
backlinkProspects.command("get").requiredOption("--project <project-id>").requiredOption("--prospect <prospect-id>").action(async (local) => {
|
|
2377
|
+
const global = program.opts();
|
|
2378
|
+
commandExitCode = await execute({ command: "backlinks prospects get", global, runtime, async action(context, client) {
|
|
2379
|
+
const data = await client.backlinks.prospects.get(local.project, local.prospect, { requestId: context.requestId });
|
|
2380
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2381
|
+
} });
|
|
2382
|
+
});
|
|
2383
|
+
backlinkProspects.command("approve").requiredOption("--project <project-id>").requiredOption("--prospect <prospect-id>").requiredOption("--expected-version <number>", "current optimistic version", Number).action(async (local) => {
|
|
2384
|
+
const global = program.opts();
|
|
2385
|
+
commandExitCode = await execute({ command: "backlinks prospects approve", global, runtime, async action(context, client) {
|
|
2386
|
+
const data = await client.backlinks.prospects.updateStage(local.project, local.prospect, { stage: "APPROVED", expectedVersion: local.expectedVersion }, context.requestId, { requestId: context.requestId });
|
|
2387
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2388
|
+
} });
|
|
2389
|
+
});
|
|
2390
|
+
const backlinkContacts = backlinks.command("contacts");
|
|
2391
|
+
backlinkContacts.command("research").requiredOption("--project <project-id>").requiredOption("--prospect <prospect-id>").action(async (local) => {
|
|
2392
|
+
const global = program.opts();
|
|
2393
|
+
commandExitCode = await execute({ command: "backlinks contacts research", global, runtime, async action(context, client) {
|
|
2394
|
+
const data = await client.backlinks.prospects.researchContact(local.project, local.prospect, context.requestId, { requestId: context.requestId });
|
|
2395
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2396
|
+
} });
|
|
2397
|
+
});
|
|
2398
|
+
backlinkContacts.command("list").requiredOption("--project <project-id>").action(async (local) => {
|
|
2399
|
+
const global = program.opts();
|
|
2400
|
+
commandExitCode = await execute({ command: "backlinks contacts list", global, runtime, async action(context, client) {
|
|
2401
|
+
const data = await client.backlinks.contacts.list(local.project, { limit: 50 }, { requestId: context.requestId });
|
|
2402
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2403
|
+
} });
|
|
2404
|
+
});
|
|
2405
|
+
backlinkContacts.command("export").requiredOption("--project <project-id>").option("--format <format>", "json or csv", "csv").action(async (local) => {
|
|
2406
|
+
const global = program.opts();
|
|
2407
|
+
commandExitCode = await execute({ command: "backlinks contacts export", global, runtime, async action(context, client) {
|
|
2408
|
+
const data = await client.backlinks.contacts.list(local.project, { limit: 50 }, { requestId: context.requestId });
|
|
2409
|
+
if (local.format !== "csv") return writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2410
|
+
const cell = (value) => {
|
|
2411
|
+
let text = String(value ?? "").replace(/[\r\n]+/g, " ");
|
|
2412
|
+
if (/^[=+\-@\t]/.test(text)) text = `'${text}`;
|
|
2413
|
+
return `"${text.replaceAll('"', '""')}"`;
|
|
2414
|
+
};
|
|
2415
|
+
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");
|
|
2416
|
+
context.stdout.write(`${csv}
|
|
2417
|
+
`);
|
|
2418
|
+
} });
|
|
2419
|
+
});
|
|
2420
|
+
const backlinkOutreach = backlinks.command("outreach");
|
|
2421
|
+
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) => {
|
|
2422
|
+
const global = program.opts();
|
|
2423
|
+
commandExitCode = await execute({ command: "backlinks outreach update", global, runtime, async action(context, client) {
|
|
2424
|
+
const payload = JSON.parse(await (0, import_promises2.readFile)((0, import_node_path2.resolve)(runtime.cwd, local.file), "utf8"));
|
|
2425
|
+
const data = await client.backlinks.prospects.updateOutreach(local.project, local.prospect, { ...payload, expectedVersion: local.expectedVersion }, context.requestId, { requestId: context.requestId });
|
|
2426
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2427
|
+
} });
|
|
2428
|
+
});
|
|
2429
|
+
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) => {
|
|
2430
|
+
const global = program.opts();
|
|
2431
|
+
commandExitCode = await execute({ command: "backlinks verify", global, runtime, async action(context, client) {
|
|
2432
|
+
const data = await client.backlinks.prospects.verify(local.project, local.prospect, { url: local.url, expectedVersion: local.expectedVersion }, context.requestId, { requestId: context.requestId });
|
|
2433
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2434
|
+
} });
|
|
2435
|
+
});
|
|
2266
2436
|
const seo = program.command("seo").description("Read authorized SEO opportunities and weekly reports");
|
|
2267
2437
|
const seoOpportunities = seo.command("opportunities").description("Read accepted SEO opportunities");
|
|
2268
2438
|
seoOpportunities.command("list").description("List bounded SEO opportunities").requiredOption("--project <project-id>", "exact Rolino project ID").option("--limit <number>", "maximum opportunities to return", (value) => {
|