@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.
- package/CHANGELOG.md +329 -0
- package/assets/bot-workers/setup/skills/joining-a-team.md +125 -0
- package/assets/bot-workers/setup/skills/your-business.md +270 -0
- package/assets/bot-workers/setup/worker.yaml +189 -58
- package/assets/lanes-hooks/hq-lanes-drain.json +15 -0
- package/assets/lanes-hooks/hq-lanes-drain.mjs +254 -0
- package/assets/lanes-hooks/hq-lanes-drain.sh +21 -0
- package/assets/lanes-reconcile/hq-lanes-reconcile.service +11 -0
- package/assets/lanes-reconcile/hq-lanes-reconcile.timer +12 -0
- package/dist/command-catalog.generated.d.ts +643 -6
- package/dist/command-catalog.generated.js +836 -6
- package/dist/command-registration-plan.d.ts +7 -0
- package/dist/command-registration-plan.js +11 -1
- package/dist/commands/agents.d.ts +12 -0
- package/dist/commands/agents.js +46 -2
- package/dist/commands/bot.d.ts +10 -0
- package/dist/commands/bot.js +6 -0
- package/dist/commands/lanes.d.ts +114 -0
- package/dist/commands/lanes.js +2969 -0
- package/dist/commands/mesh.js +287 -24
- package/dist/commands/onboard-create-identity.d.ts +26 -0
- package/dist/commands/onboard-create-identity.js +23 -0
- package/dist/commands/onboard.js +13 -4
- package/dist/lib/bot/progress.d.ts +6 -0
- package/dist/lib/bot/progress.js +16 -0
- package/dist/lib/bot/runtime/grok.d.ts +30 -7
- package/dist/lib/bot/runtime/grok.js +86 -8
- package/dist/lib/bot/runtime/index.js +15 -8
- package/dist/lib/bot/runtime/messages-stream.d.ts +12 -3
- package/dist/lib/bot/runtime/messages-stream.js +49 -3
- package/dist/lib/lanes/accounts.d.ts +103 -0
- package/dist/lib/lanes/accounts.js +318 -0
- package/dist/lib/lanes/compaction.d.ts +96 -0
- package/dist/lib/lanes/compaction.js +197 -0
- package/dist/lib/lanes/config.d.ts +71 -0
- package/dist/lib/lanes/config.js +408 -0
- package/dist/lib/lanes/delivery.d.ts +151 -0
- package/dist/lib/lanes/delivery.js +354 -0
- package/dist/lib/lanes/dropbox.d.ts +96 -0
- package/dist/lib/lanes/dropbox.js +408 -0
- package/dist/lib/lanes/effort.d.ts +50 -0
- package/dist/lib/lanes/effort.js +109 -0
- package/dist/lib/lanes/errors.d.ts +39 -0
- package/dist/lib/lanes/errors.js +72 -0
- package/dist/lib/lanes/hooks-install.d.ts +81 -0
- package/dist/lib/lanes/hooks-install.js +326 -0
- package/dist/lib/lanes/hq-root.d.ts +19 -0
- package/dist/lib/lanes/hq-root.js +28 -0
- package/dist/lib/lanes/index.d.ts +49 -0
- package/dist/lib/lanes/index.js +27 -0
- package/dist/lib/lanes/liveness.d.ts +95 -0
- package/dist/lib/lanes/liveness.js +332 -0
- package/dist/lib/lanes/machine-identity.d.ts +34 -0
- package/dist/lib/lanes/machine-identity.js +92 -0
- package/dist/lib/lanes/mesh-events.d.ts +109 -0
- package/dist/lib/lanes/mesh-events.js +275 -0
- package/dist/lib/lanes/outpost-dispatch.d.ts +65 -0
- package/dist/lib/lanes/outpost-dispatch.js +90 -0
- package/dist/lib/lanes/questions.d.ts +75 -0
- package/dist/lib/lanes/questions.js +243 -0
- package/dist/lib/lanes/quota.d.ts +95 -0
- package/dist/lib/lanes/quota.js +320 -0
- package/dist/lib/lanes/reconcile-install.d.ts +69 -0
- package/dist/lib/lanes/reconcile-install.js +221 -0
- package/dist/lib/lanes/reconcile.d.ts +102 -0
- package/dist/lib/lanes/reconcile.js +985 -0
- package/dist/lib/lanes/registry.d.ts +116 -0
- package/dist/lib/lanes/registry.js +521 -0
- package/dist/lib/lanes/spawn.d.ts +274 -0
- package/dist/lib/lanes/spawn.js +1363 -0
- package/dist/lib/lanes/store.d.ts +68 -0
- package/dist/lib/lanes/store.js +745 -0
- package/dist/lib/lanes/throttle.d.ts +89 -0
- package/dist/lib/lanes/throttle.js +297 -0
- package/dist/lib/lanes/types.d.ts +535 -0
- package/dist/lib/lanes/types.js +350 -0
- package/dist/lib/lanes/watcher-main.d.ts +9 -0
- package/dist/lib/lanes/watcher-main.js +22 -0
- package/dist/lib/lanes/watcher.d.ts +216 -0
- package/dist/lib/lanes/watcher.js +859 -0
- package/dist/lib/mesh/api.d.ts +71 -7
- package/dist/lib/mesh/api.js +167 -28
- package/dist/lib/mesh/live/backfill-held.d.ts +18 -7
- package/dist/lib/mesh/live/backfill-held.js +66 -21
- package/dist/lib/mesh/live/daemon/config.d.ts +27 -0
- package/dist/lib/mesh/live/daemon/config.js +75 -0
- package/dist/lib/mesh/live/daemon/index.d.ts +4 -3
- package/dist/lib/mesh/live/daemon/index.js +3 -3
- package/dist/lib/mesh/live/daemon/install.d.ts +143 -1
- package/dist/lib/mesh/live/daemon/install.js +444 -2
- package/dist/lib/mesh/live/daemon/paths.d.ts +2 -0
- package/dist/lib/mesh/live/daemon/paths.js +2 -0
- package/dist/lib/mesh/live/daemon/run.d.ts +2 -0
- package/dist/lib/mesh/live/daemon/run.js +111 -54
- package/dist/lib/mesh/live/daemon/state.d.ts +2 -0
- package/dist/lib/mesh/live/daemon/transcript-watch.d.ts +3 -0
- package/dist/lib/mesh/live/daemon/transcript-watch.js +31 -8
- package/dist/lib/scan-packages/index.d.ts +2 -0
- package/dist/lib/scan-packages/index.js +18 -13
- package/dist/lib/work-context/config.d.ts +20 -0
- package/dist/lib/work-context/config.js +26 -5
- package/dist/lib/work-context/reconcile.js +28 -7
- package/dist/lib/work-context/state.d.ts +12 -0
- package/dist/lib/work-context/state.js +131 -3
- package/dist/lib/work-context/windows-acl.d.ts +123 -0
- package/dist/lib/work-context/windows-acl.js +425 -0
- package/dist/utils/pack-contributions.js +5 -4
- 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;
|