@nowcrew/daemon 0.5.13 → 0.5.15

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/serve.js CHANGED
@@ -8,7 +8,7 @@ import { randomUUID } from "node:crypto";
8
8
  import { initSlog, dslog, setSlogDefaults, drainSpool, flushSlog } from "./slog.js";
9
9
  import { mergeRunAgentResults, reportScheduledStartFailure, runAgent } from "./runner.js";
10
10
  import { buildOriginDecisionRetryPrompt, buildScheduledPrompt } from "./prompt.js";
11
- import { collectMachineHello, DAEMON_CAPABILITIES, EXECUTION_PROTOCOL } from "./machine-info.js";
11
+ import { collectMachineHello, DAEMON_CAPABILITIES, EXECUTION_PROTOCOL, } from "./machine-info.js";
12
12
  import { listWorkspace, readWorkspaceFile } from "./workspace-fs.js";
13
13
  import { listSkills } from "./skills.js";
14
14
  import { inspectRaftWorkspace, importRaftWorkspace } from "./workspace-import.js";
@@ -18,8 +18,10 @@ import { formatDaemonLogLine } from "./log-format.js";
18
18
  import { reportAgentRunComplete } from "./scheduled-run-report.js";
19
19
  import { runWithOriginDecisionGuard } from "./origin-decision.js";
20
20
  import { createExecutionJournal } from "./execution-journal.js";
21
+ import { createExecutionTelemetryJournal } from "./execution-telemetry-journal.js";
21
22
  import { ExecutionRejectedSchema, ExecutionSnapshotSchema, LegacyAgentStartSchema, ServerToDaemonExecutionFrameSchema, } from "./execution-protocol.js";
22
23
  import { hashExecutionSpec, runExecution, } from "./execution-runner.js";
24
+ import { executionBackendCapability } from "./execution-backend.js";
23
25
  // normalize.ts 的活动种类 → activity 枚举
24
26
  const ACTIVITY_MAP = {
25
27
  init: "working", text: "thinking", reading: "reading", sending: "sending",
@@ -28,7 +30,7 @@ const ACTIVITY_MAP = {
28
30
  };
29
31
  export function buildControlPlaneUrl(serverUrl, machineToken, runtimePlatform = process.platform) {
30
32
  const query = new URLSearchParams({ key: machineToken });
31
- if (runtimePlatform !== "win32") {
33
+ if (executionBackendCapability(runtimePlatform).supported) {
32
34
  query.set("execution_min", String(EXECUTION_PROTOCOL.min));
33
35
  query.set("execution_max", String(EXECUTION_PROTOCOL.max));
34
36
  }
@@ -43,8 +45,9 @@ export function serve(config, opts = {}) {
43
45
  let backoff = 1000;
44
46
  const maxBackoff = opts.maxBackoffMs ?? 30_000;
45
47
  const executionJournal = opts.execution?.journal ?? createExecutionJournal(config.agentsRoot);
48
+ const executionTelemetry = createExecutionTelemetryJournal(config.agentsRoot);
46
49
  const executeProtocol = opts.execution?.runExecution ?? runExecution;
47
- let detectedRuntimes = [];
50
+ let detectedExecutionRuntimes = [];
48
51
  let runtimeFacts = null;
49
52
  const executionReady = executionJournal.reconcileAfterRestart().then(() => true, (error) => {
50
53
  dslog("execution.recovery_failed", "execution journal 恢复失败", {
@@ -66,6 +69,26 @@ export function serve(config, opts = {}) {
66
69
  }
67
70
  catch { /* reconnect/sync replays durable lifecycle */ }
68
71
  };
72
+ const reportExecutionFrame = async (frame) => {
73
+ if (frame.type === "execution:activity" || frame.type === "execution:console") {
74
+ try {
75
+ await executionTelemetry.append(frame);
76
+ }
77
+ catch (error) {
78
+ dslog("execution.telemetry_journal_failed", "execution telemetry 持久化失败", {
79
+ level: "ERROR", execution_id: frame.executionId,
80
+ error_message: error.message,
81
+ });
82
+ throw error;
83
+ }
84
+ }
85
+ safeExecutionSend(frame);
86
+ };
87
+ const replayExecutionTelemetry = async () => {
88
+ const frames = await executionTelemetry.replay();
89
+ for (const frame of frames)
90
+ safeExecutionSend(frame);
91
+ };
69
92
  const snapshotEntry = (entry) => {
70
93
  if ((entry.state === "completed" || entry.state === "interrupted") && entry.completion !== null) {
71
94
  return { executionId: entry.executionId, state: entry.state, completion: entry.completion, updatedAt: entry.updatedAt };
@@ -238,7 +261,7 @@ export function serve(config, opts = {}) {
238
261
  void drainSpool();
239
262
  // 上报本机信息 (hostname/os/daemon 版本/已装 runtimes)
240
263
  const helloPromise = collectMachineHello(config.agentsRoot, config.executionLimits);
241
- runtimeFacts = helloPromise.then((hello) => hello.runtimes, (error) => {
264
+ runtimeFacts = helloPromise.then((hello) => hello.executionRuntimes, (error) => {
242
265
  dslog("execution.runtime_detection_failed", "runtime 探测失败", {
243
266
  level: "ERROR", error_message: error.message,
244
267
  });
@@ -246,16 +269,21 @@ export function serve(config, opts = {}) {
246
269
  });
247
270
  void helloPromise
248
271
  .then((hello) => {
249
- detectedRuntimes = hello.runtimes;
272
+ detectedExecutionRuntimes = hello.executionRuntimes;
250
273
  try {
251
274
  ws?.send(JSON.stringify(hello));
252
- log(`📤 已上报机器信息: ${hello.hostname} · ${hello.os} · runtimes=[${hello.runtimes.join(",")}] · agents=[${hello.agentHandles.join(",")}]`);
275
+ log(`📤 已上报机器信息: ${hello.hostname} · ${hello.os} · installed=[${hello.runtimes.join(",")}] · executable=[${hello.executionRuntimes.join(",")}] · agents=[${hello.agentHandles.join(",")}]`);
253
276
  }
254
277
  catch { /* 非 OPEN,忽略 */ }
255
278
  })
256
279
  .catch(() => { });
257
280
  opts.onOpen?.(ws);
258
281
  void executionReady.then((ready) => ready ? sendSnapshot(`reconnect-${randomUUID()}`) : undefined);
282
+ void replayExecutionTelemetry().catch((error) => {
283
+ dslog("execution.telemetry_replay_failed", "execution telemetry 重放失败", {
284
+ level: "ERROR", error_message: error.message,
285
+ });
286
+ });
259
287
  });
260
288
  ws.on("message", async (data) => {
261
289
  let decoded;
@@ -292,6 +320,15 @@ export function serve(config, opts = {}) {
292
320
  await executionJournal.acknowledgeCompletion(frame.executionId).catch(() => { });
293
321
  return;
294
322
  }
323
+ if (frame.type === "execution:event-ack") {
324
+ await executionTelemetry.acknowledge(frame.executionId, frame.kind, frame.seq).catch((error) => {
325
+ dslog("execution.telemetry_ack_failed", "execution telemetry ACK 处理失败", {
326
+ level: "ERROR", execution_id: frame.executionId,
327
+ error_message: error.message,
328
+ });
329
+ });
330
+ return;
331
+ }
295
332
  if (frame.type === "execution:cancel") {
296
333
  if (!knownExecutionHashes.has(frame.executionId)
297
334
  && !executionRuns.has(frame.executionId)
@@ -352,7 +389,7 @@ export function serve(config, opts = {}) {
352
389
  let availableRuntimes;
353
390
  try {
354
391
  availableRuntimes = opts.execution?.availableRuntimes?.()
355
- ?? await (runtimeFacts ?? Promise.resolve(detectedRuntimes));
392
+ ?? await (runtimeFacts ?? Promise.resolve(detectedExecutionRuntimes));
356
393
  }
357
394
  catch (error) {
358
395
  reservation.release();
@@ -373,7 +410,7 @@ export function serve(config, opts = {}) {
373
410
  availableRuntimes,
374
411
  ...reservation.facts,
375
412
  },
376
- report: safeExecutionSend,
413
+ report: reportExecutionFrame,
377
414
  ...(reservation.state === undefined ? {} : {
378
415
  slot: { state: reservation.state, ready: reservation.ready },
379
416
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowcrew/daemon",
3
- "version": "0.5.13",
3
+ "version": "0.5.15",
4
4
  "type": "module",
5
5
  "description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
6
6
  "license": "Apache-2.0",
@@ -17,10 +17,11 @@
17
17
  "access": "public"
18
18
  },
19
19
  "dependencies": {
20
+ "@agentclientprotocol/sdk": "1.2.1",
20
21
  "cross-spawn": "^7.0.6",
21
22
  "ws": "^8",
22
23
  "zod": "^3.23.0",
23
- "@nowcrew/cli": "^0.4.5"
24
+ "@nowcrew/cli": "^0.4.7"
24
25
  },
25
26
  "devDependencies": {
26
27
  "@types/cross-spawn": "^6.0.6",