@nextclaw/remote 0.1.34 → 0.1.36

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.d.ts CHANGED
@@ -199,12 +199,20 @@ declare class RemoteServiceModule {
199
199
  private readonly deps;
200
200
  private abortController;
201
201
  private runTask;
202
+ private releaseOwnership;
202
203
  constructor(deps: {
203
204
  loadConfig: () => Config;
204
205
  uiEnabled: boolean;
205
206
  localOrigin: string;
206
207
  statusStore: RemoteStatusWriter;
207
208
  createConnector: (logger: RemoteLogger) => RemoteConnector;
209
+ claimOwnership?: () => {
210
+ ok: true;
211
+ release: () => void;
212
+ } | {
213
+ ok: false;
214
+ error: string;
215
+ };
208
216
  logger?: RemoteLogger;
209
217
  });
210
218
  start(): Promise<void> | null;
package/dist/index.js CHANGED
@@ -714,6 +714,9 @@ var TERMINAL_REMOTE_ERROR_PATTERNS = [
714
714
  /token expired/i,
715
715
  /token is invalid/i,
716
716
  /run "nextclaw login"/i,
717
+ /replaced by a newer connector session/i,
718
+ /already owned by (?:running )?nextclaw service/i,
719
+ /already owned by local nextclaw process/i,
717
720
  /unexpected server response:\s*400/i,
718
721
  /unexpected server response:\s*401/i,
719
722
  /unexpected server response:\s*403/i,
@@ -725,6 +728,26 @@ function isTerminalRemoteConnectorError(error) {
725
728
  const message = error instanceof Error ? error.message : String(error);
726
729
  return TERMINAL_REMOTE_ERROR_PATTERNS.some((pattern) => pattern.test(message));
727
730
  }
731
+ function describeUnexpectedRemoteConnectorClose(event) {
732
+ const code = typeof event.code === "number" && Number.isFinite(event.code) ? event.code : null;
733
+ const reason = typeof event.reason === "string" ? event.reason.trim() : "";
734
+ const wasClean = typeof event.wasClean === "boolean" ? event.wasClean : null;
735
+ if ((code === null || code === 1e3) && !reason) {
736
+ return null;
737
+ }
738
+ const detailParts = [];
739
+ if (code !== null) {
740
+ detailParts.push(`code ${code}`);
741
+ }
742
+ if (wasClean !== null) {
743
+ detailParts.push(wasClean ? "clean" : "unclean");
744
+ }
745
+ const detail = detailParts.length > 0 ? ` (${detailParts.join(", ")})` : "";
746
+ if (reason) {
747
+ return `Remote connector websocket closed${detail}: ${reason}`;
748
+ }
749
+ return `Remote connector websocket closed${detail}.`;
750
+ }
728
751
 
729
752
  // src/remote-connector-retry.utils.ts
730
753
  var BASE_RECONNECT_DELAY_MS = 3e3;
@@ -852,8 +875,13 @@ var RemoteConnector = class {
852
875
  socket
853
876
  });
854
877
  });
855
- socket.addEventListener("close", () => {
878
+ socket.addEventListener("close", (event) => {
856
879
  appAdapter.stop();
880
+ const closeMessage = describeUnexpectedRemoteConnectorClose(event ?? {});
881
+ if (!aborted && closeMessage) {
882
+ finishReject(new Error(closeMessage));
883
+ return;
884
+ }
857
885
  finishResolve(aborted ? "aborted" : "closed");
858
886
  });
859
887
  socket.addEventListener("error", (event) => {
@@ -1109,6 +1137,7 @@ var RemoteServiceModule = class {
1109
1137
  }
1110
1138
  abortController = null;
1111
1139
  runTask = null;
1140
+ releaseOwnership = null;
1112
1141
  start() {
1113
1142
  if (this.runTask) {
1114
1143
  return this.runTask;
@@ -1135,6 +1164,22 @@ var RemoteServiceModule = class {
1135
1164
  warn: (message) => console.warn(`[remote] ${message}`),
1136
1165
  error: (message) => console.error(`[remote] ${message}`)
1137
1166
  };
1167
+ const ownership = this.deps.claimOwnership?.();
1168
+ if (ownership && !ownership.ok) {
1169
+ this.deps.statusStore.write({
1170
+ enabled: true,
1171
+ state: "error",
1172
+ deviceName: config.remote.deviceName || void 0,
1173
+ deviceId: void 0,
1174
+ platformBase: config.remote.platformApiBase || void 0,
1175
+ localOrigin: this.deps.localOrigin,
1176
+ lastError: ownership.error,
1177
+ lastConnectedAt: null
1178
+ });
1179
+ logger.error(ownership.error);
1180
+ return null;
1181
+ }
1182
+ this.releaseOwnership = ownership?.release ?? null;
1138
1183
  this.abortController = new AbortController();
1139
1184
  const connector = this.deps.createConnector(logger);
1140
1185
  this.runTask = connector.run({
@@ -1157,6 +1202,9 @@ var RemoteServiceModule = class {
1157
1202
  lastError: message
1158
1203
  });
1159
1204
  logger.error(message);
1205
+ }).finally(() => {
1206
+ this.releaseOwnership?.();
1207
+ this.releaseOwnership = null;
1160
1208
  });
1161
1209
  return this.runTask;
1162
1210
  }
@@ -1172,6 +1220,8 @@ var RemoteServiceModule = class {
1172
1220
  } finally {
1173
1221
  this.abortController = null;
1174
1222
  this.runTask = null;
1223
+ this.releaseOwnership?.();
1224
+ this.releaseOwnership = null;
1175
1225
  }
1176
1226
  }
1177
1227
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/remote",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "private": false,
5
5
  "description": "Remote access runtime for NextClaw device registration, relay bridging, and service-managed connectivity.",
6
6
  "type": "module",
@@ -30,8 +30,8 @@
30
30
  "dependencies": {
31
31
  "commander": "^12.1.0",
32
32
  "ws": "^8.18.0",
33
- "@nextclaw/server": "0.10.40",
34
- "@nextclaw/core": "0.10.0"
33
+ "@nextclaw/server": "0.10.42",
34
+ "@nextclaw/core": "0.11.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^20.17.6",