@indigoai-us/hq-cli 5.345.104 → 5.345.106

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.
Files changed (83) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/assets/bot-workers/setup/skills/add-a-pack.md +130 -0
  3. package/assets/bot-workers/setup/worker.yaml +3 -0
  4. package/dist/command-catalog.generated.d.ts +206 -0
  5. package/dist/command-catalog.generated.js +246 -0
  6. package/dist/commands/bot.d.ts +10 -0
  7. package/dist/commands/bot.js +20 -12
  8. package/dist/commands/daemon.js +385 -1
  9. package/dist/commands/integrations-connect.d.ts +6 -0
  10. package/dist/commands/integrations-connect.js +36 -0
  11. package/dist/commands/integrations-slack.d.ts +30 -0
  12. package/dist/commands/integrations-slack.js +81 -0
  13. package/dist/commands/integrations.js +2 -0
  14. package/dist/commands/logout.d.ts +6 -1
  15. package/dist/commands/logout.js +18 -2
  16. package/dist/commands/pack-install.d.ts +20 -1
  17. package/dist/commands/pack-install.js +80 -5
  18. package/dist/commands/pack-start-bot.d.ts +24 -0
  19. package/dist/commands/pack-start-bot.js +76 -0
  20. package/dist/commands/packs.d.ts +9 -0
  21. package/dist/commands/packs.js +26 -3
  22. package/dist/commands/pkg-install.js +11 -2
  23. package/dist/commands/secrets.d.ts +1 -5
  24. package/dist/commands/secrets.js +2 -92
  25. package/dist/fast-scaffold.d.ts +13 -0
  26. package/dist/fast-scaffold.js +136 -0
  27. package/dist/index.js +15 -1
  28. package/dist/lib/bot/config.d.ts +7 -0
  29. package/dist/lib/bot/daemon.js +2 -1
  30. package/dist/lib/bot/index.d.ts +2 -0
  31. package/dist/lib/bot/index.js +2 -0
  32. package/dist/lib/bot/lean-settings.d.ts +16 -0
  33. package/dist/lib/bot/lean-settings.js +34 -0
  34. package/dist/lib/bot/pack-link.d.ts +66 -0
  35. package/dist/lib/bot/pack-link.js +153 -0
  36. package/dist/lib/bot/pack-setup-driver.d.ts +37 -0
  37. package/dist/lib/bot/pack-setup-driver.js +83 -0
  38. package/dist/lib/bot/pack-setup.d.ts +62 -0
  39. package/dist/lib/bot/pack-setup.js +103 -0
  40. package/dist/lib/bot/prompt.d.ts +8 -0
  41. package/dist/lib/bot/prompt.js +5 -0
  42. package/dist/lib/bot/run.d.ts +18 -2
  43. package/dist/lib/bot/run.js +163 -25
  44. package/dist/lib/bot/runtime/claude.d.ts +27 -2
  45. package/dist/lib/bot/runtime/claude.js +49 -4
  46. package/dist/lib/bot/runtime/types.d.ts +5 -0
  47. package/dist/lib/bot/runtime-failure.d.ts +18 -2
  48. package/dist/lib/bot/runtime-failure.js +21 -1
  49. package/dist/lib/bot/toolchain.d.ts +25 -0
  50. package/dist/lib/bot/toolchain.js +33 -0
  51. package/dist/lib/bot/worker-source.d.ts +1 -0
  52. package/dist/lib/bot/worker-source.js +7 -1
  53. package/dist/lib/daemon/catalog.d.ts +15 -0
  54. package/dist/lib/daemon/catalog.js +49 -0
  55. package/dist/lib/daemon/paths.d.ts +1 -0
  56. package/dist/lib/daemon/paths.js +1 -0
  57. package/dist/lib/daemon/remote-control-forwarder.d.ts +14 -0
  58. package/dist/lib/daemon/remote-control-forwarder.js +104 -0
  59. package/dist/lib/daemon/remote-control-pools.d.ts +44 -0
  60. package/dist/lib/daemon/remote-control-pools.js +245 -0
  61. package/dist/lib/daemon/remote-control.d.ts +4 -0
  62. package/dist/lib/daemon/remote-control.js +17 -2
  63. package/dist/lib/daemon/run.js +126 -0
  64. package/dist/lib/doctor/fix/refresh-sync-progress.d.ts +5 -4
  65. package/dist/lib/doctor/fix/refresh-sync-progress.js +5 -4
  66. package/dist/lib/doctor/json-output.d.ts +5 -5
  67. package/dist/lib/doctor/json-output.js +2 -5
  68. package/dist/lib/packages/setup-block.d.ts +43 -0
  69. package/dist/lib/packages/setup-block.js +236 -0
  70. package/dist/lib/packages/setup-prompt.d.ts +34 -0
  71. package/dist/lib/packages/setup-prompt.js +128 -0
  72. package/dist/lib/packages/setup-takeover.d.ts +50 -0
  73. package/dist/lib/packages/setup-takeover.js +75 -0
  74. package/dist/lib/packages/start-bot.d.ts +47 -0
  75. package/dist/lib/packages/start-bot.js +66 -0
  76. package/dist/lib/session-company-lock.d.ts +7 -0
  77. package/dist/lib/session-company-lock.js +97 -0
  78. package/dist/types.d.ts +82 -0
  79. package/dist/utils/cognito-config.d.ts +5 -0
  80. package/dist/utils/cognito-config.js +14 -0
  81. package/dist/utils/cognito-revoke.d.ts +33 -0
  82. package/dist/utils/cognito-revoke.js +42 -0
  83. package/package.json +1 -1
@@ -0,0 +1,97 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { resolveSessionId } from "./core-utils/session-id.js";
4
+ import { resolveDefaultHqRoot } from "../utils/hq-root-resolver.js";
5
+ /** Read the session's validated company scope capability or legacy metadata. */
6
+ export function readSessionCompanyLock(env = process.env) {
7
+ const root = resolveDefaultHqRoot({ env });
8
+ const sessionId = resolveSessionId(root, env);
9
+ if (!sessionId)
10
+ return undefined;
11
+ const sessionDir = path.join(root, "workspace/sessions", sessionId);
12
+ const capabilityPath = path.join(sessionDir, "scope-capability.json");
13
+ const metaPath = path.join(sessionDir, "meta.yaml");
14
+ let primary;
15
+ let companies;
16
+ let hasCapability = false;
17
+ try {
18
+ fs.lstatSync(capabilityPath);
19
+ hasCapability = true;
20
+ }
21
+ catch (error) {
22
+ const code = error.code;
23
+ if (code !== "ENOENT" && code !== "ENOTDIR")
24
+ throw new Error("Cannot verify this session's company lock because its scope capability is unreadable.");
25
+ }
26
+ if (hasCapability) {
27
+ let value;
28
+ try {
29
+ value = JSON.parse(fs.readFileSync(capabilityPath, "utf8"));
30
+ }
31
+ catch {
32
+ throw new Error("Cannot verify this session's company lock because its scope capability is unreadable.");
33
+ }
34
+ if (!value || typeof value !== "object" || Array.isArray(value))
35
+ throw new Error("Cannot verify this session's company lock because its scope capability is invalid.");
36
+ const cap = value;
37
+ if (cap.session_id !== sessionId)
38
+ throw new Error("Cannot verify this session's company lock because its scope capability does not match the active session.");
39
+ if (typeof cap.company_slug !== "string" || !/^[A-Za-z0-9_-]+$/.test(cap.company_slug))
40
+ throw new Error("Cannot verify this session's company lock because its scope capability is invalid.");
41
+ primary = cap.company_slug;
42
+ if (cap.company_slugs === undefined)
43
+ companies = [primary];
44
+ else if (Array.isArray(cap.company_slugs)
45
+ && cap.company_slugs.length > 0
46
+ && cap.company_slugs.every((slug) => typeof slug === "string" && /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(slug))
47
+ && cap.company_slugs[0] === primary
48
+ && new Set(cap.company_slugs).size === cap.company_slugs.length
49
+ && ((primary === "personal" && cap.company_slugs.length === 1)
50
+ || (primary !== "personal" && !cap.company_slugs.includes("personal"))))
51
+ companies = [...cap.company_slugs];
52
+ else
53
+ throw new Error("Cannot verify this session's company lock because its scope capability is invalid.");
54
+ }
55
+ else {
56
+ try {
57
+ const meta = fs.readFileSync(metaPath, "utf8");
58
+ const read = (key) => meta.split(/\r?\n/).find((line) => new RegExp(`^\\s*${key}:`).test(line))?.replace(/^\s*[^:]+:\s*/, "").replace(/^"|"$/g, "") ?? "";
59
+ primary = read("company_slug");
60
+ const hasList = meta.split(/\r?\n/).some((line) => /^\s*company_slugs:/.test(line));
61
+ const listed = hasList ? read("company_slugs") : "";
62
+ if (hasList && (!listed || !primary))
63
+ throw new Error("invalid company lock");
64
+ companies = hasList ? listed.split(",") : primary ? [primary] : [];
65
+ if (companies.some((slug) => !/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(slug)))
66
+ throw new Error("invalid company lock");
67
+ if (new Set(companies).size !== companies.length)
68
+ throw new Error("invalid company lock");
69
+ if (primary && companies[0] !== primary)
70
+ throw new Error("invalid company lock");
71
+ if (companies.length > 1 && companies.includes("personal"))
72
+ throw new Error("invalid company lock");
73
+ }
74
+ catch (error) {
75
+ const code = error.code;
76
+ if (code === "ENOENT" || code === "ENOTDIR" || code === "EACCES")
77
+ return { sessionId, companies: [] };
78
+ throw new Error("Cannot verify this session's company lock because its metadata is invalid.");
79
+ }
80
+ }
81
+ const real = companies.filter((slug) => {
82
+ if (slug === "personal")
83
+ return true;
84
+ try {
85
+ const companyPath = path.join(root, "companies", slug);
86
+ const stat = fs.lstatSync(companyPath);
87
+ return stat.isDirectory() && !stat.isSymbolicLink();
88
+ }
89
+ catch {
90
+ return false;
91
+ }
92
+ });
93
+ if (primary && real[0] !== primary)
94
+ return { sessionId, companies: [] };
95
+ return { sessionId, companies: real };
96
+ }
97
+ //# sourceMappingURL=session-company-lock.js.map
package/dist/types.d.ts CHANGED
@@ -127,6 +127,88 @@ export interface PackManifest {
127
127
  entrypoint: string;
128
128
  prompt?: string;
129
129
  };
130
+ /**
131
+ * Guided in-app setup. Optional — absent on
132
+ * legacy packs, which install unchanged. Validated by
133
+ * `lib/packages/setup-block.ts`; `hq packages install --start-bot` creates the
134
+ * bot it names and the bot's system prompt renders the questions and the run
135
+ * command from it.
136
+ */
137
+ setup?: PackSetup;
138
+ }
139
+ /** One question the pack bot asks, in order. */
140
+ export interface PackSetupQuestion {
141
+ id: string;
142
+ text: string;
143
+ /** Suggested answers shown as buttons; may be empty (free text only). */
144
+ options: string[];
145
+ /** The bot proposes this value and asks to confirm (e.g. company_display_name). */
146
+ default_from?: string;
147
+ skippable: boolean;
148
+ /** What the bot needs to learn from this question; it follows up until it has that. */
149
+ goal?: string;
150
+ }
151
+ export interface PackSetup {
152
+ bot: {
153
+ /** Bot handle; also the worker id under contributes.workers. ^[a-z0-9-]{2,32}$ */
154
+ name: string;
155
+ display_name: string;
156
+ intro: string;
157
+ };
158
+ /** When true and HQ's setup bot has not finished, the pack bot finishes HQ setup first. */
159
+ takeover_setup: boolean;
160
+ questions: PackSetupQuestion[];
161
+ run: {
162
+ /** Relative to the pack root; must exist in the pack. */
163
+ script: string;
164
+ /** question id -> flag (the bot passes --firm itself). */
165
+ args: Record<string, string>;
166
+ /** Commands run after `script`, in order; each first token exists in the pack. */
167
+ then: string[];
168
+ };
169
+ done: {
170
+ message: string;
171
+ suggestion?: string;
172
+ };
173
+ /** Documents worth asking for at the start (a rate card, a past proposal); the bot reads them and asks less. */
174
+ documents?: string[];
175
+ /**
176
+ * Kinds of tools the bot asks about, connects and reads (with the owner's
177
+ * yes) to answer questions itself before asking them: sent client emails for
178
+ * voice, CRM deals for pricing. `informs` names question ids.
179
+ */
180
+ sources?: PackSetupSource[];
181
+ }
182
+ /**
183
+ * One tool a source can be. It connects in one of two ways: through HQ
184
+ * integrations (`domain`, a sign-in link from `hq integrations connect`), or
185
+ * through a script the pack ships (`tool`, run with `connect` / `status` and
186
+ * read with `usage`). `available: false` means the pack cannot read it yet.
187
+ */
188
+ export interface PackSetupSourceOption {
189
+ name: string;
190
+ domain?: string;
191
+ /** Pack-relative script (.mjs/.js run with node, .sh with bash). */
192
+ tool?: string;
193
+ /** Arguments that start the connection (default "connect"). It prints JSON: a `url` to send, or `started: true` when it opened a sign-in page itself. */
194
+ connect?: string;
195
+ /** Arguments that report the connection (default "status"); JSON with `connected`. */
196
+ status?: string;
197
+ /** How to read with the tool, e.g. "search <terms> | recent | read <id>". */
198
+ usage?: string;
199
+ available?: boolean;
200
+ /** Shown to the bot when `available` is false, e.g. "coming in a later update". */
201
+ note?: string;
202
+ }
203
+ export interface PackSetupSource {
204
+ /** The kind of tool, as the person would say it: "email", "CRM", "team chat", "docs". */
205
+ kind: string;
206
+ /** What to read from it, in words the bot repeats when it asks: "the last 30 emails you sent to clients". */
207
+ read: string;
208
+ /** Question ids this source can answer. */
209
+ informs: string[];
210
+ /** Likely tools for this kind and how each one connects. */
211
+ options: PackSetupSourceOption[];
130
212
  }
131
213
  export {};
132
214
  //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,5 @@
1
+ import type { CognitoAuthConfig } from "@indigoai-us/hq-cloud";
2
+ /** Lightweight Cognito and Vault defaults shared by startup-safe modules. */
3
+ export declare const DEFAULT_COGNITO: CognitoAuthConfig;
4
+ export declare const DEFAULT_VAULT_API_URL: string;
5
+ //# sourceMappingURL=cognito-config.d.ts.map
@@ -0,0 +1,14 @@
1
+ /** Lightweight Cognito and Vault defaults shared by startup-safe modules. */
2
+ export const DEFAULT_COGNITO = {
3
+ region: process.env.AWS_REGION ?? "us-east-1",
4
+ userPoolDomain: process.env.HQ_COGNITO_DOMAIN ?? "vault-indigo-hq-prod",
5
+ clientId: process.env.HQ_COGNITO_CLIENT_ID ?? "7acei2c8v870enheptb1j5foln",
6
+ port: process.env.HQ_COGNITO_CALLBACK_PORT
7
+ ? Number(process.env.HQ_COGNITO_CALLBACK_PORT)
8
+ : 8765,
9
+ identityProvider: process.env.HQ_COGNITO_IDENTITY_PROVIDER !== undefined
10
+ ? process.env.HQ_COGNITO_IDENTITY_PROVIDER || undefined
11
+ : "Google",
12
+ };
13
+ export const DEFAULT_VAULT_API_URL = process.env.HQ_VAULT_API_URL ?? "https://hqapi.hq.computer";
14
+ //# sourceMappingURL=cognito-config.js.map
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Server-side revocation of a Cognito refresh token.
3
+ *
4
+ * `hq logout` calls this before it deletes the local token file so that a
5
+ * copied token file stops working too. Cognito's `/oauth2/revoke` endpoint on
6
+ * the hosted domain (the same domain the CLI already uses for `/oauth2/token`)
7
+ * revokes the refresh token and every access/ID token issued from it. The CLI
8
+ * app client is public (no client secret), so the body carries only the token
9
+ * and the client id.
10
+ *
11
+ * This never throws and never includes the token in its result: callers get a
12
+ * plain outcome they can print.
13
+ */
14
+ export interface CognitoRevokeConfig {
15
+ region: string;
16
+ userPoolDomain: string;
17
+ clientId: string;
18
+ }
19
+ export type RevokeOutcome = {
20
+ ok: true;
21
+ } | {
22
+ ok: false;
23
+ reason: "network" | "timeout" | "rejected";
24
+ status?: number;
25
+ };
26
+ /** Upper bound on the revoke request so logout never hangs on a dead network. */
27
+ export declare const REVOKE_TIMEOUT_MS = 5000;
28
+ export declare function cognitoRevokeEndpoint(config: CognitoRevokeConfig): string;
29
+ export declare function revokeRefreshToken(refreshToken: string, config: CognitoRevokeConfig, opts?: {
30
+ timeoutMs?: number;
31
+ fetchImpl?: typeof fetch;
32
+ }): Promise<RevokeOutcome>;
33
+ //# sourceMappingURL=cognito-revoke.d.ts.map
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Server-side revocation of a Cognito refresh token.
3
+ *
4
+ * `hq logout` calls this before it deletes the local token file so that a
5
+ * copied token file stops working too. Cognito's `/oauth2/revoke` endpoint on
6
+ * the hosted domain (the same domain the CLI already uses for `/oauth2/token`)
7
+ * revokes the refresh token and every access/ID token issued from it. The CLI
8
+ * app client is public (no client secret), so the body carries only the token
9
+ * and the client id.
10
+ *
11
+ * This never throws and never includes the token in its result: callers get a
12
+ * plain outcome they can print.
13
+ */
14
+ /** Upper bound on the revoke request so logout never hangs on a dead network. */
15
+ export const REVOKE_TIMEOUT_MS = 5_000;
16
+ export function cognitoRevokeEndpoint(config) {
17
+ return `https://${config.userPoolDomain}.auth.${config.region}.amazoncognito.com/oauth2/revoke`;
18
+ }
19
+ export async function revokeRefreshToken(refreshToken, config, opts = {}) {
20
+ const fetchImpl = opts.fetchImpl ?? fetch;
21
+ const body = new URLSearchParams({ token: refreshToken, client_id: config.clientId });
22
+ try {
23
+ const response = await fetchImpl(cognitoRevokeEndpoint(config), {
24
+ method: "POST",
25
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
26
+ body: body.toString(),
27
+ signal: AbortSignal.timeout(opts.timeoutMs ?? REVOKE_TIMEOUT_MS),
28
+ });
29
+ // Drain the body so the socket is released; its content is not needed.
30
+ await response.text().catch(() => undefined);
31
+ if (response.ok)
32
+ return { ok: true };
33
+ return { ok: false, reason: "rejected", status: response.status };
34
+ }
35
+ catch (error) {
36
+ const name = error instanceof Error ? error.name : "";
37
+ if (name === "TimeoutError" || name === "AbortError")
38
+ return { ok: false, reason: "timeout" };
39
+ return { ok: false, reason: "network" };
40
+ }
41
+ }
42
+ //# sourceMappingURL=cognito-revoke.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.345.104",
3
+ "version": "5.345.106",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {