log-llm-config 1.4.9 → 1.4.10
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/compliance_prompt_gate.js +17 -7
- package/dist/log_config_files/collection/config_collector.js +1 -1
- package/dist/log_config_files/readers/file_readers.js +1 -1
- package/dist/log_config_files/runtime/compliance_check.js +10 -16
- package/dist/log_config_files/runtime/remediation_sync.js +10 -3
- package/package.json +1 -1
|
@@ -39,6 +39,8 @@ function parseIde() {
|
|
|
39
39
|
return 'claude';
|
|
40
40
|
if (v === 'copilot')
|
|
41
41
|
return 'copilot';
|
|
42
|
+
if (v === 'opencode')
|
|
43
|
+
return 'opencode';
|
|
42
44
|
return 'cursor';
|
|
43
45
|
}
|
|
44
46
|
if (process.argv.includes('--claude'))
|
|
@@ -48,6 +50,8 @@ function parseIde() {
|
|
|
48
50
|
function defaultAgentFromIde(ide) {
|
|
49
51
|
if (ide === 'copilot')
|
|
50
52
|
return 'copilot';
|
|
53
|
+
if (ide === 'opencode')
|
|
54
|
+
return 'opencode';
|
|
51
55
|
return ide === 'claude' ? 'claude' : 'cursor';
|
|
52
56
|
}
|
|
53
57
|
function parseAgent(ide) {
|
|
@@ -60,13 +64,13 @@ function parseAgent(ide) {
|
|
|
60
64
|
return defaultAgentFromIde(ide);
|
|
61
65
|
}
|
|
62
66
|
function printAllow(ide) {
|
|
63
|
-
if (ide === 'claude' || ide === 'copilot')
|
|
67
|
+
if (ide === 'claude' || ide === 'copilot' || ide === 'opencode')
|
|
64
68
|
console.log('{}');
|
|
65
69
|
else
|
|
66
70
|
console.log(JSON.stringify({ continue: true }));
|
|
67
71
|
}
|
|
68
72
|
function printAllowWithAdvisory(ide, advisoryMessage) {
|
|
69
|
-
if (ide === 'claude' || ide === 'copilot') {
|
|
73
|
+
if (ide === 'claude' || ide === 'copilot' || ide === 'opencode') {
|
|
70
74
|
console.log(JSON.stringify({ __optimus_advisory: true, advisory_message: advisoryMessage }));
|
|
71
75
|
}
|
|
72
76
|
else {
|
|
@@ -80,7 +84,7 @@ function printAllowWithAdvisory(ide, advisoryMessage) {
|
|
|
80
84
|
function blockPayload(ide, violationMessage) {
|
|
81
85
|
const prefix = 'Prompt blocked by Optimus: ';
|
|
82
86
|
const text = prefix + violationMessage;
|
|
83
|
-
if (ide === 'claude' || ide === 'copilot') {
|
|
87
|
+
if (ide === 'claude' || ide === 'copilot' || ide === 'opencode') {
|
|
84
88
|
return JSON.stringify({ decision: 'block', reason: text, systemMessage: text });
|
|
85
89
|
}
|
|
86
90
|
return JSON.stringify({ continue: false, user_message: text });
|
|
@@ -138,6 +142,10 @@ export function formatClaudeAutofixDialog(appliedViolations) {
|
|
|
138
142
|
export function formatCopilotAutofixDialog(appliedViolations) {
|
|
139
143
|
return formatPreventiveAutofixDialog(appliedViolations, 'Copilot will now apply this policy to your environment.');
|
|
140
144
|
}
|
|
145
|
+
/** OpenCode dialog after enforced/preventive remediation is applied locally (terminal, no restart). */
|
|
146
|
+
export function formatOpenCodeAutofixDialog(appliedViolations) {
|
|
147
|
+
return formatPreventiveAutofixDialog(appliedViolations, 'OpenCode will now apply this policy to your environment.');
|
|
148
|
+
}
|
|
141
149
|
/** Cursor restart dialog after enforced/preventive remediation is applied locally. */
|
|
142
150
|
export function formatCursorRestartAutofixDialog(appliedViolations) {
|
|
143
151
|
return formatPreventiveAutofixDialog(appliedViolations, 'Cursor will now restart to apply this policy, and your context will be retained.');
|
|
@@ -270,7 +278,7 @@ export async function runCompliancePromptGate() {
|
|
|
270
278
|
// Claude / Copilot: JSON remediations are written immediately; merge/verify timing can still leave
|
|
271
279
|
// the in-process recheck red for the same UUID — allow in that case for immediate JSON agents.
|
|
272
280
|
const appliedUuids = new Set(appliedViolations.map((v) => v.uuid));
|
|
273
|
-
const claudeRecheckStaleAfterImmediateApply = (ide === 'claude' || ide === 'copilot') &&
|
|
281
|
+
const claudeRecheckStaleAfterImmediateApply = (ide === 'claude' || ide === 'copilot' || ide === 'opencode') &&
|
|
274
282
|
!recheckOk &&
|
|
275
283
|
recheck.violations.length > 0 &&
|
|
276
284
|
recheck.violations.every((v) => appliedUuids.has(v.uuid));
|
|
@@ -294,9 +302,11 @@ export async function runCompliancePromptGate() {
|
|
|
294
302
|
? formatClaudeAutofixDialog(appliedViolations)
|
|
295
303
|
: ide === 'copilot'
|
|
296
304
|
? formatCopilotAutofixDialog(appliedViolations)
|
|
297
|
-
:
|
|
298
|
-
|
|
299
|
-
|
|
305
|
+
: ide === 'opencode'
|
|
306
|
+
? formatOpenCodeAutofixDialog(appliedViolations)
|
|
307
|
+
: `Optimus Labs auto-fixed ${fixed} ${fixed === 1 ? 'policy violation' : 'policy violations'}:\n\n${appliedViolations
|
|
308
|
+
.map((v) => autofixDialogLine(v))
|
|
309
|
+
.join('\n')}${changePreviewSuffix}`;
|
|
300
310
|
const payload = { __optimus_autofix: true, autofix_message: autofixMessage };
|
|
301
311
|
if (restartCommands.length > 0)
|
|
302
312
|
payload.restart_commands = restartCommands;
|
|
@@ -69,7 +69,7 @@ function buildCollectionContext(patterns, projectRoot, home, homeRecurseSkipDirs
|
|
|
69
69
|
t.dir_subdir_filename = p.dir_subdir_filename;
|
|
70
70
|
if (p.dir_subdir_source)
|
|
71
71
|
t.dir_subdir_source = p.dir_subdir_source;
|
|
72
|
-
const key = `${t.path}\t${t.file_type}`;
|
|
72
|
+
const key = `${t.path}\t${t.file_type}\t${t.isDirectory ? 'dir' : 'file'}\t${t.dir_glob ?? ''}`;
|
|
73
73
|
if (!seenPaths.has(key)) {
|
|
74
74
|
seenPaths.add(key);
|
|
75
75
|
targets.push(t);
|
|
@@ -176,4 +176,4 @@ function readInstalledExtensions(extensionsCachePath) {
|
|
|
176
176
|
}
|
|
177
177
|
return extensions;
|
|
178
178
|
}
|
|
179
|
-
export { readMCPConfig, readJSONFile, readMarkdownFile, readInstalledExtensions };
|
|
179
|
+
export { readMCPConfig, readJSONFile, readMarkdownFile, readInstalledExtensions, parseJsonWithJsoncFallback, };
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { existsSync, readFileSync } from 'node:fs';
|
|
14
14
|
import { homedir } from 'node:os';
|
|
15
15
|
import { join } from 'node:path';
|
|
16
|
+
import { parseJsonWithJsoncFallback } from '../readers/file_readers.js';
|
|
16
17
|
import { mergeComposerShadowKeysFromReactiveBlob, readVscdbItemTableJson, } from '../readers/vscdb_reader.js';
|
|
17
18
|
import { readRemediationInstructionsFile, writeRemediationInstructionsFile, } from './management_storage.js';
|
|
18
19
|
import { resolveRemediationConfigPath } from './remediation_config_path.js';
|
|
@@ -294,12 +295,12 @@ function loadRemediationConfigJson(configFilePath, checkSettingPaths = []) {
|
|
|
294
295
|
}
|
|
295
296
|
if (!existsSync(resolvedPath))
|
|
296
297
|
return { ok: false, reason: 'file_not_found' };
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
298
|
+
// OpenCode configs are JSONC (comments / trailing commas); parse strict JSON first, then
|
|
299
|
+
// fall back to JSONC sanitization so comment-bearing files are not skipped as parse_error.
|
|
300
|
+
const parsed = parseJsonWithJsoncFallback(readFileSync(resolvedPath, 'utf8'));
|
|
301
|
+
if (parsed === null)
|
|
301
302
|
return { ok: false, reason: 'parse_error' };
|
|
302
|
-
}
|
|
303
|
+
return { ok: true, json: parsed };
|
|
303
304
|
}
|
|
304
305
|
/**
|
|
305
306
|
* Evaluate all checks in a secondary group against the group's config file.
|
|
@@ -678,12 +679,8 @@ export function applyAutofixViolations(violations, agent = 'cursor') {
|
|
|
678
679
|
updatedContent = itemKey ? (readVscdbItemTableJson(dbPath, itemKey) ?? undefined) : undefined;
|
|
679
680
|
}
|
|
680
681
|
else {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
}
|
|
684
|
-
catch {
|
|
685
|
-
updatedContent = undefined;
|
|
686
|
-
}
|
|
682
|
+
updatedContent =
|
|
683
|
+
parseJsonWithJsoncFallback(readFileSync(configPathForDisk, 'utf8')) ?? undefined;
|
|
687
684
|
}
|
|
688
685
|
if (updatedContent !== undefined) {
|
|
689
686
|
const fileType = (inst.file_type ?? '').trim();
|
|
@@ -906,11 +903,8 @@ export function uploadSatisfiedManifestConfigs(agent = 'cursor') {
|
|
|
906
903
|
continue;
|
|
907
904
|
}
|
|
908
905
|
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
rawContent = JSON.parse(readFileSync(diskPath, 'utf8'));
|
|
912
|
-
}
|
|
913
|
-
catch {
|
|
906
|
+
const rawContent = parseJsonWithJsoncFallback(readFileSync(diskPath, 'utf8'));
|
|
907
|
+
if (rawContent === null) {
|
|
914
908
|
hookRunLog(`satisfied_upload: could not read path=${diskPath} uuid=${entry.uuid}`);
|
|
915
909
|
continue;
|
|
916
910
|
}
|
|
@@ -3,6 +3,7 @@ import { delimiter, dirname, join } from 'node:path';
|
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { execFileSync } from 'node:child_process';
|
|
5
5
|
import { executeBody } from '../../endpoint_client/http_transport.js';
|
|
6
|
+
import { parseJsonWithJsoncFallback } from '../readers/file_readers.js';
|
|
6
7
|
import { complianceRunnerDiag, hookRunLog, logRemediationApplyFailure } from './hook_logger.js';
|
|
7
8
|
import { atomicWriteJson, getDeferredVscdbApplyPath, getFileCollectionVscdbContractPath, getRemediationInstructionsPath, readRemediationInstructionsFile, writeRemediationInstructionsFile, } from './management_storage.js';
|
|
8
9
|
import { readStoredAuthKey } from '../auth/auth_key_store.js';
|
|
@@ -1396,10 +1397,16 @@ export function enforceRemediation(instruction) {
|
|
|
1396
1397
|
}
|
|
1397
1398
|
let configJson = {};
|
|
1398
1399
|
if (existsSync(inst.config_file_path)) {
|
|
1399
|
-
|
|
1400
|
-
|
|
1400
|
+
// OpenCode configs are JSONC; parse strict JSON first, then JSONC fallback so a
|
|
1401
|
+
// comment-bearing opencode.json(c) keeps its existing settings instead of being reset to
|
|
1402
|
+
// {} on a parse failure. NOTE: the write-back below is JSON.stringify, so comments and
|
|
1403
|
+
// trailing commas in the original file are not preserved (same as every other agent's
|
|
1404
|
+
// JSON config) — only the key/value settings are retained and patched.
|
|
1405
|
+
const parsed = parseJsonWithJsoncFallback(readFileSync(inst.config_file_path, 'utf8'));
|
|
1406
|
+
if (parsed !== null) {
|
|
1407
|
+
configJson = parsed;
|
|
1401
1408
|
}
|
|
1402
|
-
|
|
1409
|
+
else {
|
|
1403
1410
|
hookRunLog(`remediation_enforce: could not parse existing file, starting fresh uuid=${inst.uuid}`);
|
|
1404
1411
|
}
|
|
1405
1412
|
}
|