@indigoai-us/hq-cli 5.345.201 → 5.345.203

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 (54) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/command-catalog.generated.d.ts +24 -0
  3. package/dist/command-catalog.generated.js +26 -0
  4. package/dist/commands/capacity.js +10 -2
  5. package/dist/commands/daemon.d.ts +2 -1
  6. package/dist/commands/daemon.js +225 -4
  7. package/dist/commands/integrations-core.d.ts +3 -0
  8. package/dist/commands/integrations-core.js +11 -0
  9. package/dist/commands/integrations-manage.js +2 -2
  10. package/dist/commands/integrations.js +5 -5
  11. package/dist/commands/sync-core.js +2 -1
  12. package/dist/lib/bot/executive-settings.d.ts +1 -1
  13. package/dist/lib/bot/executive-settings.js +16 -6
  14. package/dist/lib/bot/jobs/meeting-detect.js +2 -2
  15. package/dist/lib/bot/jobs/meeting-rider.js +2 -2
  16. package/dist/lib/bot/jobs/signal-to-lane.d.ts +3 -2
  17. package/dist/lib/bot/jobs/signal-to-lane.js +31 -8
  18. package/dist/lib/bot/run.js +3 -3
  19. package/dist/lib/capacity/codex-availability.d.ts +6 -0
  20. package/dist/lib/capacity/codex-availability.js +33 -0
  21. package/dist/lib/capacity/ledger.d.ts +0 -11
  22. package/dist/lib/capacity/ledger.js +30 -3
  23. package/dist/lib/capacity/route.js +11 -5
  24. package/dist/lib/daemon/catalog.d.ts +3 -0
  25. package/dist/lib/daemon/catalog.js +15 -2
  26. package/dist/lib/daemon/handoff.d.ts +2 -0
  27. package/dist/lib/daemon/handoff.js +9 -2
  28. package/dist/lib/daemon/install.d.ts +99 -0
  29. package/dist/lib/daemon/install.js +316 -7
  30. package/dist/lib/daemon/migrate.d.ts +3 -1
  31. package/dist/lib/daemon/migrate.js +14 -2
  32. package/dist/lib/daemon/plist-refresh.d.ts +34 -0
  33. package/dist/lib/daemon/plist-refresh.js +62 -0
  34. package/dist/lib/daemon/run.js +25 -0
  35. package/dist/lib/daemon/selfcheck.d.ts +71 -0
  36. package/dist/lib/daemon/selfcheck.js +150 -0
  37. package/dist/lib/daemon/supervisor.d.ts +31 -0
  38. package/dist/lib/daemon/supervisor.js +130 -6
  39. package/dist/lib/daemon/unit-holds.js +2 -1
  40. package/dist/lib/service-manager/launchd.d.ts +25 -0
  41. package/dist/lib/service-manager/launchd.js +51 -3
  42. package/dist/lib/service-manager/migration.d.ts +14 -0
  43. package/dist/lib/service-manager/migration.js +34 -0
  44. package/dist/lib/service-manager/systemd.d.ts +13 -0
  45. package/dist/lib/service-manager/systemd.js +19 -0
  46. package/dist/lib/service-manager/types.d.ts +8 -0
  47. package/dist/lib/sync-core/reconcile.d.ts +4 -1
  48. package/dist/lib/sync-core/reconcile.js +12 -2
  49. package/dist/lib/sync-core/release.d.ts +23 -0
  50. package/dist/lib/sync-core/release.js +39 -0
  51. package/dist/main.js +3 -3
  52. package/dist/utils/cli-network-lifecycle.d.ts +9 -0
  53. package/dist/utils/cli-network-lifecycle.js +25 -1
  54. package/package.json +9 -8
@@ -21,6 +21,14 @@ export interface ServiceSpec {
21
21
  restartSec: number;
22
22
  /** systemd unit file name; defaults to `hq-agent-<name>.service`. */
23
23
  unitName?: string;
24
+ /**
25
+ * macOS: run the job directly under node, without the HQ.app launcher and
26
+ * without AssociatedBundleIdentifiers, so launchd loads it at login whether
27
+ * or not the app is installed, enabled or allowed. The daemon sets this:
28
+ * on 2026-11-03 launchd skipped both app-associated agents at login and the
29
+ * daemon stayed down for 16 hours.
30
+ */
31
+ independentOfApp?: boolean;
24
32
  }
25
33
  export interface ServiceHostPaths {
26
34
  home: string;
@@ -1,5 +1,5 @@
1
1
  import { type SyncCoreVault } from "./engine-home.js";
2
- import { type SyncCoreBinaryInstall } from "./release.js";
2
+ import { type SyncCoreBinaryInstall, type SyncCorePin } from "./release.js";
3
3
  import { type SyncCoreRolloutState } from "./rollout-state.js";
4
4
  export interface SyncCoreMembership {
5
5
  companyUid: string;
@@ -14,7 +14,10 @@ export interface SyncCoreReconcileDeps {
14
14
  resolvePersonUid: () => Promise<string | null>;
15
15
  readRolloutFlag: (companyUid: string) => Promise<boolean>;
16
16
  ensureBinary: (release: string) => Promise<SyncCoreBinaryInstall>;
17
+ /** A fixed release (tests). Otherwise readPin decides, then SYNC_CORE_RELEASE. */
17
18
  release?: string;
19
+ /** Reads the server-side engine pin; never throws (see readSyncCorePin). */
20
+ readPin?: () => Promise<SyncCorePin>;
18
21
  now?: () => Date;
19
22
  log?: (line: string) => void;
20
23
  }
@@ -3,7 +3,7 @@ import * as path from "node:path";
3
3
  import { readCompaniesManifestMap } from "../work-context/repo-remote.js";
4
4
  import { readVaultSyncSwitches, writeVaultSyncSwitches } from "../vault-sync-switches.js";
5
5
  import { clearEngineStatus, engineFirstCycle, isSyncCoreVaultUid, removeEngineOwnership, syncCoreEngineHome, writeEngineHome, } from "./engine-home.js";
6
- import { SYNC_CORE_FULL_RELEASE, SYNC_CORE_RELEASE, fullModeAllowed, installedSyncCoreRelease, syncCoreBinaryPath, } from "./release.js";
6
+ import { SYNC_CORE_FULL_RELEASE, SYNC_CORE_RELEASE, compareSyncCoreReleases, fullModeAllowed, installedSyncCoreRelease, syncCoreBinaryPath, } from "./release.js";
7
7
  import { readSyncCoreSkips } from "./skip.js";
8
8
  import { SYNC_RUST_ENGINE_ROLLOUT_FLAG } from "./flag.js";
9
9
  import { readSyncCoreRolloutState, syncCoreRolloutStatePath, writeSyncCoreRolloutState, } from "./rollout-state.js";
@@ -65,8 +65,8 @@ function ownedVaultsWithoutState(home, hqRoot) {
65
65
  export async function reconcileSyncCore(deps) {
66
66
  const now = deps.now ?? (() => new Date());
67
67
  const log = deps.log ?? (() => { });
68
- const release = deps.release ?? SYNC_CORE_RELEASE;
69
68
  const warnings = [];
69
+ let release = deps.release ?? SYNC_CORE_RELEASE;
70
70
  const previous = readSyncCoreRolloutState(deps.home);
71
71
  const previousVaults = previous?.vaults ?? ownedVaultsWithoutState(deps.home, deps.hqRoot);
72
72
  const previousByUid = new Map(previousVaults.map((vault) => [vault.uid, vault]));
@@ -117,6 +117,16 @@ export async function reconcileSyncCore(deps) {
117
117
  let binaryPath = syncCoreBinaryPath(deps.home);
118
118
  let installedRelease = installedSyncCoreRelease(deps.home);
119
119
  if (desired.length > 0) {
120
+ if (deps.release === undefined && deps.readPin) {
121
+ const pin = await deps.readPin();
122
+ release = pin.release;
123
+ if (pin.warning)
124
+ warnings.push(pin.warning);
125
+ if (pin.source === "server" && installedRelease && compareSyncCoreReleases(pin.release, installedRelease) < 0) {
126
+ // The no-downgrade guard in ensureSyncCoreBinary keeps the newer engine.
127
+ log(`the server pins hq-syncd ${pin.release}, older than the installed ${installedRelease}; keeping ${installedRelease}`);
128
+ }
129
+ }
120
130
  try {
121
131
  const install = await deps.ensureBinary(release);
122
132
  binaryPath = install.binaryPath;
@@ -4,6 +4,29 @@ export declare function compareSyncCoreReleases(a: string, b: string): number;
4
4
  export declare function fullModeAllowed(release: string): boolean;
5
5
  export declare const SYNC_CORE_DOWNLOAD_BASE = "https://indigoai-public.s3.amazonaws.com/hq-sync-core";
6
6
  export declare const SYNC_CORE_DOWNLOAD_BASE_ENV = "HQ_SYNC_CORE_DOWNLOAD_BASE";
7
+ /** The server-side engine pin, published next to the release folders on the download base. */
8
+ export declare const SYNC_CORE_PIN_FILE = "pin.json";
9
+ export interface SyncCorePin {
10
+ /** The engine release machines should run. */
11
+ release: string;
12
+ /** "server": read from pin.json; "built-in": SYNC_CORE_RELEASE, because no server pin could be used. */
13
+ source: "server" | "built-in";
14
+ /** Why the server pin was not used, when it exists but could not be read. A missing pin.json sets none. */
15
+ warning?: string;
16
+ }
17
+ /**
18
+ * The engine release to install, read from `<download base>/pin.json`
19
+ * (`{"release":"v0.1.24"}`), so an engine release reaches machines without an
20
+ * hq-cli release. Falls back to SYNC_CORE_RELEASE when the file is missing,
21
+ * unreadable or malformed. Never throws. Windows stays unsupported: the pin
22
+ * only picks a version, and syncCoreAssetName has no Windows build.
23
+ */
24
+ export declare function readSyncCorePin(opts?: {
25
+ env?: NodeJS.ProcessEnv;
26
+ baseUrl?: string;
27
+ fetch?: typeof fetch;
28
+ timeoutMs?: number;
29
+ }): Promise<SyncCorePin>;
7
30
  export declare function syncCoreBinaryPath(home?: string): string;
8
31
  export declare function syncCoreReleaseMarkerPath(home?: string): string;
9
32
  export declare function syncCoreAssetName(release: string, platform: NodeJS.Platform, arch: string): string | null;
@@ -21,6 +21,45 @@ export function fullModeAllowed(release) {
21
21
  export const SYNC_CORE_DOWNLOAD_BASE = "https://indigoai-public.s3.amazonaws.com/hq-sync-core";
22
22
  export const SYNC_CORE_DOWNLOAD_BASE_ENV = "HQ_SYNC_CORE_DOWNLOAD_BASE";
23
23
  const DOWNLOAD_TIMEOUT_MS = 600_000;
24
+ /** The server-side engine pin, published next to the release folders on the download base. */
25
+ export const SYNC_CORE_PIN_FILE = "pin.json";
26
+ const PIN_TIMEOUT_MS = 10_000;
27
+ const RELEASE_PATTERN = /^v\d+\.\d+\.\d+$/;
28
+ /**
29
+ * The engine release to install, read from `<download base>/pin.json`
30
+ * (`{"release":"v0.1.24"}`), so an engine release reaches machines without an
31
+ * hq-cli release. Falls back to SYNC_CORE_RELEASE when the file is missing,
32
+ * unreadable or malformed. Never throws. Windows stays unsupported: the pin
33
+ * only picks a version, and syncCoreAssetName has no Windows build.
34
+ */
35
+ export async function readSyncCorePin(opts = {}) {
36
+ const builtIn = (warning) => ({ release: SYNC_CORE_RELEASE, source: "built-in", ...(warning ? { warning } : {}) });
37
+ const url = `${opts.baseUrl?.replace(/\/+$/, "") ?? syncCoreDownloadBase(opts.env)}/${SYNC_CORE_PIN_FILE}`;
38
+ let response;
39
+ try {
40
+ response = await (opts.fetch ?? fetch)(url, { signal: AbortSignal.timeout(opts.timeoutMs ?? PIN_TIMEOUT_MS) });
41
+ }
42
+ catch (error) {
43
+ return builtIn(`could not read the engine pin ${url}: ${error instanceof Error ? error.message : String(error)}; using built-in ${SYNC_CORE_RELEASE}`);
44
+ }
45
+ // S3 answers 403 for a missing key on a bucket without public listing.
46
+ if (response.status === 404 || response.status === 403)
47
+ return builtIn();
48
+ if (!response.ok)
49
+ return builtIn(`could not read the engine pin ${url}: HTTP ${response.status}; using built-in ${SYNC_CORE_RELEASE}`);
50
+ let parsed;
51
+ try {
52
+ parsed = JSON.parse(await response.text());
53
+ }
54
+ catch {
55
+ return builtIn(`the engine pin ${url} is not JSON; using built-in ${SYNC_CORE_RELEASE}`);
56
+ }
57
+ const release = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed.release : undefined;
58
+ if (typeof release !== "string" || !RELEASE_PATTERN.test(release)) {
59
+ return builtIn(`the engine pin ${url} has no valid "release" (want vX.Y.Z); using built-in ${SYNC_CORE_RELEASE}`);
60
+ }
61
+ return { release, source: "server" };
62
+ }
24
63
  export function syncCoreBinaryPath(home = os.homedir()) {
25
64
  return path.join(home, ".hq", "bin", "hq-syncd");
26
65
  }
package/dist/main.js CHANGED
@@ -61,7 +61,7 @@ import { packageRoot } from "./utils/hq-roots.js";
61
61
  import { classifyModuleNotFound, installTreeTornCaptureContext, installTreeTornStderrLine, isInstallTreeTornError, } from "./utils/install-tree-torn.js";
62
62
  import { isVaultAccessDeniedError, vaultAccessDeniedMessage, } from "./utils/vault-access-denied-error.js";
63
63
  import { fallbackOperatorMessage, unexpectedCliErrorMessage } from "./utils/unexpected-cli-error.js";
64
- import { runGlobalNetworkLifecycle } from "./utils/cli-network-lifecycle.js";
64
+ import { isHqDaemonRunCommand, runGlobalNetworkLifecycle, runSentryInitialization, } from "./utils/cli-network-lifecycle.js";
65
65
  import * as os from "node:os";
66
66
  import { adoptAgentIdentityEnv } from "./lib/agent-kit/adopt-identity.js";
67
67
  import { agentKitPaths } from "./lib/agent-kit/paths.js";
@@ -131,7 +131,7 @@ let activeForegroundNetworkBudget;
131
131
  */
132
132
  const RUNNING_ENTRY_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), "index.js");
133
133
  installStreamErrorBoundary();
134
- runGlobalNetworkLifecycle(process.argv, () => initSentry());
134
+ runSentryInitialization(process.argv, () => initSentry());
135
135
  const program = new Command();
136
136
  program
137
137
  .name("hq")
@@ -169,7 +169,7 @@ program.hook("preAction", async () => {
169
169
  export async function runCli() {
170
170
  const xpcServiceName = process.env.XPC_SERVICE_NAME ?? "";
171
171
  const launchedByHqAgent = xpcServiceName.startsWith("ai.indigo.") || xpcServiceName.startsWith("ai.getindigo.");
172
- const isDaemonEntryPoint = process.argv[2] === "daemon" && process.argv[3] === "run";
172
+ const isDaemonEntryPoint = isHqDaemonRunCommand(process.argv);
173
173
  const isBotHotPath = process.argv[2] === "bot" && process.argv[3] === "run";
174
174
  if (process.platform === "darwin" && typeof process.getuid === "function" &&
175
175
  !launchedByHqAgent && !isDaemonEntryPoint && !isBotHotPath) {
@@ -1,3 +1,12 @@
1
+ export declare function isHqDaemonRunCommand(argv: readonly string[]): boolean;
2
+ /**
3
+ * Resident daemons keep their own bounded local logs. Do not initialize the
4
+ * process-wide Sentry transport in these commands: they can live across many
5
+ * network changes, while the SDK is designed around one short CLI invocation.
6
+ */
7
+ export declare function isLongLivedDaemonCommand(argv: readonly string[]): boolean;
8
+ /** Suppress only Sentry startup; every other global lifecycle operation still runs. */
9
+ export declare function runSentryInitialization<T>(argv: readonly string[], operation: () => T): T | undefined;
1
10
  export declare function isMonitorFastPathCommand(argv: readonly string[]): boolean;
2
11
  /** The commit-msg hook runs `hq fingerprint self --offline`; it must stay fast and offline. */
3
12
  export declare function isOfflineFingerprintCommand(argv: readonly string[]): boolean;
@@ -1,4 +1,27 @@
1
1
  const MONITOR_HIDDEN_NETWORK_BYPASS = new Set(["drain", "wait", "enabled", "_run"]);
2
+ function commandArgv(argv) {
3
+ return argv.slice(2).filter((arg) => arg !== "--verbose");
4
+ }
5
+ export function isHqDaemonRunCommand(argv) {
6
+ const command = commandArgv(argv);
7
+ return command[0] === "daemon" && command[1] === "run";
8
+ }
9
+ /**
10
+ * Resident daemons keep their own bounded local logs. Do not initialize the
11
+ * process-wide Sentry transport in these commands: they can live across many
12
+ * network changes, while the SDK is designed around one short CLI invocation.
13
+ */
14
+ export function isLongLivedDaemonCommand(argv) {
15
+ const command = commandArgv(argv);
16
+ return (isHqDaemonRunCommand(argv) ||
17
+ (command[0] === "mesh" && command[1] === "daemon" && command[2] === "run"));
18
+ }
19
+ /** Suppress only Sentry startup; every other global lifecycle operation still runs. */
20
+ export function runSentryInitialization(argv, operation) {
21
+ if (isLongLivedDaemonCommand(argv))
22
+ return undefined;
23
+ return operation();
24
+ }
2
25
  export function isMonitorFastPathCommand(argv) {
3
26
  return argv[2] === "monitor" && MONITOR_HIDDEN_NETWORK_BYPASS.has(argv[3] ?? "");
4
27
  }
@@ -8,8 +31,9 @@ export function isOfflineFingerprintCommand(argv) {
8
31
  }
9
32
  /** Skip process-wide network lifecycle work for hooks and detached monitor supervisors. */
10
33
  export function runGlobalNetworkLifecycle(argv, operation) {
11
- if (isMonitorFastPathCommand(argv) || isOfflineFingerprintCommand(argv))
34
+ if (isMonitorFastPathCommand(argv) || isOfflineFingerprintCommand(argv)) {
12
35
  return undefined;
36
+ }
13
37
  return operation();
14
38
  }
15
39
  //# sourceMappingURL=cli-network-lifecycle.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.345.201",
3
+ "version": "5.345.203",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -48,7 +48,8 @@
48
48
  "prepare": "husky || true",
49
49
  "check:no-hermes": "bash scripts/check-no-hermes.sh",
50
50
  "prepack": "node scripts/hook-engine-manifest.mjs",
51
- "postpublish": "node scripts/hook-engine-manifest.mjs restore"
51
+ "postpublish": "node scripts/hook-engine-manifest.mjs restore",
52
+ "build:ci": "node scripts/prepare-fixture-privacy-baseline.mjs && pnpm run build"
52
53
  },
53
54
  "dependencies": {
54
55
  "@aws-sdk/client-iot-data-plane": "^3.1096.0",
@@ -116,11 +117,11 @@
116
117
  "*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}": "eslint --cache --no-error-on-unmatched-pattern"
117
118
  },
118
119
  "optionalDependencies": {
119
- "@indigoai-us/hq-hook-win32-x64": "5.345.201",
120
- "@indigoai-us/hq-hook-win32-arm64": "5.345.201",
121
- "@indigoai-us/hq-hook-darwin-x64": "5.345.201",
122
- "@indigoai-us/hq-hook-darwin-arm64": "5.345.201",
123
- "@indigoai-us/hq-hook-linux-x64": "5.345.201",
124
- "@indigoai-us/hq-hook-linux-arm64": "5.345.201"
120
+ "@indigoai-us/hq-hook-win32-x64": "5.345.203",
121
+ "@indigoai-us/hq-hook-win32-arm64": "5.345.203",
122
+ "@indigoai-us/hq-hook-darwin-x64": "5.345.203",
123
+ "@indigoai-us/hq-hook-darwin-arm64": "5.345.203",
124
+ "@indigoai-us/hq-hook-linux-x64": "5.345.203",
125
+ "@indigoai-us/hq-hook-linux-arm64": "5.345.203"
125
126
  }
126
127
  }