rol-websocket-channel 1.6.7 → 1.6.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.
|
@@ -15,13 +15,26 @@ const MEM9_PACKAGE_ROOTS = [
|
|
|
15
15
|
path.join('npm', 'node_modules', '@mem9', 'mem9'),
|
|
16
16
|
path.join('npm', 'node_modules', 'mem9')
|
|
17
17
|
];
|
|
18
|
-
const
|
|
18
|
+
const RUNTIME_FILE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs']);
|
|
19
|
+
const FALLBACK_ENTRYPOINTS = [
|
|
19
20
|
path.join('dist', 'index.js'),
|
|
20
21
|
path.join('dist', 'index.mjs'),
|
|
21
22
|
path.join('dist', 'index.cjs'),
|
|
22
23
|
'index.js',
|
|
23
24
|
'index.mjs',
|
|
24
|
-
'index.cjs'
|
|
25
|
+
'index.cjs',
|
|
26
|
+
path.join('lib', 'index.js'),
|
|
27
|
+
path.join('lib', 'index.mjs'),
|
|
28
|
+
path.join('lib', 'index.cjs'),
|
|
29
|
+
path.join('build', 'index.js'),
|
|
30
|
+
path.join('build', 'index.mjs'),
|
|
31
|
+
path.join('build', 'index.cjs'),
|
|
32
|
+
path.join('out', 'index.js'),
|
|
33
|
+
path.join('out', 'index.mjs'),
|
|
34
|
+
path.join('out', 'index.cjs'),
|
|
35
|
+
path.join('dist', 'main.js'),
|
|
36
|
+
path.join('dist', 'main.mjs'),
|
|
37
|
+
path.join('dist', 'main.cjs')
|
|
25
38
|
];
|
|
26
39
|
// ---------------------------------------------------------------------------
|
|
27
40
|
// Public API: installMem9 (idempotent, phase-based)
|
|
@@ -175,7 +188,8 @@ async function installMem9Plugin(cwd) {
|
|
|
175
188
|
}
|
|
176
189
|
export async function findMem9RuntimeEntrypoint(openclawRoot, config) {
|
|
177
190
|
for (const packageRoot of resolveMem9RuntimePackageRoots(openclawRoot, config)) {
|
|
178
|
-
|
|
191
|
+
const manifest = await readPluginManifest(packageRoot);
|
|
192
|
+
for (const entrypoint of collectEntrypointCandidates(packageRoot, manifest)) {
|
|
179
193
|
if (await pathExists(entrypoint)) {
|
|
180
194
|
return entrypoint;
|
|
181
195
|
}
|
|
@@ -190,14 +204,95 @@ async function ensureMem9RuntimeEntrypoint(openclawRoot, config) {
|
|
|
190
204
|
}
|
|
191
205
|
const installRecord = readMem9InstallRecord(config);
|
|
192
206
|
const checkedPackageRoots = resolveMem9RuntimePackageRoots(openclawRoot, config);
|
|
207
|
+
const checkedEntrypoints = [];
|
|
208
|
+
for (const packageRoot of checkedPackageRoots) {
|
|
209
|
+
const manifest = await readPluginManifest(packageRoot);
|
|
210
|
+
for (const candidate of collectEntrypointCandidates(packageRoot, manifest)) {
|
|
211
|
+
if (!checkedEntrypoints.includes(candidate)) {
|
|
212
|
+
checkedEntrypoints.push(candidate);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
193
216
|
throw new JsonRpcException(JSON_RPC_ERRORS.internalError, 'mem9 plugin is installed but missing compiled runtime output', {
|
|
194
217
|
code: 'MEM9_RUNTIME_OUTPUT_MISSING',
|
|
195
|
-
expected:
|
|
218
|
+
expected: FALLBACK_ENTRYPOINTS.map((item) => `./${item.replace(/\\/g, '/')}`),
|
|
196
219
|
checkedPackageRoots,
|
|
197
|
-
checkedEntrypoints
|
|
220
|
+
checkedEntrypoints,
|
|
198
221
|
installRecord
|
|
199
222
|
});
|
|
200
223
|
}
|
|
224
|
+
async function readPluginManifest(packageRoot) {
|
|
225
|
+
const manifestPath = path.join(packageRoot, 'package.json');
|
|
226
|
+
if (!(await pathExists(manifestPath)))
|
|
227
|
+
return null;
|
|
228
|
+
try {
|
|
229
|
+
return await readJsonFile(manifestPath);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function isRuntimeFile(relPath) {
|
|
236
|
+
return RUNTIME_FILE_EXTENSIONS.has(path.extname(relPath).toLowerCase());
|
|
237
|
+
}
|
|
238
|
+
function normalizeRelative(value) {
|
|
239
|
+
return value.replace(/^\.[\\/]+/, '').split(/[\\/]+/).join(path.sep);
|
|
240
|
+
}
|
|
241
|
+
function collectExportsEntries(value) {
|
|
242
|
+
const out = [];
|
|
243
|
+
const visit = (node, contextKey) => {
|
|
244
|
+
if (typeof node === 'string') {
|
|
245
|
+
out.push(node);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (Array.isArray(node)) {
|
|
249
|
+
for (const child of node)
|
|
250
|
+
visit(child, contextKey);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (node && typeof node === 'object') {
|
|
254
|
+
for (const [key, child] of Object.entries(node)) {
|
|
255
|
+
if (key.startsWith('.') && key !== '.')
|
|
256
|
+
continue;
|
|
257
|
+
visit(child, key);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
visit(value, null);
|
|
262
|
+
return out;
|
|
263
|
+
}
|
|
264
|
+
function collectEntrypointCandidates(packageRoot, manifest) {
|
|
265
|
+
const seen = new Set();
|
|
266
|
+
const out = [];
|
|
267
|
+
const push = (relCandidate) => {
|
|
268
|
+
if (typeof relCandidate !== 'string')
|
|
269
|
+
return;
|
|
270
|
+
const normalized = normalizeRelative(relCandidate);
|
|
271
|
+
if (!normalized)
|
|
272
|
+
return;
|
|
273
|
+
if (!isRuntimeFile(normalized))
|
|
274
|
+
return;
|
|
275
|
+
const absolute = path.isAbsolute(normalized) ? normalized : path.join(packageRoot, normalized);
|
|
276
|
+
if (seen.has(absolute))
|
|
277
|
+
return;
|
|
278
|
+
seen.add(absolute);
|
|
279
|
+
out.push(absolute);
|
|
280
|
+
};
|
|
281
|
+
const declaredRuntime = manifest?.openclaw?.runtimeExtensions;
|
|
282
|
+
if (Array.isArray(declaredRuntime)) {
|
|
283
|
+
for (const item of declaredRuntime)
|
|
284
|
+
push(item);
|
|
285
|
+
}
|
|
286
|
+
if (manifest?.exports !== undefined) {
|
|
287
|
+
for (const item of collectExportsEntries(manifest.exports))
|
|
288
|
+
push(item);
|
|
289
|
+
}
|
|
290
|
+
push(manifest?.module);
|
|
291
|
+
push(manifest?.main);
|
|
292
|
+
for (const item of FALLBACK_ENTRYPOINTS)
|
|
293
|
+
push(item);
|
|
294
|
+
return out;
|
|
295
|
+
}
|
|
201
296
|
function resolveMem9RuntimePackageRoots(openclawRoot, config) {
|
|
202
297
|
const roots = [];
|
|
203
298
|
const installPath = pickString(readMem9InstallRecord(config)?.installPath);
|
|
@@ -31,4 +31,70 @@ describe('mem9 runtime compatibility', () => {
|
|
|
31
31
|
await fs.rm(root, { recursive: true, force: true });
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
|
+
test('honors package.json main when output lives in a non-standard directory', async () => {
|
|
35
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'mem9-runtime-'));
|
|
36
|
+
try {
|
|
37
|
+
const packageRoot = path.join(root, '.openclaw', 'npm', 'node_modules', '@mem9', 'mem9');
|
|
38
|
+
await fs.mkdir(path.join(packageRoot, 'compiled'), { recursive: true });
|
|
39
|
+
await fs.writeFile(path.join(packageRoot, 'compiled', 'plugin.js'), 'export default {};\n', 'utf8');
|
|
40
|
+
await fs.writeFile(path.join(packageRoot, 'package.json'), JSON.stringify({ name: '@mem9/mem9', main: './compiled/plugin.js' }), 'utf8');
|
|
41
|
+
const entrypoint = await findMem9RuntimeEntrypoint(path.join(root, '.openclaw'));
|
|
42
|
+
assert.equal(entrypoint, path.join(packageRoot, 'compiled', 'plugin.js'));
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
test('resolves runtime from package.json exports conditional map', async () => {
|
|
49
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'mem9-runtime-'));
|
|
50
|
+
try {
|
|
51
|
+
const packageRoot = path.join(root, '.openclaw', 'npm', 'node_modules', 'mem9');
|
|
52
|
+
await fs.mkdir(path.join(packageRoot, 'dist', 'esm'), { recursive: true });
|
|
53
|
+
await fs.writeFile(path.join(packageRoot, 'dist', 'esm', 'index.js'), 'export default {};\n', 'utf8');
|
|
54
|
+
await fs.writeFile(path.join(packageRoot, 'package.json'), JSON.stringify({
|
|
55
|
+
name: 'mem9',
|
|
56
|
+
exports: {
|
|
57
|
+
'.': {
|
|
58
|
+
import: './dist/esm/index.js',
|
|
59
|
+
require: './dist/cjs/index.cjs'
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}), 'utf8');
|
|
63
|
+
const entrypoint = await findMem9RuntimeEntrypoint(path.join(root, '.openclaw'));
|
|
64
|
+
assert.equal(entrypoint, path.join(packageRoot, 'dist', 'esm', 'index.js'));
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
test('respects openclaw.runtimeExtensions declared by the plugin manifest', async () => {
|
|
71
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'mem9-runtime-'));
|
|
72
|
+
try {
|
|
73
|
+
const packageRoot = path.join(root, '.openclaw', 'npm', 'node_modules', '@mem9', 'mem9');
|
|
74
|
+
await fs.mkdir(path.join(packageRoot, 'build'), { recursive: true });
|
|
75
|
+
await fs.writeFile(path.join(packageRoot, 'build', 'runtime.mjs'), 'export default {};\n', 'utf8');
|
|
76
|
+
await fs.writeFile(path.join(packageRoot, 'package.json'), JSON.stringify({
|
|
77
|
+
name: '@mem9/mem9',
|
|
78
|
+
openclaw: { runtimeExtensions: ['./build/runtime.mjs'] }
|
|
79
|
+
}), 'utf8');
|
|
80
|
+
const entrypoint = await findMem9RuntimeEntrypoint(path.join(root, '.openclaw'));
|
|
81
|
+
assert.equal(entrypoint, path.join(packageRoot, 'build', 'runtime.mjs'));
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
test('falls back to lib/index.js when neither manifest nor dist exists', async () => {
|
|
88
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'mem9-runtime-'));
|
|
89
|
+
try {
|
|
90
|
+
const packageRoot = path.join(root, '.openclaw', 'npm', 'node_modules', 'mem9');
|
|
91
|
+
await fs.mkdir(path.join(packageRoot, 'lib'), { recursive: true });
|
|
92
|
+
await fs.writeFile(path.join(packageRoot, 'lib', 'index.js'), 'module.exports = {};\n', 'utf8');
|
|
93
|
+
const entrypoint = await findMem9RuntimeEntrypoint(path.join(root, '.openclaw'));
|
|
94
|
+
assert.equal(entrypoint, path.join(packageRoot, 'lib', 'index.js'));
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
98
|
+
}
|
|
99
|
+
});
|
|
34
100
|
});
|
package/package.json
CHANGED
|
@@ -36,4 +36,90 @@ describe('mem9 runtime compatibility', () => {
|
|
|
36
36
|
await fs.rm(root, { recursive: true, force: true });
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
|
+
|
|
40
|
+
test('honors package.json main when output lives in a non-standard directory', async () => {
|
|
41
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'mem9-runtime-'));
|
|
42
|
+
try {
|
|
43
|
+
const packageRoot = path.join(root, '.openclaw', 'npm', 'node_modules', '@mem9', 'mem9');
|
|
44
|
+
await fs.mkdir(path.join(packageRoot, 'compiled'), { recursive: true });
|
|
45
|
+
await fs.writeFile(path.join(packageRoot, 'compiled', 'plugin.js'), 'export default {};\n', 'utf8');
|
|
46
|
+
await fs.writeFile(
|
|
47
|
+
path.join(packageRoot, 'package.json'),
|
|
48
|
+
JSON.stringify({ name: '@mem9/mem9', main: './compiled/plugin.js' }),
|
|
49
|
+
'utf8'
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const entrypoint = await findMem9RuntimeEntrypoint(path.join(root, '.openclaw'));
|
|
53
|
+
|
|
54
|
+
assert.equal(entrypoint, path.join(packageRoot, 'compiled', 'plugin.js'));
|
|
55
|
+
} finally {
|
|
56
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('resolves runtime from package.json exports conditional map', async () => {
|
|
61
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'mem9-runtime-'));
|
|
62
|
+
try {
|
|
63
|
+
const packageRoot = path.join(root, '.openclaw', 'npm', 'node_modules', 'mem9');
|
|
64
|
+
await fs.mkdir(path.join(packageRoot, 'dist', 'esm'), { recursive: true });
|
|
65
|
+
await fs.writeFile(path.join(packageRoot, 'dist', 'esm', 'index.js'), 'export default {};\n', 'utf8');
|
|
66
|
+
await fs.writeFile(
|
|
67
|
+
path.join(packageRoot, 'package.json'),
|
|
68
|
+
JSON.stringify({
|
|
69
|
+
name: 'mem9',
|
|
70
|
+
exports: {
|
|
71
|
+
'.': {
|
|
72
|
+
import: './dist/esm/index.js',
|
|
73
|
+
require: './dist/cjs/index.cjs'
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}),
|
|
77
|
+
'utf8'
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const entrypoint = await findMem9RuntimeEntrypoint(path.join(root, '.openclaw'));
|
|
81
|
+
|
|
82
|
+
assert.equal(entrypoint, path.join(packageRoot, 'dist', 'esm', 'index.js'));
|
|
83
|
+
} finally {
|
|
84
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('respects openclaw.runtimeExtensions declared by the plugin manifest', async () => {
|
|
89
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'mem9-runtime-'));
|
|
90
|
+
try {
|
|
91
|
+
const packageRoot = path.join(root, '.openclaw', 'npm', 'node_modules', '@mem9', 'mem9');
|
|
92
|
+
await fs.mkdir(path.join(packageRoot, 'build'), { recursive: true });
|
|
93
|
+
await fs.writeFile(path.join(packageRoot, 'build', 'runtime.mjs'), 'export default {};\n', 'utf8');
|
|
94
|
+
await fs.writeFile(
|
|
95
|
+
path.join(packageRoot, 'package.json'),
|
|
96
|
+
JSON.stringify({
|
|
97
|
+
name: '@mem9/mem9',
|
|
98
|
+
openclaw: { runtimeExtensions: ['./build/runtime.mjs'] }
|
|
99
|
+
}),
|
|
100
|
+
'utf8'
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const entrypoint = await findMem9RuntimeEntrypoint(path.join(root, '.openclaw'));
|
|
104
|
+
|
|
105
|
+
assert.equal(entrypoint, path.join(packageRoot, 'build', 'runtime.mjs'));
|
|
106
|
+
} finally {
|
|
107
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('falls back to lib/index.js when neither manifest nor dist exists', async () => {
|
|
112
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'mem9-runtime-'));
|
|
113
|
+
try {
|
|
114
|
+
const packageRoot = path.join(root, '.openclaw', 'npm', 'node_modules', 'mem9');
|
|
115
|
+
await fs.mkdir(path.join(packageRoot, 'lib'), { recursive: true });
|
|
116
|
+
await fs.writeFile(path.join(packageRoot, 'lib', 'index.js'), 'module.exports = {};\n', 'utf8');
|
|
117
|
+
|
|
118
|
+
const entrypoint = await findMem9RuntimeEntrypoint(path.join(root, '.openclaw'));
|
|
119
|
+
|
|
120
|
+
assert.equal(entrypoint, path.join(packageRoot, 'lib', 'index.js'));
|
|
121
|
+
} finally {
|
|
122
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
123
|
+
}
|
|
124
|
+
});
|
|
39
125
|
});
|
|
@@ -19,15 +19,38 @@ const MEM9_PACKAGE_ROOTS = [
|
|
|
19
19
|
path.join('npm', 'node_modules', '@mem9', 'mem9'),
|
|
20
20
|
path.join('npm', 'node_modules', 'mem9')
|
|
21
21
|
];
|
|
22
|
-
const
|
|
22
|
+
const RUNTIME_FILE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs']);
|
|
23
|
+
const FALLBACK_ENTRYPOINTS = [
|
|
23
24
|
path.join('dist', 'index.js'),
|
|
24
25
|
path.join('dist', 'index.mjs'),
|
|
25
26
|
path.join('dist', 'index.cjs'),
|
|
26
27
|
'index.js',
|
|
27
28
|
'index.mjs',
|
|
28
|
-
'index.cjs'
|
|
29
|
+
'index.cjs',
|
|
30
|
+
path.join('lib', 'index.js'),
|
|
31
|
+
path.join('lib', 'index.mjs'),
|
|
32
|
+
path.join('lib', 'index.cjs'),
|
|
33
|
+
path.join('build', 'index.js'),
|
|
34
|
+
path.join('build', 'index.mjs'),
|
|
35
|
+
path.join('build', 'index.cjs'),
|
|
36
|
+
path.join('out', 'index.js'),
|
|
37
|
+
path.join('out', 'index.mjs'),
|
|
38
|
+
path.join('out', 'index.cjs'),
|
|
39
|
+
path.join('dist', 'main.js'),
|
|
40
|
+
path.join('dist', 'main.mjs'),
|
|
41
|
+
path.join('dist', 'main.cjs')
|
|
29
42
|
];
|
|
30
43
|
|
|
44
|
+
interface PluginManifest {
|
|
45
|
+
main?: unknown;
|
|
46
|
+
module?: unknown;
|
|
47
|
+
exports?: unknown;
|
|
48
|
+
openclaw?: {
|
|
49
|
+
runtimeExtensions?: unknown;
|
|
50
|
+
extensions?: unknown;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
31
54
|
interface OpenClawConfig {
|
|
32
55
|
plugins?: {
|
|
33
56
|
allow?: string[];
|
|
@@ -220,7 +243,8 @@ async function installMem9Plugin(cwd: string): Promise<void> {
|
|
|
220
243
|
|
|
221
244
|
export async function findMem9RuntimeEntrypoint(openclawRoot: string, config?: OpenClawConfig): Promise<string | null> {
|
|
222
245
|
for (const packageRoot of resolveMem9RuntimePackageRoots(openclawRoot, config)) {
|
|
223
|
-
|
|
246
|
+
const manifest = await readPluginManifest(packageRoot);
|
|
247
|
+
for (const entrypoint of collectEntrypointCandidates(packageRoot, manifest)) {
|
|
224
248
|
if (await pathExists(entrypoint)) {
|
|
225
249
|
return entrypoint;
|
|
226
250
|
}
|
|
@@ -236,19 +260,97 @@ async function ensureMem9RuntimeEntrypoint(openclawRoot: string, config?: OpenCl
|
|
|
236
260
|
}
|
|
237
261
|
const installRecord = readMem9InstallRecord(config);
|
|
238
262
|
const checkedPackageRoots = resolveMem9RuntimePackageRoots(openclawRoot, config);
|
|
263
|
+
const checkedEntrypoints: string[] = [];
|
|
264
|
+
for (const packageRoot of checkedPackageRoots) {
|
|
265
|
+
const manifest = await readPluginManifest(packageRoot);
|
|
266
|
+
for (const candidate of collectEntrypointCandidates(packageRoot, manifest)) {
|
|
267
|
+
if (!checkedEntrypoints.includes(candidate)) {
|
|
268
|
+
checkedEntrypoints.push(candidate);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
239
272
|
throw new JsonRpcException(
|
|
240
273
|
JSON_RPC_ERRORS.internalError,
|
|
241
274
|
'mem9 plugin is installed but missing compiled runtime output',
|
|
242
275
|
{
|
|
243
276
|
code: 'MEM9_RUNTIME_OUTPUT_MISSING',
|
|
244
|
-
expected:
|
|
277
|
+
expected: FALLBACK_ENTRYPOINTS.map((item) => `./${item.replace(/\\/g, '/')}`),
|
|
245
278
|
checkedPackageRoots,
|
|
246
|
-
checkedEntrypoints
|
|
279
|
+
checkedEntrypoints,
|
|
247
280
|
installRecord
|
|
248
281
|
}
|
|
249
282
|
);
|
|
250
283
|
}
|
|
251
284
|
|
|
285
|
+
async function readPluginManifest(packageRoot: string): Promise<PluginManifest | null> {
|
|
286
|
+
const manifestPath = path.join(packageRoot, 'package.json');
|
|
287
|
+
if (!(await pathExists(manifestPath))) return null;
|
|
288
|
+
try {
|
|
289
|
+
return await readJsonFile<PluginManifest>(manifestPath);
|
|
290
|
+
} catch {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function isRuntimeFile(relPath: string): boolean {
|
|
296
|
+
return RUNTIME_FILE_EXTENSIONS.has(path.extname(relPath).toLowerCase());
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function normalizeRelative(value: string): string {
|
|
300
|
+
return value.replace(/^\.[\\/]+/, '').split(/[\\/]+/).join(path.sep);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function collectExportsEntries(value: unknown): string[] {
|
|
304
|
+
const out: string[] = [];
|
|
305
|
+
const visit = (node: unknown, contextKey: string | null): void => {
|
|
306
|
+
if (typeof node === 'string') {
|
|
307
|
+
out.push(node);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (Array.isArray(node)) {
|
|
311
|
+
for (const child of node) visit(child, contextKey);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (node && typeof node === 'object') {
|
|
315
|
+
for (const [key, child] of Object.entries(node as Record<string, unknown>)) {
|
|
316
|
+
if (key.startsWith('.') && key !== '.') continue;
|
|
317
|
+
visit(child, key);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
visit(value, null);
|
|
322
|
+
return out;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function collectEntrypointCandidates(packageRoot: string, manifest: PluginManifest | null): string[] {
|
|
326
|
+
const seen = new Set<string>();
|
|
327
|
+
const out: string[] = [];
|
|
328
|
+
const push = (relCandidate: unknown): void => {
|
|
329
|
+
if (typeof relCandidate !== 'string') return;
|
|
330
|
+
const normalized = normalizeRelative(relCandidate);
|
|
331
|
+
if (!normalized) return;
|
|
332
|
+
if (!isRuntimeFile(normalized)) return;
|
|
333
|
+
const absolute = path.isAbsolute(normalized) ? normalized : path.join(packageRoot, normalized);
|
|
334
|
+
if (seen.has(absolute)) return;
|
|
335
|
+
seen.add(absolute);
|
|
336
|
+
out.push(absolute);
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const declaredRuntime = manifest?.openclaw?.runtimeExtensions;
|
|
340
|
+
if (Array.isArray(declaredRuntime)) {
|
|
341
|
+
for (const item of declaredRuntime) push(item);
|
|
342
|
+
}
|
|
343
|
+
if (manifest?.exports !== undefined) {
|
|
344
|
+
for (const item of collectExportsEntries(manifest.exports)) push(item);
|
|
345
|
+
}
|
|
346
|
+
push(manifest?.module);
|
|
347
|
+
push(manifest?.main);
|
|
348
|
+
|
|
349
|
+
for (const item of FALLBACK_ENTRYPOINTS) push(item);
|
|
350
|
+
|
|
351
|
+
return out;
|
|
352
|
+
}
|
|
353
|
+
|
|
252
354
|
function resolveMem9RuntimePackageRoots(openclawRoot: string, config?: OpenClawConfig): string[] {
|
|
253
355
|
const roots: string[] = [];
|
|
254
356
|
const installPath = pickString(readMem9InstallRecord(config)?.installPath);
|