@indigoai-us/hq-cli 5.345.76 → 5.345.77
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 +83 -3
- package/dist/command-catalog.generated.js +100 -3
- package/dist/commands/access.d.ts +1 -0
- package/dist/commands/access.js +17 -5
- package/dist/commands/bot.d.ts +12 -0
- package/dist/commands/bot.js +54 -2
- package/dist/commands/files.js +7 -2
- package/dist/commands/members.d.ts +42 -0
- package/dist/commands/members.js +151 -1
- package/dist/commands/secrets.js +1 -1
- package/dist/lib/bot/api.d.ts +11 -0
- package/dist/lib/bot/api.js +2 -0
- package/dist/lib/bot/inflight.d.ts +3 -0
- package/dist/lib/bot/probe.d.ts +57 -0
- package/dist/lib/bot/probe.js +100 -0
- package/dist/lib/bot/run.d.ts +4 -0
- package/dist/lib/bot/run.js +171 -37
- package/dist/lib/bot/runtime/codex.d.ts +45 -1
- package/dist/lib/bot/runtime/codex.js +134 -2
- package/dist/lib/bot/runtime/grok.js +2 -2
- package/dist/lib/bot/runtime/index.d.ts +8 -0
- package/dist/lib/bot/runtime/index.js +91 -5
- package/dist/lib/bot/runtime/messages-stream.js +1 -1
- package/dist/lib/bot/runtime/types.d.ts +33 -0
- package/dist/lib/bot/runtime/types.js +10 -0
- package/dist/lib/bot/runtime-failure.d.ts +113 -0
- package/dist/lib/bot/runtime-failure.js +180 -0
- package/dist/lib/bot/runtime-sign-in.js +2 -2
- package/dist/lib/bot/status.d.ts +21 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.345.77] — 2026-10-10
|
|
6
|
+
- Local bots no longer fail a turn when the installed Codex is too old for the
|
|
7
|
+
model it was asked to use (often the model a newer Codex app set in your own
|
|
8
|
+
Codex settings). The bot runs the same turn once more on a model your Codex
|
|
9
|
+
supports and notes it in `hq bot status`.
|
|
10
|
+
- A bot whose coding tool fails now tells you in plain words what is wrong and
|
|
11
|
+
the one thing to do (sign in, update, choose another model, or try again),
|
|
12
|
+
never the tool's raw error, and never blames the sign-in unless it is the
|
|
13
|
+
sign-in. The DM also carries a repair card for the HQ desktop app.
|
|
14
|
+
- After five model failures in ten minutes a local bot now says it is pausing,
|
|
15
|
+
holds its turns for ten minutes, and then answers the next message, instead
|
|
16
|
+
of exiting and staying down until `hq bot start`. A good turn clears the
|
|
17
|
+
recorded failure from `hq bot status`, which now shows a hold and a plain
|
|
18
|
+
problem line instead of the raw error.
|
|
19
|
+
- Bots created before this release: one that is still running restarts itself
|
|
20
|
+
onto the new hq-cli within a few minutes of the install and heals on its
|
|
21
|
+
first message. One that already exited after five failures on the old
|
|
22
|
+
release is brought back only by a login or reboot, the desktop's Start, or
|
|
23
|
+
`hq bot start`; after that it heals the same way.
|
|
24
|
+
- New `hq bot set-model <name> <model|default>` sets a bot's own model without
|
|
25
|
+
touching the coding tool's settings.
|
|
26
|
+
- New `hq bot probe (--runtime <runtime> | --bot <name>) [--model <model>]
|
|
27
|
+
--json` runs one tiny test turn exactly as a bot would, in an empty scratch
|
|
28
|
+
folder, and reports `{ ok, class, detail }`.
|
|
29
|
+
- Added `hq members remove <email>` to end an active membership and drop that
|
|
30
|
+
person's file and secret grants. `hq members revoke` still only cancels
|
|
31
|
+
pending invites. Guests stay active members, so company-wide grants (`@all` /
|
|
32
|
+
Everyone in company) still apply to them; demoting to guest does not lock
|
|
33
|
+
someone out. `hq access <path> --as <email>` now accepts an email and shows
|
|
34
|
+
that person's effective access.
|
|
35
|
+
|
|
5
36
|
## [5.345.76] — 2026-10-10
|
|
6
37
|
|
|
7
38
|
- `hq bot` on macOS now finds the Codex CLI bundled in the ChatGPT or Codex
|
|
@@ -2880,8 +2880,8 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
2880
2880
|
readonly description: "Company slug";
|
|
2881
2881
|
readonly hidden: false;
|
|
2882
2882
|
}, {
|
|
2883
|
-
readonly flags: "--as <person
|
|
2884
|
-
readonly description: "Check a person's access (
|
|
2883
|
+
readonly flags: "--as <person>";
|
|
2884
|
+
readonly description: "Check a person's access (email, prs_, or agt_ uid; self or owner/admin)";
|
|
2885
2885
|
readonly hidden: false;
|
|
2886
2886
|
}, {
|
|
2887
2887
|
readonly flags: "--json";
|
|
@@ -3164,9 +3164,26 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
3164
3164
|
readonly hidden: false;
|
|
3165
3165
|
}];
|
|
3166
3166
|
readonly subcommands: readonly [];
|
|
3167
|
+
}, {
|
|
3168
|
+
readonly name: "remove";
|
|
3169
|
+
readonly description: "Remove an active member. Ends membership so company-wide grants no longer apply, and drops that person's file and secret grants. Accepts an email, prs_ personUid, agt_ agentUid, or full membership key.";
|
|
3170
|
+
readonly aliases: readonly [];
|
|
3171
|
+
readonly hidden: false;
|
|
3172
|
+
readonly helpOption: true;
|
|
3173
|
+
readonly helpCommand: false;
|
|
3174
|
+
readonly usage: "[options] <target>";
|
|
3175
|
+
readonly helpText: readonly [];
|
|
3176
|
+
readonly arguments: readonly [{
|
|
3177
|
+
readonly name: "target";
|
|
3178
|
+
readonly required: true;
|
|
3179
|
+
readonly variadic: false;
|
|
3180
|
+
readonly description: "";
|
|
3181
|
+
}];
|
|
3182
|
+
readonly options: readonly [];
|
|
3183
|
+
readonly subcommands: readonly [];
|
|
3167
3184
|
}, {
|
|
3168
3185
|
readonly name: "revoke";
|
|
3169
|
-
readonly description: "Revoke a pending invite. Accepts an email, personUid, full membershipKey, or legacy inviteToken.";
|
|
3186
|
+
readonly description: "Revoke a pending invite. For an active member, use `hq members remove` instead. Accepts an email, personUid, full membershipKey, or legacy inviteToken.";
|
|
3170
3187
|
readonly aliases: readonly [];
|
|
3171
3188
|
readonly hidden: false;
|
|
3172
3189
|
readonly helpOption: true;
|
|
@@ -15644,6 +15661,69 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
15644
15661
|
readonly hidden: false;
|
|
15645
15662
|
}];
|
|
15646
15663
|
readonly subcommands: readonly [];
|
|
15664
|
+
}, {
|
|
15665
|
+
readonly name: "set-model";
|
|
15666
|
+
readonly description: "Set the model this bot asks its runtime for (stored in the bot's own settings; the runtime's own config is never changed), or default to clear it";
|
|
15667
|
+
readonly aliases: readonly [];
|
|
15668
|
+
readonly hidden: false;
|
|
15669
|
+
readonly helpOption: true;
|
|
15670
|
+
readonly helpCommand: false;
|
|
15671
|
+
readonly usage: "[options] <name> <model>";
|
|
15672
|
+
readonly helpText: readonly [];
|
|
15673
|
+
readonly arguments: readonly [{
|
|
15674
|
+
readonly name: "name";
|
|
15675
|
+
readonly required: true;
|
|
15676
|
+
readonly variadic: false;
|
|
15677
|
+
readonly description: "";
|
|
15678
|
+
}, {
|
|
15679
|
+
readonly name: "model";
|
|
15680
|
+
readonly required: true;
|
|
15681
|
+
readonly variadic: false;
|
|
15682
|
+
readonly description: "";
|
|
15683
|
+
}];
|
|
15684
|
+
readonly options: readonly [{
|
|
15685
|
+
readonly flags: "--json";
|
|
15686
|
+
readonly description: "Print machine-readable JSON";
|
|
15687
|
+
readonly hidden: false;
|
|
15688
|
+
}];
|
|
15689
|
+
readonly subcommands: readonly [];
|
|
15690
|
+
}, {
|
|
15691
|
+
readonly name: "probe";
|
|
15692
|
+
readonly description: "Run one minimal test turn with a runtime, model and thinking level exactly as a bot would, and say whether a bot set up that way can answer. Costs one tiny model turn on your own login; runs in an empty scratch folder, so no HQ settings or hooks load";
|
|
15693
|
+
readonly aliases: readonly [];
|
|
15694
|
+
readonly hidden: false;
|
|
15695
|
+
readonly helpOption: true;
|
|
15696
|
+
readonly helpCommand: false;
|
|
15697
|
+
readonly usage: "[options]";
|
|
15698
|
+
readonly helpText: readonly [];
|
|
15699
|
+
readonly arguments: readonly [];
|
|
15700
|
+
readonly options: readonly [{
|
|
15701
|
+
readonly flags: "--runtime <runtime>";
|
|
15702
|
+
readonly description: "Model CLI to check: claude|codex|grok (required unless --bot is given)";
|
|
15703
|
+
readonly hidden: false;
|
|
15704
|
+
}, {
|
|
15705
|
+
readonly flags: "--bot <name>";
|
|
15706
|
+
readonly description: "Check with this bot's saved runtime, model and thinking level (--runtime/--model/--effort override them)";
|
|
15707
|
+
readonly hidden: false;
|
|
15708
|
+
}, {
|
|
15709
|
+
readonly flags: "--model <model>";
|
|
15710
|
+
readonly description: "Model id to check, or default for the runtime's own default";
|
|
15711
|
+
readonly hidden: false;
|
|
15712
|
+
}, {
|
|
15713
|
+
readonly flags: "--effort <level>";
|
|
15714
|
+
readonly description: "Thinking level to check";
|
|
15715
|
+
readonly hidden: false;
|
|
15716
|
+
}, {
|
|
15717
|
+
readonly flags: "--timeout <seconds>";
|
|
15718
|
+
readonly description: "Give up after this many seconds";
|
|
15719
|
+
readonly hidden: false;
|
|
15720
|
+
readonly defaultValue: "120";
|
|
15721
|
+
}, {
|
|
15722
|
+
readonly flags: "--json";
|
|
15723
|
+
readonly description: "Print { ok, class, detail, ... } as JSON";
|
|
15724
|
+
readonly hidden: false;
|
|
15725
|
+
}];
|
|
15726
|
+
readonly subcommands: readonly [];
|
|
15647
15727
|
}, {
|
|
15648
15728
|
readonly name: "intro";
|
|
15649
15729
|
readonly description: "Have the bot re-send its introduction DM to you";
|
|
@@ -3473,8 +3473,8 @@ export const COMMAND_CATALOG = [
|
|
|
3473
3473
|
"hidden": false
|
|
3474
3474
|
},
|
|
3475
3475
|
{
|
|
3476
|
-
"flags": "--as <person
|
|
3477
|
-
"description": "Check a person's access (
|
|
3476
|
+
"flags": "--as <person>",
|
|
3477
|
+
"description": "Check a person's access (email, prs_, or agt_ uid; self or owner/admin)",
|
|
3478
3478
|
"hidden": false
|
|
3479
3479
|
},
|
|
3480
3480
|
{
|
|
@@ -3825,9 +3825,29 @@ export const COMMAND_CATALOG = [
|
|
|
3825
3825
|
],
|
|
3826
3826
|
"subcommands": []
|
|
3827
3827
|
},
|
|
3828
|
+
{
|
|
3829
|
+
"name": "remove",
|
|
3830
|
+
"description": "Remove an active member. Ends membership so company-wide grants no longer apply, and drops that person's file and secret grants. Accepts an email, prs_ personUid, agt_ agentUid, or full membership key.",
|
|
3831
|
+
"aliases": [],
|
|
3832
|
+
"hidden": false,
|
|
3833
|
+
"helpOption": true,
|
|
3834
|
+
"helpCommand": false,
|
|
3835
|
+
"usage": "[options] <target>",
|
|
3836
|
+
"helpText": [],
|
|
3837
|
+
"arguments": [
|
|
3838
|
+
{
|
|
3839
|
+
"name": "target",
|
|
3840
|
+
"required": true,
|
|
3841
|
+
"variadic": false,
|
|
3842
|
+
"description": ""
|
|
3843
|
+
}
|
|
3844
|
+
],
|
|
3845
|
+
"options": [],
|
|
3846
|
+
"subcommands": []
|
|
3847
|
+
},
|
|
3828
3848
|
{
|
|
3829
3849
|
"name": "revoke",
|
|
3830
|
-
"description": "Revoke a pending invite. Accepts an email, personUid, full membershipKey, or legacy inviteToken.",
|
|
3850
|
+
"description": "Revoke a pending invite. For an active member, use `hq members remove` instead. Accepts an email, personUid, full membershipKey, or legacy inviteToken.",
|
|
3831
3851
|
"aliases": [],
|
|
3832
3852
|
"hidden": false,
|
|
3833
3853
|
"helpOption": true,
|
|
@@ -19052,6 +19072,83 @@ export const COMMAND_CATALOG = [
|
|
|
19052
19072
|
],
|
|
19053
19073
|
"subcommands": []
|
|
19054
19074
|
},
|
|
19075
|
+
{
|
|
19076
|
+
"name": "set-model",
|
|
19077
|
+
"description": "Set the model this bot asks its runtime for (stored in the bot's own settings; the runtime's own config is never changed), or default to clear it",
|
|
19078
|
+
"aliases": [],
|
|
19079
|
+
"hidden": false,
|
|
19080
|
+
"helpOption": true,
|
|
19081
|
+
"helpCommand": false,
|
|
19082
|
+
"usage": "[options] <name> <model>",
|
|
19083
|
+
"helpText": [],
|
|
19084
|
+
"arguments": [
|
|
19085
|
+
{
|
|
19086
|
+
"name": "name",
|
|
19087
|
+
"required": true,
|
|
19088
|
+
"variadic": false,
|
|
19089
|
+
"description": ""
|
|
19090
|
+
},
|
|
19091
|
+
{
|
|
19092
|
+
"name": "model",
|
|
19093
|
+
"required": true,
|
|
19094
|
+
"variadic": false,
|
|
19095
|
+
"description": ""
|
|
19096
|
+
}
|
|
19097
|
+
],
|
|
19098
|
+
"options": [
|
|
19099
|
+
{
|
|
19100
|
+
"flags": "--json",
|
|
19101
|
+
"description": "Print machine-readable JSON",
|
|
19102
|
+
"hidden": false
|
|
19103
|
+
}
|
|
19104
|
+
],
|
|
19105
|
+
"subcommands": []
|
|
19106
|
+
},
|
|
19107
|
+
{
|
|
19108
|
+
"name": "probe",
|
|
19109
|
+
"description": "Run one minimal test turn with a runtime, model and thinking level exactly as a bot would, and say whether a bot set up that way can answer. Costs one tiny model turn on your own login; runs in an empty scratch folder, so no HQ settings or hooks load",
|
|
19110
|
+
"aliases": [],
|
|
19111
|
+
"hidden": false,
|
|
19112
|
+
"helpOption": true,
|
|
19113
|
+
"helpCommand": false,
|
|
19114
|
+
"usage": "[options]",
|
|
19115
|
+
"helpText": [],
|
|
19116
|
+
"arguments": [],
|
|
19117
|
+
"options": [
|
|
19118
|
+
{
|
|
19119
|
+
"flags": "--runtime <runtime>",
|
|
19120
|
+
"description": "Model CLI to check: claude|codex|grok (required unless --bot is given)",
|
|
19121
|
+
"hidden": false
|
|
19122
|
+
},
|
|
19123
|
+
{
|
|
19124
|
+
"flags": "--bot <name>",
|
|
19125
|
+
"description": "Check with this bot's saved runtime, model and thinking level (--runtime/--model/--effort override them)",
|
|
19126
|
+
"hidden": false
|
|
19127
|
+
},
|
|
19128
|
+
{
|
|
19129
|
+
"flags": "--model <model>",
|
|
19130
|
+
"description": "Model id to check, or default for the runtime's own default",
|
|
19131
|
+
"hidden": false
|
|
19132
|
+
},
|
|
19133
|
+
{
|
|
19134
|
+
"flags": "--effort <level>",
|
|
19135
|
+
"description": "Thinking level to check",
|
|
19136
|
+
"hidden": false
|
|
19137
|
+
},
|
|
19138
|
+
{
|
|
19139
|
+
"flags": "--timeout <seconds>",
|
|
19140
|
+
"description": "Give up after this many seconds",
|
|
19141
|
+
"hidden": false,
|
|
19142
|
+
"defaultValue": "120"
|
|
19143
|
+
},
|
|
19144
|
+
{
|
|
19145
|
+
"flags": "--json",
|
|
19146
|
+
"description": "Print { ok, class, detail, ... } as JSON",
|
|
19147
|
+
"hidden": false
|
|
19148
|
+
}
|
|
19149
|
+
],
|
|
19150
|
+
"subcommands": []
|
|
19151
|
+
},
|
|
19055
19152
|
{
|
|
19056
19153
|
"name": "intro",
|
|
19057
19154
|
"description": "Have the bot re-send its introduction DM to you",
|
|
@@ -158,6 +158,7 @@ export declare function resolveAccessCompany(target: string, opts?: {
|
|
|
158
158
|
company?: string;
|
|
159
159
|
hqRoot?: string;
|
|
160
160
|
}): string | undefined;
|
|
161
|
+
export declare function resolveAccessAsPersonUid(token: string, companyUid: string, person: string): Promise<string>;
|
|
161
162
|
export declare function runAccessForPerson(rawPath: string, personUid: string, opts?: {
|
|
162
163
|
company?: string;
|
|
163
164
|
hqRoot?: string;
|
package/dist/commands/access.js
CHANGED
|
@@ -641,7 +641,7 @@ export function registerAccessCommand(program) {
|
|
|
641
641
|
.command("access <path-or-query>")
|
|
642
642
|
.description("Tell whether a vault file exists and whether you can read it. Never a bare not-found. Exit codes: 0 local/not-synced, 2 never-existed, 3 no-access, 4 ambiguous.")
|
|
643
643
|
.option("--company <slug>", "Company slug")
|
|
644
|
-
.option("--as <person
|
|
644
|
+
.option("--as <person>", "Check a person's access (email, prs_, or agt_ uid; self or owner/admin)")
|
|
645
645
|
.option("--json", "Print a single JSON object")
|
|
646
646
|
.option("--no-fix", "Diagnose only: do not fetch, pin, or repair sync")
|
|
647
647
|
.option("--yes", "Answer the one send-confirmation yes (hosts must pass this explicitly)")
|
|
@@ -680,11 +680,22 @@ export function resolveAccessCompany(target, opts) {
|
|
|
680
680
|
const hqRoot = opts?.hqRoot ?? DEFAULT_HQ_ROOT;
|
|
681
681
|
return opts?.company ?? slugFromTarget(target) ?? readActiveCompany(hqRoot);
|
|
682
682
|
}
|
|
683
|
+
export async function resolveAccessAsPersonUid(token, companyUid, person) {
|
|
684
|
+
const trimmed = person.trim();
|
|
685
|
+
if (/^(?:prs|agt)_[A-Za-z0-9_-]+$/.test(trimmed))
|
|
686
|
+
return trimmed;
|
|
687
|
+
const email = trimmed.toLowerCase();
|
|
688
|
+
if (!/^[^\s]+@[^\s]+$/.test(email)) {
|
|
689
|
+
throw new Error("Pass an email, or the person's exact prs_ or agt_ UID, to --as.");
|
|
690
|
+
}
|
|
691
|
+
const members = await listActiveMembers(token, companyUid);
|
|
692
|
+
const member = members.find((candidate) => candidate.personEmail?.trim().toLowerCase() === email);
|
|
693
|
+
if (member?.personUid)
|
|
694
|
+
return member.personUid;
|
|
695
|
+
throw new Error(`No active member has email '${email}' in this company. Run \`hq members list\`.`);
|
|
696
|
+
}
|
|
683
697
|
export async function runAccessForPerson(rawPath, personUid, opts) {
|
|
684
|
-
const
|
|
685
|
-
if (!/^(?:prs|agt)_[A-Za-z0-9_-]+$/.test(uid)) {
|
|
686
|
-
throw new Error("Pass the person's exact prs_ or agt_ UID to --as.");
|
|
687
|
-
}
|
|
698
|
+
const requested = personUid.trim();
|
|
688
699
|
const pathSlug = slugFromTarget(rawPath);
|
|
689
700
|
if (opts?.company && pathSlug && opts.company !== pathSlug) {
|
|
690
701
|
throw new Error("The --company value does not match the company in the path.");
|
|
@@ -698,6 +709,7 @@ export async function runAccessForPerson(rawPath, personUid, opts) {
|
|
|
698
709
|
}
|
|
699
710
|
const { token } = await resolveBrowseSession();
|
|
700
711
|
const companyUid = await getCompanyUid(token, company);
|
|
712
|
+
const uid = await resolveAccessAsPersonUid(token, companyUid, requested);
|
|
701
713
|
const response = await vaultApiFetch({
|
|
702
714
|
token,
|
|
703
715
|
path: `/files/${encodeURIComponent(companyUid)}/members/${encodeURIComponent(uid)}/access`,
|
package/dist/commands/bot.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { type RuntimeSignInIssue } from "../lib/bot/runtime-sign-in.js";
|
|
3
|
+
import { probeBotRuntime, type RuntimeProbeResult } from "../lib/bot/probe.js";
|
|
3
4
|
import { type BotLocalConfig, type MyLocalBot, type BotKind, type BotMemoryMode, type BotNotRunnableReason, type BotConfig, type BotRuntimeId, type LaunchctlExec } from "../lib/bot/index.js";
|
|
4
5
|
/** Real launchctl; every failure is a non-ok result, never a throw. */
|
|
5
6
|
export declare const defaultLaunchctl: LaunchctlExec;
|
|
@@ -134,6 +135,17 @@ export declare function runBotSet(nameArg: string, opts: {
|
|
|
134
135
|
effort?: string;
|
|
135
136
|
json?: boolean;
|
|
136
137
|
}): Promise<void>;
|
|
138
|
+
/** `hq bot probe`: exit code 0 when the runtime answered, 1 when it did not. */
|
|
139
|
+
export declare function runBotProbe(opts: {
|
|
140
|
+
runtime?: string;
|
|
141
|
+
bot?: string;
|
|
142
|
+
model?: string;
|
|
143
|
+
effort?: string;
|
|
144
|
+
timeout?: string;
|
|
145
|
+
json?: boolean;
|
|
146
|
+
}, deps?: {
|
|
147
|
+
probe?: typeof probeBotRuntime;
|
|
148
|
+
}): Promise<RuntimeProbeResult>;
|
|
137
149
|
/**
|
|
138
150
|
* The one bot the caller means. Matches the local folder name, the cloud slug,
|
|
139
151
|
* the display name, or the agt_ uid; an ambiguous name is refused rather than
|
package/dist/commands/bot.js
CHANGED
|
@@ -49,6 +49,8 @@ import { daemonPaths } from "../lib/daemon/paths.js";
|
|
|
49
49
|
import { launchdLabel } from "../lib/bot/paths.js";
|
|
50
50
|
import { isAncestorProcess } from "../lib/bot/self-process.js";
|
|
51
51
|
import { runtimeDisplayName } from "../lib/bot/runtime-sign-in.js";
|
|
52
|
+
import { classifyRuntimeFailure, runtimeFailureReason } from "../lib/bot/runtime-failure.js";
|
|
53
|
+
import { PROBE_DEFAULT_TIMEOUT_MS, probeBotRuntime } from "../lib/bot/probe.js";
|
|
52
54
|
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, 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, } from "../lib/bot/index.js";
|
|
53
55
|
function collectRepeated(value, previous) {
|
|
54
56
|
return [...previous, value];
|
|
@@ -994,8 +996,17 @@ async function runBotStatus(nameArg, opts) {
|
|
|
994
996
|
const tool = runtimeDisplayName(status.runtimeSignIn.runtime);
|
|
995
997
|
console.log(` ${chalk.yellow(`${tool} needs to sign in again`)} — sign in from HQ, or run \`${status.runtimeSignIn.runtime} ${status.runtimeSignIn.runtime === "claude" ? "auth login" : "login"}\`, then \`hq bot restart ${name}\``);
|
|
996
998
|
}
|
|
997
|
-
if (status.
|
|
998
|
-
console.log(`
|
|
999
|
+
if (status.modelHold && new Date(status.modelHold.until).getTime() > Date.now()) {
|
|
1000
|
+
console.log(` ${chalk.yellow(`holding until ${status.modelHold.until}`)} after repeated model failures; the next message after that runs again`);
|
|
1001
|
+
}
|
|
1002
|
+
if (status.runtimeModelFallback) {
|
|
1003
|
+
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`);
|
|
1004
|
+
}
|
|
1005
|
+
// The raw runtime text stays in the log (hq bot logs) and the JSON output.
|
|
1006
|
+
if (status.lastError) {
|
|
1007
|
+
const cls = classifyRuntimeFailure(status.lastError);
|
|
1008
|
+
console.log(` last problem: ${chalk.yellow(`${cls}: ${runtimeFailureReason(cls, row.runtime)}`)} ${chalk.dim(`(details: hq bot logs ${name})`)}`);
|
|
1009
|
+
}
|
|
999
1010
|
}
|
|
1000
1011
|
console.log(chalk.dim(` state dir: ${dir}`));
|
|
1001
1012
|
}
|
|
@@ -1278,6 +1289,32 @@ export async function runBotSet(nameArg, opts) {
|
|
|
1278
1289
|
}
|
|
1279
1290
|
console.log(chalk.green(`${name} now thinks with ${result.model ?? "the default model"} at ${result.effort} effort (from its next message).`));
|
|
1280
1291
|
}
|
|
1292
|
+
/** `hq bot probe`: exit code 0 when the runtime answered, 1 when it did not. */
|
|
1293
|
+
export async function runBotProbe(opts, deps = {}) {
|
|
1294
|
+
const saved = opts.bot ? requireBot(opts.bot).config : undefined;
|
|
1295
|
+
const runtimeArg = opts.runtime ?? saved?.runtime;
|
|
1296
|
+
if (!isBotRuntimeId(runtimeArg)) {
|
|
1297
|
+
throw Object.assign(new Error(opts.runtime === undefined && !opts.bot ? `Pass --runtime <${BOT_RUNTIMES.join("|")}> or --bot <name>.` : `--runtime must be one of ${BOT_RUNTIMES.join(", ")}`), { expected: true });
|
|
1298
|
+
}
|
|
1299
|
+
const runtime = runtimeArg;
|
|
1300
|
+
const model = opts.model !== undefined ? validateBotModel(opts.model) : saved?.model;
|
|
1301
|
+
const effort = opts.effort !== undefined ? validateBotEffort(runtime, opts.effort) : saved?.effort;
|
|
1302
|
+
const seconds = Number(opts.timeout ?? PROBE_DEFAULT_TIMEOUT_MS / 1000);
|
|
1303
|
+
const timeoutMs = Number.isFinite(seconds) && seconds > 0 ? Math.round(seconds * 1000) : PROBE_DEFAULT_TIMEOUT_MS;
|
|
1304
|
+
const result = await (deps.probe ?? probeBotRuntime)({
|
|
1305
|
+
runtime,
|
|
1306
|
+
...(model ? { model } : {}),
|
|
1307
|
+
...(effort ? { effort } : {}),
|
|
1308
|
+
timeoutMs,
|
|
1309
|
+
});
|
|
1310
|
+
if (!result.ok)
|
|
1311
|
+
process.exitCode = 1;
|
|
1312
|
+
if (opts.json)
|
|
1313
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1314
|
+
else
|
|
1315
|
+
console.log(result.ok ? chalk.green(result.detail) : chalk.red(result.detail));
|
|
1316
|
+
return result;
|
|
1317
|
+
}
|
|
1281
1318
|
async function runBotIntro(nameArg) {
|
|
1282
1319
|
const { name, dir, config } = requireBot(nameArg);
|
|
1283
1320
|
if (hasPromotionHold(dir))
|
|
@@ -1938,6 +1975,21 @@ export function registerBotCommand(program) {
|
|
|
1938
1975
|
.option("--effort <level>", `Thinking level (claude ${BOT_EFFORT_LEVELS.claude.join("|")}; codex ${BOT_EFFORT_LEVELS.codex.join("|")}; grok ${BOT_EFFORT_LEVELS.grok.join("|")}), or default (${DEFAULT_BOT_EFFORT})`)
|
|
1939
1976
|
.option("--json", "Print machine-readable JSON")
|
|
1940
1977
|
.action((name, opts) => wrap(() => runBotSet(name, opts))());
|
|
1978
|
+
bot
|
|
1979
|
+
.command("set-model <name> <model>")
|
|
1980
|
+
.description("Set the model this bot asks its runtime for (stored in the bot's own settings; the runtime's own config is never changed), or default to clear it")
|
|
1981
|
+
.option("--json", "Print machine-readable JSON")
|
|
1982
|
+
.action((name, model, opts) => wrap(() => runBotSet(name, { model, ...(opts.json ? { json: true } : {}) }))());
|
|
1983
|
+
bot
|
|
1984
|
+
.command("probe")
|
|
1985
|
+
.description("Run one minimal test turn with a runtime, model and thinking level exactly as a bot would, and say whether a bot set up that way can answer. Costs one tiny model turn on your own login; runs in an empty scratch folder, so no HQ settings or hooks load")
|
|
1986
|
+
.option("--runtime <runtime>", `Model CLI to check: ${BOT_RUNTIMES.join("|")} (required unless --bot is given)`)
|
|
1987
|
+
.option("--bot <name>", "Check with this bot's saved runtime, model and thinking level (--runtime/--model/--effort override them)")
|
|
1988
|
+
.option("--model <model>", "Model id to check, or default for the runtime's own default")
|
|
1989
|
+
.option("--effort <level>", "Thinking level to check")
|
|
1990
|
+
.option("--timeout <seconds>", "Give up after this many seconds", String(PROBE_DEFAULT_TIMEOUT_MS / 1000))
|
|
1991
|
+
.option("--json", "Print { ok, class, detail, ... } as JSON")
|
|
1992
|
+
.action((opts) => wrap(async () => { await runBotProbe(opts); })());
|
|
1941
1993
|
bot
|
|
1942
1994
|
.command("intro <name>")
|
|
1943
1995
|
.description("Have the bot re-send its introduction DM to you")
|
package/dist/commands/files.js
CHANGED
|
@@ -474,8 +474,9 @@ export function registerFilesCommand(program) {
|
|
|
474
474
|
// store `granteeId === ""` on the wire, but a blank cell is confusing
|
|
475
475
|
// in tabular output, so we render a human-readable phrase instead.
|
|
476
476
|
function displayGrantee(e) {
|
|
477
|
-
if (e.granteeType === "company-wide")
|
|
478
|
-
return "Everyone in company";
|
|
477
|
+
if (e.granteeType === "company-wide") {
|
|
478
|
+
return "Everyone in company (including guests)";
|
|
479
|
+
}
|
|
479
480
|
return e.granteeId;
|
|
480
481
|
}
|
|
481
482
|
function printEntryTable(rows, showSource) {
|
|
@@ -531,6 +532,10 @@ export function registerFilesCommand(program) {
|
|
|
531
532
|
console.log("\nGranted on descendant prefixes (do not affect this prefix's access):");
|
|
532
533
|
printEntryTable(tree.children, true);
|
|
533
534
|
}
|
|
535
|
+
const allEntries = [...tree.direct, ...tree.inherited, ...tree.children];
|
|
536
|
+
if (allEntries.some((entry) => entry.granteeType === "company-wide")) {
|
|
537
|
+
console.log(chalk.gray("\nCompany-wide grants apply to every active member, including guests. Demoting someone to guest does not exclude them. Use hq members remove to end membership, or hq access <path> --as <email> to see effective access."));
|
|
538
|
+
}
|
|
534
539
|
}
|
|
535
540
|
catch (err) {
|
|
536
541
|
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
@@ -182,5 +182,47 @@ export declare function revokeInvite(token: string, tokenOrKey: string, companyU
|
|
|
182
182
|
* a synchronous-shaped rejection for a bad role.
|
|
183
183
|
*/
|
|
184
184
|
export declare function changeMemberRole(token: string, companyUid: string, membershipKey: string, newRole: Role): Promise<void>;
|
|
185
|
+
/** Guests remain active members, so company-wide (@all) grants still apply. */
|
|
186
|
+
export declare const GUEST_COMPANY_WIDE_NOTE = "Guests stay active members. Company-wide grants (Everyone in company / @all) still apply to them. Demoting to guest does not lock someone out. Use `hq members remove <email>`.";
|
|
187
|
+
export declare function personUidFromMembershipKey(membershipKey: string): string;
|
|
188
|
+
interface MemberAccessGrant {
|
|
189
|
+
path?: unknown;
|
|
190
|
+
permission?: unknown;
|
|
191
|
+
sources?: Array<{
|
|
192
|
+
via?: unknown;
|
|
193
|
+
}>;
|
|
194
|
+
}
|
|
195
|
+
interface MemberAccessProjection {
|
|
196
|
+
personUid?: unknown;
|
|
197
|
+
identity?: {
|
|
198
|
+
primaryEmail?: unknown;
|
|
199
|
+
};
|
|
200
|
+
files?: {
|
|
201
|
+
grants?: MemberAccessGrant[];
|
|
202
|
+
};
|
|
203
|
+
secrets?: {
|
|
204
|
+
grants?: MemberAccessGrant[];
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/** Paths granted to this person or their email — not company-wide or group. */
|
|
208
|
+
export declare function personalGrantPaths(grants: MemberAccessGrant[] | undefined): string[];
|
|
209
|
+
export declare function fetchMemberAccessProjection(token: string, companyUid: string, personUid: string): Promise<MemberAccessProjection | null>;
|
|
210
|
+
export interface RemoveMemberResult {
|
|
211
|
+
membershipKey: string;
|
|
212
|
+
droppedFileGrants: string[];
|
|
213
|
+
droppedSecretGrants: string[];
|
|
214
|
+
skippedGrantDrops: string[];
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* End an active membership via `POST /membership/revoke`, then drop person and
|
|
218
|
+
* email file/secret grants collected from the member-access projection.
|
|
219
|
+
* Company-wide and group grants are left in place — they belong to the company,
|
|
220
|
+
* not the person. Membership end already makes company-wide grants inert for
|
|
221
|
+
* this principal because they are no longer an active member.
|
|
222
|
+
*/
|
|
223
|
+
export declare function removeActiveMember(token: string, companyUid: string, membershipKey: string, opts?: {
|
|
224
|
+
email?: string;
|
|
225
|
+
}): Promise<RemoveMemberResult>;
|
|
185
226
|
export declare function registerMembersCommand(program: Command): void;
|
|
227
|
+
export {};
|
|
186
228
|
//# sourceMappingURL=members.d.ts.map
|