@chrysb/alphaclaw 0.9.29 → 0.9.30
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/README.md +1 -1
- package/bin/alphaclaw.js +34 -0
- package/lib/node-runtime.js +38 -0
- package/lib/public/dist/app.bundle.js +795 -793
- package/lib/public/js/components/gateway.js +7 -2
- package/lib/public/js/components/models-tab/model-picker.js +5 -2
- package/lib/public/js/components/models-tab/use-models.js +4 -1
- package/lib/public/js/components/nodes-tab/setup-wizard/index.js +3 -1
- package/lib/public/js/components/welcome/use-welcome.js +4 -1
- package/lib/public/js/lib/model-config.js +49 -2
- package/lib/public/js/lib/thinking-levels.js +1 -0
- package/lib/server/agents/shared.js +2 -0
- package/lib/server/auth-profiles.js +8 -1
- package/lib/server/codex-runtime-config.js +51 -0
- package/lib/server/constants.js +18 -0
- package/lib/server/cost-utils.js +18 -0
- package/lib/server/helpers.js +18 -2
- package/lib/server/model-catalog-cache.js +2 -2
- package/lib/server/onboarding/openclaw.js +4 -0
- package/lib/server/onboarding/validation.js +3 -0
- package/lib/server/openclaw-codex-migration.js +61 -17
- package/lib/server/openclaw-thinking.js +33 -27
- package/lib/server/openclaw-version.js +7 -0
- package/lib/server/plugin-config.js +22 -0
- package/lib/server/routes/models.js +4 -0
- package/lib/server/usage-tracker-config.js +3 -21
- package/lib/server/watchdog.js +43 -0
- package/package.json +3 -3
|
@@ -36,6 +36,8 @@ export const Gateway = ({
|
|
|
36
36
|
watchdogStatus?.lifecycle === "crash_loop"
|
|
37
37
|
? "crash_loop"
|
|
38
38
|
: watchdogStatus?.health;
|
|
39
|
+
const hasConfigurationError =
|
|
40
|
+
watchdogStatus?.lifecycle === "configuration_error";
|
|
39
41
|
const watchdogDotClass =
|
|
40
42
|
watchdogHealth === "healthy"
|
|
41
43
|
? "ac-status-dot ac-status-dot--healthy ac-status-dot--healthy-offset"
|
|
@@ -44,8 +46,11 @@ export const Gateway = ({
|
|
|
44
46
|
: watchdogHealth === "unhealthy" || watchdogHealth === "crash_loop"
|
|
45
47
|
? "bg-red-500"
|
|
46
48
|
: "bg-gray-500";
|
|
47
|
-
const watchdogLabel =
|
|
48
|
-
|
|
49
|
+
const watchdogLabel = hasConfigurationError
|
|
50
|
+
? "configuration error"
|
|
51
|
+
: watchdogHealth === "unknown"
|
|
52
|
+
? "initializing"
|
|
53
|
+
: watchdogHealth || "unknown";
|
|
49
54
|
const isRepairInProgress = repairing || !!watchdogStatus?.operationInProgress;
|
|
50
55
|
const showInspectButton = watchdogHealth === "degraded" && !!onOpenWatchdog;
|
|
51
56
|
const showRepairButton =
|
|
@@ -4,6 +4,7 @@ import htm from "htm";
|
|
|
4
4
|
import {
|
|
5
5
|
getModelProvider,
|
|
6
6
|
getAuthProviderFromModelProvider,
|
|
7
|
+
getFriendlyModelLabel,
|
|
7
8
|
kProviderLabels,
|
|
8
9
|
kProviderOrder,
|
|
9
10
|
} from "../../lib/model-config.js";
|
|
@@ -79,11 +80,13 @@ export const buildSyntheticModelEntry = (modelKey) => {
|
|
|
79
80
|
return {
|
|
80
81
|
key,
|
|
81
82
|
provider,
|
|
82
|
-
label: anthropicLabel || key,
|
|
83
|
+
label: anthropicLabel || getFriendlyModelLabel(key),
|
|
83
84
|
};
|
|
84
85
|
};
|
|
85
86
|
|
|
86
|
-
export const getModelDisplayLabel = (model) =>
|
|
87
|
+
export const getModelDisplayLabel = (model) =>
|
|
88
|
+
model?.featuredLabel ||
|
|
89
|
+
getFriendlyModelLabel(model?.key, model?.label || model?.key);
|
|
87
90
|
|
|
88
91
|
const buildModelSearchText = (model) =>
|
|
89
92
|
[
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
kModelCatalogCacheKey,
|
|
17
17
|
kModelCatalogPollIntervalMs,
|
|
18
18
|
} from "../../lib/model-catalog.js";
|
|
19
|
+
import { withAlwaysAvailableModels } from "../../lib/model-config.js";
|
|
19
20
|
|
|
20
21
|
let kModelsTabCache = null;
|
|
21
22
|
const getCredentialValue = (value) =>
|
|
@@ -199,7 +200,9 @@ export const useModels = (agentId) => {
|
|
|
199
200
|
const addModel = useCallback(
|
|
200
201
|
(modelKey) => {
|
|
201
202
|
if (!modelKey) return;
|
|
202
|
-
const catalogEntry = catalog.find(
|
|
203
|
+
const catalogEntry = withAlwaysAvailableModels(catalog).find(
|
|
204
|
+
(model) => model?.key === modelKey,
|
|
205
|
+
);
|
|
203
206
|
const modelConfig = catalogEntry?.agentRuntime
|
|
204
207
|
? { agentRuntime: catalogEntry.agentRuntime }
|
|
205
208
|
: {};
|
|
@@ -100,7 +100,9 @@ export const NodesSetupWizard = ({
|
|
|
100
100
|
onCopy: () =>
|
|
101
101
|
copyAndToast("npm install -g openclaw", "command"),
|
|
102
102
|
})}
|
|
103
|
-
<div class="text-xs text-fg-muted">
|
|
103
|
+
<div class="text-xs text-fg-muted">
|
|
104
|
+
Requires Node.js ${">=22.22.3 <23, >=24.15.0 <25, or >=25.9.0."}
|
|
105
|
+
</div>
|
|
104
106
|
`
|
|
105
107
|
: null
|
|
106
108
|
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
isDeprecatedOnboardingModelKey,
|
|
17
17
|
getVisibleAiFieldKeys,
|
|
18
18
|
kProviderAuthFields,
|
|
19
|
+
withAlwaysAvailableModels,
|
|
19
20
|
} from "../../lib/model-config.js";
|
|
20
21
|
import {
|
|
21
22
|
getInitialOnboardingModelKey,
|
|
@@ -169,7 +170,9 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
169
170
|
};
|
|
170
171
|
|
|
171
172
|
const applyModelCatalog = useCallback((payload) => {
|
|
172
|
-
const list = getOnboardingModels(
|
|
173
|
+
const list = getOnboardingModels(
|
|
174
|
+
withAlwaysAvailableModels(getModelCatalogModels(payload)),
|
|
175
|
+
);
|
|
173
176
|
if (!payload) return;
|
|
174
177
|
const isRefreshing = isModelCatalogRefreshing(payload);
|
|
175
178
|
const isFallbackRefresh =
|
|
@@ -25,6 +25,10 @@ export const kFeaturedModelDefs = [
|
|
|
25
25
|
label: "Sonnet 4.6",
|
|
26
26
|
preferredKeys: ["anthropic/claude-sonnet-4-6"],
|
|
27
27
|
},
|
|
28
|
+
{
|
|
29
|
+
label: "GPT-5.6 Sol",
|
|
30
|
+
preferredKeys: ["openai/gpt-5.6-sol", "openai-codex/gpt-5.6-sol"],
|
|
31
|
+
},
|
|
28
32
|
{
|
|
29
33
|
label: "GPT-5.5",
|
|
30
34
|
preferredKeys: ["openai/gpt-5.5", "openai-codex/gpt-5.5"],
|
|
@@ -42,7 +46,22 @@ const kDeprecatedOnboardingModelKeys = new Set([
|
|
|
42
46
|
const kCanonicalCodexOauthModelKeys = new Set([
|
|
43
47
|
"openai/gpt-5.4-mini",
|
|
44
48
|
"openai/gpt-5.5",
|
|
49
|
+
"openai/gpt-5.6-sol",
|
|
50
|
+
"openai/gpt-5.6-terra",
|
|
51
|
+
"openai/gpt-5.6-luna",
|
|
45
52
|
]);
|
|
53
|
+
const kFriendlyModelLabels = {
|
|
54
|
+
"openai/gpt-5.4-mini": "GPT-5.4 Mini",
|
|
55
|
+
"openai/gpt-5.5": "GPT-5.5",
|
|
56
|
+
"openai/gpt-5.6-sol": "GPT-5.6 Sol",
|
|
57
|
+
"openai/gpt-5.6-terra": "GPT-5.6 Terra",
|
|
58
|
+
"openai/gpt-5.6-luna": "GPT-5.6 Luna",
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const getFriendlyModelLabel = (modelKey, fallback = "") => {
|
|
62
|
+
const normalizedKey = String(modelKey || "").trim();
|
|
63
|
+
return kFriendlyModelLabels[normalizedKey] || fallback || normalizedKey;
|
|
64
|
+
};
|
|
46
65
|
|
|
47
66
|
export const isDeprecatedOnboardingModelKey = (modelKey) =>
|
|
48
67
|
kDeprecatedOnboardingModelKeys.has(String(modelKey || "").trim());
|
|
@@ -66,6 +85,24 @@ export const getOnboardingModelProvider = ({ modelKey, models = [] } = {}) => {
|
|
|
66
85
|
};
|
|
67
86
|
|
|
68
87
|
export const kAlwaysAvailableModelDefs = [
|
|
88
|
+
{
|
|
89
|
+
key: "openai/gpt-5.6-sol",
|
|
90
|
+
provider: "openai",
|
|
91
|
+
label: "GPT-5.6 Sol",
|
|
92
|
+
agentRuntime: { id: "codex" },
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
key: "openai/gpt-5.6-terra",
|
|
96
|
+
provider: "openai",
|
|
97
|
+
label: "GPT-5.6 Terra",
|
|
98
|
+
agentRuntime: { id: "codex" },
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
key: "openai/gpt-5.6-luna",
|
|
102
|
+
provider: "openai",
|
|
103
|
+
label: "GPT-5.6 Luna",
|
|
104
|
+
agentRuntime: { id: "codex" },
|
|
105
|
+
},
|
|
69
106
|
{
|
|
70
107
|
key: "openai/gpt-5.4-mini",
|
|
71
108
|
provider: "openai",
|
|
@@ -82,9 +119,19 @@ export const kAlwaysAvailableModelDefs = [
|
|
|
82
119
|
|
|
83
120
|
export const withAlwaysAvailableModels = (models = []) => {
|
|
84
121
|
const merged = [...models];
|
|
85
|
-
const keys = new Set(merged.map((model) => model?.key));
|
|
86
122
|
for (const model of kAlwaysAvailableModelDefs) {
|
|
87
|
-
|
|
123
|
+
const existingIndex = merged.findIndex((candidate) => candidate?.key === model.key);
|
|
124
|
+
if (existingIndex < 0) {
|
|
125
|
+
merged.push(model);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const existing = merged[existingIndex];
|
|
129
|
+
merged[existingIndex] = {
|
|
130
|
+
...model,
|
|
131
|
+
...existing,
|
|
132
|
+
label: getFriendlyModelLabel(model.key, existing?.label || model.label),
|
|
133
|
+
agentRuntime: existing?.agentRuntime || model.agentRuntime,
|
|
134
|
+
};
|
|
88
135
|
}
|
|
89
136
|
return merged;
|
|
90
137
|
};
|
|
@@ -3,6 +3,7 @@ const {
|
|
|
3
3
|
readOpenclawConfig,
|
|
4
4
|
writeOpenclawConfig,
|
|
5
5
|
} = require("../openclaw-config");
|
|
6
|
+
const { ensureCodexRuntimePlugin } = require("../codex-runtime-config");
|
|
6
7
|
|
|
7
8
|
const kDefaultAgentId = "main";
|
|
8
9
|
const kAgentIdPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
@@ -105,6 +106,7 @@ const loadConfig = ({ fsImpl, OPENCLAW_DIR }) =>
|
|
|
105
106
|
});
|
|
106
107
|
|
|
107
108
|
const saveConfig = ({ fsImpl, OPENCLAW_DIR, config }) => {
|
|
109
|
+
ensureCodexRuntimePlugin(config);
|
|
108
110
|
writeOpenclawConfig({
|
|
109
111
|
fsModule: fsImpl,
|
|
110
112
|
openclawDir: OPENCLAW_DIR,
|
|
@@ -2,6 +2,7 @@ const fs = require("fs");
|
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const { DatabaseSync } = require("node:sqlite");
|
|
4
4
|
const { AUTH_PROFILES_PATH, CODEX_PROFILE_ID, OPENCLAW_DIR } = require("./constants");
|
|
5
|
+
const { ensureCodexRuntimePlugin } = require("./codex-runtime-config");
|
|
5
6
|
|
|
6
7
|
const kDefaultAgentId = "main";
|
|
7
8
|
const kApiKeyEnvVarByProvider = {
|
|
@@ -368,8 +369,13 @@ const createAuthProfiles = () => {
|
|
|
368
369
|
const cfg = loadOpenclawConfig();
|
|
369
370
|
const defaults = cfg.agents?.defaults || {};
|
|
370
371
|
const configuredModels = preserveCodexRuntimeModels(defaults.models || {});
|
|
371
|
-
|
|
372
|
+
const modelsChanged =
|
|
373
|
+
JSON.stringify(configuredModels) !== JSON.stringify(defaults.models || {});
|
|
374
|
+
if (modelsChanged) {
|
|
372
375
|
cfg.agents.defaults.models = configuredModels;
|
|
376
|
+
}
|
|
377
|
+
const pluginsChanged = ensureCodexRuntimePlugin(cfg);
|
|
378
|
+
if (modelsChanged || pluginsChanged) {
|
|
373
379
|
saveOpenclawConfig(cfg);
|
|
374
380
|
}
|
|
375
381
|
return {
|
|
@@ -389,6 +395,7 @@ const createAuthProfiles = () => {
|
|
|
389
395
|
if (configuredModels !== undefined) {
|
|
390
396
|
cfg.agents.defaults.models = preserveCodexRuntimeModels(configuredModels);
|
|
391
397
|
}
|
|
398
|
+
ensureCodexRuntimePlugin(cfg);
|
|
392
399
|
saveOpenclawConfig(cfg);
|
|
393
400
|
};
|
|
394
401
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const {
|
|
2
|
+
ensurePluginsShell,
|
|
3
|
+
ensurePluginAllowed,
|
|
4
|
+
} = require("./plugin-config");
|
|
5
|
+
|
|
6
|
+
const modelUsesCodexRuntime = (modelKey, modelConfig) => {
|
|
7
|
+
const runtimeId = String(modelConfig?.agentRuntime?.id || "").trim();
|
|
8
|
+
if (runtimeId === "codex") return true;
|
|
9
|
+
if (runtimeId === "openclaw") return false;
|
|
10
|
+
return String(modelKey || "").startsWith("openai/gpt-");
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const configUsesCodexRuntime = (cfg = {}) => {
|
|
14
|
+
const scopes = [
|
|
15
|
+
cfg.agents?.defaults || {},
|
|
16
|
+
...(Array.isArray(cfg.agents?.list) ? cfg.agents.list : []),
|
|
17
|
+
];
|
|
18
|
+
return scopes.some((scope) => {
|
|
19
|
+
const configuredModels = scope.models || {};
|
|
20
|
+
if (
|
|
21
|
+
Object.entries(configuredModels).some(([modelKey, modelConfig]) =>
|
|
22
|
+
modelUsesCodexRuntime(modelKey, modelConfig),
|
|
23
|
+
)
|
|
24
|
+
) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
const primary = String(scope.model?.primary || "").trim();
|
|
28
|
+
return (
|
|
29
|
+
!!primary && modelUsesCodexRuntime(primary, configuredModels[primary])
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const ensureCodexRuntimePlugin = (cfg = {}) => {
|
|
35
|
+
if (!configUsesCodexRuntime(cfg)) return false;
|
|
36
|
+
const before = JSON.stringify(cfg.plugins || {});
|
|
37
|
+
ensurePluginsShell(cfg);
|
|
38
|
+
ensurePluginAllowed({ cfg, pluginKey: "codex" });
|
|
39
|
+
const existingEntry = cfg.plugins.entries.codex;
|
|
40
|
+
cfg.plugins.entries.codex = {
|
|
41
|
+
...(existingEntry && typeof existingEntry === "object" ? existingEntry : {}),
|
|
42
|
+
enabled: true,
|
|
43
|
+
};
|
|
44
|
+
return JSON.stringify(cfg.plugins) !== before;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
configUsesCodexRuntime,
|
|
49
|
+
ensureCodexRuntimePlugin,
|
|
50
|
+
modelUsesCodexRuntime,
|
|
51
|
+
};
|
package/lib/server/constants.js
CHANGED
|
@@ -172,6 +172,24 @@ const kMinimalFallbackOnboardingModels = [
|
|
|
172
172
|
provider: "anthropic",
|
|
173
173
|
label: "Claude Haiku 4.6",
|
|
174
174
|
},
|
|
175
|
+
{
|
|
176
|
+
key: "openai/gpt-5.6-sol",
|
|
177
|
+
provider: "openai",
|
|
178
|
+
label: "GPT-5.6 Sol",
|
|
179
|
+
agentRuntime: { id: "codex" },
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
key: "openai/gpt-5.6-terra",
|
|
183
|
+
provider: "openai",
|
|
184
|
+
label: "GPT-5.6 Terra",
|
|
185
|
+
agentRuntime: { id: "codex" },
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
key: "openai/gpt-5.6-luna",
|
|
189
|
+
provider: "openai",
|
|
190
|
+
label: "GPT-5.6 Luna",
|
|
191
|
+
agentRuntime: { id: "codex" },
|
|
192
|
+
},
|
|
175
193
|
{
|
|
176
194
|
key: "openai/gpt-5.5",
|
|
177
195
|
provider: "openai",
|
package/lib/server/cost-utils.js
CHANGED
|
@@ -48,6 +48,24 @@ const kGlobalModelPricing = {
|
|
|
48
48
|
"claude-haiku-4-6": { input: 0.8, output: 4.0 },
|
|
49
49
|
"gpt-5": { input: 1.25, output: 10.0 },
|
|
50
50
|
"gpt-5.4": { input: 2.5, output: 10.0 },
|
|
51
|
+
"gpt-5.6-sol": {
|
|
52
|
+
input: 5.0,
|
|
53
|
+
output: 30.0,
|
|
54
|
+
cacheRead: 0.5,
|
|
55
|
+
cacheWrite: 6.25,
|
|
56
|
+
},
|
|
57
|
+
"gpt-5.6-terra": {
|
|
58
|
+
input: 2.5,
|
|
59
|
+
output: 15.0,
|
|
60
|
+
cacheRead: 0.25,
|
|
61
|
+
cacheWrite: 3.125,
|
|
62
|
+
},
|
|
63
|
+
"gpt-5.6-luna": {
|
|
64
|
+
input: 1.0,
|
|
65
|
+
output: 6.0,
|
|
66
|
+
cacheRead: 0.1,
|
|
67
|
+
cacheWrite: 1.25,
|
|
68
|
+
},
|
|
51
69
|
"gpt-5.1-codex": { input: 2.5, output: 10.0 },
|
|
52
70
|
"gpt-5.3-codex": { input: 2.5, output: 10.0 },
|
|
53
71
|
"gpt-4.1": { input: 2.0, output: 8.0 },
|
package/lib/server/helpers.js
CHANGED
|
@@ -123,13 +123,29 @@ const normalizeOnboardingModels = (models) => {
|
|
|
123
123
|
const provider = resolveModelProvider(key);
|
|
124
124
|
if (!kOnboardingModelProviders.has(provider)) continue;
|
|
125
125
|
if (!deduped.has(key)) {
|
|
126
|
+
const agentRuntime = isCodexRuntimeModel
|
|
127
|
+
? { id: "codex" }
|
|
128
|
+
: model.agentRuntime;
|
|
126
129
|
deduped.set(key, {
|
|
127
130
|
key,
|
|
128
131
|
provider,
|
|
129
132
|
label: model.name || model.key,
|
|
130
|
-
...(
|
|
131
|
-
? {
|
|
133
|
+
...(typeof model.available === "boolean"
|
|
134
|
+
? { available: model.available }
|
|
132
135
|
: {}),
|
|
136
|
+
...(typeof model.reasoning === "boolean"
|
|
137
|
+
? { reasoning: model.reasoning }
|
|
138
|
+
: {}),
|
|
139
|
+
...(Number.isFinite(model.contextWindow)
|
|
140
|
+
? { contextWindow: model.contextWindow }
|
|
141
|
+
: {}),
|
|
142
|
+
...(Number.isFinite(model.maxTokens)
|
|
143
|
+
? { maxTokens: model.maxTokens }
|
|
144
|
+
: {}),
|
|
145
|
+
...(model.compat && typeof model.compat === "object"
|
|
146
|
+
? { compat: model.compat }
|
|
147
|
+
: {}),
|
|
148
|
+
...(agentRuntime ? { agentRuntime } : {}),
|
|
133
149
|
});
|
|
134
150
|
}
|
|
135
151
|
}
|
|
@@ -3,7 +3,7 @@ const path = require("path");
|
|
|
3
3
|
const { ALPHACLAW_DIR, kFallbackOnboardingModels } = require("./constants");
|
|
4
4
|
const { getCommandOutputCandidates } = require("./utils/command-output");
|
|
5
5
|
|
|
6
|
-
const kModelCatalogCacheVersion =
|
|
6
|
+
const kModelCatalogCacheVersion = 2;
|
|
7
7
|
const kModelCatalogRefreshBackoffMs = 30 * 1000;
|
|
8
8
|
const kModelCatalogLoadTimeoutMs = 120 * 1000;
|
|
9
9
|
const kModelCatalogBootstrapSource = "bootstrap";
|
|
@@ -36,9 +36,9 @@ const normalizeCachedModels = ({
|
|
|
36
36
|
} = {}) =>
|
|
37
37
|
normalizeOnboardingModels(
|
|
38
38
|
(Array.isArray(models) ? models : []).map((model) => ({
|
|
39
|
+
...model,
|
|
39
40
|
key: model?.key,
|
|
40
41
|
name: model?.label || model?.name || model?.key,
|
|
41
|
-
...(model?.agentRuntime ? { agentRuntime: model.agentRuntime } : {}),
|
|
42
42
|
})),
|
|
43
43
|
);
|
|
44
44
|
|
|
@@ -5,6 +5,7 @@ const {
|
|
|
5
5
|
ensureUsageTrackerPluginEntry,
|
|
6
6
|
} = require("../usage-tracker-config");
|
|
7
7
|
const { isOpenAiCompatApiEnabled } = require("../alphaclaw-config");
|
|
8
|
+
const { ensureCodexRuntimePlugin } = require("../codex-runtime-config");
|
|
8
9
|
|
|
9
10
|
const kDefaultToolsProfile = "full";
|
|
10
11
|
const kBootstrapExtraFiles = [
|
|
@@ -248,6 +249,7 @@ const writeSanitizedOpenclawConfig = ({
|
|
|
248
249
|
cfg,
|
|
249
250
|
varMap,
|
|
250
251
|
});
|
|
252
|
+
ensureCodexRuntimePlugin(cfg);
|
|
251
253
|
|
|
252
254
|
let content = JSON.stringify(cfg, null, 2);
|
|
253
255
|
const replacements = buildSecretReplacements(varMap, process.env);
|
|
@@ -357,6 +359,8 @@ const writeManagedImportOpenclawConfig = ({
|
|
|
357
359
|
ensurePluginAllowed({ cfg, pluginKey: "whatsapp" });
|
|
358
360
|
}
|
|
359
361
|
|
|
362
|
+
ensureCodexRuntimePlugin(cfg);
|
|
363
|
+
|
|
360
364
|
fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2));
|
|
361
365
|
};
|
|
362
366
|
|
|
@@ -5,6 +5,9 @@ const kAnthropicApiKeyPrefix = "sk-ant-api";
|
|
|
5
5
|
const kCanonicalCodexOauthModelKeys = new Set([
|
|
6
6
|
"openai/gpt-5.4-mini",
|
|
7
7
|
"openai/gpt-5.5",
|
|
8
|
+
"openai/gpt-5.6-sol",
|
|
9
|
+
"openai/gpt-5.6-terra",
|
|
10
|
+
"openai/gpt-5.6-luna",
|
|
8
11
|
]);
|
|
9
12
|
|
|
10
13
|
const usesCodexOauth = (modelKey, provider) =>
|
|
@@ -3,14 +3,36 @@ const path = require("path");
|
|
|
3
3
|
const { pathToFileURL } = require("url");
|
|
4
4
|
const { DatabaseSync } = require("node:sqlite");
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const findOpenclawDistModules = (prefix) => {
|
|
7
7
|
const entryPath = require.resolve("openclaw");
|
|
8
8
|
const distDir = path.dirname(entryPath);
|
|
9
|
-
const
|
|
9
|
+
const filenames = fs
|
|
10
10
|
.readdirSync(distDir)
|
|
11
|
-
.
|
|
12
|
-
if (
|
|
13
|
-
|
|
11
|
+
.filter((name) => name.startsWith(`${prefix}-`) && name.endsWith(".js"));
|
|
12
|
+
if (filenames.length === 0) {
|
|
13
|
+
throw new Error(`OpenClaw migration module not found: ${prefix}`);
|
|
14
|
+
}
|
|
15
|
+
return filenames.map((filename) => path.join(distDir, filename));
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const loadOpenclawMigrationApi = async ({ prefix, functionNames }) => {
|
|
19
|
+
const api = {};
|
|
20
|
+
for (const modulePath of findOpenclawDistModules(prefix)) {
|
|
21
|
+
const mod = await import(pathToFileURL(modulePath).href);
|
|
22
|
+
for (const candidate of Object.values(mod)) {
|
|
23
|
+
if (typeof candidate !== "function") continue;
|
|
24
|
+
if (functionNames.includes(candidate.name) && !api[candidate.name]) {
|
|
25
|
+
api[candidate.name] = candidate;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const missing = functionNames.filter((name) => typeof api[name] !== "function");
|
|
30
|
+
if (missing.length > 0) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`OpenClaw migration exports not found for ${prefix}: ${missing.join(", ")}`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return api;
|
|
14
36
|
};
|
|
15
37
|
|
|
16
38
|
const writeConfig = (configPath, cfg) => {
|
|
@@ -73,33 +95,55 @@ const migrateLegacyCodexState = async ({
|
|
|
73
95
|
}
|
|
74
96
|
|
|
75
97
|
const cfg = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
98
|
+
const routeApi = await loadOpenclawMigrationApi({
|
|
99
|
+
prefix: "codex-route-warnings",
|
|
100
|
+
functionNames: [
|
|
101
|
+
"maybeRepairCodexRoutes",
|
|
102
|
+
"maybeRepairCodexSessionRoutes",
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
const authApi = await loadOpenclawMigrationApi({
|
|
106
|
+
prefix: "doctor-auth-flat-profiles",
|
|
107
|
+
functionNames: [
|
|
108
|
+
"collectOpenAICodexAuthProfileStoreIdMap",
|
|
109
|
+
"maybeRepairOpenAICodexAuthConfig",
|
|
110
|
+
"maybeRepairOpenAICodexAuthProfileStores",
|
|
111
|
+
"maybeMigrateAuthProfileJsonStoresToSqlite",
|
|
112
|
+
],
|
|
113
|
+
});
|
|
82
114
|
|
|
83
115
|
const changes = [];
|
|
84
116
|
const warnings = [];
|
|
85
|
-
const routeRepair =
|
|
117
|
+
const routeRepair = routeApi.maybeRepairCodexRoutes({
|
|
118
|
+
cfg,
|
|
119
|
+
env,
|
|
120
|
+
shouldRepair: true,
|
|
121
|
+
});
|
|
86
122
|
let nextCfg = routeRepair.cfg;
|
|
87
123
|
changes.push(...routeRepair.changes);
|
|
88
124
|
warnings.push(...routeRepair.warnings);
|
|
89
125
|
|
|
90
|
-
const profileIdMap =
|
|
91
|
-
|
|
126
|
+
const profileIdMap = authApi.collectOpenAICodexAuthProfileStoreIdMap({
|
|
127
|
+
cfg: nextCfg,
|
|
128
|
+
env,
|
|
129
|
+
});
|
|
130
|
+
const configAuthRepair = authApi.maybeRepairOpenAICodexAuthConfig(nextCfg, {
|
|
131
|
+
profileIdMap,
|
|
132
|
+
});
|
|
92
133
|
nextCfg = configAuthRepair.config;
|
|
93
134
|
changes.push(...configAuthRepair.changes);
|
|
94
135
|
warnings.push(...configAuthRepair.warnings);
|
|
95
136
|
|
|
96
137
|
if (changes.length > 0) writeConfig(configPath, nextCfg);
|
|
97
138
|
|
|
98
|
-
const storeRepair = await
|
|
139
|
+
const storeRepair = await authApi.maybeRepairOpenAICodexAuthProfileStores({
|
|
140
|
+
cfg: nextCfg,
|
|
141
|
+
env,
|
|
142
|
+
});
|
|
99
143
|
changes.push(...storeRepair.changes);
|
|
100
144
|
warnings.push(...storeRepair.warnings);
|
|
101
145
|
|
|
102
|
-
const sqliteMigration = await
|
|
146
|
+
const sqliteMigration = await authApi.maybeMigrateAuthProfileJsonStoresToSqlite({
|
|
103
147
|
cfg: nextCfg,
|
|
104
148
|
env,
|
|
105
149
|
prompter: { confirmAutoFix: async () => true },
|
|
@@ -113,7 +157,7 @@ const migrateLegacyCodexState = async ({
|
|
|
113
157
|
writeConfig(configPath, nextCfg);
|
|
114
158
|
}
|
|
115
159
|
|
|
116
|
-
const sessionRepair = await
|
|
160
|
+
const sessionRepair = await routeApi.maybeRepairCodexSessionRoutes({
|
|
117
161
|
cfg: nextCfg,
|
|
118
162
|
env,
|
|
119
163
|
shouldRepair: true,
|
|
@@ -27,29 +27,28 @@ const loadThinkingModule = async () => {
|
|
|
27
27
|
return thinkingModulePromise;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
const normalizeThinkingLevel = (raw) => {
|
|
31
|
+
const key = String(raw || "").trim().toLowerCase();
|
|
32
|
+
if (!key) return null;
|
|
33
|
+
const collapsed = key.replace(/[\s_-]+/g, "");
|
|
34
|
+
if (collapsed === "adaptive" || collapsed === "auto") return "adaptive";
|
|
35
|
+
if (collapsed === "max") return "max";
|
|
36
|
+
if (collapsed === "ultra") return "ultra";
|
|
37
|
+
if (collapsed === "xhigh" || collapsed === "extrahigh") return "xhigh";
|
|
38
|
+
if (key === "off") return "off";
|
|
39
|
+
if (["on", "enable", "enabled"].includes(key)) return "low";
|
|
40
|
+
if (["min", "minimal"].includes(key)) return "minimal";
|
|
41
|
+
if (["low", "thinkhard", "think-hard", "think_hard"].includes(key)) {
|
|
42
|
+
return "low";
|
|
40
43
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
];
|
|
50
|
-
const match = candidates.find(isNormalizeThinkLevel);
|
|
51
|
-
if (!match) throw new Error("OpenClaw normalizeThinkLevel export not found");
|
|
52
|
-
return match;
|
|
44
|
+
if (["mid", "med", "medium", "thinkharder", "think-harder", "harder"].includes(key)) {
|
|
45
|
+
return "medium";
|
|
46
|
+
}
|
|
47
|
+
if (["high", "ultrathink", "thinkhardest", "highest"].includes(key)) {
|
|
48
|
+
return "high";
|
|
49
|
+
}
|
|
50
|
+
if (key === "think") return "minimal";
|
|
51
|
+
return null;
|
|
53
52
|
};
|
|
54
53
|
|
|
55
54
|
const splitModelKey = (modelKey = "") => {
|
|
@@ -80,13 +79,13 @@ const resolveThinkingApi = async () => {
|
|
|
80
79
|
return {
|
|
81
80
|
listThinkingLevelOptions: mod.listThinkingLevelOptions || mod.i,
|
|
82
81
|
resolveThinkingDefaultForModel: mod.resolveThinkingDefaultForModel || mod.s,
|
|
83
|
-
normalizeThinkLevel: resolveNormalizeThinkLevel(mod),
|
|
84
82
|
};
|
|
85
83
|
};
|
|
86
84
|
|
|
87
85
|
const resolveThinkingOptionsForModel = async ({
|
|
88
86
|
modelKey = "",
|
|
89
87
|
catalog = [],
|
|
88
|
+
agentRuntime = "",
|
|
90
89
|
} = {}) => {
|
|
91
90
|
const { provider, model } = splitModelKey(modelKey);
|
|
92
91
|
if (!provider || !model) {
|
|
@@ -96,12 +95,20 @@ const resolveThinkingOptionsForModel = async ({
|
|
|
96
95
|
};
|
|
97
96
|
}
|
|
98
97
|
const api = await resolveThinkingApi();
|
|
99
|
-
const
|
|
98
|
+
const normalizedAgentRuntime = String(agentRuntime || "").trim() || undefined;
|
|
99
|
+
const levels =
|
|
100
|
+
api.listThinkingLevelOptions(
|
|
101
|
+
provider,
|
|
102
|
+
model,
|
|
103
|
+
catalog,
|
|
104
|
+
normalizedAgentRuntime,
|
|
105
|
+
) || [];
|
|
100
106
|
const modelDefault =
|
|
101
107
|
api.resolveThinkingDefaultForModel({
|
|
102
108
|
provider,
|
|
103
109
|
model,
|
|
104
110
|
catalog,
|
|
111
|
+
agentRuntime: normalizedAgentRuntime,
|
|
105
112
|
}) || "off";
|
|
106
113
|
return {
|
|
107
114
|
levels: levels.map((entry) => ({
|
|
@@ -114,14 +121,13 @@ const resolveThinkingOptionsForModel = async ({
|
|
|
114
121
|
|
|
115
122
|
const normalizeThinkingDefaultValue = async (raw) => {
|
|
116
123
|
if (raw === null || raw === undefined || raw === "") return null;
|
|
117
|
-
|
|
118
|
-
const normalized = api.normalizeThinkLevel(String(raw || "").trim());
|
|
119
|
-
return normalized || null;
|
|
124
|
+
return normalizeThinkingLevel(raw);
|
|
120
125
|
};
|
|
121
126
|
|
|
122
127
|
module.exports = {
|
|
123
128
|
buildCatalogEntry,
|
|
124
129
|
loadThinkingModule,
|
|
130
|
+
normalizeThinkingLevel,
|
|
125
131
|
normalizeThinkingDefaultValue,
|
|
126
132
|
resolveThinkingOptionsForModel,
|
|
127
133
|
splitModelKey,
|
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
} = require("./constants");
|
|
11
11
|
const { normalizeOpenclawVersion } = require("./helpers");
|
|
12
12
|
const { parseJsonObjectFromNoisyOutput } = require("./utils/json");
|
|
13
|
+
const { assertSupportedNodeVersion } = require("../node-runtime");
|
|
13
14
|
|
|
14
15
|
const createOpenclawVersionService = ({
|
|
15
16
|
gatewayEnv,
|
|
@@ -124,6 +125,12 @@ const createOpenclawVersionService = ({
|
|
|
124
125
|
// Copying individual files (cp -af) avoids the rename syscall entirely.
|
|
125
126
|
const installLatestOpenclaw = () =>
|
|
126
127
|
new Promise((resolve, reject) => {
|
|
128
|
+
try {
|
|
129
|
+
assertSupportedNodeVersion();
|
|
130
|
+
} catch (error) {
|
|
131
|
+
reject(error);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
127
134
|
const installDir = findInstallDir();
|
|
128
135
|
const tmpDir = fs.mkdtempSync(
|
|
129
136
|
path.join(os.tmpdir(), "openclaw-update-"),
|