594 lines · 20.1 KB · TypeScript
RawDownload
import * as NodeServices from "@effect/platform-node/NodeServices";import {  DEFAULT_SERVER_SETTINGS,  ProviderDriverKind,  ProviderInstanceId,  ServerSettings,  ServerSettingsPatch,} from "@t3tools/contracts";import { createModelSelection } from "@t3tools/shared/model";import { assert, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Duration from "effect/Duration";import * as FileSystem from "effect/FileSystem";import * as Layer from "effect/Layer";import * as PlatformError from "effect/PlatformError";import * as Schema from "effect/Schema";import * as ServerSecretStore from "./auth/ServerSecretStore.ts";import * as ServerConfig from "./config.ts";import * as ServerSettingsModule from "./serverSettings.ts"; const decodeSettingsPatch = Schema.decodeUnknownEffect(ServerSettingsPatch);const decodeServerSettings = Schema.decodeUnknownEffect(ServerSettings); const makeServerSettingsLayer = () =>  ServerSettingsModule.layer.pipe(    Layer.provide(ServerSecretStore.layer),    Layer.provideMerge(      Layer.fresh(        ServerConfig.layerTest(process.cwd(), {          prefix: "t3code-server-settings-test-",        }),      ),    ),  ); const makeFailingSecretStoreLayer = (cause: ServerSecretStore.SecretStoreError) =>  Layer.succeed(    ServerSecretStore.ServerSecretStore,    ServerSecretStore.ServerSecretStore.of({      get: () => Effect.fail(cause),      set: () => Effect.void,      create: () => Effect.void,      getOrCreateRandom: () => Effect.succeed(new Uint8Array()),      remove: () => Effect.void,    }),  ); it.layer(NodeServices.layer)("server settings", (it) => {  it.effect("preserves context when reading a provider environment secret fails", () => {    const platformCause = PlatformError.systemError({      _tag: "PermissionDenied",      module: "FileSystem",      method: "readFile",      pathOrDescriptor: "provider environment secret",      description: "Secret backend unavailable.",    });    const cause = new ServerSecretStore.SecretStoreReadError({      resource: "provider environment secret",      cause: platformCause,    });    const configLayer = Layer.fresh(      ServerConfig.layerTest(process.cwd(), {        prefix: "t3code-server-settings-secret-failure-test-",      }),    );    const settingsLayer = ServerSettingsModule.layer.pipe(      Layer.provide(makeFailingSecretStoreLayer(cause)),      Layer.provideMerge(configLayer),    );     return Effect.gen(function* () {      const serverConfig = yield* ServerConfig.ServerConfig;      const fileSystem = yield* FileSystem.FileSystem;      const serverSettings = yield* ServerSettingsModule.ServerSettingsService;      yield* fileSystem.writeFileString(        serverConfig.settingsPath,        '{"providerInstances":{"codex_personal":{"driver":"codex","environment":[{"name":"OPENROUTER_API_KEY","value":"","sensitive":true,"valueRedacted":true}],"config":{}}}}',      );       const error = yield* Effect.flip(serverSettings.getSettings);       assert.deepInclude(error, {        _tag: "ServerSettingsError",        operation: "read-secret",        providerInstanceId: "codex_personal",        environmentVariable: "OPENROUTER_API_KEY",      });      assert.strictEqual(error.cause, cause);      assert.notInclude(error.message, cause.message);    }).pipe(Effect.provide(settingsLayer));  });   it.effect("decodes nested settings patches", () =>    Effect.gen(function* () {      assert.deepEqual(        yield* decodeSettingsPatch({ providers: { codex: { binaryPath: "/tmp/codex" } } }),        {          providers: { codex: { binaryPath: "/tmp/codex" } },        },      );       assert.deepEqual(        yield* decodeSettingsPatch({          textGenerationModelSelection: {            options: [{ id: "fastMode", value: false }],          },        }),        {          textGenerationModelSelection: {            options: [{ id: "fastMode", value: false }],          },        },      );    }),  );   it.effect(    "decodes legacy object-shaped textGenerationModelSelection.options from settings.json",    () =>      Effect.gen(function* () {        const decoded = yield* decodeServerSettings({          textGenerationModelSelection: {            provider: ProviderDriverKind.make("codex"),            model: "gpt-5.4-mini",            options: { reasoningEffort: "low" },          },        });         assert.deepEqual(decoded.textGenerationModelSelection, {          instanceId: ProviderInstanceId.make("codex"),          model: "gpt-5.4-mini",          options: [{ id: "reasoningEffort", value: "low" }],        });      }),  );   it.effect("deep merges nested settings updates without dropping siblings", () =>    Effect.gen(function* () {      const serverSettings = yield* ServerSettingsModule.ServerSettingsService;       yield* serverSettings.updateSettings({        providers: {          codex: {            binaryPath: "/usr/local/bin/codex",            homePath: "/Users/julius/.codex",          },          claudeAgent: {            binaryPath: "/usr/local/bin/claude",            customModels: ["claude-custom"],          },        },        textGenerationModelSelection: {          instanceId: ProviderInstanceId.make("codex"),          model: DEFAULT_SERVER_SETTINGS.textGenerationModelSelection.model,          options: createModelSelection(            ProviderInstanceId.make("codex"),            DEFAULT_SERVER_SETTINGS.textGenerationModelSelection.model,            [              { id: "reasoningEffort", value: "high" },              { id: "fastMode", value: true },            ],          ).options!,        },      });       const next = yield* serverSettings.updateSettings({        providers: {          codex: {            binaryPath: "/opt/homebrew/bin/codex",          },        },        textGenerationModelSelection: {          options: [{ id: "fastMode", value: false }],        },      });       assert.deepEqual(next.providers.codex, {        enabled: true,        binaryPath: "/opt/homebrew/bin/codex",        homePath: "/Users/julius/.codex",        shadowHomePath: "",        launchArgs: "",        customModels: [],      });      assert.deepEqual(next.providers.claudeAgent, {        enabled: true,        binaryPath: "/usr/local/bin/claude",        homePath: "",        customModels: ["claude-custom"],        launchArgs: "",      });      assert.deepEqual(        next.textGenerationModelSelection,        createModelSelection(          ProviderInstanceId.make("codex"),          DEFAULT_SERVER_SETTINGS.textGenerationModelSelection.model,          [            { id: "reasoningEffort", value: "high" },            { id: "fastMode", value: false },          ],        ),      );    }).pipe(Effect.provide(makeServerSettingsLayer())),  );   it.effect("preserves model when switching providers via textGenerationModelSelection", () =>    Effect.gen(function* () {      const serverSettings = yield* ServerSettingsModule.ServerSettingsService;       // Start with Claude text generation selection      yield* serverSettings.updateSettings({        textGenerationModelSelection: {          instanceId: ProviderInstanceId.make("claudeAgent"),          model: "claude-sonnet-4-6",          options: createModelSelection(            ProviderInstanceId.make("claudeAgent"),            "claude-sonnet-4-6",            [{ id: "effort", value: "high" }],          ).options!,        },      });       // Switch to Codex — the stale Claude "effort" in options must not      // cause the update to lose the selected model.      const next = yield* serverSettings.updateSettings({        textGenerationModelSelection: {          instanceId: ProviderInstanceId.make("codex"),          model: "gpt-5.4",          options: createModelSelection(ProviderInstanceId.make("codex"), "gpt-5.4", [            { id: "reasoningEffort", value: "high" },          ]).options!,        },      });       assert.deepEqual(        next.textGenerationModelSelection,        createModelSelection(ProviderInstanceId.make("codex"), "gpt-5.4", [          { id: "reasoningEffort", value: "high" },        ]),      );    }).pipe(Effect.provide(makeServerSettingsLayer())),  );   it.effect("preserves custom provider instance text generation selections", () =>    Effect.gen(function* () {      const serverSettings = yield* ServerSettingsModule.ServerSettingsService;       const next = yield* serverSettings.updateSettings({        providerInstances: {          [ProviderInstanceId.make("claude_openrouter")]: {            driver: ProviderDriverKind.make("claudeAgent"),            enabled: true,            config: { customModels: ["openai/gpt-5.5"] },          },        },        textGenerationModelSelection: {          instanceId: ProviderInstanceId.make("claude_openrouter"),          model: "openai/gpt-5.5",        },      });       assert.deepEqual(next.textGenerationModelSelection, {        instanceId: ProviderInstanceId.make("claude_openrouter"),        model: "openai/gpt-5.5",      });    }).pipe(Effect.provide(makeServerSettingsLayer())),  );   it.effect(    "uses explicit provider instance enabled state over legacy provider enabled state",    () =>      Effect.gen(function* () {        const serverSettings = yield* ServerSettingsModule.ServerSettingsService;        const instanceId = ProviderInstanceId.make("claude_openrouter");         const next = yield* serverSettings.updateSettings({          providers: {            claudeAgent: {              enabled: false,            },          },          providerInstances: {            [instanceId]: {              driver: ProviderDriverKind.make("claudeAgent"),              enabled: true,              config: { customModels: ["openai/gpt-5.5"] },            },          },          textGenerationModelSelection: {            instanceId,            model: "openai/gpt-5.5",          },        });         assert.deepEqual(next.textGenerationModelSelection, {          instanceId,          model: "openai/gpt-5.5",        });      }).pipe(Effect.provide(makeServerSettingsLayer())),  );   it.effect("preserves enabled text generation selections for non-built-in drivers", () =>    Effect.gen(function* () {      const serverSettings = yield* ServerSettingsModule.ServerSettingsService;      const instanceId = ProviderInstanceId.make("openrouter_text");       const next = yield* serverSettings.updateSettings({        providerInstances: {          [instanceId]: {            driver: ProviderDriverKind.make("openrouter"),            enabled: true,            config: { customModels: ["openai/gpt-5.5"] },          },        },        textGenerationModelSelection: {          instanceId,          model: "openai/gpt-5.5",        },      });       assert.deepEqual(next.textGenerationModelSelection, {        instanceId,        model: "openai/gpt-5.5",      });    }).pipe(Effect.provide(makeServerSettingsLayer())),  );   it.effect("drops stale text generation options when resetting model selection", () =>    Effect.gen(function* () {      const serverSettings = yield* ServerSettingsModule.ServerSettingsService;       yield* serverSettings.updateSettings({        textGenerationModelSelection: {          instanceId: ProviderInstanceId.make("codex"),          model: DEFAULT_SERVER_SETTINGS.textGenerationModelSelection.model,          options: createModelSelection(            ProviderInstanceId.make("codex"),            DEFAULT_SERVER_SETTINGS.textGenerationModelSelection.model,            [              { id: "reasoningEffort", value: "high" },              { id: "fastMode", value: true },            ],          ).options!,        },      });       const next = yield* serverSettings.updateSettings({        textGenerationModelSelection: {          instanceId: DEFAULT_SERVER_SETTINGS.textGenerationModelSelection.instanceId,          model: DEFAULT_SERVER_SETTINGS.textGenerationModelSelection.model,        },      });       assert.deepEqual(next.textGenerationModelSelection, {        instanceId: DEFAULT_SERVER_SETTINGS.textGenerationModelSelection.instanceId,        model: DEFAULT_SERVER_SETTINGS.textGenerationModelSelection.model,      });    }).pipe(Effect.provide(makeServerSettingsLayer())),  );   it.effect("replaces provider instance maps when clearing optional fields", () =>    Effect.gen(function* () {      const serverSettings = yield* ServerSettingsModule.ServerSettingsService;      const codexId = ProviderInstanceId.make("codex");       yield* serverSettings.updateSettings({        providerInstances: {          [codexId]: {            driver: ProviderDriverKind.make("codex"),            displayName: "Codex Work",            accentColor: "#7c3aed",            enabled: true,            config: { homePath: "~/.codex" },          },        },      });       const next = yield* serverSettings.updateSettings({        providerInstances: {          [codexId]: {            driver: ProviderDriverKind.make("codex"),            displayName: "Codex Work",            enabled: true,            config: { homePath: "~/.codex" },          },        },      });       assert.deepEqual(next.providerInstances[codexId], {        driver: ProviderDriverKind.make("codex"),        displayName: "Codex Work",        enabled: true,        config: { homePath: "~/.codex" },      });    }).pipe(Effect.provide(makeServerSettingsLayer())),  );   it.effect("trims provider path settings when updates are applied", () =>    Effect.gen(function* () {      const serverSettings = yield* ServerSettingsModule.ServerSettingsService;       const next = yield* serverSettings.updateSettings({        providers: {          codex: {            binaryPath: "  /opt/homebrew/bin/codex  ",            homePath: "   ",          },          claudeAgent: {            binaryPath: "  /opt/homebrew/bin/claude  ",          },          opencode: {            binaryPath: "  /opt/homebrew/bin/opencode  ",            serverUrl: "  http://127.0.0.1:4096  ",            serverPassword: "  secret-password  ",          },        },      });       assert.deepEqual(next.providers.codex, {        enabled: true,        binaryPath: "/opt/homebrew/bin/codex",        homePath: "",        shadowHomePath: "",        launchArgs: "",        customModels: [],      });      assert.deepEqual(next.providers.claudeAgent, {        enabled: true,        binaryPath: "/opt/homebrew/bin/claude",        homePath: "",        customModels: [],        launchArgs: "",      });      assert.deepEqual(next.providers.opencode, {        enabled: true,        binaryPath: "/opt/homebrew/bin/opencode",        serverUrl: "http://127.0.0.1:4096",        serverPassword: "secret-password",        customModels: [],      });    }).pipe(Effect.provide(makeServerSettingsLayer())),  );   it.effect("trims observability settings when updates are applied", () =>    Effect.gen(function* () {      const serverSettings = yield* ServerSettingsModule.ServerSettingsService;       const next = yield* serverSettings.updateSettings({        addProjectBaseDirectory: "  ~/Development  ",        observability: {          otlpTracesUrl: "  http://localhost:4318/v1/traces  ",          otlpMetricsUrl: "  http://localhost:4318/v1/metrics  ",        },      });       assert.equal(next.addProjectBaseDirectory, "~/Development");      assert.deepEqual(next.observability, {        otlpTracesUrl: "http://localhost:4318/v1/traces",        otlpMetricsUrl: "http://localhost:4318/v1/metrics",      });    }).pipe(Effect.provide(makeServerSettingsLayer())),  );   it.effect("defaults blank binary paths to provider executables", () =>    Effect.gen(function* () {      const serverSettings = yield* ServerSettingsModule.ServerSettingsService;       const next = yield* serverSettings.updateSettings({        providers: {          codex: {            binaryPath: "   ",          },          claudeAgent: {            binaryPath: "",          },        },      });       assert.equal(next.providers.codex.binaryPath, "codex");      assert.equal(next.providers.claudeAgent.binaryPath, "claude");    }).pipe(Effect.provide(makeServerSettingsLayer())),  );   it.effect("writes only non-default server settings to disk", () =>    Effect.gen(function* () {      const serverSettings = yield* ServerSettingsModule.ServerSettingsService;      const serverConfig = yield* ServerConfig.ServerConfig;      const fileSystem = yield* FileSystem.FileSystem;      const next = yield* serverSettings.updateSettings({        addProjectBaseDirectory: "~/Development",        observability: {          otlpTracesUrl: "http://localhost:4318/v1/traces",          otlpMetricsUrl: "http://localhost:4318/v1/metrics",        },        providers: {          codex: {            binaryPath: "/opt/homebrew/bin/codex",          },          opencode: {            serverUrl: "http://127.0.0.1:4096",

Lines 1–500 of 594.