repoburg 1.3.163 → 1.3.166

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 (27) hide show
  1. package/backend/.env +1 -0
  2. package/backend/dist/src/application-state/application-state.service.js +3 -3
  3. package/backend/dist/src/application-state/application-state.service.js.map +1 -1
  4. package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.d.ts +2 -1
  5. package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.js.map +1 -1
  6. package/backend/dist/src/mcp-server/mcp-internal.controller.d.ts +3 -11
  7. package/backend/dist/src/mcp-server/mcp-internal.controller.js.map +1 -1
  8. package/backend/dist/src/mcp-server/mcp-server.bootstrap.js +9 -7
  9. package/backend/dist/src/mcp-server/mcp-server.bootstrap.js.map +1 -1
  10. package/backend/dist/src/mcp-server/mcp-server.constants.d.ts +1 -0
  11. package/backend/dist/src/mcp-server/mcp-server.constants.js.map +1 -1
  12. package/backend/dist/src/mcp-server/mcp-server.controller.d.ts +1 -9
  13. package/backend/dist/src/mcp-server/mcp-server.module.js +1 -5
  14. package/backend/dist/src/mcp-server/mcp-server.module.js.map +1 -1
  15. package/backend/dist/src/mcp-server/mcp-server.process.service.js.map +1 -1
  16. package/backend/dist/src/mcp-server/mcp-server.service.d.ts +9 -13
  17. package/backend/dist/src/mcp-server/mcp-server.service.js +27 -24
  18. package/backend/dist/src/mcp-server/mcp-server.service.js.map +1 -1
  19. package/backend/dist/src/tool-hooks/hook-context.interface.d.ts +2 -1
  20. package/backend/dist/src/utils/tool-schema-converter.d.ts +8 -3
  21. package/backend/dist/src/utils/tool-schema-converter.js.map +1 -1
  22. package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
  23. package/daemon/dist/mcp-gateway.js +38 -18
  24. package/daemon/dist/mcp-gateway.spec.js +295 -0
  25. package/daemon/package.json +15 -2
  26. package/package.json +1 -1
  27. package/platform-cli.js +264 -1
package/platform-cli.js CHANGED
@@ -5,7 +5,8 @@ const axios = require('axios');
5
5
  const path = require('path');
6
6
  const fs = require('fs/promises');
7
7
  const packageJson = require('./package.json');
8
- const { spawn } = require('child_process');
8
+ const { spawn, spawnSync } = require('child_process');
9
+ const os = require('os');
9
10
 
10
11
  const DAEMON_BASE_URL = 'http://localhost:9998';
11
12
 
@@ -517,4 +518,266 @@ program
517
518
  }
518
519
  });
519
520
 
521
+ // ===== Tunnel Commands =====
522
+
523
+ const TUNNEL_CONFIG_DIR = path.join(os.homedir(), '.config', 'tunnel-client');
524
+ const DEFAULT_MCP_URL = 'http://localhost:9998/mcp';
525
+ const TUNNEL_CLIENT_INSTALL_URL = 'https://github.com/openai/tunnel-client/releases/latest';
526
+
527
+ const getTunnelConfigPath = (profile) => path.join(TUNNEL_CONFIG_DIR, `${profile}.yaml`);
528
+
529
+ const generateTunnelYaml = (tunnelId, apiKeyRef, mcpUrl) => {
530
+ return `config_version: 1
531
+ control_plane:
532
+ base_url: "https://api.openai.com"
533
+
534
+ tunnel_id: "${tunnelId}"
535
+ api_key: "${apiKeyRef}"
536
+ health:
537
+ listen_addr: "127.0.0.1:8080"
538
+ admin_ui:
539
+ open_browser: false
540
+ log:
541
+ level: info
542
+ format: json
543
+ mcp:
544
+ server_urls:
545
+ - channel: main
546
+ url: "${mcpUrl}"
547
+ `;
548
+ };
549
+
550
+ /** Checks if the tunnel-client binary is on PATH. */
551
+ const validateTunnelBinary = () => {
552
+ const result = spawnSync('tunnel-client', ['--version'], { stdio: 'pipe' });
553
+ return !result.error;
554
+ };
555
+
556
+ const tunnelCmd = program.command('tunnel').description('Manage MCP tunnel profiles for ChatGPT/OpenAI connectivity.');
557
+
558
+ tunnelCmd
559
+ .command('create')
560
+ .description('Create a new tunnel profile.')
561
+ .requiredOption('-p, --profile <name>', 'Profile name')
562
+ .option('-t, --tunnel-id <id>', 'Tunnel ID from OpenAI Platform')
563
+ .option('-k, --api-key <key>', 'API key (defaults to CONTROL_PLANE_API_KEY env var)')
564
+ .option('-m, --mcp-url <url>', 'MCP server URL', DEFAULT_MCP_URL)
565
+ .action(async (options) => {
566
+ const { default: chalk } = await import('chalk');
567
+
568
+ if (!options.tunnelId) {
569
+ console.error(chalk.red('Missing required option: --tunnel-id.'));
570
+ console.error(chalk.gray('Get your tunnel ID from https://platform.openai.com/settings/organization/tunnels'));
571
+ process.exit(1);
572
+ }
573
+
574
+ const apiKeyRef = options.apiKey
575
+ ? options.apiKey
576
+ : process.env.CONTROL_PLANE_API_KEY
577
+ ? 'env:CONTROL_PLANE_API_KEY'
578
+ : null;
579
+
580
+ if (!apiKeyRef) {
581
+ console.error(chalk.red('Missing API key.'));
582
+ console.error(chalk.yellow('Set the CONTROL_PLANE_API_KEY env var or use --api-key.'));
583
+ console.error(chalk.gray('Create a key at https://platform.openai.com/settings/organization/api-keys'));
584
+ process.exit(1);
585
+ }
586
+
587
+ const configPath = getTunnelConfigPath(options.profile);
588
+
589
+ // Warn if overwriting an existing profile.
590
+ try {
591
+ await fs.access(configPath);
592
+ console.log(chalk.yellow(`Warning: Profile '${options.profile}' already exists. Overwriting.`));
593
+ } catch {
594
+ // File doesn't exist — fine.
595
+ }
596
+
597
+ await fs.mkdir(TUNNEL_CONFIG_DIR, { recursive: true });
598
+ const yaml = generateTunnelYaml(options.tunnelId, apiKeyRef, options.mcpUrl);
599
+ await fs.writeFile(configPath, yaml, 'utf-8');
600
+
601
+ console.log(chalk.green(`\u2713 Tunnel profile '${options.profile}' created.`));
602
+ console.log(chalk.gray(` Config: ${configPath}`));
603
+ console.log(chalk.cyan(` Run with: repoburg tunnel run --profile ${options.profile}`));
604
+ });
605
+
606
+ tunnelCmd
607
+ .command('delete <profile>')
608
+ .description('Delete a tunnel profile.')
609
+ .action(async (profile) => {
610
+ const { default: chalk } = await import('chalk');
611
+ const configPath = getTunnelConfigPath(profile);
612
+
613
+ try {
614
+ await fs.access(configPath);
615
+ } catch {
616
+ console.error(chalk.red(`Tunnel profile '${profile}' not found.`));
617
+ console.error(chalk.gray(` Expected at: ${configPath}`));
618
+ process.exit(1);
619
+ }
620
+
621
+ await fs.unlink(configPath);
622
+ console.log(chalk.green(`\u2713 Tunnel profile '${profile}' deleted.`));
623
+ });
624
+
625
+ tunnelCmd
626
+ .command('run <profile>')
627
+ .description('Run a tunnel profile (connects to ChatGPT/OpenAI).')
628
+ .action(async (profile) => {
629
+ const { default: chalk } = await import('chalk');
630
+
631
+ if (!validateTunnelBinary()) {
632
+ console.error(chalk.red('tunnel-client not found on PATH.'));
633
+ console.error(chalk.yellow(`Install it from ${TUNNEL_CLIENT_INSTALL_URL}`));
634
+ process.exit(1);
635
+ }
636
+
637
+ const configPath = getTunnelConfigPath(profile);
638
+
639
+ let configContent;
640
+ try {
641
+ configContent = await fs.readFile(configPath, 'utf-8');
642
+ } catch {
643
+ console.error(chalk.red(`Tunnel profile '${profile}' not found.`));
644
+ console.error(chalk.yellow(`Create one with: repoburg tunnel create --profile ${profile} --tunnel-id <id>`));
645
+ process.exit(1);
646
+ }
647
+
648
+ // Validate tunnel_id is present and non-empty.
649
+ const tunnelIdMatch = configContent.match(/tunnel_id:\s*"?([^\s"]+)"?/);
650
+ if (!tunnelIdMatch || !tunnelIdMatch[1]) {
651
+ console.error(chalk.red(`Config error: tunnel_id is missing or empty in profile '${profile}'.`));
652
+ console.error(chalk.yellow(`Recreate with: repoburg tunnel create --profile ${profile} --tunnel-id <id>`));
653
+ process.exit(1);
654
+ }
655
+
656
+ // Validate api_key is present and non-empty.
657
+ const apiKeyMatch = configContent.match(/api_key:\s*"?([^\s"]+)"?/);
658
+ if (!apiKeyMatch || !apiKeyMatch[1]) {
659
+ console.error(chalk.red(`Config error: api_key is missing or empty in profile '${profile}'.`));
660
+ console.error(chalk.yellow(`Recreate with: repoburg tunnel create --profile ${profile} --tunnel-id <id>`));
661
+ process.exit(1);
662
+ }
663
+
664
+ // If api_key references an env var, verify it's set.
665
+ if (apiKeyMatch[1].startsWith('env:')) {
666
+ const envVarName = apiKeyMatch[1].slice(4);
667
+ if (!process.env[envVarName]) {
668
+ console.error(chalk.red(`${envVarName} environment variable is not set.`));
669
+ console.error(chalk.yellow(`Export it first: export ${envVarName}=sk-...`));
670
+ process.exit(1);
671
+ }
672
+ }
673
+
674
+ console.log(chalk.cyan(`Starting tunnel-client with profile '${profile}'...`));
675
+ console.log(chalk.gray(` Config: ${configPath}`));
676
+
677
+ const child = spawn('tunnel-client', ['run', '--profile', profile], {
678
+ stdio: 'inherit',
679
+ shell: true,
680
+ env: { ...process.env },
681
+ });
682
+
683
+ child.on('error', (error) => {
684
+ console.error(chalk.red('Failed to start tunnel-client:'), error.message);
685
+ process.exit(1);
686
+ });
687
+
688
+ child.on('exit', (code) => {
689
+ if (code !== 0) {
690
+ console.log(chalk.yellow(`tunnel-client exited with code ${code}`));
691
+ }
692
+ process.exit(code || 0);
693
+ });
694
+ });
695
+
696
+ tunnelCmd
697
+ .command('list')
698
+ .description('List all tunnel profiles.')
699
+ .action(async () => {
700
+ const { default: chalk } = await import('chalk');
701
+ const Table = require('cli-table3');
702
+
703
+ let files;
704
+ try {
705
+ files = await fs.readdir(TUNNEL_CONFIG_DIR);
706
+ } catch {
707
+ console.log(chalk.yellow('No tunnel profiles found.'));
708
+ console.log(chalk.gray('Create one with: repoburg tunnel create --profile <name> --tunnel-id <id>'));
709
+ return;
710
+ }
711
+
712
+ const yamlFiles = files.filter(f => f.endsWith('.yaml'));
713
+ if (yamlFiles.length === 0) {
714
+ console.log(chalk.yellow('No tunnel profiles found.'));
715
+ console.log(chalk.gray('Create one with: repoburg tunnel create --profile <name> --tunnel-id <id>'));
716
+ return;
717
+ }
718
+
719
+ const table = new Table({
720
+ head: ['PROFILE', 'TUNNEL_ID', 'MCP_URL'].map(h => chalk.cyan(h)),
721
+ colWidths: [20, 35, 40]
722
+ });
723
+
724
+ for (const file of yamlFiles) {
725
+ const profileName = file.replace(/\.yaml$/, '');
726
+ const configPath = path.join(TUNNEL_CONFIG_DIR, file);
727
+ try {
728
+ const content = await fs.readFile(configPath, 'utf-8');
729
+ const tunnelIdMatch = content.match(/(?:^|\s)tunnel_id:\s*"?([^\s"]+)"?/);
730
+ const urlMatch = content.match(/(?:^|\s)url:\s*"?([^\s"]+)"?/);
731
+ table.push([
732
+ profileName,
733
+ tunnelIdMatch ? tunnelIdMatch[1] : chalk.gray('(missing)'),
734
+ urlMatch ? urlMatch[1] : chalk.gray('(missing)'),
735
+ ]);
736
+ } catch {
737
+ table.push([profileName, chalk.red('(read error)'), '-']);
738
+ }
739
+ }
740
+
741
+ console.log(table.toString());
742
+ });
743
+
744
+ tunnelCmd
745
+ .command('doctor <profile>')
746
+ .description('Run tunnel-client doctor on a profile.')
747
+ .action(async (profile) => {
748
+ const { default: chalk } = await import('chalk');
749
+
750
+ if (!validateTunnelBinary()) {
751
+ console.error(chalk.red('tunnel-client not found on PATH.'));
752
+ console.error(chalk.yellow(`Install it from ${TUNNEL_CLIENT_INSTALL_URL}`));
753
+ process.exit(1);
754
+ }
755
+
756
+ const configPath = getTunnelConfigPath(profile);
757
+ try {
758
+ await fs.access(configPath);
759
+ } catch {
760
+ console.error(chalk.red(`Tunnel profile '${profile}' not found.`));
761
+ console.error(chalk.yellow(`Create one with: repoburg tunnel create --profile ${profile} --tunnel-id <id>`));
762
+ process.exit(1);
763
+ }
764
+
765
+ console.log(chalk.cyan(`Running tunnel-client doctor for profile '${profile}'...`));
766
+
767
+ const child = spawn('tunnel-client', ['doctor', '--profile', profile, '--explain'], {
768
+ stdio: 'inherit',
769
+ shell: true,
770
+ env: { ...process.env },
771
+ });
772
+
773
+ child.on('error', (error) => {
774
+ console.error(chalk.red('Failed to run tunnel-client doctor:'), error.message);
775
+ process.exit(1);
776
+ });
777
+
778
+ child.on('exit', (code) => {
779
+ process.exit(code || 0);
780
+ });
781
+ });
782
+
520
783
  program.parse(process.argv);