ai-zero-token 1.0.3 → 1.0.5

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.5 - 2026-04-27
4
+
5
+ - Added management-page proxy configuration for upstream requests, persisted in local settings.
6
+ - Routed upstream requests through configured curl proxy settings when enabled.
7
+ - Removed the local fixed-size allowlist for image generation `size`, allowing upstream validation to decide supported values.
8
+ - Documented the proxy configuration workflow without including a specific proxy address.
9
+
10
+ ## 1.0.4 - 2026-04-24
11
+
12
+ - Moved persistent account and settings state to the user home directory at `~/.ai-zero-token/.state`.
13
+ - Added automatic one-time migration from the old package-local `.state` directory when available.
14
+ - Added `AI_ZERO_TOKEN_HOME` support for overriding the persistent state location.
15
+ - Fixed repeated login prompts after npm upgrades or global package reinstalls.
16
+
3
17
  ## 1.0.3 - 2026-04-24
4
18
 
5
19
  - Added dynamic Codex model discovery from the local `~/.codex/models_cache.json` cache, with static model fallback when the cache is unavailable.
@@ -11,4 +25,3 @@
11
25
  - Improved image generation error handling with transient retries and clearer failure details.
12
26
  - Preserved response headers when using the curl HTTP fallback so quota metadata can still be captured.
13
27
  - Added Vibe Coding / OpenAI-compatible client integration documentation.
14
-
package/README.md CHANGED
@@ -227,6 +227,8 @@ azt start
227
227
 
228
228
  管理页里邮箱默认脱敏显示,需要手动点击“查看邮箱”才会显示明文。
229
229
 
230
+ 如果当前网络访问海外上游不稳定,可以在管理页的“接口测试 / 系统设置”区域启用“上游代理”,并填写你自己的代理地址。保存后,OAuth 换取 token、模型刷新和接口转发都会通过该代理访问上游;本地管理页和 `127.0.0.1` 默认保持直连。
231
+
230
232
  默认监听地址:
231
233
 
232
234
  ```text
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from "node:child_process";
3
+ import { loadSettings } from "../store/settings-store.js";
3
4
  const CURL_STATUS_MARKER = "\n__CURL_STATUS__:";
4
5
  const CURL_HEADERS_MARKER = "\n__CURL_HEADERS__:";
5
6
  let requestSequence = 0;
@@ -68,6 +69,12 @@ async function runCurlRequest(init, params) {
68
69
  "--write-out",
69
70
  `${CURL_STATUS_MARKER}%{http_code}${CURL_HEADERS_MARKER}%{header_json}`
70
71
  ];
72
+ if (params?.proxy?.enabled && params.proxy.url.trim()) {
73
+ args.push("--proxy", params.proxy.url.trim());
74
+ if (params.proxy.noProxy.trim()) {
75
+ args.push("--noproxy", params.proxy.noProxy.trim());
76
+ }
77
+ }
71
78
  for (const [key, value] of Object.entries(init.headers ?? {})) {
72
79
  args.push("--header", `${key}: ${value}`);
73
80
  }
@@ -148,12 +155,25 @@ async function runCurlRequest(init, params) {
148
155
  headers
149
156
  };
150
157
  }
158
+ async function loadNetworkProxySettings() {
159
+ try {
160
+ const settings = await loadSettings();
161
+ return settings.networkProxy;
162
+ } catch (error) {
163
+ console.warn("[http] failed to load network proxy settings", {
164
+ error: error instanceof Error ? error.message : String(error)
165
+ });
166
+ return void 0;
167
+ }
168
+ }
151
169
  async function requestText(init) {
152
170
  const requestId = nextRequestId();
171
+ const proxy = await loadNetworkProxySettings();
153
172
  const useCurlOnly = process.env.OAUTH_DEMO_USE_CURL === "1";
173
+ const useConfiguredProxy = !!proxy?.enabled && !!proxy.url.trim();
154
174
  const timeoutMs = init.timeoutMs;
155
175
  const signal = typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : void 0;
156
- if (!useCurlOnly) {
176
+ if (!useCurlOnly && !useConfiguredProxy) {
157
177
  const startedAt = performance.now();
158
178
  const phases = {};
159
179
  try {
@@ -199,7 +219,8 @@ async function requestText(init) {
199
219
  }
200
220
  return runCurlRequest(init, {
201
221
  requestId,
202
- fallbackFrom: useCurlOnly ? void 0 : "fetch"
222
+ fallbackFrom: useCurlOnly || useConfiguredProxy ? void 0 : "fetch",
223
+ proxy
203
224
  });
204
225
  }
205
226
  export {
@@ -41,6 +41,36 @@ class ConfigService {
41
41
  await saveSettings(next);
42
42
  return next;
43
43
  }
44
+ async setNetworkProxy(params) {
45
+ const url = params.url?.trim() ?? "";
46
+ const noProxy = params.noProxy?.trim() || "localhost,127.0.0.1,::1";
47
+ if (params.enabled) {
48
+ if (!url) {
49
+ throw new Error("\u542F\u7528\u4EE3\u7406\u65F6\u5FC5\u987B\u586B\u5199\u4EE3\u7406\u5730\u5740\u3002");
50
+ }
51
+ let parsed;
52
+ try {
53
+ parsed = new URL(url);
54
+ } catch {
55
+ throw new Error("\u4EE3\u7406\u5730\u5740\u683C\u5F0F\u9519\u8BEF\uFF0C\u8BF7\u586B\u5199\u5B8C\u6574\u7684\u4EE3\u7406 URL\u3002");
56
+ }
57
+ const supportedProtocols = /* @__PURE__ */ new Set(["http:", "https:", "socks4:", "socks4a:", "socks5:", "socks5h:"]);
58
+ if (!supportedProtocols.has(parsed.protocol)) {
59
+ throw new Error("\u4EE3\u7406\u5730\u5740\u4EC5\u652F\u6301 http\u3001https\u3001socks4\u3001socks4a\u3001socks5 \u6216 socks5h\u3002");
60
+ }
61
+ }
62
+ const settings = await this.getSettings();
63
+ const next = {
64
+ ...settings,
65
+ networkProxy: {
66
+ enabled: params.enabled,
67
+ url,
68
+ noProxy
69
+ }
70
+ };
71
+ await saveSettings(next);
72
+ return next;
73
+ }
44
74
  async getServerConfig() {
45
75
  const settings = await this.getSettings();
46
76
  return settings.server;
@@ -6,11 +6,6 @@ const SUPPORTED_IMAGE_MODELS = /* @__PURE__ */ new Set([
6
6
  "gpt-image-1.5",
7
7
  "gpt-image-2"
8
8
  ]);
9
- const SUPPORTED_IMAGE_SIZES = /* @__PURE__ */ new Set([
10
- "1024x1024",
11
- "1024x1536",
12
- "1536x1024"
13
- ]);
14
9
  const SUPPORTED_IMAGE_QUALITIES = /* @__PURE__ */ new Set([
15
10
  "low",
16
11
  "medium",
@@ -62,11 +57,11 @@ function toImageGenerationEventOutput(value) {
62
57
  return null;
63
58
  }
64
59
  function normalizeReturnedSize(size, fallback) {
65
- if (typeof size === "string" && SUPPORTED_IMAGE_SIZES.has(size)) {
66
- return size;
60
+ if (typeof size === "string" && size.trim()) {
61
+ return size.trim();
67
62
  }
68
- if (typeof fallback === "string" && SUPPORTED_IMAGE_SIZES.has(fallback)) {
69
- return fallback;
63
+ if (typeof fallback === "string" && fallback.trim()) {
64
+ return fallback.trim();
70
65
  }
71
66
  return void 0;
72
67
  }
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from "node:fs/promises";
3
- import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- const projectDir = path.dirname(fileURLToPath(new URL("../../../package.json", import.meta.url)));
6
- const stateDir = path.join(projectDir, ".state");
7
- const storePath = path.join(stateDir, "store.json");
3
+ import {
4
+ ensureStateMigrated,
5
+ getStateDir,
6
+ getStorePath
7
+ } from "./state-paths.js";
8
8
  const PROFILE_CLAIM_PATH = "https://api.openai.com/profile";
9
9
  function createEmptyStore() {
10
10
  return {
@@ -40,15 +40,10 @@ function extractEmailFromAccessToken(token) {
40
40
  }
41
41
  return void 0;
42
42
  }
43
- function getStateDir() {
44
- return stateDir;
45
- }
46
- function getStorePath() {
47
- return storePath;
48
- }
49
43
  async function loadStore() {
50
44
  try {
51
- const raw = await fs.readFile(storePath, "utf8");
45
+ await ensureStateMigrated();
46
+ const raw = await fs.readFile(getStorePath(), "utf8");
52
47
  const parsed = JSON.parse(raw);
53
48
  const normalizedProfiles = Object.fromEntries(
54
49
  Object.entries(parsed.profiles ?? {}).map(([profileId, profile]) => [
@@ -73,8 +68,9 @@ async function loadStore() {
73
68
  }
74
69
  }
75
70
  async function saveStore(store) {
76
- await fs.mkdir(stateDir, { recursive: true });
77
- await fs.writeFile(storePath, `${JSON.stringify(store, null, 2)}
71
+ await ensureStateMigrated();
72
+ await fs.mkdir(getStateDir(), { recursive: true });
73
+ await fs.writeFile(getStorePath(), `${JSON.stringify(store, null, 2)}
78
74
  `, "utf8");
79
75
  }
80
76
  async function saveProfile(profile) {
@@ -130,7 +126,7 @@ async function removeProfile(profileId) {
130
126
  return store.activeProfileId ? store.profiles[store.activeProfileId] ?? null : null;
131
127
  }
132
128
  async function clearStore() {
133
- await fs.rm(stateDir, { recursive: true, force: true });
129
+ await fs.rm(getStateDir(), { recursive: true, force: true });
134
130
  }
135
131
  export {
136
132
  clearStore,
@@ -1,33 +1,41 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from "node:fs/promises";
3
- import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- const projectDir = path.dirname(fileURLToPath(new URL("../../../package.json", import.meta.url)));
6
- const stateDir = path.join(projectDir, ".state");
7
- const settingsPath = path.join(stateDir, "settings.json");
3
+ import {
4
+ ensureStateMigrated,
5
+ getSettingsPath,
6
+ getStateDir
7
+ } from "./state-paths.js";
8
8
  function createDefaultSettings() {
9
9
  return {
10
10
  version: 1,
11
11
  defaultProvider: "openai-codex",
12
12
  defaultModel: "gpt-5.4",
13
+ networkProxy: {
14
+ enabled: false,
15
+ url: "",
16
+ noProxy: "localhost,127.0.0.1,::1"
17
+ },
13
18
  server: {
14
19
  host: "0.0.0.0",
15
20
  port: 8787
16
21
  }
17
22
  };
18
23
  }
19
- function getSettingsPath() {
20
- return settingsPath;
21
- }
22
24
  async function loadSettings() {
23
25
  try {
24
- const raw = await fs.readFile(settingsPath, "utf8");
26
+ await ensureStateMigrated();
27
+ const raw = await fs.readFile(getSettingsPath(), "utf8");
25
28
  const parsed = JSON.parse(raw);
26
29
  const defaults = createDefaultSettings();
27
30
  return {
28
31
  version: 1,
29
32
  defaultProvider: parsed.defaultProvider ?? defaults.defaultProvider,
30
33
  defaultModel: parsed.defaultModel ?? defaults.defaultModel,
34
+ networkProxy: {
35
+ enabled: parsed.networkProxy?.enabled ?? defaults.networkProxy.enabled,
36
+ url: parsed.networkProxy?.url ?? defaults.networkProxy.url,
37
+ noProxy: parsed.networkProxy?.noProxy ?? defaults.networkProxy.noProxy
38
+ },
31
39
  server: {
32
40
  host: parsed.server?.host ?? defaults.server.host,
33
41
  port: parsed.server?.port ?? defaults.server.port
@@ -38,8 +46,9 @@ async function loadSettings() {
38
46
  }
39
47
  }
40
48
  async function saveSettings(settings) {
41
- await fs.mkdir(stateDir, { recursive: true });
42
- await fs.writeFile(settingsPath, `${JSON.stringify(settings, null, 2)}
49
+ await ensureStateMigrated();
50
+ await fs.mkdir(getStateDir(), { recursive: true });
51
+ await fs.writeFile(getSettingsPath(), `${JSON.stringify(settings, null, 2)}
43
52
  `, "utf8");
44
53
  }
45
54
  export {
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ const packageDir = path.dirname(fileURLToPath(new URL("../../../package.json", import.meta.url)));
7
+ const legacyStateDir = path.join(packageDir, ".state");
8
+ const appHomeDir = process.env.AI_ZERO_TOKEN_HOME || path.join(os.homedir(), ".ai-zero-token");
9
+ const stateDir = path.join(appHomeDir, ".state");
10
+ let migrationPromise = null;
11
+ async function pathExists(targetPath) {
12
+ try {
13
+ await fs.access(targetPath);
14
+ return true;
15
+ } catch {
16
+ return false;
17
+ }
18
+ }
19
+ async function copyIfMissing(fileName) {
20
+ const legacyPath = path.join(legacyStateDir, fileName);
21
+ const nextPath = path.join(stateDir, fileName);
22
+ if (!await pathExists(legacyPath) || await pathExists(nextPath)) {
23
+ return;
24
+ }
25
+ await fs.mkdir(stateDir, { recursive: true });
26
+ await fs.copyFile(legacyPath, nextPath);
27
+ }
28
+ function getStateDir() {
29
+ return stateDir;
30
+ }
31
+ function getStorePath() {
32
+ return path.join(stateDir, "store.json");
33
+ }
34
+ function getSettingsPath() {
35
+ return path.join(stateDir, "settings.json");
36
+ }
37
+ async function ensureStateMigrated() {
38
+ if (!migrationPromise) {
39
+ migrationPromise = (async () => {
40
+ if (legacyStateDir === stateDir) {
41
+ return;
42
+ }
43
+ await copyIfMissing("store.json");
44
+ await copyIfMissing("settings.json");
45
+ })();
46
+ }
47
+ await migrationPromise;
48
+ }
49
+ export {
50
+ ensureStateMigrated,
51
+ getSettingsPath,
52
+ getStateDir,
53
+ getStorePath
54
+ };
@@ -990,6 +990,21 @@ function renderAdminPage() {
990
990
  color: var(--text-soft);
991
991
  }
992
992
 
993
+ .checkbox-row {
994
+ display: flex;
995
+ align-items: center;
996
+ gap: 10px;
997
+ color: var(--text-soft);
998
+ font-size: 13px;
999
+ font-weight: 600;
1000
+ }
1001
+
1002
+ .checkbox-row input {
1003
+ width: 16px;
1004
+ height: 16px;
1005
+ accent-color: var(--brand);
1006
+ }
1007
+
993
1008
  .pre {
994
1009
  margin: 0;
995
1010
  padding: 14px;
@@ -1465,6 +1480,21 @@ function renderAdminPage() {
1465
1480
  </div>
1466
1481
  </div>
1467
1482
 
1483
+ <div class="field">
1484
+ <label class="checkbox-row" for="proxyEnabled">
1485
+ <input id="proxyEnabled" type="checkbox" />
1486
+ \u542F\u7528\u4E0A\u6E38\u4EE3\u7406
1487
+ </label>
1488
+ <label for="proxyUrl">\u4EE3\u7406\u5730\u5740</label>
1489
+ <input class="input" id="proxyUrl" type="text" placeholder="\u586B\u5199\u4F60\u7684\u4EE3\u7406\u5730\u5740" />
1490
+ <label for="proxyNoProxy">\u76F4\u8FDE\u5730\u5740</label>
1491
+ <input class="input" id="proxyNoProxy" type="text" placeholder="localhost,127.0.0.1,::1" />
1492
+ <p class="hint">\u542F\u7528\u540E\uFF0COAuth \u6362\u53D6 token\u3001\u6A21\u578B\u5237\u65B0\u548C\u63A5\u53E3\u8F6C\u53D1\u4F1A\u901A\u8FC7\u6B64\u4EE3\u7406\u8BBF\u95EE\u6D77\u5916\u4E0A\u6E38\u3002</p>
1493
+ <div class="actions">
1494
+ <button class="btn-primary" id="saveProxyBtn" type="button">\u4FDD\u5B58\u4EE3\u7406\u914D\u7F6E</button>
1495
+ </div>
1496
+ </div>
1497
+
1468
1498
  <div class="field">
1469
1499
  <label for="requestBody">\u8BF7\u6C42\u4F53 JSON</label>
1470
1500
  <textarea class="textarea" id="requestBody" spellcheck="false"></textarea>
@@ -1638,6 +1668,9 @@ function renderAdminPage() {
1638
1668
  const profileSearch = document.getElementById("profileSearch");
1639
1669
  const profileStatusFilter = document.getElementById("profileStatusFilter");
1640
1670
  const profileSort = document.getElementById("profileSort");
1671
+ const proxyEnabled = document.getElementById("proxyEnabled");
1672
+ const proxyUrl = document.getElementById("proxyUrl");
1673
+ const proxyNoProxy = document.getElementById("proxyNoProxy");
1641
1674
 
1642
1675
  function setBusy(button, busy) {
1643
1676
  if (button) {
@@ -2567,6 +2600,7 @@ function renderAdminPage() {
2567
2600
  ["Base URL", config.baseUrl],
2568
2601
  ["Provider", config.status.activeProvider || "openai-codex"],
2569
2602
  ["\u9ED8\u8BA4\u6A21\u578B", config.settings.defaultModel],
2603
+ ["\u4E0A\u6E38\u4EE3\u7406", config.settings.networkProxy && config.settings.networkProxy.enabled ? "\u5DF2\u542F\u7528" : "\u672A\u542F\u7528"],
2570
2604
  ["\u5F53\u524D\u7248\u672C", getVersionValue(config)],
2571
2605
  ["\u5F53\u524D\u5957\u9910", config.profile ? getPlanType(config.profile) : "\u672A\u767B\u5F55"],
2572
2606
  ["\u751F\u56FE\u80FD\u529B", getImageCapability(config.profile).detail],
@@ -2585,6 +2619,7 @@ function renderAdminPage() {
2585
2619
  ["API Base URL", config.baseUrl],
2586
2620
  ["\u5F53\u524D\u8D26\u53F7", getProfileDisplayLabel(config.profile)],
2587
2621
  ["\u9ED8\u8BA4\u6A21\u578B", config.settings.defaultModel],
2622
+ ["\u4E0A\u6E38\u4EE3\u7406", config.settings.networkProxy && config.settings.networkProxy.enabled ? config.settings.networkProxy.url : "\u672A\u542F\u7528"],
2588
2623
  ["\u7248\u672C\u72B6\u6001", getVersionDetail(config)],
2589
2624
  ["\u5F53\u524D\u5957\u9910", config.profile ? getPlanType(config.profile) : "\u672A\u767B\u5F55"],
2590
2625
  ["\u751F\u56FE\u80FD\u529B", getImageCapability(config.profile).detail],
@@ -2640,6 +2675,17 @@ function renderAdminPage() {
2640
2675
  hint.textContent = parts.join("\uFF0C") + "\u3002";
2641
2676
  }
2642
2677
 
2678
+ function renderProxySettings(config) {
2679
+ const proxy = config.settings.networkProxy || {
2680
+ enabled: false,
2681
+ url: "",
2682
+ noProxy: "localhost,127.0.0.1,::1",
2683
+ };
2684
+ proxyEnabled.checked = !!proxy.enabled;
2685
+ proxyUrl.value = proxy.url || "";
2686
+ proxyNoProxy.value = proxy.noProxy || "localhost,127.0.0.1,::1";
2687
+ }
2688
+
2643
2689
  function syncHero(config) {
2644
2690
  const profileText = config.profile
2645
2691
  ? "\u5F53\u524D\u8D26\u53F7\u4E3A " + getProfileDisplayLabel(config.profile) + "\uFF0C\u5957\u9910 " + getPlanType(config.profile) + "\uFF0C\u53EF\u5728\u53F3\u4FA7\u5B8C\u6210\u6A21\u578B\u5207\u6362\u548C\u63A5\u53E3\u8C03\u8BD5\u3002"
@@ -2676,6 +2722,7 @@ function renderAdminPage() {
2676
2722
  renderProfiles(config);
2677
2723
  renderModelOptions(config);
2678
2724
  renderModelCatalogStatus(config);
2725
+ renderProxySettings(config);
2679
2726
  renderUpdatePanel(config);
2680
2727
  renderEndpoints(config);
2681
2728
  renderServiceInfo(config);
@@ -2880,6 +2927,33 @@ function renderAdminPage() {
2880
2927
  }
2881
2928
  }
2882
2929
 
2930
+ async function saveProxy() {
2931
+ const button = document.getElementById("saveProxyBtn");
2932
+ setBusy(button, true);
2933
+ authStatus.textContent = "\u6B63\u5728\u4FDD\u5B58\u4EE3\u7406\u914D\u7F6E...";
2934
+ try {
2935
+ const config = await fetchJson("/_gateway/admin/settings", {
2936
+ method: "PUT",
2937
+ headers: {
2938
+ "Content-Type": "application/json",
2939
+ },
2940
+ body: formatJson({
2941
+ networkProxy: {
2942
+ enabled: proxyEnabled.checked,
2943
+ url: proxyUrl.value,
2944
+ noProxy: proxyNoProxy.value,
2945
+ },
2946
+ }),
2947
+ });
2948
+ renderConfig(config);
2949
+ authStatus.textContent = proxyEnabled.checked ? "\u4EE3\u7406\u914D\u7F6E\u5DF2\u542F\u7528\u3002" : "\u4EE3\u7406\u914D\u7F6E\u5DF2\u5173\u95ED\u3002";
2950
+ } catch (error) {
2951
+ authStatus.textContent = error.message;
2952
+ } finally {
2953
+ setBusy(button, false);
2954
+ }
2955
+ }
2956
+
2883
2957
  async function refreshModels() {
2884
2958
  const button = document.getElementById("refreshModelsBtn");
2885
2959
  setBusy(button, true);
@@ -3034,6 +3108,7 @@ function renderAdminPage() {
3034
3108
  document.getElementById("closeImagePreviewBtn").addEventListener("click", closeImagePreviewModal);
3035
3109
  document.getElementById("refreshModelsBtn").addEventListener("click", refreshModels);
3036
3110
  document.getElementById("saveModelBtn").addEventListener("click", saveModel);
3111
+ document.getElementById("saveProxyBtn").addEventListener("click", saveProxy);
3037
3112
  runTestBtn.addEventListener("click", runTest);
3038
3113
  document.querySelectorAll("[data-result-tab]").forEach(function (button) {
3039
3114
  button.addEventListener("click", function () {
@@ -67,7 +67,12 @@ const chatCompletionsBodySchema = z.object({
67
67
  user: z.string().optional()
68
68
  }).passthrough();
69
69
  const settingsUpdateSchema = z.object({
70
- defaultModel: z.string().min(1)
70
+ defaultModel: z.string().min(1).optional(),
71
+ networkProxy: z.object({
72
+ enabled: z.boolean(),
73
+ url: z.string().optional(),
74
+ noProxy: z.string().optional()
75
+ }).optional()
71
76
  });
72
77
  const profileActionSchema = z.object({
73
78
  profileId: z.string().min(1)
@@ -77,7 +82,7 @@ const imageGenerationsBodySchema = z.object({
77
82
  model: z.string().optional(),
78
83
  n: z.number().int().positive().optional(),
79
84
  quality: z.enum(["low", "medium", "high", "auto"]).optional(),
80
- size: z.enum(["1024x1024", "1024x1536", "1536x1024", "auto"]).optional(),
85
+ size: z.string().min(1).optional(),
81
86
  background: z.enum(["transparent", "opaque", "auto"]).optional(),
82
87
  output_format: z.enum(["png", "webp", "jpeg"]).optional(),
83
88
  output_compression: z.number().int().min(0).max(100).optional(),
@@ -473,7 +478,12 @@ function createApp(params) {
473
478
  }
474
479
  };
475
480
  }
476
- await ctx.configService.setDefaultModel(parsed.data.defaultModel);
481
+ if (parsed.data.defaultModel) {
482
+ await ctx.configService.setDefaultModel(parsed.data.defaultModel);
483
+ }
484
+ if (parsed.data.networkProxy) {
485
+ await ctx.configService.setNetworkProxy(parsed.data.networkProxy);
486
+ }
477
487
  return buildAdminConfig(request);
478
488
  });
479
489
  app.get("/v1/models", async () => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-zero-token",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Local-first OpenAI-compatible AI CLI and gateway with Codex OAuth, multi-account management, and gpt-image-2 image generation.",
5
5
  "license": "MIT",
6
6
  "type": "module",