log-llm-config 1.0.20 → 1.0.22
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/log_config_files.js +195 -16
- package/package.json +1 -1
package/dist/log_config_files.js
CHANGED
|
@@ -6,7 +6,7 @@ import { homedir } from 'node:os';
|
|
|
6
6
|
import crypto from 'node:crypto';
|
|
7
7
|
import { execSync } from 'node:child_process';
|
|
8
8
|
import path from 'node:path';
|
|
9
|
-
import { getFileCollectionPatterns, patchPayload, postStartupPayload } from './endpoint_client.js';
|
|
9
|
+
import { getFileCollectionPatterns, patchPayload, postStartupPayload, } from './endpoint_client.js';
|
|
10
10
|
import { runSensitivePathsAudit } from './log_sensitive_paths_audit.js';
|
|
11
11
|
const AUTH_KEY_RELATIVE_PATH = path.join('opt-ai-sec', 'management', 'auth_key.txt');
|
|
12
12
|
/** Claude installed plugins manifest (cache-based installs). */
|
|
@@ -339,6 +339,20 @@ function collectConfigFilesFromPatterns(patterns, projectRoot) {
|
|
|
339
339
|
const home = homedir();
|
|
340
340
|
const seenPaths = new Set();
|
|
341
341
|
const targets = [];
|
|
342
|
+
const enrichByFileType = {};
|
|
343
|
+
for (const p of patterns) {
|
|
344
|
+
if (p.enrich && !enrichByFileType[p.file_type])
|
|
345
|
+
enrichByFileType[p.file_type] = p.enrich;
|
|
346
|
+
}
|
|
347
|
+
// Openclaw: we only collect package.json files (no version enrichment here; backend uses them in resource generation).
|
|
348
|
+
if (!enrichByFileType['openclaw_config']) {
|
|
349
|
+
enrichByFileType['openclaw_config'] = {
|
|
350
|
+
installs_path: 'plugins.installs',
|
|
351
|
+
installs_path_fallback: 'installs',
|
|
352
|
+
installs_shape: 'object',
|
|
353
|
+
id_field: 'id',
|
|
354
|
+
};
|
|
355
|
+
}
|
|
342
356
|
for (const { path_pattern, file_type } of patterns) {
|
|
343
357
|
for (const t of resolvePatternToTargets(path_pattern, file_type, projectRoot, home)) {
|
|
344
358
|
const key = `${t.path}\t${t.file_type}`;
|
|
@@ -442,14 +456,27 @@ function collectConfigFilesFromPatterns(patterns, projectRoot) {
|
|
|
442
456
|
const vscdbState = readVSCDBState();
|
|
443
457
|
if (vscdbState) {
|
|
444
458
|
if (vscdbState.composerState) {
|
|
459
|
+
const webToolsTopKeys = [
|
|
460
|
+
'lastBrowserConnectionMode',
|
|
461
|
+
'playwrightProtection',
|
|
462
|
+
'isWebSearchToolEnabled',
|
|
463
|
+
'isWebFetchToolEnabled',
|
|
464
|
+
'autoAcceptWebSearchTool',
|
|
465
|
+
];
|
|
466
|
+
const rawContent = {
|
|
467
|
+
composerState: vscdbState.composerState,
|
|
468
|
+
source: 'state.vscdb',
|
|
469
|
+
extracted_at: new Date().toISOString(),
|
|
470
|
+
};
|
|
471
|
+
for (const key of webToolsTopKeys) {
|
|
472
|
+
if (key in vscdbState && vscdbState[key] !== undefined) {
|
|
473
|
+
rawContent[key] = vscdbState[key];
|
|
474
|
+
}
|
|
475
|
+
}
|
|
445
476
|
configFiles.push({
|
|
446
477
|
file_type: 'vscode_settings',
|
|
447
478
|
file_path: `${VSCDB_PATH}#composerState`,
|
|
448
|
-
raw_content:
|
|
449
|
-
composerState: vscdbState.composerState,
|
|
450
|
-
source: 'state.vscdb',
|
|
451
|
-
extracted_at: new Date().toISOString(),
|
|
452
|
-
},
|
|
479
|
+
raw_content: rawContent,
|
|
453
480
|
});
|
|
454
481
|
}
|
|
455
482
|
const chat = vscdbState.chat;
|
|
@@ -546,15 +573,120 @@ function collectConfigFilesFromPatterns(patterns, projectRoot) {
|
|
|
546
573
|
else {
|
|
547
574
|
raw = typeof content === 'string' ? { content, source: 'file' } : content;
|
|
548
575
|
}
|
|
576
|
+
const recipe = enrichByFileType[t.file_type];
|
|
577
|
+
if (recipe && typeof raw === 'object' && raw !== null) {
|
|
578
|
+
enrichRawFromRecipe(raw, recipe);
|
|
579
|
+
}
|
|
549
580
|
configFiles.push({
|
|
550
581
|
file_type: t.file_type,
|
|
551
582
|
file_path: t.logicalFilePath ?? t.path,
|
|
552
583
|
raw_content: raw,
|
|
553
584
|
});
|
|
585
|
+
// Openclaw: collect each install's package.json for backend to use in resource generation (version, etc.)
|
|
586
|
+
if (t.file_type === 'openclaw_config' && typeof raw === 'object' && raw !== null) {
|
|
587
|
+
pushOpenclawPackageJsonFiles(raw, configFiles);
|
|
588
|
+
}
|
|
554
589
|
}
|
|
555
590
|
}
|
|
556
591
|
return configFiles;
|
|
557
592
|
}
|
|
593
|
+
/**
|
|
594
|
+
* For openclaw_config raw content, push one openclaw_package_json config file per install path
|
|
595
|
+
* (installPath/package.json or sourcePath/package.json). Backend uses these during resource generation.
|
|
596
|
+
*/
|
|
597
|
+
function pushOpenclawPackageJsonFiles(raw, configFiles) {
|
|
598
|
+
let installs = getByPath(raw, 'plugins.installs');
|
|
599
|
+
if (!installs)
|
|
600
|
+
installs = getByPath(raw, 'installs');
|
|
601
|
+
if (!installs)
|
|
602
|
+
return;
|
|
603
|
+
const entries = Array.isArray(installs)
|
|
604
|
+
? installs.filter((e) => typeof e === 'object' && e !== null)
|
|
605
|
+
: typeof installs === 'object' && installs !== null
|
|
606
|
+
? Object.values(installs).filter((e) => typeof e === 'object' && e !== null)
|
|
607
|
+
: [];
|
|
608
|
+
for (const entry of entries) {
|
|
609
|
+
const basePath = (entry.installPath ?? entry.sourcePath ?? '').trim();
|
|
610
|
+
if (!basePath)
|
|
611
|
+
continue;
|
|
612
|
+
const pkgPath = join(basePath, 'package.json');
|
|
613
|
+
const pkg = readJSONFile(pkgPath);
|
|
614
|
+
if (pkg) {
|
|
615
|
+
configFiles.push({
|
|
616
|
+
file_type: 'openclaw_package_json',
|
|
617
|
+
file_path: pkgPath,
|
|
618
|
+
raw_content: pkg,
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Get a nested value from raw by dot-notation path (e.g. "plugins.installs").
|
|
625
|
+
*/
|
|
626
|
+
function getByPath(obj, path) {
|
|
627
|
+
const parts = path.split('.');
|
|
628
|
+
let cur = obj;
|
|
629
|
+
for (const p of parts) {
|
|
630
|
+
if (cur == null || typeof cur !== 'object')
|
|
631
|
+
return undefined;
|
|
632
|
+
cur = cur[p];
|
|
633
|
+
}
|
|
634
|
+
return cur;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Enrich raw_content using backend-provided recipe. Flow: 1) install manifest (path from recipe),
|
|
638
|
+
* 2) read install location from each entry, 3) find file (path_template), 4) read version_key.
|
|
639
|
+
* Mutates raw in place. Only runs when recipe has version_from_file and installs_path.
|
|
640
|
+
*/
|
|
641
|
+
function enrichRawFromRecipe(raw, recipe) {
|
|
642
|
+
const installsPath = recipe.installs_path ?? recipe.installs_path_fallback;
|
|
643
|
+
if (!installsPath)
|
|
644
|
+
return;
|
|
645
|
+
let installs = getByPath(raw, installsPath);
|
|
646
|
+
if (!installs && recipe.installs_path_fallback && recipe.installs_path_fallback !== installsPath) {
|
|
647
|
+
installs = getByPath(raw, recipe.installs_path_fallback);
|
|
648
|
+
}
|
|
649
|
+
if (!installs)
|
|
650
|
+
return;
|
|
651
|
+
const vf = recipe.version_from_file;
|
|
652
|
+
if (!vf?.version_key)
|
|
653
|
+
return;
|
|
654
|
+
const resolveVersion = (e) => {
|
|
655
|
+
// 1) Use version from install manifest if present
|
|
656
|
+
const v = e.version;
|
|
657
|
+
if (v !== undefined && v !== null && (typeof v !== 'string' || v.trim() !== ''))
|
|
658
|
+
return;
|
|
659
|
+
// 2) Else get from installPath/package.json (or sourcePath/package.json)
|
|
660
|
+
const installPath = e.installPath?.trim();
|
|
661
|
+
const sourcePath = e.sourcePath?.trim();
|
|
662
|
+
const basePath = installPath || sourcePath;
|
|
663
|
+
if (!basePath)
|
|
664
|
+
return;
|
|
665
|
+
const pkgPath = join(basePath, 'package.json');
|
|
666
|
+
const pkg = readJSONFile(pkgPath);
|
|
667
|
+
const versionKey = vf.version_key;
|
|
668
|
+
const versionVal = versionKey && pkg ? pkg[versionKey] : undefined;
|
|
669
|
+
if (typeof versionVal === 'string')
|
|
670
|
+
e.version = versionVal.trim();
|
|
671
|
+
};
|
|
672
|
+
if (recipe.installs_shape === 'array' && Array.isArray(installs)) {
|
|
673
|
+
for (const entry of installs) {
|
|
674
|
+
if (typeof entry === 'object' && entry !== null)
|
|
675
|
+
resolveVersion(entry);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
else if (typeof installs === 'object' && installs !== null) {
|
|
679
|
+
for (const key of Object.keys(installs)) {
|
|
680
|
+
const entry = installs[key];
|
|
681
|
+
if (typeof entry === 'object' && entry !== null) {
|
|
682
|
+
const e = entry;
|
|
683
|
+
if (recipe.installs_shape === 'object' && !('id' in e && e.id != null))
|
|
684
|
+
e.id = key;
|
|
685
|
+
resolveVersion(e);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
558
690
|
/**
|
|
559
691
|
* Read and parse an MCP config file
|
|
560
692
|
*/
|
|
@@ -1257,10 +1389,26 @@ function readVSCDBState(dbPath) {
|
|
|
1257
1389
|
if (composerResult && composerResult !== '') {
|
|
1258
1390
|
try {
|
|
1259
1391
|
const parsed = JSON.parse(composerResult);
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1392
|
+
if (parsed && typeof parsed === 'object') {
|
|
1393
|
+
// Extract composerState from the nested structure
|
|
1394
|
+
const composerState = (parsed.composerState ?? parsed);
|
|
1395
|
+
if (composerState && typeof composerState === 'object') {
|
|
1396
|
+
stateData.composerState = composerState;
|
|
1397
|
+
}
|
|
1398
|
+
// Cursor may store lastBrowserConnectionMode (and other Web Tools keys) at the top level
|
|
1399
|
+
// of the reactive storage blob. Include them so the backend can merge into composerState.
|
|
1400
|
+
const topLevelKeys = [
|
|
1401
|
+
'lastBrowserConnectionMode',
|
|
1402
|
+
'playwrightProtection',
|
|
1403
|
+
'isWebSearchToolEnabled',
|
|
1404
|
+
'isWebFetchToolEnabled',
|
|
1405
|
+
'autoAcceptWebSearchTool',
|
|
1406
|
+
];
|
|
1407
|
+
for (const key of topLevelKeys) {
|
|
1408
|
+
if (key in parsed && parsed[key] !== undefined) {
|
|
1409
|
+
stateData[key] = parsed[key];
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1264
1412
|
}
|
|
1265
1413
|
}
|
|
1266
1414
|
catch (parseError) {
|
|
@@ -1527,16 +1675,30 @@ function collectConfigFiles() {
|
|
|
1527
1675
|
const vscdbState = readVSCDBState();
|
|
1528
1676
|
if (vscdbState) {
|
|
1529
1677
|
console.log(`Found Cursor state data at: ${VSCDB_PATH}`);
|
|
1530
|
-
// Store composerState as vscode_settings (general VS Code/Cursor state)
|
|
1678
|
+
// Store composerState as vscode_settings (general VS Code/Cursor state).
|
|
1679
|
+
// Include top-level keys (e.g. lastBrowserConnectionMode) so backend can merge into composerState for Web Tools policy.
|
|
1531
1680
|
if (vscdbState.composerState) {
|
|
1681
|
+
const topLevelKeys = [
|
|
1682
|
+
'lastBrowserConnectionMode',
|
|
1683
|
+
'playwrightProtection',
|
|
1684
|
+
'isWebSearchToolEnabled',
|
|
1685
|
+
'isWebFetchToolEnabled',
|
|
1686
|
+
'autoAcceptWebSearchTool',
|
|
1687
|
+
];
|
|
1688
|
+
const rawContent = {
|
|
1689
|
+
composerState: vscdbState.composerState,
|
|
1690
|
+
source: 'state.vscdb',
|
|
1691
|
+
extracted_at: new Date().toISOString(),
|
|
1692
|
+
};
|
|
1693
|
+
for (const key of topLevelKeys) {
|
|
1694
|
+
if (key in vscdbState && vscdbState[key] !== undefined) {
|
|
1695
|
+
rawContent[key] = vscdbState[key];
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1532
1698
|
configFiles.push({
|
|
1533
1699
|
file_type: 'vscode_settings',
|
|
1534
1700
|
file_path: `${VSCDB_PATH}#composerState`, // Use fragment to distinguish
|
|
1535
|
-
raw_content:
|
|
1536
|
-
composerState: vscdbState.composerState,
|
|
1537
|
-
source: 'state.vscdb',
|
|
1538
|
-
extracted_at: new Date().toISOString(),
|
|
1539
|
-
},
|
|
1701
|
+
raw_content: rawContent,
|
|
1540
1702
|
});
|
|
1541
1703
|
}
|
|
1542
1704
|
// Store chat.tools as vscode_settings (for chat.tools.autoApprove policy)
|
|
@@ -2486,6 +2648,13 @@ async function logSingleFile(filePath) {
|
|
|
2486
2648
|
// Read the SQLite database using the provided path
|
|
2487
2649
|
const vscdbState = readVSCDBState(actualPath);
|
|
2488
2650
|
if (vscdbState) {
|
|
2651
|
+
const webToolsTopKeys = [
|
|
2652
|
+
'lastBrowserConnectionMode',
|
|
2653
|
+
'playwrightProtection',
|
|
2654
|
+
'isWebSearchToolEnabled',
|
|
2655
|
+
'isWebFetchToolEnabled',
|
|
2656
|
+
'autoAcceptWebSearchTool',
|
|
2657
|
+
];
|
|
2489
2658
|
// Check if there's a fragment (e.g., "#composerState" or "#chat.tools")
|
|
2490
2659
|
const fragment = filePath.includes('#') ? filePath.split('#')[1] : null;
|
|
2491
2660
|
if (fragment === 'composerState' && vscdbState.composerState) {
|
|
@@ -2494,6 +2663,11 @@ async function logSingleFile(filePath) {
|
|
|
2494
2663
|
source: 'state.vscdb',
|
|
2495
2664
|
extracted_at: new Date().toISOString(),
|
|
2496
2665
|
};
|
|
2666
|
+
for (const key of webToolsTopKeys) {
|
|
2667
|
+
if (key in vscdbState && vscdbState[key] !== undefined) {
|
|
2668
|
+
rawContent[key] = vscdbState[key];
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2497
2671
|
fileType = 'vscode_settings';
|
|
2498
2672
|
}
|
|
2499
2673
|
else if (fragment === 'chat.tools' && vscdbState.chat) {
|
|
@@ -2540,6 +2714,11 @@ async function logSingleFile(filePath) {
|
|
|
2540
2714
|
source: 'state.vscdb',
|
|
2541
2715
|
extracted_at: new Date().toISOString(),
|
|
2542
2716
|
};
|
|
2717
|
+
for (const key of webToolsTopKeys) {
|
|
2718
|
+
if (key in vscdbState && vscdbState[key] !== undefined) {
|
|
2719
|
+
rawContent[key] = vscdbState[key];
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2543
2722
|
fileType = 'vscode_settings';
|
|
2544
2723
|
}
|
|
2545
2724
|
else {
|