Skip to content

Commit bfe695f

Browse files
authored
Merge pull request #260 from pylon-code/upstream/2026-09-03-batch-1
upstream: adopt the clean half of the 2026-09-03 T3 batch (65 commits)
2 parents b8e5774 + 95a169e commit bfe695f

392 files changed

Lines changed: 9951 additions & 9230 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/VOUCHED.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,29 @@
99
# -github:username reason for denouncement
1010
#
1111
# Keep entries sorted alphabetically.
12+
github:0x4bs3nt
13+
github:Adamulek123
1214
github:adityavardhansharma
1315
github:binbandit
1416
github:chuks-qua
1517
github:cursoragent
1618
github:gbarros-dev
1719
github:github-actions[bot]
1820
github:hwanseoc
21+
github:ipanasenko
1922
github:jamesx0416
2023
github:jasonLaster
2124
github:JoeEverest
2225
github:maria-rcks
26+
github:maxwellyoung
27+
github:nateEc
2328
github:nmggithub
2429
github:Noojuno
2530
github:notkainoa
2631
github:PatrickBauer
2732
github:realAhmedRoach
2833
github:shiroyasha9
34+
github:tsouth89
2935
github:Yash-Singh1
3036
github:eggfriedrice24
3137
github:Ymit24

apps/desktop/src/app/DesktopApp.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import * as ElectronDialog from "../electron/ElectronDialog.ts";
1111
import * as ElectronProtocol from "../electron/ElectronProtocol.ts";
1212
import * as ElectronSafeStorage from "../electron/ElectronSafeStorage.ts";
1313
import { installDesktopIpcHandlers } from "../ipc/DesktopIpcHandlers.ts";
14+
import * as DesktopAppActivation from "./DesktopAppActivation.ts";
1415
import * as DesktopAppIdentity from "./DesktopAppIdentity.ts";
1516
import * as DesktopClerk from "./DesktopClerk.ts";
1617
import * as DesktopApplicationMenu from "../window/DesktopApplicationMenu.ts";
@@ -148,6 +149,7 @@ const bootstrap = Effect.gen(function* () {
148149
const serverExposure = yield* DesktopServerExposure.DesktopServerExposure;
149150
const wslBackend = yield* DesktopWslBackend.DesktopWslBackend;
150151
const desktopWindow = yield* DesktopWindow.DesktopWindow;
152+
const appActivation = yield* DesktopAppActivation.DesktopAppActivation;
151153
yield* logBootstrapInfo("bootstrap start");
152154

153155
if (environment.isDevelopment && Option.isNone(environment.configuredBackendPort)) {
@@ -210,6 +212,10 @@ const bootstrap = Effect.gen(function* () {
210212
}
211213
yield* primaryBackend.start;
212214
yield* logBootstrapInfo("bootstrap backend start requested");
215+
yield* appActivation.start.pipe(
216+
Effect.tap(() => logBootstrapInfo("desktop app control socket ready")),
217+
Effect.catch((error) => logStartupError("desktop app control socket unavailable", { error })),
218+
);
213219
// Bring up the WSL backend if the user previously enabled it. The
214220
// primary is already starting; reconcile fires off the WSL register
215221
// in parallel rather than blocking primary readiness on a possibly
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// @effect-diagnostics nodeBuiltinImport:off -- This adapter test binds a real local socket or Windows named pipe and verifies its cleanup.
2+
import * as NodeFSP from "node:fs/promises";
3+
import * as NodeNet from "node:net";
4+
import * as NodeOS from "node:os";
5+
import * as NodePath from "node:path";
6+
7+
import {
8+
ProjectId,
9+
ThreadId,
10+
type DesktopAppActivationRequest,
11+
type DesktopAppActivationResponse,
12+
} from "@t3tools/contracts";
13+
import { resolveDesktopAppControlAddress } from "@t3tools/shared/desktopAppControl";
14+
import { HostProcessPlatform, HostProcessUserId } from "@t3tools/shared/hostProcess";
15+
import { it } from "@effect/vitest";
16+
import * as Effect from "effect/Effect";
17+
import { afterEach, describe, expect } from "vite-plus/test";
18+
19+
import { startDesktopAppControlServer } from "./DesktopAppActivation.ts";
20+
21+
const openServers: Array<{ close: () => Promise<void> }> = [];
22+
23+
afterEach(async () => {
24+
await Promise.all(openServers.splice(0).map((server) => server.close()));
25+
});
26+
27+
function makeTarget(stateDir: string, platform: NodeJS.Platform, userId: number | undefined) {
28+
return resolveDesktopAppControlAddress({
29+
stateDir,
30+
platform,
31+
tempDir: NodeOS.tmpdir(),
32+
userId,
33+
joinPath: NodePath.join,
34+
});
35+
}
36+
37+
function request(requestId: string, platform: NodeJS.Platform): DesktopAppActivationRequest {
38+
return {
39+
version: 1,
40+
requestId,
41+
type: "open-workspace",
42+
workspaceRoot: NodePath.join(NodeOS.tmpdir(), "project"),
43+
platform: platform === "win32" ? "win32" : platform === "darwin" ? "darwin" : "linux",
44+
};
45+
}
46+
47+
function exchange(address: string, payload: DesktopAppActivationRequest) {
48+
return new Promise<DesktopAppActivationResponse>((resolve, reject) => {
49+
const socket = NodeNet.createConnection(address);
50+
socket.setEncoding("utf8");
51+
let buffer = "";
52+
socket.once("error", reject);
53+
socket.once("connect", () => socket.write(`${JSON.stringify(payload)}\n`));
54+
socket.on("data", (chunk) => {
55+
buffer += chunk;
56+
const newline = buffer.indexOf("\n");
57+
if (newline === -1) return;
58+
socket.destroy();
59+
resolve(JSON.parse(buffer.slice(0, newline)) as DesktopAppActivationResponse);
60+
});
61+
});
62+
}
63+
64+
describe("desktop app control server", () => {
65+
it.effect("roundtrips a request and removes its socket on shutdown", () =>
66+
Effect.gen(function* () {
67+
const platform = yield* HostProcessPlatform;
68+
const userId = yield* HostProcessUserId;
69+
yield* Effect.promise(async () => {
70+
const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-app-control-test-"));
71+
const target = makeTarget(NodePath.join(root, "userdata"), platform, userId);
72+
const received: DesktopAppActivationRequest[] = [];
73+
const server = await startDesktopAppControlServer({
74+
...target,
75+
userId,
76+
handle: async (input) => {
77+
received.push(input);
78+
return {
79+
version: 1,
80+
requestId: input.requestId,
81+
ok: true,
82+
projectId: ProjectId.make("project-1"),
83+
threadId: ThreadId.make("thread-1"),
84+
};
85+
},
86+
cancel: () => undefined,
87+
});
88+
openServers.push(server);
89+
90+
const response = await exchange(target.address, request("request-1", platform));
91+
92+
expect(received).toHaveLength(1);
93+
expect(response).toMatchObject({ ok: true, requestId: "request-1" });
94+
await server.close();
95+
openServers.splice(openServers.indexOf(server), 1);
96+
if (target.directory !== null) {
97+
await expect(NodeFSP.stat(target.address)).rejects.toMatchObject({ code: "ENOENT" });
98+
}
99+
await NodeFSP.rm(root, { recursive: true, force: true });
100+
});
101+
}),
102+
);
103+
104+
it.effect("cancels a queued request when the client disconnects", () =>
105+
Effect.gen(function* () {
106+
const platform = yield* HostProcessPlatform;
107+
const userId = yield* HostProcessUserId;
108+
yield* Effect.promise(async () => {
109+
const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-app-cancel-test-"));
110+
const target = makeTarget(NodePath.join(root, "userdata"), platform, userId);
111+
let resolveCanceled: (requestId: string) => void = () => undefined;
112+
const canceled = new Promise<string>((resolve) => {
113+
resolveCanceled = resolve;
114+
});
115+
const server = await startDesktopAppControlServer({
116+
...target,
117+
userId,
118+
handle: () => new Promise(() => undefined),
119+
cancel: resolveCanceled,
120+
});
121+
openServers.push(server);
122+
const socket = NodeNet.createConnection(target.address);
123+
await new Promise<void>((resolve, reject) => {
124+
socket.once("error", reject);
125+
socket.once("connect", () => {
126+
socket.write(`${JSON.stringify(request("request-canceled", platform))}\n`, () => {
127+
socket.destroy();
128+
resolve();
129+
});
130+
});
131+
});
132+
133+
await expect(canceled).resolves.toBe("request-canceled");
134+
await server.close();
135+
openServers.splice(openServers.indexOf(server), 1);
136+
await NodeFSP.rm(root, { recursive: true, force: true });
137+
});
138+
}),
139+
);
140+
});

0 commit comments

Comments
 (0)