Skip to content

Fix/cn daily query - #5

Merged
dp9u0 merged 104 commits into
masterfrom
fix/cn-daily-query
Sep 14, 2026
Merged

dp9u0 merged 104 commits into
masterfrom
fix/cn-daily-query

Conversation

@dp9u0

@dp9u0 dp9u0 commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Problem

lc show -d failed 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.js uses activeDailyCodingChallengeQuestion, 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 getProblemOfToday override in lib/plugins/leetcode.cn.js using todayRecord { question { titleSlug } }, mirroring the plugin's existing getProblems override 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.
  • eslint clean.
  • Note: 3 plugin:cache login tests fail locally due to this machine's stale ~/.lc state (clean CI runners should be unaffected); plugin #Plugin.copy is network-flaky.

dp9u0 and others added 25 commits August 30, 2026 10:45
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.
Copilot AI lite review requested due to automatic review settings September 14, 2026 02:37
@dp9u0
dp9u0 merged commit da55e91 into master Sep 14, 2026
1 of 4 checks passed
@dp9u0
dp9u0 deleted the fix/cn-daily-query branch September 14, 2026 02:40

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 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 todayRecord daily 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: 0 is omitted and dislikes: 0 is 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 -a caller at lib/commands/submission.js:134 using core.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.dontTranslate first.
    lib/plugin.js:69
  • The CLI's documented installers still use npm (bin/install and 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 with pnpm 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 to EXPIRED. A 403 from Cloudflare or rate limiting is not a session-expiry signal and should be reported as BLOCKED, 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
  • getProblemOfToday is 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 by leetcode.com. Use the selected endpoint in the message so the remediation is accurate.
    lib/plugins/leetcode.js:643
  • The response shim in lib/http.js exposes resp.request.uri.href but does not expose resp.request.headers.cookie. Consequently a successful third-party redirect reaches parseCookie(undefined) and reports invalid cookie?; preserve the jar's cookies or read them from a field the adapter actually provides.
    package.json:13
  • The bin/pkg and bin/pkg.sh helpers still invoke npm run pkg, but this scripts block no longer defines a pkg script. 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 getProblems with 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.

Comment thread lib/commands/session.js
return core.deleteSession(session, printSessions);
});
return;
return core.deleteSession(session, printSessions);
Comment thread lib/plugins/cache.js
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();
Comment thread lib/plugins/leetcode.js
.values()
.value();

if (/[runcode|interpret].*/.test(result.submission_id)) {
Comment thread lib/plugins/leetcode.js
Comment on lines +655 to +657
user.sessionId = cookieData.sessionId;
user.sessionCSRF = cookieData.sessionCSRF;
session.saveUser(user);
Comment thread package.json
"ansi-styles": "3.2.1",
"cheerio": "0.20.0",
"ansi-styles": "^3.2.1",
"axios": "^1.7.0",
Comment thread lib/http.js
var jar = conf.jar ? {} : null;
var baseHeaders = conf.headers || {};
var bound = function(opts, cb) {
opts = Object.assign({}, opts);
Comment thread lib/plugins/leetcode.js
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 thread package.json
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) {
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.