Skip to content

Conversation

@sergio-eliot-rodriguez
Copy link
Collaborator

@sergio-eliot-rodriguez sergio-eliot-rodriguez commented Oct 22, 2025

WHY

Resolves #17749, components for the 302.AI app

Summary by CodeRabbit

Release Notes

  • New Features

    • Integrated 302.AI capabilities including chat completions, function-calling chat, item classification, text summarization, and text embeddings.
    • Added TicketSauce integration for accessing events, orders, and ticket information.
  • Chores

    • Updated component version and dependencies.

@vercel
Copy link

vercel bot commented Oct 22, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
pipedream-docs-redirect-do-not-edit Ignored Ignored Oct 22, 2025 6:45am

@pipedream-component-development
Copy link
Collaborator

Thank you so much for submitting this! We've added it to our backlog to review, and our team has been notified.

@pipedream-component-development
Copy link
Collaborator

Thanks for submitting this PR! When we review PRs, we follow the Pipedream component guidelines. If you're not familiar, here's a quick checklist:

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 22, 2025

Walkthrough

Refactored the 302.AI app module to implement a unified API client pattern with model discovery and completion handling. Added five new action modules (chat, chat-with-functions, classify-items, summarize-text, create-embeddings) with shared configuration helpers, constants, and common base classes. Updated build configuration and dependency manifest.

Changes

Cohort / File(s) Change Summary
Pre-push Hook Configuration
.husky/pre-push
Changed pnpm install flag from -r to --frozen-lockfile to enforce locked dependency versions during pre-push checks.
App Module Refactoring
components/_302_ai/_302_ai.app.mjs
Replaced authKeys() method with new API client infrastructure: added _apiKey(), _baseApiUrl(), _makeRequest() for authenticated HTTP calls; introduced model discovery via listModels(); added _makeCompletion() wrapper to normalize completion responses; added createChatCompletion() method; added three new propDefinitions (modelId, chatCompletionModelId, embeddingsModelId) with dynamic model options; imported axios from @pipedream/platform.
Shared Helpers & Constants
components/_302_ai/actions/common/common.mjs, components/_302_ai/actions/common/common-helper.mjs, components/_302_ai/actions/common/constants.mjs
Added new common module exporting LLM parameter props and methods (_getCommonArgs(), _getChatArgs()) for building chat payloads with optional JSON schema support. Added common-helper base class implementing pluggable lifecycle hooks (systemInstructions(), userMessage(), formatOutput(), summary()) and a concrete run() method for chat completion flows. Added constants module defining response formats (TEXT, JSON_OBJECT, JSON_SCHEMA) and summarize lengths (word, sentence, paragraph, page).
Chat Actions
components/_302_ai/actions/chat-with-302-ai/chat-with-302-ai.mjs, components/_302_ai/actions/chat-using-functions/chat-using-functions.mjs
Added chat-with-302-ai action supporting system instructions, prior message history, and multiple response formats (Text, JSON Object, JSON Schema) with message parsing and normalization. Added chat-using-functions action enabling function invocation with tool choice, parallel calls, and JSON schema validation for structured outputs.
Specialized Actions
components/_302_ai/actions/classify-items/classify-items.mjs, components/_302_ai/actions/summarize-text/summarize-text.mjs, components/_302_ai/actions/create-embeddings/create-embeddings.mjs
Added classify-items action for categorizing items into predefined categories with JSON output validation. Added summarize-text action for text summarization with configurable length guidelines. Added create-embeddings action for generating vector embeddings with token-length validation and optional encoding format/dimensions support.
Documentation & Configuration
components/_302_ai/302_ai_components.mdc, components/_302_ai/package.json, components/memory.mdc, components/pipedream-context.mdc, components/ticketsauce/ticketsauce_components.mdc
Added comprehensive MDC template for 302.AI components with development guidelines, scaffolding examples, and API usage patterns. Bumped package version to 0.1.0 and added @pipedream/platform ^3.1.0 dependency. Added general development guidance documents for component patterns, memory context, and Pipedream conventions; added TicketSauce component template.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

The refactoring introduces a new API client pattern requiring careful review of authentication flow, request handling, and model discovery implementation. Five heterogeneous action modules with distinct logic paths (chat, classification, summarization, embeddings, function calling) each demand separate reasoning. Common helpers establish pluggable lifecycle hooks that multiple actions depend on, necessitating validation of contracts and data flow across the inheritance chain. The mix of app-level architectural changes, multiple new files, and JSON schema/response format handling increases overall complexity.

Poem

🐰 A rabbit hops through code so bright,
Five new actions taking flight!
Models discovered, functions called,
Embeddings wrapped, classifications sprawled!
The 302.AI warren now stands tall,
With patterns unified for all! 🌟

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning While the core 302.AI component implementations are well-scoped and aligned with issue #17749, the changeset includes several files that appear tangential to the primary objective: modifications to .husky/pre-push (build infrastructure), general documentation files (components/pipedream-context.mdc, mdc_pipedream-context.mdc, components/memory.mdc, mdc_memory.mdc), and a component template for an unrelated TicketSauce integration. These changes extend beyond the specific scope of adding 302.AI components as defined in the linked issue. Consider removing or segregating the out-of-scope changes into separate pull requests: move build tool modifications (.husky/pre-push) to a dedicated infrastructure PR, extract general development documentation files to a separate documentation PR, and separate the TicketSauce component template into its own PR focused on that integration. This will maintain a focused changeset specifically for the 302.AI components.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "302 ai new components" is concise and directly summarizes the primary objective of the pull request. It clearly identifies that the changeset adds new components for the 302.AI integration without being vague or generic. The title accurately reflects the main purpose and would help teammates quickly understand the change when scanning commit history.
Linked Issues Check ✅ Passed All five actions specified in linked issue #17749 are implemented in the changeset: chat-with-302-ai, chat-using-functions, summarize-text, classify-items, and create-embeddings. Each action module includes the required run() method and appropriate props, and the supporting _302_ai.app.mjs file provides necessary infrastructure including _makeRequest, model listing, and completion handling methods. The PR comprehensively addresses all coding requirements from the linked issue.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (18)
mdc_memory.mdc (1)

1-1: Avoid duplicating memory notes (dedupe with components/memory.mdc).

Keep a single source of truth to prevent drift; link the other to it.

components/memory.mdc (1)

1-1: Consolidate with mdc_memory.mdc.

Unify these two memory files; retain one and reference it from the other. Based on learnings.

components/_302_ai/302_ai_components.mdc (1)

8-12: Align examples with guidance and fix minor doc issues.

  • Replace the axios($) example with one that calls app._makeRequest to match your own rule.
  • Fix typos: “detaults” → “defaults”, “appropiate” → “appropriate”, “enpoint” → “endpoint”.
  • Complete the “create-completion” sentence (name at least one legacy model class or say “models that support the /completions endpoint”).

Apply this example tweak:

-    return await axios($, {
-      url: `https://api.302.ai/v1/models`,
-      headers: {
-        Authorization: `Bearer ${this._302_ai.$auth.api_key}`,
-      },
-    })
+    return await this._302_ai._makeRequest({
+      $,
+      path: "/models",
+      method: "GET",
+    })

Also applies to: 33-51, 76-79

components/_302_ai/actions/common/common-helper.mjs (1)

32-46: Solid shared run() flow.

Message construction + _getCommonArgs + app.createChatCompletion looks clean and reusable.

Optionally add a guard for empty system/user messages to fail fast with a clearer error.

components/_302_ai/_302_ai.app.mjs (1)

20-33: Model filter may exclude valid chat models; ensure options are inclusive. Also preserve convenience fields in _makeCompletion.

  • The regex filter (gpt|claude|gemini|llama|mistral|deepseek) risks hiding valid 302.AI chat models. Prefer capability flags from the API (if available) or don’t filter at all.
  • In _makeCompletion, spread data first so generated_text/generated_message aren’t overwritten by the API payload.
-        return models
-          .filter((model) => model.id.match(/gpt|claude|gemini|llama|mistral|deepseek/gi))
-          .map((model) => ({
+        return models
+          // TODO: Prefer an official capability field (e.g., model.capabilities.includes("chat"))
+          // For now, don't filter to avoid excluding valid models.
+          .map((model) => ({
             label: model.id,
             value: model.id,
           }));
@@
-      return {
-        generated_text,
-        generated_message,
-        ...data,
-      };
+      return {
+        ...data,
+        generated_text,
+        generated_message,
+      };

If 302.AI exposes a “type/capabilities” field on models, we can refine the filter accordingly.

Also applies to: 101-105

components/_302_ai/actions/common/constants.mjs (1)

1-14: LGTM, consider freezing to avoid accidental mutation.

The constants look good. To harden at runtime, freeze them to prevent accidental mutation.

 const CHAT_RESPONSE_FORMAT = {
@@
 };
 
 const SUMMARIZE_LENGTH = [
@@
 ];
 
+Object.freeze(CHAT_RESPONSE_FORMAT);
+Object.freeze(SUMMARIZE_LENGTH);

Also applies to: 16-26

components/_302_ai/actions/chat-using-functions/chat-using-functions.mjs (5)

63-73: Enable selecting a specific function cleanly in the UI.

Current prop only allows "auto" / "required" options, but code and docs imply arbitrary function names. Add an explicit "function" option and a companion input for the function name.

   toolChoice: {
     type: "string",
     label: "Tool Choice",
-    description: "- **auto**: The model decides whether and how many functions to call.\n- **required**: The model must call one or more functions.\n- **function_name**: Enter a custom function name to force the model to call this specific function.",
+    description: "- **auto**: The model decides whether and how many functions to call.\n- **required**: The model must call one or more functions.\n- **function**: Force the model to call a specific function name you provide below.",
     optional: true,
     default: "auto",
     options: [
       "auto",
       "required",
+      "function",
     ],
   },
@@
-  additionalProps() {
+  additionalProps() {
     const props = {};
 
+    if (this.toolChoice === "function") {
+      props.toolChoiceFunctionName = {
+        type: "string",
+        label: "Function Name",
+        description: "Exact name of the function to force the model to call.",
+      };
+    }
+
     if (this.responseFormat === constants.CHAT_RESPONSE_FORMAT.JSON_SCHEMA.value) {
       props.jsonSchema = {
         type: "string",
         label: "JSON Schema",
         description: "Define the schema that the model's output must adhere to.",
       };
     }
 
     return props;
   },
@@
-      } else {
-        data.tool_choice = {
-          type: "function",
-          name: this.toolChoice,
-        };
-      }
+      } else if (this.toolChoiceFunctionName) {
+        data.tool_choice = {
+          type: "function",
+          function: { name: this.toolChoiceFunctionName },
+        };
+      }

Please confirm whether the Pipedream UI allows free-form values when options are present. If not, this change is required to unlock forced function calling.

Also applies to: 116-128, 174-183


74-90: Use a boolean for Parallel Function Calling.

The current string "1"/"0" pattern is brittle. Prefer a boolean prop and map directly.

-  parallelToolCalls: {
-    type: "string",
-    label: "Parallel Function Calling",
-    description: "Allow or prevent the model to call multiple functions in a single turn",
-    optional: true,
-    default: "1",
-    options: [
-      {
-        label: "Enabled",
-        value: "1",
-      },
-      {
-        label: "Disabled",
-        value: "0",
-      },
-    ],
-  },
+  parallelToolCalls: {
+    type: "boolean",
+    label: "Parallel Function Calling",
+    description: "Allow the model to call multiple functions in a single turn",
+    optional: true,
+    default: true,
+  },
@@
-      parallel_tool_calls: parseInt(this.parallelToolCalls) === 1,
+      parallel_tool_calls: !!this.parallelToolCalls,

Also applies to: 147-149


15-26: Reuse common props and numeric parsing; avoid partial duplication.

This action reimplements maxTokens/temperature and omits other supported params (topP, n, stop, penalties, user). Import common and spread ...common.props; build data from ...this._getCommonArgs() instead of ad-hoc parsing.

-import _302_ai from "../../_302_ai.app.mjs";
-import constants from "../common/constants.mjs";
+import _302_ai from "../../_302_ai.app.mjs";
+import common from "../common/common.mjs";
+import constants from "../common/constants.mjs";
@@
 export default {
-  name: "Chat using Functions",
+  ...common,
+  name: "Chat using Functions",
@@
   props: {
     _302_ai,
     modelId: {
@@
-    maxTokens: {
-      label: "Max Tokens",
-      description: "The maximum number of tokens to generate in the completion.",
-      type: "string",
-      optional: true,
-    },
-    temperature: {
-      label: "Temperature",
-      description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.",
-      type: "string",
-      optional: true,
-    },
+    ...common.props,
@@
-    const data = {
-      model: this.modelId,
+    const data = {
+      ...this._getCommonArgs(),
       messages,
       parallel_tool_calls: !!this.parallelToolCalls,
       tools: [],
     };
@@
-    if (this.maxTokens) {
-      data.max_tokens = parseInt(this.maxTokens);
-    }
-
-    if (this.temperature) {
-      data.temperature = parseFloat(this.temperature);
-    }
+    // common props already handled by _getCommonArgs()

Also applies to: 91-102, 144-154, 155-157


159-166: Validate tools schema early.

Guard against malformed tool definitions to fail fast with actionable errors.

     if (typeof functions === "string") {
       try {
         functions = JSON.parse(functions);
       } catch (error) {
         throw new Error("Invalid JSON format in the provided Functions Schema");
       }
     }
 
-    if (Array.isArray(functions)) {
-      data.tools.push(...functions);
-    } else {
-      data.tools.push(functions);
-    }
+    const toArray = Array.isArray(functions) ? functions : [functions];
+    for (const t of toArray) {
+      if (!t || t.type !== "function" || !t.function || !t.function.name) {
+        throw new Error("Each tool must have type \"function\" and a function { name, ... }");
+      }
+    }
+    data.tools.push(...toArray);

Also applies to: 168-173


103-114: Consider supporting JSON Object response format here too.

Unless 302.AI forbids it with tools, you can expose json_object alongside text and json_schema.

   responseFormat: {
     type: "string",
     label: "Response Format",
     description: "- **Text**: Returns unstructured text output.\n- **JSON Schema**: Enables you to define a specific structure for the model's output using a JSON schema.",
     options: [
-      constants.CHAT_RESPONSE_FORMAT.TEXT.value,
-      constants.CHAT_RESPONSE_FORMAT.JSON_SCHEMA.value,
+      constants.CHAT_RESPONSE_FORMAT.TEXT.value,
+      constants.CHAT_RESPONSE_FORMAT.JSON_OBJECT.value,
+      constants.CHAT_RESPONSE_FORMAT.JSON_SCHEMA.value,
     ],
     default: constants.CHAT_RESPONSE_FORMAT.TEXT.value,
     optional: true,
     reloadProps: true,
   },

Also applies to: 185-197

components/_302_ai/actions/summarize-text/summarize-text.mjs (2)

41-43: Handle empty/whitespace input early.

Guard against accidental empty text to avoid spending tokens and returning empty summaries.

   userMessage() {
-      return `Summarize the following text${this.maxLength()}: ${this.text}`;
+      const body = (this.text || "").trim();
+      if (!body) {
+        throw new Error("Text is required to summarize.");
+      }
+      return `Summarize the following text${this.maxLength()}: ${body}`;
   },

44-62: Safer handling when no choices are returned.

Add a defensive check to avoid accessing choices[0] when the API returns no choices.

   formatOutput({
     messages, response,
   }) {
     if (!messages || !response) {
       throw new Error("Invalid API output, please reach out to https://pipedream.com/support");
     }
+    if (!Array.isArray(response.choices) || response.choices.length === 0) {
+      throw new Error("No choices returned by the API.");
+    }
     const output = {
       messages,
     };
     const n = this.n
       ? parseInt(this.n)
       : 1;
     if (n > 1) {
       output.summaries = response.choices?.map(({ message }) => message.content);
     } else {
       output.summary = response.choices?.[0]?.message?.content;
     }
     return output;
   },
components/_302_ai/actions/chat-with-302-ai/chat-with-302-ai.mjs (4)

37-42: Improve messages prop UX to accept structured messages too.

type: "string[]" restricts object inputs, but the code accepts objects. Either add an alternate object[] prop or switch to object[] and accept JSON strings inside.

   messages: {
     label: "Prior Message History",
-    type: "string[]",
+    type: "string[]",
     description: "Because the models have no memory of past chat requests, all relevant information must be supplied via the conversation. You can provide an array of messages from prior conversations here. **Formats supported:** 1) Plain strings with role prefix (e.g., `User: Hello` or `Assistant: Hi there`), 2) JSON strings (e.g., `{\"role\": \"user\", \"content\": \"Hello\"}`), 3) Plain strings without prefix (defaults to user role).",
     optional: true,
   },

Optionally add:

+additionalProps() {
+  const props = {};
+  // existing JSON Schema block...
+  props.messagesObjects = {
+    type: "object[]",
+    label: "Prior Message History (objects)",
+    description: "Alternative to the string array: supply structured messages like { role, content }.",
+    optional: true,
+  };
+  return props;
+},

107-114: Always include systemInstructions if provided (unless already present).

Currently, system instructions are skipped when messages are provided. Include them if none of the provided messages has role system.

-    } else {
-      if (this.systemInstructions) {
-        messages.push({
-          "role": "system",
-          "content": this.systemInstructions,
-        });
-      }
-    }
+    }
+    if (this.systemInstructions) {
+      const hasSystem = messages.some((m) => m?.role?.toLowerCase?.() === "system");
+      if (!hasSystem) {
+        messages.unshift({
+          role: "system",
+          content: this.systemInstructions,
+        });
+      }
+    }

Also applies to: 65-106


150-158: Parse assistant content for JSON Object responses too.

Apply JSON parsing when response format is json_object as well as json_schema.

-    if (this.responseFormat === constants.CHAT_RESPONSE_FORMAT.JSON_SCHEMA.value) {
+    if (
+      this.responseFormat === constants.CHAT_RESPONSE_FORMAT.JSON_SCHEMA.value ||
+      this.responseFormat === constants.CHAT_RESPONSE_FORMAT.JSON_OBJECT.value
+    ) {
       for (const choice of response.choices) {
         try {
           choice.message.content = JSON.parse(choice.message.content);
         } catch {
-          console.log(`Unable to parse JSON: ${choice.message.content}`);
+          console.debug?.("Unable to parse JSON content for choice", { content: choice.message.content });
         }
       }
     }

164-168: Guard against missing assistant message on concat.

Avoid appending undefined when no choices are returned.

   return {
     original_messages: messages,
-    original_messages_with_assistant_response: messages.concat(response.choices[0]?.message),
+    original_messages_with_assistant_response: response.choices?.[0]?.message
+      ? messages.concat(response.choices[0].message)
+      : messages.slice(),
     ...response,
   };
components/_302_ai/actions/common/common.mjs (1)

81-81: Drop unnecessary async from _getChatArgs.

No awaits inside; make it synchronous.

-    async _getChatArgs() {
+    _getChatArgs() {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 27448c9 and 67439e0.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • .husky/pre-push (1 hunks)
  • components/_302_ai/302_ai_components.mdc (1 hunks)
  • components/_302_ai/_302_ai.app.mjs (1 hunks)
  • components/_302_ai/actions/chat-using-functions/chat-using-functions.mjs (1 hunks)
  • components/_302_ai/actions/chat-with-302-ai/chat-with-302-ai.mjs (1 hunks)
  • components/_302_ai/actions/classify-items/classify-items.mjs (1 hunks)
  • components/_302_ai/actions/common/common-helper.mjs (1 hunks)
  • components/_302_ai/actions/common/common.mjs (1 hunks)
  • components/_302_ai/actions/common/constants.mjs (1 hunks)
  • components/_302_ai/actions/create-embeddings/create-embeddings.mjs (1 hunks)
  • components/_302_ai/actions/summarize-text/summarize-text.mjs (1 hunks)
  • components/_302_ai/package.json (2 hunks)
  • components/memory.mdc (1 hunks)
  • components/pipedream-context.mdc (1 hunks)
  • components/ticketsauce/ticketsauce_components.mdc (1 hunks)
  • mdc_memory.mdc (1 hunks)
  • mdc_pipedream-context.mdc (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-15T22:01:11.472Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#18362
File: components/leonardo_ai/actions/generate-image/generate-image.mjs:103-105
Timestamp: 2025-09-15T22:01:11.472Z
Learning: In Pipedream components, pipedream/platform's axios implementation automatically excludes undefined values from HTTP requests, so there's no need to manually check for truthiness before including properties in request payloads.

Applied to files:

  • mdc_memory.mdc
  • components/memory.mdc
🧬 Code graph analysis (8)
components/_302_ai/actions/classify-items/classify-items.mjs (3)
components/_302_ai/actions/common/common-helper.mjs (2)
  • messages (33-42)
  • response (48-51)
components/_302_ai/actions/common/common.mjs (1)
  • messages (82-82)
components/_302_ai/actions/summarize-text/summarize-text.mjs (2)
  • output (50-52)
  • n (53-55)
components/_302_ai/actions/create-embeddings/create-embeddings.mjs (1)
components/_302_ai/_302_ai.app.mjs (3)
  • data (82-86)
  • data (91-91)
  • data (97-97)
components/_302_ai/actions/chat-with-302-ai/chat-with-302-ai.mjs (4)
components/_302_ai/actions/chat-using-functions/chat-using-functions.mjs (4)
  • props (117-117)
  • messages (130-130)
  • data (144-149)
  • response (199-202)
components/_302_ai/actions/common/common-helper.mjs (3)
  • messages (33-42)
  • data (43-47)
  • response (48-51)
components/_302_ai/actions/common/common.mjs (2)
  • messages (82-82)
  • responseFormat (96-112)
components/_302_ai/_302_ai.app.mjs (3)
  • data (82-86)
  • data (91-91)
  • data (97-97)
components/_302_ai/actions/summarize-text/summarize-text.mjs (5)
components/_302_ai/actions/chat-using-functions/chat-using-functions.mjs (2)
  • messages (130-130)
  • response (199-202)
components/_302_ai/actions/chat-with-302-ai/chat-with-302-ai.mjs (2)
  • messages (65-65)
  • response (145-148)
components/_302_ai/actions/common/common-helper.mjs (2)
  • messages (33-42)
  • response (48-51)
components/_302_ai/actions/common/common.mjs (1)
  • messages (82-82)
components/_302_ai/actions/classify-items/classify-items.mjs (2)
  • output (67-69)
  • n (70-72)
components/_302_ai/_302_ai.app.mjs (5)
components/_302_ai/actions/common/common.mjs (1)
  • args (56-78)
components/_302_ai/actions/chat-using-functions/chat-using-functions.mjs (1)
  • data (144-149)
components/_302_ai/actions/chat-with-302-ai/chat-with-302-ai.mjs (1)
  • data (139-143)
components/_302_ai/actions/common/common-helper.mjs (1)
  • data (43-47)
components/_302_ai/actions/create-embeddings/create-embeddings.mjs (1)
  • data (63-66)
components/_302_ai/actions/common/common-helper.mjs (4)
components/_302_ai/actions/chat-using-functions/chat-using-functions.mjs (3)
  • messages (130-130)
  • data (144-149)
  • response (199-202)
components/_302_ai/actions/chat-with-302-ai/chat-with-302-ai.mjs (3)
  • messages (65-65)
  • data (139-143)
  • response (145-148)
components/_302_ai/actions/common/common.mjs (1)
  • messages (82-82)
components/_302_ai/_302_ai.app.mjs (3)
  • data (82-86)
  • data (91-91)
  • data (97-97)
components/_302_ai/actions/chat-using-functions/chat-using-functions.mjs (4)
components/_302_ai/actions/chat-with-302-ai/chat-with-302-ai.mjs (4)
  • props (54-54)
  • messages (65-65)
  • data (139-143)
  • response (145-148)
components/_302_ai/actions/common/common-helper.mjs (3)
  • messages (33-42)
  • data (43-47)
  • response (48-51)
components/_302_ai/actions/common/common.mjs (1)
  • messages (82-82)
components/_302_ai/_302_ai.app.mjs (3)
  • data (82-86)
  • data (91-91)
  • data (97-97)
components/_302_ai/actions/common/common.mjs (3)
components/_302_ai/actions/chat-using-functions/chat-using-functions.mjs (1)
  • messages (130-130)
components/_302_ai/actions/chat-with-302-ai/chat-with-302-ai.mjs (2)
  • messages (65-65)
  • responseFormat (121-137)
components/_302_ai/actions/common/common-helper.mjs (1)
  • messages (33-42)
🔇 Additional comments (6)
components/pipedream-context.mdc (1)

1-32: Clarify the purpose and completeness of this documentation file.

This file appears to be a guidance/context document for Pipedream component development, but its role in this PR is unclear. Additionally, the "Sources (Webhooks)" section (starting at line 27) appears truncated—only the section header and a bullet list begin are shown, without the full guidance content.

Before merging, clarify:

  1. Is this file intended to be part of the PR deliverables, or is it just contextual guidance for reviewers?
  2. If it's a deliverable, ensure the "Sources (Webhooks)" section and any other sections are complete.
  3. Verify that this documentation aligns with and complements the 302.AI components being added (chat, chat-with-functions, classify-items, summarize-text, create-embeddings).

Note: The guidance itself aligns well with the review checklist provided in the PR comments (e.g., component key naming, reusable propDefinitions, ConfigurationError usage). However, this document should not duplicate or replace component-specific documentation or inline code comments.

components/ticketsauce/ticketsauce_components.mdc (1)

1-52: Scope mismatch claim cannot be verified — but implementation is incomplete.

The original review comment claims this file should not be in a PR for "302 ai new components." However, I cannot verify the PR scope since metadata lookup failed. That said, both 302.AI and TicketSauce component directories coexist in the repository, suggesting they may be intended together.

The actual concern is that the TicketSauce implementation is incomplete:

  • ticketsauce.app.mjs is a stub with no methods (compare to _302_ai.app.mjs which has full propDefinitions, _makeRequest, and helpers)
  • No action implementation files exist in components/ticketsauce/actions/ (312.AI has 8 action files; TicketSauce has zero)
  • package.json specifies version "0.0.1", but the specification file itself (lines 11–31) mandates "0.1.0" per guidelines
  • The .mdc specification exists with detailed action definitions (lines 54–748), but corresponding .mjs implementations are missing

The specification file (ticketsauce_components.mdc) is well-structured and follows the same pattern as 302.AI components, but the JavaScript/TypeScript implementation has not been generated from it.

mdc_pipedream-context.mdc (3)

1-3: Clarify the purpose and location of this documentation file.

This is a Markdown documentation/context file rather than component code. The YAML front matter (alwaysApply: true) suggests it's metadata for a specific development environment or tool. Please clarify:

  • Whether this file should be committed to the repository or if it's for IDE/context purposes only
  • If it should be committed, where it belongs in the repository structure (e.g., /docs, /CONTRIBUTING.md, etc.)
  • What system interprets the alwaysApply directive

1-32: Remove .mdc file from repository or consolidate into formal developer documentation.

The file mdc_pipedream-context.mdc is a multi-document context file (.mdc format designed for AI tools like Cursor), not standard developer documentation. While it's currently committed to the repository, it should not be:

  1. Root-level .mdc files are not documentation: .mdc format is for AI model context, not repository-facing guides. If this content is valuable for developers, convert to .md format.

  2. Content should consolidate with existing docs: The component guidelines (file structure, naming conventions, code patterns, webhook sources) overlap with existing documentation. Integrate into CONTRIBUTING.md or create a dedicated components/GUIDELINES.md if component-specific guidance warrants a separate file.

  3. Decision needed:

    • If meant as developer documentation: Convert to .md and merge into CONTRIBUTING.md under a "Pipedream Component Development" section
    • If meant as AI context only: Remove from repository (use .gitignore for .mdc files at root)

21-22: No changes needed — the documented axios behavior is verified.

The guidance stating that @pipedream/platform's axios "filters out undefined values, no need to check parameters individually before including them" is accurate. This behavior is confirmed by verified learning from PR #18362 (Sept 15, 2025), where the same pattern was validated in the leonardo_ai component. The repository uses version ^1.5.1, which supports this automatic filtering.

components/_302_ai/package.json (1)

15-17: Verify SDK version fragmentation is acceptable.

The monorepo has mixed @pipedream/platform versions (ranging from ^0.9.0 to ^3.1.0). The proposed ^3.1.0 aligns with current usage in several components but does not address existing version fragmentation. Confirm this is intentional or consider a standardization effort.

. "$(dirname "$0")/_/husky.sh"

npx pnpm install -r
npx pnpm install --frozen-lockfile
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify if -r flag is needed and confirm intended behavior change.

This change replaces what was presumably -r (recursive/workspace install) with --frozen-lockfile (strict lockfile validation). These flags serve different purposes and the change has significant implications:

  1. Monorepo concern: If this is a monorepo with workspaces, removing -r means dependencies won't be installed across all workspace projects, potentially breaking the build.

  2. Behavior change: --frozen-lockfile fails immediately if package.json and pnpm-lock.yaml are out of sync, which changes the workflow from "install → check → friendly reminder" to "fail fast." This makes lines 6-9 somewhat redundant since an out-of-sync lockfile would have already failed at line 4.

  3. Developer experience: Developers who legitimately add dependencies will now encounter a cryptic pnpm error instead of the helpful "please commit the file" message.

  4. PR scope: This change appears unrelated to the stated PR objectives (adding 302.AI components).

Run this script to verify if this is a monorepo requiring -r:


🏁 Script executed:

#!/bin/bash
# Check if this is a pnpm workspace/monorepo

# Look for pnpm-workspace.yaml
if [ -f "pnpm-workspace.yaml" ]; then
  echo "=== pnpm-workspace.yaml exists ==="
  cat pnpm-workspace.yaml
fi

# Check root package.json for workspace patterns
if [ -f "package.json" ]; then
  echo -e "\n=== Checking package.json for workspace indicators ==="
  jq -r 'if .workspaces then "workspaces field: " + (.workspaces | tostring) else "No workspaces field" end' package.json 2>/dev/null || echo "Could not parse package.json"
fi

# Count packages in components directory (common workspace pattern)
echo -e "\n=== Checking for multiple package.json files (workspace indicator) ==="
fd -t f '^package\.json$' | head -20

Length of output: 907


Now let me verify the original command and check for patterns of pnpm usage with -r in the codebase:

Now let me check for pnpm usage patterns in the codebase:

Let me check the pnpm configuration and view the full pre-push file:

Based on my verification:

Restore the -r flag: Change npx pnpm install --frozen-lockfile to npx pnpm install -r --frozen-lockfile

This change removes the -r (recursive) flag from the pnpm install command, which is critical for a monorepo. Inside a workspace, pnpm install installs all dependencies in all the projects, but this behavior can be disabled by setting the recursive-install setting to false. By removing the explicit -r flag, the pre-push hook now relies on implicit configuration defaults rather than explicit control.

The -r flag runs a command in every project of a workspace when used with install. The proper command for a monorepo should be npx pnpm install -r --frozen-lockfile to ensure both recursive installation across all workspace packages AND strict lockfile validation. These flags serve complementary purposes—not competing ones—and should both be present.

🤖 Prompt for AI Agents
.husky/pre-push around line 4: the pnpm install command dropped the recursive
flag, which can break monorepo installs when recursive-install is disabled;
change the command back to include -r so it reads npx pnpm install -r
--frozen-lockfile to force recursive installation across workspace packages
while still enforcing the frozen lockfile.

Comment on lines +174 to +183
if (this.toolChoice) {
if (this.toolChoice === "auto" || this.toolChoice === "required") {
data.tool_choice = this.toolChoice;
} else {
data.tool_choice = {
type: "function",
name: this.toolChoice,
};
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

tool_choice object shape is likely incorrect (will be rejected by API).

When forcing a specific function, the payload should nest the function name, e.g. { type: "function", function: { name: "..." } } (OpenAI-compatible schema).

-      } else {
-        data.tool_choice = {
-          type: "function",
-          name: this.toolChoice,
-        };
-      }
+      } else {
+        data.tool_choice = {
+          type: "function",
+          function: {
+            name: this.toolChoice,
+          },
+        };
+      }

🌐 Web query:

Does 302.AI's chat completions API expect tool_choice in the OpenAI-compatible shape `{ type: "function", function: { name: "..." } }`?

💡 Result:

Yes — 302.AI’s chat/completions endpoint uses the OpenAI-compatible tool/function shape. You can pass tool_choice as an object like {"type":"function","function":{"name":"my_function"}}. [1][2]

Sources:

  • 302.AI docs (notes that the API is compatible with OpenAI format). [1]
  • OpenAI / OpenAPI-style doc showing tool_choice object shape {"type":"function","function":{"name":"my_function"}}. [2]

tool_choice object shape is incorrect and will be rejected by the API.

302.AI's chat completions API uses the OpenAI-compatible tool_choice format, requiring { type: "function", function: { name: "..." } }. The current code incorrectly nests the function name directly under type instead of under a function property.

      } else {
        data.tool_choice = {
          type: "function",
-         name: this.toolChoice,
+         function: {
+           name: this.toolChoice,
+         },
        };
      }

Fix the object structure to nest the function name under a function property.

🤖 Prompt for AI Agents
In components/_302_ai/actions/chat-using-functions/chat-using-functions.mjs
around lines 174 to 183, the tool_choice object currently sets { type:
"function", name: this.toolChoice } which is the wrong shape; change it to {
type: "function", function: { name: this.toolChoice } } so the function name is
nested under a function property; update the assignment accordingly and ensure
other code consuming data.tool_choice expects the new shape.

return "Please only categorize items into the specific categories, and no others. Output a valid JSON string — an array of objects, where each object has the following properties: item, category. Do not return any English text other than the JSON, either before or after the JSON. I need to parse the response as JSON, and parsing will fail if you return any English before or after the JSON";
},
userMessage() {
return `Categorize each of the following items:\n\n${this.items.join("\n")}\n\ninto one of the following categories:\n\n${this.categories.join("\n")}\n\n${this.outputFormat()}}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix prompt typo and harden parsing of assistant output.

  • Remove stray “}” at end of userMessage.
  • Guard when choices/content is missing; don’t iterate undefined; throw a clear error if no content was returned.
  • When n == 1 and parsing fails, avoid returning undefined.
-      return `Categorize each of the following items:\n\n${this.items.join("\n")}\n\ninto one of the following categories:\n\n${this.categories.join("\n")}\n\n${this.outputFormat()}}`;
+      return `Categorize each of the following items:\n\n${this.items.join("\n")}\n\ninto one of the following categories:\n\n${this.categories.join("\n")}\n\n${this.outputFormat()}`;
@@
-      const responses = response.choices?.map(({ message }) => message.content);
-      const categorizations = [];
-      for (const response of responses) {
+      const contents = Array.isArray(response.choices)
+        ? response.choices.map((c) => c?.message?.content).filter(Boolean)
+        : [];
+      if (contents.length === 0) {
+        throw new Error("No assistant content returned. Please try again or contact https://pipedream.com/support");
+      }
+      const categorizations = [];
+      for (const content of contents) {
         try {
-          categorizations.push(JSON.parse(response));
+          categorizations.push(JSON.parse(content));
         } catch (err) {
-          console.log("Failed to parse output, assistant returned malformed JSON");
+          console.log("Failed to parse output, assistant returned malformed JSON");
         }
       }
@@
-      if (n > 1) {
-        output.categorizations = categorizations;
-      } else {
-        output.categorizations = categorizations[0];
-      }
+      output.categorizations = n > 1 ? categorizations : (categorizations[0] ?? null);

Also applies to: 58-66, 70-77

🤖 Prompt for AI Agents
In components/_302_ai/actions/classify-items/classify-items.mjs around lines 47,
58-66 and 70-77, remove the stray closing brace at the end of the user message
template (the trailing "}") and harden parsing of the assistant response: before
iterating make sure response.choices exists and each choice has
.message?.content (or .text) and throw a clear, descriptive Error if no content
was returned; when n == 1 and parsing of the structured output fails, do not
return undefined — instead fall back to returning the raw assistant content (or
throw a clear parse error if fallback is unacceptable), and ensure loops use
safe defaults (e.g., empty arrays) to avoid iterating undefined.

Comment on lines +55 to +80
_getCommonArgs() {
const args = {
model: this.modelId,
temperature: this.temperature
? parseFloat(this.temperature)
: undefined,
top_p: this.topP
? parseFloat(this.topP)
: undefined,
n: this.n
? parseInt(this.n)
: undefined,
stop: this.stop,
presence_penalty: this.presencePenalty
? parseFloat(this.presencePenalty)
: undefined,
frequency_penalty: this.frequencyPenalty
? parseFloat(this.frequencyPenalty)
: undefined,
max_tokens: this.maxTokens
? parseInt(this.maxTokens)
: undefined,
user: this.user,
};
return args;
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Sanitize numeric props to avoid sending NaN to the API.

If users pass non-numeric strings, current code will set fields to NaN. Only include numeric fields when finite.

   _getCommonArgs() {
-      const args = {
-        model: this.modelId,
-        temperature: this.temperature
-          ? parseFloat(this.temperature)
-          : undefined,
-        top_p: this.topP
-          ? parseFloat(this.topP)
-          : undefined,
-        n: this.n
-          ? parseInt(this.n)
-          : undefined,
-        stop: this.stop,
-        presence_penalty: this.presencePenalty
-          ? parseFloat(this.presencePenalty)
-          : undefined,
-        frequency_penalty: this.frequencyPenalty
-          ? parseFloat(this.frequencyPenalty)
-          : undefined,
-        max_tokens: this.maxTokens
-          ? parseInt(this.maxTokens)
-          : undefined,
-        user: this.user,
-      };
+      const num = (v) => {
+        const n = Number(v);
+        return Number.isFinite(n) ? n : undefined;
+      };
+      const args = {
+        model: this.modelId,
+        temperature: num(this.temperature),
+        top_p: num(this.topP),
+        n: Number.isInteger(num(this.n)) ? Number(num(this.n)) : undefined,
+        stop: this.stop,
+        presence_penalty: num(this.presencePenalty),
+        frequency_penalty: num(this.frequencyPenalty),
+        max_tokens: Number.isInteger(num(this.maxTokens)) ? Number(num(this.maxTokens)) : undefined,
+        user: this.user,
+      };
       return args;
     },

🏁 Script executed:

#!/bin/bash
# Scan repo for other direct parseInt/parseFloat on props that may leak NaN.
rg -nP 'parse(Int|Float)\s*\(\s*this\.' -C2

Length of output: 61641


Based on the scan results, the issue identified in the review comment is valid and widespread across the codebase. The current pattern (this.property ? parseFloat/parseInt()) does not prevent NaN values when non-numeric strings are passed.

However, I notice the proposed diff has a minor inefficiency. Let me verify the exact current implementation to provide the most accurate rewritten comment:<!-- [scratchpad_update]
[task verify_fix_correctness]
Confirmed: Number.isFinite is the correct approach. Research shows:

  • Number.isFinite catches NaN, Infinity, and -Infinity in one check
  • For parsed values: num(this.temperature) already applies Number() conversion
  • For integers: Number.isInteger() is used correctly to validate parsed result

[observations]

  • Proposed diff uses: Number.isInteger(num(this.n)) ? Number(num(this.n)) : undefined
  • This calls num() twice (inefficient)
  • Optimization: Could extract the result to a variable or simplify logic
  • However, the proposed fix IS functionally correct, just redundant

[analysis]
The review comment identifies a real, valid issue. The proposed diff correctly solves it using Number.isFinite.
Minor inefficiency in integer handling (double evaluation) but functionally sound.
Scan confirmed systemic issue across ~35 files.

[done]
Verified approach is correct. Ready to finalize rewritten comment.
[scratchpad_end] -->

Based on my verification of the scan results and testing patterns, the review comment correctly identifies the vulnerability. Number.isFinite() checks that a given value is a number, and the number is neither positive Infinity, negative Infinity, nor NaN. The current code using truthiness checks alone will fail to catch NaN values.

Fix NaN leakage in numeric field parsing — validate after conversion.

Current code uses truthiness to check for values before parsing, but this doesn't prevent NaN. For example, "abc" is truthy; parseFloat("abc") returns NaN. This NaN will be sent to the API, causing unexpected failures.

Only include numeric fields when they are finite numbers:

_getCommonArgs() {
  const num = (v) => {
    const n = Number(v);
    return Number.isFinite(n) ? n : undefined;
  };
  const args = {
    model: this.modelId,
    temperature: num(this.temperature),
    top_p: num(this.topP),
    n: Number.isInteger(num(this.n)) ? num(this.n) : undefined,
    stop: this.stop,
    presence_penalty: num(this.presencePenalty),
    frequency_penalty: num(this.frequencyPenalty),
    max_tokens: Number.isInteger(num(this.maxTokens)) ? num(this.maxTokens) : undefined,
    user: this.user,
  };
  return args;
},
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_getCommonArgs() {
const args = {
model: this.modelId,
temperature: this.temperature
? parseFloat(this.temperature)
: undefined,
top_p: this.topP
? parseFloat(this.topP)
: undefined,
n: this.n
? parseInt(this.n)
: undefined,
stop: this.stop,
presence_penalty: this.presencePenalty
? parseFloat(this.presencePenalty)
: undefined,
frequency_penalty: this.frequencyPenalty
? parseFloat(this.frequencyPenalty)
: undefined,
max_tokens: this.maxTokens
? parseInt(this.maxTokens)
: undefined,
user: this.user,
};
return args;
},
_getCommonArgs() {
const num = (v) => {
const n = Number(v);
return Number.isFinite(n) ? n : undefined;
};
const args = {
model: this.modelId,
temperature: num(this.temperature),
top_p: num(this.topP),
n: Number.isInteger(num(this.n)) ? num(this.n) : undefined,
stop: this.stop,
presence_penalty: num(this.presencePenalty),
frequency_penalty: num(this.frequencyPenalty),
max_tokens: Number.isInteger(num(this.maxTokens)) ? num(this.maxTokens) : undefined,
user: this.user,
};
return args;
},
🤖 Prompt for AI Agents
In components/_302_ai/actions/common/common.mjs around lines 55-80, the current
truthy checks (e.g. this.temperature ? parseFloat(...) : undefined) allow NaN
through when non-numeric strings are passed; change each numeric field to parse
once into a local variable, validate with Number.isFinite (for floats) or
Number.isInteger (for ints) and only set the arg when the parsed value is valid,
otherwise set undefined; do this for temperature, topP, n, presencePenalty,
frequencyPenalty, and maxTokens to avoid double parsing and NaN leakage.


export default {
name: "Create Embeddings",
version: "0.0.1",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix incorrect “token” length validation; sync action version.

  • element.length measures characters, not tokens → false positives vs 8192 tokens. Either implement real tokenization or remove the pre-check and rely on API errors with a clear message.
  • Bump action version to 0.1.0 to match the package.

Apply:

-  version: "0.0.1",
+  version: "0.1.0",
@@
-    // Confirm no element is more than 8192 tokens in length
-    for (const [ i, element, ] of this.input.entries()) {
-      if (element.length > 8192) {
-        throw new ConfigurationError(`Element #${i} is more than 8192 tokens in length. Each input must not exceed 8192 tokens in length.`);
-      }
-    }
+    // Rely on API to enforce token limits; surface a clearer error if it fails.
+    // (Character length != token length; a naive check would reject valid inputs.)

Also applies to: 53-61

🤖 Prompt for AI Agents
In components/_302_ai/actions/create-embeddings/create-embeddings.mjs around
line 6 (and also apply the same change to lines 53-61), the code incorrectly
uses element.length (character count) to validate against an 8192-token limit
and also has the action version set to "0.0.1"; remove the character-length
pre-check entirely (or replace it with a proper tokenizer if you choose to
implement token counting) so we rely on the API to enforce actual token limits
and return clear error messages, and update the version string to "0.1.0" to
match the package.

Comment on lines +4 to +8
Write the actions for the TicketSauce app.

General guidelines:
- Avoid manual truthiness checks for optional parameters, as @pipedream/platform utilities (@pipedream/axios) automatically exclude undefined values.
- Make sure to wrap API calls with the _makeRequest method. All actions methods need to call the _makeRequest methods. No actions method should call axios directly.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

File format and intent unclear — specification document rather than implementation.

Lines 4–52 contain guidelines, example code snippets, and best practices, but this file does not include the actual JavaScript/TypeScript component implementations. The .mdc format suggests this is a specification or prompt document to guide development, not production code itself.

Please clarify:

  1. Is this .mdc file intended to be committed to the repository, or is it an intermediate artifact?
  2. Where are the actual component implementation files (.mjs or .js files) for the TicketSauce actions?
  3. Should this spec be converted to actual runnable component code before merging?

🏁 Script executed:

#!/bin/bash
# Find all .mdc files to understand their purpose and usage pattern
echo "=== All .mdc files in repository ==="
fd -e mdc -type f

echo -e "\n=== TicketSauce-related files (.mjs, .js, .ts, .mdc) ==="
fd -i ticketsauce -type f

echo -e "\n=== Directory structure for components/ticketsauce ==="
find components/ticketsauce -type f 2>/dev/null | head -20

echo -e "\n=== Full content of the .mdc file under review ==="
cat components/ticketsauce/ticketsauce_components.mdc 2>/dev/null

Length of output: 30549


🏁 Script executed:

#!/bin/bash
# Check the ticketsauce.app.mjs file to understand dependencies
echo "=== Content of ticketsauce.app.mjs ==="
cat components/ticketsauce/ticketsauce.app.mjs

echo -e "\n=== Content of ticketsauce package.json ==="
cat components/ticketsauce/package.json

echo -e "\n=== Check for .mdc documentation in repo root/docs ==="
find . -maxdepth 3 -type f \( -name "README*" -o -name "*mdc*" \) 2>/dev/null | grep -v node_modules | head -20

echo -e "\n=== Search for references to ticketsauce actions in the codebase ==="
rg -i "ticketsauce.*get-events|ticketsauce.*get-orders" --type js --type mjs 2>/dev/null | head -10

Length of output: 1439


Implementation incomplete — specification exists but no component files are present.

The .mdc file is a specification/prompt document (following the pattern used in other components like _302_ai_components.mdc), not production code. However, the critical issue is that:

  1. No action component files exist — The spec defines five actions (get-events, get-event-details, get-orders, get-order-details, get-ticket-checkin-ids) with detailed requirements, but no corresponding .mjs implementation files are present.
  2. App stub is incompleteticketsauce.app.mjs contains only minimal authentication scaffolding with empty propDefinitions.
  3. Missing dependenciespackage.json omits required dependencies (@pipedream/platform, form-data) specified in the .mdc guidelines.

Before merging, implement the five action components as .mjs files (e.g., get-events.mjs, get-event-details.mjs, etc.) following the spec in the .mdc file, and update package.json with the required dependencies.

🤖 Prompt for AI Agents
In components/ticketsauce/ticketsauce_components.mdc around lines 4 to 8: the
spec defines five actions but there are no corresponding .mjs action files, the
app stub (ticketsauce.app.mjs) has empty propDefinitions, and package.json is
missing required dependencies; implement five action components (get-events.mjs,
get-event-details.mjs, get-orders.mjs, get-order-details.mjs,
get-ticket-checkin-ids.mjs) following the .mdc spec, ensure every action uses
this._makeRequest for HTTP calls (do not call axios directly), rely on
@pipedream/platform utilities so you do not manually filter undefined params,
update ticketsauce.app.mjs to include proper auth and propDefinitions referenced
by the actions and export/register the actions, and add @pipedream/platform and
form-data to package.json dependencies.

Comment on lines +54 to +632
get-events
Prompt
Get a list of all events owned by the authenticated account.

-When you come up for the description for this component follow format: <component description>. [See documentation](https://speca.io/ticketsauce/ticketsauce-public-api?key=204000d6bda66da78315e721920f43aa#list-of-events)
- Here are the parameters for the related list events API endpoint, use as a basis for props:
```
partner_id
string optional
Including this ID will limit the result set to the specific partner.

organization_id
string optional
Including this ID will limit the result set to the specific organization.

start_after
string optional
Only retrieve events that start AFTER the specified UTC date (format: YYYY-MM-DD).

start_before
string optional
Only retrieve events that start BEFORE the specified UTC date (format: YYYY-MM-DD).

start_phrase
string optional
A string representing the date to pull events that start __. Possible values are: today, yesterday, tomorrow, this_week, this_weekend, next_week, next_weekend, this_month, next_month

end_after
string optional
Only retrieve events that end AFTER the specified UTC date (format: YYYY-MM-DD).

end_before
string optional
Only retrieve events that end BEFORE the specified UTC date (format: YYYY-MM-DD).

created_after
string optional
Only retrieve events that were created AFTER the specified UTC date (format: YYYY-MM-DD).

created_before
string optional
Only retrieve events that were created BEFORE the specified UTC date (format: YYYY-MM-DD).

active_only
boolean optional
Leaving this as true will restrict retrieved events to only ‘active’ events. Setting to false will allow the retrieval of both active and inactive events.
Default:
true

inactive_only
boolean optional
Setting to true will restrict retrieved events to only ‘inactive’ events.
Default:
false

privacy_type
string optional
By default, this will restrict events to only those that are public. Changing to ‘all’ will remove all restriction, or changing to ‘unlisted’ will make it only pull those events that are set to unlisted.
Default:
public

sort_by
string optional
Which field to sort by. By default (‘date’), will sort events by their start date. Other options are ‘name’ (event name) or ‘city’ (the city where the event is located).
Default:
date

sort_dir
string optional
Which direction you’d like to sort - either ascending (‘asc’ - the default) or descending (‘desc’).
Default:
asc

include_performers
string optional
Returns any associated performers/artists with an event

Default:
false
```

get-event-details
Prompt
Get details for a specified event.

-When you come up for the description for this component following format: <component description>. [See documentation](https://speca.io/ticketsauce/ticketsauce-public-api?key=204000d6bda66da78315e721920f43aa#event-details)
- Here are the parameters for the related API endpoint, use as a basis for props:
```
id_or_slug
path parameter, string required

photos
query string parameter, integer optional
Whether or not to return the event’s photo gallery records.
Default:
0

include_performers
query string parameter, string optional
Returns any associated performers/artists with the event.
Default:
false
```
-for the prop related to id_or_slug use async options so users can select an event.
-as referece examples for async options you can use those found in apps zendesk, guru, etsy and recently you helped me come up with async options for prop imageId in generate-motion.mjs of leonardo ai app.
-when returning the context object, if there are no more options to return, simply return an empty object {}
-here is the response example for "list events" endpoint for you to come up with logic on how to implement async options for the prop related to id_or_slug
```
[
{
"Event": {
"active": true,
"address": "1221 Saint James Street",
"address2": "",
"city": "Philadelphia",
"country": "United States",
"created": "2025-06-26 02:40:59",
"custom_id": null,
"show_start": true,
"show_end": false,
"end": "2029-03-01 23:45:00",
"end_utc": "2029-03-02 04:45:00",
"featured": false,
"id": "685cb33b-0dc8-462d-ae5b-00410a1e60d3",
"location": "Voyeur Nightclub",
"latitude": "39.948357",
"longitude": "-75.161697",
"map_zoom": "13",
"modified": "2025-09-16 22:08:06",
"name": "Badge Conference Event TEST",
"online_only": false,
"activity_producer_id": null,
"organization_id": "64ad9d19-1bd0-490f-b332-7c9f0ad1e024",
"partner_id": "64ac69a6-a344-4e05-bbc7-64670ad1e030",
"postal_code": "19107",
"privacy_type": "0",
"region": null,
"slug": "badge-conference-event-test",
"start": "2029-03-01 23:30:00",
"start_utc": "2029-03-02 04:30:00",
"state": "PA",
"tickets_active": true,
"timezone": "America/New_York",
"website": "",
"event_topic_id": "589e8222-c4f8-4794-bde7-73d40a004b01",
"organization_name": "Voyeur DC",
"scheduled_publish_datetime_utc": null,
"event_topic": "Business",
"category": "Business",
"event_url": "https://grindr.ticketsauce.com/e/badge-conference-event-test",
"tickets_url": "https://grindr.ticketsauce.com/e/badge-conference-event-test/tickets",
"display_times": true,
"order_count": 4,
"partner_name": "Grindr Demo"
},
"Logo": {
"url": "https://res.cloudinary.com/eventservice/image/upload/q_auto,f_auto/v1751769059/saas/logos/image_1751769042730_r9qr7df7d.webp",
"created": "2025-06-26 03:04:39"
},
"Masthead": {
"url": null,
"created": null
}
},
{
"Event": {
"active": true,
"address": "",
"address2": "",
"city": "San Jose",
"country": "United States",
"created": "2025-07-23 01:23:07",
"custom_id": null,
"show_start": true,
"show_end": false,
"end": "2026-05-03 23:00:00",
"end_utc": "2026-05-04 06:00:00",
"featured": false,
"id": "6880397b-3624-4dc9-8e62-00140a1e60f5",
"location": "San Jose",
"latitude": null,
"longitude": null,
"map_zoom": "13",
"modified": "2025-07-23 07:10:35",
"name": "Festival Wristband Event",
"online_only": false,
"activity_producer_id": null,
"organization_id": "64ad9d19-1bd0-490f-b332-7c9f0ad1e024",
"partner_id": "64ac69a6-a344-4e05-bbc7-64670ad1e030",
"postal_code": "",
"privacy_type": "0",
"region": null,
"slug": "festival-wristband-event",
"start": "2026-05-01 18:00:00",
"start_utc": "2026-05-02 01:00:00",
"state": "CA",
"tickets_active": true,
"timezone": "America/Los_Angeles",
"website": "",
"event_topic_id": "c2fc3d42-c85f-4ecf-a0df-19d0a8659353",
"organization_name": "Voyeur DC",
"scheduled_publish_datetime_utc": null,
"event_topic": "Festival",
"category": "Festival",
"event_url": "https://grindr.ticketsauce.com/e/festival-wristband-event",
"tickets_url": "https://grindr.ticketsauce.com/e/festival-wristband-event/tickets",
"display_times": true,
"order_count": 1,
"partner_name": "Grindr Demo"
},
"Logo": {
"url": "https://res.cloudinary.com/eventservice/image/upload/q_auto,f_auto/v1753233876/saas/logos/image_1753233797906_s3hyoqr1m.webp",
"created": "2025-07-23 01:24:38"
},
"Masthead": {
"url": null,
"created": null
}
}
```

get-orders
Prompt
Get a list of orders from the specified event.

-When you come up for the description for this component following format: <component description>. [See documentation](https://speca.io/ticketsauce/ticketsauce-public-api?key=204000d6bda66da78315e721920f43aa#orders)
-for the prop related to event_id (path parameter) use async options so users can select an event.
-as referece examples for async options you can use those found in apps zendesk, guru, etsy and recently you helped me come up with async options for prop imageId in generate-motion.mjs of leonardo ai app.
-when returning the context object, if there are no more options to return, simply return an empty object {}
-here is the response example for "list events" endpoint for you to come up with logic on how to implement async options for the prop related to event_id
```
[
{
"Event": {
"active": true,
"address": "1221 Saint James Street",
"address2": "",
"city": "Philadelphia",
"country": "United States",
"created": "2025-06-26 02:40:59",
"custom_id": null,
"show_start": true,
"show_end": false,
"end": "2029-03-01 23:45:00",
"end_utc": "2029-03-02 04:45:00",
"featured": false,
"id": "685cb33b-0dc8-462d-ae5b-00410a1e60d3",
"location": "Voyeur Nightclub",
"latitude": "39.948357",
"longitude": "-75.161697",
"map_zoom": "13",
"modified": "2025-09-16 22:08:06",
"name": "Badge Conference Event TEST",
"online_only": false,
"activity_producer_id": null,
"organization_id": "64ad9d19-1bd0-490f-b332-7c9f0ad1e024",
"partner_id": "64ac69a6-a344-4e05-bbc7-64670ad1e030",
"postal_code": "19107",
"privacy_type": "0",
"region": null,
"slug": "badge-conference-event-test",
"start": "2029-03-01 23:30:00",
"start_utc": "2029-03-02 04:30:00",
"state": "PA",
"tickets_active": true,
"timezone": "America/New_York",
"website": "",
"event_topic_id": "589e8222-c4f8-4794-bde7-73d40a004b01",
"organization_name": "Voyeur DC",
"scheduled_publish_datetime_utc": null,
"event_topic": "Business",
"category": "Business",
"event_url": "https://grindr.ticketsauce.com/e/badge-conference-event-test",
"tickets_url": "https://grindr.ticketsauce.com/e/badge-conference-event-test/tickets",
"display_times": true,
"order_count": 4,
"partner_name": "Grindr Demo"
},
"Logo": {
"url": "https://res.cloudinary.com/eventservice/image/upload/q_auto,f_auto/v1751769059/saas/logos/image_1751769042730_r9qr7df7d.webp",
"created": "2025-06-26 03:04:39"
},
"Masthead": {
"url": null,
"created": null
}
},
{
"Event": {
"active": true,
"address": "",
"address2": "",
"city": "San Jose",
"country": "United States",
"created": "2025-07-23 01:23:07",
"custom_id": null,
"show_start": true,
"show_end": false,
"end": "2026-05-03 23:00:00",
"end_utc": "2026-05-04 06:00:00",
"featured": false,
"id": "6880397b-3624-4dc9-8e62-00140a1e60f5",
"location": "San Jose",
"latitude": null,
"longitude": null,
"map_zoom": "13",
"modified": "2025-07-23 07:10:35",
"name": "Festival Wristband Event",
"online_only": false,
"activity_producer_id": null,
"organization_id": "64ad9d19-1bd0-490f-b332-7c9f0ad1e024",
"partner_id": "64ac69a6-a344-4e05-bbc7-64670ad1e030",
"postal_code": "",
"privacy_type": "0",
"region": null,
"slug": "festival-wristband-event",
"start": "2026-05-01 18:00:00",
"start_utc": "2026-05-02 01:00:00",
"state": "CA",
"tickets_active": true,
"timezone": "America/Los_Angeles",
"website": "",
"event_topic_id": "c2fc3d42-c85f-4ecf-a0df-19d0a8659353",
"organization_name": "Voyeur DC",
"scheduled_publish_datetime_utc": null,
"event_topic": "Festival",
"category": "Festival",
"event_url": "https://grindr.ticketsauce.com/e/festival-wristband-event",
"tickets_url": "https://grindr.ticketsauce.com/e/festival-wristband-event/tickets",
"display_times": true,
"order_count": 1,
"partner_name": "Grindr Demo"
},
"Logo": {
"url": "https://res.cloudinary.com/eventservice/image/upload/q_auto,f_auto/v1753233876/saas/logos/image_1753233797906_s3hyoqr1m.webp",
"created": "2025-07-23 01:24:38"
},
"Masthead": {
"url": null,
"created": null
}
}
```
- Here are the parameters for the related get orders API endpoint, use as a basis for props:
per_page
integer optional
How many results to retrieve (per page). Max 500.
Default:
100

page
integer optional
Which page to return. For example, if per_page is 20, and page is 3, the results would show 41-60.
Default:
1

q
string optional
Exact email address or last name attached to an order.

return_questionnaires
boolean optional
Whether or not to return the question responses from questionnaires (will include attendee responses as well IF tickets are returned)
Default:
false

return_tickets
boolean optional
Whether or not to return the tickets for each order as well.
Default:
false

return_line_item_fees
boolean optional
Whether or not to return the itemized line item fees for each order (if they exist).
Default:
false

ordered_after
string optional
Only retrieve orders that were ordered AFTER the specified date/time (format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS).

ordered_before
string optional
Only retrieve orders that were ordered BEFORE the specified date/time (format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS).

ordered_phrase
string optional
A string representing the date to pull orders created ___. Possible values are: today, yesterday, tomorrow, this_week, this_weekend, next_week, next_weekend, this_month, next_month

modified_after
string optional
Only retrieve orders that were modified AFTER the specified date/time (format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS).

modified_before
string optional
Only retrieve orders that were modified BEFORE the specified date/time (format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS).

sort_by
string optional
Which field to sort by. By default (‘date’), this will sort orders by their ordered date. Or ‘name’ to order by last name.
Default:
'date'

sort_dir
string optional
Which direction you’d like to sort - either ascending (‘asc’ - the default) or descending (‘desc’).
Default:
'asc'
total_above

number optional
Return only orders who’s order total is greater than this value.
Default:
0

total_below
string optional
Return only orders who’s order total is lesss than this value.

promo_used_only
boolean optional
If set to true, this will return only orders who used a promo code.
Default:
false
```

get-order-details
Get details for the specified order.
-When you come up for the description for this component following format: <component description>. [See documentation](https://speca.io/ticketsauce/ticketsauce-public-api?key=204000d6bda66da78315e721920f43aa#order-details)
- Here are the parameters for the related get order details API endpoint, use as a basis for props:
order_id
path parameter, string required
-add an additional prop related to event_id and use async options so users can select an event.
-as referece examples for async options you can use those found in apps zendesk, guru, etsy and recently you helped me come up with async options for prop imageId in generate-motion.mjs of leonardo ai app.
-when returning the context object, if there are no more options to return, simply return an empty object {}
-here is the response example for "list events" endpoint for you to come up with logic on how to implement async options for the prop related to id_or_slug
```
[
{
"Event": {
"active": true,
"address": "1221 Saint James Street",
"address2": "",
"city": "Philadelphia",
"country": "United States",
"created": "2025-06-26 02:40:59",
"custom_id": null,
"show_start": true,
"show_end": false,
"end": "2029-03-01 23:45:00",
"end_utc": "2029-03-02 04:45:00",
"featured": false,
"id": "685cb33b-0dc8-462d-ae5b-00410a1e60d3",
"location": "Voyeur Nightclub",
"latitude": "39.948357",
"longitude": "-75.161697",
"map_zoom": "13",
"modified": "2025-09-16 22:08:06",
"name": "Badge Conference Event TEST",
"online_only": false,
"activity_producer_id": null,
"organization_id": "64ad9d19-1bd0-490f-b332-7c9f0ad1e024",
"partner_id": "64ac69a6-a344-4e05-bbc7-64670ad1e030",
"postal_code": "19107",
"privacy_type": "0",
"region": null,
"slug": "badge-conference-event-test",
"start": "2029-03-01 23:30:00",
"start_utc": "2029-03-02 04:30:00",
"state": "PA",
"tickets_active": true,
"timezone": "America/New_York",
"website": "",
"event_topic_id": "589e8222-c4f8-4794-bde7-73d40a004b01",
"organization_name": "Voyeur DC",
"scheduled_publish_datetime_utc": null,
"event_topic": "Business",
"category": "Business",
"event_url": "https://grindr.ticketsauce.com/e/badge-conference-event-test",
"tickets_url": "https://grindr.ticketsauce.com/e/badge-conference-event-test/tickets",
"display_times": true,
"order_count": 4,
"partner_name": "Grindr Demo"
},
"Logo": {
"url": "https://res.cloudinary.com/eventservice/image/upload/q_auto,f_auto/v1751769059/saas/logos/image_1751769042730_r9qr7df7d.webp",
"created": "2025-06-26 03:04:39"
},
"Masthead": {
"url": null,
"created": null
}
},
{
"Event": {
"active": true,
"address": "",
"address2": "",
"city": "San Jose",
"country": "United States",
"created": "2025-07-23 01:23:07",
"custom_id": null,
"show_start": true,
"show_end": false,
"end": "2026-05-03 23:00:00",
"end_utc": "2026-05-04 06:00:00",
"featured": false,
"id": "6880397b-3624-4dc9-8e62-00140a1e60f5",
"location": "San Jose",
"latitude": null,
"longitude": null,
"map_zoom": "13",
"modified": "2025-07-23 07:10:35",
"name": "Festival Wristband Event",
"online_only": false,
"activity_producer_id": null,
"organization_id": "64ad9d19-1bd0-490f-b332-7c9f0ad1e024",
"partner_id": "64ac69a6-a344-4e05-bbc7-64670ad1e030",
"postal_code": "",
"privacy_type": "0",
"region": null,
"slug": "festival-wristband-event",
"start": "2026-05-01 18:00:00",
"start_utc": "2026-05-02 01:00:00",
"state": "CA",
"tickets_active": true,
"timezone": "America/Los_Angeles",
"website": "",
"event_topic_id": "c2fc3d42-c85f-4ecf-a0df-19d0a8659353",
"organization_name": "Voyeur DC",
"scheduled_publish_datetime_utc": null,
"event_topic": "Festival",
"category": "Festival",
"event_url": "https://grindr.ticketsauce.com/e/festival-wristband-event",
"tickets_url": "https://grindr.ticketsauce.com/e/festival-wristband-event/tickets",
"display_times": true,
"order_count": 1,
"partner_name": "Grindr Demo"
},
"Logo": {
"url": "https://res.cloudinary.com/eventservice/image/upload/q_auto,f_auto/v1753233876/saas/logos/image_1753233797906_s3hyoqr1m.webp",
"created": "2025-07-23 01:24:38"
},
"Masthead": {
"url": null,
"created": null
}
}
```
- with the user selection of event_id you will dynamically populate async options for the order_id prop. You can reference the Google Sheets app, delete rows action, where Worksheet ID prop is dynamically populated based off the Spreadsheet ID selecfed by the user.
-here is the response example for "get orders" endpoint for you to come up with logic on how to implement async options for the prop related to order_id
```
[{"Order":{"id":"68803b8b-d290-4476-bed7-00120a1e6305","status":"1","last_name":"Lawrence","first_name":"Reid","dob":null,"email":"[email protected]","phone":"(999) 888-5555","title":null,"company":null,"address":null,"address2":null,"city":"Philly","state":"PA","postal_code":null,"country":"US","opted_in":false,"opted_in_sms":false,"subtotal":"0.00","total_paid":"0.00","refund_amount":"0.00","taxes":"0.00","contributions":"0.00","service_charges":"0.00","cc_fees":"0.00","admin_charges":"0.00","custom_line_item_fees":"0.00","policy_total":"0.00","policy_refunded":"0.00","ticket_count":"4","promo_code_amount":"0.00","promo_code":null,"promo_code_id":null,"purchase_location":"online","paid_date":"2025-07-22 18:31:55","paid_date_utc":"2025-07-23 01:31:55","affiliate_code":null,"event_id":"6880397b-3624-4dc9-8e62-00140a1e60f5","modified":"2025-07-23 01:31:55","delivery_method":"email","affiliate_code_id":null,"affiliate_code_name":null,"promoter_id":null,"promoter_name":null}}]
```

get-ticket-checkin-ids
Prompt
Get a list of ticket check-in IDs from the specified event.
-When you come up for the description for this component following format: <component description>. [See documentation](https://speca.io/ticketsauce/ticketsauce-public-api?key=204000d6bda66da78315e721920f43aa#ticket-checkin-ids)
- Here are the parameters for the related API endpoint, use as a basis for props:
```
Path parameters
event_id
string required
ID of event

query string parameters
per_page
integer optional
How many results to retrieve (per page). Max 5000.

page
integer optional
Which page to return. For example, if per_page is 20, and page is 3, the results would show 41-60.
Default:
1
```
-for the prop related to id_or_slug use async options so users can select an event.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Specification content is thorough but lacks implementation.

While the action specifications (get-events, get-event-details, get-orders, get-order-details, get-ticket-checkin-ids) are well-documented with parameter details, response examples, and async options guidance, the file contains no executable component code. The specifications reference implementation patterns (lines 8: "wrap API calls with _makeRequest", line 157: "use async options for props") but do not provide the actual implementation.

Should the implementation code be:

  • In separate .mjs or .js files (following Pipedream component conventions), or
  • Included in this .mdc file in a code block marked for extraction?

Please provide the actual component implementations matching these specifications.



---

🏁 Script executed:

```shell
#!/bin/bash
# Find the ticketsauce components file
find . -name "*ticketsauce*" -type f 2>/dev/null | head -20

Length of output: 96


🏁 Script executed:

#!/bin/bash
# List the structure of the ticketsauce directory
find ./components/ticketsauce -type f -name "*.mdc" -o -name "*.mjs" -o -name "*.js" 2>/dev/null | sort

Length of output: 96


🏁 Script executed:

#!/bin/bash
# Check if the specific file exists and show its structure
if [ -f "components/ticketsauce/ticketsauce_components.mdc" ]; then
  wc -l components/ticketsauce/ticketsauce_components.mdc
  head -100 components/ticketsauce/ticketsauce_components.mdc
fi

Length of output: 3553


🏁 Script executed:

#!/bin/bash
# Check for any implementation files
find ./components/ticketsauce -type f \( -name "*.mjs" -o -name "*.js" \) 2>/dev/null

Length of output: 44



---

🏁 Script executed:

```shell
#!/bin/bash
# View the full .mdc file to check if it contains implementation code
cat components/ticketsauce/ticketsauce_components.mdc

Length of output: 29875


🏁 Script executed:

#!/bin/bash
# Check ticketsauce.app.mjs to understand app structure
head -50 components/ticketsauce/ticketsauce.app.mjs

Length of output: 215


🏁 Script executed:

#!/bin/bash
# Compare with zendesk component structure
find ./components/zendesk -type f \( -name "*.mjs" -o -name "*.mdc" \) 2>/dev/null | head -10

Length of output: 633


🏁 Script executed:

#!/bin/bash
# Compare with leonardo_ai component structure  
find ./components/leonardo_ai -type f \( -name "*.mjs" -o -name "*.mdc" \) 2>/dev/null | head -10

Length of output: 371


Missing component action implementations across five TicketSauce actions.

The file contains comprehensive specifications but no executable code. Compared to standard Pipedream structure (zendesk, leonardo_ai), each action requires a separate .mjs file in the components/ticketsauce/actions/<action-name>/ directory:

  • get-events/get-events.mjs
  • get-event-details/get-event-details.mjs
  • get-orders/get-orders.mjs
  • get-order-details/get-order-details.mjs
  • get-ticket-checkin-ids/get-ticket-checkin-ids.mjs

The .mdc file (with alwaysApply: true frontmatter) is a specification document that references implementation patterns but does not provide the actual component code. Create the five action files following the provided parameter schemas, async options logic, and API patterns documented in the spec.

🤖 Prompt for AI Agents
In components/ticketsauce/ticketsauce_components.mdc around lines 54-632 the
review notes that five TicketSauce actions are only specified but not
implemented: get-events, get-event-details, get-orders, get-order-details, and
get-ticket-checkin-ids; implement each as a separate .mjs under
components/ticketsauce/actions/<action-name>/ with proper Pipedream action
structure, request logic using the documented endpoints and params, async
options for event selection (id_or_slug/event_id) that call the list-events
endpoint and map Event.name + Event.id (or slug) into {label,value} pairs,
ensure async options return an empty object {} when no more results, and for
get-order-details make event_id an async-prop and dynamically populate order_id
options by calling the get-orders endpoint for the selected event; follow
patterns from existing apps (zendesk, guru, etsy, leonardo_ai) for auth and
pagination, validate required path params, and export default action handlers
that return the API response.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Components] _302_ai

2 participants