mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
The desktop "Export diagnostics" path rebuilt the support bundle in the Electron main process and had to GUESS the daemon data dir from the sidecar namespace layout (`<namespaceRoot>/data`). That guess is wrong whenever the daemon's real RUNTIME_DATA_DIR differs: in tools-dev it is `<projectRoot>/.od` (OD_DATA_DIR unset), and under any OD_DATA_DIR override it is elsewhere. The exporter then swept the wrong `runs/` and `amr/opencode-home` trees and silently dropped the per-run `events.jsonl` (which already records every agent stderr chunk, tool call, and error) and the AMR OpenCode logs — the exact data needed to explain a stuck/looping run that produced nothing. The daemon already exposes a complete, correctly-pathed bundle at `GET /api/diagnostics/export` (it knows its own RUNTIME_DATA_DIR and collects run events, agent CLI logs, sidecar daemon/web/desktop logs incl. renderer.log, and host crash reports). So delegate to it instead of duplicating the assembly: - desktop: new electron-free `diagnostics-fetch.ts` fetches the daemon bundle; `diagnostics.ts` keeps only the save dialog + reveal and saves those bytes. Removes the data-dir guessing entirely (net -141 lines in diagnostics.ts). - index.ts: share one `resolveDaemonBaseUrl(runtime, options)` (daemon URL → web URL → sidecar web discovery) across app-config menu actions and the diagnostics IPC, so the export targets the same daemon as everything else. - daemon: emit a manifest warning when `runsDir` is set but no per-run event logs were found, so an empty bundle reads as "collection gap / persistence off" rather than "clean run with nothing to report". Tests: desktop `fetchDiagnosticsBundle` hits the daemon export endpoint and surfaces non-200 as an error; daemon export warns on an empty runs dir. Both red before the change.
28 lines
1.3 KiB
TypeScript
28 lines
1.3 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
|
|
import { fetchDiagnosticsBundle } from "../../src/main/diagnostics-fetch.js";
|
|
|
|
describe("fetchDiagnosticsBundle", () => {
|
|
it("GETs the daemon's own diagnostics export endpoint and returns its bytes", async () => {
|
|
// Regression for the desktop-ipc export that rebuilt the bundle with a
|
|
// GUESSED data dir (`<namespaceRoot>/data`) and so missed the daemon's real
|
|
// `runs/<id>/events.jsonl` + AMR logs in dev / OD_DATA_DIR-override setups.
|
|
// The fix delegates to the daemon, which knows its real RUNTIME_DATA_DIR.
|
|
const bytes = new Uint8Array([0x50, 0x4b, 0x03, 0x04]); // "PK\x03\x04" (zip magic)
|
|
const fetchImpl = vi.fn(async () => new Response(bytes, { status: 200 }));
|
|
|
|
const buffer = await fetchDiagnosticsBundle("http://127.0.0.1:50022", { fetchImpl });
|
|
|
|
expect(fetchImpl).toHaveBeenCalledWith("http://127.0.0.1:50022/api/diagnostics/export");
|
|
expect(Buffer.from(buffer)).toEqual(Buffer.from(bytes));
|
|
});
|
|
|
|
it("throws on a non-200 response so the user sees a real error, not a truncated zip", async () => {
|
|
const fetchImpl = vi.fn(async () => new Response("unavailable", { status: 503 }));
|
|
|
|
await expect(
|
|
fetchDiagnosticsBundle("http://127.0.0.1:50022", { fetchImpl }),
|
|
).rejects.toThrow(/503/);
|
|
});
|
|
});
|