@atom8n/n8n-nodes-langchain 2.5.7 → 2.5.8
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/dist/known/nodes.json +24 -0
- package/dist/methods/defined.json +3 -0
- package/dist/methods/referenced.json +3 -0
- package/dist/nodes/agents/OpenClawAgent/OpenClawAgent.node.js +62 -0
- package/dist/nodes/agents/OpenClawAgent/OpenClawAgent.node.js.map +1 -0
- package/dist/nodes/agents/OpenClawAgent/V1/OpenClawAgentV1.node.js +821 -0
- package/dist/nodes/agents/OpenClawAgent/V1/OpenClawAgentV1.node.js.map +1 -0
- package/dist/nodes/agents/OpenClawAgent/V2/OpenClawAgentV2.node.js +2059 -0
- package/dist/nodes/agents/OpenClawAgent/V2/OpenClawAgentV2.node.js.map +1 -0
- package/dist/nodes/agents/OpenClawAgent/channels/TelegramChannel/TelegramChannel.node.js +329 -0
- package/dist/nodes/agents/OpenClawAgent/channels/TelegramChannel/TelegramChannel.node.js.map +1 -0
- package/dist/nodes/agents/OpenClawAgent/channels/TelegramChannel/telegram-channel.svg +4 -0
- package/dist/nodes/agents/OpenClawAgent/channels/WhatsAppChannel/WhatsAppChannel.node.js +108 -0
- package/dist/nodes/agents/OpenClawAgent/channels/WhatsAppChannel/WhatsAppChannel.node.js.map +1 -0
- package/dist/nodes/agents/OpenClawAgent/channels/WhatsAppChannel/whatsapp-channel.svg +3 -0
- package/dist/nodes/agents/OpenClawAgent/mcpServers/OpenClawMcpServer/OpenClawMcpServer.node.js +228 -0
- package/dist/nodes/agents/OpenClawAgent/mcpServers/OpenClawMcpServer/OpenClawMcpServer.node.js.map +1 -0
- package/dist/nodes/agents/OpenClawAgent/mcpServers/OpenClawMcpServer/openclaw-mcp-server.svg +9 -0
- package/dist/nodes/agents/OpenClawAgent/models/OpenCodeFreeModel/OpenCodeFreeModel.node.js +97 -0
- package/dist/nodes/agents/OpenClawAgent/models/OpenCodeFreeModel/OpenCodeFreeModel.node.js.map +1 -0
- package/dist/nodes/agents/OpenClawAgent/models/OpenCodeFreeModel/opencode-free-model.svg +1 -0
- package/dist/nodes/agents/OpenClawAgent/openclaw.svg +8 -0
- package/dist/nodes/agents/OpenClawAgent/plugins/OpenClawPlugin/OpenClawPlugin.node.js +261 -0
- package/dist/nodes/agents/OpenClawAgent/plugins/OpenClawPlugin/OpenClawPlugin.node.js.map +1 -0
- package/dist/nodes/agents/OpenClawAgent/plugins/OpenClawPlugin/openclaw-plugin.svg +3 -0
- package/dist/nodes/llms/LmChat9Router/LmChat9Router.node.js +40 -3
- package/dist/nodes/llms/LmChat9Router/LmChat9Router.node.js.map +1 -1
- package/dist/types/nodes.json +8 -1
- package/package.json +17 -11
|
@@ -0,0 +1,2059 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var OpenClawAgentV2_node_exports = {};
|
|
20
|
+
__export(OpenClawAgentV2_node_exports, {
|
|
21
|
+
OpenClawAgentV2: () => OpenClawAgentV2
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(OpenClawAgentV2_node_exports);
|
|
24
|
+
var import_child_process = require("child_process");
|
|
25
|
+
var import_crypto = require("crypto");
|
|
26
|
+
var import_fs = require("fs");
|
|
27
|
+
var import_path = require("path");
|
|
28
|
+
var import_n8n_workflow = require("n8n-workflow");
|
|
29
|
+
const selectorTypeToParameterName = {
|
|
30
|
+
agent: "agentId",
|
|
31
|
+
sessionId: "sessionId",
|
|
32
|
+
recipient: "to"
|
|
33
|
+
};
|
|
34
|
+
const selectorTypeToCliFlag = {
|
|
35
|
+
agent: "--agent",
|
|
36
|
+
sessionId: "--session-id",
|
|
37
|
+
recipient: "--to"
|
|
38
|
+
};
|
|
39
|
+
const DEFAULT_TIMEOUT_SECONDS = 300;
|
|
40
|
+
const CLI_SHUTDOWN_GRACE_SECONDS = 90;
|
|
41
|
+
const OPENCLAW_CONFIG_PATH_ENV = "OPENCLAW_CONFIG_PATH";
|
|
42
|
+
const OPENCLAW_STATE_DIR_ENV = "OPENCLAW_STATE_DIR";
|
|
43
|
+
const OPENCLAW_OAUTH_DIR_ENV = "OPENCLAW_OAUTH_DIR";
|
|
44
|
+
const OPENCLAW_DEFAULT_ACCOUNT_ID = "default";
|
|
45
|
+
const OPEN_CODE_FREE_MODEL_SOURCE = "opencode-free";
|
|
46
|
+
const OPEN_CODE_FREE_PROVIDER = "opencode";
|
|
47
|
+
const OPEN_CODE_FREE_ENV_VAR = "OPENCODE_API_KEY";
|
|
48
|
+
const OPEN_CODE_FREE_ENV_ALIAS = "OPENCODE_ZEN_API_KEY";
|
|
49
|
+
const OPEN_CODE_FREE_PUBLIC_KEY = "public";
|
|
50
|
+
const NINE_ROUTER_MODEL_SOURCE = "9router";
|
|
51
|
+
const NINE_ROUTER_PROVIDER = "9router";
|
|
52
|
+
const NINE_ROUTER_DEFAULT_BASE_URL = "http://localhost:20128/api/v1";
|
|
53
|
+
const NINE_ROUTER_API = "openai-completions";
|
|
54
|
+
const NINE_ROUTER_CONTEXT_WINDOW = 128e3;
|
|
55
|
+
const NINE_ROUTER_MAX_TOKENS = 32e3;
|
|
56
|
+
const OPENCLAW_TELEGRAM_CHANNEL_NODE_TYPE = "@n8n/n8n-nodes-langchain.openClawTelegramChannel";
|
|
57
|
+
function isObject(value) {
|
|
58
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
59
|
+
}
|
|
60
|
+
function getErrorMessage(error) {
|
|
61
|
+
if (error instanceof Error) {
|
|
62
|
+
return error.message;
|
|
63
|
+
}
|
|
64
|
+
return String(error);
|
|
65
|
+
}
|
|
66
|
+
function normalizeOptionalString(value) {
|
|
67
|
+
if (typeof value !== "string") {
|
|
68
|
+
return void 0;
|
|
69
|
+
}
|
|
70
|
+
const trimmed = value.trim();
|
|
71
|
+
return trimmed || void 0;
|
|
72
|
+
}
|
|
73
|
+
function normalizeLowercaseStringOrEmpty(value) {
|
|
74
|
+
return (value ?? "").trim().toLowerCase();
|
|
75
|
+
}
|
|
76
|
+
function normalizeStringOrNumber(value) {
|
|
77
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
78
|
+
return String(value);
|
|
79
|
+
}
|
|
80
|
+
return normalizeOptionalString(value);
|
|
81
|
+
}
|
|
82
|
+
function normalizeTelegramAllowFromEntry(value) {
|
|
83
|
+
const raw = normalizeStringOrNumber(value);
|
|
84
|
+
if (!raw) {
|
|
85
|
+
return void 0;
|
|
86
|
+
}
|
|
87
|
+
const normalized = raw.replace(/^(telegram|tg):/i, "").trim();
|
|
88
|
+
return normalized || void 0;
|
|
89
|
+
}
|
|
90
|
+
function normalizeAllowFromEntries(value) {
|
|
91
|
+
const rawEntries = Array.isArray(value) ? value : typeof value === "string" ? value.split(/[\n,]/) : [];
|
|
92
|
+
const seen = /* @__PURE__ */ new Set();
|
|
93
|
+
const entries = [];
|
|
94
|
+
for (const rawEntry of rawEntries) {
|
|
95
|
+
const entry = normalizeTelegramAllowFromEntry(rawEntry);
|
|
96
|
+
if (!entry || seen.has(entry)) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
seen.add(entry);
|
|
100
|
+
entries.push(entry);
|
|
101
|
+
}
|
|
102
|
+
return entries;
|
|
103
|
+
}
|
|
104
|
+
function getPublishLiteralExpressionValue(value) {
|
|
105
|
+
const trimmed = value.trim();
|
|
106
|
+
if (!trimmed.startsWith("=")) {
|
|
107
|
+
return void 0;
|
|
108
|
+
}
|
|
109
|
+
const expression = trimmed.slice(1).trim();
|
|
110
|
+
if (!expression || expression.startsWith("{{") || expression.includes("$")) {
|
|
111
|
+
return void 0;
|
|
112
|
+
}
|
|
113
|
+
const quotedMatch = expression.match(/^(['"])(.*)\1$/);
|
|
114
|
+
if (quotedMatch) {
|
|
115
|
+
return quotedMatch[2];
|
|
116
|
+
}
|
|
117
|
+
return expression;
|
|
118
|
+
}
|
|
119
|
+
function getObjectKeys(value) {
|
|
120
|
+
if (!isObject(value)) {
|
|
121
|
+
return void 0;
|
|
122
|
+
}
|
|
123
|
+
return Object.keys(value);
|
|
124
|
+
}
|
|
125
|
+
function getModelDataPreview(value) {
|
|
126
|
+
if (value === void 0 || value === null) {
|
|
127
|
+
return void 0;
|
|
128
|
+
}
|
|
129
|
+
if (typeof value === "string") {
|
|
130
|
+
return value.length <= 300 ? value : `${value.slice(0, 300)}...`;
|
|
131
|
+
}
|
|
132
|
+
if (typeof value !== "object") {
|
|
133
|
+
return String(value);
|
|
134
|
+
}
|
|
135
|
+
if (Array.isArray(value)) {
|
|
136
|
+
return `[array length=${value.length}]`;
|
|
137
|
+
}
|
|
138
|
+
if (isObject(value)) {
|
|
139
|
+
const modelId = normalizeOptionalString(value.modelId);
|
|
140
|
+
const modelSource = normalizeOptionalString(value.modelSource);
|
|
141
|
+
const extra = isObject(value.extra) ? value.extra : void 0;
|
|
142
|
+
if (modelId || modelSource || extra) {
|
|
143
|
+
return JSON.stringify({
|
|
144
|
+
modelId,
|
|
145
|
+
modelSource,
|
|
146
|
+
extraKeys: getObjectKeys(extra)
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return `[object keys=${Object.keys(value).slice(0, 20).join(",")}]`;
|
|
150
|
+
}
|
|
151
|
+
return void 0;
|
|
152
|
+
}
|
|
153
|
+
function getModelProvider(modelId) {
|
|
154
|
+
const provider = modelId?.split("/")[0];
|
|
155
|
+
return normalizeOptionalString(provider);
|
|
156
|
+
}
|
|
157
|
+
function parseModelRef(modelId) {
|
|
158
|
+
const [providerPart, ...modelParts] = modelId.split("/");
|
|
159
|
+
const provider = normalizeOptionalString(providerPart)?.toLowerCase();
|
|
160
|
+
const model = normalizeOptionalString(modelParts.join("/"));
|
|
161
|
+
if (!provider || !model) {
|
|
162
|
+
return void 0;
|
|
163
|
+
}
|
|
164
|
+
return { provider, model };
|
|
165
|
+
}
|
|
166
|
+
function isOpenCodeFreeModel(modelConfig, modelId) {
|
|
167
|
+
return modelConfig?.modelSource === OPEN_CODE_FREE_MODEL_SOURCE && normalizeLowercaseStringOrEmpty(getModelProvider(modelId)) === OPEN_CODE_FREE_PROVIDER;
|
|
168
|
+
}
|
|
169
|
+
function hasOpenCodeAuthEnv(env) {
|
|
170
|
+
return normalizeOptionalString(env[OPEN_CODE_FREE_ENV_VAR]) !== void 0 || normalizeOptionalString(env[OPEN_CODE_FREE_ENV_ALIAS]) !== void 0;
|
|
171
|
+
}
|
|
172
|
+
function applyOpenCodeFreeAuthEnv(params) {
|
|
173
|
+
if (!isOpenCodeFreeModel(params.modelConfig, params.modelId)) {
|
|
174
|
+
return {
|
|
175
|
+
applied: false,
|
|
176
|
+
envVar: OPEN_CODE_FREE_ENV_VAR,
|
|
177
|
+
reason: "not-open-code-free"
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (hasOpenCodeAuthEnv(params.env) || hasOpenCodeAuthEnv(process.env)) {
|
|
181
|
+
return {
|
|
182
|
+
applied: false,
|
|
183
|
+
envVar: OPEN_CODE_FREE_ENV_VAR,
|
|
184
|
+
reason: "existing-auth-env"
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
params.env[OPEN_CODE_FREE_ENV_VAR] = OPEN_CODE_FREE_PUBLIC_KEY;
|
|
188
|
+
return {
|
|
189
|
+
applied: true,
|
|
190
|
+
envVar: OPEN_CODE_FREE_ENV_VAR,
|
|
191
|
+
reason: "configured-public-env"
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function getOpenClawConfigPath() {
|
|
195
|
+
const configuredPath = normalizeOptionalString(process.env[OPENCLAW_CONFIG_PATH_ENV]);
|
|
196
|
+
if (configuredPath) {
|
|
197
|
+
return configuredPath;
|
|
198
|
+
}
|
|
199
|
+
const home = getHomeDirectory();
|
|
200
|
+
return home ? (0, import_path.join)(home, ".openclaw", "openclaw.json") : void 0;
|
|
201
|
+
}
|
|
202
|
+
function resolveOpenClawStateDir() {
|
|
203
|
+
const stateDir = normalizeOptionalString(process.env[OPENCLAW_STATE_DIR_ENV]);
|
|
204
|
+
if (stateDir) {
|
|
205
|
+
return stateDir;
|
|
206
|
+
}
|
|
207
|
+
const home = getHomeDirectory();
|
|
208
|
+
return home ? (0, import_path.join)(home, ".openclaw") : void 0;
|
|
209
|
+
}
|
|
210
|
+
function resolveOpenClawCredentialsDir() {
|
|
211
|
+
const oauthDir = normalizeOptionalString(process.env[OPENCLAW_OAUTH_DIR_ENV]);
|
|
212
|
+
if (oauthDir) {
|
|
213
|
+
return oauthDir;
|
|
214
|
+
}
|
|
215
|
+
const stateDir = resolveOpenClawStateDir();
|
|
216
|
+
return stateDir ? (0, import_path.join)(stateDir, "credentials") : void 0;
|
|
217
|
+
}
|
|
218
|
+
function safeFilenameKey(value) {
|
|
219
|
+
return value.trim().toLowerCase().replace(/[\\/:*?"<>|]/g, "_").replace(/\.\./g, "_");
|
|
220
|
+
}
|
|
221
|
+
function getTelegramPairingPath(credentialsDir) {
|
|
222
|
+
return (0, import_path.join)(credentialsDir, "telegram-pairing.json");
|
|
223
|
+
}
|
|
224
|
+
function getTelegramAllowFromPaths(credentialsDir, accountId) {
|
|
225
|
+
const accountPath = (0, import_path.join)(credentialsDir, `telegram-${safeFilenameKey(accountId)}-allowFrom.json`);
|
|
226
|
+
if (accountId === OPENCLAW_DEFAULT_ACCOUNT_ID) {
|
|
227
|
+
return [accountPath, (0, import_path.join)(credentialsDir, "telegram-allowFrom.json")];
|
|
228
|
+
}
|
|
229
|
+
return [accountPath];
|
|
230
|
+
}
|
|
231
|
+
function getTelegramAllowFromWritePath(credentialsDir, accountId) {
|
|
232
|
+
return (0, import_path.join)(credentialsDir, `telegram-${safeFilenameKey(accountId)}-allowFrom.json`);
|
|
233
|
+
}
|
|
234
|
+
function readOpenClawStateJsonFile(filePath) {
|
|
235
|
+
try {
|
|
236
|
+
if (!(0, import_fs.existsSync)(filePath) || !(0, import_fs.statSync)(filePath).isFile()) {
|
|
237
|
+
return void 0;
|
|
238
|
+
}
|
|
239
|
+
return (0, import_n8n_workflow.jsonParse)((0, import_fs.readFileSync)(filePath, "utf8"), {
|
|
240
|
+
acceptJSObject: true,
|
|
241
|
+
repairJSON: true
|
|
242
|
+
});
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.log("[OpenClawAgentV2] Failed to read Telegram allowFrom source file", {
|
|
245
|
+
filePath,
|
|
246
|
+
error: getErrorMessage(error)
|
|
247
|
+
});
|
|
248
|
+
return void 0;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function getMetaString(meta, key) {
|
|
252
|
+
return isObject(meta) ? normalizeOptionalString(meta[key]) : void 0;
|
|
253
|
+
}
|
|
254
|
+
function addTelegramAllowFromEntry(entries, seen, value) {
|
|
255
|
+
const entry = normalizeTelegramAllowFromEntry(value);
|
|
256
|
+
if (!entry || seen.has(entry)) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
seen.add(entry);
|
|
260
|
+
entries.push(entry);
|
|
261
|
+
}
|
|
262
|
+
function getFallbackTelegramAllowFrom(accountId) {
|
|
263
|
+
const credentialsDir = resolveOpenClawCredentialsDir();
|
|
264
|
+
if (!credentialsDir) {
|
|
265
|
+
return { allowFrom: [], pairingCount: 0, allowFromFileCount: 0 };
|
|
266
|
+
}
|
|
267
|
+
const pairingPath = getTelegramPairingPath(credentialsDir);
|
|
268
|
+
const allowFromPaths = getTelegramAllowFromPaths(credentialsDir, accountId);
|
|
269
|
+
const entries = [];
|
|
270
|
+
const seen = /* @__PURE__ */ new Set();
|
|
271
|
+
let pairingCount = 0;
|
|
272
|
+
let allowFromFileCount = 0;
|
|
273
|
+
const pairingStore = readOpenClawStateJsonFile(pairingPath);
|
|
274
|
+
const requests = isObject(pairingStore) && Array.isArray(pairingStore.requests) ? pairingStore.requests : [];
|
|
275
|
+
for (const request of requests) {
|
|
276
|
+
if (!isObject(request)) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const requestAccountId = normalizeOpenClawAccountId(getMetaString(request.meta, "accountId")) ?? OPENCLAW_DEFAULT_ACCOUNT_ID;
|
|
280
|
+
if (requestAccountId !== accountId) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const beforeCount = entries.length;
|
|
284
|
+
addTelegramAllowFromEntry(entries, seen, request.id);
|
|
285
|
+
if (entries.length > beforeCount) {
|
|
286
|
+
pairingCount++;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
for (const allowFromPath of allowFromPaths) {
|
|
290
|
+
const allowFromStore = readOpenClawStateJsonFile(allowFromPath);
|
|
291
|
+
const allowFromEntries = isObject(allowFromStore) && Array.isArray(allowFromStore.allowFrom) ? allowFromStore.allowFrom : [];
|
|
292
|
+
for (const entry of allowFromEntries) {
|
|
293
|
+
const beforeCount = entries.length;
|
|
294
|
+
addTelegramAllowFromEntry(entries, seen, entry);
|
|
295
|
+
if (entries.length > beforeCount) {
|
|
296
|
+
allowFromFileCount++;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
allowFrom: entries,
|
|
302
|
+
credentialsDir,
|
|
303
|
+
pairingPath,
|
|
304
|
+
allowFromPaths,
|
|
305
|
+
pairingCount,
|
|
306
|
+
allowFromFileCount
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function writeTelegramAllowFromStore(accountId, allowFrom) {
|
|
310
|
+
const credentialsDir = resolveOpenClawCredentialsDir();
|
|
311
|
+
if (!credentialsDir) {
|
|
312
|
+
console.log(
|
|
313
|
+
"[OpenClawAgentV2] Telegram allowFrom store sync skipped: credentials dir unknown",
|
|
314
|
+
{
|
|
315
|
+
accountId,
|
|
316
|
+
allowFromCount: allowFrom.length
|
|
317
|
+
}
|
|
318
|
+
);
|
|
319
|
+
return { changed: false };
|
|
320
|
+
}
|
|
321
|
+
const filePath = getTelegramAllowFromWritePath(credentialsDir, accountId);
|
|
322
|
+
const existingStore = readOpenClawStateJsonFile(filePath);
|
|
323
|
+
const nextStore = isObject(existingStore) ? { ...existingStore, version: 1, allowFrom } : { version: 1, allowFrom };
|
|
324
|
+
const existingJson = isObject(existingStore) ? JSON.stringify(existingStore, null, 2) : void 0;
|
|
325
|
+
const nextJson = JSON.stringify(nextStore, null, 2);
|
|
326
|
+
if (existingJson === nextJson) {
|
|
327
|
+
console.log("[OpenClawAgentV2] Telegram allowFrom store already current", {
|
|
328
|
+
accountId,
|
|
329
|
+
filePath,
|
|
330
|
+
allowFromCount: allowFrom.length
|
|
331
|
+
});
|
|
332
|
+
return { changed: false, filePath };
|
|
333
|
+
}
|
|
334
|
+
(0, import_fs.mkdirSync)((0, import_path.dirname)(filePath), { recursive: true });
|
|
335
|
+
(0, import_fs.writeFileSync)(filePath, `${nextJson}
|
|
336
|
+
`, "utf8");
|
|
337
|
+
console.log("[OpenClawAgentV2] Telegram allowFrom store written", {
|
|
338
|
+
accountId,
|
|
339
|
+
filePath,
|
|
340
|
+
allowFromCount: allowFrom.length
|
|
341
|
+
});
|
|
342
|
+
return { changed: true, filePath };
|
|
343
|
+
}
|
|
344
|
+
function ensureDataObject(parent, key) {
|
|
345
|
+
const existing = parent[key];
|
|
346
|
+
if (isObject(existing)) {
|
|
347
|
+
return existing;
|
|
348
|
+
}
|
|
349
|
+
const next = {};
|
|
350
|
+
parent[key] = next;
|
|
351
|
+
return next;
|
|
352
|
+
}
|
|
353
|
+
function setConfigValue(target, key, value) {
|
|
354
|
+
if (target[key] === value) {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
target[key] = value;
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
function deleteConfigValue(target, key) {
|
|
361
|
+
if (target[key] === void 0) {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
delete target[key];
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
function setOptionalConfigValue(target, key, value) {
|
|
368
|
+
if (value === void 0) {
|
|
369
|
+
return deleteConfigValue(target, key);
|
|
370
|
+
}
|
|
371
|
+
return setConfigValue(target, key, value);
|
|
372
|
+
}
|
|
373
|
+
function setOptionalStringArrayConfigValue(target, key, value) {
|
|
374
|
+
if (value === void 0) {
|
|
375
|
+
return deleteConfigValue(target, key);
|
|
376
|
+
}
|
|
377
|
+
if (value.length === 0) {
|
|
378
|
+
return deleteConfigValue(target, key);
|
|
379
|
+
}
|
|
380
|
+
const existing = target[key];
|
|
381
|
+
if (Array.isArray(existing) && existing.length === value.length && existing.every((entry, index) => entry === value[index])) {
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
target[key] = value;
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
function setDefaultConfigValue(target, key, value) {
|
|
388
|
+
if (target[key] !== void 0) {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
target[key] = value;
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
function normalizeOpenClawAccountId(value) {
|
|
395
|
+
const trimmed = normalizeOptionalString(value)?.toLowerCase();
|
|
396
|
+
if (!trimmed) {
|
|
397
|
+
return void 0;
|
|
398
|
+
}
|
|
399
|
+
const normalized = trimmed.replace(/[^a-z0-9_-]+/g, "-").replace(/^-+/, "").replace(/-+$/, "").slice(0, 64);
|
|
400
|
+
if (!normalized || ["__proto__", "constructor", "prototype"].includes(normalized)) {
|
|
401
|
+
return void 0;
|
|
402
|
+
}
|
|
403
|
+
return normalized;
|
|
404
|
+
}
|
|
405
|
+
function normalizeOpenClawMcpServerName(value) {
|
|
406
|
+
const trimmed = normalizeOptionalString(value);
|
|
407
|
+
if (!trimmed) {
|
|
408
|
+
return void 0;
|
|
409
|
+
}
|
|
410
|
+
const normalized = trimmed.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+/, "").replace(/-+$/, "").slice(0, 80);
|
|
411
|
+
if (!normalized || ["__proto__", "constructor", "prototype"].includes(normalized)) {
|
|
412
|
+
return void 0;
|
|
413
|
+
}
|
|
414
|
+
return normalized;
|
|
415
|
+
}
|
|
416
|
+
function isHttpUrl(value) {
|
|
417
|
+
try {
|
|
418
|
+
const parsed = new URL(value);
|
|
419
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
420
|
+
} catch {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
function normalizeMcpTransport(value) {
|
|
425
|
+
if (value === "sse" || value === "streamable-http") {
|
|
426
|
+
return value;
|
|
427
|
+
}
|
|
428
|
+
if (value === "http") {
|
|
429
|
+
return "streamable-http";
|
|
430
|
+
}
|
|
431
|
+
return void 0;
|
|
432
|
+
}
|
|
433
|
+
function normalizeMcpHeaders(value) {
|
|
434
|
+
let rawHeaders = value;
|
|
435
|
+
if (typeof value === "string") {
|
|
436
|
+
const trimmed = value.trim();
|
|
437
|
+
if (!trimmed || trimmed === "{}") {
|
|
438
|
+
return void 0;
|
|
439
|
+
}
|
|
440
|
+
rawHeaders = (0, import_n8n_workflow.jsonParse)(trimmed, { acceptJSObject: true, repairJSON: true });
|
|
441
|
+
}
|
|
442
|
+
if (!isObject(rawHeaders)) {
|
|
443
|
+
return void 0;
|
|
444
|
+
}
|
|
445
|
+
const headers = Object.fromEntries(
|
|
446
|
+
Object.entries(rawHeaders).filter(
|
|
447
|
+
(entry) => typeof entry[1] === "string" || typeof entry[1] === "number" || typeof entry[1] === "boolean"
|
|
448
|
+
)
|
|
449
|
+
);
|
|
450
|
+
return Object.keys(headers).length > 0 ? headers : void 0;
|
|
451
|
+
}
|
|
452
|
+
function normalizePositiveInteger(value) {
|
|
453
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
454
|
+
return void 0;
|
|
455
|
+
}
|
|
456
|
+
return Math.floor(value);
|
|
457
|
+
}
|
|
458
|
+
function getActivationVariables(context) {
|
|
459
|
+
const additionalData = context.additionalData;
|
|
460
|
+
return isObject(additionalData?.variables) ? additionalData.variables : {};
|
|
461
|
+
}
|
|
462
|
+
function resolveVariableExpression(value, variables) {
|
|
463
|
+
const trimmed = value.trim();
|
|
464
|
+
const expression = trimmed.startsWith("=") ? trimmed.slice(1).trim() : trimmed;
|
|
465
|
+
const match = expression.match(
|
|
466
|
+
/^\{\{\s*\$vars(?:\.([A-Za-z_][A-Za-z0-9_]*)|\[['"]([^'"]+)['"]\])\s*\}\}$/
|
|
467
|
+
);
|
|
468
|
+
if (!match) {
|
|
469
|
+
return void 0;
|
|
470
|
+
}
|
|
471
|
+
const key = match[1] ?? match[2];
|
|
472
|
+
return key ? variables[key] : void 0;
|
|
473
|
+
}
|
|
474
|
+
function resolvePublishParameterValue(value, variables) {
|
|
475
|
+
if (typeof value !== "string") {
|
|
476
|
+
return value;
|
|
477
|
+
}
|
|
478
|
+
const trimmed = value.trim();
|
|
479
|
+
if (!trimmed.startsWith("=") && !trimmed.startsWith("{{")) {
|
|
480
|
+
return value;
|
|
481
|
+
}
|
|
482
|
+
return resolveVariableExpression(trimmed, variables);
|
|
483
|
+
}
|
|
484
|
+
function getPublishAllowFrom(params) {
|
|
485
|
+
const resolvedValue = resolvePublishParameterValue(params.rawValue, params.variables);
|
|
486
|
+
if (resolvedValue === void 0 && typeof params.rawValue === "string") {
|
|
487
|
+
const literalValue = getPublishLiteralExpressionValue(params.rawValue);
|
|
488
|
+
if (literalValue !== void 0) {
|
|
489
|
+
const literalAllowFrom = normalizeAllowFromEntries(literalValue);
|
|
490
|
+
console.log("OpenClaw publish sync: resolved Telegram allowFrom literal expression", {
|
|
491
|
+
nodeName: params.nodeName,
|
|
492
|
+
rawValue: params.rawValue,
|
|
493
|
+
allowFromCount: literalAllowFrom.length
|
|
494
|
+
});
|
|
495
|
+
return literalAllowFrom;
|
|
496
|
+
}
|
|
497
|
+
const accountId = params.accountId ?? OPENCLAW_DEFAULT_ACCOUNT_ID;
|
|
498
|
+
const fallback = getFallbackTelegramAllowFrom(accountId);
|
|
499
|
+
if (fallback.allowFrom.length > 0) {
|
|
500
|
+
console.log("OpenClaw publish sync: using Telegram allowFrom fallback from OpenClaw state", {
|
|
501
|
+
nodeName: params.nodeName,
|
|
502
|
+
accountId,
|
|
503
|
+
credentialsDir: fallback.credentialsDir,
|
|
504
|
+
pairingPath: fallback.pairingPath,
|
|
505
|
+
allowFromPaths: fallback.allowFromPaths,
|
|
506
|
+
pairingCount: fallback.pairingCount,
|
|
507
|
+
allowFromFileCount: fallback.allowFromFileCount,
|
|
508
|
+
allowFromCount: fallback.allowFrom.length
|
|
509
|
+
});
|
|
510
|
+
return fallback.allowFrom;
|
|
511
|
+
}
|
|
512
|
+
console.log("OpenClaw publish sync: Telegram allowFrom expression unresolved, skipping field", {
|
|
513
|
+
nodeName: params.nodeName,
|
|
514
|
+
rawValue: params.rawValue,
|
|
515
|
+
accountId,
|
|
516
|
+
credentialsDir: fallback.credentialsDir,
|
|
517
|
+
pairingPath: fallback.pairingPath,
|
|
518
|
+
allowFromPaths: fallback.allowFromPaths
|
|
519
|
+
});
|
|
520
|
+
return void 0;
|
|
521
|
+
}
|
|
522
|
+
if (resolvedValue === void 0) {
|
|
523
|
+
const accountId = params.accountId ?? OPENCLAW_DEFAULT_ACCOUNT_ID;
|
|
524
|
+
const fallback = getFallbackTelegramAllowFrom(accountId);
|
|
525
|
+
if (fallback.allowFrom.length > 0) {
|
|
526
|
+
console.log("OpenClaw publish sync: using Telegram allowFrom fallback from OpenClaw state", {
|
|
527
|
+
nodeName: params.nodeName,
|
|
528
|
+
accountId,
|
|
529
|
+
credentialsDir: fallback.credentialsDir,
|
|
530
|
+
pairingPath: fallback.pairingPath,
|
|
531
|
+
allowFromPaths: fallback.allowFromPaths,
|
|
532
|
+
pairingCount: fallback.pairingCount,
|
|
533
|
+
allowFromFileCount: fallback.allowFromFileCount,
|
|
534
|
+
allowFromCount: fallback.allowFrom.length
|
|
535
|
+
});
|
|
536
|
+
return fallback.allowFrom;
|
|
537
|
+
}
|
|
538
|
+
return void 0;
|
|
539
|
+
}
|
|
540
|
+
const allowFrom = normalizeAllowFromEntries(resolvedValue);
|
|
541
|
+
console.log("OpenClaw publish sync: resolved Telegram allowFrom parameter", {
|
|
542
|
+
nodeName: params.nodeName,
|
|
543
|
+
rawType: typeof params.rawValue,
|
|
544
|
+
resolvedType: Array.isArray(resolvedValue) ? "array" : typeof resolvedValue,
|
|
545
|
+
allowFromCount: allowFrom.length
|
|
546
|
+
});
|
|
547
|
+
return allowFrom;
|
|
548
|
+
}
|
|
549
|
+
function getMcpServerConfigFromParameters(parameters) {
|
|
550
|
+
const serverName = normalizeOptionalString(parameters.serverName);
|
|
551
|
+
const url = normalizeOptionalString(parameters.endpointUrl);
|
|
552
|
+
if (!serverName && !url) {
|
|
553
|
+
return { reason: "not-mcp-server-node" };
|
|
554
|
+
}
|
|
555
|
+
if (!serverName) {
|
|
556
|
+
return { reason: "missing-server-name" };
|
|
557
|
+
}
|
|
558
|
+
if (!url) {
|
|
559
|
+
return { reason: "missing-endpoint-url" };
|
|
560
|
+
}
|
|
561
|
+
const options = isObject(parameters.options) ? parameters.options : {};
|
|
562
|
+
let headers;
|
|
563
|
+
try {
|
|
564
|
+
headers = normalizeMcpHeaders(options.headers);
|
|
565
|
+
} catch (error) {
|
|
566
|
+
return { reason: `invalid-headers:${getErrorMessage(error)}` };
|
|
567
|
+
}
|
|
568
|
+
return {
|
|
569
|
+
config: {
|
|
570
|
+
mcpServerSource: "openclaw",
|
|
571
|
+
serverName,
|
|
572
|
+
url,
|
|
573
|
+
transport: normalizeMcpTransport(parameters.transport),
|
|
574
|
+
headers,
|
|
575
|
+
connectionTimeoutMs: normalizePositiveInteger(options.connectionTimeoutMs)
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
function readOpenClawConfig(configPath) {
|
|
580
|
+
if (!(0, import_fs.existsSync)(configPath)) {
|
|
581
|
+
return {};
|
|
582
|
+
}
|
|
583
|
+
const rawConfig = (0, import_fs.readFileSync)(configPath, "utf8").trim();
|
|
584
|
+
if (!rawConfig) {
|
|
585
|
+
return {};
|
|
586
|
+
}
|
|
587
|
+
const parsed = (0, import_n8n_workflow.jsonParse)(rawConfig, { acceptJSObject: true, repairJSON: true });
|
|
588
|
+
if (!isObject(parsed)) {
|
|
589
|
+
throw new import_n8n_workflow.ApplicationError(`OpenClaw config is not an object: ${configPath}`);
|
|
590
|
+
}
|
|
591
|
+
return parsed;
|
|
592
|
+
}
|
|
593
|
+
function syncChannelConfig(params) {
|
|
594
|
+
const configPath = getOpenClawConfigPath();
|
|
595
|
+
if (!configPath) {
|
|
596
|
+
throw new import_n8n_workflow.ApplicationError(
|
|
597
|
+
`Could not determine OpenClaw config path. Set ${OPENCLAW_CONFIG_PATH_ENV} or HOME for the n8n process.`
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
const config = readOpenClawConfig(configPath);
|
|
601
|
+
const originalConfigJson = JSON.stringify(config);
|
|
602
|
+
const channels = ensureDataObject(config, "channels");
|
|
603
|
+
const channel = ensureDataObject(channels, params.channelType);
|
|
604
|
+
const accountId = normalizeOpenClawAccountId(params.replyAccount);
|
|
605
|
+
const useAccountTarget = !!accountId && accountId !== OPENCLAW_DEFAULT_ACCOUNT_ID;
|
|
606
|
+
const target = useAccountTarget ? ensureDataObject(ensureDataObject(channel, "accounts"), accountId) : channel;
|
|
607
|
+
const targetPath = useAccountTarget ? `channels.${params.channelType}.accounts.${accountId}` : `channels.${params.channelType}`;
|
|
608
|
+
const dmPolicy = normalizeOptionalString(params.dmPolicy);
|
|
609
|
+
const groupPolicy = normalizeOptionalString(params.groupPolicy);
|
|
610
|
+
const isTelegramChannel = params.channelType === "telegram";
|
|
611
|
+
console.log("[OpenClawAgentV2] syncChannelConfig: start", {
|
|
612
|
+
channelType: params.channelType,
|
|
613
|
+
accountId: accountId ?? OPENCLAW_DEFAULT_ACCOUNT_ID,
|
|
614
|
+
targetPath,
|
|
615
|
+
configPath,
|
|
616
|
+
hasBotToken: !!params.botToken,
|
|
617
|
+
dmPolicy: dmPolicy ?? "(openclaw-default)",
|
|
618
|
+
groupPolicy: groupPolicy ?? "(openclaw-default)",
|
|
619
|
+
allowFromCount: params.allowFrom?.length
|
|
620
|
+
});
|
|
621
|
+
let changed = false;
|
|
622
|
+
changed = setConfigValue(channel, "enabled", true) || changed;
|
|
623
|
+
if (useAccountTarget) {
|
|
624
|
+
changed = setConfigValue(target, "enabled", true) || changed;
|
|
625
|
+
}
|
|
626
|
+
changed = (dmPolicy ? setConfigValue(target, "dmPolicy", dmPolicy) : setDefaultConfigValue(target, "dmPolicy", "pairing")) || changed;
|
|
627
|
+
changed = (groupPolicy ? setConfigValue(target, "groupPolicy", groupPolicy) : setDefaultConfigValue(target, "groupPolicy", "allowlist")) || changed;
|
|
628
|
+
let allowFromStorePath;
|
|
629
|
+
if (params.allowFrom !== void 0) {
|
|
630
|
+
if (isTelegramChannel) {
|
|
631
|
+
const allowFromStoreSync = writeTelegramAllowFromStore(
|
|
632
|
+
accountId ?? OPENCLAW_DEFAULT_ACCOUNT_ID,
|
|
633
|
+
params.allowFrom
|
|
634
|
+
);
|
|
635
|
+
allowFromStorePath = allowFromStoreSync.filePath;
|
|
636
|
+
changed = allowFromStoreSync.changed || changed;
|
|
637
|
+
} else {
|
|
638
|
+
changed = setOptionalStringArrayConfigValue(target, "allowFrom", params.allowFrom) || changed;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (isTelegramChannel) {
|
|
642
|
+
changed = deleteConfigValue(target, "allowFrom") || changed;
|
|
643
|
+
}
|
|
644
|
+
if (params.botToken) {
|
|
645
|
+
changed = setConfigValue(target, "botToken", params.botToken) || changed;
|
|
646
|
+
}
|
|
647
|
+
const configChanged = JSON.stringify(config) !== originalConfigJson;
|
|
648
|
+
if (configChanged) {
|
|
649
|
+
(0, import_fs.mkdirSync)((0, import_path.dirname)(configPath), { recursive: true });
|
|
650
|
+
(0, import_fs.writeFileSync)(configPath, `${JSON.stringify(config, null, 2)}
|
|
651
|
+
`, "utf8");
|
|
652
|
+
console.log("[OpenClawAgentV2] syncChannelConfig: openclaw.json written", {
|
|
653
|
+
channelType: params.channelType,
|
|
654
|
+
accountId: accountId ?? OPENCLAW_DEFAULT_ACCOUNT_ID,
|
|
655
|
+
targetPath,
|
|
656
|
+
configPath,
|
|
657
|
+
allowFromCount: params.allowFrom?.length,
|
|
658
|
+
allowFromStorePath
|
|
659
|
+
});
|
|
660
|
+
} else if (changed) {
|
|
661
|
+
console.log("[OpenClawAgentV2] syncChannelConfig: external channel store updated", {
|
|
662
|
+
channelType: params.channelType,
|
|
663
|
+
accountId: accountId ?? OPENCLAW_DEFAULT_ACCOUNT_ID,
|
|
664
|
+
targetPath,
|
|
665
|
+
configPath,
|
|
666
|
+
allowFromCount: params.allowFrom?.length,
|
|
667
|
+
allowFromStorePath
|
|
668
|
+
});
|
|
669
|
+
} else {
|
|
670
|
+
console.log("[OpenClawAgentV2] syncChannelConfig: no changes needed", {
|
|
671
|
+
channelType: params.channelType,
|
|
672
|
+
accountId: accountId ?? OPENCLAW_DEFAULT_ACCOUNT_ID,
|
|
673
|
+
targetPath,
|
|
674
|
+
configPath,
|
|
675
|
+
allowFromCount: params.allowFrom?.length,
|
|
676
|
+
allowFromStorePath
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
return { accountId, changed, configPath, targetPath, allowFromStorePath };
|
|
680
|
+
}
|
|
681
|
+
function getNineRouterModelDefinition(model) {
|
|
682
|
+
return {
|
|
683
|
+
id: model,
|
|
684
|
+
name: model === "auto" ? "9Router Auto" : model,
|
|
685
|
+
api: NINE_ROUTER_API,
|
|
686
|
+
reasoning: false,
|
|
687
|
+
input: ["text", "image"],
|
|
688
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
689
|
+
contextWindow: NINE_ROUTER_CONTEXT_WINDOW,
|
|
690
|
+
maxTokens: NINE_ROUTER_MAX_TOKENS
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
function syncNineRouterModelConfig(params) {
|
|
694
|
+
if (params.modelConfig?.modelSource !== NINE_ROUTER_MODEL_SOURCE || normalizeLowercaseStringOrEmpty(getModelProvider(params.modelId)) !== NINE_ROUTER_PROVIDER || !params.modelId) {
|
|
695
|
+
return { changed: false, reason: "not-nine-router" };
|
|
696
|
+
}
|
|
697
|
+
const modelRef = parseModelRef(params.modelId);
|
|
698
|
+
if (!modelRef || modelRef.provider !== NINE_ROUTER_PROVIDER) {
|
|
699
|
+
return { changed: false, reason: "invalid-model-id" };
|
|
700
|
+
}
|
|
701
|
+
const configPath = getOpenClawConfigPath();
|
|
702
|
+
if (!configPath) {
|
|
703
|
+
throw new import_n8n_workflow.ApplicationError(
|
|
704
|
+
`Could not determine OpenClaw config path. Set ${OPENCLAW_CONFIG_PATH_ENV} or HOME for the n8n process.`
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
const config = readOpenClawConfig(configPath);
|
|
708
|
+
const models = ensureDataObject(config, "models");
|
|
709
|
+
const providers = ensureDataObject(models, "providers");
|
|
710
|
+
const provider = ensureDataObject(providers, NINE_ROUTER_PROVIDER);
|
|
711
|
+
const existingBaseUrl = normalizeOptionalString(provider.baseUrl);
|
|
712
|
+
let changed = false;
|
|
713
|
+
changed = setDefaultConfigValue(
|
|
714
|
+
provider,
|
|
715
|
+
"baseUrl",
|
|
716
|
+
normalizeOptionalString(params.modelConfig.extra?.baseUrl) ?? NINE_ROUTER_DEFAULT_BASE_URL
|
|
717
|
+
) || changed;
|
|
718
|
+
changed = setConfigValue(provider, "api", NINE_ROUTER_API) || changed;
|
|
719
|
+
let providerModels;
|
|
720
|
+
if (Array.isArray(provider.models)) {
|
|
721
|
+
providerModels = provider.models.filter(isObject);
|
|
722
|
+
if (providerModels.length !== provider.models.length) {
|
|
723
|
+
provider.models = providerModels;
|
|
724
|
+
changed = true;
|
|
725
|
+
}
|
|
726
|
+
} else {
|
|
727
|
+
providerModels = [];
|
|
728
|
+
provider.models = providerModels;
|
|
729
|
+
changed = true;
|
|
730
|
+
}
|
|
731
|
+
const existingModel = providerModels.find(
|
|
732
|
+
(model) => normalizeOptionalString(model.id) === modelRef.model
|
|
733
|
+
);
|
|
734
|
+
if (existingModel) {
|
|
735
|
+
changed = setConfigValue(existingModel, "api", NINE_ROUTER_API) || changed;
|
|
736
|
+
changed = setDefaultConfigValue(existingModel, "name", modelRef.model) || changed;
|
|
737
|
+
changed = setDefaultConfigValue(existingModel, "reasoning", false) || changed;
|
|
738
|
+
changed = setDefaultConfigValue(existingModel, "input", ["text", "image"]) || changed;
|
|
739
|
+
changed = setDefaultConfigValue(existingModel, "cost", {
|
|
740
|
+
input: 0,
|
|
741
|
+
output: 0,
|
|
742
|
+
cacheRead: 0,
|
|
743
|
+
cacheWrite: 0
|
|
744
|
+
}) || changed;
|
|
745
|
+
changed = setDefaultConfigValue(existingModel, "contextWindow", NINE_ROUTER_CONTEXT_WINDOW) || changed;
|
|
746
|
+
changed = setDefaultConfigValue(existingModel, "maxTokens", NINE_ROUTER_MAX_TOKENS) || changed;
|
|
747
|
+
} else {
|
|
748
|
+
providerModels.push(getNineRouterModelDefinition(modelRef.model));
|
|
749
|
+
changed = true;
|
|
750
|
+
}
|
|
751
|
+
const agents = ensureDataObject(config, "agents");
|
|
752
|
+
const defaults = ensureDataObject(agents, "defaults");
|
|
753
|
+
const defaultModels = ensureDataObject(defaults, "models");
|
|
754
|
+
const fullModelRef = `${modelRef.provider}/${modelRef.model}`;
|
|
755
|
+
const existingDefaultModel = defaultModels[fullModelRef];
|
|
756
|
+
if (!isObject(existingDefaultModel)) {
|
|
757
|
+
defaultModels[fullModelRef] = {};
|
|
758
|
+
changed = true;
|
|
759
|
+
}
|
|
760
|
+
if (changed) {
|
|
761
|
+
(0, import_fs.mkdirSync)((0, import_path.dirname)(configPath), { recursive: true });
|
|
762
|
+
(0, import_fs.writeFileSync)(configPath, `${JSON.stringify(config, null, 2)}
|
|
763
|
+
`, "utf8");
|
|
764
|
+
}
|
|
765
|
+
return {
|
|
766
|
+
changed,
|
|
767
|
+
configPath,
|
|
768
|
+
reason: changed ? "updated-provider" : "already-current",
|
|
769
|
+
targetPath: `models.providers.${NINE_ROUTER_PROVIDER}`,
|
|
770
|
+
modelRef: fullModelRef,
|
|
771
|
+
existingBaseUrl
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
function syncMcpServerConfig(params) {
|
|
775
|
+
const synced = [];
|
|
776
|
+
const skipped = [];
|
|
777
|
+
if (params.mcpServerConfigs.length === 0) {
|
|
778
|
+
return { changed: false, synced, skipped };
|
|
779
|
+
}
|
|
780
|
+
const configPath = getOpenClawConfigPath();
|
|
781
|
+
if (!configPath) {
|
|
782
|
+
const reason = `Could not determine OpenClaw config path. Set ${OPENCLAW_CONFIG_PATH_ENV} or HOME for the n8n process.`;
|
|
783
|
+
console.log("[OpenClawAgentV2] syncMcpServerConfig: skipped all servers", {
|
|
784
|
+
reason,
|
|
785
|
+
serverCount: params.mcpServerConfigs.length
|
|
786
|
+
});
|
|
787
|
+
return { changed: false, synced, skipped: [reason] };
|
|
788
|
+
}
|
|
789
|
+
console.log("[OpenClawAgentV2] syncMcpServerConfig: start", {
|
|
790
|
+
configPath,
|
|
791
|
+
serverCount: params.mcpServerConfigs.length,
|
|
792
|
+
serverNames: params.mcpServerConfigs.map((server) => server.serverName)
|
|
793
|
+
});
|
|
794
|
+
const config = readOpenClawConfig(configPath);
|
|
795
|
+
const mcp = ensureDataObject(config, "mcp");
|
|
796
|
+
const servers = ensureDataObject(mcp, "servers");
|
|
797
|
+
let changed = false;
|
|
798
|
+
for (const serverConfig of params.mcpServerConfigs) {
|
|
799
|
+
const serverName = normalizeOpenClawMcpServerName(serverConfig.serverName);
|
|
800
|
+
const url = normalizeOptionalString(serverConfig.url);
|
|
801
|
+
if (!serverName) {
|
|
802
|
+
const reason = `invalid-name:${serverConfig.serverName}`;
|
|
803
|
+
console.log("[OpenClawAgentV2] syncMcpServerConfig: skipping server", {
|
|
804
|
+
rawServerName: serverConfig.serverName,
|
|
805
|
+
reason
|
|
806
|
+
});
|
|
807
|
+
skipped.push(reason);
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
if (!url || !isHttpUrl(url)) {
|
|
811
|
+
const reason = `invalid-url:${serverName}`;
|
|
812
|
+
console.log("[OpenClawAgentV2] syncMcpServerConfig: skipping server", {
|
|
813
|
+
serverName,
|
|
814
|
+
hasUrl: !!url,
|
|
815
|
+
reason
|
|
816
|
+
});
|
|
817
|
+
skipped.push(reason);
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
const server = ensureDataObject(servers, serverName);
|
|
821
|
+
const transport = normalizeMcpTransport(serverConfig.transport);
|
|
822
|
+
const headers = serverConfig.headers && Object.keys(serverConfig.headers).length > 0 ? serverConfig.headers : void 0;
|
|
823
|
+
const connectionTimeoutMs = normalizePositiveInteger(serverConfig.connectionTimeoutMs);
|
|
824
|
+
changed = setConfigValue(server, "url", url) || changed;
|
|
825
|
+
changed = setOptionalConfigValue(server, "transport", transport) || changed;
|
|
826
|
+
changed = setOptionalConfigValue(server, "headers", headers) || changed;
|
|
827
|
+
changed = setOptionalConfigValue(server, "connectionTimeoutMs", connectionTimeoutMs) || changed;
|
|
828
|
+
changed = deleteConfigValue(server, "command") || changed;
|
|
829
|
+
changed = deleteConfigValue(server, "args") || changed;
|
|
830
|
+
changed = deleteConfigValue(server, "env") || changed;
|
|
831
|
+
changed = deleteConfigValue(server, "cwd") || changed;
|
|
832
|
+
changed = deleteConfigValue(server, "workingDirectory") || changed;
|
|
833
|
+
console.log("[OpenClawAgentV2] syncMcpServerConfig: prepared server", {
|
|
834
|
+
serverName,
|
|
835
|
+
rawServerName: serverConfig.serverName,
|
|
836
|
+
url,
|
|
837
|
+
transport: transport ?? "(openclaw-default)",
|
|
838
|
+
headerKeys: headers ? Object.keys(headers) : [],
|
|
839
|
+
connectionTimeoutMs
|
|
840
|
+
});
|
|
841
|
+
synced.push(serverName);
|
|
842
|
+
}
|
|
843
|
+
if (changed) {
|
|
844
|
+
(0, import_fs.mkdirSync)((0, import_path.dirname)(configPath), { recursive: true });
|
|
845
|
+
(0, import_fs.writeFileSync)(configPath, `${JSON.stringify(config, null, 2)}
|
|
846
|
+
`, "utf8");
|
|
847
|
+
console.log("[OpenClawAgentV2] syncMcpServerConfig: openclaw.json written", {
|
|
848
|
+
configPath,
|
|
849
|
+
synced,
|
|
850
|
+
skipped
|
|
851
|
+
});
|
|
852
|
+
} else {
|
|
853
|
+
console.log("[OpenClawAgentV2] syncMcpServerConfig: no changes needed", {
|
|
854
|
+
configPath,
|
|
855
|
+
synced,
|
|
856
|
+
skipped
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
return { changed, configPath, synced, skipped };
|
|
860
|
+
}
|
|
861
|
+
function syncPluginConfig(params) {
|
|
862
|
+
const installed = [];
|
|
863
|
+
const skipped = [];
|
|
864
|
+
const errors = [];
|
|
865
|
+
const configPath = getOpenClawConfigPath();
|
|
866
|
+
let existingInstalls = {};
|
|
867
|
+
let existingLoadPaths = [];
|
|
868
|
+
let existingEntries = {};
|
|
869
|
+
if (configPath) {
|
|
870
|
+
try {
|
|
871
|
+
const config = readOpenClawConfig(configPath);
|
|
872
|
+
const plugins = config.plugins;
|
|
873
|
+
existingInstalls = plugins?.installs ?? {};
|
|
874
|
+
existingLoadPaths = plugins?.load?.paths ?? [];
|
|
875
|
+
existingEntries = plugins?.entries ?? {};
|
|
876
|
+
} catch {
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
const resolvedBinary = resolveOpenClawBinary("openclaw");
|
|
880
|
+
const env = createOpenClawProcessEnv(resolvedBinary.pathDirectories, { NODE_NO_WARNINGS: "1" });
|
|
881
|
+
for (const pluginCfg of params.pluginConfigs) {
|
|
882
|
+
if (pluginCfg.pluginSource === "local" && pluginCfg.pluginPath) {
|
|
883
|
+
const pluginPath = pluginCfg.pluginPath;
|
|
884
|
+
const manifestId = pluginCfg.pluginManifest?.id;
|
|
885
|
+
if (manifestId) {
|
|
886
|
+
const isPathLoaded = existingLoadPaths.includes(pluginPath);
|
|
887
|
+
const isEntryEnabled = existingEntries[manifestId]?.enabled === true;
|
|
888
|
+
if (isPathLoaded && isEntryEnabled) {
|
|
889
|
+
console.log("[OpenClawAgentV2] syncPluginConfig: already installed, skipping", {
|
|
890
|
+
manifestId,
|
|
891
|
+
pluginPath
|
|
892
|
+
});
|
|
893
|
+
skipped.push(manifestId);
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
try {
|
|
898
|
+
const cmd = `${resolvedBinary.binaryPath} plugins install -l ${JSON.stringify(pluginPath)}`;
|
|
899
|
+
console.log("[OpenClawAgentV2] syncPluginConfig: running CLI", { cmd });
|
|
900
|
+
const output = (0, import_child_process.execSync)(cmd, {
|
|
901
|
+
timeout: 3e4,
|
|
902
|
+
encoding: "utf8",
|
|
903
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
904
|
+
env
|
|
905
|
+
});
|
|
906
|
+
console.log("[OpenClawAgentV2] syncPluginConfig: CLI success", {
|
|
907
|
+
pluginPath,
|
|
908
|
+
manifestId,
|
|
909
|
+
output: output.trim().slice(0, 500)
|
|
910
|
+
});
|
|
911
|
+
installed.push(manifestId ?? pluginPath);
|
|
912
|
+
} catch (cliErr) {
|
|
913
|
+
const errMsg = cliErr instanceof Error ? cliErr.message : String(cliErr);
|
|
914
|
+
const stderr = cliErr && typeof cliErr === "object" && "stderr" in cliErr ? String(cliErr.stderr).trim().slice(0, 500) : void 0;
|
|
915
|
+
console.log("[OpenClawAgentV2] syncPluginConfig: CLI failed", {
|
|
916
|
+
pluginPath,
|
|
917
|
+
error: errMsg.slice(0, 300),
|
|
918
|
+
stderr
|
|
919
|
+
});
|
|
920
|
+
errors.push(`local:${pluginPath}: ${errMsg.slice(0, 200)}`);
|
|
921
|
+
}
|
|
922
|
+
} else if (pluginCfg.pluginSource === "cloud" && pluginCfg.pluginId) {
|
|
923
|
+
const pluginId = pluginCfg.pluginId;
|
|
924
|
+
const existingRecord = existingInstalls[pluginId];
|
|
925
|
+
if (existingRecord?.source === "npm" || existingRecord?.source === "clawhub") {
|
|
926
|
+
console.log("[OpenClawAgentV2] syncPluginConfig: already installed, skipping", {
|
|
927
|
+
pluginId,
|
|
928
|
+
source: existingRecord.source
|
|
929
|
+
});
|
|
930
|
+
skipped.push(pluginId);
|
|
931
|
+
continue;
|
|
932
|
+
}
|
|
933
|
+
const spec = pluginCfg.pluginVersion ? `${pluginId}@${pluginCfg.pluginVersion}` : pluginId;
|
|
934
|
+
try {
|
|
935
|
+
const cmd = `${resolvedBinary.binaryPath} plugins install ${JSON.stringify(spec)}`;
|
|
936
|
+
console.log("[OpenClawAgentV2] syncPluginConfig: running CLI", { cmd });
|
|
937
|
+
const output = (0, import_child_process.execSync)(cmd, {
|
|
938
|
+
timeout: 6e4,
|
|
939
|
+
encoding: "utf8",
|
|
940
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
941
|
+
env
|
|
942
|
+
});
|
|
943
|
+
console.log("[OpenClawAgentV2] syncPluginConfig: CLI success", {
|
|
944
|
+
pluginId,
|
|
945
|
+
output: output.trim().slice(0, 500)
|
|
946
|
+
});
|
|
947
|
+
installed.push(pluginId);
|
|
948
|
+
} catch (cliErr) {
|
|
949
|
+
const errMsg = cliErr instanceof Error ? cliErr.message : String(cliErr);
|
|
950
|
+
console.log("[OpenClawAgentV2] syncPluginConfig: CLI failed", {
|
|
951
|
+
pluginId,
|
|
952
|
+
error: errMsg.slice(0, 300)
|
|
953
|
+
});
|
|
954
|
+
errors.push(`cloud:${pluginId}: ${errMsg.slice(0, 200)}`);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
console.log("[OpenClawAgentV2] syncPluginConfig: result", {
|
|
959
|
+
pluginCount: params.pluginConfigs.length,
|
|
960
|
+
installedCount: installed.length,
|
|
961
|
+
skippedCount: skipped.length,
|
|
962
|
+
errorCount: errors.length,
|
|
963
|
+
installed: installed.length > 0 ? installed : void 0,
|
|
964
|
+
skipped: skipped.length > 0 ? skipped : void 0,
|
|
965
|
+
errors: errors.length > 0 ? errors : void 0
|
|
966
|
+
});
|
|
967
|
+
return { installed, skipped, errors };
|
|
968
|
+
}
|
|
969
|
+
function parseOpenClawOutput(stdout) {
|
|
970
|
+
const trimmed = stdout.trim();
|
|
971
|
+
if (!trimmed) {
|
|
972
|
+
return {};
|
|
973
|
+
}
|
|
974
|
+
const parsed = (0, import_n8n_workflow.jsonParse)(trimmed);
|
|
975
|
+
if (isObject(parsed)) {
|
|
976
|
+
return parsed;
|
|
977
|
+
}
|
|
978
|
+
return { result: parsed };
|
|
979
|
+
}
|
|
980
|
+
function isUsableFile(filePath) {
|
|
981
|
+
try {
|
|
982
|
+
return (0, import_fs.existsSync)(filePath) && (0, import_fs.statSync)(filePath).isFile();
|
|
983
|
+
} catch {
|
|
984
|
+
return false;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
function getHomeDirectory() {
|
|
988
|
+
return normalizeOptionalString(process.env.HOME) ?? normalizeOptionalString(process.env.USERPROFILE);
|
|
989
|
+
}
|
|
990
|
+
function getDefaultBinarySearchPaths(binaryName) {
|
|
991
|
+
const home = getHomeDirectory();
|
|
992
|
+
const homeCandidates = home ? [
|
|
993
|
+
(0, import_path.join)(home, ".volta", "bin", binaryName),
|
|
994
|
+
(0, import_path.join)(home, ".local", "bin", binaryName),
|
|
995
|
+
(0, import_path.join)(home, ".npm-global", "bin", binaryName),
|
|
996
|
+
(0, import_path.join)(home, ".bun", "bin", binaryName),
|
|
997
|
+
(0, import_path.join)(home, "Library", "pnpm", binaryName)
|
|
998
|
+
] : [];
|
|
999
|
+
return [
|
|
1000
|
+
normalizeOptionalString(process.env.OPENCLAW_BINARY_PATH),
|
|
1001
|
+
normalizeOptionalString(process.env.OPENCLAW_BIN),
|
|
1002
|
+
...(process.env.PATH ?? "").split(import_path.delimiter).filter(Boolean).map((pathDirectory) => (0, import_path.join)(pathDirectory, binaryName)),
|
|
1003
|
+
...homeCandidates,
|
|
1004
|
+
(0, import_path.join)("/opt/homebrew/bin", binaryName),
|
|
1005
|
+
(0, import_path.join)("/usr/local/bin", binaryName)
|
|
1006
|
+
].filter((candidate) => typeof candidate === "string");
|
|
1007
|
+
}
|
|
1008
|
+
function resolveOpenClawBinary(binaryPath) {
|
|
1009
|
+
if (binaryPath.includes("/") || binaryPath.includes("\\")) {
|
|
1010
|
+
return { binaryPath, pathDirectories: [(0, import_path.dirname)(binaryPath)] };
|
|
1011
|
+
}
|
|
1012
|
+
for (const candidate of getDefaultBinarySearchPaths(binaryPath)) {
|
|
1013
|
+
if (isUsableFile(candidate)) {
|
|
1014
|
+
return { binaryPath: candidate, pathDirectories: [(0, import_path.dirname)(candidate)] };
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
return { binaryPath, pathDirectories: [] };
|
|
1018
|
+
}
|
|
1019
|
+
function createOpenClawProcessEnv(pathDirectories, additionalEnv = {}) {
|
|
1020
|
+
const existingPath = process.env.PATH ?? "";
|
|
1021
|
+
const prependedPath = pathDirectories.filter(Boolean).join(import_path.delimiter);
|
|
1022
|
+
const nextPath = prependedPath ? `${prependedPath}${import_path.delimiter}${existingPath}` : existingPath;
|
|
1023
|
+
return { ...process.env, ...additionalEnv, PATH: nextPath };
|
|
1024
|
+
}
|
|
1025
|
+
function killOpenClawProcess(child, signal) {
|
|
1026
|
+
try {
|
|
1027
|
+
if (child.pid && process.platform !== "win32") {
|
|
1028
|
+
process.kill(-child.pid, signal);
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
} catch {
|
|
1032
|
+
}
|
|
1033
|
+
try {
|
|
1034
|
+
child.kill(signal);
|
|
1035
|
+
} catch {
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
function summarizeProcessOutput(output) {
|
|
1039
|
+
const trimmed = output.trim();
|
|
1040
|
+
if (!trimmed) return "";
|
|
1041
|
+
const maxLength = 2e3;
|
|
1042
|
+
return trimmed.length <= maxLength ? trimmed : `${trimmed.slice(0, maxLength)}...`;
|
|
1043
|
+
}
|
|
1044
|
+
function getWatchdogTimeoutMs(timeoutSeconds) {
|
|
1045
|
+
return (timeoutSeconds + CLI_SHUTDOWN_GRACE_SECONDS) * 1e3;
|
|
1046
|
+
}
|
|
1047
|
+
function getGatewayCallArgs(params, rpcTimeoutMs) {
|
|
1048
|
+
return [
|
|
1049
|
+
"gateway",
|
|
1050
|
+
"call",
|
|
1051
|
+
"agent",
|
|
1052
|
+
"--expect-final",
|
|
1053
|
+
"--json",
|
|
1054
|
+
"--timeout",
|
|
1055
|
+
String(rpcTimeoutMs),
|
|
1056
|
+
"--params",
|
|
1057
|
+
JSON.stringify(params)
|
|
1058
|
+
];
|
|
1059
|
+
}
|
|
1060
|
+
function quoteCommandArgument(value) {
|
|
1061
|
+
if (/^[A-Za-z0-9_/:=-]+$/.test(value)) return value;
|
|
1062
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
1063
|
+
}
|
|
1064
|
+
function getOpenClawCommand(binaryPath, args) {
|
|
1065
|
+
return [binaryPath, ...args].map(quoteCommandArgument).join(" ");
|
|
1066
|
+
}
|
|
1067
|
+
async function runOpenClawCli(params) {
|
|
1068
|
+
return await new Promise((resolve, reject) => {
|
|
1069
|
+
const resolvedBinary = resolveOpenClawBinary(params.binaryPath);
|
|
1070
|
+
const command = getOpenClawCommand(resolvedBinary.binaryPath, params.args);
|
|
1071
|
+
const child = (0, import_child_process.spawn)(resolvedBinary.binaryPath, params.args, {
|
|
1072
|
+
cwd: params.cwd,
|
|
1073
|
+
detached: process.platform !== "win32",
|
|
1074
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1075
|
+
env: createOpenClawProcessEnv(resolvedBinary.pathDirectories, params.env)
|
|
1076
|
+
});
|
|
1077
|
+
let stdout = "";
|
|
1078
|
+
let stderr = "";
|
|
1079
|
+
let aborted = false;
|
|
1080
|
+
let timedOut = false;
|
|
1081
|
+
let forceKillTimer;
|
|
1082
|
+
const abortHandler = () => {
|
|
1083
|
+
aborted = true;
|
|
1084
|
+
killOpenClawProcess(child, "SIGTERM");
|
|
1085
|
+
};
|
|
1086
|
+
const timeoutTimer = setTimeout(() => {
|
|
1087
|
+
timedOut = true;
|
|
1088
|
+
killOpenClawProcess(child, "SIGTERM");
|
|
1089
|
+
forceKillTimer = setTimeout(() => {
|
|
1090
|
+
killOpenClawProcess(child, "SIGKILL");
|
|
1091
|
+
}, 5e3);
|
|
1092
|
+
}, params.timeoutMs);
|
|
1093
|
+
params.abortSignal?.addEventListener("abort", abortHandler, { once: true });
|
|
1094
|
+
child.stdout.on("data", (data) => {
|
|
1095
|
+
stdout += data.toString();
|
|
1096
|
+
});
|
|
1097
|
+
child.stderr.on("data", (data) => {
|
|
1098
|
+
stderr += data.toString();
|
|
1099
|
+
});
|
|
1100
|
+
child.on("error", (error) => {
|
|
1101
|
+
clearTimeout(timeoutTimer);
|
|
1102
|
+
if (forceKillTimer) clearTimeout(forceKillTimer);
|
|
1103
|
+
params.abortSignal?.removeEventListener("abort", abortHandler);
|
|
1104
|
+
reject(
|
|
1105
|
+
new Error(
|
|
1106
|
+
`Failed to spawn OpenClaw CLI at "${resolvedBinary.binaryPath}": ${error.message}. Set Options > Binary Path to the full path of the openclaw executable, or set OPENCLAW_BINARY_PATH in the n8n process environment.`
|
|
1107
|
+
)
|
|
1108
|
+
);
|
|
1109
|
+
});
|
|
1110
|
+
child.on("close", (exitCode, signal) => {
|
|
1111
|
+
clearTimeout(timeoutTimer);
|
|
1112
|
+
if (forceKillTimer) clearTimeout(forceKillTimer);
|
|
1113
|
+
params.abortSignal?.removeEventListener("abort", abortHandler);
|
|
1114
|
+
if (aborted) {
|
|
1115
|
+
reject(new Error("OpenClaw CLI execution was cancelled"));
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
if (timedOut) {
|
|
1119
|
+
const stderrSummary = summarizeProcessOutput(stderr);
|
|
1120
|
+
const stdoutSummary = summarizeProcessOutput(stdout);
|
|
1121
|
+
const details = stderrSummary || stdoutSummary ? ` Last output: ${stderrSummary || stdoutSummary}` : "";
|
|
1122
|
+
reject(
|
|
1123
|
+
new Error(
|
|
1124
|
+
`OpenClaw CLI did not finish within ${Math.ceil(params.timeoutMs / 1e3)} seconds and was stopped.${details}`
|
|
1125
|
+
)
|
|
1126
|
+
);
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
resolve({ stdout, stderr, exitCode, signal, command });
|
|
1130
|
+
});
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
class OpenClawAgentV2 {
|
|
1134
|
+
constructor(baseDescription) {
|
|
1135
|
+
this.description = {
|
|
1136
|
+
...baseDescription,
|
|
1137
|
+
version: 2,
|
|
1138
|
+
subtitle: '={{$parameter.selectorType === "agent" ? "Agent: " + $parameter.agentId : $parameter.selectorType === "sessionId" ? "Session: " + $parameter.sessionId : $parameter.selectorType === "recipient" ? "To: " + $parameter.to : "Default route"}}',
|
|
1139
|
+
defaults: {
|
|
1140
|
+
name: "OpenClaw AI Agent"
|
|
1141
|
+
},
|
|
1142
|
+
codex: {
|
|
1143
|
+
alias: ["OpenClaw", "Agent", "Gateway", "Assistant", "Channel"],
|
|
1144
|
+
categories: ["AI"],
|
|
1145
|
+
subcategories: {
|
|
1146
|
+
AI: ["Agents", "Root Nodes"]
|
|
1147
|
+
},
|
|
1148
|
+
resources: {
|
|
1149
|
+
primaryDocumentation: [
|
|
1150
|
+
{
|
|
1151
|
+
url: "https://docs.openclaw.ai/cli/agent"
|
|
1152
|
+
}
|
|
1153
|
+
]
|
|
1154
|
+
}
|
|
1155
|
+
},
|
|
1156
|
+
inputs: [
|
|
1157
|
+
import_n8n_workflow.NodeConnectionTypes.Main,
|
|
1158
|
+
{
|
|
1159
|
+
type: import_n8n_workflow.NodeConnectionTypes.AiChannel,
|
|
1160
|
+
displayName: "Channel"
|
|
1161
|
+
},
|
|
1162
|
+
{
|
|
1163
|
+
type: import_n8n_workflow.NodeConnectionTypes.AiLanguageModel,
|
|
1164
|
+
displayName: "Model",
|
|
1165
|
+
required: false,
|
|
1166
|
+
maxConnections: 1
|
|
1167
|
+
},
|
|
1168
|
+
{
|
|
1169
|
+
type: import_n8n_workflow.NodeConnectionTypes.AiTool,
|
|
1170
|
+
displayName: "Plugin",
|
|
1171
|
+
required: false
|
|
1172
|
+
},
|
|
1173
|
+
{
|
|
1174
|
+
type: import_n8n_workflow.NodeConnectionTypes.AiTool,
|
|
1175
|
+
displayName: "MCP",
|
|
1176
|
+
required: false
|
|
1177
|
+
}
|
|
1178
|
+
],
|
|
1179
|
+
outputs: [import_n8n_workflow.NodeConnectionTypes.Main],
|
|
1180
|
+
// No hardcoded credentials — channels provide their own
|
|
1181
|
+
properties: [
|
|
1182
|
+
{
|
|
1183
|
+
displayName: "Requires the OpenClaw CLI to be installed and configured on the n8n host. Connect Channel sub-nodes to configure messaging channels (Telegram, WhatsApp, etc.).",
|
|
1184
|
+
name: "openClawNotice",
|
|
1185
|
+
type: "notice",
|
|
1186
|
+
default: ""
|
|
1187
|
+
},
|
|
1188
|
+
{
|
|
1189
|
+
displayName: "Message",
|
|
1190
|
+
name: "message",
|
|
1191
|
+
type: "string",
|
|
1192
|
+
required: true,
|
|
1193
|
+
default: '={{ $json.chatInput || $json.chat_input || $json.message || $json.text || "" }}',
|
|
1194
|
+
description: "Message body to send to the OpenClaw agent",
|
|
1195
|
+
typeOptions: { rows: 5 }
|
|
1196
|
+
},
|
|
1197
|
+
{
|
|
1198
|
+
displayName: "Route By",
|
|
1199
|
+
name: "selectorType",
|
|
1200
|
+
type: "options",
|
|
1201
|
+
default: "agent",
|
|
1202
|
+
noDataExpression: true,
|
|
1203
|
+
description: "How to target the OpenClaw agent turn",
|
|
1204
|
+
options: [
|
|
1205
|
+
{
|
|
1206
|
+
name: "Agent ID",
|
|
1207
|
+
value: "agent",
|
|
1208
|
+
description: "Run against a configured OpenClaw agent"
|
|
1209
|
+
},
|
|
1210
|
+
{
|
|
1211
|
+
name: "Existing Session ID",
|
|
1212
|
+
value: "sessionId",
|
|
1213
|
+
description: "Continue an existing OpenClaw session"
|
|
1214
|
+
},
|
|
1215
|
+
{
|
|
1216
|
+
name: "Recipient",
|
|
1217
|
+
value: "recipient",
|
|
1218
|
+
description: "Use a recipient/channel target to derive the session"
|
|
1219
|
+
},
|
|
1220
|
+
{
|
|
1221
|
+
name: "OpenClaw Default",
|
|
1222
|
+
value: "default",
|
|
1223
|
+
description: "Let OpenClaw choose its default route"
|
|
1224
|
+
}
|
|
1225
|
+
]
|
|
1226
|
+
},
|
|
1227
|
+
{
|
|
1228
|
+
displayName: "Agent ID",
|
|
1229
|
+
name: "agentId",
|
|
1230
|
+
type: "string",
|
|
1231
|
+
default: "main",
|
|
1232
|
+
description: "Configured OpenClaw agent ID",
|
|
1233
|
+
displayOptions: { show: { selectorType: ["agent"] } }
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
displayName: "Session ID",
|
|
1237
|
+
name: "sessionId",
|
|
1238
|
+
type: "string",
|
|
1239
|
+
default: "",
|
|
1240
|
+
description: "OpenClaw session ID to continue",
|
|
1241
|
+
displayOptions: { show: { selectorType: ["sessionId"] } }
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
displayName: "Recipient",
|
|
1245
|
+
name: "to",
|
|
1246
|
+
type: "string",
|
|
1247
|
+
default: "",
|
|
1248
|
+
description: "Recipient or channel target passed to OpenClaw as --to",
|
|
1249
|
+
displayOptions: { show: { selectorType: ["recipient"] } }
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
displayName: "Thinking Level",
|
|
1253
|
+
name: "thinking",
|
|
1254
|
+
type: "options",
|
|
1255
|
+
default: "",
|
|
1256
|
+
description: "Optional OpenClaw thinking level override for this run",
|
|
1257
|
+
options: [
|
|
1258
|
+
{ name: "Adaptive", value: "adaptive" },
|
|
1259
|
+
{ name: "Extra High", value: "xhigh" },
|
|
1260
|
+
{ name: "High", value: "high" },
|
|
1261
|
+
{ name: "Low", value: "low" },
|
|
1262
|
+
{ name: "Max", value: "max" },
|
|
1263
|
+
{ name: "Medium", value: "medium" },
|
|
1264
|
+
{ name: "Minimal", value: "minimal" },
|
|
1265
|
+
{ name: "Off", value: "off" },
|
|
1266
|
+
{ name: "Use OpenClaw Default", value: "" }
|
|
1267
|
+
]
|
|
1268
|
+
},
|
|
1269
|
+
{
|
|
1270
|
+
displayName: "Run Locally",
|
|
1271
|
+
name: "local",
|
|
1272
|
+
type: "boolean",
|
|
1273
|
+
default: false,
|
|
1274
|
+
description: "Whether to force OpenClaw embedded local runtime instead of Gateway mode"
|
|
1275
|
+
},
|
|
1276
|
+
{
|
|
1277
|
+
displayName: "Deliver Reply",
|
|
1278
|
+
name: "deliver",
|
|
1279
|
+
type: "boolean",
|
|
1280
|
+
default: false,
|
|
1281
|
+
description: "Whether OpenClaw should deliver the reply back to the selected channel/target"
|
|
1282
|
+
},
|
|
1283
|
+
{
|
|
1284
|
+
displayName: "Options",
|
|
1285
|
+
name: "options",
|
|
1286
|
+
type: "collection",
|
|
1287
|
+
placeholder: "Add Option",
|
|
1288
|
+
default: {},
|
|
1289
|
+
options: [
|
|
1290
|
+
{
|
|
1291
|
+
displayName: "System Message",
|
|
1292
|
+
name: "systemMessage",
|
|
1293
|
+
type: "string",
|
|
1294
|
+
default: "",
|
|
1295
|
+
description: "Additional system instructions for this OpenClaw run",
|
|
1296
|
+
typeOptions: { rows: 5 }
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
displayName: "Binary Path",
|
|
1300
|
+
name: "binaryPath",
|
|
1301
|
+
type: "string",
|
|
1302
|
+
default: "openclaw",
|
|
1303
|
+
description: "Path to the openclaw binary"
|
|
1304
|
+
},
|
|
1305
|
+
{
|
|
1306
|
+
displayName: "Working Directory",
|
|
1307
|
+
name: "workingDirectory",
|
|
1308
|
+
type: "string",
|
|
1309
|
+
default: "",
|
|
1310
|
+
description: "Working directory for the OpenClaw process"
|
|
1311
|
+
},
|
|
1312
|
+
{
|
|
1313
|
+
displayName: "Timeout",
|
|
1314
|
+
name: "timeout",
|
|
1315
|
+
type: "number",
|
|
1316
|
+
default: DEFAULT_TIMEOUT_SECONDS,
|
|
1317
|
+
description: "OpenClaw agent timeout in seconds",
|
|
1318
|
+
typeOptions: { minValue: 1 }
|
|
1319
|
+
},
|
|
1320
|
+
{
|
|
1321
|
+
displayName: "Verbose",
|
|
1322
|
+
name: "verbose",
|
|
1323
|
+
type: "options",
|
|
1324
|
+
default: "",
|
|
1325
|
+
description: "Optional OpenClaw verbose setting",
|
|
1326
|
+
options: [
|
|
1327
|
+
{ name: "Full", value: "full" },
|
|
1328
|
+
{ name: "Leave Unchanged", value: "" },
|
|
1329
|
+
{ name: "Off", value: "off" },
|
|
1330
|
+
{ name: "On", value: "on" }
|
|
1331
|
+
]
|
|
1332
|
+
},
|
|
1333
|
+
{
|
|
1334
|
+
displayName: "Include Raw Output",
|
|
1335
|
+
name: "includeRawOutput",
|
|
1336
|
+
type: "boolean",
|
|
1337
|
+
default: false,
|
|
1338
|
+
description: "Whether to include raw stdout and stderr from the OpenClaw CLI in the output"
|
|
1339
|
+
}
|
|
1340
|
+
]
|
|
1341
|
+
}
|
|
1342
|
+
]
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
async trigger() {
|
|
1346
|
+
const node = this.getNode();
|
|
1347
|
+
const workflowId = this.getWorkflow().id;
|
|
1348
|
+
const activationMode = this.getActivationMode();
|
|
1349
|
+
console.log("[OpenClawAgentV2] trigger activation registered", {
|
|
1350
|
+
nodeName: node.name,
|
|
1351
|
+
workflowId,
|
|
1352
|
+
activationMode
|
|
1353
|
+
});
|
|
1354
|
+
console.log("OpenClaw publish sync: trigger activated", {
|
|
1355
|
+
workflowId,
|
|
1356
|
+
activationMode,
|
|
1357
|
+
nodeName: node.name
|
|
1358
|
+
});
|
|
1359
|
+
if (activationMode !== "init") {
|
|
1360
|
+
try {
|
|
1361
|
+
const nodeName = node.name;
|
|
1362
|
+
const aiToolParents = this.getParentNodes(nodeName, {
|
|
1363
|
+
includeNodeParameters: true,
|
|
1364
|
+
connectionType: import_n8n_workflow.NodeConnectionTypes.AiTool,
|
|
1365
|
+
depth: 1
|
|
1366
|
+
});
|
|
1367
|
+
const aiChannelParents = this.getParentNodes(nodeName, {
|
|
1368
|
+
includeNodeParameters: true,
|
|
1369
|
+
connectionType: import_n8n_workflow.NodeConnectionTypes.AiChannel,
|
|
1370
|
+
depth: 1
|
|
1371
|
+
});
|
|
1372
|
+
console.log("OpenClaw publish sync: discovered AI tool config sub-nodes", {
|
|
1373
|
+
count: aiToolParents.length,
|
|
1374
|
+
nodes: aiToolParents.map((p) => ({
|
|
1375
|
+
name: p.name,
|
|
1376
|
+
type: p.type,
|
|
1377
|
+
params: p.parameters ? Object.keys(p.parameters) : []
|
|
1378
|
+
}))
|
|
1379
|
+
});
|
|
1380
|
+
console.log("OpenClaw publish sync: discovered AI channel config sub-nodes", {
|
|
1381
|
+
count: aiChannelParents.length,
|
|
1382
|
+
nodes: aiChannelParents.map((p) => ({
|
|
1383
|
+
name: p.name,
|
|
1384
|
+
type: p.type,
|
|
1385
|
+
params: p.parameters ? Object.keys(p.parameters) : [],
|
|
1386
|
+
hasCredentials: !!p.credentials
|
|
1387
|
+
}))
|
|
1388
|
+
});
|
|
1389
|
+
const activationVariables = getActivationVariables(this);
|
|
1390
|
+
const publishChannelConfigs = [];
|
|
1391
|
+
for (const channelNode of aiChannelParents) {
|
|
1392
|
+
const params = channelNode.parameters ?? {};
|
|
1393
|
+
const channelNodeName = channelNode.name;
|
|
1394
|
+
if (channelNode.type !== OPENCLAW_TELEGRAM_CHANNEL_NODE_TYPE) {
|
|
1395
|
+
console.log("OpenClaw publish sync: skipping unsupported channel sub-node", {
|
|
1396
|
+
nodeName: channelNodeName,
|
|
1397
|
+
type: channelNode.type
|
|
1398
|
+
});
|
|
1399
|
+
continue;
|
|
1400
|
+
}
|
|
1401
|
+
const accountId = normalizeOptionalString(
|
|
1402
|
+
resolvePublishParameterValue(params.accountId, activationVariables)
|
|
1403
|
+
) ?? void 0;
|
|
1404
|
+
const dmPolicy = normalizeOptionalString(
|
|
1405
|
+
resolvePublishParameterValue(params.dmPolicy, activationVariables)
|
|
1406
|
+
) ?? "pairing";
|
|
1407
|
+
const groupPolicy = normalizeOptionalString(
|
|
1408
|
+
resolvePublishParameterValue(params.groupPolicy, activationVariables)
|
|
1409
|
+
) ?? "allowlist";
|
|
1410
|
+
const allowFrom = getPublishAllowFrom({
|
|
1411
|
+
rawValue: params.allowFrom,
|
|
1412
|
+
variables: activationVariables,
|
|
1413
|
+
nodeName: channelNodeName,
|
|
1414
|
+
accountId: accountId ?? OPENCLAW_DEFAULT_ACCOUNT_ID
|
|
1415
|
+
});
|
|
1416
|
+
publishChannelConfigs.push({
|
|
1417
|
+
channelType: "telegram",
|
|
1418
|
+
accountId,
|
|
1419
|
+
dmPolicy,
|
|
1420
|
+
groupPolicy,
|
|
1421
|
+
allowFrom
|
|
1422
|
+
});
|
|
1423
|
+
console.log("OpenClaw publish sync: prepared Telegram channel config", {
|
|
1424
|
+
nodeName: channelNodeName,
|
|
1425
|
+
accountId: accountId ?? OPENCLAW_DEFAULT_ACCOUNT_ID,
|
|
1426
|
+
dmPolicy,
|
|
1427
|
+
groupPolicy,
|
|
1428
|
+
allowFromCount: allowFrom?.length
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
const publishPluginConfigs = [];
|
|
1432
|
+
const publishMcpServerConfigs = [];
|
|
1433
|
+
for (const pluginNode of aiToolParents) {
|
|
1434
|
+
const params = pluginNode.parameters ?? {};
|
|
1435
|
+
const mcpServerConfig = getMcpServerConfigFromParameters(
|
|
1436
|
+
params
|
|
1437
|
+
);
|
|
1438
|
+
if (mcpServerConfig.config) {
|
|
1439
|
+
console.log("OpenClaw publish sync: processing MCP Server sub-node", {
|
|
1440
|
+
type: pluginNode.type,
|
|
1441
|
+
serverName: mcpServerConfig.config.serverName,
|
|
1442
|
+
url: mcpServerConfig.config.url,
|
|
1443
|
+
transport: mcpServerConfig.config.transport ?? "(openclaw-default)",
|
|
1444
|
+
headerKeys: mcpServerConfig.config.headers ? Object.keys(mcpServerConfig.config.headers) : []
|
|
1445
|
+
});
|
|
1446
|
+
publishMcpServerConfigs.push(mcpServerConfig.config);
|
|
1447
|
+
continue;
|
|
1448
|
+
} else if (mcpServerConfig.reason !== "not-mcp-server-node") {
|
|
1449
|
+
console.log("OpenClaw publish sync: skipping MCP Server sub-node", {
|
|
1450
|
+
type: pluginNode.type,
|
|
1451
|
+
reason: mcpServerConfig.reason,
|
|
1452
|
+
params: JSON.stringify(params).slice(0, 300)
|
|
1453
|
+
});
|
|
1454
|
+
continue;
|
|
1455
|
+
}
|
|
1456
|
+
const pluginSource = params.pluginSource || "local";
|
|
1457
|
+
console.log("OpenClaw publish sync: processing plugin sub-node", {
|
|
1458
|
+
type: pluginNode.type,
|
|
1459
|
+
pluginSource,
|
|
1460
|
+
params: JSON.stringify(params).slice(0, 300)
|
|
1461
|
+
});
|
|
1462
|
+
if (pluginSource === "local") {
|
|
1463
|
+
let pluginDirectory = params.pluginDirectory || "";
|
|
1464
|
+
if (pluginDirectory.includes("$workspace.__dirPath") || pluginDirectory.includes("$workspace[")) {
|
|
1465
|
+
const workflowObj = this.workflow;
|
|
1466
|
+
const dirPath = normalizeOptionalString(workflowObj?.workspace?.__dirPath);
|
|
1467
|
+
console.log("OpenClaw publish sync: resolving $workspace.__dirPath expression", {
|
|
1468
|
+
rawValue: pluginDirectory,
|
|
1469
|
+
workspaceAvailable: !!workflowObj?.workspace,
|
|
1470
|
+
dirPath: dirPath ?? "(not set)"
|
|
1471
|
+
});
|
|
1472
|
+
if (dirPath) {
|
|
1473
|
+
pluginDirectory = dirPath;
|
|
1474
|
+
} else {
|
|
1475
|
+
console.log(
|
|
1476
|
+
"OpenClaw publish sync: $workspace.__dirPath is not available, skipping"
|
|
1477
|
+
);
|
|
1478
|
+
continue;
|
|
1479
|
+
}
|
|
1480
|
+
} else if (pluginDirectory.includes("{{") || pluginDirectory.startsWith("=")) {
|
|
1481
|
+
console.log(
|
|
1482
|
+
"OpenClaw publish sync: pluginDirectory is an unresolvable expression, skipping",
|
|
1483
|
+
{
|
|
1484
|
+
rawValue: pluginDirectory
|
|
1485
|
+
}
|
|
1486
|
+
);
|
|
1487
|
+
continue;
|
|
1488
|
+
}
|
|
1489
|
+
pluginDirectory = pluginDirectory.trim();
|
|
1490
|
+
if (!pluginDirectory) {
|
|
1491
|
+
console.log("OpenClaw publish sync: pluginDirectory is empty, skipping");
|
|
1492
|
+
continue;
|
|
1493
|
+
}
|
|
1494
|
+
const manifestPath = (0, import_path.join)(pluginDirectory, "openclaw.plugin.json");
|
|
1495
|
+
let pluginManifest;
|
|
1496
|
+
if ((0, import_fs.existsSync)(manifestPath)) {
|
|
1497
|
+
try {
|
|
1498
|
+
const raw = (0, import_fs.readFileSync)(manifestPath, "utf8").trim();
|
|
1499
|
+
const parsed = JSON.parse(raw);
|
|
1500
|
+
pluginManifest = {
|
|
1501
|
+
id: typeof parsed.id === "string" ? parsed.id : void 0,
|
|
1502
|
+
name: typeof parsed.name === "string" ? parsed.name : void 0,
|
|
1503
|
+
description: typeof parsed.description === "string" ? parsed.description : void 0,
|
|
1504
|
+
version: typeof parsed.version === "string" ? parsed.version : void 0,
|
|
1505
|
+
providers: Array.isArray(parsed.providers) ? parsed.providers.filter(
|
|
1506
|
+
(p) => typeof p === "string"
|
|
1507
|
+
) : void 0,
|
|
1508
|
+
channels: Array.isArray(parsed.channels) ? parsed.channels.filter(
|
|
1509
|
+
(c) => typeof c === "string"
|
|
1510
|
+
) : void 0
|
|
1511
|
+
};
|
|
1512
|
+
console.log("OpenClaw publish sync: loaded local manifest", {
|
|
1513
|
+
manifestPath,
|
|
1514
|
+
id: pluginManifest.id,
|
|
1515
|
+
name: pluginManifest.name
|
|
1516
|
+
});
|
|
1517
|
+
} catch (parseErr) {
|
|
1518
|
+
console.log("OpenClaw publish sync: failed to parse manifest", {
|
|
1519
|
+
manifestPath,
|
|
1520
|
+
error: getErrorMessage(parseErr)
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
publishPluginConfigs.push({
|
|
1525
|
+
pluginSource: "local",
|
|
1526
|
+
pluginPath: pluginDirectory,
|
|
1527
|
+
pluginManifest
|
|
1528
|
+
});
|
|
1529
|
+
} else if (pluginSource === "cloud") {
|
|
1530
|
+
const pluginId = (params.pluginId || "").trim();
|
|
1531
|
+
const pluginVersion = (params.pluginVersion || "").trim() || void 0;
|
|
1532
|
+
if (pluginId) {
|
|
1533
|
+
publishPluginConfigs.push({
|
|
1534
|
+
pluginSource: "cloud",
|
|
1535
|
+
pluginId,
|
|
1536
|
+
pluginVersion
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
if (publishPluginConfigs.length > 0) {
|
|
1542
|
+
try {
|
|
1543
|
+
const syncResult = syncPluginConfig({ pluginConfigs: publishPluginConfigs });
|
|
1544
|
+
console.log("OpenClaw publish sync: plugin config synced on activation", {
|
|
1545
|
+
installedCount: syncResult.installed.length,
|
|
1546
|
+
installed: syncResult.installed,
|
|
1547
|
+
errors: syncResult.errors.length > 0 ? syncResult.errors : void 0,
|
|
1548
|
+
pluginCount: publishPluginConfigs.length
|
|
1549
|
+
});
|
|
1550
|
+
} catch (syncErr) {
|
|
1551
|
+
console.log("OpenClaw publish sync: plugin config sync failed on activation", {
|
|
1552
|
+
error: getErrorMessage(syncErr)
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
} else {
|
|
1556
|
+
console.log("OpenClaw publish sync: no plugin configs to sync on activation");
|
|
1557
|
+
}
|
|
1558
|
+
if (publishChannelConfigs.length > 0) {
|
|
1559
|
+
for (const channelConfig of publishChannelConfigs) {
|
|
1560
|
+
try {
|
|
1561
|
+
const syncResult = syncChannelConfig({
|
|
1562
|
+
channelType: channelConfig.channelType,
|
|
1563
|
+
replyAccount: channelConfig.accountId,
|
|
1564
|
+
dmPolicy: channelConfig.dmPolicy,
|
|
1565
|
+
groupPolicy: channelConfig.groupPolicy,
|
|
1566
|
+
allowFrom: channelConfig.allowFrom
|
|
1567
|
+
});
|
|
1568
|
+
console.log("OpenClaw publish sync: channel config synced on activation", {
|
|
1569
|
+
channelType: channelConfig.channelType,
|
|
1570
|
+
changed: syncResult.changed,
|
|
1571
|
+
configPath: syncResult.configPath,
|
|
1572
|
+
targetPath: syncResult.targetPath,
|
|
1573
|
+
accountId: syncResult.accountId ?? OPENCLAW_DEFAULT_ACCOUNT_ID,
|
|
1574
|
+
allowFromCount: channelConfig.allowFrom?.length,
|
|
1575
|
+
allowFromStorePath: syncResult.allowFromStorePath
|
|
1576
|
+
});
|
|
1577
|
+
} catch (syncErr) {
|
|
1578
|
+
console.log("OpenClaw publish sync: channel config sync failed on activation", {
|
|
1579
|
+
channelType: channelConfig.channelType,
|
|
1580
|
+
error: getErrorMessage(syncErr)
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
} else {
|
|
1585
|
+
console.log("OpenClaw publish sync: no channel configs to sync on activation");
|
|
1586
|
+
}
|
|
1587
|
+
if (publishMcpServerConfigs.length > 0) {
|
|
1588
|
+
try {
|
|
1589
|
+
const syncResult = syncMcpServerConfig({
|
|
1590
|
+
mcpServerConfigs: publishMcpServerConfigs
|
|
1591
|
+
});
|
|
1592
|
+
console.log("OpenClaw publish sync: MCP Server config synced on activation", {
|
|
1593
|
+
changed: syncResult.changed,
|
|
1594
|
+
configPath: syncResult.configPath,
|
|
1595
|
+
synced: syncResult.synced,
|
|
1596
|
+
skipped: syncResult.skipped.length > 0 ? syncResult.skipped : void 0,
|
|
1597
|
+
serverCount: publishMcpServerConfigs.length
|
|
1598
|
+
});
|
|
1599
|
+
} catch (syncErr) {
|
|
1600
|
+
console.log("OpenClaw publish sync: MCP Server config sync failed on activation", {
|
|
1601
|
+
error: getErrorMessage(syncErr)
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
} else {
|
|
1605
|
+
console.log("OpenClaw publish sync: no MCP Server configs to sync on activation");
|
|
1606
|
+
}
|
|
1607
|
+
} catch (publishErr) {
|
|
1608
|
+
console.log("OpenClaw publish sync: activation-time config sync failed (non-fatal)", {
|
|
1609
|
+
error: getErrorMessage(publishErr)
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
return {
|
|
1614
|
+
closeFunction: async () => {
|
|
1615
|
+
console.log("[OpenClawAgentV2] trigger activation closed", {
|
|
1616
|
+
nodeName: node.name,
|
|
1617
|
+
workflowId
|
|
1618
|
+
});
|
|
1619
|
+
console.log("OpenClaw publish sync: trigger deactivated", {
|
|
1620
|
+
workflowId,
|
|
1621
|
+
nodeName: node.name
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
async execute() {
|
|
1627
|
+
const items = this.getInputData();
|
|
1628
|
+
const returnData = [];
|
|
1629
|
+
console.log("OpenClaw publish sync: execute started", {
|
|
1630
|
+
itemCount: items.length,
|
|
1631
|
+
nodeName: this.getNode().name,
|
|
1632
|
+
workflowId: this.getWorkflow().id
|
|
1633
|
+
});
|
|
1634
|
+
let channelConfigs = [];
|
|
1635
|
+
try {
|
|
1636
|
+
const channelData = await this.getInputConnectionData(import_n8n_workflow.NodeConnectionTypes.AiChannel, 0);
|
|
1637
|
+
if (Array.isArray(channelData)) {
|
|
1638
|
+
channelConfigs = channelData;
|
|
1639
|
+
} else if (channelData && isObject(channelData)) {
|
|
1640
|
+
channelConfigs = [channelData];
|
|
1641
|
+
}
|
|
1642
|
+
console.log("OpenClaw publish sync: resolved channel candidates", {
|
|
1643
|
+
channelCount: channelConfigs.length,
|
|
1644
|
+
channelTypes: channelConfigs.map((c) => c.channelType),
|
|
1645
|
+
channels: channelConfigs.map((channelConfig) => ({
|
|
1646
|
+
channelType: channelConfig.channelType,
|
|
1647
|
+
accountId: channelConfig.accountId ?? OPENCLAW_DEFAULT_ACCOUNT_ID,
|
|
1648
|
+
dmPolicy: channelConfig.dmPolicy,
|
|
1649
|
+
groupPolicy: channelConfig.groupPolicy,
|
|
1650
|
+
allowFromCount: channelConfig.allowFrom?.length
|
|
1651
|
+
}))
|
|
1652
|
+
});
|
|
1653
|
+
} catch {
|
|
1654
|
+
console.log("OpenClaw publish sync: no channels connected");
|
|
1655
|
+
}
|
|
1656
|
+
let modelConfig;
|
|
1657
|
+
try {
|
|
1658
|
+
const modelData = await this.getInputConnectionData(import_n8n_workflow.NodeConnectionTypes.AiLanguageModel, 0);
|
|
1659
|
+
console.log("[OpenClawAgentV2] Raw model data from getInputConnectionData", {
|
|
1660
|
+
type: typeof modelData,
|
|
1661
|
+
isArray: Array.isArray(modelData),
|
|
1662
|
+
isObject: isObject(modelData),
|
|
1663
|
+
constructorName: modelData && typeof modelData === "object" ? modelData.constructor?.name : void 0,
|
|
1664
|
+
keys: modelData && typeof modelData === "object" && !Array.isArray(modelData) ? Object.keys(modelData) : void 0,
|
|
1665
|
+
preview: getModelDataPreview(modelData)
|
|
1666
|
+
});
|
|
1667
|
+
let candidate = modelData;
|
|
1668
|
+
if (Array.isArray(modelData) && modelData.length > 0) {
|
|
1669
|
+
candidate = modelData[0];
|
|
1670
|
+
console.log("[OpenClawAgentV2] Model data was array, using first element", {
|
|
1671
|
+
arrayLength: modelData.length,
|
|
1672
|
+
elementType: typeof candidate
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
if (candidate && isObject(candidate) && typeof candidate.modelId === "string") {
|
|
1676
|
+
modelConfig = candidate;
|
|
1677
|
+
console.log("[OpenClawAgentV2] Model sub-node connected", {
|
|
1678
|
+
modelId: modelConfig.modelId,
|
|
1679
|
+
modelSource: modelConfig.modelSource,
|
|
1680
|
+
extraKeys: getObjectKeys(modelConfig.extra),
|
|
1681
|
+
baseUrl: normalizeOptionalString(modelConfig.extra?.baseUrl),
|
|
1682
|
+
api: normalizeOptionalString(modelConfig.extra?.api)
|
|
1683
|
+
});
|
|
1684
|
+
} else if (candidate) {
|
|
1685
|
+
console.log("[OpenClawAgentV2] Model data received but does not match ModelConfig shape", {
|
|
1686
|
+
candidateType: typeof candidate,
|
|
1687
|
+
isObj: isObject(candidate),
|
|
1688
|
+
hasModelId: isObject(candidate) ? typeof candidate.modelId : "n/a",
|
|
1689
|
+
candidateKeys: isObject(candidate) ? Object.keys(candidate) : [],
|
|
1690
|
+
candidatePreview: getModelDataPreview(candidate)
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
} catch (err) {
|
|
1694
|
+
console.log("[OpenClawAgentV2] No Model sub-node connected, using text parameter fallback", {
|
|
1695
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1698
|
+
console.log("OpenClaw publish sync: resolved model sync candidates", {
|
|
1699
|
+
hasModelSubNode: !!modelConfig,
|
|
1700
|
+
modelId: modelConfig?.modelId,
|
|
1701
|
+
modelSource: modelConfig?.modelSource ?? "none"
|
|
1702
|
+
});
|
|
1703
|
+
let pluginConfigs = [];
|
|
1704
|
+
let mcpServerConfigs = [];
|
|
1705
|
+
console.log("[OpenClawAgentV2] About to retrieve OpenClaw AiTool sub-node data");
|
|
1706
|
+
try {
|
|
1707
|
+
const pluginData = await this.getInputConnectionData(import_n8n_workflow.NodeConnectionTypes.AiTool, 0);
|
|
1708
|
+
console.log("[OpenClawAgentV2] Raw AiTool data from getInputConnectionData", {
|
|
1709
|
+
type: typeof pluginData,
|
|
1710
|
+
isNull: pluginData === null,
|
|
1711
|
+
isUndefined: pluginData === void 0,
|
|
1712
|
+
isArray: Array.isArray(pluginData),
|
|
1713
|
+
isObject: isObject(pluginData),
|
|
1714
|
+
preview: pluginData ? JSON.stringify(pluginData).slice(0, 500) : "(empty)"
|
|
1715
|
+
});
|
|
1716
|
+
const isPluginConfig = (item) => isObject(item) && typeof item.pluginSource === "string";
|
|
1717
|
+
const isMcpServerConfig = (item) => isObject(item) && item.mcpServerSource === "openclaw" && typeof item.serverName === "string" && typeof item.url === "string";
|
|
1718
|
+
if (Array.isArray(pluginData)) {
|
|
1719
|
+
pluginConfigs = pluginData.filter(isPluginConfig);
|
|
1720
|
+
mcpServerConfigs = pluginData.filter(isMcpServerConfig);
|
|
1721
|
+
} else if (pluginData && isPluginConfig(pluginData)) {
|
|
1722
|
+
pluginConfigs = [pluginData];
|
|
1723
|
+
} else if (pluginData && isMcpServerConfig(pluginData)) {
|
|
1724
|
+
mcpServerConfigs = [pluginData];
|
|
1725
|
+
}
|
|
1726
|
+
if (pluginConfigs.length > 0) {
|
|
1727
|
+
console.log("[OpenClawAgentV2] Plugin sub-nodes connected", {
|
|
1728
|
+
count: pluginConfigs.length,
|
|
1729
|
+
plugins: pluginConfigs.map((p) => ({
|
|
1730
|
+
source: p.pluginSource,
|
|
1731
|
+
path: p.pluginPath,
|
|
1732
|
+
manifestId: p.pluginManifest?.id,
|
|
1733
|
+
manifestName: p.pluginManifest?.name,
|
|
1734
|
+
cloudId: p.pluginId,
|
|
1735
|
+
cloudVersion: p.pluginVersion
|
|
1736
|
+
}))
|
|
1737
|
+
});
|
|
1738
|
+
} else {
|
|
1739
|
+
console.log("[OpenClawAgentV2] No valid Plugin sub-nodes connected");
|
|
1740
|
+
}
|
|
1741
|
+
if (mcpServerConfigs.length > 0) {
|
|
1742
|
+
console.log("[OpenClawAgentV2] MCP Server sub-nodes connected", {
|
|
1743
|
+
count: mcpServerConfigs.length,
|
|
1744
|
+
servers: mcpServerConfigs.map((server) => ({
|
|
1745
|
+
serverName: server.serverName,
|
|
1746
|
+
url: server.url,
|
|
1747
|
+
transport: server.transport ?? "(openclaw-default)",
|
|
1748
|
+
headerKeys: server.headers ? Object.keys(server.headers) : [],
|
|
1749
|
+
connectionTimeoutMs: server.connectionTimeoutMs
|
|
1750
|
+
}))
|
|
1751
|
+
});
|
|
1752
|
+
} else {
|
|
1753
|
+
console.log("[OpenClawAgentV2] No valid MCP Server sub-nodes connected");
|
|
1754
|
+
}
|
|
1755
|
+
} catch (pluginErr) {
|
|
1756
|
+
console.log("[OpenClawAgentV2] OpenClaw AiTool sub-node connection FAILED", {
|
|
1757
|
+
errorType: pluginErr instanceof Error ? pluginErr.constructor.name : typeof pluginErr,
|
|
1758
|
+
errorMessage: pluginErr instanceof Error ? pluginErr.message : String(pluginErr),
|
|
1759
|
+
errorStack: pluginErr instanceof Error ? pluginErr.stack?.split("\n").slice(0, 5).join("\n") : void 0
|
|
1760
|
+
});
|
|
1761
|
+
console.log(
|
|
1762
|
+
"[OpenClawAgentV2] No Plugin or MCP Server sub-nodes connected (AiTool input error)"
|
|
1763
|
+
);
|
|
1764
|
+
}
|
|
1765
|
+
console.log("OpenClaw publish sync: resolved plugin sync candidates", {
|
|
1766
|
+
pluginCount: pluginConfigs.length,
|
|
1767
|
+
localCount: pluginConfigs.filter((p) => p.pluginSource === "local").length,
|
|
1768
|
+
cloudCount: pluginConfigs.filter((p) => p.pluginSource === "cloud").length,
|
|
1769
|
+
manifestIds: pluginConfigs.filter((p) => p.pluginManifest?.id).map((p) => p.pluginManifest?.id),
|
|
1770
|
+
cloudIds: pluginConfigs.filter((p) => p.pluginId).map((p) => p.pluginId)
|
|
1771
|
+
});
|
|
1772
|
+
console.log("OpenClaw publish sync: resolved MCP Server sync candidates", {
|
|
1773
|
+
serverCount: mcpServerConfigs.length,
|
|
1774
|
+
serverNames: mcpServerConfigs.map((server) => server.serverName),
|
|
1775
|
+
transports: mcpServerConfigs.map((server) => server.transport ?? "(openclaw-default)")
|
|
1776
|
+
});
|
|
1777
|
+
if (mcpServerConfigs.length > 0) {
|
|
1778
|
+
try {
|
|
1779
|
+
const syncResult = syncMcpServerConfig({ mcpServerConfigs });
|
|
1780
|
+
console.log("OpenClaw publish sync: MCP Server config sync completed before execute", {
|
|
1781
|
+
changed: syncResult.changed,
|
|
1782
|
+
configPath: syncResult.configPath,
|
|
1783
|
+
synced: syncResult.synced,
|
|
1784
|
+
skipped: syncResult.skipped.length > 0 ? syncResult.skipped : void 0
|
|
1785
|
+
});
|
|
1786
|
+
} catch (mcpSyncErr) {
|
|
1787
|
+
console.log("OpenClaw publish sync: MCP Server config sync failed before execute", {
|
|
1788
|
+
error: getErrorMessage(mcpSyncErr)
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
1793
|
+
try {
|
|
1794
|
+
const message = normalizeOptionalString(this.getNodeParameter("message", itemIndex));
|
|
1795
|
+
if (!message) {
|
|
1796
|
+
throw new import_n8n_workflow.NodeOperationError(this.getNode(), "Message must not be empty", { itemIndex });
|
|
1797
|
+
}
|
|
1798
|
+
const openClawEnv = {};
|
|
1799
|
+
if (pluginConfigs.length > 0) {
|
|
1800
|
+
const localPlugins = pluginConfigs.filter((p) => p.pluginSource === "local");
|
|
1801
|
+
const cloudPlugins = pluginConfigs.filter((p) => p.pluginSource === "cloud");
|
|
1802
|
+
const pluginPaths = localPlugins.map((p) => p.pluginPath).filter((p) => typeof p === "string" && p.length > 0);
|
|
1803
|
+
const cloudSpecs = cloudPlugins.map((p) => {
|
|
1804
|
+
if (!p.pluginId) return void 0;
|
|
1805
|
+
return p.pluginVersion ? `clawhub:${p.pluginId}@${p.pluginVersion}` : `clawhub:${p.pluginId}`;
|
|
1806
|
+
}).filter((s) => typeof s === "string");
|
|
1807
|
+
if (pluginPaths.length > 0) {
|
|
1808
|
+
openClawEnv.OPENCLAW_PLUGIN_PATHS = pluginPaths.join(";");
|
|
1809
|
+
console.log("[OpenClawAgentV2] Set OPENCLAW_PLUGIN_PATHS env var", {
|
|
1810
|
+
itemIndex,
|
|
1811
|
+
value: openClawEnv.OPENCLAW_PLUGIN_PATHS
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
if (cloudSpecs.length > 0) {
|
|
1815
|
+
openClawEnv.OPENCLAW_CLAWHUB_PLUGINS = cloudSpecs.join(";");
|
|
1816
|
+
console.log("[OpenClawAgentV2] Set OPENCLAW_CLAWHUB_PLUGINS env var", {
|
|
1817
|
+
itemIndex,
|
|
1818
|
+
value: openClawEnv.OPENCLAW_CLAWHUB_PLUGINS
|
|
1819
|
+
});
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
const telegramChannel = channelConfigs.find((c) => c.channelType === "telegram");
|
|
1823
|
+
const primaryChannel = channelConfigs[0];
|
|
1824
|
+
if (telegramChannel?.botToken) {
|
|
1825
|
+
openClawEnv.TELEGRAM_BOT_TOKEN = telegramChannel.botToken;
|
|
1826
|
+
const telegramSync = syncChannelConfig({
|
|
1827
|
+
channelType: "telegram",
|
|
1828
|
+
botToken: telegramChannel.botToken,
|
|
1829
|
+
replyAccount: telegramChannel.accountId,
|
|
1830
|
+
dmPolicy: telegramChannel.dmPolicy,
|
|
1831
|
+
groupPolicy: telegramChannel.groupPolicy,
|
|
1832
|
+
allowFrom: telegramChannel.allowFrom ?? []
|
|
1833
|
+
});
|
|
1834
|
+
console.log("OpenClaw publish sync: Telegram channel config synced", {
|
|
1835
|
+
itemIndex,
|
|
1836
|
+
changed: telegramSync.changed,
|
|
1837
|
+
configPath: telegramSync.configPath,
|
|
1838
|
+
targetPath: telegramSync.targetPath,
|
|
1839
|
+
accountId: telegramSync.accountId ?? OPENCLAW_DEFAULT_ACCOUNT_ID,
|
|
1840
|
+
allowFromCount: telegramChannel.allowFrom?.length ?? 0,
|
|
1841
|
+
allowFromStorePath: telegramSync.allowFromStorePath
|
|
1842
|
+
});
|
|
1843
|
+
}
|
|
1844
|
+
const whatsappChannel = channelConfigs.find((c) => c.channelType === "whatsapp");
|
|
1845
|
+
if (whatsappChannel?.accessToken) {
|
|
1846
|
+
openClawEnv.WHATSAPP_ACCESS_TOKEN = whatsappChannel.accessToken;
|
|
1847
|
+
if (whatsappChannel.phoneNumberId) {
|
|
1848
|
+
openClawEnv.WHATSAPP_PHONE_NUMBER_ID = whatsappChannel.phoneNumberId;
|
|
1849
|
+
}
|
|
1850
|
+
const whatsappSync = syncChannelConfig({
|
|
1851
|
+
channelType: "whatsapp",
|
|
1852
|
+
botToken: whatsappChannel.accessToken,
|
|
1853
|
+
replyAccount: whatsappChannel.accountId
|
|
1854
|
+
});
|
|
1855
|
+
console.log("OpenClaw publish sync: WhatsApp channel config synced", {
|
|
1856
|
+
itemIndex,
|
|
1857
|
+
changed: whatsappSync.changed,
|
|
1858
|
+
configPath: whatsappSync.configPath,
|
|
1859
|
+
targetPath: whatsappSync.targetPath,
|
|
1860
|
+
accountId: whatsappSync.accountId ?? OPENCLAW_DEFAULT_ACCOUNT_ID
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
console.log("OpenClaw publish sync: channel config sync completed", {
|
|
1864
|
+
itemIndex,
|
|
1865
|
+
hasTelegram: !!telegramChannel,
|
|
1866
|
+
hasWhatsApp: !!whatsappChannel,
|
|
1867
|
+
channelCount: channelConfigs.length
|
|
1868
|
+
});
|
|
1869
|
+
let args = ["agent", "--message", message, "--json"];
|
|
1870
|
+
let processTimeoutMs;
|
|
1871
|
+
const gatewayParams = {
|
|
1872
|
+
message,
|
|
1873
|
+
idempotencyKey: (0, import_crypto.randomUUID)()
|
|
1874
|
+
};
|
|
1875
|
+
const selectorType = this.getNodeParameter("selectorType", itemIndex);
|
|
1876
|
+
if (selectorType !== "default") {
|
|
1877
|
+
const parameterName = selectorTypeToParameterName[selectorType];
|
|
1878
|
+
const value = normalizeOptionalString(this.getNodeParameter(parameterName, itemIndex));
|
|
1879
|
+
if (!value) {
|
|
1880
|
+
throw new import_n8n_workflow.NodeOperationError(
|
|
1881
|
+
this.getNode(),
|
|
1882
|
+
`${parameterName} must not be empty when Route By is ${selectorType}`,
|
|
1883
|
+
{ itemIndex }
|
|
1884
|
+
);
|
|
1885
|
+
}
|
|
1886
|
+
args.push(selectorTypeToCliFlag[selectorType], value);
|
|
1887
|
+
if (selectorType === "agent") {
|
|
1888
|
+
gatewayParams.agentId = value;
|
|
1889
|
+
} else if (selectorType === "sessionId") {
|
|
1890
|
+
gatewayParams.sessionId = value;
|
|
1891
|
+
} else {
|
|
1892
|
+
gatewayParams.to = value;
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
const resolvedModel = modelConfig?.modelId;
|
|
1896
|
+
console.log("[OpenClawAgentV2] Model resolution", {
|
|
1897
|
+
itemIndex,
|
|
1898
|
+
connectedModel: modelConfig?.modelId,
|
|
1899
|
+
resolvedModel,
|
|
1900
|
+
modelSource: modelConfig?.modelSource ?? "none"
|
|
1901
|
+
});
|
|
1902
|
+
if (resolvedModel) {
|
|
1903
|
+
args.push("--model", resolvedModel);
|
|
1904
|
+
gatewayParams.model = resolvedModel;
|
|
1905
|
+
}
|
|
1906
|
+
const openCodeFreeAuth = applyOpenCodeFreeAuthEnv({
|
|
1907
|
+
env: openClawEnv,
|
|
1908
|
+
modelConfig,
|
|
1909
|
+
modelId: resolvedModel
|
|
1910
|
+
});
|
|
1911
|
+
console.log("[OpenClawAgentV2] OpenCode free auth env resolution", {
|
|
1912
|
+
itemIndex,
|
|
1913
|
+
resolvedModel,
|
|
1914
|
+
modelSource: modelConfig?.modelSource ?? "text-parameter",
|
|
1915
|
+
applied: openCodeFreeAuth.applied,
|
|
1916
|
+
envVar: openCodeFreeAuth.envVar,
|
|
1917
|
+
reason: openCodeFreeAuth.reason
|
|
1918
|
+
});
|
|
1919
|
+
const nineRouterConfigSync = syncNineRouterModelConfig({
|
|
1920
|
+
modelConfig,
|
|
1921
|
+
modelId: resolvedModel
|
|
1922
|
+
});
|
|
1923
|
+
console.log("[OpenClawAgentV2] 9Router model config sync", {
|
|
1924
|
+
itemIndex,
|
|
1925
|
+
hasModelSubNode: !!modelConfig,
|
|
1926
|
+
resolvedModel,
|
|
1927
|
+
modelSource: modelConfig?.modelSource ?? "text-parameter",
|
|
1928
|
+
selectorType,
|
|
1929
|
+
changed: nineRouterConfigSync.changed,
|
|
1930
|
+
reason: nineRouterConfigSync.reason,
|
|
1931
|
+
targetPath: nineRouterConfigSync.targetPath,
|
|
1932
|
+
modelRef: nineRouterConfigSync.modelRef,
|
|
1933
|
+
configPath: nineRouterConfigSync.configPath,
|
|
1934
|
+
existingBaseUrl: nineRouterConfigSync.existingBaseUrl,
|
|
1935
|
+
restart: "publish-only"
|
|
1936
|
+
});
|
|
1937
|
+
console.log("OpenClaw publish sync: model config sync completed", {
|
|
1938
|
+
itemIndex,
|
|
1939
|
+
resolvedModel,
|
|
1940
|
+
modelSource: modelConfig?.modelSource ?? "text-parameter",
|
|
1941
|
+
syncChanged: nineRouterConfigSync.changed,
|
|
1942
|
+
syncReason: nineRouterConfigSync.reason
|
|
1943
|
+
});
|
|
1944
|
+
const thinking = normalizeOptionalString(this.getNodeParameter("thinking", itemIndex));
|
|
1945
|
+
if (thinking) {
|
|
1946
|
+
args.push("--thinking", thinking);
|
|
1947
|
+
gatewayParams.thinking = thinking;
|
|
1948
|
+
}
|
|
1949
|
+
const runLocally = this.getNodeParameter("local", itemIndex, false) === true;
|
|
1950
|
+
if (runLocally) args.push("--local");
|
|
1951
|
+
const deliverReply = this.getNodeParameter("deliver", itemIndex, false) === true;
|
|
1952
|
+
if (deliverReply) {
|
|
1953
|
+
args.push("--deliver");
|
|
1954
|
+
gatewayParams.deliver = true;
|
|
1955
|
+
}
|
|
1956
|
+
const timeout = Number(
|
|
1957
|
+
this.getNodeParameter("options.timeout", itemIndex, DEFAULT_TIMEOUT_SECONDS)
|
|
1958
|
+
);
|
|
1959
|
+
const timeoutSeconds = Number.isFinite(timeout) && timeout > 0 ? Math.floor(timeout) : DEFAULT_TIMEOUT_SECONDS;
|
|
1960
|
+
args.push("--timeout", String(timeoutSeconds));
|
|
1961
|
+
gatewayParams.timeout = timeoutSeconds;
|
|
1962
|
+
if (deliverReply && primaryChannel) {
|
|
1963
|
+
const replyChannel = primaryChannel.channelType;
|
|
1964
|
+
args.push("--reply-channel", replyChannel);
|
|
1965
|
+
gatewayParams.replyChannel = replyChannel;
|
|
1966
|
+
if (primaryChannel.accountId) {
|
|
1967
|
+
args.push("--reply-account", primaryChannel.accountId);
|
|
1968
|
+
gatewayParams.replyAccountId = primaryChannel.accountId;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
const verbose = normalizeOptionalString(
|
|
1972
|
+
this.getNodeParameter("options.verbose", itemIndex, "")
|
|
1973
|
+
);
|
|
1974
|
+
if (verbose) args.push("--verbose", verbose);
|
|
1975
|
+
const binaryPath = normalizeOptionalString(this.getNodeParameter("options.binaryPath", itemIndex, "")) ?? "openclaw";
|
|
1976
|
+
const workingDirectory = normalizeOptionalString(
|
|
1977
|
+
this.getNodeParameter("options.workingDirectory", itemIndex, "")
|
|
1978
|
+
);
|
|
1979
|
+
if (workingDirectory && (!(0, import_fs.existsSync)(workingDirectory) || !(0, import_fs.statSync)(workingDirectory).isDirectory())) {
|
|
1980
|
+
throw new import_n8n_workflow.NodeOperationError(
|
|
1981
|
+
this.getNode(),
|
|
1982
|
+
`Working directory does not exist or is not a directory: ${workingDirectory}`,
|
|
1983
|
+
{ itemIndex }
|
|
1984
|
+
);
|
|
1985
|
+
}
|
|
1986
|
+
const systemMessage = normalizeOptionalString(
|
|
1987
|
+
this.getNodeParameter("options.systemMessage", itemIndex, "")
|
|
1988
|
+
);
|
|
1989
|
+
if (systemMessage) {
|
|
1990
|
+
if (runLocally) {
|
|
1991
|
+
throw new import_n8n_workflow.NodeOperationError(
|
|
1992
|
+
this.getNode(),
|
|
1993
|
+
"System Message is only supported in OpenClaw Gateway mode. Disable Run Locally to use it.",
|
|
1994
|
+
{ itemIndex }
|
|
1995
|
+
);
|
|
1996
|
+
}
|
|
1997
|
+
gatewayParams.extraSystemPrompt = systemMessage;
|
|
1998
|
+
const rpcTimeoutMs = getWatchdogTimeoutMs(timeoutSeconds);
|
|
1999
|
+
args = getGatewayCallArgs(gatewayParams, rpcTimeoutMs);
|
|
2000
|
+
processTimeoutMs = rpcTimeoutMs + 1e4;
|
|
2001
|
+
}
|
|
2002
|
+
console.log("OpenClaw publish sync: launching CLI", {
|
|
2003
|
+
itemIndex,
|
|
2004
|
+
binaryPath,
|
|
2005
|
+
argCount: args.length,
|
|
2006
|
+
cwd: workingDirectory ?? "(default)",
|
|
2007
|
+
envKeys: Object.keys(openClawEnv),
|
|
2008
|
+
resolvedModel,
|
|
2009
|
+
pluginCount: pluginConfigs.length,
|
|
2010
|
+
mcpServerCount: mcpServerConfigs.length,
|
|
2011
|
+
channelCount: channelConfigs.length
|
|
2012
|
+
});
|
|
2013
|
+
const result = await runOpenClawCli({
|
|
2014
|
+
binaryPath,
|
|
2015
|
+
args,
|
|
2016
|
+
cwd: workingDirectory,
|
|
2017
|
+
timeoutMs: processTimeoutMs ?? getWatchdogTimeoutMs(timeoutSeconds),
|
|
2018
|
+
env: openClawEnv,
|
|
2019
|
+
abortSignal: this.getExecutionCancelSignal()
|
|
2020
|
+
});
|
|
2021
|
+
console.log("OpenClaw publish sync: CLI execution completed", {
|
|
2022
|
+
itemIndex,
|
|
2023
|
+
exitCode: result.exitCode,
|
|
2024
|
+
signal: result.signal,
|
|
2025
|
+
stdoutLength: result.stdout.length,
|
|
2026
|
+
stderrLength: result.stderr.length
|
|
2027
|
+
});
|
|
2028
|
+
if (result.exitCode !== 0) {
|
|
2029
|
+
throw new import_n8n_workflow.NodeOperationError(
|
|
2030
|
+
this.getNode(),
|
|
2031
|
+
result.stderr.trim() || result.stdout.trim() || `OpenClaw CLI exited with code ${result.exitCode ?? `signal ${result.signal}`}`,
|
|
2032
|
+
{ itemIndex }
|
|
2033
|
+
);
|
|
2034
|
+
}
|
|
2035
|
+
const json = parseOpenClawOutput(result.stdout);
|
|
2036
|
+
json.command = result.command;
|
|
2037
|
+
if (this.getNodeParameter("options.includeRawOutput", itemIndex, false) === true) {
|
|
2038
|
+
json.rawOutput = { stdout: result.stdout, stderr: result.stderr };
|
|
2039
|
+
}
|
|
2040
|
+
returnData.push({ json, pairedItem: { item: itemIndex } });
|
|
2041
|
+
} catch (error) {
|
|
2042
|
+
if (this.continueOnFail()) {
|
|
2043
|
+
returnData.push({
|
|
2044
|
+
json: { error: getErrorMessage(error) },
|
|
2045
|
+
pairedItem: { item: itemIndex }
|
|
2046
|
+
});
|
|
2047
|
+
continue;
|
|
2048
|
+
}
|
|
2049
|
+
throw error;
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
return [returnData];
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2056
|
+
0 && (module.exports = {
|
|
2057
|
+
OpenClawAgentV2
|
|
2058
|
+
});
|
|
2059
|
+
//# sourceMappingURL=OpenClawAgentV2.node.js.map
|