@clawling/clawchat-plugin-openclaw 2026.7.6-4 → 2026.7.8-2

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.
@@ -258,6 +258,9 @@ const RESTART_WINDOW_MS = 30 * 60 * 1000;
258
258
  const MAX_RESTARTS_PER_WINDOW = 5;
259
259
  const INTRO_RETRY_DELAY_MS = 30_000;
260
260
  const INTRO_MAX_TRIES = 20;
261
+ // Backoff for transient start() failures (template CDN 429s, network blips):
262
+ // a one-shot bootstrap would otherwise stay dormant until the next restart.
263
+ const START_RETRY_DELAYS_MS = [30_000, 60_000, 120_000, 300_000];
261
264
  export class LivewareSampleSupervisor {
262
265
  deps;
263
266
  serverChild = null;
@@ -272,12 +275,22 @@ export class LivewareSampleSupervisor {
272
275
  constructor(deps) {
273
276
  this.deps = deps;
274
277
  }
275
- /** Entry point. Never throws; every failure is logged + persisted only. */
278
+ /** Entry point. Never throws; every failure is logged + persisted only.
279
+ * Transient failures are retried on the START_RETRY_DELAYS_MS backoff. */
276
280
  async start() {
281
+ await this.startAttempt(0);
282
+ }
283
+ async startAttempt(attempt) {
277
284
  const { deps } = this;
278
285
  try {
279
286
  if (!deps.enabled)
280
287
  return;
288
+ // ensureLivewareCli downloads the CLI asynchronously on first boot;
289
+ // gate the whole flow on it or resolveLivewarePath() below races to null.
290
+ if (deps.cliReady)
291
+ await deps.cliReady;
292
+ if (this.stopped)
293
+ return;
281
294
  const key = { platform: deps.platform, accountId: deps.accountId };
282
295
  const row = deps.store.getLivewareSample(key);
283
296
  if (row?.status === "disabled")
@@ -293,7 +306,13 @@ export class LivewareSampleSupervisor {
293
306
  // A mid-flow failure (e.g. registerApp rejected) may have left children
294
307
  // running without a persisted row — never leak orphan processes.
295
308
  this.killChildren();
296
- deps.log?.warn?.(`clawchat-plugin-openclaw liveware-sample start failed: ${String(err)}`);
309
+ const delayMs = START_RETRY_DELAYS_MS[attempt];
310
+ if (delayMs === undefined || this.stopped) {
311
+ deps.log?.warn?.(`clawchat-plugin-openclaw liveware-sample start failed (attempt ${attempt + 1}, giving up): ${String(err)}`);
312
+ return;
313
+ }
314
+ deps.log?.warn?.(`clawchat-plugin-openclaw liveware-sample start failed (attempt ${attempt + 1}, retrying in ${delayMs / 1000}s): ${String(err)}`);
315
+ this.schedule(() => { void this.startAttempt(attempt + 1); }, delayMs);
297
316
  }
298
317
  }
299
318
  stop() {
@@ -336,7 +355,7 @@ export class LivewareSampleSupervisor {
336
355
  return;
337
356
  const livewarePath = deps.resolveLivewarePath();
338
357
  if (!livewarePath) {
339
- deps.log?.debug?.("liveware-sample: liveware CLI not ready; skip");
358
+ deps.log?.warn?.("liveware-sample: liveware CLI not ready; skip");
340
359
  return;
341
360
  }
342
361
  const token = deps.resolveToken();
@@ -395,7 +414,7 @@ export class LivewareSampleSupervisor {
395
414
  return;
396
415
  const livewarePath = deps.resolveLivewarePath();
397
416
  if (!livewarePath) {
398
- deps.log?.debug?.("liveware-sample: liveware CLI not ready; skip relaunch");
417
+ deps.log?.warn?.("liveware-sample: liveware CLI not ready; skip relaunch");
399
418
  return;
400
419
  }
401
420
  // The user deleting the app tile is an explicit opt-out — never reinstall.
@@ -668,7 +668,9 @@ export async function startOpenclawClawlingGateway(params) {
668
668
  log,
669
669
  });
670
670
  // Ensure the liveware CLI is installed (async, never blocks startup).
671
- void ensureLivewareCli({
671
+ // Keep the promise: the liveware-sample supervisor must await it, or a
672
+ // first boot races the download, sees no CLI, and silently skips forever.
673
+ const livewareCliReady = ensureLivewareCli({
672
674
  stateDir: runtime.state?.resolveStateDir?.(),
673
675
  log: {
674
676
  warn: (m) => log?.info?.(m),
@@ -800,6 +802,7 @@ export async function startOpenclawClawlingGateway(params) {
800
802
  sampleRoot: path.join(stateDirForSample ?? "", "clawchat", "liveware-sample"),
801
803
  resolveToken: () => account.token ?? "",
802
804
  resolveLivewarePath: () => resolveLivewarePath(stateDirForSample),
805
+ cliReady: livewareCliReady,
803
806
  listApps: async () => {
804
807
  const client = getConversationApiClient();
805
808
  return await client.listApps();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawling/clawchat-plugin-openclaw",
3
- "version": "2026.7.6-4",
3
+ "version": "2026.7.8-2",
4
4
  "description": "OpenClaw ClawChat channel plugin",
5
5
  "license": "MIT",
6
6
  "author": "CLAWLING PTE. LTD.",
@@ -349,6 +349,10 @@ export type LivewareSampleSupervisorDeps = {
349
349
  sampleRoot: string;
350
350
  resolveToken: () => string;
351
351
  resolveLivewarePath: () => string | null;
352
+ /** Settles when the liveware CLI self-download has finished (either way).
353
+ * start() awaits it before the CLI gate so a first boot doesn't lose the
354
+ * race against ensureLivewareCli and silently skip forever. */
355
+ cliReady?: Promise<void>;
352
356
  listApps: () => Promise<{ apps: { app_id?: string }[] }>;
353
357
  registerApp: (p: { name: string; appId: string; url: string }) => Promise<unknown>;
354
358
  notifyOwner: (text: string) => Promise<boolean>;
@@ -370,6 +374,9 @@ const RESTART_WINDOW_MS = 30 * 60 * 1000;
370
374
  const MAX_RESTARTS_PER_WINDOW = 5;
371
375
  const INTRO_RETRY_DELAY_MS = 30_000;
372
376
  const INTRO_MAX_TRIES = 20;
377
+ // Backoff for transient start() failures (template CDN 429s, network blips):
378
+ // a one-shot bootstrap would otherwise stay dormant until the next restart.
379
+ const START_RETRY_DELAYS_MS = [30_000, 60_000, 120_000, 300_000];
373
380
 
374
381
  export class LivewareSampleSupervisor {
375
382
  private readonly deps: LivewareSampleSupervisorDeps;
@@ -387,11 +394,20 @@ export class LivewareSampleSupervisor {
387
394
  this.deps = deps;
388
395
  }
389
396
 
390
- /** Entry point. Never throws; every failure is logged + persisted only. */
397
+ /** Entry point. Never throws; every failure is logged + persisted only.
398
+ * Transient failures are retried on the START_RETRY_DELAYS_MS backoff. */
391
399
  async start(): Promise<void> {
400
+ await this.startAttempt(0);
401
+ }
402
+
403
+ private async startAttempt(attempt: number): Promise<void> {
392
404
  const { deps } = this;
393
405
  try {
394
406
  if (!deps.enabled) return;
407
+ // ensureLivewareCli downloads the CLI asynchronously on first boot;
408
+ // gate the whole flow on it or resolveLivewarePath() below races to null.
409
+ if (deps.cliReady) await deps.cliReady;
410
+ if (this.stopped) return;
395
411
  const key = { platform: deps.platform, accountId: deps.accountId };
396
412
  const row = deps.store.getLivewareSample(key);
397
413
  if (row?.status === "disabled") return;
@@ -404,7 +420,17 @@ export class LivewareSampleSupervisor {
404
420
  // A mid-flow failure (e.g. registerApp rejected) may have left children
405
421
  // running without a persisted row — never leak orphan processes.
406
422
  this.killChildren();
407
- deps.log?.warn?.(`clawchat-plugin-openclaw liveware-sample start failed: ${String(err)}`);
423
+ const delayMs = START_RETRY_DELAYS_MS[attempt];
424
+ if (delayMs === undefined || this.stopped) {
425
+ deps.log?.warn?.(
426
+ `clawchat-plugin-openclaw liveware-sample start failed (attempt ${attempt + 1}, giving up): ${String(err)}`,
427
+ );
428
+ return;
429
+ }
430
+ deps.log?.warn?.(
431
+ `clawchat-plugin-openclaw liveware-sample start failed (attempt ${attempt + 1}, retrying in ${delayMs / 1000}s): ${String(err)}`,
432
+ );
433
+ this.schedule(() => { void this.startAttempt(attempt + 1); }, delayMs);
408
434
  }
409
435
  }
410
436
 
@@ -444,7 +470,7 @@ export class LivewareSampleSupervisor {
444
470
  const { deps } = this;
445
471
  if (this.stopped) return;
446
472
  const livewarePath = deps.resolveLivewarePath();
447
- if (!livewarePath) { deps.log?.debug?.("liveware-sample: liveware CLI not ready; skip"); return; }
473
+ if (!livewarePath) { deps.log?.warn?.("liveware-sample: liveware CLI not ready; skip"); return; }
448
474
  const token = deps.resolveToken();
449
475
  if (!token) { deps.log?.debug?.("liveware-sample: no token; skip"); return; }
450
476
  let apps: { apps: { app_id?: string }[] };
@@ -493,7 +519,7 @@ export class LivewareSampleSupervisor {
493
519
  const { deps } = this;
494
520
  if (this.stopped) return;
495
521
  const livewarePath = deps.resolveLivewarePath();
496
- if (!livewarePath) { deps.log?.debug?.("liveware-sample: liveware CLI not ready; skip relaunch"); return; }
522
+ if (!livewarePath) { deps.log?.warn?.("liveware-sample: liveware CLI not ready; skip relaunch"); return; }
497
523
  // The user deleting the app tile is an explicit opt-out — never reinstall.
498
524
  try {
499
525
  const apps = await deps.listApps();
package/src/runtime.ts CHANGED
@@ -981,7 +981,9 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
981
981
  log,
982
982
  });
983
983
  // Ensure the liveware CLI is installed (async, never blocks startup).
984
- void ensureLivewareCli({
984
+ // Keep the promise: the liveware-sample supervisor must await it, or a
985
+ // first boot races the download, sees no CLI, and silently skips forever.
986
+ const livewareCliReady = ensureLivewareCli({
985
987
  stateDir: runtime.state?.resolveStateDir?.(),
986
988
  log: {
987
989
  warn: (m) => log?.info?.(m),
@@ -1118,6 +1120,7 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
1118
1120
  sampleRoot: path.join(stateDirForSample ?? "", "clawchat", "liveware-sample"),
1119
1121
  resolveToken: () => account.token ?? "",
1120
1122
  resolveLivewarePath: () => resolveLivewarePath(stateDirForSample),
1123
+ cliReady: livewareCliReady,
1121
1124
  listApps: async () => {
1122
1125
  const client = getConversationApiClient();
1123
1126
  return await client.listApps();