kroki 1.0.3 → 1.0.5

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 +199 -24
  2. package/package.json +1 -1
package/bin/kroki.js CHANGED
@@ -160,10 +160,18 @@ class RealtimeConnection {
160
160
 
161
161
  if (onEvent) onEvent(envelope);
162
162
 
163
- 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
+ ) {
164
168
  clearTimeout(timer);
165
169
  this.ws.removeEventListener('message', handler);
166
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.'));
167
175
  } else if (envelope.kind === 'error') {
168
176
  clearTimeout(timer);
169
177
  this.ws.removeEventListener('message', handler);
@@ -227,6 +235,28 @@ Requires an active \x1b[33mUltra\x1b[0m plan.
227
235
  const conn = new RealtimeConnection(payload);
228
236
  await conn.connect();
229
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
+
230
260
  saveActiveSession({
231
261
  ...payload,
232
262
  pairedAt: Date.now(),
@@ -240,7 +270,54 @@ Requires an active \x1b[33mUltra\x1b[0m plan.
240
270
  console.log(' \x1b[33mnpx kroki run "My Workflow Name"\x1b[0m\n');
241
271
  }
242
272
 
243
- async function taskCommand(taskText) {
273
+ function createProgressTracker({ verbose = false, isJson = false, initialText = '' }) {
274
+ const steps = [];
275
+ let currentStatus = initialText;
276
+ let timer = null;
277
+ const isTTY = Boolean(process.stdout.isTTY) && !isJson && !verbose;
278
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
279
+ let frameIdx = 0;
280
+
281
+ if (isTTY) {
282
+ timer = setInterval(() => {
283
+ const frame = frames[frameIdx++ % frames.length];
284
+ const text = currentStatus ? ` \x1b[90m${currentStatus.slice(0, 65)}\x1b[0m` : '';
285
+ process.stdout.write(`\r\x1b[36m${frame}\x1b[0m${text}\x1b[K`);
286
+ }, 80);
287
+ } else if (!isJson && !verbose) {
288
+ if (initialText) console.log(initialText);
289
+ } else if (verbose && initialText) {
290
+ console.log(initialText);
291
+ }
292
+
293
+ return {
294
+ onStep(stepText) {
295
+ if (!stepText || typeof stepText !== 'string') return;
296
+ const firstLine = stepText.split('\n')[0].trim();
297
+ if (!firstLine) return;
298
+ steps.push(firstLine);
299
+ if (verbose) {
300
+ console.log(` \x1b[90m→ ${firstLine.slice(0, 100)}\x1b[0m`);
301
+ } else {
302
+ currentStatus = firstLine;
303
+ }
304
+ },
305
+ stop() {
306
+ if (timer) {
307
+ clearInterval(timer);
308
+ timer = null;
309
+ }
310
+ if (isTTY) {
311
+ process.stdout.write('\r\x1b[K');
312
+ }
313
+ },
314
+ getSteps() {
315
+ return steps;
316
+ },
317
+ };
318
+ }
319
+
320
+ async function taskCommand(taskText, { verbose = false, json = false } = {}) {
244
321
  const session = getActiveSession();
245
322
  if (!session) {
246
323
  console.error('\x1b[31mError: Not paired.\x1b[0m');
@@ -264,25 +341,52 @@ async function taskCommand(taskText) {
264
341
  },
265
342
  };
266
343
 
344
+ const initialMsg = `\x1b[36m[Kroki: ${session.deviceName || 'Browser'}]\x1b[0m Running: "${taskText}"...`;
345
+ const tracker = createProgressTracker({ verbose, isJson: json, initialText: initialMsg });
346
+
267
347
  try {
268
- console.log(`\x1b[36m[Kroki: ${session.deviceName || 'Browser'}]\x1b[0m Running: "${taskText}"...`);
269
348
  const result = await conn.sendCommand(command, ev => {
270
349
  if (ev.kind === 'execution') {
271
- const title = ev.payload?.title || ev.payload?.type;
272
- if (title) console.log(` \x1b[90m→ ${title}\x1b[0m`);
350
+ const details = ev.payload?.data?.details || ev.payload?.title || ev.payload?.type;
351
+ if (details) tracker.onStep(details);
273
352
  }
274
353
  });
275
354
 
276
- console.log('\x1b[32m✔ Task completed.\x1b[0m');
277
- if (result && typeof result === 'object') {
355
+ tracker.stop();
356
+
357
+ if (json) {
278
358
  console.log(JSON.stringify(result, null, 2));
359
+ } else {
360
+ if (verbose) {
361
+ console.log('\x1b[32m✔ Task completed.\x1b[0m\n');
362
+ }
363
+ if (result && typeof result === 'object' && result.message) {
364
+ console.log(result.message);
365
+ } else if (result && typeof result === 'object') {
366
+ console.log(JSON.stringify(result, null, 2));
367
+ }
368
+ }
369
+ } catch (err) {
370
+ tracker.stop();
371
+ if (!verbose && tracker.getSteps().length > 0) {
372
+ console.error('\x1b[90mRecent steps before failure:\x1b[0m');
373
+ for (const step of tracker.getSteps().slice(-10)) {
374
+ console.error(` \x1b[90m→ ${step}\x1b[0m`);
375
+ }
376
+ console.error('');
279
377
  }
378
+ if (json) {
379
+ console.error(JSON.stringify({ success: false, error: err.message, steps: tracker.getSteps() }, null, 2));
380
+ } else {
381
+ console.error(`\x1b[31m✖ Error: ${err.message}\x1b[0m`);
382
+ }
383
+ process.exit(1);
280
384
  } finally {
281
385
  conn.close();
282
386
  }
283
387
  }
284
388
 
285
- async function workflowCommand(nameOrId, varsJson) {
389
+ async function workflowCommand(nameOrId, varsJson, { verbose = false, json = false } = {}) {
286
390
  const session = getActiveSession();
287
391
  if (!session) {
288
392
  console.error('\x1b[31mError: Not paired.\x1b[0m');
@@ -316,29 +420,75 @@ async function workflowCommand(nameOrId, varsJson) {
316
420
  },
317
421
  };
318
422
 
423
+ const initialMsg = `\x1b[36m[Kroki: ${session.deviceName || 'Browser'}]\x1b[0m Executing workflow "${nameOrId}"...`;
424
+ const tracker = createProgressTracker({ verbose, isJson: json, initialText: initialMsg });
425
+
319
426
  try {
320
- console.log(`\x1b[36m[Kroki: ${session.deviceName || 'Browser'}]\x1b[0m Executing workflow "${nameOrId}"...`);
321
427
  const result = await conn.sendCommand(command, ev => {
322
428
  if (ev.kind === 'workflow_step_start') {
323
- console.log(` \x1b[90m[step] ${ev.payload?.stepType || 'action'}: ${ev.payload?.comment || ''}\x1b[0m`);
429
+ const stepDesc = `[step] ${ev.payload?.stepType || 'action'}: ${ev.payload?.comment || ''}`;
430
+ tracker.onStep(stepDesc);
324
431
  }
325
432
  });
326
433
 
327
- console.log('\x1b[32m✔ Workflow finished.\x1b[0m');
328
- if (result) console.log(JSON.stringify(result, null, 2));
434
+ tracker.stop();
435
+
436
+ if (json) {
437
+ console.log(JSON.stringify(result, null, 2));
438
+ } else {
439
+ if (verbose) {
440
+ console.log('\x1b[32m✔ Workflow finished.\x1b[0m\n');
441
+ }
442
+ if (result) console.log(JSON.stringify(result, null, 2));
443
+ }
444
+ } catch (err) {
445
+ tracker.stop();
446
+ if (!verbose && tracker.getSteps().length > 0) {
447
+ console.error('\x1b[90mRecent steps before failure:\x1b[0m');
448
+ for (const step of tracker.getSteps().slice(-10)) {
449
+ console.error(` \x1b[90m→ ${step}\x1b[0m`);
450
+ }
451
+ console.error('');
452
+ }
453
+ if (json) {
454
+ console.error(JSON.stringify({ success: false, error: err.message, steps: tracker.getSteps() }, null, 2));
455
+ } else {
456
+ console.error(`\x1b[31m✖ Error: ${err.message}\x1b[0m`);
457
+ }
458
+ process.exit(1);
329
459
  } finally {
330
460
  conn.close();
331
461
  }
332
462
  }
333
463
 
334
- function statusCommand() {
464
+ function statusCommand({ json = false } = {}) {
335
465
  const session = getActiveSession();
336
466
  if (!session) {
467
+ if (json) {
468
+ console.log(JSON.stringify({ paired: false }, null, 2));
469
+ return;
470
+ }
337
471
  console.log('No active paired profile.');
338
472
  console.log('To pair, get a code from https://kroki.ai/#/devices and run:');
339
473
  console.log(' npx kroki pair <CODE>');
340
474
  return;
341
475
  }
476
+ if (json) {
477
+ console.log(
478
+ JSON.stringify(
479
+ {
480
+ paired: true,
481
+ deviceId: session.deviceId,
482
+ deviceName: session.deviceName,
483
+ platform: session.platform,
484
+ pairedAt: session.pairedAt,
485
+ },
486
+ null,
487
+ 2,
488
+ ),
489
+ );
490
+ return;
491
+ }
342
492
  console.log(
343
493
  `\x1b[32m✔ Paired\x1b[0m with "${session.deviceName || session.deviceId}" (${session.platform || 'browser'})`,
344
494
  );
@@ -350,11 +500,16 @@ function helpCommand() {
350
500
  \x1b[1mKroki CLI\x1b[0m — Browser Automation & AI Agent Bridge for Google Chrome
351
501
 
352
502
  \x1b[1mUsage:\x1b[0m
353
- npx kroki pair <CODE> Pair with a browser profile (from https://kroki.ai/#/devices)
354
- npx kroki "<task>" Send an AI task to the live browser
355
- npx kroki run "<workflow>" [json] Execute a saved workflow
356
- npx kroki status View paired profile and status
357
- npx kroki clear Disconnect active pairing session
503
+ npx kroki "<task>" [options] Send an AI task to the live browser
504
+ npx kroki run "<workflow>" [json] [opts] Execute a saved workflow
505
+ npx kroki pair <CODE> Pair with a browser profile (from https://kroki.ai/#/devices)
506
+ npx kroki status [options] View paired profile and status
507
+ npx kroki clear Disconnect active pairing session
508
+
509
+ \x1b[1mOptions:\x1b[0m
510
+ -v, --verbose Show real-time execution steps and logs
511
+ -j, --json Output result as JSON
512
+ -h, --help Show help
358
513
 
359
514
  \x1b[1mDashboard:\x1b[0m
360
515
  https://kroki.ai/#/devices
@@ -364,24 +519,38 @@ function helpCommand() {
364
519
  // ── Entry Point ─────────────────────────────────────────────────────────────
365
520
 
366
521
  const rawArgs = process.argv.slice(2);
367
- const first = rawArgs[0] || 'help';
522
+ let verbose = false;
523
+ let json = false;
524
+ const filteredArgs = [];
525
+
526
+ for (const arg of rawArgs) {
527
+ if (arg === '--verbose' || arg === '-v') {
528
+ verbose = true;
529
+ } else if (arg === '--json' || arg === '-j') {
530
+ json = true;
531
+ } else {
532
+ filteredArgs.push(arg);
533
+ }
534
+ }
535
+
536
+ const first = filteredArgs[0] || 'help';
368
537
 
369
538
  switch (first) {
370
539
  case 'pair':
371
540
  case 'connect':
372
- pairCommand(rawArgs[1]).catch(e => {
541
+ pairCommand(filteredArgs[1]).catch(e => {
373
542
  console.error(`\x1b[31mError:\x1b[0m ${e.message}`);
374
543
  process.exit(1);
375
544
  });
376
545
  break;
377
546
 
378
547
  case 'status':
379
- statusCommand();
548
+ statusCommand({ json });
380
549
  break;
381
550
 
382
551
  case 'run':
383
552
  case 'workflow':
384
- workflowCommand(rawArgs[1], rawArgs[2]).catch(e => {
553
+ workflowCommand(filteredArgs[1], filteredArgs[2], { verbose, json }).catch(e => {
385
554
  console.error(`\x1b[31mError:\x1b[0m ${e.message}`);
386
555
  process.exit(1);
387
556
  });
@@ -399,10 +568,16 @@ switch (first) {
399
568
  helpCommand();
400
569
  break;
401
570
 
402
- default:
403
- taskCommand(rawArgs.join(' ')).catch(e => {
571
+ default: {
572
+ const taskText = filteredArgs.join(' ').trim();
573
+ if (!taskText) {
574
+ helpCommand();
575
+ break;
576
+ }
577
+ taskCommand(taskText, { verbose, json }).catch(e => {
404
578
  console.error(`\x1b[31mError:\x1b[0m ${e.message}`);
405
579
  process.exit(1);
406
580
  });
407
581
  break;
582
+ }
408
583
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kroki",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Kroki Browser Automation CLI — Control Chrome from terminal and AI agents",
5
5
  "keywords": [
6
6
  "kroki",