@chrysb/alphaclaw 0.9.28 → 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 +1027 -1020
- 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/onboarding/welcome-config.js +6 -2
- package/lib/public/js/components/onboarding/welcome-setup-step.js +58 -4
- package/lib/public/js/components/welcome/use-welcome.js +18 -8
- package/lib/public/js/lib/api.js +5 -0
- package/lib/public/js/lib/model-config.js +77 -8
- 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/index.js +11 -0
- package/lib/server/onboarding/openclaw.js +4 -0
- package/lib/server/onboarding/validation.js +17 -3
- 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/routes/onboarding.js +33 -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
|
}
|
|
@@ -53,12 +53,16 @@ const getAiGroupError = (vals, ctx = {}) => {
|
|
|
53
53
|
if (!hasValue(vals.MODEL_KEY) || !String(vals.MODEL_KEY).includes("/")) {
|
|
54
54
|
return "Choose a model to continue.";
|
|
55
55
|
}
|
|
56
|
-
if (
|
|
56
|
+
if (
|
|
57
|
+
ctx.selectedProvider === "openai-codex" &&
|
|
58
|
+
ctx.codexLoading &&
|
|
59
|
+
!ctx.hasAi
|
|
60
|
+
) {
|
|
57
61
|
return "Checking Codex OAuth status. Try Next again in a moment.";
|
|
58
62
|
}
|
|
59
63
|
if (!ctx.hasAi) {
|
|
60
64
|
return ctx.selectedProvider === "openai-codex"
|
|
61
|
-
? "Connect Codex OAuth to continue."
|
|
65
|
+
? "Connect Codex OAuth or enter an OpenAI API key to continue."
|
|
62
66
|
: "Add credentials for the selected model provider to continue.";
|
|
63
67
|
}
|
|
64
68
|
return "";
|
|
@@ -2,6 +2,7 @@ import { h } from "preact";
|
|
|
2
2
|
import { useEffect, useState } from "preact/hooks";
|
|
3
3
|
import htm from "htm";
|
|
4
4
|
import { LoadingSpinner } from "../loading-spinner.js";
|
|
5
|
+
import { fetchOnboardProgress } from "../../lib/api.js";
|
|
5
6
|
|
|
6
7
|
const html = htm.bind(h);
|
|
7
8
|
const kSetupTips = [
|
|
@@ -30,9 +31,24 @@ const kSetupTips = [
|
|
|
30
31
|
text: "Be incredibly careful installing skills from the internet - they may contain malicious code.",
|
|
31
32
|
},
|
|
32
33
|
];
|
|
34
|
+
const kDefaultProgress = {
|
|
35
|
+
stage: "creating_repo",
|
|
36
|
+
message: "Creating repo...",
|
|
37
|
+
};
|
|
38
|
+
const kProgressStepNumbers = {
|
|
39
|
+
creating_repo: 1,
|
|
40
|
+
running_openclaw_onboard: 2,
|
|
41
|
+
initial_git_push: 3,
|
|
42
|
+
starting_gateway: 4,
|
|
43
|
+
};
|
|
44
|
+
const kProgressPollIntervalMs = 500;
|
|
45
|
+
const kProgressDotsIntervalMs = 450;
|
|
46
|
+
const kProgressDotSlots = [1, 2, 3];
|
|
33
47
|
|
|
34
48
|
export const WelcomeSetupStep = ({ error, loading, onRetry, onBack }) => {
|
|
35
49
|
const [tipIndex, setTipIndex] = useState(0);
|
|
50
|
+
const [progress, setProgress] = useState(kDefaultProgress);
|
|
51
|
+
const [progressDotCount, setProgressDotCount] = useState(1);
|
|
36
52
|
|
|
37
53
|
useEffect(() => {
|
|
38
54
|
if (error || !loading) return;
|
|
@@ -42,6 +58,35 @@ export const WelcomeSetupStep = ({ error, loading, onRetry, onBack }) => {
|
|
|
42
58
|
return () => clearInterval(timer);
|
|
43
59
|
}, [error, loading]);
|
|
44
60
|
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (error || !loading) return;
|
|
63
|
+
let cancelled = false;
|
|
64
|
+
const refreshProgress = async () => {
|
|
65
|
+
try {
|
|
66
|
+
const progress = await fetchOnboardProgress();
|
|
67
|
+
if (!cancelled && progress?.message) {
|
|
68
|
+
setProgress(progress);
|
|
69
|
+
}
|
|
70
|
+
} catch {}
|
|
71
|
+
};
|
|
72
|
+
setProgress(kDefaultProgress);
|
|
73
|
+
refreshProgress();
|
|
74
|
+
const timer = setInterval(refreshProgress, kProgressPollIntervalMs);
|
|
75
|
+
return () => {
|
|
76
|
+
cancelled = true;
|
|
77
|
+
clearInterval(timer);
|
|
78
|
+
};
|
|
79
|
+
}, [error, loading]);
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (error || !loading) return;
|
|
83
|
+
setProgressDotCount(1);
|
|
84
|
+
const timer = setInterval(() => {
|
|
85
|
+
setProgressDotCount((count) => (count % 3) + 1);
|
|
86
|
+
}, kProgressDotsIntervalMs);
|
|
87
|
+
return () => clearInterval(timer);
|
|
88
|
+
}, [error, loading]);
|
|
89
|
+
|
|
45
90
|
if (error) {
|
|
46
91
|
return html`
|
|
47
92
|
<div class="py-4 flex flex-col items-center text-center gap-3">
|
|
@@ -77,6 +122,8 @@ export const WelcomeSetupStep = ({ error, loading, onRetry, onBack }) => {
|
|
|
77
122
|
}
|
|
78
123
|
|
|
79
124
|
const currentTip = kSetupTips[tipIndex];
|
|
125
|
+
const progressStep = kProgressStepNumbers[progress.stage] || 1;
|
|
126
|
+
const progressLabel = (progress.message || kDefaultProgress.message).replace(/\.{1,3}$/, "");
|
|
80
127
|
|
|
81
128
|
return html`
|
|
82
129
|
<div class="relative min-h-[320px] pt-4 pb-20 flex">
|
|
@@ -84,10 +131,17 @@ export const WelcomeSetupStep = ({ error, loading, onRetry, onBack }) => {
|
|
|
84
131
|
class="flex-1 flex flex-col items-center justify-center text-center gap-4"
|
|
85
132
|
>
|
|
86
133
|
<${LoadingSpinner} className="h-8 w-8 text-body" />
|
|
87
|
-
<h3 class="text-lg font-semibold text-body">
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
134
|
+
<h3 class="text-lg font-semibold text-body">Initializing AlphaClaw...</h3>
|
|
135
|
+
<p class="text-sm text-fg-muted">
|
|
136
|
+
${progressStep} / 4: ${progressLabel}<span class="inline-flex" aria-hidden="true"
|
|
137
|
+
>${kProgressDotSlots.map(
|
|
138
|
+
(slot) => html`<span style=${{ visibility: slot <= progressDotCount ? "visible" : "hidden" }}
|
|
139
|
+
>.</span
|
|
140
|
+
>`,
|
|
141
|
+
)}</span
|
|
142
|
+
>
|
|
143
|
+
</p>
|
|
144
|
+
<p class="text-xs text-fg-muted">This could take up to 30 seconds</p>
|
|
91
145
|
</div>
|
|
92
146
|
<div
|
|
93
147
|
class="absolute bottom-3 left-3 right-3 bg-field border border-border rounded-lg px-3 py-2 text-xs text-fg-muted"
|
|
@@ -9,11 +9,14 @@ import {
|
|
|
9
9
|
import { useCachedFetch } from "../../hooks/use-cached-fetch.js";
|
|
10
10
|
import { usePolling } from "../../hooks/usePolling.js";
|
|
11
11
|
import {
|
|
12
|
-
getModelProvider,
|
|
13
12
|
getAuthProviderFromModelProvider,
|
|
14
13
|
getFeaturedModels,
|
|
14
|
+
getOnboardingModelProvider,
|
|
15
|
+
getOnboardingModels,
|
|
16
|
+
isDeprecatedOnboardingModelKey,
|
|
15
17
|
getVisibleAiFieldKeys,
|
|
16
18
|
kProviderAuthFields,
|
|
19
|
+
withAlwaysAvailableModels,
|
|
17
20
|
} from "../../lib/model-config.js";
|
|
18
21
|
import {
|
|
19
22
|
getInitialOnboardingModelKey,
|
|
@@ -167,7 +170,9 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
167
170
|
};
|
|
168
171
|
|
|
169
172
|
const applyModelCatalog = useCallback((payload) => {
|
|
170
|
-
const list =
|
|
173
|
+
const list = getOnboardingModels(
|
|
174
|
+
withAlwaysAvailableModels(getModelCatalogModels(payload)),
|
|
175
|
+
);
|
|
171
176
|
if (!payload) return;
|
|
172
177
|
const isRefreshing = isModelCatalogRefreshing(payload);
|
|
173
178
|
const isFallbackRefresh =
|
|
@@ -181,11 +186,14 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
181
186
|
: null
|
|
182
187
|
: "No models found",
|
|
183
188
|
);
|
|
189
|
+
const currentModelIsDeprecated = isDeprecatedOnboardingModelKey(
|
|
190
|
+
vals.MODEL_KEY,
|
|
191
|
+
);
|
|
184
192
|
const defaultModelKey = getInitialOnboardingModelKey({
|
|
185
193
|
catalog: list,
|
|
186
|
-
currentModelKey: vals.MODEL_KEY,
|
|
194
|
+
currentModelKey: currentModelIsDeprecated ? "" : vals.MODEL_KEY,
|
|
187
195
|
});
|
|
188
|
-
if (!vals.MODEL_KEY && defaultModelKey) {
|
|
196
|
+
if ((!vals.MODEL_KEY || currentModelIsDeprecated) && defaultModelKey) {
|
|
189
197
|
setVals((prev) => ({ ...prev, MODEL_KEY: defaultModelKey }));
|
|
190
198
|
}
|
|
191
199
|
}, [setVals, vals.MODEL_KEY]);
|
|
@@ -212,16 +220,18 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
212
220
|
}, [modelsFetchState.error]);
|
|
213
221
|
|
|
214
222
|
const getValidationContext = (currentVals = {}) => {
|
|
215
|
-
const currentSelectedProvider =
|
|
216
|
-
|
|
217
|
-
|
|
223
|
+
const currentSelectedProvider = getOnboardingModelProvider({
|
|
224
|
+
modelKey: currentVals.MODEL_KEY,
|
|
225
|
+
models,
|
|
226
|
+
});
|
|
218
227
|
const currentSelectedAuthProvider =
|
|
219
228
|
getAuthProviderFromModelProvider(currentSelectedProvider);
|
|
220
229
|
const currentProviderAuthFields =
|
|
221
230
|
kProviderAuthFields[currentSelectedAuthProvider] || [];
|
|
222
231
|
const currentHasAi =
|
|
223
232
|
currentSelectedProvider === "openai-codex"
|
|
224
|
-
? !!codexStatus.connected
|
|
233
|
+
? !!codexStatus.connected ||
|
|
234
|
+
!!String(currentVals.OPENAI_API_KEY || "").trim()
|
|
225
235
|
: currentProviderAuthFields.some((field) =>
|
|
226
236
|
!!String(currentVals[field.key] || "").trim(),
|
|
227
237
|
);
|
package/lib/public/js/lib/api.js
CHANGED
|
@@ -818,6 +818,11 @@ export async function fetchOnboardStatus() {
|
|
|
818
818
|
return res.json();
|
|
819
819
|
}
|
|
820
820
|
|
|
821
|
+
export async function fetchOnboardProgress() {
|
|
822
|
+
const res = await authFetch("/api/onboard/progress");
|
|
823
|
+
return res.json();
|
|
824
|
+
}
|
|
825
|
+
|
|
821
826
|
export async function runOnboard(vars, modelKey, { importMode = false } = {}) {
|
|
822
827
|
const res = await authFetch("/api/onboard", {
|
|
823
828
|
method: "POST",
|
|
@@ -26,11 +26,8 @@ export const kFeaturedModelDefs = [
|
|
|
26
26
|
preferredKeys: ["anthropic/claude-sonnet-4-6"],
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
|
-
label: "
|
|
30
|
-
preferredKeys: [
|
|
31
|
-
"openai/gpt-5.3-codex",
|
|
32
|
-
"openai-codex/gpt-5.3-codex",
|
|
33
|
-
],
|
|
29
|
+
label: "GPT-5.6 Sol",
|
|
30
|
+
preferredKeys: ["openai/gpt-5.6-sol", "openai-codex/gpt-5.6-sol"],
|
|
34
31
|
},
|
|
35
32
|
{
|
|
36
33
|
label: "GPT-5.5",
|
|
@@ -42,7 +39,70 @@ export const kFeaturedModelDefs = [
|
|
|
42
39
|
},
|
|
43
40
|
];
|
|
44
41
|
|
|
42
|
+
const kDeprecatedOnboardingModelKeys = new Set([
|
|
43
|
+
"openai/gpt-5.3-codex",
|
|
44
|
+
"openai-codex/gpt-5.3-codex",
|
|
45
|
+
]);
|
|
46
|
+
const kCanonicalCodexOauthModelKeys = new Set([
|
|
47
|
+
"openai/gpt-5.4-mini",
|
|
48
|
+
"openai/gpt-5.5",
|
|
49
|
+
"openai/gpt-5.6-sol",
|
|
50
|
+
"openai/gpt-5.6-terra",
|
|
51
|
+
"openai/gpt-5.6-luna",
|
|
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
|
+
};
|
|
65
|
+
|
|
66
|
+
export const isDeprecatedOnboardingModelKey = (modelKey) =>
|
|
67
|
+
kDeprecatedOnboardingModelKeys.has(String(modelKey || "").trim());
|
|
68
|
+
|
|
69
|
+
export const getOnboardingModels = (models = []) =>
|
|
70
|
+
models.filter(
|
|
71
|
+
(model) => !isDeprecatedOnboardingModelKey(model?.key),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
export const getOnboardingModelProvider = ({ modelKey, models = [] } = {}) => {
|
|
75
|
+
const normalizedKey = String(modelKey || "").trim();
|
|
76
|
+
const model = models.find((candidate) => candidate?.key === normalizedKey);
|
|
77
|
+
if (
|
|
78
|
+
getModelProvider(normalizedKey) === "openai-codex" ||
|
|
79
|
+
kCanonicalCodexOauthModelKeys.has(normalizedKey) ||
|
|
80
|
+
model?.agentRuntime?.id === "codex"
|
|
81
|
+
) {
|
|
82
|
+
return "openai-codex";
|
|
83
|
+
}
|
|
84
|
+
return getModelProvider(normalizedKey);
|
|
85
|
+
};
|
|
86
|
+
|
|
45
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
|
+
},
|
|
46
106
|
{
|
|
47
107
|
key: "openai/gpt-5.4-mini",
|
|
48
108
|
provider: "openai",
|
|
@@ -59,9 +119,19 @@ export const kAlwaysAvailableModelDefs = [
|
|
|
59
119
|
|
|
60
120
|
export const withAlwaysAvailableModels = (models = []) => {
|
|
61
121
|
const merged = [...models];
|
|
62
|
-
const keys = new Set(merged.map((model) => model?.key));
|
|
63
122
|
for (const model of kAlwaysAvailableModelDefs) {
|
|
64
|
-
|
|
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
|
+
};
|
|
65
135
|
}
|
|
66
136
|
return merged;
|
|
67
137
|
};
|
|
@@ -345,7 +415,6 @@ export const kFeatureDefs = [
|
|
|
345
415
|
];
|
|
346
416
|
|
|
347
417
|
export const getVisibleAiFieldKeys = (provider) => {
|
|
348
|
-
if (provider === "openai-codex") return new Set();
|
|
349
418
|
const authProvider = getAuthProviderFromModelProvider(provider);
|
|
350
419
|
const fields = kProviderAuthFields[authProvider] || [];
|
|
351
420
|
return new Set(fields.map((field) => field.key));
|
|
@@ -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
|
|
|
@@ -369,7 +369,14 @@ const createOnboardingService = ({
|
|
|
369
369
|
vars,
|
|
370
370
|
modelKey,
|
|
371
371
|
importMode = false,
|
|
372
|
+
onProgress,
|
|
372
373
|
}) => {
|
|
374
|
+
const reportProgress = (stage) => {
|
|
375
|
+
if (typeof onProgress !== "function") return;
|
|
376
|
+
try {
|
|
377
|
+
onProgress(stage);
|
|
378
|
+
} catch {}
|
|
379
|
+
};
|
|
373
380
|
const validation = validateOnboardingInput({
|
|
374
381
|
vars,
|
|
375
382
|
modelKey,
|
|
@@ -425,6 +432,7 @@ const createOnboardingService = ({
|
|
|
425
432
|
syncApiKeyAuthProfilesFromEnvVars(authProfiles, varsToSave);
|
|
426
433
|
|
|
427
434
|
const [, repoName] = repoUrl.split("/");
|
|
435
|
+
reportProgress("creating_repo");
|
|
428
436
|
const repoCheck = await ensureGithubRepoAccessible({
|
|
429
437
|
repoUrl,
|
|
430
438
|
repoName,
|
|
@@ -481,6 +489,7 @@ const createOnboardingService = ({
|
|
|
481
489
|
);
|
|
482
490
|
}
|
|
483
491
|
|
|
492
|
+
reportProgress("running_openclaw_onboard");
|
|
484
493
|
if (!existingConfigPresent) {
|
|
485
494
|
const onboardArgs = buildOnboardArgs({
|
|
486
495
|
varMap,
|
|
@@ -551,6 +560,7 @@ const createOnboardingService = ({
|
|
|
551
560
|
|
|
552
561
|
ensureGatewayProxyConfig(getBaseUrl(req));
|
|
553
562
|
|
|
563
|
+
reportProgress("initial_git_push");
|
|
554
564
|
try {
|
|
555
565
|
const commitMsg = importMode
|
|
556
566
|
? "imported existing setup via AlphaClaw"
|
|
@@ -567,6 +577,7 @@ const createOnboardingService = ({
|
|
|
567
577
|
console.error("[onboard] Git push error:", e.message);
|
|
568
578
|
}
|
|
569
579
|
|
|
580
|
+
reportProgress("starting_gateway");
|
|
570
581
|
runOnboardedBootSequence();
|
|
571
582
|
return { status: 200, body: { ok: true } };
|
|
572
583
|
};
|
|
@@ -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
|
|