ugcinc 4.5.76 → 4.5.78

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.
@@ -26,7 +26,7 @@ export declare class AutomationsClient extends BaseClient {
26
26
  * @param skipValidation - If true, skips workflow validation (for saving drafts)
27
27
  */
28
28
  createTemplate(params: {
29
- orgId: string;
29
+ orgId?: string;
30
30
  name: string;
31
31
  description?: string;
32
32
  workflowDefinition: WorkflowDefinition;
@@ -0,0 +1,29 @@
1
+ import type { PortValue } from '../types';
2
+ import { type ObjectSchemaField, type OutputMode, type SelectionMode } from './types';
3
+ /** API Request inputs are dynamic (template variables in URL, headers, body). */
4
+ export type ApiRequestNodeInputs = Record<string, PortValue | PortValue[]>;
5
+ /** API Request outputs are dynamic based on outputFields config. */
6
+ export type ApiRequestNodeOutputs = Record<string, PortValue | PortValue[]>;
7
+ declare const HttpMethods: readonly ["GET", "POST", "PUT", "PATCH", "DELETE"];
8
+ type HttpMethod = typeof HttpMethods[number];
9
+ export interface HeaderEntry {
10
+ key: string;
11
+ value: string;
12
+ }
13
+ declare const definition: import("./types").NodeDefinition<"api-request", "generator", {
14
+ url: string;
15
+ method: HttpMethod;
16
+ headers: HeaderEntry[];
17
+ body: string;
18
+ outputFields: ObjectSchemaField[];
19
+ outputMode: OutputMode;
20
+ selectionMode: SelectionMode | null;
21
+ }, ApiRequestNodeInputs, ApiRequestNodeOutputs, false>;
22
+ declare const _default: typeof definition & {
23
+ __TInputs: ApiRequestNodeInputs;
24
+ __TOutputs: ApiRequestNodeOutputs;
25
+ };
26
+ export default _default;
27
+ export { HttpMethods };
28
+ export type { HttpMethod };
29
+ export type ApiRequestNodeConfig = typeof definition.defaults;
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpMethods = void 0;
4
+ const utils_1 = require("../utils");
5
+ const types_1 = require("./types");
6
+ // =============================================================================
7
+ // Config Types
8
+ // =============================================================================
9
+ const HttpMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
10
+ exports.HttpMethods = HttpMethods;
11
+ // =============================================================================
12
+ // Node Definition
13
+ // =============================================================================
14
+ const definition = (0, types_1.defineNode)({
15
+ nodeId: 'api-request',
16
+ label: 'API Request',
17
+ description: 'Make HTTP requests to external APIs',
18
+ guide: 'Makes HTTP requests to external APIs and returns parsed response data. Use when you need to fetch data from or send data to external services. Configure URL, method (GET/POST/PUT/PATCH/DELETE), headers, and body. Supports template variables ({{var}}) in URL, header values, and body for dynamic values from upstream nodes. Define output fields to extract specific data from JSON responses. Use cases: fetching external data, triggering webhooks, integrating third-party APIs, sending data to external services.',
19
+ type: 'generator',
20
+ category: 'Generation',
21
+ outputModes: ['per-input', 'single'],
22
+ selectionModes: null,
23
+ defaults: {
24
+ url: '',
25
+ method: 'GET',
26
+ headers: [],
27
+ body: '',
28
+ outputFields: [{ name: 'response', type: 'string' }],
29
+ outputMode: 'per-input',
30
+ selectionMode: null,
31
+ },
32
+ computePorts: ({ config }) => {
33
+ const url = config?.url ?? '';
34
+ const headers = (config?.headers ?? []);
35
+ const body = config?.body ?? '';
36
+ // Extract template variables from URL, header values, and body
37
+ const textsToScan = [
38
+ url,
39
+ body,
40
+ ...headers.map(h => h.value),
41
+ ];
42
+ const variables = (0, utils_1.extractTemplateVariables)(textsToScan);
43
+ const inputs = variables.map(v => ({
44
+ id: v,
45
+ type: ['text', 'object', 'boolean'],
46
+ isArray: false,
47
+ required: true,
48
+ }));
49
+ // Output ports from outputFields
50
+ const outputFields = (config?.outputFields ?? [{ name: 'response', type: 'string' }]);
51
+ const outputs = outputFields.map(f => {
52
+ let pType;
53
+ let pIsArray;
54
+ if (f.type === 'array') {
55
+ pType = f.items === 'object' ? 'object' : 'text';
56
+ pIsArray = true;
57
+ }
58
+ else if (f.type === 'object') {
59
+ pType = 'object';
60
+ pIsArray = false;
61
+ }
62
+ else if (f.type === 'number') {
63
+ pType = 'number';
64
+ pIsArray = false;
65
+ }
66
+ else if (f.type === 'boolean') {
67
+ pType = 'boolean';
68
+ pIsArray = false;
69
+ }
70
+ else {
71
+ pType = 'text';
72
+ pIsArray = false;
73
+ }
74
+ return {
75
+ id: f.name,
76
+ type: pType,
77
+ isArray: pIsArray,
78
+ required: f.required ?? true,
79
+ ...(f.objectSchema && { objectSchema: f.objectSchema }),
80
+ };
81
+ });
82
+ return { inputs, outputs };
83
+ },
84
+ generatePreview: (config, _ctx, cachedOutputs) => {
85
+ const result = {};
86
+ const outputFields = config.outputFields ?? [{ name: 'response', type: 'string' }];
87
+ for (const field of outputFields) {
88
+ result[field.name] = cachedOutputs?.[field.name] ?? null;
89
+ }
90
+ return (0, types_1.preview)(result);
91
+ },
92
+ validate: (config) => {
93
+ const errors = [];
94
+ if (!config.url || config.url.trim().length === 0) {
95
+ errors.push('URL is required');
96
+ }
97
+ if (config.method && !HttpMethods.includes(config.method)) {
98
+ errors.push(`Invalid HTTP method: ${config.method}`);
99
+ }
100
+ return errors;
101
+ },
102
+ });
103
+ exports.default = definition;
@@ -13,6 +13,18 @@ export declare const nodeDefinitions: {
13
13
  __TInputs: import("./account").AccountNodeInputs;
14
14
  __TOutputs: import("./account").AccountNodeOutputs;
15
15
  };
16
+ readonly 'api-request': NodeDefinition<"api-request", "generator", {
17
+ url: string;
18
+ method: import("./api-request").HttpMethod;
19
+ headers: import("./api-request").HeaderEntry[];
20
+ body: string;
21
+ outputFields: import("./types").ObjectSchemaField[];
22
+ outputMode: import("./types").OutputMode;
23
+ selectionMode: import("./types").SelectionMode | null;
24
+ }, import("./api-request").ApiRequestNodeInputs, import("./api-request").ApiRequestNodeOutputs, false> & {
25
+ __TInputs: import("./api-request").ApiRequestNodeInputs;
26
+ __TOutputs: import("./api-request").ApiRequestNodeOutputs;
27
+ };
16
28
  readonly 'auto-caption': NodeDefinition<"auto-caption", "generator", {
17
29
  preset: import("./auto-caption").AutoCaptionPreset;
18
30
  fontName: string | undefined;
@@ -12,6 +12,7 @@ const internal_1 = require("./internal");
12
12
  Object.defineProperty(exports, "internalNodeTypes", { enumerable: true, get: function () { return internal_1.internalNodeTypes; } });
13
13
  // Import all node definitions
14
14
  const account_1 = __importDefault(require("./account"));
15
+ const api_request_1 = __importDefault(require("./api-request"));
15
16
  const auto_caption_1 = __importDefault(require("./auto-caption"));
16
17
  const auto_post_1 = __importDefault(require("./auto-post"));
17
18
  const branch_1 = __importDefault(require("./branch"));
@@ -50,6 +51,7 @@ const video_import_1 = __importDefault(require("./video-import"));
50
51
  */
51
52
  exports.nodeDefinitions = {
52
53
  'account': account_1.default,
54
+ 'api-request': api_request_1.default,
53
55
  'auto-caption': auto_caption_1.default,
54
56
  'auto-post': auto_post_1.default,
55
57
  'branch': branch_1.default,
package/dist/index.d.ts CHANGED
@@ -27,7 +27,7 @@ export type { InputType } from './automations/nodes/types';
27
27
  export type { ClientConfig } from './base';
28
28
  export type { Account, AccountStat, AccountTask, EditProfileInfo, GetAccountsParams, GetAccountStatsParams, GetAccountStatusParams, AccountInfoUpdate, UpdateAccountInfoParams, AccountInfoUpdateResult, UpdateAccountInfoResponse, AccountSocialUpdate, UpdateAccountSocialParams, AccountSocialUpdateResult, UpdateAccountSocialResponse, DeleteAccountPostsParams, DeleteAccountPostsResponse, ResetWarmupParams, ResetWarmupResponse, NicheSwitchUpdate, NicheSwitchParams, NicheSwitchResult, NicheSwitchResponse, CreateAccountInput, CreateAccountsParams, CreateAccountResult, CreateAccountsResponse, TroubleshootFailReason, TroubleshootAccount, TroubleshootParams, } from './accounts';
29
29
  export type { TaskType, Task, GetTasksParams } from './tasks';
30
- export type { PostType, PostStatus, Post, PostStat, GetPostsParams, CreateSlideshowParams, GetPostStatsParams, GetPostStatusParams, CreateVideoParams, UpdatePostParams, DeletePostsParams, DeletePostsResponse, RetryPostsParams, SetPostStatusParams, SetPostStatusResponse, PreviewScheduleEntry, PreviewScheduleParams, PreviewScheduleResult, } from './posts';
30
+ export type { PostType, PostStatus, Post, PostStat, GetPostsParams, CreateDraftParams, CreateSlideshowParams, GetPostStatsParams, GetPostStatusParams, CreateVideoParams, UpdatePostParams, DeletePostsParams, DeletePostsResponse, RetryPostsParams, SetPostStatusParams, SetPostStatusResponse, PreviewScheduleEntry, PreviewScheduleParams, PreviewScheduleResult, } from './posts';
31
31
  export type { RefreshStatsParams, RefreshStatsError, RefreshStatsResponse, RefreshStartEvent, RefreshProgressEvent, RefreshDoneEvent, RefreshStreamEvent, DailyAggregatedStat, GetDailyAggregatedStatsParams, DailyAccountStat, GetDailyAccountStatsParams, DailyPostStat, GetDailyPostStatsParams, DashboardDailyStat, GetDashboardDailyStatsParams, TopAccount, GetTopAccountsParams, TopPost, GetTopPostsParams, } from './stats';
32
32
  export type { Org, ApiKey, DeleteApiKeyParams, EditApiKeyParams, IntegrationKey, IntegrationProvider, UpsertIntegrationKeyParams, DeleteIntegrationKeyParams } from './org';
33
33
  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, GetUploadTokenParams, UploadTokenResponse, } from './media';
@@ -38,6 +38,8 @@ export type { VideoEditorNodeConfig, VideoEditorChannel, VideoEditorSegment, Vid
38
38
  export type { ImageEditorNodeConfig, ImageComposerNodeConfig, ImageComposerRenderInput } from './automations/nodes/image-composer';
39
39
  export { prepareImageComposerInput } from './automations/nodes/image-composer';
40
40
  export type { AccountNodeConfig } from './automations/nodes/account';
41
+ export type { ApiRequestNodeConfig, HeaderEntry, HttpMethod } from './automations/nodes/api-request';
42
+ export { HttpMethods } from './automations/nodes/api-request';
41
43
  export type { AutoCaptionNodeConfig, AutoCaptionPreset, AutoCaptionFontWeight, AutoCaptionPosition, } from './automations/nodes/auto-caption';
42
44
  export { nodeConfigToCaptionStyle } from './automations/nodes/auto-caption';
43
45
  export type { AutoPostNodeConfig, AutoPostMode, PostSchedulingMode, } from './automations/nodes/auto-post';
package/dist/index.js CHANGED
@@ -6,8 +6,8 @@
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.removePreviewForConnection = exports.updatePreviewMapForConnection = exports.computePreviewMap = exports.computeAllNodePreviews = exports.resolveNodePreview = exports.getWorkflowOutputSchema = exports.hasMissingTerminalError = 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.submitIMessageDmRenderJob = exports.submitInstagramDmRenderJob = exports.submitAutoCaptionRenderJob = exports.submitScreenshotAnimationRenderJob = 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.LLMProviders = exports.applyLogicOperator = exports.IfLogicOperators = exports.resolvePath = exports.indexExpressionToString = exports.indexExpressionToIndexes = exports.resolveUnknownPath = exports.getOutputTypeFromCategory = exports.parseOpenAPIOutputPath = exports.parseOpenAPIInputs = exports.mapOpenAPIType = exports.nodeConfigToCaptionStyle = 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 = void 0;
10
- exports.prepareVideoComposerInput = exports.formatDateTag = exports.outputFieldsToZod = exports.outputFieldToZod = void 0;
9
+ exports.applyLogicOperator = exports.IfLogicOperators = exports.resolvePath = exports.indexExpressionToString = exports.indexExpressionToIndexes = exports.resolveUnknownPath = exports.getOutputTypeFromCategory = 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 = void 0;
10
+ exports.prepareVideoComposerInput = exports.formatDateTag = exports.outputFieldsToZod = exports.outputFieldToZod = exports.LLMProviders = void 0;
11
11
  // =============================================================================
12
12
  // Client Exports
13
13
  // =============================================================================
@@ -141,6 +141,8 @@ Object.defineProperty(exports, "processTemplate", { enumerable: true, get: funct
141
141
  Object.defineProperty(exports, "substituteVariables", { enumerable: true, get: function () { return utils_1.substituteVariables; } });
142
142
  var image_composer_1 = require("./automations/nodes/image-composer");
143
143
  Object.defineProperty(exports, "prepareImageComposerInput", { enumerable: true, get: function () { return image_composer_1.prepareImageComposerInput; } });
144
+ var api_request_1 = require("./automations/nodes/api-request");
145
+ Object.defineProperty(exports, "HttpMethods", { enumerable: true, get: function () { return api_request_1.HttpMethods; } });
144
146
  var auto_caption_1 = require("./automations/nodes/auto-caption");
145
147
  Object.defineProperty(exports, "nodeConfigToCaptionStyle", { enumerable: true, get: function () { return auto_caption_1.nodeConfigToCaptionStyle; } });
146
148
  var custom_model_1 = require("./automations/nodes/custom-model");
package/dist/posts.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { BaseClient } from './base';
2
2
  import type { ApiResponse } from './types';
3
3
  export type PostType = 'video' | 'slideshow';
4
- export type PostStatus = 'scheduled' | 'pending' | 'complete' | 'failed' | 'retrying' | 'deleting' | 'deleted' | 'hidden' | 'require-approval';
4
+ export type PostStatus = 'draft' | 'scheduled' | 'pending' | 'complete' | 'failed' | 'retrying' | 'deleting' | 'deleted' | 'hidden' | 'require-approval';
5
5
  export interface Post {
6
6
  id: string;
7
- account_id: string;
7
+ account_id: string | null;
8
8
  type: PostType;
9
9
  status: PostStatus;
10
10
  social_id: string | null;
@@ -102,6 +102,13 @@ export interface PreviewScheduleEntry {
102
102
  postTime: string;
103
103
  postId?: string;
104
104
  }
105
+ export interface CreateDraftParams {
106
+ type: 'video' | 'slideshow';
107
+ mediaUrls: string[];
108
+ caption?: string;
109
+ title?: string;
110
+ socialAudioId?: string;
111
+ }
105
112
  export interface PreviewScheduleParams {
106
113
  entries: PreviewScheduleEntry[];
107
114
  excludePostIds?: string[];
@@ -122,6 +129,13 @@ export declare class PostsClient extends BaseClient {
122
129
  * Get posts with optional filters
123
130
  */
124
131
  getPosts(params?: GetPostsParams): Promise<ApiResponse<Post[]>>;
132
+ /**
133
+ * Create a draft post without assigning it to an account
134
+ *
135
+ * Draft posts have no account or schedule. Use updatePost() to assign
136
+ * an account and schedule time, which transitions the draft to "scheduled" status.
137
+ */
138
+ createDraft(params: CreateDraftParams): Promise<ApiResponse<Post>>;
125
139
  /**
126
140
  * Create a slideshow post
127
141
  */
@@ -152,8 +166,9 @@ export declare class PostsClient extends BaseClient {
152
166
  /**
153
167
  * Update a post in place
154
168
  *
155
- * Only posts with status "scheduled" or "failed" can be updated.
169
+ * Only posts with status "draft", "scheduled", or "failed" can be updated.
156
170
  * If updating a failed post, it will be reset to "scheduled" status.
171
+ * If updating a draft post with both accountId and postTime, it transitions to "scheduled".
157
172
  * The post type (video/slideshow) cannot be changed.
158
173
  */
159
174
  updatePost(params: UpdatePostParams): Promise<ApiResponse<Post>>;
package/dist/posts.js CHANGED
@@ -12,6 +12,15 @@ class PostsClient extends base_1.BaseClient {
12
12
  async getPosts(params) {
13
13
  return this.post('/post', params ?? {});
14
14
  }
15
+ /**
16
+ * Create a draft post without assigning it to an account
17
+ *
18
+ * Draft posts have no account or schedule. Use updatePost() to assign
19
+ * an account and schedule time, which transitions the draft to "scheduled" status.
20
+ */
21
+ async createDraft(params) {
22
+ return this.post('/post/draft', params);
23
+ }
15
24
  /**
16
25
  * Create a slideshow post
17
26
  */
@@ -46,8 +55,9 @@ class PostsClient extends base_1.BaseClient {
46
55
  /**
47
56
  * Update a post in place
48
57
  *
49
- * Only posts with status "scheduled" or "failed" can be updated.
58
+ * Only posts with status "draft", "scheduled", or "failed" can be updated.
50
59
  * If updating a failed post, it will be reset to "scheduled" status.
60
+ * If updating a draft post with both accountId and postTime, it transitions to "scheduled".
51
61
  * The post type (video/slideshow) cannot be changed.
52
62
  */
53
63
  async updatePost(params) {
package/dist/skills.js CHANGED
@@ -216,92 +216,160 @@ ${run} delete_media '{"ids":["id1","id2"]}'
216
216
  `,
217
217
  'automations/SKILL.md': `---
218
218
  name: automations
219
- description: Run and manage UGC Inc automation workflows - list templates, trigger runs, check status, manage scheduling. Use when the user wants to run, check, or manage their automations.
219
+ description: Build, run, and manage UGC Inc automation workflows - create templates, list nodes, trigger runs, check status, manage scheduling. Use when the user wants to build, run, check, or manage their automations.
220
220
  allowed-tools: Bash, Read
221
221
  ---
222
222
 
223
- You manage UGC Inc automations through the CLI. Run tools with:
223
+ You build and manage UGC Inc automations through the CLI. Run tools with:
224
224
  \`\`\`bash
225
225
  ${run} <tool_name> '<json_params>'
226
226
  \`\`\`
227
227
 
228
228
  Auth: \`npx ugcinc auth <api_key> [--admin]\`. Only needed once. Use \`--admin\` for admin keys.
229
229
 
230
+ ## Building Workflows
231
+
232
+ ### Workflow Structure
233
+ A workflow is a directed graph of nodes. Each node has a type, config, inputs (connections from other nodes), and outputs (ports other nodes can connect to).
234
+
235
+ \`\`\`json
236
+ {
237
+ "nodes": [
238
+ {
239
+ "id": "unique-node-id",
240
+ "type": "node-type",
241
+ "name": "Display Name",
242
+ "config": { ... },
243
+ "inputs": {
244
+ "input-port-id": {
245
+ "sourceNodeId": "other-node-id",
246
+ "sourceOutputId": "output-port-id"
247
+ }
248
+ }
249
+ }
250
+ ]
251
+ }
252
+ \`\`\`
253
+
254
+ ### How to Build a Workflow
255
+ 1. Run \`list_node_types\` to see all available nodes with descriptions, guides, ports, and defaults
256
+ 2. Run \`get_node_type\` for detailed info on specific nodes you plan to use
257
+ 3. Build the workflow definition by:
258
+ - Creating nodes with unique IDs (e.g. "llm-1", "media-1", "auto-post-1")
259
+ - Setting each node's config (use defaults from get_node_type as a starting point)
260
+ - Wiring inputs: connect each node's input ports to other nodes' output ports
261
+ 4. Run \`validate_workflow\` to check for errors before creating
262
+ 5. Run \`create_automation\` to save the template
263
+
264
+ ### Port Types
265
+ Data flows between nodes through typed ports: \`image\`, \`video\`, \`audio\`, \`text\`, \`number\`, \`boolean\`, \`object\`, \`account\`, \`date\`, \`social_audio\`, \`enum\`. Ports can be arrays (e.g. image[]).
266
+
267
+ ### Common Workflow Patterns
268
+ - **Simple post**: Media → Auto Post (with Account)
269
+ - **AI-generated content**: LLM → Auto Post (with Account + Media for video)
270
+ - **Templated content**: Media + LLM → Image/Video Composer → Auto Post
271
+ - **Fan-out**: Account (per-input mode) → ... → Auto Post (one post per account)
272
+ - **Captioned video**: Media → Auto Caption → Auto Post
273
+ - **AI video pipeline**: LLM (caption) + Generate Video → Auto Post
274
+
275
+ ## Node Reference
276
+
277
+ Nodes are organized into 4 categories:
278
+
279
+ ### Source Nodes (provide data)
280
+ - **account** — Select accounts to post from. Outputs: account. Modes: per-input (fan-out, one run per account) or single.
281
+ - **media** — Pull saved media (videos, images, audio) from library. Outputs: configurable per port. Selection: random or sequential.
282
+ - **text** — Text pool with {{variable}} template support. Outputs: text. Selection: random or sequential.
283
+ - **input** — Define runtime variables supplied when triggering the workflow. Outputs: user-defined ports.
284
+ - **social-audio** — Trending TikTok/Instagram audio tracks. Outputs: social_audio. Linked natively when posting.
285
+ - **video-import** — Import video from YouTube, TikTok, Instagram URL. Inputs: url (text). Outputs: video.
286
+
287
+ ### Generator Nodes (transform/create)
288
+ - **llm** — Generate text/structured data with AI (Claude, GPT, Gemini, Groq, Kimi, Grok). Uses {{variable}} placeholders in system prompt for inputs. Can accept image/video inputs for multimodal. Outputs: structured fields defined in config.
289
+ - **generate-image** — AI image generation via fal.ai (Gemini, Nano Banana, GPT Image). Inputs: prompt (text), optional images. Outputs: image.
290
+ - **generate-video** — AI video generation (Veo, Kling, Sora, MiniMax, Wan). Modes: text-to-video, image-to-video, first-last-frame. Outputs: video.
291
+ - **image-composer** — Templated image composition with variable slots for images and text overlays. Outputs: image.
292
+ - **video-composer** — Templated video with timeline channels, variable media segments, and text overlays. Outputs: video.
293
+ - **auto-caption** — Burn TikTok-style captions into video. Presets: hormozi, minimal, bold-pop, clean, neon. Inputs: video. Outputs: video.
294
+ - **custom-model** — Run any fal.ai model by ID. Configure input params and output type/path. Outputs: image, video, or audio.
295
+ - **transcript** — Speech-to-text with timestamps. Inputs: video or audio. Outputs: text + segments (object[]).
296
+ - **create-dm** — Render fake DM screenshots (iMessage/Instagram). Outputs: image.
297
+ - **screenshot-animation** — iPhone reveal animation from image. Inputs: image. Outputs: video.
298
+ - **deduplicate** — Modify video to evade content-ID detection (levels 1-5). Inputs: video. Outputs: video.
299
+ - **regex** — Match/replace text with regex. Outputs: result (text), match (boolean), groups (text[]).
300
+ - **random** — Pick one value randomly from array or weighted inputs. Outputs: selected value.
301
+ - **not** — Invert boolean. Inputs: boolean. Outputs: boolean.
302
+
303
+ ### Flow Control Nodes (routing/iteration)
304
+ - **if** — Conditional branching. Inputs: boolean(s) + passthroughs. Outputs: true-{port} and false-{port}. Logic: and/or/xor/nor.
305
+ - **branch** — Route by key match (e.g. from LLM output). Only matching branch executes. Inputs: key (text) + passthroughs.
306
+ - **random-route** — Route to random branch with probabilities. Only one branch executes per run.
307
+ - **for-each** — Iterate over array, running downstream nodes per item. Exposes item and index. Pair with Collect.
308
+ - **collect** — Aggregate For Each outputs back into array. Must be inside For Each context.
309
+ - **destructure** — Extract elements from array by index/range/list. Outputs: per-selection ports.
310
+ - **compose-workflow** — Embed a sub-workflow. Inputs/outputs resolved from sub-workflow's Input and Pass-Through nodes.
311
+
312
+ ### Terminal Nodes (outputs)
313
+ - **auto-post** — Publish video or slideshow to TikTok/Instagram. Modes: video or slideshow. Scheduling: random window, min distance, max posts/day. Can require approval. Inputs: video/images + account + optional caption + optional social-audio.
314
+ - **save-to-media** — Save outputs to media library with optional tags. Inputs: configurable (image, video, audio, text).
315
+ - **passthrough** — Pass data to parent workflow (for compose-workflow sub-workflows only).
316
+
317
+ For detailed port schemas, config options, and guides, use \`list_node_types\` and \`get_node_type\`.
318
+
230
319
  ## Tools
231
320
 
232
- ### list_automations
233
- List all automation templates.
321
+ ### Node Discovery
234
322
  \`\`\`bash
235
- ${run} list_automations
323
+ ${run} list_node_types # All nodes with ports, guides, defaults
324
+ ${run} get_node_type '{"nodeType":"llm"}' # Detailed info for one node
236
325
  \`\`\`
237
326
 
238
- ### get_automation
239
- Get a specific template with full workflow definition.
327
+ ### Template Management
240
328
  \`\`\`bash
241
- ${run} get_automation '{"templateId":"id"}'
329
+ ${run} list_automations # List all templates
330
+ ${run} get_automation '{"templateId":"id"}' # Get template with full workflow
331
+ ${run} create_automation '{"name":"My Automation","workflowDefinition":{...}}'
332
+ ${run} update_automation '{"templateId":"id","name":"Updated","workflowDefinition":{...}}'
333
+ ${run} validate_workflow '{"workflowDefinition":{...}}'
334
+ ${run} delete_automation '{"templateId":"id"}'
242
335
  \`\`\`
243
336
 
244
- ### run_automation
245
- Run an automation. Optionally provide variable inputs.
337
+ ### Execution
246
338
  \`\`\`bash
247
339
  ${run} run_automation '{"templateId":"id"}'
248
340
  ${run} run_automation '{"templateId":"id","variableInputs":{"node-id":"value"}}'
249
- \`\`\`
250
-
251
- ### get_automation_status
252
- Get run status with executor nodes and data flow edges.
253
- \`\`\`bash
254
341
  ${run} get_automation_status '{"runId":"id"}'
342
+ ${run} get_automation_logs '{"runId":"id"}'
343
+ ${run} stop_automation '{"runId":"id"}'
344
+ ${run} export_automation '{"templateId":"id"}' # Debug: full template export
345
+ ${run} export_automation_run '{"runId":"id"}' # Debug: full run export with executor outputs
255
346
  \`\`\`
256
347
 
257
- ### list_automation_runs
258
- List runs for a specific template.
348
+ ### Run History & Review
259
349
  \`\`\`bash
260
350
  ${run} list_automation_runs '{"templateId":"id","limit":10}'
261
- \`\`\`
262
-
263
- ### list_all_automation_runs
264
- List all runs across all templates.
265
- \`\`\`bash
266
351
  ${run} list_all_automation_runs '{"limit":10}'
267
352
  ${run} list_all_automation_runs '{"reviewStatus":"pending_review"}'
353
+ ${run} update_automation_review '{"runId":"id","reviewStatus":"approved"}'
354
+ ${run} delete_automation_run '{"runId":"id"}'
268
355
  \`\`\`
269
356
 
270
- ### stop_automation
271
- Stop a running automation.
272
- \`\`\`bash
273
- ${run} stop_automation '{"runId":"id"}'
274
- \`\`\`
275
-
276
- ### publish_automation
277
- Enable recurrence scheduling.
278
- \`\`\`bash
279
- ${run} publish_automation '{"templateId":"id"}'
280
- \`\`\`
281
-
282
- ### unpublish_automation
283
- Disable recurrence scheduling.
357
+ ### Scheduling
284
358
  \`\`\`bash
359
+ ${run} get_recurrence_status '{"templateId":"id"}'
360
+ ${run} publish_automation '{"templateId":"id","scheduleConfig":{"frequencyType":"per-day","runsPerDay":3,"timingType":"random-window","randomWindowStart":"09:00","randomWindowEnd":"21:00","timezone":"America/New_York"},"accountIterationConfig":null}'
285
361
  ${run} unpublish_automation '{"templateId":"id"}'
286
- \`\`\`
287
-
288
- ### run_automation_once
289
- Run once immediately (also updates recurrence schedule if published).
290
- \`\`\`bash
291
362
  ${run} run_automation_once '{"templateId":"id"}'
292
363
  \`\`\`
293
364
 
294
- ### get_automation_logs
295
- Get execution logs for a run.
296
- \`\`\`bash
297
- ${run} get_automation_logs '{"runId":"id"}'
298
- \`\`\`
299
-
300
- ## Workflow tips
301
- - Always list automations first to get template IDs and names
365
+ ## Workflow Tips
366
+ - **Always start with \`list_node_types\`** to understand available nodes before building
367
+ - Use \`get_node_type\` to see a node's default config, then customize from there
368
+ - Use \`validate_workflow\` before \`create_automation\` to catch errors early
302
369
  - After running, use \`get_automation_status\` to monitor progress
303
- - For recurring automations, check \`publish_automation\` / \`unpublish_automation\`
304
370
  - Summarize run status: show completed/total nodes, any failures
371
+ - For recurring automations, use \`publish_automation\` / \`unpublish_automation\`
372
+ - When building from a user description, map their intent to node types, then wire the data flow
305
373
  - If $ARGUMENTS is provided, interpret it as a natural language request
306
374
  `,
307
375
  'billing/SKILL.md': `---
@@ -2,7 +2,125 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.automationTools = void 0;
4
4
  const zod_1 = require("zod");
5
+ const graph_controller_1 = require("../automations/graph-controller");
6
+ const workflowNodeSchema = zod_1.z.object({
7
+ id: zod_1.z.string().describe('Unique node ID (e.g. "llm-1", "media-2")'),
8
+ type: zod_1.z.string().describe('Node type (e.g. "llm", "media", "auto-post"). Use list_node_types to see all types.'),
9
+ name: zod_1.z.string().optional().describe('Display name for the node'),
10
+ x: zod_1.z.number().optional().describe('Canvas X position'),
11
+ y: zod_1.z.number().optional().describe('Canvas Y position'),
12
+ config: zod_1.z.record(zod_1.z.unknown()).describe('Node-specific config. Use get_node_type to see defaults and available options.'),
13
+ inputs: zod_1.z.record(zod_1.z.object({
14
+ sourceNodeId: zod_1.z.string().describe('ID of the source node'),
15
+ sourceOutputId: zod_1.z.string().describe('Output port ID on the source node'),
16
+ })).describe('Map of input port ID → source connection'),
17
+ });
18
+ const workflowDefinitionSchema = zod_1.z.object({
19
+ nodes: zod_1.z.array(workflowNodeSchema).describe('Array of workflow nodes'),
20
+ canvasState: zod_1.z.object({
21
+ zoom: zod_1.z.number(),
22
+ panX: zod_1.z.number(),
23
+ panY: zod_1.z.number(),
24
+ }).optional().describe('Canvas UI state'),
25
+ });
26
+ const scheduleConfigSchema = zod_1.z.object({
27
+ frequencyType: zod_1.z.enum(['per-day', 'periodic']).describe("'per-day' = N runs per day, 'periodic' = every M days"),
28
+ runsPerDay: zod_1.z.number().optional().describe('Number of runs per day (for per-day frequency)'),
29
+ periodDays: zod_1.z.number().optional().describe('Run every N days (for periodic frequency)'),
30
+ daysOfWeek: zod_1.z.array(zod_1.z.enum(['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'])).optional().describe('Which days of the week to run'),
31
+ timingType: zod_1.z.enum(['specific', 'random-window']).describe("'specific' = run at exact times, 'random-window' = run randomly within a window"),
32
+ specificTimes: zod_1.z.array(zod_1.z.string()).optional().describe('Exact times to run (HH:MM format, for specific timing)'),
33
+ randomWindowStart: zod_1.z.string().optional().describe('Window start time (HH:MM format, for random-window timing)'),
34
+ randomWindowEnd: zod_1.z.string().optional().describe('Window end time (HH:MM format, for random-window timing)'),
35
+ timezone: zod_1.z.string().describe('IANA timezone (e.g. "America/New_York")'),
36
+ });
37
+ const accountIterationConfigSchema = zod_1.z.object({
38
+ enabled: zod_1.z.boolean().describe('Whether account iteration is enabled'),
39
+ selectionMode: zod_1.z.enum(['specific', 'by-tag']).describe("'specific' = use accountIds, 'by-tag' = use accountTag"),
40
+ accountIds: zod_1.z.array(zod_1.z.string()).optional().describe('Specific account IDs to iterate over'),
41
+ accountTag: zod_1.z.string().optional().describe('Tag to select accounts by'),
42
+ iterationMode: zod_1.z.enum(['sequential', 'random']).describe('How to iterate through accounts'),
43
+ });
44
+ function getNodeTypeInfo(params) {
45
+ const node = (0, graph_controller_1.getNodeByType)(params.nodeType);
46
+ if (!node) {
47
+ return { ok: false, error: `Unknown node type: ${params.nodeType}` };
48
+ }
49
+ return { ok: true, data: node };
50
+ }
5
51
  exports.automationTools = [
52
+ // =========================================================================
53
+ // Node Discovery (local, no API call)
54
+ // =========================================================================
55
+ {
56
+ name: 'list_node_types',
57
+ description: 'List all available automation node types with descriptions, guides, input/output ports, and default configs. Use this to understand what nodes exist before building a workflow.',
58
+ schema: zod_1.z.object({}),
59
+ execute: async () => {
60
+ const nodes = (0, graph_controller_1.getAllNodes)().filter(n => !n.hidden);
61
+ return { ok: true, data: nodes };
62
+ },
63
+ },
64
+ {
65
+ name: 'get_node_type',
66
+ description: 'Get detailed info for a specific node type including ports, defaults, and guide. Use this to understand a node before adding it to a workflow.',
67
+ schema: zod_1.z.object({
68
+ nodeType: zod_1.z.string().describe('Node type ID (e.g. "llm", "auto-post", "media")'),
69
+ }),
70
+ execute: async (_client, params) => {
71
+ return getNodeTypeInfo(params);
72
+ },
73
+ },
74
+ // =========================================================================
75
+ // Template Management
76
+ // =========================================================================
77
+ {
78
+ name: 'create_automation',
79
+ description: 'Create a new automation template with a workflow definition. Use list_node_types and get_node_type to understand available nodes, then build the workflow.',
80
+ schema: zod_1.z.object({
81
+ name: zod_1.z.string().describe('Name for the automation'),
82
+ description: zod_1.z.string().optional().describe('Description of what this automation does'),
83
+ workflowDefinition: workflowDefinitionSchema,
84
+ skipValidation: zod_1.z.boolean().optional().describe('Skip validation (for saving drafts)'),
85
+ }),
86
+ execute: async (client, params) => {
87
+ return client.automations.createTemplate(params);
88
+ },
89
+ },
90
+ {
91
+ name: 'update_automation',
92
+ description: 'Update an existing automation template. Replaces the full workflow definition.',
93
+ schema: zod_1.z.object({
94
+ templateId: zod_1.z.string().describe('ID of the template to update'),
95
+ name: zod_1.z.string().describe('Updated name'),
96
+ description: zod_1.z.string().optional().describe('Updated description'),
97
+ workflowDefinition: workflowDefinitionSchema,
98
+ skipValidation: zod_1.z.boolean().optional().describe('Skip validation (for saving drafts)'),
99
+ }),
100
+ execute: async (client, params) => {
101
+ return client.automations.createTemplate(params);
102
+ },
103
+ },
104
+ {
105
+ name: 'validate_workflow',
106
+ description: 'Validate a workflow definition without creating it. Returns validation errors if any.',
107
+ schema: zod_1.z.object({
108
+ workflowDefinition: workflowDefinitionSchema,
109
+ }),
110
+ execute: async (client, params) => {
111
+ return client.automations.validateWorkflow(params);
112
+ },
113
+ },
114
+ {
115
+ name: 'delete_automation',
116
+ description: 'Delete an automation template permanently.',
117
+ schema: zod_1.z.object({
118
+ templateId: zod_1.z.string().describe('ID of the template to delete'),
119
+ }),
120
+ execute: async (client, params) => {
121
+ return client.automations.deleteTemplate(params);
122
+ },
123
+ },
6
124
  {
7
125
  name: 'list_automations',
8
126
  description: 'List all automation templates for the organization.',
@@ -21,6 +139,19 @@ exports.automationTools = [
21
139
  return client.automations.getTemplate(params);
22
140
  },
23
141
  },
142
+ {
143
+ name: 'export_automation',
144
+ description: 'Export an automation template for debugging. Returns the complete template definition with nodes and connections in a readable format.',
145
+ schema: zod_1.z.object({
146
+ templateId: zod_1.z.string().describe('Automation template ID'),
147
+ }),
148
+ execute: async (client, params) => {
149
+ return client.automations.exportTemplate(params);
150
+ },
151
+ },
152
+ // =========================================================================
153
+ // Execution
154
+ // =========================================================================
24
155
  {
25
156
  name: 'run_automation',
26
157
  description: 'Run an automation template. Optionally provide variable inputs and a tag.',
@@ -44,6 +175,39 @@ exports.automationTools = [
44
175
  return client.automations.getStatus(params);
45
176
  },
46
177
  },
178
+ {
179
+ name: 'get_automation_logs',
180
+ description: 'Get execution logs for an automation run. Returns log entries in chronological order.',
181
+ schema: zod_1.z.object({
182
+ runId: zod_1.z.string().describe('Run ID to get logs for'),
183
+ }),
184
+ execute: async (client, params) => {
185
+ return client.automations.getLogs(params);
186
+ },
187
+ },
188
+ {
189
+ name: 'stop_automation',
190
+ description: 'Stop a running automation. Marks the run and all pending executors as failed.',
191
+ schema: zod_1.z.object({
192
+ runId: zod_1.z.string().describe('Run ID to stop'),
193
+ }),
194
+ execute: async (client, params) => {
195
+ return client.automations.stop(params);
196
+ },
197
+ },
198
+ {
199
+ name: 'export_automation_run',
200
+ description: 'Export an automation run for debugging. Returns the complete run data including execution state, executor outputs, and edge values.',
201
+ schema: zod_1.z.object({
202
+ runId: zod_1.z.string().describe('Automation run ID'),
203
+ }),
204
+ execute: async (client, params) => {
205
+ return client.automations.exportRun(params);
206
+ },
207
+ },
208
+ // =========================================================================
209
+ // Run History & Review
210
+ // =========================================================================
47
211
  {
48
212
  name: 'list_automation_runs',
49
213
  description: 'List runs for a specific automation template.',
@@ -69,20 +233,46 @@ exports.automationTools = [
69
233
  },
70
234
  },
71
235
  {
72
- name: 'stop_automation',
73
- description: 'Stop a running automation. Marks the run and all pending executors as failed.',
236
+ name: 'update_automation_review',
237
+ description: 'Update the review status of an automation run. Approve or reject runs that require review.',
74
238
  schema: zod_1.z.object({
75
- runId: zod_1.z.string().describe('Run ID to stop'),
239
+ runId: zod_1.z.string().describe('Automation run ID'),
240
+ reviewStatus: zod_1.z.enum(['approved', 'rejected']).describe('New review status'),
76
241
  }),
77
242
  execute: async (client, params) => {
78
- return client.automations.stop(params);
243
+ return client.automations.updateReviewStatus(params);
244
+ },
245
+ },
246
+ {
247
+ name: 'delete_automation_run',
248
+ description: 'Delete an automation run permanently.',
249
+ schema: zod_1.z.object({
250
+ runId: zod_1.z.string().describe('Automation run ID to delete'),
251
+ }),
252
+ execute: async (client, params) => {
253
+ return client.automations.deleteRun(params);
254
+ },
255
+ },
256
+ // =========================================================================
257
+ // Scheduling & Recurrence
258
+ // =========================================================================
259
+ {
260
+ name: 'get_recurrence_status',
261
+ description: 'Get the recurrence scheduling status for an automation. Returns whether it is published, next/last run times.',
262
+ schema: zod_1.z.object({
263
+ templateId: zod_1.z.string().describe('Automation template ID'),
264
+ }),
265
+ execute: async (client, params) => {
266
+ return client.automations.getRecurrenceStatus(params);
79
267
  },
80
268
  },
81
269
  {
82
270
  name: 'publish_automation',
83
- description: 'Publish an automation to enable recurrence scheduling. The automation will run automatically based on its recurrence config.',
271
+ description: 'Publish an automation to enable recurrence scheduling. The automation will run automatically based on the provided schedule config.',
84
272
  schema: zod_1.z.object({
85
273
  templateId: zod_1.z.string().describe('Automation template ID to publish'),
274
+ scheduleConfig: scheduleConfigSchema,
275
+ accountIterationConfig: accountIterationConfigSchema.nullable().describe('Account iteration config, or null to disable account iteration'),
86
276
  }),
87
277
  execute: async (client, params) => {
88
278
  return client.automations.publish(params);
@@ -108,14 +298,4 @@ exports.automationTools = [
108
298
  return client.automations.runOnce(params);
109
299
  },
110
300
  },
111
- {
112
- name: 'get_automation_logs',
113
- description: 'Get execution logs for an automation run. Returns log entries in chronological order.',
114
- schema: zod_1.z.object({
115
- runId: zod_1.z.string().describe('Run ID to get logs for'),
116
- }),
117
- execute: async (client, params) => {
118
- return client.automations.getLogs(params);
119
- },
120
- },
121
301
  ];
@@ -53,6 +53,20 @@ exports.postTools = [
53
53
  return client.posts.createSlideshow(params);
54
54
  },
55
55
  },
56
+ {
57
+ name: 'create_draft_post',
58
+ description: 'Create a draft post without assigning it to an account. Use update_post to assign an account and schedule time later.',
59
+ schema: zod_1.z.object({
60
+ type: zod_1.z.enum(['video', 'slideshow']).describe('Post type'),
61
+ mediaUrls: zod_1.z.array(zod_1.z.string()).describe('Array of media URLs'),
62
+ caption: zod_1.z.string().optional().describe('Post caption'),
63
+ title: zod_1.z.string().optional().describe('Slideshow title'),
64
+ socialAudioId: zod_1.z.string().optional().describe('Social audio ID'),
65
+ }),
66
+ execute: async (client, params) => {
67
+ return client.posts.createDraft(params);
68
+ },
69
+ },
56
70
  {
57
71
  name: 'get_post_status',
58
72
  description: 'Get the status of a specific post. Returns the post URL when complete.',
@@ -65,7 +79,7 @@ exports.postTools = [
65
79
  },
66
80
  {
67
81
  name: 'update_post',
68
- description: 'Update a scheduled or failed post. Only posts with status "scheduled" or "failed" can be updated. Failed posts are reset to "scheduled".',
82
+ description: 'Update a draft, scheduled, or failed post. Draft posts transition to "scheduled" when given both accountId and postTime. Failed posts are reset to "scheduled".',
69
83
  schema: zod_1.z.object({
70
84
  postId: zod_1.z.string().describe('Post ID to update'),
71
85
  caption: zod_1.z.string().optional().describe('New caption'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc",
3
- "version": "4.5.76",
3
+ "version": "4.5.78",
4
4
  "description": "TypeScript/JavaScript client for the UGC Inc API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",