@indigoai-us/hq-cli 5.200.1 → 5.201.1

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,26 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.201.1] — 2026-09-25
6
+
7
+ ### Fixed
8
+
9
+ - Outpost grant checks now include child lanes from the owning Outpost. Owner
10
+ records take precedence over duplicate primary records, and each grant
11
+ command reads the owner lane list once.
12
+ - Outpost grant checks re-read owner lanes under the hire lock and use the
13
+ validated Outpost CLI override for the lane-list probe.
14
+
15
+ ## [5.201.0] — 2026-09-25
16
+
17
+ ### Added
18
+
19
+ - `hq daemon pin-hq-cloud <version>` pins the daemon-managed hq-cloud version to an exact semver within the bundled major, including prereleases; `--clear` removes the pin.
20
+
21
+ ### Fixed
22
+
23
+ - The daemon carries `Environment=` assignments from Linux `outpost-sync.service` drop-ins into its sync unit.
24
+
5
25
  ## [5.200.1] — 2026-09-25
6
26
 
7
27
  ### Fixed
@@ -8444,6 +8444,22 @@ export declare const COMMAND_CATALOG: readonly [{
8444
8444
  }];
8445
8445
  readonly options: readonly [];
8446
8446
  readonly subcommands: readonly [];
8447
+ }, {
8448
+ readonly name: "pin-hq-cloud";
8449
+ readonly description: "Pin hq-cloud to an exact version, or clear the pin";
8450
+ readonly aliases: readonly [];
8451
+ readonly hidden: false;
8452
+ readonly usage: "[options] [version]";
8453
+ readonly arguments: readonly [{
8454
+ readonly name: "version";
8455
+ readonly required: false;
8456
+ readonly variadic: false;
8457
+ }];
8458
+ readonly options: readonly [{
8459
+ readonly flags: "--clear";
8460
+ readonly description: "Clear the saved hq-cloud version pin";
8461
+ }];
8462
+ readonly subcommands: readonly [];
8447
8463
  }, {
8448
8464
  readonly name: "migrate";
8449
8465
  readonly description: "List the older HQ background units the daemon would take over; --apply takes them over";
@@ -10918,6 +10918,27 @@ export const COMMAND_CATALOG = [
10918
10918
  "options": [],
10919
10919
  "subcommands": []
10920
10920
  },
10921
+ {
10922
+ "name": "pin-hq-cloud",
10923
+ "description": "Pin hq-cloud to an exact version, or clear the pin",
10924
+ "aliases": [],
10925
+ "hidden": false,
10926
+ "usage": "[options] [version]",
10927
+ "arguments": [
10928
+ {
10929
+ "name": "version",
10930
+ "required": false,
10931
+ "variadic": false
10932
+ }
10933
+ ],
10934
+ "options": [
10935
+ {
10936
+ "flags": "--clear",
10937
+ "description": "Clear the saved hq-cloud version pin"
10938
+ }
10939
+ ],
10940
+ "subcommands": []
10941
+ },
10921
10942
  {
10922
10943
  "name": "migrate",
10923
10944
  "description": "List the older HQ background units the daemon would take over; --apply takes them over",
@@ -16,6 +16,7 @@
16
16
  import * as fs from "node:fs";
17
17
  import * as os from "node:os";
18
18
  import * as path from "node:path";
19
+ import { major as semverMajor, valid as validSemver } from "semver";
19
20
  import { DEFAULT_SERVICES, SERVICE_DESCRIPTIONS, enabledServices, hqCloudUnits } from "../lib/daemon/catalog.js";
20
21
  import { applyServiceToggle, readDaemonConfig, writeDaemonConfig } from "../lib/daemon/config.js";
21
22
  import { daemonUnitInstalled } from "../lib/daemon/handoff.js";
@@ -23,6 +24,7 @@ import { adoptLegacyUnits, readAdoptedUnits, restoreLegacyUnits } from "../lib/d
23
24
  import { ROOT_REFUSAL, SYSTEM_UNIT_DIR, daemonRootRefusal, installDaemonUnit, installScopeFor, uninstallDaemonUnit, } from "../lib/daemon/install.js";
24
25
  import { describeLegacyUnit, findLegacyUnits } from "../lib/daemon/legacy.js";
25
26
  import { isDaemonProcess, runningDaemonPid } from "../lib/daemon/lock.js";
27
+ import { bundledHqCloud } from "../lib/daemon/hq-cloud.js";
26
28
  import { MACHINE_TYPE_MARKER_PATH, detectMachineType, homeFromPasswd, } from "../lib/daemon/machine-type.js";
27
29
  import { daemonPaths } from "../lib/daemon/paths.js";
28
30
  import { writeControlRequest } from "../lib/daemon/requests.js";
@@ -375,6 +377,33 @@ async function runToggle(d, action, services) {
375
377
  d.out(line);
376
378
  d.out("A running daemon picks this up within 15 seconds.");
377
379
  }
380
+ async function runPinHqCloud(d, version, opts) {
381
+ if (opts.clear === Boolean(version)) {
382
+ throw new DaemonCommandError("Pass one exact semver version, or use --clear.");
383
+ }
384
+ const host = currentHost(d);
385
+ const { paths } = host;
386
+ const config = readDaemonConfig(paths.configPath);
387
+ if (opts.clear) {
388
+ delete config.hqCloudVersion;
389
+ writeDaemonConfig(paths.configPath, config);
390
+ handOver(d, host);
391
+ d.out("Cleared the hq-cloud version pin.");
392
+ }
393
+ else {
394
+ if (!version || validSemver(version) !== version) {
395
+ throw new DaemonCommandError(`Invalid hq-cloud version "${version ?? ""}". Pass an exact semver version, including prereleases if needed.`);
396
+ }
397
+ const bundled = bundledHqCloud();
398
+ if (!bundled || semverMajor(bundled.version) !== semverMajor(version)) {
399
+ throw new DaemonCommandError(`The hq-cloud pin must use the same major version as the bundled hq-cloud${bundled ? ` (${semverMajor(bundled.version)})` : ""}.`);
400
+ }
401
+ writeDaemonConfig(paths.configPath, { ...config, hqCloudVersion: version });
402
+ handOver(d, host);
403
+ d.out(`Pinned hq-cloud to ${version}.`);
404
+ }
405
+ d.out("Run `hq daemon trigger updater`; wait for the hq-cloud update or current-version message in `hq daemon logs updater`, then restart `hq daemon restart sync`, `hq daemon restart heartbeat`, and `hq daemon restart session-host`.");
406
+ }
378
407
  async function runMigrate(d, opts) {
379
408
  const units = d.legacyUnits();
380
409
  if (units.length === 0) {
@@ -585,6 +614,11 @@ export function registerDaemonCommand(program, over = {}) {
585
614
  .command("disable <services...>")
586
615
  .description("Turn default services off for this machine")
587
616
  .action(wrap((d, services) => runToggle(d, "disable", services)));
617
+ daemon
618
+ .command("pin-hq-cloud [version]")
619
+ .description("Pin hq-cloud to an exact version, or clear the pin")
620
+ .option("--clear", "Clear the saved hq-cloud version pin")
621
+ .action(wrap((d, version, opts) => runPinHqCloud(d, version, opts)));
588
622
  daemon
589
623
  .command("migrate")
590
624
  .description("List the older HQ background units the daemon would take over; --apply takes them over")
@@ -479,6 +479,14 @@ function laneHireFields(lane, lanes) {
479
479
  hire_summary: hireSummary,
480
480
  };
481
481
  }
482
+ function mergeOwnerLaneHierarchy(supplied, ownerLanes) {
483
+ const byId = new Map();
484
+ for (const lane of supplied)
485
+ byId.set(lane.lane_id, lane);
486
+ for (const lane of ownerLanes)
487
+ byId.set(lane.lane_id, lane);
488
+ return [...byId.values()];
489
+ }
482
490
  function outpostCliOverride(ctx, outpostId) {
483
491
  const envKey = `HQ_LANES_OUTPOST_CLI_${outpostId.toUpperCase().replace(/[^A-Z0-9_]/g, "_")}`;
484
492
  const configured = ctx.env[envKey];
@@ -1031,11 +1039,12 @@ async function loadHostCapacitySnapshots(ctx, hqRoot, config = loadLanesConfig(h
1031
1039
  };
1032
1040
  }
1033
1041
  async function readOutpostLaneRecords(ctx, outpostId) {
1042
+ const hqBinary = outpostCliOverride(ctx, outpostId);
1034
1043
  let result;
1035
1044
  try {
1036
1045
  result = await ctx.dispatchOutpostExec({
1037
1046
  outpostId,
1038
- command: buildOutpostLaneListCommand(),
1047
+ command: buildOutpostLaneListCommand({ hqBinary }),
1039
1048
  timeoutSeconds: CAPACITY_PROBE_TIMEOUT_MS / 1000,
1040
1049
  waitMs: CAPACITY_PROBE_TIMEOUT_MS,
1041
1050
  });
@@ -3312,9 +3321,20 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options) {
3312
3321
  throw new LanesError("outpost_dispatch_failed", `Lane ${lane.lane_id} has no Outpost host to dispatch to`, { lane_id: lane.lane_id });
3313
3322
  }
3314
3323
  const outpostId = lane.host.id;
3315
- const grantContext = action === "grant"
3316
- ? createOutpostHireGrantContext(lane, loadAllLanes(ctx.hqRoot()), await ctx.laneHireGrantsEnabled(lane.company))
3317
- : undefined;
3324
+ let grantContext;
3325
+ let grantHierarchy;
3326
+ if (action === "grant") {
3327
+ const enforcementEnabled = await ctx.laneHireGrantsEnabled(lane.company);
3328
+ let hierarchy = loadAllLanes(ctx.hqRoot());
3329
+ const ownerLanes = await readOutpostLaneRecords(ctx, outpostId);
3330
+ const byId = new Map(hierarchy.map((record) => [record.lane_id, record]));
3331
+ for (const ownerLane of ownerLanes) {
3332
+ byId.set(ownerLane.lane_id, ownerLane);
3333
+ }
3334
+ hierarchy = [...byId.values()];
3335
+ grantHierarchy = hierarchy;
3336
+ grantContext = createOutpostHireGrantContext(lane, hierarchy, enforcementEnabled);
3337
+ }
3318
3338
  let dispatched;
3319
3339
  try {
3320
3340
  dispatched = await ctx.dispatchOutpostExec({
@@ -3407,6 +3427,7 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options) {
3407
3427
  ...remote,
3408
3428
  lane_id: ownerLane.lane_id,
3409
3429
  lane: ownerLane,
3430
+ ...laneHireFields(ownerLane, grantHierarchy ?? []),
3410
3431
  outpost_id: outpostId,
3411
3432
  outpost_command_id: dispatched.commandId,
3412
3433
  outpost_transport: "outposts_exec",
@@ -4518,7 +4539,11 @@ async function applyHireGrant(ctx, laneId, hireGrant, authorization, grantContex
4518
4539
  (!grantContext || !sameHireGrantSenior(current.senior, grantContext.expected_senior))) {
4519
4540
  throw new LanesError("hire_grant_senior_changed", `Lane ${laneId}'s senior changed before the Outpost applied the grant update`, { lane_id: laneId, current_senior: current.senior });
4520
4541
  }
4521
- const hierarchy = grantContext?.hierarchy ?? loadAllLanes(hqRoot);
4542
+ const hierarchy = grantContext
4543
+ ? authorization === "ssm-owner-dispatch"
4544
+ ? mergeOwnerLaneHierarchy(grantContext.hierarchy, loadAllLanes(hqRoot))
4545
+ : grantContext.hierarchy
4546
+ : loadAllLanes(hqRoot);
4522
4547
  if (enforce && current.senior.kind === "lane") {
4523
4548
  if (!current.senior.id) {
4524
4549
  throw new LanesError("hire_grant_senior_invalid", `Lane ${laneId} has a lane senior without an id`, {
@@ -4604,7 +4629,8 @@ async function runGrantOwnerTarget(ctx, laneId, opts) {
4604
4629
  const hireGrant = parseHireGrant(opts.hire);
4605
4630
  const grantContext = parseOutpostHireGrantContext(opts.grantContext, laneId);
4606
4631
  const hqRoot = ctx.hqRoot();
4607
- printGrantResult(hqRoot, await applyHireGrant(ctx, laneId, hireGrant, "ssm-owner-dispatch", grantContext), grantContext.hierarchy);
4632
+ const updated = await applyHireGrant(ctx, laneId, hireGrant, "ssm-owner-dispatch", grantContext);
4633
+ printGrantResult(hqRoot, updated, mergeOwnerLaneHierarchy(grantContext.hierarchy, loadAllLanes(hqRoot)));
4608
4634
  }
4609
4635
  async function runBackfillGrants(ctx, opts) {
4610
4636
  if (opts.apply && opts.dryRun) {
@@ -66,6 +66,8 @@ export interface PlanContext {
66
66
  heartbeatRunnerPath: string | null;
67
67
  /** hq-cloud's `hq-outpost-session-host` bin, when the hq-cloud in use has one. */
68
68
  sessionHostRunnerPath: string | null;
69
+ /** Environment retained from legacy outpost sync systemd drop-ins. */
70
+ legacySyncEnv: Record<string, string>;
69
71
  signedIn: boolean;
70
72
  /** ~/.hq-agent/kit.json exists. */
71
73
  kitConfigured: boolean;
@@ -129,6 +129,7 @@ function planService(service, ctx) {
129
129
  script: ctx.syncRunnerPath,
130
130
  args: ["--companies", "--direction", "both", "--on-conflict", "keep", "--hq-root", ctx.hqRoot ?? "", "--watch", "--event-push"],
131
131
  },
132
+ env: { ...ctx.legacySyncEnv },
132
133
  });
133
134
  unit.blocked = firstBlock([
134
135
  [ctx.desktopAppManagesSync, "the HQ desktop app is handling sync on this machine"],
@@ -10,6 +10,8 @@ export interface DaemonConfig {
10
10
  version: 1;
11
11
  machineType?: MachineType;
12
12
  hqRoot?: string;
13
+ /** Exact hq-cloud package version preferred by this daemon, including prereleases. */
14
+ hqCloudVersion?: string;
13
15
  /** Services turned on here beyond the machine type's defaults. */
14
16
  enable: string[];
15
17
  /** Default services turned off here. */
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import * as fs from "node:fs";
8
8
  import * as path from "node:path";
9
+ import { valid as validSemver } from "semver";
9
10
  import { isServiceName } from "./catalog.js";
10
11
  import { isMachineType } from "./machine-type.js";
11
12
  import { ensurePrivateDir } from "./paths.js";
@@ -40,6 +41,9 @@ export function readDaemonConfig(file) {
40
41
  config.machineType = obj.machineType;
41
42
  if (typeof obj.hqRoot === "string" && obj.hqRoot.trim())
42
43
  config.hqRoot = obj.hqRoot;
44
+ if (typeof obj.hqCloudVersion === "string" && validSemver(obj.hqCloudVersion) === obj.hqCloudVersion) {
45
+ config.hqCloudVersion = obj.hqCloudVersion;
46
+ }
43
47
  return config;
44
48
  }
45
49
  export function writeDaemonConfig(file, config) {
@@ -50,6 +54,8 @@ export function writeDaemonConfig(file, config) {
50
54
  ordered.machineType = config.machineType;
51
55
  if (config.hqRoot)
52
56
  ordered.hqRoot = config.hqRoot;
57
+ if (config.hqCloudVersion)
58
+ ordered.hqCloudVersion = config.hqCloudVersion;
53
59
  ordered.enable = config.enable;
54
60
  ordered.disable = config.disable;
55
61
  fs.writeFileSync(tmp, `${JSON.stringify(ordered, null, 2)}\n`, { mode: 0o600 });
@@ -22,7 +22,7 @@ export declare function managedHqCloudRoot(daemonRoot: string): string;
22
22
  * `usable` says whether a copy has what the daemon runs; a newer managed copy
23
23
  * that fails it (an interrupted or damaged install) is passed over.
24
24
  */
25
- export declare function pickHqCloud(bundled: HqCloudCopy | null, managed: HqCloudCopy | null, usable?: (copy: HqCloudCopy) => boolean): PickedHqCloud | null;
25
+ export declare function pickHqCloud(bundled: HqCloudCopy | null, managed: HqCloudCopy | null, usable?: (copy: HqCloudCopy) => boolean, pinnedVersion?: string): PickedHqCloud | null;
26
26
  export interface HqCloudRunners {
27
27
  syncRunner: string | null;
28
28
  /** `hq-outpost-heartbeat`, which older hq-cloud releases do not have. */
@@ -32,7 +32,7 @@ export interface HqCloudRunners {
32
32
  }
33
33
  export declare function hqCloudRunners(root: string, exists?: (p: string) => boolean): HqCloudRunners;
34
34
  /** Install the newest release of the bundled major version, never older than the bundled copy. */
35
- export declare function hqCloudInstallCommand(prefix: string, bundledVersion: string, platform?: NodeJS.Platform): {
35
+ export declare function hqCloudInstallCommand(prefix: string, bundledVersion: string, platform?: NodeJS.Platform, pinnedVersion?: string): {
36
36
  file: string;
37
37
  args: string[];
38
38
  shell: boolean;
@@ -10,6 +10,7 @@ import { spawnSync } from "node:child_process";
10
10
  import * as fs from "node:fs";
11
11
  import { createRequire } from "node:module";
12
12
  import * as path from "node:path";
13
+ import { readDaemonConfig } from "./config.js";
13
14
  import { buildSpawnPlan } from "../../utils/windows-spawn.js";
14
15
  export const HQ_CLOUD_PACKAGE = "@indigoai-us/hq-cloud";
15
16
  export function managedHqCloudDir(daemonRoot) {
@@ -34,11 +35,17 @@ function newer(a, b) {
34
35
  * `usable` says whether a copy has what the daemon runs; a newer managed copy
35
36
  * that fails it (an interrupted or damaged install) is passed over.
36
37
  */
37
- export function pickHqCloud(bundled, managed, usable = () => true) {
38
+ export function pickHqCloud(bundled, managed, usable = () => true, pinnedVersion) {
38
39
  if (!bundled)
39
40
  return null;
40
41
  const b = parseVersion(bundled.version);
41
42
  const m = managed ? parseVersion(managed.version) : null;
43
+ if (pinnedVersion) {
44
+ if (managed && managed.version === pinnedVersion && b && m && m[0] === b[0] && usable(managed)) {
45
+ return { ...managed, source: "managed" };
46
+ }
47
+ return { ...bundled, source: "bundled" };
48
+ }
42
49
  if (managed && b && m && m[0] === b[0] && newer(m, b) && usable(managed))
43
50
  return { ...managed, source: "managed" };
44
51
  return { ...bundled, source: "bundled" };
@@ -55,9 +62,17 @@ export function hqCloudRunners(root, exists = fs.existsSync) {
55
62
  };
56
63
  }
57
64
  /** Install the newest release of the bundled major version, never older than the bundled copy. */
58
- export function hqCloudInstallCommand(prefix, bundledVersion, platform = process.platform) {
65
+ export function hqCloudInstallCommand(prefix, bundledVersion, platform = process.platform, pinnedVersion) {
59
66
  // npm is a .cmd on Windows and needs the shell, so every argument is quoted for it.
60
- const plan = buildSpawnPlan("npm", ["install", "--prefix", prefix, "--no-audit", "--no-fund", "--omit=dev", `${HQ_CLOUD_PACKAGE}@^${bundledVersion}`], platform);
67
+ const plan = buildSpawnPlan("npm", [
68
+ "install",
69
+ "--prefix",
70
+ prefix,
71
+ "--no-audit",
72
+ "--no-fund",
73
+ "--omit=dev",
74
+ `${HQ_CLOUD_PACKAGE}@${pinnedVersion ?? `^${bundledVersion}`}`,
75
+ ], platform);
61
76
  return { file: plan.cmd, args: plan.args, shell: plan.shell };
62
77
  }
63
78
  export function readHqCloudCopy(root) {
@@ -81,10 +96,12 @@ export function bundledHqCloud() {
81
96
  }
82
97
  /** The hq-cloud the daemon should run right now. */
83
98
  export function currentHqCloud(daemonRoot) {
84
- return pickHqCloud(bundledHqCloud(), readHqCloudCopy(managedHqCloudRoot(daemonRoot)), (copy) => hqCloudRunners(copy.root).syncRunner !== null);
99
+ const pinnedVersion = readDaemonConfig(path.join(daemonRoot, "config.json")).hqCloudVersion;
100
+ return pickHqCloud(bundledHqCloud(), readHqCloudCopy(managedHqCloudRoot(daemonRoot)), (copy) => hqCloudRunners(copy.root).syncRunner !== null, pinnedVersion);
85
101
  }
86
102
  /** Install or update the updater's copy, as this (the login) user. */
87
103
  export function updateManagedHqCloud(daemonRoot, env) {
104
+ const pinnedVersion = readDaemonConfig(path.join(daemonRoot, "config.json")).hqCloudVersion;
88
105
  const bundled = bundledHqCloud();
89
106
  const from = currentHqCloud(daemonRoot)?.version ?? null;
90
107
  if (!bundled)
@@ -96,7 +113,7 @@ export function updateManagedHqCloud(daemonRoot, env) {
96
113
  catch (err) {
97
114
  return { ok: false, from, to: null, detail: `could not create ${prefix}: ${err instanceof Error ? err.message : String(err)}` };
98
115
  }
99
- const cmd = hqCloudInstallCommand(prefix, bundled.version);
116
+ const cmd = hqCloudInstallCommand(prefix, bundled.version, process.platform, pinnedVersion);
100
117
  const r = spawnSync(cmd.file, cmd.args, {
101
118
  encoding: "utf8",
102
119
  env,
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Environment assignments kept in the legacy outpost sync service drop-ins.
3
+ * The daemon reads these so taking over outpost-sync.service keeps its tuned
4
+ * sync limits. This module is deliberately tolerant: a bad or missing drop-in
5
+ * must never stop daemon planning.
6
+ */
7
+ export declare const LEGACY_SYNC_DROPIN_DIR = "/etc/systemd/system/outpost-sync.service.d";
8
+ /** Read simple KEY=VALUE and quoted "KEY=VALUE" Environment directives. */
9
+ export declare function readLegacySyncEnv(platform: NodeJS.Platform, directory?: string): Record<string, string>;
10
+ //# sourceMappingURL=legacy-sync-env.d.ts.map
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Environment assignments kept in the legacy outpost sync service drop-ins.
3
+ * The daemon reads these so taking over outpost-sync.service keeps its tuned
4
+ * sync limits. This module is deliberately tolerant: a bad or missing drop-in
5
+ * must never stop daemon planning.
6
+ */
7
+ import * as fs from "node:fs";
8
+ import * as path from "node:path";
9
+ export const LEGACY_SYNC_DROPIN_DIR = "/etc/systemd/system/outpost-sync.service.d";
10
+ function assignments(text, into) {
11
+ let inService = false;
12
+ for (const rawLine of text.split(/\r?\n/)) {
13
+ const line = rawLine.trim();
14
+ if (!line || line.startsWith("#"))
15
+ continue;
16
+ const section = /^\[([^\]]+)\]$/.exec(line);
17
+ if (section) {
18
+ inService = section[1] === "Service";
19
+ continue;
20
+ }
21
+ if (!inService)
22
+ continue;
23
+ const match = /^Environment=(?:"([^"]*)"|([A-Za-z_][A-Za-z0-9_]*=[^\s"]*))$/.exec(line);
24
+ const assignment = match?.[1] ?? match?.[2];
25
+ if (assignment === undefined)
26
+ continue;
27
+ const equals = assignment.indexOf("=");
28
+ if (equals <= 0)
29
+ continue;
30
+ const key = assignment.slice(0, equals);
31
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
32
+ continue;
33
+ into[key] = assignment.slice(equals + 1);
34
+ }
35
+ }
36
+ /** Read simple KEY=VALUE and quoted "KEY=VALUE" Environment directives. */
37
+ export function readLegacySyncEnv(platform, directory = LEGACY_SYNC_DROPIN_DIR) {
38
+ if (platform !== "linux")
39
+ return {};
40
+ let files;
41
+ try {
42
+ files = fs.readdirSync(directory).filter((name) => name.endsWith(".conf")).sort();
43
+ }
44
+ catch {
45
+ return {};
46
+ }
47
+ const env = {};
48
+ for (const file of files) {
49
+ try {
50
+ assignments(fs.readFileSync(path.join(directory, file), "utf8"), env);
51
+ }
52
+ catch {
53
+ // A single unreadable drop-in cannot make daemon planning fail.
54
+ }
55
+ }
56
+ return env;
57
+ }
58
+ //# sourceMappingURL=legacy-sync-env.js.map
@@ -18,6 +18,7 @@ import { botsRoot } from "../bot/paths.js";
18
18
  import { CLAUDE_RUNNER_SCRIPT, CODEX_RUNNER_SCRIPT, enabledServices, planUnits } from "./catalog.js";
19
19
  import { readDaemonConfig } from "./config.js";
20
20
  import { findLegacyUnits, legacyOwnerLookup } from "./legacy.js";
21
+ import { readLegacySyncEnv } from "./legacy-sync-env.js";
21
22
  import { acquireDaemonLock, releaseDaemonLock } from "./lock.js";
22
23
  import { daemonRootRefusal } from "./install.js";
23
24
  import { detectMachineType } from "./machine-type.js";
@@ -258,6 +259,7 @@ export async function runDaemon(opts) {
258
259
  syncRunnerPath: runners.syncRunner,
259
260
  heartbeatRunnerPath: runners.heartbeat,
260
261
  sessionHostRunnerPath: runners.sessionHost,
262
+ legacySyncEnv: readLegacySyncEnv(platform),
261
263
  signedIn: signedIn(env, home, log),
262
264
  kitConfigured: fs.existsSync(agentKitPaths(home, env).kitConfigPath),
263
265
  desktopAppManagesSync: machineType === "desktop" &&
@@ -72,7 +72,7 @@ export declare function parseOutpostPickupPayload(raw: unknown): OutpostPickupPa
72
72
  export declare function buildOutpostPickupCommand(payloadUrl: string): string;
73
73
  /** Bounded, read-only capacity query executed by the target Outpost CLI. */
74
74
  export declare function buildOutpostCapacityCommand(): string;
75
- /** Read every lane on an Outpost, optionally using its configured HQ binary. */
75
+ /** Read lane records owned by an Outpost, using its configured HQ binary when set. */
76
76
  export declare function buildOutpostLaneListCommand(input?: {
77
77
  hqBinary?: string;
78
78
  }): string;
@@ -65,7 +65,7 @@ export function buildOutpostPickupCommand(payloadUrl) {
65
65
  export function buildOutpostCapacityCommand() {
66
66
  return withRemoteHqDir(joinCommandParts(["hq", "lanes", "capacity", "--json"]));
67
67
  }
68
- /** Read every lane on an Outpost, optionally using its configured HQ binary. */
68
+ /** Read lane records owned by an Outpost, using its configured HQ binary when set. */
69
69
  export function buildOutpostLaneListCommand(input = {}) {
70
70
  return withRemoteHqDir(joinCommandParts([
71
71
  input.hqBinary?.trim() || "hq",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.200.1",
3
+ "version": "5.201.1",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {