@nowcrew/daemon 0.5.46 → 0.5.47

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.
@@ -4,6 +4,12 @@ const DaemonUpdateMessageSchema = z.object({
4
4
  updateId: z.string().uuid(),
5
5
  targetVersion: z.string().regex(/^\d+\.\d+\.\d+$/),
6
6
  }).strict();
7
+ const DaemonRestartMessageSchema = z.object({
8
+ type: z.literal("daemon:restart"),
9
+ updateId: z.string().uuid(),
10
+ targetVersion: z.string().regex(/^\d+\.\d+\.\d+$/),
11
+ }).strict();
12
+ const DaemonControlMessageSchema = z.union([DaemonUpdateMessageSchema, DaemonRestartMessageSchema]);
7
13
  export function createDaemonUpdateController(deps) {
8
14
  const handled = new Set();
9
15
  let running = null;
@@ -17,16 +23,18 @@ export function createDaemonUpdateController(deps) {
17
23
  failed(message.updateId, "ineligible");
18
24
  return;
19
25
  }
20
- const installed = await deps.install({
21
- targetVersion: message.targetVersion,
22
- packageRoot: eligibility.packageRoot,
23
- npmPrefix: eligibility.npmPrefix,
24
- onInstalling: () => {
25
- deps.sendStatus({ type: "daemon:update-status", updateId: message.updateId, status: "installing" });
26
- },
27
- });
28
- if (!installed.ok) {
29
- failed(message.updateId, installed.errorCode);
26
+ const preparation = message.type === "daemon:restart"
27
+ ? await deps.prepareRestart()
28
+ : await deps.install({
29
+ targetVersion: message.targetVersion,
30
+ packageRoot: eligibility.packageRoot,
31
+ npmPrefix: eligibility.npmPrefix,
32
+ onInstalling: () => {
33
+ deps.sendStatus({ type: "daemon:update-status", updateId: message.updateId, status: "installing" });
34
+ },
35
+ });
36
+ if (!preparation.ok) {
37
+ failed(message.updateId, preparation.errorCode);
30
38
  return;
31
39
  }
32
40
  let released = false;
@@ -34,7 +42,7 @@ export function createDaemonUpdateController(deps) {
34
42
  if (released)
35
43
  return;
36
44
  released = true;
37
- await installed.release();
45
+ await preparation.release();
38
46
  };
39
47
  deps.sendStatus({ type: "daemon:update-status", updateId: message.updateId, status: "restarting" });
40
48
  try {
@@ -66,7 +74,7 @@ export function createDaemonUpdateController(deps) {
66
74
  }
67
75
  },
68
76
  handle: async (input) => {
69
- const parsed = DaemonUpdateMessageSchema.safeParse(input);
77
+ const parsed = DaemonControlMessageSchema.safeParse(input);
70
78
  if (!parsed.success)
71
79
  return false;
72
80
  const message = parsed.data;
@@ -10,6 +10,16 @@ import { daemonInstallationLeaseHeld } from "./daemon-installation-lease.js";
10
10
  import { inspectLegacyServiceInstallation } from "./legacy-service-installation.js";
11
11
  import { inspectWindowsServiceUpdateScope } from "./windows-scheduled-task.js";
12
12
  import { assertRegistryCoversDescriptors, listManagedServiceDescriptorPaths, managedServiceIdentityMatches, readManagedServiceRegistry, serviceDescriptorSha256, } from "./managed-service-registry.js";
13
+ export async function managedDaemonCapabilities(eligibility) {
14
+ try {
15
+ return (await eligibility()).eligible
16
+ ? ["daemon_update_v1", "daemon_restart_v1"]
17
+ : [];
18
+ }
19
+ catch {
20
+ return [];
21
+ }
22
+ }
13
23
  function defaults() {
14
24
  return {
15
25
  platform: process.platform,
@@ -3,6 +3,27 @@ import { resolve } from "node:path";
3
3
  import { systemCommandRunner } from "./computer-service.js";
4
4
  import { daemonGlobalInstallation } from "./daemon-installation.js";
5
5
  const RELEASED_VERSION_RE = /^\d+\.\d+\.\d+$/;
6
+ export async function prepareDaemonRestart(input) {
7
+ const local = input.localSlots.tryAcquireExclusive();
8
+ if (local === null)
9
+ return { ok: false, errorCode: "runtime_busy" };
10
+ const host = await input.hostCoordinator.tryAcquireExclusiveExecution();
11
+ if (host === null) {
12
+ local.release();
13
+ return { ok: false, errorCode: "runtime_busy" };
14
+ }
15
+ let released = false;
16
+ return {
17
+ ok: true,
18
+ release: async () => {
19
+ if (released)
20
+ return;
21
+ released = true;
22
+ await host.release();
23
+ local.release();
24
+ },
25
+ };
26
+ }
6
27
  async function readPackageVersion(packageRoot) {
7
28
  const body = JSON.parse(await readFile(resolve(packageRoot, "package.json"), "utf8"));
8
29
  return typeof body.version === "string" ? body.version : "";
package/dist/serve.js CHANGED
@@ -32,9 +32,9 @@ import { createCompletionRetransmitter } from "./completion-retransmitter.js";
32
32
  import { createAgentMemoryBridge } from "./agent-memory/bridge.js";
33
33
  import { createRuntimeStartupGate } from "./runtime-startup-gate.js";
34
34
  import { createHostExecutionCoordinator, hostCoordinatedSlotManager, hostCoordinatedStartupGate, } from "./host-execution-coordinator.js";
35
- import { detectDaemonUpdateEligibility, } from "./daemon-update-eligibility.js";
35
+ import { detectDaemonUpdateEligibility, managedDaemonCapabilities, } from "./daemon-update-eligibility.js";
36
36
  import { createDaemonUpdateController } from "./daemon-update-controller.js";
37
- import { installExactDaemonUpdate } from "./daemon-updater.js";
37
+ import { installExactDaemonUpdate, prepareDaemonRestart } from "./daemon-updater.js";
38
38
  import { scheduleServiceRestart } from "./computer-service.js";
39
39
  import { createProjectRegistry } from "./project-skills/registry.js";
40
40
  import { createProjectSkillsController, } from "./project-skills/controller.js";
@@ -100,6 +100,10 @@ export function serve(config, opts = {}) {
100
100
  localSlots,
101
101
  hostCoordinator,
102
102
  })),
103
+ prepareRestart: opts.update?.prepareRestart ?? (() => prepareDaemonRestart({
104
+ localSlots,
105
+ hostCoordinator,
106
+ })),
103
107
  scheduleRestart: opts.update?.scheduleRestart ?? scheduleServiceRestart,
104
108
  sendStatus: (frame) => {
105
109
  try {
@@ -310,14 +314,7 @@ export function serve(config, opts = {}) {
310
314
  ...(detectInstalled ? { detectInstalled } : {}),
311
315
  // First hello must not wait for third-party handshakes; optional transports arrive in the refresh.
312
316
  detectExecutable: async (installed) => conservativeExecutionRuntimes(installed),
313
- additionalCapabilities: async () => {
314
- try {
315
- return (await updateEligibility()).eligible ? ["daemon_update_v1"] : [];
316
- }
317
- catch {
318
- return [];
319
- }
320
- },
317
+ additionalCapabilities: () => managedDaemonCapabilities(updateEligibility),
321
318
  });
322
319
  runtimeFacts = helloPromise
323
320
  .then(async (hello) => {
@@ -390,9 +387,9 @@ export function serve(config, opts = {}) {
390
387
  if (typeof decoded !== "object" || decoded === null)
391
388
  return;
392
389
  const rawType = "type" in decoded && typeof decoded.type === "string" ? decoded.type : "";
393
- if (rawType === "daemon:update") {
390
+ if (rawType === "daemon:update" || rawType === "daemon:restart") {
394
391
  void updateController.handle(decoded).catch((error) => {
395
- dslog("daemon.update_failed", "daemon 更新处理失败", {
392
+ dslog("daemon.control_failed", "daemon 控制操作处理失败", {
396
393
  level: "ERROR", error_message: error.message,
397
394
  });
398
395
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowcrew/daemon",
3
- "version": "0.5.46",
3
+ "version": "0.5.47",
4
4
  "type": "module",
5
5
  "description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
6
6
  "license": "Apache-2.0",