@indigoai-us/hq-cli 5.198.1 → 5.200.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.
@@ -1,5 +1,7 @@
1
1
  import { loadCachedTokens } from "@indigoai-us/hq-cloud";
2
2
  import { createFlagClient, } from "@indigoai-us/hq-flags-client";
3
+ import { resolveProcessFlagCompanyContext } from "../flag-registry.js";
4
+ import { resolveFlagRegistryEndpoint } from "../flag-registry-endpoint.js";
3
5
  export const HOOK_TIMEOUT_DEBUG_CONTEXT_FLAG = "hook-timeout-debug-context";
4
6
  export const HOOK_TIMEOUT_DEBUG_CONTEXT_FLAG_FALLBACK = false;
5
7
  export const HOOK_TIMEOUT_DEBUG_CONTEXT_FLAG_TIMEOUT_MS = 150;
@@ -20,12 +22,11 @@ function combineSignals(first, second) {
20
22
  */
21
23
  export async function hookTimeoutDebugContextEnabled(dependencies = {}, callerSignal) {
22
24
  const env = dependencies.env ?? process.env;
23
- const endpoint = env.HQ_FLAGS_API_URL?.trim() || "";
24
- const companyUid = env.HQ_COMPANY_UID?.trim() || "";
25
- if (!endpoint || !/^cmp_[A-Za-z0-9]{3,128}$/.test(companyUid)) {
25
+ const endpoint = resolveFlagRegistryEndpoint(undefined, env);
26
+ const { companyUid, companySlug } = resolveProcessFlagCompanyContext(env);
27
+ if (!companyUid) {
26
28
  return HOOK_TIMEOUT_DEBUG_CONTEXT_FLAG_FALLBACK;
27
29
  }
28
- const companySlug = env.HQ_COMPANY_SLUG?.trim() || "";
29
30
  const deadline = AbortSignal.timeout(HOOK_TIMEOUT_DEBUG_CONTEXT_FLAG_TIMEOUT_MS);
30
31
  const signal = callerSignal ? AbortSignal.any([callerSignal, deadline]) : deadline;
31
32
  const doFetch = dependencies.fetch ?? globalThis.fetch;
@@ -1,5 +1,6 @@
1
1
  import { loadCachedTokens } from "@indigoai-us/hq-cloud";
2
2
  import { createFlagClient, } from "@indigoai-us/hq-flags-client";
3
+ import { resolveFlagRegistryEndpoint } from "../flag-registry-endpoint.js";
3
4
  export const CLIENT_HEALTH_REASON_CODES_FLAG = "client-health-sync-reason-codes";
4
5
  export const CLIENT_HEALTH_REASON_CODES_FLAG_FALLBACK = false;
5
6
  export const CLIENT_HEALTH_REASON_CODES_FLAG_TIMEOUT_MS = 150;
@@ -32,9 +33,9 @@ export async function clientHealthReasonCodesEnabled(dependencies = {}, callerSi
32
33
  /** Resolve the same default-off gate for one explicitly identified company. */
33
34
  export async function clientHealthReasonCodesEnabledForCompany(company, dependencies = {}, callerSignal) {
34
35
  const env = dependencies.env ?? process.env;
35
- const endpoint = env.HQ_FLAGS_API_URL?.trim() || "";
36
+ const endpoint = resolveFlagRegistryEndpoint(undefined, env);
36
37
  const companyUid = company.companyUid.trim();
37
- if (!endpoint || !/^cmp_[A-Za-z0-9]{3,128}$/.test(companyUid)) {
38
+ if (!/^cmp_[A-Za-z0-9]{3,128}$/.test(companyUid)) {
38
39
  return CLIENT_HEALTH_REASON_CODES_FLAG_FALLBACK;
39
40
  }
40
41
  const companySlug = company.companySlug?.trim() || "";
@@ -0,0 +1,5 @@
1
+ export declare const FLAG_REGISTRY_ENDPOINT_ENV = "HQ_FLAGS_API_URL";
2
+ export declare const FLAG_REGISTRY_DEFAULT_ENDPOINT = "https://hqapi.hq.computer";
3
+ /** Resolve one shared endpoint from an explicit option, environment, or service default. */
4
+ export declare function resolveFlagRegistryEndpoint(explicitEndpoint?: string, env?: NodeJS.ProcessEnv): string;
5
+ //# sourceMappingURL=flag-registry-endpoint.d.ts.map
@@ -0,0 +1,9 @@
1
+ export const FLAG_REGISTRY_ENDPOINT_ENV = "HQ_FLAGS_API_URL";
2
+ export const FLAG_REGISTRY_DEFAULT_ENDPOINT = "https://hqapi.hq.computer";
3
+ /** Resolve one shared endpoint from an explicit option, environment, or service default. */
4
+ export function resolveFlagRegistryEndpoint(explicitEndpoint, env = process.env) {
5
+ return explicitEndpoint?.trim()
6
+ || env[FLAG_REGISTRY_ENDPOINT_ENV]?.trim()
7
+ || FLAG_REGISTRY_DEFAULT_ENDPOINT;
8
+ }
9
+ //# sourceMappingURL=flag-registry-endpoint.js.map
@@ -7,8 +7,7 @@
7
7
  */
8
8
  import { type WorkerOptions } from "node:worker_threads";
9
9
  import { type FlagClient, type FlagLookupOptions, type FlagSnapshot } from "@indigoai-us/hq-flags-client";
10
- /** Dedicated endpoint configuration; this is intentionally not HQ_PRO_API_URL. */
11
- export declare const FLAG_REGISTRY_ENDPOINT_ENV = "HQ_FLAGS_API_URL";
10
+ export { FLAG_REGISTRY_ENDPOINT_ENV } from "./flag-registry-endpoint.js";
12
11
  /** The synchronous capability that command gate call sites need. */
13
12
  export type FlagReader = Pick<FlagClient, "isEnabled">;
14
13
  interface WorkerLike {
@@ -20,6 +19,12 @@ export interface FlagRegistryDependencies {
20
19
  endpoint?: string;
21
20
  createWorker?: (filename: URL, options: WorkerOptions) => WorkerLike;
22
21
  }
22
+ export interface ProcessFlagCompanyContext {
23
+ companyUid?: string;
24
+ companySlug?: string;
25
+ }
26
+ /** Resolve the same company identity used by the process-wide flag reader. */
27
+ export declare function resolveProcessFlagCompanyContext(env?: NodeJS.ProcessEnv): ProcessFlagCompanyContext;
23
28
  /**
24
29
  * Begin one registry refresh without awaiting it or retaining the CLI process.
25
30
  *
@@ -46,5 +51,4 @@ export declare function resolveProcessFlagGate(flagKey: string, lookup: FlagLook
46
51
  export declare function currentFlagSnapshot(): FlagSnapshot | null;
47
52
  /** Test-only reset for process-global readiness state. */
48
53
  export declare function _resetFlagRegistryForTests(): void;
49
- export {};
50
54
  //# sourceMappingURL=flag-registry.d.ts.map
@@ -12,8 +12,8 @@ import { readSessionState } from "./work-context/state.js";
12
12
  import { workContextRoot } from "./work-context/paths.js";
13
13
  import { readCompaniesManifestMap } from "./work-context/repo-remote.js";
14
14
  import { resolveDefaultHqRoot } from "../utils/cognito-session.js";
15
- /** Dedicated endpoint configuration; this is intentionally not HQ_PRO_API_URL. */
16
- export const FLAG_REGISTRY_ENDPOINT_ENV = "HQ_FLAGS_API_URL";
15
+ import { resolveFlagRegistryEndpoint, } from "./flag-registry-endpoint.js";
16
+ export { FLAG_REGISTRY_ENDPOINT_ENV } from "./flag-registry-endpoint.js";
17
17
  const COMPANY_UID_RE = /^cmp_[A-Za-z0-9]{3,128}$/;
18
18
  const COMPANY_SLUG_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
19
19
  let heldSnapshot = null;
@@ -76,6 +76,15 @@ function companyUidForContext(context, env = process.env) {
76
76
  const manifestUid = readCompaniesManifestMap(hqRoot)?.[context.companySlug]?.cloud_uid;
77
77
  return typeof manifestUid === "string" ? normalizedCompanyUid(manifestUid) : undefined;
78
78
  }
79
+ /** Resolve the same company identity used by the process-wide flag reader. */
80
+ export function resolveProcessFlagCompanyContext(env = process.env) {
81
+ const context = processCompanyContext(env);
82
+ const companyUid = companyUidForContext(context, env);
83
+ return {
84
+ ...(companyUid ? { companyUid } : {}),
85
+ ...(context.companySlug ? { companySlug: context.companySlug } : {}),
86
+ };
87
+ }
79
88
  function processSnapshotReader(companyIdentifiers) {
80
89
  return {
81
90
  isEnabled(flagKey, lookup = {}) {
@@ -101,11 +110,9 @@ function processSnapshotReader(companyIdentifiers) {
101
110
  export function kickFlagRegistryReadiness(dependencies = {}) {
102
111
  if (processReader)
103
112
  return processReader;
104
- const endpoint = (dependencies.endpoint ?? process.env[FLAG_REGISTRY_ENDPOINT_ENV] ?? "").trim();
105
- if (!endpoint)
106
- return null;
107
- const company = processCompanyContext();
108
- const companyUid = companyUidForContext(company);
113
+ const endpoint = resolveFlagRegistryEndpoint(dependencies.endpoint, process.env);
114
+ const company = resolveProcessFlagCompanyContext();
115
+ const companyUid = company.companyUid;
109
116
  const companyIdentifiers = [companyUid, company.companySlug].filter((value) => Boolean(value));
110
117
  processReader = processSnapshotReader(companyIdentifiers);
111
118
  try {
@@ -149,6 +156,11 @@ export function resolveFlagGate(reader, flagKey, lookup, legacyFallback) {
149
156
  }
150
157
  /** Resolve a process gate against the shared snapshot reader, when configured. */
151
158
  export function resolveProcessFlagGate(flagKey, lookup, legacyFallback) {
159
+ // The endpoint refresh is deliberately asynchronous. Until a validated
160
+ // snapshot arrives, preserve each caller's exact legacy fallback.
161
+ if (!processReader || !heldSnapshot) {
162
+ return localOverrideOrFallback(flagKey, legacyFallback);
163
+ }
152
164
  return resolveFlagGate(processReader, flagKey, lookup, legacyFallback);
153
165
  }
154
166
  /**
@@ -46,6 +46,7 @@ export declare function buildOutpostQuestionAnswerCommand(input: {
46
46
  choice?: string;
47
47
  freeform?: string;
48
48
  notes?: string;
49
+ company?: string;
49
50
  hqBinary?: string;
50
51
  }): string;
51
52
  export interface OutpostExecPort {
@@ -71,8 +72,16 @@ export declare function parseOutpostPickupPayload(raw: unknown): OutpostPickupPa
71
72
  export declare function buildOutpostPickupCommand(payloadUrl: string): string;
72
73
  /** Bounded, read-only capacity query executed by the target Outpost CLI. */
73
74
  export declare function buildOutpostCapacityCommand(): string;
74
- /** Read the lane records owned by an Outpost for exact per-account state counts. */
75
- export declare function buildOutpostLaneListCommand(): string;
75
+ /** Read every lane on an Outpost, optionally using its configured HQ binary. */
76
+ export declare function buildOutpostLaneListCommand(input?: {
77
+ hqBinary?: string;
78
+ }): string;
79
+ /** Read questions on one Outpost, scoped to a company when supplied. */
80
+ export declare function buildOutpostQuestionListCommand(input?: {
81
+ company?: string;
82
+ }): string;
83
+ /** Read one question's body and owner-computed audience from its Outpost. */
84
+ export declare function buildOutpostQuestionShowCommand(questionId: string): string;
76
85
  /** Commit a queued lane move on the host that currently owns its state. */
77
86
  export declare function buildOutpostMoveTargetCommand(input: {
78
87
  laneId: string;
@@ -22,6 +22,7 @@ export function buildOutpostQuestionAnswerCommand(input) {
22
22
  ["--choice", input.choice],
23
23
  ["--freeform", input.freeform],
24
24
  ["--notes", input.notes],
25
+ ["--company", input.company],
25
26
  ]) {
26
27
  if (value !== undefined)
27
28
  args.push(flag, value);
@@ -64,9 +65,32 @@ export function buildOutpostPickupCommand(payloadUrl) {
64
65
  export function buildOutpostCapacityCommand() {
65
66
  return withRemoteHqDir(joinCommandParts(["hq", "lanes", "capacity", "--json"]));
66
67
  }
67
- /** Read the lane records owned by an Outpost for exact per-account state counts. */
68
- export function buildOutpostLaneListCommand() {
69
- return withRemoteHqDir(joinCommandParts(["hq", "lanes", "list", "--json"]));
68
+ /** Read every lane on an Outpost, optionally using its configured HQ binary. */
69
+ export function buildOutpostLaneListCommand(input = {}) {
70
+ return withRemoteHqDir(joinCommandParts([
71
+ input.hqBinary?.trim() || "hq",
72
+ "lanes",
73
+ "list",
74
+ "--json",
75
+ ]));
76
+ }
77
+ /** Read questions on one Outpost, scoped to a company when supplied. */
78
+ export function buildOutpostQuestionListCommand(input = {}) {
79
+ const args = ["hq", "lanes", "questions", "list"];
80
+ const company = input.company?.trim();
81
+ if (company)
82
+ args.push("--company", company);
83
+ return withRemoteHqDir(joinCommandParts(args));
84
+ }
85
+ /** Read one question's body and owner-computed audience from its Outpost. */
86
+ export function buildOutpostQuestionShowCommand(questionId) {
87
+ return withRemoteHqDir(joinCommandParts([
88
+ "hq",
89
+ "lanes",
90
+ "questions",
91
+ "show",
92
+ questionId,
93
+ ]));
70
94
  }
71
95
  /** Commit a queued lane move on the host that currently owns its state. */
72
96
  export function buildOutpostMoveTargetCommand(input) {
@@ -0,0 +1,20 @@
1
+ export declare const OUTPOST_HIRE_SUMMARY_CACHE_TTL_MS = 60000;
2
+ export interface OutpostHireSummary {
3
+ hire_grant: number;
4
+ hire_alive_children: number;
5
+ hire: string;
6
+ hire_summary: string;
7
+ }
8
+ export interface OutpostHireSummaryCacheEntry extends OutpostHireSummary {
9
+ outpost_id: string;
10
+ lane_id: string;
11
+ cached_at: string;
12
+ }
13
+ export declare function outpostHireSummaryCachePath(hqRoot: string): string;
14
+ export declare function outpostHireSummaryCacheKey(outpostId: string, laneId: string): string;
15
+ export declare function readOutpostHireSummaryCache(hqRoot: string, nowMs: number): Map<string, OutpostHireSummaryCacheEntry>;
16
+ export declare function writeOutpostHireSummaryCache(hqRoot: string, updates: readonly (OutpostHireSummary & {
17
+ outpost_id: string;
18
+ lane_id: string;
19
+ })[], nowMs: number, existingEntries?: ReadonlyMap<string, OutpostHireSummaryCacheEntry>): void;
20
+ //# sourceMappingURL=outpost-hire-cache.d.ts.map
@@ -0,0 +1,146 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ export const OUTPOST_HIRE_SUMMARY_CACHE_TTL_MS = 60_000;
5
+ const CACHE_RELATIVE_PATH = path.join("workspace", "lanes-runs", ".cache", "outpost-hire-summaries.json");
6
+ export function outpostHireSummaryCachePath(hqRoot) {
7
+ return path.join(hqRoot, CACHE_RELATIVE_PATH);
8
+ }
9
+ export function outpostHireSummaryCacheKey(outpostId, laneId) {
10
+ return JSON.stringify([outpostId, laneId]);
11
+ }
12
+ export function readOutpostHireSummaryCache(hqRoot, nowMs) {
13
+ const filePath = outpostHireSummaryCachePath(hqRoot);
14
+ let contents;
15
+ try {
16
+ contents = fs.readFileSync(filePath, "utf8");
17
+ }
18
+ catch (err) {
19
+ if (isMissingFile(err))
20
+ return new Map();
21
+ console.warn(`Ignoring unreadable Outpost hire summary cache at ${filePath}: ${errorText(err)}`);
22
+ return new Map();
23
+ }
24
+ let parsed;
25
+ try {
26
+ parsed = JSON.parse(contents);
27
+ }
28
+ catch (err) {
29
+ console.warn(`Ignoring malformed Outpost hire summary cache at ${filePath}: ${errorText(err)}`);
30
+ return new Map();
31
+ }
32
+ if (!Array.isArray(parsed)) {
33
+ console.warn(`Ignoring malformed Outpost hire summary cache at ${filePath}: expected an array`);
34
+ return new Map();
35
+ }
36
+ const entries = new Map();
37
+ const duplicates = new Set();
38
+ for (const value of parsed) {
39
+ const entry = parseCacheEntry(value, nowMs);
40
+ if (!entry)
41
+ continue;
42
+ const key = outpostHireSummaryCacheKey(entry.outpost_id, entry.lane_id);
43
+ if (entries.has(key)) {
44
+ entries.delete(key);
45
+ duplicates.add(key);
46
+ continue;
47
+ }
48
+ if (!duplicates.has(key))
49
+ entries.set(key, entry);
50
+ }
51
+ return entries;
52
+ }
53
+ export function writeOutpostHireSummaryCache(hqRoot, updates, nowMs, existingEntries) {
54
+ if (updates.length === 0)
55
+ return;
56
+ const filePath = outpostHireSummaryCachePath(hqRoot);
57
+ const cached = new Map(existingEntries ?? readOutpostHireSummaryCache(hqRoot, nowMs));
58
+ for (const update of updates) {
59
+ const entry = {
60
+ outpost_id: update.outpost_id,
61
+ lane_id: update.lane_id,
62
+ hire_grant: update.hire_grant,
63
+ hire_alive_children: update.hire_alive_children,
64
+ hire: update.hire,
65
+ hire_summary: update.hire_summary,
66
+ cached_at: new Date(nowMs).toISOString(),
67
+ };
68
+ if (!parseCacheEntry(entry, nowMs))
69
+ continue;
70
+ cached.set(outpostHireSummaryCacheKey(entry.outpost_id, entry.lane_id), entry);
71
+ }
72
+ const directory = path.dirname(filePath);
73
+ let temporaryPath;
74
+ try {
75
+ fs.mkdirSync(directory, { recursive: true });
76
+ temporaryPath = path.join(directory, `.outpost-hire-summaries.${process.pid}.${randomBytes(6).toString("hex")}.tmp`);
77
+ fs.writeFileSync(temporaryPath, `${JSON.stringify([...cached.values()])}\n`, {
78
+ encoding: "utf8",
79
+ mode: 0o600,
80
+ });
81
+ fs.renameSync(temporaryPath, filePath);
82
+ }
83
+ catch (err) {
84
+ if (temporaryPath) {
85
+ try {
86
+ fs.unlinkSync(temporaryPath);
87
+ }
88
+ catch (cleanupErr) {
89
+ if (!isMissingFile(cleanupErr)) {
90
+ console.warn(`Could not remove temporary Outpost hire summary cache ${temporaryPath}: ${errorText(cleanupErr)}`);
91
+ }
92
+ }
93
+ }
94
+ console.warn(`Could not write Outpost hire summary cache at ${filePath}: ${errorText(err)}`);
95
+ }
96
+ }
97
+ function parseCacheEntry(value, nowMs) {
98
+ if (value === null || typeof value !== "object" || Array.isArray(value))
99
+ return undefined;
100
+ const entry = value;
101
+ const allowedKeys = new Set([
102
+ "outpost_id",
103
+ "lane_id",
104
+ "hire_grant",
105
+ "hire_alive_children",
106
+ "hire",
107
+ "hire_summary",
108
+ "cached_at",
109
+ ]);
110
+ if (Object.keys(entry).length !== allowedKeys.size || Object.keys(entry).some((key) => !allowedKeys.has(key))) {
111
+ return undefined;
112
+ }
113
+ if (typeof entry.outpost_id !== "string" || !entry.outpost_id.trim() ||
114
+ typeof entry.lane_id !== "string" || !entry.lane_id.trim() ||
115
+ !Number.isSafeInteger(entry.hire_grant) || entry.hire_grant < 0 ||
116
+ !Number.isSafeInteger(entry.hire_alive_children) || entry.hire_alive_children < 0 ||
117
+ typeof entry.cached_at !== "string") {
118
+ return undefined;
119
+ }
120
+ const cachedAt = Date.parse(entry.cached_at);
121
+ if (!Number.isFinite(cachedAt) ||
122
+ cachedAt > nowMs ||
123
+ nowMs - cachedAt > OUTPOST_HIRE_SUMMARY_CACHE_TTL_MS) {
124
+ return undefined;
125
+ }
126
+ const hire = `${entry.hire_alive_children}/${entry.hire_grant}`;
127
+ const hireSummary = `hire ${hire}`;
128
+ if (entry.hire !== hire || entry.hire_summary !== hireSummary)
129
+ return undefined;
130
+ return {
131
+ outpost_id: entry.outpost_id,
132
+ lane_id: entry.lane_id,
133
+ hire_grant: entry.hire_grant,
134
+ hire_alive_children: entry.hire_alive_children,
135
+ hire,
136
+ hire_summary: hireSummary,
137
+ cached_at: entry.cached_at,
138
+ };
139
+ }
140
+ function isMissingFile(err) {
141
+ return err !== null && typeof err === "object" && "code" in err && err.code === "ENOENT";
142
+ }
143
+ function errorText(err) {
144
+ return err instanceof Error ? err.message : String(err);
145
+ }
146
+ //# sourceMappingURL=outpost-hire-cache.js.map
@@ -3,6 +3,7 @@ import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import { resolveHqBinary, resolveNodeBinary } from "../mesh/live/daemon/install.js";
5
5
  import { SYSTEMD_PATH_ENV } from "../service-manager/systemd.js";
6
+ import { resolveFlagRegistryEndpoint } from "../flag-registry-endpoint.js";
6
7
  export const OUTPOST_PRUNER_PRESSURE_SERVICE = "hq-outpost-worktree-pruner-pressure.service";
7
8
  export const OUTPOST_PRUNER_PRESSURE_TIMER = "hq-outpost-worktree-pruner-pressure.timer";
8
9
  export const OUTPOST_PRUNER_DAILY_SERVICE = "hq-outpost-worktree-pruner-daily.service";
@@ -160,10 +161,10 @@ export function installOutpostPrunerTimers(options) {
160
161
  const nodeBinary = options.nodeBinary ?? resolveNodeBinary();
161
162
  const hqBinary = options.hqBinary ?? resolveHqBinary();
162
163
  const home = options.home ?? os.homedir();
163
- const flagApiUrl = (options.flagApiUrl ?? process.env.HQ_FLAGS_API_URL ?? "").trim();
164
+ const flagApiUrl = resolveFlagRegistryEndpoint(options.flagApiUrl);
164
165
  const companyUid = (options.companyUid ?? process.env.HQ_COMPANY_UID ?? "").trim();
165
- if (!flagApiUrl || !companyUid) {
166
- throw new TypeError("install requires HQ_FLAGS_API_URL and HQ_COMPANY_UID for the timer services");
166
+ if (!companyUid) {
167
+ throw new TypeError("install requires HQ_COMPANY_UID for the timer services");
167
168
  }
168
169
  const flagEnvironment = [
169
170
  systemdEnvironment("HQ_FLAGS_API_URL", flagApiUrl),
@@ -1,5 +1,6 @@
1
1
  import { loadCachedTokens } from "@indigoai-us/hq-cloud";
2
2
  import { createFlagClient } from "@indigoai-us/hq-flags-client";
3
+ import { resolveFlagRegistryEndpoint } from "../flag-registry-endpoint.js";
3
4
  export const OUTPOST_WORKTREE_PRUNER_FLAG = "outpost.worktree-pruner";
4
5
  export const OUTPOST_WORKTREE_PRUNER_FLAG_FALLBACK = false;
5
6
  export const OUTPOST_WORKTREE_PRUNER_FLAG_TIMEOUT_MS = 5_000;
@@ -21,14 +22,13 @@ function reportFlagFailure(error) {
21
22
  }
22
23
  export async function outpostWorktreePrunerEnabled(dependencies = {}) {
23
24
  const env = dependencies.env ?? process.env;
24
- const endpoint = env.HQ_FLAGS_API_URL?.trim() || "";
25
+ const endpoint = resolveFlagRegistryEndpoint(undefined, env);
25
26
  const companyUid = env.HQ_COMPANY_UID?.trim() || "";
26
- if (!endpoint || !companyUid) {
27
+ if (!companyUid) {
27
28
  process.stderr.write(`${JSON.stringify({
28
29
  event: "outpost.worktree_pruner.flag_config_missing",
29
30
  flag: OUTPOST_WORKTREE_PRUNER_FLAG,
30
31
  missing: [
31
- ...(!endpoint ? ["HQ_FLAGS_API_URL"] : []),
32
32
  ...(!companyUid ? ["HQ_COMPANY_UID"] : []),
33
33
  ],
34
34
  fallback: OUTPOST_WORKTREE_PRUNER_FLAG_FALLBACK,
@@ -1,6 +1,7 @@
1
1
  import { createFlagClient, readLocalOverride, } from "@indigoai-us/hq-flags-client";
2
2
  import { ensureCognitoIdToken } from "../../utils/cognito-session.js";
3
- export const LANES_FLAG_API_DEFAULT_ENDPOINT = "https://hqapi.hq.computer";
3
+ import { FLAG_REGISTRY_DEFAULT_ENDPOINT, resolveFlagRegistryEndpoint, } from "../flag-registry-endpoint.js";
4
+ export const LANES_FLAG_API_DEFAULT_ENDPOINT = FLAG_REGISTRY_DEFAULT_ENDPOINT;
4
5
  export const LANES_FLAG_READ_TIMEOUT_MS = 5_000;
5
6
  const registryReads = new Map();
6
7
  class MissingFlagTokenError extends Error {
@@ -45,7 +46,7 @@ export async function readLanesRegistryFlag(flagKey, options = {}) {
45
46
  const localOverride = readLocalOverride(flagKey, env);
46
47
  if (localOverride !== null)
47
48
  return localOverride;
48
- const endpoint = env.HQ_FLAGS_API_URL?.trim() || LANES_FLAG_API_DEFAULT_ENDPOINT;
49
+ const endpoint = resolveFlagRegistryEndpoint(undefined, env);
49
50
  const key = cacheKey(flagKey, options.company, endpoint);
50
51
  const cached = registryReads.get(key);
51
52
  if (cached)
@@ -1,4 +1,5 @@
1
1
  import { createFlagClient, readLocalOverride } from "@indigoai-us/hq-flags-client";
2
+ import { resolveFlagRegistryEndpoint } from "./lib/flag-registry-endpoint.js";
2
3
  export const NODE_NETWORK_FORCE_IPV4_FLAG = "cli.network.force-ipv4";
3
4
  export const NODE_NETWORK_FORCE_IPV4_FLAG_FALLBACK = false;
4
5
  // Node waits up to 1s before trying the next address family. Keep this request
@@ -29,10 +30,9 @@ export async function nodeNetworkForceIpv4Enabled(dependencies = {}) {
29
30
  const localOverride = readLocalOverride(NODE_NETWORK_FORCE_IPV4_FLAG, env);
30
31
  if (localOverride !== null)
31
32
  return localOverride;
32
- const endpoint = env.HQ_FLAGS_API_URL?.trim() || "";
33
+ const endpoint = resolveFlagRegistryEndpoint(undefined, env);
33
34
  const companyUid = env.HQ_COMPANY_UID?.trim() || "";
34
- if (!endpoint ||
35
- !/^cmp_[A-Za-z0-9]{3,128}$/.test(companyUid)) {
35
+ if (!/^cmp_[A-Za-z0-9]{3,128}$/.test(companyUid)) {
36
36
  return NODE_NETWORK_FORCE_IPV4_FLAG_FALLBACK;
37
37
  }
38
38
  const companySlug = env.HQ_COMPANY_SLUG?.trim() || "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.198.1",
3
+ "version": "5.200.0",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {