@indigoai-us/hq-cli 5.345.178 → 5.345.180
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 +39 -0
- package/dist/command-catalog.generated.d.ts +197 -1
- package/dist/command-catalog.generated.js +241 -1
- package/dist/commands/bot-jobs.d.ts +11 -9
- package/dist/commands/bot-jobs.js +56 -21
- package/dist/commands/bot.d.ts +37 -1
- package/dist/commands/bot.js +289 -13
- package/dist/commands/channels.d.ts +18 -1
- package/dist/commands/channels.js +185 -1
- package/dist/commands/daemon.d.ts +2 -0
- package/dist/commands/daemon.js +15 -4
- package/dist/commands/integrations-core.d.ts +2 -0
- package/dist/commands/integrations-core.js +1 -0
- package/dist/commands/lanes.d.ts +1 -1
- package/dist/commands/lanes.js +6 -2
- package/dist/commands/mesh.js +19 -0
- package/dist/commands/sources.d.ts +89 -0
- package/dist/commands/sources.js +363 -1
- package/dist/hq-cloud-sync-runner/bin/sync-runner.js +2285 -1743
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- package/dist/lib/bot/config.d.ts +8 -0
- package/dist/lib/bot/fleet-local.d.ts +1 -1
- package/dist/lib/bot/fleet-local.js +82 -30
- package/dist/lib/bot/jobs/hot-signal-sync.d.ts +109 -0
- package/dist/lib/bot/jobs/hot-signal-sync.js +177 -0
- package/dist/lib/bot/local-jobs.d.ts +40 -4
- package/dist/lib/bot/local-jobs.js +72 -5
- package/dist/lib/bot/run.d.ts +15 -0
- package/dist/lib/bot/run.js +99 -1
- package/dist/lib/bot/runtime-auth.d.ts +119 -0
- package/dist/lib/bot/runtime-auth.js +251 -0
- package/dist/lib/daemon/catalog.d.ts +1 -0
- package/dist/lib/daemon/catalog.js +1 -1
- package/dist/lib/daemon/handoff.d.ts +4 -0
- package/dist/lib/daemon/handoff.js +6 -2
- package/dist/lib/daemon/install.d.ts +70 -2
- package/dist/lib/daemon/install.js +185 -27
- package/dist/lib/daemon/legacy.d.ts +9 -3
- package/dist/lib/daemon/legacy.js +11 -4
- package/dist/lib/daemon/migrate.js +1 -1
- package/dist/lib/daemon/run.d.ts +5 -2
- package/dist/lib/daemon/run.js +74 -22
- package/dist/lib/doctor/checks/anywhere.d.ts +6 -0
- package/dist/lib/doctor/checks/anywhere.js +35 -6
- package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
- package/dist/lib/flag-registry-worker-source.generated.js +1 -1
- package/dist/lib/hot-channels/adapters/__fixtures__/dummy.d.ts +33 -0
- package/dist/lib/hot-channels/adapters/__fixtures__/dummy.js +53 -0
- package/dist/lib/hot-channels/adapters/__fixtures__/meeting-providers.d.ts +38 -0
- package/dist/lib/hot-channels/adapters/__fixtures__/meeting-providers.js +64 -0
- package/dist/lib/hot-channels/adapters/attio.d.ts +67 -0
- package/dist/lib/hot-channels/adapters/attio.js +374 -0
- package/dist/lib/hot-channels/adapters/gmail.d.ts +111 -0
- package/dist/lib/hot-channels/adapters/gmail.js +413 -0
- package/dist/lib/hot-channels/adapters/index.d.ts +23 -0
- package/dist/lib/hot-channels/adapters/index.js +39 -0
- package/dist/lib/hot-channels/adapters/linear.d.ts +79 -0
- package/dist/lib/hot-channels/adapters/linear.js +321 -0
- package/dist/lib/hot-channels/adapters/meeting-providers.d.ts +86 -0
- package/dist/lib/hot-channels/adapters/meeting-providers.js +253 -0
- package/dist/lib/hot-channels/adapters/meetings.d.ts +81 -0
- package/dist/lib/hot-channels/adapters/meetings.js +525 -0
- package/dist/lib/hot-channels/adapters/slack.d.ts +70 -0
- package/dist/lib/hot-channels/adapters/slack.js +277 -0
- package/dist/lib/hot-channels/alert.d.ts +78 -0
- package/dist/lib/hot-channels/alert.js +179 -0
- package/dist/lib/hot-channels/audience.d.ts +15 -0
- package/dist/lib/hot-channels/audience.js +25 -0
- package/dist/lib/hot-channels/contract.d.ts +146 -0
- package/dist/lib/hot-channels/contract.js +20 -0
- package/dist/lib/hot-channels/cursors.d.ts +41 -0
- package/dist/lib/hot-channels/cursors.js +84 -0
- package/dist/lib/hot-channels/index.d.ts +10 -0
- package/dist/lib/hot-channels/index.js +10 -0
- package/dist/lib/hot-channels/landing.d.ts +83 -0
- package/dist/lib/hot-channels/landing.js +175 -0
- package/dist/lib/hot-channels/ledger.d.ts +101 -0
- package/dist/lib/hot-channels/ledger.js +203 -0
- package/dist/lib/hot-channels/loop.d.ts +74 -0
- package/dist/lib/hot-channels/loop.js +221 -0
- package/dist/lib/hot-channels/prompts/signals-pass.md +22 -0
- package/dist/lib/hot-channels/registry.d.ts +28 -0
- package/dist/lib/hot-channels/registry.js +121 -0
- package/dist/lib/hot-channels/run.d.ts +137 -0
- package/dist/lib/hot-channels/run.js +324 -0
- package/dist/lib/hot-channels/signals-pass.d.ts +150 -0
- package/dist/lib/hot-channels/signals-pass.js +581 -0
- package/dist/lib/hot-channels/sync-lock.d.ts +51 -0
- package/dist/lib/hot-channels/sync-lock.js +224 -0
- package/dist/lib/install/hook-target.d.ts +6 -2
- package/dist/lib/install/hook-target.js +9 -4
- package/dist/lib/lanes/accounts.d.ts +1 -17
- package/dist/lib/lanes/accounts.js +5 -45
- package/dist/lib/lanes/loop.d.ts +10 -0
- package/dist/lib/lanes/loop.js +18 -1
- package/dist/lib/lanes/provider-home.d.ts +24 -0
- package/dist/lib/lanes/provider-home.js +52 -0
- package/dist/lib/lanes/spawn.d.ts +12 -0
- package/dist/lib/lanes/spawn.js +27 -0
- package/dist/lib/mesh/live/daemon/doctor.d.ts +3 -0
- package/dist/lib/mesh/live/daemon/doctor.js +13 -0
- package/dist/lib/mesh/live/daemon/owner-inbox-push.d.ts +94 -0
- package/dist/lib/mesh/live/daemon/owner-inbox-push.js +273 -0
- package/dist/lib/mesh/live/daemon/run.js +31 -0
- package/dist/lib/mesh/live/daemon/work-subscribe.d.ts +14 -0
- package/dist/lib/mesh/live/daemon/work-subscribe.js +210 -3
- package/dist/lib/mesh/live/paths.d.ts +2 -0
- package/dist/lib/mesh/live/paths.js +2 -0
- package/dist/lib/sync-core/flag.d.ts +1 -1
- package/dist/lib/sync-core/flag.js +1 -1
- package/dist/lib/sync-core/reconcile.d.ts +1 -1
- package/dist/lib/sync-core/reconcile.js +9 -7
- package/dist/lib/sync-core/release.d.ts +3 -3
- package/dist/lib/sync-core/release.js +4 -4
- package/dist/lib/sync-core/rollout-state.d.ts +1 -0
- package/dist/lib/sync-core/rollout-state.js +1 -0
- package/package.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"hq-cli-daemon-sync-runner","type":"module","version":"6.18.
|
|
1
|
+
{"name":"hq-cli-daemon-sync-runner","type":"module","version":"6.18.105","bundleSha256":"f57aa12671cdfcf08ce39f11ce6885d6e52d5478f2ae77a75d38d79162194ce5","formatVersion":1}
|
package/dist/lib/bot/config.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* bot.json — non-secret per-bot configuration (local-bots US-003/US-005).
|
|
3
3
|
*/
|
|
4
|
+
import type { BotRuntimeAuthMap } from "./runtime-auth.js";
|
|
4
5
|
export declare const BOT_RUNTIMES: readonly ["claude", "codex", "grok"];
|
|
5
6
|
export type BotRuntimeId = (typeof BOT_RUNTIMES)[number];
|
|
6
7
|
export declare const HQ_FLEET_RUNTIME_ENGINE_ID: "hq-fleet-runtime";
|
|
@@ -167,6 +168,13 @@ export interface BotConfig {
|
|
|
167
168
|
* setup bots, which only need files and shell commands.
|
|
168
169
|
*/
|
|
169
170
|
lean?: boolean;
|
|
171
|
+
/**
|
|
172
|
+
* Which model login each runtime uses (see runtime-auth.ts): unset = the
|
|
173
|
+
* owner's default sign-in; { mode: "own" } = the bot's own login under
|
|
174
|
+
* ~/.hq/bots/<name>/auth/<runtime>; { mode: "account", account } = a
|
|
175
|
+
* capacity account home. Names only, never a credential.
|
|
176
|
+
*/
|
|
177
|
+
runtimeAuth?: BotRuntimeAuthMap;
|
|
170
178
|
}
|
|
171
179
|
export declare function readBotConfig(dir: string): BotConfig | null;
|
|
172
180
|
export declare function writeBotConfig(dir: string, config: BotConfig): void;
|
|
@@ -133,7 +133,7 @@ export interface FleetLocalRemovalResult {
|
|
|
133
133
|
credentialsRemoved: number;
|
|
134
134
|
}
|
|
135
135
|
export declare const HQ_FLEET_LOCAL_RELEASE: FleetLocalReleasePin;
|
|
136
|
-
export declare const HQ_FLEET_LOCAL_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\
|
|
136
|
+
export declare const HQ_FLEET_LOCAL_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEARXr2I8RNcw2c0Ep77XkYGwylresXBFn8dm9pxN2Zq/U=\n-----END PUBLIC KEY-----\n";
|
|
137
137
|
export declare function fleetHome(home?: string, env?: NodeJS.ProcessEnv): string;
|
|
138
138
|
export declare function verifyFleetLocalRelease(files: FleetLocalReleaseFiles, release?: FleetLocalReleasePin, publicKey?: string): void;
|
|
139
139
|
export declare function resolveFleetReleaseToken(env?: NodeJS.ProcessEnv): string;
|
|
@@ -4,6 +4,7 @@ import * as fs from "node:fs";
|
|
|
4
4
|
import * as os from "node:os";
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import { HQ_FLEET_RUNTIME_ENGINE_ID, } from "./config.js";
|
|
7
|
+
import { withBotLock } from "./lock.js";
|
|
7
8
|
import { ensureFleetPython, fleetUvEnv } from "./fleet-python.js";
|
|
8
9
|
import { ensureCognitoToken } from "../../utils/cognito-session.js";
|
|
9
10
|
import { vaultApiFetch } from "../../utils/vault-api.js";
|
|
@@ -35,18 +36,18 @@ export function fleetProviderNeedsSignIn(provider) {
|
|
|
35
36
|
}
|
|
36
37
|
export const HQ_FLEET_LOCAL_RELEASE = {
|
|
37
38
|
repository: "indigoai-us/hq-fleet-local",
|
|
38
|
-
tag: "v0.4.
|
|
39
|
-
version: "0.4.
|
|
40
|
-
artifact: "hq-fleet-local-0.4.
|
|
41
|
-
artifactSha256: "
|
|
39
|
+
tag: "v0.4.14",
|
|
40
|
+
version: "0.4.14",
|
|
41
|
+
artifact: "hq-fleet-local-0.4.14.zip",
|
|
42
|
+
artifactSha256: "9ff6ad4f7235988468e1913008c5650f5d064210078d6d20c407b17120310f47",
|
|
42
43
|
runtimeArtifact: "hq-fleet-runtime-abccac13bd9e.tar",
|
|
43
44
|
runtimeArtifactSha256: "29bcfe718ad7fbd3636afd00623772f2a25bc84c4f6f10d8d57f38e549d528d5",
|
|
44
|
-
manifestSha256: "
|
|
45
|
-
signatureSha256: "
|
|
45
|
+
manifestSha256: "88e71a733a5f57089764cbf9947fb88025c2263c5f3d62a2c6f5ab3635538871",
|
|
46
|
+
signatureSha256: "b892b54d6fcb2aaca86d049d7662fe5549a250d81877d6c529288e79ae1565d9",
|
|
46
47
|
runtimeCommit: "abccac13bd9ea593b24c8cd9c0ba28d5c6050cc9",
|
|
47
48
|
};
|
|
48
49
|
export const HQ_FLEET_LOCAL_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
|
49
|
-
|
|
50
|
+
MCowBQYDK2VwAyEARXr2I8RNcw2c0Ep77XkYGwylresXBFn8dm9pxN2Zq/U=
|
|
50
51
|
-----END PUBLIC KEY-----
|
|
51
52
|
`;
|
|
52
53
|
function digest(value) {
|
|
@@ -162,7 +163,7 @@ function trustedReleaseLink(url) {
|
|
|
162
163
|
return false;
|
|
163
164
|
}
|
|
164
165
|
}
|
|
165
|
-
async function downloadFleetLocalReleaseFromHq(release, fetcher, links) {
|
|
166
|
+
async function downloadFleetLocalReleaseFromHq(release, fetcher, links, cachedRuntime) {
|
|
166
167
|
const get = async (name, timeoutMs) => {
|
|
167
168
|
const url = links.get(name);
|
|
168
169
|
if (!url)
|
|
@@ -178,7 +179,7 @@ async function downloadFleetLocalReleaseFromHq(release, fetcher, links) {
|
|
|
178
179
|
};
|
|
179
180
|
const [archive, runtimeArchive, manifest, signature] = await Promise.all([
|
|
180
181
|
get(release.artifact, FLEET_LOCAL_ARCHIVE_DOWNLOAD_TIMEOUT_MS),
|
|
181
|
-
get(release.runtimeArtifact, FLEET_LOCAL_RUNTIME_DOWNLOAD_TIMEOUT_MS),
|
|
182
|
+
cachedRuntime ?? get(release.runtimeArtifact, FLEET_LOCAL_RUNTIME_DOWNLOAD_TIMEOUT_MS),
|
|
182
183
|
get("release-manifest.json", FLEET_LOCAL_METADATA_DOWNLOAD_TIMEOUT_MS),
|
|
183
184
|
get("release-manifest.sig", FLEET_LOCAL_METADATA_DOWNLOAD_TIMEOUT_MS),
|
|
184
185
|
]);
|
|
@@ -193,7 +194,7 @@ async function responseBytes(response, label) {
|
|
|
193
194
|
export const FLEET_LOCAL_METADATA_DOWNLOAD_TIMEOUT_MS = 30_000;
|
|
194
195
|
export const FLEET_LOCAL_ARCHIVE_DOWNLOAD_TIMEOUT_MS = 5 * 60_000;
|
|
195
196
|
export const FLEET_LOCAL_RUNTIME_DOWNLOAD_TIMEOUT_MS = 20 * 60_000;
|
|
196
|
-
async function downloadFleetLocalRelease(release, fetcher, token) {
|
|
197
|
+
async function downloadFleetLocalRelease(release, fetcher, token, cachedRuntime) {
|
|
197
198
|
const headers = {
|
|
198
199
|
Accept: "application/vnd.github+json",
|
|
199
200
|
Authorization: `Bearer ${token}`,
|
|
@@ -223,7 +224,7 @@ async function downloadFleetLocalRelease(release, fetcher, token) {
|
|
|
223
224
|
};
|
|
224
225
|
const [archive, runtimeArchive, manifest, signature] = await Promise.all([
|
|
225
226
|
get(release.artifact, FLEET_LOCAL_ARCHIVE_DOWNLOAD_TIMEOUT_MS),
|
|
226
|
-
get(release.runtimeArtifact, FLEET_LOCAL_RUNTIME_DOWNLOAD_TIMEOUT_MS),
|
|
227
|
+
cachedRuntime ?? get(release.runtimeArtifact, FLEET_LOCAL_RUNTIME_DOWNLOAD_TIMEOUT_MS),
|
|
227
228
|
get("release-manifest.json", FLEET_LOCAL_METADATA_DOWNLOAD_TIMEOUT_MS),
|
|
228
229
|
get("release-manifest.sig", FLEET_LOCAL_METADATA_DOWNLOAD_TIMEOUT_MS),
|
|
229
230
|
]);
|
|
@@ -274,6 +275,24 @@ function installFiles(target, files, release, publicKey) {
|
|
|
274
275
|
fs.rmSync(staging, { recursive: true, force: true });
|
|
275
276
|
}
|
|
276
277
|
}
|
|
278
|
+
function cachedRuntimeArchive(root, release) {
|
|
279
|
+
const distributions = path.join(root, "distributions", "hq-fleet-local");
|
|
280
|
+
if (!fs.existsSync(distributions))
|
|
281
|
+
return null;
|
|
282
|
+
for (const entry of fs.readdirSync(distributions, { withFileTypes: true })) {
|
|
283
|
+
if (!entry.isDirectory() || entry.name.startsWith("."))
|
|
284
|
+
continue;
|
|
285
|
+
const directory = path.join(distributions, entry.name);
|
|
286
|
+
for (const file of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
287
|
+
if (!file.isFile() || !file.name.startsWith("hq-fleet-runtime-") || !file.name.endsWith(".tar"))
|
|
288
|
+
continue;
|
|
289
|
+
const bytes = fs.readFileSync(path.join(directory, file.name));
|
|
290
|
+
if (digest(bytes) === release.runtimeArtifactSha256)
|
|
291
|
+
return bytes;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
277
296
|
export async function installPinnedFleetLocalDistribution(deps = {}) {
|
|
278
297
|
const release = deps.release ?? HQ_FLEET_LOCAL_RELEASE;
|
|
279
298
|
const publicKey = deps.publicKey ?? HQ_FLEET_LOCAL_PUBLIC_KEY;
|
|
@@ -286,9 +305,10 @@ export async function installPinnedFleetLocalDistribution(deps = {}) {
|
|
|
286
305
|
else {
|
|
287
306
|
const fetcher = deps.fetch ?? fetch;
|
|
288
307
|
const links = await (deps.hqReleaseLinks ?? fetchHqFleetReleaseLinks)(release);
|
|
308
|
+
const cachedRuntime = cachedRuntimeArchive(root, release);
|
|
289
309
|
const files = links
|
|
290
|
-
? await downloadFleetLocalReleaseFromHq(release, fetcher, links)
|
|
291
|
-
: await downloadFleetLocalRelease(release, fetcher, (deps.token ?? resolveFleetReleaseToken)());
|
|
310
|
+
? await downloadFleetLocalReleaseFromHq(release, fetcher, links, cachedRuntime)
|
|
311
|
+
: await downloadFleetLocalRelease(release, fetcher, (deps.token ?? resolveFleetReleaseToken)(), cachedRuntime);
|
|
292
312
|
verifyFleetLocalRelease(files, release, publicKey);
|
|
293
313
|
installFiles(dir, files, release, publicKey);
|
|
294
314
|
}
|
|
@@ -744,21 +764,30 @@ export function importFleetLocalMachineCredential(agentUid, machineCredentials,
|
|
|
744
764
|
], { input: machineValue, stdio: ["pipe", "ignore", "pipe"] }, "machine credential import");
|
|
745
765
|
fs.rmSync(machineCredentials, { force: true });
|
|
746
766
|
}
|
|
747
|
-
|
|
748
|
-
const
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
767
|
+
async function installSharedFleetRuntime(installation, root, python, platform, run) {
|
|
768
|
+
const identity = digest(Buffer.from(JSON.stringify({
|
|
769
|
+
version: installation.release.version,
|
|
770
|
+
distribution: installation.release.artifactSha256,
|
|
771
|
+
runtime: installation.release.runtimeArtifactSha256,
|
|
772
|
+
python: python.python,
|
|
773
|
+
platform,
|
|
774
|
+
extras: ["mcp", "anthropic"],
|
|
775
|
+
})));
|
|
776
|
+
const shared = path.join(root, "runtimes", identity);
|
|
777
|
+
return withBotLock(identity, async () => {
|
|
778
|
+
const marker = path.join(shared, "ready.json");
|
|
779
|
+
const sources = path.join(shared, "sources");
|
|
780
|
+
const distributionSource = path.join(sources, "distribution");
|
|
781
|
+
const runtimeSource = path.join(sources, "runtime");
|
|
782
|
+
const runtimeDir = path.join(shared, "runtime");
|
|
783
|
+
const executables = ["python", "hq-fleet", "hq-fleet-local"];
|
|
784
|
+
if (fs.existsSync(marker)) {
|
|
785
|
+
if (!executables.every((name) => fs.existsSync(venvExecutable(runtimeDir, name, platform)))) {
|
|
786
|
+
throw expectedError("The shared HQ Fleet Runtime is incomplete. Reinstall it before creating another bot.");
|
|
787
|
+
}
|
|
788
|
+
return shared;
|
|
789
|
+
}
|
|
790
|
+
fs.rmSync(shared, { recursive: true, force: true });
|
|
762
791
|
fs.mkdirSync(distributionSource, { recursive: true, mode: 0o700 });
|
|
763
792
|
fs.mkdirSync(runtimeSource, { recursive: true, mode: 0o700 });
|
|
764
793
|
const uvEnv = fleetUvEnv(python.pythonDir);
|
|
@@ -766,14 +795,14 @@ export async function bootstrapFleetLocalBot(input, deps = {}) {
|
|
|
766
795
|
"-m",
|
|
767
796
|
"zipfile",
|
|
768
797
|
"-e",
|
|
769
|
-
|
|
798
|
+
installation.archive,
|
|
770
799
|
distributionSource,
|
|
771
800
|
], { stdio: "ignore" }, "distribution extraction");
|
|
772
801
|
checkedRun(run, python.python, [
|
|
773
802
|
"-m",
|
|
774
803
|
"tarfile",
|
|
775
804
|
"-e",
|
|
776
|
-
|
|
805
|
+
installation.runtimeArchive,
|
|
777
806
|
runtimeSource,
|
|
778
807
|
], { stdio: "ignore" }, "runtime extraction");
|
|
779
808
|
checkedRun(run, python.uv, [
|
|
@@ -794,6 +823,29 @@ export async function bootstrapFleetLocalBot(input, deps = {}) {
|
|
|
794
823
|
`${runtimeSource}[mcp,anthropic]`,
|
|
795
824
|
distributionSource,
|
|
796
825
|
], { stdio: ["ignore", 2, 2], env: uvEnv }, "package installation");
|
|
826
|
+
if (!executables.every((name) => fs.existsSync(venvExecutable(runtimeDir, name, platform)))) {
|
|
827
|
+
throw expectedError("HQ Fleet Local installed without its runtime executables.");
|
|
828
|
+
}
|
|
829
|
+
fs.writeFileSync(marker, `${JSON.stringify({ schemaVersion: 1, engine: installation.engine })}\n`, { mode: 0o600 });
|
|
830
|
+
return shared;
|
|
831
|
+
}, { root: path.join(root, "runtimes"), command: "HQ Fleet Runtime installation", waitMs: 20 * 60_000, staleMs: 2 * 60 * 60_000 });
|
|
832
|
+
}
|
|
833
|
+
export async function bootstrapFleetLocalBot(input, deps = {}) {
|
|
834
|
+
const run = deps.run ?? execFileSync;
|
|
835
|
+
const platform = deps.platform ?? process.platform;
|
|
836
|
+
const root = deps.fleetHome ?? fleetHome();
|
|
837
|
+
const python = deps.python ?? await ensureFleetPython({ fleetHome: root, platform, run });
|
|
838
|
+
const profileDir = profileRoot(input.agentUid, root);
|
|
839
|
+
const runtimeDir = path.join(profileDir, "runtime");
|
|
840
|
+
const marker = path.join(profileDir, "bootstrap.json");
|
|
841
|
+
fs.mkdirSync(profileDir, { recursive: true, mode: 0o700 });
|
|
842
|
+
if (!fs.existsSync(marker)) {
|
|
843
|
+
const shared = await installSharedFleetRuntime(input.installation, root, python, platform, run);
|
|
844
|
+
for (const name of ["sources", "runtime"]) {
|
|
845
|
+
const target = path.join(profileDir, name);
|
|
846
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
847
|
+
fs.symlinkSync(path.join(shared, name), target, platform === "win32" ? "junction" : "dir");
|
|
848
|
+
}
|
|
797
849
|
fs.writeFileSync(marker, `${JSON.stringify({ schemaVersion: 1, engine: input.installation.engine }, null, 2)}\n`, { mode: 0o600 });
|
|
798
850
|
}
|
|
799
851
|
const { systemPrompt, ...profileInput } = input;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bot's built-in hot-signal-sync job.
|
|
3
|
+
*
|
|
4
|
+
* A local bot that is bound to a company registers one recurring job in its
|
|
5
|
+
* own `jobs.json` the first time it starts (`ensureHotSignalSyncJob`). The job
|
|
6
|
+
* fires from the bot's inbox loop like every other local job
|
|
7
|
+
* (lib/bot/local-jobs.ts): no crontab, launchd or systemd entry is written.
|
|
8
|
+
* The default schedule is 06:30 daily in the machine's time zone, ahead of a
|
|
9
|
+
* morning brief; the owner changes it with
|
|
10
|
+
* `hq bot jobs <bot> --reschedule <jobId> --every "<when>"`, or removes it with
|
|
11
|
+
* `--rm`, and a removed job is not added back on the next start.
|
|
12
|
+
*
|
|
13
|
+
* When the job fires the bot runs the sync in-process, with no model turn
|
|
14
|
+
* (`runHotSignalSyncJob`): the same hot-channel sync `hq sources sync` runs,
|
|
15
|
+
* with `trigger: "scheduled"` and `catchUp` set when the run fired late after
|
|
16
|
+
* a missed window. Catch-up is bounded by each adapter's `catchUpWindow`, and
|
|
17
|
+
* per-connection cursors keep a redelivered run from landing anything twice.
|
|
18
|
+
* The claude signals pass (US-011) runs after each adapter when the bot wires
|
|
19
|
+
* one in; with none the signals count is 0.
|
|
20
|
+
*/
|
|
21
|
+
import type { HotChannelRunResult, WriteGuard } from "../../hot-channels/loop.js";
|
|
22
|
+
import type { RunTrigger } from "../../hot-channels/ledger.js";
|
|
23
|
+
import type { OnDemandSyncReport } from "../../hot-channels/run.js";
|
|
24
|
+
import { type BotConfig } from "../config.js";
|
|
25
|
+
import { type LocalJob } from "../local-jobs.js";
|
|
26
|
+
export declare const HOT_SIGNAL_SYNC_JOB_KIND = "hot-signal-sync";
|
|
27
|
+
/**
|
|
28
|
+
* 06:30 every day, in the job's time zone (the stored EventBridge form of
|
|
29
|
+
* "30 6 * * *"). Only the first registration uses it; after that the job's
|
|
30
|
+
* own schedule in `jobs.json` is the one that runs.
|
|
31
|
+
*/
|
|
32
|
+
export declare const HOT_SIGNAL_SYNC_DEFAULT_CRON = "cron(30 6 * * ? *)";
|
|
33
|
+
/** The private context that marks a job as this built-in, for one company. */
|
|
34
|
+
export declare function hotSignalSyncJobContext(company: string): Record<string, unknown>;
|
|
35
|
+
/** The company a job item syncs, when its context marks it as this built-in; else null. */
|
|
36
|
+
export declare function hotSignalSyncJobCompany(context: unknown): string | null;
|
|
37
|
+
/**
|
|
38
|
+
* The company a bot's hot-signal-sync job syncs: the bot's own company, else
|
|
39
|
+
* the first company it serves. Null = none, and no job is registered. There is
|
|
40
|
+
* no fallback to the HQ's active company: a personal bot syncs a company only
|
|
41
|
+
* when it was given one explicitly (`--company`), so the job never writes into
|
|
42
|
+
* a vault the bot does not belong to. Checked again each time the job fires.
|
|
43
|
+
*/
|
|
44
|
+
export declare function currentHotSignalSyncCompany(config: Pick<BotConfig, "companySlug" | "kind" | "companies" | "workerId">): string | null;
|
|
45
|
+
/**
|
|
46
|
+
* Remove every hot-signal-sync job whose company is not `current` (all of them
|
|
47
|
+
* when `current` is null), and forget their registration so the company can be
|
|
48
|
+
* registered again if the bot is given it back. Returns the removed jobs.
|
|
49
|
+
*/
|
|
50
|
+
export declare function removeStaleHotSignalSyncJobs(dir: string, current: string | null): LocalJob[];
|
|
51
|
+
export interface EnsureHotSignalSyncJobResult {
|
|
52
|
+
/** The job, or null when the owner removed it after it was first registered. */
|
|
53
|
+
job: LocalJob | null;
|
|
54
|
+
created: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Register the hot-signal-sync job for `company` once. Safe to call on every
|
|
58
|
+
* bot start: an existing job is left as it is (including a changed schedule),
|
|
59
|
+
* and a job the owner removed is not added again.
|
|
60
|
+
*/
|
|
61
|
+
export declare function ensureHotSignalSyncJob(dir: string, input: {
|
|
62
|
+
company: string;
|
|
63
|
+
timezone: string;
|
|
64
|
+
}, now?: Date): EnsureHotSignalSyncJobResult;
|
|
65
|
+
/**
|
|
66
|
+
* The signals pass over one adapter's landed sources: how many signals it
|
|
67
|
+
* generated. The bot wires the local `claude -p` pass
|
|
68
|
+
* (`createClaudeSignalsPass`); `noLocalSignalsPass` is the default when no
|
|
69
|
+
* pass is given.
|
|
70
|
+
*/
|
|
71
|
+
export type LocalSignalsPass = (result: HotChannelRunResult, guard?: WriteGuard) => Promise<number>;
|
|
72
|
+
export declare const noLocalSignalsPass: LocalSignalsPass;
|
|
73
|
+
/** One company's hot-channel sync, as `runCompanyHotChannelSync` runs it (minus the HQ root). */
|
|
74
|
+
export type HotSignalSyncFn = (input: {
|
|
75
|
+
company: string;
|
|
76
|
+
trigger: RunTrigger;
|
|
77
|
+
catchUp: boolean;
|
|
78
|
+
signalsGenerated: LocalSignalsPass;
|
|
79
|
+
now: () => Date;
|
|
80
|
+
/** The owner DM for the signals-failure alert (US-012); unset = no alert check. */
|
|
81
|
+
signalsAlert?: {
|
|
82
|
+
sendOwnerDm: (text: string) => Promise<void>;
|
|
83
|
+
log?: (message: string) => void;
|
|
84
|
+
};
|
|
85
|
+
}) => Promise<OnDemandSyncReport>;
|
|
86
|
+
export interface HotSignalSyncRunInput {
|
|
87
|
+
company: string;
|
|
88
|
+
/** The run fired late, after a missed window (job-run.ts `isCatchUpRun`). */
|
|
89
|
+
catchUp: boolean;
|
|
90
|
+
now: () => Date;
|
|
91
|
+
sync: HotSignalSyncFn;
|
|
92
|
+
signalsPass?: LocalSignalsPass;
|
|
93
|
+
/**
|
|
94
|
+
* Sends the signals-failure alert to the owner as its own message, even when
|
|
95
|
+
* the job is quiet. Throws when the message was not delivered.
|
|
96
|
+
*/
|
|
97
|
+
sendAlert?: (text: string) => Promise<void>;
|
|
98
|
+
log?: (message: string) => void;
|
|
99
|
+
}
|
|
100
|
+
export interface HotSignalSyncRunOutcome {
|
|
101
|
+
ok: boolean;
|
|
102
|
+
/** Plain report for the owner's DM and the job's run output. */
|
|
103
|
+
text: string;
|
|
104
|
+
report: OnDemandSyncReport | null;
|
|
105
|
+
}
|
|
106
|
+
/** Run the scheduled sync once and describe it in plain words. Never throws. */
|
|
107
|
+
export declare function runHotSignalSyncJob(input: HotSignalSyncRunInput): Promise<HotSignalSyncRunOutcome>;
|
|
108
|
+
export declare function describeSyncReport(company: string, report: OnDemandSyncReport): string;
|
|
109
|
+
//# sourceMappingURL=hot-signal-sync.d.ts.map
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bot's built-in hot-signal-sync job.
|
|
3
|
+
*
|
|
4
|
+
* A local bot that is bound to a company registers one recurring job in its
|
|
5
|
+
* own `jobs.json` the first time it starts (`ensureHotSignalSyncJob`). The job
|
|
6
|
+
* fires from the bot's inbox loop like every other local job
|
|
7
|
+
* (lib/bot/local-jobs.ts): no crontab, launchd or systemd entry is written.
|
|
8
|
+
* The default schedule is 06:30 daily in the machine's time zone, ahead of a
|
|
9
|
+
* morning brief; the owner changes it with
|
|
10
|
+
* `hq bot jobs <bot> --reschedule <jobId> --every "<when>"`, or removes it with
|
|
11
|
+
* `--rm`, and a removed job is not added back on the next start.
|
|
12
|
+
*
|
|
13
|
+
* When the job fires the bot runs the sync in-process, with no model turn
|
|
14
|
+
* (`runHotSignalSyncJob`): the same hot-channel sync `hq sources sync` runs,
|
|
15
|
+
* with `trigger: "scheduled"` and `catchUp` set when the run fired late after
|
|
16
|
+
* a missed window. Catch-up is bounded by each adapter's `catchUpWindow`, and
|
|
17
|
+
* per-connection cursors keep a redelivered run from landing anything twice.
|
|
18
|
+
* The claude signals pass (US-011) runs after each adapter when the bot wires
|
|
19
|
+
* one in; with none the signals count is 0.
|
|
20
|
+
*/
|
|
21
|
+
import * as fs from "node:fs";
|
|
22
|
+
import * as path from "node:path";
|
|
23
|
+
import { JOB_RUN_OPTIONS_CONTEXT_KEY } from "../job-run.js";
|
|
24
|
+
import { effectiveBotCompanies } from "../config.js";
|
|
25
|
+
import { ensureLocalJob, readLocalJobs, removeLocalJobsWhere, withJobsLock } from "../local-jobs.js";
|
|
26
|
+
export const HOT_SIGNAL_SYNC_JOB_KIND = "hot-signal-sync";
|
|
27
|
+
/**
|
|
28
|
+
* 06:30 every day, in the job's time zone (the stored EventBridge form of
|
|
29
|
+
* "30 6 * * *"). Only the first registration uses it; after that the job's
|
|
30
|
+
* own schedule in `jobs.json` is the one that runs.
|
|
31
|
+
*/
|
|
32
|
+
export const HOT_SIGNAL_SYNC_DEFAULT_CRON = "cron(30 6 * * ? *)";
|
|
33
|
+
/** Which built-in jobs this bot has registered once, so a removed one stays removed. */
|
|
34
|
+
const BUILTIN_JOBS_FILE = "builtin-jobs.json";
|
|
35
|
+
function markerKey(company) {
|
|
36
|
+
return `${HOT_SIGNAL_SYNC_JOB_KIND}:${company}`;
|
|
37
|
+
}
|
|
38
|
+
function readMarker(dir) {
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(fs.readFileSync(path.join(dir, BUILTIN_JOBS_FILE), "utf-8"));
|
|
41
|
+
if (parsed && parsed.v === 1 && parsed.registered && typeof parsed.registered === "object") {
|
|
42
|
+
return { v: 1, registered: { ...parsed.registered } };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Missing or unreadable: nothing registered yet.
|
|
47
|
+
}
|
|
48
|
+
return { v: 1, registered: {} };
|
|
49
|
+
}
|
|
50
|
+
function writeMarker(dir, marker) {
|
|
51
|
+
const file = path.join(dir, BUILTIN_JOBS_FILE);
|
|
52
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
53
|
+
fs.writeFileSync(tmp, JSON.stringify(marker, null, 2) + "\n", { mode: 0o600 });
|
|
54
|
+
fs.renameSync(tmp, file);
|
|
55
|
+
}
|
|
56
|
+
/** The private context that marks a job as this built-in, for one company. */
|
|
57
|
+
export function hotSignalSyncJobContext(company) {
|
|
58
|
+
return { [JOB_RUN_OPTIONS_CONTEXT_KEY]: { builtin: HOT_SIGNAL_SYNC_JOB_KIND, company } };
|
|
59
|
+
}
|
|
60
|
+
/** The company a job item syncs, when its context marks it as this built-in; else null. */
|
|
61
|
+
export function hotSignalSyncJobCompany(context) {
|
|
62
|
+
if (!context || typeof context !== "object" || Array.isArray(context))
|
|
63
|
+
return null;
|
|
64
|
+
const options = context[JOB_RUN_OPTIONS_CONTEXT_KEY];
|
|
65
|
+
if (!options || typeof options !== "object" || Array.isArray(options))
|
|
66
|
+
return null;
|
|
67
|
+
const { builtin, company } = options;
|
|
68
|
+
if (builtin !== HOT_SIGNAL_SYNC_JOB_KIND)
|
|
69
|
+
return null;
|
|
70
|
+
return typeof company === "string" && company.trim() ? company.trim() : null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The company a bot's hot-signal-sync job syncs: the bot's own company, else
|
|
74
|
+
* the first company it serves. Null = none, and no job is registered. There is
|
|
75
|
+
* no fallback to the HQ's active company: a personal bot syncs a company only
|
|
76
|
+
* when it was given one explicitly (`--company`), so the job never writes into
|
|
77
|
+
* a vault the bot does not belong to. Checked again each time the job fires.
|
|
78
|
+
*/
|
|
79
|
+
export function currentHotSignalSyncCompany(config) {
|
|
80
|
+
return config.companySlug ?? effectiveBotCompanies(config)[0] ?? null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Remove every hot-signal-sync job whose company is not `current` (all of them
|
|
84
|
+
* when `current` is null), and forget their registration so the company can be
|
|
85
|
+
* registered again if the bot is given it back. Returns the removed jobs.
|
|
86
|
+
*/
|
|
87
|
+
export function removeStaleHotSignalSyncJobs(dir, current) {
|
|
88
|
+
const removed = removeLocalJobsWhere(dir, (job) => {
|
|
89
|
+
const company = hotSignalSyncJobCompany(job.context);
|
|
90
|
+
return company !== null && company !== current;
|
|
91
|
+
});
|
|
92
|
+
const keep = current ? markerKey(current) : null;
|
|
93
|
+
withJobsLock(dir, () => {
|
|
94
|
+
const marker = readMarker(dir);
|
|
95
|
+
let changed = false;
|
|
96
|
+
for (const key of Object.keys(marker.registered)) {
|
|
97
|
+
if (key.startsWith(HOT_SIGNAL_SYNC_JOB_KIND + ":") && key !== keep) {
|
|
98
|
+
delete marker.registered[key];
|
|
99
|
+
changed = true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (changed)
|
|
103
|
+
writeMarker(dir, marker);
|
|
104
|
+
});
|
|
105
|
+
return removed;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Register the hot-signal-sync job for `company` once. Safe to call on every
|
|
109
|
+
* bot start: an existing job is left as it is (including a changed schedule),
|
|
110
|
+
* and a job the owner removed is not added again.
|
|
111
|
+
*/
|
|
112
|
+
export function ensureHotSignalSyncJob(dir, input, now = new Date()) {
|
|
113
|
+
const key = markerKey(input.company);
|
|
114
|
+
const matches = (job) => hotSignalSyncJobCompany(job.context) === input.company;
|
|
115
|
+
if (readMarker(dir).registered[key]) {
|
|
116
|
+
return { job: readLocalJobs(dir).find(matches) ?? null, created: false };
|
|
117
|
+
}
|
|
118
|
+
const { job, created } = ensureLocalJob(dir, matches, {
|
|
119
|
+
prompt: `Sync hot channels for ${input.company} and run the signals pass (runs in the bot, no model turn).`,
|
|
120
|
+
cron: HOT_SIGNAL_SYNC_DEFAULT_CRON,
|
|
121
|
+
timezone: input.timezone,
|
|
122
|
+
context: hotSignalSyncJobContext(input.company),
|
|
123
|
+
}, now);
|
|
124
|
+
withJobsLock(dir, () => {
|
|
125
|
+
const marker = readMarker(dir);
|
|
126
|
+
if (!marker.registered[key]) {
|
|
127
|
+
marker.registered[key] = now.toISOString();
|
|
128
|
+
writeMarker(dir, marker);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
return { job, created };
|
|
132
|
+
}
|
|
133
|
+
export const noLocalSignalsPass = async () => 0;
|
|
134
|
+
/** Run the scheduled sync once and describe it in plain words. Never throws. */
|
|
135
|
+
export async function runHotSignalSyncJob(input) {
|
|
136
|
+
const signalsPass = input.signalsPass ?? noLocalSignalsPass;
|
|
137
|
+
let report;
|
|
138
|
+
try {
|
|
139
|
+
report = await input.sync({
|
|
140
|
+
company: input.company,
|
|
141
|
+
trigger: "scheduled",
|
|
142
|
+
catchUp: input.catchUp,
|
|
143
|
+
signalsGenerated: signalsPass,
|
|
144
|
+
now: input.now,
|
|
145
|
+
...(input.sendAlert
|
|
146
|
+
? { signalsAlert: { sendOwnerDm: input.sendAlert, ...(input.log ? { log: input.log } : {}) } }
|
|
147
|
+
: {}),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
152
|
+
return { ok: false, text: `The hot-channel sync for ${input.company} failed: ${reason}`, report: null };
|
|
153
|
+
}
|
|
154
|
+
return { ok: report.exitCode === 0 && !report.signInRequired, text: describeSyncReport(input.company, report), report };
|
|
155
|
+
}
|
|
156
|
+
export function describeSyncReport(company, report) {
|
|
157
|
+
if (report.signInRequired) {
|
|
158
|
+
return `The hot-channel sync for ${company} did not run: ${report.notices.join(" ")}`;
|
|
159
|
+
}
|
|
160
|
+
if (report.skipped)
|
|
161
|
+
return report.notices.join(" ");
|
|
162
|
+
const landed = report.outcomes.reduce((sum, o) => sum + o.landed, 0);
|
|
163
|
+
const signals = report.entries.reduce((sum, e) => sum + e.signalsGenerated, 0);
|
|
164
|
+
const lines = [
|
|
165
|
+
`Hot-channel sync for ${company}: ${landed} new source${landed === 1 ? "" : "s"}, ${signals} signal${signals === 1 ? "" : "s"}.`,
|
|
166
|
+
];
|
|
167
|
+
for (const outcome of report.outcomes) {
|
|
168
|
+
if (outcome.status === "ok")
|
|
169
|
+
continue;
|
|
170
|
+
const label = outcome.appName ?? outcome.adapter;
|
|
171
|
+
lines.push(`- ${label}: ${outcome.reconnect?.prompt ?? outcome.message ?? outcome.status}`);
|
|
172
|
+
}
|
|
173
|
+
for (const notice of report.notices)
|
|
174
|
+
lines.push(notice);
|
|
175
|
+
return lines.join("\n");
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=hot-signal-sync.js.map
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* - A fire is recorded before it is handed over, so a crash never runs the
|
|
19
19
|
* same due time twice.
|
|
20
20
|
*/
|
|
21
|
-
import { type BotApi, type InboxItem } from "./api.js";
|
|
21
|
+
import { type BotApi, type BotJobRecord, type InboxItem } from "./api.js";
|
|
22
22
|
export interface LocalJob {
|
|
23
23
|
jobId: string;
|
|
24
24
|
prompt: string;
|
|
@@ -27,6 +27,12 @@ export interface LocalJob {
|
|
|
27
27
|
timezone: string;
|
|
28
28
|
context?: Record<string, unknown>;
|
|
29
29
|
createdAt: string;
|
|
30
|
+
/**
|
|
31
|
+
* When the schedule was last changed (`hq bot jobs --reschedule`). Fire
|
|
32
|
+
* times before it are never treated as missed, so a new schedule does not
|
|
33
|
+
* fire a catch-up for a time that only matches the new expression.
|
|
34
|
+
*/
|
|
35
|
+
scheduledFrom?: string;
|
|
30
36
|
/** The newest due time already handed to the bot. */
|
|
31
37
|
lastFiredAt?: string;
|
|
32
38
|
/** The run in progress, if any. */
|
|
@@ -72,6 +78,29 @@ export declare function addLocalJob(dir: string, input: {
|
|
|
72
78
|
timezone: string;
|
|
73
79
|
context?: Record<string, unknown>;
|
|
74
80
|
}, now?: Date): LocalJob;
|
|
81
|
+
/**
|
|
82
|
+
* Add a job unless one already matches, as one locked read-and-write, so two
|
|
83
|
+
* bot starts racing each other still leave a single job.
|
|
84
|
+
*/
|
|
85
|
+
export declare function ensureLocalJob(dir: string, matches: (job: LocalJob) => boolean, input: {
|
|
86
|
+
prompt: string;
|
|
87
|
+
cron: string;
|
|
88
|
+
timezone: string;
|
|
89
|
+
context?: Record<string, unknown>;
|
|
90
|
+
}, now?: Date): {
|
|
91
|
+
job: LocalJob;
|
|
92
|
+
created: boolean;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Change a job's schedule in place, keeping its id, instruction, context and
|
|
96
|
+
* run history. Returns null when the bot has no job with that id.
|
|
97
|
+
*/
|
|
98
|
+
export declare function rescheduleLocalJob(dir: string, jobId: string, schedule: {
|
|
99
|
+
cron: string;
|
|
100
|
+
timezone: string;
|
|
101
|
+
}, now?: Date): LocalJob | null;
|
|
102
|
+
/** Remove every job `matches` picks, as one locked read-and-write. Returns the removed jobs. */
|
|
103
|
+
export declare function removeLocalJobsWhere(dir: string, matches: (job: LocalJob) => boolean): LocalJob[];
|
|
75
104
|
/** Forget the once-per-minute check (tests, and a bot restarted in-process). */
|
|
76
105
|
export declare function resetLocalJobChecks(): void;
|
|
77
106
|
export declare function takeDueLocalJobRuns(dir: string, now?: Date): InboxItem[];
|
|
@@ -81,9 +110,16 @@ export declare function takeDueLocalJobRuns(dir: string, now?: Date): InboxItem[
|
|
|
81
110
|
* locally instead of being sent to the cloud.
|
|
82
111
|
*/
|
|
83
112
|
export declare function withLocalJobs(api: BotApi, dir: string, clock?: () => Date): BotApi;
|
|
113
|
+
/** What `hq bot jobs` uses: list, add, remove, and (local bots) reschedule. */
|
|
114
|
+
export type LocalJobsApi = Pick<BotApi, "listJobs" | "createJob" | "cancelJob"> & {
|
|
115
|
+
rescheduleJob?(agentUid: string, jobId: string, schedule: {
|
|
116
|
+
cron: string;
|
|
117
|
+
timezone: string;
|
|
118
|
+
}): Promise<BotJobRecord>;
|
|
119
|
+
};
|
|
84
120
|
/**
|
|
85
|
-
* `hq bot jobs` against the bot's own folder: the same
|
|
86
|
-
*
|
|
121
|
+
* `hq bot jobs` against the bot's own folder: the same calls the cloud client
|
|
122
|
+
* offers, plus reschedule, answered from `jobs.json`.
|
|
87
123
|
*/
|
|
88
|
-
export declare function localJobsClient(dir: string, clock?: () => Date):
|
|
124
|
+
export declare function localJobsClient(dir: string, clock?: () => Date): Required<LocalJobsApi>;
|
|
89
125
|
//# sourceMappingURL=local-jobs.d.ts.map
|