breakroom 2.1.4 → 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 +164 -20
  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,117 @@ 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
+
728
+ async function actionTestIntervention() {
729
+ console.log();
730
+
731
+ const configured = scanExistingConfig();
732
+ if (!configured.length) {
733
+ console.log('No Break Room proxy configured. Use option 1 first.\n');
734
+ return;
735
+ }
736
+
737
+ const licenseKey = extractLicenseKey(configured);
738
+ if (!licenseKey) {
739
+ console.log('Could not extract license key from config.\n');
740
+ return;
741
+ }
742
+
743
+ console.log('Testing Break Room interventions...\n');
744
+
745
+ // Simulate a rumination session (repeated tool calls)
746
+ const toolCalls = [
747
+ { name: 'GlobTool', input: '**/*.ts', result: 'no results' },
748
+ { name: 'GlobTool', input: 'src/**/*.ts', result: 'no results' },
749
+ { name: 'GlobTool', input: '**/*config*', result: 'ENOENT: no config file' },
750
+ { name: 'BashTool', input: 'find . -name "*.ts"', result: 'ENOENT: no config' },
751
+ { name: 'GlobTool', input: '**/*.tsx', result: 'no results' },
752
+ ];
753
+
754
+ console.log('Simulated session: 5 tool calls, 4x GlobTool, 1x BashTool\n');
755
+
756
+ try {
757
+ const resp = await postJson(`${API_ORIGIN}/breakroom/${encodeURIComponent(licenseKey)}/v1/chat/completions`, {
758
+ breakroom_test: true,
759
+ tool_calls: toolCalls
760
+ });
761
+ if (resp.status !== 200 || !resp.json.ok) {
762
+ console.log(`\x1b[31mTest failed: ${resp.json?.error || 'Unknown'}\x1b[0m\n`);
763
+ return;
764
+ }
765
+
766
+ const ix = resp.json.intervention;
767
+ console.log(` Would intervene: ${ix.would_intervene ? '\x1b[32mYES\x1b[0m' : '\x1b[31mNO\x1b[0m'}`);
768
+ console.log(` Reason: ${ix.reason}`);
769
+ if (ix.intervention_type) console.log(` Intervention type: ${ix.intervention_type}`);
770
+ if (ix.message) console.log(` CBT message: "${ix.message}"`);
771
+ console.log(`\n Session stats:\n` +
772
+ ` Tool calls: ${ix.session_stats.total_tool_calls}\n` +
773
+ ` Unique tools: ${ix.session_stats.unique_tools}\n` +
774
+ ` Tokens saved: ${ix.session_stats.tokens_saved.toLocaleString()}`);
775
+ console.log();
776
+ } catch (err) {
777
+ console.log(`\x1b[31mError: ${err.message}\x1b[0m\n`);
778
+ }
779
+ }
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
+
693
829
  // --- Menu ---
694
830
 
695
831
  function showMenu() {
@@ -697,12 +833,14 @@ function showMenu() {
697
833
  console.log(' 1) Configure a license');
698
834
  console.log(' 2) Change license key');
699
835
  console.log(' 3) Verify current license');
836
+ console.log(' a) Verify interventions');
700
837
  console.log(' 4) Get a license');
701
838
  console.log(' 5) Revert patches (restore backups)');
702
839
  console.log(' 6) Check configuration status');
703
840
  console.log(' 7) Recover lost license');
704
841
  console.log(' 8) Scan for IDE/config/Docker files');
705
842
  console.log(' 9) Rotate license key (2FA)');
843
+ console.log(' b) View analytics');
706
844
  console.log(' 0) Exit\n');
707
845
  }
708
846
 
@@ -725,6 +863,9 @@ async function main() {
725
863
  case '3':
726
864
  await actionVerify();
727
865
  break;
866
+ case 'a':
867
+ await actionTestIntervention();
868
+ break;
728
869
  case '4':
729
870
  actionGetLicense();
730
871
  break;
@@ -743,6 +884,9 @@ async function main() {
743
884
  case '9':
744
885
  await actionRotate();
745
886
  break;
887
+ case 'b':
888
+ await actionAnalytics();
889
+ break;
746
890
  case '0':
747
891
  console.log('\nGoodbye.\n');
748
892
  return;
@@ -753,7 +897,7 @@ async function main() {
753
897
  console.error(`\n\x1b[31mError:\x1b[0m ${err.message}\n`);
754
898
  }
755
899
 
756
- if (choice !== '0' && choice !== '__EOF__') {
900
+ if (!['0', 'a', 'b'].includes(choice) && choice !== '__EOF__') {
757
901
  if (await ask('Press Enter to return to the menu...') === '__EOF__') break;
758
902
  }
759
903
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "breakroom",
3
- "version": "2.1.4",
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"