← Back to skills
extension
Category: Development & EngineeringAPI key requirement unconfirmed

查询codex重置券过期时间

Query ChatGPT rate-limit reset credits using the local Codex auth token, with the complete query implementation embedded in this single skill file. Use when a user asks to check reset-credit availability or the chatgpt.com/backend-api/wham/rate-limit-reset-credits endpoint with ~/.codex/auth.json credentials.

personAuthor: jiangbian51hubModelScope

Rate Limit Reset Credits (Standalone)

Query https://chatgpt.com/backend-api/wham/rate-limit-reset-credits and return a sanitized summary. This skill contains the complete implementation; no bundled scripts, references, assets, metadata files, or other skills are required.

Runtime Requirements

  • uv and a uv-managed Python 3.10 or newer. The implementation uses only Python's standard library and OpenSSL-backed HTTPS.
  • The current user's Codex credential at ~/.codex/auth.json, with tokens.access_token populated.
  • Permission to read that credential and send an HTTPS GET request to the endpoint.

Workflow

  1. Inspect the current session permissions. If outbound network access or credential access is explicitly restricted, request the tool's supported escalated execution for the first query attempt. Otherwise, attempt the query normally. On a credible sandbox or network denial, retry once with escalation; do not repeat TLS failures in the same restricted sandbox. Respect a rejected escalation. HTTP 401/403 alone is not evidence of a local sandbox failure.
  2. Copy the complete Python block below verbatim into a temporary .py file, using UTF-8 without BOM. Run it with uv run python <temporary-file-path>, passing the path as one argument. This temporary file is generated from this document at execution time; do not look for installed companion files. Delete only the temporary file created for this query when finished. If a Python file already exists at the chosen path, choose a fresh temporary path.
  3. Keep certificate verification enabled. The implementation uses Python's normal proxy behavior; if diagnosing connectivity, account for configured proxies without printing credentials or secret-bearing proxy URLs.
  4. Return only the program output. Add no commentary around a successful JSON result. Exit code 0 means success; a nonzero code means a sanitized failure. If execution itself is blocked, report that blocker without inventing query results.

Embedded Query Implementation

from __future__ import annotations

import json
import socket
import ssl
import sys
import urllib.error
import urllib.request
from datetime import datetime, timedelta, timezone
from http import HTTPStatus
from pathlib import Path
from typing import Any


ENDPOINT = "https://chatgpt.com/backend-api/wham/rate-limit-reset-credits"
AUTH_PATH = Path.home() / ".codex" / "auth.json"
SHANGHAI = timezone(timedelta(hours=8), "Asia/Shanghai")
TIMEOUT_SECONDS = 20

MSG_UNEXPECTED = "\u54cd\u5e94\u7ed3\u6784\u4e0d\u7b26\u5408\u9884\u671f"
MSG_UNAUTHORIZED = (
    "\u51ed\u8bc1\u5931\u6548\uff0c\u6216\u8bf7\u6c42\u6ca1\u6709\u5e26\u5bf9"
    " Authorization header\u3002"
)
MSG_NO_STATUS = "\u65e0"
MSG_REQUEST_FAILED = "\u8bf7\u6c42\u5931\u8d25"
MSG_MISSING_ACCESS_TOKEN = "\u672a\u627e\u5230 access_token"
MSG_LOCAL_PREP_FAILED = (
    "\u672c\u5730\u51ed\u8bc1\u8bfb\u53d6\u6216\u8bf7\u6c42\u51c6\u5907\u5931\u8d25"
)
MSG_STATUS_CODE = "\u72b6\u6001\u7801"
MSG_ERROR = "\u9519\u8bef"
MSG_TLS_FAILURE = "HTTPS/TLS \u8fde\u63a5\u5931\u8d25\uff08\u53ef\u80fd\u88ab\u6c99\u7bb1\u6216\u4ee3\u7406\u963b\u6b62\uff09"
MSG_DNS_FAILURE = "\u57df\u540d\u89e3\u6790\u5931\u8d25"
MSG_TIMEOUT = "\u8bf7\u6c42\u8d85\u65f6"
MSG_CONNECTION_FAILURE = "\u7f51\u7edc\u8fde\u63a5\u5931\u8d25"


class UnexpectedResponseError(Exception):
    pass


def write_failure(status_code: int | str | None, reason: str) -> None:
    status = MSG_NO_STATUS if status_code is None else str(status_code)
    safe_reason = reason or MSG_REQUEST_FAILED
    print(f"{MSG_STATUS_CODE}: {status}; {MSG_ERROR}: {safe_reason}")


def exception_chain(exception: BaseException) -> list[BaseException]:
    chain: list[BaseException] = []
    pending: list[BaseException] = [exception]
    seen: set[int] = set()

    while pending and len(chain) < 8:
        current = pending.pop(0)
        if id(current) in seen:
            continue
        seen.add(id(current))
        chain.append(current)

        for linked in (current.__cause__, current.__context__):
            if isinstance(linked, BaseException):
                pending.append(linked)
        if isinstance(current, urllib.error.URLError) and isinstance(
            current.reason, BaseException
        ):
            pending.append(current.reason)

    return chain


def safe_request_reason(exception: BaseException) -> str:
    chain = exception_chain(exception)
    details = " | ".join(str(item) for item in chain).lower()

    if any(isinstance(item, (ssl.SSLError, ssl.CertificateError)) for item in chain):
        return MSG_TLS_FAILURE
    if any(isinstance(item, socket.gaierror) for item in chain):
        return MSG_DNS_FAILURE
    if any(isinstance(item, (TimeoutError, socket.timeout)) for item in chain):
        return MSG_TIMEOUT
    if any(isinstance(item, ConnectionError) for item in chain):
        return MSG_CONNECTION_FAILURE

    if any(term in details for term in ("ssl", "tls", "certificate")):
        return MSG_TLS_FAILURE
    if any(term in details for term in ("dns", "no such host", "name resolution")):
        return MSG_DNS_FAILURE
    if any(term in details for term in ("timed out", "timeout")):
        return MSG_TIMEOUT
    if any(term in details for term in ("connection", "network", "proxy", "socket")):
        return MSG_CONNECTION_FAILURE
    return MSG_REQUEST_FAILED


def read_access_token() -> str:
    with AUTH_PATH.open("r", encoding="utf-8") as stream:
        auth = json.load(stream)

    tokens = auth.get("tokens") if isinstance(auth, dict) else None
    token = tokens.get("access_token") if isinstance(tokens, dict) else None
    return token if isinstance(token, str) else ""


def request_payload(access_token: str) -> Any:
    request = urllib.request.Request(
        ENDPOINT,
        headers={"Authorization": f"Bearer {access_token}"},
        method="GET",
    )
    context = ssl.create_default_context()
    with urllib.request.urlopen(
        request,
        timeout=TIMEOUT_SECONDS,
        context=context,
    ) as response:
        return json.load(response)


def find_value(payload: Any, names: tuple[str, ...]) -> Any:
    if not isinstance(payload, dict):
        raise UnexpectedResponseError

    sources = [payload]
    nested = payload.get("data")
    if isinstance(nested, dict):
        sources.append(nested)

    for source in sources:
        for name in names:
            if name in source:
                return source[name]
    raise UnexpectedResponseError


def convert_to_shanghai(value: Any) -> str:
    if not isinstance(value, str) or not value.strip():
        raise UnexpectedResponseError

    normalized = value.strip()
    if normalized.endswith(("Z", "z")):
        normalized = normalized[:-1] + "+00:00"

    try:
        parsed = datetime.fromisoformat(normalized)
    except ValueError as error:
        raise UnexpectedResponseError from error

    if parsed.tzinfo is None:
        parsed = parsed.replace(tzinfo=timezone.utc)
    return parsed.astimezone(SHANGHAI).strftime("%Y-%m-%d %H:%M:%S")


def summarize(payload: Any) -> dict[str, Any]:
    available_count = find_value(payload, ("available_count",))
    credits = find_value(payload, ("credits", "rate_limit_reset_credits"))

    if isinstance(available_count, bool) or not isinstance(available_count, int):
        raise UnexpectedResponseError
    if not isinstance(credits, list):
        raise UnexpectedResponseError

    safe_credits = []
    for credit in credits:
        if not isinstance(credit, dict):
            raise UnexpectedResponseError

        status = credit.get("status")
        title = credit.get("title")
        if not isinstance(status, str) or not isinstance(title, str):
            raise UnexpectedResponseError

        safe_credits.append(
            {
                "status": status,
                "title": title,
                "granted_at": convert_to_shanghai(credit.get("granted_at")),
                "expires_at": convert_to_shanghai(credit.get("expires_at")),
            }
        )

    return {"available_count": available_count, "credits": safe_credits}


def http_reason(status_code: int) -> str:
    try:
        return HTTPStatus(status_code).phrase
    except ValueError:
        return MSG_REQUEST_FAILED


def main() -> int:
    try:
        access_token = read_access_token()
    except (OSError, UnicodeError, json.JSONDecodeError):
        write_failure(None, MSG_LOCAL_PREP_FAILED)
        return 1

    if not access_token.strip():
        write_failure(None, MSG_MISSING_ACCESS_TOKEN)
        return 1

    try:
        payload = request_payload(access_token)
    except urllib.error.HTTPError as error:
        if error.code == HTTPStatus.UNAUTHORIZED:
            print(MSG_UNAUTHORIZED)
        else:
            write_failure(error.code, http_reason(error.code))
        return 1
    except (urllib.error.URLError, TimeoutError, OSError) as error:
        write_failure(None, safe_request_reason(error))
        return 1
    except (json.JSONDecodeError, UnicodeError):
        print(MSG_UNEXPECTED)
        return 1
    except Exception:
        write_failure(None, MSG_REQUEST_FAILED)
        return 1

    try:
        result = summarize(payload)
    except (UnexpectedResponseError, TypeError, ValueError, OverflowError):
        print(MSG_UNEXPECTED)
        return 1

    print(json.dumps(result, ensure_ascii=False, indent=2))
    return 0


if __name__ == "__main__":
    sys.exit(main())

Output Contract

On success, output only this JSON shape:

{
  "available_count": 0,
  "credits": [
    {
      "status": "available",
      "title": "Full reset (Weekly + 5 hr)",
      "granted_at": "YYYY-MM-DD HH:mm:ss",
      "expires_at": "YYYY-MM-DD HH:mm:ss"
    }
  ]
}

Preserve the server's count, credit order, status, and title. Accept available_count and credits (or rate_limit_reset_credits) at the top level or under data, as implemented above. Convert the two timestamps to Asia/Shanghai (UTC+08:00); treat timestamps without a time zone as UTC. An empty credits array is valid. Reject malformed fields instead of guessing or returning the raw response.

Use the embedded implementation's fixed failure messages:

  • Invalid JSON or unexpected response fields: 响应结构不符合预期
  • HTTP 401: 凭证失效,或请求没有带对 Authorization header。
  • Other request failures: 状态码: <HTTP status or 无>; 错误: <sanitized reason>
  • Local credential read/preparation failure: 状态码: 无; 错误: 本地凭证读取或请求准备失败
  • Missing access token: 状态码: 无; 错误: 未找到 access_token

TLS, DNS, timeout, and connection failures map to the fixed sanitized reasons in the code. Never substitute raw exception details or response bodies.

Credential and Response Handling

Read the credential only at execution time. Never print, log, quote, summarize, or echo token values, refresh tokens, cookies, Authorization header contents, raw response bodies, or complete unique IDs from the response. Do not persist credentials in temporary files, source code, command arguments, or URLs. The temporary Python file contains implementation only.

<!-- OB-S:zq_bxTTjMP34UM3j3cisbA -->