diff --git a/n8n/.gitignore b/n8n/.gitignore
new file mode 100644
index 0000000..95d7ca8
--- /dev/null
+++ b/n8n/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+dist/
+*.js.map
+.DS_Store
diff --git a/n8n/LICENSE b/n8n/LICENSE
new file mode 100644
index 0000000..aa58f86
--- /dev/null
+++ b/n8n/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Tiny Fish, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/n8n/README.md b/n8n/README.md
new file mode 100644
index 0000000..3f0f153
--- /dev/null
+++ b/n8n/README.md
@@ -0,0 +1,167 @@
+# TinyFish Web Agent Node for n8n
+
+## Overview
+
+[TinyFish Web Agent](https://docs.mino.ai/) enables enterprises, builders, and developers to deploy AI agents that navigate real sites, complete real workflows across authenticated systems and dynamic interfaces, and return structured operational intelligence - through our visual platform or API. At scale. Reliably.
+
+## Configuration
+
+### 1. Install TinyFish Web Agent Node
+
+In your n8n instance, go to **Settings > Community Nodes** and install:
+
+```text
+n8n-nodes-tinyfish
+```
+
+Or install via npm in a self-hosted instance:
+
+```bash
+npm install n8n-nodes-tinyfish
+```
+
+### 2. Create a TinyFish Web Agent API Key
+
+Visit [TinyFish Dashboard](https://agent.tinyfish.ai/api-keys) and generate your API key.
+
+### 3. Authorize TinyFish Web Agent
+
+Add the **TinyFish Web Agent** node to a workflow, click the **Credential** dropdown, and select **Create New Credential**. Paste your API key and click **Save**.
+
+
+
+## Workflow Usage
+
+Integrate TinyFish Web Agent into your pipeline by following these steps:
+
+1. Add the **TinyFish Web Agent** node to your workflow.
+2. Select an operation and fill in the **URL** and **Goal** fields.
+3. Run the workflow to extract any information from a web page.
+
+
+
+
+
+For example, to extract the top headlines from Hacker News:
+
+- **URL:** `https://news.ycombinator.com`
+- **Goal:** `Get the top 5 headlines with their scores and comment counts. Return as a JSON array with keys: title, score, comments.`
+
+The node returns structured JSON:
+
+```json
+{
+ "status": "COMPLETED",
+ "runId": "run_abc123",
+ "streamingUrl": "https://...",
+ "resultJson": [
+ { "title": "Show HN: ...", "score": 142, "comments": 58 },
+ { "title": "Launch HN: ...", "score": 98, "comments": 33 }
+ ]
+}
+```
+
+The structured output can be piped into downstream n8n nodes — split the results, send Slack alerts, append rows to Google Sheets, or feed data into any other integration.
+
+
+
+## Agent Usage
+
+This node is compatible with n8n's AI Agent via `usableAsTool`. Give your agent the ability to browse the live web, extract data, and act on it.
+
+1. Add the **TinyFish Web Agent** node to your workflow as a tool.
+2. Prompt the Agent to perform web automations using natural language. The Agent will fill in the URL and Goal automatically.
+
+**Example prompts:**
+
+- `"Extract the blog post titles and authors from https://example.com/blog"`
+- `"Go to https://example.com/pricing and extract all plan names and prices"`
+- `"List my recent automation runs that have completed"`
+
+## Operations
+
+| Operation | Description |
+|---|---|
+| **Run (SSE Streaming)** | Execute automation with real-time streaming and return the final result. Recommended for most use cases. |
+| **Run (Sync)** | Execute automation and wait for the complete result in a single response. |
+| **Run (Async)** | Start automation and return a run ID immediately. Use with **Get Run** to poll for results. Best for batch processing multiple URLs in parallel. |
+| **Get Run** | Get the status and result of an automation run by ID. |
+| **List Runs** | List previous automation runs with optional status filter. |
+
+## Options
+
+All run operations support the following optional settings under **Add Option**:
+
+| Option | Default | Description |
+|---|---|---|
+| **Browser Profile** | `Lite` | Choose between `Lite` (fast, for standard sites) and `Stealth` (anti-detection mode for sites protected by Cloudflare, DataDome, CAPTCHAs, etc.). Start with Lite and switch to Stealth if you get blocked or see access denied errors. |
+| **Enable Proxy** | Off | Route the browser through a geographic proxy. Recommended when using Stealth mode on geo-restricted or bot-protected sites. Available countries: US, GB, CA, DE, FR, JP, AU. |
+| **Proxy Country** | US | Select the geographic location for the proxy. Only shown when Enable Proxy is turned on. Choose the country closest to the target site's expected region for best results. |
+| **Timeout** | 300s | Maximum time to wait for the automation to complete (30–600 seconds). Most tasks complete within 60–120 seconds. Increase for complex multi-step workflows like pagination or form filling. |
+
+## Use Cases
+
+- **Competitive Price Monitoring:** Extract pricing data from competitor product pages in parallel and feed it into downstream n8n nodes (e.g., Slack alerts, Google Sheets).
+- **AI Agent Web Access:** Give your n8n AI Agent the ability to browse the live web, extract data, and act on it.
+- **Lead Generation:** Run agents in parallel across a list of company URLs to extract contact information into a structured format.
+
+## Contributing
+
+We love getting contributions! To get started, here's how to set up development for the TinyFish Web Agent n8n Node:
+
+### 1. Fork this repository
+
+### 2. Clone the repository
+
+Clone the forked repository from your terminal:
+
+```shell
+git clone git@github.com:/tinyfish-web-agent-integrations.git
+```
+
+### 3. Install dependencies
+
+```shell
+cd n8n
+npm install
+```
+
+### 4. Build and link for local testing
+
+```shell
+npm run build
+npm link
+```
+
+Then in your n8n installation directory:
+
+```shell
+npm link n8n-nodes-tinyfish
+```
+
+Restart n8n to load the node.
+
+### 5. Make your changes and save
+
+### 6. Ensure the node works
+
+Rebuild and restart n8n after each change:
+
+```shell
+npm run build
+# restart n8n
+```
+
+### 7. Submit a Pull Request
+
+After confirming that the node works properly, submit a pull request to the `main` branch of this repository. If you run into issues like merge conflicts or don't know how to open a pull request, check out [GitHub's pull request tutorial](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests).
+
+## Resources
+
+- [Documentation](https://docs.mino.ai/)
+- [Cookbook](https://github.com/tinyfish-io/tinyfish-cookbook)
+- [Templates](https://agent.tinyfish.ai/examples)
+
+## Support
+
+Need help or have a question while using or contributing to the node? File a GitHub issue.
diff --git a/n8n/_assets/credentials.png b/n8n/_assets/credentials.png
new file mode 100644
index 0000000..0cca75a
Binary files /dev/null and b/n8n/_assets/credentials.png differ
diff --git a/n8n/_assets/node_configuration.png b/n8n/_assets/node_configuration.png
new file mode 100644
index 0000000..8f302ab
Binary files /dev/null and b/n8n/_assets/node_configuration.png differ
diff --git a/n8n/_assets/workflow.png b/n8n/_assets/workflow.png
new file mode 100644
index 0000000..09e36ef
Binary files /dev/null and b/n8n/_assets/workflow.png differ
diff --git a/n8n/credentials/TinyfishApi.credentials.ts b/n8n/credentials/TinyfishApi.credentials.ts
new file mode 100644
index 0000000..68dac2a
--- /dev/null
+++ b/n8n/credentials/TinyfishApi.credentials.ts
@@ -0,0 +1,47 @@
+import type {
+ IAuthenticateGeneric,
+ ICredentialTestRequest,
+ ICredentialType,
+ INodeProperties,
+} from 'n8n-workflow';
+
+export class TinyfishApi implements ICredentialType {
+ name = 'tinyfishApi';
+
+ displayName = 'TinyFish Web Agent API';
+
+ icon = { light: 'file:tinyfish.svg', dark: 'file:tinyfish.svg' } as const;
+
+ documentationUrl = 'https://docs.mino.ai';
+
+ properties: INodeProperties[] = [
+ {
+ displayName: 'API Key',
+ name: 'apiKey',
+ type: 'string',
+ typeOptions: { password: true },
+ default: '',
+ required: true,
+ description:
+ 'Your TinyFish Web Agent API key. Get one at https://agent.tinyfish.ai/api-keys.',
+ },
+ ];
+
+ authenticate: IAuthenticateGeneric = {
+ type: 'generic',
+ properties: {
+ headers: {
+ 'X-API-Key': '={{$credentials.apiKey}}',
+ },
+ },
+ };
+
+ test: ICredentialTestRequest = {
+ request: {
+ baseURL: 'https://agent.tinyfish.ai',
+ url: '/v1/runs',
+ method: 'GET',
+ qs: { limit: '1' },
+ },
+ };
+}
diff --git a/n8n/credentials/tinyfish.svg b/n8n/credentials/tinyfish.svg
new file mode 100644
index 0000000..ca9bbe9
--- /dev/null
+++ b/n8n/credentials/tinyfish.svg
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/n8n/nodes/Tinyfish/GenericFunctions.ts b/n8n/nodes/Tinyfish/GenericFunctions.ts
new file mode 100644
index 0000000..dc767a6
--- /dev/null
+++ b/n8n/nodes/Tinyfish/GenericFunctions.ts
@@ -0,0 +1,277 @@
+import type {
+ IDataObject,
+ IExecuteFunctions,
+ IHttpRequestMethods,
+ IHttpRequestOptions,
+ JsonObject,
+} from 'n8n-workflow';
+import { NodeApiError, NodeOperationError } from 'n8n-workflow';
+
+const API_BASE_URL = 'https://agent.tinyfish.ai';
+
+const MAX_RETRIES = 3;
+const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);
+
+/**
+ * Map known TinyFish API error codes to actionable user messages.
+ */
+function getActionableMessage(error: unknown): string | undefined {
+ const cause = (error as Record)?.cause as Record | undefined;
+ const body = cause?.body as Record | undefined;
+ const errorObj = body?.error as Record | undefined;
+
+ if (!errorObj) return undefined;
+
+ const code = errorObj.code as string | undefined;
+ const message = errorObj.message as string | undefined;
+ const details = errorObj.details as Record | undefined;
+
+ switch (code) {
+ case 'MISSING_API_KEY':
+ return 'API key is missing. Add your TinyFish API key in the node credentials.';
+ case 'INVALID_API_KEY':
+ return 'Invalid API key. Verify your key at https://agent.tinyfish.ai/api-keys or generate a new one.';
+ case 'UNAUTHORIZED':
+ return 'Authentication failed. Check your account status at https://agent.tinyfish.ai/api-keys.';
+ case 'FORBIDDEN':
+ return 'Insufficient credits or no active subscription. Check your account at https://agent.tinyfish.ai/api-keys.';
+ case 'NOT_FOUND':
+ return `${message || 'Resource not found'}. Verify the run ID is correct.`;
+ case 'INVALID_INPUT': {
+ if (details) {
+ const detailStr = Object.entries(details).map(([k, v]) => `${k}: ${v}`).join(', ');
+ return `Invalid input (${detailStr}). Check your URL and goal parameters.`;
+ }
+ return `Invalid input: ${message || 'Validation failed'}. Check your URL and goal parameters.`;
+ }
+ case 'RATE_LIMIT_EXCEEDED':
+ return 'Rate limit exceeded after multiple retries. Wait a few minutes and try again, or reduce request frequency.';
+ case 'INTERNAL_ERROR':
+ return `TinyFish server error: ${message || 'An unexpected error occurred'}. Retries exhausted — try again later.`;
+ default:
+ return undefined;
+ }
+}
+
+/**
+ * Check if an error has a retryable HTTP status code.
+ */
+function isRetryable(error: unknown): boolean {
+ const httpCode = (error as Record)?.httpCode as number | undefined;
+ if (httpCode && RETRYABLE_STATUS_CODES.has(httpCode)) return true;
+
+ const cause = (error as Record)?.cause as Record | undefined;
+ const status = cause?.status as number | undefined;
+ return status !== undefined && RETRYABLE_STATUS_CODES.has(status);
+}
+
+/**
+ * Sleep for a given number of milliseconds.
+ */
+function sleep(ms: number): Promise {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+}
+
+/**
+ * Make an authenticated request to the TinyFish API.
+ * Retries on 429/5xx with exponential backoff (max 3 retries).
+ */
+export async function tinyfishApiRequest(
+ this: IExecuteFunctions,
+ method: IHttpRequestMethods,
+ path: string,
+ body: IDataObject = {},
+ qs: IDataObject = {},
+ options: Partial = {},
+): Promise {
+ const requestOptions: IHttpRequestOptions = {
+ method,
+ url: `${API_BASE_URL}${path}`,
+ qs,
+ json: true,
+ ...options,
+ };
+
+ if (Object.keys(body).length > 0) {
+ requestOptions.body = body;
+ }
+
+ let lastError: unknown;
+
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
+ try {
+ return (await this.helpers.httpRequestWithAuthentication.call(
+ this,
+ 'tinyfishApi',
+ requestOptions,
+ )) as IDataObject;
+ } catch (error) {
+ lastError = error;
+
+ if (attempt < MAX_RETRIES && isRetryable(error)) {
+ await sleep(Math.pow(2, attempt) * 1000);
+ continue;
+ }
+
+ const actionableMessage = getActionableMessage(error);
+ if (actionableMessage) {
+ throw new NodeApiError(this.getNode(), error as JsonObject, {
+ message: actionableMessage,
+ });
+ }
+ throw new NodeApiError(this.getNode(), error as JsonObject);
+ }
+ }
+
+ // Should not reach here, but TypeScript needs a return path
+ throw new NodeApiError(this.getNode(), lastError as JsonObject);
+}
+
+/**
+ * Build the automation payload from node parameters.
+ * Mirrors dify/tools/base.py _build_automation_payload().
+ */
+export function buildAutomationPayload(
+ this: IExecuteFunctions,
+ itemIndex: number,
+): IDataObject {
+ const url = this.getNodeParameter('url', itemIndex) as string;
+ const goal = this.getNodeParameter('goal', itemIndex) as string;
+ const options = this.getNodeParameter('options', itemIndex, {}) as IDataObject;
+
+ const payload: IDataObject = {
+ url,
+ goal,
+ browser_profile: (options.browserProfile as string) || 'lite',
+ };
+
+ if (options.proxyEnabled) {
+ const proxyConfig: IDataObject = { enabled: true };
+ if (options.proxyCountryCode) {
+ proxyConfig.country_code = options.proxyCountryCode as string;
+ }
+ payload.proxy_config = proxyConfig;
+ }
+
+ return payload;
+}
+
+/**
+ * Consume an SSE stream from the TinyFish run-sse endpoint.
+ * Uses native fetch() for streaming support.
+ * Returns the final COMPLETE result as structured JSON.
+ */
+export async function consumeSseStream(
+ this: IExecuteFunctions,
+ payload: IDataObject,
+ timeoutMs: number,
+): Promise {
+ const credentials = await this.getCredentials('tinyfishApi');
+ const apiKey = credentials.apiKey as string;
+
+ const controller = new AbortController();
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
+ let lastProgress = '';
+
+ try {
+ const response = await fetch(`${API_BASE_URL}/v1/automation/run-sse`, {
+ method: 'POST',
+ headers: {
+ 'X-API-Key': apiKey,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(payload),
+ signal: controller.signal,
+ });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new NodeOperationError(this.getNode(), `API request failed with status ${response.status}: ${errorText}`);
+ }
+
+ if (!response.body) {
+ throw new NodeOperationError(this.getNode(), 'Response body is empty');
+ }
+
+ const reader = response.body.getReader();
+ const decoder = new TextDecoder();
+ let buffer = '';
+ let finalResult: IDataObject | null = null;
+ let runId = '';
+ let streamingUrl = '';
+
+ while (true) {
+ const { done, value } = await reader.read();
+
+ buffer += decoder.decode(value, { stream: true });
+ if (done) {
+ buffer += decoder.decode();
+ }
+ const lines = buffer.split('\n');
+ buffer = lines.pop() ?? '';
+
+ for (const line of lines) {
+ if (!line.startsWith('data: ')) continue;
+
+ let eventData: IDataObject;
+ try {
+ eventData = JSON.parse(line.slice(6)) as IDataObject;
+ } catch {
+ continue;
+ }
+
+ const eventType = eventData.type as string;
+
+ if (eventType === 'STARTED') {
+ runId = (eventData.runId as string) || '';
+ } else if (eventType === 'STREAMING_URL') {
+ streamingUrl = (eventData.streamingUrl as string) || '';
+ } else if (eventType === 'PROGRESS') {
+ lastProgress = (eventData.purpose as string) || '';
+ } else if (eventType === 'COMPLETE') {
+ const status = eventData.status as string;
+ if (status === 'COMPLETED') {
+ finalResult = {
+ status: 'COMPLETED',
+ runId,
+ streamingUrl,
+ lastProgress,
+ resultJson: eventData.resultJson || {},
+ };
+ } else {
+ finalResult = {
+ status: status || 'FAILED',
+ runId,
+ lastProgress,
+ error: eventData.error || 'Unknown error',
+ };
+ }
+ }
+ }
+
+ if (done) break;
+ }
+
+ if (!finalResult) {
+ throw new NodeOperationError(
+ this.getNode(),
+ 'SSE stream ended without a COMPLETE event',
+ );
+ }
+
+ return finalResult;
+ } catch (error) {
+ if ((error as Error).name === 'AbortError') {
+ const progressHint = lastProgress
+ ? ` Last progress: "${lastProgress}".`
+ : '';
+ throw new NodeOperationError(
+ this.getNode(),
+ `Automation timed out after ${Math.round(timeoutMs / 1000)} seconds.${progressHint} Try increasing the timeout or simplifying the goal.`,
+ );
+ }
+ throw error;
+ } finally {
+ clearTimeout(timeoutId);
+ }
+}
diff --git a/n8n/nodes/Tinyfish/Tinyfish.node.json b/n8n/nodes/Tinyfish/Tinyfish.node.json
new file mode 100644
index 0000000..a017816
--- /dev/null
+++ b/n8n/nodes/Tinyfish/Tinyfish.node.json
@@ -0,0 +1,32 @@
+{
+ "node": "n8n-nodes-tinyfish.tinyfish",
+ "nodeVersion": "1.0",
+ "codexVersion": "1.0",
+ "categories": ["Development"],
+ "resources": {
+ "primaryDocumentation": [
+ {
+ "url": "https://docs.mino.ai"
+ }
+ ],
+ "credentialDocumentation": [
+ {
+ "url": "https://docs.mino.ai/authentication"
+ }
+ ]
+ },
+ "alias": [
+ "tinyfish",
+ "mino",
+ "web automation",
+ "browser automation",
+ "web scraping",
+ "scrape",
+ "extract",
+ "ai agent",
+ "browse website"
+ ],
+ "subcategories": {
+ "Development": ["Other"]
+ }
+}
diff --git a/n8n/nodes/Tinyfish/Tinyfish.node.ts b/n8n/nodes/Tinyfish/Tinyfish.node.ts
new file mode 100644
index 0000000..69d6792
--- /dev/null
+++ b/n8n/nodes/Tinyfish/Tinyfish.node.ts
@@ -0,0 +1,192 @@
+import type {
+ IDataObject,
+ IExecuteFunctions,
+ INodeExecutionData,
+ INodeType,
+ INodeTypeDescription,
+} from 'n8n-workflow';
+import { NodeOperationError } from 'n8n-workflow';
+
+import {
+ operationField,
+ runFields,
+ getRunFields,
+ listRunsFields,
+} from './TinyfishDescription';
+import {
+ tinyfishApiRequest,
+ buildAutomationPayload,
+ consumeSseStream,
+} from './GenericFunctions';
+
+const MAX_PAGINATION_ITEMS = 10_000;
+
+export class Tinyfish implements INodeType {
+ description: INodeTypeDescription = {
+ displayName: 'TinyFish Web Agent',
+ name: 'tinyfish',
+ icon: 'file:tinyfish.svg',
+ group: ['output'],
+ version: 1,
+ subtitle: '={{$parameter["operation"]}}',
+ description:
+ 'Extract data, fill forms, and automate multi-step browser workflows using natural language',
+ defaults: {
+ name: 'TinyFish Web Agent',
+ },
+ inputs: ['main'],
+ outputs: ['main'],
+ usableAsTool: true,
+ credentials: [
+ {
+ name: 'tinyfishApi',
+ required: true,
+ },
+ ],
+ properties: [operationField, ...runFields, ...getRunFields, ...listRunsFields],
+ };
+
+ async execute(this: IExecuteFunctions): Promise {
+ const items = this.getInputData();
+ const returnData: INodeExecutionData[] = [];
+ const operation = this.getNodeParameter('operation', 0) as string;
+
+ for (let i = 0; i < items.length; i++) {
+ try {
+ let responseData: IDataObject;
+
+ if (operation === 'runSse') {
+ const payload = buildAutomationPayload.call(this, i);
+ const options = this.getNodeParameter('options', i, {}) as IDataObject;
+ const timeoutSeconds = (options.timeout as number) || 300;
+ responseData = await consumeSseStream.call(
+ this,
+ payload,
+ timeoutSeconds * 1000,
+ );
+ } else if (operation === 'runSync') {
+ const payload = buildAutomationPayload.call(this, i);
+ const options = this.getNodeParameter('options', i, {}) as IDataObject;
+ const timeoutSeconds = (options.timeout as number) || 300;
+ responseData = await tinyfishApiRequest.call(
+ this,
+ 'POST',
+ '/v1/automation/run',
+ payload,
+ {},
+ { timeout: timeoutSeconds * 1000 },
+ );
+ } else if (operation === 'runAsync') {
+ const payload = buildAutomationPayload.call(this, i);
+ responseData = await tinyfishApiRequest.call(
+ this,
+ 'POST',
+ '/v1/automation/run-async',
+ payload,
+ );
+ } else if (operation === 'getRun') {
+ const runId = this.getNodeParameter('runId', i) as string;
+ responseData = await tinyfishApiRequest.call(
+ this,
+ 'GET',
+ `/v1/runs/${runId}`,
+ );
+ } else if (operation === 'listRuns') {
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ const filters = this.getNodeParameter(
+ 'filters',
+ i,
+ {},
+ ) as IDataObject;
+ const qs: IDataObject = {};
+
+ if (filters.status) {
+ qs.status = filters.status;
+ }
+
+ if (returnAll) {
+ const allRuns: IDataObject[] = [];
+ let cursor: string | undefined;
+
+ do {
+ if (cursor) qs.cursor = cursor;
+ qs.limit = 100;
+
+ const page = await tinyfishApiRequest.call(
+ this,
+ 'GET',
+ '/v1/runs',
+ {},
+ qs,
+ );
+
+ const data = (page.data as IDataObject[]) || [];
+ allRuns.push(...data);
+
+ if (allRuns.length >= MAX_PAGINATION_ITEMS) {
+ break;
+ }
+
+ const pagination = page.pagination as IDataObject | undefined;
+ cursor = pagination?.has_more
+ ? (pagination.next_cursor as string)
+ : undefined;
+ } while (cursor);
+
+ for (const run of allRuns.slice(0, MAX_PAGINATION_ITEMS)) {
+ returnData.push({ json: run, pairedItem: { item: i } });
+ }
+ continue;
+ } else {
+ const limit = this.getNodeParameter('limit', i) as number;
+ qs.limit = limit;
+
+ const page = await tinyfishApiRequest.call(
+ this,
+ 'GET',
+ '/v1/runs',
+ {},
+ qs,
+ );
+
+ const data = (page.data as IDataObject[]) || [];
+ for (const run of data) {
+ returnData.push({ json: run, pairedItem: { item: i } });
+ }
+ continue;
+ }
+ } else {
+ throw new NodeOperationError(
+ this.getNode(),
+ `Unknown operation: ${operation}`,
+ { itemIndex: i },
+ );
+ }
+
+ const executionData = this.helpers.constructExecutionMetaData(
+ this.helpers.returnJsonArray(responseData),
+ { itemData: { item: i } },
+ );
+ returnData.push(...executionData);
+ } catch (error) {
+ if (this.continueOnFail()) {
+ returnData.push({
+ json: { error: (error as Error).message },
+ pairedItem: { item: i },
+ });
+ continue;
+ }
+
+ if (error instanceof NodeOperationError) {
+ throw error;
+ }
+
+ throw new NodeOperationError(this.getNode(), error as Error, {
+ itemIndex: i,
+ });
+ }
+ }
+
+ return [returnData];
+ }
+}
diff --git a/n8n/nodes/Tinyfish/TinyfishDescription.ts b/n8n/nodes/Tinyfish/TinyfishDescription.ts
new file mode 100644
index 0000000..6aa3742
--- /dev/null
+++ b/n8n/nodes/Tinyfish/TinyfishDescription.ts
@@ -0,0 +1,228 @@
+import type { INodeProperties } from 'n8n-workflow';
+
+export const operationField: INodeProperties = {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ default: 'runSse',
+ options: [
+ {
+ name: 'Run (SSE Streaming)',
+ value: 'runSse',
+ action: 'Run automation with SSE streaming',
+ description:
+ 'Recommended for most tasks. Streams real-time progress events and returns the final result. Best for tasks that may take 30+ seconds.',
+ },
+ {
+ name: 'Run (Sync)',
+ value: 'runSync',
+ action: 'Run automation synchronously',
+ description: 'Execute and wait for the complete result in a single response. Use for quick extractions under 60 seconds.',
+ },
+ {
+ name: 'Run (Async)',
+ value: 'runAsync',
+ action: 'Run automation asynchronously',
+ description: 'Returns a run ID immediately without waiting. Use with Get Run to poll for results. Best for batch processing multiple URLs in parallel.',
+ },
+ {
+ name: 'Get Run',
+ value: 'getRun',
+ action: 'Get run details',
+ description: 'Retrieve the status and result of a previously started async run by its ID',
+ },
+ {
+ name: 'List Runs',
+ value: 'listRuns',
+ action: 'List automation runs',
+ description: 'List past automation runs with optional status filter. Useful for monitoring or retrieving results.',
+ },
+ ],
+};
+
+export const runFields: INodeProperties[] = [
+ {
+ displayName: 'URL',
+ name: 'url',
+ type: 'string',
+ default: '',
+ required: true,
+ validateType: 'url',
+ placeholder: 'https://example.com',
+ description: 'The website URL to navigate to. Must include https://. The AI browser will load this page and execute the goal.',
+ displayOptions: {
+ show: {
+ operation: ['runSse', 'runSync', 'runAsync'],
+ },
+ },
+ },
+ {
+ displayName: 'Goal',
+ name: 'goal',
+ type: 'string',
+ typeOptions: { rows: 4 },
+ default: '',
+ required: true,
+ placeholder: 'Extract all product names and prices. Return as JSON array with keys: name, price.',
+ description:
+ 'Natural language instruction for what to accomplish on the website. For best results: specify the exact JSON schema you want returned, include termination conditions (e.g., stop after 20 items), and handle edge cases explicitly (e.g., if price shows Contact Us, set to null). Use numbered steps for multi-step workflows.',
+ displayOptions: {
+ show: {
+ operation: ['runSse', 'runSync', 'runAsync'],
+ },
+ },
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Option',
+ default: {},
+ displayOptions: {
+ show: {
+ operation: ['runSse', 'runSync', 'runAsync'],
+ },
+ },
+ options: [
+ {
+ displayName: 'Browser Profile',
+ name: 'browserProfile',
+ type: 'options',
+ default: 'lite',
+ description: 'Browser profile to use for execution. Start with Lite for speed; switch to Stealth if you get blocked.',
+ options: [
+ {
+ name: 'Lite (Standard)',
+ value: 'lite',
+ description:
+ 'Fast standard browser for sites without bot protection',
+ },
+ {
+ name: 'Stealth (Anti-Detection)',
+ value: 'stealth',
+ description:
+ 'Anti-detection browser for sites with Cloudflare, DataDome, or CAPTCHAs. Slower but bypasses bot detection. Pair with proxy for best results.',
+ },
+ ],
+ },
+ {
+ displayName: 'Enable Proxy',
+ name: 'proxyEnabled',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to route the browser through a geographic proxy. Recommended when using Stealth mode for geo-restricted or bot-protected sites.',
+ },
+ {
+ displayName: 'Proxy Country',
+ name: 'proxyCountryCode',
+ type: 'options',
+ default: 'US',
+ description: 'Geographic location for the proxy. Choose the country closest to the target site\'s expected region.',
+ displayOptions: {
+ show: {
+ proxyEnabled: [true],
+ },
+ },
+ options: [
+ { name: 'Australia', value: 'AU' },
+ { name: 'Canada', value: 'CA' },
+ { name: 'France', value: 'FR' },
+ { name: 'Germany', value: 'DE' },
+ { name: 'Japan', value: 'JP' },
+ { name: 'United Kingdom', value: 'GB' },
+ { name: 'United States', value: 'US' },
+ ],
+ },
+ {
+ displayName: 'Timeout (Seconds)',
+ name: 'timeout',
+ type: 'number',
+ default: 300,
+ description:
+ 'Maximum time to wait for automation to complete (30-600s). Most tasks complete within 60-120 seconds. Increase for complex multi-step workflows.',
+ typeOptions: {
+ minValue: 30,
+ maxValue: 600,
+ },
+ },
+ ],
+ },
+];
+
+export const getRunFields: INodeProperties[] = [
+ {
+ displayName: 'Run ID',
+ name: 'runId',
+ type: 'string',
+ default: '',
+ required: true,
+ placeholder: 'run_abc123',
+ description: 'The ID of the automation run to retrieve',
+ displayOptions: {
+ show: {
+ operation: ['getRun'],
+ },
+ },
+ },
+];
+
+export const listRunsFields: INodeProperties[] = [
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ operation: ['listRuns'],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 20,
+ description: 'Max number of results to return',
+ typeOptions: {
+ minValue: 1,
+ maxValue: 100,
+ },
+ displayOptions: {
+ show: {
+ operation: ['listRuns'],
+ returnAll: [false],
+ },
+ },
+ },
+ {
+ displayName: 'Filters',
+ name: 'filters',
+ type: 'collection',
+ placeholder: 'Add Filter',
+ default: {},
+ displayOptions: {
+ show: {
+ operation: ['listRuns'],
+ },
+ },
+ options: [
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ default: '',
+ description: 'Filter runs by status',
+ options: [
+ { name: 'All', value: '' },
+ { name: 'Cancelled', value: 'CANCELLED' },
+ { name: 'Completed', value: 'COMPLETED' },
+ { name: 'Failed', value: 'FAILED' },
+ { name: 'Pending', value: 'PENDING' },
+ { name: 'Running', value: 'RUNNING' },
+ ],
+ },
+ ],
+ },
+];
diff --git a/n8n/nodes/Tinyfish/tinyfish.svg b/n8n/nodes/Tinyfish/tinyfish.svg
new file mode 100644
index 0000000..ca9bbe9
--- /dev/null
+++ b/n8n/nodes/Tinyfish/tinyfish.svg
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/n8n/package-lock.json b/n8n/package-lock.json
new file mode 100644
index 0000000..4713857
--- /dev/null
+++ b/n8n/package-lock.json
@@ -0,0 +1,1220 @@
+{
+ "name": "n8n-nodes-tinyfish",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "n8n-nodes-tinyfish",
+ "version": "0.1.0",
+ "license": "MIT",
+ "devDependencies": {
+ "@types/node": "^22.0.0",
+ "n8n-workflow": "*",
+ "typescript": "~5.7.0"
+ },
+ "peerDependencies": {
+ "n8n-workflow": "*"
+ }
+ },
+ "node_modules/@n8n_io/riot-tmpl": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@n8n_io/riot-tmpl/-/riot-tmpl-4.0.1.tgz",
+ "integrity": "sha512-/zdRbEfTFjsm1NqnpPQHgZTkTdbp5v3VUxGeMA9098sps8jRCTraQkc3AQstJgHUm7ylBXJcIVhnVeLUMWAfwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-config-riot": "^1.0.0"
+ }
+ },
+ "node_modules/@n8n/errors": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@n8n/errors/-/errors-0.6.0.tgz",
+ "integrity": "sha512-oVJ0lgRYJY6/aPOW2h37ea5T+nX7/wULRn5FymwYeaiYlsLdqwIQEtGwZrajpzxJB0Os74u4lSH3WWQgZCkgxQ==",
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "callsites": "3.1.0"
+ }
+ },
+ "node_modules/@n8n/tournament": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@n8n/tournament/-/tournament-1.0.6.tgz",
+ "integrity": "sha512-UGSxYXXVuOX0yL6HTLBStKYwLIa0+JmRKiSZSCMcM2s2Wax984KWT6XIA1TR/27i7yYpDk1MY14KsTPnuEp27A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@n8n_io/riot-tmpl": "^4.0.1",
+ "ast-types": "^0.16.1",
+ "esprima-next": "^5.8.4",
+ "recast": "^0.22.0"
+ },
+ "engines": {
+ "node": ">=20.15",
+ "pnpm": ">=9.5"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "22.19.11",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz",
+ "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/assert": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz",
+ "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "is-nan": "^1.3.2",
+ "object-is": "^1.1.5",
+ "object.assign": "^4.1.4",
+ "util": "^0.12.5"
+ }
+ },
+ "node_modules/ast-types": {
+ "version": "0.16.1",
+ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz",
+ "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/charenc": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+ "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/crypt": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+ "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eslint-config-riot": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-riot/-/eslint-config-riot-1.0.0.tgz",
+ "integrity": "sha512-NB/L/1Y30qyJcG5xZxCJKW/+bqyj+llbcCwo9DEz8bESIP0SLTOQ8T1DWCCFc+wJ61AMEstj4511PSScqMMfCw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/esprima-next": {
+ "version": "5.8.4",
+ "resolved": "https://registry.npmjs.org/esprima-next/-/esprima-next-5.8.4.tgz",
+ "integrity": "sha512-8nYVZ4ioIH4Msjb/XmhnBdz5WRRBaYqevKa1cv9nGJdCehMbzZCPNEEnqfLCZVetUVrUPEcb5IYyu1GG4hFqgg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
+ "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/generator-function": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
+ "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/is-arguments": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz",
+ "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
+ "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.4",
+ "generator-function": "^2.0.0",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-nan": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz",
+ "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/jmespath": {
+ "version": "0.16.0",
+ "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
+ "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/js-base64": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz",
+ "integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/jsonrepair": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/jsonrepair/-/jsonrepair-3.13.1.tgz",
+ "integrity": "sha512-WJeiE0jGfxYmtLwBTEk8+y/mYcaleyLXWaqp5bJu0/ZTSeG0KQq/wWQ8pmnkKenEdN6pdnn6QtcoSUkbqDHWNw==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "jsonrepair": "bin/cli.js"
+ }
+ },
+ "node_modules/jssha": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.3.1.tgz",
+ "integrity": "sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.23",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
+ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/luxon": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz",
+ "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/md5": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
+ "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "charenc": "0.0.2",
+ "crypt": "0.0.2",
+ "is-buffer": "~1.1.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/n8n-workflow": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/n8n-workflow/-/n8n-workflow-2.9.0.tgz",
+ "integrity": "sha512-KTaMudpmbMZh0l6X5oqnhelKllGg8XYWH/v9449L4jX9+vFcmGaqp4HcAnau/CF3H3uNZL1qP0IgTb7r834hNA==",
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@n8n/errors": "0.6.0",
+ "@n8n/tournament": "1.0.6",
+ "ast-types": "0.16.1",
+ "callsites": "3.1.0",
+ "esprima-next": "5.8.4",
+ "form-data": "4.0.4",
+ "jmespath": "0.16.0",
+ "js-base64": "3.7.2",
+ "jsonrepair": "3.13.1",
+ "jssha": "3.3.1",
+ "lodash": "4.17.23",
+ "luxon": "3.7.2",
+ "md5": "2.3.0",
+ "recast": "0.22.0",
+ "title-case": "3.0.3",
+ "transliteration": "2.3.5",
+ "xml2js": "0.6.2",
+ "zod": "3.25.67"
+ }
+ },
+ "node_modules/object-is": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
+ "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/recast": {
+ "version": "0.22.0",
+ "resolved": "https://registry.npmjs.org/recast/-/recast-0.22.0.tgz",
+ "integrity": "sha512-5AAx+mujtXijsEavc5lWXBPQqrM4+Dl5qNH96N2aNeuJFUzpiiToKPsxQD/zAIJHspz7zz0maX0PCtCTFVlixQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assert": "^2.0.0",
+ "ast-types": "0.15.2",
+ "esprima": "~4.0.0",
+ "source-map": "~0.6.1",
+ "tslib": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/recast/node_modules/ast-types": {
+ "version": "0.15.2",
+ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz",
+ "integrity": "sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/sax": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz",
+ "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=11.0.0"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/title-case": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz",
+ "integrity": "sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/transliteration": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/transliteration/-/transliteration-2.3.5.tgz",
+ "integrity": "sha512-HAGI4Lq4Q9dZ3Utu2phaWgtm3vB6PkLUFqWAScg/UW+1eZ/Tg6Exo4oC0/3VUol/w4BlefLhUUSVBr/9/ZGQOw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yargs": "^17.5.1"
+ },
+ "bin": {
+ "slugify": "dist/bin/slugify",
+ "transliterate": "dist/bin/transliterate"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/typescript": {
+ "version": "5.7.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
+ "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/util": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
+ "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "which-typed-array": "^1.1.2"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.20",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
+ "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/xml2js": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
+ "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.25.67",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz",
+ "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ }
+ }
+}
diff --git a/n8n/package.json b/n8n/package.json
new file mode 100644
index 0000000..dde07f7
--- /dev/null
+++ b/n8n/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "n8n-nodes-tinyfish",
+ "version": "0.1.5",
+ "description": "n8n community node for TinyFish Web Agent - AI-powered web automation using natural language",
+ "license": "MIT",
+ "homepage": "https://docs.mino.ai",
+ "keywords": [
+ "n8n-community-node-package",
+ "n8n",
+ "tinyfish",
+ "web-automation",
+ "browser-automation",
+ "web-scraping",
+ "ai"
+ ],
+ "author": {
+ "name": "TinyFish",
+ "email": "support@tinyfish.io"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/tinyfish-io/tinyfish-web-agent-integrations.git",
+ "directory": "n8n"
+ },
+ "main": "dist/nodes/Tinyfish/Tinyfish.node.js",
+ "scripts": {
+ "build": "tsc && cp nodes/Tinyfish/tinyfish.svg dist/nodes/Tinyfish/tinyfish.svg && cp credentials/tinyfish.svg dist/credentials/tinyfish.svg",
+ "build:watch": "tsc --watch",
+ "lint": "tsc --noEmit",
+ "prepublishOnly": "npm run build"
+ },
+ "files": [
+ "dist"
+ ],
+ "n8n": {
+ "n8nNodesApiVersion": 1,
+ "credentials": [
+ "dist/credentials/TinyfishApi.credentials.js"
+ ],
+ "nodes": [
+ "dist/nodes/Tinyfish/Tinyfish.node.js"
+ ]
+ },
+ "devDependencies": {
+ "@types/node": "^22.0.0",
+ "n8n-workflow": "*",
+ "typescript": "~5.7.0"
+ },
+ "peerDependencies": {
+ "n8n-workflow": "*"
+ }
+}
diff --git a/n8n/tsconfig.json b/n8n/tsconfig.json
new file mode 100644
index 0000000..c32a5f6
--- /dev/null
+++ b/n8n/tsconfig.json
@@ -0,0 +1,34 @@
+{
+ "compilerOptions": {
+ "strict": true,
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "target": "es2022",
+ "lib": ["es2022"],
+ "removeComments": true,
+ "forceConsistentCasingInFileNames": true,
+ "noImplicitAny": true,
+ "noImplicitReturns": true,
+ "noUnusedLocals": true,
+ "strictNullChecks": true,
+ "preserveConstEnums": true,
+ "esModuleInterop": true,
+ "resolveJsonModule": true,
+ "declaration": true,
+ "sourceMap": true,
+ "skipLibCheck": true,
+ "outDir": "./dist/"
+ },
+ "include": [
+ "credentials/**/*.ts",
+ "nodes/**/*.ts",
+ "nodes/**/*.json",
+ "package.json"
+ ],
+ "exclude": [
+ "node_modules",
+ "dist",
+ "__tests__",
+ "jest.config.js"
+ ]
+}