breakroom 2.1.5 → 2.1.6

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/setup.js +108 -29
  2. package/package.json +1 -1
package/bin/setup.js CHANGED
@@ -47,7 +47,7 @@ const banner = () => {
47
47
  };
48
48
 
49
49
  const CANDIDATE_FILES = [
50
- // Project-level agent/IDE configs
50
+ // Project-level agent/IDE configs (cwd)
51
51
  ['.env'],
52
52
  ['.env.local'],
53
53
  ['.cursor', 'mcp.json'],
@@ -81,24 +81,49 @@ const CANDIDATE_FILES = [
81
81
  ['CLAUDE.md'],
82
82
  ['.continuerc.json'],
83
83
  ['.aider.conf.yml'],
84
- ['.github', 'copilot-instructions.md'],
85
- ['.github', 'copilot-instructions', 'copilot-instructions.md'],
86
- // User-level agent/IDE configs
87
- [os.homedir(), '.hermes', 'config.yaml'],
88
- [os.homedir(), '.litellm', 'config.yaml'],
89
- [os.homedir(), '.claude', 'settings.json'],
90
- [os.homedir(), '.continue', 'config.json'],
91
- [os.homedir(), '.codex', 'config.json'],
92
- [os.homedir(), '.goose', 'config.yaml'],
93
- [os.homedir(), '.opencode.json'],
94
- [os.homedir(), '.opencode.jsonc'],
95
- [os.homedir(), '.config', 'opencode.json'],
96
- [os.homedir(), '.config', 'opencode.jsonc'],
97
- [os.homedir(), '.config', 'breakroom', 'config.yaml'],
98
- [os.homedir(), '.config', 'openclaw', 'config.yaml'],
99
- [os.homedir(), '.openclaw', 'config.yaml'],
100
- [os.homedir(), '.config', 'trae', 'config.json'],
101
- [os.homedir(), '.config', 'codex', 'config.json'],
84
+ // User-level agent/IDE configs (~/ = home dir)
85
+ ['~', '.env'],
86
+ ['~', '.env.local'],
87
+ ['~', '.cursor', 'mcp.json'],
88
+ ['~', '.cursor', 'settings.json'],
89
+ ['~', '.vscode', 'settings.json'],
90
+ ['~', '.vscode', 'mcp.json'],
91
+ ['~', '.windsurf', 'settings.json'],
92
+ ['~', '.trae', 'settings.json'],
93
+ ['~', '.trae', 'mcp.json'],
94
+ ['~', '.codex', 'config.json'],
95
+ ['~', '.codex', 'setup.json'],
96
+ ['~', '.opencode.json'],
97
+ ['~', '.opencode.jsonc'],
98
+ ['~', '.openclaw', 'config.yaml'],
99
+ ['~', '.openclaw', 'config.yml'],
100
+ ['~', '.cline', 'cline.json'],
101
+ ['~', '.cline', 'settings.json'],
102
+ ['~', '.clinerules'],
103
+ ['~', '.cody.json'],
104
+ ['~', '.tabby', 'config.json'],
105
+ ['~', '.amazonq', 'config.json'],
106
+ ['~', '.supermaven', 'config.json'],
107
+ ['~', '.augment', 'config.json'],
108
+ ['~', '.hermes', 'config.yaml'],
109
+ ['~', 'litellm.yaml'],
110
+ ['~', 'litellm.yml'],
111
+ ['~', 'CLAUDE.md'],
112
+ ['~', '.continuerc.json'],
113
+ ['~', '.aider.conf.yml'],
114
+ ['~', '.cursorrules'],
115
+ ['~', '.windsurfrules'],
116
+ ['~', '.github', 'copilot-instructions.md'],
117
+ ['~', '.claude', 'settings.json'],
118
+ ['~', '.continue', 'config.json'],
119
+ ['~', '.goose', 'config.yaml'],
120
+ ['~', '.codex', 'config.json'],
121
+ ['~', '.config', 'opencode.json'],
122
+ ['~', '.config', 'opencode.jsonc'],
123
+ ['~', '.config', 'breakroom', 'config.yaml'],
124
+ ['~', '.config', 'openclaw', 'config.yaml'],
125
+ ['~', '.config', 'trae', 'config.json'],
126
+ ['~', '.config', 'codex', 'config.json'],
102
127
  ];
103
128
 
104
129
  function candidateFiles() {
@@ -690,6 +715,16 @@ async function actionRotate() {
690
715
  console.log();
691
716
  }
692
717
 
718
+ function extractLicenseKey(configured) {
719
+ for (const c of configured) {
720
+ for (const l of c.proxyLines) {
721
+ const m = l.match(/\/breakroom\/([^\/]+)\/v1/);
722
+ if (m) return decodeURIComponent(m[1]);
723
+ }
724
+ }
725
+ return '';
726
+ }
727
+
693
728
  async function actionTestIntervention() {
694
729
  console.log();
695
730
 
@@ -699,15 +734,7 @@ async function actionTestIntervention() {
699
734
  return;
700
735
  }
701
736
 
702
- // Extract license key from first configured file
703
- let licenseKey = '';
704
- for (const c of configured) {
705
- for (const l of c.proxyLines) {
706
- const m = l.match(/\/breakroom\/([^\/]+)\/v1/);
707
- if (m) { licenseKey = decodeURIComponent(m[1]); break; }
708
- }
709
- if (licenseKey) break;
710
- }
737
+ const licenseKey = extractLicenseKey(configured);
711
738
  if (!licenseKey) {
712
739
  console.log('Could not extract license key from config.\n');
713
740
  return;
@@ -751,6 +778,54 @@ async function actionTestIntervention() {
751
778
  }
752
779
  }
753
780
 
781
+ async function actionAnalytics() {
782
+ console.log();
783
+
784
+ const configured = scanExistingConfig();
785
+ if (!configured.length) {
786
+ console.log('No Break Room proxy configured. Use option 1 first.\n');
787
+ return;
788
+ }
789
+
790
+ const licenseKey = extractLicenseKey(configured);
791
+ if (!licenseKey) {
792
+ console.log('Could not extract license key from config.\n');
793
+ return;
794
+ }
795
+
796
+ console.log('Fetching intervention analytics...\n');
797
+
798
+ try {
799
+ const resp = await requestJson(`${API_ORIGIN}/breakroom/${encodeURIComponent(licenseKey)}/v1/analytics`);
800
+ if (resp.status !== 200 || !resp.json.ok) {
801
+ console.log(`\x1b[31mFailed: ${resp.json?.error || 'Unknown'}\x1b[0m\n`);
802
+ return;
803
+ }
804
+
805
+ const a = resp.json.analytics;
806
+ console.log(` Total calls analyzed: ${a.total_calls}`);
807
+ console.log(` Interventions fired: \x1b[33m${a.total_interventions}\x1b[0m`);
808
+ console.log(` Total tokens saved: ${a.tokens_saved.toLocaleString()}`);
809
+ console.log(` Total cost saved: \x1b[32m$${a.cost_saved.toFixed(2)}\x1b[0m`);
810
+
811
+ const providers = Object.keys(a.by_provider);
812
+ if (providers.length) {
813
+ console.log(`\n By provider:\n`);
814
+ for (const p of providers) {
815
+ const d = a.by_provider[p];
816
+ console.log(` ${p}:`);
817
+ console.log(` Calls: ${d.calls}`);
818
+ console.log(` Interventions: ${d.interventions}`);
819
+ console.log(` Tokens saved: ${d.tokens_saved.toLocaleString()}`);
820
+ console.log(` Cost saved: \x1b[32m$${d.cost_saved.toFixed(2)}\x1b[0m`);
821
+ }
822
+ }
823
+ console.log();
824
+ } catch (err) {
825
+ console.log(`\x1b[31mError: ${err.message}\x1b[0m\n`);
826
+ }
827
+ }
828
+
754
829
  // --- Menu ---
755
830
 
756
831
  function showMenu() {
@@ -765,6 +840,7 @@ function showMenu() {
765
840
  console.log(' 7) Recover lost license');
766
841
  console.log(' 8) Scan for IDE/config/Docker files');
767
842
  console.log(' 9) Rotate license key (2FA)');
843
+ console.log(' b) View analytics');
768
844
  console.log(' 0) Exit\n');
769
845
  }
770
846
 
@@ -808,6 +884,9 @@ async function main() {
808
884
  case '9':
809
885
  await actionRotate();
810
886
  break;
887
+ case 'b':
888
+ await actionAnalytics();
889
+ break;
811
890
  case '0':
812
891
  console.log('\nGoodbye.\n');
813
892
  return;
@@ -818,7 +897,7 @@ async function main() {
818
897
  console.error(`\n\x1b[31mError:\x1b[0m ${err.message}\n`);
819
898
  }
820
899
 
821
- if (choice !== '0' && choice !== '__EOF__') {
900
+ if (!['0', 'a', 'b'].includes(choice) && choice !== '__EOF__') {
822
901
  if (await ask('Press Enter to return to the menu...') === '__EOF__') break;
823
902
  }
824
903
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "breakroom",
3
- "version": "2.1.5",
3
+ "version": "2.1.6",
4
4
  "description": "Paid-license proxy routing for agents that get stuck in loops.",
5
5
  "bin": {
6
6
  "breakroom": "./bin/setup.js"