ugcinc 4.25.0 → 4.26.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.
package/README.md CHANGED
@@ -281,6 +281,41 @@ status/logs to inspect failures and the returned video for preview.
281
281
  Server integrations can also call `submitRemotionCompositionRenderJob` and
282
282
  `getRenderJobStatus` with the configured render-worker origin.
283
283
 
284
+ ### Native text stickers
285
+
286
+ `submitNativeTextStickerRenderJob` submits the native classic text/emoji renderer
287
+ and returns the normal render job ID. Poll with `getRenderJobStatus`; the completed
288
+ `download_url` is a transparent PNG usable as an image layer in UGC compositions.
289
+ Server calls require `UGC_RENDER_URL`.
290
+
291
+ ```ts
292
+ import {submitNativeTextStickerRenderJob, getRenderJobStatus} from 'ugcinc';
293
+
294
+ const submitted = await submitNativeTextStickerRenderJob({
295
+ text: 'Hello World 😭🙏', fontSizePt: 28,
296
+ canvasWidthPt: 360, canvasHeightPt: 120,
297
+ containerWidthPt: 320, originPt: [20, 18],
298
+ });
299
+ if (submitted.ok && submitted.data) {
300
+ const status = await getRenderJobStatus(submitted.data.job_id);
301
+ // Poll until completed/failed; use status.data.download_url when completed.
302
+ }
303
+ ```
304
+
305
+ The versioned `tiktok-classic-ios-47` profile uses centered white text, a black
306
+ outline, TikTokDisplay-Medium and AppleColorEmoji. All dimensions are **native
307
+ points**, with fixed 3× output: the example produces 1080×360 pixels. Fractional
308
+ coordinates are preserved. Sizes are 11–72pt; canvas/container sides are 1–1365pt;
309
+ text is limited to 2048 UTF-16 code units. `containerHeightPt` limits visible
310
+ lines; omitted or zero means unlimited lines clipped by the canvas. Optional
311
+ `viewFontSizePt` reproduces a native editor whose view font differs from its text
312
+ run; normally omit it. Defaults are 28pt, a 600×180pt canvas, 560pt container width
313
+ and origin `[20,18]`.
314
+
315
+ Unsupported fallback fonts, bidi, styles and empty paragraphs fail explicitly.
316
+ The profile's recorded native PNG fixtures match exactly; this does not promise
317
+ pixel parity after resizing, browser composition or lossy video encoding.
318
+
284
319
  ## Social reference acquisition
285
320
 
286
321
  Quick reference:
@@ -449,3 +484,34 @@ Inputs: HTTPS image URL and integer display-oriented pixel `box` with a small pl
449
484
  Fitting runs locally through the installed UGC ImageEditorComposition, searching the supported TikTok Sans, SF Pro and Arial-stack choices, weight, size, spacing, line height, alignment and outline. Fill/background/outline colors are estimated from the crop. Arial may use a system fallback; original font identity is always `unverified`. No custom font upload, gradients, perspective, mixed styles, emoji or shadow setting is inferred. Busy borders, blank crops and clipped foreground return explicit unavailable results. These checks do not prove that every remaining crop is suitable: inspect residuals and preview.
450
485
 
451
486
  Returns up to three candidates, renderable settings, `maskIoU`, `foregroundMae` (mean absolute RGB error on the union of reference/rendered ink, scaled0–1), and an owned reference/render/difference preview plus JSON manifest. `quality: 'close'` means IoU>=0.9 and foreground MAE<=0.1; otherwise `approximate`. These are image-fit measures, not font-recognition probabilities or content-quality scores. Single-line alignment/padding cannot be uniquely recovered; returned geometry reproduces visible positioning. Render count, installed UGC version and bundled font hashes are included. Search is capped at320 renders and a480-second search budget within existing job limits; resource failures are explicit failed jobs. The renderer bundle is reused unchanged, with no hosted inference or new font downloads.
487
+
488
+ ## Agent media inspection and content workflows
489
+
490
+ Install or refresh the bundled Claude Code instructions in the agent's project:
491
+
492
+ ```bash
493
+ npx ugcinc@4.26.1 init --update media content-automation automations
494
+ ```
495
+
496
+ `init` preserves existing files by default. `--update` writes changed skill files
497
+ and backs up their previous contents under the project's `.ugcinc/skill-backups/`.
498
+ Optional skill names limit installation/update to those skills. New `media` and
499
+ `content-automation` skills include linked tool/reference guides; no account,
500
+ schedule or publishing changes are performed by installation.
501
+
502
+ Use `UGC_API_KEY` for an existing org-scoped key. When that key already lives in
503
+ an app env file, set `UGC_ENV_FILE` to its path instead of copying the file/key.
504
+ Set `UGC_ORG_ID` in that file or the process environment to select the intended
505
+ organization; a JSON `orgId` overrides that default.
506
+ Environment credentials take precedence over the explicit env file, then saved
507
+ UGC CLI configuration and existing `.env` lookup. Acquisition and analysis need
508
+ `content:analyze` for restricted keys. The CLI can discover tool names with
509
+ `npx ugcinc --list`; JSON parameters/limits are documented in the media skill.
510
+
511
+ The media skill covers all eleven shipped acquisition/inspection operations,
512
+ including text-style fitting and the supporting analysis-status helper. The
513
+ content-automation skill covers reusable workflow inputs, real-reference review,
514
+ layout/readability, asset identity, procedural animation, CTA and audio handling.
515
+ It distinguishes measured facts from inference and requires verified public
516
+ HTTPS previews. SAM-dependent capabilities remain deferred and are not advertised
517
+ as available tools. A successful API call alone does not certify content quality.
package/dist/cli.js CHANGED
@@ -72,6 +72,11 @@ function saveConfig(config) {
72
72
  function loadEnvVar(name) {
73
73
  if (process.env[name])
74
74
  return process.env[name];
75
+ if (process.env.UGC_ENV_FILE) {
76
+ const value = checkEnvFile(path.resolve(process.env.UGC_ENV_FILE), name);
77
+ if (value)
78
+ return value;
79
+ }
75
80
  // Check ~/.ugcinc/config.json
76
81
  const config = loadConfig();
77
82
  if (name === 'UGC_API_KEY' && config.apiKey)
@@ -104,10 +109,12 @@ function printUsage() {
104
109
  console.log('');
105
110
  console.log('Commands:');
106
111
  console.log(' auth <api_key> [--admin] Save API key (use --admin for admin keys)');
107
- console.log(' init Set up Claude Code skills in your project');
112
+ console.log(' init [--update] [skills...] Install skills; --update backs up changed files');
108
113
  console.log(' <tool_name> [json_params] Run an API tool');
109
114
  console.log(' --list List all available tools as JSON');
110
115
  console.log(' --help Show this help message');
116
+ console.log(' UGC_ENV_FILE Read credentials from an existing env file');
117
+ console.log(' UGC_ORG_ID Default organization (JSON orgId takes priority)');
111
118
  console.log('');
112
119
  console.log('Available tools:');
113
120
  for (const tool of tools_1.allTools) {
@@ -130,20 +137,47 @@ function runAuth(args) {
130
137
  if (config.admin === 'true')
131
138
  console.log('Mode: admin');
132
139
  }
133
- function runInit() {
140
+ function runInit(args) {
134
141
  const cwd = process.cwd();
135
142
  const skillsDir = path.join(cwd, '.claude', 'skills');
143
+ const update = args.includes('--update');
144
+ const requested = args.filter(arg => !arg.startsWith('--'));
145
+ const known = new Set(Object.keys(skills_1.skillFiles).map(name => name.split('/')[0] ?? ''));
146
+ const unknown = args.filter(arg => arg.startsWith('--') && arg !== '--update');
147
+ const invalid = requested.filter(name => !known.has(name));
148
+ if (unknown.length || invalid.length) {
149
+ console.error('Unknown skill or option: ' + [...unknown, ...invalid].join(', '));
150
+ process.exit(1);
151
+ }
136
152
  let created = 0;
137
153
  let skipped = 0;
154
+ let updated = 0;
155
+ let backupDir;
138
156
  for (const [skillPath, content] of Object.entries(skills_1.skillFiles)) {
157
+ if (requested.length && !requested.includes(skillPath.split('/')[0] ?? ''))
158
+ continue;
139
159
  const fullPath = path.join(skillsDir, skillPath);
140
160
  const dir = path.dirname(fullPath);
141
161
  if (!fs.existsSync(dir)) {
142
162
  fs.mkdirSync(dir, { recursive: true });
143
163
  }
144
164
  if (fs.existsSync(fullPath)) {
145
- skipped++;
146
- console.log(` skip ${skillPath} (already exists)`);
165
+ if (!update || fs.readFileSync(fullPath, 'utf-8') === content) {
166
+ skipped++;
167
+ console.log(` skip ${skillPath} (already exists${update ? '; unchanged' : ''})`);
168
+ continue;
169
+ }
170
+ if (!backupDir) {
171
+ const base = path.join(cwd, '.ugcinc', 'skill-backups');
172
+ fs.mkdirSync(base, { recursive: true });
173
+ backupDir = fs.mkdtempSync(path.join(base, 'update-'));
174
+ }
175
+ const backup = path.join(backupDir, skillPath);
176
+ fs.mkdirSync(path.dirname(backup), { recursive: true });
177
+ fs.copyFileSync(fullPath, backup, fs.constants.COPYFILE_EXCL);
178
+ fs.writeFileSync(fullPath, content);
179
+ updated++;
180
+ console.log(` update ${skillPath} (previous copy backed up)`);
147
181
  }
148
182
  else {
149
183
  fs.writeFileSync(fullPath, content);
@@ -152,7 +186,9 @@ function runInit() {
152
186
  }
153
187
  }
154
188
  console.log('');
155
- console.log(`Done! Created ${created} skill files${skipped > 0 ? `, skipped ${skipped} existing` : ''}.`);
189
+ console.log(`Done! Created ${created}, updated ${updated}, skipped ${skipped} skill files.`);
190
+ if (backupDir)
191
+ console.log('Backups: ' + backupDir);
156
192
  console.log('');
157
193
  console.log('Next steps:');
158
194
  console.log(' 1. Set your API key: npx ugcinc auth <your-api-key>');
@@ -162,6 +198,7 @@ function runInit() {
162
198
  console.log(' /accounts Manage accounts, warmup, and social profiles');
163
199
  console.log(' /posts Schedule, update, and manage posts');
164
200
  console.log(' /media Manage media library and social audio');
201
+ console.log(' /content-automation Develop and review reusable content workflows');
165
202
  console.log(' /automations Run and manage automation workflows');
166
203
  console.log(' /stats View account and post statistics');
167
204
  }
@@ -191,8 +228,12 @@ async function runTool(toolName, rawParams) {
191
228
  delete parsed.orgId;
192
229
  }
193
230
  const config = loadConfig();
194
- const isAdmin = config.admin === 'true';
195
- const client = new client_1.UGCClient({ apiKey, orgId: orgId ?? config.orgId, admin: isAdmin });
231
+ const explicitEnvKey = process.env.UGC_API_KEY;
232
+ const explicitFileKey = process.env.UGC_ENV_FILE
233
+ ? checkEnvFile(path.resolve(process.env.UGC_ENV_FILE), 'UGC_API_KEY') : undefined;
234
+ const usesSavedKey = !explicitEnvKey && !explicitFileKey && apiKey === config.apiKey;
235
+ const isAdmin = usesSavedKey && config.admin === 'true';
236
+ const client = new client_1.UGCClient({ apiKey, orgId: orgId ?? loadEnvVar('UGC_ORG_ID') ?? (usesSavedKey ? config.orgId : undefined), admin: isAdmin });
196
237
  let params = undefined;
197
238
  if (parsed) {
198
239
  const parseResult = tool.schema.safeParse(parsed);
@@ -239,7 +280,7 @@ async function main() {
239
280
  process.exit(0);
240
281
  }
241
282
  if (args[0] === 'init') {
242
- runInit();
283
+ runInit(args.slice(1));
243
284
  process.exit(0);
244
285
  }
245
286
  await runTool(args[0], args[1]);
@@ -0,0 +1 @@
1
+ export declare const contentSkillFiles: Record<string, string>;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.contentSkillFiles = void 0;
4
+ // Generated by scripts/build-skills.cjs; edit skills/** instead.
5
+ exports.contentSkillFiles = {
6
+ "media/SKILL.md": "---\nname: media\ndescription: Acquire TikTok and Instagram posts and inspect images, video and audio with UGC Inc tools. Use for reference analysis, OCR, frames, scenes, transcription, colors, layout, text-style fitting, or media-library management.\nallowed-tools: Bash, Read\n---\n\nUse the installed UGC Inc CLI:\n\n```bash\nnpx ugcinc --list\nnpx ugcinc <tool_name> '<json_params>'\n```\n\nCredentials come from `UGC_API_KEY` or existing UGC configuration. Use the user's\norganization and a key with `content:analyze` for acquisition/analysis. If using\nan admin key, pass the authorized `orgId` in the JSON parameters. Do not print\ncredentials, copy an administrator key to another user's machine, or change\norganizations to bypass a denial. Confirm access with one small tool call.\nIf credentials already live in an app env file, set `UGC_ENV_FILE` to that file's\npath so the CLI reads the existing key without duplicating its contents. Set\n`UGC_ORG_ID` in that file or the process environment to scope calls to the user's\norganization; an explicit JSON `orgId` takes priority.\n\nRead [references/tools.md](references/tools.md) for request shapes, result fields\nand limits. The CLI lists names/descriptions; the reference explains parameters.\n\n## Choose the input and tools\n\n- A profile URL: `list_profile_posts` discovers a bounded page; follow its cursor\n only as needed. Listing does not download media or guarantee complete history.\n- A post URL: `download_social_post` ingests its ordered assets into owned media.\n Use returned asset URLs for analysis; the TikTok/Instagram HTML URL is not a\n video file. Check complete/partial/failed status and failed assets.\n- A direct media URL: use the corresponding inspection tool directly.\n- Video: `detect_scenes`, `extract_frames`, `read_video_text`; audio tools accept\n video too. Inspect multiple moments, including changes near the beginning/end.\n- Image: `read_image_text`, `sample_colors`, `measure_layout` using supplied\n boxes/masks, and `match_text_style` for a specified text crop.\n- Audio: `transcribe_audio` and `measure_audio_levels`.\n\nThe SDK/CLI waits for processing automatically. Check both the outer response's\n`ok` and the returned job's `status`. Analysis is usable when\n`data.status === \"completed\"`; its content is `data.result`. If still pending or\nprocessing, keep its job ID and use `get_media_analysis` rather than resubmitting.\nUse bounded retries for retryable failures; report persistent errors/partial\ncoverage. A zero process exit code alone does not establish API success.\n\n## Interpret evidence accurately\n\n- Coordinates are display-oriented source pixels; times are source-relative\n milliseconds. Use returned decoded timestamps, dimensions and coverage.\n- OCR is English, may miss stylized text, and can be empty on unreadable input.\n `fast` video OCR leaves uninspected intervals. `frame_scan` is bounded and\n cannot inspect an arbitrarily long clip in one request.\n- Speech timestamps are estimates; speaker labels/forced alignment are absent.\n A transcript does not establish music, emotion, delivery or sound effects.\n- Color sampling measures decoded sRGB pixels, not guaranteed design hex values.\n Layout measurement does not detect objects or recover original layer files.\n- Text-style matching returns supported UGC settings and visual residuals. Font\n identity is unverified; inspect the preview before calling a fit accurate.\n It has a restricted ASCII/font/style surface; the native TikTok renderer is a\n separate bounded rendering capability, not proof that matching is exact.\n- SAM/object segmentation, automatic overlay detection, element/mask tracking,\n motion measurement, extraction, media comparison/alignment and semantic search\n are not available through this tool set. Do not invent those calls or install\n SAM. Existing OCR text tracks remain available.\n\n## Review and share media\n\nOpen returned image/contact-sheet previews and review the relevant sequence.\nWhen the agent runtime can consume audio/video, use it. Otherwise use the\navailable transcript, audio preview and level measurements and explicitly state\nwhat was not listened to or inspected. Do not claim to have watched/listened\nbecause an analysis request succeeded.\n\nShare only verified browser-accessible HTTPS artifacts. Fetch without auth and\ncheck status, content type and decoding; for video also check byte-range support.\nUse the returned owned-media URLs. Local paths, localhost/private addresses and\nexpiring upstream CDN links are not deliverable previews. If an artifact needs\nuploading, use the supported owned storage path; do not claim a file is hosted\nbecause a media record was created for an arbitrary URL.\n\nFor building a reusable content format, use the `content-automation` skill.\n",
7
+ "media/references/tools.md": "# UGC media tool requests and results\n\nRun `npx ugcinc TOOL 'JSON'`. Shell quoting varies on Windows; if the shell\nchanges the JSON, use a small Node script with `UGCClient` and its `media`\nmethods instead of repeatedly sending malformed requests. For example:\n\n```js\nconst { UGCClient } = require('ugcinc');\nconst client = new UGCClient({\n apiKey: process.env.UGC_API_KEY,\n orgId: process.env.UGC_ORG_ID,\n});\n// Use admin:true only with an authorized administrator credential.\n```\n\nRequest URLs must be HTTPS. Analysis source URLs must not contain credentials\nor fragments. Image operations support still PNG/JPEG/WebP, at most4096pixels\nper side; the shared download limit is100MiB. Geometry must fit the actual\ndisplay-oriented source. Respect more restrictive operation limits below.\n\n## Acquisition\n\n| CLI tool / SDK method | Parameters | Result |\n| --- | --- | --- |\n| `list_profile_posts` / `media.listProfilePosts` | `{url, limit?, cursor?}`; TikTok/Instagram profile; limit1–50 | `data.posts`, profile metadata, continuation cursor. Follow the actual returned cursor, not a fabricated page number. |\n| `download_social_post` / `media.downloadSocialPost` | `{url}`; canonical TikTok/Instagram post URL | `data.status`, `assets[]` in order, `failedAssets[]`, `manifestUrl`, post/sound metadata. Each asset has `url`, `mediaId`, dimensions, type, duration, audio presence and hash. Repeat the same URL to resume retryable partial imports. |\n\nEmbedded video audio remains attached. Photo assets have no embedded soundtrack;\nsound attribution does not mean audio bytes were downloaded. Preserve sound\nID/link separately; never replace a post excerpt with the full sound-page track\nand call them identical. Partial imports are not a complete carousel.\n\n## Analysis\n\nThese return `{ok, data:{jobId,status,operation,result?,error?}}`. Use the completed\n`data.result` fields listed below. Failure can occur after successful submission.\n\n| CLI tool / SDK method | Parameters | Result content |\n| --- | --- | --- |\n| `extract_frames` / `media.extractFrames` | `{url,timestampsMs:[0,1500,3000],selection:\"nearest\",contactSheet:true}` OR `{url,range:{startMs,endMs,intervalMs}}`; choose exactly one selector, at most48samples | `frames[]` with requested/decoded time, source PTS, dimensions and URL; optional `contactSheet` with individual frame links; source metadata. |\n| `detect_scenes` / `media.detectScenes` | `{url}` | `scenes[]` with start/end milliseconds, frame indexes, source PTS; no split MP4 exports. |\n| `read_image_text` / `media.readImageText` | `{url}` | `regions[]` with text/rawText, polygons/boxes/confidence; inferred `lines[]`/reading order; `previewUrl`. |\n| `read_video_text` / `media.readVideoText` | `{url,mode:\"fast\",intervalMs:1000,range?:{startMs,endMs}}` OR `{url,mode:\"frame_scan\",range?:{startMs,endMs}}` | `frames[]`, text `tracks[]` with observations/positions; explicit `coverage.inspected` and `uninspected`. Maximum240inspected frames and120seconds per request. `intervalMs` only applies to fast mode. |\n| `transcribe_audio` / `media.transcribeAudio` | `{url,range?:{startMs,endMs},language?:\"auto\",timing?:\"word\",channel?:0}` | `status`, `text`, timed `segments[]` and estimated words, warnings, `audioPreviewUrl`. Range at most120seconds; timing `word` or `segment`; optional channel0/1. Empty/no-audio/no-speech states are distinct. |\n| `measure_audio_levels` / `media.measureAudioLevels` | `{url,range?:{startMs,endMs},windowMs?:100,silenceThresholdDbfs?:-50}` | Native-channel RMS/sample peaks, loudness timeline/LUFS, silence, possible clipping, `previewUrl`. Windows20–5000ms, threshold−100to−10dBFS, range at most120seconds. |\n| `sample_colors` / `media.sampleColors` | `{url,maxColors?:8,point?:{x,y},box?:{x,y,width,height},mask?}`; choose at most one selection | Observed RGBA point or palette/proportions, omitted mass, declared color policy, `previewUrl`. Palette1–32colors. |\n| `measure_layout` / `media.measureLayout` | `{url,elements:[{id,box:{x,y,width,height}}]}`; each element may instead have `mask` | Margins, centers, spacing/alignment deltas, intersections/IoU and `previewUrl`.1–32unique IDs. Boxes are supplied, not automatically detected. |\n| `match_text_style` / `media.matchTextStyle` | `{url,box:{x,y,width,height},text?}`; integer crop16–1536wide,16–512high; optional supplied text | `status`, candidates with real image-editor `element`/`previewConfig`, `maskIoU`, `foregroundMae`, `quality`, previews/comparison and warnings. Up to160printable ASCII characters/four lines; supported fonts Arial/TikTok/Apple. Missing/unsupported styles may be unavailable or approximations. |\n| `get_media_analysis` / `media.getMediaAnalysis` | `{jobId}` | Current status/result in the current organization; use when automatic waiting returned pending/processing. |\n\nBoxes are half-open source-pixel rectangles. For masks used by colors/layout:\n`{width,height,counts}` uses row-major binary RLE, alternating background then\nforeground. Counts must sum to width×height. This is not COCO's column-major\nconvention. Supply only masks you actually have; automatic segmentation is off.\n\n## Existing media library\n\n- `list_media({ids?,tag?})`, `list_social_audio({ids?,tag?})`.\n- `create_media_from_urls({urls,names?,tag?,sourceIds?,useName?})` registers URLs;\n it does not turn an inaccessible local file into a public upload.\n- `import_text({texts:[{content,name?,tag?}]})`.\n- `create_social_audio({url,tag?})` imports social audio separately; do not use it\n as proof of the exact soundtrack/mix/offset in a downloaded reference video.\n- `delete_media({ids})` removes media/storage; only use when deletion is requested.\n",
8
+ "content-automation/SKILL.md": "---\nname: content-automation\ndescription: Turn reference social posts or a rough content idea into a reusable UGC Inc workflow, with editable inputs and reviewed visual/audio output. Use for developing and validating content formats rather than scheduling existing finished posts.\nallowed-tools: Bash, Read\n---\n\nBuild the format the user wants and collaborate at the level they requested.\nWhen they want to discuss, compare or review a specific artifact, do that before\nmaking additional format decisions. Keep questions concise and ask only when a\nmissing answer prevents useful work. Do not impose a new per-video reference input\nwhen the user wants a fixed reusable visual format.\n\nUse the `media` skill for acquisition/inspection and the `automations` skill for\ncurrent workflow tool calls. Read [references/build-review.md](references/build-review.md)\nwhen constructing or reviewing a workflow. Use only shipped tools/nodes discovered\nthrough `npx ugcinc --list`, `list_node_types` and `get_node_type`.\n\n## Understand and specify the format\n\nAcquire a bounded set of real references and inspect full frames and changes over\ntime, including audio using available modalities. A contact sheet or transcript\nalone cannot establish all animation/pacing/music details. State uninspected\nparts instead of claiming playback. Distinguish reusable formats from variants;\nseveral posts may use the same structure with different assets, copy or timing.\n\nWrite a compact spec: what the viewer sees first, the sequence/reveal, required\ninputs, reusable assets/components, changing copy, audio source/timing and final\naction/CTA if required by this format. Separate measured facts from inference\nand user choices. Share a verified HTTPS reference/preview when the user needs\nto identify the parts they want changed.\n\nClassify assets by replacement behavior. A recognizable Mii, Wojak or other exact\nmeme image may need a static asset; an ordinary or newly generated face need not\nbe static just because it looks meme-like. Preserve a headshot's identity across\nvariants when requested, while making intended differences visibly meaningful.\n\n## Build reusable production behavior\n\nSeparate layout, data/assets and motion. Expose the inputs a normal run needs:\nasset pools/overrides, text pools, contact/destination, duration/timing and seed\nwhere appropriate. Keep canonical outputs independent of an operator's laptop.\nImplement procedural animations as reusable components rendered on demand;\ndo not substitute one pre-rendered clip when each run should vary. Randomness\nmust respect the intended visual roles and remain replayable from saved inputs.\n\nReuse existing nodes/compositions and actual public schemas. Analysis tools are\navailable as CLI/SDK operations; do not assume every one is a workflow node.\nValidate the graph before executing. Prefer a reviewable output over publishing\nor enabling recurring runs unless the user authorized those actions.\n\n## Make the content work\n\n- Keep the hierarchy clear: readable hook, focal content, then the intended CTA.\n Give characters/subjects enough visual weight; align visible content within\n its section instead of trusting transparent image bounds alone.\n- Measure margins, spacing, text wrapping and overlap. Check at phone viewing\n size, not only on a large canvas. Keep important text away from platform UI;\n inspect the relevant target presentation rather than assuming universal margins.\n- Use sampled reference colors and real render previews. Judge font, stroke,\n kerning, line height and emoji spacing together. Treat text-style fitting as a\n measured candidate, not proof of an exact font or pixel-perfect output.\n- Match the reference post's actual audio excerpt, mix, start and duration when\n requested. Preserve attribution separately. Empty transcription can mean music\n or unrecognized speech, not silence. Keep visual reveals and audio timing coherent.\n- Keep required CTA/contact copy present and legible across every requested\n variant. Use the user's actual destination/formatting, not a number copied from\n an old example. Vary copy intentionally without altering the required action.\n- Test with real content and multiple asset/copy lengths. A composition that\n works with one face or one short headline is not a validated reusable format.\n Inspect early, changing and final states; a still cannot certify a full video.\n\n## Deliver and describe the boundary\n\nProvide the workflow, its normal inputs, replayable example parameters, verified\nHTTPS previews, and any observed visual/audio limits. For videos verify decoding,\nduration/dimensions/audio and byte-range playback; images need correct content\ntype and decoding. Do not use local/localhost/private URLs as deliverables.\n\nSAM and automatic segmentation/overlay tracking are deferred. Use supplied\nassets, user-supplied boxes/masks or honest visual analysis; do not claim recovered\nsource layers or automatic clean cutouts. If the runtime cannot inspect a media\nmodality, record that limit and do not certify the uninspected property.\n",
9
+ "content-automation/references/build-review.md": "# Construct and review a reusable workflow\n\n## Discover the real interface\n\n`npx ugcinc list_node_types '{}'` lists current node metadata.\nUse `get_node_type '{\"nodeType\":\"...\"}'` for the nodes you choose and read the\nbundled `automations` skill for current tool parameters. These tools still need\na configured CLI key even when their own operation is local.\n\nConnections live on the target node, not a separate edges array:\n\n```json\n{\n \"nodes\": [\n {\n \"id\": \"target\",\n \"type\": \"node-type-from-discovery\",\n \"config\": {},\n \"inputs\": {\n \"actual-input-port\": {\n \"sourceNodeId\": \"source\",\n \"sourceOutputId\": \"actual-output-port\"\n }\n }\n }\n ]\n}\n```\n\nUse actual IDs/configuration returned by discovery, not these placeholders.\nConfigure dynamic interfaces before wiring them. Re-check ports after config\nchanges. Current validation requires a terminal outcome; inspect pass-through,\nsave-to-media and posting nodes to choose the intended behavior. Publishing is\nnot required just to make a reviewable workflow output.\n\nFor reusable sub-workflows, configure their input/output contracts explicitly\nand inspect the Compose Workflow node's actual schema. For custom Remotion,\ninspect `remotion-composition`; use its supported composition/props/import\ncontract. Do not invent an upload/register API or rely on arbitrary packages.\n\nValidate using `validate_workflow` with the shape described by the installed\nautomation skill/tool. Execute only the intended single test scope, inspect its\nstatus/output, and retain resolved inputs and seed. Saving a template or receiving\na run ID alone does not prove the output renders correctly.\n\n## Review a representative run\n\nCheck the viewer's actual sequence: hook visibility, subject size, changing state,\ntext wrapping/CTA, transitions and finish. Compare the full frame before zooming\ninto details. Inspect multiple points around animation/text changes. Re-test a\nlonger caption and a different asset where those vary in normal runs.\n\nDistinguish observed pixels/timestamps from estimates. Examples: sRGB sampling\ndoes not recover design colors from compression, transcript word timestamps are\nestimated, sampled OCR leaves gaps, and bounding boxes are not exact ink bounds.\nManually supplied boxes are allowed; describe how they were determined.\n\nPreserve both soundtrack bytes and source attribution. Check whether music is\nembedded or only attributed, whether the output repeats/truncates it, and whether\nvisual changes coincide with the intended moments. Do not invent beat detection,\naudio separation, universal media comparison or other unavailable tools.\n\nWhen revising content, name the concrete issue and inspect the changed output.\nKeep reusable parameters instead of baking a correction into one reference\nimage. Report remaining defects plainly; avoid calling a draft production-ready\nbased only on successful API execution.\n"
10
+ };
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@ export { AutomationsClient } from './automations';
13
13
  export { MediaClient } from './media';
14
14
  export { CommentsClient } from './comments';
15
15
  export { BillingClient } from './billing';
16
- export { submitImageRenderJob, submitVideoRenderJob, getRenderJobStatus, submitDeduplicationJob, submitSceneSplitJob, submitScreenshotAnimationRenderJob, submitRemotionCompositionRenderJob, submitAutoCaptionRenderJob, submitInstagramDmRenderJob, submitIMessageDmRenderJob, } from './render';
16
+ export { submitImageRenderJob, submitVideoRenderJob, getRenderJobStatus, submitDeduplicationJob, submitSceneSplitJob, submitScreenshotAnimationRenderJob, submitRemotionCompositionRenderJob, submitNativeTextStickerRenderJob, nativeTextStickerSchema, submitAutoCaptionRenderJob, submitInstagramDmRenderJob, submitIMessageDmRenderJob, } from './render';
17
17
  export { remotionCompositionSchema, remotionCompositionNodeSchema, remotionCompositionPortSchema, REMOTION_COMPOSITION_IMPORTS, parseRemotionPropsJson } from './render/types/remotion-composition';
18
18
  export type { RemotionCompositionInput } from './render/types/remotion-composition';
19
19
  export type { RemotionCompositionNodeConfig } from './automations/nodes/remotion-composition';
@@ -36,7 +36,7 @@ export type { Org, ApiKey, DeleteApiKeyParams, EditApiKeyParams, IntegrationKey,
36
36
  export type { UserMedia, MediaUse, SocialAudio, Media, GetMediaParams, GetSocialAudioParams, UploadMediaParams, UploadMediaResponse, MediaTagUpdate, UpdateMediaTagsParams, MediaTagUpdateResult, UpdateMediaTagsResponse, UpdateMediaTagParams, UpdateMediaNameParams, DeleteMediaParams, DeleteMediaResponse, CreateSocialAudioParams, ImportTextParams, ImportTextResponse, CreateMediaFromUrlParams, GetMediaUseParams, GetMediaUseResponse, FilterMediaParams, FilterMediaResponse, PfpSearchParams, PfpSearchResponse, PfpCandidate, ImageSearchParams, ImageSearchResult, ImageSearchResponse, ImageSearchVideoFormat, } from './media';
37
37
  export type { CommentStatus, Comment, CreateCommentParams, CreateCommentResponse, GetCommentsParams, } from './comments';
38
38
  export type { BillingInfo, PortalUrlResponse, CancelAccountParams, CancelAccountResponse, ReactivateAccountParams, ReactivateAccountResponse, CancelSubscriptionResponse, RequestReplacementParams, RequestReplacementResponse, RequestRefundParams, RequestRefundResponse, BillingRequestInfo, PortalParams, } from './billing';
39
- export type { RenderJobResponse, RenderJobStatus, SubmitImageRenderJobParams, SubmitVideoRenderJobParams, SubmitScreenshotAnimationRenderJobParams, SubmitAutoCaptionRenderJobParams, SubmitSceneSplitJobParams, SceneSplitDetectorConfig, SubmitInstagramDmRenderJobParams, SubmitIMessageDmRenderJobParams, IgDmMessage, ImDmMessage, RenderVideoEditorConfig, } from './render';
39
+ export type { RenderJobResponse, RenderJobStatus, SubmitImageRenderJobParams, NativeTextStickerInput, SubmitVideoRenderJobParams, SubmitScreenshotAnimationRenderJobParams, SubmitAutoCaptionRenderJobParams, SubmitSceneSplitJobParams, SceneSplitDetectorConfig, SubmitInstagramDmRenderJobParams, SubmitIMessageDmRenderJobParams, IgDmMessage, ImDmMessage, RenderVideoEditorConfig, } from './render';
40
40
  export type { VideoEditorNodeConfig, VideoEditorChannel, VideoEditorSegment, VideoEditorVideoSegment, VideoEditorAudioSegment, VideoEditorImageSegment, VideoEditorTextSegment, VideoEditorImageSequenceSegment, VideoEditorVideoSequenceSegment, TimeValue, TimeMode, SegmentTimelinePosition, DeduplicationLevel, DeduplicationInput, ImageEditorElement, DimensionPresetKey, } from './render/types';
41
41
  export type { ImageEditorNodeConfig, ImageComposerNodeConfig, ImageComposerRenderInput } from './automations/nodes/image-composer';
42
42
  export { prepareImageComposerInput } from './automations/nodes/image-composer';
package/dist/index.js CHANGED
@@ -5,9 +5,9 @@
5
5
  * Official TypeScript/JavaScript client for the UGC Inc API
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.hasMissingTriggerError = exports.getPortErrorsForNode = exports.getErrorNodeIds = exports.validateWorkflow = exports.getCapturedNodes = exports.getNodesDownstreamOfSet = exports.getDownstreamNodes = exports.checkCrossContextViolation = exports.getForEachContext = exports.getConnectedSource = exports.cleanupStaleConnections = exports.removeNodeConnections = exports.removeConnection = exports.addConnection = exports.deriveConnections = exports.createNode = exports.getOutputSchema = exports.getNodeByType = exports.getAllNodes = exports.getInputPreviewValue = exports.computeAllNodePortsWithPreviews = exports.computeAllNodePorts = exports.computePortsWithPreviews = exports.computePortsForNode = exports.areTypesCompatible = exports.parseRemotionPropsJson = exports.REMOTION_COMPOSITION_IMPORTS = exports.remotionCompositionPortSchema = exports.remotionCompositionNodeSchema = exports.remotionCompositionSchema = exports.submitIMessageDmRenderJob = exports.submitInstagramDmRenderJob = exports.submitAutoCaptionRenderJob = exports.submitRemotionCompositionRenderJob = exports.submitScreenshotAnimationRenderJob = exports.submitSceneSplitJob = exports.submitDeduplicationJob = exports.getRenderJobStatus = exports.submitVideoRenderJob = exports.submitImageRenderJob = exports.BillingClient = exports.CommentsClient = exports.MediaClient = exports.AutomationsClient = exports.OrganizationClient = exports.StatsClient = exports.PostsClient = exports.TasksClient = exports.AccountsClient = exports.UGCClient = void 0;
9
- exports.parseOpenAPIOutputPath = exports.parseOpenAPIInputs = exports.mapOpenAPIType = exports.nodeConfigToCaptionStyle = exports.HttpMethods = exports.prepareImageComposerInput = exports.substituteVariables = exports.processTemplate = exports.extractTemplateVariables = exports.PortIdSchema = exports.normalizeToPortId = exports.portIdToTitle = exports.isValidPortId = exports.portId = exports.selectFromPool = exports.getModelDurations = exports.getModelAspectRatios = exports.ALL_VIDEO_MODELS = exports.getAvailableInputModes = exports.hasReferenceVariant = exports.getReferenceModelId = exports.hasFirstLastFrameVariant = exports.getFirstLastFrameModelId = exports.hasImageToVideoVariant = exports.getImageToVideoModelId = exports.isImageToVideoModel = exports.IMAGE_ASPECT_RATIOS = exports.ALL_IMAGE_MODELS = exports.getEditModelId = exports.isEditModel = exports.getCapturedSourceNodeTypes = exports.getFlowControlNodeTypes = exports.getAllNodeDefinitions = exports.getNodeDefinition = exports.isPortType = exports.formatPortType = exports.isAsyncExecutor = exports.internalNodeTypes = exports.nodeDefinitions = exports.extractWorkflowConfig = exports.generateNodeName = exports.shuffleNodePreview = exports.getPreviewValue = exports.removePreviewForConnection = exports.updatePreviewMapForConnection = exports.computePreviewMap = exports.computeAllNodePreviews = exports.resolveNodePreview = exports.getWorkflowOutputSchema = exports.hasMissingTerminalError = void 0;
10
- exports.mediaAnalysisResultSchema = exports.mediaAnalysisStatusSchema = exports.detectScenesSchema = exports.extractFramesSchema = exports.videoTextResultSchema = exports.imageTextResultSchema = exports.readVideoTextSchema = exports.readImageTextSchema = exports.audioLevelsResultSchema = exports.audioTranscriptResultSchema = exports.measureAudioLevelsSchema = exports.transcribeAudioSchema = exports.binaryMaskSchema = exports.measureLayoutResultSchema = exports.sampleColorsResultSchema = exports.measureLayoutSchema = exports.sampleColorsSchema = exports.matchTextStyleResultSchema = exports.matchTextStyleSchema = exports.prepareVideoComposerInput = exports.formatDateTag = exports.outputFieldsToZod = exports.outputFieldToZod = exports.LLMProviders = exports.applyLogicOperator = exports.IfLogicOperators = exports.resolvePath = exports.indexExpressionToString = exports.indexExpressionToIndexes = exports.resolveUnknownPath = exports.getOutputTypeFromCategory = void 0;
8
+ exports.getErrorNodeIds = exports.validateWorkflow = exports.getCapturedNodes = exports.getNodesDownstreamOfSet = exports.getDownstreamNodes = exports.checkCrossContextViolation = exports.getForEachContext = exports.getConnectedSource = exports.cleanupStaleConnections = exports.removeNodeConnections = exports.removeConnection = exports.addConnection = exports.deriveConnections = exports.createNode = exports.getOutputSchema = exports.getNodeByType = exports.getAllNodes = exports.getInputPreviewValue = exports.computeAllNodePortsWithPreviews = exports.computeAllNodePorts = exports.computePortsWithPreviews = exports.computePortsForNode = exports.areTypesCompatible = exports.parseRemotionPropsJson = exports.REMOTION_COMPOSITION_IMPORTS = exports.remotionCompositionPortSchema = exports.remotionCompositionNodeSchema = exports.remotionCompositionSchema = exports.submitIMessageDmRenderJob = exports.submitInstagramDmRenderJob = exports.submitAutoCaptionRenderJob = exports.nativeTextStickerSchema = exports.submitNativeTextStickerRenderJob = exports.submitRemotionCompositionRenderJob = exports.submitScreenshotAnimationRenderJob = exports.submitSceneSplitJob = exports.submitDeduplicationJob = exports.getRenderJobStatus = exports.submitVideoRenderJob = exports.submitImageRenderJob = exports.BillingClient = exports.CommentsClient = exports.MediaClient = exports.AutomationsClient = exports.OrganizationClient = exports.StatsClient = exports.PostsClient = exports.TasksClient = exports.AccountsClient = exports.UGCClient = void 0;
9
+ exports.mapOpenAPIType = exports.nodeConfigToCaptionStyle = exports.HttpMethods = exports.prepareImageComposerInput = exports.substituteVariables = exports.processTemplate = exports.extractTemplateVariables = exports.PortIdSchema = exports.normalizeToPortId = exports.portIdToTitle = exports.isValidPortId = exports.portId = exports.selectFromPool = exports.getModelDurations = exports.getModelAspectRatios = exports.ALL_VIDEO_MODELS = exports.getAvailableInputModes = exports.hasReferenceVariant = exports.getReferenceModelId = exports.hasFirstLastFrameVariant = exports.getFirstLastFrameModelId = exports.hasImageToVideoVariant = exports.getImageToVideoModelId = exports.isImageToVideoModel = exports.IMAGE_ASPECT_RATIOS = exports.ALL_IMAGE_MODELS = exports.getEditModelId = exports.isEditModel = exports.getCapturedSourceNodeTypes = exports.getFlowControlNodeTypes = exports.getAllNodeDefinitions = exports.getNodeDefinition = exports.isPortType = exports.formatPortType = exports.isAsyncExecutor = exports.internalNodeTypes = exports.nodeDefinitions = exports.extractWorkflowConfig = exports.generateNodeName = exports.shuffleNodePreview = exports.getPreviewValue = exports.removePreviewForConnection = exports.updatePreviewMapForConnection = exports.computePreviewMap = exports.computeAllNodePreviews = exports.resolveNodePreview = exports.getWorkflowOutputSchema = exports.hasMissingTerminalError = exports.hasMissingTriggerError = exports.getPortErrorsForNode = void 0;
10
+ exports.mediaAnalysisResultSchema = exports.mediaAnalysisStatusSchema = exports.detectScenesSchema = exports.extractFramesSchema = exports.videoTextResultSchema = exports.imageTextResultSchema = exports.readVideoTextSchema = exports.readImageTextSchema = exports.audioLevelsResultSchema = exports.audioTranscriptResultSchema = exports.measureAudioLevelsSchema = exports.transcribeAudioSchema = exports.binaryMaskSchema = exports.measureLayoutResultSchema = exports.sampleColorsResultSchema = exports.measureLayoutSchema = exports.sampleColorsSchema = exports.matchTextStyleResultSchema = exports.matchTextStyleSchema = exports.prepareVideoComposerInput = exports.formatDateTag = exports.outputFieldsToZod = exports.outputFieldToZod = exports.LLMProviders = exports.applyLogicOperator = exports.IfLogicOperators = exports.resolvePath = exports.indexExpressionToString = exports.indexExpressionToIndexes = exports.resolveUnknownPath = exports.getOutputTypeFromCategory = exports.parseOpenAPIOutputPath = exports.parseOpenAPIInputs = void 0;
11
11
  // =============================================================================
12
12
  // Client Exports
13
13
  // =============================================================================
@@ -40,6 +40,8 @@ Object.defineProperty(exports, "submitDeduplicationJob", { enumerable: true, get
40
40
  Object.defineProperty(exports, "submitSceneSplitJob", { enumerable: true, get: function () { return render_1.submitSceneSplitJob; } });
41
41
  Object.defineProperty(exports, "submitScreenshotAnimationRenderJob", { enumerable: true, get: function () { return render_1.submitScreenshotAnimationRenderJob; } });
42
42
  Object.defineProperty(exports, "submitRemotionCompositionRenderJob", { enumerable: true, get: function () { return render_1.submitRemotionCompositionRenderJob; } });
43
+ Object.defineProperty(exports, "submitNativeTextStickerRenderJob", { enumerable: true, get: function () { return render_1.submitNativeTextStickerRenderJob; } });
44
+ Object.defineProperty(exports, "nativeTextStickerSchema", { enumerable: true, get: function () { return render_1.nativeTextStickerSchema; } });
43
45
  Object.defineProperty(exports, "submitAutoCaptionRenderJob", { enumerable: true, get: function () { return render_1.submitAutoCaptionRenderJob; } });
44
46
  Object.defineProperty(exports, "submitInstagramDmRenderJob", { enumerable: true, get: function () { return render_1.submitInstagramDmRenderJob; } });
45
47
  Object.defineProperty(exports, "submitIMessageDmRenderJob", { enumerable: true, get: function () { return render_1.submitIMessageDmRenderJob; } });
@@ -0,0 +1,36 @@
1
+ import { z } from 'zod';
2
+ /** Native points, rendered at 3 pixels per point. No coordinate rounding. */
3
+ export declare const nativeTextStickerSchema: z.ZodObject<{
4
+ profile: z.ZodDefault<z.ZodLiteral<"tiktok-classic-ios-47">>;
5
+ text: z.ZodEffects<z.ZodString, string, string>;
6
+ fontSizePt: z.ZodDefault<z.ZodNumber>;
7
+ canvasWidthPt: z.ZodDefault<z.ZodNumber>;
8
+ canvasHeightPt: z.ZodDefault<z.ZodNumber>;
9
+ containerWidthPt: z.ZodDefault<z.ZodNumber>;
10
+ /** Omit for unlimited lines clipped by the canvas; zero has the same meaning. */
11
+ containerHeightPt: z.ZodOptional<z.ZodNumber>;
12
+ originPt: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
13
+ /** Normally omitted. Reproduces an editor whose view font differs from the text run font. */
14
+ viewFontSizePt: z.ZodOptional<z.ZodNumber>;
15
+ }, "strict", z.ZodTypeAny, {
16
+ text: string;
17
+ profile: "tiktok-classic-ios-47";
18
+ fontSizePt: number;
19
+ canvasWidthPt: number;
20
+ canvasHeightPt: number;
21
+ containerWidthPt: number;
22
+ originPt: [number, number];
23
+ containerHeightPt?: number | undefined;
24
+ viewFontSizePt?: number | undefined;
25
+ }, {
26
+ text: string;
27
+ profile?: "tiktok-classic-ios-47" | undefined;
28
+ fontSizePt?: number | undefined;
29
+ canvasWidthPt?: number | undefined;
30
+ canvasHeightPt?: number | undefined;
31
+ containerWidthPt?: number | undefined;
32
+ containerHeightPt?: number | undefined;
33
+ originPt?: [number, number] | undefined;
34
+ viewFontSizePt?: number | undefined;
35
+ }>;
36
+ export type NativeTextStickerInput = z.input<typeof nativeTextStickerSchema>;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.nativeTextStickerSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ const dimension = zod_1.z.number().finite().min(1).max(1365);
6
+ const fontSize = zod_1.z.number().finite().min(11).max(72);
7
+ /** Native points, rendered at 3 pixels per point. No coordinate rounding. */
8
+ exports.nativeTextStickerSchema = zod_1.z.object({
9
+ profile: zod_1.z.literal('tiktok-classic-ios-47').default('tiktok-classic-ios-47'),
10
+ text: zod_1.z.string().min(1).max(2048).refine(text => text.split('\n').every(line => line.replace(/ /g, '').length > 0)
11
+ && !/[\u0000-\u0009\u000b-\u001f\u007f-\u009f\u2028\u2029\u202a-\u202e\u2066-\u2069]/u.test(text)
12
+ && !/[\uD800-\uDFFF]/u.test(text), 'Empty paragraphs, control characters and unpaired surrogates are not supported'),
13
+ fontSizePt: fontSize.default(28),
14
+ canvasWidthPt: dimension.default(600),
15
+ canvasHeightPt: dimension.default(180),
16
+ containerWidthPt: dimension.default(560),
17
+ /** Omit for unlimited lines clipped by the canvas; zero has the same meaning. */
18
+ containerHeightPt: zod_1.z.number().finite().min(0).max(1365).optional(),
19
+ originPt: zod_1.z.tuple([zod_1.z.number().finite().min(0).max(1365), zod_1.z.number().finite().min(0).max(1365)]).default([20, 18]),
20
+ /** Normally omitted. Reproduces an editor whose view font differs from the text run font. */
21
+ viewFontSizePt: fontSize.optional(),
22
+ }).strict();
package/dist/render.d.ts CHANGED
@@ -13,6 +13,25 @@ import type { CaptionWord, CaptionStyle } from './render/types/caption';
13
13
  import type { ImageEditorNodeConfig } from './automations/nodes/image-composer';
14
14
  import type { CreateDmMessage } from './automations/nodes/create-dm';
15
15
  import { type RemotionCompositionInput } from './render/types/remotion-composition';
16
+ import { type NativeTextStickerInput } from './render/types/native-text';
17
+ export { nativeTextStickerSchema, type NativeTextStickerInput } from './render/types/native-text';
18
+ /** Render a native text/emoji sticker directly to a transparent PNG. */
19
+ export declare function submitNativeTextStickerRenderJob(params: NativeTextStickerInput): Promise<{
20
+ ok: boolean;
21
+ code: number;
22
+ message: string;
23
+ data?: undefined;
24
+ } | {
25
+ ok: boolean;
26
+ code: number;
27
+ message: string;
28
+ data: {
29
+ message: string;
30
+ status: string;
31
+ job_id: string;
32
+ output_type: string;
33
+ };
34
+ }>;
16
35
  /** Submit TSX source and props; UGC owns registration and bundling. */
17
36
  export declare function submitRemotionCompositionRenderJob(params: RemotionCompositionInput & {
18
37
  outputType?: 'video' | 'image';
@@ -208,4 +227,3 @@ export declare function submitInstagramDmRenderJob(params: SubmitInstagramDmRend
208
227
  * Renders a fake iMessage conversation image.
209
228
  */
210
229
  export declare function submitIMessageDmRenderJob(params: SubmitIMessageDmRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
211
- export {};
package/dist/render.js CHANGED
@@ -9,6 +9,8 @@
9
9
  * variable set fails fast.
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.nativeTextStickerSchema = void 0;
13
+ exports.submitNativeTextStickerRenderJob = submitNativeTextStickerRenderJob;
12
14
  exports.submitRemotionCompositionRenderJob = submitRemotionCompositionRenderJob;
13
15
  exports.submitImageRenderJob = submitImageRenderJob;
14
16
  exports.submitVideoRenderJob = submitVideoRenderJob;
@@ -21,6 +23,9 @@ exports.submitInstagramDmRenderJob = submitInstagramDmRenderJob;
21
23
  exports.submitIMessageDmRenderJob = submitIMessageDmRenderJob;
22
24
  const zod_1 = require("zod");
23
25
  const remotion_composition_1 = require("./render/types/remotion-composition");
26
+ const native_text_1 = require("./render/types/native-text");
27
+ var native_text_2 = require("./render/types/native-text");
28
+ Object.defineProperty(exports, "nativeTextStickerSchema", { enumerable: true, get: function () { return native_text_2.nativeTextStickerSchema; } });
24
29
  function renderBaseUrl() {
25
30
  const url = process.env.UGC_RENDER_URL?.trim();
26
31
  if (!url) {
@@ -29,6 +34,25 @@ function renderBaseUrl() {
29
34
  return url.replace(/\/+$/, '');
30
35
  }
31
36
  const MAX_RETRIES = 3;
37
+ /** Render a native text/emoji sticker directly to a transparent PNG. */
38
+ async function submitNativeTextStickerRenderJob(params) {
39
+ const parsed = native_text_1.nativeTextStickerSchema.safeParse(params);
40
+ if (!parsed.success)
41
+ return { ok: false, code: 400, message: parsed.error.message };
42
+ const response = await renderFetch('/submit-job', {
43
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
44
+ body: JSON.stringify({ config: { _compositionType: 'native-text', ...parsed.data },
45
+ output_type: 'image', image_format: 'png' }),
46
+ });
47
+ const data = await response.json();
48
+ const result = zod_1.z.object({ job_id: zod_1.z.string(), status: zod_1.z.string(), output_type: zod_1.z.string(), message: zod_1.z.string() }).safeParse(data);
49
+ if (!response.ok || !result.success || result.data.status === 'error') {
50
+ const error = zod_1.z.object({ error: zod_1.z.string() }).safeParse(data);
51
+ return { ok: false, code: response.ok ? 400 : response.status,
52
+ message: error.success ? error.data.error : 'Failed to submit native text sticker' };
53
+ }
54
+ return { ok: true, code: 200, message: 'Native text sticker submitted', data: result.data };
55
+ }
32
56
  /** Submit TSX source and props; UGC owns registration and bundling. */
33
57
  async function submitRemotionCompositionRenderJob(params) {
34
58
  const { outputType = 'video', ...composition } = params;
package/dist/skills.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.skillFiles = void 0;
4
+ const content_skills_1 = require("./content-skills");
4
5
  const run = 'npx ugcinc';
5
6
  exports.skillFiles = {
6
7
  'accounts/SKILL.md': `---
@@ -152,67 +153,6 @@ ${run} retry_posts '{"postIds":["id1"]}'
152
153
  - After creating a post, tell the user the post ID and scheduled time
153
154
  - For failed posts, check status first to understand the error before retrying
154
155
  - If $ARGUMENTS is provided, interpret it as a natural language request
155
- `,
156
- 'media/SKILL.md': `---
157
- name: media
158
- description: Manage UGC Inc media library - list, import, and organize videos, images, audio, and text. Use when the user wants to upload media, import audio, or manage their library.
159
- allowed-tools: Bash, Read
160
- ---
161
-
162
- You manage UGC Inc media through the CLI. Run tools with:
163
- \`\`\`bash
164
- ${run} <tool_name> '<json_params>'
165
- \`\`\`
166
-
167
- Auth: \`npx ugcinc auth <api_key> [--admin]\`. Only needed once. Use \`--admin\` for admin keys.
168
-
169
- ## Tools
170
-
171
- ### list_media
172
- List all media (user media + social audio).
173
- \`\`\`bash
174
- ${run} list_media
175
- ${run} list_media '{"tag":"campaign-1"}'
176
- ${run} list_media '{"ids":["id1","id2"]}'
177
- \`\`\`
178
-
179
- ### list_social_audio
180
- List social audio only.
181
- \`\`\`bash
182
- ${run} list_social_audio
183
- ${run} list_social_audio '{"tag":"trending"}'
184
- \`\`\`
185
-
186
- ### create_media_from_urls
187
- Import media from URLs (videos, images already hosted somewhere).
188
- \`\`\`bash
189
- ${run} create_media_from_urls '{"urls":["https://example.com/video.mp4"],"tag":"imported"}'
190
- ${run} create_media_from_urls '{"urls":["url1","url2"],"names":["Video 1","Video 2"]}'
191
- \`\`\`
192
-
193
- ### import_text
194
- Import text content as media.
195
- \`\`\`bash
196
- ${run} import_text '{"texts":[{"content":"Hello world","name":"greeting","tag":"captions"}]}'
197
- \`\`\`
198
-
199
- ### create_social_audio
200
- Extract audio from a TikTok or Instagram URL.
201
- \`\`\`bash
202
- ${run} create_social_audio '{"url":"https://www.tiktok.com/@user/video/123","tag":"trending"}'
203
- \`\`\`
204
-
205
- ### delete_media
206
- Delete media items (also removes from storage).
207
- \`\`\`bash
208
- ${run} delete_media '{"ids":["id1","id2"]}'
209
- \`\`\`
210
-
211
- ## Workflow tips
212
- - Media types: video, image, audio, text, social_audio
213
- - When importing audio from TikTok/Instagram, use \`create_social_audio\` not \`create_media_from_urls\`
214
- - Tags help organize media - suggest tagging when importing
215
- - If $ARGUMENTS is provided, interpret it as a natural language request
216
156
  `,
217
157
  'automations/SKILL.md': `---
218
158
  name: automations
@@ -527,4 +467,5 @@ ${run} upsert_integration_key '{"provider":"openai","key":"sk-..."}'
527
467
  - Mention when stats were last refreshed and offer to refresh if stale
528
468
  - If $ARGUMENTS is provided, interpret it as a natural language request
529
469
  `,
470
+ ...content_skills_1.contentSkillFiles,
530
471
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc",
3
- "version": "4.25.0",
3
+ "version": "4.26.1",
4
4
  "description": "TypeScript/JavaScript client for the UGC Inc API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -23,7 +23,7 @@
23
23
  }
24
24
  },
25
25
  "scripts": {
26
- "build": "tsc",
26
+ "build": "node scripts/build-skills.cjs && tsc",
27
27
  "prepublishOnly": "npm run build",
28
28
  "test": "echo \"No tests specified\" && exit 0"
29
29
  },
@@ -65,6 +65,7 @@
65
65
  },
66
66
  "files": [
67
67
  "dist",
68
- "README.md"
68
+ "README.md",
69
+ "skills"
69
70
  ]
70
71
  }
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: content-automation
3
+ description: Turn reference social posts or a rough content idea into a reusable UGC Inc workflow, with editable inputs and reviewed visual/audio output. Use for developing and validating content formats rather than scheduling existing finished posts.
4
+ allowed-tools: Bash, Read
5
+ ---
6
+
7
+ Build the format the user wants and collaborate at the level they requested.
8
+ When they want to discuss, compare or review a specific artifact, do that before
9
+ making additional format decisions. Keep questions concise and ask only when a
10
+ missing answer prevents useful work. Do not impose a new per-video reference input
11
+ when the user wants a fixed reusable visual format.
12
+
13
+ Use the `media` skill for acquisition/inspection and the `automations` skill for
14
+ current workflow tool calls. Read [references/build-review.md](references/build-review.md)
15
+ when constructing or reviewing a workflow. Use only shipped tools/nodes discovered
16
+ through `npx ugcinc --list`, `list_node_types` and `get_node_type`.
17
+
18
+ ## Understand and specify the format
19
+
20
+ Acquire a bounded set of real references and inspect full frames and changes over
21
+ time, including audio using available modalities. A contact sheet or transcript
22
+ alone cannot establish all animation/pacing/music details. State uninspected
23
+ parts instead of claiming playback. Distinguish reusable formats from variants;
24
+ several posts may use the same structure with different assets, copy or timing.
25
+
26
+ Write a compact spec: what the viewer sees first, the sequence/reveal, required
27
+ inputs, reusable assets/components, changing copy, audio source/timing and final
28
+ action/CTA if required by this format. Separate measured facts from inference
29
+ and user choices. Share a verified HTTPS reference/preview when the user needs
30
+ to identify the parts they want changed.
31
+
32
+ Classify assets by replacement behavior. A recognizable Mii, Wojak or other exact
33
+ meme image may need a static asset; an ordinary or newly generated face need not
34
+ be static just because it looks meme-like. Preserve a headshot's identity across
35
+ variants when requested, while making intended differences visibly meaningful.
36
+
37
+ ## Build reusable production behavior
38
+
39
+ Separate layout, data/assets and motion. Expose the inputs a normal run needs:
40
+ asset pools/overrides, text pools, contact/destination, duration/timing and seed
41
+ where appropriate. Keep canonical outputs independent of an operator's laptop.
42
+ Implement procedural animations as reusable components rendered on demand;
43
+ do not substitute one pre-rendered clip when each run should vary. Randomness
44
+ must respect the intended visual roles and remain replayable from saved inputs.
45
+
46
+ Reuse existing nodes/compositions and actual public schemas. Analysis tools are
47
+ available as CLI/SDK operations; do not assume every one is a workflow node.
48
+ Validate the graph before executing. Prefer a reviewable output over publishing
49
+ or enabling recurring runs unless the user authorized those actions.
50
+
51
+ ## Make the content work
52
+
53
+ - Keep the hierarchy clear: readable hook, focal content, then the intended CTA.
54
+ Give characters/subjects enough visual weight; align visible content within
55
+ its section instead of trusting transparent image bounds alone.
56
+ - Measure margins, spacing, text wrapping and overlap. Check at phone viewing
57
+ size, not only on a large canvas. Keep important text away from platform UI;
58
+ inspect the relevant target presentation rather than assuming universal margins.
59
+ - Use sampled reference colors and real render previews. Judge font, stroke,
60
+ kerning, line height and emoji spacing together. Treat text-style fitting as a
61
+ measured candidate, not proof of an exact font or pixel-perfect output.
62
+ - Match the reference post's actual audio excerpt, mix, start and duration when
63
+ requested. Preserve attribution separately. Empty transcription can mean music
64
+ or unrecognized speech, not silence. Keep visual reveals and audio timing coherent.
65
+ - Keep required CTA/contact copy present and legible across every requested
66
+ variant. Use the user's actual destination/formatting, not a number copied from
67
+ an old example. Vary copy intentionally without altering the required action.
68
+ - Test with real content and multiple asset/copy lengths. A composition that
69
+ works with one face or one short headline is not a validated reusable format.
70
+ Inspect early, changing and final states; a still cannot certify a full video.
71
+
72
+ ## Deliver and describe the boundary
73
+
74
+ Provide the workflow, its normal inputs, replayable example parameters, verified
75
+ HTTPS previews, and any observed visual/audio limits. For videos verify decoding,
76
+ duration/dimensions/audio and byte-range playback; images need correct content
77
+ type and decoding. Do not use local/localhost/private URLs as deliverables.
78
+
79
+ SAM and automatic segmentation/overlay tracking are deferred. Use supplied
80
+ assets, user-supplied boxes/masks or honest visual analysis; do not claim recovered
81
+ source layers or automatic clean cutouts. If the runtime cannot inspect a media
82
+ modality, record that limit and do not certify the uninspected property.
@@ -0,0 +1,66 @@
1
+ # Construct and review a reusable workflow
2
+
3
+ ## Discover the real interface
4
+
5
+ `npx ugcinc list_node_types '{}'` lists current node metadata.
6
+ Use `get_node_type '{"nodeType":"..."}'` for the nodes you choose and read the
7
+ bundled `automations` skill for current tool parameters. These tools still need
8
+ a configured CLI key even when their own operation is local.
9
+
10
+ Connections live on the target node, not a separate edges array:
11
+
12
+ ```json
13
+ {
14
+ "nodes": [
15
+ {
16
+ "id": "target",
17
+ "type": "node-type-from-discovery",
18
+ "config": {},
19
+ "inputs": {
20
+ "actual-input-port": {
21
+ "sourceNodeId": "source",
22
+ "sourceOutputId": "actual-output-port"
23
+ }
24
+ }
25
+ }
26
+ ]
27
+ }
28
+ ```
29
+
30
+ Use actual IDs/configuration returned by discovery, not these placeholders.
31
+ Configure dynamic interfaces before wiring them. Re-check ports after config
32
+ changes. Current validation requires a terminal outcome; inspect pass-through,
33
+ save-to-media and posting nodes to choose the intended behavior. Publishing is
34
+ not required just to make a reviewable workflow output.
35
+
36
+ For reusable sub-workflows, configure their input/output contracts explicitly
37
+ and inspect the Compose Workflow node's actual schema. For custom Remotion,
38
+ inspect `remotion-composition`; use its supported composition/props/import
39
+ contract. Do not invent an upload/register API or rely on arbitrary packages.
40
+
41
+ Validate using `validate_workflow` with the shape described by the installed
42
+ automation skill/tool. Execute only the intended single test scope, inspect its
43
+ status/output, and retain resolved inputs and seed. Saving a template or receiving
44
+ a run ID alone does not prove the output renders correctly.
45
+
46
+ ## Review a representative run
47
+
48
+ Check the viewer's actual sequence: hook visibility, subject size, changing state,
49
+ text wrapping/CTA, transitions and finish. Compare the full frame before zooming
50
+ into details. Inspect multiple points around animation/text changes. Re-test a
51
+ longer caption and a different asset where those vary in normal runs.
52
+
53
+ Distinguish observed pixels/timestamps from estimates. Examples: sRGB sampling
54
+ does not recover design colors from compression, transcript word timestamps are
55
+ estimated, sampled OCR leaves gaps, and bounding boxes are not exact ink bounds.
56
+ Manually supplied boxes are allowed; describe how they were determined.
57
+
58
+ Preserve both soundtrack bytes and source attribution. Check whether music is
59
+ embedded or only attributed, whether the output repeats/truncates it, and whether
60
+ visual changes coincide with the intended moments. Do not invent beat detection,
61
+ audio separation, universal media comparison or other unavailable tools.
62
+
63
+ When revising content, name the concrete issue and inspect the changed output.
64
+ Keep reusable parameters instead of baking a correction into one reference
65
+ image. Report remaining defects plainly; avoid calling a draft production-ready
66
+ based only on successful API execution.
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: media
3
+ description: Acquire TikTok and Instagram posts and inspect images, video and audio with UGC Inc tools. Use for reference analysis, OCR, frames, scenes, transcription, colors, layout, text-style fitting, or media-library management.
4
+ allowed-tools: Bash, Read
5
+ ---
6
+
7
+ Use the installed UGC Inc CLI:
8
+
9
+ ```bash
10
+ npx ugcinc --list
11
+ npx ugcinc <tool_name> '<json_params>'
12
+ ```
13
+
14
+ Credentials come from `UGC_API_KEY` or existing UGC configuration. Use the user's
15
+ organization and a key with `content:analyze` for acquisition/analysis. If using
16
+ an admin key, pass the authorized `orgId` in the JSON parameters. Do not print
17
+ credentials, copy an administrator key to another user's machine, or change
18
+ organizations to bypass a denial. Confirm access with one small tool call.
19
+ If credentials already live in an app env file, set `UGC_ENV_FILE` to that file's
20
+ path so the CLI reads the existing key without duplicating its contents. Set
21
+ `UGC_ORG_ID` in that file or the process environment to scope calls to the user's
22
+ organization; an explicit JSON `orgId` takes priority.
23
+
24
+ Read [references/tools.md](references/tools.md) for request shapes, result fields
25
+ and limits. The CLI lists names/descriptions; the reference explains parameters.
26
+
27
+ ## Choose the input and tools
28
+
29
+ - A profile URL: `list_profile_posts` discovers a bounded page; follow its cursor
30
+ only as needed. Listing does not download media or guarantee complete history.
31
+ - A post URL: `download_social_post` ingests its ordered assets into owned media.
32
+ Use returned asset URLs for analysis; the TikTok/Instagram HTML URL is not a
33
+ video file. Check complete/partial/failed status and failed assets.
34
+ - A direct media URL: use the corresponding inspection tool directly.
35
+ - Video: `detect_scenes`, `extract_frames`, `read_video_text`; audio tools accept
36
+ video too. Inspect multiple moments, including changes near the beginning/end.
37
+ - Image: `read_image_text`, `sample_colors`, `measure_layout` using supplied
38
+ boxes/masks, and `match_text_style` for a specified text crop.
39
+ - Audio: `transcribe_audio` and `measure_audio_levels`.
40
+
41
+ The SDK/CLI waits for processing automatically. Check both the outer response's
42
+ `ok` and the returned job's `status`. Analysis is usable when
43
+ `data.status === "completed"`; its content is `data.result`. If still pending or
44
+ processing, keep its job ID and use `get_media_analysis` rather than resubmitting.
45
+ Use bounded retries for retryable failures; report persistent errors/partial
46
+ coverage. A zero process exit code alone does not establish API success.
47
+
48
+ ## Interpret evidence accurately
49
+
50
+ - Coordinates are display-oriented source pixels; times are source-relative
51
+ milliseconds. Use returned decoded timestamps, dimensions and coverage.
52
+ - OCR is English, may miss stylized text, and can be empty on unreadable input.
53
+ `fast` video OCR leaves uninspected intervals. `frame_scan` is bounded and
54
+ cannot inspect an arbitrarily long clip in one request.
55
+ - Speech timestamps are estimates; speaker labels/forced alignment are absent.
56
+ A transcript does not establish music, emotion, delivery or sound effects.
57
+ - Color sampling measures decoded sRGB pixels, not guaranteed design hex values.
58
+ Layout measurement does not detect objects or recover original layer files.
59
+ - Text-style matching returns supported UGC settings and visual residuals. Font
60
+ identity is unverified; inspect the preview before calling a fit accurate.
61
+ It has a restricted ASCII/font/style surface; the native TikTok renderer is a
62
+ separate bounded rendering capability, not proof that matching is exact.
63
+ - SAM/object segmentation, automatic overlay detection, element/mask tracking,
64
+ motion measurement, extraction, media comparison/alignment and semantic search
65
+ are not available through this tool set. Do not invent those calls or install
66
+ SAM. Existing OCR text tracks remain available.
67
+
68
+ ## Review and share media
69
+
70
+ Open returned image/contact-sheet previews and review the relevant sequence.
71
+ When the agent runtime can consume audio/video, use it. Otherwise use the
72
+ available transcript, audio preview and level measurements and explicitly state
73
+ what was not listened to or inspected. Do not claim to have watched/listened
74
+ because an analysis request succeeded.
75
+
76
+ Share only verified browser-accessible HTTPS artifacts. Fetch without auth and
77
+ check status, content type and decoding; for video also check byte-range support.
78
+ Use the returned owned-media URLs. Local paths, localhost/private addresses and
79
+ expiring upstream CDN links are not deliverable previews. If an artifact needs
80
+ uploading, use the supported owned storage path; do not claim a file is hosted
81
+ because a media record was created for an arbitrary URL.
82
+
83
+ For building a reusable content format, use the `content-automation` skill.
@@ -0,0 +1,64 @@
1
+ # UGC media tool requests and results
2
+
3
+ Run `npx ugcinc TOOL 'JSON'`. Shell quoting varies on Windows; if the shell
4
+ changes the JSON, use a small Node script with `UGCClient` and its `media`
5
+ methods instead of repeatedly sending malformed requests. For example:
6
+
7
+ ```js
8
+ const { UGCClient } = require('ugcinc');
9
+ const client = new UGCClient({
10
+ apiKey: process.env.UGC_API_KEY,
11
+ orgId: process.env.UGC_ORG_ID,
12
+ });
13
+ // Use admin:true only with an authorized administrator credential.
14
+ ```
15
+
16
+ Request URLs must be HTTPS. Analysis source URLs must not contain credentials
17
+ or fragments. Image operations support still PNG/JPEG/WebP, at most4096pixels
18
+ per side; the shared download limit is100MiB. Geometry must fit the actual
19
+ display-oriented source. Respect more restrictive operation limits below.
20
+
21
+ ## Acquisition
22
+
23
+ | CLI tool / SDK method | Parameters | Result |
24
+ | --- | --- | --- |
25
+ | `list_profile_posts` / `media.listProfilePosts` | `{url, limit?, cursor?}`; TikTok/Instagram profile; limit1–50 | `data.posts`, profile metadata, continuation cursor. Follow the actual returned cursor, not a fabricated page number. |
26
+ | `download_social_post` / `media.downloadSocialPost` | `{url}`; canonical TikTok/Instagram post URL | `data.status`, `assets[]` in order, `failedAssets[]`, `manifestUrl`, post/sound metadata. Each asset has `url`, `mediaId`, dimensions, type, duration, audio presence and hash. Repeat the same URL to resume retryable partial imports. |
27
+
28
+ Embedded video audio remains attached. Photo assets have no embedded soundtrack;
29
+ sound attribution does not mean audio bytes were downloaded. Preserve sound
30
+ ID/link separately; never replace a post excerpt with the full sound-page track
31
+ and call them identical. Partial imports are not a complete carousel.
32
+
33
+ ## Analysis
34
+
35
+ These return `{ok, data:{jobId,status,operation,result?,error?}}`. Use the completed
36
+ `data.result` fields listed below. Failure can occur after successful submission.
37
+
38
+ | CLI tool / SDK method | Parameters | Result content |
39
+ | --- | --- | --- |
40
+ | `extract_frames` / `media.extractFrames` | `{url,timestampsMs:[0,1500,3000],selection:"nearest",contactSheet:true}` OR `{url,range:{startMs,endMs,intervalMs}}`; choose exactly one selector, at most48samples | `frames[]` with requested/decoded time, source PTS, dimensions and URL; optional `contactSheet` with individual frame links; source metadata. |
41
+ | `detect_scenes` / `media.detectScenes` | `{url}` | `scenes[]` with start/end milliseconds, frame indexes, source PTS; no split MP4 exports. |
42
+ | `read_image_text` / `media.readImageText` | `{url}` | `regions[]` with text/rawText, polygons/boxes/confidence; inferred `lines[]`/reading order; `previewUrl`. |
43
+ | `read_video_text` / `media.readVideoText` | `{url,mode:"fast",intervalMs:1000,range?:{startMs,endMs}}` OR `{url,mode:"frame_scan",range?:{startMs,endMs}}` | `frames[]`, text `tracks[]` with observations/positions; explicit `coverage.inspected` and `uninspected`. Maximum240inspected frames and120seconds per request. `intervalMs` only applies to fast mode. |
44
+ | `transcribe_audio` / `media.transcribeAudio` | `{url,range?:{startMs,endMs},language?:"auto",timing?:"word",channel?:0}` | `status`, `text`, timed `segments[]` and estimated words, warnings, `audioPreviewUrl`. Range at most120seconds; timing `word` or `segment`; optional channel0/1. Empty/no-audio/no-speech states are distinct. |
45
+ | `measure_audio_levels` / `media.measureAudioLevels` | `{url,range?:{startMs,endMs},windowMs?:100,silenceThresholdDbfs?:-50}` | Native-channel RMS/sample peaks, loudness timeline/LUFS, silence, possible clipping, `previewUrl`. Windows20–5000ms, threshold−100to−10dBFS, range at most120seconds. |
46
+ | `sample_colors` / `media.sampleColors` | `{url,maxColors?:8,point?:{x,y},box?:{x,y,width,height},mask?}`; choose at most one selection | Observed RGBA point or palette/proportions, omitted mass, declared color policy, `previewUrl`. Palette1–32colors. |
47
+ | `measure_layout` / `media.measureLayout` | `{url,elements:[{id,box:{x,y,width,height}}]}`; each element may instead have `mask` | Margins, centers, spacing/alignment deltas, intersections/IoU and `previewUrl`.1–32unique IDs. Boxes are supplied, not automatically detected. |
48
+ | `match_text_style` / `media.matchTextStyle` | `{url,box:{x,y,width,height},text?}`; integer crop16–1536wide,16–512high; optional supplied text | `status`, candidates with real image-editor `element`/`previewConfig`, `maskIoU`, `foregroundMae`, `quality`, previews/comparison and warnings. Up to160printable ASCII characters/four lines; supported fonts Arial/TikTok/Apple. Missing/unsupported styles may be unavailable or approximations. |
49
+ | `get_media_analysis` / `media.getMediaAnalysis` | `{jobId}` | Current status/result in the current organization; use when automatic waiting returned pending/processing. |
50
+
51
+ Boxes are half-open source-pixel rectangles. For masks used by colors/layout:
52
+ `{width,height,counts}` uses row-major binary RLE, alternating background then
53
+ foreground. Counts must sum to width×height. This is not COCO's column-major
54
+ convention. Supply only masks you actually have; automatic segmentation is off.
55
+
56
+ ## Existing media library
57
+
58
+ - `list_media({ids?,tag?})`, `list_social_audio({ids?,tag?})`.
59
+ - `create_media_from_urls({urls,names?,tag?,sourceIds?,useName?})` registers URLs;
60
+ it does not turn an inaccessible local file into a public upload.
61
+ - `import_text({texts:[{content,name?,tag?}]})`.
62
+ - `create_social_audio({url,tag?})` imports social audio separately; do not use it
63
+ as proof of the exact soundtrack/mix/offset in a downloaded reference video.
64
+ - `delete_media({ids})` removes media/storage; only use when deletion is requested.