Fix/cn daily query - #5
Merged
Merged
Conversation
chore: modify the plugin source
checkError mapped every 401/403 to "session expired, please login again", but leetcode now signals auth failures with 401 (REST) or 200 + empty user fields (GraphQL / problem list). A 403/429 is cloudflare blocking or rate limiting the client with a perfectly valid session, so report it as BLOCKED instead. This also stops the autologin machinery from firing doomed relogins on transient 403s: retry only engages on the EXPIRED object. Co-Authored-By: Claude <noreply@anthropic.com>
Fixes the "Cannot read property 'toString' of undefined" crash when a caller passes a missing value to any log method. Co-Authored-By: Claude <noreply@anthropic.com>
parseCookie took (cookie, body, cb) while cookieLogin called it with (cookie, cb), so on an invalid cookie the error callback landed on the unused `body` param and the caller crashed dereferencing the undefined return value instead of showing "invalid cookie?". Make parseCookie a pure function returning null on failure and have both call sites check. Co-Authored-By: Claude <noreply@anthropic.com>
The `user { isCurrentUserPremium }` query returns http 400 on
leetcode.cn, so user.paid was never set there (locked problems then
fail to load even for premium users, upstream skygragon#228/skygragon#207). Query
`userStatus { username isPremium }` instead, which works on both
sites. On fetch failure keep the (still valid) login but warn instead
of silently saving a user without name, and fall back to the login
label in the success message instead of printing "login as undefined".
Also pins the login tests' user-info call to a nock instead of relying
on a real network request happening to fail.
Co-Authored-By: Claude <noreply@anthropic.com>
Cookie/third-party logins store no password, so an EXPIRED-triggered relogin ran the dead password form flow twice per request and only produced "invalid password?" noise. Warn and skip instead, pointing the user at a manual re-login. Co-Authored-By: Claude <noreply@anthropic.com>
The month header position is fractional whenever the month's day-1 is not a whole number of weeks away, and modern node throws ERR_OUT_OF_RANGE on the non-integer Buffer.write offset instead of silently coercing it. `leetcode stat -c` crashed on any recent node. Co-Authored-By: Claude <noreply@anthropic.com>
Ported from leetcode-tools#60 with the graphql query replaced: the PR's currentDailyCodingChallenge field no longer exists on leetcode.com, activeDailyCodingChallengeQuestion does. Co-Authored-By: Claude <noreply@anthropic.com>
Fetch the `hints` field on question detail and print it under the description (plain terminal lines instead of the upstream PR's raw <details> HTML), and emit a Hint section in the detailed template. Per-problem caches saved before this change lack the field, so extend the existing staleness check to treat missing hints as too old. Co-Authored-By: Claude <noreply@anthropic.com>
Extract the slug from urls like https://leetcode.com/problems/two-sum/ before the usual lookup, so show/test/submit all accept pasted links. Also default hints to an empty array in exportProblem so the detailed template renders for problems (and older caches) without hints. Co-Authored-By: Claude <noreply@anthropic.com>
Removals (all verified unreferenced or trivially replaced): - sqlite3: zero require sites, 15 audit findings, native build cost - lodash: only startCase was used; moved to helper.startCase - wordwrap: abandoned; replaced with a local greedy wrap in core.js (exportProblem's detailed tests pin the exact output) - mkdirp: superseded by fs.mkdirSync(recursive) - pkg: vercel pkg is deprecated and pkg4 cannot target node >= 20; single-binary builds belong to @yao-pkg/pkg if wanted later Bumps: underscore 1.13.8 (fixes a prototype/recursion advisory), cheerio 1.2.0, nconf 0.13.0, mocha 11, nock 14, nyc 18, chai 6, rewire 9. Also fixes a test isolation bug exposed by the mocha bump: test_file.js set process.env.HOME/USERPROFILE without restoring them, poisoning every suite loaded afterwards (config.init then tried to write into /home/skygragon). With that fixed the full suite passes for the first time: 167 passing, 0 failing. Audit: 55 findings (2 critical) -> 18 (1 critical, all in the eslint/request chains slated for a separate upgrade). Co-Authored-By: Claude <noreply@anthropic.com>
prompt (and its winston dependency chain) was the source of the padLevels circular-dependency warning on modern node. helper.readInput covers the four call sites (login, cookie, third-party, github 2FA) with secret muting for hidden fields. Co-Authored-By: Claude <noreply@anthropic.com>
Replace the archived eslint-config-google bridge with a flat config generated from the old stack's effective ruleset (eslint --print-config on eslint 5, active rules only). Most google style rules were already off in practice, so the 59-rule inline set reproduces lint behavior exactly: 0 errors, same warning classes. Co-Authored-By: Claude <noreply@anthropic.com>
yargs snapshots process.argv at require time and exits silently when the leading option is unknown, so `leetcode -v <cmd>` / `-vv <cmd>` produced no output at all (the flags predate this branch's work — a latent yargs 12 -> 17 migration gap). initLogLevel consumes them first; they are now removed from argv before yargs is required. Co-Authored-By: Claude <noreply@anthropic.com>
request has been unmaintained since 2020 and accounts for the last
runtime audit findings (including a critical via form-data). lib/http
reimplements the request call surface the codebase relies on —
cb(e, resp, body), resp.statusCode / resp.request.uri.href, opts.json
/ opts.form, defaults({jar}) for the github/linkedin login flows,
response streaming for plugin downloads and a -vv trace hook — with
redirects followed manually so cookies propagate across hops and
error statuses stay checkError's job. All 167 tests pass unchanged
against the adapter and live commands hit leetcode.com successfully.
Co-Authored-By: Claude <noreply@anthropic.com>
The description was printed as raw HTML (lists, code tags, entities). Decode it for the terminal the same way exportProblem does for generated files, leaving the cached raw HTML untouched. Co-Authored-By: Claude <noreply@anthropic.com>
The REST problem list carries no tags, so `-t stack` and friends silently matched nothing. Fetch the whole tag map from the paged problemsetQuestionListV2 graphql endpoint after the category list and merge it into the problems before caching. A tag-fetch failure keeps the list usable with a warning. problemsMeta gains a version field so existing caches refresh once and pick tags up. Co-Authored-By: Claude <noreply@anthropic.com>
log.fail, failing tests, rejected submissions and failed downloads now set process.exitCode = 1 (instead of always exiting 0), so scripts can detect failures; batch commands still run to completion. Also print a blank line after the test command's stdout block. Co-Authored-By: Claude <noreply@anthropic.com>
`leetcode cache` now cross-references the problems list and shows a check mark on per-problem caches whose problem is solved. The solution.discuss plugin follows leetcode's move from /discuss/ to /solutions/ for community solution pages. Co-Authored-By: Claude <noreply@anthropic.com>
The emitWarning filter in bin/leetcode silenced prompt's winston circular-dependency warning; prompt is gone now. Plugin dependency installation shells out to pnpm, matching this checkout and avoiding a hard npm dependency. Co-Authored-By: Claude <noreply@anthropic.com>
- chmod 0600 on user.json: it holds a live session cookie and was world-readable (existing files fixed too, via saveUser path) - drop the dead "pkg" config block and the unused solved variable in the cache listing; stop linting the vendored company tag list - remove the stale skygragon-era docs/ folder - password login now reports that leetcode.com rejects it and points at cookie login instead of a misleading "invalid password?"; the session-expired message names the actual remedy (leetcode user -c) - mechanical lint cleanup: unused catch bindings/imports, a missing semicolon, .parse() instead of the bare .argv expression; warnings drop from 31 to 17, all remaining ones are deliberate Co-Authored-By: Claude <noreply@anthropic.com>
Node 20/22/24 matrix, pnpm with frozen lockfile, running the same `pnpm test` entry point locally used (lint + mocha across lib, command and plugin suites). Co-Authored-By: Claude <noreply@anthropic.com>
New test/commands suite (list, stat, show, user, cache, submission) exercising handlers with stubbed core + temp-dir caches: filter semantics and the -s summary, stat bars and graph legend, plain-text desc/hints rendering, code generation, the solved marker in cache listings, submission download + error exit codes. Assertions strip ANSI so colored output stays testable. `pnpm test` now runs lib, commands and plugin suites. Co-Authored-By: Claude <noreply@anthropic.com>
A push to main builds @yao-pkg/pkg binaries (linux/mac x64+arm64, win x64), attaches sha256 checksums and publishes a GitHub Release tagged from the package version; re-pushing without a version bump is a no-op. pkg config restored with arm64 targets added; the local macos-arm64 build was verified end to end (version/stat/show all work from the snapshot). ci.yml now follows the main-based flow. Co-Authored-By: Claude <noreply@anthropic.com>
lc dev: backlog fixes, dep modernization, CI + release
activeDailyCodingChallengeQuestion only exists in leetcode.com's GraphQL schema; leetcode.cn rejects it with 400, breaking `show -d`. Override getProblemOfToday with the cn-only todayRecord query, mirroring the existing getProblems override. The .com path is untouched.
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical and moderate issues remain in session deletion, cache invalidation, HTTP/authentication, bulk submissions, and package compatibility.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This pull request fixes daily problem lookup for leetcode.cn and modernizes related CLI, API, HTTP, caching, tooling, and test behavior.
Changes:
- Adds the CN-specific
todayRecorddaily query. - Updates translation-aware APIs, authentication, caching, and command flows.
- Refreshes dependencies, templates, documentation, workflows, and tests.
File summaries
| File | Summary |
|---|---|
test/test_plugin.js |
Plugin installation tests |
test/test_log.js |
Logging and exit-code tests |
test/test_helper.js |
Helper and language tests |
test/test_file.js |
File and plugin discovery tests |
test/test_core.js |
Core and daily retrieval tests |
test/plugins/test_retry.js |
Retry behavior tests |
test/plugins/test_leetcode.js |
LeetCode API and authentication tests |
test/plugins/test_cache.js |
Cache behavior tests |
test/mock/find-the-difference.json.20171216 |
API fixture |
test/mock/find-the-difference-unstar.json.20200821 |
Favorite-state fixture |
test/mock/find-the-difference-star.json.20200821 |
Favorite-state fixture |
test/mock/add-two-numbers.20161015.json |
API fixture |
test/commands/test_user.js |
User command tests |
test/commands/test_submission.js |
Submission command tests |
test/commands/test_stat.js |
Statistics command tests |
test/commands/test_show.js |
Show command tests |
test/commands/test_list.js |
List command tests |
test/commands/test_cache.js |
Cache command tests |
templates/detailed.tpl |
Detailed output template |
templates/codeonly.tpl |
Code-only output template |
README.md |
CLI documentation |
package.json |
Dependencies, binaries, and scripts |
lib/session.js |
Session and cache management |
lib/plugins/solution.discuss.js |
Discussion solution integration |
lib/plugins/retry.js |
Retry and relogin behavior |
lib/plugins/leetcode.js |
Shared API and authentication logic |
lib/plugins/leetcode.cn.js |
CN endpoint and daily query |
lib/plugins/cache.js |
Cache freshness and invalidation |
lib/plugin.js |
Plugin installation |
lib/log.js |
Logging and exit signaling |
lib/http.js |
HTTP compatibility layer |
lib/helper.js |
Input, language, and cache helpers |
lib/file.js |
File and code-marker handling |
lib/core.js |
Translation-aware core APIs |
lib/config.js |
URLs, languages, and initialization |
lib/commands/version.js |
Version command |
lib/commands/user.js |
User command behavior |
lib/commands/test.js |
Test result presentation |
lib/commands/submit.js |
Submission result presentation |
lib/commands/submission.js |
Submission download flow |
lib/commands/stat.js |
Statistics command |
lib/commands/star.js |
Favorite management |
lib/commands/show.js |
Problem detail and daily output |
lib/commands/session.js |
Session management |
lib/commands/list.js |
Problem listing |
lib/commands/config.js |
Configuration command |
lib/commands/cache.js |
Cache command |
lib/cli.js |
CLI initialization |
lib/cache.js |
Bulk cache operations |
eslint.config.js |
ESLint configuration |
docs/releases.md |
Release documentation |
docs/install.md |
Installation documentation |
docs/index.html |
Documentation site page |
docs/demo.html |
Documentation demo page |
docs/commands.md |
Command documentation |
docs/advanced.md |
Advanced documentation |
docs/_config.yml |
Documentation configuration |
1.js |
Sample generated solution |
.vscode/launch.json |
Debug launch configurations |
.travis.yml |
Legacy CI configuration |
.npmignore |
Package exclusions |
.github/workflows/release.yml |
Release automation |
.github/workflows/ci.yml |
CI workflow |
.eslintrc.js |
Legacy ESLint configuration |
Review details
Suppressed comments (9)
lib/commands/show.js:174
- These truthiness checks mishandle legitimate zero counts:
likes: 0is omitted anddislikes: 0is rendered as-. Check for null/undefined so the detail output reflects the API values.
if (problem.likes)
log.printf('* Likes: %s', problem.likes);
if (problem.dislikes)
log.printf('* Dislikes: %s', problem.dislikes);
else
lib/core.js:93
- This signature change leaves the
submission -acaller atlib/commands/submission.js:134usingcore.getProblems(callback)instead of(needTranslation, callback). The callback is then interpreted as the boolean argument and the real callback is missing, so bulk submission downloads fail when the chain invokes it; update that caller to pass!argv.dontTranslatefirst.
lib/plugin.js:69 - The CLI's documented installers still use npm (
bin/installand the Dockerfile), and no dependency or engine requirement ensures that pnpm is available to end users. A normal npm-installed CLI will therefore fail whenever it tries to install a missing plugin withpnpm add; use a package-manager-independent path or explicitly bootstrap/check pnpm.
lib/plugins/leetcode.cn.js:138 - This new CN daily path uses the local
checkError, which still maps both 401 and 403 toEXPIRED. A 403 from Cloudflare or rate limiting is not a session-expiry signal and should be reported asBLOCKED, consistent with the base plugin; otherwise the daily command reports the wrong failure and can trigger misleading re-login behavior.
lib/plugins/leetcode.cn.js:138 getProblemOfTodayis not one of the methods wrapped by the retry plugin, so an expired CN session bypasses the automatic re-login path used by the other problem APIs and fails immediately. Add the new method to the retry wrapper list and cover its retry behavior.
lib/plugins/leetcode.js:574- The CN branch changes the spinner based on
isCN, but this new failure message still tells CN users that password login was rejected byleetcode.com. Use the selected endpoint in the message so the remediation is accurate.
lib/plugins/leetcode.js:643 - The response shim in
lib/http.jsexposesresp.request.uri.hrefbut does not exposeresp.request.headers.cookie. Consequently a successful third-party redirect reachesparseCookie(undefined)and reportsinvalid cookie?; preserve the jar's cookies or read them from a field the adapter actually provides.
package.json:13 - The
bin/pkgandbin/pkg.shhelpers still invokenpm run pkg, but this scripts block no longer defines apkgscript. Running the repository's packaging helper therefore fails with npm's missing-script error; restore a script matching the helper's target/output arguments or update both helpers to invoke the new binary directly.
test/plugins/test_retry.js:109 - This new retry test still mocks and invokes
getProblemswith the old callback-only signature. The active plugin API is(needTranslation, cb), so the test does not verify forwarding of the translation flag and can pass while real callers use the wrong argument positions; update this test and the existing mocks to the new signature.
- Files reviewed: 65/70 changed files
- Comments generated: 12
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| return core.deleteSession(session, printSessions); | ||
| }); | ||
| return; | ||
| return core.deleteSession(session, printSessions); |
| if (!translationConfig || translationConfig['useEndpointTranslation'] != needTranslation) { | ||
| // cache doesn't have the key => old cache version, need to update | ||
| // or cache does have the key but it contains a different value | ||
| cache.deleteAll(); |
| .values() | ||
| .value(); | ||
|
|
||
| if (/[runcode|interpret].*/.test(result.submission_id)) { |
Comment on lines
+655
to
+657
| user.sessionId = cookieData.sessionId; | ||
| user.sessionCSRF = cookieData.sessionCSRF; | ||
| session.saveUser(user); |
| "ansi-styles": "3.2.1", | ||
| "cheerio": "0.20.0", | ||
| "ansi-styles": "^3.2.1", | ||
| "axios": "^1.7.0", |
| var jar = conf.jar ? {} : null; | ||
| var baseHeaders = conf.headers || {}; | ||
| var bound = function(opts, cb) { | ||
| opts = Object.assign({}, opts); |
Comment on lines
+638
to
+639
| request.get({url: leetcodeUrl}, function(e, resp, body) { | ||
| const redirectUri = resp.request.uri.href; |
| var lang = session.argv.lang; | ||
| getSolution(problem, lang, function(e, solution) { | ||
| if (e) return cb(e); | ||
| if (!solution) return log.error('Solution not found for ' + lang); |
Comment on lines
8
to
10
| "bin": { | ||
| "leetcode": "./bin/leetcode" | ||
| "lc": "./bin/leetcode" | ||
| }, |
|
|
||
| // the .com daily query (activeDailyCodingChallengeQuestion) doesn't exist in | ||
| // leetcode.cn's GraphQL schema, which made `show -d` fail with http error 400. | ||
| plugin.getProblemOfToday = function(needTranslation, cb) { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
lc show -dfailed with[ERROR] http error [code=400]against leetcode.cn since 2026-09-13. All other commands (show <id>,list,test,submit) kept working.Root cause
The daily query in
lib/plugins/leetcode.jsusesactiveDailyCodingChallengeQuestion, which exists only in leetcode.com's GraphQL schema. leetcode.cn rejects it:{"errors":[{"message":"Cannot query field \"activeDailyCodingChallengeQuestion\" on type \"Query\"."}]}The cn-only field is
todayRecord. Both endpoints were tested live: each returns 200 only for its own field, so the query must branch per endpoint.Fix
Add a
getProblemOfTodayoverride inlib/plugins/leetcode.cn.jsusingtodayRecord { question { titleSlug } }, mirroring the plugin's existinggetProblemsoverride pattern. The returned slug flows through the unchanged core detail-fetch path. The leetcode.com implementation is untouched.Verification
lc show -d(global install, linked to this tree, leetcode.cn plugin enabled) prints[836] 矩形重叠with the full translated description.eslintclean.plugin:cachelogin tests fail locally due to this machine's stale~/.lcstate (clean CI runners should be unaffected);plugin #Plugin.copyis network-flaky.