@indigoai-us/hq-cli 5.345.142 → 5.345.144

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.
Files changed (61) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/dist/command-catalog.generated.d.ts +278 -6
  3. package/dist/command-catalog.generated.js +341 -6
  4. package/dist/command-registration-plan.d.ts +6 -0
  5. package/dist/command-registration-plan.js +1 -0
  6. package/dist/commands/agent-enroll.d.ts +1 -0
  7. package/dist/commands/agent-enroll.js +7 -0
  8. package/dist/commands/agents.d.ts +25 -3
  9. package/dist/commands/agents.js +57 -4
  10. package/dist/commands/api-keys.js +66 -8
  11. package/dist/commands/billing-sponsor.d.ts +4 -0
  12. package/dist/commands/billing-sponsor.js +48 -5
  13. package/dist/commands/billing-usage.d.ts +59 -0
  14. package/dist/commands/billing-usage.js +162 -0
  15. package/dist/commands/billing.d.ts +9 -0
  16. package/dist/commands/billing.js +29 -2
  17. package/dist/commands/core-policy.js +76 -13
  18. package/dist/commands/library-add.d.ts +120 -0
  19. package/dist/commands/library-add.js +434 -0
  20. package/dist/commands/library-caption.d.ts +112 -0
  21. package/dist/commands/library-caption.js +393 -0
  22. package/dist/commands/library-link.d.ts +32 -0
  23. package/dist/commands/library-link.js +95 -0
  24. package/dist/commands/library.d.ts +72 -0
  25. package/dist/commands/library.js +527 -0
  26. package/dist/commands/meetings.d.ts +8 -0
  27. package/dist/commands/meetings.js +25 -2
  28. package/dist/commands/storage-offload.d.ts +16 -2
  29. package/dist/commands/storage-offload.js +55 -4
  30. package/dist/commands/storage.d.ts +8 -1
  31. package/dist/commands/storage.js +6 -2
  32. package/dist/hq-cloud-sync-runner/bin/sync-runner.js +2410 -2063
  33. package/dist/hq-cloud-sync-runner/package.json +1 -1
  34. package/dist/lib/billing/plan-lock.d.ts +15 -1
  35. package/dist/lib/billing/plan-lock.js +45 -5
  36. package/dist/lib/billing/usage-card.d.ts +82 -0
  37. package/dist/lib/billing/usage-card.js +198 -0
  38. package/dist/lib/billing/usage-cost.d.ts +214 -0
  39. package/dist/lib/billing/usage-cost.js +440 -0
  40. package/dist/lib/billing/usage-only.d.ts +87 -0
  41. package/dist/lib/billing/usage-only.js +262 -0
  42. package/dist/lib/billing/usage-rate-card.d.ts +51 -0
  43. package/dist/lib/billing/usage-rate-card.js +62 -0
  44. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  45. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  46. package/dist/lib/plan-limit-nag.d.ts +4 -6
  47. package/dist/lib/plan-limit-nag.js +19 -1
  48. package/dist/lib/provider-launcher.d.ts +22 -0
  49. package/dist/lib/provider-launcher.js +65 -0
  50. package/dist/lib/search-index/index.d.ts +2 -0
  51. package/dist/lib/search-index/index.js +34 -0
  52. package/dist/lib/search-index/max-doc-bytes.d.ts +2 -1
  53. package/dist/lib/search-index/max-doc-bytes.js +2 -1
  54. package/dist/main.d.ts +9 -0
  55. package/dist/main.js +20 -3
  56. package/dist/utils/billing-gate.d.ts +19 -4
  57. package/dist/utils/billing-gate.js +25 -4
  58. package/dist/utils/plan-gate-error.d.ts +10 -0
  59. package/dist/utils/plan-gate-error.js +54 -0
  60. package/dist/utils/vault-api.js +4 -1
  61. package/package.json +2 -2
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Is this account on usage pricing? (usage-only-pricing landing 2026-10-16, US-012)
3
+ *
4
+ * The signal is hq-pro's own `pricingPlan: "usage-only" | "current"` field,
5
+ * never a local guess. Sources, first answer wins:
6
+ *
7
+ * - Company:
8
+ * 1. the company's row in `GET /membership/me` (hq-pro #5042; every member
9
+ * sees it),
10
+ * 2. `GET /v1/billing/usage-limits?companyUid=` (hq-pro #5042; members),
11
+ * 3. `GET /v1/billing/summary?companyUid=` (hq-pro US-002; owner-gated,
12
+ * 403 for a non-owner).
13
+ * - Person:
14
+ * 1. `GET /v1/billing/person/usage-limits` (hq-pro #5042),
15
+ * 2. `GET /v1/billing/person/summary`, which carries a `personalUsage`
16
+ * block only for a person on usage-only pricing (hq-pro US-004).
17
+ *
18
+ * Any other decoded hq-pro body that carries the same fields is read the same
19
+ * way ({@link pricingPlanFromBody}).
20
+ *
21
+ * UNKNOWN (null) keeps the legacy copy: existing customers see exactly what
22
+ * they saw before, and an account is only told about usage pricing when the
23
+ * server said so.
24
+ */
25
+ import { BUNDLED_USAGE_CARD, formatBoxQuotes, formatCardUsd, } from "./usage-card.js";
26
+ // Static imports on purpose: resident command paths (agents, daemon, ...) may
27
+ // not lazy-load packages (src/utils/resident-package-preload.test.ts).
28
+ // vault-api loads plan-limit-nag with a dynamic import, so there is no
29
+ // static cycle through this module.
30
+ import * as vaultApi from "../../utils/vault-api.js";
31
+ import * as cognitoSession from "../../utils/cognito-session.js";
32
+ async function http() {
33
+ return vaultApi;
34
+ }
35
+ async function session() {
36
+ return cognitoSession;
37
+ }
38
+ function asRecord(value) {
39
+ return value !== null && typeof value === "object" && !Array.isArray(value)
40
+ ? value
41
+ : null;
42
+ }
43
+ /**
44
+ * Read the pricing plan out of a decoded hq-pro body. `pricingPlan` wins;
45
+ * a `personalUsage` object (person summary) means usage-only. Anything else
46
+ * is UNKNOWN.
47
+ */
48
+ export function pricingPlanFromBody(body) {
49
+ const rec = asRecord(body);
50
+ if (!rec)
51
+ return null;
52
+ if (rec.pricingPlan === "usage-only")
53
+ return "usage-only";
54
+ if (rec.pricingPlan === "current")
55
+ return "current";
56
+ if (asRecord(rec.personalUsage))
57
+ return "usage-only";
58
+ return null;
59
+ }
60
+ export function isUsageOnly(plan) {
61
+ return plan === "usage-only";
62
+ }
63
+ /** True when a company summary says a usage-only trial is running. Absent is false. */
64
+ export function usageTrialActiveFromBody(body) {
65
+ return asRecord(asRecord(body)?.usageTrial)?.status === "active";
66
+ }
67
+ async function defaultGetJson(token, path, query, timeoutMs) {
68
+ const { vaultApiFetch } = await http();
69
+ const res = await vaultApiFetch({
70
+ token,
71
+ path,
72
+ ...(query ? { query } : {}),
73
+ signal: AbortSignal.timeout(timeoutMs),
74
+ });
75
+ if (!res.ok)
76
+ return null;
77
+ return res.json().catch(() => null);
78
+ }
79
+ function getter(deps) {
80
+ return deps.getJson
81
+ ? deps.getJson
82
+ : (t, p, q) => defaultGetJson(t, p, q, deps.timeoutMs ?? 5000);
83
+ }
84
+ const companyCache = new Map();
85
+ const companyLimitsCache = new Map();
86
+ let membershipCache;
87
+ let personCache;
88
+ let personLimitsCache;
89
+ /** Test-only: forget cached reads. */
90
+ export function _resetPricingPlanCacheForTests() {
91
+ companyCache.clear();
92
+ companyLimitsCache.clear();
93
+ membershipCache = undefined;
94
+ personCache = undefined;
95
+ personLimitsCache = undefined;
96
+ }
97
+ /**
98
+ * The pricing plan on a company's row of a decoded `/membership/me` body,
99
+ * matched by uid or slug. Null when the row or the field is absent.
100
+ */
101
+ export function pricingPlanFromMembership(body, companyRef) {
102
+ const rows = asRecord(body)?.memberships;
103
+ if (!Array.isArray(rows))
104
+ return null;
105
+ const ref = companyRef.trim().toLowerCase();
106
+ for (const raw of rows) {
107
+ const row = asRecord(raw);
108
+ if (!row)
109
+ continue;
110
+ const uid = typeof row.companyUid === "string" ? row.companyUid.toLowerCase() : "";
111
+ const slug = typeof row.companySlug === "string" ? row.companySlug.toLowerCase() : "";
112
+ if (uid !== ref && slug !== ref)
113
+ continue;
114
+ if (row.pricingPlan === "usage-only" || row.pricingPlan === "current")
115
+ return row.pricingPlan;
116
+ return null;
117
+ }
118
+ return null;
119
+ }
120
+ /** The company billing summary body, read once per process. Null when unreadable. */
121
+ export async function fetchCompanyBillingSummary(token, companyUid, deps = {}) {
122
+ let pending = companyCache.get(companyUid);
123
+ if (!pending) {
124
+ pending = getter(deps)(token, "/v1/billing/summary", { companyUid }).catch(() => null);
125
+ companyCache.set(companyUid, pending);
126
+ }
127
+ return pending;
128
+ }
129
+ /** The company's pricing plan (membership row, usage-limits, then summary). Null when unknown. */
130
+ /** Resolve `work`, or null once `ms` has passed. Never rejects. */
131
+ async function withDeadline(work, ms) {
132
+ let timer;
133
+ const late = new Promise((resolve) => {
134
+ timer = setTimeout(() => resolve(null), ms);
135
+ timer.unref?.();
136
+ });
137
+ try {
138
+ return await Promise.race([work.catch(() => null), late]);
139
+ }
140
+ finally {
141
+ if (timer)
142
+ clearTimeout(timer);
143
+ }
144
+ }
145
+ const DEFAULT_DEADLINE_MS = 6000;
146
+ export async function fetchCompanyPricingPlan(token, companyUid, deps = {}) {
147
+ return withDeadline(lookupCompanyPricingPlan(token, companyUid, deps), deps.deadlineMs ?? DEFAULT_DEADLINE_MS);
148
+ }
149
+ async function lookupCompanyPricingPlan(token, companyUid, deps) {
150
+ const get = getter(deps);
151
+ membershipCache ??= get(token, "/membership/me").catch(() => null);
152
+ const fromMembership = pricingPlanFromMembership(await membershipCache, companyUid);
153
+ if (fromMembership)
154
+ return fromMembership;
155
+ let limits = companyLimitsCache.get(companyUid);
156
+ if (!limits) {
157
+ limits = get(token, "/v1/billing/usage-limits", { companyUid }).catch(() => null);
158
+ companyLimitsCache.set(companyUid, limits);
159
+ }
160
+ const fromLimits = pricingPlanFromBody(await limits);
161
+ if (fromLimits)
162
+ return fromLimits;
163
+ return pricingPlanFromBody(await fetchCompanyBillingSummary(token, companyUid, deps));
164
+ }
165
+ /** The caller's personal pricing plan (person usage-limits, then summary). Null when unknown. */
166
+ export async function fetchPersonPricingPlan(token, deps = {}) {
167
+ return withDeadline(lookupPersonPricingPlan(token, deps), deps.deadlineMs ?? DEFAULT_DEADLINE_MS);
168
+ }
169
+ async function lookupPersonPricingPlan(token, deps) {
170
+ personLimitsCache ??= getter(deps)(token, "/v1/billing/person/usage-limits").catch(() => null);
171
+ const fromLimits = pricingPlanFromBody(await personLimitsCache);
172
+ if (fromLimits)
173
+ return fromLimits;
174
+ personCache ??= getter(deps)(token, "/v1/billing/person/summary").catch(() => null);
175
+ const body = await personCache;
176
+ if (body === null || body === undefined)
177
+ return null;
178
+ // The person summary answered: no `personalUsage` block means the legacy
179
+ // personal plan, not unknown.
180
+ return pricingPlanFromBody(body) ?? "current";
181
+ }
182
+ /**
183
+ * Best-effort, non-interactive: resolve the token and company, then the plan.
184
+ * For error paths that do not already hold a token. Never throws and never
185
+ * opens a browser sign-in.
186
+ */
187
+ export async function resolveCompanyPricingPlan(company, deps = {}) {
188
+ try {
189
+ const { ensureCognitoToken } = await session();
190
+ const { getCompanyUid } = await http();
191
+ const token = await ensureCognitoToken({ interactive: false });
192
+ const companyUid = await getCompanyUid(token, company);
193
+ return await fetchCompanyPricingPlan(token, companyUid, deps);
194
+ }
195
+ catch {
196
+ return null;
197
+ }
198
+ }
199
+ /** Best-effort person-scope counterpart of {@link resolveCompanyPricingPlan}. */
200
+ export async function resolvePersonPricingPlan(deps = {}) {
201
+ try {
202
+ const { ensureCognitoToken } = await session();
203
+ const token = await ensureCognitoToken({ interactive: false });
204
+ return await fetchPersonPricingPlan(token, deps);
205
+ }
206
+ catch {
207
+ return null;
208
+ }
209
+ }
210
+ // ── Copy for usage-only accounts ─────────────────────────────────────────────
211
+ // One place for what the CLI tells an account on usage pricing. None of these
212
+ // lines names a Workforce, Individual or Starter plan or their prices.
213
+ /** One sentence every usage-only surface uses to point at the bill. */
214
+ export const USAGE_PRICING_POINTER = "Run `hq billing usage` for this month's usage, the included usage left and the projected bill.";
215
+ /** "It pays per use, with <included> of usage included each month." */
216
+ export function usageIncludedSentence(card = BUNDLED_USAGE_CARD) {
217
+ return `It pays per use, with ${formatCardUsd(card.includedUsd)} of usage included each month.`;
218
+ }
219
+ /** `hq billing upgrade` for a usage-only company. */
220
+ export function usageOnlyUpgradeLines(card = BUNDLED_USAGE_CARD) {
221
+ return [
222
+ "This company is on usage pricing, so there is no plan to upgrade to.",
223
+ usageIncludedSentence(card),
224
+ USAGE_PRICING_POINTER,
225
+ ];
226
+ }
227
+ /** The `Plan:` orientation line (`hq whoami --plan`) for a usage-only company. */
228
+ export function usageOnlyPlanLine(card = BUNDLED_USAGE_CARD) {
229
+ return (`Plan: usage pricing — ${formatCardUsd(card.includedUsd)} of usage included each month. ` +
230
+ "See `hq billing usage`.");
231
+ }
232
+ /** Agent boxes on usage pricing, priced by size from the card. */
233
+ export function usageOnlyBoxSentence(card = BUNDLED_USAGE_CARD) {
234
+ return `Agent boxes on usage pricing are billed by size: ${formatBoxQuotes(card)}.`;
235
+ }
236
+ /**
237
+ * A refusal that a legacy account answers by upgrading its plan. On usage
238
+ * pricing there is no plan: the account adds a card and pays per use.
239
+ */
240
+ export function usageOnlyCardRequiredLines(what, card = BUNDLED_USAGE_CARD) {
241
+ return [
242
+ `${what} is billed on usage pricing and needs a card on file.`,
243
+ "Add one with `hq billing checkout` (safe to share with whoever owns billing), then re-run.",
244
+ `The company pays per use, with ${formatCardUsd(card.includedUsd)} of usage included each month.`,
245
+ USAGE_PRICING_POINTER,
246
+ ];
247
+ }
248
+ /** `hq billing sponsor request` for a person on usage pricing. */
249
+ export function usageOnlySponsorLines(card = BUNDLED_USAGE_CARD) {
250
+ return [
251
+ "Your personal account is on usage pricing, so there is no personal plan for a company to cover.",
252
+ `It includes ${formatCardUsd(card.includedUsd)} of usage each month; usage above that is billed to your card.`,
253
+ "Run `hq billing usage --personal` for this month's usage and projected bill.",
254
+ ];
255
+ }
256
+ /** A personal feature refusal for a person on usage pricing. */
257
+ export function usageOnlyPersonalFeatureNotice(label) {
258
+ return (`${label} is not available on your personal account yet. ` +
259
+ "Your personal account is on usage pricing, so there is no plan to upgrade to. " +
260
+ "Run `hq billing usage --personal` for this month's usage.");
261
+ }
262
+ //# sourceMappingURL=usage-only.js.map
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Meter rates for `hq billing usage`, derived from the usage card
3
+ * (`./usage-card.ts`, the one place usage prices live in this CLI).
4
+ *
5
+ * hq-pro publishes the card in the `usage` block of `GET /v1/pricing`. When
6
+ * the feed has no `usage` block yet (or cannot be read), the bundled card is
7
+ * used; it equals the feed contract.
8
+ *
9
+ * Keys match the feed's meter ids so a usage reading, the feed and this file
10
+ * join without a mapping table. Only meters this CLI knows how to bill (gauge
11
+ * or counter) are priced here; an unknown feed meter is left out of the bill
12
+ * rather than billed with a guessed shape.
13
+ */
14
+ import { type UsageCard } from "./usage-card.js";
15
+ export type UsageMeterKey = "member-sync" | "integration-call" | "hq-mcp-call" | "agent-run" | "storage" | "secret" | "vault";
16
+ /**
17
+ * Gauges are billed on the amount held (GB, secrets, vaults) for the month.
18
+ * Counters are billed per event, so a month-to-date count is projected to
19
+ * month end.
20
+ */
21
+ export type UsageMeterKind = "gauge" | "counter";
22
+ export interface UsageMeterRate {
23
+ readonly key: UsageMeterKey;
24
+ readonly displayName: string;
25
+ /** What one billed unit is, in words. */
26
+ readonly unit: string;
27
+ /** Price of one unit in US dollars. */
28
+ readonly unitUsd: number;
29
+ readonly kind: UsageMeterKind;
30
+ /** Not billed while a trial runs (the vault fee). */
31
+ readonly waivedInTrial?: boolean;
32
+ }
33
+ export declare function isUsageMeterKey(id: string): id is UsageMeterKey;
34
+ /** Rates for the meters this CLI can bill, in card order. */
35
+ export declare function ratesFromCard(card: UsageCard): UsageMeterRate[];
36
+ /** Included usage every month before anything bills, in USD. */
37
+ export declare const USAGE_MONTHLY_ALLOWANCE_USD: number;
38
+ /** The bundled card's rates. */
39
+ export declare const BUNDLED_USAGE_METER_RATES: readonly UsageMeterRate[];
40
+ export interface UsageRates {
41
+ rates: readonly UsageMeterRate[];
42
+ allowanceUsd: number;
43
+ source: "published" | "bundled";
44
+ card: UsageCard;
45
+ }
46
+ /**
47
+ * Rates from a `GET /v1/pricing` body. A body with no `usage` block (before
48
+ * hq-pro ships it) returns the bundled card.
49
+ */
50
+ export declare function ratesFromPricingStatement(body: unknown): UsageRates;
51
+ //# sourceMappingURL=usage-rate-card.d.ts.map
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Meter rates for `hq billing usage`, derived from the usage card
3
+ * (`./usage-card.ts`, the one place usage prices live in this CLI).
4
+ *
5
+ * hq-pro publishes the card in the `usage` block of `GET /v1/pricing`. When
6
+ * the feed has no `usage` block yet (or cannot be read), the bundled card is
7
+ * used; it equals the feed contract.
8
+ *
9
+ * Keys match the feed's meter ids so a usage reading, the feed and this file
10
+ * join without a mapping table. Only meters this CLI knows how to bill (gauge
11
+ * or counter) are priced here; an unknown feed meter is left out of the bill
12
+ * rather than billed with a guessed shape.
13
+ */
14
+ import { BUNDLED_USAGE_CARD, usageCardFromPricingFeed, } from "./usage-card.js";
15
+ const METER_KINDS = {
16
+ "member-sync": "counter",
17
+ "integration-call": "counter",
18
+ "hq-mcp-call": "counter",
19
+ "agent-run": "counter",
20
+ storage: "gauge",
21
+ secret: "gauge",
22
+ vault: "gauge",
23
+ };
24
+ export function isUsageMeterKey(id) {
25
+ return Object.prototype.hasOwnProperty.call(METER_KINDS, id);
26
+ }
27
+ /** Rates for the meters this CLI can bill, in card order. */
28
+ export function ratesFromCard(card) {
29
+ const out = [];
30
+ for (const m of card.meters) {
31
+ if (!isUsageMeterKey(m.id))
32
+ continue;
33
+ out.push({
34
+ key: m.id,
35
+ displayName: m.label,
36
+ unit: m.unit,
37
+ unitUsd: m.unitUsd,
38
+ kind: METER_KINDS[m.id],
39
+ ...(m.waivedInTrial ? { waivedInTrial: true } : {}),
40
+ });
41
+ }
42
+ return out;
43
+ }
44
+ /** Included usage every month before anything bills, in USD. */
45
+ export const USAGE_MONTHLY_ALLOWANCE_USD = BUNDLED_USAGE_CARD.includedUsd;
46
+ /** The bundled card's rates. */
47
+ export const BUNDLED_USAGE_METER_RATES = ratesFromCard(BUNDLED_USAGE_CARD);
48
+ /**
49
+ * Rates from a `GET /v1/pricing` body. A body with no `usage` block (before
50
+ * hq-pro ships it) returns the bundled card.
51
+ */
52
+ export function ratesFromPricingStatement(body) {
53
+ const { card, source } = usageCardFromPricingFeed(body);
54
+ const rates = ratesFromCard(card);
55
+ return {
56
+ rates: rates.length > 0 ? rates : BUNDLED_USAGE_METER_RATES,
57
+ allowanceUsd: card.includedUsd,
58
+ source: source === "feed" ? "published" : "bundled",
59
+ card,
60
+ };
61
+ }
62
+ //# sourceMappingURL=usage-rate-card.js.map