log-llm-config 1.0.30 → 1.0.31

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,10 +1,7 @@
1
1
  import { executeBody } from './http_transport.js';
2
2
  export const postStartupPayload = async (endpointUrl, body, timeoutMs = 5000) => {
3
3
  const payload = JSON.stringify(body);
4
- console.log('Sending payload to endpoint:', endpointUrl, payload);
5
- const { statusCode, statusMessage, headers, body: responseBody } = await executeBody(endpointUrl, 'POST', payload, timeoutMs);
6
- console.log(`HTTP ${statusCode} ${statusMessage}`);
7
- console.log('Response headers:', headers);
4
+ const { statusCode, statusMessage, body: responseBody } = await executeBody(endpointUrl, 'POST', payload, timeoutMs);
8
5
  if (statusCode >= 400) {
9
6
  const msg = statusCode === 413
10
7
  ? `413 Request Entity Too Large: ${responseBody.substring(0, 200)}`
@@ -13,7 +10,6 @@ export const postStartupPayload = async (endpointUrl, body, timeoutMs = 5000) =>
13
10
  }
14
11
  if (!responseBody)
15
12
  return { status: 'error', message: 'Empty response from endpoint' };
16
- console.log('Raw endpoint response body:', responseBody);
17
13
  try {
18
14
  return JSON.parse(responseBody);
19
15
  }
@@ -1,4 +1,5 @@
1
- import { existsSync, statSync } from 'node:fs';
1
+ import { existsSync, statSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
2
3
  import { homedir } from 'node:os';
3
4
  import { readJSONFile, readMCPConfig, readMarkdownFile, readInstalledExtensions } from '../readers/file_readers.js';
4
5
  import { getExtensionsCachePath, getExtensionsCacheInstalledSuffix, getVscdbPath } from '../paths/path_constants_helpers.js';
@@ -46,9 +47,18 @@ function readContentByFormat(path, format) {
46
47
  }
47
48
  function collectRegularFileEntry(t, enrichByFileType) {
48
49
  const format = t.content_format || 'json';
49
- const content = readContentByFormat(t.path, format);
50
+ let content = readContentByFormat(t.path, format);
50
51
  if (content === null)
51
52
  return null;
53
+ // For JSON-format files, backend/policy engine expect raw_content to be the parsed object (e.g. permissions.allow)
54
+ if (format === 'json' && typeof content === 'string') {
55
+ try {
56
+ content = JSON.parse(content);
57
+ }
58
+ catch {
59
+ // leave as string if parse fails
60
+ }
61
+ }
52
62
  let raw;
53
63
  if (t.raw_key && typeof content === 'string') {
54
64
  raw = { [t.raw_key]: content };
@@ -81,7 +91,20 @@ function handleInstalledExtensions(handledSpecialPaths, configFiles, extensionsC
81
91
  configFiles.push({ file_type: 'cursor_extensions', file_path: `${extensionsCachePath}${suffix}`, raw_content: { installedExtensions: installed, source: 'extensions.user.cache', extracted_at: new Date().toISOString() } });
82
92
  }
83
93
  function collectConfigFilesFromPatterns(patterns, projectRoot, onProgress, options) {
84
- const home = homedir();
94
+ // Prefer process.env.HOME so hook invoker (e.g. Cursor) can pass real home when it differs from os.homedir()
95
+ const home = (process.env.HOME && process.env.HOME.trim()) || homedir();
96
+ try {
97
+ const claudeSettingsPath = join(home, '.claude', 'settings.json');
98
+ writeFileSync(join(process.cwd(), '.optimus-home-debug.json'), JSON.stringify({
99
+ process_env_HOME: process.env.HOME ?? null,
100
+ homedir: homedir(),
101
+ resolved_home: home,
102
+ claude_settings_path: claudeSettingsPath,
103
+ claude_settings_exists: existsSync(claudeSettingsPath),
104
+ cwd: process.cwd(),
105
+ }, null, 2));
106
+ }
107
+ catch (_) { }
85
108
  const pathConstants = options?.client_path_constants;
86
109
  if (!pathConstants)
87
110
  throw new Error('client_path_constants required from API response but not provided');
@@ -32,7 +32,10 @@ function collectDirectoryEntries(t) {
32
32
  if (!entry.isFile() || !matchName(entry.name))
33
33
  continue;
34
34
  const fullPath = join(t.path, entry.name);
35
- const content = readMarkdownFile(fullPath) ?? readJSONFile(fullPath);
35
+ // Prefer parsed JSON for .json files so backend/policy engine see top-level keys (e.g. permissions.allow)
36
+ const content = entry.name.endsWith('.json')
37
+ ? readJSONFile(fullPath) ?? readMarkdownFile(fullPath)
38
+ : readMarkdownFile(fullPath) ?? readJSONFile(fullPath);
36
39
  if (content !== null) {
37
40
  const raw = typeof content === 'string' ? { content, source: 'file' } : content;
38
41
  results.push({ file_type: t.file_type, file_path: fullPath, raw_content: raw });
@@ -118,6 +118,11 @@ async function main() {
118
118
  const collectMs = Date.now() - collectStartMs;
119
119
  const fileTypes = [...new Set(configFiles.map((c) => c.file_type))].join(', ');
120
120
  hookRunLog(`collected ${configFiles.length} config file(s) collect_ms=${collectMs} file_types=${fileTypes}`);
121
+ const claudeSettings = configFiles.filter((c) => c.file_type === 'claude_settings');
122
+ if (claudeSettings.length > 0)
123
+ hookRunLog(`claude_settings in batch: ${claudeSettings.length} path(s): ${claudeSettings.map((c) => c.file_path).join(', ')}`);
124
+ else
125
+ hookRunLog(`claude_settings in batch: 0 (none collected)`);
121
126
  if (configFiles.length === 0) {
122
127
  hookRunLog('no config files found, exiting');
123
128
  process.exit(0);
@@ -103,13 +103,6 @@ async function sendConfigFilesBatch(configFiles, hardwareUuid, authKey, hookRequ
103
103
  catch (error) {
104
104
  const errorMessage = error instanceof Error ? error.message : String(error);
105
105
  hookRunLog(`batch chunk error: ${errorMessage}`);
106
- const is413 = errorMessage.includes('413') || errorMessage.includes('Request Entity Too Large') || errorMessage.includes('Entity Too Large');
107
- if (is413 && chunk.length > 1) {
108
- hookRunLog(`413 error, splitting ${chunk.length} files`);
109
- splitChunk(chunks, chunkIndex);
110
- // Don't increment — retry with the smaller first half
111
- continue;
112
- }
113
106
  totals.failed += chunk.length;
114
107
  }
115
108
  chunkIndex++;
@@ -1,27 +1,14 @@
1
1
  import crypto from 'node:crypto';
2
- /** Recursively sort object keys to produce a canonical representation. */
3
- function canonicalizeValue(value) {
4
- if (Array.isArray(value)) {
5
- return value.map(canonicalizeValue);
6
- }
7
- if (value && typeof value === 'object') {
8
- const sortedKeys = Object.keys(value).sort();
9
- const result = {};
10
- for (const key of sortedKeys) {
11
- result[key] = canonicalizeValue(value[key]);
12
- }
13
- return result;
14
- }
15
- return value;
16
- }
17
- /** Canonicalize payload (sort keys, compact JSON) to match server. */
2
+ import canonicalize from 'canonicalize';
3
+ /** RFC 8785 canonical JSON — must match server's rfc8785.dumps() exactly. */
18
4
  function canonicalizePayload(payload) {
19
- return JSON.stringify(canonicalizeValue(payload));
5
+ const out = canonicalize(payload);
6
+ return out ?? '{}';
20
7
  }
21
- /** Create HMAC-SHA256 signature for a payload. */
8
+ /** Create HMAC-SHA256 signature for a payload (canonicalize then sign). */
22
9
  function createSignature(payload, keyHex) {
23
10
  const canonicalPayload = canonicalizePayload(payload);
24
11
  const keyBuffer = Buffer.from(keyHex, 'hex');
25
12
  return crypto.createHmac('sha256', keyBuffer).update(canonicalPayload).digest('hex');
26
13
  }
27
- export { canonicalizeValue, canonicalizePayload, createSignature };
14
+ export { canonicalizePayload, createSignature };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "log-llm-config",
3
- "version": "1.0.30",
3
+ "version": "1.0.31",
4
4
  "description": "CLI helpers for logging hardware UUIDs and posting startup payloads to Optimus Security.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -49,6 +49,7 @@
49
49
  "vitest": "^4.0.15"
50
50
  },
51
51
  "dependencies": {
52
- "axios": "^1.7.9"
52
+ "axios": "^1.7.9",
53
+ "canonicalize": "^2.1.0"
53
54
  }
54
- }
55
+ }