@zhin.js/gateway 0.0.1-bootstrap.0 → 1.1.2

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 (181) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -0
  3. package/lib/config/ai-config-loader.d.ts +22 -0
  4. package/lib/config/ai-config-loader.js +96 -0
  5. package/lib/config/configuration-chat-policy.d.ts +57 -0
  6. package/lib/config/configuration-chat-policy.js +423 -0
  7. package/lib/config/configuration-patch.d.ts +16 -0
  8. package/lib/config/configuration-patch.js +105 -0
  9. package/lib/config/configuration-path.d.ts +6 -0
  10. package/lib/config/configuration-path.js +40 -0
  11. package/lib/config/configuration-projection.d.ts +6 -0
  12. package/lib/config/configuration-projection.js +28 -0
  13. package/lib/config/configuration-redaction.d.ts +18 -0
  14. package/lib/config/configuration-redaction.js +75 -0
  15. package/lib/config/configuration.d.ts +81 -0
  16. package/lib/config/configuration.js +686 -0
  17. package/lib/config/console-schema.d.ts +11 -0
  18. package/lib/config/console-schema.js +223 -0
  19. package/lib/config/environment-files.d.ts +3 -0
  20. package/lib/config/environment-files.js +14 -0
  21. package/lib/config/literal-secrets.d.ts +34 -0
  22. package/lib/config/literal-secrets.js +136 -0
  23. package/lib/config/plugin-package-map.d.ts +2 -0
  24. package/lib/config/plugin-package-map.js +14 -0
  25. package/lib/config/plugin-schema-catalog.d.ts +22 -0
  26. package/lib/config/plugin-schema-catalog.js +262 -0
  27. package/lib/config.d.ts +13 -0
  28. package/lib/config.js +13 -0
  29. package/lib/database/config.d.ts +22 -0
  30. package/lib/database/config.js +159 -0
  31. package/lib/database/index.d.ts +2 -0
  32. package/lib/database/index.js +2 -0
  33. package/lib/database/project-database.d.ts +31 -0
  34. package/lib/database/project-database.js +130 -0
  35. package/lib/database.d.ts +2 -0
  36. package/lib/database.js +2 -0
  37. package/lib/http/console-rpc.d.ts +27 -0
  38. package/lib/http/console-rpc.js +51 -0
  39. package/lib/http/gateway-console.d.ts +63 -0
  40. package/lib/http/gateway-console.js +309 -0
  41. package/lib/http/gateway-events.d.ts +40 -0
  42. package/lib/http/gateway-events.js +185 -0
  43. package/lib/http/gateway-http-server.d.ts +95 -0
  44. package/lib/http/gateway-http-server.js +522 -0
  45. package/lib/http/local-principal.d.ts +2 -0
  46. package/lib/http/local-principal.js +16 -0
  47. package/lib/http/plugin-switch-rpc.d.ts +19 -0
  48. package/lib/http/plugin-switch-rpc.js +214 -0
  49. package/lib/http/run-gateway.d.ts +43 -0
  50. package/lib/http/run-gateway.js +268 -0
  51. package/lib/http/start-gateway.d.ts +114 -0
  52. package/lib/http/start-gateway.js +226 -0
  53. package/lib/http-response.d.ts +6 -0
  54. package/lib/http-response.js +25 -0
  55. package/lib/http.d.ts +6 -0
  56. package/lib/http.js +6 -0
  57. package/lib/iam/crypto.d.ts +12 -0
  58. package/lib/iam/crypto.js +58 -0
  59. package/lib/iam/index.d.ts +8 -0
  60. package/lib/iam/index.js +8 -0
  61. package/lib/iam/install.d.ts +36 -0
  62. package/lib/iam/install.js +74 -0
  63. package/lib/iam/local-admin.d.ts +27 -0
  64. package/lib/iam/local-admin.js +102 -0
  65. package/lib/iam/rate-limit.d.ts +15 -0
  66. package/lib/iam/rate-limit.js +52 -0
  67. package/lib/iam/routes.d.ts +23 -0
  68. package/lib/iam/routes.js +157 -0
  69. package/lib/iam/rpc.d.ts +14 -0
  70. package/lib/iam/rpc.js +100 -0
  71. package/lib/iam/service.d.ts +203 -0
  72. package/lib/iam/service.js +784 -0
  73. package/lib/iam/setup-service.d.ts +83 -0
  74. package/lib/iam/setup-service.js +206 -0
  75. package/lib/iam/tables.d.ts +20 -0
  76. package/lib/iam/tables.js +82 -0
  77. package/lib/iam.d.ts +2 -0
  78. package/lib/iam.js +2 -0
  79. package/lib/index.d.ts +22 -0
  80. package/lib/index.js +22 -0
  81. package/lib/local/client.d.ts +71 -0
  82. package/lib/local/client.js +296 -0
  83. package/lib/local/handler.d.ts +30 -0
  84. package/lib/local/handler.js +86 -0
  85. package/lib/local/local-socket.d.ts +55 -0
  86. package/lib/local/local-socket.js +232 -0
  87. package/lib/local/paths.d.ts +34 -0
  88. package/lib/local/paths.js +58 -0
  89. package/lib/local/protocol.d.ts +140 -0
  90. package/lib/local/protocol.js +90 -0
  91. package/lib/local/state-file.d.ts +8 -0
  92. package/lib/local/state-file.js +45 -0
  93. package/lib/local.d.ts +10 -0
  94. package/lib/local.js +10 -0
  95. package/lib/onboarding/allowlist.d.ts +54 -0
  96. package/lib/onboarding/allowlist.js +106 -0
  97. package/lib/onboarding/errors.d.ts +7 -0
  98. package/lib/onboarding/errors.js +17 -0
  99. package/lib/onboarding/index.d.ts +6 -0
  100. package/lib/onboarding/index.js +6 -0
  101. package/lib/onboarding/install.d.ts +99 -0
  102. package/lib/onboarding/install.js +132 -0
  103. package/lib/onboarding/package-manager.d.ts +47 -0
  104. package/lib/onboarding/package-manager.js +179 -0
  105. package/lib/onboarding/service.d.ts +53 -0
  106. package/lib/onboarding/service.js +144 -0
  107. package/lib/onboarding/state-store.d.ts +26 -0
  108. package/lib/onboarding/state-store.js +97 -0
  109. package/lib/onboarding.d.ts +2 -0
  110. package/lib/onboarding.js +2 -0
  111. package/lib/packages/authorization.d.ts +24 -0
  112. package/lib/packages/authorization.js +128 -0
  113. package/lib/packages/errors.d.ts +11 -0
  114. package/lib/packages/errors.js +26 -0
  115. package/lib/packages/index.d.ts +5 -0
  116. package/lib/packages/index.js +5 -0
  117. package/lib/packages/rpc.d.ts +30 -0
  118. package/lib/packages/rpc.js +160 -0
  119. package/lib/packages/service.d.ts +139 -0
  120. package/lib/packages/service.js +504 -0
  121. package/lib/packages/snapshot.d.ts +14 -0
  122. package/lib/packages/snapshot.js +73 -0
  123. package/lib/packages.d.ts +2 -0
  124. package/lib/packages.js +2 -0
  125. package/lib/plugin-management/activity.d.ts +6 -0
  126. package/lib/plugin-management/activity.js +14 -0
  127. package/lib/plugin-management/config-explanation.d.ts +6 -0
  128. package/lib/plugin-management/config-explanation.js +113 -0
  129. package/lib/plugin-management/use-cases.d.ts +15 -0
  130. package/lib/plugin-management/use-cases.js +120 -0
  131. package/lib/plugin-management/workflow.d.ts +136 -0
  132. package/lib/plugin-management/workflow.js +878 -0
  133. package/lib/plugin-management.d.ts +5 -0
  134. package/lib/plugin-management.js +5 -0
  135. package/lib/project/config-file.d.ts +6 -0
  136. package/lib/project/config-file.js +58 -0
  137. package/lib/project/environment.d.ts +13 -0
  138. package/lib/project/environment.js +52 -0
  139. package/lib/project/instance-declarations.d.ts +55 -0
  140. package/lib/project/instance-declarations.js +542 -0
  141. package/lib/project/legacy-config.d.ts +25 -0
  142. package/lib/project/legacy-config.js +70 -0
  143. package/lib/project/node-requirements.d.ts +7 -0
  144. package/lib/project/node-requirements.js +20 -0
  145. package/lib/project/tsx-module-loader.d.ts +7 -0
  146. package/lib/project/tsx-module-loader.js +29 -0
  147. package/lib/project.d.ts +7 -0
  148. package/lib/project.js +7 -0
  149. package/lib/server.d.ts +11 -0
  150. package/lib/server.js +11 -0
  151. package/lib/supervisor/instance-logs.d.ts +39 -0
  152. package/lib/supervisor/instance-logs.js +152 -0
  153. package/lib/supervisor/instance-supervisor.d.ts +140 -0
  154. package/lib/supervisor/instance-supervisor.js +408 -0
  155. package/lib/supervisor/run-instance.d.ts +35 -0
  156. package/lib/supervisor/run-instance.js +161 -0
  157. package/lib/supervisor/terminal-state.d.ts +30 -0
  158. package/lib/supervisor/terminal-state.js +57 -0
  159. package/lib/supervisor.d.ts +4 -0
  160. package/lib/supervisor.js +4 -0
  161. package/lib/terminal/backends.d.ts +38 -0
  162. package/lib/terminal/backends.js +193 -0
  163. package/lib/terminal/config.d.ts +63 -0
  164. package/lib/terminal/config.js +237 -0
  165. package/lib/terminal/host.d.ts +26 -0
  166. package/lib/terminal/host.js +76 -0
  167. package/lib/terminal/index.d.ts +60 -0
  168. package/lib/terminal/index.js +135 -0
  169. package/lib/terminal/modules.d.ts +87 -0
  170. package/lib/terminal/modules.js +44 -0
  171. package/lib/terminal/service.d.ts +101 -0
  172. package/lib/terminal/service.js +455 -0
  173. package/lib/terminal/tickets.d.ts +46 -0
  174. package/lib/terminal/tickets.js +76 -0
  175. package/lib/terminal.d.ts +3 -0
  176. package/lib/terminal.js +3 -0
  177. package/lib/usage/aggregate.d.ts +87 -0
  178. package/lib/usage/aggregate.js +212 -0
  179. package/lib/usage.d.ts +2 -0
  180. package/lib/usage.js +2 -0
  181. package/package.json +117 -4
@@ -0,0 +1,7 @@
1
+ import type { TsxModuleLoader } from '@zhin.js/runtime';
2
+ /**
3
+ * Creates a lazy, namespaced TSX loader for the CLI composition root.
4
+ * It does not replace Node's native `.ts` handling and is registered only
5
+ * after the active process loader reports that it cannot load a `.tsx` file.
6
+ */
7
+ export declare function createTsxModuleLoader(projectRoot: string): TsxModuleLoader;
@@ -0,0 +1,29 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ /**
5
+ * Creates a lazy, namespaced TSX loader for the CLI composition root.
6
+ * It does not replace Node's native `.ts` handling and is registered only
7
+ * after the active process loader reports that it cannot load a `.tsx` file.
8
+ */
9
+ export function createTsxModuleLoader(projectRoot) {
10
+ const root = resolve(projectRoot);
11
+ const tsconfigFile = join(root, 'tsconfig.json');
12
+ let registration;
13
+ const acquire = () => {
14
+ registration ??= import('tsx/esm/api').then(({ register }) => register({
15
+ namespace: `zhin-runtime:${pathToFileURL(root).href}`,
16
+ tsconfig: existsSync(tsconfigFile) ? tsconfigFile : false,
17
+ }));
18
+ return registration;
19
+ };
20
+ return Object.freeze({
21
+ async load(source, parentURL) {
22
+ return (await acquire()).import(source, parentURL);
23
+ },
24
+ async close() {
25
+ if (registration)
26
+ await (await registration).unregister();
27
+ },
28
+ });
29
+ }
@@ -0,0 +1,7 @@
1
+ /** `@zhin.js/gateway/project` — project files: config file selection, dotenv layers, TSX loading, migrations, Node requirements. */
2
+ export * from './project/config-file.js';
3
+ export * from './project/environment.js';
4
+ export * from './project/instance-declarations.js';
5
+ export * from './project/legacy-config.js';
6
+ export * from './project/node-requirements.js';
7
+ export * from './project/tsx-module-loader.js';
package/lib/project.js ADDED
@@ -0,0 +1,7 @@
1
+ /** `@zhin.js/gateway/project` — project files: config file selection, dotenv layers, TSX loading, migrations, Node requirements. */
2
+ export * from './project/config-file.js';
3
+ export * from './project/environment.js';
4
+ export * from './project/instance-declarations.js';
5
+ export * from './project/legacy-config.js';
6
+ export * from './project/node-requirements.js';
7
+ export * from './project/tsx-module-loader.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * `@zhin.js/gateway/server` — the gateway HTTP server (ADR 0054 §2 / §3), its composition with
3
+ * the instance supervisor (`startGateway`), the CLI entry (`runGateway`) and the gateway-owned
4
+ * Console RPC route. Loads the Node HTTP stack: never import it from the instance.
5
+ */
6
+ export * from './http/gateway-http-server.js';
7
+ export * from './http/console-rpc.js';
8
+ export * from './http/start-gateway.js';
9
+ export * from './http/gateway-console.js';
10
+ export * from './http/gateway-events.js';
11
+ export * from './http/run-gateway.js';
package/lib/server.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * `@zhin.js/gateway/server` — the gateway HTTP server (ADR 0054 §2 / §3), its composition with
3
+ * the instance supervisor (`startGateway`), the CLI entry (`runGateway`) and the gateway-owned
4
+ * Console RPC route. Loads the Node HTTP stack: never import it from the instance.
5
+ */
6
+ export * from './http/gateway-http-server.js';
7
+ export * from './http/console-rpc.js';
8
+ export * from './http/start-gateway.js';
9
+ export * from './http/gateway-console.js';
10
+ export * from './http/gateway-events.js';
11
+ export * from './http/run-gateway.js';
@@ -0,0 +1,39 @@
1
+ import type { Readable } from 'node:stream';
2
+ export type InstanceLogStream = 'stdout' | 'stderr';
3
+ export interface InstanceLogLine {
4
+ /** Monotonic sequence number, unique per collector. */
5
+ readonly seq: number;
6
+ /** Epoch milliseconds when the line was captured. */
7
+ readonly time: number;
8
+ readonly stream: InstanceLogStream;
9
+ readonly line: string;
10
+ }
11
+ export interface InstanceLogOptions {
12
+ /** Log file path; `false` keeps the ring buffer only. */
13
+ readonly file?: string | false;
14
+ /** Rotate once the file would exceed this many bytes. Default 10 MiB. */
15
+ readonly maxBytes?: number;
16
+ /** Rotated files kept besides the active one (`.1` … `.N`). Default 5. */
17
+ readonly maxFiles?: number;
18
+ /** Ring buffer capacity in lines. Default 1000. */
19
+ readonly capacity?: number;
20
+ }
21
+ export declare const DEFAULT_INSTANCE_LOG_MAX_BYTES: number;
22
+ export declare const DEFAULT_INSTANCE_LOG_MAX_FILES = 5;
23
+ export declare const DEFAULT_INSTANCE_LOG_CAPACITY = 1000;
24
+ export declare class InstanceLogCollector {
25
+ #private;
26
+ constructor(options?: InstanceLogOptions);
27
+ get file(): string | undefined;
28
+ /** Captures a child stream until it ends. */
29
+ attach(stream: Readable, kind: InstanceLogStream): void;
30
+ /** Appends raw text; complete lines are recorded, the remainder waits for its newline. */
31
+ write(kind: InstanceLogStream, text: string): void;
32
+ /** Records an unterminated trailing line (e.g. when the child exits). */
33
+ flush(kind?: InstanceLogStream): void;
34
+ /** The newest `limit` lines (all buffered lines by default), oldest first. */
35
+ tail(limit?: number): readonly InstanceLogLine[];
36
+ /** Live lines; returns an unsubscribe function. */
37
+ subscribe(listener: (line: InstanceLogLine) => void): () => void;
38
+ close(): void;
39
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Instance log capture (ADR 0054 §3): the gateway collects the instance's stdout / stderr into an
3
+ * in-memory ring buffer (for `gateway.instance.logs` and offline viewing) and a size-rotated file
4
+ * (`.zhin/logs/instance.log`, `.1`, `.2`, …).
5
+ */
6
+ import { closeSync, existsSync, mkdirSync, openSync, renameSync, rmSync, statSync, writeSync } from 'node:fs';
7
+ import { dirname } from 'node:path';
8
+ import { StringDecoder } from 'node:string_decoder';
9
+ export const DEFAULT_INSTANCE_LOG_MAX_BYTES = 10 * 1024 * 1024;
10
+ export const DEFAULT_INSTANCE_LOG_MAX_FILES = 5;
11
+ export const DEFAULT_INSTANCE_LOG_CAPACITY = 1_000;
12
+ /** A line longer than this is split, so a child that never prints a newline cannot grow memory. */
13
+ const MAX_PENDING_LINE = 64 * 1024;
14
+ export class InstanceLogCollector {
15
+ #file;
16
+ #maxBytes;
17
+ #maxFiles;
18
+ #capacity;
19
+ #ring = [];
20
+ #listeners = new Set();
21
+ #pending = { stdout: '', stderr: '' };
22
+ #seq = 0;
23
+ #fd;
24
+ #size = 0;
25
+ #closed = false;
26
+ constructor(options = {}) {
27
+ this.#file = options.file === false ? undefined : options.file;
28
+ this.#maxBytes = Math.max(1, options.maxBytes ?? DEFAULT_INSTANCE_LOG_MAX_BYTES);
29
+ this.#maxFiles = Math.max(0, Math.floor(options.maxFiles ?? DEFAULT_INSTANCE_LOG_MAX_FILES));
30
+ this.#capacity = Math.max(1, Math.floor(options.capacity ?? DEFAULT_INSTANCE_LOG_CAPACITY));
31
+ }
32
+ get file() { return this.#file; }
33
+ /** Captures a child stream until it ends. */
34
+ attach(stream, kind) {
35
+ const decoder = new StringDecoder('utf8');
36
+ stream.on('data', (chunk) => {
37
+ this.write(kind, typeof chunk === 'string' ? chunk : decoder.write(chunk));
38
+ });
39
+ stream.once('end', () => {
40
+ const rest = decoder.end();
41
+ if (rest)
42
+ this.write(kind, rest);
43
+ this.flush(kind);
44
+ });
45
+ }
46
+ /** Appends raw text; complete lines are recorded, the remainder waits for its newline. */
47
+ write(kind, text) {
48
+ if (this.#closed || text.length === 0)
49
+ return;
50
+ let buffer = this.#pending[kind] + text;
51
+ for (;;) {
52
+ const newline = buffer.indexOf('\n');
53
+ if (newline < 0)
54
+ break;
55
+ this.#record(kind, buffer.slice(0, newline).replace(/\r$/u, ''));
56
+ buffer = buffer.slice(newline + 1);
57
+ }
58
+ while (buffer.length > MAX_PENDING_LINE) {
59
+ this.#record(kind, buffer.slice(0, MAX_PENDING_LINE));
60
+ buffer = buffer.slice(MAX_PENDING_LINE);
61
+ }
62
+ this.#pending[kind] = buffer;
63
+ }
64
+ /** Records an unterminated trailing line (e.g. when the child exits). */
65
+ flush(kind) {
66
+ for (const stream of kind ? [kind] : ['stdout', 'stderr']) {
67
+ const rest = this.#pending[stream];
68
+ this.#pending[stream] = '';
69
+ if (rest)
70
+ this.#record(stream, rest);
71
+ }
72
+ }
73
+ /** The newest `limit` lines (all buffered lines by default), oldest first. */
74
+ tail(limit = this.#capacity) {
75
+ const count = Math.max(0, Math.min(limit, this.#ring.length));
76
+ return Object.freeze(this.#ring.slice(this.#ring.length - count));
77
+ }
78
+ /** Live lines; returns an unsubscribe function. */
79
+ subscribe(listener) {
80
+ this.#listeners.add(listener);
81
+ return () => { this.#listeners.delete(listener); };
82
+ }
83
+ close() {
84
+ if (this.#closed)
85
+ return;
86
+ this.flush();
87
+ this.#closed = true;
88
+ this.#listeners.clear();
89
+ this.#closeFile();
90
+ }
91
+ #record(stream, line) {
92
+ const entry = Object.freeze({ seq: ++this.#seq, time: Date.now(), stream, line });
93
+ this.#ring.push(entry);
94
+ if (this.#ring.length > this.#capacity)
95
+ this.#ring.splice(0, this.#ring.length - this.#capacity);
96
+ this.#append(`${line}\n`);
97
+ for (const listener of [...this.#listeners]) {
98
+ try {
99
+ listener(entry);
100
+ }
101
+ catch { /* a faulty subscriber must not stop capture */ }
102
+ }
103
+ }
104
+ #append(text) {
105
+ if (!this.#file)
106
+ return;
107
+ try {
108
+ const bytes = Buffer.byteLength(text);
109
+ this.#open();
110
+ if (this.#size > 0 && this.#size + bytes > this.#maxBytes)
111
+ this.#rotate();
112
+ this.#open();
113
+ writeSync(this.#fd, text);
114
+ this.#size += bytes;
115
+ }
116
+ catch {
117
+ // Disk errors must not take the supervisor down; the ring buffer still has the line.
118
+ this.#closeFile();
119
+ }
120
+ }
121
+ #open() {
122
+ if (this.#fd !== undefined || !this.#file)
123
+ return;
124
+ mkdirSync(dirname(this.#file), { recursive: true });
125
+ this.#fd = openSync(this.#file, 'a');
126
+ this.#size = statSync(this.#file).size;
127
+ }
128
+ #rotate() {
129
+ const file = this.#file;
130
+ this.#closeFile();
131
+ if (this.#maxFiles === 0) {
132
+ rmSync(file, { force: true });
133
+ return;
134
+ }
135
+ rmSync(`${file}.${this.#maxFiles}`, { force: true });
136
+ for (let index = this.#maxFiles - 1; index >= 1; index -= 1) {
137
+ if (existsSync(`${file}.${index}`))
138
+ renameSync(`${file}.${index}`, `${file}.${index + 1}`);
139
+ }
140
+ renameSync(file, `${file}.1`);
141
+ }
142
+ #closeFile() {
143
+ if (this.#fd === undefined)
144
+ return;
145
+ try {
146
+ closeSync(this.#fd);
147
+ }
148
+ catch { /* already closed */ }
149
+ this.#fd = undefined;
150
+ this.#size = 0;
151
+ }
152
+ }
@@ -0,0 +1,140 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { IpcInstanceTransport, type IpcInstanceTransportOptions, type InstanceReadyPayload, type InstanceRestartRequestPayload } from '@zhin.js/host-http/bridge';
3
+ import { InstanceLogCollector, type InstanceLogLine, type InstanceLogOptions } from './instance-logs.js';
4
+ export type InstanceState = 'stopped' | 'starting' | 'running' | 'stopping' | 'backoff' | 'crashed';
5
+ export type InstanceFailureReason = 'exit' | 'spawn-error' | 'ready-timeout' | 'protocol-mismatch' | 'restart-storm';
6
+ export interface InstanceFailure {
7
+ readonly reason: InstanceFailureReason;
8
+ readonly message: string;
9
+ readonly at: number;
10
+ readonly exitCode: number | null;
11
+ readonly signal: NodeJS.Signals | null;
12
+ /** Last captured log lines before the failure (empty with inherited output). */
13
+ readonly logTail: readonly string[];
14
+ }
15
+ export interface InstanceStatus {
16
+ readonly state: InstanceState;
17
+ readonly pid?: number;
18
+ /** Epoch ms of the current child's `ready`. */
19
+ readonly startedAt?: number;
20
+ /** Runtime generation reported by the instance in `ready`. */
21
+ readonly generation?: number;
22
+ /** Protocol version reported by the instance in `ready`. */
23
+ readonly protocolVersion?: number;
24
+ readonly lastExitCode: number | null;
25
+ readonly lastExitSignal: NodeJS.Signals | null;
26
+ readonly lastFailure?: InstanceFailure;
27
+ /** Automatic restarts (crash or requested) inside the current storm window. */
28
+ readonly recentRestarts: number;
29
+ }
30
+ export interface InstanceSpawnSpec {
31
+ readonly command: string;
32
+ readonly args: readonly string[];
33
+ readonly cwd: string;
34
+ /** Child environment; defaults to `process.env`. {@link INSTANCE_CHILD_ENV} is always set. */
35
+ readonly env?: Readonly<Record<string, string | undefined>>;
36
+ }
37
+ export interface InstanceRestartPolicy {
38
+ /** Restart crashed instances automatically. Default true. */
39
+ readonly onCrash?: boolean;
40
+ /** Honour `restart-request` from the instance. Default true. */
41
+ readonly onRequest?: boolean;
42
+ /** Automatic restarts allowed per window before the instance stays `crashed`. Default 10. */
43
+ readonly maxRestarts?: number;
44
+ /** Storm window. Default 60 s. */
45
+ readonly windowMs?: number;
46
+ /** Delay before an automatic restart (`backoff` state). Default 3 s. */
47
+ readonly delayMs?: number;
48
+ }
49
+ export interface InstanceSupervisorOptions {
50
+ readonly spawn: InstanceSpawnSpec;
51
+ /** `pipe` captures stdout / stderr into {@link InstanceSupervisor.logs}; `inherit` shares the terminal. */
52
+ readonly output?: 'pipe' | 'inherit';
53
+ /**
54
+ * The child's stdin. `inherit` hands it the supervisor's own stdin (a foreground gateway on a
55
+ * TTY, so terminal adapters can read input); default `ignore` (background gateways).
56
+ */
57
+ readonly input?: 'ignore' | 'inherit';
58
+ readonly logs?: InstanceLogOptions;
59
+ /** Time a spawned instance gets to report `ready`; see {@link InstanceSupervisor.setReadyTimeout}. */
60
+ readonly readyTimeoutMs?: number;
61
+ /** Budget announced in `shutdown`; SIGTERM follows when the child has not exited after it. */
62
+ readonly shutdownBudgetMs?: number;
63
+ /** Time between SIGTERM and SIGKILL. */
64
+ readonly killGraceMs?: number;
65
+ readonly restart?: InstanceRestartPolicy;
66
+ /** Protocol versions this supervisor accepts. Default: only {@link INSTANCE_BRIDGE_PROTOCOL_VERSION}. */
67
+ readonly acceptedProtocolVersions?: readonly number[];
68
+ /** HTTP bridge settings of {@link InstanceSupervisor.transport}. */
69
+ readonly transport?: IpcInstanceTransportOptions;
70
+ }
71
+ export interface InstanceStopOptions {
72
+ /** Time to wait after `shutdown` before SIGTERM. Default: shutdown budget + 1 s. */
73
+ readonly timeoutMs?: number;
74
+ readonly reason?: string;
75
+ /** Kill immediately with SIGKILL (also escalates a stop already in progress). */
76
+ readonly force?: boolean;
77
+ }
78
+ export interface InstanceExit {
79
+ readonly code: number | null;
80
+ readonly signal: NodeJS.Signals | null;
81
+ /** The exit was requested by the supervisor (stop / restart). */
82
+ readonly expected: boolean;
83
+ }
84
+ export interface InstanceSupervisorEvents {
85
+ state: [InstanceStatus];
86
+ ready: [InstanceReadyPayload];
87
+ exit: [InstanceExit];
88
+ 'restart-request': [InstanceRestartRequestPayload];
89
+ failure: [InstanceFailure];
90
+ log: [InstanceLogLine];
91
+ }
92
+ export declare const DEFAULT_INSTANCE_READY_TIMEOUT_MS = 60000;
93
+ export declare const DEFAULT_INSTANCE_KILL_GRACE_MS = 2000;
94
+ /** Storm guard: 10 automatic restarts per minute, 3 s apart (ADR 0054 §5). */
95
+ export declare const MAX_RESTARTS_PER_WINDOW = 10;
96
+ export declare const RESTART_WINDOW_MS = 60000;
97
+ export declare const RESTART_DELAY_MS = 3000;
98
+ export declare class InstanceProtocolMismatchError extends Error {
99
+ readonly instanceVersion: number;
100
+ readonly accepted: readonly number[];
101
+ readonly code = "ZHIN_INSTANCE_PROTOCOL_MISMATCH";
102
+ constructor(instanceVersion: number, accepted: readonly number[]);
103
+ }
104
+ export declare class InstanceStartError extends Error {
105
+ readonly failure: InstanceFailure;
106
+ readonly code = "ZHIN_INSTANCE_START_FAILED";
107
+ constructor(failure: InstanceFailure);
108
+ }
109
+ export declare class InstanceSupervisor extends EventEmitter<InstanceSupervisorEvents> {
110
+ #private;
111
+ readonly logs: InstanceLogCollector;
112
+ /**
113
+ * HTTP / WebSocket bridge to the current instance child (ADR 0054 §2). Stable across
114
+ * restarts: the gateway's HTTP server forwards through it; it is `ready` while a child that
115
+ * announced its route table runs.
116
+ */
117
+ readonly transport: IpcInstanceTransport;
118
+ constructor(options: InstanceSupervisorOptions);
119
+ get state(): InstanceState;
120
+ status(): InstanceStatus;
121
+ /**
122
+ * Starts the instance and resolves on its `ready`. A manual start resets the storm window.
123
+ * Rejects with {@link InstanceStartError} / {@link InstanceProtocolMismatchError} when this
124
+ * attempt fails; automatic restarts after a crash still follow the restart policy.
125
+ */
126
+ start(): Promise<void>;
127
+ /** Graceful `shutdown`, then SIGTERM after `timeoutMs`, then SIGKILL after the kill grace. */
128
+ stop(options?: InstanceStopOptions): Promise<void>;
129
+ /**
130
+ * Ready timeout for the next spawns (`gateway.readyTimeout`, re-read by the gateway before each
131
+ * start, restart and transaction); `undefined` goes back to the constructor option.
132
+ */
133
+ setReadyTimeout(ms: number | undefined): void;
134
+ /** Ready timeout the next spawn uses. */
135
+ get readyTimeoutMs(): number;
136
+ /** Manual restart: stop gracefully, then start (not counted by the storm guard). */
137
+ restart(options?: Omit<InstanceStopOptions, 'force'>): Promise<void>;
138
+ /** Releases timers and the log file; kills a child that is still running. */
139
+ dispose(): void;
140
+ }