@kb-labs/shared-daemon 2.112.0 → 2.114.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.
package/README.md CHANGED
@@ -2,44 +2,37 @@
2
2
 
3
3
  Universal process-lifecycle bootstrapper for KB Labs daemon services.
4
4
 
5
- `runDaemon()` owns the boilerplate every daemon repeats — repo-root
6
- resolution, `.env` loading, platform bootstrap, port/host resolution from
7
- env, and graceful `SIGTERM`/`SIGINT` shutdown — so each service only writes
8
- its own `setup()`.
5
+ `runService()` is the only process launcher for KB Labs services. It owns root
6
+ resolution, layered `.env` loading, platform initialisation, port/host
7
+ resolution and graceful `SIGTERM`/`SIGINT` shutdown; a service only writes its
8
+ own `setup()`.
9
9
 
10
10
  ## Usage
11
11
 
12
12
  ```ts
13
- import { runDaemon } from '@kb-labs/shared-daemon';
14
- import { createServiceBootstrap } from '@kb-labs/core-runtime';
13
+ import { runService } from '@kb-labs/shared-daemon';
15
14
  import { makeAssemblyHook } from '@kb-labs/plugin-runtime';
16
15
  import { getListenOptions } from '@kb-labs/shared-http';
17
16
 
18
- await runDaemon(
19
- {
17
+ await runService({
20
18
  appId: 'my-service',
21
19
  defaultPort: 5070,
22
20
  portEnvVar: 'MY_SERVICE_PORT',
23
21
  hostEnvVar: 'MY_SERVICE_HOST',
24
- async setup({ platform, port, host, repoRoot }) {
25
- const server = await createServer({ platform, repoRoot });
22
+ platform: { assemblyHook: makeAssemblyHook() },
23
+ async setup({ platform, port, host, projectRoot }) {
24
+ const server = await createServer({ platform, projectRoot });
26
25
  await server.listen(getListenOptions(port, host));
27
26
  return () => server.close(); // teardown — runs before platform.shutdown()
28
27
  },
29
- },
30
- // DI: keeps makeAssemblyHook (Layer 1) out of this package
31
- (appId, repoRoot) =>
32
- createServiceBootstrap({ appId, repoRoot, assemblyHook: makeAssemblyHook() }),
33
- );
28
+ });
34
29
  ```
35
30
 
36
31
  ## What it handles
37
32
 
38
- - **Env + platform** — resolves `repoRoot` via `findRepoRoot`, loads `.env`,
39
- bootstraps the platform through the injected factory.
40
- - **Socket isolation** — seeds `KB_SOCKET_HASH` (from `KB_PROJECT_ROOT ?? repoRoot`)
41
- before config interpolation so `${KB_SOCKET_HASH}` socket paths resolve, matching
42
- kb-dev's per-project hash.
33
+ - **Env + platform** — resolves platform/project roots, loads their `.env`
34
+ layers, derives shared platform environment and initialises adapters through
35
+ the declared assembly hook.
43
36
  - **Port/host** — read from the configured env vars with defaults.
44
37
  - **Shutdown** — a single re-entrant handler runs `setup()`'s teardown, then
45
38
  `platform.shutdown()`, then exits; a second signal is ignored.
package/dist/index.d.ts CHANGED
@@ -1,47 +1,51 @@
1
- import { PlatformContainer } from '@kb-labs/core-runtime';
2
- import { ILogger } from '@kb-labs/core-platform';
1
+ import { PlatformAssemblyHook, PlatformFailurePolicy, PlatformUiProvider, PlatformContainer, PlatformRuntime } from '@kb-labs/core-runtime';
2
+ import { IContextLogger } from '@kb-labs/core-platform';
3
3
 
4
- interface DaemonContext {
4
+ interface ServiceContext {
5
5
  platform: PlatformContainer;
6
- logger: ILogger;
6
+ logger: IContextLogger;
7
7
  port: number;
8
8
  host: string;
9
- /**
10
- * Repository root resolved via findRepoRoot(process.cwd()). Services should
11
- * use this (not process.cwd()) for plugin/workflow discovery so they behave
12
- * correctly when launched from a subdirectory.
13
- */
14
- repoRoot: string;
9
+ runtime: PlatformRuntime;
10
+ platformRoot: string;
11
+ projectRoot: string;
15
12
  }
16
- interface DaemonConfig {
13
+ interface NetworkServiceConfig {
17
14
  appId: string;
18
15
  /**
19
- * serviceId in the transport map (declarative network). Defaults to appId.
20
- * Set explicitly when they differ. The daemon binds the port the transport
21
- * publishes for this serviceId — keeping bind and route consistent (incl.
22
- * any KB_NET_OFFSET shift).
16
+ * serviceId in the declarative transport map. Defaults to appId.
17
+ * Edge services that are not in the map use defaultPort + KB_NET_OFFSET.
23
18
  */
24
19
  serviceId?: string;
25
20
  defaultPort: number;
26
21
  portEnvVar: string;
27
22
  defaultHost?: string;
28
23
  hostEnvVar?: string;
24
+ }
25
+ interface ServiceConfig extends NetworkServiceConfig {
26
+ /** Starting directory for project/platform root resolution. */
27
+ startDir?: string;
28
+ /** Entrypoint import.meta.url for installed-mode platform discovery. */
29
+ moduleUrl?: string;
30
+ platform: {
31
+ assemblyHook: PlatformAssemblyHook;
32
+ failurePolicy?: PlatformFailurePolicy;
33
+ loadEnv?: boolean;
34
+ storeRawConfig?: boolean;
35
+ uiProvider?: PlatformUiProvider;
36
+ };
29
37
  /**
30
- * Called after platform is ready. Returns a teardown callback.
31
- * The callback is invoked before platform.shutdown() on SIGTERM/SIGINT.
38
+ * Runs after the platform is ready. The returned teardown is always called
39
+ * before PlatformRuntime.shutdown().
32
40
  */
33
- setup(ctx: DaemonContext): Promise<() => Promise<void>>;
41
+ setup(ctx: ServiceContext): Promise<() => Promise<void>>;
34
42
  }
35
43
  /**
36
- * Universal process-lifecycle runner for KB Labs daemon services.
37
- *
38
- * Handles: env loading, platform init, port/host resolution,
39
- * setup invocation, and graceful SIGTERM/SIGINT shutdown.
44
+ * Canonical service process launcher.
40
45
  *
41
- * @param config - Service-specific configuration
42
- * @param platformBootstrap - DI factory for platform (keeps makeAssemblyHook out of this package)
43
- * Example: (appId, repoRoot) => createServiceBootstrap({ appId, repoRoot, assemblyHook: makeAssemblyHook() })
46
+ * Every service gets the same roots/env/config/platform lifecycle. Service code
47
+ * starts only inside setup(), after PlatformRuntime is ready.
44
48
  */
45
- declare function runDaemon(config: DaemonConfig, platformBootstrap: (appId: string, repoRoot: string) => Promise<PlatformContainer>): Promise<void>;
49
+ declare function runService(config: ServiceConfig): Promise<void>;
46
50
 
47
- export { type DaemonConfig, type DaemonContext, runDaemon };
51
+ export { type ServiceConfig, type ServiceContext, runService };
package/dist/index.js CHANGED
@@ -1,49 +1,131 @@
1
- import { createHash } from 'crypto';
2
- import { findRepoRoot } from '@kb-labs/core-sys';
3
- import { loadEnvFromRoot } from '@kb-labs/core-runtime';
1
+ import { launchPlatform } from '@kb-labs/core-runtime';
4
2
 
5
3
  // src/daemon.ts
6
- async function runDaemon(config, platformBootstrap) {
7
- const repoRoot = await findRepoRoot(process.cwd());
8
- loadEnvFromRoot(repoRoot);
9
- if (!process.env.KB_SOCKET_HASH) {
10
- const hashRoot = process.env.KB_PROJECT_ROOT ?? repoRoot;
11
- process.env.KB_SOCKET_HASH = createHash("md5").update(hashRoot).digest("hex").slice(0, 8);
12
- }
13
- const platform = await platformBootstrap(config.appId, repoRoot);
14
- const logger = platform.logger.child({
15
- serviceId: config.appId,
16
- service: "bootstrap"
17
- });
4
+ function resolveNetwork(config, platform) {
18
5
  const serviceId = config.serviceId ?? config.appId;
19
6
  const transport = platform.getAdapter("serviceTransport");
20
- const addr = transport?.listenAddress?.(serviceId);
7
+ const address = transport?.listenAddress?.(serviceId);
21
8
  const netOffset = Number(process.env.KB_NET_OFFSET) || 0;
22
- const port = addr && "port" in addr ? addr.port : (process.env[config.portEnvVar] ? parseInt(process.env[config.portEnvVar], 10) : config.defaultPort) + netOffset;
23
- const addrHost = addr && "host" in addr ? addr.host : void 0;
24
- const host = config.hostEnvVar && process.env[config.hostEnvVar] ? process.env[config.hostEnvVar] : addrHost ?? config.defaultHost ?? "0.0.0.0";
25
- logger.info(`${config.appId}: starting`, { port, host });
26
- const teardown = await config.setup({ platform, logger, port, host, repoRoot });
27
- let shuttingDown = false;
28
- const shutdown = async (signal) => {
29
- if (shuttingDown) {
30
- return;
31
- }
32
- shuttingDown = true;
33
- logger.warn(`${config.appId}: received ${signal}`);
34
- await teardown();
35
- await platform.shutdown();
36
- logger.info(`${config.appId}: stopped`);
37
- process.exit(0);
38
- };
39
- process.on("SIGTERM", async () => {
40
- await shutdown("SIGTERM");
9
+ const port = address && "port" in address ? address.port : (process.env[config.portEnvVar] ? parseInt(process.env[config.portEnvVar], 10) : config.defaultPort) + netOffset;
10
+ const transportHost = address && "host" in address ? address.host : void 0;
11
+ const host = config.hostEnvVar && process.env[config.hostEnvVar] ? process.env[config.hostEnvVar] : transportHost ?? config.defaultHost ?? "0.0.0.0";
12
+ return { port, host };
13
+ }
14
+ async function runManagedService(config, managed, setup) {
15
+ const { port, host } = resolveNetwork(config, managed.platform);
16
+ const logger = managed.logger.forComponent("service-bootstrap");
17
+ logger.event("info", {
18
+ event: "service.starting",
19
+ message: "Service starting",
20
+ fields: { port, host }
41
21
  });
42
- process.on("SIGINT", async () => {
43
- await shutdown("SIGINT");
22
+ let teardown;
23
+ try {
24
+ teardown = await setup({
25
+ runtime: managed.runtime,
26
+ platform: managed.platform,
27
+ logger,
28
+ port,
29
+ host,
30
+ projectRoot: managed.projectRoot,
31
+ platformRoot: managed.platformRoot
32
+ });
33
+ } catch (error) {
34
+ logger.error(
35
+ "Service setup failed",
36
+ error instanceof Error ? error : void 0,
37
+ {
38
+ event: "service.failed",
39
+ error: error instanceof Error ? error.message : String(error)
40
+ }
41
+ );
42
+ await managed.shutdown("service.setup-failed");
43
+ throw error;
44
+ }
45
+ logger.event("info", {
46
+ event: "service.ready",
47
+ message: "Service ready",
48
+ fields: { port, host, outcome: "success" }
49
+ });
50
+ let shutdownPromise;
51
+ const shutdown = (signal) => {
52
+ shutdownPromise ??= (async () => {
53
+ logger.event("info", {
54
+ event: "service.stopping",
55
+ message: "Service stopping",
56
+ fields: { signal }
57
+ });
58
+ let shutdownError;
59
+ try {
60
+ await teardown?.();
61
+ } catch (error) {
62
+ shutdownError = error;
63
+ logger.error(
64
+ "Service teardown failed",
65
+ error instanceof Error ? error : void 0,
66
+ {
67
+ event: "service.failed",
68
+ phase: "teardown"
69
+ }
70
+ );
71
+ }
72
+ try {
73
+ await managed.shutdown(`signal:${signal}`);
74
+ } catch (error) {
75
+ shutdownError ??= error;
76
+ logger.error(
77
+ "Platform shutdown failed",
78
+ error instanceof Error ? error : void 0,
79
+ {
80
+ event: "service.failed",
81
+ phase: "platform-shutdown"
82
+ }
83
+ );
84
+ }
85
+ const exitCode = shutdownError ? 1 : 0;
86
+ logger.event(shutdownError ? "error" : "info", {
87
+ event: shutdownError ? "service.failed" : "service.stopped",
88
+ message: shutdownError ? "Service stopped with errors" : "Service stopped",
89
+ fields: {
90
+ signal,
91
+ exitCode,
92
+ outcome: shutdownError ? "failure" : "success"
93
+ }
94
+ });
95
+ process.exit(exitCode);
96
+ })();
97
+ return shutdownPromise;
98
+ };
99
+ process.once("SIGTERM", () => shutdown("SIGTERM"));
100
+ process.once("SIGINT", () => shutdown("SIGINT"));
101
+ }
102
+ async function runService(config) {
103
+ const runtime = await launchPlatform({
104
+ applicationId: config.appId,
105
+ serviceId: config.serviceId ?? config.appId,
106
+ kind: "service",
107
+ startDir: config.startDir,
108
+ moduleUrl: config.moduleUrl,
109
+ assemblyHook: config.platform.assemblyHook,
110
+ failurePolicy: config.platform.failurePolicy,
111
+ loadEnv: config.platform.loadEnv,
112
+ storeRawConfig: config.platform.storeRawConfig,
113
+ uiProvider: config.platform.uiProvider
44
114
  });
115
+ await runManagedService(
116
+ config,
117
+ {
118
+ runtime,
119
+ platform: runtime.platform,
120
+ logger: runtime.logger,
121
+ projectRoot: runtime.roots.projectRoot,
122
+ platformRoot: runtime.roots.platformRoot,
123
+ shutdown: (reason) => runtime.shutdown(reason)
124
+ },
125
+ config.setup
126
+ );
45
127
  }
46
128
 
47
- export { runDaemon };
129
+ export { runService };
48
130
  //# sourceMappingURL=index.js.map
49
131
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/daemon.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { createHash } from 'node:crypto';\nimport { findRepoRoot } from '@kb-labs/core-sys';\nimport { loadEnvFromRoot } from '@kb-labs/core-runtime';\nimport type { PlatformContainer } from '@kb-labs/core-runtime';\nimport type { ILogger, IServiceTransport } from '@kb-labs/core-platform';\n\nexport interface DaemonContext {\n platform: PlatformContainer;\n logger: ILogger;\n port: number;\n host: string;\n /**\n * Repository root resolved via findRepoRoot(process.cwd()). Services should\n * use this (not process.cwd()) for plugin/workflow discovery so they behave\n * correctly when launched from a subdirectory.\n */\n repoRoot: string;\n}\n\nexport interface DaemonConfig {\n appId: string;\n /**\n * serviceId in the transport map (declarative network). Defaults to appId.\n * Set explicitly when they differ. The daemon binds the port the transport\n * publishes for this serviceId — keeping bind and route consistent (incl.\n * any KB_NET_OFFSET shift).\n */\n serviceId?: string;\n defaultPort: number;\n portEnvVar: string;\n defaultHost?: string;\n hostEnvVar?: string;\n /**\n * Called after platform is ready. Returns a teardown callback.\n * The callback is invoked before platform.shutdown() on SIGTERM/SIGINT.\n */\n setup(ctx: DaemonContext): Promise<() => Promise<void>>;\n}\n\n/**\n * Universal process-lifecycle runner for KB Labs daemon services.\n *\n * Handles: env loading, platform init, port/host resolution,\n * setup invocation, and graceful SIGTERM/SIGINT shutdown.\n *\n * @param config - Service-specific configuration\n * @param platformBootstrap - DI factory for platform (keeps makeAssemblyHook out of this package)\n * Example: (appId, repoRoot) => createServiceBootstrap({ appId, repoRoot, assemblyHook: makeAssemblyHook() })\n */\nexport async function runDaemon(\n config: DaemonConfig,\n platformBootstrap: (appId: string, repoRoot: string) => Promise<PlatformContainer>,\n): Promise<void> {\n const repoRoot = await findRepoRoot(process.cwd());\n loadEnvFromRoot(repoRoot);\n\n // Ensure KB_SOCKET_HASH is set before interpolateConfig() runs inside platformBootstrap.\n // kb-dev sets it via spawnEnv() for all managed services (including gateway, rest-api).\n // For manual starts (dev scripts, local testing), derive it the same way kb-dev does —\n // from the project dir (KB_PROJECT_ROOT), falling back to repoRoot — so a manually\n // started service lands in the same /tmp/kb-<hash>/ dir as kb-dev-managed peers.\n if (!process.env.KB_SOCKET_HASH) {\n const hashRoot = process.env.KB_PROJECT_ROOT ?? repoRoot;\n process.env.KB_SOCKET_HASH = createHash('md5').update(hashRoot).digest('hex').slice(0, 8);\n }\n\n const platform = await platformBootstrap(config.appId, repoRoot);\n const logger = platform.logger.child({\n serviceId: config.appId,\n service: 'bootstrap',\n });\n\n // Bind port from the transport (the single declarative network source): the\n // daemon listens on exactly the port the transport publishes for its\n // serviceId, so bind and route stay consistent — including any KB_NET_OFFSET\n // shift. Socket services resolve their bind via KB_SOCKET_PATH (setup →\n // getListenOptions), so listenAddress returns socketPath and we keep the\n // fallback port. Services NOT in the transport map (e.g. state-daemon — not\n // gateway-routed) are treated as edges: the fallback applies KB_NET_OFFSET\n // directly, so the one offset knob still shifts their bind. Host stays the\n // daemon's own concern (offset never affects host).\n const serviceId = config.serviceId ?? config.appId;\n const transport = platform.getAdapter<IServiceTransport>('serviceTransport');\n const addr = transport?.listenAddress?.(serviceId);\n const netOffset = Number(process.env.KB_NET_OFFSET) || 0;\n const port = addr && 'port' in addr\n ? addr.port\n : (process.env[config.portEnvVar]\n ? parseInt(process.env[config.portEnvVar]!, 10)\n : config.defaultPort) + netOffset;\n\n // Host precedence: explicit env override > transport's advisory host (set only\n // by adapters that own the bind host, e.g. k8s) > daemon default.\n const addrHost = addr && 'host' in addr ? addr.host : undefined;\n const host = (config.hostEnvVar && process.env[config.hostEnvVar])\n ? process.env[config.hostEnvVar]!\n : (addrHost ?? config.defaultHost ?? '0.0.0.0');\n\n logger.info(`${config.appId}: starting`, { port, host } as Record<string, unknown>);\n\n const teardown = await config.setup({ platform, logger, port, host, repoRoot });\n\n // Guard against a second signal (e.g. SIGTERM then Ctrl-C) re-running teardown\n // against already-closed resources.\n let shuttingDown = false;\n const shutdown = async (signal: string) => {\n if (shuttingDown) {\n return;\n }\n shuttingDown = true;\n logger.warn(`${config.appId}: received ${signal}`);\n await teardown();\n await platform.shutdown();\n logger.info(`${config.appId}: stopped`);\n process.exit(0);\n };\n\n process.on('SIGTERM', async () => { await shutdown('SIGTERM'); });\n process.on('SIGINT', async () => { await shutdown('SIGINT'); });\n}\n"]}
1
+ {"version":3,"sources":["../src/daemon.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import {\n launchPlatform,\n type PlatformAssemblyHook,\n type PlatformContainer,\n type PlatformFailurePolicy,\n type PlatformRuntime,\n type PlatformUiProvider,\n} from \"@kb-labs/core-runtime\";\nimport type { IContextLogger, IServiceTransport } from \"@kb-labs/core-platform\";\n\nexport interface ServiceContext {\n platform: PlatformContainer;\n logger: IContextLogger;\n port: number;\n host: string;\n runtime: PlatformRuntime;\n platformRoot: string;\n projectRoot: string;\n}\n\ninterface NetworkServiceConfig {\n appId: string;\n /**\n * serviceId in the declarative transport map. Defaults to appId.\n * Edge services that are not in the map use defaultPort + KB_NET_OFFSET.\n */\n serviceId?: string;\n defaultPort: number;\n portEnvVar: string;\n defaultHost?: string;\n hostEnvVar?: string;\n}\n\nexport interface ServiceConfig extends NetworkServiceConfig {\n /** Starting directory for project/platform root resolution. */\n startDir?: string;\n /** Entrypoint import.meta.url for installed-mode platform discovery. */\n moduleUrl?: string;\n platform: {\n assemblyHook: PlatformAssemblyHook;\n failurePolicy?: PlatformFailurePolicy;\n loadEnv?: boolean;\n storeRawConfig?: boolean;\n uiProvider?: PlatformUiProvider;\n };\n /**\n * Runs after the platform is ready. The returned teardown is always called\n * before PlatformRuntime.shutdown().\n */\n setup(ctx: ServiceContext): Promise<() => Promise<void>>;\n}\n\ninterface ManagedRuntime {\n platform: PlatformContainer;\n logger: IContextLogger;\n projectRoot: string;\n platformRoot: string;\n shutdown(reason?: string): Promise<void>;\n runtime: PlatformRuntime;\n}\n\nfunction resolveNetwork(\n config: NetworkServiceConfig,\n platform: PlatformContainer,\n): { port: number; host: string } {\n const serviceId = config.serviceId ?? config.appId;\n const transport = platform.getAdapter<IServiceTransport>(\"serviceTransport\");\n const address = transport?.listenAddress?.(serviceId);\n const netOffset = Number(process.env.KB_NET_OFFSET) || 0;\n\n const port =\n address && \"port\" in address\n ? address.port\n : (process.env[config.portEnvVar]\n ? parseInt(process.env[config.portEnvVar]!, 10)\n : config.defaultPort) + netOffset;\n\n const transportHost = address && \"host\" in address ? address.host : undefined;\n const host =\n config.hostEnvVar && process.env[config.hostEnvVar]\n ? process.env[config.hostEnvVar]!\n : (transportHost ?? config.defaultHost ?? \"0.0.0.0\");\n\n return { port, host };\n}\n\nasync function runManagedService(\n config: NetworkServiceConfig,\n managed: ManagedRuntime,\n setup: (context: ServiceContext) => Promise<() => Promise<void>>,\n): Promise<void> {\n const { port, host } = resolveNetwork(config, managed.platform);\n const logger = managed.logger.forComponent(\"service-bootstrap\");\n\n logger.event(\"info\", {\n event: \"service.starting\",\n message: \"Service starting\",\n fields: { port, host },\n });\n\n let teardown: (() => Promise<void>) | undefined;\n try {\n teardown = await setup({\n runtime: managed.runtime as PlatformRuntime,\n platform: managed.platform,\n logger,\n port,\n host,\n projectRoot: managed.projectRoot,\n platformRoot: managed.platformRoot,\n });\n } catch (error) {\n logger.error(\n \"Service setup failed\",\n error instanceof Error ? error : undefined,\n {\n event: \"service.failed\",\n error: error instanceof Error ? error.message : String(error),\n },\n );\n await managed.shutdown(\"service.setup-failed\");\n throw error;\n }\n\n logger.event(\"info\", {\n event: \"service.ready\",\n message: \"Service ready\",\n fields: { port, host, outcome: \"success\" },\n });\n\n let shutdownPromise: Promise<void> | undefined;\n const shutdown = (signal: string): Promise<void> => {\n shutdownPromise ??= (async () => {\n logger.event(\"info\", {\n event: \"service.stopping\",\n message: \"Service stopping\",\n fields: { signal },\n });\n let shutdownError: unknown;\n\n try {\n await teardown?.();\n } catch (error) {\n shutdownError = error;\n logger.error(\n \"Service teardown failed\",\n error instanceof Error ? error : undefined,\n {\n event: \"service.failed\",\n phase: \"teardown\",\n },\n );\n }\n\n try {\n await managed.shutdown(`signal:${signal}`);\n } catch (error) {\n shutdownError ??= error;\n logger.error(\n \"Platform shutdown failed\",\n error instanceof Error ? error : undefined,\n {\n event: \"service.failed\",\n phase: \"platform-shutdown\",\n },\n );\n }\n\n const exitCode = shutdownError ? 1 : 0;\n logger.event(shutdownError ? \"error\" : \"info\", {\n event: shutdownError ? \"service.failed\" : \"service.stopped\",\n message: shutdownError\n ? \"Service stopped with errors\"\n : \"Service stopped\",\n fields: {\n signal,\n exitCode,\n outcome: shutdownError ? \"failure\" : \"success\",\n },\n });\n process.exit(exitCode);\n })();\n return shutdownPromise;\n };\n\n process.once(\"SIGTERM\", () => shutdown(\"SIGTERM\"));\n process.once(\"SIGINT\", () => shutdown(\"SIGINT\"));\n}\n\n/**\n * Canonical service process launcher.\n *\n * Every service gets the same roots/env/config/platform lifecycle. Service code\n * starts only inside setup(), after PlatformRuntime is ready.\n */\nexport async function runService(config: ServiceConfig): Promise<void> {\n const runtime = await launchPlatform({\n applicationId: config.appId,\n serviceId: config.serviceId ?? config.appId,\n kind: \"service\",\n startDir: config.startDir,\n moduleUrl: config.moduleUrl,\n assemblyHook: config.platform.assemblyHook,\n failurePolicy: config.platform.failurePolicy,\n loadEnv: config.platform.loadEnv,\n storeRawConfig: config.platform.storeRawConfig,\n uiProvider: config.platform.uiProvider,\n });\n\n await runManagedService(\n config,\n {\n runtime,\n platform: runtime.platform,\n logger: runtime.logger,\n projectRoot: runtime.roots.projectRoot,\n platformRoot: runtime.roots.platformRoot,\n shutdown: (reason) => runtime.shutdown(reason),\n },\n config.setup,\n );\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kb-labs/shared-daemon",
3
3
  "description": "Universal process-lifecycle bootstrapper for KB Labs daemon services",
4
- "version": "2.112.0",
4
+ "version": "2.114.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -26,9 +26,8 @@
26
26
  "type-check": "tsc --noEmit"
27
27
  },
28
28
  "dependencies": {
29
- "@kb-labs/core-platform": "^2.112.0",
30
- "@kb-labs/core-runtime": "^2.112.0",
31
- "@kb-labs/core-sys": "^2.112.0"
29
+ "@kb-labs/core-platform": "^2.114.0",
30
+ "@kb-labs/core-runtime": "^2.114.0"
32
31
  },
33
32
  "devDependencies": {
34
33
  "@kb-labs/devkit": "workspace:*",