From bd3ca9e553bed2c332fa00892d515d774d719dfb Mon Sep 17 00:00:00 2001 From: Igor Karpovich Date: Mon, 8 Dec 2025 07:38:38 +0000 Subject: [PATCH] Remove example nodes and update package metadata --- README.md | 243 ++---------------- credentials/GithubIssuesApi.credentials.ts | 45 ---- .../GithubIssuesOAuth2Api.credentials.ts | 54 ---- nodes/Example/Example.node.json | 18 -- nodes/Example/Example.node.ts | 78 ------ nodes/Example/example.dark.svg | 13 - nodes/Example/example.svg | 13 - nodes/GithubIssues/GithubIssues.node.json | 18 -- nodes/GithubIssues/GithubIssues.node.ts | 96 ------- nodes/GithubIssues/listSearch/getIssues.ts | 49 ---- .../listSearch/getRepositories.ts | 50 ---- nodes/GithubIssues/listSearch/getUsers.ts | 49 ---- nodes/GithubIssues/resources/issue/create.ts | 74 ------ nodes/GithubIssues/resources/issue/get.ts | 14 - nodes/GithubIssues/resources/issue/getAll.ts | 124 --------- nodes/GithubIssues/resources/issue/index.ts | 75 ------ .../resources/issueComment/getAll.ts | 65 ----- .../resources/issueComment/index.ts | 47 ---- nodes/GithubIssues/shared/descriptions.ts | 151 ----------- nodes/GithubIssues/shared/transport.ts | 32 --- nodes/GithubIssues/shared/utils.ts | 14 - package.json | 28 +- 22 files changed, 32 insertions(+), 1318 deletions(-) delete mode 100644 credentials/GithubIssuesApi.credentials.ts delete mode 100644 credentials/GithubIssuesOAuth2Api.credentials.ts delete mode 100644 nodes/Example/Example.node.json delete mode 100644 nodes/Example/Example.node.ts delete mode 100644 nodes/Example/example.dark.svg delete mode 100644 nodes/Example/example.svg delete mode 100644 nodes/GithubIssues/GithubIssues.node.json delete mode 100644 nodes/GithubIssues/GithubIssues.node.ts delete mode 100644 nodes/GithubIssues/listSearch/getIssues.ts delete mode 100644 nodes/GithubIssues/listSearch/getRepositories.ts delete mode 100644 nodes/GithubIssues/listSearch/getUsers.ts delete mode 100644 nodes/GithubIssues/resources/issue/create.ts delete mode 100644 nodes/GithubIssues/resources/issue/get.ts delete mode 100644 nodes/GithubIssues/resources/issue/getAll.ts delete mode 100644 nodes/GithubIssues/resources/issue/index.ts delete mode 100644 nodes/GithubIssues/resources/issueComment/getAll.ts delete mode 100644 nodes/GithubIssues/resources/issueComment/index.ts delete mode 100644 nodes/GithubIssues/shared/descriptions.ts delete mode 100644 nodes/GithubIssues/shared/transport.ts delete mode 100644 nodes/GithubIssues/shared/utils.ts diff --git a/README.md b/README.md index 06ea132..70c27f7 100644 --- a/README.md +++ b/README.md @@ -1,247 +1,42 @@ -![Banner image](https://user-images.githubusercontent.com/10284570/173569848-c624317f-42b1-45a6-ab09-f0ea3c247648.png) +# n8n-nodes-wyoming -# n8n-nodes-starter +n8n community nodes for [Wyoming protocol](https://github.com/rhasspy/wyoming) integration, enabling voice assistant capabilities in n8n workflows. -This starter repository helps you build custom integrations for [n8n](https://n8n.io). It includes example nodes, credentials, the node linter, and all the tooling you need to get started. +## Features -## Quick Start +- **Speech-to-Text** - Transcribe audio using Wyoming-compatible ASR services (Whisper, etc.) -> [!TIP] -> **New to building n8n nodes?** The fastest way to get started is with `npm create @n8n/node`. This command scaffolds a complete node package for you using the [@n8n/node-cli](https://www.npmjs.com/package/@n8n/node-cli). +## Installation -**To create a new node package from scratch:** +Install via npm: ```bash -npm create @n8n/node +npm install n8n-nodes-wyoming ``` -**Already using this starter? Start developing with:** +Or install directly in n8n via the Community Nodes menu. -```bash -npm run dev -``` +## Usage -This starts n8n with your nodes loaded and hot reload enabled. +1. Add Wyoming credentials with your server host and port +2. Use the Wyoming node in your workflow +3. Connect audio input (binary data) to the node +4. Get transcription results as JSON output -## What's Included +## Supported Services -This starter repository includes two example nodes to learn from: +This node is designed to work with Wyoming protocol implementations: -- **[Example Node](nodes/Example/)** - A simple starter node that shows the basic structure with a custom `execute` method -- **[GitHub Issues Node](nodes/GithubIssues/)** - A complete, production-ready example built using the **declarative style**: - - **Low-code approach** - Define operations declaratively without writing request logic - - Multiple resources (Issues, Comments) - - Multiple operations (Get, Get All, Create) - - Two authentication methods (OAuth2 and Personal Access Token) - - List search functionality for dynamic dropdowns - - Proper error handling and typing - - Ideal for HTTP API-based integrations +- [wyoming-faster-whisper](https://github.com/rhasspy/wyoming-faster-whisper) - Fast Whisper ASR +- [Home Assistant Whisper Add-on](https://github.com/home-assistant/addons/tree/master/whisper) -> [!TIP] -> The declarative/low-code style (used in GitHub Issues) is the recommended approach for building nodes that interact with HTTP APIs. It significantly reduces boilerplate code and handles requests automatically. - -Browse these examples to understand both approaches, then modify them or create your own. - -## Finding Inspiration - -Looking for more examples? Check out these resources: - -- **[npm Community Nodes](https://www.npmjs.com/search?q=keywords:n8n-community-node-package)** - Browse thousands of community-built nodes on npm using the `n8n-community-node-package` tag -- **[n8n Built-in Nodes](https://github.com/n8n-io/n8n/tree/master/packages/nodes-base/nodes)** - Study the source code of n8n's official nodes for production-ready patterns and best practices -- **[n8n Credentials](https://github.com/n8n-io/n8n/tree/master/packages/nodes-base/credentials)** - See how authentication is implemented for various services - -These are excellent resources to understand how to structure your nodes, handle different API patterns, and implement advanced features. - -## Prerequisites - -Before you begin, install the following on your development machine: - -### Required - -- **[Node.js](https://nodejs.org/)** (v22 or higher) and npm - - Linux/Mac/WSL: Install via [nvm](https://github.com/nvm-sh/nvm) - - Windows: Follow [Microsoft's NodeJS guide](https://learn.microsoft.com/en-us/windows/dev-environment/javascript/nodejs-on-windows) -- **[git](https://git-scm.com/downloads)** - -### Recommended - -- Follow n8n's [development environment setup guide](https://docs.n8n.io/integrations/creating-nodes/build/node-development-environment/) - -> [!NOTE] -> The `@n8n/node-cli` is included as a dev dependency and will be installed automatically when you run `npm install`. The CLI includes n8n for local development, so you don't need to install n8n globally. - -## Getting Started with this Starter - -Follow these steps to create your own n8n community node package: - -### 1. Create Your Repository - -[Generate a new repository](https://github.com/n8n-io/n8n-nodes-starter/generate) from this template, then clone it: - -```bash -git clone https://github.com//.git -cd -``` - -### 2. Install Dependencies +## Development ```bash npm install -``` - -This installs all required dependencies including the `@n8n/node-cli`. - -### 3. Explore the Examples - -Browse the example nodes in [nodes/](nodes/) and [credentials/](credentials/) to understand the structure: - -- Start with [nodes/Example/](nodes/Example/) for a basic node -- Study [nodes/GithubIssues/](nodes/GithubIssues/) for a real-world implementation - -### 4. Build Your Node - -Edit the example nodes to fit your use case, or create new node files by copying the structure from [nodes/Example/](nodes/Example/). - -> [!TIP] -> If you want to scaffold a completely new node package, use `npm create @n8n/node` to start fresh with the CLI's interactive generator. - -### 5. Configure Your Package - -Update `package.json` with your details: - -- `name` - Your package name (must start with `n8n-nodes-`) -- `author` - Your name and email -- `repository` - Your repository URL -- `description` - What your node does - -Make sure your node is registered in the `n8n.nodes` array. - -### 6. Develop and Test Locally - -Start n8n with your node loaded: - -```bash npm run dev ``` -This command runs `n8n-node dev` which: - -- Builds your node with watch mode -- Starts n8n with your node available -- Automatically rebuilds when you make changes -- Opens n8n in your browser (usually http://localhost:5678) - -You can now test your node in n8n workflows! - -> [!NOTE] -> Learn more about CLI commands in the [@n8n/node-cli documentation](https://www.npmjs.com/package/@n8n/node-cli). - -### 7. Lint Your Code - -Check for errors: - -```bash -npm run lint -``` - -Auto-fix issues when possible: - -```bash -npm run lint:fix -``` - -### 8. Build for Production - -When ready to publish: - -```bash -npm run build -``` - -This compiles your TypeScript code to the `dist/` folder. - -### 9. Prepare for Publishing - -Before publishing: - -1. **Update documentation**: Replace this README with your node's documentation. Use [README_TEMPLATE.md](README_TEMPLATE.md) as a starting point. -2. **Update the LICENSE**: Add your details to the [LICENSE](LICENSE.md) file. -3. **Test thoroughly**: Ensure your node works in different scenarios. - -### 10. Publish to npm - -Publish your package to make it available to the n8n community: - -```bash -npm publish -``` - -Learn more about [publishing to npm](https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry). - -### 11. Submit for Verification (Optional) - -Get your node verified for n8n Cloud: - -1. Ensure your node meets the [requirements](https://docs.n8n.io/integrations/creating-nodes/deploy/submit-community-nodes/): - - Uses MIT license ✅ (included in this starter) - - No external package dependencies - - Follows n8n's design guidelines - - Passes quality and security review - -2. Submit through the [n8n Creator Portal](https://creators.n8n.io/nodes) - -**Benefits of verification:** - -- Available directly in n8n Cloud -- Discoverable in the n8n nodes panel -- Verified badge for quality assurance -- Increased visibility in the n8n community - -## Available Scripts - -This starter includes several npm scripts to streamline development: - -| Script | Description | -| --------------------- | ---------------------------------------------------------------- | -| `npm run dev` | Start n8n with your node and watch for changes (runs `n8n-node dev`) | -| `npm run build` | Compile TypeScript to JavaScript for production (runs `n8n-node build`) | -| `npm run build:watch` | Build in watch mode (auto-rebuild on changes) | -| `npm run lint` | Check your code for errors and style issues (runs `n8n-node lint`) | -| `npm run lint:fix` | Automatically fix linting issues when possible (runs `n8n-node lint --fix`) | -| `npm run release` | Create a new release (runs `n8n-node release`) | - -> [!TIP] -> These scripts use the [@n8n/node-cli](https://www.npmjs.com/package/@n8n/node-cli) under the hood. You can also run CLI commands directly, e.g., `npx n8n-node dev`. - -## Troubleshooting - -### My node doesn't appear in n8n - -1. Make sure you ran `npm install` to install dependencies -2. Check that your node is listed in `package.json` under `n8n.nodes` -3. Restart the dev server with `npm run dev` -4. Check the console for any error messages - -### Linting errors - -Run `npm run lint:fix` to automatically fix most common issues. For remaining errors, check the [n8n node development guidelines](https://docs.n8n.io/integrations/creating-nodes/). - -### TypeScript errors - -Make sure you're using Node.js v22 or higher and have run `npm install` to get all type definitions. - -## Resources - -- **[n8n Node Documentation](https://docs.n8n.io/integrations/creating-nodes/)** - Complete guide to building nodes -- **[n8n Community Forum](https://community.n8n.io/)** - Get help and share your nodes -- **[@n8n/node-cli Documentation](https://www.npmjs.com/package/@n8n/node-cli)** - CLI tool reference -- **[n8n Creator Portal](https://creators.n8n.io/nodes)** - Submit your node for verification -- **[Submit Community Nodes Guide](https://docs.n8n.io/integrations/creating-nodes/deploy/submit-community-nodes/)** - Verification requirements and process - -## Contributing - -Have suggestions for improving this starter? [Open an issue](https://github.com/n8n-io/n8n-nodes-starter/issues) or submit a pull request! - ## License -[MIT](https://github.com/n8n-io/n8n-nodes-starter/blob/master/LICENSE.md) +[MIT](LICENSE.md) diff --git a/credentials/GithubIssuesApi.credentials.ts b/credentials/GithubIssuesApi.credentials.ts deleted file mode 100644 index f8b267e..0000000 --- a/credentials/GithubIssuesApi.credentials.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { - IAuthenticateGeneric, - Icon, - ICredentialTestRequest, - ICredentialType, - INodeProperties, -} from 'n8n-workflow'; - -export class GithubIssuesApi implements ICredentialType { - name = 'githubIssuesApi'; - - displayName = 'GitHub Issues API'; - - icon: Icon = { light: 'file:../icons/github.svg', dark: 'file:../icons/github.dark.svg' }; - - documentationUrl = - 'https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#deleting-a-personal-access-token'; - - properties: INodeProperties[] = [ - { - displayName: 'Access Token', - name: 'accessToken', - type: 'string', - typeOptions: { password: true }, - default: '', - }, - ]; - - authenticate: IAuthenticateGeneric = { - type: 'generic', - properties: { - headers: { - Authorization: '=token {{$credentials?.accessToken}}', - }, - }, - }; - - test: ICredentialTestRequest = { - request: { - baseURL: 'https://api.github.com', - url: '/user', - method: 'GET', - }, - }; -} diff --git a/credentials/GithubIssuesOAuth2Api.credentials.ts b/credentials/GithubIssuesOAuth2Api.credentials.ts deleted file mode 100644 index 0eb98fc..0000000 --- a/credentials/GithubIssuesOAuth2Api.credentials.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { Icon, ICredentialType, INodeProperties } from 'n8n-workflow'; - -export class GithubIssuesOAuth2Api implements ICredentialType { - name = 'githubIssuesOAuth2Api'; - - extends = ['oAuth2Api']; - - displayName = 'GitHub Issues OAuth2 API'; - - icon: Icon = { light: 'file:../icons/github.svg', dark: 'file:../icons/github.dark.svg' }; - - documentationUrl = 'https://docs.github.com/en/apps/oauth-apps'; - - properties: INodeProperties[] = [ - { - displayName: 'Grant Type', - name: 'grantType', - type: 'hidden', - default: 'authorizationCode', - }, - { - displayName: 'Authorization URL', - name: 'authUrl', - type: 'hidden', - default: 'https://github.com/login/oauth/authorize', - required: true, - }, - { - displayName: 'Access Token URL', - name: 'accessTokenUrl', - type: 'hidden', - default: 'https://github.com/login/oauth/access_token', - required: true, - }, - { - displayName: 'Scope', - name: 'scope', - type: 'hidden', - default: 'repo', - }, - { - displayName: 'Auth URI Query Parameters', - name: 'authQueryParameters', - type: 'hidden', - default: '', - }, - { - displayName: 'Authentication', - name: 'authentication', - type: 'hidden', - default: 'header', - }, - ]; -} diff --git a/nodes/Example/Example.node.json b/nodes/Example/Example.node.json deleted file mode 100644 index 266250f..0000000 --- a/nodes/Example/Example.node.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "node": "n8n-nodes-example", - "nodeVersion": "1.0", - "codexVersion": "1.0", - "categories": ["Development", "Developer Tools"], - "resources": { - "credentialDocumentation": [ - { - "url": "https://github.com/org/repo?tab=readme-ov-file#credentials" - } - ], - "primaryDocumentation": [ - { - "url": "https://github.com/org/repo?tab=readme-ov-file" - } - ] - } -} diff --git a/nodes/Example/Example.node.ts b/nodes/Example/Example.node.ts deleted file mode 100644 index fb5e019..0000000 --- a/nodes/Example/Example.node.ts +++ /dev/null @@ -1,78 +0,0 @@ -import type { - IExecuteFunctions, - INodeExecutionData, - INodeType, - INodeTypeDescription, -} from 'n8n-workflow'; -import { NodeConnectionTypes, NodeOperationError } from 'n8n-workflow'; - -export class Example implements INodeType { - description: INodeTypeDescription = { - displayName: 'Example', - name: 'example', - icon: { light: 'file:example.svg', dark: 'file:example.dark.svg' }, - group: ['input'], - version: 1, - description: 'Basic Example Node', - defaults: { - name: 'Example', - }, - inputs: [NodeConnectionTypes.Main], - outputs: [NodeConnectionTypes.Main], - usableAsTool: true, - properties: [ - // Node properties which the user gets displayed and - // can change on the node. - { - displayName: 'My String', - name: 'myString', - type: 'string', - default: '', - placeholder: 'Placeholder value', - description: 'The description text', - }, - ], - }; - - // The function below is responsible for actually doing whatever this node - // is supposed to do. In this case, we're just appending the `myString` property - // with whatever the user has entered. - // You can make async calls and use `await`. - async execute(this: IExecuteFunctions): Promise { - const items = this.getInputData(); - - let item: INodeExecutionData; - let myString: string; - - // Iterates over all input items and add the key "myString" with the - // value the parameter "myString" resolves to. - // (This could be a different value for each item in case it contains an expression) - for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { - try { - myString = this.getNodeParameter('myString', itemIndex, '') as string; - item = items[itemIndex]; - - item.json.myString = myString; - } catch (error) { - // This node should never fail but we want to showcase how - // to handle errors. - if (this.continueOnFail()) { - items.push({ json: this.getInputData(itemIndex)[0].json, error, pairedItem: itemIndex }); - } else { - // Adding `itemIndex` allows other workflows to handle this error - if (error.context) { - // If the error thrown already contains the context property, - // only append the itemIndex - error.context.itemIndex = itemIndex; - throw error; - } - throw new NodeOperationError(this.getNode(), error, { - itemIndex, - }); - } - } - } - - return [items]; - } -} diff --git a/nodes/Example/example.dark.svg b/nodes/Example/example.dark.svg deleted file mode 100644 index c07cb10..0000000 --- a/nodes/Example/example.dark.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - diff --git a/nodes/Example/example.svg b/nodes/Example/example.svg deleted file mode 100644 index 703e1fe..0000000 --- a/nodes/Example/example.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - diff --git a/nodes/GithubIssues/GithubIssues.node.json b/nodes/GithubIssues/GithubIssues.node.json deleted file mode 100644 index 5eca62e..0000000 --- a/nodes/GithubIssues/GithubIssues.node.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "node": "n8n-nodes-github-issues", - "nodeVersion": "1.0", - "codexVersion": "1.0", - "categories": ["Development", "Developer Tools"], - "resources": { - "credentialDocumentation": [ - { - "url": "https://github.com/org/repo?tab=readme-ov-file#credentials" - } - ], - "primaryDocumentation": [ - { - "url": "https://github.com/org/repo?tab=readme-ov-file" - } - ] - } -} diff --git a/nodes/GithubIssues/GithubIssues.node.ts b/nodes/GithubIssues/GithubIssues.node.ts deleted file mode 100644 index 9965864..0000000 --- a/nodes/GithubIssues/GithubIssues.node.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { NodeConnectionTypes, type INodeType, type INodeTypeDescription } from 'n8n-workflow'; -import { issueDescription } from './resources/issue'; -import { issueCommentDescription } from './resources/issueComment'; -import { getRepositories } from './listSearch/getRepositories'; -import { getUsers } from './listSearch/getUsers'; -import { getIssues } from './listSearch/getIssues'; - -export class GithubIssues implements INodeType { - description: INodeTypeDescription = { - displayName: 'GitHub Issues', - name: 'githubIssues', - icon: { light: 'file:../../icons/github.svg', dark: 'file:../../icons/github.dark.svg' }, - group: ['input'], - version: 1, - subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', - description: 'Consume issues from the GitHub API', - defaults: { - name: 'GitHub Issues', - }, - usableAsTool: true, - inputs: [NodeConnectionTypes.Main], - outputs: [NodeConnectionTypes.Main], - credentials: [ - { - name: 'githubIssuesApi', - required: true, - displayOptions: { - show: { - authentication: ['accessToken'], - }, - }, - }, - { - name: 'githubIssuesOAuth2Api', - required: true, - displayOptions: { - show: { - authentication: ['oAuth2'], - }, - }, - }, - ], - requestDefaults: { - baseURL: 'https://api.github.com', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - }, - properties: [ - { - displayName: 'Authentication', - name: 'authentication', - type: 'options', - options: [ - { - name: 'Access Token', - value: 'accessToken', - }, - { - name: 'OAuth2', - value: 'oAuth2', - }, - ], - default: 'accessToken', - }, - { - displayName: 'Resource', - name: 'resource', - type: 'options', - noDataExpression: true, - options: [ - { - name: 'Issue', - value: 'issue', - }, - { - name: 'Issue Comment', - value: 'issueComment', - }, - ], - default: 'issue', - }, - ...issueDescription, - ...issueCommentDescription, - ], - }; - - methods = { - listSearch: { - getRepositories, - getUsers, - getIssues, - }, - }; -} diff --git a/nodes/GithubIssues/listSearch/getIssues.ts b/nodes/GithubIssues/listSearch/getIssues.ts deleted file mode 100644 index f340b03..0000000 --- a/nodes/GithubIssues/listSearch/getIssues.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { - ILoadOptionsFunctions, - INodeListSearchResult, - INodeListSearchItems, -} from 'n8n-workflow'; -import { githubApiRequest } from '../shared/transport'; - -type IssueSearchItem = { - number: number; - title: string; - html_url: string; -}; - -type IssueSearchResponse = { - items: IssueSearchItem[]; - total_count: number; -}; - -export async function getIssues( - this: ILoadOptionsFunctions, - filter?: string, - paginationToken?: string, -): Promise { - const page = paginationToken ? +paginationToken : 1; - const per_page = 100; - - let responseData: IssueSearchResponse = { - items: [], - total_count: 0, - }; - const owner = this.getNodeParameter('owner', '', { extractValue: true }); - const repository = this.getNodeParameter('repository', '', { extractValue: true }); - const filters = [filter, `repo:${owner}/${repository}`]; - - responseData = await githubApiRequest.call(this, 'GET', '/search/issues', { - q: filters.filter(Boolean).join(' '), - page, - per_page, - }); - - const results: INodeListSearchItems[] = responseData.items.map((item: IssueSearchItem) => ({ - name: item.title, - value: item.number, - url: item.html_url, - })); - - const nextPaginationToken = page * per_page < responseData.total_count ? page + 1 : undefined; - return { results, paginationToken: nextPaginationToken }; -} diff --git a/nodes/GithubIssues/listSearch/getRepositories.ts b/nodes/GithubIssues/listSearch/getRepositories.ts deleted file mode 100644 index 9f5a6b1..0000000 --- a/nodes/GithubIssues/listSearch/getRepositories.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { - ILoadOptionsFunctions, - INodeListSearchItems, - INodeListSearchResult, -} from 'n8n-workflow'; -import { githubApiRequest } from '../shared/transport'; - -type RepositorySearchItem = { - name: string; - html_url: string; -}; - -type RepositorySearchResponse = { - items: RepositorySearchItem[]; - total_count: number; -}; - -export async function getRepositories( - this: ILoadOptionsFunctions, - filter?: string, - paginationToken?: string, -): Promise { - const owner = this.getCurrentNodeParameter('owner', { extractValue: true }); - const page = paginationToken ? +paginationToken : 1; - const per_page = 100; - const q = `${filter ?? ''} user:${owner} fork:true`; - let responseData: RepositorySearchResponse = { - items: [], - total_count: 0, - }; - - try { - responseData = await githubApiRequest.call(this, 'GET', '/search/repositories', { - q, - page, - per_page, - }); - } catch { - // will fail if the owner does not have any repositories - } - - const results: INodeListSearchItems[] = responseData.items.map((item: RepositorySearchItem) => ({ - name: item.name, - value: item.name, - url: item.html_url, - })); - - const nextPaginationToken = page * per_page < responseData.total_count ? page + 1 : undefined; - return { results, paginationToken: nextPaginationToken }; -} diff --git a/nodes/GithubIssues/listSearch/getUsers.ts b/nodes/GithubIssues/listSearch/getUsers.ts deleted file mode 100644 index d8e0853..0000000 --- a/nodes/GithubIssues/listSearch/getUsers.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { - ILoadOptionsFunctions, - INodeListSearchResult, - INodeListSearchItems, -} from 'n8n-workflow'; -import { githubApiRequest } from '../shared/transport'; - -type UserSearchItem = { - login: string; - html_url: string; -}; - -type UserSearchResponse = { - items: UserSearchItem[]; - total_count: number; -}; - -export async function getUsers( - this: ILoadOptionsFunctions, - filter?: string, - paginationToken?: string, -): Promise { - const page = paginationToken ? +paginationToken : 1; - const per_page = 100; - - let responseData: UserSearchResponse = { - items: [], - total_count: 0, - }; - - try { - responseData = await githubApiRequest.call(this, 'GET', '/search/users', { - q: filter, - page, - per_page, - }); - } catch { - // will fail if the owner does not have any users - } - - const results: INodeListSearchItems[] = responseData.items.map((item: UserSearchItem) => ({ - name: item.login, - value: item.login, - url: item.html_url, - })); - - const nextPaginationToken = page * per_page < responseData.total_count ? page + 1 : undefined; - return { results, paginationToken: nextPaginationToken }; -} diff --git a/nodes/GithubIssues/resources/issue/create.ts b/nodes/GithubIssues/resources/issue/create.ts deleted file mode 100644 index 4c7fd7e..0000000 --- a/nodes/GithubIssues/resources/issue/create.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type { INodeProperties } from 'n8n-workflow'; - -const showOnlyForIssueCreate = { - operation: ['create'], - resource: ['issue'], -}; - -export const issueCreateDescription: INodeProperties[] = [ - { - displayName: 'Title', - name: 'title', - type: 'string', - default: '', - required: true, - displayOptions: { - show: showOnlyForIssueCreate, - }, - description: 'The title of the issue', - routing: { - send: { - type: 'body', - property: 'title', - }, - }, - }, - { - displayName: 'Body', - name: 'body', - type: 'string', - typeOptions: { - rows: 5, - }, - default: '', - displayOptions: { - show: showOnlyForIssueCreate, - }, - description: 'The body of the issue', - routing: { - send: { - type: 'body', - property: 'body', - }, - }, - }, - { - displayName: 'Labels', - name: 'labels', - type: 'collection', - typeOptions: { - multipleValues: true, - multipleValueButtonText: 'Add Label', - }, - displayOptions: { - show: showOnlyForIssueCreate, - }, - default: { label: '' }, - options: [ - { - displayName: 'Label', - name: 'label', - type: 'string', - default: '', - description: 'Label to add to issue', - }, - ], - routing: { - send: { - type: 'body', - property: 'labels', - value: '={{$value.map((data) => data.label)}}', - }, - }, - }, -]; diff --git a/nodes/GithubIssues/resources/issue/get.ts b/nodes/GithubIssues/resources/issue/get.ts deleted file mode 100644 index 7dc7181..0000000 --- a/nodes/GithubIssues/resources/issue/get.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { INodeProperties } from 'n8n-workflow'; -import { issueSelect } from '../../shared/descriptions'; - -const showOnlyForIssueGet = { - operation: ['get'], - resource: ['issue'], -}; - -export const issueGetDescription: INodeProperties[] = [ - { - ...issueSelect, - displayOptions: { show: showOnlyForIssueGet }, - }, -]; diff --git a/nodes/GithubIssues/resources/issue/getAll.ts b/nodes/GithubIssues/resources/issue/getAll.ts deleted file mode 100644 index b5b5fed..0000000 --- a/nodes/GithubIssues/resources/issue/getAll.ts +++ /dev/null @@ -1,124 +0,0 @@ -import type { INodeProperties } from 'n8n-workflow'; -import { parseLinkHeader } from '../../shared/utils'; - -const showOnlyForIssueGetMany = { - operation: ['getAll'], - resource: ['issue'], -}; - -export const issueGetManyDescription: INodeProperties[] = [ - { - displayName: 'Limit', - name: 'limit', - type: 'number', - displayOptions: { - show: { - ...showOnlyForIssueGetMany, - returnAll: [false], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 100, - }, - default: 50, - routing: { - send: { - type: 'query', - property: 'per_page', - }, - output: { - maxResults: '={{$value}}', - }, - }, - description: 'Max number of results to return', - }, - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: showOnlyForIssueGetMany, - }, - default: false, - description: 'Whether to return all results or only up to a given limit', - routing: { - send: { - paginate: '={{ $value }}', - type: 'query', - property: 'per_page', - value: '100', - }, - operations: { - pagination: { - type: 'generic', - properties: { - continue: `={{ !!(${parseLinkHeader.toString()})($response.headers?.link).next }}`, - request: { - url: `={{ (${parseLinkHeader.toString()})($response.headers?.link)?.next ?? $request.url }}`, - }, - }, - }, - }, - }, - }, - { - displayName: 'Filters', - name: 'filters', - type: 'collection', - typeOptions: { - multipleValueButtonText: 'Add Filter', - }, - displayOptions: { - show: showOnlyForIssueGetMany, - }, - default: {}, - options: [ - { - displayName: 'Updated Since', - name: 'since', - type: 'dateTime', - default: '', - description: 'Return only issues updated at or after this time', - routing: { - request: { - qs: { - since: '={{$value}}', - }, - }, - }, - }, - { - displayName: 'State', - name: 'state', - type: 'options', - options: [ - { - name: 'All', - value: 'all', - description: 'Returns issues with any state', - }, - { - name: 'Closed', - value: 'closed', - description: 'Return issues with "closed" state', - }, - { - name: 'Open', - value: 'open', - description: 'Return issues with "open" state', - }, - ], - default: 'open', - description: 'The issue state to filter on', - routing: { - request: { - qs: { - state: '={{$value}}', - }, - }, - }, - }, - ], - }, -]; diff --git a/nodes/GithubIssues/resources/issue/index.ts b/nodes/GithubIssues/resources/issue/index.ts deleted file mode 100644 index 6c915d2..0000000 --- a/nodes/GithubIssues/resources/issue/index.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { INodeProperties } from 'n8n-workflow'; -import { repoNameSelect, repoOwnerSelect } from '../../shared/descriptions'; -import { issueGetManyDescription } from './getAll'; -import { issueGetDescription } from './get'; -import { issueCreateDescription } from './create'; - -const showOnlyForIssues = { - resource: ['issue'], -}; - -export const issueDescription: INodeProperties[] = [ - { - displayName: 'Operation', - name: 'operation', - type: 'options', - noDataExpression: true, - displayOptions: { - show: showOnlyForIssues, - }, - options: [ - { - name: 'Get Many', - value: 'getAll', - action: 'Get issues in a repository', - description: 'Get many issues in a repository', - routing: { - request: { - method: 'GET', - url: '=/repos/{{$parameter.owner}}/{{$parameter.repository}}/issues', - }, - }, - }, - { - name: 'Get', - value: 'get', - action: 'Get an issue', - description: 'Get the data of a single issue', - routing: { - request: { - method: 'GET', - url: '=/repos/{{$parameter.owner}}/{{$parameter.repository}}/issues/{{$parameter.issue}}', - }, - }, - }, - { - name: 'Create', - value: 'create', - action: 'Create a new issue', - description: 'Create a new issue', - routing: { - request: { - method: 'POST', - url: '=/repos/{{$parameter.owner}}/{{$parameter.repository}}/issues', - }, - }, - }, - ], - default: 'getAll', - }, - { - ...repoOwnerSelect, - displayOptions: { - show: showOnlyForIssues, - }, - }, - { - ...repoNameSelect, - displayOptions: { - show: showOnlyForIssues, - }, - }, - ...issueGetManyDescription, - ...issueGetDescription, - ...issueCreateDescription, -]; diff --git a/nodes/GithubIssues/resources/issueComment/getAll.ts b/nodes/GithubIssues/resources/issueComment/getAll.ts deleted file mode 100644 index 53b2057..0000000 --- a/nodes/GithubIssues/resources/issueComment/getAll.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { INodeProperties } from 'n8n-workflow'; -import { parseLinkHeader } from '../../shared/utils'; - -const showOnlyForIssueCommentGetMany = { - operation: ['getAll'], - resource: ['issueComment'], -}; - -export const issueCommentGetManyDescription: INodeProperties[] = [ - { - displayName: 'Limit', - name: 'limit', - type: 'number', - displayOptions: { - show: { - ...showOnlyForIssueCommentGetMany, - returnAll: [false], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 100, - }, - default: 50, - routing: { - send: { - type: 'query', - property: 'per_page', - }, - output: { - maxResults: '={{$value}}', - }, - }, - description: 'Max number of results to return', - }, - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: showOnlyForIssueCommentGetMany, - }, - default: false, - description: 'Whether to return all results or only up to a given limit', - routing: { - send: { - paginate: '={{ $value }}', - type: 'query', - property: 'per_page', - value: '100', - }, - operations: { - pagination: { - type: 'generic', - properties: { - continue: `={{ !!(${parseLinkHeader.toString()})($response.headers?.link).next }}`, - request: { - url: `={{ (${parseLinkHeader.toString()})($response.headers?.link)?.next ?? $request.url }}`, - }, - }, - }, - }, - }, - }, -]; diff --git a/nodes/GithubIssues/resources/issueComment/index.ts b/nodes/GithubIssues/resources/issueComment/index.ts deleted file mode 100644 index 886f7c2..0000000 --- a/nodes/GithubIssues/resources/issueComment/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { INodeProperties } from 'n8n-workflow'; -import { repoNameSelect, repoOwnerSelect } from '../../shared/descriptions'; -import { issueCommentGetManyDescription } from './getAll'; - -const showOnlyForIssueComments = { - resource: ['issueComment'], -}; - -export const issueCommentDescription: INodeProperties[] = [ - { - displayName: 'Operation', - name: 'operation', - type: 'options', - noDataExpression: true, - displayOptions: { - show: showOnlyForIssueComments, - }, - options: [ - { - name: 'Get Many', - value: 'getAll', - action: 'Get issue comments', - description: 'Get issue comments', - routing: { - request: { - method: 'GET', - url: '=/repos/{{$parameter.owner}}/{{$parameter.repository}}/issues/comments', - }, - }, - }, - ], - default: 'getAll', - }, - { - ...repoOwnerSelect, - displayOptions: { - show: showOnlyForIssueComments, - }, - }, - { - ...repoNameSelect, - displayOptions: { - show: showOnlyForIssueComments, - }, - }, - ...issueCommentGetManyDescription, -]; diff --git a/nodes/GithubIssues/shared/descriptions.ts b/nodes/GithubIssues/shared/descriptions.ts deleted file mode 100644 index aaeaaa8..0000000 --- a/nodes/GithubIssues/shared/descriptions.ts +++ /dev/null @@ -1,151 +0,0 @@ -import type { INodeProperties } from 'n8n-workflow'; - -export const repoOwnerSelect: INodeProperties = { - displayName: 'Repository Owner', - name: 'owner', - type: 'resourceLocator', - default: { mode: 'list', value: '' }, - required: true, - modes: [ - { - displayName: 'Repository Owner', - name: 'list', - type: 'list', - placeholder: 'Select an owner...', - typeOptions: { - searchListMethod: 'getUsers', - searchable: true, - searchFilterRequired: false, - }, - }, - { - displayName: 'Link', - name: 'url', - type: 'string', - placeholder: 'e.g. https://github.com/n8n-io', - extractValue: { - type: 'regex', - regex: 'https:\\/\\/github.com\\/([-_0-9a-zA-Z]+)', - }, - validation: [ - { - type: 'regex', - properties: { - regex: 'https:\\/\\/github.com\\/([-_0-9a-zA-Z]+)(?:.*)', - errorMessage: 'Not a valid GitHub URL', - }, - }, - ], - }, - { - displayName: 'By Name', - name: 'name', - type: 'string', - placeholder: 'e.g. n8n-io', - validation: [ - { - type: 'regex', - properties: { - regex: '[-_a-zA-Z0-9]+', - errorMessage: 'Not a valid GitHub Owner Name', - }, - }, - ], - url: '=https://github.com/{{$value}}', - }, - ], -}; - -export const repoNameSelect: INodeProperties = { - displayName: 'Repository Name', - name: 'repository', - type: 'resourceLocator', - default: { - mode: 'list', - value: '', - }, - required: true, - modes: [ - { - displayName: 'Repository Name', - name: 'list', - type: 'list', - placeholder: 'Select an Repository...', - typeOptions: { - searchListMethod: 'getRepositories', - searchable: true, - }, - }, - { - displayName: 'Link', - name: 'url', - type: 'string', - placeholder: 'e.g. https://github.com/n8n-io/n8n', - extractValue: { - type: 'regex', - regex: 'https:\\/\\/github.com\\/(?:[-_0-9a-zA-Z]+)\\/([-_.0-9a-zA-Z]+)', - }, - validation: [ - { - type: 'regex', - properties: { - regex: 'https:\\/\\/github.com\\/(?:[-_0-9a-zA-Z]+)\\/([-_.0-9a-zA-Z]+)(?:.*)', - errorMessage: 'Not a valid GitHub Repository URL', - }, - }, - ], - }, - { - displayName: 'By Name', - name: 'name', - type: 'string', - placeholder: 'e.g. n8n', - validation: [ - { - type: 'regex', - properties: { - regex: '[-_.0-9a-zA-Z]+', - errorMessage: 'Not a valid GitHub Repository Name', - }, - }, - ], - url: '=https://github.com/{{$parameter["owner"]}}/{{$value}}', - }, - ], - displayOptions: { - hide: { - resource: ['user', 'organization'], - operation: ['getRepositories'], - }, - }, -}; - -export const issueSelect: INodeProperties = { - displayName: 'Issue', - name: 'issue', - type: 'resourceLocator', - default: { - mode: 'list', - value: '', - }, - required: true, - modes: [ - { - displayName: 'Issue', - name: 'list', - type: 'list', - placeholder: 'Select an Issue...', - typeOptions: { - searchListMethod: 'getIssues', - searchable: true, - }, - }, - { - displayName: 'By ID', - name: 'name', - type: 'string', - placeholder: 'e.g. 123', - url: '=https://github.com/{{$parameter.owner}}/{{$parameter.repository}}/issues/{{$value}}', - }, - ], -}; diff --git a/nodes/GithubIssues/shared/transport.ts b/nodes/GithubIssues/shared/transport.ts deleted file mode 100644 index 3555ee0..0000000 --- a/nodes/GithubIssues/shared/transport.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { - IHookFunctions, - IExecuteFunctions, - IExecuteSingleFunctions, - ILoadOptionsFunctions, - IHttpRequestMethods, - IDataObject, - IHttpRequestOptions, -} from 'n8n-workflow'; - -export async function githubApiRequest( - this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, - method: IHttpRequestMethods, - resource: string, - qs: IDataObject = {}, - body: IDataObject | undefined = undefined, -) { - const authenticationMethod = this.getNodeParameter('authentication', 0); - - const options: IHttpRequestOptions = { - method: method, - qs, - body, - url: `https://api.github.com${resource}`, - json: true, - }; - - const credentialType = - authenticationMethod === 'accessToken' ? 'githubIssuesApi' : 'githubIssuesOAuth2Api'; - - return this.helpers.httpRequestWithAuthentication.call(this, credentialType, options); -} diff --git a/nodes/GithubIssues/shared/utils.ts b/nodes/GithubIssues/shared/utils.ts deleted file mode 100644 index 2b91882..0000000 --- a/nodes/GithubIssues/shared/utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -export function parseLinkHeader(header?: string): { [rel: string]: string } { - const links: { [rel: string]: string } = {}; - - for (const part of header?.split(',') ?? []) { - const section = part.trim(); - const match = section.match(/^<([^>]+)>\s*;\s*rel="?([^"]+)"?/); - if (match) { - const [, url, rel] = match; - links[rel] = url; - } - } - - return links; -} diff --git a/package.json b/package.json index 66b8c45..8552a7a 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,23 @@ { - "name": "n8n-nodes-<...>", + "name": "n8n-nodes-wyoming", "version": "0.1.0", - "description": "", + "description": "n8n community nodes for Wyoming protocol (voice assistant integration)", "license": "MIT", - "homepage": "", + "homepage": "https://github.com/ikarpovich/n8n-nodes-wyoming", "keywords": [ - "n8n-community-node-package" + "n8n-community-node-package", + "wyoming", + "voice-assistant", + "speech-to-text", + "whisper" ], "author": { - "name": "", - "email": "" + "name": "Igor Karpovich", + "email": "info@zatupilki.click" }, "repository": { "type": "git", - "url": "https://github.com/<...>/n8n-nodes-<...>.git" + "url": "https://github.com/ikarpovich/n8n-nodes-wyoming.git" }, "scripts": { "build": "n8n-node build", @@ -30,14 +34,8 @@ "n8n": { "n8nNodesApiVersion": 1, "strict": true, - "credentials": [ - "dist/credentials/GithubIssuesApi.credentials.js", - "dist/credentials/GithubIssuesOAuth2Api.credentials.js" - ], - "nodes": [ - "dist/nodes/GithubIssues/GithubIssues.node.js", - "dist/nodes/Example/Example.node.js" - ] + "credentials": [], + "nodes": [] }, "devDependencies": { "@n8n/node-cli": "*",