openclaw-vchat-plugin 0.0.7 → 0.0.9

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 (53) hide show
  1. package/dist/commands.d.ts +1 -0
  2. package/dist/commands.d.ts.map +1 -1
  3. package/dist/commands.js +37 -62
  4. package/dist/commands.js.map +1 -1
  5. package/dist/constants.d.ts +1 -1
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/constants.js +3 -1
  8. package/dist/constants.js.map +1 -1
  9. package/dist/group-event-store.d.ts +47 -0
  10. package/dist/group-event-store.d.ts.map +1 -0
  11. package/dist/group-event-store.js +145 -0
  12. package/dist/group-event-store.js.map +1 -0
  13. package/dist/index.js +74 -8
  14. package/dist/index.js.map +1 -1
  15. package/dist/message-handler.d.ts.map +1 -1
  16. package/dist/message-handler.js +11 -4
  17. package/dist/message-handler.js.map +1 -1
  18. package/dist/plugin-update-runner.d.ts +2 -0
  19. package/dist/plugin-update-runner.d.ts.map +1 -0
  20. package/dist/plugin-update-runner.js +179 -0
  21. package/dist/plugin-update-runner.js.map +1 -0
  22. package/dist/relay-server.d.ts +2 -0
  23. package/dist/relay-server.d.ts.map +1 -1
  24. package/dist/relay-server.js +268 -8
  25. package/dist/relay-server.js.bak-self-send-filter-20260310 +1028 -0
  26. package/dist/relay-server.js.map +1 -1
  27. package/dist/routes/config.routes.d.ts.map +1 -1
  28. package/dist/routes/config.routes.js +53 -0
  29. package/dist/routes/config.routes.js.map +1 -1
  30. package/dist/services/config.service.d.ts +8 -0
  31. package/dist/services/config.service.d.ts.map +1 -1
  32. package/dist/services/config.service.js +200 -2
  33. package/dist/services/config.service.js.map +1 -1
  34. package/dist/services/plugin-update.service.d.ts +24 -0
  35. package/dist/services/plugin-update.service.d.ts.map +1 -0
  36. package/dist/services/plugin-update.service.js +308 -0
  37. package/dist/services/plugin-update.service.js.map +1 -0
  38. package/dist/services/skills.service.d.ts +33 -0
  39. package/dist/services/skills.service.d.ts.map +1 -0
  40. package/dist/services/skills.service.js +177 -0
  41. package/dist/services/skills.service.js.map +1 -0
  42. package/package.json +1 -1
  43. package/src/commands.ts +38 -61
  44. package/src/constants.ts +4 -1
  45. package/src/group-event-store.ts +204 -0
  46. package/src/index.ts +72 -8
  47. package/src/message-handler.ts +19 -5
  48. package/src/plugin-update-runner.ts +217 -0
  49. package/src/relay-server.ts +292 -8
  50. package/src/routes/config.routes.ts +56 -0
  51. package/src/services/config.service.ts +223 -3
  52. package/src/services/plugin-update.service.ts +335 -0
  53. package/src/services/skills.service.ts +175 -0
@@ -0,0 +1,175 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+ import { GatewayClient } from '../gateway-client';
4
+
5
+ const OPENCLAW_HOME = process.env.OPENCLAW_HOME || path.join(process.env.HOME || '/root', '.openclaw');
6
+ const OPENCLAW_CONFIG = process.env.OPENCLAW_CONFIG || path.join(OPENCLAW_HOME, 'openclaw.json');
7
+
8
+ function cleanString(value: unknown): string {
9
+ return String(value || '').trim();
10
+ }
11
+
12
+ function toStringArray(value: unknown): string[] {
13
+ return Array.isArray(value)
14
+ ? value.map((item) => cleanString(item)).filter(Boolean)
15
+ : [];
16
+ }
17
+
18
+ function toInstallOptions(value: unknown): Array<{ id: string; kind: string; label: string; bins: string[] }> {
19
+ if (!Array.isArray(value)) return [];
20
+ return value
21
+ .map((item) => {
22
+ const id = cleanString((item as any)?.id);
23
+ if (!id) return null;
24
+ return {
25
+ id,
26
+ kind: cleanString((item as any)?.kind) || 'custom',
27
+ label: cleanString((item as any)?.label) || id,
28
+ bins: toStringArray((item as any)?.bins),
29
+ };
30
+ })
31
+ .filter((item): item is { id: string; kind: string; label: string; bins: string[] } => Boolean(item));
32
+ }
33
+
34
+ function normalizeSkillsStatus(raw: any) {
35
+ const managedSkillsDir = cleanString(raw?.managedSkillsDir);
36
+ return {
37
+ workspaceDir: cleanString(raw?.workspaceDir),
38
+ managedSkillsDir,
39
+ skills: (Array.isArray(raw?.skills) ? raw.skills : [])
40
+ .map((item: any) => ({
41
+ name: cleanString(item?.name),
42
+ description: cleanString(item?.description),
43
+ source: cleanString(item?.source),
44
+ bundled: Boolean(item?.bundled),
45
+ filePath: cleanString(item?.filePath),
46
+ baseDir: cleanString(item?.baseDir),
47
+ skillKey: cleanString(item?.skillKey) || cleanString(item?.name),
48
+ primaryEnv: cleanString(item?.primaryEnv),
49
+ emoji: cleanString(item?.emoji),
50
+ homepage: cleanString(item?.homepage),
51
+ always: Boolean(item?.always),
52
+ disabled: Boolean(item?.disabled),
53
+ blockedByAllowlist: Boolean(item?.blockedByAllowlist),
54
+ eligible: Boolean(item?.eligible),
55
+ requirements: {
56
+ bins: toStringArray(item?.requirements?.bins),
57
+ anyBins: toStringArray(item?.requirements?.anyBins),
58
+ env: toStringArray(item?.requirements?.env),
59
+ config: toStringArray(item?.requirements?.config),
60
+ os: toStringArray(item?.requirements?.os),
61
+ },
62
+ missing: {
63
+ bins: toStringArray(item?.missing?.bins),
64
+ anyBins: toStringArray(item?.missing?.anyBins),
65
+ env: toStringArray(item?.missing?.env),
66
+ config: toStringArray(item?.missing?.config),
67
+ os: toStringArray(item?.missing?.os),
68
+ },
69
+ configChecks: Array.isArray(item?.configChecks) ? item.configChecks : [],
70
+ install: toInstallOptions(item?.install),
71
+ }))
72
+ .filter((item: any) => item.name && item.skillKey)
73
+ .sort((a: any, b: any) => a.name.localeCompare(b.name, 'zh-Hans-CN')),
74
+ };
75
+ }
76
+
77
+ function writeConfig(config: any): void {
78
+ fs.mkdirSync(path.dirname(OPENCLAW_CONFIG), { recursive: true });
79
+ const tmpPath = `${OPENCLAW_CONFIG}.tmp`;
80
+ fs.writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
81
+ fs.renameSync(tmpPath, OPENCLAW_CONFIG);
82
+ }
83
+
84
+ function isManagedSkillTarget(managedSkillsDir: string, targetPath: string): boolean {
85
+ const root = path.resolve(managedSkillsDir);
86
+ const target = path.resolve(targetPath);
87
+ if (root === target) return false;
88
+ const relative = path.relative(root, target);
89
+ return Boolean(relative) && !relative.startsWith('..') && !path.isAbsolute(relative);
90
+ }
91
+
92
+ function resolveManagedSkillTarget(skill: any, managedSkillsDir: string): string {
93
+ if (!managedSkillsDir || skill?.bundled) return '';
94
+ const baseDir = cleanString(skill?.baseDir);
95
+ if (baseDir && isManagedSkillTarget(managedSkillsDir, baseDir)) {
96
+ return path.resolve(baseDir);
97
+ }
98
+ const filePath = cleanString(skill?.filePath);
99
+ if (filePath && isManagedSkillTarget(managedSkillsDir, filePath)) {
100
+ return path.dirname(path.resolve(filePath));
101
+ }
102
+ return '';
103
+ }
104
+
105
+ export async function getSkillsStatus(gateway: GatewayClient, agentId?: string) {
106
+ const safeAgentId = cleanString(agentId);
107
+ const result = await gateway.call('skills.status', safeAgentId ? { agentId: safeAgentId } : {});
108
+ return normalizeSkillsStatus(result);
109
+ }
110
+
111
+ export async function installSkill(gateway: GatewayClient, params: { name: string; installId?: string; timeoutMs?: number }) {
112
+ const name = cleanString(params?.name);
113
+ if (!name) throw new Error('缺少 skill 名称');
114
+
115
+ const payload: Record<string, unknown> = { name };
116
+ const installId = cleanString(params?.installId);
117
+ if (installId) payload.installId = installId;
118
+
119
+ const timeoutMs = Number(params?.timeoutMs);
120
+ if (Number.isFinite(timeoutMs) && timeoutMs > 0) {
121
+ payload.timeoutMs = Math.max(1_000, Math.min(900_000, Math.floor(timeoutMs)));
122
+ }
123
+
124
+ const result = await gateway.call('skills.install', payload);
125
+ return {
126
+ ok: result?.ok === false ? false : true,
127
+ message: cleanString(result?.message) || '安装完成',
128
+ stdout: cleanString(result?.stdout),
129
+ stderr: cleanString(result?.stderr),
130
+ warnings: Array.isArray(result?.warnings) ? result.warnings : [],
131
+ };
132
+ }
133
+
134
+ export async function updateSkill(gateway: GatewayClient, skillKey: string, patch: { enabled?: boolean; apiKey?: string; env?: Record<string, string> }) {
135
+ const safeSkillKey = cleanString(skillKey);
136
+ if (!safeSkillKey) throw new Error('缺少 skillKey');
137
+
138
+ const payload: Record<string, unknown> = { skillKey: safeSkillKey };
139
+ if (typeof patch?.enabled === 'boolean') payload.enabled = patch.enabled;
140
+ if (typeof patch?.apiKey === 'string') payload.apiKey = patch.apiKey;
141
+ if (patch?.env && typeof patch.env === 'object') {
142
+ const envEntries = Object.entries(patch.env)
143
+ .map(([key, value]) => [cleanString(key), cleanString(value)] as const)
144
+ .filter(([key]) => Boolean(key));
145
+ payload.env = Object.fromEntries(envEntries);
146
+ }
147
+
148
+ const result = await gateway.call('skills.update', payload);
149
+ return {
150
+ ok: result?.ok === false ? false : true,
151
+ skillKey: cleanString(result?.skillKey) || safeSkillKey,
152
+ enabled: typeof result?.enabled === 'boolean' ? result.enabled : patch?.enabled,
153
+ };
154
+ }
155
+
156
+ export function setSkillEnabledLocally(gateway: GatewayClient, skillKey: string, enabled: boolean) {
157
+ const safeSkillKey = cleanString(skillKey);
158
+ if (!safeSkillKey) throw new Error('缺少 skillKey');
159
+ const config = gateway.readConfig() || {};
160
+ const skills = config.skills && typeof config.skills === 'object' ? { ...config.skills } : {};
161
+ const entries = skills.entries && typeof skills.entries === 'object' ? { ...skills.entries } : {};
162
+ const current = entries[safeSkillKey] && typeof entries[safeSkillKey] === 'object'
163
+ ? { ...entries[safeSkillKey] }
164
+ : {};
165
+ current.enabled = Boolean(enabled);
166
+ entries[safeSkillKey] = current;
167
+ skills.entries = entries;
168
+ config.skills = skills;
169
+ writeConfig(config);
170
+ return { ok: true, skillKey: safeSkillKey, enabled: Boolean(enabled) };
171
+ }
172
+
173
+ export function isManagedSkillInstallable(skill: any, managedSkillsDir: string): boolean {
174
+ return Boolean(resolveManagedSkillTarget(skill, managedSkillsDir));
175
+ }