Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
8e16684
add reference
juliusmarminge Mar 11, 2026
98289a2
remove test file for now
juliusmarminge Mar 11, 2026
7b78c42
remove another test file that just trips the model up
juliusmarminge Mar 11, 2026
61a365a
mirror setup
juliusmarminge Mar 11, 2026
ae2651f
setup http router
juliusmarminge Mar 11, 2026
29fee97
wire in http routes
juliusmarminge Mar 11, 2026
63c7cee
start wiring in rpc
juliusmarminge Mar 11, 2026
aa34e66
add test
juliusmarminge Mar 11, 2026
75e8170
plan out remaining ports
juliusmarminge Mar 11, 2026
9c3a40d
wire up keybindings
juliusmarminge Mar 11, 2026
a5ebd0c
searchEntries
juliusmarminge Mar 11, 2026
70f7adb
finihs phase 2
juliusmarminge Mar 11, 2026
89fc5c1
phase 3 - git - complete
juliusmarminge Mar 11, 2026
0ca441f
phase 4 complete
juliusmarminge Mar 11, 2026
9e3677e
phase 5
juliusmarminge Mar 11, 2026
d984557
prune unused stuff from wsServer.ts
juliusmarminge Mar 11, 2026
84c6b51
add schemas for streaming rpcs
juliusmarminge Mar 11, 2026
95650de
streaming server config
juliusmarminge Mar 11, 2026
c0788c0
remaining
juliusmarminge Mar 13, 2026
7d73b72
type
juliusmarminge Mar 13, 2026
8ea82bc
client
juliusmarminge Mar 14, 2026
80b42f5
Derive server paths from base dir and unify platform layers
juliusmarminge Mar 24, 2026
6295ae6
kewl
juliusmarminge Mar 24, 2026
864c936
rm reference
juliusmarminge Mar 24, 2026
35561a0
stale plan
juliusmarminge Mar 24, 2026
eb8ee3f
Merge origin/main into effect-http-router
juliusmarminge Mar 29, 2026
ee67778
fix stm and sqlite
juliusmarminge Mar 29, 2026
b6105c8
Merge origin/main into effect-http-router
juliusmarminge Mar 29, 2026
ec4226e
mv helper
juliusmarminge Mar 30, 2026
d190fdb
Merge origin/main into effect-http-router
juliusmarminge Mar 30, 2026
dbdc2bf
Use callback queue for terminal event streaming
juliusmarminge Mar 30, 2026
6502a6c
Merge origin/main into effect-http-router
juliusmarminge Mar 30, 2026
b65e74d
Fix RPC browser test websocket mocks
juliusmarminge Mar 30, 2026
0f48b4f
Use Effect RPC in browser test mocks
juliusmarminge Mar 30, 2026
1cf79b5
bump
juliusmarminge Mar 30, 2026
d59a554
merge
juliusmarminge Mar 30, 2026
b6771ca
Move git error types to contracts
juliusmarminge Mar 30, 2026
849a39c
rm reexport shim
juliusmarminge Mar 30, 2026
1f1a3dd
Add bootstrap envelope config precedence
juliusmarminge Mar 31, 2026
f9b0f14
kewl
juliusmarminge Mar 31, 2026
7c692d4
Refactor ServerSettings imports and remove unused error handling
juliusmarminge Mar 31, 2026
0ba3f29
tidy up
juliusmarminge Mar 31, 2026
dd44ef0
rm
juliusmarminge Mar 31, 2026
3656164
rev bun a bit
juliusmarminge Mar 31, 2026
fe8ba96
cool
juliusmarminge Mar 31, 2026
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
1 change: 1 addition & 0 deletions .oxfmtrc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"ignorePatterns": [
".reference",
".plans",
"dist",
"dist-electron",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ function waitFor<A, E>(
read: Effect.Effect<A, E>,
predicate: (value: A) => boolean,
description: string,
timeoutMs = 10_000,
timeoutMs = 40_000,
): Effect.Effect<A, never> {
const RETRY_SIGNAL = "wait_for_retry";
const retryIntervalMs = 10;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function waitForSync<A>(
read: () => A,
predicate: (value: A) => boolean,
description: string,
timeoutMs = 3000,
timeoutMs = 10_000,
): Effect.Effect<A, never> {
return Effect.gen(function* () {
const deadline = Date.now() + timeoutMs;
Expand Down
7 changes: 4 additions & 3 deletions apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,23 @@
"directory": "apps/server"
},
"bin": {
"t3": "./dist/index.mjs"
"t3": "./dist/bin.mjs"
},
"files": [
"dist"
],
"type": "module",
"scripts": {
"dev": "bun run src/index.ts",
"dev": "bun run src/bin.ts",
"build": "node scripts/cli.ts build",
"start": "node dist/index.mjs",
"start": "node dist/bin.mjs",
"prepare": "effect-language-service patch",
"typecheck": "tsc --noEmit",
"test": "vitest run"
},
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.77",
"@effect/platform-bun": "catalog:",
"@effect/platform-node": "catalog:",
"@effect/sql-sqlite-bun": "catalog:",
"@pierre/diffs": "^1.1.0-beta.16",
Expand Down
13 changes: 13 additions & 0 deletions apps/server/src/bin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
import * as NodeServices from "@effect/platform-node/NodeServices";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import { Command } from "effect/unstable/cli";

import { NetService } from "@t3tools/shared/Net";
import { cli } from "./cli";
import { version } from "../package.json" with { type: "json" };

const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, NetService.layer);

Command.run(cli, { version }).pipe(Effect.provide(CliRuntimeLayer), NodeRuntime.runMain);
2 changes: 1 addition & 1 deletion apps/server/src/checkpointing/Errors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Schema } from "effect";
import type { ProjectionRepositoryError } from "../persistence/Errors.ts";
import { GitCommandError } from "../git/Errors.ts";
import { GitCommandError } from "@t3tools/contracts";

/**
* CheckpointUnavailableError - Expected checkpoint does not exist.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { CheckpointStoreLive } from "./CheckpointStore.ts";
import { CheckpointStore } from "../Services/CheckpointStore.ts";
import { GitCoreLive } from "../../git/Layers/GitCore.ts";
import { GitCore } from "../../git/Services/GitCore.ts";
import { GitCommandError } from "../../git/Errors.ts";
import { GitCommandError } from "@t3tools/contracts";
import { ServerConfig } from "../../config.ts";
import { ThreadId } from "@t3tools/contracts";

Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/checkpointing/Layers/CheckpointStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { randomUUID } from "node:crypto";
import { Effect, Layer, FileSystem, Path } from "effect";

import { CheckpointInvariantError } from "../Errors.ts";
import { GitCommandError } from "../../git/Errors.ts";
import { GitCommandError } from "@t3tools/contracts";
import { GitCore } from "../../git/Services/GitCore.ts";
import { CheckpointStore, type CheckpointStoreShape } from "../Services/CheckpointStore.ts";
import { CheckpointRef } from "@t3tools/contracts";
Expand Down
275 changes: 275 additions & 0 deletions apps/server/src/cli-config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
import os from "node:os";

import { assert, expect, it } from "@effect/vitest";
import { ConfigProvider, Effect, FileSystem, Layer, Option, Path } from "effect";

import { NetService } from "@t3tools/shared/Net";
import * as NodeServices from "@effect/platform-node/NodeServices";
import { deriveServerPaths } from "./config";
import { resolveServerConfig } from "./cli";

it.layer(NodeServices.layer)("cli config resolution", (it) => {
const openBootstrapFd = Effect.fn(function* (payload: Record<string, unknown>) {
const fs = yield* FileSystem.FileSystem;
const filePath = yield* fs.makeTempFileScoped({ prefix: "t3-bootstrap-", suffix: ".ndjson" });
yield* fs.writeFileString(filePath, `${JSON.stringify(payload)}\n`);
const { fd } = yield* fs.open(filePath, { flag: "r" });
return fd;
});

it.effect("falls back to effect/config values when flags are omitted", () =>
Effect.gen(function* () {
const { join } = yield* Path.Path;
const baseDir = join(os.tmpdir(), "t3-cli-config-env-base");
const derivedPaths = yield* deriveServerPaths(baseDir, new URL("http://127.0.0.1:5173"));
const resolved = yield* resolveServerConfig(
{
mode: Option.none(),
port: Option.none(),
host: Option.none(),
baseDir: Option.none(),
devUrl: Option.none(),
noBrowser: Option.none(),
authToken: Option.none(),
bootstrapFd: Option.none(),
autoBootstrapProjectFromCwd: Option.none(),
logWebSocketEvents: Option.none(),
},
Option.none(),
).pipe(
Effect.provide(
Layer.mergeAll(
ConfigProvider.layer(
ConfigProvider.fromEnv({
env: {
T3CODE_LOG_LEVEL: "Warn",
T3CODE_MODE: "desktop",
T3CODE_PORT: "4001",
T3CODE_HOST: "0.0.0.0",
T3CODE_HOME: baseDir,
VITE_DEV_SERVER_URL: "http://127.0.0.1:5173",
T3CODE_NO_BROWSER: "true",
T3CODE_AUTH_TOKEN: "env-token",
T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "false",
T3CODE_LOG_WS_EVENTS: "true",
},
}),
),
NetService.layer,
),
),
);

expect(resolved).toEqual({
logLevel: "Warn",
mode: "desktop",
port: 4001,
cwd: process.cwd(),
baseDir,
...derivedPaths,
host: "0.0.0.0",
staticDir: undefined,
devUrl: new URL("http://127.0.0.1:5173"),
noBrowser: true,
authToken: "env-token",
autoBootstrapProjectFromCwd: false,
logWebSocketEvents: true,
});
}),
);

it.effect("uses CLI flags when provided", () =>
Effect.gen(function* () {
const { join } = yield* Path.Path;
const baseDir = join(os.tmpdir(), "t3-cli-config-flags-base");
const derivedPaths = yield* deriveServerPaths(baseDir, new URL("http://127.0.0.1:4173"));
const resolved = yield* resolveServerConfig(
{
mode: Option.some("web"),
port: Option.some(8788),
host: Option.some("127.0.0.1"),
baseDir: Option.some(baseDir),
devUrl: Option.some(new URL("http://127.0.0.1:4173")),
noBrowser: Option.some(true),
authToken: Option.some("flag-token"),
bootstrapFd: Option.none(),
autoBootstrapProjectFromCwd: Option.some(true),
logWebSocketEvents: Option.some(true),
},
Option.some("Debug"),
).pipe(
Effect.provide(
Layer.mergeAll(
ConfigProvider.layer(
ConfigProvider.fromEnv({
env: {
T3CODE_LOG_LEVEL: "Warn",
T3CODE_MODE: "desktop",
T3CODE_PORT: "4001",
T3CODE_HOST: "0.0.0.0",
T3CODE_HOME: join(os.tmpdir(), "ignored-base"),
VITE_DEV_SERVER_URL: "http://127.0.0.1:5173",
T3CODE_NO_BROWSER: "false",
T3CODE_AUTH_TOKEN: "ignored-token",
T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "false",
T3CODE_LOG_WS_EVENTS: "false",
},
}),
),
NetService.layer,
),
),
);

expect(resolved).toEqual({
logLevel: "Debug",
mode: "web",
port: 8788,
cwd: process.cwd(),
baseDir,
...derivedPaths,
host: "127.0.0.1",
staticDir: undefined,
devUrl: new URL("http://127.0.0.1:4173"),
noBrowser: true,
authToken: "flag-token",
autoBootstrapProjectFromCwd: true,
logWebSocketEvents: true,
});
}),
);

it.effect("uses bootstrap envelope values as fallbacks when flags and env are absent", () =>
Effect.gen(function* () {
const { join } = yield* Path.Path;
const baseDir = "/tmp/t3-bootstrap-home";
const fd = yield* openBootstrapFd({
mode: "desktop",
port: 4888,
host: "127.0.0.2",
t3Home: baseDir,
devUrl: "http://127.0.0.1:5173",
noBrowser: true,
authToken: "bootstrap-token",
autoBootstrapProjectFromCwd: false,
logWebSocketEvents: true,
});
const derivedPaths = yield* deriveServerPaths(baseDir, new URL("http://127.0.0.1:5173"));

const resolved = yield* resolveServerConfig(
{
mode: Option.none(),
port: Option.none(),
host: Option.none(),
baseDir: Option.none(),
devUrl: Option.none(),
noBrowser: Option.none(),
authToken: Option.none(),
bootstrapFd: Option.none(),
autoBootstrapProjectFromCwd: Option.none(),
logWebSocketEvents: Option.none(),
},
Option.none(),
).pipe(
Effect.provide(
Layer.mergeAll(
ConfigProvider.layer(
ConfigProvider.fromEnv({
env: {
T3CODE_BOOTSTRAP_FD: String(fd),
},
}),
),
NetService.layer,
),
),
);

expect(resolved).toEqual({
logLevel: "Info",
mode: "desktop",
port: 4888,
cwd: process.cwd(),
baseDir,
...derivedPaths,
host: "127.0.0.2",
staticDir: undefined,
devUrl: new URL("http://127.0.0.1:5173"),
noBrowser: true,
authToken: "bootstrap-token",
autoBootstrapProjectFromCwd: false,
logWebSocketEvents: true,
});
assert.equal(join(baseDir, "dev"), resolved.stateDir);
}),
);

it.effect("applies flag then env precedence over bootstrap envelope values", () =>
Effect.gen(function* () {
const { join } = yield* Path.Path;
const baseDir = join(os.tmpdir(), "t3-cli-config-env-wins");
const fd = yield* openBootstrapFd({
mode: "desktop",
port: 4888,
host: "127.0.0.2",
t3Home: "/tmp/t3-bootstrap-home",
devUrl: "http://127.0.0.1:5173",
noBrowser: false,
authToken: "bootstrap-token",
autoBootstrapProjectFromCwd: false,
logWebSocketEvents: false,
});
const derivedPaths = yield* deriveServerPaths(baseDir, new URL("http://127.0.0.1:4173"));

const resolved = yield* resolveServerConfig(
{
mode: Option.none(),
port: Option.some(8788),
host: Option.some("127.0.0.1"),
baseDir: Option.none(),
devUrl: Option.some(new URL("http://127.0.0.1:4173")),
noBrowser: Option.none(),
authToken: Option.some("flag-token"),
bootstrapFd: Option.none(),
autoBootstrapProjectFromCwd: Option.none(),
logWebSocketEvents: Option.none(),
},
Option.some("Debug"),
).pipe(
Effect.provide(
Layer.mergeAll(
ConfigProvider.layer(
ConfigProvider.fromEnv({
env: {
T3CODE_MODE: "web",
T3CODE_BOOTSTRAP_FD: String(fd),
T3CODE_HOME: baseDir,
T3CODE_NO_BROWSER: "true",
T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "true",
T3CODE_LOG_WS_EVENTS: "true",
},
}),
),
NetService.layer,
),
),
);

expect(resolved).toEqual({
logLevel: "Debug",
mode: "web",
port: 8788,
cwd: process.cwd(),
baseDir,
...derivedPaths,
host: "127.0.0.1",
staticDir: undefined,
devUrl: new URL("http://127.0.0.1:4173"),
noBrowser: true,
authToken: "flag-token",
autoBootstrapProjectFromCwd: true,
logWebSocketEvents: true,
});
}),
);
});
Loading
Loading