Skip to content

feat(skill-store): add GitHub Token setting to raise API rate limit (#108)#123

Merged
legeling merged 2 commits into
legeling:mainfrom
TuTouPower:feat/issue-108-github-token
May 10, 2026
Merged

feat(skill-store): add GitHub Token setting to raise API rate limit (#108)#123
legeling merged 2 commits into
legeling:mainfrom
TuTouPower:feat/issue-108-github-token

Conversation

@TuTouPower
Copy link
Copy Markdown
Contributor

@TuTouPower TuTouPower commented May 9, 2026

Summary

Fixes #108 — users hitting the anonymous 60 req/h GitHub API limit saw an error message telling them to "add a GitHub Token in settings", but the setting did not exist anywhere. This PR adds it, wired from UI to main-process HTTP layer.

What the token does

Once configured, the main process attaches `Authorization: Bearer ` to outbound skill-store requests targeting `api.github.com` and `raw.githubusercontent.com`. That raises the limit from 60 req/h (unauthenticated) to 5 000 req/h (authenticated PAT) without needing any scope — a read-only classic or fine-grained token is enough for public skill repos.

Security

  • The token is attached only to hostnames in a small allow-list (`api.github.com`, `raw.githubusercontent.com`). Any 3xx redirect to a different host drops the token before the next request (see `fetchRemoteText` redirect handling).
  • The main process re-validates every read: non-string types, empty strings, and values containing CR / LF / NUL / other control bytes are all treated as "no token". This is defence in depth against HTTP header injection via a tampered localStorage.
  • The renderer-side setter performs the same sanitization before writing to localStorage and pushing to the main DB.
  • The token value is never logged.

Changes

File Change
`packages/shared/types/settings.ts` Add optional `githubToken` field.
`apps/desktop/src/main/ipc/settings.ipc.ts` New `getGithubTokenSetting(db)` helper with sanitization.
`apps/desktop/src/main/services/skill-installer-remote.ts` `fetchRemoteText` now accepts an optional `githubToken` that is attached only on trusted GitHub hosts and dropped on cross-host redirects. Exported `shouldAttachGithubAuth` helper.
`apps/desktop/src/main/services/skill-installer.ts` `fetchRemoteContent` reads the token from DB and passes it to `fetchRemoteText`.
`apps/desktop/src/renderer/stores/settings.store.ts` Add `githubToken` state + `setGithubToken` action that strips control chars and syncs to the main DB via `window.api.settings.set`.
`apps/desktop/src/renderer/components/settings/SkillSettings.tsx` New "GitHub Access Token" section with masked input, show/hide toggle, clear button, and link to the GitHub PAT generation page.
`apps/desktop/src/renderer/i18n/locales/*.json` New keys added to all 7 locales (en, zh, zh-TW, ja, fr, de, es).

Tests

  • `tests/unit/main/github-token-setting.test.ts` — 18 tests covering missing row, valid token, trim, empty, wrong types, control characters, malformed JSON, legacy plain-string rows, isolation from other keys, and long fine-grained PATs.
  • `tests/unit/stores/settings-github-token.test.ts` — 8 tests covering happy path, trimming, control-character stripping, clear round-trip, and verifying the value is pushed to `window.api.settings.set` so the main process picks it up.
  • `tests/unit/main/skill-installer-remote.test.ts` — +11 new tests on `shouldAttachGithubAuth` covering trusted hosts (case-insensitive), untrusted hosts (including lookalikes like `evilgithub.com`, `api.github.com.evil.com`), and invalid inputs.

Verification

  • `pnpm lint` → clean
  • `pnpm test -- --run tests/unit/stores tests/unit/main/settings-startup.test.ts tests/unit/main/github-token-setting.test.ts tests/unit/main/skill-installer-remote.test.ts` → 83/83 passing

Notes

Summary by CodeRabbit

  • 新功能
    • 在设置中新增“GitHub 访问令牌”项,支持显示/隐藏、清除及持久化,提升技能商店的 API 请求配额。
  • 安全与行为
    • 令牌会被校验与清理(去除控制字符、修剪空白);仅在官方 GitHub 域名的请求中附加令牌;错误或不合规时安全忽略并记录。
  • 测试
    • 增加单元测试覆盖令牌读取、校验、存储和域名信任逻辑。
  • 本地化
    • 新增多语言界面文案(EN/DE/ES/FR/JA/ZH 等)。

…egeling#108)

Users hitting the anonymous 60 req/h GitHub API limit saw an error
message telling them to 'add a GitHub Token in settings', but the
setting did not exist anywhere. This PR adds it.

### What the token does

Once configured, the main process attaches 'Authorization: Bearer <token>'
to outbound skill-store requests targeting api.github.com and
raw.githubusercontent.com. That raises the limit from 60 req/h
(unauthenticated) to 5 000 req/h (authenticated PAT) without needing
any scope — a read-only classic or fine-grained token is enough for
public skill repos.

### Security

- The token is attached ONLY to hostnames in a small allow-list
  (api.github.com, raw.githubusercontent.com). Any 3xx redirect to a
  different host drops the token before the next request.
- The main process re-validates every read: non-string types, empty
  strings, and values containing CR / LF / NUL / other control bytes
  are all treated as 'no token'. This is defence in depth against
  HTTP header injection via a tampered localStorage.
- The renderer-side setter performs the same sanitization before
  writing to localStorage and pushing to the main DB.
- The token value is never logged.

### Changes

- packages/shared/types/settings.ts: add optional `githubToken` field.
- apps/desktop/src/main/ipc/settings.ipc.ts: new
  `getGithubTokenSetting(db)` helper with sanitization.
- apps/desktop/src/main/services/skill-installer-remote.ts: extend
  `fetchRemoteText` with an optional `githubToken` that is attached
  only on trusted GitHub hosts and dropped on cross-host redirects.
  Added tested `shouldAttachGithubAuth` helper.
- apps/desktop/src/main/services/skill-installer.ts:
  `fetchRemoteContent` reads the token from DB and passes it to
  `fetchRemoteText`.
- apps/desktop/src/renderer/stores/settings.store.ts: add
  `githubToken` state + `setGithubToken` action that strips control
  chars and syncs to the main DB.
- apps/desktop/src/renderer/components/settings/SkillSettings.tsx: new
  'GitHub Access Token' section with masked input, show/hide toggle,
  clear button, and a link to the GitHub PAT generation page.
- i18n: added new keys to all 7 locales (en, zh, zh-TW, ja, fr, de, es).

### Tests

- tests/unit/main/github-token-setting.test.ts (18 tests): covers
  missing row, valid token, trim, empty, wrong types, control
  characters, malformed JSON, legacy plain-string rows, isolation
  from other keys, and long fine-grained PATs.
- tests/unit/stores/settings-github-token.test.ts (8 tests): covers
  happy path, trimming, control-character stripping, clear round-trip,
  and verifies the value is pushed to window.api.settings.set so the
  main process picks it up.
- tests/unit/main/skill-installer-remote.test.ts: +11 new tests on
  `shouldAttachGithubAuth` covering trusted hosts (case-insensitive),
  untrusted hosts (including lookalikes / homograph-style attackers),
  and invalid inputs.

### Verification

- pnpm lint → clean
- pnpm test -- --run tests/unit/stores tests/unit/main/settings-startup.test.ts tests/unit/main/github-token-setting.test.ts tests/unit/main/skill-installer-remote.test.ts → 83/83 passing

Closes legeling#108
@vercel
Copy link
Copy Markdown

vercel Bot commented May 9, 2026

@TuTouPower is attempting to deploy a commit to the legeling's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ca352af9-80e1-407d-bd43-b3249f94bdd8

📥 Commits

Reviewing files that changed from the base of the PR and between f6fb7de and b850993.

📒 Files selected for processing (5)
  • apps/desktop/src/main/ipc/settings.ipc.ts
  • apps/desktop/src/main/services/skill-installer-remote.ts
  • apps/desktop/src/main/services/skill-installer.ts
  • apps/desktop/src/renderer/stores/settings.store.ts
  • packages/shared/types/settings.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/shared/types/settings.ts
  • apps/desktop/src/main/services/skill-installer.ts
  • apps/desktop/src/main/services/skill-installer-remote.ts
  • apps/desktop/src/main/ipc/settings.ipc.ts
  • apps/desktop/src/renderer/stores/settings.store.ts

📝 Walkthrough

Walkthrough

此 PR 为 Skill Store 添加本地 GitHub PAT 支持:类型契约、后端安全读取与验证、按主机条件附加/移除认证头、前端持久化与设置 UI、多语言本地化及单元测试覆盖。

变更

GitHub PAT 认证与本地配置

Layer / File(s) Summary
类型契约
packages/shared/types/settings.ts
Settings 接口新增 githubToken?: string 字段,记录 GitHub API 认证令牌用途。
令牌检索与验证
apps/desktop/src/main/ipc/settings.ipc.ts
getGithubTokenSetting(db) 导出函数从数据库读取、支持 JSON 与旧纯文本、验证为非空且无控制字符、修剪并返回令牌或 null,错误仅记录通用日志。
主机信任网关
apps/desktop/src/main/services/skill-installer-remote.ts
新增 GitHub 主机允许列表并导出 shouldAttachGithubAuth(hostname) 判定函数与 FetchRemoteTextOptions
远程获取请求头 & 重定向处理
apps/desktop/src/main/services/skill-installer-remote.ts
fetchRemoteText 接受 options(含 githubToken),构建 baseHeaders,仅为受信初始主机并有 token 时附加 AuthorizationX-GitHub-Api-Version;重定向时重新评估目标主机并在不受信时移除 token。
技能安装器集成
apps/desktop/src/main/services/skill-installer.ts
fetchRemoteContent 在运行时尝试通过 initDatabase + getGithubTokenSetting 加载 PAT,若成功则将其作为 options 传入 fetchRemoteText;加载失败记录警告并以无认证方式继续。
前端状态存储
apps/desktop/src/renderer/stores/settings.store.ts
新增 persisted 字段 githubToken(默认 "")和 setGithubToken(token):剥离控制字符、修剪、更新状态并通过 syncSettingsToMain 同步到主进程。
设置 UI 组件
apps/desktop/src/renderer/components/settings/SkillSettings.tsx
新增 GitHub Access Token 区块:掩码输入、眼睛图标可见性切换、清除按钮、描述文本与创建令牌外部链接。
多语言本地化
apps/desktop/src/renderer/i18n/locales/{en,de,es,fr,ja,zh-TW,zh}.json
为七种语言新增与令牌设置相关的 7 个 i18n 键(title/desc/placeholder/show/hide/learnMore/scopeHint)。
后端令牌检索测试
apps/desktop/tests/unit/main/github-token-setting.test.ts
全面的 getGithubTokenSetting 单元测试:无值/有效值、修剪、拒绝空白/非字符串/控制字符、兼容旧纯文本、损坏 JSON 容错、隔离性与长令牌处理。
主机网关测试
apps/desktop/tests/unit/main/skill-installer-remote.test.ts
shouldAttachGithubAuth 添加回归测试(issue #108):仅官方受信主机允许、大小写无关、仿冒域名拒绝、无效输入返回 false。
前端存储动作测试
apps/desktop/tests/unit/stores/settings-github-token.test.ts
setGithubToken 添加测试:验证存储与 window.api.settings.set 同步、修剪空白、剥离控制字符并保持安全前缀、清除同步。

预估代码审查工作量

🎯 3 (中等) | ⏱️ ~25 分钟

🐰 一只小兔子欢快跳,
GitHub 令牌藏好又明了,
请求随主机信任来或去,
界面可见可清空令牌,
安全与测试把守每一秒。

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% 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 标题清晰准确地总结了主要改动:在技能商店中添加 GitHub Token 设置以提高 API 速率限制,并引用了相关问题编号。
Linked Issues check ✅ Passed 代码变更完全满足问题 #108 的所有要求:提供了 GitHub Token 设置 UI、持久化存储、安全的 token 验证与清理、限制 token 仅用于可信 GitHub 主机、防止 token 日志记录,以及重定向时的 token 丢弃机制。
Out of Scope Changes check ✅ Passed 所有代码变更均与问题 #108 的需求相关:token 设置 UI、token 管理、token 使用、测试和国际化文本都在范围内,无超出范围的改动。

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Add GitHub personal access token setting to raise API rate limit

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add GitHub personal access token setting to raise API rate limit
• Implement token sanitization and secure attachment to GitHub API requests
• Add UI controls for token management with show/hide and clear buttons
• Extend localization across 7 languages with new token-related strings
• Add comprehensive unit tests for token validation and security
Diagram
flowchart LR
  UI["UI Input<br/>SkillSettings.tsx"]
  Store["Renderer Store<br/>setGithubToken"]
  IPC["IPC Sync<br/>window.api.settings.set"]
  DB["SQLite DB<br/>settings table"]
  Main["Main Process<br/>getGithubTokenSetting"]
  Fetch["fetchRemoteText<br/>shouldAttachGithubAuth"]
  GitHub["GitHub API<br/>api.github.com"]
  
  UI -- "token input" --> Store
  Store -- "sanitize & persist" --> IPC
  IPC -- "sync to main" --> DB
  DB -- "read token" --> Main
  Main -- "pass token" --> Fetch
  Fetch -- "attach Bearer header<br/>if trusted host" --> GitHub
Loading

Grey Divider

File Changes

1. packages/shared/types/settings.ts ✨ Enhancement +6/-0

Add optional githubToken field to Settings interface

packages/shared/types/settings.ts


2. apps/desktop/src/main/ipc/settings.ipc.ts ✨ Enhancement +56/-0

Implement getGithubTokenSetting with sanitization logic

apps/desktop/src/main/ipc/settings.ipc.ts


3. apps/desktop/src/main/services/skill-installer-remote.ts ✨ Enhancement +64/-6

Add GitHub token attachment with host validation and redirect handling

apps/desktop/src/main/services/skill-installer-remote.ts


View more (13)
4. apps/desktop/src/main/services/skill-installer.ts ✨ Enhancement +25/-2

Integrate GitHub token retrieval into fetchRemoteContent

apps/desktop/src/main/services/skill-installer.ts


5. apps/desktop/src/renderer/stores/settings.store.ts ✨ Enhancement +28/-0

Add githubToken state and setGithubToken action with sanitization

apps/desktop/src/renderer/stores/settings.store.ts


6. apps/desktop/src/renderer/components/settings/SkillSettings.tsx ✨ Enhancement +77/-0

Add GitHub Access Token UI section with masked input and controls

apps/desktop/src/renderer/components/settings/SkillSettings.tsx


7. apps/desktop/src/renderer/i18n/locales/en.json 📝 Documentation +7/-0

Add English localization strings for GitHub token feature

apps/desktop/src/renderer/i18n/locales/en.json


8. apps/desktop/src/renderer/i18n/locales/zh.json 📝 Documentation +7/-0

Add Simplified Chinese localization strings for GitHub token feature

apps/desktop/src/renderer/i18n/locales/zh.json


9. apps/desktop/src/renderer/i18n/locales/zh-TW.json 📝 Documentation +7/-0

Add Traditional Chinese localization strings for GitHub token feature

apps/desktop/src/renderer/i18n/locales/zh-TW.json


10. apps/desktop/src/renderer/i18n/locales/ja.json 📝 Documentation +7/-0

Add Japanese localization strings for GitHub token feature

apps/desktop/src/renderer/i18n/locales/ja.json


11. apps/desktop/src/renderer/i18n/locales/fr.json 📝 Documentation +7/-0

Add French localization strings for GitHub token feature

apps/desktop/src/renderer/i18n/locales/fr.json


12. apps/desktop/src/renderer/i18n/locales/de.json 📝 Documentation +7/-0

Add German localization strings for GitHub token feature

apps/desktop/src/renderer/i18n/locales/de.json


13. apps/desktop/src/renderer/i18n/locales/es.json 📝 Documentation +7/-0

Add Spanish localization strings for GitHub token feature

apps/desktop/src/renderer/i18n/locales/es.json


14. apps/desktop/tests/unit/main/github-token-setting.test.ts 🧪 Tests +137/-0

Add 18 tests for GitHub token validation and sanitization

apps/desktop/tests/unit/main/github-token-setting.test.ts


15. apps/desktop/tests/unit/main/skill-installer-remote.test.ts 🧪 Tests +52/-0

Add 11 tests for shouldAttachGithubAuth host validation

apps/desktop/tests/unit/main/skill-installer-remote.test.ts


16. apps/desktop/tests/unit/stores/settings-github-token.test.ts 🧪 Tests +115/-0

Add 8 tests for renderer-side token setter and sync behavior

apps/desktop/tests/unit/stores/settings-github-token.test.ts


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented May 9, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (3) 📎 Requirement gaps (0)

Grey Divider


Action required

1. as assertions undocumented 📘 Rule violation ⚙ Maintainability
Description
New as type assertions were added without an explanatory comment or clear necessity, weakening
TypeScript strictness and potentially masking type errors. This violates the rule restricting type
assertions unless justified with a comment.
Code

apps/desktop/src/main/ipc/settings.ipc.ts[R47-49]

+    const stmt = db.prepare('SELECT value FROM settings WHERE key = ?');
+    const row = stmt.get('githubToken') as { value: string } | undefined;
+    if (!row) {
Evidence
PR Compliance ID 4 disallows unsafe typing escapes and requires justification for as assertions.
The PR adds multiple as assertions without any explanatory comment (e.g., casting DB rows and IPC
payload shapes).

AGENTS.md
apps/desktop/src/main/ipc/settings.ipc.ts[47-49]
apps/desktop/src/renderer/stores/settings.store.ts[1277-1282]
apps/desktop/tests/unit/stores/settings-github-token.test.ts[44-46]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New `as` assertions were introduced without justification comments, violating the strictness rule and potentially hiding type mismatches.
## Issue Context
Casts appear in main-process settings DB reads, renderer store sync, and tests. Prefer safe typing (generics, runtime guards, proper types) over assertions; if an assertion is truly required for interop, add a brief comment explaining why it is safe/necessary.
## Fix Focus Areas
- apps/desktop/src/main/ipc/settings.ipc.ts[47-49]
- apps/desktop/src/renderer/stores/settings.store.ts[1277-1282]
- apps/desktop/tests/unit/stores/settings-github-token.test.ts[44-46]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Relative imports in skill-installer 📘 Rule violation ⚙ Maintainability
Description
New imports in the main process use relative paths instead of the documented @/ alias for
src/main, increasing coupling to folder structure and risking circularity/drift. This violates the
import/path-alias compliance rule.
Code

apps/desktop/src/main/services/skill-installer.ts[R23-24]

+import { initDatabase } from "../database";
+import { getGithubTokenSetting } from "../ipc/settings.ipc";
Evidence
PR Compliance ID 28 requires using documented path aliases (@/ for src/main). The PR adds new
relative imports (../database, ../ipc/settings.ipc) instead of alias-based imports.

AGENTS.md
apps/desktop/src/main/services/skill-installer.ts[23-24]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New code adds relative imports in a `src/main` module where the repository requires documented aliases.
## Issue Context
The compliance rule requires `@/` for main-process imports to keep paths stable and avoid drift.
## Fix Focus Areas
- apps/desktop/src/main/services/skill-installer.ts[23-24]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Chinese text outside locales ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
New Chinese text was introduced in TypeScript source comments outside locale JSON files, violating
the rule prohibiting Chinese outside i18n locale resources. This can also complicate localization
standards and consistency checks.
Code

apps/desktop/src/main/ipc/settings.ipc.ts[R42-43]

+ * 从 settings 表读取用户配置的 GitHub PAT,用于给 Skill Store 的 GitHub API
+ * 请求加鉴权头,避免未登录状态下触发 60 次/小时的限额 (#108)。
Evidence
PR Compliance ID 10 forbids Chinese characters in source code outside locale JSON files. The PR adds
multiple Chinese comment lines in main/renderer TS files (not locale JSON).

AGENTS.md
apps/desktop/src/main/ipc/settings.ipc.ts[42-43]
apps/desktop/src/main/services/skill-installer-remote.ts[269-293]
apps/desktop/src/main/services/skill-installer.ts[822-837]
apps/desktop/src/renderer/stores/settings.store.ts[426-431]
apps/desktop/src/renderer/stores/settings.store.ts[1270-1276]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Chinese characters were added in non-locale TypeScript files (even if only in comments), which violates the rule banning Chinese outside locale JSON.
## Issue Context
The requirement is explicit: Chinese must not appear in source outside locale JSON files.
## Fix Focus Areas
- apps/desktop/src/main/ipc/settings.ipc.ts[42-43]
- apps/desktop/src/main/services/skill-installer-remote.ts[269-293]
- apps/desktop/src/main/services/skill-installer.ts[822-837]
- apps/desktop/src/renderer/stores/settings.store.ts[426-431]
- apps/desktop/src/renderer/stores/settings.store.ts[1270-1276]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. CLI pulls Electron dependency 🐞 Bug ≡ Correctness
Description
SkillInstaller now imports getGithubTokenSetting from settings.ipc.ts, which has a top-level import
from 'electron'. Because the CLI imports SkillInstaller and the CLI build config doesn’t externalize
'electron' (and electron is only a devDependency), the published CLI can fail to bundle or crash at
startup with “Cannot find module 'electron'”.
Code

apps/desktop/src/main/services/skill-installer.ts[R23-24]

+import { initDatabase } from "../database";
+import { getGithubTokenSetting } from "../ipc/settings.ipc";
Evidence
The CLI entrypoint imports SkillInstaller; SkillInstaller now imports settings.ipc.ts for
getGithubTokenSetting; settings.ipc.ts imports electron at module scope. The CLI Rollup config
externalizes builtins and node-sqlite3-wasm but not electron, and electron is not a runtime
dependency of the published package (devDependency only), so requiring the CLI bundle in a
non-Electron install can fail immediately during module resolution.

apps/desktop/src/main/services/skill-installer.ts[15-26]
apps/desktop/src/main/ipc/settings.ipc.ts[1-4]
apps/desktop/src/cli/run.ts[4-12]
apps/desktop/vite.cli.config.ts[1-10]
apps/desktop/vite.cli.config.ts[21-38]
apps/desktop/package.json[66-95]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`SkillInstaller` (used by the Node CLI) now imports `getGithubTokenSetting` from `main/ipc/settings.ipc.ts`, which has a top-level `import { ipcMain } from 'electron'`. This makes the CLI bundle/runtime require the `electron` module even though the published CLI package doesn’t ship it as a dependency.
## Issue Context
The helper `getGithubTokenSetting(db)` is electron-independent, but it currently lives in an Electron IPC module. The CLI build config also does not externalize `electron`, making this a high-risk transitive dependency.
## Fix Focus Areas
- apps/desktop/src/main/services/skill-installer.ts[20-24]
- apps/desktop/src/main/ipc/settings.ipc.ts[1-10]
### Suggested approach
- Move `getGithubTokenSetting` into a new electron-free module (e.g. `apps/desktop/src/main/settings/github-token.ts` or `apps/desktop/src/main/database/settings.ts`).
- Import that helper from both:
- `settings.ipc.ts` (for IPC usage)
- `skill-installer.ts` (for fetch usage)
- Keep `ipcMain` imports only in IPC registration code, not in modules shared with the CLI entry graph.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Hardcoded fallback UI strings 📘 Rule violation ⚙ Maintainability
Description
The new Settings UI section passes hardcoded English strings as fallbacks to t(...), introducing
user-facing strings directly in source instead of relying purely on i18n resources. This violates
the rule disallowing hardcoded user-facing strings in UI code.
Code

apps/desktop/src/renderer/components/settings/SkillSettings.tsx[R161-229]

+      <SettingSection
+        title={t("settings.githubTokenTitle", "GitHub Access Token")}
+      >
+        <div className="p-4 space-y-3">
+          <p className="text-xs text-muted-foreground">
+            {t(
+              "settings.githubTokenDesc",
+              "Optional. Attach a GitHub personal access token (classic or fine-grained) so Skill Store requests use your authenticated rate limit (5 000 req/h) instead of the anonymous 60 req/h limit. The token is only sent to api.github.com and raw.githubusercontent.com.",
+            )}
+          </p>
+          <div className="flex items-center gap-2">
+            <input
+              type={isGithubTokenVisible ? "text" : "password"}
+              autoComplete="off"
+              spellCheck={false}
+              value={settings.githubToken}
+              onChange={(e) => settings.setGithubToken(e.target.value)}
+              placeholder={t(
+                "settings.githubTokenPlaceholder",
+                "ghp_… or github_pat_…",
+              )}
+              className="flex-1 h-9 px-3 rounded-lg bg-muted border-0 text-sm font-mono placeholder:text-muted-foreground/50"
+              aria-label={t("settings.githubTokenTitle", "GitHub Access Token")}
+            />
+            <button
+              type="button"
+              onClick={() => setIsGithubTokenVisible((prev) => !prev)}
+              className="h-9 px-3 rounded-lg border border-border text-sm text-muted-foreground hover:border-primary/30 hover:text-foreground transition-colors"
+              aria-label={
+                isGithubTokenVisible
+                  ? t("settings.githubTokenHide", "Hide token")
+                  : t("settings.githubTokenShow", "Show token")
+              }
+              title={
+                isGithubTokenVisible
+                  ? t("settings.githubTokenHide", "Hide token")
+                  : t("settings.githubTokenShow", "Show token")
+              }
+            >
+              {isGithubTokenVisible ? (
+                <EyeOffIcon className="h-4 w-4" />
+              ) : (
+                <EyeIcon className="h-4 w-4" />
+              )}
+            </button>
+            {settings.githubToken.length > 0 ? (
+              <button
+                type="button"
+                onClick={() => settings.setGithubToken("")}
+                className="h-9 px-3 rounded-lg border border-border text-sm text-muted-foreground hover:border-primary/30 hover:text-foreground transition-colors"
+              >
+                {t("common.clear", "Clear")}
+              </button>
+            ) : null}
+          </div>
+          <a
+            href="https://github.com/settings/tokens"
+            target="_blank"
+            rel="noopener noreferrer"
+            className="inline-flex items-center gap-1 text-xs text-primary hover:underline"
+          >
+            <ExternalLinkIcon className="h-3 w-3" />
+            {t("settings.githubTokenLearnMore", "Create a personal access token")}
+          </a>
+          <p className="text-[11px] text-muted-foreground/80">
+            {t(
+              "settings.githubTokenScopeHint",
+              "A read-only token without any scope (public repositories) is enough for the skill store.",
+            )}
Evidence
PR Compliance ID 10 requires all user-facing UI strings to come from i18n via t() with no
hardcoded UI text in source. The PR adds new UI strings as inline fallback arguments (e.g., `"GitHub
Access Token", "Optional. Attach a GitHub personal access token...", "Create a personal access
token"`).

AGENTS.md
apps/desktop/src/renderer/components/settings/SkillSettings.tsx[161-229]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New user-facing strings are embedded in source as fallback values passed to `t()`. Compliance requires UI text to be sourced from locale JSON keys (without inline hardcoded UI strings).
## Issue Context
The locale keys already exist for the GitHub token settings section; the component can rely on them directly.
## Fix Focus Areas
- apps/desktop/src/renderer/components/settings/SkillSettings.tsx[161-229]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. DB init on every fetch 🐞 Bug ➹ Performance
Description
SkillInstaller.fetchRemoteContent calls initDatabase() for every remote fetch to read githubToken,
which re-runs desktop wrapper filesystem checks (db path resolution + pre-upgrade backup marker
checks) each time. This adds avoidable synchronous FS work to skill-store fetch loops.
Code

apps/desktop/src/main/services/skill-installer.ts[R827-843]

+      let githubToken: string | null = null;
+      try {
+        const db = initDatabase();
+        if (db && typeof db.prepare === "function") {
+          githubToken = getGithubTokenSetting(db);
+        }
+      } catch (tokenError) {
+        // DB may be unavailable during very early startup or in tests —
+        // fall back to an unauthenticated request without failing the
+        // fetch.
+        // DB 不可用时(启动初期或测试场景)回落到未登录请求。
+        console.warn(
+          "Unable to load githubToken setting, continuing unauthenticated:",
+          tokenError instanceof Error ? tokenError.message : "unknown",
+        );
+      }
+      return await fetchRemoteText(url, 0, { githubToken });
Evidence
Even though the underlying @prompthub/db initDatabase() caches the DB connection, the desktop
wrapper always calls getDbPath() and ensurePreUpgradeBackup(dbPath) before delegating, which
performs fs.existsSync/fs.statSync checks. fetchRemoteContent is invoked per remote fetch, so these
sync FS checks are repeated unnecessarily.

apps/desktop/src/main/services/skill-installer.ts[817-844]
apps/desktop/src/main/database/index.ts[68-171]
packages/db/src/init.ts[39-100]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`SkillInstaller.fetchRemoteContent()` calls the desktop `initDatabase()` wrapper on every remote fetch just to read `githubToken`. The wrapper does synchronous filesystem checks each call (path resolution + backup marker checks), creating avoidable overhead in remote fetch-heavy workflows.
## Issue Context
The underlying DB layer caches the connection, but the desktop wrapper still performs work before returning it. For remote skill store operations that fetch multiple files, this overhead can accumulate.
## Fix Focus Areas
- apps/desktop/src/main/services/skill-installer.ts[817-844]
- apps/desktop/src/main/database/index.ts[68-171]
### Suggested approach
- Avoid calling the desktop wrapper per request:
- Prefer `getDatabase()` when available, falling back to `initDatabase()` only once.
- Or cache `githubToken` in module/static scope (optionally with an invalidation mechanism when settings change).
- Keep the existing try/catch behavior (unauthenticated fallback) but make the DB/token retrieval constant-time for repeated fetches.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@apps/desktop/src/main/ipc/settings.ipc.ts`:
- Around line 76-81: The catch block that handles reading the githubToken
setting currently logs only e.message which loses stack info; update the catch
for the githubToken read (the catch (e) around the githubToken logic) to log the
full Error object (e) or e.stack via console.error so the full stack is
preserved while still ensuring you do not print the token value itself; keep the
existing descriptive prefix ("Failed to read githubToken setting:") and either
re-throw or return/handle after logging according to surrounding flow to satisfy
the "no empty catch" guideline.

In `@apps/desktop/src/main/services/skill-installer.ts`:
- Around line 833-842: The catch block that swallows token loading failures
(catch (tokenError) in skill-installer.ts when reading the githubToken setting)
must not silently fall back to an unauthenticated request; change the behavior
to surface the failure to callers by either rethrowing a contextual Error (e.g.,
wrap tokenError with a message like "failed to load githubToken setting") or
returning a clear degradation status/value (e.g., { ok: false, reason:
'token_load_failed', error: tokenError }) from the function that attempts the
fetch; update any callers of that function (the code that performs the GitHub
fetch) to handle the new error/status instead of assuming anonymous requests.
Ensure you reference tokenError and the githubToken read logic so the change
affects all code paths that currently rely on the silent fallback.

In `@apps/desktop/src/renderer/stores/settings.store.ts`:
- Around line 426-431: The settings.store currently persists githubToken in the
renderer (the githubToken field and any zustand persist usage), which must be
removed: stop storing the actual PAT in the renderer persist/localStorage, keep
only a session-state flag like githubTokenConfigured or githubTokenSet in the
settings store, and route all get/set operations for the real token through the
main process secure storage (e.g., Electron safeStorage or system credential
store) via IPC handlers (implement IPC methods such as
getGithubToken/setGithubToken in the main process and call them from settings
actions instead of writing to persist), and remove any code in settings.store
that writes the raw token into zustand persist/localStorage (also update code
paths that read the token to use the IPC get call).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b5ecf592-138c-4cab-b4e6-b0300e1f9911

📥 Commits

Reviewing files that changed from the base of the PR and between ebc43d9 and f6fb7de.

📒 Files selected for processing (16)
  • apps/desktop/src/main/ipc/settings.ipc.ts
  • apps/desktop/src/main/services/skill-installer-remote.ts
  • apps/desktop/src/main/services/skill-installer.ts
  • apps/desktop/src/renderer/components/settings/SkillSettings.tsx
  • apps/desktop/src/renderer/i18n/locales/de.json
  • apps/desktop/src/renderer/i18n/locales/en.json
  • apps/desktop/src/renderer/i18n/locales/es.json
  • apps/desktop/src/renderer/i18n/locales/fr.json
  • apps/desktop/src/renderer/i18n/locales/ja.json
  • apps/desktop/src/renderer/i18n/locales/zh-TW.json
  • apps/desktop/src/renderer/i18n/locales/zh.json
  • apps/desktop/src/renderer/stores/settings.store.ts
  • apps/desktop/tests/unit/main/github-token-setting.test.ts
  • apps/desktop/tests/unit/main/skill-installer-remote.test.ts
  • apps/desktop/tests/unit/stores/settings-github-token.test.ts
  • packages/shared/types/settings.ts

Comment on lines +76 to +81
} catch (e) {
// Never log the token content, only the error class.
console.error(
'Failed to read githubToken setting:',
e instanceof Error ? e.message : 'unknown',
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

保留完整错误堆栈,避免排障信息丢失。

Line 78-81 目前只记录 e.message,会丢失关键堆栈信息。建议直接记录 Error 对象(仍不打印 token 内容)。

建议修改
-    console.error(
-      'Failed to read githubToken setting:',
-      e instanceof Error ? e.message : 'unknown',
-    );
+    console.error('Failed to read githubToken setting:', e);

As per coding guidelines "No empty catch blocks are allowed; every catch must either re-throw, log with full stack, or handle the error meaningfully".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (e) {
// Never log the token content, only the error class.
console.error(
'Failed to read githubToken setting:',
e instanceof Error ? e.message : 'unknown',
);
} catch (e) {
// Never log the token content, only the error class.
console.error('Failed to read githubToken setting:', e);
🤖 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 `@apps/desktop/src/main/ipc/settings.ipc.ts` around lines 76 - 81, The catch
block that handles reading the githubToken setting currently logs only e.message
which loses stack info; update the catch for the githubToken read (the catch (e)
around the githubToken logic) to log the full Error object (e) or e.stack via
console.error so the full stack is preserved while still ensuring you do not
print the token value itself; keep the existing descriptive prefix ("Failed to
read githubToken setting:") and either re-throw or return/handle after logging
according to surrounding flow to satisfy the "no empty catch" guideline.

Comment on lines +833 to +842
} catch (tokenError) {
// DB may be unavailable during very early startup or in tests —
// fall back to an unauthenticated request without failing the
// fetch.
// DB 不可用时(启动初期或测试场景)回落到未登录请求。
console.warn(
"Unable to load githubToken setting, continuing unauthenticated:",
tokenError instanceof Error ? tokenError.message : "unknown",
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

避免静默降级为未鉴权请求。

Line 833-842 捕获 token 读取失败后继续请求,会把“配置/DB异常”伪装成“正常匿名请求”,后续只表现为 rate-limit 问题。建议向调用方显式暴露失败(抛出上下文错误或返回可判定的降级状态)。

As per coding guidelines "Functions must not swallow errors and return default values; if an operation can fail, the caller must know".

🤖 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 `@apps/desktop/src/main/services/skill-installer.ts` around lines 833 - 842,
The catch block that swallows token loading failures (catch (tokenError) in
skill-installer.ts when reading the githubToken setting) must not silently fall
back to an unauthenticated request; change the behavior to surface the failure
to callers by either rethrowing a contextual Error (e.g., wrap tokenError with a
message like "failed to load githubToken setting") or returning a clear
degradation status/value (e.g., { ok: false, reason: 'token_load_failed', error:
tokenError }) from the function that attempts the fetch; update any callers of
that function (the code that performs the GitHub fetch) to handle the new
error/status instead of assuming anonymous requests. Ensure you reference
tokenError and the githubToken read logic so the change affects all code paths
that currently rely on the silent fallback.

Comment on lines +426 to +431
// GitHub personal access token for authenticated skill-store fetches
// Used to raise the GitHub API rate limit from 60 req/h (unauthenticated)
// to 5000 req/h (authenticated). Only sent to api.github.com and
// raw.githubusercontent.com. See #108.
// GitHub PAT,提升 Skill Store 的 GitHub API 请求限额 (#108)。
githubToken: string;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

不要在 renderer 持久化层长期明文保存 GitHub PAT。

当前实现把 githubToken 落到 zustand persist(localStorage)。这会扩大泄露面:一旦 renderer 侧被注入或被调试脚本读取,PAT 可直接外流。建议将 PAT 仅放在主进程安全存储(如 safeStorage/系统凭据库),renderer 仅持有会话态与“已配置”标记。

Also applies to: 651-652, 1272-1282

🤖 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 `@apps/desktop/src/renderer/stores/settings.store.ts` around lines 426 - 431,
The settings.store currently persists githubToken in the renderer (the
githubToken field and any zustand persist usage), which must be removed: stop
storing the actual PAT in the renderer persist/localStorage, keep only a
session-state flag like githubTokenConfigured or githubTokenSet in the settings
store, and route all get/set operations for the real token through the main
process secure storage (e.g., Electron safeStorage or system credential store)
via IPC handlers (implement IPC methods such as getGithubToken/setGithubToken in
the main process and call them from settings actions instead of writing to
persist), and remove any code in settings.store that writes the raw token into
zustand persist/localStorage (also update code paths that read the token to use
the IPC get call).

Comment on lines +47 to +49
const stmt = db.prepare('SELECT value FROM settings WHERE key = ?');
const row = stmt.get('githubToken') as { value: string } | undefined;
if (!row) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. as assertions undocumented 📘 Rule violation ⚙ Maintainability

New as type assertions were added without an explanatory comment or clear necessity, weakening
TypeScript strictness and potentially masking type errors. This violates the rule restricting type
assertions unless justified with a comment.
Agent Prompt
## Issue description
New `as` assertions were introduced without justification comments, violating the strictness rule and potentially hiding type mismatches.

## Issue Context
Casts appear in main-process settings DB reads, renderer store sync, and tests. Prefer safe typing (generics, runtime guards, proper types) over assertions; if an assertion is truly required for interop, add a brief comment explaining why it is safe/necessary.

## Fix Focus Areas
- apps/desktop/src/main/ipc/settings.ipc.ts[47-49]
- apps/desktop/src/renderer/stores/settings.store.ts[1277-1282]
- apps/desktop/tests/unit/stores/settings-github-token.test.ts[44-46]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +23 to +24
import { initDatabase } from "../database";
import { getGithubTokenSetting } from "../ipc/settings.ipc";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Relative imports in skill-installer 📘 Rule violation ⚙ Maintainability

New imports in the main process use relative paths instead of the documented @/ alias for
src/main, increasing coupling to folder structure and risking circularity/drift. This violates the
import/path-alias compliance rule.
Agent Prompt
## Issue description
New code adds relative imports in a `src/main` module where the repository requires documented aliases.

## Issue Context
The compliance rule requires `@/` for main-process imports to keep paths stable and avoid drift.

## Fix Focus Areas
- apps/desktop/src/main/services/skill-installer.ts[23-24]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread apps/desktop/src/main/ipc/settings.ipc.ts Outdated
Comment on lines +23 to +24
import { initDatabase } from "../database";
import { getGithubTokenSetting } from "../ipc/settings.ipc";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

4. Cli pulls electron dependency 🐞 Bug ≡ Correctness

SkillInstaller now imports getGithubTokenSetting from settings.ipc.ts, which has a top-level import
from 'electron'. Because the CLI imports SkillInstaller and the CLI build config doesn’t externalize
'electron' (and electron is only a devDependency), the published CLI can fail to bundle or crash at
startup with “Cannot find module 'electron'”.
Agent Prompt
## Issue description
`SkillInstaller` (used by the Node CLI) now imports `getGithubTokenSetting` from `main/ipc/settings.ipc.ts`, which has a top-level `import { ipcMain } from 'electron'`. This makes the CLI bundle/runtime require the `electron` module even though the published CLI package doesn’t ship it as a dependency.

## Issue Context
The helper `getGithubTokenSetting(db)` is electron-independent, but it currently lives in an Electron IPC module. The CLI build config also does not externalize `electron`, making this a high-risk transitive dependency.

## Fix Focus Areas
- apps/desktop/src/main/services/skill-installer.ts[20-24]
- apps/desktop/src/main/ipc/settings.ipc.ts[1-10]

### Suggested approach
- Move `getGithubTokenSetting` into a new electron-free module (e.g. `apps/desktop/src/main/settings/github-token.ts` or `apps/desktop/src/main/database/settings.ts`).
- Import that helper from both:
  - `settings.ipc.ts` (for IPC usage)
  - `skill-installer.ts` (for fetch usage)
- Keep `ipcMain` imports only in IPC registration code, not in modules shared with the CLI entry graph.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

AGENTS.md forbids hardcoded Chinese characters in source files outside
the locale JSON resources. The review flagged every Chinese bilingual
comment block this PR added. Removed them so only the English
explanations remain.
@legeling legeling merged commit 23b8058 into legeling:main May 10, 2026
1 of 2 checks passed
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.

拉取远程商店失败: GitHub API 请求限额已达到,请几分钟后重试,或在设置中添加 GitHub Token。

2 participants