@chat-js/cli 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/dist/index.js +1163 -959
  2. package/package.json +1 -1
  3. package/templates/chat-app/app/(auth)/device-login/page.tsx +37 -0
  4. package/templates/chat-app/app/(auth)/login/page.tsx +26 -2
  5. package/templates/chat-app/app/(auth)/register/page.tsx +0 -12
  6. package/templates/chat-app/app/(chat)/api/chat/filter-reasoning-parts.ts +1 -1
  7. package/templates/chat-app/app/(chat)/api/chat/route.ts +13 -5
  8. package/templates/chat-app/app/(chat)/layout.tsx +4 -1
  9. package/templates/chat-app/app/api/trpc/[trpc]/route.ts +1 -0
  10. package/templates/chat-app/app/globals.css +9 -9
  11. package/templates/chat-app/app/layout.tsx +4 -2
  12. package/templates/chat-app/biome.jsonc +3 -3
  13. package/templates/chat-app/chat.config.ts +144 -141
  14. package/templates/chat-app/components/ai-elements/prompt-input.tsx +1 -1
  15. package/templates/chat-app/components/anonymous-session-init.tsx +10 -6
  16. package/templates/chat-app/components/artifact-actions.tsx +81 -18
  17. package/templates/chat-app/components/artifact-panel.tsx +142 -41
  18. package/templates/chat-app/components/attachment-list.tsx +1 -1
  19. package/templates/chat-app/components/{social-auth-providers.tsx → auth-providers.tsx} +49 -4
  20. package/templates/chat-app/components/chat/chat-welcome.tsx +3 -3
  21. package/templates/chat-app/components/chat-menu-items.tsx +1 -1
  22. package/templates/chat-app/components/chat-sync.tsx +3 -8
  23. package/templates/chat-app/components/console.tsx +9 -9
  24. package/templates/chat-app/components/context-usage.tsx +2 -2
  25. package/templates/chat-app/components/create-artifact.tsx +15 -5
  26. package/templates/chat-app/components/data-stream-handler.tsx +57 -16
  27. package/templates/chat-app/components/device-login-page.tsx +191 -0
  28. package/templates/chat-app/components/diffview.tsx +8 -2
  29. package/templates/chat-app/components/electron-auth-handler.tsx +184 -0
  30. package/templates/chat-app/components/electron-auth-ui.tsx +121 -0
  31. package/templates/chat-app/components/favicon-group.tsx +1 -1
  32. package/templates/chat-app/components/feedback-actions.tsx +1 -1
  33. package/templates/chat-app/components/greeting.tsx +1 -1
  34. package/templates/chat-app/components/interactive-chart-impl.tsx +3 -4
  35. package/templates/chat-app/components/interactive-charts.tsx +1 -1
  36. package/templates/chat-app/components/login-form.tsx +52 -10
  37. package/templates/chat-app/components/message-editor.tsx +4 -5
  38. package/templates/chat-app/components/model-selector.tsx +661 -655
  39. package/templates/chat-app/components/multimodal-input.tsx +13 -10
  40. package/templates/chat-app/components/parallel-response-cards.tsx +53 -35
  41. package/templates/chat-app/components/part/code-execution.tsx +8 -2
  42. package/templates/chat-app/components/part/document-common.tsx +1 -1
  43. package/templates/chat-app/components/part/document-preview.tsx +5 -5
  44. package/templates/chat-app/components/part/retrieve-url.tsx +12 -12
  45. package/templates/chat-app/components/part/text-message-part.tsx +13 -9
  46. package/templates/chat-app/components/project-chat-item.tsx +1 -1
  47. package/templates/chat-app/components/project-menu-items.tsx +1 -1
  48. package/templates/chat-app/components/research-task.tsx +1 -1
  49. package/templates/chat-app/components/research-tasks.tsx +1 -1
  50. package/templates/chat-app/components/retry-button.tsx +1 -1
  51. package/templates/chat-app/components/sandbox.tsx +1 -1
  52. package/templates/chat-app/components/sheet-editor.tsx +7 -7
  53. package/templates/chat-app/components/sidebar-chats-list.tsx +1 -1
  54. package/templates/chat-app/components/sidebar-toggle.tsx +15 -2
  55. package/templates/chat-app/components/sidebar-top-row.tsx +27 -12
  56. package/templates/chat-app/components/sidebar-user-nav.tsx +10 -1
  57. package/templates/chat-app/components/signup-form.tsx +49 -10
  58. package/templates/chat-app/components/sources.tsx +4 -4
  59. package/templates/chat-app/components/text-editor.tsx +5 -2
  60. package/templates/chat-app/components/toolbar.tsx +3 -3
  61. package/templates/chat-app/components/ui/sidebar.tsx +0 -1
  62. package/templates/chat-app/components/upgrade-cta/limit-display.tsx +1 -1
  63. package/templates/chat-app/components/user-message.tsx +135 -134
  64. package/templates/chat-app/electron.d.ts +41 -0
  65. package/templates/chat-app/evals/my-eval.eval.ts +3 -1
  66. package/templates/chat-app/hooks/use-artifact.tsx +13 -13
  67. package/templates/chat-app/lib/ai/gateways/provider-types.ts +19 -10
  68. package/templates/chat-app/lib/ai/stream-errors.test.ts +72 -0
  69. package/templates/chat-app/lib/ai/stream-errors.ts +94 -0
  70. package/templates/chat-app/lib/ai/tools/code-execution.javascript.ts +171 -0
  71. package/templates/chat-app/lib/ai/tools/code-execution.python.ts +336 -0
  72. package/templates/chat-app/lib/ai/tools/code-execution.shared.test.ts +71 -0
  73. package/templates/chat-app/lib/ai/tools/code-execution.shared.ts +59 -0
  74. package/templates/chat-app/lib/ai/tools/code-execution.ts +62 -391
  75. package/templates/chat-app/lib/ai/tools/code-execution.types.ts +24 -0
  76. package/templates/chat-app/lib/ai/tools/steps/multi-query-web-search.ts +3 -2
  77. package/templates/chat-app/lib/anonymous-session-client.ts +0 -3
  78. package/templates/chat-app/lib/artifacts/code/client.tsx +35 -5
  79. package/templates/chat-app/lib/artifacts/sheet/client.tsx +11 -3
  80. package/templates/chat-app/lib/auth-client.ts +23 -1
  81. package/templates/chat-app/lib/auth.ts +18 -1
  82. package/templates/chat-app/lib/blob.ts +1 -1
  83. package/templates/chat-app/lib/clone-messages.ts +1 -1
  84. package/templates/chat-app/lib/config-schema.ts +13 -1
  85. package/templates/chat-app/lib/constants.ts +3 -4
  86. package/templates/chat-app/lib/db/migrations/meta/0044_snapshot.json +42 -129
  87. package/templates/chat-app/lib/db/migrations/meta/_journal.json +1 -1
  88. package/templates/chat-app/lib/editor/config.ts +4 -4
  89. package/templates/chat-app/lib/electron-auth.ts +96 -0
  90. package/templates/chat-app/lib/env-schema.ts +33 -4
  91. package/templates/chat-app/lib/message-conversion.ts +1 -1
  92. package/templates/chat-app/lib/playwright-test-environment.ts +18 -0
  93. package/templates/chat-app/lib/social-auth.ts +5 -0
  94. package/templates/chat-app/lib/stores/hooks-threads.ts +2 -1
  95. package/templates/chat-app/lib/stores/with-threads.test.ts +1 -1
  96. package/templates/chat-app/lib/stores/with-threads.ts +5 -6
  97. package/templates/chat-app/lib/stores/with-tracing.ts +1 -1
  98. package/templates/chat-app/lib/thread-utils.ts +19 -21
  99. package/templates/chat-app/lib/utils/download-assets.ts +6 -7
  100. package/templates/chat-app/lib/utils/rate-limit.ts +9 -3
  101. package/templates/chat-app/package.json +20 -18
  102. package/templates/chat-app/playwright.config.ts +0 -19
  103. package/templates/chat-app/providers/chat-input-provider.tsx +1 -1
  104. package/templates/chat-app/proxy.ts +28 -3
  105. package/templates/chat-app/scripts/check-env.ts +10 -0
  106. package/templates/chat-app/trpc/server.tsx +7 -2
  107. package/templates/chat-app/tsconfig.json +2 -1
  108. package/templates/chat-app/vercel.json +0 -10
  109. package/templates/electron/CHANGELOG.md +7 -0
  110. package/templates/electron/README.md +54 -0
  111. package/templates/electron/entitlements.mac.plist +10 -0
  112. package/templates/electron/forge.config.ts +157 -0
  113. package/templates/electron/icon.png +0 -0
  114. package/templates/electron/package.json +53 -0
  115. package/templates/electron/scripts/generate-icons.test.js +37 -0
  116. package/templates/electron/scripts/generate-icons.ts +29 -0
  117. package/templates/electron/scripts/run-forge.cjs +28 -0
  118. package/templates/electron/scripts/write-branding.ts +18 -0
  119. package/templates/electron/src/config.ts +16 -0
  120. package/templates/electron/src/lib/auth-client.ts +64 -0
  121. package/templates/electron/src/main.ts +670 -0
  122. package/templates/electron/src/preload.d.ts +27 -0
  123. package/templates/electron/src/preload.ts +25 -0
  124. package/templates/electron/tsconfig.json +18 -0
@@ -0,0 +1,54 @@
1
+ # Electron Desktop App
2
+
3
+ This directory contains the Electron wrapper for your ChatJS app.
4
+
5
+ ## Development
6
+
7
+ Make sure the Next.js web app is running first (`bun run dev` in the parent directory), then:
8
+
9
+ ```bash
10
+ bun install
11
+ bun run dev
12
+ ```
13
+
14
+ `bun run dev` points Electron at `http://localhost:3000` by default. You can override
15
+ that during development with `ELECTRON_APP_URL=... bun run dev`.
16
+
17
+ ## Customization
18
+
19
+ ### App Icon
20
+
21
+ Replace `icon.png` with your own 512×512 PNG. Then regenerate the platform icons:
22
+
23
+ ```bash
24
+ bun run generate-icons
25
+ ```
26
+
27
+ This produces the generated assets in `build/` used by Electron Forge.
28
+
29
+ ### App Name & ID
30
+
31
+ The app name, protocol prefix, and production URL are generated from your ChatJS config before each build. Use `forge.config.ts` only for packaging-specific overrides:
32
+
33
+ - `appName` controls the display name shown in the OS
34
+ - `appPrefix` controls the bundle identifier prefix and protocol scheme
35
+
36
+ ### Protocol Scheme (Deep Links)
37
+
38
+ The `APP_SCHEME` in `src/config.ts` controls the custom URL scheme used for OAuth deep links (e.g. `yourapp://`). It must match the `protocols` entries in `forge.config.ts`.
39
+
40
+ ### Production URL
41
+
42
+ Update `appUrl` in `apps/chat/chat.config.ts` for your deployed site. Packaged Electron
43
+ builds use that URL by default, while development uses `http://localhost:3000` unless
44
+ `ELECTRON_APP_URL` is set.
45
+
46
+ ## Building for Distribution
47
+
48
+ ```bash
49
+ bun run make:mac # macOS .dmg and .zip
50
+ bun run make:win # Windows installer
51
+ bun run make:linux # Linux .deb and .rpm
52
+ ```
53
+
54
+ Forge outputs are written to `out/`.
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>com.apple.security.cs.allow-jit</key>
6
+ <true/>
7
+ <key>com.apple.security.network.client</key>
8
+ <true/>
9
+ </dict>
10
+ </plist>
@@ -0,0 +1,157 @@
1
+ import { existsSync, readFileSync, rmSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { spawnSync } from "node:child_process";
4
+ import type { ForgeConfig } from "@electron-forge/shared-types";
5
+ import { MakerDeb } from "@electron-forge/maker-deb";
6
+ import { MakerDMG } from "@electron-forge/maker-dmg";
7
+ import { MakerRpm } from "@electron-forge/maker-rpm";
8
+ import { MakerSquirrel } from "@electron-forge/maker-squirrel";
9
+ import { MakerZIP } from "@electron-forge/maker-zip";
10
+
11
+ type Branding = {
12
+ appName: string;
13
+ appPrefix: string;
14
+ appUrl: string;
15
+ orgName?: string;
16
+ orgEmail?: string;
17
+ };
18
+
19
+ const appRoot = __dirname;
20
+ const brandingPath = join(appRoot, "branding.json");
21
+ let prebuildComplete = false;
22
+
23
+ function loadBranding(): Branding {
24
+ ensurePrebuild();
25
+
26
+ if (!existsSync(brandingPath)) {
27
+ throw new Error(
28
+ `Electron packaging requires ${brandingPath}, but it was not generated by prebuild. ` +
29
+ `Run \`bun run prebuild\` in ${appRoot} and try again.`
30
+ );
31
+ }
32
+
33
+ const branding = JSON.parse(readFileSync(brandingPath, "utf8")) as Partial<Branding>;
34
+
35
+ if (!branding.appName || !branding.appPrefix || !branding.appUrl) {
36
+ throw new Error(
37
+ `Electron branding file ${brandingPath} is invalid. Expected appName, appPrefix, and appUrl.`
38
+ );
39
+ }
40
+
41
+ return branding as Branding;
42
+ }
43
+
44
+ function runBunScript(script: string, env: NodeJS.ProcessEnv = {}): void {
45
+ const result = spawnSync("bun", ["run", script], {
46
+ stdio: "inherit",
47
+ env: { ...process.env, ...env },
48
+ });
49
+
50
+ if (result.status !== 0) {
51
+ throw new Error(`bun run ${script} failed with exit code ${result.status ?? "unknown"}`);
52
+ }
53
+ }
54
+
55
+ function ensurePrebuild(): void {
56
+ if (prebuildComplete) {
57
+ return;
58
+ }
59
+
60
+ runBunScript("prebuild");
61
+ prebuildComplete = true;
62
+ }
63
+
64
+ function removeLocalNodeModules(): void {
65
+ rmSync(join(appRoot, "node_modules"), { recursive: true, force: true });
66
+ }
67
+
68
+ function createForgeConfig(): ForgeConfig {
69
+ const branding = loadBranding();
70
+ const { appName, appPrefix, orgName, orgEmail } = branding;
71
+
72
+ return {
73
+ packagerConfig: {
74
+ name: appName,
75
+ executableName: appPrefix,
76
+ icon: "./build/icon",
77
+ appBundleId: `dev.${appPrefix}.app`,
78
+ appCategoryType: "public.app-category.productivity",
79
+ asar: true,
80
+ protocols: [
81
+ {
82
+ name: `${appName} Auth`,
83
+ schemes: [appPrefix],
84
+ },
85
+ ],
86
+ ignore: [
87
+ /^\/out($|\/)/,
88
+ /^\/release($|\/)/,
89
+ /^\/src($|\/)/,
90
+ /^\/node_modules($|\/)/,
91
+ /^\/scripts($|\/)/,
92
+ /^\/README\.md$/,
93
+ /^\/tsconfig\.json$/,
94
+ ],
95
+ },
96
+ makers: [
97
+ new MakerZIP({}, ["darwin"]),
98
+ new MakerDMG(
99
+ {
100
+ name: `${appName}-mac`,
101
+ },
102
+ ["darwin"]
103
+ ),
104
+ new MakerSquirrel(
105
+ {
106
+ name: appPrefix,
107
+ authors: orgName ?? appName,
108
+ description: `${appName} desktop application`,
109
+ setupExe: `${appName}-windows.exe`,
110
+ },
111
+ ["win32"]
112
+ ),
113
+ new MakerDeb(
114
+ {
115
+ options: {
116
+ bin: appPrefix,
117
+ maintainer:
118
+ orgName && orgEmail
119
+ ? `${orgName} <${orgEmail}>`
120
+ : (orgName ?? orgEmail ?? appName),
121
+ homepage: branding.appUrl,
122
+ icon: "./build/icon.png",
123
+ categories: ["Utility"],
124
+ },
125
+ },
126
+ ["linux"]
127
+ ),
128
+ new MakerRpm(
129
+ {
130
+ options: {
131
+ bin: appPrefix,
132
+ homepage: branding.appUrl,
133
+ icon: "./build/icon.png",
134
+ license: "Apache-2.0",
135
+ },
136
+ },
137
+ ["linux"]
138
+ ),
139
+ ],
140
+ hooks: {
141
+ generateAssets: async () => {
142
+ ensurePrebuild();
143
+ },
144
+ preStart: async () => {
145
+ runBunScript("build", { NODE_ENV: "development" });
146
+ },
147
+ prePackage: async () => {
148
+ runBunScript("build", { NODE_ENV: "production" });
149
+ removeLocalNodeModules();
150
+ },
151
+ },
152
+ };
153
+ }
154
+
155
+ const config = createForgeConfig();
156
+
157
+ export default config;
Binary file
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "__PROJECT_NAME__-electron",
3
+ "version": "0.3.0",
4
+ "private": true,
5
+ "description": "ChatJS desktop application",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/__GITHUB_OWNER__/__GITHUB_REPO__.git"
9
+ },
10
+ "author": {
11
+ "name": "ChatJS",
12
+ "email": "support@chatjs.dev"
13
+ },
14
+ "homepage": "https://demo.chatjs.dev",
15
+ "main": "dist/main.js",
16
+ "scripts": {
17
+ "forge": "node ./scripts/run-forge.cjs",
18
+ "generate-icons": "bun scripts/generate-icons.ts",
19
+ "prebuild": "bun scripts/write-branding.ts && bun run generate-icons",
20
+ "build": "bun build src/main.ts --target=node --format=cjs --outfile dist/main.js --external electron && bun build src/preload.ts --target=browser --format=cjs --outfile dist/preload.js --external electron",
21
+ "start": "bun run forge start",
22
+ "dev": "bun run start",
23
+ "package": "bun run forge package",
24
+ "make": "bun run forge make",
25
+ "make:mac": "bun run forge make --platform=darwin --arch=universal",
26
+ "make:win": "bun run forge make --platform=win32 --arch=x64",
27
+ "make:linux": "bun run forge make --platform=linux --arch=x64",
28
+ "publish": "bun run forge publish",
29
+ "electron:build": "bun run build",
30
+ "electron:dev": "bun run dev",
31
+ "electron:make": "bun run make",
32
+ "electron:publish": "bun run publish"
33
+ },
34
+ "dependencies": {},
35
+ "devDependencies": {
36
+ "@better-auth/electron": "^1.5.6",
37
+ "@electron-forge/cli": "^7.11.1",
38
+ "@electron-forge/maker-deb": "^7.11.1",
39
+ "@electron-forge/maker-dmg": "^7.11.1",
40
+ "@electron-forge/maker-rpm": "^7.11.1",
41
+ "@electron-forge/maker-squirrel": "^7.11.1",
42
+ "@electron-forge/maker-zip": "^7.11.1",
43
+ "@electron-forge/shared-types": "^7.11.1",
44
+ "@types/node": "^22.8.6",
45
+ "better-auth": "^1.5.6",
46
+ "conf": "^15.1.0",
47
+ "electron": "36.0.0",
48
+ "png2icons": "^2.0.1",
49
+ "supports-color": "^7.2.0",
50
+ "typescript": "5.8.3",
51
+ "update-electron-app": "^3.1.2"
52
+ }
53
+ }
@@ -0,0 +1,37 @@
1
+ const { existsSync, rmSync } = require("node:fs");
2
+ const { join, resolve } = require("node:path");
3
+ const { spawnSync } = require("node:child_process");
4
+ const { describe, expect, test } = require("bun:test");
5
+ const forgeConfig = require("../forge.config").default;
6
+
7
+ const appRoot = resolve(__dirname, "..");
8
+ const buildDir = join(appRoot, "build");
9
+ const outputFiles = ["icon.png", "icon.icns", "icon.ico"];
10
+
11
+ function cleanupGeneratedIcons() {
12
+ for (const file of outputFiles) {
13
+ rmSync(join(buildDir, file), { force: true });
14
+ }
15
+ }
16
+
17
+ describe("generate-icons", () => {
18
+ test("writes Forge-compatible icon assets", () => {
19
+ cleanupGeneratedIcons();
20
+
21
+ const result = spawnSync("bun", ["scripts/generate-icons.ts"], {
22
+ cwd: appRoot,
23
+ stdio: "pipe",
24
+ encoding: "utf8",
25
+ });
26
+
27
+ expect(result.status).toBe(0);
28
+
29
+ for (const file of outputFiles) {
30
+ expect(existsSync(join(buildDir, file))).toBe(true);
31
+ }
32
+ });
33
+
34
+ test("forge config points packager at generated icons", () => {
35
+ expect(forgeConfig.packagerConfig?.icon).toBe("./build/icon");
36
+ });
37
+ });
@@ -0,0 +1,29 @@
1
+ import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import * as png2icons from "png2icons";
4
+
5
+ const root = resolve(__dirname, "..");
6
+ const src = join(root, "icon.png");
7
+ const buildDir = join(root, "build");
8
+ const outputBase = join(buildDir, "icon");
9
+
10
+ mkdirSync(buildDir, { recursive: true });
11
+
12
+ const sourcePng = readFileSync(src);
13
+ const icns = png2icons.createICNS(sourcePng, png2icons.BICUBIC2, 0);
14
+ const ico = png2icons.createICO(sourcePng, png2icons.BICUBIC2, 0, false, true);
15
+
16
+ if (!icns) {
17
+ throw new Error("Failed to generate build/icon.icns");
18
+ }
19
+
20
+ if (!ico) {
21
+ throw new Error("Failed to generate build/icon.ico");
22
+ }
23
+
24
+ copyFileSync(src, `${outputBase}.png`);
25
+ writeFileSync(`${outputBase}.icns`, icns);
26
+ writeFileSync(`${outputBase}.ico`, ico);
27
+ png2icons.clearCache();
28
+
29
+ console.log("Generated build/icon.{png,icns,ico}");
@@ -0,0 +1,28 @@
1
+ const { spawnSync } = require("node:child_process");
2
+ const path = require("node:path");
3
+
4
+ const candidates = [
5
+ path.resolve(__dirname, "..", "..", "..", "node_modules", "@electron-forge", "cli", "dist", "electron-forge.js"),
6
+ path.resolve(__dirname, "..", "node_modules", "@electron-forge", "cli", "dist", "electron-forge.js"),
7
+ ];
8
+
9
+ const forgeEntrypoint = candidates.find((candidate) => {
10
+ try {
11
+ require("node:fs").accessSync(candidate);
12
+ return true;
13
+ } catch {
14
+ return false;
15
+ }
16
+ });
17
+
18
+ if (!forgeEntrypoint) {
19
+ console.error("Could not locate @electron-forge/cli.");
20
+ process.exit(1);
21
+ }
22
+
23
+ const result = spawnSync(process.execPath, [forgeEntrypoint, ...process.argv.slice(2)], {
24
+ stdio: "inherit",
25
+ env: process.env,
26
+ });
27
+
28
+ process.exit(result.status ?? 1);
@@ -0,0 +1,18 @@
1
+ import { writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { config } from "@/lib/config";
4
+
5
+ const { appName, appPrefix, appUrl, organization } = config;
6
+ const orgEmail =
7
+ organization.contact?.privacyEmail || organization.contact?.legalEmail;
8
+
9
+ writeFileSync(
10
+ resolve(__dirname, "..", "branding.json"),
11
+ JSON.stringify(
12
+ { appName, appPrefix, appUrl, orgName: organization.name, orgEmail },
13
+ null,
14
+ 2
15
+ )
16
+ );
17
+
18
+ console.log("branding.json written:", { appName, appPrefix, appUrl });
@@ -0,0 +1,16 @@
1
+ import { config } from "@/lib/config";
2
+
3
+ export const APP_NAME = config.appName;
4
+ export const APP_SCHEME = config.appPrefix;
5
+ const DEFAULT_DEV_APP_URL = "http://localhost:3000";
6
+
7
+ export const APP_URL =
8
+ process.env.ELECTRON_APP_URL ||
9
+ (process.env.NODE_ENV === "production" ? config.appUrl : DEFAULT_DEV_APP_URL);
10
+
11
+ export const WINDOW_DEFAULTS = {
12
+ width: 1280,
13
+ height: 800,
14
+ minWidth: 800,
15
+ minHeight: 600,
16
+ } as const;
@@ -0,0 +1,64 @@
1
+ import { safeStorage } from "electron";
2
+ import { electronClient } from "@better-auth/electron/client";
3
+ import { storage } from "@better-auth/electron/storage";
4
+ import { createAuthClient } from "better-auth/client";
5
+ import {
6
+ ELECTRON_AUTH_CALLBACK_PATH,
7
+ ELECTRON_AUTH_CLIENT_ID,
8
+ ELECTRON_AUTH_COOKIE_PREFIX,
9
+ } from "@/lib/electron-auth";
10
+ import { APP_SCHEME, APP_URL } from "../config";
11
+
12
+ if (process.env.NODE_ENV !== "production") {
13
+ Object.defineProperty(safeStorage, "isEncryptionAvailable", {
14
+ configurable: true,
15
+ value: () => false,
16
+ });
17
+ }
18
+
19
+ const memoryStorage = () => {
20
+ const store = new Map<string, string>();
21
+
22
+ return {
23
+ getItem: (key: string) => store.get(key) ?? null,
24
+ setItem: (key: string, value: string) => {
25
+ store.set(key, value);
26
+ },
27
+ };
28
+ };
29
+
30
+ const electronAuthStorage =
31
+ process.env.NODE_ENV === "production" ? storage() : memoryStorage();
32
+
33
+ export const authClient = createAuthClient({
34
+ baseURL: APP_URL,
35
+ plugins: [
36
+ electronClient({
37
+ callbackPath: ELECTRON_AUTH_CALLBACK_PATH,
38
+ clientID: ELECTRON_AUTH_CLIENT_ID,
39
+ cookiePrefix: ELECTRON_AUTH_COOKIE_PREFIX,
40
+ signInURL: `${APP_URL}/device-login`,
41
+ protocol: {
42
+ scheme: APP_SCHEME,
43
+ },
44
+ storage: electronAuthStorage,
45
+ // `as any`: @better-auth/electron does not export a typed Storage union
46
+ // compatible with both `storage()` and our in-memory shim, and the
47
+ // plugin's inferred return type leaks through to createAuthClient.
48
+ }) as any,
49
+ ],
50
+ });
51
+
52
+ export type ElectronAuthClient = typeof authClient & {
53
+ authenticate: (data: { token: string }) => Promise<unknown>;
54
+ getCookie: () => string;
55
+ getSession: () => Promise<{ data?: { user?: unknown | null } | null }>;
56
+ requestAuth: (options?: { provider?: string }) => Promise<void>;
57
+ signOut: () => Promise<unknown>;
58
+ setupMain: (cfg?: {
59
+ getWindow?: () => Electron.BrowserWindow | null;
60
+ scheme?: boolean;
61
+ }) => void;
62
+ };
63
+
64
+ export const electronAuthClient = authClient as ElectronAuthClient;