Skip to content

Commit 41e10a5

Browse files
authored
add output fields to other plugins (#103)
1 parent 8071aaf commit 41e10a5

File tree

9 files changed

+82
-82
lines changed

9 files changed

+82
-82
lines changed

CONTRIBUTING.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,11 @@ const myIntegrationPlugin: IntegrationPlugin = {
457457
category: "My Integration",
458458
stepFunction: "sendMessageStep",
459459
stepImportPath: "send-message",
460+
// Output fields for template autocomplete (what this action returns)
461+
outputFields: [
462+
{ field: "id", description: "Message ID" },
463+
{ field: "url", description: "Message URL" },
464+
],
460465
// Declarative config fields (not React components)
461466
configFields: [
462467
{
@@ -490,7 +495,22 @@ export default myIntegrationPlugin;
490495
2. **envVar**: Maps formField to environment variable (auto-generates credential mapping)
491496
3. **getTestFunction**: Lazy-loads test function to avoid bundling server code
492497
4. **slug**: Action identifier (full ID becomes `my-integration/send-message`)
493-
5. **configFields**: Declarative array defining UI fields (not React components)
498+
5. **outputFields**: Defines what fields the action returns (for template autocomplete)
499+
6. **configFields**: Declarative array defining UI fields (not React components)
500+
501+
**Output Fields:**
502+
503+
The `outputFields` array defines what fields the action returns, enabling autocomplete when referencing this action's output in subsequent steps:
504+
505+
```typescript
506+
outputFields: [
507+
{ field: "id", description: "Message ID" },
508+
{ field: "url", description: "Message URL" },
509+
{ field: "items", description: "Array of items" }, // For arrays, just use the array name
510+
],
511+
```
512+
513+
These fields appear in the template variable dropdown when users type `@` in a template input field. The `field` should match the property names in your step's return type.
494514

495515
**Supported configField types:**
496516
- `template-input`: Single-line input with `{{variable}}` support
@@ -594,6 +614,10 @@ actions: [
594614
category: "My Integration",
595615
stepFunction: "sendMessageStep",
596616
stepImportPath: "send-message",
617+
outputFields: [
618+
{ field: "id", description: "Message ID" },
619+
{ field: "timestamp", description: "Send timestamp" },
620+
],
597621
configFields: [
598622
{ key: "message", label: "Message", type: "template-input" },
599623
{ key: "channel", label: "Channel", type: "text" },
@@ -606,6 +630,10 @@ actions: [
606630
category: "My Integration",
607631
stepFunction: "createRecordStep",
608632
stepImportPath: "create-record",
633+
outputFields: [
634+
{ field: "id", description: "Record ID" },
635+
{ field: "url", description: "Record URL" },
636+
],
609637
configFields: [
610638
{ key: "title", label: "Title", type: "template-input", required: true },
611639
{ key: "description", label: "Description", type: "template-textarea" },

components/ui/template-autocomplete.tsx

Lines changed: 14 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -111,44 +111,16 @@ const isActionType = (
111111
const getCommonFields = (node: WorkflowNode) => {
112112
const actionType = node.data.config?.actionType as string | undefined;
113113

114-
// First, check if the plugin defines output fields
115-
if (actionType) {
116-
const action = findActionById(actionType);
117-
if (action?.outputFields && action.outputFields.length > 0) {
118-
return action.outputFields;
119-
}
120-
}
121-
122-
// Legacy hardcoded fields for backwards compatibility
123-
if (isActionType(actionType, "Find Issues", "linear/find-issues")) {
124-
return [
125-
{ field: "issues", description: "Array of issues found" },
126-
{ field: "count", description: "Number of issues" },
127-
];
128-
}
129-
if (isActionType(actionType, "Send Email", "resend/send-email")) {
130-
return [
131-
{ field: "id", description: "Email ID" },
132-
{ field: "status", description: "Send status" },
133-
];
134-
}
135-
if (isActionType(actionType, "Create Ticket", "linear/create-ticket")) {
136-
return [
137-
{ field: "id", description: "Ticket ID" },
138-
{ field: "url", description: "Ticket URL" },
139-
{ field: "number", description: "Ticket number" },
140-
];
141-
}
114+
// Special handling for dynamic outputs (system actions and schema-based)
142115
if (actionType === "HTTP Request") {
143116
return [
144117
{ field: "data", description: "Response data" },
145118
{ field: "status", description: "HTTP status code" },
146119
];
147120
}
121+
148122
if (actionType === "Database Query") {
149123
const dbSchema = node.data.config?.dbSchema as string | undefined;
150-
151-
// If schema is defined, show schema fields
152124
if (dbSchema) {
153125
try {
154126
const schema = JSON.parse(dbSchema) as SchemaField[];
@@ -159,18 +131,17 @@ const getCommonFields = (node: WorkflowNode) => {
159131
// If schema parsing fails, fall through to default fields
160132
}
161133
}
162-
163-
// Default fields when no schema
164134
return [
165135
{ field: "rows", description: "Query result rows" },
166136
{ field: "count", description: "Number of rows" },
167137
];
168138
}
139+
140+
// AI Gateway generate-text has dynamic output based on format/schema
169141
if (isActionType(actionType, "Generate Text", "ai-gateway/generate-text")) {
170142
const aiFormat = node.data.config?.aiFormat as string | undefined;
171143
const aiSchema = node.data.config?.aiSchema as string | undefined;
172144

173-
// If format is object and schema is defined, show schema fields under "object" prefix
174145
if (aiFormat === "object" && aiSchema) {
175146
try {
176147
const schema = JSON.parse(aiSchema) as SchemaField[];
@@ -181,59 +152,22 @@ const getCommonFields = (node: WorkflowNode) => {
181152
// If schema parsing fails, fall through to default fields
182153
}
183154
}
184-
185-
// Default fields for text format or when no schema
186-
return [
187-
{ field: "text", description: "Generated text" },
188-
{ field: "model", description: "Model used" },
189-
];
155+
return [{ field: "text", description: "Generated text" }];
190156
}
191-
if (isActionType(actionType, "Generate Image", "ai-gateway/generate-image")) {
192-
return [
193-
{ field: "base64", description: "Base64 image data" },
194-
{ field: "model", description: "Model used" },
195-
];
196-
}
197-
if (
198-
isActionType(actionType, "Scrape", "Scrape URL", "firecrawl/scrape")
199-
) {
200-
return [
201-
{ field: "markdown", description: "Scraped content as markdown" },
202-
{ field: "metadata.url", description: "Page URL" },
203-
{ field: "metadata.title", description: "Page title" },
204-
{ field: "metadata.description", description: "Page description" },
205-
{ field: "metadata.language", description: "Page language" },
206-
{ field: "metadata.favicon", description: "Favicon URL" },
207-
];
208-
}
209-
if (isActionType(actionType, "Search", "Search Web", "firecrawl/search")) {
210-
return [{ field: "web", description: "Array of search results" }];
211-
}
212-
if (isActionType(actionType, "Create Chat", "v0/create-chat")) {
213-
return [
214-
{ field: "chatId", description: "v0 chat ID" },
215-
{ field: "url", description: "v0 chat URL" },
216-
{ field: "demoUrl", description: "Demo preview URL" },
217-
];
218-
}
219-
if (isActionType(actionType, "Send Message", "v0/send-message")) {
220-
return [
221-
{ field: "chatId", description: "v0 chat ID" },
222-
{ field: "demoUrl", description: "Demo preview URL" },
223-
];
224-
}
225-
if (isActionType(actionType, "Send Slack Message", "slack/send-message")) {
226-
return [
227-
{ field: "ok", description: "Success status" },
228-
{ field: "ts", description: "Message timestamp" },
229-
{ field: "channel", description: "Channel ID" },
230-
];
157+
158+
// Check if the plugin defines output fields
159+
if (actionType) {
160+
const action = findActionById(actionType);
161+
if (action?.outputFields && action.outputFields.length > 0) {
162+
return action.outputFields;
163+
}
231164
}
165+
166+
// Trigger fields
232167
if (node.data.type === "trigger") {
233168
const triggerType = node.data.config?.triggerType as string | undefined;
234169
const webhookSchema = node.data.config?.webhookSchema as string | undefined;
235170

236-
// If it's a webhook trigger with a schema, show schema fields
237171
if (triggerType === "Webhook" && webhookSchema) {
238172
try {
239173
const schema = JSON.parse(webhookSchema) as SchemaField[];
@@ -245,7 +179,6 @@ const getCommonFields = (node: WorkflowNode) => {
245179
}
246180
}
247181

248-
// Default trigger fields
249182
return [
250183
{ field: "triggered", description: "Trigger status" },
251184
{ field: "timestamp", description: "Trigger timestamp" },

plugins/ai-gateway/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ const aiGatewayPlugin: IntegrationPlugin = {
112112
category: "AI Gateway",
113113
stepFunction: "generateImageStep",
114114
stepImportPath: "generate-image",
115+
outputFields: [{ field: "base64", description: "Base64-encoded image data" }],
115116
configFields: [
116117
{
117118
key: "imageModel",

plugins/firecrawl/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ const firecrawlPlugin: IntegrationPlugin = {
4040
category: "Firecrawl",
4141
stepFunction: "firecrawlScrapeStep",
4242
stepImportPath: "scrape",
43+
outputFields: [
44+
{ field: "markdown", description: "Scraped content as markdown" },
45+
{ field: "metadata", description: "Page metadata object" },
46+
],
4347
configFields: [
4448
{
4549
key: "url",
@@ -58,6 +62,7 @@ const firecrawlPlugin: IntegrationPlugin = {
5862
category: "Firecrawl",
5963
stepFunction: "firecrawlSearchStep",
6064
stepImportPath: "search",
65+
outputFields: [{ field: "data", description: "Array of search results" }],
6166
configFields: [
6267
{
6368
key: "query",

plugins/linear/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ const linearPlugin: IntegrationPlugin = {
5050
category: "Linear",
5151
stepFunction: "createTicketStep",
5252
stepImportPath: "create-ticket",
53+
outputFields: [
54+
{ field: "id", description: "Ticket ID" },
55+
{ field: "url", description: "Ticket URL" },
56+
{ field: "title", description: "Ticket title" },
57+
],
5358
configFields: [
5459
{
5560
key: "ticketTitle",
@@ -90,6 +95,10 @@ const linearPlugin: IntegrationPlugin = {
9095
category: "Linear",
9196
stepFunction: "findIssuesStep",
9297
stepImportPath: "find-issues",
98+
outputFields: [
99+
{ field: "issues", description: "Array of issues found" },
100+
{ field: "count", description: "Number of issues" },
101+
],
93102
configFields: [
94103
{
95104
key: "linearAssigneeId",

plugins/resend/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const resendPlugin: IntegrationPlugin = {
4949
category: "Resend",
5050
stepFunction: "sendEmailStep",
5151
stepImportPath: "send-email",
52+
outputFields: [{ field: "id", description: "Email ID" }],
5253
configFields: [
5354
{
5455
key: "emailFrom",

plugins/slack/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ const slackPlugin: IntegrationPlugin = {
4040
category: "Slack",
4141
stepFunction: "sendSlackMessageStep",
4242
stepImportPath: "send-slack-message",
43+
outputFields: [
44+
{ field: "ts", description: "Message timestamp" },
45+
{ field: "channel", description: "Channel ID" },
46+
],
4347
configFields: [
4448
{
4549
key: "slackChannel",

plugins/superagent/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ const superagentPlugin: IntegrationPlugin = {
4141
category: "Superagent",
4242
stepFunction: "superagentGuardStep",
4343
stepImportPath: "guard",
44+
outputFields: [
45+
{ field: "classification", description: "Threat classification" },
46+
{ field: "violationTypes", description: "Array of violation types" },
47+
{ field: "cweCodes", description: "Array of CWE codes" },
48+
{ field: "reasoning", description: "Analysis reasoning" },
49+
],
4450
configFields: [
4551
{
4652
key: "text",
@@ -61,6 +67,10 @@ const superagentPlugin: IntegrationPlugin = {
6167
category: "Superagent",
6268
stepFunction: "superagentRedactStep",
6369
stepImportPath: "redact",
70+
outputFields: [
71+
{ field: "redactedText", description: "Text with PII redacted" },
72+
{ field: "reasoning", description: "Redaction reasoning" },
73+
],
6474
configFields: [
6575
{
6676
key: "text",

plugins/v0/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ const v0Plugin: IntegrationPlugin = {
4242
category: "v0",
4343
stepFunction: "createChatStep",
4444
stepImportPath: "create-chat",
45+
outputFields: [
46+
{ field: "chatId", description: "v0 chat ID" },
47+
{ field: "url", description: "v0 chat URL" },
48+
{ field: "demoUrl", description: "Demo preview URL" },
49+
],
4550
configFields: [
4651
{
4752
key: "message",
@@ -68,6 +73,10 @@ const v0Plugin: IntegrationPlugin = {
6873
category: "v0",
6974
stepFunction: "sendMessageStep",
7075
stepImportPath: "send-message",
76+
outputFields: [
77+
{ field: "chatId", description: "v0 chat ID" },
78+
{ field: "demoUrl", description: "Demo preview URL" },
79+
],
7180
configFields: [
7281
{
7382
key: "chatId",

0 commit comments

Comments
 (0)