@indigoai-us/hq-cli 5.345.156 → 5.345.160
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/CHANGELOG.md +255 -0
- package/assets/scaffold/core/scripts/rebuild-company-knowledge-index.sh +110 -2
- package/assets/scaffold/core/scripts/rebuild-projects-index.sh +101 -5
- package/assets/scaffold/core/scripts/worktree.sh +4 -0
- package/dist/command-catalog.generated.d.ts +535 -10
- package/dist/command-catalog.generated.js +678 -31
- package/dist/command-registration-plan.d.ts +18 -0
- package/dist/command-registration-plan.js +4 -0
- package/dist/commands/_patterns.d.ts +17 -0
- package/dist/commands/_patterns.js +22 -0
- package/dist/commands/access.d.ts +6 -0
- package/dist/commands/access.js +18 -1
- package/dist/commands/acl-lock.d.ts +41 -0
- package/dist/commands/acl-lock.js +28 -0
- package/dist/commands/agent-enroll.d.ts +2 -0
- package/dist/commands/agent-enroll.js +9 -4
- package/dist/commands/agent-kit.js +8 -6
- package/dist/commands/agents.js +5 -11
- package/dist/commands/billing-sponsor.d.ts +4 -4
- package/dist/commands/billing-sponsor.js +11 -11
- package/dist/commands/billing-usage.d.ts +22 -8
- package/dist/commands/billing-usage.js +57 -22
- package/dist/commands/billing.d.ts +7 -7
- package/dist/commands/billing.js +13 -13
- package/dist/commands/bot.js +7 -4
- package/dist/commands/channel-suggest.d.ts +56 -0
- package/dist/commands/channel-suggest.js +170 -0
- package/dist/commands/channels.js +7 -1
- package/dist/commands/cloud-provision.js +2 -2
- package/dist/commands/cloud.d.ts +45 -0
- package/dist/commands/cloud.js +157 -17
- package/dist/commands/company.js +4 -3
- package/dist/commands/core-checkpoint-native.d.ts +18 -0
- package/dist/commands/core-checkpoint-native.js +67 -2
- package/dist/commands/core-pipeline-driver-native.d.ts +12 -1
- package/dist/commands/core-pipeline-driver-native.js +131 -5
- package/dist/commands/core.js +12 -0
- package/dist/commands/crm.js +83 -6
- package/dist/commands/db-remote-deps.js +12 -1
- package/dist/commands/deploy-access.d.ts +33 -0
- package/dist/commands/deploy-access.js +322 -0
- package/dist/commands/dm-attach.d.ts +106 -0
- package/dist/commands/dm-attach.js +184 -0
- package/dist/commands/dm.d.ts +37 -0
- package/dist/commands/dm.js +172 -10
- package/dist/commands/feedback-show.d.ts +40 -0
- package/dist/commands/feedback-show.js +261 -0
- package/dist/commands/feedback.d.ts +21 -0
- package/dist/commands/feedback.js +152 -2
- package/dist/commands/files.d.ts +24 -0
- package/dist/commands/files.js +71 -8
- package/dist/commands/integrations-connect.js +7 -3
- package/dist/commands/lanes-accounts.js +16 -1
- package/dist/commands/lanes-loop.js +14 -4
- package/dist/commands/lanes-message.js +6 -4
- package/dist/commands/lanes-resume.js +36 -13
- package/dist/commands/lanes-shared.d.ts +5 -0
- package/dist/commands/lanes-shared.js +13 -3
- package/dist/commands/lanes-watch.js +1 -0
- package/dist/commands/lanes.d.ts +20 -1
- package/dist/commands/lanes.js +201 -51
- package/dist/commands/meetings.d.ts +4 -4
- package/dist/commands/meetings.js +17 -8
- package/dist/commands/mesh.js +149 -1
- package/dist/commands/monitor.d.ts +1 -0
- package/dist/commands/monitor.js +4 -1
- package/dist/commands/repo.d.ts +41 -0
- package/dist/commands/repo.js +331 -0
- package/dist/commands/scan.d.ts +3 -1
- package/dist/commands/scan.js +7 -3
- package/dist/commands/secrets.d.ts +21 -0
- package/dist/commands/secrets.js +114 -14
- package/dist/commands/signals.js +17 -5
- package/dist/commands/skill.js +6 -6
- package/dist/commands/staff/listings.d.ts +41 -0
- package/dist/commands/staff/listings.js +160 -0
- package/dist/commands/staff/reset-test-account.d.ts +3 -2
- package/dist/commands/staff/reset-test-account.js +4 -3
- package/dist/commands/whoami.js +1 -1
- package/dist/hook-engine-path.js +13 -2
- package/dist/hq-cloud-sync-runner/bin/sync-runner.js +3392 -1243
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- package/dist/lib/agent-kit/run/inbox.js +2 -1
- package/dist/lib/agent-kit/wake-secrets.d.ts +7 -0
- package/dist/lib/agent-kit/wake-secrets.js +32 -0
- package/dist/lib/agent-kit/wake.d.ts +4 -0
- package/dist/lib/agent-kit/wake.js +49 -3
- package/dist/lib/billing/plan-limit-refusals.d.ts +13 -3
- package/dist/lib/billing/plan-limit-refusals.js +26 -3
- package/dist/lib/billing/plan-limit-upgrade-url.d.ts +8 -0
- package/dist/lib/billing/plan-limit-upgrade-url.js +18 -0
- package/dist/lib/billing/plan-lock.d.ts +22 -6
- package/dist/lib/billing/plan-lock.js +58 -30
- package/dist/lib/billing/usage-cost.d.ts +24 -2
- package/dist/lib/billing/usage-cost.js +103 -3
- package/dist/lib/billing/usage-only.d.ts +37 -1
- package/dist/lib/billing/usage-only.js +89 -4
- package/dist/lib/billing/usage-pause.d.ts +27 -0
- package/dist/lib/billing/usage-pause.js +50 -0
- package/dist/lib/bot/inflight.d.ts +7 -1
- package/dist/lib/bot/inflight.js +10 -2
- package/dist/lib/bot/prompt.js +3 -0
- package/dist/lib/bot/run.js +72 -20
- package/dist/lib/bot/session.d.ts +14 -0
- package/dist/lib/bot/session.js +70 -0
- package/dist/lib/capacity/collectors/claude-oauth.d.ts +6 -0
- package/dist/lib/capacity/collectors/claude-oauth.js +12 -0
- package/dist/lib/capacity/lane-login.d.ts +6 -0
- package/dist/lib/capacity/lane-login.js +5 -0
- package/dist/lib/core-utils/worktree.js +2 -0
- package/dist/lib/daemon/exit-cause.d.ts +43 -0
- package/dist/lib/daemon/exit-cause.js +88 -0
- package/dist/lib/daemon/handoff.d.ts +2 -0
- package/dist/lib/daemon/handoff.js +6 -1
- package/dist/lib/daemon/hq-child-bootstrap.mjs +20 -3
- package/dist/lib/daemon/paths.d.ts +2 -0
- package/dist/lib/daemon/paths.js +1 -0
- package/dist/lib/daemon/run.js +13 -0
- package/dist/lib/daemon/supervisor.d.ts +54 -1
- package/dist/lib/daemon/supervisor.js +297 -21
- package/dist/lib/daemon/unit-holds.d.ts +22 -0
- package/dist/lib/daemon/unit-holds.js +86 -0
- package/dist/lib/db/control-plane.d.ts +5 -0
- package/dist/lib/db/control-plane.js +10 -0
- package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
- package/dist/lib/flag-registry-worker-source.generated.js +1 -1
- package/dist/lib/hook-engine/resolve.d.ts +14 -0
- package/dist/lib/hook-engine/resolve.js +55 -1
- package/dist/lib/index-render/company-knowledge.js +1 -3
- package/dist/lib/index-render/projects.js +1 -3
- package/dist/lib/lanes/account-company.d.ts +28 -0
- package/dist/lib/lanes/account-company.js +53 -0
- package/dist/lib/lanes/accounts.d.ts +22 -3
- package/dist/lib/lanes/accounts.js +38 -2
- package/dist/lib/lanes/claude-balance.d.ts +6 -1
- package/dist/lib/lanes/claude-balance.js +6 -1
- package/dist/lib/lanes/claude-credential.d.ts +78 -0
- package/dist/lib/lanes/claude-credential.js +163 -0
- package/dist/lib/lanes/compaction.d.ts +2 -0
- package/dist/lib/lanes/compaction.js +9 -0
- package/dist/lib/lanes/config.js +6 -0
- package/dist/lib/lanes/envelope-repair-dispatch.js +2 -1
- package/dist/lib/lanes/hooks-install.js +2 -1
- package/dist/lib/lanes/host-capacity.d.ts +2 -0
- package/dist/lib/lanes/host-capacity.js +1 -0
- package/dist/lib/lanes/keep-alive.d.ts +1 -1
- package/dist/lib/lanes/keep-alive.js +3 -1
- package/dist/lib/lanes/links.js +10 -4
- package/dist/lib/lanes/loop.js +16 -1
- package/dist/lib/lanes/node-binary.d.ts +17 -0
- package/dist/lib/lanes/node-binary.js +30 -0
- package/dist/lib/lanes/outpost-dispatch.d.ts +2 -1
- package/dist/lib/lanes/outpost-dispatch.js +1 -0
- package/dist/lib/lanes/reconcile.js +4 -0
- package/dist/lib/lanes/remote-control-lane.js +2 -0
- package/dist/lib/lanes/spawn.d.ts +3 -0
- package/dist/lib/lanes/spawn.js +119 -9
- package/dist/lib/lanes/store.d.ts +6 -0
- package/dist/lib/lanes/store.js +107 -18
- package/dist/lib/lanes/types.d.ts +2 -0
- package/dist/lib/lanes/watch.js +28 -5
- package/dist/lib/lanes/watcher-main.js +4 -3
- package/dist/lib/lanes/watcher.d.ts +4 -0
- package/dist/lib/lanes/watcher.js +110 -21
- package/dist/lib/mesh/api.d.ts +32 -0
- package/dist/lib/mesh/api.js +75 -0
- package/dist/lib/plan-limit-nag.d.ts +0 -16
- package/dist/lib/plan-limit-nag.js +17 -18
- package/dist/lib/secret-scan/detect.js +9 -0
- package/dist/lib/secret-scan/repo-config.d.ts +41 -0
- package/dist/lib/secret-scan/repo-config.js +237 -0
- package/dist/lib/secret-scan/rules.d.ts +7 -1
- package/dist/lib/secret-scan/rules.js +3 -1
- package/dist/lib/secret-scan/staged.d.ts +5 -0
- package/dist/lib/secret-scan/staged.js +19 -2
- package/dist/lib/secret-scan/types.d.ts +2 -0
- package/dist/lib/sync/server-owned-local-only.d.ts +29 -0
- package/dist/lib/sync/server-owned-local-only.js +75 -0
- package/dist/main.d.ts +0 -2
- package/dist/main.js +19 -57
- package/dist/utils/package-use-lease.js +27 -9
- package/dist/utils/plan-gate-error.d.ts +30 -14
- package/dist/utils/plan-gate-error.js +60 -46
- package/dist/utils/team-upgrade.d.ts +19 -6
- package/dist/utils/team-upgrade.js +37 -39
- package/dist/utils/version-gate.d.ts +7 -0
- package/dist/utils/version-gate.js +66 -0
- package/package.json +8 -8
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
* - optionally, the live cloud file-history listing `hq storage status`
|
|
14
14
|
* uses, to split storage into current files and old versions and to see a
|
|
15
15
|
* prune right away (Storage Lens, behind usage-limits, updates daily).
|
|
16
|
+
* With the usage statement, the listing is a separate current-storage
|
|
17
|
+
* breakdown only; the statement's month-average storage meter is billed.
|
|
16
18
|
* usage-limits also answers `vaults` when the server counts them; without it
|
|
17
19
|
* the vault meter is `not-available`.
|
|
18
20
|
* The call-count meters (syncs, gateway calls, HQ MCP calls, agent runs) have
|
|
@@ -45,6 +47,16 @@ export interface MeterReading {
|
|
|
45
47
|
/** In billed units (GB for storage). Null unless measured. */
|
|
46
48
|
value: number | null;
|
|
47
49
|
state: MeterState;
|
|
50
|
+
/**
|
|
51
|
+
* The server's own priced amounts for this line (usage statement only).
|
|
52
|
+
* When present they are the bill: the statement is what is invoiced to the
|
|
53
|
+
* cent, so the line is never re-priced from the rate card.
|
|
54
|
+
*/
|
|
55
|
+
accrued?: {
|
|
56
|
+
monthToDateUsd: number;
|
|
57
|
+
projectedUsd: number;
|
|
58
|
+
waived?: boolean;
|
|
59
|
+
};
|
|
48
60
|
}
|
|
49
61
|
export interface StorageSplit {
|
|
50
62
|
/** Bytes in current files. */
|
|
@@ -60,7 +72,7 @@ export interface StorageSplit {
|
|
|
60
72
|
export interface MonthUsage {
|
|
61
73
|
scope: UsageScope;
|
|
62
74
|
/** Which source answered; lets a reader tell interim numbers from the rollup. */
|
|
63
|
-
source: "usage-limits" | "daily-rollup";
|
|
75
|
+
source: "usage-statement" | "usage-limits" | "daily-rollup";
|
|
64
76
|
/** Month-to-date window, UTC. */
|
|
65
77
|
period: {
|
|
66
78
|
start: string;
|
|
@@ -71,6 +83,8 @@ export interface MonthUsage {
|
|
|
71
83
|
storage?: StorageSplit;
|
|
72
84
|
/** True when the account's usage trial is running (meters waived in trial bill $0). Absent = unknown. */
|
|
73
85
|
trialActive?: boolean;
|
|
86
|
+
/** The included usage the usage statement applied. Absent: use the rate card's. */
|
|
87
|
+
allowanceUsd?: number;
|
|
74
88
|
}
|
|
75
89
|
/** One way to read a month of usage. The rollup becomes a second implementation. */
|
|
76
90
|
export interface MonthUsageSource {
|
|
@@ -119,6 +133,14 @@ export declare function meterReadingsFromDailyRows(rows: readonly UsageDailyRowL
|
|
|
119
133
|
* {@link readMonthUsage} once that route ships; the company scope only.
|
|
120
134
|
*/
|
|
121
135
|
export declare function dailyRollupSource(fetchRows: (scope: UsageScope, from: string, to: string) => Promise<readonly UsageDailyRowLike[] | null>): MonthUsageSource;
|
|
136
|
+
export declare function meterReadingsFromStatement(body: unknown): MeterReading[] | null;
|
|
137
|
+
/**
|
|
138
|
+
* The usage statement as a source (`GET /v1/billing/usage`, US-015). The
|
|
139
|
+
* company statement is owner-gated on the server; a person statement is the
|
|
140
|
+
* caller's own. `fetchStatement` resolves the decoded body, or null when the
|
|
141
|
+
* server did not answer.
|
|
142
|
+
*/
|
|
143
|
+
export declare function usageStatementSource(fetchStatement: (scope: UsageScope) => Promise<unknown | null>): MonthUsageSource;
|
|
122
144
|
/**
|
|
123
145
|
* The one function every caller uses to read a month of usage. Switch the
|
|
124
146
|
* default source here when the US-005 rollup has a read route.
|
|
@@ -151,7 +173,7 @@ export interface UsageBill {
|
|
|
151
173
|
daysElapsed: number;
|
|
152
174
|
daysInMonth: number;
|
|
153
175
|
}
|
|
154
|
-
export declare function computeBill(usage: MonthUsage, rates?: readonly UsageMeterRate[],
|
|
176
|
+
export declare function computeBill(usage: MonthUsage, rates?: readonly UsageMeterRate[], cardAllowanceUsd?: number): UsageBill;
|
|
155
177
|
export type FixId = "prune-old-versions" | "remove-unused-secrets" | "exclude-folders" | "turn-sync-off";
|
|
156
178
|
/**
|
|
157
179
|
* A fix is a pair of command lists. `preview` only reads (dry runs, listings);
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
* - optionally, the live cloud file-history listing `hq storage status`
|
|
14
14
|
* uses, to split storage into current files and old versions and to see a
|
|
15
15
|
* prune right away (Storage Lens, behind usage-limits, updates daily).
|
|
16
|
+
* With the usage statement, the listing is a separate current-storage
|
|
17
|
+
* breakdown only; the statement's month-average storage meter is billed.
|
|
16
18
|
* usage-limits also answers `vaults` when the server counts them; without it
|
|
17
19
|
* the vault meter is `not-available`.
|
|
18
20
|
* The call-count meters (syncs, gateway calls, HQ MCP calls, agent runs) have
|
|
@@ -145,6 +147,83 @@ export function dailyRollupSource(fetchRows) {
|
|
|
145
147
|
},
|
|
146
148
|
};
|
|
147
149
|
}
|
|
150
|
+
/** Statement line key (hq-pro `UsageStatementLine.key`) -> rate-card key. */
|
|
151
|
+
const STATEMENT_KEYS = new Set(BUNDLED_USAGE_METER_RATES.map(({ key }) => key));
|
|
152
|
+
/**
|
|
153
|
+
* Meter readings from a `GET /v1/billing/usage` statement body (hq-pro
|
|
154
|
+
* src/billing/usage-statement.ts, US-006). `usage` is the natural amount:
|
|
155
|
+
* counters are the month's total, gauges the mean daily level. Storage comes
|
|
156
|
+
* in bytes and is converted with the statement's own `storageBytesPerGb`.
|
|
157
|
+
* A line that is unavailable, not billed yet, or missing is `not-available`,
|
|
158
|
+
* never zero. Null when the body is not a statement.
|
|
159
|
+
*/
|
|
160
|
+
function centsField(v) {
|
|
161
|
+
return typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : null;
|
|
162
|
+
}
|
|
163
|
+
export function meterReadingsFromStatement(body) {
|
|
164
|
+
const rec = body !== null && typeof body === "object" && !Array.isArray(body)
|
|
165
|
+
? body
|
|
166
|
+
: null;
|
|
167
|
+
if (!rec || !Array.isArray(rec.lines))
|
|
168
|
+
return null;
|
|
169
|
+
const bytesPerGb = typeof rec.storageBytesPerGb === "number" && rec.storageBytesPerGb > 0
|
|
170
|
+
? rec.storageBytesPerGb
|
|
171
|
+
: BYTES_PER_GB;
|
|
172
|
+
const byKey = new Map();
|
|
173
|
+
for (const raw of rec.lines) {
|
|
174
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
175
|
+
continue;
|
|
176
|
+
const line = raw;
|
|
177
|
+
if (typeof line.key !== "string" || !STATEMENT_KEYS.has(line.key))
|
|
178
|
+
continue;
|
|
179
|
+
const key = line.key;
|
|
180
|
+
const measured = line.status === "measured" || line.status === "partial";
|
|
181
|
+
const usage = line.usage;
|
|
182
|
+
if (!measured || typeof usage !== "number" || !Number.isFinite(usage) || usage < 0) {
|
|
183
|
+
byKey.set(key, { key, value: null, state: "not-available" });
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const reading = { key, value: key === "storage" ? usage / bytesPerGb : usage, state: "measured" };
|
|
187
|
+
const cost = centsField(line.costCents);
|
|
188
|
+
const projected = centsField(line.projectedCostCents);
|
|
189
|
+
if (cost !== null && projected !== null) {
|
|
190
|
+
reading.accrued = {
|
|
191
|
+
monthToDateUsd: cost / 100,
|
|
192
|
+
projectedUsd: projected / 100,
|
|
193
|
+
...(line.waived === true ? { waived: true } : {}),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
byKey.set(key, reading);
|
|
197
|
+
}
|
|
198
|
+
return BUNDLED_USAGE_METER_RATES.map(({ key }) => byKey.get(key) ?? { key, value: null, state: "not-available" });
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* The usage statement as a source (`GET /v1/billing/usage`, US-015). The
|
|
202
|
+
* company statement is owner-gated on the server; a person statement is the
|
|
203
|
+
* caller's own. `fetchStatement` resolves the decoded body, or null when the
|
|
204
|
+
* server did not answer.
|
|
205
|
+
*/
|
|
206
|
+
export function usageStatementSource(fetchStatement) {
|
|
207
|
+
return {
|
|
208
|
+
name: "usage-statement",
|
|
209
|
+
async read(scope, now) {
|
|
210
|
+
const { start, end } = monthWindow(now);
|
|
211
|
+
const body = await fetchStatement(scope);
|
|
212
|
+
const meters = meterReadingsFromStatement(body);
|
|
213
|
+
if (meters === null) {
|
|
214
|
+
throw new Error("Could not read usage from HQ right now. Try again, or run `hq login` if you are signed out.");
|
|
215
|
+
}
|
|
216
|
+
const allowanceCents = centsField(body.allowanceCents);
|
|
217
|
+
return {
|
|
218
|
+
scope,
|
|
219
|
+
source: "usage-statement",
|
|
220
|
+
period: { start: start.toISOString(), end: end.toISOString(), asOf: now.toISOString() },
|
|
221
|
+
meters,
|
|
222
|
+
...(allowanceCents !== null ? { allowanceUsd: allowanceCents / 100 } : {}),
|
|
223
|
+
};
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
}
|
|
148
227
|
/**
|
|
149
228
|
* The one function every caller uses to read a month of usage. Switch the
|
|
150
229
|
* default source here when the US-005 rollup has a read route.
|
|
@@ -159,8 +238,15 @@ export async function readMonthUsage(scope, deps, source = usageLimitsSource(dep
|
|
|
159
238
|
const split = await deps.readStorageSplit(scope).catch(() => null);
|
|
160
239
|
if (!split)
|
|
161
240
|
return base;
|
|
162
|
-
//
|
|
163
|
-
//
|
|
241
|
+
// An accrued source (the usage statement, the daily rollup) bills storage as
|
|
242
|
+
// the month's average. That meter is authoritative: the live listing is a
|
|
243
|
+
// point-in-time figure, kept only as the separate `storage` breakdown that
|
|
244
|
+
// explains what is taking space. It never replaces or re-prices the meter.
|
|
245
|
+
if (base.source !== "usage-limits")
|
|
246
|
+
return { ...base, storage: split };
|
|
247
|
+
// usage-limits storage is itself a point-in-time reading (Storage Lens,
|
|
248
|
+
// updated daily). The live listing measures the same thing more freshly, so
|
|
249
|
+
// a prune shows up in the very next read.
|
|
164
250
|
const liveGb = (split.currentBytes + split.oldVersionBytes) / BYTES_PER_GB;
|
|
165
251
|
return {
|
|
166
252
|
...base,
|
|
@@ -169,7 +255,8 @@ export async function readMonthUsage(scope, deps, source = usageLimitsSource(dep
|
|
|
169
255
|
};
|
|
170
256
|
}
|
|
171
257
|
const round2 = (n) => Math.round(n * 100) / 100;
|
|
172
|
-
export function computeBill(usage, rates = BUNDLED_USAGE_METER_RATES,
|
|
258
|
+
export function computeBill(usage, rates = BUNDLED_USAGE_METER_RATES, cardAllowanceUsd = USAGE_MONTHLY_ALLOWANCE_USD) {
|
|
259
|
+
const allowanceUsd = usage.allowanceUsd ?? cardAllowanceUsd;
|
|
173
260
|
const asOf = new Date(usage.period.asOf);
|
|
174
261
|
const { start, end } = monthWindow(asOf);
|
|
175
262
|
const dayMs = 86_400_000;
|
|
@@ -187,6 +274,19 @@ export function computeBill(usage, rates = BUNDLED_USAGE_METER_RATES, allowanceU
|
|
|
187
274
|
incomplete = true;
|
|
188
275
|
return { ...pick(rate), value: null, state: reading.state, monthToDateUsd: null, projectedUsd: null };
|
|
189
276
|
}
|
|
277
|
+
if (reading.accrued) {
|
|
278
|
+
const { monthToDateUsd: m, projectedUsd: p, waived } = reading.accrued;
|
|
279
|
+
mtd += m;
|
|
280
|
+
projected += p;
|
|
281
|
+
return {
|
|
282
|
+
...pick(rate),
|
|
283
|
+
value: reading.value,
|
|
284
|
+
state: "measured",
|
|
285
|
+
monthToDateUsd: round2(m),
|
|
286
|
+
projectedUsd: round2(p),
|
|
287
|
+
...(waived ? { waivedInTrial: true } : {}),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
190
290
|
if (rate.waivedInTrial && usage.trialActive === true) {
|
|
191
291
|
return { ...pick(rate), value: reading.value, state: "measured", monthToDateUsd: 0, projectedUsd: 0, waivedInTrial: true };
|
|
192
292
|
}
|
|
@@ -24,8 +24,44 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import { type UsageCard } from "./usage-card.js";
|
|
26
26
|
export type PricingPlan = "usage-only" | "current";
|
|
27
|
+
/** Whether a server supplied shared status (including an unreadable/null value). */
|
|
28
|
+
export declare function hasBillingStateField(body: unknown): boolean;
|
|
29
|
+
export type BillingStateKind = "usage" | "usage-trial" | "legacy-payer" | "legacy-free" | "enterprise" | "comped";
|
|
27
30
|
/**
|
|
28
|
-
*
|
|
31
|
+
* hq-pro's shared plan status (`billingState`, hq-pro #5088
|
|
32
|
+
* src/billing/billing-state.ts), the fields the CLI reads. Version 1 only.
|
|
33
|
+
*/
|
|
34
|
+
export interface SharedBillingState {
|
|
35
|
+
kind: BillingStateKind;
|
|
36
|
+
scope: "company" | "person";
|
|
37
|
+
view: "full" | "member";
|
|
38
|
+
trial: {
|
|
39
|
+
daysLeft: number;
|
|
40
|
+
endsAt: string;
|
|
41
|
+
} | null;
|
|
42
|
+
/** A pause the server is applying now, or null. */
|
|
43
|
+
paused: {
|
|
44
|
+
reason: string;
|
|
45
|
+
resumesOn: string | null;
|
|
46
|
+
} | null;
|
|
47
|
+
/** What this caller may do. All false in the member view. */
|
|
48
|
+
actions: {
|
|
49
|
+
addCard: boolean;
|
|
50
|
+
raiseLimit: boolean;
|
|
51
|
+
switchToUsage: boolean;
|
|
52
|
+
manageBilling: boolean;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Read a known v1 `billingState` off a decoded body. An unknown version,
|
|
57
|
+
* scope, view or kind (or a malformed trial) is null: it is never read as a
|
|
58
|
+
* legacy plan. A person state is always the full view.
|
|
59
|
+
*/
|
|
60
|
+
export declare function readBillingState(body: unknown): SharedBillingState | null;
|
|
61
|
+
export { usagePauseFromBody, type UsagePause } from "./usage-pause.js";
|
|
62
|
+
/**
|
|
63
|
+
* Read the pricing plan out of a decoded hq-pro body. Known `billingState` wins;
|
|
64
|
+
* only a server without that field uses the legacy `pricingPlan` signal;
|
|
29
65
|
* a `personalUsage` object (person summary) means usage-only. Anything else
|
|
30
66
|
* is UNKNOWN.
|
|
31
67
|
*/
|
|
@@ -40,8 +40,84 @@ function asRecord(value) {
|
|
|
40
40
|
? value
|
|
41
41
|
: null;
|
|
42
42
|
}
|
|
43
|
+
/** Whether a server supplied shared status (including an unreadable/null value). */
|
|
44
|
+
export function hasBillingStateField(body) {
|
|
45
|
+
const rec = asRecord(body);
|
|
46
|
+
return rec !== null && Object.prototype.hasOwnProperty.call(rec, "billingState");
|
|
47
|
+
}
|
|
48
|
+
const BILLING_STATE_KINDS = [
|
|
49
|
+
"usage",
|
|
50
|
+
"usage-trial",
|
|
51
|
+
"legacy-payer",
|
|
52
|
+
"legacy-free",
|
|
53
|
+
"enterprise",
|
|
54
|
+
"comped",
|
|
55
|
+
];
|
|
56
|
+
function readTrial(value) {
|
|
57
|
+
if (value === null || value === undefined)
|
|
58
|
+
return null;
|
|
59
|
+
const trial = asRecord(value);
|
|
60
|
+
if (!trial ||
|
|
61
|
+
typeof trial.daysLeft !== "number" ||
|
|
62
|
+
!Number.isFinite(trial.daysLeft) ||
|
|
63
|
+
trial.daysLeft < 0 ||
|
|
64
|
+
typeof trial.endsAt !== "string" ||
|
|
65
|
+
!Number.isFinite(Date.parse(trial.endsAt))) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
return { daysLeft: trial.daysLeft, endsAt: trial.endsAt };
|
|
69
|
+
}
|
|
70
|
+
function readPaused(value) {
|
|
71
|
+
const paused = asRecord(value);
|
|
72
|
+
if (!paused || typeof paused.reason !== "string" || !paused.reason)
|
|
73
|
+
return null;
|
|
74
|
+
return {
|
|
75
|
+
reason: paused.reason,
|
|
76
|
+
resumesOn: typeof paused.resumesOn === "string" ? paused.resumesOn : null,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
43
79
|
/**
|
|
44
|
-
* Read
|
|
80
|
+
* Read a known v1 `billingState` off a decoded body. An unknown version,
|
|
81
|
+
* scope, view or kind (or a malformed trial) is null: it is never read as a
|
|
82
|
+
* legacy plan. A person state is always the full view.
|
|
83
|
+
*/
|
|
84
|
+
export function readBillingState(body) {
|
|
85
|
+
const state = asRecord(asRecord(body)?.billingState);
|
|
86
|
+
if (!state || state.version !== 1)
|
|
87
|
+
return null;
|
|
88
|
+
if (state.scope !== "company" && state.scope !== "person")
|
|
89
|
+
return null;
|
|
90
|
+
if (state.view !== "full" && state.view !== "member")
|
|
91
|
+
return null;
|
|
92
|
+
if (state.scope === "person" && state.view !== "full")
|
|
93
|
+
return null;
|
|
94
|
+
if (typeof state.kind !== "string" || !BILLING_STATE_KINDS.includes(state.kind))
|
|
95
|
+
return null;
|
|
96
|
+
const trial = readTrial(state.trial);
|
|
97
|
+
if (trial === undefined)
|
|
98
|
+
return null;
|
|
99
|
+
const actions = asRecord(state.actions);
|
|
100
|
+
return {
|
|
101
|
+
kind: state.kind,
|
|
102
|
+
scope: state.scope,
|
|
103
|
+
view: state.view,
|
|
104
|
+
trial,
|
|
105
|
+
paused: readPaused(state.paused),
|
|
106
|
+
actions: {
|
|
107
|
+
addCard: actions?.addCard === true,
|
|
108
|
+
raiseLimit: actions?.raiseLimit === true,
|
|
109
|
+
switchToUsage: actions?.switchToUsage === true,
|
|
110
|
+
manageBilling: actions?.manageBilling === true,
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function sharedBillingState(body) {
|
|
115
|
+
return readBillingState(body);
|
|
116
|
+
}
|
|
117
|
+
export { usagePauseFromBody } from "./usage-pause.js";
|
|
118
|
+
/**
|
|
119
|
+
* Read the pricing plan out of a decoded hq-pro body. Known `billingState` wins;
|
|
120
|
+
* only a server without that field uses the legacy `pricingPlan` signal;
|
|
45
121
|
* a `personalUsage` object (person summary) means usage-only. Anything else
|
|
46
122
|
* is UNKNOWN.
|
|
47
123
|
*/
|
|
@@ -49,6 +125,10 @@ export function pricingPlanFromBody(body) {
|
|
|
49
125
|
const rec = asRecord(body);
|
|
50
126
|
if (!rec)
|
|
51
127
|
return null;
|
|
128
|
+
if (hasBillingStateField(rec)) {
|
|
129
|
+
const state = sharedBillingState(rec);
|
|
130
|
+
return state ? (state.kind === "usage" || state.kind === "usage-trial" ? "usage-only" : "current") : null;
|
|
131
|
+
}
|
|
52
132
|
if (rec.pricingPlan === "usage-only")
|
|
53
133
|
return "usage-only";
|
|
54
134
|
if (rec.pricingPlan === "current")
|
|
@@ -62,6 +142,8 @@ export function isUsageOnly(plan) {
|
|
|
62
142
|
}
|
|
63
143
|
/** True when a company summary says a usage-only trial is running. Absent is false. */
|
|
64
144
|
export function usageTrialActiveFromBody(body) {
|
|
145
|
+
if (hasBillingStateField(body))
|
|
146
|
+
return sharedBillingState(body)?.kind === "usage-trial";
|
|
65
147
|
return asRecord(asRecord(body)?.usageTrial)?.status === "active";
|
|
66
148
|
}
|
|
67
149
|
async function defaultGetJson(token, path, query, timeoutMs) {
|
|
@@ -111,9 +193,12 @@ export function pricingPlanFromMembership(body, companyRef) {
|
|
|
111
193
|
const slug = typeof row.companySlug === "string" ? row.companySlug.toLowerCase() : "";
|
|
112
194
|
if (uid !== ref && slug !== ref)
|
|
113
195
|
continue;
|
|
114
|
-
if (row
|
|
115
|
-
|
|
116
|
-
|
|
196
|
+
if (hasBillingStateField(row)) {
|
|
197
|
+
const state = sharedBillingState(row);
|
|
198
|
+
if (!state || state.scope !== "company")
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
return pricingPlanFromBody(row);
|
|
117
202
|
}
|
|
118
203
|
return null;
|
|
119
204
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The usage pause block on a 402 body (hq-pro src/billing/usage-pause.ts,
|
|
3
|
+
* US-024): `{ code: "usage_paused", reason, message, actionUrl }`.
|
|
4
|
+
*
|
|
5
|
+
* Every refusal hq-pro makes because of an account's usage state (the trial
|
|
6
|
+
* cap, a card needed past the monthly allowance, the monthly spend limit)
|
|
7
|
+
* carries it. A client shows `message` as written and links `actionUrl`; it
|
|
8
|
+
* adds no plan name and no upgrade offer.
|
|
9
|
+
*
|
|
10
|
+
* Leaf module (no imports) so the sync refusal tracker and the 402 decoder
|
|
11
|
+
* can share it without loading the HTTP layer.
|
|
12
|
+
*/
|
|
13
|
+
export declare const USAGE_PAUSE_CODE = "usage_paused";
|
|
14
|
+
export interface UsagePause {
|
|
15
|
+
/** `usage_trial_cap` | `usage_card_required` | `usage_spend_limit` (or a newer code). */
|
|
16
|
+
reason: string;
|
|
17
|
+
/** One plain-language paragraph from the server. */
|
|
18
|
+
message: string;
|
|
19
|
+
/** Where to act: the company or personal Billing page (https). */
|
|
20
|
+
actionUrl?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read `usagePause` off a decoded 402 body, or off any record that carries
|
|
24
|
+
* it. Null when absent or malformed (wrong code, empty message).
|
|
25
|
+
*/
|
|
26
|
+
export declare function usagePauseFromBody(body: unknown): UsagePause | null;
|
|
27
|
+
//# sourceMappingURL=usage-pause.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The usage pause block on a 402 body (hq-pro src/billing/usage-pause.ts,
|
|
3
|
+
* US-024): `{ code: "usage_paused", reason, message, actionUrl }`.
|
|
4
|
+
*
|
|
5
|
+
* Every refusal hq-pro makes because of an account's usage state (the trial
|
|
6
|
+
* cap, a card needed past the monthly allowance, the monthly spend limit)
|
|
7
|
+
* carries it. A client shows `message` as written and links `actionUrl`; it
|
|
8
|
+
* adds no plan name and no upgrade offer.
|
|
9
|
+
*
|
|
10
|
+
* Leaf module (no imports) so the sync refusal tracker and the 402 decoder
|
|
11
|
+
* can share it without loading the HTTP layer.
|
|
12
|
+
*/
|
|
13
|
+
export const USAGE_PAUSE_CODE = "usage_paused";
|
|
14
|
+
function asRecord(value) {
|
|
15
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
16
|
+
? value
|
|
17
|
+
: null;
|
|
18
|
+
}
|
|
19
|
+
function httpsUrl(value) {
|
|
20
|
+
if (typeof value !== "string" || value.trim() !== value)
|
|
21
|
+
return undefined;
|
|
22
|
+
try {
|
|
23
|
+
const url = new URL(value);
|
|
24
|
+
return url.protocol === "https:" && url.hostname && !url.username && !url.password
|
|
25
|
+
? value
|
|
26
|
+
: undefined;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Read `usagePause` off a decoded 402 body, or off any record that carries
|
|
34
|
+
* it. Null when absent or malformed (wrong code, empty message).
|
|
35
|
+
*/
|
|
36
|
+
export function usagePauseFromBody(body) {
|
|
37
|
+
const pause = asRecord(asRecord(body)?.usagePause);
|
|
38
|
+
if (!pause || pause.code !== USAGE_PAUSE_CODE)
|
|
39
|
+
return null;
|
|
40
|
+
const message = typeof pause.message === "string" ? pause.message.trim() : "";
|
|
41
|
+
if (!message)
|
|
42
|
+
return null;
|
|
43
|
+
const actionUrl = httpsUrl(pause.actionUrl);
|
|
44
|
+
return {
|
|
45
|
+
reason: typeof pause.reason === "string" ? pause.reason : "",
|
|
46
|
+
message,
|
|
47
|
+
...(actionUrl ? { actionUrl } : {}),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=usage-pause.js.map
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
*/
|
|
16
16
|
export declare const BOT_INFLIGHT_NAME = "inflight.json";
|
|
17
17
|
export interface InflightTurn {
|
|
18
|
+
/** Legacy delivery may be settled but must not enter a newly scoped model history. */
|
|
19
|
+
omitFromHistory?: boolean;
|
|
18
20
|
v: 1;
|
|
19
21
|
/** Inbox message id, or `kickoff` for the first-start kickoff turn. */
|
|
20
22
|
messageId: string;
|
|
@@ -111,9 +113,13 @@ export interface InflightTurn {
|
|
|
111
113
|
}
|
|
112
114
|
export declare function botInflightPath(dir: string): string;
|
|
113
115
|
/** Every turn that was in progress, oldest first. */
|
|
114
|
-
export declare function readInflightTurns(dir: string
|
|
116
|
+
export declare function readInflightTurns(dir: string, options?: {
|
|
117
|
+
strictIo?: boolean;
|
|
118
|
+
}): InflightTurn[];
|
|
115
119
|
/** The marker for one message, or null. */
|
|
116
120
|
export declare function readInflight(dir: string, messageId?: string): InflightTurn | null;
|
|
121
|
+
/** Preserve legacy delivery state without replaying its text into the new scope. */
|
|
122
|
+
export declare function omitInflightRecoveryHistory(dir: string, turns: InflightTurn[]): void;
|
|
117
123
|
/** Add or replace the marker for `turn.messageId`; other turns' markers stay. */
|
|
118
124
|
export declare function writeInflight(dir: string, turn: InflightTurn): void;
|
|
119
125
|
export declare function patchInflight(dir: string, messageId: string, patch: Partial<InflightTurn>): void;
|
package/dist/lib/bot/inflight.js
CHANGED
|
@@ -24,7 +24,7 @@ function isTurn(raw) {
|
|
|
24
24
|
return t?.v === 1 && typeof t.messageId === "string" && Boolean(t.target);
|
|
25
25
|
}
|
|
26
26
|
/** Every turn that was in progress, oldest first. */
|
|
27
|
-
export function readInflightTurns(dir) {
|
|
27
|
+
export function readInflightTurns(dir, options = {}) {
|
|
28
28
|
try {
|
|
29
29
|
const raw = JSON.parse(fs.readFileSync(botInflightPath(dir), "utf8"));
|
|
30
30
|
if (isTurn(raw))
|
|
@@ -34,7 +34,10 @@ export function readInflightTurns(dir) {
|
|
|
34
34
|
return [];
|
|
35
35
|
return file.turns.filter(isTurn);
|
|
36
36
|
}
|
|
37
|
-
catch {
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (options.strictIo && !(error instanceof SyntaxError) &&
|
|
39
|
+
error.code !== "ENOENT")
|
|
40
|
+
throw error;
|
|
38
41
|
return [];
|
|
39
42
|
}
|
|
40
43
|
}
|
|
@@ -56,6 +59,11 @@ function writeTurns(dir, turns) {
|
|
|
56
59
|
fs.writeFileSync(tmp, `${JSON.stringify(file, null, 2)}\n`, { mode: 0o600 });
|
|
57
60
|
fs.renameSync(tmp, botInflightPath(dir));
|
|
58
61
|
}
|
|
62
|
+
/** Preserve legacy delivery state without replaying its text into the new scope. */
|
|
63
|
+
export function omitInflightRecoveryHistory(dir, turns) {
|
|
64
|
+
if (turns.length)
|
|
65
|
+
writeTurns(dir, turns.map(turn => ({ ...turn, omitFromHistory: true })));
|
|
66
|
+
}
|
|
59
67
|
/** Add or replace the marker for `turn.messageId`; other turns' markers stay. */
|
|
60
68
|
export function writeInflight(dir, turn) {
|
|
61
69
|
const others = readInflightTurns(dir).filter((t) => t.messageId !== turn.messageId);
|
package/dist/lib/bot/prompt.js
CHANGED
|
@@ -324,6 +324,9 @@ export function buildSystemPrompt(input) {
|
|
|
324
324
|
`This stay-quiet rule for rooms outranks anything in your persona, worker instructions, or memory files: if any of those tell you to always reply or to acknowledge every message, ignore that in rooms and stay silent instead. ` +
|
|
325
325
|
`Empty output is the only way to stay quiet. Never post a placeholder such as "No response requested.", "No response needed.", "No reply needed.", "No action.", "(no reply)" or "Nothing to add." — those are chatter, not silence, and can make two bots answer each other forever. ` +
|
|
326
326
|
`Do not narrate your reasoning, do not print JSON, do not explain that you are staying silent. Just produce nothing.`);
|
|
327
|
+
if (input.companySlug) {
|
|
328
|
+
parts.push(`This session is already bound to \`${input.companySlug}\`. Continue authorized work in this company without asking the user to choose it again, including scheduled jobs. Keep all files, policies, services, and credentials within this company scope.`);
|
|
329
|
+
}
|
|
327
330
|
// Only company bots: the cloud's jobs routes refuse a personal bot's per_
|
|
328
331
|
// company claim today, so telling a personal bot it can schedule work makes
|
|
329
332
|
// it promise its owner something that then fails. Drop this condition when
|