@indigoai-us/hq-cli 5.294.0 → 5.296.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 +33 -0
- package/dist/command-catalog.generated.d.ts +70 -9
- package/dist/command-catalog.generated.js +88 -12
- package/dist/commands/auth.d.ts +1 -0
- package/dist/commands/auth.js +76 -1
- package/dist/commands/core-job-native.d.ts +7 -0
- package/dist/commands/core-job-native.js +20 -0
- package/dist/commands/core-job-probe-native.d.ts +45 -0
- package/dist/commands/core-job-probe-native.js +564 -0
- package/dist/commands/core-job-remote-launcher.d.ts +7 -0
- package/dist/commands/core-job-remote-launcher.js +51 -0
- package/dist/commands/core-job-run-native.d.ts +47 -0
- package/dist/commands/core-job-run-native.js +1215 -0
- package/dist/commands/core.js +1 -1
- package/dist/commands/doctor.js +0 -74
- package/dist/commands/lanes-message.js +24 -5
- package/dist/commands/lanes-resume.js +1 -3
- package/dist/commands/lanes-shared.d.ts +1 -2
- package/dist/commands/lanes-stories.d.ts +1 -0
- package/dist/commands/lanes-stories.js +40 -1
- package/dist/commands/lanes.d.ts +1 -9
- package/dist/commands/lanes.js +197 -174
- package/dist/commands/run.js +5 -0
- package/dist/fast-scaffold.d.ts +0 -5
- package/dist/fast-scaffold.js +0 -10
- package/dist/index.js +1 -10
- package/dist/lib/core-utils/sentry-report.d.ts +0 -19
- package/dist/lib/core-utils/sentry-report.js +8 -61
- package/dist/lib/doctor/checks/claude-wiring.js +9 -0
- package/dist/lib/lanes/flag-keys.d.ts +2 -0
- package/dist/lib/lanes/flag-keys.js +2 -0
- package/dist/lib/lanes/manager-status-gate.d.ts +0 -17
- package/dist/lib/lanes/manager-status-gate.js +2 -43
- package/dist/lib/lanes/mesh-events.js +3 -0
- package/dist/lib/lanes/outpost-dispatch.d.ts +2 -0
- package/dist/lib/lanes/outpost-dispatch.js +7 -1
- package/dist/lib/lanes/outpost-hire-cache.d.ts +0 -1
- package/dist/lib/lanes/outpost-hire-cache.js +0 -1
- package/dist/lib/lanes/outpost-snapshot-memo.d.ts +6 -0
- package/dist/lib/lanes/outpost-snapshot-memo.js +14 -0
- package/dist/lib/lanes/reconcile.d.ts +2 -3
- package/dist/lib/lanes/reconcile.js +67 -57
- package/dist/lib/lanes/spawn.d.ts +0 -2
- package/dist/lib/lanes/spawn.js +57 -5
- package/dist/lib/lanes/store.js +54 -0
- package/dist/lib/lanes/stories.d.ts +7 -1
- package/dist/lib/lanes/stories.js +16 -0
- package/dist/lib/lanes/types.d.ts +30 -1
- package/dist/lib/lanes/types.js +2 -2
- package/dist/lib/lanes/watcher.d.ts +4 -3
- package/dist/lib/lanes/watcher.js +122 -56
- package/dist/lib/mesh/live/daemon/run.js +18 -1
- package/dist/run/env-graph-guard.d.ts +2 -2
- package/dist/run/env-graph-guard.js +5 -2
- package/dist/run/hq-plugin.js +6 -5
- package/dist/utils/cognito-session.d.ts +6 -0
- package/dist/utils/cognito-session.js +9 -0
- package/dist/utils/self-update.js +31 -0
- package/dist/utils/version-gate.js +22 -3
- package/package.json +2 -2
- package/dist/fast-lanes-list-route.d.ts +0 -8
- package/dist/fast-lanes-list-route.js +0 -32
- package/dist/fast-lanes-list.d.ts +0 -3
- package/dist/fast-lanes-list.js +0 -224
- package/dist/lib/doctor/client-health-sentry-flag.d.ts +0 -23
- package/dist/lib/doctor/client-health-sentry-flag.js +0 -99
- package/dist/lib/doctor/client-health-sentry.d.ts +0 -71
- package/dist/lib/doctor/client-health-sentry.js +0 -286
- package/dist/lib/lanes/outpost-snapshot-cache.d.ts +0 -26
- package/dist/lib/lanes/outpost-snapshot-cache.js +0 -215
- package/dist/lib/lanes/outpost-snapshot-refresh.d.ts +0 -21
- package/dist/lib/lanes/outpost-snapshot-refresh.js +0 -329
package/dist/fast-lanes-list.js
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as os from "node:os";
|
|
3
|
-
import * as path from "node:path";
|
|
4
|
-
import { compareLaneQueueOrder, laneRunsOnHost } from "./lib/lanes/admission.js";
|
|
5
|
-
import { readOutpostIdentity, resolveAdmissionHost } from "./lib/lanes/admission-host.js";
|
|
6
|
-
import { LaneSchemaError, isLanesError } from "./lib/lanes/errors.js";
|
|
7
|
-
import { startOutpostLaneSnapshotRefresh } from "./lib/lanes/outpost-snapshot-refresh.js";
|
|
8
|
-
import { OUTPOST_HIRE_SUMMARY_CACHE_TTL_MS, outpostHireSummaryCacheKey, readOutpostHireSummaryCacheIncludingStale, } from "./lib/lanes/outpost-hire-cache.js";
|
|
9
|
-
import { resolveLanesHqRoot } from "./lib/lanes/hq-root.js";
|
|
10
|
-
import { laneStories } from "./lib/lanes/stories.js";
|
|
11
|
-
import { readAllLanes } from "./lib/lanes/store.js";
|
|
12
|
-
import { isTerminalLaneState } from "./lib/lanes/types.js";
|
|
13
|
-
const LANES_RUNS_REL = path.join("workspace", "lanes-runs");
|
|
14
|
-
function withoutProviderHome(value) {
|
|
15
|
-
if (Array.isArray(value))
|
|
16
|
-
return value.map(withoutProviderHome);
|
|
17
|
-
if (value === null || typeof value !== "object")
|
|
18
|
-
return value;
|
|
19
|
-
const safe = {};
|
|
20
|
-
for (const [key, nested] of Object.entries(value)) {
|
|
21
|
-
if (key === "provider_home_at_spawn")
|
|
22
|
-
continue;
|
|
23
|
-
safe[key] = withoutProviderHome(nested);
|
|
24
|
-
}
|
|
25
|
-
return safe;
|
|
26
|
-
}
|
|
27
|
-
function printJson(value) {
|
|
28
|
-
console.log(JSON.stringify(withoutProviderHome(value), null, 2));
|
|
29
|
-
}
|
|
30
|
-
function errorMessage(err) {
|
|
31
|
-
return err instanceof Error ? err.message : String(err);
|
|
32
|
-
}
|
|
33
|
-
function failJson(err) {
|
|
34
|
-
if (isLanesError(err)) {
|
|
35
|
-
if (err.error === "hire_grant_exhausted") {
|
|
36
|
-
process.stderr.write(`hq lanes: ${err.message}\n`);
|
|
37
|
-
}
|
|
38
|
-
printJson({ ...err.toJSON(), message: err.message });
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
const message = errorMessage(err);
|
|
42
|
-
process.stderr.write(`hq lanes: ${message}${err instanceof Error && err.stack ? `\n${err.stack}` : ""}\n`);
|
|
43
|
-
printJson({ error: "internal", message });
|
|
44
|
-
}
|
|
45
|
-
process.exitCode = 1;
|
|
46
|
-
}
|
|
47
|
-
function laneHireFields(lane, aliveChildrenByParent) {
|
|
48
|
-
const aliveChildCount = aliveChildrenByParent.get(lane.lane_id) ?? 0;
|
|
49
|
-
const hireGrant = lane.hire_grant ?? 0;
|
|
50
|
-
return {
|
|
51
|
-
hire_grant: hireGrant,
|
|
52
|
-
hire_alive_children: aliveChildCount,
|
|
53
|
-
hire: `${aliveChildCount}/${hireGrant}`,
|
|
54
|
-
hire_summary: `hire ${aliveChildCount}/${hireGrant}`,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
function isOverdue(hqRoot, laneId, now) {
|
|
58
|
-
const filePath = path.join(hqRoot, LANES_RUNS_REL, laneId, "timeout.json");
|
|
59
|
-
let text;
|
|
60
|
-
try {
|
|
61
|
-
text = fs.readFileSync(filePath, "utf8");
|
|
62
|
-
}
|
|
63
|
-
catch (err) {
|
|
64
|
-
if (err.code === "ENOENT")
|
|
65
|
-
return false;
|
|
66
|
-
throw err;
|
|
67
|
-
}
|
|
68
|
-
let raw;
|
|
69
|
-
try {
|
|
70
|
-
raw = JSON.parse(text);
|
|
71
|
-
}
|
|
72
|
-
catch (err) {
|
|
73
|
-
throw new LaneSchemaError(`Invalid JSON in ${filePath}: ${errorMessage(err)}`, { path: filePath });
|
|
74
|
-
}
|
|
75
|
-
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
76
|
-
return false;
|
|
77
|
-
const deadline = raw.deadline_at;
|
|
78
|
-
if (typeof deadline !== "string")
|
|
79
|
-
return false;
|
|
80
|
-
const at = Date.parse(deadline);
|
|
81
|
-
if (!Number.isFinite(at))
|
|
82
|
-
return false;
|
|
83
|
-
return now.getTime() > at;
|
|
84
|
-
}
|
|
85
|
-
function isOwnerLane(lane, host) {
|
|
86
|
-
return lane.host.kind === "outpost" &&
|
|
87
|
-
!isTerminalLaneState(lane.state) &&
|
|
88
|
-
!laneRunsOnHost(lane, host);
|
|
89
|
-
}
|
|
90
|
-
async function buildLaneList(hqRoot, sourceLanes, host, now, env) {
|
|
91
|
-
const selectedLanes = sourceLanes.sort(compareLaneQueueOrder);
|
|
92
|
-
const aliveChildrenByParent = new Map();
|
|
93
|
-
for (const child of selectedLanes) {
|
|
94
|
-
if (child.senior.kind !== "lane" || !child.senior.id || isTerminalLaneState(child.state))
|
|
95
|
-
continue;
|
|
96
|
-
aliveChildrenByParent.set(child.senior.id, (aliveChildrenByParent.get(child.senior.id) ?? 0) + 1);
|
|
97
|
-
}
|
|
98
|
-
const ownerLanes = selectedLanes.filter((lane) => isOwnerLane(lane, host));
|
|
99
|
-
const hireByLane = new Map();
|
|
100
|
-
const remoteSnapshotMetadata = new Map();
|
|
101
|
-
let remoteLanesLoading = false;
|
|
102
|
-
for (const lane of selectedLanes) {
|
|
103
|
-
if (!isOwnerLane(lane, host)) {
|
|
104
|
-
hireByLane.set(lane.lane_id, {
|
|
105
|
-
...laneHireFields(lane, aliveChildrenByParent),
|
|
106
|
-
hire_source: "local",
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
if (ownerLanes.length > 0) {
|
|
111
|
-
const nowMs = now.getTime();
|
|
112
|
-
const cached = readOutpostHireSummaryCacheIncludingStale(hqRoot, nowMs);
|
|
113
|
-
let refreshNeeded = false;
|
|
114
|
-
for (const lane of ownerLanes) {
|
|
115
|
-
if (lane.host.kind !== "outpost")
|
|
116
|
-
continue;
|
|
117
|
-
const summary = cached.get(outpostHireSummaryCacheKey(lane.host.id, lane.lane_id));
|
|
118
|
-
if (!summary) {
|
|
119
|
-
hireByLane.set(lane.lane_id, {
|
|
120
|
-
...laneHireFields(lane, aliveChildrenByParent),
|
|
121
|
-
hire_source: "local",
|
|
122
|
-
});
|
|
123
|
-
remoteSnapshotMetadata.set(lane.lane_id, { ageSeconds: null, stale: true, loading: true });
|
|
124
|
-
refreshNeeded = true;
|
|
125
|
-
remoteLanesLoading = true;
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
const ageMs = Math.max(0, nowMs - Date.parse(summary.cached_at));
|
|
129
|
-
const stale = ageMs >= OUTPOST_HIRE_SUMMARY_CACHE_TTL_MS;
|
|
130
|
-
hireByLane.set(lane.lane_id, {
|
|
131
|
-
hire_grant: summary.hire_grant,
|
|
132
|
-
hire_alive_children: summary.hire_alive_children,
|
|
133
|
-
hire: summary.hire,
|
|
134
|
-
hire_summary: summary.hire_summary,
|
|
135
|
-
hire_source: "owner",
|
|
136
|
-
});
|
|
137
|
-
remoteSnapshotMetadata.set(lane.lane_id, {
|
|
138
|
-
ageSeconds: Math.floor(ageMs / 1_000),
|
|
139
|
-
stale,
|
|
140
|
-
loading: stale,
|
|
141
|
-
});
|
|
142
|
-
if (stale) {
|
|
143
|
-
refreshNeeded = true;
|
|
144
|
-
remoteLanesLoading = true;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
if (refreshNeeded)
|
|
148
|
-
startOutpostLaneSnapshotRefresh(hqRoot, env);
|
|
149
|
-
}
|
|
150
|
-
const sessions = selectedLanes.filter((lane) => lane.state === "running" && lane.senior.kind === "session" && Boolean(lane.senior.id));
|
|
151
|
-
let sessionLiveness;
|
|
152
|
-
let livenessDeps;
|
|
153
|
-
if (sessions.length > 0) {
|
|
154
|
-
const liveness = await import("./lib/lanes/liveness.js");
|
|
155
|
-
sessionLiveness = liveness.sessionLiveness;
|
|
156
|
-
const base = liveness.defaultLivenessDeps();
|
|
157
|
-
livenessDeps = {
|
|
158
|
-
...base,
|
|
159
|
-
now: () => now.getTime(),
|
|
160
|
-
homedir: () => os.homedir(),
|
|
161
|
-
env,
|
|
162
|
-
existsSync: (file) => fs.existsSync(file),
|
|
163
|
-
readFileSync: (file, encoding) => fs.readFileSync(file, encoding),
|
|
164
|
-
hostname: () => os.hostname(),
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
const livenessBySession = new Map();
|
|
168
|
-
const lanes = selectedLanes.map((lane) => {
|
|
169
|
-
const stories = laneStories(lane);
|
|
170
|
-
const remoteQueued = lane.state === "queued" && lane.host.kind === "outpost" && !laneRunsOnHost(lane, host);
|
|
171
|
-
let seniorLost = false;
|
|
172
|
-
if (lane.state === "running" && lane.senior.kind === "session" && lane.senior.id && sessionLiveness && livenessDeps) {
|
|
173
|
-
const sessionId = lane.senior.id;
|
|
174
|
-
const cached = livenessBySession.get(sessionId);
|
|
175
|
-
if (cached !== undefined)
|
|
176
|
-
seniorLost = cached;
|
|
177
|
-
else {
|
|
178
|
-
const state = sessionLiveness(hqRoot, sessionId, {
|
|
179
|
-
nowMs: now.getTime(),
|
|
180
|
-
deps: livenessDeps,
|
|
181
|
-
});
|
|
182
|
-
seniorLost = state === null || state === "ended" || state === "lost";
|
|
183
|
-
livenessBySession.set(sessionId, seniorLost);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return {
|
|
187
|
-
...lane,
|
|
188
|
-
...hireByLane.get(lane.lane_id),
|
|
189
|
-
stories,
|
|
190
|
-
story_ids: stories.map((story) => story.story_id),
|
|
191
|
-
no_story_reason: lane.no_story_reason ?? null,
|
|
192
|
-
...(remoteQueued ? { state: "remote", remote_state: lane.state, queued_reason: undefined } : {}),
|
|
193
|
-
...(remoteSnapshotMetadata.has(lane.lane_id)
|
|
194
|
-
? {
|
|
195
|
-
remote_snapshot_age_s: remoteSnapshotMetadata.get(lane.lane_id).ageSeconds,
|
|
196
|
-
remote_snapshot_stale: remoteSnapshotMetadata.get(lane.lane_id).stale,
|
|
197
|
-
...(remoteSnapshotMetadata.get(lane.lane_id).loading ? { remote_snapshot_loading: true } : {}),
|
|
198
|
-
}
|
|
199
|
-
: {}),
|
|
200
|
-
overdue: Boolean(lane.overdue) || isOverdue(hqRoot, lane.lane_id, now),
|
|
201
|
-
stalled: Boolean(lane.stalled),
|
|
202
|
-
senior_lost: seniorLost,
|
|
203
|
-
};
|
|
204
|
-
});
|
|
205
|
-
return { lanes, ...(remoteLanesLoading ? { remote_lanes_loading: true } : {}) };
|
|
206
|
-
}
|
|
207
|
-
/** Lightweight implementation for the exact cache-first `lanes list --json` entrypoint. */
|
|
208
|
-
export async function runFastCachedLanesList() {
|
|
209
|
-
try {
|
|
210
|
-
const env = process.env;
|
|
211
|
-
const hqRoot = resolveLanesHqRoot({ env, cwd: process.cwd() });
|
|
212
|
-
const now = new Date();
|
|
213
|
-
const host = resolveAdmissionHost({
|
|
214
|
-
hostname: () => os.hostname(),
|
|
215
|
-
readOutpostIdentity: () => readOutpostIdentity(),
|
|
216
|
-
});
|
|
217
|
-
const lanes = await buildLaneList(hqRoot, readAllLanes(hqRoot), host, now, env);
|
|
218
|
-
printJson({ ok: true, action: "list", ...lanes });
|
|
219
|
-
}
|
|
220
|
-
catch (err) {
|
|
221
|
-
failJson(err);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
//# sourceMappingURL=fast-lanes-list.js.map
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { type FlagClient, type FlagClientOptions } from "@indigoai-us/hq-flags-client";
|
|
2
|
-
export declare const CLIENT_HEALTH_SENTRY_DIRECT_FLAG = "client-health.sentry-direct";
|
|
3
|
-
export declare const CLIENT_HEALTH_SENTRY_DIRECT_FALLBACK = false;
|
|
4
|
-
export declare const CLIENT_HEALTH_SENTRY_DIRECT_TIMEOUT_MS = 150;
|
|
5
|
-
type ClientHealthFlagClient = Pick<FlagClient, "ready" | "isEnabled" | "close">;
|
|
6
|
-
export interface ClientHealthSentryFlagDependencies {
|
|
7
|
-
env?: NodeJS.ProcessEnv;
|
|
8
|
-
createClient?: (options: FlagClientOptions) => ClientHealthFlagClient;
|
|
9
|
-
getToken?: FlagClientOptions["getToken"];
|
|
10
|
-
tokenTimeoutMs?: number;
|
|
11
|
-
fetch?: typeof fetch;
|
|
12
|
-
reportFailure?: (error: unknown) => void;
|
|
13
|
-
}
|
|
14
|
-
export interface ClientHealthSentryCompany {
|
|
15
|
-
companyUid: string;
|
|
16
|
-
companySlug?: string;
|
|
17
|
-
}
|
|
18
|
-
/** Resolve the active company gate with a bounded hq-flags read. */
|
|
19
|
-
export declare function clientHealthSentryDirectEnabled(dependencies?: ClientHealthSentryFlagDependencies, callerSignal?: AbortSignal): Promise<boolean>;
|
|
20
|
-
/** Resolve the default-off gate for one explicitly identified company. */
|
|
21
|
-
export declare function clientHealthSentryDirectEnabledForCompany(company: ClientHealthSentryCompany, dependencies?: ClientHealthSentryFlagDependencies, callerSignal?: AbortSignal): Promise<boolean>;
|
|
22
|
-
export {};
|
|
23
|
-
//# sourceMappingURL=client-health-sentry-flag.d.ts.map
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { createFlagClient, } from "@indigoai-us/hq-flags-client";
|
|
2
|
-
import { resolveFlagRegistryEndpoint } from "../flag-registry-endpoint.js";
|
|
3
|
-
import { getClientHealthFlagToken } from "./client-health-flag-token.js";
|
|
4
|
-
export const CLIENT_HEALTH_SENTRY_DIRECT_FLAG = "client-health.sentry-direct";
|
|
5
|
-
export const CLIENT_HEALTH_SENTRY_DIRECT_FALLBACK = false;
|
|
6
|
-
export const CLIENT_HEALTH_SENTRY_DIRECT_TIMEOUT_MS = 150;
|
|
7
|
-
function reportFlagFailure(error) {
|
|
8
|
-
const name = error instanceof Error ? error.name : "UnknownError";
|
|
9
|
-
const safeName = /^[A-Za-z][A-Za-z0-9]{0,63}$/.test(name) ? name : "UnknownError";
|
|
10
|
-
process.stderr.write(`HQ client-health Sentry flag lookup failed (${safeName}); using default off.\n`);
|
|
11
|
-
}
|
|
12
|
-
function combineSignals(first, second) {
|
|
13
|
-
return first ? AbortSignal.any([first, second]) : second;
|
|
14
|
-
}
|
|
15
|
-
/** Resolve the active company gate with a bounded hq-flags read. */
|
|
16
|
-
export async function clientHealthSentryDirectEnabled(dependencies = {}, callerSignal) {
|
|
17
|
-
const env = dependencies.env ?? process.env;
|
|
18
|
-
const companyUid = env.HQ_COMPANY_UID?.trim() || "";
|
|
19
|
-
if (!/^cmp_[A-Za-z0-9]{3,128}$/.test(companyUid)) {
|
|
20
|
-
return CLIENT_HEALTH_SENTRY_DIRECT_FALLBACK;
|
|
21
|
-
}
|
|
22
|
-
return clientHealthSentryDirectEnabledForCompany({
|
|
23
|
-
companyUid,
|
|
24
|
-
companySlug: env.HQ_COMPANY_SLUG?.trim() || undefined,
|
|
25
|
-
}, dependencies, callerSignal);
|
|
26
|
-
}
|
|
27
|
-
/** Resolve the default-off gate for one explicitly identified company. */
|
|
28
|
-
export async function clientHealthSentryDirectEnabledForCompany(company, dependencies = {}, callerSignal) {
|
|
29
|
-
const env = dependencies.env ?? process.env;
|
|
30
|
-
const companyUid = company.companyUid.trim();
|
|
31
|
-
if (!/^cmp_[A-Za-z0-9]{3,128}$/.test(companyUid)) {
|
|
32
|
-
return CLIENT_HEALTH_SENTRY_DIRECT_FALLBACK;
|
|
33
|
-
}
|
|
34
|
-
if (callerSignal?.aborted)
|
|
35
|
-
return CLIENT_HEALTH_SENTRY_DIRECT_FALLBACK;
|
|
36
|
-
let client;
|
|
37
|
-
let failed = false;
|
|
38
|
-
let failureReported = false;
|
|
39
|
-
let enabled = CLIENT_HEALTH_SENTRY_DIRECT_FALLBACK;
|
|
40
|
-
const reportFailureOnce = (error) => {
|
|
41
|
-
failed = true;
|
|
42
|
-
if (failureReported)
|
|
43
|
-
return;
|
|
44
|
-
failureReported = true;
|
|
45
|
-
(dependencies.reportFailure ?? reportFlagFailure)(error);
|
|
46
|
-
};
|
|
47
|
-
let token;
|
|
48
|
-
try {
|
|
49
|
-
token = await getClientHealthFlagToken(dependencies.getToken, dependencies.tokenTimeoutMs, callerSignal);
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
52
|
-
if (!callerSignal?.aborted)
|
|
53
|
-
reportFailureOnce(error);
|
|
54
|
-
return CLIENT_HEALTH_SENTRY_DIRECT_FALLBACK;
|
|
55
|
-
}
|
|
56
|
-
const companySlug = company.companySlug?.trim() || "";
|
|
57
|
-
const deadline = AbortSignal.timeout(CLIENT_HEALTH_SENTRY_DIRECT_TIMEOUT_MS);
|
|
58
|
-
const signal = callerSignal ? AbortSignal.any([callerSignal, deadline]) : deadline;
|
|
59
|
-
const doFetch = dependencies.fetch ?? globalThis.fetch;
|
|
60
|
-
try {
|
|
61
|
-
client = (dependencies.createClient ?? createFlagClient)({
|
|
62
|
-
endpoint: resolveFlagRegistryEndpoint(undefined, env),
|
|
63
|
-
companyUid,
|
|
64
|
-
...(companySlug && /^[A-Za-z0-9][A-Za-z0-9-]{0,63}$/.test(companySlug)
|
|
65
|
-
? { companyIdentifiers: [companySlug] }
|
|
66
|
-
: {}),
|
|
67
|
-
env: {},
|
|
68
|
-
getToken: () => token,
|
|
69
|
-
fetch: (input, init) => doFetch(input, {
|
|
70
|
-
...init,
|
|
71
|
-
signal: combineSignals(init?.signal ?? undefined, signal),
|
|
72
|
-
}),
|
|
73
|
-
refreshIntervalMs: 0,
|
|
74
|
-
requestTimeoutMs: CLIENT_HEALTH_SENTRY_DIRECT_TIMEOUT_MS,
|
|
75
|
-
onError: reportFailureOnce,
|
|
76
|
-
});
|
|
77
|
-
await client.ready();
|
|
78
|
-
if (!failed && !signal.aborted) {
|
|
79
|
-
enabled = client.isEnabled(CLIENT_HEALTH_SENTRY_DIRECT_FLAG, {
|
|
80
|
-
fallback: CLIENT_HEALTH_SENTRY_DIRECT_FALLBACK,
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
catch (error) {
|
|
85
|
-
reportFailureOnce(error);
|
|
86
|
-
}
|
|
87
|
-
finally {
|
|
88
|
-
try {
|
|
89
|
-
client?.close();
|
|
90
|
-
}
|
|
91
|
-
catch (error) {
|
|
92
|
-
reportFailureOnce(error);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return failed || signal.aborted
|
|
96
|
-
? CLIENT_HEALTH_SENTRY_DIRECT_FALLBACK
|
|
97
|
-
: enabled;
|
|
98
|
-
}
|
|
99
|
-
//# sourceMappingURL=client-health-sentry-flag.js.map
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
export declare const CLIENT_HEALTH_SENTRY_NOT_HANDLED_EXIT_CODE = 10;
|
|
2
|
-
export declare const CLIENT_HEALTH_SENTRY_MAX_INPUT_BYTES: number;
|
|
3
|
-
export declare const CLIENT_HEALTH_SENTRY_MAX_FINDINGS = 250;
|
|
4
|
-
export declare const CLIENT_HEALTH_INSTALL_ID_FILENAME = "install-id";
|
|
5
|
-
export declare const CLIENT_HEALTH_LAST_RESULT_FILENAME = "last-result.json";
|
|
6
|
-
export type ClientHealthFixOutcome = "not_auto_fixable" | "fix_failed" | "fix_timed_out";
|
|
7
|
-
export type ClientHealthStatus = "FAIL" | "WARN";
|
|
8
|
-
export type ClientHealthCheckClass = "sync.update.core" | "sync.journal.personal" | "sync.journal.personal_vault" | "sync.journal.company" | "sync.manifest.personal" | "sync.manifest.personal_vault" | "sync.manifest.company" | "sync.runtime" | "other";
|
|
9
|
-
export interface ClientHealthSentryEvent {
|
|
10
|
-
message: string;
|
|
11
|
-
level: "warning";
|
|
12
|
-
fingerprint: ["client-health", ClientHealthCheckClass, string];
|
|
13
|
-
tags: {
|
|
14
|
-
check_class: ClientHealthCheckClass;
|
|
15
|
-
reason_code: string;
|
|
16
|
-
fix_outcome: ClientHealthFixOutcome;
|
|
17
|
-
status: ClientHealthStatus;
|
|
18
|
-
failing_check_count: string;
|
|
19
|
-
cli_version: string;
|
|
20
|
-
core_version: string;
|
|
21
|
-
sync_version: string;
|
|
22
|
-
platform: string;
|
|
23
|
-
arch: string;
|
|
24
|
-
};
|
|
25
|
-
user: {
|
|
26
|
-
id: string;
|
|
27
|
-
ip_address: null;
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
interface ClientHealthFinding {
|
|
31
|
-
checkClass: ClientHealthCheckClass;
|
|
32
|
-
companySlug: string | null;
|
|
33
|
-
reasonCode: string;
|
|
34
|
-
status: ClientHealthStatus;
|
|
35
|
-
}
|
|
36
|
-
export interface ClientHealthLastResult {
|
|
37
|
-
check_class: ClientHealthCheckClass;
|
|
38
|
-
outcome: ClientHealthFixOutcome;
|
|
39
|
-
next_step: string;
|
|
40
|
-
result_id: string;
|
|
41
|
-
}
|
|
42
|
-
export interface ClientHealthReportVersions {
|
|
43
|
-
cli: string | null;
|
|
44
|
-
core: string | null;
|
|
45
|
-
sync: string | null;
|
|
46
|
-
}
|
|
47
|
-
export interface ClientHealthSentryReportDependencies {
|
|
48
|
-
resolveFlagForCompany: (companySlug: string | null) => Promise<boolean>;
|
|
49
|
-
sendEvents: (events: readonly ClientHealthSentryEvent[]) => Promise<boolean>;
|
|
50
|
-
randomUUID?: () => string;
|
|
51
|
-
versions?: (hqRoot: string) => ClientHealthReportVersions;
|
|
52
|
-
platform?: () => string;
|
|
53
|
-
arch?: () => string;
|
|
54
|
-
}
|
|
55
|
-
/** Collapse company-specific doctor ids before any value can enter a Sentry event. */
|
|
56
|
-
export declare function normalizeClientHealthCheckId(checkId: string): ClientHealthCheckClass;
|
|
57
|
-
/** Keep only the doctor fields needed for safe health reporting. */
|
|
58
|
-
export declare function parseClientHealthDoctorFindings(input: string): ClientHealthFinding[];
|
|
59
|
-
/** Store a random UUID once per local HQ install, without using host identity. */
|
|
60
|
-
export declare function getOrCreateClientHealthInstallId(hqRoot: string, randomUUID?: () => string): string;
|
|
61
|
-
/**
|
|
62
|
-
* Report only after every finding's company has opted in. A mixed rollout
|
|
63
|
-
* falls back as one batch so the old path cannot duplicate partially sent rows.
|
|
64
|
-
*/
|
|
65
|
-
export declare function handleClientHealthSentryReport(input: {
|
|
66
|
-
hqRoot: string;
|
|
67
|
-
doctorJson: string;
|
|
68
|
-
fixOutcome: string;
|
|
69
|
-
}, dependencies: ClientHealthSentryReportDependencies): Promise<"handled" | "not_handled" | "send_failed">;
|
|
70
|
-
export {};
|
|
71
|
-
//# sourceMappingURL=client-health-sentry.d.ts.map
|