@vama/openclaw 2026.5.5-7 → 2026.5.5-9

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.
@@ -3,7 +3,7 @@ import { i as dispatchStarted, r as dispatchEnded } from "./monitor-BlnFXk24.js"
3
3
  //#region extensions/vama/src/cli-metadata.ts
4
4
  function registerVamaCliMetadata(api) {
5
5
  api.registerCli(async ({ program }) => {
6
- const { registerVamaCli } = await import("./cli-uxMxzdnO.js");
6
+ const { registerVamaCli } = await import("./cli-DY7JS2Ny.js");
7
7
  registerVamaCli({ program });
8
8
  }, { descriptors: [{
9
9
  name: "vama",
package/dist/api.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as registerVamaCliMetadata, t as registerVamaSubagentKeepaliveHooks } from "./api-CBt1Mjce.js";
1
+ import { n as registerVamaCliMetadata, t as registerVamaSubagentKeepaliveHooks } from "./api-jjxQl66-.js";
2
2
  import { n as probeVama } from "./probe-COpuChEq.js";
3
3
  import { n as sendMessageVama, t as monitorVamaProvider } from "./monitor-BlnFXk24.js";
4
4
  export { monitorVamaProvider, probeVama, registerVamaCliMetadata, registerVamaSubagentKeepaliveHooks, sendMessageVama };
@@ -4,8 +4,10 @@ import { t as getVamaRuntime } from "./runtime-CQ9kld4S.js";
4
4
  import { t as parseVamaConnectCode } from "./connect-code-Bbp9J6TH.js";
5
5
  import fs from "node:fs";
6
6
  import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
7
- import { spawn } from "node:child_process";
7
+ import { execFile, spawn } from "node:child_process";
8
+ import { promisify } from "node:util";
8
9
  //#region extensions/vama/src/connect-verify.ts
10
+ const execFileAsync = promisify(execFile);
9
11
  /**
10
12
  * Post-connect verification: make `openclaw vama connect` end with a TRUE
11
13
  * statement about liveness instead of "credentials look right, good luck".
@@ -134,6 +136,86 @@ async function ensureGatewayRunning(params, deps = {}) {
134
136
  detail: (install.output || start.output).split("\n").slice(-4).join("\n").trim() || void 0
135
137
  };
136
138
  }
139
+ /** lsof-based lookup of whoever LISTENs on the gateway port (POSIX only). */
140
+ async function findGatewayPortOwner(port) {
141
+ try {
142
+ const { stdout } = await execFileAsync("lsof", [
143
+ "-ti",
144
+ `tcp:${port}`,
145
+ "-sTCP:LISTEN"
146
+ ], { timeout: 5e3 });
147
+ const pid = Number.parseInt(stdout.trim().split("\n")[0] ?? "", 10);
148
+ if (!Number.isInteger(pid) || pid <= 0) return null;
149
+ const { stdout: comm } = await execFileAsync("ps", [
150
+ "-p",
151
+ String(pid),
152
+ "-o",
153
+ "comm="
154
+ ], { timeout: 5e3 });
155
+ return {
156
+ pid,
157
+ command: comm.trim()
158
+ };
159
+ } catch {
160
+ return null;
161
+ }
162
+ }
163
+ const OPENCLAW_PROCESS_RE = /openclaw|node|bun/i;
164
+ /**
165
+ * Kill a wedged gateway process squatting the port. Real-world case (seen on
166
+ * a user machine): a days-old `openclaw gateway run` whose HTTP side still
167
+ * answers /health but whose WS stack hangs up every connection — config
168
+ * reloads can't reach it, and it never dials BotHub. Killing it is the only
169
+ * way to one-step that machine. Refuses to touch processes that don't look
170
+ * like an OpenClaw/Node gateway.
171
+ */
172
+ async function killStaleGateway(port, deps = {}) {
173
+ const findOwner = deps.findOwner ?? findGatewayPortOwner;
174
+ const probe = deps.probe ?? isGatewayUp;
175
+ const sleep = deps.sleep ?? defaultSleep;
176
+ const kill = deps.kill ?? ((pid, signal) => process.kill(pid, signal));
177
+ const owner = await findOwner(port);
178
+ if (!owner) return {
179
+ killed: false,
180
+ reason: `could not identify the process on port ${port}`
181
+ };
182
+ if (!OPENCLAW_PROCESS_RE.test(owner.command)) return {
183
+ killed: false,
184
+ pid: owner.pid,
185
+ reason: `port ${port} is held by "${owner.command}" (pid ${owner.pid}), which does not look like an OpenClaw gateway — not touching it`
186
+ };
187
+ try {
188
+ kill(owner.pid, "SIGTERM");
189
+ } catch (err) {
190
+ return {
191
+ killed: false,
192
+ pid: owner.pid,
193
+ reason: err instanceof Error ? err.message : String(err)
194
+ };
195
+ }
196
+ for (let i = 0; i < 8; i += 1) {
197
+ await sleep(500);
198
+ if (!await probe(port)) return {
199
+ killed: true,
200
+ pid: owner.pid
201
+ };
202
+ }
203
+ try {
204
+ kill(owner.pid, "SIGKILL");
205
+ } catch {}
206
+ for (let i = 0; i < 4; i += 1) {
207
+ await sleep(500);
208
+ if (!await probe(port)) return {
209
+ killed: true,
210
+ pid: owner.pid
211
+ };
212
+ }
213
+ return {
214
+ killed: false,
215
+ pid: owner.pid,
216
+ reason: `process ${owner.pid} survived SIGTERM+SIGKILL or the port did not free up`
217
+ };
218
+ }
137
219
  /**
138
220
  * Poll GET /v1/bot/me until `ws_connected` is true. "unsupported" means the
139
221
  * server doesn't report the field yet (BotHub older than this feature) —
@@ -177,7 +259,15 @@ async function waitForWSConnected(params, deps = {}) {
177
259
  * (dmPolicy, webhookPort, …) are preserved.
178
260
  */
179
261
  function applyVamaConnectCredentials(params) {
180
- const { cfg, accountId } = params;
262
+ const { accountId } = params;
263
+ const gateway = params.cfg.gateway;
264
+ const cfg = gateway && typeof gateway === "object" && gateway.mode !== void 0 ? params.cfg : {
265
+ ...params.cfg,
266
+ gateway: {
267
+ ...gateway,
268
+ mode: "local"
269
+ }
270
+ };
181
271
  const vama = cfg.channels?.vama ?? {};
182
272
  const credentialPatch = {
183
273
  enabled: true,
@@ -233,6 +323,7 @@ async function runVamaConnect(params, deps = {}) {
233
323
  const probe = deps.probe ?? probeVama;
234
324
  const ensureGateway = deps.ensureGateway ?? ensureGatewayRunning;
235
325
  const waitForLive = deps.waitForLive ?? waitForWSConnected;
326
+ const killStale = deps.killStale ?? killStaleGateway;
236
327
  const log = deps.log ?? ((msg) => console.log(msg));
237
328
  const parsed = parseVamaConnectCode(params.rawCode);
238
329
  const runtime = getVamaRuntime();
@@ -282,8 +373,9 @@ async function runVamaConnect(params, deps = {}) {
282
373
  result.liveCheck = "skipped";
283
374
  return result;
284
375
  }
376
+ const port = resolveGatewayPort(updated);
285
377
  const gateway = await ensureGateway({
286
- port: resolveGatewayPort(updated),
378
+ port,
287
379
  log
288
380
  });
289
381
  result.gatewayAction = gateway.action;
@@ -298,10 +390,30 @@ async function runVamaConnect(params, deps = {}) {
298
390
  return result;
299
391
  }
300
392
  log("Waiting for your agent to come online…");
301
- const live = await waitForLive({
393
+ let live = await waitForLive({
302
394
  bothubUrl,
303
395
  botToken: exchanged.bot_token
304
396
  });
397
+ if (live.status !== "live" && live.status !== "unsupported" && gateway.action === "already-running") {
398
+ log("A gateway is running but never connected to Vama — replacing it…");
399
+ const killed = await killStale(port);
400
+ if (killed.killed) {
401
+ result.staleGatewayKilled = true;
402
+ const retry = await ensureGateway({
403
+ port,
404
+ log
405
+ });
406
+ result.gatewayAction = retry.action;
407
+ if (retry.detail) result.gatewayDetail = retry.detail;
408
+ if (retry.up) {
409
+ log("Waiting for your agent to come online…");
410
+ live = await waitForLive({
411
+ bothubUrl,
412
+ botToken: exchanged.bot_token
413
+ });
414
+ }
415
+ } else if (killed.reason) result.gatewayDetail = killed.reason;
416
+ }
305
417
  result.liveCheck = live.status;
306
418
  result.live = live.status === "live";
307
419
  if (live.error) result.liveError = live.error;
@@ -321,6 +433,7 @@ function printConnectResult(result) {
321
433
  if (result.websocketEnabled) console.log(" Transport: WebSocket — no tunnel or public URL needed.");
322
434
  else console.log(" Transport: webhook — set channels.vama.webhookUrl to a public URL (or mint a new code from Vama settings to enable WebSocket).");
323
435
  if (result.gatewayAction) console.log(` Gateway: ${GATEWAY_ACTION_LABEL[result.gatewayAction]}`);
436
+ if (result.staleGatewayKilled) console.log(" Replaced a stale gateway process that was holding the port.");
324
437
  if (result.probeOk === false && result.probeError) console.log(` Verification warning: ${result.probeError}`);
325
438
  console.log("");
326
439
  if (result.live) {
@@ -347,6 +460,7 @@ function printConnectResult(result) {
347
460
  default:
348
461
  console.log("⚠ NOT live yet — gateway is up but BotHub hasn't seen its connection.");
349
462
  if (result.liveError) console.log(` Last error: ${result.liveError}`);
463
+ if (result.gatewayDetail) console.log(` ${result.gatewayDetail}`);
350
464
  console.log(" It may just be slow. Check again with: openclaw channels status --probe");
351
465
  console.log(" Gateway logs: openclaw gateway status");
352
466
  return;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as registerVamaCliMetadata, t as registerVamaSubagentKeepaliveHooks } from "./api-CBt1Mjce.js";
1
+ import { n as registerVamaCliMetadata, t as registerVamaSubagentKeepaliveHooks } from "./api-jjxQl66-.js";
2
2
  import { n as probeVama } from "./probe-COpuChEq.js";
3
3
  import { n as sendMessageVama, t as monitorVamaProvider } from "./monitor-BlnFXk24.js";
4
4
  import { n as setVamaRuntime } from "./runtime-CQ9kld4S.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vama/openclaw",
3
- "version": "2026.5.5-7",
3
+ "version": "2026.5.5-9",
4
4
  "description": "OpenClaw Vama channel plugin via BotHub",
5
5
  "homepage": "https://web.vama.com/connect-guide",
6
6
  "repository": {