@postplus/cli 0.1.34 → 0.1.35
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 +27 -5
- package/build/hosted-domain-commands.js +366 -0
- package/build/hosted-request-schemas.js +507 -0
- package/build/hosted-schema-catalog.js +307 -0
- package/build/index.js +35 -0
- package/build/skill-catalog.js +1 -3
- package/build/skill-management.js +22 -15
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -33,7 +33,10 @@ Requires Node.js and npm.
|
|
|
33
33
|
```bash
|
|
34
34
|
npm install -g @postplus/cli@latest
|
|
35
35
|
postplus auth login
|
|
36
|
-
|
|
36
|
+
POSTPLUS_AGENT_TARGETS="claude-code codex cursor github-copilot windsurf trae trae-cn openclaw hermes-agent"
|
|
37
|
+
for agent in $POSTPLUS_AGENT_TARGETS; do
|
|
38
|
+
npx -y skills add PostPlusAI/postplus-skills --global --full-depth --skill '*' --agent "$agent" --yes
|
|
39
|
+
done
|
|
37
40
|
postplus skills verify
|
|
38
41
|
```
|
|
39
42
|
|
|
@@ -41,7 +44,10 @@ If you explicitly do not want global skills, run the install from the target
|
|
|
41
44
|
project directory and omit `--global`:
|
|
42
45
|
|
|
43
46
|
```bash
|
|
44
|
-
|
|
47
|
+
POSTPLUS_AGENT_TARGETS="claude-code codex cursor github-copilot windsurf trae trae-cn openclaw hermes-agent"
|
|
48
|
+
for agent in $POSTPLUS_AGENT_TARGETS; do
|
|
49
|
+
npx -y skills add PostPlusAI/postplus-skills --full-depth --skill '*' --agent "$agent" --yes
|
|
50
|
+
done
|
|
45
51
|
```
|
|
46
52
|
|
|
47
53
|
Useful checks:
|
|
@@ -51,6 +57,22 @@ postplus status
|
|
|
51
57
|
npx -y skills add PostPlusAI/postplus-skills --global --list
|
|
52
58
|
```
|
|
53
59
|
|
|
60
|
+
Hosted request schema discovery:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
postplus research schema --collection-key <collection-key> --json
|
|
64
|
+
postplus media schema --endpoint <endpoint-key> --json
|
|
65
|
+
postplus publish schema --json
|
|
66
|
+
postplus mobile schema --json
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Use these schema commands before an agent writes a `--input` or `--request`
|
|
70
|
+
JSON file for a hosted PostPlus command. For media work, run
|
|
71
|
+
`postplus media schema --json` first to list `endpointKeys`, then rerun with
|
|
72
|
+
the selected `--endpoint`. For research work, run
|
|
73
|
+
`postplus research schema --json` first to list `collectionKeys`, then rerun
|
|
74
|
+
with the selected `--collection-key`.
|
|
75
|
+
|
|
54
76
|
## Local Studio
|
|
55
77
|
|
|
56
78
|
For heavier skills that benefit from a visual workspace, use the CLI-managed
|
|
@@ -384,7 +406,7 @@ Start from the job, not the file name.
|
|
|
384
406
|
|
|
385
407
|
1. Describe the outcome you want in natural language.
|
|
386
408
|
2. If you are unsure which workflow fits, start with a router skill.
|
|
387
|
-
3. Use `skills/
|
|
409
|
+
3. Use `skills/catalog.json` for machine-readable released skill metadata.
|
|
388
410
|
4. Read the target `SKILL.md` before execution.
|
|
389
411
|
5. Follow shared rulebooks when the task crosses platforms, products, ads, media, or publishing.
|
|
390
412
|
6. Chain only the minimum skills needed to produce the next useful artifact.
|
|
@@ -392,9 +414,9 @@ Start from the job, not the file name.
|
|
|
392
414
|
Important files:
|
|
393
415
|
|
|
394
416
|
- `README.md`: this first-time onboarding page
|
|
395
|
-
- `skills/INDEX.md`: detailed agent-facing navigation map
|
|
396
417
|
- `skills/README.md`: short runtime catalog notes
|
|
397
|
-
- `skills/
|
|
418
|
+
- `skills/catalog.json`: released skill metadata for CLI and verification
|
|
419
|
+
- `skills/00-shared/postplus-shared/references/`: shared routing and judgment rules
|
|
398
420
|
- each `SKILL.md`: the workflow contract for one specific capability
|
|
399
421
|
|
|
400
422
|
## First Requests To Try
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { resolveFreshRemoteAuth } from './auth-session.js';
|
|
5
|
+
import { buildPostPlusClientCompatibilityHeaders, formatPostPlusCompatibilityError, } from './client-compatibility.js';
|
|
6
|
+
import { buildHostedRequestSchemaReport, buildMediaGenerationRequestDimensions, } from './hosted-request-schemas.js';
|
|
7
|
+
import { readLargeCreditQuoteConfirmationChallenge, } from './quote-confirmation.js';
|
|
8
|
+
class HostedQuoteConfirmationRequiredError extends Error {
|
|
9
|
+
challenge;
|
|
10
|
+
constructor(message, challenge) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.challenge = challenge;
|
|
13
|
+
this.name = 'HostedQuoteConfirmationRequiredError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const HOSTED_DOMAIN_CAPABILITIES = {
|
|
17
|
+
media: new Set(['media-file', 'media-generation', 'video-analysis']),
|
|
18
|
+
mobile: new Set(['mobile-automation']),
|
|
19
|
+
publish: new Set(['social-publishing']),
|
|
20
|
+
research: new Set([
|
|
21
|
+
'public-content-collection',
|
|
22
|
+
'public-content-discovery',
|
|
23
|
+
]),
|
|
24
|
+
};
|
|
25
|
+
export async function runHostedDomainCommand(domain, args) {
|
|
26
|
+
const [subcommand, ...rest] = args;
|
|
27
|
+
if (domain === 'research') {
|
|
28
|
+
if (subcommand === 'schema') {
|
|
29
|
+
return runHostedSchema(domain, rest);
|
|
30
|
+
}
|
|
31
|
+
if (subcommand === 'collect') {
|
|
32
|
+
return runResearchCollect(rest);
|
|
33
|
+
}
|
|
34
|
+
if (subcommand === 'capability') {
|
|
35
|
+
return runHostedCapability(domain, rest);
|
|
36
|
+
}
|
|
37
|
+
printResearchHelp();
|
|
38
|
+
return subcommand === undefined || isHelp(subcommand) ? 0 : 1;
|
|
39
|
+
}
|
|
40
|
+
if (subcommand === 'schema') {
|
|
41
|
+
return runHostedSchema(domain, rest);
|
|
42
|
+
}
|
|
43
|
+
if (subcommand === 'capability') {
|
|
44
|
+
return runHostedCapability(domain, rest);
|
|
45
|
+
}
|
|
46
|
+
printCapabilityHelp(domain);
|
|
47
|
+
return subcommand === undefined || isHelp(subcommand) ? 0 : 1;
|
|
48
|
+
}
|
|
49
|
+
async function runResearchCollect(args) {
|
|
50
|
+
const flags = parseFlags(args, new Set(['json']));
|
|
51
|
+
const runHandle = flags.values.get('run-handle');
|
|
52
|
+
const outputPath = flags.values.get('output') ?? null;
|
|
53
|
+
if (runHandle) {
|
|
54
|
+
const payload = await postHostedJson({
|
|
55
|
+
body: { runHandle },
|
|
56
|
+
pathName: '/api/postplus-cli/hosted/collection',
|
|
57
|
+
skillName: null,
|
|
58
|
+
});
|
|
59
|
+
await writeResult(payload, outputPath, flags.booleans.has('json'));
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
const skillName = requireFlag(flags, 'skill');
|
|
63
|
+
const collectionKey = requireFlag(flags, 'collection-key');
|
|
64
|
+
const inputPath = requireFlag(flags, 'input');
|
|
65
|
+
const envelope = readHostedEnvelope(await readJsonFile(inputPath), inputPath);
|
|
66
|
+
const operationId = flags.values.get('hosted-operation-id') ??
|
|
67
|
+
normalizeString(envelope.hostedOperationId) ??
|
|
68
|
+
normalizeString(envelope.operationId) ??
|
|
69
|
+
`postplus-cli:research:${collectionKey}:${randomUUID()}`;
|
|
70
|
+
const quoteConfirmationToken = flags.values.get('quote-confirmation-token') ??
|
|
71
|
+
normalizeString(envelope.quoteConfirmationToken);
|
|
72
|
+
const payload = await postHostedJson({
|
|
73
|
+
body: {
|
|
74
|
+
collectionKey,
|
|
75
|
+
input: envelope.input,
|
|
76
|
+
operationId,
|
|
77
|
+
quoteConfirmationToken: quoteConfirmationToken ?? undefined,
|
|
78
|
+
skillName,
|
|
79
|
+
},
|
|
80
|
+
pathName: '/api/postplus-cli/hosted/collection',
|
|
81
|
+
skillName,
|
|
82
|
+
}).catch((error) => buildHostedCommandError(error, {
|
|
83
|
+
inputPath,
|
|
84
|
+
outputPath,
|
|
85
|
+
}));
|
|
86
|
+
await writeResult(payload, outputPath, flags.booleans.has('json'));
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
async function runHostedSchema(domain, args) {
|
|
90
|
+
const flags = parseFlags(args, new Set(['json']));
|
|
91
|
+
const allowedFlags = domain === 'media'
|
|
92
|
+
? new Set(['endpoint'])
|
|
93
|
+
: domain === 'research'
|
|
94
|
+
? new Set(['collection-key'])
|
|
95
|
+
: new Set();
|
|
96
|
+
for (const key of flags.values.keys()) {
|
|
97
|
+
if (!allowedFlags.has(key)) {
|
|
98
|
+
throw new Error(`Unknown option for ${domain} schema: --${key}.`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
writeJson(buildHostedRequestSchemaReport({
|
|
102
|
+
collectionKey: flags.values.get('collection-key') ?? null,
|
|
103
|
+
domain,
|
|
104
|
+
endpointKey: flags.values.get('endpoint') ?? null,
|
|
105
|
+
}));
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
async function runHostedCapability(domain, args) {
|
|
109
|
+
const flags = parseFlags(args, new Set(['json']));
|
|
110
|
+
const requestPath = requireFlag(flags, 'request');
|
|
111
|
+
const outputPath = flags.values.get('output') ?? null;
|
|
112
|
+
const request = await readJsonFile(requestPath);
|
|
113
|
+
if (!request || typeof request !== 'object' || Array.isArray(request)) {
|
|
114
|
+
throw new Error(`Hosted ${domain} capability request must be a JSON object.`);
|
|
115
|
+
}
|
|
116
|
+
const record = request;
|
|
117
|
+
const capability = requireDomainCapability(record, domain);
|
|
118
|
+
const operation = requireRecordString(record, 'operation');
|
|
119
|
+
const operationId = flags.values.get('hosted-operation-id') ??
|
|
120
|
+
normalizeString(record.operationId) ??
|
|
121
|
+
`postplus-cli:${domain}:${capability}:${operation}:${randomUUID()}`;
|
|
122
|
+
const quoteConfirmationToken = flags.values.get('quote-confirmation-token') ??
|
|
123
|
+
normalizeString(record.quoteConfirmationToken);
|
|
124
|
+
const publicRecord = { ...record };
|
|
125
|
+
delete publicRecord.skillName;
|
|
126
|
+
const derivedFields = buildDerivedHostedCapabilityFields({
|
|
127
|
+
capability,
|
|
128
|
+
domain,
|
|
129
|
+
operation,
|
|
130
|
+
record,
|
|
131
|
+
});
|
|
132
|
+
const body = {
|
|
133
|
+
...publicRecord,
|
|
134
|
+
...derivedFields,
|
|
135
|
+
capability,
|
|
136
|
+
operation,
|
|
137
|
+
operationId,
|
|
138
|
+
quoteConfirmationToken: quoteConfirmationToken ?? undefined,
|
|
139
|
+
};
|
|
140
|
+
const skillName = flags.values.get('skill') ?? normalizeString(record.skillName);
|
|
141
|
+
const payload = await postHostedJson({
|
|
142
|
+
body,
|
|
143
|
+
pathName: '/api/postplus-cli/hosted/capability',
|
|
144
|
+
skillName,
|
|
145
|
+
}).catch((error) => buildHostedCommandError(error, {
|
|
146
|
+
inputPath: requestPath,
|
|
147
|
+
outputPath,
|
|
148
|
+
}));
|
|
149
|
+
await writeResult(payload, outputPath, flags.booleans.has('json'));
|
|
150
|
+
return 0;
|
|
151
|
+
}
|
|
152
|
+
function buildDerivedHostedCapabilityFields(input) {
|
|
153
|
+
if (input.domain !== 'media' ||
|
|
154
|
+
input.capability !== 'media-generation' ||
|
|
155
|
+
input.operation !== 'request') {
|
|
156
|
+
return {};
|
|
157
|
+
}
|
|
158
|
+
if (Object.hasOwn(input.record, 'requestDimensions')) {
|
|
159
|
+
throw new Error('Hosted media-generation request must not include requestDimensions. The CLI derives billing dimensions from endpointKey and input.');
|
|
160
|
+
}
|
|
161
|
+
const endpointKey = requireRecordString(input.record, 'endpointKey');
|
|
162
|
+
const mediaInput = requireRecordObject(input.record, 'input');
|
|
163
|
+
return {
|
|
164
|
+
requestDimensions: buildMediaGenerationRequestDimensions(endpointKey, mediaInput),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
async function postHostedJson(input) {
|
|
168
|
+
let auth = await resolveFreshRemoteAuth();
|
|
169
|
+
let response = await postJson({
|
|
170
|
+
apiBaseUrl: auth.apiBaseUrl,
|
|
171
|
+
body: input.body,
|
|
172
|
+
cliSessionToken: auth.cliSessionToken,
|
|
173
|
+
pathName: input.pathName,
|
|
174
|
+
skillName: input.skillName,
|
|
175
|
+
});
|
|
176
|
+
if (response.status === 401) {
|
|
177
|
+
auth = await resolveFreshRemoteAuth({ forceRefresh: true });
|
|
178
|
+
response = await postJson({
|
|
179
|
+
apiBaseUrl: auth.apiBaseUrl,
|
|
180
|
+
body: input.body,
|
|
181
|
+
cliSessionToken: auth.cliSessionToken,
|
|
182
|
+
pathName: input.pathName,
|
|
183
|
+
skillName: input.skillName,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
const payload = await readJsonResponse(response);
|
|
187
|
+
if (!response.ok) {
|
|
188
|
+
const challenge = readLargeCreditQuoteConfirmationChallenge(payload);
|
|
189
|
+
if (challenge) {
|
|
190
|
+
throw new HostedQuoteConfirmationRequiredError(readProductError(payload), challenge);
|
|
191
|
+
}
|
|
192
|
+
const compatibilityError = formatPostPlusCompatibilityError(payload);
|
|
193
|
+
if (compatibilityError) {
|
|
194
|
+
throw new Error(compatibilityError);
|
|
195
|
+
}
|
|
196
|
+
throw new Error(readProductError(payload));
|
|
197
|
+
}
|
|
198
|
+
return payload;
|
|
199
|
+
}
|
|
200
|
+
async function buildHostedCommandError(error, input) {
|
|
201
|
+
if (!(error instanceof HostedQuoteConfirmationRequiredError)) {
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
const challengePath = path.resolve(input.outputPath
|
|
205
|
+
? `${input.outputPath}.quote-confirmation.json`
|
|
206
|
+
: `${input.inputPath}.quote-confirmation.json`);
|
|
207
|
+
await mkdir(path.dirname(challengePath), { recursive: true });
|
|
208
|
+
await writeFile(challengePath, `${JSON.stringify(error.challenge, null, 2)}\n`, {
|
|
209
|
+
encoding: 'utf8',
|
|
210
|
+
mode: 0o600,
|
|
211
|
+
});
|
|
212
|
+
throw new Error([
|
|
213
|
+
error.message,
|
|
214
|
+
`Quote confirmation challenge: ${challengePath}`,
|
|
215
|
+
`Confirm: postplus quote confirm --json --challenge-file "${challengePath}"`,
|
|
216
|
+
'Then rerun the hosted command with --quote-confirmation-token <token>.',
|
|
217
|
+
].join('\n'));
|
|
218
|
+
}
|
|
219
|
+
async function postJson(input) {
|
|
220
|
+
const headers = await buildPostPlusClientCompatibilityHeaders({
|
|
221
|
+
skillName: input.skillName,
|
|
222
|
+
});
|
|
223
|
+
return fetch(`${input.apiBaseUrl}${input.pathName}`, {
|
|
224
|
+
body: JSON.stringify(input.body),
|
|
225
|
+
headers: {
|
|
226
|
+
accept: 'application/json',
|
|
227
|
+
authorization: `Bearer ${input.cliSessionToken}`,
|
|
228
|
+
...headers,
|
|
229
|
+
'content-type': 'application/json',
|
|
230
|
+
},
|
|
231
|
+
method: 'POST',
|
|
232
|
+
signal: AbortSignal.timeout(120000),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
async function readJsonResponse(response) {
|
|
236
|
+
const text = await response.text();
|
|
237
|
+
if (!text.trim()) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
return JSON.parse(text);
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
throw new Error('PostPlus Cloud returned invalid JSON.');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function readProductError(payload) {
|
|
248
|
+
if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
|
|
249
|
+
const record = payload;
|
|
250
|
+
if (typeof record.error === 'string' && record.error.trim()) {
|
|
251
|
+
return record.error.trim();
|
|
252
|
+
}
|
|
253
|
+
if (typeof record.message === 'string' && record.message.trim()) {
|
|
254
|
+
return record.message.trim();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return 'PostPlus hosted capability request failed.';
|
|
258
|
+
}
|
|
259
|
+
async function readJsonFile(filePath) {
|
|
260
|
+
try {
|
|
261
|
+
return JSON.parse(await readFile(filePath, 'utf8'));
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
throw new Error(error instanceof Error
|
|
265
|
+
? `Failed to read JSON file ${filePath}: ${error.message}`
|
|
266
|
+
: `Failed to read JSON file ${filePath}.`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function readHostedEnvelope(value, filePath) {
|
|
270
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
271
|
+
throw new Error(`${filePath} must be a schemaVersion 1 hosted envelope.`);
|
|
272
|
+
}
|
|
273
|
+
const envelope = value;
|
|
274
|
+
if (envelope.schemaVersion !== 1 || !Object.hasOwn(envelope, 'input')) {
|
|
275
|
+
throw new Error(`${filePath} must be a schemaVersion 1 hosted envelope.`);
|
|
276
|
+
}
|
|
277
|
+
return envelope;
|
|
278
|
+
}
|
|
279
|
+
async function writeResult(payload, outputPath, forceStdout) {
|
|
280
|
+
const text = `${JSON.stringify(payload, null, 2)}\n`;
|
|
281
|
+
if (!outputPath || forceStdout) {
|
|
282
|
+
process.stdout.write(text);
|
|
283
|
+
}
|
|
284
|
+
if (outputPath) {
|
|
285
|
+
await mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
|
|
286
|
+
await writeFile(outputPath, text);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function parseFlags(args, booleanFlags) {
|
|
290
|
+
const values = new Map();
|
|
291
|
+
const booleans = new Set();
|
|
292
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
293
|
+
const arg = args[index];
|
|
294
|
+
if (!arg.startsWith('--')) {
|
|
295
|
+
throw new Error(`Unexpected positional argument: ${arg}`);
|
|
296
|
+
}
|
|
297
|
+
const key = arg.slice(2);
|
|
298
|
+
if (booleanFlags.has(key)) {
|
|
299
|
+
booleans.add(key);
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
const value = args[index + 1];
|
|
303
|
+
if (!value || value.startsWith('--')) {
|
|
304
|
+
throw new Error(`Missing value for --${key}.`);
|
|
305
|
+
}
|
|
306
|
+
values.set(key, value);
|
|
307
|
+
index += 1;
|
|
308
|
+
}
|
|
309
|
+
return { booleans, values };
|
|
310
|
+
}
|
|
311
|
+
function requireFlag(flags, key) {
|
|
312
|
+
const value = flags.values.get(key);
|
|
313
|
+
if (!value) {
|
|
314
|
+
throw new Error(`Missing required option --${key}.`);
|
|
315
|
+
}
|
|
316
|
+
return value;
|
|
317
|
+
}
|
|
318
|
+
function requireDomainCapability(record, domain) {
|
|
319
|
+
const capability = requireRecordString(record, 'capability');
|
|
320
|
+
const allowed = HOSTED_DOMAIN_CAPABILITIES[domain];
|
|
321
|
+
if (!allowed.has(capability)) {
|
|
322
|
+
throw new Error(`Hosted ${domain} capability request uses unsupported capability ${capability}.`);
|
|
323
|
+
}
|
|
324
|
+
return capability;
|
|
325
|
+
}
|
|
326
|
+
function requireRecordString(record, key) {
|
|
327
|
+
const value = normalizeString(record[key]);
|
|
328
|
+
if (!value) {
|
|
329
|
+
throw new Error(`Hosted capability request must include string ${key}.`);
|
|
330
|
+
}
|
|
331
|
+
return value;
|
|
332
|
+
}
|
|
333
|
+
function requireRecordObject(record, key) {
|
|
334
|
+
const value = record[key];
|
|
335
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
336
|
+
throw new Error(`Hosted capability request must include object ${key}.`);
|
|
337
|
+
}
|
|
338
|
+
return value;
|
|
339
|
+
}
|
|
340
|
+
function normalizeString(value) {
|
|
341
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
342
|
+
}
|
|
343
|
+
function isHelp(value) {
|
|
344
|
+
return value === 'help' || value === '--help' || value === '-h';
|
|
345
|
+
}
|
|
346
|
+
function printResearchHelp() {
|
|
347
|
+
process.stdout.write(`PostPlus CLI - research commands
|
|
348
|
+
|
|
349
|
+
Usage:
|
|
350
|
+
postplus research schema [--collection-key <key>] [--json]
|
|
351
|
+
postplus research collect --skill <skill-id> --collection-key <key> --input <hosted-envelope.json> [--output <result.json>]
|
|
352
|
+
postplus research collect --run-handle <runHandle> [--output <result.json>]
|
|
353
|
+
postplus research capability --request <hosted-capability-request.json> [--output <result.json>]
|
|
354
|
+
`);
|
|
355
|
+
}
|
|
356
|
+
function printCapabilityHelp(domain) {
|
|
357
|
+
process.stdout.write(`PostPlus CLI - ${domain} commands
|
|
358
|
+
|
|
359
|
+
Usage:
|
|
360
|
+
postplus ${domain} schema${domain === 'media' ? ' [--endpoint <endpoint-key>]' : ''} [--json]
|
|
361
|
+
postplus ${domain} capability --request <hosted-capability-request.json> [--output <result.json>]
|
|
362
|
+
`);
|
|
363
|
+
}
|
|
364
|
+
function writeJson(value) {
|
|
365
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
366
|
+
}
|