codexpanel 0.1.3 → 0.1.4
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/bin/codexpanel.cjs +64 -14
- package/docs/desktop-npx-install-flow.md +2 -0
- package/package.json +1 -1
package/bin/codexpanel.cjs
CHANGED
|
@@ -11,7 +11,7 @@ const crypto = require("crypto");
|
|
|
11
11
|
const readline = require("readline");
|
|
12
12
|
const { spawn, spawnSync } = require("child_process");
|
|
13
13
|
|
|
14
|
-
const VERSION = "0.1.
|
|
14
|
+
const VERSION = "0.1.4";
|
|
15
15
|
const PROD_URL = "https://codexpanel.com";
|
|
16
16
|
const TEST_URL = "https://jd.6a.gs";
|
|
17
17
|
const LOCAL_HOST = "127.0.0.1";
|
|
@@ -244,18 +244,62 @@ function powershellPath() {
|
|
|
244
244
|
return "powershell.exe";
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
function browserCommandAttempt(command, args, options = {}) {
|
|
248
|
+
try {
|
|
249
|
+
const result = spawnSync(command, args, {
|
|
250
|
+
encoding: "utf8",
|
|
251
|
+
timeout: 8000,
|
|
252
|
+
windowsHide: true,
|
|
253
|
+
...options,
|
|
254
|
+
});
|
|
255
|
+
if (result.error) return { ok: false, command, error: result.error.message };
|
|
256
|
+
if (typeof result.status === "number" && result.status !== 0) {
|
|
257
|
+
const detail = (result.stderr || result.stdout || "").trim() || `exit ${result.status}`;
|
|
258
|
+
return { ok: false, command, error: detail };
|
|
259
|
+
}
|
|
260
|
+
return { ok: true, command };
|
|
261
|
+
} catch (error) {
|
|
262
|
+
return { ok: false, command, error: error.message || String(error) };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function cmdStartUrlCommand(url) {
|
|
267
|
+
const quoted = String(url).replace(/"/g, '""');
|
|
268
|
+
return `start "" "${quoted}"`;
|
|
269
|
+
}
|
|
270
|
+
|
|
247
271
|
function openBrowser(url) {
|
|
248
272
|
const target = String(url);
|
|
249
|
-
if (process.platform === "win32")
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
273
|
+
if (process.platform === "win32") {
|
|
274
|
+
const ps = powershellPath();
|
|
275
|
+
const attempts = [
|
|
276
|
+
["explorer.exe", [target]],
|
|
277
|
+
["rundll32.exe", ["url.dll,FileProtocolHandler", target]],
|
|
278
|
+
[ps, [
|
|
279
|
+
"-NoProfile",
|
|
280
|
+
"-ExecutionPolicy",
|
|
281
|
+
"Bypass",
|
|
282
|
+
"-Command",
|
|
283
|
+
"$url = [Environment]::GetEnvironmentVariable('CODEXPANEL_OPEN_URL', 'Process'); if (-not $url) { exit 64 }; Start-Process -FilePath $url",
|
|
284
|
+
], { env: { ...process.env, CODEXPANEL_OPEN_URL: target } }],
|
|
285
|
+
["cmd.exe", ["/d", "/s", "/c", cmdStartUrlCommand(target)]],
|
|
286
|
+
];
|
|
287
|
+
const failures = [];
|
|
288
|
+
for (const [command, args, options] of attempts) {
|
|
289
|
+
const result = browserCommandAttempt(command, args, options || {});
|
|
290
|
+
if (result.ok) return { ok: true, command };
|
|
291
|
+
failures.push(`${command}: ${result.error}`);
|
|
292
|
+
}
|
|
293
|
+
return { ok: false, error: failures.join("; ") };
|
|
294
|
+
}
|
|
295
|
+
const command = process.platform === "darwin" ? "open" : "xdg-open";
|
|
296
|
+
try {
|
|
297
|
+
const child = spawn(command, [target], { detached: true, stdio: "ignore" });
|
|
298
|
+
child.unref();
|
|
299
|
+
return { ok: true, command };
|
|
300
|
+
} catch (error) {
|
|
301
|
+
return { ok: false, error: error.message || String(error) };
|
|
302
|
+
}
|
|
259
303
|
}
|
|
260
304
|
|
|
261
305
|
function localAgentRoot() {
|
|
@@ -349,7 +393,7 @@ async function pollSetup(relayUrl, flowId, flowSecret) {
|
|
|
349
393
|
try {
|
|
350
394
|
const result = await requestJson("POST", relayUrl, "/api/desktop/setup/poll", { flowId, flowSecret }, {}, 30000);
|
|
351
395
|
transientFailures = 0;
|
|
352
|
-
if (result.status === "approved") return result;
|
|
396
|
+
if (result.status === "approved" || result.status === "completed") return result;
|
|
353
397
|
if (result.status === "rejected") throw new Error(result.error || "Setup was rejected in the browser.");
|
|
354
398
|
if (result.status === "expired") throw new Error("Setup login expired. Run npx -y codexpanel again.");
|
|
355
399
|
const hint = result.message || "等待浏览器登录绑定 / Waiting for browser sign-in";
|
|
@@ -478,8 +522,14 @@ async function install(options) {
|
|
|
478
522
|
const rl = createInterface();
|
|
479
523
|
await question(rl, "Press Enter to open browser / 按 Enter 拉起浏览器登录...");
|
|
480
524
|
rl.close();
|
|
481
|
-
|
|
482
|
-
|
|
525
|
+
const opened = openBrowser(start.loginUrl);
|
|
526
|
+
if (opened.ok) {
|
|
527
|
+
console.log(`CodexPanel: 已请求系统打开浏览器 / Browser launch requested (${opened.command}).`);
|
|
528
|
+
console.log(`CodexPanel: 如果浏览器没有弹出,请复制完整链接手动打开 / If no browser opens, copy this URL: ${start.loginUrl}`);
|
|
529
|
+
} else {
|
|
530
|
+
console.log(`CodexPanel: 自动拉起浏览器失败 / Unable to open browser automatically: ${opened.error || "unknown error"}`);
|
|
531
|
+
console.log(`CodexPanel: 请复制完整链接手动打开 / Please copy this full URL: ${start.loginUrl}`);
|
|
532
|
+
}
|
|
483
533
|
} else {
|
|
484
534
|
console.log("CodexPanel: --no-browser enabled. Open the URL above in your browser.");
|
|
485
535
|
}
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
|
|
25
25
|
这个 8 位绑定码不是旧访问码,也不是长期凭证。它只指向当前这一次 pending setup flow;真正的审批仍然依赖控制端或绑定页里的已登录用户 session。绑定完成后,agent 后续使用的是服务端签发的 device token。
|
|
26
26
|
|
|
27
|
+
从 `0.1.4` 开始,同一个安装流的三种绑定入口是幂等的:浏览器绑定页、控制端输入 8 位绑定码、手机扫码三者任意一个先确认后,另外两个入口再次操作只会提示“已经确认/已经完成,无需重复操作”,不会重新签发 setup token 或 device token。
|
|
28
|
+
|
|
27
29
|
登录成功后,页面会检查这台设备是否已经绑定过其他账号。如果已经绑定,它不会自动抢绑,而是让你选择:
|
|
28
30
|
|
|
29
31
|
- 保持原绑定并退出
|