@indigoai-us/hq-cli 5.125.0 → 5.127.0

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 (108) hide show
  1. package/CHANGELOG.md +329 -0
  2. package/assets/bot-workers/setup/skills/joining-a-team.md +125 -0
  3. package/assets/bot-workers/setup/skills/your-business.md +270 -0
  4. package/assets/bot-workers/setup/worker.yaml +189 -58
  5. package/assets/lanes-hooks/hq-lanes-drain.json +15 -0
  6. package/assets/lanes-hooks/hq-lanes-drain.mjs +254 -0
  7. package/assets/lanes-hooks/hq-lanes-drain.sh +21 -0
  8. package/assets/lanes-reconcile/hq-lanes-reconcile.service +11 -0
  9. package/assets/lanes-reconcile/hq-lanes-reconcile.timer +12 -0
  10. package/dist/command-catalog.generated.d.ts +643 -6
  11. package/dist/command-catalog.generated.js +836 -6
  12. package/dist/command-registration-plan.d.ts +7 -0
  13. package/dist/command-registration-plan.js +11 -1
  14. package/dist/commands/agents.d.ts +12 -0
  15. package/dist/commands/agents.js +46 -2
  16. package/dist/commands/bot.d.ts +10 -0
  17. package/dist/commands/bot.js +6 -0
  18. package/dist/commands/lanes.d.ts +114 -0
  19. package/dist/commands/lanes.js +2969 -0
  20. package/dist/commands/mesh.js +287 -24
  21. package/dist/commands/onboard-create-identity.d.ts +26 -0
  22. package/dist/commands/onboard-create-identity.js +23 -0
  23. package/dist/commands/onboard.js +13 -4
  24. package/dist/lib/bot/progress.d.ts +6 -0
  25. package/dist/lib/bot/progress.js +16 -0
  26. package/dist/lib/bot/runtime/grok.d.ts +30 -7
  27. package/dist/lib/bot/runtime/grok.js +86 -8
  28. package/dist/lib/bot/runtime/index.js +15 -8
  29. package/dist/lib/bot/runtime/messages-stream.d.ts +12 -3
  30. package/dist/lib/bot/runtime/messages-stream.js +49 -3
  31. package/dist/lib/lanes/accounts.d.ts +103 -0
  32. package/dist/lib/lanes/accounts.js +318 -0
  33. package/dist/lib/lanes/compaction.d.ts +96 -0
  34. package/dist/lib/lanes/compaction.js +197 -0
  35. package/dist/lib/lanes/config.d.ts +71 -0
  36. package/dist/lib/lanes/config.js +408 -0
  37. package/dist/lib/lanes/delivery.d.ts +151 -0
  38. package/dist/lib/lanes/delivery.js +354 -0
  39. package/dist/lib/lanes/dropbox.d.ts +96 -0
  40. package/dist/lib/lanes/dropbox.js +408 -0
  41. package/dist/lib/lanes/effort.d.ts +50 -0
  42. package/dist/lib/lanes/effort.js +109 -0
  43. package/dist/lib/lanes/errors.d.ts +39 -0
  44. package/dist/lib/lanes/errors.js +72 -0
  45. package/dist/lib/lanes/hooks-install.d.ts +81 -0
  46. package/dist/lib/lanes/hooks-install.js +326 -0
  47. package/dist/lib/lanes/hq-root.d.ts +19 -0
  48. package/dist/lib/lanes/hq-root.js +28 -0
  49. package/dist/lib/lanes/index.d.ts +49 -0
  50. package/dist/lib/lanes/index.js +27 -0
  51. package/dist/lib/lanes/liveness.d.ts +95 -0
  52. package/dist/lib/lanes/liveness.js +332 -0
  53. package/dist/lib/lanes/machine-identity.d.ts +34 -0
  54. package/dist/lib/lanes/machine-identity.js +92 -0
  55. package/dist/lib/lanes/mesh-events.d.ts +109 -0
  56. package/dist/lib/lanes/mesh-events.js +275 -0
  57. package/dist/lib/lanes/outpost-dispatch.d.ts +65 -0
  58. package/dist/lib/lanes/outpost-dispatch.js +90 -0
  59. package/dist/lib/lanes/questions.d.ts +75 -0
  60. package/dist/lib/lanes/questions.js +243 -0
  61. package/dist/lib/lanes/quota.d.ts +95 -0
  62. package/dist/lib/lanes/quota.js +320 -0
  63. package/dist/lib/lanes/reconcile-install.d.ts +69 -0
  64. package/dist/lib/lanes/reconcile-install.js +221 -0
  65. package/dist/lib/lanes/reconcile.d.ts +102 -0
  66. package/dist/lib/lanes/reconcile.js +985 -0
  67. package/dist/lib/lanes/registry.d.ts +116 -0
  68. package/dist/lib/lanes/registry.js +521 -0
  69. package/dist/lib/lanes/spawn.d.ts +274 -0
  70. package/dist/lib/lanes/spawn.js +1363 -0
  71. package/dist/lib/lanes/store.d.ts +68 -0
  72. package/dist/lib/lanes/store.js +745 -0
  73. package/dist/lib/lanes/throttle.d.ts +89 -0
  74. package/dist/lib/lanes/throttle.js +297 -0
  75. package/dist/lib/lanes/types.d.ts +535 -0
  76. package/dist/lib/lanes/types.js +350 -0
  77. package/dist/lib/lanes/watcher-main.d.ts +9 -0
  78. package/dist/lib/lanes/watcher-main.js +22 -0
  79. package/dist/lib/lanes/watcher.d.ts +216 -0
  80. package/dist/lib/lanes/watcher.js +859 -0
  81. package/dist/lib/mesh/api.d.ts +71 -7
  82. package/dist/lib/mesh/api.js +167 -28
  83. package/dist/lib/mesh/live/backfill-held.d.ts +18 -7
  84. package/dist/lib/mesh/live/backfill-held.js +66 -21
  85. package/dist/lib/mesh/live/daemon/config.d.ts +27 -0
  86. package/dist/lib/mesh/live/daemon/config.js +75 -0
  87. package/dist/lib/mesh/live/daemon/index.d.ts +4 -3
  88. package/dist/lib/mesh/live/daemon/index.js +3 -3
  89. package/dist/lib/mesh/live/daemon/install.d.ts +143 -1
  90. package/dist/lib/mesh/live/daemon/install.js +444 -2
  91. package/dist/lib/mesh/live/daemon/paths.d.ts +2 -0
  92. package/dist/lib/mesh/live/daemon/paths.js +2 -0
  93. package/dist/lib/mesh/live/daemon/run.d.ts +2 -0
  94. package/dist/lib/mesh/live/daemon/run.js +111 -54
  95. package/dist/lib/mesh/live/daemon/state.d.ts +2 -0
  96. package/dist/lib/mesh/live/daemon/transcript-watch.d.ts +3 -0
  97. package/dist/lib/mesh/live/daemon/transcript-watch.js +31 -8
  98. package/dist/lib/scan-packages/index.d.ts +2 -0
  99. package/dist/lib/scan-packages/index.js +18 -13
  100. package/dist/lib/work-context/config.d.ts +20 -0
  101. package/dist/lib/work-context/config.js +26 -5
  102. package/dist/lib/work-context/reconcile.js +28 -7
  103. package/dist/lib/work-context/state.d.ts +12 -0
  104. package/dist/lib/work-context/state.js +131 -3
  105. package/dist/lib/work-context/windows-acl.d.ts +123 -0
  106. package/dist/lib/work-context/windows-acl.js +425 -0
  107. package/dist/utils/pack-contributions.js +5 -4
  108. package/package.json +3 -1
@@ -16,6 +16,81 @@ export function verifyHqRoot(candidate) {
16
16
  return null;
17
17
  }
18
18
  }
19
+ /**
20
+ * A directory the daemon may be installed for: it must carry
21
+ * companies/manifest.yaml (what verifyHqRoot demands at runtime) and one of
22
+ * the tree markers utils/hq-roots.ts accepts (core/, .claude/, personal/).
23
+ * A bare .claude/ directory — every repo with Claude settings has one — is
24
+ * not an HQ root and must never be persisted or passed as --hq-root.
25
+ */
26
+ export function isInstallableHqRoot(dir) {
27
+ if (verifyHqRoot(dir) === null)
28
+ return false;
29
+ return ["core", ".claude", "personal"].some((marker) => {
30
+ try {
31
+ return fs.statSync(path.join(dir, marker)).isDirectory();
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ });
37
+ }
38
+ /** Nearest ancestor of startDir (inclusive) that is an installable HQ root, realpath'd; null if none. */
39
+ export function findAncestorHqRoot(startDir) {
40
+ let dir;
41
+ try {
42
+ dir = fs.realpathSync(startDir);
43
+ }
44
+ catch {
45
+ dir = path.resolve(startDir);
46
+ }
47
+ for (;;) {
48
+ if (isInstallableHqRoot(dir))
49
+ return fs.realpathSync(dir);
50
+ const parent = path.dirname(dir);
51
+ if (parent === dir)
52
+ return null;
53
+ dir = parent;
54
+ }
55
+ }
56
+ /**
57
+ * Resolve the HQ root `hq mesh daemon install` records and passes to the
58
+ * service. Precedence: --hq-root, then HQ_ROOT, then the nearest ancestor of
59
+ * cwd that is an installable HQ root. An explicit value that is not an HQ
60
+ * root is an error rather than a silent fallback, and with nothing
61
+ * qualifying the install is refused: an unverified root would be persisted
62
+ * and trusted by the daemon for company attribution.
63
+ */
64
+ export function resolveInstallHqRoot(opts = {}) {
65
+ const env = opts.env ?? process.env;
66
+ const cwd = opts.cwd ?? process.cwd();
67
+ const explicit = opts.explicit?.trim();
68
+ if (explicit) {
69
+ if (!isInstallableHqRoot(explicit)) {
70
+ throw expectedError(`--hq-root ${explicit} is not an HQ root (needs companies/manifest.yaml plus core/, .claude/, or personal/).`);
71
+ }
72
+ return { root: fs.realpathSync(explicit), source: "flag" };
73
+ }
74
+ const fromEnv = env.HQ_ROOT?.trim();
75
+ if (fromEnv) {
76
+ if (!isInstallableHqRoot(fromEnv)) {
77
+ throw expectedError(`HQ_ROOT=${fromEnv} is not an HQ root (needs companies/manifest.yaml plus core/, .claude/, or personal/). ` +
78
+ `Fix HQ_ROOT or pass --hq-root <path>.`);
79
+ }
80
+ return { root: fs.realpathSync(fromEnv), source: "env" };
81
+ }
82
+ const ancestor = findAncestorHqRoot(cwd);
83
+ if (!ancestor) {
84
+ throw expectedError(`No HQ root found above ${cwd} (a directory with companies/manifest.yaml plus core/, .claude/, or personal/). ` +
85
+ `Run from inside your HQ tree or pass --hq-root <path>.`);
86
+ }
87
+ return { root: ancestor, source: "ancestor" };
88
+ }
89
+ function expectedError(message) {
90
+ const err = new Error(message);
91
+ err.expected = true;
92
+ return err;
93
+ }
19
94
  export function readDaemonConfig(daemonDirectory) {
20
95
  try {
21
96
  const raw = JSON.parse(fs.readFileSync(daemonConfigPath(daemonDirectory), "utf8"));
@@ -1,4 +1,4 @@
1
- export { DAEMON_DIRNAME, DAEMON_LOG_MAX_BYTES, DAEMON_LOG_NAME, DAEMON_PID_NAME, DAEMON_STATE_NAME, LAUNCHD_LABEL, SYSTEMD_UNIT_NAME, daemonDir, daemonLogPath, daemonPidPath, daemonStatePath, sessionBoardPath, } from "./paths.js";
1
+ export { DAEMON_DIRNAME, DAEMON_LOG_MAX_BYTES, DAEMON_LOG_NAME, DAEMON_PID_NAME, DAEMON_STATE_NAME, LAUNCHD_LABEL, SYSTEMD_UNIT_NAME, WINDOWS_TASK_NAME, daemonDir, daemonLogPath, daemonPidPath, daemonStatePath, sessionBoardPath, } from "./paths.js";
2
2
  export { acquirePidLock, defaultPidLockDeps, parsePidLock, pidLockStatus, readPidLock, releasePidLock, resolveDaemonDir, } from "./pid-lock.js";
3
3
  export type { PidLockDeps, PidLockRecord, PidLockResult } from "./pid-lock.js";
4
4
  export { CREDENTIAL_RENEWAL_FRACTION, DEFAULT_REFUSED_RETRY_MS, MAX_RETRY_DELAY_MS, MAX_TIMER_DELAY_MS, MQTT_KEEPALIVE_SECONDS, REALTIME_CREDENTIALS_PATH, REFUSED_RETRY_ENV, CredentialRenewalManager, CredentialVendError, classifyCredentialVendFailure, clampRetryDelayMs, createContract3Fetcher, defaultRefusedRetryMs, extractVendErrorCode, isCredentialVendRefused, normalizeContract3Bundle, parseRetryAfterMs, realTimerHost, refusedRetryDelayMs, renewalDelayMs, scheduleBoundedTimeout, } from "./credentials.js";
@@ -12,9 +12,10 @@ export type { DaemonStateFile, PresenceRefusalState } from "./state.js";
12
12
  export { appendDaemonLog, daemonLogLine, ensureDaemonLog, resolveDaemonAssetDir, rotateDaemonLogIfNeeded, } from "./log.js";
13
13
  export { BOARD_REFRESH_INTERVAL_MS, createVaultBoardReader, formatBoardMarkdown, refreshBoundSessionBoards, writeBoardMarkdown, } from "./board-refresh.js";
14
14
  export type { BoardReader, BoardRefreshDeps, BoardStorySnapshot } from "./board-refresh.js";
15
- export { buildInstallPaths, daemonServiceStatus, detectPlatform, installDaemonService, readAssetTemplate, restartDaemonServiceIfInstalled, renderLaunchdPlist, renderSystemdSystemUnit, renderSystemdUserUnit, resolveHqBinary, resolveNodeBinary, uninstallDaemonService, } from "./install.js";
15
+ export { buildInstallPaths, daemonServiceStatus, detectPlatform, installDaemonService, readAssetTemplate, restartDaemonServiceIfInstalled, renderLaunchdPlist, renderSystemdSystemUnit, renderSystemdUserUnit, resolveHqBinary, resolveNodeBinary, parseSchtasksLastResult, parseWindowsTaskHqRoot, readRecordedWindowsHqRoot, schtasksLastResultFailed, uninstallDaemonService, windowsLaunchMode, windowsLogonNotice, } from "./install.js";
16
16
  export type { DaemonInstallPlatform, InstallPaths, InstallResult, } from "./install.js";
17
- export { daemonConfigPath, readDaemonConfig, registerTrustedHqRoot, verifyHqRoot } from "./config.js";
17
+ export { daemonConfigPath, findAncestorHqRoot, isInstallableHqRoot, readDaemonConfig, registerTrustedHqRoot, resolveInstallHqRoot, verifyHqRoot, } from "./config.js";
18
+ export type { InstallHqRootResolution } from "./config.js";
18
19
  export type { DaemonConfig } from "./config.js";
19
20
  export { UNHEALTHY_SPOOL_AGE_MS, collectDaemonDoctor, formatDaemonDoctor, } from "./doctor.js";
20
21
  export type { DaemonDoctorDeps, DaemonDoctorReport } from "./doctor.js";
@@ -1,4 +1,4 @@
1
- export { DAEMON_DIRNAME, DAEMON_LOG_MAX_BYTES, DAEMON_LOG_NAME, DAEMON_PID_NAME, DAEMON_STATE_NAME, LAUNCHD_LABEL, SYSTEMD_UNIT_NAME, daemonDir, daemonLogPath, daemonPidPath, daemonStatePath, sessionBoardPath, } from "./paths.js";
1
+ export { DAEMON_DIRNAME, DAEMON_LOG_MAX_BYTES, DAEMON_LOG_NAME, DAEMON_PID_NAME, DAEMON_STATE_NAME, LAUNCHD_LABEL, SYSTEMD_UNIT_NAME, WINDOWS_TASK_NAME, daemonDir, daemonLogPath, daemonPidPath, daemonStatePath, sessionBoardPath, } from "./paths.js";
2
2
  export { acquirePidLock, defaultPidLockDeps, parsePidLock, pidLockStatus, readPidLock, releasePidLock, resolveDaemonDir, } from "./pid-lock.js";
3
3
  export { CREDENTIAL_RENEWAL_FRACTION, DEFAULT_REFUSED_RETRY_MS, MAX_RETRY_DELAY_MS, MAX_TIMER_DELAY_MS, MQTT_KEEPALIVE_SECONDS, REALTIME_CREDENTIALS_PATH, REFUSED_RETRY_ENV, CredentialRenewalManager, CredentialVendError, classifyCredentialVendFailure, clampRetryDelayMs, createContract3Fetcher, defaultRefusedRetryMs, extractVendErrorCode, isCredentialVendRefused, normalizeContract3Bundle, parseRetryAfterMs, realTimerHost, refusedRetryDelayMs, renewalDelayMs, scheduleBoundedTimeout, } from "./credentials.js";
4
4
  export { amzDateOf, hex, presignIotWssUrl, rfc3986Encode } from "./presign.js";
@@ -6,8 +6,8 @@ export { CLOSE_LOG_ALWAYS_COUNT, CLOSE_LOG_INTERVAL_MS, CONNECT_STABLE_GRACE_MS,
6
6
  export { defaultDaemonState, patchDaemonState, readDaemonState, writeDaemonState, } from "./state.js";
7
7
  export { appendDaemonLog, daemonLogLine, ensureDaemonLog, resolveDaemonAssetDir, rotateDaemonLogIfNeeded, } from "./log.js";
8
8
  export { BOARD_REFRESH_INTERVAL_MS, createVaultBoardReader, formatBoardMarkdown, refreshBoundSessionBoards, writeBoardMarkdown, } from "./board-refresh.js";
9
- export { buildInstallPaths, daemonServiceStatus, detectPlatform, installDaemonService, readAssetTemplate, restartDaemonServiceIfInstalled, renderLaunchdPlist, renderSystemdSystemUnit, renderSystemdUserUnit, resolveHqBinary, resolveNodeBinary, uninstallDaemonService, } from "./install.js";
10
- export { daemonConfigPath, readDaemonConfig, registerTrustedHqRoot, verifyHqRoot } from "./config.js";
9
+ export { buildInstallPaths, daemonServiceStatus, detectPlatform, installDaemonService, readAssetTemplate, restartDaemonServiceIfInstalled, renderLaunchdPlist, renderSystemdSystemUnit, renderSystemdUserUnit, resolveHqBinary, resolveNodeBinary, parseSchtasksLastResult, parseWindowsTaskHqRoot, readRecordedWindowsHqRoot, schtasksLastResultFailed, uninstallDaemonService, windowsLaunchMode, windowsLogonNotice, } from "./install.js";
10
+ export { daemonConfigPath, findAncestorHqRoot, isInstallableHqRoot, readDaemonConfig, registerTrustedHqRoot, resolveInstallHqRoot, verifyHqRoot, } from "./config.js";
11
11
  export { UNHEALTHY_SPOOL_AGE_MS, collectDaemonDoctor, formatDaemonDoctor, } from "./doctor.js";
12
12
  export { scopeBundleToAgentIdentity, } from "./credentials.js";
13
13
  export { FLUSH_INTERVAL_MS, DAEMON_VERSION_DRIFT_EXIT_CODE, SPOOL_DEBOUNCE_MS, runMeshDaemon, } from "./run.js";
@@ -4,13 +4,36 @@
4
4
  * - macOS: LaunchAgent ai.getindigo.hq-mesh-daemon
5
5
  * - Linux: systemd user unit hq-mesh-daemon.service
6
6
  * (documented fallback: root-owned unit running as ec2-user)
7
+ * - Windows: per-user Scheduled Task ai.getindigo.hq-mesh-daemon registered
8
+ * from a rendered task XML via schtasks.exe (no native dependencies)
7
9
  *
8
10
  * The unit embeds the hq binary resolved at install time. Logs go to
9
11
  * ~/.hq/work-mesh/daemon/log (rotation at 5MB handled by the daemon).
10
12
  * Tests must not actually install services — use a temp HOME/prefix.
11
13
  */
12
14
  import * as fs from "node:fs";
13
- export type DaemonInstallPlatform = "darwin" | "linux" | "other";
15
+ export type DaemonInstallPlatform = "darwin" | "linux" | "win32" | "other";
16
+ /** Result of one schtasks.exe invocation (tests inject; never the real binary). */
17
+ export interface SchtasksResult {
18
+ status: number | null;
19
+ stdout: string;
20
+ stderr: string;
21
+ }
22
+ export type SchtasksRunner = (args: string[]) => SchtasksResult;
23
+ /**
24
+ * `schtasks /End` terminates the task's root process only. The daemon is
25
+ * launched through `conhost.exe --headless`, so ending the task kills conhost
26
+ * and leaves node running as an orphan that still holds the pid lock. Stop
27
+ * the recorded daemon pid explicitly. Returns the pid that was signalled, or
28
+ * null when no live daemon pid was recorded.
29
+ */
30
+ export declare function stopWindowsDaemonProcess(dir: string, deps?: {
31
+ readFileSync?: typeof fs.readFileSync;
32
+ existsSync?: typeof fs.existsSync;
33
+ kill?: (pid: number) => void;
34
+ selfPid?: number;
35
+ }): number | null;
36
+ export declare const defaultSchtasks: SchtasksRunner;
14
37
  export interface InstallPaths {
15
38
  home: string;
16
39
  hqBinary: string;
@@ -21,10 +44,22 @@ export interface InstallPaths {
21
44
  plistPath: string;
22
45
  systemdUserDir: string;
23
46
  systemdUnitPath: string;
47
+ /** Windows: rendered Task Scheduler XML kept under the daemon dir. */
48
+ windowsTaskXmlPath: string;
49
+ /** Windows: `DOMAIN\user` the task is registered for (logon trigger + principal). */
50
+ windowsUser: string;
51
+ /**
52
+ * Windows: `conhost.exe` used as `conhost --headless node hq ...` so the
53
+ * logon-triggered daemon gets no visible console window. Undefined when the
54
+ * host has no conhost (the task then runs node directly).
55
+ */
56
+ windowsHeadlessLauncher?: string;
24
57
  /** When true, render the root/ec2-user system unit instead of user unit. */
25
58
  systemUser?: string;
26
59
  /** Verified HQ root to persist and pass to the installed service. */
27
60
  hqRoot?: string;
61
+ /** How hqRoot was chosen (shown in the Windows install confirmation). */
62
+ hqRootSource?: "flag" | "env" | "ancestor";
28
63
  }
29
64
  export interface InstallResult {
30
65
  platform: DaemonInstallPlatform;
@@ -34,6 +69,22 @@ export interface InstallResult {
34
69
  dest?: string;
35
70
  running?: boolean;
36
71
  message: string;
72
+ /**
73
+ * Windows status: the task's "Last Result" code from `schtasks /Query /V`
74
+ * (normalized to unsigned 32-bit), or null when unavailable.
75
+ */
76
+ lastResult?: number | null;
77
+ /** Windows status: true when lastResult indicates the last start failed. */
78
+ lastRunFailed?: boolean;
79
+ /** One plain line of operator context (Windows: logged-on-only visibility). */
80
+ notice?: string;
81
+ /**
82
+ * Windows status: HQ root read back from the registered task XML (else the
83
+ * persisted daemon config), never re-resolved from cwd. Null when unknown.
84
+ */
85
+ recordedHqRoot?: string | null;
86
+ /** Windows status: whether recordedHqRoot still passes isInstallableHqRoot. */
87
+ rootValid?: boolean;
37
88
  }
38
89
  /** Best-effort restart result used after an in-place CLI upgrade. */
39
90
  export interface RestartDaemonResult {
@@ -54,6 +105,8 @@ export declare function restartDaemonServiceIfInstalled(opts?: {
54
105
  run?: (command: string, args: string[]) => {
55
106
  status: number | null;
56
107
  };
108
+ /** Windows: terminate the recorded daemon pid after `schtasks /End` (tests inject). */
109
+ kill?: (pid: number) => void;
57
110
  }): RestartDaemonResult;
58
111
  export declare function detectPlatform(platform?: NodeJS.Platform): DaemonInstallPlatform;
59
112
  /**
@@ -76,7 +129,34 @@ export declare function buildInstallPaths(opts: {
76
129
  argv?: string[];
77
130
  execPath?: string;
78
131
  hqRoot?: string;
132
+ hqRootSource?: "flag" | "env" | "ancestor";
133
+ /** Windows account (`DOMAIN\user`) the task runs as; defaults to the current user. */
134
+ windowsUser?: string;
135
+ /** Windows headless launcher override (tests); defaults to %SystemRoot%\System32\conhost.exe when present. */
136
+ windowsHeadlessLauncher?: string | null;
79
137
  }): InstallPaths;
138
+ /** `DOMAIN\user` for the current account, as Task Scheduler expects. */
139
+ export declare function resolveWindowsUser(env?: NodeJS.ProcessEnv): string;
140
+ /**
141
+ * `conhost.exe --headless` is undocumented. It shipped with the ConPTY
142
+ * conhost in Windows 10 1809 (build 17763); older conhost builds reject the
143
+ * flag and the task would fail on every logon. Gate on the kernel build
144
+ * reported by os.release() ("10.0.<build>") plus a presence check.
145
+ */
146
+ export declare const WINDOWS_CONHOST_HEADLESS_MIN_BUILD = 17763;
147
+ /** Build number from an os.release() string such as "10.0.26200"; null if unparsable. */
148
+ export declare function parseWindowsBuild(release: string): number | null;
149
+ /**
150
+ * `conhost.exe --headless` (Windows 10 1809+) runs a console app with no
151
+ * window. Returns the launcher path only when conhost.exe exists under
152
+ * SystemRoot and the host build is new enough to honour `--headless`.
153
+ */
154
+ export declare function resolveWindowsHeadlessLauncher(env?: NodeJS.ProcessEnv, existsSync?: typeof fs.existsSync, release?: string): string | undefined;
155
+ export type WindowsLaunchMode = "conhost-headless" | "direct";
156
+ /** Which launch wrapper the rendered task uses (recorded in the install message). */
157
+ export declare function windowsLaunchMode(paths: Pick<InstallPaths, "windowsHeadlessLauncher">): WindowsLaunchMode;
158
+ /** The task uses the interactive token, so it exists only inside the user's logon session. */
159
+ export declare function windowsLogonNotice(paths: Pick<InstallPaths, "windowsUser">): string;
80
160
  export declare function renderLaunchdPlist(paths: InstallPaths): string;
81
161
  export declare function renderSystemdUserUnit(paths: InstallPaths): string;
82
162
  /**
@@ -84,6 +164,55 @@ export declare function renderSystemdUserUnit(paths: InstallPaths): string;
84
164
  * Documented fallback — written only when --system-user is passed.
85
165
  */
86
166
  export declare function renderSystemdSystemUnit(paths: InstallPaths): string;
167
+ /**
168
+ * Windows Task Scheduler definition (schtasks /Create /XML). Runs at logon of
169
+ * the registering user, hidden, no execution time limit, one instance, and
170
+ * restarts every minute on failure. HOME/USERPROFILE come from the user's own
171
+ * session. Task Scheduler XML has no environment block, so the HQ root the
172
+ * install was run for is passed explicitly as `mesh daemon run --hq-root
173
+ * <path>` (the launchd/systemd equivalent of Environment=HQ_ROOT) and is also
174
+ * persisted in the daemon config as a trusted root.
175
+ *
176
+ * LogonType is InteractiveToken: S4U ("run whether user is logged on or not")
177
+ * needs elevation to register, and the interactive token is what an
178
+ * unprivileged `hq mesh daemon install` can create. As a consequence the task
179
+ * runs only while the registering user is logged on. The console window that
180
+ * an interactive-token task would otherwise open is avoided by launching
181
+ * through `conhost.exe --headless` when the host supports it (see
182
+ * resolveWindowsHeadlessLauncher); otherwise node is run directly.
183
+ */
184
+ export declare function renderWindowsTaskXml(paths: InstallPaths): string;
185
+ /** schtasks stores task XML as UTF-16 LE with BOM; match it so /XML parses. */
186
+ export declare function encodeWindowsTaskXml(rendered: string): Buffer;
187
+ /** Status column of `schtasks /Query /FO CSV /NH`: "TaskName","Next Run Time","Status". */
188
+ export declare function parseSchtasksCsvStatus(stdout: string): string | null;
189
+ /**
190
+ * "Last Result" of `schtasks /Query /TN <name> /V /FO CSV`, normalized to an
191
+ * unsigned 32-bit code (schtasks prints HRESULT/NTSTATUS values as signed
192
+ * decimals, e.g. -2147024894 for 0x80070002). Prefers the English header
193
+ * when present; otherwise uses the fixed column position. Null when the
194
+ * output carries no task row or the cell is not numeric.
195
+ */
196
+ export declare function parseSchtasksLastResult(stdout: string): number | null;
197
+ /**
198
+ * Task Scheduler informational results mean "no failed start": 0 is success,
199
+ * and every SCHED_S_* code (ready, running, not yet run, terminated, queued,
200
+ * ...) is a success-severity HRESULT (bit 31 clear) in FACILITY_ITF (0x4)
201
+ * with code 0x1300-0x13FF, i.e. 0x00041300-0x000413FF. Everything else
202
+ * (0x8007xxxx, 0xC000xxxx, process exit codes such as 1) is a failed start.
203
+ */
204
+ export declare function schtasksLastResultFailed(code: number | null): boolean;
205
+ /** `--hq-root <path>` from a rendered task's <Arguments> (XML-escaped, optionally quoted). */
206
+ export declare function parseWindowsTaskHqRoot(xml: string): string | null;
207
+ /**
208
+ * The HQ root the installed Windows task actually carries: the task XML on
209
+ * disk first, else the first persisted trusted root. Reads state only; never
210
+ * resolves from cwd, so status reports what install recorded.
211
+ */
212
+ export declare function readRecordedWindowsHqRoot(paths: Pick<InstallPaths, "windowsTaskXmlPath" | "daemonDir">, deps?: {
213
+ existsSync?: typeof fs.existsSync;
214
+ readFileSync?: typeof fs.readFileSync;
215
+ }): string | null;
87
216
  export declare function installDaemonService(opts: {
88
217
  platform?: DaemonInstallPlatform;
89
218
  paths?: InstallPaths;
@@ -97,6 +226,10 @@ export declare function installDaemonService(opts: {
97
226
  reloadSystemd?: () => {
98
227
  status: number | null;
99
228
  };
229
+ /** Windows: schtasks.exe runner (tests inject; never the real binary). */
230
+ schtasks?: SchtasksRunner;
231
+ /** Windows: terminate the recorded daemon pid when replacing a task (tests inject). */
232
+ kill?: (pid: number) => void;
100
233
  }): InstallResult;
101
234
  export declare function uninstallDaemonService(opts: {
102
235
  platform?: DaemonInstallPlatform;
@@ -104,6 +237,10 @@ export declare function uninstallDaemonService(opts: {
104
237
  unlinkSync?: typeof fs.unlinkSync;
105
238
  existsSync?: typeof fs.existsSync;
106
239
  deactivate?: (platform: DaemonInstallPlatform, dest: string) => void;
240
+ /** Windows: schtasks.exe runner (tests inject; never the real binary). */
241
+ schtasks?: SchtasksRunner;
242
+ /** Windows: terminate the recorded daemon pid after `schtasks /End` (tests inject). */
243
+ kill?: (pid: number) => void;
107
244
  }): InstallResult;
108
245
  export declare function daemonServiceStatus(opts: {
109
246
  platform?: DaemonInstallPlatform;
@@ -111,6 +248,11 @@ export declare function daemonServiceStatus(opts: {
111
248
  existsSync?: typeof fs.existsSync;
112
249
  /** Optional live probe (launchctl/systemctl); tests inject. */
113
250
  isLoaded?: (platform: DaemonInstallPlatform, dest: string) => boolean;
251
+ /** Windows: schtasks.exe runner (tests inject; never the real binary). */
252
+ schtasks?: SchtasksRunner;
253
+ /** Windows: liveness probe for the daemon pid lock (tests inject). */
254
+ isProcessAlive?: (pid: number) => boolean;
255
+ readFileSync?: typeof fs.readFileSync;
114
256
  }): InstallResult;
115
257
  /** Read baked template files from assets/ (optional; renderers are primary). */
116
258
  export declare function readAssetTemplate(name: "launchd.plist.template" | "systemd.user.service.template" | "systemd.system.service.template"): string | null;