@indigoai-us/hq-cli 5.345.198 → 5.345.200
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 +71 -0
- package/assets/scaffold/core/scripts/lib/session-scope-capability.sh +6 -4
- package/dist/command-catalog.generated.d.ts +150 -6
- package/dist/command-catalog.generated.js +182 -6
- package/dist/commands/bot-events.d.ts +19 -0
- package/dist/commands/bot-events.js +46 -0
- package/dist/commands/bot-jobs.d.ts +2 -0
- package/dist/commands/bot-jobs.js +43 -2
- package/dist/commands/bot-runtime.d.ts +35 -0
- package/dist/commands/bot-runtime.js +115 -0
- package/dist/commands/bot.d.ts +11 -0
- package/dist/commands/bot.js +72 -11
- package/dist/commands/creators.js +2 -1
- package/dist/commands/lanes-report.d.ts +2 -1
- package/dist/commands/lanes-report.js +44 -1
- package/dist/commands/lanes.d.ts +9 -0
- package/dist/commands/lanes.js +95 -9
- package/dist/commands/library-remove.js +2 -1
- package/dist/commands/library.js +5 -4
- package/dist/commands/meetings.js +1 -21
- package/dist/commands/mesh.d.ts +0 -7
- package/dist/commands/mesh.js +5 -1
- package/dist/commands/signals-run.d.ts +2 -0
- package/dist/commands/signals-run.js +6 -1
- package/dist/commands/sync-held.js +2 -2
- package/dist/lanes-drain-hook.js +9 -0
- package/dist/lib/bot/api.d.ts +5 -0
- package/dist/lib/bot/claude-login.d.ts +31 -0
- package/dist/lib/bot/claude-login.js +160 -0
- package/dist/lib/bot/events.d.ts +18 -0
- package/dist/lib/bot/events.js +160 -0
- package/dist/lib/bot/executive-settings.d.ts +10 -0
- package/dist/lib/bot/executive-settings.js +43 -0
- package/dist/lib/bot/fleet-cron.js +4 -0
- package/dist/lib/bot/fleet-download.d.ts +27 -0
- package/dist/lib/bot/fleet-download.js +162 -0
- package/dist/lib/bot/fleet-local.d.ts +51 -2
- package/dist/lib/bot/fleet-local.js +179 -76
- package/dist/lib/bot/fleet-python.d.ts +2 -0
- package/dist/lib/bot/fleet-python.js +29 -0
- package/dist/lib/bot/job-run.js +1 -1
- package/dist/lib/bot/jobs/hot-signal-sync.d.ts +8 -0
- package/dist/lib/bot/jobs/hot-signal-sync.js +2 -0
- package/dist/lib/bot/jobs/meeting-detect.d.ts +28 -0
- package/dist/lib/bot/jobs/meeting-detect.js +176 -0
- package/dist/lib/bot/jobs/meeting-rider.d.ts +53 -0
- package/dist/lib/bot/jobs/meeting-rider.js +284 -0
- package/dist/lib/bot/jobs/signal-to-lane.d.ts +32 -0
- package/dist/lib/bot/jobs/signal-to-lane.js +169 -0
- package/dist/lib/bot/local-jobs.d.ts +15 -2
- package/dist/lib/bot/local-jobs.js +24 -7
- package/dist/lib/bot/run.d.ts +7 -49
- package/dist/lib/bot/run.js +100 -4
- package/dist/lib/bot/status.d.ts +21 -0
- package/dist/lib/doctor/checks/bots.d.ts +6 -0
- package/dist/lib/doctor/checks/bots.js +47 -1
- package/dist/lib/hot-channels/adapters/meetings.d.ts +1 -0
- package/dist/lib/hot-channels/adapters/meetings.js +3 -1
- package/dist/lib/hot-channels/meeting-source-ref.d.ts +3 -0
- package/dist/lib/hot-channels/meeting-source-ref.js +7 -0
- package/dist/lib/hot-channels/schedule.js +2 -2
- package/dist/lib/hot-channels/signals-pass.d.ts +9 -32
- package/dist/lib/hot-channels/signals-pass.js +51 -17
- package/dist/lib/lanes/admission.d.ts +2 -0
- package/dist/lib/lanes/admission.js +4 -0
- package/dist/lib/lanes/approval-gate.js +4 -1
- package/dist/lib/lanes/links.d.ts +8 -1
- package/dist/lib/lanes/links.js +84 -3
- package/dist/lib/lanes/mesh-events.js +3 -1
- package/dist/lib/lanes/outcome.d.ts +154 -0
- package/dist/lib/lanes/outcome.js +151 -0
- package/dist/lib/lanes/reconcile.js +1 -0
- package/dist/lib/lanes/signal-brief-template.d.ts +7 -0
- package/dist/lib/lanes/signal-brief-template.js +40 -0
- package/dist/lib/lanes/signal-lane.d.ts +24 -0
- package/dist/lib/lanes/signal-lane.js +99 -0
- package/dist/lib/lanes/source-report.d.ts +37 -0
- package/dist/lib/lanes/source-report.js +66 -0
- package/dist/lib/lanes/spawn.d.ts +6 -0
- package/dist/lib/lanes/spawn.js +21 -0
- package/dist/lib/lanes/store.d.ts +3 -0
- package/dist/lib/lanes/store.js +64 -0
- package/dist/lib/lanes/story-ledger.d.ts +3 -0
- package/dist/lib/lanes/story-ledger.js +1 -0
- package/dist/lib/lanes/types.d.ts +137 -0
- package/dist/lib/lanes/types.js +17 -2
- package/dist/lib/lanes/watcher.js +4 -0
- package/dist/lib/live-meetings.d.ts +19 -0
- package/dist/lib/live-meetings.js +23 -0
- package/dist/lib/mesh/api.js +2 -1
- package/dist/lib/mesh/live/daemon/doctor.js +6 -7
- package/dist/lib/mesh/live/daemon/run.d.ts +0 -10
- package/dist/lib/mesh/live/daemon/run.js +3 -0
- package/dist/lib/mesh/live/emit.d.ts +2 -11
- package/dist/lib/mesh/live/emit.js +20 -2
- package/dist/lib/mesh/live/flush.d.ts +2 -0
- package/dist/lib/mesh/live/flush.js +34 -3
- package/dist/lib/mesh/live/reporting-privacy.d.ts +7 -0
- package/dist/lib/mesh/live/reporting-privacy.js +47 -0
- package/dist/lib/mesh/live/spool.d.ts +8 -0
- package/dist/lib/mesh/live/spool.js +59 -40
- package/dist/lib/plan-limit-nag.d.ts +30 -4
- package/dist/lib/plan-limit-nag.js +68 -17
- package/dist/lib/signals/engine.d.ts +4 -0
- package/dist/lib/signals/engine.js +25 -5
- package/dist/lib/signals/incremental.d.ts +42 -0
- package/dist/lib/signals/incremental.js +191 -0
- package/dist/lib/signals/prior-signals.d.ts +11 -0
- package/dist/lib/signals/prior-signals.js +44 -0
- package/dist/lib/signals/reply.js +6 -1
- package/dist/lib/signals/settings.d.ts +5 -0
- package/dist/lib/signals/settings.js +17 -0
- package/dist/lib/signals/steps/extract.d.ts +23 -2
- package/dist/lib/signals/steps/extract.js +142 -9
- package/dist/lib/signals/steps/types.d.ts +1 -0
- package/dist/lib/signals/write-guard.js +7 -0
- package/dist/lib/work-context/company.d.ts +2 -3
- package/dist/lib/work-context/company.js +4 -22
- package/dist/secrets-env-action.js +5 -2
- package/dist/utils/cognito-callback-port.d.ts +17 -0
- package/dist/utils/cognito-callback-port.js +56 -0
- package/dist/utils/cognito-config.js +7 -3
- package/dist/utils/cognito-session.d.ts +7 -6
- package/dist/utils/cognito-session.js +19 -14
- package/dist/utils/interactive-caller.d.ts +23 -0
- package/dist/utils/interactive-caller.js +80 -0
- package/dist/utils/login-browser.js +5 -2
- package/dist/utils/resident-package-preload.js +4 -0
- package/package.json +7 -7
|
@@ -6,6 +6,7 @@ import * as path from "node:path";
|
|
|
6
6
|
import * as zlib from "node:zlib";
|
|
7
7
|
import { HQ_FLEET_RUNTIME_ENGINE_ID, } from "./config.js";
|
|
8
8
|
import { withBotLock } from "./lock.js";
|
|
9
|
+
import { downloadFleetFile, fileSha256 } from "./fleet-download.js";
|
|
9
10
|
import { ensureFleetPython, fleetUvEnv } from "./fleet-python.js";
|
|
10
11
|
import semver from "semver";
|
|
11
12
|
import { CLI_VERSION } from "../../cli-version.js";
|
|
@@ -79,15 +80,15 @@ export function sharedRuntimeLayout(root, id, platform) {
|
|
|
79
80
|
}
|
|
80
81
|
export const HQ_FLEET_LOCAL_RELEASE = {
|
|
81
82
|
repository: "indigoai-us/hq-fleet-local",
|
|
82
|
-
tag: "v0.4.
|
|
83
|
-
version: "0.4.
|
|
84
|
-
artifact: "hq-fleet-local-0.4.
|
|
85
|
-
artifactSha256: "
|
|
86
|
-
runtimeArtifact: "hq-fleet-runtime-
|
|
87
|
-
runtimeArtifactSha256: "
|
|
88
|
-
manifestSha256: "
|
|
89
|
-
signatureSha256: "
|
|
90
|
-
runtimeCommit: "
|
|
83
|
+
tag: "v0.4.25",
|
|
84
|
+
version: "0.4.25",
|
|
85
|
+
artifact: "hq-fleet-local-0.4.25.zip",
|
|
86
|
+
artifactSha256: "4d32e04f992600ab0f1656de3536358dfdba77bfccdc23e1b01ba7a0de4ea38d",
|
|
87
|
+
runtimeArtifact: "hq-fleet-runtime-d257fdd1171f.tar.gz",
|
|
88
|
+
runtimeArtifactSha256: "4d26d250eb100efff280bcd254e06e3a2993875c1fff4eec1733eaf99e52cd70",
|
|
89
|
+
manifestSha256: "d7e3a3309bf60e6366b192c9131d44166edf63cb5a60ca7923fe30145fc890f0",
|
|
90
|
+
signatureSha256: "1b018c39e69aefa26a0b1f95ed5d97388fd4747851643c8a53cae3069c670c0a",
|
|
91
|
+
runtimeCommit: "d257fdd1171f1edd5e381b3bad7baacab0cb00d3",
|
|
91
92
|
};
|
|
92
93
|
/** hq-fleet-local trust/release-public.pem (rotated 2026-10-30; signed v0.4.17). */
|
|
93
94
|
export const HQ_FLEET_LOCAL_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
|
@@ -470,7 +471,8 @@ export function parseFleetLocalReleasePin(value) {
|
|
|
470
471
|
return null;
|
|
471
472
|
if (!COMMIT_RE.test(pin.runtimeCommit))
|
|
472
473
|
return null;
|
|
473
|
-
|
|
474
|
+
const runtimeStem = `hq-fleet-runtime-${pin.runtimeCommit.slice(0, 12)}`;
|
|
475
|
+
if (pin.runtimeArtifact !== `${runtimeStem}.tar` && pin.runtimeArtifact !== `${runtimeStem}.tar.gz`)
|
|
474
476
|
return null;
|
|
475
477
|
if (![pin.artifactSha256, pin.runtimeArtifactSha256, pin.manifestSha256, pin.signatureSha256].every((hash) => SHA256_RE.test(hash))) {
|
|
476
478
|
return null;
|
|
@@ -637,38 +639,67 @@ function trustedReleaseLink(url) {
|
|
|
637
639
|
return false;
|
|
638
640
|
}
|
|
639
641
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
+
export const FLEET_LOCAL_METADATA_DOWNLOAD_TIMEOUT_MS = 30_000;
|
|
643
|
+
/** One machine-wide lock: concurrent creates and adopts share one download instead of repeating it. */
|
|
644
|
+
export const FLEET_LOCAL_DISTRIBUTION_LOCK_NAME = "hq-fleet-local-distribution";
|
|
645
|
+
export const FLEET_LOCAL_DISTRIBUTION_LOCK_WAIT_MS = 30 * 60_000;
|
|
646
|
+
export const FLEET_LOCAL_DISTRIBUTION_LOCK_STALE_MS = 60 * 60_000;
|
|
647
|
+
const RELEASE_FILE_NAMES = (release) => [
|
|
648
|
+
release.artifact,
|
|
649
|
+
release.runtimeArtifact,
|
|
650
|
+
"release-manifest.json",
|
|
651
|
+
"release-manifest.sig",
|
|
652
|
+
];
|
|
653
|
+
function downloadsDir(root, release) {
|
|
654
|
+
return path.join(root, "distributions", "hq-fleet-local", ".downloads", release.version);
|
|
655
|
+
}
|
|
656
|
+
function discardDownloads(target) {
|
|
657
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
658
|
+
try {
|
|
659
|
+
fs.rmdirSync(path.dirname(target));
|
|
660
|
+
}
|
|
661
|
+
catch {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
async function downloadReleaseFiles(release, fetcher, resolve, cachedRuntime, target, onProgress, stallMs) {
|
|
666
|
+
fs.mkdirSync(target, { recursive: true, mode: 0o700 });
|
|
667
|
+
const downloaded = new Map();
|
|
668
|
+
for (const name of RELEASE_FILE_NAMES(release)) {
|
|
669
|
+
if (name === release.runtimeArtifact && cachedRuntime) {
|
|
670
|
+
downloaded.set(name, cachedRuntime);
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
const { url, headers } = resolve(name);
|
|
674
|
+
const file = await downloadFleetFile(url, path.join(target, name), {
|
|
675
|
+
fetch: fetcher,
|
|
676
|
+
...(headers ? { headers } : {}),
|
|
677
|
+
...(stallMs !== undefined ? { stallMs } : {}),
|
|
678
|
+
onProgress: (progress) => onProgress?.({ phase: "download", file: name, received: progress.received, total: progress.total }),
|
|
679
|
+
});
|
|
680
|
+
downloaded.set(name, file);
|
|
681
|
+
}
|
|
682
|
+
return {
|
|
683
|
+
archive: fs.readFileSync(downloaded.get(release.artifact)),
|
|
684
|
+
runtimeArchive: fs.readFileSync(downloaded.get(release.runtimeArtifact)),
|
|
685
|
+
manifest: fs.readFileSync(downloaded.get("release-manifest.json")),
|
|
686
|
+
signature: fs.readFileSync(downloaded.get("release-manifest.sig")),
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
async function downloadFleetLocalReleaseFromHq(release, fetcher, links, cachedRuntime, target, onProgress, stallMs) {
|
|
690
|
+
for (const name of RELEASE_FILE_NAMES(release)) {
|
|
691
|
+
if (name === release.runtimeArtifact && cachedRuntime)
|
|
692
|
+
continue;
|
|
642
693
|
const url = links.get(name);
|
|
643
694
|
if (!url)
|
|
644
695
|
throw expectedError(`HQ Fleet Local ${release.tag} is missing release asset ${name}.`);
|
|
645
696
|
if (!trustedReleaseLink(url)) {
|
|
646
697
|
throw expectedError(`HQ Fleet Local ${release.tag} returned an untrusted download link for ${name}.`);
|
|
647
698
|
}
|
|
648
|
-
const response = await fetcher(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
649
|
-
if (!response.ok) {
|
|
650
|
-
throw expectedError(`Could not download ${name}: HQ storage returned HTTP ${response.status}.`);
|
|
651
|
-
}
|
|
652
|
-
return Buffer.from(await response.arrayBuffer());
|
|
653
|
-
};
|
|
654
|
-
const [archive, runtimeArchive, manifest, signature] = await Promise.all([
|
|
655
|
-
get(release.artifact, FLEET_LOCAL_ARCHIVE_DOWNLOAD_TIMEOUT_MS),
|
|
656
|
-
cachedRuntime ?? get(release.runtimeArtifact, FLEET_LOCAL_RUNTIME_DOWNLOAD_TIMEOUT_MS),
|
|
657
|
-
get("release-manifest.json", FLEET_LOCAL_METADATA_DOWNLOAD_TIMEOUT_MS),
|
|
658
|
-
get("release-manifest.sig", FLEET_LOCAL_METADATA_DOWNLOAD_TIMEOUT_MS),
|
|
659
|
-
]);
|
|
660
|
-
return { archive, runtimeArchive, manifest, signature };
|
|
661
|
-
}
|
|
662
|
-
async function responseBytes(response, label) {
|
|
663
|
-
if (!response.ok) {
|
|
664
|
-
throw expectedError(`Could not download ${label}: GitHub returned HTTP ${response.status}.`);
|
|
665
699
|
}
|
|
666
|
-
return
|
|
700
|
+
return downloadReleaseFiles(release, fetcher, (name) => ({ url: links.get(name) }), cachedRuntime, target, onProgress, stallMs);
|
|
667
701
|
}
|
|
668
|
-
|
|
669
|
-
export const FLEET_LOCAL_ARCHIVE_DOWNLOAD_TIMEOUT_MS = 5 * 60_000;
|
|
670
|
-
export const FLEET_LOCAL_RUNTIME_DOWNLOAD_TIMEOUT_MS = 20 * 60_000;
|
|
671
|
-
async function downloadFleetLocalRelease(release, fetcher, token, cachedRuntime) {
|
|
702
|
+
async function downloadFleetLocalRelease(release, fetcher, token, cachedRuntime, target, onProgress, stallMs) {
|
|
672
703
|
const headers = {
|
|
673
704
|
Accept: "application/vnd.github+json",
|
|
674
705
|
Authorization: `Bearer ${token}`,
|
|
@@ -676,7 +707,7 @@ async function downloadFleetLocalRelease(release, fetcher, token, cachedRuntime)
|
|
|
676
707
|
"X-GitHub-Api-Version": "2022-11-28",
|
|
677
708
|
};
|
|
678
709
|
const releaseUrl = `https://api.github.com/repos/${release.repository}/releases/tags/${release.tag}`;
|
|
679
|
-
const response = await fetcher(releaseUrl, { headers, signal: AbortSignal.timeout(
|
|
710
|
+
const response = await fetcher(releaseUrl, { headers, signal: AbortSignal.timeout(FLEET_LOCAL_METADATA_DOWNLOAD_TIMEOUT_MS) });
|
|
680
711
|
if (!response.ok) {
|
|
681
712
|
throw expectedError(`Could not read HQ Fleet Local ${release.tag}: GitHub returned HTTP ${response.status}.`);
|
|
682
713
|
}
|
|
@@ -684,25 +715,17 @@ async function downloadFleetLocalRelease(release, fetcher, token, cachedRuntime)
|
|
|
684
715
|
const assets = new Map((body.assets ?? [])
|
|
685
716
|
.filter((asset) => !!asset.name && !!asset.url)
|
|
686
717
|
.map((asset) => [asset.name, asset.url]));
|
|
687
|
-
const
|
|
718
|
+
for (const name of RELEASE_FILE_NAMES(release)) {
|
|
719
|
+
if (name === release.runtimeArtifact && cachedRuntime)
|
|
720
|
+
continue;
|
|
688
721
|
const url = assets.get(name);
|
|
689
722
|
if (!url)
|
|
690
723
|
throw expectedError(`HQ Fleet Local ${release.tag} is missing release asset ${name}.`);
|
|
691
724
|
if (new URL(url).origin !== "https://api.github.com") {
|
|
692
725
|
throw expectedError(`HQ Fleet Local ${release.tag} returned an untrusted asset URL for ${name}.`);
|
|
693
726
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
697
|
-
}), name);
|
|
698
|
-
};
|
|
699
|
-
const [archive, runtimeArchive, manifest, signature] = await Promise.all([
|
|
700
|
-
get(release.artifact, FLEET_LOCAL_ARCHIVE_DOWNLOAD_TIMEOUT_MS),
|
|
701
|
-
cachedRuntime ?? get(release.runtimeArtifact, FLEET_LOCAL_RUNTIME_DOWNLOAD_TIMEOUT_MS),
|
|
702
|
-
get("release-manifest.json", FLEET_LOCAL_METADATA_DOWNLOAD_TIMEOUT_MS),
|
|
703
|
-
get("release-manifest.sig", FLEET_LOCAL_METADATA_DOWNLOAD_TIMEOUT_MS),
|
|
704
|
-
]);
|
|
705
|
-
return { archive, runtimeArchive, manifest, signature };
|
|
727
|
+
}
|
|
728
|
+
return downloadReleaseFiles(release, fetcher, (name) => ({ url: assets.get(name), headers: { ...headers, Accept: "application/octet-stream" } }), cachedRuntime, target, onProgress, stallMs);
|
|
706
729
|
}
|
|
707
730
|
function installedFiles(dir, release) {
|
|
708
731
|
const archive = path.join(dir, release.artifact);
|
|
@@ -749,49 +772,83 @@ function installFiles(target, files, release, publicKey) {
|
|
|
749
772
|
fs.rmSync(staging, { recursive: true, force: true });
|
|
750
773
|
}
|
|
751
774
|
}
|
|
775
|
+
/** A runtime archive already on this machine whose bytes match the pin: any installed release, or a finished download. */
|
|
752
776
|
function cachedRuntimeArchive(root, release) {
|
|
753
777
|
const distributions = path.join(root, "distributions", "hq-fleet-local");
|
|
754
778
|
if (!fs.existsSync(distributions))
|
|
755
779
|
return null;
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
continue;
|
|
759
|
-
const directory = path.join(distributions, entry.name);
|
|
780
|
+
const candidates = [];
|
|
781
|
+
const visit = (directory) => {
|
|
760
782
|
for (const file of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
761
|
-
|
|
783
|
+
const full = path.join(directory, file.name);
|
|
784
|
+
if (file.isDirectory()) {
|
|
785
|
+
if (file.name === ".downloads" || !file.name.startsWith("."))
|
|
786
|
+
visit(full);
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
if (!file.isFile() || !file.name.startsWith("hq-fleet-runtime-"))
|
|
762
790
|
continue;
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
791
|
+
if (!file.name.endsWith(".tar") && !file.name.endsWith(".tar.gz"))
|
|
792
|
+
continue;
|
|
793
|
+
candidates.push(full);
|
|
766
794
|
}
|
|
795
|
+
};
|
|
796
|
+
visit(distributions);
|
|
797
|
+
for (const candidate of candidates) {
|
|
798
|
+
if (fileSha256(candidate) === release.runtimeArtifactSha256)
|
|
799
|
+
return candidate;
|
|
767
800
|
}
|
|
768
801
|
return null;
|
|
769
802
|
}
|
|
803
|
+
/** Whether the pinned release is installed on this machine (no download needed). */
|
|
804
|
+
export function fleetLocalDistributionInstalled(release = HQ_FLEET_LOCAL_RELEASE, root = fleetHome()) {
|
|
805
|
+
return installedFiles(path.join(root, "distributions", "hq-fleet-local", release.version), release) !== null;
|
|
806
|
+
}
|
|
770
807
|
export async function installPinnedFleetLocalDistribution(deps = {}) {
|
|
771
808
|
const release = deps.release ?? HQ_FLEET_LOCAL_RELEASE;
|
|
772
809
|
const publicKey = deps.publicKey ?? HQ_FLEET_LOCAL_PUBLIC_KEYS;
|
|
773
810
|
const root = deps.fleetHome ?? fleetHome();
|
|
774
811
|
const dir = path.join(root, "distributions", "hq-fleet-local", release.version);
|
|
775
|
-
const
|
|
812
|
+
const onProgress = deps.onProgress;
|
|
776
813
|
const warnCompatibility = (manifest) => {
|
|
777
814
|
const warning = fleetLocalCliCompatibilityWarning(manifest, release);
|
|
778
815
|
if (warning)
|
|
779
816
|
process.stderr.write(`Warning: ${warning}\n`);
|
|
780
817
|
};
|
|
818
|
+
const installation = () => ({
|
|
819
|
+
release,
|
|
820
|
+
dir,
|
|
821
|
+
archive: path.join(dir, release.artifact),
|
|
822
|
+
runtimeArchive: path.join(dir, release.runtimeArtifact),
|
|
823
|
+
manifest: path.join(dir, "release-manifest.json"),
|
|
824
|
+
signature: path.join(dir, "release-manifest.sig"),
|
|
825
|
+
engine: engineMetadata(release),
|
|
826
|
+
});
|
|
827
|
+
const current = installedFiles(dir, release);
|
|
781
828
|
if (current) {
|
|
782
829
|
verifyFleetLocalRelease(current, release, publicKey);
|
|
783
830
|
warnCompatibility(current.manifest);
|
|
784
|
-
|
|
785
|
-
|
|
831
|
+
onProgress?.({ phase: "ready", step: "download", cached: true });
|
|
832
|
+
return installation();
|
|
833
|
+
}
|
|
834
|
+
return withBotLock(FLEET_LOCAL_DISTRIBUTION_LOCK_NAME, async () => {
|
|
835
|
+
const again = installedFiles(dir, release);
|
|
836
|
+
if (again) {
|
|
837
|
+
verifyFleetLocalRelease(again, release, publicKey);
|
|
838
|
+
warnCompatibility(again.manifest);
|
|
839
|
+
onProgress?.({ phase: "ready", step: "download", cached: true });
|
|
840
|
+
return installation();
|
|
841
|
+
}
|
|
786
842
|
const fetcher = deps.fetch ?? fetch;
|
|
787
843
|
const lookup = deps.hqReleaseLinks
|
|
788
844
|
? { links: await deps.hqReleaseLinks(release), reason: null }
|
|
789
845
|
: await (deps.hqReleaseLookup ?? lookupHqFleetReleaseLinks)(release);
|
|
790
846
|
const links = lookup.links;
|
|
791
847
|
const cachedRuntime = cachedRuntimeArchive(root, release);
|
|
848
|
+
const target = downloadsDir(root, release);
|
|
792
849
|
let files;
|
|
793
850
|
if (links) {
|
|
794
|
-
files = await downloadFleetLocalReleaseFromHq(release, fetcher, links, cachedRuntime);
|
|
851
|
+
files = await downloadFleetLocalReleaseFromHq(release, fetcher, links, cachedRuntime, target, onProgress, deps.downloadStallMs);
|
|
795
852
|
}
|
|
796
853
|
else {
|
|
797
854
|
let githubToken;
|
|
@@ -799,24 +856,29 @@ export async function installPinnedFleetLocalDistribution(deps = {}) {
|
|
|
799
856
|
githubToken = (deps.token ?? resolveFleetReleaseToken)();
|
|
800
857
|
}
|
|
801
858
|
catch (error) {
|
|
802
|
-
// A customer has no GitHub token; say why HQ had no download instead of asking them to sign in again.
|
|
803
859
|
throw noReleaseSourceError(release, lookup.reason) ?? error;
|
|
804
860
|
}
|
|
805
|
-
files = await downloadFleetLocalRelease(release, fetcher, githubToken, cachedRuntime);
|
|
861
|
+
files = await downloadFleetLocalRelease(release, fetcher, githubToken, cachedRuntime, target, onProgress, deps.downloadStallMs);
|
|
862
|
+
}
|
|
863
|
+
onProgress?.({ phase: "verify" });
|
|
864
|
+
try {
|
|
865
|
+
verifyFleetLocalRelease(files, release, publicKey);
|
|
866
|
+
}
|
|
867
|
+
catch (error) {
|
|
868
|
+
discardDownloads(target);
|
|
869
|
+
throw error;
|
|
806
870
|
}
|
|
807
|
-
verifyFleetLocalRelease(files, release, publicKey);
|
|
808
871
|
warnCompatibility(files.manifest);
|
|
809
872
|
installFiles(dir, files, release, publicKey);
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
};
|
|
873
|
+
discardDownloads(target);
|
|
874
|
+
onProgress?.({ phase: "ready", step: "download", cached: false });
|
|
875
|
+
return installation();
|
|
876
|
+
}, {
|
|
877
|
+
root: path.join(root, "distributions"),
|
|
878
|
+
command: "HQ Fleet Runtime download",
|
|
879
|
+
waitMs: FLEET_LOCAL_DISTRIBUTION_LOCK_WAIT_MS,
|
|
880
|
+
staleMs: FLEET_LOCAL_DISTRIBUTION_LOCK_STALE_MS,
|
|
881
|
+
});
|
|
820
882
|
}
|
|
821
883
|
function profileName(agentUid) {
|
|
822
884
|
return agentUid.toLowerCase();
|
|
@@ -1633,7 +1695,7 @@ export function importFleetLocalMachineCredential(agentUid, machineCredentials,
|
|
|
1633
1695
|
if (!opts.keepSource)
|
|
1634
1696
|
fs.rmSync(machineCredentials, { force: true });
|
|
1635
1697
|
}
|
|
1636
|
-
async function installSharedFleetRuntime(installation, root, python, platform, run, spawn) {
|
|
1698
|
+
async function installSharedFleetRuntime(installation, root, python, platform, run, spawn, onProgress) {
|
|
1637
1699
|
const identity = digest(Buffer.from(JSON.stringify({
|
|
1638
1700
|
version: installation.release.version,
|
|
1639
1701
|
distribution: installation.release.artifactSha256,
|
|
@@ -1672,9 +1734,11 @@ async function installSharedFleetRuntime(installation, root, python, platform, r
|
|
|
1672
1734
|
if (!executables.every((name) => fs.existsSync(venvExecutable(runtimeDir, name, platform)))) {
|
|
1673
1735
|
throw expectedError("The shared HQ Fleet Runtime is incomplete. Reinstall it before creating another bot.");
|
|
1674
1736
|
}
|
|
1737
|
+
onProgress?.({ phase: "ready", step: "runtime", cached: true });
|
|
1675
1738
|
return layout;
|
|
1676
1739
|
}
|
|
1677
1740
|
fs.rmSync(shared, { recursive: true, force: true });
|
|
1741
|
+
onProgress?.({ phase: "extract" });
|
|
1678
1742
|
fs.mkdirSync(distributionSource, { recursive: true, mode: 0o700 });
|
|
1679
1743
|
fs.mkdirSync(runtimeSource, { recursive: true, mode: 0o700 });
|
|
1680
1744
|
const uvEnv = fleetUvEnv(python.pythonDir);
|
|
@@ -1689,6 +1753,7 @@ async function installSharedFleetRuntime(installation, root, python, platform, r
|
|
|
1689
1753
|
installation.runtimeArchive,
|
|
1690
1754
|
runtimeSource,
|
|
1691
1755
|
], { stdio: "ignore" }, "runtime extraction");
|
|
1756
|
+
onProgress?.({ phase: "environment" });
|
|
1692
1757
|
checkedRun(run, python.uv, [
|
|
1693
1758
|
"venv",
|
|
1694
1759
|
"--no-config",
|
|
@@ -1697,6 +1762,7 @@ async function installSharedFleetRuntime(installation, root, python, platform, r
|
|
|
1697
1762
|
runtimeDir,
|
|
1698
1763
|
], { stdio: "ignore", env: uvEnv }, "environment creation");
|
|
1699
1764
|
const runtimePython = venvExecutable(runtimeDir, "python", platform);
|
|
1765
|
+
onProgress?.({ phase: "packages" });
|
|
1700
1766
|
checkedRun(run, python.uv, [
|
|
1701
1767
|
"pip",
|
|
1702
1768
|
"install",
|
|
@@ -1711,13 +1777,14 @@ async function installSharedFleetRuntime(installation, root, python, platform, r
|
|
|
1711
1777
|
throw expectedError("HQ Fleet Local installed without its runtime executables.");
|
|
1712
1778
|
}
|
|
1713
1779
|
fs.writeFileSync(marker, `${JSON.stringify({ schemaVersion: 1, runtimeId: identity, engine: installation.engine })}\n`, { mode: 0o600 });
|
|
1780
|
+
onProgress?.({ phase: "ready", step: "runtime", cached: false });
|
|
1714
1781
|
return layout;
|
|
1715
1782
|
}, { root: path.join(root, "runtimes"), command: "HQ Fleet Runtime installation", waitMs: 20 * 60_000, staleMs: 2 * 60 * 60_000 });
|
|
1716
1783
|
}
|
|
1717
1784
|
function writeNativeProfileMarker(profileDir, shared, engine) {
|
|
1718
1785
|
const marker = path.join(profileDir, "bootstrap.json");
|
|
1719
1786
|
const temporary = `${marker}.${process.pid}.${crypto.randomUUID()}`;
|
|
1720
|
-
fs.writeFileSync(temporary, `${JSON.stringify({ schemaVersion: 2, runtimeId: shared.id, engine }, null, 2)}\n`, { mode: 0o600 });
|
|
1787
|
+
fs.writeFileSync(temporary, `${JSON.stringify({ schemaVersion: 2, runtimeId: shared.id, engine, cliVersion: CLI_VERSION }, null, 2)}\n`, { mode: 0o600 });
|
|
1721
1788
|
fs.renameSync(temporary, marker);
|
|
1722
1789
|
}
|
|
1723
1790
|
function removeEmbeddedRuntime(profileDir, platform = process.platform) {
|
|
@@ -1725,6 +1792,42 @@ function removeEmbeddedRuntime(profileDir, platform = process.platform) {
|
|
|
1725
1792
|
if (hasEmbeddedRuntime(profileDir, platform))
|
|
1726
1793
|
fs.rmSync(path.join(profileDir, "runtime"), { recursive: true, force: true });
|
|
1727
1794
|
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Everything a bot needs that is shared by every bot on this machine: the
|
|
1797
|
+
* pinned distribution and runtime archive, the private Python, and the built
|
|
1798
|
+
* runtime environment. Safe to run any number of times; a complete install
|
|
1799
|
+
* returns at once.
|
|
1800
|
+
*/
|
|
1801
|
+
export async function ensureFleetRuntimeInstalled(installation, deps = {}) {
|
|
1802
|
+
const run = deps.run ?? execFileSync;
|
|
1803
|
+
const platform = deps.platform ?? process.platform;
|
|
1804
|
+
const root = deps.fleetHome ?? fleetHome();
|
|
1805
|
+
deps.onProgress?.({ phase: "python" });
|
|
1806
|
+
const python = deps.python ?? await ensureFleetPython({ fleetHome: root, platform, run });
|
|
1807
|
+
const spawn = deps.spawn ?? (deps.run ? spawnFromRun(deps.run) : spawnSync);
|
|
1808
|
+
const shared = await installSharedFleetRuntime(installation, root, python, platform, run, spawn, deps.onProgress);
|
|
1809
|
+
return { release: installation.release, distributionDir: installation.dir, runtimeDir: shared.runtimeDir, runtimeId: shared.id };
|
|
1810
|
+
}
|
|
1811
|
+
/** Whether the shared runtime for this release is already built on this machine. */
|
|
1812
|
+
export function fleetRuntimeReady(installation, deps = {}) {
|
|
1813
|
+
const root = deps.fleetHome ?? fleetHome();
|
|
1814
|
+
const platform = deps.platform ?? process.platform;
|
|
1815
|
+
const python = deps.python;
|
|
1816
|
+
if (!python)
|
|
1817
|
+
return false;
|
|
1818
|
+
const identity = digest(Buffer.from(JSON.stringify({
|
|
1819
|
+
version: installation.release.version,
|
|
1820
|
+
distribution: installation.release.artifactSha256,
|
|
1821
|
+
runtime: installation.release.runtimeArtifactSha256,
|
|
1822
|
+
python: python.python,
|
|
1823
|
+
platform,
|
|
1824
|
+
extras: ["mcp", "anthropic"],
|
|
1825
|
+
})));
|
|
1826
|
+
const layout = sharedRuntimeLayout(root, identity, platform);
|
|
1827
|
+
if (!fs.existsSync(path.join(layout.root, "ready.json")))
|
|
1828
|
+
return false;
|
|
1829
|
+
return ["python", "hq-fleet", "hq-fleet-local"].every((name) => fs.existsSync(venvExecutable(layout.runtimeDir, name, platform)));
|
|
1830
|
+
}
|
|
1728
1831
|
export async function bootstrapFleetLocalBot(input, deps = {}) {
|
|
1729
1832
|
const run = deps.run ?? execFileSync;
|
|
1730
1833
|
const platform = deps.platform ?? process.platform;
|
|
@@ -1733,7 +1836,7 @@ export async function bootstrapFleetLocalBot(input, deps = {}) {
|
|
|
1733
1836
|
const profileDir = adoptLegacyProfileDirectory(input.agentUid, root);
|
|
1734
1837
|
fs.mkdirSync(profileDir, { recursive: true, mode: 0o700 });
|
|
1735
1838
|
const spawn = deps.spawn ?? (deps.run ? spawnFromRun(deps.run) : spawnSync);
|
|
1736
|
-
const shared = await installSharedFleetRuntime(input.installation, root, python, platform, run, spawn);
|
|
1839
|
+
const shared = await installSharedFleetRuntime(input.installation, root, python, platform, run, spawn, deps.onProgress);
|
|
1737
1840
|
writeNativeProfileMarker(profileDir, shared, input.installation.engine);
|
|
1738
1841
|
const { systemPrompt, machineCredentials, ...profileInput } = input;
|
|
1739
1842
|
if (systemPrompt !== undefined)
|
|
@@ -43,6 +43,8 @@ export declare function fleetUvEnv(pythonDir: string, base?: NodeJS.ProcessEnv):
|
|
|
43
43
|
/** The operating system's own tar when it is where it always is, so a `tar` on PATH or in the current folder is never run. */
|
|
44
44
|
export declare function tarExecutable(platform: NodeJS.Platform, env?: NodeJS.ProcessEnv): string;
|
|
45
45
|
export declare function fleetPythonToolPath(deps?: FleetPythonDeps): string;
|
|
46
|
+
/** The installer and private Python already on this machine, or null; never downloads anything. */
|
|
47
|
+
export declare function installedFleetPython(deps?: FleetPythonDeps): FleetPython | null;
|
|
46
48
|
/**
|
|
47
49
|
* The pinned installer and private Python, installed once per machine under
|
|
48
50
|
* the fleet folder and reused by every bot. Concurrent creates wait for each
|
|
@@ -222,6 +222,35 @@ async function ensureFleetPythonLocked(root, deps) {
|
|
|
222
222
|
}
|
|
223
223
|
return { uv, python, pythonDir };
|
|
224
224
|
}
|
|
225
|
+
/** The installer and private Python already on this machine, or null; never downloads anything. */
|
|
226
|
+
export function installedFleetPython(deps = {}) {
|
|
227
|
+
const root = deps.fleetHome ?? fleetHome();
|
|
228
|
+
const platform = deps.platform ?? process.platform;
|
|
229
|
+
const run = deps.run ?? execFileSync;
|
|
230
|
+
const version = deps.pythonVersion ?? HQ_FLEET_PYTHON_VERSION;
|
|
231
|
+
const uv = fleetPythonToolPath(deps);
|
|
232
|
+
const pythonDir = path.join(root, "python");
|
|
233
|
+
if (!installedTool(uv) || !fs.existsSync(pythonDir))
|
|
234
|
+
return null;
|
|
235
|
+
try {
|
|
236
|
+
const output = run(uv, ["python", "find", version, "--managed-python", "--no-config"], {
|
|
237
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
238
|
+
env: fleetUvEnv(pythonDir, deps.env ?? process.env),
|
|
239
|
+
encoding: "utf8",
|
|
240
|
+
windowsHide: true,
|
|
241
|
+
timeout: FLEET_PYTHON_FIND_TIMEOUT_MS,
|
|
242
|
+
killSignal: "SIGKILL",
|
|
243
|
+
});
|
|
244
|
+
const found = (Buffer.isBuffer(output) ? output.toString("utf8") : output).trim();
|
|
245
|
+
const python = path.resolve(found);
|
|
246
|
+
if (!found || !fs.existsSync(python) || !isInside(pythonDir, python, platform))
|
|
247
|
+
return null;
|
|
248
|
+
return { uv, python, pythonDir };
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
225
254
|
/**
|
|
226
255
|
* The pinned installer and private Python, installed once per machine under
|
|
227
256
|
* the fleet folder and reused by every bot. Concurrent creates wait for each
|
package/dist/lib/bot/job-run.js
CHANGED
|
@@ -215,7 +215,7 @@ export function planJobRuns(items, handled) {
|
|
|
215
215
|
const byJob = new Map();
|
|
216
216
|
for (const item of items) {
|
|
217
217
|
const job = readJobRun(item);
|
|
218
|
-
if (!job)
|
|
218
|
+
if (!job || item.messageId.startsWith("localjob-event-"))
|
|
219
219
|
continue;
|
|
220
220
|
const list = byJob.get(job.jobId);
|
|
221
221
|
if (list)
|
|
@@ -48,6 +48,12 @@ export declare const HOT_SIGNAL_SYNC_JOB_KIND = "hot-signal-sync";
|
|
|
48
48
|
* own schedule in `jobs.json` is the one that runs.
|
|
49
49
|
*/
|
|
50
50
|
export declare const HOT_SIGNAL_SYNC_DEFAULT_CRON = "cron(30 6 * * ? *)";
|
|
51
|
+
interface BuiltinJobsMarker {
|
|
52
|
+
v: 1;
|
|
53
|
+
registered: Record<string, string>;
|
|
54
|
+
}
|
|
55
|
+
declare function readMarker(dir: string): BuiltinJobsMarker;
|
|
56
|
+
declare function writeMarker(dir: string, marker: BuiltinJobsMarker): void;
|
|
51
57
|
export declare function hotSignalSyncRegistered(dir: string, company: string): boolean;
|
|
52
58
|
export declare function fleetHotSignalSyncRegistered(dir: string, company: string): boolean;
|
|
53
59
|
export declare function markFleetHotSignalSyncRegistered(dir: string, company: string, now?: Date): void;
|
|
@@ -210,4 +216,6 @@ export interface HotSignalSyncRunOutcome {
|
|
|
210
216
|
/** Run the scheduled sync once and describe it in plain words. Never throws. */
|
|
211
217
|
export declare function runHotSignalSyncJob(input: HotSignalSyncRunInput): Promise<HotSignalSyncRunOutcome>;
|
|
212
218
|
export declare function describeSyncReport(company: string, report: OnDemandSyncReport): string;
|
|
219
|
+
/** Shared registration marker for additive built-in event jobs. */
|
|
220
|
+
export { readMarker as readBuiltinJobsMarker, writeMarker as writeBuiltinJobsMarker };
|
|
213
221
|
//# sourceMappingURL=hot-signal-sync.d.ts.map
|
|
@@ -430,4 +430,6 @@ export function describeSyncReport(company, report) {
|
|
|
430
430
|
lines.push(notice);
|
|
431
431
|
return lines.join("\n");
|
|
432
432
|
}
|
|
433
|
+
/** Shared registration marker for additive built-in event jobs. */
|
|
434
|
+
export { readMarker as readBuiltinJobsMarker, writeMarker as writeBuiltinJobsMarker };
|
|
433
435
|
//# sourceMappingURL=hot-signal-sync.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type LiveMeetingBot } from "../../live-meetings.js";
|
|
2
|
+
import type { BotConfig } from "../config.js";
|
|
3
|
+
export declare function detectIntervalSeconds(hqRoot: string): number;
|
|
4
|
+
/** Desktop events carry company UIDs; local rider files use manifest slugs. */
|
|
5
|
+
export declare function normalizeDetectedCompany(hqRoot: string, payload: unknown): unknown;
|
|
6
|
+
export interface MeetingDetectDeps {
|
|
7
|
+
hqRoot: string;
|
|
8
|
+
dir: string;
|
|
9
|
+
enabled: () => boolean;
|
|
10
|
+
companies: () => string[];
|
|
11
|
+
list: (company: string, signal: AbortSignal) => Promise<{
|
|
12
|
+
companyId: string;
|
|
13
|
+
bots: LiveMeetingBot[];
|
|
14
|
+
}>;
|
|
15
|
+
intervalSeconds?: () => number;
|
|
16
|
+
now?: () => Date;
|
|
17
|
+
log: (message: string) => void;
|
|
18
|
+
}
|
|
19
|
+
/** Live reads are keyed by Recall ID; the desktop may emit a saved meeting ID or SDK window ID. */
|
|
20
|
+
export declare function resolveDetectedMeeting(hqRoot: string, payload: unknown, list: MeetingDetectDeps["list"]): Promise<unknown>;
|
|
21
|
+
export declare function createMeetingDetect(deps: MeetingDetectDeps): {
|
|
22
|
+
/** Called by the inbox loop; network work never holds up inbox delivery. */
|
|
23
|
+
tick(): void;
|
|
24
|
+
settled(): Promise<void>;
|
|
25
|
+
stop(): Promise<void>;
|
|
26
|
+
};
|
|
27
|
+
export declare function defaultMeetingDetectDeps(config: BotConfig, dir: string, log: (message: string) => void): MeetingDetectDeps;
|
|
28
|
+
//# sourceMappingURL=meeting-detect.d.ts.map
|