@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.
Files changed (57) hide show
  1. package/dist/index.js +80 -285
  2. package/openclaw.plugin.json +50 -50
  3. package/package.json +34 -37
  4. package/src/active-skills.test.ts +32 -32
  5. package/src/active-skills.ts +77 -77
  6. package/src/config-sync.test.ts +165 -165
  7. package/src/config-sync.ts +544 -544
  8. package/src/expert-skill-layout.test.ts +196 -0
  9. package/src/expert-skill-layout.ts +233 -0
  10. package/src/global.d.ts +4 -0
  11. package/src/hooks.test.ts +228 -251
  12. package/src/hooks.ts +494 -517
  13. package/src/http.ts +61 -61
  14. package/src/identity.ts +88 -64
  15. package/src/index.test.ts +53 -53
  16. package/src/index.ts +218 -226
  17. package/src/integration.test.ts +119 -119
  18. package/src/matcher.test.ts +170 -170
  19. package/src/matcher.ts +393 -393
  20. package/src/paths.test.ts +57 -57
  21. package/src/paths.ts +84 -84
  22. package/src/reporter.test.ts +139 -139
  23. package/src/reporter.ts +303 -298
  24. package/src/sample-config.json +72 -72
  25. package/src/semver.test.ts +33 -23
  26. package/src/semver.ts +65 -60
  27. package/src/skill-version.ts +53 -53
  28. package/src/types.ts +202 -198
  29. package/src/updater.test.ts +431 -325
  30. package/src/updater.ts +584 -549
  31. package/src/ws-client.test.ts +158 -128
  32. package/src/ws-client.ts +805 -769
  33. package/test-ws.ts +17 -17
  34. package/tsconfig.json +18 -14
  35. package/dist/active-skills.js +0 -67
  36. package/dist/active-skills.test.js +0 -29
  37. package/dist/config-sync.js +0 -439
  38. package/dist/config-sync.test.js +0 -145
  39. package/dist/hooks.js +0 -337
  40. package/dist/hooks.test.js +0 -123
  41. package/dist/http.js +0 -54
  42. package/dist/identity.js +0 -56
  43. package/dist/index.test.js +0 -39
  44. package/dist/integration.test.js +0 -102
  45. package/dist/matcher.js +0 -362
  46. package/dist/matcher.test.js +0 -139
  47. package/dist/paths.js +0 -62
  48. package/dist/paths.test.js +0 -49
  49. package/dist/reporter.js +0 -267
  50. package/dist/reporter.test.js +0 -128
  51. package/dist/semver.js +0 -64
  52. package/dist/semver.test.js +0 -21
  53. package/dist/skill-version.js +0 -23
  54. package/dist/types.js +0 -9
  55. package/dist/updater.js +0 -352
  56. package/dist/updater.test.js +0 -212
  57. package/dist/ws-client.js +0 -484
@@ -1,128 +1,158 @@
1
- import { describe, it } from "node:test";
2
- import assert from "node:assert/strict";
3
- import fs from "node:fs";
4
- import os from "node:os";
5
- import path from "node:path";
6
- import { DatabaseSync } from "node:sqlite";
7
- import {
8
- normalizeAssistantUserId,
9
- parseAssistantWorkspaceAgentId,
10
- readCronJobsByAgentId,
11
- shouldSyncBuiltInTemplate,
12
- } from "./ws-client.ts";
13
-
14
- describe("assistant workspace naming", () => {
15
- it("accepts workspace-assistant-* only when suffix is at least 5 digits", () => {
16
- assert.equal(parseAssistantWorkspaceAgentId("workspace-assistant-12345"), "assistant-12345");
17
- assert.equal(
18
- parseAssistantWorkspaceAgentId("workspace-assistant-12342325232333223223"),
19
- "assistant-12342325232333223223"
20
- );
21
- assert.equal(parseAssistantWorkspaceAgentId("workspace-assistant-000001"), "assistant-000001");
22
- });
23
-
24
- it("rejects short, non-numeric, and malformed workspace names", () => {
25
- assert.equal(parseAssistantWorkspaceAgentId("workspace-assistant-1234"), undefined);
26
- assert.equal(parseAssistantWorkspaceAgentId("workspace-assistant-1234a"), undefined);
27
- assert.equal(parseAssistantWorkspaceAgentId("workspace-assistant-abcde"), undefined);
28
- assert.equal(parseAssistantWorkspaceAgentId("workspace-coder-12345"), undefined);
29
- });
30
-
31
- it("normalizes command userId using the same numeric suffix rule", () => {
32
- assert.equal(normalizeAssistantUserId("12345"), "12345");
33
- assert.equal(normalizeAssistantUserId("assistant-12345"), "12345");
34
- assert.equal(normalizeAssistantUserId("assistant-000001"), "000001");
35
- });
36
-
37
- it("rejects unsafe or non-conforming command userId values", () => {
38
- assert.equal(normalizeAssistantUserId("1234"), undefined);
39
- assert.equal(normalizeAssistantUserId("assistant-1234"), undefined);
40
- assert.equal(normalizeAssistantUserId("assistant-1234a"), undefined);
41
- assert.equal(normalizeAssistantUserId("../assistant-12345"), undefined);
42
- });
43
- });
44
-
45
- describe("built-in template update boundary", () => {
46
- it("only syncs the template for an explicit built-in UPDATE_SKILL command", () => {
47
- assert.equal(shouldSyncBuiltInTemplate("UPDATE_SKILL", true), true);
48
- assert.equal(shouldSyncBuiltInTemplate("UPDATE_SKILL", false), false);
49
- assert.equal(shouldSyncBuiltInTemplate("UPDATE_SKILL", undefined), false);
50
- assert.equal(shouldSyncBuiltInTemplate("INSTALL_SKILL", true), false);
51
- assert.equal(shouldSyncBuiltInTemplate("UNINSTALL_SKILL", true), false);
52
- });
53
- });
54
-
55
- describe("cron job sqlite lookup", () => {
56
- it("returns all cron_jobs rows for the requested agent_id", () => {
57
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-cron-jobs-"));
58
- const dbPath = path.join(dir, "openclaw.sqlite");
59
- const db = new DatabaseSync(dbPath);
60
- try {
61
- db.exec(`
62
- CREATE TABLE cron_jobs (
63
- id INTEGER PRIMARY KEY,
64
- agent_id TEXT NOT NULL,
65
- name TEXT NOT NULL,
66
- cron TEXT NOT NULL
67
- );
68
- `);
69
- db.prepare("INSERT INTO cron_jobs (agent_id, name, cron) VALUES (?, ?, ?)").run(
70
- "assistant-1579723293989240833",
71
- "morning",
72
- "0 9 * * *"
73
- );
74
- db.prepare("INSERT INTO cron_jobs (agent_id, name, cron) VALUES (?, ?, ?)").run(
75
- "assistant-1579723293989240833",
76
- "evening",
77
- "0 18 * * *"
78
- );
79
- db.prepare("INSERT INTO cron_jobs (agent_id, name, cron) VALUES (?, ?, ?)").run(
80
- "assistant-000001",
81
- "other",
82
- "0 12 * * *"
83
- );
84
- } finally {
85
- db.close();
86
- }
87
-
88
- const rows = readCronJobsByAgentId("assistant-1579723293989240833", dbPath);
89
-
90
- assert.equal(rows.length, 2);
91
- assert.deepEqual(
92
- rows.map((row) => row.name),
93
- ["morning", "evening"]
94
- );
95
- });
96
-
97
- it("returns an empty list when no cron_jobs row matches", () => {
98
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-cron-jobs-"));
99
- const dbPath = path.join(dir, "openclaw.sqlite");
100
- const db = new DatabaseSync(dbPath);
101
- try {
102
- db.exec("CREATE TABLE cron_jobs (id INTEGER PRIMARY KEY, agent_id TEXT NOT NULL);");
103
- } finally {
104
- db.close();
105
- }
106
-
107
- assert.deepEqual(readCronJobsByAgentId("assistant-1579723293989240833", dbPath), []);
108
- });
109
-
110
- it("returns an empty list when the sqlite file is not readable", () => {
111
- const missingPath = path.join(os.tmpdir(), `missing-openclaw-${Date.now()}.sqlite`);
112
-
113
- assert.deepEqual(readCronJobsByAgentId("assistant-1579723293989240833", missingPath), []);
114
- });
115
-
116
- it("returns an empty list when cron_jobs table does not exist", () => {
117
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-cron-jobs-"));
118
- const dbPath = path.join(dir, "openclaw.sqlite");
119
- const db = new DatabaseSync(dbPath);
120
- try {
121
- db.exec("CREATE TABLE other_table (id INTEGER PRIMARY KEY, agent_id TEXT NOT NULL);");
122
- } finally {
123
- db.close();
124
- }
125
-
126
- assert.deepEqual(readCronJobsByAgentId("assistant-1579723293989240833", dbPath), []);
127
- });
128
- });
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { DatabaseSync } from "node:sqlite";
7
+ import {
8
+ normalizeAssistantUserId,
9
+ normalizeCommandCode,
10
+ parseAssistantWorkspaceAgentId,
11
+ readCronJobsByAgentId,
12
+ findInstalledExpertSkillsRoot,
13
+ shouldSyncBuiltInTemplate,
14
+ } from "./ws-client.ts";
15
+
16
+ describe("command code boundary", () => {
17
+ it("只接受单一路径段,不静默改写路径穿越输入", () => {
18
+ assert.equal(normalizeCommandCode("demo-skill"), "demo-skill");
19
+ assert.equal(normalizeCommandCode("../demo"), undefined);
20
+ assert.equal(normalizeCommandCode("a/b"), undefined);
21
+ assert.equal(normalizeCommandCode("a\\b"), undefined);
22
+ assert.equal(normalizeCommandCode("."), undefined);
23
+ assert.equal(normalizeCommandCode(".."), undefined);
24
+ assert.equal(normalizeCommandCode(""), undefined);
25
+ assert.equal(normalizeCommandCode(123), undefined);
26
+ });
27
+ });
28
+
29
+ describe("assistant workspace naming", () => {
30
+ it("accepts workspace-assistant-* only when suffix is at least 5 digits", () => {
31
+ assert.equal(parseAssistantWorkspaceAgentId("workspace-assistant-12345"), "assistant-12345");
32
+ assert.equal(
33
+ parseAssistantWorkspaceAgentId("workspace-assistant-12342325232333223223"),
34
+ "assistant-12342325232333223223"
35
+ );
36
+ assert.equal(parseAssistantWorkspaceAgentId("workspace-assistant-000001"), "assistant-000001");
37
+ });
38
+
39
+ it("rejects short, non-numeric, and malformed workspace names", () => {
40
+ assert.equal(parseAssistantWorkspaceAgentId("workspace-assistant-1234"), undefined);
41
+ assert.equal(parseAssistantWorkspaceAgentId("workspace-assistant-1234a"), undefined);
42
+ assert.equal(parseAssistantWorkspaceAgentId("workspace-assistant-abcde"), undefined);
43
+ assert.equal(parseAssistantWorkspaceAgentId("workspace-coder-12345"), undefined);
44
+ });
45
+
46
+ it("normalizes command userId using the same numeric suffix rule", () => {
47
+ assert.equal(normalizeAssistantUserId("12345"), "12345");
48
+ assert.equal(normalizeAssistantUserId("assistant-12345"), "12345");
49
+ assert.equal(normalizeAssistantUserId("assistant-000001"), "000001");
50
+ });
51
+
52
+ it("rejects unsafe or non-conforming command userId values", () => {
53
+ assert.equal(normalizeAssistantUserId("1234"), undefined);
54
+ assert.equal(normalizeAssistantUserId("assistant-1234"), undefined);
55
+ assert.equal(normalizeAssistantUserId("assistant-1234a"), undefined);
56
+ assert.equal(normalizeAssistantUserId("../assistant-12345"), undefined);
57
+ });
58
+ });
59
+
60
+ describe("built-in template update boundary", () => {
61
+ it("only syncs the template for an explicit built-in UPDATE_SKILL command", () => {
62
+ assert.equal(shouldSyncBuiltInTemplate("UPDATE_SKILL", true), true);
63
+ assert.equal(shouldSyncBuiltInTemplate("UPDATE_SKILL", false), false);
64
+ assert.equal(shouldSyncBuiltInTemplate("UPDATE_SKILL", undefined), false);
65
+ assert.equal(shouldSyncBuiltInTemplate("INSTALL_SKILL", true), false);
66
+ assert.equal(shouldSyncBuiltInTemplate("UNINSTALL_SKILL", true), false);
67
+ });
68
+ });
69
+
70
+ describe("expert skill update target", () => {
71
+ it("返回 .user/skills 父目录,由安装器统一追加 skill code", async () => {
72
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-expert-skill-"));
73
+ try {
74
+ const skillsRoot = path.join(root, "workspace-assistant-12345", ".user", "skills");
75
+ fs.mkdirSync(path.join(skillsRoot, "demo"), { recursive: true });
76
+
77
+ assert.equal(await findInstalledExpertSkillsRoot(root, "12345", "demo"), skillsRoot);
78
+ assert.equal(await findInstalledExpertSkillsRoot(root, "12345", "missing"), undefined);
79
+ } finally {
80
+ fs.rmSync(root, { recursive: true, force: true });
81
+ }
82
+ });
83
+ });
84
+
85
+ describe("cron job sqlite lookup", () => {
86
+ it("returns all cron_jobs rows for the requested agent_id", () => {
87
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-cron-jobs-"));
88
+ const dbPath = path.join(dir, "openclaw.sqlite");
89
+ const db = new DatabaseSync(dbPath);
90
+ try {
91
+ db.exec(`
92
+ CREATE TABLE cron_jobs (
93
+ id INTEGER PRIMARY KEY,
94
+ agent_id TEXT NOT NULL,
95
+ name TEXT NOT NULL,
96
+ cron TEXT NOT NULL
97
+ );
98
+ `);
99
+ db.prepare("INSERT INTO cron_jobs (agent_id, name, cron) VALUES (?, ?, ?)").run(
100
+ "assistant-1579723293989240833",
101
+ "morning",
102
+ "0 9 * * *"
103
+ );
104
+ db.prepare("INSERT INTO cron_jobs (agent_id, name, cron) VALUES (?, ?, ?)").run(
105
+ "assistant-1579723293989240833",
106
+ "evening",
107
+ "0 18 * * *"
108
+ );
109
+ db.prepare("INSERT INTO cron_jobs (agent_id, name, cron) VALUES (?, ?, ?)").run(
110
+ "assistant-000001",
111
+ "other",
112
+ "0 12 * * *"
113
+ );
114
+ } finally {
115
+ db.close();
116
+ }
117
+
118
+ const rows = readCronJobsByAgentId("assistant-1579723293989240833", dbPath);
119
+
120
+ assert.equal(rows.length, 2);
121
+ assert.deepEqual(
122
+ rows.map((row) => row.name),
123
+ ["morning", "evening"]
124
+ );
125
+ });
126
+
127
+ it("returns an empty list when no cron_jobs row matches", () => {
128
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-cron-jobs-"));
129
+ const dbPath = path.join(dir, "openclaw.sqlite");
130
+ const db = new DatabaseSync(dbPath);
131
+ try {
132
+ db.exec("CREATE TABLE cron_jobs (id INTEGER PRIMARY KEY, agent_id TEXT NOT NULL);");
133
+ } finally {
134
+ db.close();
135
+ }
136
+
137
+ assert.deepEqual(readCronJobsByAgentId("assistant-1579723293989240833", dbPath), []);
138
+ });
139
+
140
+ it("returns an empty list when the sqlite file is not readable", () => {
141
+ const missingPath = path.join(os.tmpdir(), `missing-openclaw-${Date.now()}.sqlite`);
142
+
143
+ assert.deepEqual(readCronJobsByAgentId("assistant-1579723293989240833", missingPath), []);
144
+ });
145
+
146
+ it("returns an empty list when cron_jobs table does not exist", () => {
147
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-cron-jobs-"));
148
+ const dbPath = path.join(dir, "openclaw.sqlite");
149
+ const db = new DatabaseSync(dbPath);
150
+ try {
151
+ db.exec("CREATE TABLE other_table (id INTEGER PRIMARY KEY, agent_id TEXT NOT NULL);");
152
+ } finally {
153
+ db.close();
154
+ }
155
+
156
+ assert.deepEqual(readCronJobsByAgentId("assistant-1579723293989240833", dbPath), []);
157
+ });
158
+ });