@chrysb/alphaclaw 0.8.1-beta.5 → 0.8.1-beta.7

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.
@@ -93,6 +93,7 @@ export const GmailSetupWizard = ({
93
93
  const [watchEnabled, setWatchEnabled] = useState(false);
94
94
  const [sendingToAgent, setSendingToAgent] = useState(false);
95
95
  const [agentMessageSent, setAgentMessageSent] = useState(false);
96
+ const [existingWebhookAtOpen, setExistingWebhookAtOpen] = useState(false);
96
97
 
97
98
  const {
98
99
  selectedSessionKey,
@@ -123,6 +124,7 @@ export const GmailSetupWizard = ({
123
124
  setWatchEnabled(false);
124
125
  setSendingToAgent(false);
125
126
  setAgentMessageSent(false);
127
+ setExistingWebhookAtOpen(Boolean(clientConfig?.webhookExists));
126
128
  }, [visible, account?.id]);
127
129
 
128
130
  const commands = clientConfig?.commands || null;
@@ -135,7 +137,7 @@ export const GmailSetupWizard = ({
135
137
  String(projectIdInput || "").trim() ||
136
138
  String(clientConfig?.projectId || "").trim() ||
137
139
  "<project-id>";
138
- const hasExistingWebhookSetup = Boolean(clientConfig?.webhookExists);
140
+ const hasExistingWebhookSetup = existingWebhookAtOpen;
139
141
  const stepTitles = hasExistingWebhookSetup ? kTutorialStepTitles : kSetupStepTitles;
140
142
  const totalSteps = stepTitles.length;
141
143
  const client =
@@ -26,40 +26,43 @@ export const SessionSelectField = ({
26
26
  statusClassName = "text-xs text-gray-500",
27
27
  errorClassName = "text-xs text-red-400",
28
28
  }) => {
29
+ const resolvedValue = selectedSessionKey || (allowNone ? noneValue : "");
30
+ const isDisabled = disabled || loading;
29
31
  return html`
30
32
  <div class=${containerClassName}>
31
33
  ${label
32
34
  ? html`<label class=${labelClassName}>${label}</label>`
33
35
  : null}
34
36
  <select
35
- value=${selectedSessionKey || (allowNone ? noneValue : "")}
37
+ value=${resolvedValue}
36
38
  onInput=${(event) => {
37
39
  const nextValue = String(event.currentTarget?.value || "");
38
40
  onChangeSessionKey(allowNone && nextValue === noneValue ? "" : nextValue);
39
41
  }}
40
- disabled=${disabled}
42
+ disabled=${isDisabled}
41
43
  class=${selectClassName}
42
44
  >
43
- ${allowNone
44
- ? html`<option value=${noneValue}>${noneLabel}</option>`
45
- : null}
46
- ${!allowNone && sessions.length === 0
47
- ? html`<option value="">${emptyOptionLabel}</option>`
48
- : null}
49
- ${sessions.map(
50
- (sessionRow) => html`
51
- <option value=${getSessionRowKey(sessionRow)}>
52
- ${String(sessionRow?.label || getSessionRowKey(sessionRow) || "Session")}
53
- </option>
54
- `,
55
- )}
45
+ ${loading
46
+ ? html`<option value=${resolvedValue || ""}>${loadingLabel}</option>`
47
+ : html`
48
+ ${allowNone
49
+ ? html`<option value=${noneValue}>${noneLabel}</option>`
50
+ : null}
51
+ ${!allowNone && sessions.length === 0
52
+ ? html`<option value="">${emptyOptionLabel}</option>`
53
+ : null}
54
+ ${sessions.map(
55
+ (sessionRow) => html`
56
+ <option value=${getSessionRowKey(sessionRow)}>
57
+ ${String(sessionRow?.label || getSessionRowKey(sessionRow) || "Session")}
58
+ </option>
59
+ `,
60
+ )}
61
+ `}
56
62
  </select>
57
63
  ${helperText
58
64
  ? html`<div class=${helperClassName}>${helperText}</div>`
59
65
  : null}
60
- ${loading
61
- ? html`<div class=${statusClassName}>${loadingLabel}</div>`
62
- : null}
63
66
  ${error
64
67
  ? html`<div class=${errorClassName}>${error}</div>`
65
68
  : null}
@@ -3,6 +3,7 @@ const startServerLifecycle = ({
3
3
  PORT,
4
4
  isOnboarded,
5
5
  runOnboardedBootSequence,
6
+ ensureUsageTrackerPluginConfig,
6
7
  doSyncPromptFiles,
7
8
  reloadEnv,
8
9
  syncChannelConfig,
@@ -17,6 +18,7 @@ const startServerLifecycle = ({
17
18
  console.log(`[alphaclaw] Express listening on :${PORT}`);
18
19
  if (isOnboarded()) {
19
20
  runOnboardedBootSequence({
21
+ ensureUsageTrackerPluginConfig,
20
22
  doSyncPromptFiles,
21
23
  reloadEnv,
22
24
  syncChannelConfig,
@@ -16,9 +16,17 @@ const buildGithubHeaders = (githubToken) => ({
16
16
  const parseGithubErrorMessage = async (response) => {
17
17
  try {
18
18
  const payload = await response.json();
19
- if (typeof payload?.message === "string" && payload.message.trim()) {
20
- return payload.message.trim();
21
- }
19
+ const base =
20
+ typeof payload?.message === "string" ? payload.message.trim() : "";
21
+ const detail = Array.isArray(payload?.errors)
22
+ ? payload.errors
23
+ .map((e) => (typeof e?.message === "string" ? e.message.trim() : ""))
24
+ .filter(Boolean)
25
+ .join("; ")
26
+ : "";
27
+ if (base && detail) return `${base} (${detail})`;
28
+ if (base) return base;
29
+ if (detail) return detail;
22
30
  } catch {}
23
31
  return response.statusText || `HTTP ${response.status}`;
24
32
  };
@@ -165,7 +173,7 @@ const ensureGithubRepoAccessible = async ({
165
173
  return {
166
174
  ok: false,
167
175
  status: 400,
168
- error: `Failed to create repo: ${details}.${hint}`,
176
+ error: `Failed to create repo: ${details.replace(/\.$/, "")}${hint ? `. ${hint.trim()}` : ""}`,
169
177
  };
170
178
  }
171
179
  console.log(`[onboard] Repo ${repoUrl} created`);
@@ -1,13 +1,10 @@
1
- const path = require("path");
2
1
  const { buildSecretReplacements } = require("../helpers");
2
+ const {
3
+ ensurePluginsShell,
4
+ ensurePluginAllowed,
5
+ ensureUsageTrackerPluginEntry,
6
+ } = require("../usage-tracker-config");
3
7
 
4
- const kUsageTrackerPluginPath = path.resolve(
5
- __dirname,
6
- "..",
7
- "..",
8
- "plugin",
9
- "usage-tracker",
10
- );
11
8
  const kDefaultToolsProfile = "full";
12
9
  const kBootstrapExtraFiles = [
13
10
  "hooks/bootstrap/AGENTS.md",
@@ -136,19 +133,9 @@ const buildOnboardArgs = ({
136
133
  return onboardArgs;
137
134
  };
138
135
 
139
- const ensurePluginAllowed = (cfg, pluginKey) => {
140
- if (!cfg.plugins.allow.includes(pluginKey)) {
141
- cfg.plugins.allow.push(pluginKey);
142
- }
143
- };
144
-
145
136
  const ensureManagedConfigShell = (cfg) => {
146
137
  if (!cfg.channels) cfg.channels = {};
147
- if (!cfg.plugins) cfg.plugins = {};
148
- if (!Array.isArray(cfg.plugins.allow)) cfg.plugins.allow = [];
149
- if (!cfg.plugins.load) cfg.plugins.load = {};
150
- if (!Array.isArray(cfg.plugins.load.paths)) cfg.plugins.load.paths = [];
151
- if (!cfg.plugins.entries) cfg.plugins.entries = {};
138
+ ensurePluginsShell(cfg);
152
139
  if (!cfg.commands) cfg.commands = {};
153
140
  if (!cfg.tools) cfg.tools = {};
154
141
  if (!cfg.hooks) cfg.hooks = {};
@@ -184,7 +171,7 @@ const applyFreshOnboardingChannels = ({ cfg, varMap }) => {
184
171
  groupPolicy: "allowlist",
185
172
  };
186
173
  cfg.plugins.entries.telegram = { enabled: true };
187
- ensurePluginAllowed(cfg, "telegram");
174
+ ensurePluginAllowed({ cfg, pluginKey: "telegram" });
188
175
  console.log("[onboard] Telegram configured");
189
176
  }
190
177
  if (varMap.DISCORD_BOT_TOKEN) {
@@ -195,7 +182,7 @@ const applyFreshOnboardingChannels = ({ cfg, varMap }) => {
195
182
  groupPolicy: "allowlist",
196
183
  };
197
184
  cfg.plugins.entries.discord = { enabled: true };
198
- ensurePluginAllowed(cfg, "discord");
185
+ ensurePluginAllowed({ cfg, pluginKey: "discord" });
199
186
  console.log("[onboard] Discord configured");
200
187
  }
201
188
  if (varMap.SLACK_BOT_TOKEN && varMap.SLACK_APP_TOKEN) {
@@ -208,14 +195,10 @@ const applyFreshOnboardingChannels = ({ cfg, varMap }) => {
208
195
  groupPolicy: "open",
209
196
  };
210
197
  cfg.plugins.entries.slack = { enabled: true };
211
- ensurePluginAllowed(cfg, "slack");
198
+ ensurePluginAllowed({ cfg, pluginKey: "slack" });
212
199
  console.log("[onboard] Slack configured");
213
200
  }
214
- if (!cfg.plugins.load.paths.includes(kUsageTrackerPluginPath)) {
215
- cfg.plugins.load.paths.push(kUsageTrackerPluginPath);
216
- }
217
- ensurePluginAllowed(cfg, "usage-tracker");
218
- cfg.plugins.entries["usage-tracker"] = { enabled: true };
201
+ ensureUsageTrackerPluginEntry(cfg);
219
202
  };
220
203
 
221
204
  const writeSanitizedOpenclawConfig = ({ fs, openclawDir, varMap }) => {
@@ -248,14 +231,7 @@ const writeManagedImportOpenclawConfig = ({ fs, openclawDir, varMap }) => {
248
231
  const cfg = JSON.parse(fs.readFileSync(configPath, "utf8"));
249
232
  ensureManagedConfigShell(cfg);
250
233
 
251
- if (!cfg.plugins.load.paths.includes(kUsageTrackerPluginPath)) {
252
- cfg.plugins.load.paths.push(kUsageTrackerPluginPath);
253
- }
254
- ensurePluginAllowed(cfg, "usage-tracker");
255
- cfg.plugins.entries["usage-tracker"] = {
256
- ...(cfg.plugins.entries["usage-tracker"] || {}),
257
- enabled: true,
258
- };
234
+ ensureUsageTrackerPluginEntry(cfg);
259
235
 
260
236
  if (varMap.TELEGRAM_BOT_TOKEN) {
261
237
  cfg.channels.telegram = {
@@ -269,7 +245,7 @@ const writeManagedImportOpenclawConfig = ({ fs, openclawDir, varMap }) => {
269
245
  ...(cfg.plugins.entries.telegram || {}),
270
246
  enabled: true,
271
247
  };
272
- ensurePluginAllowed(cfg, "telegram");
248
+ ensurePluginAllowed({ cfg, pluginKey: "telegram" });
273
249
  }
274
250
 
275
251
  if (varMap.DISCORD_BOT_TOKEN) {
@@ -284,7 +260,7 @@ const writeManagedImportOpenclawConfig = ({ fs, openclawDir, varMap }) => {
284
260
  ...(cfg.plugins.entries.discord || {}),
285
261
  enabled: true,
286
262
  };
287
- ensurePluginAllowed(cfg, "discord");
263
+ ensurePluginAllowed({ cfg, pluginKey: "discord" });
288
264
  }
289
265
 
290
266
  if (varMap.SLACK_BOT_TOKEN && varMap.SLACK_APP_TOKEN) {
@@ -301,7 +277,7 @@ const writeManagedImportOpenclawConfig = ({ fs, openclawDir, varMap }) => {
301
277
  ...(cfg.plugins.entries.slack || {}),
302
278
  enabled: true,
303
279
  };
304
- ensurePluginAllowed(cfg, "slack");
280
+ ensurePluginAllowed({ cfg, pluginKey: "slack" });
305
281
  }
306
282
 
307
283
  fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2));
@@ -1,4 +1,5 @@
1
1
  const runOnboardedBootSequence = ({
2
+ ensureUsageTrackerPluginConfig,
2
3
  doSyncPromptFiles,
3
4
  reloadEnv,
4
5
  syncChannelConfig,
@@ -9,6 +10,13 @@ const runOnboardedBootSequence = ({
9
10
  watchdog,
10
11
  gmailWatchService,
11
12
  }) => {
13
+ try {
14
+ ensureUsageTrackerPluginConfig();
15
+ } catch (error) {
16
+ console.error(
17
+ `[alphaclaw] Failed to ensure usage-tracker plugin config on boot: ${error.message}`,
18
+ );
19
+ }
12
20
  doSyncPromptFiles();
13
21
  reloadEnv();
14
22
  syncChannelConfig(readEnvFile());
@@ -0,0 +1,71 @@
1
+ const path = require("path");
2
+ const { readOpenclawConfig, writeOpenclawConfig } = require("./openclaw-config");
3
+
4
+ const kUsageTrackerPluginPath = path.resolve(
5
+ __dirname,
6
+ "..",
7
+ "plugin",
8
+ "usage-tracker",
9
+ );
10
+
11
+ const ensurePluginsShell = (cfg = {}) => {
12
+ if (!cfg.plugins || typeof cfg.plugins !== "object") cfg.plugins = {};
13
+ if (!Array.isArray(cfg.plugins.allow)) cfg.plugins.allow = [];
14
+ if (!cfg.plugins.load || typeof cfg.plugins.load !== "object") {
15
+ cfg.plugins.load = {};
16
+ }
17
+ if (!Array.isArray(cfg.plugins.load.paths)) cfg.plugins.load.paths = [];
18
+ if (!cfg.plugins.entries || typeof cfg.plugins.entries !== "object") {
19
+ cfg.plugins.entries = {};
20
+ }
21
+ };
22
+
23
+ const ensurePluginAllowed = ({ cfg = {}, pluginKey = "" }) => {
24
+ const normalizedPluginKey = String(pluginKey || "").trim();
25
+ if (!normalizedPluginKey) return;
26
+ ensurePluginsShell(cfg);
27
+ if (!cfg.plugins.allow.includes(normalizedPluginKey)) {
28
+ cfg.plugins.allow.push(normalizedPluginKey);
29
+ }
30
+ };
31
+
32
+ const ensureUsageTrackerPluginEntry = (cfg = {}) => {
33
+ const before = JSON.stringify(cfg);
34
+ ensurePluginAllowed({ cfg, pluginKey: "usage-tracker" });
35
+ if (!cfg.plugins.load.paths.includes(kUsageTrackerPluginPath)) {
36
+ cfg.plugins.load.paths.push(kUsageTrackerPluginPath);
37
+ }
38
+ cfg.plugins.entries["usage-tracker"] = {
39
+ ...(cfg.plugins.entries["usage-tracker"] &&
40
+ typeof cfg.plugins.entries["usage-tracker"] === "object"
41
+ ? cfg.plugins.entries["usage-tracker"]
42
+ : {}),
43
+ enabled: true,
44
+ };
45
+ return JSON.stringify(cfg) !== before;
46
+ };
47
+
48
+ const ensureUsageTrackerPluginConfig = ({ fsModule, openclawDir }) => {
49
+ const cfg = readOpenclawConfig({
50
+ fsModule,
51
+ openclawDir,
52
+ fallback: {},
53
+ });
54
+ const changed = ensureUsageTrackerPluginEntry(cfg);
55
+ if (!changed) return false;
56
+ writeOpenclawConfig({
57
+ fsModule,
58
+ openclawDir,
59
+ config: cfg,
60
+ spacing: 2,
61
+ });
62
+ return true;
63
+ };
64
+
65
+ module.exports = {
66
+ kUsageTrackerPluginPath,
67
+ ensurePluginsShell,
68
+ ensurePluginAllowed,
69
+ ensureUsageTrackerPluginEntry,
70
+ ensureUsageTrackerPluginConfig,
71
+ };
package/lib/server.js CHANGED
@@ -133,6 +133,9 @@ const {
133
133
  startServerLifecycle,
134
134
  registerServerShutdown,
135
135
  } = require("./server/init/server-lifecycle");
136
+ const {
137
+ ensureUsageTrackerPluginConfig,
138
+ } = require("./server/usage-tracker-config");
136
139
 
137
140
  const { PORT, kTrustProxyHops, SETUP_API_PREFIXES } = constants;
138
141
 
@@ -338,6 +341,11 @@ startServerLifecycle({
338
341
  PORT,
339
342
  isOnboarded,
340
343
  runOnboardedBootSequence,
344
+ ensureUsageTrackerPluginConfig: () =>
345
+ ensureUsageTrackerPluginConfig({
346
+ fsModule: fs,
347
+ openclawDir: constants.OPENCLAW_DIR,
348
+ }),
341
349
  doSyncPromptFiles,
342
350
  reloadEnv,
343
351
  syncChannelConfig,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.8.1-beta.5",
3
+ "version": "0.8.1-beta.7",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },