@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.
@@ -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 = config?.managedSkills?.releaseId?.trim();
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": "width_and_height",
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": "--quality",
1143
+ "flag": "--aspect-ratio",
1151
1144
  "type": "string",
1152
1145
  "enumValues": [
1153
- "720p",
1154
- "1080p"
1146
+ "9:16",
1147
+ "16:9",
1148
+ "4:3",
1149
+ "3:4",
1150
+ "1:1",
1151
+ "2:3",
1152
+ "3:2"
1155
1153
  ],
1156
- "canonicalize": "lowercase",
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
- "quality"
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 readJsonFile(requestPath);
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: requestPath,
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 readJsonFile(requestPath);
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 runHostedCommand({
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: requestPath,
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 runHostedCommand({
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 runHostedCommand({
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 runHostedCommand({
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 runHostedCommand({
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 readJsonFile(requestPath);
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 runHostedCommand({
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: requestPath,
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 runHostedCommand({
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 readJsonFile(requestPath);
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 runHostedCommand({
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: requestPath,
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 readJsonFile(requestPath);
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 runHostedCommand({
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: requestPath,
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
- writeJson(buildHostedRequestSchemaReport({
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 auth = await resolveFreshRemoteAuth();
868
- const response = await sendAuthedCloudRequest({
869
- auth,
870
- body: input.body,
871
- method: 'POST',
872
- pathName: input.pathName,
873
- retryOn401: () => resolveFreshRemoteAuth({ forceRefresh: true }),
874
- skillName: input.skillName,
875
- timeoutMs: 120000,
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 every hosted command: success writes the result and
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.46",
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",