bypass-vpn 1.0.1 → 1.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,6 +1,6 @@
1
1
  # bypass-vpn
2
2
 
3
- Route AI service traffic (Claude, ChatGPT, Firebase, Google Auth) through your Wi-Fi gateway to bypass VPN routing.
3
+ Route AI and work service traffic (Claude, ChatGPT, Atlassian, Firebase, Google Auth) through your Wi-Fi gateway to bypass VPN routing.
4
4
 
5
5
  Works on **macOS** and **Windows**. Zero dependencies.
6
6
 
@@ -23,7 +23,7 @@ npx bypass-vpn
23
23
  ## Usage
24
24
 
25
25
  ```bash
26
- # Route all AI services through Wi-Fi
26
+ # Route all services through Wi-Fi
27
27
  sudo bypass-vpn
28
28
 
29
29
  # Route specific services only
@@ -39,6 +39,23 @@ sudo bypass-vpn --dry-run
39
39
  bypass-vpn --list
40
40
  ```
41
41
 
42
+ ### Custom Domains
43
+
44
+ Save your own domains (e.g., your company's Jira instance) so they're automatically routed on every run:
45
+
46
+ ```bash
47
+ # One-time setup
48
+ bypass-vpn --add-domain mycompany.atlassian.net
49
+
50
+ # Now just run normally — saved domains are included automatically
51
+ sudo bypass-vpn
52
+
53
+ # Remove a saved domain
54
+ bypass-vpn --remove-domain mycompany.atlassian.net
55
+ ```
56
+
57
+ Custom domains are persisted in `~/.bypass-vpn.json`.
58
+
42
59
  ## Supported Services
43
60
 
44
61
  | Service | Domains |
@@ -47,6 +64,7 @@ bypass-vpn --list
47
64
  | ChatGPT | chatgpt.com, chat.openai.com, api.openai.com, + 5 more |
48
65
  | Firebase | firestore.googleapis.com, securetoken.googleapis.com, + 3 more |
49
66
  | Google Auth | accounts.google.com, oauth2.googleapis.com, + 2 more |
67
+ | Atlassian | api.atlassian.com, auth.atlassian.com, id.atlassian.com |
50
68
 
51
69
  Run `bypass-vpn --list` for the full domain list.
52
70
 
package/bin/bypass-vpn.js CHANGED
@@ -6,6 +6,7 @@ const { detect } = require('../src/gateway');
6
6
  const { resolveAll } = require('../src/resolver');
7
7
  const { addRoute, removeRoute } = require('../src/router');
8
8
  const services = require('../src/services');
9
+ const { loadConfig, addDomain, removeDomain } = require('../src/config');
9
10
  const { version } = require('../package.json');
10
11
 
11
12
  // ── Arg parsing ────────────────────────────────────────────────
@@ -18,12 +19,20 @@ const flags = {
18
19
  dryRun: args.includes('--dry-run'),
19
20
  list: args.includes('--list'),
20
21
  services: [],
22
+ addDomain: null,
23
+ removeDomain: null,
21
24
  };
22
25
 
23
26
  for (let i = 0; i < args.length; i++) {
24
27
  if (args[i] === '--service' && args[i + 1]) {
25
28
  flags.services.push(args[i + 1].toLowerCase());
26
29
  i++;
30
+ } else if (args[i] === '--add-domain' && args[i + 1]) {
31
+ flags.addDomain = args[i + 1];
32
+ i++;
33
+ } else if (args[i] === '--remove-domain' && args[i + 1]) {
34
+ flags.removeDomain = args[i + 1];
35
+ i++;
27
36
  }
28
37
  }
29
38
 
@@ -41,6 +50,8 @@ if (flags.help) {
41
50
  bypass-vpn ${c.dim('--dry-run')} Show commands without executing
42
51
  bypass-vpn ${c.dim('--service claude')} Route specific service(s) only
43
52
  bypass-vpn ${c.dim('--list')} List available services
53
+ bypass-vpn ${c.dim('--add-domain')} h Save a custom domain for routing
54
+ bypass-vpn ${c.dim('--remove-domain')} h Remove a saved custom domain
44
55
 
45
56
  ${c.bold('Flags:')}
46
57
  -h, --help Show this help
@@ -49,12 +60,16 @@ if (flags.help) {
49
60
  --dry-run Print route commands without executing
50
61
  --service <name> Route only specified service (repeatable)
51
62
  --list List services and their domains
63
+ --add-domain <host> Save a custom domain (persisted in ~/.bypass-vpn.json)
64
+ --remove-domain <h> Remove a saved custom domain
52
65
 
53
- ${c.bold('Services:')} claude, chatgpt, firebase, googleauth
66
+ ${c.bold('Services:')} claude, chatgpt, firebase, googleauth, atlassian
54
67
 
55
68
  ${c.bold('Examples:')}
56
69
  sudo npx bypass-vpn
57
70
  sudo bypass-vpn --service claude --service chatgpt
71
+ bypass-vpn --add-domain mycompany.atlassian.net
72
+ sudo bypass-vpn
58
73
  sudo bypass-vpn --remove
59
74
  `);
60
75
  process.exit(0);
@@ -67,6 +82,27 @@ if (flags.version) {
67
82
  process.exit(0);
68
83
  }
69
84
 
85
+ // ── Add / Remove domain ───────────────────────────────────────
86
+
87
+ const DOMAIN_RE = /^[a-zA-Z0-9.-]+$/;
88
+
89
+ if (flags.addDomain) {
90
+ if (!DOMAIN_RE.test(flags.addDomain)) {
91
+ console.error(c.red(` Invalid domain: ${flags.addDomain}`));
92
+ process.exit(1);
93
+ }
94
+ addDomain(flags.addDomain);
95
+ console.log(` ${c.green('Saved:')} ${flags.addDomain}`);
96
+ console.log(` ${c.dim('This domain will be routed on every run.')}`);
97
+ process.exit(0);
98
+ }
99
+
100
+ if (flags.removeDomain) {
101
+ removeDomain(flags.removeDomain);
102
+ console.log(` ${c.green('Removed:')} ${flags.removeDomain}`);
103
+ process.exit(0);
104
+ }
105
+
70
106
  // ── List ───────────────────────────────────────────────────────
71
107
 
72
108
  if (flags.list) {
@@ -78,6 +114,14 @@ if (flags.list) {
78
114
  }
79
115
  console.log('');
80
116
  }
117
+ const config = loadConfig();
118
+ if (config.domains.length > 0) {
119
+ console.log(` ${c.bold('Custom Domains')} ${c.dim('(saved via --add-domain)')}`);
120
+ for (const d of config.domains) {
121
+ console.log(` ${c.dim('-')} ${d}`);
122
+ }
123
+ console.log('');
124
+ }
81
125
  process.exit(0);
82
126
  }
83
127
 
@@ -114,7 +158,16 @@ async function main() {
114
158
  selectedServices[key] = services[key];
115
159
  }
116
160
  } else {
117
- selectedServices = services;
161
+ selectedServices = { ...services };
162
+ }
163
+
164
+ // Inject saved custom domains
165
+ const config = loadConfig();
166
+ if (config.domains.length > 0) {
167
+ selectedServices.custom = {
168
+ name: 'Custom Domains',
169
+ domains: config.domains,
170
+ };
118
171
  }
119
172
 
120
173
  // Process each service
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bypass-vpn",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "Route AI service traffic (Claude, ChatGPT, Firebase) through Wi-Fi gateway to bypass VPN",
5
5
  "bin": {
6
6
  "bypass-vpn": "bin/bypass-vpn.js"
package/src/config.js ADDED
@@ -0,0 +1,35 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ const CONFIG_PATH = path.join(os.homedir(), '.bypass-vpn.json');
6
+
7
+ function loadConfig() {
8
+ try {
9
+ const data = fs.readFileSync(CONFIG_PATH, 'utf8');
10
+ const config = JSON.parse(data);
11
+ return { domains: Array.isArray(config.domains) ? config.domains : [] };
12
+ } catch {
13
+ return { domains: [] };
14
+ }
15
+ }
16
+
17
+ function saveConfig(config) {
18
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n');
19
+ }
20
+
21
+ function addDomain(domain) {
22
+ const config = loadConfig();
23
+ if (!config.domains.includes(domain)) {
24
+ config.domains.push(domain);
25
+ saveConfig(config);
26
+ }
27
+ }
28
+
29
+ function removeDomain(domain) {
30
+ const config = loadConfig();
31
+ config.domains = config.domains.filter((d) => d !== domain);
32
+ saveConfig(config);
33
+ }
34
+
35
+ module.exports = { loadConfig, addDomain, removeDomain };
package/src/resolver.js CHANGED
@@ -1,12 +1,39 @@
1
+ const { execSync } = require('child_process');
1
2
  const dns = require('dns');
2
3
  const dnsPromises = dns.promises;
3
4
 
5
+ /**
6
+ * Resolve a domain using `dig` — works outside VPN tunnel.
7
+ * Falls back to Node.js DNS if `dig` is unavailable.
8
+ */
9
+ function resolveDomain(domain) {
10
+ try {
11
+ const output = execSync(`dig +short +time=3 "${domain}"`, {
12
+ timeout: 5000,
13
+ encoding: 'utf8',
14
+ });
15
+ const ips = output
16
+ .split('\n')
17
+ .map((line) => line.trim())
18
+ .filter((line) => /^\d+\.\d+\.\d+\.\d+$/.test(line));
19
+ if (ips.length > 0) return ips;
20
+ } catch {
21
+ // dig not available or failed — fall through to Node DNS
22
+ }
23
+ return null;
24
+ }
25
+
4
26
  async function resolveAll(domains) {
5
27
  const resolved = new Map();
6
28
  const failed = [];
7
29
 
8
30
  const results = await Promise.allSettled(
9
31
  domains.map(async (domain) => {
32
+ // Try dig first (bypasses VPN DNS interception)
33
+ const digIps = resolveDomain(domain);
34
+ if (digIps) return { domain, ips: digIps };
35
+
36
+ // Fallback to Node.js built-in DNS
10
37
  const ips = await withTimeout(dnsPromises.resolve4(domain), 5000);
11
38
  return { domain, ips };
12
39
  })
@@ -21,7 +48,6 @@ async function resolveAll(domains) {
21
48
  failed.push(domain);
22
49
  }
23
50
  } else {
24
- // Extract domain from the original array by index
25
51
  const idx = results.indexOf(result);
26
52
  failed.push(domains[idx]);
27
53
  }
package/src/services.js CHANGED
@@ -35,4 +35,12 @@ module.exports = {
35
35
  'iamcredentials.googleapis.com',
36
36
  ],
37
37
  },
38
+ atlassian: {
39
+ name: 'Atlassian',
40
+ domains: [
41
+ 'api.atlassian.com',
42
+ 'auth.atlassian.com',
43
+ 'id.atlassian.com',
44
+ ],
45
+ },
38
46
  };