glidercli 0.1.4 β†’ 0.2.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/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  <div align="center">
2
2
 
3
- <img src="https://res.cloudinary.com/ddyc1es5v/image/upload/v1768050242/gh-repos/glidercli/code.png" alt="logo" width="80" height="80" />
4
- <img src="https://res.cloudinary.com/ddyc1es5v/image/upload/v1768050244/gh-repos/glidercli/github.png" alt="logo" width="80" height="80" />
3
+ <img src="assets/icons/glider-blue-squircle.webp" alt="glider" width="80" height="80" />
4
+ <img src="assets/icons/claude.webp" alt="claude" width="80" height="80" />
5
+ <img src="assets/icons/ralph-wiggum.webp" alt="ralph" width="80" height="80" />
5
6
 
6
7
  <h1 align="center">glidercli</h1>
7
8
  <p align="center"><i><b>Browser automation CLI with autonomous loop execution.</b></i></p>
@@ -9,8 +10,6 @@
9
10
  [![Github][github]][github-url]
10
11
  [![npm][npm]][npm-url]
11
12
 
12
- <img src="https://res.cloudinary.com/ddyc1es5v/image/upload/v1768050244/gh-repos/glidercli/social-preview.png" />
13
-
14
13
  </div>
15
14
 
16
15
  <br/>
@@ -47,9 +46,12 @@ npm i -g glidercli
47
46
 
48
47
  ### Requirements
49
48
 
50
- - Node 18+
51
- - Chrome with Glider extension
52
- - bserve relay server
49
+ 1. **Node 18+**
50
+
51
+ 2. **Glider Chrome Extension** - [Source](https://github.com/vdutts7/glider) *(Chrome Web Store pending approval)*
52
+ - For now: clone repo, load unpacked in `chrome://extensions`
53
+
54
+ 3. **bserve relay server** - included with extension, auto-starts
53
55
 
54
56
  ## πŸš€Usage
55
57
 
@@ -101,6 +103,7 @@ steps:
101
103
 
102
104
  ## πŸ”§Tools Used
103
105
 
106
+ [![Claude][claude-badge]][claude-url]
104
107
  [![Node.js][nodejs-badge]][nodejs-url]
105
108
  [![Chrome DevTools Protocol][cdp-badge]][cdp-url]
106
109
 
@@ -114,6 +117,8 @@ steps:
114
117
  [github-url]: https://github.com/vdutts7/glidercli
115
118
  [npm]: https://img.shields.io/badge/npm-glidercli-CB3837?style=for-the-badge&logo=npm
116
119
  [npm-url]: https://www.npmjs.com/package/glidercli
120
+ [claude-badge]: https://img.shields.io/badge/Claude-D97757?style=for-the-badge&logo=anthropic&logoColor=white
121
+ [claude-url]: https://claude.ai
117
122
  [nodejs-badge]: https://img.shields.io/badge/Node.js-339933?style=for-the-badge&logo=nodedotjs&logoColor=white
118
123
  [nodejs-url]: https://nodejs.org
119
124
  [cdp-badge]: https://img.shields.io/badge/Chrome_DevTools_Protocol-4285F4?style=for-the-badge&logo=googlechrome&logoColor=white
package/bin/glider.js CHANGED
@@ -32,10 +32,25 @@ const YAML = require('yaml');
32
32
  // Config
33
33
  const PORT = process.env.GLIDER_PORT || 19988;
34
34
  const SERVER_URL = `http://127.0.0.1:${PORT}`;
35
- const SCRIPTS_DIR = process.env.SCRIPTS || path.join(os.homedir(), 'scripts');
35
+ const LIB_DIR = path.join(__dirname, '..', 'lib');
36
36
  const STATE_FILE = '/tmp/glider-state.json';
37
37
  const LOG_FILE = '/tmp/glider.log';
38
38
 
39
+ // Domain extensions - load from ~/.cursor/glider/domains.json or ~/.glider/domains.json
40
+ const DOMAIN_CONFIG_PATHS = [
41
+ path.join(os.homedir(), '.cursor', 'glider', 'domains.json'),
42
+ path.join(os.homedir(), '.glider', 'domains.json'),
43
+ ];
44
+ let DOMAINS = {};
45
+ for (const cfgPath of DOMAIN_CONFIG_PATHS) {
46
+ if (fs.existsSync(cfgPath)) {
47
+ try {
48
+ DOMAINS = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
49
+ break;
50
+ } catch (e) { /* ignore parse errors */ }
51
+ }
52
+ }
53
+
39
54
  // Colors
40
55
  const RED = '\x1b[31m';
41
56
  const GREEN = '\x1b[32m';
@@ -198,7 +213,7 @@ async function cmdStart() {
198
213
  }
199
214
 
200
215
  log.info('Starting glider server...');
201
- const bserve = path.join(SCRIPTS_DIR, 'bserve');
216
+ const bserve = path.join(LIB_DIR, 'bserve.js');
202
217
 
203
218
  if (!fs.existsSync(bserve)) {
204
219
  log.fail(`bserve not found at ${bserve}`);
@@ -386,6 +401,151 @@ async function cmdText() {
386
401
  }
387
402
  }
388
403
 
404
+ // ═══════════════════════════════════════════════════════════════════
405
+ // NEW COMMANDS: restart, test, tabs, domains, open, html, title, url
406
+ // ═══════════════════════════════════════════════════════════════════
407
+
408
+ async function cmdRestart() {
409
+ await cmdStop();
410
+ await new Promise(r => setTimeout(r, 500));
411
+ await cmdStart();
412
+ }
413
+
414
+ async function cmdTest() {
415
+ showBanner();
416
+ console.log('═══════════════════════════════════════');
417
+ console.log(' GLIDER TEST');
418
+ console.log('═══════════════════════════════════════');
419
+
420
+ // Test 1: Server
421
+ const serverOk = await checkServer();
422
+ console.log(serverOk ? `${GREEN}[1/4]${NC} Server: OK` : `${RED}[1/4]${NC} Server: FAIL`);
423
+ if (!serverOk) {
424
+ log.info('Starting server...');
425
+ await cmdStart();
426
+ }
427
+
428
+ // Test 2: Extension
429
+ const extOk = await checkExtension();
430
+ console.log(extOk ? `${GREEN}[2/4]${NC} Extension: OK` : `${RED}[2/4]${NC} Extension: NOT CONNECTED`);
431
+
432
+ // Test 3: Tab
433
+ const tabOk = await checkTab();
434
+ console.log(tabOk ? `${GREEN}[3/4]${NC} Tab: OK` : `${RED}[3/4]${NC} Tab: NO TABS`);
435
+
436
+ // Test 4: CDP command
437
+ if (tabOk) {
438
+ try {
439
+ const result = await httpPost('/cdp', {
440
+ method: 'Runtime.evaluate',
441
+ params: { expression: '1+1', returnByValue: true }
442
+ });
443
+ const cdpOk = result.result?.value === 2;
444
+ console.log(cdpOk ? `${GREEN}[4/4]${NC} CDP: OK` : `${RED}[4/4]${NC} CDP: FAIL`);
445
+ } catch {
446
+ console.log(`${RED}[4/4]${NC} CDP: FAIL`);
447
+ }
448
+ } else {
449
+ console.log(`${YELLOW}[4/4]${NC} CDP: SKIPPED (no tab)`);
450
+ }
451
+
452
+ console.log('═══════════════════════════════════════');
453
+ }
454
+
455
+ async function cmdTabs() {
456
+ const targets = await getTargets();
457
+ if (targets.length === 0) {
458
+ log.warn('No tabs connected');
459
+ return;
460
+ }
461
+ console.log(`${GREEN}${targets.length}${NC} tab(s) connected:\n`);
462
+ targets.forEach((t, i) => {
463
+ const url = t.targetInfo?.url || 'unknown';
464
+ const title = t.targetInfo?.title || '';
465
+ console.log(` ${CYAN}[${i + 1}]${NC} ${title}`);
466
+ console.log(` ${DIM}${url}${NC}`);
467
+ });
468
+ }
469
+
470
+ async function cmdDomains() {
471
+ const domainKeys = Object.keys(DOMAINS);
472
+ if (domainKeys.length === 0) {
473
+ log.warn('No domains configured');
474
+ log.info('Add domains to ~/.cursor/glider/domains.json or ~/.glider/domains.json');
475
+ return;
476
+ }
477
+ console.log(`${GREEN}${domainKeys.length}${NC} domain(s) configured:\n`);
478
+ for (const key of domainKeys) {
479
+ const d = DOMAINS[key];
480
+ const type = d.script ? 'script' : 'url';
481
+ const target = d.script || d.url || '';
482
+ console.log(` ${CYAN}${key}${NC} ${DIM}(${type})${NC}`);
483
+ if (d.description) console.log(` ${d.description}`);
484
+ console.log(` ${DIM}${target}${NC}`);
485
+ }
486
+ }
487
+
488
+ async function cmdOpen(url) {
489
+ if (!url) {
490
+ log.fail('Usage: glider open <url>');
491
+ process.exit(1);
492
+ }
493
+
494
+ // Open URL in default browser (not in connected tab)
495
+ const { exec } = require('child_process');
496
+ const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
497
+ exec(`${cmd} "${url}"`, (err) => {
498
+ if (err) {
499
+ log.fail(`Failed to open: ${err.message}`);
500
+ process.exit(1);
501
+ }
502
+ log.ok(`Opened: ${url}`);
503
+ });
504
+ }
505
+
506
+ async function cmdHtml(selector) {
507
+ try {
508
+ const expression = selector
509
+ ? `document.querySelector('${selector.replace(/'/g, "\\'")}')?.outerHTML || 'Element not found'`
510
+ : 'document.documentElement.outerHTML';
511
+
512
+ const result = await httpPost('/cdp', {
513
+ method: 'Runtime.evaluate',
514
+ params: { expression, returnByValue: true }
515
+ });
516
+ console.log(result.result?.value || '');
517
+ } catch (e) {
518
+ log.fail(`HTML extraction failed: ${e.message}`);
519
+ process.exit(1);
520
+ }
521
+ }
522
+
523
+ async function cmdTitle() {
524
+ try {
525
+ const result = await httpPost('/cdp', {
526
+ method: 'Runtime.evaluate',
527
+ params: { expression: 'document.title', returnByValue: true }
528
+ });
529
+ console.log(result.result?.value || '');
530
+ } catch (e) {
531
+ log.fail(`Title extraction failed: ${e.message}`);
532
+ process.exit(1);
533
+ }
534
+ }
535
+
536
+ async function cmdUrl() {
537
+ try {
538
+ const result = await httpPost('/cdp', {
539
+ method: 'Runtime.evaluate',
540
+ params: { expression: 'window.location.href', returnByValue: true }
541
+ });
542
+ console.log(result.result?.value || '');
543
+ } catch (e) {
544
+ log.fail(`URL extraction failed: ${e.message}`);
545
+ process.exit(1);
546
+ }
547
+ }
548
+
389
549
  // YAML Task Runner
390
550
  async function cmdRun(taskFile) {
391
551
  if (!taskFile || !fs.existsSync(taskFile)) {
@@ -650,19 +810,31 @@ ${YELLOW}SERVER:${NC}
650
810
  status Check server, extension, tabs
651
811
  start Start relay server
652
812
  stop Stop relay server
813
+ restart Stop then start relay server
814
+ test Run connectivity test
653
815
 
654
816
  ${YELLOW}NAVIGATION:${NC}
655
817
  goto <url> Navigate current tab to URL
818
+ open <url> Open URL in default browser
656
819
  eval <js> Execute JavaScript, return result
657
820
  click <selector> Click element
658
821
  type <sel> <text> Type into input
659
822
  screenshot [path] Take screenshot
823
+
824
+ ${YELLOW}PAGE INFO:${NC}
660
825
  text Get page text content
826
+ html [selector] Get page HTML (or element HTML)
827
+ title Get page title
828
+ url Get current URL
829
+ tabs List connected tabs
661
830
 
662
831
  ${YELLOW}AUTOMATION:${NC}
663
832
  run <task.yaml> Execute YAML task file
664
833
  loop <task> [opts] Run in Ralph Wiggum loop until complete
665
834
 
835
+ ${YELLOW}CONFIG:${NC}
836
+ domains List configured domain shortcuts
837
+
666
838
  ${YELLOW}LOOP OPTIONS:${NC}
667
839
  -n, --max-iterations N Max iterations (default: 10)
668
840
  -t, --timeout N Max runtime in seconds (default: 3600)
@@ -685,6 +857,7 @@ ${YELLOW}EXAMPLES:${NC}
685
857
  glider start
686
858
  glider goto "https://google.com"
687
859
  glider eval "document.title"
860
+ glider html "div.main"
688
861
  glider run mytask.yaml
689
862
  glider loop mytask.yaml -n 20 -t 600
690
863
 
@@ -697,9 +870,29 @@ ${YELLOW}RALPH WIGGUM PATTERN:${NC}
697
870
 
698
871
  ${YELLOW}REQUIREMENTS:${NC}
699
872
  - Node.js 18+
700
- - bserve relay server (~/scripts/bserve)
701
873
  - Glider Chrome extension connected
874
+
875
+ ${YELLOW}DOMAIN EXTENSIONS:${NC}
876
+ Add custom domain commands via ~/.cursor/glider/domains.json:
877
+ {
878
+ "mysite": { "url": "https://mysite.com/dashboard" },
879
+ "mytool": { "script": "~/.cursor/tools/scripts/mytool.sh" }
880
+ }
881
+ Then: glider mysite -> navigates to that URL
882
+ glider mytool -> runs that script
702
883
  `);
884
+
885
+ // Show loaded domains if any
886
+ const domainKeys = Object.keys(DOMAINS);
887
+ if (domainKeys.length > 0) {
888
+ console.log(`${YELLOW}LOADED DOMAINS:${NC} (from config)`);
889
+ for (const key of domainKeys) {
890
+ const d = DOMAINS[key];
891
+ const desc = d.description || d.url || d.script || '';
892
+ console.log(` ${GREEN}${key}${NC} ${DIM}${desc}${NC}`);
893
+ }
894
+ console.log('');
895
+ }
703
896
  }
704
897
 
705
898
  // Main
@@ -730,10 +923,25 @@ async function main() {
730
923
  case 'stop':
731
924
  await cmdStop();
732
925
  break;
926
+ case 'restart':
927
+ await cmdRestart();
928
+ break;
929
+ case 'test':
930
+ await cmdTest();
931
+ break;
932
+ case 'tabs':
933
+ await cmdTabs();
934
+ break;
935
+ case 'domains':
936
+ await cmdDomains();
937
+ break;
733
938
  case 'goto':
734
939
  case 'navigate':
735
940
  await cmdGoto(args[1]);
736
941
  break;
942
+ case 'open':
943
+ await cmdOpen(args[1]);
944
+ break;
737
945
  case 'eval':
738
946
  case 'js':
739
947
  await cmdEval(args.slice(1).join(' '));
@@ -750,6 +958,15 @@ async function main() {
750
958
  case 'text':
751
959
  await cmdText();
752
960
  break;
961
+ case 'html':
962
+ await cmdHtml(args[1]);
963
+ break;
964
+ case 'title':
965
+ await cmdTitle();
966
+ break;
967
+ case 'url':
968
+ await cmdUrl();
969
+ break;
753
970
  case 'run':
754
971
  await cmdRun(args[1]);
755
972
  break;
@@ -773,6 +990,29 @@ async function main() {
773
990
  await cmdLoop(taskArg, loopOpts);
774
991
  break;
775
992
  default:
993
+ // Check if it's a domain command from config
994
+ if (DOMAINS[cmd]) {
995
+ const domain = DOMAINS[cmd];
996
+ if (domain.script) {
997
+ // Execute external script
998
+ const scriptPath = domain.script.replace(/^~/, os.homedir());
999
+ if (fs.existsSync(scriptPath)) {
1000
+ const { execSync } = require('child_process');
1001
+ try {
1002
+ execSync(`"${scriptPath}" ${args.slice(1).map(a => `"${a}"`).join(' ')}`, { stdio: 'inherit' });
1003
+ } catch (e) {
1004
+ process.exit(e.status || 1);
1005
+ }
1006
+ } else {
1007
+ log.fail(`Domain script not found: ${scriptPath}`);
1008
+ process.exit(1);
1009
+ }
1010
+ } else if (domain.url) {
1011
+ // Navigate to domain URL
1012
+ await cmdGoto(domain.url);
1013
+ }
1014
+ break;
1015
+ }
776
1016
  log.fail(`Unknown command: ${cmd}`);
777
1017
  showHelp();
778
1018
  process.exit(1);