@indigoai-us/hq-cli 5.345.43 → 5.345.45
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 +18 -0
- package/dist/command-catalog.generated.d.ts +17 -1
- package/dist/command-catalog.generated.js +21 -1
- package/dist/commands/access.js +6 -4
- package/dist/commands/bot.js +1 -1
- package/dist/commands/db-destroy.js +5 -4
- package/dist/commands/db-remote-deps.js +4 -0
- package/dist/commands/lanes-checks.d.ts +1 -0
- package/dist/commands/lanes-checks.js +6 -1
- package/dist/commands/lanes-message.js +84 -51
- package/dist/commands/lanes-report.d.ts +2 -1
- package/dist/commands/lanes-report.js +63 -6
- package/dist/commands/lanes-resume.js +44 -0
- package/dist/commands/lanes-shared.d.ts +25 -0
- package/dist/commands/lanes.d.ts +4 -0
- package/dist/commands/lanes.js +228 -9
- package/dist/commands/meetings.js +4 -1
- package/dist/index.js +43 -7
- package/dist/lib/core-utils/sentry-report.js +8 -1
- package/dist/lib/db/control-plane.d.ts +5 -0
- package/dist/lib/db/control-plane.js +7 -0
- package/dist/lib/flag-snapshot-file.js +33 -25
- package/dist/lib/lanes/flag-keys.d.ts +4 -0
- package/dist/lib/lanes/flag-keys.js +4 -0
- package/dist/lib/lanes/focus-guard.d.ts +65 -0
- package/dist/lib/lanes/focus-guard.js +91 -0
- package/dist/lib/lanes/grants.js +1 -1
- package/dist/lib/lanes/outpost-dispatch.d.ts +1 -0
- package/dist/lib/lanes/outpost-dispatch.js +1 -0
- package/dist/lib/lanes/reconcile.d.ts +2 -0
- package/dist/lib/lanes/reconcile.js +39 -1
- package/dist/lib/lanes/spawn.d.ts +4 -0
- package/dist/lib/lanes/spawn.js +132 -6
- package/dist/lib/lanes/store.js +64 -0
- package/dist/lib/lanes/story-ledger.d.ts +14 -0
- package/dist/lib/lanes/story-ledger.js +46 -0
- package/dist/lib/lanes/tracker-report.d.ts +46 -0
- package/dist/lib/lanes/tracker-report.js +85 -1
- package/dist/lib/lanes/types.d.ts +12 -2
- package/dist/lib/lanes/types.js +5 -3
- package/dist/main.js +5 -1
- package/dist/startup-registration.d.ts +3 -0
- package/dist/startup-registration.js +11 -4
- package/dist/utils/package-use-lease.d.ts +1 -0
- package/dist/utils/package-use-lease.js +29 -0
- package/dist/utils/self-update-backoff.d.ts +17 -0
- package/dist/utils/self-update-backoff.js +73 -0
- package/dist/utils/self-update.d.ts +7 -0
- package/dist/utils/self-update.js +36 -0
- package/dist/utils/vault-api.d.ts +33 -1
- package/dist/utils/vault-api.js +55 -5
- package/dist/utils/version-gate.d.ts +13 -0
- package/dist/utils/version-gate.js +35 -5
- package/package.json +1 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-target backoff for automatic CLI self-updates.
|
|
3
|
+
*
|
|
4
|
+
* The older loop guard in `version-check.ts` (`markLatestIneffective`) only
|
|
5
|
+
* suppresses the exact npm `latest` that failed, and only when the version
|
|
6
|
+
* cache file exists and still names that version. HQ publishes several
|
|
7
|
+
* releases a day, and the hq-pro "update recommended" path hands the updater a
|
|
8
|
+
* version that need not match the cache. Either way the next invocation saw a
|
|
9
|
+
* "new" target and ran the same failing install again, so a broken package
|
|
10
|
+
* manager made every `hq` command retry the update.
|
|
11
|
+
*
|
|
12
|
+
* This record is keyed on the failure itself, not on the target version: after
|
|
13
|
+
* any failed or ineffective automatic update, automatic updates pause for
|
|
14
|
+
* {@link SELF_UPDATE_FAILURE_BACKOFF_MS} whatever version is newest. An explicit
|
|
15
|
+
* `hq rescue` ignores the record and clears it when it succeeds.
|
|
16
|
+
*/
|
|
17
|
+
import * as fs from "node:fs";
|
|
18
|
+
import * as os from "node:os";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
export const SELF_UPDATE_FAILURE_BACKOFF_MS = 24 * 60 * 60 * 1000;
|
|
21
|
+
export function selfUpdateBackoffPath(homeDir = os.homedir()) {
|
|
22
|
+
return path.join(homeDir, ".hq", "self-update-backoff.json");
|
|
23
|
+
}
|
|
24
|
+
function readRecord(file) {
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
27
|
+
if (typeof parsed.target !== "string" || typeof parsed.failedAt !== "number")
|
|
28
|
+
return null;
|
|
29
|
+
if (!Number.isFinite(parsed.failedAt))
|
|
30
|
+
return null;
|
|
31
|
+
return { target: parsed.target, failedAt: parsed.failedAt };
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** File-backed backoff under `~/.hq`. Every operation is best-effort. */
|
|
38
|
+
export function fileSelfUpdateBackoff(homeDir) {
|
|
39
|
+
const file = selfUpdateBackoffPath(homeDir);
|
|
40
|
+
return {
|
|
41
|
+
active(now = Date.now()) {
|
|
42
|
+
const record = readRecord(file);
|
|
43
|
+
if (!record)
|
|
44
|
+
return null;
|
|
45
|
+
const age = now - record.failedAt;
|
|
46
|
+
// A record from the future (clock moved back) is treated as fresh only
|
|
47
|
+
// within one window, so a bad clock cannot disable updates forever.
|
|
48
|
+
if (age < -SELF_UPDATE_FAILURE_BACKOFF_MS)
|
|
49
|
+
return null;
|
|
50
|
+
return age <= SELF_UPDATE_FAILURE_BACKOFF_MS ? record : null;
|
|
51
|
+
},
|
|
52
|
+
record(target, now = Date.now()) {
|
|
53
|
+
try {
|
|
54
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
55
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
56
|
+
fs.writeFileSync(tmp, `${JSON.stringify({ target, failedAt: now })}\n`);
|
|
57
|
+
fs.renameSync(tmp, file);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// A write failure only means the next invocation may retry once.
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
clear() {
|
|
64
|
+
try {
|
|
65
|
+
fs.rmSync(file, { force: true });
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Best-effort.
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=self-update-backoff.js.map
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
import { spawn } from "node:child_process";
|
|
60
60
|
import * as fs from "node:fs";
|
|
61
61
|
import { probePnpmVersion, type RunningInstall, type UpdateResult } from "./version-gate.js";
|
|
62
|
+
import { type SelfUpdateBackoff } from "./self-update-backoff.js";
|
|
62
63
|
import { type RestartDaemonResult } from "../lib/mesh/live/daemon/install.js";
|
|
63
64
|
/**
|
|
64
65
|
* Set on the re-exec'd child so it can never self-update (and re-exec) again.
|
|
@@ -167,6 +168,12 @@ export interface SelfUpdateDeps {
|
|
|
167
168
|
* stops auto-retrying it. Defaults to {@link markLatestIneffective}.
|
|
168
169
|
*/
|
|
169
170
|
markIneffective?: (version: string) => void;
|
|
171
|
+
/**
|
|
172
|
+
* Cross-target failure backoff. After a failed or ineffective automatic
|
|
173
|
+
* update, automatic updates pause for 24 hours. Defaults to a record under
|
|
174
|
+
* `~/.hq` (see `self-update-backoff.ts`).
|
|
175
|
+
*/
|
|
176
|
+
backoff?: SelfUpdateBackoff;
|
|
170
177
|
/**
|
|
171
178
|
* Whether a human is watching this invocation. Defaults to "stderr is a TTY",
|
|
172
179
|
* which is false for exactly the callers that must not replace the CLI
|
|
@@ -69,6 +69,7 @@ import { buildBunInstallArgv, buildPnpmInstallArgv, buildPrefixedInstallArgv, bu
|
|
|
69
69
|
import { canStageInstall, stagedNpmInstall } from "./staged-install.js";
|
|
70
70
|
import { acquireUpdateLock as acquireSharedUpdateLock } from "./update-lock.js";
|
|
71
71
|
import { markLatestIneffective } from "./version-check.js";
|
|
72
|
+
import { fileSelfUpdateBackoff } from "./self-update-backoff.js";
|
|
72
73
|
import { restartDaemonServiceIfInstalled } from "../lib/mesh/live/daemon/install.js";
|
|
73
74
|
import { createCurrentPackageUseLeaseHandoff, PACKAGE_USE_LEASE_HANDOFF_ENV, releaseCurrentPackageUseLease, } from "./package-use-lease.js";
|
|
74
75
|
/**
|
|
@@ -847,9 +848,44 @@ function sleepForRetry(ms) {
|
|
|
847
848
|
}
|
|
848
849
|
async function updateAndReexec(argv, flavor, known, deps) {
|
|
849
850
|
const env = deps.env ?? process.env;
|
|
851
|
+
// Recursion guard: a process started by an update (the re-exec'd command, a
|
|
852
|
+
// package-manager lifecycle script, or anything those spawn) carries this
|
|
853
|
+
// marker and must never start another update.
|
|
850
854
|
if (env[REEXEC_GUARD_ENV] === "1" || env.HQ_NO_UPDATE_CHECK === "1") {
|
|
851
855
|
return { action: "skipped" };
|
|
852
856
|
}
|
|
857
|
+
// A failed automatic update pauses automatic updates for every target, not
|
|
858
|
+
// just the one that failed: HQ ships several releases a day, so a target-keyed
|
|
859
|
+
// guard let each new release re-run the same broken install on every
|
|
860
|
+
// command. `hq rescue` is an explicit request and ignores the backoff.
|
|
861
|
+
const backoff = deps.backoff ?? fileSelfUpdateBackoff(deps.homeDir);
|
|
862
|
+
const explicit = flavor.noun === "rescue";
|
|
863
|
+
if (!explicit && backoff.active()) {
|
|
864
|
+
return { action: "skipped" };
|
|
865
|
+
}
|
|
866
|
+
let outcome;
|
|
867
|
+
try {
|
|
868
|
+
outcome = await attemptUpdateAndReexec(argv, flavor, known, deps, env);
|
|
869
|
+
}
|
|
870
|
+
catch (error) {
|
|
871
|
+
if (known)
|
|
872
|
+
backoff.record(known);
|
|
873
|
+
else
|
|
874
|
+
backoff.record("unknown");
|
|
875
|
+
throw error;
|
|
876
|
+
}
|
|
877
|
+
if (outcome.action === "update-failed" || outcome.action === "update-ineffective") {
|
|
878
|
+
backoff.record(outcome.latest ?? known ?? "unknown");
|
|
879
|
+
}
|
|
880
|
+
else if (outcome.action === "updated" ||
|
|
881
|
+
outcome.action === "reexec" ||
|
|
882
|
+
outcome.action === "updated-no-reexec" ||
|
|
883
|
+
outcome.action === "current") {
|
|
884
|
+
backoff.clear();
|
|
885
|
+
}
|
|
886
|
+
return outcome;
|
|
887
|
+
}
|
|
888
|
+
async function attemptUpdateAndReexec(argv, flavor, known, deps, env) {
|
|
853
889
|
const latest = known ?? (await (deps.fetchLatest ?? fetchLatestVersion)());
|
|
854
890
|
if (!latest)
|
|
855
891
|
return { action: "skipped" };
|
|
@@ -77,7 +77,39 @@ export declare function getCompanyUid(token: string, companySlug: string | undef
|
|
|
77
77
|
baseUrl?: string, options?: {
|
|
78
78
|
allowOfflineScopeFallback?: boolean;
|
|
79
79
|
}): Promise<string>;
|
|
80
|
-
|
|
80
|
+
/**
|
|
81
|
+
* The `agt_*` uid an agent machine token names, or null for any other token.
|
|
82
|
+
* An agent ID token carries `custom:entityType=agent` and
|
|
83
|
+
* `custom:entityUid=agt_*`; a person token carries neither. The claim is read
|
|
84
|
+
* without verification and only picks which identity the CLI resolves locally;
|
|
85
|
+
* hq-pro verifies the token and authorizes every request itself.
|
|
86
|
+
*/
|
|
87
|
+
export declare function agentUidFromToken(token: string): string | null;
|
|
88
|
+
export type CallerIdentity = {
|
|
89
|
+
kind: 'person';
|
|
90
|
+
uid: string;
|
|
91
|
+
} | {
|
|
92
|
+
kind: 'agent';
|
|
93
|
+
uid: string;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Resolve the caller's own principal uid. An agent machine token resolves to
|
|
97
|
+
* its `agt_*` uid with no network call (agents have no person entity); any
|
|
98
|
+
* other token resolves to its canonical `prs_*` uid exactly as
|
|
99
|
+
* {@link resolveCallerPersonUid} does. Use this where the server accepts an
|
|
100
|
+
* agent principal; use `resolveCallerPersonUid` where only a person can act.
|
|
101
|
+
*/
|
|
102
|
+
export declare function resolveCallerUid(token: string, baseUrl?: string): Promise<CallerIdentity>;
|
|
103
|
+
/** Thrown when a person-only command runs under an agent machine identity. */
|
|
104
|
+
export declare function agentIdentityUnsupportedError(agentUid: string, command?: string): Error;
|
|
105
|
+
/**
|
|
106
|
+
* Resolve the caller's canonical `prs_*` uid for a person-only command. An
|
|
107
|
+
* agent machine token fails fast with a clear "not available to agent
|
|
108
|
+
* identities" error instead of the person lookup's "No person entity found".
|
|
109
|
+
*/
|
|
110
|
+
export declare function resolveCallerPersonUid(token: string, baseUrl?: string, opts?: {
|
|
111
|
+
command?: string;
|
|
112
|
+
}): Promise<string>;
|
|
81
113
|
export declare function getEntityUid(token: string, opts: {
|
|
82
114
|
personal?: boolean;
|
|
83
115
|
companySlug?: string;
|
package/dist/utils/vault-api.js
CHANGED
|
@@ -7,6 +7,7 @@ import { markExpectedUserError } from './expected-cli-error.js';
|
|
|
7
7
|
import { redactErrorText } from './redact-error-text.js';
|
|
8
8
|
import { isSecretLoadUnavailable, isUnavailableSecretLoadHttpStatus, SecretLoadUnavailableError } from './secret-load-availability.js';
|
|
9
9
|
import { readCachedScopeUid, writeCachedScopeUid } from './secrets-cache.js';
|
|
10
|
+
import { peekIdToken } from './id-token.js';
|
|
10
11
|
/**
|
|
11
12
|
* Identity / company resolution lookups must never hang forever. These small
|
|
12
13
|
* GETs run BEFORE a command does its real work (e.g. `hq secrets env` resolves
|
|
@@ -679,9 +680,57 @@ baseUrl, options = {}) {
|
|
|
679
680
|
return cachedUid;
|
|
680
681
|
}
|
|
681
682
|
}
|
|
683
|
+
const AGENT_UID_PATTERN = /^agt_[A-Za-z0-9_-]+$/;
|
|
684
|
+
/**
|
|
685
|
+
* The `agt_*` uid an agent machine token names, or null for any other token.
|
|
686
|
+
* An agent ID token carries `custom:entityType=agent` and
|
|
687
|
+
* `custom:entityUid=agt_*`; a person token carries neither. The claim is read
|
|
688
|
+
* without verification and only picks which identity the CLI resolves locally;
|
|
689
|
+
* hq-pro verifies the token and authorizes every request itself.
|
|
690
|
+
*/
|
|
691
|
+
export function agentUidFromToken(token) {
|
|
692
|
+
const claims = peekIdToken(token);
|
|
693
|
+
const entityUid = claims['custom:entityUid'];
|
|
694
|
+
if (typeof entityUid !== 'string' || !AGENT_UID_PATTERN.test(entityUid))
|
|
695
|
+
return null;
|
|
696
|
+
const entityType = claims['custom:entityType'];
|
|
697
|
+
if (entityType !== undefined && entityType !== 'agent')
|
|
698
|
+
return null;
|
|
699
|
+
return entityUid;
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Resolve the caller's own principal uid. An agent machine token resolves to
|
|
703
|
+
* its `agt_*` uid with no network call (agents have no person entity); any
|
|
704
|
+
* other token resolves to its canonical `prs_*` uid exactly as
|
|
705
|
+
* {@link resolveCallerPersonUid} does. Use this where the server accepts an
|
|
706
|
+
* agent principal; use `resolveCallerPersonUid` where only a person can act.
|
|
707
|
+
*/
|
|
708
|
+
export async function resolveCallerUid(token, baseUrl) {
|
|
709
|
+
const agentUid = agentUidFromToken(token);
|
|
710
|
+
if (agentUid)
|
|
711
|
+
return { kind: 'agent', uid: agentUid };
|
|
712
|
+
return { kind: 'person', uid: await lookupCallerPersonUid(token, baseUrl) };
|
|
713
|
+
}
|
|
714
|
+
/** Thrown when a person-only command runs under an agent machine identity. */
|
|
715
|
+
export function agentIdentityUnsupportedError(agentUid, command) {
|
|
716
|
+
const what = command ? `\`${command}\`` : 'This command';
|
|
717
|
+
return markExpectedUserError(new Error(`${what} is not available to agent identities (signed in as agent ${agentUid}). ` +
|
|
718
|
+
'It acts for a person; run it from a person\'s HQ login.'));
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Resolve the caller's canonical `prs_*` uid for a person-only command. An
|
|
722
|
+
* agent machine token fails fast with a clear "not available to agent
|
|
723
|
+
* identities" error instead of the person lookup's "No person entity found".
|
|
724
|
+
*/
|
|
725
|
+
export async function resolveCallerPersonUid(token, baseUrl, opts = {}) {
|
|
726
|
+
const agentUid = agentUidFromToken(token);
|
|
727
|
+
if (agentUid)
|
|
728
|
+
throw agentIdentityUnsupportedError(agentUid, opts.command);
|
|
729
|
+
return lookupCallerPersonUid(token, baseUrl);
|
|
730
|
+
}
|
|
682
731
|
// Same selection rule as the backend's `resolveCallerPersonUid`: ascending by
|
|
683
732
|
// createdAt, tie-break by uid ascending. Returns the `prs_*` UID.
|
|
684
|
-
|
|
733
|
+
async function lookupCallerPersonUid(token, baseUrl) {
|
|
685
734
|
const res = await vaultApiFetch({
|
|
686
735
|
token,
|
|
687
736
|
path: '/entity/by-type/person',
|
|
@@ -708,16 +757,17 @@ export async function resolveCallerPersonUid(token, baseUrl) {
|
|
|
708
757
|
});
|
|
709
758
|
return persons[0].uid;
|
|
710
759
|
}
|
|
711
|
-
// Resolves the scope UID (cmp_* or
|
|
712
|
-
// `--personal` → caller's canonical person entity
|
|
760
|
+
// Resolves the scope UID (cmp_*, prs_* or agt_*) for a secrets command.
|
|
761
|
+
// Precedence: `--personal` → caller's own scope (canonical person entity, or an
|
|
762
|
+
// agent's own `agt_*` vault, which hq-pro treats as agent-self scope); else `--company <slug>` →
|
|
713
763
|
// resolved company UID; else fallback to single active company membership.
|
|
714
764
|
export async function getEntityUid(token, opts, options = {}) {
|
|
715
765
|
if (opts.personal) {
|
|
716
766
|
if (!options.allowOfflineScopeFallback)
|
|
717
|
-
return
|
|
767
|
+
return (await resolveCallerUid(token)).uid;
|
|
718
768
|
const principalUid = cognitoCachePrincipalUid(token);
|
|
719
769
|
try {
|
|
720
|
-
const uid = await
|
|
770
|
+
const { uid } = await resolveCallerUid(token);
|
|
721
771
|
if (principalUid)
|
|
722
772
|
writeCachedScopeUid("personal", principalUid, uid);
|
|
723
773
|
return uid;
|
|
@@ -47,6 +47,19 @@ export interface VersionCheckResponse {
|
|
|
47
47
|
downloadUrl?: string;
|
|
48
48
|
message?: string;
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Environment for every package-manager child an update starts.
|
|
52
|
+
*
|
|
53
|
+
* - The recursion guard stops anything the install runs (lifecycle scripts,
|
|
54
|
+
* a postinstall that calls `hq`) from starting a second update.
|
|
55
|
+
* - The Corepack settings keep an update from changing the user's package
|
|
56
|
+
* manager setup. With `COREPACK_DEFAULT_TO_LATEST` unset, Corepack resolves a
|
|
57
|
+
* missing default from the registry's newest release and writes it to its
|
|
58
|
+
* global lastKnownGood file, which moved users from pnpm 10 to pnpm 12.
|
|
59
|
+
* Project selection and auto-pinning are off so the caller's working
|
|
60
|
+
* directory cannot pick, or be rewritten with, a package-manager version.
|
|
61
|
+
*/
|
|
62
|
+
export declare function packageManagerChildEnv(base?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
50
63
|
export declare function npmPrefixFromPackageDir(pkgDir: string): string | null;
|
|
51
64
|
/**
|
|
52
65
|
* Whether the running package lives inside a pnpm-managed **global** install.
|
|
@@ -55,7 +55,30 @@ export function getVersionGatePhase() {
|
|
|
55
55
|
return currentVersionGatePhase;
|
|
56
56
|
}
|
|
57
57
|
function isOptedOut() {
|
|
58
|
-
|
|
58
|
+
// A process started by an update (re-exec, package-manager lifecycle script)
|
|
59
|
+
// carries the recursion guard and must never start another install.
|
|
60
|
+
return process.env.HQ_NO_UPDATE_CHECK === "1" || process.env[UPDATE_RECURSION_GUARD_ENV] === "1";
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Environment for every package-manager child an update starts.
|
|
64
|
+
*
|
|
65
|
+
* - The recursion guard stops anything the install runs (lifecycle scripts,
|
|
66
|
+
* a postinstall that calls `hq`) from starting a second update.
|
|
67
|
+
* - The Corepack settings keep an update from changing the user's package
|
|
68
|
+
* manager setup. With `COREPACK_DEFAULT_TO_LATEST` unset, Corepack resolves a
|
|
69
|
+
* missing default from the registry's newest release and writes it to its
|
|
70
|
+
* global lastKnownGood file, which moved users from pnpm 10 to pnpm 12.
|
|
71
|
+
* Project selection and auto-pinning are off so the caller's working
|
|
72
|
+
* directory cannot pick, or be rewritten with, a package-manager version.
|
|
73
|
+
*/
|
|
74
|
+
export function packageManagerChildEnv(base = process.env) {
|
|
75
|
+
return {
|
|
76
|
+
...base,
|
|
77
|
+
[UPDATE_RECURSION_GUARD_ENV]: "1",
|
|
78
|
+
COREPACK_DEFAULT_TO_LATEST: "0",
|
|
79
|
+
COREPACK_ENABLE_PROJECT_SPEC: "0",
|
|
80
|
+
COREPACK_ENABLE_AUTO_PIN: "0",
|
|
81
|
+
};
|
|
59
82
|
}
|
|
60
83
|
function findCliPackageRoot(startDir) {
|
|
61
84
|
let dir = startDir;
|
|
@@ -608,6 +631,7 @@ export function pnpmUpdateEnv(install, base = process.env) {
|
|
|
608
631
|
// project, which can silently switch the update subprocess to that version.
|
|
609
632
|
COREPACK_ENABLE_PROJECT_SPEC: "0",
|
|
610
633
|
COREPACK_DEFAULT_TO_LATEST: "0",
|
|
634
|
+
COREPACK_ENABLE_AUTO_PIN: "0",
|
|
611
635
|
};
|
|
612
636
|
if (!base.PNPM_HOME)
|
|
613
637
|
result.PNPM_HOME = home;
|
|
@@ -723,10 +747,8 @@ export function probePnpmVersion(cmd, args, env, cwd) {
|
|
|
723
747
|
export function performPnpmUpdate(install, runner = runUpdateCommand, env = process.env, probe = probePnpmVersion) {
|
|
724
748
|
const cwd = mkdtempSync(path.join(os.tmpdir(), "hq-cli-pnpm-update-"));
|
|
725
749
|
const updateEnv = {
|
|
726
|
-
...env,
|
|
727
|
-
COREPACK_ENABLE_PROJECT_SPEC: "0",
|
|
750
|
+
...packageManagerChildEnv(env),
|
|
728
751
|
COREPACK_ENABLE_STRICT: "0",
|
|
729
|
-
COREPACK_DEFAULT_TO_LATEST: "0",
|
|
730
752
|
npm_config_manage_package_manager_versions: "false",
|
|
731
753
|
};
|
|
732
754
|
try {
|
|
@@ -1125,6 +1147,14 @@ export function openInstallOutput(verbose, isTty = process.stdout.isTTY === true
|
|
|
1125
1147
|
};
|
|
1126
1148
|
}
|
|
1127
1149
|
export function runSupervisedUpdateCommand(cmd, args, output, env, cwd) {
|
|
1150
|
+
// This process was itself started by an update. Starting another install
|
|
1151
|
+
// from here is how one failed update turns into a process storm, so refuse.
|
|
1152
|
+
if (process.env[UPDATE_RECURSION_GUARD_ENV] === "1") {
|
|
1153
|
+
const detail = "refusing to start a package-manager install from inside another hq update " +
|
|
1154
|
+
`(${UPDATE_RECURSION_GUARD_ENV}=1 is set)`;
|
|
1155
|
+
console.error(chalk.yellow(`hq: ${detail}.`));
|
|
1156
|
+
return { ok: false, code: "EHQUPDATERECURSION", detail };
|
|
1157
|
+
}
|
|
1128
1158
|
try {
|
|
1129
1159
|
const plan = buildSpawnPlan(cmd, args);
|
|
1130
1160
|
const timeoutMs = updateTimeoutMs(env);
|
|
@@ -1147,7 +1177,7 @@ export function runSupervisedUpdateCommand(cmd, args, output, env, cwd) {
|
|
|
1147
1177
|
}),
|
|
1148
1178
|
], inOwnProcessGroup({
|
|
1149
1179
|
stdio: output.stdio,
|
|
1150
|
-
|
|
1180
|
+
env: packageManagerChildEnv(env ?? process.env),
|
|
1151
1181
|
...(cwd ? { cwd } : {}),
|
|
1152
1182
|
}));
|
|
1153
1183
|
// spawnSync reports a missing executable via `error`, not a throw.
|