@indigoai-us/hq-cli 5.234.2 → 5.236.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 +56 -0
- package/assets/bot-workers/setup/skills/agents-and-team.md +154 -0
- package/assets/bot-workers/setup/skills/first-company.md +4 -1
- package/assets/bot-workers/setup/skills/pricing.md +92 -0
- package/assets/bot-workers/setup/skills/standing-help.md +7 -0
- package/assets/bot-workers/setup/worker.yaml +244 -63
- package/dist/command-catalog.generated.d.ts +3 -0
- package/dist/command-catalog.generated.js +4 -0
- package/dist/commands/bot.d.ts +2 -0
- package/dist/commands/bot.js +14 -2
- package/dist/commands/lanes-checks.d.ts +0 -3
- package/dist/commands/lanes-checks.js +22 -40
- package/dist/commands/lanes-import.d.ts +0 -2
- package/dist/commands/lanes-import.js +3 -7
- package/dist/commands/lanes-report.js +5 -12
- package/dist/commands/lanes-tracker.d.ts +0 -3
- package/dist/commands/lanes-tracker.js +41 -17
- package/dist/commands/lanes.d.ts +0 -5
- package/dist/commands/lanes.js +3 -329
- package/dist/lib/bot/config.d.ts +12 -0
- package/dist/lib/bot/config.js +25 -0
- package/dist/lib/bot/local-config.d.ts +2 -1
- package/dist/lib/bot/local-config.js +4 -0
- package/dist/lib/bot/progress.d.ts +25 -0
- package/dist/lib/bot/progress.js +39 -1
- package/dist/lib/bot/prompt.d.ts +2 -0
- package/dist/lib/bot/prompt.js +12 -2
- package/dist/lib/bot/run.js +10 -2
- package/dist/lib/lanes/reconcile.d.ts +0 -2
- package/dist/lib/lanes/reconcile.js +0 -16
- package/dist/lib/lanes/store.js +1 -9
- package/dist/lib/lanes/tracker-store.d.ts +2 -6
- package/dist/lib/lanes/tracker-store.js +1 -1
- package/dist/lib/lanes/types.d.ts +0 -4
- package/dist/node-network-compat.d.ts +1 -10
- package/dist/node-network-compat.js +3 -70
- package/package.json +1 -1
- package/dist/lib/lanes/tracker-mesh-questions.d.ts +0 -54
- package/dist/lib/lanes/tracker-mesh-questions.js +0 -138
- package/dist/lib/lanes/tracker-mesh.d.ts +0 -19
- package/dist/lib/lanes/tracker-mesh.js +0 -251
- package/dist/node-network-compat-flag.d.ts +0 -19
- package/dist/node-network-compat-flag.js +0 -87
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { meshJson, requireToken } from "../mesh/client.js";
|
|
3
|
-
import { LanesError } from "./errors.js";
|
|
4
|
-
const ROOT = "/v1/work-mesh/tracker/records";
|
|
5
|
-
const MAX_RECORD_PAGES = 20;
|
|
6
|
-
const RECORD_FIELDS = {
|
|
7
|
-
blocker: ["story_id", "what", "waiting_on", "next_step", "opened_at", "resolved_at", "resolution", "import_key", "source_path", "source_item_id"],
|
|
8
|
-
kpi: ["name", "definition", "unit", "target", "direction", "status", "approved_by", "approved_at", "values", "import_key", "source_path", "source_item_id"],
|
|
9
|
-
okr: ["objective", "period", "key_results", "status", "approved_by", "approved_at", "import_key", "source_path", "source_item_id"],
|
|
10
|
-
directive: ["text", "date", "source_url", "teams", "policy_path", "acknowledgements", "import_key", "source_path", "source_item_id"],
|
|
11
|
-
};
|
|
12
|
-
function fail(error, message, details = {}) {
|
|
13
|
-
throw new LanesError(error, message, details);
|
|
14
|
-
}
|
|
15
|
-
export function mapWorkMeshTrackerError(err) {
|
|
16
|
-
if (err instanceof LanesError)
|
|
17
|
-
return err;
|
|
18
|
-
const value = err;
|
|
19
|
-
const status = typeof value?.status === "number" ? value.status : Number(/^\s*(\d{3})\b/.exec(err instanceof Error ? err.message : "")?.[1]);
|
|
20
|
-
const code = typeof value?.code === "string" ? value.code : "";
|
|
21
|
-
if (status === 404 && code === "tracker_records_disabled") {
|
|
22
|
-
return new LanesError("tracker_records_disabled", "Work Mesh tracker records are disabled for this company", { status, code });
|
|
23
|
-
}
|
|
24
|
-
if (status === 404)
|
|
25
|
-
return new LanesError("tracker_record_not_found", "Work Mesh tracker record was not found", { status, code });
|
|
26
|
-
if (status === 409 && (code === "stale_version" || /stale.version/i.test(err instanceof Error ? err.message : ""))) {
|
|
27
|
-
return new LanesError("tracker_version_conflict", "Work Mesh tracker record changed before this update", { status, code });
|
|
28
|
-
}
|
|
29
|
-
if (status === 409)
|
|
30
|
-
return new LanesError("tracker_mesh_conflict", "Work Mesh rejected the tracker update because the record state changed", { status, code });
|
|
31
|
-
if (status === 401 || status === 403)
|
|
32
|
-
return new LanesError("tracker_mesh_auth_failed", "Work Mesh tracker access was denied for this company", { status, code });
|
|
33
|
-
if (status === 400 || status === 422)
|
|
34
|
-
return new LanesError("tracker_mesh_validation_failed", "Work Mesh rejected the tracker record data", { status, code });
|
|
35
|
-
return new LanesError("tracker_mesh_unavailable", `Work Mesh tracker request failed: ${err instanceof Error ? err.message : String(err)}`, { status: Number.isFinite(status) ? status : undefined });
|
|
36
|
-
}
|
|
37
|
-
function toMesh(value) {
|
|
38
|
-
if (Array.isArray(value))
|
|
39
|
-
return value.map(toMesh);
|
|
40
|
-
if (!value || typeof value !== "object")
|
|
41
|
-
return value;
|
|
42
|
-
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
43
|
-
key.replace(/_([a-z])/g, (_match, letter) => letter.toUpperCase()),
|
|
44
|
-
toMesh(item),
|
|
45
|
-
]));
|
|
46
|
-
}
|
|
47
|
-
function fromMesh(value) {
|
|
48
|
-
if (Array.isArray(value))
|
|
49
|
-
return value.map(fromMesh);
|
|
50
|
-
if (!value || typeof value !== "object")
|
|
51
|
-
return value;
|
|
52
|
-
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
53
|
-
key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`),
|
|
54
|
-
fromMesh(item),
|
|
55
|
-
]));
|
|
56
|
-
}
|
|
57
|
-
function recordFromMeta(raw, company, projectId, expectedKind) {
|
|
58
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
59
|
-
return fail("tracker_mesh_response_invalid", "Work Mesh returned a malformed tracker record", {
|
|
60
|
-
source: "Work Mesh", kind: expectedKind, company: company.slug, project_id: projectId,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
const meta = raw;
|
|
64
|
-
if (meta.companyUid !== company.uid || meta.projectId !== projectId || meta.kind !== expectedKind || typeof meta.threadId !== "string" || !meta.threadId) {
|
|
65
|
-
return fail("tracker_mesh_response_invalid", "Work Mesh tracker record identity did not match the requested company, project, and kind", {
|
|
66
|
-
source: "Work Mesh",
|
|
67
|
-
...(typeof meta.threadId === "string" && meta.threadId ? { record_id: meta.threadId } : {}),
|
|
68
|
-
kind: expectedKind,
|
|
69
|
-
company: company.slug,
|
|
70
|
-
project_id: projectId,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
const fields = fromMesh(meta);
|
|
74
|
-
const createdAt = typeof meta.createdAt === "string" ? meta.createdAt : "";
|
|
75
|
-
const updatedAt = typeof meta.lastActivityAt === "string" ? meta.lastActivityAt : createdAt;
|
|
76
|
-
const version = meta.version;
|
|
77
|
-
if (!Number.isSafeInteger(version) || version < 1 || !Number.isFinite(Date.parse(createdAt)) || !Number.isFinite(Date.parse(updatedAt))) {
|
|
78
|
-
return fail("tracker_mesh_response_invalid", "Work Mesh tracker record has invalid version or timestamps", {
|
|
79
|
-
source: "Work Mesh", record_id: meta.threadId, kind: expectedKind, company: company.slug, project_id: projectId,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
const record = {
|
|
83
|
-
schema_version: "hq-lanes-tracker-record.v1",
|
|
84
|
-
kind: expectedKind,
|
|
85
|
-
id: meta.threadId,
|
|
86
|
-
company: company.slug,
|
|
87
|
-
project_id: projectId,
|
|
88
|
-
version: version,
|
|
89
|
-
created_at: createdAt,
|
|
90
|
-
updated_at: updatedAt,
|
|
91
|
-
};
|
|
92
|
-
for (const field of RECORD_FIELDS[expectedKind]) {
|
|
93
|
-
if (Object.hasOwn(fields, field))
|
|
94
|
-
record[field] = fields[field];
|
|
95
|
-
}
|
|
96
|
-
// hq-pro stores KPI and OKR status in the shared threadStatus metadata field.
|
|
97
|
-
if ((expectedKind === "kpi" || expectedKind === "okr") && !Object.hasOwn(record, "status") && Object.hasOwn(fields, "thread_status")) {
|
|
98
|
-
record.status = fields.thread_status;
|
|
99
|
-
}
|
|
100
|
-
if (expectedKind === "blocker" && !Object.hasOwn(record, "opened_at"))
|
|
101
|
-
record.opened_at = createdAt;
|
|
102
|
-
return record;
|
|
103
|
-
}
|
|
104
|
-
function identityMatches(record, kind, id, company, projectId) {
|
|
105
|
-
return record.kind === kind && record.id === id && record.company === company.slug && record.project_id === projectId;
|
|
106
|
-
}
|
|
107
|
-
function equalValue(left, right) {
|
|
108
|
-
return JSON.stringify(left) === JSON.stringify(right);
|
|
109
|
-
}
|
|
110
|
-
export function createWorkMeshTrackerStore(options) {
|
|
111
|
-
const { company, projectId } = options;
|
|
112
|
-
if (!company.slug.trim() || !company.uid.trim())
|
|
113
|
-
fail("tracker_company_unresolved", "A resolved company slug and uid are required for Work Mesh tracker storage", {});
|
|
114
|
-
if (!projectId.trim())
|
|
115
|
-
fail("tracker_project_invalid", "A project id is required for Work Mesh tracker storage", {});
|
|
116
|
-
const now = options.now ?? (() => new Date());
|
|
117
|
-
const request = options.request ?? (async (endpoint, init) => {
|
|
118
|
-
const token = await requireToken({ interactive: false });
|
|
119
|
-
return meshJson(token, endpoint, init);
|
|
120
|
-
});
|
|
121
|
-
const requestJson = async (endpoint, init) => {
|
|
122
|
-
try {
|
|
123
|
-
const result = await request(endpoint, init);
|
|
124
|
-
if (!result || typeof result !== "object" || Array.isArray(result)) {
|
|
125
|
-
return fail("tracker_mesh_response_invalid", "Work Mesh returned an invalid tracker response", { endpoint });
|
|
126
|
-
}
|
|
127
|
-
return result;
|
|
128
|
-
}
|
|
129
|
-
catch (err) {
|
|
130
|
-
throw mapWorkMeshTrackerError(err);
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
const list = async (kind) => {
|
|
134
|
-
const records = [];
|
|
135
|
-
let cursor;
|
|
136
|
-
for (let page = 0; page < MAX_RECORD_PAGES; page += 1) {
|
|
137
|
-
const query = { companyUid: company.uid, projectId, kind, limit: "100", ...(cursor ? { cursor } : {}) };
|
|
138
|
-
const result = await requestJson(ROOT, { method: "GET", query });
|
|
139
|
-
if (!Array.isArray(result.threads))
|
|
140
|
-
return fail("tracker_mesh_response_invalid", "Work Mesh tracker list did not include a threads array", { kind, project_id: projectId });
|
|
141
|
-
for (const row of result.threads)
|
|
142
|
-
records.push(recordFromMeta(row, company, projectId, kind));
|
|
143
|
-
if (result.nextCursor === undefined)
|
|
144
|
-
return records.sort((a, b) => b.updated_at.localeCompare(a.updated_at) || a.id.localeCompare(b.id));
|
|
145
|
-
if (typeof result.nextCursor !== "string" || !result.nextCursor || result.nextCursor === cursor) {
|
|
146
|
-
return fail("tracker_mesh_response_invalid", "Work Mesh tracker list returned an invalid pagination cursor", { kind, project_id: projectId });
|
|
147
|
-
}
|
|
148
|
-
cursor = result.nextCursor;
|
|
149
|
-
}
|
|
150
|
-
return fail("tracker_mesh_response_incomplete", "Work Mesh tracker list exceeded its bounded page limit", { kind, project_id: projectId, max_pages: MAX_RECORD_PAGES });
|
|
151
|
-
};
|
|
152
|
-
const read = async (kind, id) => {
|
|
153
|
-
const record = (await list(kind)).find((candidate) => candidate.id === id);
|
|
154
|
-
if (record && !identityMatches(record, kind, id, company, projectId)) {
|
|
155
|
-
return fail("tracker_company_mismatch", "Work Mesh tracker record does not belong to the selected company and project", { kind, id, project_id: projectId });
|
|
156
|
-
}
|
|
157
|
-
return record ?? null;
|
|
158
|
-
};
|
|
159
|
-
return {
|
|
160
|
-
read,
|
|
161
|
-
list,
|
|
162
|
-
async create(input) {
|
|
163
|
-
if (input.company !== company.slug || input.project_id !== projectId) {
|
|
164
|
-
return fail("tracker_company_mismatch", "Tracker record company and project must match the resolved Work Mesh scope", { company: input.company, project_id: input.project_id });
|
|
165
|
-
}
|
|
166
|
-
const clientRecordId = input.id ?? (options.clientRecordId ?? randomUUID)();
|
|
167
|
-
const { id: _id, company: _company, project_id: _projectId, kind, ...fields } = input;
|
|
168
|
-
const result = await requestJson(ROOT, {
|
|
169
|
-
method: "POST",
|
|
170
|
-
body: { companyUid: company.uid, kind, projectId, clientRecordId, ...toMesh(fields) },
|
|
171
|
-
});
|
|
172
|
-
if (typeof result.threadId !== "string" || !result.threadId || !Number.isSafeInteger(result.version) || result.version < 1 || typeof result.createdAt !== "string" || !Number.isFinite(Date.parse(result.createdAt))) {
|
|
173
|
-
return fail("tracker_mesh_response_invalid", "Work Mesh did not return valid tracker creation metadata", { kind, project_id: projectId });
|
|
174
|
-
}
|
|
175
|
-
const createdAt = result.createdAt;
|
|
176
|
-
return {
|
|
177
|
-
...input,
|
|
178
|
-
schema_version: "hq-lanes-tracker-record.v1",
|
|
179
|
-
kind,
|
|
180
|
-
id: result.threadId,
|
|
181
|
-
company: company.slug,
|
|
182
|
-
project_id: projectId,
|
|
183
|
-
version: result.version,
|
|
184
|
-
created_at: createdAt,
|
|
185
|
-
updated_at: typeof result.lastActivityAt === "string" ? result.lastActivityAt : createdAt,
|
|
186
|
-
};
|
|
187
|
-
},
|
|
188
|
-
async update(kind, id, expectedVersion, change, metadata) {
|
|
189
|
-
const current = await read(kind, id);
|
|
190
|
-
if (!current)
|
|
191
|
-
return fail("tracker_record_not_found", `Tracker ${kind} record ${id} was not found`, { kind, id, project_id: projectId });
|
|
192
|
-
if (current.version !== expectedVersion) {
|
|
193
|
-
return fail("tracker_version_conflict", `Tracker record version conflict: expected ${expectedVersion}, found ${current.version}`, { kind, id, expected: expectedVersion, actual: current.version });
|
|
194
|
-
}
|
|
195
|
-
const changed = change(current);
|
|
196
|
-
if (changed.id !== current.id || changed.kind !== current.kind || changed.company !== current.company || changed.project_id !== current.project_id || changed.created_at !== current.created_at) {
|
|
197
|
-
return fail("tracker_update_invalid", "Tracker update cannot change record identity", { kind, id });
|
|
198
|
-
}
|
|
199
|
-
const patch = Object.fromEntries(Object.entries(changed).filter(([key, value]) => !["schema_version", "kind", "id", "company", "project_id", "version", "created_at", "updated_at"].includes(key) && !equalValue(current[key], value)));
|
|
200
|
-
if (Object.keys(patch).length === 0)
|
|
201
|
-
return current;
|
|
202
|
-
const currentValues = Array.isArray(current.values) ? current.values : [];
|
|
203
|
-
const changedValues = Array.isArray(changed.values) ? changed.values : [];
|
|
204
|
-
const isKpiAppend = kind === "kpi" && changedValues.length === currentValues.length + 1 && currentValues.every((value, index) => equalValue(value, changedValues[index]));
|
|
205
|
-
const currentAcks = Array.isArray(current.acknowledgements) ? current.acknowledgements : [];
|
|
206
|
-
const changedAcks = Array.isArray(changed.acknowledgements) ? changed.acknowledgements : [];
|
|
207
|
-
const isAckAppend = kind === "directive" && changedAcks.length === currentAcks.length + 1 && currentAcks.every((value, index) => equalValue(value, changedAcks[index]));
|
|
208
|
-
let version;
|
|
209
|
-
let updatedAt = now().toISOString();
|
|
210
|
-
if (isKpiAppend) {
|
|
211
|
-
const value = changedValues.at(-1);
|
|
212
|
-
const response = await requestJson(`${ROOT}/${encodeURIComponent(id)}/events`, {
|
|
213
|
-
method: "POST",
|
|
214
|
-
body: { companyUid: company.uid, eventKind: "kpi_value", payload: toMesh(value) },
|
|
215
|
-
});
|
|
216
|
-
version = Number(response.version ?? current.version + 1);
|
|
217
|
-
if (typeof response.createdAt === "string")
|
|
218
|
-
updatedAt = response.createdAt;
|
|
219
|
-
}
|
|
220
|
-
else if (isAckAppend) {
|
|
221
|
-
const ack = changedAcks.at(-1);
|
|
222
|
-
const response = await requestJson(`${ROOT}/${encodeURIComponent(id)}/events`, {
|
|
223
|
-
method: "POST",
|
|
224
|
-
body: { companyUid: company.uid, eventKind: "ack", payload: toMesh(ack) },
|
|
225
|
-
});
|
|
226
|
-
version = Number(response.version ?? current.version + 1);
|
|
227
|
-
if (typeof response.createdAt === "string")
|
|
228
|
-
updatedAt = response.createdAt;
|
|
229
|
-
}
|
|
230
|
-
else {
|
|
231
|
-
const action = kind === "blocker" && current.resolved_at == null && changed.resolved_at != null
|
|
232
|
-
? "resolve"
|
|
233
|
-
: ((kind === "kpi" || kind === "okr") && changed.status === "approved" && current.status !== "approved")
|
|
234
|
-
? "approve"
|
|
235
|
-
: "update";
|
|
236
|
-
const result = await requestJson(`${ROOT}/${encodeURIComponent(id)}`, {
|
|
237
|
-
method: "PATCH",
|
|
238
|
-
body: { companyUid: company.uid, action, expectedVersion, ...(action === "approve" && metadata?.approvalQuestionId ? { approvalQuestionId: metadata.approvalQuestionId } : {}), ...toMesh(patch) },
|
|
239
|
-
});
|
|
240
|
-
version = Number(result.version);
|
|
241
|
-
if (typeof result.lastActivityAt === "string")
|
|
242
|
-
updatedAt = result.lastActivityAt;
|
|
243
|
-
}
|
|
244
|
-
if (!Number.isSafeInteger(version) || version <= current.version) {
|
|
245
|
-
return fail("tracker_mesh_response_invalid", "Work Mesh tracker update returned an invalid version", { kind, id });
|
|
246
|
-
}
|
|
247
|
-
return { ...changed, schema_version: "hq-lanes-tracker-record.v1", version, created_at: current.created_at, updated_at: updatedAt };
|
|
248
|
-
},
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
//# sourceMappingURL=tracker-mesh.js.map
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { FlagClient, FlagClientOptions } from "@indigoai-us/hq-flags-client";
|
|
2
|
-
export declare const NODE_NETWORK_FORCE_IPV4_FLAG = "cli.network.force-ipv4";
|
|
3
|
-
export declare const NODE_NETWORK_FORCE_IPV4_FLAG_FALLBACK = false;
|
|
4
|
-
export declare const NODE_NETWORK_FORCE_IPV4_FLAG_TIMEOUT_MS = 1500;
|
|
5
|
-
type NetworkFlagClient = Pick<FlagClient, "ready" | "isEnabled" | "close">;
|
|
6
|
-
export interface NodeNetworkForceIpv4Dependencies {
|
|
7
|
-
env?: NodeJS.ProcessEnv;
|
|
8
|
-
createClient?: (options: FlagClientOptions) => NetworkFlagClient;
|
|
9
|
-
getToken?: () => string | Promise<string>;
|
|
10
|
-
reportFailure?: (error: unknown) => void;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Read the opt-in hq-flags gate before CLI dispatch. Local hq-flags overrides
|
|
14
|
-
* work without a token or endpoint; service decisions require a company
|
|
15
|
-
* identity and use a short request deadline so startup remains bounded.
|
|
16
|
-
*/
|
|
17
|
-
export declare function nodeNetworkForceIpv4Enabled(dependencies?: NodeNetworkForceIpv4Dependencies): Promise<boolean>;
|
|
18
|
-
export {};
|
|
19
|
-
//# sourceMappingURL=node-network-compat-flag.d.ts.map
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { createFlagClient, readLocalOverride } from "@indigoai-us/hq-flags-client";
|
|
2
|
-
import { resolveFlagRegistryEndpoint } from "./lib/flag-registry-endpoint.js";
|
|
3
|
-
export const NODE_NETWORK_FORCE_IPV4_FLAG = "cli.network.force-ipv4";
|
|
4
|
-
export const NODE_NETWORK_FORCE_IPV4_FLAG_FALLBACK = false;
|
|
5
|
-
// Node waits up to 1s before trying the next address family. Keep this request
|
|
6
|
-
// deadline above that wait so a dead IPv6 path can reach the usable IPv4 host.
|
|
7
|
-
export const NODE_NETWORK_FORCE_IPV4_FLAG_TIMEOUT_MS = 1_500;
|
|
8
|
-
function safeErrorClass(error) {
|
|
9
|
-
const name = error instanceof Error ? error.name : "UnknownError";
|
|
10
|
-
return /^[A-Za-z][A-Za-z0-9]{0,63}$/.test(name)
|
|
11
|
-
? name
|
|
12
|
-
: "UnknownError";
|
|
13
|
-
}
|
|
14
|
-
function reportFlagFailure(error) {
|
|
15
|
-
process.stderr.write("HQ network IPv4 flag lookup failed (" +
|
|
16
|
-
safeErrorClass(error) +
|
|
17
|
-
"); using default off.\n");
|
|
18
|
-
}
|
|
19
|
-
async function loadCachedIdToken() {
|
|
20
|
-
const { loadCachedTokens } = await import("@indigoai-us/hq-cloud");
|
|
21
|
-
return loadCachedTokens()?.idToken ?? "";
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Read the opt-in hq-flags gate before CLI dispatch. Local hq-flags overrides
|
|
25
|
-
* work without a token or endpoint; service decisions require a company
|
|
26
|
-
* identity and use a short request deadline so startup remains bounded.
|
|
27
|
-
*/
|
|
28
|
-
export async function nodeNetworkForceIpv4Enabled(dependencies = {}) {
|
|
29
|
-
const env = dependencies.env ?? process.env;
|
|
30
|
-
const localOverride = readLocalOverride(NODE_NETWORK_FORCE_IPV4_FLAG, env);
|
|
31
|
-
if (localOverride !== null)
|
|
32
|
-
return localOverride;
|
|
33
|
-
const endpoint = resolveFlagRegistryEndpoint(undefined, env);
|
|
34
|
-
const companyUid = env.HQ_COMPANY_UID?.trim() || "";
|
|
35
|
-
if (!/^cmp_[A-Za-z0-9]{3,128}$/.test(companyUid)) {
|
|
36
|
-
return NODE_NETWORK_FORCE_IPV4_FLAG_FALLBACK;
|
|
37
|
-
}
|
|
38
|
-
const companySlug = env.HQ_COMPANY_SLUG?.trim() || "";
|
|
39
|
-
let client;
|
|
40
|
-
let failed = false;
|
|
41
|
-
let failureReported = false;
|
|
42
|
-
let enabled = NODE_NETWORK_FORCE_IPV4_FLAG_FALLBACK;
|
|
43
|
-
const reportFailureOnce = (error) => {
|
|
44
|
-
failed = true;
|
|
45
|
-
if (failureReported)
|
|
46
|
-
return;
|
|
47
|
-
failureReported = true;
|
|
48
|
-
(dependencies.reportFailure ?? reportFlagFailure)(error);
|
|
49
|
-
};
|
|
50
|
-
try {
|
|
51
|
-
const token = await (dependencies.getToken ?? loadCachedIdToken)();
|
|
52
|
-
if (!token)
|
|
53
|
-
return NODE_NETWORK_FORCE_IPV4_FLAG_FALLBACK;
|
|
54
|
-
const createClient = dependencies.createClient ?? createFlagClient;
|
|
55
|
-
client = createClient({
|
|
56
|
-
endpoint,
|
|
57
|
-
companyUid,
|
|
58
|
-
...(companySlug && /^[A-Za-z0-9][A-Za-z0-9-]{0,63}$/.test(companySlug)
|
|
59
|
-
? { companyIdentifiers: [companySlug] }
|
|
60
|
-
: {}),
|
|
61
|
-
env: {},
|
|
62
|
-
getToken: () => token,
|
|
63
|
-
refreshIntervalMs: 0,
|
|
64
|
-
requestTimeoutMs: NODE_NETWORK_FORCE_IPV4_FLAG_TIMEOUT_MS,
|
|
65
|
-
onError: reportFailureOnce,
|
|
66
|
-
});
|
|
67
|
-
await client.ready();
|
|
68
|
-
if (!failed) {
|
|
69
|
-
enabled = client.isEnabled(NODE_NETWORK_FORCE_IPV4_FLAG, {
|
|
70
|
-
fallback: NODE_NETWORK_FORCE_IPV4_FLAG_FALLBACK,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
reportFailureOnce(error);
|
|
76
|
-
}
|
|
77
|
-
finally {
|
|
78
|
-
try {
|
|
79
|
-
client?.close();
|
|
80
|
-
}
|
|
81
|
-
catch (error) {
|
|
82
|
-
reportFailureOnce(error);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return failed ? NODE_NETWORK_FORCE_IPV4_FLAG_FALLBACK : enabled;
|
|
86
|
-
}
|
|
87
|
-
//# sourceMappingURL=node-network-compat-flag.js.map
|