@indigoai-us/hq-cli 5.257.0 → 5.259.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 +22 -0
- package/assets/scaffold/core/scripts/checkpoint-stop-gate.sh +20 -7
- package/dist/command-catalog.generated.d.ts +12 -0
- package/dist/command-catalog.generated.js +15 -0
- package/dist/commands/agent-kit.d.ts +8 -6
- package/dist/commands/agent-kit.js +8 -6
- package/dist/commands/index-cmd.js +5 -1
- package/dist/commands/lanes.js +23 -1
- package/dist/commands/packs.js +11 -1
- package/dist/commands/reindex.js +6 -5
- package/dist/lib/agent-kit/paths.d.ts +1 -1
- package/dist/lib/agent-kit/paths.js +1 -1
- package/dist/lib/agent-kit/run/heartbeat.d.ts +4 -6
- package/dist/lib/agent-kit/run/heartbeat.js +4 -6
- package/dist/lib/doctor/checks/runtime-health.d.ts +2 -0
- package/dist/lib/doctor/checks/runtime-health.js +13 -3
- package/dist/lib/lanes/envelope-repair-dispatch.d.ts +26 -0
- package/dist/lib/lanes/envelope-repair-dispatch.js +74 -0
- package/dist/lib/lanes/spawn.d.ts +12 -0
- package/dist/lib/lanes/spawn.js +87 -0
- package/dist/lib/lanes/watcher-main.d.ts +3 -1
- package/dist/lib/lanes/watcher-main.js +27 -17
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.259.0] — 2026-09-28
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- The checkpoint stop gate now recognizes path-qualified `hq core checkpoint`
|
|
10
|
+
commands without treating lookalike text as a checkpoint.
|
|
11
|
+
- `hq doctor --live-runtimes` now invokes Windows npm shims through their `.cmd`
|
|
12
|
+
entry points.
|
|
13
|
+
|
|
14
|
+
## [5.258.0] — 2026-09-28
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Envelope repair runs through the currently installed CLI so a long-lived
|
|
19
|
+
watcher cannot use stale worker-profile validation.
|
|
20
|
+
- Updated the external-agent CLI guide to match the kit services, probe checks,
|
|
21
|
+
shipped skills, and MCP tools.
|
|
22
|
+
- Fixed the Docker agent-kit launcher to supervise configured services through
|
|
23
|
+
`hq agent kit run all`.
|
|
24
|
+
- `hq packages list` and `hq packs list` return an error when run outside an HQ tree.
|
|
25
|
+
- `hq index collections` prints a summary when no collections are expected or registered.
|
|
26
|
+
|
|
5
27
|
## [5.257.0] — 2026-09-28
|
|
6
28
|
|
|
7
29
|
### Added
|
|
@@ -350,7 +350,11 @@ exec 3>&2
|
|
|
350
350
|
else ""
|
|
351
351
|
end;
|
|
352
352
|
def checkpoint_command:
|
|
353
|
-
|
|
353
|
+
# Match the actual command at a shell command boundary. A whitespace
|
|
354
|
+
# boundary alone would accept `echo hq core checkpoint`; allow only the
|
|
355
|
+
# shell wrappers and environment assignments that can invoke hq itself.
|
|
356
|
+
# Quoted assignment values and executable paths may contain spaces.
|
|
357
|
+
test("(^\\s*|[;&|()]\\s*|\\n\\s*)(?:(?:command|exec)\\s+)?(?:env\\s+)?([A-Za-z_][A-Za-z0-9_]*=(?:\"(?:\\\\.|[^\"\\\\])*\"|\\u0027[^\\u0027]*\\u0027|[^\\s;&|()]*)\\s+)*(?:(?:command|exec|env)\\s+)?(\"[^\"]*[/\\\\]hq\"|[^\\s;&|()]*[/\\\\]hq|hq)\\s+core\\s+checkpoint(\\s|$)");
|
|
354
358
|
def strip_quoted:
|
|
355
359
|
# Remove quoted spans so flag text inside a --summary value (e.g.
|
|
356
360
|
# --summary "investigated --gate-probe behavior") is never mistaken for
|
|
@@ -362,12 +366,21 @@ exec 3>&2
|
|
|
362
366
|
def checkpoint_flag($flag):
|
|
363
367
|
strip_quoted | test("(^|\\s)" + $flag + "(\\s|$)");
|
|
364
368
|
def pure_checkpoint:
|
|
365
|
-
# The command is ONLY a checkpoint invocation (
|
|
366
|
-
#
|
|
367
|
-
# `git status; hq core checkpoint
|
|
368
|
-
# tool call, so its exemptions
|
|
369
|
-
|
|
370
|
-
|
|
369
|
+
# The command is ONLY a checkpoint invocation (recognized wrappers and
|
|
370
|
+
# env assignments are allowed, but no other command may be joined by a
|
|
371
|
+
# separator). A compound call like `git status; hq core checkpoint
|
|
372
|
+
# --idle` did other work in the same tool call, so its exemptions do not
|
|
373
|
+
# apply.
|
|
374
|
+
# Normalize a quoted or path-qualified executable to hq before
|
|
375
|
+
# stripping quoted argument values, so a path with spaces still gets
|
|
376
|
+
# the same --idle exemptions.
|
|
377
|
+
(capture("^(?<prefix>\\s*(?:(?:command|exec)\\s+)?(?:env\\s+)?([A-Za-z_][A-Za-z0-9_]*=(?:\"(?:\\\\.|[^\"\\\\])*\"|\\u0027[^\\u0027]*\\u0027|[^\\s;&|()]*)\\s+)*(?:(?:command|exec|env)\\s+)?)(?<executable>\"[^\"]*[/\\\\]hq\"|[^\\s;&|()]*[/\\\\]hq|hq)(?<tail>\\s+core\\s+checkpoint(?:\\s+[^;&|\n]*)?)$")? // false) as $parts
|
|
378
|
+
| if $parts == false then false
|
|
379
|
+
else
|
|
380
|
+
($parts.prefix + "hq" + $parts.tail)
|
|
381
|
+
| strip_quoted
|
|
382
|
+
| test("^\\s*(?:(?:command|exec)\\s+)?(?:env\\s+)?([A-Za-z_][A-Za-z0-9_]*=(?:\"(?:\\\\.|[^\"\\\\])*\"|\\u0027[^\\u0027]*\\u0027|[^\\s;&|()]*)\\s+)*(?:(?:command|exec|env)\\s+)?hq\\s+core\\s+checkpoint([\\s][^;&|\n]*)?$")
|
|
383
|
+
end;
|
|
371
384
|
def gate_feedback_user:
|
|
372
385
|
# This gate surfaces its own Stop feedback as a synthetic user row. That
|
|
373
386
|
# row is a turn boundary for the checkpoint requirement, but NOT for
|
|
@@ -8180,6 +8180,18 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
8180
8180
|
readonly description: "JSON on stdout (always)";
|
|
8181
8181
|
}];
|
|
8182
8182
|
readonly subcommands: readonly [];
|
|
8183
|
+
}, {
|
|
8184
|
+
readonly name: "_spawn-envelope-repair";
|
|
8185
|
+
readonly description: "";
|
|
8186
|
+
readonly aliases: readonly [];
|
|
8187
|
+
readonly hidden: true;
|
|
8188
|
+
readonly usage: "[options]";
|
|
8189
|
+
readonly arguments: readonly [];
|
|
8190
|
+
readonly options: readonly [{
|
|
8191
|
+
readonly flags: "--json";
|
|
8192
|
+
readonly description: "JSON on stdout (always)";
|
|
8193
|
+
}];
|
|
8194
|
+
readonly subcommands: readonly [];
|
|
8183
8195
|
}, {
|
|
8184
8196
|
readonly name: "_monitor-message";
|
|
8185
8197
|
readonly description: "";
|
|
@@ -10581,6 +10581,21 @@ export const COMMAND_CATALOG = [
|
|
|
10581
10581
|
],
|
|
10582
10582
|
"subcommands": []
|
|
10583
10583
|
},
|
|
10584
|
+
{
|
|
10585
|
+
"name": "_spawn-envelope-repair",
|
|
10586
|
+
"description": "",
|
|
10587
|
+
"aliases": [],
|
|
10588
|
+
"hidden": true,
|
|
10589
|
+
"usage": "[options]",
|
|
10590
|
+
"arguments": [],
|
|
10591
|
+
"options": [
|
|
10592
|
+
{
|
|
10593
|
+
"flags": "--json",
|
|
10594
|
+
"description": "JSON on stdout (always)"
|
|
10595
|
+
}
|
|
10596
|
+
],
|
|
10597
|
+
"subcommands": []
|
|
10598
|
+
},
|
|
10584
10599
|
{
|
|
10585
10600
|
"name": "_monitor-message",
|
|
10586
10601
|
"description": "",
|
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
*
|
|
5
5
|
* sync company vault pull loop → component-sync
|
|
6
6
|
* inbox inbox poller (mirror + optional ack) → component-inbox
|
|
7
|
-
* heartbeat 60 s POST /v1/agents/{uid}/heartbeat with
|
|
7
|
+
* heartbeat 60 s POST /v1/agents/{uid}/heartbeat with enabled component status
|
|
8
8
|
*
|
|
9
|
-
* `install` renders one user-level unit per service (LaunchAgent on
|
|
10
|
-
* systemd --user on Linux), each of which just runs
|
|
11
|
-
* `node hq agent kit run <service
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* `install` renders one user-level unit per enabled service (LaunchAgent on
|
|
10
|
+
* macOS, systemd --user on Linux), each of which just runs
|
|
11
|
+
* `node hq agent kit run <service>`. The Docker entrypoint runs
|
|
12
|
+
* `hq agent kit run all`, which supervises the services enabled in kit.json,
|
|
13
|
+
* including sync only when file mirroring is configured. Hosts where the
|
|
14
|
+
* service manager is unusable, such as a missing systemd user session bus or
|
|
15
|
+
* unavailable launchd, fall back to one
|
|
14
16
|
* detached `hq agent kit run all` supervisor (pid in ~/.hq-agent/kit.pid,
|
|
15
17
|
* output in ~/.hq-agent/logs/kit.log). Logs: ~/.hq-agent/logs/<service>.log. Skills:
|
|
16
18
|
* ~/.hq-agent/skills/<name>/SKILL.md.
|
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
*
|
|
5
5
|
* sync company vault pull loop → component-sync
|
|
6
6
|
* inbox inbox poller (mirror + optional ack) → component-inbox
|
|
7
|
-
* heartbeat 60 s POST /v1/agents/{uid}/heartbeat with
|
|
7
|
+
* heartbeat 60 s POST /v1/agents/{uid}/heartbeat with enabled component status
|
|
8
8
|
*
|
|
9
|
-
* `install` renders one user-level unit per service (LaunchAgent on
|
|
10
|
-
* systemd --user on Linux), each of which just runs
|
|
11
|
-
* `node hq agent kit run <service
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* `install` renders one user-level unit per enabled service (LaunchAgent on
|
|
10
|
+
* macOS, systemd --user on Linux), each of which just runs
|
|
11
|
+
* `node hq agent kit run <service>`. The Docker entrypoint runs
|
|
12
|
+
* `hq agent kit run all`, which supervises the services enabled in kit.json,
|
|
13
|
+
* including sync only when file mirroring is configured. Hosts where the
|
|
14
|
+
* service manager is unusable, such as a missing systemd user session bus or
|
|
15
|
+
* unavailable launchd, fall back to one
|
|
14
16
|
* detached `hq agent kit run all` supervisor (pid in ~/.hq-agent/kit.pid,
|
|
15
17
|
* output in ~/.hq-agent/logs/kit.log). Logs: ~/.hq-agent/logs/<service>.log. Skills:
|
|
16
18
|
* ~/.hq-agent/skills/<name>/SKILL.md.
|
|
@@ -131,7 +131,11 @@ export function registerIndexCommand(program, dependencies = defaults) {
|
|
|
131
131
|
.action((options) => {
|
|
132
132
|
const hqRoot = resolveRoot(options.hqRoot);
|
|
133
133
|
const registered = dependencies.listRegisteredCollections(hqRoot);
|
|
134
|
-
|
|
134
|
+
const expected = dependencies.deriveCollections(hqRoot);
|
|
135
|
+
const lines = collectionStatusLines(expected, registered);
|
|
136
|
+
if (lines.length === 0)
|
|
137
|
+
console.log(collectionSummary(expected, registered));
|
|
138
|
+
for (const line of lines)
|
|
135
139
|
console.log(line);
|
|
136
140
|
});
|
|
137
141
|
index
|
package/dist/commands/lanes.js
CHANGED
|
@@ -61,7 +61,7 @@ import { SESSION_MOVE_REASON } from "../lib/lanes/move-account.js";
|
|
|
61
61
|
import { managerStatusGateEnabledForLane, resolveManagerStatusGateForLane, } from "../lib/lanes/manager-status-gate.js";
|
|
62
62
|
import { ensureSessionHome } from "../lib/lanes/session-relink.js";
|
|
63
63
|
import { defaultProbe, validateProfileLaneModel, validateLaneModel, } from "../lib/lanes/models.js";
|
|
64
|
-
import { parseCliModel, refuseOutpostSelfTarget, resolveLaneModel, FOREGROUND_ONLY_RULE, STANDING_AUTHORIZATION, spawnLaneWorker, laneRunDir, readSpawnManifest, stopLaneProcesses, } from "../lib/lanes/spawn.js";
|
|
64
|
+
import { parseCliModel, refuseOutpostSelfTarget, resolveLaneModel, FOREGROUND_ONLY_RULE, STANDING_AUTHORIZATION, spawnLaneWorker, spawnEnvelopeRepairRequest, laneRunDir, readSpawnManifest, stopLaneProcesses, } from "../lib/lanes/spawn.js";
|
|
65
65
|
import { handOffInstallHere } from "../lib/daemon/handoff.js";
|
|
66
66
|
export { spawnLaneWorker };
|
|
67
67
|
export { questionBodyAbsPath };
|
|
@@ -7155,6 +7155,28 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
7155
7155
|
.action(wrap(async (laneId) => {
|
|
7156
7156
|
printJson(await messageHandlers.recoverQueuedMessages(ctx, laneId));
|
|
7157
7157
|
}));
|
|
7158
|
+
lanes
|
|
7159
|
+
.command("_spawn-envelope-repair", { hidden: true })
|
|
7160
|
+
.action(wrap(async () => {
|
|
7161
|
+
let payload;
|
|
7162
|
+
try {
|
|
7163
|
+
payload = JSON.parse(ctx.readStdin());
|
|
7164
|
+
}
|
|
7165
|
+
catch (err) {
|
|
7166
|
+
throw new LanesError("repair_request_invalid", `Envelope repair request is invalid JSON: ${err instanceof Error ? err.message : String(err)}`, {});
|
|
7167
|
+
}
|
|
7168
|
+
printJson(spawnEnvelopeRepairRequest(ctx.hqRoot(), payload, {
|
|
7169
|
+
env: ctx.env,
|
|
7170
|
+
hostname: ctx.hostname(),
|
|
7171
|
+
existsSync: ctx.existsSync,
|
|
7172
|
+
spawnProcess: ctx.spawnProcess,
|
|
7173
|
+
proveDetached: ctx.proveDetached,
|
|
7174
|
+
killProcess: ctx.killProcess,
|
|
7175
|
+
resolveMachineIdentity: ctx.resolveMachineIdentity,
|
|
7176
|
+
runCompaction: ctx.runCompaction,
|
|
7177
|
+
locateTranscript: ctx.locateTranscript,
|
|
7178
|
+
}));
|
|
7179
|
+
}));
|
|
7158
7180
|
lanes
|
|
7159
7181
|
.command("_monitor-message", { hidden: true })
|
|
7160
7182
|
.argument("<lane-id>", "Lane id")
|
package/dist/commands/packs.js
CHANGED
|
@@ -23,11 +23,21 @@ import { spawnSync } from 'child_process';
|
|
|
23
23
|
import chalk from 'chalk';
|
|
24
24
|
import semverSatisfies from 'semver/functions/satisfies.js';
|
|
25
25
|
import { findHqRoot } from '../utils/manifest.js';
|
|
26
|
+
import { resolveHqRoot as resolveDetectedHqRoot } from './doctor.js';
|
|
26
27
|
import { classify, resolveLatest, resolveLatestMarketplace, runScanPackages, installPack, } from './pack-install.js';
|
|
27
28
|
import { contributionLinks, linkStatus, listInstalledPacks, findDependentPacks, readPackManifest, unwirePack, unwirePackMcp, readHqVersion, readRecommendedPackages, packagesDir, } from '../utils/pack-contributions.js';
|
|
28
29
|
function resolveRoot(opts) {
|
|
29
30
|
return opts.hqRoot ? path.resolve(opts.hqRoot) : findHqRoot();
|
|
30
31
|
}
|
|
32
|
+
function resolvePackListRoot(opts) {
|
|
33
|
+
if (opts.hqRoot)
|
|
34
|
+
return path.resolve(opts.hqRoot);
|
|
35
|
+
const hqRoot = resolveDetectedHqRoot(process.cwd());
|
|
36
|
+
if (!hqRoot) {
|
|
37
|
+
throw new Error('hq packages: not inside an HQ tree. Run this command from inside your HQ root or pass --hq-root <path>.');
|
|
38
|
+
}
|
|
39
|
+
return hqRoot;
|
|
40
|
+
}
|
|
31
41
|
/** JSON when --json or when piped (non-TTY); human-readable on a terminal. */
|
|
32
42
|
function wantsJson(opts) {
|
|
33
43
|
return opts.json === true || !process.stdout.isTTY;
|
|
@@ -377,7 +387,7 @@ export function registerPacksListCommand(parent) {
|
|
|
377
387
|
.option('--eval-conditionals', 'Evaluate catalog conditional predicates (runs bash)')
|
|
378
388
|
.action(async (opts) => {
|
|
379
389
|
try {
|
|
380
|
-
const view = await buildListView(
|
|
390
|
+
const view = await buildListView(resolvePackListRoot(opts), !!opts.checkUpdates, !!opts.evalConditionals, !!opts.refresh);
|
|
381
391
|
if (wantsJson(opts))
|
|
382
392
|
emitJson(view);
|
|
383
393
|
else
|
package/dist/commands/reindex.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* hq reindex — surface namespaced skills
|
|
3
|
-
*
|
|
4
|
-
* Codex, Grok, and
|
|
2
|
+
* hq reindex — surface namespaced skills and commands, clean up retired
|
|
3
|
+
* personal-overlay mirror links, capture coding-harness session logs, regenerate
|
|
4
|
+
* the workers registry, and converge HQ-owned hook trust for Codex, Grok, and
|
|
5
|
+
* Claude Code.
|
|
5
6
|
*
|
|
6
|
-
* Thin wrapper over @indigoai-us/hq-cloud's reindex(), which
|
|
7
|
-
*
|
|
7
|
+
* Thin wrapper over @indigoai-us/hq-cloud's native TypeScript reindex(), which
|
|
8
|
+
* runs against the HQ root. This command is what the hq-core
|
|
8
9
|
* reindex hook shim calls on Stop / PostToolUse, and what sync()/rescue() call
|
|
9
10
|
* after they change on-disk sources.
|
|
10
11
|
*
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* ~/.hq-agent/host-key.pub Ed25519 public key, SPKI PEM
|
|
6
6
|
* ~/.hq-agent/machine-creds.json credentials + hostKeyPath/companySlug/apiBaseUrl, 0600
|
|
7
7
|
* ~/.hq-agent/kit.json kit install settings (hq root, intervals)
|
|
8
|
-
* ~/.hq-agent/state/ component-{sync,
|
|
8
|
+
* ~/.hq-agent/state/ component-{sync,inbox}, last-heartbeat.json
|
|
9
9
|
* ~/.hq-agent/inbox/ mirrored inbox items (one JSON per message)
|
|
10
10
|
* ~/.hq-agent/attachments/<scope>/ message attachments fetched on demand (0700 / 0600)
|
|
11
11
|
* ~/.hq-agent/logs/*.log one log per kit service
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* ~/.hq-agent/host-key.pub Ed25519 public key, SPKI PEM
|
|
6
6
|
* ~/.hq-agent/machine-creds.json credentials + hostKeyPath/companySlug/apiBaseUrl, 0600
|
|
7
7
|
* ~/.hq-agent/kit.json kit install settings (hq root, intervals)
|
|
8
|
-
* ~/.hq-agent/state/ component-{sync,
|
|
8
|
+
* ~/.hq-agent/state/ component-{sync,inbox}, last-heartbeat.json
|
|
9
9
|
* ~/.hq-agent/inbox/ mirrored inbox items (one JSON per message)
|
|
10
10
|
* ~/.hq-agent/attachments/<scope>/ message attachments fetched on demand (0700 / 0600)
|
|
11
11
|
* ~/.hq-agent/logs/*.log one log per kit service
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Kit service: heartbeat reporter.
|
|
3
3
|
*
|
|
4
|
-
* Every 60 s POST /v1/agents/{uid}/heartbeat with
|
|
5
|
-
* `
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* beat). Component values are read from the stamps the other three services
|
|
9
|
-
* write; a missing or stale stamp reports `error`, never silence.
|
|
4
|
+
* Every 60 s POST /v1/agents/{uid}/heartbeat with the enabled component status
|
|
5
|
+
* values (`sync` and `inbox`), plus cliVersion and hostInfo. The external-agent
|
|
6
|
+
* body mirrors the hosted box's hq-agent-heartbeat.sh, which folds per-component
|
|
7
|
+
* stamp files into one beat. A missing or stale enabled stamp reports `error`.
|
|
10
8
|
*
|
|
11
9
|
* The last successful beat is recorded at state/last-heartbeat.json so
|
|
12
10
|
* `hq agent probe` can report its age.
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Kit service: heartbeat reporter.
|
|
3
3
|
*
|
|
4
|
-
* Every 60 s POST /v1/agents/{uid}/heartbeat with
|
|
5
|
-
* `
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* beat). Component values are read from the stamps the other three services
|
|
9
|
-
* write; a missing or stale stamp reports `error`, never silence.
|
|
4
|
+
* Every 60 s POST /v1/agents/{uid}/heartbeat with the enabled component status
|
|
5
|
+
* values (`sync` and `inbox`), plus cliVersion and hostInfo. The external-agent
|
|
6
|
+
* body mirrors the hosted box's hq-agent-heartbeat.sh, which folds per-component
|
|
7
|
+
* stamp files into one beat. A missing or stale enabled stamp reports `error`.
|
|
10
8
|
*
|
|
11
9
|
* The last successful beat is recorded at state/last-heartbeat.json so
|
|
12
10
|
* `hq agent probe` can report its age.
|
|
@@ -80,6 +80,8 @@ export interface RuntimeHealthDeps {
|
|
|
80
80
|
execProbe?: (file: string, args: string[], timeoutMs: number) => Promise<ProbeOutcome>;
|
|
81
81
|
/** Per-probe timeout. Default {@link DEFAULT_PROBE_TIMEOUT_MS}. */
|
|
82
82
|
probeTimeoutMs?: number;
|
|
83
|
+
/** Runtime platform override for the spawn plan (normally process.platform). */
|
|
84
|
+
platform?: NodeJS.Platform;
|
|
83
85
|
}
|
|
84
86
|
/**
|
|
85
87
|
* The AI-runtime-health check family entry. Presence checks always run; the
|
|
@@ -29,6 +29,7 @@ import { spawn } from "node:child_process";
|
|
|
29
29
|
import * as fs from "node:fs";
|
|
30
30
|
import * as os from "node:os";
|
|
31
31
|
import * as path from "node:path";
|
|
32
|
+
import { planCommandSpawn } from "../../../utils/windows-spawn.js";
|
|
32
33
|
/** The id of the AI-runtime-health family. */
|
|
33
34
|
export const RUNTIMES_FAMILY_ID = "runtimes";
|
|
34
35
|
/** Common id prefix for every result this family emits. */
|
|
@@ -101,7 +102,8 @@ export async function checkRuntimeHealth(context, deps = {}) {
|
|
|
101
102
|
async function runRuntimeChecks(context, deps) {
|
|
102
103
|
const resolved = {
|
|
103
104
|
resolveBinary: deps.resolveBinary ?? resolveOnPath,
|
|
104
|
-
execProbe: deps.execProbe ??
|
|
105
|
+
execProbe: deps.execProbe ??
|
|
106
|
+
((file, args, timeoutMs) => defaultExecProbe(file, args, timeoutMs, deps.platform ?? process.platform)),
|
|
105
107
|
timeoutMs: deps.probeTimeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS,
|
|
106
108
|
live: context.liveRuntimes === true,
|
|
107
109
|
};
|
|
@@ -268,11 +270,19 @@ export function resolveOnPath(binary) {
|
|
|
268
270
|
* CLI that reads piped stdin in non-TTY mode — blocks forever on a pipe that
|
|
269
271
|
* never reaches EOF, which turns every probe into a timeout.
|
|
270
272
|
*/
|
|
271
|
-
function defaultExecProbe(file, args, timeoutMs) {
|
|
273
|
+
function defaultExecProbe(file, args, timeoutMs, platform) {
|
|
272
274
|
return new Promise((resolve) => {
|
|
273
|
-
const
|
|
275
|
+
const command = platform === "win32" && !/\.(?:cmd|bat|exe|com)$/i.test(file)
|
|
276
|
+
? `${file}.cmd`
|
|
277
|
+
: file;
|
|
278
|
+
const plan = planCommandSpawn(command, args, platform);
|
|
279
|
+
const child = spawn(plan.cmd, plan.args, {
|
|
274
280
|
cwd: os.tmpdir(),
|
|
275
281
|
stdio: ["ignore", "pipe", "pipe"],
|
|
282
|
+
shell: plan.shell,
|
|
283
|
+
...(plan.windowsVerbatimArguments === undefined
|
|
284
|
+
? {}
|
|
285
|
+
: { windowsVerbatimArguments: plan.windowsVerbatimArguments }),
|
|
276
286
|
});
|
|
277
287
|
const cap = 1024 * 1024;
|
|
278
288
|
let stdout = "";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { LaneRecord } from "./types.js";
|
|
2
|
+
import type { EnvelopeRepairSpawnInput } from "./spawn.js";
|
|
3
|
+
interface EnvelopeRepairDispatchResult {
|
|
4
|
+
error?: Error;
|
|
5
|
+
status: number | null;
|
|
6
|
+
signal: NodeJS.Signals | null;
|
|
7
|
+
stdout: string;
|
|
8
|
+
stderr: string;
|
|
9
|
+
}
|
|
10
|
+
interface EnvelopeRepairDispatchOptions {
|
|
11
|
+
encoding: "utf8";
|
|
12
|
+
timeout: number;
|
|
13
|
+
input: string;
|
|
14
|
+
env: NodeJS.ProcessEnv;
|
|
15
|
+
}
|
|
16
|
+
export interface EnvelopeRepairDispatchDeps {
|
|
17
|
+
env?: NodeJS.ProcessEnv;
|
|
18
|
+
spawnSync?: (command: string, args: string[], options: EnvelopeRepairDispatchOptions) => EnvelopeRepairDispatchResult;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Dispatch repair into the currently installed CLI. A watcher can stay alive
|
|
22
|
+
* across upgrades, so repair must not call its in-memory spawn implementation.
|
|
23
|
+
*/
|
|
24
|
+
export declare function spawnEnvelopeRepairViaCurrentCli(hqRoot: string, lane: LaneRecord, input: EnvelopeRepairSpawnInput, deps?: EnvelopeRepairDispatchDeps): number;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=envelope-repair-dispatch.d.ts.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { spawnSync as nodeSpawnSync } from "node:child_process";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { LanesError } from "./errors.js";
|
|
4
|
+
/**
|
|
5
|
+
* Dispatch repair into the currently installed CLI. A watcher can stay alive
|
|
6
|
+
* across upgrades, so repair must not call its in-memory spawn implementation.
|
|
7
|
+
*/
|
|
8
|
+
export function spawnEnvelopeRepairViaCurrentCli(hqRoot, lane, input, deps = {}) {
|
|
9
|
+
const request = {
|
|
10
|
+
lane_id: lane.lane_id,
|
|
11
|
+
input: {
|
|
12
|
+
...(input.session_id !== undefined ? { session_id: input.session_id } : {}),
|
|
13
|
+
envelopePath: input.envelopePath,
|
|
14
|
+
error: input.error,
|
|
15
|
+
rawText: input.rawText,
|
|
16
|
+
...(input.missing !== undefined ? { missing: input.missing } : {}),
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
const run = deps.spawnSync ?? ((command, args, options) => {
|
|
20
|
+
const result = nodeSpawnSync(command, args, options);
|
|
21
|
+
return {
|
|
22
|
+
...(result.error !== undefined ? { error: result.error } : {}),
|
|
23
|
+
status: result.status,
|
|
24
|
+
signal: result.signal,
|
|
25
|
+
stdout: result.stdout ?? "",
|
|
26
|
+
stderr: result.stderr ?? "",
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
const env = deps.env ?? process.env;
|
|
30
|
+
const cliEntry = env.HQ_LANE_CLI_ENTRY?.trim();
|
|
31
|
+
if (!cliEntry || !path.isAbsolute(cliEntry) || path.resolve(cliEntry) !== cliEntry) {
|
|
32
|
+
throw new LanesError("repair_cli_entry_invalid", "Lane watcher has no valid absolute hq CLI entrypoint for envelope repair", { lane_id: lane.lane_id });
|
|
33
|
+
}
|
|
34
|
+
const result = run(process.execPath, [cliEntry, "lanes", "_spawn-envelope-repair"], {
|
|
35
|
+
encoding: "utf8",
|
|
36
|
+
timeout: 120_000,
|
|
37
|
+
input: JSON.stringify(request),
|
|
38
|
+
env: {
|
|
39
|
+
...env,
|
|
40
|
+
HQ_ROOT: hqRoot,
|
|
41
|
+
HQ_NO_UPDATE_CHECK: "1",
|
|
42
|
+
HQ_TELEMETRY: "0",
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
if (result.error) {
|
|
46
|
+
throw new LanesError("repair_cli_spawn_failed", `Current hq CLI could not start the envelope repair: ${result.error.message}`, { lane_id: lane.lane_id });
|
|
47
|
+
}
|
|
48
|
+
if (result.status !== 0) {
|
|
49
|
+
const detail = result.stderr.trim() || `exit ${result.status ?? result.signal ?? "without status"}`;
|
|
50
|
+
throw new LanesError("repair_cli_failed", `Current hq CLI envelope repair failed: ${detail}`, { lane_id: lane.lane_id, status: result.status, signal: result.signal });
|
|
51
|
+
}
|
|
52
|
+
let parsed;
|
|
53
|
+
try {
|
|
54
|
+
parsed = JSON.parse(result.stdout);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
throw new LanesError("repair_cli_result_invalid", `Current hq CLI returned invalid envelope repair JSON: ${err instanceof Error ? err.message : String(err)}`, { lane_id: lane.lane_id });
|
|
58
|
+
}
|
|
59
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
60
|
+
throw new LanesError("repair_cli_result_invalid", "Current hq CLI returned an invalid envelope repair result", { lane_id: lane.lane_id });
|
|
61
|
+
}
|
|
62
|
+
const response = parsed;
|
|
63
|
+
if (!Number.isInteger(response.pid) ||
|
|
64
|
+
Number(response.pid) <= 0 ||
|
|
65
|
+
!Array.isArray(response.profile_warnings) ||
|
|
66
|
+
!response.profile_warnings.every((warning) => Array.isArray(warning) && warning.every((field) => typeof field === "string"))) {
|
|
67
|
+
throw new LanesError("repair_cli_result_invalid", "Current hq CLI returned an invalid envelope repair result", { lane_id: lane.lane_id });
|
|
68
|
+
}
|
|
69
|
+
for (const warning of response.profile_warnings) {
|
|
70
|
+
input.onProfileWarning?.(warning);
|
|
71
|
+
}
|
|
72
|
+
return Number(response.pid);
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=envelope-repair-dispatch.js.map
|
|
@@ -68,6 +68,8 @@ export type ProveDetachedFn = (pid: number) => DetachProof;
|
|
|
68
68
|
export type KillProcessFn = (pid: number, signal?: NodeJS.Signals) => void;
|
|
69
69
|
export interface SpawnLaneDeps {
|
|
70
70
|
env?: NodeJS.ProcessEnv;
|
|
71
|
+
/** Absolute CLI entry captured when the watcher is armed; tests may inject it. */
|
|
72
|
+
hqCliEntry?: string;
|
|
71
73
|
hostname?: string;
|
|
72
74
|
/** The caller's host scope for admission checks; reconcile supplies its resolved host. */
|
|
73
75
|
admissionHost?: AdmissionHost;
|
|
@@ -311,6 +313,16 @@ export interface EnvelopeRepairSpawnInput {
|
|
|
311
313
|
/** Repair-only warning hook for profile metadata ignored during recovery. */
|
|
312
314
|
onProfileWarning?: (fields: readonly string[]) => void;
|
|
313
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Handle the private request sent by an older, long-running watcher to the
|
|
318
|
+
* currently installed `hq` executable. The persisted repair attempt is the
|
|
319
|
+
* authorization for this one-shot spawn; the request cannot select an
|
|
320
|
+
* arbitrary lane or write outside that lane's envelope directory.
|
|
321
|
+
*/
|
|
322
|
+
export declare function spawnEnvelopeRepairRequest(hqRoot: string, rawRequest: unknown, deps?: SpawnLaneDeps): {
|
|
323
|
+
pid: number;
|
|
324
|
+
profile_warnings: string[][];
|
|
325
|
+
};
|
|
314
326
|
/** How the spec §4 repair turn reaches the provider (recorded as `envelope_repair.mode`). */
|
|
315
327
|
export type EnvelopeRepairMode = "resume" | "fresh_turn";
|
|
316
328
|
/**
|
package/dist/lib/lanes/spawn.js
CHANGED
|
@@ -1508,10 +1508,15 @@ export function armLaneWatcher(hqRoot, laneId, deps = {}) {
|
|
|
1508
1508
|
const runDir = laneRunDir(hqRoot, laneId);
|
|
1509
1509
|
const logsDir = path.join(runDir, "logs");
|
|
1510
1510
|
ensureDir(logsDir);
|
|
1511
|
+
const cliEntry = deps.hqCliEntry?.trim() || process.argv[1]?.trim();
|
|
1512
|
+
if (!cliEntry) {
|
|
1513
|
+
throw new LanesError("watcher_cli_entry_unavailable", "Cannot arm the lane watcher without the current hq CLI entrypoint", { lane_id: laneId });
|
|
1514
|
+
}
|
|
1511
1515
|
const watcherPid = spawnDetached(spawnProcess, process.execPath, [watcherMainPath(), laneId], {
|
|
1512
1516
|
...env,
|
|
1513
1517
|
HQ_ROOT: hqRoot,
|
|
1514
1518
|
HQ_LANE_ID: laneId,
|
|
1519
|
+
HQ_LANE_CLI_ENTRY: path.resolve(cliEntry),
|
|
1515
1520
|
}, hqRoot, path.join(logsDir, "watcher.out"), path.join(logsDir, "watcher.err"));
|
|
1516
1521
|
writePidRecord(path.join(runDir, "watcher.pid"), {
|
|
1517
1522
|
pid: watcherPid,
|
|
@@ -2228,6 +2233,88 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
2228
2233
|
}
|
|
2229
2234
|
return laneLaunchResult(readLane(hqRoot, lane.lane_id));
|
|
2230
2235
|
}
|
|
2236
|
+
/**
|
|
2237
|
+
* Handle the private request sent by an older, long-running watcher to the
|
|
2238
|
+
* currently installed `hq` executable. The persisted repair attempt is the
|
|
2239
|
+
* authorization for this one-shot spawn; the request cannot select an
|
|
2240
|
+
* arbitrary lane or write outside that lane's envelope directory.
|
|
2241
|
+
*/
|
|
2242
|
+
export function spawnEnvelopeRepairRequest(hqRoot, rawRequest, deps = {}) {
|
|
2243
|
+
const invalid = (message) => {
|
|
2244
|
+
throw new LanesError("repair_request_invalid", message, {});
|
|
2245
|
+
};
|
|
2246
|
+
if (!rawRequest || typeof rawRequest !== "object" || Array.isArray(rawRequest)) {
|
|
2247
|
+
return invalid("Envelope repair request must be an object");
|
|
2248
|
+
}
|
|
2249
|
+
const request = rawRequest;
|
|
2250
|
+
if (Object.keys(request).some((key) => key !== "lane_id" && key !== "input")) {
|
|
2251
|
+
return invalid("Envelope repair request has unknown fields");
|
|
2252
|
+
}
|
|
2253
|
+
if (typeof request.lane_id !== "string" ||
|
|
2254
|
+
!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(request.lane_id)) {
|
|
2255
|
+
return invalid("Envelope repair request must include a lane_id");
|
|
2256
|
+
}
|
|
2257
|
+
if (!request.input || typeof request.input !== "object" || Array.isArray(request.input)) {
|
|
2258
|
+
return invalid("Envelope repair request input must be an object");
|
|
2259
|
+
}
|
|
2260
|
+
const rawInput = request.input;
|
|
2261
|
+
const allowedInputFields = new Set([
|
|
2262
|
+
"session_id",
|
|
2263
|
+
"envelopePath",
|
|
2264
|
+
"error",
|
|
2265
|
+
"rawText",
|
|
2266
|
+
"missing",
|
|
2267
|
+
]);
|
|
2268
|
+
if (Object.keys(rawInput).some((key) => !allowedInputFields.has(key))) {
|
|
2269
|
+
return invalid("Envelope repair request input has unknown fields");
|
|
2270
|
+
}
|
|
2271
|
+
if (typeof rawInput.envelopePath !== "string" ||
|
|
2272
|
+
!path.isAbsolute(rawInput.envelopePath) ||
|
|
2273
|
+
path.resolve(rawInput.envelopePath) !== rawInput.envelopePath ||
|
|
2274
|
+
typeof rawInput.error !== "string" ||
|
|
2275
|
+
!rawInput.error.trim() ||
|
|
2276
|
+
typeof rawInput.rawText !== "string" ||
|
|
2277
|
+
(rawInput.session_id !== undefined &&
|
|
2278
|
+
rawInput.session_id !== null &&
|
|
2279
|
+
(typeof rawInput.session_id !== "string" || !rawInput.session_id.trim())) ||
|
|
2280
|
+
(rawInput.missing !== undefined && typeof rawInput.missing !== "boolean")) {
|
|
2281
|
+
return invalid("Envelope repair request input has invalid fields");
|
|
2282
|
+
}
|
|
2283
|
+
const lane = readLane(hqRoot, request.lane_id);
|
|
2284
|
+
const repair = lane.envelope_repair;
|
|
2285
|
+
if (lane.state !== "running" || !repair?.attempted || repair.succeeded) {
|
|
2286
|
+
throw new LanesError("repair_request_not_authorized", `Lane ${lane.lane_id} has no pending envelope repair attempt`, { lane_id: lane.lane_id });
|
|
2287
|
+
}
|
|
2288
|
+
if (repair.first_error !== rawInput.error) {
|
|
2289
|
+
throw new LanesError("repair_request_stale", `Lane ${lane.lane_id} envelope repair request no longer matches its recorded error`, { lane_id: lane.lane_id });
|
|
2290
|
+
}
|
|
2291
|
+
const envelopeDir = path.resolve(laneRunDir(hqRoot, lane.lane_id), "envelopes");
|
|
2292
|
+
const envelopePath = path.resolve(rawInput.envelopePath);
|
|
2293
|
+
if (path.dirname(envelopePath) !== envelopeDir ||
|
|
2294
|
+
!path.basename(envelopePath).endsWith(".json") ||
|
|
2295
|
+
typeof repair.envelope_ref !== "string" ||
|
|
2296
|
+
path.posix.dirname(repair.envelope_ref) !== "envelopes" ||
|
|
2297
|
+
!path.posix.basename(repair.envelope_ref).endsWith(".json")) {
|
|
2298
|
+
throw new LanesError("repair_request_path_invalid", `Lane ${lane.lane_id} envelope repair request does not name a lane envelope`, { lane_id: lane.lane_id });
|
|
2299
|
+
}
|
|
2300
|
+
const recordedEnvelopePath = path.resolve(laneRunDir(hqRoot, lane.lane_id), repair.envelope_ref);
|
|
2301
|
+
if (envelopePath !== recordedEnvelopePath) {
|
|
2302
|
+
throw new LanesError("repair_request_envelope_mismatch", `Lane ${lane.lane_id} envelope repair request does not match the recorded repair slot`, { lane_id: lane.lane_id, envelope_ref: repair.envelope_ref });
|
|
2303
|
+
}
|
|
2304
|
+
const warnings = [];
|
|
2305
|
+
const input = {
|
|
2306
|
+
envelopePath,
|
|
2307
|
+
error: rawInput.error,
|
|
2308
|
+
rawText: rawInput.rawText,
|
|
2309
|
+
...(typeof rawInput.session_id === "string" || rawInput.session_id === null
|
|
2310
|
+
? { session_id: rawInput.session_id }
|
|
2311
|
+
: {}),
|
|
2312
|
+
...(typeof rawInput.missing === "boolean" ? { missing: rawInput.missing } : {}),
|
|
2313
|
+
onProfileWarning: (fields) => warnings.push([...fields]),
|
|
2314
|
+
};
|
|
2315
|
+
const pid = spawnEnvelopeRepair(hqRoot, lane, input, deps);
|
|
2316
|
+
return { pid, profile_warnings: warnings };
|
|
2317
|
+
}
|
|
2231
2318
|
/**
|
|
2232
2319
|
* `resume`: claude and codex continue the recorded session with the repair
|
|
2233
2320
|
* prompt. `fresh_turn`: grok gets a new single-turn invocation on the same
|
|
@@ -5,5 +5,7 @@
|
|
|
5
5
|
* Usage: node watcher-main.js <lane-id>
|
|
6
6
|
* Requires HQ_ROOT in the environment.
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
import { type EnvelopeRepairDispatchDeps } from "./envelope-repair-dispatch.js";
|
|
9
|
+
import type { WatcherLoopDeps } from "./watcher.js";
|
|
10
|
+
export declare function watcherMainDeps(hqRoot: string, laneId: string, env?: NodeJS.ProcessEnv, repairDispatchDeps?: Pick<EnvelopeRepairDispatchDeps, "spawnSync">): WatcherLoopDeps;
|
|
9
11
|
//# sourceMappingURL=watcher-main.d.ts.map
|
|
@@ -6,12 +6,33 @@
|
|
|
6
6
|
* Requires HQ_ROOT in the environment.
|
|
7
7
|
*/
|
|
8
8
|
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
9
10
|
import * as path from "node:path";
|
|
10
11
|
import { fileURLToPath } from "node:url";
|
|
11
12
|
import { runWatcherLoop } from "./watcher.js";
|
|
13
|
+
import { spawnEnvelopeRepairViaCurrentCli, } from "./envelope-repair-dispatch.js";
|
|
12
14
|
import { LanesError } from "./errors.js";
|
|
13
15
|
import { openMeshTransport } from "../mesh/client.js";
|
|
14
16
|
import { CompanySelectionError } from "../../utils/company-selection-error.js";
|
|
17
|
+
export function watcherMainDeps(hqRoot, laneId, env = process.env, repairDispatchDeps = {}) {
|
|
18
|
+
const cliEntry = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../index.js");
|
|
19
|
+
return {
|
|
20
|
+
resolveQuestionActiveCompany,
|
|
21
|
+
spawnRepair: (root, lane, input) => spawnEnvelopeRepairViaCurrentCli(root, lane, input, { ...repairDispatchDeps, env }),
|
|
22
|
+
recoverQueuedMessages: async () => {
|
|
23
|
+
const result = spawnSync(process.execPath, [cliEntry, "lanes", "_recover-queued-messages", laneId], {
|
|
24
|
+
env,
|
|
25
|
+
encoding: "utf8",
|
|
26
|
+
timeout: 120_000,
|
|
27
|
+
});
|
|
28
|
+
if (result.error)
|
|
29
|
+
throw result.error;
|
|
30
|
+
if (result.status !== 0) {
|
|
31
|
+
throw new Error(`recovery command exited ${result.status ?? result.signal ?? "without status"}: ${(result.stderr ?? "").trim()}`);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
15
36
|
async function resolveQuestionActiveCompany() {
|
|
16
37
|
try {
|
|
17
38
|
const { company } = await openMeshTransport({});
|
|
@@ -30,25 +51,14 @@ function main() {
|
|
|
30
51
|
process.stderr.write("hq lanes watcher: requires lane id argv and HQ_ROOT in the environment\n");
|
|
31
52
|
process.exit(1);
|
|
32
53
|
}
|
|
33
|
-
|
|
34
|
-
void runWatcherLoop(hqRoot, laneId, {
|
|
35
|
-
resolveQuestionActiveCompany,
|
|
36
|
-
recoverQueuedMessages: async () => {
|
|
37
|
-
const result = spawnSync(process.execPath, [cliEntry, "lanes", "_recover-queued-messages", laneId], {
|
|
38
|
-
env: process.env,
|
|
39
|
-
encoding: "utf8",
|
|
40
|
-
timeout: 120_000,
|
|
41
|
-
});
|
|
42
|
-
if (result.error)
|
|
43
|
-
throw result.error;
|
|
44
|
-
if (result.status !== 0) {
|
|
45
|
-
throw new Error(`recovery command exited ${result.status ?? result.signal ?? "without status"}: ${(result.stderr ?? "").trim()}`);
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
}).catch((err) => {
|
|
54
|
+
void runWatcherLoop(hqRoot, laneId, watcherMainDeps(hqRoot, laneId, process.env)).catch((err) => {
|
|
49
55
|
process.stderr.write(`hq lanes watcher: ${err instanceof Error ? err.message : String(err)}${err instanceof Error && err.stack ? `\n${err.stack}` : ""}\n`);
|
|
50
56
|
process.exit(1);
|
|
51
57
|
});
|
|
52
58
|
}
|
|
53
|
-
|
|
59
|
+
const invokedEntryPath = process.argv[1] ? path.resolve(process.argv[1]) : undefined;
|
|
60
|
+
const currentModule = realpathSync(fileURLToPath(import.meta.url));
|
|
61
|
+
if (invokedEntryPath && existsSync(invokedEntryPath) && realpathSync(invokedEntryPath) === currentModule) {
|
|
62
|
+
main();
|
|
63
|
+
}
|
|
54
64
|
//# sourceMappingURL=watcher-main.js.map
|