@spzhongwin/skill-logger-plugin 1.0.15 → 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 (56) hide show
  1. package/dist/index.js +193 -3109
  2. package/openclaw.plugin.json +50 -50
  3. package/package.json +34 -34
  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/hooks.test.ts +228 -228
  11. package/src/hooks.ts +494 -494
  12. package/src/http.ts +61 -61
  13. package/src/identity.ts +88 -88
  14. package/src/index.test.ts +53 -53
  15. package/src/index.ts +218 -197
  16. package/src/integration.test.ts +119 -119
  17. package/src/matcher.test.ts +170 -170
  18. package/src/matcher.ts +393 -393
  19. package/src/paths.test.ts +57 -57
  20. package/src/paths.ts +84 -84
  21. package/src/reporter.test.ts +139 -139
  22. package/src/reporter.ts +303 -303
  23. package/src/sample-config.json +72 -72
  24. package/src/semver.test.ts +33 -23
  25. package/src/semver.ts +65 -60
  26. package/src/skill-version.ts +53 -53
  27. package/src/types.ts +202 -202
  28. package/src/updater.test.ts +431 -314
  29. package/src/updater.ts +584 -532
  30. package/src/ws-client.test.ts +158 -128
  31. package/src/ws-client.ts +805 -773
  32. package/test-ws.ts +17 -17
  33. package/tsconfig.json +18 -18
  34. package/dist/active-skills.js +0 -67
  35. package/dist/active-skills.test.js +0 -29
  36. package/dist/config-sync.js +0 -439
  37. package/dist/config-sync.test.js +0 -145
  38. package/dist/hooks.js +0 -337
  39. package/dist/hooks.test.js +0 -123
  40. package/dist/http.js +0 -54
  41. package/dist/identity.js +0 -56
  42. package/dist/index.test.js +0 -39
  43. package/dist/integration.test.js +0 -102
  44. package/dist/matcher.js +0 -362
  45. package/dist/matcher.test.js +0 -139
  46. package/dist/paths.js +0 -62
  47. package/dist/paths.test.js +0 -49
  48. package/dist/reporter.js +0 -267
  49. package/dist/reporter.test.js +0 -128
  50. package/dist/semver.js +0 -64
  51. package/dist/semver.test.js +0 -21
  52. package/dist/skill-version.js +0 -23
  53. package/dist/types.js +0 -9
  54. package/dist/updater.js +0 -352
  55. package/dist/updater.test.js +0 -212
  56. 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
+ });