log-llm-config 0.1.2
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/README.md +33 -0
- package/dist/cli.js +172 -0
- package/dist/endpoint_client.js +75 -0
- package/dist/log_uuid.js +192 -0
- package/dist/log_uuid_helper.js +30 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
## log-llm-config
|
|
2
|
+
|
|
3
|
+
CLI helpers for Optimus Security startup workflows.
|
|
4
|
+
|
|
5
|
+
### Prerequisites
|
|
6
|
+
|
|
7
|
+
- Node.js 18+
|
|
8
|
+
- `OPTIMUS_ENDPOINT` defined in `optimus.env` (e.g. `http://localhost:8080/endpoint_security/`)
|
|
9
|
+
|
|
10
|
+
### Commands
|
|
11
|
+
|
|
12
|
+
#### `log_uuid`
|
|
13
|
+
|
|
14
|
+
Collects the machine hardware UUID (or uses the `OPTIMUS_HARDWARE_UUID` env variable), prints it, and POSTs a startup payload to `OPTIMUS_ENDPOINT/startup/`. If the server responds with a key, it stores it at `~/opt-ai-sec/management/auth_key.txt`.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
OPTIMUS_ENDPOINT=http://localhost:8080/endpoint_security/ \
|
|
18
|
+
npx log-llm-config log_uuid
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
#### `log-llm-config`
|
|
22
|
+
|
|
23
|
+
Legacy helper that logs local Optimus/Cursor configuration files to `configs.txt`.
|
|
24
|
+
|
|
25
|
+
### Development
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install
|
|
29
|
+
npm run build
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
> Proprietary package. Do not publish to public registries.
|
|
33
|
+
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const args = process.argv.slice(2);
|
|
3
|
+
if (args[0] === 'log_uuid') {
|
|
4
|
+
await import('./log_uuid.js');
|
|
5
|
+
process.exit(0);
|
|
6
|
+
}
|
|
7
|
+
const fileCategories = [
|
|
8
|
+
{
|
|
9
|
+
label: 'Cursor: mcp.json',
|
|
10
|
+
targets: ['./.cursor/mcp.json', '$HOME/.cursor/mcp.json']
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
label: 'Claude: .mcp.json',
|
|
14
|
+
targets: ['./mcp.json', './.mcp.json', '/Library/Application Support/ClaudeCode/managed-mcp.json']
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
label: 'Claude: settings.json',
|
|
18
|
+
targets: [
|
|
19
|
+
'./.claude/settings.json',
|
|
20
|
+
'./.claude/settings.local.json',
|
|
21
|
+
'$HOME/.claude/settings.json',
|
|
22
|
+
'/Library/Application Support/ClaudeCode/managed-settings.json'
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: 'Cursor: hooks.json',
|
|
27
|
+
targets: [
|
|
28
|
+
'./.cursor/hooks.json',
|
|
29
|
+
'$HOME/.cursor/hooks.json',
|
|
30
|
+
'/Library/Application Support/Cursor/hooks.json'
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
];
|
|
34
|
+
const sqliteCategories = [
|
|
35
|
+
{
|
|
36
|
+
label: 'Cursor: state.vscdb (composerState)',
|
|
37
|
+
dbPath: '$HOME/Library/Application Support/Cursor/User/globalStorage/state.vscdb',
|
|
38
|
+
table: 'ItemTable',
|
|
39
|
+
key: 'src.vs.platform.reactivestorage.browser.reactiveStorageServiceImpl.persistentStorage.applicationUser',
|
|
40
|
+
jsonPaths: [['composerState'], []]
|
|
41
|
+
}
|
|
42
|
+
];
|
|
43
|
+
const buildFileCategoryLines = (category) => [
|
|
44
|
+
`echo "===== ${category.label} ====="`,
|
|
45
|
+
'files=(',
|
|
46
|
+
...category.targets.map((target) => ` "${target}"`),
|
|
47
|
+
')',
|
|
48
|
+
'expanded_files=()',
|
|
49
|
+
'paths=()',
|
|
50
|
+
'display_paths=()',
|
|
51
|
+
'for f in "${files[@]}"; do',
|
|
52
|
+
' expanded=$(eval echo "$f")',
|
|
53
|
+
' expanded_dir=$(dirname "$expanded")',
|
|
54
|
+
' expanded_base=$(basename "$expanded")',
|
|
55
|
+
' abs_dir=$(cd "$expanded_dir" 2>/dev/null && pwd || echo "$expanded_dir")',
|
|
56
|
+
' abs_path="$abs_dir/$expanded_base"',
|
|
57
|
+
' expanded_files+=("$abs_path")',
|
|
58
|
+
' relative_path="$(to_relative "$abs_path")"',
|
|
59
|
+
' paths+=("$relative_path")',
|
|
60
|
+
' display_paths+=("$relative_path")',
|
|
61
|
+
'done',
|
|
62
|
+
'echo ""',
|
|
63
|
+
'if [ ${#paths[@]} -gt 0 ]; then',
|
|
64
|
+
' echo "Paths:"',
|
|
65
|
+
' printf "%s\\n" "${paths[@]}" | awk \'!seen[$0]++\' | while IFS= read -r dir; do',
|
|
66
|
+
' echo "$dir"',
|
|
67
|
+
' done',
|
|
68
|
+
' echo ""',
|
|
69
|
+
'else',
|
|
70
|
+
' echo "Paths: (none)"',
|
|
71
|
+
'fi',
|
|
72
|
+
'echo ""',
|
|
73
|
+
'for idx in "${!expanded_files[@]}"; do',
|
|
74
|
+
' expanded="${expanded_files[$idx]}"',
|
|
75
|
+
' display="${display_paths[$idx]}"',
|
|
76
|
+
' if [ -f "$expanded" ]; then',
|
|
77
|
+
` echo "===== ${category.label}: $display ====="`,
|
|
78
|
+
' cat "$expanded"',
|
|
79
|
+
' else',
|
|
80
|
+
` echo "===== ${category.label}: missing: $display ====="`,
|
|
81
|
+
' fi',
|
|
82
|
+
'done',
|
|
83
|
+
'echo ""'
|
|
84
|
+
];
|
|
85
|
+
const buildSqliteCategoryLines = (category) => {
|
|
86
|
+
const jsonPathsLiteral = JSON.stringify(category.jsonPaths);
|
|
87
|
+
return [
|
|
88
|
+
`echo "===== ${category.label} ====="`,
|
|
89
|
+
`db_path="${category.dbPath}"`,
|
|
90
|
+
'expanded=$(eval echo "$db_path")',
|
|
91
|
+
'if [ -f "$expanded" ]; then',
|
|
92
|
+
' if command -v sqlite3 >/dev/null 2>&1; then',
|
|
93
|
+
' echo "===== $expanded ====="',
|
|
94
|
+
` query_result=$(sqlite3 "$expanded" "SELECT value FROM ${category.table} WHERE key='${category.key}'")`,
|
|
95
|
+
' if [ -n "$query_result" ]; then',
|
|
96
|
+
` echo "===== key: ${category.key} ====="`,
|
|
97
|
+
` LOG_CONFIG_JSON_VALUE="$query_result" python3 - <<'PY'`,
|
|
98
|
+
'import json',
|
|
99
|
+
'import os',
|
|
100
|
+
'',
|
|
101
|
+
'raw = os.environ.get("LOG_CONFIG_JSON_VALUE", "")',
|
|
102
|
+
'if not raw:',
|
|
103
|
+
' print("{}")',
|
|
104
|
+
' raise SystemExit',
|
|
105
|
+
`paths = ${jsonPathsLiteral}`,
|
|
106
|
+
'try:',
|
|
107
|
+
' data = json.loads(raw)',
|
|
108
|
+
'except json.JSONDecodeError as exc:',
|
|
109
|
+
' print(f"failed to parse JSON: {exc}")',
|
|
110
|
+
' raise SystemExit',
|
|
111
|
+
'def walk(obj, path):',
|
|
112
|
+
' cur = obj',
|
|
113
|
+
' for segment in path:',
|
|
114
|
+
' if isinstance(cur, dict):',
|
|
115
|
+
' cur = cur.get(segment)',
|
|
116
|
+
' else:',
|
|
117
|
+
' return None',
|
|
118
|
+
' return cur',
|
|
119
|
+
'selected_value = None',
|
|
120
|
+
'selected_label = None',
|
|
121
|
+
'for path in paths:',
|
|
122
|
+
' value = walk(data, path)',
|
|
123
|
+
' if value is not None:',
|
|
124
|
+
' selected_value = value',
|
|
125
|
+
' selected_label = ".".join(path) if path else "root"',
|
|
126
|
+
' break',
|
|
127
|
+
'if selected_value is None:',
|
|
128
|
+
' print("{}")',
|
|
129
|
+
'else:',
|
|
130
|
+
' print(f"[path: {selected_label}]")',
|
|
131
|
+
' print(json.dumps(selected_value, indent=2, sort_keys=True))',
|
|
132
|
+
'PY',
|
|
133
|
+
' else',
|
|
134
|
+
` echo "===== key not found: ${category.key} ====="`,
|
|
135
|
+
' fi',
|
|
136
|
+
' else',
|
|
137
|
+
' echo "===== sqlite3 not found; skipping ====="',
|
|
138
|
+
' fi',
|
|
139
|
+
'else',
|
|
140
|
+
' echo "===== missing: $expanded ====="',
|
|
141
|
+
'fi',
|
|
142
|
+
'echo ""'
|
|
143
|
+
];
|
|
144
|
+
};
|
|
145
|
+
const scriptLines = [
|
|
146
|
+
'#!/usr/bin/env bash',
|
|
147
|
+
'set -euo pipefail',
|
|
148
|
+
'output_file="$(pwd)/configs.txt"',
|
|
149
|
+
': > "$output_file"',
|
|
150
|
+
'exec >"$output_file"',
|
|
151
|
+
'exec 2>&1',
|
|
152
|
+
'',
|
|
153
|
+
'repo_root="$(pwd)"',
|
|
154
|
+
'to_relative() {',
|
|
155
|
+
' local path="$1"',
|
|
156
|
+
' if [[ "$path" == "$repo_root"* ]]; then',
|
|
157
|
+
' local suffix="${path#"$repo_root"}"',
|
|
158
|
+
' if [ -z "$suffix" ]; then',
|
|
159
|
+
' echo "<project_root>"',
|
|
160
|
+
' else',
|
|
161
|
+
' echo "<project_root>${suffix}"',
|
|
162
|
+
' fi',
|
|
163
|
+
' else',
|
|
164
|
+
' echo "$path"',
|
|
165
|
+
' fi',
|
|
166
|
+
'}',
|
|
167
|
+
'',
|
|
168
|
+
...fileCategories.flatMap(buildFileCategoryLines),
|
|
169
|
+
...sqliteCategories.flatMap(buildSqliteCategoryLines)
|
|
170
|
+
];
|
|
171
|
+
console.log(scriptLines.join('\n'));
|
|
172
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import https from 'node:https';
|
|
3
|
+
import { URL } from 'node:url';
|
|
4
|
+
export const postStartupPayload = async (endpointUrl, body, timeoutMs = 5000) => {
|
|
5
|
+
const url = new URL(endpointUrl);
|
|
6
|
+
const isHttps = url.protocol === 'https:';
|
|
7
|
+
const payload = JSON.stringify(body);
|
|
8
|
+
console.log('Sending payload to endpoint:', endpointUrl, payload);
|
|
9
|
+
const requestOptions = {
|
|
10
|
+
hostname: url.hostname,
|
|
11
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
12
|
+
path: `${url.pathname}${url.search}`,
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: {
|
|
15
|
+
'Content-Type': 'application/json',
|
|
16
|
+
'Content-Length': Buffer.byteLength(payload).toString(),
|
|
17
|
+
},
|
|
18
|
+
timeout: timeoutMs,
|
|
19
|
+
};
|
|
20
|
+
const transport = isHttps ? https.request : http.request;
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const req = transport(requestOptions, (res) => {
|
|
23
|
+
let responseBody = '';
|
|
24
|
+
res.setEncoding('utf8');
|
|
25
|
+
res.on('data', (chunk) => {
|
|
26
|
+
responseBody += chunk;
|
|
27
|
+
});
|
|
28
|
+
res.on('end', () => {
|
|
29
|
+
if (!responseBody) {
|
|
30
|
+
resolve({ status: 'error', message: 'Empty response from endpoint' });
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
console.log('Raw endpoint response body:', responseBody);
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(responseBody);
|
|
36
|
+
resolve(parsed);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
reject(new Error(`Failed to parse endpoint response (${error.message}): ${responseBody}`));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
req.on('error', (error) => {
|
|
44
|
+
reject(error);
|
|
45
|
+
});
|
|
46
|
+
req.write(payload);
|
|
47
|
+
req.end();
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
export const classifyEndpointResponse = (response) => {
|
|
51
|
+
if (response.status === 'key_issued' && response.key) {
|
|
52
|
+
return {
|
|
53
|
+
branch: 'key_issued',
|
|
54
|
+
message: 'Server issued a new symmetric key; store it for future signatures.',
|
|
55
|
+
key: response.key,
|
|
56
|
+
keyId: response.key_id,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (response.status === 'accepted' && response.signature_valid) {
|
|
60
|
+
return {
|
|
61
|
+
branch: 'accepted',
|
|
62
|
+
message: 'Startup payload accepted and signature verified.',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (response.status === 'error' && response.error) {
|
|
66
|
+
return {
|
|
67
|
+
branch: 'error',
|
|
68
|
+
message: response.error,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
branch: 'error',
|
|
73
|
+
message: response.message || 'Endpoint returned an unexpected response.',
|
|
74
|
+
};
|
|
75
|
+
};
|
package/dist/log_uuid.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import { mkdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import crypto from 'node:crypto';
|
|
6
|
+
import { classifyEndpointResponse, postStartupPayload } from './endpoint_client.js';
|
|
7
|
+
const AUTH_KEY_RELATIVE_PATH = path.join('opt-ai-sec', 'management', 'auth_key.txt');
|
|
8
|
+
const OPTIMUS_ENV_FILENAME = 'optimus.env';
|
|
9
|
+
const STARTUP_ENDPOINT_PATH = 'startup/';
|
|
10
|
+
const readCommandOutput = (command) => {
|
|
11
|
+
try {
|
|
12
|
+
return execSync(command, {
|
|
13
|
+
encoding: 'utf8',
|
|
14
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
15
|
+
}).trim();
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const readHardwareUuidFromCommands = () => {
|
|
22
|
+
const ioregOutput = readCommandOutput('ioreg -rd1 -c IOPlatformExpertDevice');
|
|
23
|
+
const ioregMatch = ioregOutput.match(/"IOPlatformUUID"\\s*=\\s*"([^"]+)"/i);
|
|
24
|
+
if (ioregMatch && ioregMatch[1]) {
|
|
25
|
+
return ioregMatch[1];
|
|
26
|
+
}
|
|
27
|
+
const profilerOutput = readCommandOutput('system_profiler SPHardwareDataType');
|
|
28
|
+
const profilerMatch = profilerOutput.match(/Hardware UUID:\\s*([A-F0-9-]+)/i);
|
|
29
|
+
if (profilerMatch && profilerMatch[1]) {
|
|
30
|
+
return profilerMatch[1];
|
|
31
|
+
}
|
|
32
|
+
throw new Error('Unable to determine hardware UUID via ioreg or system_profiler.');
|
|
33
|
+
};
|
|
34
|
+
const resolveHardwareUuid = () => {
|
|
35
|
+
const envUuid = process.env.OPTIMUS_HARDWARE_UUID?.trim();
|
|
36
|
+
if (envUuid) {
|
|
37
|
+
return envUuid;
|
|
38
|
+
}
|
|
39
|
+
return readHardwareUuidFromCommands();
|
|
40
|
+
};
|
|
41
|
+
const getMetadata = () => ({
|
|
42
|
+
github_username: process.env.GITHUB_USER || process.env.GH_USER || '',
|
|
43
|
+
org_identifier: process.env.GITHUB_ORG || '',
|
|
44
|
+
repo_identifier: process.env.GITHUB_REPOSITORY || '',
|
|
45
|
+
branch: process.env.GITHUB_REF_NAME || process.env.BRANCH_NAME || '',
|
|
46
|
+
commit_sha: process.env.GITHUB_SHA || '',
|
|
47
|
+
});
|
|
48
|
+
const getAuthKeyPath = () => {
|
|
49
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
50
|
+
if (!homeDir) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return path.join(homeDir, AUTH_KEY_RELATIVE_PATH);
|
|
54
|
+
};
|
|
55
|
+
const writeAuthKey = (key, keyId, hardwareUuid) => {
|
|
56
|
+
const authPath = getAuthKeyPath();
|
|
57
|
+
if (!authPath) {
|
|
58
|
+
console.warn('Unable to determine home directory; cannot store auth key.');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const authDir = path.dirname(authPath);
|
|
62
|
+
mkdirSync(authDir, { recursive: true, mode: 0o700 });
|
|
63
|
+
const payload = {
|
|
64
|
+
key,
|
|
65
|
+
key_id: keyId ?? null,
|
|
66
|
+
hardware_uuid: hardwareUuid,
|
|
67
|
+
stored_at: new Date().toISOString(),
|
|
68
|
+
};
|
|
69
|
+
writeFileSync(authPath, JSON.stringify(payload, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
70
|
+
console.log(`Stored auth key at ${authPath}`);
|
|
71
|
+
};
|
|
72
|
+
const readStoredAuthKey = () => {
|
|
73
|
+
const authPath = getAuthKeyPath();
|
|
74
|
+
if (!authPath || !existsSync(authPath)) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const raw = readFileSync(authPath, 'utf8');
|
|
79
|
+
const parsed = JSON.parse(raw);
|
|
80
|
+
if (parsed?.key && parsed?.hardware_uuid) {
|
|
81
|
+
return parsed;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
console.warn(`Unable to read stored auth key: ${error.message}`);
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
};
|
|
89
|
+
const loadEndpointBase = () => {
|
|
90
|
+
const envPath = path.join(process.cwd(), OPTIMUS_ENV_FILENAME);
|
|
91
|
+
try {
|
|
92
|
+
const envContent = readFileSync(envPath, 'utf8');
|
|
93
|
+
const lines = envContent.split(/\r?\n/);
|
|
94
|
+
for (const line of lines) {
|
|
95
|
+
const trimmed = line.trim();
|
|
96
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const [key, ...rest] = trimmed.split('=');
|
|
100
|
+
if (key === 'OPTIMUS_ENDPOINT') {
|
|
101
|
+
const value = rest.join('=').trim();
|
|
102
|
+
if (value) {
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Ignore missing or unreadable optimus.env
|
|
110
|
+
}
|
|
111
|
+
return undefined;
|
|
112
|
+
};
|
|
113
|
+
const buildStartupEndpointUrl = (baseUrl) => {
|
|
114
|
+
const trimmed = baseUrl.replace(/\/+$/, '');
|
|
115
|
+
return `${trimmed}/${STARTUP_ENDPOINT_PATH}`;
|
|
116
|
+
};
|
|
117
|
+
const canonicalizeValue = (value) => {
|
|
118
|
+
if (Array.isArray(value)) {
|
|
119
|
+
return value.map(canonicalizeValue);
|
|
120
|
+
}
|
|
121
|
+
if (value && typeof value === 'object') {
|
|
122
|
+
const sortedKeys = Object.keys(value).sort();
|
|
123
|
+
const result = {};
|
|
124
|
+
for (const key of sortedKeys) {
|
|
125
|
+
result[key] = canonicalizeValue(value[key]);
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
return value;
|
|
130
|
+
};
|
|
131
|
+
const canonicalStringify = (value) => {
|
|
132
|
+
return JSON.stringify(canonicalizeValue(value));
|
|
133
|
+
};
|
|
134
|
+
const createSignature = (payloadSummary, keyHex) => {
|
|
135
|
+
const canonicalPayload = canonicalStringify(payloadSummary);
|
|
136
|
+
const keyBuffer = Buffer.from(keyHex, 'hex');
|
|
137
|
+
return crypto.createHmac('sha256', keyBuffer).update(canonicalPayload).digest('hex');
|
|
138
|
+
};
|
|
139
|
+
const maybeSendToEndpoint = async (hardwareUuid, timestamp) => {
|
|
140
|
+
const baseUrl = loadEndpointBase();
|
|
141
|
+
if (!baseUrl) {
|
|
142
|
+
console.log('OPTIMUS_ENDPOINT not set in optimus.env; skipping endpoint handshake.');
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const startupEndpointUrl = buildStartupEndpointUrl(baseUrl);
|
|
146
|
+
const payloadSummary = {
|
|
147
|
+
timestamp,
|
|
148
|
+
directory: process.cwd(),
|
|
149
|
+
hardware_uuid: hardwareUuid,
|
|
150
|
+
};
|
|
151
|
+
const requestBody = {
|
|
152
|
+
hardware_uuid: hardwareUuid,
|
|
153
|
+
payload: payloadSummary,
|
|
154
|
+
metadata: getMetadata(),
|
|
155
|
+
scan_timestamp: timestamp,
|
|
156
|
+
};
|
|
157
|
+
const storedKey = readStoredAuthKey();
|
|
158
|
+
const requestPayload = { ...requestBody };
|
|
159
|
+
if (storedKey && storedKey.key && storedKey.hardware_uuid === hardwareUuid) {
|
|
160
|
+
requestPayload.signature = createSignature(payloadSummary, storedKey.key);
|
|
161
|
+
console.log('Using stored auth key to sign startup payload.');
|
|
162
|
+
}
|
|
163
|
+
else if (storedKey && storedKey.hardware_uuid !== hardwareUuid) {
|
|
164
|
+
console.warn('Stored auth key hardware UUID mismatch; requesting new key from server.');
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
console.log('No stored auth key found; requesting new key from server.');
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const response = await postStartupPayload(startupEndpointUrl, requestPayload);
|
|
171
|
+
const result = classifyEndpointResponse(response);
|
|
172
|
+
console.log(result.message);
|
|
173
|
+
if (result.branch === 'key_issued' && result.key) {
|
|
174
|
+
writeAuthKey(result.key, result.keyId, hardwareUuid);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
console.error(`Failed to contact endpoint: ${error.message}`);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const main = async () => {
|
|
182
|
+
const timestamp = new Date().toISOString();
|
|
183
|
+
try {
|
|
184
|
+
const hardwareUuid = resolveHardwareUuid();
|
|
185
|
+
await maybeSendToEndpoint(hardwareUuid, timestamp);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error(`Unable to log hardware UUID: ${error.message}`);
|
|
189
|
+
process.exitCode = 1;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
void main();
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const buildLogUuidScriptLines = () => [
|
|
2
|
+
'#!/usr/bin/env bash',
|
|
3
|
+
'set -euo pipefail',
|
|
4
|
+
'',
|
|
5
|
+
'output_file="$(pwd)/uuid.txt"',
|
|
6
|
+
'timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")',
|
|
7
|
+
'if command -v ioreg >/dev/null 2>&1; then',
|
|
8
|
+
' uuid_value=$(ioreg -rd1 -c IOPlatformExpertDevice | awk \'/IOPlatformUUID/ {gsub(/\\"/, "", $3); print $3}\')',
|
|
9
|
+
'elif command -v system_profiler >/dev/null 2>&1; then',
|
|
10
|
+
' uuid_value=$(system_profiler SPHardwareDataType | awk \'/Hardware UUID/ {print $3}\')',
|
|
11
|
+
'else',
|
|
12
|
+
' echo "Unable to locate hardware UUID utilities (ioreg/system_profiler)" >&2',
|
|
13
|
+
' exit 1',
|
|
14
|
+
'fi',
|
|
15
|
+
'',
|
|
16
|
+
'if [ -z "$uuid_value" ]; then',
|
|
17
|
+
' echo "Hardware UUID could not be determined" >&2',
|
|
18
|
+
' exit 1',
|
|
19
|
+
'fi',
|
|
20
|
+
'',
|
|
21
|
+
'cat <<EOF | tee "$output_file"',
|
|
22
|
+
'===== Optimus UUID =====',
|
|
23
|
+
'Timestamp: $timestamp',
|
|
24
|
+
'Directory: $(pwd)',
|
|
25
|
+
'UUID: $uuid_value',
|
|
26
|
+
'EOF',
|
|
27
|
+
'',
|
|
28
|
+
'echo "UUID details saved to $output_file"'
|
|
29
|
+
];
|
|
30
|
+
export const renderLogUuidScript = () => buildLogUuidScriptLines().join('\n');
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "log-llm-config",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "CLI helpers for logging hardware UUIDs and posting startup payloads to Optimus Security.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"log-llm-config": "./dist/cli.js",
|
|
8
|
+
"log_uuid": "./dist/log_uuid.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc -p tsconfig.json",
|
|
12
|
+
"dev": "ts-node src/cli.ts",
|
|
13
|
+
"lint": "eslint \"src/**/*.ts\"",
|
|
14
|
+
"prepare": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"config",
|
|
19
|
+
"logging",
|
|
20
|
+
"npx"
|
|
21
|
+
],
|
|
22
|
+
"author": "",
|
|
23
|
+
"license": "UNLICENSED",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/optimuslabs-io/optimus-secure-fdn.git",
|
|
27
|
+
"directory": "npx_packages/log-llm-config"
|
|
28
|
+
},
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/optimuslabs-io/optimus-secure-fdn/issues"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/optimuslabs-io/optimus-secure-fdn/tree/main/npx_packages/log-llm-config#readme",
|
|
33
|
+
"files": [
|
|
34
|
+
"dist/**/*",
|
|
35
|
+
"README.md"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^20.11.30",
|
|
42
|
+
"ts-node": "^10.9.2",
|
|
43
|
+
"typescript": "^5.4.5"
|
|
44
|
+
}
|
|
45
|
+
}
|