claude360 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,7 +39,7 @@
39
39
 
40
40
  ## 系统要求
41
41
 
42
- - Node.js **18+** 与 npmbootstrap 脚本仅检测,不自动安装系统运行时)。
42
+ - Node.js **18+** 与 npm(没有时 bootstrap 脚本会经用户确认后通过系统包管理器 winget / brew / apt / dnf 安装 Node.js LTS;不修改 shell profile,不持久化环境变量)。
43
43
  - 网络可达 `https://claude360.xyz`。
44
44
  - macOS / Linux:当前用户对其 `~/.claude360/` 拥有写权限。
45
45
  - Windows:当前用户对 `%USERPROFILE%\.claude360\` 拥有写权限。
@@ -98,7 +98,7 @@ claude360
98
98
 
99
99
  关键约束:
100
100
 
101
- - 首次运行(无 `cliToken`)会先做环境检查(Node≥18 / npm / 全局 npm 权限),缺失或版本过低则给出修复指引并中止;不自动安装系统运行时。
101
+ - 首次运行(无 `cliToken`)会先做环境检查(Node≥18 / npm / 全局 npm 权限),缺失或版本过低则给出修复指引并中止;CLI 自身不安装系统运行时(连 Node/npm 都没有的用户请使用上文 bootstrap 一键脚本)。
102
102
  - 环境检查通过后进入工具安装引导(Claude Code / Codex / 两者 / 跳过),已安装的工具会标注,安装失败不阻断后续授权。
103
103
  - 授权服务不可用(接口返回 404 或网络不可达)时给出友好提示并退出,已安装工具不受影响,可稍后运行 `claude360 setup` 重新完成授权与 Key 配置。
104
104
  - 授权码短期有效;CLI 仅在浏览器无法自动打开时把 `verification_url` 与 `user_code` 输出到终端。
@@ -120,7 +120,7 @@ Claude360
120
120
  ```
121
121
 
122
122
  - **查看余额**:调用 `GET /api/cli/me`,展示账号、余额、已用、当前 Key 名称与分组。
123
- - **微信扫码充值**:先取 `/api/cli/topup/options`,按后端校验通过的金额或最低额提交,再用 `qrcode-terminal` 在终端渲染 `code_url`,渲染失败时降级为纯文本 URL。轮询 `/api/cli/topup/:order_id`,支付完成自动刷新余额。
123
+ - **微信扫码充值**:先取 `/api/cli/topup/options`,按后端校验通过的金额或最低额提交,再用 `qrcode-terminal` 在终端渲染 `code_url`,渲染失败时降级为纯文本 URL。轮询 `/api/cli/topup/order?order_id=`,支付完成自动刷新余额。
124
124
  - **启动 Claude Code**:调用 `claude` 子进程,并通过 `ANTHROPIC_BASE_URL` + `ANTHROPIC_AUTH_TOKEN` 注入。
125
125
  - **启动 Codex**:写入 `~/.codex/config.toml` 中 `[model_providers.claude360]` 与 `[profiles.claude360]`,发现既有冲突字段会先要求用户确认再覆盖,然后 `codex --profile claude360`,并通过 `CLAUDE360_API_KEY` 注入。
126
126
  - **安装或更新工具**:三选一菜单(仅 Claude Code / 仅 Codex / 两者),每步都需要确认。
@@ -251,7 +251,7 @@ codex --profile claude360
251
251
  # 进入 CLI 包
252
252
  cd claude360-cli
253
253
 
254
- # 安装依赖(仅 qrcode-terminal)
254
+ # 安装依赖(qrcode-terminal、smol-toml
255
255
  npm install
256
256
 
257
257
  # 跑全部测试
package/bin/claude360.js CHANGED
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { runCli } from "../src/index.js";
4
+ import { safeErrorMessage } from "../src/sanitize.js";
4
5
 
5
- const forceSetup = process.argv[2] === "setup";
6
+ const command = process.argv[2] || "";
6
7
 
7
8
  try {
8
- await runCli({ forceSetup });
9
+ await runCli({ command, forceSetup: command === "setup" });
9
10
  } catch (error) {
10
- console.error(error?.message || String(error));
11
+ console.error(safeErrorMessage(error));
11
12
  process.exitCode = 1;
12
13
  }
@@ -1,7 +1,20 @@
1
+ # Claude360 CLI 一键安装引导脚本(Windows PowerShell)
2
+ #
1
3
  # Website one-line command:
2
4
  # irm https://claude360.xyz/install.ps1 | iex
5
+ #
6
+ # 行为:
7
+ # 1. 检测 Node.js >= 18 与 npm;缺失或版本过低时,经用户确认后
8
+ # 使用 winget 安装 Node.js LTS(OpenJS.NodeJS.LTS)
9
+ # 2. Node 就绪后,经用户确认执行 npm install -g claude360@latest 并启动 claude360 setup
10
+ # 3. 全程不修改 PowerShell profile、不持久化任何环境变量
11
+ #
12
+ # 注意:本文件与 newapi/controller/install_scripts/install.ps1 保持同步,
13
+ # 后端以 embed 版本为准(由 GET /install.ps1 下发)。
3
14
 
4
15
  $ErrorActionPreference = "Stop"
16
+ $NodeMinMajor = 18
17
+ $NodeJsUrl = "https://nodejs.org"
5
18
 
6
19
  function Confirm-Action {
7
20
  param([string]$Message)
@@ -22,35 +35,68 @@ function Invoke-LoggedCommand {
22
35
  }
23
36
  }
24
37
 
38
+ function Test-NodeOk {
39
+ $node = Get-Command "node" -ErrorAction SilentlyContinue
40
+ $npm = Get-Command "npm" -ErrorAction SilentlyContinue
41
+ if (-not $node -or -not $npm) { return $false }
42
+ try {
43
+ $version = (& "node" @("--version")).TrimStart("v")
44
+ $major = [int]($version.Split(".")[0])
45
+ return $major -ge $NodeMinMajor
46
+ } catch {
47
+ return $false
48
+ }
49
+ }
50
+
51
+ function Show-ManualHint {
52
+ Write-Host "请手动安装 Node.js $NodeMinMajor+(LTS 版本)后重新运行本脚本:"
53
+ Write-Host " $NodeJsUrl"
54
+ }
55
+
56
+ function Update-SessionPath {
57
+ # 仅刷新当前会话的 PATH(读取系统注册值,不写入、不持久化任何环境变量)
58
+ $machinePath = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
59
+ $userPath = [System.Environment]::GetEnvironmentVariable("Path", "User")
60
+ $env:Path = "$machinePath;$userPath"
61
+ }
62
+
25
63
  $os = [System.Runtime.InteropServices.RuntimeInformation]::OSDescription
26
64
  $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
27
65
  Write-Host "Claude360 bootstrap"
28
66
  Write-Host "OS: $os"
29
67
  Write-Host "Arch: $arch"
30
68
 
31
- $node = Get-Command "node" -ErrorAction SilentlyContinue
32
- $npm = Get-Command "npm" -ErrorAction SilentlyContinue
33
- if (-not $node -or -not $npm) {
34
- Write-Host "未检测到 Node.js/npm。Claude360 CLI 需要 Node.js 18+ 和 npm。"
35
- Write-Host "请先通过 winget、Node.js 官网安装 Node.js LTS 后重新运行本脚本。"
36
- if (-not (Confirm-Action "本脚本不会自动安装系统运行时。确认已了解 Node.js/npm 前置要求?")) {
37
- exit 1
69
+ if (-not (Test-NodeOk)) {
70
+ $winget = Get-Command "winget" -ErrorAction SilentlyContinue
71
+ if (-not $winget) {
72
+ Write-Host "未检测到 Node.js $NodeMinMajor+,且当前系统没有 winget。"
73
+ Show-ManualHint
74
+ return
75
+ }
76
+ if (-not (Confirm-Action "未检测到 Node.js $NodeMinMajor+。将使用 winget 安装 Node.js LTS(OpenJS.NodeJS.LTS)。是否继续?")) {
77
+ Show-ManualHint
78
+ return
79
+ }
80
+ & winget install --id OpenJS.NodeJS.LTS --source winget --accept-package-agreements --accept-source-agreements
81
+ if ($LASTEXITCODE -ne 0) {
82
+ Write-Host "winget 安装失败(退出码 $LASTEXITCODE)。"
83
+ Show-ManualHint
84
+ return
85
+ }
86
+ Update-SessionPath
87
+ if (-not (Test-NodeOk)) {
88
+ Write-Host "Node.js 已安装,但当前终端尚未识别到新的 PATH。"
89
+ Write-Host "请关闭并重新打开终端,然后重新运行本脚本。"
90
+ return
38
91
  }
39
- exit 1
40
92
  }
41
93
 
42
94
  & "node" @("--version")
43
95
  & "npm" @("--version")
44
- $nodeVersion = (& "node" @("--version")).TrimStart("v")
45
- $nodeMajor = [int]($nodeVersion.Split(".")[0])
46
- if ($nodeMajor -lt 18) {
47
- Write-Host "当前 Node.js 版本低于 18,请升级到 Node.js LTS 后重新运行本脚本。"
48
- exit 1
49
- }
50
96
 
51
97
  if (-not (Confirm-Action "将执行全局 npm 操作:npm install -g claude360@latest`n影响范围:安装或更新 Claude360 CLI 到当前用户 npm 全局目录,并随后启动 claude360 setup 首次配置向导;配置向导可能打开浏览器授权并写入用户目录配置。是否继续?")) {
52
98
  Write-Host "已取消安装。"
53
- exit 0
99
+ return
54
100
  }
55
101
 
56
102
  Invoke-LoggedCommand "npm" @("install", "-g", "claude360@latest")
@@ -1,13 +1,28 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
+ # Claude360 CLI 一键安装引导脚本(macOS / Linux)
5
+ #
4
6
  # Website one-line command:
5
7
  # curl -fsSL https://claude360.xyz/install.sh | bash
8
+ #
9
+ # 行为:
10
+ # 1. 检测 Node.js >= 18 与 npm;缺失或版本过低时,经用户确认后
11
+ # 使用系统包管理器(brew / apt / dnf)安装 Node.js LTS
12
+ # 2. Node 就绪后,经用户确认执行 npm install -g claude360@latest 并启动 claude360 setup
13
+ # 3. 全程不修改任何 shell 配置文件、不持久化任何环境变量
14
+ #
15
+ # 注意:本文件与 newapi/controller/install_scripts/install.sh 保持同步,
16
+ # 后端以 embed 版本为准(由 GET /install.sh 下发)。
17
+
18
+ NODE_MIN_MAJOR=18
19
+ NODEJS_URL="https://nodejs.org"
6
20
 
7
21
  confirm() {
8
22
  local message="$1"
9
23
  printf "%s\n" "$message"
10
24
  printf "输入 yes 确认继续: "
25
+ # curl | bash 时 stdin 被管道占用,必须从 /dev/tty 读取确认
11
26
  if [ -r /dev/tty ]; then
12
27
  read -r answer </dev/tty
13
28
  else
@@ -24,28 +39,75 @@ run_cmd() {
24
39
  "$@"
25
40
  }
26
41
 
42
+ manual_hint() {
43
+ printf "请手动安装 Node.js %s+(LTS 版本)后重新运行本脚本:\n %s\n" "$NODE_MIN_MAJOR" "$NODEJS_URL"
44
+ exit 1
45
+ }
46
+
47
+ node_ok() {
48
+ command -v node >/dev/null 2>&1 || return 1
49
+ command -v npm >/dev/null 2>&1 || return 1
50
+ local major
51
+ major="$(node --version 2>/dev/null | sed 's/^v//' | cut -d. -f1)"
52
+ [ -n "$major" ] && [ "$major" -ge "$NODE_MIN_MAJOR" ] 2>/dev/null
53
+ }
54
+
55
+ maybe_sudo() {
56
+ if [ "$(id -u)" -eq 0 ]; then
57
+ "$@"
58
+ elif command -v sudo >/dev/null 2>&1; then
59
+ sudo "$@"
60
+ else
61
+ printf "需要 root 权限安装,且未找到 sudo。\n"
62
+ manual_hint
63
+ fi
64
+ }
65
+
66
+ install_node() {
67
+ local os
68
+ os="$(uname -s)"
69
+ if [ "$os" = "Darwin" ]; then
70
+ if command -v brew >/dev/null 2>&1; then
71
+ if ! confirm "未检测到 Node.js ${NODE_MIN_MAJOR}+。将使用 Homebrew 安装 Node.js LTS(brew install node)。是否继续?"; then
72
+ manual_hint
73
+ fi
74
+ run_cmd brew install node || manual_hint
75
+ else
76
+ printf "未检测到 Homebrew。\n"
77
+ manual_hint
78
+ fi
79
+ elif command -v apt-get >/dev/null 2>&1; then
80
+ if ! confirm "未检测到 Node.js ${NODE_MIN_MAJOR}+。将使用 apt 安装 nodejs 与 npm(需要 sudo)。是否继续?"; then
81
+ manual_hint
82
+ fi
83
+ maybe_sudo apt-get update || manual_hint
84
+ maybe_sudo apt-get install -y nodejs npm || manual_hint
85
+ elif command -v dnf >/dev/null 2>&1; then
86
+ if ! confirm "未检测到 Node.js ${NODE_MIN_MAJOR}+。将使用 dnf 安装 nodejs 与 npm(需要 sudo)。是否继续?"; then
87
+ manual_hint
88
+ fi
89
+ maybe_sudo dnf install -y nodejs npm || manual_hint
90
+ else
91
+ printf "未识别到受支持的包管理器(brew / apt / dnf)。\n"
92
+ manual_hint
93
+ fi
94
+ }
95
+
27
96
  OS_NAME="$(uname -s)"
28
97
  ARCH_NAME="$(uname -m)"
29
98
  printf "Claude360 bootstrap\nOS: %s\nArch: %s\n" "$OS_NAME" "$ARCH_NAME"
30
99
 
31
- if ! command -v node >/dev/null 2>&1 || ! command -v npm >/dev/null 2>&1; then
32
- cat <<'EOF'
33
- 未检测到 Node.js/npm。Claude360 CLI 需要 Node.js 18+ 和 npm。
34
- 请先通过系统包管理器或 Node.js 官网安装 Node.js LTS 后重新运行本脚本。
35
- EOF
36
- if ! confirm "本脚本不会自动安装系统运行时。确认已了解 Node.js/npm 前置要求?"; then
37
- exit 1
100
+ if ! node_ok; then
101
+ install_node
102
+ # 系统源安装的 Node 可能仍低于 18(如旧版发行版),装完后必须复查
103
+ if ! node_ok; then
104
+ printf "已安装的 Node.js 版本低于 %s,不满足要求。\n" "$NODE_MIN_MAJOR"
105
+ manual_hint
38
106
  fi
39
- exit 1
40
107
  fi
41
108
 
42
109
  node --version
43
110
  npm --version
44
- NODE_MAJOR="$(node --version | sed 's/^v//' | cut -d. -f1)"
45
- if [ "${NODE_MAJOR}" -lt 18 ]; then
46
- printf "当前 Node.js 版本低于 18,请升级到 Node.js LTS 后重新运行本脚本。\n"
47
- exit 1
48
- fi
49
111
 
50
112
  if ! confirm "将执行全局 npm 操作:npm install -g claude360@latest
51
113
  影响范围:安装或更新 Claude360 CLI 到当前用户 npm 全局目录,并随后启动 claude360 setup 首次配置向导;配置向导可能打开浏览器授权并写入用户目录配置。是否继续?"; then
@@ -12,7 +12,7 @@
12
12
  | Windows PowerShell | Detects OS/architecture, checks `"node"` and `"npm"`, asks confirmation before `npm install -g claude360@latest`, and confirmation text discloses that `claude360 setup` may open browser auth and write user config. |
13
13
  | macOS Terminal | Detects `uname -s`/`uname -m`, checks Node/npm, asks confirmation before global npm install, and confirmation text discloses that `claude360 setup` may open browser auth and write user config. |
14
14
  | Ubuntu bash | Same as macOS Terminal; failure messages should mention Node.js/npm prerequisite when missing. |
15
- | Node missing | Script explains Node.js 18+/npm requirement and exits without running npm install. |
15
+ | Node missing | Script explains Node.js 18+/npm requirement, asks confirmation, then installs Node.js LTS via the system package manager (winget / brew / apt / dnf) and re-checks the version; without a supported package manager or interactive TTY it prints the nodejs.org manual hint and exits without running npm install. |
16
16
  | Node present | Script prints Node/npm versions before asking for global npm install confirmation. |
17
- | Node present but version < 18 | Script exits before npm install and tells the user to upgrade to Node.js LTS. |
17
+ | Node present but version < 18 | Treated the same as Node missing: asks confirmation to install Node.js LTS via the system package manager, re-checks the version afterwards, and exits with the nodejs.org manual hint if it is still below 18. |
18
18
  | npm global permission missing | `npm install -g claude360@latest` failure is visible to the user; user should rerun after fixing npm global directory permissions. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude360",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Interactive Claude360 CLI for browser auth, API key setup, balance checks, top-up, Claude Code and Codex launch.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,8 @@
16
16
  "node": ">=18"
17
17
  },
18
18
  "dependencies": {
19
- "qrcode-terminal": "^0.12.0"
19
+ "qrcode-terminal": "^0.12.0",
20
+ "smol-toml": "^1.6.1"
20
21
  },
21
22
  "scripts": {
22
23
  "test": "node ./scripts/test.js"
@@ -0,0 +1,97 @@
1
+ // 账户状态区:日常菜单顶部显著展示账号 / 余额 / 今日用量 / 当前 Key /
2
+ // Claude Code / Codex 配置状态。业务口径(余额、低余额阈值、今日用量格式)
3
+ // 全部以后端 /api/cli/me 返回为准,CLI 仅做兜底格式化。
4
+
5
+ import { sanitizeError } from "./sanitize.js";
6
+
7
+ export async function loadAccountStatus({ api, config = {} } = {}) {
8
+ let me = null;
9
+ let error = null;
10
+ if (api && config.cliToken) {
11
+ try {
12
+ me = await api.get("/api/cli/me");
13
+ } catch (err) {
14
+ error = err;
15
+ }
16
+ }
17
+ return { me, error, config };
18
+ }
19
+
20
+ // 兜底格式化:今日 token 用量,million 口径,如 128.6k/m、1.25m/m
21
+ export function formatTokensPerMillion(tokens) {
22
+ const value = Number(tokens);
23
+ if (!Number.isFinite(value) || value < 0) {
24
+ return "-";
25
+ }
26
+ if (value >= 1_000_000) {
27
+ return `${(value / 1_000_000).toFixed(2)}m/m`;
28
+ }
29
+ if (value >= 1_000) {
30
+ return `${(value / 1_000).toFixed(1)}k/m`;
31
+ }
32
+ return `${Math.round(value)}/m`;
33
+ }
34
+
35
+ // 账号脱敏:je***@example.com / wa***en
36
+ export function maskAccount(account) {
37
+ if (!account) {
38
+ return "-";
39
+ }
40
+ const text = String(account);
41
+ const atIndex = text.indexOf("@");
42
+ if (atIndex > 0) {
43
+ const name = text.slice(0, atIndex);
44
+ const domain = text.slice(atIndex);
45
+ return `${name.slice(0, 2)}***${domain}`;
46
+ }
47
+ if (text.length <= 4) {
48
+ return `${text.slice(0, 1)}***`;
49
+ }
50
+ return `${text.slice(0, 2)}***${text.slice(-2)}`;
51
+ }
52
+
53
+ export function formatAccountStatus({ me, error, config = {} } = {}) {
54
+ const lines = ["Claude360 账户状态", ""];
55
+ const configured = config.configuredTools || {};
56
+ const claudeCodeState = configured.claudeCode ? "已配置" : "未配置";
57
+ const codexState = configured.codex ? "已配置" : "未配置";
58
+
59
+ if (!config.cliToken) {
60
+ lines.push(
61
+ "账号:未登录",
62
+ "余额:-",
63
+ "今日用量:-",
64
+ "当前 Key:-",
65
+ `Claude Code:${claudeCodeState}`,
66
+ `Codex:${codexState}`,
67
+ );
68
+ return lines.join("\n");
69
+ }
70
+
71
+ if (!me) {
72
+ lines.push(
73
+ `账号:${maskAccount(config.account)}(状态获取失败${sanitizeError(error) ? `:${sanitizeError(error)}` : ""})`,
74
+ "余额:-",
75
+ "今日用量:-",
76
+ `当前 Key:${config.tokenName || "-"}`,
77
+ `Claude Code:${claudeCodeState}`,
78
+ `Codex:${codexState}`,
79
+ );
80
+ return lines.join("\n");
81
+ }
82
+
83
+ const account = me.email || me.display_name || me.username;
84
+ const balance = me.balance_display ?? String(me.quota ?? "-");
85
+ const lowBalanceTag = me.low_balance ? " ! 余额较低" : "";
86
+ const usage = me.today_usage_display || formatTokensPerMillion(me.today_tokens);
87
+
88
+ lines.push(
89
+ `账号:${maskAccount(account)}`,
90
+ `余额:${balance}${lowBalanceTag}`,
91
+ `今日用量:${usage}`,
92
+ `当前 Key:${config.tokenName || "-"}`,
93
+ `Claude Code:${claudeCodeState}`,
94
+ `Codex:${codexState}`,
95
+ );
96
+ return lines.join("\n");
97
+ }
package/src/api-client.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { sanitizeText } from "./sanitize.js";
2
+
1
3
  export class ApiError extends Error {
2
4
  constructor(message, { status, response } = {}) {
3
5
  super(message);
@@ -47,19 +49,20 @@ export class ApiClient {
47
49
  try {
48
50
  envelope = text ? JSON.parse(text) : {};
49
51
  } catch {
50
- throw new ApiError(`响应不是有效 JSON${text ? `: ${text}` : ""}`, {
52
+ // JSON 正文可能回显请求里的凭证(如网关错误页),必须脱敏后再进入错误信息
53
+ throw new ApiError(`响应不是有效 JSON${text ? `: ${sanitizeText(text)}` : ""}`, {
51
54
  status: response.status,
52
55
  });
53
56
  }
54
57
 
55
58
  if (!response.ok) {
56
- throw new ApiError(`HTTP ${response.status}: ${envelope?.message || "请求失败"}`, {
59
+ throw new ApiError(`HTTP ${response.status}: ${sanitizeText(envelope?.message) || "请求失败"}`, {
57
60
  status: response.status,
58
61
  response: envelope,
59
62
  });
60
63
  }
61
64
  if (!envelope?.success) {
62
- throw new ApiError(envelope?.message || "请求失败", {
65
+ throw new ApiError(sanitizeText(envelope?.message) || "请求失败", {
63
66
  status: response.status,
64
67
  response: envelope,
65
68
  });
package/src/auth.js CHANGED
@@ -21,7 +21,6 @@ export async function startAuth({
21
21
 
22
22
  const verificationUrl = withAuthCodes(start.verification_url, {
23
23
  userCode: start.user_code,
24
- deviceCode: start.device_code,
25
24
  });
26
25
  writeLine(`授权地址:${verificationUrl}`);
27
26
  writeLine(`用户码:${start.user_code}`);
@@ -58,6 +57,8 @@ export async function startAuth({
58
57
  throw new Error("用户拒绝授权");
59
58
  case "expired":
60
59
  throw new Error("授权已过期");
60
+ case "consumed":
61
+ throw new Error("该授权请求已被使用,请重新发起授权");
61
62
  default:
62
63
  throw new Error(`未知授权状态: ${poll?.status || "empty"}`);
63
64
  }
@@ -74,9 +75,10 @@ function secondsToMs(value, fallback) {
74
75
  return seconds * 1000;
75
76
  }
76
77
 
77
- function withAuthCodes(url, { userCode, deviceCode }) {
78
+ // 安全要求:device_code 是可换取 cli_token 的轮询凭证,只随 CLI 轮询发送,
79
+ // 绝不拼进浏览器 URL(避免进入浏览器历史 / 服务端访问日志)。
80
+ function withAuthCodes(url, { userCode }) {
78
81
  const parsed = new URL(url);
79
82
  parsed.searchParams.set("user_code", userCode);
80
- parsed.searchParams.set("device_code", deviceCode);
81
83
  return parsed.toString();
82
84
  }
package/src/banner.js ADDED
@@ -0,0 +1,42 @@
1
+ export const BRAND_BASE_URL = "https://claude360.xyz";
2
+
3
+ const LOGO_LINES = [
4
+ " ██████╗██╗ █████╗ ██╗ ██╗██████╗ ███████╗██████╗ ██████╗ ██████╗ ",
5
+ "██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔════╝╚════██╗██╔════╝ ██╔═████╗",
6
+ "██║ ██║ ███████║██║ ██║██║ ██║█████╗ █████╔╝███████╗ ██║██╔██║",
7
+ "██║ ██║ ██╔══██║██║ ██║██║ ██║██╔══╝ ╚═══██╗██╔═══██╗████╔╝██║",
8
+ "╚██████╗███████╗██║ ██║╚██████╔╝██████╔╝███████╗██████╔╝╚██████╔╝╚██████╔╝",
9
+ " ╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚═════╝ ╚═════╝ ╚═════╝ ",
10
+ ];
11
+
12
+ export function renderBanner({ version = "", baseUrl = BRAND_BASE_URL } = {}) {
13
+ const lines = [
14
+ "",
15
+ ...LOGO_LINES,
16
+ "",
17
+ " Claude360 模型站接入助手",
18
+ " 一键配置 Claude Code / Codex / API Key / 充值",
19
+ "",
20
+ `版本:${version || "-"} | 官网:${baseUrl}`,
21
+ "",
22
+ ];
23
+ return lines.join("\n");
24
+ }
25
+
26
+ export const CHECK_OK = "✓";
27
+ export const CHECK_WARN = "!";
28
+ export const CHECK_FAIL = "×";
29
+
30
+ export function formatCheckLine(status, text) {
31
+ const symbols = { ok: CHECK_OK, warn: CHECK_WARN, fail: CHECK_FAIL };
32
+ const symbol = symbols[status] || CHECK_WARN;
33
+ return `${symbol} ${text}`;
34
+ }
35
+
36
+ export function renderEnvironmentChecks(checks = []) {
37
+ return [
38
+ "正在检查运行环境...",
39
+ "",
40
+ ...checks.map((check) => formatCheckLine(check.status, check.text)),
41
+ ].join("\n");
42
+ }