@chrysb/alphaclaw 0.9.0-beta.7 → 0.9.1-beta.0
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/bin/alphaclaw.js +26 -25
- package/lib/cli/git-runtime.js +97 -0
- package/lib/public/css/chat.css +0 -12
- package/lib/public/css/explorer.css +48 -0
- package/lib/public/css/shell.css +149 -0
- package/lib/public/css/tailwind.generated.css +1 -1
- package/lib/public/css/theme.css +265 -0
- package/lib/public/dist/app.bundle.js +2770 -2762
- package/lib/public/js/app.js +26 -14
- package/lib/public/js/components/agents-tab/create-channel-modal.js +259 -59
- package/lib/public/js/components/gateway.js +0 -286
- package/lib/public/js/components/general/index.js +0 -7
- package/lib/public/js/components/icons.js +26 -25
- package/lib/public/js/components/modal-shell.js +1 -1
- package/lib/public/js/components/models-tab/provider-auth-card.js +60 -49
- package/lib/public/js/components/models-tab/use-models.js +74 -9
- package/lib/public/js/components/models.js +52 -37
- package/lib/public/js/components/onboarding/use-welcome-codex.js +34 -24
- package/lib/public/js/components/onboarding/welcome-config.js +76 -10
- package/lib/public/js/components/onboarding/welcome-form-step.js +2 -7
- package/lib/public/js/components/onboarding/welcome-header.js +12 -14
- package/lib/public/js/components/onboarding/welcome-setup-step.js +3 -3
- package/lib/public/js/components/providers.js +53 -42
- package/lib/public/js/components/routes/chat-route.js +2 -9
- package/lib/public/js/components/routes/general-route.js +0 -6
- package/lib/public/js/components/routes/index.js +0 -1
- package/lib/public/js/components/routes/watchdog-route.js +0 -6
- package/lib/public/js/components/sidebar.js +21 -7
- package/lib/public/js/components/theme-toggle.js +113 -0
- package/lib/public/js/components/update-modal.js +174 -51
- package/lib/public/js/components/watchdog-tab/index.js +0 -6
- package/lib/public/js/components/welcome/index.js +0 -2
- package/lib/public/js/components/welcome/use-welcome.js +107 -36
- package/lib/public/js/hooks/use-app-shell-controller.js +16 -33
- package/lib/public/js/lib/api.js +0 -28
- package/lib/public/js/lib/app-navigation.js +0 -2
- package/lib/public/js/lib/channel-provider-availability.js +1 -2
- package/lib/public/js/lib/codex-oauth-window.js +22 -0
- package/lib/public/js/lib/model-catalog.js +31 -0
- package/lib/public/js/lib/storage-keys.js +1 -1
- package/lib/public/login.html +8 -4
- package/lib/public/setup.html +9 -0
- package/lib/scripts/git +110 -16
- package/lib/server/agents/channels.js +1 -4
- package/lib/server/alphaclaw-version.js +590 -132
- package/lib/server/constants.js +5 -0
- package/lib/server/db/webhooks/index.js +48 -8
- package/lib/server/exec-defaults-config.js +163 -0
- package/lib/server/gateway.js +1 -0
- package/lib/server/init/register-server-routes.js +0 -8
- package/lib/server/init/server-lifecycle.js +2 -0
- package/lib/server/model-catalog-cache.js +251 -0
- package/lib/server/onboarding/github.js +83 -2
- package/lib/server/onboarding/index.js +7 -0
- package/lib/server/routes/models.js +14 -23
- package/lib/server/routes/nodes.js +9 -23
- package/lib/server/routes/system.js +3 -16
- package/lib/server/routes/webhooks.js +12 -1
- package/lib/server/startup.js +8 -0
- package/lib/server/watchdog-notify.js +172 -55
- package/lib/server.js +17 -2
- package/lib/setup/core-prompts/AGENTS.md +12 -0
- package/lib/setup/core-prompts/TOOLS.md +12 -0
- package/package.json +2 -2
- package/patches/openclaw+2026.4.9.patch +13 -0
- package/lib/public/js/components/mcp-tab/index.js +0 -237
- package/lib/public/js/components/routes/mcp-route.js +0 -7
- package/lib/server/mcp-bridge.js +0 -158
- package/lib/server/routes/mcp.js +0 -292
- package/patches/openclaw+2026.3.28.patch +0 -13
|
@@ -34,20 +34,62 @@ const getReleaseUrl = (tag) =>
|
|
|
34
34
|
? `https://github.com/chrysb/alphaclaw/releases/tag/${encodeURIComponent(tag)}`
|
|
35
35
|
: "https://github.com/chrysb/alphaclaw/releases";
|
|
36
36
|
|
|
37
|
+
const VersionSummaryRow = ({
|
|
38
|
+
label = "",
|
|
39
|
+
currentVersion = "",
|
|
40
|
+
latestVersion = "",
|
|
41
|
+
}) => {
|
|
42
|
+
const currentLabel = String(currentVersion || "").trim() || "Unknown";
|
|
43
|
+
const latestLabel = String(latestVersion || "").trim() || "Unknown";
|
|
44
|
+
const changed = currentLabel !== latestLabel;
|
|
45
|
+
return html`
|
|
46
|
+
<div class="ac-surface-inset border border-border rounded-lg px-3 py-2">
|
|
47
|
+
<p class="text-[11px] uppercase tracking-[0.18em] text-fg-muted">${label}</p>
|
|
48
|
+
<p class="mt-1 text-sm text-body">
|
|
49
|
+
${changed
|
|
50
|
+
? html`
|
|
51
|
+
<span>${currentLabel}</span>
|
|
52
|
+
<span class="mx-2 text-fg-muted">→</span>
|
|
53
|
+
<span class="font-semibold">${latestLabel}</span>
|
|
54
|
+
`
|
|
55
|
+
: html`<span>${currentLabel}</span>`}
|
|
56
|
+
</p>
|
|
57
|
+
</div>
|
|
58
|
+
`;
|
|
59
|
+
};
|
|
60
|
+
|
|
37
61
|
export const UpdateModal = ({
|
|
38
62
|
visible = false,
|
|
39
63
|
onClose = () => {},
|
|
64
|
+
currentVersion = "",
|
|
65
|
+
currentOpenclawVersion = "",
|
|
40
66
|
version = "",
|
|
67
|
+
latestOpenclawVersion = "",
|
|
68
|
+
updateStrategy = null,
|
|
41
69
|
onUpdate = () => {},
|
|
42
70
|
updating = false,
|
|
43
71
|
}) => {
|
|
44
72
|
const requestedTag = useMemo(() => getReleaseTagFromVersion(version), [version]);
|
|
73
|
+
const shouldLoadReleaseNotes =
|
|
74
|
+
visible &&
|
|
75
|
+
String(version || "").trim() &&
|
|
76
|
+
String(currentVersion || "").trim() &&
|
|
77
|
+
String(version || "").trim() !== String(currentVersion || "").trim();
|
|
78
|
+
const canApplyUpdate =
|
|
79
|
+
updateStrategy?.action === "self-update" ||
|
|
80
|
+
updateStrategy?.action === "managed-update";
|
|
45
81
|
const [loadingNotes, setLoadingNotes] = useState(false);
|
|
46
82
|
const [notesError, setNotesError] = useState("");
|
|
47
83
|
const [notesData, setNotesData] = useState(null);
|
|
48
84
|
|
|
49
85
|
useEffect(() => {
|
|
50
86
|
if (!visible) return;
|
|
87
|
+
if (!shouldLoadReleaseNotes) {
|
|
88
|
+
setLoadingNotes(false);
|
|
89
|
+
setNotesError("");
|
|
90
|
+
setNotesData(null);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
51
93
|
let isActive = true;
|
|
52
94
|
const loadNotes = async () => {
|
|
53
95
|
setLoadingNotes(true);
|
|
@@ -74,12 +116,11 @@ export const UpdateModal = ({
|
|
|
74
116
|
return () => {
|
|
75
117
|
isActive = false;
|
|
76
118
|
};
|
|
77
|
-
}, [visible, requestedTag]);
|
|
119
|
+
}, [visible, requestedTag, shouldLoadReleaseNotes]);
|
|
78
120
|
|
|
79
121
|
const effectiveTag = String(notesData?.tag || requestedTag || "").trim();
|
|
80
122
|
const effectiveReleaseUrl =
|
|
81
123
|
String(notesData?.htmlUrl || "").trim() || getReleaseUrl(effectiveTag);
|
|
82
|
-
const updateLabel = effectiveTag ? `Update to ${effectiveTag}` : "Update now";
|
|
83
124
|
const publishedAtLabel = formatPublishedAt(notesData?.publishedAt);
|
|
84
125
|
const releaseBody = String(notesData?.body || "").trim();
|
|
85
126
|
const releasePreviewHtml = useMemo(
|
|
@@ -90,6 +131,33 @@ export const UpdateModal = ({
|
|
|
90
131
|
}),
|
|
91
132
|
[releaseBody],
|
|
92
133
|
);
|
|
134
|
+
const strategyLabel = String(updateStrategy?.label || "").trim();
|
|
135
|
+
const strategyDescription = String(updateStrategy?.description || "").trim();
|
|
136
|
+
const strategySteps = Array.isArray(updateStrategy?.steps)
|
|
137
|
+
? updateStrategy.steps
|
|
138
|
+
: [];
|
|
139
|
+
const templateRepoUrl = String(updateStrategy?.templateRepoUrl || "").trim();
|
|
140
|
+
const showStrategyDetails =
|
|
141
|
+
updateStrategy?.provider === "apex" &&
|
|
142
|
+
updateStrategy?.action === "managed-update"
|
|
143
|
+
? false
|
|
144
|
+
: Boolean(strategyDescription || strategySteps.length > 0 || templateRepoUrl);
|
|
145
|
+
const primaryActionUrl = String(updateStrategy?.primaryActionUrl || "").trim();
|
|
146
|
+
const primaryLabel = canApplyUpdate
|
|
147
|
+
? String(updateStrategy?.primaryActionLabel || "").trim() || "Update now"
|
|
148
|
+
: "Done";
|
|
149
|
+
const handlePrimaryAction = () => {
|
|
150
|
+
if (canApplyUpdate) {
|
|
151
|
+
onUpdate();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (primaryActionUrl) {
|
|
155
|
+
try {
|
|
156
|
+
window.open(primaryActionUrl, "_blank", "noopener,noreferrer");
|
|
157
|
+
} catch {}
|
|
158
|
+
}
|
|
159
|
+
onClose();
|
|
160
|
+
};
|
|
93
161
|
|
|
94
162
|
return html`
|
|
95
163
|
<${ModalShell}
|
|
@@ -106,52 +174,107 @@ export const UpdateModal = ({
|
|
|
106
174
|
<${CloseIcon} className="w-3.5 h-3.5 text-body" />
|
|
107
175
|
</button>
|
|
108
176
|
<div class="space-y-1 pr-10">
|
|
109
|
-
<h3 class="text-sm font-semibold">
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
177
|
+
<h3 class="text-sm font-semibold">Update available</h3>
|
|
178
|
+
<p class="text-xs text-fg-muted">
|
|
179
|
+
${strategyLabel
|
|
180
|
+
? `Detected deployment target: ${strategyLabel}`
|
|
181
|
+
: "Review the latest bundled versions before updating."}
|
|
182
|
+
</p>
|
|
113
183
|
</div>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
184
|
+
|
|
185
|
+
<div class="grid gap-2 sm:grid-cols-2">
|
|
186
|
+
<${VersionSummaryRow}
|
|
187
|
+
label="AlphaClaw"
|
|
188
|
+
currentVersion=${currentVersion}
|
|
189
|
+
latestVersion=${version}
|
|
190
|
+
/>
|
|
191
|
+
<${VersionSummaryRow}
|
|
192
|
+
label="OpenClaw"
|
|
193
|
+
currentVersion=${currentOpenclawVersion}
|
|
194
|
+
latestVersion=${latestOpenclawVersion || currentOpenclawVersion}
|
|
195
|
+
/>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
${shouldLoadReleaseNotes
|
|
199
|
+
? html`
|
|
200
|
+
${publishedAtLabel
|
|
201
|
+
? html`<p class="text-xs text-fg-muted">Published ${publishedAtLabel}</p>`
|
|
202
|
+
: null}
|
|
203
|
+
<div class="ac-surface-inset border border-border rounded-lg p-2 overflow-auto min-h-[220px] max-h-[52vh]">
|
|
204
|
+
${loadingNotes
|
|
205
|
+
? html`
|
|
206
|
+
<div class="min-h-[200px] flex items-center justify-center text-fg-muted">
|
|
207
|
+
<span class="inline-flex items-center gap-2 text-sm">
|
|
208
|
+
<${LoadingSpinner} className="h-4 w-4" />
|
|
209
|
+
Loading release notes...
|
|
210
|
+
</span>
|
|
211
|
+
</div>
|
|
212
|
+
`
|
|
213
|
+
: notesError
|
|
214
|
+
? html`
|
|
215
|
+
<div class="space-y-2">
|
|
216
|
+
<p class="text-sm text-status-error">${notesError}</p>
|
|
217
|
+
<a
|
|
218
|
+
class="ac-tip-link text-xs"
|
|
219
|
+
href=${effectiveReleaseUrl}
|
|
220
|
+
target="_blank"
|
|
221
|
+
rel="noreferrer"
|
|
222
|
+
>View release on GitHub</a
|
|
223
|
+
>
|
|
224
|
+
</div>
|
|
225
|
+
`
|
|
226
|
+
: releaseBody
|
|
227
|
+
? html`<div
|
|
228
|
+
class="file-viewer-preview release-notes-preview"
|
|
229
|
+
dangerouslySetInnerHTML=${{ __html: releasePreviewHtml }}
|
|
230
|
+
></div>`
|
|
231
|
+
: html`
|
|
232
|
+
<div class="space-y-2">
|
|
233
|
+
<p class="text-sm text-body">
|
|
234
|
+
No release notes were published for this tag.
|
|
235
|
+
</p>
|
|
236
|
+
<a
|
|
237
|
+
class="ac-tip-link text-xs"
|
|
238
|
+
href=${effectiveReleaseUrl}
|
|
239
|
+
target="_blank"
|
|
240
|
+
rel="noreferrer"
|
|
241
|
+
>Open release on GitHub</a
|
|
242
|
+
>
|
|
243
|
+
</div>
|
|
244
|
+
`}
|
|
245
|
+
</div>
|
|
246
|
+
`
|
|
247
|
+
: null}
|
|
248
|
+
|
|
249
|
+
${showStrategyDetails &&
|
|
250
|
+
html`
|
|
251
|
+
<div class="ac-surface-inset border border-border rounded-lg p-3 space-y-2">
|
|
252
|
+
${strategyDescription
|
|
253
|
+
? html`<p class="text-sm text-body">${strategyDescription}</p>`
|
|
254
|
+
: null}
|
|
255
|
+
${strategySteps.length > 0
|
|
125
256
|
? html`
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
target="_blank"
|
|
132
|
-
rel="noreferrer"
|
|
133
|
-
>View release on GitHub</a
|
|
134
|
-
>
|
|
135
|
-
</div>
|
|
257
|
+
<ol class="space-y-2 text-sm text-body list-decimal list-outside ml-6 pl-0">
|
|
258
|
+
${strategySteps.map(
|
|
259
|
+
(step) => html`<li key=${step}>${step}</li>`,
|
|
260
|
+
)}
|
|
261
|
+
</ol>
|
|
136
262
|
`
|
|
137
|
-
:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
</div>
|
|
153
|
-
`}
|
|
154
|
-
</div>
|
|
263
|
+
: null}
|
|
264
|
+
${templateRepoUrl
|
|
265
|
+
? html`
|
|
266
|
+
<a
|
|
267
|
+
class="ac-tip-link text-xs block mt-3"
|
|
268
|
+
href=${templateRepoUrl}
|
|
269
|
+
target="_blank"
|
|
270
|
+
rel="noreferrer"
|
|
271
|
+
>View deployment template</a
|
|
272
|
+
>
|
|
273
|
+
`
|
|
274
|
+
: null}
|
|
275
|
+
</div>
|
|
276
|
+
`}
|
|
277
|
+
|
|
155
278
|
<div class="flex items-center justify-end gap-2 pt-1">
|
|
156
279
|
<${ActionButton}
|
|
157
280
|
onClick=${onClose}
|
|
@@ -160,12 +283,12 @@ export const UpdateModal = ({
|
|
|
160
283
|
disabled=${updating}
|
|
161
284
|
/>
|
|
162
285
|
<${ActionButton}
|
|
163
|
-
onClick=${
|
|
164
|
-
tone
|
|
165
|
-
idleLabel=${
|
|
166
|
-
loadingLabel
|
|
167
|
-
loading=${updating}
|
|
168
|
-
disabled=${loadingNotes}
|
|
286
|
+
onClick=${handlePrimaryAction}
|
|
287
|
+
tone=${canApplyUpdate ? "warning" : "neutral"}
|
|
288
|
+
idleLabel=${primaryLabel}
|
|
289
|
+
loadingLabel=${canApplyUpdate ? "Updating..." : primaryLabel}
|
|
290
|
+
loading=${canApplyUpdate && updating}
|
|
291
|
+
disabled=${canApplyUpdate ? loadingNotes : false}
|
|
169
292
|
/>
|
|
170
293
|
</div>
|
|
171
294
|
</${ModalShell}>
|
|
@@ -17,9 +17,6 @@ export const WatchdogTab = ({
|
|
|
17
17
|
restartingGateway = false,
|
|
18
18
|
onRestartGateway,
|
|
19
19
|
restartSignal = 0,
|
|
20
|
-
openclawUpdateInProgress = false,
|
|
21
|
-
onOpenclawVersionActionComplete = () => {},
|
|
22
|
-
onOpenclawUpdate,
|
|
23
20
|
}) => {
|
|
24
21
|
const state = useWatchdogTab({
|
|
25
22
|
watchdogStatus,
|
|
@@ -37,9 +34,6 @@ export const WatchdogTab = ({
|
|
|
37
34
|
watchdogStatus=${state.currentWatchdogStatus}
|
|
38
35
|
onRepair=${state.onRepair}
|
|
39
36
|
repairing=${state.isRepairInProgress}
|
|
40
|
-
openclawUpdateInProgress=${openclawUpdateInProgress}
|
|
41
|
-
onOpenclawVersionActionComplete=${onOpenclawVersionActionComplete}
|
|
42
|
-
onOpenclawUpdate=${onOpenclawUpdate}
|
|
43
37
|
/>
|
|
44
38
|
|
|
45
39
|
<${WatchdogResourcesCard}
|
|
@@ -102,12 +102,10 @@ export const Welcome = ({ onComplete, acVersion }) => {
|
|
|
102
102
|
error=${state.formError}
|
|
103
103
|
step=${state.step}
|
|
104
104
|
totalGroups=${kWelcomeGroups.length}
|
|
105
|
-
currentGroupValid=${state.currentGroupValid}
|
|
106
105
|
goBack=${actions.goBack}
|
|
107
106
|
goNext=${actions.goNext}
|
|
108
107
|
loading=${state.loading}
|
|
109
108
|
githubStepLoading=${state.githubStepLoading}
|
|
110
|
-
allValid=${state.allValid}
|
|
111
109
|
handleSubmit=${actions.handleSubmit}
|
|
112
110
|
/>
|
|
113
111
|
`}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
applyImport,
|
|
7
7
|
fetchModels,
|
|
8
8
|
} from "../../lib/api.js";
|
|
9
|
+
import { useCachedFetch } from "../../hooks/use-cached-fetch.js";
|
|
9
10
|
import {
|
|
10
11
|
getModelProvider,
|
|
11
12
|
getAuthProviderFromModelProvider,
|
|
@@ -13,8 +14,16 @@ import {
|
|
|
13
14
|
getVisibleAiFieldKeys,
|
|
14
15
|
kProviderAuthFields,
|
|
15
16
|
} from "../../lib/model-config.js";
|
|
17
|
+
import {
|
|
18
|
+
getInitialOnboardingModelKey,
|
|
19
|
+
getModelCatalogModels,
|
|
20
|
+
kModelCatalogCacheKey,
|
|
21
|
+
preloadModelCatalog,
|
|
22
|
+
} from "../../lib/model-catalog.js";
|
|
16
23
|
import {
|
|
17
24
|
kWelcomeGroups,
|
|
25
|
+
getWelcomeGroupError,
|
|
26
|
+
findFirstInvalidWelcomeGroup,
|
|
18
27
|
isValidGithubRepoInput,
|
|
19
28
|
kGithubFlowFresh,
|
|
20
29
|
kGithubFlowImport,
|
|
@@ -76,11 +85,18 @@ const normalizePlaceholderReview = (review) => {
|
|
|
76
85
|
export const useWelcome = ({ onComplete }) => {
|
|
77
86
|
const kSetupStepIndex = kWelcomeGroups.length;
|
|
78
87
|
const kPairingStepIndex = kSetupStepIndex + 1;
|
|
79
|
-
const {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
88
|
+
const {
|
|
89
|
+
vals,
|
|
90
|
+
setVals,
|
|
91
|
+
setValue: setStoredValue,
|
|
92
|
+
step,
|
|
93
|
+
setStep,
|
|
94
|
+
setupError,
|
|
95
|
+
setSetupError,
|
|
96
|
+
} = useWelcomeStorage({
|
|
97
|
+
kSetupStepIndex,
|
|
98
|
+
kPairingStepIndex,
|
|
99
|
+
});
|
|
84
100
|
const [models, setModels] = useState([]);
|
|
85
101
|
const [modelsLoading, setModelsLoading] = useState(true);
|
|
86
102
|
const [modelsError, setModelsError] = useState(null);
|
|
@@ -110,6 +126,19 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
110
126
|
const [importScanResult, setImportScanResult] = useState(null);
|
|
111
127
|
const [importScanning, setImportScanning] = useState(false);
|
|
112
128
|
const [importError, setImportError] = useState(null);
|
|
129
|
+
const modelsFetchState = useCachedFetch(kModelCatalogCacheKey, fetchModels, {
|
|
130
|
+
maxAgeMs: 30000,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
// Warm the real catalog immediately so the AI step usually opens ready.
|
|
135
|
+
preloadModelCatalog().catch(() => {});
|
|
136
|
+
}, []);
|
|
137
|
+
|
|
138
|
+
const setValue = (key, value) => {
|
|
139
|
+
if (formError) setFormError(null);
|
|
140
|
+
setStoredValue(key, value);
|
|
141
|
+
};
|
|
113
142
|
|
|
114
143
|
const setImportStep = (nextStep) => {
|
|
115
144
|
setImportStepState(nextStep);
|
|
@@ -129,21 +158,54 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
129
158
|
};
|
|
130
159
|
|
|
131
160
|
useEffect(() => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
161
|
+
const list = getModelCatalogModels(modelsFetchState.data);
|
|
162
|
+
if (!modelsFetchState.data) return;
|
|
163
|
+
setModels(list);
|
|
164
|
+
setModelsError(list.length > 0 ? null : "No models found");
|
|
165
|
+
const defaultModelKey = getInitialOnboardingModelKey({
|
|
166
|
+
catalog: list,
|
|
167
|
+
currentModelKey: vals.MODEL_KEY,
|
|
168
|
+
});
|
|
169
|
+
if (!vals.MODEL_KEY && defaultModelKey) {
|
|
170
|
+
setVals((prev) => ({ ...prev, MODEL_KEY: defaultModelKey }));
|
|
171
|
+
}
|
|
172
|
+
}, [modelsFetchState.data, setVals, vals.MODEL_KEY]);
|
|
173
|
+
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
const hasModels = getModelCatalogModels(modelsFetchState.data).length > 0;
|
|
176
|
+
setModelsLoading(modelsFetchState.loading && !hasModels);
|
|
177
|
+
}, [modelsFetchState.data, modelsFetchState.loading]);
|
|
178
|
+
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
if (!modelsFetchState.error) return;
|
|
181
|
+
setModelsError("Failed to load models");
|
|
182
|
+
setModelsLoading(false);
|
|
183
|
+
}, [modelsFetchState.error]);
|
|
184
|
+
|
|
185
|
+
const getValidationContext = (currentVals = {}) => {
|
|
186
|
+
const currentSelectedProvider = getModelProvider(
|
|
187
|
+
String(currentVals.MODEL_KEY || "").trim(),
|
|
188
|
+
);
|
|
189
|
+
const currentSelectedAuthProvider =
|
|
190
|
+
getAuthProviderFromModelProvider(currentSelectedProvider);
|
|
191
|
+
const currentProviderAuthFields =
|
|
192
|
+
kProviderAuthFields[currentSelectedAuthProvider] || [];
|
|
193
|
+
const currentHasAi =
|
|
194
|
+
currentSelectedProvider === "openai-codex"
|
|
195
|
+
? !!codexStatus.connected
|
|
196
|
+
: currentProviderAuthFields.some((field) =>
|
|
197
|
+
!!String(currentVals[field.key] || "").trim(),
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
hasAi: currentHasAi,
|
|
202
|
+
selectedProvider: currentSelectedProvider,
|
|
203
|
+
codexLoading,
|
|
204
|
+
};
|
|
205
|
+
};
|
|
145
206
|
|
|
146
|
-
const
|
|
207
|
+
const validationContext = getValidationContext(vals);
|
|
208
|
+
const { selectedProvider, hasAi } = validationContext;
|
|
147
209
|
const placeholderReview = normalizePlaceholderReview(
|
|
148
210
|
vals[kImportPlaceholderReviewKey],
|
|
149
211
|
);
|
|
@@ -164,23 +226,10 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
164
226
|
const canToggleFullCatalog =
|
|
165
227
|
featuredModels.length > 0 && models.length > featuredModels.length;
|
|
166
228
|
const visibleAiFieldKeys = getVisibleAiFieldKeys(selectedProvider);
|
|
167
|
-
const selectedAuthProvider = getAuthProviderFromModelProvider(selectedProvider);
|
|
168
|
-
const selectedProviderAuthFields = kProviderAuthFields[selectedAuthProvider] || [];
|
|
169
|
-
const hasAi =
|
|
170
|
-
selectedProvider === "openai-codex"
|
|
171
|
-
? !!codexStatus.connected
|
|
172
|
-
: selectedProviderAuthFields.some(
|
|
173
|
-
(field) => !!String(vals[field.key] || "").trim(),
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
const allValid = kWelcomeGroups.every((group) => group.validate(vals, { hasAi }));
|
|
177
229
|
const isPreStep = step === -1;
|
|
178
230
|
const isSetupStep = step === kSetupStepIndex;
|
|
179
231
|
const isPairingStep = step === kPairingStepIndex;
|
|
180
232
|
const activeGroup = step >= 0 && step < kSetupStepIndex ? kWelcomeGroups[step] : null;
|
|
181
|
-
const currentGroupValid = activeGroup
|
|
182
|
-
? activeGroup.validate(vals, { hasAi })
|
|
183
|
-
: false;
|
|
184
233
|
const selectedPairingChannel = String(
|
|
185
234
|
vals[kPairingChannelKey] || getPreferredPairingChannel(vals),
|
|
186
235
|
);
|
|
@@ -202,7 +251,21 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
202
251
|
const handleSubmit = async () => {
|
|
203
252
|
const { normalizedVals, didChange } = normalizeOnboardingVals(vals);
|
|
204
253
|
if (didChange) setVals(normalizedVals);
|
|
205
|
-
|
|
254
|
+
const submitValidationContext = getValidationContext(normalizedVals);
|
|
255
|
+
const invalidGroup = findFirstInvalidWelcomeGroup(
|
|
256
|
+
normalizedVals,
|
|
257
|
+
submitValidationContext,
|
|
258
|
+
);
|
|
259
|
+
if (invalidGroup) {
|
|
260
|
+
setFormError(
|
|
261
|
+
getWelcomeGroupError(
|
|
262
|
+
invalidGroup.id,
|
|
263
|
+
normalizedVals,
|
|
264
|
+
submitValidationContext,
|
|
265
|
+
),
|
|
266
|
+
);
|
|
267
|
+
setSetupError(null);
|
|
268
|
+
setStep(kWelcomeGroups.findIndex((group) => group.id === invalidGroup.id));
|
|
206
269
|
return;
|
|
207
270
|
}
|
|
208
271
|
if (loading) return;
|
|
@@ -309,7 +372,17 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
309
372
|
const goNext = async () => {
|
|
310
373
|
const { normalizedVals, didChange } = normalizeOnboardingVals(vals);
|
|
311
374
|
if (didChange) setVals(normalizedVals);
|
|
312
|
-
if (!activeGroup
|
|
375
|
+
if (!activeGroup) return;
|
|
376
|
+
const stepValidationContext = getValidationContext(normalizedVals);
|
|
377
|
+
const stepValidationError = getWelcomeGroupError(
|
|
378
|
+
activeGroup.id,
|
|
379
|
+
normalizedVals,
|
|
380
|
+
stepValidationContext,
|
|
381
|
+
);
|
|
382
|
+
if (stepValidationError) {
|
|
383
|
+
setFormError(stepValidationError);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
313
386
|
setFormError(null);
|
|
314
387
|
if (activeGroup.id === "github") {
|
|
315
388
|
const githubFlow = normalizedVals._GITHUB_FLOW || kGithubFlowFresh;
|
|
@@ -545,12 +618,10 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
545
618
|
canToggleFullCatalog,
|
|
546
619
|
visibleAiFieldKeys,
|
|
547
620
|
hasAi,
|
|
548
|
-
allValid,
|
|
549
621
|
isPreStep,
|
|
550
622
|
isSetupStep,
|
|
551
623
|
isPairingStep,
|
|
552
624
|
activeGroup,
|
|
553
|
-
currentGroupValid,
|
|
554
625
|
selectedPairingChannel,
|
|
555
626
|
placeholderReview,
|
|
556
627
|
isImportStep,
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
restartGateway,
|
|
11
11
|
fetchWatchdogStatus,
|
|
12
12
|
fetchDoctorStatus,
|
|
13
|
-
updateOpenclaw,
|
|
14
13
|
subscribeStatusEvents,
|
|
15
14
|
} from "../lib/api.js";
|
|
16
15
|
import { shouldRequireRestartForBrowsePath } from "../lib/browse-restart-policy.js";
|
|
@@ -22,8 +21,11 @@ export const useAppShellController = ({ location = "" } = {}) => {
|
|
|
22
21
|
const [onboarded, setOnboarded] = useState(null);
|
|
23
22
|
const [authEnabled, setAuthEnabled] = useState(false);
|
|
24
23
|
const [acVersion, setAcVersion] = useState(null);
|
|
24
|
+
const [acCurrentOpenclawVersion, setAcCurrentOpenclawVersion] = useState(null);
|
|
25
25
|
const [acLatest, setAcLatest] = useState(null);
|
|
26
|
+
const [acLatestOpenclawVersion, setAcLatestOpenclawVersion] = useState(null);
|
|
26
27
|
const [acHasUpdate, setAcHasUpdate] = useState(false);
|
|
28
|
+
const [acUpdateStrategy, setAcUpdateStrategy] = useState(null);
|
|
27
29
|
const [acUpdating, setAcUpdating] = useState(false);
|
|
28
30
|
const [restartRequired, setRestartRequired] = useState(false);
|
|
29
31
|
const [browseRestartRequired, setBrowseRestartRequired] = useState(false);
|
|
@@ -31,7 +33,6 @@ export const useAppShellController = ({ location = "" } = {}) => {
|
|
|
31
33
|
const [gatewayRestartSignal, setGatewayRestartSignal] = useState(0);
|
|
32
34
|
const [statusPollCadenceMs, setStatusPollCadenceMs] = useState(15000);
|
|
33
35
|
const [statusPollingGraceElapsed, setStatusPollingGraceElapsed] = useState(false);
|
|
34
|
-
const [openclawUpdateInProgress, setOpenclawUpdateInProgress] = useState(false);
|
|
35
36
|
const [statusStreamConnected, setStatusStreamConnected] = useState(false);
|
|
36
37
|
const [statusStreamStatus, setStatusStreamStatus] = useState(null);
|
|
37
38
|
const [statusStreamWatchdog, setStatusStreamWatchdog] = useState(null);
|
|
@@ -138,8 +139,11 @@ export const useAppShellController = ({ location = "" } = {}) => {
|
|
|
138
139
|
const data = await fetchAlphaclawVersion(refresh);
|
|
139
140
|
if (!active) return;
|
|
140
141
|
setAcVersion(data.currentVersion || null);
|
|
142
|
+
setAcCurrentOpenclawVersion(data.currentOpenclawVersion || null);
|
|
141
143
|
setAcLatest(data.latestVersion || null);
|
|
144
|
+
setAcLatestOpenclawVersion(data.latestOpenclawVersion || null);
|
|
142
145
|
setAcHasUpdate(!!data.hasUpdate);
|
|
146
|
+
setAcUpdateStrategy(data.updateStrategy || null);
|
|
143
147
|
} catch {}
|
|
144
148
|
};
|
|
145
149
|
check(true);
|
|
@@ -236,40 +240,19 @@ export const useAppShellController = ({ location = "" } = {}) => {
|
|
|
236
240
|
}
|
|
237
241
|
}, [refreshRestartStatus, refreshSharedStatuses, restartingGateway]);
|
|
238
242
|
|
|
239
|
-
const handleOpenclawUpdate = useCallback(async () => {
|
|
240
|
-
if (openclawUpdateInProgress) {
|
|
241
|
-
return { ok: false, error: "OpenClaw update already in progress" };
|
|
242
|
-
}
|
|
243
|
-
setOpenclawUpdateInProgress(true);
|
|
244
|
-
try {
|
|
245
|
-
const data = await updateOpenclaw();
|
|
246
|
-
return data;
|
|
247
|
-
} finally {
|
|
248
|
-
setOpenclawUpdateInProgress(false);
|
|
249
|
-
refreshSharedStatuses();
|
|
250
|
-
setTimeout(refreshSharedStatuses, 1200);
|
|
251
|
-
setTimeout(refreshSharedStatuses, 3500);
|
|
252
|
-
setTimeout(refreshRestartStatus, 1200);
|
|
253
|
-
}
|
|
254
|
-
}, [openclawUpdateInProgress, refreshRestartStatus, refreshSharedStatuses]);
|
|
255
|
-
|
|
256
|
-
const handleOpenclawVersionActionComplete = useCallback(
|
|
257
|
-
({ type }) => {
|
|
258
|
-
if (type !== "update") return;
|
|
259
|
-
refreshSharedStatuses();
|
|
260
|
-
setTimeout(refreshSharedStatuses, 1200);
|
|
261
|
-
},
|
|
262
|
-
[refreshSharedStatuses],
|
|
263
|
-
);
|
|
264
|
-
|
|
265
243
|
const handleAcUpdate = useCallback(async () => {
|
|
266
244
|
if (acUpdating) return;
|
|
267
245
|
setAcUpdating(true);
|
|
268
246
|
try {
|
|
269
247
|
const data = await updateAlphaclaw();
|
|
270
248
|
if (data.ok) {
|
|
271
|
-
showToast(
|
|
272
|
-
|
|
249
|
+
showToast(
|
|
250
|
+
data.managedUpdate
|
|
251
|
+
? "Deployment update started — reconnecting..."
|
|
252
|
+
: "AlphaClaw updated — restarting...",
|
|
253
|
+
"success",
|
|
254
|
+
);
|
|
255
|
+
setTimeout(() => window.location.reload(), data.managedUpdate ? 8000 : 5000);
|
|
273
256
|
} else {
|
|
274
257
|
showToast(data.error || "AlphaClaw update failed", "error");
|
|
275
258
|
setAcUpdating(false);
|
|
@@ -296,13 +279,15 @@ export const useAppShellController = ({ location = "" } = {}) => {
|
|
|
296
279
|
state: {
|
|
297
280
|
acHasUpdate,
|
|
298
281
|
acLatest,
|
|
282
|
+
acLatestOpenclawVersion,
|
|
283
|
+
acCurrentOpenclawVersion,
|
|
284
|
+
acUpdateStrategy,
|
|
299
285
|
acUpdating,
|
|
300
286
|
acVersion,
|
|
301
287
|
authEnabled,
|
|
302
288
|
gatewayRestartSignal,
|
|
303
289
|
isAnyRestartRequired,
|
|
304
290
|
onboarded,
|
|
305
|
-
openclawUpdateInProgress,
|
|
306
291
|
restartingGateway,
|
|
307
292
|
sharedDoctorStatus,
|
|
308
293
|
sharedStatus,
|
|
@@ -312,8 +297,6 @@ export const useAppShellController = ({ location = "" } = {}) => {
|
|
|
312
297
|
handleAcUpdate,
|
|
313
298
|
handleGatewayRestart,
|
|
314
299
|
handleOnboardingComplete: () => setOnboarded(true),
|
|
315
|
-
handleOpenclawUpdate,
|
|
316
|
-
handleOpenclawVersionActionComplete,
|
|
317
300
|
refreshSharedStatuses,
|
|
318
301
|
dismissRestartBanner,
|
|
319
302
|
setRestartRequired,
|
package/lib/public/js/lib/api.js
CHANGED
|
@@ -469,17 +469,6 @@ export async function fetchDashboardUrl() {
|
|
|
469
469
|
return res.json();
|
|
470
470
|
}
|
|
471
471
|
|
|
472
|
-
export async function fetchOpenclawVersion(refresh = false) {
|
|
473
|
-
const query = refresh ? "?refresh=1" : "";
|
|
474
|
-
const res = await authFetch(`/api/openclaw/version${query}`);
|
|
475
|
-
return res.json();
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
export async function updateOpenclaw() {
|
|
479
|
-
const res = await authFetch("/api/openclaw/update", { method: "POST" });
|
|
480
|
-
return res.json();
|
|
481
|
-
}
|
|
482
|
-
|
|
483
472
|
export async function fetchAlphaclawVersion(refresh = false) {
|
|
484
473
|
const query = refresh ? "?refresh=1" : "";
|
|
485
474
|
const res = await authFetch(`/api/alphaclaw/version${query}`);
|
|
@@ -1365,20 +1354,3 @@ export const syncBrowseChanges = async (message = "") => {
|
|
|
1365
1354
|
});
|
|
1366
1355
|
return parseJsonOrThrow(res, "Could not sync changes");
|
|
1367
1356
|
};
|
|
1368
|
-
|
|
1369
|
-
// ── MCP ──────────────────────────────────────────────────────────
|
|
1370
|
-
|
|
1371
|
-
export const fetchMcpInfo = async () => {
|
|
1372
|
-
const res = await authFetch("/api/mcp/info");
|
|
1373
|
-
return res.json();
|
|
1374
|
-
};
|
|
1375
|
-
|
|
1376
|
-
export const startMcpBridge = async () => {
|
|
1377
|
-
const res = await authFetch("/api/mcp/start", { method: "POST" });
|
|
1378
|
-
return res.json();
|
|
1379
|
-
};
|
|
1380
|
-
|
|
1381
|
-
export const stopMcpBridge = async () => {
|
|
1382
|
-
const res = await authFetch("/api/mcp/stop", { method: "POST" });
|
|
1383
|
-
return res.json();
|
|
1384
|
-
};
|