@mearl/daemon-core 3.0.0-beta.9 → 3.0.0

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 (3) hide show
  1. package/README.en.md +102 -0
  2. package/README.md +2 -0
  3. package/package.json +3 -2
package/README.en.md ADDED
@@ -0,0 +1,102 @@
1
+ # @mearl/daemon-core
2
+
3
+ [简体中文](README.md) | [English](README.en.md)
4
+
5
+ Daemon lifecycle primitives shared by Mearl's Node packages: process liveness, JSON registration records, log rotation, and a detached daemon manager.
6
+
7
+ `cloud-server`, `cloud-connector`, and `native-host` share daemon operations (`isAlive`, atomic registry reads and writes, log rotation, and keyed paths) here so all readers and writers agree on **record locations and formats**.
8
+
9
+ This Node-only package can be used as a normal dependency or inlined into an execution host bundle.
10
+
11
+ ## API
12
+
13
+ ### Process liveness
14
+
15
+ ```typescript
16
+ isAlive(pid: number | undefined | null): boolean
17
+ isProcessGroupAlive(group: number): boolean
18
+ ```
19
+
20
+ Uses signal-0 to probe liveness. `EPERM` counts as alive: the process exists but belongs to another user. On Linux, `/proc/<pid>/stat` identifies exited, unreaped zombie and dead processes as stopped. If other threads remain after the main thread exits, or status cannot be read, it conservatively treats the process as alive.
21
+
22
+ POSIX process-group checks include descendants surviving the root process. Linux confirms execution has stopped only when two checks find the same exited members and creation identities. Active descendants, membership changes, permission errors, or read failures leave cleanup unconfirmed. Windows does not use POSIX process-group checks.
23
+
24
+ ### Path derivation (multiple instances)
25
+
26
+ ```typescript
27
+ DAEMON_HOME // ~/.mearl
28
+ hashKey(key: string): string // First 16 hexadecimal digits of sha256(key)
29
+ keyedDir(subdir: string): string // ~/.mearl/<subdir>
30
+ keyedConfigFile(subdir, key): string // ~/.mearl/<subdir>/<hash>.json
31
+ keyedLogFile(subdir, key): string // ~/.mearl/<subdir>/<hash>.log
32
+ ```
33
+
34
+ Multi-instance subsystems, such as connectors keyed by `serverUrl`, derive per-instance paths with these helpers. **All readers and writers must use them** to agree on record locations.
35
+
36
+ ### Record registry
37
+
38
+ ```typescript
39
+ interface DaemonRecord { pid: number; startedAt?: string; [k: string]: unknown }
40
+
41
+ ensureDir(dir: string): void
42
+ writeRecord(file: string, record: DaemonRecord): void // Cross-process lock; atomic temporary file + rename
43
+ claimRecord(file: string, record: DaemonRecord): boolean // Atomically claim only if absent
44
+ readRecord<T>(file: string): T | null
45
+ removeRecord(file: string): void
46
+ removeRecordIfPid(file: string, pid: number): boolean // Remove only if still owned by this PID
47
+ writeRecordIfPid(file: string, pid: number, record: DaemonRecord): boolean
48
+ removeInvalidRecord(file: string): boolean // Clean up unparseable records
49
+ listRecordFiles(dir: string): string[] // Absolute paths of all *.json files
50
+ readLiveRecords<T>(dir: string): T[] // Read live records and clean up dead ones
51
+ ```
52
+
53
+ ### Daemon manager
54
+
55
+ ```typescript
56
+ createDaemon(opts: CreateDaemonOptions): Daemon
57
+ ```
58
+
59
+ Builds a single-instance daemon manager around `(configFile, logFile)`. `start()` atomically claims a startup lock,
60
+ launches the consumer CLI detached with `--foreground`, and immediately writes a `starting` record
61
+ with the child PID. The ready child overwrites the record, allowing the parent to confirm startup. Concurrent `start()` calls reuse a starting
62
+ or running child. Multi-instance subsystems derive their own `configFile` / `logFile` from the instance key.
63
+
64
+ ```typescript
65
+ interface CreateDaemonOptions {
66
+ name: string; // Log label, e.g. 'CloudServer' / 'CloudConnector'
67
+ scriptPath: string; // Absolute path to the consumer's bundled cli.js
68
+ configFile: string; // Instance JSON record written by the foreground child
69
+ logFile: string; // Instance log file
70
+ foregroundArgs?: string[]; // argv appended after `--foreground`
71
+ readyTimeoutMs?: number; // Child record timeout, default 15000ms
72
+ formatInfo?: (record: DaemonRecord) => string | null; // Additional startup information
73
+ startingRecord?: (pid: number, startedAt: string) => DaemonRecord;
74
+ }
75
+
76
+ interface Daemon {
77
+ start(): Promise<void>;
78
+ stop(expectedPid?: number): Promise<void>; // Stop only the matching instance when PID is supplied
79
+ restart(): Promise<void>;
80
+ status(): void;
81
+ logs(lines?: number): void;
82
+ running(): DaemonRecord | null;
83
+ }
84
+ ```
85
+
86
+ > `scriptPath` must identify the consumer's own `cli.js`. Derive it with `fileURLToPath(import.meta.url)` in a module bundled into the CLI entry. If daemon-core is resolved separately, its `import.meta.url` points to daemon-core, not the CLI.
87
+
88
+ ### Logs
89
+
90
+ ```typescript
91
+ rotateLogIfNeeded(logFile: string, maxBytes?: number): void // Rotate to <log>.1 above limit; default 5MB
92
+ tailFile(logFile: string, lines: number): string
93
+ ```
94
+
95
+ ## Consumers
96
+
97
+ - **cloud-server** — `createDaemon`, single instance at `~/.mearl/cloud-server.json`.
98
+ - **cloud-connector** — `createDaemon`, instances keyed by `serverUrl` at `~/.mearl/connectors/<hash>.json`, listed through `readLiveRecords`.
99
+ - **native-host** — Uses primitives such as `isAlive` / `keyedConfigFile` / `readRecord` / `readLiveRecords` to discover, take over, and list connectors through their shared registry.
100
+
101
+
102
+ `NodeCapabilityRegistry` from `@mearl/daemon-core/capabilities` accepts `hooks` in its third constructor argument to load only modules referenced by those bindings. The whole selection is still validated, target module bytes are digest-verified, and commits still check the current selection. Ignored modules do not execute. This partial instance suits hosts needing a few native services, such as the installer CLI; it does not replace full Native Host activation and readiness receipts. Omit `hooks` for full loading.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @mearl/daemon-core
2
2
 
3
+ [简体中文](README.md) | [English](README.en.md)
4
+
3
5
  Mearl 各 Node 包共享的守护进程生命周期原语:进程存活探测、JSON 记录注册表、日志滚动,以及一个 detached 守护进程管理器。
4
6
 
5
7
  把 `cloud-server`、`cloud-connector`、`native-host` 里原本各写一份、容易彼此漂移的 daemon 逻辑(`isAlive`、原子读写注册表、日志滚动、按 key 派生路径)收敛到这一处,确保「写方」和「读方」对**记录位置与格式**始终一致。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mearl/daemon-core",
3
- "version": "3.0.0-beta.9",
3
+ "version": "3.0.0",
4
4
  "description": "Shared Node daemon lifecycle primitives for Mearl — process liveness, JSON record registry, log rotation, and a detached daemon manager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,6 +24,7 @@
24
24
  }
25
25
  },
26
26
  "files": [
27
+ "README.en.md",
27
28
  "dist",
28
29
  "!dist/**/*.map"
29
30
  ],
@@ -43,7 +44,7 @@
43
44
  "typescript": "^5.8.3"
44
45
  },
45
46
  "dependencies": {
46
- "@mearl/preset": "3.0.0-beta.9"
47
+ "@mearl/preset": "3.0.0"
47
48
  },
48
49
  "repository": {
49
50
  "type": "git",