@indigoai-us/hq-cli 5.345.59 → 5.345.60

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,11 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.345.60] — 2026-10-09
6
+ - The daemon now keeps an `hqd.enabled` marker beside the hqd socket while HQ Anywhere is enabled for this person and removes it when the gate turns off. The hq-core hook shim fails closed only where that marker or the socket exists, so machines without HQ Anywhere are no longer blocked, and a stopped daemon on an HQ Anywhere machine still blocks company writes. The marker is published before HQ Anywhere revocation is lifted and withdrawn after it is set. It is written only into a private, non-symlinked socket directory and never through a pre-existing symlink or FIFO. If it cannot be written safely, revocation stays in place. A failed gate lookup or a daemon shutdown leaves the marker as it was.
7
+
8
+ - Improved the Windows grant command parity check by avoiding a redundant jq wrapper process while retaining exact output checks.
9
+
5
10
  ## [5.345.59] — 2026-10-09
6
11
  - Keep conduct-mode SessionStart injection out of unattended sessions unless `HQ_AUTO_CONDUCT` explicitly forces it on.
7
12
  - Back off the external agent-kit inbox poller from 15 seconds to 30 seconds after repeated empty polls, reset to 15 seconds when a message is mirrored, and stop promptly on SIGTERM or SIGINT with a 10-second deadline for stalled shutdowns. The default-on `agents.inbox-poll-backoff-agent-kit` hq-flags kill switch preserves the regular cadence when disabled; installed services retain a configured `HQ_FLAGS_API_URL`.
@@ -29,8 +29,15 @@ export interface DaemonLockConflictOutcome {
29
29
  export declare function daemonLockConflictOutcome(lock: DaemonLockResult, anywhereEnabled: boolean): DaemonLockConflictOutcome | null;
30
30
  /** hqd is available on every platform while Anywhere is enabled; its listener fails closed if setup is unsafe. */
31
31
  export declare function shouldStartHqd(anywhereEnabled: boolean): boolean;
32
- /** Resolve the supervisor's runtime-access decision and reflect it in the local wrapper marker. */
33
- export declare function refreshHqdRuntimeDecision(readDecision: () => Promise<boolean>, home: string, env: NodeJS.ProcessEnv, onMarkerError?: (error: unknown) => void): Promise<boolean>;
32
+ /**
33
+ * Resolve the supervisor's runtime-access decision and reflect it in the local
34
+ * markers: the wrapper revocation marker and the hook shims' hqd enabled marker.
35
+ * Turning on publishes the enabled marker before lifting revocation, and turning
36
+ * off revokes before withdrawing it, so no hook ever sees revocation lifted while
37
+ * the shim would stay inert. If the enabled marker cannot be published,
38
+ * revocation stays in place.
39
+ */
40
+ export declare function refreshHqdRuntimeDecision(readDecision: () => Promise<boolean>, home: string, env: NodeJS.ProcessEnv, onMarkerError?: (error: unknown) => void, syncEnabledMarker?: (enabled: boolean) => void): Promise<boolean>;
34
41
  /** Route an updater request through the daemon's existing graceful handoff. */
35
42
  export declare function handoffForPackageUpdateRequest(requested: boolean, restartDaemon: () => void): void;
36
43
  export interface RunDaemonOptions {
@@ -35,7 +35,7 @@ import { daemonRootRefusal } from "./install.js";
35
35
  import { detectMachineType } from "./machine-type.js";
36
36
  import { appendPrivate, daemonPaths, ensurePrivateDir, openPrivateAppend } from "./paths.js";
37
37
  import { hqdSocketPath } from "./protocol.js";
38
- import { startHqdServer } from "./server.js";
38
+ import { startHqdServer, syncHqdEnabledMarker } from "./server.js";
39
39
  import { startHqdRuntimeListener } from "./anywhere-listener.js";
40
40
  import { heartbeatFile, startHeartbeat } from "./heartbeat.js";
41
41
  import { createProtocolTail, ensureProtocolFifo, openProtocolWriter } from "./protocol-tail.js";
@@ -94,15 +94,39 @@ export function daemonLockConflictOutcome(lock, anywhereEnabled) {
94
94
  export function shouldStartHqd(anywhereEnabled) {
95
95
  return anywhereEnabled;
96
96
  }
97
- /** Resolve the supervisor's runtime-access decision and reflect it in the local wrapper marker. */
98
- export async function refreshHqdRuntimeDecision(readDecision, home, env, onMarkerError = () => { }) {
97
+ /**
98
+ * Resolve the supervisor's runtime-access decision and reflect it in the local
99
+ * markers: the wrapper revocation marker and the hook shims' hqd enabled marker.
100
+ * Turning on publishes the enabled marker before lifting revocation, and turning
101
+ * off revokes before withdrawing it, so no hook ever sees revocation lifted while
102
+ * the shim would stay inert. If the enabled marker cannot be published,
103
+ * revocation stays in place.
104
+ */
105
+ export async function refreshHqdRuntimeDecision(readDecision, home, env, onMarkerError = () => { }, syncEnabledMarker = () => { }) {
99
106
  const enabled = await readDecision();
107
+ if (enabled) {
108
+ try {
109
+ syncEnabledMarker(true);
110
+ }
111
+ catch (error) {
112
+ onMarkerError(error);
113
+ return enabled;
114
+ }
115
+ }
100
116
  try {
101
117
  updateAnywhereRuntimeRevocationMarker(enabled, home, env);
102
118
  }
103
119
  catch (error) {
104
120
  onMarkerError(error);
105
121
  }
122
+ if (!enabled) {
123
+ try {
124
+ syncEnabledMarker(false);
125
+ }
126
+ catch (error) {
127
+ onMarkerError(error);
128
+ }
129
+ }
106
130
  return enabled;
107
131
  }
108
132
  /** Route an updater request through the daemon's existing graceful handoff. */
@@ -915,7 +939,7 @@ export async function runDaemon(opts) {
915
939
  await refreshFlagRegistrySnapshot(5_000);
916
940
  },
917
941
  readSetting: readPersonSetting,
918
- })), home, env, () => log("warn", "hqd runtime revocation marker update failed")),
942
+ })), home, env, (error) => log("warn", `hqd runtime marker update failed: ${error.message ?? String(error)}`), (enabled) => syncHqdEnabledMarker(hqdSocketPath(home, env), enabled)),
919
943
  startServer: async () => {
920
944
  const startConfig = readDaemonConfig(paths.configPath);
921
945
  return startHqdServer({
@@ -68,5 +68,16 @@ export declare function createResolver(registryPath: string, options?: {
68
68
  readRegistry?: typeof readRegistry;
69
69
  readCompaniesManifest?: typeof readCompaniesManifestMap;
70
70
  }): (cwd: string) => ResolveResult;
71
+ /** Marker beside the hqd socket; hook shims fail closed only where it (or the socket) exists. */
72
+ export declare const HQD_ENABLED_MARKER = "hqd.enabled";
73
+ /**
74
+ * Keep `<socket dir>/hqd.enabled` in step with the hqd gate. The POSIX hook
75
+ * shim reads it to tell "hqd should answer" from "hqd is not set up here".
76
+ * The directory gets the same trust checks as the socket itself, and the
77
+ * marker is opened without following symlinks or blocking on a FIFO, so a
78
+ * pre-planted entry is refused instead of written through. Throws when the
79
+ * marker cannot be published safely. Windows named pipes have no marker.
80
+ */
81
+ export declare function syncHqdEnabledMarker(socketPath: string, enabled: boolean): void;
71
82
  export declare function startHqdServer(opts: HqdServerOptions): Promise<HqdServer>;
72
83
  //# sourceMappingURL=server.d.ts.map
@@ -305,6 +305,57 @@ function prepareSocketDirectory(directory, lstat, uid) {
305
305
  throw new Error(`${directory}: newly created socket directory mode is not 0700 after chmod`);
306
306
  }
307
307
  }
308
+ /** Marker beside the hqd socket; hook shims fail closed only where it (or the socket) exists. */
309
+ export const HQD_ENABLED_MARKER = "hqd.enabled";
310
+ /**
311
+ * Keep `<socket dir>/hqd.enabled` in step with the hqd gate. The POSIX hook
312
+ * shim reads it to tell "hqd should answer" from "hqd is not set up here".
313
+ * The directory gets the same trust checks as the socket itself, and the
314
+ * marker is opened without following symlinks or blocking on a FIFO, so a
315
+ * pre-planted entry is refused instead of written through. Throws when the
316
+ * marker cannot be published safely. Windows named pipes have no marker.
317
+ */
318
+ export function syncHqdEnabledMarker(socketPath, enabled) {
319
+ if (socketPath.startsWith("\\\\.\\pipe\\"))
320
+ return;
321
+ const directory = path.dirname(path.resolve(socketPath));
322
+ const marker = path.join(directory, HQD_ENABLED_MARKER);
323
+ const lstat = (candidate) => fs.lstatSync(candidate);
324
+ const uid = process.getuid?.();
325
+ if (uid === undefined)
326
+ throw new Error(`${directory}: cannot verify current uid for the hqd enabled marker`);
327
+ if (!enabled) {
328
+ assertTrustedSocketPathComponents(directory, lstat, uid);
329
+ try {
330
+ fs.unlinkSync(marker);
331
+ }
332
+ catch (err) {
333
+ if (err.code !== "ENOENT")
334
+ throw err;
335
+ }
336
+ return;
337
+ }
338
+ prepareSocketDirectory(directory, lstat, uid);
339
+ const { O_WRONLY, O_CREAT, O_NOFOLLOW, O_NONBLOCK } = fs.constants;
340
+ let fd;
341
+ try {
342
+ fd = fs.openSync(marker, O_WRONLY | O_CREAT | O_NOFOLLOW | O_NONBLOCK, 0o600);
343
+ }
344
+ catch (err) {
345
+ const detail = err instanceof Error ? err.message : String(err);
346
+ throw new Error(`${marker}: cannot open hqd enabled marker safely: ${detail}`);
347
+ }
348
+ try {
349
+ const stat = fs.fstatSync(fd);
350
+ if (!stat.isFile())
351
+ throw new Error(`${marker}: existing path is not a regular file`);
352
+ if (stat.uid !== uid)
353
+ throw new Error(`${marker}: existing marker is not owned by the current uid`);
354
+ }
355
+ finally {
356
+ fs.closeSync(fd);
357
+ }
358
+ }
308
359
  function verifyUserOnlySocket(socketPath, lstat, uid) {
309
360
  assertTrustedSocketPathComponents(path.dirname(socketPath), lstat, uid);
310
361
  const before = socketLstatOrMissing(socketPath, lstat);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.345.59",
3
+ "version": "5.345.60",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {