@xdarkicex/openclaw-memory-libravdb 1.4.38 → 1.4.40
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.
- package/dist/cli.js +50 -13
- package/dist/context-engine.d.ts +4 -11
- package/dist/context-engine.js +14 -9
- package/dist/index.js +61 -22
- package/dist/plugin-runtime.js +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -276,13 +276,22 @@ async function runSearch(runtime, cfg, queryArg, opts, logger) {
|
|
|
276
276
|
process.exitCode = 1;
|
|
277
277
|
return;
|
|
278
278
|
}
|
|
279
|
+
let maxResults;
|
|
280
|
+
let explicitMinScore;
|
|
281
|
+
try {
|
|
282
|
+
maxResults = normalizeCliLimit(opts?.maxResults ?? opts?.limit, "--max-results");
|
|
283
|
+
explicitMinScore = normalizeCliScore(opts?.minScore, "--min-score");
|
|
284
|
+
}
|
|
285
|
+
catch (validationError) {
|
|
286
|
+
logger.error(formatError(validationError));
|
|
287
|
+
process.exitCode = 1;
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
279
290
|
try {
|
|
280
291
|
const bridge = buildMemoryRuntimeBridge(runtime.getRpc, cfg);
|
|
281
292
|
const { manager } = await bridge.getMemorySearchManager({
|
|
282
293
|
agentId: opts?.agent,
|
|
283
294
|
});
|
|
284
|
-
const maxResults = normalizeLimit(opts?.maxResults ?? opts?.limit);
|
|
285
|
-
const explicitMinScore = normalizeNumber(opts?.minScore);
|
|
286
295
|
const minScore = explicitMinScore ?? resolveDefaultSearchMinScore(manager.status(), cfg);
|
|
287
296
|
const results = (await manager.search({
|
|
288
297
|
query,
|
|
@@ -336,10 +345,16 @@ async function runFlush(runtime, opts, logger) {
|
|
|
336
345
|
}
|
|
337
346
|
}
|
|
338
347
|
async function runExport(runtime, opts, logger) {
|
|
348
|
+
const namespace = resolveCliNamespace(opts);
|
|
349
|
+
if (!namespace) {
|
|
350
|
+
logger.error("LibraVDB export requires a namespace. Provide --user-id or --session-key.");
|
|
351
|
+
process.exitCode = 1;
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
339
354
|
try {
|
|
340
355
|
const rpc = await runtime.getRpc();
|
|
341
356
|
const result = await rpc.call("export_memory", {
|
|
342
|
-
namespace
|
|
357
|
+
namespace,
|
|
343
358
|
});
|
|
344
359
|
for (const record of result.records ?? []) {
|
|
345
360
|
stdout.write(`${JSON.stringify(record)}\n`);
|
|
@@ -351,11 +366,20 @@ async function runExport(runtime, opts, logger) {
|
|
|
351
366
|
}
|
|
352
367
|
}
|
|
353
368
|
async function runJournal(runtime, opts, logger) {
|
|
369
|
+
let limit;
|
|
370
|
+
try {
|
|
371
|
+
limit = normalizeCliLimit(opts?.limit, "--limit");
|
|
372
|
+
}
|
|
373
|
+
catch (validationError) {
|
|
374
|
+
logger.error(formatError(validationError));
|
|
375
|
+
process.exitCode = 1;
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
354
378
|
try {
|
|
355
379
|
const rpc = await runtime.getRpc();
|
|
356
380
|
const result = await rpc.call("list_lifecycle_journal", {
|
|
357
381
|
sessionId: opts?.sessionId?.trim() || undefined,
|
|
358
|
-
limit
|
|
382
|
+
limit,
|
|
359
383
|
});
|
|
360
384
|
for (const record of result.results ?? []) {
|
|
361
385
|
stdout.write(`${JSON.stringify(record)}\n`);
|
|
@@ -400,17 +424,30 @@ function formatError(error) {
|
|
|
400
424
|
}
|
|
401
425
|
return String(error);
|
|
402
426
|
}
|
|
403
|
-
function
|
|
404
|
-
if (
|
|
405
|
-
return
|
|
427
|
+
function normalizeCliLimit(limit, optionName) {
|
|
428
|
+
if (limit === undefined)
|
|
429
|
+
return undefined;
|
|
430
|
+
const parsed = parseStrictNumber(limit);
|
|
431
|
+
if (Number.isFinite(parsed) && Number.isInteger(parsed) && parsed > 0) {
|
|
432
|
+
return parsed;
|
|
406
433
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
434
|
+
throw new Error(`Invalid value for ${optionName}: must be a positive integer`);
|
|
435
|
+
}
|
|
436
|
+
function normalizeCliScore(value, optionName) {
|
|
437
|
+
if (value === undefined)
|
|
438
|
+
return undefined;
|
|
439
|
+
const parsed = parseStrictNumber(value);
|
|
440
|
+
if (Number.isFinite(parsed) && parsed >= 0 && parsed <= 1) {
|
|
441
|
+
return parsed;
|
|
412
442
|
}
|
|
413
|
-
|
|
443
|
+
throw new Error(`Invalid value for ${optionName}: must be a number between 0 and 1`);
|
|
444
|
+
}
|
|
445
|
+
function parseStrictNumber(value) {
|
|
446
|
+
if (typeof value === "number") {
|
|
447
|
+
return value;
|
|
448
|
+
}
|
|
449
|
+
const trimmed = value.trim();
|
|
450
|
+
return trimmed === "" ? NaN : Number(trimmed);
|
|
414
451
|
}
|
|
415
452
|
function normalizeNumber(value) {
|
|
416
453
|
if (typeof value === "number" && Number.isFinite(value)) {
|
package/dist/context-engine.d.ts
CHANGED
|
@@ -8,8 +8,9 @@ type KernelCompatibleMessage = {
|
|
|
8
8
|
};
|
|
9
9
|
type OpenClawCompatibleMessage = {
|
|
10
10
|
role: string;
|
|
11
|
-
content: string;
|
|
11
|
+
content: string | unknown[];
|
|
12
12
|
id?: string;
|
|
13
|
+
[key: string]: unknown;
|
|
13
14
|
};
|
|
14
15
|
type OpenClawCompatibleAssembleResult = {
|
|
15
16
|
messages: OpenClawCompatibleMessage[];
|
|
@@ -76,11 +77,7 @@ export declare function buildContextEngineFactory(runtime: PluginRuntime, cfg: P
|
|
|
76
77
|
sessionId: string;
|
|
77
78
|
sessionKey?: string;
|
|
78
79
|
userId?: string;
|
|
79
|
-
messages:
|
|
80
|
-
role: string;
|
|
81
|
-
content: unknown;
|
|
82
|
-
id?: string;
|
|
83
|
-
}>;
|
|
80
|
+
messages: OpenClawCompatibleMessage[];
|
|
84
81
|
tokenBudget: number;
|
|
85
82
|
prompt?: string;
|
|
86
83
|
currentTokenCount?: number;
|
|
@@ -96,11 +93,7 @@ export declare function buildContextEngineFactory(runtime: PluginRuntime, cfg: P
|
|
|
96
93
|
sessionId: string;
|
|
97
94
|
sessionKey?: string;
|
|
98
95
|
userId?: string;
|
|
99
|
-
messages:
|
|
100
|
-
role: string;
|
|
101
|
-
content: unknown;
|
|
102
|
-
id?: string;
|
|
103
|
-
}>;
|
|
96
|
+
messages: OpenClawCompatibleMessage[];
|
|
104
97
|
prePromptMessageCount?: number;
|
|
105
98
|
isHeartbeat?: boolean;
|
|
106
99
|
tokenBudget?: number;
|
package/dist/context-engine.js
CHANGED
|
@@ -85,9 +85,13 @@ function normalizeKernelContent(content) {
|
|
|
85
85
|
return content.map(stringifyKernelBlock).filter((part) => part.length > 0).join("\n");
|
|
86
86
|
}
|
|
87
87
|
function approximateTokenCount(text) {
|
|
88
|
-
if (
|
|
88
|
+
if (typeof text === "string") {
|
|
89
|
+
return Math.ceil(text.length / APPROX_CHARS_PER_TOKEN);
|
|
90
|
+
}
|
|
91
|
+
if (!Array.isArray(text)) {
|
|
89
92
|
return 0;
|
|
90
|
-
|
|
93
|
+
}
|
|
94
|
+
return Math.ceil(normalizeKernelContent(text).length / APPROX_CHARS_PER_TOKEN);
|
|
91
95
|
}
|
|
92
96
|
function approximateMessageTokens(message) {
|
|
93
97
|
// Approximate per-message wrapper overhead so trimming is conservative.
|
|
@@ -162,10 +166,11 @@ function truncateContentToTokenBudget(content, tokenBudget) {
|
|
|
162
166
|
if (tokenBudget <= 0)
|
|
163
167
|
return "";
|
|
164
168
|
const maxChars = Math.max(1, tokenBudget * APPROX_CHARS_PER_TOKEN);
|
|
165
|
-
|
|
166
|
-
|
|
169
|
+
const normalized = normalizeKernelContent(content);
|
|
170
|
+
if (normalized.length <= maxChars)
|
|
171
|
+
return normalized;
|
|
167
172
|
// Keep the tail so recent tool output / latest answer content is preserved.
|
|
168
|
-
return
|
|
173
|
+
return normalized.slice(normalized.length - maxChars);
|
|
169
174
|
}
|
|
170
175
|
function trimMessagesToBudget(messages, tokenBudget) {
|
|
171
176
|
if (tokenBudget <= 0 || messages.length === 0) {
|
|
@@ -399,7 +404,7 @@ export function buildContextEngineFactory(runtime, cfg, logger = console) {
|
|
|
399
404
|
return assembled;
|
|
400
405
|
const existingBlocks = [
|
|
401
406
|
assembled.systemPromptAddition,
|
|
402
|
-
...assembled.messages.map((message) => message.content),
|
|
407
|
+
...assembled.messages.map((message) => normalizeKernelContent(message.content)),
|
|
403
408
|
]
|
|
404
409
|
.flatMap((block) => block.split(/\n+/))
|
|
405
410
|
.map((block) => block.trim())
|
|
@@ -657,7 +662,7 @@ export function buildContextEngineFactory(runtime, cfg, logger = console) {
|
|
|
657
662
|
if (!compactionResult.ok) {
|
|
658
663
|
logger.warn?.(`LibraVDB predictive compaction blocked assemble path at ${currentContextTokens} tokens ` +
|
|
659
664
|
`(threshold=${dynamicCompactThreshold}): ${compactionResult.reason ?? "compaction failed"}`);
|
|
660
|
-
return buildBudgetFallbackContext(messages, args.tokenBudget);
|
|
665
|
+
return buildBudgetFallbackContext(args.messages, args.tokenBudget);
|
|
661
666
|
}
|
|
662
667
|
}
|
|
663
668
|
const kernel = await getKernelOrNull("assemble");
|
|
@@ -682,7 +687,7 @@ export function buildContextEngineFactory(runtime, cfg, logger = console) {
|
|
|
682
687
|
}
|
|
683
688
|
catch (error) {
|
|
684
689
|
logger.warn?.(`LibraVDB assemble kernel failed, using budget-clamped fallback context: ${error instanceof Error ? error.message : String(error)}`);
|
|
685
|
-
return buildBudgetFallbackContext(messages, args.tokenBudget);
|
|
690
|
+
return buildBudgetFallbackContext(args.messages, args.tokenBudget);
|
|
686
691
|
}
|
|
687
692
|
}
|
|
688
693
|
const rpc = await runtime.getRpc();
|
|
@@ -707,7 +712,7 @@ export function buildContextEngineFactory(runtime, cfg, logger = console) {
|
|
|
707
712
|
}
|
|
708
713
|
catch (error) {
|
|
709
714
|
logger.warn?.(`LibraVDB assemble sidecar failed, using budget-clamped fallback context: ${error instanceof Error ? error.message : String(error)}`);
|
|
710
|
-
return buildBudgetFallbackContext(messages, args.tokenBudget);
|
|
715
|
+
return buildBudgetFallbackContext(args.messages, args.tokenBudget);
|
|
711
716
|
}
|
|
712
717
|
},
|
|
713
718
|
async compact(args) {
|
package/dist/index.js
CHANGED
|
@@ -33354,13 +33354,21 @@ async function runSearch(runtime, cfg, queryArg, opts, logger) {
|
|
|
33354
33354
|
process.exitCode = 1;
|
|
33355
33355
|
return;
|
|
33356
33356
|
}
|
|
33357
|
+
let maxResults;
|
|
33358
|
+
let explicitMinScore;
|
|
33359
|
+
try {
|
|
33360
|
+
maxResults = normalizeCliLimit(opts?.maxResults ?? opts?.limit, "--max-results");
|
|
33361
|
+
explicitMinScore = normalizeCliScore(opts?.minScore, "--min-score");
|
|
33362
|
+
} catch (validationError) {
|
|
33363
|
+
logger.error(formatError2(validationError));
|
|
33364
|
+
process.exitCode = 1;
|
|
33365
|
+
return;
|
|
33366
|
+
}
|
|
33357
33367
|
try {
|
|
33358
33368
|
const bridge = buildMemoryRuntimeBridge(runtime.getRpc, cfg);
|
|
33359
33369
|
const { manager } = await bridge.getMemorySearchManager({
|
|
33360
33370
|
agentId: opts?.agent
|
|
33361
33371
|
});
|
|
33362
|
-
const maxResults = normalizeLimit(opts?.maxResults ?? opts?.limit);
|
|
33363
|
-
const explicitMinScore = normalizeNumber2(opts?.minScore);
|
|
33364
33372
|
const minScore = explicitMinScore ?? resolveDefaultSearchMinScore(manager.status(), cfg);
|
|
33365
33373
|
const results = await manager.search(
|
|
33366
33374
|
{
|
|
@@ -33414,10 +33422,16 @@ async function runFlush(runtime, opts, logger) {
|
|
|
33414
33422
|
}
|
|
33415
33423
|
}
|
|
33416
33424
|
async function runExport(runtime, opts, logger) {
|
|
33425
|
+
const namespace = resolveCliNamespace(opts);
|
|
33426
|
+
if (!namespace) {
|
|
33427
|
+
logger.error("LibraVDB export requires a namespace. Provide --user-id or --session-key.");
|
|
33428
|
+
process.exitCode = 1;
|
|
33429
|
+
return;
|
|
33430
|
+
}
|
|
33417
33431
|
try {
|
|
33418
33432
|
const rpc = await runtime.getRpc();
|
|
33419
33433
|
const result = await rpc.call("export_memory", {
|
|
33420
|
-
namespace
|
|
33434
|
+
namespace
|
|
33421
33435
|
});
|
|
33422
33436
|
for (const record of result.records ?? []) {
|
|
33423
33437
|
stdout.write(`${JSON.stringify(record)}
|
|
@@ -33429,11 +33443,19 @@ async function runExport(runtime, opts, logger) {
|
|
|
33429
33443
|
}
|
|
33430
33444
|
}
|
|
33431
33445
|
async function runJournal(runtime, opts, logger) {
|
|
33446
|
+
let limit;
|
|
33447
|
+
try {
|
|
33448
|
+
limit = normalizeCliLimit(opts?.limit, "--limit");
|
|
33449
|
+
} catch (validationError) {
|
|
33450
|
+
logger.error(formatError2(validationError));
|
|
33451
|
+
process.exitCode = 1;
|
|
33452
|
+
return;
|
|
33453
|
+
}
|
|
33432
33454
|
try {
|
|
33433
33455
|
const rpc = await runtime.getRpc();
|
|
33434
33456
|
const result = await rpc.call("list_lifecycle_journal", {
|
|
33435
33457
|
sessionId: opts?.sessionId?.trim() || void 0,
|
|
33436
|
-
limit
|
|
33458
|
+
limit
|
|
33437
33459
|
});
|
|
33438
33460
|
for (const record of result.results ?? []) {
|
|
33439
33461
|
stdout.write(`${JSON.stringify(record)}
|
|
@@ -33478,17 +33500,28 @@ function formatError2(error) {
|
|
|
33478
33500
|
}
|
|
33479
33501
|
return String(error);
|
|
33480
33502
|
}
|
|
33481
|
-
function
|
|
33482
|
-
if (
|
|
33483
|
-
|
|
33503
|
+
function normalizeCliLimit(limit, optionName) {
|
|
33504
|
+
if (limit === void 0) return void 0;
|
|
33505
|
+
const parsed = parseStrictNumber(limit);
|
|
33506
|
+
if (Number.isFinite(parsed) && Number.isInteger(parsed) && parsed > 0) {
|
|
33507
|
+
return parsed;
|
|
33484
33508
|
}
|
|
33485
|
-
|
|
33486
|
-
|
|
33487
|
-
|
|
33488
|
-
|
|
33489
|
-
|
|
33509
|
+
throw new Error(`Invalid value for ${optionName}: must be a positive integer`);
|
|
33510
|
+
}
|
|
33511
|
+
function normalizeCliScore(value, optionName) {
|
|
33512
|
+
if (value === void 0) return void 0;
|
|
33513
|
+
const parsed = parseStrictNumber(value);
|
|
33514
|
+
if (Number.isFinite(parsed) && parsed >= 0 && parsed <= 1) {
|
|
33515
|
+
return parsed;
|
|
33490
33516
|
}
|
|
33491
|
-
|
|
33517
|
+
throw new Error(`Invalid value for ${optionName}: must be a number between 0 and 1`);
|
|
33518
|
+
}
|
|
33519
|
+
function parseStrictNumber(value) {
|
|
33520
|
+
if (typeof value === "number") {
|
|
33521
|
+
return value;
|
|
33522
|
+
}
|
|
33523
|
+
const trimmed = value.trim();
|
|
33524
|
+
return trimmed === "" ? NaN : Number(trimmed);
|
|
33492
33525
|
}
|
|
33493
33526
|
function normalizeNumber2(value) {
|
|
33494
33527
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -33631,8 +33664,13 @@ function normalizeKernelContent(content) {
|
|
|
33631
33664
|
return content.map(stringifyKernelBlock).filter((part) => part.length > 0).join("\n");
|
|
33632
33665
|
}
|
|
33633
33666
|
function approximateTokenCount(text) {
|
|
33634
|
-
if (
|
|
33635
|
-
|
|
33667
|
+
if (typeof text === "string") {
|
|
33668
|
+
return Math.ceil(text.length / APPROX_CHARS_PER_TOKEN);
|
|
33669
|
+
}
|
|
33670
|
+
if (!Array.isArray(text)) {
|
|
33671
|
+
return 0;
|
|
33672
|
+
}
|
|
33673
|
+
return Math.ceil(normalizeKernelContent(text).length / APPROX_CHARS_PER_TOKEN);
|
|
33636
33674
|
}
|
|
33637
33675
|
function approximateMessageTokens(message) {
|
|
33638
33676
|
return approximateTokenCount(message.content) + 8;
|
|
@@ -33698,8 +33736,9 @@ function logPredictiveCompactionOutcome(params) {
|
|
|
33698
33736
|
function truncateContentToTokenBudget(content, tokenBudget) {
|
|
33699
33737
|
if (tokenBudget <= 0) return "";
|
|
33700
33738
|
const maxChars = Math.max(1, tokenBudget * APPROX_CHARS_PER_TOKEN);
|
|
33701
|
-
|
|
33702
|
-
|
|
33739
|
+
const normalized = normalizeKernelContent(content);
|
|
33740
|
+
if (normalized.length <= maxChars) return normalized;
|
|
33741
|
+
return normalized.slice(normalized.length - maxChars);
|
|
33703
33742
|
}
|
|
33704
33743
|
function trimMessagesToBudget(messages, tokenBudget) {
|
|
33705
33744
|
if (tokenBudget <= 0 || messages.length === 0) {
|
|
@@ -33919,7 +33958,7 @@ function buildContextEngineFactory(runtime, cfg, logger = console) {
|
|
|
33919
33958
|
if (tokens.length === 0) return assembled;
|
|
33920
33959
|
const existingBlocks = [
|
|
33921
33960
|
assembled.systemPromptAddition,
|
|
33922
|
-
...assembled.messages.map((message) => message.content)
|
|
33961
|
+
...assembled.messages.map((message) => normalizeKernelContent(message.content))
|
|
33923
33962
|
].flatMap((block) => block.split(/\n+/)).map((block) => block.trim()).filter((block) => block.length > 0);
|
|
33924
33963
|
const missingTokens = tokens.filter(
|
|
33925
33964
|
(token) => !existingBlocks.some((block) => isExactRecallFact(block, token))
|
|
@@ -34161,7 +34200,7 @@ function buildContextEngineFactory(runtime, cfg, logger = console) {
|
|
|
34161
34200
|
logger.warn?.(
|
|
34162
34201
|
`LibraVDB predictive compaction blocked assemble path at ${currentContextTokens} tokens (threshold=${dynamicCompactThreshold}): ${compactionResult.reason ?? "compaction failed"}`
|
|
34163
34202
|
);
|
|
34164
|
-
return buildBudgetFallbackContext(messages, args.tokenBudget);
|
|
34203
|
+
return buildBudgetFallbackContext(args.messages, args.tokenBudget);
|
|
34165
34204
|
}
|
|
34166
34205
|
}
|
|
34167
34206
|
const kernel = await getKernelOrNull("assemble");
|
|
@@ -34190,7 +34229,7 @@ function buildContextEngineFactory(runtime, cfg, logger = console) {
|
|
|
34190
34229
|
logger.warn?.(
|
|
34191
34230
|
`LibraVDB assemble kernel failed, using budget-clamped fallback context: ${error instanceof Error ? error.message : String(error)}`
|
|
34192
34231
|
);
|
|
34193
|
-
return buildBudgetFallbackContext(messages, args.tokenBudget);
|
|
34232
|
+
return buildBudgetFallbackContext(args.messages, args.tokenBudget);
|
|
34194
34233
|
}
|
|
34195
34234
|
}
|
|
34196
34235
|
const rpc = await runtime.getRpc();
|
|
@@ -34219,7 +34258,7 @@ function buildContextEngineFactory(runtime, cfg, logger = console) {
|
|
|
34219
34258
|
logger.warn?.(
|
|
34220
34259
|
`LibraVDB assemble sidecar failed, using budget-clamped fallback context: ${error instanceof Error ? error.message : String(error)}`
|
|
34221
34260
|
);
|
|
34222
|
-
return buildBudgetFallbackContext(messages, args.tokenBudget);
|
|
34261
|
+
return buildBudgetFallbackContext(args.messages, args.tokenBudget);
|
|
34223
34262
|
}
|
|
34224
34263
|
},
|
|
34225
34264
|
async compact(args) {
|
|
@@ -39552,7 +39591,7 @@ function formatError5(error) {
|
|
|
39552
39591
|
function enrichStartupError(error, healthMessage) {
|
|
39553
39592
|
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
39554
39593
|
const message = rawMessage.trim() || "LibraVDB daemon startup failed";
|
|
39555
|
-
if (message.includes("
|
|
39594
|
+
if (message.includes("package does not provision the daemon binary")) {
|
|
39556
39595
|
return error instanceof Error ? error : new Error(message);
|
|
39557
39596
|
}
|
|
39558
39597
|
const shouldHint = /health check|daemon unavailable|connection refused|ECONNREFUSED|ENOENT|fallback mode|ONNX Runtime|embedder/i.test(
|
package/dist/plugin-runtime.js
CHANGED
|
@@ -133,7 +133,7 @@ function formatError(error) {
|
|
|
133
133
|
export function enrichStartupError(error, healthMessage) {
|
|
134
134
|
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
135
135
|
const message = rawMessage.trim() || "LibraVDB daemon startup failed";
|
|
136
|
-
if (message.includes("
|
|
136
|
+
if (message.includes("package does not provision the daemon binary")) {
|
|
137
137
|
return error instanceof Error ? error : new Error(message);
|
|
138
138
|
}
|
|
139
139
|
const shouldHint = /health check|daemon unavailable|connection refused|ECONNREFUSED|ENOENT|fallback mode|ONNX Runtime|embedder/i.test(`${message} ${healthMessage ?? ""}`);
|
package/openclaw.plugin.json
CHANGED