@clawling/clawchat-plugin-openclaw 2026.9.9-1 → 2026.9.10-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.
@@ -378,6 +378,28 @@ export async function startTunnelAgent(opts) {
378
378
  await waitForOutput(child, (acc) => parseAgentReady(acc), opts.timeoutMs ?? TUNNEL_START_TIMEOUT_MS, "liveware agent start");
379
379
  return { child };
380
380
  }
381
+ /** Where the liveware CLI keeps its login state, relative to its `HOME`. */
382
+ const LIVEWARE_CREDENTIAL_DIRNAME = ".clawling";
383
+ /**
384
+ * True when the liveware CLI has usable cached credentials under `cliHome`.
385
+ *
386
+ * Tolerates either shape the CLI might use: a credential file (non-empty) or a
387
+ * directory (non-empty). Anything unreadable answers "no" — the caller's only
388
+ * use is deciding whether it is safe to skip a login, and guessing "yes" there
389
+ * is exactly the failure this check exists to prevent.
390
+ */
391
+ export async function hasCachedLivewareCredentials(cliHome) {
392
+ const target = path.join(cliHome, LIVEWARE_CREDENTIAL_DIRNAME);
393
+ try {
394
+ const stat = await fs.promises.stat(target);
395
+ if (stat.isDirectory())
396
+ return (await fs.promises.readdir(target)).length > 0;
397
+ return stat.size > 0;
398
+ }
399
+ catch {
400
+ return false;
401
+ }
402
+ }
381
403
  export async function livewareLogin(opts) {
382
404
  const execFileFn = opts.execFileFn ?? nodeExecFile;
383
405
  await new Promise((resolve, reject) => {
@@ -833,12 +855,14 @@ export class LivewareSampleSupervisor {
833
855
  // lifetime. `login` is idempotent; best-effort, so a stale token still falls
834
856
  // through to whatever credentials the CLI already has.
835
857
  const token = deps.resolveToken();
858
+ let loggedIn = false;
836
859
  if (token) {
837
860
  try {
838
861
  await livewareLogin({ livewarePath, token, execFileFn: deps.execFileFn, env: cliEnv });
862
+ loggedIn = true;
839
863
  }
840
864
  catch (err) {
841
- deps.log?.warn?.(`liveware-sample: relaunch re-login failed; continuing with cached CLI credentials: ${String(err)}`);
865
+ deps.log?.warn?.(`liveware-sample: relaunch re-login failed; continuing only if the CLI has cached credentials: ${String(err)}`);
842
866
  }
843
867
  if (this.bailIfStopped())
844
868
  return;
@@ -846,6 +870,20 @@ export class LivewareSampleSupervisor {
846
870
  else {
847
871
  deps.log?.debug?.("liveware-sample: no token; relaunch without re-login");
848
872
  }
873
+ // Falling through without a fresh login is only safe when the CLI actually
874
+ // has credentials cached. Since the private-HOME redirect those live in
875
+ // `<cliHome>/.clawling`, and a first relaunch after upgrading finds that
876
+ // home EMPTY — the old ones are still under the ambient `$HOME`. Continuing
877
+ // there makes every tunnelBind/agent call below fail auth, which surfaces as
878
+ // a confusing tunnel error rather than the credential problem it is, and
879
+ // burns the whole retry ladder on a state that cannot recover on its own.
880
+ // Throwing hands it to `startAttempt`'s ladder instead, so the worst case is
881
+ // a delayed sample rather than a silent auth failure.
882
+ if (!loggedIn && cliEnv && deps.cliHome) {
883
+ if (!(await hasCachedLivewareCredentials(deps.cliHome))) {
884
+ throw new Error(`liveware CLI has no cached credentials under ${deps.cliHome} and no token was available to log in`);
885
+ }
886
+ }
849
887
  // Kept after the re-login above so the lookup runs against a CLI that is
850
888
  // actually authenticated: a pending row's app id came straight from
851
889
  // `app create`'s parser and was never confirmed by the liveware side, so
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawling/clawchat-plugin-openclaw",
3
- "version": "2026.9.9-1",
3
+ "version": "2026.9.10-1",
4
4
  "description": "OpenClaw ClawChat channel plugin",
5
5
  "license": "MIT",
6
6
  "author": "CLAWLING PTE. LTD.",
@@ -467,6 +467,28 @@ export async function startTunnelAgent(opts: {
467
467
  return { child };
468
468
  }
469
469
 
470
+ /** Where the liveware CLI keeps its login state, relative to its `HOME`. */
471
+ const LIVEWARE_CREDENTIAL_DIRNAME = ".clawling";
472
+
473
+ /**
474
+ * True when the liveware CLI has usable cached credentials under `cliHome`.
475
+ *
476
+ * Tolerates either shape the CLI might use: a credential file (non-empty) or a
477
+ * directory (non-empty). Anything unreadable answers "no" — the caller's only
478
+ * use is deciding whether it is safe to skip a login, and guessing "yes" there
479
+ * is exactly the failure this check exists to prevent.
480
+ */
481
+ export async function hasCachedLivewareCredentials(cliHome: string): Promise<boolean> {
482
+ const target = path.join(cliHome, LIVEWARE_CREDENTIAL_DIRNAME);
483
+ try {
484
+ const stat = await fs.promises.stat(target);
485
+ if (stat.isDirectory()) return (await fs.promises.readdir(target)).length > 0;
486
+ return stat.size > 0;
487
+ } catch {
488
+ return false;
489
+ }
490
+ }
491
+
470
492
  export async function livewareLogin(opts: {
471
493
  livewarePath: string;
472
494
  token: string;
@@ -980,18 +1002,36 @@ export class LivewareSampleSupervisor {
980
1002
  // lifetime. `login` is idempotent; best-effort, so a stale token still falls
981
1003
  // through to whatever credentials the CLI already has.
982
1004
  const token = deps.resolveToken();
1005
+ let loggedIn = false;
983
1006
  if (token) {
984
1007
  try {
985
1008
  await livewareLogin({ livewarePath, token, execFileFn: deps.execFileFn, env: cliEnv });
1009
+ loggedIn = true;
986
1010
  } catch (err) {
987
1011
  deps.log?.warn?.(
988
- `liveware-sample: relaunch re-login failed; continuing with cached CLI credentials: ${String(err)}`,
1012
+ `liveware-sample: relaunch re-login failed; continuing only if the CLI has cached credentials: ${String(err)}`,
989
1013
  );
990
1014
  }
991
1015
  if (this.bailIfStopped()) return;
992
1016
  } else {
993
1017
  deps.log?.debug?.("liveware-sample: no token; relaunch without re-login");
994
1018
  }
1019
+ // Falling through without a fresh login is only safe when the CLI actually
1020
+ // has credentials cached. Since the private-HOME redirect those live in
1021
+ // `<cliHome>/.clawling`, and a first relaunch after upgrading finds that
1022
+ // home EMPTY — the old ones are still under the ambient `$HOME`. Continuing
1023
+ // there makes every tunnelBind/agent call below fail auth, which surfaces as
1024
+ // a confusing tunnel error rather than the credential problem it is, and
1025
+ // burns the whole retry ladder on a state that cannot recover on its own.
1026
+ // Throwing hands it to `startAttempt`'s ladder instead, so the worst case is
1027
+ // a delayed sample rather than a silent auth failure.
1028
+ if (!loggedIn && cliEnv && deps.cliHome) {
1029
+ if (!(await hasCachedLivewareCredentials(deps.cliHome))) {
1030
+ throw new Error(
1031
+ `liveware CLI has no cached credentials under ${deps.cliHome} and no token was available to log in`,
1032
+ );
1033
+ }
1034
+ }
995
1035
  // Kept after the re-login above so the lookup runs against a CLI that is
996
1036
  // actually authenticated: a pending row's app id came straight from
997
1037
  // `app create`'s parser and was never confirmed by the liveware side, so