@indigoai-us/hq-cli 5.77.14 → 5.79.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.
Files changed (309) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/assets/scaffold/core/scripts/archive-old-threads.sh +86 -0
  3. package/assets/scaffold/core/scripts/backfill-company-skill-mirrors.sh +73 -0
  4. package/assets/scaffold/core/scripts/backfill-workspace-mirror.sh +51 -0
  5. package/assets/scaffold/core/scripts/codex-skill-bridge.sh +550 -0
  6. package/assets/scaffold/core/scripts/detect-stale-core-policy-mirror.sh +185 -0
  7. package/assets/scaffold/core/scripts/hq-status-summary.sh +187 -0
  8. package/assets/scaffold/core/scripts/ontology-readme-drift.sh +264 -0
  9. package/assets/scaffold/core/scripts/qmd-reindex-after-sync.sh +93 -0
  10. package/assets/scaffold/core/scripts/rebuild-all-indexes.sh +69 -0
  11. package/assets/scaffold/core/scripts/rebuild-companies-index.sh +206 -0
  12. package/assets/scaffold/core/scripts/rebuild-company-knowledge-index.sh +127 -0
  13. package/assets/scaffold/core/scripts/rebuild-orchestrator-index.sh +86 -0
  14. package/assets/scaffold/core/scripts/rebuild-projects-index.sh +105 -0
  15. package/assets/scaffold/core/scripts/rebuild-public-knowledge-index.sh +83 -0
  16. package/assets/scaffold/core/scripts/rebuild-reports-index.sh +114 -0
  17. package/assets/scaffold/core/scripts/rebuild-social-drafts-index.sh +135 -0
  18. package/assets/scaffold/core/scripts/rebuild-threads-index.sh +82 -0
  19. package/assets/scaffold/core/scripts/rebuild-workers-index.sh +97 -0
  20. package/assets/scaffold/core/scripts/resize-screenshot.sh +47 -0
  21. package/assets/scaffold/core/scripts/token-usage-report.sh +254 -0
  22. package/assets/scaffold/core/scripts/worktree.sh +136 -0
  23. package/dist/commands/agents.js +28 -12
  24. package/dist/commands/api-keys.js +100 -20
  25. package/dist/commands/core.d.ts +90 -0
  26. package/dist/commands/core.js +279 -0
  27. package/dist/commands/files-browse.d.ts +21 -0
  28. package/dist/commands/files-browse.js +145 -24
  29. package/dist/commands/files.js +33 -7
  30. package/dist/commands/meetings.js +16 -9
  31. package/dist/commands/members.js +16 -5
  32. package/dist/commands/pack-install.d.ts +14 -0
  33. package/dist/commands/pack-install.js +42 -2
  34. package/dist/commands/pkg-install.js +39 -2
  35. package/dist/commands/run.js +6 -2
  36. package/dist/commands/secrets.js +120 -65
  37. package/dist/main.js +6 -0
  38. package/dist/utils/api-key-command-gate.d.ts +45 -0
  39. package/dist/utils/api-key-command-gate.js +51 -0
  40. package/dist/utils/hq-roots.d.ts +91 -0
  41. package/dist/utils/hq-roots.js +212 -0
  42. package/dist/utils/resolve-vault-credential.d.ts +63 -3
  43. package/dist/utils/resolve-vault-credential.js +152 -6
  44. package/dist/utils/run-bundled-script.d.ts +66 -0
  45. package/dist/utils/run-bundled-script.js +80 -0
  46. package/dist/utils/vault-api.d.ts +21 -0
  47. package/dist/utils/vault-api.js +74 -3
  48. package/package.json +7 -1
  49. package/.claude/policies/hq-cli-interactive-reads-use-sts-vend.md +0 -49
  50. package/.github/workflows/ci.yml +0 -46
  51. package/.github/workflows/publish.yml +0 -36
  52. package/.github/workflows/scripts/smoke-test-pkg.sh +0 -97
  53. package/e2e/cli.test.ts +0 -128
  54. package/eslint.config.js +0 -37
  55. package/pnpm-workspace.yaml +0 -4
  56. package/scripts/chmod-bins.mjs +0 -49
  57. package/scripts/generate-dsn.mjs +0 -26
  58. package/scripts/generate-scan-packages-table.mjs +0 -113
  59. package/scripts/smoke-sources-signals.sh +0 -103
  60. package/src/bin/hq-auth-refresh.ts +0 -43
  61. package/src/cli-version.ts +0 -13
  62. package/src/commands/__fixtures__/make-tar.ts +0 -126
  63. package/src/commands/_patterns.ts +0 -10
  64. package/src/commands/add.ts +0 -75
  65. package/src/commands/agents.test.ts +0 -527
  66. package/src/commands/agents.ts +0 -923
  67. package/src/commands/api-keys.test.ts +0 -291
  68. package/src/commands/api-keys.ts +0 -382
  69. package/src/commands/artifact-verify.test.ts +0 -177
  70. package/src/commands/auth.test.ts +0 -82
  71. package/src/commands/auth.ts +0 -213
  72. package/src/commands/billing.test.ts +0 -158
  73. package/src/commands/billing.ts +0 -146
  74. package/src/commands/channels.test.ts +0 -74
  75. package/src/commands/channels.ts +0 -91
  76. package/src/commands/cloud-demote.test.ts +0 -401
  77. package/src/commands/cloud-demote.ts +0 -278
  78. package/src/commands/cloud-provision.test.ts +0 -1117
  79. package/src/commands/cloud-provision.ts +0 -942
  80. package/src/commands/cloud.pull-all.test.ts +0 -711
  81. package/src/commands/cloud.pull-per-company.test.ts +0 -188
  82. package/src/commands/cloud.push-all.test.ts +0 -485
  83. package/src/commands/cloud.scope-excluded-warning.test.ts +0 -22
  84. package/src/commands/cloud.selectors.test.ts +0 -208
  85. package/src/commands/cloud.status.test.ts +0 -160
  86. package/src/commands/cloud.test.ts +0 -41
  87. package/src/commands/cloud.ts +0 -2130
  88. package/src/commands/company.test.ts +0 -202
  89. package/src/commands/company.ts +0 -148
  90. package/src/commands/creators.test.ts +0 -60
  91. package/src/commands/creators.ts +0 -117
  92. package/src/commands/crm.test.ts +0 -179
  93. package/src/commands/crm.ts +0 -236
  94. package/src/commands/db-migrate.ts +0 -55
  95. package/src/commands/db-provision.ts +0 -114
  96. package/src/commands/db-sql.ts +0 -124
  97. package/src/commands/db-status.ts +0 -108
  98. package/src/commands/db.ts +0 -26
  99. package/src/commands/dm.test.ts +0 -748
  100. package/src/commands/dm.ts +0 -1226
  101. package/src/commands/feedback.test.ts +0 -413
  102. package/src/commands/feedback.ts +0 -169
  103. package/src/commands/files-browse.test.ts +0 -1249
  104. package/src/commands/files-browse.ts +0 -1506
  105. package/src/commands/files-delete.test.ts +0 -484
  106. package/src/commands/files-recovery.test.ts +0 -370
  107. package/src/commands/files.test.ts +0 -722
  108. package/src/commands/files.ts +0 -1446
  109. package/src/commands/group-grants.test.ts +0 -330
  110. package/src/commands/group-grants.ts +0 -455
  111. package/src/commands/groups.test.ts +0 -44
  112. package/src/commands/groups.ts +0 -368
  113. package/src/commands/integrations.test.ts +0 -633
  114. package/src/commands/integrations.ts +0 -507
  115. package/src/commands/list.ts +0 -85
  116. package/src/commands/login.ts +0 -52
  117. package/src/commands/logout.ts +0 -30
  118. package/src/commands/marketplace-install.test.ts +0 -543
  119. package/src/commands/marketplace-security.test.ts +0 -646
  120. package/src/commands/mcp-registration.test.ts +0 -2787
  121. package/src/commands/mcp-registration.ts +0 -2612
  122. package/src/commands/mcp-status.test.ts +0 -483
  123. package/src/commands/mcp-status.ts +0 -575
  124. package/src/commands/mcp-status.us011.test.ts +0 -243
  125. package/src/commands/meetings.test.ts +0 -522
  126. package/src/commands/meetings.ts +0 -789
  127. package/src/commands/members.test.ts +0 -1474
  128. package/src/commands/members.ts +0 -1181
  129. package/src/commands/onboard-identity-guard.test.ts +0 -99
  130. package/src/commands/onboard-identity-guard.ts +0 -103
  131. package/src/commands/onboard-join.test.ts +0 -96
  132. package/src/commands/onboard-join.ts +0 -101
  133. package/src/commands/onboard-warning.test.ts +0 -26
  134. package/src/commands/onboard-warning.ts +0 -12
  135. package/src/commands/onboard.ts +0 -267
  136. package/src/commands/outposts-heartbeat.test.ts +0 -299
  137. package/src/commands/outposts-heartbeat.ts +0 -310
  138. package/src/commands/outposts-replica-sync.test.ts +0 -272
  139. package/src/commands/outposts-self-deploy.test.ts +0 -243
  140. package/src/commands/outposts.test.ts +0 -1039
  141. package/src/commands/outposts.ts +0 -1935
  142. package/src/commands/pack-install-secret-authorization.test.ts +0 -115
  143. package/src/commands/pack-install.test.ts +0 -1469
  144. package/src/commands/pack-install.ts +0 -2256
  145. package/src/commands/pack-update-cache.test.ts +0 -149
  146. package/src/commands/packs-update-api-key.test.ts +0 -105
  147. package/src/commands/packs.test.ts +0 -88
  148. package/src/commands/packs.ts +0 -622
  149. package/src/commands/people.test.ts +0 -807
  150. package/src/commands/people.ts +0 -422
  151. package/src/commands/pkg-install.dispatch.test.ts +0 -103
  152. package/src/commands/pkg-install.ts +0 -221
  153. package/src/commands/pkg-list.test.ts +0 -79
  154. package/src/commands/pkg-list.ts +0 -13
  155. package/src/commands/pkg-remove.ts +0 -71
  156. package/src/commands/pkg-update.ts +0 -189
  157. package/src/commands/publish.test.ts +0 -650
  158. package/src/commands/publish.ts +0 -580
  159. package/src/commands/reindex.test.ts +0 -259
  160. package/src/commands/reindex.ts +0 -274
  161. package/src/commands/rescue.test.ts +0 -39
  162. package/src/commands/rescue.ts +0 -210
  163. package/src/commands/run.env-local.test.ts +0 -88
  164. package/src/commands/run.test.ts +0 -45
  165. package/src/commands/run.ts +0 -190
  166. package/src/commands/safe-extract.test.ts +0 -459
  167. package/src/commands/safe-extract.ts +0 -444
  168. package/src/commands/secrets-scope.test.ts +0 -56
  169. package/src/commands/secrets-scope.ts +0 -32
  170. package/src/commands/secrets.parse-destination.test.ts +0 -38
  171. package/src/commands/secrets.test.ts +0 -2050
  172. package/src/commands/secrets.ts +0 -2249
  173. package/src/commands/signals.ts +0 -351
  174. package/src/commands/skill.test.ts +0 -770
  175. package/src/commands/skill.ts +0 -796
  176. package/src/commands/sources.ts +0 -362
  177. package/src/commands/sync-mode.test.ts +0 -366
  178. package/src/commands/sync-mode.ts +0 -387
  179. package/src/commands/sync-narrow.test.ts +0 -633
  180. package/src/commands/sync-narrow.ts +0 -555
  181. package/src/commands/sync.ts +0 -165
  182. package/src/commands/team-sync.ts +0 -628
  183. package/src/commands/update.ts +0 -71
  184. package/src/commands/whoami.ts +0 -83
  185. package/src/commands/workers.test.ts +0 -158
  186. package/src/commands/workers.ts +0 -301
  187. package/src/index.test.ts +0 -32
  188. package/src/index.ts +0 -20
  189. package/src/lib/db/control-plane.test.ts +0 -59
  190. package/src/lib/db/control-plane.ts +0 -113
  191. package/src/lib/db/local.test.ts +0 -81
  192. package/src/lib/db/local.ts +0 -148
  193. package/src/lib/db/migrate.test.ts +0 -133
  194. package/src/lib/db/migrate.ts +0 -137
  195. package/src/lib/db/paths.test.ts +0 -112
  196. package/src/lib/db/paths.ts +0 -128
  197. package/src/lib/db/remote-engine.test.ts +0 -44
  198. package/src/lib/db/remote-engine.ts +0 -148
  199. package/src/lib/db/remote-sql.test.ts +0 -32
  200. package/src/lib/db/remote-sql.ts +0 -62
  201. package/src/lib/db/sql.test.ts +0 -106
  202. package/src/lib/db/sql.ts +0 -192
  203. package/src/lib/hq-cloud-dep.smoke.test.ts +0 -75
  204. package/src/lib/local-tree-diff.test.ts +0 -301
  205. package/src/lib/local-tree-diff.ts +0 -362
  206. package/src/lib/narrow-hint-banner.test.ts +0 -235
  207. package/src/lib/narrow-hint-banner.ts +0 -212
  208. package/src/lib/plan-limit-nag.test.ts +0 -317
  209. package/src/lib/plan-limit-nag.ts +0 -264
  210. package/src/main.ts +0 -371
  211. package/src/node-preflight.test.ts +0 -60
  212. package/src/node-preflight.ts +0 -67
  213. package/src/outpost/session-heartbeat-bounds.test.ts +0 -195
  214. package/src/outpost/session-heartbeat-guard.test.ts +0 -105
  215. package/src/outpost/session-heartbeat-publisher.test.ts +0 -178
  216. package/src/outpost/session-heartbeat-publisher.ts +0 -186
  217. package/src/outpost/session-heartbeat-retain-guard.test.ts +0 -126
  218. package/src/outpost/session-heartbeat.test.ts +0 -459
  219. package/src/outpost/session-heartbeat.ts +0 -877
  220. package/src/packaging.test.ts +0 -109
  221. package/src/run/__fixtures__/discover-schemas/example.env.schema +0 -4
  222. package/src/run/discover-schemas.test.ts +0 -153
  223. package/src/run/discover-schemas.ts +0 -79
  224. package/src/run/hq-plugin.test.ts +0 -371
  225. package/src/run/hq-plugin.ts +0 -277
  226. package/src/run/varlock-shape.test.ts +0 -57
  227. package/src/schemas/hq-package.schema.json +0 -155
  228. package/src/sentry-before-send.test.ts +0 -359
  229. package/src/sentry-before-send.ts +0 -221
  230. package/src/sentry-epipe.test.ts +0 -37
  231. package/src/sentry-release.test.ts +0 -54
  232. package/src/sentry.test.ts +0 -24
  233. package/src/sentry.ts +0 -54
  234. package/src/strategies/link.ts +0 -62
  235. package/src/strategies/merge.ts +0 -142
  236. package/src/types.ts +0 -146
  237. package/src/utils/__fixtures__/scan-packages.generated-block.sh +0 -23
  238. package/src/utils/auth-error.test.ts +0 -40
  239. package/src/utils/auth-error.ts +0 -42
  240. package/src/utils/billing-gate.test.ts +0 -95
  241. package/src/utils/billing-gate.ts +0 -228
  242. package/src/utils/breadcrumb-buffer.ts +0 -18
  243. package/src/utils/cli-telemetry.test.ts +0 -153
  244. package/src/utils/cli-telemetry.ts +0 -61
  245. package/src/utils/cognito-session.machine.test.ts +0 -109
  246. package/src/utils/cognito-session.refresh.test.ts +0 -84
  247. package/src/utils/cognito-session.test.ts +0 -328
  248. package/src/utils/cognito-session.ts +0 -448
  249. package/src/utils/company-selection-error.test.ts +0 -42
  250. package/src/utils/company-selection-error.ts +0 -45
  251. package/src/utils/contribution-table.ts +0 -83
  252. package/src/utils/environmental-error.test.ts +0 -45
  253. package/src/utils/environmental-error.ts +0 -39
  254. package/src/utils/epipe.test.ts +0 -28
  255. package/src/utils/epipe.ts +0 -29
  256. package/src/utils/expected-cli-error.test.ts +0 -28
  257. package/src/utils/expected-cli-error.ts +0 -39
  258. package/src/utils/feedback-diagnostics.test.ts +0 -183
  259. package/src/utils/feedback-diagnostics.ts +0 -123
  260. package/src/utils/feedback-screenshots.test.ts +0 -134
  261. package/src/utils/feedback-screenshots.ts +0 -124
  262. package/src/utils/feedback-versions.test.ts +0 -98
  263. package/src/utils/feedback-versions.ts +0 -68
  264. package/src/utils/git.ts +0 -74
  265. package/src/utils/integrity.ts +0 -59
  266. package/src/utils/intercepted-process-exit.test.ts +0 -37
  267. package/src/utils/intercepted-process-exit.ts +0 -36
  268. package/src/utils/login-provider.test.ts +0 -42
  269. package/src/utils/login-provider.ts +0 -30
  270. package/src/utils/manifest.test.ts +0 -167
  271. package/src/utils/manifest.ts +0 -131
  272. package/src/utils/pack-contributions.test.ts +0 -603
  273. package/src/utils/pack-contributions.ts +0 -450
  274. package/src/utils/people.ts +0 -215
  275. package/src/utils/registry-client.ts +0 -204
  276. package/src/utils/registry.ts +0 -90
  277. package/src/utils/resolve-vault-credential.test.ts +0 -69
  278. package/src/utils/resolve-vault-credential.ts +0 -60
  279. package/src/utils/sandbox-runner-client.test.ts +0 -279
  280. package/src/utils/sandbox-runner-client.ts +0 -260
  281. package/src/utils/secrets-cache.ts +0 -183
  282. package/src/utils/sentry-identity.ts +0 -45
  283. package/src/utils/settle-with-timeout.test.ts +0 -21
  284. package/src/utils/settle-with-timeout.ts +0 -22
  285. package/src/utils/user-yaml-error.test.ts +0 -24
  286. package/src/utils/user-yaml-error.ts +0 -30
  287. package/src/utils/vault-api.test.ts +0 -536
  288. package/src/utils/vault-api.ts +0 -382
  289. package/src/utils/version-check.test.ts +0 -176
  290. package/src/utils/version-check.ts +0 -155
  291. package/src/utils/version-gate.test.ts +0 -1024
  292. package/src/utils/version-gate.ts +0 -702
  293. package/test/commands/db-tenant-isolation.test.ts +0 -94
  294. package/test/commands/db.test.ts +0 -85
  295. package/test/commands/signals.test.ts +0 -207
  296. package/test/commands/sources.test.ts +0 -231
  297. package/test/e2e/smoke-install-mcp.sh +0 -113
  298. package/test/fixtures/hq-pack-smoke-mcp/mcp/smoke-http.json +0 -1
  299. package/test/fixtures/hq-pack-smoke-mcp/package.yaml +0 -11
  300. package/test/fixtures/signals/action_item/sample.md +0 -16
  301. package/test/fixtures/signals/summary/sample.md +0 -12
  302. package/test/fixtures/sources/meetings/sample.md +0 -25
  303. package/test/helpers/cli-runner.ts +0 -150
  304. package/test/helpers/s3-list-mock.ts +0 -79
  305. package/test/helpers/vault-service-mock.ts +0 -295
  306. package/test/sources-signals/smoke.test.ts +0 -228
  307. package/tsconfig.json +0 -20
  308. package/vitest.config.ts +0 -11
  309. package/vitest.e2e.config.ts +0 -7
@@ -1,124 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { vaultApiFetch } from "./vault-api.js";
4
-
5
- export const MAX_SCREENSHOTS = 5;
6
- // Per-image ceiling. Screenshots are PNG/JPEG captures; 10 MB is generous.
7
- export const MAX_SCREENSHOT_BYTES = 10 * 1024 * 1024;
8
-
9
- // Extension → content type. Must stay in sync with the server's allowed set
10
- // (hq-pro feedback-screenshots.ts ALLOWED_CONTENT_TYPES).
11
- const EXT_CONTENT_TYPE: Record<string, string> = {
12
- ".png": "image/png",
13
- ".jpg": "image/jpeg",
14
- ".jpeg": "image/jpeg",
15
- ".webp": "image/webp",
16
- ".gif": "image/gif",
17
- };
18
-
19
- export function contentTypeForPath(filePath: string): string {
20
- const ext = path.extname(filePath).toLowerCase();
21
- const contentType = EXT_CONTENT_TYPE[ext];
22
- if (!contentType) {
23
- throw new Error(
24
- `unsupported screenshot type: ${filePath} (allowed: ${Object.keys(EXT_CONTENT_TYPE).join(", ")})`,
25
- );
26
- }
27
- return contentType;
28
- }
29
-
30
- export interface ScreenshotInput {
31
- path: string;
32
- contentType: string;
33
- bytes: Buffer;
34
- }
35
-
36
- /** Validate + read the given screenshot paths (count, type, existence, size). */
37
- export function loadScreenshots(paths: string[]): ScreenshotInput[] {
38
- if (paths.length > MAX_SCREENSHOTS) {
39
- throw new Error(
40
- `at most ${MAX_SCREENSHOTS} screenshots are allowed (got ${paths.length})`,
41
- );
42
- }
43
- return paths.map((filePath) => {
44
- const contentType = contentTypeForPath(filePath);
45
- let bytes: Buffer;
46
- try {
47
- bytes = fs.readFileSync(filePath);
48
- } catch {
49
- throw new Error(`cannot read screenshot: ${filePath}`);
50
- }
51
- if (bytes.byteLength === 0) {
52
- throw new Error(`screenshot is empty: ${filePath}`);
53
- }
54
- if (bytes.byteLength > MAX_SCREENSHOT_BYTES) {
55
- throw new Error(
56
- `screenshot too large: ${filePath} (${bytes.byteLength} bytes, max ${MAX_SCREENSHOT_BYTES})`,
57
- );
58
- }
59
- return { path: filePath, contentType, bytes };
60
- });
61
- }
62
-
63
- interface PresignResponse {
64
- screenshots: Array<{ key: string; url: string; contentType: string }>;
65
- }
66
-
67
- /**
68
- * Validate the screenshot paths, request presigned PUT URLs from the feedback
69
- * endpoint, upload each image direct to S3, and return the object keys to
70
- * attach to the feedback submission. Returns [] for no screenshots.
71
- *
72
- * `fetchImpl` is injectable for tests; defaults to the global fetch.
73
- */
74
- export async function uploadScreenshots(opts: {
75
- paths: string[];
76
- token: string;
77
- fetchImpl?: typeof fetch;
78
- }): Promise<string[]> {
79
- if (opts.paths.length === 0) return [];
80
- const inputs = loadScreenshots(opts.paths);
81
-
82
- const res = await vaultApiFetch({
83
- token: opts.token,
84
- path: "/v1/feedback/screenshots/presign",
85
- method: "POST",
86
- body: { contentTypes: inputs.map((i) => i.contentType) },
87
- });
88
- if (!res.ok) {
89
- const data = await res.json().catch(() => ({}));
90
- const msg =
91
- data &&
92
- typeof data === "object" &&
93
- typeof (data as { error?: unknown }).error === "string"
94
- ? (data as { error: string }).error
95
- : res.statusText;
96
- throw new Error(`Failed to presign screenshots: ${msg}`);
97
- }
98
- const parsed = (await res.json()) as PresignResponse;
99
- if (
100
- !parsed ||
101
- !Array.isArray(parsed.screenshots) ||
102
- parsed.screenshots.length !== inputs.length
103
- ) {
104
- throw new Error("Presign response did not match the requested screenshots");
105
- }
106
-
107
- const doFetch = opts.fetchImpl ?? fetch;
108
- const keys: string[] = [];
109
- for (let i = 0; i < inputs.length; i++) {
110
- const slot = parsed.screenshots[i]!;
111
- const put = await doFetch(slot.url, {
112
- method: "PUT",
113
- headers: { "Content-Type": slot.contentType },
114
- body: inputs[i]!.bytes,
115
- });
116
- if (!put.ok) {
117
- throw new Error(
118
- `Failed to upload screenshot ${inputs[i]!.path}: HTTP ${put.status}`,
119
- );
120
- }
121
- keys.push(slot.key);
122
- }
123
- return keys;
124
- }
@@ -1,98 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import * as fs from "node:fs";
3
- import * as os from "node:os";
4
- import * as path from "node:path";
5
-
6
- vi.mock("./manifest.js", () => ({
7
- findHqRoot: vi.fn(() => "/fake/hq"),
8
- }));
9
- vi.mock("./pack-contributions.js", () => ({
10
- readHqVersion: vi.fn(() => "15.0.20"),
11
- }));
12
-
13
- import { findHqRoot } from "./manifest.js";
14
- import { readHqVersion } from "./pack-contributions.js";
15
- import { CLI_VERSION } from "../cli-version.js";
16
- import {
17
- collectVersions,
18
- readCoreVersion,
19
- readSyncVersion,
20
- } from "./feedback-versions.js";
21
-
22
- describe("readSyncVersion", () => {
23
- let tmpHome: string;
24
-
25
- beforeEach(() => {
26
- tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "hqsync-"));
27
- fs.mkdirSync(path.join(tmpHome, ".hq"), { recursive: true });
28
- });
29
- afterEach(() => {
30
- fs.rmSync(tmpHome, { recursive: true, force: true });
31
- });
32
-
33
- function writeMarker(contents: string): void {
34
- fs.writeFileSync(path.join(tmpHome, ".hq", "sync-version.json"), contents);
35
- }
36
-
37
- it("reads the version the hq-sync app records", () => {
38
- writeMarker(JSON.stringify({ version: "0.8.22-beta.1", updatedAt: "x" }));
39
- expect(readSyncVersion(tmpHome)).toBe("0.8.22-beta.1");
40
- });
41
-
42
- it("returns null when the marker file is absent (hq-sync not installed)", () => {
43
- expect(readSyncVersion(tmpHome)).toBeNull();
44
- });
45
-
46
- it("returns null on malformed JSON rather than throwing", () => {
47
- writeMarker("{ not json");
48
- expect(readSyncVersion(tmpHome)).toBeNull();
49
- });
50
-
51
- it("returns null when version is missing or not a non-empty string", () => {
52
- writeMarker(JSON.stringify({ updatedAt: "x" }));
53
- expect(readSyncVersion(tmpHome)).toBeNull();
54
- writeMarker(JSON.stringify({ version: "" }));
55
- expect(readSyncVersion(tmpHome)).toBeNull();
56
- writeMarker(JSON.stringify({ version: 123 }));
57
- expect(readSyncVersion(tmpHome)).toBeNull();
58
- });
59
- });
60
-
61
- describe("readCoreVersion", () => {
62
- beforeEach(() => {
63
- vi.mocked(findHqRoot).mockReset().mockReturnValue("/fake/hq");
64
- vi.mocked(readHqVersion).mockReset().mockReturnValue("15.0.20");
65
- });
66
-
67
- it("reads hqVersion from the resolved HQ root", () => {
68
- expect(readCoreVersion()).toBe("15.0.20");
69
- expect(readHqVersion).toHaveBeenCalledWith("/fake/hq");
70
- });
71
-
72
- it("returns null (never throws) when resolution fails", () => {
73
- vi.mocked(findHqRoot).mockImplementation(() => {
74
- throw new Error("no hq root");
75
- });
76
- expect(readCoreVersion()).toBeNull();
77
- });
78
-
79
- it("returns null when core.yaml has no hqVersion", () => {
80
- vi.mocked(readHqVersion).mockReturnValue(null);
81
- expect(readCoreVersion()).toBeNull();
82
- });
83
- });
84
-
85
- describe("collectVersions", () => {
86
- beforeEach(() => {
87
- vi.mocked(findHqRoot).mockReset().mockReturnValue("/fake/hq");
88
- vi.mocked(readHqVersion).mockReset().mockReturnValue("15.0.20");
89
- });
90
-
91
- it("always reports the cli version and best-effort core/sync", () => {
92
- const versions = collectVersions();
93
- expect(versions.cli).toBe(CLI_VERSION);
94
- expect(versions.core).toBe("15.0.20");
95
- // sync reads the real homedir marker, which won't exist in CI → null.
96
- expect(["string", "object"]).toContain(typeof versions.sync); // string | null
97
- });
98
- });
@@ -1,68 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as os from "node:os";
3
- import * as path from "node:path";
4
- import { CLI_VERSION } from "../cli-version.js";
5
- import { findHqRoot } from "./manifest.js";
6
- import { readHqVersion } from "./pack-contributions.js";
7
-
8
- /**
9
- * The three HQ component versions captured from the submitter's environment
10
- * and attached to a feedback submission so triage can see exactly which
11
- * versions a report came from.
12
- *
13
- * - `cli` — this hq-cli build (always known).
14
- * - `core` — the HQ scaffold version from `core/core.yaml` (`hqVersion`);
15
- * null when the command runs outside an HQ tree.
16
- * - `sync` — the installed hq-sync menubar app version, which the app
17
- * records at `~/.hq/sync-version.json` on startup; null when
18
- * hq-sync is not installed (e.g. CLI-only / CI environments).
19
- */
20
- export interface VersionInfo {
21
- cli: string;
22
- core: string | null;
23
- sync: string | null;
24
- }
25
-
26
- /**
27
- * Best-effort read of the hq-core scaffold version (`core/core.yaml`
28
- * `hqVersion`). Resolves the HQ root from the working directory; returns
29
- * null when no HQ root / core.yaml is found rather than throwing — version
30
- * capture must never break a feedback submission.
31
- */
32
- export function readCoreVersion(): string | null {
33
- try {
34
- return readHqVersion(findHqRoot());
35
- } catch {
36
- return null;
37
- }
38
- }
39
-
40
- /**
41
- * Best-effort read of the hq-sync menubar app version. The app writes
42
- * `{ version, updatedAt }` to `~/.hq/sync-version.json` on startup; the CLI
43
- * reads it here. Returns null when the file is absent or malformed (hq-sync
44
- * not installed, or an older build that predates the marker).
45
- */
46
- export function readSyncVersion(homeDir: string = os.homedir()): string | null {
47
- try {
48
- const raw = fs.readFileSync(
49
- path.join(homeDir, ".hq", "sync-version.json"),
50
- "utf-8",
51
- );
52
- const parsed = JSON.parse(raw) as { version?: unknown };
53
- return typeof parsed.version === "string" && parsed.version.length > 0
54
- ? parsed.version
55
- : null;
56
- } catch {
57
- return null;
58
- }
59
- }
60
-
61
- /** Collect all three component versions, each independently best-effort. */
62
- export function collectVersions(): VersionInfo {
63
- return {
64
- cli: CLI_VERSION,
65
- core: readCoreVersion(),
66
- sync: readSyncVersion(),
67
- };
68
- }
package/src/utils/git.ts DELETED
@@ -1,74 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- import simpleGit, { SimpleGit } from 'simple-git';
4
-
5
- export async function cloneRepo(repoUrl: string, targetDir: string, branch?: string): Promise<void> {
6
- const git = simpleGit();
7
- const options = branch ? ['--branch', branch] : [];
8
- await git.clone(repoUrl, targetDir, options);
9
- }
10
-
11
- export async function fetchRepo(repoDir: string): Promise<void> {
12
- const git = simpleGit(repoDir);
13
- await git.fetch(['--all']);
14
- }
15
-
16
- export async function pullRepo(repoDir: string): Promise<void> {
17
- const git = simpleGit(repoDir);
18
- await git.pull();
19
- }
20
-
21
- export async function getCurrentCommit(repoDir: string): Promise<string> {
22
- const git = simpleGit(repoDir);
23
- const log = await git.log({ maxCount: 1 });
24
- return log.latest?.hash ?? '';
25
- }
26
-
27
- export async function checkoutCommit(repoDir: string, commitSha: string): Promise<void> {
28
- const git = simpleGit(repoDir);
29
- await git.checkout(commitSha);
30
- }
31
-
32
- export async function isRepo(dir: string): Promise<boolean> {
33
- if (!fs.existsSync(dir)) return false;
34
- try {
35
- const git = simpleGit(dir);
36
- return await git.checkIsRepo();
37
- } catch {
38
- return false;
39
- }
40
- }
41
-
42
- export async function getRemoteUrl(repoDir: string): Promise<string | null> {
43
- try {
44
- const git = simpleGit(repoDir);
45
- const remotes = await git.getRemotes(true);
46
- const origin = remotes.find(r => r.name === 'origin');
47
- return origin?.refs?.fetch ?? null;
48
- } catch {
49
- return null;
50
- }
51
- }
52
-
53
- export async function isBehindRemote(repoDir: string): Promise<{ behind: boolean; commits: number }> {
54
- try {
55
- const git = simpleGit(repoDir);
56
- await git.fetch();
57
- const status = await git.status();
58
- return { behind: status.behind > 0, commits: status.behind };
59
- } catch {
60
- return { behind: false, commits: 0 };
61
- }
62
- }
63
-
64
- export function ensureGitignore(hqRoot: string, entry: string): void {
65
- const gitignorePath = path.join(hqRoot, '.gitignore');
66
- let content = '';
67
- if (fs.existsSync(gitignorePath)) {
68
- content = fs.readFileSync(gitignorePath, 'utf-8');
69
- }
70
- if (!content.includes(entry)) {
71
- content = content.trimEnd() + '\n' + entry + '\n';
72
- fs.writeFileSync(gitignorePath, content);
73
- }
74
- }
@@ -1,59 +0,0 @@
1
- /**
2
- * Integrity verification — SHA256 hash and RSA signature checks (US-005)
3
- */
4
-
5
- import * as crypto from 'crypto';
6
- import * as fs from 'fs';
7
- import * as path from 'path';
8
- import { resolveDefaultHqRoot } from './cognito-session.js';
9
-
10
- /**
11
- * Verify a file's SHA256 hash matches the expected value.
12
- */
13
- export async function computeSha256(filePath: string): Promise<string> {
14
- return new Promise((resolve, reject) => {
15
- const hash = crypto.createHash('sha256');
16
- const stream = fs.createReadStream(filePath);
17
-
18
- stream.on('data', (chunk) => hash.update(chunk));
19
- stream.on('end', () => resolve(hash.digest('hex')));
20
- stream.on('error', reject);
21
- });
22
- }
23
-
24
- /**
25
- * Verify a file's SHA256 hash matches the expected value.
26
- */
27
- export async function verifySha256(
28
- filePath: string,
29
- expectedHash: string
30
- ): Promise<boolean> {
31
- const computed = await computeSha256(filePath);
32
- return computed === expectedHash.toLowerCase();
33
- }
34
-
35
- /**
36
- * Verify an RSA signature of a SHA256 hash using the registry public key.
37
- * The public key is expected at packages/.keys/registry-public.pem.
38
- * Returns false (does not throw) if the key file is missing.
39
- */
40
- export function verifyRsaSignature(
41
- sha256Hash: string,
42
- signature: string,
43
- publicKeyPath?: string
44
- ): boolean {
45
- const keyPath =
46
- publicKeyPath ??
47
- path.resolve(resolveDefaultHqRoot({ onMissing: 'throw' }), 'packages', '.keys', 'registry-public.pem');
48
-
49
- if (!fs.existsSync(keyPath)) {
50
- return false;
51
- }
52
-
53
- const publicKey = fs.readFileSync(keyPath, 'utf-8');
54
- const verifier = crypto.createVerify('SHA256');
55
- verifier.update(sha256Hash);
56
- verifier.end();
57
-
58
- return verifier.verify(publicKey, Buffer.from(signature, 'base64'));
59
- }
@@ -1,37 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { isInterceptedProcessExit } from "./intercepted-process-exit.js";
3
-
4
- describe("isInterceptedProcessExit", () => {
5
- it("matches the fuzz harness's intercepted-process.exit marker error", () => {
6
- expect(isInterceptedProcessExit(new Error("fuzz-intercepted-process-exit"))).toBe(true);
7
- });
8
-
9
- it("matches when the marker is wrapped in a longer message", () => {
10
- expect(
11
- isInterceptedProcessExit(new Error("audit: fuzz-intercepted-process-exit code=1")),
12
- ).toBe(true);
13
- });
14
-
15
- it("matches a plain object carrying the marker message", () => {
16
- expect(isInterceptedProcessExit({ message: "fuzz-intercepted-process-exit" })).toBe(true);
17
- });
18
-
19
- it("matches a bare string carrying the marker", () => {
20
- expect(isInterceptedProcessExit("fuzz-intercepted-process-exit")).toBe(true);
21
- });
22
-
23
- it("does NOT match genuine errors (so real faults still report)", () => {
24
- expect(isInterceptedProcessExit(new Error("boom"))).toBe(false);
25
- expect(isInterceptedProcessExit(new Error("ENOENT: no such file or directory"))).toBe(false);
26
- expect(isInterceptedProcessExit(Object.assign(new Error("nope"), { code: "EACCES" }))).toBe(
27
- false,
28
- );
29
- });
30
-
31
- it("does NOT match non-error values", () => {
32
- expect(isInterceptedProcessExit(null)).toBe(false);
33
- expect(isInterceptedProcessExit(undefined)).toBe(false);
34
- expect(isInterceptedProcessExit(42)).toBe(false);
35
- expect(isInterceptedProcessExit({})).toBe(false);
36
- });
37
- });
@@ -1,36 +0,0 @@
1
- // src/utils/intercepted-process-exit.ts
2
- //
3
- // Classify the "intercepted process.exit" synthetic error (HQ-CLI-3).
4
- //
5
- // Security/audit FUZZ harnesses (e.g. `/opt/audit/fuzz-exports.js`) monkey-patch
6
- // `process.exit` to THROW a marker instead of actually terminating, so the
7
- // fuzzer can keep exercising a binary's entrypoints. When such a harness drives
8
- // `hq` (e.g. `hq <unknown-command>`), commander legitimately calls
9
- // `process.exit(1)` — and the patched exit turns that normal CLI control-flow
10
- // into a thrown `Error: fuzz-intercepted-process-exit` that propagates out of
11
- // `program.parseAsync()` into the CLI's top-level catch, which then shipped it to
12
- // Sentry as a fatal.
13
- //
14
- // This is a test-harness artifact, NOT an hq-cli defect: a real user's
15
- // `process.exit` exits the process and nothing is ever thrown or captured.
16
- // There is no user-facing degradation to fix, so the top-level catch skips
17
- // Sentry capture for it — the genuine "expected, non-actionable" carve-out,
18
- // mirroring the EPIPE (HQ-6B) and environmental-FS (HQ-CLI-2) carve-outs.
19
-
20
- /** The marker a fuzz/audit harness throws in place of a real `process.exit`. */
21
- const INTERCEPTED_PROCESS_EXIT_MARKER = "fuzz-intercepted-process-exit";
22
-
23
- /**
24
- * True when `err` is a fuzz/audit harness's intercepted-`process.exit` marker
25
- * rather than a genuine fault. Callers should SKIP Sentry capture (no defect, no
26
- * user-facing degradation) while preserving the intended exit code.
27
- */
28
- export function isInterceptedProcessExit(err: unknown): boolean {
29
- if (typeof err === "object" && err !== null) {
30
- const message = (err as { message?: unknown }).message;
31
- if (typeof message === "string" && message.includes(INTERCEPTED_PROCESS_EXIT_MARKER)) {
32
- return true;
33
- }
34
- }
35
- return typeof err === "string" && err.includes(INTERCEPTED_PROCESS_EXIT_MARKER);
36
- }
@@ -1,42 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
-
3
- vi.mock("./cognito-session.js", () => ({
4
- DEFAULT_COGNITO: {
5
- region: "us-east-1",
6
- userPoolDomain: "vault-indigo-hq-prod",
7
- clientId: "client-123",
8
- port: 8765,
9
- identityProvider: "Google",
10
- },
11
- }));
12
-
13
- import {
14
- cognitoConfigForLoginProvider,
15
- parseLoginProvider,
16
- } from "./login-provider.js";
17
-
18
- describe("login provider helpers", () => {
19
- it("preserves the default Cognito config when no provider is supplied", () => {
20
- expect(cognitoConfigForLoginProvider(undefined).identityProvider).toBe("Google");
21
- });
22
-
23
- it("maps Microsoft to the Cognito MicrosoftPersonal IdP", () => {
24
- expect(cognitoConfigForLoginProvider("microsoft").identityProvider).toBe(
25
- "MicrosoftPersonal",
26
- );
27
- });
28
-
29
- it("maps picker to no forced IdP", () => {
30
- expect(cognitoConfigForLoginProvider("picker").identityProvider).toBeUndefined();
31
- });
32
-
33
- it("accepts provider values case-insensitively", () => {
34
- expect(parseLoginProvider("Microsoft")).toBe("microsoft");
35
- });
36
-
37
- it("rejects unknown providers", () => {
38
- expect(() => parseLoginProvider("github")).toThrow(
39
- "Provider must be one of: google, microsoft, picker",
40
- );
41
- });
42
- });
@@ -1,30 +0,0 @@
1
- import type { CognitoAuthConfig } from "@indigoai-us/hq-cloud";
2
- import { DEFAULT_COGNITO } from "./cognito-session.js";
3
-
4
- const PROVIDER_TO_COGNITO_IDP = {
5
- google: "Google",
6
- microsoft: "MicrosoftPersonal",
7
- picker: undefined,
8
- } as const;
9
-
10
- export type LoginProvider = keyof typeof PROVIDER_TO_COGNITO_IDP;
11
-
12
- export function parseLoginProvider(value: string | undefined): LoginProvider | undefined {
13
- if (value === undefined) return undefined;
14
- const normalized = value.trim().toLowerCase();
15
- if (normalized in PROVIDER_TO_COGNITO_IDP) {
16
- return normalized as LoginProvider;
17
- }
18
- throw new Error("Provider must be one of: google, microsoft, picker");
19
- }
20
-
21
- export function cognitoConfigForLoginProvider(
22
- value: string | undefined,
23
- ): CognitoAuthConfig {
24
- const provider = parseLoginProvider(value);
25
- if (!provider) return DEFAULT_COGNITO;
26
- return {
27
- ...DEFAULT_COGNITO,
28
- identityProvider: PROVIDER_TO_COGNITO_IDP[provider],
29
- };
30
- }