@indigoai-us/hq-cli 5.345.173 → 5.345.175

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 CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.345.175] — 2026-10-28
6
+ - A local bot whose coding tool crashed before it could start on a message (seen as "Claude Code stopped with an error" on the first message after a day or more idle) now tries that message once more on its own. If it crashes again, the reply carries a Heal bot card so HQ can restart the bot and send the message again, and the bot's log and `hq bot status` record what the tool printed, how long it ran and which binary it was, so the cause can be found.
7
+ - `hq dm --attach` after a person `hq login` looks up the caller's `prs_` uid
8
+ when the session token has no `custom:entityUid` (human tokens never carry
9
+ that claim). Plain `hq dm` already worked; attaching a file no longer fails
10
+ with "Could not read your person uid from the session token".
11
+
12
+ ## [5.345.174] — 2026-10-27
13
+ - `hq channels delete <name> --yes` deletes a channel you own, including its
14
+ messages. Group DMs stay undeletable. Without `--yes` the command refuses.
15
+ - New `hq deploy domains base add | status | remove` sets up a company deploy domain, so every app of the company serves at `<app>.<your-domain>`. `add` shows the monthly price for your company (included on Enterprise) and the two DNS records to create. `status --wait` checks every 15 seconds until the domain is live and gives up after 15 minutes (`--timeout` sets up to 60). `remove` releases the domain.
16
+ - Any signed-in HQ user can now create a bot with `--hq-fleet-runtime`: the CLI downloads the signed HQ Fleet Local release through short-lived links from HQ instead of a private GitHub release, and still checks every file against its pinned hash and the release signature. GitHub access is only used when HQ has no download for the pinned version.
17
+ - A bot created with `--hq-fleet-runtime` now runs HQ's own hooks: its tool calls, prompts and turn ends go through the HQ folder's `.claude` hooks, so it is refused the same core edits, secret dumps and unbound company reads a Claude Code session is, and it binds a company for its conversation before working on that company's files. Messages that arrive while the bot is answering now wait for that answer instead of stalling its inbox. Pins hq-fleet-local v0.4.6.
18
+
5
19
  ## [5.345.173] — 2026-10-27
6
20
  - The sync-core reconcile now counts the conflict and merge actions in a vault's read-only plan as writes: a vault is held read-only while its plan would write more than 100 conflict or merge copies, the count is recorded next to the plan's upload, download and delete counts in `~/.hq/sync-core/rollout.json`, and `hq sync-core status` prints it.
7
21
  - A machine can keep one vault off the new sync engine while its company switch is on: `~/.hq/sync-core/skip.json` with `{"vaults": {"<vault uid>": "<reason>"}}` makes the reconcile leave that vault on the current sync (stopping its engine unit if one ran), record the uid and reason under `skipped` in `rollout.json`, and show it in `hq sync-core status`. A malformed file is reported as a warning and skips nothing.
@@ -4365,6 +4365,31 @@ export declare const COMMAND_CATALOG: readonly [{
4365
4365
  readonly hidden: false;
4366
4366
  }];
4367
4367
  readonly subcommands: readonly [];
4368
+ }, {
4369
+ readonly name: "delete";
4370
+ readonly description: "Delete a channel you own, including its messages. Group DMs cannot be deleted.";
4371
+ readonly aliases: readonly [];
4372
+ readonly hidden: false;
4373
+ readonly helpOption: true;
4374
+ readonly helpCommand: false;
4375
+ readonly usage: "[options] <channel>";
4376
+ readonly helpText: readonly [];
4377
+ readonly arguments: readonly [{
4378
+ readonly name: "channel";
4379
+ readonly required: true;
4380
+ readonly variadic: false;
4381
+ readonly description: "";
4382
+ }];
4383
+ readonly options: readonly [{
4384
+ readonly flags: "--json";
4385
+ readonly description: "Output { ok, deleted } as JSON.";
4386
+ readonly hidden: false;
4387
+ }, {
4388
+ readonly flags: "--yes";
4389
+ readonly description: "Confirm the delete. Required.";
4390
+ readonly hidden: false;
4391
+ }];
4392
+ readonly subcommands: readonly [];
4368
4393
  }];
4369
4394
  }, {
4370
4395
  readonly name: "onboard";
@@ -19837,6 +19862,111 @@ export declare const COMMAND_CATALOG: readonly [{
19837
19862
  readonly hidden: false;
19838
19863
  }];
19839
19864
  readonly subcommands: readonly [];
19865
+ }, {
19866
+ readonly name: "base";
19867
+ readonly description: "Manage the company deploy domain: one domain for every app (<app>.<domain>)";
19868
+ readonly aliases: readonly [];
19869
+ readonly hidden: false;
19870
+ readonly helpOption: true;
19871
+ readonly helpCommand: true;
19872
+ readonly usage: "[options] [command]";
19873
+ readonly helpText: readonly [];
19874
+ readonly arguments: readonly [];
19875
+ readonly options: readonly [];
19876
+ readonly subcommands: readonly [{
19877
+ readonly name: "add";
19878
+ readonly description: "Claim a company deploy domain; prints the price and the DNS records";
19879
+ readonly aliases: readonly [];
19880
+ readonly hidden: false;
19881
+ readonly helpOption: true;
19882
+ readonly helpCommand: false;
19883
+ readonly usage: "[options] <domain>";
19884
+ readonly helpText: readonly [];
19885
+ readonly arguments: readonly [{
19886
+ readonly name: "domain";
19887
+ readonly required: true;
19888
+ readonly variadic: false;
19889
+ readonly description: "";
19890
+ }];
19891
+ readonly options: readonly [{
19892
+ readonly flags: "--yes";
19893
+ readonly description: "add without a prompt (the domain can add a monthly charge)";
19894
+ readonly hidden: false;
19895
+ }, {
19896
+ readonly flags: "--company <slug>";
19897
+ readonly description: "use this company instead of the linked or active company";
19898
+ readonly hidden: false;
19899
+ }, {
19900
+ readonly flags: "--path <dir>";
19901
+ readonly description: "folder linked to an app of the company (default: current folder)";
19902
+ readonly hidden: false;
19903
+ }, {
19904
+ readonly flags: "--json";
19905
+ readonly description: "print machine-readable JSON";
19906
+ readonly hidden: false;
19907
+ }];
19908
+ readonly subcommands: readonly [];
19909
+ }, {
19910
+ readonly name: "status";
19911
+ readonly description: "Check the company deploy domain and advance it toward live (exits 2 while not live)";
19912
+ readonly aliases: readonly [];
19913
+ readonly hidden: false;
19914
+ readonly helpOption: true;
19915
+ readonly helpCommand: false;
19916
+ readonly usage: "[options]";
19917
+ readonly helpText: readonly [];
19918
+ readonly arguments: readonly [];
19919
+ readonly options: readonly [{
19920
+ readonly flags: "--wait";
19921
+ readonly description: "keep checking until it is live or the timeout passes";
19922
+ readonly hidden: false;
19923
+ }, {
19924
+ readonly flags: "--timeout <minutes>";
19925
+ readonly description: "stop waiting after this many minutes (default 15, at most 60)";
19926
+ readonly hidden: false;
19927
+ }, {
19928
+ readonly flags: "--company <slug>";
19929
+ readonly description: "use this company instead of the linked or active company";
19930
+ readonly hidden: false;
19931
+ }, {
19932
+ readonly flags: "--path <dir>";
19933
+ readonly description: "folder linked to an app of the company (default: current folder)";
19934
+ readonly hidden: false;
19935
+ }, {
19936
+ readonly flags: "--json";
19937
+ readonly description: "print machine-readable JSON";
19938
+ readonly hidden: false;
19939
+ }];
19940
+ readonly subcommands: readonly [];
19941
+ }, {
19942
+ readonly name: "remove";
19943
+ readonly description: "Release the company deploy domain";
19944
+ readonly aliases: readonly [];
19945
+ readonly hidden: false;
19946
+ readonly helpOption: true;
19947
+ readonly helpCommand: false;
19948
+ readonly usage: "[options]";
19949
+ readonly helpText: readonly [];
19950
+ readonly arguments: readonly [];
19951
+ readonly options: readonly [{
19952
+ readonly flags: "--yes";
19953
+ readonly description: "remove without a prompt";
19954
+ readonly hidden: false;
19955
+ }, {
19956
+ readonly flags: "--company <slug>";
19957
+ readonly description: "use this company instead of the linked or active company";
19958
+ readonly hidden: false;
19959
+ }, {
19960
+ readonly flags: "--path <dir>";
19961
+ readonly description: "folder linked to an app of the company (default: current folder)";
19962
+ readonly hidden: false;
19963
+ }, {
19964
+ readonly flags: "--json";
19965
+ readonly description: "print machine-readable JSON";
19966
+ readonly hidden: false;
19967
+ }];
19968
+ readonly subcommands: readonly [];
19969
+ }];
19840
19970
  }, {
19841
19971
  readonly name: "search";
19842
19972
  readonly description: "Check whether a domain can be bought, and its price";
@@ -5290,6 +5290,37 @@ export const COMMAND_CATALOG = [
5290
5290
  }
5291
5291
  ],
5292
5292
  "subcommands": []
5293
+ },
5294
+ {
5295
+ "name": "delete",
5296
+ "description": "Delete a channel you own, including its messages. Group DMs cannot be deleted.",
5297
+ "aliases": [],
5298
+ "hidden": false,
5299
+ "helpOption": true,
5300
+ "helpCommand": false,
5301
+ "usage": "[options] <channel>",
5302
+ "helpText": [],
5303
+ "arguments": [
5304
+ {
5305
+ "name": "channel",
5306
+ "required": true,
5307
+ "variadic": false,
5308
+ "description": ""
5309
+ }
5310
+ ],
5311
+ "options": [
5312
+ {
5313
+ "flags": "--json",
5314
+ "description": "Output { ok, deleted } as JSON.",
5315
+ "hidden": false
5316
+ },
5317
+ {
5318
+ "flags": "--yes",
5319
+ "description": "Confirm the delete. Required.",
5320
+ "hidden": false
5321
+ }
5322
+ ],
5323
+ "subcommands": []
5293
5324
  }
5294
5325
  ]
5295
5326
  },
@@ -24186,6 +24217,134 @@ export const COMMAND_CATALOG = [
24186
24217
  ],
24187
24218
  "subcommands": []
24188
24219
  },
24220
+ {
24221
+ "name": "base",
24222
+ "description": "Manage the company deploy domain: one domain for every app (<app>.<domain>)",
24223
+ "aliases": [],
24224
+ "hidden": false,
24225
+ "helpOption": true,
24226
+ "helpCommand": true,
24227
+ "usage": "[options] [command]",
24228
+ "helpText": [],
24229
+ "arguments": [],
24230
+ "options": [],
24231
+ "subcommands": [
24232
+ {
24233
+ "name": "add",
24234
+ "description": "Claim a company deploy domain; prints the price and the DNS records",
24235
+ "aliases": [],
24236
+ "hidden": false,
24237
+ "helpOption": true,
24238
+ "helpCommand": false,
24239
+ "usage": "[options] <domain>",
24240
+ "helpText": [],
24241
+ "arguments": [
24242
+ {
24243
+ "name": "domain",
24244
+ "required": true,
24245
+ "variadic": false,
24246
+ "description": ""
24247
+ }
24248
+ ],
24249
+ "options": [
24250
+ {
24251
+ "flags": "--yes",
24252
+ "description": "add without a prompt (the domain can add a monthly charge)",
24253
+ "hidden": false
24254
+ },
24255
+ {
24256
+ "flags": "--company <slug>",
24257
+ "description": "use this company instead of the linked or active company",
24258
+ "hidden": false
24259
+ },
24260
+ {
24261
+ "flags": "--path <dir>",
24262
+ "description": "folder linked to an app of the company (default: current folder)",
24263
+ "hidden": false
24264
+ },
24265
+ {
24266
+ "flags": "--json",
24267
+ "description": "print machine-readable JSON",
24268
+ "hidden": false
24269
+ }
24270
+ ],
24271
+ "subcommands": []
24272
+ },
24273
+ {
24274
+ "name": "status",
24275
+ "description": "Check the company deploy domain and advance it toward live (exits 2 while not live)",
24276
+ "aliases": [],
24277
+ "hidden": false,
24278
+ "helpOption": true,
24279
+ "helpCommand": false,
24280
+ "usage": "[options]",
24281
+ "helpText": [],
24282
+ "arguments": [],
24283
+ "options": [
24284
+ {
24285
+ "flags": "--wait",
24286
+ "description": "keep checking until it is live or the timeout passes",
24287
+ "hidden": false
24288
+ },
24289
+ {
24290
+ "flags": "--timeout <minutes>",
24291
+ "description": "stop waiting after this many minutes (default 15, at most 60)",
24292
+ "hidden": false
24293
+ },
24294
+ {
24295
+ "flags": "--company <slug>",
24296
+ "description": "use this company instead of the linked or active company",
24297
+ "hidden": false
24298
+ },
24299
+ {
24300
+ "flags": "--path <dir>",
24301
+ "description": "folder linked to an app of the company (default: current folder)",
24302
+ "hidden": false
24303
+ },
24304
+ {
24305
+ "flags": "--json",
24306
+ "description": "print machine-readable JSON",
24307
+ "hidden": false
24308
+ }
24309
+ ],
24310
+ "subcommands": []
24311
+ },
24312
+ {
24313
+ "name": "remove",
24314
+ "description": "Release the company deploy domain",
24315
+ "aliases": [],
24316
+ "hidden": false,
24317
+ "helpOption": true,
24318
+ "helpCommand": false,
24319
+ "usage": "[options]",
24320
+ "helpText": [],
24321
+ "arguments": [],
24322
+ "options": [
24323
+ {
24324
+ "flags": "--yes",
24325
+ "description": "remove without a prompt",
24326
+ "hidden": false
24327
+ },
24328
+ {
24329
+ "flags": "--company <slug>",
24330
+ "description": "use this company instead of the linked or active company",
24331
+ "hidden": false
24332
+ },
24333
+ {
24334
+ "flags": "--path <dir>",
24335
+ "description": "folder linked to an app of the company (default: current folder)",
24336
+ "hidden": false
24337
+ },
24338
+ {
24339
+ "flags": "--json",
24340
+ "description": "print machine-readable JSON",
24341
+ "hidden": false
24342
+ }
24343
+ ],
24344
+ "subcommands": []
24345
+ }
24346
+ ]
24347
+ },
24189
24348
  {
24190
24349
  "name": "search",
24191
24350
  "description": "Check whether a domain can be bought, and its price",
@@ -1257,6 +1257,9 @@ async function runBotStatus(nameArg, opts) {
1257
1257
  if (status.modelHold && new Date(status.modelHold.until).getTime() > Date.now()) {
1258
1258
  console.log(` ${chalk.yellow(`holding until ${status.modelHold.until}`)} after repeated model failures; the next message after that runs again`);
1259
1259
  }
1260
+ if (status.lastCrash) {
1261
+ console.log(` last crash: ${chalk.yellow(`${status.lastCrash.at}, exit ${status.lastCrash.code ?? "?"}${status.lastCrash.durationMs !== undefined ? ` after ${status.lastCrash.durationMs} ms` : ""}`)}`);
1262
+ }
1260
1263
  if (status.runtimeModelFallback) {
1261
1264
  console.log(` model: ${chalk.yellow(`running on ${status.runtimeModelFallback.to}`)} because ${row.runtime} here is too old for ${status.runtimeModelFallback.from || "its configured model"}; update ${row.runtime} to use it`);
1262
1265
  }
@@ -212,6 +212,88 @@ async function runChannelsMembers(channelName, opts) {
212
212
  process.exit(1);
213
213
  }
214
214
  }
215
+ function channelDeleteError(status, err) {
216
+ const code = err.code ?? "";
217
+ if (code === "CHANNEL_NOT_OWNER" || status === 403) {
218
+ return "Only the channel owner may delete the channel.";
219
+ }
220
+ if (code === "CHANNEL_GROUP_NOT_DELETABLE" || status === 409) {
221
+ return "Group DMs cannot be deleted.";
222
+ }
223
+ if (code === "CHANNEL_DELETE_INCOMPLETE" || status === 503) {
224
+ return "Channel deletion did not finish — try again.";
225
+ }
226
+ return err.error ?? err.message ?? `Could not delete channel (HTTP ${status}).`;
227
+ }
228
+ /**
229
+ * `hq channels delete` — owner-only. The server already accepts
230
+ * DELETE /v1/notify/channels/{id} and removes messages with the channel.
231
+ * `--yes` is required so a mistyped name cannot wipe a room.
232
+ */
233
+ async function runChannelsDelete(channelName, opts) {
234
+ try {
235
+ if (!opts.yes) {
236
+ const message = "This permanently deletes the channel and its messages. Re-run with --yes to confirm.";
237
+ if (opts.json) {
238
+ console.log(JSON.stringify({ ok: false, error: message }, null, 2));
239
+ }
240
+ console.error(chalk.red(message));
241
+ process.exit(1);
242
+ }
243
+ const token = await ensureCognitoToken();
244
+ const channels = await fetchChannels(token);
245
+ const matches = matchChannelsByName(channels, channelName);
246
+ if (matches.length === 0) {
247
+ const message = `No channel named '${channelName}' — run \`hq channels\` to see your channels.`;
248
+ const suggestions = suggestChannels(channels, channelName);
249
+ if (opts.json) {
250
+ console.log(JSON.stringify({ ok: false, error: message, suggestions }, null, 2));
251
+ }
252
+ console.error(chalk.red(withChannelSuggestions(message, suggestions)));
253
+ process.exit(1);
254
+ }
255
+ if (matches.length > 1) {
256
+ const scopes = matches.map((match) => match.scope ?? "?").join(", ");
257
+ console.error(chalk.red(`'${channelName}' matches ${matches.length} channels (${scopes}) — this is ambiguous.`));
258
+ process.exit(1);
259
+ }
260
+ const target = matches[0];
261
+ if (target.scope === "group") {
262
+ const message = "Group DMs cannot be deleted.";
263
+ if (opts.json) {
264
+ console.log(JSON.stringify({ ok: false, error: message, code: "CHANNEL_GROUP_NOT_DELETABLE" }, null, 2));
265
+ }
266
+ console.error(chalk.red(message));
267
+ process.exit(1);
268
+ }
269
+ const res = await vaultApiFetch({
270
+ token,
271
+ path: `/v1/notify/channels/${encodeURIComponent(target.channelId)}`,
272
+ method: "DELETE",
273
+ });
274
+ if (!res.ok) {
275
+ const err = (await res.json().catch(() => ({})));
276
+ const message = channelDeleteError(res.status, err);
277
+ if (opts.json) {
278
+ console.log(JSON.stringify({ ok: false, error: message, code: err.code ?? null, status: res.status }, null, 2));
279
+ }
280
+ console.error(chalk.red(message));
281
+ process.exit(1);
282
+ }
283
+ const body = (await res.json().catch(() => ({})));
284
+ const deleted = body.deleted ?? target.channelId;
285
+ if (opts.json) {
286
+ console.log(JSON.stringify({ ok: true, deleted }, null, 2));
287
+ }
288
+ else {
289
+ console.log(`Deleted ${target.name ?? target.slug ?? deleted}.`);
290
+ }
291
+ }
292
+ catch (err) {
293
+ console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
294
+ process.exit(1);
295
+ }
296
+ }
215
297
  export function registerChannelsCommand(program) {
216
298
  const channels = program
217
299
  .command("channels")
@@ -239,5 +321,13 @@ export function registerChannelsCommand(program) {
239
321
  .action(async (channelName, opts) => {
240
322
  await runChannelsMembers(channelName, opts);
241
323
  });
324
+ channels
325
+ .command("delete <channel>")
326
+ .description("Delete a channel you own, including its messages. Group DMs cannot be deleted.")
327
+ .option("--json", "Output { ok, deleted } as JSON.")
328
+ .option("--yes", "Confirm the delete. Required.")
329
+ .action(async (channelName, opts) => {
330
+ await runChannelsDelete(channelName, opts);
331
+ });
242
332
  }
243
333
  //# sourceMappingURL=channels.js.map
@@ -47,6 +47,26 @@ export declare function addOnConfirmQuestion(domain: string, environment: string
47
47
  export declare const PURCHASE_TERMS_UNAVAILABLE = "The renewal and refund terms could not be loaded. Check them on the HQ pricing page before you buy.";
48
48
  /** The purchase terms published as usage.domains.purchase. Null when not published. */
49
49
  export declare function purchaseTermsLine(pricing: Record<string, unknown> | null): string | null;
50
+ /** Hard deadline for `base status --wait`, so a script never waits forever. */
51
+ export declare const BASE_WAIT_DEFAULT_MINUTES = 15;
52
+ export declare const BASE_WAIT_MAX_MINUTES = 60;
53
+ /** Clock hooks for `base status --wait`. Tests replace these. */
54
+ export declare const deployDomainsClock: {
55
+ now: () => number;
56
+ sleep: (ms: number) => Promise<void>;
57
+ };
58
+ /**
59
+ * The price line for a company deploy domain, from the server's answer:
60
+ * Enterprise includes it; a company with a price override (or comped) pays
61
+ * what the server says, $0 included; otherwise the server's price, falling
62
+ * back to the published premium price, then $500.
63
+ */
64
+ export declare function baseDomainPriceLine(body: {
65
+ billingBasis?: unknown;
66
+ monthlyPriceUsd?: unknown;
67
+ }, pricing: Record<string, unknown> | null): string | undefined;
68
+ /** The approval question for `base add`, before the server says the basis. */
69
+ export declare function baseAddConfirmQuestion(domain: string, orgSlug: string, pricing: Record<string, unknown> | null): string;
50
70
  export declare function parsePrice(raw: string): number;
51
71
  export declare function registerDeployDomainsCommand(deploy: Command): void;
52
72
  export {};