@zokizuan/satori-mcp 6.2.0 → 6.3.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/dist/index.js CHANGED
@@ -8,6 +8,9 @@ const bootstrapKeepAlive = setInterval(() => {
8
8
  // The server owns steady-state lifetime after startMcpServerFromEnv resolves.
9
9
  }, 60 * 60 * 1000);
10
10
  function resolveRunMode() {
11
+ if (process.env.SATORI_RUN_MODE === "host") {
12
+ return "host";
13
+ }
11
14
  if (process.env.SATORI_RUN_MODE === "cli") {
12
15
  return "cli";
13
16
  }
@@ -60,6 +63,11 @@ async function handleShutdown(reason) {
60
63
  }
61
64
  async function main() {
62
65
  const runMode = resolveRunMode();
66
+ if (runMode === "host") {
67
+ const { startSharedRuntimeHostFromEnv } = await import("./server/shared-runtime-host.js");
68
+ activeServer = await startSharedRuntimeHostFromEnv();
69
+ return;
70
+ }
63
71
  const originalStdoutWrite = process.stdout.write.bind(process.stdout);
64
72
  const protocolStdout = createProtocolStdout(originalStdoutWrite);
65
73
  const { installBootstrapStdioSafety } = await import("./server/bootstrap-stdio.js");
@@ -86,14 +94,33 @@ process.on("SIGINT", () => {
86
94
  process.on("SIGTERM", () => {
87
95
  void handleShutdown("SIGTERM");
88
96
  });
89
- process.stdin.once("end", () => {
90
- void handleShutdown("STDIN_EOF");
91
- });
97
+ if (resolveRunMode() !== "host") {
98
+ process.stdin.once("end", () => {
99
+ void handleShutdown("STDIN_EOF");
100
+ });
101
+ }
92
102
  main().then(() => {
93
103
  clearInterval(bootstrapKeepAlive);
94
- }).catch((error) => {
104
+ }).catch(async (error) => {
95
105
  clearInterval(bootstrapKeepAlive);
96
106
  const message = error instanceof Error ? error.message : String(error);
107
+ if (resolveRunMode() === "host" && process.env.SATORI_SHARED_RUNTIME_ERROR_PATH) {
108
+ try {
109
+ const fs = await import("node:fs");
110
+ const boundedMessage = message.slice(0, 4096);
111
+ const errorPath = process.env.SATORI_SHARED_RUNTIME_ERROR_PATH;
112
+ const temporaryPath = `${errorPath}.${process.pid}.tmp`;
113
+ fs.writeFileSync(temporaryPath, `${JSON.stringify({
114
+ formatVersion: 1,
115
+ recordedAt: new Date().toISOString(),
116
+ message: boundedMessage,
117
+ })}\n`, { encoding: "utf8", mode: 0o600 });
118
+ fs.renameSync(temporaryPath, errorPath);
119
+ }
120
+ catch {
121
+ // Startup diagnostics are best-effort and must not mask the owner failure.
122
+ }
123
+ }
97
124
  if (message.includes("E_PROTOCOL_FAILURE")) {
98
125
  console.error(`E_PROTOCOL_FAILURE ${message}`);
99
126
  }
@@ -30,6 +30,7 @@ export declare class ProviderRuntime {
30
30
  private readonly mutationLeaseCoordinator;
31
31
  private readonly now;
32
32
  private readonly searchContinuationCoordinator;
33
+ private readonly onLifecycleActivityChanged?;
33
34
  private readonly generationProofCoordinator;
34
35
  private embeddingRuntimePromise;
35
36
  private vectorRuntimePromise;
@@ -48,6 +49,7 @@ export declare class ProviderRuntime {
48
49
  runtimeOwnerGate?: RuntimeOwnerMutationGate | null;
49
50
  mutationLeaseCoordinator?: MutationLeaseCoordinator;
50
51
  searchContinuationCoordinator?: SearchContinuationCoordinator;
52
+ onLifecycleActivityChanged?: () => void;
51
53
  now?: () => number;
52
54
  });
53
55
  validate(operation: ProviderBackedOperation): MissingProviderConfigIssue | null;
@@ -58,6 +60,7 @@ export declare class ProviderRuntime {
58
60
  private createVectorBackend;
59
61
  private createReranker;
60
62
  private createSyncCompletionHook;
63
+ getActiveLifecycleOperationCount(): number;
61
64
  shutdown(): Promise<void>;
62
65
  }
63
66
  //# sourceMappingURL=provider-runtime.d.ts.map
@@ -1,9 +1,12 @@
1
1
  import { Context, createGenerationProofCoordinator, Embedding, MilvusVectorDatabase, VoyageAIReranker, } from "@zokizuan/satori-core";
2
+ import fs from "node:fs";
3
+ import { createRequire } from "node:module";
2
4
  import { SearchContinuationCoordinator, ToolHandlers, } from "../core/handlers.js";
3
5
  import { MutationLeaseCoordinator } from "../core/mutation-lease.js";
4
6
  import { SyncManager } from "../core/sync.js";
5
7
  import { resolveConfiguredEmbeddingDimension, } from "../config.js";
6
8
  import { createEmbeddingInstance, logEmbeddingProviderInfo } from "../embedding.js";
9
+ const requireFromProviderRuntime = createRequire(import.meta.url);
7
10
  export async function startProviderSyncLifecycle(syncManager, options) {
8
11
  // Incremental synchronization may embed changed files, so the
9
12
  // metadata-only vector runtime must never own periodic or watcher work.
@@ -133,6 +136,7 @@ export class ProviderRuntime {
133
136
  this.mutationLeaseCoordinator = args.mutationLeaseCoordinator || new MutationLeaseCoordinator();
134
137
  this.searchContinuationCoordinator = args.searchContinuationCoordinator
135
138
  ?? new SearchContinuationCoordinator();
139
+ this.onLifecycleActivityChanged = args.onLifecycleActivityChanged;
136
140
  this.now = args.now || (() => Date.now());
137
141
  }
138
142
  validate(operation) {
@@ -214,6 +218,7 @@ export class ProviderRuntime {
214
218
  watchDebounceMs: this.watchDebounceMs,
215
219
  onSyncCompleted: this.createSyncCompletionHook(context),
216
220
  mutationLeaseCoordinator: this.mutationLeaseCoordinator,
221
+ onLifecycleActivityChanged: this.onLifecycleActivityChanged,
217
222
  });
218
223
  const reranker = this.createReranker(bootstrap);
219
224
  if (reranker) {
@@ -300,7 +305,20 @@ export class ProviderRuntime {
300
305
  switch (bootstrap.vectorBackend.kind) {
301
306
  case 'lancedb': {
302
307
  const moduleSpecifier = '@zokizuan/satori-core/lancedb';
303
- const { LanceDbVectorDatabase } = await import(moduleSpecifier);
308
+ // Core deliberately publishes this native boundary as CommonJS.
309
+ // Requiring it lazily avoids Node's synthetic ESM named-export
310
+ // snapshot, which can observe getter-backed exports as undefined
311
+ // in a detached compiled host.
312
+ let LanceDbVectorDatabase;
313
+ try {
314
+ const resolvedModule = fs.realpathSync(requireFromProviderRuntime.resolve(moduleSpecifier));
315
+ const requireFromResolvedModule = createRequire(resolvedModule);
316
+ ({ LanceDbVectorDatabase } = requireFromResolvedModule(resolvedModule));
317
+ }
318
+ catch (error) {
319
+ throw new Error(`${error instanceof Error ? error.message : String(error)} `
320
+ + `(resolved through ${moduleSpecifier})`);
321
+ }
304
322
  return new LanceDbVectorDatabase({
305
323
  databasePath: bootstrap.vectorBackend.databasePath,
306
324
  });
@@ -331,12 +349,14 @@ export class ProviderRuntime {
331
349
  }
332
350
  };
333
351
  }
352
+ getActiveLifecycleOperationCount() {
353
+ return this.activeContexts.reduce((count, toolContext) => count + toolContext.syncManager.getActiveLifecycleOperationCount(), 0);
354
+ }
334
355
  async shutdown() {
335
356
  await Promise.all([
336
357
  Promise.all(this.activeContexts.map(async (toolContext) => {
337
358
  toolContext.toolHandlers.releaseSearchContinuationOwnership();
338
- toolContext.syncManager.stopBackgroundSync();
339
- await toolContext.syncManager.stopWatcherMode();
359
+ await toolContext.syncManager.stopAndDrainLifecycle();
340
360
  await toolContext.context.getVectorStore().close?.();
341
361
  })),
342
362
  Promise.all([...this.activeEmbeddings].map((embedding) => embedding.close())),
@@ -0,0 +1,11 @@
1
+ import type { Readable, Writable } from "node:stream";
2
+ export type SharedRuntimeClientOptions = Readonly<{
3
+ runtimeEntry: string;
4
+ env: NodeJS.ProcessEnv;
5
+ stdin?: Readable;
6
+ stdout?: Writable;
7
+ stderr?: Writable;
8
+ attachTimeoutMs?: number;
9
+ }>;
10
+ export declare function runSharedRuntimeClient(options: SharedRuntimeClientOptions): Promise<void>;
11
+ //# sourceMappingURL=shared-runtime-client.d.ts.map
@@ -0,0 +1,360 @@
1
+ import { spawn } from "node:child_process";
2
+ import crypto from "node:crypto";
3
+ import fs from "node:fs";
4
+ import net from "node:net";
5
+ import { SHARED_RUNTIME_ATTACH_TIMEOUT_MS, SHARED_RUNTIME_HANDSHAKE_MAX_BYTES, SHARED_RUNTIME_PROTOCOL_VERSION, buildSharedRuntimeIdentity, isSharedOfflineRuntimeEligible, resolveSharedRuntimePaths, } from "./shared-runtime-identity.js";
6
+ import { acquireLifecycleLock, isLinuxProcessIdentityLive, metadataMatchesIdentity, readHostMetadata, removeOwnedLifecycleState, } from "./shared-runtime-lifecycle.js";
7
+ class SharedRuntimeAttachError extends Error {
8
+ constructor(message, code) {
9
+ super(message);
10
+ this.code = code;
11
+ this.name = "SharedRuntimeAttachError";
12
+ }
13
+ }
14
+ function wait(delayMs) {
15
+ return new Promise((resolve) => setTimeout(resolve, delayMs));
16
+ }
17
+ function connectSocket(socketPath, timeoutMs) {
18
+ return new Promise((resolve, reject) => {
19
+ const socket = net.createConnection({ path: socketPath });
20
+ const timer = setTimeout(() => {
21
+ socket.destroy();
22
+ reject(new Error(`Timed out connecting to Satori shared runtime at '${socketPath}'.`));
23
+ }, timeoutMs);
24
+ socket.once("connect", () => {
25
+ clearTimeout(timer);
26
+ resolve(socket);
27
+ });
28
+ socket.once("error", (error) => {
29
+ clearTimeout(timer);
30
+ reject(error);
31
+ });
32
+ });
33
+ }
34
+ async function attach(socketPath, identity, timeoutMs, expectedHost) {
35
+ const socket = await connectSocket(socketPath, timeoutMs);
36
+ socket.pause();
37
+ const launcherNonce = crypto.randomBytes(24).toString("hex");
38
+ const request = Object.freeze({
39
+ type: "satori-shared-runtime-attach",
40
+ protocolVersion: SHARED_RUNTIME_PROTOCOL_VERSION,
41
+ sharedRuntimeIdentityHash: identity.hash,
42
+ installedRuntimeRoot: identity.installedRuntimeRoot,
43
+ mcpVersion: identity.mcpVersion,
44
+ launcherNonce,
45
+ });
46
+ socket.write(`${JSON.stringify(request)}\n`);
47
+ let response;
48
+ try {
49
+ response = await new Promise((resolve, reject) => {
50
+ let buffered = Buffer.alloc(0);
51
+ const timer = setTimeout(() => {
52
+ cleanup();
53
+ reject(new Error("Timed out waiting for Satori shared runtime handshake."));
54
+ }, timeoutMs);
55
+ const cleanup = () => {
56
+ clearTimeout(timer);
57
+ socket.off("data", onData);
58
+ socket.off("error", onError);
59
+ socket.off("close", onClose);
60
+ };
61
+ const onError = (error) => {
62
+ cleanup();
63
+ reject(error);
64
+ };
65
+ const onClose = () => {
66
+ cleanup();
67
+ reject(new Error("Satori shared runtime closed before completing the handshake."));
68
+ };
69
+ const onData = (chunk) => {
70
+ if (buffered.length + chunk.length > SHARED_RUNTIME_HANDSHAKE_MAX_BYTES) {
71
+ cleanup();
72
+ reject(new Error("Satori shared runtime handshake response exceeded its byte limit."));
73
+ return;
74
+ }
75
+ buffered = buffered.length === 0
76
+ ? Buffer.from(chunk)
77
+ : Buffer.concat([buffered, chunk]);
78
+ const newline = buffered.indexOf(0x0a);
79
+ if (newline < 0)
80
+ return;
81
+ const trailing = buffered.subarray(newline + 1);
82
+ try {
83
+ const parsed = JSON.parse(buffered.subarray(0, newline).toString("utf8"));
84
+ cleanup();
85
+ if (trailing.length > 0)
86
+ socket.unshift(trailing);
87
+ resolve(parsed);
88
+ }
89
+ catch (error) {
90
+ cleanup();
91
+ reject(error);
92
+ }
93
+ };
94
+ socket.on("data", onData);
95
+ socket.once("error", onError);
96
+ socket.once("close", onClose);
97
+ socket.resume();
98
+ });
99
+ }
100
+ catch (error) {
101
+ socket.destroy();
102
+ throw error;
103
+ }
104
+ if (response.type !== "satori-shared-runtime-attached"
105
+ || response.protocolVersion !== SHARED_RUNTIME_PROTOCOL_VERSION
106
+ || response.sharedRuntimeIdentityHash !== identity.hash
107
+ || response.installedRuntimeRoot !== identity.installedRuntimeRoot
108
+ || response.mcpVersion !== identity.mcpVersion) {
109
+ socket.destroy();
110
+ throw new SharedRuntimeAttachError(response.error || "Satori shared runtime returned an incompatible handshake.", "EHOSTREJECTED");
111
+ }
112
+ if (response.accepted !== true) {
113
+ socket.destroy();
114
+ const message = response.error || "Satori shared runtime rejected the launcher identity.";
115
+ throw new SharedRuntimeAttachError(message, message.includes("not accepting sessions")
116
+ ? "EHOSTUNREADY"
117
+ : "EHOSTREJECTED");
118
+ }
119
+ if (response.launcherNonce !== launcherNonce
120
+ || (expectedHost !== undefined && (response.hostPid !== expectedHost.hostPid
121
+ || response.bootId !== expectedHost.bootId
122
+ || response.processStartTime !== expectedHost.processStartTime))) {
123
+ socket.destroy();
124
+ throw new SharedRuntimeAttachError("Satori shared runtime live process identity does not match its metadata.", "EHOSTREJECTED");
125
+ }
126
+ socket.pause();
127
+ return socket;
128
+ }
129
+ async function attachFromMetadata(identity, metadataPath, timeoutMs) {
130
+ const metadata = readHostMetadata(metadataPath);
131
+ if (!metadata)
132
+ return null;
133
+ if (!metadataMatchesIdentity(metadata, identity)) {
134
+ throw new Error("Satori shared runtime metadata belongs to an incompatible runtime.");
135
+ }
136
+ if (!isLinuxProcessIdentityLive({
137
+ pid: metadata.hostPid,
138
+ bootId: metadata.bootId,
139
+ startTime: metadata.processStartTime,
140
+ })) {
141
+ return null;
142
+ }
143
+ return attach(metadata.socketPath, identity, timeoutMs, metadata);
144
+ }
145
+ function isTransientAttachError(error) {
146
+ const code = error.code;
147
+ return code === "EHOSTUNREADY"
148
+ || code === "ECONNREFUSED"
149
+ || code === "ENOENT"
150
+ || code === "ECONNRESET";
151
+ }
152
+ async function waitForHostTransition(identity, metadataPath, socketPath, timeoutMs) {
153
+ const deadline = Date.now() + timeoutMs;
154
+ let lastError = null;
155
+ while (Date.now() < deadline) {
156
+ const metadata = readHostMetadata(metadataPath);
157
+ if (!metadata) {
158
+ if (!fs.existsSync(socketPath))
159
+ return null;
160
+ try {
161
+ const unexpected = await attach(socketPath, identity, Math.min(500, Math.max(1, deadline - Date.now())));
162
+ unexpected.destroy();
163
+ throw new Error("Satori shared runtime accepted a session before publishing lifecycle metadata.");
164
+ }
165
+ catch (error) {
166
+ const code = error.code;
167
+ if (code === "ECONNREFUSED" || code === "ENOENT" || code === "ECONNRESET") {
168
+ return null;
169
+ }
170
+ if (code !== "EHOSTUNREADY")
171
+ throw error;
172
+ lastError = error;
173
+ }
174
+ }
175
+ else {
176
+ if (!metadataMatchesIdentity(metadata, identity)) {
177
+ throw new Error("Satori shared runtime metadata belongs to an incompatible runtime.");
178
+ }
179
+ if (!isLinuxProcessIdentityLive({
180
+ pid: metadata.hostPid,
181
+ bootId: metadata.bootId,
182
+ startTime: metadata.processStartTime,
183
+ })) {
184
+ return null;
185
+ }
186
+ try {
187
+ return await attach(metadata.socketPath, identity, Math.min(500, Math.max(1, deadline - Date.now())), metadata);
188
+ }
189
+ catch (error) {
190
+ if (!isTransientAttachError(error))
191
+ throw error;
192
+ lastError = error;
193
+ }
194
+ }
195
+ await wait(25);
196
+ }
197
+ throw new Error(`Timed out waiting for the existing Satori shared runtime to finish starting or stopping.`
198
+ + (lastError ? ` ${lastError instanceof Error ? lastError.message : String(lastError)}` : ""));
199
+ }
200
+ function startDetachedHost(runtimeEntry, identity, env) {
201
+ const child = spawn(process.execPath, [runtimeEntry], {
202
+ detached: true,
203
+ stdio: "ignore",
204
+ env: {
205
+ ...env,
206
+ SATORI_RUN_MODE: "host",
207
+ SATORI_SHARED_RUNTIME_EXPECTED_HASH: identity.hash,
208
+ SATORI_SHARED_RUNTIME_ENTRY: runtimeEntry,
209
+ SATORI_SHARED_RUNTIME_ERROR_PATH: resolveSharedRuntimePaths(identity, env).errorPath,
210
+ },
211
+ });
212
+ child.unref();
213
+ }
214
+ async function connectOrStart(options) {
215
+ const timeoutMs = options.attachTimeoutMs ?? SHARED_RUNTIME_ATTACH_TIMEOUT_MS;
216
+ const deadline = Date.now() + timeoutMs;
217
+ const identity = buildSharedRuntimeIdentity(options.runtimeEntry, options.env);
218
+ const paths = resolveSharedRuntimePaths(identity, options.env);
219
+ const remainingTime = () => Math.max(1, deadline - Date.now());
220
+ for (;;) {
221
+ let existing;
222
+ try {
223
+ existing = await attachFromMetadata(identity, paths.metadataPath, Math.min(500, remainingTime()));
224
+ }
225
+ catch (error) {
226
+ if (!isTransientAttachError(error))
227
+ throw error;
228
+ existing = await waitForHostTransition(identity, paths.metadataPath, paths.socketPath, remainingTime());
229
+ }
230
+ if (existing)
231
+ return existing;
232
+ if (Date.now() >= deadline) {
233
+ throw new Error("Timed out attaching to the Satori shared runtime.");
234
+ }
235
+ const lifecycleLock = await acquireLifecycleLock(paths.lockPath, remainingTime());
236
+ try {
237
+ let joined;
238
+ try {
239
+ joined = await attachFromMetadata(identity, paths.metadataPath, Math.min(500, remainingTime()));
240
+ }
241
+ catch (error) {
242
+ if (!isTransientAttachError(error))
243
+ throw error;
244
+ // The existing host may already be waiting for this same lock
245
+ // to finish shutdown. Release it before waiting for the
246
+ // lifecycle transition, then re-enter through the outer
247
+ // metadata/handshake proof.
248
+ continue;
249
+ }
250
+ if (joined)
251
+ return joined;
252
+ const stale = readHostMetadata(paths.metadataPath);
253
+ if (fs.existsSync(paths.metadataPath) && !stale) {
254
+ throw new Error(`Satori shared runtime metadata is malformed at '${paths.metadataPath}'. `
255
+ + "Run `satori doctor --verbose` before retrying.");
256
+ }
257
+ if (stale && !isLinuxProcessIdentityLive({
258
+ pid: stale.hostPid,
259
+ bootId: stale.bootId,
260
+ startTime: stale.processStartTime,
261
+ })) {
262
+ removeOwnedLifecycleState(paths, stale);
263
+ }
264
+ if (fs.existsSync(paths.socketPath)) {
265
+ try {
266
+ return await attach(paths.socketPath, identity, Math.min(500, remainingTime()));
267
+ }
268
+ catch (error) {
269
+ const code = error.code;
270
+ if (code === "EHOSTUNREADY") {
271
+ // As above, never wait for shutdown while holding its
272
+ // lifecycle lock.
273
+ continue;
274
+ }
275
+ if (code !== "ECONNREFUSED" && code !== "ENOENT") {
276
+ throw error;
277
+ }
278
+ if (fs.existsSync(paths.socketPath)) {
279
+ const socket = fs.lstatSync(paths.socketPath);
280
+ if (!socket.isSocket()) {
281
+ throw new Error(`Shared runtime path '${paths.socketPath}' is not a Unix-domain socket.`);
282
+ }
283
+ fs.unlinkSync(paths.socketPath);
284
+ }
285
+ }
286
+ }
287
+ try {
288
+ fs.unlinkSync(paths.errorPath);
289
+ }
290
+ catch (error) {
291
+ if (error.code !== "ENOENT")
292
+ throw error;
293
+ }
294
+ startDetachedHost(options.runtimeEntry, identity, options.env);
295
+ let lastError = null;
296
+ while (Date.now() < deadline) {
297
+ try {
298
+ const connected = await attachFromMetadata(identity, paths.metadataPath, Math.min(500, remainingTime()));
299
+ if (connected)
300
+ return connected;
301
+ }
302
+ catch (error) {
303
+ lastError = error;
304
+ }
305
+ await wait(25);
306
+ }
307
+ let startupDetail = "";
308
+ try {
309
+ const errorRecord = JSON.parse(fs.readFileSync(paths.errorPath, "utf8"));
310
+ if (typeof errorRecord.message === "string") {
311
+ startupDetail = ` Host startup failed: ${errorRecord.message}`;
312
+ }
313
+ }
314
+ catch {
315
+ // No bounded host diagnostic was published.
316
+ }
317
+ throw new Error(`Satori shared runtime did not become ready within ${timeoutMs}ms.`
318
+ + startupDetail
319
+ + (lastError
320
+ ? ` ${lastError instanceof Error ? lastError.message : String(lastError)}`
321
+ : ""));
322
+ }
323
+ finally {
324
+ lifecycleLock.release();
325
+ }
326
+ }
327
+ }
328
+ export async function runSharedRuntimeClient(options) {
329
+ if (!isSharedOfflineRuntimeEligible(options.env)) {
330
+ throw new Error("Shared runtime client was invoked for an ineligible configuration.");
331
+ }
332
+ const input = options.stdin ?? process.stdin;
333
+ const output = options.stdout ?? process.stdout;
334
+ const socket = await connectOrStart(options);
335
+ await new Promise((resolve, reject) => {
336
+ let finished = false;
337
+ const finish = (error) => {
338
+ if (finished)
339
+ return;
340
+ finished = true;
341
+ input.unpipe(socket);
342
+ socket.unpipe(output);
343
+ if (error)
344
+ reject(error);
345
+ else
346
+ resolve();
347
+ };
348
+ input.pipe(socket);
349
+ socket.pipe(output);
350
+ input.once("end", () => socket.end());
351
+ input.once("error", (error) => {
352
+ socket.destroy();
353
+ finish(error);
354
+ });
355
+ socket.once("error", finish);
356
+ socket.once("close", () => finish());
357
+ socket.resume();
358
+ });
359
+ }
360
+ //# sourceMappingURL=shared-runtime-client.js.map
@@ -0,0 +1,24 @@
1
+ import { type SharedRuntimeIdentity, type SharedRuntimePaths } from "./shared-runtime-identity.js";
2
+ import { SharedRuntimeHost } from "./shared-runtime.js";
3
+ export declare class SharedRuntimeSocketHost {
4
+ private readonly runtimeHost;
5
+ private readonly identity;
6
+ private readonly paths;
7
+ private readonly idleMs;
8
+ private readonly server;
9
+ private readonly sessions;
10
+ private readonly ownershipToken;
11
+ private metadata;
12
+ private idleTimer;
13
+ private closing;
14
+ private readonly unsubscribeActivity;
15
+ constructor(runtimeHost: SharedRuntimeHost, identity: SharedRuntimeIdentity, paths: SharedRuntimePaths, idleMs?: number);
16
+ start(): Promise<void>;
17
+ private acceptHandshake;
18
+ private attachSession;
19
+ private cancelIdleShutdown;
20
+ private scheduleIdleShutdown;
21
+ shutdown(): Promise<void>;
22
+ }
23
+ export declare function startSharedRuntimeHostFromEnv(): Promise<SharedRuntimeSocketHost>;
24
+ //# sourceMappingURL=shared-runtime-host.d.ts.map