| author | |
| committer | |
| log | 72b960fa3985ec61af6a459431861a5dcb360009 |
| tree | b14c631e3f2facdc481d562d4526d50cd657266e |
| parent | 08e4932f0465327968368b22be343b58a07d476a |
| signature | Signed by PGP key B5690EEEBB952194 |
2 files changed, 68 insertions(+), 18 deletions(-)
scripts/build-desktop-artifact.test.ts+48-3| ... | @@ -13,7 +13,8 @@ import { | ... | @@ -13,7 +13,8 @@ import { |
| 13 | createStageWorkspaceConfig, | 13 | createStageWorkspaceConfig, |
| 14 | createStagePatchedDependencies, | 14 | createStagePatchedDependencies, |
| 15 | createBuildConfig, | 15 | createBuildConfig, |
| 16 | DESKTOP_ASAR_UNPACK, | 16 | DESKTOP_ELECTRON_LANGUAGES, |
| 17 | DESKTOP_FILE_EXCLUSIONS, | ||
| 17 | InvalidMacPasskeyRpDomainError, | 18 | InvalidMacPasskeyRpDomainError, |
| 18 | InvalidMacPasskeyPublishableKeyError, | 19 | InvalidMacPasskeyPublishableKeyError, |
| 19 | InvalidMockUpdateServerPortError, | 20 | InvalidMockUpdateServerPortError, |
| ... | @@ -37,6 +38,7 @@ import { | ... | @@ -37,6 +38,7 @@ import { |
| 37 | resolvePackageManagerUserAgent, | 38 | resolvePackageManagerUserAgent, |
| 38 | stageLinuxIconSize, | 39 | stageLinuxIconSize, |
| 39 | STAGE_INSTALL_ARGS, | 40 | STAGE_INSTALL_ARGS, |
| 41 | WINDOWS_ASAR_UNPACK, | ||
| 40 | } from "./build-desktop-artifact.ts"; | 42 | } from "./build-desktop-artifact.ts"; |
| 41 | import { BRAND_ASSET_PATHS } from "./lib/brand-assets.ts"; | 43 | import { BRAND_ASSET_PATHS } from "./lib/brand-assets.ts"; |
| 42 | import { HostProcessArchitecture, HostProcessPlatform } from "@t3tools/shared/hostProcess"; | 44 | import { HostProcessArchitecture, HostProcessPlatform } from "@t3tools/shared/hostProcess"; |
| ... | @@ -303,10 +305,53 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { | ... | @@ -303,10 +305,53 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 303 | ); | 305 | ); |
| 304 | }); | 306 | }); |
| 305 | 307 | ||
| 306 | it("unpacks the fff shared library for filesystem and FFI access", () => { | 308 | it("limits Electron locales and excludes the unused Claude SDK executable", () => { |
| 307 | assert.deepStrictEqual(DESKTOP_ASAR_UNPACK, ["node_modules/@ff-labs/fff-bin-*/**/*"]); | 309 | assert.deepStrictEqual(DESKTOP_ELECTRON_LANGUAGES, ["en-US"]); |
| 310 | assert.deepStrictEqual(DESKTOP_FILE_EXCLUSIONS, [ | ||
| 311 | "!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*", | ||
| 312 | ]); | ||
| 308 | }); | 313 | }); |
| 309 | 314 | ||
| 315 | it.effect("applies platform-specific packaging to the build config", () => | ||
| 316 | Effect.gen(function* () { | ||
| 317 | const mac = yield* createBuildConfig( | ||
| 318 | "mac", | ||
| 319 | "dmg", | ||
| 320 | "1.2.3", | ||
| 321 | false, | ||
| 322 | false, | ||
| 323 | undefined, | ||
| 324 | undefined, | ||
| 325 | ); | ||
| 326 | const linux = yield* createBuildConfig( | ||
| 327 | "linux", | ||
| 328 | "AppImage", | ||
| 329 | "1.2.3", | ||
| 330 | false, | ||
| 331 | false, | ||
| 332 | undefined, | ||
| 333 | undefined, | ||
| 334 | ); | ||
| 335 | const win = yield* createBuildConfig( | ||
| 336 | "win", | ||
| 337 | "nsis", | ||
| 338 | "1.2.3", | ||
| 339 | false, | ||
| 340 | false, | ||
| 341 | undefined, | ||
| 342 | undefined, | ||
| 343 | ); | ||
| 344 | |||
| 345 | assert.notProperty(mac, "asarUnpack"); | ||
| 346 | assert.notProperty(linux, "asarUnpack"); | ||
| 347 | assert.deepStrictEqual(win.asarUnpack, WINDOWS_ASAR_UNPACK); | ||
| 348 | for (const config of [mac, linux, win]) { | ||
| 349 | assert.deepStrictEqual(config.electronLanguages, DESKTOP_ELECTRON_LANGUAGES); | ||
| 350 | assert.deepStrictEqual(config.files, DESKTOP_FILE_EXCLUSIONS); | ||
| 351 | } | ||
| 352 | }).pipe(Effect.provide(ConfigProvider.layer(ConfigProvider.fromEnv({ env: {} })))), | ||
| 353 | ); | ||
| 354 | |||
| 310 | it.effect("preserves both Linux icon resize failures with structural context", () => { | 355 | it.effect("preserves both Linux icon resize failures with structural context", () => { |
| 311 | const commands: Array<{ readonly command: string; readonly args: ReadonlyArray<string> }> = []; | 356 | const commands: Array<{ readonly command: string; readonly args: ReadonlyArray<string> }> = []; |
| 312 | 357 |
scripts/build-desktop-artifact.ts+20-15| ... | @@ -579,7 +579,20 @@ interface StagePackageJson { | ... | @@ -579,7 +579,20 @@ interface StagePackageJson { |
| 579 | } | 579 | } |
| 580 | 580 | ||
| 581 | export const STAGE_INSTALL_ARGS = ["install", "--prod"] as const; | 581 | export const STAGE_INSTALL_ARGS = ["install", "--prod"] as const; |
| 582 | export const DESKTOP_ASAR_UNPACK = ["node_modules/@ff-labs/fff-bin-*/**/*"] as const; | 582 | export const DESKTOP_ELECTRON_LANGUAGES = ["en-US"] as const; |
| 583 | export const DESKTOP_FILE_EXCLUSIONS = [ | ||
| 584 | // T3 Code always passes the user's installed Claude executable to the SDK, | ||
| 585 | // so the SDK's optional platform packages (each a ~200MB bundled executable) | ||
| 586 | // are dead weight. The trailing dash keeps the SDK's own JS package. | ||
| 587 | "!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*", | ||
| 588 | ] as const; | ||
| 589 | // The WSL backend launches the server with plain `wsl.exe -- node`, which | ||
| 590 | // cannot read inside an asar archive — and the server bundle externalizes its | ||
| 591 | // runtime deps, so the whole node_modules tree must be unpacked, not just the | ||
| 592 | // bundle (otherwise ERR_MODULE_NOT_FOUND: "Cannot find package 'effect'"). | ||
| 593 | // The Windows primary backend reads the same files through the asar redirect, | ||
| 594 | // so nothing is duplicated. | ||
| 595 | export const WINDOWS_ASAR_UNPACK = ["apps/server/dist/**", "**/node_modules/**"] as const; | ||
| 583 | 596 | ||
| 584 | export interface MacPasskeySigningConfiguration { | 597 | export interface MacPasskeySigningConfiguration { |
| 585 | readonly appId: string; | 598 | readonly appId: string; |
| ... | @@ -1390,23 +1403,15 @@ export const createBuildConfig = Effect.fn("createBuildConfig")(function* ( | ... | @@ -1390,23 +1403,15 @@ export const createBuildConfig = Effect.fn("createBuildConfig")(function* ( |
| 1390 | appId: DESKTOP_APP_ID, | 1403 | appId: DESKTOP_APP_ID, |
| 1391 | productName: resolveDesktopProductName(version), | 1404 | productName: resolveDesktopProductName(version), |
| 1392 | artifactName: "T3-Code-${version}-${arch}.${ext}", | 1405 | artifactName: "T3-Code-${version}-${arch}.${ext}", |
| 1406 | electronLanguages: [...DESKTOP_ELECTRON_LANGUAGES], | ||
| 1407 | files: [...DESKTOP_FILE_EXCLUSIONS], | ||
| 1393 | directories: { | 1408 | directories: { |
| 1394 | buildResources: "apps/desktop/resources", | 1409 | buildResources: "apps/desktop/resources", |
| 1395 | }, | 1410 | }, |
| 1396 | // The Windows primary backend runs the server bundle through | 1411 | // Only the Windows WSL backend needs files outside the asar (see |
| 1397 | // ELECTRON_RUN_AS_NODE (asar-aware), so it reads bin.mjs straight out of | 1412 | // WINDOWS_ASAR_UNPACK); macOS and Linux stay packed — smart unpack |
| 1398 | // app.asar. The WSL backend instead launches plain `wsl.exe -- node`, which | 1413 | // extracts native libraries, which fff-node finds in app.asar.unpacked. |
| 1399 | // cannot read inside an asar archive, so everything it loads must be on the | 1414 | ...(platform === "win" ? { asarUnpack: [...WINDOWS_ASAR_UNPACK] } : {}), |
| 1400 | // real filesystem. The server bundle externalizes its runtime dependencies | ||
| 1401 | // (effect, @effect/*, node-pty, ...) to node_modules rather than inlining | ||
| 1402 | // them, so unpacking just the bundle + node-pty isn't enough — the Linux Node | ||
| 1403 | // fails with ERR_MODULE_NOT_FOUND (e.g. "Cannot find package 'effect'") before | ||
| 1404 | // it even reaches node-pty. Unpack the server bundle AND the whole | ||
| 1405 | // node_modules tree so every import resolves (this also covers the fff native | ||
| 1406 | // binaries in DESKTOP_ASAR_UNPACK). The Windows primary keeps reading the same | ||
| 1407 | // files through the asar (transparently redirected to the unpacked copy), so | ||
| 1408 | // there's no duplication. | ||
| 1409 | asarUnpack: [...DESKTOP_ASAR_UNPACK, "apps/server/dist/**", "**/node_modules/**"], | ||
| 1410 | }; | 1415 | }; |
| 1411 | const updateChannel = resolveDesktopUpdateChannel(version); | 1416 | const updateChannel = resolveDesktopUpdateChannel(version); |
| 1412 | const publishConfig = yield* resolveGitHubPublishConfig(updateChannel); | 1417 | const publishConfig = yield* resolveGitHubPublishConfig(updateChannel); |