@chrysb/alphaclaw 0.9.30 → 0.9.31

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.
@@ -14,6 +14,7 @@ const { registerWebhookRoutes } = require("../routes/webhooks");
14
14
  const { registerWatchdogRoutes } = require("../routes/watchdog");
15
15
  const { registerUsageRoutes } = require("../routes/usage");
16
16
  const { registerGmailRoutes } = require("../routes/gmail");
17
+ const { ensureWebhookMappingIds } = require("../webhooks");
17
18
  const { registerDoctorRoutes } = require("../routes/doctor");
18
19
  const { registerAgentRoutes } = require("../routes/agents");
19
20
  const { registerCronRoutes } = require("../routes/cron");
@@ -178,6 +179,7 @@ const registerServerRoutes = ({
178
179
  runOnboardedBootSequence({
179
180
  ensureManagedExecDefaults,
180
181
  ensureUsageTrackerPluginConfig,
182
+ ensureWebhookMappingIds: () => ensureWebhookMappingIds({ fs, constants }),
181
183
  doSyncPromptFiles,
182
184
  reloadEnv,
183
185
  syncChannelConfig,
@@ -1,6 +1,7 @@
1
1
  const runOnboardedBootSequence = ({
2
2
  ensureManagedExecDefaults,
3
3
  ensureUsageTrackerPluginConfig,
4
+ ensureWebhookMappingIds,
4
5
  doSyncPromptFiles,
5
6
  reloadEnv,
6
7
  syncChannelConfig,
@@ -25,6 +26,18 @@ const runOnboardedBootSequence = ({
25
26
  `[alphaclaw] Failed to ensure usage-tracker plugin config on boot: ${error.message}`,
26
27
  );
27
28
  }
29
+ try {
30
+ const result = ensureWebhookMappingIds();
31
+ if (result?.changed) {
32
+ console.log(
33
+ `[alphaclaw] Added IDs to webhook mappings: ${result.updatedIds.join(", ")}`,
34
+ );
35
+ }
36
+ } catch (error) {
37
+ console.error(
38
+ `[alphaclaw] Failed to ensure webhook mapping IDs on boot: ${error.message}`,
39
+ );
40
+ }
28
41
  doSyncPromptFiles();
29
42
  reloadEnv();
30
43
  syncChannelConfig(readEnvFile());
@@ -1,4 +1,8 @@
1
1
  const path = require("path");
2
+ const {
3
+ readOpenclawConfig,
4
+ resolveOpenclawConfigPath,
5
+ } = require("./openclaw-config");
2
6
 
3
7
  const kNamePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
4
8
  const kTransformsDir = "hooks/transforms";
@@ -12,11 +16,16 @@ const kManagedWebhookConfigs = [
12
16
  ];
13
17
 
14
18
  const getConfigPath = ({ OPENCLAW_DIR }) =>
15
- path.join(OPENCLAW_DIR, "openclaw.json");
19
+ resolveOpenclawConfigPath({ openclawDir: OPENCLAW_DIR });
16
20
 
17
21
  const readConfig = ({ fs, constants }) => {
18
22
  const configPath = getConfigPath(constants);
19
- const cfg = JSON.parse(fs.readFileSync(configPath, "utf8"));
23
+ const cfg = readOpenclawConfig({
24
+ fsModule: fs,
25
+ openclawDir: constants.OPENCLAW_DIR,
26
+ fallback: null,
27
+ });
28
+ if (!cfg) throw new Error("Could not read openclaw.json");
20
29
  return { cfg, configPath };
21
30
  };
22
31
 
@@ -78,6 +87,32 @@ const ensureHooksRoot = (cfg) => {
78
87
 
79
88
  const getMappingHookName = (mapping) =>
80
89
  String(mapping?.match?.path || "").trim();
90
+ const normalizeMappingIdPart = (value) =>
91
+ String(value || "")
92
+ .trim()
93
+ .toLowerCase()
94
+ .replace(/[^a-z0-9]+/g, "-")
95
+ .replace(/-+/g, "-")
96
+ .replace(/^-|-$/g, "");
97
+ const getMappingIdBase = (mapping, index) => {
98
+ const hookPath = normalizeMappingIdPart(getMappingHookName(mapping));
99
+ if (hookPath) return hookPath;
100
+ const name = normalizeMappingIdPart(mapping?.name);
101
+ if (name) return name;
102
+ const source = normalizeMappingIdPart(mapping?.match?.source);
103
+ if (source) return `source-${source}`;
104
+ return `mapping-${index + 1}`;
105
+ };
106
+ const reserveUniqueMappingId = (baseId, usedIds) => {
107
+ let candidate = baseId;
108
+ let suffix = 2;
109
+ while (usedIds.has(candidate)) {
110
+ candidate = `${baseId}-${suffix}`;
111
+ suffix += 1;
112
+ }
113
+ usedIds.add(candidate);
114
+ return candidate;
115
+ };
81
116
  const isWebhookMapping = (mapping) => !!getMappingHookName(mapping);
82
117
  const findMappingIndexByName = (mappings, name) =>
83
118
  mappings.findIndex((mapping) => getMappingHookName(mapping) === name);
@@ -142,6 +177,35 @@ const normalizeMappingTransformModules = (mappings) => {
142
177
  return changed;
143
178
  };
144
179
 
180
+ const ensureWebhookMappingIds = ({ fs, constants }) => {
181
+ const { cfg, configPath } = readConfig({ fs, constants });
182
+ const mappings = Array.isArray(cfg?.hooks?.mappings)
183
+ ? cfg.hooks.mappings
184
+ : [];
185
+ const usedIds = new Set(
186
+ mappings
187
+ .map((mapping) => String(mapping?.id || "").trim())
188
+ .filter(Boolean),
189
+ );
190
+ const updatedIds = [];
191
+ for (const [index, mapping] of mappings.entries()) {
192
+ if (!mapping || typeof mapping !== "object" || Array.isArray(mapping)) {
193
+ continue;
194
+ }
195
+ if (String(mapping?.id || "").trim()) continue;
196
+ const id = reserveUniqueMappingId(
197
+ getMappingIdBase(mapping, index),
198
+ usedIds,
199
+ );
200
+ mapping.id = id;
201
+ updatedIds.push(id);
202
+ }
203
+ if (updatedIds.length > 0) {
204
+ writeConfig({ fs, configPath, cfg });
205
+ }
206
+ return { changed: updatedIds.length > 0, updatedIds };
207
+ };
208
+
145
209
  const buildDefaultTransformSource = (name) => {
146
210
  return [
147
211
  "export default async function transform(payload, context) {",
@@ -188,6 +252,7 @@ const ensureWebhookMapping = ({ cfg, name, mapping = {} }) => {
188
252
  const normalizedModulesChanged = normalizeMappingTransformModules(mappings);
189
253
  const index = findMappingIndexByName(mappings, webhookName);
190
254
  const defaults = {
255
+ id: webhookName,
191
256
  match: { path: webhookName },
192
257
  action: "agent",
193
258
  name: webhookName,
@@ -198,6 +263,7 @@ const ensureWebhookMapping = ({ cfg, name, mapping = {} }) => {
198
263
  mappings.push({
199
264
  ...defaults,
200
265
  ...mapping,
266
+ id: webhookName,
201
267
  match: { ...defaults.match, ...(mapping.match || {}) },
202
268
  transform: { ...defaults.transform, ...(mapping.transform || {}) },
203
269
  });
@@ -207,6 +273,7 @@ const ensureWebhookMapping = ({ cfg, name, mapping = {} }) => {
207
273
  const next = {
208
274
  ...current,
209
275
  ...mapping,
276
+ id: webhookName,
210
277
  match: {
211
278
  ...(current.match || {}),
212
279
  ...(mapping.match || {}),
@@ -436,6 +503,7 @@ const updateWebhookDestination = ({ fs, constants, name, destination = null }) =
436
503
  });
437
504
  const next = {
438
505
  ...current,
506
+ id: webhookName,
439
507
  deliver: true,
440
508
  channel:
441
509
  String(normalizedDestination?.channel || "").trim() ||
@@ -499,6 +567,7 @@ const deleteWebhook = ({ fs, constants, name, deleteTransformDir = false }) => {
499
567
  };
500
568
 
501
569
  module.exports = {
570
+ ensureWebhookMappingIds,
502
571
  listWebhooks,
503
572
  getWebhookDetail,
504
573
  createWebhook,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.9.30",
3
+ "version": "0.9.31",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },