@oxygen-agent/cli 1.922.14 → 1.948.1

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.
Files changed (65) hide show
  1. package/README.md +1 -1
  2. package/dist/admin-primary-providers-render.d.ts +18 -0
  3. package/dist/admin-primary-providers-render.js +371 -0
  4. package/dist/command-manifest.js +30 -2
  5. package/dist/functions-commands.d.ts +6 -0
  6. package/dist/functions-commands.js +56 -0
  7. package/dist/help.js +1 -0
  8. package/dist/http-client.d.ts +4 -0
  9. package/dist/http-client.js +49 -2
  10. package/dist/index.js +515 -92
  11. package/dist/ugc-commands.d.ts +6 -0
  12. package/dist/ugc-commands.js +1089 -0
  13. package/dist/visual-commands.d.ts +6 -0
  14. package/dist/visual-commands.js +57 -0
  15. package/dist/visual-render-wait.d.ts +3 -0
  16. package/dist/visual-render-wait.js +56 -0
  17. package/node_modules/@oxygen/shared/dist/byok-connect.d.ts +48 -0
  18. package/node_modules/@oxygen/shared/dist/byok-connect.js +92 -0
  19. package/node_modules/@oxygen/shared/dist/capability-discovery.js +77 -13
  20. package/node_modules/@oxygen/shared/dist/email-dsn.d.ts +60 -0
  21. package/node_modules/@oxygen/shared/dist/email-dsn.js +120 -0
  22. package/node_modules/@oxygen/shared/dist/email-warmup-readiness.d.ts +64 -0
  23. package/node_modules/@oxygen/shared/dist/email-warmup-readiness.js +90 -0
  24. package/node_modules/@oxygen/shared/dist/feature-gates.d.ts +2 -0
  25. package/node_modules/@oxygen/shared/dist/feature-gates.js +3 -0
  26. package/node_modules/@oxygen/shared/dist/index.d.ts +10 -0
  27. package/node_modules/@oxygen/shared/dist/index.js +10 -0
  28. package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.d.ts +50 -21
  29. package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.js +47 -21
  30. package/node_modules/@oxygen/shared/dist/knowledge-constants.d.ts +1 -1
  31. package/node_modules/@oxygen/shared/dist/knowledge-constants.js +3 -2
  32. package/node_modules/@oxygen/shared/dist/knowledge-seed-content.js +1 -1
  33. package/node_modules/@oxygen/shared/dist/langfuse.d.ts +8 -3
  34. package/node_modules/@oxygen/shared/dist/langfuse.js +185 -121
  35. package/node_modules/@oxygen/shared/dist/llm-payload.d.ts +10 -0
  36. package/node_modules/@oxygen/shared/dist/llm-payload.js +54 -0
  37. package/node_modules/@oxygen/shared/dist/llm-usage.d.ts +11 -0
  38. package/node_modules/@oxygen/shared/dist/llm-usage.js +30 -0
  39. package/node_modules/@oxygen/shared/dist/object-storage.d.ts +6 -0
  40. package/node_modules/@oxygen/shared/dist/object-storage.js +5 -0
  41. package/node_modules/@oxygen/shared/dist/product-analytics-core.d.ts +98 -0
  42. package/node_modules/@oxygen/shared/dist/product-analytics-core.js +159 -0
  43. package/node_modules/@oxygen/shared/dist/product-analytics-environment.d.ts +18 -0
  44. package/node_modules/@oxygen/shared/dist/product-analytics-environment.js +46 -0
  45. package/node_modules/@oxygen/shared/dist/product-analytics-events.d.ts +92 -0
  46. package/node_modules/@oxygen/shared/dist/product-analytics-events.js +96 -0
  47. package/node_modules/@oxygen/shared/dist/provider-balance-signal.d.ts +113 -0
  48. package/node_modules/@oxygen/shared/dist/provider-balance-signal.js +158 -0
  49. package/node_modules/@oxygen/shared/dist/sequence-failures.d.ts +12 -0
  50. package/node_modules/@oxygen/shared/dist/sequence-failures.js +24 -0
  51. package/node_modules/@oxygen/shared/dist/sequences.d.ts +16 -0
  52. package/node_modules/@oxygen/shared/dist/sequences.js +54 -0
  53. package/node_modules/@oxygen/shared/dist/ugc.d.ts +133 -0
  54. package/node_modules/@oxygen/shared/dist/ugc.js +2 -0
  55. package/node_modules/@oxygen/shared/dist/vercel-sandbox-fetch.d.ts +9 -0
  56. package/node_modules/@oxygen/shared/dist/vercel-sandbox-fetch.js +33 -0
  57. package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
  58. package/node_modules/@oxygen/shared/dist/version.js +1 -1
  59. package/node_modules/@oxygen/shared/dist/visual-render.d.ts +30 -0
  60. package/node_modules/@oxygen/shared/dist/visual-render.js +55 -0
  61. package/node_modules/@oxygen/shared/dist/workspace-file-storage.d.ts +43 -0
  62. package/node_modules/@oxygen/shared/dist/workspace-file-storage.js +126 -0
  63. package/node_modules/@oxygen/shared/package.json +10 -0
  64. package/node_modules/@oxygen/workflows/dist/graph/lint.js +22 -0
  65. package/package.json +1 -1
@@ -0,0 +1,1089 @@
1
+ import { requestOxygen } from "./http-client.js";
2
+ function parseField(value, field) {
3
+ if (field.kind === "array" || field.kind === "object") {
4
+ const parsed = JSON.parse(String(value));
5
+ if (field.kind === "array"
6
+ ? !Array.isArray(parsed) ||
7
+ !parsed.every((item) => typeof item === "string")
8
+ : !parsed || typeof parsed !== "object" || Array.isArray(parsed))
9
+ throw new Error(`${field.name} must be a JSON ${field.kind === "array" ? "array of strings" : "object"}.`);
10
+ return parsed;
11
+ }
12
+ if (field.kind === "number") {
13
+ const parsed = Number(value);
14
+ if (!Number.isFinite(parsed) || parsed < 0)
15
+ throw new Error(`${field.name} must be a nonnegative finite number.`);
16
+ return parsed;
17
+ }
18
+ if (field.kind === "boolean") {
19
+ if (value === true || value === "true")
20
+ return true;
21
+ if (value === false || value === "false")
22
+ return false;
23
+ throw new Error(`${field.name} must be true or false.`);
24
+ }
25
+ return value;
26
+ }
27
+ export function registerUgcCommands(program, handle) {
28
+ const ugc = program
29
+ .command("ugc")
30
+ .description("Manage UGC programs and independent creator participations: briefs, voice, posts, approvals, sponsorship and bounded amplification. Requires UGC availability.");
31
+ const groupDescriptions = {
32
+ programs: "Programs you operate or joined. Lifecycle: create, update, duplicate (settings + briefs), archive (stops future work, keeps shared history, reversible with restore) and delete (permanent; only while no creator has ever joined).",
33
+ creators: "Creator participations: invite, revoke an invite, accept in the creator workspace, connect a sender, change approval authority, or revoke.",
34
+ briefs: "Program briefs with ordered questions for creators.",
35
+ posts: "Enrolled program posts: enroll existing posts, draft from briefs, edit, schedule, and record creator or brand approval.",
36
+ analytics: "Stored daily program and creator performance.",
37
+ knowledge: "Master Knowledge pages shared live with a program.",
38
+ voice: "Creator voice profiles and imports.",
39
+ sponsorship: "Seat and credit sponsorship per participation.",
40
+ amplification: "Bounded, approval-gated amplification of enrolled posts.",
41
+ calendar: "Bounded enrolled-post calendar reads.",
42
+ };
43
+ const groups = new Map();
44
+ const group = (name) => {
45
+ let command = groups.get(name);
46
+ if (!command) {
47
+ command = ugc.command(name);
48
+ const description = groupDescriptions[name];
49
+ if (description)
50
+ command.description(description);
51
+ groups.set(name, command);
52
+ }
53
+ return command;
54
+ };
55
+ group("programs")
56
+ .command("list")
57
+ .description("Read UGC programs in the active workspace, archived ones included unless --status filters. Read-only.")
58
+ .option("--program-id <id>", "Program ID.")
59
+ .option("--participation-id <id>", "Creator participation ID.")
60
+ .option("--status <active|archived>", "Only programs in this status.")
61
+ .option("--json", "Print a JSON envelope.")
62
+ .action((options) => handle("ugc programs list", options, () => {
63
+ const query = new URLSearchParams({ view: "programs" });
64
+ if (options.programId)
65
+ query.set("program_id", String(options.programId));
66
+ if (options.participationId)
67
+ query.set("participation_id", String(options.participationId));
68
+ if (options.status)
69
+ query.set("status", String(options.status));
70
+ return requestOxygen(`/api/cli/ugc?${query}`);
71
+ }));
72
+ group("programs")
73
+ .command("get")
74
+ .description("Read UGC program in the active workspace. Read-only.")
75
+ .requiredOption("--program-id <id>", "Program ID.")
76
+ .option("--participation-id <id>", "Creator participation ID.")
77
+ .option("--json", "Print a JSON envelope.")
78
+ .action((options) => handle("ugc programs get", options, () => {
79
+ const query = new URLSearchParams({ view: "program" });
80
+ if (options.programId)
81
+ query.set("program_id", String(options.programId));
82
+ if (options.participationId)
83
+ query.set("participation_id", String(options.participationId));
84
+ return requestOxygen(`/api/cli/ugc?${query}`);
85
+ }));
86
+ group("creators")
87
+ .command("list")
88
+ .description("Read creators and invitations, including persisted email delivery status. Read-only.")
89
+ .option("--program-id <id>", "Program ID.")
90
+ .option("--participation-id <id>", "Creator participation ID.")
91
+ .option("--json", "Print a JSON envelope.")
92
+ .action((options) => handle("ugc creators list", options, () => {
93
+ const query = new URLSearchParams({ view: "participations" });
94
+ if (options.programId)
95
+ query.set("program_id", String(options.programId));
96
+ if (options.participationId)
97
+ query.set("participation_id", String(options.participationId));
98
+ return requestOxygen(`/api/cli/ugc?${query}`);
99
+ }));
100
+ group("briefs")
101
+ .command("list")
102
+ .description("Read one program's UGC briefs. Read-only; requires --program-id.")
103
+ .showHelpAfterError(`Briefs belong to a program. Run \`${program.name() || "oxygen"} ugc programs list --json\` to find its ID, then \`${program.name() || "oxygen"} ugc briefs list --program-id <id> --json\`.`)
104
+ .requiredOption("--program-id <id>", "Program ID.")
105
+ .option("--participation-id <id>", "Creator participation ID.")
106
+ .option("--json", "Print a JSON envelope.")
107
+ .action((options) => handle("ugc briefs list", options, () => {
108
+ const query = new URLSearchParams({ view: "briefs" });
109
+ if (options.programId)
110
+ query.set("program_id", String(options.programId));
111
+ if (options.participationId)
112
+ query.set("participation_id", String(options.participationId));
113
+ return requestOxygen(`/api/cli/ugc?${query}`);
114
+ }));
115
+ group("posts")
116
+ .command("list")
117
+ .description("Read UGC posts in the active workspace. Read-only.")
118
+ .requiredOption("--program-id <id>", "Program ID.")
119
+ .option("--participation-id <id>", "Creator participation ID.")
120
+ .option("--cursor <cursor>", "Opaque next_cursor from the previous page.")
121
+ .option("--limit <count>", "Posts per page, 1–500.")
122
+ .option("--json", "Print a JSON envelope.")
123
+ .action((options) => handle("ugc posts list", options, () => {
124
+ const query = new URLSearchParams({ view: "posts" });
125
+ if (options.programId)
126
+ query.set("program_id", String(options.programId));
127
+ if (options.participationId)
128
+ query.set("participation_id", String(options.participationId));
129
+ if (options.cursor)
130
+ query.set("cursor", String(options.cursor));
131
+ if (options.limit)
132
+ query.set("limit", String(options.limit));
133
+ return requestOxygen(`/api/cli/ugc?${query}`);
134
+ }));
135
+ group("analytics")
136
+ .command("get")
137
+ .description("Read UGC analytics in the active workspace. Read-only.")
138
+ .requiredOption("--program-id <id>", "Program ID.")
139
+ .option("--participation-id <id>", "Creator participation ID.")
140
+ .option("--post-cursor <cursor>", "Next post_performance page from next_post_cursor; totals still cover all enrolled posts.")
141
+ .option("--limit <count>", "Post performance rows per page, up to 100.")
142
+ .option("--json", "Print a JSON envelope.")
143
+ .action((options) => handle("ugc analytics get", options, () => {
144
+ const query = new URLSearchParams({ view: "analytics" });
145
+ if (options.programId)
146
+ query.set("program_id", String(options.programId));
147
+ if (options.participationId)
148
+ query.set("participation_id", String(options.participationId));
149
+ if (options.postCursor)
150
+ query.set("post_cursor", String(options.postCursor));
151
+ if (options.limit)
152
+ query.set("limit", String(options.limit));
153
+ return requestOxygen(`/api/cli/ugc?${query}`);
154
+ }));
155
+ group("knowledge")
156
+ .command("list")
157
+ .description("Read UGC knowledge in the active workspace. Read-only.")
158
+ .requiredOption("--program-id <id>", "Program ID.")
159
+ .option("--participation-id <id>", "Creator participation ID.")
160
+ .option("--json", "Print a JSON envelope.")
161
+ .action((options) => handle("ugc knowledge list", options, () => {
162
+ const query = new URLSearchParams({ view: "knowledge" });
163
+ if (options.programId)
164
+ query.set("program_id", String(options.programId));
165
+ if (options.participationId)
166
+ query.set("participation_id", String(options.participationId));
167
+ return requestOxygen(`/api/cli/ugc?${query}`);
168
+ }));
169
+ group("voice")
170
+ .command("get")
171
+ .description("Read UGC voice in the active workspace. Read-only.")
172
+ .option("--program-id <id>", "Program ID.")
173
+ .requiredOption("--participation-id <id>", "Creator participation ID.")
174
+ .option("--json", "Print a JSON envelope.")
175
+ .action((options) => handle("ugc voice get", options, () => {
176
+ const query = new URLSearchParams({ view: "voice" });
177
+ if (options.programId)
178
+ query.set("program_id", String(options.programId));
179
+ if (options.participationId)
180
+ query.set("participation_id", String(options.participationId));
181
+ return requestOxygen(`/api/cli/ugc?${query}`);
182
+ }));
183
+ group("posts")
184
+ .command("draft-status")
185
+ .description("Read the original paid draft request and saved result without generating again. Creator workspace only, 0 credits.")
186
+ .requiredOption("--participation-id <id>", "Creator participation ID.")
187
+ .requiredOption("--request-id <id>", "Original draft request ID.")
188
+ .option("--json", "Print a JSON envelope.")
189
+ .action((options) => handle("ugc posts draft-status", options, () => {
190
+ const query = new URLSearchParams({
191
+ view: "draft_run",
192
+ participation_id: String(options.participationId),
193
+ request_id: String(options.requestId),
194
+ });
195
+ return requestOxygen(`/api/cli/ugc?${query}`);
196
+ }));
197
+ group("sponsorship")
198
+ .command("get")
199
+ .description("Read sponsorship in the active workspace. Creators see their remaining credits and exact organizer access; masters see program payment status. Read-only.")
200
+ .requiredOption("--program-id <id>", "Program ID.")
201
+ .option("--participation-id <id>", "Creator participation ID.")
202
+ .option("--json", "Print a JSON envelope.")
203
+ .action((options) => handle("ugc sponsorship get", options, () => {
204
+ const query = new URLSearchParams({ view: "sponsorship" });
205
+ if (options.programId)
206
+ query.set("program_id", String(options.programId));
207
+ if (options.participationId)
208
+ query.set("participation_id", String(options.participationId));
209
+ return requestOxygen(`/api/cli/ugc?${query}`);
210
+ }));
211
+ for (const spec of [
212
+ {
213
+ group: "calendar",
214
+ command: "list",
215
+ view: "calendar",
216
+ description: "Read the program's enrolled post calendar in a bounded UTC date range (up to 42 days). Stored data only, 0 credits.",
217
+ fields: [
218
+ ["date_from", "Start ISO instant, inclusive.", true],
219
+ ["date_to", "End ISO instant, exclusive.", true],
220
+ [
221
+ "calendar_cursor",
222
+ "Calendar next_cursor from the previous page.",
223
+ false,
224
+ ],
225
+ ["participation_id", "Filter to one creator participation.", false],
226
+ ],
227
+ },
228
+ {
229
+ group: "posts",
230
+ command: "history",
231
+ view: "post_history",
232
+ description: "Read retained public comments, metrics, and publication receipts for one enrolled post. Stored data only, 0 credits.",
233
+ fields: [
234
+ ["post_id", "Enrolled source post UUID.", true],
235
+ ["cursor", "next_comments_cursor from the previous page.", false],
236
+ ["metric_cursor", "next_metrics_cursor from the previous page.", false],
237
+ ["action_cursor", "next_actions_cursor from the previous page.", false],
238
+ ],
239
+ },
240
+ {
241
+ group: "amplification",
242
+ command: "get",
243
+ view: "amplification",
244
+ description: "Read this program's current amplification authority and immutable action history. Master workspace only; 0 credits, no provider call.",
245
+ fields: [["cursor", "next_cursor from the previous action page.", false]],
246
+ },
247
+ ]) {
248
+ const command = group(spec.group)
249
+ .command(spec.command)
250
+ .description(spec.description)
251
+ .requiredOption("--program-id <id>", "Program ID.")
252
+ .option("--limit <count>", "Rows per page, up to 100.")
253
+ .option("--json", "Print a JSON envelope.");
254
+ for (const [name, description, required] of spec.fields)
255
+ command[required ? "requiredOption" : "option"](`--${name.replaceAll("_", "-")} <value>`, description);
256
+ command.action((options) => handle(`ugc ${spec.group} ${spec.command}`, options, () => {
257
+ const query = new URLSearchParams({
258
+ view: spec.view,
259
+ program_id: String(options.programId),
260
+ });
261
+ for (const [name] of spec.fields) {
262
+ const key = name.replace(/_([a-z])/g, (_match, letter) => letter.toUpperCase());
263
+ if (options[key] !== undefined)
264
+ query.set(name, String(options[key]));
265
+ }
266
+ if (options.limit)
267
+ query.set("limit", String(options.limit));
268
+ return requestOxygen(`/api/cli/ugc?${query}`);
269
+ }));
270
+ }
271
+ const writes = [
272
+ {
273
+ action: "program_create",
274
+ command: "programs create",
275
+ description: "Create a master UGC program. Free: 0 credits, no provider call.",
276
+ fields: [
277
+ {
278
+ name: "name",
279
+ kind: "string",
280
+ description: "Program name.",
281
+ required: true,
282
+ },
283
+ {
284
+ name: "product_url",
285
+ kind: "string",
286
+ description: "Product website URL.",
287
+ required: true,
288
+ },
289
+ {
290
+ name: "description",
291
+ kind: "string",
292
+ description: "Program description.",
293
+ required: false,
294
+ },
295
+ {
296
+ name: "knowledge_slugs",
297
+ kind: "array",
298
+ description: "Selected live master wiki slugs shared with creators; empty array clears sharing.",
299
+ required: false,
300
+ },
301
+ {
302
+ name: "brand_approval_required",
303
+ kind: "boolean",
304
+ description: "Require a separate brand approval before publishing.",
305
+ required: false,
306
+ },
307
+ ],
308
+ },
309
+ {
310
+ action: "program_update",
311
+ command: "programs update",
312
+ description: "Update program settings and selected shared knowledge.",
313
+ fields: [
314
+ {
315
+ name: "program_id",
316
+ kind: "string",
317
+ description: "Program ID.",
318
+ required: true,
319
+ },
320
+ {
321
+ name: "name",
322
+ kind: "string",
323
+ description: "Program name.",
324
+ required: false,
325
+ },
326
+ {
327
+ name: "product_url",
328
+ kind: "string",
329
+ description: "Product website URL.",
330
+ required: false,
331
+ },
332
+ {
333
+ name: "description",
334
+ kind: "string",
335
+ description: "Program description.",
336
+ required: false,
337
+ },
338
+ {
339
+ name: "knowledge_slugs",
340
+ kind: "array",
341
+ description: "Selected live master wiki slugs shared with creators; empty array clears sharing.",
342
+ required: false,
343
+ },
344
+ {
345
+ name: "brand_approval_required",
346
+ kind: "boolean",
347
+ description: "Require a separate brand approval before publishing.",
348
+ required: false,
349
+ },
350
+ {
351
+ name: "status",
352
+ kind: "string",
353
+ description: "active or archived; prefer `programs archive` / `programs restore`.",
354
+ required: false,
355
+ },
356
+ ],
357
+ },
358
+ {
359
+ action: "program_update",
360
+ command: "programs archive",
361
+ description: "Archive a program: stop future access, publishing, tracking and amplification, revoke pending invitations and request cancellation of sponsorship renewals. Shared history is retained; `programs restore` re-activates it.",
362
+ fixed: { status: "archived" },
363
+ fields: [
364
+ {
365
+ name: "program_id",
366
+ kind: "string",
367
+ description: "Program ID.",
368
+ required: true,
369
+ },
370
+ ],
371
+ },
372
+ {
373
+ action: "program_update",
374
+ command: "programs restore",
375
+ description: "Restore an archived program. Creators removed earlier stay removed, revoked invitations stay revoked, and cancelled sponsorships need a new preview and confirmation.",
376
+ fixed: { status: "active" },
377
+ fields: [
378
+ {
379
+ name: "program_id",
380
+ kind: "string",
381
+ description: "Program ID.",
382
+ required: true,
383
+ },
384
+ ],
385
+ },
386
+ {
387
+ action: "program_duplicate",
388
+ command: "programs duplicate",
389
+ description: "Copy a program's settings, shared knowledge and brand review into a new active program, with its briefs by default. Free: 0 credits. Never copies creators, invitations, posts or sponsorship.",
390
+ fields: [
391
+ {
392
+ name: "program_id",
393
+ kind: "string",
394
+ description: "Source program ID.",
395
+ required: true,
396
+ },
397
+ {
398
+ name: "name",
399
+ kind: "string",
400
+ description: 'Name of the copy; defaults to "<name> (copy)".',
401
+ required: false,
402
+ },
403
+ {
404
+ name: "include_briefs",
405
+ kind: "boolean",
406
+ description: "Copy the program's briefs (default true).",
407
+ required: false,
408
+ },
409
+ ],
410
+ },
411
+ {
412
+ action: "program_delete",
413
+ command: "programs delete",
414
+ description: "Permanently delete a program with its briefs and pending invitations. A bare call previews the counts and whether it is deletable; pass --approved to delete. Allowed only while no creator has ever joined; otherwise use `programs archive`.",
415
+ fields: [
416
+ {
417
+ name: "program_id",
418
+ kind: "string",
419
+ description: "Program ID.",
420
+ required: true,
421
+ },
422
+ {
423
+ name: "approved",
424
+ kind: "boolean",
425
+ description: "Confirm the previewed permanent deletion.",
426
+ required: false,
427
+ },
428
+ ],
429
+ },
430
+ {
431
+ action: "invite_create",
432
+ command: "creators invite",
433
+ description: "Preview an invitation email (0 credits); send with --approved --preview-token from that preview. Use --link-only to create a link without email.",
434
+ fields: [
435
+ {
436
+ name: "program_id",
437
+ kind: "string",
438
+ description: "Program ID.",
439
+ required: true,
440
+ },
441
+ {
442
+ name: "email",
443
+ kind: "string",
444
+ description: "Exact creator email to receive the invitation.",
445
+ required: true,
446
+ },
447
+ {
448
+ name: "approved",
449
+ kind: "boolean",
450
+ description: "Authorize sending the exact previewed invitation email.",
451
+ required: false,
452
+ },
453
+ {
454
+ name: "preview_token",
455
+ kind: "string",
456
+ description: "Exact token from the invitation email preview.",
457
+ required: false,
458
+ },
459
+ ],
460
+ },
461
+ {
462
+ action: "invite_send",
463
+ command: "creators send-invite",
464
+ description: "Preview email for an existing pending invitation; send or resend with --approved --preview-token. Replaces the old link, keeps its expiry. Sent means provider accepted, not inbox delivery.",
465
+ fields: [
466
+ {
467
+ name: "program_id",
468
+ kind: "string",
469
+ description: "Program ID.",
470
+ required: true,
471
+ },
472
+ {
473
+ name: "invitation_id",
474
+ kind: "string",
475
+ description: "Exact pending invitation ID from creators list.",
476
+ required: true,
477
+ },
478
+ {
479
+ name: "approved",
480
+ kind: "boolean",
481
+ description: "Authorize the exact previewed email send or resend.",
482
+ required: false,
483
+ },
484
+ {
485
+ name: "preview_token",
486
+ kind: "string",
487
+ description: "Exact token from this invitation email preview.",
488
+ required: false,
489
+ },
490
+ ],
491
+ },
492
+ {
493
+ action: "invite_revoke",
494
+ command: "creators revoke-invite",
495
+ description: "Revoke an unaccepted invitation.",
496
+ fields: [
497
+ {
498
+ name: "program_id",
499
+ kind: "string",
500
+ description: "Program ID.",
501
+ required: true,
502
+ },
503
+ {
504
+ name: "invitation_id",
505
+ kind: "string",
506
+ description: "Exact invitation ID to revoke.",
507
+ required: true,
508
+ },
509
+ ],
510
+ },
511
+ {
512
+ action: "invite_accept",
513
+ command: "creators accept",
514
+ description: "Accept an exact invitation in the creator workspace and choose who may approve.",
515
+ fields: [
516
+ {
517
+ name: "token",
518
+ kind: "string",
519
+ description: "Exact invitation token.",
520
+ required: true,
521
+ },
522
+ {
523
+ name: "approval_mode",
524
+ kind: "string",
525
+ description: "creator: creator approves; delegated: master operators may approve and schedule.",
526
+ required: true,
527
+ },
528
+ {
529
+ name: "linkedin_url",
530
+ kind: "string",
531
+ description: "Creator LinkedIn profile URL.",
532
+ required: true,
533
+ },
534
+ ],
535
+ },
536
+ {
537
+ action: "participation_update",
538
+ command: "creators update",
539
+ description: "Connect an existing LinkedIn sender, change approval authority, or revoke participation.",
540
+ fields: [
541
+ {
542
+ name: "participation_id",
543
+ kind: "string",
544
+ description: "Creator participation ID.",
545
+ required: true,
546
+ },
547
+ {
548
+ name: "approval_mode",
549
+ kind: "string",
550
+ description: "creator: creator approves; delegated: master operators may approve and schedule.",
551
+ required: false,
552
+ },
553
+ {
554
+ name: "sender_account_id",
555
+ kind: "string",
556
+ description: "Existing physical LinkedIn connection ID; reused across programs.",
557
+ required: false,
558
+ },
559
+ {
560
+ name: "status",
561
+ kind: "string",
562
+ description: "Program status, or revoked for a participation.",
563
+ required: false,
564
+ },
565
+ ],
566
+ },
567
+ {
568
+ action: "brief_create",
569
+ command: "briefs create",
570
+ description: "Create a brief with ordered questions. Free: 0 credits, no provider call.",
571
+ fields: [
572
+ {
573
+ name: "program_id",
574
+ kind: "string",
575
+ description: "Program ID.",
576
+ required: true,
577
+ },
578
+ {
579
+ name: "title",
580
+ kind: "string",
581
+ description: "Brief or post title.",
582
+ required: true,
583
+ },
584
+ {
585
+ name: "prompt",
586
+ kind: "string",
587
+ description: "Instructions for this brief.",
588
+ required: true,
589
+ },
590
+ {
591
+ name: "questions",
592
+ kind: "array",
593
+ description: "Ordered questions for the creator.",
594
+ required: true,
595
+ },
596
+ {
597
+ name: "cycle",
598
+ kind: "string",
599
+ description: "Brief cycle label.",
600
+ required: false,
601
+ },
602
+ ],
603
+ },
604
+ {
605
+ action: "brief_update",
606
+ command: "briefs update",
607
+ description: "Update a program brief.",
608
+ fields: [
609
+ {
610
+ name: "program_id",
611
+ kind: "string",
612
+ description: "Program ID.",
613
+ required: true,
614
+ },
615
+ {
616
+ name: "brief_id",
617
+ kind: "string",
618
+ description: "Brief ID.",
619
+ required: true,
620
+ },
621
+ {
622
+ name: "title",
623
+ kind: "string",
624
+ description: "Brief or post title.",
625
+ required: false,
626
+ },
627
+ {
628
+ name: "prompt",
629
+ kind: "string",
630
+ description: "Instructions for this brief.",
631
+ required: false,
632
+ },
633
+ {
634
+ name: "questions",
635
+ kind: "array",
636
+ description: "Ordered questions for the creator.",
637
+ required: false,
638
+ },
639
+ {
640
+ name: "cycle",
641
+ kind: "string",
642
+ description: "Brief cycle label.",
643
+ required: false,
644
+ },
645
+ ],
646
+ },
647
+ {
648
+ action: "post_enroll",
649
+ command: "posts enroll",
650
+ description: "Enroll one existing creator-owned post in this participation.",
651
+ fields: [
652
+ {
653
+ name: "participation_id",
654
+ kind: "string",
655
+ description: "Creator participation ID.",
656
+ required: true,
657
+ },
658
+ {
659
+ name: "post_id",
660
+ kind: "string",
661
+ description: "Canonical post ID.",
662
+ required: true,
663
+ },
664
+ ],
665
+ },
666
+ {
667
+ action: "draft_create",
668
+ command: "posts draft",
669
+ description: "Preview AI drafting from brief answers. Execute only in the creator workspace with approved and a credit ceiling; publishing delegation does not grant authority to spend creator credits.",
670
+ fields: [
671
+ {
672
+ name: "request_id",
673
+ kind: "string",
674
+ description: "Request ID from preview. Reuse it after a timeout; a new ID is a new paid draft.",
675
+ required: false,
676
+ },
677
+ {
678
+ name: "participation_id",
679
+ kind: "string",
680
+ description: "Creator participation ID.",
681
+ required: true,
682
+ },
683
+ {
684
+ name: "brief_id",
685
+ kind: "string",
686
+ description: "Brief ID.",
687
+ required: true,
688
+ },
689
+ {
690
+ name: "answers",
691
+ kind: "array",
692
+ description: "Answers in brief question order.",
693
+ required: true,
694
+ },
695
+ {
696
+ name: "approved",
697
+ kind: "boolean",
698
+ description: "Explicit authorization of the exact preview or approval. Never inferred.",
699
+ required: false,
700
+ },
701
+ {
702
+ name: "max_credits",
703
+ kind: "number",
704
+ description: "Explicit credit ceiling from preview.",
705
+ required: false,
706
+ },
707
+ ],
708
+ },
709
+ {
710
+ action: "post_update",
711
+ command: "posts update",
712
+ description: "Edit or schedule an enrolled post with creator or delegated authority; publishing gates still apply.",
713
+ fields: [
714
+ {
715
+ name: "program_id",
716
+ kind: "string",
717
+ description: "Program ID.",
718
+ required: true,
719
+ },
720
+ {
721
+ name: "post_id",
722
+ kind: "string",
723
+ description: "Canonical post ID.",
724
+ required: true,
725
+ },
726
+ {
727
+ name: "title",
728
+ kind: "string",
729
+ description: "Brief or post title.",
730
+ required: false,
731
+ },
732
+ {
733
+ name: "content_text",
734
+ kind: "string",
735
+ description: "Edited post text.",
736
+ required: false,
737
+ },
738
+ {
739
+ name: "scheduled_for",
740
+ kind: "string",
741
+ description: "Future ISO-8601 publishing time with timezone.",
742
+ required: false,
743
+ },
744
+ ],
745
+ },
746
+ {
747
+ action: "post_approve",
748
+ command: "posts approve",
749
+ description: "Record creator or explicitly delegated operator approval of the current post.",
750
+ fields: [
751
+ {
752
+ name: "program_id",
753
+ kind: "string",
754
+ description: "Program ID.",
755
+ required: true,
756
+ },
757
+ {
758
+ name: "post_id",
759
+ kind: "string",
760
+ description: "Canonical post ID.",
761
+ required: true,
762
+ },
763
+ {
764
+ name: "source_revision",
765
+ kind: "string",
766
+ description: "Exact reviewed content fingerprint returned by posts list.",
767
+ required: true,
768
+ },
769
+ {
770
+ name: "approved",
771
+ kind: "boolean",
772
+ description: "Explicit authorization of the exact preview or approval. Never inferred.",
773
+ required: true,
774
+ },
775
+ ],
776
+ },
777
+ {
778
+ action: "brand_approve",
779
+ command: "posts brand-approve",
780
+ description: "Record separate master brand approval of the current post.",
781
+ fields: [
782
+ {
783
+ name: "program_id",
784
+ kind: "string",
785
+ description: "Program ID.",
786
+ required: true,
787
+ },
788
+ {
789
+ name: "post_id",
790
+ kind: "string",
791
+ description: "Canonical post ID.",
792
+ required: true,
793
+ },
794
+ {
795
+ name: "source_revision",
796
+ kind: "string",
797
+ description: "Exact reviewed content fingerprint returned by posts list.",
798
+ required: true,
799
+ },
800
+ {
801
+ name: "approved",
802
+ kind: "boolean",
803
+ description: "Explicit authorization of the exact preview or approval. Never inferred.",
804
+ required: true,
805
+ },
806
+ ],
807
+ },
808
+ {
809
+ action: "voice_import",
810
+ command: "voice import",
811
+ description: "Preview all retrievable LinkedIn history and voice inference; approve a total credit ceiling to start. Use --import-id to resume a safely interrupted import with its saved progress and spend.",
812
+ fields: [
813
+ {
814
+ name: "participation_id",
815
+ kind: "string",
816
+ description: "Creator participation ID.",
817
+ required: true,
818
+ },
819
+ {
820
+ name: "import_id",
821
+ kind: "string",
822
+ description: "Existing safely interrupted import to resume; max-credits is its total ceiling including credits already used.",
823
+ required: false,
824
+ },
825
+ {
826
+ name: "approved",
827
+ kind: "boolean",
828
+ description: "Explicit authorization of the exact preview or approval. Never inferred.",
829
+ required: false,
830
+ },
831
+ {
832
+ name: "max_credits",
833
+ kind: "number",
834
+ description: "Explicit credit ceiling from preview.",
835
+ required: false,
836
+ },
837
+ ],
838
+ },
839
+ {
840
+ action: "voice_save",
841
+ command: "voice save",
842
+ description: "Save your own writing preferences and examples as a private provisional Knowledge voice. Free: 0 credits, no provider call or connected account required. Existing voice edits require its current revision.",
843
+ fields: [
844
+ {
845
+ name: "participation_id",
846
+ kind: "string",
847
+ description: "Your creator participation ID.",
848
+ required: true,
849
+ },
850
+ {
851
+ name: "body",
852
+ kind: "string",
853
+ description: "Your own writing preferences or voice guide (Markdown).",
854
+ required: true,
855
+ },
856
+ {
857
+ name: "writing_examples",
858
+ kind: "array",
859
+ description: "JSON array of up to five examples you actually wrote; do not invent personal experiences.",
860
+ required: false,
861
+ },
862
+ {
863
+ name: "expected_revision",
864
+ kind: "number",
865
+ description: "Current profile revision returned by voice get, required when editing it.",
866
+ required: false,
867
+ },
868
+ ],
869
+ },
870
+ {
871
+ action: "sponsorship_cancel",
872
+ command: "sponsorship cancel",
873
+ description: "Preview stopping renewal for one program sponsorship. Confirm with --approved and a signed-in master admin. Participation and credits through the paid period remain available.",
874
+ fields: [
875
+ {
876
+ name: "program_id",
877
+ kind: "string",
878
+ description: "Master program ID.",
879
+ required: true,
880
+ },
881
+ {
882
+ name: "participation_id",
883
+ kind: "string",
884
+ description: "Exact creator participation ID.",
885
+ required: true,
886
+ },
887
+ {
888
+ name: "approved",
889
+ kind: "boolean",
890
+ description: "Approve the exact cancellation preview. Omit for a free preview.",
891
+ required: false,
892
+ },
893
+ ],
894
+ },
895
+ {
896
+ action: "sponsorship_preview",
897
+ command: "sponsorship preview",
898
+ description: "Preview seat charges and monthly 10,000-credit creator grants as a signed-in master workspace admin. Org API keys cannot create this quote.",
899
+ fields: [
900
+ {
901
+ name: "program_id",
902
+ kind: "string",
903
+ description: "Program ID.",
904
+ required: true,
905
+ },
906
+ {
907
+ name: "participation_ids",
908
+ kind: "array",
909
+ description: "Exact creator participation IDs to sponsor.",
910
+ required: true,
911
+ },
912
+ ],
913
+ },
914
+ {
915
+ action: "sponsorship_confirm",
916
+ command: "sponsorship confirm",
917
+ description: "Confirm the exact sponsorship quote as the same signed-in master workspace admin. Each program pays separately, even for the same account.",
918
+ fields: [
919
+ {
920
+ name: "program_id",
921
+ kind: "string",
922
+ description: "Program ID.",
923
+ required: true,
924
+ },
925
+ {
926
+ name: "participation_ids",
927
+ kind: "array",
928
+ description: "Exact creator participation IDs to sponsor.",
929
+ required: true,
930
+ },
931
+ {
932
+ name: "quote",
933
+ kind: "string",
934
+ description: "Exact sponsorship quote returned by preview.",
935
+ required: true,
936
+ },
937
+ {
938
+ name: "approved",
939
+ kind: "boolean",
940
+ description: "Explicit authorization of the exact preview or approval. Never inferred.",
941
+ required: true,
942
+ },
943
+ ],
944
+ },
945
+ {
946
+ action: "amplification_reconcile",
947
+ command: "amplification reconcile",
948
+ description: "Preview recording a provider observation for an uncertain program action. Confirmation needs a signed-in operator and --approved; it never contacts the provider or authorizes a retry.",
949
+ fields: [
950
+ {
951
+ name: "program_id",
952
+ kind: "string",
953
+ description: "Program ID in the master workspace.",
954
+ required: true,
955
+ },
956
+ {
957
+ name: "action_id",
958
+ kind: "string",
959
+ description: "Unresolved effect_unknown action ID.",
960
+ required: true,
961
+ },
962
+ {
963
+ name: "outcome",
964
+ kind: "string",
965
+ description: "Observed applied or not_applied outcome.",
966
+ required: true,
967
+ },
968
+ {
969
+ name: "evidence",
970
+ kind: "object",
971
+ description: "JSON object: provider_post_id, evidence_ref, observed_at ISO instant; applied needs provider_object_id and comments/reposts need exact observed_text. Optional note.",
972
+ required: true,
973
+ },
974
+ {
975
+ name: "approved",
976
+ kind: "boolean",
977
+ description: "Explicitly confirm this immutable observation. No retry is authorized.",
978
+ required: false,
979
+ },
980
+ ],
981
+ },
982
+ {
983
+ action: "amplification_save",
984
+ command: "amplification save",
985
+ description: "Save exact opted-in sender scope and bounded post-specific amplification. Enabling needs explicit approval.",
986
+ fields: [
987
+ {
988
+ name: "sender_voice_pages",
989
+ kind: "object",
990
+ description: "JSON map from each acting sender UUID to its selected active Knowledge voice page UUID. Required for comments or reposts; the exact mapping is approved with the policy.",
991
+ required: false,
992
+ },
993
+ {
994
+ name: "program_id",
995
+ kind: "string",
996
+ description: "Program ID.",
997
+ required: true,
998
+ },
999
+ {
1000
+ name: "participant_sender_ids",
1001
+ kind: "array",
1002
+ description: "Exact opted-in participant sender IDs.",
1003
+ required: true,
1004
+ },
1005
+ {
1006
+ name: "actions",
1007
+ kind: "array",
1008
+ description: "Allowed post-specific amplification actions.",
1009
+ required: true,
1010
+ },
1011
+ {
1012
+ name: "enabled",
1013
+ kind: "boolean",
1014
+ description: "Enable the bounded amplification policy.",
1015
+ required: true,
1016
+ },
1017
+ {
1018
+ name: "max_actions_per_post",
1019
+ kind: "number",
1020
+ description: "Hard limit for amplification actions on each enrolled published post.",
1021
+ required: true,
1022
+ },
1023
+ {
1024
+ name: "max_credits_per_cycle",
1025
+ kind: "number",
1026
+ description: "Hard credit ceiling for the amplification cycle.",
1027
+ required: true,
1028
+ },
1029
+ {
1030
+ name: "approved",
1031
+ kind: "boolean",
1032
+ description: "Explicit authorization of the exact preview or approval. Never inferred.",
1033
+ required: false,
1034
+ },
1035
+ {
1036
+ name: "delay_min_seconds",
1037
+ kind: "number",
1038
+ description: "Minimum amplification delay, 60–3600 seconds.",
1039
+ required: false,
1040
+ },
1041
+ {
1042
+ name: "delay_max_seconds",
1043
+ kind: "number",
1044
+ description: "Maximum amplification delay, 60–86400 seconds.",
1045
+ required: false,
1046
+ },
1047
+ ],
1048
+ },
1049
+ ];
1050
+ for (const spec of writes) {
1051
+ const [family, leaf] = spec.command.split(" ");
1052
+ const command = group(family).command(leaf).description(spec.description);
1053
+ for (const field of spec.fields) {
1054
+ const flag = `--${field.name.replaceAll("_", "-")}${field.name === "approved" ? "" : ` <${field.kind === "array" || field.kind === "object" ? "json" : field.kind === "boolean" ? "true|false" : "value"}>`}`;
1055
+ if (field.required)
1056
+ command.requiredOption(flag, field.description);
1057
+ else
1058
+ command.option(flag, field.description);
1059
+ }
1060
+ if (spec.action === "invite_create")
1061
+ command.option("--link-only", "Create a shareable link without sending email.");
1062
+ command
1063
+ .option("--json", "Print a JSON envelope.")
1064
+ .action((options) => handle(`ugc ${spec.command}`, options, () => {
1065
+ const body = {
1066
+ action: spec.action,
1067
+ ...(spec.fixed ?? {}),
1068
+ };
1069
+ for (const field of spec.fields) {
1070
+ const key = field.name.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
1071
+ if (options[key] !== undefined)
1072
+ body[field.name] = parseField(options[key], field);
1073
+ }
1074
+ if (spec.action === "invite_create") {
1075
+ body.send_email = options.linkOnly !== true;
1076
+ if (options.linkOnly && (body.approved || body.preview_token))
1077
+ throw new Error("--link-only cannot be combined with --approved or --preview-token.");
1078
+ }
1079
+ return requestOxygen("/api/cli/ugc", {
1080
+ method: "POST",
1081
+ body,
1082
+ ...(spec.action === "draft_create" &&
1083
+ typeof body.request_id === "string"
1084
+ ? { idempotencyKey: body.request_id }
1085
+ : {}),
1086
+ });
1087
+ }));
1088
+ }
1089
+ }