kroki 1.0.2 → 1.0.4

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.
Files changed (2) hide show
  1. package/bin/kroki.js +63 -10
  2. package/package.json +1 -1
package/bin/kroki.js CHANGED
@@ -70,7 +70,9 @@ class RealtimeConnection {
70
70
  this.deviceId = config.deviceId;
71
71
  this.topic = `realtime:control:${this.googleId}`;
72
72
  this.ref = 0;
73
+ this.joinRef = null;
73
74
  this.ws = null;
75
+ this.heartbeatTimer = null;
74
76
  }
75
77
 
76
78
  nextRef() {
@@ -90,7 +92,16 @@ class RealtimeConnection {
90
92
  this.ws.onopen = () => {
91
93
  clearTimeout(timer);
92
94
  this.joinChannel()
93
- .then(() => resolve(this))
95
+ .then(() => {
96
+ this.heartbeatTimer = setInterval(() => {
97
+ if (this.ws && this.ws.readyState === globalThis.WebSocket.OPEN) {
98
+ this.ws.send(
99
+ JSON.stringify({ topic: 'phoenix', event: 'heartbeat', payload: {}, ref: this.nextRef() }),
100
+ );
101
+ }
102
+ }, 25000);
103
+ resolve(this);
104
+ })
94
105
  .catch(reject);
95
106
  };
96
107
 
@@ -103,21 +114,21 @@ class RealtimeConnection {
103
114
 
104
115
  joinChannel() {
105
116
  return new Promise((resolve, reject) => {
106
- const joinRef = this.nextRef();
117
+ this.joinRef = this.nextRef();
107
118
  const payload = {
108
119
  topic: this.topic,
109
120
  event: 'phx_join',
110
121
  payload: {
111
- config: { broadcast: { self: false } },
122
+ config: { private: true, broadcast: { self: false } },
112
123
  access_token: this.token,
113
124
  },
114
- ref: joinRef,
125
+ ref: this.joinRef,
115
126
  };
116
127
 
117
128
  const handler = event => {
118
129
  try {
119
130
  const msg = JSON.parse(event.data);
120
- if (msg.ref === joinRef) {
131
+ if (msg.ref === this.joinRef) {
121
132
  this.ws.removeEventListener('message', handler);
122
133
  if (msg.payload?.status === 'ok') {
123
134
  resolve();
@@ -149,10 +160,18 @@ class RealtimeConnection {
149
160
 
150
161
  if (onEvent) onEvent(envelope);
151
162
 
152
- if (envelope.kind === 'chat_task_complete' || envelope.kind === 'workflow_complete') {
163
+ if (
164
+ envelope.kind === 'chat_task_complete' ||
165
+ envelope.kind === 'workflow_complete' ||
166
+ envelope.kind === 'pair_approved'
167
+ ) {
153
168
  clearTimeout(timer);
154
169
  this.ws.removeEventListener('message', handler);
155
170
  resolve(envelope.payload);
171
+ } else if (envelope.kind === 'pair_rejected') {
172
+ clearTimeout(timer);
173
+ this.ws.removeEventListener('message', handler);
174
+ reject(new Error('Pairing request was rejected in Kroki browser.'));
156
175
  } else if (envelope.kind === 'error') {
157
176
  clearTimeout(timer);
158
177
  this.ws.removeEventListener('message', handler);
@@ -173,6 +192,7 @@ class RealtimeConnection {
173
192
  payload: command,
174
193
  },
175
194
  ref: msgRef,
195
+ join_ref: this.joinRef,
176
196
  };
177
197
 
178
198
  this.ws.send(JSON.stringify(packet));
@@ -180,6 +200,10 @@ class RealtimeConnection {
180
200
  }
181
201
 
182
202
  close() {
203
+ if (this.heartbeatTimer) {
204
+ clearInterval(this.heartbeatTimer);
205
+ this.heartbeatTimer = null;
206
+ }
183
207
  if (this.ws) {
184
208
  try {
185
209
  this.ws.close();
@@ -211,6 +235,28 @@ Requires an active \x1b[33mUltra\x1b[0m plan.
211
235
  const conn = new RealtimeConnection(payload);
212
236
  await conn.connect();
213
237
 
238
+ console.log(`\x1b[33mWaiting for confirmation in Kroki browser (click "Разрешить")...\x1b[0m`);
239
+
240
+ const pairCmd = {
241
+ v: 1,
242
+ id: `pair-${Date.now()}`,
243
+ kind: 'pair_request',
244
+ targetDeviceId: payload.deviceId,
245
+ taskId: `pair-${Date.now()}`,
246
+ issuedAt: Date.now(),
247
+ payload: {
248
+ clientName: 'Antigravity CLI',
249
+ },
250
+ };
251
+
252
+ try {
253
+ await conn.sendCommand(pairCmd, undefined, 60000);
254
+ } catch (err) {
255
+ conn.close();
256
+ console.error(`\x1b[31m✖ ${err.message}\x1b[0m`);
257
+ process.exit(1);
258
+ }
259
+
214
260
  saveActiveSession({
215
261
  ...payload,
216
262
  pairedAt: Date.now(),
@@ -252,13 +298,19 @@ async function taskCommand(taskText) {
252
298
  console.log(`\x1b[36m[Kroki: ${session.deviceName || 'Browser'}]\x1b[0m Running: "${taskText}"...`);
253
299
  const result = await conn.sendCommand(command, ev => {
254
300
  if (ev.kind === 'execution') {
255
- const title = ev.payload?.title || ev.payload?.type;
256
- if (title) console.log(` \x1b[90m→ ${title}\x1b[0m`);
301
+ const details = ev.payload?.data?.details || ev.payload?.title || ev.payload?.type;
302
+ if (details && typeof details === 'string') {
303
+ const firstLine = details.split('\n')[0].trim();
304
+ if (firstLine) console.log(` \x1b[90m→ ${firstLine.slice(0, 80)}\x1b[0m`);
305
+ }
257
306
  }
258
307
  });
259
308
 
260
309
  console.log('\x1b[32m✔ Task completed.\x1b[0m');
261
310
  if (result && typeof result === 'object') {
311
+ if (result.message) {
312
+ console.log(`\n\x1b[1m${result.message}\x1b[0m\n`);
313
+ }
262
314
  console.log(JSON.stringify(result, null, 2));
263
315
  }
264
316
  } finally {
@@ -323,7 +375,9 @@ function statusCommand() {
323
375
  console.log(' npx kroki pair <CODE>');
324
376
  return;
325
377
  }
326
- console.log(`\x1b[32m✔ Paired\x1b[0m with "${session.deviceName || session.deviceId}" (${session.platform || 'browser'})`);
378
+ console.log(
379
+ `\x1b[32m✔ Paired\x1b[0m with "${session.deviceName || session.deviceId}" (${session.platform || 'browser'})`,
380
+ );
327
381
  console.log(` Device ID: ${session.deviceId}`);
328
382
  }
329
383
 
@@ -388,4 +442,3 @@ switch (first) {
388
442
  });
389
443
  break;
390
444
  }
391
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kroki",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Kroki Browser Automation CLI — Control Chrome from terminal and AI agents",
5
5
  "keywords": [
6
6
  "kroki",