@lhi/n8m 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,369 @@
1
+ # n8n Node Reference for AI Models
2
+
3
+ This document serves as a static reference for common n8n nodes when the live
4
+ fetch from the n8n instance fails. It covers some of the most frequently used
5
+ nodes and their parameters.
6
+
7
+ ## Core Nodes
8
+
9
+ ### 1. Manual Trigger (n8n-nodes-base.start)
10
+
11
+ - **Description**: The starting point for manual execution.
12
+ - **Parameters**: (None usually required)
13
+
14
+ ### 2. HTTP Request (n8n-nodes-base.httpRequest)
15
+
16
+ - **Description**: Send HTTP requests to any API.
17
+ - **Key Parameters**:
18
+ - `method`: HTTP method (GET, POST, PUT, DELETE, etc.)
19
+ - `url`: The endpoint URL.
20
+ - `authentication`: "none", "predefinedCredentialType".
21
+ - `sendBody`: (boolean) Whether to send a body (required for POST/PUT).
22
+ - `body`: { `contentType`: "json"|"form-data"|"binary", `content`: { ... } }
23
+ - `sendHeaders`: (boolean)
24
+ - `headerParameters`: List of { name, value }.
25
+
26
+ ### 3. Slack (n8n-nodes-base.slack)
27
+
28
+ - **Description**: Send messages, manage channels, users, files, and reactions in Slack.
29
+ - **Resources & Operations**:
30
+ - `message`: post | update | delete | get | getAll | search
31
+ - `channel`: create | archive | unarchive | get | getAll | history | invite | join | kick | leave | open | rename | replies | setPurpose | setTopic
32
+ - `file`: upload | get | getAll
33
+ - `reaction`: add | remove | get
34
+ - `user`: get | getAll | getPresence
35
+ - `userGroup`: create | disable | enable | get | getAll
36
+ - **Core Parameters** (message.post):
37
+ - `channel`: Channel name (`#general`) or ID (`C1234567890`). Required.
38
+ - `text`: Plain-text fallback. Required when no blocks/attachments present. Supports mrkdwn if `otherOptions.mrkdwn=true`.
39
+ - `blocksUi`: **Block Kit blocks** as a JSON array string. See Block Kit section below.
40
+ - `attachments`: Legacy Slack attachment array as JSON string. Prefer `blocksUi` for new workflows.
41
+ - **otherOptions** (collection, all optional):
42
+ - `thread_ts`: Timestamp of parent message — posts as a thread reply. Source from `$json.ts` on a previous Slack response.
43
+ - `reply_broadcast`: `true` to also broadcast a thread reply to the channel.
44
+ - `mrkdwn`: `true` to enable Markdown in `text`.
45
+ - `username`: Override the bot display name for this post.
46
+ - `icon_emoji`: Override bot icon with an emoji (`:robot_face:`).
47
+ - `icon_url`: Override bot icon with an image URL.
48
+ - `unfurl_links` / `unfurl_media`: Control link/media previews.
49
+
50
+ #### Block Kit (`blocksUi`) — CRITICAL NOTES
51
+
52
+ `blocksUi` must be a **valid JSON array string** at runtime. n8n parses it with
53
+ `JSON.parse()` — if the expression produces anything invalid the node throws
54
+ `"Parameter 'blocksUi' could not be parsed"`.
55
+
56
+ **Rules:**
57
+ 1. The entire value must be a JSON array: `[{...}, {...}]`
58
+ 2. n8n expressions (`={{ ... }}`) go **inside string values**, never at the array wrapper level.
59
+ 3. If a referenced field might be `null`/`undefined`, guard it: `={{ $json.body.content ?? '' }}`
60
+ 4. Keep block text under 3000 chars. Max 50 blocks per message.
61
+
62
+ **Supported block types**: `section`, `header`, `divider`, `image`, `actions`, `context`, `input`
63
+
64
+ **Minimal working examples:**
65
+
66
+ ```json
67
+ // Simple text section
68
+ [{"type":"section","text":{"type":"mrkdwn","text":"={{ $json.body.content }}"}}]
69
+
70
+ // Header + body
71
+ [
72
+ {"type":"header","text":{"type":"plain_text","text":"={{ $json.body.title }}","emoji":true}},
73
+ {"type":"section","text":{"type":"mrkdwn","text":"={{ $json.body.content }}"}},
74
+ {"type":"divider"}
75
+ ]
76
+
77
+ // Section with two fields side-by-side
78
+ [{"type":"section","fields":[
79
+ {"type":"mrkdwn","text":"*Status:*\n={{ $json.status }}"},
80
+ {"type":"mrkdwn","text":"*Priority:*\n={{ $json.priority }}"}
81
+ ]}]
82
+
83
+ // Action buttons
84
+ [{"type":"actions","elements":[
85
+ {"type":"button","text":{"type":"plain_text","text":"Approve"},"style":"primary","action_id":"approve","value":"={{ $json.id }}"},
86
+ {"type":"button","text":{"type":"plain_text","text":"Reject"},"style":"danger","action_id":"reject","value":"={{ $json.id }}"}
87
+ ]}]
88
+
89
+ // Context (small grey text)
90
+ [{"type":"context","elements":[{"type":"mrkdwn","text":"Posted by n8m • {{ $now.toISO() }}"}]}]
91
+ ```
92
+
93
+ **Thread reply example:**
94
+ ```json
95
+ {
96
+ "resource": "message",
97
+ "operation": "post",
98
+ "channel": "#alerts",
99
+ "text": "={{ $json.summary }}",
100
+ "otherOptions": {
101
+ "thread_ts": "={{ $json.ts }}",
102
+ "reply_broadcast": false
103
+ }
104
+ }
105
+ ```
106
+
107
+ #### Trigger (n8n-nodes-base.slackTrigger)
108
+
109
+ Receives Slack events via webhooks. Key `triggerOn` values:
110
+ - `message` — new message in a channel
111
+ - `appMention` — bot is @mentioned
112
+ - `reactionAdded` / `reactionRemoved`
113
+ - `teamJoin` — new user joins workspace
114
+ - `channelCreated` — new public channel
115
+
116
+ ### 4. Set (n8n-nodes-base.set)
117
+
118
+ - **Description**: Manipulate data or create new variables.
119
+ - **Key Parameters**:
120
+ - `values`: Array of { `name`, `type` (string/number/boolean), `value` }.
121
+ - `options`: { `keepOnlySet`: boolean }.
122
+
123
+ ### 5. IF (n8n-nodes-base.if)
124
+
125
+ - **Description**: Conditional logic (True/False branches).
126
+ - **Key Parameters**:
127
+ - `conditions`: { `string`|`number`|`boolean`: [ { `value1`, `operation`,
128
+ `value2` } ] }
129
+ - Common operations: `equals`, `contains`, `isEmpty`, `isTrue`.
130
+
131
+ ### 6. Webhook (n8n-nodes-base.webhook)
132
+
133
+ - **Description**: Receive external HTTP requests.
134
+ - **Key Parameters**:
135
+ - `httpMethod`: GET, POST, etc.
136
+ - `path`: URL path.
137
+ - `responseMode`: "onReceived", "lastNode".
138
+
139
+ ### 7. Google Sheets (n8n-nodes-base.googleSheets)
140
+
141
+ - **Description**: Read/Write data to Google Sheets.
142
+ - **Key Parameters**:
143
+ - `resource`: "sheet".
144
+ - `operation`: "append", "update", "read".
145
+ - `sheetId`: Spreadsheet ID.
146
+ - `range`: Sheet name and range (e.g., "Sheet1!A:Z").
147
+
148
+ ### 8. Code (n8n-nodes-base.code)
149
+
150
+ - **Description**: Run arbitrary JavaScript/TypeScript.
151
+ - **Key Parameters**:
152
+ - `language`: "javaScript" or "typeScript".
153
+ - `jsCode`: The code string.
154
+
155
+ ### 9. Schedule Trigger (n8n-nodes-base.scheduleTrigger)
156
+
157
+ - **Description**: Triggers a workflow on a time-based schedule (cron).
158
+ - **Key Parameters**:
159
+ - `rule`: Object with `interval` array. Each entry has:
160
+ - `field`: `"cronExpression"` | `"hours"` | `"days"` | `"weeks"` | `"months"`
161
+ - `expression`: cron string when `field` is `"cronExpression"` (e.g. `"0 9 * * 1-5"`)
162
+ - `hoursInterval` / `minutesInterval`: numeric intervals for simpler schedules
163
+ - **Notes**: Use `cronExpression` for precise schedules (e.g. every weekday at 9 AM). Do NOT use `n8n-nodes-base.cron` — it is deprecated.
164
+
165
+ ### 10. Execute Workflow (n8n-nodes-base.executeWorkflow)
166
+
167
+ - **Description**: Call another n8n workflow as a sub-workflow and get its output.
168
+ - **Key Parameters**:
169
+ - `source`: `"database"` (reference by ID) or `"localFile"` (path to JSON).
170
+ - `workflowId`: ID of the workflow to call (when `source` is `"database"`).
171
+ - `options.waitForSubWorkflow`: `true` to wait for the sub-workflow to finish (default).
172
+
173
+ ### 11. Execute Workflow Trigger (n8n-nodes-base.executeWorkflowTrigger)
174
+
175
+ - **Description**: The trigger node that receives data when another workflow calls this one via Execute Workflow.
176
+ - **Parameters**: None required. The node receives whatever data the calling workflow sends.
177
+ - **Notes**: Use this as the start node in sub-workflows. Do NOT pair it with `n8n-nodes-base.start` in the same workflow.
178
+
179
+ ### 12. Merge (n8n-nodes-base.merge)
180
+
181
+ - **Description**: Combine data from multiple input branches.
182
+ - **Key Parameters**:
183
+ - `mode`: `"append"` | `"mergeByIndex"` | `"mergeByKey"` | `"multiplex"` | `"passThrough"` | `"wait"`
184
+ - `propertyName1` / `propertyName2`: Key fields for `"mergeByKey"` mode.
185
+ - **Common use**: `"append"` to collect results from parallel branches; `"mergeByKey"` to join datasets on a shared field.
186
+
187
+ ### 13. Switch (n8n-nodes-base.switch)
188
+
189
+ - **Description**: Route items to different outputs based on a value (multi-branch IF).
190
+ - **Key Parameters**:
191
+ - `dataType`: `"string"` | `"number"` | `"boolean"`
192
+ - `value1`: The value to compare (expression).
193
+ - `rules.rules`: Array of `{ value2, outputKey }` pairs.
194
+ - `fallbackOutput`: Index to route non-matching items (default: ignored).
195
+
196
+ ### 14. Loop Over Items (n8n-nodes-base.splitInBatches)
197
+
198
+ - **Description**: Iterate over a list in chunks, re-running downstream nodes for each batch.
199
+ - **Key Parameters**:
200
+ - `batchSize`: Number of items per loop iteration.
201
+ - `options.reset`: `true` to reset the counter on the first run.
202
+ - **Notes**: Connect the "loop" output back to the nodes you want to repeat. Connect the "done" output to what runs after the loop.
203
+
204
+ ### 15. Wait (n8n-nodes-base.wait)
205
+
206
+ - **Description**: Pause execution and resume on a webhook call, a time delay, or a specific date/time.
207
+ - **Key Parameters**:
208
+ - `resume`: `"timeInterval"` | `"specificTime"` | `"webhook"`
209
+ - `amount` + `unit`: For `"timeInterval"` (e.g. `amount: 5`, `unit: "minutes"`).
210
+ - `dateTime`: ISO string for `"specificTime"`.
211
+
212
+ ### 16. Respond to Webhook (n8n-nodes-base.respondToWebhook)
213
+
214
+ - **Description**: Send an HTTP response back to the caller of a Webhook node.
215
+ - **Key Parameters**:
216
+ - `respondWith`: `"text"` | `"json"` | `"binary"` | `"noData"` | `"redirect"`
217
+ - `responseBody`: The body to return (for `"text"` or `"json"`).
218
+ - `responseCode`: HTTP status code (default `200`).
219
+ - **Notes**: Only valid when the upstream Webhook node has `responseMode: "lastNode"`. Must be the final node in the chain.
220
+
221
+ ### 17. Gmail (n8n-nodes-base.gmail)
222
+
223
+ - **Description**: Send, receive, and manage Gmail messages and labels.
224
+ - **Resources & Operations**:
225
+ - `message`: `send` | `get` | `getAll` | `delete` | `reply` | `addLabels` | `removeLabels` | `markAsRead` | `markAsUnread`
226
+ - `label`: `create` | `delete` | `get` | `getAll`
227
+ - **Core send parameters**:
228
+ - `sendTo`: Recipient email address.
229
+ - `subject`: Email subject.
230
+ - `message`: Body text (HTML supported when `emailType: "html"`).
231
+ - `options.attachmentsUi`: List of binary field names to attach.
232
+
233
+ ### 18. Gmail Trigger (n8n-nodes-base.gmailTrigger)
234
+
235
+ - **Description**: Polls Gmail and triggers when new messages arrive matching a filter.
236
+ - **Key Parameters**:
237
+ - `filters.labelIds`: Array of Gmail label IDs to watch (e.g. `["INBOX", "UNREAD"]`).
238
+ - `pollTime.mode`: `"everyMinute"` | `"everyHour"` | `"custom"` (cron).
239
+ - `simple`: `true` to return simplified output; `false` for raw Gmail API response.
240
+
241
+ ### 19. Postgres (n8n-nodes-base.postgres)
242
+
243
+ - **Description**: Execute queries against a PostgreSQL database.
244
+ - **Key Parameters**:
245
+ - `operation`: `"executeQuery"` | `"insert"` | `"update"` | `"delete"` | `"select"`
246
+ - `query`: Raw SQL (for `"executeQuery"`).
247
+ - `table`: Target table name.
248
+ - `schema`: Schema name (default `"public"`).
249
+ - `columns`: Comma-separated column names for insert/update.
250
+ - **Authentication**: Requires `postgres` credential type.
251
+
252
+ ### 20. MySQL (n8n-nodes-base.mySql)
253
+
254
+ - **Description**: Execute queries against a MySQL/MariaDB database.
255
+ - **Key Parameters**: Same pattern as Postgres (`operation`, `query`, `table`).
256
+ - **Authentication**: Requires `mySql` credential type.
257
+
258
+ ### 21. Airtable (n8n-nodes-base.airtable)
259
+
260
+ - **Description**: Read, create, update, and delete Airtable records.
261
+ - **Resources & Operations**:
262
+ - `record`: `create` | `delete` | `get` | `list` | `update`
263
+ - **Key Parameters**:
264
+ - `baseId`: Airtable Base ID (starts with `app`).
265
+ - `tableId`: Table ID or name.
266
+ - `fields`: Object of field values for create/update.
267
+ - `filterByFormula`: Airtable formula string for list filtering.
268
+
269
+ ### 22. Discord (n8n-nodes-base.discord)
270
+
271
+ - **Description**: Send messages and manage content in Discord channels.
272
+ - **Resources & Operations**:
273
+ - `message`: `send` | `get` | `getAll` | `delete` | `react` | `pin` | `unpin`
274
+ - `channel`: `create` | `delete` | `get` | `getAll` | `update`
275
+ - `member`: `ban` | `get` | `getAll` | `kick` | `roleAdd` | `roleRemove`
276
+ - **Core send parameters**:
277
+ - `channelId`: Target channel ID.
278
+ - `content`: Message text. Supports Discord markdown.
279
+ - `options.embeds`: Array of embed objects.
280
+
281
+ ### 23. GitHub (n8n-nodes-base.github)
282
+
283
+ - **Description**: Interact with the GitHub REST API — repos, issues, PRs, releases, files.
284
+ - **Resources & Operations**:
285
+ - `issue`: `create` | `edit` | `get` | `getAll` | `lock` | `createComment`
286
+ - `pullRequest`: `create` | `get` | `getAll` | `createReview` | `merge`
287
+ - `release`: `create` | `delete` | `get` | `getAll` | `update`
288
+ - `file`: `create` | `delete` | `edit` | `get` | `list`
289
+ - `repository`: `get` | `getLicense` | `getProfile` | `listPopularPaths`
290
+ - **Key Parameters**:
291
+ - `owner`: Repository owner (user or org).
292
+ - `repository`: Repository name.
293
+
294
+ ### 24. Notion (n8n-nodes-base.notion)
295
+
296
+ - **Description**: Read and write Notion pages, databases, blocks, and users.
297
+ - **Resources & Operations**:
298
+ - `database`: `get` | `getAll` | `search`
299
+ - `databasePage`: `create` | `get` | `getAll` | `update`
300
+ - `page`: `archive` | `create` | `get` | `search` | `update`
301
+ - `block`: `append` | `getAll` | `delete`
302
+ - `user`: `get` | `getAll`
303
+ - **Key Parameters**:
304
+ - `pageId` / `databaseId`: Notion object IDs (32-char hex, no dashes required).
305
+ - `propertiesUi`: Collection of property values for page create/update.
306
+
307
+ ### 25. OpenAI (n8n-nodes-base.openAi)
308
+
309
+ - **Description**: Call OpenAI APIs for text, image, audio, and embeddings.
310
+ - **Resources & Operations**:
311
+ - `text`: `message` (chat completions)
312
+ - `image`: `generate` | `analyze`
313
+ - `audio`: `transcribe` | `translate` | `generateSpeech`
314
+ - `assistant`: `create` | `delete` | `get` | `list` | `message` | `update`
315
+ - `file`: `delete` | `get` | `list` | `upload`
316
+ - **Key chat parameters**:
317
+ - `modelId`: e.g. `"gpt-4o"`, `"gpt-4o-mini"`.
318
+ - `messages.values`: Array of `{ role, content }` objects.
319
+ - `options.temperature` / `options.maxTokens`.
320
+
321
+ ### 26. AI Agent (n8n-nodes-langchain.agent)
322
+
323
+ - **Description**: An LLM-powered agent that can call tools in a loop to complete a goal.
324
+ - **Key Parameters**:
325
+ - `text`: The user prompt / task description. Supports `={{ $json.query }}`.
326
+ - `options.systemMessage`: System prompt to set the agent's persona.
327
+ - `options.maxIterations`: Maximum tool-call iterations (default 10).
328
+ - **Notes**: This is a LangChain node (`n8n-nodes-langchain`), not `n8n-nodes-base`. It requires sub-nodes connected to the `ai_tool`, `ai_memory`, and `ai_languageModel` inputs.
329
+
330
+ ### 27. HTTP Request Tool (n8n-nodes-langchain.toolHttpRequest)
331
+
332
+ - **Description**: Expose an HTTP endpoint as a tool available to an AI Agent node.
333
+ - **Key Parameters**:
334
+ - `url`: The endpoint URL.
335
+ - `method`: HTTP method.
336
+ - `sendBody` / `bodyParameters`: Body to send.
337
+ - `description`: Human-readable description the LLM uses to decide when to call this tool.
338
+ - **Notes**: Connect to the `ai_tool` input of an AI Agent node.
339
+
340
+ ### 28. Respond to AI Agent (n8n-nodes-langchain.toolWorkflow)
341
+
342
+ - **Description**: Expose another n8n workflow as a callable tool for an AI Agent.
343
+ - **Key Parameters**:
344
+ - `workflowId`: ID of the sub-workflow to invoke.
345
+ - `description`: What this tool does (seen by the LLM).
346
+
347
+ ---
348
+
349
+ ## Configuration Patterns
350
+
351
+ - **Authentication**: Most nodes use either `authentication: "none"` or a
352
+ credential type string in `nodeCredentialType`.
353
+ - **Expressions**: Use `={{ ... }}` for dynamic values, e.g.,
354
+ `={{ $json.fieldName }}`. Guard potentially null values:
355
+ `={{ $json.field ?? 'default' }}`.
356
+ - **Accessing upstream nodes**: `$('Node Name').item.json.field` or
357
+ `$('Node Name').all()` to get all items from a named node.
358
+ - **Binary data**: Access binary fields via `$binary.fieldName`. When passing
359
+ binary between nodes, ensure the field name matches exactly what the
360
+ producing node outputs (HTTP Request → `data`, Code nodes → whatever the
361
+ code sets).
362
+ - **Node Connections**: Nodes are connected using the `connections` property in
363
+ the workflow JSON, mapping source node outputs to target node inputs.
364
+ - **Error handling**: Add `"onError": "continueErrorOutput"` to a node's
365
+ settings to route errors to a secondary output instead of stopping the
366
+ workflow.
367
+ - **typeVersion**: Always set the correct `typeVersion` for each node type.
368
+ When in doubt, use the version from the live n8n instance's node schema.
369
+ Using an incorrect version can cause silent failures or missing parameters.
@@ -0,0 +1,110 @@
1
+ <!-- keywords: bigquery, google bigquery, bq, sql, analytics, data warehouse, dataset, table, query, merge, insert, drop, create table -->
2
+
3
+ # Pattern: BigQuery Operations via HTTP Request
4
+
5
+ ## Critical Rule
6
+ **NEVER use `n8n-nodes-base.googleBigQuery`** — it returns no output items for DDL/DML statements (CREATE, MERGE, UPDATE, DROP), breaking the workflow chain. Always use `n8n-nodes-base.httpRequest` with the BigQuery REST API instead.
7
+
8
+ ## Authentication
9
+ All BigQuery HTTP nodes must use:
10
+ ```json
11
+ {
12
+ "authentication": "predefinedCredentialType",
13
+ "nodeCredentialType": "googleApi"
14
+ }
15
+ ```
16
+ The `googleApi` credential must have scopes: `https://www.googleapis.com/auth/bigquery`
17
+
18
+ ---
19
+
20
+ ## DDL / DML Queries (CREATE, MERGE, UPDATE, DROP)
21
+
22
+ Use the `jobs.query` synchronous endpoint. It always returns a JSON response, ensuring the workflow continues.
23
+
24
+ ```json
25
+ {
26
+ "parameters": {
27
+ "method": "POST",
28
+ "url": "=https://bigquery.googleapis.com/bigquery/v2/projects/YOUR_PROJECT/queries",
29
+ "authentication": "predefinedCredentialType",
30
+ "nodeCredentialType": "googleApi",
31
+ "sendBody": true,
32
+ "specifyBody": "json",
33
+ "jsonBody": "={{ JSON.stringify({ query: 'YOUR SQL HERE', useLegacySql: false, timeoutMs: 30000 }) }}",
34
+ "options": {}
35
+ },
36
+ "type": "n8n-nodes-base.httpRequest",
37
+ "typeVersion": 4
38
+ }
39
+ ```
40
+
41
+ ### CREATE TABLE example
42
+ ```json
43
+ "jsonBody": "={{ JSON.stringify({ query: 'CREATE OR REPLACE TABLE `project.dataset.table` (Email STRING, Name STRING, Stripe_plan STRING, Unsubscribed BOOL)', useLegacySql: false, timeoutMs: 30000 }) }}"
44
+ ```
45
+
46
+ ### MERGE example (reference earlier node for dynamic table name)
47
+ ```json
48
+ "jsonBody": "={{ JSON.stringify({ query: 'MERGE INTO `project.dataset.target` T USING `project.dataset.' + $('Parse CSV').item.json.stagingTable + '` S ON T.Email = S.Email WHEN MATCHED THEN UPDATE SET T.Name = S.Name WHEN NOT MATCHED THEN INSERT (Email, Name) VALUES (S.Email, S.Name)', useLegacySql: false, timeoutMs: 30000 }) }}"
49
+ ```
50
+
51
+ ### UPDATE example
52
+ ```json
53
+ "jsonBody": "={{ JSON.stringify({ query: 'UPDATE `project.dataset.table` T SET T.active = false WHERE T.id NOT IN (SELECT id FROM `project.dataset.' + $('upstream').item.json.stagingTable + '`)', useLegacySql: false, timeoutMs: 30000 }) }}"
54
+ ```
55
+
56
+ ### DROP TABLE example
57
+ ```json
58
+ "jsonBody": "={{ JSON.stringify({ query: 'DROP TABLE IF EXISTS `project.dataset.' + $('upstream').item.json.stagingTable + '`', useLegacySql: false, timeoutMs: 30000 }) }}"
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Streaming Insert (insertAll)
64
+
65
+ For inserting rows, use the `tabledata.insertAll` endpoint:
66
+
67
+ ```json
68
+ {
69
+ "parameters": {
70
+ "method": "POST",
71
+ "url": "=https://bigquery.googleapis.com/bigquery/v2/projects/YOUR_PROJECT/datasets/YOUR_DATASET/tables/{{ $('upstream').item.json.stagingTable }}/insertAll",
72
+ "authentication": "predefinedCredentialType",
73
+ "nodeCredentialType": "googleApi",
74
+ "sendBody": true,
75
+ "specifyBody": "json",
76
+ "jsonBody": "={{ JSON.stringify({ rows: $('Parse CSV').item.json.rows }) }}",
77
+ "options": {}
78
+ },
79
+ "type": "n8n-nodes-base.httpRequest",
80
+ "typeVersion": 4
81
+ }
82
+ ```
83
+
84
+ Each row in `rows` must have an `insertId` (string) and `json` object:
85
+ ```json
86
+ { "insertId": "0", "json": { "Email": "user@example.com", "Name": "User" } }
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Error Cleanup Pattern
92
+
93
+ When a workflow creates a staging table, all intermediate nodes must have an error connection to a cleanup node that drops the table. This prevents orphaned tables on failure.
94
+
95
+ ```json
96
+ "connections": {
97
+ "Create Staging Table": {
98
+ "main": [ [ { "node": "Insert Rows", "type": "main", "index": 0 } ] ],
99
+ "error": [ [ { "node": "Drop Staging Table (Error)", "type": "main", "index": 0 } ] ]
100
+ }
101
+ }
102
+ ```
103
+
104
+ The error cleanup node uses `$('Generate Table Name').item.json.stagingTable` (not `$('Parse CSV')`) because errors may fire before the CSV parsing node runs.
105
+
106
+ ---
107
+
108
+ ## Required IAM Roles for Service Account
109
+ - `roles/bigquery.jobUser` — run jobs
110
+ - `roles/bigquery.dataEditor` — read/write table data
@@ -122,13 +122,14 @@
122
122
  "aliases": [],
123
123
  "args": {
124
124
  "workflow": {
125
- "description": "Path to the workflow file or workflow ID",
125
+ "description": "Path to workflow JSON file (omit for interactive menu)",
126
126
  "name": "workflow",
127
- "required": true
127
+ "required": false
128
128
  }
129
129
  },
130
130
  "description": "Push workflows to n8n instance via API",
131
131
  "examples": [
132
+ "<%= config.bin %> <%= command.id %>",
132
133
  "<%= config.bin %> <%= command.id %> ./workflows/slack-notifier.json"
133
134
  ],
134
135
  "flags": {
@@ -147,6 +148,14 @@
147
148
  "name": "activate",
148
149
  "allowNo": false,
149
150
  "type": "boolean"
151
+ },
152
+ "dir": {
153
+ "char": "d",
154
+ "description": "Directory to scan for workflows (default: ./workflows)",
155
+ "name": "dir",
156
+ "hasDynamicHelp": false,
157
+ "multiple": false,
158
+ "type": "option"
150
159
  }
151
160
  },
152
161
  "hasDynamicHelp": false,
@@ -239,6 +248,76 @@
239
248
  "fixture.js"
240
249
  ]
241
250
  },
251
+ "learn": {
252
+ "aliases": [],
253
+ "args": {
254
+ "workflow": {
255
+ "description": "Path to workflow JSON file (omit for interactive menu)",
256
+ "name": "workflow",
257
+ "required": false
258
+ }
259
+ },
260
+ "description": "Extract reusable patterns from a validated workflow into the pattern library",
261
+ "examples": [
262
+ "<%= config.bin %> <%= command.id %>",
263
+ "<%= config.bin %> <%= command.id %> ./workflows/my-workflow/workflow.json",
264
+ "<%= config.bin %> <%= command.id %> --github owner/repo",
265
+ "<%= config.bin %> <%= command.id %> --github owner/repo --github-path patterns/google"
266
+ ],
267
+ "flags": {
268
+ "dir": {
269
+ "char": "d",
270
+ "description": "Directory to scan for workflows (default: ./workflows)",
271
+ "name": "dir",
272
+ "hasDynamicHelp": false,
273
+ "multiple": false,
274
+ "type": "option"
275
+ },
276
+ "all": {
277
+ "description": "Generate patterns for all workflows in the directory",
278
+ "name": "all",
279
+ "allowNo": false,
280
+ "type": "boolean"
281
+ },
282
+ "github": {
283
+ "description": "Import patterns from a GitHub repo (format: owner/repo or owner/repo@branch)",
284
+ "name": "github",
285
+ "hasDynamicHelp": false,
286
+ "multiple": false,
287
+ "type": "option"
288
+ },
289
+ "github-path": {
290
+ "description": "Path within the GitHub repo to import from (default: patterns)",
291
+ "name": "github-path",
292
+ "default": "patterns",
293
+ "hasDynamicHelp": false,
294
+ "multiple": false,
295
+ "type": "option"
296
+ },
297
+ "token": {
298
+ "description": "GitHub personal access token (increases rate limit for public repos)",
299
+ "env": "GITHUB_TOKEN",
300
+ "name": "token",
301
+ "hasDynamicHelp": false,
302
+ "multiple": false,
303
+ "type": "option"
304
+ }
305
+ },
306
+ "hasDynamicHelp": false,
307
+ "hiddenAliases": [],
308
+ "id": "learn",
309
+ "pluginAlias": "@lhi/n8m",
310
+ "pluginName": "@lhi/n8m",
311
+ "pluginType": "core",
312
+ "strict": true,
313
+ "enableJsonFlag": false,
314
+ "isESM": true,
315
+ "relativePath": [
316
+ "dist",
317
+ "commands",
318
+ "learn.js"
319
+ ]
320
+ },
242
321
  "mcp": {
243
322
  "aliases": [],
244
323
  "args": {},
@@ -439,5 +518,5 @@
439
518
  ]
440
519
  }
441
520
  },
442
- "version": "0.2.3"
521
+ "version": "0.3.0"
443
522
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lhi/n8m",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "Agentic n8n CLI wrapper - A Skill Bridge for n8n workflow automation",
5
5
  "author": "Lem Canady",
6
6
  "license": "MIT",
@@ -15,6 +15,7 @@
15
15
  "files": [
16
16
  "bin",
17
17
  "dist",
18
+ "docs",
18
19
  "oclif.manifest.json"
19
20
  ],
20
21
  "dependencies": {
@@ -94,6 +95,7 @@
94
95
  "n8m": "./bin/run.js",
95
96
  "start": "npm run build && ./bin/run.js",
96
97
  "dev": "tsc -b -w",
98
+ "generate-patterns": "tsx scripts/generate-patterns.ts",
97
99
  "version": "oclif readme && git add README.md"
98
100
  }
99
101
  }