ai-zero-token 1.0.3 → 1.0.4

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,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.4 - 2026-04-24
4
+
5
+ - Moved persistent account and settings state to the user home directory at `~/.ai-zero-token/.state`.
6
+ - Added automatic one-time migration from the old package-local `.state` directory when available.
7
+ - Added `AI_ZERO_TOKEN_HOME` support for overriding the persistent state location.
8
+ - Fixed repeated login prompts after npm upgrades or global package reinstalls.
9
+
3
10
  ## 1.0.3 - 2026-04-24
4
11
 
5
12
  - Added dynamic Codex model discovery from the local `~/.codex/models_cache.json` cache, with static model fallback when the cache is unavailable.
@@ -11,4 +18,3 @@
11
18
  - Improved image generation error handling with transient retries and clearer failure details.
12
19
  - Preserved response headers when using the curl HTTP fallback so quota metadata can still be captured.
13
20
  - Added Vibe Coding / OpenAI-compatible client integration documentation.
14
-
@@ -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,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 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,
@@ -16,12 +16,10 @@ function createDefaultSettings() {
16
16
  }
17
17
  };
18
18
  }
19
- function getSettingsPath() {
20
- return settingsPath;
21
- }
22
19
  async function loadSettings() {
23
20
  try {
24
- const raw = await fs.readFile(settingsPath, "utf8");
21
+ await ensureStateMigrated();
22
+ const raw = await fs.readFile(getSettingsPath(), "utf8");
25
23
  const parsed = JSON.parse(raw);
26
24
  const defaults = createDefaultSettings();
27
25
  return {
@@ -38,8 +36,9 @@ async function loadSettings() {
38
36
  }
39
37
  }
40
38
  async function saveSettings(settings) {
41
- await fs.mkdir(stateDir, { recursive: true });
42
- await fs.writeFile(settingsPath, `${JSON.stringify(settings, null, 2)}
39
+ await ensureStateMigrated();
40
+ await fs.mkdir(getStateDir(), { recursive: true });
41
+ await fs.writeFile(getSettingsPath(), `${JSON.stringify(settings, null, 2)}
43
42
  `, "utf8");
44
43
  }
45
44
  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
+ };
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.4",
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",