@xmoxmo/bncr 0.3.0 → 0.3.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.
- package/dist/index.js +754 -0
- package/package.json +10 -4
package/dist/index.js
ADDED
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
// index.ts
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
// src/core/config-schema.ts
|
|
9
|
+
var BncrConfigSchema = {
|
|
10
|
+
schema: {
|
|
11
|
+
type: "object",
|
|
12
|
+
additionalProperties: true,
|
|
13
|
+
properties: {
|
|
14
|
+
enabled: { type: "boolean" },
|
|
15
|
+
dmPolicy: {
|
|
16
|
+
type: "string",
|
|
17
|
+
enum: ["open", "allowlist", "disabled"]
|
|
18
|
+
},
|
|
19
|
+
groupPolicy: {
|
|
20
|
+
type: "string",
|
|
21
|
+
enum: ["open", "allowlist", "disabled"]
|
|
22
|
+
},
|
|
23
|
+
allowFrom: {
|
|
24
|
+
type: "array",
|
|
25
|
+
items: { type: "string" }
|
|
26
|
+
},
|
|
27
|
+
groupAllowFrom: {
|
|
28
|
+
type: "array",
|
|
29
|
+
items: { type: "string" }
|
|
30
|
+
},
|
|
31
|
+
debug: {
|
|
32
|
+
type: "object",
|
|
33
|
+
additionalProperties: true,
|
|
34
|
+
properties: {
|
|
35
|
+
verbose: {
|
|
36
|
+
type: "boolean",
|
|
37
|
+
default: false,
|
|
38
|
+
description: "Enable verbose debug logs for bncr channel runtime."
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
allowTool: {
|
|
43
|
+
type: "boolean",
|
|
44
|
+
default: false,
|
|
45
|
+
description: "Allow tool messages to be forwarded when streaming is enabled. Defaults to false; only explicit true enables forwarding. When enabled, bncr also requests upstream tool summaries/results."
|
|
46
|
+
},
|
|
47
|
+
requireMention: {
|
|
48
|
+
type: "boolean",
|
|
49
|
+
default: false,
|
|
50
|
+
description: "Whether group messages must explicitly mention the bot before bncr handles them. Default false. Current version keeps this as a reserved field and does not enforce it yet."
|
|
51
|
+
},
|
|
52
|
+
outboundRequireAck: {
|
|
53
|
+
type: "boolean",
|
|
54
|
+
default: true,
|
|
55
|
+
description: "Whether outbound text waits for bncr.ack before leaving the retry queue. Default true to preserve current ack/dead-letter behavior."
|
|
56
|
+
},
|
|
57
|
+
accounts: {
|
|
58
|
+
type: "object",
|
|
59
|
+
additionalProperties: {
|
|
60
|
+
type: "object",
|
|
61
|
+
additionalProperties: true,
|
|
62
|
+
properties: {
|
|
63
|
+
enabled: { type: "boolean" },
|
|
64
|
+
name: { type: "string" }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// src/core/logging.ts
|
|
73
|
+
var BNCR_PREFIX = "[bncr]";
|
|
74
|
+
function resolveConsoleMethod(level) {
|
|
75
|
+
switch (level) {
|
|
76
|
+
case "warn":
|
|
77
|
+
return "warn";
|
|
78
|
+
case "error":
|
|
79
|
+
return "error";
|
|
80
|
+
default:
|
|
81
|
+
return "log";
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function emitConsole(method, line) {
|
|
85
|
+
if (method === "warn") {
|
|
86
|
+
console.warn(line);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (method === "error") {
|
|
90
|
+
console.error(line);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
console.log(line);
|
|
94
|
+
}
|
|
95
|
+
function normalizeBncrLogLine(raw) {
|
|
96
|
+
const text = String(raw || "").trim();
|
|
97
|
+
if (!text) return BNCR_PREFIX;
|
|
98
|
+
return text.startsWith(BNCR_PREFIX) ? text : `${BNCR_PREFIX} ${text}`;
|
|
99
|
+
}
|
|
100
|
+
function emitBncrLogLine(level, line, options, isDebugEnabled) {
|
|
101
|
+
if (options?.debugOnly && !(isDebugEnabled?.() ?? false)) return;
|
|
102
|
+
emitConsole(resolveConsoleMethod(level), normalizeBncrLogLine(line));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/openclaw/config-runtime.ts
|
|
106
|
+
function resolveConfigApi(api) {
|
|
107
|
+
const config = api?.runtime?.config;
|
|
108
|
+
if (!config) throw new Error("OpenClaw runtime config API is unavailable");
|
|
109
|
+
return config;
|
|
110
|
+
}
|
|
111
|
+
function getOpenClawRuntimeConfig(api) {
|
|
112
|
+
const config = resolveConfigApi(api);
|
|
113
|
+
if (typeof config.current === "function") return config.current();
|
|
114
|
+
if (typeof config.get === "function") return config.get();
|
|
115
|
+
throw new Error("OpenClaw runtime config read API is unavailable");
|
|
116
|
+
}
|
|
117
|
+
async function mutateOpenClawRuntimeConfigFile(api, params) {
|
|
118
|
+
const config = resolveConfigApi(api);
|
|
119
|
+
if (typeof config.mutateConfigFile !== "function") {
|
|
120
|
+
throw new Error("OpenClaw runtime config mutate API is unavailable");
|
|
121
|
+
}
|
|
122
|
+
return config.mutateConfigFile(params);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// index.ts
|
|
126
|
+
var pluginFile = fileURLToPath(import.meta.url);
|
|
127
|
+
var pluginDir = path.dirname(pluginFile);
|
|
128
|
+
var pluginRequire = createRequire(import.meta.url);
|
|
129
|
+
var sdkCoreSpecifier = "openclaw/plugin-sdk/core";
|
|
130
|
+
var linkType = process.platform === "win32" ? "junction" : "dir";
|
|
131
|
+
var BNCR_REGISTER_META = /* @__PURE__ */ Symbol.for("bncr.register.meta");
|
|
132
|
+
var BNCR_GLOBAL_REGISTER_TRACE = /* @__PURE__ */ Symbol.for("bncr.global.register.trace");
|
|
133
|
+
var BNCR_BRIDGE_OWNER = /* @__PURE__ */ Symbol.for("bncr.bridge.owner");
|
|
134
|
+
var BNCR_GATEWAY_RUNTIME = /* @__PURE__ */ Symbol.for("bncr.gateway.runtime");
|
|
135
|
+
var MODULE_EPOCH = `${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
136
|
+
var runtime = null;
|
|
137
|
+
var activeServiceStop = null;
|
|
138
|
+
var identityIds = /* @__PURE__ */ new WeakMap();
|
|
139
|
+
var identitySeq = 0;
|
|
140
|
+
var tryExec = (command, args) => {
|
|
141
|
+
try {
|
|
142
|
+
return execFileSync(command, args, {
|
|
143
|
+
encoding: "utf8",
|
|
144
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
145
|
+
}).trim();
|
|
146
|
+
} catch {
|
|
147
|
+
return "";
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
var readOpenClawPackageName = (pkgPath) => {
|
|
151
|
+
try {
|
|
152
|
+
const raw = fs.readFileSync(pkgPath, "utf8");
|
|
153
|
+
const parsed = JSON.parse(raw);
|
|
154
|
+
return typeof parsed?.name === "string" ? parsed.name : "";
|
|
155
|
+
} catch {
|
|
156
|
+
return "";
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
var readPluginVersion = () => {
|
|
160
|
+
try {
|
|
161
|
+
const raw = fs.readFileSync(path.join(pluginDir, "package.json"), "utf8");
|
|
162
|
+
const parsed = JSON.parse(raw);
|
|
163
|
+
return typeof parsed?.version === "string" ? parsed.version : "unknown";
|
|
164
|
+
} catch {
|
|
165
|
+
return "unknown";
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
var pluginVersion = readPluginVersion();
|
|
169
|
+
var findOpenClawPackageRoot = (startPath) => {
|
|
170
|
+
let current = startPath;
|
|
171
|
+
try {
|
|
172
|
+
current = fs.realpathSync(startPath);
|
|
173
|
+
} catch {
|
|
174
|
+
}
|
|
175
|
+
let cursor = current;
|
|
176
|
+
while (true) {
|
|
177
|
+
const statPath = fs.existsSync(cursor) ? cursor : path.dirname(cursor);
|
|
178
|
+
const pkgPath = path.join(statPath, "package.json");
|
|
179
|
+
if (fs.existsSync(pkgPath) && readOpenClawPackageName(pkgPath) === "openclaw") {
|
|
180
|
+
return statPath;
|
|
181
|
+
}
|
|
182
|
+
const parent = path.dirname(statPath);
|
|
183
|
+
if (parent === statPath) break;
|
|
184
|
+
cursor = parent;
|
|
185
|
+
}
|
|
186
|
+
return "";
|
|
187
|
+
};
|
|
188
|
+
var unique = (items) => {
|
|
189
|
+
const seen = /* @__PURE__ */ new Set();
|
|
190
|
+
const out = [];
|
|
191
|
+
for (const item of items) {
|
|
192
|
+
if (!item) continue;
|
|
193
|
+
const normalized = path.normalize(item);
|
|
194
|
+
if (seen.has(normalized)) continue;
|
|
195
|
+
seen.add(normalized);
|
|
196
|
+
out.push(normalized);
|
|
197
|
+
}
|
|
198
|
+
return out;
|
|
199
|
+
};
|
|
200
|
+
var collectOpenClawCandidates = () => {
|
|
201
|
+
const directCandidates = [
|
|
202
|
+
path.join(pluginDir, "node_modules", "openclaw"),
|
|
203
|
+
path.join("/usr/lib/node_modules", "openclaw"),
|
|
204
|
+
path.join("/usr/local/lib/node_modules", "openclaw"),
|
|
205
|
+
path.join("/opt/homebrew/lib/node_modules", "openclaw"),
|
|
206
|
+
path.join(process.env.HOME || "", ".npm-global/lib/node_modules", "openclaw")
|
|
207
|
+
];
|
|
208
|
+
const npmRoot = tryExec("npm", ["root", "-g"]);
|
|
209
|
+
if (npmRoot) directCandidates.push(path.join(npmRoot, "openclaw"));
|
|
210
|
+
const nodePathEntries = (process.env.NODE_PATH || "").split(path.delimiter).map((entry) => entry.trim()).filter(Boolean);
|
|
211
|
+
for (const entry of nodePathEntries) {
|
|
212
|
+
directCandidates.push(path.join(entry, "openclaw"));
|
|
213
|
+
}
|
|
214
|
+
const openclawBin = tryExec("which", ["openclaw"]);
|
|
215
|
+
if (openclawBin) {
|
|
216
|
+
directCandidates.push(openclawBin);
|
|
217
|
+
directCandidates.push(path.dirname(openclawBin));
|
|
218
|
+
}
|
|
219
|
+
const packageRoots = unique(
|
|
220
|
+
directCandidates.map((candidate) => findOpenClawPackageRoot(candidate)).filter(Boolean)
|
|
221
|
+
);
|
|
222
|
+
return packageRoots.filter((candidate) => {
|
|
223
|
+
const pkgJson = path.join(candidate, "package.json");
|
|
224
|
+
return fs.existsSync(pkgJson) && readOpenClawPackageName(pkgJson) === "openclaw";
|
|
225
|
+
});
|
|
226
|
+
};
|
|
227
|
+
var canResolveSdkCore = () => {
|
|
228
|
+
try {
|
|
229
|
+
pluginRequire.resolve(sdkCoreSpecifier);
|
|
230
|
+
return true;
|
|
231
|
+
} catch {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
var ensurePluginNodeModulesLink = (targetRoot) => {
|
|
236
|
+
const nodeModulesDir = path.join(pluginDir, "node_modules");
|
|
237
|
+
const linkPath = path.join(nodeModulesDir, "openclaw");
|
|
238
|
+
fs.mkdirSync(nodeModulesDir, { recursive: true });
|
|
239
|
+
try {
|
|
240
|
+
const stat = fs.lstatSync(linkPath);
|
|
241
|
+
if (stat.isSymbolicLink()) {
|
|
242
|
+
const existingTarget = fs.realpathSync(linkPath);
|
|
243
|
+
const normalizedExisting = path.normalize(existingTarget);
|
|
244
|
+
const normalizedTarget = path.normalize(fs.realpathSync(targetRoot));
|
|
245
|
+
if (normalizedExisting === normalizedTarget) return;
|
|
246
|
+
fs.unlinkSync(linkPath);
|
|
247
|
+
} else {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
} catch {
|
|
251
|
+
}
|
|
252
|
+
fs.symlinkSync(targetRoot, linkPath, linkType);
|
|
253
|
+
};
|
|
254
|
+
var ensureOpenClawSdkResolution = () => {
|
|
255
|
+
if (canResolveSdkCore()) return;
|
|
256
|
+
let lastError = "";
|
|
257
|
+
const candidates = collectOpenClawCandidates();
|
|
258
|
+
for (const candidate of candidates) {
|
|
259
|
+
try {
|
|
260
|
+
ensurePluginNodeModulesLink(candidate);
|
|
261
|
+
if (canResolveSdkCore()) return;
|
|
262
|
+
} catch (error) {
|
|
263
|
+
lastError = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const suffix = candidates.length ? ` Tried candidates: ${candidates.join(", ")}.` : " No openclaw package root candidates were found from npm root, NODE_PATH, common global paths, or the openclaw binary path.";
|
|
267
|
+
const extra = lastError ? ` Last repair error: ${lastError}.` : "";
|
|
268
|
+
throw new Error(
|
|
269
|
+
`bncr failed to resolve ${sdkCoreSpecifier} from ${pluginDir}.${suffix}${extra} You can repair manually with: mkdir -p ${path.join(pluginDir, "node_modules")} && ln -s "$(npm root -g)/openclaw" ${path.join(pluginDir, "node_modules", "openclaw")}`
|
|
270
|
+
);
|
|
271
|
+
};
|
|
272
|
+
var loadRuntimeSync = () => {
|
|
273
|
+
if (runtime) return runtime;
|
|
274
|
+
ensureOpenClawSdkResolution();
|
|
275
|
+
try {
|
|
276
|
+
const mod = pluginRequire("./src/channel.ts");
|
|
277
|
+
runtime = {
|
|
278
|
+
createBncrBridge: mod.createBncrBridge,
|
|
279
|
+
createBncrChannelPlugin: mod.createBncrChannelPlugin
|
|
280
|
+
};
|
|
281
|
+
return runtime;
|
|
282
|
+
} catch (error) {
|
|
283
|
+
const detail = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
284
|
+
throw new Error(`bncr failed to load channel runtime after dependency bootstrap: ${detail}`);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
var getIdentityId = (obj, prefix) => {
|
|
288
|
+
const existing = identityIds.get(obj);
|
|
289
|
+
if (existing) return existing;
|
|
290
|
+
const next = `${prefix}_${MODULE_EPOCH}_${++identitySeq}`;
|
|
291
|
+
identityIds.set(obj, next);
|
|
292
|
+
return next;
|
|
293
|
+
};
|
|
294
|
+
var getRegistryFingerprint = (api) => {
|
|
295
|
+
const serviceId = getIdentityId(api.registerService, "svc");
|
|
296
|
+
const channelId = getIdentityId(api.registerChannel, "chn");
|
|
297
|
+
const methodId = getIdentityId(api.registerGatewayMethod, "mth");
|
|
298
|
+
return `${serviceId}:${channelId}:${methodId}`;
|
|
299
|
+
};
|
|
300
|
+
var getRegisterMeta = (api) => {
|
|
301
|
+
const host = api;
|
|
302
|
+
if (!host[BNCR_REGISTER_META]) {
|
|
303
|
+
host[BNCR_REGISTER_META] = { methods: /* @__PURE__ */ new Set() };
|
|
304
|
+
}
|
|
305
|
+
if (!host[BNCR_REGISTER_META].methods) {
|
|
306
|
+
host[BNCR_REGISTER_META].methods = /* @__PURE__ */ new Set();
|
|
307
|
+
}
|
|
308
|
+
if (!host[BNCR_REGISTER_META].apiInstanceId) {
|
|
309
|
+
host[BNCR_REGISTER_META].apiInstanceId = getIdentityId(api, "api");
|
|
310
|
+
}
|
|
311
|
+
if (!host[BNCR_REGISTER_META].registryFingerprint) {
|
|
312
|
+
host[BNCR_REGISTER_META].registryFingerprint = getRegistryFingerprint(api);
|
|
313
|
+
}
|
|
314
|
+
return host[BNCR_REGISTER_META];
|
|
315
|
+
};
|
|
316
|
+
var getProcessStore = () => {
|
|
317
|
+
const p = process;
|
|
318
|
+
return p;
|
|
319
|
+
};
|
|
320
|
+
var getGlobalRegisterTrace = () => {
|
|
321
|
+
const p = getProcessStore();
|
|
322
|
+
if (!p[BNCR_GLOBAL_REGISTER_TRACE]) {
|
|
323
|
+
p[BNCR_GLOBAL_REGISTER_TRACE] = {
|
|
324
|
+
seenRegistryFingerprints: /* @__PURE__ */ new Set(),
|
|
325
|
+
seenApiInstanceIds: /* @__PURE__ */ new Set()
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
return p[BNCR_GLOBAL_REGISTER_TRACE];
|
|
329
|
+
};
|
|
330
|
+
var getGatewayRuntime = () => {
|
|
331
|
+
const p = getProcessStore();
|
|
332
|
+
if (!p[BNCR_GATEWAY_RUNTIME]) {
|
|
333
|
+
p[BNCR_GATEWAY_RUNTIME] = {
|
|
334
|
+
registeredMethodsByRegistry: /* @__PURE__ */ new Map(),
|
|
335
|
+
serviceRegistered: false,
|
|
336
|
+
channelRegistered: false
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
return p[BNCR_GATEWAY_RUNTIME];
|
|
340
|
+
};
|
|
341
|
+
var getProcessOwnerApiInstanceId = (gatewayRuntime) => gatewayRuntime.serviceOwnerApiInstanceId || gatewayRuntime.channelOwnerApiInstanceId || void 0;
|
|
342
|
+
var shouldAdoptProcessOwner = (apiInstanceId, gatewayRuntime) => {
|
|
343
|
+
const existingOwnerApiInstanceId = getProcessOwnerApiInstanceId(gatewayRuntime);
|
|
344
|
+
const hasSingletonOwner = Boolean(gatewayRuntime.serviceRegistered) || Boolean(gatewayRuntime.channelRegistered);
|
|
345
|
+
if (!hasSingletonOwner) {
|
|
346
|
+
return {
|
|
347
|
+
adoptOwner: true,
|
|
348
|
+
existingOwnerApiInstanceId,
|
|
349
|
+
reason: "no-singleton-owner"
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
if (existingOwnerApiInstanceId && existingOwnerApiInstanceId === apiInstanceId) {
|
|
353
|
+
return {
|
|
354
|
+
adoptOwner: true,
|
|
355
|
+
existingOwnerApiInstanceId,
|
|
356
|
+
reason: "same-owner-api"
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
adoptOwner: false,
|
|
361
|
+
existingOwnerApiInstanceId,
|
|
362
|
+
reason: "singleton-owned-by-other-api"
|
|
363
|
+
};
|
|
364
|
+
};
|
|
365
|
+
var gatewayMethodDispatchers = {
|
|
366
|
+
"bncr.connect": (bridge, opts) => bridge.handleConnect(opts),
|
|
367
|
+
"bncr.inbound": (bridge, opts) => bridge.handleInbound(opts),
|
|
368
|
+
"bncr.activity": (bridge, opts) => bridge.handleActivity(opts),
|
|
369
|
+
"bncr.ack": (bridge, opts) => bridge.handleAck(opts),
|
|
370
|
+
"bncr.diagnostics": (bridge, opts) => bridge.handleDiagnostics(opts),
|
|
371
|
+
"bncr.file.init": (bridge, opts) => bridge.handleFileInit(opts),
|
|
372
|
+
"bncr.file.chunk": (bridge, opts) => bridge.handleFileChunk(opts),
|
|
373
|
+
"bncr.file.complete": (bridge, opts) => bridge.handleFileComplete(opts),
|
|
374
|
+
"bncr.file.abort": (bridge, opts) => bridge.handleFileAbort(opts),
|
|
375
|
+
"bncr.file.ack": (bridge, opts) => bridge.handleFileAck(opts)
|
|
376
|
+
};
|
|
377
|
+
var dispatchGatewayMethod = (name, opts) => {
|
|
378
|
+
const gatewayRuntime = getGatewayRuntime();
|
|
379
|
+
const bridge = gatewayRuntime.currentBridge;
|
|
380
|
+
if (!bridge) {
|
|
381
|
+
throw new Error(`bncr gateway runtime unavailable for ${name}`);
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
return gatewayMethodDispatchers[name](bridge, opts);
|
|
385
|
+
} catch (error) {
|
|
386
|
+
const detail = error instanceof Error ? {
|
|
387
|
+
name: error.name,
|
|
388
|
+
message: error.message,
|
|
389
|
+
stack: error.stack || null
|
|
390
|
+
} : { name: "NonError", message: String(error), stack: null };
|
|
391
|
+
emitBncrLogLine(
|
|
392
|
+
"error",
|
|
393
|
+
`[bncr] gateway method error ${JSON.stringify({
|
|
394
|
+
method: name,
|
|
395
|
+
bridgeId: bridge.getBridgeId?.() || null,
|
|
396
|
+
gatewayPid: bridge.gatewayPid || null,
|
|
397
|
+
detail
|
|
398
|
+
})}`,
|
|
399
|
+
{ debugOnly: true },
|
|
400
|
+
() => true
|
|
401
|
+
);
|
|
402
|
+
throw error;
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
var mirrorGatewayMethodForMockApi = (api, name) => {
|
|
406
|
+
const host = api;
|
|
407
|
+
if (!Array.isArray(host.methods)) return;
|
|
408
|
+
if (host.methods.some((item) => item?.name === name)) return;
|
|
409
|
+
host.methods.push({ name, handler: (opts) => dispatchGatewayMethod(name, opts) });
|
|
410
|
+
};
|
|
411
|
+
var ensureGatewayMethodRegistered = (api, name, debugLog) => {
|
|
412
|
+
const meta = getRegisterMeta(api);
|
|
413
|
+
const gatewayRuntime = getGatewayRuntime();
|
|
414
|
+
const registryFingerprint = meta.registryFingerprint || getRegistryFingerprint(api);
|
|
415
|
+
let registryMethods = gatewayRuntime.registeredMethodsByRegistry.get(registryFingerprint);
|
|
416
|
+
if (!registryMethods) {
|
|
417
|
+
registryMethods = /* @__PURE__ */ new Set();
|
|
418
|
+
gatewayRuntime.registeredMethodsByRegistry.set(registryFingerprint, registryMethods);
|
|
419
|
+
}
|
|
420
|
+
if (meta.methods?.has(name)) {
|
|
421
|
+
debugLog(`register method skip ${name} (already registered on this api)`);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
if (registryMethods.has(name)) {
|
|
425
|
+
mirrorGatewayMethodForMockApi(api, name);
|
|
426
|
+
meta.methods?.add(name);
|
|
427
|
+
debugLog(`register method reuse ${name} (already registered in registry)`);
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
api.registerGatewayMethod(name, (opts) => dispatchGatewayMethod(name, opts));
|
|
431
|
+
mirrorGatewayMethodForMockApi(api, name);
|
|
432
|
+
registryMethods.add(name);
|
|
433
|
+
meta.methods?.add(name);
|
|
434
|
+
debugLog(`register method ok ${name}`);
|
|
435
|
+
};
|
|
436
|
+
var getBridgeOwner = (api, loaded) => {
|
|
437
|
+
const meta = getRegisterMeta(api);
|
|
438
|
+
return {
|
|
439
|
+
moduleEpoch: MODULE_EPOCH,
|
|
440
|
+
bridgeFactoryId: getIdentityId(loaded.createBncrBridge, "bridgeFactory"),
|
|
441
|
+
apiInstanceId: meta.apiInstanceId || "unknown",
|
|
442
|
+
registryFingerprint: meta.registryFingerprint || "unknown",
|
|
443
|
+
registrationMode: meta.registrationMode
|
|
444
|
+
};
|
|
445
|
+
};
|
|
446
|
+
var sameBridgeOwner = (left, right) => {
|
|
447
|
+
if (!left || !right) return false;
|
|
448
|
+
return left.moduleEpoch === right.moduleEpoch && left.bridgeFactoryId === right.bridgeFactoryId && left.apiInstanceId === right.apiInstanceId && left.registryFingerprint === right.registryFingerprint;
|
|
449
|
+
};
|
|
450
|
+
var snapshotBridgeRegisterState = (bridge) => {
|
|
451
|
+
if (!bridge) return null;
|
|
452
|
+
return {
|
|
453
|
+
registerCount: Number(bridge.registerCount || 0),
|
|
454
|
+
apiGeneration: Number(bridge.apiGeneration || 0),
|
|
455
|
+
firstRegisterAt: typeof bridge.firstRegisterAt === "number" ? bridge.firstRegisterAt : bridge.firstRegisterAt ?? null,
|
|
456
|
+
lastRegisterAt: typeof bridge.lastRegisterAt === "number" ? bridge.lastRegisterAt : bridge.lastRegisterAt ?? null,
|
|
457
|
+
lastApiRebindAt: typeof bridge.lastApiRebindAt === "number" ? bridge.lastApiRebindAt : bridge.lastApiRebindAt ?? null,
|
|
458
|
+
pluginSource: typeof bridge.pluginSource === "string" ? bridge.pluginSource : null,
|
|
459
|
+
pluginVersion: typeof bridge.pluginVersion === "string" ? bridge.pluginVersion : null,
|
|
460
|
+
lastApiInstanceId: typeof bridge.lastApiInstanceId === "string" ? bridge.lastApiInstanceId : null,
|
|
461
|
+
lastRegistryFingerprint: typeof bridge.lastRegistryFingerprint === "string" ? bridge.lastRegistryFingerprint : null,
|
|
462
|
+
lastDriftSnapshot: bridge.lastDriftSnapshot ?? null,
|
|
463
|
+
registerTraceRecent: Array.isArray(bridge.registerTraceRecent) ? bridge.registerTraceRecent.map((trace) => ({ ...trace })) : []
|
|
464
|
+
};
|
|
465
|
+
};
|
|
466
|
+
var hydrateBridgeRegisterState = (bridge, snapshot) => {
|
|
467
|
+
if (!snapshot) return bridge;
|
|
468
|
+
bridge.registerCount = snapshot.registerCount;
|
|
469
|
+
bridge.apiGeneration = snapshot.apiGeneration;
|
|
470
|
+
bridge.firstRegisterAt = snapshot.firstRegisterAt;
|
|
471
|
+
bridge.lastRegisterAt = snapshot.lastRegisterAt;
|
|
472
|
+
bridge.lastApiRebindAt = snapshot.lastApiRebindAt;
|
|
473
|
+
bridge.pluginSource = snapshot.pluginSource;
|
|
474
|
+
bridge.pluginVersion = snapshot.pluginVersion;
|
|
475
|
+
bridge.lastApiInstanceId = snapshot.lastApiInstanceId;
|
|
476
|
+
bridge.lastRegistryFingerprint = snapshot.lastRegistryFingerprint;
|
|
477
|
+
bridge.lastDriftSnapshot = snapshot.lastDriftSnapshot;
|
|
478
|
+
bridge.registerTraceRecent = snapshot.registerTraceRecent.map((trace) => ({ ...trace }));
|
|
479
|
+
return bridge;
|
|
480
|
+
};
|
|
481
|
+
var assignBridgeOwner = (bridge, owner) => {
|
|
482
|
+
bridge[BNCR_BRIDGE_OWNER] = owner;
|
|
483
|
+
return bridge;
|
|
484
|
+
};
|
|
485
|
+
var getBridgeSingleton = (api) => {
|
|
486
|
+
const loaded = loadRuntimeSync();
|
|
487
|
+
const g = globalThis;
|
|
488
|
+
const owner = getBridgeOwner(api, loaded);
|
|
489
|
+
const previousOwner = g.__bncrBridge?.[BNCR_BRIDGE_OWNER];
|
|
490
|
+
let created = false;
|
|
491
|
+
let rebuilt = false;
|
|
492
|
+
if (g.__bncrBridge) {
|
|
493
|
+
const mustRebuild = !sameBridgeOwner(previousOwner, owner) && (previousOwner?.moduleEpoch !== owner.moduleEpoch || previousOwner?.bridgeFactoryId !== owner.bridgeFactoryId || previousOwner?.registrationMode !== owner.registrationMode || previousOwner?.apiInstanceId !== owner.apiInstanceId || previousOwner?.registryFingerprint !== owner.registryFingerprint);
|
|
494
|
+
if (mustRebuild) {
|
|
495
|
+
const registerState = snapshotBridgeRegisterState(g.__bncrBridge);
|
|
496
|
+
try {
|
|
497
|
+
g.__bncrBridge.stopService?.();
|
|
498
|
+
} catch {
|
|
499
|
+
}
|
|
500
|
+
g.__bncrBridge = hydrateBridgeRegisterState(
|
|
501
|
+
assignBridgeOwner(loaded.createBncrBridge(api), owner),
|
|
502
|
+
registerState
|
|
503
|
+
);
|
|
504
|
+
created = true;
|
|
505
|
+
rebuilt = true;
|
|
506
|
+
} else {
|
|
507
|
+
g.__bncrBridge.bindApi?.(api);
|
|
508
|
+
assignBridgeOwner(g.__bncrBridge, owner);
|
|
509
|
+
created = false;
|
|
510
|
+
rebuilt = false;
|
|
511
|
+
}
|
|
512
|
+
} else {
|
|
513
|
+
g.__bncrBridge = assignBridgeOwner(loaded.createBncrBridge(api), owner);
|
|
514
|
+
created = true;
|
|
515
|
+
}
|
|
516
|
+
return { bridge: g.__bncrBridge, runtime: loaded, created, rebuilt, owner, previousOwner };
|
|
517
|
+
};
|
|
518
|
+
var getExistingBridgeSingleton = () => {
|
|
519
|
+
const g = globalThis;
|
|
520
|
+
return g.__bncrBridge;
|
|
521
|
+
};
|
|
522
|
+
var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
523
|
+
var getCurrentBridge = () => {
|
|
524
|
+
const bridge = getGatewayRuntime().currentBridge;
|
|
525
|
+
if (!bridge) throw new Error("bncr current bridge unavailable");
|
|
526
|
+
return bridge;
|
|
527
|
+
};
|
|
528
|
+
var createDynamicChannelPlugin = (loaded) => {
|
|
529
|
+
const base = loaded.createBncrChannelPlugin(() => getCurrentBridge());
|
|
530
|
+
return {
|
|
531
|
+
...base,
|
|
532
|
+
outbound: {
|
|
533
|
+
...base.outbound,
|
|
534
|
+
sendText: (ctx) => getCurrentBridge().channelSendText(ctx),
|
|
535
|
+
sendMedia: (ctx) => getCurrentBridge().channelSendMedia(ctx)
|
|
536
|
+
},
|
|
537
|
+
status: {
|
|
538
|
+
...base.status,
|
|
539
|
+
buildChannelSummary: async ({ defaultAccountId }) => getCurrentBridge().getChannelSummary(defaultAccountId || "Primary"),
|
|
540
|
+
buildAccountSnapshot: async ({ account, runtime: runtime2 }) => {
|
|
541
|
+
const bridgeNow = getCurrentBridge();
|
|
542
|
+
return base.status.buildAccountSnapshot({
|
|
543
|
+
account,
|
|
544
|
+
runtime: runtime2 || bridgeNow.getAccountRuntimeSnapshot(account?.accountId)
|
|
545
|
+
});
|
|
546
|
+
},
|
|
547
|
+
resolveAccountState: ({ enabled, configured, account, cfg, runtime: runtime2 }) => {
|
|
548
|
+
const bridgeNow = getCurrentBridge();
|
|
549
|
+
return base.status.resolveAccountState({
|
|
550
|
+
enabled,
|
|
551
|
+
configured,
|
|
552
|
+
account,
|
|
553
|
+
cfg,
|
|
554
|
+
runtime: runtime2 || bridgeNow.getAccountRuntimeSnapshot(account?.accountId)
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
gateway: {
|
|
559
|
+
...base.gateway,
|
|
560
|
+
startAccount: (ctx) => getCurrentBridge().channelStartAccount(ctx),
|
|
561
|
+
stopAccount: (ctx) => getCurrentBridge().channelStopAccount(ctx)
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
};
|
|
565
|
+
var registerBncrCli = (api) => {
|
|
566
|
+
if (typeof api.registerCli !== "function") return;
|
|
567
|
+
api.registerCli(
|
|
568
|
+
({ program }) => {
|
|
569
|
+
const bncr = program.command("bncr").description("Bncr channel utilities");
|
|
570
|
+
bncr.command("miniconfig").description(
|
|
571
|
+
"Seed minimal channels.bncr config (adds enabled=true and allowTool=false only when missing)"
|
|
572
|
+
).action(async () => {
|
|
573
|
+
const cfg = getOpenClawRuntimeConfig(api);
|
|
574
|
+
const channels = isPlainObject(cfg.channels) ? cfg.channels : {};
|
|
575
|
+
const existing = isPlainObject(channels.bncr) ? channels.bncr : {};
|
|
576
|
+
const added = [];
|
|
577
|
+
if (existing.enabled === void 0) {
|
|
578
|
+
added.push("enabled=true");
|
|
579
|
+
}
|
|
580
|
+
if (existing.allowTool === void 0) {
|
|
581
|
+
added.push("allowTool=false");
|
|
582
|
+
}
|
|
583
|
+
if (added.length === 0) {
|
|
584
|
+
console.log("Minimal bncr config already present. No changes made.");
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
await mutateOpenClawRuntimeConfigFile(api, {
|
|
588
|
+
afterWrite: { mode: "auto" },
|
|
589
|
+
mutate(draft) {
|
|
590
|
+
if (!isPlainObject(draft.channels)) draft.channels = {};
|
|
591
|
+
const draftChannels = draft.channels;
|
|
592
|
+
const draftExisting = isPlainObject(draftChannels.bncr) ? draftChannels.bncr : {};
|
|
593
|
+
const draftBncrCfg = { ...draftExisting };
|
|
594
|
+
if (draftBncrCfg.enabled === void 0) {
|
|
595
|
+
draftBncrCfg.enabled = true;
|
|
596
|
+
}
|
|
597
|
+
if (draftBncrCfg.allowTool === void 0) {
|
|
598
|
+
draftBncrCfg.allowTool = false;
|
|
599
|
+
}
|
|
600
|
+
draftChannels.bncr = draftBncrCfg;
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
console.log("Seeded minimal bncr config at channels.bncr.");
|
|
604
|
+
console.log(`Added missing fields: ${added.join(", ")}`);
|
|
605
|
+
console.log("Gateway will apply the config using the host afterWrite policy.");
|
|
606
|
+
});
|
|
607
|
+
},
|
|
608
|
+
{ commands: ["bncr"] }
|
|
609
|
+
);
|
|
610
|
+
};
|
|
611
|
+
var shouldSkipNonRuntimeRegister = (mode) => mode === "cli-metadata" || mode === "discovery";
|
|
612
|
+
var plugin = {
|
|
613
|
+
id: "bncr",
|
|
614
|
+
name: "Bncr",
|
|
615
|
+
description: "Bncr channel plugin",
|
|
616
|
+
configSchema: BncrConfigSchema,
|
|
617
|
+
register(api) {
|
|
618
|
+
registerBncrCli(api);
|
|
619
|
+
if (shouldSkipNonRuntimeRegister(api.registrationMode)) return;
|
|
620
|
+
const meta = getRegisterMeta(api);
|
|
621
|
+
meta.registrationMode = api.registrationMode;
|
|
622
|
+
const globalTrace = getGlobalRegisterTrace();
|
|
623
|
+
const previousApiInstanceId = globalTrace.lastApiInstanceId;
|
|
624
|
+
const previousRegistryFingerprint = globalTrace.lastRegistryFingerprint;
|
|
625
|
+
const apiInstanceId = meta.apiInstanceId || "unknown";
|
|
626
|
+
const registryFingerprint = meta.registryFingerprint || "unknown";
|
|
627
|
+
const sameApiAsPrevious = previousApiInstanceId === apiInstanceId;
|
|
628
|
+
const sameRegistryAsPrevious = previousRegistryFingerprint === registryFingerprint;
|
|
629
|
+
const firstSeenApi = !globalTrace.seenApiInstanceIds.has(apiInstanceId);
|
|
630
|
+
const firstSeenRegistry = !globalTrace.seenRegistryFingerprints.has(registryFingerprint);
|
|
631
|
+
const gatewayRuntime = getGatewayRuntime();
|
|
632
|
+
const ownerDecision = shouldAdoptProcessOwner(apiInstanceId, gatewayRuntime);
|
|
633
|
+
let bridge;
|
|
634
|
+
let runtime2;
|
|
635
|
+
let created = false;
|
|
636
|
+
let rebuilt = false;
|
|
637
|
+
let owner;
|
|
638
|
+
let previousOwner;
|
|
639
|
+
if (ownerDecision.adoptOwner) {
|
|
640
|
+
const adopted = getBridgeSingleton(api);
|
|
641
|
+
bridge = adopted.bridge;
|
|
642
|
+
runtime2 = adopted.runtime;
|
|
643
|
+
created = adopted.created;
|
|
644
|
+
rebuilt = adopted.rebuilt;
|
|
645
|
+
owner = adopted.owner;
|
|
646
|
+
previousOwner = adopted.previousOwner;
|
|
647
|
+
gatewayRuntime.currentBridge = bridge;
|
|
648
|
+
} else {
|
|
649
|
+
runtime2 = loadRuntimeSync();
|
|
650
|
+
bridge = gatewayRuntime.currentBridge || getExistingBridgeSingleton();
|
|
651
|
+
previousOwner = getExistingBridgeSingleton()?.[BNCR_BRIDGE_OWNER];
|
|
652
|
+
owner = previousOwner;
|
|
653
|
+
if (bridge && !gatewayRuntime.currentBridge) {
|
|
654
|
+
gatewayRuntime.currentBridge = bridge;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
globalTrace.seenApiInstanceIds.add(apiInstanceId);
|
|
658
|
+
globalTrace.seenRegistryFingerprints.add(registryFingerprint);
|
|
659
|
+
globalTrace.lastApiInstanceId = apiInstanceId;
|
|
660
|
+
globalTrace.lastRegistryFingerprint = registryFingerprint;
|
|
661
|
+
bridge?.noteRegister?.({
|
|
662
|
+
source: "~/.openclaw/workspace/plugins/bncr/index.ts",
|
|
663
|
+
pluginVersion,
|
|
664
|
+
apiRebound: ownerDecision.adoptOwner ? !created && !rebuilt : false,
|
|
665
|
+
apiInstanceId: meta.apiInstanceId,
|
|
666
|
+
registryFingerprint: meta.registryFingerprint
|
|
667
|
+
});
|
|
668
|
+
const debugLog = (...args) => {
|
|
669
|
+
const rendered = args.map((arg) => typeof arg === "string" ? arg : JSON.stringify(arg)).join(" ").trim();
|
|
670
|
+
if (!rendered) return;
|
|
671
|
+
emitBncrLogLine(
|
|
672
|
+
"info",
|
|
673
|
+
`[bncr] debug ${rendered}`,
|
|
674
|
+
{ debugOnly: true },
|
|
675
|
+
() => Boolean(bridge?.isDebugEnabled?.())
|
|
676
|
+
);
|
|
677
|
+
};
|
|
678
|
+
debugLog(
|
|
679
|
+
`register begin bridge=${bridge?.getBridgeId?.() || "unknown"} created=${created} rebuilt=${rebuilt} ownerApi=${owner?.apiInstanceId || "none"} ownerRegistry=${owner?.registryFingerprint || "none"} previousOwnerApi=${previousOwner?.apiInstanceId || "none"} previousOwnerRegistry=${previousOwner?.registryFingerprint || "none"}`
|
|
680
|
+
);
|
|
681
|
+
debugLog(
|
|
682
|
+
`register classify mode=${meta.registrationMode || "unknown"} api=${apiInstanceId} registry=${registryFingerprint} sameApiAsPrevious=${sameApiAsPrevious} sameRegistryAsPrevious=${sameRegistryAsPrevious} firstSeenApi=${firstSeenApi} firstSeenRegistry=${firstSeenRegistry}`
|
|
683
|
+
);
|
|
684
|
+
debugLog(
|
|
685
|
+
`register owner adopt=${ownerDecision.adoptOwner} reason=${ownerDecision.reason} existingOwnerApi=${ownerDecision.existingOwnerApiInstanceId || "none"}`
|
|
686
|
+
);
|
|
687
|
+
if (!ownerDecision.adoptOwner) {
|
|
688
|
+
debugLog(
|
|
689
|
+
`bridge rebuild suppressed due to existing singleton owner api ${ownerDecision.existingOwnerApiInstanceId || "unknown"}`
|
|
690
|
+
);
|
|
691
|
+
} else {
|
|
692
|
+
if (!created && !rebuilt) debugLog("bridge api rebound");
|
|
693
|
+
if (rebuilt) debugLog("bridge rebuilt due to owner/runtime change");
|
|
694
|
+
}
|
|
695
|
+
const resolveDebug = async () => {
|
|
696
|
+
try {
|
|
697
|
+
const cfg = getOpenClawRuntimeConfig(api);
|
|
698
|
+
return Boolean(cfg?.channels?.bncr?.debug?.verbose);
|
|
699
|
+
} catch {
|
|
700
|
+
return false;
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
if (!gatewayRuntime.serviceRegistered) {
|
|
704
|
+
const serviceStopHandler = async () => {
|
|
705
|
+
await getCurrentBridge().stopService?.();
|
|
706
|
+
};
|
|
707
|
+
api.registerService({
|
|
708
|
+
id: "bncr-bridge-service",
|
|
709
|
+
start: async (ctx) => {
|
|
710
|
+
const debug = await resolveDebug();
|
|
711
|
+
await getCurrentBridge().startService(ctx, debug);
|
|
712
|
+
},
|
|
713
|
+
stop: serviceStopHandler
|
|
714
|
+
});
|
|
715
|
+
activeServiceStop = serviceStopHandler;
|
|
716
|
+
gatewayRuntime.serviceRegistered = true;
|
|
717
|
+
gatewayRuntime.serviceOwnerApiInstanceId = apiInstanceId;
|
|
718
|
+
meta.service = true;
|
|
719
|
+
debugLog(`register service ok ownerApi=${apiInstanceId}`);
|
|
720
|
+
} else {
|
|
721
|
+
meta.service = true;
|
|
722
|
+
debugLog(
|
|
723
|
+
`register service skip (process singleton already registered by api ${gatewayRuntime.serviceOwnerApiInstanceId || "unknown"})`
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
if (!gatewayRuntime.channelRegistered) {
|
|
727
|
+
api.registerChannel({ plugin: createDynamicChannelPlugin(runtime2) });
|
|
728
|
+
gatewayRuntime.channelRegistered = true;
|
|
729
|
+
gatewayRuntime.channelOwnerApiInstanceId = apiInstanceId;
|
|
730
|
+
meta.channel = true;
|
|
731
|
+
debugLog(`register channel ok ownerApi=${apiInstanceId}`);
|
|
732
|
+
} else {
|
|
733
|
+
meta.channel = true;
|
|
734
|
+
debugLog(
|
|
735
|
+
`register channel skip (process singleton already registered by api ${gatewayRuntime.channelOwnerApiInstanceId || "unknown"})`
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
ensureGatewayMethodRegistered(api, "bncr.connect", debugLog);
|
|
739
|
+
ensureGatewayMethodRegistered(api, "bncr.inbound", debugLog);
|
|
740
|
+
ensureGatewayMethodRegistered(api, "bncr.activity", debugLog);
|
|
741
|
+
ensureGatewayMethodRegistered(api, "bncr.ack", debugLog);
|
|
742
|
+
ensureGatewayMethodRegistered(api, "bncr.diagnostics", debugLog);
|
|
743
|
+
ensureGatewayMethodRegistered(api, "bncr.file.init", debugLog);
|
|
744
|
+
ensureGatewayMethodRegistered(api, "bncr.file.chunk", debugLog);
|
|
745
|
+
ensureGatewayMethodRegistered(api, "bncr.file.complete", debugLog);
|
|
746
|
+
ensureGatewayMethodRegistered(api, "bncr.file.abort", debugLog);
|
|
747
|
+
ensureGatewayMethodRegistered(api, "bncr.file.ack", debugLog);
|
|
748
|
+
debugLog("register done");
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
var index_default = plugin;
|
|
752
|
+
export {
|
|
753
|
+
index_default as default
|
|
754
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmoxmo/bncr",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,9 +21,11 @@
|
|
|
21
21
|
"README.md",
|
|
22
22
|
"LICENSE",
|
|
23
23
|
"src/**/*.ts",
|
|
24
|
-
"scripts/**/*.mjs"
|
|
24
|
+
"scripts/**/*.mjs",
|
|
25
|
+
"dist/**/*.js"
|
|
25
26
|
],
|
|
26
27
|
"scripts": {
|
|
28
|
+
"build": "npx -y esbuild@0.28.0 index.ts --bundle --platform=node --format=esm --target=node22 --packages=external --outfile=dist/index.js",
|
|
27
29
|
"selfcheck": "node ./scripts/selfcheck.mjs",
|
|
28
30
|
"test": "node --import ./tests/register-ts-hooks.mjs --test ./tests/*.test.mjs",
|
|
29
31
|
"check-register-drift": "node ./scripts/check-register-drift.mjs",
|
|
@@ -38,7 +40,8 @@
|
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
40
42
|
"@biomejs/biome": "^1.9.4",
|
|
41
|
-
"openclaw": ">=2026.5.27"
|
|
43
|
+
"openclaw": ">=2026.5.27",
|
|
44
|
+
"esbuild": "^0.28.0"
|
|
42
45
|
},
|
|
43
46
|
"openclaw": {
|
|
44
47
|
"extensions": [
|
|
@@ -51,6 +54,9 @@
|
|
|
51
54
|
"build": {
|
|
52
55
|
"openclawVersion": "2026.5.28",
|
|
53
56
|
"pluginSdkVersion": "2026.5.28"
|
|
54
|
-
}
|
|
57
|
+
},
|
|
58
|
+
"runtimeExtensions": [
|
|
59
|
+
"./dist/index.js"
|
|
60
|
+
]
|
|
55
61
|
}
|
|
56
62
|
}
|