@optimuslabs/harness-map-staging 1.5.16

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.
Files changed (88) hide show
  1. package/README.md +46 -0
  2. package/dist/apply_deferred_vscdb.js +38 -0
  3. package/dist/bootstrap_constants.js +15 -0
  4. package/dist/cli/bash_script_generator.js +95 -0
  5. package/dist/cli.js +144 -0
  6. package/dist/cli_invocation_match.js +30 -0
  7. package/dist/compliance_check_runner.js +57 -0
  8. package/dist/compliance_prompt_gate.js +396 -0
  9. package/dist/dialog_prefs_cli.js +109 -0
  10. package/dist/endpoint_client/compress_payload.js +25 -0
  11. package/dist/endpoint_client/http_transport.js +90 -0
  12. package/dist/endpoint_client/index.js +3 -0
  13. package/dist/endpoint_client/registry_api.js +41 -0
  14. package/dist/endpoint_client/startup_api.js +44 -0
  15. package/dist/endpoint_client/types.js +4 -0
  16. package/dist/execute_trusted_restarts.js +60 -0
  17. package/dist/log_config_files/auth/auth_flow.js +22 -0
  18. package/dist/log_config_files/auth/auth_key_store.js +14 -0
  19. package/dist/log_config_files/collection/claude_desktop_extensions_collector.js +248 -0
  20. package/dist/log_config_files/collection/claude_known_projects_collector.js +42 -0
  21. package/dist/log_config_files/collection/claude_token_usage_collector.js +203 -0
  22. package/dist/log_config_files/collection/codex_token_usage_collector.js +342 -0
  23. package/dist/log_config_files/collection/config_collector.js +245 -0
  24. package/dist/log_config_files/collection/copilot_token_usage_collector.js +167 -0
  25. package/dist/log_config_files/collection/cowork_desktop_version_collector.js +60 -0
  26. package/dist/log_config_files/collection/cowork_session_whitelist.js +89 -0
  27. package/dist/log_config_files/collection/cursor_project_mcp_collector.js +230 -0
  28. package/dist/log_config_files/collection/cursor_project_workspace_activity.js +73 -0
  29. package/dist/log_config_files/collection/cursor_token_usage_collector.js +192 -0
  30. package/dist/log_config_files/collection/directory_collector.js +126 -0
  31. package/dist/log_config_files/collection/enrichment_helpers.js +53 -0
  32. package/dist/log_config_files/collection/ensure_cursor_user_settings_snapshot.js +50 -0
  33. package/dist/log_config_files/collection/file_type_rules.js +47 -0
  34. package/dist/log_config_files/collection/grok_cli_version_collector.js +53 -0
  35. package/dist/log_config_files/collection/hermes_token_usage_collector.js +241 -0
  36. package/dist/log_config_files/collection/mcp_tool_collector.js +106 -0
  37. package/dist/log_config_files/collection/metadata_merge.js +43 -0
  38. package/dist/log_config_files/collection/openclaw_helpers.js +55 -0
  39. package/dist/log_config_files/collection/openclaw_token_usage_collector.js +223 -0
  40. package/dist/log_config_files/collection/opencode_token_usage_collector.js +177 -0
  41. package/dist/log_config_files/collection/pi_token_usage_collector.js +239 -0
  42. package/dist/log_config_files/collection/plugin_collector.js +223 -0
  43. package/dist/log_config_files/collection/plugin_version_helpers.js +37 -0
  44. package/dist/log_config_files/collection/skills_cli_collector.js +162 -0
  45. package/dist/log_config_files/index.js +19 -0
  46. package/dist/log_config_files/paths/path_constants_helpers.js +71 -0
  47. package/dist/log_config_files/paths/pattern_resolver.js +326 -0
  48. package/dist/log_config_files/readers/composer_shadow_merge.js +67 -0
  49. package/dist/log_config_files/readers/cursor_shadow_merge_ruleset.js +46 -0
  50. package/dist/log_config_files/readers/file_readers.js +196 -0
  51. package/dist/log_config_files/readers/vscdb_config_builder.js +148 -0
  52. package/dist/log_config_files/readers/vscdb_reactive_storage.js +62 -0
  53. package/dist/log_config_files/readers/vscdb_reader.js +337 -0
  54. package/dist/log_config_files/runtime/client_event_reporter.js +347 -0
  55. package/dist/log_config_files/runtime/compliance_check.js +1013 -0
  56. package/dist/log_config_files/runtime/compliance_session_log.js +378 -0
  57. package/dist/log_config_files/runtime/dialog_preferences.js +31 -0
  58. package/dist/log_config_files/runtime/hardware_uuid.js +36 -0
  59. package/dist/log_config_files/runtime/hook_logger.js +267 -0
  60. package/dist/log_config_files/runtime/hook_type_for_request.js +30 -0
  61. package/dist/log_config_files/runtime/log_metadata.js +122 -0
  62. package/dist/log_config_files/runtime/main_runner.js +376 -0
  63. package/dist/log_config_files/runtime/management_storage.js +144 -0
  64. package/dist/log_config_files/runtime/ops_target_path.js +18 -0
  65. package/dist/log_config_files/runtime/remediation_apply_tracking.js +152 -0
  66. package/dist/log_config_files/runtime/remediation_config_path.js +140 -0
  67. package/dist/log_config_files/runtime/remediation_sync.js +1705 -0
  68. package/dist/log_config_files/runtime/secret_regex_scan.js +126 -0
  69. package/dist/log_config_files/runtime/sqlite_binary.js +124 -0
  70. package/dist/log_config_files/runtime/trusted_restarts.js +113 -0
  71. package/dist/log_config_files/runtime/workspace_repo.js +170 -0
  72. package/dist/log_config_files/runtime/worktree_absent.js +50 -0
  73. package/dist/log_config_files/runtime/worktree_scanner.js +137 -0
  74. package/dist/log_config_files/sender/batch_sender.js +283 -0
  75. package/dist/log_config_files/sender/endpoint_config.js +24 -0
  76. package/dist/log_config_files/sender/signing.js +1 -0
  77. package/dist/log_sensitive_paths_audit.js +89 -0
  78. package/dist/log_uuid/auth_key_store.js +37 -0
  79. package/dist/log_uuid/hardware_uuid.js +35 -0
  80. package/dist/log_uuid/index.js +11 -0
  81. package/dist/log_uuid/log_uuid_helper.js +30 -0
  82. package/dist/log_uuid/startup_sender.js +131 -0
  83. package/dist/log_uuid/user_profile.js +192 -0
  84. package/dist/remediation_change_preview.js +182 -0
  85. package/dist/tofu.js +22 -0
  86. package/dist/tofu_environment.js +36 -0
  87. package/dist/types/config_file_types.js +1 -0
  88. package/package.json +67 -0
@@ -0,0 +1,378 @@
1
+ import { existsSync, mkdirSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync, appendFileSync, readFileSync, } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { OPTIMUSLABS_LOGS_REL, LOG_GROUP_COMPLIANCE } from '../../bootstrap_constants.js';
5
+ import { buildLogMetadataBlock } from './log_metadata.js';
6
+ const COMPLIANCE_SUBDIR = LOG_GROUP_COMPLIANCE;
7
+ const TIER_NOOP = 'noop';
8
+ const TIER_SUCCESS = 'success';
9
+ const TIER_FAILURES = 'failures';
10
+ const TIER_IN_PROCESS = 'in_process';
11
+ const TIER_DIRS = [TIER_NOOP, TIER_SUCCESS, TIER_FAILURES];
12
+ /** Server upload contract still uses "error" for failed remediation sessions. */
13
+ const UPLOAD_OUTCOME_ERROR = 'error';
14
+ const NOOP_MAX_AGE_MS = 30 * 60 * 1000;
15
+ const SUCCESS_ERROR_MAX_AGE_MS = 24 * 60 * 60 * 1000;
16
+ /** In-flight logs in compliance/in_process/ before finalize. */
17
+ const INFLIGHT_MAX_AGE_MS = 2 * 60 * 60 * 1000;
18
+ const LATEST_POINTER = 'latest';
19
+ const MAX_SESSION_LOG_UPLOAD_BYTES = 5 * 1024 * 1024;
20
+ let activeComplianceLogPath = null;
21
+ export function parseComplianceLogArg(argv) {
22
+ const eq = argv.find((a) => a.startsWith('--compliance-log='));
23
+ if (eq) {
24
+ const v = eq.slice('--compliance-log='.length).trim();
25
+ return v || null;
26
+ }
27
+ const idx = argv.indexOf('--compliance-log');
28
+ if (idx >= 0 && argv[idx + 1]) {
29
+ const v = argv[idx + 1].trim();
30
+ return v || null;
31
+ }
32
+ return null;
33
+ }
34
+ export function isFinalizeComplianceSessionArgv(argv) {
35
+ return argv.includes('--finalize-compliance-session');
36
+ }
37
+ export function getComplianceLogDir() {
38
+ return path.join(homedir(), OPTIMUSLABS_LOGS_REL, COMPLIANCE_SUBDIR);
39
+ }
40
+ export function getActiveComplianceLogPath() {
41
+ return activeComplianceLogPath;
42
+ }
43
+ export function setActiveComplianceLogPath(logPath) {
44
+ activeComplianceLogPath = logPath ? path.resolve(logPath) : null;
45
+ }
46
+ function isTierDirName(name) {
47
+ return TIER_DIRS.includes(name);
48
+ }
49
+ function isManagedDirName(name) {
50
+ return isTierDirName(name) || name === TIER_IN_PROCESS;
51
+ }
52
+ function getInProcessComplianceLogDir() {
53
+ return path.join(getComplianceLogDir(), TIER_IN_PROCESS);
54
+ }
55
+ /**
56
+ * Convert legacy/root session paths into the explicit in_process/ location.
57
+ *
58
+ * Hooks may still pass ~/.../compliance/<timestamp>.log; future writes should live
59
+ * at ~/.../compliance/in_process/<timestamp>.log until finalize classifies them.
60
+ */
61
+ export function resolveComplianceSessionLogPath(logPath) {
62
+ const resolved = path.resolve(logPath);
63
+ if (path.dirname(resolved) === getComplianceLogDir()) {
64
+ return path.join(getInProcessComplianceLogDir(), path.basename(resolved));
65
+ }
66
+ return resolved;
67
+ }
68
+ /** Session log still in compliance/in_process/ (not yet finalized into a tier folder). */
69
+ export function isInflightComplianceLogPath(logPath) {
70
+ const resolved = resolveComplianceSessionLogPath(logPath);
71
+ return path.dirname(resolved) === getInProcessComplianceLogDir();
72
+ }
73
+ export function readLatestComplianceLogPointer() {
74
+ const pointer = path.join(getComplianceLogDir(), LATEST_POINTER);
75
+ if (!existsSync(pointer))
76
+ return null;
77
+ try {
78
+ const p = readFileSync(pointer, 'utf8').trim();
79
+ return p || null;
80
+ }
81
+ catch {
82
+ return null;
83
+ }
84
+ }
85
+ function pruneLogFilesInDir(dir, maxAgeMs, exceptPath) {
86
+ if (!existsSync(dir))
87
+ return 0;
88
+ const cutoff = Date.now() - maxAgeMs;
89
+ let removed = 0;
90
+ for (const name of readdirSync(dir)) {
91
+ if (!name.endsWith('.log'))
92
+ continue;
93
+ const full = path.join(dir, name);
94
+ if (exceptPath && full === exceptPath)
95
+ continue;
96
+ try {
97
+ const st = statSync(full);
98
+ if (st.mtimeMs < cutoff) {
99
+ unlinkSync(full);
100
+ removed += 1;
101
+ }
102
+ }
103
+ catch {
104
+ // best-effort
105
+ }
106
+ }
107
+ return removed;
108
+ }
109
+ export function pruneComplianceSessionLogs(exceptPath) {
110
+ const base = getComplianceLogDir();
111
+ const keep = exceptPath ? path.resolve(exceptPath) : null;
112
+ let removed = 0;
113
+ removed += pruneLogFilesInDir(path.join(base, TIER_NOOP), NOOP_MAX_AGE_MS, keep);
114
+ removed += pruneLogFilesInDir(path.join(base, TIER_SUCCESS), SUCCESS_ERROR_MAX_AGE_MS, keep);
115
+ removed += pruneLogFilesInDir(path.join(base, TIER_FAILURES), SUCCESS_ERROR_MAX_AGE_MS, keep);
116
+ removed += pruneLogFilesInDir(path.join(base, TIER_IN_PROCESS), INFLIGHT_MAX_AGE_MS, keep);
117
+ if (!existsSync(base))
118
+ return removed;
119
+ for (const name of readdirSync(base)) {
120
+ if (name === LATEST_POINTER || isManagedDirName(name))
121
+ continue;
122
+ if (!name.endsWith('.log'))
123
+ continue;
124
+ const full = path.join(base, name);
125
+ if (keep && full === keep)
126
+ continue;
127
+ try {
128
+ const st = statSync(full);
129
+ if (st.mtimeMs < Date.now() - INFLIGHT_MAX_AGE_MS) {
130
+ unlinkSync(full);
131
+ removed += 1;
132
+ }
133
+ }
134
+ catch {
135
+ // best-effort
136
+ }
137
+ }
138
+ return removed;
139
+ }
140
+ /**
141
+ * Classify a compliance session for finalize + optional server upload.
142
+ *
143
+ * Only two outcomes are uploaded (success / error on the wire): remediation **succeeded** or **failed**.
144
+ * Policy violations, in-flight restart, and sync-only sessions are noop (local retention only).
145
+ */
146
+ export function classifyComplianceSessionLog(content) {
147
+ const text = content.trim();
148
+ if (!text)
149
+ return TIER_NOOP;
150
+ if (/remediation_tracking: verified uuid=/.test(text)) {
151
+ return TIER_SUCCESS;
152
+ }
153
+ const remediationFailedPatterns = [
154
+ /REMEDIATION APPLY FAILURE/,
155
+ /compliance_check_runner: uncaught error/,
156
+ /remediation_tracking: verification_failed uuid=/,
157
+ /post_restart_verification count=\d+ quarantined=[1-9]/,
158
+ /enforceRemediation.*failed/i,
159
+ ];
160
+ if (remediationFailedPatterns.some((p) => p.test(text))) {
161
+ return TIER_FAILURES;
162
+ }
163
+ return TIER_NOOP;
164
+ }
165
+ /** Keep at compliance/ root until verify or remediation failure is recorded in the session. */
166
+ export function isRemediationCycleInflight(content) {
167
+ if (/remediation_tracking: verified uuid=/.test(content))
168
+ return false;
169
+ if (/remediation_tracking: verification_failed uuid=/.test(content))
170
+ return false;
171
+ if (/REMEDIATION APPLY FAILURE/.test(content))
172
+ return false;
173
+ if (/post_restart_verification count=\d+ quarantined=[1-9]/.test(content))
174
+ return false;
175
+ return /remediation_tracking: pending_post_restart_verify uuid=/.test(content);
176
+ }
177
+ function ensureComplianceLogDir() {
178
+ const dir = getComplianceLogDir();
179
+ if (!existsSync(dir))
180
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
181
+ for (const tier of [...TIER_DIRS, TIER_IN_PROCESS]) {
182
+ const sub = path.join(dir, tier);
183
+ if (!existsSync(sub))
184
+ mkdirSync(sub, { recursive: true, mode: 0o700 });
185
+ }
186
+ }
187
+ function writeLatestPointer(logPath) {
188
+ try {
189
+ ensureComplianceLogDir();
190
+ writeFileSync(path.join(getComplianceLogDir(), LATEST_POINTER), `${logPath}\n`, 'utf8');
191
+ }
192
+ catch {
193
+ // best-effort
194
+ }
195
+ }
196
+ function appendComplianceLine(logPath, line) {
197
+ try {
198
+ const dir = path.dirname(logPath);
199
+ if (!existsSync(dir))
200
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
201
+ appendFileSync(logPath, line, 'utf8');
202
+ }
203
+ catch {
204
+ // best-effort
205
+ }
206
+ }
207
+ export function complianceLogSessionBanner(logPath, label, mode) {
208
+ const resolved = path.resolve(logPath);
209
+ const ts = new Date().toISOString();
210
+ const banner = mode === 'replace'
211
+ ? `\n${'='.repeat(72)}\n${ts} session: ${label}\n${'='.repeat(72)}\n`
212
+ : `\n${'-'.repeat(72)}\n${ts} section: ${label}\n${'-'.repeat(72)}\n`;
213
+ try {
214
+ const dir = path.dirname(resolved);
215
+ if (!existsSync(dir))
216
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
217
+ if (mode === 'replace') {
218
+ writeFileSync(resolved, banner + buildLogMetadataBlock(), 'utf8');
219
+ }
220
+ else {
221
+ appendFileSync(resolved, banner, 'utf8');
222
+ }
223
+ writeLatestPointer(resolved);
224
+ }
225
+ catch {
226
+ // best-effort
227
+ }
228
+ }
229
+ /** Gate: prune old files, bind path, start or resume an inflight session file. */
230
+ export function initComplianceSessionForGate(logPath) {
231
+ const resolved = resolveComplianceSessionLogPath(logPath);
232
+ ensureComplianceLogDir();
233
+ const removed = pruneComplianceSessionLogs(resolved);
234
+ setActiveComplianceLogPath(resolved);
235
+ const resume = existsSync(resolved) && statSync(resolved).size > 0 && isInflightComplianceLogPath(resolved);
236
+ complianceLogSessionBanner(resolved, 'compliance_prompt_gate (before submit)', resume ? 'append' : 'replace');
237
+ if (!resume) {
238
+ writeLatestPointer(resolved);
239
+ }
240
+ if (removed > 0) {
241
+ appendComplianceLine(resolved, `${new Date().toISOString()} compliance_session: pruned ${removed} log(s) (in_process 2h, noop 30m, success/failures 24h)\n`);
242
+ }
243
+ return resolved;
244
+ }
245
+ /** Runner: prune old files, bind path, append runner section to the gate session file. */
246
+ export function initComplianceSessionForRunner(logPath) {
247
+ const resolved = resolveComplianceSessionLogPath(logPath);
248
+ ensureComplianceLogDir();
249
+ const removed = pruneComplianceSessionLogs(resolved);
250
+ setActiveComplianceLogPath(resolved);
251
+ complianceLogSessionBanner(resolved, 'compliance_check_runner (background sync + check)', 'append');
252
+ if (removed > 0) {
253
+ appendComplianceLine(resolved, `${new Date().toISOString()} compliance_session: pruned ${removed} log(s) (in_process 2h, noop 30m, success/failures 24h)\n`);
254
+ }
255
+ return resolved;
256
+ }
257
+ /**
258
+ * Move session log from compliance/ root into noop|success|failures/ and prune tiers.
259
+ * No-op if file missing or already finalized.
260
+ */
261
+ export function finalizeComplianceSessionLog(logPath, forcedTier) {
262
+ const resolved = resolveComplianceSessionLogPath(logPath);
263
+ if (!existsSync(resolved))
264
+ return null;
265
+ const parentName = path.basename(path.dirname(resolved));
266
+ if (isTierDirName(parentName)) {
267
+ return { tier: parentName, logPath: resolved, moved: false };
268
+ }
269
+ if (parentName === 'error') {
270
+ return { tier: TIER_FAILURES, logPath: resolved, moved: false };
271
+ }
272
+ const content = readFileSync(resolved, 'utf8');
273
+ if (isRemediationCycleInflight(content)) {
274
+ return null;
275
+ }
276
+ const tier = forcedTier ?? classifyComplianceSessionLog(content);
277
+ ensureComplianceLogDir();
278
+ const dest = path.join(getComplianceLogDir(), tier, path.basename(resolved));
279
+ if (resolved !== dest) {
280
+ if (existsSync(dest))
281
+ unlinkSync(dest);
282
+ renameSync(resolved, dest);
283
+ }
284
+ setActiveComplianceLogPath(null);
285
+ writeLatestPointer(dest);
286
+ pruneComplianceSessionLogs(dest);
287
+ return { tier, logPath: dest, moved: true };
288
+ }
289
+ export function appendToActiveComplianceLog(message) {
290
+ const logPath = activeComplianceLogPath;
291
+ if (!logPath)
292
+ return;
293
+ try {
294
+ appendComplianceLine(logPath, `${new Date().toISOString()} ${message}\n`);
295
+ }
296
+ catch {
297
+ // best-effort
298
+ }
299
+ }
300
+ export function readComplianceSessionLog(logPath) {
301
+ try {
302
+ if (!existsSync(logPath))
303
+ return '';
304
+ return readFileSync(logPath, 'utf8');
305
+ }
306
+ catch {
307
+ return '';
308
+ }
309
+ }
310
+ function appendLineToSessionLogFile(logPath, message) {
311
+ try {
312
+ appendComplianceLine(logPath, `${new Date().toISOString()} ${message}\n`);
313
+ }
314
+ catch {
315
+ // best-effort
316
+ }
317
+ }
318
+ /**
319
+ * Upload full session log to the server (success and failures tiers only, first finalize only).
320
+ */
321
+ export async function uploadComplianceSessionLogFromFinalize(result) {
322
+ if (result.tier !== TIER_SUCCESS && result.tier !== TIER_FAILURES)
323
+ return;
324
+ if (!result.moved)
325
+ return;
326
+ const logText = readComplianceSessionLog(result.logPath);
327
+ if (!logText.trim())
328
+ return;
329
+ const logBytes = Buffer.byteLength(logText, 'utf8');
330
+ if (logBytes > MAX_SESSION_LOG_UPLOAD_BYTES) {
331
+ appendLineToSessionLogFile(result.logPath, `compliance_session_upload: skipped (${logBytes} bytes exceeds ${MAX_SESSION_LOG_UPLOAD_BYTES})`);
332
+ return;
333
+ }
334
+ const { readStoredAuthKey } = await import('../auth/auth_key_store.js');
335
+ const { tryResolveHardwareUuid } = await import('./hardware_uuid.js');
336
+ const { loadEndpointBase } = await import('../sender/endpoint_config.js');
337
+ const { createSignature } = await import('../sender/signing.js');
338
+ const { buildApiUrl } = await import('../../endpoint_client/registry_api.js');
339
+ const { executeBody } = await import('../../endpoint_client/http_transport.js');
340
+ const authKey = readStoredAuthKey();
341
+ const hardwareUuid = tryResolveHardwareUuid();
342
+ if (!authKey || !hardwareUuid) {
343
+ appendLineToSessionLogFile(result.logPath, 'compliance_session_upload: skipped (auth key or hardware UUID unavailable)');
344
+ return;
345
+ }
346
+ const sessionFilename = path.basename(result.logPath);
347
+ const outcome = result.tier === TIER_FAILURES ? UPLOAD_OUTCOME_ERROR : result.tier;
348
+ const signingPayload = {
349
+ hardware_uuid: hardwareUuid,
350
+ outcome,
351
+ session_filename: sessionFilename,
352
+ log_text: logText,
353
+ };
354
+ const signature = createSignature(signingPayload, authKey.key);
355
+ const body = JSON.stringify({ ...signingPayload, signature });
356
+ const url = buildApiUrl(loadEndpointBase(), '/endpoint_security/api/remediation-activity-log/');
357
+ try {
358
+ const { statusCode } = await executeBody(url, 'POST', body, 30_000);
359
+ if (statusCode === 200) {
360
+ appendLineToSessionLogFile(result.logPath, `compliance_session_upload: ok outcome=${outcome} bytes=${logBytes}`);
361
+ }
362
+ else {
363
+ appendLineToSessionLogFile(result.logPath, `compliance_session_upload: server status=${statusCode}`);
364
+ }
365
+ }
366
+ catch (err) {
367
+ const msg = err instanceof Error ? err.message : String(err);
368
+ appendLineToSessionLogFile(result.logPath, `compliance_session_upload: failed ${msg}`);
369
+ }
370
+ }
371
+ /** Finalize session file into success|failures|noop tier, then upload when success or failures. */
372
+ export async function finalizeAndUploadComplianceSessionLog(logPath, forcedTier) {
373
+ const result = finalizeComplianceSessionLog(logPath, forcedTier);
374
+ if (result) {
375
+ await uploadComplianceSessionLogFromFinalize(result);
376
+ }
377
+ return result;
378
+ }
@@ -0,0 +1,31 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { atomicWriteJson, getDialogPreferencesPath, } from './management_storage.js';
3
+ export function readDialogPreferences() {
4
+ const path = getDialogPreferencesPath();
5
+ if (!existsSync(path)) {
6
+ return { version: 1, suppress_non_restart_autofix_dialogs: false };
7
+ }
8
+ try {
9
+ const parsed = JSON.parse(readFileSync(path, 'utf8'));
10
+ if (parsed?.version !== 1)
11
+ return { version: 1, suppress_non_restart_autofix_dialogs: false };
12
+ return {
13
+ version: 1,
14
+ suppress_non_restart_autofix_dialogs: Boolean(parsed.suppress_non_restart_autofix_dialogs),
15
+ };
16
+ }
17
+ catch {
18
+ return { version: 1, suppress_non_restart_autofix_dialogs: false };
19
+ }
20
+ }
21
+ export function isNonRestartAutofixDialogSuppressed() {
22
+ return readDialogPreferences().suppress_non_restart_autofix_dialogs === true;
23
+ }
24
+ export function setSuppressNonRestartAutofixDialogs(suppress) {
25
+ const next = {
26
+ version: 1,
27
+ suppress_non_restart_autofix_dialogs: suppress,
28
+ updated_at: new Date().toISOString(),
29
+ };
30
+ atomicWriteJson(getDialogPreferencesPath(), next);
31
+ }
@@ -0,0 +1,36 @@
1
+ import { execSync } from 'node:child_process';
2
+ /** Resolve hardware UUID from environment or system commands. */
3
+ function resolveHardwareUuid() {
4
+ const envUuid = process.env.OPTIMUS_HARDWARE_UUID?.trim();
5
+ if (envUuid)
6
+ return envUuid;
7
+ try {
8
+ const ioregOutput = execSync('ioreg -rd1 -c IOPlatformExpertDevice', { encoding: 'utf8' }).trim();
9
+ const ioregMatch = ioregOutput.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/i);
10
+ if (ioregMatch?.[1])
11
+ return ioregMatch[1];
12
+ }
13
+ catch {
14
+ // try next method
15
+ }
16
+ try {
17
+ const profilerOutput = execSync('system_profiler SPHardwareDataType', { encoding: 'utf8' }).trim();
18
+ const profilerMatch = profilerOutput.match(/Hardware UUID:\s*([A-F0-9-]+)/i);
19
+ if (profilerMatch?.[1])
20
+ return profilerMatch[1];
21
+ }
22
+ catch {
23
+ // both methods failed
24
+ }
25
+ throw new Error('Unable to determine hardware UUID via ioreg or system_profiler.');
26
+ }
27
+ /** Same as {@link resolveHardwareUuid} but returns null when the host exposes no stable ID (e.g. Linux CI). */
28
+ export function tryResolveHardwareUuid() {
29
+ try {
30
+ return resolveHardwareUuid();
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ export { resolveHardwareUuid };