@solarains/va-cli 0.1.5 → 0.1.10

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.
@@ -7,6 +7,7 @@ const doctor_command_1 = require("../commands/doctor/doctor-command");
7
7
  const init_command_1 = require("../commands/init/init-command");
8
8
  const intelligence_command_1 = require("../commands/intelligence/intelligence-command");
9
9
  const reports_command_1 = require("../commands/reports/reports-command");
10
+ const subscription_command_1 = require("../commands/subscription/subscription-command");
10
11
  const update_command_1 = require("../commands/update/update-command");
11
12
  const uninstall_command_1 = require("../commands/uninstall/uninstall-command");
12
13
  const usage_command_1 = require("../commands/usage/usage-command");
@@ -23,6 +24,7 @@ exports.rootCommand = {
23
24
  auth_command_1.authCommand,
24
25
  assets_command_1.assetsCommand,
25
26
  usage_command_1.usageCommand,
27
+ subscription_command_1.subscriptionCommand,
26
28
  reports_command_1.reportsCommand,
27
29
  intelligence_command_1.intelligenceCommand,
28
30
  doctor_command_1.doctorCommand,
@@ -95,10 +95,6 @@ exports.intelligenceQueryCommand = {
95
95
  error.code === 'ASSET_INTELLIGENCE_ACCESS_DENIED') {
96
96
  throw new Error('This account does not currently have paid asset-intelligence access.');
97
97
  }
98
- if (error instanceof authenticated_api_client_1.CliApiError &&
99
- error.code === 'ASSET_INTELLIGENCE_QUOTA_EXCEEDED') {
100
- throw new Error('The current intelligence query would exceed the daily paid usage quota.');
101
- }
102
98
  if (error instanceof authenticated_api_client_1.CliApiError && error.code === 'INVALID_CURSOR') {
103
99
  await stateStore.clear();
104
100
  throw new Error('The saved or provided query cursor is no longer valid. Restart the query without `--next`.');
@@ -13,16 +13,10 @@ function resolveAssetId(positionals, optionAssetId) {
13
13
  return optionAssetId ?? positionals[0] ?? '';
14
14
  }
15
15
  function getSelectionMessage(input) {
16
- if (input.previousSelectedReportKey === input.requestedReportKey) {
17
- return 'This report is already the locked daily selection for the current product day.';
18
- }
19
- if (!input.previousSelectedReportKey && input.detailSelectedToday) {
20
- return 'This read locked the account daily-report selection for the current product day.';
21
- }
22
16
  if (input.detailSelectedToday) {
23
- return 'This report is currently the selected daily report for the current product day.';
17
+ return 'This report is included in the current product day usage state. Repeat reads today do not consume another unit.';
24
18
  }
25
- return 'Selection state is available but no new lock was created.';
19
+ return 'This report did not consume a new daily-report unit.';
26
20
  }
27
21
  exports.reportsGetCommand = {
28
22
  name: 'get',
@@ -38,10 +32,7 @@ exports.reportsGetCommand = {
38
32
  const usageSummary = await (0, usage_api_client_1.getUsageSummary)(context);
39
33
  try {
40
34
  const report = await (0, daily_reports_api_client_1.getDailyReportDetail)(context, assetId, { date });
41
- const requestedReportKey = `${report.assetId}:${report.date}`;
42
35
  (0, daily_reports_output_1.renderDailyReportDetail)(context.logger, report, getSelectionMessage({
43
- previousSelectedReportKey: usageSummary.dailyReport.selectedReportKey,
44
- requestedReportKey,
45
36
  detailSelectedToday: report.selectedToday,
46
37
  }));
47
38
  return 0;
@@ -49,10 +40,7 @@ exports.reportsGetCommand = {
49
40
  catch (error) {
50
41
  if (error instanceof authenticated_api_client_1.CliApiError &&
51
42
  error.code === 'DAILY_REPORT_SELECTION_LIMIT_REACHED') {
52
- const selectedReportKey = usageSummary.dailyReport.selectedReportKey;
53
- throw new Error(selectedReportKey
54
- ? `Daily report selection is already locked for ${selectedReportKey}. Free accounts can only keep one report selection per product day.`
55
- : 'Daily report selection is already locked for the current product day.');
43
+ throw new Error(`Daily report limit reached for the current product day. Remaining quota: ${usageSummary.dailyReport.remainingToday ?? 0}.`);
56
44
  }
57
45
  if (error instanceof authenticated_api_client_1.CliApiError &&
58
46
  error.code === 'DAILY_REPORT_NOT_FOUND') {
@@ -7,7 +7,7 @@ const daily_reports_output_1 = require("../../features/daily-reports/daily-repor
7
7
  const usage_api_client_1 = require("../../features/usage/usage-api-client");
8
8
  exports.reportsListCommand = {
9
9
  name: 'list',
10
- summary: 'List normalized daily reports with current selection state.',
10
+ summary: 'List normalized daily reports with current usage state.',
11
11
  usage: 'vaone reports list [--asset-id <id>] [--category <category>] [--date-from <yyyy-mm-dd>] [--date-to <yyyy-mm-dd>] [--limit <n>]',
12
12
  async run(context, args) {
13
13
  const parsed = (0, argv_1.parseCommandArgs)(args);
@@ -26,7 +26,6 @@ exports.reportsListCommand = {
26
26
  ]);
27
27
  (0, daily_reports_output_1.renderDailyReportList)(context.logger, reports, {
28
28
  statusLabel: usageSummary.dailyReport.statusLabel,
29
- selectedReportKey: usageSummary.dailyReport.selectedReportKey,
30
29
  });
31
30
  return 0;
32
31
  },
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.subscriptionCommand = void 0;
4
+ exports.runSubscriptionCommand = runSubscriptionCommand;
5
+ const authenticated_api_client_1 = require("../../features/auth/authenticated-api-client");
6
+ const subscription_api_client_1 = require("../../features/subscription/subscription-api-client");
7
+ const subscription_output_1 = require("../../features/subscription/subscription-output");
8
+ const subscribe_handoff_1 = require("../../features/subscription/subscribe-handoff");
9
+ function isUnauthorizedError(error) {
10
+ if (error instanceof authenticated_api_client_1.CliApiError && error.status === 401) {
11
+ return true;
12
+ }
13
+ return error instanceof Error && error.message === 'Unauthorized';
14
+ }
15
+ async function runSubscriptionCommand(context, options = {}) {
16
+ try {
17
+ const status = await (options.getStatus ?? subscription_api_client_1.getSubscriptionStatus)(context);
18
+ (options.renderStatus ?? subscription_output_1.renderSubscriptionStatus)(context.logger, status);
19
+ if (!status.active && status.subscribe) {
20
+ await (options.handoff ?? subscribe_handoff_1.runSubscribeHandoff)(context.logger, status.subscribe);
21
+ }
22
+ return 0;
23
+ }
24
+ catch (error) {
25
+ if (error instanceof authenticated_api_client_1.AuthRequiredError) {
26
+ throw new Error('Subscription status requires login. Run `vaone auth login` first.');
27
+ }
28
+ if (isUnauthorizedError(error)) {
29
+ throw new Error('Subscription status requires a valid CLI login session.');
30
+ }
31
+ throw error;
32
+ }
33
+ }
34
+ exports.subscriptionCommand = {
35
+ name: 'subscription',
36
+ summary: 'Show current subscription status.',
37
+ usage: 'vaone subscription',
38
+ async run(context) {
39
+ return runSubscriptionCommand(context);
40
+ },
41
+ };
@@ -6,12 +6,9 @@ const output_1 = require("../../shared/output");
6
6
  function formatAssetName(report) {
7
7
  return report.assetNameZh || report.assetNameEn || report.assetId;
8
8
  }
9
- function renderDailyReportList(logger, reports, selectionSummary) {
10
- (0, output_1.printSection)(logger, 'Daily report selection');
11
- (0, output_1.printKeyValue)(logger, 'Status', selectionSummary.statusLabel);
12
- if (selectionSummary.selectedReportKey) {
13
- (0, output_1.printKeyValue)(logger, 'Locked report', selectionSummary.selectedReportKey);
14
- }
9
+ function renderDailyReportList(logger, reports, usageSummary) {
10
+ (0, output_1.printSection)(logger, 'Daily report usage');
11
+ (0, output_1.printKeyValue)(logger, 'Status', usageSummary.statusLabel);
15
12
  (0, output_1.printSection)(logger, 'Available reports');
16
13
  if (!reports.length) {
17
14
  logger.info('No reports matched the current filters.');
@@ -28,12 +25,12 @@ function renderDailyReportList(logger, reports, selectionSummary) {
28
25
  (0, output_1.printList)(logger, [report.summary || 'No summary available.']);
29
26
  });
30
27
  }
31
- function renderDailyReportDetail(logger, report, selectionMessage) {
28
+ function renderDailyReportDetail(logger, report, usageMessage) {
32
29
  (0, output_1.printSection)(logger, 'Daily report');
33
30
  (0, output_1.printKeyValue)(logger, 'Asset', `${formatAssetName(report)} (${report.assetId})`);
34
31
  (0, output_1.printKeyValue)(logger, 'Date', report.date);
35
32
  (0, output_1.printKeyValue)(logger, 'Category', report.category || 'unknown');
36
- (0, output_1.printKeyValue)(logger, 'Selection', selectionMessage);
33
+ (0, output_1.printKeyValue)(logger, 'Usage', usageMessage);
37
34
  (0, output_1.printSection)(logger, 'Summary');
38
35
  (0, output_1.printList)(logger, [report.summary || 'No summary available.']);
39
36
  (0, output_1.printSection)(logger, 'Content');
@@ -163,11 +163,8 @@ function getInitCopy(locale) {
163
163
  summaryDailyReport(value) {
164
164
  return `日报状态: ${value}`;
165
165
  },
166
- summaryLockedReport(value) {
167
- return `锁定日报: ${value}`;
168
- },
169
- summaryIntelligence(value) {
170
- return `情报额度: ${value}`;
166
+ summaryDailyReportUsed(value) {
167
+ return `今日日报已用: ${value}`;
171
168
  },
172
169
  summaryResetCadence(value) {
173
170
  return `重置周期: ${value}`;
@@ -324,11 +321,8 @@ function getInitCopy(locale) {
324
321
  summaryDailyReport(value) {
325
322
  return `Daily report: ${value}`;
326
323
  },
327
- summaryLockedReport(value) {
328
- return `Locked report: ${value}`;
329
- },
330
- summaryIntelligence(value) {
331
- return `Intelligence quota: ${value}`;
324
+ summaryDailyReportUsed(value) {
325
+ return `Daily report used today: ${value}`;
332
326
  },
333
327
  summaryResetCadence(value) {
334
328
  return `Reset cadence: ${value}`;
@@ -155,8 +155,7 @@ function extractUsageSummary(lines, copy) {
155
155
  continue;
156
156
  }
157
157
  if (line === 'Plan' ||
158
- line === 'Daily report' ||
159
- line === 'Intelligence quota') {
158
+ line === 'Daily report') {
160
159
  section = line;
161
160
  continue;
162
161
  }
@@ -173,19 +172,12 @@ function extractUsageSummary(lines, copy) {
173
172
  }
174
173
  if (section === 'Daily report') {
175
174
  const status = extractValue([line], 'Status');
176
- const lockedReport = extractValue([line], 'Locked report');
175
+ const usedToday = extractValue([line], 'Used today');
177
176
  if (status) {
178
177
  summary.push(copy.messages.summaryDailyReport(status));
179
178
  }
180
- if (lockedReport) {
181
- summary.push(copy.messages.summaryLockedReport(lockedReport));
182
- }
183
- continue;
184
- }
185
- if (section === 'Intelligence quota') {
186
- const status = extractValue([line], 'Status');
187
- if (status) {
188
- summary.push(copy.messages.summaryIntelligence(status));
179
+ if (usedToday) {
180
+ summary.push(copy.messages.summaryDailyReportUsed(usedToday));
189
181
  continue;
190
182
  }
191
183
  const normalized = line.replace(/^- /, '');
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runSubscribeHandoff = runSubscribeHandoff;
4
+ const promises_1 = require("node:readline/promises");
5
+ const node_process_1 = require("node:process");
6
+ const browser_login_1 = require("../auth/browser-login");
7
+ async function promptToOpenInTerminal(entry) {
8
+ if (!node_process_1.stdin.isTTY || !node_process_1.stdout.isTTY) {
9
+ return false;
10
+ }
11
+ const rl = (0, promises_1.createInterface)({ input: node_process_1.stdin, output: node_process_1.stdout });
12
+ try {
13
+ const answer = await rl.question(`Press Enter to open the subscribe URL in your browser, or type anything else to skip: ${entry.url}\n`);
14
+ return answer.trim().length === 0;
15
+ }
16
+ finally {
17
+ rl.close();
18
+ }
19
+ }
20
+ async function runSubscribeHandoff(logger, entry, options = {}) {
21
+ const shouldOpen = await (options.promptToOpen ?? promptToOpenInTerminal)(entry);
22
+ if (!shouldOpen) {
23
+ logger.plain(`Open this URL manually when ready: ${entry.url}`);
24
+ return;
25
+ }
26
+ const opened = await (options.openBrowser ?? browser_login_1.openSystemBrowser)(entry.url);
27
+ if (opened) {
28
+ logger.plain('Opened subscribe URL in your default browser.');
29
+ return;
30
+ }
31
+ logger.plain(`Could not open the browser automatically. Open this URL manually: ${entry.url}`);
32
+ }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSubscriptionStatus = getSubscriptionStatus;
4
+ const authenticated_api_client_1 = require("../auth/authenticated-api-client");
5
+ async function getSubscriptionStatus(context) {
6
+ return (0, authenticated_api_client_1.requestAuthenticatedJson)(context, '/v1/subscription/status');
7
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderSubscriptionStatus = renderSubscriptionStatus;
4
+ function renderSubscriptionStatus(logger, status) {
5
+ logger.plain(`Subscription: ${status.active ? 'active' : 'inactive'}`);
6
+ logger.plain(`Status: ${status.status}`);
7
+ if (status.subscribe) {
8
+ logger.plain(`Subscribe URL: ${status.subscribe.url}`);
9
+ logger.plain('Next step: Press Enter when prompted to open it in your browser, or copy the URL above.');
10
+ }
11
+ }
@@ -23,7 +23,7 @@ exports.PREFLIGHT_GUARD_ENV = 'VAONE_SKIP_PREFLIGHT';
23
23
  exports.EXPLICIT_UPDATE_REEXEC_ENV = 'VAONE_EXPLICIT_UPDATE_REEXEC';
24
24
  exports.EXPLICIT_UPDATE_TARGET_VERSION_ENV = 'VAONE_EXPLICIT_UPDATE_TARGET_VERSION';
25
25
  const UPDATE_COPY = {
26
- en: {
26
+ 'en': {
27
27
  promptTitle(currentVersion, latestVersion) {
28
28
  return `A newer VAOne CLI is available (${currentVersion} -> ${latestVersion}).`;
29
29
  },
@@ -432,8 +432,7 @@ async function runStartupPreflight(input, deps = {}) {
432
432
  return null;
433
433
  }
434
434
  const deferredBoundary = input.runtimeConfig.updateDeferredUntilVersion;
435
- if (deferredBoundary &&
436
- !(0, version_1.isVersionNewer)(latestVersion, deferredBoundary)) {
435
+ if (deferredBoundary && !(0, version_1.isVersionNewer)(latestVersion, deferredBoundary)) {
437
436
  await updateStateStore.save(nextState);
438
437
  return null;
439
438
  }
@@ -2,22 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renderUsageSummary = renderUsageSummary;
4
4
  const output_1 = require("../../shared/output");
5
- function formatQuotaLine(label, quota) {
6
- if (!quota) {
7
- return `${label}: not enabled`;
8
- }
9
- return `${label}: ${quota.used}/${quota.limit} used, ${quota.remaining} remaining`;
10
- }
11
- function formatSelectedReportKey(key) {
12
- if (!key) {
13
- return undefined;
14
- }
15
- const [assetId, date] = key.split(':');
16
- if (!assetId || !date) {
17
- return key;
18
- }
19
- return `${assetId} on ${date}`;
20
- }
21
5
  function renderUsageSummary(logger, summary) {
22
6
  (0, output_1.printSection)(logger, 'Plan');
23
7
  (0, output_1.printKeyValue)(logger, 'Current plan', summary.plan.name);
@@ -38,15 +22,8 @@ function renderUsageSummary(logger, summary) {
38
22
  (0, output_1.printKeyValue)(logger, 'Remaining selections today', summary.dailyReport.remainingToday === null
39
23
  ? 'not limited'
40
24
  : String(summary.dailyReport.remainingToday));
41
- const selectedReport = formatSelectedReportKey(summary.dailyReport.selectedReportKey);
42
- if (selectedReport) {
43
- (0, output_1.printKeyValue)(logger, 'Locked report', selectedReport);
44
- }
45
- (0, output_1.printSection)(logger, 'Intelligence quota');
46
- (0, output_1.printKeyValue)(logger, 'Status', summary.intelligence.statusLabel);
25
+ (0, output_1.printKeyValue)(logger, 'Used today', String(summary.dailyReport.usedToday));
47
26
  (0, output_1.printList)(logger, [
48
- formatQuotaLine('Calls', summary.intelligence.calls),
49
- formatQuotaLine('Rows', summary.intelligence.rows),
50
- `Reset cadence: ${summary.intelligence.resetCadence} (${summary.intelligence.resetTimezone})`,
27
+ `Reset cadence: ${summary.dailyReport.resetCadence} (${summary.dailyReport.resetTimezone})`,
51
28
  ]);
52
29
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solarains/va-cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.10",
4
4
  "description": "Bootstrap CLI for the VAOne product.",
5
5
  "bin": {
6
6
  "vaone": "dist/main.js"
@@ -27,6 +27,7 @@ Use this skill when the user:
27
27
 
28
28
  - Daily reports: read [references/daily-reports.md](references/daily-reports.md)
29
29
  - Usage summary: read [references/usage-summary.md](references/usage-summary.md)
30
+ - Subscription status: read [references/subscription-status.md](references/subscription-status.md)
30
31
  - Auth recovery or logout: read [references/auth-recovery.md](references/auth-recovery.md)
31
32
  - Asset intelligence query: read [references/asset-intelligence.md](references/asset-intelligence.md)
32
33
  - Asset catalog listing: read [references/assets.md](references/assets.md)
@@ -34,5 +35,4 @@ Use this skill when the user:
34
35
  ## Unsupported in this version
35
36
 
36
37
  - Installations or device-slot management: explain that this currently routes through the web portal. Do not invent CLI steps.
37
- - Billing or subscription actions: explain that this currently routes through the web portal. Do not invent CLI steps.
38
-
38
+ - Browser checkout execution: keep this on the web portal. Use `vaone subscription` for subscription query plus subscribe-entry handoff, but do not invent payment steps or direct billing API calls.
@@ -26,5 +26,6 @@ Use this reference when the user wants a report list, a specific report, or "tod
26
26
  ## Recovery notes
27
27
 
28
28
  - If the CLI says login is required, run `vaone auth login`.
29
- - If the CLI reports the daily report selection is already locked, explain that free accounts can keep only one report selection for the current product day.
30
-
29
+ - If the CLI reports that the daily report limit is reached, explain that free
30
+ accounts can access one unique report per product day and paid accounts can
31
+ access one hundred unique reports per product day.
@@ -0,0 +1,37 @@
1
+ # Subscription Status
2
+
3
+ Use this reference when the user asks whether the current VA or VisionAlpha account has an active subscription.
4
+
5
+ ## Command
6
+
7
+ - `vaone subscription`
8
+
9
+ ## Workflow
10
+
11
+ 1. Run `vaone subscription`.
12
+ 2. Summarize the CLI verdict instead of inventing subscription state.
13
+ 3. If the CLI reports an inactive account, surface the subscribe URL shown by the CLI and let the CLI own the Enter-to-open browser prompt.
14
+
15
+ ## Required outcomes
16
+
17
+ ### Authenticated and active
18
+
19
+ - Tell the user the current account has an active subscription.
20
+ - Keep the answer short and based on the CLI verdict.
21
+
22
+ ### Authenticated and not active
23
+
24
+ - Tell the user the current account does not have an active subscription.
25
+ - Use the subscribe URL already printed by `vaone subscription`.
26
+ - Let `vaone subscription` handle the explicit browser-open prompt instead of inventing alternate steps.
27
+
28
+ ### Authentication required
29
+
30
+ - Tell the user to run `vaone auth login` first.
31
+ - Do not ask the user to paste tokens or secrets into chat.
32
+
33
+ ## Phase boundary
34
+
35
+ - `vaone subscription` now covers both subscription query and subscribe-entry handoff.
36
+ - Checkout execution still remains on the web portal after the CLI opens the canonical subscribe URL.
37
+ - Do not call private HTTP APIs directly from the skill.