@indigoai-us/hq-cli 5.345.177 → 5.345.178
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 +31 -0
- package/dist/command-catalog.generated.d.ts +8 -0
- package/dist/command-catalog.generated.js +10 -0
- package/dist/commands/bot.d.ts +5 -0
- package/dist/commands/bot.js +186 -21
- package/dist/commands/cloud.d.ts +49 -0
- package/dist/commands/cloud.js +155 -15
- package/dist/commands/dm-attach.js +15 -3
- package/dist/commands/library-add.d.ts +9 -0
- package/dist/commands/library-add.js +19 -2
- package/dist/commands/run.js +9 -1
- package/dist/lib/bot/config.d.ts +2 -0
- package/dist/lib/bot/fleet-default-flag.d.ts +31 -0
- package/dist/lib/bot/fleet-default-flag.js +127 -0
- package/dist/lib/bot/fleet-local.d.ts +29 -0
- package/dist/lib/bot/fleet-local.js +85 -10
- package/dist/lib/bot/index.d.ts +1 -0
- package/dist/lib/bot/index.js +1 -0
- package/dist/lib/bot/local-config.js +1 -1
- package/dist/lib/daemon/catalog.d.ts +2 -0
- package/dist/lib/daemon/catalog.js +7 -2
- package/dist/lib/daemon/config.d.ts +2 -1
- package/dist/lib/daemon/config.js +4 -3
- package/dist/lib/mesh/live/daemon/work-subscribe.d.ts +1 -0
- package/dist/lib/mesh/live/daemon/work-subscribe.js +26 -6
- package/dist/run/hq-plugin.d.ts +3 -0
- package/dist/run/hq-plugin.js +18 -0
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.345.178] — 2026-10-29
|
|
6
|
+
- `hq dm send --attach` now uploads the bytes it read with their exact length, so a file changed after the size check no longer stalls and fails with `Upload failed (HTTP 400)`. A refused upload now names the storage error (for example `RequestTimeout`).
|
|
7
|
+
- `hq daemon disable mesh` now keeps `mesh` off on machines where it is not on by default,
|
|
8
|
+
such as an agent kit with `sync-core` turned on. In 5.345.176 the disable was not saved
|
|
9
|
+
there, so `sync-core` turned `mesh` back on; the 5.345.176 note that disabling `mesh`
|
|
10
|
+
still turns it off held only for machine types that run `mesh` by default.
|
|
11
|
+
- `hq run` now skips `# @optional` secrets the caller cannot read or that are missing, instead of failing the whole run. Required secrets still fail.
|
|
12
|
+
- The company-gated HQ Fleet default now supports new personal Claude, Codex and Grok bots. Codex and Grok bots stay stopped until their own provider sign-in succeeds; creation and status show the sign-in command. Cancelled sign-in can be retried without creating another bot. Existing bots and the explicit legacy runner are preserved.
|
|
13
|
+
- Where the company flag `agents.local-bots-hq-fleet-default` is on, `hq bot create`
|
|
14
|
+
makes personal Claude bots (the setup bot included) on the HQ Fleet Runtime.
|
|
15
|
+
These bots run on the owner's Claude Code login and need no sign-in of their own.
|
|
16
|
+
`--legacy-runtime` keeps the model-CLI runner, and Codex and Grok bots keep it
|
|
17
|
+
unless `--hq-fleet-runtime` is passed. `--hq-fleet-runtime` now takes
|
|
18
|
+
`--runtime claude|codex|grok` and `--model` to choose the bot's model login.
|
|
19
|
+
- HQ Fleet Runtime bots send their intro and run their kickoff on first start.
|
|
20
|
+
`hq bot status` and `hq bot list` read their status file.
|
|
21
|
+
`hq bot adopt` and `hq bot restore` bring them back on the runtime instead of as
|
|
22
|
+
Claude CLI bots. The bot's saved settings now keep the model login it uses.
|
|
23
|
+
- Pins hq-fleet-local v0.4.12. On that release a bot gives its owner the owner's
|
|
24
|
+
companies that turned the flag on, and gives a teammate only the ones the
|
|
25
|
+
teammate also belongs to. A tool call is blocked when an HQ check cannot run.
|
|
26
|
+
- The fleet-default flag is read for the owner's own active memberships, and a
|
|
27
|
+
bot is given only the companies that turned it on. `HQ_BOT_FLEET_DEFAULT=1`
|
|
28
|
+
skips the flag and gives the bot every active membership whose company
|
|
29
|
+
folder is on the computer.
|
|
30
|
+
- HQ Fleet Runtime bots install a smaller runtime (the mcp and anthropic extras
|
|
31
|
+
only), so creating one takes less time and disk space.
|
|
32
|
+
- `hq sync pull`, `push` and `now` (including `pull --all` and the first sync after setup) now refresh the HQ session when the access token expires during a long run and retry the rejected request once. Before, the sync read the token once at the start, so a run that outlasted the one-hour token failed every remaining target with 401 Unauthorized. If the session is rejected, the sync stops with "Run `hq login`, then rerun the sync". A network or server failure during the refresh gets its own message. The refreshed token must belong to the account that started the run: if a mid-run `hq login` or the machine-identity fallback changes the account, the sync stops instead of continuing as a different account. Feedback 3d3fab0b-9475-472d-91ce-9e7aa5a7443c.
|
|
33
|
+
- A sync run's account is now fixed once the run starts. If the session would switch to another account mid-run, including a fallback to this computer's machine credentials after a rejected refresh, the sync stops with an explanation instead of continuing as that account. A run whose starting token does not identify an account does not start.
|
|
34
|
+
- The daemon now records the newest change number the server announced for each vault, so the sync engine can skip background requests when it already has that change once it reads this.
|
|
35
|
+
|
|
5
36
|
## [5.345.177] — 2026-10-28
|
|
6
37
|
- The new sync engine now installs as release v0.1.21, and company vaults move to full mode only on v0.1.21 or later. A remote delete the server refuses because the file changed now re-reads the server's changes within seconds instead of waiting for the hourly check. Deleting a file that is already gone on the server now counts as done instead of stopping the sync until someone repairs it, and a held remote delete no longer stops the rest of the sync. A file this machine just replaced is no longer downloaded again or turned into a conflict copy while the server catches up.
|
|
7
38
|
|
|
@@ -18787,10 +18787,18 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
18787
18787
|
readonly flags: "--runtime <runtime>";
|
|
18788
18788
|
readonly description: "Model CLI to run the bot with: claude|codex|grok";
|
|
18789
18789
|
readonly hidden: false;
|
|
18790
|
+
}, {
|
|
18791
|
+
readonly flags: "--provider <provider>";
|
|
18792
|
+
readonly description: "HQ Fleet provider: claude|codex|grok (interactive Fleet creation prompts when omitted)";
|
|
18793
|
+
readonly hidden: false;
|
|
18790
18794
|
}, {
|
|
18791
18795
|
readonly flags: "--hq-fleet-runtime";
|
|
18792
18796
|
readonly description: "Create this personal bot with the local HQ Fleet Runtime";
|
|
18793
18797
|
readonly hidden: false;
|
|
18798
|
+
}, {
|
|
18799
|
+
readonly flags: "--legacy-runtime";
|
|
18800
|
+
readonly description: "Run this bot with the model CLI even where the HQ Fleet Runtime is the default";
|
|
18801
|
+
readonly hidden: false;
|
|
18794
18802
|
}, {
|
|
18795
18803
|
readonly flags: "--model <model>";
|
|
18796
18804
|
readonly description: "Model override passed to the runtime CLI";
|
|
@@ -22893,11 +22893,21 @@ export const COMMAND_CATALOG = [
|
|
|
22893
22893
|
"description": "Model CLI to run the bot with: claude|codex|grok",
|
|
22894
22894
|
"hidden": false
|
|
22895
22895
|
},
|
|
22896
|
+
{
|
|
22897
|
+
"flags": "--provider <provider>",
|
|
22898
|
+
"description": "HQ Fleet provider: claude|codex|grok (interactive Fleet creation prompts when omitted)",
|
|
22899
|
+
"hidden": false
|
|
22900
|
+
},
|
|
22896
22901
|
{
|
|
22897
22902
|
"flags": "--hq-fleet-runtime",
|
|
22898
22903
|
"description": "Create this personal bot with the local HQ Fleet Runtime",
|
|
22899
22904
|
"hidden": false
|
|
22900
22905
|
},
|
|
22906
|
+
{
|
|
22907
|
+
"flags": "--legacy-runtime",
|
|
22908
|
+
"description": "Run this bot with the model CLI even where the HQ Fleet Runtime is the default",
|
|
22909
|
+
"hidden": false
|
|
22910
|
+
},
|
|
22901
22911
|
{
|
|
22902
22912
|
"flags": "--model <model>",
|
|
22903
22913
|
"description": "Model override passed to the runtime CLI",
|
package/dist/commands/bot.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ export interface BotRow {
|
|
|
12
12
|
agentUid: string;
|
|
13
13
|
ownerUid: string;
|
|
14
14
|
runtime: BotRuntimeId;
|
|
15
|
+
needsSignIn?: boolean;
|
|
16
|
+
authCommand?: string;
|
|
17
|
+
serviceInstallPending?: boolean;
|
|
15
18
|
model?: string;
|
|
16
19
|
/** Thinking level the bot runs with (its setting, or the default). */
|
|
17
20
|
effort: string;
|
|
@@ -135,7 +138,9 @@ export declare function resolveCreateKind(opts: {
|
|
|
135
138
|
};
|
|
136
139
|
export interface BotCreateOptions {
|
|
137
140
|
runtime?: string;
|
|
141
|
+
provider?: string;
|
|
138
142
|
hqFleetRuntime?: boolean;
|
|
143
|
+
legacyRuntime?: boolean;
|
|
139
144
|
model?: string;
|
|
140
145
|
effort?: string;
|
|
141
146
|
autoApprove?: boolean;
|
package/dist/commands/bot.js
CHANGED
|
@@ -52,7 +52,7 @@ import { isAncestorProcess } from "../lib/bot/self-process.js";
|
|
|
52
52
|
import { runtimeDisplayName, signInCommand } from "../lib/bot/runtime-sign-in.js";
|
|
53
53
|
import { classifyRuntimeFailure, runtimeFailureReason } from "../lib/bot/runtime-failure.js";
|
|
54
54
|
import { PROBE_DEFAULT_TIMEOUT_MS, probeBotRuntime } from "../lib/bot/probe.js";
|
|
55
|
-
import { BotApi, BotApiError, BOT_RESTORE_DEFAULTS, buildBotLocalConfig, describeBotLocalConfig, orphanBotRunMessage, parseBotLocalConfig, removeOrphanBotDaemon, botDaemonStatus, botDir, botCredsPath, botLogPath, botsRoot, botTokenStateDir, botWorkerRelDir, buildBotDaemonPaths, createBotLogger, deleteBotCreds, installBotDaemon, repairBotDaemonIfStale, healBotDaemonNodeFromJob, introDmText, isBotMemoryMode, botLocalMemoryDir, botMemoryMode, resolveBotMemoryDir, validateBotIntro, validateBotDisplayName, validateBotKickoff, validateBotEffort, validateBotModel, effectiveBotEffort, BOT_EFFORT_LEVELS, DEFAULT_BOT_EFFORT, BOT_MEMORY_MODES, BOT_KINDS, isBotKind, effectiveBotKind, effectiveBotCompanies, normalizeBotCompanies, SETUP_BOT_WORKER_ID, isBotRuntimeId, localNameForRemoteBot, remoteBotRunnability, isPidAlive, listBotWorkerOptions, patchBotConfig, readBotConfig, readBotCredsIdentity, readBotStatus, runtimeCrashSinceStart, resolveBotWorker, runBot, runtimeFor, scaffoldBotMemory, scaffoldBotWorker, startBotDaemon, stopBotDaemon, uninstallBotDaemon, validateBotName, writeBotConfig, writeBotCreds, BOT_RUNTIMES, BOT_INTRO_MAX_CHARS, BOT_DISPLAY_NAME_MAX_CHARS, BOT_KICKOFF_MAX_CHARS, detectBotPlatform, withBotLock, localBotRole, remoteBotRole, remoteBotDisplayName, personFacingName, writeCloudDisplayName, setupSlugFor, ASSISTANT_ROLE, HQ_FLEET_RUNTIME_ENGINE_ID, authenticateFleetLocalBot, bootstrapFleetLocalBot, buildSystemPrompt, controlFleetLocalBot, ensureFleetPython, fleetLocalGatewayPid, refreshFleetLocalInstructions, fleetLocalPromotionRuntime, installPinnedFleetLocalDistribution, readPromptSources, removeFleetLocalBot, } from "../lib/bot/index.js";
|
|
55
|
+
import { BotApi, BotApiError, BOT_RESTORE_DEFAULTS, buildBotLocalConfig, describeBotLocalConfig, orphanBotRunMessage, parseBotLocalConfig, removeOrphanBotDaemon, botDaemonStatus, botDir, botCredsPath, botLogPath, botsRoot, botTokenStateDir, botWorkerRelDir, buildBotDaemonPaths, createBotLogger, deleteBotCreds, installBotDaemon, repairBotDaemonIfStale, healBotDaemonNodeFromJob, introDmText, isBotMemoryMode, botLocalMemoryDir, botMemoryMode, resolveBotMemoryDir, validateBotIntro, validateBotDisplayName, validateBotKickoff, validateBotEffort, validateBotModel, effectiveBotEffort, BOT_EFFORT_LEVELS, DEFAULT_BOT_EFFORT, BOT_MEMORY_MODES, BOT_KINDS, isBotKind, effectiveBotKind, effectiveBotCompanies, normalizeBotCompanies, SETUP_BOT_WORKER_ID, isBotRuntimeId, localNameForRemoteBot, remoteBotRunnability, isPidAlive, listBotWorkerOptions, patchBotConfig, readBotConfig, readBotCredsIdentity, readBotStatus, runtimeCrashSinceStart, resolveBotWorker, runBot, runtimeFor, scaffoldBotMemory, scaffoldBotWorker, startBotDaemon, stopBotDaemon, uninstallBotDaemon, validateBotName, writeBotConfig, writeBotCreds, BOT_RUNTIMES, BOT_INTRO_MAX_CHARS, BOT_DISPLAY_NAME_MAX_CHARS, BOT_KICKOFF_MAX_CHARS, detectBotPlatform, withBotLock, localBotRole, remoteBotRole, remoteBotDisplayName, personFacingName, writeCloudDisplayName, setupSlugFor, ASSISTANT_ROLE, HQ_FLEET_RUNTIME_ENGINE_ID, authenticateFleetLocalBot, bootstrapFleetLocalBot, buildSystemPrompt, controlFleetLocalBot, ensureFleetPython, fleetLocalGatewayPid, fleetProviderForRuntime, importFleetLocalMachineCredential, fleetProviderNeedsSignIn, fleetDefaultCompanies, fleetOwnerCompanies, fleetRuntimeIsDefault, refreshFleetLocalInstructions, fleetLocalPromotionRuntime, installPinnedFleetLocalDistribution, readPromptSources, removeFleetLocalBot, } from "../lib/bot/index.js";
|
|
56
56
|
function collectRepeated(value, previous) {
|
|
57
57
|
return [...previous, value];
|
|
58
58
|
}
|
|
@@ -144,11 +144,25 @@ function fleetBotSystemPrompt(name, config, overrides = {}) {
|
|
|
144
144
|
}),
|
|
145
145
|
});
|
|
146
146
|
}
|
|
147
|
+
function fleetAuthCommand(name, config) {
|
|
148
|
+
return `hq bot auth ${name} --provider ${config.runtime === "grok" ? "grok" : config.runtime === "codex" ? "chatgpt" : "claude"}`;
|
|
149
|
+
}
|
|
147
150
|
function runFleetRuntimeControl(name, config, action, json) {
|
|
151
|
+
if ((action === "start" || action === "restart") && config.fleetAuthPending === true) {
|
|
152
|
+
const authCommand = fleetAuthCommand(name, config);
|
|
153
|
+
if (json)
|
|
154
|
+
console.log(JSON.stringify({ ok: false, name, running: false, state: "stopped", needsSignIn: true, authCommand }, null, 2));
|
|
155
|
+
else
|
|
156
|
+
throw Object.assign(new Error(`Sign this bot in first: ${authCommand}`), { expected: true });
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
148
159
|
if (action === "start" || action === "restart") {
|
|
149
160
|
refreshFleetLocalInstructions(config.agentUid, fleetBotSystemPrompt(name, config));
|
|
150
161
|
}
|
|
151
|
-
const
|
|
162
|
+
const installService = config.fleetServiceInstallPending === true && (action === "start" || action === "restart");
|
|
163
|
+
const result = controlFleetLocalBot(config.agentUid, installService ? "install" : action);
|
|
164
|
+
if (installService && result.ok)
|
|
165
|
+
patchBotConfig(botDir(name), { fleetServiceInstallPending: false, enabled: true });
|
|
152
166
|
if (json) {
|
|
153
167
|
console.log(JSON.stringify({ name, runtime: HQ_FLEET_RUNTIME_ENGINE_ID, ...result }, null, 2));
|
|
154
168
|
return;
|
|
@@ -192,7 +206,7 @@ function rowFor(name, opts = {}) {
|
|
|
192
206
|
const dir = botDir(name);
|
|
193
207
|
const config = readBotConfig(dir);
|
|
194
208
|
const fleet = isFleetRuntimeBot(config);
|
|
195
|
-
const status =
|
|
209
|
+
const status = readBotStatus(dir);
|
|
196
210
|
const pid = fleet ? fleetLocalGatewayPid(config.agentUid) : (status?.pid ?? null);
|
|
197
211
|
const processAlive = pid !== null && isPidAlive(pid) && (fleet || status?.state === "running");
|
|
198
212
|
// Turns this bot has taken and not finished: what "thinking" means.
|
|
@@ -220,7 +234,7 @@ function rowFor(name, opts = {}) {
|
|
|
220
234
|
model: config.model,
|
|
221
235
|
effort: effectiveBotEffort(config),
|
|
222
236
|
effortIsDefault: !config.effort,
|
|
223
|
-
state: processAlive ? "running" : status?.state === "failed" ? "failed" : "stopped",
|
|
237
|
+
state: processAlive ? "running" : !fleet && status?.state === "failed" ? "failed" : "stopped",
|
|
224
238
|
pid: processAlive ? pid : null,
|
|
225
239
|
processAlive,
|
|
226
240
|
hosting: hasActivePromotion(dir, config.agentUid, config.ownerUid) ? "cloud" : "local",
|
|
@@ -234,8 +248,10 @@ function rowFor(name, opts = {}) {
|
|
|
234
248
|
busySince: processAlive ? (inflightTurns[0]?.startedAt ?? null) : null,
|
|
235
249
|
runtimeSignIn: status?.runtimeSignIn ?? null,
|
|
236
250
|
runtimeSignInCheck: status?.runtimeSignInCheck ?? null,
|
|
237
|
-
runtimeSignedIn: runtimeSignedIn(status, processAlive),
|
|
238
|
-
|
|
251
|
+
runtimeSignedIn: config.fleetAuthPending === true ? false : runtimeSignedIn(status, processAlive),
|
|
252
|
+
...(fleet && config.fleetAuthPending === true ? { needsSignIn: true, authCommand: fleetAuthCommand(name, config) } : {}),
|
|
253
|
+
...(fleet && config.fleetServiceInstallPending === true ? { needsSignIn: false, serviceInstallPending: true } : {}),
|
|
254
|
+
signInCommand: fleet ? fleetAuthCommand(name, config) : signInCommand(config.runtime),
|
|
239
255
|
runtimeCrash: processAlive ? runtimeCrashSinceStart(status) : null,
|
|
240
256
|
daemonInstalled,
|
|
241
257
|
daemonLoaded,
|
|
@@ -340,6 +356,8 @@ function stopProcess(dir) {
|
|
|
340
356
|
async function startBot(name, dir, config, output, extra = {}) {
|
|
341
357
|
if (hasPromotionHold(dir))
|
|
342
358
|
throw new Error("This bot is held for cloud promotion. Continue its promotion before changing local settings or lifecycle.");
|
|
359
|
+
if (isFleetRuntimeBot(config))
|
|
360
|
+
return startFleetRuntimeBot(name, dir, config, output, extra);
|
|
343
361
|
if (config.enabled === false)
|
|
344
362
|
patchBotConfig(dir, { enabled: true });
|
|
345
363
|
const paths = buildBotDaemonPaths({ name, botDir: dir, hqRoot: config.hqRoot });
|
|
@@ -401,6 +419,41 @@ async function startBot(name, dir, config, output, extra = {}) {
|
|
|
401
419
|
}
|
|
402
420
|
return { running, how };
|
|
403
421
|
}
|
|
422
|
+
/** Start (or restart) an HQ Fleet Runtime bot under its own service, with fresh instructions. */
|
|
423
|
+
async function startFleetRuntimeBot(name, dir, config, output, extra) {
|
|
424
|
+
if (config.fleetAuthPending === true) {
|
|
425
|
+
const authCommand = fleetAuthCommand(name, config);
|
|
426
|
+
if (output === "json")
|
|
427
|
+
console.log(JSON.stringify({ ok: false, name, running: false, needsSignIn: true, authCommand }, null, 2));
|
|
428
|
+
else if (output !== "silent")
|
|
429
|
+
throw Object.assign(new Error(`Sign this bot in first: ${authCommand}`), { expected: true });
|
|
430
|
+
return { running: false, how: "waiting for sign-in" };
|
|
431
|
+
}
|
|
432
|
+
refreshFleetLocalInstructions(config.agentUid, fleetBotSystemPrompt(name, config));
|
|
433
|
+
const before = readBotStatus(dir);
|
|
434
|
+
const alreadyRunning = !!before && before.state === "running" && isPidAlive(before.pid);
|
|
435
|
+
const installService = config.fleetServiceInstallPending === true;
|
|
436
|
+
const result = controlFleetLocalBot(config.agentUid, installService ? "install" : alreadyRunning ? "restart" : "start");
|
|
437
|
+
if (installService && result.ok)
|
|
438
|
+
patchBotConfig(dir, { fleetServiceInstallPending: false, enabled: true });
|
|
439
|
+
const how = alreadyRunning ? "restarted by the HQ Fleet Runtime" : "started by the HQ Fleet Runtime";
|
|
440
|
+
const running = result.ok === true && (await waitFor(() => {
|
|
441
|
+
const s = readBotStatus(dir);
|
|
442
|
+
return !!s && s.state === "running" && isPidAlive(s.pid);
|
|
443
|
+
}, 45_000));
|
|
444
|
+
const status = readBotStatus(dir);
|
|
445
|
+
if (output === "silent")
|
|
446
|
+
return { running, how };
|
|
447
|
+
if (output === "json") {
|
|
448
|
+
console.log(JSON.stringify({ ok: running, name, ...extra, running, how, state: status?.state ?? "unknown", pid: status?.pid ?? null, lastError: null }, null, 2));
|
|
449
|
+
return { running, how };
|
|
450
|
+
}
|
|
451
|
+
if (running)
|
|
452
|
+
console.log(chalk.green(`${name} is running`) + chalk.dim(` (${how})`));
|
|
453
|
+
else
|
|
454
|
+
console.log(chalk.yellow(`${name} did not report running within 45s`) + chalk.dim(` logs: ~/.hq-fleet/profiles/${config.agentUid}/logs`));
|
|
455
|
+
return { running, how };
|
|
456
|
+
}
|
|
404
457
|
/**
|
|
405
458
|
* `output`: "json" | "text" print a result; "silent" prints nothing — used
|
|
406
459
|
* when stop is a step inside rm/restart, whose own JSON must be the only
|
|
@@ -436,6 +489,14 @@ function requestSelfStopDrain(botPid) {
|
|
|
436
489
|
async function stopBot(name, dir, config, output) {
|
|
437
490
|
if (hasPromotionHold(dir))
|
|
438
491
|
throw new Error("This bot is held for cloud promotion. Continue its promotion before changing local settings or lifecycle.");
|
|
492
|
+
if (isFleetRuntimeBot(config)) {
|
|
493
|
+
const result = controlFleetLocalBot(config.agentUid, "stop");
|
|
494
|
+
if (output === "json")
|
|
495
|
+
console.log(JSON.stringify({ ok: result.ok === true, name, stopped: result.ok === true, how: "stopped by the HQ Fleet Runtime" }, null, 2));
|
|
496
|
+
else if (output === "text")
|
|
497
|
+
console.log(chalk.green(`${name} stopped`));
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
439
500
|
const paths = buildBotDaemonPaths({ name, botDir: dir, hqRoot: config.hqRoot });
|
|
440
501
|
const hqDaemon = daemonPaths();
|
|
441
502
|
const before = readBotStatus(dir);
|
|
@@ -546,6 +607,28 @@ export function resolveCreateKind(opts, worker) {
|
|
|
546
607
|
}
|
|
547
608
|
return { kind, companies };
|
|
548
609
|
}
|
|
610
|
+
async function promptFleetProvider() {
|
|
611
|
+
if (process.stdin.isTTY !== true || process.stderr.isTTY !== true) {
|
|
612
|
+
throw Object.assign(new Error("--provider is required with --hq-fleet-runtime in a non-interactive command (claude, codex, or grok)."), { expected: true });
|
|
613
|
+
}
|
|
614
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
615
|
+
try {
|
|
616
|
+
const answer = await new Promise((resolve) => {
|
|
617
|
+
rl.question("Provider: 1) Codex 2) Claude 3) Grok [1]: ", resolve);
|
|
618
|
+
});
|
|
619
|
+
const selected = answer.trim().toLowerCase();
|
|
620
|
+
if (selected === "" || selected === "1" || selected === "codex")
|
|
621
|
+
return "codex";
|
|
622
|
+
if (selected === "2" || selected === "claude")
|
|
623
|
+
return "claude";
|
|
624
|
+
if (selected === "3" || selected === "grok")
|
|
625
|
+
return "grok";
|
|
626
|
+
throw Object.assign(new Error("Provider must be claude, codex, or grok."), { expected: true });
|
|
627
|
+
}
|
|
628
|
+
finally {
|
|
629
|
+
rl.close();
|
|
630
|
+
}
|
|
631
|
+
}
|
|
549
632
|
/**
|
|
550
633
|
* `hq bot create`. Runs under the bot's lock, so a create racing an adopt,
|
|
551
634
|
* restore, rm or set of the same bot waits for it instead of tripping over
|
|
@@ -664,11 +747,21 @@ async function finishExistingSetupBot(name, dir, config, displayName, opts) {
|
|
|
664
747
|
console.log(chalk.yellow(`${name} did not report running within 15s`) + chalk.dim(` logs: hq bot logs ${name}`));
|
|
665
748
|
}
|
|
666
749
|
async function runBotCreateLocked(name, opts) {
|
|
667
|
-
const
|
|
668
|
-
if (
|
|
669
|
-
throw Object.assign(new Error("--hq-fleet-runtime
|
|
750
|
+
const requestedFleetRuntime = opts.hqFleetRuntime === true;
|
|
751
|
+
if (requestedFleetRuntime && opts.legacyRuntime) {
|
|
752
|
+
throw Object.assign(new Error("Pick one of --hq-fleet-runtime and --legacy-runtime."), { expected: true });
|
|
753
|
+
}
|
|
754
|
+
if (requestedFleetRuntime && (opts.effort || opts.lean)) {
|
|
755
|
+
throw Object.assign(new Error("--hq-fleet-runtime sets its own thinking level. Do not combine it with --effort or --lean."), { expected: true });
|
|
670
756
|
}
|
|
671
|
-
|
|
757
|
+
if (opts.provider && !isBotRuntimeId(opts.provider)) {
|
|
758
|
+
throw Object.assign(new Error(`--provider must be one of ${BOT_RUNTIMES.join(", ")}`), { expected: true });
|
|
759
|
+
}
|
|
760
|
+
if (opts.provider && opts.runtime && opts.provider !== opts.runtime) {
|
|
761
|
+
throw Object.assign(new Error("--provider and --runtime must name the same provider when both are used."), { expected: true });
|
|
762
|
+
}
|
|
763
|
+
const selectedProvider = opts.provider ?? opts.runtime ?? (requestedFleetRuntime ? await promptFleetProvider() : undefined);
|
|
764
|
+
const runtime = selectedProvider ?? "claude";
|
|
672
765
|
if (!isBotRuntimeId(runtime)) {
|
|
673
766
|
throw Object.assign(new Error(`--runtime must be one of ${BOT_RUNTIMES.join(", ")}`), { expected: true });
|
|
674
767
|
}
|
|
@@ -703,6 +796,15 @@ async function runBotCreateLocked(name, opts) {
|
|
|
703
796
|
// cost an identity).
|
|
704
797
|
const worker = opts.worker?.trim() ? resolveBotWorker(hqRoot, opts.worker) : null;
|
|
705
798
|
const { kind, companies } = resolveCreateKind(opts, worker);
|
|
799
|
+
// Companies with the fleet-default flag get their personal bots on
|
|
800
|
+
// the HQ Fleet Runtime; --legacy-runtime keeps the model-CLI runner.
|
|
801
|
+
const fleetCandidate = requestedFleetRuntime ||
|
|
802
|
+
(!opts.legacyRuntime && kind === "personal" && !opts.effort && !opts.lean);
|
|
803
|
+
// Owner turns on the bot may use only these companies: the owner's own,
|
|
804
|
+
// and only where the company turned the flag on.
|
|
805
|
+
const flaggedCompanies = fleetCandidate && kind === "personal" ? await fleetDefaultCompanies(hqRoot) : [];
|
|
806
|
+
const useFleetRuntime = requestedFleetRuntime || (fleetCandidate && fleetRuntimeIsDefault(flaggedCompanies));
|
|
807
|
+
const ownerCompanies = useFleetRuntime && kind === "personal" ? await fleetOwnerCompanies() : [];
|
|
706
808
|
if (useFleetRuntime && kind !== "personal") {
|
|
707
809
|
throw Object.assign(new Error("--hq-fleet-runtime creates a personal bot. Drop --kind company and --company."), {
|
|
708
810
|
expected: true,
|
|
@@ -726,6 +828,8 @@ async function runBotCreateLocked(name, opts) {
|
|
|
726
828
|
? await installPinnedFleetLocalDistribution()
|
|
727
829
|
: null;
|
|
728
830
|
const fleetPython = fleetInstallation ? await ensureFleetPython() : null;
|
|
831
|
+
const fleetProvider = fleetInstallation ? fleetProviderForRuntime(runtime, model) : null;
|
|
832
|
+
const fleetAuthPending = fleetProvider !== null && fleetProviderNeedsSignIn(fleetProvider);
|
|
729
833
|
const token = await ownerToken();
|
|
730
834
|
const owner = await resolveCallerPersonUid(token, undefined, { command: "hq bot create" });
|
|
731
835
|
if (!owner)
|
|
@@ -932,7 +1036,8 @@ async function runBotCreateLocked(name, opts) {
|
|
|
932
1036
|
...(kickoff ? { kickoff } : {}),
|
|
933
1037
|
...(opts.lean && runtime === "claude" ? { lean: true } : {}),
|
|
934
1038
|
createdAt: new Date().toISOString(),
|
|
935
|
-
enabled:
|
|
1039
|
+
enabled: !fleetAuthPending,
|
|
1040
|
+
...(fleetAuthPending ? { fleetAuthPending: true } : {}),
|
|
936
1041
|
};
|
|
937
1042
|
writeBotConfig(dir, config);
|
|
938
1043
|
// A company bot acts as itself, so it must be a member of each of its
|
|
@@ -971,7 +1076,13 @@ async function runBotCreateLocked(name, opts) {
|
|
|
971
1076
|
installation: fleetInstallation,
|
|
972
1077
|
machineCredentials: botCredsPath(dir),
|
|
973
1078
|
systemPrompt: fleetBotSystemPrompt(name, config, { kind, companies }),
|
|
974
|
-
|
|
1079
|
+
provider: fleetProvider,
|
|
1080
|
+
ownerCompanies,
|
|
1081
|
+
firstStart: {
|
|
1082
|
+
intro: introDmText(name, runtime, intro, { kind, companies }),
|
|
1083
|
+
...(kickoff ? { kickoff } : {}),
|
|
1084
|
+
},
|
|
1085
|
+
}, { start: opts.start !== false && !fleetAuthPending, python: fleetPython })
|
|
975
1086
|
: null;
|
|
976
1087
|
const fleetProfile = fleetBootstrap?.profile ?? null;
|
|
977
1088
|
const runtimeLabel = fleetInstallation ? HQ_FLEET_RUNTIME_ENGINE_ID : runtime;
|
|
@@ -998,6 +1109,7 @@ async function runBotCreateLocked(name, opts) {
|
|
|
998
1109
|
kickoff: kickoff ?? null,
|
|
999
1110
|
model: model ?? null,
|
|
1000
1111
|
effort: fleetInstallation ? null : effort ?? DEFAULT_BOT_EFFORT,
|
|
1112
|
+
...(fleetProvider ? { provider: fleetProvider.id, needsSignIn: fleetAuthPending, ...(fleetAuthPending ? { authCommand: fleetAuthCommand(name, config) } : {}) } : {}),
|
|
1001
1113
|
};
|
|
1002
1114
|
let daemonMessage = null;
|
|
1003
1115
|
if (!fleetInstallation && opts.daemon !== false && process.platform === "darwin") {
|
|
@@ -1015,6 +1127,9 @@ async function runBotCreateLocked(name, opts) {
|
|
|
1015
1127
|
console.log(chalk.dim(` state: ${dir}`));
|
|
1016
1128
|
if (fleetProfile)
|
|
1017
1129
|
console.log(chalk.dim(` profile: ${fleetProfile}`));
|
|
1130
|
+
if (fleetProvider && fleetProviderNeedsSignIn(fleetProvider)) {
|
|
1131
|
+
console.log(chalk.yellow(` sign it in before it can answer: hq bot auth ${name} --provider ${runtime === "grok" ? "grok" : "chatgpt"}`));
|
|
1132
|
+
}
|
|
1018
1133
|
if (daemonMessage)
|
|
1019
1134
|
console.log(chalk.dim(` ${daemonMessage}`));
|
|
1020
1135
|
}
|
|
@@ -1232,25 +1347,26 @@ async function runBotList(opts) {
|
|
|
1232
1347
|
}
|
|
1233
1348
|
async function runBotStatus(nameArg, opts) {
|
|
1234
1349
|
const { name, dir, config } = requireBot(nameArg);
|
|
1235
|
-
|
|
1236
|
-
runFleetRuntimeControl(name, config, "status", opts.json === true);
|
|
1237
|
-
return;
|
|
1238
|
-
}
|
|
1350
|
+
const fleet = isFleetRuntimeBot(config);
|
|
1239
1351
|
const row = rowFor(name);
|
|
1240
1352
|
await decorateOnline([row]);
|
|
1241
1353
|
const status = readBotStatus(dir);
|
|
1242
1354
|
const creds = readBotCredsIdentity(dir);
|
|
1243
1355
|
if (opts.json) {
|
|
1244
|
-
console.log(JSON.stringify({ ...row, credsPresent: !!creds, status }, null, 2));
|
|
1356
|
+
console.log(JSON.stringify({ ...row, ...(fleet ? { engine: config.engine } : {}), credsPresent: fleet || !!creds, status }, null, 2));
|
|
1245
1357
|
return;
|
|
1246
1358
|
}
|
|
1247
1359
|
console.log(`${chalk.bold(name)} ${row.agentUid}`);
|
|
1360
|
+
if (fleet)
|
|
1361
|
+
console.log(` engine: HQ Fleet Runtime ${config.engine?.version ?? ""} ${chalk.dim(`logs ~/.hq-fleet/profiles/${config.agentUid}/logs`)}`);
|
|
1248
1362
|
console.log(` kind: ${describeKind(row.kind, row.companies)}`);
|
|
1249
1363
|
console.log(` runtime: ${row.runtime} model ${row.model ?? "default"} thinking ${row.effort}${row.effortIsDefault ? " (default)" : ""}`);
|
|
1250
1364
|
if (row.workerId)
|
|
1251
1365
|
console.log(` worker: ${row.workerId}${row.companySlug ? ` (company ${row.companySlug})` : ""} ${chalk.dim(row.workerDir)}`);
|
|
1252
1366
|
console.log(` memory: ${row.memory} ${chalk.dim(row.memoryDir)}`);
|
|
1253
1367
|
console.log(` process: ${row.processAlive ? chalk.green(`running (pid ${row.pid})`) : chalk.gray(row.state)}`);
|
|
1368
|
+
if (row.needsSignIn)
|
|
1369
|
+
console.log(` sign-in: required (${row.authCommand})`);
|
|
1254
1370
|
console.log(` HQ online: ${humanOnline(row)}${row.lastHeartbeatAt ? chalk.dim(` last heartbeat ${row.lastHeartbeatAt}`) : ""}${row.lastHeartbeatOk === false && status?.lastHeartbeatError ? chalk.dim(` (${status.lastHeartbeatError})`) : ""}`);
|
|
1255
1371
|
if (status?.lastInboxPollOk === false) {
|
|
1256
1372
|
console.log(` inbox: ${chalk.red("cannot receive")}${status.lastInboxError ? chalk.dim(` ${status.lastInboxError}`) : ""}`);
|
|
@@ -1263,7 +1379,7 @@ async function runBotStatus(nameArg, opts) {
|
|
|
1263
1379
|
if (row.daemonNodeMissing) {
|
|
1264
1380
|
console.log(` ${chalk.red(`node missing: ${row.daemonNodeMissing}`)} — launchd cannot start ${name}; fix with: hq bot start ${name}`);
|
|
1265
1381
|
}
|
|
1266
|
-
console.log(` creds: ${creds ? `present for ${creds.entityUid}` : chalk.red("missing")}`);
|
|
1382
|
+
console.log(` creds: ${fleet ? "in the system keychain" : creds ? `present for ${creds.entityUid}` : chalk.red("missing")}`);
|
|
1267
1383
|
if (status) {
|
|
1268
1384
|
console.log(` started: ${status.startedAt} restarts: ${status.restarts} replies: ${status.repliesSent}`);
|
|
1269
1385
|
if (status.cliVersion)
|
|
@@ -1729,17 +1845,30 @@ async function runBotAuth(nameArg, opts) {
|
|
|
1729
1845
|
const result = authenticateFleetLocalBot({
|
|
1730
1846
|
agentUid: b.config.agentUid,
|
|
1731
1847
|
provider: opts.provider,
|
|
1848
|
+
...(b.config.fleetAuthPending === true ? { installService: true } : {}),
|
|
1732
1849
|
...(opts.model ? { model: opts.model } : {}),
|
|
1733
1850
|
...(apiKey ? { apiKey } : {}),
|
|
1734
1851
|
...(opts.baseUrl ? { baseUrl: opts.baseUrl } : {}),
|
|
1735
1852
|
...(opts.transport ? { transport: opts.transport } : {}),
|
|
1736
1853
|
openBrowser: opts.browser !== false,
|
|
1737
1854
|
});
|
|
1855
|
+
const signedInRuntime = result.runtimeProvider === "anthropic" ? "claude" : result.runtimeProvider === "xai-oauth" ? "grok" : result.runtimeProvider === "openai-codex" ? "codex" : null;
|
|
1856
|
+
if (result.authenticated !== true)
|
|
1857
|
+
throw Object.assign(new Error("The provider has not completed sign-in."), { expected: true });
|
|
1858
|
+
patchBotConfig(b.dir, {
|
|
1859
|
+
...(signedInRuntime ? { runtime: signedInRuntime } : {}),
|
|
1860
|
+
model: result.model,
|
|
1861
|
+
...(b.config.fleetAuthPending === true ? { fleetAuthPending: false, fleetServiceInstallPending: !result.restarted, enabled: result.restarted } : {}),
|
|
1862
|
+
});
|
|
1863
|
+
const pid = fleetLocalGatewayPid(b.config.agentUid);
|
|
1864
|
+
const running = pid !== null && isPidAlive(pid);
|
|
1738
1865
|
if (opts.json) {
|
|
1739
|
-
console.log(JSON.stringify({ ok: true, name: b.name, runtime: HQ_FLEET_RUNTIME_ENGINE_ID, ...result }, null, 2));
|
|
1866
|
+
console.log(JSON.stringify({ ok: true, name: b.name, runtime: HQ_FLEET_RUNTIME_ENGINE_ID, ...result, needsSignIn: false, running, serviceInstallPending: b.config.fleetAuthPending === true && !result.restarted }, null, 2));
|
|
1740
1867
|
return;
|
|
1741
1868
|
}
|
|
1742
|
-
console.log(
|
|
1869
|
+
console.log(running
|
|
1870
|
+
? chalk.green(`${b.name} is signed in to ${result.provider} and running ${result.model}.`)
|
|
1871
|
+
: chalk.yellow(`${b.name} is signed in to ${result.provider}, but did not start. Run hq bot start ${b.name}.`));
|
|
1743
1872
|
}
|
|
1744
1873
|
// ── adopt / restore (bring a bot you own back to this computer) ──────────────
|
|
1745
1874
|
/**
|
|
@@ -1993,6 +2122,9 @@ async function adoptRemoteBotLocked(input) {
|
|
|
1993
2122
|
region: DEFAULT_COGNITO.region,
|
|
1994
2123
|
});
|
|
1995
2124
|
const local = parseBotLocalConfig(reissued.localConfig ?? bot.localConfig);
|
|
2125
|
+
const fleetEngine = local?.engine?.id === HQ_FLEET_RUNTIME_ENGINE_ID;
|
|
2126
|
+
if (fleetEngine && local && !local.runtime)
|
|
2127
|
+
local.runtime = "codex";
|
|
1996
2128
|
const { config, notes } = configFromLocalConfig({
|
|
1997
2129
|
name,
|
|
1998
2130
|
agentUid: bot.agentUid,
|
|
@@ -2003,14 +2135,40 @@ async function adoptRemoteBotLocked(input) {
|
|
|
2003
2135
|
...(input.overrides ? { overrides: input.overrides } : {}),
|
|
2004
2136
|
...(input.preserveFirstStart ? { preserveFirstStart: true } : {}),
|
|
2005
2137
|
});
|
|
2138
|
+
// A bot made on the HQ Fleet Runtime comes back on it, on the release this CLI pins.
|
|
2139
|
+
const fleetInstallation = fleetEngine ? await installPinnedFleetLocalDistribution() : null;
|
|
2140
|
+
const fleetPython = fleetInstallation ? await ensureFleetPython() : null;
|
|
2141
|
+
if (fleetInstallation)
|
|
2142
|
+
config.engine = fleetInstallation.engine;
|
|
2006
2143
|
writeBotConfig(dir, config);
|
|
2007
2144
|
const scaffoldOpts = { memoryDir: config.memoryDir };
|
|
2008
2145
|
const scaffold = config.workerSource === "worker"
|
|
2009
2146
|
? scaffoldBotMemory(input.hqRoot, name, undefined, scaffoldOpts)
|
|
2010
2147
|
: scaffoldBotWorker(input.hqRoot, name, bot.agentUid, config.runtime, undefined, scaffoldOpts);
|
|
2148
|
+
if (fleetInstallation && fleetPython) {
|
|
2149
|
+
await bootstrapFleetLocalBot({
|
|
2150
|
+
agentUid: bot.agentUid,
|
|
2151
|
+
ownerUid: config.ownerUid,
|
|
2152
|
+
hqRoot: input.hqRoot,
|
|
2153
|
+
botDir: dir,
|
|
2154
|
+
installation: fleetInstallation,
|
|
2155
|
+
machineCredentials: botCredsPath(dir),
|
|
2156
|
+
systemPrompt: fleetBotSystemPrompt(name, config),
|
|
2157
|
+
provider: fleetProviderForRuntime(config.runtime, config.model),
|
|
2158
|
+
ownerCompanies: await fleetOwnerCompanies(),
|
|
2159
|
+
...(input.preserveFirstStart
|
|
2160
|
+
? {
|
|
2161
|
+
firstStart: {
|
|
2162
|
+
intro: introDmText(name, config.runtime, config.intro, { kind: effectiveBotKind(config), companies: effectiveBotCompanies(config) }),
|
|
2163
|
+
...(config.kickoff ? { kickoff: config.kickoff } : {}),
|
|
2164
|
+
},
|
|
2165
|
+
}
|
|
2166
|
+
: {}),
|
|
2167
|
+
}, { python: fleetPython, start: false });
|
|
2168
|
+
}
|
|
2011
2169
|
createBotLogger(dir)("info", `adopted ${name} (${bot.agentUid}) on ${localMachineId()} kind=${config.kind}${local ? "" : " (no saved settings; defaults)"} runtime=${config.runtime}`);
|
|
2012
2170
|
let daemonMessage = null;
|
|
2013
|
-
if (input.daemon && process.platform === "darwin") {
|
|
2171
|
+
if (!fleetInstallation && input.daemon && process.platform === "darwin") {
|
|
2014
2172
|
daemonMessage = installBotAgent(buildBotDaemonPaths({ name, botDir: dir, hqRoot: input.hqRoot })).message;
|
|
2015
2173
|
}
|
|
2016
2174
|
return {
|
|
@@ -2118,6 +2276,11 @@ async function repairLocalBot(name, api, bot, daemon) {
|
|
|
2118
2276
|
region: DEFAULT_COGNITO.region,
|
|
2119
2277
|
});
|
|
2120
2278
|
createBotLogger(dir)("info", `re-issued machine credentials for ${name} (${bot.agentUid}) on ${localMachineId()}`);
|
|
2279
|
+
if (isFleetRuntimeBot(config)) {
|
|
2280
|
+
importFleetLocalMachineCredential(bot.agentUid, botCredsPath(dir));
|
|
2281
|
+
const restarted = controlFleetLocalBot(bot.agentUid, "restart").ok === true;
|
|
2282
|
+
return restarted ? "new credentials, HQ Fleet Runtime restarted" : "new credentials; the HQ Fleet Runtime did not restart (hq bot start " + name + ")";
|
|
2283
|
+
}
|
|
2121
2284
|
if (daemon && process.platform === "darwin") {
|
|
2122
2285
|
installBotAgent(buildBotDaemonPaths({ name, botDir: dir, hqRoot: config.hqRoot }));
|
|
2123
2286
|
}
|
|
@@ -2308,7 +2471,9 @@ export function registerBotCommand(program) {
|
|
|
2308
2471
|
.option("--kind <kind>", `personal (acts as you, everywhere you are) or company (acts as itself, as a member of its companies). Default: personal, or company when created from a company worker`)
|
|
2309
2472
|
.option("--company <slug>", "Company bots: a company the bot is a member of (repeat for several); the owner invites it on creation", collectRepeated, [])
|
|
2310
2473
|
.option("--runtime <runtime>", `Model CLI to run the bot with: ${BOT_RUNTIMES.join("|")}`)
|
|
2474
|
+
.option("--provider <provider>", `HQ Fleet provider: ${BOT_RUNTIMES.join("|")} (interactive Fleet creation prompts when omitted)`)
|
|
2311
2475
|
.option("--hq-fleet-runtime", "Create this personal bot with the local HQ Fleet Runtime")
|
|
2476
|
+
.option("--legacy-runtime", "Run this bot with the model CLI even where the HQ Fleet Runtime is the default")
|
|
2312
2477
|
.option("--model <model>", "Model override passed to the runtime CLI")
|
|
2313
2478
|
.option("--effort <level>", `Thinking level: claude ${BOT_EFFORT_LEVELS.claude.join("|")}; codex ${BOT_EFFORT_LEVELS.codex.join("|")}; grok ${BOT_EFFORT_LEVELS.grok.join("|")} (default ${DEFAULT_BOT_EFFORT})`)
|
|
2314
2479
|
.option("--no-auto-approve", "Keep the runtime's permission gates (default: everything pre-approved; a headless bot cannot answer prompts)")
|
package/dist/commands/cloud.d.ts
CHANGED
|
@@ -41,6 +41,50 @@ export declare class SyncExitError extends Error {
|
|
|
41
41
|
health?: "failed" | "succeeded";
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
|
+
/** Shown when the session expires during a sync and cannot be renewed. */
|
|
45
|
+
export declare const SYNC_SESSION_EXPIRED_MESSAGE = "Your HQ session expired during sync and could not be refreshed. Run `hq login`, then rerun the sync.";
|
|
46
|
+
/** Shown when the refresh failed for a network or server reason. */
|
|
47
|
+
export declare const SYNC_REFRESH_UNAVAILABLE_MESSAGE = "Could not refresh your HQ session during sync because the sign-in service did not respond. Check your connection, then rerun the sync.";
|
|
48
|
+
/** Shown when the start token carries no principal to bind the run to. */
|
|
49
|
+
export declare const SYNC_NO_IDENTITY_MESSAGE = "The HQ session token does not identify an account, so the sync did not start. Run `hq login`, then rerun the sync.";
|
|
50
|
+
/** Shown when the token would change to a different account mid-run. */
|
|
51
|
+
export declare const SYNC_IDENTITY_CHANGED_MESSAGE = "The signed-in HQ account changed while this sync was running, so the sync stopped instead of continuing as a different account. Rerun the sync as the account you want.";
|
|
52
|
+
/**
|
|
53
|
+
* The session token expired during a sync run and the non-interactive
|
|
54
|
+
* refresh failed. `kind` separates a rejected session (sign in again) from a
|
|
55
|
+
* network or server failure (retry later).
|
|
56
|
+
*/
|
|
57
|
+
export declare class SyncSessionExpiredError extends Error {
|
|
58
|
+
readonly kind: "reauth" | "unavailable";
|
|
59
|
+
readonly reason?: string | undefined;
|
|
60
|
+
constructor(kind: "reauth" | "unavailable", reason?: string | undefined);
|
|
61
|
+
}
|
|
62
|
+
export interface SyncVaultConfigDeps {
|
|
63
|
+
/** Non-interactive token resolver: returns the cached token or refreshes it. */
|
|
64
|
+
ensureToken?: () => Promise<string>;
|
|
65
|
+
}
|
|
66
|
+
/** The last token failure recorded by a {@link buildSyncVaultConfig} config. */
|
|
67
|
+
export declare function syncAuthFailureFor(config: VaultServiceConfig | undefined): Error | null;
|
|
68
|
+
/**
|
|
69
|
+
* Vault config for a sync run whose token stays valid for the whole run.
|
|
70
|
+
*
|
|
71
|
+
* `initialToken` is resolved (and, on a TTY, logged in) before the run starts.
|
|
72
|
+
* The config carries a getter instead of that string, so every vault request
|
|
73
|
+
* re-reads the cached session and refreshes it when it is close to expiry.
|
|
74
|
+
* When a request still gets a 401, hq-cloud's VaultClient calls the getter
|
|
75
|
+
* once more and retries once if the token changed (no loop). A multi-company
|
|
76
|
+
* `hq sync pull --all` can outlast the one-hour Cognito access token; a
|
|
77
|
+
* captured string made every later target fail with 401 (feedback_3d3fab0b).
|
|
78
|
+
*
|
|
79
|
+
* The getter is bound to the principal of `initialToken`. If a later token
|
|
80
|
+
* belongs to a different principal (a mid-run `hq login` as another account,
|
|
81
|
+
* or the machine-identity fallback after a rejected person refresh), it throws
|
|
82
|
+
* an {@link IdentitySwapRefusal} and the run stops. It never continues as a
|
|
83
|
+
* different principal.
|
|
84
|
+
*
|
|
85
|
+
* Mid-run refreshes never open a browser.
|
|
86
|
+
*/
|
|
87
|
+
export declare function buildSyncVaultConfig(initialToken: string, deps?: SyncVaultConfigDeps): VaultServiceConfig;
|
|
44
88
|
/**
|
|
45
89
|
* Preserve the vault's typed write-scope refusal at the CLI boundary. This is
|
|
46
90
|
* deliberately duck-typed so hq-cli remains compatible while hq-cloud and the
|
|
@@ -50,6 +94,7 @@ export declare function formatSyncFailure(err: unknown, ctx?: {
|
|
|
50
94
|
hqRoot?: string;
|
|
51
95
|
cwd?: string;
|
|
52
96
|
companySlug?: string;
|
|
97
|
+
vaultConfig?: VaultServiceConfig;
|
|
53
98
|
}): string;
|
|
54
99
|
/**
|
|
55
100
|
* The `.hqinclude` line that admits a named path: its HQ-relative spelling,
|
|
@@ -259,6 +304,8 @@ export interface SyncCallResult {
|
|
|
259
304
|
aborted: boolean;
|
|
260
305
|
}
|
|
261
306
|
export interface PullAllDeps {
|
|
307
|
+
/** Formats a per-target failure; the real path binds the run's vault config. */
|
|
308
|
+
formatFailure?: (err: unknown) => string;
|
|
262
309
|
vaultClient: PullAllVaultClient;
|
|
263
310
|
sync: (options: SyncCallOptions) => Promise<SyncCallResult>;
|
|
264
311
|
/**
|
|
@@ -352,6 +399,8 @@ export interface ShareCallResult {
|
|
|
352
399
|
planLimit?: PlanLimitRefusalSummary | null;
|
|
353
400
|
}
|
|
354
401
|
export interface PushAllDeps {
|
|
402
|
+
/** Formats a per-target failure; the real path binds the run's vault config. */
|
|
403
|
+
formatFailure?: (err: unknown) => string;
|
|
355
404
|
vaultClient: PullAllVaultClient;
|
|
356
405
|
share: (options: ShareCallOptions) => Promise<ShareCallResult>;
|
|
357
406
|
beforeShare?: (input: {
|