@chrysb/alphaclaw 0.3.5-beta.1 → 0.4.1-beta.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 +1 -31
- package/lib/public/assets/icons/google_icon.svg +8 -0
- package/lib/public/css/explorer.css +53 -0
- package/lib/public/css/shell.css +21 -19
- package/lib/public/css/theme.css +17 -0
- package/lib/public/js/app.js +205 -109
- package/lib/public/js/components/credentials-modal.js +36 -8
- package/lib/public/js/components/file-tree.js +212 -22
- package/lib/public/js/components/file-viewer/editor-surface.js +5 -0
- package/lib/public/js/components/file-viewer/index.js +47 -6
- package/lib/public/js/components/file-viewer/markdown-split-view.js +2 -0
- package/lib/public/js/components/file-viewer/status-banners.js +11 -6
- package/lib/public/js/components/file-viewer/toolbar.js +56 -1
- package/lib/public/js/components/file-viewer/use-editor-selection-restore.js +6 -0
- package/lib/public/js/components/file-viewer/use-file-diff.js +11 -0
- package/lib/public/js/components/file-viewer/use-file-loader.js +12 -2
- package/lib/public/js/components/file-viewer/use-file-viewer.js +142 -15
- package/lib/public/js/components/google/account-row.js +131 -0
- package/lib/public/js/components/google/add-account-modal.js +93 -0
- package/lib/public/js/components/google/gmail-setup-wizard.js +450 -0
- package/lib/public/js/components/google/gmail-watch-toggle.js +81 -0
- package/lib/public/js/components/google/index.js +553 -0
- package/lib/public/js/components/google/use-gmail-watch.js +140 -0
- package/lib/public/js/components/google/use-google-accounts.js +41 -0
- package/lib/public/js/components/icons.js +26 -0
- package/lib/public/js/components/scope-picker.js +1 -1
- package/lib/public/js/components/sidebar-git-panel.js +48 -20
- package/lib/public/js/components/sidebar.js +93 -75
- package/lib/public/js/components/toast.js +11 -7
- package/lib/public/js/components/usage-tab/constants.js +31 -0
- package/lib/public/js/components/usage-tab/formatters.js +24 -0
- package/lib/public/js/components/usage-tab/index.js +72 -0
- package/lib/public/js/components/usage-tab/overview-section.js +147 -0
- package/lib/public/js/components/usage-tab/sessions-section.js +175 -0
- package/lib/public/js/components/usage-tab/use-usage-tab.js +241 -0
- package/lib/public/js/components/webhooks.js +182 -129
- package/lib/public/js/lib/api.js +178 -9
- package/lib/public/js/lib/browse-file-policies.js +29 -11
- package/lib/public/js/lib/format.js +71 -0
- package/lib/public/js/lib/syntax-highlighters/index.js +6 -5
- package/lib/public/shared/browse-file-policies.json +13 -0
- package/lib/server/constants.js +47 -7
- package/lib/server/gmail-push.js +109 -0
- package/lib/server/gmail-serve.js +254 -0
- package/lib/server/gmail-watch.js +725 -0
- package/lib/server/google-state.js +317 -0
- package/lib/server/helpers.js +17 -11
- package/lib/server/internal-files-migration.js +31 -3
- package/lib/server/onboarding/github.js +21 -2
- package/lib/server/onboarding/index.js +1 -3
- package/lib/server/onboarding/openclaw.js +3 -0
- package/lib/server/onboarding/workspace.js +40 -0
- package/lib/server/routes/browse/index.js +90 -2
- package/lib/server/routes/gmail.js +128 -0
- package/lib/server/routes/google.js +433 -213
- package/lib/server/routes/system.js +107 -0
- package/lib/server/routes/usage.js +29 -2
- package/lib/server/routes/webhooks.js +52 -17
- package/lib/server/usage-db.js +283 -15
- package/lib/server/watchdog.js +66 -0
- package/lib/server/webhook-middleware.js +99 -1
- package/lib/server/webhooks.js +214 -65
- package/lib/server.js +27 -0
- package/lib/setup/gitignore +6 -0
- package/lib/setup/hourly-git-sync.sh +29 -2
- package/package.json +1 -1
- package/lib/public/js/components/google.js +0 -228
- package/lib/public/js/components/usage-tab.js +0 -531
package/bin/alphaclaw.js
CHANGED
|
@@ -626,37 +626,7 @@ try {
|
|
|
626
626
|
} catch {}
|
|
627
627
|
|
|
628
628
|
// ---------------------------------------------------------------------------
|
|
629
|
-
// 10.
|
|
630
|
-
// ---------------------------------------------------------------------------
|
|
631
|
-
|
|
632
|
-
if (process.env.GOG_CLIENT_CREDENTIALS_JSON && process.env.GOG_REFRESH_TOKEN) {
|
|
633
|
-
try {
|
|
634
|
-
const tmpCreds = `/tmp/gog-creds-${process.pid}.json`;
|
|
635
|
-
const tmpToken = `/tmp/gog-token-${process.pid}.json`;
|
|
636
|
-
fs.writeFileSync(tmpCreds, process.env.GOG_CLIENT_CREDENTIALS_JSON);
|
|
637
|
-
execSync(`gog auth credentials set "${tmpCreds}"`, { stdio: "ignore" });
|
|
638
|
-
fs.unlinkSync(tmpCreds);
|
|
639
|
-
fs.writeFileSync(
|
|
640
|
-
tmpToken,
|
|
641
|
-
JSON.stringify({
|
|
642
|
-
email: process.env.GOG_ACCOUNT || "",
|
|
643
|
-
refresh_token: process.env.GOG_REFRESH_TOKEN,
|
|
644
|
-
}),
|
|
645
|
-
);
|
|
646
|
-
execSync(`gog auth tokens import "${tmpToken}"`, { stdio: "ignore" });
|
|
647
|
-
fs.unlinkSync(tmpToken);
|
|
648
|
-
console.log(
|
|
649
|
-
`[alphaclaw] gog CLI configured for ${process.env.GOG_ACCOUNT || "account"}`,
|
|
650
|
-
);
|
|
651
|
-
} catch (e) {
|
|
652
|
-
console.log(`[alphaclaw] gog credentials setup skipped: ${e.message}`);
|
|
653
|
-
}
|
|
654
|
-
} else {
|
|
655
|
-
console.log("[alphaclaw] Google credentials not set -- skipping gog setup");
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
// ---------------------------------------------------------------------------
|
|
659
|
-
// 11. Reconcile channels if already onboarded
|
|
629
|
+
// 10. Reconcile channels if already onboarded
|
|
660
630
|
// ---------------------------------------------------------------------------
|
|
661
631
|
|
|
662
632
|
const configPath = path.join(openclawDir, "openclaw.json");
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img" aria-labelledby="title">
|
|
2
|
+
<title>Google</title>
|
|
3
|
+
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/>
|
|
4
|
+
<path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
|
|
5
|
+
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/>
|
|
6
|
+
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
|
|
7
|
+
<path d="M1 1h22v22H1z" fill="none"/>
|
|
8
|
+
</svg>
|
|
@@ -216,6 +216,28 @@
|
|
|
216
216
|
background: var(--bg-hover);
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
+
.tree-folder.active {
|
|
220
|
+
background: var(--bg-active);
|
|
221
|
+
color: var(--accent);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.tree-folder.active .arrow {
|
|
225
|
+
color: var(--accent);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.tree-folder-toggle {
|
|
229
|
+
border: 0;
|
|
230
|
+
margin: 0;
|
|
231
|
+
padding: 0;
|
|
232
|
+
background: transparent;
|
|
233
|
+
color: inherit;
|
|
234
|
+
display: inline-flex;
|
|
235
|
+
align-items: center;
|
|
236
|
+
justify-content: center;
|
|
237
|
+
cursor: pointer;
|
|
238
|
+
flex: 0 0 auto;
|
|
239
|
+
}
|
|
240
|
+
|
|
219
241
|
.arrow {
|
|
220
242
|
font-size: 10px;
|
|
221
243
|
transition: transform 0.15s;
|
|
@@ -565,6 +587,37 @@
|
|
|
565
587
|
flex-shrink: 0;
|
|
566
588
|
}
|
|
567
589
|
|
|
590
|
+
.file-viewer-icon-action {
|
|
591
|
+
display: inline-flex;
|
|
592
|
+
align-items: center;
|
|
593
|
+
justify-content: center;
|
|
594
|
+
width: 28px;
|
|
595
|
+
height: 28px;
|
|
596
|
+
margin-right: 10px;
|
|
597
|
+
border: 1px solid var(--border);
|
|
598
|
+
border-radius: 8px;
|
|
599
|
+
background: rgba(255, 255, 255, 0.02);
|
|
600
|
+
color: var(--text-muted);
|
|
601
|
+
transition: border-color 0.12s ease, color 0.12s ease, background 0.12s ease;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.file-viewer-icon-action:hover {
|
|
605
|
+
border-color: rgba(148, 163, 184, 0.45);
|
|
606
|
+
color: var(--text);
|
|
607
|
+
background: rgba(148, 163, 184, 0.08);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.file-viewer-icon-action.is-disabled {
|
|
611
|
+
opacity: 0.45;
|
|
612
|
+
cursor: not-allowed;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.file-viewer-icon-action-icon {
|
|
616
|
+
width: 14px;
|
|
617
|
+
height: 14px;
|
|
618
|
+
flex-shrink: 0;
|
|
619
|
+
}
|
|
620
|
+
|
|
568
621
|
.file-viewer-view-toggle {
|
|
569
622
|
display: flex;
|
|
570
623
|
align-items: center;
|
package/lib/public/css/shell.css
CHANGED
|
@@ -11,35 +11,37 @@
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
.global-restart-banner {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
position: fixed;
|
|
15
|
+
left: 50%;
|
|
16
|
+
bottom: 52px;
|
|
17
|
+
transform: translateX(-50%);
|
|
18
|
+
width: auto;
|
|
19
|
+
max-width: calc(100vw - 32px);
|
|
20
|
+
z-index: 40;
|
|
21
|
+
pointer-events: none;
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
.global-restart-banner__content {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
border: 1px solid rgba(234, 179, 8, 0.35);
|
|
26
|
+
background: rgba(43, 32, 6, 0.95);
|
|
27
|
+
box-shadow: 0 18px 46px rgba(0, 0, 0, 0.42);
|
|
28
|
+
border-radius: 14px;
|
|
29
|
+
padding: 10px 14px;
|
|
25
30
|
display: flex;
|
|
26
31
|
align-items: center;
|
|
27
|
-
justify-content:
|
|
32
|
+
justify-content: space-between;
|
|
28
33
|
gap: 12px;
|
|
29
|
-
|
|
34
|
+
pointer-events: auto;
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
.global-restart-banner__text {
|
|
33
38
|
font-size: 12px;
|
|
34
39
|
color: #fde68a;
|
|
35
|
-
|
|
40
|
+
line-height: 1.4;
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
.global-restart-banner__button {
|
|
39
|
-
|
|
40
|
-
right: 0;
|
|
41
|
-
top: 50%;
|
|
42
|
-
transform: translateY(-50%);
|
|
44
|
+
flex-shrink: 0;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
.app-content {
|
|
@@ -292,13 +294,13 @@
|
|
|
292
294
|
grid-template-rows: auto 1fr 24px;
|
|
293
295
|
}
|
|
294
296
|
.global-restart-banner {
|
|
295
|
-
|
|
297
|
+
max-width: calc(100vw - 20px);
|
|
298
|
+
bottom: 44px;
|
|
296
299
|
}
|
|
297
300
|
.global-restart-banner__content {
|
|
298
|
-
|
|
299
|
-
align-items: flex-start;
|
|
301
|
+
align-items: stretch;
|
|
300
302
|
flex-direction: column;
|
|
301
|
-
|
|
303
|
+
gap: 8px;
|
|
302
304
|
}
|
|
303
305
|
.global-restart-banner__text {
|
|
304
306
|
text-align: left;
|
package/lib/public/css/theme.css
CHANGED
|
@@ -66,6 +66,13 @@ body::before {
|
|
|
66
66
|
background: rgba(0, 0, 0, 0.12);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/* Shared inset panel for "surface on surface" layouts. */
|
|
70
|
+
.ac-surface-inset {
|
|
71
|
+
border: 1px solid var(--panel-border-contrast);
|
|
72
|
+
border-radius: 10px;
|
|
73
|
+
background: rgba(0, 0, 0, 0.12);
|
|
74
|
+
}
|
|
75
|
+
|
|
69
76
|
.ac-history-summary {
|
|
70
77
|
cursor: pointer;
|
|
71
78
|
list-style: none;
|
|
@@ -116,6 +123,16 @@ body::before {
|
|
|
116
123
|
border-color: var(--panel-border-contrast) !important;
|
|
117
124
|
}
|
|
118
125
|
|
|
126
|
+
.ac-tip-link {
|
|
127
|
+
color: var(--accent-link);
|
|
128
|
+
text-decoration: underline;
|
|
129
|
+
text-underline-offset: 2px;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.ac-tip-link:hover {
|
|
133
|
+
color: var(--accent);
|
|
134
|
+
}
|
|
135
|
+
|
|
119
136
|
/* Universal field contrast treatment (all tabs/pages). */
|
|
120
137
|
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
|
|
121
138
|
select,
|
package/lib/public/js/app.js
CHANGED
|
@@ -28,7 +28,7 @@ import { Gateway } from "./components/gateway.js";
|
|
|
28
28
|
import { Channels, ALL_CHANNELS } from "./components/channels.js";
|
|
29
29
|
import { Pairings } from "./components/pairings.js";
|
|
30
30
|
import { DevicePairings } from "./components/device-pairings.js";
|
|
31
|
-
import { Google } from "./components/google.js";
|
|
31
|
+
import { Google } from "./components/google/index.js";
|
|
32
32
|
import { Features } from "./components/features.js";
|
|
33
33
|
import { Providers } from "./components/providers.js";
|
|
34
34
|
import { Welcome } from "./components/welcome.js";
|
|
@@ -43,7 +43,7 @@ import { LoadingSpinner } from "./components/loading-spinner.js";
|
|
|
43
43
|
import { WatchdogTab } from "./components/watchdog-tab.js";
|
|
44
44
|
import { FileViewer } from "./components/file-viewer/index.js";
|
|
45
45
|
import { AppSidebar } from "./components/sidebar.js";
|
|
46
|
-
import { UsageTab } from "./components/usage-tab.js";
|
|
46
|
+
import { UsageTab } from "./components/usage-tab/index.js";
|
|
47
47
|
import { readUiSettings, writeUiSettings } from "./lib/ui-settings.js";
|
|
48
48
|
const html = htm.bind(h);
|
|
49
49
|
const kDefaultUiTab = "general";
|
|
@@ -52,6 +52,32 @@ const kSidebarMinWidthPx = 180;
|
|
|
52
52
|
const kSidebarMaxWidthPx = 460;
|
|
53
53
|
const kBrowseLastPathUiSettingKey = "browseLastPath";
|
|
54
54
|
const kLastMenuRouteUiSettingKey = "lastMenuRoute";
|
|
55
|
+
const kBrowseRestartRequiredRules = [
|
|
56
|
+
{ type: "file", path: "openclaw.json" },
|
|
57
|
+
{ type: "directory", path: "hooks/transforms" },
|
|
58
|
+
];
|
|
59
|
+
const normalizeBrowsePath = (value) => String(value || "").replace(/^\/+|\/+$/g, "");
|
|
60
|
+
const normalizeRestartRulePath = (value) =>
|
|
61
|
+
String(value || "")
|
|
62
|
+
.trim()
|
|
63
|
+
.replace(/^\/+|\/+$/g, "");
|
|
64
|
+
const matchesBrowseRestartRequiredRule = (path, rule) => {
|
|
65
|
+
const normalizedPath = normalizeRestartRulePath(path);
|
|
66
|
+
if (!normalizedPath) return false;
|
|
67
|
+
if (!rule || typeof rule !== "object") return false;
|
|
68
|
+
const type = String(rule.type || "").toLowerCase();
|
|
69
|
+
const targetPath = normalizeRestartRulePath(rule.path);
|
|
70
|
+
if (!targetPath) return false;
|
|
71
|
+
if (type === "directory") {
|
|
72
|
+
return normalizedPath === targetPath || normalizedPath.startsWith(`${targetPath}/`);
|
|
73
|
+
}
|
|
74
|
+
if (type === "file") {
|
|
75
|
+
return normalizedPath === targetPath;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
};
|
|
79
|
+
const shouldRequireRestartForBrowsePath = (path) =>
|
|
80
|
+
kBrowseRestartRequiredRules.some((rule) => matchesBrowseRestartRequiredRule(path, rule));
|
|
55
81
|
|
|
56
82
|
const clampSidebarWidth = (value) =>
|
|
57
83
|
Math.max(kSidebarMinWidthPx, Math.min(kSidebarMaxWidthPx, value));
|
|
@@ -98,6 +124,7 @@ const GeneralTab = ({
|
|
|
98
124
|
onRefreshStatuses = () => {},
|
|
99
125
|
onSwitchTab,
|
|
100
126
|
onNavigate,
|
|
127
|
+
onOpenGmailWebhook = () => {},
|
|
101
128
|
isActive,
|
|
102
129
|
restartingGateway,
|
|
103
130
|
onRestartGateway,
|
|
@@ -105,8 +132,8 @@ const GeneralTab = ({
|
|
|
105
132
|
openclawUpdateInProgress = false,
|
|
106
133
|
onOpenclawVersionActionComplete = () => {},
|
|
107
134
|
onOpenclawUpdate,
|
|
135
|
+
onRestartRequired = () => {},
|
|
108
136
|
}) => {
|
|
109
|
-
const [googleKey, setGoogleKey] = useState(0);
|
|
110
137
|
const [dashboardLoading, setDashboardLoading] = useState(false);
|
|
111
138
|
const [repairingWatchdog, setRepairingWatchdog] = useState(false);
|
|
112
139
|
const status = statusData;
|
|
@@ -179,7 +206,6 @@ const GeneralTab = ({
|
|
|
179
206
|
onRefreshStatuses();
|
|
180
207
|
pairingsPoll.refresh();
|
|
181
208
|
devicePoll.refresh();
|
|
182
|
-
setGoogleKey((k) => k + 1);
|
|
183
209
|
}, [isActive]);
|
|
184
210
|
|
|
185
211
|
useEffect(() => {
|
|
@@ -280,7 +306,11 @@ const GeneralTab = ({
|
|
|
280
306
|
onReject=${handleReject}
|
|
281
307
|
/>
|
|
282
308
|
<${Features} onSwitchTab=${onSwitchTab} />
|
|
283
|
-
<${Google}
|
|
309
|
+
<${Google}
|
|
310
|
+
gatewayStatus=${gatewayStatus}
|
|
311
|
+
onRestartRequired=${onRestartRequired}
|
|
312
|
+
onOpenGmailWebhook=${onOpenGmailWebhook}
|
|
313
|
+
/>
|
|
284
314
|
|
|
285
315
|
${repo &&
|
|
286
316
|
html`
|
|
@@ -421,11 +451,13 @@ const App = () => {
|
|
|
421
451
|
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
|
|
422
452
|
const [mobileTopbarScrolled, setMobileTopbarScrolled] = useState(false);
|
|
423
453
|
const [restartRequired, setRestartRequired] = useState(false);
|
|
454
|
+
const [browseRestartRequired, setBrowseRestartRequired] = useState(false);
|
|
424
455
|
const [restartingGateway, setRestartingGateway] = useState(false);
|
|
425
456
|
const [gatewayRestartSignal, setGatewayRestartSignal] = useState(0);
|
|
426
457
|
const [statusPollCadenceMs, setStatusPollCadenceMs] = useState(15000);
|
|
427
458
|
const [openclawUpdateInProgress, setOpenclawUpdateInProgress] = useState(false);
|
|
428
459
|
const menuRef = useRef(null);
|
|
460
|
+
const routeHistoryRef = useRef([]);
|
|
429
461
|
const sharedStatusPoll = usePolling(fetchStatus, statusPollCadenceMs, {
|
|
430
462
|
enabled: onboarded === true,
|
|
431
463
|
});
|
|
@@ -434,6 +466,7 @@ const App = () => {
|
|
|
434
466
|
});
|
|
435
467
|
const sharedStatus = sharedStatusPoll.data || null;
|
|
436
468
|
const sharedWatchdogStatus = sharedWatchdogPoll.data?.status || null;
|
|
469
|
+
const isAnyRestartRequired = restartRequired || browseRestartRequired;
|
|
437
470
|
const refreshSharedStatuses = useCallback(() => {
|
|
438
471
|
sharedStatusPoll.refresh();
|
|
439
472
|
sharedWatchdogPoll.refresh();
|
|
@@ -537,6 +570,18 @@ const App = () => {
|
|
|
537
570
|
return () => clearInterval(id);
|
|
538
571
|
}, [onboarded, restartRequired, restartingGateway, refreshRestartStatus]);
|
|
539
572
|
|
|
573
|
+
useEffect(() => {
|
|
574
|
+
const handleBrowseFileSaved = (event) => {
|
|
575
|
+
const savedPath = String(event?.detail?.path || "");
|
|
576
|
+
if (!shouldRequireRestartForBrowsePath(savedPath)) return;
|
|
577
|
+
setBrowseRestartRequired(true);
|
|
578
|
+
};
|
|
579
|
+
window.addEventListener("alphaclaw:browse-file-saved", handleBrowseFileSaved);
|
|
580
|
+
return () => {
|
|
581
|
+
window.removeEventListener("alphaclaw:browse-file-saved", handleBrowseFileSaved);
|
|
582
|
+
};
|
|
583
|
+
}, []);
|
|
584
|
+
|
|
540
585
|
const handleGatewayRestart = useCallback(async () => {
|
|
541
586
|
if (restartingGateway) return;
|
|
542
587
|
setRestartingGateway(true);
|
|
@@ -544,6 +589,7 @@ const App = () => {
|
|
|
544
589
|
const data = await restartGateway();
|
|
545
590
|
if (!data?.ok) throw new Error(data?.error || "Gateway restart failed");
|
|
546
591
|
setRestartRequired(!!data.restartRequired);
|
|
592
|
+
setBrowseRestartRequired(false);
|
|
547
593
|
setGatewayRestartSignal(Date.now());
|
|
548
594
|
refreshSharedStatuses();
|
|
549
595
|
showToast("Gateway restarted", "success");
|
|
@@ -646,9 +692,27 @@ const App = () => {
|
|
|
646
692
|
setLocation(`/${screen}`);
|
|
647
693
|
setMobileSidebarOpen(false);
|
|
648
694
|
};
|
|
695
|
+
const handleBrowsePreviewFile = useCallback((nextPreviewPath) => {
|
|
696
|
+
const normalizedPreviewPath = normalizeBrowsePath(nextPreviewPath);
|
|
697
|
+
setBrowsePreviewPath(normalizedPreviewPath);
|
|
698
|
+
}, []);
|
|
649
699
|
const navigateToBrowseFile = (relativePath, options = {}) => {
|
|
650
|
-
|
|
651
|
-
|
|
700
|
+
const normalizedTargetPath = normalizeBrowsePath(relativePath);
|
|
701
|
+
const selectingDirectory =
|
|
702
|
+
!!options.directory || String(relativePath || "").trim().endsWith("/");
|
|
703
|
+
const shouldPreservePreview = selectingDirectory && !!options.preservePreview;
|
|
704
|
+
const activePath = normalizeBrowsePath(
|
|
705
|
+
browsePreviewPath || selectedBrowsePath || "",
|
|
706
|
+
);
|
|
707
|
+
const nextPreviewPath =
|
|
708
|
+
shouldPreservePreview && activePath && activePath !== normalizedTargetPath
|
|
709
|
+
? activePath
|
|
710
|
+
: "";
|
|
711
|
+
setBrowsePreviewPath(nextPreviewPath);
|
|
712
|
+
const routeOptions = selectingDirectory
|
|
713
|
+
? { ...options, view: "edit" }
|
|
714
|
+
: options;
|
|
715
|
+
setLocation(buildBrowseRoute(normalizedTargetPath, routeOptions));
|
|
652
716
|
setMobileSidebarOpen(false);
|
|
653
717
|
};
|
|
654
718
|
const handleSidebarLogout = async () => {
|
|
@@ -765,6 +829,16 @@ const App = () => {
|
|
|
765
829
|
setBrowsePreviewPath("");
|
|
766
830
|
}, [location]);
|
|
767
831
|
|
|
832
|
+
useEffect(() => {
|
|
833
|
+
const historyStack = routeHistoryRef.current;
|
|
834
|
+
const lastEntry = historyStack[historyStack.length - 1];
|
|
835
|
+
if (lastEntry === location) return;
|
|
836
|
+
historyStack.push(location);
|
|
837
|
+
if (historyStack.length > 100) {
|
|
838
|
+
historyStack.shift();
|
|
839
|
+
}
|
|
840
|
+
}, [location]);
|
|
841
|
+
|
|
768
842
|
useEffect(() => {
|
|
769
843
|
if (location.startsWith("/browse")) return;
|
|
770
844
|
if (location === "/telegram") return;
|
|
@@ -848,8 +922,24 @@ const App = () => {
|
|
|
848
922
|
<${Webhooks}
|
|
849
923
|
selectedHookName=${hookName}
|
|
850
924
|
onSelectHook=${(name) => setLocation(`/webhooks/${encodeURIComponent(name)}`)}
|
|
851
|
-
onBackToList=${() =>
|
|
925
|
+
onBackToList=${() => {
|
|
926
|
+
const historyStack = routeHistoryRef.current;
|
|
927
|
+
const hasPreviousRoute = historyStack.length > 1;
|
|
928
|
+
if (!hasPreviousRoute) {
|
|
929
|
+
setLocation("/webhooks");
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
const currentPath = getHashPath();
|
|
933
|
+
window.history.back();
|
|
934
|
+
window.setTimeout(() => {
|
|
935
|
+
if (getHashPath() === currentPath) {
|
|
936
|
+
setLocation("/webhooks");
|
|
937
|
+
}
|
|
938
|
+
}, 180);
|
|
939
|
+
}}
|
|
852
940
|
onRestartRequired=${setRestartRequired}
|
|
941
|
+
onOpenFile=${(relativePath) =>
|
|
942
|
+
navigateToBrowseFile(String(relativePath || "").trim(), { view: "edit" })}
|
|
853
943
|
/>
|
|
854
944
|
</div>
|
|
855
945
|
`;
|
|
@@ -861,7 +951,7 @@ const App = () => {
|
|
|
861
951
|
style=${{ "--sidebar-width": `${sidebarWidthPx}px` }}
|
|
862
952
|
>
|
|
863
953
|
<${GlobalRestartBanner}
|
|
864
|
-
visible=${
|
|
954
|
+
visible=${isAnyRestartRequired}
|
|
865
955
|
restarting=${restartingGateway}
|
|
866
956
|
onRestart=${handleGatewayRestart}
|
|
867
957
|
/>
|
|
@@ -879,7 +969,7 @@ const App = () => {
|
|
|
879
969
|
onSelectNavItem=${handleSelectNavItem}
|
|
880
970
|
selectedBrowsePath=${selectedBrowsePath}
|
|
881
971
|
onSelectBrowseFile=${navigateToBrowseFile}
|
|
882
|
-
onPreviewBrowseFile=${
|
|
972
|
+
onPreviewBrowseFile=${handleBrowsePreviewFile}
|
|
883
973
|
acHasUpdate=${acHasUpdate}
|
|
884
974
|
acLatest=${acLatest}
|
|
885
975
|
acDismissed=${acDismissed}
|
|
@@ -921,115 +1011,121 @@ const App = () => {
|
|
|
921
1011
|
</span>
|
|
922
1012
|
</div>
|
|
923
1013
|
<div class=${isBrowseRoute ? "w-full" : "max-w-2xl w-full mx-auto"}>
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1014
|
+
<div style=${{ display: isBrowseRoute ? "block" : "none" }}>
|
|
1015
|
+
<${FileViewer}
|
|
1016
|
+
filePath=${activeBrowsePath}
|
|
1017
|
+
isPreviewOnly=${false}
|
|
1018
|
+
browseView=${browseViewerMode}
|
|
1019
|
+
onRequestEdit=${(targetPath) => {
|
|
1020
|
+
const normalizedTargetPath = String(targetPath || "");
|
|
1021
|
+
if (
|
|
1022
|
+
normalizedTargetPath &&
|
|
1023
|
+
normalizedTargetPath !== selectedBrowsePath
|
|
1024
|
+
) {
|
|
1025
|
+
navigateToBrowseFile(normalizedTargetPath, { view: "edit" });
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
setLocation(buildBrowseRoute(selectedBrowsePath, { view: "edit" }));
|
|
1029
|
+
}}
|
|
1030
|
+
onRequestClearSelection=${() => {
|
|
1031
|
+
setBrowsePreviewPath("");
|
|
1032
|
+
setLocation("/browse");
|
|
1033
|
+
}}
|
|
1034
|
+
/>
|
|
1035
|
+
</div>
|
|
1036
|
+
<div style=${{ display: isBrowseRoute ? "none" : "block" }}>
|
|
1037
|
+
<div style=${{ display: location === "/general" ? "block" : "none" }}>
|
|
1038
|
+
<div class="pt-4">
|
|
1039
|
+
<${GeneralTab}
|
|
1040
|
+
statusData=${sharedStatus}
|
|
1041
|
+
watchdogData=${sharedWatchdogStatus}
|
|
1042
|
+
onRefreshStatuses=${refreshSharedStatuses}
|
|
1043
|
+
onSwitchTab=${(nextTab) => setLocation(`/${nextTab}`)}
|
|
1044
|
+
onNavigate=${navigateToSubScreen}
|
|
1045
|
+
onOpenGmailWebhook=${() => setLocation("/webhooks/gmail")}
|
|
1046
|
+
isActive=${location === "/general"}
|
|
1047
|
+
restartingGateway=${restartingGateway}
|
|
1048
|
+
onRestartGateway=${handleGatewayRestart}
|
|
1049
|
+
restartSignal=${gatewayRestartSignal}
|
|
1050
|
+
openclawUpdateInProgress=${openclawUpdateInProgress}
|
|
1051
|
+
onOpenclawVersionActionComplete=${handleOpenclawVersionActionComplete}
|
|
1052
|
+
onOpenclawUpdate=${handleOpenclawUpdate}
|
|
1053
|
+
onRestartRequired=${setRestartRequired}
|
|
944
1054
|
/>
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
/>
|
|
990
|
-
</div>
|
|
991
|
-
</${Route}>
|
|
992
|
-
<${Route} path="/usage/:sessionId">
|
|
993
|
-
${(params) => html`
|
|
1055
|
+
</div>
|
|
1056
|
+
</div>
|
|
1057
|
+
${!isBrowseRoute && location !== "/general"
|
|
1058
|
+
? html`
|
|
1059
|
+
<${Switch}>
|
|
1060
|
+
<${Route} path="/telegram">
|
|
1061
|
+
<div class="pt-4">
|
|
1062
|
+
<${TelegramWorkspace} onBack=${exitSubScreen} />
|
|
1063
|
+
</div>
|
|
1064
|
+
</${Route}>
|
|
1065
|
+
<${Route} path="/providers">
|
|
1066
|
+
<div class="pt-4">
|
|
1067
|
+
<${Providers} onRestartRequired=${setRestartRequired} />
|
|
1068
|
+
</div>
|
|
1069
|
+
</${Route}>
|
|
1070
|
+
<${Route} path="/watchdog">
|
|
1071
|
+
<div class="pt-4">
|
|
1072
|
+
<${WatchdogTab}
|
|
1073
|
+
gatewayStatus=${sharedStatus?.gateway || null}
|
|
1074
|
+
openclawVersion=${sharedStatus?.openclawVersion || null}
|
|
1075
|
+
watchdogStatus=${sharedWatchdogStatus}
|
|
1076
|
+
onRefreshStatuses=${refreshSharedStatuses}
|
|
1077
|
+
restartingGateway=${restartingGateway}
|
|
1078
|
+
onRestartGateway=${handleGatewayRestart}
|
|
1079
|
+
restartSignal=${gatewayRestartSignal}
|
|
1080
|
+
openclawUpdateInProgress=${openclawUpdateInProgress}
|
|
1081
|
+
onOpenclawVersionActionComplete=${handleOpenclawVersionActionComplete}
|
|
1082
|
+
onOpenclawUpdate=${handleOpenclawUpdate}
|
|
1083
|
+
/>
|
|
1084
|
+
</div>
|
|
1085
|
+
</${Route}>
|
|
1086
|
+
<${Route} path="/usage/:sessionId">
|
|
1087
|
+
${(params) => html`
|
|
1088
|
+
<div class="pt-4">
|
|
1089
|
+
<${UsageTab}
|
|
1090
|
+
sessionId=${decodeURIComponent(params.sessionId || "")}
|
|
1091
|
+
onSelectSession=${(id) =>
|
|
1092
|
+
setLocation(`/usage/${encodeURIComponent(String(id || ""))}`)}
|
|
1093
|
+
onBackToSessions=${() => setLocation("/usage")}
|
|
1094
|
+
/>
|
|
1095
|
+
</div>
|
|
1096
|
+
`}
|
|
1097
|
+
</${Route}>
|
|
1098
|
+
<${Route} path="/usage">
|
|
994
1099
|
<div class="pt-4">
|
|
995
1100
|
<${UsageTab}
|
|
996
|
-
sessionId=${decodeURIComponent(params.sessionId || "")}
|
|
997
1101
|
onSelectSession=${(id) =>
|
|
998
1102
|
setLocation(`/usage/${encodeURIComponent(String(id || ""))}`)}
|
|
999
1103
|
onBackToSessions=${() => setLocation("/usage")}
|
|
1000
1104
|
/>
|
|
1001
1105
|
</div>
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
<${Route} path="/webhooks">
|
|
1023
|
-
${() => renderWebhooks("")}
|
|
1024
|
-
</${Route}>
|
|
1025
|
-
<${Route}>
|
|
1026
|
-
<${RouteRedirect} to="/general" />
|
|
1027
|
-
</${Route}>
|
|
1028
|
-
</${Switch}>
|
|
1029
|
-
`}
|
|
1106
|
+
</${Route}>
|
|
1107
|
+
<${Route} path="/envars">
|
|
1108
|
+
<div class="pt-4">
|
|
1109
|
+
<${Envars} onRestartRequired=${setRestartRequired} />
|
|
1110
|
+
</div>
|
|
1111
|
+
</${Route}>
|
|
1112
|
+
<${Route} path="/webhooks/:hookName">
|
|
1113
|
+
${(params) =>
|
|
1114
|
+
renderWebhooks(decodeURIComponent(params.hookName || ""))}
|
|
1115
|
+
</${Route}>
|
|
1116
|
+
<${Route} path="/webhooks">
|
|
1117
|
+
${() => renderWebhooks("")}
|
|
1118
|
+
</${Route}>
|
|
1119
|
+
<${Route}>
|
|
1120
|
+
<${RouteRedirect} to="/general" />
|
|
1121
|
+
</${Route}>
|
|
1122
|
+
</${Switch}>
|
|
1123
|
+
`
|
|
1124
|
+
: null}
|
|
1125
|
+
</div>
|
|
1030
1126
|
</div>
|
|
1031
1127
|
<${ToastContainer}
|
|
1032
|
-
className="fixed
|
|
1128
|
+
className="fixed top-4 right-4 z-50 space-y-2 pointer-events-none"
|
|
1033
1129
|
/>
|
|
1034
1130
|
</div>
|
|
1035
1131
|
|