Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions apps/desktop/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ const UPDATE_STATE_CHANNEL = "desktop:update-state";
const UPDATE_GET_STATE_CHANNEL = "desktop:update-get-state";
const UPDATE_DOWNLOAD_CHANNEL = "desktop:update-download";
const UPDATE_INSTALL_CHANNEL = "desktop:update-install";
const STATE_DIR =
process.env.T3CODE_STATE_DIR?.trim() || Path.join(OS.homedir(), ".t3", "userdata");
const BASE_DIR = process.env.T3CODE_HOME?.trim() || Path.join(OS.homedir(), ".t3");
const STATE_DIR = Path.join(BASE_DIR, "userdata");
const DESKTOP_SCHEME = "t3";
const ROOT_DIR = Path.resolve(__dirname, "../../..");
const isDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL);
Expand Down Expand Up @@ -924,7 +924,7 @@ function backendEnv(): NodeJS.ProcessEnv {
T3CODE_MODE: "desktop",
T3CODE_NO_BROWSER: "1",
T3CODE_PORT: String(backendPort),
T3CODE_STATE_DIR: STATE_DIR,
T3CODE_HOME: BASE_DIR,
T3CODE_AUTH_TOKEN: backendAuthToken,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ export const makeOrchestrationIntegrationHarness = (
}),
).pipe(
Layer.provide(makeCodexAdapterLive()),
Layer.provideMerge(ServerConfig.layerTest(workspaceDir, stateDir)),
Layer.provideMerge(ServerConfig.layerTest(workspaceDir, stateDir, rootDir)),
Layer.provideMerge(NodeServices.layer),
Layer.provideMerge(providerSessionDirectoryLayer),
);
Expand Down Expand Up @@ -311,7 +311,7 @@ export const makeOrchestrationIntegrationHarness = (
);
const layer = orchestrationReactorLayer.pipe(
Layer.provide(persistenceLayer),
Layer.provideMerge(ServerConfig.layerTest(workspaceDir, stateDir)),
Layer.provideMerge(ServerConfig.layerTest(workspaceDir, stateDir, rootDir)),
Layer.provideMerge(NodeServices.layer),
);

Expand Down
4 changes: 3 additions & 1 deletion apps/server/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface ServerConfigShape {
readonly host: string | undefined;
readonly cwd: string;
readonly keybindingsConfigPath: string;
readonly baseDir: string;
readonly stateDir: string;
readonly staticDir: string | undefined;
readonly devUrl: URL | undefined;
Expand All @@ -36,13 +37,14 @@ export interface ServerConfigShape {
export class ServerConfig extends ServiceMap.Service<ServerConfig, ServerConfigShape>()(
"t3/config/ServerConfig",
) {
static readonly layerTest = (cwd: string, statedir: string) =>
static readonly layerTest = (cwd: string, statedir: string, baseDir: string) =>
Layer.effect(
ServerConfig,
Effect.gen(function* () {
const path = yield* Path.Path;
return {
cwd,
baseDir,
stateDir: statedir,
mode: "web",
autoBootstrapProjectFromCwd: false,
Expand Down
5 changes: 3 additions & 2 deletions apps/server/src/git/Layers/CodexTextGeneration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import * as NodeServices from "@effect/platform-node/NodeServices";
import { it } from "@effect/vitest";
import { Effect, FileSystem, Layer, Path } from "effect";
import { expect } from "vitest";
import path from "node:path";

import { ServerConfig } from "../../config.ts";
import { CodexTextGenerationLive } from "./CodexTextGeneration.ts";
import { TextGenerationError } from "../Errors.ts";
import { TextGeneration } from "../Services/TextGeneration.ts";

const makeCodexTextGenerationTestLayer = (stateDir: string) =>
const makeCodexTextGenerationTestLayer = (baseDir: string) =>
CodexTextGenerationLive.pipe(
Layer.provideMerge(ServerConfig.layerTest(process.cwd(), stateDir)),
Layer.provideMerge(ServerConfig.layerTest(process.cwd(), path.join(baseDir, "userdata"), baseDir)),
Layer.provideMerge(NodeServices.layer),
);

Expand Down
4 changes: 4 additions & 0 deletions apps/server/src/git/Layers/GitCore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ import { GitCoreLive } from "./GitCore.ts";
import { GitCore, type GitCoreShape } from "../Services/GitCore.ts";
import { GitCommandError } from "../Errors.ts";
import { type ProcessRunResult, runProcess } from "../../processRunner.ts";
import { ServerConfig } from "../../config.ts";

// ── Helpers ──

const GitServiceTestLayer = GitServiceLive.pipe(Layer.provide(NodeServices.layer));
const ServerConfigLayer = ServerConfig.layerTest(process.cwd(), process.cwd(), process.cwd());
const GitCoreTestLayer = GitCoreLive.pipe(
Layer.provide(GitServiceTestLayer),
Layer.provide(ServerConfigLayer),
Layer.provide(NodeServices.layer),
);
const TestLayer = Layer.mergeAll(NodeServices.layer, GitServiceTestLayer, GitCoreTestLayer);
Expand Down Expand Up @@ -90,6 +93,7 @@ const makeIsolatedGitCore = (gitService: GitServiceShape) =>
const gitServiceLayer = Layer.succeed(GitService, gitService);
const coreLayer = GitCoreLive.pipe(
Layer.provide(gitServiceLayer),
Layer.provide(ServerConfigLayer),
Layer.provide(NodeServices.layer),
);
const core = await Effect.runPromise(Effect.service(GitCore).pipe(Effect.provide(coreLayer)));
Expand Down
7 changes: 4 additions & 3 deletions apps/server/src/git/Layers/GitCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Cache, Data, Duration, Effect, Exit, FileSystem, Layer, Path } from "ef
import { GitCommandError } from "../Errors.ts";
import { GitService } from "../Services/GitService.ts";
import { GitCore, type GitCoreShape } from "../Services/GitCore.ts";
import { ServerConfig } from "../../config.ts";

const STATUS_UPSTREAM_REFRESH_INTERVAL = Duration.seconds(15);
const STATUS_UPSTREAM_REFRESH_TIMEOUT = Duration.seconds(5);
Expand Down Expand Up @@ -221,6 +222,8 @@ const makeGitCore = Effect.gen(function* () {
const git = yield* GitService;
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const { baseDir } = yield* ServerConfig;
const worktreesDir = path.join(baseDir, "worktrees");

const executeGit = (
operation: string,
Expand Down Expand Up @@ -1186,9 +1189,7 @@ const makeGitCore = Effect.gen(function* () {
const targetBranch = input.newBranch ?? input.branch;
const sanitizedBranch = targetBranch.replace(/\//g, "-");
const repoName = path.basename(input.cwd);
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "/tmp";
const worktreePath =
input.path ?? path.join(homeDir, ".t3", "worktrees", repoName, sanitizedBranch);
const worktreePath = input.path ?? path.join(worktreesDir, repoName, sanitizedBranch);
const args = input.newBranch
? ["worktree", "add", "-b", input.newBranch, worktreePath, input.branch]
: ["worktree", "add", worktreePath, input.branch];
Expand Down
6 changes: 5 additions & 1 deletion apps/server/src/git/Layers/GitManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { GitServiceLive } from "./GitService.ts";
import { GitService } from "../Services/GitService.ts";
import { GitCoreLive } from "./GitCore.ts";
import { makeGitManager } from "./GitManager.ts";
import { ServerConfig } from "../../config.ts";

interface FakeGhScenario {
prListSequence?: string[];
Expand Down Expand Up @@ -474,17 +475,20 @@ function makeManager(input?: {
}) {
const { service: gitHubCli, ghCalls } = createGitHubCliWithFakeGh(input?.ghScenario);
const textGeneration = createTextGeneration(input?.textGeneration);
const ServerConfigLayer = ServerConfig.layerTest(process.cwd(), process.cwd(), process.cwd());

const gitCoreLayer = GitCoreLive.pipe(
Layer.provideMerge(GitServiceLive),
Layer.provideMerge(NodeServices.layer),
Layer.provideMerge(ServerConfigLayer),
);

const managerLayer = Layer.mergeAll(
Layer.succeed(GitHubCli, gitHubCli),
Layer.succeed(TextGeneration, textGeneration),
gitCoreLayer,
NodeServices.layer,
).pipe(
Layer.provideMerge(NodeServices.layer),
);

return makeGitManager.pipe(
Expand Down
14 changes: 7 additions & 7 deletions apps/server/src/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,11 @@ const runCli = (
args: ReadonlyArray<string>,
env: Record<string, string> = { T3CODE_NO_BROWSER: "true" },
) => {
const uniqueStateDir = `/tmp/t3-cli-state-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
return Command.runWith(t3Cli, { version: "0.0.0-test" })(args).pipe(
Effect.provide(
ConfigProvider.layer(
ConfigProvider.fromEnv({
env: {
T3CODE_STATE_DIR: uniqueStateDir,
...env,
},
}),
Expand All @@ -93,8 +91,8 @@ it.layer(testLayer)("server CLI command", (it) => {
"4010",
"--host",
"0.0.0.0",
"--state-dir",
"/tmp/t3-cli-state",
"--home-dir",
"/tmp/t3-cli-home",
"--dev-url",
"http://127.0.0.1:5173",
"--no-browser",
Expand All @@ -106,7 +104,8 @@ it.layer(testLayer)("server CLI command", (it) => {
assert.equal(resolvedConfig?.mode, "desktop");
assert.equal(resolvedConfig?.port, 4010);
assert.equal(resolvedConfig?.host, "0.0.0.0");
assert.equal(resolvedConfig?.stateDir, "/tmp/t3-cli-state");
assert.equal(resolvedConfig?.baseDir, "/tmp/t3-cli-home");
assert.equal(resolvedConfig?.stateDir, "/tmp/t3-cli-home/userdata");
assert.equal(resolvedConfig?.devUrl?.toString(), "http://127.0.0.1:5173/");
assert.equal(resolvedConfig?.noBrowser, true);
assert.equal(resolvedConfig?.authToken, "auth-secret");
Expand All @@ -131,7 +130,7 @@ it.layer(testLayer)("server CLI command", (it) => {
T3CODE_MODE: "desktop",
T3CODE_PORT: "4999",
T3CODE_HOST: "100.88.10.4",
T3CODE_STATE_DIR: "/tmp/t3-env-state",
T3CODE_HOME: "/tmp/t3-env-home",
VITE_DEV_SERVER_URL: "http://localhost:5173",
T3CODE_NO_BROWSER: "true",
T3CODE_AUTH_TOKEN: "env-token",
Expand All @@ -141,7 +140,8 @@ it.layer(testLayer)("server CLI command", (it) => {
assert.equal(resolvedConfig?.mode, "desktop");
assert.equal(resolvedConfig?.port, 4999);
assert.equal(resolvedConfig?.host, "100.88.10.4");
assert.equal(resolvedConfig?.stateDir, "/tmp/t3-env-state");
assert.equal(resolvedConfig?.baseDir, "/tmp/t3-env-home");
assert.equal(resolvedConfig?.stateDir, "/tmp/t3-env-home/userdata");
assert.equal(resolvedConfig?.devUrl?.toString(), "http://localhost:5173/");
assert.equal(resolvedConfig?.noBrowser, true);
assert.equal(resolvedConfig?.authToken, "env-token");
Expand Down
22 changes: 10 additions & 12 deletions apps/server/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
type RuntimeMode,
type ServerConfigShape,
} from "./config";
import { fixPath, resolveStateDir } from "./os-jank";
import { fixPath, resolveBaseDir, resolveStateDir } from "./os-jank";
import { Open } from "./open";
import * as SqlitePersistence from "./persistence/Layers/Sqlite";
import { makeServerProviderLayer, makeServerRuntimeServicesLayer } from "./serverLayers";
Expand All @@ -36,7 +36,7 @@ interface CliInput {
readonly mode: Option.Option<RuntimeMode>;
readonly port: Option.Option<number>;
readonly host: Option.Option<string>;
readonly stateDir: Option.Option<string>;
readonly t3Home: Option.Option<string>;
readonly devUrl: Option.Option<URL>;
readonly noBrowser: Option.Option<boolean>;
readonly authToken: Option.Option<string>;
Expand Down Expand Up @@ -99,10 +99,7 @@ const CliEnvConfig = Config.all({
),
port: Config.port("T3CODE_PORT").pipe(Config.option, Config.map(Option.getOrUndefined)),
host: Config.string("T3CODE_HOST").pipe(Config.option, Config.map(Option.getOrUndefined)),
stateDir: Config.string("T3CODE_STATE_DIR").pipe(
Config.option,
Config.map(Option.getOrUndefined),
),
t3Home: Config.string("T3CODE_HOME").pipe(Config.option, Config.map(Option.getOrUndefined)),
devUrl: Config.url("VITE_DEV_SERVER_URL").pipe(Config.option, Config.map(Option.getOrUndefined)),
noBrowser: Config.boolean("T3CODE_NO_BROWSER").pipe(
Config.option,
Expand Down Expand Up @@ -152,10 +149,10 @@ const ServerConfigLive = (input: CliInput) =>
return findAvailablePort(DEFAULT_PORT);
},
});
const stateDir = yield* resolveStateDir(
Option.getOrUndefined(input.stateDir) ?? env.stateDir,
);

const devUrl = Option.getOrElse(input.devUrl, () => env.devUrl);
const baseDir = yield* resolveBaseDir(Option.getOrUndefined(input.t3Home) ?? env.t3Home);
const stateDir = yield* resolveStateDir(baseDir, devUrl);
const noBrowser = resolveBooleanFlag(input.noBrowser, env.noBrowser ?? mode === "desktop");
const authToken = Option.getOrUndefined(input.authToken) ?? env.authToken;
const autoBootstrapProjectFromCwd = resolveBooleanFlag(
Expand All @@ -180,6 +177,7 @@ const ServerConfigLive = (input: CliInput) =>
cwd: cliConfig.cwd,
keybindingsConfigPath,
host,
baseDir,
stateDir,
staticDir,
devUrl,
Expand Down Expand Up @@ -299,8 +297,8 @@ const hostFlag = Flag.string("host").pipe(
Flag.withDescription("Host/interface to bind (for example 127.0.0.1, 0.0.0.0, or a Tailnet IP)."),
Flag.optional,
);
const stateDirFlag = Flag.string("state-dir").pipe(
Flag.withDescription("State directory path (equivalent to T3CODE_STATE_DIR)."),
const t3HomeFlag = Flag.string("home-dir").pipe(
Flag.withDescription("Base directory for all T3 Code data (equivalent to T3CODE_HOME)."),
Flag.optional,
);
const devUrlFlag = Flag.string("dev-url").pipe(
Expand Down Expand Up @@ -335,7 +333,7 @@ export const t3Cli = Command.make("t3", {
mode: modeFlag,
port: portFlag,
host: hostFlag,
stateDir: stateDirFlag,
t3Home: t3HomeFlag,
devUrl: devUrlFlag,
noBrowser: noBrowserFlag,
authToken: authTokenFlag,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ describe("CheckpointReactor", () => {
Layer.provideMerge(RuntimeReceiptBusLive),
Layer.provideMerge(Layer.succeed(ProviderService, provider.service)),
Layer.provideMerge(CheckpointStoreLive),
Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd())),
Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd(), process.cwd())),
Layer.provideMerge(NodeServices.layer),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ async function createOrchestrationSystem() {
Layer.provide(OrchestrationEventStoreLive),
Layer.provide(OrchestrationCommandReceiptRepositoryLive),
Layer.provide(SqlitePersistenceMemory),
Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd())),
Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd(), process.cwd())),
Layer.provideMerge(NodeServices.layer),
);
const runtime = ManagedRuntime.make(orchestrationLayer);
Expand Down Expand Up @@ -323,7 +323,7 @@ describe("OrchestrationEngine", () => {
Layer.provide(Layer.succeed(OrchestrationEventStore, flakyStore)),
Layer.provide(OrchestrationCommandReceiptRepositoryLive),
Layer.provide(SqlitePersistenceMemory),
Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd())),
Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd(), process.cwd())),
Layer.provideMerge(NodeServices.layer),
),
);
Expand Down
Loading