@spzhongwin/skill-logger-plugin 1.0.14 → 1.0.16
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/index.js +80 -285
- package/openclaw.plugin.json +50 -50
- package/package.json +34 -37
- package/src/active-skills.test.ts +32 -32
- package/src/active-skills.ts +77 -77
- package/src/config-sync.test.ts +165 -165
- package/src/config-sync.ts +544 -544
- package/src/expert-skill-layout.test.ts +196 -0
- package/src/expert-skill-layout.ts +233 -0
- package/src/global.d.ts +4 -0
- package/src/hooks.test.ts +228 -251
- package/src/hooks.ts +494 -517
- package/src/http.ts +61 -61
- package/src/identity.ts +88 -64
- package/src/index.test.ts +53 -53
- package/src/index.ts +218 -226
- package/src/integration.test.ts +119 -119
- package/src/matcher.test.ts +170 -170
- package/src/matcher.ts +393 -393
- package/src/paths.test.ts +57 -57
- package/src/paths.ts +84 -84
- package/src/reporter.test.ts +139 -139
- package/src/reporter.ts +303 -298
- package/src/sample-config.json +72 -72
- package/src/semver.test.ts +33 -23
- package/src/semver.ts +65 -60
- package/src/skill-version.ts +53 -53
- package/src/types.ts +202 -198
- package/src/updater.test.ts +431 -325
- package/src/updater.ts +584 -549
- package/src/ws-client.test.ts +158 -128
- package/src/ws-client.ts +805 -769
- package/test-ws.ts +17 -17
- package/tsconfig.json +18 -14
- package/dist/active-skills.js +0 -67
- package/dist/active-skills.test.js +0 -29
- package/dist/config-sync.js +0 -439
- package/dist/config-sync.test.js +0 -145
- package/dist/hooks.js +0 -337
- package/dist/hooks.test.js +0 -123
- package/dist/http.js +0 -54
- package/dist/identity.js +0 -56
- package/dist/index.test.js +0 -39
- package/dist/integration.test.js +0 -102
- package/dist/matcher.js +0 -362
- package/dist/matcher.test.js +0 -139
- package/dist/paths.js +0 -62
- package/dist/paths.test.js +0 -49
- package/dist/reporter.js +0 -267
- package/dist/reporter.test.js +0 -128
- package/dist/semver.js +0 -64
- package/dist/semver.test.js +0 -21
- package/dist/skill-version.js +0 -23
- package/dist/types.js +0 -9
- package/dist/updater.js +0 -352
- package/dist/updater.test.js +0 -212
- package/dist/ws-client.js +0 -484
package/dist/index.js
CHANGED
|
@@ -106,6 +106,7 @@ import fsSync from "node:fs";
|
|
|
106
106
|
import path3 from "node:path";
|
|
107
107
|
import os2 from "node:os";
|
|
108
108
|
import { execFile } from "node:child_process";
|
|
109
|
+
import { promisify } from "node:util";
|
|
109
110
|
import { randomUUID, createHash } from "node:crypto";
|
|
110
111
|
|
|
111
112
|
// src/skill-version.ts
|
|
@@ -138,37 +139,14 @@ function parseSkillVersion(content) {
|
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
// src/updater.ts
|
|
141
|
-
var
|
|
142
|
+
var execFileAsync = promisify(execFile);
|
|
142
143
|
var ATTEMPT_COOLDOWN_MS = 30 * 60 * 1e3;
|
|
143
144
|
function skillIdentityHash(code, version) {
|
|
144
145
|
return createHash("sha256").update(`${code} ${code} ${version}`).digest("hex");
|
|
145
146
|
}
|
|
146
|
-
function extractorCommandForPlatform(platform, zipPath, destDir) {
|
|
147
|
-
return platform === "darwin" ? { command: "ditto", args: ["-x", "-k", zipPath, destDir] } : { command: "unzip", args: ["-o", "-q", zipPath, "-d", destDir] };
|
|
148
|
-
}
|
|
149
|
-
function runExtractor(command, args) {
|
|
150
|
-
return new Promise((resolve, reject) => {
|
|
151
|
-
const child = execFile(command, args, {
|
|
152
|
-
encoding: "utf8",
|
|
153
|
-
timeout: EXTRACT_TIMEOUT_MS,
|
|
154
|
-
killSignal: "SIGKILL",
|
|
155
|
-
maxBuffer: 1024 * 1024
|
|
156
|
-
}, (error, _stdout, stderr) => {
|
|
157
|
-
if (!error) {
|
|
158
|
-
resolve();
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
const detail = String(stderr || "").trim();
|
|
162
|
-
const timeout = error.killed ? `\uFF0C\u8D85\u8FC7 ${EXTRACT_TIMEOUT_MS}ms \u5DF2\u7EC8\u6B62` : "";
|
|
163
|
-
reject(new Error(`${command} \u89E3\u538B\u5931\u8D25${timeout}: ${detail || error.message}`));
|
|
164
|
-
});
|
|
165
|
-
child.stdin?.end();
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
147
|
async function systemUnzip(zipPath, destDir) {
|
|
169
148
|
await fs3.mkdir(destDir, { recursive: true });
|
|
170
|
-
|
|
171
|
-
await runExtractor(command, args);
|
|
149
|
+
await execFileAsync("unzip", ["-o", "-q", zipPath, "-d", destDir]);
|
|
172
150
|
}
|
|
173
151
|
var SkillUpdater = class {
|
|
174
152
|
getConfig;
|
|
@@ -417,85 +395,36 @@ var SkillUpdater = class {
|
|
|
417
395
|
}
|
|
418
396
|
}
|
|
419
397
|
async manualInstall(options) {
|
|
420
|
-
const { code, force, targetDir
|
|
398
|
+
const { code, force, targetDir } = options;
|
|
421
399
|
let { url, version } = options;
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
currentStage = stage;
|
|
427
|
-
try {
|
|
428
|
-
trace?.(stage, data);
|
|
429
|
-
} catch {
|
|
400
|
+
if (!url) {
|
|
401
|
+
const dl = await this.fetchDownloadUrl(code, version || "latest");
|
|
402
|
+
if (!dl?.url) {
|
|
403
|
+
return { success: false, message: `\u65E0\u6CD5\u83B7\u53D6\u6280\u80FD ${code} \u7684\u4E0B\u8F7D\u5730\u5740` };
|
|
430
404
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
});
|
|
405
|
+
url = dl.url;
|
|
406
|
+
version = dl.version || version;
|
|
407
|
+
}
|
|
408
|
+
const targetSkillPath = path3.join(targetDir, code);
|
|
409
|
+
if (!force && await this.exists(targetSkillPath)) {
|
|
410
|
+
return { success: false, message: `\u6280\u80FD ${code} \u5DF2\u5B58\u5728\u4E8E\u76EE\u6807\u76EE\u5F55` };
|
|
411
|
+
}
|
|
412
|
+
const work = path3.join(this.tmpDir, `slp-manual-${randomUUID()}`);
|
|
413
|
+
await fs3.mkdir(work, { recursive: true });
|
|
439
414
|
try {
|
|
440
|
-
if (!url) {
|
|
441
|
-
const lookupStartedAt = Date.now();
|
|
442
|
-
emit("download_url.lookup.start", { code, version: version || "latest" });
|
|
443
|
-
const dl = await this.fetchDownloadUrl(code, version || "latest");
|
|
444
|
-
if (!dl?.url) {
|
|
445
|
-
const message = `\u65E0\u6CD5\u83B7\u53D6\u6280\u80FD ${code} \u7684\u4E0B\u8F7D\u5730\u5740`;
|
|
446
|
-
emit("install.failed", { stage: currentStage, message, elapsedMs: Date.now() - startedAt });
|
|
447
|
-
return { success: false, message };
|
|
448
|
-
}
|
|
449
|
-
url = dl.url;
|
|
450
|
-
version = dl.version || version;
|
|
451
|
-
emit("download_url.lookup.completed", {
|
|
452
|
-
version: version || "latest",
|
|
453
|
-
url,
|
|
454
|
-
elapsedMs: Date.now() - lookupStartedAt
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
const targetSkillPath = path3.join(targetDir, code);
|
|
458
|
-
if (!force && await this.exists(targetSkillPath)) {
|
|
459
|
-
const message = `\u6280\u80FD ${code} \u5DF2\u5B58\u5728\u4E8E\u76EE\u6807\u76EE\u5F55`;
|
|
460
|
-
emit("install.failed", { stage: currentStage, message, elapsedMs: Date.now() - startedAt });
|
|
461
|
-
return { success: false, message };
|
|
462
|
-
}
|
|
463
|
-
work = path3.join(this.tmpDir, `slp-manual-${randomUUID()}`);
|
|
464
|
-
await fs3.mkdir(work, { recursive: true });
|
|
465
415
|
const zipPath = path3.join(work, "pkg.zip");
|
|
466
|
-
const downloadStartedAt = Date.now();
|
|
467
|
-
emit("download.request.start", { url });
|
|
468
416
|
const res = await this.fetchImpl(url);
|
|
469
|
-
emit("download.headers.received", {
|
|
470
|
-
status: res.status,
|
|
471
|
-
contentLength: res.headers?.get("content-length") || void 0,
|
|
472
|
-
contentType: res.headers?.get("content-type") || void 0,
|
|
473
|
-
elapsedMs: Date.now() - downloadStartedAt
|
|
474
|
-
});
|
|
475
417
|
if (!res.ok) {
|
|
476
|
-
|
|
477
|
-
emit("install.failed", { stage: currentStage, message, elapsedMs: Date.now() - startedAt });
|
|
478
|
-
return { success: false, message };
|
|
418
|
+
return { success: false, message: `\u4E0B\u8F7D\u5931\u8D25: HTTP ${res.status}` };
|
|
479
419
|
}
|
|
480
420
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
481
|
-
emit("download.body.completed", {
|
|
482
|
-
bytes: buf.byteLength,
|
|
483
|
-
elapsedMs: Date.now() - downloadStartedAt
|
|
484
|
-
});
|
|
485
421
|
await fs3.writeFile(zipPath, buf);
|
|
486
|
-
emit("download.file.written", { bytes: buf.byteLength });
|
|
487
422
|
const staging = path3.join(work, "staging");
|
|
488
|
-
const unzipStartedAt = Date.now();
|
|
489
|
-
emit("unzip.start", { extractor: process.platform === "darwin" ? "ditto" : "unzip" });
|
|
490
423
|
await this.unzip(zipPath, staging);
|
|
491
|
-
emit("unzip.completed", { elapsedMs: Date.now() - unzipStartedAt });
|
|
492
424
|
const srcRoot = await this.locateSkillRoot(staging, 0);
|
|
493
425
|
if (!srcRoot) {
|
|
494
|
-
|
|
495
|
-
emit("install.failed", { stage: currentStage, message, elapsedMs: Date.now() - startedAt });
|
|
496
|
-
return { success: false, message };
|
|
426
|
+
return { success: false, message: `\u4E0B\u8F7D\u5305\u5185\u672A\u627E\u5230 SKILL.md\uFF0C\u975E\u6CD5\u7684\u6280\u80FD\u5305\u7ED3\u6784` };
|
|
497
427
|
}
|
|
498
|
-
emit("skill_root.located");
|
|
499
428
|
const metaPath = path3.join(srcRoot, ".meta.json");
|
|
500
429
|
if (!await this.exists(metaPath)) {
|
|
501
430
|
let parsedVersion = version || "unknown";
|
|
@@ -513,37 +442,13 @@ var SkillUpdater = class {
|
|
|
513
442
|
publishedAt: Date.now()
|
|
514
443
|
}, null, 2));
|
|
515
444
|
}
|
|
516
|
-
|
|
517
|
-
for (let index = 0; index < targets.length; index += 1) {
|
|
518
|
-
const rootDir = targets[index];
|
|
519
|
-
const targetPath = path3.join(rootDir, code);
|
|
520
|
-
const replaceStartedAt = Date.now();
|
|
521
|
-
emit("target.replace.start", { targetDir: rootDir, targetIndex: index, targetCount: targets.length });
|
|
522
|
-
await this.replaceDir(srcRoot, targetPath);
|
|
523
|
-
emit("target.replace.completed", {
|
|
524
|
-
targetDir: rootDir,
|
|
525
|
-
targetIndex: index,
|
|
526
|
-
targetCount: targets.length,
|
|
527
|
-
elapsedMs: Date.now() - replaceStartedAt
|
|
528
|
-
});
|
|
529
|
-
}
|
|
530
|
-
emit("install.completed", { elapsedMs: Date.now() - startedAt, targetCount: targets.length });
|
|
445
|
+
await this.replaceDir(srcRoot, targetSkillPath);
|
|
531
446
|
return { success: true, message: `\u6280\u80FD ${code} \u5B89\u88C5/\u66F4\u65B0\u6210\u529F` };
|
|
532
447
|
} catch (err) {
|
|
533
|
-
emit("install.failed", {
|
|
534
|
-
stage: currentStage,
|
|
535
|
-
errorName: err?.name,
|
|
536
|
-
message: err?.message || String(err),
|
|
537
|
-
stack: err?.stack,
|
|
538
|
-
elapsedMs: Date.now() - startedAt
|
|
539
|
-
});
|
|
540
448
|
return { success: false, message: `\u6267\u884C\u51FA\u9519: ${err.message}` };
|
|
541
449
|
} finally {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
});
|
|
545
|
-
emit("cleanup.completed", { elapsedMs: Date.now() - startedAt });
|
|
546
|
-
}
|
|
450
|
+
await fs3.rm(work, { recursive: true, force: true }).catch(() => {
|
|
451
|
+
});
|
|
547
452
|
}
|
|
548
453
|
}
|
|
549
454
|
};
|
|
@@ -1376,11 +1281,11 @@ import path6 from "node:path";
|
|
|
1376
1281
|
// src/identity.ts
|
|
1377
1282
|
import os3 from "node:os";
|
|
1378
1283
|
import { execFile as execFile2 } from "node:child_process";
|
|
1379
|
-
import { promisify } from "node:util";
|
|
1380
|
-
var
|
|
1284
|
+
import { promisify as promisify2 } from "node:util";
|
|
1285
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
1381
1286
|
async function getGitConfigValue(key) {
|
|
1382
1287
|
try {
|
|
1383
|
-
const { stdout } = await
|
|
1288
|
+
const { stdout } = await execFileAsync2("git", ["config", "--global", key]);
|
|
1384
1289
|
return stdout.trim();
|
|
1385
1290
|
} catch {
|
|
1386
1291
|
console.warn(
|
|
@@ -1638,7 +1543,6 @@ var Reporter = class {
|
|
|
1638
1543
|
import WebSocket from "ws";
|
|
1639
1544
|
import path7 from "path";
|
|
1640
1545
|
import fs6 from "fs/promises";
|
|
1641
|
-
import { DatabaseSync } from "node:sqlite";
|
|
1642
1546
|
var HEARTBEAT_INTERVAL_MS = 3e4;
|
|
1643
1547
|
var HEARTBEAT_ACK_TIMEOUT_MS = 75e3;
|
|
1644
1548
|
var AGENT_SCAN_INTERVAL_MS = 3 * 60 * 1e3;
|
|
@@ -1658,24 +1562,6 @@ function normalizeAssistantUserId(userId) {
|
|
|
1658
1562
|
if (!ASSISTANT_WORKSPACE_ID_RE.test(pureId)) return void 0;
|
|
1659
1563
|
return pureId;
|
|
1660
1564
|
}
|
|
1661
|
-
function shouldSyncBuiltInTemplate(action, isBuiltIn) {
|
|
1662
|
-
return action === "UPDATE_SKILL" && isBuiltIn === true;
|
|
1663
|
-
}
|
|
1664
|
-
function defaultOpenclawSqlitePath() {
|
|
1665
|
-
return path7.join(openclawHome(), "state", "openclaw.sqlite");
|
|
1666
|
-
}
|
|
1667
|
-
function readCronJobsByAgentId(agentId, sqlitePath = defaultOpenclawSqlitePath(), onError) {
|
|
1668
|
-
let db;
|
|
1669
|
-
try {
|
|
1670
|
-
db = new DatabaseSync(sqlitePath, { readOnly: true });
|
|
1671
|
-
return db.prepare("SELECT * FROM cron_jobs WHERE agent_id = ?").all(agentId);
|
|
1672
|
-
} catch (err) {
|
|
1673
|
-
onError?.(err);
|
|
1674
|
-
return [];
|
|
1675
|
-
} finally {
|
|
1676
|
-
db?.close();
|
|
1677
|
-
}
|
|
1678
|
-
}
|
|
1679
1565
|
var GatewayWsClient = class {
|
|
1680
1566
|
ws = null;
|
|
1681
1567
|
options;
|
|
@@ -1712,14 +1598,6 @@ var GatewayWsClient = class {
|
|
|
1712
1598
|
} catch (e) {
|
|
1713
1599
|
}
|
|
1714
1600
|
}
|
|
1715
|
-
createInstallTrace(context) {
|
|
1716
|
-
return (stage, data) => {
|
|
1717
|
-
this.appendLogToFile(stage === "install.failed" ? "ERROR" : "INFO", "Install", stage, {
|
|
1718
|
-
...context,
|
|
1719
|
-
...data
|
|
1720
|
-
});
|
|
1721
|
-
};
|
|
1722
|
-
}
|
|
1723
1601
|
constructor(options) {
|
|
1724
1602
|
this.options = options;
|
|
1725
1603
|
}
|
|
@@ -1971,39 +1849,10 @@ var GatewayWsClient = class {
|
|
|
1971
1849
|
* 核心指令分发中心:完全跳过沙盒,基于 userId 直接进行底层物理文件操作
|
|
1972
1850
|
*/
|
|
1973
1851
|
async handleMessage(msg) {
|
|
1974
|
-
const { action, userId, code, url, force, version, replyId
|
|
1975
|
-
this.appendLogToFile("INFO", "Command", `Received WS message`, {
|
|
1976
|
-
|
|
1977
|
-
userId
|
|
1978
|
-
code,
|
|
1979
|
-
version,
|
|
1980
|
-
replyId,
|
|
1981
|
-
isBuiltIn,
|
|
1982
|
-
hasDirectUrl: Boolean(url)
|
|
1983
|
-
});
|
|
1984
|
-
if (!action) {
|
|
1985
|
-
this.appendLogToFile("WARN", "Command", `Message dropped: missing action`, msg);
|
|
1986
|
-
return;
|
|
1987
|
-
}
|
|
1988
|
-
if (action === "GET_CRON_JOBS_BY_AGENT_ID") {
|
|
1989
|
-
const agentId = typeof msg.agent_id === "string" ? msg.agent_id : msg.agentId;
|
|
1990
|
-
if (!agentId) {
|
|
1991
|
-
this.reply(replyId, { success: false, message: "Missing agent_id parameter", action });
|
|
1992
|
-
return;
|
|
1993
|
-
}
|
|
1994
|
-
try {
|
|
1995
|
-
const data = readCronJobsByAgentId(agentId, defaultOpenclawSqlitePath(), (err) => {
|
|
1996
|
-
this.appendLogToFile("WARN", "Command", `GET_CRON_JOBS_BY_AGENT_ID sqlite lookup skipped`, err);
|
|
1997
|
-
});
|
|
1998
|
-
this.reply(replyId, { success: true, data, action });
|
|
1999
|
-
} catch (err) {
|
|
2000
|
-
this.appendLogToFile("ERROR", "Command", `GET_CRON_JOBS_BY_AGENT_ID threw`, err);
|
|
2001
|
-
this.reply(replyId, { success: false, message: err.message, action });
|
|
2002
|
-
}
|
|
2003
|
-
return;
|
|
2004
|
-
}
|
|
2005
|
-
if (!userId) {
|
|
2006
|
-
this.appendLogToFile("WARN", "Command", `Message dropped: missing userId`, msg);
|
|
1852
|
+
const { action, userId, code, url, force, version, replyId } = msg;
|
|
1853
|
+
this.appendLogToFile("INFO", "Command", `Received WS message`, { action, userId, code, version, replyId });
|
|
1854
|
+
if (!action || !userId) {
|
|
1855
|
+
this.appendLogToFile("WARN", "Command", `Message dropped: missing action or userId`, msg);
|
|
2007
1856
|
return;
|
|
2008
1857
|
}
|
|
2009
1858
|
const safeCode = code ? path7.basename(code) : void 0;
|
|
@@ -2023,8 +1872,7 @@ var GatewayWsClient = class {
|
|
|
2023
1872
|
url,
|
|
2024
1873
|
version,
|
|
2025
1874
|
force: force !== false,
|
|
2026
|
-
targetDir
|
|
2027
|
-
trace: this.createInstallTrace({ action, replyId, userId, code: safeCode })
|
|
1875
|
+
targetDir
|
|
2028
1876
|
});
|
|
2029
1877
|
this.reply(replyId, { success: result.success, message: result.message, action });
|
|
2030
1878
|
} else if (action === "UNINSTALL_SKILL") {
|
|
@@ -2059,7 +1907,7 @@ var GatewayWsClient = class {
|
|
|
2059
1907
|
}
|
|
2060
1908
|
const metaPath = path7.join(skillDir, ".meta.json");
|
|
2061
1909
|
let isPlatform = false;
|
|
2062
|
-
let
|
|
1910
|
+
let isBuiltIn = e.isSymbolicLink();
|
|
2063
1911
|
let metaData = null;
|
|
2064
1912
|
let name = e.name;
|
|
2065
1913
|
let description = "";
|
|
@@ -2080,7 +1928,7 @@ var GatewayWsClient = class {
|
|
|
2080
1928
|
const parsed = JSON.parse(metaContent);
|
|
2081
1929
|
if (parsed) {
|
|
2082
1930
|
if (parsed.ownerId === "CMS" || parsed.ownerId === "CMS_COMPAT") isPlatform = true;
|
|
2083
|
-
if (parsed.isBuiltIn === true || parsed.ownerId === "built-in")
|
|
1931
|
+
if (parsed.isBuiltIn === true || parsed.ownerId === "built-in") isBuiltIn = true;
|
|
2084
1932
|
metaData = parsed;
|
|
2085
1933
|
}
|
|
2086
1934
|
} catch (err) {
|
|
@@ -2091,7 +1939,7 @@ var GatewayWsClient = class {
|
|
|
2091
1939
|
list.push({
|
|
2092
1940
|
code: e.name,
|
|
2093
1941
|
isPlatform: true,
|
|
2094
|
-
isBuiltIn
|
|
1942
|
+
isBuiltIn,
|
|
2095
1943
|
version: skillVersion,
|
|
2096
1944
|
name,
|
|
2097
1945
|
description,
|
|
@@ -2101,7 +1949,7 @@ var GatewayWsClient = class {
|
|
|
2101
1949
|
list.push({
|
|
2102
1950
|
code: e.name,
|
|
2103
1951
|
isPlatform: false,
|
|
2104
|
-
isBuiltIn
|
|
1952
|
+
isBuiltIn,
|
|
2105
1953
|
version: skillVersion,
|
|
2106
1954
|
name,
|
|
2107
1955
|
description
|
|
@@ -2112,60 +1960,21 @@ var GatewayWsClient = class {
|
|
|
2112
1960
|
} else if (action === "UPDATE_SKILL") {
|
|
2113
1961
|
if (!safeCode) throw new Error("Missing code parameter");
|
|
2114
1962
|
const delayMs = Math.random() * 5e3;
|
|
2115
|
-
const syncBuiltInTemplate = shouldSyncBuiltInTemplate(action, isBuiltIn);
|
|
2116
1963
|
console.log(`[skill-logger-plugin][WS] Scheduled UPDATE for user ${userId}, code: ${safeCode} in ${Math.round(delayMs)}ms`);
|
|
2117
|
-
this.appendLogToFile("INFO", "Command", `Scheduled UPDATE_SKILL`, {
|
|
2118
|
-
userId,
|
|
2119
|
-
code: safeCode,
|
|
2120
|
-
version,
|
|
2121
|
-
isBuiltIn,
|
|
2122
|
-
syncBuiltInTemplate,
|
|
2123
|
-
delayMs: Math.round(delayMs)
|
|
2124
|
-
});
|
|
1964
|
+
this.appendLogToFile("INFO", "Command", `Scheduled UPDATE_SKILL`, { userId, code: safeCode, version, delayMs: Math.round(delayMs) });
|
|
2125
1965
|
setTimeout(async () => {
|
|
2126
1966
|
try {
|
|
2127
|
-
|
|
2128
|
-
const userSkillDir = path7.join(openclawHome(), `workspace-assistant-${pureId}`, ".user", "skills", safeCode);
|
|
2129
|
-
try {
|
|
2130
|
-
await fs6.stat(userSkillDir);
|
|
2131
|
-
additionalTargetDirs.push(userSkillDir);
|
|
2132
|
-
} catch {
|
|
2133
|
-
}
|
|
2134
|
-
const result = await this.options.updater.manualInstall({
|
|
1967
|
+
await this.options.updater.manualInstall({
|
|
2135
1968
|
code: safeCode,
|
|
2136
1969
|
url,
|
|
2137
1970
|
version,
|
|
2138
1971
|
force: true,
|
|
2139
|
-
targetDir
|
|
2140
|
-
additionalTargetDirs,
|
|
2141
|
-
trace: this.createInstallTrace({
|
|
2142
|
-
action,
|
|
2143
|
-
replyId,
|
|
2144
|
-
userId,
|
|
2145
|
-
code: safeCode,
|
|
2146
|
-
isBuiltIn,
|
|
2147
|
-
syncBuiltInTemplate
|
|
2148
|
-
})
|
|
2149
|
-
});
|
|
2150
|
-
this.appendLogToFile(result.success ? "INFO" : "ERROR", "Command", `UPDATE_SKILL completed`, {
|
|
2151
|
-
userId,
|
|
2152
|
-
code: safeCode,
|
|
2153
|
-
replyId,
|
|
2154
|
-
success: result.success,
|
|
2155
|
-
message: result.message,
|
|
2156
|
-
syncBuiltInTemplate
|
|
1972
|
+
targetDir
|
|
2157
1973
|
});
|
|
2158
1974
|
if (replyId) {
|
|
2159
|
-
this.reply(replyId, { success:
|
|
1975
|
+
this.reply(replyId, { success: true, message: `Skill ${safeCode} updated successfully`, action });
|
|
2160
1976
|
}
|
|
2161
1977
|
} catch (e) {
|
|
2162
|
-
this.appendLogToFile("ERROR", "Command", `UPDATE_SKILL threw`, {
|
|
2163
|
-
userId,
|
|
2164
|
-
code: safeCode,
|
|
2165
|
-
replyId,
|
|
2166
|
-
message: e?.message || String(e),
|
|
2167
|
-
stack: e?.stack
|
|
2168
|
-
});
|
|
2169
1978
|
if (replyId) this.reply(replyId, { success: false, message: e.message, action });
|
|
2170
1979
|
}
|
|
2171
1980
|
}, delayMs);
|
|
@@ -2176,25 +1985,11 @@ var GatewayWsClient = class {
|
|
|
2176
1985
|
this.appendLogToFile("INFO", "Command", `INSTALL_EXPERT received`, { userId, code: safeCode, version: version2, downloadUrl });
|
|
2177
1986
|
const userSkillRoot = path7.join(openclawHome(), `workspace-assistant-${pureId}`, ".user");
|
|
2178
1987
|
const expertTarget = path7.join(userSkillRoot, "experts", safeCode);
|
|
2179
|
-
|
|
2180
|
-
const
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
const existing = JSON.parse(raw);
|
|
2184
|
-
if (existing.version && existing.version === (version2 || "1.0.0")) {
|
|
2185
|
-
skipInstall = true;
|
|
2186
|
-
}
|
|
2187
|
-
} catch {
|
|
1988
|
+
await fs6.mkdir(path7.dirname(expertTarget), { recursive: true });
|
|
1989
|
+
const expertResult = await this.options.updater.installZipFromUrl(downloadUrl, expertTarget);
|
|
1990
|
+
if (!expertResult.success) {
|
|
1991
|
+
throw new Error(`\u4E13\u5BB6\u5B89\u88C5\u5931\u8D25: ${expertResult.message}`);
|
|
2188
1992
|
}
|
|
2189
|
-
if (!skipInstall) {
|
|
2190
|
-
await fs6.mkdir(path7.dirname(expertTarget), { recursive: true });
|
|
2191
|
-
const expertResult = await this.options.updater.installZipFromUrl(downloadUrl, expertTarget);
|
|
2192
|
-
if (!expertResult.success) {
|
|
2193
|
-
throw new Error(`\u4E13\u5BB6\u5B89\u88C5\u5931\u8D25: ${expertResult.message}`);
|
|
2194
|
-
}
|
|
2195
|
-
}
|
|
2196
|
-
const meta = { code: safeCode, name, version: version2 || "1.0.0", installedAt: Date.now() };
|
|
2197
|
-
await fs6.writeFile(metaPath, JSON.stringify(meta, null, 2));
|
|
2198
1993
|
const skillTargetRoot = path7.join(userSkillRoot, "skills");
|
|
2199
1994
|
await fs6.mkdir(skillTargetRoot, { recursive: true });
|
|
2200
1995
|
const skillResults = [];
|
|
@@ -2219,42 +2014,48 @@ var GatewayWsClient = class {
|
|
|
2219
2014
|
action,
|
|
2220
2015
|
data: { expertCode: safeCode, skills: skillResults }
|
|
2221
2016
|
});
|
|
2222
|
-
} else if (action === "
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
this.reply(replyId, { success: true, message: `\u4E13\u5BB6 ${safeCode} \u5DF2\u5378\u8F7D`, action });
|
|
2229
|
-
} else if (action === "LIST_EXPERTS") {
|
|
2230
|
-
console.log(`[skill-logger-plugin][WS] Executing LIST_EXPERTS for user ${userId}`);
|
|
2231
|
-
this.appendLogToFile("INFO", "Command", `LIST_EXPERTS received`, { userId });
|
|
2232
|
-
const expertsDir = path7.join(openclawHome(), `workspace-assistant-${pureId}`, ".user", "experts");
|
|
2233
|
-
const list = [];
|
|
2017
|
+
} else if (action === "GET_EXPERT_REGISTRY") {
|
|
2018
|
+
console.log(`[skill-logger-plugin][WS] Executing GET_EXPERT_REGISTRY for user ${userId}`);
|
|
2019
|
+
this.appendLogToFile("INFO", "Command", `GET_EXPERT_REGISTRY received`, { userId });
|
|
2020
|
+
const registryFilePath = path7.join(openclawHome(), `workspace-assistant-${pureId}`, ".user", "expert-registry.yaml");
|
|
2021
|
+
let content = "";
|
|
2022
|
+
let fileExists = false;
|
|
2234
2023
|
try {
|
|
2235
|
-
const stat = await fs6.stat(
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2024
|
+
const stat = await fs6.stat(registryFilePath);
|
|
2025
|
+
fileExists = stat.isFile();
|
|
2026
|
+
} catch (err) {
|
|
2027
|
+
if (err?.code !== "ENOENT") throw err;
|
|
2028
|
+
}
|
|
2029
|
+
if (fileExists) {
|
|
2030
|
+
content = await fs6.readFile(registryFilePath, "utf8");
|
|
2031
|
+
const expertsList = [];
|
|
2032
|
+
const lines = content.split("\n");
|
|
2033
|
+
let currentExpert = null;
|
|
2034
|
+
for (const line of lines) {
|
|
2035
|
+
const trimmed = line.trim();
|
|
2036
|
+
if (trimmed.startsWith("#")) continue;
|
|
2037
|
+
const idMatch = line.match(/^\s*-\s*id:\s*(.+)$/);
|
|
2038
|
+
if (idMatch) {
|
|
2039
|
+
if (currentExpert && currentExpert.id) expertsList.push(currentExpert);
|
|
2040
|
+
currentExpert = { id: idMatch[1].trim() };
|
|
2041
|
+
continue;
|
|
2042
|
+
}
|
|
2043
|
+
if (currentExpert) {
|
|
2044
|
+
const nameMatch = line.match(/^\s*name:\s*(.+)$/);
|
|
2045
|
+
if (nameMatch) {
|
|
2046
|
+
currentExpert.name = nameMatch[1].trim();
|
|
2047
|
+
}
|
|
2048
|
+
const descMatch = line.match(/^\s*description:\s*(.+)$/);
|
|
2049
|
+
if (descMatch) {
|
|
2050
|
+
currentExpert.description = descMatch[1].trim();
|
|
2252
2051
|
}
|
|
2253
2052
|
}
|
|
2254
2053
|
}
|
|
2255
|
-
|
|
2054
|
+
if (currentExpert && currentExpert.id) expertsList.push(currentExpert);
|
|
2055
|
+
this.reply(replyId, { success: true, data: expertsList, action });
|
|
2056
|
+
} else {
|
|
2057
|
+
this.reply(replyId, { success: false, message: `\u672A\u627E\u5230\u7528\u6237\u6280\u80FD\u914D\u7F6E\u6587\u4EF6\uFF0C\u53EF\u80FD\u5C1A\u672A\u6CE8\u518C\u6216\u6587\u4EF6\u5DF2\u4E22\u5931`, action });
|
|
2256
2058
|
}
|
|
2257
|
-
this.reply(replyId, { success: true, data: list, action });
|
|
2258
2059
|
} else {
|
|
2259
2060
|
console.warn(`[skill-logger-plugin][WS] Unknown action: ${action}`);
|
|
2260
2061
|
this.appendLogToFile("WARN", "Command", `Unknown action: ${action}`);
|
|
@@ -2271,13 +2072,7 @@ var GatewayWsClient = class {
|
|
|
2271
2072
|
this.appendLogToFile("WARN", "Command", `Cannot reply: WS closed or missing replyId`, { replyId });
|
|
2272
2073
|
return;
|
|
2273
2074
|
}
|
|
2274
|
-
this.ws.send(JSON.stringify({ type: "REPLY", replyId, ...payload })
|
|
2275
|
-
if (err) {
|
|
2276
|
-
this.appendLogToFile("ERROR", "Command", `Reply send failed`, { replyId, message: err.message });
|
|
2277
|
-
return;
|
|
2278
|
-
}
|
|
2279
|
-
this.appendLogToFile("INFO", "Command", `Reply sent`, { replyId });
|
|
2280
|
-
});
|
|
2075
|
+
this.ws.send(JSON.stringify({ type: "REPLY", replyId, ...payload }));
|
|
2281
2076
|
}
|
|
2282
2077
|
destroy() {
|
|
2283
2078
|
this.isDestroyed = true;
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "skill-logger-plugin",
|
|
3
|
-
"name": "Skill Logger",
|
|
4
|
-
"description": "追踪 openclaw skill 内功能点(脚本/命令/工具/HTTP)使用与报错,落本地并批量上报",
|
|
5
|
-
"activation": {
|
|
6
|
-
"onStartup": true
|
|
7
|
-
},
|
|
8
|
-
"configSchema": {
|
|
9
|
-
"type": "object",
|
|
10
|
-
"additionalProperties": true,
|
|
11
|
-
"properties": {
|
|
12
|
-
"pluginId": {
|
|
13
|
-
"type": "string",
|
|
14
|
-
"description": "自定义插件 ID"
|
|
15
|
-
},
|
|
16
|
-
"pluginName": {
|
|
17
|
-
"type": "string",
|
|
18
|
-
"description": "自定义插件名称"
|
|
19
|
-
},
|
|
20
|
-
"platformBaseUrl": {
|
|
21
|
-
"type": "string",
|
|
22
|
-
"description": "拉取 skill 标准配置的服务地址;缺省时使用内置静态桩"
|
|
23
|
-
},
|
|
24
|
-
"reportBaseUrl": {
|
|
25
|
-
"type": "string",
|
|
26
|
-
"description": "批量上报的服务地址;缺省时只落本地、不上报"
|
|
27
|
-
},
|
|
28
|
-
"authToken": {
|
|
29
|
-
"type": "string",
|
|
30
|
-
"description": "拉取/上报接口的 Authorization 头值(如 'Bearer xxx')"
|
|
31
|
-
},
|
|
32
|
-
"recordUnattributed": {
|
|
33
|
-
"type": "boolean",
|
|
34
|
-
"description": "是否记录无法归属到具体功能点的 exec 调用(默认 true)"
|
|
35
|
-
},
|
|
36
|
-
"enableFileLog": {
|
|
37
|
-
"type": "boolean",
|
|
38
|
-
"description": "是否开启插件专属文件日志(落盘为 skill-logger.err),用于排查 WS 连接和指令运行情况"
|
|
39
|
-
},
|
|
40
|
-
"autoUpdateSkills": {
|
|
41
|
-
"type": "boolean",
|
|
42
|
-
"description": "是否开启 skill 版本自动更新(检测到本地落后于平台最新版本时下载并覆盖各 workspace 下的副本);默认开启,仅显式设为 false 才关闭"
|
|
43
|
-
},
|
|
44
|
-
"versionCheckIntervalMinutes": {
|
|
45
|
-
"type": "number",
|
|
46
|
-
"description": "版本检查+自动更新的周期(分钟)。每隔该周期按本地所有已装 skill 向服务端拉最新版本并按需更新;默认 30。本地扫描发现固定 3 分钟,与此独立"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "skill-logger-plugin",
|
|
3
|
+
"name": "Skill Logger",
|
|
4
|
+
"description": "追踪 openclaw skill 内功能点(脚本/命令/工具/HTTP)使用与报错,落本地并批量上报",
|
|
5
|
+
"activation": {
|
|
6
|
+
"onStartup": true
|
|
7
|
+
},
|
|
8
|
+
"configSchema": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"additionalProperties": true,
|
|
11
|
+
"properties": {
|
|
12
|
+
"pluginId": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "自定义插件 ID"
|
|
15
|
+
},
|
|
16
|
+
"pluginName": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "自定义插件名称"
|
|
19
|
+
},
|
|
20
|
+
"platformBaseUrl": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "拉取 skill 标准配置的服务地址;缺省时使用内置静态桩"
|
|
23
|
+
},
|
|
24
|
+
"reportBaseUrl": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "批量上报的服务地址;缺省时只落本地、不上报"
|
|
27
|
+
},
|
|
28
|
+
"authToken": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"description": "拉取/上报接口的 Authorization 头值(如 'Bearer xxx')"
|
|
31
|
+
},
|
|
32
|
+
"recordUnattributed": {
|
|
33
|
+
"type": "boolean",
|
|
34
|
+
"description": "是否记录无法归属到具体功能点的 exec 调用(默认 true)"
|
|
35
|
+
},
|
|
36
|
+
"enableFileLog": {
|
|
37
|
+
"type": "boolean",
|
|
38
|
+
"description": "是否开启插件专属文件日志(落盘为 skill-logger.err),用于排查 WS 连接和指令运行情况"
|
|
39
|
+
},
|
|
40
|
+
"autoUpdateSkills": {
|
|
41
|
+
"type": "boolean",
|
|
42
|
+
"description": "是否开启 skill 版本自动更新(检测到本地落后于平台最新版本时下载并覆盖各 workspace 下的副本);默认开启,仅显式设为 false 才关闭"
|
|
43
|
+
},
|
|
44
|
+
"versionCheckIntervalMinutes": {
|
|
45
|
+
"type": "number",
|
|
46
|
+
"description": "版本检查+自动更新的周期(分钟)。每隔该周期按本地所有已装 skill 向服务端拉最新版本并按需更新;默认 30。本地扫描发现固定 3 分钟,与此独立"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|