Skip to content

Commit a9f0cc5

Browse files
leorivastechclaude
andcommitted
Merge main into fix/17071-antigravity-usage-attempts
main moved the Antigravity reader to provider/Drivers and its tests to antigravityUsageReader.test.ts (#17576, #17579, #17615). The fix is carried over unchanged: the attempt folding stays in metadata() and the id-less pairing now lives in usageCandidates(), which takes the database path for the local key. The five tests move to the new test file in its Effect style. Co-authored-by: Claude Opus 5.5 <[email protected]>
2 parents b132c28 + 5ce5a8f commit a9f0cc5

1,249 files changed

Lines changed: 50406 additions & 17246 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.

‎AGENTS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ You can think of T3 Code as an open source "bring-your-own-subscription" alterna
66

77
## What makes T3 Code special?
88

9-
We have over 400,000 users who love T3 Code. It's important we maintain the things they love as we continue to iterate on the product. Here's a brief list of the things we can never compromise on.
9+
We have over 500,000 users who love T3 Code. It's important we maintain the things they love as we continue to iterate on the product. Here's a brief list of the things we can never compromise on.
1010

1111
### 1. Open at the core
1212

‎README.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ Install the latest version of the desktop app from [GitHub Releases](https://git
4848
winget install T3Tools.T3Code
4949
```
5050

51+
#### Windows (`scoop`)
52+
53+
```bash
54+
scoop install extras/t3code
55+
```
56+
5157
#### macOS (Homebrew)
5258

5359
```bash

‎apps/desktop/package.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@
2727
"electron": "44.4.5",
2828
"electron-store": "^8.2.0",
2929
"electron-updater": "^6.8.9",
30+
"electron-webauthn": "catalog:",
3031
"ffi-rs": "1.3.2",
3132
"playwright-core": "1.60.0",
32-
"react-grab": "^0.1.32"
33+
"react-grab": "^0.1.32",
34+
"tldts": "catalog:"
3335
},
3436
"devDependencies": {
3537
"@effect/vitest": "catalog:",

‎apps/desktop/src/app/DesktopApp.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import * as DesktopAppIdentity from "./DesktopAppIdentity.ts";
1717
import * as DesktopClerk from "./DesktopClerk.ts";
1818
import * as DesktopApplicationMenu from "../window/DesktopApplicationMenu.ts";
1919
import * as DesktopWindow from "../window/DesktopWindow.ts";
20+
import * as PreviewPasskeys from "../preview/Passkeys.ts";
2021
import * as DesktopBackendPool from "../backend/DesktopBackendPool.ts";
2122
import * as DesktopEnvironment from "./DesktopEnvironment.ts";
2223
import * as DesktopLegacyLocalStorage from "./DesktopLegacyLocalStorage.ts";
@@ -34,6 +35,7 @@ import * as DesktopUpdates from "../updates/DesktopUpdates.ts";
3435
import * as DesktopSnapShot from "../snapShot/DesktopSnapShot.ts";
3536
import * as DesktopWslBackend from "../wsl/DesktopWslBackend.ts";
3637
import * as DesktopRendererHistory from "../telemetry/DesktopRendererHistory.ts";
38+
import * as HostProcess from "@t3tools/shared/HostProcess";
3739

3840
const DEFAULT_DESKTOP_BACKEND_PORT = 3773;
3941
const MAX_TCP_PORT = 65_535;
@@ -276,13 +278,16 @@ const startup = Effect.gen(function* () {
276278
const safeStorage = yield* ElectronSafeStorage.ElectronSafeStorage;
277279
const updates = yield* DesktopUpdates.DesktopUpdates;
278280
const environment = yield* DesktopEnvironment.DesktopEnvironment;
281+
const previewPasskeys = yield* PreviewPasskeys.PreviewPasskeys;
279282

280283
yield* shellEnvironment.installIntoProcess;
281284
const hasCommandLinePasswordStore =
282285
preReadyElectronOptions.linuxPasswordStoreCommandLine !== null;
283286
const linuxElectronOptions =
284287
environment.platform === "linux" && !hasCommandLinePasswordStore
285-
? DesktopPreReadyPlatform.resolveEarlyLinuxElectronOptionsFromProcess()
288+
? DesktopPreReadyPlatform.resolveEarlyLinuxElectronOptionsFromProcess(
289+
yield* HostProcess.HomeDirectory,
290+
)
286291
: preReadyElectronOptions.linux;
287292
if (linuxElectronOptions !== null && !hasCommandLinePasswordStore) {
288293
if (
@@ -329,6 +334,7 @@ const startup = Effect.gen(function* () {
329334
});
330335
}
331336
yield* appIdentity.configure;
337+
yield* previewPasskeys.configure;
332338
yield* applicationMenu.configure;
333339
yield* updates.configure;
334340
yield* DesktopRemoteUpdates.listen;

‎apps/desktop/src/app/DesktopAppActivation.test.ts‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
type DesktopAppActivationResponse,
1212
} from "@t3tools/contracts";
1313
import { resolveDesktopAppControlAddress } from "@t3tools/shared/desktopAppControl";
14-
import { HostProcessPlatform, HostProcessUserId } from "@t3tools/shared/hostProcess";
14+
import * as HostProcess from "@t3tools/shared/HostProcess";
1515
import { it } from "@effect/vitest";
1616
import * as Effect from "effect/Effect";
1717
import { afterEach, describe, expect } from "vite-plus/test";
@@ -83,8 +83,8 @@ function exchange(address: string, payload: DesktopAppActivationRequest) {
8383
describe("desktop app control server", () => {
8484
it.effect("roundtrips a request and removes its socket on shutdown", () =>
8585
Effect.gen(function* () {
86-
const platform = yield* HostProcessPlatform;
87-
const userId = yield* HostProcessUserId;
86+
const platform = yield* HostProcess.Platform;
87+
const userId = yield* HostProcess.UserId;
8888
yield* Effect.promise(async () => {
8989
const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-app-control-test-"));
9090
const target = makeTarget(NodePath.join(root, "userdata"), platform, userId);
@@ -123,8 +123,8 @@ describe("desktop app control server", () => {
123123

124124
it.effect("cancels a queued request when the client disconnects", () =>
125125
Effect.gen(function* () {
126-
const platform = yield* HostProcessPlatform;
127-
const userId = yield* HostProcessUserId;
126+
const platform = yield* HostProcess.Platform;
127+
const userId = yield* HostProcess.UserId;
128128
yield* Effect.promise(async () => {
129129
const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-app-cancel-test-"));
130130
const target = makeTarget(NodePath.join(root, "userdata"), platform, userId);
@@ -162,8 +162,8 @@ describe("desktop app control server", () => {
162162
// Two desktop apps can share one state dir, such as nightly and a preview build.
163163
it.effect("keeps a newer app's socket when an older app on the same state dir quits", () =>
164164
Effect.gen(function* () {
165-
const platform = yield* HostProcessPlatform;
166-
const userId = yield* HostProcessUserId;
165+
const platform = yield* HostProcess.Platform;
166+
const userId = yield* HostProcess.UserId;
167167
if (platform === "win32") return;
168168
yield* Effect.promise(async () => {
169169
const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-app-takeover-test-"));
@@ -183,8 +183,8 @@ describe("desktop app control server", () => {
183183

184184
it.effect("binds its address again after the socket file is removed", () =>
185185
Effect.gen(function* () {
186-
const platform = yield* HostProcessPlatform;
187-
const userId = yield* HostProcessUserId;
186+
const platform = yield* HostProcess.Platform;
187+
const userId = yield* HostProcess.UserId;
188188
if (platform === "win32") return;
189189
yield* Effect.promise(async () => {
190190
const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-app-reclaim-test-"));

‎apps/desktop/src/app/DesktopAppActivation.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
type DesktopAppActivationResponse,
1313
} from "@t3tools/contracts";
1414
import { resolveDesktopAppControlAddress } from "@t3tools/shared/desktopAppControl";
15-
import { HostProcessUserId } from "@t3tools/shared/hostProcess";
15+
import * as HostProcess from "@t3tools/shared/HostProcess";
1616
import * as Context from "effect/Context";
1717
import * as Effect from "effect/Effect";
1818
import * as Layer from "effect/Layer";
@@ -315,7 +315,7 @@ export const make = Effect.gen(function* () {
315315
const desktopWindow = yield* DesktopWindow.DesktopWindow;
316316
const electronWindow = yield* ElectronWindow.ElectronWindow;
317317
const path = yield* Path.Path;
318-
const userId = yield* HostProcessUserId;
318+
const userId = yield* HostProcess.UserId;
319319
const runPromise = Effect.runPromiseWith(yield* Effect.context<never>());
320320
const address = resolveDesktopAppControlAddress({
321321
stateDir: path.resolve(desktopEnvironment.stateDir),

‎apps/desktop/src/app/DesktopClerk.test.ts‎

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as NodeHttp from "node:http";
33
import * as NodePath from "@effect/platform-node/NodePath";
44
import { codexAuthHandoffUrl, readCodexAuthDelivery } from "@t3tools/shared/codexAuthHandoff";
55
import { EnvironmentId, ProviderInstanceId } from "@t3tools/contracts";
6-
import { HostProcessArguments } from "@t3tools/shared/hostProcess";
6+
import * as HostProcess from "@t3tools/shared/HostProcess";
77
import { assert, describe, it } from "@effect/vitest";
88
import * as Cause from "effect/Cause";
99
import * as Effect from "effect/Effect";
@@ -35,9 +35,16 @@ import * as ElectronApp from "../electron/ElectronApp.ts";
3535
import * as ElectronShell from "../electron/ElectronShell.ts";
3636
import * as ElectronWindow from "../electron/ElectronWindow.ts";
3737
import * as DesktopClerk from "./DesktopClerk.ts";
38+
import * as DesktopWebLinks from "./DesktopWebLinks.ts";
3839
import * as DesktopEnvironment from "./DesktopEnvironment.ts";
3940
import * as DesktopPreReadyFileSystem from "./DesktopPreReadyFileSystem.ts";
4041

42+
/** Clerk forwards web links; tests that are not about links ignore them. */
43+
const ignoreWebLinks = DesktopWebLinks.DesktopWebLinks.of({
44+
receive: () => Effect.void,
45+
setRendererReady: () => Effect.void,
46+
});
47+
4148
const layerDesktopClerk = (
4249
isDevelopment = true,
4350
events: string[] = [],
@@ -220,11 +227,12 @@ describe("DesktopClerk", () => {
220227

221228
assert.isTrue(Exit.isSuccess(exit));
222229
assert.equal(quit.mock.calls.length, 0);
223-
assert.deepEqual(registeredEvents, ["open-url", "second-instance"]);
230+
assert.deepEqual(registeredEvents, ["open-url", "open-file", "second-instance"]);
224231
}).pipe(
225232
Effect.provide(layerDesktopClerk()),
226233
Effect.provideService(ElectronApp.ElectronApp, electronApp),
227234
Effect.provideService(ElectronWindow.ElectronWindow, electronWindow),
235+
Effect.provideService(DesktopWebLinks.DesktopWebLinks, ignoreWebLinks),
228236
);
229237
});
230238

@@ -253,6 +261,7 @@ describe("DesktopClerk", () => {
253261
Effect.provide(layerDesktopClerk()),
254262
Effect.provideService(ElectronApp.ElectronApp, electronApp),
255263
Effect.provideService(ElectronWindow.ElectronWindow, electronWindow),
264+
Effect.provideService(DesktopWebLinks.DesktopWebLinks, ignoreWebLinks),
256265
);
257266
});
258267
});
@@ -299,6 +308,7 @@ it.effect(
299308
Effect.provide(layerDesktopClerk()),
300309
Effect.provideService(ElectronApp.ElectronApp, electronApp),
301310
Effect.provideService(ElectronWindow.ElectronWindow, electronWindow),
311+
Effect.provideService(DesktopWebLinks.DesktopWebLinks, ignoreWebLinks),
302312
);
303313
},
304314
);
@@ -375,12 +385,60 @@ it.effect.each(["startup", "open-url"] as const)(
375385
assert.strictEqual(delivery?.returnUrl, request.returnUrl);
376386
}).pipe(
377387
Effect.provide(layerDesktopClerk(true, [], "darwin", undefined, shell)),
378-
Effect.provideService(HostProcessArguments, entry === "startup" ? ["t3", link] : ["t3"]),
388+
Effect.provideService(HostProcess.Arguments, entry === "startup" ? ["t3", link] : ["t3"]),
379389
Effect.provideService(ElectronApp.ElectronApp, electronApp),
380390
Effect.provideService(
381391
ElectronWindow.ElectronWindow,
382392
{} as ElectronWindow.ElectronWindow["Service"],
383393
),
394+
Effect.provideService(DesktopWebLinks.DesktopWebLinks, ignoreWebLinks),
384395
);
385396
}).pipe(Effect.scoped),
386397
);
398+
399+
it.effect("hands a web link to the renderer and leaves other links alone", () =>
400+
Effect.gen(function* () {
401+
storageMock.mockReturnValue(storageAdapter);
402+
createClerkBridgeMock.mockReturnValue({ cleanup: vi.fn(), isPrimaryInstance: true });
403+
const listeners = new Map<string, (...args: unknown[]) => void>();
404+
const electronApp = {
405+
whenReady: Effect.void,
406+
on: (name: string, listener: (...args: unknown[]) => void) =>
407+
Effect.sync(() => {
408+
listeners.set(name, listener);
409+
}),
410+
} as unknown as ElectronApp.ElectronApp["Service"];
411+
const received: Array<string> = [];
412+
yield* Effect.gen(function* () {
413+
const clerk = yield* DesktopClerk.DesktopClerk;
414+
yield* clerk.configure;
415+
const open = (url: string) => {
416+
const event = { preventDefault: vi.fn() };
417+
listeners.get("open-url")!(event, url);
418+
return event.preventDefault.mock.calls.length;
419+
};
420+
// macOS hands the default browser every web link.
421+
assert.strictEqual(open("https://example.com/page"), 1);
422+
assert.strictEqual(open("http://localhost:3000/"), 1);
423+
// Anything else is not a web page; Electron keeps its own handling.
424+
assert.strictEqual(open("mailto:[email protected]"), 0);
425+
yield* Effect.yieldNow;
426+
assert.deepStrictEqual(received, ["https://example.com/page", "http://localhost:3000/"]);
427+
}).pipe(
428+
Effect.provide(layerDesktopClerk(true, [], "darwin")),
429+
Effect.provideService(HostProcess.Arguments, ["t3"]),
430+
Effect.provideService(ElectronApp.ElectronApp, electronApp),
431+
Effect.provideService(
432+
ElectronWindow.ElectronWindow,
433+
{} as ElectronWindow.ElectronWindow["Service"],
434+
),
435+
Effect.provideService(
436+
DesktopWebLinks.DesktopWebLinks,
437+
DesktopWebLinks.DesktopWebLinks.of({
438+
receive: (url) => Effect.sync(() => void received.push(url)),
439+
setRendererReady: () => Effect.void,
440+
}),
441+
),
442+
);
443+
}).pipe(Effect.scoped),
444+
);

‎apps/desktop/src/app/DesktopClerk.ts‎

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createClerkBridge } from "@clerk/electron";
2+
import * as NodeURL from "node:url";
23
import { storage } from "@clerk/electron/storage";
34
import * as Context from "effect/Context";
45
import * as Effect from "effect/Effect";
@@ -11,13 +12,14 @@ import { codexAuthDeliveryUrl, readCodexAuthHandoff } from "@t3tools/shared/code
1112
import { receiveCodexAuthCallback, CodexAuthCallbackError } from "./CodexAuthCallback.ts";
1213
import * as ElectronShell from "../electron/ElectronShell.ts";
1314
import { providerAuthReturnUrl } from "@t3tools/shared/providerAuthReturnUrl";
14-
import { HostProcessArguments } from "@t3tools/shared/hostProcess";
15+
import * as HostProcess from "@t3tools/shared/HostProcess";
1516
import { clerkFrontendApiHostnameFromPublishableKey } from "@t3tools/shared/relayAuth";
1617
import * as ElectronApp from "../electron/ElectronApp.ts";
1718
import * as ElectronProtocol from "../electron/ElectronProtocol.ts";
1819
import * as ElectronWindow from "../electron/ElectronWindow.ts";
1920
import * as DesktopUserData from "./DesktopUserData.ts";
2021
import * as DesktopEnvironment from "./DesktopEnvironment.ts";
22+
import * as DesktopWebLinks from "./DesktopWebLinks.ts";
2123

2224
declare const __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__: string | undefined;
2325

@@ -53,7 +55,10 @@ export class DesktopClerk extends Context.Service<
5355
readonly configure: Effect.Effect<
5456
void,
5557
never,
56-
ElectronApp.ElectronApp | ElectronWindow.ElectronWindow | Scope.Scope
58+
| ElectronApp.ElectronApp
59+
| ElectronWindow.ElectronWindow
60+
| DesktopWebLinks.DesktopWebLinks
61+
| Scope.Scope
5762
>;
5863
}
5964
>()("@t3tools/desktop/app/DesktopClerk") {}
@@ -125,6 +130,7 @@ export const make = Effect.gen(function* () {
125130
configure: Effect.gen(function* () {
126131
const electronApp = yield* ElectronApp.ElectronApp;
127132
const electronWindow = yield* ElectronWindow.ElectronWindow;
133+
const webLinks = yield* DesktopWebLinks.DesktopWebLinks;
128134
const context = yield* Effect.context<ElectronWindow.ElectronWindow>();
129135
const runPromise = Effect.runPromiseWith(context);
130136

@@ -182,10 +188,23 @@ export const make = Effect.gen(function* () {
182188
);
183189
return true;
184190
};
185-
const args = yield* HostProcessArguments;
191+
const args = yield* HostProcess.Arguments;
186192
args.some((value) => startProviderAuthHandoff(value));
193+
// As the default browser, macOS hands T3 Code every web link through the same event.
194+
const openWebLink = (url: string) => {
195+
if (!DesktopWebLinks.isWebLink(url)) return false;
196+
void runPromise(webLinks.receive(url));
197+
return true;
198+
};
187199
yield* electronApp.on("open-url", (event: { preventDefault: () => void }, url: string) => {
188-
if (startProviderAuthHandoff(url) || resumeProviderAuth(url)) event.preventDefault();
200+
if (startProviderAuthHandoff(url) || resumeProviderAuth(url) || openWebLink(url))
201+
event.preventDefault();
202+
});
203+
// A browser opens HTML files too, which macOS hands over by path.
204+
yield* electronApp.on("open-file", (event: { preventDefault: () => void }, path: string) => {
205+
if (!DesktopWebLinks.isWebPageFile(path)) return;
206+
event.preventDefault();
207+
void runPromise(webLinks.receive(NodeURL.pathToFileURL(path).href));
189208
});
190209
yield* electronApp.on("second-instance", (_event: unknown, argv: readonly string[]) => {
191210
if (argv?.some((value) => startProviderAuthHandoff(value) || resumeProviderAuth(value)))

‎apps/desktop/src/app/DesktopPreReadyFileSystem.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as NodeServices from "@effect/platform-node/NodeServices";
22
import { assert, it } from "@effect/vitest";
3-
import { HostProcessPlatform } from "@t3tools/shared/hostProcess";
3+
import * as HostProcess from "@t3tools/shared/HostProcess";
44
import * as Effect from "effect/Effect";
55
import * as Exit from "effect/Exit";
66
import * as FileSystem from "effect/FileSystem";
@@ -32,7 +32,7 @@ it.layer(NodeServices.layer)("DesktopPreReadyFileSystem", (it) => {
3232
}),
3333
);
3434

35-
it.effect.skipIf(HostProcessPlatform.defaultValue() === "win32" || process.getuid?.() === 0)(
35+
it.effect.skipIf(HostProcess.Platform.defaultValue() === "win32" || process.getuid?.() === 0)(
3636
"fails instead of treating an unreadable profile as missing",
3737
() =>
3838
Effect.gen(function* () {

0 commit comments

Comments
 (0)