log-llm-config-staging 1.4.1 → 1.4.3
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/collection/skills_cli_collector.js +134 -0
- package/dist/log_config_files/paths/pattern_resolver.js +43 -2
- package/dist/log_config_files/runtime/compliance_check.js +4 -0
- package/dist/log_config_files/runtime/hook_type_for_request.js +3 -1
- package/dist/log_config_files/runtime/main_runner.js +9 -0
- package/dist/log_config_files/runtime/trusted_restarts.js +4 -0
- package/package.json +3 -2
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
export const SKILLS_CLI_FILE_TYPE = 'skills_cli_installed';
|
|
7
|
+
export const SKILLS_CLI_INSTALLED_PATH = join(homedir(), '.agents', '.skills-cli-installed.json');
|
|
8
|
+
/** Override fallback runner, e.g. `skills@1.5.10`. Default `skills` uses the machine npx cache. */
|
|
9
|
+
export const SKILLS_CLI_NPX_PACKAGE_ENV = 'SKILLS_CLI_NPX_PACKAGE';
|
|
10
|
+
const LIST_TIMEOUT_MS = 120_000;
|
|
11
|
+
function listExecEnv() {
|
|
12
|
+
return {
|
|
13
|
+
...process.env,
|
|
14
|
+
DISABLE_TELEMETRY: process.env.DISABLE_TELEMETRY ?? '1',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function npxPackageSpecForFallback() {
|
|
18
|
+
const fromEnv = (process.env[SKILLS_CLI_NPX_PACKAGE_ENV] || '').trim();
|
|
19
|
+
return fromEnv || 'skills';
|
|
20
|
+
}
|
|
21
|
+
function resolveSkillsListRunner() {
|
|
22
|
+
try {
|
|
23
|
+
const require = createRequire(fileURLToPath(import.meta.url));
|
|
24
|
+
const bin = require.resolve('skills/bin/cli.mjs');
|
|
25
|
+
const pkg = require('skills/package.json');
|
|
26
|
+
const version = (pkg.version || '').trim() || 'unknown';
|
|
27
|
+
return { mode: 'bundled', bin, version };
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return { mode: 'npx', packageSpec: npxPackageSpecForFallback() };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function execSkillsList(args, cwd, runner) {
|
|
34
|
+
if (runner.mode === 'bundled') {
|
|
35
|
+
return execFileSync(process.execPath, [runner.bin, 'list', ...args, '--json'], {
|
|
36
|
+
encoding: 'utf8',
|
|
37
|
+
cwd,
|
|
38
|
+
timeout: LIST_TIMEOUT_MS,
|
|
39
|
+
env: listExecEnv(),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return execFileSync('npx', ['--yes', runner.packageSpec, 'list', ...args, '--json'], {
|
|
43
|
+
encoding: 'utf8',
|
|
44
|
+
cwd,
|
|
45
|
+
timeout: LIST_TIMEOUT_MS,
|
|
46
|
+
env: listExecEnv(),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function runnerVersionLabel(runner) {
|
|
50
|
+
if (runner.mode === 'bundled')
|
|
51
|
+
return runner.version;
|
|
52
|
+
return `npx:${runner.packageSpec}`;
|
|
53
|
+
}
|
|
54
|
+
export function runSkillsListJson(args, cwd) {
|
|
55
|
+
const runner = resolveSkillsListRunner();
|
|
56
|
+
const out = execSkillsList(args, cwd, runner);
|
|
57
|
+
const trimmed = out.trim();
|
|
58
|
+
if (!trimmed)
|
|
59
|
+
return [];
|
|
60
|
+
const parsed = JSON.parse(trimmed);
|
|
61
|
+
if (!Array.isArray(parsed))
|
|
62
|
+
return [];
|
|
63
|
+
return parsed;
|
|
64
|
+
}
|
|
65
|
+
function normalizeListRows(rows, scope) {
|
|
66
|
+
const out = [];
|
|
67
|
+
for (const row of rows) {
|
|
68
|
+
const name = (row.name || '').trim();
|
|
69
|
+
const path = (row.path || '').trim();
|
|
70
|
+
if (!name || !path)
|
|
71
|
+
continue;
|
|
72
|
+
const agents = Array.isArray(row.agents)
|
|
73
|
+
? row.agents.map((a) => String(a).trim()).filter(Boolean)
|
|
74
|
+
: [];
|
|
75
|
+
out.push({
|
|
76
|
+
name,
|
|
77
|
+
path,
|
|
78
|
+
scope: (row.scope || scope).trim() || scope,
|
|
79
|
+
agents,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
/** One-line-per-scope summary for hook_request.log (matches `skills list --json`). */
|
|
85
|
+
export function formatSkillsListScopeForHookLog(scopeLabel, entries) {
|
|
86
|
+
if (entries.length === 0) {
|
|
87
|
+
return `skills_cli list ${scopeLabel}: 0 skill(s)`;
|
|
88
|
+
}
|
|
89
|
+
const detail = entries
|
|
90
|
+
.map((e) => `${e.name}@${e.path} agents=${e.agents.length ? e.agents.join(',') : 'none'}`)
|
|
91
|
+
.join(' | ');
|
|
92
|
+
return `skills_cli list ${scopeLabel}: ${entries.length} skill(s) — ${detail}`;
|
|
93
|
+
}
|
|
94
|
+
export function collectSkillsCliInstalled(projectRoot, log) {
|
|
95
|
+
const logLine = (message) => {
|
|
96
|
+
log?.(message);
|
|
97
|
+
};
|
|
98
|
+
try {
|
|
99
|
+
const runner = resolveSkillsListRunner();
|
|
100
|
+
if (runner.mode === 'bundled') {
|
|
101
|
+
logLine(`skills_cli: bundled skills@${runner.version} — list -g --json && list --json (projectRoot=${projectRoot})`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
logLine(`skills_cli: npx fallback (${runner.packageSpec}) — no bundled skills dep; uses machine npx cache (projectRoot=${projectRoot})`);
|
|
105
|
+
}
|
|
106
|
+
const globalRows = runSkillsListJson(['-g'], projectRoot);
|
|
107
|
+
const projectRows = runSkillsListJson([], projectRoot);
|
|
108
|
+
const global = normalizeListRows(globalRows, 'global');
|
|
109
|
+
const project = normalizeListRows(projectRows, 'project');
|
|
110
|
+
logLine(formatSkillsListScopeForHookLog('-g', global));
|
|
111
|
+
logLine(formatSkillsListScopeForHookLog('project', project));
|
|
112
|
+
const payload = {
|
|
113
|
+
version: 1,
|
|
114
|
+
skills_cli_version: runnerVersionLabel(runner),
|
|
115
|
+
generated_at: new Date().toISOString(),
|
|
116
|
+
global,
|
|
117
|
+
project,
|
|
118
|
+
};
|
|
119
|
+
if (payload.global.length === 0 && payload.project.length === 0) {
|
|
120
|
+
logLine('skills_cli_installed: not uploaded (no global or project skills)');
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
logLine(`skills_cli_installed: upload ${SKILLS_CLI_INSTALLED_PATH} global=${global.length} project=${project.length}`);
|
|
124
|
+
return {
|
|
125
|
+
file_type: SKILLS_CLI_FILE_TYPE,
|
|
126
|
+
file_path: SKILLS_CLI_INSTALLED_PATH,
|
|
127
|
+
raw_content: payload,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
logLine(`skills_cli: list --json failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -8,6 +8,20 @@ function normalizePathSkipPrefixes(prefixes) {
|
|
|
8
8
|
return [];
|
|
9
9
|
return prefixes.filter((p) => typeof p === 'string' && p.length > 0);
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Expands glob patterns with double-asterisk (recursive directory traversal).
|
|
13
|
+
*
|
|
14
|
+
* Example: ~/.cursor/plugins/cache/ ** /skills/ recursively finds all skills
|
|
15
|
+
* directories under the cache folder, up to RECURSIVE_GLOB_MAX_DEPTH.
|
|
16
|
+
*
|
|
17
|
+
* @param pathPattern - Glob pattern with double-asterisk for recursive descent
|
|
18
|
+
* @param fileType - File type classification for collected targets
|
|
19
|
+
* @param home - User home directory path
|
|
20
|
+
* @param contentFormat - Optional content format hint
|
|
21
|
+
* @param dirGlob - Optional directory glob pattern
|
|
22
|
+
* @param homeRecurseSkipDirs - Directory names to skip when recursing from home
|
|
23
|
+
* @returns Array of collection targets matching the pattern
|
|
24
|
+
*/
|
|
11
25
|
function expandRecursiveGlobPathPattern(pathPattern, fileType, home, contentFormat, dirGlob, homeRecurseSkipDirs = []) {
|
|
12
26
|
const norm = pathPattern.replace(/\\/g, '/');
|
|
13
27
|
const doubleStarIndex = norm.indexOf('**');
|
|
@@ -67,6 +81,29 @@ function expandRecursiveGlobPathPattern(pathPattern, fileType, home, contentForm
|
|
|
67
81
|
walk(basePath, 0, true);
|
|
68
82
|
return targets;
|
|
69
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Expands glob patterns with asterisk wildcards (non-recursive) into concrete file or directory paths.
|
|
86
|
+
*
|
|
87
|
+
* Supports multi-segment wildcards (e.g., star-slash-star-slash patterns).
|
|
88
|
+
*
|
|
89
|
+
* **Key behavior:** On the final segment of a file pattern (not ending in slash), wildcards match
|
|
90
|
+
* files; earlier segments—and directory patterns—match directories only. This allows filename
|
|
91
|
+
* patterns like local_STAR.json to correctly resolve files rather than directories.
|
|
92
|
+
*
|
|
93
|
+
* Examples:
|
|
94
|
+
* - Pattern ending with a literal filename matches that file in wildcard directories
|
|
95
|
+
* - Pattern ending with prefix_STAR.ext matches files with that prefix and extension
|
|
96
|
+
* - Pattern ending with slash matches only directories
|
|
97
|
+
*
|
|
98
|
+
* @param pathPattern - Glob pattern (may not contain double-asterisk; use expandRecursiveGlobPathPattern for that)
|
|
99
|
+
* @param fileType - File type classification for collected targets
|
|
100
|
+
* @param home - User home directory path
|
|
101
|
+
* @param projectRoot - Project root directory path
|
|
102
|
+
* @param contentFormat - Optional content format hint
|
|
103
|
+
* @param dirGlob - Optional directory glob pattern
|
|
104
|
+
* @param absolutePathPrefixes - Allowed absolute path prefixes
|
|
105
|
+
* @returns Array of collection targets matching the pattern
|
|
106
|
+
*/
|
|
70
107
|
function expandGlobPathPattern(pathPattern, fileType, home, projectRoot, contentFormat, dirGlob, absolutePathPrefixes = []) {
|
|
71
108
|
const norm = pathPattern.replace(/\\/g, '/');
|
|
72
109
|
if (!norm.includes('*'))
|
|
@@ -111,12 +148,16 @@ function expandGlobPathPattern(pathPattern, fileType, home, projectRoot, content
|
|
|
111
148
|
return;
|
|
112
149
|
}
|
|
113
150
|
const seg = segments[segIdx];
|
|
151
|
+
// On the final segment of a file pattern (not ending in `/`) wildcards
|
|
152
|
+
// match files; earlier segments — and dir patterns — match directories.
|
|
153
|
+
const wantFile = segIdx === segments.length - 1 && !isDir;
|
|
154
|
+
const entryMatches = (entry) => wantFile ? entry.isFile() : entry.isDirectory();
|
|
114
155
|
if (seg === '*') {
|
|
115
156
|
if (!existsSync(currentPath))
|
|
116
157
|
return;
|
|
117
158
|
try {
|
|
118
159
|
for (const entry of readdirSync(currentPath, { withFileTypes: true })) {
|
|
119
|
-
if (!entry
|
|
160
|
+
if (!entryMatches(entry))
|
|
120
161
|
continue;
|
|
121
162
|
recurse(join(currentPath, entry.name), segIdx + 1);
|
|
122
163
|
}
|
|
@@ -131,7 +172,7 @@ function expandGlobPathPattern(pathPattern, fileType, home, projectRoot, content
|
|
|
131
172
|
return;
|
|
132
173
|
try {
|
|
133
174
|
for (const entry of readdirSync(currentPath, { withFileTypes: true })) {
|
|
134
|
-
if (!entry
|
|
175
|
+
if (!entryMatches(entry))
|
|
135
176
|
continue;
|
|
136
177
|
if (prefix && !entry.name.startsWith(prefix))
|
|
137
178
|
continue;
|
|
@@ -43,6 +43,8 @@ export function normalizeAgentToken(raw) {
|
|
|
43
43
|
return 'copilot';
|
|
44
44
|
if (s === 'opencode')
|
|
45
45
|
return 'opencode';
|
|
46
|
+
if (s === 'codex')
|
|
47
|
+
return 'codex';
|
|
46
48
|
return '';
|
|
47
49
|
}
|
|
48
50
|
function currentAgentFromEnv() {
|
|
@@ -58,6 +60,8 @@ function currentAgentFromEnv() {
|
|
|
58
60
|
return 'copilot';
|
|
59
61
|
if (hookType === 'opencode')
|
|
60
62
|
return 'opencode';
|
|
63
|
+
if (hookType === 'codex')
|
|
64
|
+
return 'codex';
|
|
61
65
|
return 'claude';
|
|
62
66
|
}
|
|
63
67
|
function targetsCurrentAgent(entry, agent) {
|
|
@@ -6,7 +6,7 @@ function normalizeToken(raw) {
|
|
|
6
6
|
return 'claude';
|
|
7
7
|
if (s === 'github_copilot')
|
|
8
8
|
return 'copilot';
|
|
9
|
-
if (s === 'cursor' || s === 'claude' || s === 'copilot' || s === 'opencode')
|
|
9
|
+
if (s === 'cursor' || s === 'claude' || s === 'copilot' || s === 'opencode' || s === 'codex')
|
|
10
10
|
return s;
|
|
11
11
|
// Legacy hooks set OPTIMUS_AGENT=Cursor (display casing)
|
|
12
12
|
if (raw.trim() === 'Cursor')
|
|
@@ -24,5 +24,7 @@ export function resolveHookTypeFromEnv(env = process.env) {
|
|
|
24
24
|
return 'copilot';
|
|
25
25
|
if (token === 'opencode')
|
|
26
26
|
return 'opencode';
|
|
27
|
+
if (token === 'codex')
|
|
28
|
+
return 'codex';
|
|
27
29
|
return 'claude';
|
|
28
30
|
}
|
|
@@ -14,6 +14,7 @@ import { readJSONFile, readMarkdownFile } from '../readers/file_readers.js';
|
|
|
14
14
|
import { isVscdbVirtualPath, tryReadVscdbVirtualFile, summarizeComposerPayloadForDiagnostics, } from '../readers/vscdb_config_builder.js';
|
|
15
15
|
import { persistVscdbComposerContractFromPatternsResponse } from '../readers/vscdb_reader.js';
|
|
16
16
|
import { collectConfigFilesFromPatterns, collectMcpToolFiles, collectConfigFilesFromInstalledPlugins, collectPluginCacheMcpFiles, collectMcpFromClaudeJsonProjects, collectClaudeDesktopExtensionManifests, collectClaudeDesktopExtensionSettingsFiles, enrichClaudeDesktopExtensionsInstallationsUpload, determineFileTypeFromPath, } from '../collection/config_collector.js';
|
|
17
|
+
import { collectSkillsCliInstalled } from '../collection/skills_cli_collector.js';
|
|
17
18
|
import { collectWorkspaceVscdbs } from '../collection/mcp_tool_collector.js';
|
|
18
19
|
import { collectCursorProjectWorkspaceMcpConfigs } from '../collection/cursor_project_mcp_collector.js';
|
|
19
20
|
import { normalizePathSkipPrefixes } from '../paths/pattern_resolver.js';
|
|
@@ -115,6 +116,14 @@ async function collectAllConfigFiles(endpointBase) {
|
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
118
|
}
|
|
119
|
+
const skillsCliEntry = collectSkillsCliInstalled(PROJECT_ROOT, hookRunLog);
|
|
120
|
+
if (skillsCliEntry) {
|
|
121
|
+
const key = `${skillsCliEntry.file_type}\t${skillsCliEntry.file_path}`;
|
|
122
|
+
if (!existingPaths.has(key)) {
|
|
123
|
+
existingPaths.add(key);
|
|
124
|
+
configFiles.push(skillsCliEntry);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
118
127
|
const worktreeReport = scanActiveWorktrees(PROJECT_ROOT, HOME_DIR);
|
|
119
128
|
const activeRoots = activeWorktreeRootSet(worktreeReport);
|
|
120
129
|
const beforeFilter = configFiles.length;
|
|
@@ -36,6 +36,8 @@ function currentAgentFromEnv() {
|
|
|
36
36
|
return 'copilot';
|
|
37
37
|
if (override === 'opencode')
|
|
38
38
|
return 'opencode';
|
|
39
|
+
if (override === 'codex')
|
|
40
|
+
return 'codex';
|
|
39
41
|
if (override === 'claude' || override === 'claude_desktop')
|
|
40
42
|
return 'claude';
|
|
41
43
|
const hookType = normalizeAgentToken(process.env.OPTIMUS_HOOK_TYPE);
|
|
@@ -45,6 +47,8 @@ function currentAgentFromEnv() {
|
|
|
45
47
|
return 'copilot';
|
|
46
48
|
if (hookType === 'opencode')
|
|
47
49
|
return 'opencode';
|
|
50
|
+
if (hookType === 'codex')
|
|
51
|
+
return 'codex';
|
|
48
52
|
return 'claude';
|
|
49
53
|
}
|
|
50
54
|
/** Spawn each trusted command detached (same pattern as former compliance_prompt_gate fireRestartCommands). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "log-llm-config-staging",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
4
4
|
"description": "CLI helpers for logging hardware UUIDs and posting startup payloads to Optimus Security.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"axios": "^1.15.2",
|
|
60
60
|
"canonicalize": "^2.1.0",
|
|
61
|
-
"optimus-tofu-staging": "^0.1.17"
|
|
61
|
+
"optimus-tofu-staging": "^0.1.17",
|
|
62
|
+
"skills": "1.5.10"
|
|
62
63
|
}
|
|
63
64
|
}
|