outrank-cli 0.5.0 → 0.6.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/README.md +317 -20
- package/SKILL.md +20 -2
- package/bin/outrank-mcp.js +3 -0
- package/package.json +21 -5
- package/src/adapters/cli/jsonInput.js +42 -0
- package/src/adapters/cli/parse.js +124 -0
- package/src/adapters/cli/run.js +139 -0
- package/src/adapters/mcp/constants.js +49 -0
- package/src/adapters/mcp/descriptions.js +87 -0
- package/src/adapters/mcp/help.js +34 -0
- package/src/adapters/mcp/input.js +195 -0
- package/src/adapters/mcp/instructions.js +139 -0
- package/src/adapters/mcp/logger.js +45 -0
- package/src/adapters/mcp/main.js +36 -0
- package/src/adapters/mcp/options.js +118 -0
- package/src/adapters/mcp/result.js +33 -0
- package/src/adapters/mcp/server.js +174 -0
- package/src/adapters/mcp/session.js +274 -0
- package/src/adapters/mcp/toolNotes.js +426 -0
- package/src/adapters/mcp/toolsets.js +106 -0
- package/src/commands/articles.js +285 -301
- package/src/commands/auth.js +204 -86
- package/src/commands/billing.js +102 -104
- package/src/commands/improvements.js +319 -211
- package/src/commands/index.js +22 -0
- package/src/commands/keywords.js +266 -249
- package/src/commands/marketplace/buyer.js +386 -0
- package/src/commands/marketplace/index.js +83 -0
- package/src/commands/marketplace/notifications.js +60 -0
- package/src/commands/marketplace/seller.js +595 -0
- package/src/commands/marketplace/shared.js +27 -0
- package/src/commands/products/backlinks.js +181 -0
- package/src/commands/products/core.js +149 -0
- package/src/commands/products/gsc.js +250 -0
- package/src/commands/products/index.js +174 -0
- package/src/commands/products/integrations.js +343 -0
- package/src/commands/products/linking.js +192 -0
- package/src/commands/products/shared.js +8 -0
- package/src/commands/registry.js +272 -0
- package/src/commands/subscription.js +20 -30
- package/src/commands/usage.js +17 -30
- package/src/core/config.js +243 -62
- package/src/core/constants.js +101 -0
- package/src/core/context.js +311 -0
- package/src/core/errors.js +39 -0
- package/src/core/execute.js +71 -0
- package/src/core/files.js +186 -0
- package/src/core/http.js +393 -0
- package/src/core/profileSelection.js +7 -0
- package/src/core/query.js +11 -0
- package/src/core/redact.js +27 -0
- package/src/core/schema.js +222 -0
- package/src/core/version.js +12 -0
- package/src/index.js +30 -58
- package/src/commands/marketplace.js +0 -740
- package/src/commands/products.js +0 -1185
- package/src/core/api.js +0 -82
- package/src/core/args.js +0 -144
package/src/core/api.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { cliError } from './errors.js';
|
|
2
|
-
import { resolveRuntimeConfig } from './config.js';
|
|
3
|
-
|
|
4
|
-
const API_BASE_PATH = '/api/agent/v1';
|
|
5
|
-
|
|
6
|
-
export const resolveApiContext = async (parsed, { requireAuth = true } = {}) => {
|
|
7
|
-
const runtime = await resolveRuntimeConfig(parsed);
|
|
8
|
-
if (requireAuth && !runtime.apiKey) {
|
|
9
|
-
throw Object.assign(new Error('No Outrank API key is configured.'), {
|
|
10
|
-
cliCode: 'CLI_AUTH_REQUIRED',
|
|
11
|
-
guidance:
|
|
12
|
-
'Run `outrank-cli auth login <api-key>` to store a key locally, or set OUTRANK_API_KEY in the environment before retrying.',
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
return runtime;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const buildHeaders = ({ apiKey, idempotencyKey, hasJsonBody }) => {
|
|
19
|
-
const headers = {
|
|
20
|
-
Accept: 'application/json',
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
if (apiKey) {
|
|
24
|
-
headers.Authorization = `Bearer ${apiKey}`;
|
|
25
|
-
}
|
|
26
|
-
if (hasJsonBody) {
|
|
27
|
-
headers['Content-Type'] = 'application/json';
|
|
28
|
-
}
|
|
29
|
-
if (idempotencyKey) {
|
|
30
|
-
headers['Idempotency-Key'] = idempotencyKey;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return headers;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export const requestAgentApi = async (
|
|
37
|
-
parsed,
|
|
38
|
-
{ method = 'GET', path, body, idempotencyKey, requireAuth = true } = {},
|
|
39
|
-
) => {
|
|
40
|
-
const runtime = await resolveApiContext(parsed, { requireAuth });
|
|
41
|
-
const url = new URL(`${API_BASE_PATH}${path}`, runtime.baseUrl);
|
|
42
|
-
const response = await fetch(url, {
|
|
43
|
-
method,
|
|
44
|
-
headers: buildHeaders({
|
|
45
|
-
apiKey: runtime.apiKey,
|
|
46
|
-
idempotencyKey,
|
|
47
|
-
hasJsonBody: body !== undefined,
|
|
48
|
-
}),
|
|
49
|
-
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
50
|
-
}).catch((error) => {
|
|
51
|
-
throw Object.assign(new Error(`Request failed: ${error.message}`), {
|
|
52
|
-
cliCode: 'CLI_NETWORK_ERROR',
|
|
53
|
-
guidance:
|
|
54
|
-
`The CLI could not reach ${runtime.baseUrl}. Confirm OUTRANK_BASE_URL, the user's network, and the Outrank deployment, then retry.`,
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const contentType = response.headers.get('content-type') ?? '';
|
|
59
|
-
let payload;
|
|
60
|
-
if (contentType.includes('application/json')) {
|
|
61
|
-
payload = await response.json();
|
|
62
|
-
} else {
|
|
63
|
-
const guidance =
|
|
64
|
-
response.status === 404
|
|
65
|
-
? `The host ${runtime.baseUrl} responded, but ${url.pathname} is not deployed there. Point OUTRANK_BASE_URL or --base-url at the Outrank app deployment that serves the agent API, or deploy the /api/agent/v1 routes to this host.`
|
|
66
|
-
: 'The CLI expects the agent API to return JSON. Check the base URL and server deployment.';
|
|
67
|
-
payload = cliError(
|
|
68
|
-
'CLI_NON_JSON_RESPONSE',
|
|
69
|
-
`Outrank returned HTTP ${response.status} without a JSON body.`,
|
|
70
|
-
guidance,
|
|
71
|
-
{ status: response.status, url: url.toString() },
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
ok: response.ok,
|
|
77
|
-
status: response.status,
|
|
78
|
-
headers: Object.fromEntries(response.headers.entries()),
|
|
79
|
-
payload,
|
|
80
|
-
runtime,
|
|
81
|
-
};
|
|
82
|
-
};
|
package/src/core/args.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { readJsonFile, readStdin, resolvePath } from './io.js';
|
|
2
|
-
|
|
3
|
-
const invalidUsageError = (message) =>
|
|
4
|
-
Object.assign(new Error(message), {
|
|
5
|
-
cliCode: 'CLI_INVALID_USAGE',
|
|
6
|
-
guidance: 'Run the current command again with --help to see the required arguments and input format.',
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
export const parseArgs = (argv) => {
|
|
10
|
-
const positionals = [];
|
|
11
|
-
const options = new Map();
|
|
12
|
-
const flags = new Set();
|
|
13
|
-
|
|
14
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
15
|
-
const token = argv[i];
|
|
16
|
-
|
|
17
|
-
if (token === '--') {
|
|
18
|
-
positionals.push(...argv.slice(i + 1));
|
|
19
|
-
break;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (token === '-h') {
|
|
23
|
-
flags.add('help');
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (token === '-v') {
|
|
28
|
-
flags.add('version');
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (!token.startsWith('--')) {
|
|
33
|
-
positionals.push(token);
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const eqIndex = token.indexOf('=');
|
|
38
|
-
if (eqIndex >= 0) {
|
|
39
|
-
const key = token.slice(2, eqIndex);
|
|
40
|
-
const value = token.slice(eqIndex + 1);
|
|
41
|
-
const current = options.get(key) ?? [];
|
|
42
|
-
current.push(value);
|
|
43
|
-
options.set(key, current);
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const key = token.slice(2);
|
|
48
|
-
const next = argv[i + 1];
|
|
49
|
-
if (next !== undefined && !next.startsWith('--')) {
|
|
50
|
-
const current = options.get(key) ?? [];
|
|
51
|
-
current.push(next);
|
|
52
|
-
options.set(key, current);
|
|
53
|
-
i += 1;
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
flags.add(key);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return { positionals, options, flags };
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export const hasFlag = (parsed, name) => parsed.flags.has(name);
|
|
64
|
-
|
|
65
|
-
export const getOption = (parsed, name) => {
|
|
66
|
-
const values = parsed.options.get(name);
|
|
67
|
-
return values ? values[values.length - 1] : undefined;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export const getOptions = (parsed, name) => parsed.options.get(name) ?? [];
|
|
71
|
-
|
|
72
|
-
export const getRequiredPositional = (parsed, index, label) => {
|
|
73
|
-
const value = parsed.positionals[index];
|
|
74
|
-
if (!value) {
|
|
75
|
-
throw invalidUsageError(`Missing required argument: ${label}.`);
|
|
76
|
-
}
|
|
77
|
-
return value;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export const parseIntegerOption = (parsed, name, { required = false, min, max } = {}) => {
|
|
81
|
-
const raw = getOption(parsed, name);
|
|
82
|
-
if (raw === undefined) {
|
|
83
|
-
if (required) {
|
|
84
|
-
throw invalidUsageError(`Missing required option: --${name}.`);
|
|
85
|
-
}
|
|
86
|
-
return undefined;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const value = Number(raw);
|
|
90
|
-
if (!Number.isInteger(value)) {
|
|
91
|
-
throw invalidUsageError(`--${name} must be an integer.`);
|
|
92
|
-
}
|
|
93
|
-
if (min !== undefined && value < min) {
|
|
94
|
-
throw invalidUsageError(`--${name} must be >= ${min}.`);
|
|
95
|
-
}
|
|
96
|
-
if (max !== undefined && value > max) {
|
|
97
|
-
throw invalidUsageError(`--${name} must be <= ${max}.`);
|
|
98
|
-
}
|
|
99
|
-
return value;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
export const readJsonInput = async (parsed) => {
|
|
103
|
-
const inline = getOption(parsed, 'json');
|
|
104
|
-
const file = getOption(parsed, 'json-file');
|
|
105
|
-
const useStdin = hasFlag(parsed, 'json-stdin');
|
|
106
|
-
|
|
107
|
-
const sources = [inline ? 'inline' : null, file ? 'file' : null, useStdin ? 'stdin' : null].filter(
|
|
108
|
-
Boolean,
|
|
109
|
-
);
|
|
110
|
-
if (sources.length === 0) {
|
|
111
|
-
throw invalidUsageError('Provide one of: --json, --json-file, or --json-stdin.');
|
|
112
|
-
}
|
|
113
|
-
if (sources.length > 1) {
|
|
114
|
-
throw invalidUsageError(
|
|
115
|
-
'Use only one JSON input source: --json, --json-file, or --json-stdin.',
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (inline) {
|
|
120
|
-
try {
|
|
121
|
-
return JSON.parse(inline);
|
|
122
|
-
} catch (error) {
|
|
123
|
-
throw invalidUsageError(`Invalid JSON in --json: ${error.message}`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (file) {
|
|
128
|
-
try {
|
|
129
|
-
return await readJsonFile(resolvePath(file));
|
|
130
|
-
} catch (error) {
|
|
131
|
-
throw invalidUsageError(error.message);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const stdin = await readStdin();
|
|
136
|
-
if (!stdin.trim()) {
|
|
137
|
-
throw invalidUsageError('No JSON was received on stdin.');
|
|
138
|
-
}
|
|
139
|
-
try {
|
|
140
|
-
return JSON.parse(stdin);
|
|
141
|
-
} catch (error) {
|
|
142
|
-
throw invalidUsageError(`Invalid JSON from stdin: ${error.message}`);
|
|
143
|
-
}
|
|
144
|
-
};
|