n8n-nodes-batch-install2 1.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wangyin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # n8n-nodes-batch-install2
2
+
3
+ [![npm version](https://badge.fury.io/js/n8n-nodes-batch-install2.svg)](https://www.npmjs.com/package/n8n-nodes-batch-install2)
4
+ [![n8n-community-node](https://img.shields.io/badge/n8n-community--node-brightgreen.svg)](https://n8n.io/)
5
+
6
+ 这是一个用于 n8n 的社区节点插件,旨在帮助用户**批量管理(安装、列出、卸载、重装)**其他 n8n 社区节点。特别适合在多个环境之间同步插件或快速初始化全新的 n8n 实例。
7
+
8
+ ## 核心功能
9
+
10
+ - 🚀 **批量安装**: 输入多个 NPM 包名即可一键自动安装。
11
+ - 📋 **列表概览**: 查看当前 n8n 实例中已安装的所有社区插件。
12
+ - 🗑️ **批量卸载**: 快速清理不再需要的插件包。
13
+ - 🔄 **一键重装**: 扫描所有已安装节点并重新执行安装,用于修复依赖损坏或迁移环境。
14
+ - 🛠️ **环境自愈**: 自动检测并初始化 n8n 自定义插件目录(创建 `package.json`)。
15
+
16
+ ## 安装方法
17
+
18
+ ### 方式 A:通过 n8n 界面安装 (推荐)
19
+
20
+ 1. 打开 n8n。
21
+ 2. 进入 **Settings > Community Nodes**。
22
+ 3. 点击 **Install a community node**。
23
+ 4. 输入包名:`n8n-nodes-batch-install2`。
24
+ 5. 点击 **Install**。
25
+
26
+ ### 方式 B:手动安装
27
+
28
+ 在你的 n8n 插件目录(通常是 `~/.n8n/custom`)下执行:
29
+
30
+ ```bash
31
+ npm install n8n-nodes-batch-install2
32
+ ```
33
+
34
+ 然后重启 n8n。
35
+
36
+ ## 使用说明
37
+
38
+ 添加 **Batch Community Nodes Installer** 节点后,你可以选择以下操作:
39
+
40
+ ### 1. Install (安装)
41
+ - **Package Names**: 输入 NPM 包名,用逗号分隔(例如:`n8n-nodes-chatgpt, n8n-nodes-python`)。
42
+ - **Custom Path**: (可选) 默认会自动识别路径,如果是在 Docker 或特殊环境中,可以手动指定。
43
+
44
+ ### 2. List (列出)
45
+ - 获取当前已安装插件的详细 JSON 列表。
46
+
47
+ ### 3. Uninstall (卸载)
48
+ - 输入包名删除对应的插件。
49
+
50
+ ### 4. Reinstall All (一键重装)
51
+ - 自动遍历当前目录下的所有插件并重新安装,适合解决“节点虽在但无法加载”的问题。
52
+
53
+ ## 开发与贡献
54
+
55
+ 如果你想基于此项目进行二次开发:
56
+
57
+ 1. **克隆项目**:
58
+ ```bash
59
+ git clone https://github.com/hackyinge/n8n-nodes-batch-install.git
60
+ cd n8n-nodes-batch-install
61
+ ```
62
+
63
+ 2. **安装依赖**:
64
+ ```bash
65
+ npm install
66
+ ```
67
+
68
+ 3. **构建项目**:
69
+ ```bash
70
+ npm run build
71
+ ```
72
+
73
+ 4. **本地调试**:
74
+ ```bash
75
+ npm run dev
76
+ ```
77
+ 这会启动一个加载了该节点的本地 n8n 实例。
78
+
79
+ ## 许可证
80
+
81
+ [MIT](LICENSE)
82
+
83
+ ---
84
+ Made with ❤️ by [wangyin](admin@hackyin.com)
@@ -0,0 +1,5 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class BatchInstall implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,286 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.BatchInstall = void 0;
37
+ const n8n_workflow_1 = require("n8n-workflow");
38
+ const child_process_1 = require("child_process");
39
+ const util_1 = require("util");
40
+ const path = __importStar(require("path"));
41
+ const os = __importStar(require("os"));
42
+ const fs = __importStar(require("fs"));
43
+ const execPromise = (0, util_1.promisify)(child_process_1.exec);
44
+ class BatchInstall {
45
+ constructor() {
46
+ this.description = {
47
+ displayName: 'Batch Community Nodes Installer',
48
+ name: 'batchInstall',
49
+ icon: 'fa:download',
50
+ group: ['transform'],
51
+ version: 1,
52
+ description: 'Batch install, list or uninstall n8n community nodes',
53
+ defaults: {
54
+ name: 'Batch Install',
55
+ },
56
+ inputs: ['main'],
57
+ outputs: ['main'],
58
+ properties: [
59
+ {
60
+ displayName: 'Resource',
61
+ name: 'resource',
62
+ type: 'options',
63
+ noDataExpression: true,
64
+ options: [
65
+ {
66
+ name: 'Community Node',
67
+ value: 'communityNode',
68
+ },
69
+ ],
70
+ default: 'communityNode',
71
+ },
72
+ {
73
+ displayName: 'Operation',
74
+ name: 'operation',
75
+ type: 'options',
76
+ noDataExpression: true,
77
+ displayOptions: {
78
+ show: {
79
+ resource: ['communityNode'],
80
+ },
81
+ },
82
+ options: [
83
+ {
84
+ name: 'Install',
85
+ value: 'install',
86
+ description: 'Install multiple community nodes',
87
+ action: 'Install community nodes',
88
+ },
89
+ {
90
+ name: 'List',
91
+ value: 'list',
92
+ description: 'List all installed community nodes',
93
+ action: 'List community nodes',
94
+ },
95
+ {
96
+ name: 'Uninstall',
97
+ value: 'uninstall',
98
+ description: 'Uninstall multiple community nodes',
99
+ action: 'Uninstall community nodes',
100
+ },
101
+ {
102
+ name: 'Reinstall All',
103
+ value: 'reinstallAll',
104
+ description: 'Reinstall all currently installed community nodes',
105
+ action: 'Reinstall all community nodes',
106
+ },
107
+ ],
108
+ default: 'install',
109
+ },
110
+ {
111
+ displayName: 'Package Names',
112
+ name: 'packageNames',
113
+ type: 'string',
114
+ default: '',
115
+ placeholder: 'n8n-nodes-chatgpt, n8n-nodes-python',
116
+ description: 'Comma-separated list of NPM package names',
117
+ required: true,
118
+ displayOptions: {
119
+ show: {
120
+ resource: ['communityNode'],
121
+ operation: ['install', 'uninstall'],
122
+ },
123
+ },
124
+ },
125
+ {
126
+ displayName: 'Custom Path',
127
+ name: 'customPath',
128
+ type: 'string',
129
+ default: '',
130
+ placeholder: '/home/node/.n8n/custom',
131
+ description: 'Optional: Manually specify the n8n custom nodes directory',
132
+ displayOptions: {
133
+ show: {
134
+ resource: ['communityNode'],
135
+ },
136
+ },
137
+ },
138
+ ],
139
+ };
140
+ }
141
+ async execute() {
142
+ const items = this.getInputData();
143
+ const returnData = [];
144
+ const resource = this.getNodeParameter('resource', 0);
145
+ const operation = this.getNodeParameter('operation', 0);
146
+ // 获取自定义路径逻辑
147
+ let n8nDir = this.getNodeParameter('customPath', 0, '');
148
+ if (!n8nDir) {
149
+ // 默认逻辑:尝试从环境变量或常用路径获取
150
+ n8nDir = process.env.N8N_CUSTOM_EXTENSIONS || path.join(os.homedir(), '.n8n', 'custom');
151
+ }
152
+ // 确保目录存在
153
+ if (!fs.existsSync(n8nDir)) {
154
+ try {
155
+ fs.mkdirSync(n8nDir, { recursive: true });
156
+ }
157
+ catch (error) {
158
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to create directory ${n8nDir}: ${error.message}`);
159
+ }
160
+ }
161
+ // 确保目录下有 package.json,否则 npm install/list 会报错
162
+ const packageJsonPath = path.join(n8nDir, 'package.json');
163
+ if (!fs.existsSync(packageJsonPath)) {
164
+ try {
165
+ await execPromise('npm init -y', { cwd: n8nDir });
166
+ }
167
+ catch (error) {
168
+ // 如果 npm 命令不可用,至少我们创建了目录
169
+ console.error(`Failed to initialize npm in ${n8nDir}: ${error.message}`);
170
+ }
171
+ }
172
+ if (resource === 'communityNode') {
173
+ if (operation === 'install' || operation === 'uninstall') {
174
+ const packageNamesRaw = this.getNodeParameter('packageNames', 0);
175
+ const packageNames = packageNamesRaw.split(',').map((p) => p.trim()).filter((p) => p !== '');
176
+ if (packageNames.length === 0) {
177
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No package names provided');
178
+ }
179
+ const command = operation === 'install' ? 'npm install' : 'npm uninstall';
180
+ const results = [];
181
+ for (const pkg of packageNames) {
182
+ try {
183
+ // 在指定目录下执行安装
184
+ // 注意:n8n 社区节点目录通常需要有 package.json
185
+ const { stdout, stderr } = await execPromise(`${command} ${pkg}`, { cwd: n8nDir });
186
+ results.push({
187
+ package: pkg,
188
+ status: 'success',
189
+ output: stdout,
190
+ error: stderr,
191
+ });
192
+ }
193
+ catch (error) {
194
+ results.push({
195
+ package: pkg,
196
+ status: 'error',
197
+ message: error.message,
198
+ });
199
+ }
200
+ }
201
+ returnData.push({
202
+ json: {
203
+ operation,
204
+ results,
205
+ directory: n8nDir,
206
+ },
207
+ });
208
+ }
209
+ else if (operation === 'reinstallAll') {
210
+ const results = [];
211
+ let installedPackages = [];
212
+ try {
213
+ // 1. 获取已安装的包
214
+ const { stdout } = await execPromise('npm list --depth=0 --json', { cwd: n8nDir });
215
+ const listData = JSON.parse(stdout);
216
+ const dependencies = listData.dependencies || {};
217
+ installedPackages = Object.keys(dependencies);
218
+ if (installedPackages.length === 0) {
219
+ returnData.push({
220
+ json: {
221
+ operation,
222
+ message: 'No packages found to reinstall',
223
+ directory: n8nDir,
224
+ },
225
+ });
226
+ return [returnData];
227
+ }
228
+ // 2. 批量执行安装
229
+ for (const pkg of installedPackages) {
230
+ try {
231
+ const { stdout: out, stderr: err } = await execPromise(`npm install ${pkg}`, { cwd: n8nDir });
232
+ results.push({
233
+ package: pkg,
234
+ status: 'success',
235
+ output: out,
236
+ error: err,
237
+ });
238
+ }
239
+ catch (error) {
240
+ results.push({
241
+ package: pkg,
242
+ status: 'error',
243
+ message: error.message,
244
+ });
245
+ }
246
+ }
247
+ returnData.push({
248
+ json: {
249
+ operation,
250
+ totalReinstalled: installedPackages.length,
251
+ results,
252
+ directory: n8nDir,
253
+ },
254
+ });
255
+ }
256
+ catch (error) {
257
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Reinstall failed: ${error.message}`);
258
+ }
259
+ }
260
+ else if (operation === 'list') {
261
+ try {
262
+ const { stdout } = await execPromise('npm list --depth=0 --json', { cwd: n8nDir });
263
+ const listData = JSON.parse(stdout);
264
+ returnData.push({
265
+ json: {
266
+ directory: n8nDir,
267
+ dependencies: listData.dependencies || {},
268
+ },
269
+ });
270
+ }
271
+ catch (error) {
272
+ // 即使命令报错(如目录不存在),我们也返回友好提示
273
+ returnData.push({
274
+ json: {
275
+ error: 'Failed to list packages',
276
+ message: error.message,
277
+ directory: n8nDir,
278
+ },
279
+ });
280
+ }
281
+ }
282
+ }
283
+ return [returnData];
284
+ }
285
+ }
286
+ exports.BatchInstall = BatchInstall;
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "n8n-nodes-batch-install2",
3
+ "version": "1.0.0",
4
+ "description": "n8n community node for batch installing community nodes",
5
+ "keywords": [
6
+ "n8n-community-node-package",
7
+ "n8n",
8
+ "batch-install",
9
+ "community-nodes"
10
+ ],
11
+ "license": "MIT",
12
+ "author": {
13
+ "name": "wangyin",
14
+ "email": "admin@hackyin.com"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/hackyinge/n8n-nodes-batch-install.git"
19
+ },
20
+ "engines": {
21
+ "node": ">=18.10",
22
+ "pnpm": ">=9.1"
23
+ },
24
+ "packageManager": "pnpm@9.1.4",
25
+ "main": "dist/index.js",
26
+ "scripts": {
27
+ "dev": "n8n-node dev",
28
+ "build": "n8n-node build",
29
+ "lint": "n8n-node lint",
30
+ "lint:fix": "n8n-node lint --fix",
31
+ "release": "n8n-node release"
32
+ },
33
+ "files": [
34
+ "dist"
35
+ ],
36
+ "n8n": {
37
+ "credentials": [],
38
+ "nodes": [
39
+ "dist/nodes/BatchInstall/BatchInstall.node.js"
40
+ ]
41
+ },
42
+ "devDependencies": {
43
+ "@n8n/node-cli": "^0.17.0",
44
+ "@types/node": "^25.0.9",
45
+ "n8n-workflow": "*"
46
+ },
47
+ "peerDependencies": {
48
+ "n8n-workflow": "*"
49
+ }
50
+ }