@chrysb/alphaclaw 0.9.0-beta.6 → 0.9.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 +25 -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 +101 -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 +20 -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 +47 -1
- 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/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/index.js +5 -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/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 -252
- 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,15 @@ import {
|
|
|
13
14
|
getVisibleAiFieldKeys,
|
|
14
15
|
kProviderAuthFields,
|
|
15
16
|
} from "../../lib/model-config.js";
|
|
17
|
+
import {
|
|
18
|
+
getInitialOnboardingModelKey,
|
|
19
|
+
getModelCatalogModels,
|
|
20
|
+
kModelCatalogCacheKey,
|
|
21
|
+
} from "../../lib/model-catalog.js";
|
|
16
22
|
import {
|
|
17
23
|
kWelcomeGroups,
|
|
24
|
+
getWelcomeGroupError,
|
|
25
|
+
findFirstInvalidWelcomeGroup,
|
|
18
26
|
isValidGithubRepoInput,
|
|
19
27
|
kGithubFlowFresh,
|
|
20
28
|
kGithubFlowImport,
|
|
@@ -76,11 +84,18 @@ const normalizePlaceholderReview = (review) => {
|
|
|
76
84
|
export const useWelcome = ({ onComplete }) => {
|
|
77
85
|
const kSetupStepIndex = kWelcomeGroups.length;
|
|
78
86
|
const kPairingStepIndex = kSetupStepIndex + 1;
|
|
79
|
-
const {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
const {
|
|
88
|
+
vals,
|
|
89
|
+
setVals,
|
|
90
|
+
setValue: setStoredValue,
|
|
91
|
+
step,
|
|
92
|
+
setStep,
|
|
93
|
+
setupError,
|
|
94
|
+
setSetupError,
|
|
95
|
+
} = useWelcomeStorage({
|
|
96
|
+
kSetupStepIndex,
|
|
97
|
+
kPairingStepIndex,
|
|
98
|
+
});
|
|
84
99
|
const [models, setModels] = useState([]);
|
|
85
100
|
const [modelsLoading, setModelsLoading] = useState(true);
|
|
86
101
|
const [modelsError, setModelsError] = useState(null);
|
|
@@ -110,6 +125,14 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
110
125
|
const [importScanResult, setImportScanResult] = useState(null);
|
|
111
126
|
const [importScanning, setImportScanning] = useState(false);
|
|
112
127
|
const [importError, setImportError] = useState(null);
|
|
128
|
+
const modelsFetchState = useCachedFetch(kModelCatalogCacheKey, fetchModels, {
|
|
129
|
+
maxAgeMs: 30000,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const setValue = (key, value) => {
|
|
133
|
+
if (formError) setFormError(null);
|
|
134
|
+
setStoredValue(key, value);
|
|
135
|
+
};
|
|
113
136
|
|
|
114
137
|
const setImportStep = (nextStep) => {
|
|
115
138
|
setImportStepState(nextStep);
|
|
@@ -129,21 +152,54 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
129
152
|
};
|
|
130
153
|
|
|
131
154
|
useEffect(() => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
155
|
+
const list = getModelCatalogModels(modelsFetchState.data);
|
|
156
|
+
if (!modelsFetchState.data) return;
|
|
157
|
+
setModels(list);
|
|
158
|
+
setModelsError(list.length > 0 ? null : "No models found");
|
|
159
|
+
const defaultModelKey = getInitialOnboardingModelKey({
|
|
160
|
+
catalog: list,
|
|
161
|
+
currentModelKey: vals.MODEL_KEY,
|
|
162
|
+
});
|
|
163
|
+
if (!vals.MODEL_KEY && defaultModelKey) {
|
|
164
|
+
setVals((prev) => ({ ...prev, MODEL_KEY: defaultModelKey }));
|
|
165
|
+
}
|
|
166
|
+
}, [modelsFetchState.data, setVals, vals.MODEL_KEY]);
|
|
167
|
+
|
|
168
|
+
useEffect(() => {
|
|
169
|
+
const hasModels = getModelCatalogModels(modelsFetchState.data).length > 0;
|
|
170
|
+
setModelsLoading(modelsFetchState.loading && !hasModels);
|
|
171
|
+
}, [modelsFetchState.data, modelsFetchState.loading]);
|
|
172
|
+
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (!modelsFetchState.error) return;
|
|
175
|
+
setModelsError("Failed to load models");
|
|
176
|
+
setModelsLoading(false);
|
|
177
|
+
}, [modelsFetchState.error]);
|
|
178
|
+
|
|
179
|
+
const getValidationContext = (currentVals = {}) => {
|
|
180
|
+
const currentSelectedProvider = getModelProvider(
|
|
181
|
+
String(currentVals.MODEL_KEY || "").trim(),
|
|
182
|
+
);
|
|
183
|
+
const currentSelectedAuthProvider =
|
|
184
|
+
getAuthProviderFromModelProvider(currentSelectedProvider);
|
|
185
|
+
const currentProviderAuthFields =
|
|
186
|
+
kProviderAuthFields[currentSelectedAuthProvider] || [];
|
|
187
|
+
const currentHasAi =
|
|
188
|
+
currentSelectedProvider === "openai-codex"
|
|
189
|
+
? !!codexStatus.connected
|
|
190
|
+
: currentProviderAuthFields.some((field) =>
|
|
191
|
+
!!String(currentVals[field.key] || "").trim(),
|
|
192
|
+
);
|
|
145
193
|
|
|
146
|
-
|
|
194
|
+
return {
|
|
195
|
+
hasAi: currentHasAi,
|
|
196
|
+
selectedProvider: currentSelectedProvider,
|
|
197
|
+
codexLoading,
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const validationContext = getValidationContext(vals);
|
|
202
|
+
const { selectedProvider, hasAi } = validationContext;
|
|
147
203
|
const placeholderReview = normalizePlaceholderReview(
|
|
148
204
|
vals[kImportPlaceholderReviewKey],
|
|
149
205
|
);
|
|
@@ -164,23 +220,10 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
164
220
|
const canToggleFullCatalog =
|
|
165
221
|
featuredModels.length > 0 && models.length > featuredModels.length;
|
|
166
222
|
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
223
|
const isPreStep = step === -1;
|
|
178
224
|
const isSetupStep = step === kSetupStepIndex;
|
|
179
225
|
const isPairingStep = step === kPairingStepIndex;
|
|
180
226
|
const activeGroup = step >= 0 && step < kSetupStepIndex ? kWelcomeGroups[step] : null;
|
|
181
|
-
const currentGroupValid = activeGroup
|
|
182
|
-
? activeGroup.validate(vals, { hasAi })
|
|
183
|
-
: false;
|
|
184
227
|
const selectedPairingChannel = String(
|
|
185
228
|
vals[kPairingChannelKey] || getPreferredPairingChannel(vals),
|
|
186
229
|
);
|
|
@@ -202,7 +245,21 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
202
245
|
const handleSubmit = async () => {
|
|
203
246
|
const { normalizedVals, didChange } = normalizeOnboardingVals(vals);
|
|
204
247
|
if (didChange) setVals(normalizedVals);
|
|
205
|
-
|
|
248
|
+
const submitValidationContext = getValidationContext(normalizedVals);
|
|
249
|
+
const invalidGroup = findFirstInvalidWelcomeGroup(
|
|
250
|
+
normalizedVals,
|
|
251
|
+
submitValidationContext,
|
|
252
|
+
);
|
|
253
|
+
if (invalidGroup) {
|
|
254
|
+
setFormError(
|
|
255
|
+
getWelcomeGroupError(
|
|
256
|
+
invalidGroup.id,
|
|
257
|
+
normalizedVals,
|
|
258
|
+
submitValidationContext,
|
|
259
|
+
),
|
|
260
|
+
);
|
|
261
|
+
setSetupError(null);
|
|
262
|
+
setStep(kWelcomeGroups.findIndex((group) => group.id === invalidGroup.id));
|
|
206
263
|
return;
|
|
207
264
|
}
|
|
208
265
|
if (loading) return;
|
|
@@ -309,7 +366,17 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
309
366
|
const goNext = async () => {
|
|
310
367
|
const { normalizedVals, didChange } = normalizeOnboardingVals(vals);
|
|
311
368
|
if (didChange) setVals(normalizedVals);
|
|
312
|
-
if (!activeGroup
|
|
369
|
+
if (!activeGroup) return;
|
|
370
|
+
const stepValidationContext = getValidationContext(normalizedVals);
|
|
371
|
+
const stepValidationError = getWelcomeGroupError(
|
|
372
|
+
activeGroup.id,
|
|
373
|
+
normalizedVals,
|
|
374
|
+
stepValidationContext,
|
|
375
|
+
);
|
|
376
|
+
if (stepValidationError) {
|
|
377
|
+
setFormError(stepValidationError);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
313
380
|
setFormError(null);
|
|
314
381
|
if (activeGroup.id === "github") {
|
|
315
382
|
const githubFlow = normalizedVals._GITHUB_FLOW || kGithubFlowFresh;
|
|
@@ -545,12 +612,10 @@ export const useWelcome = ({ onComplete }) => {
|
|
|
545
612
|
canToggleFullCatalog,
|
|
546
613
|
visibleAiFieldKeys,
|
|
547
614
|
hasAi,
|
|
548
|
-
allValid,
|
|
549
615
|
isPreStep,
|
|
550
616
|
isSetupStep,
|
|
551
617
|
isPairingStep,
|
|
552
618
|
activeGroup,
|
|
553
|
-
currentGroupValid,
|
|
554
619
|
selectedPairingChannel,
|
|
555
620
|
placeholderReview,
|
|
556
621
|
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
|
-
};
|