@rome-os/node 0.1.0 → 0.1.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/README.md CHANGED
@@ -8,8 +8,9 @@ Build an installation archive from this repository:
8
8
 
9
9
  ```sh
10
10
  pnpm --filter @rome-os/node build
11
+ pnpm --filter @rome-os/node-core pack --pack-destination /tmp
11
12
  pnpm --filter @rome-os/node pack --pack-destination /tmp
12
- npm install -g /tmp/rome-os-node-0.1.0.tgz
13
+ npm install -g /tmp/rome-os-node-core-0.1.0.tgz /tmp/rome-os-node-0.1.0.tgz
13
14
  ```
14
15
 
15
16
  On the target computer:
@@ -22,7 +23,27 @@ Open the printed authorization URL in a browser on that computer and approve it.
22
23
  The browser callback uses a temporary loopback listener. A remote browser needs
23
24
  a tunnel to that listener. Ctrl+C disconnects and preserves the credential.
24
25
 
25
- On the caller, configure a previously issued communication token through stdin:
26
+ Over SSH or on a headless computer, use device authorization:
27
+
28
+ ```sh
29
+ rome-node connect --device-code --name "Remote Linux" --cloud https://romeos.cc
30
+ ```
31
+
32
+ Open the printed URL on another device and approve the displayed user code.
33
+ The CLI prints the expiry and waits for approval without a callback listener or SSH tunnel.
34
+ Ctrl+C cancels the authorization. Both modes reuse stored credentials when valid.
35
+
36
+ Rome instances prepare caller authorization automatically at startup and after
37
+ Cloud enrollment. In a server environment that supplies `ROME_INSTANCE_TOKEN`, run:
38
+
39
+ ```sh
40
+ rome-node auth --server --cloud https://romeos.cc
41
+ ```
42
+
43
+ This exchanges the Instance Token with Cloud and stores only the communication
44
+ token. Existing caller credentials are reused. The CLI does not read Rome's database.
45
+
46
+ For manual caller configuration, pass an issued communication token through stdin:
26
47
 
27
48
  ```sh
28
49
  rome-node auth --cloud https://romeos.cc < /path/to/private-token-file
@@ -49,10 +70,21 @@ Gateway connection. `rome-node daemon status` reports its PID, and
49
70
  daemon before changing credentials. `rome-node daemon serve` runs in the
50
71
  foreground for diagnosis. Set `ROME_NODE_CONFIG_DIR` to isolate configurations.
51
72
 
73
+ `rome-node watch` subscribes to caller connection changes and prints one JSON
74
+ event per line. It starts the daemon if needed and begins with a snapshot.
75
+ After a connection loss it waits for the daemon and subscribes again; it does
76
+ not restart a stopped daemon. Ctrl+C closes only the watcher. These events
77
+ report the caller's Gateway connection, not remote device presence.
78
+
52
79
  Commands print JSON to stdout and diagnostics to stderr. Errors and nonzero
53
- remote exit codes produce a nonzero local exit status. An authorized device is
80
+ remote exit codes produce a nonzero local exit status. Output is collected in memory
81
+ and returned in full after program exit. Action message limits follow the WebSocket library and infrastructure.
82
+ The local daemon disconnects event observers that exceed its send-buffer limit. A lost response reports
83
+ `unknown_outcome`. Never automatically retry an unknown outcome.
84
+ An authorized device is
54
85
  not necessarily online. Use `describe` to query its current platform and actions.
55
86
 
56
- The package exports the Gateway client, socket adapter, and action response
57
- contract for other clients. See [the device architecture and operating guide](../../docs/rome-node.md)
87
+ The CLI uses [`@rome-os/node-core`](../rome-node-core/README.md) for authorization,
88
+ its shared caller daemon, and the computer host. The package also re-exports
89
+ the Gateway client, socket adapter, and action response contract for compatibility. See [the device architecture and operating guide](../../docs/rome-node.md)
58
90
  for credential boundaries, file operations, output limits, and disconnection behavior.
package/dist/entry.js CHANGED
@@ -1,12 +1,26 @@
1
1
  import { parseArgs } from "node:util";
2
- import { connectComputer, defaultDeviceName } from "./connect.js";
3
- import { isRecord, parseResponse } from "./actions.js";
4
- import { callerCredentialPath } from "./local.js";
5
- import { writePrivateJson } from "./storage.js";
6
- import { cloudOrigin, gatewayConfig } from "./cloud.js";
7
- import { callDaemon, daemonStatus, serveDaemon, startDaemon, stopDaemon } from "./daemon.js";
8
- import { validId } from "./protocol.js";
2
+ import { join } from "node:path";
3
+ import { isRecord, cloudOrigin, validId } from "@rome-os/node-core";
4
+ import { authorizeServer, configureCaller } from "@rome-os/node-core/auth";
5
+ import { createNodeClient, CallerConfigurationError, DeviceActionError, nodeConfigFromEnvironment, readOptionalCallerCredential, daemonStatus, startDaemon, stopDaemon, } from "@rome-os/node-core/client";
9
6
  import { commandHelp } from "./help.js";
7
+ async function withShutdown(run) {
8
+ const controller = new AbortController();
9
+ const stop = () => controller.abort();
10
+ process.once("SIGINT", stop);
11
+ process.once("SIGTERM", stop);
12
+ try {
13
+ await run(controller.signal);
14
+ }
15
+ catch (error) {
16
+ if (!controller.signal.aborted)
17
+ throw error;
18
+ }
19
+ finally {
20
+ process.removeListener("SIGINT", stop);
21
+ process.removeListener("SIGTERM", stop);
22
+ }
23
+ }
10
24
  async function main() {
11
25
  const { values, positionals } = parseArgs({
12
26
  allowPositionals: true,
@@ -16,6 +30,8 @@ async function main() {
16
30
  cloud: { type: "string" },
17
31
  name: { type: "string" },
18
32
  args: { type: "string" },
33
+ server: { type: "boolean" },
34
+ "device-code": { type: "boolean" },
19
35
  },
20
36
  });
21
37
  if (values.help || positionals.length === 0 || positionals[0] === "help") {
@@ -23,10 +39,28 @@ async function main() {
23
39
  process.stdout.write(commandHelp(explicitTopic ? positionals.slice(1) : positionals, explicitTopic));
24
40
  return;
25
41
  }
42
+ const config = nodeConfigFromEnvironment(process.env);
43
+ if (values.server && !(positionals[0] === "auth" && positionals.length === 1))
44
+ throw new Error("--server is only supported by rome-node auth.");
45
+ if (values["device-code"] && !(positionals[0] === "connect" && positionals.length === 1))
46
+ throw new Error("--device-code is only supported by rome-node connect.");
47
+ if (positionals[0] === "auth" && positionals[1] === "status" && positionals.length === 2) {
48
+ const credential = await readOptionalCallerCredential(config);
49
+ process.stdout.write(`${JSON.stringify(credential ? { configured: true, cloudUrl: credential.cloudUrl } : { configured: false })}\n`);
50
+ return;
51
+ }
26
52
  if (positionals[0] === "auth" && positionals.length === 1) {
53
+ const cloudUrl = cloudOrigin(values.cloud ?? "https://romeos.cc");
54
+ if (values.server) {
55
+ await authorizeServer(cloudUrl, process.env.ROME_INSTANCE_TOKEN, config, () => {
56
+ process.stderr.write("Rome Cloud is temporarily unavailable. Retrying server authorization.\n");
57
+ });
58
+ process.stdout.write(`${JSON.stringify({ configured: true })}\n`);
59
+ return;
60
+ }
27
61
  if (process.stdin.isTTY)
28
62
  throw new Error("Pass the communication token through stdin, not command arguments.");
29
- if (await daemonStatus())
63
+ if (await daemonStatus(config))
30
64
  throw new Error("Stop the CLI daemon before changing credentials: rome-node daemon stop");
31
65
  const chunks = [];
32
66
  let size = 0;
@@ -38,11 +72,7 @@ async function main() {
38
72
  chunks.push(buffer);
39
73
  }
40
74
  const token = Buffer.concat(chunks).toString("utf8").trim();
41
- if (!/^romedev_[A-Za-z0-9_-]{43}$/.test(token))
42
- throw new Error("Invalid communication token.");
43
- const cloudUrl = cloudOrigin(values.cloud ?? "https://romeos.cc");
44
- await gatewayConfig(cloudUrl, token);
45
- await writePrivateJson(callerCredentialPath(), { cloudUrl, token });
75
+ await configureCaller(cloudUrl, token, config);
46
76
  process.stdout.write(`${JSON.stringify({ configured: true })}\n`);
47
77
  return;
48
78
  }
@@ -51,36 +81,75 @@ async function main() {
51
81
  throw new Error("Invalid daemon command.");
52
82
  const operation = positionals[1] ?? "start";
53
83
  if (operation === "serve") {
54
- await serveDaemon();
84
+ const { serveDaemon } = await import("@rome-os/node-core/daemon");
85
+ await withShutdown((signal) => serveDaemon(config, signal));
55
86
  return;
56
87
  }
57
88
  if (operation === "stop") {
58
- await stopDaemon();
89
+ await stopDaemon(config);
59
90
  process.stdout.write(`${JSON.stringify({ running: false })}\n`);
60
91
  return;
61
92
  }
62
93
  if (operation !== "start" && operation !== "status")
63
94
  throw new Error("Unknown daemon command.");
64
- const state = operation === "start" ? await startDaemon() : await daemonStatus();
95
+ const state = operation === "start" ? await startDaemon(config) : await daemonStatus(config);
65
96
  process.stdout.write(`${JSON.stringify(state ? { running: true, pid: state.pid } : { running: false })}\n`);
66
97
  return;
67
98
  }
99
+ if (positionals[0] === "watch" && positionals.length === 1) {
100
+ await withShutdown(async (signal) => {
101
+ const client = createNodeClient(config);
102
+ let finish;
103
+ const stopped = new Promise((resolve) => {
104
+ finish = resolve;
105
+ });
106
+ const stop = () => {
107
+ client.disconnect();
108
+ finish();
109
+ };
110
+ signal.addEventListener("abort", stop, { once: true });
111
+ try {
112
+ if (signal.aborted)
113
+ return;
114
+ await client.subscribe("connection", (event) => process.stdout.write(`${JSON.stringify(event)}\n`));
115
+ await stopped;
116
+ }
117
+ finally {
118
+ signal.removeEventListener("abort", stop);
119
+ client.disconnect();
120
+ }
121
+ });
122
+ return;
123
+ }
68
124
  if (positionals[0] === "connect" && positionals.length === 1) {
69
- const controller = new AbortController();
70
- const stop = () => controller.abort();
71
- process.once("SIGINT", stop);
72
- process.once("SIGTERM", stop);
73
- try {
74
- await connectComputer(values.cloud ?? "https://romeos.cc", values.name ?? defaultDeviceName(), controller.signal);
75
- }
76
- catch (error) {
77
- if (!controller.signal.aborted)
78
- throw error;
79
- }
80
- finally {
81
- process.removeListener("SIGINT", stop);
82
- process.removeListener("SIGTERM", stop);
83
- }
125
+ const { connectHost, defaultDeviceName, loginDevice, loginDeviceCode, createFileCredentialStore, } = await import("@rome-os/node-core/host");
126
+ const name = values.name ?? defaultDeviceName();
127
+ await withShutdown((signal) => connectHost({
128
+ cloudUrl: values.cloud ?? "https://romeos.cc",
129
+ name,
130
+ signal,
131
+ credentials: createFileCredentialStore(join(config.directory, "credential.json")),
132
+ authorize: (origin, deviceName, abort) => values["device-code"]
133
+ ? loginDeviceCode(origin, deviceName, abort, (prompt) => {
134
+ process.stderr.write(`Open this URL in a browser on another device:\n${prompt.verificationUri}\n` +
135
+ `User code: ${prompt.userCode}\n` +
136
+ `Or open: ${prompt.verificationUriComplete}\n` +
137
+ `Expires in ${prompt.expiresIn} seconds. Waiting for approval. Ctrl+C cancels.\n`);
138
+ })
139
+ : loginDevice(origin, deviceName, abort, (url) => {
140
+ process.stderr.write(`Open this URL in your browser to authorize ${deviceName}:\n${url}\n`);
141
+ }),
142
+ onEvent: (event) => {
143
+ if (event.type === "authorization_required")
144
+ process.stderr.write("While connect is running, Rome instances in your account can execute programs and read or change files as your OS user.\n");
145
+ else if (event.type === "retrying")
146
+ process.stderr.write("Rome Cloud is unavailable; keeping credentials and retrying.\n");
147
+ else if (event.type === "device")
148
+ process.stderr.write(`Device: ${event.name}\nDevice ID: ${event.deviceId}\n`);
149
+ else
150
+ process.stderr.write(`Connection: ${event.status}\n`);
151
+ },
152
+ }));
84
153
  return;
85
154
  }
86
155
  if (positionals[0] !== "device")
@@ -102,26 +171,42 @@ async function main() {
102
171
  throw new Error("--args must contain valid JSON.");
103
172
  }
104
173
  }
105
- const response = await callDaemon(listing ? "/devices" : "/run", listing
106
- ? undefined
107
- : { deviceId, action: command === "describe" ? "system.info" : action, args });
108
- const body = await response.json();
109
- if (listing && response.ok && isRecord(body) && Array.isArray(body.items)) {
110
- process.stdout.write(`${JSON.stringify({ items: body.items })}\n`);
111
- return;
174
+ const client = createNodeClient(config);
175
+ try {
176
+ if (listing) {
177
+ process.stdout.write(`${JSON.stringify(await client.listDevices())}\n`);
178
+ return;
179
+ }
180
+ const result = command === "describe"
181
+ ? await client.describe(deviceId)
182
+ : await client.run(deviceId, action, args);
183
+ process.stdout.write(`${JSON.stringify(result)}\n`);
184
+ if (!result.ok ||
185
+ (isRecord(result.result) &&
186
+ Object.hasOwn(result.result, "exitCode") &&
187
+ result.result.exitCode !== 0))
188
+ process.exitCode = 1;
189
+ }
190
+ finally {
191
+ client.disconnect();
112
192
  }
113
- const result = parseResponse(body);
114
- if (!result)
115
- throw new Error("Invalid response from the CLI daemon.");
116
- process.stdout.write(`${JSON.stringify(result)}\n`);
117
- if (!result.ok ||
118
- !response.ok ||
119
- (isRecord(result.result) &&
120
- Object.hasOwn(result.result, "exitCode") &&
121
- result.result.exitCode !== 0))
122
- process.exitCode = 1;
123
193
  }
124
194
  main().catch((error) => {
195
+ if (error instanceof DeviceActionError) {
196
+ process.stdout.write(`${JSON.stringify(error.response)}\n`);
197
+ process.exitCode = 1;
198
+ return;
199
+ }
200
+ if (error instanceof CallerConfigurationError) {
201
+ const messages = {
202
+ not_configured: "Configure a communication token with rome-node auth --cloud <origin> using stdin first.",
203
+ daemon_running: "Stop the CLI daemon before changing credentials: rome-node daemon stop",
204
+ instance_token_required: "Server authorization requires ROME_INSTANCE_TOKEN in the environment.",
205
+ };
206
+ process.stderr.write(`${messages[error.code]}\n`);
207
+ process.exitCode = 1;
208
+ return;
209
+ }
125
210
  process.stderr.write(`${error instanceof Error ? error.message : "Rome Node failed."}\n`);
126
211
  process.exitCode = 1;
127
212
  });
package/dist/help.js CHANGED
@@ -6,10 +6,12 @@ Usage:
6
6
 
7
7
  Commands:
8
8
  connect Keep this computer online as a remote executor.
9
- auth Configure the caller's communication token through stdin.
9
+ auth Configure caller authorization through stdin or server mode.
10
+ auth status Report local caller configuration without revealing the token.
10
11
  device List authorized devices (they may be offline).
11
12
  device describe Query a device's platform and supported actions.
12
13
  device run Execute an action on a device.
14
+ watch Stream caller connection snapshots and changes as JSON lines.
13
15
  daemon Manage the caller's background connection.
14
16
 
15
17
  Agent workflow:
@@ -23,36 +25,72 @@ Use -h or --help on any command. No arguments also prints this overview.
23
25
  Set ROME_NODE_CONFIG_DIR to use a separate configuration directory.
24
26
  `;
25
27
  const commands = {
26
- connect: `Usage: rome-node connect [--cloud <origin>] [--name <name>]
28
+ watch: `Usage: rome-node watch
29
+
30
+ Subscribe to caller connection state as newline-delimited JSON.
31
+ Starts the daemon if needed, but does not open its Gateway connection.
32
+ Each connected event includes {transport:"connected",daemon:{pid,protocolVersion,connection}}.
33
+ Local disconnection emits {transport:"disconnected",daemon:null,reason:"connection_lost"}.
34
+ The daemon connection and its Gateway connection are separate states.
35
+
36
+ Reconnection restores the subscription and emits a fresh snapshot, not missed history.
37
+ A watcher does not restart an explicitly stopped daemon. Another command can start it.
38
+ Ctrl+C exits the watcher without stopping the daemon or remote operations.
39
+ These events do not indicate whether individual remote devices are online.
40
+ `,
41
+ connect: `Usage: rome-node connect [--device-code] [--cloud <origin>] [--name <name>]
27
42
 
28
43
  Run this on the computer that will execute programs.
29
44
  Open the printed browser URL and approve access when authorization is needed.
45
+ By default, the browser returns to a loopback listener on this computer.
46
+ Use --device-code over SSH or on a headless computer to approve from another device.
47
+ This mode prints a URL and user code without opening a browser or callback listener.
48
+ Stored credentials are reused in either mode. SSH is not detected automatically.
30
49
  The process stays in the foreground. Ctrl+C disconnects and preserves authorization.
31
50
  Programs run as the OS user who starts connect.
32
51
 
33
52
  Options:
34
53
  --cloud <origin> Cloud origin (default: https://romeos.cc).
35
54
  --name <name> Name for browser authorization (default: computer hostname).
55
+ --device-code Authorize from another device without a callback or SSH tunnel.
36
56
 
37
57
  Example:
38
58
  rome-node connect --name "My computer"
59
+ rome-node connect --device-code --name "Remote Linux" --cloud https://romeos.cc
39
60
  `,
40
- auth: `Usage: rome-node auth [--cloud <origin>] < token-file
61
+ auth: `Usage:
62
+ rome-node auth [--cloud <origin>] < token-file
63
+ rome-node auth --server [--cloud <origin>]
41
64
 
42
65
  Read an issued communication token (romedev_...) from stdin, validate it with
43
- Cloud, and save it privately for device commands. An Instance Token must first
44
- be exchanged through Cloud. This command does not perform that exchange.
66
+ Cloud, and save it privately for device commands.
45
67
  Stop a running caller daemon before changing credentials.
46
68
 
69
+ With --server, reuse existing caller credentials for this Cloud origin. If missing,
70
+ read ROME_INSTANCE_TOKEN from the environment, exchange it with Cloud, and save
71
+ only the communication token. This does not start a daemon or read Rome's database.
72
+ Transient Cloud failures get up to three attempts with delays of one and five seconds.
73
+ An issued token is reused across validation retries. Existing credentials for
74
+ another Cloud origin are preserved and require manual configuration.
75
+
47
76
  Options:
48
77
  --cloud <origin> Cloud origin (default: https://romeos.cc).
78
+ --server Authorize using ROME_INSTANCE_TOKEN from the environment.
49
79
 
50
80
  Example:
51
81
  rome-node daemon stop
52
82
  rome-node auth --cloud https://romeos.cc < /path/to/private-token-file
83
+ rome-node auth --server --cloud https://romeos.cc
53
84
 
54
85
  Output: {"configured":true}
55
- Pass tokens through stdin. Never put them in command arguments or print them.
86
+ Pass communication tokens through stdin. Never put tokens in command arguments or print them.
87
+ `,
88
+ "auth status": `Usage: rome-node auth status
89
+
90
+ Report local caller configuration without contacting Cloud or starting the daemon.
91
+ Output: {"configured":true,"cloudUrl":"https://romeos.cc"} or {"configured":false}.
92
+ This checks the local credential format, not its validity at Cloud.
93
+ The token is never printed.
56
94
  `,
57
95
  device: `Usage:
58
96
  rome-node device
@@ -112,8 +150,9 @@ Output:
112
150
  Check exec's exitCode even when ok is true. The local exit status is 1 for
113
151
  action failures, nonzero remote exit codes, or signal termination, otherwise 0.
114
152
  Local setup and argument errors print diagnostics to stderr and exit 1.
115
- Each output stream retains at most 48 KiB, with further truncation to fit a frame.
116
- Check truncated before treating output or file contents as complete.
153
+ Output is collected in memory and returned in full after program exit.
154
+ The CLI does not truncate output or impose message or send-buffer size limits.
155
+ Transport limits still apply. Connection loss returns unknown_outcome.
117
156
 
118
157
  The caller waits up to 60 seconds for a response. unknown_outcome means execution
119
158
  may have started or completed. A local timeout does not cancel the remote program.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,2 @@
1
- export { connectGateway, type GatewayClientOptions, type ConnectionStatus, type ClientSocket, } from "./client.js";
2
- export { MAX_MESSAGE_BYTES, byteLength, validId, type GatewayMessage, type InboundEnvelope, type OutboundEnvelope, } from "./protocol.js";
3
- export { CloudError, cloudOrigin, cloudRequest, gatewayConfig } from "./cloud.js";
4
- export { actionError, isRecord, parseResponse, type ActionResponse, type ActionRequest, } from "./actions.js";
5
- export { createNodeSocket } from "./socket.js";
1
+ export * from "@rome-os/node-core";
2
+ export { createNodeSocket } from "@rome-os/node-core/socket";
package/dist/index.js CHANGED
@@ -1,5 +1,2 @@
1
- export { connectGateway, } from "./client.js";
2
- export { MAX_MESSAGE_BYTES, byteLength, validId, } from "./protocol.js";
3
- export { CloudError, cloudOrigin, cloudRequest, gatewayConfig } from "./cloud.js";
4
- export { actionError, isRecord, parseResponse, } from "./actions.js";
5
- export { createNodeSocket } from "./socket.js";
1
+ export * from "@rome-os/node-core";
2
+ export { createNodeSocket } from "@rome-os/node-core/socket";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rome-os/node",
3
- "version": "0.1.0",
4
- "description": "Rome device protocol, Gateway client, and remote computer CLI",
3
+ "version": "0.1.2",
4
+ "description": "Standalone CLI for Rome remote devices",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "engines": {
@@ -22,21 +22,22 @@
22
22
  }
23
23
  },
24
24
  "dependencies": {
25
- "ws": "^8.18.0"
25
+ "@rome-os/node-core": "^0.1.1"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@rstest/core": "^0.11.11",
29
29
  "@types/node": "25.2.3",
30
30
  "@types/ws": "^8.18.0",
31
- "typescript": "^5.9.3"
31
+ "typescript": "^5.9.3",
32
+ "ws": "^8.18.0"
32
33
  },
33
34
  "repository": {
34
35
  "type": "git",
35
36
  "url": "git+https://github.com/rome-os/rome.git",
36
- "directory": "packages/rome-node"
37
+ "directory": "packages/rome-node-cli"
37
38
  },
38
39
  "scripts": {
39
- "build": "tsc -p tsconfig.build.json",
40
+ "build": "pnpm --filter @rome-os/node-core build && node -e \"require('node:fs').rmSync('dist', {recursive:true, force:true})\" && tsc -p tsconfig.build.json",
40
41
  "typecheck": "tsc --noEmit",
41
42
  "test": "pnpm build && ../../scripts/test-env.sh rstest"
42
43
  }
package/dist/actions.d.ts DELETED
@@ -1,20 +0,0 @@
1
- export interface ActionRequest {
2
- type: "request";
3
- action: string;
4
- args: unknown;
5
- }
6
- export type ActionResponse = {
7
- type: "response";
8
- ok: true;
9
- result: unknown;
10
- } | {
11
- type: "response";
12
- ok: false;
13
- error: {
14
- code: string;
15
- message: string;
16
- };
17
- };
18
- export declare function actionError(code: string, message: string): ActionResponse;
19
- export declare function isRecord(value: unknown): value is Record<string, unknown>;
20
- export declare function parseResponse(value: unknown): ActionResponse | null;
package/dist/actions.js DELETED
@@ -1,18 +0,0 @@
1
- export function actionError(code, message) {
2
- return { type: "response", ok: false, error: { code, message } };
3
- }
4
- export function isRecord(value) {
5
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
6
- }
7
- export function parseResponse(value) {
8
- if (!isRecord(value) || value.type !== "response")
9
- return null;
10
- if (value.ok === true && Object.hasOwn(value, "result"))
11
- return { type: "response", ok: true, result: value.result };
12
- if (value.ok === false &&
13
- isRecord(value.error) &&
14
- typeof value.error.code === "string" &&
15
- typeof value.error.message === "string")
16
- return actionError(value.error.code, value.error.message);
17
- return null;
18
- }
package/dist/client.d.ts DELETED
@@ -1,29 +0,0 @@
1
- import { type GatewayMessage, type OutboundEnvelope } from "./protocol.js";
2
- export interface ClientSocket {
3
- readyState: number;
4
- bufferedAmount: number;
5
- send(data: string): void;
6
- close(code?: number, reason?: string): void;
7
- addEventListener(type: "open", listener: () => void): void;
8
- addEventListener(type: "message", listener: (event: {
9
- data: unknown;
10
- }) => void): void;
11
- addEventListener(type: "close", listener: (event: {
12
- code: number;
13
- }) => void): void;
14
- addEventListener(type: "error", listener: () => void): void;
15
- }
16
- export type ConnectionStatus = "connecting" | "online" | "retrying" | "revoked" | "superseded" | "stopped";
17
- export interface GatewayClientOptions {
18
- gatewayUrl: string;
19
- deviceToken: string;
20
- createSocket(url: string, authorization: string): ClientSocket;
21
- onMessage(message: GatewayMessage): void;
22
- onStatus?(status: ConnectionStatus): void;
23
- random?(): number;
24
- beforeConnect?(): Promise<string | null>;
25
- }
26
- export declare function connectGateway(options: GatewayClientOptions): {
27
- send(envelope: OutboundEnvelope): boolean;
28
- stop(): void;
29
- };