@pagelines/n8n-mcp 0.3.1 → 0.3.2
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.
- package/CHANGELOG.md +10 -6
- package/dist/index.js +2 -71
- package/dist/n8n-client.d.ts +1 -2
- package/dist/n8n-client.js +0 -6
- package/dist/tools.js +0 -29
- package/dist/types.d.ts +1 -28
- package/dist/types.js +1 -1
- package/dist/validators.d.ts +1 -9
- package/dist/validators.js +0 -85
- package/package.json +2 -2
- package/.github/workflows/ci.yml +0 -38
- package/dist/n8n-client.test.d.ts +0 -1
- package/dist/n8n-client.test.js +0 -382
- package/dist/response-format.test.d.ts +0 -1
- package/dist/response-format.test.js +0 -291
- package/dist/validators.test.d.ts +0 -1
- package/dist/validators.test.js +0 -310
- package/docs/best-practices.md +0 -165
- package/docs/node-config.md +0 -205
- package/plans/ai-guidelines.md +0 -233
- package/plans/architecture.md +0 -220
- package/server.json +0 -66
- package/src/autofix.ts +0 -275
- package/src/expressions.ts +0 -254
- package/src/index.ts +0 -550
- package/src/n8n-client.test.ts +0 -467
- package/src/n8n-client.ts +0 -374
- package/src/response-format.test.ts +0 -355
- package/src/response-format.ts +0 -278
- package/src/tools.ts +0 -489
- package/src/types.ts +0 -183
- package/src/validators.test.ts +0 -374
- package/src/validators.ts +0 -394
- package/src/versions.ts +0 -320
- package/tsconfig.json +0 -15
- package/vitest.config.ts +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.3.2] - 2025-01-13
|
|
6
|
+
|
|
7
|
+
### Removed
|
|
8
|
+
- `node_types_list` tool - n8n API doesn't reliably expose node types across versions
|
|
9
|
+
- Node type pre-validation from `workflow_create` and `workflow_update` - without reliable API, validation was causing false 404 errors
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- `workflow_update` failing with "request/body/tags is read-only" error
|
|
13
|
+
- Removed `tags` from `N8N_WORKFLOW_WRITABLE_FIELDS` since it's read-only in some n8n versions
|
|
14
|
+
|
|
5
15
|
## [0.3.1] - 2025-01-13
|
|
6
16
|
|
|
7
17
|
### Fixed
|
|
@@ -18,14 +28,8 @@ All notable changes to this project will be documented in this file.
|
|
|
18
28
|
|
|
19
29
|
### Added
|
|
20
30
|
|
|
21
|
-
#### Node Type Discovery & Validation
|
|
22
|
-
- `node_types_list` - Search available node types by name/category
|
|
23
|
-
- Pre-validation blocks invalid node types before `workflow_create` and `workflow_update`
|
|
24
|
-
- Fuzzy matching suggests correct types when invalid types detected
|
|
25
|
-
|
|
26
31
|
#### Auto-Cleanup Pipeline
|
|
27
32
|
- Every `workflow_create` and `workflow_update` now automatically:
|
|
28
|
-
- Validates node types (blocks if invalid)
|
|
29
33
|
- Runs validation rules
|
|
30
34
|
- Auto-fixes fixable issues (snake_case, $json refs, AI settings)
|
|
31
35
|
- Formats workflow (sorts nodes, removes nulls)
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
8
8
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
9
9
|
import { N8nClient } from './n8n-client.js';
|
|
10
10
|
import { tools } from './tools.js';
|
|
11
|
-
import { validateWorkflow
|
|
11
|
+
import { validateWorkflow } from './validators.js';
|
|
12
12
|
import { validateExpressions, checkCircularReferences } from './expressions.js';
|
|
13
13
|
import { autofixWorkflow, formatWorkflow } from './autofix.js';
|
|
14
14
|
import { initVersionControl, saveVersion, listVersions, getVersion, diffWorkflows, getVersionStats, } from './versions.js';
|
|
@@ -37,7 +37,7 @@ initVersionControl({
|
|
|
37
37
|
// ─────────────────────────────────────────────────────────────
|
|
38
38
|
const server = new Server({
|
|
39
39
|
name: '@pagelines/n8n-mcp',
|
|
40
|
-
version: '0.3.
|
|
40
|
+
version: '0.3.2',
|
|
41
41
|
}, {
|
|
42
42
|
capabilities: {
|
|
43
43
|
tools: {},
|
|
@@ -103,21 +103,6 @@ async function handleTool(name, args) {
|
|
|
103
103
|
}
|
|
104
104
|
case 'workflow_create': {
|
|
105
105
|
const inputNodes = args.nodes;
|
|
106
|
-
// Validate node types BEFORE creating workflow
|
|
107
|
-
const availableTypes = await client.listNodeTypes();
|
|
108
|
-
const validTypeSet = new Set(availableTypes.map((nt) => nt.name));
|
|
109
|
-
const typeErrors = validateNodeTypes(inputNodes, validTypeSet);
|
|
110
|
-
if (typeErrors.length > 0) {
|
|
111
|
-
const errorMessages = typeErrors.map((e) => {
|
|
112
|
-
let msg = e.message;
|
|
113
|
-
if (e.suggestions && e.suggestions.length > 0) {
|
|
114
|
-
msg += `. Did you mean: ${e.suggestions.join(', ')}?`;
|
|
115
|
-
}
|
|
116
|
-
return msg;
|
|
117
|
-
});
|
|
118
|
-
throw new Error(`Invalid node types detected:\n${errorMessages.join('\n')}\n\n` +
|
|
119
|
-
`Use node_types_list to discover available node types.`);
|
|
120
|
-
}
|
|
121
106
|
const nodes = inputNodes.map((n, i) => ({
|
|
122
107
|
id: crypto.randomUUID(),
|
|
123
108
|
name: n.name,
|
|
@@ -154,29 +139,6 @@ async function handleTool(name, args) {
|
|
|
154
139
|
}
|
|
155
140
|
case 'workflow_update': {
|
|
156
141
|
const operations = args.operations;
|
|
157
|
-
// Extract addNode operations that need validation
|
|
158
|
-
const addNodeOps = operations.filter((op) => op.type === 'addNode');
|
|
159
|
-
if (addNodeOps.length > 0) {
|
|
160
|
-
// Fetch available types and validate
|
|
161
|
-
const availableTypes = await client.listNodeTypes();
|
|
162
|
-
const validTypeSet = new Set(availableTypes.map((nt) => nt.name));
|
|
163
|
-
const nodesToValidate = addNodeOps.map((op) => ({
|
|
164
|
-
name: op.node.name,
|
|
165
|
-
type: op.node.type,
|
|
166
|
-
}));
|
|
167
|
-
const typeErrors = validateNodeTypes(nodesToValidate, validTypeSet);
|
|
168
|
-
if (typeErrors.length > 0) {
|
|
169
|
-
const errorMessages = typeErrors.map((e) => {
|
|
170
|
-
let msg = e.message;
|
|
171
|
-
if (e.suggestions && e.suggestions.length > 0) {
|
|
172
|
-
msg += `. Did you mean: ${e.suggestions.join(', ')}?`;
|
|
173
|
-
}
|
|
174
|
-
return msg;
|
|
175
|
-
});
|
|
176
|
-
throw new Error(`Invalid node types in addNode operations:\n${errorMessages.join('\n')}\n\n` +
|
|
177
|
-
`Use node_types_list to discover available node types.`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
142
|
// Save version before updating
|
|
181
143
|
const currentWorkflow = await client.getWorkflow(args.id);
|
|
182
144
|
const versionSaved = await saveVersion(currentWorkflow, 'before_update');
|
|
@@ -297,37 +259,6 @@ async function handleTool(name, args) {
|
|
|
297
259
|
previewWorkflow: formatted,
|
|
298
260
|
};
|
|
299
261
|
}
|
|
300
|
-
// Node Discovery
|
|
301
|
-
case 'node_types_list': {
|
|
302
|
-
const nodeTypes = await client.listNodeTypes();
|
|
303
|
-
const search = args.search?.toLowerCase();
|
|
304
|
-
const category = args.category;
|
|
305
|
-
const limit = args.limit || 50;
|
|
306
|
-
let results = nodeTypes.map((nt) => ({
|
|
307
|
-
type: nt.name,
|
|
308
|
-
name: nt.displayName,
|
|
309
|
-
description: nt.description,
|
|
310
|
-
category: nt.codex?.categories?.[0] || nt.group?.[0] || 'Other',
|
|
311
|
-
version: nt.version,
|
|
312
|
-
}));
|
|
313
|
-
// Apply search filter
|
|
314
|
-
if (search) {
|
|
315
|
-
results = results.filter((nt) => nt.type.toLowerCase().includes(search) ||
|
|
316
|
-
nt.name.toLowerCase().includes(search) ||
|
|
317
|
-
nt.description.toLowerCase().includes(search));
|
|
318
|
-
}
|
|
319
|
-
// Apply category filter
|
|
320
|
-
if (category) {
|
|
321
|
-
results = results.filter((nt) => nt.category.toLowerCase().includes(category.toLowerCase()));
|
|
322
|
-
}
|
|
323
|
-
// Apply limit
|
|
324
|
-
results = results.slice(0, limit);
|
|
325
|
-
return {
|
|
326
|
-
nodeTypes: results,
|
|
327
|
-
total: results.length,
|
|
328
|
-
hint: 'Use the "type" field value when creating nodes (e.g., "n8n-nodes-base.webhook")',
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
262
|
// Version Control
|
|
332
263
|
case 'version_list': {
|
|
333
264
|
const versions = await listVersions(args.workflowId);
|
package/dist/n8n-client.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* n8n REST API Client
|
|
3
3
|
* Clean, minimal implementation with built-in safety checks
|
|
4
4
|
*/
|
|
5
|
-
import { type N8nWorkflow, type N8nWorkflowListItem, type N8nExecution, type N8nExecutionListItem, type N8nListResponse, type N8nNode, type
|
|
5
|
+
import { type N8nWorkflow, type N8nWorkflowListItem, type N8nExecution, type N8nExecutionListItem, type N8nListResponse, type N8nNode, type PatchOperation } from './types.js';
|
|
6
6
|
export interface N8nClientConfig {
|
|
7
7
|
apiUrl: string;
|
|
8
8
|
apiKey: string;
|
|
@@ -51,5 +51,4 @@ export declare class N8nClient {
|
|
|
51
51
|
version?: string;
|
|
52
52
|
error?: string;
|
|
53
53
|
}>;
|
|
54
|
-
listNodeTypes(): Promise<N8nNodeType[]>;
|
|
55
54
|
}
|
package/dist/n8n-client.js
CHANGED
|
@@ -275,10 +275,4 @@ export class N8nClient {
|
|
|
275
275
|
};
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
|
-
// ─────────────────────────────────────────────────────────────
|
|
279
|
-
// Node Types
|
|
280
|
-
// ─────────────────────────────────────────────────────────────
|
|
281
|
-
async listNodeTypes() {
|
|
282
|
-
return this.request('GET', '/api/v1/nodes');
|
|
283
|
-
}
|
|
284
278
|
}
|
package/dist/tools.js
CHANGED
|
@@ -325,35 +325,6 @@ Returns the fixed workflow and list of changes made.`,
|
|
|
325
325
|
},
|
|
326
326
|
},
|
|
327
327
|
// ─────────────────────────────────────────────────────────────
|
|
328
|
-
// Node Discovery
|
|
329
|
-
// ─────────────────────────────────────────────────────────────
|
|
330
|
-
{
|
|
331
|
-
name: 'node_types_list',
|
|
332
|
-
description: `List available n8n node types. Use this to discover valid node types BEFORE creating workflows.
|
|
333
|
-
|
|
334
|
-
Returns: type name, display name, description, category, and version for each node.
|
|
335
|
-
Use the search parameter to filter by keyword (searches type name, display name, and description).
|
|
336
|
-
|
|
337
|
-
IMPORTANT: Always check node types exist before using them in workflow_create or workflow_update.`,
|
|
338
|
-
inputSchema: {
|
|
339
|
-
type: 'object',
|
|
340
|
-
properties: {
|
|
341
|
-
search: {
|
|
342
|
-
type: 'string',
|
|
343
|
-
description: 'Filter nodes by keyword (searches name, type, description)',
|
|
344
|
-
},
|
|
345
|
-
category: {
|
|
346
|
-
type: 'string',
|
|
347
|
-
description: 'Filter by category (e.g., "Core Nodes", "Flow", "AI")',
|
|
348
|
-
},
|
|
349
|
-
limit: {
|
|
350
|
-
type: 'number',
|
|
351
|
-
description: 'Max results (default 50)',
|
|
352
|
-
},
|
|
353
|
-
},
|
|
354
|
-
},
|
|
355
|
-
},
|
|
356
|
-
// ─────────────────────────────────────────────────────────────
|
|
357
328
|
// Version Control
|
|
358
329
|
// ─────────────────────────────────────────────────────────────
|
|
359
330
|
{
|
package/dist/types.d.ts
CHANGED
|
@@ -139,7 +139,7 @@ export interface N8nListResponse<T> {
|
|
|
139
139
|
*
|
|
140
140
|
* If n8n adds new writable fields, check the OpenAPI spec and update this array.
|
|
141
141
|
*/
|
|
142
|
-
export declare const N8N_WORKFLOW_WRITABLE_FIELDS: readonly ["name", "nodes", "connections", "settings", "staticData"
|
|
142
|
+
export declare const N8N_WORKFLOW_WRITABLE_FIELDS: readonly ["name", "nodes", "connections", "settings", "staticData"];
|
|
143
143
|
export type N8nWorkflowWritableField = (typeof N8N_WORKFLOW_WRITABLE_FIELDS)[number];
|
|
144
144
|
export type N8nWorkflowUpdate = Pick<N8nWorkflow, N8nWorkflowWritableField>;
|
|
145
145
|
/**
|
|
@@ -147,30 +147,3 @@ export type N8nWorkflowUpdate = Pick<N8nWorkflow, N8nWorkflowWritableField>;
|
|
|
147
147
|
* Generic utility for schema-driven field filtering
|
|
148
148
|
*/
|
|
149
149
|
export declare function pickFields<T, K extends keyof T>(obj: T, fields: readonly K[]): Pick<T, K>;
|
|
150
|
-
export interface N8nNodeType {
|
|
151
|
-
name: string;
|
|
152
|
-
displayName: string;
|
|
153
|
-
description: string;
|
|
154
|
-
group: string[];
|
|
155
|
-
version: number;
|
|
156
|
-
defaults?: {
|
|
157
|
-
name: string;
|
|
158
|
-
};
|
|
159
|
-
codex?: {
|
|
160
|
-
categories?: string[];
|
|
161
|
-
alias?: string[];
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
export interface N8nNodeTypeSummary {
|
|
165
|
-
type: string;
|
|
166
|
-
name: string;
|
|
167
|
-
description: string;
|
|
168
|
-
category: string;
|
|
169
|
-
version: number;
|
|
170
|
-
}
|
|
171
|
-
export interface NodeTypeValidationError {
|
|
172
|
-
nodeType: string;
|
|
173
|
-
nodeName: string;
|
|
174
|
-
message: string;
|
|
175
|
-
suggestions?: string[];
|
|
176
|
-
}
|
package/dist/types.js
CHANGED
package/dist/validators.d.ts
CHANGED
|
@@ -2,17 +2,9 @@
|
|
|
2
2
|
* Opinion-based workflow validation
|
|
3
3
|
* Enforces best practices from n8n-best-practices.md
|
|
4
4
|
*/
|
|
5
|
-
import type { N8nWorkflow, ValidationResult, ValidationWarning
|
|
5
|
+
import type { N8nWorkflow, ValidationResult, ValidationWarning } from './types.js';
|
|
6
6
|
export declare function validateWorkflow(workflow: N8nWorkflow): ValidationResult;
|
|
7
7
|
/**
|
|
8
8
|
* Validate a partial update to ensure it won't cause issues
|
|
9
9
|
*/
|
|
10
10
|
export declare function validatePartialUpdate(currentWorkflow: N8nWorkflow, nodeName: string, newParameters: Record<string, unknown>): ValidationWarning[];
|
|
11
|
-
/**
|
|
12
|
-
* Validate that all node types in an array exist in the available types
|
|
13
|
-
* Returns errors for any invalid node types with suggestions
|
|
14
|
-
*/
|
|
15
|
-
export declare function validateNodeTypes(nodes: Array<{
|
|
16
|
-
name: string;
|
|
17
|
-
type: string;
|
|
18
|
-
}>, availableTypes: Set<string>): NodeTypeValidationError[];
|
package/dist/validators.js
CHANGED
|
@@ -236,88 +236,3 @@ export function validatePartialUpdate(currentWorkflow, nodeName, newParameters)
|
|
|
236
236
|
}
|
|
237
237
|
return warnings;
|
|
238
238
|
}
|
|
239
|
-
// ─────────────────────────────────────────────────────────────
|
|
240
|
-
// Node Type Validation
|
|
241
|
-
// ─────────────────────────────────────────────────────────────
|
|
242
|
-
/**
|
|
243
|
-
* Validate that all node types in an array exist in the available types
|
|
244
|
-
* Returns errors for any invalid node types with suggestions
|
|
245
|
-
*/
|
|
246
|
-
export function validateNodeTypes(nodes, availableTypes) {
|
|
247
|
-
const errors = [];
|
|
248
|
-
for (const node of nodes) {
|
|
249
|
-
if (!availableTypes.has(node.type)) {
|
|
250
|
-
errors.push({
|
|
251
|
-
nodeType: node.type,
|
|
252
|
-
nodeName: node.name,
|
|
253
|
-
message: `Invalid node type "${node.type}" for node "${node.name}"`,
|
|
254
|
-
suggestions: findSimilarTypes(node.type, availableTypes),
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return errors;
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* Find similar node types for suggestions (fuzzy matching)
|
|
262
|
-
* Returns up to 3 suggestions
|
|
263
|
-
*/
|
|
264
|
-
function findSimilarTypes(invalidType, availableTypes) {
|
|
265
|
-
const suggestions = [];
|
|
266
|
-
const searchTerm = invalidType.toLowerCase();
|
|
267
|
-
// Extract the last part after the dot (e.g., "webhook" from "n8n-nodes-base.webhook")
|
|
268
|
-
const typeParts = searchTerm.split('.');
|
|
269
|
-
const shortName = typeParts[typeParts.length - 1];
|
|
270
|
-
for (const validType of availableTypes) {
|
|
271
|
-
const validLower = validType.toLowerCase();
|
|
272
|
-
const validShortName = validLower.split('.').pop() || '';
|
|
273
|
-
// Check for partial matches (substring)
|
|
274
|
-
if (validLower.includes(shortName) ||
|
|
275
|
-
shortName.includes(validShortName) ||
|
|
276
|
-
validShortName.includes(shortName)) {
|
|
277
|
-
suggestions.push(validType);
|
|
278
|
-
if (suggestions.length >= 3)
|
|
279
|
-
break;
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
// Check for typos using Levenshtein distance
|
|
283
|
-
const distance = levenshteinDistance(shortName, validShortName);
|
|
284
|
-
const maxLen = Math.max(shortName.length, validShortName.length);
|
|
285
|
-
// Allow up to 2 character differences for short names, or 20% of length for longer ones
|
|
286
|
-
const threshold = Math.max(2, Math.floor(maxLen * 0.2));
|
|
287
|
-
if (distance <= threshold) {
|
|
288
|
-
suggestions.push(validType);
|
|
289
|
-
if (suggestions.length >= 3)
|
|
290
|
-
break;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
return suggestions;
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Calculate Levenshtein distance between two strings
|
|
297
|
-
*/
|
|
298
|
-
function levenshteinDistance(a, b) {
|
|
299
|
-
if (a.length === 0)
|
|
300
|
-
return b.length;
|
|
301
|
-
if (b.length === 0)
|
|
302
|
-
return a.length;
|
|
303
|
-
const matrix = [];
|
|
304
|
-
// Initialize first column
|
|
305
|
-
for (let i = 0; i <= a.length; i++) {
|
|
306
|
-
matrix[i] = [i];
|
|
307
|
-
}
|
|
308
|
-
// Initialize first row
|
|
309
|
-
for (let j = 0; j <= b.length; j++) {
|
|
310
|
-
matrix[0][j] = j;
|
|
311
|
-
}
|
|
312
|
-
// Fill in the rest of the matrix
|
|
313
|
-
for (let i = 1; i <= a.length; i++) {
|
|
314
|
-
for (let j = 1; j <= b.length; j++) {
|
|
315
|
-
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
316
|
-
matrix[i][j] = Math.min(matrix[i - 1][j] + 1, // deletion
|
|
317
|
-
matrix[i][j - 1] + 1, // insertion
|
|
318
|
-
matrix[i - 1][j - 1] + cost // substitution
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return matrix[a.length][b.length];
|
|
323
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagelines/n8n-mcp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Opinionated MCP server for n8n workflow automation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|
|
29
|
-
"url": "https://github.com/PageLines/n8n-mcp"
|
|
29
|
+
"url": "git+https://github.com/PageLines/n8n-mcp.git"
|
|
30
30
|
},
|
|
31
31
|
"engines": {
|
|
32
32
|
"node": ">=18"
|
package/.github/workflows/ci.yml
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [main]
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
test:
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
steps:
|
|
13
|
-
- uses: actions/checkout@v4
|
|
14
|
-
- uses: actions/setup-node@v4
|
|
15
|
-
with:
|
|
16
|
-
node-version: '20'
|
|
17
|
-
cache: 'npm'
|
|
18
|
-
- run: npm ci
|
|
19
|
-
- run: npm run build
|
|
20
|
-
- run: npm test -- --run
|
|
21
|
-
|
|
22
|
-
publish:
|
|
23
|
-
needs: test
|
|
24
|
-
runs-on: ubuntu-latest
|
|
25
|
-
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
|
26
|
-
steps:
|
|
27
|
-
- uses: actions/checkout@v4
|
|
28
|
-
- uses: actions/setup-node@v4
|
|
29
|
-
with:
|
|
30
|
-
node-version: '20'
|
|
31
|
-
cache: 'npm'
|
|
32
|
-
registry-url: 'https://registry.npmjs.org'
|
|
33
|
-
- run: npm ci
|
|
34
|
-
- run: npm run build
|
|
35
|
-
- run: npm publish --access public
|
|
36
|
-
env:
|
|
37
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
38
|
-
continue-on-error: true # Won't fail if version already exists
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|