@nowcrew/daemon 0.6.88-beta.19 → 0.6.88-beta.20
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.
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Readable as NodeReadable } from "node:stream";
|
|
2
|
+
import { ReadableStream, WritableStream } from "node:stream/web";
|
|
3
|
+
/** Probe-only counters. Never retain/log chunks, JSON-RPC params, stderr or env values. */
|
|
4
|
+
export class AcpProbeTransport {
|
|
5
|
+
elapsed;
|
|
6
|
+
initializeRequestedMs;
|
|
7
|
+
initializeResponseMs;
|
|
8
|
+
firstStdinWriteMs;
|
|
9
|
+
firstStdoutMs;
|
|
10
|
+
stdinBytesWritten = 0;
|
|
11
|
+
stdoutBytes = 0;
|
|
12
|
+
stdoutEnded = false;
|
|
13
|
+
constructor(elapsed) {
|
|
14
|
+
this.elapsed = elapsed;
|
|
15
|
+
}
|
|
16
|
+
initializeRequested() { this.initializeRequestedMs = this.elapsed(); }
|
|
17
|
+
initializeResponse() { this.initializeResponseMs = this.elapsed(); }
|
|
18
|
+
fields() {
|
|
19
|
+
return {
|
|
20
|
+
initialize_requested_ms: this.initializeRequestedMs,
|
|
21
|
+
initialize_response_ms: this.initializeResponseMs,
|
|
22
|
+
first_stdin_write_ms: this.firstStdinWriteMs,
|
|
23
|
+
first_stdout_ms: this.firstStdoutMs,
|
|
24
|
+
stdin_bytes_written: this.stdinBytesWritten,
|
|
25
|
+
stdout_bytes: this.stdoutBytes,
|
|
26
|
+
stdout_ended: this.stdoutEnded,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** A write callback confirms acceptance by the pipe, NOT receipt by the agent. */
|
|
30
|
+
writable(stdin) {
|
|
31
|
+
return new WritableStream({
|
|
32
|
+
start: (controller) => { stdin.once("error", (error) => controller.error(error)); },
|
|
33
|
+
write: (chunk) => new Promise((resolve, reject) => {
|
|
34
|
+
stdin.write(chunk, (error) => {
|
|
35
|
+
if (error) {
|
|
36
|
+
reject(error);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
this.firstStdinWriteMs ??= this.elapsed();
|
|
40
|
+
this.stdinBytesWritten += chunk.byteLength;
|
|
41
|
+
resolve();
|
|
42
|
+
});
|
|
43
|
+
}),
|
|
44
|
+
close: () => new Promise((resolve, reject) => {
|
|
45
|
+
stdin.end((error) => error ? reject(error) : resolve());
|
|
46
|
+
}),
|
|
47
|
+
abort: () => { stdin.destroy(); },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/** Observe before NDJSON decoding, without adding a competing 'data' listener. */
|
|
51
|
+
readable(stdout) {
|
|
52
|
+
const reader = NodeReadable.toWeb(stdout).getReader();
|
|
53
|
+
return new ReadableStream({
|
|
54
|
+
pull: async (controller) => {
|
|
55
|
+
const item = await reader.read();
|
|
56
|
+
if (item.done) {
|
|
57
|
+
this.stdoutEnded = true;
|
|
58
|
+
controller.close();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const chunk = item.value;
|
|
62
|
+
this.firstStdoutMs ??= this.elapsed();
|
|
63
|
+
this.stdoutBytes += chunk.byteLength;
|
|
64
|
+
controller.enqueue(chunk);
|
|
65
|
+
},
|
|
66
|
+
cancel: (reason) => reader.cancel(reason),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AcpProbeTransport } from "./acp-probe-transport.js";
|
|
1
2
|
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { dslog } from "../slog.js";
|
|
3
4
|
import { once } from "node:events";
|
|
@@ -204,18 +205,27 @@ export async function probeKimiAcp(options, spawnProcess = spawn) {
|
|
|
204
205
|
const provider = options.provider ?? "kimi";
|
|
205
206
|
const permission = options.effectivePermission ?? "sandboxed";
|
|
206
207
|
const probeId = options.probeId ?? randomUUID();
|
|
207
|
-
const started =
|
|
208
|
+
const started = performance.now();
|
|
209
|
+
const elapsed = () => Math.round(performance.now() - started);
|
|
210
|
+
const transport = new AcpProbeTransport(elapsed);
|
|
211
|
+
let spawnConfirmedMs;
|
|
208
212
|
let reason = "initializing", protocolVersion;
|
|
209
213
|
let stderrSeen = false;
|
|
210
|
-
const fields = () => ({ probe_id: probeId, runtime: provider, duration_ms:
|
|
214
|
+
const fields = () => ({ probe_id: probeId, runtime: provider, duration_ms: elapsed(),
|
|
215
|
+
...transport.fields(), spawn_confirmed_ms: spawnConfirmedMs, child_pid: child.pid,
|
|
211
216
|
reason, protocol_version: protocolVersion, stderr_seen: stderrSeen, aborted: options.signal?.aborted ?? false });
|
|
212
217
|
dslog("execution.acp_probe_started", "ACP 探测开始", { probe_id: probeId, runtime: provider, node_version: process.version });
|
|
218
|
+
const launchEnv = options.launchEnv ?? providerEnv(provider, permission);
|
|
213
219
|
const child = spawnProcess(options.bin, options.launchArgs ?? providerArgs(provider, options.configPath), {
|
|
214
220
|
cwd: options.launchCwd ?? process.cwd(),
|
|
215
|
-
//
|
|
216
|
-
env: { ...
|
|
221
|
+
// 显式 launchEnv 也决定 PATH/HOME;不能把它与父进程 PATH 混用。
|
|
222
|
+
env: { ...launchEnv, PATH: augmentedPath(launchEnv) },
|
|
217
223
|
stdio: ["pipe", "pipe", "pipe"],
|
|
218
224
|
});
|
|
225
|
+
child.once("spawn", () => {
|
|
226
|
+
spawnConfirmedMs = elapsed();
|
|
227
|
+
dslog("execution.acp_probe_spawned", "ACP 探测子进程已启动", fields());
|
|
228
|
+
});
|
|
219
229
|
if (child.stdin === null || child.stdout === null || child.stderr === null)
|
|
220
230
|
return false;
|
|
221
231
|
child.stderr.on("data", () => { stderrSeen = true; });
|
|
@@ -228,13 +238,15 @@ export async function probeKimiAcp(options, spawnProcess = spawn) {
|
|
|
228
238
|
let timeout;
|
|
229
239
|
let onAbort;
|
|
230
240
|
try {
|
|
231
|
-
const stream = ndJsonStream(
|
|
241
|
+
const stream = ndJsonStream(transport.writable(child.stdin), transport.readable(child.stdout));
|
|
232
242
|
const connected = app.connectWith(stream, async (context) => {
|
|
243
|
+
transport.initializeRequested();
|
|
233
244
|
const initialized = await context.request(methods.agent.initialize, {
|
|
234
245
|
protocolVersion: PROTOCOL_VERSION,
|
|
235
246
|
clientCapabilities: {},
|
|
236
247
|
clientInfo: { name: "nowcrew-daemon", version: "1" },
|
|
237
248
|
});
|
|
249
|
+
transport.initializeResponse();
|
|
238
250
|
protocolVersion = initialized.protocolVersion;
|
|
239
251
|
const ready = protocolVersion === PROTOCOL_VERSION;
|
|
240
252
|
if (reason === "initializing")
|