@kb-labs/shared-daemon 2.112.0 → 2.116.11
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 +13 -20
- package/dist/index.d.ts +31 -27
- package/dist/index.js +120 -38
- package/dist/index.js.map +1 -1
- package/package.json +17 -19
package/README.md
CHANGED
|
@@ -2,44 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
Universal process-lifecycle bootstrapper for KB Labs daemon services.
|
|
4
4
|
|
|
5
|
-
`
|
|
6
|
-
resolution, `.env` loading, platform
|
|
7
|
-
|
|
8
|
-
|
|
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 {
|
|
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
|
|
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
|
-
|
|
25
|
-
|
|
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
|
|
39
|
-
|
|
40
|
-
|
|
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 {
|
|
1
|
+
import { PlatformAssemblyHook, PlatformFailurePolicy, PlatformUiProvider, PlatformContainer, PlatformRuntime } from '@kb-labs/core-runtime';
|
|
2
|
+
import { IContextLogger } from '@kb-labs/core-platform';
|
|
3
3
|
|
|
4
|
-
interface
|
|
4
|
+
interface ServiceContext {
|
|
5
5
|
platform: PlatformContainer;
|
|
6
|
-
logger:
|
|
6
|
+
logger: IContextLogger;
|
|
7
7
|
port: number;
|
|
8
8
|
host: string;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* correctly when launched from a subdirectory.
|
|
13
|
-
*/
|
|
14
|
-
repoRoot: string;
|
|
9
|
+
runtime: PlatformRuntime;
|
|
10
|
+
platformRoot: string;
|
|
11
|
+
projectRoot: string;
|
|
15
12
|
}
|
|
16
|
-
interface
|
|
13
|
+
interface NetworkServiceConfig {
|
|
17
14
|
appId: string;
|
|
18
15
|
/**
|
|
19
|
-
* serviceId in the transport map
|
|
20
|
-
*
|
|
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
|
-
*
|
|
31
|
-
*
|
|
38
|
+
* Runs after the platform is ready. The returned teardown is always called
|
|
39
|
+
* before PlatformRuntime.shutdown().
|
|
32
40
|
*/
|
|
33
|
-
setup(ctx:
|
|
41
|
+
setup(ctx: ServiceContext): Promise<() => Promise<void>>;
|
|
34
42
|
}
|
|
35
43
|
/**
|
|
36
|
-
*
|
|
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
|
-
*
|
|
42
|
-
*
|
|
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
|
|
49
|
+
declare function runService(config: ServiceConfig): Promise<void>;
|
|
46
50
|
|
|
47
|
-
export { type
|
|
51
|
+
export { type ServiceConfig, type ServiceContext, runService };
|
package/dist/index.js
CHANGED
|
@@ -1,49 +1,131 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
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
|
|
7
|
+
const address = transport?.listenAddress?.(serviceId);
|
|
21
8
|
const netOffset = Number(process.env.KB_NET_OFFSET) || 0;
|
|
22
|
-
const port =
|
|
23
|
-
const
|
|
24
|
-
const host = config.hostEnvVar && process.env[config.hostEnvVar] ? process.env[config.hostEnvVar] :
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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 {
|
|
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":";;;;;AAiDA,eAAsB,SAAA,CACpB,QACA,iBAAA,EACe;AACf,EAAA,MAAM,QAAA,GAAW,MAAM,YAAA,CAAa,OAAA,CAAQ,KAAK,CAAA;AACjD,EAAA,eAAA,CAAgB,QAAQ,CAAA;AAOxB,EAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,cAAA,EAAgB;AAC/B,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,eAAA,IAAmB,QAAA;AAChD,IAAA,OAAA,CAAQ,GAAA,CAAI,cAAA,GAAiB,UAAA,CAAW,KAAK,CAAA,CAAE,MAAA,CAAO,QAAQ,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA,CAAE,KAAA,CAAM,GAAG,CAAC,CAAA;AAAA,EAC1F;AAEA,EAAA,MAAM,QAAA,GAAW,MAAM,iBAAA,CAAkB,MAAA,CAAO,OAAO,QAAQ,CAAA;AAC/D,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,MAAA,CAAO,KAAA,CAAM;AAAA,IACnC,WAAW,MAAA,CAAO,KAAA;AAAA,IAClB,OAAA,EAAS;AAAA,GACV,CAAA;AAWD,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,SAAA,IAAa,MAAA,CAAO,KAAA;AAC7C,EAAA,MAAM,SAAA,GAAY,QAAA,CAAS,UAAA,CAA8B,kBAAkB,CAAA;AAC3E,EAAA,MAAM,IAAA,GAAO,SAAA,EAAW,aAAA,GAAgB,SAAS,CAAA;AACjD,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAA,IAAK,CAAA;AACvD,EAAA,MAAM,IAAA,GAAO,QAAQ,MAAA,IAAU,IAAA,GAC3B,KAAK,IAAA,GAAA,CACJ,OAAA,CAAQ,IAAI,MAAA,CAAO,UAAU,IAC1B,QAAA,CAAS,OAAA,CAAQ,IAAI,MAAA,CAAO,UAAU,GAAI,EAAE,CAAA,GAC5C,OAAO,WAAA,IAAe,SAAA;AAI9B,EAAA,MAAM,QAAA,GAAW,IAAA,IAAQ,MAAA,IAAU,IAAA,GAAO,KAAK,IAAA,GAAO,MAAA;AACtD,EAAA,MAAM,IAAA,GAAQ,MAAA,CAAO,UAAA,IAAc,OAAA,CAAQ,IAAI,MAAA,CAAO,UAAU,CAAA,GAC5D,OAAA,CAAQ,IAAI,MAAA,CAAO,UAAU,CAAA,GAC5B,QAAA,IAAY,OAAO,WAAA,IAAe,SAAA;AAEvC,EAAA,MAAA,CAAO,IAAA,CAAK,GAAG,MAAA,CAAO,KAAK,cAAc,EAAE,IAAA,EAAM,MAAiC,CAAA;AAElF,EAAA,MAAM,QAAA,GAAW,MAAM,MAAA,CAAO,KAAA,CAAM,EAAE,UAAU,MAAA,EAAQ,IAAA,EAAM,IAAA,EAAM,QAAA,EAAU,CAAA;AAI9E,EAAA,IAAI,YAAA,GAAe,KAAA;AACnB,EAAA,MAAM,QAAA,GAAW,OAAO,MAAA,KAAmB;AACzC,IAAA,IAAI,YAAA,EAAc;AAChB,MAAA;AAAA,IACF;AACA,IAAA,YAAA,GAAe,IAAA;AACf,IAAA,MAAA,CAAO,KAAK,CAAA,EAAG,MAAA,CAAO,KAAK,CAAA,WAAA,EAAc,MAAM,CAAA,CAAE,CAAA;AACjD,IAAA,MAAM,QAAA,EAAS;AACf,IAAA,MAAM,SAAS,QAAA,EAAS;AACxB,IAAA,MAAA,CAAO,IAAA,CAAK,CAAA,EAAG,MAAA,CAAO,KAAK,CAAA,SAAA,CAAW,CAAA;AACtC,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB,CAAA;AAEA,EAAA,OAAA,CAAQ,EAAA,CAAG,WAAW,YAAY;AAAE,IAAA,MAAM,SAAS,SAAS,CAAA;AAAA,EAAG,CAAC,CAAA;AAChE,EAAA,OAAA,CAAQ,EAAA,CAAG,UAAU,YAAY;AAAE,IAAA,MAAM,SAAS,QAAQ,CAAA;AAAA,EAAG,CAAC,CAAA;AAChE","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.
|
|
4
|
+
"version": "2.116.11",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -15,36 +15,34 @@
|
|
|
15
15
|
"dist",
|
|
16
16
|
"README.md"
|
|
17
17
|
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build": "tsup --config tsup.config.ts",
|
|
20
|
-
"clean": "rimraf dist",
|
|
21
|
-
"dev": "tsup --config tsup.config.ts --watch",
|
|
22
|
-
"lint": "eslint src",
|
|
23
|
-
"lint:fix": "eslint src --fix",
|
|
24
|
-
"test": "vitest run --passWithNoTests",
|
|
25
|
-
"test:watch": "vitest",
|
|
26
|
-
"type-check": "tsc --noEmit"
|
|
27
|
-
},
|
|
28
18
|
"dependencies": {
|
|
29
|
-
"@kb-labs/core-platform": "
|
|
30
|
-
"@kb-labs/core-runtime": "
|
|
31
|
-
"@kb-labs/core-sys": "^2.112.0"
|
|
19
|
+
"@kb-labs/core-platform": "2.116.11",
|
|
20
|
+
"@kb-labs/core-runtime": "2.116.11"
|
|
32
21
|
},
|
|
33
22
|
"devDependencies": {
|
|
34
|
-
"@kb-labs/devkit": "workspace:*",
|
|
35
23
|
"@types/node": "^24.3.3",
|
|
36
24
|
"rimraf": "^6.0.1",
|
|
37
25
|
"tsup": "^8.5.0",
|
|
38
26
|
"typescript": "^5.6.3",
|
|
39
|
-
"vitest": "^3.2.6"
|
|
27
|
+
"vitest": "^3.2.6",
|
|
28
|
+
"@kb-labs/devkit": "2.116.11"
|
|
40
29
|
},
|
|
41
30
|
"engines": {
|
|
42
31
|
"node": ">=22.0.0",
|
|
43
32
|
"pnpm": ">=9.0.0"
|
|
44
33
|
},
|
|
45
|
-
"packageManager": "pnpm@9.11.0",
|
|
46
34
|
"publishConfig": {
|
|
47
35
|
"access": "public"
|
|
48
36
|
},
|
|
49
|
-
"sideEffects": false
|
|
50
|
-
|
|
37
|
+
"sideEffects": false,
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsup --config tsup.config.ts",
|
|
40
|
+
"clean": "rimraf dist",
|
|
41
|
+
"dev": "tsup --config tsup.config.ts --watch",
|
|
42
|
+
"lint": "eslint src",
|
|
43
|
+
"lint:fix": "eslint src --fix",
|
|
44
|
+
"test": "vitest run --passWithNoTests",
|
|
45
|
+
"test:watch": "vitest",
|
|
46
|
+
"type-check": "tsc --noEmit"
|
|
47
|
+
}
|
|
48
|
+
}
|