@posthog/cli 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # posthog-cli
2
2
 
3
+ ## 0.11.1 — 2026-08-13
4
+
5
+ ### Patch changes
6
+
7
+ - [c75bed06227](https://github.com/PostHog/posthog/commit/c75bed062278959c10907d1245c39a0d8ef20d11) Retry transient GitHub release download failures with exponential backoff when installing `@posthog/cli` from npm. — Thanks @marandaneto!
8
+
9
+ ## 0.11.0 — 2026-08-12
10
+
11
+ ### Minor changes
12
+
13
+ - [8d58ef5de6c](https://github.com/PostHog/posthog/commit/8d58ef5de6c34563ed1c07a54c92fbbfe9e397d4) Add `--release-mode` (env `POSTHOG_RELEASE_MODE`) to `inject`/`upload`/`process`. The default `symbol-set` keeps the existing behavior: the release id is stamped onto the uploaded symbol sets. The experimental `event` mode derives content-addressed chunk ids that stay stable across rebuilds and injects the release id into each JS chunk as `_posthogReleaseId`, so the SDK reports it per event and symbol sets stay release-independent. The release is still created; it just isn't bound to any chunk. — Thanks @ablaszkiewicz!
14
+
3
15
  ## 0.10.0 — 2026-08-03
4
16
 
5
17
  ### Minor changes
package/binary-install.js CHANGED
@@ -12,6 +12,50 @@ const { tmpdir } = require("os");
12
12
  const https = require("node:https");
13
13
  const http = require("node:http");
14
14
 
15
+ const DOWNLOAD_MAX_ATTEMPTS = 4;
16
+ const DOWNLOAD_RETRY_BASE_DELAY_MS = 1000;
17
+
18
+ function isTransientDownloadError(error) {
19
+ const status = Number(/HTTP (\d{3})/.exec(error?.message ?? '')?.[1])
20
+ if (status) {
21
+ return status === 408 || status === 429 || status >= 500
22
+ }
23
+
24
+ return (
25
+ ['ECONNRESET', 'ECONNREFUSED', 'EHOSTUNREACH', 'ENETUNREACH', 'ETIMEDOUT', 'EAI_AGAIN'].includes(error?.code) ||
26
+ /socket hang up|network|timed? ?out/i.test(error?.message ?? '')
27
+ )
28
+ }
29
+
30
+ function sleep(delayMs) {
31
+ return new Promise((resolve) => setTimeout(resolve, delayMs))
32
+ }
33
+
34
+ async function downloadWithRetry(
35
+ operation,
36
+ {
37
+ maxAttempts = DOWNLOAD_MAX_ATTEMPTS,
38
+ baseDelayMs = DOWNLOAD_RETRY_BASE_DELAY_MS,
39
+ sleepFn = sleep,
40
+ onRetry = () => {},
41
+ } = {}
42
+ ) {
43
+ for (let attempt = 1; ; attempt++) {
44
+ try {
45
+ return await operation()
46
+ } catch (error) {
47
+ if (attempt >= maxAttempts || !isTransientDownloadError(error)) {
48
+ throw error
49
+ }
50
+
51
+ const delayMs = baseDelayMs * 2 ** (attempt - 1)
52
+ onRetry(error, attempt, delayMs)
53
+ await sleepFn(delayMs)
54
+ }
55
+ }
56
+ }
57
+
58
+
15
59
  const tmpDir = tmpdir();
16
60
 
17
61
  const error = (msg) => {
@@ -230,7 +274,13 @@ class Package {
230
274
  console.error(`Downloading release from ${this.url}`);
231
275
  }
232
276
 
233
- return download(this.url)
277
+ return downloadWithRetry(() => download(this.url), {
278
+ onRetry: (error, attempt, delayMs) => {
279
+ console.error(
280
+ `Download attempt ${attempt} failed (${error.message}); retrying in ${delayMs}ms...`,
281
+ );
282
+ },
283
+ })
234
284
  .then((res) => {
235
285
  return new Promise((resolve, reject) => {
236
286
  mkdtemp(`${tmpDir}${sep}`, (err, directory) => {