rol-websocket-channel 1.8.8 → 1.8.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.
@@ -8,6 +8,8 @@ import { JsonRpcException, JSON_RPC_ERRORS } from '../jsonrpc.js';
8
8
  const execFileAsync = promisify(execFile);
9
9
  const REGISTRY_PACKAGE = '@rorolee-expert/registry';
10
10
  const OPTIONAL_SKILL_FILES = ['manifest.json', 'prompt.md'];
11
+ const OPTIONAL_SKILL_DIRS = ['scripts'];
12
+ const RESOURCE_SYNC_VERSION = 1;
11
13
  export const syncExperts = async (params, context) => {
12
14
  const objectParams = isObject(params) ? params : {};
13
15
  const filterId = pickString(objectParams.id) ?? pickString(objectParams.expertId);
@@ -35,6 +37,8 @@ export const syncExperts = async (params, context) => {
35
37
  if (!force &&
36
38
  previous?.version === expert.version &&
37
39
  previous.packageName === expert.packageName &&
40
+ previous.resourceSyncVersion === RESOURCE_SYNC_VERSION &&
41
+ await copiedDirsStillExist(targetDir, previous.copiedDirs) &&
38
42
  await pathExists(skillPath)) {
39
43
  results.push({
40
44
  id: expert.id,
@@ -60,17 +64,31 @@ export const syncExperts = async (params, context) => {
60
64
  await ensureDir(targetDir);
61
65
  await fs.copyFile(sourceSkillPath, skillPath);
62
66
  const copiedFiles = ['SKILL.md'];
67
+ const copiedDirs = [];
63
68
  for (const fileName of OPTIONAL_SKILL_FILES) {
64
69
  const copied = await copyIfExists(path.join(packageDir, fileName), path.join(targetDir, fileName));
65
70
  if (copied) {
66
71
  copiedFiles.push(fileName);
67
72
  }
68
73
  }
74
+ for (const dirName of OPTIONAL_SKILL_DIRS) {
75
+ const sourceDir = path.join(packageDir, dirName);
76
+ if (!(await pathExists(sourceDir))) {
77
+ continue;
78
+ }
79
+ const targetResourceDir = path.join(targetDir, dirName);
80
+ await fs.rm(targetResourceDir, { recursive: true, force: true });
81
+ await fs.cp(sourceDir, targetResourceDir, { recursive: true });
82
+ copiedFiles.push(`${dirName}/`);
83
+ copiedDirs.push(dirName);
84
+ }
69
85
  installedState[expert.id] = {
70
86
  packageName: expert.packageName,
71
87
  version: expert.version,
72
88
  skillPath,
73
- installedAt: new Date().toISOString()
89
+ installedAt: new Date().toISOString(),
90
+ resourceSyncVersion: RESOURCE_SYNC_VERSION,
91
+ copiedDirs
74
92
  };
75
93
  results.push({
76
94
  id: expert.id,
@@ -175,6 +193,12 @@ async function readInstalledState(installedStatePath) {
175
193
  const version = pickString(value.version);
176
194
  const skillPath = pickString(value.skillPath);
177
195
  const installedAt = pickString(value.installedAt);
196
+ const resourceSyncVersion = typeof value.resourceSyncVersion === 'number'
197
+ ? value.resourceSyncVersion
198
+ : undefined;
199
+ const copiedDirs = Array.isArray(value.copiedDirs)
200
+ ? value.copiedDirs.filter((item) => typeof item === 'string')
201
+ : undefined;
178
202
  if (!packageName || !version || !skillPath || !installedAt) {
179
203
  continue;
180
204
  }
@@ -182,11 +206,24 @@ async function readInstalledState(installedStatePath) {
182
206
  packageName,
183
207
  version,
184
208
  skillPath,
185
- installedAt
209
+ installedAt,
210
+ resourceSyncVersion,
211
+ copiedDirs
186
212
  };
187
213
  }
188
214
  return installed;
189
215
  }
216
+ async function copiedDirsStillExist(targetDir, copiedDirs) {
217
+ if (!copiedDirs?.length) {
218
+ return true;
219
+ }
220
+ for (const dirName of copiedDirs) {
221
+ if (!(await pathExists(path.join(targetDir, dirName)))) {
222
+ return false;
223
+ }
224
+ }
225
+ return true;
226
+ }
190
227
  async function resolveOpenClawDir(context) {
191
228
  const candidates = uniqueStrings([
192
229
  context.openclawRoot,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rol-websocket-channel",
3
- "version": "1.8.8",
3
+ "version": "1.8.9",
4
4
  "description": "Unified OpenClaw plugin: MQTT Channel + Admin Bridge for remote management",
5
5
  "license": "MIT",
6
6
  "author": "nixgnehc",
@@ -11,6 +11,8 @@ import type { JsonValue, MethodContext, MethodHandler } from '../types.js';
11
11
  const execFileAsync = promisify(execFile);
12
12
  const REGISTRY_PACKAGE = '@rorolee-expert/registry';
13
13
  const OPTIONAL_SKILL_FILES = ['manifest.json', 'prompt.md'] as const;
14
+ const OPTIONAL_SKILL_DIRS = ['scripts'] as const;
15
+ const RESOURCE_SYNC_VERSION = 1;
14
16
 
15
17
  interface ExpertRegistry {
16
18
  experts?: unknown;
@@ -27,6 +29,8 @@ interface InstalledExpert {
27
29
  version: string;
28
30
  skillPath: string;
29
31
  installedAt: string;
32
+ resourceSyncVersion?: number;
33
+ copiedDirs?: string[];
30
34
  }
31
35
 
32
36
  interface SyncExpertsParams {
@@ -78,6 +82,8 @@ export const syncExperts: MethodHandler = async (params, context): Promise<JsonV
78
82
  !force &&
79
83
  previous?.version === expert.version &&
80
84
  previous.packageName === expert.packageName &&
85
+ previous.resourceSyncVersion === RESOURCE_SYNC_VERSION &&
86
+ await copiedDirsStillExist(targetDir, previous.copiedDirs) &&
81
87
  await pathExists(skillPath)
82
88
  ) {
83
89
  results.push({
@@ -116,6 +122,7 @@ export const syncExperts: MethodHandler = async (params, context): Promise<JsonV
116
122
  await fs.copyFile(sourceSkillPath, skillPath);
117
123
 
118
124
  const copiedFiles = ['SKILL.md'];
125
+ const copiedDirs: string[] = [];
119
126
  for (const fileName of OPTIONAL_SKILL_FILES) {
120
127
  const copied = await copyIfExists(
121
128
  path.join(packageDir, fileName),
@@ -126,11 +133,26 @@ export const syncExperts: MethodHandler = async (params, context): Promise<JsonV
126
133
  }
127
134
  }
128
135
 
136
+ for (const dirName of OPTIONAL_SKILL_DIRS) {
137
+ const sourceDir = path.join(packageDir, dirName);
138
+ if (!(await pathExists(sourceDir))) {
139
+ continue;
140
+ }
141
+
142
+ const targetResourceDir = path.join(targetDir, dirName);
143
+ await fs.rm(targetResourceDir, { recursive: true, force: true });
144
+ await fs.cp(sourceDir, targetResourceDir, { recursive: true });
145
+ copiedFiles.push(`${dirName}/`);
146
+ copiedDirs.push(dirName);
147
+ }
148
+
129
149
  installedState[expert.id] = {
130
150
  packageName: expert.packageName,
131
151
  version: expert.version,
132
152
  skillPath,
133
- installedAt: new Date().toISOString()
153
+ installedAt: new Date().toISOString(),
154
+ resourceSyncVersion: RESOURCE_SYNC_VERSION,
155
+ copiedDirs
134
156
  };
135
157
 
136
158
  results.push({
@@ -274,6 +296,12 @@ async function readInstalledState(installedStatePath: string): Promise<Installed
274
296
  const version = pickString(value.version);
275
297
  const skillPath = pickString(value.skillPath);
276
298
  const installedAt = pickString(value.installedAt);
299
+ const resourceSyncVersion = typeof value.resourceSyncVersion === 'number'
300
+ ? value.resourceSyncVersion
301
+ : undefined;
302
+ const copiedDirs = Array.isArray(value.copiedDirs)
303
+ ? value.copiedDirs.filter((item): item is string => typeof item === 'string')
304
+ : undefined;
277
305
  if (!packageName || !version || !skillPath || !installedAt) {
278
306
  continue;
279
307
  }
@@ -282,13 +310,29 @@ async function readInstalledState(installedStatePath: string): Promise<Installed
282
310
  packageName,
283
311
  version,
284
312
  skillPath,
285
- installedAt
313
+ installedAt,
314
+ resourceSyncVersion,
315
+ copiedDirs
286
316
  };
287
317
  }
288
318
 
289
319
  return installed;
290
320
  }
291
321
 
322
+ async function copiedDirsStillExist(targetDir: string, copiedDirs: string[] | undefined): Promise<boolean> {
323
+ if (!copiedDirs?.length) {
324
+ return true;
325
+ }
326
+
327
+ for (const dirName of copiedDirs) {
328
+ if (!(await pathExists(path.join(targetDir, dirName)))) {
329
+ return false;
330
+ }
331
+ }
332
+
333
+ return true;
334
+ }
335
+
292
336
  async function resolveOpenClawDir(context: MethodContext): Promise<string> {
293
337
  const candidates = uniqueStrings([
294
338
  context.openclawRoot,