Skip to content

feat: 远程 RPC 下载支持#186

Merged
AmintaCCCP merged 8 commits into
mainfrom
feat/rpc-download
Jun 1, 2026
Merged

feat: 远程 RPC 下载支持#186
AmintaCCCP merged 8 commits into
mainfrom
feat/rpc-download

Conversation

@AmintaCCCP

@AmintaCCCP AmintaCCCP commented Jun 1, 2026

Copy link
Copy Markdown
Owner

概述

在网络设置中新增远程 RPC 下载配置,支持将 Release 资产下载任务发送到 aria2 远程下载器。

功能

  • 默认关闭:在 设置 > 网络设置 > 网络代理 下方新增「远程下载」配置卡片
  • 可配置项:RPC 地址、端口(默认 6800)、密钥(可选)
  • 测试连接:调用 aria2.getVersion 验证连接,成功显示版本号
  • 下载行为切换:开启后 Release 资产点击下载会发送到远程下载器,关闭则恢复浏览器直接下载
  • 服务未运行提示:aria2 未运行时 toast 提示「远程下载服务未运行,请检查配置」

改动文件

文件 说明
src/types/index.ts 新增 RpcDownloadConfig 接口
src/store/useAppStore.ts 新增 rpcDownloadConfig 状态(7 处 touchpoint)
src/services/rpcDownloadService.ts 新增:测试连接 + 发送下载 API
src/components/settings/NetworkPanel.tsx 新增 RPC 下载配置 UI
src/components/ReleaseCard.tsx 下载链接行为改造
server/src/routes/proxy.ts 新增 4 个 API 端点
server/src/services/proxyService.ts 导出 validateUrl 供复用

安全措施

  • 密钥前端不持久化(IndexedDB),后端 AES-256-GCM 加密存储
  • /api/download/rpc 端点有 SSRF 防护(阻止 localhost、内网 IP)
  • 测试连接 5s 超时,下载转发 10s 超时

测试

  1. 启动 aria2:aria2c --enable-rpc --rpc-listen-port=6800 --rpc-secret=test
  2. 设置中配置 RPC → 测试连接 → 显示版本号
  3. 开启 RPC → Release 页面点击下载 → 任务出现在 aria2
  4. 关闭 aria2 → 点击下载 → toast 提示服务未运行
  5. 关闭 RPC 开关 → 恢复浏览器直接下载

Summary by CodeRabbit

  • New Features

    • Remote RPC Download: configure host/port and optional secret, test connectivity, save settings, and route downloads via the RPC service with per-link status and toasts.
    • Settings syncs with backend when available and reflects whether a secret is stored; secrets are not persisted locally.
  • Bug Fixes

    • URL validation to block invalid/unsafe URLs before remote-download requests and clearer JSON error responses for RPC/connection failures.

Add aria2 JSON-RPC integration to send release asset downloads to a
remote downloader. Default disabled, configurable in Settings > Network.

- Add RpcDownloadConfig type and store state (host, port, secret)
- Add backend endpoints: GET/PUT/test for config, POST for download
- Add frontend service (rpcDownloadService.ts) for API calls
- Add RPC config UI in NetworkPanel below proxy settings
- Modify ReleaseCard to send downloads via RPC when enabled
- Show toast when RPC service is not running
- Encrypt secret with AES-256-GCM in backend, skip persistence in frontend
- Add SSRF protection on download URL validation
- Export validateUrl from proxyService for reuse

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds RPC download: new RpcDownloadConfig type, frontend client (test/send), app-store state and persistence (secret excluded), backend RPC settings/test/download endpoints with URL validation and aria2 JSON-RPC, settings UI to configure/test/save, and ReleaseCard integration to queue remote downloads.

Changes

RPC Download Feature

Layer / File(s) Summary
RPC Download Type Contract
src/types/index.ts
Introduces RpcDownloadConfig interface with enabled, host, port, and optional secret fields.
Frontend RPC Download Service
src/services/rpcDownloadService.ts
Implements testRpcDownload and sendToRpcDownload client functions that POST JSON to backend endpoints with optional Bearer auth, unified error handling, and local result types.
App State Management for RPC Config
src/store/useAppStore.ts
Extends app store with rpcDownloadConfig state and setRpcDownloadConfig action, initial defaults, hydration normalization, migration, and selective persistence excluding secret.
Backend RPC Endpoints and SSRF Protection
server/src/routes/proxy.ts, server/src/services/proxyService.ts
Exports validateUrl and adds endpoints: GET /api/settings/rpc-download, PUT /api/settings/rpc-download, POST /api/settings/rpc-download/test (aria2.getVersion), and POST /api/download/rpc (validate URL then aria2.addUri).
RPC Configuration UI in Settings
src/components/settings/NetworkPanel.tsx
Adds "Remote Download" card with host/port/secret inputs (secret show/hide), Test/Save buttons wired to handlers, backend sync-on-mount, change detection, and a test-result banner showing aria2 version.
RPC Download Integration in Release Card
src/components/ReleaseCard.tsx
Adds RPC download handler, per-link downloading/downloaded state tracking, conditional rendering (RPC-enabled links as buttons with status icons) and success/error toasts (handles unreachable RPC service case).

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ReleaseCard
  participant Frontend as Frontend Service
  participant Backend as Backend API
  participant Aria2
  User->>ReleaseCard: Click RPC download button
  ReleaseCard->>Frontend: sendToRpcDownload(url, filename, apiSecret?)
  Frontend->>Backend: POST /api/download/rpc {url, filename}
  Backend->>Backend: validateUrl(url)
  Backend->>Aria2: JSON-RPC aria2.addUri (with token if configured, and out: filename)
  Aria2->>Backend: {gid} or error
  Backend->>Frontend: JSON {success, gid?, error?}
  Frontend->>ReleaseCard: Show toast / update UI
Loading
sequenceDiagram
  participant User
  participant NetworkPanel
  participant Frontend as Frontend Service
  participant Backend as Backend API
  participant Aria2
  User->>NetworkPanel: Enter host, port, secret
  User->>NetworkPanel: Click Test
  NetworkPanel->>Frontend: testRpcDownload(config, apiSecret)
  Frontend->>Backend: POST /api/settings/rpc-download/test
  Backend->>Aria2: JSON-RPC aria2.getVersion (token if provided)
  Aria2->>Backend: version response
  Backend->>Frontend: {success: true, version}
  Frontend->>NetworkPanel: Show result with version
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through types and routes tonight,
I hid the secret, kept the UI bright.
Aria2 hums, queues take their flight,
Buttons click, toasts glow in light.
A rabbit's patch: remote downloads delight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: 远程 RPC 下载支持' (Remote RPC Download Support) accurately describes the main feature addition - implementing remote RPC download functionality with aria2 integration across the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/rpc-download

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/services/rpcDownloadService.ts (1)

27-43: ⚡ Quick win

Consider checking HTTP response status before parsing JSON.

Both testRpcDownload and sendToRpcDownload parse the response JSON without checking resp.ok. While the current backend implementation always returns 200 with a success field in the JSON body (making this work), it's more robust to validate the HTTP status:

if (!resp.ok) {
  return { success: false, error: `Server returned ${resp.status}` };
}
return await resp.json();

This pattern prevents silent failures if the backend changes to return error status codes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/rpcDownloadService.ts` around lines 27 - 43, The HTTP response
is parsed without checking status in testRpcDownload (and similarly in
sendToRpcDownload), so update both functions to check resp.ok after the fetch
call and before calling resp.json(); if !resp.ok return an object like {
success: false, error: `Server returned ${resp.status}` } (preserving existing
error shape), otherwise return await resp.json(); keep existing error-catch
behavior using e instanceof Error; reference the fetch call, resp variable, and
functions testRpcDownload and sendToRpcDownload when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/src/routes/proxy.ts`:
- Line 651: The code uses AbortSignal.timeout(5000) in
server/src/routes/proxy.ts which requires Node >=17.3.0; either declare and pin
the supported Node version (engines.node) in root and/or server package.json and
ensure CI/Docker use that runtime, or replace the direct AbortSignal.timeout
calls with an explicit AbortController + setTimeout fallback (create
AbortController, call setTimeout(() => controller.abort(), 5000) and pass
controller.signal) for both spots where AbortSignal.timeout is used so older
Node runtimes remain compatible.

In `@src/types/index.ts`:
- Around line 172-177: Change RpcDownloadConfig.secret to an optional property
(secret?: string) so the frontend won't send an empty string and inadvertently
clear stored secrets; update the type definition for RpcDownloadConfig (where
it's declared) to make secret optional, then update the store initializations
referenced (useAppStore initializers around the mentioned indices) to initialize
secret as undefined instead of '' and modify NetworkPanel.tsx to only include
the secret field in the PUT payload when the user has explicitly changed it;
keep ProxyConfig.password's pattern for reference and ensure backend PUT handler
behavior remains unchanged.

---

Nitpick comments:
In `@src/services/rpcDownloadService.ts`:
- Around line 27-43: The HTTP response is parsed without checking status in
testRpcDownload (and similarly in sendToRpcDownload), so update both functions
to check resp.ok after the fetch call and before calling resp.json(); if
!resp.ok return an object like { success: false, error: `Server returned
${resp.status}` } (preserving existing error shape), otherwise return await
resp.json(); keep existing error-catch behavior using e instanceof Error;
reference the fetch call, resp variable, and functions testRpcDownload and
sendToRpcDownload when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 75876f4b-4ff6-4d7d-afd8-2636aafb8549

📥 Commits

Reviewing files that changed from the base of the PR and between 79788cc and 83d8dd7.

📒 Files selected for processing (7)
  • server/src/routes/proxy.ts
  • server/src/services/proxyService.ts
  • src/components/ReleaseCard.tsx
  • src/components/settings/NetworkPanel.tsx
  • src/services/rpcDownloadService.ts
  • src/store/useAppStore.ts
  • src/types/index.ts

Comment thread server/src/routes/proxy.ts Outdated
Comment thread src/types/index.ts
AmintaCCCP and others added 3 commits June 1, 2026 22:37
- Check resp.ok before parsing JSON in frontend service (CodeRabbit)
- Replace AbortSignal.timeout with AbortController+setTimeout for compatibility
- Make RpcDownloadConfig.secret optional to avoid clearing stored secrets
- Add logging to test and download endpoints (logger.info/warn/error)
- Move variable declarations before try blocks for catch block access
- Fix mixed-language placeholder text

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The RPC service was using fetch('/api/...') which targets the current
origin. When frontend and backend run on different ports (e.g. Vite
dev server on 5173, backend on 3000), this causes 'Failed to fetch'.

Use backend.backendUrl (probed by backendAdapter at init) to construct
the correct API URL, matching how other backend calls work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1. Use resolveBaseUrl() with relative URL fallback instead of requiring
   backend.backendUrl, fixing 'Backend not available' when backendAdapter
   hasn't probed yet.

2. Persist RPC secret: load hasSecret from backend on mount, show masked
   placeholder, fall back to stored secret in test endpoint when request
   doesn't include one.

3. NetworkPanel save/toggle revert to relative URLs (matching proxy config
   pattern) since frontend and backend share the same origin in production.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/services/rpcDownloadService.ts (1)

35-47: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Preserve backend error payloads on non-2xx responses.

Both wrappers replace the server's JSON { error } body with a generic Server returned <status>. That hides actionable failures from these new endpoints, e.g. invalid download URL or RPC not configured, so the UI loses the real reason.

💡 Suggested fix
+async function readErrorMessage(resp: Response): Promise<string> {
+  try {
+    const data = await resp.json() as { error?: string };
+    if (typeof data.error === 'string' && data.error) {
+      return data.error;
+    }
+  } catch {
+    // ignore parse failures and fall back to status
+  }
+  return `Server returned ${resp.status}`;
+}
+
 export async function testRpcDownload(
   config: RpcDownloadConfig,
   apiSecret?: string,
 ): Promise<RpcTestResult> {
   const base = resolveBaseUrl();
   try {
     const resp = await fetch(`${base}/settings/rpc-download/test`, {
       method: 'POST',
       headers: getAuthHeaders(apiSecret),
       body: JSON.stringify({
         host: config.host,
         port: config.port,
         secret: config.secret,
       }),
     });
     if (!resp.ok) {
-      return { success: false, error: `Server returned ${resp.status}` };
+      return { success: false, error: await readErrorMessage(resp) };
     }
     return await resp.json();
   } catch (e) {
     return {
       success: false,
       error: e instanceof Error ? e.message : 'Request failed',
     };
   }
 }
@@
 export async function sendToRpcDownload(
   url: string,
   filename: string,
   apiSecret?: string,
 ): Promise<RpcDownloadResult> {
   const base = resolveBaseUrl();
   try {
     const resp = await fetch(`${base}/download/rpc`, {
       method: 'POST',
       headers: getAuthHeaders(apiSecret),
       body: JSON.stringify({ url, filename }),
     });
     if (!resp.ok) {
-      return { success: false, error: `Server returned ${resp.status}` };
+      return { success: false, error: await readErrorMessage(resp) };
     }
     return await resp.json();
   } catch (e) {
     return {
       success: false,
       error: e instanceof Error ? e.message : 'Request failed',
     };
   }
 }

Also applies to: 63-71

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/rpcDownloadService.ts` around lines 35 - 47, The non-2xx branch
replaces the server JSON error with a generic message; change the error handling
for the fetch to attempt to parse the response body and preserve the backend
payload. Specifically, in the POST to `${base}/settings/rpc-download/test` (the
resp variable handling) and the similar block at lines 63-71, when resp.ok is
false call await resp.json() (falling back to await resp.text() if JSON parse
fails) and return { success: false, error: /* use parsed error.message or full
parsed object or text */ } so the original server error payload is surfaced to
the caller/UI.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/src/routes/proxy.ts`:
- Around line 644-657: The code incorrectly falls back to the stored secret
whenever requestSecret is falsy (e.g., ''), causing blank secrets to be
replaced; change the fallback to only occur when the secret field is omitted
from the request — check presence of the field on req.body (e.g., use "secret"
in req.body or Object.prototype.hasOwnProperty.call(req.body, "secret")) and
only assign from getRpcDownloadConfig() when the field is not present; update
the logic around requestSecret/secret and the call to getRpcDownloadConfig()
(referencing requestSecret, secret, req.body, and getRpcDownloadConfig) so an
explicit empty string provided by the client is respected and not replaced by
the stored secret.

---

Outside diff comments:
In `@src/services/rpcDownloadService.ts`:
- Around line 35-47: The non-2xx branch replaces the server JSON error with a
generic message; change the error handling for the fetch to attempt to parse the
response body and preserve the backend payload. Specifically, in the POST to
`${base}/settings/rpc-download/test` (the resp variable handling) and the
similar block at lines 63-71, when resp.ok is false call await resp.json()
(falling back to await resp.text() if JSON parse fails) and return { success:
false, error: /* use parsed error.message or full parsed object or text */ } so
the original server error payload is surfaced to the caller/UI.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c9a96186-b85f-4d88-9e86-a29be70b790a

📥 Commits

Reviewing files that changed from the base of the PR and between 6a613ed and 80f744e.

📒 Files selected for processing (3)
  • server/src/routes/proxy.ts
  • src/components/settings/NetworkPanel.tsx
  • src/services/rpcDownloadService.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/settings/NetworkPanel.tsx

Comment thread server/src/routes/proxy.ts
The proxy config endpoints use fetch('/api/...') and work correctly.
Revert rpcDownloadService to the same pattern instead of using
backend.backendUrl which may not be initialized or may point to a
different origin.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/services/rpcDownloadService.ts (1)

37-39: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve backend JSON error messages on non-2xx responses.

Both methods discard structured backend errors and replace them with a generic status string. That can suppress required user-facing messages (e.g., aria2 not running) and weakens troubleshooting.

Suggested fix
+async function readErrorMessage(resp: Response): Promise<string> {
+  try {
+    const data = await resp.json();
+    if (data && typeof data.error === 'string' && data.error.trim()) {
+      return data.error;
+    }
+  } catch {
+    // fall through
+  }
+  return `Server returned ${resp.status}`;
+}
+
 export async function testRpcDownload(
   config: RpcDownloadConfig,
   apiSecret?: string,
 ): Promise<RpcTestResult> {
   try {
     const resp = await fetch('/api/settings/rpc-download/test', {
@@
     });
     if (!resp.ok) {
-      return { success: false, error: `Server returned ${resp.status}` };
+      return { success: false, error: await readErrorMessage(resp) };
     }
     return await resp.json();
   } catch (e) {
@@
 export async function sendToRpcDownload(
   url: string,
   filename: string,
   apiSecret?: string,
 ): Promise<RpcDownloadResult> {
@@
     });
     if (!resp.ok) {
-      return { success: false, error: `Server returned ${resp.status}` };
+      return { success: false, error: await readErrorMessage(resp) };
     }
     return await resp.json();
   } catch (e) {

Also applies to: 60-62

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/rpcDownloadService.ts` around lines 37 - 39, The current non-2xx
handling returns a generic `Server returned ${resp.status}` and discards any
structured backend error; instead, when `resp.ok` is false (the `resp` checks at
the shown locations), attempt to parse the response body (prefer JSON, fallback
to text) to extract and preserve backend error details and include them in the
returned error object (e.g., `{ success: false, error: extractedMessage ||
\`Server returned ${resp.status}\` }`); apply this change to both occurrences
that check `resp.ok` so the service preserves user-facing backend messages.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/services/rpcDownloadService.ts`:
- Around line 37-39: The current non-2xx handling returns a generic `Server
returned ${resp.status}` and discards any structured backend error; instead,
when `resp.ok` is false (the `resp` checks at the shown locations), attempt to
parse the response body (prefer JSON, fallback to text) to extract and preserve
backend error details and include them in the returned error object (e.g., `{
success: false, error: extractedMessage || \`Server returned ${resp.status}\`
}`); apply this change to both occurrences that check `resp.ok` so the service
preserves user-facing backend messages.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 44fbf388-4003-427b-a19f-07f48670c162

📥 Commits

Reviewing files that changed from the base of the PR and between 80f744e and 568dfae.

📒 Files selected for processing (1)
  • src/services/rpcDownloadService.ts

AmintaCCCP and others added 3 commits June 1, 2026 23:11
… fallback

1. Electron loads from file:// so relative URLs resolve to file:///api/...
   Use backend.backendUrl (probed via backendAdapter.init()) for all RPC
   API calls. getBaseUrl() ensures init() completes before fetching.

2. Load hasSecret from backend on mount, show placeholder hint in input.
   Only send secret in PUT when user explicitly typed one (omit field
   to let backend preserve stored encrypted value).

3. CodeRabbit: test endpoint secret fallback now checks field presence
   via hasOwnProperty instead of truthiness, so explicit empty string
   is respected (clear secret) vs omitted field (use stored value).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When no backend is available, the frontend calls aria2 JSON-RPC directly
at http://host:port/jsonrpc. This enables RPC download in both browser
and Electron desktop modes without requiring the Express server.

Changes:
- rpcDownloadService: add callAria2Direct() fallback, testRpcDownload
  and sendToRpcDownload use direct calls when backend is unavailable
- NetworkPanel: proxy card hidden without backend, RPC card always
  visible; save/toggle persist to local store when no backend
- Electron build: set webSecurity: false to allow cross-origin requests
  to local aria2 RPC endpoint (safe for desktop app)

Usage (client-only):
1. Start aria2: aria2c --enable-rpc --rpc-listen-port=6800 --rpc-secret=xxx
2. In app: Settings > Remote Download > enable, fill host/port/secret
3. Click download on any release asset → sent to aria2

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@AmintaCCCP AmintaCCCP merged commit fe46392 into main Jun 1, 2026
5 checks passed
@AmintaCCCP AmintaCCCP deleted the feat/rpc-download branch June 3, 2026 12:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant