weacpx 0.1.2 → 0.1.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.
@@ -43,6 +43,123 @@ var init_spawn_command = __esm(() => {
43
43
  SCRIPT_FILE_PATTERN = /\.(c|m)?js$/i;
44
44
  });
45
45
 
46
+ // src/transport/prompt-output.ts
47
+ function getPromptText(result) {
48
+ const stdoutOutput = extractPromptOutput(result.stdout);
49
+ if (result.code === 0) {
50
+ return sanitizePromptText(stdoutOutput.text);
51
+ }
52
+ const preferredError = extractPromptFailureMessage(result);
53
+ if (preferredError) {
54
+ throw new Error(preferredError);
55
+ }
56
+ const stderrOutput = extractPromptOutput(result.stderr);
57
+ const partialReply = [stdoutOutput, stderrOutput].filter((output) => output.hasAgentMessage && output.text.length > 0).map((output) => sanitizePromptText(output.text)).find((text) => text.length > 0);
58
+ if (partialReply) {
59
+ return partialReply;
60
+ }
61
+ throw new Error(`command failed with exit code ${result.code}`);
62
+ }
63
+ function normalizeCommandError(result) {
64
+ const preferredError = extractPromptFailureMessage(result);
65
+ if (preferredError) {
66
+ return preferredError;
67
+ }
68
+ return result.stdout.trim() || null;
69
+ }
70
+ function extractPromptFailureMessage(result) {
71
+ const rpcMessages = extractJsonRpcErrorMessages(result.stderr).concat(extractJsonRpcErrorMessages(result.stdout)).filter((message) => message.length > 0);
72
+ const preferredMessage = [...rpcMessages].reverse().find((message) => message !== "Resource not found");
73
+ if (preferredMessage) {
74
+ return preferredMessage;
75
+ }
76
+ if (rpcMessages.length > 0) {
77
+ return rpcMessages[rpcMessages.length - 1] ?? null;
78
+ }
79
+ const stderrText = result.stderr.trim();
80
+ if (stderrText.length > 0) {
81
+ return stderrText;
82
+ }
83
+ return null;
84
+ }
85
+ function extractPromptOutput(output) {
86
+ const lines = output.split(`
87
+ `).map((line) => line.trim()).filter((line) => line.length > 0);
88
+ const messageSegments = [];
89
+ let currentSegment = "";
90
+ let hasAgentMessage = false;
91
+ for (const line of lines) {
92
+ try {
93
+ const event = JSON.parse(line);
94
+ const isMessageChunk = event.method === "session/update" && event.params?.update?.sessionUpdate === "agent_message_chunk" && event.params.update.content?.type === "text" && typeof event.params.update.content.text === "string";
95
+ if (isMessageChunk) {
96
+ hasAgentMessage = true;
97
+ const chunk = event.params.update.content.text ?? "";
98
+ if (chunk.length > 0) {
99
+ currentSegment += chunk;
100
+ }
101
+ continue;
102
+ }
103
+ if (currentSegment.trim().length > 0) {
104
+ messageSegments.push(currentSegment.trim());
105
+ }
106
+ currentSegment = "";
107
+ } catch {
108
+ if (currentSegment.trim().length > 0) {
109
+ messageSegments.push(currentSegment.trim());
110
+ currentSegment = "";
111
+ }
112
+ }
113
+ }
114
+ if (currentSegment.trim().length > 0) {
115
+ messageSegments.push(currentSegment.trim());
116
+ }
117
+ if (messageSegments.length > 0) {
118
+ return {
119
+ text: messageSegments[messageSegments.length - 1],
120
+ hasAgentMessage
121
+ };
122
+ }
123
+ return {
124
+ text: output.trim(),
125
+ hasAgentMessage
126
+ };
127
+ }
128
+ function sanitizePromptText(text) {
129
+ const trimmed = text.trim();
130
+ const paragraphs = trimmed.split(/\n\s*\n/);
131
+ if (paragraphs.length < 2) {
132
+ return trimmed;
133
+ }
134
+ const firstParagraph = paragraphs[0].trim().replace(/\s+/g, " ").toLowerCase();
135
+ if (!looksLikeWorkflowPreamble(firstParagraph)) {
136
+ return trimmed;
137
+ }
138
+ return paragraphs.slice(1).join(`
139
+
140
+ `).trim();
141
+ }
142
+ function looksLikeWorkflowPreamble(paragraph) {
143
+ if (!paragraph.startsWith("using ")) {
144
+ return false;
145
+ }
146
+ return paragraph.includes("using-superpowers") || paragraph.includes("repo workflow requirement") || paragraph.includes("workflow requirement") || paragraph.includes("before responding") || paragraph.includes("skill check");
147
+ }
148
+ function extractJsonRpcErrorMessages(output) {
149
+ return output.split(`
150
+ `).map((line) => line.trim()).filter((line) => line.length > 0).flatMap((line) => {
151
+ try {
152
+ const payload = JSON.parse(line);
153
+ if (typeof payload.error?.message === "string" && payload.error.message.length > 0) {
154
+ return [payload.error.message];
155
+ }
156
+ } catch {
157
+ return [];
158
+ }
159
+ return [];
160
+ });
161
+ }
162
+
46
163
  // src/bridge/bridge-main.ts
47
164
  import { createInterface } from "node:readline";
48
165
 
@@ -169,17 +286,14 @@ class BridgeRuntime {
169
286
  return {};
170
287
  }
171
288
  async prompt(input) {
172
- const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
289
+ const spawnSpec = resolveSpawnCommand(this.command, this.buildPromptArgs(input, [
173
290
  "prompt",
174
291
  "-s",
175
292
  input.name,
176
293
  input.text
177
294
  ]));
178
295
  const result = await this.run(spawnSpec.command, spawnSpec.args);
179
- if (result.code !== 0) {
180
- throw new Error(result.stderr || result.stdout || "prompt failed");
181
- }
182
- return { text: result.stdout.trim() };
296
+ return { text: getPromptText(result) };
183
297
  }
184
298
  async cancel(input) {
185
299
  const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
@@ -205,6 +319,12 @@ class BridgeRuntime {
205
319
  }
206
320
  return ["--format", "quiet", "--cwd", input.cwd, input.agent, ...tail];
207
321
  }
322
+ buildPromptArgs(input, tail) {
323
+ if (input.agentCommand) {
324
+ return ["--format", "json", "--json-strict", "--cwd", input.cwd, "--agent", input.agentCommand, ...tail];
325
+ }
326
+ return ["--format", "json", "--json-strict", "--cwd", input.cwd, input.agent, ...tail];
327
+ }
208
328
  }
209
329
  async function defaultRunner(command, args) {
210
330
  return await new Promise((resolve, reject) => {
package/dist/cli.js CHANGED
@@ -1440,6 +1440,9 @@ function parseCommand(input) {
1440
1440
  }
1441
1441
  return { kind: "session.attach", alias, agent, workspace, transportSession };
1442
1442
  }
1443
+ if (command.startsWith("/") && isRecognizedCommand(command)) {
1444
+ return { kind: "invalid", text: trimmed, recognizedCommand: command };
1445
+ }
1443
1446
  return { kind: "prompt", text: trimmed };
1444
1447
  }
1445
1448
  function hasAnyFlag(parts, flags) {
@@ -1462,6 +1465,9 @@ function normalizeCommand(command) {
1462
1465
  return "/cancel";
1463
1466
  return command;
1464
1467
  }
1468
+ function isRecognizedCommand(command) {
1469
+ return RECOGNIZED_COMMANDS.has(command);
1470
+ }
1465
1471
  function tokenizeCommand(input) {
1466
1472
  const tokens = [];
1467
1473
  let current = "";
@@ -1493,10 +1499,26 @@ function tokenizeCommand(input) {
1493
1499
  }
1494
1500
  return tokens;
1495
1501
  }
1502
+ var RECOGNIZED_COMMANDS;
1503
+ var init_parse_command = __esm(() => {
1504
+ RECOGNIZED_COMMANDS = new Set([
1505
+ "/help",
1506
+ "/agents",
1507
+ "/workspaces",
1508
+ "/sessions",
1509
+ "/status",
1510
+ "/cancel",
1511
+ "/session",
1512
+ "/workspace",
1513
+ "/use",
1514
+ "/agent"
1515
+ ]);
1516
+ });
1496
1517
 
1497
1518
  // src/commands/command-router.ts
1498
1519
  import { access } from "node:fs/promises";
1499
1520
  import { basename as basename2, normalize } from "node:path";
1521
+ import { homedir } from "node:os";
1500
1522
 
1501
1523
  class CommandRouter {
1502
1524
  sessions;
@@ -1520,6 +1542,19 @@ class CommandRouter {
1520
1542
  });
1521
1543
  return await this.executeCommand(chatKey, command.kind, startedAt, async () => {
1522
1544
  switch (command.kind) {
1545
+ case "invalid":
1546
+ return {
1547
+ text: [
1548
+ "无法识别的命令格式。",
1549
+ "",
1550
+ "正确的会话创建格式:",
1551
+ "/session new <别名> --agent <Agent名> --ws <工作区名>",
1552
+ "",
1553
+ "例如:",
1554
+ "/session new demo --agent claude --ws weacpx"
1555
+ ].join(`
1556
+ `)
1557
+ };
1523
1558
  case "help":
1524
1559
  return { text: renderHelpText() };
1525
1560
  case "agents":
@@ -1553,10 +1588,11 @@ class CommandRouter {
1553
1588
  if (!this.config || !this.configStore) {
1554
1589
  return { text: "当前没有加载可写入的配置。" };
1555
1590
  }
1556
- if (!await pathExists(command.cwd)) {
1591
+ const wsCwd = normalizePathForWorkspace(command.cwd);
1592
+ if (!await pathExists(wsCwd)) {
1557
1593
  return { text: `工作区路径不存在:${command.cwd}` };
1558
1594
  }
1559
- const updated = await this.configStore.upsertWorkspace(command.name, command.cwd);
1595
+ const updated = await this.configStore.upsertWorkspace(command.name, wsCwd);
1560
1596
  this.replaceConfig(updated);
1561
1597
  return { text: `工作区「${command.name}」已保存` };
1562
1598
  }
@@ -1755,7 +1791,17 @@ class CommandRouter {
1755
1791
  `)
1756
1792
  };
1757
1793
  }
1758
- throw error;
1794
+ if (!isPartialPromptOutputError(message)) {
1795
+ throw error;
1796
+ }
1797
+ return {
1798
+ text: [
1799
+ `当前会话「${session.alias}」执行中断,未收到最终回复。`,
1800
+ "请直接重试;如果长时间无响应,可先发送 /cancel 后再重试。",
1801
+ `错误信息:${summarizeTransportError(message)}`
1802
+ ].join(`
1803
+ `)
1804
+ };
1759
1805
  }
1760
1806
  renderSessionCreationError(session, error) {
1761
1807
  const message = error instanceof Error ? error.message : String(error);
@@ -1891,7 +1937,8 @@ async function pathExists(path) {
1891
1937
  }
1892
1938
  }
1893
1939
  function normalizePathForWorkspace(path) {
1894
- return normalize(path);
1940
+ const expanded = path.startsWith("~") ? homedir() + path.slice(1) : path;
1941
+ return normalize(expanded);
1895
1942
  }
1896
1943
  function sameWorkspacePath(left, right) {
1897
1944
  const normalizedLeft = normalizePathForWorkspace(left);
@@ -1901,9 +1948,16 @@ function sameWorkspacePath(left, right) {
1901
1948
  }
1902
1949
  return normalizedLeft === normalizedRight;
1903
1950
  }
1951
+ function summarizeTransportError(message) {
1952
+ return message.replace(/\s+/g, " ").trim().slice(0, 200);
1953
+ }
1954
+ function isPartialPromptOutputError(message) {
1955
+ return message.includes("未收到最终回复");
1956
+ }
1904
1957
  var init_command_router = __esm(() => {
1905
1958
  init_agent_templates();
1906
1959
  init_app_logger();
1960
+ init_parse_command();
1907
1961
  });
1908
1962
 
1909
1963
  // src/config/resolve-agent-command.ts
@@ -2319,17 +2373,27 @@ async function runConsole(paths, deps) {
2319
2373
  configPath: paths.configPath,
2320
2374
  statePath: paths.statePath
2321
2375
  });
2322
- heartbeatTimer = setIntervalFn(() => deps.daemonRuntime?.heartbeat(), deps.heartbeatIntervalMs ?? 30000);
2376
+ heartbeatTimer = setIntervalFn(() => {
2377
+ deps.daemonRuntime?.heartbeat().catch(() => {});
2378
+ }, deps.heartbeatIntervalMs ?? 30000);
2323
2379
  }
2324
2380
  await sdk.start(runtime.agent);
2325
2381
  } finally {
2382
+ let disposeError = null;
2326
2383
  if (heartbeatTimer !== null) {
2327
2384
  clearIntervalFn(heartbeatTimer);
2328
2385
  }
2329
- await runtime.dispose();
2386
+ try {
2387
+ await runtime.dispose();
2388
+ } catch (error) {
2389
+ disposeError = error;
2390
+ }
2330
2391
  if (deps.daemonRuntime) {
2331
2392
  await deps.daemonRuntime.stop();
2332
2393
  }
2394
+ if (disposeError) {
2395
+ throw disposeError;
2396
+ }
2333
2397
  }
2334
2398
  }
2335
2399
 
@@ -2376,6 +2440,13 @@ class AcpxBridgeClient {
2376
2440
  }
2377
2441
  pending.reject(new Error(response.error.message));
2378
2442
  }
2443
+ handleExit(error) {
2444
+ const pendingRequests = [...this.pending.values()];
2445
+ this.pending.clear();
2446
+ for (const pending of pendingRequests) {
2447
+ pending.reject(error);
2448
+ }
2449
+ }
2379
2450
  }
2380
2451
  function buildBridgeSpawnSpec(options) {
2381
2452
  if (options.execPath.endsWith("bun")) {
@@ -2415,6 +2486,10 @@ async function spawnAcpxBridgeClient(options = {}) {
2415
2486
  });
2416
2487
  child.on("exit", () => {
2417
2488
  output.close();
2489
+ client.handleExit(new Error("bridge process exited before responding"));
2490
+ });
2491
+ child.on("error", (error) => {
2492
+ client.handleExit(error);
2418
2493
  });
2419
2494
  client.waitUntilReady = async () => {
2420
2495
  await client.request("ping", {});
@@ -2490,6 +2565,123 @@ var init_spawn_command = __esm(() => {
2490
2565
  SCRIPT_FILE_PATTERN = /\.(c|m)?js$/i;
2491
2566
  });
2492
2567
 
2568
+ // src/transport/prompt-output.ts
2569
+ function getPromptText(result) {
2570
+ const stdoutOutput = extractPromptOutput(result.stdout);
2571
+ if (result.code === 0) {
2572
+ return sanitizePromptText(stdoutOutput.text);
2573
+ }
2574
+ const preferredError = extractPromptFailureMessage(result);
2575
+ if (preferredError) {
2576
+ throw new Error(preferredError);
2577
+ }
2578
+ const stderrOutput = extractPromptOutput(result.stderr);
2579
+ const partialReply = [stdoutOutput, stderrOutput].filter((output) => output.hasAgentMessage && output.text.length > 0).map((output) => sanitizePromptText(output.text)).find((text) => text.length > 0);
2580
+ if (partialReply) {
2581
+ return partialReply;
2582
+ }
2583
+ throw new Error(`command failed with exit code ${result.code}`);
2584
+ }
2585
+ function normalizeCommandError(result) {
2586
+ const preferredError = extractPromptFailureMessage(result);
2587
+ if (preferredError) {
2588
+ return preferredError;
2589
+ }
2590
+ return result.stdout.trim() || null;
2591
+ }
2592
+ function extractPromptFailureMessage(result) {
2593
+ const rpcMessages = extractJsonRpcErrorMessages(result.stderr).concat(extractJsonRpcErrorMessages(result.stdout)).filter((message) => message.length > 0);
2594
+ const preferredMessage = [...rpcMessages].reverse().find((message) => message !== "Resource not found");
2595
+ if (preferredMessage) {
2596
+ return preferredMessage;
2597
+ }
2598
+ if (rpcMessages.length > 0) {
2599
+ return rpcMessages[rpcMessages.length - 1] ?? null;
2600
+ }
2601
+ const stderrText = result.stderr.trim();
2602
+ if (stderrText.length > 0) {
2603
+ return stderrText;
2604
+ }
2605
+ return null;
2606
+ }
2607
+ function extractPromptOutput(output) {
2608
+ const lines = output.split(`
2609
+ `).map((line) => line.trim()).filter((line) => line.length > 0);
2610
+ const messageSegments = [];
2611
+ let currentSegment = "";
2612
+ let hasAgentMessage = false;
2613
+ for (const line of lines) {
2614
+ try {
2615
+ const event = JSON.parse(line);
2616
+ const isMessageChunk = event.method === "session/update" && event.params?.update?.sessionUpdate === "agent_message_chunk" && event.params.update.content?.type === "text" && typeof event.params.update.content.text === "string";
2617
+ if (isMessageChunk) {
2618
+ hasAgentMessage = true;
2619
+ const chunk = event.params.update.content.text ?? "";
2620
+ if (chunk.length > 0) {
2621
+ currentSegment += chunk;
2622
+ }
2623
+ continue;
2624
+ }
2625
+ if (currentSegment.trim().length > 0) {
2626
+ messageSegments.push(currentSegment.trim());
2627
+ }
2628
+ currentSegment = "";
2629
+ } catch {
2630
+ if (currentSegment.trim().length > 0) {
2631
+ messageSegments.push(currentSegment.trim());
2632
+ currentSegment = "";
2633
+ }
2634
+ }
2635
+ }
2636
+ if (currentSegment.trim().length > 0) {
2637
+ messageSegments.push(currentSegment.trim());
2638
+ }
2639
+ if (messageSegments.length > 0) {
2640
+ return {
2641
+ text: messageSegments[messageSegments.length - 1],
2642
+ hasAgentMessage
2643
+ };
2644
+ }
2645
+ return {
2646
+ text: output.trim(),
2647
+ hasAgentMessage
2648
+ };
2649
+ }
2650
+ function sanitizePromptText(text) {
2651
+ const trimmed = text.trim();
2652
+ const paragraphs = trimmed.split(/\n\s*\n/);
2653
+ if (paragraphs.length < 2) {
2654
+ return trimmed;
2655
+ }
2656
+ const firstParagraph = paragraphs[0].trim().replace(/\s+/g, " ").toLowerCase();
2657
+ if (!looksLikeWorkflowPreamble(firstParagraph)) {
2658
+ return trimmed;
2659
+ }
2660
+ return paragraphs.slice(1).join(`
2661
+
2662
+ `).trim();
2663
+ }
2664
+ function looksLikeWorkflowPreamble(paragraph) {
2665
+ if (!paragraph.startsWith("using ")) {
2666
+ return false;
2667
+ }
2668
+ return paragraph.includes("using-superpowers") || paragraph.includes("repo workflow requirement") || paragraph.includes("workflow requirement") || paragraph.includes("before responding") || paragraph.includes("skill check");
2669
+ }
2670
+ function extractJsonRpcErrorMessages(output) {
2671
+ return output.split(`
2672
+ `).map((line) => line.trim()).filter((line) => line.length > 0).flatMap((line) => {
2673
+ try {
2674
+ const payload = JSON.parse(line);
2675
+ if (typeof payload.error?.message === "string" && payload.error.message.length > 0) {
2676
+ return [payload.error.message];
2677
+ }
2678
+ } catch {
2679
+ return [];
2680
+ }
2681
+ return [];
2682
+ });
2683
+ }
2684
+
2493
2685
  // src/transport/acpx-cli/node-pty-helper.ts
2494
2686
  import { chmod as chmodFs } from "node:fs/promises";
2495
2687
  import { dirname as dirname7, join as join3 } from "node:path";
@@ -2598,8 +2790,8 @@ class AcpxCliTransport {
2598
2790
  });
2599
2791
  }
2600
2792
  async prompt(session, text) {
2601
- const output = await this.run(this.buildPromptArgs(session, text));
2602
- return { text: sanitizePromptText(extractPromptText(output)) };
2793
+ const result = await this.runCommand(this.command, this.buildPromptArgs(session, text));
2794
+ return { text: getPromptText(result) };
2603
2795
  }
2604
2796
  async cancel(session) {
2605
2797
  const output = await this.run(this.buildArgs(session, [
@@ -2689,86 +2881,6 @@ function renderCommandForError(args) {
2689
2881
  }
2690
2882
  return rendered.join(" ");
2691
2883
  }
2692
- function extractPromptText(output) {
2693
- const lines = output.split(`
2694
- `).map((line) => line.trim()).filter((line) => line.length > 0);
2695
- const messageSegments = [];
2696
- let currentSegment = "";
2697
- for (const line of lines) {
2698
- try {
2699
- const event = JSON.parse(line);
2700
- const isMessageChunk = event.method === "session/update" && event.params?.update?.sessionUpdate === "agent_message_chunk" && event.params.update.content?.type === "text" && typeof event.params.update.content.text === "string";
2701
- if (isMessageChunk) {
2702
- const chunk = event.params.update.content.text ?? "";
2703
- if (chunk.length > 0) {
2704
- currentSegment += chunk;
2705
- }
2706
- continue;
2707
- }
2708
- if (currentSegment.trim().length > 0) {
2709
- messageSegments.push(currentSegment.trim());
2710
- }
2711
- currentSegment = "";
2712
- } catch {
2713
- if (currentSegment.trim().length > 0) {
2714
- messageSegments.push(currentSegment.trim());
2715
- currentSegment = "";
2716
- }
2717
- }
2718
- }
2719
- if (currentSegment.trim().length > 0) {
2720
- messageSegments.push(currentSegment.trim());
2721
- }
2722
- if (messageSegments.length > 0) {
2723
- return messageSegments[messageSegments.length - 1];
2724
- }
2725
- return output.trim();
2726
- }
2727
- function sanitizePromptText(text) {
2728
- const trimmed = text.trim();
2729
- const paragraphs = trimmed.split(/\n\s*\n/);
2730
- if (paragraphs.length < 2) {
2731
- return trimmed;
2732
- }
2733
- const firstParagraph = paragraphs[0].trim().replace(/\s+/g, " ").toLowerCase();
2734
- if (!looksLikeWorkflowPreamble(firstParagraph)) {
2735
- return trimmed;
2736
- }
2737
- return paragraphs.slice(1).join(`
2738
-
2739
- `).trim();
2740
- }
2741
- function looksLikeWorkflowPreamble(paragraph) {
2742
- if (!paragraph.startsWith("using ")) {
2743
- return false;
2744
- }
2745
- return paragraph.includes("using-superpowers") || paragraph.includes("repo workflow requirement") || paragraph.includes("workflow requirement") || paragraph.includes("before responding") || paragraph.includes("skill check");
2746
- }
2747
- function normalizeCommandError(result) {
2748
- const rpcMessages = extractJsonRpcErrorMessages(result.stderr).concat(extractJsonRpcErrorMessages(result.stdout)).filter((message) => message.length > 0);
2749
- const preferredMessage = [...rpcMessages].reverse().find((message) => message !== "Resource not found");
2750
- if (preferredMessage) {
2751
- return preferredMessage;
2752
- }
2753
- if (rpcMessages.length > 0) {
2754
- return rpcMessages[rpcMessages.length - 1] ?? null;
2755
- }
2756
- return result.stderr.trim() || result.stdout.trim() || null;
2757
- }
2758
- function extractJsonRpcErrorMessages(output) {
2759
- return output.split(`
2760
- `).map((line) => line.trim()).filter((line) => line.length > 0).flatMap((line) => {
2761
- try {
2762
- const payload = JSON.parse(line);
2763
- if (typeof payload.error?.message === "string" && payload.error.message.length > 0) {
2764
- return [payload.error.message];
2765
- }
2766
- } catch {
2767
- return [];
2768
- }
2769
- return [];
2770
- });
2771
- }
2772
2884
  var require3;
2773
2885
  var init_acpx_cli_transport = __esm(() => {
2774
2886
  init_spawn_command();
@@ -2783,7 +2895,7 @@ __export(exports_main, {
2783
2895
  main: () => main2,
2784
2896
  buildApp: () => buildApp
2785
2897
  });
2786
- import { homedir } from "node:os";
2898
+ import { homedir as homedir2 } from "node:os";
2787
2899
  import { dirname as dirname8, join as join4 } from "node:path";
2788
2900
  import { fileURLToPath as fileURLToPath2 } from "node:url";
2789
2901
  async function buildApp(paths, deps = {}) {
@@ -2844,7 +2956,7 @@ async function main2() {
2844
2956
  }
2845
2957
  }
2846
2958
  function resolveRuntimePaths() {
2847
- const home = process.env.HOME ?? homedir();
2959
+ const home = process.env.HOME ?? homedir2();
2848
2960
  if (!home) {
2849
2961
  throw new Error("Unable to resolve the current user home directory");
2850
2962
  }
@@ -2880,7 +2992,7 @@ var init_main = __esm(async () => {
2880
2992
  });
2881
2993
 
2882
2994
  // src/cli.ts
2883
- import { homedir as homedir2 } from "node:os";
2995
+ import { homedir as homedir3 } from "node:os";
2884
2996
  import { sep } from "node:path";
2885
2997
  import { fileURLToPath as fileURLToPath3 } from "node:url";
2886
2998
 
@@ -2929,10 +3041,18 @@ class DaemonController {
2929
3041
  paths;
2930
3042
  deps;
2931
3043
  statusStore;
3044
+ startupPollIntervalMs;
3045
+ startupTimeoutMs;
3046
+ onStartupPoll;
2932
3047
  constructor(paths, deps) {
2933
3048
  this.paths = paths;
2934
3049
  this.deps = deps;
2935
3050
  this.statusStore = new DaemonStatusStore(paths.statusFile);
3051
+ this.startupPollIntervalMs = deps.startupPollIntervalMs ?? 50;
3052
+ this.startupTimeoutMs = deps.startupTimeoutMs ?? 5000;
3053
+ this.onStartupPoll = deps.onStartupPoll ?? (async () => {
3054
+ await new Promise((resolve) => setTimeout(resolve, this.startupPollIntervalMs));
3055
+ });
2936
3056
  }
2937
3057
  async getStatus() {
2938
3058
  const pid = await this.loadPid();
@@ -2958,8 +3078,10 @@ class DaemonController {
2958
3078
  if (current.state === "running") {
2959
3079
  return { state: "already-running", pid: current.pid };
2960
3080
  }
3081
+ await this.statusStore.clear();
2961
3082
  const pid = await this.deps.spawnDetached();
2962
3083
  await this.writePid(pid);
3084
+ await this.waitForStartupMetadata(pid);
2963
3085
  return { state: "started", pid };
2964
3086
  }
2965
3087
  async stop() {
@@ -2994,6 +3116,21 @@ class DaemonController {
2994
3116
  await rm2(this.paths.pidFile, { force: true });
2995
3117
  await this.statusStore.clear();
2996
3118
  }
3119
+ async waitForStartupMetadata(pid) {
3120
+ const deadline = Date.now() + this.startupTimeoutMs;
3121
+ while (Date.now() < deadline) {
3122
+ const status = await this.statusStore.load();
3123
+ if (status?.pid === pid) {
3124
+ return;
3125
+ }
3126
+ if (!this.deps.isProcessRunning(pid)) {
3127
+ await this.clearRuntimeFiles();
3128
+ throw new Error(`weacpx daemon exited before reporting ready state (pid ${pid})`);
3129
+ }
3130
+ await this.onStartupPoll();
3131
+ }
3132
+ throw new Error(`weacpx daemon did not report ready state within ${this.startupTimeoutMs}ms (pid ${pid})`);
3133
+ }
2997
3134
  }
2998
3135
 
2999
3136
  // src/daemon/create-daemon-controller.ts
@@ -3100,6 +3237,7 @@ async function spawnWindowsHiddenProcess(request) {
3100
3237
  return;
3101
3238
  }
3102
3239
  settled = true;
3240
+ child.stdout?.destroy();
3103
3241
  child.unref();
3104
3242
  resolve(pid);
3105
3243
  });
@@ -3315,7 +3453,7 @@ function createDefaultController() {
3315
3453
  });
3316
3454
  }
3317
3455
  function requireHome() {
3318
- const home = process.env.HOME ?? homedir2();
3456
+ const home = process.env.HOME ?? homedir3();
3319
3457
  if (!home) {
3320
3458
  throw new Error("Unable to resolve the current user home directory");
3321
3459
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "weacpx",
3
3
  "description": "用微信远程控制 `acpx` 会话的控制台, 底层基于 `weixin-agent-sdk` 与 `acpx`",
4
- "version": "0.1.2",
4
+ "version": "0.1.3",
5
5
  "main": "index.js",
6
6
  "directories": {
7
7
  "doc": "docs",