@chrysb/alphaclaw 0.1.19 → 0.1.21
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 +30 -8
- package/lib/public/assets/icons/discord.svg +7 -0
- package/lib/public/assets/icons/telegram.svg +13 -0
- package/lib/public/css/shell.css +51 -3
- package/lib/public/css/theme.css +34 -5
- package/lib/public/js/app.js +57 -12
- package/lib/public/js/components/channels.js +11 -1
- package/lib/public/js/components/envars.js +45 -14
- package/lib/public/js/components/models.js +5 -4
- package/lib/public/js/components/onboarding/pairing-utils.js +12 -0
- package/lib/public/js/components/onboarding/welcome-config.js +18 -1
- package/lib/public/js/components/onboarding/welcome-form-step.js +246 -222
- package/lib/public/js/components/onboarding/welcome-header.js +52 -41
- package/lib/public/js/components/onboarding/welcome-pairing-step.js +173 -0
- package/lib/public/js/components/onboarding/welcome-setup-step.js +108 -36
- package/lib/public/js/components/secret-input.js +2 -0
- package/lib/public/js/components/welcome.js +151 -18
- package/lib/public/js/lib/api.js +24 -1
- package/lib/public/js/lib/model-config.js +29 -3
- package/lib/public/login.html +6 -15
- package/lib/server/routes/auth.js +61 -9
- package/lib/server/routes/google.js +0 -1
- package/lib/server/routes/proxy.js +4 -4
- package/lib/server/routes/system.js +36 -11
- package/lib/server.js +20 -2
- package/lib/setup/core-prompts/AGENTS.md +11 -0
- package/lib/setup/core-prompts/TOOLS.md +22 -4
- package/lib/setup/skills/control-ui/SKILL.md +8 -8
- package/package.json +1 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import { useEffect, useState } from "https://esm.sh/preact/hooks";
|
|
2
3
|
import htm from "https://esm.sh/htm";
|
|
3
4
|
import { SecretInput } from "../secret-input.js";
|
|
5
|
+
import { isValidGithubRepoInput } from "./welcome-config.js";
|
|
4
6
|
|
|
5
7
|
const html = htm.bind(h);
|
|
6
8
|
|
|
@@ -36,245 +38,267 @@ export const WelcomeFormStep = ({
|
|
|
36
38
|
loading,
|
|
37
39
|
allValid,
|
|
38
40
|
handleSubmit,
|
|
39
|
-
}) =>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
}) => {
|
|
42
|
+
const [repoTouched, setRepoTouched] = useState(false);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (activeGroup.id !== "github") {
|
|
46
|
+
setRepoTouched(false);
|
|
47
|
+
}
|
|
48
|
+
}, [activeGroup.id]);
|
|
49
|
+
|
|
50
|
+
return html`
|
|
51
|
+
<div class="flex items-center justify-between">
|
|
52
|
+
<div>
|
|
53
|
+
<h2 class="text-sm font-medium text-gray-200">${activeGroup.title}</h2>
|
|
54
|
+
<p class="text-xs text-gray-500">${activeGroup.description}</p>
|
|
55
|
+
</div>
|
|
56
|
+
${activeGroup.validate(vals, { hasAi })
|
|
51
57
|
? html`<span
|
|
52
|
-
class="text-xs font-medium px-2 py-0.5 rounded-full bg-
|
|
53
|
-
|
|
58
|
+
class="text-xs font-medium px-2 py-0.5 rounded-full bg-green-900/50 text-green-400"
|
|
59
|
+
>✓</span
|
|
54
60
|
>`
|
|
55
|
-
:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<label class="text-xs font-medium text-gray-400">Model</label>
|
|
62
|
-
<select
|
|
63
|
-
value=${vals.MODEL_KEY || ""}
|
|
64
|
-
onInput=${(e) => setValue("MODEL_KEY", e.target.value)}
|
|
65
|
-
class="w-full bg-black/30 border border-border rounded-lg pl-3 pr-8 py-2 text-sm text-gray-200 outline-none focus:border-gray-500"
|
|
66
|
-
>
|
|
67
|
-
<option value="">Select a model</option>
|
|
68
|
-
${modelOptions.map(
|
|
69
|
-
(model) => html`
|
|
70
|
-
<option value=${model.key}>${model.label || model.key}</option>
|
|
71
|
-
`,
|
|
72
|
-
)}
|
|
73
|
-
</select>
|
|
74
|
-
<p class="text-xs text-gray-600">
|
|
75
|
-
${modelsLoading
|
|
76
|
-
? "Loading model catalog..."
|
|
77
|
-
: modelsError
|
|
78
|
-
? modelsError
|
|
79
|
-
: ""}
|
|
80
|
-
</p>
|
|
81
|
-
${canToggleFullCatalog &&
|
|
82
|
-
html`
|
|
83
|
-
<button
|
|
84
|
-
type="button"
|
|
85
|
-
onclick=${() => setShowAllModels((prev) => !prev)}
|
|
86
|
-
class="text-xs text-gray-500 hover:text-gray-300"
|
|
87
|
-
>
|
|
88
|
-
${showAllModels
|
|
89
|
-
? "Show recommended models"
|
|
90
|
-
: "Show full model catalog"}
|
|
91
|
-
</button>
|
|
92
|
-
`}
|
|
61
|
+
: activeGroup.id !== "tools"
|
|
62
|
+
? html`<span
|
|
63
|
+
class="text-xs font-medium px-2 py-0.5 rounded-full bg-yellow-900/50 text-yellow-400"
|
|
64
|
+
>Required</span
|
|
65
|
+
>`
|
|
66
|
+
: null}
|
|
93
67
|
</div>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
: codexStatus.connected
|
|
104
|
-
? html`<span class="text-xs text-green-400">Connected</span>`
|
|
105
|
-
: html`<span class="text-xs text-yellow-400">Not connected</span>`}
|
|
106
|
-
</div>
|
|
107
|
-
<div class="flex gap-2">
|
|
108
|
-
<button
|
|
109
|
-
type="button"
|
|
110
|
-
onclick=${startCodexAuth}
|
|
111
|
-
class="text-xs font-medium px-3 py-1.5 rounded-lg ${codexStatus.connected
|
|
112
|
-
? "border border-border text-gray-300 hover:border-gray-500"
|
|
113
|
-
: "ac-btn-cyan"}"
|
|
68
|
+
|
|
69
|
+
${activeGroup.id === "ai" &&
|
|
70
|
+
html`
|
|
71
|
+
<div class="space-y-1">
|
|
72
|
+
<label class="text-xs font-medium text-gray-400">Model</label>
|
|
73
|
+
<select
|
|
74
|
+
value=${vals.MODEL_KEY || ""}
|
|
75
|
+
onInput=${(e) => setValue("MODEL_KEY", e.target.value)}
|
|
76
|
+
class="w-full bg-black/30 border border-border rounded-lg pl-3 pr-8 py-2 text-sm text-gray-200 outline-none focus:border-gray-500"
|
|
114
77
|
>
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
78
|
+
<option value="">Select a model</option>
|
|
79
|
+
${modelOptions.map(
|
|
80
|
+
(model) => html`
|
|
81
|
+
<option value=${model.key}>${model.label || model.key}</option>
|
|
82
|
+
`,
|
|
83
|
+
)}
|
|
84
|
+
</select>
|
|
85
|
+
<p class="text-xs text-gray-600">
|
|
86
|
+
${modelsLoading
|
|
87
|
+
? "Loading model catalog..."
|
|
88
|
+
: modelsError
|
|
89
|
+
? modelsError
|
|
90
|
+
: ""}
|
|
91
|
+
</p>
|
|
92
|
+
${canToggleFullCatalog &&
|
|
118
93
|
html`
|
|
119
94
|
<button
|
|
120
95
|
type="button"
|
|
121
|
-
onclick=${
|
|
122
|
-
class="text-xs
|
|
96
|
+
onclick=${() => setShowAllModels((prev) => !prev)}
|
|
97
|
+
class="text-xs text-gray-500 hover:text-gray-300"
|
|
123
98
|
>
|
|
124
|
-
|
|
99
|
+
${showAllModels
|
|
100
|
+
? "Show recommended models"
|
|
101
|
+
: "Show full model catalog"}
|
|
125
102
|
</button>
|
|
126
103
|
`}
|
|
127
104
|
</div>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
placeholder="http://localhost:1455/auth/callback?code=...&state=..."
|
|
145
|
-
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"
|
|
146
|
-
/>
|
|
105
|
+
`}
|
|
106
|
+
${activeGroup.id === "ai" &&
|
|
107
|
+
selectedProvider === "openai-codex" &&
|
|
108
|
+
html`
|
|
109
|
+
<div class="bg-black/20 border border-border rounded-lg p-3 space-y-2">
|
|
110
|
+
<div class="flex items-center justify-between">
|
|
111
|
+
<span class="text-xs text-gray-400">Codex OAuth</span>
|
|
112
|
+
${codexLoading
|
|
113
|
+
? html`<span class="text-xs text-gray-500">Checking...</span>`
|
|
114
|
+
: codexStatus.connected
|
|
115
|
+
? html`<span class="text-xs text-green-400">Connected</span>`
|
|
116
|
+
: html`<span class="text-xs text-yellow-400"
|
|
117
|
+
>Not connected</span
|
|
118
|
+
>`}
|
|
119
|
+
</div>
|
|
120
|
+
<div class="flex gap-2">
|
|
147
121
|
<button
|
|
148
122
|
type="button"
|
|
149
|
-
onclick=${
|
|
150
|
-
|
|
151
|
-
|
|
123
|
+
onclick=${startCodexAuth}
|
|
124
|
+
class="text-xs font-medium px-3 py-1.5 rounded-lg ${codexStatus.connected
|
|
125
|
+
? "border border-border text-gray-300 hover:border-gray-500"
|
|
126
|
+
: "ac-btn-cyan"}"
|
|
152
127
|
>
|
|
153
|
-
${
|
|
128
|
+
${codexStatus.connected ? "Reconnect Codex" : "Connect Codex OAuth"}
|
|
154
129
|
</button>
|
|
130
|
+
${codexStatus.connected &&
|
|
131
|
+
html`
|
|
132
|
+
<button
|
|
133
|
+
type="button"
|
|
134
|
+
onclick=${handleCodexDisconnect}
|
|
135
|
+
class="text-xs font-medium px-3 py-1.5 rounded-lg border border-border text-gray-300 hover:border-gray-500"
|
|
136
|
+
>
|
|
137
|
+
Disconnect
|
|
138
|
+
</button>
|
|
139
|
+
`}
|
|
155
140
|
</div>
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
141
|
+
${!codexStatus.connected &&
|
|
142
|
+
codexAuthStarted &&
|
|
143
|
+
html`
|
|
144
|
+
<div class="space-y-1 pt-1">
|
|
145
|
+
<p class="text-xs text-gray-500">
|
|
146
|
+
${codexAuthWaiting
|
|
147
|
+
? "Complete login in the popup, then paste the full redirect URL from the address bar (starts with "
|
|
148
|
+
: "Paste the full redirect URL from the address bar (starts with "}
|
|
149
|
+
<code class="text-xs bg-black/30 px-1 rounded"
|
|
150
|
+
>http://localhost:1455/auth/callback</code
|
|
151
|
+
>) ${codexAuthWaiting ? " to finish setup." : " to finish setup."}
|
|
152
|
+
</p>
|
|
153
|
+
<input
|
|
154
|
+
type="text"
|
|
155
|
+
value=${codexManualInput}
|
|
156
|
+
onInput=${(e) => setCodexManualInput(e.target.value)}
|
|
157
|
+
placeholder="http://localhost:1455/auth/callback?code=...&state=..."
|
|
158
|
+
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"
|
|
159
|
+
/>
|
|
160
|
+
<button
|
|
161
|
+
type="button"
|
|
162
|
+
onclick=${completeCodexAuth}
|
|
163
|
+
disabled=${!codexManualInput.trim() || codexExchanging}
|
|
164
|
+
class="text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-cyan"
|
|
165
|
+
>
|
|
166
|
+
${codexExchanging ? "Completing..." : "Complete Codex OAuth"}
|
|
167
|
+
</button>
|
|
168
|
+
</div>
|
|
169
|
+
`}
|
|
175
170
|
</div>
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
171
|
+
`}
|
|
172
|
+
${(activeGroup.id === "ai"
|
|
173
|
+
? activeGroup.fields.filter((field) => visibleAiFieldKeys.has(field.key))
|
|
174
|
+
: activeGroup.fields
|
|
175
|
+
).map(
|
|
176
|
+
(field) => html`
|
|
177
|
+
<div class="space-y-1" key=${field.key}>
|
|
178
|
+
<label class="text-xs font-medium text-gray-400"
|
|
179
|
+
>${field.label}</label
|
|
180
|
+
>
|
|
181
|
+
<${SecretInput}
|
|
182
|
+
key=${field.key}
|
|
183
|
+
value=${vals[field.key] || ""}
|
|
184
|
+
onInput=${(e) => setValue(field.key, e.target.value)}
|
|
185
|
+
onBlur=${field.key === "GITHUB_WORKSPACE_REPO"
|
|
186
|
+
? () => setRepoTouched(true)
|
|
187
|
+
: undefined}
|
|
188
|
+
placeholder=${field.placeholder || ""}
|
|
189
|
+
isSecret=${!field.isText}
|
|
190
|
+
inputClass="flex-1 bg-black/30 border border-border rounded-lg px-3 py-2 text-sm text-gray-200 outline-none focus:border-gray-500 font-mono"
|
|
191
|
+
/>
|
|
192
|
+
<p class="text-xs text-gray-600">${field.hint}</p>
|
|
193
|
+
</div>
|
|
194
|
+
`,
|
|
195
|
+
)}
|
|
196
|
+
${activeGroup.id === "github" &&
|
|
197
|
+
repoTouched &&
|
|
198
|
+
vals.GITHUB_WORKSPACE_REPO &&
|
|
199
|
+
!isValidGithubRepoInput(vals.GITHUB_WORKSPACE_REPO)
|
|
200
|
+
? html`<div class="text-xs text-red-300">
|
|
201
|
+
Workspace Repo must be in
|
|
202
|
+
<code class="text-xs bg-black/30 px-1 rounded">owner/repo</code>
|
|
203
|
+
format.
|
|
204
|
+
</div>`
|
|
205
|
+
: null}
|
|
206
|
+
${error
|
|
207
|
+
? html`<div
|
|
208
|
+
class="bg-red-900/30 border border-red-800 rounded-xl p-3 text-red-300 text-sm"
|
|
209
|
+
>
|
|
210
|
+
${error}
|
|
211
|
+
</div>`
|
|
212
|
+
: null}
|
|
213
|
+
${step === totalGroups - 1 && (!vals.OPENAI_API_KEY || !vals.GEMINI_API_KEY)
|
|
214
|
+
? html`
|
|
215
|
+
${!vals.OPENAI_API_KEY
|
|
216
|
+
? html`<div class="space-y-1">
|
|
217
|
+
<label class="text-xs font-medium text-gray-400"
|
|
218
|
+
>OpenAI API Key</label
|
|
207
219
|
>
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
>
|
|
220
|
+
<${SecretInput}
|
|
221
|
+
value=${vals.OPENAI_API_KEY || ""}
|
|
222
|
+
onInput=${(e) => setValue("OPENAI_API_KEY", e.target.value)}
|
|
223
|
+
placeholder="sk-..."
|
|
224
|
+
isSecret=${true}
|
|
225
|
+
inputClass="flex-1 bg-black/30 border border-border rounded-lg px-3 py-2 text-sm text-gray-200 outline-none focus:border-gray-500 font-mono"
|
|
226
|
+
/>
|
|
227
|
+
<p class="text-xs text-gray-600">
|
|
228
|
+
Used for memory embeddings -${" "}
|
|
229
|
+
<a
|
|
230
|
+
href="https://platform.openai.com"
|
|
231
|
+
target="_blank"
|
|
232
|
+
class="hover:underline"
|
|
233
|
+
style="color: var(--accent-link)"
|
|
234
|
+
>get key</a
|
|
235
|
+
>
|
|
236
|
+
</p>
|
|
237
|
+
</div>`
|
|
238
|
+
: null}
|
|
239
|
+
${!vals.GEMINI_API_KEY
|
|
240
|
+
? html`<div class="space-y-1">
|
|
241
|
+
<label class="text-xs font-medium text-gray-400"
|
|
242
|
+
>Gemini API Key</label
|
|
231
243
|
>
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
disabled=${!currentGroupValid}
|
|
252
|
-
class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all ${currentGroupValid
|
|
253
|
-
? "bg-white text-black hover:opacity-85"
|
|
254
|
-
: "bg-gray-800 text-gray-500 cursor-not-allowed"}"
|
|
255
|
-
>
|
|
256
|
-
Next
|
|
257
|
-
</button>
|
|
244
|
+
<${SecretInput}
|
|
245
|
+
value=${vals.GEMINI_API_KEY || ""}
|
|
246
|
+
onInput=${(e) => setValue("GEMINI_API_KEY", e.target.value)}
|
|
247
|
+
placeholder="AI..."
|
|
248
|
+
isSecret=${true}
|
|
249
|
+
inputClass="flex-1 bg-black/30 border border-border rounded-lg px-3 py-2 text-sm text-gray-200 outline-none focus:border-gray-500 font-mono"
|
|
250
|
+
/>
|
|
251
|
+
<p class="text-xs text-gray-600">
|
|
252
|
+
Used for memory embeddings and Nano Banana -${" "}
|
|
253
|
+
<a
|
|
254
|
+
href="https://aistudio.google.com"
|
|
255
|
+
target="_blank"
|
|
256
|
+
class="hover:underline"
|
|
257
|
+
style="color: var(--accent-link)"
|
|
258
|
+
>get key</a
|
|
259
|
+
>
|
|
260
|
+
</p>
|
|
261
|
+
</div>`
|
|
262
|
+
: null}
|
|
258
263
|
`
|
|
259
|
-
:
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
264
|
+
: null}
|
|
265
|
+
|
|
266
|
+
<div class="grid grid-cols-2 gap-2 pt-3">
|
|
267
|
+
${step < totalGroups - 1
|
|
268
|
+
? html`
|
|
269
|
+
${step > 0
|
|
270
|
+
? html`<button
|
|
271
|
+
onclick=${goBack}
|
|
272
|
+
class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all border border-border text-gray-300 hover:border-gray-500"
|
|
273
|
+
>
|
|
274
|
+
Back
|
|
275
|
+
</button>`
|
|
276
|
+
: html`<div class="w-full"></div>`}
|
|
277
|
+
<button
|
|
278
|
+
onclick=${goNext}
|
|
279
|
+
disabled=${!currentGroupValid}
|
|
280
|
+
class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all ac-btn-cyan"
|
|
281
|
+
>
|
|
282
|
+
Next
|
|
283
|
+
</button>
|
|
284
|
+
`
|
|
285
|
+
: html`
|
|
286
|
+
${step > 0
|
|
287
|
+
? html`<button
|
|
288
|
+
onclick=${goBack}
|
|
289
|
+
class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all border border-border text-gray-300 hover:border-gray-500"
|
|
290
|
+
>
|
|
291
|
+
Back
|
|
292
|
+
</button>`
|
|
293
|
+
: html`<div class="w-full"></div>`}
|
|
294
|
+
<button
|
|
295
|
+
onclick=${handleSubmit}
|
|
296
|
+
disabled=${!allValid || loading}
|
|
297
|
+
class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all ac-btn-cyan"
|
|
298
|
+
>
|
|
299
|
+
${loading ? "Starting..." : "Next"}
|
|
300
|
+
</button>
|
|
301
|
+
`}
|
|
302
|
+
</div>
|
|
303
|
+
`;
|
|
304
|
+
};
|
|
@@ -7,51 +7,62 @@ export const WelcomeHeader = ({
|
|
|
7
7
|
groups,
|
|
8
8
|
step,
|
|
9
9
|
isSetupStep,
|
|
10
|
+
isPairingStep,
|
|
10
11
|
stepNumber,
|
|
11
12
|
activeStepLabel,
|
|
12
|
-
vals,
|
|
13
|
-
hasAi,
|
|
14
13
|
}) => {
|
|
15
|
-
const progressSteps = [
|
|
14
|
+
const progressSteps = [
|
|
15
|
+
...groups,
|
|
16
|
+
{ id: "setup", title: "Initializing" },
|
|
17
|
+
{ id: "pairing", title: "Pairing" },
|
|
18
|
+
];
|
|
16
19
|
|
|
17
20
|
return html`
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
21
|
+
<div class="text-center mb-1">
|
|
22
|
+
<img
|
|
23
|
+
src="./img/logo.svg"
|
|
24
|
+
alt="alphaclaw"
|
|
25
|
+
class="mx-auto mb-3"
|
|
26
|
+
width="32"
|
|
27
|
+
height="33"
|
|
28
|
+
/>
|
|
29
|
+
<h1 class="text-2xl font-semibold mb-2">Setup</h1>
|
|
30
|
+
<p style="color: var(--text-muted)" class="text-sm">
|
|
31
|
+
Let's get your agent running
|
|
32
|
+
</p>
|
|
33
|
+
<div class="mt-4 mb-2 flex items-center justify-center">
|
|
34
|
+
<span
|
|
35
|
+
class="text-[11px] px-2.5 py-1 rounded-full border border-border font-medium"
|
|
36
|
+
style="background: rgba(0, 0, 0, 0.3); color: var(--text-muted)"
|
|
37
|
+
>
|
|
38
|
+
Step ${stepNumber} of ${progressSteps.length} - ${activeStepLabel}
|
|
39
|
+
</span>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
`;
|
|
43
|
+
<div class="flex items-center gap-2">
|
|
44
|
+
${progressSteps.map((group, idx) => {
|
|
45
|
+
const isActive = idx === step;
|
|
46
|
+
const isComplete = idx < step || (isSetupStep && group.id === "setup");
|
|
47
|
+
const isPairingComplete =
|
|
48
|
+
idx < step || (isPairingStep && group.id === "pairing");
|
|
49
|
+
const bg = isActive
|
|
50
|
+
? "rgba(99, 235, 255, 0.9)"
|
|
51
|
+
: group.id === "pairing"
|
|
52
|
+
? isPairingComplete
|
|
53
|
+
? "rgba(99, 235, 255, 0.55)"
|
|
54
|
+
: "rgba(82, 94, 122, 0.45)"
|
|
55
|
+
: isComplete
|
|
56
|
+
? "rgba(99, 235, 255, 0.55)"
|
|
57
|
+
: "rgba(82, 94, 122, 0.45)";
|
|
58
|
+
return html`
|
|
59
|
+
<div
|
|
60
|
+
class="h-1 flex-1 rounded-full transition-colors"
|
|
61
|
+
style=${{ background: bg }}
|
|
62
|
+
title=${group.title}
|
|
63
|
+
></div>
|
|
64
|
+
`;
|
|
65
|
+
})}
|
|
66
|
+
</div>
|
|
67
|
+
`;
|
|
57
68
|
};
|