unbound-cli 1.1.1 → 1.1.2

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": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "CLI tool for Unbound - AI Gateway management",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -23,7 +23,7 @@ function register(program) {
23
23
  'One-step user onboarding: install the default AI tools bundle and run device discovery. ' +
24
24
  'Runs `setup --all` followed by `discover` in a single command.'
25
25
  )
26
- .option('--api-key <key>', 'User API key (or set UNBOUND_API_KEY env var)')
26
+ .option('--api-key <key>', 'User API key (or set UNBOUND_API_KEY env var, or reuse a stored `unbound login` key)')
27
27
  .option('--discovery-key <key>', 'Discovery API key for device scan (or set UNBOUND_DISCOVERY_KEY env var)')
28
28
  .option('--domain <url>', 'Backend URL for discovery (defaults to configured backend)')
29
29
  .option('--set-cron', 'Set up a daily background job to keep governance up to date')
@@ -33,7 +33,8 @@ function register(program) {
33
33
  .addOption(new Option('--gateway-url <url>', 'Override gateway URL for setup scripts (dev only)').hideHelp())
34
34
  .addHelpText('after', `
35
35
  Runs the full onboarding flow for an end user:
36
- 1. Logs in with --api-key and stores credentials.
36
+ 1. Logs in with --api-key and stores credentials (or reuses a stored
37
+ \`unbound login\` key when --api-key is omitted).
37
38
  2. Installs the default tool bundle: ${ALL_TOOLS.join(', ')}.
38
39
  3. Runs device discovery with --discovery-key. With --set-cron, sets up a
39
40
  recurring daily scheduled scan (cross-platform) instead of a one-time scan.
@@ -56,8 +57,10 @@ Examples:
56
57
  .action(async (opts) => {
57
58
  const apiKeyOpt = opts.apiKey || process.env.UNBOUND_API_KEY;
58
59
  const discoveryKeyOpt = opts.discoveryKey || process.env.UNBOUND_DISCOVERY_KEY;
59
- if (!apiKeyOpt) {
60
- output.error('--api-key is required (or set UNBOUND_API_KEY env var)');
60
+ // A stored `unbound login` key is enough — only demand --api-key when not
61
+ // already logged in. ensureLoggedIn() reuses the stored credential below.
62
+ if (!apiKeyOpt && !config.isLoggedIn()) {
63
+ output.error('--api-key is required (or set UNBOUND_API_KEY env var, or run `unbound login` first)');
61
64
  process.exitCode = 1;
62
65
  return;
63
66
  }
@@ -116,7 +119,7 @@ Examples:
116
119
  const { setupScheduledRun } = require('../scheduled');
117
120
  await setupScheduledRun({
118
121
  command: 'onboard',
119
- apiKey: apiKeyOpt,
122
+ apiKey: apiKeyOpt || apiKey,
120
123
  discoveryKey: discoveryKeyOpt,
121
124
  domain: discoveryDomain,
122
125
  skipRunAtLoad: true,
@@ -182,7 +185,7 @@ Examples:
182
185
  'One-step MDM onboarding: install the default MDM tool bundle and run device discovery. ' +
183
186
  'Requires root. Used by organization admins to enroll devices via MDM.'
184
187
  )
185
- .requiredOption('--admin-api-key <key>', 'Admin API key for MDM enrollment')
188
+ .option('--admin-api-key <key>', 'Admin API key for MDM enrollment (falls back to your stored `unbound login` key)')
186
189
  .requiredOption('--discovery-key <key>', 'Discovery API key (for device scan)')
187
190
  .option('--domain <url>', 'Backend URL for discovery (defaults to configured backend)')
188
191
  .option('--backfill', 'Seed historical Claude Code / Codex / Copilot sessions from local transcripts into Unbound analytics (Cursor skipped automatically)')
@@ -196,11 +199,14 @@ Runs the full MDM onboarding flow for device enrollment:
196
199
 
197
200
  Both steps require root. The admin API key and discovery API key are
198
201
  separate keys obtained from different parts of the Unbound admin dashboard.
202
+ --admin-api-key may be omitted to reuse the key stored by a prior
203
+ \`unbound login\` (run sudo with HOME preserved so the stored key is found).
199
204
 
200
205
  For end-user onboarding (non-MDM), use \`unbound onboard\` instead.
201
206
 
202
207
  Examples:
203
208
  $ sudo unbound onboard-mdm --admin-api-key <ADMIN_KEY> --discovery-key <DISCOVERY_KEY>
209
+ $ sudo unbound onboard-mdm --discovery-key <DISCOVERY_KEY> Reuse the stored \`unbound login\` key
204
210
  $ sudo unbound onboard-mdm --admin-api-key <ADMIN_KEY> --discovery-key <DISCOVERY_KEY> --backfill
205
211
  `)
206
212
  .action(async (opts) => {
@@ -221,9 +227,17 @@ Examples:
221
227
 
222
228
  checkRoot('onboard-mdm');
223
229
 
230
+ // Reuse the key stored by `unbound login` when --admin-api-key is omitted.
231
+ const adminApiKey = opts.adminApiKey || config.getApiKey();
232
+ if (!adminApiKey) {
233
+ output.error('--admin-api-key is required (or run `unbound login` first).');
234
+ process.exitCode = 1;
235
+ return;
236
+ }
237
+
224
238
  console.log('');
225
239
  output.info('Step 1/2: Installing MDM tool bundle');
226
- const ok = await runMdmSetupAllBundle(opts.adminApiKey, {
240
+ const ok = await runMdmSetupAllBundle(adminApiKey, {
227
241
  backendUrl, gatewayUrl, backfill: !!opts.backfill,
228
242
  });
229
243
  if (!ok) return;
@@ -510,7 +510,7 @@ requires authentication.
510
510
  // No tools specified → interactive multi-select (existing flow)
511
511
  if (tools.length === 0) {
512
512
  const selected = await output.multiSelect(
513
- 'Select tools to set up with Unbound:',
513
+ opts.clear ? 'Select tools to remove Unbound configuration for:' : 'Select tools to set up with Unbound:',
514
514
  SETUP_TOOLS
515
515
  );
516
516
 
@@ -530,14 +530,15 @@ requires authentication.
530
530
  const ok = await runBatch(selectedTools, (tool) => {
531
531
  const toolArgs = buildScriptArgs(apiKey, {
532
532
  ...urlOpts,
533
+ clear: opts.clear,
533
534
  backfill: opts.backfill && scriptSupportsBackfill(tool.script),
534
535
  });
535
536
  return runScriptPiped(tool.script, toolArgs);
536
- });
537
+ }, { clear: opts.clear });
537
538
  if (!ok) return;
538
539
 
539
540
  console.log('');
540
- output.success('All tools configured');
541
+ output.success(opts.clear ? 'All tools cleared' : 'All tools configured');
541
542
  return;
542
543
  }
543
544
 
@@ -705,7 +706,7 @@ requires authentication.
705
706
  'Used by organization admins to enroll devices via MDM.'
706
707
  )
707
708
  .argument('[tools...]', 'Tools to set up: ' + mdmToolNames)
708
- .option('--admin-api-key <key>', 'Admin API key for MDM enrollment (not required with --clear)')
709
+ .option('--admin-api-key <key>', 'Admin API key for MDM enrollment (falls back to your stored `unbound login` key; not required with --clear)')
709
710
  .option('--clear', 'Remove Unbound configuration for the specified tools (no API key required)')
710
711
  .option('--all', 'Set up all available tools')
711
712
  .option('--backfill', 'Seed historical Claude Code / Codex / Copilot sessions from local transcripts into Unbound analytics (subscription/hooks mode only; Cursor unsupported)')
@@ -725,10 +726,12 @@ Note: claude-code-subscription and claude-code-gateway are mutually exclusive wh
725
726
  setting up; same for codex. Bare claude-code/codex set up subscription mode.
726
727
  When using --all, subscription mode is used by default for Claude Code and Codex.
727
728
 
728
- Setup examples (require --admin-api-key):
729
+ Setup examples (need an admin key — pass --admin-api-key, or omit it to reuse the
730
+ key stored by a prior \`unbound login\`):
729
731
  $ sudo unbound setup mdm --admin-api-key KEY cursor
730
732
  $ sudo unbound setup mdm --admin-api-key KEY cursor claude-code-subscription codex-subscription
731
733
  $ sudo unbound setup mdm --admin-api-key KEY --all
734
+ $ sudo unbound setup mdm --all Reuse the stored \`unbound login\` key
732
735
  $ sudo unbound setup mdm --admin-api-key KEY claude-code-subscription --backfill
733
736
  Install hooks AND backfill local history
734
737
  $ sudo unbound setup mdm --admin-api-key KEY copilot --backfill
@@ -748,9 +751,12 @@ Clear examples (no API key required):
748
751
  // Use optsWithGlobals() so they all work regardless of position relative to `mdm`.
749
752
  const globalOpts = command.optsWithGlobals();
750
753
  // Clearing removes config without calling the API, so a key is only
751
- // required when actually enrolling tools.
752
- if (!globalOpts.clear && !opts.adminApiKey) {
753
- output.error('--admin-api-key is required to set up tools.');
754
+ // required when actually enrolling tools. Fall back to the API key
755
+ // stored by `unbound login` so admins who are already logged in don't
756
+ // have to pass --admin-api-key again.
757
+ const adminApiKey = opts.adminApiKey || config.getApiKey();
758
+ if (!globalOpts.clear && !adminApiKey) {
759
+ output.error('--admin-api-key is required to set up tools (or run `unbound login` first).');
754
760
  process.exitCode = 1;
755
761
  return;
756
762
  }
@@ -829,7 +835,7 @@ Clear examples (no API key required):
829
835
  const ok = await runBatch(
830
836
  resolvedTools,
831
837
  (tool) => {
832
- const toolArgs = buildScriptArgs(opts.adminApiKey, {
838
+ const toolArgs = buildScriptArgs(adminApiKey, {
833
839
  backendUrl,
834
840
  gatewayUrl,
835
841
  clear: globalOpts.clear,