@indigoai-us/hq-cli 5.345.159 → 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 +24 -0
- package/dist/command-catalog.generated.d.ts +4 -4
- package/dist/command-catalog.generated.js +4 -4
- 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/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/cloud-provision.js +2 -2
- package/dist/commands/cloud.js +7 -7
- package/dist/commands/db-remote-deps.js +12 -1
- package/dist/commands/files.js +19 -7
- package/dist/commands/meetings.d.ts +4 -4
- package/dist/commands/meetings.js +17 -8
- package/dist/commands/skill.js +6 -6
- 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 +982 -426
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- 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/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/plan-limit-nag.d.ts +0 -16
- package/dist/lib/plan-limit-nag.js +17 -18
- package/dist/main.d.ts +0 -2
- package/dist/main.js +19 -57
- 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/package.json +8 -8
|
@@ -13,16 +13,16 @@
|
|
|
13
13
|
* hq billing checkout [--company <slug>] [--personal] — mint a card-capture link
|
|
14
14
|
* hq billing usage [--company <slug>] [--personal] — usage, projected bill,
|
|
15
15
|
* drivers (read-only)
|
|
16
|
-
* hq billing upgrade [--company <slug>] —
|
|
17
|
-
* (
|
|
16
|
+
* hq billing upgrade [--company <slug>] — the switch to usage
|
|
17
|
+
* pricing (earlier plans)
|
|
18
18
|
* hq billing sponsor … — ask a company to cover
|
|
19
|
-
* your
|
|
19
|
+
* your personal plan, or
|
|
20
20
|
* answer such a request
|
|
21
21
|
*
|
|
22
|
-
* Usage pricing (US-012): a company
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* checkout
|
|
22
|
+
* Usage pricing (US-012, US-024): a company on usage pricing has no plan to
|
|
23
|
+
* upgrade to; `hq billing upgrade` tells it so and points at
|
|
24
|
+
* `hq billing usage`. Every other company is pointed at the switch to usage
|
|
25
|
+
* pricing: the flat-plan checkout is closed, so the CLI never opens it.
|
|
26
26
|
*/
|
|
27
27
|
import { Command } from "commander";
|
|
28
28
|
interface DatabaseBilling {
|
package/dist/commands/billing.js
CHANGED
|
@@ -13,22 +13,22 @@
|
|
|
13
13
|
* hq billing checkout [--company <slug>] [--personal] — mint a card-capture link
|
|
14
14
|
* hq billing usage [--company <slug>] [--personal] — usage, projected bill,
|
|
15
15
|
* drivers (read-only)
|
|
16
|
-
* hq billing upgrade [--company <slug>] —
|
|
17
|
-
* (
|
|
16
|
+
* hq billing upgrade [--company <slug>] — the switch to usage
|
|
17
|
+
* pricing (earlier plans)
|
|
18
18
|
* hq billing sponsor … — ask a company to cover
|
|
19
|
-
* your
|
|
19
|
+
* your personal plan, or
|
|
20
20
|
* answer such a request
|
|
21
21
|
*
|
|
22
|
-
* Usage pricing (US-012): a company
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* checkout
|
|
22
|
+
* Usage pricing (US-012, US-024): a company on usage pricing has no plan to
|
|
23
|
+
* upgrade to; `hq billing upgrade` tells it so and points at
|
|
24
|
+
* `hq billing usage`. Every other company is pointed at the switch to usage
|
|
25
|
+
* pricing: the flat-plan checkout is closed, so the CLI never opens it.
|
|
26
26
|
*/
|
|
27
27
|
import chalk from "chalk";
|
|
28
28
|
import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
29
29
|
import { vaultApiFetch, getCompanyUid } from "../utils/vault-api.js";
|
|
30
30
|
import { mintPaymentLink } from "../utils/billing-gate.js";
|
|
31
|
-
import {
|
|
31
|
+
import { openUsageSwitch } from "../utils/team-upgrade.js";
|
|
32
32
|
import { registerBillingSponsorCommands } from "./billing-sponsor.js";
|
|
33
33
|
import { fetchCompanyPricingPlan, isUsageOnly, usageOnlyUpgradeLines, } from "../lib/billing/usage-only.js";
|
|
34
34
|
import { fetchUsageCard } from "../lib/billing/usage-card.js";
|
|
@@ -157,10 +157,10 @@ export function registerBillingCommand(program) {
|
|
|
157
157
|
});
|
|
158
158
|
billing
|
|
159
159
|
.command("upgrade")
|
|
160
|
-
.description("Open
|
|
160
|
+
.description("Open the switch to usage pricing for a company on an earlier plan (usage-pricing companies have nothing to change)")
|
|
161
161
|
.option("--company <slug>", "Company slug (resolves to companyUid)")
|
|
162
|
-
.option("--no-browser", "Print the
|
|
163
|
-
.option("--json", "Emit the
|
|
162
|
+
.option("--no-browser", "Print the URL instead of opening it")
|
|
163
|
+
.option("--json", "Emit the URL as JSON")
|
|
164
164
|
.action(async function (opts) {
|
|
165
165
|
try {
|
|
166
166
|
const token = await ensureCognitoToken();
|
|
@@ -177,8 +177,8 @@ export function registerBillingCommand(program) {
|
|
|
177
177
|
console.log(line);
|
|
178
178
|
return;
|
|
179
179
|
}
|
|
180
|
-
await
|
|
181
|
-
|
|
180
|
+
await openUsageSwitch({
|
|
181
|
+
companySlug: companyOf(this),
|
|
182
182
|
noBrowser: opts.browser === false,
|
|
183
183
|
json: opts.json,
|
|
184
184
|
});
|
|
@@ -484,8 +484,8 @@ async function defaultRunInitialSync(args) {
|
|
|
484
484
|
if (event.type === "plan-limit") {
|
|
485
485
|
failureCount += Math.max(1, event.count ?? 0, event.paths?.length ?? 0);
|
|
486
486
|
firstFailure ??= {
|
|
487
|
-
path: event.paths?.[0] ?? "
|
|
488
|
-
message: event.message ?? "
|
|
487
|
+
path: event.paths?.[0] ?? "new files",
|
|
488
|
+
message: event.message ?? "new files are paused for this account",
|
|
489
489
|
};
|
|
490
490
|
return;
|
|
491
491
|
}
|
package/dist/commands/cloud.js
CHANGED
|
@@ -1119,7 +1119,7 @@ export function registerCloudCommands(program) {
|
|
|
1119
1119
|
// and keep their exit status.
|
|
1120
1120
|
log(chalk.yellow(`\n⚠ Pushed ${result.filesUploaded} file(s) (${formatBytes(result.bytesUploaded)}, ` +
|
|
1121
1121
|
`${result.filesSkipped} skipped), but ${describeRefusedFiles(planLimitRefused)} ` +
|
|
1122
|
-
"refused
|
|
1122
|
+
"refused: new files are paused for this account."));
|
|
1123
1123
|
for (const line of renderPlanLimitRefusalLines(planLimitRefused)) {
|
|
1124
1124
|
log(chalk.yellow(line));
|
|
1125
1125
|
}
|
|
@@ -1811,7 +1811,7 @@ async function runPushAll(hqRoot, message, onConflict, skipPersonal, skipCompani
|
|
|
1811
1811
|
`${r.conflictPaths.length} conflict(s)` +
|
|
1812
1812
|
(r.aborted ? " — aborted" : "") +
|
|
1813
1813
|
(legRefused
|
|
1814
|
-
? ` — ${describeRefusedCount(legRefused)} refused
|
|
1814
|
+
? ` — ${describeRefusedCount(legRefused)} refused (new files paused)`
|
|
1815
1815
|
: ""));
|
|
1816
1816
|
if (legRefused) {
|
|
1817
1817
|
printPlanLimitRefusal(legRefused);
|
|
@@ -1828,7 +1828,7 @@ async function runPushAll(hqRoot, message, onConflict, skipPersonal, skipCompani
|
|
|
1828
1828
|
`${result.filesRefusedStale ?? 0} stale-refused; ` +
|
|
1829
1829
|
`${result.filesUploadFailed ?? 0} failed` +
|
|
1830
1830
|
(refusedTargets > 0
|
|
1831
|
-
? `; new files
|
|
1831
|
+
? `; new files paused on ${refusedTargets} target(s)`
|
|
1832
1832
|
: "");
|
|
1833
1833
|
console.log(errored > 0 ||
|
|
1834
1834
|
result.filesExcludedByScope > 0 ||
|
|
@@ -1973,7 +1973,7 @@ async function runNowSingle(hqRoot, company, personal, message, onConflict, mode
|
|
|
1973
1973
|
`${pushResult.filesDeleted} deleted, ${pushResult.filesExcludedByScope} scope-excluded` +
|
|
1974
1974
|
(pushResult.aborted ? " — aborted" : "") +
|
|
1975
1975
|
(pushRefused
|
|
1976
|
-
? ` — ${describeRefusedCount(pushRefused)} refused
|
|
1976
|
+
? ` — ${describeRefusedCount(pushRefused)} refused (new files paused)`
|
|
1977
1977
|
: ""));
|
|
1978
1978
|
if (pushRefused)
|
|
1979
1979
|
printPlanLimitRefusal(pushRefused, " ");
|
|
@@ -2105,7 +2105,7 @@ async function runNowSingle(hqRoot, company, personal, message, onConflict, mode
|
|
|
2105
2105
|
if (pushRefused) {
|
|
2106
2106
|
// The pull ran, but the push did not upload everything: no success line.
|
|
2107
2107
|
console.log(chalk.yellow(`\n⚠ Sync now finished, but ${describeRefusedFiles(pushRefused)} ` +
|
|
2108
|
-
"refused
|
|
2108
|
+
"refused: new files are paused for this account (listed above)."));
|
|
2109
2109
|
throw new SyncExitError(1, { health: "succeeded" });
|
|
2110
2110
|
}
|
|
2111
2111
|
if (pushResult.filesExcludedByScope > 0 ||
|
|
@@ -2159,9 +2159,9 @@ async function runNowAll(hqRoot, message, onConflict, modeAllOverride, skipPerso
|
|
|
2159
2159
|
// like a clean sync. Its per-target details were printed above.
|
|
2160
2160
|
if (refused.planLimitRefused) {
|
|
2161
2161
|
console.log(chalk.yellow(`\n⚠ Sync finished, but ${describeRefusedFiles(refused.planLimitRefused)} refused ` +
|
|
2162
|
-
"
|
|
2162
|
+
"because new files are paused for this account (listed under push --all above)."));
|
|
2163
2163
|
if (refused.planLimitRefused.upgradeUrl) {
|
|
2164
|
-
console.log(chalk.yellow(`
|
|
2164
|
+
console.log(chalk.yellow(` Billing: ${refused.planLimitRefused.upgradeUrl}`));
|
|
2165
2165
|
}
|
|
2166
2166
|
}
|
|
2167
2167
|
console.log(chalk.yellow("\n⚠ Sync finished, but one or more requested uploads were refused."));
|
|
@@ -74,7 +74,18 @@ export function describeRemoteError(error) {
|
|
|
74
74
|
return "Remote databases are not available yet.";
|
|
75
75
|
}
|
|
76
76
|
if (status === 402 || code === "PLAN_REQUIRED") {
|
|
77
|
-
|
|
77
|
+
// US-024: the server's usage pause message, else its own sentence. The
|
|
78
|
+
// CLI names no plan: the flat plans are closed.
|
|
79
|
+
const pause = error.usagePause;
|
|
80
|
+
if (pause && typeof pause.message === "string" && pause.message) {
|
|
81
|
+
return typeof pause.actionUrl === "string" && pause.actionUrl
|
|
82
|
+
? `${pause.message}\nBilling: ${pause.actionUrl}`
|
|
83
|
+
: pause.message;
|
|
84
|
+
}
|
|
85
|
+
const serverMessage = error.serverMessage;
|
|
86
|
+
if (typeof serverMessage === "string" && serverMessage)
|
|
87
|
+
return serverMessage;
|
|
88
|
+
return "Remote databases are not available on this company's current billing. Run `hq billing status` to check it.";
|
|
78
89
|
}
|
|
79
90
|
if (status === 403)
|
|
80
91
|
return "You are not a member of this company, or this app belongs to another company.";
|
package/dist/commands/files.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
+
import { aclLockedBy, describeAclLock } from "./acl-lock.js";
|
|
2
3
|
import open from "open";
|
|
3
4
|
import * as readline from "node:readline";
|
|
4
5
|
import { resolveVaultCredential } from "../utils/resolve-vault-credential.js";
|
|
@@ -489,17 +490,21 @@ export function registerFilesCommand(program) {
|
|
|
489
490
|
throw new Error('Invalid ACL tree response');
|
|
490
491
|
}
|
|
491
492
|
const row = tree.directRow;
|
|
493
|
+
// The most-specific row has no grants, so broader grants do not apply.
|
|
494
|
+
const lockedBy = aclLockedBy(tree);
|
|
492
495
|
if (opts.json) {
|
|
493
|
-
console.log(JSON.stringify({ ...tree, schemaVersion: 1, companyUid, exists: row !== null }, null, 2));
|
|
496
|
+
console.log(JSON.stringify({ ...tree, schemaVersion: 1, companyUid, exists: row !== null, lockedBy }, null, 2));
|
|
494
497
|
return;
|
|
495
498
|
}
|
|
496
499
|
// No own row AND nothing inherited or granted below — preserve the
|
|
497
500
|
// original "no ACL record" exit path.
|
|
498
|
-
if (!row && tree.inherited.length === 0 && tree.children.length === 0) {
|
|
501
|
+
if (!row && !lockedBy && tree.inherited.length === 0 && tree.children.length === 0) {
|
|
499
502
|
console.error(chalk.red(`No ACL record exists for '${canonicalPrefix}'`));
|
|
500
503
|
process.exit(1);
|
|
501
504
|
}
|
|
502
|
-
const aclStatus =
|
|
505
|
+
const aclStatus = lockedBy
|
|
506
|
+
? lockedBy.readUnknown ? "locked unless open" : "locked"
|
|
507
|
+
: row?.open ? "open" : "restricted";
|
|
503
508
|
console.log(chalk.green(`ACL for ${displayAclPattern(tree.prefix)} (${aclStatus})`));
|
|
504
509
|
if (isPrivateFolderPattern(tree.prefix)) {
|
|
505
510
|
console.log(chalk.gray(privateFolderExplanation(tree.prefix)));
|
|
@@ -507,9 +512,12 @@ export function registerFilesCommand(program) {
|
|
|
507
512
|
if (row) {
|
|
508
513
|
console.log(`Creator: ${row.creatorUid}`);
|
|
509
514
|
}
|
|
510
|
-
else {
|
|
515
|
+
else if (!lockedBy) {
|
|
511
516
|
console.log(chalk.gray("No direct ACL row — access flows from the inherited/descendant grants below."));
|
|
512
517
|
}
|
|
518
|
+
if (lockedBy) {
|
|
519
|
+
console.log(chalk.yellow(describeAclLock(lockedBy, displayAclPattern(lockedBy.path))));
|
|
520
|
+
}
|
|
513
521
|
if (tree.effectivePermission) {
|
|
514
522
|
console.log(`Your effective permission: ${tree.effectivePermission}`);
|
|
515
523
|
}
|
|
@@ -560,9 +568,9 @@ export function registerFilesCommand(program) {
|
|
|
560
568
|
const directEntries = tree.direct;
|
|
561
569
|
if (directEntries.length === 0) {
|
|
562
570
|
if (row?.open) {
|
|
563
|
-
console.log(chalk.gray("
|
|
571
|
+
console.log(chalk.gray("No grants on this row (open: broader grants still apply). All active members have at least read access."));
|
|
564
572
|
}
|
|
565
|
-
else if (row) {
|
|
573
|
+
else if (row && !lockedBy) {
|
|
566
574
|
console.log(chalk.gray("No explicit grants on this prefix — only creator has access."));
|
|
567
575
|
}
|
|
568
576
|
}
|
|
@@ -571,7 +579,11 @@ export function registerFilesCommand(program) {
|
|
|
571
579
|
printEntryTable(directEntries, false);
|
|
572
580
|
}
|
|
573
581
|
if (tree.inherited.length > 0) {
|
|
574
|
-
console.log(
|
|
582
|
+
console.log(lockedBy
|
|
583
|
+
? lockedBy.readUnknown
|
|
584
|
+
? `\nGranted on ancestor prefixes (apply only if ${displayAclPattern(lockedBy.path)} is open):`
|
|
585
|
+
: `\nGranted on ancestor prefixes (do not apply: locked by ${displayAclPattern(lockedBy.path)}):`
|
|
586
|
+
: "\nInherited (granted on an ancestor prefix):");
|
|
575
587
|
printEntryTable(tree.inherited, true);
|
|
576
588
|
}
|
|
577
589
|
if (tree.children.length > 0) {
|
|
@@ -3,10 +3,10 @@ import { type PricingPlan } from "../lib/billing/usage-only.js";
|
|
|
3
3
|
/** Upper bound on list pages one short-id lookup reads (per cursor chain). */
|
|
4
4
|
export declare const SHORT_ID_MAX_PAGES = 50;
|
|
5
5
|
/**
|
|
6
|
-
* The 402 copy for meeting bots. A usage
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* The 402 copy for meeting bots. A usage pause (US-024) prints the server's
|
|
7
|
+
* message and Billing link. A usage-only company (US-012) is told to add a
|
|
8
|
+
* card. Every other company gets the server's sentence under a line that
|
|
9
|
+
* names no plan: the flat plans are closed.
|
|
10
10
|
*/
|
|
11
11
|
export declare function meetingPaymentRequiredLines(body: Record<string, unknown>, resolvePlan?: () => Promise<PricingPlan | null>): Promise<string[]>;
|
|
12
12
|
export declare function registerMeetingsCommand(program: Command): void;
|
|
@@ -4,7 +4,7 @@ import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
|
4
4
|
import { vaultApiFetch, getCompanyUid, resolveCallerPersonUid, } from "../utils/vault-api.js";
|
|
5
5
|
import { isPlanGateError, planGateErrorFromPayload, } from "../utils/plan-gate-error.js";
|
|
6
6
|
import { mapWithConcurrency } from "./sync-manifest.js";
|
|
7
|
-
import { isUsageOnly, pricingPlanFromBody, resolveCompanyPricingPlan, usageOnlyCardRequiredLines, } from "../lib/billing/usage-only.js";
|
|
7
|
+
import { isUsageOnly, pricingPlanFromBody, resolveCompanyPricingPlan, usageOnlyCardRequiredLines, usagePauseFromBody, } from "../lib/billing/usage-only.js";
|
|
8
8
|
const LIVE_SNAPSHOT_STATUSES = new Set([
|
|
9
9
|
"joining",
|
|
10
10
|
"recording",
|
|
@@ -318,23 +318,32 @@ async function resolveShortId(token, prefix, query) {
|
|
|
318
318
|
process.exit(1);
|
|
319
319
|
}
|
|
320
320
|
/**
|
|
321
|
-
* The 402 copy for meeting bots. A usage
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
321
|
+
* The 402 copy for meeting bots. A usage pause (US-024) prints the server's
|
|
322
|
+
* message and Billing link. A usage-only company (US-012) is told to add a
|
|
323
|
+
* card. Every other company gets the server's sentence under a line that
|
|
324
|
+
* names no plan: the flat plans are closed.
|
|
325
325
|
*/
|
|
326
326
|
export async function meetingPaymentRequiredLines(body, resolvePlan = () => resolveCompanyPricingPlan()) {
|
|
327
327
|
const plan = pricingPlanFromBody(body) ?? (await resolvePlan().catch(() => null));
|
|
328
328
|
if (isUsageOnly(plan)) {
|
|
329
329
|
return usageOnlyCardRequiredLines("A meeting notetaker bot").map((line, i) => i === 0 ? chalk.red(line) : chalk.dim(line));
|
|
330
330
|
}
|
|
331
|
+
// US-024: a usage pause is the server's own answer: its message and link.
|
|
332
|
+
const pause = usagePauseFromBody(body);
|
|
333
|
+
if (pause) {
|
|
334
|
+
return [
|
|
335
|
+
chalk.red(pause.message),
|
|
336
|
+
...(pause.actionUrl ? [chalk.dim(`Billing: ${pause.actionUrl}`)] : []),
|
|
337
|
+
];
|
|
338
|
+
}
|
|
339
|
+
// The server's sentence when it sent one; otherwise copy that names no plan.
|
|
331
340
|
const detail = typeof body.message === "string"
|
|
332
341
|
? body.message
|
|
333
342
|
: typeof body.error === "string"
|
|
334
343
|
? body.error
|
|
335
|
-
: "
|
|
344
|
+
: "Run `hq billing status` or open the company's Billing page to check it.";
|
|
336
345
|
return [
|
|
337
|
-
chalk.red("Meeting notetaker bots
|
|
346
|
+
chalk.red("Meeting notetaker bots are not available on this company's current billing."),
|
|
338
347
|
chalk.dim(detail),
|
|
339
348
|
];
|
|
340
349
|
}
|
|
@@ -660,7 +669,7 @@ export function registerMeetingsCommand(program) {
|
|
|
660
669
|
const companySlug = meetings.opts().company;
|
|
661
670
|
// Always name a company. With no companyId the backend files the bot
|
|
662
671
|
// under Personal, which can never record, so an entitled user would
|
|
663
|
-
// get the
|
|
672
|
+
// get the payment-required refusal. Without --company this uses the
|
|
664
673
|
// caller's only active company, or asks them to pick one.
|
|
665
674
|
const query = {
|
|
666
675
|
companyId: await getCompanyUid(token, companySlug),
|
package/dist/commands/skill.js
CHANGED
|
@@ -28,7 +28,7 @@ import { AuthError } from "../utils/auth-error.js";
|
|
|
28
28
|
import { redactErrorText } from "../utils/redact-error-text.js";
|
|
29
29
|
import { stampVaultAccessDenied, vaultAccessDeniedDiagnostics, } from "../utils/vault-access-denied-error.js";
|
|
30
30
|
import { PlanGateError } from "../utils/plan-gate-error.js";
|
|
31
|
-
import { createPlanLimitRefusalTracker, trackingProgressRecorder, } from "../lib/billing/plan-limit-refusals.js";
|
|
31
|
+
import { createPlanLimitRefusalTracker, REFUSAL_FALLBACK_SENTENCE, trackingProgressRecorder, } from "../lib/billing/plan-limit-refusals.js";
|
|
32
32
|
export const SKILL_UID_PATTERN = /^skl_[A-Za-z0-9]+$/;
|
|
33
33
|
export const SKILL_SLUG_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
34
34
|
const COMPANY_SLUG_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
@@ -54,16 +54,16 @@ async function defaultSyncFile(input) {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
|
-
* The upload of a stamped skill file was refused
|
|
57
|
+
* The upload of a stamped skill file was refused (new files paused). Registration
|
|
58
58
|
* and the local stamp already succeeded, so say that on stderr, then return a
|
|
59
|
-
* typed plan gate for main.ts to render (reason and
|
|
59
|
+
* typed plan gate for main.ts to render (reason and billing link) and exit
|
|
60
60
|
* non-zero. Never "Skill ready … Sync: complete".
|
|
61
61
|
*/
|
|
62
62
|
function skillUploadRefused(skillUid, filePath, companySlug, refused) {
|
|
63
|
-
console.warn(chalk.yellow(`⚠ Skill ${skillUid} is stamped locally at '${filePath}', but the upload was refused
|
|
63
|
+
console.warn(chalk.yellow(`⚠ Skill ${skillUid} is stamped locally at '${filePath}', but the upload was refused: new files are paused for this account.`));
|
|
64
64
|
return new PlanGateError("PLAN_LIMIT_EXCEEDED", {
|
|
65
|
-
reason: `${refused.message ??
|
|
66
|
-
"The skill file was not uploaded. Once
|
|
65
|
+
reason: `${refused.message ?? REFUSAL_FALLBACK_SENTENCE} ` +
|
|
66
|
+
"The skill file was not uploaded. Once uploads resume, upload it with: " +
|
|
67
67
|
`hq sync push --company ${companySlug} '${filePath}'`,
|
|
68
68
|
...(refused.upgradeUrl ? { upgradeUrl: refused.upgradeUrl } : {}),
|
|
69
69
|
});
|
package/dist/commands/whoami.js
CHANGED
|
@@ -123,7 +123,7 @@ export function registerWhoamiCommand(program) {
|
|
|
123
123
|
.command('whoami')
|
|
124
124
|
.description('Show the currently authenticated user')
|
|
125
125
|
.option('--json', 'Output identity metadata as JSON (never tokens)')
|
|
126
|
-
.option('--company <slug>', 'Also report this company\'s
|
|
126
|
+
.option('--company <slug>', 'Also report this company\'s billing and usage notice')
|
|
127
127
|
.action(async (opts) => {
|
|
128
128
|
try {
|
|
129
129
|
// Effective source (same as auth status / ensureCognitoToken): env
|
package/dist/hook-engine-path.js
CHANGED
|
@@ -18,7 +18,7 @@ export async function runHookEnginePath(args) {
|
|
|
18
18
|
console.log('Usage: hq core hook-engine-path [--repair] [--for-registration]\nResolve and pin the installed native hook engine offline.\n--for-registration prints a path only when the launcher flag is enabled.');
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
|
-
const { installHookEngine, hookEngineRequired, HOOK_ENGINE_REPAIR } = await import('./lib/hook-engine/resolve.js');
|
|
21
|
+
const { installHookEngine, hookEngineRequired, removeHookEnginePointer, verifyHookEngine, HOOK_ENGINE_REPAIR } = await import('./lib/hook-engine/resolve.js');
|
|
22
22
|
if (args.includes('--for-registration')) {
|
|
23
23
|
const { launcherEnabled } = await import('./lib/hook-engine/registration.js');
|
|
24
24
|
if (!launcherEnabled()) {
|
|
@@ -27,8 +27,19 @@ export async function runHookEnginePath(args) {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
const binary = installHookEngine({ repair: args.includes('--repair') });
|
|
30
|
-
if (binary)
|
|
30
|
+
if (binary) {
|
|
31
|
+
// The printed path is one that runs and reports as an hq-hook engine.
|
|
32
|
+
// A binary that cannot answer its self-check must not be bridged, so the
|
|
33
|
+
// shell pointer is removed and the command exits non-zero.
|
|
34
|
+
const problem = verifyHookEngine(binary);
|
|
35
|
+
if (problem) {
|
|
36
|
+
removeHookEnginePointer();
|
|
37
|
+
console.error(`${problem}. Existing bash hooks remain active. Repair: ${HOOK_ENGINE_REPAIR}`);
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
31
41
|
console.log(binary);
|
|
42
|
+
}
|
|
32
43
|
else {
|
|
33
44
|
console.error(`Native hook engine missing; ${hookEngineRequired() ? 'repair before running hooks' : 'existing bash hooks remain active'}. Repair: ${HOOK_ENGINE_REPAIR}`);
|
|
34
45
|
process.exitCode = 1;
|