@chrysb/alphaclaw 0.2.2 → 0.3.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 +79 -0
- package/lib/public/css/shell.css +57 -2
- package/lib/public/css/theme.css +184 -0
- package/lib/public/js/app.js +330 -89
- package/lib/public/js/components/action-button.js +92 -0
- package/lib/public/js/components/channels.js +16 -7
- package/lib/public/js/components/confirm-dialog.js +25 -19
- package/lib/public/js/components/credentials-modal.js +32 -23
- package/lib/public/js/components/device-pairings.js +15 -2
- package/lib/public/js/components/envars.js +22 -65
- package/lib/public/js/components/features.js +1 -1
- package/lib/public/js/components/gateway.js +139 -32
- package/lib/public/js/components/global-restart-banner.js +31 -0
- package/lib/public/js/components/google.js +9 -9
- package/lib/public/js/components/icons.js +19 -0
- package/lib/public/js/components/info-tooltip.js +18 -0
- package/lib/public/js/components/loading-spinner.js +32 -0
- package/lib/public/js/components/modal-shell.js +42 -0
- package/lib/public/js/components/models.js +34 -29
- package/lib/public/js/components/onboarding/welcome-form-step.js +45 -32
- package/lib/public/js/components/onboarding/welcome-pairing-step.js +2 -2
- package/lib/public/js/components/onboarding/welcome-setup-step.js +7 -24
- package/lib/public/js/components/page-header.js +13 -0
- package/lib/public/js/components/pairings.js +15 -2
- package/lib/public/js/components/providers.js +216 -142
- package/lib/public/js/components/scope-picker.js +1 -1
- package/lib/public/js/components/secret-input.js +1 -0
- package/lib/public/js/components/telegram-workspace.js +37 -49
- package/lib/public/js/components/toast.js +34 -5
- package/lib/public/js/components/toggle-switch.js +25 -0
- package/lib/public/js/components/update-action-button.js +13 -53
- package/lib/public/js/components/watchdog-tab.js +312 -0
- package/lib/public/js/components/webhooks.js +981 -0
- package/lib/public/js/components/welcome.js +2 -1
- package/lib/public/js/lib/api.js +102 -1
- package/lib/public/js/lib/model-config.js +0 -5
- package/lib/public/login.html +1 -0
- package/lib/public/setup.html +1 -0
- package/lib/server/alphaclaw-version.js +5 -3
- package/lib/server/constants.js +33 -0
- package/lib/server/discord-api.js +48 -0
- package/lib/server/gateway.js +64 -4
- package/lib/server/log-writer.js +102 -0
- package/lib/server/onboarding/github.js +21 -1
- package/lib/server/openclaw-version.js +2 -6
- package/lib/server/restart-required-state.js +86 -0
- package/lib/server/routes/auth.js +9 -4
- package/lib/server/routes/proxy.js +12 -14
- package/lib/server/routes/system.js +61 -15
- package/lib/server/routes/telegram.js +17 -48
- package/lib/server/routes/watchdog.js +68 -0
- package/lib/server/routes/webhooks.js +214 -0
- package/lib/server/telegram-api.js +11 -0
- package/lib/server/watchdog-db.js +148 -0
- package/lib/server/watchdog-notify.js +93 -0
- package/lib/server/watchdog.js +585 -0
- package/lib/server/webhook-middleware.js +195 -0
- package/lib/server/webhooks-db.js +265 -0
- package/lib/server/webhooks.js +238 -0
- package/lib/server.js +119 -4
- package/lib/setup/core-prompts/AGENTS.md +84 -0
- package/lib/setup/core-prompts/TOOLS.md +13 -0
- package/lib/setup/core-prompts/UI-DRY-OPPORTUNITIES.md +50 -0
- package/lib/setup/gitignore +2 -0
- package/package.json +2 -1
|
@@ -65,13 +65,13 @@ export function Google({ gatewayStatus }) {
|
|
|
65
65
|
useEffect(() => {
|
|
66
66
|
const handler = async (e) => {
|
|
67
67
|
if (e.data?.google === "success") {
|
|
68
|
-
showToast("✓ Google account connected", "
|
|
68
|
+
showToast("✓ Google account connected", "success");
|
|
69
69
|
setApiStatus({});
|
|
70
70
|
await refresh();
|
|
71
71
|
} else if (e.data?.google === "error") {
|
|
72
72
|
showToast(
|
|
73
73
|
"✗ Google auth failed: " + (e.data.message || "unknown"),
|
|
74
|
-
"
|
|
74
|
+
"error",
|
|
75
75
|
);
|
|
76
76
|
}
|
|
77
77
|
};
|
|
@@ -111,7 +111,7 @@ export function Google({ gatewayStatus }) {
|
|
|
111
111
|
});
|
|
112
112
|
setApiStatus({});
|
|
113
113
|
setScopes(getDefaultScopes());
|
|
114
|
-
showToast("Google account disconnected", "
|
|
114
|
+
showToast("Google account disconnected", "success");
|
|
115
115
|
} else {
|
|
116
116
|
alert("Failed to disconnect: " + (data.error || "unknown"));
|
|
117
117
|
}
|
|
@@ -155,7 +155,7 @@ export function Google({ gatewayStatus }) {
|
|
|
155
155
|
${isAuthed &&
|
|
156
156
|
html`<button
|
|
157
157
|
onclick=${handleCheckApis}
|
|
158
|
-
class="text-xs
|
|
158
|
+
class="text-xs px-2 py-1 rounded-lg ac-btn-ghost"
|
|
159
159
|
>
|
|
160
160
|
↻ Check APIs
|
|
161
161
|
</button>`}
|
|
@@ -166,26 +166,26 @@ export function Google({ gatewayStatus }) {
|
|
|
166
166
|
apiStatus=${isAuthed ? apiStatus : {}}
|
|
167
167
|
loading=${isAuthed && checkingApis}
|
|
168
168
|
/>
|
|
169
|
-
<div class="flex justify-between items-center
|
|
170
|
-
<div class="
|
|
169
|
+
<div class="pt-1 space-y-2 sm:space-y-0 sm:flex sm:justify-between sm:items-center">
|
|
170
|
+
<div class="grid grid-cols-2 gap-2 w-full sm:w-auto sm:flex sm:items-center">
|
|
171
171
|
<button
|
|
172
172
|
onclick=${() => startAuth(email)}
|
|
173
173
|
disabled=${isAuthed && !scopesChanged}
|
|
174
|
-
class="text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-cyan"
|
|
174
|
+
class="w-full sm:w-auto text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-cyan"
|
|
175
175
|
>
|
|
176
176
|
${isAuthed ? "Update Permissions" : "Sign in with Google"}
|
|
177
177
|
</button>
|
|
178
178
|
<button
|
|
179
179
|
type="button"
|
|
180
180
|
onclick=${() => setModalOpen(true)}
|
|
181
|
-
class="text-xs font-medium px-3 py-1.5 rounded-lg
|
|
181
|
+
class="w-full sm:w-auto text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-secondary"
|
|
182
182
|
>
|
|
183
183
|
Edit credentials
|
|
184
184
|
</button>
|
|
185
185
|
</div>
|
|
186
186
|
<button
|
|
187
187
|
onclick=${() => setDisconnectDialogOpen(true)}
|
|
188
|
-
class="text-xs
|
|
188
|
+
class="text-xs px-2 py-1 rounded-lg ac-btn-ghost w-full sm:w-auto"
|
|
189
189
|
>
|
|
190
190
|
Disconnect
|
|
191
191
|
</button>
|
|
@@ -21,3 +21,22 @@ export const ChevronDownIcon = ({ className = "" }) => html`
|
|
|
21
21
|
/>
|
|
22
22
|
</svg>
|
|
23
23
|
`;
|
|
24
|
+
|
|
25
|
+
export const CloseIcon = ({ className = "" }) => html`
|
|
26
|
+
<svg
|
|
27
|
+
class=${className}
|
|
28
|
+
width="16"
|
|
29
|
+
height="16"
|
|
30
|
+
viewBox="0 0 16 16"
|
|
31
|
+
fill="none"
|
|
32
|
+
aria-hidden="true"
|
|
33
|
+
>
|
|
34
|
+
<path
|
|
35
|
+
d="M4 4L12 12M12 4L4 12"
|
|
36
|
+
stroke="currentColor"
|
|
37
|
+
stroke-width="1.5"
|
|
38
|
+
stroke-linecap="round"
|
|
39
|
+
stroke-linejoin="round"
|
|
40
|
+
/>
|
|
41
|
+
</svg>
|
|
42
|
+
`;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import htm from "https://esm.sh/htm";
|
|
3
|
+
|
|
4
|
+
const html = htm.bind(h);
|
|
5
|
+
|
|
6
|
+
export const InfoTooltip = ({ text = "", widthClass = "w-64" }) => html`
|
|
7
|
+
<span class="relative group inline-flex items-center cursor-default select-none">
|
|
8
|
+
<span
|
|
9
|
+
class="inline-flex h-4 w-4 items-center justify-center rounded-full border border-gray-500 text-[10px] text-gray-400 cursor-default"
|
|
10
|
+
aria-label=${text}
|
|
11
|
+
>?</span
|
|
12
|
+
>
|
|
13
|
+
<span
|
|
14
|
+
class=${`pointer-events-none absolute left-1/2 top-full z-10 mt-2 hidden -translate-x-1/2 rounded-md border border-border bg-modal px-2 py-1 text-[11px] text-gray-300 shadow-lg group-hover:block ${widthClass}`}
|
|
15
|
+
>${text}</span
|
|
16
|
+
>
|
|
17
|
+
</span>
|
|
18
|
+
`;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import htm from "https://esm.sh/htm";
|
|
3
|
+
|
|
4
|
+
const html = htm.bind(h);
|
|
5
|
+
|
|
6
|
+
export const LoadingSpinner = ({
|
|
7
|
+
className = "h-4 w-4",
|
|
8
|
+
ariaHidden = true,
|
|
9
|
+
style = "",
|
|
10
|
+
}) => html`
|
|
11
|
+
<svg
|
|
12
|
+
class=${`ac-spinner ${className}`.trim()}
|
|
13
|
+
viewBox="0 0 24 24"
|
|
14
|
+
fill="none"
|
|
15
|
+
aria-hidden=${ariaHidden ? "true" : "false"}
|
|
16
|
+
style=${style}
|
|
17
|
+
>
|
|
18
|
+
<circle
|
|
19
|
+
class="opacity-25"
|
|
20
|
+
cx="12"
|
|
21
|
+
cy="12"
|
|
22
|
+
r="10"
|
|
23
|
+
stroke="currentColor"
|
|
24
|
+
stroke-width="4"
|
|
25
|
+
/>
|
|
26
|
+
<path
|
|
27
|
+
class="opacity-75"
|
|
28
|
+
fill="currentColor"
|
|
29
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
|
|
30
|
+
/>
|
|
31
|
+
</svg>
|
|
32
|
+
`;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import { useEffect } from "https://esm.sh/preact/hooks";
|
|
3
|
+
import { createPortal } from "https://esm.sh/preact/compat";
|
|
4
|
+
import htm from "https://esm.sh/htm";
|
|
5
|
+
|
|
6
|
+
const html = htm.bind(h);
|
|
7
|
+
|
|
8
|
+
export const ModalShell = ({
|
|
9
|
+
visible = false,
|
|
10
|
+
onClose = () => {},
|
|
11
|
+
closeOnOverlayClick = true,
|
|
12
|
+
closeOnEscape = true,
|
|
13
|
+
panelClassName = "bg-modal border border-border rounded-xl p-5 max-w-md w-full space-y-3",
|
|
14
|
+
children = null,
|
|
15
|
+
}) => {
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!visible || !closeOnEscape) return;
|
|
18
|
+
|
|
19
|
+
const handleKeydown = (event) => {
|
|
20
|
+
if (event.key === "Escape") onClose?.();
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
window.addEventListener("keydown", handleKeydown);
|
|
24
|
+
return () => window.removeEventListener("keydown", handleKeydown);
|
|
25
|
+
}, [visible, closeOnEscape, onClose]);
|
|
26
|
+
|
|
27
|
+
if (!visible) return null;
|
|
28
|
+
|
|
29
|
+
return createPortal(
|
|
30
|
+
html`
|
|
31
|
+
<div
|
|
32
|
+
class="fixed inset-0 bg-black/70 flex items-center justify-center p-4 z-50"
|
|
33
|
+
onclick=${(event) => {
|
|
34
|
+
if (closeOnOverlayClick && event.target === event.currentTarget) onClose?.();
|
|
35
|
+
}}
|
|
36
|
+
>
|
|
37
|
+
<div class=${panelClassName}>${children}</div>
|
|
38
|
+
</div>
|
|
39
|
+
`,
|
|
40
|
+
document.body,
|
|
41
|
+
);
|
|
42
|
+
};
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
import { showToast } from "./toast.js";
|
|
15
15
|
import { Badge } from "./badge.js";
|
|
16
16
|
import { SecretInput } from "./secret-input.js";
|
|
17
|
+
import { LoadingSpinner } from "./loading-spinner.js";
|
|
18
|
+
import { ActionButton } from "./action-button.js";
|
|
17
19
|
import {
|
|
18
20
|
getModelProvider,
|
|
19
21
|
getAuthProviderFromModelProvider,
|
|
@@ -87,7 +89,7 @@ export const Models = () => {
|
|
|
87
89
|
};
|
|
88
90
|
} catch (err) {
|
|
89
91
|
setModelsError("Failed to load model settings");
|
|
90
|
-
showToast(`Failed to load model settings: ${err.message}`, "
|
|
92
|
+
showToast(`Failed to load model settings: ${err.message}`, "error");
|
|
91
93
|
} finally {
|
|
92
94
|
setReady(true);
|
|
93
95
|
setModelsLoading(false);
|
|
@@ -123,10 +125,10 @@ export const Models = () => {
|
|
|
123
125
|
useEffect(() => {
|
|
124
126
|
const onMessage = async (e) => {
|
|
125
127
|
if (e.data?.codex === "success") {
|
|
126
|
-
showToast("Codex connected", "
|
|
128
|
+
showToast("Codex connected", "success");
|
|
127
129
|
await refreshCodexConnection();
|
|
128
130
|
} else if (e.data?.codex === "error") {
|
|
129
|
-
showToast(`Codex auth failed: ${e.data.message || "unknown error"}`, "
|
|
131
|
+
showToast(`Codex auth failed: ${e.data.message || "unknown error"}`, "error");
|
|
130
132
|
}
|
|
131
133
|
};
|
|
132
134
|
window.addEventListener("message", onMessage);
|
|
@@ -145,7 +147,7 @@ export const Models = () => {
|
|
|
145
147
|
if (savingChanges) return;
|
|
146
148
|
if (!modelDirty && !aiCredentialsDirty) return;
|
|
147
149
|
if (modelDirty && !hasSelectedProviderAuth) {
|
|
148
|
-
showToast("Add credentials for the selected model provider before saving model changes", "
|
|
150
|
+
showToast("Add credentials for the selected model provider before saving model changes", "error");
|
|
149
151
|
return;
|
|
150
152
|
}
|
|
151
153
|
setSavingChanges(true);
|
|
@@ -176,10 +178,10 @@ export const Models = () => {
|
|
|
176
178
|
kModelsTabCache = { ...(kModelsTabCache || {}), selectedModel: targetModel, savedModel: targetModel };
|
|
177
179
|
}
|
|
178
180
|
|
|
179
|
-
showToast("Changes saved", "
|
|
181
|
+
showToast("Changes saved", "success");
|
|
180
182
|
await refresh();
|
|
181
183
|
} catch (err) {
|
|
182
|
-
showToast(err.message || "Failed to save changes", "
|
|
184
|
+
showToast(err.message || "Failed to save changes", "error");
|
|
183
185
|
} finally {
|
|
184
186
|
setSavingChanges(false);
|
|
185
187
|
}
|
|
@@ -214,12 +216,12 @@ export const Models = () => {
|
|
|
214
216
|
const result = await exchangeCodexOAuth(codexManualInput.trim());
|
|
215
217
|
if (!result.ok) throw new Error(result.error || "Codex OAuth exchange failed");
|
|
216
218
|
setCodexManualInput("");
|
|
217
|
-
showToast("Codex connected", "
|
|
219
|
+
showToast("Codex connected", "success");
|
|
218
220
|
setCodexAuthStarted(false);
|
|
219
221
|
setCodexAuthWaiting(false);
|
|
220
222
|
await refreshCodexConnection();
|
|
221
223
|
} catch (err) {
|
|
222
|
-
showToast(err.message || "Codex OAuth exchange failed", "
|
|
224
|
+
showToast(err.message || "Codex OAuth exchange failed", "error");
|
|
223
225
|
} finally {
|
|
224
226
|
setCodexExchanging(false);
|
|
225
227
|
}
|
|
@@ -228,10 +230,10 @@ export const Models = () => {
|
|
|
228
230
|
const handleCodexDisconnect = async () => {
|
|
229
231
|
const result = await disconnectCodex();
|
|
230
232
|
if (!result.ok) {
|
|
231
|
-
showToast(result.error || "Failed to disconnect Codex", "
|
|
233
|
+
showToast(result.error || "Failed to disconnect Codex", "error");
|
|
232
234
|
return;
|
|
233
235
|
}
|
|
234
|
-
showToast("Codex disconnected", "
|
|
236
|
+
showToast("Codex disconnected", "success");
|
|
235
237
|
setCodexAuthStarted(false);
|
|
236
238
|
setCodexAuthWaiting(false);
|
|
237
239
|
setCodexManualInput("");
|
|
@@ -305,13 +307,13 @@ export const Models = () => {
|
|
|
305
307
|
<div class="flex gap-2">
|
|
306
308
|
<button
|
|
307
309
|
onclick=${startCodexAuth}
|
|
308
|
-
class="text-xs font-medium px-3 py-1.5 rounded-lg
|
|
310
|
+
class="text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-secondary"
|
|
309
311
|
>
|
|
310
312
|
Reconnect Codex
|
|
311
313
|
</button>
|
|
312
314
|
<button
|
|
313
315
|
onclick=${handleCodexDisconnect}
|
|
314
|
-
class="text-xs font-medium px-3 py-1.5 rounded-lg
|
|
316
|
+
class="text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-ghost"
|
|
315
317
|
>
|
|
316
318
|
Disconnect
|
|
317
319
|
</button>
|
|
@@ -335,7 +337,7 @@ export const Models = () => {
|
|
|
335
337
|
</p>
|
|
336
338
|
<button
|
|
337
339
|
onclick=${startCodexAuth}
|
|
338
|
-
class="text-xs font-medium px-3 py-1.5 rounded-lg
|
|
340
|
+
class="text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-secondary shrink-0"
|
|
339
341
|
>
|
|
340
342
|
Restart
|
|
341
343
|
</button>
|
|
@@ -355,13 +357,16 @@ export const Models = () => {
|
|
|
355
357
|
placeholder="http://localhost:1455/auth/callback?code=...&state=..."
|
|
356
358
|
class="w-full bg-black/30 border border-border rounded-lg px-3 py-2 text-xs text-gray-200 outline-none focus:border-gray-500"
|
|
357
359
|
/>
|
|
358
|
-
|
|
359
|
-
|
|
360
|
+
<${ActionButton}
|
|
361
|
+
onClick=${completeCodexAuth}
|
|
360
362
|
disabled=${!codexManualInput.trim() || codexExchanging}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
363
|
+
loading=${codexExchanging}
|
|
364
|
+
tone="primary"
|
|
365
|
+
size="sm"
|
|
366
|
+
idleLabel="Complete Codex OAuth"
|
|
367
|
+
loadingLabel="Completing..."
|
|
368
|
+
className="text-xs font-medium px-3 py-1.5"
|
|
369
|
+
/>
|
|
365
370
|
`
|
|
366
371
|
: null}
|
|
367
372
|
</div>
|
|
@@ -373,10 +378,7 @@ export const Models = () => {
|
|
|
373
378
|
return html`
|
|
374
379
|
<div class="bg-surface border border-border rounded-xl p-4">
|
|
375
380
|
<div class="flex items-center gap-2 text-sm text-gray-400">
|
|
376
|
-
|
|
377
|
-
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
|
|
378
|
-
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
|
379
|
-
</svg>
|
|
381
|
+
<${LoadingSpinner} className="h-4 w-4" />
|
|
380
382
|
Loading model settings...
|
|
381
383
|
</div>
|
|
382
384
|
</div>
|
|
@@ -440,13 +442,16 @@ export const Models = () => {
|
|
|
440
442
|
)}
|
|
441
443
|
</div>
|
|
442
444
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
+
<${ActionButton}
|
|
446
|
+
onClick=${saveChanges}
|
|
445
447
|
disabled=${!canSaveChanges}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
448
|
+
loading=${savingChanges}
|
|
449
|
+
tone="primary"
|
|
450
|
+
size="md"
|
|
451
|
+
idleLabel="Save changes"
|
|
452
|
+
loadingLabel="Saving..."
|
|
453
|
+
className="w-full py-2.5 transition-all"
|
|
454
|
+
/>
|
|
450
455
|
${modelDirty && !hasSelectedProviderAuth
|
|
451
456
|
? html`
|
|
452
457
|
<p class="text-xs text-yellow-500">
|
|
@@ -2,6 +2,8 @@ import { h } from "https://esm.sh/preact";
|
|
|
2
2
|
import { useEffect, useState } from "https://esm.sh/preact/hooks";
|
|
3
3
|
import htm from "https://esm.sh/htm";
|
|
4
4
|
import { SecretInput } from "../secret-input.js";
|
|
5
|
+
import { ActionButton } from "../action-button.js";
|
|
6
|
+
import { Badge } from "../badge.js";
|
|
5
7
|
import { isValidGithubRepoInput } from "./welcome-config.js";
|
|
6
8
|
|
|
7
9
|
const html = htm.bind(h);
|
|
@@ -41,6 +43,8 @@ export const WelcomeFormStep = ({
|
|
|
41
43
|
handleSubmit,
|
|
42
44
|
}) => {
|
|
43
45
|
const [repoTouched, setRepoTouched] = useState(false);
|
|
46
|
+
const [showOptionalOpenai, setShowOptionalOpenai] = useState(false);
|
|
47
|
+
const [showOptionalGemini, setShowOptionalGemini] = useState(false);
|
|
44
48
|
|
|
45
49
|
useEffect(() => {
|
|
46
50
|
if (activeGroup.id !== "github") {
|
|
@@ -48,6 +52,13 @@ export const WelcomeFormStep = ({
|
|
|
48
52
|
}
|
|
49
53
|
}, [activeGroup.id]);
|
|
50
54
|
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (step === totalGroups - 1) {
|
|
57
|
+
setShowOptionalOpenai(!vals.OPENAI_API_KEY);
|
|
58
|
+
setShowOptionalGemini(!vals.GEMINI_API_KEY);
|
|
59
|
+
}
|
|
60
|
+
}, [step === totalGroups - 1]);
|
|
61
|
+
|
|
51
62
|
return html`
|
|
52
63
|
<div class="flex items-center justify-between">
|
|
53
64
|
<div>
|
|
@@ -113,30 +124,30 @@ export const WelcomeFormStep = ({
|
|
|
113
124
|
${codexLoading
|
|
114
125
|
? html`<span class="text-xs text-gray-500">Checking...</span>`
|
|
115
126
|
: codexStatus.connected
|
|
116
|
-
? html
|
|
117
|
-
: html
|
|
118
|
-
>Not connected</span
|
|
119
|
-
>`}
|
|
127
|
+
? html`<${Badge} tone="success">Connected</${Badge}>`
|
|
128
|
+
: html`<${Badge} tone="warning">Not connected</${Badge}>`}
|
|
120
129
|
</div>
|
|
121
130
|
<div class="flex gap-2">
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
+
<${ActionButton}
|
|
132
|
+
onClick=${startCodexAuth}
|
|
133
|
+
tone=${codexStatus.connected || codexAuthStarted
|
|
134
|
+
? "neutral"
|
|
135
|
+
: "primary"}
|
|
136
|
+
size="sm"
|
|
137
|
+
idleLabel=${codexStatus.connected
|
|
138
|
+
? "Reconnect Codex"
|
|
139
|
+
: "Connect Codex OAuth"}
|
|
140
|
+
className="font-medium"
|
|
141
|
+
/>
|
|
131
142
|
${codexStatus.connected &&
|
|
132
143
|
html`
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
144
|
+
<${ActionButton}
|
|
145
|
+
onClick=${handleCodexDisconnect}
|
|
146
|
+
tone="ghost"
|
|
147
|
+
size="sm"
|
|
148
|
+
idleLabel="Disconnect"
|
|
149
|
+
className="font-medium"
|
|
150
|
+
/>
|
|
140
151
|
`}
|
|
141
152
|
</div>
|
|
142
153
|
${!codexStatus.connected &&
|
|
@@ -158,14 +169,16 @@ export const WelcomeFormStep = ({
|
|
|
158
169
|
placeholder="http://localhost:1455/auth/callback?code=...&state=..."
|
|
159
170
|
class="w-full bg-black/30 border border-border rounded-lg px-3 py-2 text-xs text-gray-200 outline-none focus:border-gray-500"
|
|
160
171
|
/>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
onclick=${completeCodexAuth}
|
|
172
|
+
<${ActionButton}
|
|
173
|
+
onClick=${completeCodexAuth}
|
|
164
174
|
disabled=${!codexManualInput.trim() || codexExchanging}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
175
|
+
loading=${codexExchanging}
|
|
176
|
+
tone="primary"
|
|
177
|
+
size="sm"
|
|
178
|
+
idleLabel="Complete Codex OAuth"
|
|
179
|
+
loadingLabel="Completing..."
|
|
180
|
+
className="font-medium"
|
|
181
|
+
/>
|
|
169
182
|
</div>
|
|
170
183
|
`}
|
|
171
184
|
</div>
|
|
@@ -211,9 +224,9 @@ export const WelcomeFormStep = ({
|
|
|
211
224
|
${error}
|
|
212
225
|
</div>`
|
|
213
226
|
: null}
|
|
214
|
-
${step === totalGroups - 1 && (
|
|
227
|
+
${step === totalGroups - 1 && (showOptionalOpenai || showOptionalGemini)
|
|
215
228
|
? html`
|
|
216
|
-
${
|
|
229
|
+
${showOptionalOpenai
|
|
217
230
|
? html`<div class="space-y-1">
|
|
218
231
|
<label class="text-xs font-medium text-gray-400"
|
|
219
232
|
>OpenAI API Key</label
|
|
@@ -237,7 +250,7 @@ export const WelcomeFormStep = ({
|
|
|
237
250
|
</p>
|
|
238
251
|
</div>`
|
|
239
252
|
: null}
|
|
240
|
-
${
|
|
253
|
+
${showOptionalGemini
|
|
241
254
|
? html`<div class="space-y-1">
|
|
242
255
|
<label class="text-xs font-medium text-gray-400"
|
|
243
256
|
>Gemini API Key</label
|
|
@@ -270,7 +283,7 @@ export const WelcomeFormStep = ({
|
|
|
270
283
|
${step > 0
|
|
271
284
|
? html`<button
|
|
272
285
|
onclick=${goBack}
|
|
273
|
-
class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all
|
|
286
|
+
class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all ac-btn-secondary"
|
|
274
287
|
>
|
|
275
288
|
Back
|
|
276
289
|
</button>`
|
|
@@ -289,7 +302,7 @@ export const WelcomeFormStep = ({
|
|
|
289
302
|
${step > 0
|
|
290
303
|
? html`<button
|
|
291
304
|
onclick=${goBack}
|
|
292
|
-
class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all
|
|
305
|
+
class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all ac-btn-secondary"
|
|
293
306
|
>
|
|
294
307
|
Back
|
|
295
308
|
</button>`
|
|
@@ -54,14 +54,14 @@ const PairingRow = ({ pairing, onApprove, onReject }) => {
|
|
|
54
54
|
<button
|
|
55
55
|
onclick=${handleApprove}
|
|
56
56
|
disabled=${!!busyAction}
|
|
57
|
-
class="ac-btn-green text-xs font-medium px-3 py-1.5 rounded-lg ${busyAction ? "opacity-
|
|
57
|
+
class="ac-btn-green text-xs font-medium px-3 py-1.5 rounded-lg ${busyAction ? "opacity-50 cursor-not-allowed" : ""}"
|
|
58
58
|
>
|
|
59
59
|
${busyAction === "approve" ? "Approving..." : "Approve"}
|
|
60
60
|
</button>
|
|
61
61
|
<button
|
|
62
62
|
onclick=${handleReject}
|
|
63
63
|
disabled=${!!busyAction}
|
|
64
|
-
class="
|
|
64
|
+
class="ac-btn-secondary text-xs font-medium px-3 py-1.5 rounded-lg ${busyAction ? "opacity-50 cursor-not-allowed" : ""}"
|
|
65
65
|
>
|
|
66
66
|
${busyAction === "reject" ? "Rejecting..." : "Reject"}
|
|
67
67
|
</button>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { h } from "https://esm.sh/preact";
|
|
2
2
|
import { useEffect, useState } from "https://esm.sh/preact/hooks";
|
|
3
3
|
import htm from "https://esm.sh/htm";
|
|
4
|
+
import { LoadingSpinner } from "../loading-spinner.js";
|
|
4
5
|
|
|
5
6
|
const html = htm.bind(h);
|
|
6
7
|
const kSetupTips = [
|
|
@@ -56,8 +57,8 @@ export const WelcomeSetupStep = ({ error, loading, onRetry, onBack }) => {
|
|
|
56
57
|
<button
|
|
57
58
|
onclick=${onBack}
|
|
58
59
|
disabled=${loading}
|
|
59
|
-
class="w-full text-sm font-medium px-4 py-3 rounded-xl transition-all
|
|
60
|
-
? "opacity-
|
|
60
|
+
class="w-full text-sm font-medium px-4 py-3 rounded-xl transition-all ac-btn-secondary ${loading
|
|
61
|
+
? "opacity-50 cursor-not-allowed"
|
|
61
62
|
: ""}"
|
|
62
63
|
>
|
|
63
64
|
Back
|
|
@@ -65,9 +66,9 @@ export const WelcomeSetupStep = ({ error, loading, onRetry, onBack }) => {
|
|
|
65
66
|
<button
|
|
66
67
|
onclick=${onRetry}
|
|
67
68
|
disabled=${loading}
|
|
68
|
-
class="w-full text-sm font-medium px-4 py-3 rounded-xl transition-all ${loading
|
|
69
|
-
? "
|
|
70
|
-
: "
|
|
69
|
+
class="w-full text-sm font-medium px-4 py-3 rounded-xl transition-all ac-btn-cyan ${loading
|
|
70
|
+
? "opacity-50 cursor-not-allowed"
|
|
71
|
+
: ""}"
|
|
71
72
|
>
|
|
72
73
|
${loading ? "Retrying..." : "Retry"}
|
|
73
74
|
</button>
|
|
@@ -82,25 +83,7 @@ export const WelcomeSetupStep = ({ error, loading, onRetry, onBack }) => {
|
|
|
82
83
|
<div
|
|
83
84
|
class="flex-1 flex flex-col items-center justify-center text-center gap-4"
|
|
84
85
|
>
|
|
85
|
-
|
|
86
|
-
class="animate-spin h-8 w-8 text-white"
|
|
87
|
-
viewBox="0 0 24 24"
|
|
88
|
-
fill="none"
|
|
89
|
-
>
|
|
90
|
-
<circle
|
|
91
|
-
class="opacity-25"
|
|
92
|
-
cx="12"
|
|
93
|
-
cy="12"
|
|
94
|
-
r="10"
|
|
95
|
-
stroke="currentColor"
|
|
96
|
-
stroke-width="4"
|
|
97
|
-
/>
|
|
98
|
-
<path
|
|
99
|
-
class="opacity-75"
|
|
100
|
-
fill="currentColor"
|
|
101
|
-
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
|
|
102
|
-
/>
|
|
103
|
-
</svg>
|
|
86
|
+
<${LoadingSpinner} className="h-8 w-8 text-white" />
|
|
104
87
|
<h3 class="text-lg font-semibold text-white">
|
|
105
88
|
Initializing OpenClaw...
|
|
106
89
|
</h3>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import htm from "https://esm.sh/htm";
|
|
3
|
+
|
|
4
|
+
const html = htm.bind(h);
|
|
5
|
+
|
|
6
|
+
export const PageHeader = ({ title = "", actions = null, leading = null }) => html`
|
|
7
|
+
<div class="flex items-center justify-between gap-3">
|
|
8
|
+
<div>
|
|
9
|
+
${leading || html`<h2 class="font-semibold text-base">${title}</h2>`}
|
|
10
|
+
</div>
|
|
11
|
+
<div class="flex items-center gap-2">${actions}</div>
|
|
12
|
+
</div>
|
|
13
|
+
`;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { h } from 'https://esm.sh/preact';
|
|
2
2
|
import { useState } from 'https://esm.sh/preact/hooks';
|
|
3
3
|
import htm from 'https://esm.sh/htm';
|
|
4
|
+
import { ActionButton } from './action-button.js';
|
|
4
5
|
const html = htm.bind(h);
|
|
5
6
|
|
|
6
7
|
const PairingRow = ({ p, onApprove, onReject }) => {
|
|
@@ -37,8 +38,20 @@ const PairingRow = ({ p, onApprove, onReject }) => {
|
|
|
37
38
|
<div class="bg-black/30 rounded-lg p-3 mb-2">
|
|
38
39
|
<div class="font-medium text-sm mb-2">${label} · <code class="text-gray-400">${p.code || p.id || '?'}</code></div>
|
|
39
40
|
<div class="flex gap-2">
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
<${ActionButton}
|
|
42
|
+
onClick=${() => handle("approve")}
|
|
43
|
+
tone="success"
|
|
44
|
+
size="sm"
|
|
45
|
+
idleLabel="Approve"
|
|
46
|
+
className="font-medium px-3 py-1.5"
|
|
47
|
+
/>
|
|
48
|
+
<${ActionButton}
|
|
49
|
+
onClick=${() => handle("reject")}
|
|
50
|
+
tone="secondary"
|
|
51
|
+
size="sm"
|
|
52
|
+
idleLabel="Reject"
|
|
53
|
+
className="font-medium px-3 py-1.5"
|
|
54
|
+
/>
|
|
42
55
|
</div>
|
|
43
56
|
</div>`;
|
|
44
57
|
};
|