-
Notifications
You must be signed in to change notification settings - Fork 2
refactor: migrate n8n node to official n8n-node CLI tooling #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
e37011a
9073a50
c702e71
150c326
521d646
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| name: n8n Node CI | ||
|
|
||
| on: | ||
| pull_request: | ||
| push: | ||
| branches: [main] | ||
|
|
||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| defaults: | ||
| run: | ||
| working-directory: n8n | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '22' | ||
| cache: 'npm' | ||
| cache-dependency-path: n8n/package-lock.json | ||
|
|
||
| - run: npm ci | ||
|
|
||
| - run: npm run lint | ||
|
|
||
| - run: npm run build |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| name: Publish n8n Node to npm | ||
|
|
||
| on: | ||
| release: | ||
| types: [published] | ||
| workflow_dispatch: | ||
|
|
||
| jobs: | ||
| publish: | ||
| runs-on: ubuntu-latest | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
| defaults: | ||
| run: | ||
| working-directory: n8n | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '22' | ||
| registry-url: 'https://registry.npmjs.org' | ||
|
|
||
| - run: npm ci | ||
|
|
||
| - run: npm run lint | ||
|
|
||
| - run: npm run build | ||
|
|
||
| - run: npm publish --provenance --access public | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| module.exports = { | ||
| semi: true, | ||
| trailingComma: 'all', | ||
| bracketSpacing: true, | ||
| useTabs: true, | ||
| tabWidth: 2, | ||
| arrowParens: 'always', | ||
| singleQuote: true, | ||
| quoteProps: 'as-needed', | ||
| endOfLine: 'lf', | ||
| printWidth: 100, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| import { config } from '@n8n/node-cli/eslint'; | ||
|
|
||
| export default config; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -52,7 +52,6 @@ function getActionableMessage(error: unknown): string | undefined { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Make an authenticated request to the TinyFish API. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Retries on 429/5xx with exponential backoff (max 3 retries). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function tinyfishApiRequest( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this: IExecuteFunctions, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -134,93 +133,89 @@ export async function consumeSseStream( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!response.ok) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const errorText = await response.text(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new NodeOperationError(this.getNode(), `API request failed with status ${response.status}: ${errorText}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+137
to
+144
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: find . -type f -name "GenericFunctions.ts" | grep -i tinyfishRepository: tinyfish-io/tinyfish-web-agent-integrations Length of output: 127 🏁 Script executed: cat -n ./n8n/nodes/Tinyfish/GenericFunctions.ts | sed -n '130,150p'Repository: tinyfish-io/tinyfish-web-agent-integrations Length of output: 811 🏁 Script executed: cat -n ./n8n/nodes/Tinyfish/GenericFunctions.ts | sed -n '120,180p'Repository: tinyfish-io/tinyfish-web-agent-integrations Length of output: 2073 🏁 Script executed: cat -n ./n8n/nodes/Tinyfish/GenericFunctions.ts | sed -n '180,220p'Repository: tinyfish-io/tinyfish-web-agent-integrations Length of output: 1318 🏁 Script executed: cat -n ./n8n/nodes/Tinyfish/GenericFunctions.ts | grep -A 10 "fetch("Repository: tinyfish-io/tinyfish-web-agent-integrations Length of output: 874 🏁 Script executed: wc -l ./n8n/nodes/Tinyfish/GenericFunctions.tsRepository: tinyfish-io/tinyfish-web-agent-integrations Length of output: 131 🏁 Script executed: cat -n ./n8n/nodes/Tinyfish/GenericFunctions.ts | sed -n '155,170p'Repository: tinyfish-io/tinyfish-web-agent-integrations Length of output: 614 🏁 Script executed: grep -n "AbortController\|signal\|timeout\|setTimeout" ./n8n/nodes/Tinyfish/GenericFunctions.tsRepository: tinyfish-io/tinyfish-web-agent-integrations Length of output: 69 Add timeout protection to the SSE At Line 137, the 💡 Proposed fix+ const controller = new AbortController();
+ const timeoutMs = 60_000;
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
+
- 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),
- });
+ let response: Response;
+ try {
+ 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,
+ });
+ } catch (error) {
+ if ((error as Error).name === 'AbortError') {
+ throw new NodeOperationError(this.getNode(), `SSE request timed out after ${timeoutMs}ms`);
+ }
+ throw error;
+ } finally {
+ clearTimeout(timeoutId);
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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 = ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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() ?? ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| buffer += decoder.decode(value, { stream: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (done) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| buffer += decoder.decode(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const lines = buffer.split('\n'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| buffer = lines.pop() ?? ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
pranavjana marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const line of lines) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!line.startsWith('data: ')) continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const line of lines) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!line.startsWith('data: ')) continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let eventData: IDataObject; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eventData = JSON.parse(line.slice(6)) as IDataObject; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (done) break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return finalResult; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw error; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!finalResult) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new NodeOperationError( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.getNode(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'SSE stream ended without a COMPLETE event', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return finalResult; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
workflow_dispatchfrom a branch is silently skipped by the tag guard.When manually dispatched from a branch (e.g.,
main),github.refresolves torefs/heads/main, causing thestartsWith(github.ref, 'refs/tags/n8n-')guard to evaluate tofalse. The job is silently skipped — no error, no publish. Manual dispatch only works when the workflow is triggered from the tag ref directly (via the GitHub UI "Run workflow" drop-down with the tag selected).This is subtle enough to confuse operators expecting a manual publish to "just work" from the default branch. Consider either documenting this constraint or restructuring the guard:
🔧 Option: make workflow_dispatch always publish (bypass guard)
Also applies to: 11-11
🤖 Prompt for AI Agents