From 5a7ccefc68e3d7359bfd9a6262701641da380d37 Mon Sep 17 00:00:00 2001 From: N0zoM1z0 <1705004398@qq.com> Date: Tue, 14 Jul 2026 19:18:30 +0800 Subject: [PATCH] fix: preserve setup diagnostics and clean Knip config --- knip.json | 15 ++---------- src/application/SetupInstallFailure.ts | 34 +++++++++++++++++++++++++- src/config.ts | 9 ++++--- tests/config.test.ts | 13 ++++++++++ tests/setupInstallFailure.test.ts | 4 +++ 5 files changed, 58 insertions(+), 17 deletions(-) diff --git a/knip.json b/knip.json index bdff845d3..a93c36685 100644 --- a/knip.json +++ b/knip.json @@ -1,18 +1,7 @@ { "$schema": "./node_modules/knip/schema.json", - "entry": [ - "src/main.ts", - "src/cli.ts", - "scripts/rea.mjs", - "scripts/scan-todos.mjs" - ], - "project": [ - "src/**/*.ts", - "tests/**/*.ts", - "scripts/**/*.mjs", - "vitest.config.ts" - ], - "ignore": ["dist/**", "docs/**", ".codex/**", ".devin/**"], + "entry": ["src/main.ts", "src/cli.ts"], + "project": ["src/**/*.ts", "tests/**/*.ts", "scripts/**/*.mjs"], "ignoreDependencies": [], "rules": { "files": "error", diff --git a/src/application/SetupInstallFailure.ts b/src/application/SetupInstallFailure.ts index 763b01538..3fd7c3718 100644 --- a/src/application/SetupInstallFailure.ts +++ b/src/application/SetupInstallFailure.ts @@ -6,7 +6,11 @@ export type SetupFailureCode = | "launcher_missing" | "runtime_dependency_unavailable" | "unsupported_hopper_build" - | "setup_cancelled"; + | "setup_cancelled" + | "destination_exists" + | "mount_failed" + | "bundle_invalid" + | "copy_failed"; export type SetupHopperInstallResult = | { readonly status: "installed"; readonly launcherPath: string } @@ -84,6 +88,34 @@ export const setupInstallFailure = ( remediation: "Setup was cancelled before Hopper was installed. Rerun rea setup when ready.", }; + case "destination_exists": + return { + status: "failed", + code: "destination_exists", + remediation: + "The Hopper installation destination already exists and was not replaced. Reuse the existing installation or move it aside, then rerun rea setup.", + }; + case "mount": + return { + status: "failed", + code: "mount_failed", + remediation: + "The verified Hopper disk image could not be mounted. Eject any stale Hopper image, check local disk access, then rerun rea setup.", + }; + case "bundle": + return { + status: "failed", + code: "bundle_invalid", + remediation: + "The mounted package did not contain the expected Hopper application bundle. Retry setup; if it repeats, update REA before installing.", + }; + case "copy": + return { + status: "failed", + code: "copy_failed", + remediation: + "The verified Hopper application could not be copied to the installation destination. Check destination permissions and free space, then rerun rea setup.", + }; default: return { status: "failed", diff --git a/src/config.ts b/src/config.ts index 109e5a6e9..ad81f50ed 100644 --- a/src/config.ts +++ b/src/config.ts @@ -108,9 +108,12 @@ const parseStringArray = ( return parsed.success ? ok(parsed.data) : err( - new ConfigurationError(`${name} must encode an array of strings`, { - cause: parsed.error, - }), + new ConfigurationError( + parsed.error.issues.some(({ code }) => code === "too_big") + ? `${name} must encode at most 128 strings` + : `${name} must encode an array of strings`, + { cause: parsed.error }, + ), ); } catch (cause: unknown) { return err(new ConfigurationError(`${name} must be valid JSON`, { cause })); diff --git a/tests/config.test.ts b/tests/config.test.ts index 3d88afabd..542113700 100644 --- a/tests/config.test.ts +++ b/tests/config.test.ts @@ -125,6 +125,19 @@ describe("runtime configuration", () => { }, ); + it("reports the configured string-array size limit", () => { + const result = parseConfig({ + REA_INVESTIGATION_INPUT_ROOTS_JSON: JSON.stringify( + Array.from({ length: 129 }, (_, index) => `/input/${String(index)}`), + ), + }); + expect(result.ok).toBe(false); + if (!result.ok) + expect(result.error.message).toBe( + "REA_INVESTIGATION_INPUT_ROOTS_JSON must encode at most 128 strings", + ); + }); + it("parses supported log levels and rejects unknown levels", () => { const configured = parseConfig({ REA_LOG_LEVEL: "debug" }); expect(configured.ok && configured.value.logLevel).toBe("debug"); diff --git a/tests/setupInstallFailure.test.ts b/tests/setupInstallFailure.test.ts index 3cdd0f421..0fafb9812 100644 --- a/tests/setupInstallFailure.test.ts +++ b/tests/setupInstallFailure.test.ts @@ -19,6 +19,10 @@ describe("setup installer failure projection", () => { ["runtime_dependencies", "runtime_dependency_unavailable"], ["unsupported_hopper_build", "unsupported_hopper_build"], ["cancelled", "setup_cancelled"], + ["destination_exists", "destination_exists"], + ["mount", "mount_failed"], + ["bundle", "bundle_invalid"], + ["copy", "copy_failed"], ] as const)("maps %s to %s", (reason, code) => { const result = setupInstallFailure(reason as HopperInstallFailureReason); expect(result).toMatchObject({ status: "failed", code });