@dai_ming/plugin-deliverables 1.0.14 → 1.0.15
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/INSTALL.md +215 -0
- package/README.md +186 -90
- package/agents-rules/deliverables.md +30 -34
- package/index.js +118 -186
- package/mcp-servers/deliverables.js +43 -324
- package/openclaw-plugin.json +2 -4
- package/openclaw.plugin.json +4 -3
- package/package.json +9 -9
- package/skills/deliverables/SKILL.md +12 -20
- package/install.js +0 -559
package/install.js
DELETED
|
@@ -1,559 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
var childProcess = require("child_process");
|
|
5
|
-
var fs = require("fs");
|
|
6
|
-
var os = require("os");
|
|
7
|
-
var path = require("path");
|
|
8
|
-
|
|
9
|
-
var LEGACY_DELIVERABLE_TOOL_NAMES = {
|
|
10
|
-
"upload_deliverable": true,
|
|
11
|
-
"list_deliverables": true,
|
|
12
|
-
"renew_deliverable": true,
|
|
13
|
-
"deliverables__upload_deliverable": true,
|
|
14
|
-
"deliverables__list_deliverables": true,
|
|
15
|
-
"deliverables__renew_deliverable": true
|
|
16
|
-
};
|
|
17
|
-
var DELIVERABLES_UPLOAD_TOOL = "deliverables__upload_deliverable";
|
|
18
|
-
|
|
19
|
-
function usage() {
|
|
20
|
-
console.log([
|
|
21
|
-
"Usage:",
|
|
22
|
-
" node install.js [--home <openclaw-home>] [--plugin-root <dir>] [--no-clear-sessions] [--dry-run]",
|
|
23
|
-
"",
|
|
24
|
-
"Defaults:",
|
|
25
|
-
" --home " + path.join(os.homedir(), ".openclaw"),
|
|
26
|
-
" --plugin-root current package directory"
|
|
27
|
-
].join("\n"));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function parseArgs(argv) {
|
|
31
|
-
var args = {
|
|
32
|
-
home: path.join(os.homedir(), ".openclaw"),
|
|
33
|
-
pluginRoot: __dirname,
|
|
34
|
-
clearSessions: true,
|
|
35
|
-
dryRun: false
|
|
36
|
-
};
|
|
37
|
-
for (var i = 0; i < argv.length; i++) {
|
|
38
|
-
var cur = argv[i];
|
|
39
|
-
if (cur === "--home") {
|
|
40
|
-
i += 1;
|
|
41
|
-
if (i >= argv.length) throw new Error("--home requires a value");
|
|
42
|
-
args.home = path.resolve(argv[i]);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
if (cur === "--plugin-root") {
|
|
46
|
-
i += 1;
|
|
47
|
-
if (i >= argv.length) throw new Error("--plugin-root requires a value");
|
|
48
|
-
args.pluginRoot = path.resolve(argv[i]);
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (cur === "--no-clear-sessions") {
|
|
52
|
-
args.clearSessions = false;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (cur === "--dry-run") {
|
|
56
|
-
args.dryRun = true;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (cur === "--help" || cur === "-h") {
|
|
60
|
-
usage();
|
|
61
|
-
process.exit(0);
|
|
62
|
-
}
|
|
63
|
-
throw new Error("unknown argument: " + cur);
|
|
64
|
-
}
|
|
65
|
-
return args;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function isPlainObject(val) {
|
|
69
|
-
return !!val && typeof val === "object" && !Array.isArray(val);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function deepCopy(val) {
|
|
73
|
-
if (val === undefined) return undefined;
|
|
74
|
-
return JSON.parse(JSON.stringify(val));
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function resolveEnv(val) {
|
|
78
|
-
if (typeof val !== "string") return val;
|
|
79
|
-
return val.replace(/\$\{([^}]+)\}/g, function(_, key) {
|
|
80
|
-
return process.env[key] || "";
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function resolveEnvDeep(val) {
|
|
85
|
-
if (Array.isArray(val)) return val.map(resolveEnvDeep);
|
|
86
|
-
if (isPlainObject(val)) {
|
|
87
|
-
var out = {};
|
|
88
|
-
Object.keys(val).forEach(function(key) {
|
|
89
|
-
out[key] = resolveEnvDeep(val[key]);
|
|
90
|
-
});
|
|
91
|
-
return out;
|
|
92
|
-
}
|
|
93
|
-
return resolveEnv(val);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function normalizeRuntimeName(name) {
|
|
97
|
-
if (typeof name !== "string") return "";
|
|
98
|
-
var out = name.trim();
|
|
99
|
-
if (!out) return "";
|
|
100
|
-
if (out.charAt(0) === "@") {
|
|
101
|
-
var slash = out.indexOf("/");
|
|
102
|
-
out = slash >= 0 ? out.slice(slash + 1) : out.slice(1);
|
|
103
|
-
}
|
|
104
|
-
var at = out.indexOf("@");
|
|
105
|
-
if (at >= 0) out = out.slice(0, at);
|
|
106
|
-
return out.trim();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function ensureDir(dir, dryRun) {
|
|
110
|
-
if (dryRun) return;
|
|
111
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function readJSON(filePath, fallback) {
|
|
115
|
-
try {
|
|
116
|
-
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
117
|
-
} catch (err) {
|
|
118
|
-
return deepCopy(fallback);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function writeJSON(filePath, value, dryRun) {
|
|
123
|
-
if (dryRun) return;
|
|
124
|
-
ensureDir(path.dirname(filePath), dryRun);
|
|
125
|
-
fs.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n");
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function samePath(a, b) {
|
|
129
|
-
try {
|
|
130
|
-
return fs.realpathSync(a) === fs.realpathSync(b);
|
|
131
|
-
} catch (err) {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function copyRecursive(src, dst, dryRun) {
|
|
137
|
-
var stat = fs.lstatSync(src);
|
|
138
|
-
var baseName = path.basename(src);
|
|
139
|
-
if (baseName.indexOf("._") === 0 || /\.tgz$/i.test(baseName)) {
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
if (stat.isSymbolicLink()) {
|
|
143
|
-
var target = fs.realpathSync(src);
|
|
144
|
-
copyRecursive(target, dst, dryRun);
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
if (stat.isDirectory()) {
|
|
148
|
-
ensureDir(dst, dryRun);
|
|
149
|
-
fs.readdirSync(src).forEach(function(entry) {
|
|
150
|
-
if (entry === "node_modules" || entry === ".git" || entry.indexOf("._") === 0 || /\.tgz$/i.test(entry)) return;
|
|
151
|
-
copyRecursive(path.join(src, entry), path.join(dst, entry), dryRun);
|
|
152
|
-
});
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
ensureDir(path.dirname(dst), dryRun);
|
|
156
|
-
if (!dryRun) {
|
|
157
|
-
fs.copyFileSync(src, dst);
|
|
158
|
-
if (path.extname(dst) === ".js") {
|
|
159
|
-
fs.chmodSync(dst, 0o755);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function replaceDirectory(src, dst, dryRun) {
|
|
165
|
-
if (samePath(src, dst)) return false;
|
|
166
|
-
if (!dryRun) fs.rmSync(dst, { recursive: true, force: true });
|
|
167
|
-
copyRecursive(src, dst, dryRun);
|
|
168
|
-
return true;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function stagePluginRoot(pluginRoot, dryRun) {
|
|
172
|
-
if (dryRun) {
|
|
173
|
-
return {
|
|
174
|
-
path: pluginRoot,
|
|
175
|
-
cleanup: function() {}
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
var stageDir = fs.mkdtempSync(path.join(os.tmpdir(), "plugin-deliverables-"));
|
|
179
|
-
copyRecursive(pluginRoot, stageDir, false);
|
|
180
|
-
return {
|
|
181
|
-
path: stageDir,
|
|
182
|
-
cleanup: function() {
|
|
183
|
-
try {
|
|
184
|
-
fs.rmSync(stageDir, { recursive: true, force: true });
|
|
185
|
-
} catch (err) {}
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function normalizePluginEntryMap(entriesCfg, pluginId) {
|
|
191
|
-
if (!isPlainObject(entriesCfg)) return {};
|
|
192
|
-
var merged = {};
|
|
193
|
-
Object.keys(entriesCfg).forEach(function(name) {
|
|
194
|
-
var item = entriesCfg[name];
|
|
195
|
-
if (!isPlainObject(item)) return;
|
|
196
|
-
Object.keys(item).forEach(function(key) {
|
|
197
|
-
merged[key] = deepCopy(item[key]);
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
if (merged.enabled === undefined) merged.enabled = true;
|
|
201
|
-
var out = {};
|
|
202
|
-
out[pluginId] = merged;
|
|
203
|
-
return out;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function collectWorkspaceRoots(home, cfg) {
|
|
207
|
-
var roots = {};
|
|
208
|
-
function add(dir) {
|
|
209
|
-
if (!dir || typeof dir !== "string") return;
|
|
210
|
-
roots[path.resolve(dir)] = true;
|
|
211
|
-
}
|
|
212
|
-
add(path.join(home, "workspace"));
|
|
213
|
-
try {
|
|
214
|
-
fs.readdirSync(home).forEach(function(entry) {
|
|
215
|
-
if (entry === "workspace" || entry.indexOf("workspace-") === 0) {
|
|
216
|
-
add(path.join(home, entry));
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
} catch (err) {}
|
|
220
|
-
if (cfg && cfg.agents && Array.isArray(cfg.agents.list)) {
|
|
221
|
-
cfg.agents.list.forEach(function(agent) {
|
|
222
|
-
if (agent && typeof agent.workspace === "string" && agent.workspace.trim()) {
|
|
223
|
-
add(agent.workspace.trim());
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
return Object.keys(roots);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function collectAgentIDs(home, cfg) {
|
|
231
|
-
var ids = {};
|
|
232
|
-
function add(id) {
|
|
233
|
-
if (!id || typeof id !== "string") return;
|
|
234
|
-
ids[id.trim()] = true;
|
|
235
|
-
}
|
|
236
|
-
add("main");
|
|
237
|
-
try {
|
|
238
|
-
fs.readdirSync(path.join(home, "agents")).forEach(function(entry) {
|
|
239
|
-
if (entry && entry !== "." && entry !== "..") add(entry);
|
|
240
|
-
});
|
|
241
|
-
} catch (err) {}
|
|
242
|
-
if (cfg && cfg.agents && Array.isArray(cfg.agents.list)) {
|
|
243
|
-
cfg.agents.list.forEach(function(agent) {
|
|
244
|
-
if (agent && agent.id) add(String(agent.id));
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
return Object.keys(ids);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function writeText(filePath, content, dryRun) {
|
|
251
|
-
if (dryRun) return;
|
|
252
|
-
ensureDir(path.dirname(filePath), dryRun);
|
|
253
|
-
fs.writeFileSync(filePath, content);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function installSkills(pluginRoot, manifest, home, workspaceRoots, agentIDs, dryRun) {
|
|
257
|
-
var installed = [];
|
|
258
|
-
if (!isPlainObject(manifest.skills)) return installed;
|
|
259
|
-
Object.keys(manifest.skills).forEach(function(skillName) {
|
|
260
|
-
var relFile = manifest.skills[skillName];
|
|
261
|
-
var src = path.join(pluginRoot, relFile);
|
|
262
|
-
var content = fs.readFileSync(src, "utf8");
|
|
263
|
-
workspaceRoots.forEach(function(wsRoot) {
|
|
264
|
-
var skillFile = path.join(wsRoot, "skills", skillName, "SKILL.md");
|
|
265
|
-
writeText(skillFile, content, dryRun);
|
|
266
|
-
ensureDir(path.join(wsRoot, "output"), dryRun);
|
|
267
|
-
installed.push(skillFile);
|
|
268
|
-
});
|
|
269
|
-
agentIDs.forEach(function(agentID) {
|
|
270
|
-
var skillFile = path.join(home, "agents", agentID, "skills", skillName, "SKILL.md");
|
|
271
|
-
writeText(skillFile, content, dryRun);
|
|
272
|
-
installed.push(skillFile);
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
return installed;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
function injectRules(pluginRoot, manifest, workspaceRoots, dryRun) {
|
|
279
|
-
if (!manifest.agents_rules || !manifest.agents_rules.file) return [];
|
|
280
|
-
var startMarker = manifest.agents_rules.start_marker || "";
|
|
281
|
-
var endMarker = manifest.agents_rules.end_marker || "";
|
|
282
|
-
var rules = fs.readFileSync(path.join(pluginRoot, manifest.agents_rules.file), "utf8").trim() + "\n";
|
|
283
|
-
var changed = [];
|
|
284
|
-
workspaceRoots.forEach(function(wsRoot) {
|
|
285
|
-
var agentsFile = path.join(wsRoot, "AGENTS.md");
|
|
286
|
-
var existing = "";
|
|
287
|
-
try {
|
|
288
|
-
existing = fs.readFileSync(agentsFile, "utf8");
|
|
289
|
-
} catch (err) {}
|
|
290
|
-
var next = existing;
|
|
291
|
-
if (startMarker && endMarker && existing.indexOf(startMarker) >= 0) {
|
|
292
|
-
var re = new RegExp("<!--\\s*" + escapeRegExp(startMarker) + "\\s*-->[\\s\\S]*?<!--\\s*" + escapeRegExp(endMarker) + "\\s*-->", "g");
|
|
293
|
-
next = existing.replace(re, rules.trim());
|
|
294
|
-
if (next !== "" && next.charAt(next.length - 1) !== "\n") next += "\n";
|
|
295
|
-
} else if (existing) {
|
|
296
|
-
next = existing.replace(/\s*$/, "") + "\n\n" + rules;
|
|
297
|
-
} else {
|
|
298
|
-
next = rules;
|
|
299
|
-
}
|
|
300
|
-
ensureDir(path.join(wsRoot, "output"), dryRun);
|
|
301
|
-
if (next !== existing) {
|
|
302
|
-
writeText(agentsFile, next, dryRun);
|
|
303
|
-
changed.push(agentsFile);
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
return changed;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function escapeRegExp(text) {
|
|
310
|
-
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function sanitizeToolList(items, isAllow) {
|
|
314
|
-
if (!Array.isArray(items)) return items;
|
|
315
|
-
var out = [];
|
|
316
|
-
var seen = {};
|
|
317
|
-
var hasWildcard = false;
|
|
318
|
-
items.forEach(function(raw) {
|
|
319
|
-
var item = String(raw || "").trim();
|
|
320
|
-
if (!item) return;
|
|
321
|
-
if (item === "*") hasWildcard = true;
|
|
322
|
-
if (LEGACY_DELIVERABLE_TOOL_NAMES[item]) return;
|
|
323
|
-
if (!seen[item]) {
|
|
324
|
-
out.push(item);
|
|
325
|
-
seen[item] = true;
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
if (isAllow && !hasWildcard && !seen[DELIVERABLES_UPLOAD_TOOL]) {
|
|
329
|
-
out.push(DELIVERABLES_UPLOAD_TOOL);
|
|
330
|
-
}
|
|
331
|
-
return out;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function sanitizeToolConfig(toolsCfg) {
|
|
335
|
-
if (!isPlainObject(toolsCfg)) return;
|
|
336
|
-
if (Array.isArray(toolsCfg.allow)) toolsCfg.allow = sanitizeToolList(toolsCfg.allow, true);
|
|
337
|
-
if (Array.isArray(toolsCfg.deny)) toolsCfg.deny = sanitizeToolList(toolsCfg.deny, false);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function applyPluginConfig(cfg, manifest, home) {
|
|
341
|
-
if (!isPlainObject(cfg)) cfg = {};
|
|
342
|
-
var pluginId = normalizeRuntimeName(manifest.name || "plugin-deliverables");
|
|
343
|
-
var mcpRoot = path.join(home, "mcp-servers");
|
|
344
|
-
var extensionsExtraDir = path.join(home, "extensions-extra", pluginId);
|
|
345
|
-
|
|
346
|
-
cfg.plugins = isPlainObject(cfg.plugins) ? cfg.plugins : {};
|
|
347
|
-
cfg.plugins.allow = Array.isArray(cfg.plugins.allow) ? cfg.plugins.allow.slice() : [];
|
|
348
|
-
if (cfg.plugins.allow.indexOf(pluginId) === -1) cfg.plugins.allow.push(pluginId);
|
|
349
|
-
cfg.plugins.entries = isPlainObject(cfg.plugins.entries) ? cfg.plugins.entries : {};
|
|
350
|
-
cfg.plugins.load = isPlainObject(cfg.plugins.load) ? cfg.plugins.load : {};
|
|
351
|
-
var existingLoadPaths = Array.isArray(cfg.plugins.load.paths) ? cfg.plugins.load.paths.slice() : [];
|
|
352
|
-
var nextLoadPaths = [];
|
|
353
|
-
var seenLoadPaths = {};
|
|
354
|
-
[extensionsExtraDir].concat(existingLoadPaths).forEach(function(rawPath) {
|
|
355
|
-
var item = String(rawPath || "").trim();
|
|
356
|
-
if (!item) return;
|
|
357
|
-
var key = path.resolve(item);
|
|
358
|
-
if (seenLoadPaths[key]) return;
|
|
359
|
-
seenLoadPaths[key] = true;
|
|
360
|
-
nextLoadPaths.push(item);
|
|
361
|
-
});
|
|
362
|
-
cfg.plugins.load.paths = nextLoadPaths;
|
|
363
|
-
|
|
364
|
-
var manifestPluginCfg = isPlainObject(manifest.openclaw_config) ? resolveEnvDeep(manifest.openclaw_config) : {};
|
|
365
|
-
var desiredEntries = {};
|
|
366
|
-
if (isPlainObject(manifestPluginCfg.plugins) && isPlainObject(manifestPluginCfg.plugins.entries)) {
|
|
367
|
-
desiredEntries = normalizePluginEntryMap(manifestPluginCfg.plugins.entries, pluginId);
|
|
368
|
-
}
|
|
369
|
-
var desiredPluginEntry = desiredEntries[pluginId] || { enabled: true };
|
|
370
|
-
if (desiredPluginEntry.enabled === undefined) desiredPluginEntry.enabled = true;
|
|
371
|
-
var currentEntry = isPlainObject(cfg.plugins.entries[pluginId]) ? cfg.plugins.entries[pluginId] : {};
|
|
372
|
-
cfg.plugins.entries[pluginId] = Object.assign({}, currentEntry, desiredPluginEntry);
|
|
373
|
-
|
|
374
|
-
cfg.skills = isPlainObject(cfg.skills) ? cfg.skills : {};
|
|
375
|
-
cfg.skills.entries = isPlainObject(cfg.skills.entries) ? cfg.skills.entries : {};
|
|
376
|
-
if (isPlainObject(manifest.skills)) {
|
|
377
|
-
Object.keys(manifest.skills).forEach(function(skillName) {
|
|
378
|
-
var currentSkill = isPlainObject(cfg.skills.entries[skillName]) ? cfg.skills.entries[skillName] : {};
|
|
379
|
-
cfg.skills.entries[skillName] = Object.assign({}, currentSkill, { enabled: true });
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
cfg.mcp = isPlainObject(cfg.mcp) ? cfg.mcp : {};
|
|
384
|
-
cfg.mcp.servers = isPlainObject(cfg.mcp.servers) ? cfg.mcp.servers : {};
|
|
385
|
-
if (isPlainObject(manifest.mcp_servers)) {
|
|
386
|
-
Object.keys(manifest.mcp_servers).forEach(function(serverName) {
|
|
387
|
-
var server = manifest.mcp_servers[serverName];
|
|
388
|
-
if (!isPlainObject(server)) return;
|
|
389
|
-
var generated = {
|
|
390
|
-
command: typeof server.command === "string" && server.command.trim() ? resolveEnv(server.command) : "node"
|
|
391
|
-
};
|
|
392
|
-
if (Array.isArray(server.args) && server.args.length) {
|
|
393
|
-
generated.args = resolveEnvDeep(server.args);
|
|
394
|
-
} else if (typeof server.script === "string" && server.script.trim()) {
|
|
395
|
-
generated.args = [path.join(mcpRoot, path.basename(server.script))];
|
|
396
|
-
}
|
|
397
|
-
if (isPlainObject(server.env)) generated.env = resolveEnvDeep(server.env);
|
|
398
|
-
if (typeof server.cwd === "string" && server.cwd.trim()) generated.cwd = resolveEnv(server.cwd);
|
|
399
|
-
if (typeof server.transport === "string" && server.transport.trim()) generated.transport = resolveEnv(server.transport);
|
|
400
|
-
var currentServer = isPlainObject(cfg.mcp.servers[serverName]) ? cfg.mcp.servers[serverName] : {};
|
|
401
|
-
cfg.mcp.servers[serverName] = Object.assign({}, currentServer, generated);
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
sanitizeToolConfig(cfg.tools);
|
|
406
|
-
if (cfg.agents && Array.isArray(cfg.agents.list)) {
|
|
407
|
-
cfg.agents.list.forEach(function(agent) {
|
|
408
|
-
if (isPlainObject(agent)) sanitizeToolConfig(agent.tools);
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return cfg;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
function installMcpServers(pluginRoot, manifest, home, dryRun) {
|
|
416
|
-
var copied = [];
|
|
417
|
-
if (!isPlainObject(manifest.mcp_servers)) return copied;
|
|
418
|
-
Object.keys(manifest.mcp_servers).forEach(function(serverName) {
|
|
419
|
-
var server = manifest.mcp_servers[serverName];
|
|
420
|
-
if (!isPlainObject(server) || typeof server.script !== "string" || !server.script.trim()) return;
|
|
421
|
-
var src = path.join(pluginRoot, server.script);
|
|
422
|
-
var dst = path.join(home, "mcp-servers", path.basename(server.script));
|
|
423
|
-
ensureDir(path.dirname(dst), dryRun);
|
|
424
|
-
if (!dryRun) {
|
|
425
|
-
fs.copyFileSync(src, dst);
|
|
426
|
-
fs.chmodSync(dst, 0o755);
|
|
427
|
-
}
|
|
428
|
-
copied.push(dst);
|
|
429
|
-
});
|
|
430
|
-
return copied;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
function restartMcpServers(manifest, home, dryRun) {
|
|
434
|
-
var targets = [];
|
|
435
|
-
if (!isPlainObject(manifest.mcp_servers)) return targets;
|
|
436
|
-
|
|
437
|
-
Object.keys(manifest.mcp_servers).forEach(function(serverName) {
|
|
438
|
-
var server = manifest.mcp_servers[serverName];
|
|
439
|
-
if (!isPlainObject(server) || typeof server.script !== "string" || !server.script.trim()) return;
|
|
440
|
-
targets.push(path.join(home, "mcp-servers", path.basename(server.script)));
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
if (dryRun || targets.length === 0) return [];
|
|
444
|
-
|
|
445
|
-
var raw = "";
|
|
446
|
-
try {
|
|
447
|
-
raw = childProcess.execFileSync("ps", ["-eo", "pid=,args="], {
|
|
448
|
-
encoding: "utf8",
|
|
449
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
450
|
-
});
|
|
451
|
-
} catch (err) {
|
|
452
|
-
return [];
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
var restarted = [];
|
|
456
|
-
raw.split(/\r?\n/).forEach(function(line) {
|
|
457
|
-
var match = line.match(/^\s*(\d+)\s+(.*)$/);
|
|
458
|
-
if (!match) return;
|
|
459
|
-
var pid = parseInt(match[1], 10);
|
|
460
|
-
var args = match[2] || "";
|
|
461
|
-
if (!pid || pid === process.pid) return;
|
|
462
|
-
var matchedTarget = "";
|
|
463
|
-
targets.some(function(target) {
|
|
464
|
-
if (args.indexOf(target) >= 0) {
|
|
465
|
-
matchedTarget = target;
|
|
466
|
-
return true;
|
|
467
|
-
}
|
|
468
|
-
return false;
|
|
469
|
-
});
|
|
470
|
-
if (!matchedTarget) return;
|
|
471
|
-
try {
|
|
472
|
-
process.kill(pid, "SIGTERM");
|
|
473
|
-
restarted.push({ pid: pid, script: matchedTarget });
|
|
474
|
-
} catch (err) {}
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
return restarted;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
function clearSessions(home, dryRun) {
|
|
481
|
-
var cleared = [];
|
|
482
|
-
var agentsRoot = path.join(home, "agents");
|
|
483
|
-
try {
|
|
484
|
-
fs.readdirSync(agentsRoot).forEach(function(agentID) {
|
|
485
|
-
var sessionDir = path.join(agentsRoot, agentID, "sessions");
|
|
486
|
-
if (!fs.existsSync(sessionDir)) return;
|
|
487
|
-
fs.readdirSync(sessionDir).forEach(function(entry) {
|
|
488
|
-
if (!/\.jsonl$/.test(entry) && entry !== "sessions.json") return;
|
|
489
|
-
var full = path.join(sessionDir, entry);
|
|
490
|
-
if (!dryRun) fs.rmSync(full, { force: true });
|
|
491
|
-
cleared.push(full);
|
|
492
|
-
});
|
|
493
|
-
});
|
|
494
|
-
} catch (err) {}
|
|
495
|
-
return cleared;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
function main() {
|
|
499
|
-
var args = parseArgs(process.argv.slice(2));
|
|
500
|
-
var pluginRoot = path.resolve(args.pluginRoot);
|
|
501
|
-
var home = path.resolve(args.home);
|
|
502
|
-
var manifestPath = path.join(pluginRoot, "openclaw-plugin.json");
|
|
503
|
-
if (!fs.existsSync(manifestPath)) {
|
|
504
|
-
manifestPath = path.join(pluginRoot, "openclaw.plugin.json");
|
|
505
|
-
}
|
|
506
|
-
if (!fs.existsSync(manifestPath)) {
|
|
507
|
-
throw new Error("missing plugin manifest at " + path.join(pluginRoot, "openclaw-plugin.json"));
|
|
508
|
-
}
|
|
509
|
-
var manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
510
|
-
var pluginName = normalizeRuntimeName(manifest.name || "plugin-deliverables");
|
|
511
|
-
var openclawPath = path.join(home, "openclaw.json");
|
|
512
|
-
var currentConfig = readJSON(openclawPath, {});
|
|
513
|
-
var workspaceRoots = collectWorkspaceRoots(home, currentConfig);
|
|
514
|
-
var agentIDs = collectAgentIDs(home, currentConfig);
|
|
515
|
-
var extensionsDir = path.join(home, "extensions", pluginName);
|
|
516
|
-
var extensionsExtraDir = path.join(home, "extensions-extra", pluginName);
|
|
517
|
-
var staged = stagePluginRoot(pluginRoot, args.dryRun);
|
|
518
|
-
|
|
519
|
-
try {
|
|
520
|
-
ensureDir(home, args.dryRun);
|
|
521
|
-
replaceDirectory(staged.path, extensionsDir, args.dryRun);
|
|
522
|
-
replaceDirectory(staged.path, extensionsExtraDir, args.dryRun);
|
|
523
|
-
var mcpFiles = installMcpServers(staged.path, manifest, home, args.dryRun);
|
|
524
|
-
var restartedMcpServers = restartMcpServers(manifest, home, args.dryRun);
|
|
525
|
-
var skillFiles = installSkills(staged.path, manifest, home, workspaceRoots, agentIDs, args.dryRun);
|
|
526
|
-
var agentFiles = injectRules(staged.path, manifest, workspaceRoots, args.dryRun);
|
|
527
|
-
var nextConfig = applyPluginConfig(currentConfig, manifest, home);
|
|
528
|
-
writeJSON(openclawPath, nextConfig, args.dryRun);
|
|
529
|
-
var clearedSessions = args.clearSessions ? clearSessions(home, args.dryRun) : [];
|
|
530
|
-
|
|
531
|
-
var summary = {
|
|
532
|
-
plugin: pluginName,
|
|
533
|
-
version: manifest.version || "",
|
|
534
|
-
openclaw_home: home,
|
|
535
|
-
dry_run: args.dryRun,
|
|
536
|
-
copied_extensions: [extensionsDir, extensionsExtraDir],
|
|
537
|
-
mcp_servers: mcpFiles,
|
|
538
|
-
restarted_mcp_servers: restartedMcpServers,
|
|
539
|
-
skill_files: skillFiles.length,
|
|
540
|
-
agents_files: agentFiles,
|
|
541
|
-
cleared_sessions: clearedSessions.length,
|
|
542
|
-
openclaw_json: openclawPath,
|
|
543
|
-
load_paths: nextConfig.plugins && nextConfig.plugins.load ? nextConfig.plugins.load.paths : []
|
|
544
|
-
};
|
|
545
|
-
console.log(JSON.stringify(summary, null, 2));
|
|
546
|
-
if (!args.dryRun) {
|
|
547
|
-
console.log("Install complete. Wait about 15s for OpenClaw to reload plugins.allow/plugins.load.paths, then send a new message to verify.");
|
|
548
|
-
}
|
|
549
|
-
} finally {
|
|
550
|
-
staged.cleanup();
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
try {
|
|
555
|
-
main();
|
|
556
|
-
} catch (err) {
|
|
557
|
-
console.error("[plugin-deliverables] install failed:", err && err.stack ? err.stack : err);
|
|
558
|
-
process.exit(1);
|
|
559
|
-
}
|