@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
|
|
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,
|