@indigoai-us/hq-cli 5.345.199 → 5.345.201
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 +40 -0
- package/assets/scaffold/core/scripts/lib/session-scope-capability.sh +6 -4
- package/dist/command-catalog.generated.d.ts +44 -0
- package/dist/command-catalog.generated.js +52 -0
- package/dist/commands/bot-runtime.d.ts +35 -0
- package/dist/commands/bot-runtime.js +115 -0
- package/dist/commands/bot.d.ts +53 -0
- package/dist/commands/bot.js +239 -30
- package/dist/commands/cloud.d.ts +39 -0
- package/dist/commands/cloud.js +173 -82
- package/dist/commands/creators.js +2 -1
- package/dist/commands/deploy-domains.js +8 -4
- package/dist/commands/library-remove.js +2 -1
- package/dist/commands/library.js +5 -4
- package/dist/commands/mesh.d.ts +0 -7
- package/dist/commands/mesh.js +5 -1
- package/dist/commands/sync-held.js +2 -2
- package/dist/lib/bot/api.d.ts +8 -0
- package/dist/lib/bot/api.js +9 -0
- package/dist/lib/bot/fleet-download.d.ts +27 -0
- package/dist/lib/bot/fleet-download.js +162 -0
- package/dist/lib/bot/fleet-local.d.ts +79 -2
- package/dist/lib/bot/fleet-local.js +246 -74
- package/dist/lib/bot/fleet-python.d.ts +2 -0
- package/dist/lib/bot/fleet-python.js +29 -0
- package/dist/lib/bot/jobs/source-report.d.ts +31 -0
- package/dist/lib/bot/jobs/source-report.js +276 -0
- package/dist/lib/bot/legacy-convert.d.ts +107 -4
- package/dist/lib/bot/legacy-convert.js +159 -13
- package/dist/lib/bot/legacy-provider-login.d.ts +34 -0
- package/dist/lib/bot/legacy-provider-login.js +144 -0
- package/dist/lib/bot/run.js +20 -1
- package/dist/lib/doctor/checks/bots.d.ts +4 -0
- package/dist/lib/doctor/checks/bots.js +33 -0
- package/dist/lib/lanes/mesh-events.js +3 -1
- package/dist/lib/lanes/store.js +7 -0
- package/dist/lib/lanes/types.d.ts +2 -0
- package/dist/lib/lanes/types.js +1 -0
- package/dist/lib/lanes/watcher.d.ts +0 -33
- package/dist/lib/lanes/watcher.js +7 -0
- package/dist/lib/mesh/api.js +2 -1
- package/dist/lib/mesh/live/daemon/run.d.ts +0 -10
- package/dist/lib/mesh/live/daemon/run.js +3 -0
- package/dist/lib/mesh/live/emit.d.ts +2 -11
- package/dist/lib/mesh/live/emit.js +20 -2
- package/dist/lib/mesh/live/flush.d.ts +2 -0
- package/dist/lib/mesh/live/flush.js +34 -3
- package/dist/lib/mesh/live/reporting-privacy.d.ts +7 -0
- package/dist/lib/mesh/live/reporting-privacy.js +47 -0
- package/dist/lib/work-context/company.d.ts +2 -3
- package/dist/lib/work-context/company.js +4 -22
- package/dist/secrets-env-action.js +5 -2
- package/dist/utils/cognito-callback-port.d.ts +2 -0
- package/dist/utils/cognito-callback-port.js +10 -5
- package/dist/utils/cognito-config.js +6 -1
- package/dist/utils/cognito-session.js +6 -1
- package/dist/utils/interactive-caller.d.ts +8 -0
- package/dist/utils/interactive-caller.js +10 -0
- package/dist/utils/login-browser.js +2 -1
- package/dist/utils/resident-package-preload.js +57 -4
- package/package.json +7 -7
package/dist/commands/cloud.js
CHANGED
|
@@ -22,7 +22,7 @@ import * as path from "path";
|
|
|
22
22
|
import { share, getStateDir, listJournals, createIgnoreFilter, VaultClient, computePersonalVaultPaths, PERSONAL_VAULT_JOURNAL_SLUG, resolvePullScope, readSyncProgress, syncDoctor, } from "@indigoai-us/hq-cloud";
|
|
23
23
|
import { syncLeg } from "../utils/sync-operation-lock.js";
|
|
24
24
|
import { SYNC_WATCHDOG_EXIT_CODE, SYNC_WATCHDOG_REPORT_GRACE_MS, SyncWatchdogTimeoutError, createSnapshotProgressProbe, resolveSyncWatchdogLimits, withSyncWatchdog, } from "../utils/sync-watchdog.js";
|
|
25
|
-
import { DEFAULT_HQ_ROOT, ensureCognitoToken, buildVaultConfig, IdentitySwapRefusal, cognitoCachePrincipalUid, isDefinitivePersonAuthRejection, isIdentitySwapRefusal, loadCachedTokens, } from "../utils/cognito-session.js";
|
|
25
|
+
import { DEFAULT_HQ_ROOT, ensureCognitoToken, buildVaultConfig, IdentitySwapRefusal, cognitoCachePrincipalUid, isDefinitivePersonAuthRejection, isIdentitySwapRefusal, loadCachedTokens, refreshCachedSession, } from "../utils/cognito-session.js";
|
|
26
26
|
import { resolveUploadAuthor } from "../utils/upload-author.js";
|
|
27
27
|
import { companyFolderExceedsThreshold, emitNarrowHint, isStrictRefusal, resolveBannerLevel, resolveNarrowHintPresentationLevel, resolveNarrowHintMinBytes, } from "../lib/narrow-hint-banner.js";
|
|
28
28
|
import { beginSyncHealthReport, classifySyncFailure, } from "../utils/client-health.js";
|
|
@@ -90,6 +90,80 @@ export class SyncSessionExpiredError extends Error {
|
|
|
90
90
|
this.name = "SyncSessionExpiredError";
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
+
/** Shown when the vault rejected the session even after a forced refresh. */
|
|
94
|
+
export const SYNC_SESSION_REJECTED_MESSAGE = "The HQ server rejected your session even after it was refreshed, so the saved sign-in is no longer accepted. `hq auth status` only checks the saved expiry, not the server. Run `hq login`, then rerun the sync.";
|
|
95
|
+
/** Reason text when token acquisition does not settle within the bound. */
|
|
96
|
+
export const SYNC_TOKEN_TIMEOUT_REASON = "timed out waiting for the sign-in service";
|
|
97
|
+
/** Longest a single token acquisition or refresh may take before the sync fails. */
|
|
98
|
+
export const SYNC_TOKEN_TIMEOUT_MS = 45_000;
|
|
99
|
+
/**
|
|
100
|
+
* The vault rejected the session after a forced refresh had already replaced
|
|
101
|
+
* the saved token. Retrying again cannot help; the user has to sign in again.
|
|
102
|
+
*/
|
|
103
|
+
export class SyncSessionRejectedError extends Error {
|
|
104
|
+
constructor() {
|
|
105
|
+
super(SYNC_SESSION_REJECTED_MESSAGE);
|
|
106
|
+
this.name = "SyncSessionRejectedError";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** Reject with a named "unavailable" error when `promise` has not settled within `ms`. */
|
|
110
|
+
function withTokenTimeout(promise, ms) {
|
|
111
|
+
let timer;
|
|
112
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
113
|
+
timer = setTimeout(() => reject(new SyncSessionExpiredError("unavailable", SYNC_TOKEN_TIMEOUT_REASON)), ms);
|
|
114
|
+
timer.unref?.();
|
|
115
|
+
});
|
|
116
|
+
return Promise.race([promise, timeout]).finally(() => {
|
|
117
|
+
if (timer !== undefined)
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* feedback_3d752910: the vault can reject a token that still looks valid in the
|
|
123
|
+
* local cache (revoked or replaced server-side). The cached expiry check never
|
|
124
|
+
* notices, and `hq auth refresh` is a no-op for a valid-looking token, so every
|
|
125
|
+
* sync kept presenting the same rejected token. On a vault 401, force one real
|
|
126
|
+
* refresh and rerun the sync once. A second rejection is reported as a rejected
|
|
127
|
+
* session (sign in again), never retried again.
|
|
128
|
+
*/
|
|
129
|
+
export async function runWithRejectedSessionRecovery(run, deps = {}) {
|
|
130
|
+
const forceRefresh = deps.forceRefresh ?? (() => refreshCachedSession({ force: true }));
|
|
131
|
+
try {
|
|
132
|
+
return await run();
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
if (!isVaultAuthRejection(err))
|
|
136
|
+
throw err;
|
|
137
|
+
// The getter already failed to produce a token; a forced refresh would hit
|
|
138
|
+
// the same failure. Report the real cause.
|
|
139
|
+
const firstFailure = deps.recordedFailure?.();
|
|
140
|
+
if (firstFailure)
|
|
141
|
+
throw firstFailure;
|
|
142
|
+
await deps.onRetry?.();
|
|
143
|
+
let refreshed;
|
|
144
|
+
try {
|
|
145
|
+
refreshed = await withTokenTimeout(forceRefresh(), SYNC_TOKEN_TIMEOUT_MS);
|
|
146
|
+
}
|
|
147
|
+
catch (refreshErr) {
|
|
148
|
+
throw refreshErr instanceof SyncSessionExpiredError
|
|
149
|
+
? refreshErr
|
|
150
|
+
: classifyRefreshFailure(refreshErr);
|
|
151
|
+
}
|
|
152
|
+
if (!refreshed.refreshed) {
|
|
153
|
+
throw classifyRefreshFailure(new Error(refreshed.reason ?? "refresh failed"));
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
return await run();
|
|
157
|
+
}
|
|
158
|
+
catch (retryErr) {
|
|
159
|
+
if (isVaultAuthRejection(retryErr)) {
|
|
160
|
+
// A getter failure surfaced as a 401 is not a server rejection.
|
|
161
|
+
throw deps.recordedFailure?.() ?? new SyncSessionRejectedError();
|
|
162
|
+
}
|
|
163
|
+
throw retryErr;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
93
167
|
/**
|
|
94
168
|
* Per-config record of the last token-function failure. hq-cloud's one-shot
|
|
95
169
|
* 401 retry swallows getter errors and rethrows the original 401, so
|
|
@@ -128,6 +202,7 @@ function classifyRefreshFailure(err) {
|
|
|
128
202
|
*/
|
|
129
203
|
export function buildSyncVaultConfig(initialToken, deps = {}) {
|
|
130
204
|
const ensureToken = deps.ensureToken ?? (() => ensureCognitoToken({ interactive: false }));
|
|
205
|
+
const tokenTimeoutMs = deps.tokenTimeoutMs ?? SYNC_TOKEN_TIMEOUT_MS;
|
|
131
206
|
const runPrincipal = cognitoCachePrincipalUid(initialToken);
|
|
132
207
|
// A start token with no sub/entityUid cannot be bound to a principal, and a
|
|
133
208
|
// null binding would accept any later identity-less token. Refuse up front.
|
|
@@ -142,7 +217,7 @@ export function buildSyncVaultConfig(initialToken, deps = {}) {
|
|
|
142
217
|
throw refused;
|
|
143
218
|
let token;
|
|
144
219
|
try {
|
|
145
|
-
token = await ensureToken();
|
|
220
|
+
token = await withTokenTimeout(ensureToken(), tokenTimeoutMs);
|
|
146
221
|
}
|
|
147
222
|
catch (err) {
|
|
148
223
|
const failure = isIdentitySwapRefusal(err) ? err : classifyRefreshFailure(err);
|
|
@@ -177,7 +252,9 @@ function isVaultAuthRejection(err) {
|
|
|
177
252
|
* CLI are released independently.
|
|
178
253
|
*/
|
|
179
254
|
export function formatSyncFailure(err, ctx = {}) {
|
|
180
|
-
if (err instanceof SyncSessionExpiredError ||
|
|
255
|
+
if (err instanceof SyncSessionExpiredError ||
|
|
256
|
+
err instanceof SyncSessionRejectedError ||
|
|
257
|
+
isIdentitySwapRefusal(err)) {
|
|
181
258
|
return err.message;
|
|
182
259
|
}
|
|
183
260
|
if (isVaultAuthRejection(err)) {
|
|
@@ -1622,87 +1699,101 @@ export function registerCloudCommands(program) {
|
|
|
1622
1699
|
console.log(chalk.bold("\nHQ Sync — Pull"));
|
|
1623
1700
|
console.log(` HQ root: ${options.hqRoot}`);
|
|
1624
1701
|
console.log(` Company: ${options.company ?? "(from .hq/config.json)"}\n`);
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
nudgeExceedsSize &&
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1702
|
+
// feedback_3d752910: one forced refresh and rerun when the vault
|
|
1703
|
+
// rejects a token that still looks valid in the local cache.
|
|
1704
|
+
await runWithRejectedSessionRecovery(async () => {
|
|
1705
|
+
runVaultConfig = undefined;
|
|
1706
|
+
const accessToken = await withTokenTimeout(ensureSyncCognitoToken(), SYNC_TOKEN_TIMEOUT_MS);
|
|
1707
|
+
const vaultConfig = buildSyncVaultConfig(accessToken);
|
|
1708
|
+
runVaultConfig = vaultConfig;
|
|
1709
|
+
// US-011 (2026-05-21 fix): resolve the caller's sync-config for
|
|
1710
|
+
// the targeted membership BEFORE the pull, so we can (a) emit the
|
|
1711
|
+
// narrow-hint banner after success if still on all-mode and
|
|
1712
|
+
// (b) respect strict-mode refusal mirror of the --all + sync-now
|
|
1713
|
+
// paths. Failure to resolve degrades silently — pull still works,
|
|
1714
|
+
// banner just stays quiet (same as the catch in runPullAll).
|
|
1715
|
+
const narrowHintLevel = resolveBannerLevel();
|
|
1716
|
+
const pullClient = new VaultClient(vaultConfig);
|
|
1717
|
+
const { resolvedCompanyUid, resolvedMode } = await resolvePerCompanyPullPlan(pullClient, options.company);
|
|
1718
|
+
// DEV-1768: resolve the REAL effective scope (mode + prefixSet) and
|
|
1719
|
+
// thread it into the pull below — not just the banner. Best-effort;
|
|
1720
|
+
// degrades to "all" inside the resolver on any failure.
|
|
1721
|
+
const pullScope = await resolveCliPullScope(pullClient, options.company, options.hqRoot);
|
|
1722
|
+
// Size gate: only a large local folder is nudged / strict-refused.
|
|
1723
|
+
const nudgeExceedsSize = resolvedMode === "all" &&
|
|
1724
|
+
resolvedCompanyUid !== undefined &&
|
|
1725
|
+
narrowNudgeExceedsSize(options.hqRoot, options.company);
|
|
1726
|
+
const strictRefusal = resolvedMode === "all" &&
|
|
1727
|
+
nudgeExceedsSize &&
|
|
1728
|
+
isStrictRefusal(resolvedMode, narrowHintLevel) &&
|
|
1729
|
+
options.modeAll !== true;
|
|
1730
|
+
const bannerLevel = resolveNarrowHintPresentationLevel(narrowHintLevel, strictRefusal);
|
|
1731
|
+
// Strict-mode refusal: matches runPullAll + runNowSingle behavior.
|
|
1732
|
+
// Default banner level is 'hint' which never triggers refusal —
|
|
1733
|
+
// wired now so a future release can flip the default to 'strict'
|
|
1734
|
+
// (still size-gated) without re-touching this command.
|
|
1735
|
+
if (strictRefusal &&
|
|
1736
|
+
resolvedMode === "all" &&
|
|
1737
|
+
resolvedCompanyUid) {
|
|
1738
|
+
emitNarrowHint({
|
|
1739
|
+
companyUid: resolvedCompanyUid,
|
|
1740
|
+
syncMode: resolvedMode,
|
|
1741
|
+
level: bannerLevel,
|
|
1742
|
+
});
|
|
1743
|
+
console.error(chalk.red("\n✗ Pull refused: strict narrow-hint mode is on and this " +
|
|
1744
|
+
"company's local folder has grown large. Run `hq sync narrow --apply` " +
|
|
1745
|
+
"to migrate, or re-run with --mode-all."));
|
|
1746
|
+
process.exit(1);
|
|
1747
|
+
}
|
|
1748
|
+
const excludePrefixes = readScopeExcludePrefixes(pullScope);
|
|
1749
|
+
syncHealth = beginSyncHealthReport();
|
|
1750
|
+
const result = await syncLeg({
|
|
1751
|
+
company: options.company,
|
|
1752
|
+
onConflict: options.onConflict,
|
|
1753
|
+
vaultConfig,
|
|
1754
|
+
hqRoot: options.hqRoot,
|
|
1755
|
+
...(pullScope?.syncMode !== undefined
|
|
1756
|
+
? { syncMode: pullScope.syncMode }
|
|
1757
|
+
: {}),
|
|
1758
|
+
...(pullScope?.prefixSet !== undefined
|
|
1759
|
+
? { prefixSet: pullScope.prefixSet }
|
|
1760
|
+
: {}),
|
|
1761
|
+
// Sessions-exclusion: never materialize excluded prefixes even for
|
|
1762
|
+
// a wide-STS owner.
|
|
1763
|
+
...(excludePrefixes ? { excludePrefixes } : {}),
|
|
1764
|
+
...(options.forceScopeShrink ? { forceScopeShrink: true } : {}),
|
|
1661
1765
|
});
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
company: options.company,
|
|
1671
|
-
onConflict: options.onConflict,
|
|
1672
|
-
vaultConfig,
|
|
1673
|
-
hqRoot: options.hqRoot,
|
|
1674
|
-
...(pullScope?.syncMode !== undefined
|
|
1675
|
-
? { syncMode: pullScope.syncMode }
|
|
1676
|
-
: {}),
|
|
1677
|
-
...(pullScope?.prefixSet !== undefined
|
|
1678
|
-
? { prefixSet: pullScope.prefixSet }
|
|
1679
|
-
: {}),
|
|
1680
|
-
// Sessions-exclusion: never materialize excluded prefixes even for
|
|
1681
|
-
// a wide-STS owner.
|
|
1682
|
-
...(excludePrefixes ? { excludePrefixes } : {}),
|
|
1683
|
-
...(options.forceScopeShrink ? { forceScopeShrink: true } : {}),
|
|
1684
|
-
});
|
|
1685
|
-
if (result.aborted) {
|
|
1686
|
-
console.log(chalk.yellow(`\n⚠ Pull aborted (${result.filesDownloaded} downloaded, ${result.filesSkipped} skipped, ${result.conflicts} conflicts)`));
|
|
1687
|
-
await syncHealth.failed();
|
|
1688
|
-
process.exit(1);
|
|
1689
|
-
}
|
|
1690
|
-
recordFirstWorkBearingSync({
|
|
1691
|
-
filesDownloaded: result.filesDownloaded,
|
|
1692
|
-
aborted: result.aborted,
|
|
1693
|
-
});
|
|
1694
|
-
console.log(chalk.green(`\n✓ Pulled ${result.filesDownloaded} file(s) (${formatBytes(result.bytesDownloaded)}, ${result.filesSkipped} skipped, ${result.conflicts} conflicts)`));
|
|
1695
|
-
// Emit the hint banner after success so it appears alongside the
|
|
1696
|
-
// summary line. Mirrors the wiring in runPullAll and runNowSingle.
|
|
1697
|
-
// Size-gated: only a large local folder is nudged.
|
|
1698
|
-
if (resolvedMode === "all" && nudgeExceedsSize && resolvedCompanyUid) {
|
|
1699
|
-
emitNarrowHint({
|
|
1700
|
-
companyUid: resolvedCompanyUid,
|
|
1701
|
-
syncMode: resolvedMode,
|
|
1702
|
-
level: bannerLevel,
|
|
1766
|
+
if (result.aborted) {
|
|
1767
|
+
console.log(chalk.yellow(`\n⚠ Pull aborted (${result.filesDownloaded} downloaded, ${result.filesSkipped} skipped, ${result.conflicts} conflicts)`));
|
|
1768
|
+
await syncHealth.failed();
|
|
1769
|
+
process.exit(1);
|
|
1770
|
+
}
|
|
1771
|
+
recordFirstWorkBearingSync({
|
|
1772
|
+
filesDownloaded: result.filesDownloaded,
|
|
1773
|
+
aborted: result.aborted,
|
|
1703
1774
|
});
|
|
1704
|
-
|
|
1705
|
-
|
|
1775
|
+
console.log(chalk.green(`\n✓ Pulled ${result.filesDownloaded} file(s) (${formatBytes(result.bytesDownloaded)}, ${result.filesSkipped} skipped, ${result.conflicts} conflicts)`));
|
|
1776
|
+
// Emit the hint banner after success so it appears alongside the
|
|
1777
|
+
// summary line. Mirrors the wiring in runPullAll and runNowSingle.
|
|
1778
|
+
// Size-gated: only a large local folder is nudged.
|
|
1779
|
+
if (resolvedMode === "all" && nudgeExceedsSize && resolvedCompanyUid) {
|
|
1780
|
+
emitNarrowHint({
|
|
1781
|
+
companyUid: resolvedCompanyUid,
|
|
1782
|
+
syncMode: resolvedMode,
|
|
1783
|
+
level: bannerLevel,
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
await syncHealth.succeeded();
|
|
1787
|
+
}, {
|
|
1788
|
+
recordedFailure: () => syncAuthFailureFor(runVaultConfig),
|
|
1789
|
+
onRetry: async () => {
|
|
1790
|
+
console.error(chalk.yellow("HQ rejected the session; refreshing once and retrying."));
|
|
1791
|
+
// Close the first attempt's health report; the rerun opens its own.
|
|
1792
|
+
const first = syncHealth;
|
|
1793
|
+
syncHealth = undefined;
|
|
1794
|
+
await first?.failed("AUTH_EXPIRED");
|
|
1795
|
+
},
|
|
1796
|
+
});
|
|
1706
1797
|
}
|
|
1707
1798
|
catch (err) {
|
|
1708
1799
|
if (syncHealth)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
3
3
|
import { vaultApiFetch } from "../utils/vault-api.js";
|
|
4
|
+
import { gatedInteractiveLogin } from "../utils/interactive-caller.js";
|
|
4
5
|
/**
|
|
5
6
|
* Testable core. Submits the application and maps the server response to a
|
|
6
7
|
* single human-readable message. Throws on hard failures.
|
|
@@ -46,7 +47,7 @@ export function registerCreatorsCommand(program) {
|
|
|
46
47
|
.action(async (options) => {
|
|
47
48
|
try {
|
|
48
49
|
const result = await runCreatorApply({ reason: options.reason, handle: options.handle }, {
|
|
49
|
-
getAccessToken: () => ensureCognitoToken({ interactive:
|
|
50
|
+
getAccessToken: () => ensureCognitoToken({ interactive: gatedInteractiveLogin() }),
|
|
50
51
|
post: (token, body) => vaultApiFetch({
|
|
51
52
|
token,
|
|
52
53
|
path: "/v1/creators/request-access",
|
|
@@ -907,12 +907,16 @@ function availabilityLabel(kind) {
|
|
|
907
907
|
function isOfferable(kind) {
|
|
908
908
|
return kind === "available" || kind === "premium";
|
|
909
909
|
}
|
|
910
|
+
/** hq-pro sends the registrar currency as lowercase "usd". Either case is US dollars. */
|
|
911
|
+
function isUsdCurrency(currency) {
|
|
912
|
+
return typeof currency === "string" && currency.toUpperCase() === "USD";
|
|
913
|
+
}
|
|
910
914
|
function moneyFromCents(cents, currency) {
|
|
911
915
|
if (typeof cents !== "number" || !Number.isFinite(cents) || cents < 0 || !Number.isInteger(cents))
|
|
912
916
|
return undefined;
|
|
913
917
|
const amount = cents / 100;
|
|
914
918
|
const text = Number.isInteger(amount) ? `$${amount}` : `$${amount.toFixed(2)}`;
|
|
915
|
-
if (typeof currency === "string" && currency !== "" && currency
|
|
919
|
+
if (typeof currency === "string" && currency !== "" && !isUsdCurrency(currency))
|
|
916
920
|
return `${text} ${currency}`;
|
|
917
921
|
return text;
|
|
918
922
|
}
|
|
@@ -1053,7 +1057,7 @@ function registrarCurrency(result) {
|
|
|
1053
1057
|
/** First-year amount comparable to --price. A quote in another currency is not one. */
|
|
1054
1058
|
function usdFirstYearAmount(result) {
|
|
1055
1059
|
const currency = registrarCurrency(result);
|
|
1056
|
-
if (currency !== undefined && currency
|
|
1060
|
+
if (currency !== undefined && !isUsdCurrency(currency))
|
|
1057
1061
|
return undefined;
|
|
1058
1062
|
const registrar = result.registrarPrice;
|
|
1059
1063
|
const block = registrar && typeof registrar === "object" ? registrar : undefined;
|
|
@@ -1067,7 +1071,7 @@ function usdFirstYearAmount(result) {
|
|
|
1067
1071
|
}
|
|
1068
1072
|
function assertMatchingUsdPrice(match, quote, expectedPriceUsd) {
|
|
1069
1073
|
const currency = registrarCurrency(match);
|
|
1070
|
-
if (currency !== undefined && currency
|
|
1074
|
+
if (currency !== undefined && !isUsdCurrency(currency)) {
|
|
1071
1075
|
const shown = quote.firstYear
|
|
1072
1076
|
? `The registrar's first-year price is ${quote.firstYear}, not US dollars.`
|
|
1073
1077
|
: "The registrar's first-year price is not in US dollars.";
|
|
@@ -1362,7 +1366,7 @@ async function renewAction(domainArg, autoRenew, options) {
|
|
|
1362
1366
|
const cents = detail.renewalPriceCents;
|
|
1363
1367
|
const currency = detail.currency;
|
|
1364
1368
|
const amount = moneyFromCents(cents, currency);
|
|
1365
|
-
if (typeof cents !== "number" || !Number.isSafeInteger(cents) || cents < 0 || currency
|
|
1369
|
+
if (typeof cents !== "number" || !Number.isSafeInteger(cents) || cents < 0 || !isUsdCurrency(currency) || !amount || !terms) {
|
|
1366
1370
|
throw new DeployDomainsError("Could not load complete USD renewal terms. Nothing was changed. Try again later.", "RENEWAL_TERMS_UNAVAILABLE");
|
|
1367
1371
|
}
|
|
1368
1372
|
const nextRenewal = detail.nextRenewalAt ?? detail.expiresAt;
|
|
@@ -44,6 +44,7 @@ import * as readline from "node:readline";
|
|
|
44
44
|
import chalk from "chalk";
|
|
45
45
|
import { isCloudPlaceholderName, parseCloudPlaceholder } from "@indigoai-us/hq-cloud";
|
|
46
46
|
import { OFFLOAD_PREFIX, PLACEHOLDER_SUFFIX, digestFile, resolvePlaceholder } from "./storage-offload.js";
|
|
47
|
+
import { gatedInteractiveLogin } from "../utils/interactive-caller.js";
|
|
47
48
|
/** A company library blob key: `.hq-offload/blobs/<64 lowercase hex>`. */
|
|
48
49
|
const BLOB_KEY_PATTERN = /^\.hq-offload\/blobs\/([0-9a-f]{64})$/;
|
|
49
50
|
/** hq-pro caps an asset search at 50 hits. */
|
|
@@ -637,7 +638,7 @@ export function registerLibraryRemoveCommand(library) {
|
|
|
637
638
|
import("../utils/manifest.js"),
|
|
638
639
|
]);
|
|
639
640
|
const credential = await resolveVaultCredentialForCapability("files:write", "library rm", {
|
|
640
|
-
interactive:
|
|
641
|
+
interactive: gatedInteractiveLogin(),
|
|
641
642
|
});
|
|
642
643
|
const { token } = credential;
|
|
643
644
|
const apiKey = credential.kind === "api-key";
|
package/dist/commands/library.js
CHANGED
|
@@ -41,6 +41,7 @@ import { defaultLibraryIo, resolveLibrarySyncScope, runLibraryAdd, runLibraryTag
|
|
|
41
41
|
import { captionAddedAsset, extractCaptionFrames, runLibraryDescribe, runLocalClaude } from "./library-caption.js";
|
|
42
42
|
import { LIBRARY_LINK_DEFAULT_MINUTES, LIBRARY_LINK_MAX_MINUTES, runLibraryLink } from "./library-link.js";
|
|
43
43
|
import { registerLibraryRemoveCommand } from "./library-remove.js";
|
|
44
|
+
import { gatedInteractiveLogin } from "../utils/interactive-caller.js";
|
|
44
45
|
/** Mirrors hq-pro's MAX_ASSET_SEARCH_TAGS / tag length bound. */
|
|
45
46
|
export const MAX_LIBRARY_SEARCH_TAGS = 20;
|
|
46
47
|
const MAX_TAG_LENGTH = 100;
|
|
@@ -445,7 +446,7 @@ export function registerLibraryCommand(program) {
|
|
|
445
446
|
};
|
|
446
447
|
const result = await runLibrarySearch(queryParts.join(" "), searchOptions, {
|
|
447
448
|
getToken: async () => (await resolveVaultCredentialForCapability("search:query", "library search", {
|
|
448
|
-
interactive:
|
|
449
|
+
interactive: gatedInteractiveLogin(),
|
|
449
450
|
})).token,
|
|
450
451
|
post: (token, body) => vaultApiFetch({ token, path: "/v1/search", method: "POST", body }),
|
|
451
452
|
searchLocal: (query, opts) => searchLocalAssets(query, opts, defaultLocalEnv()),
|
|
@@ -476,7 +477,7 @@ export function registerLibraryCommand(program) {
|
|
|
476
477
|
.action(async (sources, options) => {
|
|
477
478
|
try {
|
|
478
479
|
const credential = await resolveVaultCredentialForCapability("files:write", "library add", {
|
|
479
|
-
interactive:
|
|
480
|
+
interactive: gatedInteractiveLogin(),
|
|
480
481
|
});
|
|
481
482
|
const { token } = credential;
|
|
482
483
|
const hqRoot = findHqRoot();
|
|
@@ -561,7 +562,7 @@ export function registerLibraryCommand(program) {
|
|
|
561
562
|
.action(async (target, options) => {
|
|
562
563
|
try {
|
|
563
564
|
const credential = await resolveVaultCredentialForCapability("files:read", "library describe", {
|
|
564
|
-
interactive:
|
|
565
|
+
interactive: gatedInteractiveLogin(),
|
|
565
566
|
});
|
|
566
567
|
const { token } = credential;
|
|
567
568
|
const hqRoot = findHqRoot();
|
|
@@ -618,7 +619,7 @@ export function registerLibraryCommand(program) {
|
|
|
618
619
|
.action(async (target, options) => {
|
|
619
620
|
try {
|
|
620
621
|
const credential = await resolveVaultCredentialForCapability("files:read", "library link", {
|
|
621
|
-
interactive:
|
|
622
|
+
interactive: gatedInteractiveLogin(),
|
|
622
623
|
});
|
|
623
624
|
const { token } = credential;
|
|
624
625
|
const result = await runLibraryLink(target, { company: options.company, expiresMinutes: options.expires }, {
|
package/dist/commands/mesh.d.ts
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `hq mesh` — work-mesh verbs for agents and local sessions.
|
|
3
|
-
*
|
|
4
|
-
* Native hq-cli: Cognito + hq-pro REST + ~/.hq/work-mesh/cache. Does not
|
|
5
|
-
* require hq-pack-work-mesh on disk and does not start MQTT listen.
|
|
6
|
-
* Distinct from `hq doctor` (hook guardrails).
|
|
7
|
-
*/
|
|
8
1
|
import { Command } from "commander";
|
|
9
2
|
import { type MeshCompany, type MeshProjectRepo, type MeshProjectStoryInput, type MeshThread } from "../lib/mesh/api.js";
|
|
10
3
|
import { UnknownProjectStoryStateError } from "../lib/mesh/story-status.js";
|
package/dist/commands/mesh.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { reportingRuntimeAllowed } from "../lib/mesh/live/reporting-privacy.js";
|
|
1
2
|
/**
|
|
2
3
|
* `hq mesh` — work-mesh verbs for agents and local sessions.
|
|
3
4
|
*
|
|
@@ -1123,6 +1124,7 @@ async function runPrdSync(opts) {
|
|
|
1123
1124
|
}));
|
|
1124
1125
|
const poster = createEmitPoster({ token, baseUrl: resolveVaultApiBase(process.env) });
|
|
1125
1126
|
const emitted = await emitEvents({
|
|
1127
|
+
reportingAllowed: () => reportingRuntimeAllowed(isMachineIdentity()),
|
|
1126
1128
|
workMeshRoot: meshRoot,
|
|
1127
1129
|
poster,
|
|
1128
1130
|
newEvents: events,
|
|
@@ -2534,6 +2536,7 @@ async function runSessionEnqueue(cliKind, opts) {
|
|
|
2534
2536
|
baseUrl: resolveVaultApiBase(process.env),
|
|
2535
2537
|
});
|
|
2536
2538
|
const summary = await emitEvents({
|
|
2539
|
+
reportingAllowed: () => reportingRuntimeAllowed(isMachineIdentity()),
|
|
2537
2540
|
workMeshRoot: meshRoot,
|
|
2538
2541
|
poster,
|
|
2539
2542
|
newEvents: [event],
|
|
@@ -2617,7 +2620,7 @@ async function runMeshEmit(opts) {
|
|
|
2617
2620
|
skipped = backlog.skipped;
|
|
2618
2621
|
newEvents = backlog.events;
|
|
2619
2622
|
}
|
|
2620
|
-
const summary = await emitEvents({ workMeshRoot: meshRoot, poster, newEvents });
|
|
2623
|
+
const summary = await emitEvents({ reportingAllowed: () => reportingRuntimeAllowed(isMachineIdentity()), workMeshRoot: meshRoot, poster, newEvents });
|
|
2621
2624
|
const out = {
|
|
2622
2625
|
ok: true,
|
|
2623
2626
|
action: "emit",
|
|
@@ -2643,6 +2646,7 @@ async function runSessionFlush(opts) {
|
|
|
2643
2646
|
baseUrl: resolveVaultApiBase(process.env),
|
|
2644
2647
|
});
|
|
2645
2648
|
const summary = await flushSessionEvents({
|
|
2649
|
+
reportingAllowed: () => reportingRuntimeAllowed(isMachineIdentity()),
|
|
2646
2650
|
workMeshRoot: workMeshHomeRoot(),
|
|
2647
2651
|
workContextRoot: workContextHomeRoot(),
|
|
2648
2652
|
poster,
|
|
@@ -19,6 +19,7 @@ import { VaultClient, PERSONAL_VAULT_JOURNAL_SLUG, getStateDir } from "@indigoai
|
|
|
19
19
|
import { buildVaultConfig, cognitoSubjectFromToken, ensureCognitoToken } from "../utils/cognito-session.js";
|
|
20
20
|
import { decodeJwtPayload } from "../utils/person-token-cache.js";
|
|
21
21
|
import { resolveCanonicalPersonUid } from "./cloud.js";
|
|
22
|
+
import { gatedInteractiveLogin } from "../utils/interactive-caller.js";
|
|
22
23
|
export const HELD_UNAVAILABLE_MESSAGE = "this HQ build cannot list or release held uploads yet; update HQ and try again";
|
|
23
24
|
export function fenceExports(mod = hqCloud) {
|
|
24
25
|
const list = mod.listPushIdentityFences;
|
|
@@ -157,11 +158,10 @@ export async function resolveHeldIdentity(options, io) {
|
|
|
157
158
|
return { sub, personalUid };
|
|
158
159
|
}
|
|
159
160
|
export function createDefaultSyncHeldDeps() {
|
|
160
|
-
const tty = process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
161
161
|
return {
|
|
162
162
|
exports: fenceExports(),
|
|
163
163
|
stateDir: getStateDir(),
|
|
164
|
-
identity: (options) => resolveHeldIdentity({ needPersonal: options.needPersonal, interactive: options.interactive
|
|
164
|
+
identity: (options) => resolveHeldIdentity({ needPersonal: options.needPersonal, interactive: gatedInteractiveLogin(options.interactive) }, {
|
|
165
165
|
ensureToken: (interactive) => ensureCognitoToken({ interactive }),
|
|
166
166
|
listPersons: async (token) => (await new VaultClient(buildVaultConfig(token)).entity.listByType("person")),
|
|
167
167
|
}),
|
package/dist/lib/bot/api.d.ts
CHANGED
|
@@ -243,6 +243,14 @@ export declare class BotApi {
|
|
|
243
243
|
createdAt?: string;
|
|
244
244
|
state?: string;
|
|
245
245
|
}>;
|
|
246
|
+
/**
|
|
247
|
+
* POST /v1/agents/{agentUid}/failure-reports, as the bot. The same route
|
|
248
|
+
* the HQ Fleet Runtime's HQ DM adapter files a failed turn through; the
|
|
249
|
+
* payload mirrors its `build_upload`. Returns the id HQ stored it under.
|
|
250
|
+
*/
|
|
251
|
+
reportFailure(agentUid: string, report: Record<string, unknown>): Promise<{
|
|
252
|
+
reportId: string | null;
|
|
253
|
+
}>;
|
|
246
254
|
/** POST|DELETE /v1/notify/reactions on a DM message (best-effort thinking status). */
|
|
247
255
|
setReaction(input: {
|
|
248
256
|
peerUid: string;
|
package/dist/lib/bot/api.js
CHANGED
|
@@ -163,6 +163,15 @@ export class BotApi {
|
|
|
163
163
|
const { body } = await this.call("/v1/notify/dm", { method: "POST", body: payload });
|
|
164
164
|
return body;
|
|
165
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* POST /v1/agents/{agentUid}/failure-reports, as the bot. The same route
|
|
168
|
+
* the HQ Fleet Runtime's HQ DM adapter files a failed turn through; the
|
|
169
|
+
* payload mirrors its `build_upload`. Returns the id HQ stored it under.
|
|
170
|
+
*/
|
|
171
|
+
async reportFailure(agentUid, report) {
|
|
172
|
+
const { body } = await this.call(`/v1/agents/${encodeURIComponent(agentUid)}/failure-reports`, { method: "POST", body: report });
|
|
173
|
+
return { reportId: typeof body?.reportId === "string" && body.reportId.trim() ? body.reportId.trim() : null };
|
|
174
|
+
}
|
|
166
175
|
/** POST|DELETE /v1/notify/reactions on a DM message (best-effort thinking status). */
|
|
167
176
|
async setReaction(input, present) {
|
|
168
177
|
try {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** A download that receives no bytes for this long is treated as stalled and retried. */
|
|
2
|
+
export declare const FLEET_DOWNLOAD_STALL_MS = 90000;
|
|
3
|
+
/** How many times a stalled or broken download is resumed before giving up. */
|
|
4
|
+
export declare const FLEET_DOWNLOAD_MAX_RESUMES = 5;
|
|
5
|
+
export interface FleetDownloadProgress {
|
|
6
|
+
name: string;
|
|
7
|
+
received: number;
|
|
8
|
+
total: number | null;
|
|
9
|
+
}
|
|
10
|
+
export interface FleetDownloadOptions {
|
|
11
|
+
fetch?: typeof fetch;
|
|
12
|
+
headers?: Record<string, string>;
|
|
13
|
+
stallMs?: number;
|
|
14
|
+
maxResumes?: number;
|
|
15
|
+
onProgress?: (progress: FleetDownloadProgress) => void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Download `url` into `destination`, writing through `<destination>.part` and
|
|
19
|
+
* resuming from the bytes already there. Every attempt that receives nothing
|
|
20
|
+
* for `stallMs` is retried from where it stopped, up to `maxResumes` times.
|
|
21
|
+
* The file is renamed into place only when the body ended cleanly and, when
|
|
22
|
+
* the server told us its size, the sizes match.
|
|
23
|
+
*/
|
|
24
|
+
export declare function downloadFleetFile(url: string, destination: string, options?: FleetDownloadOptions): Promise<string>;
|
|
25
|
+
/** sha256 of a file on disk, streamed. */
|
|
26
|
+
export declare function fileSha256(file: string): string;
|
|
27
|
+
//# sourceMappingURL=fleet-download.d.ts.map
|