ugcinc 4.27.1 → 5.0.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 +42 -0
- package/dist/automations/nodes/create-dm.d.ts +3 -0
- package/dist/automations/nodes/create-dm.js +31 -18
- package/dist/cli.js +1 -1
- package/dist/content-skills.js +4 -3
- package/dist/imessage-conversation.d.ts +33 -0
- package/dist/imessage-conversation.js +16 -4
- package/dist/imessage-workflow.d.ts +35 -0
- package/dist/imessage-workflow.js +80 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +7 -4
- package/dist/render/Root.js +1 -38
- package/dist/render/compositions/index.d.ts +0 -1
- package/dist/render/compositions/index.js +1 -5
- package/dist/render.d.ts +0 -39
- package/dist/render.js +0 -63
- package/dist/skills.js +1 -1
- package/dist/tools/imessage.d.ts +2 -0
- package/dist/tools/imessage.js +12 -0
- package/dist/tools/index.js +2 -0
- package/package.json +1 -1
- package/skills/content-automation/SKILL.md +4 -0
- package/skills/imessage/SKILL.md +101 -0
- package/skills/media/SKILL.md +4 -0
- package/dist/render/compositions/IMessageDmComposition/convertPropsToElements.d.ts +0 -27
- package/dist/render/compositions/IMessageDmComposition/convertPropsToElements.js +0 -629
- package/dist/render/compositions/IMessageDmComposition/index.d.ts +0 -21
- package/dist/render/compositions/IMessageDmComposition/index.js +0 -504
- package/dist/render/compositions/IMessageDmComposition/types.d.ts +0 -767
- package/dist/render/compositions/IMessageDmComposition/types.js +0 -228
package/README.md
CHANGED
|
@@ -706,3 +706,45 @@ layout/readability, asset identity, procedural animation, CTA and audio handling
|
|
|
706
706
|
It distinguishes measured facts from inference and requires verified public
|
|
707
707
|
HTTPS previews. SAM-dependent capabilities remain deferred and are not advertised
|
|
708
708
|
as available tools. A successful API call alone does not certify content quality.
|
|
709
|
+
|
|
710
|
+
## Native iMessage workflows (5.0)
|
|
711
|
+
|
|
712
|
+
All iMessage images and videos use the native service. The Remotion iMessage
|
|
713
|
+
composition/player exports, `submitIMessageDmRenderJob` and its old payload types
|
|
714
|
+
were removed; use authenticated `client.media.renderIMessageConversation`,
|
|
715
|
+
`getIMessageConversation` and `cancelIMessageConversation`. Instagram remains
|
|
716
|
+
unchanged. This is a major version because the old public exports were removed.
|
|
717
|
+
|
|
718
|
+
Create DM uses explicit `imessageOutputType: "image" | "video"` independently of
|
|
719
|
+
variable authoring data. Select `platform: "imessage"`, `platformInputType:
|
|
720
|
+
"static"`, `imessageRenderMode: "conversation"`, and either set
|
|
721
|
+
`imessageConversation` or connect `conversation` with
|
|
722
|
+
`imessageConversationInputType: "variable"`. Static PNG timing still uses the
|
|
723
|
+
conversation's `output: {kind:"png",atMs:...}`. Variable inputs declaring the
|
|
724
|
+
opposite output kind reject instead of changing the connected output type.
|
|
725
|
+
|
|
726
|
+
Saved screenshot inputs are translated at execution without database rewrites;
|
|
727
|
+
existing message/avatar/attachment connections remain usable. Custom bubble
|
|
728
|
+
colors, message-based cropping and free-text legacy receipts require explicit
|
|
729
|
+
correction. Use the native editor for receipts and downstream composition for
|
|
730
|
+
cropping. There is no fallback to the old renderer.
|
|
731
|
+
|
|
732
|
+
Contact/group/participant `avatarUrl` and message `imageUrl` accept public HTTPS
|
|
733
|
+
PNG/JPEG/WebP images. The API resolves them into bounded PNGs (avatar fits96px,
|
|
734
|
+
message image fits2048px; at most12MiB source bytes per URL). Supply exactly one
|
|
735
|
+
image source per avatar and one body per message. `imagePngBase64` and
|
|
736
|
+
`avatarPngBase64` remain supported. URL resolution rejects private destinations,
|
|
737
|
+
unsafe redirects, oversized/invalid media and requests exceeding image budgets.
|
|
738
|
+
|
|
739
|
+
Agents can use `compile_imessage_conversation` locally, then
|
|
740
|
+
`render_imessage_conversation`, `get_imessage_conversation` and
|
|
741
|
+
`cancel_imessage_conversation`. Compile does not require an API key. Rendering
|
|
742
|
+
needs an existing key authorized for `content:render` and the correct org.
|
|
743
|
+
Submission returns immediately; poll to a terminal job state. See
|
|
744
|
+
https://docs.ugc.inc/guides/imessage-conversations for the full contract.
|
|
745
|
+
|
|
746
|
+
Install the bundled instructions with:
|
|
747
|
+
|
|
748
|
+
```bash
|
|
749
|
+
npx ugcinc@5.0.0 init --update imessage media content-automation automations
|
|
750
|
+
```
|
|
@@ -31,6 +31,9 @@ declare const definition: import("./types").NodeDefinition<"create-dm", "generat
|
|
|
31
31
|
platformInputType: InputType;
|
|
32
32
|
imessageRenderMode: "still" | "conversation";
|
|
33
33
|
imessageConversationInputType: InputType;
|
|
34
|
+
imessageOutputType: "video" | "image" | undefined;
|
|
35
|
+
imessageLegacyInputs: boolean;
|
|
36
|
+
imessageAttachmentMessageId: string;
|
|
34
37
|
imessageConversation: IMessageConversationInput | undefined;
|
|
35
38
|
lightMode: boolean;
|
|
36
39
|
lightModeInputType: InputType;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
3
|
+
const imessage_workflow_1 = require("../../imessage-workflow");
|
|
4
4
|
const types_1 = require("./types");
|
|
5
5
|
// =============================================================================
|
|
6
6
|
// Node Definition
|
|
7
7
|
// =============================================================================
|
|
8
|
+
function optionalOutput() { return undefined; }
|
|
8
9
|
const definition = (0, types_1.defineNode)({
|
|
9
10
|
nodeId: 'create-dm',
|
|
10
11
|
label: 'Create DM',
|
|
11
12
|
description: 'Create DM screenshots or native iMessage videos and snapshots',
|
|
12
|
-
guide: '
|
|
13
|
+
guide: 'iMessage uses the native service for both PNG images and 60fps videos. Set platform imessage, imessageRenderMode conversation, imessageOutputType image or video, and imessageConversation with public authoring JSON. Use imessageConversationInputType variable for the conversation object input. The output selector fixes the output port type. Connect profile-pic with profilePicInputType variable for a direct-chat avatar; connect image-attachment with imageAttachmentInputType variable and imessageAttachmentMessageId for a message image. Public HTTPS avatarUrl/imageUrl fields also resolve to bounded PNGs internally. Legacy screenshot nodes are adapted to native PNG with connections preserved; unsupported crop/bubble colors/read receipts require explicit correction. Instagram keeps its screenshot renderer.',
|
|
13
14
|
type: 'generator',
|
|
14
15
|
category: 'Generation',
|
|
15
16
|
outputModes: ['per-input', 'single'],
|
|
@@ -19,6 +20,9 @@ const definition = (0, types_1.defineNode)({
|
|
|
19
20
|
platformInputType: 'static',
|
|
20
21
|
imessageRenderMode: 'still',
|
|
21
22
|
imessageConversationInputType: 'static',
|
|
23
|
+
imessageOutputType: optionalOutput(),
|
|
24
|
+
imessageLegacyInputs: false,
|
|
25
|
+
imessageAttachmentMessageId: '',
|
|
22
26
|
imessageConversation: undefined,
|
|
23
27
|
lightMode: true,
|
|
24
28
|
lightModeInputType: 'static',
|
|
@@ -53,25 +57,34 @@ const definition = (0, types_1.defineNode)({
|
|
|
53
57
|
selectionMode: null,
|
|
54
58
|
},
|
|
55
59
|
validate: config => {
|
|
56
|
-
if (config.
|
|
60
|
+
if (config.platform !== 'imessage')
|
|
57
61
|
return [];
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (config.
|
|
62
|
+
const issues = (0, imessage_workflow_1.legacyIMessageIssues)(config);
|
|
63
|
+
if (issues.length)
|
|
64
|
+
return issues;
|
|
65
|
+
if (config.platformInputType === 'variable' && (0, imessage_workflow_1.iMessageWorkflowOutput)(config) === 'video')
|
|
66
|
+
return ['Native iMessage video requires static platform selection.'];
|
|
67
|
+
if (config.imessageRenderMode === 'conversation' && config.imessageConversationInputType === 'variable')
|
|
68
|
+
return [];
|
|
69
|
+
if (config.imessageRenderMode !== 'conversation' && [config.messagesInputType, config.usernameInputType, config.timeInputType, config.imageAttachmentInputType].includes('variable'))
|
|
70
|
+
return [];
|
|
71
|
+
try {
|
|
72
|
+
(0, imessage_workflow_1.prepareIMessageWorkflow)(config);
|
|
62
73
|
return [];
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
return [error instanceof Error ? error.message : 'Invalid iMessage conversation.'];
|
|
77
|
+
}
|
|
65
78
|
},
|
|
66
79
|
computePorts: ({ config }) => {
|
|
67
|
-
if (config?.imessageRenderMode === 'conversation') {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
80
|
+
if (config?.imessageRenderMode === 'conversation' && !config.imessageLegacyInputs) {
|
|
81
|
+
const inputs = config.imessageConversationInputType === 'variable'
|
|
82
|
+
? [{ id: 'conversation', type: 'object', isArray: false, required: true }] : [];
|
|
83
|
+
if (config.profilePicInputType === 'variable')
|
|
84
|
+
inputs.push({ id: 'profile-pic', type: 'image', isArray: false, required: false });
|
|
85
|
+
if (config.imageAttachmentInputType === 'variable')
|
|
86
|
+
inputs.push({ id: 'image-attachment', type: 'image', isArray: false, required: true });
|
|
87
|
+
return { inputs, outputs: [{ id: 'output', type: (0, imessage_workflow_1.iMessageWorkflowOutput)(config), isArray: false, required: true }] };
|
|
75
88
|
}
|
|
76
89
|
const inputs = [];
|
|
77
90
|
if (config?.platformInputType === 'variable') {
|
|
@@ -128,7 +141,7 @@ const definition = (0, types_1.defineNode)({
|
|
|
128
141
|
});
|
|
129
142
|
return {
|
|
130
143
|
inputs,
|
|
131
|
-
outputs: [{ id: 'output', type: 'image', isArray: false, required: true }],
|
|
144
|
+
outputs: [{ id: 'output', type: config?.platform === 'imessage' ? (0, imessage_workflow_1.iMessageWorkflowOutput)(config) : 'image', isArray: false, required: true }],
|
|
132
145
|
};
|
|
133
146
|
},
|
|
134
147
|
generatePreview: (_config, _ctx, cachedOutputs) => {
|
package/dist/cli.js
CHANGED
|
@@ -212,7 +212,7 @@ async function runTool(toolName, rawParams) {
|
|
|
212
212
|
}));
|
|
213
213
|
process.exit(1);
|
|
214
214
|
}
|
|
215
|
-
const apiKey = loadEnvVar('UGC_API_KEY');
|
|
215
|
+
const apiKey = loadEnvVar('UGC_API_KEY') ?? (toolName === 'compile_imessage_conversation' ? 'local-compilation-only' : undefined);
|
|
216
216
|
if (!apiKey) {
|
|
217
217
|
console.error(JSON.stringify({
|
|
218
218
|
ok: false,
|
package/dist/content-skills.js
CHANGED
|
@@ -3,8 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.contentSkillFiles = void 0;
|
|
4
4
|
// Generated by scripts/build-skills.cjs; edit skills/** instead.
|
|
5
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",
|
|
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\nFor native iMessage render jobs use the `imessage` skill and its separate\nsubmit/status/cancel tools. They require `content:render`, not `content:analyze`,\nand are not media-analysis jobs.\n",
|
|
7
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"
|
|
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\nFor text-message formats, use the `imessage` skill and native Create DM for both\nPNG and video. Generate dialogue as structured authoring data, compile pacing,\nand review native output before combining with reaction footage, audio or CTA.\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
|
+
"imessage/SKILL.md": "---\nname: imessage\ndescription: Create native iMessage conversation PNGs and videos, or wire messaging scenes into UGC Inc workflows. Use for synthetic text-message content, typing, replies, reactions, receipts, avatars and image attachments.\nallowed-tools: Bash, Read\n---\n\nUse the native service for every iMessage image and video. The old Remotion\ncomposition and iMessage player are removed. Instagram DM remains a separate\nscreenshot implementation. These are synthetic renders, never real messages.\n\nRead the current [authoring guide](https://docs.ugc.inc/guides/imessage-conversations.md)\nand [field contract](https://docs.ugc.inc/data-structures/imessage-conversation.md)\nfor optional appearance, group, reply/reaction and timing fields.\n\n## Author and inspect pacing\n\nGive the conversation one concrete situation, a readable opening, useful replies\nand a clear payoff. Preserve the user's requested tone and product capabilities;\nrendering success does not establish that the dialogue is good. Use stable IDs,\n`sender: \"user\"` for the phone owner and `\"recipient\"` for the other person\n(or declared group participant IDs). Give each bubble one thought. Separate\ncontent generation from reusable appearance/pacing settings.\n\n`compile_imessage_conversation` accepts the authoring JSON and returns duration,\ntyping/arrival schedule and events without submitting a render. Inspect the total\nlength and important pauses; adjust authoring timing before rendering. A long\nconversation at default typing speed may exceed the 120-second limit. Do not\nsubmit the compiled plan to the render endpoint.\n\nFor a populated still, mark all messages `initial: true` and use\n`output: {\"kind\":\"png\"}`. For an animated scene, put only existing history first\nwith `initial:true`; later messages animate. `output:{\"kind\":\"png\",\"atMs\":500}`\nrenders that native moment losslessly. Video defaults to 60fps. Emoji are complete\nUnicode graphemes and local typing uses the native keyboard.\n\n## Render and handle jobs\n\nUse existing UGC credentials/org; restricted keys need `content:render`.\n`render_imessage_conversation` submits authoring JSON and immediately returns a\njob. Check outer `ok`, save `data.jobId`, and poll `get_imessage_conversation`\nwith `{jobId}` until completed/failed/cancelled. Only completed output has a URL.\nUse a bounded polling deadline; on timeout retain the job instead of duplicating\nit. `cancel_imessage_conversation` requests cancellation; keep polling to terminal.\nThese jobs do not use `get_media_analysis`.\n\nAn idempotency key reuses identical input within the org; changing input needs a\nnew key. Status records are retained seven days. Report safe returned errors and\nretryability. Use the SDK methods when shell JSON quoting is awkward.\n\nImage bodies accept exactly one `text`, `imagePngBase64`, or public HTTPS\n`imageUrl`. Contact/group/participant avatars accept a PNG payload or `avatarUrl`.\nThe service resolves URL images; do not make agents build base64 blobs. Avatar\nconversion fits96px, attachment conversion fits2048px; media/request budgets\nstill apply. A document-name string is text, not an actual file attachment;\nrender a deliberate PNG asset if the format calls for an image of a document.\n\n## Workflow integration\n\nDiscover `create-dm` with `get_node_type`. Configure:\n\n```json\n{\n \"platform\": \"imessage\",\n \"platformInputType\": \"static\",\n \"imessageRenderMode\": \"conversation\",\n \"imessageOutputType\": \"video\",\n \"imessageConversationInputType\": \"variable\"\n}\n```\n\nConnect the full authoring object to `conversation`. The output port is `output`;\n`imessageOutputType:\"image\"` fixes an image port, `\"video\"` fixes a video port.\nA variable input declaring the opposite output kind rejects. Static settings use\n`imessageConversation`. For a direct-chat avatar, set `profilePicInputType` to\n`variable` and connect `profile-pic`. To replace an image message, set\n`imageAttachmentInputType` to `variable`, set `imessageAttachmentMessageId` to the\ntarget ID and connect `image-attachment`. Input images retain normal `{url}` shape.\n\nSaved screenshot nodes retain their existing connections and resolve to native\npopulated PNGs. Custom bubble colors, message-based cropping and free-text legacy\nread receipts need explicit correction; do not silently discard them. Use native\nmessage receipt/readAt fields or downstream composition as appropriate.\n\nValidate the graph before running. Feed video into Video Composer alongside the\nreaction clip, soundtrack and required CTA; feed PNG into an image workflow.\nEnd with Save to Media or a Pass-Through output for review. Do not add posting\nor recurrence without authorization. Use the automations skill for graph details.\n\n## Review boundaries\n\nInspect PNGs and opening/changing/final video states at phone viewing size.\nUse the compiled schedule to choose moments around typing, arrival and payoff;\ncheck scrolling, emoji, attachments and end hold. Native video is1126×2436 with\none codec-padding column; PNG is1125×2436. Composite deliberately into the target\ncanvas. Audio is added separately; inspect the final mixed video's actual audio.\n\nThe profile is iPhone XS/iOS18.6.2, not the newest iOS UI. Full-screen/bubble-send\neffects, arbitrary fonts/coordinates and custom native dimensions/fps are absent.\nSeeded timing is repeatable, but independent reaction pixels can differ. Do not\npromise universal pixel parity. Share only decoded, verified public HTTPS previews,\nincluding video Range support. Describe uninspected playback/audio honestly.\n"
|
|
10
11
|
};
|
|
@@ -35,29 +35,36 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
35
35
|
contact: z.ZodOptional<z.ZodObject<{
|
|
36
36
|
name: z.ZodPipeline<z.ZodEffects<z.ZodString, string, string>, z.ZodString>;
|
|
37
37
|
avatarPngBase64: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
38
|
+
avatarUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
38
39
|
}, "strict", z.ZodTypeAny, {
|
|
39
40
|
name: string;
|
|
40
41
|
avatarPngBase64?: string | undefined;
|
|
42
|
+
avatarUrl?: string | undefined;
|
|
41
43
|
}, {
|
|
42
44
|
name: string;
|
|
43
45
|
avatarPngBase64?: string | undefined;
|
|
46
|
+
avatarUrl?: string | undefined;
|
|
44
47
|
}>>;
|
|
45
48
|
group: z.ZodOptional<z.ZodObject<{
|
|
46
49
|
name: z.ZodPipeline<z.ZodEffects<z.ZodString, string, string>, z.ZodString>;
|
|
47
50
|
avatarPngBase64: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
51
|
+
avatarUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
48
52
|
localParticipantId: z.ZodDefault<z.ZodString>;
|
|
49
53
|
participants: z.ZodArray<z.ZodObject<{
|
|
50
54
|
id: z.ZodString;
|
|
51
55
|
name: z.ZodPipeline<z.ZodEffects<z.ZodString, string, string>, z.ZodString>;
|
|
52
56
|
avatarPngBase64: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
57
|
+
avatarUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
53
58
|
}, "strict", z.ZodTypeAny, {
|
|
54
59
|
id: string;
|
|
55
60
|
name: string;
|
|
56
61
|
avatarPngBase64?: string | undefined;
|
|
62
|
+
avatarUrl?: string | undefined;
|
|
57
63
|
}, {
|
|
58
64
|
id: string;
|
|
59
65
|
name: string;
|
|
60
66
|
avatarPngBase64?: string | undefined;
|
|
67
|
+
avatarUrl?: string | undefined;
|
|
61
68
|
}>, "many">;
|
|
62
69
|
}, "strict", z.ZodTypeAny, {
|
|
63
70
|
name: string;
|
|
@@ -66,16 +73,20 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
66
73
|
id: string;
|
|
67
74
|
name: string;
|
|
68
75
|
avatarPngBase64?: string | undefined;
|
|
76
|
+
avatarUrl?: string | undefined;
|
|
69
77
|
}[];
|
|
70
78
|
avatarPngBase64?: string | undefined;
|
|
79
|
+
avatarUrl?: string | undefined;
|
|
71
80
|
}, {
|
|
72
81
|
name: string;
|
|
73
82
|
participants: {
|
|
74
83
|
id: string;
|
|
75
84
|
name: string;
|
|
76
85
|
avatarPngBase64?: string | undefined;
|
|
86
|
+
avatarUrl?: string | undefined;
|
|
77
87
|
}[];
|
|
78
88
|
avatarPngBase64?: string | undefined;
|
|
89
|
+
avatarUrl?: string | undefined;
|
|
79
90
|
localParticipantId?: string | undefined;
|
|
80
91
|
}>>;
|
|
81
92
|
statusBar: z.ZodDefault<z.ZodObject<{
|
|
@@ -163,6 +174,7 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
163
174
|
sender: z.ZodString;
|
|
164
175
|
text: z.ZodOptional<z.ZodPipeline<z.ZodEffects<z.ZodString, string, string>, z.ZodString>>;
|
|
165
176
|
imagePngBase64: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
177
|
+
imageUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
166
178
|
initial: z.ZodDefault<z.ZodBoolean>;
|
|
167
179
|
showTyping: z.ZodOptional<z.ZodBoolean>;
|
|
168
180
|
receipt: z.ZodDefault<z.ZodEnum<["none", "delivered", "read"]>>;
|
|
@@ -251,6 +263,7 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
251
263
|
id?: string | undefined;
|
|
252
264
|
text?: string | undefined;
|
|
253
265
|
imagePngBase64?: string | undefined;
|
|
266
|
+
imageUrl?: string | undefined;
|
|
254
267
|
showTyping?: boolean | undefined;
|
|
255
268
|
readAt?: string | undefined;
|
|
256
269
|
timing?: {
|
|
@@ -271,6 +284,7 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
271
284
|
id?: string | undefined;
|
|
272
285
|
text?: string | undefined;
|
|
273
286
|
imagePngBase64?: string | undefined;
|
|
287
|
+
imageUrl?: string | undefined;
|
|
274
288
|
initial?: boolean | undefined;
|
|
275
289
|
showTyping?: boolean | undefined;
|
|
276
290
|
receipt?: "none" | "delivered" | "read" | undefined;
|
|
@@ -313,6 +327,7 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
313
327
|
id?: string | undefined;
|
|
314
328
|
text?: string | undefined;
|
|
315
329
|
imagePngBase64?: string | undefined;
|
|
330
|
+
imageUrl?: string | undefined;
|
|
316
331
|
showTyping?: boolean | undefined;
|
|
317
332
|
readAt?: string | undefined;
|
|
318
333
|
timing?: {
|
|
@@ -333,6 +348,7 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
333
348
|
id?: string | undefined;
|
|
334
349
|
text?: string | undefined;
|
|
335
350
|
imagePngBase64?: string | undefined;
|
|
351
|
+
imageUrl?: string | undefined;
|
|
336
352
|
initial?: boolean | undefined;
|
|
337
353
|
showTyping?: boolean | undefined;
|
|
338
354
|
receipt?: "none" | "delivered" | "read" | undefined;
|
|
@@ -424,6 +440,7 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
424
440
|
id?: string | undefined;
|
|
425
441
|
text?: string | undefined;
|
|
426
442
|
imagePngBase64?: string | undefined;
|
|
443
|
+
imageUrl?: string | undefined;
|
|
427
444
|
showTyping?: boolean | undefined;
|
|
428
445
|
readAt?: string | undefined;
|
|
429
446
|
timing?: {
|
|
@@ -450,6 +467,7 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
450
467
|
contact?: {
|
|
451
468
|
name: string;
|
|
452
469
|
avatarPngBase64?: string | undefined;
|
|
470
|
+
avatarUrl?: string | undefined;
|
|
453
471
|
} | undefined;
|
|
454
472
|
group?: {
|
|
455
473
|
name: string;
|
|
@@ -458,8 +476,10 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
458
476
|
id: string;
|
|
459
477
|
name: string;
|
|
460
478
|
avatarPngBase64?: string | undefined;
|
|
479
|
+
avatarUrl?: string | undefined;
|
|
461
480
|
}[];
|
|
462
481
|
avatarPngBase64?: string | undefined;
|
|
482
|
+
avatarUrl?: string | undefined;
|
|
463
483
|
} | undefined;
|
|
464
484
|
}, {
|
|
465
485
|
messages: {
|
|
@@ -467,6 +487,7 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
467
487
|
id?: string | undefined;
|
|
468
488
|
text?: string | undefined;
|
|
469
489
|
imagePngBase64?: string | undefined;
|
|
490
|
+
imageUrl?: string | undefined;
|
|
470
491
|
initial?: boolean | undefined;
|
|
471
492
|
showTyping?: boolean | undefined;
|
|
472
493
|
receipt?: "none" | "delivered" | "read" | undefined;
|
|
@@ -510,6 +531,7 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
510
531
|
contact?: {
|
|
511
532
|
name: string;
|
|
512
533
|
avatarPngBase64?: string | undefined;
|
|
534
|
+
avatarUrl?: string | undefined;
|
|
513
535
|
} | undefined;
|
|
514
536
|
group?: {
|
|
515
537
|
name: string;
|
|
@@ -517,8 +539,10 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
517
539
|
id: string;
|
|
518
540
|
name: string;
|
|
519
541
|
avatarPngBase64?: string | undefined;
|
|
542
|
+
avatarUrl?: string | undefined;
|
|
520
543
|
}[];
|
|
521
544
|
avatarPngBase64?: string | undefined;
|
|
545
|
+
avatarUrl?: string | undefined;
|
|
522
546
|
localParticipantId?: string | undefined;
|
|
523
547
|
} | undefined;
|
|
524
548
|
statusBar?: {
|
|
@@ -600,6 +624,7 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
600
624
|
id?: string | undefined;
|
|
601
625
|
text?: string | undefined;
|
|
602
626
|
imagePngBase64?: string | undefined;
|
|
627
|
+
imageUrl?: string | undefined;
|
|
603
628
|
showTyping?: boolean | undefined;
|
|
604
629
|
readAt?: string | undefined;
|
|
605
630
|
timing?: {
|
|
@@ -626,6 +651,7 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
626
651
|
contact?: {
|
|
627
652
|
name: string;
|
|
628
653
|
avatarPngBase64?: string | undefined;
|
|
654
|
+
avatarUrl?: string | undefined;
|
|
629
655
|
} | undefined;
|
|
630
656
|
group?: {
|
|
631
657
|
name: string;
|
|
@@ -634,8 +660,10 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
634
660
|
id: string;
|
|
635
661
|
name: string;
|
|
636
662
|
avatarPngBase64?: string | undefined;
|
|
663
|
+
avatarUrl?: string | undefined;
|
|
637
664
|
}[];
|
|
638
665
|
avatarPngBase64?: string | undefined;
|
|
666
|
+
avatarUrl?: string | undefined;
|
|
639
667
|
} | undefined;
|
|
640
668
|
}, {
|
|
641
669
|
messages: {
|
|
@@ -643,6 +671,7 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
643
671
|
id?: string | undefined;
|
|
644
672
|
text?: string | undefined;
|
|
645
673
|
imagePngBase64?: string | undefined;
|
|
674
|
+
imageUrl?: string | undefined;
|
|
646
675
|
initial?: boolean | undefined;
|
|
647
676
|
showTyping?: boolean | undefined;
|
|
648
677
|
receipt?: "none" | "delivered" | "read" | undefined;
|
|
@@ -686,6 +715,7 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
686
715
|
contact?: {
|
|
687
716
|
name: string;
|
|
688
717
|
avatarPngBase64?: string | undefined;
|
|
718
|
+
avatarUrl?: string | undefined;
|
|
689
719
|
} | undefined;
|
|
690
720
|
group?: {
|
|
691
721
|
name: string;
|
|
@@ -693,8 +723,10 @@ export declare const iMessageConversationSchema: z.ZodEffects<z.ZodObject<{
|
|
|
693
723
|
id: string;
|
|
694
724
|
name: string;
|
|
695
725
|
avatarPngBase64?: string | undefined;
|
|
726
|
+
avatarUrl?: string | undefined;
|
|
696
727
|
}[];
|
|
697
728
|
avatarPngBase64?: string | undefined;
|
|
729
|
+
avatarUrl?: string | undefined;
|
|
698
730
|
localParticipantId?: string | undefined;
|
|
699
731
|
} | undefined;
|
|
700
732
|
statusBar?: {
|
|
@@ -802,6 +834,7 @@ export interface IMessageConversationPlan {
|
|
|
802
834
|
sender: string;
|
|
803
835
|
text: string;
|
|
804
836
|
imagePngBase64?: string;
|
|
837
|
+
imageUrl?: string;
|
|
805
838
|
initial: boolean;
|
|
806
839
|
receipt: 'none' | 'delivered' | 'read';
|
|
807
840
|
readAt?: string;
|
|
@@ -8,6 +8,10 @@ const avatarPng = zod_1.z.string().max(65536).regex(/^iVBORw0KGgo[A-Za-z0-9+/]*=
|
|
|
8
8
|
.refine(value => value.length % 4 === 0, 'Expected a base64 PNG, without a data URL prefix.');
|
|
9
9
|
const attachmentPng = zod_1.z.string().max(8 * 1024 * 1024).regex(/^iVBORw0KGgo[A-Za-z0-9+/]*={0,2}$/)
|
|
10
10
|
.refine(value => value.length % 4 === 0, 'Expected a base64 PNG, without a data URL prefix.');
|
|
11
|
+
const mediaUrl = zod_1.z.string().url().max(4096).refine(raw => {
|
|
12
|
+
const url = new URL(raw);
|
|
13
|
+
return url.protocol === 'https:' && !url.username && !url.password && !url.port && !url.hash;
|
|
14
|
+
}, 'Use a public HTTPS image URL without credentials, ports or fragments.');
|
|
11
15
|
const timingFields = {
|
|
12
16
|
charactersPerSecond: zod_1.z.number().finite().min(0.5).max(60),
|
|
13
17
|
pauseBeforeMs: zod_1.z.number().int().min(0).max(30000),
|
|
@@ -51,6 +55,7 @@ const messageSchema = zod_1.z.object({
|
|
|
51
55
|
sender: zod_1.z.string().regex(/^[A-Za-z0-9_-]{1,80}$/),
|
|
52
56
|
text: displayText.pipe(zod_1.z.string().min(1).max(2000)).optional(),
|
|
53
57
|
imagePngBase64: attachmentPng.optional(),
|
|
58
|
+
imageUrl: mediaUrl.optional(),
|
|
54
59
|
initial: zod_1.z.boolean().default(false),
|
|
55
60
|
showTyping: zod_1.z.boolean().optional(),
|
|
56
61
|
receipt: zod_1.z.enum(['none', 'delivered', 'read']).default('none'),
|
|
@@ -62,8 +67,8 @@ const messageSchema = zod_1.z.object({
|
|
|
62
67
|
timestampText: displayText.pipe(zod_1.z.string().trim().min(1).max(80)).optional(),
|
|
63
68
|
reactions: zod_1.z.array(reactionSchema).max(20).default([]),
|
|
64
69
|
}).strict().superRefine((value, ctx) => {
|
|
65
|
-
if (
|
|
66
|
-
ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, message: 'Provide exactly one text or
|
|
70
|
+
if ([value.text, value.imagePngBase64, value.imageUrl].filter(body => body !== undefined).length !== 1)
|
|
71
|
+
ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, message: 'Provide exactly one text, imagePngBase64 or imageUrl body; image captions are not supported.' });
|
|
67
72
|
});
|
|
68
73
|
const outputSchema = zod_1.z.discriminatedUnion('kind', [
|
|
69
74
|
zod_1.z.object({ kind: zod_1.z.literal('video') }).strict(),
|
|
@@ -76,15 +81,18 @@ exports.iMessageConversationSchema = zod_1.z.object({
|
|
|
76
81
|
contact: zod_1.z.object({
|
|
77
82
|
name: displayText.pipe(zod_1.z.string().trim().min(1).max(80)),
|
|
78
83
|
avatarPngBase64: avatarPng.optional(),
|
|
84
|
+
avatarUrl: mediaUrl.optional(),
|
|
79
85
|
}).strict().optional(),
|
|
80
86
|
group: zod_1.z.object({
|
|
81
87
|
name: displayText.pipe(zod_1.z.string().trim().min(1).max(80)),
|
|
82
88
|
avatarPngBase64: avatarPng.optional(),
|
|
89
|
+
avatarUrl: mediaUrl.optional(),
|
|
83
90
|
localParticipantId: zod_1.z.string().regex(/^[A-Za-z0-9_-]{1,80}$/).default('user'),
|
|
84
91
|
participants: zod_1.z.array(zod_1.z.object({
|
|
85
92
|
id: zod_1.z.string().regex(/^[A-Za-z0-9_-]{1,80}$/),
|
|
86
93
|
name: displayText.pipe(zod_1.z.string().trim().min(1).max(80)),
|
|
87
94
|
avatarPngBase64: avatarPng.optional(),
|
|
95
|
+
avatarUrl: mediaUrl.optional(),
|
|
88
96
|
}).strict()).min(3).max(20),
|
|
89
97
|
}).strict().optional(),
|
|
90
98
|
statusBar: statusBarSchema.default({}),
|
|
@@ -109,6 +117,10 @@ exports.iMessageConversationSchema = zod_1.z.object({
|
|
|
109
117
|
ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, message: 'Conversation request exceeds the 16 MiB limit.' });
|
|
110
118
|
if (!!value.contact === !!value.group)
|
|
111
119
|
ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, message: 'Provide a contact or a group, exclusively.' });
|
|
120
|
+
for (const avatar of [value.contact, value.group, ...(value.group?.participants ?? [])]) {
|
|
121
|
+
if (avatar?.avatarUrl !== undefined && avatar.avatarPngBase64 !== undefined)
|
|
122
|
+
ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, message: 'Provide avatarUrl or avatarPngBase64, not both.' });
|
|
123
|
+
}
|
|
112
124
|
const localId = value.group?.localParticipantId ?? 'user';
|
|
113
125
|
const participantIds = new Set(value.group?.participants.map(person => person.id) ?? ['user', 'recipient']);
|
|
114
126
|
if (value.group && (participantIds.size !== value.group.participants.length || !participantIds.has(localId)))
|
|
@@ -186,7 +198,7 @@ function compileIMessageConversation(input) {
|
|
|
186
198
|
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
187
199
|
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
188
200
|
}
|
|
189
|
-
const messages = value.messages.map((m, i) => ({ id: m.id ?? `message-${i + 1}`, sender: m.sender, text: m.text ?? '', imagePngBase64: m.imagePngBase64, initial: m.initial, receipt: m.receipt, readAt: m.readAt, replyTo: m.replyTo, sentAt: m.sentAt, timestampText: m.timestampText }));
|
|
201
|
+
const messages = value.messages.map((m, i) => ({ id: m.id ?? `message-${i + 1}`, sender: m.sender, text: m.text ?? '', imagePngBase64: m.imagePngBase64, imageUrl: m.imageUrl, initial: m.initial, receipt: m.receipt, readAt: m.readAt, replyTo: m.replyTo, sentAt: m.sentAt, timestampText: m.timestampText }));
|
|
190
202
|
const events = [];
|
|
191
203
|
const schedule = [];
|
|
192
204
|
let cursor = 0;
|
|
@@ -238,7 +250,7 @@ function compileIMessageConversation(input) {
|
|
|
238
250
|
cursor += 1400;
|
|
239
251
|
}
|
|
240
252
|
const typingStartMs = cursor;
|
|
241
|
-
if (isLocal && message.imagePngBase64 !== undefined) {
|
|
253
|
+
if (isLocal && (message.imagePngBase64 !== undefined || message.imageUrl !== undefined)) {
|
|
242
254
|
events.push({ type: 'composer.attach', atMs: cursor, messageId: resolved.id });
|
|
243
255
|
cursor += 1000;
|
|
244
256
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type IMessageConversationInput } from './imessage-conversation';
|
|
2
|
+
import type { CreateDmMessage, CreateDmNodeInputs } from './automations/nodes/create-dm';
|
|
3
|
+
export interface IMessageWorkflowConfig {
|
|
4
|
+
imessageLegacyInputs?: boolean;
|
|
5
|
+
imessageOutputType?: 'image' | 'video';
|
|
6
|
+
imessageRenderMode?: 'still' | 'conversation';
|
|
7
|
+
imessageConversation?: IMessageConversationInput;
|
|
8
|
+
imessageConversationInputType?: 'static' | 'variable';
|
|
9
|
+
imessageAttachmentMessageId?: string;
|
|
10
|
+
dynamicCrop?: unknown;
|
|
11
|
+
imessageSenderBubbleColor?: string;
|
|
12
|
+
messages?: CreateDmMessage[];
|
|
13
|
+
messagesInputType?: 'static' | 'variable';
|
|
14
|
+
username?: string;
|
|
15
|
+
usernameInputType?: 'static' | 'variable';
|
|
16
|
+
time?: string;
|
|
17
|
+
timeInputType?: 'static' | 'variable';
|
|
18
|
+
lightMode?: boolean;
|
|
19
|
+
lightModeInputType?: 'static' | 'variable';
|
|
20
|
+
profilePicUrl?: string;
|
|
21
|
+
profilePicInputType?: 'static' | 'variable';
|
|
22
|
+
imageAttachmentUrl?: string;
|
|
23
|
+
imageAttachmentInputType?: 'static' | 'variable';
|
|
24
|
+
imessageUnreadBadgeInputType?: 'static' | 'variable';
|
|
25
|
+
imessageUnreadBadgeText?: string;
|
|
26
|
+
imessageReadReceiptTimeInputType?: 'static' | 'variable';
|
|
27
|
+
imessageReadReceiptText?: string;
|
|
28
|
+
imessageShowReadReceipt?: boolean;
|
|
29
|
+
imessageMessageHeaderTimeInputType?: 'static' | 'variable';
|
|
30
|
+
imessageMessageHeaderTimestampText?: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function iMessageWorkflowOutput(config: IMessageWorkflowConfig): 'image' | 'video';
|
|
33
|
+
export declare function legacyIMessageIssues(config: IMessageWorkflowConfig): string[];
|
|
34
|
+
/** Resolve saved screenshot fields and current workflow inputs without moving connections. */
|
|
35
|
+
export declare function prepareIMessageWorkflow(config: IMessageWorkflowConfig, inputs?: CreateDmNodeInputs): IMessageConversationInput;
|