@nowcrew/daemon 0.6.29 → 0.6.31

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.
Files changed (40) hide show
  1. package/dist/agent-ability/materializer.js +4 -4
  2. package/dist/config.js +3 -3
  3. package/dist/execution-runner.js +17 -13
  4. package/dist/host-execution-coordinator.js +169 -52
  5. package/dist/local-executor.js +107 -34
  6. package/dist/project-skills/reconciler.js +5 -2
  7. package/dist/remote/claude-bridge.js +558 -0
  8. package/dist/remote/claude-channel.js +164 -0
  9. package/dist/remote/codex-client.js +451 -0
  10. package/dist/remote/codex-runtime.js +77 -0
  11. package/dist/remote/config.js +135 -0
  12. package/dist/remote/gateway.js +879 -0
  13. package/dist/remote/identity.js +39 -0
  14. package/dist/remote/owner.js +77 -0
  15. package/dist/remote/protocol.js +211 -0
  16. package/dist/remote/remote-cli.js +254 -0
  17. package/dist/remote/runtime-probe.js +182 -0
  18. package/dist/remote/session-discovery.js +249 -0
  19. package/dist/remote/wrapper.js +40 -0
  20. package/dist/remote-web/assets/index-B_6VM_tw.js +94 -0
  21. package/dist/remote-web/assets/index-L6EiQbJn.css +1 -0
  22. package/dist/remote-web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  23. package/dist/remote-web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  24. package/dist/remote-web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  25. package/dist/remote-web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  26. package/dist/remote-web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  27. package/dist/remote-web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  28. package/dist/remote-web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  29. package/dist/remote-web/icons/nowwork-192.png +0 -0
  30. package/dist/remote-web/icons/nowwork-512.png +0 -0
  31. package/dist/remote-web/icons/nowwork.svg +7 -0
  32. package/dist/remote-web/index.html +20 -0
  33. package/dist/remote-web/manifest.webmanifest +13 -0
  34. package/dist/remote-web/sw.js +12 -0
  35. package/dist/runner.js +6 -1
  36. package/dist/runtime-startup-gate.js +47 -23
  37. package/dist/runtimes/codex-app-server-runner.js +6 -0
  38. package/dist/serve.js +55 -3
  39. package/dist/shared-execution-slots.js +75 -16
  40. package/package.json +1 -1
package/dist/serve.js CHANGED
@@ -80,7 +80,11 @@ export function serve(config, opts = {}) {
80
80
  let runtimeFacts = null;
81
81
  const runtimeProbe = createRuntimeProbeCoordinator();
82
82
  const hostCoordinator = opts.execution?.hostCoordinator
83
- ?? createHostExecutionCoordinator({ executionSlots: config.executionLimits.maxParallelTotal });
83
+ ?? createHostExecutionCoordinator({
84
+ executionSlots: config.executionLimits.maxParallelTotal,
85
+ startupSlots: config.executionLimits.maxStartingTotal,
86
+ startupGapMs: config.executionLimits.startupGapMs,
87
+ });
84
88
  const localSlots = createSharedSlotManager(config.executionLimits);
85
89
  const sharedSlots = hostCoordinatedSlotManager(localSlots, hostCoordinator);
86
90
  const runtimeStartupGate = hostCoordinatedStartupGate(createRuntimeStartupGate(config.executionLimits), hostCoordinator);
@@ -502,7 +506,9 @@ export function serve(config, opts = {}) {
502
506
  task_key: spec.workspace.taskKey,
503
507
  });
504
508
  }
505
- const executionClass = spec.context.scheduledRunId === undefined ? "normal" : "scheduled";
509
+ const executionClass = pruneTraceId !== null
510
+ ? "memory_prune"
511
+ : spec.context.scheduledRunId === undefined ? "normal" : "scheduled";
506
512
  const reservation = sharedSlots.reserve(spec.agent.handle, "execution", executionClass);
507
513
  if (!reservation.accepted) {
508
514
  dslog("execution.machine_queue_rejected", "机器执行队列已满", {
@@ -604,19 +610,49 @@ export function serve(config, opts = {}) {
604
610
  report: reportExecutionFrame,
605
611
  startupGate: runtimeStartupGate,
606
612
  startupTimeoutMs: config.executionLimits.startupTimeoutMs,
613
+ onRuntimePhase: (phase) => {
614
+ if (phase === "starting")
615
+ reservation.markStarting();
616
+ else
617
+ reservation.markRunning();
618
+ const snapshot = sharedSlots.snapshot();
619
+ dslog(`execution.runtime_${phase}`, `execution runtime ${phase}`, {
620
+ execution_id: spec.executionId,
621
+ agent_handle: spec.agent.handle,
622
+ execution_class: executionClass,
623
+ admitted_total: snapshot.admittedTotal,
624
+ preparing_total: snapshot.preparingTotal,
625
+ starting_total: snapshot.startingTotal,
626
+ running_total: snapshot.runningTotal,
627
+ queued_total: snapshot.queuedTotal,
628
+ memory_prune_preparing_total: snapshot.memoryPrunePreparingTotal,
629
+ memory_prune_starting_total: snapshot.memoryPruneStartingTotal,
630
+ memory_prune_running_total: snapshot.memoryPruneRunningTotal,
631
+ });
632
+ },
607
633
  ...(reservation.state === undefined ? {} : {
608
634
  slot: {
609
635
  state: reservation.state,
610
636
  ready: reservation.ready.then(() => {
611
637
  markExecutionTaskKeyActive();
638
+ reservation.markPreparing();
612
639
  const snapshot = sharedSlots.snapshot();
613
640
  dslog("execution.machine_slot_ready", "execution 获得机器执行名额", {
614
641
  execution_id: spec.executionId,
615
642
  agent_handle: spec.agent.handle,
616
643
  execution_class: executionClass,
617
644
  queue_ms: Date.now() - machineQueueEnteredAt,
618
- active_total: snapshot.activeTotal,
645
+ active_total: snapshot.runningTotal,
646
+ admitted_total: snapshot.admittedTotal,
647
+ preparing_total: snapshot.preparingTotal,
648
+ starting_total: snapshot.startingTotal,
649
+ running_total: snapshot.runningTotal,
619
650
  queued_total: snapshot.queuedTotal,
651
+ memory_prune_admitted_total: snapshot.memoryPruneAdmittedTotal,
652
+ memory_prune_queued_total: snapshot.memoryPruneQueuedTotal,
653
+ memory_prune_preparing_total: snapshot.memoryPrunePreparingTotal,
654
+ memory_prune_starting_total: snapshot.memoryPruneStartingTotal,
655
+ memory_prune_running_total: snapshot.memoryPruneRunningTotal,
620
656
  });
621
657
  }),
622
658
  },
@@ -846,6 +882,7 @@ export function serve(config, opts = {}) {
846
882
  });
847
883
  }
848
884
  await awaitWithCancellation(legacyReservation.ready, controller.cancellation);
885
+ legacyReservation.markPreparing();
849
886
  const queueMs = Date.now() - queueWaitStart;
850
887
  const machineSnapshot = sharedSlots.snapshot();
851
888
  const threadLabel = threadId ?? null;
@@ -967,6 +1004,21 @@ export function serve(config, opts = {}) {
967
1004
  cancellation: controller.cancellation,
968
1005
  startupGate: runtimeStartupGate,
969
1006
  startupTimeoutMs: config.executionLimits.startupTimeoutMs,
1007
+ onRuntimePhase: (phase) => {
1008
+ if (phase === "starting")
1009
+ legacyReservation?.markStarting();
1010
+ else
1011
+ legacyReservation?.markRunning();
1012
+ const snapshot = sharedSlots.snapshot();
1013
+ dslog(`run.runtime_${phase}`, `legacy runtime ${phase}`, {
1014
+ ...runKeys,
1015
+ admitted_total: snapshot.admittedTotal,
1016
+ preparing_total: snapshot.preparingTotal,
1017
+ starting_total: snapshot.startingTotal,
1018
+ running_total: snapshot.runningTotal,
1019
+ queued_total: snapshot.queuedTotal,
1020
+ });
1021
+ },
970
1022
  });
971
1023
  });
972
1024
  const result = mergeRunAgentResults(guarded.results);
@@ -1,25 +1,44 @@
1
1
  export function createSharedSlotManager(limits) {
2
2
  const activeByHandleAndClass = new Map();
3
3
  const queue = [];
4
+ const entries = new Set();
4
5
  let activeTotal = 0;
6
+ let activeMemoryPruneTotal = 0;
5
7
  let exclusive = false;
6
- const queuedFor = (handle) => queue.filter((entry) => !entry.released && entry.handle === handle).length;
8
+ const queuedFor = (handle, executionClass) => queue.filter((entry) => !entry.released
9
+ && entry.handle === handle
10
+ && (entry.executionClass === "memory_prune") === (executionClass === "memory_prune")).length;
7
11
  const activeKey = (handle, executionClass) => `${handle}:${executionClass}`;
8
12
  const maxParallelFor = (executionClass) => executionClass === "scheduled"
9
13
  ? limits.maxParallelScheduledPerAgent
10
- : limits.maxParallelPerAgent;
14
+ : executionClass === "memory_prune"
15
+ ? 1
16
+ : limits.maxParallelPerAgent;
11
17
  const promote = () => {
12
- while (activeTotal < limits.maxParallelTotal) {
13
- const index = queue.findIndex((entry) => !entry.released
14
- && (activeByHandleAndClass.get(activeKey(entry.handle, entry.executionClass)) ?? 0)
15
- < maxParallelFor(entry.executionClass));
18
+ while (true) {
19
+ const foregroundIndex = activeTotal < limits.maxParallelTotal
20
+ ? queue.findIndex((entry) => !entry.released
21
+ && entry.executionClass !== "memory_prune"
22
+ && (activeByHandleAndClass.get(activeKey(entry.handle, entry.executionClass)) ?? 0)
23
+ < maxParallelFor(entry.executionClass))
24
+ : -1;
25
+ const memoryPruneIndex = activeMemoryPruneTotal < 1
26
+ ? queue.findIndex((entry) => !entry.released
27
+ && entry.executionClass === "memory_prune"
28
+ && (activeByHandleAndClass.get(activeKey(entry.handle, entry.executionClass)) ?? 0) < 1)
29
+ : -1;
30
+ const index = foregroundIndex >= 0 ? foregroundIndex : memoryPruneIndex;
16
31
  if (index < 0)
17
32
  return;
18
33
  const [next] = queue.splice(index, 1);
19
34
  if (!next || next.released)
20
35
  continue;
21
36
  next.promoted = true;
22
- activeTotal += 1;
37
+ next.phase = "admitted";
38
+ if (next.executionClass === "memory_prune")
39
+ activeMemoryPruneTotal += 1;
40
+ else
41
+ activeTotal += 1;
23
42
  const key = activeKey(next.handle, next.executionClass);
24
43
  activeByHandleAndClass.set(key, (activeByHandleAndClass.get(key) ?? 0) + 1);
25
44
  next.resolve();
@@ -29,12 +48,13 @@ export function createSharedSlotManager(limits) {
29
48
  reserve: (handle, kind, executionClass = "normal") => {
30
49
  const key = activeKey(handle, executionClass);
31
50
  const activeForAgent = activeByHandleAndClass.get(key) ?? 0;
32
- const queuedForAgent = queuedFor(handle);
51
+ const queuedForAgent = queuedFor(handle, executionClass);
52
+ const laneQueuedTotal = queue.filter((entry) => (entry.executionClass === "memory_prune") === (executionClass === "memory_prune")).length;
33
53
  const facts = {
34
54
  activeForAgent,
35
55
  queuedForAgent,
36
- activeTotal,
37
- queuedTotal: queue.length,
56
+ activeTotal: executionClass === "memory_prune" ? activeMemoryPruneTotal : activeTotal,
57
+ queuedTotal: laneQueuedTotal,
38
58
  };
39
59
  if (exclusive) {
40
60
  return {
@@ -42,19 +62,27 @@ export function createSharedSlotManager(limits) {
42
62
  facts,
43
63
  ready: Promise.resolve(),
44
64
  isQueued: () => false,
65
+ markPreparing: () => { },
66
+ markStarting: () => { },
67
+ markRunning: () => { },
45
68
  release: () => { },
46
69
  };
47
70
  }
48
- const canStartImmediately = activeTotal < limits.maxParallelTotal
71
+ const canStartImmediately = (executionClass === "memory_prune"
72
+ ? activeMemoryPruneTotal < 1
73
+ : activeTotal < limits.maxParallelTotal)
49
74
  && activeForAgent < maxParallelFor(executionClass)
50
- && queue.length === 0;
75
+ && laneQueuedTotal === 0;
51
76
  if (kind === "execution" && !canStartImmediately && (queuedForAgent >= limits.maxQueuedPerAgent
52
- || queue.length >= limits.maxQueuedTotal)) {
77
+ || laneQueuedTotal >= limits.maxQueuedTotal)) {
53
78
  return {
54
79
  accepted: false,
55
80
  facts,
56
81
  ready: Promise.resolve(),
57
82
  isQueued: () => false,
83
+ markPreparing: () => { },
84
+ markStarting: () => { },
85
+ markRunning: () => { },
58
86
  release: () => { },
59
87
  };
60
88
  }
@@ -65,22 +93,36 @@ export function createSharedSlotManager(limits) {
65
93
  executionClass,
66
94
  released: false,
67
95
  promoted: false,
96
+ phase: "queued",
68
97
  resolve: resolveReady,
69
98
  };
70
99
  queue.push(entry);
100
+ entries.add(entry);
71
101
  promote();
102
+ const markPhase = (phase) => {
103
+ if (!entry.released && entry.promoted)
104
+ entry.phase = phase;
105
+ };
72
106
  return {
73
107
  accepted: true,
74
108
  facts,
75
109
  state: entry.promoted ? "ready" : "queued",
76
110
  ready,
77
111
  isQueued: () => !entry.promoted && !entry.released,
112
+ markPreparing: () => markPhase("preparing"),
113
+ markStarting: () => markPhase("starting"),
114
+ markRunning: () => markPhase("running"),
78
115
  release: () => {
79
116
  if (entry.released)
80
117
  return;
81
118
  entry.released = true;
119
+ entries.delete(entry);
82
120
  if (entry.promoted) {
83
- activeTotal = Math.max(0, activeTotal - 1);
121
+ if (entry.executionClass === "memory_prune") {
122
+ activeMemoryPruneTotal = Math.max(0, activeMemoryPruneTotal - 1);
123
+ }
124
+ else
125
+ activeTotal = Math.max(0, activeTotal - 1);
84
126
  const nextForAgent = Math.max(0, (activeByHandleAndClass.get(key) ?? 1) - 1);
85
127
  if (nextForAgent === 0)
86
128
  activeByHandleAndClass.delete(key);
@@ -96,9 +138,26 @@ export function createSharedSlotManager(limits) {
96
138
  },
97
139
  };
98
140
  },
99
- snapshot: () => ({ activeTotal, queuedTotal: queue.length }),
141
+ snapshot: () => {
142
+ const foreground = [...entries].filter((entry) => entry.executionClass !== "memory_prune");
143
+ const memoryPrune = [...entries].filter((entry) => entry.executionClass === "memory_prune");
144
+ const runningTotal = foreground.filter((entry) => entry.phase === "running").length;
145
+ return {
146
+ activeTotal: runningTotal,
147
+ queuedTotal: foreground.filter((entry) => entry.phase === "queued").length,
148
+ admittedTotal: foreground.filter((entry) => entry.promoted).length,
149
+ preparingTotal: foreground.filter((entry) => entry.phase === "preparing" || entry.phase === "admitted").length,
150
+ startingTotal: foreground.filter((entry) => entry.phase === "starting").length,
151
+ runningTotal,
152
+ memoryPruneAdmittedTotal: memoryPrune.filter((entry) => entry.promoted).length,
153
+ memoryPruneQueuedTotal: memoryPrune.filter((entry) => entry.phase === "queued").length,
154
+ memoryPrunePreparingTotal: memoryPrune.filter((entry) => entry.phase === "preparing" || entry.phase === "admitted").length,
155
+ memoryPruneStartingTotal: memoryPrune.filter((entry) => entry.phase === "starting").length,
156
+ memoryPruneRunningTotal: memoryPrune.filter((entry) => entry.phase === "running").length,
157
+ };
158
+ },
100
159
  tryAcquireExclusive: () => {
101
- if (exclusive || activeTotal !== 0 || queue.length !== 0)
160
+ if (exclusive || activeTotal !== 0 || activeMemoryPruneTotal !== 0 || queue.length !== 0)
102
161
  return null;
103
162
  exclusive = true;
104
163
  let released = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowcrew/daemon",
3
- "version": "0.6.29",
3
+ "version": "0.6.31",
4
4
  "type": "module",
5
5
  "description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
6
6
  "license": "Apache-2.0",