Dashboard › cli › Session 0sQiitcwQJb5
0sQiitcwQJb5McnMTPerform an independent, read-only security/pentest review of getsentry/cli PR #1569 pinned to exact base ec83887a16f780f32fba4b7d710bad262dba3a22 and exact head 723375b6ce4e37884e760fa0f848e7e7e3effcc9 in worktree /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade. Do not modify files, branches, commits, or PR state. Inspect the complete base..head diff and surrounding code/tests. Focus on supply-chain and network trust boundaries: source fallback only on genuine HTTP 404, no downgrade/fail-open behavior on 401/403/429/5xx/transport/malformed data, source affinity preventing mixed-repository binary/patch/changelog operations, GitHub tag-prefix and release metadata validation, GHCR token scoping and redirect authorization stripping, SHA-256/delta integrity, untrusted URLs/JSON/tags, cancellation, cache behavior, and installer remaining unchanged. Report findings first with severity and exact file:line evidence plus deterministic regression for each defect. If none, state PASS with residual risks. End exactly MERGE or DO-NOT-MERGE.
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade</path> <type>directory</type> <entries> .craft.yml .git .github/ .gitignore .lore.md .nojekyll .npmrc AGENTS.md apps/ CHANGELOG.md codemods/ docs/ LICENSE.md node_modules/ package.json packages/ pnpm-lock.yaml pnpm-workspace.yaml README.md (19 entries) </entries>
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/AGENTS.md</path> <type>file</type> <content> 1: # Jared (Outpost agent) 2: 3: Autonomous GitHub coding agent. Work in `/workspace/repo`. 4: 5: ## Model tiers 6: 7: The primary model is chosen per event (see `src/agents/models.ts`): heavy for 8: code-producing situations, cheaper for lightweight ones. 9: 10: | Role | Subagent | Model | 11: | --- | --- | --- | 12: | Triage / plan / review (heavy) | (primary Jared) | Claude Opus 4.8 | 13: | Triage / plan / review (light) | (primary Jared) | xAI Grok 4.3 | 14: | Explore | `explore` | OpenAI gpt-5-mini | 15: | Implement | `implement` | Moonshot kimi-k2.7-code | 16: | Ship (commit/push/PR) | `ship` | xAI Grok (`grok-build-0.1`) | 17: 18: Pipeline: triage → explore → plan → implement → review → ship. 19: (`worker` is a deprecated alias of `implement`.) 20: 21: Operators also talk to Jared directly from the Outpost dashboard. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/.lore.md</path>
<type>file</type>
<content>
1: <!-- Managed by lore (https://github.com/BYK/loreai) — manual edits are imported on next session. -->
2:
3: ## Long-term Knowledge
4:
5: ### Architecture
6:
7: <!-- lore:01a02357-f0a8-7fdb-9e9d-b0710cfe65c9 -->
8: * **API output binary and HTTP contracts**: Always preserve strict output-mode and binary-safety contracts. `sentry api` must preserve `rawApiRequest()` status text; empty textual non-2xx bodies must produce an HTTP status/request fallback, and JSON mode must expose `{status, statusText, body}` so empty success and error responses are distinguishable. Binary `Uint8Array` successes bypass formatters and remain raw; binary errors emit only status/content-type/byte-count summaries, never bytes or JSON coercion. Prompts must never block scripted runs or interleave with stdout JSON.
9:
10: <!-- ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/package.json</path>
<type>file</type>
<content>
1: {
2: "name": "toolkit-cli-workspace",
3: "version": "0.0.0",
4: "private": true,
5: "type": "module",
6: "packageManager": "pnpm@10.11.0",
7: "engines": {
8: "node": ">=20.0"
9: },
10: "scripts": {
11: "tsx": "pnpm --filter sentry run tsx",
12: "cli": "pnpm --filter sentry run cli",
13: "dev": "pnpm --filter sentry run dev",
14: "build": "pnpm --filter sentry run build",
15: "build:all": "pnpm --filter sentry run build:all",
16: "bundle": "pnpm --filter sentry run bundle",
17: "typecheck": "pnpm --filter sentry run typecheck",
18: "lint": "pnpm --filter sentry run lint",
19: "lint:fix": "pnpm --filter sentry run lint:fix",
20: "test": "pnpm --filter sentry run test",
21: "test:unit": "pnpm --filter sentry run test:unit",
22: "test:changed": "pnpm --filter ...
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/AGENTS.md /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/AGENTS.md
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/AGENTS.md</path> <type>file</type> <content> 1: # AGENTS.md 2: 3: Guidelines for AI agents working in this codebase. 4: 5: ## Project Overview 6: 7: **Sentry CLI** is a command-line interface for [Sentry](https://sentry.io), built with Node.js, [pnpm](https://pnpm.io), and [Stricli](https://bloomberg.github.io/stricli/). It lives in `packages/cli` of a pnpm workspace; the docs site lives in `apps/cli-docs`. ...
Found 5 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/AGENTS.md:
Line 284: if (flags["dry-run"]) { yield preview; return; }
Line 507: ├── UpgradeError (exitCode=50 — upgrade failures)
Line 1008: 2. **Rebase before review.** A PR that is many commits behind `main` may fail CI
Line 1011: exists before requesting review. Verify against current `main`, not the
Line 1017: deliberate. Do not bundle UX opinions into bug-fix PRs; they waste review
## feat/toolkit-bridge-upgrade...origin/feat/toolkit-bridge-upgrade 723375b6ce4e37884e760fa0f848e7e7e3effcc9 ec83887a16f780f32fba4b7d710bad262dba3a22 723375b6ce4e37884e760fa0f848e7e7e3effcc9 ec83887a16f780f32fba4b7d710bad262dba3a22
M packages/cli/src/commands/cli/upgrade.ts M packages/cli/src/lib/binary.ts M packages/cli/src/lib/delta-upgrade.ts M packages/cli/src/lib/ghcr.ts M packages/cli/src/lib/release-notes.ts M packages/cli/src/lib/upgrade.ts M packages/cli/src/lib/version-check.ts M packages/cli/test/commands/cli/upgrade.test.ts M packages/cli/test/lib/binary.test.ts M packages/cli/test/lib/delta-upgrade.mocked.test.ts M packages/cli/test/lib/delta-upgrade.test.ts M packages/cli/test/lib/ghcr.test.ts M packages/cli/test/lib/release-notes.test.ts M packages/cli/test/lib/upgrade.test.ts M packages/cli/test/lib/version-check.test.ts packages/cli/src/commands/cli/upgrade.ts | 114 +++++--- packages/cli/src/lib/binary.ts | 154 +++++++++- packages/cli/src/lib/delta-upgrade.ts | 139 +++++++-- packages/cli/src/lib/ghcr.ts | 68 +++-- packages/cli/src/lib/release-notes.ts | 119 +++++--- packages/cli/src/lib/upgrade.ts ...
3417558ce792780a5ada5fef4c6a57cd9a2f7106236abc803b3cea31fe79efbf - 723375b6c (HEAD -> feat/toolkit-bridge-upgrade, origin/feat/toolkit-bridge-upgrade) fix(cli): address upgrade review findings 9c874298e fix(cli): address toolkit bridge review 04867eaf9 feat(cli): add toolkit upgrade bridge
diff --git a/packages/cli/src/commands/cli/upgrade.ts b/packages/cli/src/commands/cli/upgrade.ts index 8d8282368..610a12025 100644 --- a/packages/cli/src/commands/cli/upgrade.ts +++ b/packages/cli/src/commands/cli/upgrade.ts @@ -1,414 +1,450 @@ /** * sentry cli upgrade * * Self-update the Sentry CLI to the latest or a specific version. * After upgrading, spawns the NEW binary with `cli setup` to update * completions, agent skills, and record installation metadata. * * Supports two release channels: * - stable (default): tracks the latest GitHub release * - nightly: tracks the rolling nightly prerelease built from main * * The channel can be set via --channel or by passing "nightly"/"stable" * as the version argument. The choice is persisted in the local database * so that subsequent bare `sentry cli upgrade` calls use the same channel. ...
diff --git a/packages/cli/src/lib/binary.ts b/packages/cli/src/lib/binary.ts
index 75582612a..61e10568d 100644
--- a/packages/cli/src/lib/binary.ts
+++ b/packages/cli/src/lib/binary.ts
@@ -25,287 +25,429 @@ import {
} from "./custom-ca.js";
import { stringifyUnknown, UpgradeError } from "./errors.js";
import { logger } from "./logger.js";
import { isProcessRunning } from "./process-utils.js";
/** Known directories where the curl installer may place the binary */
export const KNOWN_CURL_DIRS = [".local/bin", "bin", ".sentry/bin"];
/**
* Whether the current platform's filesystem is case-insensitive by default
* (Windows, macOS). Resolved once at module load — `process.platform` never
* changes at runtime.
*/
const IS_CASE_INSENSITIVE_FS =
process.platform === "win32" || process.platform === "darwin";
/**
* Legacy install directory (relative to home) that predates the XDG layout.
* The curl installer used to drop the binary here; migration moves it out. ...
diff --git a/packages/cli/src/lib/delta-upgrade.ts b/packages/cli/src/lib/delta-upgrade.ts
index ec709b8c8..37f4b7d54 100644
--- a/packages/cli/src/lib/delta-upgrade.ts
+++ b/packages/cli/src/lib/delta-upgrade.ts
@@ -1,388 +1,458 @@
/** Delta upgrade discovery and application backed by binpatch. */
import { join } from "node:path";
// biome-ignore lint/performance/noNamespaceImport: Sentry SDK recommends namespace import
import * as Sentry from "@sentry/node-core/light";
import {
applyPatchChainInMemory,
extractStableChain as binpatchExtractStableChain,
filterAndSortChainTags as binpatchFilterAndSortChainTags,
validateChainStep as binpatchValidateChainStep,
type DeltaTelemetry,
type DeltaUnavailableReason,
type ExtractStableChainOpts,
type GitHubRelease,
getPatchFromVersion,
getPatchTargetSha256,
ghcrSource,
githubReleaseSource,
type InstrumentHook,
MAX_NIGHTLY_CHAIN_DEPTH,
makeCache,
OciClient,
type OciManifest,
...
No files found
No files found
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/node_modules/binpatch</path> <type>directory</type> <entries> dist/ LICENSE package.json README.md (4 entries) </entries>
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/node_modules/binpatch/dist/index.d.ts /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/node_modules/binpatch/dist/index.js.map /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/node_modules/binpatch/dist/index.js
Found 4 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/node_modules/binpatch/dist/index.js:
Line 610: function makeCache(cacheDir) {
Line 654: async function resolveAndApply(opts) {
Line 707: return await cache.load(currentVersion, targetVersion);
Line 1318: export { BinpatchError, MAX_NIGHTLY_CHAIN_DEPTH, MAX_OUTPUT_SIZE, MAX_STABLE_CHAIN_DEPTH, OciClient, PATCH_TAG_PREFIX, SIZE_THRESHOLD_RATIO, addDiffChunk, applyPatch, applyPatchChainInMemory, applyPatchToMemory, chainFileName, extractSha256, extractStableChain, filterAndSortChainTags, getPatchFromVersion, getPatchTargetSha256, getStableTargetSha256, ghcrSource, githubReleaseSource, makeCache, offtin, parsePatchHeader, patchFileName, resolveAndApply, safeProgress, validateChainStep };
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/node_modules/binpatch/dist/index.js</path>
<type>file</type>
<content>
560: if (!result) continue;
561: if (now - result.meta.cachedAt > CACHE_MAX_AGE_MS) {
562: expiredMetas.push(result.meta);
563: } else {
564: for (const step of result.meta.patches) {
565: livePatchFiles.add(patchFileName(step.fromVersion, step.toVersion));
566: }
567: }
568: }
569: const deletions = [];
570: for (const meta of expiredMetas) {
571: for (const step of meta.patches) {
572: const name = patchFileName(step.fromVersion, step.toVersion);
573: if (!livePatchFiles.has(name)) {
574: deletions.push(unlink(join(cacheDir, name)).catch(() => {
575: }));
576: }
577: }
578: deletions.push(
579: unlink(
580: join(cacheDir, chainFileName(meta.fromVersion, meta.toVersion))
581: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/node_modules/binpatch/dist/index.js</path>
<type>file</type>
<content>
400: await reader.close();
401: }
402: }
403: function applyPatch(oldPath, patchData, destPath) {
404: return applyPatchChainInMemory(oldPath, [patchData], destPath);
405: }
406: var CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
407: var MAX_CHAIN_WALK_DEPTH = 10;
408: function sanitizeVersion(version) {
409: return version.replace(/[^a-zA-Z0-9.-]/g, "_");
410: }
411: function patchFileName(fromVersion, toVersion) {
412: return `${sanitizeVersion(fromVersion)}-${sanitizeVersion(toVersion)}.patch`;
413: }
414: function chainFileName(fromVersion, toVersion) {
415: return `chain-${sanitizeVersion(fromVersion)}-${sanitizeVersion(toVersion)}.json`;
416: }
417: function isNotFound(err) {
418: return err instanceof Error && "code" in err && err.code === "ENOENT";
419: }
420: async ...
diff --git a/packages/cli/src/lib/ghcr.ts b/packages/cli/src/lib/ghcr.ts index 43d6deb7e..5bc671a6a 100644 --- a/packages/cli/src/lib/ghcr.ts +++ b/packages/cli/src/lib/ghcr.ts @@ -1,520 +1,546 @@ /** * GHCR (GitHub Container Registry) Client * * Encapsulates the OCI download protocol for fetching nightly CLI binaries * from ghcr.io/getsentry/cli. Nightly builds are pushed as OCI artifacts * via ORAS with the version baked into the manifest annotation. * * Key design decisions: * - Anonymous access: nightly package is public; no token needed beyond the * standard ghcr.io anonymous token exchange. * - Version discovery from manifest annotation: `annotations.version` in the * OCI manifest holds the nightly version. Checking the latest version only * requires a token exchange + manifest fetch (2 HTTP requests total). * - Redirect quirk: ghcr.io blob downloads return 307 to Azure Blob Storage. ...
diff --git a/packages/cli/src/lib/upgrade.ts b/packages/cli/src/lib/upgrade.ts
index 945adc48d..64228ba41 100644
--- a/packages/cli/src/lib/upgrade.ts
+++ b/packages/cli/src/lib/upgrade.ts
@@ -1,209 +1,248 @@
/**
* Upgrade Module
*
* Detects how the CLI was installed and provides self-upgrade functionality.
* Binary management helpers (download URLs, locking, replacement) live in
* binary.ts and are shared with the setup --install flow.
*/
import { spawn } from "node:child_process";
import {
chmodSync,
closeSync,
existsSync,
openSync,
realpathSync,
statSync,
unlinkSync,
writeSync,
} from "node:fs";
import { writeFile } from "node:fs/promises";
import { homedir } from "node:os";
import { dirname, isAbsolute, join, sep } from "node:path";
import { setTimeout } from "node:timers/promises";
+import { valid as semverValid } from "semver";
import {
acquireLock,
cleanupOldBinary,
+ compareVersions,
determineInstallDir,
...
Found 100 matches (more matches available)
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/e2e/completion.test.ts:
Line 65: test("completion exits under 500ms", async () => {
Line 70: // 500ms budget: dev mode ~67ms, CI ~140ms, slow CI runners ~300ms,
Line 73: expect(result.duration).toBeLessThan(500);
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts:
Line 352: status: 500,
Line 358: "Failed to fetch from npm: 500"
Line 828: // Manifest returns 500 (server error, not 404)
Line 829: return new Response(null, { status: 500 });
Line 836: test("does not classify GHCR HTTP 403 as a missing nightly version", async () => {
Line 849: ).rejects.toThrow("HTTP 403");
Line 1578: mockFetch(async () => new Response("Server Error", { status: 500 }));
...
Found 5 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/commands/cli/upgrade.test.ts:
Line 234: // npm registry fallback
Line 378: // No fetch mock needed — error is thrown before any network call
Line 412: test("uses the selected legacy source for the check-mode changelog", async () => {
Line 428: return new Response("Unexpected", { status: 500 });
Line 1155: const err = new Error("EBUSY: resource busy or locked, uv_spawn");
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/commands/cli/upgrade.test.ts</path> <type>file</type> <content> 1: /** 2: * Upgrade Command Tests 3: * 4: * Tests the `sentry cli upgrade` command through Stricli's run(). 5: * Covers resolveTargetVersion branches (check mode, already up-to-date, 6: * version validation) and error paths. 7: * 8: * Status messages go through consola (→ process.stderr). Tests capture stderr 9: * via a spy on process.stderr.write and assert on the collected output. ...
No files found
diff --git a/packages/cli/src/lib/release-notes.ts b/packages/cli/src/lib/release-notes.ts index ddf617535..fb32efd78 100644 --- a/packages/cli/src/lib/release-notes.ts +++ b/packages/cli/src/lib/release-notes.ts @@ -1,119 +1,121 @@ /** * Release Notes Parser & Aggregation * * Extracts user-facing changelog entries from GitHub Release bodies (stable) * or conventional commit messages (nightly). Uses `marked.lexer()` for * AST-based section extraction and produces structured data that can be * re-serialized as filtered markdown for rendering via `renderMarkdown()`. ...
diff --git a/packages/cli/src/lib/version-check.ts b/packages/cli/src/lib/version-check.ts
index c69ff0466..34393b1c7 100644
--- a/packages/cli/src/lib/version-check.ts
+++ b/packages/cli/src/lib/version-check.ts
@@ -1,411 +1,416 @@
/**
* Background version check for "new version available" notifications.
*
* For nightly builds (CLI_VERSION contains "-dev.<timestamp>"), checks GHCR for the
* latest nightly version via the OCI manifest annotation. For stable builds,
* checks GitHub Releases. Results are cached in the database and shown on
* subsequent runs.
*/
// biome-ignore lint/performance/noNamespaceImport: Sentry SDK recommends namespace import
import * as Sentry from "@sentry/node-core/light";
import { compare as semverCompare } from "semver";
+import type { UpgradeSource } from "./binary.js";
import { CLI_VERSION } from "./constants.js";
import { getReleaseChannel } from "./db/release-channel.js";
import {
getVersionCheckInfo,
markUpdateNotified,
...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/binary.test.ts</path>
<type>file</type>
<content>
1: /**
2: * Binary Management Tests
3: *
4: * Tests for shared binary helpers: install directory selection, paths,
5: * download URLs, locking, and binary installation.
6: */
7:
8: import {
9: chmodSync,
10: mkdirSync,
11: readFileSync,
12: realpathSync,
13: rmSync,
14: symlinkSync,
15: writeFileSync,
16: } from "node:fs";
17: import { access, readFile, writeFile } from "node:fs/promises";
18: import { join, sep } from "node:path";
19: import { afterEach, beforeEach, describe, expect, test } from "vitest";
20: import {
21: acquireLock,
22: compareVersions,
23: determineInstallDir,
24: fetchWithUpgradeError,
25: getBinaryDownloadUrl,
26: getBinaryFilename,
27: getBinaryPaths,
28: getGitHubReleaseByTagUrl,
29: getLegacyInstallDirs,
30: getPlatformBinaryName,
31: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts</path>
<type>file</type>
<content>
700:
701: const exists = await versionExists("brew", "1.0.0");
702: expect(exists).toBe(true);
703: });
704:
705: test("checks GitHub for brew method - version does not exist", async () => {
706: mockFetch(async () => new Response(null, { status: 404 }));
707:
708: const exists = await versionExists("brew", "99.99.99");
709: expect(exists).toBe(false);
710: });
711:
712: test("checks npm for yarn method", async () => {
713: mockFetch(async () => new Response(null, { status: 200 }));
714:
715: const exists = await versionExists("yarn", "1.0.0");
716: expect(exists).toBe(true);
717: });
718:
719: test("throws on network failure", async () => {
720: mockFetch(async () => {
721: throw new TypeError("fetch failed");
722: });
723:
724: await ...
Found 10 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts:
Line 144: resolveExistingUpgradeVersion,
Line 193: test("selects the latest CLI-prefixed Toolkit release", async () => {
Line 214: test("falls back to the legacy latest release only on Toolkit HTTP 404", async () => {
Line 598: test("probes prefixed Toolkit tags and retains the selected source", async () => {
Line 613: test("falls back to an unprefixed legacy tag on Toolkit HTTP 404", async () => {
Line 636: await expect(resolveExistingUpgradeVersion("1.0.0")).rejects.toThrow(
Line 641: test("does not fall back from an explicit selected source", async () => {
Line 1663: test("falls back to legacy when the Toolkit nightly manifest returns 404", async () => {
Line 1708: test("does not fall back from a non-404 Toolkit nightly failure", async () => {
Line 1730: test("does not fall ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts</path>
<type>file</type>
<content>
150:
151: // Store original fetch for restoration
152: let originalFetch: typeof globalThis.fetch;
153:
154: /** Helper to mock fetch without TypeScript errors about missing Bun-specific properties */
155: function mockFetch(
156: fn: (url: string | URL | Request, init?: RequestInit) => Promise<Response>
157: ): void {
158: globalThis.fetch = fn as typeof globalThis.fetch;
159: }
160:
161: beforeEach(() => {
162: originalFetch = globalThis.fetch;
163: });
164:
165: afterEach(() => {
166: globalThis.fetch = originalFetch;
167: });
168:
169: describe("parseInstallationMethod", () => {
170: test("parses valid methods", () => {
171: expect(parseInstallationMethod("curl")).toBe("curl");
172: expect(parseInstallationMethod("brew")).toBe("brew");
173: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts</path>
<type>file</type>
<content>
560: mockFetch(async (url) => {
561: const urlStr = String(url);
562: if (urlStr === "https://api.github.com/repos/getsentry/toolkit") {
563: return new Response(null, { status: 200 });
564: }
565: if (urlStr.includes("ghcr.io/token")) {
566: return new Response(JSON.stringify({ token: "tok" }), { status: 200 });
567: }
568: if (urlStr.includes("/manifests/nightly")) {
569: return new Response(
570: JSON.stringify({
571: annotations: { version: "0.0.0-dev.1740393600" },
572: }),
573: { status: 200 }
574: );
575: }
576: return new Response("Not Found", { status: 404 });
577: });
578:
579: const version = await fetchLatestVersion("npm", "nightly");
580: ...
Found 21 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/node_modules/binpatch/dist/index.js:
Line 633: offline_cache_miss: "Cannot update offline \u2014 no pre-downloaded patch is available."
Line 890: async downloadBlob(token, digest, signal) {
Line 923: // Storage) download. Passing the raw `signal` alone means no timeout
Line 932: `Failed to download from blob storage: ${msg}`
Line 938: `Blob storage download failed: HTTP ${redirectResponse.status}`
Line 949: async downloadBlobBuffer(token, digest, signal) {
Line 950: const response = await this.downloadBlob(token, digest, signal);
Line 1104: const downloadResults = await Promise.all(
Line 1107: "download-patch",
Line 1108: () => client.downloadBlobBuffer(token, digest, signal).then((buf) => new Uint8Array(buf))
Line 1113: let downloadedSize = 0;
...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/node_modules/binpatch/dist/index.js</path>
<type>file</type>
<content>
1140: ),
1141: i(
1142: "list-patch-tags",
1143: () => client.listTags(token, PATCH_TAG_PREFIX, signal)
1144: )
1145: ]);
1146: const gzLayer = targetManifest.layers.find(
1147: (l) => l.annotations?.["org.opencontainers.image.title"] === `${binaryName}.gz`
1148: );
1149: if (!gzLayer) {
1150: report?.("malformed_chain");
1151: return null;
1152: }
1153: return await resolveNightlyChain({
1154: token,
1155: currentVersion,
1156: targetVersion,
1157: fullGzSize: gzLayer.size,
1158: preloadedTags: patchTags,
1159: signal,
1160: report
1161: });
1162: } catch (error) {
1163: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/node_modules/binpatch/dist/index.js</path>
<type>file</type>
<content>
747: // src/sources/oci.ts
748: var REQUEST_TIMEOUT = 1e4;
749: var MAX_RETRIES = 1;
750: var BLOB_TIMEOUT = 3e4;
751: var TAGS_PAGE_SIZE = 100;
752: var OCI_MANIFEST_TYPE = "application/vnd.oci.image.manifest.v1+json";
753: function isRetryableError(error) {
754: if (error.name === "TimeoutError" || error.name === "AbortError") {
755: return true;
756: }
757: const msg = error.message.toLowerCase();
758: return msg.includes("timeout") || msg.includes("econnreset") || msg.includes("econnrefused") || msg.includes("network") || msg.includes("fetch failed");
759: }
760: function buildSignal(timeout, externalSignal) {
761: const timeoutSignal = AbortSignal.timeout(timeout);
762: return externalSignal ? AbortSignal.any([timeoutSignal, externalSignal]) : timeoutSignal;
763: }
764: ...
diff --git a/packages/cli/test/lib/binary.test.ts b/packages/cli/test/lib/binary.test.ts
index 6fcfb3b1a..5eaa53474 100644
--- a/packages/cli/test/lib/binary.test.ts
+++ b/packages/cli/test/lib/binary.test.ts
@@ -1,103 +1,229 @@
/**
* Binary Management Tests
*
* Tests for shared binary helpers: install directory selection, paths,
* download URLs, locking, and binary installation.
*/
import {
chmodSync,
mkdirSync,
readFileSync,
realpathSync,
rmSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import { access, readFile, writeFile } from "node:fs/promises";
import { join, sep } from "node:path";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import {
acquireLock,
compareVersions,
determineInstallDir,
fetchWithUpgradeError,
getBinaryDownloadUrl,
getBinaryFilename,
getBinaryPaths,
+ getGitHubReleaseByTagUrl,
getLegacyInstallDirs,
getPlatformBinaryName,
installBinary,
isDowngrade,
...
diff --git a/packages/cli/test/lib/upgrade.test.ts b/packages/cli/test/lib/upgrade.test.ts
index 7c6f63a4b..bffea175b 100644
--- a/packages/cli/test/lib/upgrade.test.ts
+++ b/packages/cli/test/lib/upgrade.test.ts
@@ -98,201 +98,245 @@ const { spawnImpl } = vi.hoisted(() => ({
fn: (() => {
// placeholder — replaced per-test
}) as (cmd: string, args: string[], opts: object) => FakeProc,
},
}));
// Initialize with the real default now that fakeProcess is defined
spawnImpl.fn = () => fakeProcess(0);
vi.mock("node:child_process", async (importOriginal) => {
const orig = await importOriginal<typeof import("node:child_process")>();
return {
...orig,
spawn: (cmd: string, args: string[], opts: object) =>
spawnImpl.fn(cmd, args, opts),
};
});
// Dynamic imports: must run AFTER vi.mock() so upgrade.ts picks up the
// mocked spawn.
import { isEnoentSpawnError } from "../../src/commands/cli/upgrade.js";
import {
acquireLock,
...
diff --git a/packages/cli/test/lib/delta-upgrade.test.ts b/packages/cli/test/lib/delta-upgrade.test.ts
index 3752d4ae6..993e7c89a 100644
--- a/packages/cli/test/lib/delta-upgrade.test.ts
+++ b/packages/cli/test/lib/delta-upgrade.test.ts
@@ -1,75 +1,84 @@
/**
* Unit Tests for Delta Upgrade Module
*
* Tests the exported pure-computation functions that drive chain resolution
* for both stable (GitHub Releases) and nightly (GHCR) channels, plus
* async orchestration functions tested via fetch mocking.
*/
import { createHash } from "node:crypto";
import { existsSync, unlinkSync } from "node:fs";
import { access, readFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
-import { afterEach, beforeEach, describe, expect, test } from "vitest";
-import { getPlatformBinaryName } from "../../src/lib/binary.js";
+import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
+import {
+ getPlatformBinaryName,
+ ...
Found 1 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/binary.ts:
Line 472: export function getGitHubHeaders(): Record<string, string> {
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/binary.ts</path>
<type>file</type>
<content>
1: /**
2: * Binary Management
3: *
4: * Shared utilities for installing, replacing, and managing the CLI binary.
5: * Used by both `setup --install` (fresh installs) and `upgrade` (self-updates).
6: */
7:
8: import { spawnSync } from "node:child_process";
9: import {
10: existsSync,
11: mkdirSync,
12: readFileSync,
13: renameSync,
14: unlinkSync,
15: writeFileSync,
16: } from "node:fs";
17: import { chmod, copyFile, mkdir, realpath, unlink } from "node:fs/promises";
18: import { delimiter, dirname, isAbsolute, join, resolve, sep } from "node:path";
19: import { compare as semverCompare } from "semver";
20: import { getUserAgent } from "./constants.js";
21: import {
22: buildTlsErrorDetail,
23: customFetch,
24: isTlsCertError,
25: } from "./custom-ca.js";
26: import { stringifyUnknown, ...
diff --git a/packages/cli/test/lib/ghcr.test.ts b/packages/cli/test/lib/ghcr.test.ts
index e5c81d532..b971c7e1e 100644
--- a/packages/cli/test/lib/ghcr.test.ts
+++ b/packages/cli/test/lib/ghcr.test.ts
@@ -1,195 +1,222 @@
/**
* GHCR Client Tests
*
* Unit tests for the GHCR/OCI download protocol helpers.
* All HTTP calls are mocked via globalThis.fetch to avoid network access.
*/
import { afterEach, beforeEach, describe, expect, test } from "vitest";
+import { UPGRADE_SOURCES } from "../../src/lib/binary.js";
import { UpgradeError } from "../../src/lib/errors.js";
import {
downloadLayerBlob,
downloadNightlyBlob,
fetchManifest,
fetchNightlyManifest,
findLayerByFilename,
GHCR_REPO,
GHCR_TAG,
+ GhcrManifestHttpError,
getAnonymousToken,
getNightlyVersion,
listTags,
type OciManifest,
} from "../../src/lib/ghcr.js";
/** Store original fetch for restoration */
let originalFetch: typeof globalThis.fetch;
/** Helper to mock fetch without ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/ghcr.test.ts</path>
<type>file</type>
<content>
240: findLayerByFilename(manifest, "sentry-freebsd-x64.gz")
241: ).toThrow(UpgradeError);
242: expect(() =>
243: findLayerByFilename(manifest, "sentry-freebsd-x64.gz")
244: ).toThrow("No nightly build found for sentry-freebsd-x64.gz");
245: });
246:
247: test("throws UpgradeError when layer has no annotations", () => {
248: const manifest = makeManifest({
249: layers: [
250: {
251: digest: "sha256:noannotations",
252: mediaType: "application/octet-stream",
253: size: 100,
254: // no annotations
255: },
256: ],
257: });
258: expect(() => findLayerByFilename(manifest, "sentry-linux-x64.gz")).toThrow(
259: UpgradeError
260: );
261: });
262: });
263:
264: describe("downloadNightlyBlob", () ...
diff --git a/packages/cli/test/commands/cli/upgrade.test.ts b/packages/cli/test/commands/cli/upgrade.test.ts
index ca9688e46..b79a9c342 100644
--- a/packages/cli/test/commands/cli/upgrade.test.ts
+++ b/packages/cli/test/commands/cli/upgrade.test.ts
@@ -140,149 +140,154 @@ function createMockContext(
return true;
},
},
stdin: process.stdin,
setFlags: () => {
// no-op for tests
},
} as unknown as SentryContext;
return {
context,
// Combine stderr (progress) and stdout (rendered result) so assertions
// work regardless of which stream a message goes to
getOutput: () => stderrChunks.join("") + stdoutChunks.join(""),
errors,
restore: () => {
process.stderr.write = origStderrWrite;
process.stdout.write = origStdoutWrite;
if (origPlain === undefined) {
delete process.env.SENTRY_PLAIN_OUTPUT;
} else {
process.env.SENTRY_PLAIN_OUTPUT = origPlain;
}
},
...
Found 18 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts:
Line 598: test("probes prefixed Toolkit tags and retains the selected source", async () => {
Line 641: test("does not fall back from an explicit selected source", async () => {
Line 1127: "https://github.com/getsentry/toolkit/releases/download/"
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/ghcr.test.ts:
Line 96: test("uses the selected source's GHCR repository", async () => {
Line 171: test("uses the selected source's GHCR repository", async () => {
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/delta-upgrade.test.ts:
Line 951: const patchUrl = ...
diff --git a/packages/cli/test/lib/delta-upgrade.mocked.test.ts b/packages/cli/test/lib/delta-upgrade.mocked.test.ts
index cd0a2fddc..f6186ab59 100644
--- a/packages/cli/test/lib/delta-upgrade.mocked.test.ts
+++ b/packages/cli/test/lib/delta-upgrade.mocked.test.ts
@@ -52,125 +52,125 @@ import {
let originalFetch: typeof globalThis.fetch;
beforeEach(() => {
originalFetch = globalThis.fetch;
});
afterEach(() => {
globalThis.fetch = originalFetch;
});
function mockFetch(
fn: (url: string | URL | Request, init?: RequestInit) => Promise<Response>
): void {
globalThis.fetch = fn as typeof globalThis.fetch;
}
// ============================================================================
// Helpers
// ============================================================================
const BINARY_NAME = getPlatformBinaryName();
function versionHex(version: string): string {
return Array.from(version)
.map((c) => c.charCodeAt(0).toString(16).padStart(2, ...
diff --git a/packages/cli/test/lib/release-notes.test.ts b/packages/cli/test/lib/release-notes.test.ts
index 78963b471..d60066802 100644
--- a/packages/cli/test/lib/release-notes.test.ts
+++ b/packages/cli/test/lib/release-notes.test.ts
@@ -1,56 +1,59 @@
/**
* Unit Tests for Release Notes Parser & Aggregation
*
* Tests core invariants (section extraction, version filtering, truncation)
* that are hard to express as property-based tests due to format specifics.
*
* Core random-input invariants (category validity, filtering, commit parsing)
* are tested via property-based tests in release-notes.property.test.ts.
*/
import { marked } from "marked";
-import { describe, expect, test } from "vitest";
+import { afterEach, beforeEach, describe, expect, test } from "vitest";
+import { UPGRADE_SOURCES } from "../../src/lib/binary.js";
import type { GitHubRelease } from "../../src/lib/delta-upgrade.js";
import {
buildChangelogSummary,
type ChangeCategory,
...
diff --git a/packages/cli/test/lib/version-check.test.ts b/packages/cli/test/lib/version-check.test.ts
index 20172cd83..dd047a5cb 100644
--- a/packages/cli/test/lib/version-check.test.ts
+++ b/packages/cli/test/lib/version-check.test.ts
@@ -1,46 +1,49 @@
/**
* Version Check Logic Tests
*/
import { setTimeout as sleep } from "node:timers/promises";
-import { afterEach, beforeEach, describe, expect, test } from "vitest";
+import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
+import { UPGRADE_SOURCES } from "../../src/lib/binary.js";
import { setReleaseChannel } from "../../src/lib/db/release-channel.js";
import {
getVersionCheckInfo,
setVersionCheckInfo,
} from "../../src/lib/db/version-check.js";
+// biome-ignore lint/performance/noNamespaceImport: Vitest requires the module namespace to spy on an ESM export
+import * as deltaUpgrade from "../../src/lib/delta-upgrade.js";
import {
ApiError,
ContextError,
ValidationError,
} from ...
Found 21 matches /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts: Line 462: export async function fetchLatestFromGitHub( Line 561: export async function fetchLatestNightlyVersion( Line 590: return fetchLatestNightlyVersion(); Line 593: ? fetchLatestFromGitHub() Line 692: export async function versionExists( Line 905: const url = getBinaryDownloadUrl(version, source); Line 1042: export async function downloadBinaryToTemp( Line 1141: return await attemptDeltaUpgrade( Line 1280: export async function executeUpgrade( Line 1290: return downloadBinaryToTemp( /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/delta-upgrade.ts: Line 595: export function attemptDeltaUpgrade( Line 688: export function prefetchNightlyPatches( Line 696: export function ...
Found 37 matches /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts: Line 42: PRIMARY_UPGRADE_SOURCE, Line 45: UPGRADE_SOURCES, Line 440: sources: readonly UpgradeSource[] = UPGRADE_SOURCES Line 469: source ? [source] : UPGRADE_SOURCES Line 516: sources: readonly UpgradeSource[] = UPGRADE_SOURCES Line 568: source ? [source] : UPGRADE_SOURCES Line 616: UPGRADE_SOURCES Line 864: source: UpgradeSource = PRIMARY_UPGRADE_SOURCE Line 903: source: UpgradeSource = PRIMARY_UPGRADE_SOURCE Line 1047: source: UpgradeSource = PRIMARY_UPGRADE_SOURCE Line 1139: source: UpgradeSource = PRIMARY_UPGRADE_SOURCE Line 1164: source: UpgradeSource = PRIMARY_UPGRADE_SOURCE Line 1286: source: UpgradeSource = ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/ghcr.ts</path>
<type>file</type>
<content>
320: * @param filename - Filename to find (e.g., "sentry-linux-x64.gz")
321: * @returns Matching layer
322: * @throws {UpgradeError} When no layer matches the filename
323: */
324: export function findLayerByFilename(
325: manifest: OciManifest,
326: filename: string
327: ): OciLayer {
328: const layer = manifest.layers.find(
329: (l) => l.annotations?.["org.opencontainers.image.title"] === filename
330: );
331: if (!layer) {
332: throw new UpgradeError(
333: "version_not_found",
334: `No nightly build found for ${filename}`
335: );
336: }
337: return layer;
338: }
339:
340: /**
341: * Download a nightly binary blob from GHCR and write it to disk.
342: *
343: * The blob endpoint returns a 307 redirect to a signed Azure Blob Storage URL. ...
Found 19 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/commands/cli/upgrade.test.ts:
Line 34: import { CLI_VERSION } from "../../../src/lib/constants.js";
Line 298: test("shows 'already on the target version' when current equals latest", async () => {
Line 299: mockGitHubVersion(CLI_VERSION);
Line 314: expect(combined).toContain(CLI_VERSION);
Line 361: mockGitHubVersion(CLI_VERSION);
Line 477: mockGitHubVersion(CLI_VERSION);
Line 487: ["cli", "upgrade", "--method", "curl", `v${CLI_VERSION}`],
Line 536: // CLI_VERSION is "0.0.0-dev" (not matching nightlyVersion), show upgrade hint
Line 566: mockNightlyVersion(CLI_VERSION);
Line 584: mockGitHubVersion(CLI_VERSION);
Line 604: mockNightlyVersion(CLI_VERSION);
Line 624: mockNightlyVersion(CLI_VERSION);
Line 642: mockGitHubVersion(CLI_VERSION);
...
* 🟡 (00:04) `packages/cli/src/lib/ghcr.ts` lines 354-437 defines `downloadNightlyBlob(token, digest, signal?, source: UpgradeSource = PRIMARY_UPGRADE_SOURCE)`: it requests `${GHCR_REGISTRY}/v2/${source.ghcrRepo}/blobs/${digest}` with bearer authorization, `redirect: "manual"`, and `buildSignal(GHCR_BLOB_TIMEOUT, signal)`. GHCR may return the blob directly with HTTP 200 or redirect via HTTP 301/3…
* 🟡 (00:02) `packages/cli/test/lib/version-check.test.ts` now imports Vitest `vi`, `UPGRADE_SOURCES` from `../../src/lib/binary.js`, and the ESM namespace `* as deltaUpgrade` from `../../src/lib/delta-upgrade.js`; the namespace import carries `// biome-ignore lint/performance/noNamespaceImport: Vitest requires the module namespace to spy on an ESM export`. * 🟡 (00:02) `maybeCheckForUpdateInBack…
* 🟡 (00:02) `packages/cli/test/lib/delta-upgrade.mocked.test.ts` migrated stable delta fixtures from legacy `getsentry/cli` release tags to Toolkit-prefixed tags: `"0.14.0"` → `"cli@0.14.0"` and `"0.13.0"` → `"cli@0.13.0"` in the stable resolution, patch-error fallback, and successful stable patch tests. * 🟡 (00:02) In `packages/cli/test/lib/delta-upgrade.mocked.test.ts`, the stable delta patch…
* 🔴 (00:01) User specified that `stripTrailingSep(p: string)` strips a trailing path separator but never strips it from a bare root such as `/`. * 🔴 (00:01) User specified that the source-probe caller receives the successful probe response so it never repeats the probe request. * 🟡 (00:01) `packages/cli/test/lib/ghcr.test.ts` now imports `UPGRADE_SOURCES` from `../../src/lib/binary.js` and `Gh…
* 🟡 (00:00) `packages/cli/test/lib/upgrade.test.ts` now imports `UPGRADE_SOURCES` from `../../src/lib/binary.js` and `resolveExistingUpgradeVersion` from `../../src/lib/upgrade.js`. * 🟡 (00:00) `fetchLatestFromGitHub` test fixtures now use GitHub release arrays with Toolkit-prefixed tags such as `"cli@v2.0.0"` instead of a single release object with `"v2.0.0"`; additional fixtures include malfo…
Date: Sep 9, 2026 * 🟡 (23:57) `fetchLatestVersion("npm", "nightly")` coverage in `/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts` mocks Toolkit repository availability at `https://api.github.com/repos/getsentry/toolkit`, GHCR token `"tok"`, and a nightly manifest annotation `{ version: "0.0.0-dev.1740…
Date: Sep 9, 2026 * 🟡 (23:56) `/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts` defines `mockFetch(fn)` by assigning `globalThis.fetch`, saves the original fetch in `beforeEach`, and restores it in `afterEach`. * 🟡 (23:56) `parseInstallationMethod` tests in `/home/byk/.local/share/opencode/worktree/0e…
* 🟡 (23:55) `packages/cli/src/lib/release-notes.ts` was made source-aware: removed static `GITHUB_RELEASES_URL` usage and imported `getGitHubReleasesUrl`, `PRIMARY_UPGRADE_SOURCE`, and `UpgradeSource` from `./binary.js`. * 🟡 (23:55) Stable changelog retrieval in `packages/cli/src/lib/release-notes.ts` now accepts `FetchChangelogOptions & { source: UpgradeSource }`, fetches releases through `fet…
Date: Sep 9, 2026 * 🔴 (23:54) User specified installation detection must always check Homebrew first because stored install information may be stale—for example, a user may switch from a recorded curl install to Homebrew; the cheap realpath check is authoritative and overrides stale stored data. * 🔴 (23:54) User specified the downloaded-file visibility check must throw an `UpgradeError` when th…
Date: Sep 9, 2026 * 🟡 (23:52) `packages/cli/src/lib/delta-upgrade.ts` replaced the static `GITHUB_RELEASES_URL` import with `getGitHubReleasesUrl()` and added imports for `PRIMARY_UPGRADE_SOURCE` and `UpgradeSource` from `./binary.js`; the direct `GHCR_REPO` import from `./ghcr.js` was removed so delta-upgrade sources can carry their own repository configuration. * 🟡 (23:52) `packages/cli/src/l…
🔴 (23:52) User provided the patch checksum `3417558ce792780a5ada5fef4c6a57cd9a2f7106236abc803b3cea31fe79efbf`. 🔴 (23:52) User provided the branch’s 3-commit sequence, newest first: `723375b6c` (`fix(cli): address upgrade review findings`), `9c874298e` (`fix(cli): address toolkit bridge review`), and `04867eaf9` (`feat(cli): add toolkit upgrade bridge`). 🔴 (23:52) User stated package-manager up…
🔴 (23:50) User directed: Always check `package.json` for the latest scripts. 🔴 (23:50) User stated the workspace uses `pnpm@10.11.0` and requires Node.js `>=20.0`. 🔴 (23:50) User directed that all packages must be added to `devDependencies`, never `dependencies`, because everything is bundled at build time via esbuild; CI enforces this with `pnpm run check:deps`. 🔴 (23:50) User directed: Alwa…
🔴 (23:49) [requested-security-review] User requested an independent, read-only security/pentest review of getsentry/cli PR #1569, pinned to exact base `ec83887a16f780f32fba4b7d710bad262dba3a22` and exact head `723375b6ce4e37884e760fa0f848e7e7e3effcc9`, in worktree `/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade`. 🔴 (23:49) User required …