lynkeus-client 0.4.0 → 0.5.0

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/lib/index.js CHANGED
@@ -15,6 +15,8 @@ export class AgentServer extends EventEmitter {
15
15
  port;
16
16
  host;
17
17
  #token;
18
+ /** An app connected and stayed silent after the auth: the sign of a token that does not match. */
19
+ #unanswered = false;
18
20
  #session = Math.random().toString(36).slice(2) + Date.now().toString(36);
19
21
  #server = null;
20
22
  #socket = null;
@@ -69,7 +71,11 @@ export class AgentServer extends EventEmitter {
69
71
  return new Promise((resolve, reject) => {
70
72
  const timer = setTimeout(() => {
71
73
  this.off('hello', onHello);
72
- reject(new AgentError(`No app attached on ${this.host}:${this.port} within ${timeoutMs}ms. Is a dev build running with <Lynkeus>?`));
74
+ // An app that dials in and never says hello is one whose token is not ours: the agent stays silent then.
75
+ const why = this.#unanswered
76
+ ? ' An app connected but did not answer: its token is not this one. Start Metro with the LYNKEUS_TOKEN this driver has (.lynkeus/token or the environment), with --reset-cache.'
77
+ : ' Is a dev build running with <Lynkeus>?';
78
+ reject(new AgentError(`No app attached on ${this.host}:${this.port} within ${timeoutMs}ms.${why}`));
73
79
  }, timeoutMs);
74
80
  const onHello = (hello) => {
75
81
  clearTimeout(timer);
@@ -114,6 +120,11 @@ export class AgentServer extends EventEmitter {
114
120
  this.#hello = null;
115
121
  this.#cancelGrace();
116
122
  socket.send(JSON.stringify({ event: 'auth', session: this.#session, token: this.#token }));
123
+ const silence = setTimeout(() => {
124
+ if (this.#socket === socket && !this.#hello && socket.readyState === WebSocket.OPEN)
125
+ this.#unanswered = true;
126
+ }, 2000);
127
+ socket.once('close', () => clearTimeout(silence));
117
128
  socket.on('message', (data) => {
118
129
  let message;
119
130
  try {
@@ -125,6 +136,8 @@ export class AgentServer extends EventEmitter {
125
136
  if ('event' in message) {
126
137
  if (message.event === 'hello') {
127
138
  this.#hello = message;
139
+ this.#unanswered = false;
140
+ clearTimeout(silence);
128
141
  // Resend what the previous socket may have dropped, but only once this one has spoken the
129
142
  // protocol: a bare local connection could be any process.
130
143
  if (socket.readyState === WebSocket.OPEN) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lynkeus-client",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Node driver for apps running lynkeus-agent: attach, read the screen, press, type, wait.",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -31,7 +31,7 @@
31
31
  "node": ">=22"
32
32
  },
33
33
  "dependencies": {
34
- "lynkeus-protocol": "^0.4.0",
34
+ "lynkeus-protocol": "^0.5.0",
35
35
  "ws": "^8.18.0"
36
36
  },
37
37
  "devDependencies": {
package/src/index.ts CHANGED
@@ -39,6 +39,8 @@ export class AgentServer extends EventEmitter {
39
39
  readonly host: string;
40
40
 
41
41
  #token?: string;
42
+ /** An app connected and stayed silent after the auth: the sign of a token that does not match. */
43
+ #unanswered = false;
42
44
  #session = Math.random().toString(36).slice(2) + Date.now().toString(36);
43
45
  #server: WebSocketServer | null = null;
44
46
  #socket: WebSocket | null = null;
@@ -101,7 +103,11 @@ export class AgentServer extends EventEmitter {
101
103
  return new Promise<HelloEvent>((resolve, reject) => {
102
104
  const timer = setTimeout(() => {
103
105
  this.off('hello', onHello);
104
- reject(new AgentError(`No app attached on ${this.host}:${this.port} within ${timeoutMs}ms. Is a dev build running with <Lynkeus>?`));
106
+ // An app that dials in and never says hello is one whose token is not ours: the agent stays silent then.
107
+ const why = this.#unanswered
108
+ ? ' An app connected but did not answer: its token is not this one. Start Metro with the LYNKEUS_TOKEN this driver has (.lynkeus/token or the environment), with --reset-cache.'
109
+ : ' Is a dev build running with <Lynkeus>?';
110
+ reject(new AgentError(`No app attached on ${this.host}:${this.port} within ${timeoutMs}ms.${why}`));
105
111
  }, timeoutMs);
106
112
  const onHello = (hello: HelloEvent) => {
107
113
  clearTimeout(timer);
@@ -152,6 +158,10 @@ export class AgentServer extends EventEmitter {
152
158
  this.#hello = null;
153
159
  this.#cancelGrace();
154
160
  socket.send(JSON.stringify({ event: 'auth', session: this.#session, token: this.#token }));
161
+ const silence = setTimeout(() => {
162
+ if (this.#socket === socket && !this.#hello && socket.readyState === WebSocket.OPEN) this.#unanswered = true;
163
+ }, 2000);
164
+ socket.once('close', () => clearTimeout(silence));
155
165
 
156
166
  socket.on('message', (data) => {
157
167
  let message: Response | AgentEvent;
@@ -163,6 +173,8 @@ export class AgentServer extends EventEmitter {
163
173
  if ('event' in message) {
164
174
  if (message.event === 'hello') {
165
175
  this.#hello = message;
176
+ this.#unanswered = false;
177
+ clearTimeout(silence);
166
178
  // Resend what the previous socket may have dropped, but only once this one has spoken the
167
179
  // protocol: a bare local connection could be any process.
168
180
  if (socket.readyState === WebSocket.OPEN) {