log-llm-config-staging 1.4.5 → 1.4.6
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.
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { closeSync, mkdtempSync, openSync, readFileSync, rmSync } from 'node:fs';
|
|
3
|
+
import { homedir, tmpdir } from 'node:os';
|
|
3
4
|
import { join } from 'node:path';
|
|
4
5
|
export const SKILLS_CLI_FILE_TYPE = 'skills_cli_installed';
|
|
5
6
|
export const SKILLS_CLI_INSTALLED_PATH = join(homedir(), '.agents', '.skills-cli-installed.json');
|
|
6
7
|
/** Override the skills package spec, e.g. `skills@1.5.10`. Default uses whatever is on the machine. */
|
|
7
8
|
export const SKILLS_CLI_NPX_PACKAGE_ENV = 'SKILLS_CLI_NPX_PACKAGE';
|
|
8
9
|
const LIST_TIMEOUT_MS = 120_000;
|
|
10
|
+
/** stderr from npx/npm only; stdout is streamed to a temp file (avoids pipe/maxBuffer truncation). */
|
|
11
|
+
const LIST_STDERR_MAX_BUFFER = 16 * 1024 * 1024;
|
|
9
12
|
function listExecEnv() {
|
|
10
13
|
return {
|
|
11
14
|
...process.env,
|
|
@@ -16,20 +19,61 @@ function npxPackageSpec() {
|
|
|
16
19
|
const fromEnv = (process.env[SKILLS_CLI_NPX_PACKAGE_ENV] || '').trim();
|
|
17
20
|
return fromEnv || 'skills';
|
|
18
21
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
cwd,
|
|
23
|
-
timeout: LIST_TIMEOUT_MS,
|
|
24
|
-
env: listExecEnv(),
|
|
25
|
-
});
|
|
26
|
-
const trimmed = out.trim();
|
|
22
|
+
/** Strip leading npx/npm noise and parse the skills `list --json` array payload. */
|
|
23
|
+
export function parseSkillsListJsonStdout(stdout) {
|
|
24
|
+
const trimmed = stdout.trim();
|
|
27
25
|
if (!trimmed)
|
|
28
26
|
return [];
|
|
29
|
-
const
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
const start = trimmed.indexOf('[');
|
|
28
|
+
if (start < 0) {
|
|
29
|
+
throw new SyntaxError('skills list --json: no JSON array in stdout');
|
|
30
|
+
}
|
|
31
|
+
let end = trimmed.lastIndexOf(']');
|
|
32
|
+
let lastErr;
|
|
33
|
+
while (end > start) {
|
|
34
|
+
const slice = trimmed.slice(start, end + 1);
|
|
35
|
+
try {
|
|
36
|
+
const parsed = JSON.parse(slice);
|
|
37
|
+
if (!Array.isArray(parsed))
|
|
38
|
+
return [];
|
|
39
|
+
return parsed;
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
lastErr = err instanceof SyntaxError ? err : new SyntaxError(String(err));
|
|
43
|
+
end = trimmed.lastIndexOf(']', end - 1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
throw lastErr ?? new SyntaxError('skills list --json: could not parse stdout');
|
|
47
|
+
}
|
|
48
|
+
export function runSkillsListJson(args, cwd) {
|
|
49
|
+
const tmpDir = mkdtempSync(join(tmpdir(), 'optimus-skills-list-'));
|
|
50
|
+
const outPath = join(tmpDir, 'stdout.json');
|
|
51
|
+
const outFd = openSync(outPath, 'w');
|
|
52
|
+
try {
|
|
53
|
+
const child = spawnSync('npx', [npxPackageSpec(), 'list', ...args, '--json'], {
|
|
54
|
+
cwd,
|
|
55
|
+
timeout: LIST_TIMEOUT_MS,
|
|
56
|
+
env: listExecEnv(),
|
|
57
|
+
stdio: ['ignore', outFd, 'pipe'],
|
|
58
|
+
maxBuffer: LIST_STDERR_MAX_BUFFER,
|
|
59
|
+
});
|
|
60
|
+
if (child.error)
|
|
61
|
+
throw child.error;
|
|
62
|
+
if (child.status !== 0) {
|
|
63
|
+
const stderr = (child.stderr ?? '').toString().trim();
|
|
64
|
+
throw new Error(stderr || `npx skills list exited ${child.status ?? 'unknown'}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
closeSync(outFd);
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const out = readFileSync(outPath, 'utf8');
|
|
72
|
+
return parseSkillsListJsonStdout(out);
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
76
|
+
}
|
|
33
77
|
}
|
|
34
78
|
function normalizeListRows(rows, scope) {
|
|
35
79
|
const out = [];
|
|
@@ -60,39 +104,43 @@ export function formatSkillsListScopeForHookLog(scopeLabel, entries) {
|
|
|
60
104
|
.join(' | ');
|
|
61
105
|
return `skills_cli list ${scopeLabel}: ${entries.length} skill(s) — ${detail}`;
|
|
62
106
|
}
|
|
107
|
+
function runSkillsListForScope(scopeLabel, args, projectRoot, logLine) {
|
|
108
|
+
try {
|
|
109
|
+
return runSkillsListJson(args, projectRoot);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
113
|
+
logLine(`skills_cli: list ${scopeLabel} --json failed: ${msg}`);
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
63
117
|
export function collectSkillsCliInstalled(projectRoot, log) {
|
|
64
118
|
const logLine = (message) => {
|
|
65
119
|
log?.(message);
|
|
66
120
|
};
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
logLine('skills_cli_installed: not uploaded (no global or project skills)');
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
logLine(`skills_cli_installed: upload ${SKILLS_CLI_INSTALLED_PATH} global=${global.length} project=${project.length}`);
|
|
88
|
-
return {
|
|
89
|
-
file_type: SKILLS_CLI_FILE_TYPE,
|
|
90
|
-
file_path: SKILLS_CLI_INSTALLED_PATH,
|
|
91
|
-
raw_content: payload,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
catch (err) {
|
|
95
|
-
logLine(`skills_cli: list --json failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
121
|
+
const packageSpec = npxPackageSpec();
|
|
122
|
+
logLine(`skills_cli: npx ${packageSpec} — list -g --json && list --json (projectRoot=${projectRoot})`);
|
|
123
|
+
const globalRows = runSkillsListForScope('-g', ['-g'], projectRoot, logLine);
|
|
124
|
+
const projectRows = runSkillsListForScope('project', [], projectRoot, logLine);
|
|
125
|
+
const global = normalizeListRows(globalRows, 'global');
|
|
126
|
+
const project = normalizeListRows(projectRows, 'project');
|
|
127
|
+
logLine(formatSkillsListScopeForHookLog('-g', global));
|
|
128
|
+
logLine(formatSkillsListScopeForHookLog('project', project));
|
|
129
|
+
const payload = {
|
|
130
|
+
version: 1,
|
|
131
|
+
skills_cli_version: packageSpec,
|
|
132
|
+
generated_at: new Date().toISOString(),
|
|
133
|
+
global,
|
|
134
|
+
project,
|
|
135
|
+
};
|
|
136
|
+
if (payload.global.length === 0 && payload.project.length === 0) {
|
|
137
|
+
logLine('skills_cli_installed: not uploaded (no global or project skills)');
|
|
96
138
|
return null;
|
|
97
139
|
}
|
|
140
|
+
logLine(`skills_cli_installed: upload ${SKILLS_CLI_INSTALLED_PATH} global=${global.length} project=${project.length}`);
|
|
141
|
+
return {
|
|
142
|
+
file_type: SKILLS_CLI_FILE_TYPE,
|
|
143
|
+
file_path: SKILLS_CLI_INSTALLED_PATH,
|
|
144
|
+
raw_content: payload,
|
|
145
|
+
};
|
|
98
146
|
}
|