unbound-cli 0.1.7 → 0.1.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unbound-cli",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "CLI tool for Unbound - AI Gateway management",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1,3 +1,4 @@
1
+ const { Option } = require('commander');
1
2
  const config = require('../config');
2
3
  const output = require('../output');
3
4
  const api = require('../api');
@@ -8,7 +9,7 @@ function register(program) {
8
9
  .command('login')
9
10
  .description('Authenticate with Unbound. Opens a browser for interactive login, or use --api-key for CI/CD environments.')
10
11
  .option('--api-key <key>', 'Authenticate with an API key directly (non-interactive)')
11
- .option('--base-url <url>', 'Set a custom API base URL before logging in')
12
+ .addOption(new Option('--base-url <url>', 'Set a custom API base URL').hideHelp())
12
13
  .option('--domain <domain>', 'Use a custom domain for login (e.g. custom.example.com)')
13
14
  .addHelpText('after', `
14
15
  Authentication methods:
@@ -23,13 +24,11 @@ Authentication methods:
23
24
 
24
25
  Options:
25
26
  --domain sets a custom domain for organizations with self-hosted frontends.
26
- --base-url sets the backend API URL before authenticating (persisted).
27
27
 
28
28
  Examples:
29
29
  $ unbound login # Login via default gateway
30
30
  $ unbound login --domain custom.example.com # Login via custom domain
31
31
  $ unbound login --api-key sk-abc123 # Non-interactive login
32
- $ unbound login --base-url http://localhost:8000 # Use local backend
33
32
  `)
34
33
  .action(async (opts) => {
35
34
  try {
@@ -8,13 +8,12 @@ function register(program) {
8
8
  .description('Show the current CLI status including config location, login state, and API connectivity. Useful for debugging connection issues.')
9
9
  .addHelpText('after', `
10
10
  Output fields:
11
- Config file - Path to the config file (~/.unbound/config.json)
12
- Logged in - Whether credentials are stored (Yes/No)
13
- Email - The authenticated user's email (if logged in)
14
- Organization - The organization name (if logged in)
15
- API base URL - The backend API URL being used (if logged in)
16
- Frontend URL - The frontend URL for browser auth (if logged in)
17
- API status - Connectivity check result (Connected / Error)
11
+ Config file - Path to the config file (~/.unbound/config.json)
12
+ Logged in - Whether credentials are stored (Yes/No)
13
+ Email - The authenticated user's email (if logged in)
14
+ Organization - The organization name (if logged in)
15
+ Unbound Gateway - The Unbound gateway URL (if logged in)
16
+ API status - Connectivity check result (Connected / Error)
18
17
 
19
18
  Examples:
20
19
  $ unbound status
@@ -34,8 +33,7 @@ Examples:
34
33
  if (loggedIn) {
35
34
  pairs.push(['Email', cfg.email || '-']);
36
35
  pairs.push(['Organization', cfg.org_name || '-']);
37
- pairs.push(['API base URL', config.getBaseUrl()]);
38
- pairs.push(['Frontend URL', config.getFrontendUrl()]);
36
+ pairs.push(['Unbound Gateway', config.getFrontendUrl()]);
39
37
  }
40
38
 
41
39
  // Check API connectivity
@@ -59,8 +57,7 @@ Examples:
59
57
  logged_in: loggedIn,
60
58
  email: loggedIn ? (cfg.email || null) : null,
61
59
  organization: loggedIn ? (cfg.org_name || null) : null,
62
- api_base_url: loggedIn ? config.getBaseUrl() : null,
63
- frontend_url: loggedIn ? config.getFrontendUrl() : null,
60
+ gateway_url: loggedIn ? config.getFrontendUrl() : null,
64
61
  api_status: connectivity,
65
62
  });
66
63
  return;
package/src/index.js CHANGED
@@ -4,6 +4,9 @@ const { Command } = require('commander');
4
4
  const config = require('./config');
5
5
  const output = require('./output');
6
6
  const { version } = require('../package.json');
7
+ const { checkForUpdates } = require('./update-check');
8
+
9
+ checkForUpdates();
7
10
 
8
11
  const program = new Command();
9
12
 
@@ -67,14 +70,6 @@ TOOL CONNECTIONS
67
70
 
68
71
  CONFIGURATION
69
72
  $ unbound config show Show all settings
70
- $ unbound config set-url <url> Set API base URL
71
- $ unbound config set-frontend-url <url> Set frontend URL
72
- $ unbound config reset-url Reset API URL to default
73
- $ unbound config reset-frontend-url Reset frontend URL to default
74
-
75
- ENVIRONMENT VARIABLES
76
- UNBOUND_API_URL Override the API base URL (e.g. http://localhost:8000)
77
- UNBOUND_FRONTEND_URL Override the frontend URL (e.g. http://localhost:3000)
78
73
 
79
74
  FILES
80
75
  ~/.unbound/config.json Credentials and CLI settings
@@ -98,31 +93,27 @@ require('./commands/setup').register(program);
98
93
  // config command for managing CLI settings
99
94
  const configCmd = program
100
95
  .command('config')
101
- .description('Manage CLI configuration. Set or view the API base URL and other settings.');
96
+ .description('Manage CLI configuration settings.');
102
97
 
98
+ // Internal/dev commands — hidden from help but still functional
103
99
  configCmd
104
- .command('set-url <url>')
105
- .description('Set the API base URL. Use http://localhost:8000 for local development.')
106
- .addHelpText('after', `
107
- Examples:
108
- $ unbound config set-url http://localhost:8000 # Local development
109
- $ unbound config set-url https://backend.getunbound.ai # Production (default)
110
- `)
100
+ .command('set-url <url>', { hidden: true })
101
+ .description('Set the API base URL (internal)')
111
102
  .action((url) => {
112
103
  config.setBaseUrl(url);
113
104
  output.success(`API base URL set to ${url}`);
114
105
  });
115
106
 
116
107
  configCmd
117
- .command('get-url')
118
- .description('Show the current API base URL.')
108
+ .command('get-url', { hidden: true })
109
+ .description('Show the current API base URL (internal)')
119
110
  .action(() => {
120
111
  console.log(config.getBaseUrl());
121
112
  });
122
113
 
123
114
  configCmd
124
- .command('reset-url')
125
- .description('Reset the API base URL to the default (https://backend.getunbound.ai).')
115
+ .command('reset-url', { hidden: true })
116
+ .description('Reset the API base URL to default (internal)')
126
117
  .action(() => {
127
118
  const cfg = config.readConfig();
128
119
  delete cfg.base_url;
@@ -131,28 +122,23 @@ configCmd
131
122
  });
132
123
 
133
124
  configCmd
134
- .command('set-frontend-url <url>')
135
- .description('Set the frontend URL. Use http://localhost:3000 for local development.')
136
- .addHelpText('after', `
137
- Examples:
138
- $ unbound config set-frontend-url http://localhost:3000 # Local development
139
- $ unbound config set-frontend-url https://gateway.getunbound.ai # Production (default)
140
- `)
125
+ .command('set-frontend-url <url>', { hidden: true })
126
+ .description('Set the frontend URL (internal)')
141
127
  .action((url) => {
142
128
  config.setFrontendUrl(url);
143
129
  output.success(`Frontend URL set to ${url}`);
144
130
  });
145
131
 
146
132
  configCmd
147
- .command('get-frontend-url')
148
- .description('Show the current frontend URL.')
133
+ .command('get-frontend-url', { hidden: true })
134
+ .description('Show the current frontend URL (internal)')
149
135
  .action(() => {
150
136
  console.log(config.getFrontendUrl());
151
137
  });
152
138
 
153
139
  configCmd
154
- .command('reset-frontend-url')
155
- .description('Reset the frontend URL to the default (https://gateway.getunbound.ai).')
140
+ .command('reset-frontend-url', { hidden: true })
141
+ .description('Reset the frontend URL to default (internal)')
156
142
  .action(() => {
157
143
  const cfg = config.readConfig();
158
144
  delete cfg.frontend_url;
@@ -170,8 +156,7 @@ configCmd
170
156
  if (opts.json) {
171
157
  output.json({
172
158
  config_file: config.CONFIG_FILE,
173
- api_base_url: config.getBaseUrl(),
174
- frontend_url: config.getFrontendUrl(),
159
+ gateway_url: config.getFrontendUrl(),
175
160
  logged_in: config.isLoggedIn(),
176
161
  email: cfg.email || null,
177
162
  organization: cfg.org_name || null,
@@ -181,8 +166,7 @@ configCmd
181
166
 
182
167
  output.keyValue([
183
168
  ['Config file', config.CONFIG_FILE],
184
- ['API base URL', config.getBaseUrl()],
185
- ['Frontend URL', config.getFrontendUrl()],
169
+ ['Unbound Gateway', config.getFrontendUrl()],
186
170
  ['Logged in', config.isLoggedIn() ? 'Yes' : 'No'],
187
171
  ['Email', cfg.email || '-'],
188
172
  ['Organization', cfg.org_name || '-'],
@@ -0,0 +1,79 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execFile } = require('child_process');
4
+ const { CONFIG_DIR } = require('./config');
5
+
6
+ const CACHE_FILE = path.join(CONFIG_DIR, 'update-check.json');
7
+ const CHECK_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours
8
+ const REGISTRY_URL = 'https://registry.npmjs.org/unbound-cli/latest';
9
+
10
+ function newerThan(latest, current) {
11
+ const a = latest.split('.').map(Number);
12
+ const b = current.split('.').map(Number);
13
+ for (let i = 0; i < 3; i++) {
14
+ if ((a[i] || 0) > (b[i] || 0)) return true;
15
+ if ((a[i] || 0) < (b[i] || 0)) return false;
16
+ }
17
+ return false;
18
+ }
19
+
20
+ function checkForUpdates() {
21
+ if (!process.stderr.isTTY || process.env.UNBOUND_NO_UPDATE_CHECK) return;
22
+
23
+ const { version: current } = require('../package.json');
24
+ let cache = null;
25
+
26
+ try {
27
+ cache = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));
28
+ } catch {
29
+ // No cache or corrupted — will trigger a background check
30
+ }
31
+
32
+ // If cached result shows a newer version, schedule notice on exit
33
+ if (cache && cache.latest && newerThan(cache.latest, current)) {
34
+ const useColor = !process.env.NO_COLOR;
35
+ const yellow = (s) => useColor ? `\x1b[33m${s}\x1b[0m` : s;
36
+ process.on('exit', () => {
37
+ console.error('');
38
+ console.error(yellow(`A new version of Unbound CLI is available: ${current} → ${cache.latest}`));
39
+ console.error(yellow(`Update using: npm install -g unbound-cli`));
40
+ });
41
+ }
42
+
43
+ // If cache is stale or missing, spawn a background check
44
+ const isStale = !cache || !cache.checkedAt || (Date.now() - cache.checkedAt > CHECK_INTERVAL);
45
+ if (isStale) {
46
+ const configDir = CONFIG_DIR.replace(/\\/g, '\\\\');
47
+ const cacheFile = CACHE_FILE.replace(/\\/g, '\\\\');
48
+ const script = `
49
+ const https = require('https');
50
+ const fs = require('fs');
51
+ https.get('${REGISTRY_URL}', { headers: { 'Accept': 'application/json' }, timeout: 10000 }, (res) => {
52
+ let data = '';
53
+ res.on('data', (c) => data += c);
54
+ res.on('end', () => {
55
+ try {
56
+ const latest = JSON.parse(data).version;
57
+ if (latest) {
58
+ fs.mkdirSync('${configDir}', { recursive: true });
59
+ fs.writeFileSync('${cacheFile}', JSON.stringify({ latest, checkedAt: Date.now() }));
60
+ }
61
+ } catch {}
62
+ });
63
+ }).on('error', () => {});
64
+ `;
65
+
66
+ try {
67
+ const child = execFile(process.execPath, ['-e', script], {
68
+ detached: true,
69
+ stdio: 'ignore',
70
+ windowsHide: true,
71
+ });
72
+ child.unref();
73
+ } catch {
74
+ // Best-effort — silently ignore failures
75
+ }
76
+ }
77
+ }
78
+
79
+ module.exports = { checkForUpdates };