@chrysb/alphaclaw 0.4.1-beta.0 → 0.4.1-beta.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.
@@ -1,5 +1,10 @@
1
1
  import { h } from "https://esm.sh/preact";
2
- import { useCallback, useEffect, useMemo, useState } from "https://esm.sh/preact/hooks";
2
+ import {
3
+ useCallback,
4
+ useEffect,
5
+ useMemo,
6
+ useState,
7
+ } from "https://esm.sh/preact/hooks";
3
8
  import htm from "https://esm.sh/htm";
4
9
  import {
5
10
  checkGoogleApis,
@@ -34,12 +39,8 @@ export const Google = ({
34
39
  onRestartRequired = () => {},
35
40
  onOpenGmailWebhook = () => {},
36
41
  }) => {
37
- const {
38
- accounts,
39
- loading,
40
- hasCompanyCredentials,
41
- refreshAccounts,
42
- } = useGoogleAccounts({ gatewayStatus });
42
+ const { accounts, loading, hasCompanyCredentials, refreshAccounts } =
43
+ useGoogleAccounts({ gatewayStatus });
43
44
  const [expandedAccountId, setExpandedAccountId] = useState("");
44
45
  const [scopesByAccountId, setScopesByAccountId] = useState({});
45
46
  const [savedScopesByAccountId, setSavedScopesByAccountId] = useState({});
@@ -90,12 +91,16 @@ export const Google = ({
90
91
  );
91
92
 
92
93
  const ensureScopesForAccount = useCallback((account) => {
93
- const nextScopes = Array.isArray(account.activeScopes) && account.activeScopes.length
94
- ? account.activeScopes
95
- : Array.isArray(account.services) && account.services.length
96
- ? account.services
97
- : getDefaultScopes();
98
- setSavedScopesByAccountId((prev) => ({ ...prev, [account.id]: [...nextScopes] }));
94
+ const nextScopes =
95
+ Array.isArray(account.activeScopes) && account.activeScopes.length
96
+ ? account.activeScopes
97
+ : Array.isArray(account.services) && account.services.length
98
+ ? account.services
99
+ : getDefaultScopes();
100
+ setSavedScopesByAccountId((prev) => ({
101
+ ...prev,
102
+ [account.id]: [...nextScopes],
103
+ }));
99
104
  setScopesByAccountId((prev) => {
100
105
  const current = prev[account.id];
101
106
  if (!current || !hasScopesChanged(current, nextScopes)) {
@@ -125,7 +130,10 @@ export const Google = ({
125
130
  (accountId) => {
126
131
  const account = getAccountById(accountId);
127
132
  if (!account) return;
128
- const scopes = scopesByAccountId[accountId] || account.activeScopes || getDefaultScopes();
133
+ const scopes =
134
+ scopesByAccountId[accountId] ||
135
+ account.activeScopes ||
136
+ getDefaultScopes();
129
137
  if (!scopes.length) {
130
138
  window.alert("Select at least one service");
131
139
  return;
@@ -160,7 +168,10 @@ export const Google = ({
160
168
  try {
161
169
  const data = await checkGoogleApis(accountId);
162
170
  if (data.results) {
163
- setApiStatusByAccountId((prev) => ({ ...prev, [accountId]: data.results }));
171
+ setApiStatusByAccountId((prev) => ({
172
+ ...prev,
173
+ [accountId]: data.results,
174
+ }));
164
175
  }
165
176
  } finally {
166
177
  setCheckingByAccountId((prev) => {
@@ -184,7 +195,10 @@ export const Google = ({
184
195
  await handleCheckApis(accountId);
185
196
  }
186
197
  } else if (event.data?.google === "error") {
187
- showToast(`✗ Google auth failed: ${event.data.message || "unknown"}`, "error");
198
+ showToast(
199
+ `✗ Google auth failed: ${event.data.message || "unknown"}`,
200
+ "error",
201
+ );
188
202
  }
189
203
  };
190
204
  window.addEventListener("message", handler);
@@ -249,6 +263,9 @@ export const Google = ({
249
263
  };
250
264
 
251
265
  const handleCredentialsSaved = async (account) => {
266
+ if (account?.id) {
267
+ setExpandedAccountId(account.id);
268
+ }
252
269
  await refreshAccounts();
253
270
  if (account?.id) startAuth(account.id);
254
271
  };
@@ -267,6 +284,9 @@ export const Google = ({
267
284
  return;
268
285
  }
269
286
  setAddCompanyModalOpen(false);
287
+ if (data.accountId) {
288
+ setExpandedAccountId(data.accountId);
289
+ }
270
290
  await refreshAccounts();
271
291
  if (data.accountId) startAuth(data.accountId);
272
292
  } finally {
@@ -304,7 +324,7 @@ export const Google = ({
304
324
  const account = getAccountById(accountId);
305
325
  if (!account) return;
306
326
  const personal = isPersonalAccount(account);
307
- const client = personal ? "personal" : (account.client || "default");
327
+ const client = personal ? "personal" : account.client || "default";
308
328
  let credentialValues = {};
309
329
  try {
310
330
  const credentialResponse = await fetchGoogleCredentials({
@@ -390,22 +410,23 @@ export const Google = ({
390
410
  };
391
411
 
392
412
  const renderEmptyState = () => html`
393
- <div class="text-center space-y-2 py-1">
413
+ <div class="text-center space-y-2 pt-3">
394
414
  <div class="rounded-lg border border-border bg-black/20 px-3 py-5">
395
- <div class="flex flex-col items-center justify-center gap-1.5">
415
+ <div class="flex flex-col items-center justify-center gap-3">
396
416
  <img
397
417
  src=${kGoogleIconPath}
398
418
  alt="Google logo"
399
- class="h-4 w-4 shrink-0"
419
+ class="h-5 w-5 shrink-0"
400
420
  loading="lazy"
401
421
  decoding="async"
402
422
  />
403
423
  <p class="text-xs text-gray-500">
404
- Connect Gmail, Calendar, Contacts, Drive, Sheets, Tasks, Docs, and Meet.
424
+ Connect Gmail, Calendar, Contacts, Drive, Sheets, Tasks, Docs, and
425
+ Meet.
405
426
  </p>
406
427
  </div>
407
428
  </div>
408
- <div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
429
+ <div class="grid grid-cols-1 sm:grid-cols-2 gap-2 pt-2">
409
430
  <${ActionButton}
410
431
  onClick=${handleAddCompanyClick}
411
432
  tone="primary"
@@ -469,34 +490,46 @@ export const Google = ({
469
490
  `}
470
491
  />
471
492
  ${loading
472
- ? html`<div class="text-gray-500 text-sm text-center py-2">Loading...</div>`
493
+ ? html`<div class="text-gray-500 text-sm text-center py-2">
494
+ Loading...
495
+ </div>`
473
496
  : accounts.length
474
497
  ? html`
475
498
  <div class="space-y-2 mt-3">
476
- ${accounts.map((account) =>
477
- html`<${GoogleAccountRow}
478
- key=${account.id}
479
- account=${account}
480
- personal=${isPersonalAccount(account)}
481
- expanded=${expandedAccountId === account.id}
482
- onToggleExpanded=${(accountId) =>
483
- setExpandedAccountId((prev) => (prev === accountId ? "" : accountId))}
484
- scopes=${scopesByAccountId[account.id] || account.activeScopes || getDefaultScopes()}
485
- savedScopes=${savedScopesByAccountId[account.id] || account.activeScopes || getDefaultScopes()}
486
- apiStatus=${apiStatusByAccountId[account.id] || {}}
487
- checkingApis=${expandedAccountId === account.id && Boolean(checkingByAccountId[account.id])}
488
- onToggleScope=${handleToggleScope}
489
- onCheckApis=${handleCheckApis}
490
- onUpdatePermissions=${(accountId) => startAuth(accountId)}
491
- onEditCredentials=${handleEditCredentials}
492
- onDisconnect=${(accountId) => setDisconnectAccountId(accountId)}
493
- gmailWatchStatus=${watchByAccountId.get(account.id) || null}
494
- gmailWatchBusy=${Boolean(busyByAccountId[account.id])}
495
- onEnableGmailWatch=${handleEnableGmailWatch}
496
- onDisableGmailWatch=${handleDisableGmailWatch}
497
- onOpenGmailSetup=${openGmailSetupWizard}
498
- onOpenGmailWebhook=${onOpenGmailWebhook}
499
- />`,
499
+ ${accounts.map(
500
+ (account) =>
501
+ html`<${GoogleAccountRow}
502
+ key=${account.id}
503
+ account=${account}
504
+ personal=${isPersonalAccount(account)}
505
+ expanded=${expandedAccountId === account.id}
506
+ onToggleExpanded=${(accountId) =>
507
+ setExpandedAccountId((prev) =>
508
+ prev === accountId ? "" : accountId,
509
+ )}
510
+ scopes=${scopesByAccountId[account.id] ||
511
+ account.activeScopes ||
512
+ getDefaultScopes()}
513
+ savedScopes=${savedScopesByAccountId[account.id] ||
514
+ account.activeScopes ||
515
+ getDefaultScopes()}
516
+ apiStatus=${apiStatusByAccountId[account.id] || {}}
517
+ checkingApis=${expandedAccountId === account.id &&
518
+ Boolean(checkingByAccountId[account.id])}
519
+ onToggleScope=${handleToggleScope}
520
+ onCheckApis=${handleCheckApis}
521
+ onUpdatePermissions=${(accountId) => startAuth(accountId)}
522
+ onEditCredentials=${handleEditCredentials}
523
+ onDisconnect=${(accountId) =>
524
+ setDisconnectAccountId(accountId)}
525
+ gmailWatchStatus=${watchByAccountId.get(account.id) ||
526
+ null}
527
+ gmailWatchBusy=${Boolean(busyByAccountId[account.id])}
528
+ onEnableGmailWatch=${handleEnableGmailWatch}
529
+ onDisableGmailWatch=${handleDisableGmailWatch}
530
+ onOpenGmailSetup=${openGmailSetupWizard}
531
+ onOpenGmailWebhook=${onOpenGmailWebhook}
532
+ />`,
500
533
  )}
501
534
  </div>
502
535
  `
@@ -528,7 +561,9 @@ export const Google = ({
528
561
  visible=${gmailWizardState.visible}
529
562
  account=${getAccountById(gmailWizardState.accountId)}
530
563
  clientConfig=${clientConfigByClient.get(
531
- String(getAccountById(gmailWizardState.accountId)?.client || "default").trim() || "default",
564
+ String(
565
+ getAccountById(gmailWizardState.accountId)?.client || "default",
566
+ ).trim() || "default",
532
567
  ) || null}
533
568
  saving=${savingClient || gmailLoading}
534
569
  onClose=${closeGmailSetupWizard}
@@ -12,7 +12,7 @@ const kBrowseBottomPanelUiSettingKey = "browseBottomPanelHeightPx";
12
12
  const kBrowsePanelMinHeightPx = 120;
13
13
  const kBrowseBottomMinHeightPx = 120;
14
14
  const kBrowseResizerHeightPx = 6;
15
- const kDefaultBrowseBottomPanelHeightPx = 160;
15
+ const kDefaultBrowseBottomPanelHeightPx = 260;
16
16
 
17
17
  const readStoredBrowseBottomPanelHeight = () => {
18
18
  try {
@@ -116,7 +116,7 @@ export const CreateGroupStep = ({ onNext, onBack }) => html`
116
116
  <p class="text-xs font-medium text-gray-300">Create the group</p>
117
117
  <ol class="text-xs text-gray-400 space-y-2 list-decimal list-inside">
118
118
  <li>
119
- Open Telegram and create a
119
+ Open Telegram and create a${" "}
120
120
  <span class="text-gray-300">new group</span>
121
121
  </li>
122
122
  <li>
@@ -232,7 +232,7 @@ export const Webhooks = ({
232
232
  selectedWebhook?.fullUrl || `.../hooks/${selectedHookName}`;
233
233
  const webhookUrlWithQueryToken =
234
234
  selectedWebhook?.queryStringUrl ||
235
- `${webhookUrl}${webhookUrl.includes("?") ? "&" : "?"}token=<OPENCLAW_HOOKS_TOKEN>`;
235
+ `${webhookUrl}${webhookUrl.includes("?") ? "&" : "?"}token=<WEBHOOK_TOKEN>`;
236
236
  const derivedTokenFromQuery = (() => {
237
237
  try {
238
238
  const parsed = new URL(webhookUrlWithQueryToken);
@@ -245,7 +245,7 @@ export const Webhooks = ({
245
245
  selectedWebhook?.authHeaderValue ||
246
246
  (derivedTokenFromQuery
247
247
  ? `Authorization: Bearer ${derivedTokenFromQuery}`
248
- : "Authorization: Bearer <OPENCLAW_HOOKS_TOKEN>");
248
+ : "Authorization: Bearer <WEBHOOK_TOKEN>");
249
249
  const bearerTokenValue = authHeaderValue.startsWith("Authorization: ")
250
250
  ? authHeaderValue.slice("Authorization: ".length)
251
251
  : authHeaderValue;
@@ -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) {
@@ -177,7 +177,15 @@ const writeSanitizedOpenclawConfig = ({ fs, openclawDir, varMap }) => {
177
177
  const replacements = buildSecretReplacements(varMap, process.env);
178
178
  for (const [secret, envRef] of replacements) {
179
179
  if (secret) {
180
- content = content.split(secret).join(envRef);
180
+ // Only replace exact JSON string values so path substrings are never mutated.
181
+ const secretJson = JSON.stringify(secret);
182
+ content = content.replace(
183
+ new RegExp(
184
+ secretJson.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"),
185
+ "g",
186
+ ),
187
+ JSON.stringify(envRef),
188
+ );
181
189
  }
182
190
  }
183
191
  fs.writeFileSync(configPath, content);
@@ -52,15 +52,13 @@ const normalizeStatusFilter = (rawStatus) => {
52
52
 
53
53
  const buildWebhookUrls = ({ baseUrl, name }) => {
54
54
  const fullUrl = `${baseUrl}/hooks/${name}`;
55
- const token = String(
56
- process.env.OPENCLAW_HOOKS_TOKEN || process.env.WEBHOOK_TOKEN || "",
57
- ).trim();
55
+ const token = String(process.env.WEBHOOK_TOKEN || "").trim();
58
56
  const queryStringUrl = token
59
57
  ? `${fullUrl}?token=${encodeURIComponent(token)}`
60
- : `${fullUrl}?token=<OPENCLAW_HOOKS_TOKEN>`;
58
+ : `${fullUrl}?token=<WEBHOOK_TOKEN>`;
61
59
  const authHeaderValue = token
62
60
  ? `Authorization: Bearer ${token}`
63
- : "Authorization: Bearer <OPENCLAW_HOOKS_TOKEN>";
61
+ : "Authorization: Bearer <WEBHOOK_TOKEN>";
64
62
  return { fullUrl, queryStringUrl, authHeaderValue, hasRuntimeToken: !!token };
65
63
  };
66
64
 
@@ -53,7 +53,7 @@ const ensureHooksRoot = (cfg) => {
53
53
  if (typeof cfg.hooks.path !== "string" || !cfg.hooks.path.trim())
54
54
  cfg.hooks.path = "/hooks";
55
55
  if (typeof cfg.hooks.token !== "string" || !cfg.hooks.token.trim()) {
56
- cfg.hooks.token = "${OPENCLAW_HOOKS_TOKEN}";
56
+ cfg.hooks.token = "${WEBHOOK_TOKEN}";
57
57
  }
58
58
  if (
59
59
  typeof cfg.hooks.defaultSessionKey !== "string" ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.4.1-beta.0",
3
+ "version": "0.4.1-beta.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },