@indigoai-us/hq-cli 5.345.190 → 5.345.191

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 CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.345.191] — 2026-11-02
6
+ - `hq bot list` and `hq bot status --json` rows now carry `engine: "hq-fleet-runtime"` for a bot on
7
+ the HQ Fleet Runtime, so the desktop app can tell a Fleet bot from a model-CLI bot (Create brief
8
+ uses this to prefer a Fleet bot).
9
+ - `hq bot auth` on an HQ Fleet Runtime bot gives the restarted gateway up to 15 s to come up before
10
+ reporting; it used to say "signed in, but did not start" while the bot was in fact starting.
11
+ - `hq bot status` on an HQ Fleet Runtime bot shows whether its gateway is installed as a login
12
+ service instead of a "launchd: not installed" line that was about the legacy bot daemon.
13
+ - `hq bot create --no-auto-approve` now reaches an HQ Fleet Runtime bot's profile (`autoApprove:
14
+ false`); with hq-fleet-local 0.4.18 the default profile runs with command approvals off, like a
15
+ model-CLI bot, so a headless bot no longer stalls on an approval nobody can answer.
16
+ - HQ Fleet Runtime pin moves to hq-fleet-local 0.4.18, which stores the bot's
17
+ machine credential with Windows DPAPI under PowerShell 5.1 and no longer
18
+ flashes a console on Windows. The CLI now trusts two release signing keys
19
+ (the 2026-10-30 key and a second key added 2026-11-01); a manifest signed by
20
+ either verifies, and any other key is rejected.
21
+ - `hq bot create` / `hq bot start` (HQ Fleet Runtime): every helper, uv and
22
+ Python child process now always starts with `windowsHide`, which a caller's
23
+ options can no longer turn off, so no console window flashes on Windows.
24
+ A new test records every child process in a Windows create, start, stop and
25
+ status run and checks each one.
26
+
5
27
  ## [5.345.190] — 2026-11-01
6
28
  - `hq files purge <path> --version-id <id>` permanently deletes one old version of a vault file, after a confirmation prompt.
7
29
  - `hq bot create` / `hq bot start` on Windows: the shared HQ Fleet Runtime now
@@ -3,7 +3,7 @@ import { type RuntimeSignInCheck, type RuntimeSignInIssue } from "../lib/bot/run
3
3
  import type { BotStatusFile } from "../lib/bot/status.js";
4
4
  import { probeBotRuntime, type RuntimeProbeResult } from "../lib/bot/probe.js";
5
5
  import { type SpawnInteractive } from "../lib/bot/runtime-auth.js";
6
- import { type BotLocalConfig, type MyLocalBot, type BotKind, type BotMemoryMode, type BotNotRunnableReason, type RuntimeCrash, type BotConfig, type BotRuntimeId, type LaunchctlExec, type BotRole } from "../lib/bot/index.js";
6
+ import { type BotLocalConfig, type MyLocalBot, type BotKind, type BotMemoryMode, type BotNotRunnableReason, type RuntimeCrash, type BotConfig, type BotRuntimeId, type LaunchctlExec, HQ_FLEET_RUNTIME_ENGINE_ID, type BotRole } from "../lib/bot/index.js";
7
7
  /** Real launchctl; every failure is a non-ok result, never a throw. */
8
8
  export declare const defaultLaunchctl: LaunchctlExec;
9
9
  /** The desktop app creates HQ's setup bot under this name, always from the `setup` worker. */
@@ -13,6 +13,8 @@ export interface BotRow {
13
13
  agentUid: string;
14
14
  ownerUid: string;
15
15
  runtime: BotRuntimeId;
16
+ /** `hq-fleet-runtime` for a bot on the HQ Fleet Runtime; absent for a model-CLI bot. */
17
+ engine?: typeof HQ_FLEET_RUNTIME_ENGINE_ID;
16
18
  needsSignIn?: boolean;
17
19
  authCommand?: string;
18
20
  serviceInstallPending?: boolean;
@@ -57,7 +57,7 @@ import { runtimeDisplayName, signInCommand } from "../lib/bot/runtime-sign-in.js
57
57
  import { classifyRuntimeFailure, runtimeFailureReason } from "../lib/bot/runtime-failure.js";
58
58
  import { PROBE_DEFAULT_TIMEOUT_MS, probeBotRuntime } from "../lib/bot/probe.js";
59
59
  import { BOT_LOGIN_RUNTIMES, botAuthEnv, botRuntimeAuth, capacityAccountHome, describeBotAuth, formatBotAuth, isBotLoginRuntime, restoreBotSessions, setAsideBotSessions, signInBotRuntime, signOutBotRuntime, withRuntimeAuth, } from "../lib/bot/runtime-auth.js";
60
- import { BotApi, BotApiError, BOT_RESTORE_DEFAULTS, buildBotLocalConfig, describeBotLocalConfig, orphanBotRunMessage, parseBotLocalConfig, removeOrphanBotDaemon, botDaemonStatus, botDir, botCredsPath, botLogPath, botsRoot, botTokenStateDir, botWorkerRelDir, buildBotDaemonPaths, createBotLogger, deleteBotCreds, installBotDaemon, repairBotDaemonIfStale, healBotDaemonNodeFromJob, introDmText, isBotMemoryMode, botLocalMemoryDir, botMemoryMode, resolveBotMemoryDir, validateBotIntro, validateBotDisplayName, validateBotKickoff, validateBotEffort, validateBotModel, effectiveBotEffort, BOT_EFFORT_LEVELS, DEFAULT_BOT_EFFORT, BOT_MEMORY_MODES, BOT_KINDS, isBotKind, effectiveBotKind, effectiveBotCompanies, normalizeBotCompanies, SETUP_BOT_WORKER_ID, isBotRuntimeId, localNameForRemoteBot, remoteBotRunnability, isPidAlive, listBotWorkerOptions, patchBotConfig, readBotConfig, readBotCredsIdentity, readBotStatus, runtimeCrashSinceStart, resolveBotWorker, runBot, runtimeFor, scaffoldBotMemory, scaffoldBotWorker, startBotDaemon, stopBotDaemon, uninstallBotDaemon, validateBotName, writeBotConfig, writeBotCreds, BOT_RUNTIMES, BOT_INTRO_MAX_CHARS, BOT_DISPLAY_NAME_MAX_CHARS, BOT_KICKOFF_MAX_CHARS, detectBotPlatform, withBotLock, localBotRole, remoteBotRole, remoteBotDisplayName, personFacingName, writeCloudDisplayName, setupSlugFor, ASSISTANT_ROLE, HQ_FLEET_RUNTIME_ENGINE_ID, authenticateFleetLocalBot, bootstrapFleetLocalBot, buildSystemPrompt, controlFleetLocalBot, ensureFleetPython, fleetLocalGatewayPid, fleetLocalProfileNeedsAdoption, fleetProviderForRuntime, importFleetLocalMachineCredential, fleetProviderNeedsSignIn, fleetDefaultCompanies, fleetOwnerCompanies, fleetRuntimeIsDefault, refreshFleetLocalInstructions, fleetLocalPromotionRuntime, installPinnedFleetLocalDistribution, readPromptSources, removeFleetLocalBot, } from "../lib/bot/index.js";
60
+ import { BotApi, BotApiError, BOT_RESTORE_DEFAULTS, buildBotLocalConfig, describeBotLocalConfig, orphanBotRunMessage, parseBotLocalConfig, removeOrphanBotDaemon, botDaemonStatus, botDir, botCredsPath, botLogPath, botsRoot, botTokenStateDir, botWorkerRelDir, buildBotDaemonPaths, createBotLogger, deleteBotCreds, installBotDaemon, repairBotDaemonIfStale, healBotDaemonNodeFromJob, introDmText, isBotMemoryMode, botLocalMemoryDir, botMemoryMode, resolveBotMemoryDir, validateBotIntro, validateBotDisplayName, validateBotKickoff, validateBotEffort, validateBotModel, effectiveBotEffort, BOT_EFFORT_LEVELS, DEFAULT_BOT_EFFORT, BOT_MEMORY_MODES, BOT_KINDS, isBotKind, effectiveBotKind, effectiveBotCompanies, normalizeBotCompanies, SETUP_BOT_WORKER_ID, isBotRuntimeId, localNameForRemoteBot, remoteBotRunnability, isPidAlive, listBotWorkerOptions, patchBotConfig, readBotConfig, readBotCredsIdentity, readBotStatus, runtimeCrashSinceStart, resolveBotWorker, runBot, runtimeFor, scaffoldBotMemory, scaffoldBotWorker, startBotDaemon, stopBotDaemon, uninstallBotDaemon, validateBotName, writeBotConfig, writeBotCreds, BOT_RUNTIMES, BOT_INTRO_MAX_CHARS, BOT_DISPLAY_NAME_MAX_CHARS, BOT_KICKOFF_MAX_CHARS, detectBotPlatform, withBotLock, localBotRole, remoteBotRole, remoteBotDisplayName, personFacingName, writeCloudDisplayName, setupSlugFor, ASSISTANT_ROLE, HQ_FLEET_RUNTIME_ENGINE_ID, authenticateFleetLocalBot, bootstrapFleetLocalBot, buildSystemPrompt, controlFleetLocalBot, ensureFleetPython, fleetLocalGatewayPid, fleetLocalProfileNeedsAdoption, fleetLocalServiceInstalled, fleetProviderForRuntime, importFleetLocalMachineCredential, fleetProviderNeedsSignIn, fleetDefaultCompanies, fleetOwnerCompanies, fleetRuntimeIsDefault, refreshFleetLocalInstructions, fleetLocalPromotionRuntime, installPinnedFleetLocalDistribution, readPromptSources, removeFleetLocalBot, } from "../lib/bot/index.js";
61
61
  function collectRepeated(value, previous) {
62
62
  return [...previous, value];
63
63
  }
@@ -152,6 +152,22 @@ function fleetBotSystemPrompt(name, config, overrides = {}) {
152
152
  function fleetAuthCommand(name, config) {
153
153
  return `hq bot auth ${name} --provider ${config.runtime === "grok" ? "grok" : config.runtime === "codex" ? "chatgpt" : "claude"}`;
154
154
  }
155
+ /** How long a just-(re)started HQ Fleet gateway gets to write its pid before it counts as not started. */
156
+ function fleetStartWaitMs() {
157
+ const raw = Number(process.env.HQ_BOT_FLEET_START_WAIT_MS);
158
+ return Number.isFinite(raw) && raw >= 0 ? raw : 15_000;
159
+ }
160
+ async function fleetGatewayComesUp(agentUid, waitMs, stepMs = 500) {
161
+ const deadline = Date.now() + waitMs;
162
+ for (;;) {
163
+ const pid = fleetLocalGatewayPid(agentUid);
164
+ if (pid !== null && isPidAlive(pid))
165
+ return true;
166
+ if (Date.now() >= deadline)
167
+ return false;
168
+ await new Promise((r) => setTimeout(r, stepMs));
169
+ }
170
+ }
155
171
  function runFleetRuntimeControl(name, config, action, json) {
156
172
  if ((action === "install" || action === "start" || action === "restart") && config.fleetAuthPending === true) {
157
173
  const authCommand = fleetAuthCommand(name, config);
@@ -236,6 +252,7 @@ function rowFor(name, opts = {}) {
236
252
  agentUid: config.agentUid,
237
253
  ownerUid: config.ownerUid,
238
254
  runtime: config.runtime,
255
+ ...(fleet ? { engine: HQ_FLEET_RUNTIME_ENGINE_ID } : {}),
239
256
  model: config.model,
240
257
  effort: effectiveBotEffort(config),
241
258
  effortIsDefault: !config.effort,
@@ -477,6 +494,7 @@ async function adoptFleetRuntimeProfile(name, dir, config) {
477
494
  systemPrompt: fleetBotSystemPrompt(name, config),
478
495
  provider: fleetProviderForRuntime(config.runtime, config.model),
479
496
  ownerCompanies: companyScope,
497
+ ...(config.autoApprove === false ? { autoApprove: false } : {}),
480
498
  }, { python, start: false });
481
499
  patchBotConfig(dir, { engine: installation.engine });
482
500
  const credentials = botCredsPath(dir);
@@ -1124,6 +1142,7 @@ async function runBotCreateLocked(name, opts) {
1124
1142
  systemPrompt: fleetBotSystemPrompt(name, config, { kind, companies }),
1125
1143
  provider: fleetProvider,
1126
1144
  ownerCompanies,
1145
+ ...(opts.autoApprove === false ? { autoApprove: false } : {}),
1127
1146
  firstStart: {
1128
1147
  intro: introDmText(name, runtime, intro, { kind, companies }),
1129
1148
  ...(kickoff ? { kickoff } : {}),
@@ -1452,7 +1471,12 @@ async function runBotStatus(nameArg, opts) {
1452
1471
  console.log(` inbox: last poll ${status.lastInboxPollAt}`);
1453
1472
  }
1454
1473
  console.log(` answering: ${row.busy ? chalk.green(`yes${row.busySince ? `, since ${row.busySince}` : ""}`) : chalk.gray("no")}`);
1455
- console.log(` launchd: ${row.daemonInstalled ? (row.daemonLoaded ? "installed, loaded" : "installed, not loaded") : "not installed"}`);
1474
+ if (fleet) {
1475
+ console.log(` service: ${fleetLocalServiceInstalled(config.agentUid) ? "installed (HQ Fleet gateway, starts at login)" : row.serviceInstallPending ? "not installed yet; installs on hq bot start" : "not installed"}`);
1476
+ }
1477
+ else {
1478
+ console.log(` launchd: ${row.daemonInstalled ? (row.daemonLoaded ? "installed, loaded" : "installed, not loaded") : "not installed"}`);
1479
+ }
1456
1480
  if (row.daemonNodeMissing) {
1457
1481
  console.log(` ${chalk.red(`node missing: ${row.daemonNodeMissing}`)} — launchd cannot start ${name}; fix with: hq bot start ${name}`);
1458
1482
  }
@@ -2150,8 +2174,7 @@ async function runBotAuth(nameArg, opts) {
2150
2174
  model: result.model,
2151
2175
  ...(b.config.fleetAuthPending === true ? { fleetAuthPending: false, fleetServiceInstallPending: !result.restarted, enabled: result.restarted } : {}),
2152
2176
  });
2153
- const pid = fleetLocalGatewayPid(b.config.agentUid);
2154
- const running = pid !== null && isPidAlive(pid);
2177
+ const running = await fleetGatewayComesUp(b.config.agentUid, result.restarted ? fleetStartWaitMs() : 0);
2155
2178
  if (opts.json) {
2156
2179
  console.log(JSON.stringify({ ok: true, name: b.name, runtime: HQ_FLEET_RUNTIME_ENGINE_ID, ...result, needsSignIn: false, running, serviceInstallPending: b.config.fleetAuthPending === true && !result.restarted }, null, 2));
2157
2180
  return;
@@ -2446,6 +2469,7 @@ async function adoptRemoteBotLocked(input) {
2446
2469
  systemPrompt: fleetBotSystemPrompt(name, config),
2447
2470
  provider: fleetProviderForRuntime(config.runtime, config.model),
2448
2471
  ownerCompanies: await fleetOwnerCompanies(),
2472
+ ...(config.autoApprove === false ? { autoApprove: false } : {}),
2449
2473
  ...(input.preserveFirstStart
2450
2474
  ? {
2451
2475
  firstStart: {
@@ -41,6 +41,7 @@ export interface FleetLocalProfile {
41
41
  transport?: string;
42
42
  };
43
43
  ownerCompanies?: string[];
44
+ autoApprove?: boolean;
44
45
  hqRoot: string;
45
46
  profileDir: string;
46
47
  runtime: {
@@ -60,7 +61,7 @@ export interface FleetLocalInstallDeps {
60
61
  hqReleaseLinks?: (release: FleetLocalReleasePin) => Promise<Map<string, string> | null>;
61
62
  now?: () => Date;
62
63
  release?: FleetLocalReleasePin;
63
- publicKey?: string;
64
+ publicKey?: string | readonly string[];
64
65
  }
65
66
  export interface FleetLocalProfileInput {
66
67
  agentUid: string;
@@ -79,6 +80,8 @@ export interface FleetLocalProfileInput {
79
80
  };
80
81
  /** Companies the owner's own turns may use; other senders get none of them. */
81
82
  ownerCompanies?: string[];
83
+ /** False keeps the runtime's command approval prompts (`hq bot create --no-auto-approve`). */
84
+ autoApprove?: boolean;
82
85
  }
83
86
  /** The model the HQ Fleet Runtime asks for when a Claude bot names none. */
84
87
  export declare const FLEET_CLAUDE_DEFAULT_MODEL = "claude-opus-5-5";
@@ -162,9 +165,16 @@ export interface FleetLocalRemovalResult {
162
165
  credentialsRemoved: number;
163
166
  }
164
167
  export declare const HQ_FLEET_LOCAL_RELEASE: FleetLocalReleasePin;
168
+ /** hq-fleet-local trust/release-public.pem (rotated 2026-10-30; signed v0.4.17). */
165
169
  export declare const HQ_FLEET_LOCAL_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEASJoapvo0WMTg+Wu3Jvt0nzlXLOBEr+G1aJRi0JkEb4A=\n-----END PUBLIC KEY-----\n";
170
+ /** hq-fleet-local trust/release-public-v2.pem (added 2026-11-01; signs v0.4.18). */
171
+ export declare const HQ_FLEET_LOCAL_PUBLIC_KEY_V2 = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAmgUi7pKTJUmf3yzwcuH65wSvn1NyJrDfZHZUX08sGN8=\n-----END PUBLIC KEY-----\n";
172
+ /** Every key a release manifest may be signed with. Any one must verify it. */
173
+ export declare const HQ_FLEET_LOCAL_PUBLIC_KEYS: readonly string[];
174
+ /** True when any trusted Ed25519 key verifies the manifest signature. */
175
+ export declare function fleetLocalManifestSignatureValid(manifest: Buffer, signature: Buffer, publicKey?: string | readonly string[]): boolean;
166
176
  export declare function fleetHome(home?: string, env?: NodeJS.ProcessEnv): string;
167
- export declare function verifyFleetLocalRelease(files: FleetLocalReleaseFiles, release?: FleetLocalReleasePin, publicKey?: string): void;
177
+ export declare function verifyFleetLocalRelease(files: FleetLocalReleaseFiles, release?: FleetLocalReleasePin, publicKey?: string | readonly string[]): void;
168
178
  /**
169
179
  * Warning text when the signed manifest asks for a newer hq CLI than this
170
180
  * one, or null. Advisory only: the pinned runtime is still installed, since
@@ -181,6 +191,15 @@ export declare const FLEET_LOCAL_RUNTIME_DOWNLOAD_TIMEOUT_MS: number;
181
191
  export declare function installPinnedFleetLocalDistribution(deps?: FleetLocalInstallDeps): Promise<FleetLocalInstallation>;
182
192
  /** The process id the bot's gateway recorded, when its file reads; the caller checks whether it is alive. */
183
193
  export declare function fleetLocalGatewayPid(agentUid: string, deps?: Pick<FleetLocalControlDeps, "fleetHome">): number | null;
194
+ /**
195
+ * Whether the bot's HQ Fleet gateway is installed as a login service. The
196
+ * runtime registers it with launchd as `ai.hq-fleet.gateway-<profile>`; null
197
+ * on platforms where hq-cli does not know the service layout.
198
+ */
199
+ export declare function fleetLocalServiceInstalled(agentUid: string, deps?: {
200
+ platform?: NodeJS.Platform;
201
+ home?: string;
202
+ }): boolean | null;
184
203
  export declare function fleetLocalProfileNeedsAdoption(agentUid: string, deps?: Pick<FleetLocalControlDeps, "fleetHome" | "platform"> & {
185
204
  release?: FleetLocalReleasePin;
186
205
  }): boolean;
@@ -78,20 +78,42 @@ export function sharedRuntimeLayout(root, id, platform) {
78
78
  }
79
79
  export const HQ_FLEET_LOCAL_RELEASE = {
80
80
  repository: "indigoai-us/hq-fleet-local",
81
- tag: "v0.4.17",
82
- version: "0.4.17",
83
- artifact: "hq-fleet-local-0.4.17.zip",
84
- artifactSha256: "d2c5c49d4ef47da6003f4c2f3be7dcd2d8a287523c8577dfe0239e6e0705e4b4",
81
+ tag: "v0.4.18",
82
+ version: "0.4.18",
83
+ artifact: "hq-fleet-local-0.4.18.zip",
84
+ artifactSha256: "194ae7da24d02205061b18172a84c972754bd0607ed3b4c32af7ac10d73904a7",
85
85
  runtimeArtifact: "hq-fleet-runtime-abccac13bd9e.tar",
86
86
  runtimeArtifactSha256: "29bcfe718ad7fbd3636afd00623772f2a25bc84c4f6f10d8d57f38e549d528d5",
87
- manifestSha256: "63663ffe8c6bef4f2aefa587d20431156cab4170a0f23533cdab7aab444a7d35",
88
- signatureSha256: "bb4af0b453ae03fdbc18e6335ca23e2f0de42bd9e322dba7c4f31d0c8dc08aa7",
87
+ manifestSha256: "ce14449198a0f5d8558ecc617c19551b7a2a31dba5bd563501a93d2ee58e5ff0",
88
+ signatureSha256: "ca7dac6bcc2d12f6e304d1940e7b00243f9c306e7f0a72fd4aa236b7778d3a93",
89
89
  runtimeCommit: "abccac13bd9ea593b24c8cd9c0ba28d5c6050cc9",
90
90
  };
91
+ /** hq-fleet-local trust/release-public.pem (rotated 2026-10-30; signed v0.4.17). */
91
92
  export const HQ_FLEET_LOCAL_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
92
93
  MCowBQYDK2VwAyEASJoapvo0WMTg+Wu3Jvt0nzlXLOBEr+G1aJRi0JkEb4A=
93
94
  -----END PUBLIC KEY-----
94
95
  `;
96
+ /** hq-fleet-local trust/release-public-v2.pem (added 2026-11-01; signs v0.4.18). */
97
+ export const HQ_FLEET_LOCAL_PUBLIC_KEY_V2 = `-----BEGIN PUBLIC KEY-----
98
+ MCowBQYDK2VwAyEAmgUi7pKTJUmf3yzwcuH65wSvn1NyJrDfZHZUX08sGN8=
99
+ -----END PUBLIC KEY-----
100
+ `;
101
+ /** Every key a release manifest may be signed with. Any one must verify it. */
102
+ export const HQ_FLEET_LOCAL_PUBLIC_KEYS = [HQ_FLEET_LOCAL_PUBLIC_KEY, HQ_FLEET_LOCAL_PUBLIC_KEY_V2];
103
+ function publicKeyList(publicKey) {
104
+ return typeof publicKey === "string" ? [publicKey] : publicKey;
105
+ }
106
+ /** True when any trusted Ed25519 key verifies the manifest signature. */
107
+ export function fleetLocalManifestSignatureValid(manifest, signature, publicKey = HQ_FLEET_LOCAL_PUBLIC_KEYS) {
108
+ return publicKeyList(publicKey).some((key) => {
109
+ try {
110
+ return crypto.verify(null, manifest, crypto.createPublicKey(key), signature);
111
+ }
112
+ catch {
113
+ return false;
114
+ }
115
+ });
116
+ }
95
117
  function digest(value) {
96
118
  return crypto.createHash("sha256").update(value).digest("hex");
97
119
  }
@@ -110,7 +132,7 @@ function engineMetadata(release) {
110
132
  export function fleetHome(home = os.homedir(), env = process.env) {
111
133
  return env.HQ_FLEET_HOME?.trim() || path.join(home, ".hq-fleet");
112
134
  }
113
- export function verifyFleetLocalRelease(files, release = HQ_FLEET_LOCAL_RELEASE, publicKey = HQ_FLEET_LOCAL_PUBLIC_KEY) {
135
+ export function verifyFleetLocalRelease(files, release = HQ_FLEET_LOCAL_RELEASE, publicKey = HQ_FLEET_LOCAL_PUBLIC_KEYS) {
114
136
  if (digest(files.archive) !== release.artifactSha256) {
115
137
  throw expectedError(`HQ Fleet Local ${release.version} archive hash did not match the release pin.`);
116
138
  }
@@ -142,7 +164,7 @@ export function verifyFleetLocalRelease(files, release = HQ_FLEET_LOCAL_RELEASE,
142
164
  if (signature.algorithm !== "ed25519" || typeof signature.signature !== "string") {
143
165
  throw expectedError(`HQ Fleet Local ${release.version} signature record is invalid.`);
144
166
  }
145
- const valid = crypto.verify(null, files.manifest, crypto.createPublicKey(publicKey), Buffer.from(signature.signature, "base64"));
167
+ const valid = fleetLocalManifestSignatureValid(files.manifest, Buffer.from(signature.signature, "base64"), publicKey);
146
168
  if (!valid) {
147
169
  throw expectedError(`HQ Fleet Local ${release.version} manifest signature is invalid.`);
148
170
  }
@@ -326,7 +348,7 @@ function installFiles(target, files, release, publicKey) {
326
348
  fs.writeFileSync(path.join(staging, release.runtimeArtifact), files.runtimeArchive, { mode: 0o644 });
327
349
  fs.writeFileSync(path.join(staging, "release-manifest.json"), files.manifest, { mode: 0o644 });
328
350
  fs.writeFileSync(path.join(staging, "release-manifest.sig"), files.signature, { mode: 0o644 });
329
- fs.writeFileSync(path.join(staging, "release-public.pem"), publicKey, { mode: 0o644 });
351
+ fs.writeFileSync(path.join(staging, "release-public.pem"), publicKeyList(publicKey).join(""), { mode: 0o644 });
330
352
  fs.writeFileSync(path.join(staging, "install.json"), `${JSON.stringify({ schemaVersion: 1, release, engine: engineMetadata(release) }, null, 2)}\n`, { mode: 0o644 });
331
353
  if (fs.existsSync(target))
332
354
  fs.renameSync(target, backup);
@@ -366,7 +388,7 @@ function cachedRuntimeArchive(root, release) {
366
388
  }
367
389
  export async function installPinnedFleetLocalDistribution(deps = {}) {
368
390
  const release = deps.release ?? HQ_FLEET_LOCAL_RELEASE;
369
- const publicKey = deps.publicKey ?? HQ_FLEET_LOCAL_PUBLIC_KEY;
391
+ const publicKey = deps.publicKey ?? HQ_FLEET_LOCAL_PUBLIC_KEYS;
370
392
  const root = deps.fleetHome ?? fleetHome();
371
393
  const dir = path.join(root, "distributions", "hq-fleet-local", release.version);
372
394
  const current = installedFiles(dir, release);
@@ -513,7 +535,8 @@ function fleetLocalExecutables(agentUid, deps) {
513
535
  function runJson(run, executable, args, options, label) {
514
536
  let output;
515
537
  try {
516
- output = run(executable, args, { windowsHide: true, encoding: "utf8", ...options });
538
+ // windowsHide last: a helper, uv or Python child never opens a console window on Windows.
539
+ output = run(executable, args, { encoding: "utf8", ...options, windowsHide: true });
517
540
  }
518
541
  catch {
519
542
  throw expectedError(`HQ Fleet Local ${label} failed.`);
@@ -541,6 +564,31 @@ export function fleetLocalGatewayPid(agentUid, deps = {}) {
541
564
  return null;
542
565
  }
543
566
  }
567
+ /** The first hq-fleet-local release whose profile schema accepts `autoApprove`; older ones reject unknown fields. */
568
+ const AUTO_APPROVE_RELEASE = [0, 4, 18];
569
+ function releaseKnowsAutoApprove(release) {
570
+ const parts = release.version.split(".").map((part) => Number.parseInt(part, 10));
571
+ for (let i = 0; i < AUTO_APPROVE_RELEASE.length; i += 1) {
572
+ const have = Number.isFinite(parts[i]) ? parts[i] : 0;
573
+ if (have !== AUTO_APPROVE_RELEASE[i])
574
+ return have > AUTO_APPROVE_RELEASE[i];
575
+ }
576
+ return true;
577
+ }
578
+ /**
579
+ * Whether the bot's HQ Fleet gateway is installed as a login service. The
580
+ * runtime registers it with launchd as `ai.hq-fleet.gateway-<profile>`; null
581
+ * on platforms where hq-cli does not know the service layout.
582
+ */
583
+ export function fleetLocalServiceInstalled(agentUid, deps = {}) {
584
+ if (!/^agt_[A-Za-z0-9_-]+$/.test(agentUid))
585
+ return null;
586
+ const platform = deps.platform ?? process.platform;
587
+ if (platform !== "darwin")
588
+ return null;
589
+ const home = deps.home ?? os.homedir();
590
+ return fs.existsSync(path.join(home, "Library", "LaunchAgents", `ai.hq-fleet.gateway-${profileName(agentUid)}.plist`));
591
+ }
544
592
  export function fleetLocalProfileNeedsAdoption(agentUid, deps = {}) {
545
593
  if (!/^agt_[A-Za-z0-9_-]+$/.test(agentUid))
546
594
  return true;
@@ -842,6 +890,7 @@ export function writeFleetLocalProfile(input, deps = {}) {
842
890
  botId: input.agentUid,
843
891
  ownerUid: input.ownerUid,
844
892
  ...(input.ownerCompanies?.length ? { ownerCompanies: [...new Set(input.ownerCompanies)].sort() } : {}),
893
+ ...(input.autoApprove === false && releaseKnowsAutoApprove(input.installation.release) ? { autoApprove: false } : {}),
845
894
  provider: input.provider ?? { id: "custom:personal-chatgpt", model: "gpt-5.6-sol" },
846
895
  hqRoot: path.resolve(input.hqRoot),
847
896
  profileDir: path.resolve(dir),
@@ -958,10 +1007,11 @@ function checkedRun(run, executable, args, options, label) {
958
1007
  const stdio = options.stdio === "ignore" ? ["ignore", "ignore", "pipe"] : options.stdio;
959
1008
  try {
960
1009
  run(executable, args, {
961
- windowsHide: true,
962
1010
  maxBuffer: 16 * 1024 * 1024,
963
1011
  ...options,
964
1012
  ...(stdio === undefined ? {} : { stdio }),
1013
+ // Last, so no caller can turn it off: no console window flashes on Windows.
1014
+ windowsHide: true,
965
1015
  });
966
1016
  }
967
1017
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.345.190",
3
+ "version": "5.345.191",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -114,11 +114,11 @@
114
114
  "*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}": "eslint --cache --no-error-on-unmatched-pattern"
115
115
  },
116
116
  "optionalDependencies": {
117
- "@indigoai-us/hq-hook-win32-x64": "5.345.190",
118
- "@indigoai-us/hq-hook-win32-arm64": "5.345.190",
119
- "@indigoai-us/hq-hook-darwin-x64": "5.345.190",
120
- "@indigoai-us/hq-hook-darwin-arm64": "5.345.190",
121
- "@indigoai-us/hq-hook-linux-x64": "5.345.190",
122
- "@indigoai-us/hq-hook-linux-arm64": "5.345.190"
117
+ "@indigoai-us/hq-hook-win32-x64": "5.345.191",
118
+ "@indigoai-us/hq-hook-win32-arm64": "5.345.191",
119
+ "@indigoai-us/hq-hook-darwin-x64": "5.345.191",
120
+ "@indigoai-us/hq-hook-darwin-arm64": "5.345.191",
121
+ "@indigoai-us/hq-hook-linux-x64": "5.345.191",
122
+ "@indigoai-us/hq-hook-linux-arm64": "5.345.191"
123
123
  }
124
124
  }