@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/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
# @rolino/cli
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f78e0ff: Prefer advertised Streamable HTTP MCP with client-owned OAuth, add explicit HTTP and STDIO transport selection, and keep local MCP as a fallback.
|
|
8
|
+
- 358f37a: Expose additive safe Blog preparation, next-action, and quality summaries through the shared read contracts and existing clients.
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- Updated dependencies [358f37a]
|
|
13
|
+
- @rolino/contracts@0.7.0
|
|
14
|
+
- @rolino/sdk@0.7.0
|
|
15
|
+
- @rolino/local-auth@0.7.0
|
|
16
|
+
|
|
3
17
|
## 0.6.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
|
6
20
|
|
|
21
|
+
- Add matching backlink prospecting commands with safe JSON output and formula-safe contact CSV export. Rolino does not send email.
|
|
22
|
+
|
|
7
23
|
- d162f85: Add safe Blog editorial cadence preview and apply operations across contracts, SDK, CLI, and MCP.
|
|
8
24
|
- 50cb9fc: Add Google Business Profile as a disabled-by-default standard text and single-image publishing destination across shared public clients.
|
|
9
25
|
- 1ac7ed5: Add signed custom Blog webhook setup, connection testing, one-time secret rotation, and safe idempotent replay parity.
|
|
@@ -93,6 +109,10 @@
|
|
|
93
109
|
|
|
94
110
|
### Minor Changes
|
|
95
111
|
|
|
112
|
+
- Show additive safe Blog preparation and quality state in existing JSON reads.
|
|
113
|
+
|
|
114
|
+
### Minor Changes
|
|
115
|
+
|
|
96
116
|
- Add Blog image generation, exact local-file upload, image review, confirmed
|
|
97
117
|
exact-bundle approval, future scheduling, and schedule cancellation commands
|
|
98
118
|
with deterministic agent output, stable idempotency, safe errors, and
|
package/README.md
CHANGED
|
@@ -14,16 +14,17 @@ The supported installation keeps the CLI independent from MCP:
|
|
|
14
14
|
npm install --global @rolino/cli
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
Users who
|
|
17
|
+
Users who need local STDIO MCP will install both packages explicitly:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
20
|
npm install --global @rolino/cli @rolino/mcp
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
The
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
The default `rolino setup mcp` mode checks the selected Rolino instance. It
|
|
24
|
+
uses hosted Streamable HTTP when the instance advertises it. This mode does
|
|
25
|
+
not need `@rolino/mcp`. If remote MCP is not available, the command uses local
|
|
26
|
+
STDIO and explains how to install the separate MCP package. Rolino never
|
|
27
|
+
downloads or installs MCP silently.
|
|
27
28
|
|
|
28
29
|
Initial commands:
|
|
29
30
|
|
|
@@ -31,12 +32,24 @@ Initial commands:
|
|
|
31
32
|
rolino auth login
|
|
32
33
|
rolino auth status
|
|
33
34
|
rolino auth logout
|
|
34
|
-
rolino setup mcp --client codex [--scope user|project]
|
|
35
|
-
rolino setup mcp --client claude-code [--scope user|project]
|
|
35
|
+
rolino setup mcp --client codex [--transport auto|http|stdio] [--scope user|project]
|
|
36
|
+
rolino setup mcp --client claude-code [--transport auto|http|stdio] [--scope user|project]
|
|
36
37
|
rolino whoami
|
|
37
38
|
rolino projects create --name NAME [--type PRODUCT|SERVICE|PERSONAL|COMPANY] [--website URL] [--description TEXT] [--idempotency-key KEY] --yes
|
|
38
39
|
rolino projects list [--limit 50] [--cursor ID]
|
|
39
40
|
rolino projects show PROJECT_ID
|
|
41
|
+
rolino backlinks targets list --project PROJECT_ID
|
|
42
|
+
rolino backlinks targets add --project PROJECT_ID --url URL --label LABEL
|
|
43
|
+
rolino backlinks discover --project PROJECT_ID --limit 20
|
|
44
|
+
rolino backlinks runs get --project PROJECT_ID --run RUN_ID
|
|
45
|
+
rolino backlinks prospects list --project PROJECT_ID [--stage STAGE]
|
|
46
|
+
rolino backlinks prospects get --project PROJECT_ID --prospect PROSPECT_ID
|
|
47
|
+
rolino backlinks prospects approve --project PROJECT_ID --prospect PROSPECT_ID --expected-version VERSION
|
|
48
|
+
rolino backlinks contacts research --project PROJECT_ID --prospect PROSPECT_ID
|
|
49
|
+
rolino backlinks contacts list --project PROJECT_ID
|
|
50
|
+
rolino backlinks contacts export --project PROJECT_ID --format csv
|
|
51
|
+
rolino backlinks outreach update --project PROJECT_ID --prospect PROSPECT_ID --file DRAFT.json --expected-version VERSION
|
|
52
|
+
rolino backlinks verify --project PROJECT_ID --prospect PROSPECT_ID --url PUBLIC_URL --expected-version VERSION
|
|
40
53
|
rolino media list --project PROJECT_ID [--type IMAGE|VIDEO] [--query TEXT] [--limit 50] [--cursor ID]
|
|
41
54
|
rolino media upload FILE_PATH --project PROJECT_ID --yes
|
|
42
55
|
rolino posts create --project PROJECT_ID --caption TEXT [--platform INSTAGRAM|TIKTOK|YOUTUBE|BLUESKY|LINKEDIN|GOOGLE_BUSINESS_PROFILE] [--media ASSET_ID] --yes
|
|
@@ -70,10 +83,10 @@ rolino blog schedule cancel execute ARTICLE_ID --project PROJECT_ID --confirmati
|
|
|
70
83
|
rolino doctor
|
|
71
84
|
```
|
|
72
85
|
|
|
73
|
-
Interactive login uses standard OAuth Authorization Code with PKCE S256. It
|
|
74
|
-
opens the configured Rolino instance, requests the exact \`/api/v1\` resource,
|
|
75
|
-
uses the fixed \`http://127.0.0.1:48391/oauth/callback\` redirect, and obtains
|
|
76
|
-
explicit workspace approval.
|
|
86
|
+
Interactive login uses standard OAuth Authorization Code with PKCE S256. It
|
|
87
|
+
opens the configured Rolino instance, requests the exact \`/api/v1\` resource,
|
|
88
|
+
uses the fixed \`http://127.0.0.1:48391/oauth/callback\` redirect, and obtains
|
|
89
|
+
explicit workspace approval.
|
|
77
90
|
The approval screen always includes identity and workspace reads and lets the
|
|
78
91
|
user independently opt into project creation, draft writing, confirmed
|
|
79
92
|
scheduling, confirmed immediate publishing, and read-only SEO Intelligence.
|
|
@@ -89,20 +102,25 @@ with evidence. It cannot create a final draft in the same action.
|
|
|
89
102
|
Optional permissions start off,
|
|
90
103
|
and existing credentials never gain a newly selected permission. Revoke and
|
|
91
104
|
reauthorize an existing connection to grant project creation or `seo:read`.
|
|
92
|
-
Existing grants never receive `seo:read` automatically. OAuth tokens never
|
|
93
|
-
pass through a browser URL. The CLI stores a short-lived access token and a
|
|
94
|
-
rotating refresh token by host, issuer, client, and resource in the
|
|
95
|
-
operating-system configuration directory. It does not read or migrate the old
|
|
96
|
-
interactive credential, so the first use after this cutover requires sign-in;
|
|
105
|
+
Existing grants never receive `seo:read` automatically. OAuth tokens never
|
|
106
|
+
pass through a browser URL. The CLI stores a short-lived access token and a
|
|
107
|
+
rotating refresh token by host, issuer, client, and resource in the
|
|
108
|
+
operating-system configuration directory. It does not read or migrate the old
|
|
109
|
+
interactive credential, so the first use after this cutover requires sign-in;
|
|
97
110
|
`ROLINO_CONFIG_DIR` overrides that location. `auth logout` revokes the calling
|
|
98
|
-
workspace grant and OAuth refresh token before deleting its local copy.
|
|
111
|
+
workspace grant and OAuth refresh token before deleting its local copy.
|
|
99
112
|
|
|
100
113
|
The terminal waits five minutes for browser approval. If that request expires,
|
|
101
114
|
close the old approval page and run `rolino auth login` again; refreshing the
|
|
102
115
|
page cannot restart the closed loopback listener.
|
|
103
116
|
|
|
104
|
-
`setup mcp`
|
|
105
|
-
|
|
117
|
+
`setup mcp` checks `/api/v1/meta` and prefers hosted Streamable HTTP when it is
|
|
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
|
|
121
|
+
require remote MCP or `--transport stdio` to require the local package and its
|
|
122
|
+
shared CLI OAuth session. The command previews the exact token-free
|
|
123
|
+
configuration and asks before changing anything. User scope is the default.
|
|
106
124
|
Codex configuration is written to `CODEX_HOME/config.toml` or
|
|
107
125
|
`~/.codex/config.toml`; Claude Code user scope is applied through its official
|
|
108
126
|
CLI. Project scope writes `.codex/config.toml` or `.mcp.json` in the current
|
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.7.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.7.0",
|
|
95
|
+
"@rolino/local-auth": "0.7.0",
|
|
96
|
+
"@rolino/sdk": "0.7.0",
|
|
97
97
|
commander: "^15.0.0",
|
|
98
98
|
open: "^11.0.0"
|
|
99
99
|
},
|
|
@@ -649,12 +649,18 @@ function removeExistingCodexEntry(source, newline) {
|
|
|
649
649
|
return kept.join(newline).trimEnd();
|
|
650
650
|
}
|
|
651
651
|
function codexBlock(server, newline) {
|
|
652
|
+
const configuration = server.transport === "http" ? [
|
|
653
|
+
`url = ${tomlString(server.url)}`,
|
|
654
|
+
`auth = ${tomlString(server.auth)}`
|
|
655
|
+
] : [
|
|
656
|
+
`command = ${tomlString(server.command)}`,
|
|
657
|
+
`args = [${server.args.map(tomlString).join(", ")}]`,
|
|
658
|
+
`env = { ROLINO_URL = ${tomlString(server.env.ROLINO_URL)} }`
|
|
659
|
+
];
|
|
652
660
|
return [
|
|
653
661
|
CODEX_BEGIN,
|
|
654
662
|
"[mcp_servers.rolino]",
|
|
655
|
-
|
|
656
|
-
`args = [${server.args.map(tomlString).join(", ")}]`,
|
|
657
|
-
`env = { ROLINO_URL = ${tomlString(server.env.ROLINO_URL)} }`,
|
|
663
|
+
...configuration,
|
|
658
664
|
CODEX_END
|
|
659
665
|
].join(newline);
|
|
660
666
|
}
|
|
@@ -694,7 +700,12 @@ function updateClaudeProjectConfig(source, server) {
|
|
|
694
700
|
...parsed,
|
|
695
701
|
mcpServers: {
|
|
696
702
|
...currentServers,
|
|
697
|
-
rolino: { type: "
|
|
703
|
+
rolino: server.transport === "http" ? { type: "http", url: server.url } : {
|
|
704
|
+
type: "stdio",
|
|
705
|
+
command: server.command,
|
|
706
|
+
args: server.args,
|
|
707
|
+
env: server.env
|
|
708
|
+
}
|
|
698
709
|
}
|
|
699
710
|
}, null, 2)}
|
|
700
711
|
`;
|
|
@@ -726,6 +737,7 @@ async function setupCodex(options, server) {
|
|
|
726
737
|
backupPath,
|
|
727
738
|
changed,
|
|
728
739
|
dryRun: options.dryRun ?? false,
|
|
740
|
+
transport: server.transport,
|
|
729
741
|
server
|
|
730
742
|
};
|
|
731
743
|
}
|
|
@@ -746,6 +758,7 @@ async function setupClaudeCode(options, server) {
|
|
|
746
758
|
backupPath,
|
|
747
759
|
changed,
|
|
748
760
|
dryRun: options.dryRun ?? false,
|
|
761
|
+
transport: server.transport,
|
|
749
762
|
server
|
|
750
763
|
};
|
|
751
764
|
}
|
|
@@ -758,6 +771,7 @@ async function setupClaudeCode(options, server) {
|
|
|
758
771
|
backupPath: null,
|
|
759
772
|
changed: true,
|
|
760
773
|
dryRun: true,
|
|
774
|
+
transport: server.transport,
|
|
761
775
|
server
|
|
762
776
|
};
|
|
763
777
|
}
|
|
@@ -771,7 +785,12 @@ async function setupClaudeCode(options, server) {
|
|
|
771
785
|
"mcp",
|
|
772
786
|
"add-json",
|
|
773
787
|
"rolino",
|
|
774
|
-
JSON.stringify({ type: "
|
|
788
|
+
JSON.stringify(server.transport === "http" ? { type: "http", url: server.url } : {
|
|
789
|
+
type: "stdio",
|
|
790
|
+
command: server.command,
|
|
791
|
+
args: server.args,
|
|
792
|
+
env: server.env
|
|
793
|
+
}),
|
|
775
794
|
"--scope",
|
|
776
795
|
"user"
|
|
777
796
|
], options);
|
|
@@ -787,14 +806,51 @@ async function setupClaudeCode(options, server) {
|
|
|
787
806
|
backupPath: null,
|
|
788
807
|
changed: true,
|
|
789
808
|
dryRun: false,
|
|
809
|
+
transport: server.transport,
|
|
790
810
|
server
|
|
791
811
|
};
|
|
792
812
|
}
|
|
813
|
+
function remoteMcpUrl(baseUrl) {
|
|
814
|
+
return new URL("mcp", `${baseUrl.replace(/\/$/, "")}/`).toString();
|
|
815
|
+
}
|
|
816
|
+
async function advertisedRemoteMcp(options) {
|
|
817
|
+
const fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
818
|
+
try {
|
|
819
|
+
const response = await fetchImplementation(
|
|
820
|
+
new URL("api/v1/meta", `${options.baseUrl.replace(/\/$/, "")}/`),
|
|
821
|
+
{
|
|
822
|
+
headers: { accept: "application/json" },
|
|
823
|
+
signal: AbortSignal.timeout(5e3)
|
|
824
|
+
}
|
|
825
|
+
);
|
|
826
|
+
if (!response.ok) return false;
|
|
827
|
+
const payload = await response.json();
|
|
828
|
+
return payload.data?.mcp?.streamableHttp === true;
|
|
829
|
+
} catch {
|
|
830
|
+
return false;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
async function resolveTransport(options) {
|
|
834
|
+
const requested = options.transport ?? "stdio";
|
|
835
|
+
if (requested === "stdio") return "stdio";
|
|
836
|
+
if (await advertisedRemoteMcp(options)) return "http";
|
|
837
|
+
if (requested === "http") {
|
|
838
|
+
throw new TypeError(
|
|
839
|
+
"This Rolino instance does not advertise Streamable HTTP MCP. Use --transport stdio, or enable and verify remote MCP on the server."
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
return "stdio";
|
|
843
|
+
}
|
|
793
844
|
async function setupMcp(options) {
|
|
794
|
-
const
|
|
795
|
-
const server = {
|
|
845
|
+
const transport = await resolveTransport(options);
|
|
846
|
+
const server = transport === "http" ? {
|
|
847
|
+
transport: "http",
|
|
848
|
+
url: remoteMcpUrl(options.baseUrl),
|
|
849
|
+
auth: "oauth"
|
|
850
|
+
} : {
|
|
851
|
+
transport: "stdio",
|
|
796
852
|
command: options.nodePath,
|
|
797
|
-
args: [
|
|
853
|
+
args: [await resolveServerPath(options)],
|
|
798
854
|
env: { ROLINO_URL: options.baseUrl }
|
|
799
855
|
};
|
|
800
856
|
return options.client === "codex" ? setupCodex(options, server) : setupClaudeCode(options, server);
|
|
@@ -935,6 +991,10 @@ function mcpScope(value) {
|
|
|
935
991
|
if (value === "user" || value === "project") return value;
|
|
936
992
|
throw new import_commander.InvalidArgumentError("MCP setup scope must be user or project.");
|
|
937
993
|
}
|
|
994
|
+
function mcpTransport(value) {
|
|
995
|
+
if (value === "auto" || value === "http" || value === "stdio") return value;
|
|
996
|
+
throw new import_commander.InvalidArgumentError("MCP transport must be auto, http, or stdio.");
|
|
997
|
+
}
|
|
938
998
|
function isoDateTime(value) {
|
|
939
999
|
const date = new Date(value);
|
|
940
1000
|
if (Number.isNaN(date.getTime())) {
|
|
@@ -995,13 +1055,21 @@ function requireBlogExecutionConsent(options) {
|
|
|
995
1055
|
throw new TypeError("This Blog execute command requires explicit consent. Review the confirmed operation and pass --yes.");
|
|
996
1056
|
}
|
|
997
1057
|
function formatMcpSetupPreview(result) {
|
|
1058
|
+
const connection = result.server.transport === "http" ? [
|
|
1059
|
+
`Transport: Streamable HTTP`,
|
|
1060
|
+
`Endpoint: ${result.server.url}`,
|
|
1061
|
+
"Authentication: OAuth in the MCP client"
|
|
1062
|
+
] : [
|
|
1063
|
+
"Transport: local STDIO",
|
|
1064
|
+
`Command: ${result.server.command}`,
|
|
1065
|
+
`Arguments: ${result.server.args.join(" ")}`,
|
|
1066
|
+
`Rolino URL: ${result.server.env.ROLINO_URL}`
|
|
1067
|
+
];
|
|
998
1068
|
return [
|
|
999
1069
|
`Client: ${result.client === "codex" ? "Codex" : "Claude Code"}`,
|
|
1000
1070
|
`Scope: ${result.scope}`,
|
|
1001
1071
|
`Target: ${result.target}`,
|
|
1002
|
-
|
|
1003
|
-
`Arguments: ${result.server.args.join(" ")}`,
|
|
1004
|
-
`Rolino URL: ${result.server.env.ROLINO_URL}`,
|
|
1072
|
+
...connection,
|
|
1005
1073
|
"No token will be written to MCP configuration."
|
|
1006
1074
|
].join("\n");
|
|
1007
1075
|
}
|
|
@@ -1464,7 +1532,7 @@ async function runCli(argv = process.argv, overrides = {}) {
|
|
|
1464
1532
|
});
|
|
1465
1533
|
});
|
|
1466
1534
|
const setup = program.command("setup").description("Configure local agent tools for Rolino");
|
|
1467
|
-
setup.command("mcp").description("Configure
|
|
1535
|
+
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) => {
|
|
1468
1536
|
const global = program.opts();
|
|
1469
1537
|
commandExitCode = await execute({
|
|
1470
1538
|
command: "setup mcp",
|
|
@@ -1477,7 +1545,8 @@ async function runCli(argv = process.argv, overrides = {}) {
|
|
|
1477
1545
|
cwd: runtime.cwd,
|
|
1478
1546
|
env: runtime.env,
|
|
1479
1547
|
nodePath: runtime.nodePath,
|
|
1480
|
-
cliEntryPath: runtime.cliEntryPath
|
|
1548
|
+
cliEntryPath: runtime.cliEntryPath,
|
|
1549
|
+
fetch: runtime.fetch
|
|
1481
1550
|
};
|
|
1482
1551
|
const preview = await setupMcp({ ...setupOptions, dryRun: true });
|
|
1483
1552
|
let result = preview;
|
|
@@ -1504,11 +1573,12 @@ async function runCli(argv = process.argv, overrides = {}) {
|
|
|
1504
1573
|
`${state} Rolino MCP for ${clientLabel}.`,
|
|
1505
1574
|
`Scope: ${result.scope}`,
|
|
1506
1575
|
`Target: ${result.target}`,
|
|
1576
|
+
`Transport: ${result.transport === "http" ? "Streamable HTTP" : "local STDIO"}`,
|
|
1507
1577
|
...result.backupPath && !result.dryRun ? [`Backup: ${result.backupPath}`] : [],
|
|
1508
1578
|
`Rolino URL: ${client.baseUrl}`,
|
|
1509
1579
|
"No token was written to MCP configuration."
|
|
1510
1580
|
].join("\n"),
|
|
1511
|
-
result.client === "codex" ? ["codex mcp
|
|
1581
|
+
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"]
|
|
1512
1582
|
);
|
|
1513
1583
|
}
|
|
1514
1584
|
});
|
|
@@ -2247,6 +2317,106 @@ Revision: ${local.revision}` });
|
|
|
2247
2317
|
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2248
2318
|
} });
|
|
2249
2319
|
});
|
|
2320
|
+
const backlinks = program.command("backlinks").description("Review backlink prospects and public contact drafts. Rolino never sends email.");
|
|
2321
|
+
const backlinkTargets = backlinks.command("targets");
|
|
2322
|
+
backlinkTargets.command("list").requiredOption("--project <project-id>").action(async (local) => {
|
|
2323
|
+
const global = program.opts();
|
|
2324
|
+
commandExitCode = await execute({ command: "backlinks targets list", global, runtime, async action(context, client) {
|
|
2325
|
+
const data = await client.backlinks.targets.list(local.project, { requestId: context.requestId });
|
|
2326
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2327
|
+
} });
|
|
2328
|
+
});
|
|
2329
|
+
backlinkTargets.command("add").requiredOption("--project <project-id>").requiredOption("--url <url>").requiredOption("--label <label>").action(async (local) => {
|
|
2330
|
+
const global = program.opts();
|
|
2331
|
+
commandExitCode = await execute({ command: "backlinks targets add", global, runtime, async action(context, client) {
|
|
2332
|
+
const data = await client.backlinks.targets.add(local.project, { url: local.url, label: local.label }, context.requestId, { requestId: context.requestId });
|
|
2333
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2334
|
+
} });
|
|
2335
|
+
});
|
|
2336
|
+
backlinks.command("discover").requiredOption("--project <project-id>").option("--limit <number>", "maximum saved prospects", Number, 20).option("--idempotency-key <key>").action(async (local) => {
|
|
2337
|
+
const global = program.opts();
|
|
2338
|
+
commandExitCode = await execute({ command: "backlinks discover", global, runtime, async action(context, client) {
|
|
2339
|
+
const data = await client.backlinks.discoveries.start(local.project, { limit: local.limit }, local.idempotencyKey ?? context.requestId, { requestId: context.requestId });
|
|
2340
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2341
|
+
} });
|
|
2342
|
+
});
|
|
2343
|
+
const backlinkRuns = backlinks.command("runs");
|
|
2344
|
+
backlinkRuns.command("get").requiredOption("--project <project-id>").requiredOption("--run <run-id>").action(async (local) => {
|
|
2345
|
+
const global = program.opts();
|
|
2346
|
+
commandExitCode = await execute({ command: "backlinks runs get", global, runtime, async action(context, client) {
|
|
2347
|
+
const data = await client.backlinks.discoveries.get(local.project, local.run, { requestId: context.requestId });
|
|
2348
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2349
|
+
} });
|
|
2350
|
+
});
|
|
2351
|
+
const backlinkProspects = backlinks.command("prospects");
|
|
2352
|
+
backlinkProspects.command("list").requiredOption("--project <project-id>").option("--stage <stage>").action(async (local) => {
|
|
2353
|
+
const global = program.opts();
|
|
2354
|
+
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;
|
|
2356
|
+
const data = await client.backlinks.prospects.list(local.project, { stage }, { requestId: context.requestId });
|
|
2357
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2358
|
+
} });
|
|
2359
|
+
});
|
|
2360
|
+
backlinkProspects.command("get").requiredOption("--project <project-id>").requiredOption("--prospect <prospect-id>").action(async (local) => {
|
|
2361
|
+
const global = program.opts();
|
|
2362
|
+
commandExitCode = await execute({ command: "backlinks prospects get", global, runtime, async action(context, client) {
|
|
2363
|
+
const data = await client.backlinks.prospects.get(local.project, local.prospect, { requestId: context.requestId });
|
|
2364
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2365
|
+
} });
|
|
2366
|
+
});
|
|
2367
|
+
backlinkProspects.command("approve").requiredOption("--project <project-id>").requiredOption("--prospect <prospect-id>").requiredOption("--expected-version <number>", "current optimistic version", Number).action(async (local) => {
|
|
2368
|
+
const global = program.opts();
|
|
2369
|
+
commandExitCode = await execute({ command: "backlinks prospects approve", global, runtime, async action(context, client) {
|
|
2370
|
+
const data = await client.backlinks.prospects.updateStage(local.project, local.prospect, { stage: "APPROVED", expectedVersion: local.expectedVersion }, context.requestId, { requestId: context.requestId });
|
|
2371
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2372
|
+
} });
|
|
2373
|
+
});
|
|
2374
|
+
const backlinkContacts = backlinks.command("contacts");
|
|
2375
|
+
backlinkContacts.command("research").requiredOption("--project <project-id>").requiredOption("--prospect <prospect-id>").action(async (local) => {
|
|
2376
|
+
const global = program.opts();
|
|
2377
|
+
commandExitCode = await execute({ command: "backlinks contacts research", global, runtime, async action(context, client) {
|
|
2378
|
+
const data = await client.backlinks.prospects.researchContact(local.project, local.prospect, context.requestId, { requestId: context.requestId });
|
|
2379
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2380
|
+
} });
|
|
2381
|
+
});
|
|
2382
|
+
backlinkContacts.command("list").requiredOption("--project <project-id>").action(async (local) => {
|
|
2383
|
+
const global = program.opts();
|
|
2384
|
+
commandExitCode = await execute({ command: "backlinks contacts list", global, runtime, async action(context, client) {
|
|
2385
|
+
const data = await client.backlinks.contacts.list(local.project, { limit: 50 }, { requestId: context.requestId });
|
|
2386
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2387
|
+
} });
|
|
2388
|
+
});
|
|
2389
|
+
backlinkContacts.command("export").requiredOption("--project <project-id>").option("--format <format>", "json or csv", "csv").action(async (local) => {
|
|
2390
|
+
const global = program.opts();
|
|
2391
|
+
commandExitCode = await execute({ command: "backlinks contacts export", global, runtime, async action(context, client) {
|
|
2392
|
+
const data = await client.backlinks.contacts.list(local.project, { limit: 50 }, { requestId: context.requestId });
|
|
2393
|
+
if (local.format !== "csv") return writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2394
|
+
const cell = (value) => {
|
|
2395
|
+
let text = String(value ?? "").replace(/[\r\n]+/g, " ");
|
|
2396
|
+
if (/^[=+\-@\t]/.test(text)) text = `'${text}`;
|
|
2397
|
+
return `"${text.replaceAll('"', '""')}"`;
|
|
2398
|
+
};
|
|
2399
|
+
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");
|
|
2400
|
+
context.stdout.write(`${csv}
|
|
2401
|
+
`);
|
|
2402
|
+
} });
|
|
2403
|
+
});
|
|
2404
|
+
const backlinkOutreach = backlinks.command("outreach");
|
|
2405
|
+
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) => {
|
|
2406
|
+
const global = program.opts();
|
|
2407
|
+
commandExitCode = await execute({ command: "backlinks outreach update", global, runtime, async action(context, client) {
|
|
2408
|
+
const payload = JSON.parse(await (0, import_promises2.readFile)((0, import_node_path2.resolve)(runtime.cwd, local.file), "utf8"));
|
|
2409
|
+
const data = await client.backlinks.prospects.updateOutreach(local.project, local.prospect, { ...payload, expectedVersion: local.expectedVersion }, context.requestId, { requestId: context.requestId });
|
|
2410
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2411
|
+
} });
|
|
2412
|
+
});
|
|
2413
|
+
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) => {
|
|
2414
|
+
const global = program.opts();
|
|
2415
|
+
commandExitCode = await execute({ command: "backlinks verify", global, runtime, async action(context, client) {
|
|
2416
|
+
const data = await client.backlinks.prospects.verify(local.project, local.prospect, { url: local.url, expectedVersion: local.expectedVersion }, context.requestId, { requestId: context.requestId });
|
|
2417
|
+
writeSuccess(context, data, JSON.stringify(data, null, 2));
|
|
2418
|
+
} });
|
|
2419
|
+
});
|
|
2250
2420
|
const seo = program.command("seo").description("Read authorized SEO opportunities and weekly reports");
|
|
2251
2421
|
const seoOpportunities = seo.command("opportunities").description("Read accepted SEO opportunities");
|
|
2252
2422
|
seoOpportunities.command("list").description("List bounded SEO opportunities").requiredOption("--project <project-id>", "exact Rolino project ID").option("--limit <number>", "maximum opportunities to return", (value) => {
|