Skip to content

Commit e95b57d

Browse files
[codex] Rewrite client connection architecture (#2978)
Co-authored-by: codex <codex@users.noreply.github.com>
1 parent 3bdaa6e commit e95b57d

606 files changed

Lines changed: 37206 additions & 53661 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -60,52 +60,6 @@ jobs:
6060
- name: Test
6161
run: vp run test
6262

63-
test_browser:
64-
name: Test Browser
65-
runs-on: blacksmith-8vcpu-ubuntu-2404
66-
timeout-minutes: 10
67-
steps:
68-
- name: Checkout
69-
uses: actions/checkout@v6
70-
71-
- name: Setup Vite+
72-
uses: voidzero-dev/setup-vp@v1
73-
with:
74-
node-version-file: package.json
75-
cache: true
76-
run-install: true
77-
78-
- name: Cache Playwright browsers
79-
uses: actions/cache@v5
80-
with:
81-
path: ~/.cache/ms-playwright
82-
key: ${{ runner.os }}-playwright-${{ hashFiles('pnpm-lock.yaml') }}
83-
restore-keys: |
84-
${{ runner.os }}-playwright-
85-
86-
- name: Install browser test runtime
87-
run: vp run --filter @t3tools/web test:browser:install
88-
89-
- name: Browser test / Chat view
90-
working-directory: apps/web
91-
run: vp test run --mode browser --browser=chromium src/components/ChatView.browser.tsx
92-
93-
- name: Browser test / Chat markdown
94-
working-directory: apps/web
95-
run: vp test run --mode browser --browser=chromium src/components/ChatMarkdown.browser.tsx
96-
97-
- name: Browser test / Components
98-
working-directory: apps/web
99-
run: |
100-
vp test run --mode browser --browser=chromium \
101-
src/components/GitActionsControl.browser.tsx \
102-
src/components/KeybindingsToast.browser.tsx \
103-
src/components/ThreadTerminalDrawer.browser.tsx \
104-
src/components/chat/MessagesTimeline.browser.tsx \
105-
src/components/chat/ProviderModelPicker.browser.tsx \
106-
src/components/chat/CompactComposerControlsMenu.browser.tsx \
107-
src/components/settings/SettingsPanels.browser.tsx
108-
10963
mobile_native_static_analysis:
11064
name: Mobile Native Static Analysis
11165
runs-on: blacksmith-12vcpu-macos-26
Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
import * as NodeServices from "@effect/platform-node/NodeServices";
2+
import { assert, describe, it } from "@effect/vitest";
3+
import { ConnectionCatalogDocument } from "@t3tools/client-runtime/platform";
4+
import { EnvironmentId, type PersistedSavedEnvironmentRecord } from "@t3tools/contracts";
5+
import * as Effect from "effect/Effect";
6+
import * as FileSystem from "effect/FileSystem";
7+
import * as Layer from "effect/Layer";
8+
import * as Option from "effect/Option";
9+
import * as PlatformError from "effect/PlatformError";
10+
import * as Ref from "effect/Ref";
11+
import * as Schema from "effect/Schema";
12+
13+
import * as ElectronSafeStorage from "../electron/ElectronSafeStorage.ts";
14+
import * as DesktopSavedEnvironments from "../settings/DesktopSavedEnvironments.ts";
15+
import * as DesktopConfig from "./DesktopConfig.ts";
16+
import * as DesktopConnectionCatalogStore from "./DesktopConnectionCatalogStore.ts";
17+
import * as DesktopEnvironment from "./DesktopEnvironment.ts";
18+
19+
const textDecoder = new TextDecoder();
20+
const textEncoder = new TextEncoder();
21+
const decodeConnectionCatalog = Schema.decodeEffect(
22+
Schema.fromJsonString(ConnectionCatalogDocument),
23+
);
24+
25+
function makeSafeStorageLayer(available: boolean, failDecrypt: Ref.Ref<boolean> | null = null) {
26+
return Layer.succeed(ElectronSafeStorage.ElectronSafeStorage, {
27+
isEncryptionAvailable: Effect.succeed(available),
28+
encryptString: (value) => Effect.succeed(textEncoder.encode(`encrypted:${value}`)),
29+
decryptString: (value) => {
30+
return Effect.gen(function* () {
31+
const decoded = textDecoder.decode(value);
32+
if (
33+
!decoded.startsWith("encrypted:") ||
34+
(failDecrypt !== null && (yield* Ref.get(failDecrypt)))
35+
) {
36+
return yield* new ElectronSafeStorage.ElectronSafeStorageDecryptError({
37+
cause: new Error("invalid encrypted catalog"),
38+
});
39+
}
40+
return decoded.slice("encrypted:".length);
41+
});
42+
},
43+
} satisfies ElectronSafeStorage.ElectronSafeStorageShape);
44+
}
45+
46+
function makeLayer(
47+
baseDir: string,
48+
encryptionAvailable = true,
49+
failDecrypt: Ref.Ref<boolean> | null = null,
50+
fileSystemLayer: Layer.Layer<FileSystem.FileSystem> = NodeServices.layer,
51+
) {
52+
const environmentLayer = DesktopEnvironment.layer({
53+
dirname: "/repo/apps/desktop/src",
54+
homeDirectory: baseDir,
55+
platform: "darwin",
56+
processArch: "arm64",
57+
appVersion: "1.2.3",
58+
appPath: "/repo",
59+
isPackaged: true,
60+
resourcesPath: "/missing/resources",
61+
runningUnderArm64Translation: false,
62+
}).pipe(
63+
Layer.provide(
64+
Layer.mergeAll(NodeServices.layer, DesktopConfig.layerTest({ T3CODE_HOME: baseDir })),
65+
),
66+
);
67+
const safeStorageLayer = makeSafeStorageLayer(encryptionAvailable, failDecrypt);
68+
const dependencies = Layer.mergeAll(
69+
environmentLayer,
70+
safeStorageLayer,
71+
NodeServices.layer,
72+
fileSystemLayer,
73+
);
74+
const savedEnvironmentsLayer = DesktopSavedEnvironments.layer.pipe(
75+
Layer.provideMerge(dependencies),
76+
);
77+
78+
return DesktopConnectionCatalogStore.layer.pipe(
79+
Layer.provideMerge(savedEnvironmentsLayer),
80+
Layer.provideMerge(dependencies),
81+
);
82+
}
83+
84+
const withStore = <A, E, R>(
85+
effect: Effect.Effect<A, E, R | DesktopConnectionCatalogStore.DesktopConnectionCatalogStore>,
86+
encryptionAvailable = true,
87+
) =>
88+
Effect.gen(function* () {
89+
const fileSystem = yield* FileSystem.FileSystem;
90+
const baseDir = yield* fileSystem.makeTempDirectoryScoped({
91+
prefix: "t3-desktop-connection-catalog-test-",
92+
});
93+
return yield* effect.pipe(Effect.provide(makeLayer(baseDir, encryptionAvailable)));
94+
}).pipe(Effect.provide(NodeServices.layer), Effect.scoped);
95+
96+
describe("DesktopConnectionCatalogStore", () => {
97+
it.effect("persists, reads, and clears an encrypted connection catalog", () =>
98+
withStore(
99+
Effect.gen(function* () {
100+
const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore;
101+
const catalog = '{"schemaVersion":1,"targets":[]}';
102+
103+
assert.isTrue(yield* store.set(catalog));
104+
assert.deepStrictEqual(yield* store.get, Option.some(catalog));
105+
106+
yield* store.clear;
107+
assert.deepStrictEqual(yield* store.get, Option.none());
108+
}),
109+
),
110+
);
111+
112+
it.effect("does not persist when secure storage is unavailable", () =>
113+
withStore(
114+
Effect.gen(function* () {
115+
const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore;
116+
assert.isFalse(yield* store.set("{}"));
117+
assert.deepStrictEqual(yield* store.get, Option.none());
118+
}),
119+
false,
120+
),
121+
);
122+
123+
it.effect("migrates legacy relay, SSH, bearer profile, and credential data", () =>
124+
withStore(
125+
Effect.gen(function* () {
126+
const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore;
127+
const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments;
128+
const records: readonly PersistedSavedEnvironmentRecord[] = [
129+
{
130+
environmentId: EnvironmentId.make("relay-environment"),
131+
label: "Relay",
132+
httpBaseUrl: "https://relay.example.com/",
133+
wsBaseUrl: "wss://relay.example.com/",
134+
createdAt: "2026-06-01T00:00:00.000Z",
135+
lastConnectedAt: null,
136+
relayManaged: { relayUrl: "https://relay-control.example.com/" },
137+
},
138+
{
139+
environmentId: EnvironmentId.make("ssh-environment"),
140+
label: "SSH",
141+
httpBaseUrl: "http://127.0.0.1:41773/",
142+
wsBaseUrl: "ws://127.0.0.1:41773/",
143+
createdAt: "2026-06-02T00:00:00.000Z",
144+
lastConnectedAt: null,
145+
desktopSsh: {
146+
alias: "devbox",
147+
hostname: "devbox.example.com",
148+
username: "julius",
149+
port: 22,
150+
},
151+
},
152+
{
153+
environmentId: EnvironmentId.make("bearer-environment"),
154+
label: "Bearer",
155+
httpBaseUrl: "https://bearer.example.com/",
156+
wsBaseUrl: "wss://bearer.example.com/",
157+
createdAt: "2026-06-03T00:00:00.000Z",
158+
lastConnectedAt: null,
159+
},
160+
];
161+
yield* savedEnvironments.setRegistry(records);
162+
assert.isTrue(
163+
yield* savedEnvironments.setSecret({
164+
environmentId: EnvironmentId.make("bearer-environment"),
165+
secret: "legacy-token",
166+
}),
167+
);
168+
169+
const migrated = yield* store.get;
170+
assert.isTrue(Option.isSome(migrated));
171+
if (Option.isNone(migrated)) {
172+
return;
173+
}
174+
const catalog = yield* decodeConnectionCatalog(migrated.value);
175+
176+
assert.deepInclude(catalog.targets[0], {
177+
_tag: "RelayConnectionTarget",
178+
environmentId: EnvironmentId.make("relay-environment"),
179+
label: "Relay",
180+
});
181+
assert.deepInclude(catalog.targets[1], {
182+
_tag: "SshConnectionTarget",
183+
environmentId: EnvironmentId.make("ssh-environment"),
184+
label: "SSH",
185+
connectionId: "ssh:ssh-environment",
186+
});
187+
assert.deepInclude(catalog.targets[2], {
188+
_tag: "BearerConnectionTarget",
189+
environmentId: EnvironmentId.make("bearer-environment"),
190+
label: "Bearer",
191+
connectionId: "bearer:bearer-environment",
192+
});
193+
assert.deepInclude(catalog.profiles[0], {
194+
_tag: "SshConnectionProfile",
195+
connectionId: "ssh:ssh-environment",
196+
environmentId: EnvironmentId.make("ssh-environment"),
197+
label: "SSH",
198+
target: {
199+
alias: "devbox",
200+
hostname: "devbox.example.com",
201+
username: "julius",
202+
port: 22,
203+
},
204+
});
205+
assert.deepInclude(catalog.profiles[1], {
206+
_tag: "BearerConnectionProfile",
207+
connectionId: "bearer:bearer-environment",
208+
environmentId: EnvironmentId.make("bearer-environment"),
209+
label: "Bearer",
210+
httpBaseUrl: "https://bearer.example.com/",
211+
wsBaseUrl: "wss://bearer.example.com/",
212+
});
213+
assert.equal(catalog.credentials.length, 1);
214+
assert.equal(catalog.credentials[0]?.connectionId, "bearer:bearer-environment");
215+
assert.equal(catalog.credentials[0]?.credential._tag, "BearerConnectionCredential");
216+
if (catalog.credentials[0]?.credential._tag === "BearerConnectionCredential") {
217+
assert.equal(catalog.credentials[0].credential.token, "legacy-token");
218+
}
219+
220+
yield* savedEnvironments.setRegistry([]);
221+
assert.deepEqual(yield* store.get, migrated);
222+
}),
223+
),
224+
);
225+
226+
it.effect("surfaces malformed catalog documents without deleting them", () =>
227+
withStore(
228+
Effect.gen(function* () {
229+
const environment = yield* DesktopEnvironment.DesktopEnvironment;
230+
const fileSystem = yield* FileSystem.FileSystem;
231+
const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore;
232+
const catalogPath = `${environment.stateDir}/connection-catalog.json`;
233+
yield* fileSystem.makeDirectory(environment.stateDir, { recursive: true });
234+
yield* fileSystem.writeFileString(catalogPath, "{not-json");
235+
236+
const error = yield* store.get.pipe(Effect.flip);
237+
assert.instanceOf(
238+
error,
239+
DesktopConnectionCatalogStore.DesktopConnectionCatalogStoreReadError,
240+
);
241+
assert.equal(yield* fileSystem.readFileString(catalogPath), "{not-json");
242+
}),
243+
),
244+
);
245+
246+
it.effect("surfaces catalog filesystem failures instead of treating them as missing", () =>
247+
Effect.gen(function* () {
248+
const baseFileSystem = yield* FileSystem.FileSystem;
249+
const baseDir = yield* baseFileSystem.makeTempDirectoryScoped({
250+
prefix: "t3-desktop-connection-catalog-test-",
251+
});
252+
const permissionError = PlatformError.systemError({
253+
_tag: "PermissionDenied",
254+
module: "FileSystem",
255+
method: "readFileString",
256+
pathOrDescriptor: `${baseDir}/connection-catalog.json`,
257+
});
258+
const fileSystemLayer = Layer.succeed(
259+
FileSystem.FileSystem,
260+
FileSystem.makeNoop({
261+
readFileString: () => Effect.fail(permissionError),
262+
}),
263+
);
264+
const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore.pipe(
265+
Effect.provide(makeLayer(baseDir, true, null, fileSystemLayer)),
266+
);
267+
268+
const error = yield* store.get.pipe(Effect.flip);
269+
assert.instanceOf(
270+
error,
271+
DesktopConnectionCatalogStore.DesktopConnectionCatalogStoreReadError,
272+
);
273+
assert.equal(error.cause, permissionError);
274+
}).pipe(Effect.provide(NodeServices.layer), Effect.scoped),
275+
);
276+
277+
it.effect("surfaces a catalog that can no longer be decrypted without deleting it", () =>
278+
Effect.gen(function* () {
279+
const fileSystem = yield* FileSystem.FileSystem;
280+
const baseDir = yield* fileSystem.makeTempDirectoryScoped({
281+
prefix: "t3-desktop-connection-catalog-test-",
282+
});
283+
const failDecrypt = yield* Ref.make(false);
284+
const layer = makeLayer(baseDir, true, failDecrypt);
285+
const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore.pipe(
286+
Effect.provide(layer),
287+
);
288+
289+
assert.isTrue(yield* store.set('{"schemaVersion":1,"targets":[]}'));
290+
yield* Ref.set(failDecrypt, true);
291+
const error = yield* store.get.pipe(Effect.flip);
292+
assert.instanceOf(error, ElectronSafeStorage.ElectronSafeStorageDecryptError);
293+
yield* Ref.set(failDecrypt, false);
294+
assert.deepStrictEqual(yield* store.get, Option.some('{"schemaVersion":1,"targets":[]}'));
295+
}).pipe(Effect.provide(NodeServices.layer), Effect.scoped),
296+
);
297+
});

0 commit comments

Comments
 (0)