@chrysb/alphaclaw 0.9.0-beta.6 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/bin/alphaclaw.js +25 -25
  2. package/lib/cli/git-runtime.js +97 -0
  3. package/lib/public/css/chat.css +0 -12
  4. package/lib/public/css/explorer.css +48 -0
  5. package/lib/public/css/shell.css +149 -0
  6. package/lib/public/css/tailwind.generated.css +1 -1
  7. package/lib/public/css/theme.css +265 -0
  8. package/lib/public/dist/app.bundle.js +2770 -2762
  9. package/lib/public/js/app.js +26 -14
  10. package/lib/public/js/components/agents-tab/create-channel-modal.js +259 -59
  11. package/lib/public/js/components/gateway.js +0 -286
  12. package/lib/public/js/components/general/index.js +0 -7
  13. package/lib/public/js/components/icons.js +26 -25
  14. package/lib/public/js/components/modal-shell.js +1 -1
  15. package/lib/public/js/components/models-tab/provider-auth-card.js +60 -49
  16. package/lib/public/js/components/models-tab/use-models.js +74 -9
  17. package/lib/public/js/components/models.js +52 -37
  18. package/lib/public/js/components/onboarding/use-welcome-codex.js +34 -24
  19. package/lib/public/js/components/onboarding/welcome-config.js +76 -10
  20. package/lib/public/js/components/onboarding/welcome-form-step.js +2 -7
  21. package/lib/public/js/components/onboarding/welcome-header.js +12 -14
  22. package/lib/public/js/components/onboarding/welcome-setup-step.js +3 -3
  23. package/lib/public/js/components/providers.js +53 -42
  24. package/lib/public/js/components/routes/chat-route.js +2 -9
  25. package/lib/public/js/components/routes/general-route.js +0 -6
  26. package/lib/public/js/components/routes/index.js +0 -1
  27. package/lib/public/js/components/routes/watchdog-route.js +0 -6
  28. package/lib/public/js/components/sidebar.js +21 -7
  29. package/lib/public/js/components/theme-toggle.js +113 -0
  30. package/lib/public/js/components/update-modal.js +174 -51
  31. package/lib/public/js/components/watchdog-tab/index.js +0 -6
  32. package/lib/public/js/components/welcome/index.js +0 -2
  33. package/lib/public/js/components/welcome/use-welcome.js +101 -36
  34. package/lib/public/js/hooks/use-app-shell-controller.js +16 -33
  35. package/lib/public/js/lib/api.js +0 -28
  36. package/lib/public/js/lib/app-navigation.js +0 -2
  37. package/lib/public/js/lib/channel-provider-availability.js +1 -2
  38. package/lib/public/js/lib/codex-oauth-window.js +22 -0
  39. package/lib/public/js/lib/model-catalog.js +20 -0
  40. package/lib/public/js/lib/storage-keys.js +1 -1
  41. package/lib/public/login.html +8 -4
  42. package/lib/public/setup.html +9 -0
  43. package/lib/scripts/git +47 -1
  44. package/lib/server/agents/channels.js +1 -4
  45. package/lib/server/alphaclaw-version.js +590 -132
  46. package/lib/server/constants.js +5 -0
  47. package/lib/server/db/webhooks/index.js +48 -8
  48. package/lib/server/exec-defaults-config.js +163 -0
  49. package/lib/server/init/register-server-routes.js +0 -8
  50. package/lib/server/init/server-lifecycle.js +2 -0
  51. package/lib/server/model-catalog-cache.js +251 -0
  52. package/lib/server/onboarding/index.js +5 -0
  53. package/lib/server/routes/models.js +14 -23
  54. package/lib/server/routes/nodes.js +9 -23
  55. package/lib/server/routes/system.js +3 -16
  56. package/lib/server/routes/webhooks.js +12 -1
  57. package/lib/server/startup.js +8 -0
  58. package/lib/server/watchdog-notify.js +172 -55
  59. package/lib/server.js +17 -2
  60. package/package.json +2 -2
  61. package/patches/openclaw+2026.4.9.patch +13 -0
  62. package/lib/public/js/components/mcp-tab/index.js +0 -237
  63. package/lib/public/js/components/routes/mcp-route.js +0 -7
  64. package/lib/server/mcp-bridge.js +0 -158
  65. package/lib/server/routes/mcp.js +0 -252
  66. package/patches/openclaw+2026.3.28.patch +0 -13
@@ -9,6 +9,7 @@ import {
9
9
  } from "wouter-preact";
10
10
  import { logout } from "./lib/api.js";
11
11
  import { Welcome } from "./components/welcome/index.js";
12
+ import { ThemeToggle } from "./components/theme-toggle.js";
12
13
  import { ToastContainer } from "./components/toast.js";
13
14
  import { GlobalRestartBanner } from "./components/global-restart-banner.js";
14
15
  import { LoadingSpinner } from "./components/loading-spinner.js";
@@ -21,7 +22,6 @@ import {
21
22
  DoctorRoute,
22
23
  EnvarsRoute,
23
24
  GeneralRoute,
24
- McpRoute,
25
25
  ModelsRoute,
26
26
  NodesRoute,
27
27
  RouteRedirect,
@@ -105,6 +105,22 @@ const App = () => {
105
105
  } = useAgentSessions({
106
106
  enabled: controllerState.onboarded === true,
107
107
  });
108
+ const footerVersion = (() => {
109
+ const openclawVersion = String(
110
+ controllerState.acCurrentOpenclawVersion || "",
111
+ ).trim();
112
+ const alphaclawVersion = String(controllerState.acVersion || "").trim();
113
+ if (openclawVersion && alphaclawVersion) {
114
+ return `OpenClaw ${openclawVersion} / AlphaClaw ${alphaclawVersion}`;
115
+ }
116
+ if (openclawVersion) {
117
+ return `OpenClaw ${openclawVersion}`;
118
+ }
119
+ if (alphaclawVersion) {
120
+ return `AlphaClaw ${alphaclawVersion}`;
121
+ }
122
+ return null;
123
+ })();
108
124
 
109
125
  useEffect(() => {
110
126
  if (!isAgentsRoute) return;
@@ -161,6 +177,9 @@ const App = () => {
161
177
  class="min-h-screen flex flex-col items-center pt-12 pb-8 px-4"
162
178
  style="position: relative; z-index: 1"
163
179
  >
180
+ <div style="position: fixed; top: 16px; right: 16px; z-index: 50;">
181
+ <${ThemeToggle} />
182
+ </div>
164
183
  <${Welcome}
165
184
  onComplete=${controllerActions.handleOnboardingComplete}
166
185
  acVersion=${controllerState.acVersion}
@@ -198,7 +217,11 @@ const App = () => {
198
217
  onSelectBrowseFile=${browseActions.navigateToBrowseFile}
199
218
  onPreviewBrowseFile=${browseActions.handleBrowsePreviewFile}
200
219
  acHasUpdate=${controllerState.acHasUpdate}
220
+ acVersion=${controllerState.acVersion}
221
+ acCurrentOpenclawVersion=${controllerState.acCurrentOpenclawVersion}
201
222
  acLatest=${controllerState.acLatest}
223
+ acLatestOpenclawVersion=${controllerState.acLatestOpenclawVersion}
224
+ acUpdateStrategy=${controllerState.acUpdateStrategy}
202
225
  acUpdating=${controllerState.acUpdating}
203
226
  onAcUpdate=${controllerActions.handleAcUpdate}
204
227
  agents=${agentsState.agents}
@@ -381,9 +404,6 @@ const App = () => {
381
404
  restartingGateway=${controllerState.restartingGateway}
382
405
  onRestartGateway=${controllerActions.handleGatewayRestart}
383
406
  restartSignal=${controllerState.gatewayRestartSignal}
384
- openclawUpdateInProgress=${controllerState.openclawUpdateInProgress}
385
- onOpenclawVersionActionComplete=${controllerActions.handleOpenclawVersionActionComplete}
386
- onOpenclawUpdate=${controllerActions.handleOpenclawUpdate}
387
407
  onRestartRequired=${controllerActions.setRestartRequired}
388
408
  onDismissDoctorWarning=${() =>
389
409
  setDoctorWarningDismissedUntilMs(
@@ -416,9 +436,6 @@ const App = () => {
416
436
  restartingGateway=${controllerState.restartingGateway}
417
437
  onRestartGateway=${controllerActions.handleGatewayRestart}
418
438
  restartSignal=${controllerState.gatewayRestartSignal}
419
- openclawUpdateInProgress=${controllerState.openclawUpdateInProgress}
420
- onOpenclawVersionActionComplete=${controllerActions.handleOpenclawVersionActionComplete}
421
- onOpenclawUpdate=${controllerActions.handleOpenclawUpdate}
422
439
  />
423
440
  </${Route}>
424
441
  <${Route} path="/usage/:sessionId">
@@ -455,9 +472,6 @@ const App = () => {
455
472
  onNavigateToBrowseFile=${browseActions.navigateToBrowseFile}
456
473
  />
457
474
  </${Route}>
458
- <${Route} path="/mcp">
459
- <${McpRoute} />
460
- </${Route}>
461
475
  <${Route}>
462
476
  <${RouteRedirect} to="/general" />
463
477
  </${Route}>
@@ -472,10 +486,8 @@ const App = () => {
472
486
 
473
487
  <div class="app-statusbar">
474
488
  <div class="statusbar-left">
475
- ${controllerState.acVersion
476
- ? html`<span style="color: var(--text-muted)"
477
- >v${controllerState.acVersion}</span
478
- >`
489
+ ${footerVersion
490
+ ? html`<span style="color: var(--text-muted)">${footerVersion}</span>`
479
491
  : null}
480
492
  </div>
481
493
  <div class="statusbar-right">
@@ -2,12 +2,15 @@ import { h } from "preact";
2
2
  import { useEffect, useMemo, useState } from "preact/hooks";
3
3
  import htm from "htm";
4
4
  import { ActionButton } from "../action-button.js";
5
- import { CloseIcon } from "../icons.js";
5
+ import { CloseIcon, FileCopyLineIcon } from "../icons.js";
6
6
  import { ModalShell } from "../modal-shell.js";
7
7
  import { PageHeader } from "../page-header.js";
8
8
  import { SecretInput } from "../secret-input.js";
9
9
  import { fetchChannelAccountToken } from "../../lib/api.js";
10
+ import { copyTextToClipboard } from "../../lib/clipboard.js";
11
+ import { isSingleAccountChannelProvider } from "../../lib/channel-provider-availability.js";
10
12
  import { ALL_CHANNELS, getChannelMeta } from "../channels.js";
13
+ import { showToast } from "../toast.js";
11
14
 
12
15
  const html = htm.bind(h);
13
16
 
@@ -25,15 +28,33 @@ const kSlackBotScopes = [
25
28
  "channels:history",
26
29
  "channels:read",
27
30
  "chat:write",
31
+ "commands",
32
+ "emoji:read",
33
+ "files:read",
34
+ "files:write",
35
+ "groups:read",
28
36
  "groups:history",
29
37
  "im:history",
30
38
  "im:read",
31
39
  "im:write",
32
40
  "mpim:history",
41
+ "mpim:read",
42
+ "mpim:write",
43
+ "pins:read",
44
+ "pins:write",
33
45
  "reactions:read",
34
46
  "reactions:write",
35
47
  "users:read",
36
48
  ];
49
+ const kSlackBotEvents = [
50
+ "app_mention",
51
+ "message.channels",
52
+ "message.groups",
53
+ "message.im",
54
+ "message.mpim",
55
+ "reaction_added",
56
+ "reaction_removed",
57
+ ];
37
58
  const kSlackInstructionsLink = "https://docs.openclaw.ai/channels/slack";
38
59
 
39
60
  const slugifyChannelAccountId = (value) =>
@@ -50,7 +71,63 @@ const deriveChannelEnvKey = ({ provider, accountId }) => {
50
71
  if (!normalizedAccountId || normalizedAccountId === "default") return baseKey;
51
72
  return `${baseKey}_${normalizedAccountId.replace(/-/g, "_").toUpperCase()}`;
52
73
  };
74
+ const deriveChannelExtraEnvKey = ({ provider, accountId, index = 0 }) => {
75
+ const baseKeys = [kChannelExtraEnvKeys[String(provider || "").trim()]].filter(
76
+ Boolean,
77
+ );
78
+ const baseKey = String(baseKeys[index] || "").trim();
79
+ const normalizedAccountId = String(accountId || "").trim();
80
+ if (!baseKey) return "";
81
+ if (!normalizedAccountId || normalizedAccountId === "default") return baseKey;
82
+ return `${baseKey}_${normalizedAccountId.replace(/-/g, "_").toUpperCase()}`;
83
+ };
53
84
  const isMaskedTokenValue = (value) => /^\*+$/.test(String(value || "").trim());
85
+ const buildSlackManifest = (appName = "AlphaClaw") =>
86
+ JSON.stringify(
87
+ {
88
+ _metadata: {
89
+ major_version: 1,
90
+ },
91
+ display_information: {
92
+ name: String(appName || "").trim() || "AlphaClaw",
93
+ description: "Slack connector for AlphaClaw",
94
+ },
95
+ features: {
96
+ bot_user: {
97
+ display_name: String(appName || "").trim() || "AlphaClaw",
98
+ always_online: false,
99
+ },
100
+ app_home: {
101
+ messages_tab_enabled: true,
102
+ messages_tab_read_only_enabled: false,
103
+ },
104
+ },
105
+ oauth_config: {
106
+ scopes: {
107
+ bot: kSlackBotScopes,
108
+ },
109
+ },
110
+ settings: {
111
+ event_subscriptions: {
112
+ bot_events: kSlackBotEvents,
113
+ },
114
+ org_deploy_enabled: false,
115
+ socket_mode_enabled: true,
116
+ is_hosted: false,
117
+ token_rotation_enabled: false,
118
+ },
119
+ },
120
+ null,
121
+ 2,
122
+ );
123
+ const copyAndToast = async (value, label = "text") => {
124
+ const copied = await copyTextToClipboard(value);
125
+ if (copied) {
126
+ showToast("Copied to clipboard", "success");
127
+ return;
128
+ }
129
+ showToast(`Could not copy ${label}`, "error");
130
+ };
54
131
 
55
132
  export const CreateChannelModal = ({
56
133
  visible = false,
@@ -152,9 +229,7 @@ export const CreateChannelModal = ({
152
229
  }
153
230
  setName(providerLabel);
154
231
  }, [provider, providerHasAccounts, nameEditedManually, isEditMode]);
155
- const isSingleAccountProvider =
156
- String(provider || "").trim() === "discord" ||
157
- String(provider || "").trim() === "slack";
232
+ const isSingleAccountProvider = isSingleAccountChannelProvider(provider);
158
233
  const needsAppToken = String(provider || "").trim() === "slack";
159
234
 
160
235
  const accountId = useMemo(() => {
@@ -170,6 +245,24 @@ export const CreateChannelModal = ({
170
245
  () => deriveChannelEnvKey({ provider, accountId }),
171
246
  [provider, accountId],
172
247
  );
248
+ const extraEnvKey = useMemo(
249
+ () =>
250
+ deriveChannelExtraEnvKey({
251
+ provider,
252
+ accountId,
253
+ }),
254
+ [provider, accountId],
255
+ );
256
+ const slackManifestName = useMemo(() => {
257
+ const normalizedName = String(name || "").trim();
258
+ if (!normalizedName) return "AlphaClaw";
259
+ if (normalizedName.toLowerCase() === "slack") return "AlphaClaw";
260
+ return normalizedName;
261
+ }, [name]);
262
+ const slackManifest = useMemo(
263
+ () => buildSlackManifest(slackManifestName),
264
+ [slackManifestName],
265
+ );
173
266
 
174
267
  const accountExists = useMemo(
175
268
  () =>
@@ -269,7 +362,7 @@ export const CreateChannelModal = ({
269
362
  <${ModalShell}
270
363
  visible=${visible}
271
364
  onClose=${onClose}
272
- panelClassName="bg-modal border border-border rounded-xl p-6 max-w-lg w-full space-y-4"
365
+ panelClassName="bg-modal border border-border rounded-xl p-6 max-w-lg w-full max-h-[calc(100vh-2rem)] overflow-y-auto space-y-4"
273
366
  >
274
367
  <${PageHeader}
275
368
  title=${
@@ -360,7 +453,7 @@ export const CreateChannelModal = ({
360
453
  <p class="text-xs text-fg-muted">
361
454
  Saved behind the scenes as
362
455
  <code class="font-mono text-fg-muted ml-1">
363
- ${kChannelExtraEnvKeys.slack}
456
+ ${extraEnvKey || kChannelExtraEnvKeys.slack}
364
457
  </code>
365
458
  .
366
459
  </p>
@@ -371,60 +464,167 @@ export const CreateChannelModal = ({
371
464
  ${
372
465
  needsAppToken
373
466
  ? html`
374
- <details class="rounded-lg border border-border bg-field px-3 py-2.5">
375
- <summary class="cursor-pointer text-xs text-body hover:text-body">
376
- Slack-specific instructions (step-by-step)
377
- </summary>
378
- <div class="mt-2 space-y-2 text-xs text-fg-muted">
379
- <ol class="list-decimal list-inside space-y-1.5">
380
- <li>
381
- In Slack app settings, turn on
382
- ${" "}
383
- <span class="text-body">Socket Mode</span>.
384
- </li>
385
- <li>
386
- In
387
- ${" "}
388
- <span class="text-body">App Home</span>, enable
389
- <code class="font-mono text-fg-muted ml-1">
390
- Allow users to send Slash commands and messages from the messages tab
391
- </code>.
392
- </li>
393
- <li>
394
- In
395
- ${" "}
396
- <span class="text-body">Event Subscriptions</span>, toggle on
397
- <code class="font-mono text-fg-muted ml-1">Subscribe to bot events</code>
398
- ${" "}
399
- and add
400
- <code class="font-mono text-fg-muted ml-1">message.im</code>.
401
- </li>
402
- <li>
403
- Create a Bot Token (<code class="font-mono text-fg-muted">xoxb-...</code>)
404
- with scopes:
405
- <code class="font-mono text-fg-muted ml-1">
406
- ${kSlackBotScopes.join(", ")}
407
- </code>
408
- </li>
409
- <li>
410
- Create an App Token (<code class="font-mono text-fg-muted">xapp-...</code>)
411
- with
412
- <code class="font-mono text-fg-muted ml-1">connections:write</code>.
413
- </li>
414
- <li>
415
- Reinstall the app after changing scopes.
416
- </li>
417
- </ol>
418
- <a
419
- href=${kSlackInstructionsLink}
420
- target="_blank"
421
- class="hover:underline"
422
- style="color: var(--accent-link)"
467
+ <div class="space-y-2">
468
+ <details
469
+ class="rounded-lg border border-border bg-field px-3 py-2.5"
470
+ >
471
+ <summary
472
+ class="cursor-pointer text-xs text-body hover:text-body"
473
+ >
474
+ <span class="inline-block ml-1">
475
+ Create app from manifest (recommended)
476
+ </span>
477
+ </summary>
478
+ <div class="mt-2 space-y-2 text-xs text-fg-muted">
479
+ <div class="flex items-center justify-between gap-3 pt-1">
480
+ <div class="space-y-0.5">
481
+ <p class="text-[12px] text-fg-muted">
482
+ ${slackManifestName} App Manifest
483
+ </p>
484
+ </div>
485
+ <button
486
+ type="button"
487
+ onclick=${() =>
488
+ copyAndToast(slackManifest, "Slack manifest")}
489
+ class="text-xs px-2 py-1 rounded-lg ac-btn-cyan inline-flex items-center gap-1.5 shrink-0"
490
+ >
491
+ <${FileCopyLineIcon} className="w-3.5 h-3.5" />
492
+ Copy
493
+ </button>
494
+ </div>
495
+ <pre
496
+ class="max-h-72 overflow-auto rounded-lg border border-border bg-field p-3 text-[11px] leading-5 whitespace-pre-wrap break-all font-mono text-body"
497
+ >
498
+ ${slackManifest}</pre
499
+ >
500
+ <ol
501
+ class="list-decimal list-inside space-y-1.5 text-[11px] text-fg-muted"
502
+ >
503
+ <li>
504
+ In Slack, click ${" "}
505
+ <span class="text-body"
506
+ >Create app from manifest</span
507
+ >
508
+ ${" "} and paste this manifest.
509
+ </li>
510
+ <li>
511
+ Open ${" "}
512
+ <span class="text-body">Basic Information</span>
513
+ ${" "} and create an ${" "}
514
+ <span class="text-body">App-Level Token</span>
515
+ ${" "} with
516
+ <code class="font-mono text-fg-muted ml-1"
517
+ >connections:write</code
518
+ >.
519
+ </li>
520
+ <li>
521
+ Open ${" "}
522
+ <span class="text-body">OAuth & Permissions</span>
523
+ ${" "} and use ${" "}
524
+ <span class="text-body">Install to Workspace</span>
525
+ ${" "} or ${" "}
526
+ <span class="text-body">Reinstall to Workspace</span>
527
+ ${" "} so Slack issues a bot token.
528
+ </li>
529
+ <li>
530
+ In ${" "}
531
+ <span class="text-body">OAuth & Permissions</span>
532
+ ${" "} copy the ${" "}
533
+ <span class="text-body">Bot User OAuth Token</span>
534
+ ${" "} (
535
+ <code class="font-mono text-fg-muted">xoxb-...</code>
536
+ ).
537
+ </li>
538
+ <li>
539
+ Paste the generated ${" "}
540
+ <code class="font-mono text-fg-muted">xoxb-...</code>
541
+ ${" "} and ${" "}
542
+ <code class="font-mono text-fg-muted">xapp-...</code>
543
+ ${" "} tokens here.
544
+ </li>
545
+ </ol>
546
+ </div>
547
+ </details>
548
+ <details
549
+ class="rounded-lg border border-border bg-field px-3 py-2.5"
550
+ >
551
+ <summary
552
+ class="cursor-pointer text-xs text-body hover:text-body"
423
553
  >
424
- Open full Slack setup guide
425
- </a>
426
- </div>
427
- </details>
554
+ <span class="inline-block ml-1">
555
+ Manual setup instructions
556
+ </span>
557
+ </summary>
558
+ <div class="mt-2 space-y-2 text-xs text-fg-muted">
559
+ <p>
560
+ Use this if you want to configure the Slack app by hand
561
+ instead of importing a manifest.
562
+ </p>
563
+ <ol class="list-decimal list-inside space-y-1.5">
564
+ <li>
565
+ In Slack app settings, turn on ${" "}
566
+ <span class="text-body">Socket Mode</span>.
567
+ </li>
568
+ <li>
569
+ In ${" "}
570
+ <span class="text-body">App Home</span>, enable
571
+ <code class="font-mono text-fg-muted ml-1">
572
+ Allow users to send Slash commands and messages from
573
+ the messages tab </code
574
+ >.
575
+ </li>
576
+ <li>
577
+ In ${" "}
578
+ <span class="text-body">Event Subscriptions</span>,
579
+ toggle on
580
+ <code class="font-mono text-fg-muted ml-1"
581
+ >Subscribe to bot events</code
582
+ >
583
+ ${" "} and add
584
+ <code class="font-mono text-fg-muted ml-1"
585
+ >message.im</code
586
+ >.
587
+ </li>
588
+ <li>
589
+ In ${" "}
590
+ <span class="text-body">OAuth & Permissions</span>,
591
+ add the bot scopes:
592
+ <code class="font-mono text-fg-muted ml-1">
593
+ ${kSlackBotScopes.join(", ")}
594
+ </code>
595
+ </li>
596
+ <li>
597
+ In ${" "}
598
+ <span class="text-body">Basic Information</span>,
599
+ create an App Token (<code
600
+ class="font-mono text-fg-muted"
601
+ >xapp-...</code
602
+ >) with
603
+ <code class="font-mono text-fg-muted ml-1"
604
+ >connections:write</code
605
+ >.
606
+ </li>
607
+ <li>
608
+ Back in ${" "}
609
+ <span class="text-body">OAuth & Permissions</span>,
610
+ install or reinstall the app, then copy the ${" "}
611
+ <span class="text-body">Bot User OAuth Token</span>
612
+ ${" "} (
613
+ <code class="font-mono text-fg-muted">xoxb-...</code>
614
+ ).
615
+ </li>
616
+ </ol>
617
+ <a
618
+ href=${kSlackInstructionsLink}
619
+ target="_blank"
620
+ class="hover:underline"
621
+ style="color: var(--accent-link)"
622
+ >
623
+ Open full Slack setup guide
624
+ </a>
625
+ </div>
626
+ </details>
627
+ </div>
428
628
  `
429
629
  : null
430
630
  }