@rynx-ai/runtime 0.1.11-beta.5 → 0.1.11-beta.51
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/claude/executor.d.ts +19 -5
- package/dist/claude/executor.js +56 -12
- package/dist/claude/models.d.ts +0 -5
- package/dist/claude/models.js +1 -7
- package/dist/claude/native-bridge.d.ts +103 -1
- package/dist/claude/native-bridge.js +460 -30
- package/dist/claude/native-hook-main.js +81 -1
- package/dist/claude/native-hooks.js +7 -0
- package/dist/claude/native-integration.d.ts +178 -26
- package/dist/claude/native-integration.js +1571 -170
- package/dist/claude/session-status.d.ts +39 -0
- package/dist/claude/session-status.js +163 -0
- package/dist/claude/transcript-clone.d.ts +18 -0
- package/dist/claude/transcript-clone.js +497 -0
- package/dist/claude/transcript.d.ts +27 -4
- package/dist/claude/transcript.js +183 -46
- package/dist/codex-app-server/client.d.ts +10 -6
- package/dist/codex-app-server/client.js +67 -15
- package/dist/codex-app-server/forwarder.d.ts +96 -3
- package/dist/codex-app-server/forwarder.js +542 -57
- package/dist/codex-app-server/mapping.d.ts +3 -6
- package/dist/codex-app-server/mapping.js +228 -36
- package/dist/codex-app-server/mcp-startup.d.ts +13 -0
- package/dist/codex-app-server/mcp-startup.js +63 -0
- package/dist/codex-app-server/process-registry.d.ts +36 -0
- package/dist/codex-app-server/process-registry.js +320 -0
- package/dist/codex-app-server/protocol.d.ts +68 -7
- package/dist/codex-app-server/transport.d.ts +2 -2
- package/dist/codex-app-server/transport.js +4 -0
- package/dist/codex-app-server/ws-channel.d.ts +8 -1
- package/dist/codex-app-server/ws-channel.js +113 -28
- package/dist/codex-child-env.js +3 -32
- package/dist/codex-home.d.ts +35 -3
- package/dist/codex-home.js +323 -18
- package/dist/codex-session-store.d.ts +23 -0
- package/dist/codex-session-store.js +21 -0
- package/dist/host.d.ts +106 -48
- package/dist/host.js +2024 -640
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/input-resources.d.ts +4 -0
- package/dist/input-resources.js +21 -5
- package/dist/models-catalog.js +12 -3
- package/dist/runner/child.d.ts +105 -28
- package/dist/runner/child.js +1625 -100
- package/dist/runner/manager.d.ts +114 -46
- package/dist/runner/manager.js +1523 -446
- package/dist/runner/protocol.d.ts +212 -24
- package/dist/runner/protocol.js +5 -0
- package/dist/runner/startup-policy.d.ts +7 -0
- package/dist/runner/startup-policy.js +10 -0
- package/dist/runner/transport.d.ts +18 -2
- package/dist/runner/transport.js +82 -3
- package/dist/runner-main.js +8 -3
- package/dist/terminal/codex-tui.d.ts +4 -0
- package/dist/terminal/codex-tui.js +5 -0
- package/dist/terminal/control-parser.d.ts +39 -0
- package/dist/terminal/control-parser.js +172 -0
- package/dist/terminal/registry.d.ts +18 -15
- package/dist/terminal/registry.js +44 -23
- package/dist/terminal/spool.d.ts +47 -0
- package/dist/terminal/spool.js +231 -0
- package/dist/terminal/tmux.d.ts +126 -74
- package/dist/terminal/tmux.js +807 -211
- package/package.json +4 -4
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure codex app-server notification → {@link AgentEvent} mapping. Extracted from
|
|
3
|
-
* the executor so BOTH the per-turn executor and the persistent session
|
|
4
|
-
* forwarder (which mirrors every thread turn — web- AND TUI-initiated — into the
|
|
5
|
-
* canonical log, reference implementation's codex-native model) share one mapping.
|
|
6
|
-
*/
|
|
7
1
|
import type { AgentEvent } from "@rynx-ai/core";
|
|
8
2
|
import type { ThreadItem } from "./protocol.js";
|
|
9
3
|
export interface CodexMapResult {
|
|
@@ -11,8 +5,11 @@ export interface CodexMapResult {
|
|
|
11
5
|
finalText?: string;
|
|
12
6
|
usage?: Record<string, unknown>;
|
|
13
7
|
turnCompleted?: boolean;
|
|
8
|
+
turnInterrupted?: boolean;
|
|
14
9
|
fatalError?: Error;
|
|
15
10
|
}
|
|
11
|
+
export declare function codexTurnStatus(turn: unknown): string | undefined;
|
|
12
|
+
export declare function codexResumeTerminalStatus(turn: unknown): "idle" | "failed" | undefined;
|
|
16
13
|
/** Map one thread item (`item/started` | `item/completed`) to events. */
|
|
17
14
|
export declare function mapCodexItem(method: string, item: ThreadItem): CodexMapResult;
|
|
18
15
|
/** Map one codex app-server notification to events (+ turn/usage/error signals). */
|
|
@@ -1,12 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure codex app-server notification → {@link AgentEvent} mapping. Extracted from
|
|
3
|
+
* the executor so BOTH the per-turn executor and the persistent session
|
|
4
|
+
* forwarder (which mirrors every thread turn — web- AND TUI-initiated — into the
|
|
5
|
+
* canonical log, reference implementation's codex-native model) share one mapping.
|
|
6
|
+
*/
|
|
7
|
+
import { extname, isAbsolute } from "node:path";
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
const CODEX_AUTH_ERROR_FRAGMENTS = [
|
|
12
|
+
"401",
|
|
13
|
+
"403",
|
|
14
|
+
"unauthorized",
|
|
15
|
+
"authentication",
|
|
16
|
+
"not logged in",
|
|
17
|
+
"not authenticated",
|
|
18
|
+
"log in",
|
|
19
|
+
"login",
|
|
20
|
+
"sign in",
|
|
21
|
+
"re-authenticate",
|
|
22
|
+
"reauthenticate",
|
|
23
|
+
"credentials",
|
|
24
|
+
"access token",
|
|
25
|
+
"token expired",
|
|
26
|
+
"expired token",
|
|
27
|
+
"session expired",
|
|
28
|
+
"api key",
|
|
29
|
+
];
|
|
30
|
+
const CODEX_REAUTH_HINT = "Codex needs you to re-authenticate. Run `codex login` and retry.";
|
|
31
|
+
function codexErrorMessage(error) {
|
|
32
|
+
for (const key of ["message", "error", "text", "detail"]) {
|
|
33
|
+
const value = error[key];
|
|
34
|
+
if (typeof value === "string" && value.trim())
|
|
35
|
+
return value.trim();
|
|
36
|
+
}
|
|
37
|
+
return "Codex turn ended with an unspecified error.";
|
|
38
|
+
}
|
|
39
|
+
function codexErrorIsAuth(error, message) {
|
|
40
|
+
const info = error.codexErrorInfo;
|
|
41
|
+
let variant;
|
|
42
|
+
let httpStatusCode;
|
|
43
|
+
if (typeof info === "string") {
|
|
44
|
+
variant = info;
|
|
45
|
+
}
|
|
46
|
+
else if (isRecord(info)) {
|
|
47
|
+
variant = info.type ?? info.kind ?? info.variant;
|
|
48
|
+
httpStatusCode = info.httpStatusCode;
|
|
49
|
+
}
|
|
50
|
+
if (typeof variant === "string" && variant.toLowerCase() === "unauthorized")
|
|
51
|
+
return true;
|
|
52
|
+
if (httpStatusCode === 401 || httpStatusCode === 403)
|
|
53
|
+
return true;
|
|
54
|
+
const lowered = message.toLowerCase();
|
|
55
|
+
return CODEX_AUTH_ERROR_FRAGMENTS.some((fragment) => lowered.includes(fragment));
|
|
56
|
+
}
|
|
1
57
|
function turnError(error, fallback) {
|
|
2
|
-
if (
|
|
3
|
-
return new Error(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
58
|
+
if (!error)
|
|
59
|
+
return new Error(fallback);
|
|
60
|
+
const message = codexErrorMessage(error);
|
|
61
|
+
return new Error(codexErrorIsAuth(error, message)
|
|
62
|
+
? `${message}\n\n${CODEX_REAUTH_HINT}`
|
|
63
|
+
: message);
|
|
64
|
+
}
|
|
65
|
+
export function codexTurnStatus(turn) {
|
|
66
|
+
if (!isRecord(turn))
|
|
67
|
+
return undefined;
|
|
68
|
+
const status = turn.status;
|
|
69
|
+
if (typeof status === "string")
|
|
70
|
+
return status;
|
|
71
|
+
if (!isRecord(status))
|
|
72
|
+
return undefined;
|
|
73
|
+
const value = status.type ?? status.status;
|
|
74
|
+
return typeof value === "string" ? value : undefined;
|
|
75
|
+
}
|
|
76
|
+
export function codexResumeTerminalStatus(turn) {
|
|
77
|
+
if (terminalTurnError(turn, "turn/completed"))
|
|
78
|
+
return "failed";
|
|
79
|
+
const status = codexTurnStatus(turn);
|
|
80
|
+
if (status === "completed" || status === "interrupted" ||
|
|
81
|
+
status === "cancelled" || status === "canceled") {
|
|
82
|
+
return "idle";
|
|
8
83
|
}
|
|
9
|
-
|
|
84
|
+
if (status === "failed" || status === "errored")
|
|
85
|
+
return "failed";
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
function terminalTurnError(turn, method) {
|
|
89
|
+
if (!isRecord(turn)) {
|
|
90
|
+
return method === "turn/failed" ? new Error("Codex turn failed") : undefined;
|
|
91
|
+
}
|
|
92
|
+
let error = turn.error;
|
|
93
|
+
if (!isRecord(error) && Array.isArray(turn.items)) {
|
|
94
|
+
error = turn.items.find((item) => isRecord(item) && item.type === "error");
|
|
95
|
+
}
|
|
96
|
+
if (isRecord(error))
|
|
97
|
+
return turnError(error, "Codex turn failed");
|
|
98
|
+
const status = codexTurnStatus(turn);
|
|
99
|
+
if (method === "turn/failed" || status === "failed" || status === "errored") {
|
|
100
|
+
return turnError(undefined, "Codex turn failed");
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
10
103
|
}
|
|
11
104
|
function webSearchInput(item) {
|
|
12
105
|
const action = item.action;
|
|
@@ -29,6 +122,25 @@ function webSearchInput(item) {
|
|
|
29
122
|
}
|
|
30
123
|
return { id: item.id, query: item.query };
|
|
31
124
|
}
|
|
125
|
+
function fileChangeSummary(changes) {
|
|
126
|
+
const lines = [];
|
|
127
|
+
for (const change of changes) {
|
|
128
|
+
if (!isRecord(change))
|
|
129
|
+
continue;
|
|
130
|
+
if (typeof change.path !== "string")
|
|
131
|
+
continue;
|
|
132
|
+
const kind = isRecord(change.kind) && typeof change.kind.type === "string" && change.kind.type
|
|
133
|
+
? change.kind.type
|
|
134
|
+
: "change";
|
|
135
|
+
lines.push(`${kind} ${change.path}`);
|
|
136
|
+
}
|
|
137
|
+
return lines.join("\n");
|
|
138
|
+
}
|
|
139
|
+
function isSupportedAbsoluteImagePath(path) {
|
|
140
|
+
if (!isAbsolute(path))
|
|
141
|
+
return false;
|
|
142
|
+
return [".png", ".jpg", ".jpeg", ".webp", ".gif", ".svg"].includes(extname(path).toLowerCase());
|
|
143
|
+
}
|
|
32
144
|
/** Map one thread item (`item/started` | `item/completed`) to events. */
|
|
33
145
|
export function mapCodexItem(method, item) {
|
|
34
146
|
const events = [];
|
|
@@ -37,20 +149,37 @@ export function mapCodexItem(method, item) {
|
|
|
37
149
|
switch (item.type) {
|
|
38
150
|
case "commandExecution": {
|
|
39
151
|
const commandItem = item;
|
|
152
|
+
const id = typeof commandItem.id === "string" ? commandItem.id : "";
|
|
153
|
+
const command = typeof commandItem.command === "string" ? commandItem.command : "";
|
|
154
|
+
if (!id || !command)
|
|
155
|
+
return { events };
|
|
156
|
+
const exitCode = typeof commandItem.exitCode === "number"
|
|
157
|
+
? commandItem.exitCode
|
|
158
|
+
: null;
|
|
159
|
+
const rawOutput = commandItem.aggregatedOutput ?? "";
|
|
160
|
+
const aggregatedOutput = exitCode !== null && exitCode !== 0
|
|
161
|
+
? `${rawOutput}${rawOutput ? "\n" : ""}[exit code: ${exitCode}]`
|
|
162
|
+
: rawOutput;
|
|
40
163
|
events.push({
|
|
41
164
|
type: "tool",
|
|
42
165
|
event: isStart ? "on_tool_start" : "on_tool_end",
|
|
43
|
-
name: "
|
|
44
|
-
input: isStart
|
|
45
|
-
? {
|
|
166
|
+
name: "shell",
|
|
167
|
+
input: isStart || isEnd
|
|
168
|
+
? {
|
|
169
|
+
id,
|
|
170
|
+
command,
|
|
171
|
+
...(typeof commandItem.cwd === "string" && commandItem.cwd
|
|
172
|
+
? { cwd: commandItem.cwd }
|
|
173
|
+
: {}),
|
|
174
|
+
}
|
|
46
175
|
: undefined,
|
|
47
176
|
output: isEnd
|
|
48
177
|
? {
|
|
49
|
-
id
|
|
50
|
-
command
|
|
178
|
+
id,
|
|
179
|
+
command,
|
|
51
180
|
status: commandItem.status,
|
|
52
|
-
aggregatedOutput
|
|
53
|
-
exitCode
|
|
181
|
+
aggregatedOutput,
|
|
182
|
+
exitCode,
|
|
54
183
|
}
|
|
55
184
|
: undefined,
|
|
56
185
|
data: { method, item },
|
|
@@ -58,16 +187,23 @@ export function mapCodexItem(method, item) {
|
|
|
58
187
|
return { events };
|
|
59
188
|
}
|
|
60
189
|
case "fileChange": {
|
|
190
|
+
const fileChange = item;
|
|
191
|
+
const changes = Array.isArray(fileChange.changes) ? fileChange.changes : [];
|
|
192
|
+
const id = typeof item.id === "string" ? item.id : "";
|
|
193
|
+
if (!id ||
|
|
194
|
+
changes.length === 0 ||
|
|
195
|
+
!changes.some((change) => isRecord(change) && typeof change.path === "string"))
|
|
196
|
+
return { events };
|
|
61
197
|
events.push({
|
|
62
198
|
type: "tool",
|
|
63
199
|
event: isStart ? "on_tool_start" : "on_tool_end",
|
|
64
|
-
name: "
|
|
65
|
-
input: isStart ? { id
|
|
200
|
+
name: "apply_patch",
|
|
201
|
+
input: isStart || isEnd ? { id, changes } : undefined,
|
|
66
202
|
output: isEnd
|
|
67
203
|
? {
|
|
68
|
-
id
|
|
69
|
-
|
|
70
|
-
|
|
204
|
+
id,
|
|
205
|
+
status: fileChange.status,
|
|
206
|
+
aggregatedOutput: fileChangeSummary(changes),
|
|
71
207
|
}
|
|
72
208
|
: undefined,
|
|
73
209
|
data: { method, item },
|
|
@@ -113,6 +249,52 @@ export function mapCodexItem(method, item) {
|
|
|
113
249
|
});
|
|
114
250
|
return { events };
|
|
115
251
|
}
|
|
252
|
+
case "imageView": {
|
|
253
|
+
const image = item;
|
|
254
|
+
if (typeof image.id !== "string" || !image.id ||
|
|
255
|
+
typeof image.path !== "string" || !image.path)
|
|
256
|
+
return { events };
|
|
257
|
+
events.push({
|
|
258
|
+
type: "tool",
|
|
259
|
+
event: isStart ? "on_tool_start" : "on_tool_end",
|
|
260
|
+
name: "view_image",
|
|
261
|
+
// Image views may arrive only as completed items, including backfill.
|
|
262
|
+
input: isStart || isEnd ? { id: image.id, path: image.path } : undefined,
|
|
263
|
+
output: isEnd
|
|
264
|
+
? {
|
|
265
|
+
id: image.id,
|
|
266
|
+
aggregatedOutput: `Viewed image at ${image.path}`,
|
|
267
|
+
generatedImage: { savedPath: image.path },
|
|
268
|
+
}
|
|
269
|
+
: undefined,
|
|
270
|
+
data: { method, item },
|
|
271
|
+
});
|
|
272
|
+
return { events };
|
|
273
|
+
}
|
|
274
|
+
case "imageGeneration": {
|
|
275
|
+
const image = item;
|
|
276
|
+
events.push({
|
|
277
|
+
type: "tool",
|
|
278
|
+
event: isStart ? "on_tool_start" : "on_tool_end",
|
|
279
|
+
name: "image_generation",
|
|
280
|
+
input: isStart ? { id: image.id } : undefined,
|
|
281
|
+
output: isEnd
|
|
282
|
+
? {
|
|
283
|
+
id: image.id,
|
|
284
|
+
status: image.status,
|
|
285
|
+
generatedImage: {
|
|
286
|
+
...(!image.savedPath || !isSupportedAbsoluteImagePath(image.savedPath)
|
|
287
|
+
? { result: image.result }
|
|
288
|
+
: {}),
|
|
289
|
+
revisedPrompt: image.revisedPrompt,
|
|
290
|
+
...(image.savedPath ? { savedPath: image.savedPath } : {}),
|
|
291
|
+
},
|
|
292
|
+
}
|
|
293
|
+
: undefined,
|
|
294
|
+
data: { method, item: { ...image, result: image.result ? "[image data omitted]" : "" } },
|
|
295
|
+
});
|
|
296
|
+
return { events };
|
|
297
|
+
}
|
|
116
298
|
case "agentMessage": {
|
|
117
299
|
const text = item.text?.trim() ?? "";
|
|
118
300
|
if (isEnd && text) {
|
|
@@ -122,7 +304,11 @@ export function mapCodexItem(method, item) {
|
|
|
122
304
|
return { events };
|
|
123
305
|
}
|
|
124
306
|
case "plan": {
|
|
125
|
-
|
|
307
|
+
const text = item.text?.trim() ?? "";
|
|
308
|
+
if (isEnd && text) {
|
|
309
|
+
events.push({ type: "message_completed", itemId: item.id, text });
|
|
310
|
+
return { events, finalText: text };
|
|
311
|
+
}
|
|
126
312
|
return { events };
|
|
127
313
|
}
|
|
128
314
|
case "reasoning": {
|
|
@@ -151,23 +337,18 @@ export function mapCodexNotification(method, params) {
|
|
|
151
337
|
return { events };
|
|
152
338
|
case "turn/started":
|
|
153
339
|
return { events };
|
|
154
|
-
case "turn/completed":
|
|
340
|
+
case "turn/completed":
|
|
341
|
+
case "turn/failed": {
|
|
155
342
|
const turnPayload = typed.params?.turn;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
events,
|
|
166
|
-
fatalError: turnError(turnPayload.error, "Codex turn was interrupted"),
|
|
167
|
-
turnCompleted: true,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
return { events, turnCompleted: true };
|
|
343
|
+
const fatalError = terminalTurnError(turnPayload, typed.method);
|
|
344
|
+
const turnInterrupted = typed.method === "turn/completed" &&
|
|
345
|
+
["interrupted", "cancelled", "canceled"].includes(codexTurnStatus(turnPayload) ?? "");
|
|
346
|
+
return {
|
|
347
|
+
events,
|
|
348
|
+
...(fatalError ? { fatalError } : {}),
|
|
349
|
+
turnCompleted: true,
|
|
350
|
+
...(turnInterrupted ? { turnInterrupted: true } : {}),
|
|
351
|
+
};
|
|
171
352
|
}
|
|
172
353
|
case "turn/plan/updated": {
|
|
173
354
|
const planParams = typed.params;
|
|
@@ -195,6 +376,17 @@ export function mapCodexNotification(method, params) {
|
|
|
195
376
|
}
|
|
196
377
|
return { events };
|
|
197
378
|
}
|
|
379
|
+
case "item/plan/delta": {
|
|
380
|
+
const delta = typed.params.delta ?? "";
|
|
381
|
+
if (delta) {
|
|
382
|
+
events.push({
|
|
383
|
+
type: "token",
|
|
384
|
+
text: delta,
|
|
385
|
+
metadata: { source: "app_server", itemId: typed.params.itemId },
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
return { events };
|
|
389
|
+
}
|
|
198
390
|
case "item/reasoning/summaryTextDelta":
|
|
199
391
|
case "item/reasoning/textDelta": {
|
|
200
392
|
const p = typed.params;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CodexLineageRuntime } from "../codex-home.js";
|
|
2
|
+
export interface McpStartupPlan {
|
|
3
|
+
servers: string[];
|
|
4
|
+
settleTimeoutMs: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Read the MCP table fields needed for startup tracking. Provider config stays
|
|
8
|
+
* authoritative: malformed TOML disables the synthesized status rather than
|
|
9
|
+
* preventing the native CLI from reporting its own startup error.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseMcpStartupToml(input: string): McpStartupPlan | null;
|
|
12
|
+
/** Enabled Provider-configured MCP servers and their synthesized settle window. */
|
|
13
|
+
export declare function readMcpStartupPlan(runtimeHome: string, runtime: CodexLineageRuntime): McpStartupPlan | null;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { parse } from "smol-toml";
|
|
4
|
+
const DEFAULT_SERVER_TIMEOUT_MS = 10_000;
|
|
5
|
+
const SETTLE_GRACE_MS = 15_000;
|
|
6
|
+
const MAX_SETTLE_TIMEOUT_MS = 240_000;
|
|
7
|
+
function configNames(runtime) {
|
|
8
|
+
return runtime === "codex"
|
|
9
|
+
? ["config.toml"]
|
|
10
|
+
: ["traecli.toml"];
|
|
11
|
+
}
|
|
12
|
+
function isTomlTable(value) {
|
|
13
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Read the MCP table fields needed for startup tracking. Provider config stays
|
|
17
|
+
* authoritative: malformed TOML disables the synthesized status rather than
|
|
18
|
+
* preventing the native CLI from reporting its own startup error.
|
|
19
|
+
*/
|
|
20
|
+
export function parseMcpStartupToml(input) {
|
|
21
|
+
let config;
|
|
22
|
+
try {
|
|
23
|
+
config = parse(input);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const servers = config.mcp_servers;
|
|
29
|
+
if (!isTomlTable(servers))
|
|
30
|
+
return null;
|
|
31
|
+
const enabled = Object.entries(servers)
|
|
32
|
+
.filter(([name, server]) => Boolean(name) && isTomlTable(server) && server.enabled !== false)
|
|
33
|
+
.sort(([left], [right]) => left.localeCompare(right));
|
|
34
|
+
if (enabled.length === 0)
|
|
35
|
+
return null;
|
|
36
|
+
const slowest = Math.max(DEFAULT_SERVER_TIMEOUT_MS, ...enabled.map(([, server]) => {
|
|
37
|
+
if (!isTomlTable(server))
|
|
38
|
+
return DEFAULT_SERVER_TIMEOUT_MS;
|
|
39
|
+
const seconds = server.startup_timeout_sec;
|
|
40
|
+
return typeof seconds === "number" && Number.isFinite(seconds) && seconds > 0
|
|
41
|
+
? seconds * 1_000
|
|
42
|
+
: DEFAULT_SERVER_TIMEOUT_MS;
|
|
43
|
+
}));
|
|
44
|
+
return {
|
|
45
|
+
servers: enabled.map(([name]) => name),
|
|
46
|
+
settleTimeoutMs: Math.min(slowest + SETTLE_GRACE_MS, MAX_SETTLE_TIMEOUT_MS),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Enabled Provider-configured MCP servers and their synthesized settle window. */
|
|
50
|
+
export function readMcpStartupPlan(runtimeHome, runtime) {
|
|
51
|
+
for (const name of configNames(runtime)) {
|
|
52
|
+
try {
|
|
53
|
+
const plan = parseMcpStartupToml(readFileSync(join(runtimeHome, name), "utf8"));
|
|
54
|
+
if (plan)
|
|
55
|
+
return plan;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Missing/unreadable config: the Provider still owns startup; Rynx simply
|
|
59
|
+
// cannot synthesize per-server progress for this launch.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface RuntimeProcessEntry {
|
|
2
|
+
pid: number;
|
|
3
|
+
pgid: number;
|
|
4
|
+
ownerPid: number;
|
|
5
|
+
/** OS process-start identity. Unlike a PID, this cannot silently refer to a
|
|
6
|
+
* later process after the launcher crashes and the PID is recycled. */
|
|
7
|
+
ownerIdentity?: string;
|
|
8
|
+
sessionTag: string;
|
|
9
|
+
stateDir: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function runtimeProcessRegistryPath(): string;
|
|
12
|
+
export declare function runtimeProcessTagArg(sessionTag: string): string;
|
|
13
|
+
export declare function runtimeProcessArgv0(cliPath: string, sessionTag: string): string;
|
|
14
|
+
export declare function withRuntimeProcessStateArg(baseArgs: string[], stateDir: string): string[];
|
|
15
|
+
export declare function registerRuntimeProcess(entry: RuntimeProcessEntry, registryPath?: string): void;
|
|
16
|
+
export declare function unregisterRuntimeProcess(sessionTag: string, registryPath?: string): void;
|
|
17
|
+
export interface RuntimeProcessReconcileOptions {
|
|
18
|
+
registryPath?: string;
|
|
19
|
+
ownerAlive?: (pid: number) => boolean;
|
|
20
|
+
ownerIdentity?: (pid: number) => string;
|
|
21
|
+
childAlive?: (pid: number) => boolean;
|
|
22
|
+
commandLine?: (pid: number) => string;
|
|
23
|
+
terminateGroup?: (pgid: number) => boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare function reconcileRuntimeProcesses(opts?: RuntimeProcessReconcileOptions): number;
|
|
26
|
+
export declare function reapRuntimeProcessesForStateDir(stateDir: string, opts?: RuntimeProcessStateReapOptions): number;
|
|
27
|
+
export interface RuntimeProcessStateReapOptions {
|
|
28
|
+
processListing?: () => string;
|
|
29
|
+
currentPgid?: () => number;
|
|
30
|
+
childAlive?: (pid: number) => boolean;
|
|
31
|
+
signalGroup?: (pgid: number, signal: NodeJS.Signals) => boolean;
|
|
32
|
+
graceMs?: number;
|
|
33
|
+
}
|
|
34
|
+
/** Return an OS process-start identity used to distinguish PID reuse. */
|
|
35
|
+
export declare function runtimeProcessOwnerIdentity(pid: number): string;
|
|
36
|
+
export declare function newRuntimeProcessTag(): string;
|