@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.
- package/dist/src/liveware-sample.js +23 -4
- package/dist/src/runtime.js +4 -1
- package/package.json +1 -1
- package/src/liveware-sample.ts +30 -4
- package/src/runtime.ts +4 -1
|
@@ -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
|
-
|
|
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?.
|
|
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?.
|
|
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.
|
package/dist/src/runtime.js
CHANGED
|
@@ -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
|
-
|
|
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
package/src/liveware-sample.ts
CHANGED
|
@@ -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
|
-
|
|
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?.
|
|
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?.
|
|
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
|
-
|
|
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();
|