@chrysb/alphaclaw 0.9.0-beta.7 → 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 -292
- package/patches/openclaw+2026.3.28.patch +0 -13
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { h } from "preact";
|
|
2
2
|
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
3
3
|
import htm from "htm";
|
|
4
|
-
import { fetchOpenclawVersion, updateOpenclaw } from "../lib/api.js";
|
|
5
4
|
import { UpdateActionButton } from "./update-action-button.js";
|
|
6
|
-
import { ConfirmDialog } from "./confirm-dialog.js";
|
|
7
|
-
import { showToast } from "./toast.js";
|
|
8
5
|
const html = htm.bind(h);
|
|
9
6
|
|
|
10
7
|
const formatDuration = (ms) => {
|
|
@@ -21,287 +18,14 @@ const formatDuration = (ms) => {
|
|
|
21
18
|
return `${seconds}s`;
|
|
22
19
|
};
|
|
23
20
|
|
|
24
|
-
const VersionRow = ({
|
|
25
|
-
label,
|
|
26
|
-
currentVersion,
|
|
27
|
-
fetchVersion,
|
|
28
|
-
applyUpdate,
|
|
29
|
-
updateInProgress = false,
|
|
30
|
-
onActionComplete = () => {},
|
|
31
|
-
}) => {
|
|
32
|
-
const [checking, setChecking] = useState(false);
|
|
33
|
-
const [version, setVersion] = useState(currentVersion || null);
|
|
34
|
-
const [latestVersion, setLatestVersion] = useState(null);
|
|
35
|
-
const [hasUpdate, setHasUpdate] = useState(false);
|
|
36
|
-
const [error, setError] = useState("");
|
|
37
|
-
const [hasViewedChangelog, setHasViewedChangelog] = useState(false);
|
|
38
|
-
const [confirmWithoutChangelogOpen, setConfirmWithoutChangelogOpen] =
|
|
39
|
-
useState(false);
|
|
40
|
-
const simulateUpdate = (() => {
|
|
41
|
-
try {
|
|
42
|
-
const params = new URLSearchParams(window.location.search);
|
|
43
|
-
return params.get("simulateUpdate") === "1";
|
|
44
|
-
} catch {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
})();
|
|
48
|
-
const simulatedVersion = (() => {
|
|
49
|
-
if (!simulateUpdate) return null;
|
|
50
|
-
try {
|
|
51
|
-
const params = new URLSearchParams(window.location.search);
|
|
52
|
-
return params.get("simulateVersion") || "v0.0.0-preview";
|
|
53
|
-
} catch {
|
|
54
|
-
return "v0.0.0-preview";
|
|
55
|
-
}
|
|
56
|
-
})();
|
|
57
|
-
const effectiveHasUpdate = simulateUpdate || hasUpdate;
|
|
58
|
-
const effectiveLatestVersion = simulatedVersion || latestVersion;
|
|
59
|
-
const isUpdateActionActive = updateInProgress || effectiveHasUpdate;
|
|
60
|
-
const updateIdleLabel = effectiveLatestVersion
|
|
61
|
-
? `Update to ${effectiveLatestVersion}`
|
|
62
|
-
: "Update";
|
|
63
|
-
const changelogUrl = "https://github.com/openclaw/openclaw/tags";
|
|
64
|
-
const showMobileUpdateRow = effectiveHasUpdate && effectiveLatestVersion;
|
|
65
|
-
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
setVersion(currentVersion || null);
|
|
68
|
-
}, [currentVersion]);
|
|
69
|
-
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
let active = true;
|
|
72
|
-
const load = async (refresh = false) => {
|
|
73
|
-
try {
|
|
74
|
-
const data = await fetchVersion(refresh);
|
|
75
|
-
if (!active) return;
|
|
76
|
-
setVersion(data.currentVersion || currentVersion || null);
|
|
77
|
-
setLatestVersion(data.latestVersion || null);
|
|
78
|
-
setHasUpdate(!!data.hasUpdate);
|
|
79
|
-
setError(data.ok ? "" : data.error || "");
|
|
80
|
-
} catch (err) {
|
|
81
|
-
if (!active) return;
|
|
82
|
-
setError(err.message || "Could not check updates");
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
load(false);
|
|
86
|
-
return () => {
|
|
87
|
-
active = false;
|
|
88
|
-
};
|
|
89
|
-
}, [currentVersion, fetchVersion]);
|
|
90
|
-
|
|
91
|
-
useEffect(() => {
|
|
92
|
-
if (updateInProgress) return () => {};
|
|
93
|
-
let active = true;
|
|
94
|
-
const timeoutId = setTimeout(async () => {
|
|
95
|
-
try {
|
|
96
|
-
const data = await fetchVersion(true);
|
|
97
|
-
if (!active) return;
|
|
98
|
-
setVersion(data.currentVersion || currentVersion || null);
|
|
99
|
-
setLatestVersion(data.latestVersion || null);
|
|
100
|
-
setHasUpdate(!!data.hasUpdate);
|
|
101
|
-
setError(data.ok ? "" : data.error || "");
|
|
102
|
-
} catch {}
|
|
103
|
-
}, 1200);
|
|
104
|
-
return () => {
|
|
105
|
-
active = false;
|
|
106
|
-
clearTimeout(timeoutId);
|
|
107
|
-
};
|
|
108
|
-
}, [updateInProgress, currentVersion, fetchVersion]);
|
|
109
|
-
|
|
110
|
-
useEffect(() => {
|
|
111
|
-
if (!effectiveHasUpdate || !effectiveLatestVersion) {
|
|
112
|
-
setHasViewedChangelog(false);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
setHasViewedChangelog(false);
|
|
116
|
-
}, [effectiveHasUpdate, effectiveLatestVersion]);
|
|
117
|
-
|
|
118
|
-
const runAction = async () => {
|
|
119
|
-
const isUpdateAction = !!effectiveHasUpdate;
|
|
120
|
-
const busy = isUpdateActionActive ? checking || updateInProgress : checking;
|
|
121
|
-
if (busy) return;
|
|
122
|
-
setChecking(true);
|
|
123
|
-
setError("");
|
|
124
|
-
try {
|
|
125
|
-
const data = isUpdateAction
|
|
126
|
-
? await applyUpdate()
|
|
127
|
-
: await fetchVersion(true);
|
|
128
|
-
setVersion(data.currentVersion || version);
|
|
129
|
-
setLatestVersion(data.latestVersion || null);
|
|
130
|
-
setHasUpdate(!!data.hasUpdate);
|
|
131
|
-
setError(data.ok ? "" : data.error || "");
|
|
132
|
-
if (isUpdateAction) {
|
|
133
|
-
if (!data.ok) {
|
|
134
|
-
showToast(data.error || `${label} update failed`, "error");
|
|
135
|
-
} else if (data.updated || data.restarting) {
|
|
136
|
-
showToast(
|
|
137
|
-
data.restarting
|
|
138
|
-
? `${label} updated — restarting...`
|
|
139
|
-
: `Updated ${label} to ${data.currentVersion}`,
|
|
140
|
-
"success",
|
|
141
|
-
);
|
|
142
|
-
} else {
|
|
143
|
-
showToast(`Already at latest ${label} version`, "success");
|
|
144
|
-
}
|
|
145
|
-
} else if (data.hasUpdate && data.latestVersion) {
|
|
146
|
-
showToast(
|
|
147
|
-
`${label} update available: ${data.latestVersion}`,
|
|
148
|
-
"warning",
|
|
149
|
-
);
|
|
150
|
-
} else {
|
|
151
|
-
showToast(`${label} is up to date`, "success");
|
|
152
|
-
}
|
|
153
|
-
await onActionComplete({
|
|
154
|
-
type: isUpdateAction ? "update" : "check",
|
|
155
|
-
ok: !!data?.ok,
|
|
156
|
-
result: data,
|
|
157
|
-
});
|
|
158
|
-
} catch (err) {
|
|
159
|
-
setError(
|
|
160
|
-
err.message ||
|
|
161
|
-
(isUpdateAction
|
|
162
|
-
? `Could not update ${label}`
|
|
163
|
-
: "Could not check updates"),
|
|
164
|
-
);
|
|
165
|
-
showToast(
|
|
166
|
-
isUpdateAction
|
|
167
|
-
? `Could not update ${label}`
|
|
168
|
-
: "Could not check updates",
|
|
169
|
-
"error",
|
|
170
|
-
);
|
|
171
|
-
await onActionComplete({
|
|
172
|
-
type: isUpdateAction ? "update" : "check",
|
|
173
|
-
ok: false,
|
|
174
|
-
error: err,
|
|
175
|
-
});
|
|
176
|
-
} finally {
|
|
177
|
-
setChecking(false);
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
const handleAction = () => {
|
|
182
|
-
const busy = isUpdateActionActive ? checking || updateInProgress : checking;
|
|
183
|
-
if (busy) return;
|
|
184
|
-
if (effectiveHasUpdate && effectiveLatestVersion && !hasViewedChangelog) {
|
|
185
|
-
setConfirmWithoutChangelogOpen(true);
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
runAction();
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const handleConfirmWithoutChangelog = () => {
|
|
192
|
-
setConfirmWithoutChangelogOpen(false);
|
|
193
|
-
runAction();
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
const updateButtonLoading = isUpdateActionActive
|
|
197
|
-
? checking || updateInProgress
|
|
198
|
-
: checking;
|
|
199
|
-
|
|
200
|
-
return html`
|
|
201
|
-
<div class="flex items-center justify-between gap-3">
|
|
202
|
-
<div class="min-w-0">
|
|
203
|
-
<p class="text-xs text-body truncate">
|
|
204
|
-
<span class="text-fg-muted">${label}</span>${" "}${version
|
|
205
|
-
? `${version}`
|
|
206
|
-
: "..."}
|
|
207
|
-
</p>
|
|
208
|
-
${error &&
|
|
209
|
-
effectiveHasUpdate &&
|
|
210
|
-
html`<div
|
|
211
|
-
class="mt-1 text-xs text-status-error bg-status-error-bg border border-status-error-border rounded-lg px-2 py-1"
|
|
212
|
-
>
|
|
213
|
-
${error}
|
|
214
|
-
</div>`}
|
|
215
|
-
</div>
|
|
216
|
-
<div class="flex items-center gap-3 shrink-0">
|
|
217
|
-
${effectiveHasUpdate &&
|
|
218
|
-
effectiveLatestVersion &&
|
|
219
|
-
html`
|
|
220
|
-
<a
|
|
221
|
-
href=${changelogUrl}
|
|
222
|
-
target="_blank"
|
|
223
|
-
rel="noreferrer"
|
|
224
|
-
onclick=${() => setHasViewedChangelog(true)}
|
|
225
|
-
class="hidden md:inline text-xs text-fg-muted hover:text-body transition-colors"
|
|
226
|
-
>View changelog</a
|
|
227
|
-
>
|
|
228
|
-
`}
|
|
229
|
-
${showMobileUpdateRow
|
|
230
|
-
? html`
|
|
231
|
-
<${UpdateActionButton}
|
|
232
|
-
onClick=${handleAction}
|
|
233
|
-
loading=${updateButtonLoading}
|
|
234
|
-
warning=${isUpdateActionActive}
|
|
235
|
-
idleLabel=${isUpdateActionActive
|
|
236
|
-
? updateIdleLabel
|
|
237
|
-
: "Check updates"}
|
|
238
|
-
loadingLabel=${isUpdateActionActive
|
|
239
|
-
? "Updating..."
|
|
240
|
-
: "Checking..."}
|
|
241
|
-
className="hidden md:inline-flex"
|
|
242
|
-
/>
|
|
243
|
-
`
|
|
244
|
-
: html`
|
|
245
|
-
<${UpdateActionButton}
|
|
246
|
-
onClick=${handleAction}
|
|
247
|
-
loading=${updateButtonLoading}
|
|
248
|
-
warning=${isUpdateActionActive}
|
|
249
|
-
idleLabel=${isUpdateActionActive
|
|
250
|
-
? updateIdleLabel
|
|
251
|
-
: "Check updates"}
|
|
252
|
-
loadingLabel=${isUpdateActionActive
|
|
253
|
-
? "Updating..."
|
|
254
|
-
: "Checking..."}
|
|
255
|
-
/>
|
|
256
|
-
`}
|
|
257
|
-
</div>
|
|
258
|
-
</div>
|
|
259
|
-
${showMobileUpdateRow &&
|
|
260
|
-
html`
|
|
261
|
-
<div class="mt-2 md:hidden flex items-center gap-2">
|
|
262
|
-
<a
|
|
263
|
-
href=${changelogUrl}
|
|
264
|
-
target="_blank"
|
|
265
|
-
rel="noreferrer"
|
|
266
|
-
onclick=${() => setHasViewedChangelog(true)}
|
|
267
|
-
class="inline-flex items-center justify-center flex-1 h-9 text-xs rounded-lg border border-border text-fg-muted hover:text-body hover:border-fg-muted transition-colors"
|
|
268
|
-
>View changelog</a
|
|
269
|
-
>
|
|
270
|
-
<${UpdateActionButton}
|
|
271
|
-
onClick=${handleAction}
|
|
272
|
-
loading=${updateButtonLoading}
|
|
273
|
-
warning=${isUpdateActionActive}
|
|
274
|
-
idleLabel=${updateIdleLabel}
|
|
275
|
-
loadingLabel="Updating..."
|
|
276
|
-
className="flex-1 h-9 px-3"
|
|
277
|
-
/>
|
|
278
|
-
</div>
|
|
279
|
-
`}
|
|
280
|
-
<${ConfirmDialog}
|
|
281
|
-
visible=${confirmWithoutChangelogOpen}
|
|
282
|
-
title="Update without changelog?"
|
|
283
|
-
message="Are you sure you want to update without viewing the changelog?"
|
|
284
|
-
confirmLabel=${`Update to ${effectiveLatestVersion || "latest"}`}
|
|
285
|
-
cancelLabel="Cancel"
|
|
286
|
-
confirmTone="warning"
|
|
287
|
-
onCancel=${() => setConfirmWithoutChangelogOpen(false)}
|
|
288
|
-
onConfirm=${handleConfirmWithoutChangelog}
|
|
289
|
-
/>
|
|
290
|
-
`;
|
|
291
|
-
};
|
|
292
|
-
|
|
293
21
|
export const Gateway = ({
|
|
294
22
|
status,
|
|
295
|
-
openclawVersion,
|
|
296
23
|
restarting = false,
|
|
297
24
|
onRestart,
|
|
298
25
|
watchdogStatus = null,
|
|
299
26
|
onOpenWatchdog,
|
|
300
27
|
onRepair,
|
|
301
28
|
repairing = false,
|
|
302
|
-
openclawUpdateInProgress = false,
|
|
303
|
-
onOpenclawVersionActionComplete = () => {},
|
|
304
|
-
onOpenclawUpdate = updateOpenclaw,
|
|
305
29
|
}) => {
|
|
306
30
|
const [nowMs, setNowMs] = useState(() => Date.now());
|
|
307
31
|
const isRunning = status === "running" && !restarting;
|
|
@@ -436,15 +160,5 @@ export const Gateway = ({
|
|
|
436
160
|
: null}
|
|
437
161
|
</div>
|
|
438
162
|
</div>
|
|
439
|
-
<div class="mt-3">
|
|
440
|
-
<${VersionRow}
|
|
441
|
-
label="OpenClaw"
|
|
442
|
-
currentVersion=${openclawVersion}
|
|
443
|
-
fetchVersion=${fetchOpenclawVersion}
|
|
444
|
-
applyUpdate=${onOpenclawUpdate}
|
|
445
|
-
updateInProgress=${openclawUpdateInProgress}
|
|
446
|
-
onActionComplete=${onOpenclawVersionActionComplete}
|
|
447
|
-
/>
|
|
448
|
-
</div>
|
|
449
163
|
</div>`;
|
|
450
164
|
};
|
|
@@ -28,9 +28,6 @@ export const GeneralTab = ({
|
|
|
28
28
|
restartingGateway = false,
|
|
29
29
|
onRestartGateway = () => {},
|
|
30
30
|
restartSignal = 0,
|
|
31
|
-
openclawUpdateInProgress = false,
|
|
32
|
-
onOpenclawVersionActionComplete = () => {},
|
|
33
|
-
onOpenclawUpdate = () => {},
|
|
34
31
|
onRestartRequired = () => {},
|
|
35
32
|
onDismissDoctorWarning = () => {},
|
|
36
33
|
}) => {
|
|
@@ -47,16 +44,12 @@ export const GeneralTab = ({
|
|
|
47
44
|
<div class="space-y-4">
|
|
48
45
|
<${Gateway}
|
|
49
46
|
status=${state.gatewayStatus}
|
|
50
|
-
openclawVersion=${state.openclawVersion}
|
|
51
47
|
restarting=${restartingGateway}
|
|
52
48
|
onRestart=${onRestartGateway}
|
|
53
49
|
watchdogStatus=${state.watchdogStatus}
|
|
54
50
|
onOpenWatchdog=${() => onSwitchTab("watchdog")}
|
|
55
51
|
onRepair=${actions.handleWatchdogRepair}
|
|
56
52
|
repairing=${state.repairingWatchdog}
|
|
57
|
-
openclawUpdateInProgress=${openclawUpdateInProgress}
|
|
58
|
-
onOpenclawVersionActionComplete=${onOpenclawVersionActionComplete}
|
|
59
|
-
onOpenclawUpdate=${onOpenclawUpdate}
|
|
60
53
|
/>
|
|
61
54
|
<${GeneralDoctorWarning}
|
|
62
55
|
doctorStatus=${state.doctorStatus}
|
|
@@ -321,19 +321,6 @@ export const SignalTowerLineIcon = ({ className = "" }) => html`
|
|
|
321
321
|
</svg>
|
|
322
322
|
`;
|
|
323
323
|
|
|
324
|
-
export const WebhookLineIcon = ({ className = "" }) => html`
|
|
325
|
-
<svg
|
|
326
|
-
class=${className}
|
|
327
|
-
viewBox="0 0 24 24"
|
|
328
|
-
fill="currentColor"
|
|
329
|
-
aria-hidden="true"
|
|
330
|
-
>
|
|
331
|
-
<path
|
|
332
|
-
d="M8.86874 14.1392C8.6556 14.4912 8.55014 14.7778 8.72043 15.2253C9.1905 16.4613 8.52737 17.664 7.28097 17.9905C6.10556 18.2985 4.96035 17.526 4.72713 16.2676C4.52048 15.1537 5.38488 14.0617 6.61294 13.8877C6.67963 13.8781 6.74717 13.874 6.83351 13.8688C6.88044 13.866 6.93293 13.8628 6.99384 13.8582L8.86194 10.7257C7.687 9.55742 6.98767 8.19164 7.14246 6.49936C7.25188 5.30308 7.72226 4.26933 8.58208 3.42201C10.2288 1.79945 12.7411 1.53667 14.68 2.78212C16.5423 3.97841 17.3951 6.30867 16.6681 8.30311L14.9611 7.84C15.1895 6.73115 15.0206 5.73536 14.2727 4.88234C13.7786 4.31914 13.1446 4.02394 12.4236 3.91516C10.9783 3.69681 9.55922 4.6254 9.13816 6.04399C8.66019 7.65406 9.38355 8.96924 11.3603 9.96029C10.5311 11.3541 9.70859 12.7518 8.86874 14.1392ZM13.7838 8.27337C14.3816 9.32798 14.9886 10.3986 15.5902 11.4593C18.631 10.5186 20.9237 12.2018 21.7462 14.004C22.7396 16.1809 22.0605 18.7593 20.1094 20.1023C18.1067 21.481 15.5741 21.2454 13.7997 19.4744L15.1919 18.3094C16.9444 19.4445 18.4772 19.3911 19.6151 18.047C20.5855 16.9003 20.5644 15.1906 19.5659 14.068C18.4136 12.7726 16.8701 12.7331 15.0044 13.9767C14.2305 12.6037 13.443 11.2413 12.6936 9.85845C12.4409 9.39233 12.1618 9.12196 11.5923 9.0233C10.6411 8.85839 10.027 8.04157 9.99016 7.12642C9.95395 6.22138 10.4871 5.4033 11.3205 5.08455C12.146 4.7688 13.1148 5.02367 13.6701 5.72554C14.1239 6.29901 14.2681 6.94443 14.0293 7.65167C13.9843 7.7852 13.9304 7.91584 13.8713 8.05885C13.8431 8.12694 13.8138 8.19801 13.7838 8.27337ZM11.552 16.895H15.2126C15.2636 16.963 15.3113 17.0303 15.3579 17.0959C15.4551 17.233 15.5474 17.3632 15.6551 17.4788C16.4304 18.3077 17.7395 18.3489 18.5682 17.5795C19.4271 16.7821 19.466 15.4426 18.6544 14.6101C17.8602 13.7955 16.5029 13.7177 15.7655 14.5802C15.3176 15.1044 14.8586 15.166 14.2641 15.1567C12.7414 15.1332 11.2177 15.149 9.69524 15.149C9.79406 17.2909 8.98436 18.6255 7.37841 18.9424C5.80582 19.2528 4.3575 18.4504 3.84759 16.9864C3.26842 15.3229 3.98467 13.9925 6.05421 12.9366C5.89847 12.3725 5.74115 11.8016 5.58541 11.236C3.32977 11.7276 1.63749 13.916 1.8122 16.378C1.96652 18.5514 3.71968 20.4815 5.86369 20.8273C7.02819 21.0153 8.12233 20.82 9.13741 20.2442C10.4433 19.5032 11.2011 18.3381 11.552 16.895Z"
|
|
333
|
-
/>
|
|
334
|
-
</svg>
|
|
335
|
-
`;
|
|
336
|
-
|
|
337
324
|
export const GitBranchLineIcon = ({ className = "" }) => html`
|
|
338
325
|
<svg
|
|
339
326
|
class=${className}
|
|
@@ -521,48 +508,62 @@ export const EyeLineIcon = ({ className = "" }) => html`
|
|
|
521
508
|
</svg>
|
|
522
509
|
`;
|
|
523
510
|
|
|
524
|
-
export const
|
|
511
|
+
export const SunIcon = ({ className = "" }) => html`
|
|
525
512
|
<svg
|
|
526
513
|
class=${className}
|
|
527
514
|
viewBox="0 0 24 24"
|
|
528
|
-
fill="
|
|
515
|
+
fill="none"
|
|
516
|
+
stroke="currentColor"
|
|
517
|
+
stroke-width="2"
|
|
518
|
+
stroke-linecap="round"
|
|
519
|
+
stroke-linejoin="round"
|
|
529
520
|
aria-hidden="true"
|
|
530
521
|
>
|
|
531
|
-
<
|
|
522
|
+
<circle cx="12" cy="12" r="5" />
|
|
523
|
+
<line x1="12" y1="1" x2="12" y2="3" />
|
|
524
|
+
<line x1="12" y1="21" x2="12" y2="23" />
|
|
525
|
+
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
|
526
|
+
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
|
527
|
+
<line x1="1" y1="12" x2="3" y2="12" />
|
|
528
|
+
<line x1="21" y1="12" x2="23" y2="12" />
|
|
529
|
+
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
|
530
|
+
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
|
532
531
|
</svg>
|
|
533
532
|
`;
|
|
534
533
|
|
|
535
|
-
export const
|
|
534
|
+
export const MoonIcon = ({ className = "" }) => html`
|
|
536
535
|
<svg
|
|
537
536
|
class=${className}
|
|
538
537
|
viewBox="0 0 24 24"
|
|
539
|
-
fill="
|
|
538
|
+
fill="none"
|
|
539
|
+
stroke="currentColor"
|
|
540
|
+
stroke-width="2"
|
|
541
|
+
stroke-linecap="round"
|
|
542
|
+
stroke-linejoin="round"
|
|
540
543
|
aria-hidden="true"
|
|
541
544
|
>
|
|
542
|
-
<path d="
|
|
545
|
+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
|
543
546
|
</svg>
|
|
544
547
|
`;
|
|
545
548
|
|
|
546
|
-
export const
|
|
549
|
+
export const FullscreenLineIcon = ({ className = "" }) => html`
|
|
547
550
|
<svg
|
|
548
551
|
class=${className}
|
|
549
552
|
viewBox="0 0 24 24"
|
|
550
553
|
fill="currentColor"
|
|
551
554
|
aria-hidden="true"
|
|
552
555
|
>
|
|
553
|
-
<path d="
|
|
556
|
+
<path d="M8 3V5H4V9H2V3H8ZM2 21V15H4V19H8V21H2ZM22 21H16V19H20V15H22V21ZM22 9H20V5H16V3H22V9Z" />
|
|
554
557
|
</svg>
|
|
555
558
|
`;
|
|
556
559
|
|
|
557
|
-
export const
|
|
560
|
+
export const ComputerLineIcon = ({ className = "" }) => html`
|
|
558
561
|
<svg
|
|
559
562
|
class=${className}
|
|
560
563
|
viewBox="0 0 24 24"
|
|
561
564
|
fill="currentColor"
|
|
562
565
|
aria-hidden="true"
|
|
563
566
|
>
|
|
564
|
-
<path
|
|
565
|
-
d="M13.0607 8.11097L14.4749 9.52518C17.2086 12.2589 17.2086 16.691 14.4749 19.4247L14.1214 19.7782C11.3877 22.5119 6.95555 22.5119 4.22188 19.7782C1.48821 17.0446 1.48821 12.6124 4.22188 9.87874L5.6361 11.293C3.68348 13.2456 3.68348 16.4114 5.6361 18.364C7.58872 20.3166 10.7545 20.3166 12.7072 18.364L13.0607 18.0105C15.0133 16.0578 15.0133 12.892 13.0607 10.9394L11.6465 9.52518L13.0607 8.11097ZM19.7782 14.1214L18.364 12.7072C20.3166 10.7545 20.3166 7.58872 18.364 5.6361C16.4114 3.68348 13.2456 3.68348 11.293 5.6361L10.9394 5.98965C8.98678 7.94227 8.98678 11.1081 10.9394 13.0607L12.3536 14.4749L10.9394 15.8891L9.52518 14.4749C6.79151 11.7413 6.79151 7.30911 9.52518 4.57544L9.87874 4.22188C12.6124 1.48821 17.0446 1.48821 19.7782 4.22188C22.5119 6.95555 22.5119 11.3877 19.7782 14.1214Z"
|
|
566
|
-
/>
|
|
567
|
+
<path d="M4 16H20V5H4V16ZM13 18V20H17V22H7V20H11V18H2.9918C2.44405 18 2 17.5511 2 16.9925V4.00748C2 3.45107 2.45531 3 2.9918 3H21.0082C21.556 3 22 3.44892 22 4.00748V16.9925C22 17.5489 21.5447 18 21.0082 18H13Z" />
|
|
567
568
|
</svg>
|
|
568
569
|
`;
|
|
@@ -29,7 +29,7 @@ export const ModalShell = ({
|
|
|
29
29
|
return createPortal(
|
|
30
30
|
html`
|
|
31
31
|
<div
|
|
32
|
-
class="fixed inset-0 bg-overlay flex items-
|
|
32
|
+
class="fixed inset-0 bg-overlay flex items-start justify-center overflow-y-auto p-4 sm:items-center z-50"
|
|
33
33
|
onclick=${(event) => {
|
|
34
34
|
if (closeOnOverlayClick && event.target === event.currentTarget) onClose?.();
|
|
35
35
|
}}
|
|
@@ -5,6 +5,10 @@ import { Badge } from "../badge.js";
|
|
|
5
5
|
import { SecretInput } from "../secret-input.js";
|
|
6
6
|
import { ActionButton } from "../action-button.js";
|
|
7
7
|
import { exchangeCodexOAuth, disconnectCodex } from "../../lib/api.js";
|
|
8
|
+
import {
|
|
9
|
+
isCodexAuthCallbackMessage,
|
|
10
|
+
openCodexAuthWindow,
|
|
11
|
+
} from "../../lib/codex-oauth-window.js";
|
|
8
12
|
import { showToast } from "../toast.js";
|
|
9
13
|
import {
|
|
10
14
|
kProviderAuthFields,
|
|
@@ -108,6 +112,7 @@ const CodexOAuthSection = ({ codexStatus, onRefreshCodex }) => {
|
|
|
108
112
|
const [authWaiting, setAuthWaiting] = useState(false);
|
|
109
113
|
const [manualInput, setManualInput] = useState("");
|
|
110
114
|
const [exchanging, setExchanging] = useState(false);
|
|
115
|
+
const exchangeInFlightRef = useRef(false);
|
|
111
116
|
const popupPollRef = useRef(null);
|
|
112
117
|
|
|
113
118
|
useEffect(
|
|
@@ -117,6 +122,30 @@ const CodexOAuthSection = ({ codexStatus, onRefreshCodex }) => {
|
|
|
117
122
|
[],
|
|
118
123
|
);
|
|
119
124
|
|
|
125
|
+
const submitAuthInput = async (input) => {
|
|
126
|
+
const normalizedInput = String(input || "").trim();
|
|
127
|
+
if (!normalizedInput || exchangeInFlightRef.current) return;
|
|
128
|
+
exchangeInFlightRef.current = true;
|
|
129
|
+
setManualInput(normalizedInput);
|
|
130
|
+
setExchanging(true);
|
|
131
|
+
try {
|
|
132
|
+
const result = await exchangeCodexOAuth(normalizedInput);
|
|
133
|
+
if (!result.ok)
|
|
134
|
+
throw new Error(result.error || "Codex OAuth exchange failed");
|
|
135
|
+
setManualInput("");
|
|
136
|
+
showToast("Codex connected", "success");
|
|
137
|
+
setAuthStarted(false);
|
|
138
|
+
setAuthWaiting(false);
|
|
139
|
+
await onRefreshCodex();
|
|
140
|
+
} catch (err) {
|
|
141
|
+
setAuthWaiting(false);
|
|
142
|
+
showToast(err.message || "Codex OAuth exchange failed", "error");
|
|
143
|
+
} finally {
|
|
144
|
+
exchangeInFlightRef.current = false;
|
|
145
|
+
setExchanging(false);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
120
149
|
useEffect(() => {
|
|
121
150
|
const onMessage = async (e) => {
|
|
122
151
|
if (e.data?.codex === "success") {
|
|
@@ -124,6 +153,8 @@ const CodexOAuthSection = ({ codexStatus, onRefreshCodex }) => {
|
|
|
124
153
|
setAuthStarted(false);
|
|
125
154
|
setAuthWaiting(false);
|
|
126
155
|
await onRefreshCodex();
|
|
156
|
+
} else if (isCodexAuthCallbackMessage(e.data)) {
|
|
157
|
+
await submitAuthInput(e.data.input);
|
|
127
158
|
} else if (e.data?.codex === "error") {
|
|
128
159
|
showToast(
|
|
129
160
|
`Codex auth failed: ${e.data.message || "unknown error"}`,
|
|
@@ -133,19 +164,14 @@ const CodexOAuthSection = ({ codexStatus, onRefreshCodex }) => {
|
|
|
133
164
|
};
|
|
134
165
|
window.addEventListener("message", onMessage);
|
|
135
166
|
return () => window.removeEventListener("message", onMessage);
|
|
136
|
-
}, [onRefreshCodex]);
|
|
167
|
+
}, [onRefreshCodex, submitAuthInput]);
|
|
137
168
|
|
|
138
169
|
const startAuth = () => {
|
|
139
170
|
setAuthStarted(true);
|
|
140
171
|
setAuthWaiting(true);
|
|
141
|
-
const popup =
|
|
142
|
-
"/auth/codex/start",
|
|
143
|
-
"codex-auth",
|
|
144
|
-
"popup=yes,width=640,height=780",
|
|
145
|
-
);
|
|
172
|
+
const popup = openCodexAuthWindow();
|
|
146
173
|
if (!popup || popup.closed) {
|
|
147
174
|
setAuthWaiting(false);
|
|
148
|
-
window.location.href = "/auth/codex/start";
|
|
149
175
|
return;
|
|
150
176
|
}
|
|
151
177
|
if (popupPollRef.current) clearInterval(popupPollRef.current);
|
|
@@ -159,22 +185,7 @@ const CodexOAuthSection = ({ codexStatus, onRefreshCodex }) => {
|
|
|
159
185
|
};
|
|
160
186
|
|
|
161
187
|
const completeAuth = async () => {
|
|
162
|
-
|
|
163
|
-
setExchanging(true);
|
|
164
|
-
try {
|
|
165
|
-
const result = await exchangeCodexOAuth(manualInput.trim());
|
|
166
|
-
if (!result.ok)
|
|
167
|
-
throw new Error(result.error || "Codex OAuth exchange failed");
|
|
168
|
-
setManualInput("");
|
|
169
|
-
showToast("Codex connected", "success");
|
|
170
|
-
setAuthStarted(false);
|
|
171
|
-
setAuthWaiting(false);
|
|
172
|
-
await onRefreshCodex();
|
|
173
|
-
} catch (err) {
|
|
174
|
-
showToast(err.message || "Codex OAuth exchange failed", "error");
|
|
175
|
-
} finally {
|
|
176
|
-
setExchanging(false);
|
|
177
|
-
}
|
|
188
|
+
await submitAuthInput(manualInput);
|
|
178
189
|
};
|
|
179
190
|
|
|
180
191
|
const handleDisconnect = async () => {
|
|
@@ -198,7 +209,23 @@ const CodexOAuthSection = ({ codexStatus, onRefreshCodex }) => {
|
|
|
198
209
|
? html`<${Badge} tone="success">Connected</${Badge}>`
|
|
199
210
|
: html`<${Badge} tone="warning">Not connected</${Badge}>`}
|
|
200
211
|
</div>
|
|
201
|
-
${
|
|
212
|
+
${authStarted
|
|
213
|
+
? html`
|
|
214
|
+
<div class="flex items-center justify-between gap-2">
|
|
215
|
+
<p class="text-xs text-fg-muted">
|
|
216
|
+
${authWaiting
|
|
217
|
+
? "Complete login in the popup. AlphaClaw should finish automatically, but you can paste the redirect URL below if it doesn't."
|
|
218
|
+
: "Paste the redirect URL from your browser to finish connecting."}
|
|
219
|
+
</p>
|
|
220
|
+
<button
|
|
221
|
+
onclick=${startAuth}
|
|
222
|
+
class="text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-secondary shrink-0"
|
|
223
|
+
>
|
|
224
|
+
Restart
|
|
225
|
+
</button>
|
|
226
|
+
</div>
|
|
227
|
+
`
|
|
228
|
+
: codexStatus.connected
|
|
202
229
|
? html`
|
|
203
230
|
<div class="flex gap-2">
|
|
204
231
|
<button
|
|
@@ -215,32 +242,16 @@ const CodexOAuthSection = ({ codexStatus, onRefreshCodex }) => {
|
|
|
215
242
|
</button>
|
|
216
243
|
</div>
|
|
217
244
|
`
|
|
218
|
-
:
|
|
245
|
+
: html`
|
|
246
|
+
<button
|
|
247
|
+
onclick=${startAuth}
|
|
248
|
+
class="text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-cyan"
|
|
249
|
+
>
|
|
250
|
+
Connect Codex OAuth
|
|
251
|
+
</button>
|
|
252
|
+
`}
|
|
253
|
+
${authStarted
|
|
219
254
|
? html`
|
|
220
|
-
<button
|
|
221
|
-
onclick=${startAuth}
|
|
222
|
-
class="text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-cyan"
|
|
223
|
-
>
|
|
224
|
-
Connect Codex OAuth
|
|
225
|
-
</button>
|
|
226
|
-
`
|
|
227
|
-
: html`
|
|
228
|
-
<div class="flex items-center justify-between gap-2">
|
|
229
|
-
<p class="text-xs text-fg-muted">
|
|
230
|
-
${authWaiting
|
|
231
|
-
? "Complete login in the popup, then paste the redirect URL."
|
|
232
|
-
: "Paste the redirect URL from your browser to finish connecting."}
|
|
233
|
-
</p>
|
|
234
|
-
<button
|
|
235
|
-
onclick=${startAuth}
|
|
236
|
-
class="text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-secondary shrink-0"
|
|
237
|
-
>
|
|
238
|
-
Restart
|
|
239
|
-
</button>
|
|
240
|
-
</div>
|
|
241
|
-
`}
|
|
242
|
-
${!codexStatus.connected && authStarted
|
|
243
|
-
? html`
|
|
244
255
|
<p class="text-xs text-fg-muted">
|
|
245
256
|
After login, copy the full redirect URL (starts with
|
|
246
257
|
<code class="text-xs bg-field px-1 rounded"
|