ai-zero-token 2.0.1 → 2.0.3

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 (55) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +13 -5
  3. package/README.zh-CN.md +13 -5
  4. package/admin-ui/dist/assets/InfoRow-0ULI9iI3.js +1 -0
  5. package/admin-ui/dist/assets/accounts-DymL4WIa.js +2 -0
  6. package/admin-ui/dist/assets/activity-D21-Xrc4.js +1 -0
  7. package/admin-ui/dist/assets/app-mark-nsRs4vo7.svg +8 -0
  8. package/admin-ui/dist/assets/circle-check-ZYtn9GqY.js +1 -0
  9. package/admin-ui/dist/assets/clock-3-BzDANsVk.js +1 -0
  10. package/admin-ui/dist/assets/docs-BRNWAMPw.css +1 -0
  11. package/admin-ui/dist/assets/docs-DtO-AOWU.js +1735 -0
  12. package/admin-ui/dist/assets/earth-DFdZaQIi.js +1 -0
  13. package/admin-ui/dist/assets/image-bed-yIVQ4dKs.js +1 -0
  14. package/admin-ui/dist/assets/index-By4r-wy3.css +1 -0
  15. package/admin-ui/dist/assets/index-DRe-tByu.js +10 -0
  16. package/admin-ui/dist/assets/jsx-runtime-DqpGtLhh.js +1 -0
  17. package/admin-ui/dist/assets/launch-CQXYrl-h.js +1 -0
  18. package/admin-ui/dist/assets/logs-awABDg1C.js +1 -0
  19. package/admin-ui/dist/assets/network-detect-sSrnwZqf.js +1 -0
  20. package/admin-ui/dist/assets/overview-BbSON0Jl.js +1 -0
  21. package/admin-ui/dist/assets/profiles-DMOjJORP.js +1 -0
  22. package/admin-ui/dist/assets/refresh-cw-CAAH2rqe.js +1 -0
  23. package/admin-ui/dist/assets/search-B2hz41D3.js +1 -0
  24. package/admin-ui/dist/assets/server-BrjJPb9D.js +1 -0
  25. package/admin-ui/dist/assets/settings-DvRiHS7i.js +1 -0
  26. package/admin-ui/dist/assets/tester-CftPgRE9.js +3 -0
  27. package/admin-ui/dist/assets/upload-CwXb7Q1b.js +1 -0
  28. package/admin-ui/dist/assets/zap-B4_oDbCp.js +1 -0
  29. package/admin-ui/dist/index.html +5 -3
  30. package/build/icon.icns +0 -0
  31. package/build/icon.ico +0 -0
  32. package/build/icon.png +0 -0
  33. package/build/icon.svg +8 -0
  34. package/build/mac-install-guide.txt +25 -0
  35. package/dist/core/context.js +3 -0
  36. package/dist/core/providers/http-client.js +11 -4
  37. package/dist/core/services/auth-service.js +88 -12
  38. package/dist/core/services/config-service.js +87 -23
  39. package/dist/core/services/github-image-bed-service.js +264 -0
  40. package/dist/core/services/network-detect-service.js +189 -74
  41. package/dist/core/services/version-service.js +1 -1
  42. package/dist/core/store/github-image-bed-history-store.js +68 -0
  43. package/dist/core/store/github-image-bed-store.js +52 -0
  44. package/dist/core/store/profile-store.js +73 -32
  45. package/dist/core/store/settings-store.js +14 -0
  46. package/dist/desktop/main.js +158 -6
  47. package/dist/server/app.js +168 -26
  48. package/dist/server/index.js +41 -15
  49. package/docs/DESKTOP_RELEASE.md +35 -3
  50. package/docs/PRODUCT_UPDATE_DESKTOP_TOOLBOX.md +2 -2
  51. package/package.json +34 -2
  52. package/admin-ui/dist/assets/app-mark-Gd2QnHMO.svg +0 -9
  53. package/admin-ui/dist/assets/index-DNzR8XR7.css +0 -1
  54. package/admin-ui/dist/assets/index-DZMegNPs.js +0 -1745
  55. package/dist/server/admin-page.js +0 -4586
@@ -8,7 +8,6 @@ import cors from "@fastify/cors";
8
8
  import { z } from "zod";
9
9
  import { createGatewayContext } from "../core/context.js";
10
10
  import { requestText } from "../core/providers/http-client.js";
11
- import { renderAdminPage } from "./admin-page.js";
12
11
  const packageRoot = path.dirname(fileURLToPath(new URL("../../package.json", import.meta.url)));
13
12
  const adminUiDistDir = path.join(packageRoot, "admin-ui", "dist");
14
13
  const adminUiIndexPath = path.join(adminUiDistDir, "index.html");
@@ -26,14 +25,6 @@ const assetContentTypes = {
26
25
  ".svg": "image/svg+xml",
27
26
  ".webp": "image/webp"
28
27
  };
29
- async function pathExists(targetPath) {
30
- try {
31
- await fs.access(targetPath);
32
- return true;
33
- } catch {
34
- return false;
35
- }
36
- }
37
28
  function getContentType(filePath) {
38
29
  return assetContentTypes[path.extname(filePath).toLowerCase()] ?? "application/octet-stream";
39
30
  }
@@ -123,6 +114,12 @@ const settingsUpdateSchema = z.object({
123
114
  }).optional(),
124
115
  autoSwitch: z.object({
125
116
  enabled: z.boolean()
117
+ }).optional(),
118
+ runtime: z.object({
119
+ quotaSyncConcurrency: z.number().int().min(1).max(32).optional()
120
+ }).optional(),
121
+ server: z.object({
122
+ port: z.number().int().min(1).max(65535)
126
123
  }).optional()
127
124
  });
128
125
  const proxyTestSchema = z.object({
@@ -135,9 +132,15 @@ const proxyTestSchema = z.object({
135
132
  const profileActionSchema = z.object({
136
133
  profileId: z.string().min(1)
137
134
  });
135
+ const profileRemoveBatchSchema = z.object({
136
+ profileIds: z.array(z.string().min(1)).min(1)
137
+ });
138
138
  const profileImportSchema = z.object({
139
139
  profile: z.unknown()
140
140
  });
141
+ const runtimeRefreshSchema = z.object({
142
+ staleOnly: z.boolean().optional()
143
+ });
141
144
  const profileExportSchema = z.object({
142
145
  profileId: z.string().min(1).optional(),
143
146
  profileIds: z.array(z.string().min(1)).optional(),
@@ -146,6 +149,19 @@ const profileExportSchema = z.object({
146
149
  const codexApplySchema = z.object({
147
150
  profileId: z.string().min(1)
148
151
  });
152
+ const githubImageBedConfigSchema = z.object({
153
+ token: z.string().min(1)
154
+ });
155
+ const githubImageBedUploadSchema = z.object({
156
+ filename: z.string().min(1),
157
+ dataUrl: z.string().min(1)
158
+ });
159
+ const githubImageBedHistoryQuerySchema = z.object({
160
+ limit: z.coerce.number().int().min(1).max(100).optional()
161
+ });
162
+ const githubImageBedHistoryParamsSchema = z.object({
163
+ id: z.string().min(1)
164
+ });
149
165
  const imageGenerationsBodySchema = z.object({
150
166
  prompt: z.string().min(1),
151
167
  model: z.string().optional(),
@@ -431,6 +447,7 @@ function serializeProfile(profile) {
431
447
  email: profile.email,
432
448
  quota: profile.quota,
433
449
  authStatus: profile.authStatus,
450
+ exportAudit: profile.exportAudit,
434
451
  expiresAt: profile.expires,
435
452
  accessTokenPreview: maskSecret(profile.access),
436
453
  refreshTokenPreview: maskSecret(profile.refresh)
@@ -444,6 +461,7 @@ function serializeManagedProfile(profile) {
444
461
  email: profile.email,
445
462
  quota: profile.quota,
446
463
  authStatus: profile.authStatus,
464
+ exportAudit: profile.exportAudit,
447
465
  expiresAt: profile.expiresAt,
448
466
  accessTokenPreview: profile.accessTokenPreview,
449
467
  refreshTokenPreview: profile.refreshTokenPreview,
@@ -486,7 +504,7 @@ function createApp(params) {
486
504
  const ctx = createGatewayContext();
487
505
  void app.register(cors, {
488
506
  origin: params?.corsOrigin ?? true,
489
- methods: ["GET", "POST", "PUT", "OPTIONS"]
507
+ methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
490
508
  });
491
509
  app.setErrorHandler((error, request, reply) => {
492
510
  const normalized = normalizeError(error);
@@ -529,6 +547,7 @@ function createApp(params) {
529
547
  codex: codexStatus,
530
548
  adminUrl: `${origin}/`,
531
549
  baseUrl: `${origin}/v1`,
550
+ restartSupported: Boolean(params?.onRestart),
532
551
  supportedEndpoints: [
533
552
  {
534
553
  method: "GET",
@@ -559,12 +578,18 @@ function createApp(params) {
559
578
  };
560
579
  }
561
580
  app.get("/", async (_request, reply) => {
562
- if (await pathExists(adminUiIndexPath)) {
581
+ try {
563
582
  reply.header("Content-Type", "text/html; charset=utf-8");
564
583
  return fs.readFile(adminUiIndexPath, "utf8");
584
+ } catch {
585
+ reply.code(503);
586
+ return {
587
+ error: {
588
+ type: "admin_ui_missing",
589
+ message: "React \u7BA1\u7406\u9875\u672A\u6784\u5EFA\uFF0C\u8BF7\u5148\u8FD0\u884C npm run build:ui\u3002"
590
+ }
591
+ };
565
592
  }
566
- reply.header("Content-Type", "text/html; charset=utf-8");
567
- return renderAdminPage();
568
593
  });
569
594
  app.get("/assets/*", async (request, reply) => {
570
595
  const assetPath = request.params["*"];
@@ -598,10 +623,21 @@ function createApp(params) {
598
623
  catalog: result.catalog
599
624
  };
600
625
  });
601
- app.post("/_gateway/admin/runtime-refresh", async (request) => {
626
+ app.post("/_gateway/admin/runtime-refresh", async (request, reply) => {
627
+ const parsed = runtimeRefreshSchema.safeParse(request.body ?? {});
628
+ if (!parsed.success) {
629
+ reply.code(400);
630
+ return {
631
+ error: {
632
+ type: "validation_error",
633
+ message: parsed.error.issues[0]?.message ?? "\u8BF7\u6C42\u4F53\u683C\u5F0F\u9519\u8BEF"
634
+ }
635
+ };
636
+ }
602
637
  const [quotaSync] = await Promise.all([
603
638
  ctx.authService.syncAllProfileQuotas("openai-codex", {
604
- suppressErrors: true
639
+ suppressErrors: true,
640
+ staleAfterMs: parsed.data.staleOnly ? 30 * 60 * 1e3 : void 0
605
641
  }),
606
642
  ctx.versionService.getVersionStatus({
607
643
  force: true
@@ -674,6 +710,23 @@ function createApp(params) {
674
710
  await ctx.authService.removeProfile(parsed.data.profileId);
675
711
  return buildAdminConfig(request);
676
712
  });
713
+ app.post("/_gateway/admin/profiles/remove-batch", async (request, reply) => {
714
+ const parsed = profileRemoveBatchSchema.safeParse(request.body);
715
+ if (!parsed.success) {
716
+ reply.code(400);
717
+ return {
718
+ error: {
719
+ type: "validation_error",
720
+ message: parsed.error.issues[0]?.message ?? "\u8BF7\u6C42\u4F53\u683C\u5F0F\u9519\u8BEF"
721
+ }
722
+ };
723
+ }
724
+ const removedProfileCount = await ctx.authService.removeProfiles(parsed.data.profileIds);
725
+ return {
726
+ ...await buildAdminConfig(request),
727
+ removedProfileCount
728
+ };
729
+ });
677
730
  app.post("/_gateway/admin/profiles/import", async (request, reply) => {
678
731
  const parsed = profileImportSchema.safeParse(request.body);
679
732
  if (!parsed.success) {
@@ -694,6 +747,23 @@ function createApp(params) {
694
747
  importedProfileCount: importedProfiles.length
695
748
  };
696
749
  });
750
+ app.post("/_gateway/admin/profiles/import/validate", async (request, reply) => {
751
+ const parsed = profileImportSchema.safeParse(request.body);
752
+ if (!parsed.success) {
753
+ reply.code(400);
754
+ return {
755
+ error: {
756
+ type: "validation_error",
757
+ message: parsed.error.issues[0]?.message ?? "\u8BF7\u6C42\u4F53\u683C\u5F0F\u9519\u8BEF"
758
+ }
759
+ };
760
+ }
761
+ const profiles = ctx.authService.validateProfilesImport(parsed.data.profile);
762
+ return {
763
+ valid: true,
764
+ profileCount: profiles.length
765
+ };
766
+ });
697
767
  app.get("/_gateway/admin/profiles/import-template", async () => ({
698
768
  profile: ctx.authService.getProfileImportTemplate()
699
769
  }));
@@ -710,11 +780,13 @@ function createApp(params) {
710
780
  }
711
781
  if (parsed.data.all || parsed.data.profileIds) {
712
782
  return {
713
- profile: await ctx.authService.exportProfiles(parsed.data.profileIds)
783
+ profile: await ctx.authService.exportProfiles(parsed.data.profileIds, "openai-codex", parsed.data.all ? "all" : "batch"),
784
+ config: await buildAdminConfig(request)
714
785
  };
715
786
  }
716
787
  return {
717
- profile: await ctx.authService.exportProfile(parsed.data.profileId)
788
+ profile: await ctx.authService.exportProfile(parsed.data.profileId),
789
+ config: await buildAdminConfig(request)
718
790
  };
719
791
  });
720
792
  app.post("/_gateway/admin/codex/apply", async (request, reply) => {
@@ -744,17 +816,29 @@ function createApp(params) {
744
816
  }
745
817
  };
746
818
  }
747
- if (parsed.data.defaultModel) {
748
- await ctx.configService.setDefaultModel(parsed.data.defaultModel);
749
- }
750
- if (parsed.data.networkProxy) {
751
- await ctx.configService.setNetworkProxy(parsed.data.networkProxy);
752
- }
753
- if (parsed.data.autoSwitch) {
754
- await ctx.configService.setAutoSwitch(parsed.data.autoSwitch);
755
- }
819
+ await ctx.configService.updateSettings(parsed.data);
756
820
  return buildAdminConfig(request);
757
821
  });
822
+ app.post("/_gateway/admin/restart", async (_request, reply) => {
823
+ if (!params?.onRestart) {
824
+ reply.code(501);
825
+ return {
826
+ error: {
827
+ type: "not_supported",
828
+ message: "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u91CD\u542F\u3002"
829
+ }
830
+ };
831
+ }
832
+ setTimeout(() => {
833
+ void Promise.resolve(params.onRestart?.()).catch((error) => {
834
+ console.error("[gateway:restart]", error);
835
+ });
836
+ }, 100);
837
+ return {
838
+ ok: true,
839
+ restarting: true
840
+ };
841
+ });
758
842
  app.post("/_gateway/admin/settings/proxy-test", async (request, reply) => {
759
843
  const parsed = proxyTestSchema.safeParse(request.body);
760
844
  if (!parsed.success) {
@@ -809,6 +893,64 @@ function createApp(params) {
809
893
  const settings = await ctx.configService.getSettings();
810
894
  return ctx.networkDetectService.collectReport(settings.networkProxy);
811
895
  });
896
+ app.get("/_gateway/image-bed/config", async () => ctx.githubImageBedService.getConfig());
897
+ app.post("/_gateway/image-bed/validate", async () => ctx.githubImageBedService.testConnection());
898
+ app.get("/_gateway/image-bed/history", async (request, reply) => {
899
+ const parsed = githubImageBedHistoryQuerySchema.safeParse(request.query ?? {});
900
+ if (!parsed.success) {
901
+ reply.code(400);
902
+ return {
903
+ error: {
904
+ type: "validation_error",
905
+ message: parsed.error.issues[0]?.message ?? "\u8BF7\u6C42\u53C2\u6570\u683C\u5F0F\u9519\u8BEF"
906
+ }
907
+ };
908
+ }
909
+ return ctx.githubImageBedService.listHistory(parsed.data.limit ?? 50);
910
+ });
911
+ app.put("/_gateway/image-bed/config", async (request, reply) => {
912
+ const parsed = githubImageBedConfigSchema.safeParse(request.body);
913
+ if (!parsed.success) {
914
+ reply.code(400);
915
+ return {
916
+ error: {
917
+ type: "validation_error",
918
+ message: parsed.error.issues[0]?.message ?? "\u8BF7\u6C42\u4F53\u683C\u5F0F\u9519\u8BEF"
919
+ }
920
+ };
921
+ }
922
+ return ctx.githubImageBedService.saveToken(parsed.data.token);
923
+ });
924
+ app.delete("/_gateway/image-bed/config", async () => ctx.githubImageBedService.clearToken());
925
+ app.post("/_gateway/image-bed/upload", async (request, reply) => {
926
+ const parsed = githubImageBedUploadSchema.safeParse(request.body);
927
+ if (!parsed.success) {
928
+ reply.code(400);
929
+ return {
930
+ error: {
931
+ type: "validation_error",
932
+ message: parsed.error.issues[0]?.message ?? "\u8BF7\u6C42\u4F53\u683C\u5F0F\u9519\u8BEF"
933
+ }
934
+ };
935
+ }
936
+ const uploaded = await ctx.githubImageBedService.uploadImage(parsed.data);
937
+ await ctx.githubImageBedService.rememberUpload(uploaded);
938
+ return uploaded;
939
+ });
940
+ app.delete("/_gateway/image-bed/history/:id", async (request, reply) => {
941
+ const parsed = githubImageBedHistoryParamsSchema.safeParse(request.params);
942
+ if (!parsed.success) {
943
+ reply.code(400);
944
+ return {
945
+ error: {
946
+ type: "validation_error",
947
+ message: parsed.error.issues[0]?.message ?? "\u8BF7\u6C42\u53C2\u6570\u683C\u5F0F\u9519\u8BEF"
948
+ }
949
+ };
950
+ }
951
+ return ctx.githubImageBedService.deleteHistoryItem(parsed.data.id);
952
+ });
953
+ app.delete("/_gateway/image-bed/history", async () => ctx.githubImageBedService.clearHistory());
812
954
  app.get("/v1/models", async () => ({
813
955
  object: "list",
814
956
  data: (await ctx.modelService.listModels()).map((model) => ({
@@ -21,27 +21,53 @@ function resolveBodyLimitBytes() {
21
21
  }
22
22
  return Math.floor(value * 1024 * 1024);
23
23
  }
24
+ function isPortInUseError(error) {
25
+ const normalized = error;
26
+ if (normalized.code === "EADDRINUSE") {
27
+ return true;
28
+ }
29
+ return typeof normalized.message === "string" && normalized.message.includes("EADDRINUSE");
30
+ }
24
31
  async function startServer(params) {
25
32
  const bodyLimit = resolveBodyLimitBytes();
26
- const app = createApp({
27
- corsOrigin: resolveCorsOrigin(),
28
- bodyLimit
29
- });
30
33
  const configService = new ConfigService();
31
34
  const defaults = await configService.getServerConfig();
32
35
  const host = params?.host ?? defaults.host;
33
36
  const port = params?.port ?? defaults.port;
34
- await app.listen({
35
- host,
36
- port
37
- });
38
- return {
39
- app,
40
- host,
41
- port,
42
- corsOrigin: process.env.AZT_CORS_ORIGIN?.trim() || "*",
43
- bodyLimit
44
- };
37
+ const maxPortAttempts = 20;
38
+ let lastError;
39
+ for (let offset = 0; offset <= maxPortAttempts; offset += 1) {
40
+ const listenPort = port + offset;
41
+ const app = createApp({
42
+ corsOrigin: resolveCorsOrigin(),
43
+ bodyLimit,
44
+ onRestart: params?.onRestart
45
+ });
46
+ try {
47
+ await app.listen({
48
+ host,
49
+ port: listenPort
50
+ });
51
+ if (offset > 0) {
52
+ console.warn(`[server] port ${port} was busy, using ${listenPort} instead.`);
53
+ await configService.setServerConfig({ port: listenPort });
54
+ }
55
+ return {
56
+ app,
57
+ host,
58
+ port: listenPort,
59
+ corsOrigin: process.env.AZT_CORS_ORIGIN?.trim() || "*",
60
+ bodyLimit
61
+ };
62
+ } catch (error) {
63
+ await app.close().catch(() => void 0);
64
+ if (!isPortInUseError(error) || offset === maxPortAttempts) {
65
+ throw error;
66
+ }
67
+ lastError = error;
68
+ }
69
+ }
70
+ throw lastError ?? new Error("\u65E0\u6CD5\u542F\u52A8\u672C\u5730\u670D\u52A1\u3002");
45
71
  }
46
72
  export {
47
73
  startServer
@@ -41,6 +41,13 @@ npm run dist:win
41
41
 
42
42
  Creates macOS and Windows distributables. macOS builds should be produced on macOS. Windows builds are best produced on Windows CI or a runner with a complete Windows packaging environment.
43
43
 
44
+ `npm run dist:mac` must build both Apple Silicon and Intel macOS packages. It runs:
45
+
46
+ ```bash
47
+ npm run dist:mac:arm64
48
+ npm run dist:mac:x64
49
+ ```
50
+
44
51
  ## UI Engineering Standards
45
52
 
46
53
  Before building release artifacts, the desktop React UI should follow:
@@ -65,6 +72,8 @@ Unsigned builds are suitable for internal testing only. Public commercial distri
65
72
 
66
73
  `electron-builder` reads the standard signing environment variables. Configure these in CI instead of committing credentials to the repository.
67
74
 
75
+ Until macOS Developer ID signing and notarization are configured, each macOS DMG must include `build/mac-install-guide.txt` so users can handle the Gatekeeper verification prompt.
76
+
68
77
  ## Release Artifacts
69
78
 
70
79
  The packaged output is written to:
@@ -75,17 +84,40 @@ release/
75
84
 
76
85
  The folder is intentionally ignored by git.
77
86
 
87
+ Every desktop GitHub Release must upload exactly these user-facing artifacts:
88
+
89
+ ```text
90
+ AI Zero Token-{version}-mac-arm64.dmg
91
+ AI Zero Token-{version}-mac-x64.dmg
92
+ AI Zero Token Setup {version}.exe
93
+ AI Zero Token-{version}-win.zip
94
+ ```
95
+
96
+ Artifact purpose:
97
+
98
+ - `AI Zero Token-{version}-mac-arm64.dmg`: macOS Apple Silicon builds for M1/M2/M3/M4 devices.
99
+ - `AI Zero Token-{version}-mac-x64.dmg`: macOS Intel builds.
100
+ - `AI Zero Token Setup {version}.exe`: Windows installer and primary Windows download.
101
+ - `AI Zero Token-{version}-win.zip`: Windows portable zip.
102
+
103
+ Do not upload unpacked app directories, debug metadata, universal macOS builds, or auto-update metadata unless the release explicitly enables an auto-update channel.
104
+
105
+ macOS DMG files should include the in-package `mac-install-guide.txt` helper document. Do not upload this text file as a standalone GitHub Release asset.
106
+
78
107
  ### Publish Flow
79
108
 
80
109
  1. Build the desktop package:
81
110
 
82
111
  ```bash
83
- npm run dist:dir
112
+ npm run dist:mac
113
+ npm run dist:win
84
114
  ```
85
115
 
86
- 2. Upload the generated files from `release/` to the matching GitHub Release tag.
116
+ 2. Rename the generated files, if needed, so the GitHub Release uses the standard artifact names listed above.
117
+
118
+ 3. Upload only the standard artifact files from `release/` to the matching GitHub Release tag.
87
119
 
88
- 3. Publish the npm package after confirming `package.json` and `package-lock.json` both point at the new version:
120
+ 4. Publish the npm package after confirming `package.json` and `package-lock.json` both point at the new version:
89
121
 
90
122
  ```bash
91
123
  npm publish
@@ -38,7 +38,7 @@
38
38
 
39
39
  ### 2.2 第二阶段:桌面端 React 管理台
40
40
 
41
- 当前管理页是服务端字符串 HTML,适合验证功能,但不适合长期维护桌面端和工具箱。
41
+ 管理页已迁移为 React/Vite 应用,网关生产环境只托管 `admin-ui/dist` 静态构建产物。
42
42
 
43
43
  第二阶段将管理界面迁移为 React/Vite 应用,但迁移范围只限 UI 层。
44
44
 
@@ -49,7 +49,7 @@
49
49
  - 封装 `/_gateway/*` 管理接口客户端
50
50
  - 网关生产环境托管前端静态构建产物
51
51
  - 桌面端加载同一套管理台 UI
52
- - 移除或降级当前 `admin-page.ts` 的职责
52
+ - 删除服务端内嵌管理页,保留一套 React 管理台
53
53
 
54
54
  这一阶段结束后,项目形态应变为:
55
55
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-zero-token",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "Local-first OpenAI-compatible AI CLI and gateway with Codex OAuth, multi-account management, and gpt-image-2 image generation/editing.",
5
5
  "license": "MIT",
6
6
  "author": "AI Zero Token Contributors",
@@ -50,7 +50,9 @@
50
50
  "desktop:dev": "node scripts/dev.mjs desktop",
51
51
  "dist": "npm run build && electron-builder",
52
52
  "dist:dir": "npm run build && electron-builder --dir",
53
- "dist:mac": "npm run build && electron-builder --mac",
53
+ "dist:mac": "npm run dist:mac:arm64 && npm run dist:mac:x64",
54
+ "dist:mac:arm64": "npm run build && electron-builder --mac --arm64",
55
+ "dist:mac:x64": "npm run build && electron-builder --mac --x64",
54
56
  "dist:win": "npm run build && electron-builder --win",
55
57
  "typecheck": "npm run typecheck:server && npm run typecheck:ui",
56
58
  "typecheck:server": "bunx tsc -p tsconfig.json --noEmit",
@@ -61,6 +63,7 @@
61
63
  "dependencies": {
62
64
  "@fastify/cors": "^11.1.0",
63
65
  "fastify": "^5.8.2",
66
+ "fflate": "^0.8.2",
64
67
  "zod": "^4.3.6"
65
68
  },
66
69
  "devDependencies": {
@@ -92,6 +95,12 @@
92
95
  "README.zh-CN.md",
93
96
  "LICENSE"
94
97
  ],
98
+ "extraResources": [
99
+ {
100
+ "from": "build/mac-install-guide.txt",
101
+ "to": "mac-install-guide.txt"
102
+ }
103
+ ],
95
104
  "extraMetadata": {
96
105
  "main": "dist/desktop/main.js"
97
106
  },
@@ -104,6 +113,27 @@
104
113
  "zip"
105
114
  ]
106
115
  },
116
+ "dmg": {
117
+ "contents": [
118
+ {
119
+ "x": 130,
120
+ "y": 160,
121
+ "type": "file"
122
+ },
123
+ {
124
+ "x": 410,
125
+ "y": 160,
126
+ "type": "link",
127
+ "path": "/Applications"
128
+ },
129
+ {
130
+ "x": 270,
131
+ "y": 300,
132
+ "type": "file",
133
+ "path": "build/mac-install-guide.txt"
134
+ }
135
+ ]
136
+ },
107
137
  "win": {
108
138
  "icon": "build/icon.ico",
109
139
  "target": [
@@ -119,9 +149,11 @@
119
149
  "files": [
120
150
  "dist",
121
151
  "admin-ui/dist",
152
+ "build/icon.svg",
122
153
  "build/icon.png",
123
154
  "build/icon.icns",
124
155
  "build/icon.ico",
156
+ "build/mac-install-guide.txt",
125
157
  "CHANGELOG.md",
126
158
  "docs/API_USAGE.md",
127
159
  "docs/DESKTOP_RELEASE.md",
@@ -1,9 +0,0 @@
1
- <svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <rect x="10" y="10" width="108" height="108" rx="26" fill="#111827"/>
3
- <rect x="18" y="18" width="92" height="92" rx="20" fill="#FFFFFF" fill-opacity=".08"/>
4
- <path d="M39 36h50c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12H39c-6.627 0-12-5.373-12-12V48c0-6.627 5.373-12 12-12Z" fill="#F8FAFC"/>
5
- <path d="M42 54h20M42 66h34M42 78h25" stroke="#111827" stroke-width="7" stroke-linecap="round"/>
6
- <path d="M83 53l8 11-8 11-8-11 8-11Z" fill="#2563EB"/>
7
- <path d="M71 36c3.2-9.2 11-14 23-14 0 12-4.9 19.9-14.7 23.7" stroke="#22C55E" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"/>
8
- <path d="M48 92c-3.2 9.2-11 14-23 14 0-12 4.9-19.9 14.7-23.7" stroke="#F59E0B" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"/>
9
- </svg>