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.
- package/dist/bridge/bridge-main.js +125 -5
- package/dist/cli.js +230 -92
- package/package.json +1 -1
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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(() =>
|
|
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
|
-
|
|
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
|
|
2602
|
-
return { text:
|
|
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 ??
|
|
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
|
|
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 ??
|
|
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
|
}
|