@chrysb/alphaclaw 0.4.1-beta.0 → 0.4.1-beta.2

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 (35) hide show
  1. package/lib/public/css/explorer.css +156 -4
  2. package/lib/public/js/components/file-tree.js +533 -48
  3. package/lib/public/js/components/file-viewer/index.js +2 -8
  4. package/lib/public/js/components/google/index.js +83 -48
  5. package/lib/public/js/components/icons.js +26 -0
  6. package/lib/public/js/components/onboarding/use-welcome-codex.js +129 -0
  7. package/lib/public/js/components/onboarding/use-welcome-pairing.js +74 -0
  8. package/lib/public/js/components/onboarding/use-welcome-storage.js +60 -0
  9. package/lib/public/js/components/sidebar.js +1 -1
  10. package/lib/public/js/components/telegram-workspace/onboarding.js +1 -1
  11. package/lib/public/js/components/webhooks.js +2 -2
  12. package/lib/public/js/components/welcome.js +57 -210
  13. package/lib/public/js/lib/api.js +27 -0
  14. package/lib/public/js/lib/browse-file-policies.js +3 -1
  15. package/lib/public/shared/browse-file-policies.json +2 -1
  16. package/lib/server/constants.js +0 -1
  17. package/lib/server/gmail-serve.js +1 -1
  18. package/lib/server/gmail-watch.js +8 -28
  19. package/lib/server/gog-skill.js +169 -0
  20. package/lib/server/onboarding/openclaw.js +9 -1
  21. package/lib/server/routes/browse/index.js +123 -6
  22. package/lib/server/routes/browse/path-utils.js +3 -1
  23. package/lib/server/routes/google.js +4 -0
  24. package/lib/server/routes/webhooks.js +3 -5
  25. package/lib/server/webhooks.js +1 -1
  26. package/lib/server.js +2 -0
  27. package/lib/setup/skills/gog-cli/calendar.md +63 -0
  28. package/lib/setup/skills/gog-cli/contacts.md +30 -0
  29. package/lib/setup/skills/gog-cli/docs.md +46 -0
  30. package/lib/setup/skills/gog-cli/drive.md +48 -0
  31. package/lib/setup/skills/gog-cli/gmail.md +64 -0
  32. package/lib/setup/skills/gog-cli/meet.md +6 -0
  33. package/lib/setup/skills/gog-cli/sheets.md +43 -0
  34. package/lib/setup/skills/gog-cli/tasks.md +32 -0
  35. package/package.json +2 -2
@@ -1,19 +1,11 @@
1
1
  import { h } from "https://esm.sh/preact";
2
- import { useState, useEffect, useRef } from "https://esm.sh/preact/hooks";
2
+ import { useState, useEffect } from "https://esm.sh/preact/hooks";
3
3
  import htm from "https://esm.sh/htm";
4
4
  import {
5
5
  runOnboard,
6
6
  verifyGithubOnboardingRepo,
7
7
  fetchModels,
8
- fetchCodexStatus,
9
- disconnectCodex,
10
- exchangeCodexOAuth,
11
- fetchStatus,
12
- fetchPairings,
13
- approvePairing,
14
- rejectPairing,
15
8
  } from "../lib/api.js";
16
- import { usePolling } from "../hooks/usePolling.js";
17
9
  import {
18
10
  getModelProvider,
19
11
  getFeaturedModels,
@@ -27,41 +19,46 @@ import { WelcomeHeader } from "./onboarding/welcome-header.js";
27
19
  import { WelcomeSetupStep } from "./onboarding/welcome-setup-step.js";
28
20
  import { WelcomeFormStep } from "./onboarding/welcome-form-step.js";
29
21
  import { WelcomePairingStep } from "./onboarding/welcome-pairing-step.js";
22
+ import { getPreferredPairingChannel } from "./onboarding/pairing-utils.js";
30
23
  import {
31
- getPreferredPairingChannel,
32
- isChannelPaired,
33
- } from "./onboarding/pairing-utils.js";
24
+ kOnboardingStorageKey,
25
+ kPairingChannelKey,
26
+ useWelcomeStorage,
27
+ } from "./onboarding/use-welcome-storage.js";
28
+ import { useWelcomeCodex } from "./onboarding/use-welcome-codex.js";
29
+ import { useWelcomePairing } from "./onboarding/use-welcome-pairing.js";
34
30
  const html = htm.bind(h);
35
- const kOnboardingStorageKey = "openclaw_setup";
36
- const kOnboardingStepKey = "_step";
37
- const kPairingChannelKey = "_pairingChannel";
38
31
  const kMaxOnboardingVars = 64;
39
32
  const kMaxEnvKeyLength = 128;
40
33
  const kMaxEnvValueLength = 4096;
41
34
 
42
35
  export const Welcome = ({ onComplete }) => {
43
- const [initialSetupState] = useState(() => {
44
- try {
45
- return JSON.parse(localStorage.getItem(kOnboardingStorageKey) || "{}");
46
- } catch {
47
- return {};
48
- }
49
- });
50
- const [vals, setVals] = useState(() => ({ ...initialSetupState }));
36
+ const kSetupStepIndex = kWelcomeGroups.length;
37
+ const kPairingStepIndex = kSetupStepIndex + 1;
38
+ const { vals, setVals, setValue, step, setStep, setupError, setSetupError } =
39
+ useWelcomeStorage({
40
+ kSetupStepIndex,
41
+ kPairingStepIndex,
42
+ });
51
43
  const [models, setModels] = useState([]);
52
44
  const [modelsLoading, setModelsLoading] = useState(true);
53
45
  const [modelsError, setModelsError] = useState(null);
54
46
  const [showAllModels, setShowAllModels] = useState(false);
55
- const [codexStatus, setCodexStatus] = useState({ connected: false });
56
- const [codexLoading, setCodexLoading] = useState(true);
57
- const [codexManualInput, setCodexManualInput] = useState("");
58
- const [codexExchanging, setCodexExchanging] = useState(false);
59
- const [codexAuthStarted, setCodexAuthStarted] = useState(false);
60
- const [codexAuthWaiting, setCodexAuthWaiting] = useState(false);
61
47
  const [loading, setLoading] = useState(false);
62
48
  const [githubStepLoading, setGithubStepLoading] = useState(false);
63
- const [error, setError] = useState(null);
64
- const codexPopupPollRef = useRef(null);
49
+ const [formError, setFormError] = useState(null);
50
+ const {
51
+ codexStatus,
52
+ codexLoading,
53
+ codexManualInput,
54
+ setCodexManualInput,
55
+ codexExchanging,
56
+ codexAuthStarted,
57
+ codexAuthWaiting,
58
+ startCodexAuth,
59
+ completeCodexAuth,
60
+ handleCodexDisconnect,
61
+ } = useWelcomeCodex({ setFormError });
65
62
 
66
63
  useEffect(() => {
67
64
  fetchModels()
@@ -78,50 +75,6 @@ export const Welcome = ({ onComplete }) => {
78
75
  .finally(() => setModelsLoading(false));
79
76
  }, []);
80
77
 
81
- const refreshCodexStatus = async () => {
82
- try {
83
- const status = await fetchCodexStatus();
84
- setCodexStatus(status);
85
- if (status?.connected) {
86
- setCodexAuthStarted(false);
87
- setCodexAuthWaiting(false);
88
- }
89
- } catch {
90
- setCodexStatus({ connected: false });
91
- } finally {
92
- setCodexLoading(false);
93
- }
94
- };
95
-
96
- useEffect(() => {
97
- refreshCodexStatus();
98
- }, []);
99
-
100
- useEffect(() => {
101
- const onMessage = async (e) => {
102
- if (e.data?.codex === "success") {
103
- await refreshCodexStatus();
104
- }
105
- if (e.data?.codex === "error") {
106
- setError(`Codex auth failed: ${e.data.message || "unknown error"}`);
107
- }
108
- };
109
- window.addEventListener("message", onMessage);
110
- return () => window.removeEventListener("message", onMessage);
111
- }, []);
112
-
113
- useEffect(
114
- () => () => {
115
- if (codexPopupPollRef.current) {
116
- clearInterval(codexPopupPollRef.current);
117
- codexPopupPollRef.current = null;
118
- }
119
- },
120
- [],
121
- );
122
-
123
- const set = (key, value) => setVals((prev) => ({ ...prev, [key]: value }));
124
-
125
78
  const selectedProvider = getModelProvider(vals.MODEL_KEY);
126
79
  const featuredModels = getFeaturedModels(models);
127
80
  const baseModelOptions = showAllModels
@@ -152,18 +105,6 @@ export const Welcome = ({ onComplete }) => {
152
105
  : false;
153
106
 
154
107
  const allValid = kWelcomeGroups.every((g) => g.validate(vals, { hasAi }));
155
- const kSetupStepIndex = kWelcomeGroups.length;
156
- const kPairingStepIndex = kSetupStepIndex + 1;
157
- const [step, setStep] = useState(() => {
158
- const parsedStep = Number.parseInt(
159
- String(initialSetupState?.[kOnboardingStepKey] || ""),
160
- 10,
161
- );
162
- if (!Number.isFinite(parsedStep)) return 0;
163
- return Math.max(0, Math.min(kPairingStepIndex, parsedStep));
164
- });
165
- const [pairingError, setPairingError] = useState(null);
166
- const [pairingComplete, setPairingComplete] = useState(false);
167
108
  const isSetupStep = step === kSetupStepIndex;
168
109
  const isPairingStep = step === kPairingStepIndex;
169
110
  const activeGroup = step < kSetupStepIndex ? kWelcomeGroups[step] : null;
@@ -173,94 +114,20 @@ export const Welcome = ({ onComplete }) => {
173
114
  const selectedPairingChannel = String(
174
115
  vals[kPairingChannelKey] || getPreferredPairingChannel(vals),
175
116
  );
176
- const pairingStatusPoll = usePolling(fetchStatus, 3000, {
177
- enabled: isPairingStep,
117
+ const {
118
+ pairingStatusPoll,
119
+ pairingRequestsPoll,
120
+ pairingChannels,
121
+ canFinishPairing,
122
+ pairingError,
123
+ pairingComplete,
124
+ handlePairingApprove,
125
+ handlePairingReject,
126
+ resetPairingState,
127
+ } = useWelcomePairing({
128
+ isPairingStep,
129
+ selectedPairingChannel,
178
130
  });
179
- const pairingRequestsPoll = usePolling(
180
- async () => {
181
- const payload = await fetchPairings();
182
- const allPending = payload.pending || [];
183
- return allPending.filter((p) => p.channel === selectedPairingChannel);
184
- },
185
- 1000,
186
- { enabled: isPairingStep && !!selectedPairingChannel },
187
- );
188
- const pairingChannels = pairingStatusPoll.data?.channels || {};
189
- const canFinishPairing = isChannelPaired(pairingChannels, selectedPairingChannel);
190
-
191
- useEffect(() => {
192
- if (isPairingStep && canFinishPairing) {
193
- setPairingComplete(true);
194
- }
195
- }, [isPairingStep, canFinishPairing]);
196
-
197
- useEffect(() => {
198
- localStorage.setItem(
199
- kOnboardingStorageKey,
200
- JSON.stringify({
201
- ...vals,
202
- [kOnboardingStepKey]: step,
203
- }),
204
- );
205
- }, [vals, step]);
206
-
207
- const startCodexAuth = () => {
208
- if (codexStatus.connected) return;
209
- setCodexAuthStarted(true);
210
- setCodexAuthWaiting(true);
211
- const authUrl = "/auth/codex/start";
212
- const popup = window.open(
213
- authUrl,
214
- "codex-auth",
215
- "popup=yes,width=640,height=780",
216
- );
217
- if (!popup || popup.closed) {
218
- setCodexAuthWaiting(false);
219
- window.location.href = authUrl;
220
- return;
221
- }
222
- if (codexPopupPollRef.current) {
223
- clearInterval(codexPopupPollRef.current);
224
- }
225
- codexPopupPollRef.current = setInterval(() => {
226
- if (popup.closed) {
227
- clearInterval(codexPopupPollRef.current);
228
- codexPopupPollRef.current = null;
229
- setCodexAuthWaiting(false);
230
- }
231
- }, 500);
232
- };
233
-
234
- const completeCodexAuth = async () => {
235
- if (!codexManualInput.trim() || codexExchanging) return;
236
- setCodexExchanging(true);
237
- setError(null);
238
- try {
239
- const result = await exchangeCodexOAuth(codexManualInput.trim());
240
- if (!result.ok)
241
- throw new Error(result.error || "Codex OAuth exchange failed");
242
- setCodexManualInput("");
243
- setCodexAuthStarted(false);
244
- setCodexAuthWaiting(false);
245
- await refreshCodexStatus();
246
- } catch (err) {
247
- setError(err.message || "Codex OAuth exchange failed");
248
- } finally {
249
- setCodexExchanging(false);
250
- }
251
- };
252
-
253
- const handleCodexDisconnect = async () => {
254
- const result = await disconnectCodex();
255
- if (!result.ok) {
256
- setError(result.error || "Failed to disconnect Codex");
257
- return;
258
- }
259
- setCodexAuthStarted(false);
260
- setCodexAuthWaiting(false);
261
- setCodexManualInput("");
262
- await refreshCodexStatus();
263
- };
264
131
 
265
132
  const handleSubmit = async () => {
266
133
  if (!allValid || loading) return;
@@ -294,14 +161,16 @@ export const Welcome = ({ onComplete }) => {
294
161
  return "";
295
162
  })();
296
163
  if (preflightError) {
297
- setError(preflightError);
164
+ setFormError(preflightError);
165
+ setSetupError(null);
298
166
  setStep(Math.max(0, kWelcomeGroups.findIndex((g) => g.id === "github")));
299
167
  return;
300
168
  }
301
169
  setStep(kSetupStepIndex);
302
170
  setLoading(true);
303
- setError(null);
304
- setPairingError(null);
171
+ setFormError(null);
172
+ setSetupError(null);
173
+ resetPairingState();
305
174
 
306
175
  try {
307
176
  const result = await runOnboard(vars, vals.MODEL_KEY);
@@ -316,38 +185,15 @@ export const Welcome = ({ onComplete }) => {
316
185
  }));
317
186
  setLoading(false);
318
187
  setStep(kPairingStepIndex);
319
- setPairingComplete(false);
188
+ resetPairingState();
189
+ setSetupError(null);
320
190
  } catch (err) {
321
191
  console.error("Onboard error:", err);
322
- setError(err.message);
192
+ setSetupError(err.message || "Onboarding failed");
323
193
  setLoading(false);
324
194
  }
325
195
  };
326
196
 
327
- const handlePairingApprove = async (id, channel) => {
328
- try {
329
- setPairingError(null);
330
- const result = await approvePairing(id, channel);
331
- if (!result.ok) throw new Error(result.error || "Could not approve pairing");
332
- setPairingComplete(true);
333
- pairingRequestsPoll.refresh();
334
- pairingStatusPoll.refresh();
335
- } catch (err) {
336
- setPairingError(err.message || "Could not approve pairing");
337
- }
338
- };
339
-
340
- const handlePairingReject = async (id, channel) => {
341
- try {
342
- setPairingError(null);
343
- const result = await rejectPairing(id, channel);
344
- if (!result.ok) throw new Error(result.error || "Could not reject pairing");
345
- pairingRequestsPoll.refresh();
346
- } catch (err) {
347
- setPairingError(err.message || "Could not reject pairing");
348
- }
349
- };
350
-
351
197
  const finishOnboarding = () => {
352
198
  localStorage.removeItem(kOnboardingStorageKey);
353
199
  onComplete();
@@ -355,17 +201,18 @@ export const Welcome = ({ onComplete }) => {
355
201
 
356
202
  const goBack = () => {
357
203
  if (isSetupStep) return;
358
- setError(null);
204
+ setFormError(null);
359
205
  setStep((prev) => Math.max(0, prev - 1));
360
206
  };
361
207
  const goBackFromSetupError = () => {
362
208
  setLoading(false);
209
+ setSetupError(null);
363
210
  setStep(kWelcomeGroups.length - 1);
364
211
  };
365
212
 
366
213
  const goNext = async () => {
367
214
  if (!activeGroup || !currentGroupValid) return;
368
- setError(null);
215
+ setFormError(null);
369
216
  if (activeGroup.id === "github") {
370
217
  setGithubStepLoading(true);
371
218
  try {
@@ -374,11 +221,11 @@ export const Welcome = ({ onComplete }) => {
374
221
  vals.GITHUB_TOKEN,
375
222
  );
376
223
  if (!result?.ok) {
377
- setError(result?.error || "GitHub verification failed");
224
+ setFormError(result?.error || "GitHub verification failed");
378
225
  return;
379
226
  }
380
227
  } catch (err) {
381
- setError(err?.message || "GitHub verification failed");
228
+ setFormError(err?.message || "GitHub verification failed");
382
229
  return;
383
230
  } finally {
384
231
  setGithubStepLoading(false);
@@ -412,7 +259,7 @@ export const Welcome = ({ onComplete }) => {
412
259
  <div class="bg-surface border border-border rounded-xl p-4 space-y-3">
413
260
  ${isSetupStep
414
261
  ? html`<${WelcomeSetupStep}
415
- error=${error}
262
+ error=${setupError}
416
263
  loading=${loading}
417
264
  onRetry=${handleSubmit}
418
265
  onBack=${goBackFromSetupError}
@@ -434,7 +281,7 @@ export const Welcome = ({ onComplete }) => {
434
281
  activeGroup=${activeGroup}
435
282
  vals=${vals}
436
283
  hasAi=${hasAi}
437
- setValue=${set}
284
+ setValue=${setValue}
438
285
  modelOptions=${modelOptions}
439
286
  modelsLoading=${modelsLoading}
440
287
  modelsError=${modelsError}
@@ -453,7 +300,7 @@ export const Welcome = ({ onComplete }) => {
453
300
  completeCodexAuth=${completeCodexAuth}
454
301
  codexExchanging=${codexExchanging}
455
302
  visibleAiFieldKeys=${visibleAiFieldKeys}
456
- error=${error}
303
+ error=${formError}
457
304
  step=${step}
458
305
  totalGroups=${kWelcomeGroups.length}
459
306
  currentGroupValid=${currentGroupValid}
@@ -540,6 +540,33 @@ export const saveFileContent = async (filePath, content) => {
540
540
  return parseJsonOrThrow(res, 'Could not save file');
541
541
  };
542
542
 
543
+ export const createBrowseFile = async (filePath) => {
544
+ const res = await authFetch('/api/browse/create-file', {
545
+ method: 'POST',
546
+ headers: { 'Content-Type': 'application/json' },
547
+ body: JSON.stringify({ path: String(filePath || '') }),
548
+ });
549
+ return parseJsonOrThrow(res, 'Could not create file');
550
+ };
551
+
552
+ export const createBrowseFolder = async (folderPath) => {
553
+ const res = await authFetch('/api/browse/create-folder', {
554
+ method: 'POST',
555
+ headers: { 'Content-Type': 'application/json' },
556
+ body: JSON.stringify({ path: String(folderPath || '') }),
557
+ });
558
+ return parseJsonOrThrow(res, 'Could not create folder');
559
+ };
560
+
561
+ export const moveBrowsePath = async (from, to) => {
562
+ const res = await authFetch('/api/browse/move', {
563
+ method: 'POST',
564
+ headers: { 'Content-Type': 'application/json' },
565
+ body: JSON.stringify({ from: String(from || ''), to: String(to || '') }),
566
+ });
567
+ return parseJsonOrThrow(res, 'Could not move path');
568
+ };
569
+
543
570
  export const deleteBrowseFile = async (filePath) => {
544
571
  const res = await authFetch('/api/browse/delete', {
545
572
  method: 'DELETE',
@@ -43,7 +43,9 @@ export const matchesBrowsePolicyPath = (policyPathSet, normalizedPath) => {
43
43
  for (const policyPath of policyPathSet) {
44
44
  if (
45
45
  safeNormalizedPath === policyPath ||
46
- safeNormalizedPath.endsWith(`/${policyPath}`)
46
+ safeNormalizedPath.endsWith(`/${policyPath}`) ||
47
+ safeNormalizedPath.startsWith(`${policyPath}/`) ||
48
+ safeNormalizedPath.includes(`/${policyPath}/`)
47
49
  ) {
48
50
  return true;
49
51
  }
@@ -6,7 +6,8 @@
6
6
  "lockedPaths": [
7
7
  "hooks/bootstrap/agents.md",
8
8
  "hooks/bootstrap/tools.md",
9
- "skills/control-ui/skill.md",
9
+ "skills/control-ui",
10
+ "skills/gog-cli",
10
11
  ".alphaclaw/hourly-git-sync.sh",
11
12
  ".alphaclaw/.cli-device-auto-approved"
12
13
  ]
@@ -147,7 +147,6 @@ const kLogMaxBytes = parsePositiveIntEnv(
147
147
 
148
148
  const kSystemVars = new Set([
149
149
  "WEBHOOK_TOKEN",
150
- "OPENCLAW_HOOKS_TOKEN",
151
150
  "OPENCLAW_GATEWAY_TOKEN",
152
151
  "SETUP_PASSWORD",
153
152
  "PORT",
@@ -110,7 +110,7 @@ const createGmailServeManager = ({
110
110
  }
111
111
  const token = String(webhookToken || "").trim();
112
112
  if (!token) {
113
- throw new Error("OPENCLAW_HOOKS_TOKEN is required to start Gmail watch serve");
113
+ throw new Error("WEBHOOK_TOKEN is required to start Gmail watch serve");
114
114
  }
115
115
 
116
116
  const args = buildArgs({ account, port: normalizedPort, webhookToken: token });
@@ -203,39 +203,19 @@ const createGmailWatchService = ({
203
203
  };
204
204
 
205
205
  const ensureWebhookToken = () => {
206
- const existing = String(
207
- process.env.OPENCLAW_HOOKS_TOKEN || process.env.WEBHOOK_TOKEN || "",
208
- ).trim();
206
+ const existing = String(process.env.WEBHOOK_TOKEN || "").trim();
209
207
  if (existing) return { token: existing, changed: false };
210
208
  const vars = readEnvFile();
211
- const tokenFromOpenclawHooks = String(
212
- vars.find((entry) => entry.key === "OPENCLAW_HOOKS_TOKEN")?.value || "",
213
- ).trim();
214
- const tokenFromLegacyWebhook = String(
209
+ const tokenFromFile = String(
215
210
  vars.find((entry) => entry.key === "WEBHOOK_TOKEN")?.value || "",
216
211
  ).trim();
217
- const tokenFromFile = tokenFromOpenclawHooks || tokenFromLegacyWebhook;
218
212
  if (tokenFromFile) {
219
- process.env.OPENCLAW_HOOKS_TOKEN = tokenFromFile;
220
- if (!process.env.WEBHOOK_TOKEN) {
221
- process.env.WEBHOOK_TOKEN = tokenFromFile;
222
- }
223
- if (!tokenFromOpenclawHooks) {
224
- const nextVars = vars.filter(
225
- (entry) => entry.key !== "OPENCLAW_HOOKS_TOKEN",
226
- );
227
- nextVars.push({ key: "OPENCLAW_HOOKS_TOKEN", value: tokenFromFile });
228
- writeEnvFile(nextVars);
229
- reloadEnv();
230
- return { token: tokenFromFile, changed: true };
231
- }
213
+ process.env.WEBHOOK_TOKEN = tokenFromFile;
232
214
  return { token: tokenFromFile, changed: false };
233
215
  }
234
216
  const token = generatePushToken();
235
- const nextVars = vars.filter(
236
- (entry) => entry.key !== "OPENCLAW_HOOKS_TOKEN",
237
- );
238
- nextVars.push({ key: "OPENCLAW_HOOKS_TOKEN", value: token });
217
+ const nextVars = vars.filter((entry) => entry.key !== "WEBHOOK_TOKEN");
218
+ nextVars.push({ key: "WEBHOOK_TOKEN", value: token });
239
219
  writeEnvFile(nextVars);
240
220
  reloadEnv();
241
221
  return { token, changed: true };
@@ -258,7 +238,7 @@ const createGmailWatchService = ({
258
238
  changed = true;
259
239
  }
260
240
  if (typeof cfg.hooks.token !== "string" || !cfg.hooks.token.trim()) {
261
- cfg.hooks.token = "${OPENCLAW_HOOKS_TOKEN}";
241
+ cfg.hooks.token = "${WEBHOOK_TOKEN}";
262
242
  changed = true;
263
243
  }
264
244
  if (!Array.isArray(cfg.hooks.presets)) {
@@ -337,7 +317,7 @@ const createGmailWatchService = ({
337
317
  const watch = getAccountGmailWatch(account || {});
338
318
  if (!account || !watch.enabled || !watch.port) return;
339
319
  const token = String(
340
- process.env.OPENCLAW_HOOKS_TOKEN || process.env.WEBHOOK_TOKEN || "",
320
+ process.env.WEBHOOK_TOKEN || "",
341
321
  ).trim();
342
322
  if (!token) return;
343
323
  const status = await serveManager.startServe({
@@ -633,7 +613,7 @@ const createGmailWatchService = ({
633
613
  try {
634
614
  let state = readState();
635
615
  const hookToken = String(
636
- process.env.OPENCLAW_HOOKS_TOKEN || process.env.WEBHOOK_TOKEN || "",
616
+ process.env.WEBHOOK_TOKEN || "",
637
617
  ).trim();
638
618
  const enabled = listWatchEnabledAccounts(state);
639
619
  for (const account of enabled) {