@chrysb/alphaclaw 0.9.24 → 0.9.26

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.
@@ -1,5 +1,6 @@
1
1
  const fs = require("fs");
2
2
  const path = require("path");
3
+ const { DatabaseSync } = require("node:sqlite");
3
4
  const { AUTH_PROFILES_PATH, CODEX_PROFILE_ID, OPENCLAW_DIR } = require("./constants");
4
5
 
5
6
  const kDefaultAgentId = "main";
@@ -54,6 +55,86 @@ const resolveAgentDir = (agentId = kDefaultAgentId) =>
54
55
  const resolveAuthProfilesPath = (agentId = kDefaultAgentId) =>
55
56
  path.join(resolveAgentDir(agentId), "auth-profiles.json");
56
57
 
58
+ const resolveAuthDatabasePath = (agentId = kDefaultAgentId) =>
59
+ path.join(resolveAgentDir(agentId), "openclaw-agent.sqlite");
60
+
61
+ const loadSqliteAuthStore = (agentId = kDefaultAgentId) => {
62
+ const databasePath = resolveAuthDatabasePath(agentId);
63
+ if (!fs.existsSync(databasePath)) return null;
64
+ let database;
65
+ try {
66
+ database = new DatabaseSync(databasePath, { readOnly: true });
67
+ const secretsRow = database
68
+ .prepare("SELECT store_json FROM auth_profile_store WHERE store_key = ?")
69
+ .get("primary");
70
+ if (!secretsRow?.store_json) return null;
71
+ const secrets = JSON.parse(secretsRow.store_json);
72
+ const stateRow = database
73
+ .prepare("SELECT state_json FROM auth_profile_state WHERE state_key = ?")
74
+ .get("primary");
75
+ const state = stateRow?.state_json ? JSON.parse(stateRow.state_json) : {};
76
+ return {
77
+ version: Number(secrets.version || state.version || 1),
78
+ profiles: secrets.profiles || {},
79
+ order: state.order,
80
+ lastGood: state.lastGood,
81
+ usageStats: state.usageStats,
82
+ };
83
+ } catch {
84
+ return null;
85
+ } finally {
86
+ database?.close();
87
+ }
88
+ };
89
+
90
+ const saveSqliteAuthStore = (agentId, store) => {
91
+ const databasePath = resolveAuthDatabasePath(agentId);
92
+ if (!fs.existsSync(databasePath)) return false;
93
+ let database;
94
+ try {
95
+ database = new DatabaseSync(databasePath);
96
+ const now = Date.now();
97
+ const secrets = {
98
+ version: Number(store.version || 1),
99
+ profiles: store.profiles || {},
100
+ };
101
+ const state = {
102
+ version: Number(store.version || 1),
103
+ ...(store.order !== undefined ? { order: store.order } : {}),
104
+ ...(store.lastGood !== undefined ? { lastGood: store.lastGood } : {}),
105
+ ...(store.usageStats !== undefined ? { usageStats: store.usageStats } : {}),
106
+ };
107
+ database.exec("BEGIN IMMEDIATE");
108
+ database
109
+ .prepare(
110
+ `INSERT INTO auth_profile_store (store_key, store_json, updated_at)
111
+ VALUES (?, ?, ?)
112
+ ON CONFLICT(store_key) DO UPDATE SET
113
+ store_json = excluded.store_json,
114
+ updated_at = excluded.updated_at`,
115
+ )
116
+ .run("primary", JSON.stringify(secrets), now);
117
+ database
118
+ .prepare(
119
+ `INSERT INTO auth_profile_state (state_key, state_json, updated_at)
120
+ VALUES (?, ?, ?)
121
+ ON CONFLICT(state_key) DO UPDATE SET
122
+ state_json = excluded.state_json,
123
+ updated_at = excluded.updated_at`,
124
+ )
125
+ .run("primary", JSON.stringify(state), now);
126
+ database.exec("COMMIT");
127
+ return true;
128
+ } catch {
129
+ try {
130
+ database?.exec("ROLLBACK");
131
+ } catch {}
132
+ return false;
133
+ } finally {
134
+ database?.close();
135
+ }
136
+ };
137
+
57
138
  const resolveOpenclawConfigPath = () =>
58
139
  path.join(OPENCLAW_DIR, "openclaw.json");
59
140
 
@@ -61,6 +142,8 @@ const hasCompletedOnboardingConfig = (cfg) =>
61
142
  String(cfg?.agents?.defaults?.model?.primary || "").trim().includes("/");
62
143
 
63
144
  const loadAuthStore = (agentId = kDefaultAgentId) => {
145
+ const sqliteStore = loadSqliteAuthStore(agentId);
146
+ if (sqliteStore) return sqliteStore;
64
147
  const storePath = resolveAuthProfilesPath(agentId);
65
148
  let store = { version: 1, profiles: {} };
66
149
  try {
@@ -86,6 +169,7 @@ const loadAuthStore = (agentId = kDefaultAgentId) => {
86
169
  };
87
170
 
88
171
  const saveAuthStore = (agentId, store) => {
172
+ if (saveSqliteAuthStore(agentId, store)) return;
89
173
  const storePath = resolveAuthProfilesPath(agentId);
90
174
  fs.mkdirSync(path.dirname(storePath), { recursive: true });
91
175
  fs.writeFileSync(
@@ -256,12 +340,41 @@ const createAuthProfiles = () => {
256
340
 
257
341
  // ── Model config operations ──
258
342
 
343
+ const preserveCodexRuntimeModels = (configuredModels) => {
344
+ const models =
345
+ configuredModels && typeof configuredModels === "object"
346
+ ? configuredModels
347
+ : {};
348
+ if (!hasCodexOauthProfile()) return models;
349
+ return Object.fromEntries(
350
+ Object.entries(models).map(([modelKey, modelConfig]) => {
351
+ if (!modelKey.startsWith("openai/gpt-")) {
352
+ return [modelKey, modelConfig];
353
+ }
354
+ return [
355
+ modelKey,
356
+ {
357
+ ...(modelConfig && typeof modelConfig === "object"
358
+ ? modelConfig
359
+ : {}),
360
+ agentRuntime: { id: "codex" },
361
+ },
362
+ ];
363
+ }),
364
+ );
365
+ };
366
+
259
367
  const getModelConfig = () => {
260
368
  const cfg = loadOpenclawConfig();
261
369
  const defaults = cfg.agents?.defaults || {};
370
+ const configuredModels = preserveCodexRuntimeModels(defaults.models || {});
371
+ if (JSON.stringify(configuredModels) !== JSON.stringify(defaults.models || {})) {
372
+ cfg.agents.defaults.models = configuredModels;
373
+ saveOpenclawConfig(cfg);
374
+ }
262
375
  return {
263
376
  primary: defaults.model?.primary || null,
264
- configuredModels: defaults.models || {},
377
+ configuredModels,
265
378
  };
266
379
  };
267
380
 
@@ -274,7 +387,7 @@ const createAuthProfiles = () => {
274
387
  cfg.agents.defaults.model.primary = primary;
275
388
  }
276
389
  if (configuredModels !== undefined) {
277
- cfg.agents.defaults.models = configuredModels;
390
+ cfg.agents.defaults.models = preserveCodexRuntimeModels(configuredModels);
278
391
  }
279
392
  saveOpenclawConfig(cfg);
280
393
  };
@@ -1,6 +1,7 @@
1
1
  const fs = require("fs");
2
2
  const path = require("path");
3
3
  const { pathToFileURL } = require("url");
4
+ const { DatabaseSync } = require("node:sqlite");
4
5
 
5
6
  const findOpenclawDistModule = (prefix) => {
6
7
  const entryPath = require.resolve("openclaw");
@@ -16,6 +17,53 @@ const writeConfig = (configPath, cfg) => {
16
17
  fs.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}\n`, "utf8");
17
18
  };
18
19
 
20
+ const hasCanonicalCodexOauthProfile = (configPath) => {
21
+ const databasePath = path.join(
22
+ path.dirname(configPath),
23
+ "agents",
24
+ "main",
25
+ "agent",
26
+ "openclaw-agent.sqlite",
27
+ );
28
+ if (!fs.existsSync(databasePath)) return false;
29
+ let database;
30
+ try {
31
+ database = new DatabaseSync(databasePath, { readOnly: true });
32
+ const row = database
33
+ .prepare("SELECT store_json FROM auth_profile_store WHERE store_key = ?")
34
+ .get("primary");
35
+ const store = row?.store_json ? JSON.parse(row.store_json) : {};
36
+ return Object.values(store.profiles || {}).some(
37
+ (profile) =>
38
+ profile?.type === "oauth" &&
39
+ profile?.provider === "openai" &&
40
+ profile?.access &&
41
+ profile?.refresh,
42
+ );
43
+ } catch {
44
+ return false;
45
+ } finally {
46
+ database?.close();
47
+ }
48
+ };
49
+
50
+ const restoreCanonicalCodexRuntimeModels = ({ configPath, cfg }) => {
51
+ if (!hasCanonicalCodexOauthProfile(configPath)) return false;
52
+ const configuredModels = cfg?.agents?.defaults?.models;
53
+ if (!configuredModels || typeof configuredModels !== "object") return false;
54
+ let changed = false;
55
+ for (const [modelKey, modelConfig] of Object.entries(configuredModels)) {
56
+ if (!modelKey.startsWith("openai/gpt-")) continue;
57
+ if (modelConfig?.agentRuntime?.id === "codex") continue;
58
+ configuredModels[modelKey] = {
59
+ ...(modelConfig && typeof modelConfig === "object" ? modelConfig : {}),
60
+ agentRuntime: { id: "codex" },
61
+ };
62
+ changed = true;
63
+ }
64
+ return changed;
65
+ };
66
+
19
67
  const migrateLegacyCodexState = async ({
20
68
  configPath = process.env.OPENCLAW_CONFIG_PATH,
21
69
  env = process.env,
@@ -60,6 +108,11 @@ const migrateLegacyCodexState = async ({
60
108
  warnings.push(...sqliteMigration.warnings);
61
109
  if (sqliteMigration.configChanged) writeConfig(configPath, nextCfg);
62
110
 
111
+ if (restoreCanonicalCodexRuntimeModels({ configPath, cfg: nextCfg })) {
112
+ changes.push("Restored Codex runtime metadata for canonical OpenAI models.");
113
+ writeConfig(configPath, nextCfg);
114
+ }
115
+
63
116
  const sessionRepair = await routeModule.i({
64
117
  cfg: nextCfg,
65
118
  env,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.9.24",
3
+ "version": "0.9.26",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },