codexpanel 0.1.9 → 0.1.11
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 +69 -9
- package/package.json +3 -2
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.11";
|
|
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";
|
|
@@ -34,6 +34,7 @@ Options:
|
|
|
34
34
|
--token-login Ask for a one-time terminal token from the setup page
|
|
35
35
|
--no-browser Do not open a browser; print the login URL instead
|
|
36
36
|
--no-autostart Do not enable Windows login autostart
|
|
37
|
+
--install-only Install/update the local agent and panel without binding
|
|
37
38
|
--dry-run Print resolved installer details without installing
|
|
38
39
|
--print-command Print the equivalent PowerShell bootstrap command after login approval
|
|
39
40
|
-h, --help Show help
|
|
@@ -59,6 +60,7 @@ function parseArgs(argv) {
|
|
|
59
60
|
autoStart: true,
|
|
60
61
|
dryRun: false,
|
|
61
62
|
printCommand: false,
|
|
63
|
+
installOnly: false,
|
|
62
64
|
};
|
|
63
65
|
|
|
64
66
|
const readValue = (flag) => {
|
|
@@ -80,6 +82,7 @@ function parseArgs(argv) {
|
|
|
80
82
|
else if (arg === "--token-login") options.tokenLogin = true;
|
|
81
83
|
else if (arg === "--no-browser") options.browser = false;
|
|
82
84
|
else if (arg === "--no-autostart") options.autoStart = false;
|
|
85
|
+
else if (arg === "--install-only") options.installOnly = true;
|
|
83
86
|
else if (arg === "--legacy-tunnel") throw new Error("--legacy-tunnel has been retired. Device traffic uses the server WSS gateway.");
|
|
84
87
|
else if (arg === "--no-tunnel") throw new Error("--no-tunnel has been retired. Device traffic uses the server WSS gateway.");
|
|
85
88
|
else if (arg === "--dry-run") options.dryRun = true;
|
|
@@ -364,6 +367,8 @@ async function preloadResources(relayUrl) {
|
|
|
364
367
|
let manifest = {};
|
|
365
368
|
try { manifest = JSON.parse(manifestText); }
|
|
366
369
|
catch { throw new Error("Server returned an invalid agent manifest."); }
|
|
370
|
+
const cacheKey = crypto.createHash("sha256").update(manifestText).digest("hex").slice(0, 16);
|
|
371
|
+
const preloadDir = path.join(localAgentRoot(), "preload", cacheKey);
|
|
367
372
|
const resources = [
|
|
368
373
|
manifest.hostRuntime,
|
|
369
374
|
manifest.hostRuntimeSchema,
|
|
@@ -376,9 +381,16 @@ async function preloadResources(relayUrl) {
|
|
|
376
381
|
const text = await downloadText(resourceUrl, 60000);
|
|
377
382
|
const hash = crypto.createHash("sha256").update(Buffer.from(text, "utf8")).digest("hex");
|
|
378
383
|
if (item.sha256 && hash !== item.sha256) throw new Error(`Checksum mismatch for ${item.fileName || item.path}`);
|
|
384
|
+
if (!item.dynamicAfterApproval && item.fileName) {
|
|
385
|
+
const target = path.join(preloadDir, ...String(item.fileName).split(/[\\/]+/));
|
|
386
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
387
|
+
fs.writeFileSync(target, text, "utf8");
|
|
388
|
+
}
|
|
379
389
|
}
|
|
380
390
|
console.log("CodexPanel: 资源已下载并校验完成 / Resources downloaded and verified.");
|
|
381
|
-
|
|
391
|
+
fs.mkdirSync(preloadDir, { recursive: true });
|
|
392
|
+
fs.writeFileSync(path.join(preloadDir, "manifest.json"), JSON.stringify(manifest, null, 2));
|
|
393
|
+
return { manifest, preloadDir };
|
|
382
394
|
}
|
|
383
395
|
|
|
384
396
|
function powershellCommand(url) {
|
|
@@ -427,14 +439,14 @@ async function tokenLogin(relayUrl, flowId, flowSecret) {
|
|
|
427
439
|
}
|
|
428
440
|
}
|
|
429
441
|
|
|
430
|
-
async function runBootstrap(relayUrl,
|
|
442
|
+
async function runBootstrap(relayUrl, profile, options, preloadDir = "") {
|
|
431
443
|
const url = new URL("/agent/bootstrap.ps1", relayUrl);
|
|
432
|
-
url.searchParams.set("setupToken", approval.setupToken);
|
|
433
444
|
url.searchParams.set("manualDeviceId", "1");
|
|
434
445
|
url.searchParams.set("manualDeviceName", "1");
|
|
435
|
-
url.searchParams.set("deviceId",
|
|
436
|
-
if (
|
|
446
|
+
url.searchParams.set("deviceId", profile.deviceId || stableDeviceId());
|
|
447
|
+
if (profile.deviceName) url.searchParams.set("deviceName", profile.deviceName);
|
|
437
448
|
if (options.workspace) url.searchParams.set("workspace", options.workspace);
|
|
449
|
+
if (preloadDir) url.searchParams.set("preloadDir", preloadDir);
|
|
438
450
|
url.searchParams.set("mode", "host-runtime");
|
|
439
451
|
url.searchParams.set("autoStart", options.autoStart ? "1" : "0");
|
|
440
452
|
|
|
@@ -453,9 +465,48 @@ async function runBootstrap(relayUrl, approval, options) {
|
|
|
453
465
|
if (result.status !== 0) throw new Error(`PowerShell installer exited with code ${result.status}`);
|
|
454
466
|
}
|
|
455
467
|
|
|
468
|
+
async function bindLocalAgent(approval) {
|
|
469
|
+
const root = localAgentRoot();
|
|
470
|
+
const script = path.join(root, "bind-agent.ps1");
|
|
471
|
+
if (!fs.existsSync(script)) throw new Error(`Local bind script not found: ${script}`);
|
|
472
|
+
const args = [
|
|
473
|
+
"-NoLogo", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", script,
|
|
474
|
+
"-SetupToken", approval.setupToken || "",
|
|
475
|
+
"-DeviceToken", approval.deviceToken || "",
|
|
476
|
+
"-UserId", approval.userId || "",
|
|
477
|
+
"-DeviceId", approval.deviceId || "",
|
|
478
|
+
];
|
|
479
|
+
if (approval.deviceName) args.push("-DeviceName", approval.deviceName);
|
|
480
|
+
const result = spawnSync(powershellPath(), args, { stdio: "inherit", windowsHide: false });
|
|
481
|
+
if (result.error) throw result.error;
|
|
482
|
+
if (result.status !== 0) throw new Error(`PowerShell bind script exited with code ${result.status}`);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function localProfileFromConfig(options, resolved) {
|
|
486
|
+
try {
|
|
487
|
+
const runtime = readLocalRuntimeConfig();
|
|
488
|
+
const config = runtime.config || {};
|
|
489
|
+
return {
|
|
490
|
+
...computerProfile(options, resolved),
|
|
491
|
+
deviceId: config.deviceId || config.agentId || stableDeviceId(),
|
|
492
|
+
deviceName: options.deviceName || config.deviceName || `${os.hostname()} / ${defaultUsername()} Codex Desktop Agent`,
|
|
493
|
+
workspace: options.workspace || config.workspace || process.env.USERPROFILE || process.cwd(),
|
|
494
|
+
};
|
|
495
|
+
} catch {
|
|
496
|
+
return computerProfile(options, resolved);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
456
500
|
async function install(options) {
|
|
501
|
+
if (options.command === "login" && !process.env.CODEXPANEL_SERVER) {
|
|
502
|
+
try {
|
|
503
|
+
const existing = readLocalRuntimeConfig().config;
|
|
504
|
+
if (existing.relayUrl && options.server === "production") options.server = existing.relayUrl;
|
|
505
|
+
} catch {}
|
|
506
|
+
}
|
|
457
507
|
const resolved = await resolveServer(options.server);
|
|
458
|
-
const
|
|
508
|
+
const loginOnly = options.command === "login";
|
|
509
|
+
const profile = loginOnly ? localProfileFromConfig(options, resolved) : computerProfile(options, resolved);
|
|
459
510
|
if (options.dryRun) {
|
|
460
511
|
console.log(JSON.stringify({
|
|
461
512
|
ok: true,
|
|
@@ -480,7 +531,16 @@ async function install(options) {
|
|
|
480
531
|
console.log(`Local relay port / 本地 relay 端口: ${resolved.localPort} (dynamic, not fixed)`);
|
|
481
532
|
}
|
|
482
533
|
|
|
483
|
-
|
|
534
|
+
let manifest = {};
|
|
535
|
+
if (!loginOnly) {
|
|
536
|
+
const preload = await preloadResources(resolved.relayUrl);
|
|
537
|
+
manifest = preload.manifest;
|
|
538
|
+
await runBootstrap(resolved.relayUrl, profile, options, preload.preloadDir);
|
|
539
|
+
}
|
|
540
|
+
if (options.installOnly) {
|
|
541
|
+
console.log("\nCodexPanel: local agent installed. Run `npx -y codexpanel login` to bind this device.");
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
484
544
|
const start = await requestJson("POST", resolved.relayUrl, "/api/desktop/setup/start", {
|
|
485
545
|
...profile,
|
|
486
546
|
agentVersion: manifest.agentVersion || "",
|
|
@@ -514,7 +574,7 @@ async function install(options) {
|
|
|
514
574
|
console.log("\nCodexPanel: 登录绑定成功 / Sign-in and binding approved.");
|
|
515
575
|
console.log(`User / 用户: ${approval.user?.name || approval.userId || "unknown"}`);
|
|
516
576
|
console.log(`Device / 设备: ${approval.deviceName || approval.deviceId}`);
|
|
517
|
-
await
|
|
577
|
+
await bindLocalAgent(approval);
|
|
518
578
|
if (approval.panelUrl) {
|
|
519
579
|
console.log(`CodexPanel local status panel / 本地状态面板: ${approval.panelUrl}`);
|
|
520
580
|
try { openBrowser(approval.panelUrl); } catch {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codexpanel",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "CodexPanel mobile control plane monorepo.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"verify:version": "node scripts/sync-product-version.mjs --check",
|
|
29
29
|
"prebuild": "npm run sync:version",
|
|
30
30
|
"build": "npm run build --workspaces --if-present",
|
|
31
|
-
"check": "npm run verify:version && npm run scan:encoding && npm run verify:api-contracts && npm run verify:client-gateway && npm run verify:server-gateway-boundary && npm run verify:iam && npm run verify:storage && npm run verify:domain-config && node scripts/money-smoke.cjs && npm run check --workspaces --if-present",
|
|
31
|
+
"check": "npm run verify:version && npm run verify:codex-adapter && npm run scan:encoding && npm run verify:api-contracts && npm run verify:client-gateway && npm run verify:server-gateway-boundary && npm run verify:iam && npm run verify:storage && npm run verify:domain-config && node scripts/money-smoke.cjs && npm run check --workspaces --if-present",
|
|
32
32
|
"release:local": "node scripts/release-local.mjs",
|
|
33
33
|
"release:server": "bash scripts/server-autodeploy.sh",
|
|
34
34
|
"release:test": "node scripts/test-release.mjs",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"generate:codex": "npm run generate -w @codexpanel/codex-app-server",
|
|
44
44
|
"verify:storage": "node scripts/verify-storage-boundary.mjs",
|
|
45
45
|
"verify:domain-config": "node scripts/verify-domain-config.mjs",
|
|
46
|
+
"verify:codex-adapter": "node scripts/verify-codex-adapter.mjs",
|
|
46
47
|
"smoke:storage": "node scripts/storage-smoke.cjs",
|
|
47
48
|
"purge:retired-desktop-residue": "node scripts/purge-retired-desktop-residue.mjs"
|
|
48
49
|
},
|