auvezy-terminal-remote 0.7.2 → 0.7.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/dist/cli.js CHANGED
@@ -14,7 +14,7 @@ var DEFAULT_PORT, DEFAULT_SESSION_TTL_MS, DEFAULT_AUTH_RATE_LIMIT, DEFAULT_MAX_B
14
14
  var init_constants = __esm({
15
15
  "shared/dist/constants.js"() {
16
16
  "use strict";
17
- DEFAULT_PORT = 3e3;
17
+ DEFAULT_PORT = 3737;
18
18
  DEFAULT_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
19
19
  DEFAULT_AUTH_RATE_LIMIT = 20;
20
20
  DEFAULT_MAX_BUFFER_LINES = 1e4;
@@ -608,6 +608,9 @@ function assignFlag(out, key, value) {
608
608
  case "--strict-port":
609
609
  out.strictPort = value === true || value === "true";
610
610
  return;
611
+ case "--foreground":
612
+ out.foreground = value === true || value === "true";
613
+ return;
611
614
  case "--help":
612
615
  out.help = true;
613
616
  return;
@@ -719,7 +722,8 @@ var init_cli_utils = __esm({
719
722
  "--version",
720
723
  "--no-open",
721
724
  "--wait-confirm",
722
- "--strict-port"
725
+ "--strict-port",
726
+ "--foreground"
723
727
  ]);
724
728
  KNOWN_FLAGS_VALUE = /* @__PURE__ */ new Set([
725
729
  "--port",
@@ -774,8 +778,9 @@ Usage:
774
778
  atr <subcommand> [...] manage the broker / instances (see below)
775
779
 
776
780
  Subcommands:
777
- start [--port n] [--host ip] start the background service (broker) in the foreground
778
- (Ctrl+C to quit). --port / --host override 3000 / 0.0.0.0.
781
+ start [--port n] [--host ip] start the background service (broker); the command
782
+ returns immediately once the broker is healthy.
783
+ add --foreground to keep it attached (systemd / Docker).
779
784
  stop stop the background service
780
785
  status one-shot view: process, token, entry URLs, instances
781
786
  list list all live instances
@@ -804,7 +809,7 @@ Strict argument order:
804
809
  atr -- --weird '--' forces split; default shell with '--weird'
805
810
 
806
811
  Run options (for atr [program]):
807
- -p, --port <n> Background service (broker) port (default 3000). If broker is
812
+ -p, --port <n> Background service (broker) port (default 3737). If broker is
808
813
  already running and on a different port, atr will refuse to
809
814
  start \u2014 run 'atr stop' first if you want to switch.
810
815
  Worker ports are internal and auto-assigned; you don't set them.
@@ -842,7 +847,7 @@ Run options (for atr [program]):
842
847
  passes through automatically)
843
848
 
844
849
  Multi-instance:
845
- The background service (broker) runs once on port 3000 and is shared by all
850
+ The background service (broker) runs once on port 3737 and is shared by all
846
851
  instances. Running atr [program] in different terminals all connect to the
847
852
  same service; PTY children are independent. Click the tab bar in the browser
848
853
  to switch between them. If the service isn't running, the first atr will
@@ -2278,6 +2283,15 @@ var init_session_controller = __esm({
2278
2283
  lastError = null;
2279
2284
  /** Stop 携带的 last_assistant_message,可用于推送正文 */
2280
2285
  lastAssistantMessage = null;
2286
+ /**
2287
+ * 用户已请求跳过当前 pending(在 awaiting approval 状态下按了 ESC),
2288
+ * 但稳态确认信号(approval_resolved / turn_ended / user_prompt / 非 ESC
2289
+ * 输入)尚未到达。
2290
+ *
2291
+ * 用作两阶段取消的中间状态:前端在 status bar 显示 "已请求跳过,等待确认...";
2292
+ * 任意稳态信号一到就转 false 并清 pending。
2293
+ */
2294
+ pendingCancelRequested = false;
2281
2295
  buffer;
2282
2296
  writeToProcessStdout;
2283
2297
  // ──────────── WS 输出批合并 ────────────
@@ -2394,6 +2408,7 @@ var init_session_controller = __esm({
2394
2408
  }
2395
2409
  case "turn_ended": {
2396
2410
  this.activeTool = null;
2411
+ this.pendingApprovals.clear();
2397
2412
  if (e.lastMessage)
2398
2413
  this.lastAssistantMessage = e.lastMessage;
2399
2414
  break;
@@ -2401,6 +2416,7 @@ var init_session_controller = __esm({
2401
2416
  case "turn_failed": {
2402
2417
  this.lastError = { kind: e.errorKind, ...e.detail ? { detail: e.detail } : {}, at: Date.now() };
2403
2418
  this.activeTool = null;
2419
+ this.pendingApprovals.clear();
2404
2420
  pushTitle = `Claude turn \u5931\u8D25:${e.errorKind}`;
2405
2421
  pushBody = e.detail ?? e.errorKind;
2406
2422
  break;
@@ -2409,11 +2425,20 @@ var init_session_controller = __esm({
2409
2425
  logger.info({ phase: e.phase, detail: e.detail }, "session event");
2410
2426
  break;
2411
2427
  }
2412
- case "cwd_changed":
2428
+ case "cwd_changed": {
2429
+ break;
2430
+ }
2413
2431
  case "user_prompt": {
2432
+ if (this.pendingApprovals.size === 0 && !this.activeTool)
2433
+ return;
2434
+ this.pendingApprovals.clear();
2435
+ this.activeTool = null;
2414
2436
  break;
2415
2437
  }
2416
2438
  }
2439
+ if (this.pendingApprovals.size === 0) {
2440
+ this.pendingCancelRequested = false;
2441
+ }
2417
2442
  this.broadcastStatus();
2418
2443
  if (pushTitle && this.pushService && this.pushContext) {
2419
2444
  const ctx = this.pushContext;
@@ -2461,10 +2486,48 @@ var init_session_controller = __esm({
2461
2486
  activeTool: this.activeTool?.summary ?? null,
2462
2487
  pendingApprovals: this.pendingApprovals.size,
2463
2488
  pendingApprovalTools: tools,
2489
+ // pendingCancelRequested 只在仍有 pending 时有意义,否则前端不渲染
2490
+ pendingCancelRequested: this.pendingApprovals.size > 0 && this.pendingCancelRequested,
2464
2491
  lastError: this.lastError,
2465
2492
  lastAssistantMessage: this.lastAssistantMessage
2466
2493
  };
2467
2494
  }
2495
+ /**
2496
+ * 观察用户输入,推断审批是否被取消
2497
+ *
2498
+ * 两阶段取消:
2499
+ * 1. 收到纯 ESC(单字节 `\x1b`)→ 标记 cancel_requested,broadcast(UI 显示
2500
+ * "已请求跳过,等待确认...")。不立即清 pending,以防 claude 内部把 ESC
2501
+ * 拦住继续等审批。
2502
+ * 2. cancel_requested 后,任何非 ESC 输入(回车 / 字母 / 方向键 `\x1b[A` 等
2503
+ * length>1 的 ESC 序列)= prompt 已回 idle、用户在打新内容 → 清 pending。
2504
+ *
2505
+ * hook 路径(approval_resolved / turn_ended / turn_failed / user_prompt)在
2506
+ * `onIntegrationEvent` 末尾会清 cancel_requested。
2507
+ *
2508
+ * 误判防护:仅 `pendingApprovals.size > 0` 时触发——vim/htop 里按 ESC 是常态,
2509
+ * 但那时 pending 为 0,不会动状态。
2510
+ */
2511
+ observeUserInputForCancel(data) {
2512
+ if (this.pendingApprovals.size === 0)
2513
+ return;
2514
+ if (data.length === 0)
2515
+ return;
2516
+ const isEscOnly = data === "\x1B";
2517
+ if (!this.pendingCancelRequested) {
2518
+ if (isEscOnly) {
2519
+ this.pendingCancelRequested = true;
2520
+ this.broadcastStatus();
2521
+ }
2522
+ return;
2523
+ }
2524
+ if (isEscOnly)
2525
+ return;
2526
+ this.pendingApprovals.clear();
2527
+ this.pendingCancelRequested = false;
2528
+ this.activeTool = null;
2529
+ this.broadcastStatus();
2530
+ }
2468
2531
  // ──────────────── 公共 API ────────────────
2469
2532
  get status() {
2470
2533
  return this.deriveStatus();
@@ -2519,6 +2582,7 @@ var init_session_controller = __esm({
2519
2582
  this._baseStatus = "idle";
2520
2583
  this.activeTool = null;
2521
2584
  this.pendingApprovals.clear();
2585
+ this.pendingCancelRequested = false;
2522
2586
  this.ws.broadcast({
2523
2587
  type: "session_ended",
2524
2588
  exitCode,
@@ -2601,6 +2665,7 @@ var init_session_controller = __esm({
2601
2665
  handleWsMessage(wsConn, raw, {
2602
2666
  onUserInput: (data) => {
2603
2667
  this.pty.write(data);
2668
+ this.observeUserInputForCancel(data);
2604
2669
  },
2605
2670
  onResize: (cols, rows, source, master) => {
2606
2671
  const counts = this.ws.getClientCounts();
@@ -3813,7 +3878,7 @@ function buildHooksConfig(port, toggles = DEFAULT_CLAUDE_CODE_EVENTS) {
3813
3878
  out["PostCompact"] = allMatcher;
3814
3879
  out["CwdChanged"] = allMatcher;
3815
3880
  }
3816
- if (toggles.userPrompts) {
3881
+ if (toggles.userPrompts || toggles.approvals) {
3817
3882
  out["UserPromptSubmit"] = allMatcher;
3818
3883
  }
3819
3884
  return out;
@@ -4437,7 +4502,7 @@ async function renderQrCode(url, opts = {}) {
4437
4502
  try {
4438
4503
  return await QRCode.toString(url, {
4439
4504
  type: "utf8",
4440
- errorCorrectionLevel: opts.errorCorrectionLevel ?? "L",
4505
+ errorCorrectionLevel: opts.errorCorrectionLevel ?? "M",
4441
4506
  // utf8 模式本身就是半字符垂直压缩,体积约 qrcode-terminal small=true 的 1/2。
4442
4507
  // margin: utf8 渲染器在 margin=1 时有"Invalid array length" bug,避开它;
4443
4508
  // margin=2 视觉上仍然紧凑,且扫码识别率更高
@@ -5512,7 +5577,7 @@ var init_broker_server = __esm({
5512
5577
  init_instance_router();
5513
5578
  init_router();
5514
5579
  init_port_finder();
5515
- DEFAULT_BROKER_PORT = 3e3;
5580
+ DEFAULT_BROKER_PORT = 3737;
5516
5581
  DEFAULT_BROKER_HOST = "0.0.0.0";
5517
5582
  }
5518
5583
  });
@@ -5691,6 +5756,41 @@ var init_broker = __esm({
5691
5756
  }
5692
5757
  });
5693
5758
 
5759
+ // backend/dist/utils/wsl-detect.js
5760
+ import { readFileSync as readFileSync9 } from "node:fs";
5761
+ function isWsl(deps) {
5762
+ if (cached !== void 0 && deps === void 0)
5763
+ return cached;
5764
+ const platform2 = deps?.platform ?? process.platform;
5765
+ if (platform2 !== "linux") {
5766
+ if (deps === void 0)
5767
+ cached = false;
5768
+ return false;
5769
+ }
5770
+ let content;
5771
+ try {
5772
+ content = (deps?.readProcVersion ?? defaultReadProcVersion)();
5773
+ } catch {
5774
+ if (deps === void 0)
5775
+ cached = false;
5776
+ return false;
5777
+ }
5778
+ const lower = content.toLowerCase();
5779
+ const result = lower.includes("microsoft") || lower.includes("wsl");
5780
+ if (deps === void 0)
5781
+ cached = result;
5782
+ return result;
5783
+ }
5784
+ function defaultReadProcVersion() {
5785
+ return readFileSync9("/proc/version", "utf-8");
5786
+ }
5787
+ var cached;
5788
+ var init_wsl_detect = __esm({
5789
+ "backend/dist/utils/wsl-detect.js"() {
5790
+ "use strict";
5791
+ }
5792
+ });
5793
+
5694
5794
  // backend/dist/broker/entry-discovery.js
5695
5795
  var entry_discovery_exports = {};
5696
5796
  __export(entry_discovery_exports, {
@@ -5737,13 +5837,8 @@ function discoverEntries(opts) {
5737
5837
  url: buildEntryUrl("127.0.0.1", brokerPort, instanceId, urlOpts)
5738
5838
  });
5739
5839
  }
5740
- const order = {
5741
- tailscale: 0,
5742
- lan: 1,
5743
- ipv6: 2,
5744
- other: 3,
5745
- loopback: 4
5746
- };
5840
+ const wsl = isWsl();
5841
+ const order = wsl ? { lan: 0, ipv6: 1, tailscale: 2, other: 3, loopback: 4 } : { tailscale: 0, lan: 1, ipv6: 2, other: 3, loopback: 4 };
5747
5842
  out.sort((a, b) => {
5748
5843
  if (a.kind !== b.kind)
5749
5844
  return order[a.kind] - order[b.kind];
@@ -5804,6 +5899,7 @@ var init_entry_discovery = __esm({
5804
5899
  "backend/dist/broker/entry-discovery.js"() {
5805
5900
  "use strict";
5806
5901
  init_network();
5902
+ init_wsl_detect();
5807
5903
  }
5808
5904
  });
5809
5905
 
@@ -6301,17 +6397,17 @@ hint: check whether ~/.atr/broker.json is held by a stale process; set ATR_DEBUG
6301
6397
  setTimeout(() => triggerSpawn("timeout"), cfg.spawnTimeoutSec * 1e3).unref();
6302
6398
  }
6303
6399
  }
6400
+ const brokerHost = brokerState.host === "0.0.0.0" || brokerState.host === "::" ? displayIp : brokerState.host;
6304
6401
  void registry.register({
6305
6402
  instanceId,
6306
6403
  name: cfg.instanceName,
6307
- // 0.7.0:host 改为 worker 实际监听地址(loopback)。broker 阶段 3 反代时
6308
- // 直接用这个 host:port 连 worker。displayIp 已不参与 worker 注册
6309
6404
  host: "127.0.0.1",
6310
6405
  port: cfg.port,
6311
6406
  pid: process.pid,
6312
6407
  cwd: cfg.claudeCwd,
6313
6408
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
6314
- headless: cfg.noTerminal
6409
+ headless: cfg.noTerminal,
6410
+ ...brokerHost ? { brokerHost } : {}
6315
6411
  }).catch((err) => logger.warn({ err }, "\u6CE8\u518C\u5B9E\u4F8B\u5931\u8D25"));
6316
6412
  logger.info({
6317
6413
  port: cfg.port,
@@ -7386,7 +7482,7 @@ __export(service_installer_exports, {
7386
7482
  renderSystemdUnit: () => renderSystemdUnit,
7387
7483
  uninstall: () => uninstall
7388
7484
  });
7389
- import { existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync9, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "node:fs";
7485
+ import { existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync10, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "node:fs";
7390
7486
  import { resolve as resolve15, dirname as dirname8 } from "node:path";
7391
7487
  import { homedir as homedir9, platform } from "node:os";
7392
7488
  function detectPlatform(env = process.env, plat = platform()) {
@@ -7408,7 +7504,7 @@ After=network.target
7408
7504
 
7409
7505
  [Service]
7410
7506
  Type=simple
7411
- ExecStart=${opts.nodeBin} ${opts.cliPath} start
7507
+ ExecStart=${opts.nodeBin} ${opts.cliPath} start --foreground
7412
7508
  Restart=on-failure
7413
7509
  RestartSec=5s
7414
7510
  ${portEnv}[Install]
@@ -7433,6 +7529,7 @@ function renderLaunchdPlist(opts) {
7433
7529
  <string>${opts.nodeBin}</string>
7434
7530
  <string>${opts.cliPath}</string>
7435
7531
  <string>start</string>
7532
+ <string>--foreground</string>
7436
7533
  </array>
7437
7534
  <key>RunAtLoad</key><true/>
7438
7535
  <key>KeepAlive</key><true/>
@@ -7548,7 +7645,7 @@ var init_service_installer = __esm({
7548
7645
  mkdirSync12(p, o);
7549
7646
  },
7550
7647
  writeFileSync: (p, d, o) => writeFileSync5(p, d, { encoding: "utf-8", ...o ?? {} }),
7551
- readFileSync: (p) => readFileSync9(p, "utf-8"),
7648
+ readFileSync: (p) => readFileSync10(p, "utf-8"),
7552
7649
  rmSync: rmSync3
7553
7650
  };
7554
7651
  ServicePlatformUnsupportedError = class extends AppError {
@@ -7605,8 +7702,8 @@ var cli_exports = {};
7605
7702
  __export(cli_exports, {
7606
7703
  runServiceCli: () => runServiceCli
7607
7704
  });
7608
- import { execSync as execSync2 } from "node:child_process";
7609
- import { readFileSync as readFileSync10 } from "node:fs";
7705
+ import { execSync as execSync2, spawn as spawn4 } from "node:child_process";
7706
+ import { existsSync as existsSync16, openSync as openSync3, readFileSync as readFileSync11 } from "node:fs";
7610
7707
  import { resolve as resolve16, dirname as dirname9 } from "node:path";
7611
7708
  import { fileURLToPath as fileURLToPath3 } from "node:url";
7612
7709
  import { homedir as homedir10 } from "node:os";
@@ -7619,7 +7716,7 @@ function getBrokerVersion() {
7619
7716
  ];
7620
7717
  for (const pkgPath of candidates) {
7621
7718
  try {
7622
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
7719
+ const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
7623
7720
  if (pkg.name === "auvezy-terminal-remote" && typeof pkg.version === "string") {
7624
7721
  return pkg.version;
7625
7722
  }
@@ -7631,18 +7728,19 @@ function getBrokerVersion() {
7631
7728
  }
7632
7729
  function getCliPath() {
7633
7730
  const __dirname2 = dirname9(fileURLToPath3(import.meta.url));
7634
- const candidates = [
7635
- resolve16(__dirname2, "cli.js"),
7636
- resolve16(__dirname2, "..", "cli.js")
7637
- ];
7638
- for (const p of candidates) {
7639
- try {
7640
- readFileSync10(p);
7641
- return p;
7642
- } catch {
7643
- }
7731
+ const parentDir = resolve16(__dirname2, "..", "cli.js");
7732
+ const sameDir = resolve16(__dirname2, "cli.js");
7733
+ try {
7734
+ readFileSync11(parentDir);
7735
+ return parentDir;
7736
+ } catch {
7644
7737
  }
7645
- return candidates[0];
7738
+ try {
7739
+ readFileSync11(sameDir);
7740
+ return sameDir;
7741
+ } catch {
7742
+ }
7743
+ return parentDir;
7646
7744
  }
7647
7745
  async function runServiceCli(action, cli) {
7648
7746
  switch (action) {
@@ -7663,6 +7761,10 @@ async function runServiceCli(action, cli) {
7663
7761
  }
7664
7762
  }
7665
7763
  async function runBrokerStart(cli) {
7764
+ const wantForeground = cli.foreground === true || process.env["ATR_BROKER_FOREGROUND"] === "1";
7765
+ if (!wantForeground) {
7766
+ return runBrokerStartDaemonize(cli);
7767
+ }
7666
7768
  const port = cli.port ?? parseEnvPort(process.env["ATR_BROKER_PORT"]) ?? DEFAULT_BROKER_PORT;
7667
7769
  const host = cli.host ?? process.env["ATR_BROKER_HOST"] ?? DEFAULT_BROKER_HOST;
7668
7770
  const brokerVersion = getBrokerVersion();
@@ -7705,7 +7807,7 @@ async function runBrokerStart(cli) {
7705
7807
  const pushService = new PushService();
7706
7808
  await pushService.init();
7707
7809
  startInstanceWatcher(registry.filePath);
7708
- const cliJsPath = resolve16(__dirname2, "cli.js");
7810
+ const cliJsPath = getCliPath();
7709
7811
  const workdirAllow = currentUserConfig.workdirAllow;
7710
7812
  const workdirDeny = currentUserConfig.workdirDeny;
7711
7813
  const spawner = new DefaultInstanceSpawner({
@@ -7780,6 +7882,83 @@ function installBrokerLogRotator() {
7780
7882
  });
7781
7883
  return rotator;
7782
7884
  }
7885
+ async function runBrokerStartDaemonize(cli) {
7886
+ const tag = c.cyan("[atr]");
7887
+ const port = cli.port ?? parseEnvPort(process.env["ATR_BROKER_PORT"]) ?? DEFAULT_BROKER_PORT;
7888
+ const existing = readBrokerState();
7889
+ if (existing && isBrokerAlive(existing)) {
7890
+ process.stderr.write(`${tag} broker already running on ${existing.host}:${existing.port} (pid=${existing.pid})
7891
+ `);
7892
+ return 0;
7893
+ }
7894
+ const cliJsPath = getCliPath();
7895
+ const entry = resolveDaemonEntry(cliJsPath);
7896
+ const logFd = process.env["ATR_DEBUG_SPAWN"] ? openSync3(`/tmp/atr-broker-${Date.now()}.log`, "a") : "ignore";
7897
+ const childEnv = {
7898
+ ...process.env,
7899
+ ATR_BROKER_FOREGROUND: "1"
7900
+ };
7901
+ if (cli.port !== void 0)
7902
+ childEnv["ATR_BROKER_PORT"] = String(cli.port);
7903
+ if (cli.host !== void 0)
7904
+ childEnv["ATR_BROKER_HOST"] = cli.host;
7905
+ if (cli.strictPort)
7906
+ childEnv["ATR_BROKER_STRICT_PORT"] = "1";
7907
+ const child = spawn4(entry.execPath, [...entry.args, "start", "--foreground"], {
7908
+ env: childEnv,
7909
+ detached: true,
7910
+ stdio: ["ignore", logFd, logFd]
7911
+ });
7912
+ if (typeof child.pid !== "number") {
7913
+ process.stderr.write(`${tag} failed to spawn broker subprocess (no pid)
7914
+ `);
7915
+ return 1;
7916
+ }
7917
+ const earlyExit = [];
7918
+ const earlyError = [];
7919
+ child.once("error", (e) => {
7920
+ earlyError.push(e);
7921
+ });
7922
+ child.once("exit", (code, signal) => {
7923
+ earlyExit.push({ code, signal });
7924
+ });
7925
+ child.unref();
7926
+ const t0 = Date.now();
7927
+ const statePath = defaultBrokerStatePath();
7928
+ while (Date.now() - t0 < DAEMONIZE_TIMEOUT_MS) {
7929
+ const st = readBrokerState(statePath);
7930
+ if (st && isBrokerAlive(st) && st.pid === child.pid) {
7931
+ process.stdout.write(`${tag} broker started on ${c.green(`${st.host}:${st.port}`)} (pid=${st.pid})
7932
+ `);
7933
+ return 0;
7934
+ }
7935
+ await sleep4(DAEMONIZE_POLL_INTERVAL_MS);
7936
+ }
7937
+ let detail = "";
7938
+ if (earlyError[0]) {
7939
+ detail = `
7940
+ - spawn error: ${earlyError[0].message}`;
7941
+ } else if (earlyExit[0]) {
7942
+ detail = `
7943
+ - child exited early (code=${earlyExit[0].code}, signal=${earlyExit[0].signal})`;
7944
+ }
7945
+ process.stderr.write(`${tag} broker did not become ready within ${DAEMONIZE_TIMEOUT_MS}ms.${detail}
7946
+ - check ~/.auvezy/terminal-remote/broker-*.log for errors
7947
+ - or set ATR_DEBUG_SPAWN=1 and retry to capture /tmp/atr-broker-*.log
7948
+ - port ${port} may be busy; try '--port <other>' or '--strict-port' to fail fast
7949
+ `);
7950
+ return 1;
7951
+ }
7952
+ function resolveDaemonEntry(cliJsPath) {
7953
+ if (existsSync16(cliJsPath)) {
7954
+ return { execPath: process.execPath, args: [cliJsPath] };
7955
+ }
7956
+ const tsPath = cliJsPath.replace(/\.js$/, ".ts");
7957
+ if (existsSync16(tsPath)) {
7958
+ return { execPath: process.execPath, args: ["--import", "tsx", tsPath] };
7959
+ }
7960
+ return { execPath: process.execPath, args: [cliJsPath] };
7961
+ }
7783
7962
  async function runBrokerStop() {
7784
7963
  const tag = c.cyan("[atr]");
7785
7964
  const state = readBrokerState();
@@ -7937,12 +8116,12 @@ function writeServiceInstallSection() {
7937
8116
  async function writeTokenSection() {
7938
8117
  process.stdout.write(c.bold("=== Token ===\n"));
7939
8118
  try {
7940
- const { readFileSync: readFileSync11, statSync: statSync6 } = await import("node:fs");
8119
+ const { readFileSync: readFileSync12, statSync: statSync6 } = await import("node:fs");
7941
8120
  const { resolve: pathResolve2 } = await import("node:path");
7942
8121
  const { homedir: homedir11 } = await import("node:os");
7943
8122
  const path = pathResolve2(homedir11(), ".atrrc");
7944
8123
  const stat = statSync6(path);
7945
- const cfg = JSON.parse(readFileSync11(path, "utf-8"));
8124
+ const cfg = JSON.parse(readFileSync12(path, "utf-8"));
7946
8125
  if (typeof cfg.token === "string" && cfg.token.length > 0) {
7947
8126
  process.stdout.write(` token: ${cfg.token}
7948
8127
  `);
@@ -7964,10 +8143,10 @@ async function writeEntriesSection(brokerPort) {
7964
8143
  const { discoverEntries: discoverEntries2, kindLabel: kindLabel2 } = await Promise.resolve().then(() => (init_entry_discovery(), entry_discovery_exports));
7965
8144
  let token;
7966
8145
  try {
7967
- const { readFileSync: readFileSync11 } = await import("node:fs");
8146
+ const { readFileSync: readFileSync12 } = await import("node:fs");
7968
8147
  const { resolve: pathResolve2 } = await import("node:path");
7969
8148
  const { homedir: homedir11 } = await import("node:os");
7970
- const cfg = JSON.parse(readFileSync11(pathResolve2(homedir11(), ".atrrc"), "utf-8"));
8149
+ const cfg = JSON.parse(readFileSync12(pathResolve2(homedir11(), ".atrrc"), "utf-8"));
7971
8150
  if (typeof cfg.token === "string" && cfg.token.length > 0)
7972
8151
  token = cfg.token;
7973
8152
  } catch {
@@ -8026,12 +8205,12 @@ async function runListInstances() {
8026
8205
  async function runShowLogs() {
8027
8206
  const { homedir: homedir11 } = await import("node:os");
8028
8207
  const { resolve: pathResolve2 } = await import("node:path");
8029
- const { existsSync: existsSync16 } = await import("node:fs");
8030
- const { spawn: spawn4 } = await import("node:child_process");
8208
+ const { existsSync: existsSync17 } = await import("node:fs");
8209
+ const { spawn: spawn5 } = await import("node:child_process");
8031
8210
  const today = /* @__PURE__ */ new Date();
8032
8211
  const day = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, "0")}-${String(today.getDate()).padStart(2, "0")}`;
8033
8212
  const logPath = pathResolve2(homedir11(), ".atr", `broker-${day}.log`);
8034
- if (!existsSync16(logPath)) {
8213
+ if (!existsSync17(logPath)) {
8035
8214
  process.stderr.write(`[atr] today's log not found: ${logPath}
8036
8215
  hint: is the service running? try atr status, or atr start to launch.
8037
8216
  `);
@@ -8039,14 +8218,14 @@ hint: is the service running? try atr status, or atr start to launch.
8039
8218
  }
8040
8219
  process.stderr.write(`[atr] tail -F ${logPath} (Ctrl+C to quit)
8041
8220
  `);
8042
- const tail = spawn4("tail", ["-F", logPath], { stdio: "inherit" });
8221
+ const tail = spawn5("tail", ["-F", logPath], { stdio: "inherit" });
8043
8222
  return new Promise((resolveExit) => {
8044
8223
  tail.on("error", (err) => {
8045
8224
  process.stderr.write(`[atr] cannot spawn tail (${err.message}); falling back to one-shot output:
8046
8225
  `);
8047
- void import("node:fs").then(({ readFileSync: readFileSync11 }) => {
8226
+ void import("node:fs").then(({ readFileSync: readFileSync12 }) => {
8048
8227
  try {
8049
- process.stdout.write(readFileSync11(logPath, "utf-8"));
8228
+ process.stdout.write(readFileSync12(logPath, "utf-8"));
8050
8229
  resolveExit(0);
8051
8230
  } catch (e) {
8052
8231
  process.stderr.write(`[atr] failed to read log: ${e.message}
@@ -8132,7 +8311,7 @@ function parseEnvPort(raw) {
8132
8311
  function sleep4(ms) {
8133
8312
  return new Promise((r) => setTimeout(r, ms));
8134
8313
  }
8135
- var STOP_GRACE_MS, STOP_POLL_INTERVAL_MS;
8314
+ var DAEMONIZE_TIMEOUT_MS, DAEMONIZE_POLL_INTERVAL_MS, STOP_GRACE_MS, STOP_POLL_INTERVAL_MS;
8136
8315
  var init_cli = __esm({
8137
8316
  "backend/dist/broker/cli.js"() {
8138
8317
  "use strict";
@@ -8154,6 +8333,8 @@ var init_cli = __esm({
8154
8333
  init_broker_state();
8155
8334
  init_service_installer();
8156
8335
  init_colors();
8336
+ DAEMONIZE_TIMEOUT_MS = 8e3;
8337
+ DAEMONIZE_POLL_INTERVAL_MS = 100;
8157
8338
  STOP_GRACE_MS = 5e3;
8158
8339
  STOP_POLL_INTERVAL_MS = 100;
8159
8340
  }
@@ -8222,11 +8403,11 @@ void (async () => {
8222
8403
  process.exit(0);
8223
8404
  }
8224
8405
  if (cli.version) {
8225
- const { readFileSync: readFileSync11 } = await import("node:fs");
8406
+ const { readFileSync: readFileSync12 } = await import("node:fs");
8226
8407
  const { resolve: resolve17, dirname: dirname10 } = await import("node:path");
8227
8408
  const { fileURLToPath: fileURLToPath4 } = await import("node:url");
8228
8409
  const __dirname2 = dirname10(fileURLToPath4(import.meta.url));
8229
- const pkg = JSON.parse(readFileSync11(resolve17(__dirname2, "..", "package.json"), "utf-8"));
8410
+ const pkg = JSON.parse(readFileSync12(resolve17(__dirname2, "..", "package.json"), "utf-8"));
8230
8411
  process.stdout.write(`${pkg.version}
8231
8412
  `);
8232
8413
  process.exit(0);