* Fire-and-forget subprocess invocation of gstack-telemetry-log with the * attack_attempt event type. The binary handles tier gating internally * (community → upload, anonymous → local only, off → no-op), so we don't * need to re-check here. * * Never throws. Never blocks. If the binary isn't f
(record: AttemptRecord)
| 479 | * local attempts.jsonl write from logAttempt() still gives us the audit trail. |
| 480 | */ |
| 481 | function reportAttemptTelemetry(record: AttemptRecord): void { |
| 482 | const bin = findTelemetryBinary(); |
| 483 | if (!bin) return; |
| 484 | try { |
| 485 | const result = buildTelemetrySpawnCommand(bin, [ |
| 486 | '--event-type', 'attack_attempt', |
| 487 | '--url-domain', record.urlDomain || '', |
| 488 | '--payload-hash', record.payloadHash, |
| 489 | '--confidence', String(record.confidence), |
| 490 | '--layer', record.layer, |
| 491 | '--verdict', record.verdict, |
| 492 | ]); |
| 493 | if (!result) return; |
| 494 | const child = spawn(result.cmd, result.cmdArgs, { |
| 495 | stdio: 'ignore', |
| 496 | detached: true, |
| 497 | }); |
| 498 | // unref so this subprocess doesn't hold the event loop open |
| 499 | child.unref(); |
| 500 | child.on('error', () => { /* swallow — telemetry must never break sidebar */ }); |
| 501 | } catch { |
| 502 | // Spawn failure is non-fatal. |
| 503 | } |
| 504 | } |
| 505 | |
| 506 | /** |
| 507 | * Append an attempt to the local log AND fire telemetry via |
no test coverage detected