@postplus/cli 0.1.46 → 0.1.47
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/build/authed-cloud-request.d.ts +39 -0
- package/build/authed-cloud-request.js +3 -0
- package/build/client-compatibility.js +5 -2
- package/build/generated/hosted-execution-manifest.generated.js +207 -14
- package/build/hosted-domain-commands.d.ts +14 -0
- package/build/hosted-domain-commands.js +125 -67
- package/build/hosted-lib.d.ts +34 -0
- package/build/hosted-lib.js +45 -0
- package/build/hosted-manifest-index.d.ts +69 -0
- package/build/index.js +6 -4
- package/package.json +13 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type AuthedCloudRequestAuth = {
|
|
2
|
+
apiBaseUrl: string;
|
|
3
|
+
cliSessionToken: string;
|
|
4
|
+
};
|
|
5
|
+
export type AuthedCloudRequestInput = {
|
|
6
|
+
auth: AuthedCloudRequestAuth;
|
|
7
|
+
pathName: string;
|
|
8
|
+
method?: 'GET' | 'POST';
|
|
9
|
+
/**
|
|
10
|
+
* Parsed JSON body. When provided the request sends `content-type:
|
|
11
|
+
* application/json` and the serialized body; when omitted neither is sent.
|
|
12
|
+
*/
|
|
13
|
+
body?: unknown;
|
|
14
|
+
skillName?: string | null;
|
|
15
|
+
/**
|
|
16
|
+
* In-process override for the skills release id header. When provided (the
|
|
17
|
+
* hosted-lib path) it is stamped verbatim and the disk config is NOT read for
|
|
18
|
+
* the release id; when omitted (the bin path) the release id comes from
|
|
19
|
+
* `readLocalConfig()` as before.
|
|
20
|
+
*/
|
|
21
|
+
skillsReleaseId?: string | null;
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Optional once-only 401 refresh. When provided, a `401` response triggers a
|
|
25
|
+
* single retry: `refreshAuth()` returns fresh credentials and the request is
|
|
26
|
+
* re-issued once with them. When omitted, the first response is returned
|
|
27
|
+
* verbatim (the caller keeps its own `!ok` interpretation either way).
|
|
28
|
+
*/
|
|
29
|
+
retryOn401?: () => Promise<AuthedCloudRequestAuth>;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Single transport envelope for every authenticated PostPlus Cloud request:
|
|
33
|
+
* canonical header set (`accept` + compatibility headers + `Bearer` token +
|
|
34
|
+
* optional `content-type`), `AbortSignal.timeout`, and an optional once-only
|
|
35
|
+
* 401-refresh-retry. It returns the raw `Response` so each caller keeps its own
|
|
36
|
+
* `!ok` interpretation — this is a narrow transport primitive, not a request
|
|
37
|
+
* framework.
|
|
38
|
+
*/
|
|
39
|
+
export declare function sendAuthedCloudRequest(input: AuthedCloudRequestInput): Promise<Response>;
|
|
@@ -19,6 +19,9 @@ export async function sendAuthedCloudRequest(input) {
|
|
|
19
19
|
async function issueAuthedCloudRequest(auth, input) {
|
|
20
20
|
const compatibilityHeaders = await buildPostPlusClientCompatibilityHeaders({
|
|
21
21
|
skillName: input.skillName ?? null,
|
|
22
|
+
...(input.skillsReleaseId !== undefined
|
|
23
|
+
? { skillsReleaseId: input.skillsReleaseId }
|
|
24
|
+
: {}),
|
|
22
25
|
});
|
|
23
26
|
const hasBody = input.body !== undefined;
|
|
24
27
|
const headers = {
|
|
@@ -14,16 +14,19 @@ export const POSTPLUS_CLIENT_COMPATIBILITY_HEADERS = {
|
|
|
14
14
|
skillName: 'x-postplus-skill-name',
|
|
15
15
|
};
|
|
16
16
|
export async function buildPostPlusClientCompatibilityHeaders(input = {}) {
|
|
17
|
+
const hasReleaseIdOverride = input.skillsReleaseId !== undefined;
|
|
17
18
|
const [cliVersion, config] = await Promise.all([
|
|
18
19
|
readCurrentCliVersion(),
|
|
19
|
-
readLocalConfig(),
|
|
20
|
+
hasReleaseIdOverride ? Promise.resolve(null) : readLocalConfig(),
|
|
20
21
|
]);
|
|
21
22
|
const headers = {
|
|
22
23
|
[POSTPLUS_CLIENT_COMPATIBILITY_HEADERS.cliVersion]: cliVersion,
|
|
23
24
|
[POSTPLUS_CLIENT_COMPATIBILITY_HEADERS.contractVersion]: String(POSTPLUS_CLIENT_CONTRACT_VERSION),
|
|
24
25
|
[POSTPLUS_CLIENT_COMPATIBILITY_HEADERS.runtime]: POSTPLUS_CLIENT_RUNTIME,
|
|
25
26
|
};
|
|
26
|
-
const skillsReleaseId =
|
|
27
|
+
const skillsReleaseId = hasReleaseIdOverride
|
|
28
|
+
? input.skillsReleaseId?.trim()
|
|
29
|
+
: config?.managedSkills?.releaseId?.trim();
|
|
27
30
|
const skillName = input.skillName?.trim();
|
|
28
31
|
if (skillsReleaseId) {
|
|
29
32
|
headers[POSTPLUS_CLIENT_COMPATIBILITY_HEADERS.skillsReleaseId] =
|
|
@@ -1138,23 +1138,20 @@ export const HOSTED_EXECUTION_MANIFESTS = {
|
|
|
1138
1138
|
"required": true
|
|
1139
1139
|
},
|
|
1140
1140
|
{
|
|
1141
|
-
"name": "
|
|
1142
|
-
"class": "intent",
|
|
1143
|
-
"flag": "--width-and-height",
|
|
1144
|
-
"type": "string",
|
|
1145
|
-
"required": true
|
|
1146
|
-
},
|
|
1147
|
-
{
|
|
1148
|
-
"name": "quality",
|
|
1141
|
+
"name": "aspect_ratio",
|
|
1149
1142
|
"class": "default",
|
|
1150
|
-
"flag": "--
|
|
1143
|
+
"flag": "--aspect-ratio",
|
|
1151
1144
|
"type": "string",
|
|
1152
1145
|
"enumValues": [
|
|
1153
|
-
"
|
|
1154
|
-
"
|
|
1146
|
+
"9:16",
|
|
1147
|
+
"16:9",
|
|
1148
|
+
"4:3",
|
|
1149
|
+
"3:4",
|
|
1150
|
+
"1:1",
|
|
1151
|
+
"2:3",
|
|
1152
|
+
"3:2"
|
|
1155
1153
|
],
|
|
1156
|
-
"
|
|
1157
|
-
"default": "720p",
|
|
1154
|
+
"default": "1:1",
|
|
1158
1155
|
"required": false
|
|
1159
1156
|
},
|
|
1160
1157
|
{
|
|
@@ -1201,7 +1198,7 @@ export const HOSTED_EXECUTION_MANIFESTS = {
|
|
|
1201
1198
|
}
|
|
1202
1199
|
],
|
|
1203
1200
|
"billingDimensions": [
|
|
1204
|
-
"
|
|
1201
|
+
"billableUnitCount"
|
|
1205
1202
|
]
|
|
1206
1203
|
}
|
|
1207
1204
|
]
|
|
@@ -1261,6 +1258,8 @@ export const HOSTED_EXECUTION_MANIFESTS = {
|
|
|
1261
1258
|
"capability": "media-generation",
|
|
1262
1259
|
"endpointKeys": [
|
|
1263
1260
|
"video-seedance-2-image",
|
|
1261
|
+
"video-seedance-2-mini-image",
|
|
1262
|
+
"video-seedance-2-mini-text",
|
|
1264
1263
|
"video-seedance-2-text"
|
|
1265
1264
|
],
|
|
1266
1265
|
"endpoints": [
|
|
@@ -1336,6 +1335,200 @@ export const HOSTED_EXECUTION_MANIFESTS = {
|
|
|
1336
1335
|
"referenceVideoMode"
|
|
1337
1336
|
]
|
|
1338
1337
|
},
|
|
1338
|
+
{
|
|
1339
|
+
"endpointKey": "video-seedance-2-mini-image",
|
|
1340
|
+
"provider": "wavespeed",
|
|
1341
|
+
"providerModelPath": "bytedance/seedance-2.0-mini/image-to-video",
|
|
1342
|
+
"fields": [
|
|
1343
|
+
{
|
|
1344
|
+
"name": "prompt",
|
|
1345
|
+
"class": "intent",
|
|
1346
|
+
"flag": null,
|
|
1347
|
+
"type": "string",
|
|
1348
|
+
"required": true
|
|
1349
|
+
},
|
|
1350
|
+
{
|
|
1351
|
+
"name": "image",
|
|
1352
|
+
"class": "intent",
|
|
1353
|
+
"flag": null,
|
|
1354
|
+
"type": "media-url",
|
|
1355
|
+
"required": true
|
|
1356
|
+
},
|
|
1357
|
+
{
|
|
1358
|
+
"name": "resolution",
|
|
1359
|
+
"class": "default",
|
|
1360
|
+
"flag": null,
|
|
1361
|
+
"type": "string",
|
|
1362
|
+
"enumValues": [
|
|
1363
|
+
"480p",
|
|
1364
|
+
"720p",
|
|
1365
|
+
"1080p",
|
|
1366
|
+
"4k"
|
|
1367
|
+
],
|
|
1368
|
+
"canonicalize": "lowercase",
|
|
1369
|
+
"default": "720p",
|
|
1370
|
+
"required": false
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
"name": "duration",
|
|
1374
|
+
"class": "default",
|
|
1375
|
+
"flag": null,
|
|
1376
|
+
"type": "number",
|
|
1377
|
+
"default": 5,
|
|
1378
|
+
"required": false,
|
|
1379
|
+
"min": 4,
|
|
1380
|
+
"max": 15
|
|
1381
|
+
},
|
|
1382
|
+
{
|
|
1383
|
+
"name": "operationId",
|
|
1384
|
+
"class": "runner-managed",
|
|
1385
|
+
"flag": null,
|
|
1386
|
+
"type": "string",
|
|
1387
|
+
"required": false
|
|
1388
|
+
},
|
|
1389
|
+
{
|
|
1390
|
+
"name": "quoteConfirmationToken",
|
|
1391
|
+
"class": "runner-managed",
|
|
1392
|
+
"flag": null,
|
|
1393
|
+
"type": "string",
|
|
1394
|
+
"required": false
|
|
1395
|
+
},
|
|
1396
|
+
{
|
|
1397
|
+
"name": "requestDimensions",
|
|
1398
|
+
"class": "runner-managed",
|
|
1399
|
+
"flag": null,
|
|
1400
|
+
"type": "string",
|
|
1401
|
+
"required": false
|
|
1402
|
+
}
|
|
1403
|
+
],
|
|
1404
|
+
"billingDimensions": [
|
|
1405
|
+
"duration",
|
|
1406
|
+
"resolution",
|
|
1407
|
+
"referenceVideoCount",
|
|
1408
|
+
"referenceVideoMode"
|
|
1409
|
+
]
|
|
1410
|
+
},
|
|
1411
|
+
{
|
|
1412
|
+
"endpointKey": "video-seedance-2-mini-text",
|
|
1413
|
+
"provider": "wavespeed",
|
|
1414
|
+
"providerModelPath": "bytedance/seedance-2.0-mini/text-to-video",
|
|
1415
|
+
"fields": [
|
|
1416
|
+
{
|
|
1417
|
+
"name": "prompt",
|
|
1418
|
+
"class": "intent",
|
|
1419
|
+
"flag": null,
|
|
1420
|
+
"type": "string",
|
|
1421
|
+
"required": true
|
|
1422
|
+
},
|
|
1423
|
+
{
|
|
1424
|
+
"name": "resolution",
|
|
1425
|
+
"class": "default",
|
|
1426
|
+
"flag": null,
|
|
1427
|
+
"type": "string",
|
|
1428
|
+
"enumValues": [
|
|
1429
|
+
"480p",
|
|
1430
|
+
"720p",
|
|
1431
|
+
"1080p",
|
|
1432
|
+
"4k"
|
|
1433
|
+
],
|
|
1434
|
+
"canonicalize": "lowercase",
|
|
1435
|
+
"default": "720p",
|
|
1436
|
+
"required": false
|
|
1437
|
+
},
|
|
1438
|
+
{
|
|
1439
|
+
"name": "aspect_ratio",
|
|
1440
|
+
"class": "intent",
|
|
1441
|
+
"flag": null,
|
|
1442
|
+
"type": "string",
|
|
1443
|
+
"enumValues": [
|
|
1444
|
+
"21:9",
|
|
1445
|
+
"16:9",
|
|
1446
|
+
"4:3",
|
|
1447
|
+
"1:1",
|
|
1448
|
+
"3:4",
|
|
1449
|
+
"9:16"
|
|
1450
|
+
],
|
|
1451
|
+
"required": false
|
|
1452
|
+
},
|
|
1453
|
+
{
|
|
1454
|
+
"name": "duration",
|
|
1455
|
+
"class": "default",
|
|
1456
|
+
"flag": null,
|
|
1457
|
+
"type": "number",
|
|
1458
|
+
"default": 5,
|
|
1459
|
+
"required": false,
|
|
1460
|
+
"min": 4,
|
|
1461
|
+
"max": 15
|
|
1462
|
+
},
|
|
1463
|
+
{
|
|
1464
|
+
"name": "reference_images",
|
|
1465
|
+
"class": "intent",
|
|
1466
|
+
"flag": null,
|
|
1467
|
+
"type": "media-url",
|
|
1468
|
+
"repeatable": true,
|
|
1469
|
+
"required": false
|
|
1470
|
+
},
|
|
1471
|
+
{
|
|
1472
|
+
"name": "reference_videos",
|
|
1473
|
+
"class": "intent",
|
|
1474
|
+
"flag": null,
|
|
1475
|
+
"type": "media-url",
|
|
1476
|
+
"repeatable": true,
|
|
1477
|
+
"required": false
|
|
1478
|
+
},
|
|
1479
|
+
{
|
|
1480
|
+
"name": "reference_audios",
|
|
1481
|
+
"class": "intent",
|
|
1482
|
+
"flag": null,
|
|
1483
|
+
"type": "media-url",
|
|
1484
|
+
"repeatable": true,
|
|
1485
|
+
"required": false
|
|
1486
|
+
},
|
|
1487
|
+
{
|
|
1488
|
+
"name": "enable_web_search",
|
|
1489
|
+
"class": "default",
|
|
1490
|
+
"flag": null,
|
|
1491
|
+
"type": "boolean",
|
|
1492
|
+
"default": false,
|
|
1493
|
+
"required": false
|
|
1494
|
+
},
|
|
1495
|
+
{
|
|
1496
|
+
"name": "generate_audio",
|
|
1497
|
+
"class": "default",
|
|
1498
|
+
"flag": null,
|
|
1499
|
+
"type": "boolean",
|
|
1500
|
+
"default": true,
|
|
1501
|
+
"required": false
|
|
1502
|
+
},
|
|
1503
|
+
{
|
|
1504
|
+
"name": "operationId",
|
|
1505
|
+
"class": "runner-managed",
|
|
1506
|
+
"flag": null,
|
|
1507
|
+
"type": "string",
|
|
1508
|
+
"required": false
|
|
1509
|
+
},
|
|
1510
|
+
{
|
|
1511
|
+
"name": "quoteConfirmationToken",
|
|
1512
|
+
"class": "runner-managed",
|
|
1513
|
+
"flag": null,
|
|
1514
|
+
"type": "string",
|
|
1515
|
+
"required": false
|
|
1516
|
+
},
|
|
1517
|
+
{
|
|
1518
|
+
"name": "requestDimensions",
|
|
1519
|
+
"class": "runner-managed",
|
|
1520
|
+
"flag": null,
|
|
1521
|
+
"type": "string",
|
|
1522
|
+
"required": false
|
|
1523
|
+
}
|
|
1524
|
+
],
|
|
1525
|
+
"billingDimensions": [
|
|
1526
|
+
"duration",
|
|
1527
|
+
"resolution",
|
|
1528
|
+
"referenceVideoCount",
|
|
1529
|
+
"referenceVideoMode"
|
|
1530
|
+
]
|
|
1531
|
+
},
|
|
1339
1532
|
{
|
|
1340
1533
|
"endpointKey": "video-seedance-2-text",
|
|
1341
1534
|
"provider": "wavespeed",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type AuthedCloudRequestAuth } from './authed-cloud-request.js';
|
|
2
|
+
import { type HostedDomain } from './hosted-manifest-index.js';
|
|
3
|
+
export type HostedRequestContext = {
|
|
4
|
+
auth: AuthedCloudRequestAuth;
|
|
5
|
+
skillsReleaseId?: string;
|
|
6
|
+
/**
|
|
7
|
+
* The request-json envelope injected in place of a `--request <file>` read.
|
|
8
|
+
* Surfaces that need a body assert it is present and the right shape (object vs
|
|
9
|
+
* array) exactly as the file-read path validated the parsed file contents.
|
|
10
|
+
*/
|
|
11
|
+
requestJson?: Record<string, unknown> | unknown[];
|
|
12
|
+
};
|
|
13
|
+
export declare function runHostedDomainCommand(domain: HostedDomain, args: string[], context?: HostedRequestContext): Promise<number | unknown>;
|
|
14
|
+
export declare function runMediaFileCommand(args: string[], context?: HostedRequestContext): Promise<number | unknown>;
|
|
@@ -3,7 +3,7 @@ import { createReadStream } from 'node:fs';
|
|
|
3
3
|
import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { resolveFreshRemoteAuth } from './auth-session.js';
|
|
6
|
-
import { sendAuthedCloudRequest } from './authed-cloud-request.js';
|
|
6
|
+
import { sendAuthedCloudRequest, } from './authed-cloud-request.js';
|
|
7
7
|
import { formatPostPlusCompatibilityError } from './client-compatibility.js';
|
|
8
8
|
import { assertModelledFieldValuesInRange } from './hosted-field-validation.js';
|
|
9
9
|
import { buildVerbTargetIndex, } from './hosted-manifest-index.js';
|
|
@@ -27,6 +27,20 @@ function buildPublishVerbIndex() {
|
|
|
27
27
|
}
|
|
28
28
|
return index;
|
|
29
29
|
}
|
|
30
|
+
// Reads the request-json body for a surface: from the injected object (lib path)
|
|
31
|
+
// or by reading `--request <file>` (bin path). This is the SINGLE place the two
|
|
32
|
+
// paths diverge on input source; the resolved body then flows through the SAME
|
|
33
|
+
// validation + envelope build, so the URL/body/headers stay byte-identical.
|
|
34
|
+
async function resolveRequestBody(context, flags) {
|
|
35
|
+
if (context) {
|
|
36
|
+
if (context.requestJson === undefined) {
|
|
37
|
+
throw new Error('This hosted command requires a requestJson body.');
|
|
38
|
+
}
|
|
39
|
+
return { body: context.requestJson, errorInputLabel: 'requestJson' };
|
|
40
|
+
}
|
|
41
|
+
const requestPath = requireFlag(flags, 'request');
|
|
42
|
+
return { body: await readJsonFile(requestPath), errorInputLabel: requestPath };
|
|
43
|
+
}
|
|
30
44
|
class HostedQuoteConfirmationRequiredError extends Error {
|
|
31
45
|
challenge;
|
|
32
46
|
constructor(message, challenge) {
|
|
@@ -43,41 +57,45 @@ class HostedProductRequestError extends Error {
|
|
|
43
57
|
this.name = 'HostedProductRequestError';
|
|
44
58
|
}
|
|
45
59
|
}
|
|
46
|
-
export async function runHostedDomainCommand(domain, args
|
|
60
|
+
export async function runHostedDomainCommand(domain, args,
|
|
61
|
+
// Present only on the in-process hosted-lib path; the bin path never passes it.
|
|
62
|
+
// See HostedRequestContext: it carries the injected auth/releaseId/requestJson
|
|
63
|
+
// and switches every leaf onto the no-disk, no-file, return-payload behavior.
|
|
64
|
+
context) {
|
|
47
65
|
const [subcommand, ...rest] = args;
|
|
48
66
|
if (domain === 'research') {
|
|
49
67
|
if (subcommand === 'schema') {
|
|
50
|
-
return runHostedSchema(domain, rest);
|
|
68
|
+
return runHostedSchema(domain, rest, context);
|
|
51
69
|
}
|
|
52
70
|
if (subcommand === 'collect') {
|
|
53
|
-
return runResearchCollect(rest);
|
|
71
|
+
return runResearchCollect(rest, context);
|
|
54
72
|
}
|
|
55
73
|
if (subcommand === 'scrape') {
|
|
56
|
-
return runResearchScrape(rest);
|
|
74
|
+
return runResearchScrape(rest, context);
|
|
57
75
|
}
|
|
58
76
|
printResearchHelp();
|
|
59
77
|
return subcommand === undefined || isHelp(subcommand) ? 0 : 1;
|
|
60
78
|
}
|
|
61
79
|
if (subcommand === 'schema') {
|
|
62
|
-
return runHostedSchema(domain, rest);
|
|
80
|
+
return runHostedSchema(domain, rest, context);
|
|
63
81
|
}
|
|
64
82
|
// Poll a pending async media-generation run by handle. This is a hand-coded
|
|
65
83
|
// branch (not a manifest verb) because a status poll has no endpointKey/field
|
|
66
84
|
// contract to project — exactly like the `research collect --run-handle`
|
|
67
85
|
// polling branch. It must be checked before the manifest verb dispatch.
|
|
68
86
|
if (domain === 'media' && subcommand === 'poll') {
|
|
69
|
-
return runMediaPoll(rest);
|
|
87
|
+
return runMediaPoll(rest, context);
|
|
70
88
|
}
|
|
71
89
|
if (domain === 'media' &&
|
|
72
90
|
subcommand &&
|
|
73
91
|
MEDIA_VERB_ENDPOINTS.has(subcommand)) {
|
|
74
|
-
return runMediaVerb(subcommand, rest);
|
|
92
|
+
return runMediaVerb(subcommand, rest, context);
|
|
75
93
|
}
|
|
76
94
|
// publish: the OPERATION is the subcommand (no separate target positional).
|
|
77
95
|
if (domain === 'publish' &&
|
|
78
96
|
subcommand &&
|
|
79
97
|
PUBLISH_VERB_OPERATIONS.has(subcommand)) {
|
|
80
|
-
return runPublishOperation(subcommand, rest);
|
|
98
|
+
return runPublishOperation(subcommand, rest, context);
|
|
81
99
|
}
|
|
82
100
|
printDomainVerbHelp(domain);
|
|
83
101
|
return subcommand === undefined || isHelp(subcommand) ? 0 : 1;
|
|
@@ -87,7 +105,7 @@ export async function runHostedDomainCommand(domain, args) {
|
|
|
87
105
|
// scalar intent/default fields to flags, a request-json surface reads the nested
|
|
88
106
|
// envelope from `--request <file>`. Either way runner-managed fields (billing
|
|
89
107
|
// dimensions, ids, tokens) are derived/minted by the runner, never agent-supplied.
|
|
90
|
-
async function runMediaVerb(verb, args) {
|
|
108
|
+
async function runMediaVerb(verb, args, context) {
|
|
91
109
|
const targets = MEDIA_VERB_ENDPOINTS.get(verb);
|
|
92
110
|
if (!targets) {
|
|
93
111
|
throw new Error(`Unknown media verb ${verb}.`);
|
|
@@ -112,6 +130,7 @@ async function runMediaVerb(verb, args) {
|
|
|
112
130
|
modelKey: targetKey,
|
|
113
131
|
resolved,
|
|
114
132
|
verb,
|
|
133
|
+
context,
|
|
115
134
|
});
|
|
116
135
|
}
|
|
117
136
|
if (resolved.surface === 'request-json') {
|
|
@@ -120,6 +139,7 @@ async function runMediaVerb(verb, args) {
|
|
|
120
139
|
endpointKey: targetKey,
|
|
121
140
|
resolved,
|
|
122
141
|
verb,
|
|
142
|
+
context,
|
|
123
143
|
});
|
|
124
144
|
}
|
|
125
145
|
return runMediaVerbFlags({
|
|
@@ -127,12 +147,13 @@ async function runMediaVerb(verb, args) {
|
|
|
127
147
|
endpointKey: targetKey,
|
|
128
148
|
resolved,
|
|
129
149
|
verb,
|
|
150
|
+
context,
|
|
130
151
|
});
|
|
131
152
|
}
|
|
132
153
|
// Flags surface (e.g. audio-transcription): scalar intent/default fields map to
|
|
133
154
|
// flags; runner-managed fields have no flag so the agent cannot pass them.
|
|
134
155
|
async function runMediaVerbFlags(args) {
|
|
135
|
-
const { endpointKey, resolved, verb } = args;
|
|
156
|
+
const { endpointKey, resolved, verb, context } = args;
|
|
136
157
|
const endpoint = requireResolvedEndpoint(resolved, verb, endpointKey);
|
|
137
158
|
const fields = endpoint.fields;
|
|
138
159
|
const flagToField = new Map();
|
|
@@ -193,6 +214,7 @@ async function runMediaVerbFlags(args) {
|
|
|
193
214
|
outputPath,
|
|
194
215
|
quoteConfirmationToken: flags.values.get('quote-confirmation-token'),
|
|
195
216
|
skillName: flags.values.get('skill') ?? resolved.skill,
|
|
217
|
+
context,
|
|
196
218
|
});
|
|
197
219
|
}
|
|
198
220
|
// Request-json surface (e.g. seedance-submitter): the nested envelope is supplied
|
|
@@ -200,7 +222,7 @@ async function runMediaVerbFlags(args) {
|
|
|
200
222
|
// so the body carries only the media-generation input. Runner-managed fields have
|
|
201
223
|
// no flag and must not appear in the body — the CLI fast-fails if they do.
|
|
202
224
|
async function runMediaVerbRequestJson(args) {
|
|
203
|
-
const { endpointKey, resolved, verb } = args;
|
|
225
|
+
const { endpointKey, resolved, verb, context } = args;
|
|
204
226
|
const endpoint = requireResolvedEndpoint(resolved, verb, endpointKey);
|
|
205
227
|
const flags = parseFlags(args.args, new Set(['json']));
|
|
206
228
|
const allowedKeys = new Set([
|
|
@@ -216,9 +238,8 @@ async function runMediaVerbRequestJson(args) {
|
|
|
216
238
|
throw new Error(`Unknown option for media ${verb}: --${key}.`);
|
|
217
239
|
}
|
|
218
240
|
}
|
|
219
|
-
const requestPath = requireFlag(flags, 'request');
|
|
220
241
|
const outputPath = flags.values.get('output') ?? null;
|
|
221
|
-
const raw = await
|
|
242
|
+
const { body: raw, errorInputLabel } = await resolveRequestBody(context, flags);
|
|
222
243
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
223
244
|
throw new Error(`media ${verb} ${endpointKey} --request must be a JSON object of media-generation input.`);
|
|
224
245
|
}
|
|
@@ -239,7 +260,7 @@ async function runMediaVerbRequestJson(args) {
|
|
|
239
260
|
return submitMediaGenerationRequest({
|
|
240
261
|
capability: resolved.capability,
|
|
241
262
|
endpointKey,
|
|
242
|
-
errorInputLabel
|
|
263
|
+
errorInputLabel,
|
|
243
264
|
input,
|
|
244
265
|
json: flags.booleans.has('json'),
|
|
245
266
|
operationId: flags.values.get('hosted-operation-id') ??
|
|
@@ -247,6 +268,7 @@ async function runMediaVerbRequestJson(args) {
|
|
|
247
268
|
outputPath,
|
|
248
269
|
quoteConfirmationToken: flags.values.get('quote-confirmation-token'),
|
|
249
270
|
skillName: flags.values.get('skill') ?? resolved.skill,
|
|
271
|
+
context,
|
|
250
272
|
});
|
|
251
273
|
}
|
|
252
274
|
function requireResolvedEndpoint(resolved, verb, endpointKey) {
|
|
@@ -264,7 +286,7 @@ function requireResolvedEndpoint(resolved, verb, endpointKey) {
|
|
|
264
286
|
// `estimatedUsage.videoSeconds` so the Web boundary can route eligible short
|
|
265
287
|
// videos through its preflight/routing path (omit it to use the default route).
|
|
266
288
|
async function runVideoAnalysisVerb(args) {
|
|
267
|
-
const { modelKey, resolved, verb } = args;
|
|
289
|
+
const { modelKey, resolved, verb, context } = args;
|
|
268
290
|
const flags = parseFlags(args.args, new Set(['json']));
|
|
269
291
|
const allowedKeys = new Set([
|
|
270
292
|
'hosted-operation-id',
|
|
@@ -280,9 +302,8 @@ async function runVideoAnalysisVerb(args) {
|
|
|
280
302
|
throw new Error(`Unknown option for media ${verb}: --${key}.`);
|
|
281
303
|
}
|
|
282
304
|
}
|
|
283
|
-
const requestPath = requireFlag(flags, 'request');
|
|
284
305
|
const outputPath = flags.values.get('output') ?? null;
|
|
285
|
-
const raw = await
|
|
306
|
+
const { body: raw, errorInputLabel } = await resolveRequestBody(context, flags);
|
|
286
307
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
287
308
|
throw new Error(`media ${verb} ${modelKey} --request must be a JSON object of Gemini request payload.`);
|
|
288
309
|
}
|
|
@@ -311,16 +332,17 @@ async function runVideoAnalysisVerb(args) {
|
|
|
311
332
|
`postplus-cli:media:video-analysis:analyze:${randomUUID()}`,
|
|
312
333
|
quoteConfirmationToken: flags.values.get('quote-confirmation-token') ?? undefined,
|
|
313
334
|
};
|
|
314
|
-
return
|
|
335
|
+
return dispatchHostedCommand({
|
|
315
336
|
request: () => postHostedJson({
|
|
316
337
|
body,
|
|
317
338
|
pathName: '/api/postplus-cli/hosted/capability',
|
|
318
339
|
skillName: flags.values.get('skill') ?? resolved.skill,
|
|
340
|
+
context,
|
|
319
341
|
}),
|
|
320
|
-
errorInputLabel
|
|
342
|
+
errorInputLabel,
|
|
321
343
|
json: flags.booleans.has('json'),
|
|
322
344
|
outputPath,
|
|
323
|
-
});
|
|
345
|
+
}, context);
|
|
324
346
|
}
|
|
325
347
|
// `media-file upload`: the generic local-file -> hosted media verb. Released
|
|
326
348
|
// skills ship no scripts, so a skill that must place a local file behind hosted
|
|
@@ -346,15 +368,15 @@ function inferUploadMimeType(filePath) {
|
|
|
346
368
|
return (MEDIA_FILE_MIME_BY_EXTENSION[path.extname(filePath).toLowerCase()] ??
|
|
347
369
|
'application/octet-stream');
|
|
348
370
|
}
|
|
349
|
-
export async function runMediaFileCommand(args) {
|
|
371
|
+
export async function runMediaFileCommand(args, context) {
|
|
350
372
|
const [subcommand, ...rest] = args;
|
|
351
373
|
if (subcommand === 'upload') {
|
|
352
|
-
return runMediaFileUpload(rest);
|
|
374
|
+
return runMediaFileUpload(rest, context);
|
|
353
375
|
}
|
|
354
376
|
printMediaFileHelp();
|
|
355
377
|
return subcommand === undefined || isHelp(subcommand) ? 0 : 1;
|
|
356
378
|
}
|
|
357
|
-
async function runMediaFileUpload(args) {
|
|
379
|
+
async function runMediaFileUpload(args, context) {
|
|
358
380
|
const flags = parseFlags(args, new Set(['json']));
|
|
359
381
|
const allowedKeys = new Set([
|
|
360
382
|
'hosted-operation-id',
|
|
@@ -391,12 +413,13 @@ async function runMediaFileUpload(args) {
|
|
|
391
413
|
`postplus-cli:media-file:create-upload-url:${randomUUID()}`,
|
|
392
414
|
quoteConfirmationToken: flags.values.get('quote-confirmation-token') ?? undefined,
|
|
393
415
|
};
|
|
394
|
-
return
|
|
416
|
+
return dispatchHostedCommand({
|
|
395
417
|
request: async () => {
|
|
396
418
|
const payload = await postHostedJson({
|
|
397
419
|
body,
|
|
398
420
|
pathName: '/api/postplus-cli/hosted/capability',
|
|
399
421
|
skillName: flags.values.get('skill') ?? null,
|
|
422
|
+
context,
|
|
400
423
|
});
|
|
401
424
|
const output = readHostedUploadOutput(payload);
|
|
402
425
|
const signedUpload = readSignedUpload(output);
|
|
@@ -418,12 +441,13 @@ async function runMediaFileUpload(args) {
|
|
|
418
441
|
},
|
|
419
442
|
pathName: '/api/postplus-cli/hosted/capability',
|
|
420
443
|
skillName: flags.values.get('skill') ?? null,
|
|
444
|
+
context,
|
|
421
445
|
});
|
|
422
446
|
},
|
|
423
447
|
errorInputLabel: inputFile,
|
|
424
448
|
json: flags.booleans.has('json'),
|
|
425
449
|
outputPath,
|
|
426
|
-
});
|
|
450
|
+
}, context);
|
|
427
451
|
}
|
|
428
452
|
function readHostedUploadOutput(payload) {
|
|
429
453
|
if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
|
|
@@ -503,16 +527,17 @@ function submitMediaGenerationRequest(params) {
|
|
|
503
527
|
operationId: params.operationId,
|
|
504
528
|
quoteConfirmationToken: params.quoteConfirmationToken ?? undefined,
|
|
505
529
|
};
|
|
506
|
-
return
|
|
530
|
+
return dispatchHostedCommand({
|
|
507
531
|
request: () => postHostedJson({
|
|
508
532
|
body,
|
|
509
533
|
pathName: '/api/postplus-cli/hosted/capability',
|
|
510
534
|
skillName: params.skillName,
|
|
535
|
+
context: params.context,
|
|
511
536
|
}),
|
|
512
537
|
errorInputLabel: params.errorInputLabel,
|
|
513
538
|
json: params.json,
|
|
514
539
|
outputPath: params.outputPath,
|
|
515
|
-
});
|
|
540
|
+
}, params.context);
|
|
516
541
|
}
|
|
517
542
|
// Poll a pending media-generation run: `postplus media poll --handle <run-id>`.
|
|
518
543
|
// A media `create`/`transcribe`/`analyze` submit returns an async run handle
|
|
@@ -524,11 +549,11 @@ function submitMediaGenerationRequest(params) {
|
|
|
524
549
|
// The body carries only the status quadruple; submit-only fields (input,
|
|
525
550
|
// requestDimensions, quoteConfirmationToken) are never sent. Mirrors the
|
|
526
551
|
// `research collect --run-handle` polling branch.
|
|
527
|
-
async function runMediaPoll(args) {
|
|
552
|
+
async function runMediaPoll(args, context) {
|
|
528
553
|
const flags = parseFlags(args, new Set(['json']));
|
|
529
554
|
const handle = requireFlag(flags, 'handle');
|
|
530
555
|
const outputPath = flags.values.get('output') ?? null;
|
|
531
|
-
return
|
|
556
|
+
return dispatchHostedCommand({
|
|
532
557
|
request: () => postHostedJson({
|
|
533
558
|
body: {
|
|
534
559
|
capability: 'media-generation',
|
|
@@ -538,11 +563,12 @@ async function runMediaPoll(args) {
|
|
|
538
563
|
},
|
|
539
564
|
pathName: '/api/postplus-cli/hosted/capability',
|
|
540
565
|
skillName: null,
|
|
566
|
+
context,
|
|
541
567
|
}),
|
|
542
568
|
errorInputLabel: 'media-poll-handle',
|
|
543
569
|
json: flags.booleans.has('json'),
|
|
544
570
|
outputPath,
|
|
545
|
-
});
|
|
571
|
+
}, context);
|
|
546
572
|
}
|
|
547
573
|
function buildMediaVerbInput(input) {
|
|
548
574
|
const record = {};
|
|
@@ -606,23 +632,24 @@ function buildMediaVerbInput(input) {
|
|
|
606
632
|
// schemaVersion envelope), and posts to /hosted/collection. The resolved entry
|
|
607
633
|
// gives the default skillName (overridable by `--skill`); the actorId stays
|
|
608
634
|
// internal and is never placed on the public body.
|
|
609
|
-
async function runResearchCollect(args) {
|
|
635
|
+
async function runResearchCollect(args, context) {
|
|
610
636
|
const [first, ...rest] = args;
|
|
611
637
|
// Polling path: `research collect --run-handle <h>`. No positional collectionKey.
|
|
612
638
|
if (!first || first.startsWith('--')) {
|
|
613
639
|
const flags = parseFlags(args, new Set(['json']));
|
|
614
640
|
const runHandle = requireFlag(flags, 'run-handle');
|
|
615
641
|
const outputPath = flags.values.get('output') ?? null;
|
|
616
|
-
return
|
|
642
|
+
return dispatchHostedCommand({
|
|
617
643
|
request: () => postHostedJson({
|
|
618
644
|
body: { runHandle, runHandleType: 'hosted-collection' },
|
|
619
645
|
pathName: '/api/postplus-cli/hosted/collection',
|
|
620
646
|
skillName: null,
|
|
647
|
+
context,
|
|
621
648
|
}),
|
|
622
649
|
errorInputLabel: 'research-collect-run-handle',
|
|
623
650
|
json: flags.booleans.has('json'),
|
|
624
651
|
outputPath,
|
|
625
|
-
});
|
|
652
|
+
}, context);
|
|
626
653
|
}
|
|
627
654
|
const verb = 'collect';
|
|
628
655
|
const collectionKey = first;
|
|
@@ -652,9 +679,8 @@ async function runResearchCollect(args) {
|
|
|
652
679
|
throw new Error(`Unknown option for research ${verb}: --${key}.`);
|
|
653
680
|
}
|
|
654
681
|
}
|
|
655
|
-
const requestPath = requireFlag(flags, 'request');
|
|
656
682
|
const outputPath = flags.values.get('output') ?? null;
|
|
657
|
-
const raw = await
|
|
683
|
+
const { body: raw, errorInputLabel } = await resolveRequestBody(context, flags);
|
|
658
684
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
659
685
|
throw new Error(`research ${verb} ${collectionKey} --request must be a JSON object of collection input.`);
|
|
660
686
|
}
|
|
@@ -673,7 +699,7 @@ async function runResearchCollect(args) {
|
|
|
673
699
|
}
|
|
674
700
|
maxTotalChargeUsd = parsed;
|
|
675
701
|
}
|
|
676
|
-
return
|
|
702
|
+
return dispatchHostedCommand({
|
|
677
703
|
request: () => postHostedJson({
|
|
678
704
|
body: {
|
|
679
705
|
collectionKey,
|
|
@@ -685,11 +711,12 @@ async function runResearchCollect(args) {
|
|
|
685
711
|
},
|
|
686
712
|
pathName: '/api/postplus-cli/hosted/collection',
|
|
687
713
|
skillName,
|
|
714
|
+
context,
|
|
688
715
|
}),
|
|
689
|
-
errorInputLabel
|
|
716
|
+
errorInputLabel,
|
|
690
717
|
json: flags.booleans.has('json'),
|
|
691
718
|
outputPath,
|
|
692
|
-
});
|
|
719
|
+
}, context);
|
|
693
720
|
}
|
|
694
721
|
// Manifest-driven public-content-collection verb (request-json surface). Resolves
|
|
695
722
|
// the positional `<sourceKey>` against the research verb index for verb `scrape`,
|
|
@@ -698,7 +725,7 @@ async function runResearchCollect(args) {
|
|
|
698
725
|
// capability `public-content-collection` / operation `scrape`. The resolved entry
|
|
699
726
|
// gives the default skillName (overridable by `--skill`); the datasetId stays
|
|
700
727
|
// internal and is never placed on the public body.
|
|
701
|
-
async function runResearchScrape(args) {
|
|
728
|
+
async function runResearchScrape(args, context) {
|
|
702
729
|
const [first, ...rest] = args;
|
|
703
730
|
const verb = 'scrape';
|
|
704
731
|
const targets = RESEARCH_VERB_TARGETS.get(verb);
|
|
@@ -706,16 +733,17 @@ async function runResearchScrape(args) {
|
|
|
706
733
|
const flags = parseFlags(args, new Set(['json']));
|
|
707
734
|
const runHandle = requireFlag(flags, 'run-handle');
|
|
708
735
|
const outputPath = flags.values.get('output') ?? null;
|
|
709
|
-
return
|
|
736
|
+
return dispatchHostedCommand({
|
|
710
737
|
request: () => postHostedJson({
|
|
711
738
|
body: { runHandle, runHandleType: 'public-content-collection' },
|
|
712
739
|
pathName: '/api/postplus-cli/hosted/collection',
|
|
713
740
|
skillName: null,
|
|
741
|
+
context,
|
|
714
742
|
}),
|
|
715
743
|
errorInputLabel: 'research-scrape-run-handle',
|
|
716
744
|
json: flags.booleans.has('json'),
|
|
717
745
|
outputPath,
|
|
718
|
-
});
|
|
746
|
+
}, context);
|
|
719
747
|
}
|
|
720
748
|
const sourceKey = first;
|
|
721
749
|
const resolved = targets?.get(sourceKey);
|
|
@@ -743,9 +771,8 @@ async function runResearchScrape(args) {
|
|
|
743
771
|
throw new Error(`Unknown option for research ${verb}: --${key}.`);
|
|
744
772
|
}
|
|
745
773
|
}
|
|
746
|
-
const requestPath = requireFlag(flags, 'request');
|
|
747
774
|
const outputPath = flags.values.get('output') ?? null;
|
|
748
|
-
const raw = await
|
|
775
|
+
const { body: raw, errorInputLabel } = await resolveRequestBody(context, flags);
|
|
749
776
|
if (!Array.isArray(raw) || raw.length === 0) {
|
|
750
777
|
throw new Error(`research ${verb} ${sourceKey} --request must be a non-empty JSON array of scrape input records.`);
|
|
751
778
|
}
|
|
@@ -766,7 +793,7 @@ async function runResearchScrape(args) {
|
|
|
766
793
|
}
|
|
767
794
|
// The Web /hosted/capability scrape contract is a strict object: skillName is
|
|
768
795
|
// carried as the compatibility header (postHostedJson), never on the public body.
|
|
769
|
-
return
|
|
796
|
+
return dispatchHostedCommand({
|
|
770
797
|
request: () => postHostedJson({
|
|
771
798
|
body: {
|
|
772
799
|
capability: 'public-content-collection',
|
|
@@ -779,11 +806,12 @@ async function runResearchScrape(args) {
|
|
|
779
806
|
},
|
|
780
807
|
pathName: '/api/postplus-cli/hosted/capability',
|
|
781
808
|
skillName,
|
|
809
|
+
context,
|
|
782
810
|
}),
|
|
783
|
-
errorInputLabel
|
|
811
|
+
errorInputLabel,
|
|
784
812
|
json: flags.booleans.has('json'),
|
|
785
813
|
outputPath,
|
|
786
|
-
});
|
|
814
|
+
}, context);
|
|
787
815
|
}
|
|
788
816
|
// Manifest-driven publish operation (request-json surface). The OPERATION is the
|
|
789
817
|
// subcommand and the target: `postplus publish <operation> --request <file>`. The
|
|
@@ -792,7 +820,7 @@ async function runResearchScrape(args) {
|
|
|
792
820
|
// Side-effecting operations surface the Web quote-confirmation challenge; the
|
|
793
821
|
// shared runHostedCommand handles the challenge -> retry-with-token path. There is
|
|
794
822
|
// no requestDimensions/approval/execute — those were private-runtime concepts.
|
|
795
|
-
async function runPublishOperation(operation, args) {
|
|
823
|
+
async function runPublishOperation(operation, args, context) {
|
|
796
824
|
const resolved = PUBLISH_VERB_OPERATIONS.get(operation);
|
|
797
825
|
if (!resolved) {
|
|
798
826
|
throw new Error(`Unknown publish operation ${operation}. Valid: ${[...PUBLISH_VERB_OPERATIONS.keys()].join(', ')}.`);
|
|
@@ -816,9 +844,8 @@ async function runPublishOperation(operation, args) {
|
|
|
816
844
|
throw new Error(`Unknown option for publish ${operation}: --${key}.`);
|
|
817
845
|
}
|
|
818
846
|
}
|
|
819
|
-
const requestPath = requireFlag(flags, 'request');
|
|
820
847
|
const outputPath = flags.values.get('output') ?? null;
|
|
821
|
-
const raw = await
|
|
848
|
+
const { body: raw, errorInputLabel } = await resolveRequestBody(context, flags);
|
|
822
849
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
823
850
|
throw new Error(`publish ${operation} --request must be a JSON object of publishing input.`);
|
|
824
851
|
}
|
|
@@ -827,7 +854,7 @@ async function runPublishOperation(operation, args) {
|
|
|
827
854
|
const operationId = flags.values.get('hosted-operation-id') ??
|
|
828
855
|
`postplus-cli:publish:social-publishing:request:${randomUUID()}`;
|
|
829
856
|
const quoteConfirmationToken = flags.values.get('quote-confirmation-token');
|
|
830
|
-
return
|
|
857
|
+
return dispatchHostedCommand({
|
|
831
858
|
request: () => postHostedJson({
|
|
832
859
|
body: {
|
|
833
860
|
capability: 'social-publishing',
|
|
@@ -838,13 +865,14 @@ async function runPublishOperation(operation, args) {
|
|
|
838
865
|
},
|
|
839
866
|
pathName: '/api/postplus-cli/hosted/capability',
|
|
840
867
|
skillName,
|
|
868
|
+
context,
|
|
841
869
|
}),
|
|
842
|
-
errorInputLabel
|
|
870
|
+
errorInputLabel,
|
|
843
871
|
json: flags.booleans.has('json'),
|
|
844
872
|
outputPath,
|
|
845
|
-
});
|
|
873
|
+
}, context);
|
|
846
874
|
}
|
|
847
|
-
async function runHostedSchema(domain, args) {
|
|
875
|
+
async function runHostedSchema(domain, args, context) {
|
|
848
876
|
const flags = parseFlags(args, new Set(['json']));
|
|
849
877
|
const allowedFlags = domain === 'media'
|
|
850
878
|
? new Set(['endpoint'])
|
|
@@ -856,24 +884,40 @@ async function runHostedSchema(domain, args) {
|
|
|
856
884
|
throw new Error(`Unknown option for ${domain} schema: --${key}.`);
|
|
857
885
|
}
|
|
858
886
|
}
|
|
859
|
-
|
|
887
|
+
const report = buildHostedRequestSchemaReport({
|
|
860
888
|
collectionKey: flags.values.get('collection-key') ?? null,
|
|
861
889
|
domain,
|
|
862
890
|
endpointKey: flags.values.get('endpoint') ?? null,
|
|
863
|
-
})
|
|
891
|
+
});
|
|
892
|
+
// In-process / context path: RETURN the structured catalog so the model
|
|
893
|
+
// receives it as the call result. The bin path (no context) keeps writeJson +
|
|
894
|
+
// return 0 for human CLI stdout output. Mirrors the spend-verb dispatch.
|
|
895
|
+
if (context) {
|
|
896
|
+
return report;
|
|
897
|
+
}
|
|
898
|
+
writeJson(report);
|
|
864
899
|
return 0;
|
|
865
900
|
}
|
|
866
901
|
async function postHostedJson(input) {
|
|
867
|
-
const
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
902
|
+
const response = input.context
|
|
903
|
+
? await sendAuthedCloudRequest({
|
|
904
|
+
auth: input.context.auth,
|
|
905
|
+
body: input.body,
|
|
906
|
+
method: 'POST',
|
|
907
|
+
pathName: input.pathName,
|
|
908
|
+
skillName: input.skillName,
|
|
909
|
+
skillsReleaseId: input.context.skillsReleaseId ?? null,
|
|
910
|
+
timeoutMs: 120000,
|
|
911
|
+
})
|
|
912
|
+
: await sendAuthedCloudRequest({
|
|
913
|
+
auth: await resolveFreshRemoteAuth(),
|
|
914
|
+
body: input.body,
|
|
915
|
+
method: 'POST',
|
|
916
|
+
pathName: input.pathName,
|
|
917
|
+
retryOn401: () => resolveFreshRemoteAuth({ forceRefresh: true }),
|
|
918
|
+
skillName: input.skillName,
|
|
919
|
+
timeoutMs: 120000,
|
|
920
|
+
});
|
|
877
921
|
const payload = await readJsonResponse(response);
|
|
878
922
|
if (!response.ok) {
|
|
879
923
|
const productError = readHostedProductError(payload);
|
|
@@ -889,7 +933,7 @@ async function postHostedJson(input) {
|
|
|
889
933
|
}
|
|
890
934
|
return payload;
|
|
891
935
|
}
|
|
892
|
-
// Single exit path for
|
|
936
|
+
// Single exit path for the BIN hosted command: success writes the result and
|
|
893
937
|
// returns 0; a quote challenge writes the challenge file and rethrows actionable
|
|
894
938
|
// guidance; a structured product error writes the full error envelope to the
|
|
895
939
|
// result JSON and surfaces code/layer/operationId on the terminal, exiting 1.
|
|
@@ -921,6 +965,20 @@ async function runHostedCommand(input) {
|
|
|
921
965
|
await writeResult(payload, input.outputPath, input.json);
|
|
922
966
|
return 0;
|
|
923
967
|
}
|
|
968
|
+
// Single exit path for both BIN and LIB hosted commands. Each dispatch function
|
|
969
|
+
// builds the SAME `request` closure (resolve verb -> build envelope -> POST) and
|
|
970
|
+
// hands it here. The bin path (no `context`) keeps stdout/file/exit-code behavior
|
|
971
|
+
// via runHostedCommand. The lib path (with `context`) returns the parsed payload
|
|
972
|
+
// and rethrows the structured HostedProductRequestError / quote-confirmation error
|
|
973
|
+
// VERBATIM — no stdout, no file writes, no exit code — so the in-process caller
|
|
974
|
+
// surfaces the structured JSON and fails honestly. Because the closure is shared,
|
|
975
|
+
// the wire request (URL + body + headers) is byte-identical across both paths.
|
|
976
|
+
async function dispatchHostedCommand(input, context) {
|
|
977
|
+
if (!context) {
|
|
978
|
+
return runHostedCommand(input);
|
|
979
|
+
}
|
|
980
|
+
return input.request();
|
|
981
|
+
}
|
|
924
982
|
async function writeQuoteConfirmationChallenge(error, input) {
|
|
925
983
|
const challengePath = path.resolve(input.outputPath
|
|
926
984
|
? `${input.outputPath}.quote-confirmation.json`
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AuthedCloudRequestAuth } from './authed-cloud-request.js';
|
|
2
|
+
export type HostedLibDomain = 'media' | 'research' | 'publish' | 'media-file';
|
|
3
|
+
export type RunHostedRequestInput = {
|
|
4
|
+
/** Which hosted verb family `args` belongs to (the first CLI token). */
|
|
5
|
+
domain: HostedLibDomain;
|
|
6
|
+
/**
|
|
7
|
+
* The CLI tokens AFTER the domain, exactly as the bin would receive them, e.g.
|
|
8
|
+
* `['create', 'video-seedance-2-text']` or `['collect', 'tiktok-research']`.
|
|
9
|
+
* Flags surfaces still pass their `--flag value` tokens here; request-json
|
|
10
|
+
* surfaces pass the body via `requestJson` instead of a `--request <file>`.
|
|
11
|
+
*/
|
|
12
|
+
args: string[];
|
|
13
|
+
/**
|
|
14
|
+
* The request-json envelope for request-json surfaces, injected in place of a
|
|
15
|
+
* `--request <file>` read. Omit it for flags surfaces (media create/transcribe
|
|
16
|
+
* flag-driven) and for surfaces that need no body (polling/run-handle).
|
|
17
|
+
*/
|
|
18
|
+
requestJson?: Record<string, unknown> | unknown[];
|
|
19
|
+
/** The account's fresh session auth, supplied by the trusted host runtime. */
|
|
20
|
+
auth: AuthedCloudRequestAuth;
|
|
21
|
+
/**
|
|
22
|
+
* The skills release id stamped into `x-postplus-skills-release-id`. Provided
|
|
23
|
+
* verbatim by the host (it is NOT read from disk on this path).
|
|
24
|
+
*/
|
|
25
|
+
skillsReleaseId?: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Runs a hosted media / research / publish / media-file request in-process and
|
|
29
|
+
* returns the parsed hosted payload. Throws the structured
|
|
30
|
+
* HostedProductRequestError / quote-confirmation error VERBATIM on failure — no
|
|
31
|
+
* stdout, no file writes, no exit code. The wire request is identical to the bin
|
|
32
|
+
* path for the same input (proven by the parity test).
|
|
33
|
+
*/
|
|
34
|
+
export declare function runHostedRequest(input: RunHostedRequestInput): Promise<unknown>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// In-process hosted-execution library entry. This is the trusted-runtime
|
|
2
|
+
// counterpart to the `postplus` bin: a host process (e.g. eve-agent) that already
|
|
3
|
+
// holds the account's fresh session auth can run the SAME hosted verb grammar
|
|
4
|
+
// in-process — resolve verb -> build the typed envelope -> POST -> return the
|
|
5
|
+
// parsed payload — WITHOUT spawning a CLI subprocess, reading disk config, or
|
|
6
|
+
// writing any temp/output file.
|
|
7
|
+
//
|
|
8
|
+
// Why this exists (anti-drift): the bin path and this lib path share ONE
|
|
9
|
+
// resolve+build+post core in hosted-domain-commands.ts. For the same
|
|
10
|
+
// domain+args+input+auth they produce a byte-identical hosted HTTP request (URL +
|
|
11
|
+
// JSON body + headers). The only divergence is the input source and the result
|
|
12
|
+
// sink, both injected via HostedRequestContext: auth + skillsReleaseId come in as
|
|
13
|
+
// parameters (no `resolveFreshRemoteAuth()` disk read, no 401-refresh-retry — the
|
|
14
|
+
// host supplies fresh session auth each turn), the request-json body comes from
|
|
15
|
+
// `requestJson` instead of a `--request <file>`, and the parsed payload is RETURNED
|
|
16
|
+
// (the structured HostedProductRequestError / quote-confirmation error are thrown
|
|
17
|
+
// verbatim) instead of being written to stdout/file with an exit code.
|
|
18
|
+
//
|
|
19
|
+
// Scope: only the hosted spend/write surfaces go through here —
|
|
20
|
+
// media / research / publish / media-file. Read-only diagnostics (status / doctor
|
|
21
|
+
// / skills / whoami / quote / list / --version / --help) are NOT hosted-domain
|
|
22
|
+
// commands and are out of scope for this entry.
|
|
23
|
+
import { runHostedDomainCommand, runMediaFileCommand, } from './hosted-domain-commands.js';
|
|
24
|
+
/**
|
|
25
|
+
* Runs a hosted media / research / publish / media-file request in-process and
|
|
26
|
+
* returns the parsed hosted payload. Throws the structured
|
|
27
|
+
* HostedProductRequestError / quote-confirmation error VERBATIM on failure — no
|
|
28
|
+
* stdout, no file writes, no exit code. The wire request is identical to the bin
|
|
29
|
+
* path for the same input (proven by the parity test).
|
|
30
|
+
*/
|
|
31
|
+
export async function runHostedRequest(input) {
|
|
32
|
+
const context = {
|
|
33
|
+
auth: input.auth,
|
|
34
|
+
...(input.skillsReleaseId !== undefined
|
|
35
|
+
? { skillsReleaseId: input.skillsReleaseId }
|
|
36
|
+
: {}),
|
|
37
|
+
...(input.requestJson !== undefined
|
|
38
|
+
? { requestJson: input.requestJson }
|
|
39
|
+
: {}),
|
|
40
|
+
};
|
|
41
|
+
if (input.domain === 'media-file') {
|
|
42
|
+
return runMediaFileCommand(input.args, context);
|
|
43
|
+
}
|
|
44
|
+
return runHostedDomainCommand(input.domain, input.args, context);
|
|
45
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export type HostedDomain = 'media' | 'publish' | 'research';
|
|
2
|
+
export type ManifestFieldClass = 'intent' | 'default' | 'runner-managed';
|
|
3
|
+
export type ManifestField = {
|
|
4
|
+
name: string;
|
|
5
|
+
class: ManifestFieldClass;
|
|
6
|
+
flag: string | null;
|
|
7
|
+
type: 'string' | 'number' | 'boolean' | 'media-url';
|
|
8
|
+
repeatable?: boolean;
|
|
9
|
+
enumValues?: readonly string[];
|
|
10
|
+
min?: number;
|
|
11
|
+
max?: number;
|
|
12
|
+
canonicalize?: 'lowercase' | 'image-resolution-tier';
|
|
13
|
+
default?: string | number | boolean;
|
|
14
|
+
required: boolean;
|
|
15
|
+
derivedFrom?: string;
|
|
16
|
+
};
|
|
17
|
+
export type ManifestEndpoint = {
|
|
18
|
+
endpointKey: string;
|
|
19
|
+
provider: string;
|
|
20
|
+
providerModelPath: string;
|
|
21
|
+
fields: readonly ManifestField[];
|
|
22
|
+
billingDimensions?: readonly string[];
|
|
23
|
+
};
|
|
24
|
+
export type ManifestModel = {
|
|
25
|
+
modelKey: string;
|
|
26
|
+
providerModelPath: string;
|
|
27
|
+
};
|
|
28
|
+
export type ManifestCollection = {
|
|
29
|
+
collectionKey: string;
|
|
30
|
+
actorId: string;
|
|
31
|
+
};
|
|
32
|
+
export type ManifestSource = {
|
|
33
|
+
sourceKey: string;
|
|
34
|
+
datasetId: string;
|
|
35
|
+
};
|
|
36
|
+
export type ManifestOperation = {
|
|
37
|
+
operation: string;
|
|
38
|
+
};
|
|
39
|
+
export type ManifestEntry = {
|
|
40
|
+
skill: string;
|
|
41
|
+
mode?: 'cli-runner';
|
|
42
|
+
surface: 'flags' | 'request-json';
|
|
43
|
+
verb: string;
|
|
44
|
+
domain: HostedDomain;
|
|
45
|
+
capability: string;
|
|
46
|
+
endpointKeys?: readonly string[];
|
|
47
|
+
modelKeys?: readonly string[];
|
|
48
|
+
collectionKeys?: readonly string[];
|
|
49
|
+
sourceKeys?: readonly string[];
|
|
50
|
+
endpoints?: readonly ManifestEndpoint[];
|
|
51
|
+
models?: readonly ManifestModel[];
|
|
52
|
+
collections?: readonly ManifestCollection[];
|
|
53
|
+
sources?: readonly ManifestSource[];
|
|
54
|
+
operations?: readonly ManifestOperation[];
|
|
55
|
+
};
|
|
56
|
+
export type ResolvedVerbTarget = {
|
|
57
|
+
skill: string;
|
|
58
|
+
capability: string;
|
|
59
|
+
surface: 'flags' | 'request-json';
|
|
60
|
+
endpoint?: ManifestEndpoint;
|
|
61
|
+
model?: ManifestModel;
|
|
62
|
+
collection?: ManifestCollection;
|
|
63
|
+
source?: ManifestSource;
|
|
64
|
+
operation?: string;
|
|
65
|
+
};
|
|
66
|
+
export declare function allManifestEntries(): ManifestEntry[];
|
|
67
|
+
export declare function buildVerbTargetIndex(domain: HostedDomain): Map<string, Map<string, ResolvedVerbTarget>>;
|
|
68
|
+
export declare function manifestTargetKeys(domain: HostedDomain, capability?: string): string[];
|
|
69
|
+
export declare function findMediaEndpoint(endpointKey: string): ManifestEndpoint | null;
|
package/build/index.js
CHANGED
|
@@ -412,17 +412,19 @@ async function main() {
|
|
|
412
412
|
case 'doctor':
|
|
413
413
|
process.exitCode = await runDoctor(parseDiagnosticOptions(rest));
|
|
414
414
|
return;
|
|
415
|
+
// The bin path never passes the in-process context, so these always resolve
|
|
416
|
+
// to the numeric exit code (the `unknown` return is the hosted-lib path only).
|
|
415
417
|
case 'research':
|
|
416
|
-
process.exitCode = await runHostedDomainCommand('research', rest);
|
|
418
|
+
process.exitCode = (await runHostedDomainCommand('research', rest));
|
|
417
419
|
return;
|
|
418
420
|
case 'media':
|
|
419
|
-
process.exitCode = await runHostedDomainCommand('media', rest);
|
|
421
|
+
process.exitCode = (await runHostedDomainCommand('media', rest));
|
|
420
422
|
return;
|
|
421
423
|
case 'media-file':
|
|
422
|
-
process.exitCode = await runMediaFileCommand(rest);
|
|
424
|
+
process.exitCode = (await runMediaFileCommand(rest));
|
|
423
425
|
return;
|
|
424
426
|
case 'publish':
|
|
425
|
-
process.exitCode = await runHostedDomainCommand('publish', rest);
|
|
427
|
+
process.exitCode = (await runHostedDomainCommand('publish', rest));
|
|
426
428
|
return;
|
|
427
429
|
case 'quote':
|
|
428
430
|
process.exitCode = await runQuoteCommand(rest);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@postplus/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.47",
|
|
4
4
|
"packageManager": "pnpm@10.30.3+sha512.c961d1e0a2d8e354ecaa5166b822516668b7f44cb5bd95122d590dd81922f606f5473b6d23ec4a5be05e7fcd18e8488d47d978bbe981872f1145d06e9a740017",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "PostPlus CLI for PostPlus Cloud auth, status, and diagnostics.",
|
|
@@ -13,14 +13,19 @@
|
|
|
13
13
|
"build/auth-validate.js",
|
|
14
14
|
"build/auth.js",
|
|
15
15
|
"build/authed-cloud-request.js",
|
|
16
|
+
"build/authed-cloud-request.d.ts",
|
|
16
17
|
"build/client-compatibility.js",
|
|
17
18
|
"build/command-runner.js",
|
|
18
19
|
"build/doctor.js",
|
|
19
20
|
"build/hosted-domain-commands.js",
|
|
21
|
+
"build/hosted-domain-commands.d.ts",
|
|
22
|
+
"build/hosted-lib.js",
|
|
23
|
+
"build/hosted-lib.d.ts",
|
|
20
24
|
"build/generated/hosted-execution-manifest.generated.js",
|
|
21
25
|
"build/generated/hosted-field-validation-core.generated.js",
|
|
22
26
|
"build/hosted-field-validation.js",
|
|
23
27
|
"build/hosted-manifest-index.js",
|
|
28
|
+
"build/hosted-manifest-index.d.ts",
|
|
24
29
|
"build/hosted-release.js",
|
|
25
30
|
"build/hosted-request-schemas.js",
|
|
26
31
|
"build/index.js",
|
|
@@ -48,6 +53,13 @@
|
|
|
48
53
|
"bin": {
|
|
49
54
|
"postplus": "build/index.js"
|
|
50
55
|
},
|
|
56
|
+
"exports": {
|
|
57
|
+
"./hosted-lib": {
|
|
58
|
+
"types": "./build/hosted-lib.d.ts",
|
|
59
|
+
"default": "./build/hosted-lib.js"
|
|
60
|
+
},
|
|
61
|
+
"./package.json": "./package.json"
|
|
62
|
+
},
|
|
51
63
|
"scripts": {
|
|
52
64
|
"build": "node ./scripts/clean-build.mjs && tsc && node ./scripts/finalize-build.mjs",
|
|
53
65
|
"clean": "rm -rf .turbo node_modules build",
|