@postplus/cli 0.1.34 → 0.1.36
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 +29 -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
|
|
@@ -209,6 +231,7 @@ Example requests:
|
|
|
209
231
|
"Generate a storyboard for a 15-second hook."
|
|
210
232
|
"Transcribe this video, create subtitles, and suggest B-roll placements."
|
|
211
233
|
"Prepare provider-ready image or video generation requests."
|
|
234
|
+
"Turn this product into a UGC-style video workflow with image, voice, clip, montage, and QA handoffs."
|
|
212
235
|
```
|
|
213
236
|
|
|
214
237
|
Typical outputs:
|
|
@@ -218,6 +241,7 @@ Typical outputs:
|
|
|
218
241
|
- hook and pattern breakdowns
|
|
219
242
|
- storyboards and prompt requests
|
|
220
243
|
- B-roll plans and edit-ready packages
|
|
244
|
+
- image, video, audio, and UGC workflow controller handoffs
|
|
221
245
|
- generated media candidates and manifests
|
|
222
246
|
|
|
223
247
|
### Strategy, Copy, SEO, and Growth
|
|
@@ -384,7 +408,7 @@ Start from the job, not the file name.
|
|
|
384
408
|
|
|
385
409
|
1. Describe the outcome you want in natural language.
|
|
386
410
|
2. If you are unsure which workflow fits, start with a router skill.
|
|
387
|
-
3. Use `skills/
|
|
411
|
+
3. Use `skills/catalog.json` for machine-readable released skill metadata.
|
|
388
412
|
4. Read the target `SKILL.md` before execution.
|
|
389
413
|
5. Follow shared rulebooks when the task crosses platforms, products, ads, media, or publishing.
|
|
390
414
|
6. Chain only the minimum skills needed to produce the next useful artifact.
|
|
@@ -392,9 +416,9 @@ Start from the job, not the file name.
|
|
|
392
416
|
Important files:
|
|
393
417
|
|
|
394
418
|
- `README.md`: this first-time onboarding page
|
|
395
|
-
- `skills/INDEX.md`: detailed agent-facing navigation map
|
|
396
419
|
- `skills/README.md`: short runtime catalog notes
|
|
397
|
-
- `skills/
|
|
420
|
+
- `skills/catalog.json`: released skill metadata for CLI and verification
|
|
421
|
+
- `skills/00-shared/postplus-shared/references/`: shared routing and judgment rules
|
|
398
422
|
- each `SKILL.md`: the workflow contract for one specific capability
|
|
399
423
|
|
|
400
424
|
## 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
|
+
}
|