@indigoai-us/hq-cli 5.345.199 → 5.345.201

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.
Files changed (61) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/assets/scaffold/core/scripts/lib/session-scope-capability.sh +6 -4
  3. package/dist/command-catalog.generated.d.ts +44 -0
  4. package/dist/command-catalog.generated.js +52 -0
  5. package/dist/commands/bot-runtime.d.ts +35 -0
  6. package/dist/commands/bot-runtime.js +115 -0
  7. package/dist/commands/bot.d.ts +53 -0
  8. package/dist/commands/bot.js +239 -30
  9. package/dist/commands/cloud.d.ts +39 -0
  10. package/dist/commands/cloud.js +173 -82
  11. package/dist/commands/creators.js +2 -1
  12. package/dist/commands/deploy-domains.js +8 -4
  13. package/dist/commands/library-remove.js +2 -1
  14. package/dist/commands/library.js +5 -4
  15. package/dist/commands/mesh.d.ts +0 -7
  16. package/dist/commands/mesh.js +5 -1
  17. package/dist/commands/sync-held.js +2 -2
  18. package/dist/lib/bot/api.d.ts +8 -0
  19. package/dist/lib/bot/api.js +9 -0
  20. package/dist/lib/bot/fleet-download.d.ts +27 -0
  21. package/dist/lib/bot/fleet-download.js +162 -0
  22. package/dist/lib/bot/fleet-local.d.ts +79 -2
  23. package/dist/lib/bot/fleet-local.js +246 -74
  24. package/dist/lib/bot/fleet-python.d.ts +2 -0
  25. package/dist/lib/bot/fleet-python.js +29 -0
  26. package/dist/lib/bot/jobs/source-report.d.ts +31 -0
  27. package/dist/lib/bot/jobs/source-report.js +276 -0
  28. package/dist/lib/bot/legacy-convert.d.ts +107 -4
  29. package/dist/lib/bot/legacy-convert.js +159 -13
  30. package/dist/lib/bot/legacy-provider-login.d.ts +34 -0
  31. package/dist/lib/bot/legacy-provider-login.js +144 -0
  32. package/dist/lib/bot/run.js +20 -1
  33. package/dist/lib/doctor/checks/bots.d.ts +4 -0
  34. package/dist/lib/doctor/checks/bots.js +33 -0
  35. package/dist/lib/lanes/mesh-events.js +3 -1
  36. package/dist/lib/lanes/store.js +7 -0
  37. package/dist/lib/lanes/types.d.ts +2 -0
  38. package/dist/lib/lanes/types.js +1 -0
  39. package/dist/lib/lanes/watcher.d.ts +0 -33
  40. package/dist/lib/lanes/watcher.js +7 -0
  41. package/dist/lib/mesh/api.js +2 -1
  42. package/dist/lib/mesh/live/daemon/run.d.ts +0 -10
  43. package/dist/lib/mesh/live/daemon/run.js +3 -0
  44. package/dist/lib/mesh/live/emit.d.ts +2 -11
  45. package/dist/lib/mesh/live/emit.js +20 -2
  46. package/dist/lib/mesh/live/flush.d.ts +2 -0
  47. package/dist/lib/mesh/live/flush.js +34 -3
  48. package/dist/lib/mesh/live/reporting-privacy.d.ts +7 -0
  49. package/dist/lib/mesh/live/reporting-privacy.js +47 -0
  50. package/dist/lib/work-context/company.d.ts +2 -3
  51. package/dist/lib/work-context/company.js +4 -22
  52. package/dist/secrets-env-action.js +5 -2
  53. package/dist/utils/cognito-callback-port.d.ts +2 -0
  54. package/dist/utils/cognito-callback-port.js +10 -5
  55. package/dist/utils/cognito-config.js +6 -1
  56. package/dist/utils/cognito-session.js +6 -1
  57. package/dist/utils/interactive-caller.d.ts +8 -0
  58. package/dist/utils/interactive-caller.js +10 -0
  59. package/dist/utils/login-browser.js +2 -1
  60. package/dist/utils/resident-package-preload.js +57 -4
  61. package/package.json +7 -7
@@ -0,0 +1,162 @@
1
+ import * as crypto from "node:crypto";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ /** A download that receives no bytes for this long is treated as stalled and retried. */
5
+ export const FLEET_DOWNLOAD_STALL_MS = 90_000;
6
+ /** How many times a stalled or broken download is resumed before giving up. */
7
+ export const FLEET_DOWNLOAD_MAX_RESUMES = 5;
8
+ function expectedError(message) {
9
+ return Object.assign(new Error(message), { expected: true });
10
+ }
11
+ function partFileSize(file) {
12
+ try {
13
+ return fs.statSync(file).size;
14
+ }
15
+ catch {
16
+ return 0;
17
+ }
18
+ }
19
+ function contentLength(response) {
20
+ const raw = response.headers.get("content-length");
21
+ if (!raw)
22
+ return null;
23
+ const value = Number.parseInt(raw, 10);
24
+ return Number.isFinite(value) && value >= 0 ? value : null;
25
+ }
26
+ function contentRangeTotal(response) {
27
+ const raw = response.headers.get("content-range");
28
+ const match = raw ? /\/(\d+)\s*$/.exec(raw) : null;
29
+ if (!match)
30
+ return null;
31
+ const value = Number.parseInt(match[1], 10);
32
+ return Number.isFinite(value) ? value : null;
33
+ }
34
+ async function pumpBody(response, fd, offset, name, total, stallMs, onProgress) {
35
+ if (!response.body) {
36
+ const bytes = Buffer.from(await response.arrayBuffer());
37
+ fs.writeSync(fd, bytes, 0, bytes.length, offset);
38
+ const received = offset + bytes.length;
39
+ onProgress?.({ name, received, total });
40
+ return received;
41
+ }
42
+ const reader = response.body.getReader();
43
+ let received = offset;
44
+ let lastReport = 0;
45
+ for (;;) {
46
+ let timer;
47
+ const stalled = new Promise((_resolve, reject) => {
48
+ timer = setTimeout(() => reject(expectedError(`stalled: no data for ${Math.round(stallMs / 1000)}s`)), stallMs);
49
+ });
50
+ let chunk;
51
+ try {
52
+ chunk = await Promise.race([reader.read(), stalled]);
53
+ }
54
+ catch (error) {
55
+ void reader.cancel().catch(() => undefined);
56
+ throw error;
57
+ }
58
+ finally {
59
+ if (timer)
60
+ clearTimeout(timer);
61
+ }
62
+ if (chunk.done)
63
+ break;
64
+ fs.writeSync(fd, chunk.value, 0, chunk.value.length, received);
65
+ received += chunk.value.length;
66
+ const now = Date.now();
67
+ if (now - lastReport >= 250 || (total !== null && received >= total)) {
68
+ lastReport = now;
69
+ onProgress?.({ name, received, total });
70
+ }
71
+ }
72
+ onProgress?.({ name, received, total });
73
+ return received;
74
+ }
75
+ /**
76
+ * Download `url` into `destination`, writing through `<destination>.part` and
77
+ * resuming from the bytes already there. Every attempt that receives nothing
78
+ * for `stallMs` is retried from where it stopped, up to `maxResumes` times.
79
+ * The file is renamed into place only when the body ended cleanly and, when
80
+ * the server told us its size, the sizes match.
81
+ */
82
+ export async function downloadFleetFile(url, destination, options = {}) {
83
+ const fetcher = options.fetch ?? fetch;
84
+ const stallMs = options.stallMs ?? FLEET_DOWNLOAD_STALL_MS;
85
+ const maxResumes = options.maxResumes ?? FLEET_DOWNLOAD_MAX_RESUMES;
86
+ const name = path.basename(destination);
87
+ const part = `${destination}.part`;
88
+ fs.mkdirSync(path.dirname(destination), { recursive: true, mode: 0o700 });
89
+ let lastFailure = null;
90
+ for (let attempt = 0; attempt <= maxResumes; attempt += 1) {
91
+ const have = partFileSize(part);
92
+ const headers = { ...options.headers };
93
+ if (have > 0)
94
+ headers.Range = `bytes=${have}-`;
95
+ let response;
96
+ try {
97
+ response = await (Object.keys(headers).length > 0 ? fetcher(url, { headers }) : fetcher(url));
98
+ }
99
+ catch (error) {
100
+ lastFailure = error;
101
+ continue;
102
+ }
103
+ let offset = have;
104
+ let total;
105
+ if (response.status === 206 && have > 0) {
106
+ total = contentRangeTotal(response) ?? (contentLength(response) !== null ? have + contentLength(response) : null);
107
+ }
108
+ else if (response.status === 416 && have > 0) {
109
+ void response.body?.cancel().catch(() => undefined);
110
+ fs.renameSync(part, destination);
111
+ return destination;
112
+ }
113
+ else if (response.ok) {
114
+ offset = 0;
115
+ total = contentLength(response);
116
+ }
117
+ else {
118
+ void response.body?.cancel().catch(() => undefined);
119
+ throw expectedError(`Could not download ${name}: HQ storage returned HTTP ${response.status}.`);
120
+ }
121
+ const fd = fs.openSync(part, offset === 0 ? "w" : "r+", 0o600);
122
+ let received;
123
+ try {
124
+ received = await pumpBody(response, fd, offset, name, total, stallMs, options.onProgress);
125
+ }
126
+ catch (error) {
127
+ fs.closeSync(fd);
128
+ lastFailure = error;
129
+ continue;
130
+ }
131
+ fs.closeSync(fd);
132
+ if (total !== null && received !== total) {
133
+ if (received > total)
134
+ fs.rmSync(part, { force: true });
135
+ lastFailure = expectedError(`ended early at ${received} of ${total} bytes`);
136
+ continue;
137
+ }
138
+ fs.renameSync(part, destination);
139
+ return destination;
140
+ }
141
+ const cause = lastFailure instanceof Error ? lastFailure.message : String(lastFailure ?? "unknown error");
142
+ throw expectedError(`Could not download ${name} after ${maxResumes + 1} attempts (${cause}). Check your internet connection and retry; the download continues where it stopped.`);
143
+ }
144
+ /** sha256 of a file on disk, streamed. */
145
+ export function fileSha256(file) {
146
+ const hash = crypto.createHash("sha256");
147
+ const fd = fs.openSync(file, "r");
148
+ try {
149
+ const chunk = Buffer.allocUnsafe(1 << 20);
150
+ for (;;) {
151
+ const read = fs.readSync(fd, chunk, 0, chunk.length, null);
152
+ if (read === 0)
153
+ break;
154
+ hash.update(chunk.subarray(0, read));
155
+ }
156
+ }
157
+ finally {
158
+ fs.closeSync(fd);
159
+ }
160
+ return hash.digest("hex");
161
+ }
162
+ //# sourceMappingURL=fleet-download.js.map
@@ -65,6 +65,9 @@ export interface FleetLocalInstallDeps {
65
65
  now?: () => Date;
66
66
  release?: FleetLocalReleasePin;
67
67
  publicKey?: string | readonly string[];
68
+ onProgress?: FleetInstallProgressListener;
69
+ /** How long a download may go without bytes before it is resumed (tests). */
70
+ downloadStallMs?: number;
68
71
  }
69
72
  export interface FleetLocalProfileInput {
70
73
  agentUid: string;
@@ -113,6 +116,7 @@ export interface FleetLocalBootstrapDeps {
113
116
  spawn?: FleetLocalSpawn;
114
117
  start?: boolean;
115
118
  python?: FleetPython;
119
+ onProgress?: FleetInstallProgressListener;
116
120
  }
117
121
  interface SharedFleetRuntime {
118
122
  id: string;
@@ -257,9 +261,35 @@ export declare function installTargetFleetLocalDistribution(deps?: Omit<FleetLoc
257
261
  target?: FleetLocalTargetRelease;
258
262
  now?: () => number;
259
263
  }): Promise<FleetLocalInstallation>;
264
+ /** What an install is doing right now, for a progress bar or a log line. */
265
+ export type FleetInstallProgress = {
266
+ phase: "download";
267
+ file: string;
268
+ received: number;
269
+ total: number | null;
270
+ } | {
271
+ phase: "verify";
272
+ } | {
273
+ phase: "python";
274
+ } | {
275
+ phase: "extract";
276
+ } | {
277
+ phase: "environment";
278
+ } | {
279
+ phase: "packages";
280
+ } | {
281
+ phase: "ready";
282
+ step: "download" | "runtime";
283
+ cached: boolean;
284
+ };
285
+ export type FleetInstallProgressListener = (progress: FleetInstallProgress) => void;
260
286
  export declare const FLEET_LOCAL_METADATA_DOWNLOAD_TIMEOUT_MS = 30000;
261
- export declare const FLEET_LOCAL_ARCHIVE_DOWNLOAD_TIMEOUT_MS: number;
262
- export declare const FLEET_LOCAL_RUNTIME_DOWNLOAD_TIMEOUT_MS: number;
287
+ /** One machine-wide lock: concurrent creates and adopts share one download instead of repeating it. */
288
+ export declare const FLEET_LOCAL_DISTRIBUTION_LOCK_NAME = "hq-fleet-local-distribution";
289
+ export declare const FLEET_LOCAL_DISTRIBUTION_LOCK_WAIT_MS: number;
290
+ export declare const FLEET_LOCAL_DISTRIBUTION_LOCK_STALE_MS: number;
291
+ /** Whether the pinned release is installed on this machine (no download needed). */
292
+ export declare function fleetLocalDistributionInstalled(release?: FleetLocalReleasePin, root?: string): boolean;
263
293
  export declare function installPinnedFleetLocalDistribution(deps?: FleetLocalInstallDeps): Promise<FleetLocalInstallation>;
264
294
  /** The process id the bot's gateway recorded, when its file reads; the caller checks whether it is alive. */
265
295
  export declare function fleetLocalGatewayPid(agentUid: string, deps?: Pick<FleetLocalControlDeps, "fleetHome">): number | null;
@@ -339,6 +369,53 @@ export declare function windowsExtractionPathProblem(destination: string, longes
339
369
  export declare function importFleetLocalMachineCredential(agentUid: string, machineCredentials: string, deps?: FleetLocalControlDeps, opts?: {
340
370
  keepSource?: boolean;
341
371
  }): void;
372
+ /** The bots with an HQ Fleet profile on this computer, by the id their profile.json names. */
373
+ export declare function listFleetLocalProfiles(deps?: Pick<FleetLocalControlDeps, "fleetHome">): Array<{
374
+ agentUid: string;
375
+ profileDir: string;
376
+ }>;
377
+ export interface FleetLocalProviderAuthStatus {
378
+ authenticated: boolean;
379
+ /** `oauth` or `api_key`, when authenticated. */
380
+ authType?: string;
381
+ }
382
+ /**
383
+ * The helper's word on a provider credential a bot holds here (never the
384
+ * credential itself). Null when the helper cannot be asked: the profile is
385
+ * not installed, or the helper failed.
386
+ */
387
+ export declare function fleetLocalProviderAuthStatus(agentUid: string, provider: string, deps?: FleetLocalControlDeps): FleetLocalProviderAuthStatus | null;
388
+ /**
389
+ * Sign a bot in from a login already on this computer, without a browser:
390
+ * the helper imports the Codex CLI login (CODEX_HOME, else ~/.codex) or
391
+ * copies another Fleet bot's Grok login. `env` is added to the helper's
392
+ * environment (a bot's own Codex home). The profile's provider is already
393
+ * the runtime's preset from the bootstrap; this re-renders and checks it.
394
+ */
395
+ export declare function importFleetLocalProviderLogin(agentUid: string, runtime: "codex" | "grok", deps?: FleetLocalControlDeps, opts?: {
396
+ env?: Record<string, string>;
397
+ }): {
398
+ runtimeProvider: string;
399
+ };
400
+ export interface FleetRuntimeInstallResult {
401
+ release: FleetLocalReleasePin;
402
+ distributionDir: string;
403
+ runtimeDir: string;
404
+ runtimeId: string;
405
+ }
406
+ /**
407
+ * Everything a bot needs that is shared by every bot on this machine: the
408
+ * pinned distribution and runtime archive, the private Python, and the built
409
+ * runtime environment. Safe to run any number of times; a complete install
410
+ * returns at once.
411
+ */
412
+ export declare function ensureFleetRuntimeInstalled(installation: FleetLocalInstallation, deps?: FleetLocalBootstrapDeps): Promise<FleetRuntimeInstallResult>;
413
+ /** Whether the shared runtime for this release is already built on this machine. */
414
+ export declare function fleetRuntimeReady(installation: Pick<FleetLocalInstallation, "release">, deps?: {
415
+ fleetHome?: string;
416
+ platform?: NodeJS.Platform;
417
+ python?: FleetPython;
418
+ }): boolean;
342
419
  export declare function bootstrapFleetLocalBot(input: FleetLocalProfileInput & {
343
420
  machineCredentials?: string;
344
421
  }, deps?: FleetLocalBootstrapDeps): Promise<FleetLocalBootstrapResult>;