@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.
- package/README.md +46 -0
- package/dist/apply_deferred_vscdb.js +38 -0
- package/dist/bootstrap_constants.js +15 -0
- package/dist/cli/bash_script_generator.js +95 -0
- package/dist/cli.js +144 -0
- package/dist/cli_invocation_match.js +30 -0
- package/dist/compliance_check_runner.js +57 -0
- package/dist/compliance_prompt_gate.js +396 -0
- package/dist/dialog_prefs_cli.js +109 -0
- package/dist/endpoint_client/compress_payload.js +25 -0
- package/dist/endpoint_client/http_transport.js +90 -0
- package/dist/endpoint_client/index.js +3 -0
- package/dist/endpoint_client/registry_api.js +41 -0
- package/dist/endpoint_client/startup_api.js +44 -0
- package/dist/endpoint_client/types.js +4 -0
- package/dist/execute_trusted_restarts.js +60 -0
- package/dist/log_config_files/auth/auth_flow.js +22 -0
- package/dist/log_config_files/auth/auth_key_store.js +14 -0
- package/dist/log_config_files/collection/claude_desktop_extensions_collector.js +248 -0
- package/dist/log_config_files/collection/claude_known_projects_collector.js +42 -0
- package/dist/log_config_files/collection/claude_token_usage_collector.js +203 -0
- package/dist/log_config_files/collection/codex_token_usage_collector.js +342 -0
- package/dist/log_config_files/collection/config_collector.js +245 -0
- package/dist/log_config_files/collection/copilot_token_usage_collector.js +167 -0
- package/dist/log_config_files/collection/cowork_desktop_version_collector.js +60 -0
- package/dist/log_config_files/collection/cowork_session_whitelist.js +89 -0
- package/dist/log_config_files/collection/cursor_project_mcp_collector.js +230 -0
- package/dist/log_config_files/collection/cursor_project_workspace_activity.js +73 -0
- package/dist/log_config_files/collection/cursor_token_usage_collector.js +192 -0
- package/dist/log_config_files/collection/directory_collector.js +126 -0
- package/dist/log_config_files/collection/enrichment_helpers.js +53 -0
- package/dist/log_config_files/collection/ensure_cursor_user_settings_snapshot.js +50 -0
- package/dist/log_config_files/collection/file_type_rules.js +47 -0
- package/dist/log_config_files/collection/grok_cli_version_collector.js +53 -0
- package/dist/log_config_files/collection/hermes_token_usage_collector.js +241 -0
- package/dist/log_config_files/collection/mcp_tool_collector.js +106 -0
- package/dist/log_config_files/collection/metadata_merge.js +43 -0
- package/dist/log_config_files/collection/openclaw_helpers.js +55 -0
- package/dist/log_config_files/collection/openclaw_token_usage_collector.js +223 -0
- package/dist/log_config_files/collection/opencode_token_usage_collector.js +177 -0
- package/dist/log_config_files/collection/pi_token_usage_collector.js +239 -0
- package/dist/log_config_files/collection/plugin_collector.js +223 -0
- package/dist/log_config_files/collection/plugin_version_helpers.js +37 -0
- package/dist/log_config_files/collection/skills_cli_collector.js +162 -0
- package/dist/log_config_files/index.js +19 -0
- package/dist/log_config_files/paths/path_constants_helpers.js +71 -0
- package/dist/log_config_files/paths/pattern_resolver.js +326 -0
- package/dist/log_config_files/readers/composer_shadow_merge.js +67 -0
- package/dist/log_config_files/readers/cursor_shadow_merge_ruleset.js +46 -0
- package/dist/log_config_files/readers/file_readers.js +196 -0
- package/dist/log_config_files/readers/vscdb_config_builder.js +148 -0
- package/dist/log_config_files/readers/vscdb_reactive_storage.js +62 -0
- package/dist/log_config_files/readers/vscdb_reader.js +337 -0
- package/dist/log_config_files/runtime/client_event_reporter.js +347 -0
- package/dist/log_config_files/runtime/compliance_check.js +1013 -0
- package/dist/log_config_files/runtime/compliance_session_log.js +378 -0
- package/dist/log_config_files/runtime/dialog_preferences.js +31 -0
- package/dist/log_config_files/runtime/hardware_uuid.js +36 -0
- package/dist/log_config_files/runtime/hook_logger.js +267 -0
- package/dist/log_config_files/runtime/hook_type_for_request.js +30 -0
- package/dist/log_config_files/runtime/log_metadata.js +122 -0
- package/dist/log_config_files/runtime/main_runner.js +376 -0
- package/dist/log_config_files/runtime/management_storage.js +144 -0
- package/dist/log_config_files/runtime/ops_target_path.js +18 -0
- package/dist/log_config_files/runtime/remediation_apply_tracking.js +152 -0
- package/dist/log_config_files/runtime/remediation_config_path.js +140 -0
- package/dist/log_config_files/runtime/remediation_sync.js +1705 -0
- package/dist/log_config_files/runtime/secret_regex_scan.js +126 -0
- package/dist/log_config_files/runtime/sqlite_binary.js +124 -0
- package/dist/log_config_files/runtime/trusted_restarts.js +113 -0
- package/dist/log_config_files/runtime/workspace_repo.js +170 -0
- package/dist/log_config_files/runtime/worktree_absent.js +50 -0
- package/dist/log_config_files/runtime/worktree_scanner.js +137 -0
- package/dist/log_config_files/sender/batch_sender.js +283 -0
- package/dist/log_config_files/sender/endpoint_config.js +24 -0
- package/dist/log_config_files/sender/signing.js +1 -0
- package/dist/log_sensitive_paths_audit.js +89 -0
- package/dist/log_uuid/auth_key_store.js +37 -0
- package/dist/log_uuid/hardware_uuid.js +35 -0
- package/dist/log_uuid/index.js +11 -0
- package/dist/log_uuid/log_uuid_helper.js +30 -0
- package/dist/log_uuid/startup_sender.js +131 -0
- package/dist/log_uuid/user_profile.js +192 -0
- package/dist/remediation_change_preview.js +182 -0
- package/dist/tofu.js +22 -0
- package/dist/tofu_environment.js +36 -0
- package/dist/types/config_file_types.js +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, appendFileSync, writeFileSync, statSync, readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { OPTIMUSLABS_LOGS_REL, LOG_GROUP_HOOK, LOG_GROUP_COMPLIANCE, } from '../../bootstrap_constants.js';
|
|
4
|
+
import { getActiveComplianceLogPath } from './compliance_session_log.js';
|
|
5
|
+
import { buildLogMetadataBlock, formatErrorBlock } from './log_metadata.js';
|
|
6
|
+
const HOOK_LOG_FILENAME = 'hook_log.txt';
|
|
7
|
+
const COMPLIANCE_RUNNER_LOG_FILENAME = 'compliance_runner.log';
|
|
8
|
+
const COMPLIANCE_FALLBACK_TIER = 'noop';
|
|
9
|
+
/** Hard cap so a single upload/sync session cannot grow hook_log.txt without bound. */
|
|
10
|
+
const MAX_HOOK_LOG_BYTES = 2 * 1024 * 1024;
|
|
11
|
+
function getHookLogPath() {
|
|
12
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
13
|
+
if (!homeDir)
|
|
14
|
+
return null;
|
|
15
|
+
return path.join(homeDir, OPTIMUSLABS_LOGS_REL, LOG_GROUP_HOOK, HOOK_LOG_FILENAME);
|
|
16
|
+
}
|
|
17
|
+
function getComplianceRunnerLogPath() {
|
|
18
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
19
|
+
if (!homeDir)
|
|
20
|
+
return null;
|
|
21
|
+
return path.join(homeDir, OPTIMUSLABS_LOGS_REL, LOG_GROUP_COMPLIANCE, COMPLIANCE_FALLBACK_TIER, COMPLIANCE_RUNNER_LOG_FILENAME);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Append one ISO-timestamped line to the active compliance session log.
|
|
25
|
+
* If no session is active, use compliance/noop/compliance_runner.log as a legacy fallback.
|
|
26
|
+
* `level` examples: INFO, RUNNER, RESTART, FAIL
|
|
27
|
+
*/
|
|
28
|
+
function appendComplianceRunnerLine(level, message) {
|
|
29
|
+
const sessionPath = getActiveComplianceLogPath();
|
|
30
|
+
if (sessionPath) {
|
|
31
|
+
try {
|
|
32
|
+
appendFileSync(sessionPath, `${new Date().toISOString()} [${level}] pid=${process.pid} ${message}\n`, 'utf8');
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// best-effort
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const logPath = getComplianceRunnerLogPath();
|
|
40
|
+
if (!logPath)
|
|
41
|
+
return;
|
|
42
|
+
try {
|
|
43
|
+
const dir = path.dirname(logPath);
|
|
44
|
+
if (!existsSync(dir))
|
|
45
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
46
|
+
const ts = new Date().toISOString();
|
|
47
|
+
appendFileSync(logPath, `${ts} [${level}] pid=${process.pid} ${message}\n`, 'utf8');
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// best-effort
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Append-only diagnostics for remediation sync / compliance (structured).
|
|
55
|
+
*/
|
|
56
|
+
function complianceRunnerDiag(message) {
|
|
57
|
+
appendComplianceRunnerLine('INFO', message);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Background compliance_check_runner should use this so entries match the same format.
|
|
61
|
+
*/
|
|
62
|
+
function complianceRunnerRunnerLine(message) {
|
|
63
|
+
appendComplianceRunnerLine('RUNNER', message);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Loud record when a remediation cannot be verified or applied. Writes the block to the active
|
|
67
|
+
* compliance session log (which finalizes into the failures/ tier), and appends a single summary
|
|
68
|
+
* line to hook_log.txt for the current session. No separate permanent ledger — the failure detail
|
|
69
|
+
* lives in the per-session compliance log.
|
|
70
|
+
*/
|
|
71
|
+
function logRemediationApplyFailure(context, fields) {
|
|
72
|
+
const ts = new Date().toISOString();
|
|
73
|
+
const bodyLines = Object.entries(fields)
|
|
74
|
+
.filter(([, v]) => v !== undefined && v !== null && String(v) !== '')
|
|
75
|
+
.map(([k, v]) => ` ${k}: ${typeof v === 'string' ? v : JSON.stringify(v)}`);
|
|
76
|
+
const block = [
|
|
77
|
+
'',
|
|
78
|
+
'='.repeat(72),
|
|
79
|
+
`${ts} REMEDIATION APPLY FAILURE — ${context}`,
|
|
80
|
+
...bodyLines,
|
|
81
|
+
'='.repeat(72),
|
|
82
|
+
'',
|
|
83
|
+
].join('\n');
|
|
84
|
+
const summary = fields.reason != null
|
|
85
|
+
? String(fields.reason)
|
|
86
|
+
: fields.message != null
|
|
87
|
+
? String(fields.message)
|
|
88
|
+
: 'see fields above';
|
|
89
|
+
const writeFailAppend = (filePath) => {
|
|
90
|
+
const dir = path.dirname(filePath);
|
|
91
|
+
if (!existsSync(dir))
|
|
92
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
93
|
+
appendFileSync(filePath, block, 'utf8');
|
|
94
|
+
};
|
|
95
|
+
try {
|
|
96
|
+
const activeCompliancePath = getActiveComplianceLogPath();
|
|
97
|
+
if (activeCompliancePath) {
|
|
98
|
+
writeFailAppend(activeCompliancePath);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const crPath = getComplianceRunnerLogPath();
|
|
102
|
+
if (crPath) {
|
|
103
|
+
const dir = path.dirname(crPath);
|
|
104
|
+
if (!existsSync(dir))
|
|
105
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
106
|
+
appendFileSync(crPath, block, 'utf8');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
/* best-effort */
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
hookRunLog(`REMEDIATION_APPLY_FAILURE [${context}] ${fields.uuid != null ? `uuid=${fields.uuid} ` : ''}${summary}`);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
/* best-effort */
|
|
118
|
+
}
|
|
119
|
+
appendComplianceRunnerLine('FAIL', `${context} — ${summary}`);
|
|
120
|
+
}
|
|
121
|
+
function ensureHookLogUnderCap() {
|
|
122
|
+
const logPath = getHookLogPath();
|
|
123
|
+
if (!logPath || !existsSync(logPath))
|
|
124
|
+
return;
|
|
125
|
+
try {
|
|
126
|
+
if (statSync(logPath).size <= MAX_HOOK_LOG_BYTES)
|
|
127
|
+
return;
|
|
128
|
+
const ts = new Date().toISOString();
|
|
129
|
+
writeFileSync(logPath, `${'='.repeat(72)}\n${ts} hook_log.txt truncated (exceeded ${MAX_HOOK_LOG_BYTES} bytes)\n${'='.repeat(72)}\n`, 'utf8');
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// best-effort
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Start a new hook_log.txt session (replaces the file). Use once per logical run
|
|
137
|
+
* (e.g. compliance_prompt_gate, log_config_files upload via hookLogReplace).
|
|
138
|
+
*/
|
|
139
|
+
function hookLogSessionBanner(label) {
|
|
140
|
+
const logPath = getHookLogPath();
|
|
141
|
+
if (!logPath)
|
|
142
|
+
return;
|
|
143
|
+
try {
|
|
144
|
+
const dir = path.dirname(logPath);
|
|
145
|
+
if (!existsSync(dir))
|
|
146
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
147
|
+
const ts = new Date().toISOString();
|
|
148
|
+
const banner = `\n${'='.repeat(72)}\n${ts} session: ${label}\n${'='.repeat(72)}\n`;
|
|
149
|
+
writeFileSync(logPath, banner + buildLogMetadataBlock(), 'utf8');
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// best-effort
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Record a structured error block to the active compliance session log (if any) or
|
|
157
|
+
* hook_log.txt. Prefer this over silent best-effort catches so failures carry the
|
|
158
|
+
* error name/message/stack needed to diagnose a customer report.
|
|
159
|
+
*/
|
|
160
|
+
function hookLogError(context, err, fields) {
|
|
161
|
+
const block = formatErrorBlock(context, err, fields);
|
|
162
|
+
const compliancePath = getActiveComplianceLogPath();
|
|
163
|
+
const logPath = compliancePath ?? getHookLogPath();
|
|
164
|
+
if (!logPath)
|
|
165
|
+
return;
|
|
166
|
+
try {
|
|
167
|
+
const dir = path.dirname(logPath);
|
|
168
|
+
if (!existsSync(dir))
|
|
169
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
170
|
+
if (!compliancePath)
|
|
171
|
+
ensureHookLogUnderCap();
|
|
172
|
+
appendFileSync(logPath, block, 'utf8');
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// best-effort
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Append a subsection after an existing session (e.g. compliance_check_runner after the gate)
|
|
180
|
+
* without replacing hook_log.txt.
|
|
181
|
+
*/
|
|
182
|
+
function hookLogAppendSection(label) {
|
|
183
|
+
const compliancePath = getActiveComplianceLogPath();
|
|
184
|
+
if (compliancePath) {
|
|
185
|
+
try {
|
|
186
|
+
const ts = new Date().toISOString();
|
|
187
|
+
const banner = `\n${'-'.repeat(72)}\n${ts} section: ${label}\n${'-'.repeat(72)}\n`;
|
|
188
|
+
appendFileSync(compliancePath, banner, 'utf8');
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
// best-effort
|
|
192
|
+
}
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const logPath = getHookLogPath();
|
|
196
|
+
if (!logPath)
|
|
197
|
+
return;
|
|
198
|
+
try {
|
|
199
|
+
ensureHookLogUnderCap();
|
|
200
|
+
const dir = path.dirname(logPath);
|
|
201
|
+
if (!existsSync(dir))
|
|
202
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
203
|
+
const ts = new Date().toISOString();
|
|
204
|
+
const banner = `\n${'-'.repeat(72)}\n${ts} section: ${label}\n${'-'.repeat(72)}\n`;
|
|
205
|
+
appendFileSync(logPath, banner, 'utf8');
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// best-effort
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/** Begins a log_config_files upload session (replaces hook_log.txt, same as hookLogSessionBanner). */
|
|
212
|
+
function hookLogReplace() {
|
|
213
|
+
hookLogSessionBanner('log_config_files (config upload)');
|
|
214
|
+
}
|
|
215
|
+
/** Append a timestamped line to hook_log.txt or the active compliance session log. */
|
|
216
|
+
function hookRunLog(message) {
|
|
217
|
+
const compliancePath = getActiveComplianceLogPath();
|
|
218
|
+
if (compliancePath) {
|
|
219
|
+
try {
|
|
220
|
+
const ts = new Date().toISOString();
|
|
221
|
+
appendFileSync(compliancePath, `${ts} ${message}\n`, 'utf8');
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
// best-effort
|
|
225
|
+
}
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const logPath = getHookLogPath();
|
|
229
|
+
if (!logPath)
|
|
230
|
+
return;
|
|
231
|
+
try {
|
|
232
|
+
ensureHookLogUnderCap();
|
|
233
|
+
const ts = new Date().toISOString();
|
|
234
|
+
appendFileSync(logPath, `${ts} ${message}\n`, 'utf8');
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
// best-effort; don't break the run
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/** Append a line without timestamp (for multi-line blocks like manifests). */
|
|
241
|
+
function hookLogLine(message) {
|
|
242
|
+
const logPath = getHookLogPath();
|
|
243
|
+
if (!logPath)
|
|
244
|
+
return;
|
|
245
|
+
try {
|
|
246
|
+
ensureHookLogUnderCap();
|
|
247
|
+
appendFileSync(logPath, `${message}\n`, 'utf8');
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
// best-effort
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/** Read the current hook_log.txt content. Returns empty string if not found or unreadable. */
|
|
254
|
+
function readHookLog() {
|
|
255
|
+
const logPath = getHookLogPath();
|
|
256
|
+
if (!logPath)
|
|
257
|
+
return '';
|
|
258
|
+
try {
|
|
259
|
+
if (!existsSync(logPath))
|
|
260
|
+
return '';
|
|
261
|
+
return readFileSync(logPath, 'utf8');
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
return '';
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
export { getHookLogPath, getComplianceRunnerLogPath, hookLogReplace, hookLogSessionBanner, hookLogAppendSection, hookRunLog, hookLogError, hookLogLine, complianceRunnerDiag, complianceRunnerRunnerLine, appendComplianceRunnerLine, logRemediationApplyFailure, readHookLog, };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
function normalizeToken(raw) {
|
|
2
|
+
if (!raw?.trim())
|
|
3
|
+
return '';
|
|
4
|
+
const s = raw.trim().toLowerCase().replace(/-/g, '_');
|
|
5
|
+
if (s === 'claude_desktop')
|
|
6
|
+
return 'claude';
|
|
7
|
+
if (s === 'github_copilot')
|
|
8
|
+
return 'copilot';
|
|
9
|
+
if (s === 'cursor' || s === 'claude' || s === 'copilot' || s === 'opencode' || s === 'codex')
|
|
10
|
+
return s;
|
|
11
|
+
// Legacy hooks set OPTIMUS_AGENT=Cursor (display casing)
|
|
12
|
+
if (raw.trim() === 'Cursor')
|
|
13
|
+
return 'cursor';
|
|
14
|
+
return s;
|
|
15
|
+
}
|
|
16
|
+
/** Resolve hook_request.hook_type from hook wrapper env (OPTIMUS_HOOK_TYPE, then OPTIMUS_AGENT). */
|
|
17
|
+
export function resolveHookTypeFromEnv(env = process.env) {
|
|
18
|
+
const fromHook = normalizeToken(env.OPTIMUS_HOOK_TYPE);
|
|
19
|
+
const fromAgent = normalizeToken(env.OPTIMUS_AGENT);
|
|
20
|
+
const token = fromHook || fromAgent || 'claude';
|
|
21
|
+
if (token === 'cursor')
|
|
22
|
+
return 'cursor';
|
|
23
|
+
if (token === 'copilot')
|
|
24
|
+
return 'copilot';
|
|
25
|
+
if (token === 'opencode')
|
|
26
|
+
return 'opencode';
|
|
27
|
+
if (token === 'codex')
|
|
28
|
+
return 'codex';
|
|
29
|
+
return 'claude';
|
|
30
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared log enrichment helpers: a host/run-context metadata block appended to
|
|
3
|
+
* session banners, and a structured error block. Kept dependency-free of
|
|
4
|
+
* hook_logger / compliance_session_log so both can import it without cycles.
|
|
5
|
+
*/
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { OPTIMUSLABS_MANAGEMENT_REL } from '../../bootstrap_constants.js';
|
|
11
|
+
/** This package's name + version as "<name>: <version>", read from its own package.json. */
|
|
12
|
+
function readPackageVersion() {
|
|
13
|
+
try {
|
|
14
|
+
const pkgPath = fileURLToPath(new URL('../../../package.json', import.meta.url));
|
|
15
|
+
const parsed = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
16
|
+
const name = typeof parsed?.name === 'string' ? parsed.name : '';
|
|
17
|
+
const version = typeof parsed?.version === 'string' ? parsed.version : '';
|
|
18
|
+
if (name && version)
|
|
19
|
+
return `${name}: ${version}`;
|
|
20
|
+
return version || name || 'unknown';
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return 'unknown';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Deployed bundle version from release/bundle-version.json, or 'unknown'. */
|
|
27
|
+
function readBundleVersion() {
|
|
28
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
29
|
+
if (!home)
|
|
30
|
+
return 'unknown';
|
|
31
|
+
const p = path.join(home, OPTIMUSLABS_MANAGEMENT_REL, 'release', 'bundle-version.json');
|
|
32
|
+
try {
|
|
33
|
+
if (!existsSync(p))
|
|
34
|
+
return 'unknown';
|
|
35
|
+
const parsed = JSON.parse(readFileSync(p, 'utf8'));
|
|
36
|
+
return typeof parsed?.version === 'string' ? parsed.version : 'unknown';
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return 'unknown';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Object form of the host/run-context metadata, for JSON telemetry sent to the backend
|
|
44
|
+
* (client-event ingest). Same fields as {@link buildLogMetadataBlock}, minus empty values.
|
|
45
|
+
*/
|
|
46
|
+
export function buildLogMetadataObject() {
|
|
47
|
+
const out = {};
|
|
48
|
+
const add = (key, value) => {
|
|
49
|
+
if (value !== undefined && value !== '')
|
|
50
|
+
out[key] = value;
|
|
51
|
+
};
|
|
52
|
+
add('host', os.hostname());
|
|
53
|
+
add('platform', `${process.platform}/${process.arch}`);
|
|
54
|
+
add('os_release', os.release());
|
|
55
|
+
add('node', process.version);
|
|
56
|
+
add('pid', String(process.pid));
|
|
57
|
+
add('hook_type', process.env.OPTIMUS_HOOK_TYPE);
|
|
58
|
+
add('hook_script_version', process.env.OPTIMUS_HOOK_SCRIPT_VERSION);
|
|
59
|
+
add('reporter_version', readPackageVersion());
|
|
60
|
+
add('bundle_version', readBundleVersion());
|
|
61
|
+
add('endpoint', process.env.OPTIMUS_ENDPOINT);
|
|
62
|
+
add('cwd', process.cwd());
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Indented, multi-line metadata describing the host and run context. Appended to each
|
|
67
|
+
* session banner so a single log file is self-describing during customer support triage.
|
|
68
|
+
* Lines with no value are omitted.
|
|
69
|
+
*/
|
|
70
|
+
export function buildLogMetadataBlock() {
|
|
71
|
+
const lines = ['meta:'];
|
|
72
|
+
const add = (key, value) => {
|
|
73
|
+
if (value !== undefined && value !== '')
|
|
74
|
+
lines.push(` ${key}=${value}`);
|
|
75
|
+
};
|
|
76
|
+
add('host', os.hostname());
|
|
77
|
+
add('platform', `${process.platform}/${process.arch}`);
|
|
78
|
+
add('os_release', os.release());
|
|
79
|
+
add('node', process.version);
|
|
80
|
+
add('pid', String(process.pid));
|
|
81
|
+
add('hook_type', process.env.OPTIMUS_HOOK_TYPE);
|
|
82
|
+
add('hook_script_version', process.env.OPTIMUS_HOOK_SCRIPT_VERSION);
|
|
83
|
+
add('reporter_version', readPackageVersion());
|
|
84
|
+
add('bundle_version', readBundleVersion());
|
|
85
|
+
add('endpoint', process.env.OPTIMUS_ENDPOINT);
|
|
86
|
+
add('cwd', process.cwd());
|
|
87
|
+
// Only the banner line + meta: header would be useless on its own; require ≥1 field.
|
|
88
|
+
return lines.length > 1 ? lines.join('\n') + '\n' : '';
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Structured, multi-line error block: name/message, a trimmed stack, and any extra
|
|
92
|
+
* context fields. Used by log writers so errors carry enough detail to diagnose
|
|
93
|
+
* without re-running, instead of being swallowed by best-effort catches.
|
|
94
|
+
*/
|
|
95
|
+
export function formatErrorBlock(context, err, fields) {
|
|
96
|
+
const ts = new Date().toISOString();
|
|
97
|
+
const lines = ['', '-'.repeat(72), `${ts} ERROR — ${context}`];
|
|
98
|
+
if (err instanceof Error) {
|
|
99
|
+
lines.push(` error: ${err.name}: ${err.message}`);
|
|
100
|
+
if (err.stack) {
|
|
101
|
+
const frames = err.stack
|
|
102
|
+
.split('\n')
|
|
103
|
+
.slice(1, 6)
|
|
104
|
+
.map((l) => ` ${l.trim()}`)
|
|
105
|
+
.filter((l) => l.trim().length > 0);
|
|
106
|
+
if (frames.length)
|
|
107
|
+
lines.push(' stack:', ...frames);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
lines.push(` error: ${String(err)}`);
|
|
112
|
+
}
|
|
113
|
+
if (fields) {
|
|
114
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
115
|
+
if (v !== undefined && v !== null && String(v) !== '') {
|
|
116
|
+
lines.push(` ${k}: ${typeof v === 'string' ? v : JSON.stringify(v)}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
lines.push('-'.repeat(72), '');
|
|
121
|
+
return lines.join('\n');
|
|
122
|
+
}
|