@empirical-gauzza/cli 1.0.36 → 1.0.37

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/bin/empirical.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  'use strict';
4
4
 
@@ -25,6 +25,7 @@ const { showVersion } = require('../lib/help');
25
25
  const { isPluginCommand, runPluginCommand } = require('../lib/pluginCommands');
26
26
  const { runPolicyCommand } = require('../lib/policyCommands');
27
27
  const { runSetupCommand } = require('../lib/setupCommands');
28
+ const { runUsageCommand } = require('../lib/usageCommands');
28
29
  const { runBlueprintCommand } = require('../lib/blueprintCommands');
29
30
  const { checkNativePluginUpdates } = require('../lib/pluginUpdateCheck');
30
31
 
@@ -112,6 +113,11 @@ async function main() {
112
113
  return;
113
114
  }
114
115
 
116
+ if (args[0] === 'usage') {
117
+ await runUsageCommand(args.slice(1));
118
+ return;
119
+ }
120
+
115
121
  if (args[0] === 'setup') {
116
122
  await runSetupCommand(args.slice(1));
117
123
  return;
package/lib/auth.js CHANGED
@@ -361,7 +361,7 @@ function inspectToken(token) {
361
361
  // Gathers the same identity/workspace facts `doctor` reports (token presence/type/expiry, plus
362
362
  // user + workspace from /api/v1/users/me), formatted for `auth status` / `whoami` as a single
363
363
  // JSON object instead of doctor's multi-check report.
364
- async function gatherAuthStatus() {
364
+ async function gatherAuthStatus({ includeEmpiricalTokens = false } = {}) {
365
365
  const token = resolveAccessToken();
366
366
  const tokenInfo = inspectToken(token);
367
367
 
@@ -398,6 +398,18 @@ async function gatherAuthStatus() {
398
398
  cliWorkspace = null;
399
399
  }
400
400
 
401
+ // `whoami` / `auth status` add one Empirical Tokens line while they are enforced (spec 8.4).
402
+ // Best effort with a short timeout; doctor does not ask for it.
403
+ let empiricalTokens = null;
404
+ if (includeEmpiricalTokens && token && !error) {
405
+ try {
406
+ const { fetchBillingMe, empiricalTokensLine } = require('./usageCommands');
407
+ empiricalTokens = empiricalTokensLine((await fetchBillingMe(3000)).empiricalTokenUsage);
408
+ } catch (_error) {
409
+ empiricalTokens = null;
410
+ }
411
+ }
412
+
401
413
  return {
402
414
  authenticated: Boolean(token) && !error && tokenInfo.expired !== true,
403
415
  tokenPresent: tokenInfo.present,
@@ -407,6 +419,8 @@ async function gatherAuthStatus() {
407
419
  user,
408
420
  workspaceId,
409
421
  cliWorkspace,
422
+ // Present only while enforced for this account: a dark account's JSON has no such key.
423
+ ...(empiricalTokens ? { empiricalTokens } : {}),
410
424
  error
411
425
  };
412
426
  }
@@ -422,7 +436,7 @@ async function runAuthCommand(args) {
422
436
  }
423
437
 
424
438
  if (action === 'status' || action === 'whoami') {
425
- const status = await gatherAuthStatus();
439
+ const status = await gatherAuthStatus({ includeEmpiricalTokens: true });
426
440
  log(JSON.stringify(status));
427
441
  if (!status.authenticated) {
428
442
  process.exitCode = 1;
@@ -3,7 +3,9 @@
3
3
  const { httpJson, printApiError } = require('./http');
4
4
  const { parseOption } = require('./env');
5
5
  const { requireAccessToken, resolveApiBaseUrl, buildApiAuthHeaders } = require('./auth');
6
- const { warn, log } = require('./ui');
6
+ const { warn, log, createPrompter } = require('./ui');
7
+ const { sanitizeForTerminal } = require('./setupCommands');
8
+ const { confirmCostOrExit, isCostPreview, isInteractiveTerminal } = require('./empiricalTokensFormat');
7
9
 
8
10
  function apiUrl(pathSuffix) {
9
11
  return new URL(`/api/v1/blueprints${pathSuffix}`, resolveApiBaseUrl()).toString();
@@ -61,17 +63,35 @@ async function runClaim(args) {
61
63
  async function runInstall(args) {
62
64
  const blueprintId = args[0] && !args[0].startsWith('--') ? args[0] : '';
63
65
  if (!blueprintId) {
64
- warn('Usage: empirical blueprint install <blueprintId> [--version <v>]');
66
+ warn('Usage: empirical blueprint install <blueprintId> [--version <v>] [--accept-cost]');
65
67
  process.exit(1);
66
68
  }
67
69
  const version = parseOption(args, '--version', '');
70
+ const acceptCost = args.includes('--accept-cost');
68
71
 
69
72
  const detail = await callApi('GET', `/${blueprintId}`, undefined);
70
73
  if (!detail.owned && detail.priceModel === 'free') {
71
74
  await callApi('POST', `/${blueprintId}/claim`, {});
72
75
  }
73
76
 
74
- const installResult = await callApi('POST', `/${blueprintId}/install`, version ? { version } : {});
77
+ const installBody = { ...(version ? { version } : {}), ...(acceptCost ? { acceptEmpiricalTokenCost: true } : {}) };
78
+ let installResult = await callApi('POST', `/${blueprintId}/install`, installBody);
79
+ if (isCostPreview(installResult)) {
80
+ // A priced blueprint in enforce answers 200 with the cost and starts nothing (no installId yet).
81
+ // Acceptance is sent only from --accept-cost or an explicit y at a real terminal, never for the person.
82
+ const prompter = isInteractiveTerminal() ? createPrompter(process.stderr) : null;
83
+ try {
84
+ await confirmCostOrExit(installResult, { prompter, warn, sanitize: sanitizeForTerminal });
85
+ } finally {
86
+ if (prompter) prompter.close();
87
+ }
88
+ installResult = await callApi('POST', `/${blueprintId}/install`, { ...installBody, acceptEmpiricalTokenCost: true });
89
+ }
90
+ if (!installResult || !installResult.installId) {
91
+ // Never poll /installs/undefined: a reply with no install id is not a started install.
92
+ log(JSON.stringify(installResult, null, 2));
93
+ return;
94
+ }
75
95
  const finalStatus = await waitForInstall(installResult.installId);
76
96
  log(JSON.stringify(finalStatus, null, 2));
77
97
  }