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.
Files changed (2) hide show
  1. package/bin/codexpanel.cjs +69 -9
  2. package/package.json +3 -2
@@ -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.9";
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
- return manifest;
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, approval, options) {
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", approval.deviceId || stableDeviceId());
436
- if (approval.deviceName) url.searchParams.set("deviceName", approval.deviceName);
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 profile = computerProfile(options, resolved);
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
- const manifest = await preloadResources(resolved.relayUrl);
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 runBootstrap(resolved.relayUrl, approval, options);
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.9",
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
  },