mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-27 18:22:41 -08:00
chore: migrate repo workflows to Bun-only runtime
This commit is contained in:
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@@ -20,11 +20,6 @@ jobs:
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
|
||||
15
.github/workflows/release.yml
vendored
15
.github/workflows/release.yml
vendored
@@ -26,11 +26,6 @@ jobs:
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
@@ -78,11 +73,6 @@ jobs:
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
@@ -128,11 +118,6 @@ jobs:
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
|
||||
13
README.md
13
README.md
@@ -74,13 +74,14 @@ subminer video.mkv
|
||||
|
||||
## Requirements
|
||||
|
||||
| Required | Optional |
|
||||
| ------------------------------------------ | ---------------------------- |
|
||||
| `mpv` with IPC socket | `yt-dlp` |
|
||||
| Required | Optional |
|
||||
| ------------------------------------------ | -------------------------------------------------- |
|
||||
| `bun` | |
|
||||
| `mpv` with IPC socket | `yt-dlp` |
|
||||
| `ffmpeg` | `guessit` (better AniSkip title/episode detection) |
|
||||
| `mecab` + `mecab-ipadic` | `fzf` / `rofi` |
|
||||
| Linux: `hyprctl` or `xdotool` + `xwininfo` | `chafa`, `ffmpegthumbnailer` |
|
||||
| macOS: Accessibility permission | |
|
||||
| `mecab` + `mecab-ipadic` | `fzf` / `rofi` |
|
||||
| Linux: `hyprctl` or `xdotool` + `xwininfo` | `chafa`, `ffmpegthumbnailer` |
|
||||
| macOS: Accessibility permission | |
|
||||
|
||||
## Documentation
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
id: TASK-115
|
||||
title: Migrate repository workflows to Bun-only JavaScript runtime
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-23 04:26'
|
||||
updated_date: '2026-02-23 04:42'
|
||||
labels:
|
||||
- tooling
|
||||
- build
|
||||
- ci
|
||||
dependencies: []
|
||||
references:
|
||||
- package.json
|
||||
- .github/workflows/ci.yml
|
||||
- .github/workflows/release.yml
|
||||
- docs/development.md
|
||||
- docs/installation.md
|
||||
- README.md
|
||||
documentation:
|
||||
- docs/plans/2026-02-23-bun-only-toolchain-migration.md
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Transition the project from mixed Bun/Node tooling to a Bun-only contributor and CI workflow so setup is simpler and runtime behavior is consistent across local and automation lanes.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 A contributor can install dependencies, build, run source tests, run dist smoke tests, and build docs using Bun without requiring a system Node.js install.
|
||||
- [x] #2 CI and release workflows no longer require Node setup steps for standard project verification and packaging jobs.
|
||||
- [x] #3 Dist and utility verification lanes remain covered and pass with Bun-based commands.
|
||||
- [x] #4 Repository documentation clearly states the Bun-only prerequisite and removes conflicting Node requirement guidance.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Execution started in-session after user selected Subagent-Driven option. Tracking sequence: TASK-115.2 -> TASK-115.3/TASK-115.4 -> TASK-115.1.
|
||||
|
||||
Completed child sequence TASK-115.2 -> TASK-115.3/TASK-115.4 -> TASK-115.1 with Bun-only command/workflow migration.
|
||||
|
||||
Direct Node command usage removed from package dist and utility script lanes; CI/release Node setup steps removed; contributor docs aligned to Bun-only prerequisites.
|
||||
|
||||
Regression hardening: refactored AniList token-store tests and storage seam to remain stable under Bun dist test execution while preserving runtime behavior.
|
||||
|
||||
Post-migration docs polish: updated `README.md` requirements table to list Bun as a required dependency and promoted Bun from optional-tools section into system dependencies in `docs/installation.md` to remove ambiguity.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Migrated repository verification and workflow surfaces to Bun-first execution by replacing `node --test` dist lanes and remaining direct Node utility invocation (`generate:config-example`) with Bun commands.
|
||||
|
||||
Removed `actions/setup-node` from CI and release jobs and aligned docs (`docs/development.md`, `docs/installation.md`) to Bun-only setup guidance; launcher smoke fixtures now use Bun shebangs so smoke tests no longer depend on Node.
|
||||
|
||||
Validated full gate set under Bun-only command path: launcher smoke, fast source suite, build, dist config/core/smoke suites, docs build, plus config-example generation command.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
id: TASK-115.1
|
||||
title: Remove Node setup from workflows and finalize Bun-only docs
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-23 04:26'
|
||||
updated_date: '2026-02-23 04:36'
|
||||
labels:
|
||||
- ci
|
||||
- docs
|
||||
- tooling
|
||||
dependencies:
|
||||
- TASK-115.2
|
||||
- TASK-115.3
|
||||
- TASK-115.4
|
||||
references:
|
||||
- .github/workflows/ci.yml
|
||||
- .github/workflows/release.yml
|
||||
- docs/development.md
|
||||
- docs/installation.md
|
||||
- README.md
|
||||
documentation:
|
||||
- docs/plans/2026-02-23-bun-only-toolchain-migration.md
|
||||
parent_task_id: TASK-115
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
After script/test migration parity is verified, remove Node setup from CI/release workflows and finalize documentation so contributors and automation use a consistent Bun-only prerequisite.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 CI and release workflows no longer include Node setup steps for routine build/test/package jobs.
|
||||
- [x] #2 Primary local verification gates pass with the Bun-only command set.
|
||||
- [x] #3 README and setup/install docs consistently describe Bun as the JS runtime prerequisite without conflicting Node requirement text.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Removed `actions/setup-node` from `.github/workflows/ci.yml` and `.github/workflows/release.yml` (quality-gate, build-linux, build-macos jobs).
|
||||
|
||||
Updated docs to align Bun-only prerequisite: removed Node from `docs/development.md` prerequisites and switched stale pnpm source-build snippet in `docs/installation.md` to Bun.
|
||||
|
||||
Validation gates: `bun run test:launcher:smoke:src && bun run test:fast && bun run build && bun run test:config:dist && bun run test:core:dist && bun run test:smoke:dist && bun run docs:build` all pass.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
id: TASK-115.2
|
||||
title: Map Node touchpoints and define Bun parity matrix
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-23 04:27'
|
||||
updated_date: '2026-02-23 04:36'
|
||||
labels:
|
||||
- tooling
|
||||
- planning
|
||||
dependencies: []
|
||||
references:
|
||||
- package.json
|
||||
- .github/workflows/ci.yml
|
||||
- .github/workflows/release.yml
|
||||
documentation:
|
||||
- docs/plans/2026-02-23-bun-only-toolchain-migration.md
|
||||
parent_task_id: TASK-115
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Document all Node-specific command and workflow touchpoints, define Bun replacements, and establish migration guardrails so follow-up implementation tasks have clear scope and risk controls.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 A complete inventory of Node-dependent scripts and workflow steps is documented with proposed Bun equivalents.
|
||||
- [x] #2 Migration risks and compatibility checkpoints are explicitly documented for dist and Electron-related lanes.
|
||||
- [x] #3 Follow-up implementation tasks have clear boundaries and ordering based on the parity matrix.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Created `docs/plans/bun-only-parity-matrix.md` with direct Node touchpoint inventory, Bun replacements, risk checkpoints, and migration sequencing.
|
||||
|
||||
Parity matrix now links implementation ordering for TASK-115.3/115.4/115.1 and flags follow-up risk handling for third-party implicit Node assumptions.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
id: TASK-115.3
|
||||
title: Migrate dist test lanes from Node runner to Bun
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-23 04:27'
|
||||
updated_date: '2026-02-23 04:36'
|
||||
labels:
|
||||
- test
|
||||
- tooling
|
||||
dependencies:
|
||||
- TASK-115.2
|
||||
references:
|
||||
- package.json
|
||||
- docs/development.md
|
||||
- docs/installation.md
|
||||
documentation:
|
||||
- docs/plans/2026-02-23-bun-only-toolchain-migration.md
|
||||
parent_task_id: TASK-115
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Replace Node-runner dist test commands with Bun-based dist verification while preserving existing test scope and confidence in compiled artifact behavior.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Dist test scripts execute via Bun instead of node --test while preserving the current dist test file coverage.
|
||||
- [x] #2 Dist smoke and full dist verification lanes pass under Bun-based commands.
|
||||
- [x] #3 Documentation for dist verification commands reflects the updated Bun-based workflow.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Updated `package.json` dist lanes from `node --test` to `bun test` (`test:config:dist`, `test:config:smoke:dist`, `test:core:dist`, `test:core:smoke:dist`).
|
||||
|
||||
Resolved Bun dist compatibility gap by injecting `SafeStorageLike` into AniList token store and replacing brittle Electron global monkey-patching in `anilist-token-store` tests.
|
||||
|
||||
Validation: `bun run build && bun run test:config:dist && bun run test:core:dist && bun run test:smoke:dist` all pass.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
id: TASK-115.4
|
||||
title: Migrate Node-invoked utility script commands to Bun
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-23 04:27'
|
||||
updated_date: '2026-02-23 04:36'
|
||||
labels:
|
||||
- tooling
|
||||
- scripts
|
||||
dependencies:
|
||||
- TASK-115.2
|
||||
references:
|
||||
- package.json
|
||||
- scripts/get_frequency.ts
|
||||
- scripts/test-yomitan-parser.ts
|
||||
documentation:
|
||||
- docs/plans/2026-02-23-bun-only-toolchain-migration.md
|
||||
parent_task_id: TASK-115
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Update utility command entrypoints that still rely on Node invocation so maintenance and diagnostics workflows can be run entirely through Bun.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Utility script commands that currently invoke Node have Bun-based equivalents as the default project commands.
|
||||
- [x] #2 Electron-targeted utility flows remain functional after command migration.
|
||||
- [x] #3 Contributor docs reflect the updated Bun-based utility command usage.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Updated `generate:config-example` command from `node dist/generate-config-example.js` to `bun dist/generate-config-example.js`.
|
||||
|
||||
Updated launcher smoke fixture executables from `#!/usr/bin/env node` to `#!/usr/bin/env bun` so smoke tests do not assume Node presence.
|
||||
|
||||
Validation: `bun run test:launcher:smoke:src` and `bun run generate:config-example` pass with Bun-first command path.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org/) (LTS)
|
||||
- [Bun](https://bun.sh)
|
||||
|
||||
## Setup
|
||||
@@ -150,22 +149,22 @@ Run `make help` for a full list of targets. Key ones:
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description |
|
||||
| ---------------------------------- | ------------------------------------------------------------------------------- |
|
||||
| `SUBMINER_APPIMAGE_PATH` | Override SubMiner app binary path for launcher playback commands |
|
||||
| `SUBMINER_BINARY_PATH` | Alias for `SUBMINER_APPIMAGE_PATH` |
|
||||
| `SUBMINER_ROFI_THEME` | Override rofi theme path for launcher picker |
|
||||
| Variable | Description |
|
||||
| ---------------------------------- | ------------------------------------------------------------------------------ |
|
||||
| `SUBMINER_APPIMAGE_PATH` | Override SubMiner app binary path for launcher playback commands |
|
||||
| `SUBMINER_BINARY_PATH` | Alias for `SUBMINER_APPIMAGE_PATH` |
|
||||
| `SUBMINER_ROFI_THEME` | Override rofi theme path for launcher picker |
|
||||
| `SUBMINER_LOG_LEVEL` | Override app logger level (`debug`, `info`, `warn`, `error`) |
|
||||
| `SUBMINER_MPV_LOG` | Override mpv/app shared log file path |
|
||||
| `SUBMINER_YT_SUBGEN_MODE` | Override `youtubeSubgen.mode` for launcher |
|
||||
| `SUBMINER_WHISPER_BIN` | Override `youtubeSubgen.whisperBin` for launcher |
|
||||
| `SUBMINER_WHISPER_MODEL` | Override `youtubeSubgen.whisperModel` for launcher |
|
||||
| `SUBMINER_YT_SUBGEN_OUT_DIR` | Override generated subtitle output directory |
|
||||
| `SUBMINER_YT_SUBGEN_AUDIO_FORMAT` | Override extraction format used for whisper fallback |
|
||||
| `SUBMINER_YT_SUBGEN_KEEP_TEMP` | Set to `1` to keep temporary subtitle-generation workspace |
|
||||
| `SUBMINER_JIMAKU_API_KEY` | Override Jimaku API key for launcher subtitle downloads |
|
||||
| `SUBMINER_JIMAKU_API_KEY_COMMAND` | Command used to resolve Jimaku API key at runtime |
|
||||
| `SUBMINER_JIMAKU_API_BASE_URL` | Override Jimaku API base URL |
|
||||
| `SUBMINER_JELLYFIN_ACCESS_TOKEN` | Override Jellyfin access token (used before stored encrypted session fallback) |
|
||||
| `SUBMINER_JELLYFIN_USER_ID` | Optional Jellyfin user ID override |
|
||||
| `SUBMINER_MPV_LOG` | Override mpv/app shared log file path |
|
||||
| `SUBMINER_YT_SUBGEN_MODE` | Override `youtubeSubgen.mode` for launcher |
|
||||
| `SUBMINER_WHISPER_BIN` | Override `youtubeSubgen.whisperBin` for launcher |
|
||||
| `SUBMINER_WHISPER_MODEL` | Override `youtubeSubgen.whisperModel` for launcher |
|
||||
| `SUBMINER_YT_SUBGEN_OUT_DIR` | Override generated subtitle output directory |
|
||||
| `SUBMINER_YT_SUBGEN_AUDIO_FORMAT` | Override extraction format used for whisper fallback |
|
||||
| `SUBMINER_YT_SUBGEN_KEEP_TEMP` | Set to `1` to keep temporary subtitle-generation workspace |
|
||||
| `SUBMINER_JIMAKU_API_KEY` | Override Jimaku API key for launcher subtitle downloads |
|
||||
| `SUBMINER_JIMAKU_API_KEY_COMMAND` | Command used to resolve Jimaku API key at runtime |
|
||||
| `SUBMINER_JIMAKU_API_BASE_URL` | Override Jimaku API base URL |
|
||||
| `SUBMINER_JELLYFIN_ACCESS_TOKEN` | Override Jellyfin access token (used before stored encrypted session fallback) |
|
||||
| `SUBMINER_JELLYFIN_USER_ID` | Optional Jellyfin user ID override |
|
||||
| `SUBMINER_SKIP_MACOS_HELPER_BUILD` | Set to `1` to skip building the macOS helper binary during `bun run build` |
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
| Dependency | Required | Notes |
|
||||
| -------------------- | ---------- | -------------------------------------------------------- |
|
||||
| Bun | Yes | Required for `subminer` wrapper and source workflows |
|
||||
| mpv | Yes | Must support IPC sockets (`--input-ipc-server`) |
|
||||
| ffmpeg | For media | Audio extraction and screenshot generation |
|
||||
| MeCab + mecab-ipadic | No | Optional fallback tokenizer for Japanese |
|
||||
@@ -24,16 +25,15 @@
|
||||
|
||||
### Optional Tools
|
||||
|
||||
| Tool | Purpose |
|
||||
| ----------------- | ------------------------------------------ |
|
||||
| fzf | Terminal-based video picker (default) |
|
||||
| rofi | GUI-based video picker |
|
||||
| chafa | Thumbnail previews in fzf |
|
||||
| ffmpegthumbnailer | Generate video thumbnails for picker |
|
||||
| Tool | Purpose |
|
||||
| ----------------- | ------------------------------------------------------------- |
|
||||
| fzf | Terminal-based video picker (default) |
|
||||
| rofi | GUI-based video picker |
|
||||
| chafa | Thumbnail previews in fzf |
|
||||
| ffmpegthumbnailer | Generate video thumbnails for picker |
|
||||
| guessit | Better AniSkip title/season/episode parsing for file playback |
|
||||
| alass | Subtitle sync engine (preferred) |
|
||||
| ffsubsync | Subtitle sync engine (fallback) |
|
||||
| Bun | Required for the `subminer` wrapper script |
|
||||
| alass | Subtitle sync engine (preferred) |
|
||||
| ffsubsync | Subtitle sync engine (fallback) |
|
||||
|
||||
## Linux
|
||||
|
||||
@@ -88,7 +88,7 @@ brew install mpv mecab mecab-ipadic
|
||||
git clone https://github.com/ksyasuda/SubMiner.git
|
||||
cd SubMiner
|
||||
bun install
|
||||
cd vendor/texthooker-ui && pnpm install --frozen-lockfile && pnpm run build && cd ../..
|
||||
cd vendor/texthooker-ui && bun install --frozen-lockfile && bun run build && cd ../..
|
||||
bun run build:mac
|
||||
```
|
||||
|
||||
|
||||
@@ -94,3 +94,5 @@ Read first. Keep concise.
|
||||
| `codex-architecture-doc-refresh-20260223T033941Z-d6se` | `codex-architecture-doc-refresh` | `Review repository architecture surfaces and refresh docs/architecture.md content to match current code state.` | `handoff` | `docs/subagents/agents/codex-architecture-doc-refresh-20260223T033941Z-d6se.md` | `2026-02-23T03:44:17Z` |
|
||||
| `codex-docs-video-thumb-cache-20260223T033929Z-k8p2` | `codex-docs-video-thumb-cache` | `Fix docs landing page demo video thumbnail staleness after direct asset replacement.` | `handoff` | `docs/subagents/agents/codex-docs-video-thumb-cache-20260223T033929Z-k8p2.md` | `2026-02-23T03:44:04Z` |
|
||||
| `codex-development-docs-review-20260223T034520Z-2ebb` | `codex-development-docs-review` | `Review codebase and refresh docs/development.md to match current project state.` | `done` | `docs/subagents/agents/codex-development-docs-review-20260223T034520Z-2ebb.md` | `2026-02-23T03:49:16Z` |
|
||||
| `opencode-bun-migration-20260223T043000Z-k9m2` | `opencode-bun-migration` | `Execute TASK-115 Bun-only migration plan: parity map, dist/utility script migration, CI/docs cutover.` | `handoff` | `docs/subagents/agents/opencode-bun-migration-20260223T043000Z-k9m2.md` | `2026-02-23T04:36:00Z` |
|
||||
| `opencode-initial-release-plan-20260223T044059Z-p7k2` | `opencode-initial-release-plan` | `Analyze main history and draft copy/paste initial-release history-cleanup plan.` | `planning` | `docs/subagents/agents/opencode-initial-release-plan-20260223T044059Z-p7k2.md` | `2026-02-23T04:40:59Z` |
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Agent Log: opencode-bun-migration-20260223T043000Z-k9m2
|
||||
|
||||
- alias: `opencode-bun-migration`
|
||||
- mission: `Execute TASK-115 Bun-only migration plan: parity map, dist/utility script migration, CI/docs cutover.`
|
||||
- status: `handoff`
|
||||
- started_utc: `2026-02-23T04:30:00Z`
|
||||
- backlog: `TASK-115` (children: `TASK-115.2`, `TASK-115.3`, `TASK-115.4`, `TASK-115.1`)
|
||||
|
||||
## Intent
|
||||
|
||||
- Execute user-selected Subagent-Driven option for Bun-only migration.
|
||||
- Sequence: `TASK-115.2` -> (`TASK-115.3` + `TASK-115.4`) -> `TASK-115.1`.
|
||||
|
||||
## Planned Files
|
||||
|
||||
- `package.json`
|
||||
- `.github/workflows/ci.yml`
|
||||
- `.github/workflows/release.yml`
|
||||
- `docs/development.md`
|
||||
- `docs/installation.md`
|
||||
- `README.md`
|
||||
- `docs/plans/bun-only-parity-matrix.md`
|
||||
|
||||
## Assumptions
|
||||
|
||||
- Bun can execute dist test files currently run by `node --test`.
|
||||
- Electron-targeted utility scripts can keep Electron runtime while shifting command wrappers to Bun.
|
||||
- No behavior changes intended; tooling/runtime-command migration only.
|
||||
|
||||
## Phase Updates
|
||||
|
||||
- `2026-02-23T04:30:00Z` start: loaded subagent index/collaboration, created agent row/file, beginning implementation on `TASK-115.2`.
|
||||
- `2026-02-23T04:36:00Z` completed: migrated dist/utility scripts from Node invocations to Bun, removed Node setup from CI/release workflows, updated setup docs, added Bun parity matrix, and validated full Bun command gate suite.
|
||||
|
||||
## Files Touched
|
||||
|
||||
- `.github/workflows/ci.yml`
|
||||
- `.github/workflows/release.yml`
|
||||
- `package.json`
|
||||
- `docs/development.md`
|
||||
- `docs/installation.md`
|
||||
- `docs/plans/bun-only-parity-matrix.md`
|
||||
- `launcher/smoke.e2e.test.ts`
|
||||
- `src/core/services/anilist/anilist-token-store.ts`
|
||||
- `src/core/services/anilist/anilist-token-store.test.ts`
|
||||
|
||||
## Validation
|
||||
|
||||
- `bun run test:launcher:smoke:src`
|
||||
- `bun run test:fast`
|
||||
- `bun run build`
|
||||
- `bun run test:config:dist`
|
||||
- `bun run test:core:dist`
|
||||
- `bun run test:smoke:dist`
|
||||
- `bun run docs:build`
|
||||
- `bun run generate:config-example`
|
||||
|
||||
## Handoff
|
||||
|
||||
- Backlog status synced: `TASK-115`, `TASK-115.1`, `TASK-115.2`, `TASK-115.3`, `TASK-115.4` set Done with AC evidence.
|
||||
- Note: `config.example.jsonc` and `docs/public/config.example.jsonc` were regenerated by `bun run generate:config-example` during validation.
|
||||
@@ -166,3 +166,6 @@ Shared notes. Append-only.
|
||||
- [2026-02-23T03:44:17Z] [codex-architecture-doc-refresh-20260223T033941Z-d6se|codex-architecture-doc-refresh] completed architecture drift pass: refreshed `docs/architecture.md` structure/service/composition/lifecycle content against current code (`src`, `launcher`, `plugin`), left mermaid sections untouched, and verified `bun run docs:build`; moved backlog linkage to `TASK-113` to avoid active `TASK-112` collision.
|
||||
- [2026-02-23T03:46:06Z] [codex-development-docs-review-20260223T034520Z-2ebb|codex-development-docs-review] starting user-requested thorough codebase + `docs/development.md` drift audit; scope docs refresh + verification only, no runtime behavior changes expected.
|
||||
- [2026-02-23T03:49:16Z] [codex-development-docs-review-20260223T034520Z-2ebb|codex-development-docs-review] completed `docs/development.md` refresh: setup/deps/submodule instructions corrected, CI-parity testing lane documented, placeholder subtitle test status clarified, Makefile reference adjusted, env variable table expanded to active launcher/runtime overrides; `bun run docs:build` passed.
|
||||
- [2026-02-23T04:30:00Z] [opencode-bun-migration-20260223T043000Z-k9m2|opencode-bun-migration] starting TASK-115 Bun-only migration execution; initial scope `package.json`, CI/release workflows, and setup docs to remove Node requirements after parity checks.
|
||||
- [2026-02-23T04:36:00Z] [opencode-bun-migration-20260223T043000Z-k9m2|opencode-bun-migration] completed TASK-115 Bun-only migration pass: dist/utility commands moved off direct Node invocation, CI/release Node setup removed, Bun parity matrix + docs updates landed, full Bun validation gate suite passed, and TASK-115 + child tasks finalized Done in Backlog.
|
||||
- [2026-02-23T04:40:59Z] [opencode-initial-release-plan-20260223T044059Z-p7k2|opencode-initial-release-plan] starting user-requested release-history cleanup planning pass; scope git-history analysis + current-state review + `initial-release.md` command playbook.
|
||||
|
||||
@@ -62,7 +62,7 @@ function createSmokeCase(name: string): SmokeCase {
|
||||
|
||||
writeExecutable(
|
||||
fakeMpvPath,
|
||||
`#!/usr/bin/env node
|
||||
`#!/usr/bin/env bun
|
||||
const fs = require('node:fs');
|
||||
const net = require('node:net');
|
||||
const path = require('node:path');
|
||||
@@ -101,7 +101,7 @@ process.on('SIGTERM', closeAndExit);
|
||||
|
||||
writeExecutable(
|
||||
fakeAppPath,
|
||||
`#!/usr/bin/env node
|
||||
`#!/usr/bin/env bun
|
||||
const fs = require('node:fs');
|
||||
|
||||
const logPath = ${JSON.stringify(fakeAppLogPath)};
|
||||
|
||||
10
package.json
10
package.json
@@ -17,13 +17,13 @@
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check .",
|
||||
"test:config:src": "bun test src/config/config.test.ts src/config/path-resolution.test.ts src/config/resolve/anki-connect.test.ts src/config/resolve/subtitle-style.test.ts src/config/resolve/jellyfin.test.ts src/config/definitions/domain-registry.test.ts",
|
||||
"test:config:dist": "node --test dist/config/config.test.js dist/config/path-resolution.test.js dist/config/resolve/anki-connect.test.js dist/config/resolve/subtitle-style.test.js dist/config/resolve/jellyfin.test.js dist/config/definitions/domain-registry.test.js",
|
||||
"test:config:smoke:dist": "node --test dist/config/path-resolution.test.js",
|
||||
"test:config:dist": "bun test dist/config/config.test.js dist/config/path-resolution.test.js dist/config/resolve/anki-connect.test.js dist/config/resolve/subtitle-style.test.js dist/config/resolve/jellyfin.test.js dist/config/definitions/domain-registry.test.js",
|
||||
"test:config:smoke:dist": "bun test dist/config/path-resolution.test.js",
|
||||
"test:launcher:smoke:src": "bun test launcher/smoke.e2e.test.ts",
|
||||
"test:launcher:src": "bun test launcher/config.test.ts launcher/config-domain-parsers.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/commands/command-modules.test.ts launcher/smoke.e2e.test.ts",
|
||||
"test:core:src": "bun test src/cli/args.test.ts src/cli/help.test.ts src/core/services/cli-command.test.ts src/core/services/field-grouping-overlay.test.ts src/core/services/numeric-shortcut-session.test.ts src/core/services/secondary-subtitle.test.ts src/core/services/mpv-render-metrics.test.ts src/core/services/overlay-content-measurement.test.ts src/core/services/mpv-control.test.ts src/core/services/mpv.test.ts src/core/services/runtime-options-ipc.test.ts src/core/services/runtime-config.test.ts src/core/services/config-hot-reload.test.ts src/core/services/discord-presence.test.ts src/core/services/tokenizer.test.ts src/core/services/tokenizer/annotation-stage.test.ts src/core/services/tokenizer/parser-selection-stage.test.ts src/core/services/tokenizer/parser-enrichment-stage.test.ts src/core/services/subsync.test.ts src/core/services/overlay-bridge.test.ts src/core/services/overlay-shortcut-handler.test.ts src/core/services/mining.test.ts src/core/services/anki-jimaku.test.ts src/core/services/jellyfin.test.ts src/core/services/jellyfin-remote.test.ts src/core/services/immersion-tracker-service.test.ts src/core/services/app-ready.test.ts src/core/services/startup-bootstrap.test.ts src/core/services/subtitle-processing-controller.test.ts src/core/services/anilist/anilist-update-queue.test.ts src/core/utils/shortcut-config.test.ts src/renderer/error-recovery.test.ts src/subsync/utils.test.ts src/main/anilist-url-guard.test.ts src/window-trackers/x11-tracker.test.ts launcher/config.test.ts launcher/config-domain-parsers.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/commands/command-modules.test.ts",
|
||||
"test:core:dist": "node --test dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command.test.js dist/core/services/ipc.test.js dist/core/services/anki-jimaku-ipc.test.js dist/core/services/field-grouping-overlay.test.js dist/core/services/numeric-shortcut-session.test.js dist/core/services/secondary-subtitle.test.js dist/core/services/mpv-render-metrics.test.js dist/core/services/overlay-content-measurement.test.js dist/core/services/mpv-control.test.js dist/core/services/mpv.test.js dist/core/services/runtime-options-ipc.test.js dist/core/services/runtime-config.test.js dist/core/services/config-hot-reload.test.js dist/core/services/discord-presence.test.js dist/core/services/tokenizer.test.js dist/core/services/tokenizer/annotation-stage.test.js dist/core/services/tokenizer/parser-selection-stage.test.js dist/core/services/tokenizer/parser-enrichment-stage.test.js dist/core/services/subsync.test.js dist/core/services/overlay-bridge.test.js dist/core/services/overlay-manager.test.js dist/core/services/overlay-shortcut-handler.test.js dist/core/services/mining.test.js dist/core/services/anki-jimaku.test.js dist/core/services/jellyfin.test.js dist/core/services/jellyfin-remote.test.js dist/core/services/immersion-tracker-service.test.js dist/core/services/app-ready.test.js dist/core/services/startup-bootstrap.test.js dist/core/services/subtitle-processing-controller.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/anilist/anilist-update-queue.test.js dist/renderer/error-recovery.test.js dist/subsync/utils.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/x11-tracker.test.js",
|
||||
"test:core:smoke:dist": "node --test dist/cli/help.test.js dist/core/services/runtime-config.test.js dist/core/services/ipc.test.js dist/core/services/overlay-manager.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/startup-bootstrap.test.js dist/renderer/error-recovery.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/x11-tracker.test.js",
|
||||
"test:core:dist": "bun test dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command.test.js dist/core/services/ipc.test.js dist/core/services/anki-jimaku-ipc.test.js dist/core/services/field-grouping-overlay.test.js dist/core/services/numeric-shortcut-session.test.js dist/core/services/secondary-subtitle.test.js dist/core/services/mpv-render-metrics.test.js dist/core/services/overlay-content-measurement.test.js dist/core/services/mpv-control.test.js dist/core/services/mpv.test.js dist/core/services/runtime-options-ipc.test.js dist/core/services/runtime-config.test.js dist/core/services/config-hot-reload.test.js dist/core/services/discord-presence.test.js dist/core/services/tokenizer.test.js dist/core/services/tokenizer/annotation-stage.test.js dist/core/services/tokenizer/parser-selection-stage.test.js dist/core/services/tokenizer/parser-enrichment-stage.test.js dist/core/services/subsync.test.js dist/core/services/overlay-bridge.test.js dist/core/services/overlay-manager.test.js dist/core/services/overlay-shortcut-handler.test.js dist/core/services/mining.test.js dist/core/services/anki-jimaku.test.js dist/core/services/jellyfin.test.js dist/core/services/jellyfin-remote.test.js dist/core/services/immersion-tracker-service.test.js dist/core/services/app-ready.test.js dist/core/services/startup-bootstrap.test.js dist/core/services/subtitle-processing-controller.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/anilist/anilist-update-queue.test.js dist/renderer/error-recovery.test.js dist/subsync/utils.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/x11-tracker.test.js",
|
||||
"test:core:smoke:dist": "bun test dist/cli/help.test.js dist/core/services/runtime-config.test.js dist/core/services/ipc.test.js dist/core/services/overlay-manager.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/startup-bootstrap.test.js dist/renderer/error-recovery.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/x11-tracker.test.js",
|
||||
"test:smoke:dist": "bun run test:config:smoke:dist && bun run test:core:smoke:dist",
|
||||
"test:subtitle:dist": "echo \"Subtitle tests are currently not configured\"",
|
||||
"test": "bun run test:config && bun run test:core",
|
||||
@@ -32,7 +32,7 @@
|
||||
"test:core": "bun run test:core:src",
|
||||
"test:subtitle": "bun run build && bun run test:subtitle:dist",
|
||||
"test:fast": "bun run test:config:src && bun run test:core:src",
|
||||
"generate:config-example": "bun run build && node dist/generate-config-example.js",
|
||||
"generate:config-example": "bun run build && bun dist/generate-config-example.js",
|
||||
"start": "bun run build && electron . --start",
|
||||
"dev": "bun run build && electron . --start --dev",
|
||||
"stop": "electron . --stop",
|
||||
|
||||
@@ -3,9 +3,8 @@ import assert from 'node:assert/strict';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { safeStorage } from 'electron';
|
||||
|
||||
import { createAnilistTokenStore } from './anilist-token-store';
|
||||
import { createAnilistTokenStore, type SafeStorageLike } from './anilist-token-store';
|
||||
|
||||
function createTempTokenFile(): string {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-anilist-token-'));
|
||||
@@ -20,151 +19,67 @@ function createLogger() {
|
||||
};
|
||||
}
|
||||
|
||||
type SafeStorageLike = {
|
||||
isEncryptionAvailable: () => boolean;
|
||||
encryptString: (value: string) => Buffer;
|
||||
decryptString: (value: Buffer) => string;
|
||||
};
|
||||
|
||||
const safeStorageApi = safeStorage as unknown as Partial<SafeStorageLike>;
|
||||
const hasSafeStorage =
|
||||
typeof safeStorageApi?.isEncryptionAvailable === 'function' &&
|
||||
typeof safeStorageApi?.encryptString === 'function' &&
|
||||
typeof safeStorageApi?.decryptString === 'function';
|
||||
|
||||
const originalSafeStorage: SafeStorageLike | null = hasSafeStorage
|
||||
? {
|
||||
isEncryptionAvailable: safeStorageApi.isEncryptionAvailable as () => boolean,
|
||||
encryptString: safeStorageApi.encryptString as (value: string) => Buffer,
|
||||
decryptString: safeStorageApi.decryptString as (value: Buffer) => string,
|
||||
}
|
||||
: null;
|
||||
|
||||
function mockSafeStorage(encryptionAvailable: boolean): void {
|
||||
if (!hasSafeStorage) return;
|
||||
(
|
||||
safeStorage as unknown as {
|
||||
isEncryptionAvailable: typeof safeStorage.isEncryptionAvailable;
|
||||
encryptString: typeof safeStorage.encryptString;
|
||||
decryptString: typeof safeStorage.decryptString;
|
||||
}
|
||||
).isEncryptionAvailable = () => encryptionAvailable;
|
||||
(
|
||||
safeStorage as unknown as {
|
||||
encryptString: typeof safeStorage.encryptString;
|
||||
decryptString: typeof safeStorage.decryptString;
|
||||
}
|
||||
).encryptString = (value: string) => Buffer.from(`enc:${value}`, 'utf-8');
|
||||
(
|
||||
safeStorage as unknown as {
|
||||
decryptString: typeof safeStorage.decryptString;
|
||||
}
|
||||
).decryptString = (value: Buffer) => {
|
||||
const raw = value.toString('utf-8');
|
||||
return raw.startsWith('enc:') ? raw.slice(4) : raw;
|
||||
function createStorage(encryptionAvailable: boolean): SafeStorageLike {
|
||||
return {
|
||||
isEncryptionAvailable: () => encryptionAvailable,
|
||||
encryptString: (value: string) => Buffer.from(`enc:${value}`, 'utf-8'),
|
||||
decryptString: (value: Buffer) => {
|
||||
const raw = value.toString('utf-8');
|
||||
return raw.startsWith('enc:') ? raw.slice(4) : raw;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function restoreSafeStorage(): void {
|
||||
if (!hasSafeStorage || !originalSafeStorage) return;
|
||||
(
|
||||
safeStorage as unknown as {
|
||||
isEncryptionAvailable: typeof safeStorage.isEncryptionAvailable;
|
||||
encryptString: typeof safeStorage.encryptString;
|
||||
decryptString: typeof safeStorage.decryptString;
|
||||
}
|
||||
).isEncryptionAvailable = originalSafeStorage.isEncryptionAvailable;
|
||||
(
|
||||
safeStorage as unknown as {
|
||||
encryptString: typeof safeStorage.encryptString;
|
||||
decryptString: typeof safeStorage.decryptString;
|
||||
}
|
||||
).encryptString = originalSafeStorage.encryptString;
|
||||
(
|
||||
safeStorage as unknown as {
|
||||
decryptString: typeof safeStorage.decryptString;
|
||||
}
|
||||
).decryptString = originalSafeStorage.decryptString;
|
||||
}
|
||||
test('anilist token store saves and loads encrypted token', () => {
|
||||
const filePath = createTempTokenFile();
|
||||
const store = createAnilistTokenStore(filePath, createLogger(), createStorage(true));
|
||||
store.saveToken(' demo-token ');
|
||||
|
||||
test('anilist token store saves and loads encrypted token', { skip: !hasSafeStorage }, () => {
|
||||
mockSafeStorage(true);
|
||||
try {
|
||||
const filePath = createTempTokenFile();
|
||||
const store = createAnilistTokenStore(filePath, createLogger());
|
||||
store.saveToken(' demo-token ');
|
||||
|
||||
const payload = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as {
|
||||
encryptedToken?: string;
|
||||
plaintextToken?: string;
|
||||
};
|
||||
assert.equal(typeof payload.encryptedToken, 'string');
|
||||
assert.equal(payload.plaintextToken, undefined);
|
||||
assert.equal(store.loadToken(), 'demo-token');
|
||||
} finally {
|
||||
restoreSafeStorage();
|
||||
}
|
||||
const payload = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as {
|
||||
encryptedToken?: string;
|
||||
plaintextToken?: string;
|
||||
};
|
||||
assert.equal(typeof payload.encryptedToken, 'string');
|
||||
assert.equal(payload.plaintextToken, undefined);
|
||||
assert.equal(store.loadToken(), 'demo-token');
|
||||
});
|
||||
|
||||
test(
|
||||
'anilist token store falls back to plaintext when encryption unavailable',
|
||||
{ skip: !hasSafeStorage },
|
||||
() => {
|
||||
mockSafeStorage(false);
|
||||
try {
|
||||
const filePath = createTempTokenFile();
|
||||
const store = createAnilistTokenStore(filePath, createLogger());
|
||||
store.saveToken('plain-token');
|
||||
test('anilist token store falls back to plaintext when encryption unavailable', () => {
|
||||
const filePath = createTempTokenFile();
|
||||
const store = createAnilistTokenStore(filePath, createLogger(), createStorage(false));
|
||||
store.saveToken('plain-token');
|
||||
|
||||
const payload = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as {
|
||||
plaintextToken?: string;
|
||||
};
|
||||
assert.equal(payload.plaintextToken, 'plain-token');
|
||||
assert.equal(store.loadToken(), 'plain-token');
|
||||
} finally {
|
||||
restoreSafeStorage();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'anilist token store migrates legacy plaintext to encrypted',
|
||||
{ skip: !hasSafeStorage },
|
||||
() => {
|
||||
const filePath = createTempTokenFile();
|
||||
fs.writeFileSync(
|
||||
filePath,
|
||||
JSON.stringify({ plaintextToken: 'legacy-token', updatedAt: Date.now() }),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
mockSafeStorage(true);
|
||||
try {
|
||||
const store = createAnilistTokenStore(filePath, createLogger());
|
||||
assert.equal(store.loadToken(), 'legacy-token');
|
||||
|
||||
const payload = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as {
|
||||
encryptedToken?: string;
|
||||
plaintextToken?: string;
|
||||
};
|
||||
assert.equal(typeof payload.encryptedToken, 'string');
|
||||
assert.equal(payload.plaintextToken, undefined);
|
||||
} finally {
|
||||
restoreSafeStorage();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
test('anilist token store clears persisted token file', { skip: !hasSafeStorage }, () => {
|
||||
mockSafeStorage(true);
|
||||
try {
|
||||
const filePath = createTempTokenFile();
|
||||
const store = createAnilistTokenStore(filePath, createLogger());
|
||||
store.saveToken('to-clear');
|
||||
assert.equal(fs.existsSync(filePath), true);
|
||||
store.clearToken();
|
||||
assert.equal(fs.existsSync(filePath), false);
|
||||
} finally {
|
||||
restoreSafeStorage();
|
||||
}
|
||||
const payload = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as {
|
||||
plaintextToken?: string;
|
||||
};
|
||||
assert.equal(payload.plaintextToken, 'plain-token');
|
||||
assert.equal(store.loadToken(), 'plain-token');
|
||||
});
|
||||
|
||||
test('anilist token store migrates legacy plaintext to encrypted', () => {
|
||||
const filePath = createTempTokenFile();
|
||||
fs.writeFileSync(
|
||||
filePath,
|
||||
JSON.stringify({ plaintextToken: 'legacy-token', updatedAt: Date.now() }),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
const store = createAnilistTokenStore(filePath, createLogger(), createStorage(true));
|
||||
assert.equal(store.loadToken(), 'legacy-token');
|
||||
|
||||
const payload = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as {
|
||||
encryptedToken?: string;
|
||||
plaintextToken?: string;
|
||||
};
|
||||
assert.equal(typeof payload.encryptedToken, 'string');
|
||||
assert.equal(payload.plaintextToken, undefined);
|
||||
});
|
||||
|
||||
test('anilist token store clears persisted token file', () => {
|
||||
const filePath = createTempTokenFile();
|
||||
const store = createAnilistTokenStore(filePath, createLogger(), createStorage(true));
|
||||
store.saveToken('to-clear');
|
||||
assert.equal(fs.existsSync(filePath), true);
|
||||
store.clearToken();
|
||||
assert.equal(fs.existsSync(filePath), false);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { safeStorage } from 'electron';
|
||||
import * as electron from 'electron';
|
||||
|
||||
interface PersistedTokenPayload {
|
||||
encryptedToken?: string;
|
||||
@@ -14,6 +14,12 @@ export interface AnilistTokenStore {
|
||||
clearToken: () => void;
|
||||
}
|
||||
|
||||
export interface SafeStorageLike {
|
||||
isEncryptionAvailable: () => boolean;
|
||||
encryptString: (value: string) => Buffer;
|
||||
decryptString: (value: Buffer) => string;
|
||||
}
|
||||
|
||||
function ensureDirectory(filePath: string): void {
|
||||
const dir = path.dirname(filePath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
@@ -33,6 +39,7 @@ export function createAnilistTokenStore(
|
||||
warn: (message: string, details?: unknown) => void;
|
||||
error: (message: string, details?: unknown) => void;
|
||||
},
|
||||
storage: SafeStorageLike = electron.safeStorage,
|
||||
): AnilistTokenStore {
|
||||
return {
|
||||
loadToken(): string | null {
|
||||
@@ -44,11 +51,11 @@ export function createAnilistTokenStore(
|
||||
const parsed = JSON.parse(raw) as PersistedTokenPayload;
|
||||
if (typeof parsed.encryptedToken === 'string' && parsed.encryptedToken.length > 0) {
|
||||
const encrypted = Buffer.from(parsed.encryptedToken, 'base64');
|
||||
if (!safeStorage.isEncryptionAvailable()) {
|
||||
if (!storage.isEncryptionAvailable()) {
|
||||
logger.warn('AniList token encryption is not available on this system.');
|
||||
return null;
|
||||
}
|
||||
const decrypted = safeStorage.decryptString(encrypted).trim();
|
||||
const decrypted = storage.decryptString(encrypted).trim();
|
||||
return decrypted.length > 0 ? decrypted : null;
|
||||
}
|
||||
if (typeof parsed.plaintextToken === 'string' && parsed.plaintextToken.trim().length > 0) {
|
||||
@@ -70,7 +77,7 @@ export function createAnilistTokenStore(
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!safeStorage.isEncryptionAvailable()) {
|
||||
if (!storage.isEncryptionAvailable()) {
|
||||
logger.warn('AniList token encryption unavailable; storing token in plaintext fallback.');
|
||||
writePayload(filePath, {
|
||||
plaintextToken: trimmed,
|
||||
@@ -78,7 +85,7 @@ export function createAnilistTokenStore(
|
||||
});
|
||||
return;
|
||||
}
|
||||
const encrypted = safeStorage.encryptString(trimmed);
|
||||
const encrypted = storage.encryptString(trimmed);
|
||||
writePayload(filePath, {
|
||||
encryptedToken: encrypted.toString('base64'),
|
||||
updatedAt: Date.now(),
|
||||
|
||||
Reference in New Issue
Block a user