@indigoai-us/hq-cli 5.242.0 → 5.244.0
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 +42 -0
- package/assets/bot-workers/setup/skills/agents-and-team.md +17 -4
- package/assets/bot-workers/setup/skills/your-business.md +11 -0
- package/dist/commands/auth.js +73 -16
- package/dist/commands/lanes-accounts.d.ts +25 -0
- package/dist/commands/lanes-accounts.js +527 -0
- package/dist/commands/lanes-questions.d.ts +34 -0
- package/dist/commands/lanes-questions.js +876 -0
- package/dist/commands/lanes-shared.d.ts +155 -0
- package/dist/commands/lanes-shared.js +2 -0
- package/dist/commands/lanes.d.ts +4 -6
- package/dist/commands/lanes.js +128 -1437
- package/dist/commands/safe-extract.d.ts +11 -2
- package/dist/commands/safe-extract.js +37 -8
- package/dist/lib/auth-login-provider-state.d.ts +8 -0
- package/dist/lib/auth-login-provider-state.js +63 -0
- package/dist/lib/bot/progress.d.ts +0 -3
- package/dist/lib/bot/progress.js +0 -3
- package/dist/lib/bot/run.js +5 -7
- package/dist/lib/install-path-compatibility-flag.d.ts +9 -0
- package/dist/lib/install-path-compatibility-flag.js +12 -0
- package/dist/main.js +9 -6
- package/dist/utils/cognito-session.js +2 -1
- package/dist/utils/exclusive-lock.d.ts +32 -0
- package/dist/utils/exclusive-lock.js +75 -0
- package/dist/utils/login-provider.d.ts +7 -0
- package/dist/utils/login-provider.js +34 -0
- package/dist/utils/version-gate.d.ts +3 -3
- package/dist/utils/version-gate.js +19 -10
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.244.0] — 2026-09-27
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- The setup bot no longer posts "This one takes a minute. I'll reply here as
|
|
10
|
+
soon as it's done." when a step runs long. Other bots keep their notice.
|
|
11
|
+
- After the bundled setup bot connects its first app, it says "Great, <Tool> is
|
|
12
|
+
connected" with a link to the company's integrations page on hq.computer.
|
|
13
|
+
- The bundled setup bot invites teammates through the new-hire flow when HQ
|
|
14
|
+
has it, and tells the person to have the invitee check their spam folder.
|
|
15
|
+
|
|
16
|
+
## [5.243.0] — 2026-09-27
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- `hq auth login` remembers the last provider used on this computer and shows the Cognito provider choice page on first login. Only the provider name is stored locally.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Internal: moved the accounts, sessions and questions subcommands out of src/commands/lanes.ts; no behaviour change
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Removed the unused `cli.network.force-ipv4` hq-flags lookup and forced IPv4
|
|
28
|
+
startup path. The existing Node family-attempt timeout remains unchanged.
|
|
29
|
+
- `hq auth login` continues when it cannot read or save the remembered provider. It prints a dim warning with the state path and error code; a save failure does not turn a successful sign-in into a login failure.
|
|
30
|
+
|
|
5
31
|
## [5.242.0] — 2026-09-27
|
|
6
32
|
|
|
7
33
|
### Changed
|
|
@@ -14,6 +40,10 @@
|
|
|
14
40
|
company selection when `--company` is omitted; explicit `--company` values
|
|
15
41
|
remain validated.
|
|
16
42
|
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- The `cli.install-path-compatibility` hq-flags key enables pnpm v11+ global-path recognition. On Windows, it also passes drive-path tarballs to tar by filename from the archive directory. The flag defaults to off.
|
|
46
|
+
|
|
17
47
|
## [5.241.0] — 2026-09-27
|
|
18
48
|
|
|
19
49
|
### Fixed
|
|
@@ -1611,6 +1641,18 @@
|
|
|
1611
1641
|
|
|
1612
1642
|
## [Unreleased]
|
|
1613
1643
|
|
|
1644
|
+
### Fixed
|
|
1645
|
+
|
|
1646
|
+
- A bot whose agent home is on a network share can now mint a token. The
|
|
1647
|
+
refresh lock was a POSIX hard-link lock, and CIFS/SMB shares — Azure Files
|
|
1648
|
+
again — do not implement `link()` at all, so every mint failed with
|
|
1649
|
+
ENOTSUP and took the whole probe down with it: no heartbeat ever posted
|
|
1650
|
+
and the inbox poller retried and failed every 15 seconds. The lock now
|
|
1651
|
+
falls back to an exclusive create, which gives the same guarantee and
|
|
1652
|
+
works there. Contention still surfaces as EEXIST on both paths, so
|
|
1653
|
+
stale-owner reclamation is unchanged, and a link that fails for any other
|
|
1654
|
+
reason is still a real error.
|
|
1655
|
+
|
|
1614
1656
|
- `hq mesh daemon install` and `hq mesh daemon run` unload and remove the
|
|
1615
1657
|
legacy `ai.getindigo.hq-work-mesh-listen` unit (LaunchAgent, systemd user
|
|
1616
1658
|
unit, or Scheduled Task) so only the daemon subscribes. The daemon writes
|
|
@@ -114,16 +114,29 @@ console, and they sign in in the browser.
|
|
|
114
114
|
|
|
115
115
|
## Inviting a teammate
|
|
116
116
|
|
|
117
|
+
Every invite runs through HQ's new-hire flow. Read
|
|
118
|
+
`.claude/skills/new-hire/SKILL.md` in the HQ folder before the first invite and
|
|
119
|
+
follow its steps: check `hq members list` first, set the role, invite, then
|
|
120
|
+
the access that person needs (groups, shared apps and keys) and the
|
|
121
|
+
acceptance follow-through. It is your playbook, not something to show them:
|
|
122
|
+
ask its questions in plain words, one at a time, skip what setup has not
|
|
123
|
+
created yet (groups, an onboarding packet), and never name the skill or its
|
|
124
|
+
command. If that file is not in this HQ, the steps below are enough.
|
|
125
|
+
|
|
117
126
|
1. Ask for one email and whether they should be a member or an admin
|
|
118
|
-
(member is the default; admins can only invite members).
|
|
127
|
+
(member is the default; admins can only invite members). If they already
|
|
128
|
+
said who, do not ask again.
|
|
119
129
|
2. Confirm in one line that an invitation email will go to that address, and
|
|
120
130
|
wait for yes.
|
|
121
|
-
3.
|
|
131
|
+
3. Send the invite the way the new-hire flow says
|
|
132
|
+
(`hq members invite <email> --company <slug> --role member`).
|
|
122
133
|
4. Tell them what happens next: the teammate opens the email, signs in with
|
|
123
134
|
Google or Microsoft, and gets the company in their own HQ, so their Claude
|
|
124
135
|
Code or Codex knows the business from their first session. Say what they
|
|
125
|
-
will see that matters to this person (the
|
|
126
|
-
|
|
136
|
+
will see that matters to this person (the client homes, and any app you
|
|
137
|
+
have shared with them; a connected app stays private to the person who
|
|
138
|
+
connected it until it is shared). Always add: "Tell them to check their
|
|
139
|
+
spam folder if the email doesn't show up in a few minutes."
|
|
127
140
|
5. Check with `hq members list` (pending until they accept). If they never
|
|
128
141
|
got it, `--resend` sends it again.
|
|
129
142
|
|
|
@@ -142,6 +142,17 @@ the way a good engineer would, and then do it:
|
|
|
142
142
|
what it means for their team (see "What HQ is" in your instructions). A
|
|
143
143
|
connection is not done until this read works. Never say "connected",
|
|
144
144
|
"done" or "everything is set up" before it does.
|
|
145
|
+
6. **After the first connection only, say where connections live.** Once
|
|
146
|
+
the read works for the first app they connect, open your reply with the
|
|
147
|
+
confirmation and the link, in these words:
|
|
148
|
+
"Great, <Tool> is connected. You can see everything that's connected at
|
|
149
|
+
https://hq.computer/companies/<company-slug>/integrations, or ask me or
|
|
150
|
+
any other AI in HQ to connect another." Use the company's slug from the
|
|
151
|
+
Owner context (for a personal connection, the link is
|
|
152
|
+
https://hq.computer/personal/integrations). Then the real data from step
|
|
153
|
+
5 and the next question. Say this once in the whole setup. For every
|
|
154
|
+
later connection, skip it: show the real data from step 5 and move on.
|
|
155
|
+
Give the link again only if they ask where connections live.
|
|
145
156
|
|
|
146
157
|
If a step needs something only they can do (an admin approval, a paid plan),
|
|
147
158
|
say exactly what, record it in the progress note as waiting, and move on to
|
package/dist/commands/auth.js
CHANGED
|
@@ -16,31 +16,70 @@
|
|
|
16
16
|
import chalk from "chalk";
|
|
17
17
|
import { browserLogin, clearCachedTokens, loadCachedTokens, isExpiring, loadMachineCreds, CognitoAuthError, } from "@indigoai-us/hq-cloud";
|
|
18
18
|
import { DEFAULT_COGNITO, refreshCachedSession, resolveCognitoTokenSource, } from "../utils/cognito-session.js";
|
|
19
|
-
import {
|
|
19
|
+
import { resolveLoginProviderSelection, } from "../utils/login-provider.js";
|
|
20
|
+
import { getLastLoginProviderStatePath, loadLastLoginProvider, storeLastLoginProvider, } from "../lib/auth-login-provider-state.js";
|
|
20
21
|
import { callbackPortBusyGuidance, DEFAULT_CALLBACK_PORT, isCallbackPortBusy, } from "../utils/callback-port-busy.js";
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
* The token was just returned by Cognito's token endpoint, so its contents
|
|
24
|
-
* are trusted enough to print — we never use these claims for authorization.
|
|
25
|
-
*/
|
|
26
|
-
function peekIdToken(idToken) {
|
|
22
|
+
/** Decode token claims for local display and provider-memory purposes only. */
|
|
23
|
+
function decodeIdTokenClaims(idToken) {
|
|
27
24
|
try {
|
|
28
25
|
const payload = idToken.split(".")[1];
|
|
29
26
|
if (!payload)
|
|
30
|
-
return
|
|
27
|
+
return undefined;
|
|
31
28
|
const pad = payload.length % 4 === 0 ? "" : "=".repeat(4 - (payload.length % 4));
|
|
32
29
|
const normalized = payload.replace(/-/g, "+").replace(/_/g, "/") + pad;
|
|
33
30
|
const decoded = JSON.parse(Buffer.from(normalized, "base64").toString("utf-8"));
|
|
34
|
-
return
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
entityType: decoded["custom:entityType"],
|
|
38
|
-
entityUid: decoded["custom:entityUid"],
|
|
39
|
-
};
|
|
31
|
+
return decoded && typeof decoded === "object" && !Array.isArray(decoded)
|
|
32
|
+
? decoded
|
|
33
|
+
: undefined;
|
|
40
34
|
}
|
|
41
35
|
catch {
|
|
42
|
-
return
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function peekIdToken(idToken) {
|
|
40
|
+
const claims = decodeIdTokenClaims(idToken);
|
|
41
|
+
return {
|
|
42
|
+
email: typeof claims?.email === "string" ? claims.email : undefined,
|
|
43
|
+
sub: typeof claims?.sub === "string" ? claims.sub : undefined,
|
|
44
|
+
entityType: typeof claims?.["custom:entityType"] === "string"
|
|
45
|
+
? claims["custom:entityType"]
|
|
46
|
+
: undefined,
|
|
47
|
+
entityUid: typeof claims?.["custom:entityUid"] === "string"
|
|
48
|
+
? claims["custom:entityUid"]
|
|
49
|
+
: undefined,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function errorCode(error) {
|
|
53
|
+
if (error
|
|
54
|
+
&& typeof error === "object"
|
|
55
|
+
&& "code" in error
|
|
56
|
+
&& typeof error.code === "string") {
|
|
57
|
+
return error.code;
|
|
43
58
|
}
|
|
59
|
+
return "UNKNOWN";
|
|
60
|
+
}
|
|
61
|
+
function providerFromIdToken(idToken) {
|
|
62
|
+
const identities = decodeIdTokenClaims(idToken)?.identities;
|
|
63
|
+
if (!Array.isArray(identities))
|
|
64
|
+
return undefined;
|
|
65
|
+
const providers = new Set();
|
|
66
|
+
for (const identity of identities) {
|
|
67
|
+
if (!identity || typeof identity !== "object" || Array.isArray(identity))
|
|
68
|
+
continue;
|
|
69
|
+
const item = identity;
|
|
70
|
+
const providerName = typeof item.providerName === "string"
|
|
71
|
+
? item.providerName
|
|
72
|
+
: item.providerType;
|
|
73
|
+
if (typeof providerName !== "string")
|
|
74
|
+
continue;
|
|
75
|
+
const normalized = providerName.trim().toLowerCase();
|
|
76
|
+
if (normalized === "google")
|
|
77
|
+
providers.add("google");
|
|
78
|
+
if (normalized === "microsoft" || normalized === "microsoftpersonal") {
|
|
79
|
+
providers.add("microsoft");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return providers.size === 1 ? providers.values().next().value : undefined;
|
|
44
83
|
}
|
|
45
84
|
/** Display label for the local machine identity (company agents). */
|
|
46
85
|
function machineIdentityLabel() {
|
|
@@ -67,7 +106,25 @@ export function registerAuthCommands(program) {
|
|
|
67
106
|
return;
|
|
68
107
|
}
|
|
69
108
|
try {
|
|
70
|
-
const
|
|
109
|
+
const canUseRememberedProvider = options.provider === undefined
|
|
110
|
+
&& process.env.HQ_COGNITO_IDENTITY_PROVIDER === undefined;
|
|
111
|
+
let rememberedProvider;
|
|
112
|
+
if (canUseRememberedProvider) {
|
|
113
|
+
try {
|
|
114
|
+
rememberedProvider = loadLastLoginProvider();
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.error(chalk.dim(`Could not read provider state at ${getLastLoginProviderStatePath()} (${errorCode(error)}). Continuing to the provider choice page.`));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const selection = resolveLoginProviderSelection(options.provider, process.env, rememberedProvider);
|
|
121
|
+
const tokens = await browserLogin(selection.config);
|
|
122
|
+
try {
|
|
123
|
+
storeLastLoginProvider(providerFromIdToken(tokens.idToken) ?? selection.providerToRemember);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
console.error(chalk.dim(`Could not save provider state at ${getLastLoginProviderStatePath()} (${errorCode(error)}). The provider choice was not saved.`));
|
|
127
|
+
}
|
|
71
128
|
const who = peekIdToken(tokens.idToken).email ?? "HQ";
|
|
72
129
|
console.log(chalk.green(`Signed in as ${who}`));
|
|
73
130
|
console.log(chalk.dim(` Token cached at ~/.hq/cognito-tokens.json (expires ${tokens.expiresAt})`));
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Ctx, LaneAccountsHandlerDeps } from "./lanes-shared.js";
|
|
2
|
+
export declare function createLaneAccountsHandlers(deps: LaneAccountsHandlerDeps): {
|
|
3
|
+
runAccountsList: (ctx: Ctx, opts: {
|
|
4
|
+
provider?: string;
|
|
5
|
+
}) => Promise<void>;
|
|
6
|
+
runAccountsBalance: (ctx: Ctx, opts: {
|
|
7
|
+
provider?: string;
|
|
8
|
+
apply?: boolean;
|
|
9
|
+
json?: boolean;
|
|
10
|
+
}) => Promise<void>;
|
|
11
|
+
runAccountsLogin: (ctx: Ctx, providerRaw: string, accountId: string, opts: {
|
|
12
|
+
home?: string;
|
|
13
|
+
deviceAuth?: boolean;
|
|
14
|
+
company?: string;
|
|
15
|
+
}) => Promise<void>;
|
|
16
|
+
runAccountsSetHome: (ctx: Ctx, providerRaw: string, accountIdRaw: string, homeRaw: string, opts: {
|
|
17
|
+
host?: string;
|
|
18
|
+
company?: string;
|
|
19
|
+
}) => Promise<void>;
|
|
20
|
+
runAccountsEnable: (ctx: Ctx, providerRaw: string, accountId: string) => Promise<void>;
|
|
21
|
+
runAccountsDisable: (ctx: Ctx, providerRaw: string, accountId: string, opts: {
|
|
22
|
+
reason?: string;
|
|
23
|
+
}) => Promise<void>;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=lanes-accounts.d.ts.map
|