easyrouter-config 1.0.1 → 1.0.3

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.
Files changed (2) hide show
  1. package/dist/index.js +100 -20
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  intro,
6
6
  outro,
7
7
  text,
8
+ select,
8
9
  confirm,
9
10
  spinner,
10
11
  isCancel,
@@ -15,7 +16,7 @@ import {
15
16
  import { execa } from "execa";
16
17
  import pc from "picocolors";
17
18
  import { parseArgs } from "util";
18
- var VERSION = "0.1.0";
19
+ var VERSION = "0.1.1";
19
20
  var CLAUDE_BASE_URL = "https://easyrouter.io";
20
21
  var CODEX_BASE_URL = "https://easyrouter.io/v1";
21
22
  function parseCliArgs() {
@@ -74,6 +75,8 @@ ${pc.bold("\u539F\u7406:")}
74
75
  \u2022 Codex \u2192 ~/.codex/config.toml + ~/.codex/auth.json
75
76
  `);
76
77
  }
78
+ var IDLE_TIMEOUT_MS = 6e4;
79
+ var HARD_TIMEOUT_MS = 3e5;
77
80
  async function runZcf(opts) {
78
81
  const args = [
79
82
  "-y",
@@ -108,27 +111,86 @@ async function runZcf(opts) {
108
111
  "false"
109
112
  // 不装状态栏
110
113
  ];
114
+ const isWin = process.platform === "win32";
115
+ const cmd = isWin ? "npx.cmd" : "npx";
111
116
  let capturedOut = "";
112
117
  let capturedErr = "";
113
- try {
114
- const child = execa("npx", args, {
115
- stdio: opts.verbose ? "inherit" : "pipe",
116
- env: { ...process.env, FORCE_COLOR: opts.verbose ? "1" : "0" },
117
- reject: true
118
- });
119
- if (!opts.verbose) {
120
- child.stdout?.on("data", (chunk) => {
121
- capturedOut += chunk.toString();
122
- });
123
- child.stderr?.on("data", (chunk) => {
124
- capturedErr += chunk.toString();
125
- });
118
+ let lastDataAt = Date.now();
119
+ let killedReason = null;
120
+ const child = execa(cmd, args, {
121
+ stdio: ["ignore", "pipe", "pipe"],
122
+ env: {
123
+ ...process.env,
124
+ FORCE_COLOR: "0",
125
+ // 强制无颜色,免得 ANSI 干扰我们的检测
126
+ CI: "1",
127
+ // 让某些库自动进入"非交互模式"
128
+ npm_config_yes: "true"
129
+ // npx 自动 yes
130
+ },
131
+ reject: false,
132
+ // 我们自己处理退出码,避免 throw 时丢失输出
133
+ windowsHide: true
134
+ });
135
+ const handleChunk = (chunk, target) => {
136
+ const text2 = typeof chunk === "string" ? chunk : chunk.toString("utf8");
137
+ if (target === "out") capturedOut += text2;
138
+ else capturedErr += text2;
139
+ lastDataAt = Date.now();
140
+ if (opts.onProgress) {
141
+ const lines = text2.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
142
+ const last = lines[lines.length - 1];
143
+ if (last) opts.onProgress(last.slice(0, 80));
126
144
  }
127
- await child;
128
- } catch (err) {
129
- const detail = err?.stderr || err?.stdout || err?.message || String(err);
145
+ if (opts.verbose) {
146
+ process.stderr.write(text2);
147
+ }
148
+ };
149
+ child.stdout?.on("data", (c) => handleChunk(c, "out"));
150
+ child.stderr?.on("data", (c) => handleChunk(c, "err"));
151
+ const idleTimer = setInterval(() => {
152
+ if (Date.now() - lastDataAt > IDLE_TIMEOUT_MS) {
153
+ killedReason = `\u5B50\u8FDB\u7A0B\u5DF2 ${Math.floor(IDLE_TIMEOUT_MS / 1e3)} \u79D2\u65E0\u8F93\u51FA\uFF08\u7591\u4F3C\u5361\u5728\u4EA4\u4E92\u5F0F\u63D0\u95EE\u6216\u7F51\u7EDC\u95EE\u9898\uFF09`;
154
+ child.kill("SIGTERM");
155
+ setTimeout(() => {
156
+ try {
157
+ child.kill("SIGKILL");
158
+ } catch {
159
+ }
160
+ }, 5e3);
161
+ }
162
+ }, 5e3);
163
+ const hardTimer = setTimeout(() => {
164
+ killedReason = `\u5B50\u8FDB\u7A0B\u8D85\u8FC7 ${Math.floor(HARD_TIMEOUT_MS / 1e3)} \u79D2\u672A\u7ED3\u675F\uFF08\u5F3A\u5236\u8D85\u65F6\uFF09`;
165
+ child.kill("SIGTERM");
166
+ setTimeout(() => {
167
+ try {
168
+ child.kill("SIGKILL");
169
+ } catch {
170
+ }
171
+ }, 5e3);
172
+ }, HARD_TIMEOUT_MS);
173
+ const result = await child.catch((e) => e);
174
+ clearInterval(idleTimer);
175
+ clearTimeout(hardTimer);
176
+ const exitCode = result?.exitCode ?? null;
177
+ const signal = result?.signal ?? null;
178
+ if (killedReason) {
130
179
  throw new Error(
131
- `zcf \u6267\u884C\u5931\u8D25 (code-type=${opts.codeType}):
180
+ `zcf \u8C03\u7528\u5931\u8D25\uFF1A${killedReason}
181
+
182
+ \u5E38\u89C1\u539F\u56E0\uFF1A
183
+ \u2022 \u6CA1\u88C5\u76EE\u6807\u5BA2\u6237\u7AEF\uFF08zcf \u8BD5\u56FE\u8BE2\u95EE\u662F\u5426\u5B89\u88C5\uFF09
184
+ \u2022 npm \u955C\u50CF/\u7F51\u7EDC\u6162\uFF0Cnpx \u4E0B\u8F7D zcf \u5305\u5361\u4F4F
185
+ \u2022 \u9632\u706B\u5899\u62E6\u622A\u4E86 registry.npmjs.org
186
+
187
+ \u5EFA\u8BAE\uFF1A\u7528 ${pc.green("--verbose")} \u91CD\u65B0\u8FD0\u884C\u67E5\u770B\u8BE6\u7EC6\u65E5\u5FD7`
188
+ );
189
+ }
190
+ if (exitCode !== 0 || signal) {
191
+ const detail = (capturedErr || capturedOut || "").slice(-2e3) || `exit ${exitCode}, signal ${signal}`;
192
+ throw new Error(
193
+ `zcf \u5F02\u5E38\u9000\u51FA\uFF08code-type=${opts.codeType}, exit=${exitCode}\uFF09:
132
194
  ${detail}
133
195
 
134
196
  \u{1F4A1} \u7528 ${pc.green("--verbose")} \u91CD\u65B0\u8FD0\u884C\u53EF\u770B\u5230\u5B8C\u6574\u65E5\u5FD7`
@@ -212,6 +274,22 @@ async function main() {
212
274
  configCodex = false;
213
275
  } else if (args.only === "codex") {
214
276
  configClaude = false;
277
+ } else if (!args.apiKey) {
278
+ const target = await select({
279
+ message: "\u8BF7\u9009\u62E9\u8981\u914D\u7F6E\u7684\u5BA2\u6237\u7AEF",
280
+ options: [
281
+ { value: "both", label: "Claude Code + Codex", hint: "\u4E24\u4E2A\u90FD\u914D\uFF08\u63A8\u8350\uFF09" },
282
+ { value: "claude", label: "\u4EC5 Claude Code", hint: "\u53EA\u914D ~/.claude/settings.json" },
283
+ { value: "codex", label: "\u4EC5 Codex", hint: "\u53EA\u914D ~/.codex/config.toml" }
284
+ ],
285
+ initialValue: "both"
286
+ });
287
+ if (isCancel(target)) {
288
+ cancel("\u5DF2\u53D6\u6D88");
289
+ process.exit(0);
290
+ }
291
+ if (target === "claude") configCodex = false;
292
+ else if (target === "codex") configClaude = false;
215
293
  }
216
294
  note(
217
295
  [
@@ -246,7 +324,8 @@ async function main() {
246
324
  codeType: "cc",
247
325
  baseUrl: CLAUDE_BASE_URL,
248
326
  apiKey,
249
- verbose
327
+ verbose,
328
+ onProgress: (line) => s?.message(`\u914D\u7F6E Claude Code \xB7 ${pc.dim(line)}`)
250
329
  });
251
330
  s?.stop(pc.green("\u2713 Claude Code \u914D\u7F6E\u5B8C\u6210 ") + pc.dim("\u2192 ~/.claude/settings.json"));
252
331
  if (!s) log.success(pc.green("\u2713 Claude Code \u914D\u7F6E\u5B8C\u6210 \u2192 ~/.claude/settings.json"));
@@ -268,7 +347,8 @@ async function main() {
268
347
  codeType: "cx",
269
348
  baseUrl: CODEX_BASE_URL,
270
349
  apiKey,
271
- verbose
350
+ verbose,
351
+ onProgress: (line) => s?.message(`\u914D\u7F6E Codex \xB7 ${pc.dim(line)}`)
272
352
  });
273
353
  s?.stop(pc.green("\u2713 Codex \u914D\u7F6E\u5B8C\u6210 ") + pc.dim("\u2192 ~/.codex/config.toml"));
274
354
  if (!s) log.success(pc.green("\u2713 Codex \u914D\u7F6E\u5B8C\u6210 \u2192 ~/.codex/config.toml"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easyrouter-config",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "🚀 一键把 EasyRouter 接入 Claude Code & Codex —— 粘贴 Key 即用",
5
5
  "type": "module",
6
6
  "bin": {