log-llm-config-staging 1.3.90 → 1.3.93

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.
@@ -25,6 +25,27 @@ function querySqlite(dbPath, key) {
25
25
  stdio: ['pipe', 'pipe', 'pipe'],
26
26
  }).trim();
27
27
  }
28
+ /**
29
+ * Cursor keeps live agent settings (e.g. yoloCommandAllowlist) on nested composerState inside
30
+ * the reactive blob while the legacy ItemTable `composerState` row is often stale/partial.
31
+ * Merge nested fields so compliance/autofix match policy scans on uploaded reactive data.
32
+ */
33
+ function mergeLegacyComposerStateWithReactiveNested(dbPath, reactiveKey, legacyComposerState) {
34
+ try {
35
+ const reactive = querySqlite(dbPath, reactiveKey);
36
+ if (!reactive)
37
+ return legacyComposerState;
38
+ const root = JSON.parse(reactive);
39
+ const nested = root.composerState;
40
+ if (nested === null || typeof nested !== 'object' || Array.isArray(nested)) {
41
+ return legacyComposerState;
42
+ }
43
+ return { ...legacyComposerState, ...nested };
44
+ }
45
+ catch {
46
+ return legacyComposerState;
47
+ }
48
+ }
28
49
  /**
29
50
  * Fallback if the API omits vscdb_composer_contract (older server): derive from vscdb_read_queries.
30
51
  */
@@ -113,6 +134,10 @@ export function readVscdbItemTableJson(dbPath, itemKey) {
113
134
  }
114
135
  const parsed = JSON.parse(raw);
115
136
  if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
137
+ if (itemKey === 'composerState' && reactiveKey) {
138
+ const merged = mergeLegacyComposerStateWithReactiveNested(dbPath, reactiveKey, parsed);
139
+ return { [itemKey]: merged };
140
+ }
116
141
  return { [itemKey]: parsed };
117
142
  }
118
143
  // Bare JSON arrays (e.g. cursor/disabledMcpServers) — wrap under itemKey so ops-based
@@ -39,6 +39,8 @@ export function normalizeAgentToken(raw) {
39
39
  return 'claude';
40
40
  if (s === 'cursor')
41
41
  return 'cursor';
42
+ if (s === 'copilot')
43
+ return 'copilot';
42
44
  return '';
43
45
  }
44
46
  function currentAgentFromEnv() {
@@ -46,9 +48,13 @@ function currentAgentFromEnv() {
46
48
  const override = normalizeAgentToken(process.env.OPTIMUS_AGENT);
47
49
  if (override)
48
50
  return override;
49
- // Backwards-compatible: hook wrappers set OPTIMUS_HOOK_TYPE to cursor|claude.
51
+ // Backwards-compatible: hook wrappers set OPTIMUS_HOOK_TYPE to cursor|claude|copilot.
50
52
  const hookType = normalizeAgentToken(process.env.OPTIMUS_HOOK_TYPE);
51
- return hookType === 'cursor' ? 'cursor' : 'claude';
53
+ if (hookType === 'cursor')
54
+ return 'cursor';
55
+ if (hookType === 'copilot')
56
+ return 'copilot';
57
+ return 'claude';
52
58
  }
53
59
  function targetsCurrentAgent(entry, agent) {
54
60
  // Prefer top-level manifest field; fall back to embedded fix payload for older local files.
@@ -91,7 +91,7 @@ async function addSensitivePathsAudit(endpointBase, configFiles) {
91
91
  }
92
92
  async function sendAllConfigFiles(configFiles, worktreeReport, hardwareUuid, authKey) {
93
93
  const hookTypeRaw = (process.env.OPTIMUS_HOOK_TYPE || 'claude').toLowerCase();
94
- const hookType = hookTypeRaw === 'cursor' ? 'cursor' : 'claude';
94
+ const hookType = hookTypeRaw === 'cursor' ? 'cursor' : hookTypeRaw === 'copilot' ? 'copilot' : 'claude';
95
95
  const manifest = configFiles.map((c) => canonicalCursorUserStateVscdbPath(c.file_path));
96
96
  const workspaceRepo = ensureWorkspaceRepoEnv(manifest);
97
97
  const hookRequestId = await sendHookRequestCreate(hardwareUuid, authKey, hookType, workspaceRepo);
@@ -132,7 +132,8 @@ async function main() {
132
132
  const endpointBase = loadEndpointBase();
133
133
  hookRunLog(`start endpoint=${endpointBase} hardware_uuid=${hardwareUuid}`);
134
134
  hookRunLog(`endpoint_source=${getEndpointSource()} cwd=${process.cwd()}`);
135
- hookRunLog(`env: OPTIMUS_HOOK_TYPE=${process.env.OPTIMUS_HOOK_TYPE ? 'set' : 'unset'} OPTIMUS_PROJECT_DIR=${process.env.OPTIMUS_PROJECT_DIR ? 'set' : 'unset'} OPTIMUS_WORKSPACE_REPO=${process.env.OPTIMUS_WORKSPACE_REPO ? 'set' : 'unset'} OPTIMUS_ENDPOINT=${process.env.OPTIMUS_ENDPOINT ? 'set' : 'unset'}`);
135
+ const envForLog = (v) => (v && v.trim() ? v.trim() : 'unset');
136
+ hookRunLog(`env: OPTIMUS_HOOK_TYPE=${envForLog(process.env.OPTIMUS_HOOK_TYPE)} OPTIMUS_PROJECT_DIR=${envForLog(process.env.OPTIMUS_PROJECT_DIR)} OPTIMUS_WORKSPACE_REPO=${envForLog(process.env.OPTIMUS_WORKSPACE_REPO)} OPTIMUS_ENDPOINT=${envForLog(process.env.OPTIMUS_ENDPOINT)}`);
136
137
  let authKey;
137
138
  try {
138
139
  authKey = await ensureAuthentication(hardwareUuid);
@@ -645,6 +645,35 @@ function mergeSqliteOpIntoJson(currentJson, sqliteOp) {
645
645
  currentJson[key] = arr;
646
646
  return;
647
647
  }
648
+ if (sqliteOp.array_remove?.length) {
649
+ const jp = (sqliteOp.json_path ?? '').trim();
650
+ const removeSet = new Set(sqliteOp.array_remove.map((v) => String(v)));
651
+ if (!jp) {
652
+ const key = sqliteOp.target_key;
653
+ const existing = currentJson[key];
654
+ const arr = Array.isArray(existing) ? [...existing] : [];
655
+ currentJson[key] = arr.filter((x) => !removeSet.has(String(x)));
656
+ return;
657
+ }
658
+ const parts = jp.split('.').filter(Boolean);
659
+ if (parts.length === 0)
660
+ return;
661
+ let node = currentJson;
662
+ for (const p of parts.slice(0, -1)) {
663
+ if (node == null || typeof node !== 'object' || Array.isArray(node))
664
+ return;
665
+ node = node[p];
666
+ }
667
+ if (node == null || typeof node !== 'object' || Array.isArray(node))
668
+ return;
669
+ const container = node;
670
+ const arrayKey = parts[parts.length - 1];
671
+ const existing = container[arrayKey];
672
+ if (!Array.isArray(existing))
673
+ return;
674
+ container[arrayKey] = existing.filter((x) => !removeSet.has(String(x)));
675
+ return;
676
+ }
648
677
  const where = sqliteOp.array_item_where;
649
678
  const jp = sqliteOp.json_path ?? '';
650
679
  if (where && typeof where === 'object' && Object.keys(where).length > 0 && jp) {
@@ -32,10 +32,16 @@ function currentAgentFromEnv() {
32
32
  const override = normalizeAgentToken(process.env.OPTIMUS_AGENT);
33
33
  if (override === 'cursor')
34
34
  return 'cursor';
35
+ if (override === 'copilot')
36
+ return 'copilot';
35
37
  if (override === 'claude' || override === 'claude_desktop')
36
38
  return 'claude';
37
39
  const hookType = normalizeAgentToken(process.env.OPTIMUS_HOOK_TYPE);
38
- return hookType === 'cursor' ? 'cursor' : 'claude';
40
+ if (hookType === 'cursor')
41
+ return 'cursor';
42
+ if (hookType === 'copilot')
43
+ return 'copilot';
44
+ return 'claude';
39
45
  }
40
46
  /** Spawn each trusted command detached (same pattern as former compliance_prompt_gate fireRestartCommands). */
41
47
  export function executeTrustedRestartCommands(commands) {
@@ -7,21 +7,25 @@ export function managementEnvPath() {
7
7
  return null;
8
8
  return path.join(home, 'opt-ai-sec', 'management', 'optimus_dev.env');
9
9
  }
10
- /** Resolve optimus environment label (file wins over shell OPTIMUS_ENVIRONMENT). */
10
+ /**
11
+ * Staging package: default staging when no management file (promotion rewrites to production).
12
+ * File present → environment= line in file, then OPTIMUS_ENVIRONMENT, then staging.
13
+ */
11
14
  export function readEnvironment() {
12
15
  const envPath = managementEnvPath();
13
16
  if (!envPath) {
14
17
  return 'staging';
15
18
  }
19
+ let content;
16
20
  try {
17
- const content = readFileSync(envPath, 'utf8');
18
- const match = content.match(/^(?:environment|ENVIRONMENT|OPTIMUS_ENVIRONMENT)\s*=\s*(.+)/m);
19
- if (match)
20
- return match[1].trim().toLowerCase();
21
+ content = readFileSync(envPath, 'utf8');
21
22
  }
22
23
  catch {
23
24
  return 'staging';
24
25
  }
26
+ const match = content?.match(/^(?:environment|ENVIRONMENT|OPTIMUS_ENVIRONMENT)\s*=\s*(.+)/m);
27
+ if (match)
28
+ return match[1].trim().toLowerCase();
25
29
  const fromEnv = process.env.OPTIMUS_ENVIRONMENT?.trim().toLowerCase();
26
30
  if (fromEnv)
27
31
  return fromEnv;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "log-llm-config-staging",
3
- "version": "1.3.90",
3
+ "version": "1.3.93",
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,6 @@
58
58
  "dependencies": {
59
59
  "axios": "^1.15.2",
60
60
  "canonicalize": "^2.1.0",
61
- "optimus-tofu-staging": "^0.1.16"
61
+ "optimus-tofu-staging": "^0.1.17"
62
62
  }
63
63
  }