@indigoai-us/hq-cloud 6.15.0 → 6.15.2

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 (329) hide show
  1. package/dist/bin/sync-mutation.d.ts +16 -0
  2. package/dist/bin/sync-mutation.d.ts.map +1 -0
  3. package/dist/bin/sync-mutation.js +60 -0
  4. package/dist/bin/sync-mutation.js.map +1 -0
  5. package/dist/bin/sync-mutation.test.d.ts +2 -0
  6. package/dist/bin/sync-mutation.test.d.ts.map +1 -0
  7. package/dist/bin/sync-mutation.test.js +165 -0
  8. package/dist/bin/sync-mutation.test.js.map +1 -0
  9. package/dist/bin/sync-runner-company.d.ts +8 -0
  10. package/dist/bin/sync-runner-company.d.ts.map +1 -1
  11. package/dist/bin/sync-runner-company.js +16 -0
  12. package/dist/bin/sync-runner-company.js.map +1 -1
  13. package/dist/bin/sync-runner-company.test.d.ts +2 -0
  14. package/dist/bin/sync-runner-company.test.d.ts.map +1 -0
  15. package/dist/bin/sync-runner-company.test.js +36 -0
  16. package/dist/bin/sync-runner-company.test.js.map +1 -0
  17. package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
  18. package/dist/bin/sync-runner-watch-loop.js +98 -8
  19. package/dist/bin/sync-runner-watch-loop.js.map +1 -1
  20. package/dist/bin/sync-runner.d.ts +17 -0
  21. package/dist/bin/sync-runner.d.ts.map +1 -1
  22. package/dist/bin/sync-runner.js.map +1 -1
  23. package/dist/bin/sync-runner.test.js +109 -0
  24. package/dist/bin/sync-runner.test.js.map +1 -1
  25. package/dist/cli/conflict-recovery.test.d.ts +2 -0
  26. package/dist/cli/conflict-recovery.test.d.ts.map +1 -0
  27. package/dist/cli/conflict-recovery.test.js +201 -0
  28. package/dist/cli/conflict-recovery.test.js.map +1 -0
  29. package/dist/cli/conflict.d.ts +60 -0
  30. package/dist/cli/conflict.d.ts.map +1 -1
  31. package/dist/cli/conflict.js +333 -0
  32. package/dist/cli/conflict.js.map +1 -1
  33. package/dist/cli/sync.d.ts +35 -0
  34. package/dist/cli/sync.d.ts.map +1 -1
  35. package/dist/cli/sync.js +100 -0
  36. package/dist/cli/sync.js.map +1 -1
  37. package/dist/cli/sync.test.js +85 -1
  38. package/dist/cli/sync.test.js.map +1 -1
  39. package/dist/index.d.ts +2 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +1 -0
  42. package/dist/index.js.map +1 -1
  43. package/dist/skill-telemetry.d.ts +6 -0
  44. package/dist/skill-telemetry.d.ts.map +1 -1
  45. package/dist/skill-telemetry.js +14 -2
  46. package/dist/skill-telemetry.js.map +1 -1
  47. package/dist/skill-telemetry.test.js +79 -0
  48. package/dist/skill-telemetry.test.js.map +1 -1
  49. package/dist/sync/candidate-uploader.d.ts +88 -0
  50. package/dist/sync/candidate-uploader.d.ts.map +1 -0
  51. package/dist/sync/candidate-uploader.js +212 -0
  52. package/dist/sync/candidate-uploader.js.map +1 -0
  53. package/dist/sync/candidate-uploader.test.d.ts +2 -0
  54. package/dist/sync/candidate-uploader.test.d.ts.map +1 -0
  55. package/dist/sync/candidate-uploader.test.js +132 -0
  56. package/dist/sync/candidate-uploader.test.js.map +1 -0
  57. package/dist/sync/delta-client.d.ts +73 -0
  58. package/dist/sync/delta-client.d.ts.map +1 -0
  59. package/dist/sync/delta-client.js +201 -0
  60. package/dist/sync/delta-client.js.map +1 -0
  61. package/dist/sync/delta-client.test.d.ts +2 -0
  62. package/dist/sync/delta-client.test.d.ts.map +1 -0
  63. package/dist/sync/delta-client.test.js +97 -0
  64. package/dist/sync/delta-client.test.js.map +1 -0
  65. package/dist/sync/durable-apply.d.ts +76 -0
  66. package/dist/sync/durable-apply.d.ts.map +1 -0
  67. package/dist/sync/durable-apply.js +530 -0
  68. package/dist/sync/durable-apply.js.map +1 -0
  69. package/dist/sync/durable-apply.test.d.ts +2 -0
  70. package/dist/sync/durable-apply.test.d.ts.map +1 -0
  71. package/dist/sync/durable-apply.test.js +180 -0
  72. package/dist/sync/durable-apply.test.js.map +1 -0
  73. package/dist/sync/event-sync.d.ts +33 -1
  74. package/dist/sync/event-sync.d.ts.map +1 -1
  75. package/dist/sync/event-sync.js +149 -1
  76. package/dist/sync/event-sync.js.map +1 -1
  77. package/dist/sync/event-sync.test.js +142 -1
  78. package/dist/sync/event-sync.test.js.map +1 -1
  79. package/dist/sync/index.d.ts +2 -0
  80. package/dist/sync/index.d.ts.map +1 -1
  81. package/dist/sync/index.js +1 -0
  82. package/dist/sync/index.js.map +1 -1
  83. package/dist/sync/multipart-uploader.d.ts +99 -0
  84. package/dist/sync/multipart-uploader.d.ts.map +1 -0
  85. package/dist/sync/multipart-uploader.js +447 -0
  86. package/dist/sync/multipart-uploader.js.map +1 -0
  87. package/dist/sync/multipart-uploader.test.d.ts +2 -0
  88. package/dist/sync/multipart-uploader.test.d.ts.map +1 -0
  89. package/dist/sync/multipart-uploader.test.js +119 -0
  90. package/dist/sync/multipart-uploader.test.js.map +1 -0
  91. package/dist/sync/mutation-client.d.ts +85 -0
  92. package/dist/sync/mutation-client.d.ts.map +1 -0
  93. package/dist/sync/mutation-client.js +245 -0
  94. package/dist/sync/mutation-client.js.map +1 -0
  95. package/dist/sync/mutation-client.test.d.ts +2 -0
  96. package/dist/sync/mutation-client.test.d.ts.map +1 -0
  97. package/dist/sync/mutation-client.test.js +51 -0
  98. package/dist/sync/mutation-client.test.js.map +1 -0
  99. package/dist/sync/push-receiver.d.ts +45 -0
  100. package/dist/sync/push-receiver.d.ts.map +1 -1
  101. package/dist/sync/push-receiver.js +101 -0
  102. package/dist/sync/push-receiver.js.map +1 -1
  103. package/dist/sync/push-receiver.test.js +54 -2
  104. package/dist/sync/push-receiver.test.js.map +1 -1
  105. package/dist/sync/scope-inventory-client.d.ts +69 -0
  106. package/dist/sync/scope-inventory-client.d.ts.map +1 -0
  107. package/dist/sync/scope-inventory-client.js +210 -0
  108. package/dist/sync/scope-inventory-client.js.map +1 -0
  109. package/dist/sync/scope-inventory-client.test.d.ts +2 -0
  110. package/dist/sync/scope-inventory-client.test.d.ts.map +1 -0
  111. package/dist/sync/scope-inventory-client.test.js +94 -0
  112. package/dist/sync/scope-inventory-client.test.js.map +1 -0
  113. package/dist/sync/snapshot-client.d.ts +98 -0
  114. package/dist/sync/snapshot-client.d.ts.map +1 -0
  115. package/dist/sync/snapshot-client.js +402 -0
  116. package/dist/sync/snapshot-client.js.map +1 -0
  117. package/dist/sync/snapshot-client.test.d.ts +2 -0
  118. package/dist/sync/snapshot-client.test.d.ts.map +1 -0
  119. package/dist/sync/snapshot-client.test.js +169 -0
  120. package/dist/sync/snapshot-client.test.js.map +1 -0
  121. package/dist/sync/uploader-finalization.d.ts +97 -0
  122. package/dist/sync/uploader-finalization.d.ts.map +1 -0
  123. package/dist/sync/uploader-finalization.js +273 -0
  124. package/dist/sync/uploader-finalization.js.map +1 -0
  125. package/dist/sync/uploader-finalization.test.d.ts +2 -0
  126. package/dist/sync/uploader-finalization.test.d.ts.map +1 -0
  127. package/dist/sync/uploader-finalization.test.js +92 -0
  128. package/dist/sync/uploader-finalization.test.js.map +1 -0
  129. package/dist/telemetry.d.ts +11 -1
  130. package/dist/telemetry.d.ts.map +1 -1
  131. package/dist/telemetry.js +21 -2
  132. package/dist/telemetry.js.map +1 -1
  133. package/dist/telemetry.test.js +80 -0
  134. package/dist/telemetry.test.js.map +1 -1
  135. package/package.json +6 -1
  136. package/.claude/policies/hq-cloud-esm-cannot-spy-fs-builtins.md +0 -30
  137. package/.claude/policies/hq-cloud-strip-types-no-parameter-properties.md +0 -22
  138. package/.github/workflows/ci.yml +0 -84
  139. package/.github/workflows/publish.yml +0 -56
  140. package/.github/workflows/unreleased-commits-nag.yml +0 -256
  141. package/eslint.config.js +0 -67
  142. package/pnpm-workspace.yaml +0 -2
  143. package/scripts/presign-transport-e2e.mjs +0 -250
  144. package/scripts/vault-rebaseline.sh +0 -323
  145. package/scripts/vault-rescue.sh +0 -332
  146. package/src/active-company.test.ts +0 -188
  147. package/src/active-company.ts +0 -168
  148. package/src/agent-codex-instructions.test.ts +0 -332
  149. package/src/agent-codex-instructions.ts +0 -309
  150. package/src/auth.ts +0 -146
  151. package/src/backup-prune.test.ts +0 -98
  152. package/src/backup-prune.ts +0 -182
  153. package/src/bin/backup-prune-runner.ts +0 -33
  154. package/src/bin/rescue-runner.ts +0 -25
  155. package/src/bin/sync-runner-company.ts +0 -695
  156. package/src/bin/sync-runner-events.test.ts +0 -143
  157. package/src/bin/sync-runner-events.ts +0 -55
  158. package/src/bin/sync-runner-planning.test.ts +0 -311
  159. package/src/bin/sync-runner-planning.ts +0 -258
  160. package/src/bin/sync-runner-rollup.test.ts +0 -37
  161. package/src/bin/sync-runner-rollup.ts +0 -97
  162. package/src/bin/sync-runner-telemetry.ts +0 -15
  163. package/src/bin/sync-runner-watch-loop.ts +0 -1235
  164. package/src/bin/sync-runner-watch-routes.test.ts +0 -71
  165. package/src/bin/sync-runner-watch-routes.ts +0 -184
  166. package/src/bin/sync-runner.test.ts +0 -8767
  167. package/src/bin/sync-runner.ts +0 -2190
  168. package/src/cli/accept.ts +0 -124
  169. package/src/cli/conflict.ts +0 -119
  170. package/src/cli/doctor.test.ts +0 -581
  171. package/src/cli/doctor.ts +0 -642
  172. package/src/cli/index.ts +0 -49
  173. package/src/cli/invite.test.ts +0 -250
  174. package/src/cli/invite.ts +0 -214
  175. package/src/cli/promote.ts +0 -157
  176. package/src/cli/reindex-knowledge.test.ts +0 -307
  177. package/src/cli/reindex-knowledge.ts +0 -450
  178. package/src/cli/reindex.test.ts +0 -957
  179. package/src/cli/reindex.ts +0 -979
  180. package/src/cli/rescue-classify-ordering.test.ts +0 -548
  181. package/src/cli/rescue-clone-diagnostics.test.ts +0 -120
  182. package/src/cli/rescue-core.ts +0 -3011
  183. package/src/cli/rescue-drift-reconcile.test.ts +0 -179
  184. package/src/cli/rescue-drop-dir-symlink.test.ts +0 -224
  185. package/src/cli/rescue-exec-bit-preserve.test.ts +0 -187
  186. package/src/cli/rescue-hq-root-guard.test.ts +0 -232
  187. package/src/cli/rescue-journal-reconcile.test.ts +0 -215
  188. package/src/cli/rescue-mtime-preserve.test.ts +0 -203
  189. package/src/cli/rescue-settings-reconcile.test.ts +0 -637
  190. package/src/cli/rescue-snapshot.test.ts +0 -57
  191. package/src/cli/rescue-snapshot.ts +0 -51
  192. package/src/cli/rescue.reindex.test.ts +0 -63
  193. package/src/cli/rescue.test.ts +0 -131
  194. package/src/cli/rescue.ts +0 -182
  195. package/src/cli/share.test.ts +0 -7843
  196. package/src/cli/share.ts +0 -3663
  197. package/src/cli/sync-scope.test.ts +0 -652
  198. package/src/cli/sync.test.ts +0 -5207
  199. package/src/cli/sync.ts +0 -3470
  200. package/src/cli/tombstones.ts +0 -106
  201. package/src/cli/watch-event-push-conflict.test.ts +0 -234
  202. package/src/client-info.test.ts +0 -214
  203. package/src/client-info.ts +0 -121
  204. package/src/cognito-auth.test.ts +0 -712
  205. package/src/cognito-auth.ts +0 -1422
  206. package/src/company-resolver.test.ts +0 -618
  207. package/src/company-resolver.ts +0 -521
  208. package/src/context.test.ts +0 -583
  209. package/src/context.ts +0 -378
  210. package/src/daemon-worker.ts +0 -26
  211. package/src/daemon.ts +0 -99
  212. package/src/entity-resolver.test.ts +0 -315
  213. package/src/entity-resolver.ts +0 -180
  214. package/src/ignore.test.ts +0 -466
  215. package/src/ignore.ts +0 -469
  216. package/src/index.ts +0 -439
  217. package/src/journal.test.ts +0 -968
  218. package/src/journal.ts +0 -765
  219. package/src/lib/cloud-authoritative.test.ts +0 -45
  220. package/src/lib/cloud-authoritative.ts +0 -59
  221. package/src/lib/conflict-file.ts +0 -86
  222. package/src/lib/conflict-index.ts +0 -289
  223. package/src/lib/conflict.test.ts +0 -348
  224. package/src/lib/describe-error.test.ts +0 -100
  225. package/src/lib/describe-error.ts +0 -58
  226. package/src/lib/exit-codes.ts +0 -24
  227. package/src/lib/machine-id.test.ts +0 -231
  228. package/src/lib/machine-id.ts +0 -175
  229. package/src/lib/net-errors.test.ts +0 -65
  230. package/src/lib/net-errors.ts +0 -86
  231. package/src/lib/readlink-safe.test.ts +0 -43
  232. package/src/lib/readlink-safe.ts +0 -29
  233. package/src/local-path-codec.test.ts +0 -138
  234. package/src/local-path-codec.ts +0 -161
  235. package/src/machine-auth.test.ts +0 -1323
  236. package/src/manifest-reconcile.test.ts +0 -1123
  237. package/src/manifest-reconcile.ts +0 -518
  238. package/src/object-io.test.ts +0 -1221
  239. package/src/object-io.ts +0 -1306
  240. package/src/operation-lock.test.ts +0 -484
  241. package/src/operation-lock.ts +0 -680
  242. package/src/outcome-telemetry.test.ts +0 -498
  243. package/src/outcome-telemetry.ts +0 -639
  244. package/src/personal-vault-exclusions.test.ts +0 -308
  245. package/src/personal-vault-exclusions.ts +0 -354
  246. package/src/personal-vault.test.ts +0 -756
  247. package/src/personal-vault.ts +0 -496
  248. package/src/prefix-coalesce.test.ts +0 -240
  249. package/src/prefix-coalesce.ts +0 -273
  250. package/src/public-surface.test.ts +0 -117
  251. package/src/qmd-reindex.test.ts +0 -877
  252. package/src/qmd-reindex.ts +0 -842
  253. package/src/read-only-state-dir.test.ts +0 -188
  254. package/src/remote-pull.test.ts +0 -1130
  255. package/src/remote-pull.ts +0 -618
  256. package/src/s3.symlink-materialize.test.ts +0 -492
  257. package/src/s3.test.ts +0 -1789
  258. package/src/s3.ts +0 -1532
  259. package/src/schemas/signal-types.test.ts +0 -82
  260. package/src/schemas/signal-types.ts +0 -38
  261. package/src/schemas/source-channels.test.ts +0 -82
  262. package/src/schemas/source-channels.ts +0 -53
  263. package/src/scope-shrink.test.ts +0 -633
  264. package/src/scope-shrink.ts +0 -481
  265. package/src/signals/get.test.ts +0 -310
  266. package/src/signals/get.ts +0 -75
  267. package/src/signals/internals.ts +0 -195
  268. package/src/signals/list.test.ts +0 -420
  269. package/src/signals/list.ts +0 -79
  270. package/src/signals/parse.ts +0 -8
  271. package/src/signals/types.ts +0 -91
  272. package/src/skill-telemetry.test.ts +0 -1825
  273. package/src/skill-telemetry.ts +0 -1439
  274. package/src/sources/get.test.ts +0 -293
  275. package/src/sources/get.ts +0 -66
  276. package/src/sources/internals.ts +0 -198
  277. package/src/sources/list.test.ts +0 -402
  278. package/src/sources/list.ts +0 -84
  279. package/src/sources/parse.ts +0 -43
  280. package/src/sources/types.ts +0 -84
  281. package/src/sync/event-sync.test.ts +0 -594
  282. package/src/sync/event-sync.ts +0 -545
  283. package/src/sync/feature-flags.test.ts +0 -378
  284. package/src/sync/feature-flags.ts +0 -62
  285. package/src/sync/index.ts +0 -76
  286. package/src/sync/lease-client.test.ts +0 -128
  287. package/src/sync/lease-client.ts +0 -207
  288. package/src/sync/logger.test.ts +0 -242
  289. package/src/sync/logger.ts +0 -79
  290. package/src/sync/metrics.test.ts +0 -462
  291. package/src/sync/metrics.ts +0 -213
  292. package/src/sync/pull-scope.ts +0 -265
  293. package/src/sync/push-event.test.ts +0 -266
  294. package/src/sync/push-event.ts +0 -224
  295. package/src/sync/push-receiver.test.ts +0 -566
  296. package/src/sync/push-receiver.ts +0 -1048
  297. package/src/sync/push-transport.ts +0 -231
  298. package/src/sync/realtime-rollout.test.ts +0 -86
  299. package/src/sync/realtime-rollout.ts +0 -262
  300. package/src/sync/state-store.test.ts +0 -194
  301. package/src/sync/state-store.ts +0 -727
  302. package/src/sync-core.ts +0 -58
  303. package/src/sync-progress.test.ts +0 -94
  304. package/src/sync-progress.ts +0 -140
  305. package/src/telemetry-events.test.ts +0 -88
  306. package/src/telemetry-events.ts +0 -205
  307. package/src/telemetry.test.ts +0 -1280
  308. package/src/telemetry.ts +0 -1109
  309. package/src/types.ts +0 -314
  310. package/src/vault-client.test.ts +0 -1380
  311. package/src/vault-client.ts +0 -1694
  312. package/src/version.ts +0 -24
  313. package/src/watch-roots.test.ts +0 -278
  314. package/src/watch-roots.ts +0 -162
  315. package/src/watcher-event-gate.test.ts +0 -212
  316. package/src/watcher.test.ts +0 -1079
  317. package/src/watcher.ts +0 -1741
  318. package/test/e2e/sync/cross-tenant-isolation.test.ts +0 -630
  319. package/test/e2e/sync/skill-telemetry-oversized-transcript.test.ts +0 -124
  320. package/test/e2e/sync/transient-company-leg.test.ts +0 -384
  321. package/test/e2e/sync/windows-unreadable-link-leg.test.ts +0 -191
  322. package/test/e2e/watcher-real-chokidar.test.ts +0 -165
  323. package/test/e2e/watcher-recursive-backend.test.ts +0 -181
  324. package/test/e2e/watcher-scoped-coverage.test.ts +0 -381
  325. package/test/invite-flow.integration.test.ts +0 -244
  326. package/test/joiner-manifest-reconcile.integration.test.ts +0 -322
  327. package/test/share-sync.integration.test.ts +0 -213
  328. package/tsconfig.json +0 -19
  329. package/vitest.config.ts +0 -22
@@ -1,256 +0,0 @@
1
- name: Nag unreleased commits on main
2
-
3
- # Two weekly release nags for hq-cloud:
4
- #
5
- # nag — `main` is carrying work nobody has released (below).
6
- # pin-lag — the latest hq-cloud release is not pinned by hq-cli and
7
- # hq-desktop-app `main`. See that job's own header.
8
- #
9
- # A commit counts as UNRELEASED when it is reachable from `main` but not from
10
- # any STABLE tag. Stable means `vMAJOR.MINOR.PATCH` with no prerelease suffix —
11
- # `-rc.N`, `-beta.N` and `-alpha.N` tags deliberately do NOT silence the nag,
12
- # because a prerelease is not a release.
13
- #
14
- # Release sync-back commits (`chore(release): …`) are ignored. The publish
15
- # workflow pushes them to `main` *after* cutting the tag, so they are
16
- # unreachable from that tag by construction and would nag forever otherwise.
17
- #
18
- # Releasing this repo is one command — `git push origin v<next>` — so the fix
19
- # for a nag is always to cut the tag (or to say out loud why you are holding).
20
- #
21
- # Requires repo secret SLACK_BOT_TOKEN_RELEASE_MANAGER (the same release-manager
22
- # bot hq-core's `release-pr-nag.yml` uses). The bot MUST be a member of #hq-dev.
23
- # Without the secret the job logs a warning and exits 0 rather than failing.
24
-
25
- on:
26
- schedule:
27
- # Mondays 15:00 UTC (~08:00 PT). Staggered against the sibling repos'
28
- # nags so all four do not land in #hq-dev in the same second.
29
- - cron: '10 15 * * 1'
30
- workflow_dispatch:
31
- inputs:
32
- threshold_hours:
33
- description: 'Age of the OLDEST unreleased commit, in hours, before nagging (default 24)'
34
- required: false
35
- default: '24'
36
-
37
- permissions:
38
- contents: read
39
-
40
- jobs:
41
- nag:
42
- name: Nag when main has unreleased commits
43
- if: github.repository == 'indigoai-us/hq-cloud'
44
- runs-on: ubuntu-latest
45
- env:
46
- SLACK_TOKEN: ${{ secrets.SLACK_BOT_TOKEN_RELEASE_MANAGER }}
47
- SLACK_CHANNEL: '#hq-dev'
48
- THRESHOLD: ${{ github.event.inputs.threshold_hours || '24' }}
49
- steps:
50
- - name: Check out full history and tags
51
- uses: actions/checkout@v4
52
- with:
53
- ref: main
54
- fetch-depth: 0
55
-
56
- - name: Find unreleased commits on main and nag
57
- run: |
58
- set -euo pipefail
59
-
60
- if [ -z "${SLACK_TOKEN:-}" ]; then
61
- echo "::warning::SLACK_BOT_TOKEN_RELEASE_MANAGER is not set on this repository; cannot post the nag."
62
- exit 0
63
- fi
64
-
65
- git fetch --force --tags --quiet origin main
66
-
67
- # Newest stable tag reachable from main — display only.
68
- LAST_TAG="$(git tag --list 'v*' --merged origin/main --sort=-v:refname \
69
- | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1 || true)"
70
-
71
- # Commits on main not reachable from ANY stable tag. `--exclude` applies
72
- # to the `--tags` glob that follows it, so `v*-*` drops every prerelease.
73
- git log origin/main --format='%H%x09%ct%x09%s' \
74
- --not --exclude='v*-*' --tags='v*' > /tmp/unreleased_all.tsv
75
-
76
- # Drop the post-tag release sync-back commits.
77
- awk -F'\t' '$3 !~ /^chore\(release\)/' /tmp/unreleased_all.tsv > /tmp/unreleased.tsv
78
-
79
- COUNT="$(wc -l < /tmp/unreleased.tsv | tr -d ' ')"
80
- echo "Last stable tag: ${LAST_TAG:-<none>}"
81
- echo "Unreleased commits on main: ${COUNT}"
82
-
83
- if [ "$COUNT" -eq 0 ]; then
84
- echo "main is fully released. Nothing to nag about."
85
- exit 0
86
- fi
87
-
88
- OLDEST_TS="$(tail -1 /tmp/unreleased.tsv | cut -f2)"
89
- NOW="$(date -u +%s)"
90
- AGE_H=$(( (NOW - OLDEST_TS) / 3600 ))
91
- echo "Oldest unreleased commit age: ${AGE_H}h (threshold ${THRESHOLD}h)"
92
-
93
- if [ "$AGE_H" -lt "$THRESHOLD" ]; then
94
- echo "Under threshold. Skipping."
95
- exit 0
96
- fi
97
-
98
- REPO_NAME="${GITHUB_REPOSITORY##*/}"
99
- DAYS=$(( AGE_H / 24 ))
100
- if [ -n "$LAST_TAG" ]; then
101
- LINK="https://github.com/${GITHUB_REPOSITORY}/compare/${LAST_TAG}...main"
102
- TAG_LABEL="\`${LAST_TAG}\`"
103
- else
104
- LINK="https://github.com/${GITHUB_REPOSITORY}/commits/main"
105
- TAG_LABEL="_never released_"
106
- fi
107
-
108
- # Oldest-first sample of what is waiting, capped at 5 lines.
109
- SAMPLE="$(tail -5 /tmp/unreleased.tsv | tac | cut -f3 | sed 's/^/• /')"
110
- if [ "$COUNT" -gt 5 ]; then
111
- SAMPLE="${SAMPLE}"$'\n'"• …and $(( COUNT - 5 )) more"
112
- fi
113
-
114
- TEXT=":package: *\`${REPO_NAME}\`: ${COUNT} commit(s) have been sitting unreleased on \`main\` for ~${AGE_H}h (${DAYS}d).* Last stable release: ${TAG_LABEL} — prereleases don't count. Shipping is one command: \`git push origin v<next>\`. <${LINK}|See what's waiting>."$'\n'"${SAMPLE}"
115
-
116
- RESP="$(curl -s -X POST \
117
- -H "Authorization: Bearer ${SLACK_TOKEN}" \
118
- -H 'Content-type: application/json; charset=utf-8' \
119
- --data "$(jq -nc --arg c "$SLACK_CHANNEL" --arg t "$TEXT" '{channel:$c,text:$t,unfurl_links:false}')" \
120
- https://slack.com/api/chat.postMessage)"
121
- OK="$(echo "$RESP" | jq -r '.ok')"
122
- echo "Slack post ok=${OK} err=$(echo "$RESP" | jq -r '.error // "-"')"
123
- if [ "$OK" != "true" ]; then
124
- echo "::error::Slack rejected the nag: $(echo "$RESP" | jq -r '.error // "unknown"')"
125
- exit 1
126
- fi
127
-
128
- pin-lag:
129
- name: Nag when consumers lag the latest hq-cloud release
130
- # hq-cloud ships the sync runner that hq-cli and hq-desktop-app spawn. A
131
- # release nobody has pinned is a release nobody is running, so this job
132
- # compares the newest STABLE hq-cloud tag against the version each
133
- # consumer's `main` actually declares:
134
- #
135
- # hq-cli pnpm-lock.yaml -> resolved @indigoai-us/hq-cloud version
136
- # hq-desktop-app crates/hq-desktop-core/src/hq_cloud.rs -> HQ_CLOUD_VERSION
137
- #
138
- # Comparison is EXACT on the version string (the desktop pin's leading `~`
139
- # is stripped first). A tilde range like `~6.14.47` does resolve to 6.14.48
140
- # at npx spawn time, so this will flag a pin that is already functionally
141
- # current — that is deliberate: the written pin is kept literally in step
142
- # with the latest release.
143
- #
144
- # Reading the two sibling repos needs a cross-repo token, so this mints an
145
- # installation token from the hq-audit-bot App (same pattern as
146
- # hq-core's promote-to-hq-core.yml). The App must be installed on
147
- # indigoai-us/hq-cli and indigoai-us/hq-desktop-app with `contents: read`.
148
- if: github.repository == 'indigoai-us/hq-cloud'
149
- runs-on: ubuntu-latest
150
- env:
151
- SLACK_TOKEN: ${{ secrets.SLACK_BOT_TOKEN_RELEASE_MANAGER }}
152
- SLACK_CHANNEL: '#hq-dev'
153
- HQ_AUDIT_BOT_APP_ID: ${{ secrets.HQ_AUDIT_BOT_APP_ID }}
154
- HQ_AUDIT_BOT_PRIVATE_KEY: ${{ secrets.HQ_AUDIT_BOT_PRIVATE_KEY }}
155
- steps:
156
- - name: Check out full history and tags
157
- uses: actions/checkout@v4
158
- with:
159
- ref: main
160
- fetch-depth: 0
161
-
162
- - name: Preflight credentials
163
- id: pre
164
- run: |
165
- set -euo pipefail
166
- if [ -z "${SLACK_TOKEN:-}" ]; then
167
- echo "::warning::SLACK_BOT_TOKEN_RELEASE_MANAGER is not set on this repository; cannot post the nag."
168
- echo "ready=false" >> "$GITHUB_OUTPUT"
169
- exit 0
170
- fi
171
- if [ -z "${HQ_AUDIT_BOT_APP_ID:-}" ] || [ -z "${HQ_AUDIT_BOT_PRIVATE_KEY:-}" ]; then
172
- echo "::warning::hq-audit-bot App credentials are not set; cannot read hq-cli / hq-desktop-app main."
173
- echo "ready=false" >> "$GITHUB_OUTPUT"
174
- exit 0
175
- fi
176
- echo "ready=true" >> "$GITHUB_OUTPUT"
177
-
178
- - name: Mint installation token for the consumer repos
179
- id: app-token
180
- if: steps.pre.outputs.ready == 'true'
181
- uses: actions/create-github-app-token@v1
182
- with:
183
- app-id: ${{ env.HQ_AUDIT_BOT_APP_ID }}
184
- private-key: ${{ env.HQ_AUDIT_BOT_PRIVATE_KEY }}
185
- owner: indigoai-us
186
- repositories: |
187
- hq-cli
188
- hq-desktop-app
189
-
190
- - name: Compare consumer pins against the latest hq-cloud release
191
- if: steps.pre.outputs.ready == 'true'
192
- env:
193
- GH_TOKEN: ${{ steps.app-token.outputs.token }}
194
- run: |
195
- set -euo pipefail
196
-
197
- git fetch --force --tags --quiet origin main
198
-
199
- LATEST_TAG="$(git tag --list 'v*' --merged origin/main --sort=-v:refname \
200
- | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1 || true)"
201
- if [ -z "$LATEST_TAG" ]; then
202
- echo "No stable hq-cloud release yet; nothing to compare against."
203
- exit 0
204
- fi
205
- LATEST="${LATEST_TAG#v}"
206
- echo "Latest stable hq-cloud release: ${LATEST}"
207
-
208
- # --- hq-cli: resolved version in the lockfile's importers section ---
209
- gh api "repos/indigoai-us/hq-cli/contents/pnpm-lock.yaml?ref=main" \
210
- -H 'Accept: application/vnd.github.raw' > /tmp/hq-cli-lock.yaml
211
- CLI_PIN="$(grep -A2 "^ '@indigoai-us/hq-cloud':" /tmp/hq-cli-lock.yaml \
212
- | awk '/^ version:/{print $2; exit}' | sed 's/(.*//')"
213
- if [ -z "$CLI_PIN" ]; then
214
- echo "::error::Could not read the resolved @indigoai-us/hq-cloud version from hq-cli's pnpm-lock.yaml. The lockfile layout changed — fix this parser rather than letting the check pass silently."
215
- exit 1
216
- fi
217
- echo "hq-cli pins: ${CLI_PIN}"
218
-
219
- # --- hq-desktop-app: HQ_CLOUD_VERSION constant, tilde stripped ---
220
- gh api "repos/indigoai-us/hq-desktop-app/contents/crates/hq-desktop-core/src/hq_cloud.rs?ref=main" \
221
- -H 'Accept: application/vnd.github.raw' > /tmp/hq_cloud.rs
222
- DESK_RAW="$(grep -oP 'HQ_CLOUD_VERSION\s*:\s*&str\s*=\s*"\K[^"]+' /tmp/hq_cloud.rs | head -1)"
223
- if [ -z "$DESK_RAW" ]; then
224
- echo "::error::Could not read HQ_CLOUD_VERSION from hq-desktop-app's crates/hq-desktop-core/src/hq_cloud.rs. The constant moved or was renamed — fix this parser rather than letting the check pass silently."
225
- exit 1
226
- fi
227
- DESK_PIN="${DESK_RAW#\~}"
228
- DESK_PIN="${DESK_PIN#^}"
229
- echo "hq-desktop-app pins: ${DESK_RAW} (compared as ${DESK_PIN})"
230
-
231
- LAGGARDS=""
232
- if [ "$CLI_PIN" != "$LATEST" ]; then
233
- LAGGARDS="${LAGGARDS}"$'\n'"• \`hq-cli\` is on \`${CLI_PIN}\` (pnpm-lock.yaml)"
234
- fi
235
- if [ "$DESK_PIN" != "$LATEST" ]; then
236
- LAGGARDS="${LAGGARDS}"$'\n'"• \`hq-desktop-app\` is on \`${DESK_RAW}\` (HQ_CLOUD_VERSION)"
237
- fi
238
-
239
- if [ -z "$LAGGARDS" ]; then
240
- echo "Both consumers are pinned to ${LATEST}. Nothing to nag about."
241
- exit 0
242
- fi
243
-
244
- TEXT=":link: *hq-cloud \`${LATEST}\` is released but not pinned everywhere.* A runner nobody pins is a runner nobody runs:${LAGGARDS}"$'\n'"Bump the pin(s) to \`${LATEST}\` and ship. <https://github.com/indigoai-us/hq-cloud/releases/tag/${LATEST_TAG}|Release ${LATEST_TAG}>."
245
-
246
- RESP="$(curl -s -X POST \
247
- -H "Authorization: Bearer ${SLACK_TOKEN}" \
248
- -H 'Content-type: application/json; charset=utf-8' \
249
- --data "$(jq -nc --arg c "$SLACK_CHANNEL" --arg t "$TEXT" '{channel:$c,text:$t,unfurl_links:false}')" \
250
- https://slack.com/api/chat.postMessage)"
251
- OK="$(echo "$RESP" | jq -r '.ok')"
252
- echo "Slack post ok=${OK} err=$(echo "$RESP" | jq -r '.error // "-"')"
253
- if [ "$OK" != "true" ]; then
254
- echo "::error::Slack rejected the nag: $(echo "$RESP" | jq -r '.error // "unknown"')"
255
- exit 1
256
- fi
package/eslint.config.js DELETED
@@ -1,67 +0,0 @@
1
- import js from "@eslint/js";
2
- import tseslint from "typescript-eslint";
3
-
4
- const nodeGlobals = {
5
- AbortController: "readonly",
6
- Buffer: "readonly",
7
- clearInterval: "readonly",
8
- clearTimeout: "readonly",
9
- console: "readonly",
10
- fetch: "readonly",
11
- process: "readonly",
12
- setInterval: "readonly",
13
- setTimeout: "readonly",
14
- structuredClone: "readonly",
15
- URL: "readonly",
16
- };
17
-
18
- const vitestGlobals = {
19
- afterEach: "readonly",
20
- beforeEach: "readonly",
21
- describe: "readonly",
22
- expect: "readonly",
23
- it: "readonly",
24
- vi: "readonly",
25
- };
26
-
27
- export default tseslint.config(
28
- {
29
- ignores: [
30
- "coverage/**",
31
- "dist/**",
32
- "node_modules/**",
33
- "pnpm-lock.yaml",
34
- "*.config.js",
35
- "*.config.mjs",
36
- "*.config.cjs",
37
- "generated/**",
38
- "**/*.generated.*",
39
- ],
40
- },
41
- {
42
- files: ["src/**/*.ts", "test/**/*.ts"],
43
- extends: [js.configs.recommended, ...tseslint.configs.recommended],
44
- languageOptions: {
45
- ecmaVersion: 2022,
46
- sourceType: "module",
47
- globals: {
48
- ...nodeGlobals,
49
- ...vitestGlobals,
50
- },
51
- },
52
- rules: {
53
- "@typescript-eslint/no-explicit-any": "warn",
54
- "@typescript-eslint/no-unused-vars": [
55
- "error",
56
- {
57
- argsIgnorePattern: "^_",
58
- caughtErrorsIgnorePattern: "^_",
59
- destructuredArrayIgnorePattern: "^_",
60
- varsIgnorePattern: "^_",
61
- },
62
- ],
63
- "no-useless-assignment": "off",
64
- "preserve-caught-error": "off",
65
- },
66
- },
67
- );
@@ -1,2 +0,0 @@
1
- allowBuilds:
2
- esbuild: true
@@ -1,250 +0,0 @@
1
- /**
2
- * Live E2E for the presigned-URL transport (hq-cloud feat/presign-transport).
3
- *
4
- * Drives the real s3.ts primitives through the PRESIGN factory against
5
- * production (hqapi.hq.computer), exercising the exact code path that
6
- * @getindigo.ai sync sessions will use: VaultClient.presign/listFiles +
7
- * PresignObjectIO + fetch. Creates a scratch object under a clearly-marked
8
- * prefix, round-trips it (put → list → head → get → symlink → delete), and
9
- * deletes everything it created. Read-only on all real vault data.
10
- *
11
- * Run from the worktree root after `pnpm build`:
12
- * node scripts/presign-transport-e2e.mjs
13
- */
14
- import * as fs from "fs";
15
- import * as os from "os";
16
- import * as path from "path";
17
- import { VaultClient } from "../dist/vault-client.js";
18
- import {
19
- setObjectIOFactory,
20
- presignObjectIOFactory,
21
- } from "../dist/object-io.js";
22
- import {
23
- uploadFile,
24
- uploadSymlink,
25
- downloadFile,
26
- listRemoteFiles,
27
- headRemoteFile,
28
- deleteRemoteFile,
29
- } from "../dist/s3.js";
30
-
31
- const API = process.env.HQ_VAULT_API_URL ?? "https://hqapi.hq.computer";
32
-
33
- const results = [];
34
- function check(name, cond, detail = "") {
35
- results.push({ name, ok: !!cond, detail });
36
- console.log(`${cond ? "✓" : "✗"} ${name}${detail ? ` — ${detail}` : ""}`);
37
- }
38
-
39
- function loadAccessToken() {
40
- const p = path.join(os.homedir(), ".hq", "cognito-tokens.json");
41
- const t = JSON.parse(fs.readFileSync(p, "utf-8"));
42
- const at = t.accessToken;
43
- if (!at) throw new Error("no accessToken in cognito-tokens.json");
44
- return at;
45
- }
46
-
47
- async function main() {
48
- const token = loadAccessToken();
49
- const vault = new VaultClient({ apiUrl: API, authToken: token });
50
-
51
- // Resolve a company the caller can write to (owner/admin role).
52
- const memberships = await vault.listMyMemberships();
53
- const writable = memberships.find(
54
- (m) => m.role === "owner" || m.role === "admin",
55
- );
56
- if (!writable) {
57
- throw new Error(
58
- `no owner/admin membership found (roles: ${memberships.map((m) => m.role).join(",") || "none"})`,
59
- );
60
- }
61
- const companyUid = writable.companyUid;
62
- let slug = companyUid;
63
- try {
64
- slug = (await vault.entity.get(companyUid)).slug ?? companyUid;
65
- } catch {
66
- /* display only */
67
- }
68
- console.log(`\nTarget: ${slug} (${companyUid}), role=${writable.role}\n`);
69
-
70
- // Install the presign transport — the same selection runRunner makes for a
71
- // @getindigo.ai session. ctx.credentials/bucketName/region are UNUSED on the
72
- // presign path (only ctx.uid → companyUid matters), so dummy values are fine.
73
- setObjectIOFactory(presignObjectIOFactory(vault));
74
- const ctx = {
75
- uid: companyUid,
76
- slug,
77
- bucketName: "(presign-unused)",
78
- region: "us-east-1",
79
- credentials: { accessKeyId: "x", secretAccessKey: "x", sessionToken: "x" },
80
- expiresAt: "2099-01-01T00:00:00.000Z",
81
- };
82
-
83
- const stamp = Date.now().toString(36);
84
- const prefix = `shared/_presign_e2e_${stamp}/`;
85
- const fileKey = `${prefix}hooks/presign-e2e-hook.sh`;
86
- const legacyKey = `${prefix}hooks/legacy-e2e-hook.sh`;
87
- const linkKey = `${prefix}link`;
88
- const author = { userSub: "e2e-probe-sub", email: "hassaan@getindigo.ai" };
89
-
90
- const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "presign-e2e-"));
91
- const localUp = path.join(tmp, "presign-e2e-hook.sh");
92
- const localDown = path.join(tmp, "downloaded-hook.sh");
93
- const legacyDown = path.join(tmp, "legacy-hook.sh");
94
- const localLink = path.join(tmp, "link");
95
- const content = `#!/usr/bin/env bash\n# presign-transport probe ${stamp}\n${"x".repeat(2000)}`;
96
- fs.writeFileSync(localUp, content);
97
- fs.chmodSync(localUp, 0o755);
98
-
99
- let allCreated = [];
100
- try {
101
- // ---- PUT (presigned, with metadata signed in) -----------------------
102
- const up = await uploadFile(ctx, localUp, fileKey, author);
103
- allCreated.push(fileKey);
104
- check("uploadFile returns an etag", !!up.etag, up.etag);
105
-
106
- // ---- LIST (presigned list, etag is load-bearing) --------------------
107
- const listed = await listRemoteFiles(ctx, prefix);
108
- const row = listed.find((o) => o.key === fileKey);
109
- check("listRemoteFiles finds the uploaded key", !!row);
110
- check(
111
- "listed object carries a non-empty etag (#269)",
112
- !!row && typeof row.etag === "string" && row.etag.length > 0,
113
- row?.etag,
114
- );
115
- check(
116
- "listed size matches uploaded bytes",
117
- !!row && row.size === Buffer.byteLength(content),
118
- `${row?.size} vs ${Buffer.byteLength(content)}`,
119
- );
120
-
121
- // ---- HEAD (presigned GET + header read) -----------------------------
122
- const head = await headRemoteFile(ctx, fileKey);
123
- check("headRemoteFile returns metadata", !!head && !!head.metadata);
124
- check(
125
- "metadata.created-by round-trips (signed PUT meta)",
126
- head?.metadata?.["created-by"] === author.email,
127
- head?.metadata?.["created-by"],
128
- );
129
- check(
130
- "metadata.hq-mode round-trips as 755",
131
- head?.metadata?.["hq-mode"] === "755",
132
- head?.metadata?.["hq-mode"],
133
- );
134
- check(
135
- "head etag matches list etag",
136
- !!head && !!row && head.etag === row.etag,
137
- `${head?.etag} vs ${row?.etag}`,
138
- );
139
-
140
- // ---- GET (presigned download to an empty destination, bytes + mode) --
141
- const dl = await downloadFile(ctx, fileKey, localDown);
142
- const got = fs.readFileSync(localDown, "utf-8");
143
- check("downloadFile bytes match upload", got === content);
144
- check(
145
- "downloaded hook mode is 0755 (hq-mode applied)",
146
- (fs.lstatSync(localDown).mode & 0o777) === 0o755,
147
- "0" + (fs.lstatSync(localDown).mode & 0o777).toString(8),
148
- );
149
- check(
150
- "downloadFile surfaces created-by metadata",
151
- dl?.metadata?.["created-by"] === author.email,
152
- );
153
-
154
- // ---- Legacy hq-mode fallback ----------------------------------------
155
- // Create a pre-hq-mode object through the same real presigned PUT
156
- // endpoint, deliberately omitting metadata. A legacy remote overwrite
157
- // must retain the known-good local executable mode instead of silently
158
- // replacing the hook with the receiver's usual 0644 umask default.
159
- const legacyPresign = await vault.presign({
160
- companyUid,
161
- op: "put",
162
- keys: [{ key: legacyKey, op: "put", contentType: "text/x-shellscript" }],
163
- });
164
- const legacyPut = legacyPresign.results[0];
165
- if (!legacyPut?.url) throw new Error("legacy PUT presign was not returned");
166
- const legacyPutResponse = await fetch(legacyPut.url, {
167
- method: "PUT",
168
- headers: legacyPut.headers,
169
- body: "#!/usr/bin/env bash\necho legacy hook\n",
170
- });
171
- if (!legacyPutResponse.ok) {
172
- throw new Error(`legacy presigned PUT failed: ${legacyPutResponse.status}`);
173
- }
174
- allCreated.push(legacyKey);
175
- const legacyHead = await headRemoteFile(ctx, legacyKey);
176
- check(
177
- "legacy object deliberately has no hq-mode metadata",
178
- legacyHead?.metadata?.["hq-mode"] === undefined,
179
- legacyHead?.metadata?.["hq-mode"],
180
- );
181
- fs.writeFileSync(legacyDown, "#!/usr/bin/env bash\necho prior hook\n");
182
- fs.chmodSync(legacyDown, 0o755);
183
- const legacyDownload = await downloadFile(ctx, legacyKey, legacyDown);
184
- check(
185
- "legacy hook fallback retains existing 0755 mode (not 0644)",
186
- (fs.lstatSync(legacyDown).mode & 0o777) === 0o755,
187
- "0" + (fs.lstatSync(legacyDown).mode & 0o777).toString(8),
188
- );
189
- check(
190
- "legacy hq-mode fallback is surfaced for repair telemetry",
191
- legacyDownload.modeWarnings?.some(
192
- (warning) =>
193
- warning.reason === "missing-hq-mode" &&
194
- warning.fallback === "preserved-local-mode",
195
- ) === true,
196
- );
197
-
198
- // ---- SYMLINK round-trip --------------------------------------------
199
- const ln = await uploadSymlink(ctx, "../target/path.txt", linkKey, author);
200
- allCreated.push(linkKey);
201
- check("uploadSymlink returns an etag", !!ln.etag);
202
- await downloadFile(ctx, linkKey, localLink);
203
- const st = fs.lstatSync(localLink);
204
- check("downloaded symlink is a symlink", st.isSymbolicLink());
205
- check(
206
- "symlink target round-trips",
207
- st.isSymbolicLink() && fs.readlinkSync(localLink) === "../target/path.txt",
208
- st.isSymbolicLink() ? fs.readlinkSync(localLink) : "(not a link)",
209
- );
210
-
211
- // ---- DELETE (presigned) --------------------------------------------
212
- await deleteRemoteFile(ctx, fileKey);
213
- await deleteRemoteFile(ctx, legacyKey);
214
- await deleteRemoteFile(ctx, linkKey);
215
- allCreated = [];
216
- const afterFile = await headRemoteFile(ctx, fileKey);
217
- check("headRemoteFile is null after delete", afterFile === null);
218
- const afterList = await listRemoteFiles(ctx, prefix);
219
- check(
220
- "listRemoteFiles empty after delete",
221
- afterList.filter((o) => o.key.startsWith(prefix)).length === 0,
222
- `${afterList.length} remaining`,
223
- );
224
- } finally {
225
- // Best-effort cleanup of anything left if an assertion threw mid-run.
226
- for (const k of allCreated) {
227
- try {
228
- await deleteRemoteFile(ctx, k);
229
- } catch {
230
- /* ignore */
231
- }
232
- }
233
- fs.rmSync(tmp, { recursive: true, force: true });
234
- }
235
-
236
- const failed = results.filter((r) => !r.ok);
237
- console.log(
238
- `\n${results.length - failed.length}/${results.length} checks passed`,
239
- );
240
- if (failed.length > 0) {
241
- console.log("FAILED:", failed.map((f) => f.name).join("; "));
242
- process.exit(1);
243
- }
244
- console.log("ALL GREEN");
245
- }
246
-
247
- main().catch((err) => {
248
- console.error("E2E error:", err);
249
- process.exit(2);
250
- });