@indigoai-us/hq-cli 5.345.197 → 5.345.199
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 +111 -0
- package/dist/command-catalog.generated.d.ts +393 -12
- package/dist/command-catalog.generated.js +482 -12
- package/dist/commands/bot-events.d.ts +19 -0
- package/dist/commands/bot-events.js +46 -0
- package/dist/commands/bot-jobs.d.ts +2 -0
- package/dist/commands/bot-jobs.js +43 -2
- package/dist/commands/bot.d.ts +5 -0
- package/dist/commands/bot.js +33 -1
- package/dist/commands/core-resolve-company-native.d.ts +2 -1
- package/dist/commands/core-resolve-company-native.js +2 -24
- package/dist/commands/deploy-domains.d.ts +14 -2
- package/dist/commands/deploy-domains.js +726 -42
- package/dist/commands/integrations-connect.js +41 -3
- package/dist/commands/lanes-report.d.ts +2 -1
- package/dist/commands/lanes-report.js +44 -1
- package/dist/commands/lanes.d.ts +9 -0
- package/dist/commands/lanes.js +95 -9
- package/dist/commands/meetings.js +1 -21
- package/dist/commands/secrets.js +27 -6
- package/dist/commands/signals-run.d.ts +2 -0
- package/dist/commands/signals-run.js +6 -1
- package/dist/lanes-drain-hook.js +9 -0
- package/dist/lib/bot/api.d.ts +5 -0
- package/dist/lib/bot/claude-login.d.ts +31 -0
- package/dist/lib/bot/claude-login.js +160 -0
- package/dist/lib/bot/company-bind.d.ts +34 -0
- package/dist/lib/bot/company-bind.js +112 -0
- package/dist/lib/bot/config.js +3 -1
- package/dist/lib/bot/events.d.ts +18 -0
- package/dist/lib/bot/events.js +160 -0
- package/dist/lib/bot/executive-settings.d.ts +10 -0
- package/dist/lib/bot/executive-settings.js +43 -0
- package/dist/lib/bot/fleet-cron.js +4 -0
- package/dist/lib/bot/fleet-local.d.ts +12 -0
- package/dist/lib/bot/fleet-local.js +50 -20
- package/dist/lib/bot/job-run.js +1 -1
- package/dist/lib/bot/jobs/hot-signal-sync.d.ts +8 -0
- package/dist/lib/bot/jobs/hot-signal-sync.js +2 -0
- package/dist/lib/bot/jobs/meeting-detect.d.ts +28 -0
- package/dist/lib/bot/jobs/meeting-detect.js +176 -0
- package/dist/lib/bot/jobs/meeting-rider.d.ts +53 -0
- package/dist/lib/bot/jobs/meeting-rider.js +284 -0
- package/dist/lib/bot/jobs/signal-to-lane.d.ts +32 -0
- package/dist/lib/bot/jobs/signal-to-lane.js +169 -0
- package/dist/lib/bot/local-jobs.d.ts +15 -2
- package/dist/lib/bot/local-jobs.js +24 -7
- package/dist/lib/bot/run.d.ts +7 -49
- package/dist/lib/bot/run.js +135 -12
- package/dist/lib/bot/session.d.ts +19 -1
- package/dist/lib/bot/session.js +75 -28
- package/dist/lib/bot/status.d.ts +21 -0
- package/dist/lib/core-utils/manifest-slugs.d.ts +2 -0
- package/dist/lib/core-utils/manifest-slugs.js +26 -0
- package/dist/lib/doctor/checks/bots.d.ts +6 -0
- package/dist/lib/doctor/checks/bots.js +47 -1
- package/dist/lib/hot-channels/adapters/meetings.d.ts +1 -0
- package/dist/lib/hot-channels/adapters/meetings.js +3 -1
- package/dist/lib/hot-channels/meeting-source-ref.d.ts +3 -0
- package/dist/lib/hot-channels/meeting-source-ref.js +7 -0
- package/dist/lib/hot-channels/schedule.js +2 -2
- package/dist/lib/hot-channels/signals-pass.d.ts +9 -32
- package/dist/lib/hot-channels/signals-pass.js +51 -17
- package/dist/lib/lanes/admission.d.ts +2 -0
- package/dist/lib/lanes/admission.js +4 -0
- package/dist/lib/lanes/approval-gate.js +4 -1
- package/dist/lib/lanes/links.d.ts +8 -1
- package/dist/lib/lanes/links.js +84 -3
- package/dist/lib/lanes/outcome.d.ts +154 -0
- package/dist/lib/lanes/outcome.js +151 -0
- package/dist/lib/lanes/reconcile.js +1 -0
- package/dist/lib/lanes/signal-brief-template.d.ts +7 -0
- package/dist/lib/lanes/signal-brief-template.js +40 -0
- package/dist/lib/lanes/signal-lane.d.ts +24 -0
- package/dist/lib/lanes/signal-lane.js +99 -0
- package/dist/lib/lanes/source-report.d.ts +37 -0
- package/dist/lib/lanes/source-report.js +66 -0
- package/dist/lib/lanes/spawn.d.ts +6 -0
- package/dist/lib/lanes/spawn.js +21 -0
- package/dist/lib/lanes/store.d.ts +3 -0
- package/dist/lib/lanes/store.js +64 -0
- package/dist/lib/lanes/story-ledger.d.ts +3 -0
- package/dist/lib/lanes/story-ledger.js +1 -0
- package/dist/lib/lanes/types.d.ts +137 -0
- package/dist/lib/lanes/types.js +17 -2
- package/dist/lib/lanes/watcher.js +4 -0
- package/dist/lib/live-meetings.d.ts +19 -0
- package/dist/lib/live-meetings.js +23 -0
- package/dist/lib/mesh/live/daemon/doctor.js +6 -7
- package/dist/lib/mesh/live/spool.d.ts +8 -0
- package/dist/lib/mesh/live/spool.js +59 -40
- package/dist/lib/plan-limit-nag.d.ts +30 -4
- package/dist/lib/plan-limit-nag.js +68 -17
- package/dist/lib/signals/engine.d.ts +4 -0
- package/dist/lib/signals/engine.js +25 -5
- package/dist/lib/signals/incremental.d.ts +42 -0
- package/dist/lib/signals/incremental.js +191 -0
- package/dist/lib/signals/prior-signals.d.ts +11 -0
- package/dist/lib/signals/prior-signals.js +44 -0
- package/dist/lib/signals/reply.js +6 -1
- package/dist/lib/signals/settings.d.ts +5 -0
- package/dist/lib/signals/settings.js +17 -0
- package/dist/lib/signals/steps/extract.d.ts +30 -2
- package/dist/lib/signals/steps/extract.js +153 -11
- package/dist/lib/signals/steps/types.d.ts +1 -0
- package/dist/lib/signals/write-guard.js +7 -0
- package/dist/secrets-env-action.d.ts +70 -0
- package/dist/secrets-env-action.js +220 -2
- package/dist/utils/cognito-callback-port.d.ts +15 -0
- package/dist/utils/cognito-callback-port.js +51 -0
- package/dist/utils/cognito-config.js +2 -3
- package/dist/utils/cognito-session.d.ts +7 -6
- package/dist/utils/cognito-session.js +14 -14
- package/dist/utils/interactive-caller.d.ts +15 -0
- package/dist/utils/interactive-caller.js +70 -0
- package/dist/utils/login-browser.js +3 -1
- package/dist/utils/resident-package-preload.js +4 -0
- package/package.json +9 -7
|
@@ -2,11 +2,14 @@ import chalk from "chalk";
|
|
|
2
2
|
import { writeSync } from "node:fs";
|
|
3
3
|
import * as nodePath from "node:path";
|
|
4
4
|
import { cognitoCachePrincipalUid } from "./utils/cognito-session.js";
|
|
5
|
-
import { isAuthError } from "./utils/auth-error.js";
|
|
6
5
|
import { DEFAULT_SECRETS_CACHE_TTL_MS, hasCacheEntry, readCache, removeCacheEntry, writeCache, } from "./utils/secrets-cache.js";
|
|
7
6
|
import { computeSha256 } from "./utils/integrity.js";
|
|
8
7
|
import { SECRET_NAME_PATTERN } from "./commands/_patterns.js";
|
|
9
8
|
import { finishEnvExportsAndExit } from "./commands/secrets-env-exit.js";
|
|
9
|
+
import { DELEGATED_MACHINE_SESSION_ENV, delegatedMachineSessionFilePath, loadDelegatedMachineSession, loadMachineCreds, } from "@indigoai-us/hq-cloud";
|
|
10
|
+
import { AuthError, isAuthError } from "./utils/auth-error.js";
|
|
11
|
+
import { peekIdToken } from "./utils/id-token.js";
|
|
12
|
+
import { wantsMachineCognitoTokens } from "./utils/machine-principal-state.js";
|
|
10
13
|
import { getEntityUid, looksLikeCompanyUid, vaultApiFetch } from "./utils/vault-api.js";
|
|
11
14
|
import { HQ_API_KEY_PREFIX, resolveVaultCredentialForCapability, } from "./utils/resolve-vault-credential.js";
|
|
12
15
|
import { CACHED_SECRETS_SERVER_UNAVAILABLE_MESSAGE, isSecretLoadUnavailable, isUnavailableSecretLoadHttpStatus, SecretLoadUnavailableError, } from "./utils/secret-load-availability.js";
|
|
@@ -221,10 +224,224 @@ export async function assertApiKeyCompany(token, requested, commandLabel) {
|
|
|
221
224
|
throw new Error(`Could not verify that HQ_API_KEY belongs to ${requested} (HTTP ${response.status}` +
|
|
222
225
|
`${message ? `: ${message}` : ""}).`);
|
|
223
226
|
}
|
|
227
|
+
const COMPANY_OUTPOST_UID_PATTERN = /^out_[0-9A-HJKMNP-TV-Z]{26}$/;
|
|
228
|
+
const COMPANY_UID_PATTERN = /^cmp_[A-Za-z0-9]+$/;
|
|
229
|
+
/**
|
|
230
|
+
* The company Outpost identity named by the token this command will send, or
|
|
231
|
+
* null for any other token (person, agent, owner-delegated outpost, API key).
|
|
232
|
+
*
|
|
233
|
+
* hq-cloud says whether this process is a machine identity but exposes no
|
|
234
|
+
* accessor for the bound company, and hq-pro routes on the claims of the
|
|
235
|
+
* token it receives, so the route choice reads them from that token. The
|
|
236
|
+
* claims are not verified here; hq-pro verifies the token and re-checks the
|
|
237
|
+
* live Outpost binding on every request. A token with any company Outpost
|
|
238
|
+
* marker but no usable company or out_* uid fails closed rather than falling
|
|
239
|
+
* through to the person routes.
|
|
240
|
+
*/
|
|
241
|
+
export function companyOutpostIdentityFromToken(token) {
|
|
242
|
+
if (token.startsWith(HQ_API_KEY_PREFIX))
|
|
243
|
+
return null;
|
|
244
|
+
const claims = peekIdToken(token);
|
|
245
|
+
const has = (key) => Object.prototype.hasOwnProperty.call(claims, key);
|
|
246
|
+
if (claims["custom:entityType"] !== "company-outpost" &&
|
|
247
|
+
!has("custom:ownerCompanyUid") &&
|
|
248
|
+
!has("custom:companyOutpostId")) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
const outpostUid = claims["custom:entityUid"];
|
|
252
|
+
const companyUid = claims["custom:ownerCompanyUid"];
|
|
253
|
+
if (claims["custom:entityType"] !== "company-outpost" ||
|
|
254
|
+
typeof outpostUid !== "string" ||
|
|
255
|
+
!COMPANY_OUTPOST_UID_PATTERN.test(outpostUid) ||
|
|
256
|
+
typeof companyUid !== "string" ||
|
|
257
|
+
!COMPANY_UID_PATTERN.test(companyUid)) {
|
|
258
|
+
throw new Error("This HQ session carries company Outpost markers but does not name its Outpost (out_…) " +
|
|
259
|
+
"and company (cmp_…). Refusing to read secrets with it.");
|
|
260
|
+
}
|
|
261
|
+
return { outpostUid, companyUid };
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* True when this process will authenticate as a company Outpost: hq-cli picks
|
|
265
|
+
* machine tokens and hq-cloud's machine identity (a delegated session file or
|
|
266
|
+
* a credentials file) is a company Outpost. Local state only, no network.
|
|
267
|
+
* Used before a credential is resolved, where there is no token to read yet.
|
|
268
|
+
*/
|
|
269
|
+
export function callerIsCompanyOutpost(env = process.env) {
|
|
270
|
+
try {
|
|
271
|
+
return wantsMachineCognitoTokens({}, env) && loadMachineCreds()?.entityType === "company-outpost";
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* The company uid a company Outpost reads secrets from. The token names its
|
|
279
|
+
* bound company, so an omitted `--company` uses it with no lookup. An explicit
|
|
280
|
+
* `--company` must name that same company: a uid is compared locally, and a
|
|
281
|
+
* slug is checked with GET /entity/check-slug/me, the one slug lookup hq-pro
|
|
282
|
+
* answers for a company Outpost (it reports a conflict only for the bound
|
|
283
|
+
* company). Any other company, and `--personal`, is refused.
|
|
284
|
+
*/
|
|
285
|
+
export async function resolveCompanyOutpostSecretsCompany(token, outpost, scope) {
|
|
286
|
+
if (scope.personal) {
|
|
287
|
+
throw new Error("A company Outpost session has no personal vault. Drop --personal; it reads its company's secrets.");
|
|
288
|
+
}
|
|
289
|
+
const ref = scope.companySlug?.trim();
|
|
290
|
+
if (!ref || ref === outpost.companyUid)
|
|
291
|
+
return outpost.companyUid;
|
|
292
|
+
const notBound = () => new Error(`Company '${ref}' is not the company this Outpost (${outpost.outpostUid}) is bound to ` +
|
|
293
|
+
`(${outpost.companyUid}). A company Outpost can read only its own company's secrets.`);
|
|
294
|
+
if (looksLikeCompanyUid(ref) || /^(prs|agt|out)_/.test(ref))
|
|
295
|
+
throw notBound();
|
|
296
|
+
const res = await vaultApiFetch({
|
|
297
|
+
token,
|
|
298
|
+
path: "/entity/check-slug/me",
|
|
299
|
+
query: { type: "company", slug: ref },
|
|
300
|
+
signal: AbortSignal.timeout(SECRET_LOAD_TIMEOUT_MS),
|
|
301
|
+
});
|
|
302
|
+
const body = (await res.json().catch(() => ({})));
|
|
303
|
+
if (!res.ok) {
|
|
304
|
+
if (res.status === 401)
|
|
305
|
+
throw companyOutpostSessionRejected();
|
|
306
|
+
const message = `Failed to resolve company '${ref}' for this Outpost: ${extractApiMessage(body, `HTTP ${res.status}`)}`;
|
|
307
|
+
if (isUnavailableSecretLoadHttpStatus(res.status))
|
|
308
|
+
throw new SecretLoadUnavailableError(message);
|
|
309
|
+
throw new Error(message);
|
|
310
|
+
}
|
|
311
|
+
if (body.available === false && body.conflictingCompanyUid === outpost.companyUid) {
|
|
312
|
+
return outpost.companyUid;
|
|
313
|
+
}
|
|
314
|
+
throw notBound();
|
|
315
|
+
}
|
|
316
|
+
function companyOutpostSessionRejected() {
|
|
317
|
+
return new AuthError("HQ rejected this company Outpost session (HTTP 401). The job runner refreshes " +
|
|
318
|
+
"HQ_MACHINE_SESSION_FILE; re-run the job, and if it persists check the Outpost's binding.");
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* The company uid a `secrets exec` / `env` / `get` call reads from. A company
|
|
322
|
+
* Outpost token takes its company from the token (see
|
|
323
|
+
* {@link resolveCompanyOutpostSecretsCompany}); every other Cognito token keeps
|
|
324
|
+
* the existing {@link getEntityUid} resolution unchanged.
|
|
325
|
+
*/
|
|
326
|
+
export async function resolveSecretsReadCompanyUid(token, scope, options = {}) {
|
|
327
|
+
const outpost = companyOutpostIdentityFromToken(token);
|
|
328
|
+
if (outpost)
|
|
329
|
+
return resolveCompanyOutpostSecretsCompany(token, outpost, scope);
|
|
330
|
+
return getEntityUid(token, scope, options);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* The message for a 403 a company Outpost gets on the single-secret GET, or
|
|
334
|
+
* null when the status/body is not one of these denials.
|
|
335
|
+
*
|
|
336
|
+
* - No code: hq-pro answers a plain "read permission required" both when the
|
|
337
|
+
* secret is not shared with this Outpost and when the Outpost's live binding
|
|
338
|
+
* to its company is not active, and the response does not say which.
|
|
339
|
+
* - `reveal_disabled` / `env_disabled`: the secret is script-locked. The
|
|
340
|
+
* Outpost route evaluates the read with no script attestation, so a
|
|
341
|
+
* script-locked secret can never be read by an Outpost job.
|
|
342
|
+
*/
|
|
343
|
+
export function companyOutpostReadDenialMessage(name, status, body, companyUid, outpost) {
|
|
344
|
+
if (status !== 403)
|
|
345
|
+
return null;
|
|
346
|
+
if (body.code === undefined) {
|
|
347
|
+
return (`Failed to fetch secret '${name}': ${extractApiMessage(body, "No read permission")}. ` +
|
|
348
|
+
`Either the secret is not shared with this Outpost (${outpost.outpostUid}), or the Outpost's ` +
|
|
349
|
+
`binding to ${companyUid} is not active. To share it: ` +
|
|
350
|
+
`hq secrets --company ${companyUid} share ${name} --with ${outpost.outpostUid}`);
|
|
351
|
+
}
|
|
352
|
+
if (body.code === "reveal_disabled" || body.code === "env_disabled") {
|
|
353
|
+
return (`Failed to fetch secret '${name}': it is script-locked, and company Outpost jobs cannot ` +
|
|
354
|
+
`read script-locked secrets (the Outpost secret route carries no script attestation).`);
|
|
355
|
+
}
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Fail closed when HQ_MACHINE_SESSION_FILE is set but hq-cloud cannot use the
|
|
360
|
+
* file. The parent that set it chose that identity for this process, so an
|
|
361
|
+
* unreadable or malformed file must stop the read with a message naming the
|
|
362
|
+
* file, never fall through to another identity or a generic scope error.
|
|
363
|
+
* An expired but well-formed session passes here; the token request reports
|
|
364
|
+
* the expiry.
|
|
365
|
+
*/
|
|
366
|
+
export function assertDelegatedMachineSessionUsable() {
|
|
367
|
+
const file = delegatedMachineSessionFilePath();
|
|
368
|
+
if (file === null)
|
|
369
|
+
return;
|
|
370
|
+
if (loadDelegatedMachineSession() !== null)
|
|
371
|
+
return;
|
|
372
|
+
throw new Error(`${DELEGATED_MACHINE_SESSION_ENV} names ${file}, but that file is not a usable delegated machine ` +
|
|
373
|
+
`session (missing, unreadable, malformed, or its tokens do not describe the identity it claims). ` +
|
|
374
|
+
`Refusing to read secrets; the job runner must rewrite the session file.`);
|
|
375
|
+
}
|
|
376
|
+
function encodeSecretNamePath(name) {
|
|
377
|
+
// Encode each segment so structural `/` separators stay route separators
|
|
378
|
+
// for GET /secrets/{companyUid}/name/{proxy+} (same as `hq secrets get`).
|
|
379
|
+
return name.split("/").map(encodeURIComponent).join("/");
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Read each key with GET /secrets/{companyUid}/name/{name}?reveal=true, the
|
|
383
|
+
* only secrets route hq-pro serves to a company Outpost. Throws on the first
|
|
384
|
+
* key that does not resolve, with the same `Failed to fetch secret '<k>':
|
|
385
|
+
* <reason>` shape as the batch path. Values are never written to the local
|
|
386
|
+
* secrets cache and there is no offline fallback: the Outpost reads only what
|
|
387
|
+
* the server authorizes on each run.
|
|
388
|
+
*
|
|
389
|
+
* The route evaluates the read as a plain reveal with no script usage, so a
|
|
390
|
+
* script-locked or high-security secret is refused by the server; the denial
|
|
391
|
+
* is surfaced as is (high-security gets the sandbox guidance).
|
|
392
|
+
*/
|
|
393
|
+
async function loadRevealedSecretsForCompanyOutpost(token, outpost, companyUid, keys, lockModes) {
|
|
394
|
+
if (companyUid !== outpost.companyUid) {
|
|
395
|
+
throw new Error(`This company Outpost (${outpost.outpostUid}) is bound to ${outpost.companyUid} ` +
|
|
396
|
+
`and cannot read secrets from ${companyUid}.`);
|
|
397
|
+
}
|
|
398
|
+
const resolved = new Map();
|
|
399
|
+
for (const name of [...new Set(keys)]) {
|
|
400
|
+
const res = await vaultApiFetch({
|
|
401
|
+
token,
|
|
402
|
+
path: `/secrets/${encodeURIComponent(companyUid)}/name/${encodeSecretNamePath(name)}`,
|
|
403
|
+
query: { reveal: "true" },
|
|
404
|
+
signal: AbortSignal.timeout(SECRET_LOAD_TIMEOUT_MS),
|
|
405
|
+
});
|
|
406
|
+
const body = (await res.json().catch(() => ({})));
|
|
407
|
+
if (!res.ok) {
|
|
408
|
+
if (body.highSecurity === true ||
|
|
409
|
+
body.code === "high_security_denied" ||
|
|
410
|
+
body.code === "high_security_sandbox_only") {
|
|
411
|
+
throw new Error(highSecuritySandboxOnlyMessage(name));
|
|
412
|
+
}
|
|
413
|
+
if (res.status === 401)
|
|
414
|
+
throw companyOutpostSessionRejected();
|
|
415
|
+
if (res.status === 404) {
|
|
416
|
+
throw new Error(`Failed to fetch secret '${name}': Secret not found`);
|
|
417
|
+
}
|
|
418
|
+
const message = extractApiMessage(body, res.statusText || `HTTP ${res.status}`);
|
|
419
|
+
if (isUnavailableSecretLoadHttpStatus(res.status)) {
|
|
420
|
+
throw new SecretLoadUnavailableError(`Failed to fetch secret '${name}': ${message}`);
|
|
421
|
+
}
|
|
422
|
+
throw new Error(companyOutpostReadDenialMessage(name, res.status, body, companyUid, outpost) ??
|
|
423
|
+
`Failed to fetch secret '${name}': ${message}`);
|
|
424
|
+
}
|
|
425
|
+
const secret = typeof body.secret === "object" && body.secret !== null
|
|
426
|
+
? body.secret
|
|
427
|
+
: null;
|
|
428
|
+
if (!secret || secret.name !== name || typeof secret.value !== "string") {
|
|
429
|
+
throw new Error(`Failed to fetch secret '${name}': malformed fetch response`);
|
|
430
|
+
}
|
|
431
|
+
const mode = secret.scriptLock?.mode;
|
|
432
|
+
lockModes?.set(name, typeof mode === "string" ? mode : undefined);
|
|
433
|
+
resolved.set(name, secret.value);
|
|
434
|
+
}
|
|
435
|
+
return resolved;
|
|
436
|
+
}
|
|
224
437
|
export async function loadRevealedSecrets(token, companyUid, keys, usage, options = {}) {
|
|
225
438
|
if (token.startsWith(HQ_API_KEY_PREFIX)) {
|
|
226
439
|
return loadRevealedSecretsViaApiKey(token, keys, companyUid, options.lockModes);
|
|
227
440
|
}
|
|
441
|
+
const outpost = companyOutpostIdentityFromToken(token);
|
|
442
|
+
if (outpost) {
|
|
443
|
+
return loadRevealedSecretsForCompanyOutpost(token, outpost, companyUid, keys, options.lockModes);
|
|
444
|
+
}
|
|
228
445
|
const resolved = new Map();
|
|
229
446
|
const requested = [...new Set(keys)];
|
|
230
447
|
const principalUid = cognitoCachePrincipalUid(token);
|
|
@@ -469,6 +686,7 @@ export async function runSecretsEnvCommand(opts, parentScope, options = {}) {
|
|
|
469
686
|
console.error(chalk.yellow("stdout is a terminal — values redacted. Use: source <(hq secrets env --only KEY1,KEY2)"));
|
|
470
687
|
}
|
|
471
688
|
const keys = parseSecretNameList(opts.only);
|
|
689
|
+
assertDelegatedMachineSessionUsable();
|
|
472
690
|
const cred = await resolveVaultCredentialForCapability("secrets:read", "secrets env", secretsCredentialOptions());
|
|
473
691
|
const apiKeyCompanyRef = cred.kind === "api-key"
|
|
474
692
|
? requestedApiKeyCompany(parentScope.company)
|
|
@@ -477,7 +695,7 @@ export async function runSecretsEnvCommand(opts, parentScope, options = {}) {
|
|
|
477
695
|
? (apiKeyCompanyRef
|
|
478
696
|
? await assertApiKeyCompany(cred.token, apiKeyCompanyRef, "secrets env")
|
|
479
697
|
: "__api_key__")
|
|
480
|
-
: await
|
|
698
|
+
: await resolveSecretsReadCompanyUid(cred.token, scopeOpts(parentScope), {
|
|
481
699
|
allowOfflineScopeFallback: true,
|
|
482
700
|
});
|
|
483
701
|
const usage = cred.kind === "cognito"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CognitoAuthConfig } from "@indigoai-us/hq-cloud";
|
|
2
|
+
/** Fallback and default loopback callback port. */
|
|
3
|
+
export declare const DEFAULT_COGNITO_CALLBACK_PORT = 8765;
|
|
4
|
+
/** Ports registered as callback URLs on the HQ CLI Cognito app client. */
|
|
5
|
+
export declare const REGISTERED_COGNITO_CALLBACK_PORTS: readonly number[];
|
|
6
|
+
export type PortWarn = (message: string) => void;
|
|
7
|
+
/**
|
|
8
|
+
* Resolve a callback port value, a string from the environment or a number,
|
|
9
|
+
* to a registered port. Unset resolves to 8765 silently; an unregistered value
|
|
10
|
+
* resolves to 8765 and logs a warning.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveCognitoCallbackPort(raw: string | number | undefined, warn?: PortWarn): number;
|
|
13
|
+
/** Return `config` with its callback port forced into the registered set. */
|
|
14
|
+
export declare function withRegisteredCallbackPort<T extends CognitoAuthConfig>(config: T, warn?: PortWarn): T;
|
|
15
|
+
//# sourceMappingURL=cognito-callback-port.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// src/utils/cognito-callback-port.ts
|
|
2
|
+
//
|
|
3
|
+
// The HQ CLI Cognito app client accepts exactly three loopback redirect URIs:
|
|
4
|
+
// http://localhost:{8765,3000,53682}/callback (hq-pro infra/cognito.ts,
|
|
5
|
+
// callbackUrls). browserLogin builds its redirect_uri from config.port, so any
|
|
6
|
+
// other port opens a sign-in tab that Cognito rejects with redirect_mismatch.
|
|
7
|
+
// A stray HQ_COGNITO_CALLBACK_PORT (seen with random ports such as 57726 and
|
|
8
|
+
// 61604) produced exactly that tab. Every browser sign-in now goes through
|
|
9
|
+
// this guard: an unregistered port falls back to 8765 with a stderr warning.
|
|
10
|
+
/** Fallback and default loopback callback port. */
|
|
11
|
+
export const DEFAULT_COGNITO_CALLBACK_PORT = 8765;
|
|
12
|
+
/** Ports registered as callback URLs on the HQ CLI Cognito app client. */
|
|
13
|
+
export const REGISTERED_COGNITO_CALLBACK_PORTS = [8765, 3000, 53682];
|
|
14
|
+
const warned = new Set();
|
|
15
|
+
function defaultWarn(message) {
|
|
16
|
+
console.error(message);
|
|
17
|
+
}
|
|
18
|
+
function warnOnce(key, message, warn) {
|
|
19
|
+
if (warn === defaultWarn) {
|
|
20
|
+
if (warned.has(key))
|
|
21
|
+
return;
|
|
22
|
+
warned.add(key);
|
|
23
|
+
}
|
|
24
|
+
warn(message);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve a callback port value, a string from the environment or a number,
|
|
28
|
+
* to a registered port. Unset resolves to 8765 silently; an unregistered value
|
|
29
|
+
* resolves to 8765 and logs a warning.
|
|
30
|
+
*/
|
|
31
|
+
export function resolveCognitoCallbackPort(raw, warn = defaultWarn) {
|
|
32
|
+
if (raw === undefined || (typeof raw === "string" && !raw.trim())) {
|
|
33
|
+
return DEFAULT_COGNITO_CALLBACK_PORT;
|
|
34
|
+
}
|
|
35
|
+
const port = typeof raw === "number" ? raw : Number(raw.trim());
|
|
36
|
+
if (REGISTERED_COGNITO_CALLBACK_PORTS.includes(port))
|
|
37
|
+
return port;
|
|
38
|
+
warnOnce(String(raw), `hq: ignoring callback port ${String(raw)}: it is not a registered Cognito callback ` +
|
|
39
|
+
`(${REGISTERED_COGNITO_CALLBACK_PORTS.join(", ")}). Using ${DEFAULT_COGNITO_CALLBACK_PORT}. ` +
|
|
40
|
+
"Unset HQ_COGNITO_CALLBACK_PORT or set it to a registered port.", warn);
|
|
41
|
+
return DEFAULT_COGNITO_CALLBACK_PORT;
|
|
42
|
+
}
|
|
43
|
+
/** Return `config` with its callback port forced into the registered set. */
|
|
44
|
+
export function withRegisteredCallbackPort(config, warn = defaultWarn) {
|
|
45
|
+
// Unset keeps hq-cloud's own default (3000), which is registered.
|
|
46
|
+
if (config.port === undefined)
|
|
47
|
+
return config;
|
|
48
|
+
const port = resolveCognitoCallbackPort(config.port, warn);
|
|
49
|
+
return port === config.port ? config : { ...config, port };
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=cognito-callback-port.js.map
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
+
import { resolveCognitoCallbackPort } from "./cognito-callback-port.js";
|
|
1
2
|
/** Lightweight Cognito and Vault defaults shared by startup-safe modules. */
|
|
2
3
|
export const DEFAULT_COGNITO = {
|
|
3
4
|
region: process.env.AWS_REGION ?? "us-east-1",
|
|
4
5
|
userPoolDomain: process.env.HQ_COGNITO_DOMAIN ?? "vault-indigo-hq-prod",
|
|
5
6
|
clientId: process.env.HQ_COGNITO_CLIENT_ID ?? "7acei2c8v870enheptb1j5foln",
|
|
6
|
-
port: process.env.HQ_COGNITO_CALLBACK_PORT
|
|
7
|
-
? Number(process.env.HQ_COGNITO_CALLBACK_PORT)
|
|
8
|
-
: 8765,
|
|
7
|
+
port: resolveCognitoCallbackPort(process.env.HQ_COGNITO_CALLBACK_PORT),
|
|
9
8
|
identityProvider: process.env.HQ_COGNITO_IDENTITY_PROVIDER !== undefined
|
|
10
9
|
? process.env.HQ_COGNITO_IDENTITY_PROVIDER || undefined
|
|
11
10
|
: "Google",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { isExpiring, isMachineIdentity, type CognitoAuthConfig, type CognitoTokens, type ClientInfo, type VaultServiceConfig } from "@indigoai-us/hq-cloud";
|
|
22
22
|
import { hasPersonCachedSession, isMachineCredsFileEnvSet, wantsMachineCognitoTokens, type CognitoTokenSource } from "./machine-principal-state.js";
|
|
23
|
+
import { HQ_NO_BROWSER_LOGIN_ENV, type InteractiveCallerInput } from "./interactive-caller.js";
|
|
23
24
|
import { loadCachedTokens, personTokenCacheFile, readPersonTokenFile } from "./person-token-cache.js";
|
|
24
25
|
export { loadCachedTokens, personTokenCacheFile, readPersonTokenFile };
|
|
25
26
|
export { isExpiring, isMachineIdentity };
|
|
@@ -115,14 +116,14 @@ export type EnsureCognitoTokenOptions = {
|
|
|
115
116
|
/** Cancels bounded, non-interactive person-session refreshes. */
|
|
116
117
|
signal?: AbortSignal;
|
|
117
118
|
};
|
|
119
|
+
export { HQ_NO_BROWSER_LOGIN_ENV };
|
|
118
120
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
121
|
+
* An explicit `interactive` option wins. Otherwise the implicit browser sign-in
|
|
122
|
+
* is allowed only for an interactive caller (see isInteractiveCaller): a TTY on
|
|
123
|
+
* stdin and stdout, and not a daemon, lane, monitor, bot, `mcp serve`, sync
|
|
124
|
+
* runner, launchd task, or HQ_NO_BROWSER_LOGIN=1 child.
|
|
122
125
|
*/
|
|
123
|
-
export declare
|
|
124
|
-
/** An explicit `interactive` option wins; otherwise interactive unless the env switch is "1". */
|
|
125
|
-
export declare function resolveInteractiveLogin(options: Pick<EnsureCognitoTokenOptions, "interactive">, env?: NodeJS.ProcessEnv): boolean;
|
|
126
|
+
export declare function resolveInteractiveLogin(options: Pick<EnsureCognitoTokenOptions, "interactive">, env?: NodeJS.ProcessEnv, caller?: Omit<InteractiveCallerInput, "env">): boolean;
|
|
126
127
|
/**
|
|
127
128
|
* Fresh-box / Outpost bootstrap: env unset, not forced to person, no person
|
|
128
129
|
* cache, and the default machine-creds file is readable. Distinct from
|
|
@@ -32,6 +32,8 @@ import { daemonDir } from "../lib/mesh/live/daemon/paths.js";
|
|
|
32
32
|
import { workMeshRoot } from "../lib/mesh/live/paths.js";
|
|
33
33
|
import { defaultHqRootFallback, hqDiscoveryCandidates, pathForPlatform, } from "./hq-root-resolver.js";
|
|
34
34
|
import { acquireLockFileExclusively } from "./exclusive-lock.js";
|
|
35
|
+
import { resolveCognitoCallbackPort, withRegisteredCallbackPort } from "./cognito-callback-port.js";
|
|
36
|
+
import { HQ_NO_BROWSER_LOGIN_ENV, NON_INTERACTIVE_LOGIN_MESSAGE, isInteractiveCaller, } from "./interactive-caller.js";
|
|
35
37
|
import { reportNoninteractivePersonRefreshRejection } from "./cognito-refresh-sentry.js";
|
|
36
38
|
import { decodeJwtPayload, loadCachedTokens, personTokenCacheFile, readPersonTokenFile, readTokenFile, } from "./person-token-cache.js";
|
|
37
39
|
export { loadCachedTokens, personTokenCacheFile, readPersonTokenFile };
|
|
@@ -49,9 +51,7 @@ export const DEFAULT_COGNITO = {
|
|
|
49
51
|
region: process.env.AWS_REGION ?? "us-east-1",
|
|
50
52
|
userPoolDomain: process.env.HQ_COGNITO_DOMAIN ?? "vault-indigo-hq-prod",
|
|
51
53
|
clientId: process.env.HQ_COGNITO_CLIENT_ID ?? "7acei2c8v870enheptb1j5foln",
|
|
52
|
-
port: process.env.HQ_COGNITO_CALLBACK_PORT
|
|
53
|
-
? Number(process.env.HQ_COGNITO_CALLBACK_PORT)
|
|
54
|
-
: 8765,
|
|
54
|
+
port: resolveCognitoCallbackPort(process.env.HQ_COGNITO_CALLBACK_PORT),
|
|
55
55
|
// Skip Cognito's Hosted UI — go straight to Google OAuth. The Cognito
|
|
56
56
|
// /oauth2/authorize endpoint honors `identity_provider` and performs an
|
|
57
57
|
// internal redirect, so the user never sees the (un-styleable) Hosted UI.
|
|
@@ -244,17 +244,17 @@ export function machineTokenCacheFile(home = os.homedir(), env = process.env) {
|
|
|
244
244
|
export function defaultMachineCredsFile(home = os.homedir()) {
|
|
245
245
|
return path.join(home, ".hq-agent", "machine-creds.json");
|
|
246
246
|
}
|
|
247
|
+
export { HQ_NO_BROWSER_LOGIN_ENV };
|
|
247
248
|
/**
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
249
|
+
* An explicit `interactive` option wins. Otherwise the implicit browser sign-in
|
|
250
|
+
* is allowed only for an interactive caller (see isInteractiveCaller): a TTY on
|
|
251
|
+
* stdin and stdout, and not a daemon, lane, monitor, bot, `mcp serve`, sync
|
|
252
|
+
* runner, launchd task, or HQ_NO_BROWSER_LOGIN=1 child.
|
|
251
253
|
*/
|
|
252
|
-
export
|
|
253
|
-
/** An explicit `interactive` option wins; otherwise interactive unless the env switch is "1". */
|
|
254
|
-
export function resolveInteractiveLogin(options, env = process.env) {
|
|
254
|
+
export function resolveInteractiveLogin(options, env = process.env, caller = {}) {
|
|
255
255
|
if (options.interactive !== undefined)
|
|
256
256
|
return options.interactive;
|
|
257
|
-
return
|
|
257
|
+
return isInteractiveCaller({ ...caller, env });
|
|
258
258
|
}
|
|
259
259
|
/**
|
|
260
260
|
* Fresh-box / Outpost bootstrap: env unset, not forced to person, no person
|
|
@@ -939,7 +939,7 @@ async function ensureCognitoTokenUnserialized(options) {
|
|
|
939
939
|
return fallback.idToken;
|
|
940
940
|
}
|
|
941
941
|
if (!interactive) {
|
|
942
|
-
throw new Error(
|
|
942
|
+
throw new Error(NON_INTERACTIVE_LOGIN_MESSAGE);
|
|
943
943
|
}
|
|
944
944
|
if (isHeadlessHost() && hostRunsAnAgent()) {
|
|
945
945
|
// An agent host with no display has no one to complete a browser sign-in,
|
|
@@ -951,7 +951,7 @@ async function ensureCognitoTokenUnserialized(options) {
|
|
|
951
951
|
"(or HQ_MACHINE_CREDS_FILE to its machine-creds.json). Otherwise run `hq login` on a computer with a browser.");
|
|
952
952
|
}
|
|
953
953
|
console.error(chalk.cyan(" No cached HQ session — launching browser sign-in..."));
|
|
954
|
-
const tokens = await browserLogin(DEFAULT_COGNITO);
|
|
954
|
+
const tokens = await browserLogin(withRegisteredCallbackPort(DEFAULT_COGNITO));
|
|
955
955
|
return tokens.accessToken;
|
|
956
956
|
}
|
|
957
957
|
/**
|
|
@@ -1022,7 +1022,7 @@ async function ensureCognitoIdTokenUnserialized(options) {
|
|
|
1022
1022
|
return fallback.idToken;
|
|
1023
1023
|
}
|
|
1024
1024
|
if (!interactive) {
|
|
1025
|
-
throw new Error(
|
|
1025
|
+
throw new Error(NON_INTERACTIVE_LOGIN_MESSAGE);
|
|
1026
1026
|
}
|
|
1027
1027
|
if (isHeadlessHost() && hostRunsAnAgent()) {
|
|
1028
1028
|
// A browser sign-in on a server has no one to complete it and blocks
|
|
@@ -1032,7 +1032,7 @@ async function ensureCognitoIdTokenUnserialized(options) {
|
|
|
1032
1032
|
"(or HQ_MACHINE_CREDS_FILE to its machine-creds.json). Otherwise run `hq login` on a computer with a browser.");
|
|
1033
1033
|
}
|
|
1034
1034
|
console.error(chalk.cyan(" No cached HQ session — launching browser sign-in..."));
|
|
1035
|
-
const tokens = await browserLogin(DEFAULT_COGNITO);
|
|
1035
|
+
const tokens = await browserLogin(withRegisteredCallbackPort(DEFAULT_COGNITO));
|
|
1036
1036
|
return tokens.idToken;
|
|
1037
1037
|
}
|
|
1038
1038
|
/**
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Parent-set switch: a child `hq` must never open a browser sign-in. */
|
|
2
|
+
export declare const HQ_NO_BROWSER_LOGIN_ENV = "HQ_NO_BROWSER_LOGIN";
|
|
3
|
+
/** Remedy printed when an implicit sign-in is refused. */
|
|
4
|
+
export declare const NON_INTERACTIVE_LOGIN_MESSAGE: string;
|
|
5
|
+
export type InteractiveCallerInput = {
|
|
6
|
+
env?: NodeJS.ProcessEnv;
|
|
7
|
+
argv?: readonly string[];
|
|
8
|
+
stdinIsTTY?: boolean;
|
|
9
|
+
stdoutIsTTY?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/** The reason a caller is not interactive, or null when it is. */
|
|
12
|
+
export declare function nonInteractiveReason(input?: InteractiveCallerInput): string | null;
|
|
13
|
+
/** True only when a person is at a terminal and may complete a browser sign-in. */
|
|
14
|
+
export declare function isInteractiveCaller(input?: InteractiveCallerInput): boolean;
|
|
15
|
+
//# sourceMappingURL=interactive-caller.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// src/utils/interactive-caller.ts
|
|
2
|
+
//
|
|
3
|
+
// One predicate for "may this process open an implicit browser sign-in?".
|
|
4
|
+
//
|
|
5
|
+
// HQ-CLI-15 follow-up: ~100 ensureCognitoToken/ensureCognitoIdToken call sites
|
|
6
|
+
// fall back to browserLogin when a token refresh fails. Before this predicate
|
|
7
|
+
// the fallback ran for every caller unless HQ_NO_BROWSER_LOGIN=1 was set, so a
|
|
8
|
+
// daemon, lane, monitor, bot, `hq mcp serve`, sync runner, or launchd task
|
|
9
|
+
// opened a sign-in tab on its own that nobody had asked for. The implicit
|
|
10
|
+
// sign-in is now allowed only when a person is at a terminal: stdin AND stdout
|
|
11
|
+
// are TTYs and none of the background markers below is present. Explicit
|
|
12
|
+
// `hq login` calls browserLogin directly and is not gated here.
|
|
13
|
+
/** Parent-set switch: a child `hq` must never open a browser sign-in. */
|
|
14
|
+
export const HQ_NO_BROWSER_LOGIN_ENV = "HQ_NO_BROWSER_LOGIN";
|
|
15
|
+
/** Remedy printed when an implicit sign-in is refused. */
|
|
16
|
+
export const NON_INTERACTIVE_LOGIN_MESSAGE = "No valid HQ session and interactive login is disabled. Run `hq login` first. " +
|
|
17
|
+
"This process is not interactive (no terminal, or a background HQ runtime), so it did not open a browser sign-in.";
|
|
18
|
+
/** launchd job labels the HQ service manager installs (see main.ts runCli). */
|
|
19
|
+
const HQ_LAUNCHD_LABEL_PREFIXES = ["ai.indigo.", "ai.getindigo."];
|
|
20
|
+
/** Env markers set for background HQ runtimes. Any non-empty value counts. */
|
|
21
|
+
const BACKGROUND_ENV_MARKERS = [
|
|
22
|
+
"HQ_LANE_ID", // hq lanes worker sessions
|
|
23
|
+
"HQ_BOT_AGENT_UID", // hq bot runtimes
|
|
24
|
+
"HQ_DAEMON_UNIT", // daemon service units
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* argv prefixes (after `hq`) for long-running background entry points. The
|
|
28
|
+
* daemon's sync runner child has no TTY on stdio, so the TTY check covers it.
|
|
29
|
+
*/
|
|
30
|
+
const BACKGROUND_COMMANDS = [
|
|
31
|
+
["daemon"],
|
|
32
|
+
["mesh", "daemon"],
|
|
33
|
+
["bot", "run"],
|
|
34
|
+
["mcp", "serve"],
|
|
35
|
+
["monitor"],
|
|
36
|
+
];
|
|
37
|
+
function nonEmpty(value) {
|
|
38
|
+
return Boolean(value && value.trim());
|
|
39
|
+
}
|
|
40
|
+
/** The reason a caller is not interactive, or null when it is. */
|
|
41
|
+
export function nonInteractiveReason(input = {}) {
|
|
42
|
+
const env = input.env ?? process.env;
|
|
43
|
+
const argv = input.argv ?? process.argv.slice(2);
|
|
44
|
+
const stdinIsTTY = input.stdinIsTTY ?? process.stdin.isTTY === true;
|
|
45
|
+
const stdoutIsTTY = input.stdoutIsTTY ?? process.stdout.isTTY === true;
|
|
46
|
+
if (env[HQ_NO_BROWSER_LOGIN_ENV]?.trim() === "1")
|
|
47
|
+
return `${HQ_NO_BROWSER_LOGIN_ENV}=1`;
|
|
48
|
+
if (!stdinIsTTY)
|
|
49
|
+
return "stdin is not a terminal";
|
|
50
|
+
if (!stdoutIsTTY)
|
|
51
|
+
return "stdout is not a terminal";
|
|
52
|
+
for (const marker of BACKGROUND_ENV_MARKERS) {
|
|
53
|
+
if (nonEmpty(env[marker]))
|
|
54
|
+
return `${marker} is set`;
|
|
55
|
+
}
|
|
56
|
+
const xpc = env.XPC_SERVICE_NAME ?? "";
|
|
57
|
+
if (HQ_LAUNCHD_LABEL_PREFIXES.some((p) => xpc.startsWith(p)))
|
|
58
|
+
return "launchd task";
|
|
59
|
+
const args = argv.filter((a) => !a.startsWith("-"));
|
|
60
|
+
for (const cmd of BACKGROUND_COMMANDS) {
|
|
61
|
+
if (cmd.every((part, i) => args[i] === part))
|
|
62
|
+
return `\`hq ${cmd.join(" ")}\``;
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
/** True only when a person is at a terminal and may complete a browser sign-in. */
|
|
67
|
+
export function isInteractiveCaller(input = {}) {
|
|
68
|
+
return nonInteractiveReason(input) === null;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=interactive-caller.js.map
|
|
@@ -2,6 +2,7 @@ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
|
2
2
|
import { createServer } from "node:http";
|
|
3
3
|
import { browserLogin, saveCachedTokens, CognitoAuthError } from "@indigoai-us/hq-cloud";
|
|
4
4
|
import { isHeadless } from "./headless.js";
|
|
5
|
+
import { withRegisteredCallbackPort } from "./cognito-callback-port.js";
|
|
5
6
|
function assertHumanLoginAllowed() {
|
|
6
7
|
// Preserve hq-cloud's company-machine boundary on the manual URL path.
|
|
7
8
|
if (["1", "true", "yes"].includes(process.env.HQ_MACHINE_AUTH_REQUIRED?.trim().toLowerCase() ?? "")) {
|
|
@@ -10,8 +11,9 @@ function assertHumanLoginAllowed() {
|
|
|
10
11
|
}
|
|
11
12
|
/** Keep desktop login unchanged; manual login uses the same PKCE loopback flow. */
|
|
12
13
|
export async function loginWithBrowserPreference(config, browser = true) {
|
|
14
|
+
// A callback port outside the registered set opens a redirect_mismatch tab.
|
|
13
15
|
if (browser && !isHeadless())
|
|
14
|
-
return browserLogin(config);
|
|
16
|
+
return browserLogin(withRegisteredCallbackPort(config));
|
|
15
17
|
assertHumanLoginAllowed();
|
|
16
18
|
const port = config.port ?? 3000;
|
|
17
19
|
const redirectUri = `http://localhost:${port}/callback`;
|
|
@@ -28,6 +28,8 @@ export const RESIDENT_PACKAGE_MODULES = {
|
|
|
28
28
|
],
|
|
29
29
|
"agents-terminal": ["../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
30
30
|
bot: [
|
|
31
|
+
"../commands/lanes.js",
|
|
32
|
+
"../lib/lanes/reconcile.js",
|
|
31
33
|
"../lib/flag-registry.js",
|
|
32
34
|
"../commands/access.js",
|
|
33
35
|
"../commands/sources.js",
|
|
@@ -176,6 +178,8 @@ const LOADERS = {
|
|
|
176
178
|
() => import("@indigoai-us/hq-cloud"),
|
|
177
179
|
],
|
|
178
180
|
bot: [
|
|
181
|
+
() => import("../commands/lanes.js"),
|
|
182
|
+
() => import("../lib/lanes/reconcile.js"),
|
|
179
183
|
() => import("../lib/flag-registry.js"),
|
|
180
184
|
() => import("../commands/access.js"),
|
|
181
185
|
// The signals engine's adapters step (lib/signals/steps/adapters.ts)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@indigoai-us/hq-cli",
|
|
3
|
-
"version": "5.345.
|
|
3
|
+
"version": "5.345.199",
|
|
4
4
|
"description": "HQ by Indigo management CLI — modules and cloud sync",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
],
|
|
28
28
|
"scripts": {
|
|
29
29
|
"build": "node scripts/generate-dsn.mjs && node scripts/build-flag-registry-worker.mjs && tsc && node scripts/generate-command-catalog.mjs --check && node scripts/copy-schemas.mjs && node scripts/chmod-bins.mjs && node scripts/bundle-sync-runner.mjs",
|
|
30
|
+
"release:preflight": "node scripts/check-fleet-local-pin-published.mjs",
|
|
31
|
+
"release:tag": "node scripts/check-fleet-local-pin-published.mjs --release-tag",
|
|
30
32
|
"prepublishOnly": "npm run build",
|
|
31
33
|
"pretypecheck": "node scripts/generate-dsn.mjs && node scripts/build-flag-registry-worker.mjs",
|
|
32
34
|
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.typetest.json",
|
|
@@ -114,11 +116,11 @@
|
|
|
114
116
|
"*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}": "eslint --cache --no-error-on-unmatched-pattern"
|
|
115
117
|
},
|
|
116
118
|
"optionalDependencies": {
|
|
117
|
-
"@indigoai-us/hq-hook-win32-x64": "5.345.
|
|
118
|
-
"@indigoai-us/hq-hook-win32-arm64": "5.345.
|
|
119
|
-
"@indigoai-us/hq-hook-darwin-x64": "5.345.
|
|
120
|
-
"@indigoai-us/hq-hook-darwin-arm64": "5.345.
|
|
121
|
-
"@indigoai-us/hq-hook-linux-x64": "5.345.
|
|
122
|
-
"@indigoai-us/hq-hook-linux-arm64": "5.345.
|
|
119
|
+
"@indigoai-us/hq-hook-win32-x64": "5.345.199",
|
|
120
|
+
"@indigoai-us/hq-hook-win32-arm64": "5.345.199",
|
|
121
|
+
"@indigoai-us/hq-hook-darwin-x64": "5.345.199",
|
|
122
|
+
"@indigoai-us/hq-hook-darwin-arm64": "5.345.199",
|
|
123
|
+
"@indigoai-us/hq-hook-linux-x64": "5.345.199",
|
|
124
|
+
"@indigoai-us/hq-hook-linux-arm64": "5.345.199"
|
|
123
125
|
}
|
|
124
126
|
}
|