@indigoai-us/hq-cli 5.241.0 → 5.243.0
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 +31 -0
- package/dist/command-catalog.generated.d.ts +2 -2
- package/dist/command-catalog.generated.js +2 -2
- package/dist/commands/auth.js +73 -16
- package/dist/commands/lanes-accounts.d.ts +25 -0
- package/dist/commands/lanes-accounts.js +527 -0
- package/dist/commands/lanes-questions.d.ts +34 -0
- package/dist/commands/lanes-questions.js +876 -0
- package/dist/commands/lanes-shared.d.ts +155 -0
- package/dist/commands/lanes-shared.js +2 -0
- package/dist/commands/lanes.d.ts +4 -12
- package/dist/commands/lanes.js +130 -1478
- package/dist/commands/safe-extract.d.ts +11 -2
- package/dist/commands/safe-extract.js +37 -8
- package/dist/lib/auth-login-provider-state.d.ts +8 -0
- package/dist/lib/auth-login-provider-state.js +63 -0
- package/dist/lib/install-path-compatibility-flag.d.ts +9 -0
- package/dist/lib/install-path-compatibility-flag.js +12 -0
- package/dist/lib/lanes/dropbox.d.ts +1 -6
- package/dist/lib/lanes/dropbox.js +6 -11
- package/dist/lib/lanes/message-replay.d.ts +0 -1
- package/dist/lib/lanes/message-replay.js +0 -1
- package/dist/main.js +9 -6
- package/dist/utils/login-provider.d.ts +7 -0
- package/dist/utils/login-provider.js +34 -0
- package/dist/utils/version-gate.d.ts +3 -3
- package/dist/utils/version-gate.js +19 -10
- package/package.json +2 -1
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
* actually written (we do NOT rely solely on the pre-flight scan or on the
|
|
33
33
|
* tar binary's own protections).
|
|
34
34
|
*/
|
|
35
|
+
import { execFileSync } from 'child_process';
|
|
35
36
|
export interface ExtractCaps {
|
|
36
37
|
/** Max total uncompressed bytes across all entries. Default 256 MiB. */
|
|
37
38
|
maxUncompressedBytes: number;
|
|
@@ -109,9 +110,13 @@ export declare function resolveContainedPath(targetDir: string, entryName: strin
|
|
|
109
110
|
export declare function assertLinkTargetContained(packDir: string, entryAbs: string, linkTarget: string): void;
|
|
110
111
|
/**
|
|
111
112
|
* List a gzip-compressed tarball's entries via `tar -tvf` (no extraction).
|
|
112
|
-
* argv form
|
|
113
|
+
* argv form, never a shell string, keeps hostile filenames out of shell syntax.
|
|
114
|
+
* When the default-off compatibility flag is enabled, Windows drive paths use
|
|
115
|
+
* a basename relative to the archive directory. GNU tar cannot parse `C:\\...`
|
|
116
|
+
* as remote `host:path`, and Windows' bundled bsdtar accepts the same relative
|
|
117
|
+
* argv without GNU-only `--force-local`.
|
|
113
118
|
*/
|
|
114
|
-
export declare function listTarball(tarballPath: string): TarEntry[];
|
|
119
|
+
export declare function listTarball(tarballPath: string, opts?: Pick<SafeExtractOptions, 'platform' | 'useRelativeWindowsDrivePath' | 'execFileSync'>): TarEntry[];
|
|
115
120
|
/**
|
|
116
121
|
* Pre-flight validation against the entry headers. Enforces (a) containment of
|
|
117
122
|
* every entry name under `targetDir`, (b) containment of every link target
|
|
@@ -129,6 +134,10 @@ export declare function validateEntries(entries: TarEntry[], targetDir: string,
|
|
|
129
134
|
export declare function assertExtractContained(stagingDir: string): void;
|
|
130
135
|
export interface SafeExtractOptions {
|
|
131
136
|
caps?: ExtractCaps;
|
|
137
|
+
/** Test seams for platform-specific argv construction; runtime gating uses hq-flags. */
|
|
138
|
+
platform?: NodeJS.Platform;
|
|
139
|
+
useRelativeWindowsDrivePath?: boolean;
|
|
140
|
+
execFileSync?: typeof execFileSync;
|
|
132
141
|
/**
|
|
133
142
|
* Test seam: invoked AFTER `tar` extracts into the staging dir but BEFORE
|
|
134
143
|
* the post-extract sweep / atomic rename. Throwing here simulates a failure
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
import { execFileSync } from 'child_process';
|
|
36
36
|
import * as fs from 'fs';
|
|
37
37
|
import * as path from 'path';
|
|
38
|
+
import { installPathCompatibilityEnabled } from '../lib/install-path-compatibility-flag.js';
|
|
38
39
|
export const DEFAULT_CAPS = {
|
|
39
40
|
// A content pack is docs/skills/policies/small scripts. 256 MiB is already
|
|
40
41
|
// generous; anything larger is almost certainly a bomb or a mistake.
|
|
@@ -203,18 +204,34 @@ export function assertLinkTargetContained(packDir, entryAbs, linkTarget) {
|
|
|
203
204
|
`"${linkTarget}" -> "${resolvedTarget}", outside pack dir "${path.resolve(packDir)}".`);
|
|
204
205
|
}
|
|
205
206
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
function tarballArgvLocation(tarballPath, platform, useRelativeWindowsDrivePath) {
|
|
208
|
+
if (platform === 'win32' &&
|
|
209
|
+
useRelativeWindowsDrivePath &&
|
|
210
|
+
/^[A-Za-z]:[\\/]/.test(tarballPath)) {
|
|
211
|
+
return {
|
|
212
|
+
archiveArg: path.win32.basename(tarballPath),
|
|
213
|
+
cwd: path.win32.dirname(tarballPath),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
return { archiveArg: tarballPath };
|
|
217
|
+
}
|
|
209
218
|
/**
|
|
210
219
|
* List a gzip-compressed tarball's entries via `tar -tvf` (no extraction).
|
|
211
|
-
* argv form
|
|
220
|
+
* argv form, never a shell string, keeps hostile filenames out of shell syntax.
|
|
221
|
+
* When the default-off compatibility flag is enabled, Windows drive paths use
|
|
222
|
+
* a basename relative to the archive directory. GNU tar cannot parse `C:\\...`
|
|
223
|
+
* as remote `host:path`, and Windows' bundled bsdtar accepts the same relative
|
|
224
|
+
* argv without GNU-only `--force-local`.
|
|
212
225
|
*/
|
|
213
|
-
export function listTarball(tarballPath) {
|
|
214
|
-
const
|
|
226
|
+
export function listTarball(tarballPath, opts = {}) {
|
|
227
|
+
const platform = opts.platform ?? process.platform;
|
|
228
|
+
const useRelativeWindowsDrivePath = opts.useRelativeWindowsDrivePath ?? installPathCompatibilityEnabled();
|
|
229
|
+
const location = tarballArgvLocation(tarballPath, platform, useRelativeWindowsDrivePath);
|
|
230
|
+
const out = (opts.execFileSync ?? execFileSync)('tar', ['-tvf', location.archiveArg], {
|
|
215
231
|
encoding: 'utf-8',
|
|
216
232
|
maxBuffer: 64 * 1024 * 1024,
|
|
217
233
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
234
|
+
...(location.cwd ? { cwd: location.cwd } : {}),
|
|
218
235
|
});
|
|
219
236
|
return parseTarListing(out);
|
|
220
237
|
}
|
|
@@ -311,8 +328,17 @@ export function safeExtractTarball(tarballPath, finalDir, opts = {}) {
|
|
|
311
328
|
if (fs.existsSync(finalDir)) {
|
|
312
329
|
throw new Error(`safeExtractTarball: refusing to extract over existing dir "${finalDir}".`);
|
|
313
330
|
}
|
|
331
|
+
// Resolve once so listing and extraction use the same archive location.
|
|
332
|
+
const platform = opts.platform ?? process.platform;
|
|
333
|
+
const useRelativeWindowsDrivePath = opts.useRelativeWindowsDrivePath ?? installPathCompatibilityEnabled();
|
|
334
|
+
const exec = opts.execFileSync ?? execFileSync;
|
|
335
|
+
const location = tarballArgvLocation(tarballPath, platform, useRelativeWindowsDrivePath);
|
|
314
336
|
// 1. Pre-flight against headers (no bytes written yet).
|
|
315
|
-
const entries = listTarball(tarballPath
|
|
337
|
+
const entries = listTarball(tarballPath, {
|
|
338
|
+
platform,
|
|
339
|
+
useRelativeWindowsDrivePath,
|
|
340
|
+
execFileSync: exec,
|
|
341
|
+
});
|
|
316
342
|
validateEntries(entries, finalDir, caps);
|
|
317
343
|
// 2. Sibling staging dir on the same filesystem so the rename is atomic.
|
|
318
344
|
const parent = path.dirname(path.resolve(finalDir));
|
|
@@ -322,7 +348,10 @@ export function safeExtractTarball(tarballPath, finalDir, opts = {}) {
|
|
|
322
348
|
// `--no-same-owner` avoids chown surprises when run as root; argv form, no
|
|
323
349
|
// shell. We deliberately re-extract here (not the pre-flight listing) and
|
|
324
350
|
// re-validate after, rather than trusting tar to honor any single flag.
|
|
325
|
-
|
|
351
|
+
exec('tar', ['-xzf', location.archiveArg, '-C', stagingDir, '--no-same-owner'], {
|
|
352
|
+
stdio: 'inherit',
|
|
353
|
+
...(location.cwd ? { cwd: location.cwd } : {}),
|
|
354
|
+
});
|
|
326
355
|
// 3. Independent backstop sweep over what actually landed.
|
|
327
356
|
assertExtractContained(stagingDir);
|
|
328
357
|
// 4. Failure-injection seam (tests).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RememberedLoginProvider } from "../utils/login-provider.js";
|
|
2
|
+
export declare const LOGIN_PROVIDER_STATE_FILE_NAME = "auth-login-provider.json";
|
|
3
|
+
export declare function getLastLoginProviderStatePath(): string;
|
|
4
|
+
/** Read only the provider preference; malformed or unknown state is ignored. */
|
|
5
|
+
export declare function loadLastLoginProvider(): RememberedLoginProvider | undefined;
|
|
6
|
+
/** Store only the provider name, clearing stale state when the choice is unknown. */
|
|
7
|
+
export declare function storeLastLoginProvider(provider: RememberedLoginProvider | undefined): void;
|
|
8
|
+
//# sourceMappingURL=auth-login-provider-state.d.ts.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { personTokenCacheFile } from "../utils/cognito-session.js";
|
|
5
|
+
export const LOGIN_PROVIDER_STATE_FILE_NAME = "auth-login-provider.json";
|
|
6
|
+
export function getLastLoginProviderStatePath() {
|
|
7
|
+
return path.join(path.dirname(personTokenCacheFile()), LOGIN_PROVIDER_STATE_FILE_NAME);
|
|
8
|
+
}
|
|
9
|
+
function isRememberedProvider(value) {
|
|
10
|
+
return value === "google" || value === "microsoft";
|
|
11
|
+
}
|
|
12
|
+
/** Read only the provider preference; malformed or unknown state is ignored. */
|
|
13
|
+
export function loadLastLoginProvider() {
|
|
14
|
+
let raw;
|
|
15
|
+
try {
|
|
16
|
+
raw = readFileSync(getLastLoginProviderStatePath(), "utf8");
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
let value;
|
|
25
|
+
try {
|
|
26
|
+
value = JSON.parse(raw);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
32
|
+
return undefined;
|
|
33
|
+
const state = value;
|
|
34
|
+
const keys = Object.keys(state).sort();
|
|
35
|
+
if (keys.length !== 2
|
|
36
|
+
|| keys[0] !== "lastProvider"
|
|
37
|
+
|| keys[1] !== "schemaVersion"
|
|
38
|
+
|| state.schemaVersion !== 1
|
|
39
|
+
|| !isRememberedProvider(state.lastProvider)) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return state.lastProvider;
|
|
43
|
+
}
|
|
44
|
+
/** Store only the provider name, clearing stale state when the choice is unknown. */
|
|
45
|
+
export function storeLastLoginProvider(provider) {
|
|
46
|
+
const filePath = getLastLoginProviderStatePath();
|
|
47
|
+
if (!provider) {
|
|
48
|
+
rmSync(filePath, { force: true });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const directory = path.dirname(filePath);
|
|
52
|
+
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
53
|
+
const temporaryPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
54
|
+
try {
|
|
55
|
+
const state = { schemaVersion: 1, lastProvider: provider };
|
|
56
|
+
writeFileSync(temporaryPath, JSON.stringify(state), { mode: 0o600, flag: "wx" });
|
|
57
|
+
renameSync(temporaryPath, filePath);
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
rmSync(temporaryPath, { force: true });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=auth-login-provider-state.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const INSTALL_PATH_COMPATIBILITY_FLAG = "cli.install-path-compatibility";
|
|
2
|
+
export declare const INSTALL_PATH_COMPATIBILITY_FALLBACK = false;
|
|
3
|
+
/**
|
|
4
|
+
* Read the default-off rollout flag from the process snapshot. Missing,
|
|
5
|
+
* unreadable, or not-yet-loaded registry state keeps the compatibility fixes
|
|
6
|
+
* disabled until the operator enables a canary.
|
|
7
|
+
*/
|
|
8
|
+
export declare function installPathCompatibilityEnabled(): boolean;
|
|
9
|
+
//# sourceMappingURL=install-path-compatibility-flag.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { resolveProcessFlagGate } from "./flag-registry.js";
|
|
2
|
+
export const INSTALL_PATH_COMPATIBILITY_FLAG = "cli.install-path-compatibility";
|
|
3
|
+
export const INSTALL_PATH_COMPATIBILITY_FALLBACK = false;
|
|
4
|
+
/**
|
|
5
|
+
* Read the default-off rollout flag from the process snapshot. Missing,
|
|
6
|
+
* unreadable, or not-yet-loaded registry state keeps the compatibility fixes
|
|
7
|
+
* disabled until the operator enables a canary.
|
|
8
|
+
*/
|
|
9
|
+
export function installPathCompatibilityEnabled() {
|
|
10
|
+
return resolveProcessFlagGate(INSTALL_PATH_COMPATIBILITY_FLAG, {}, () => INSTALL_PATH_COMPATIBILITY_FALLBACK);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=install-path-compatibility-flag.js.map
|
|
@@ -10,13 +10,12 @@
|
|
|
10
10
|
import * as fs from "node:fs";
|
|
11
11
|
import { probeProviderCompactionsSince } from "./compaction.js";
|
|
12
12
|
import { tryReadLane } from "./store.js";
|
|
13
|
-
import { type
|
|
13
|
+
import { type LaneRecord, type LaneState, type QuestionRecord } from "./types.js";
|
|
14
14
|
export declare const DROPBOX_DIR = "dropbox";
|
|
15
15
|
export declare const INBOX_NAME = "inbox.jsonl";
|
|
16
16
|
export declare const DRAINING_DIR = "draining";
|
|
17
17
|
export declare const DELIVERED_DIR = "delivered";
|
|
18
18
|
export declare const INFLIGHT_DIR = "inflight";
|
|
19
|
-
export { LANE_MESSAGE_COMPACTION_REPLAY_FLAG } from "./message-replay.js";
|
|
20
19
|
export interface DropboxMessage {
|
|
21
20
|
at?: string;
|
|
22
21
|
message_id?: string;
|
|
@@ -59,10 +58,6 @@ export interface LaneDrainHookInput {
|
|
|
59
58
|
hook_event_name?: string;
|
|
60
59
|
}
|
|
61
60
|
export interface RunDrainHookDeps {
|
|
62
|
-
readFlag?: (flag: string, options: {
|
|
63
|
-
company?: LaneCompanyRef | null;
|
|
64
|
-
env: NodeJS.ProcessEnv;
|
|
65
|
-
}) => Promise<boolean>;
|
|
66
61
|
readLane?: typeof tryReadLane;
|
|
67
62
|
probeCompactionsSince?: typeof probeProviderCompactionsSince;
|
|
68
63
|
}
|
|
@@ -12,8 +12,7 @@ import * as fs from "node:fs";
|
|
|
12
12
|
import * as path from "node:path";
|
|
13
13
|
import { probeProviderCompactionsSince, providerTranscriptSize, } from "./compaction.js";
|
|
14
14
|
import { LanesError } from "./errors.js";
|
|
15
|
-
import {
|
|
16
|
-
import { LANE_MESSAGE_COMPACTION_REPLAY_FLAG, MESSAGE_COMPACTION_REPLAY_NOTE, MESSAGE_REPLAY_STATE_NAME, } from "./message-replay.js";
|
|
15
|
+
import { MESSAGE_COMPACTION_REPLAY_NOTE, MESSAGE_REPLAY_STATE_NAME, } from "./message-replay.js";
|
|
17
16
|
import { LANES_RUNS_REL, readProcStarttime } from "./spawn.js";
|
|
18
17
|
import { tryReadLane } from "./store.js";
|
|
19
18
|
export const DROPBOX_DIR = "dropbox";
|
|
@@ -21,7 +20,6 @@ export const INBOX_NAME = "inbox.jsonl";
|
|
|
21
20
|
export const DRAINING_DIR = "draining";
|
|
22
21
|
export const DELIVERED_DIR = "delivered";
|
|
23
22
|
export const INFLIGHT_DIR = "inflight";
|
|
24
|
-
export { LANE_MESSAGE_COMPACTION_REPLAY_FLAG } from "./message-replay.js";
|
|
25
23
|
function defaultDrainDeps() {
|
|
26
24
|
return {
|
|
27
25
|
mkdirSync: fs.mkdirSync.bind(fs),
|
|
@@ -492,8 +490,8 @@ function readReplayState(filePath, turnId) {
|
|
|
492
490
|
(state.flag_enabled !== undefined && typeof state.flag_enabled !== "boolean")) {
|
|
493
491
|
throw new LanesError("message_replay_state_invalid", `Replay state ${filePath} does not match the current provider turn`);
|
|
494
492
|
}
|
|
495
|
-
// Older state files persisted
|
|
496
|
-
// before the next write so the
|
|
493
|
+
// Older state files persisted the removed per-turn flag. Accept it, but
|
|
494
|
+
// remove it before the next write so the stale cache does not live on.
|
|
497
495
|
delete state.flag_enabled;
|
|
498
496
|
return state;
|
|
499
497
|
}
|
|
@@ -602,12 +600,9 @@ export async function runDrainHook(env, input = {}, deps = {}) {
|
|
|
602
600
|
}
|
|
603
601
|
else if (delta.count > 0) {
|
|
604
602
|
state.last_compaction_marker += delta.count;
|
|
605
|
-
|
|
606
|
-
if (
|
|
607
|
-
|
|
608
|
-
if (replayed.length > 0)
|
|
609
|
-
replayNote = MESSAGE_COMPACTION_REPLAY_NOTE;
|
|
610
|
-
}
|
|
603
|
+
replayed = deliveredMessagesForTurn(hqRoot, laneId, turnId);
|
|
604
|
+
if (replayed.length > 0)
|
|
605
|
+
replayNote = MESSAGE_COMPACTION_REPLAY_NOTE;
|
|
611
606
|
}
|
|
612
607
|
}
|
|
613
608
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export declare const LANE_MESSAGE_COMPACTION_REPLAY_FLAG = "lanes.message-compaction-replay";
|
|
2
1
|
export declare const MESSAGE_REPLAY_STATE_NAME = "message-compaction-replay.json";
|
|
3
2
|
export declare const MESSAGE_COMPACTION_REPLAY_NOTE = "These messages were delivered before a context compaction and still apply.";
|
|
4
3
|
export interface MessageReplayState {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export const LANE_MESSAGE_COMPACTION_REPLAY_FLAG = "lanes.message-compaction-replay";
|
|
2
1
|
export const MESSAGE_REPLAY_STATE_NAME = "message-compaction-replay.json";
|
|
3
2
|
export const MESSAGE_COMPACTION_REPLAY_NOTE = "These messages were delivered before a context compaction and still apply.";
|
|
4
3
|
//# sourceMappingURL=message-replay.js.map
|
package/dist/main.js
CHANGED
|
@@ -180,12 +180,15 @@ program.hook("preAction", async () => {
|
|
|
180
180
|
export async function runCli() {
|
|
181
181
|
const foregroundNetworkBudget = new ForegroundNetworkBudget();
|
|
182
182
|
activeForegroundNetworkBudget = foregroundNetworkBudget;
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
183
|
+
// Login must be usable before any identity or company is available, so do
|
|
184
|
+
// not make a pre-auth feature-flag request on this path. Other commands
|
|
185
|
+
// retain the shared asynchronous registry refresh.
|
|
186
|
+
const isAuthLogin = process.argv[2] === "auth" && process.argv[3] === "login";
|
|
187
|
+
if (!isAuthLogin) {
|
|
188
|
+
runGlobalNetworkLifecycle(process.argv, () => {
|
|
189
|
+
void kickFlagRegistryReadiness();
|
|
190
|
+
});
|
|
191
|
+
}
|
|
189
192
|
// Set when a self-update re-exec'd this command on a newer CLI: the child
|
|
190
193
|
// already did the work, so this process only has to carry its exit status
|
|
191
194
|
// out (after the finally block's telemetry, hence not process.exit here).
|
|
@@ -5,7 +5,14 @@ declare const PROVIDER_TO_COGNITO_IDP: {
|
|
|
5
5
|
readonly picker: undefined;
|
|
6
6
|
};
|
|
7
7
|
export type LoginProvider = keyof typeof PROVIDER_TO_COGNITO_IDP;
|
|
8
|
+
export type RememberedLoginProvider = Exclude<LoginProvider, "picker">;
|
|
9
|
+
export interface LoginProviderSelection {
|
|
10
|
+
config: CognitoAuthConfig;
|
|
11
|
+
providerToRemember?: RememberedLoginProvider;
|
|
12
|
+
}
|
|
8
13
|
export declare function parseLoginProvider(value: string | undefined): LoginProvider | undefined;
|
|
9
14
|
export declare function cognitoConfigForLoginProvider(value: string | undefined): CognitoAuthConfig;
|
|
15
|
+
/** Resolve CLI, environment, remembered, then first-login choice-page precedence. */
|
|
16
|
+
export declare function resolveLoginProviderSelection(explicitProvider: string | undefined, env: NodeJS.ProcessEnv, rememberedProvider?: RememberedLoginProvider): LoginProviderSelection;
|
|
10
17
|
export {};
|
|
11
18
|
//# sourceMappingURL=login-provider.d.ts.map
|
|
@@ -22,4 +22,38 @@ export function cognitoConfigForLoginProvider(value) {
|
|
|
22
22
|
identityProvider: PROVIDER_TO_COGNITO_IDP[provider],
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
+
function rememberedProviderForCognitoProvider(value) {
|
|
26
|
+
const normalized = value?.trim().toLowerCase();
|
|
27
|
+
if (normalized === "google")
|
|
28
|
+
return "google";
|
|
29
|
+
if (normalized === "microsoft" || normalized === "microsoftpersonal")
|
|
30
|
+
return "microsoft";
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
/** Resolve CLI, environment, remembered, then first-login choice-page precedence. */
|
|
34
|
+
export function resolveLoginProviderSelection(explicitProvider, env, rememberedProvider) {
|
|
35
|
+
if (explicitProvider !== undefined) {
|
|
36
|
+
const provider = parseLoginProvider(explicitProvider);
|
|
37
|
+
return {
|
|
38
|
+
config: cognitoConfigForLoginProvider(explicitProvider),
|
|
39
|
+
...(provider && provider !== "picker" ? { providerToRemember: provider } : {}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (env.HQ_COGNITO_IDENTITY_PROVIDER !== undefined) {
|
|
43
|
+
const identityProvider = env.HQ_COGNITO_IDENTITY_PROVIDER.trim() || undefined;
|
|
44
|
+
return {
|
|
45
|
+
config: { ...DEFAULT_COGNITO, identityProvider },
|
|
46
|
+
...(rememberedProviderForCognitoProvider(identityProvider)
|
|
47
|
+
? { providerToRemember: rememberedProviderForCognitoProvider(identityProvider) }
|
|
48
|
+
: {}),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (rememberedProvider) {
|
|
52
|
+
return {
|
|
53
|
+
config: cognitoConfigForLoginProvider(rememberedProvider),
|
|
54
|
+
providerToRemember: rememberedProvider,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return { config: cognitoConfigForLoginProvider("picker") };
|
|
58
|
+
}
|
|
25
59
|
//# sourceMappingURL=login-provider.js.map
|
|
@@ -52,8 +52,8 @@ export declare function npmPrefixFromPackageDir(pkgDir: string): string | null;
|
|
|
52
52
|
* on PATH:
|
|
53
53
|
*
|
|
54
54
|
* $PNPM_HOME/hq <- shim on PATH
|
|
55
|
-
* $PNPM_HOME/global/5/node_modules/@indigoai-us/hq-cli
|
|
56
|
-
* $PNPM_HOME/global/
|
|
55
|
+
* pnpm through v10: $PNPM_HOME/global/5/node_modules/@indigoai-us/hq-cli
|
|
56
|
+
* pnpm v11+: $PNPM_HOME/global/v11/<hash>/node_modules/@indigoai-us/hq-cli
|
|
57
57
|
*
|
|
58
58
|
* Both the symlinked and the resolved (`.pnpm`) form are recognised, because
|
|
59
59
|
* whether `import.meta.url` reports the link or its target depends on how node
|
|
@@ -67,7 +67,7 @@ export declare function npmPrefixFromPackageDir(pkgDir: string): string | null;
|
|
|
67
67
|
* i.e. the gate would re-fire and re-install globally on every subsequent run.
|
|
68
68
|
* Those layouts fall through to the ordinary npm-prefix/manual handling.
|
|
69
69
|
*/
|
|
70
|
-
export declare function isPnpmManagedPackageDir(pkgDir: string): boolean;
|
|
70
|
+
export declare function isPnpmManagedPackageDir(pkgDir: string, acceptVersionPrefixedGlobalDir?: boolean): boolean;
|
|
71
71
|
/** Whether the running package lives inside Bun's global install tree. */
|
|
72
72
|
export declare function isBunManagedPackageDir(pkgDir: string): boolean;
|
|
73
73
|
/**
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* to silence both check + gate).
|
|
29
29
|
*/
|
|
30
30
|
import { spawnSync } from "node:child_process";
|
|
31
|
+
import { installPathCompatibilityEnabled } from "../lib/install-path-compatibility-flag.js";
|
|
31
32
|
import { buildSpawnPlan, quoteForWindowsShell } from "./windows-spawn.js";
|
|
32
33
|
import { accessSync, closeSync, constants as fsConstants, existsSync, mkdtempSync, openSync, readdirSync, readFileSync, rmSync, } from "node:fs";
|
|
33
34
|
import os from "node:os";
|
|
@@ -93,8 +94,8 @@ function pnpmHomeGlobalRoot() {
|
|
|
93
94
|
* on PATH:
|
|
94
95
|
*
|
|
95
96
|
* $PNPM_HOME/hq <- shim on PATH
|
|
96
|
-
* $PNPM_HOME/global/5/node_modules/@indigoai-us/hq-cli
|
|
97
|
-
* $PNPM_HOME/global/
|
|
97
|
+
* pnpm through v10: $PNPM_HOME/global/5/node_modules/@indigoai-us/hq-cli
|
|
98
|
+
* pnpm v11+: $PNPM_HOME/global/v11/<hash>/node_modules/@indigoai-us/hq-cli
|
|
98
99
|
*
|
|
99
100
|
* Both the symlinked and the resolved (`.pnpm`) form are recognised, because
|
|
100
101
|
* whether `import.meta.url` reports the link or its target depends on how node
|
|
@@ -108,18 +109,26 @@ function pnpmHomeGlobalRoot() {
|
|
|
108
109
|
* i.e. the gate would re-fire and re-install globally on every subsequent run.
|
|
109
110
|
* Those layouts fall through to the ordinary npm-prefix/manual handling.
|
|
110
111
|
*/
|
|
111
|
-
export function isPnpmManagedPackageDir(pkgDir) {
|
|
112
|
+
export function isPnpmManagedPackageDir(pkgDir, acceptVersionPrefixedGlobalDir = false) {
|
|
112
113
|
const normalized = pkgDir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
113
114
|
const segments = normalized.split("/").filter(Boolean);
|
|
114
|
-
// `global/<digits
|
|
115
|
-
//
|
|
115
|
+
// pnpm through v10 uses `global/<digits>`; pnpm v11+ uses
|
|
116
|
+
// `global/v<major>`. Keep the newer form behind the default-off rollout
|
|
117
|
+
// flag, and require a documented version segment so `global/next` or a
|
|
118
|
+
// `v11` directory elsewhere cannot be mistaken for a global install.
|
|
116
119
|
for (let i = 0; i < segments.length - 1; i += 1) {
|
|
117
|
-
if (segments[i]
|
|
120
|
+
if (segments[i] !== "global")
|
|
121
|
+
continue;
|
|
122
|
+
const globalDirVersion = segments[i + 1];
|
|
123
|
+
if (/^\d+$/.test(globalDirVersion))
|
|
118
124
|
return true;
|
|
125
|
+
if (acceptVersionPrefixedGlobalDir && /^v\d+$/.test(globalDirVersion)) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
119
128
|
}
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
129
|
+
// PNPM_HOME/global is pnpm's configured global root by construction, so
|
|
130
|
+
// keep this fallback in both flag states. The flag above gates only the new
|
|
131
|
+
// documented global/v<major> segment match outside that configured root.
|
|
123
132
|
const globalRoot = pnpmHomeGlobalRoot();
|
|
124
133
|
if (globalRoot) {
|
|
125
134
|
if (normalized === globalRoot || normalized.startsWith(`${globalRoot}/`)) {
|
|
@@ -246,7 +255,7 @@ export function resolveRunningInstall() {
|
|
|
246
255
|
const packageRoot = findRunningPackageRoot();
|
|
247
256
|
if (!packageRoot)
|
|
248
257
|
return { manager: "npm", prefix: null, packageRoot: null };
|
|
249
|
-
if (isPnpmManagedPackageDir(packageRoot)) {
|
|
258
|
+
if (isPnpmManagedPackageDir(packageRoot, installPathCompatibilityEnabled())) {
|
|
250
259
|
return { manager: "pnpm", prefix: null, packageRoot };
|
|
251
260
|
}
|
|
252
261
|
if (isBunManagedPackageDir(packageRoot)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@indigoai-us/hq-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.243.0",
|
|
4
4
|
"description": "HQ by Indigo management CLI — modules and cloud sync",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"test:db": "vitest run src/lib/db test/commands/db.test.ts test/commands/db-tenant-isolation.test.ts",
|
|
35
35
|
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
36
36
|
"coverage": "vitest run --coverage --coverage.reporter=text-summary --coverage.reporter=json-summary",
|
|
37
|
+
"check:lanes-module-cycles": "node scripts/check-lanes-module-cycles.mjs",
|
|
37
38
|
"vitest": "vitest",
|
|
38
39
|
"clean": "rm -rf dist",
|
|
39
40
|
"postinstall": "node scripts/mesh-daemon-if-stale.mjs || true",
|