@chrysb/alphaclaw 0.5.1-beta.0 → 0.5.1
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 +3 -1
- package/lib/public/css/theme.css +57 -0
- package/lib/public/js/app.js +63 -26
- package/lib/public/js/components/onboarding/use-welcome-storage.js +2 -2
- package/lib/public/js/components/onboarding/welcome-config.js +1 -1
- package/lib/public/js/components/onboarding/welcome-form-step.js +2 -23
- package/lib/public/js/components/onboarding/welcome-header.js +19 -13
- package/lib/public/js/components/onboarding/welcome-pre-step.js +81 -0
- package/lib/public/js/components/usage-tab/index.js +0 -43
- package/lib/public/js/components/usage-tab/overview-section.js +1 -2
- package/lib/public/js/components/usage-tab/use-usage-tab.js +0 -48
- package/lib/public/js/components/welcome/index.js +13 -2
- package/lib/public/js/components/welcome/use-welcome.js +16 -4
- package/lib/public/js/lib/api.js +0 -14
- package/lib/server/db/usage/index.js +0 -4
- package/lib/server/onboarding/workspace.js +10 -0
- package/lib/server/routes/usage.js +0 -80
- package/lib/server.js +0 -7
- package/package.json +1 -1
- package/lib/server/db/usage/backfill.js +0 -416
package/bin/alphaclaw.js
CHANGED
|
@@ -481,6 +481,8 @@ if (!kSetupPassword) {
|
|
|
481
481
|
|
|
482
482
|
process.env.OPENCLAW_HOME = rootDir;
|
|
483
483
|
process.env.OPENCLAW_CONFIG_PATH = path.join(openclawDir, "openclaw.json");
|
|
484
|
+
process.env.GOG_KEYRING_PASSWORD =
|
|
485
|
+
process.env.GOG_KEYRING_PASSWORD || "alphaclaw";
|
|
484
486
|
|
|
485
487
|
// ---------------------------------------------------------------------------
|
|
486
488
|
// 8. Install gog (Google Workspace CLI) if not present
|
|
@@ -516,7 +518,7 @@ if (!gogInstalled) {
|
|
|
516
518
|
}
|
|
517
519
|
|
|
518
520
|
// ---------------------------------------------------------------------------
|
|
519
|
-
//
|
|
521
|
+
// 9. Install/reconcile system cron entry
|
|
520
522
|
// ---------------------------------------------------------------------------
|
|
521
523
|
|
|
522
524
|
const packagedHourlyGitSyncPath = path.join(setupDir, "hourly-git-sync.sh");
|
package/lib/public/css/theme.css
CHANGED
|
@@ -246,6 +246,45 @@ textarea:focus {
|
|
|
246
246
|
background: rgba(99, 235, 255, 0.08);
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
.ac-path-card {
|
|
250
|
+
border: 1px solid var(--panel-border-contrast);
|
|
251
|
+
background: rgba(0, 0, 0, 0.2);
|
|
252
|
+
transition:
|
|
253
|
+
border-color 0.15s ease,
|
|
254
|
+
background 0.15s ease,
|
|
255
|
+
box-shadow 0.15s ease,
|
|
256
|
+
transform 0.15s ease;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.ac-path-card:hover {
|
|
260
|
+
border-color: rgba(99, 235, 255, 0.5);
|
|
261
|
+
background: rgba(99, 235, 255, 0.04);
|
|
262
|
+
box-shadow:
|
|
263
|
+
inset 0 0 0 1px rgba(99, 235, 255, 0.1),
|
|
264
|
+
0 0 12px rgba(99, 235, 255, 0.08);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.ac-path-card:hover .ac-path-icon {
|
|
268
|
+
color: var(--accent);
|
|
269
|
+
border-color: rgba(99, 235, 255, 0.3);
|
|
270
|
+
background: rgba(99, 235, 255, 0.1);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.ac-path-card:hover .ac-path-title {
|
|
274
|
+
color: #b9f8ff;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.ac-path-card:hover .ac-path-desc {
|
|
278
|
+
color: #94a3b8;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.ac-path-icon {
|
|
282
|
+
transition:
|
|
283
|
+
color 0.15s ease,
|
|
284
|
+
border-color 0.15s ease,
|
|
285
|
+
background 0.15s ease;
|
|
286
|
+
}
|
|
287
|
+
|
|
249
288
|
.ac-btn-secondary {
|
|
250
289
|
border: 1px solid var(--panel-border-contrast);
|
|
251
290
|
color: #d1d5db;
|
|
@@ -426,6 +465,24 @@ textarea:focus {
|
|
|
426
465
|
}
|
|
427
466
|
}
|
|
428
467
|
|
|
468
|
+
@keyframes acStepPillPulse {
|
|
469
|
+
0%,
|
|
470
|
+
100% {
|
|
471
|
+
box-shadow:
|
|
472
|
+
0 0 0 0 rgba(99, 235, 255, 0.14),
|
|
473
|
+
0 0 5px rgba(99, 235, 255, 0.18);
|
|
474
|
+
}
|
|
475
|
+
50% {
|
|
476
|
+
box-shadow:
|
|
477
|
+
0 0 0 3px rgba(99, 235, 255, 0.08),
|
|
478
|
+
0 0 9px rgba(99, 235, 255, 0.32);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.ac-step-pill-pulse {
|
|
483
|
+
animation: acStepPillPulse 2.6s ease-in-out infinite;
|
|
484
|
+
}
|
|
485
|
+
|
|
429
486
|
.ac-status-dot {
|
|
430
487
|
width: 8px;
|
|
431
488
|
height: 8px;
|
package/lib/public/js/app.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { h, render } from "https://esm.sh/preact";
|
|
2
2
|
import { useState, useEffect } from "https://esm.sh/preact/hooks";
|
|
3
3
|
import htm from "https://esm.sh/htm";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Router,
|
|
6
|
+
Route,
|
|
7
|
+
Switch,
|
|
8
|
+
useLocation,
|
|
9
|
+
} from "https://esm.sh/wouter-preact";
|
|
5
10
|
import { logout } from "./lib/api.js";
|
|
6
11
|
import { Welcome } from "./components/welcome/index.js";
|
|
7
12
|
import { ToastContainer } from "./components/toast.js";
|
|
@@ -14,7 +19,6 @@ import {
|
|
|
14
19
|
EnvarsRoute,
|
|
15
20
|
GeneralRoute,
|
|
16
21
|
ModelsRoute,
|
|
17
|
-
ProvidersRoute,
|
|
18
22
|
RouteRedirect,
|
|
19
23
|
TelegramRoute,
|
|
20
24
|
UsageRoute,
|
|
@@ -24,34 +28,48 @@ import {
|
|
|
24
28
|
import { useAppShellController } from "./hooks/use-app-shell-controller.js";
|
|
25
29
|
import { useAppShellUi } from "./hooks/use-app-shell-ui.js";
|
|
26
30
|
import { useBrowseNavigation } from "./hooks/use-browse-navigation.js";
|
|
27
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
getHashRouterPath,
|
|
33
|
+
useHashLocation,
|
|
34
|
+
} from "./hooks/use-hash-location.js";
|
|
28
35
|
import { readUiSettings, writeUiSettings } from "./lib/ui-settings.js";
|
|
29
36
|
|
|
30
37
|
const html = htm.bind(h);
|
|
31
|
-
const kDoctorWarningDismissedUntilUiSettingKey =
|
|
38
|
+
const kDoctorWarningDismissedUntilUiSettingKey =
|
|
39
|
+
"doctorWarningDismissedUntilMs";
|
|
32
40
|
const kOneWeekMs = 7 * 24 * 60 * 60 * 1000;
|
|
33
41
|
|
|
34
42
|
const App = () => {
|
|
35
43
|
const [location, setLocation] = useLocation();
|
|
36
|
-
const [doctorWarningDismissedUntilMs, setDoctorWarningDismissedUntilMs] =
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
const [doctorWarningDismissedUntilMs, setDoctorWarningDismissedUntilMs] =
|
|
45
|
+
useState(() => {
|
|
46
|
+
const settings = readUiSettings();
|
|
47
|
+
return Number(settings[kDoctorWarningDismissedUntilUiSettingKey] || 0);
|
|
48
|
+
});
|
|
40
49
|
|
|
41
|
-
const { state: controllerState, actions: controllerActions } =
|
|
42
|
-
|
|
43
|
-
});
|
|
44
|
-
const { refs: shellRefs, state: shellState, actions: shellActions } = useAppShellUi();
|
|
45
|
-
const { state: browseState, actions: browseActions, constants: browseConstants } =
|
|
46
|
-
useBrowseNavigation({
|
|
50
|
+
const { state: controllerState, actions: controllerActions } =
|
|
51
|
+
useAppShellController({
|
|
47
52
|
location,
|
|
48
|
-
setLocation,
|
|
49
|
-
onCloseMobileSidebar: shellActions.closeMobileSidebar,
|
|
50
53
|
});
|
|
54
|
+
const {
|
|
55
|
+
refs: shellRefs,
|
|
56
|
+
state: shellState,
|
|
57
|
+
actions: shellActions,
|
|
58
|
+
} = useAppShellUi();
|
|
59
|
+
const {
|
|
60
|
+
state: browseState,
|
|
61
|
+
actions: browseActions,
|
|
62
|
+
constants: browseConstants,
|
|
63
|
+
} = useBrowseNavigation({
|
|
64
|
+
location,
|
|
65
|
+
setLocation,
|
|
66
|
+
onCloseMobileSidebar: shellActions.closeMobileSidebar,
|
|
67
|
+
});
|
|
51
68
|
|
|
52
69
|
useEffect(() => {
|
|
53
70
|
const settings = readUiSettings();
|
|
54
|
-
settings[kDoctorWarningDismissedUntilUiSettingKey] =
|
|
71
|
+
settings[kDoctorWarningDismissedUntilUiSettingKey] =
|
|
72
|
+
doctorWarningDismissedUntilMs;
|
|
55
73
|
writeUiSettings(settings);
|
|
56
74
|
}, [doctorWarningDismissedUntilMs]);
|
|
57
75
|
|
|
@@ -83,10 +101,13 @@ const App = () => {
|
|
|
83
101
|
if (!controllerState.onboarded) {
|
|
84
102
|
return html`
|
|
85
103
|
<div
|
|
86
|
-
class="min-h-screen flex
|
|
104
|
+
class="min-h-screen flex flex-col items-center pt-12 pb-8 px-4"
|
|
87
105
|
style="position: relative; z-index: 1"
|
|
88
106
|
>
|
|
89
|
-
<${Welcome}
|
|
107
|
+
<${Welcome}
|
|
108
|
+
onComplete=${controllerActions.handleOnboardingComplete}
|
|
109
|
+
acVersion=${controllerState.acVersion}
|
|
110
|
+
/>
|
|
90
111
|
</div>
|
|
91
112
|
<${ToastContainer} />
|
|
92
113
|
`;
|
|
@@ -150,7 +171,9 @@ const App = () => {
|
|
|
150
171
|
onNavigateToBrowseFile=${browseActions.navigateToBrowseFile}
|
|
151
172
|
onEditSelectedBrowseFile=${() =>
|
|
152
173
|
setLocation(
|
|
153
|
-
browseActions.buildBrowseRoute(browseState.selectedBrowsePath, {
|
|
174
|
+
browseActions.buildBrowseRoute(browseState.selectedBrowsePath, {
|
|
175
|
+
view: "edit",
|
|
176
|
+
}),
|
|
154
177
|
)}
|
|
155
178
|
onClearSelection=${() => {
|
|
156
179
|
browseActions.clearBrowsePreview();
|
|
@@ -163,14 +186,22 @@ const App = () => {
|
|
|
163
186
|
onscroll=${shellActions.handlePaneScroll}
|
|
164
187
|
style=${{ display: browseState.isBrowseRoute ? "none" : "block" }}
|
|
165
188
|
>
|
|
166
|
-
<div
|
|
189
|
+
<div
|
|
190
|
+
class=${`mobile-topbar ${shellState.mobileTopbarScrolled ? "is-scrolled" : ""}`}
|
|
191
|
+
>
|
|
167
192
|
<button
|
|
168
193
|
class="mobile-topbar-menu"
|
|
169
|
-
onclick=${() =>
|
|
194
|
+
onclick=${() =>
|
|
195
|
+
shellActions.setMobileSidebarOpen((open) => !open)}
|
|
170
196
|
aria-label="Open menu"
|
|
171
197
|
aria-expanded=${shellState.mobileSidebarOpen ? "true" : "false"}
|
|
172
198
|
>
|
|
173
|
-
<svg
|
|
199
|
+
<svg
|
|
200
|
+
width="18"
|
|
201
|
+
height="18"
|
|
202
|
+
viewBox="0 0 16 16"
|
|
203
|
+
fill="currentColor"
|
|
204
|
+
>
|
|
174
205
|
<path
|
|
175
206
|
d="M2 3.75a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H2.75A.75.75 0 012 3.75zm0 4.25a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H2.75A.75.75 0 012 8zm0 4.25a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H2.75a.75.75 0 01-.75-.75z"
|
|
176
207
|
/>
|
|
@@ -201,7 +232,9 @@ const App = () => {
|
|
|
201
232
|
onOpenclawUpdate=${controllerActions.handleOpenclawUpdate}
|
|
202
233
|
onRestartRequired=${controllerActions.setRestartRequired}
|
|
203
234
|
onDismissDoctorWarning=${() =>
|
|
204
|
-
setDoctorWarningDismissedUntilMs(
|
|
235
|
+
setDoctorWarningDismissedUntilMs(
|
|
236
|
+
Date.now() + kOneWeekMs,
|
|
237
|
+
)}
|
|
205
238
|
/>
|
|
206
239
|
</${Route}>
|
|
207
240
|
<${Route} path="/doctor">
|
|
@@ -232,7 +265,9 @@ const App = () => {
|
|
|
232
265
|
<${Route} path="/usage/:sessionId">
|
|
233
266
|
${(params) => html`
|
|
234
267
|
<${UsageRoute}
|
|
235
|
-
sessionId=${decodeURIComponent(
|
|
268
|
+
sessionId=${decodeURIComponent(
|
|
269
|
+
params.sessionId || "",
|
|
270
|
+
)}
|
|
236
271
|
onSetLocation=${setLocation}
|
|
237
272
|
/>
|
|
238
273
|
`}
|
|
@@ -280,7 +315,9 @@ const App = () => {
|
|
|
280
315
|
<div class="app-statusbar">
|
|
281
316
|
<div class="statusbar-left">
|
|
282
317
|
${controllerState.acVersion
|
|
283
|
-
? html`<span style="color: var(--text-muted)"
|
|
318
|
+
? html`<span style="color: var(--text-muted)"
|
|
319
|
+
>v${controllerState.acVersion}</span
|
|
320
|
+
>`
|
|
284
321
|
: null}
|
|
285
322
|
</div>
|
|
286
323
|
<div class="statusbar-right">
|
|
@@ -30,8 +30,8 @@ export const useWelcomeStorage = ({
|
|
|
30
30
|
String(initialSetupState?.[kOnboardingStepKey] || ""),
|
|
31
31
|
10,
|
|
32
32
|
);
|
|
33
|
-
if (!Number.isFinite(parsedStep)) return
|
|
34
|
-
const clampedStep = Math.max(
|
|
33
|
+
if (!Number.isFinite(parsedStep)) return -1;
|
|
34
|
+
const clampedStep = Math.max(-1, Math.min(kPairingStepIndex, parsedStep));
|
|
35
35
|
if (clampedStep === kSetupStepIndex && shouldRecoverFromSetupState) return 0;
|
|
36
36
|
return clampedStep;
|
|
37
37
|
});
|
|
@@ -6,7 +6,6 @@ import { ActionButton } from "../action-button.js";
|
|
|
6
6
|
import { Badge } from "../badge.js";
|
|
7
7
|
import { SegmentedControl } from "../segmented-control.js";
|
|
8
8
|
import {
|
|
9
|
-
isValidGithubRepoInput,
|
|
10
9
|
kGithubFlowFresh,
|
|
11
10
|
kGithubFlowImport,
|
|
12
11
|
kGithubTargetRepoModeCreate,
|
|
@@ -195,29 +194,9 @@ export const WelcomeFormStep = ({
|
|
|
195
194
|
${activeGroup.id === "github" &&
|
|
196
195
|
html`
|
|
197
196
|
<div class="space-y-3">
|
|
198
|
-
<div class="space-y-1 pt-1">
|
|
199
|
-
<div>
|
|
200
|
-
<label class="text-xs font-medium text-gray-400">Setup mode</label>
|
|
201
|
-
</div>
|
|
202
|
-
<${SegmentedControl}
|
|
203
|
-
options=${[
|
|
204
|
-
{ label: "Start fresh", value: kGithubFlowFresh },
|
|
205
|
-
{ label: "Import existing setup", value: kGithubFlowImport },
|
|
206
|
-
]}
|
|
207
|
-
value=${githubFlow}
|
|
208
|
-
onChange=${(value) => setValue("_GITHUB_FLOW", value)}
|
|
209
|
-
size="md"
|
|
210
|
-
fullWidth=${true}
|
|
211
|
-
/>
|
|
212
|
-
</div>
|
|
213
197
|
${githubFlow === kGithubFlowFresh
|
|
214
198
|
? html`
|
|
215
199
|
<div class="space-y-1">
|
|
216
|
-
<div>
|
|
217
|
-
<label class="text-xs font-medium text-gray-400"
|
|
218
|
-
>Repository setup</label
|
|
219
|
-
>
|
|
220
|
-
</div>
|
|
221
200
|
<${SegmentedControl}
|
|
222
201
|
options=${[
|
|
223
202
|
{
|
|
@@ -366,7 +345,7 @@ export const WelcomeFormStep = ({
|
|
|
366
345
|
<div class="grid grid-cols-2 gap-2 pt-3">
|
|
367
346
|
${step < totalGroups - 1
|
|
368
347
|
? html`
|
|
369
|
-
${step
|
|
348
|
+
${step >= 0
|
|
370
349
|
? html`<${ActionButton}
|
|
371
350
|
onClick=${goBack}
|
|
372
351
|
tone="secondary"
|
|
@@ -390,7 +369,7 @@ export const WelcomeFormStep = ({
|
|
|
390
369
|
/>
|
|
391
370
|
`
|
|
392
371
|
: html`
|
|
393
|
-
${step
|
|
372
|
+
${step >= 0
|
|
394
373
|
? html`<${ActionButton}
|
|
395
374
|
onClick=${goBack}
|
|
396
375
|
tone="secondary"
|
|
@@ -6,6 +6,7 @@ const html = htm.bind(h);
|
|
|
6
6
|
export const WelcomeHeader = ({
|
|
7
7
|
groups,
|
|
8
8
|
step,
|
|
9
|
+
isPreStep,
|
|
9
10
|
isSetupStep,
|
|
10
11
|
isPairingStep,
|
|
11
12
|
stepNumber,
|
|
@@ -30,12 +31,14 @@ export const WelcomeHeader = ({
|
|
|
30
31
|
<p style="color: var(--text-muted)" class="text-sm">
|
|
31
32
|
Let's get your agent running
|
|
32
33
|
</p>
|
|
33
|
-
|
|
34
|
+
<div class="mt-4 mb-2 flex items-center justify-center">
|
|
34
35
|
<span
|
|
35
36
|
class="text-[11px] px-2.5 py-1 rounded-full border border-border font-medium"
|
|
36
37
|
style="background: rgba(0, 0, 0, 0.3); color: var(--text-muted)"
|
|
37
38
|
>
|
|
38
|
-
|
|
39
|
+
${isPreStep
|
|
40
|
+
? "Choose your destiny"
|
|
41
|
+
: `Step ${stepNumber} of ${progressSteps.length} - ${activeStepLabel}`}
|
|
39
42
|
</span>
|
|
40
43
|
</div>
|
|
41
44
|
</div>
|
|
@@ -43,21 +46,24 @@ export const WelcomeHeader = ({
|
|
|
43
46
|
<div class="flex items-center gap-2">
|
|
44
47
|
${progressSteps.map((group, idx) => {
|
|
45
48
|
const isActive = idx === step;
|
|
46
|
-
const isComplete =
|
|
49
|
+
const isComplete =
|
|
50
|
+
idx < step || (isSetupStep && group.id === "setup");
|
|
47
51
|
const isPairingComplete =
|
|
48
52
|
idx < step || (isPairingStep && group.id === "pairing");
|
|
49
|
-
const bg =
|
|
50
|
-
? "rgba(
|
|
51
|
-
:
|
|
52
|
-
?
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
:
|
|
53
|
+
const bg = isPreStep
|
|
54
|
+
? "rgba(82, 94, 122, 0.45)"
|
|
55
|
+
: isActive
|
|
56
|
+
? "rgba(99, 235, 255, 0.9)"
|
|
57
|
+
: group.id === "pairing"
|
|
58
|
+
? isPairingComplete
|
|
59
|
+
? "rgba(99, 235, 255, 0.55)"
|
|
60
|
+
: "rgba(82, 94, 122, 0.45)"
|
|
61
|
+
: isComplete
|
|
62
|
+
? "rgba(99, 235, 255, 0.55)"
|
|
63
|
+
: "rgba(82, 94, 122, 0.45)";
|
|
58
64
|
return html`
|
|
59
65
|
<div
|
|
60
|
-
class="h-1 flex-1 rounded-full transition-colors"
|
|
66
|
+
class="h-1 flex-1 rounded-full transition-colors ${isActive ? "ac-step-pill-pulse" : ""}"
|
|
61
67
|
style=${{ background: bg }}
|
|
62
68
|
title=${group.title}
|
|
63
69
|
></div>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import htm from "https://esm.sh/htm";
|
|
3
|
+
import { kGithubFlowFresh, kGithubFlowImport } from "./welcome-config.js";
|
|
4
|
+
|
|
5
|
+
const html = htm.bind(h);
|
|
6
|
+
|
|
7
|
+
export const WelcomePreStep = ({ onSelectFlow }) => {
|
|
8
|
+
return html`
|
|
9
|
+
<div class="space-y-3">
|
|
10
|
+
<button
|
|
11
|
+
type="button"
|
|
12
|
+
onclick=${() => onSelectFlow(kGithubFlowFresh)}
|
|
13
|
+
class="w-full flex items-center gap-4 text-left p-4 rounded-xl ac-path-card"
|
|
14
|
+
>
|
|
15
|
+
<div
|
|
16
|
+
class="ac-path-icon flex-shrink-0 w-10 h-10 flex items-center justify-center bg-black/30 rounded-lg border border-border text-gray-300"
|
|
17
|
+
>
|
|
18
|
+
<svg
|
|
19
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
20
|
+
viewBox="0 0 24 24"
|
|
21
|
+
fill="currentColor"
|
|
22
|
+
class="w-5 h-5"
|
|
23
|
+
>
|
|
24
|
+
<path
|
|
25
|
+
d="M14 4.4375C15.3462 4.4375 16.4375 3.34619 16.4375 2H17.5625C17.5625 3.34619 18.6538 4.4375 20 4.4375V5.5625C18.6538 5.5625 17.5625 6.65381 17.5625 8H16.4375C16.4375 6.65381 15.3462 5.5625 14 5.5625V4.4375ZM1 11C4.31371 11 7 8.31371 7 5H9C9 8.31371 11.6863 11 15 11V13C11.6863 13 9 15.6863 9 19H7C7 15.6863 4.31371 13 1 13V11ZM4.87601 12C6.18717 12.7276 7.27243 13.8128 8 15.124 8.72757 13.8128 9.81283 12.7276 11.124 12 9.81283 11.2724 8.72757 10.1872 8 8.87601 7.27243 10.1872 6.18717 11.2724 4.87601 12ZM17.25 14C17.25 15.7949 15.7949 17.25 14 17.25V18.75C15.7949 18.75 17.25 20.2051 17.25 22H18.75C18.75 20.2051 20.2051 18.75 22 18.75V17.25C20.2051 17.25 18.75 15.7949 18.75 14H17.25Z"
|
|
26
|
+
></path>
|
|
27
|
+
</svg>
|
|
28
|
+
</div>
|
|
29
|
+
<div>
|
|
30
|
+
<div
|
|
31
|
+
class="ac-path-title text-sm font-medium text-gray-200 mb-0.5 transition-colors duration-150"
|
|
32
|
+
>
|
|
33
|
+
Start fresh
|
|
34
|
+
</div>
|
|
35
|
+
<div
|
|
36
|
+
class="ac-path-desc text-xs text-gray-500 transition-colors duration-150"
|
|
37
|
+
>
|
|
38
|
+
Create a new repository and set up your agent from scratch.
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</button>
|
|
42
|
+
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
onclick=${() => onSelectFlow(kGithubFlowImport)}
|
|
46
|
+
class="w-full flex items-center gap-4 text-left p-4 rounded-xl ac-path-card"
|
|
47
|
+
>
|
|
48
|
+
<div
|
|
49
|
+
class="ac-path-icon flex-shrink-0 w-10 h-10 flex items-center justify-center bg-black/30 rounded-lg border border-border text-gray-300"
|
|
50
|
+
>
|
|
51
|
+
<svg
|
|
52
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
53
|
+
viewBox="0 0 24 24"
|
|
54
|
+
fill="none"
|
|
55
|
+
stroke="currentColor"
|
|
56
|
+
stroke-width="2"
|
|
57
|
+
stroke-linecap="round"
|
|
58
|
+
stroke-linejoin="round"
|
|
59
|
+
class="w-5 h-5"
|
|
60
|
+
>
|
|
61
|
+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
|
62
|
+
<polyline points="7 10 12 15 17 10"></polyline>
|
|
63
|
+
<line x1="12" y1="15" x2="12" y2="3"></line>
|
|
64
|
+
</svg>
|
|
65
|
+
</div>
|
|
66
|
+
<div>
|
|
67
|
+
<div
|
|
68
|
+
class="ac-path-title text-sm font-medium text-gray-200 mb-0.5 transition-colors duration-150"
|
|
69
|
+
>
|
|
70
|
+
Import existing setup
|
|
71
|
+
</div>
|
|
72
|
+
<div
|
|
73
|
+
class="ac-path-desc text-xs text-gray-500 transition-colors duration-150"
|
|
74
|
+
>
|
|
75
|
+
Connect an existing repository that already has an OpenClaw setup.
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</button>
|
|
79
|
+
</div>
|
|
80
|
+
`;
|
|
81
|
+
};
|
|
@@ -2,7 +2,6 @@ import { h } from "https://esm.sh/preact";
|
|
|
2
2
|
import htm from "https://esm.sh/htm";
|
|
3
3
|
import { ActionButton } from "../action-button.js";
|
|
4
4
|
import { PageHeader } from "../page-header.js";
|
|
5
|
-
import { showToast } from "../toast.js";
|
|
6
5
|
import { OverviewSection } from "./overview-section.js";
|
|
7
6
|
import { SessionsSection } from "./sessions-section.js";
|
|
8
7
|
import { useUsageTab } from "./use-usage-tab.js";
|
|
@@ -27,20 +26,6 @@ export const UsageTab = ({ sessionId = "" }) => {
|
|
|
27
26
|
);
|
|
28
27
|
};
|
|
29
28
|
|
|
30
|
-
const handleRunBackfill = async () => {
|
|
31
|
-
try {
|
|
32
|
-
const result = await actions.triggerBackfill();
|
|
33
|
-
const backfilledEvents = Number(result?.backfilledEvents || 0);
|
|
34
|
-
const filesScanned = Number(result?.filesScanned || 0);
|
|
35
|
-
showToast(
|
|
36
|
-
`Imported ${backfilledEvents.toLocaleString()} usage events from ${filesScanned.toLocaleString()} session files`,
|
|
37
|
-
"success",
|
|
38
|
-
);
|
|
39
|
-
} catch (error) {
|
|
40
|
-
showToast(error.message || "Could not import historical usage data", "error");
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
29
|
return html`
|
|
45
30
|
<div class="space-y-4">
|
|
46
31
|
<${PageHeader}
|
|
@@ -61,34 +46,6 @@ export const UsageTab = ({ sessionId = "" }) => {
|
|
|
61
46
|
${state.error}
|
|
62
47
|
</div>`
|
|
63
48
|
: null}
|
|
64
|
-
${state.showBackfillBanner
|
|
65
|
-
? html`
|
|
66
|
-
<div class="bg-surface border border-border rounded-xl p-4 space-y-3">
|
|
67
|
-
<p class="text-xs text-gray-300 leading-5">
|
|
68
|
-
We found historical usage data from
|
|
69
|
-
<span class="font-medium text-gray-200"
|
|
70
|
-
>${Number(state.estimatedBackfillFiles || 0).toLocaleString()}</span
|
|
71
|
-
>
|
|
72
|
-
session files. Import it?
|
|
73
|
-
</p>
|
|
74
|
-
<div class="flex flex-wrap items-center gap-2">
|
|
75
|
-
<${ActionButton}
|
|
76
|
-
onClick=${handleRunBackfill}
|
|
77
|
-
loading=${state.runningBackfill}
|
|
78
|
-
disabled=${state.loadingBackfillStatus}
|
|
79
|
-
idleLabel="Import historical data"
|
|
80
|
-
loadingLabel="Importing..."
|
|
81
|
-
/>
|
|
82
|
-
<${ActionButton}
|
|
83
|
-
onClick=${actions.dismissBackfillBanner}
|
|
84
|
-
tone="secondary"
|
|
85
|
-
disabled=${state.runningBackfill}
|
|
86
|
-
idleLabel="Dismiss"
|
|
87
|
-
/>
|
|
88
|
-
</div>
|
|
89
|
-
</div>
|
|
90
|
-
`
|
|
91
|
-
: null}
|
|
92
49
|
${state.loadingSummary && !state.summary
|
|
93
50
|
? html`<div class="text-sm text-[var(--text-muted)]">Loading usage summary...</div>`
|
|
94
51
|
: html`
|
|
@@ -31,9 +31,8 @@ const getCacheHitRateValueClass = (ratio) => {
|
|
|
31
31
|
const getOverviewMetrics = (summary) => {
|
|
32
32
|
const totals = summary?.totals || {};
|
|
33
33
|
const cacheReadTokens = Number(totals.cacheReadTokens || 0);
|
|
34
|
-
const cacheWriteTokens = Number(totals.cacheWriteTokens || 0);
|
|
35
34
|
const inputTokens = Number(totals.inputTokens || 0);
|
|
36
|
-
const promptTokens = inputTokens + cacheReadTokens
|
|
35
|
+
const promptTokens = inputTokens + cacheReadTokens;
|
|
37
36
|
const turnCount = Number(totals.turnCount || 0);
|
|
38
37
|
const totalTokens = Number(totals.totalTokens || 0);
|
|
39
38
|
const totalCost = Number(totals.totalCost || 0);
|
|
@@ -6,11 +6,9 @@ import {
|
|
|
6
6
|
useState,
|
|
7
7
|
} from "https://esm.sh/preact/hooks";
|
|
8
8
|
import {
|
|
9
|
-
fetchUsageBackfillStatus,
|
|
10
9
|
fetchUsageSessionDetail,
|
|
11
10
|
fetchUsageSessions,
|
|
12
11
|
fetchUsageSummary,
|
|
13
|
-
runUsageBackfill,
|
|
14
12
|
} from "../../lib/api.js";
|
|
15
13
|
import { formatInteger, formatUsd } from "../../lib/format.js";
|
|
16
14
|
import { readUiSettings, writeUiSettings } from "../../lib/ui-settings.js";
|
|
@@ -43,10 +41,6 @@ export const useUsageTab = ({ sessionId = "" }) => {
|
|
|
43
41
|
const [loadingSummary, setLoadingSummary] = useState(false);
|
|
44
42
|
const [loadingSessions, setLoadingSessions] = useState(false);
|
|
45
43
|
const [loadingDetailById, setLoadingDetailById] = useState({});
|
|
46
|
-
const [loadingBackfillStatus, setLoadingBackfillStatus] = useState(false);
|
|
47
|
-
const [runningBackfill, setRunningBackfill] = useState(false);
|
|
48
|
-
const [showBackfillBanner, setShowBackfillBanner] = useState(false);
|
|
49
|
-
const [estimatedBackfillFiles, setEstimatedBackfillFiles] = useState(0);
|
|
50
44
|
const [expandedSessionIds, setExpandedSessionIds] = useState(() =>
|
|
51
45
|
sessionId ? [String(sessionId)] : [],
|
|
52
46
|
);
|
|
@@ -102,37 +96,6 @@ export const useUsageTab = ({ sessionId = "" }) => {
|
|
|
102
96
|
}
|
|
103
97
|
}, []);
|
|
104
98
|
|
|
105
|
-
const loadBackfillStatus = useCallback(async () => {
|
|
106
|
-
setLoadingBackfillStatus(true);
|
|
107
|
-
try {
|
|
108
|
-
const data = await fetchUsageBackfillStatus();
|
|
109
|
-
const available = !!data?.available;
|
|
110
|
-
setShowBackfillBanner(available);
|
|
111
|
-
setEstimatedBackfillFiles(Number(data?.estimatedFiles || 0));
|
|
112
|
-
} catch {
|
|
113
|
-
setShowBackfillBanner(false);
|
|
114
|
-
setEstimatedBackfillFiles(0);
|
|
115
|
-
} finally {
|
|
116
|
-
setLoadingBackfillStatus(false);
|
|
117
|
-
}
|
|
118
|
-
}, []);
|
|
119
|
-
|
|
120
|
-
const triggerBackfill = useCallback(async () => {
|
|
121
|
-
setRunningBackfill(true);
|
|
122
|
-
try {
|
|
123
|
-
const result = await runUsageBackfill();
|
|
124
|
-
setShowBackfillBanner(false);
|
|
125
|
-
await loadSummary();
|
|
126
|
-
await loadSessions();
|
|
127
|
-
return result;
|
|
128
|
-
} catch (err) {
|
|
129
|
-
setError(err.message || "Could not backfill usage data");
|
|
130
|
-
throw err;
|
|
131
|
-
} finally {
|
|
132
|
-
setRunningBackfill(false);
|
|
133
|
-
}
|
|
134
|
-
}, [loadSessions, loadSummary]);
|
|
135
|
-
|
|
136
99
|
useEffect(() => {
|
|
137
100
|
loadSummary();
|
|
138
101
|
}, [loadSummary]);
|
|
@@ -148,10 +111,6 @@ export const useUsageTab = ({ sessionId = "" }) => {
|
|
|
148
111
|
loadSessions();
|
|
149
112
|
}, [loadSessions]);
|
|
150
113
|
|
|
151
|
-
useEffect(() => {
|
|
152
|
-
loadBackfillStatus();
|
|
153
|
-
}, [loadBackfillStatus]);
|
|
154
|
-
|
|
155
114
|
useEffect(() => {
|
|
156
115
|
const safeSessionId = String(sessionId || "").trim();
|
|
157
116
|
if (!safeSessionId) return;
|
|
@@ -297,10 +256,6 @@ export const useUsageTab = ({ sessionId = "" }) => {
|
|
|
297
256
|
loadingSummary,
|
|
298
257
|
loadingSessions,
|
|
299
258
|
loadingDetailById,
|
|
300
|
-
loadingBackfillStatus,
|
|
301
|
-
runningBackfill,
|
|
302
|
-
showBackfillBanner,
|
|
303
|
-
estimatedBackfillFiles,
|
|
304
259
|
expandedSessionIds,
|
|
305
260
|
error,
|
|
306
261
|
periodSummary,
|
|
@@ -310,9 +265,6 @@ export const useUsageTab = ({ sessionId = "" }) => {
|
|
|
310
265
|
setDays,
|
|
311
266
|
setMetric,
|
|
312
267
|
loadSummary,
|
|
313
|
-
loadBackfillStatus,
|
|
314
|
-
triggerBackfill,
|
|
315
|
-
dismissBackfillBanner: () => setShowBackfillBanner(false),
|
|
316
268
|
loadSessionDetail,
|
|
317
269
|
setExpandedSessionIds,
|
|
318
270
|
},
|