@nextclaw/remote 0.1.14 → 0.1.16

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
@@ -206,4 +206,17 @@ declare class RemoteServiceModule {
206
206
  stop(): Promise<void>;
207
207
  }
208
208
 
209
- export { type RegisteredRemoteDevice, type RelayRequestFrame, type RemoteConnectCommandOptions, RemoteConnector, type RemoteConnectorRunOptions, type RemoteDoctorCommandOptions, type RemoteEnableCommandOptions, type RemoteLogger, RemotePlatformClient, type RemotePlatformClientDeps, RemoteRelayBridge, type RemoteRunContext, RemoteRuntimeActions, type RemoteRuntimeState, RemoteServiceModule, type RemoteServiceStateView, type RemoteStatusCommandOptions, type RemoteStatusSnapshot, RemoteStatusStore, type RemoteStatusWriter, buildConfiguredRemoteState, delay, normalizeOptionalString, redactWsUrl, registerRemoteCommands, resolveRemoteStatusSnapshot };
209
+ type PlatformSessionTokenState = {
210
+ valid: false;
211
+ reason: "missing" | "malformed" | "expired";
212
+ payload: Record<string, unknown> | null;
213
+ } | {
214
+ valid: true;
215
+ reason: "valid";
216
+ payload: Record<string, unknown>;
217
+ };
218
+ declare function decodePlatformSessionTokenPayload(token: string): Record<string, unknown> | null;
219
+ declare function readPlatformSessionTokenState(token: string | null | undefined): PlatformSessionTokenState;
220
+ declare function isValidPlatformSessionToken(token: string | null | undefined): token is string;
221
+
222
+ export { type PlatformSessionTokenState, type RegisteredRemoteDevice, type RelayRequestFrame, type RemoteConnectCommandOptions, RemoteConnector, type RemoteConnectorRunOptions, type RemoteDoctorCommandOptions, type RemoteEnableCommandOptions, type RemoteLogger, RemotePlatformClient, type RemotePlatformClientDeps, RemoteRelayBridge, type RemoteRunContext, RemoteRuntimeActions, type RemoteRuntimeState, RemoteServiceModule, type RemoteServiceStateView, type RemoteStatusCommandOptions, type RemoteStatusSnapshot, RemoteStatusStore, type RemoteStatusWriter, buildConfiguredRemoteState, decodePlatformSessionTokenPayload, delay, isValidPlatformSessionToken, normalizeOptionalString, readPlatformSessionTokenState, redactWsUrl, registerRemoteCommands, resolveRemoteStatusSnapshot };
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ function registerRemoteCommands(program, runtime) {
5
5
  remote.command("disable").description("Disable service-managed remote access").action(async () => runtime.disable());
6
6
  remote.command("status").description("Show remote access status").option("--json", "Print JSON").action(async (opts) => runtime.status(opts));
7
7
  remote.command("doctor").description("Run remote access diagnostics").option("--json", "Print JSON").action(async (opts) => runtime.doctor(opts));
8
- remote.command("connect").description("Foreground debug mode: register this machine and keep the connector online").option("--api-base <url>", "Platform API base (supports /v1 suffix)").option("--local-origin <url>", "Local NextClaw UI origin (default: active service or http://127.0.0.1:18791)").option("--name <name>", "Device display name").option("--once", "Connect once without auto-reconnect", false).action(async (opts) => runtime.connect(opts));
8
+ remote.command("connect").description("Foreground debug mode: register this machine and keep the connector online").option("--api-base <url>", "Platform API base (supports /v1 suffix)").option("--local-origin <url>", "Local NextClaw UI origin (default: active service or http://127.0.0.1:55667)").option("--name <name>", "Device display name").option("--once", "Connect once without auto-reconnect", false).action(async (opts) => runtime.connect(opts));
9
9
  }
10
10
 
11
11
  // src/remote-runtime-actions.ts
@@ -53,6 +53,68 @@ var RemoteRuntimeActions = class {
53
53
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
54
54
  import { dirname, join } from "path";
55
55
  import { hostname, platform as readPlatform } from "os";
56
+
57
+ // src/platform-session-token.ts
58
+ function decodeBase64UrlSegment(segment) {
59
+ try {
60
+ return Buffer.from(segment, "base64url").toString("utf-8");
61
+ } catch {
62
+ return null;
63
+ }
64
+ }
65
+ function decodePlatformSessionTokenPayload(token) {
66
+ const segments = token.split(".");
67
+ if (segments.length !== 3 || segments[0] !== "nca" || !segments[1]) {
68
+ return null;
69
+ }
70
+ const raw = decodeBase64UrlSegment(segments[1]);
71
+ if (!raw) {
72
+ return null;
73
+ }
74
+ try {
75
+ const parsed = JSON.parse(raw);
76
+ return typeof parsed === "object" && parsed !== null ? parsed : null;
77
+ } catch {
78
+ return null;
79
+ }
80
+ }
81
+ function readPlatformSessionTokenState(token) {
82
+ if (typeof token !== "string" || token.trim().length === 0) {
83
+ return {
84
+ valid: false,
85
+ reason: "missing",
86
+ payload: null
87
+ };
88
+ }
89
+ const trimmed = token.trim();
90
+ const payload = decodePlatformSessionTokenPayload(trimmed);
91
+ if (!payload) {
92
+ return {
93
+ valid: false,
94
+ reason: "malformed",
95
+ payload: null
96
+ };
97
+ }
98
+ const exp = typeof payload.exp === "number" && Number.isFinite(payload.exp) ? payload.exp : Number.NaN;
99
+ const now = Math.floor(Date.now() / 1e3);
100
+ if (!Number.isFinite(exp) || exp <= now) {
101
+ return {
102
+ valid: false,
103
+ reason: "expired",
104
+ payload
105
+ };
106
+ }
107
+ return {
108
+ valid: true,
109
+ reason: "valid",
110
+ payload
111
+ };
112
+ }
113
+ function isValidPlatformSessionToken(token) {
114
+ return readPlatformSessionTokenState(token).valid;
115
+ }
116
+
117
+ // src/remote-platform-client.ts
56
118
  function ensureDir(path) {
57
119
  mkdirSync(path, { recursive: true });
58
120
  }
@@ -169,16 +231,23 @@ var RemotePlatformClient = class {
169
231
  const providers = config.providers;
170
232
  const nextclawProvider = providers.nextclaw;
171
233
  const token = typeof nextclawProvider?.apiKey === "string" ? nextclawProvider.apiKey.trim() : "";
172
- if (!token) {
234
+ const tokenState = readPlatformSessionTokenState(token);
235
+ if (tokenState.reason === "missing") {
173
236
  throw new Error('NextClaw platform token is missing. Run "nextclaw login" first.');
174
237
  }
238
+ if (tokenState.reason === "expired") {
239
+ throw new Error('NextClaw platform token expired. Run "nextclaw login" or browser sign-in again.');
240
+ }
241
+ if (tokenState.reason === "malformed") {
242
+ throw new Error('NextClaw platform token is invalid. Run "nextclaw login" again.');
243
+ }
175
244
  const configuredApiBase = normalizeOptionalString(config.remote.platformApiBase) ?? (typeof nextclawProvider?.apiBase === "string" ? nextclawProvider.apiBase.trim() : "");
176
245
  const rawApiBase = normalizeOptionalString(opts.apiBase) ?? configuredApiBase;
177
246
  if (!rawApiBase) {
178
247
  throw new Error("Platform API base is missing. Pass --api-base, run nextclaw login, or set remote.platformApiBase.");
179
248
  }
180
249
  const platformBase = this.deps.resolvePlatformBase(rawApiBase);
181
- return { platformBase, token, config };
250
+ return { platformBase, token: token.trim(), config };
182
251
  }
183
252
  resolveLocalOrigin(config, opts) {
184
253
  const explicitOrigin = normalizeOptionalString(opts.localOrigin);
@@ -189,7 +258,7 @@ var RemotePlatformClient = class {
189
258
  if (state && this.deps.isProcessRunning?.(state.pid) && Number.isFinite(state.uiPort)) {
190
259
  return `http://127.0.0.1:${state.uiPort}`;
191
260
  }
192
- const configuredPort = typeof config.ui?.port === "number" && Number.isFinite(config.ui.port) ? config.ui.port : 18791;
261
+ const configuredPort = typeof config.ui?.port === "number" && Number.isFinite(config.ui.port) ? config.ui.port : 55667;
193
262
  return `http://127.0.0.1:${configuredPort}`;
194
263
  }
195
264
  resolveDisplayName(config, opts) {
@@ -656,8 +725,11 @@ export {
656
725
  RemoteServiceModule,
657
726
  RemoteStatusStore,
658
727
  buildConfiguredRemoteState,
728
+ decodePlatformSessionTokenPayload,
659
729
  delay,
730
+ isValidPlatformSessionToken,
660
731
  normalizeOptionalString,
732
+ readPlatformSessionTokenState,
661
733
  redactWsUrl,
662
734
  registerRemoteCommands,
663
735
  resolveRemoteStatusSnapshot
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/remote",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "private": false,
5
5
  "description": "Remote access runtime for NextClaw device registration, relay bridging, and service-managed connectivity.",
6
6
  "type": "module",
@@ -29,8 +29,8 @@
29
29
  ],
30
30
  "dependencies": {
31
31
  "commander": "^12.1.0",
32
- "@nextclaw/core": "0.9.7",
33
- "@nextclaw/server": "0.10.18"
32
+ "@nextclaw/server": "0.10.20",
33
+ "@nextclaw/core": "0.9.8"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^20.17.6",