@ours.network/codex 1.2.0-nightly.4 → 1.2.0-nightly.5

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.
@@ -1,308 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { readFileSync } from 'node:fs';
3
- import { homedir } from 'node:os';
4
- import { dirname, join } from 'node:path';
5
-
6
- import { Client } from '@modelcontextprotocol/sdk/client/index.js';
7
- import { StreamableHTTPClientTransport, StreamableHTTPError } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
8
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
9
- import { ErrorCode, McpError, ResultSchema } from '@modelcontextprotocol/sdk/types.js';
10
- import {
11
- completeHostFileCall,
12
- endNativeSessionAtRoot,
13
- hostProfileFromEnv,
14
- nativeClientForAtRoot,
15
- nativeSessionRecordPath,
16
- prepareHostFileCall,
17
- } from '../../core/src/host-client/index.ts';
18
-
19
- class NetworkTransportError extends Error {
20
- constructor(cause) {
21
- super(`Network transport failed: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
22
- this.name = 'NetworkTransportError';
23
- }
24
- }
25
-
26
- function isMcpSessionLoss(error) {
27
- return error instanceof NetworkTransportError
28
- || error instanceof StreamableHTTPError
29
- || (error instanceof McpError && error.code === ErrorCode.ConnectionClosed);
30
- }
31
-
32
- function threadSelector(frame) {
33
- const value = frame?.params?._meta?.threadId;
34
- if (typeof value !== 'string' || !value || value.trim() !== value) {
35
- throw new Error('Native session metadata is missing or invalid for this host-profile tool call.');
36
- }
37
- return value;
38
- }
39
-
40
- function defaultSessionSelector(frame) {
41
- return frame.method === 'tools/call' ? threadSelector(frame) : '__discovery__';
42
- }
43
-
44
- export function createNetworkBridge({ sessionFactory, send, selectorFor = defaultSessionSelector }) {
45
- const sessions = new Map();
46
- const inFlight = new Map();
47
- const sessionFor = async (selector) => {
48
- let promise = sessions.get(selector);
49
- if (!promise) {
50
- promise = Promise.resolve(sessionFactory(selector, {
51
- onClose: () => { if (sessions.get(selector) === promise) sessions.delete(selector); },
52
- }));
53
- sessions.set(selector, promise);
54
- promise.catch(() => { if (sessions.get(selector) === promise) sessions.delete(selector); });
55
- }
56
- return promise;
57
- };
58
- const evictSession = async (selector, session) => {
59
- const cached = sessions.get(selector);
60
- if (cached && await cached.catch(() => undefined) === session && sessions.get(selector) === cached) {
61
- sessions.delete(selector);
62
- }
63
- await session.close().catch(() => {});
64
- };
65
- const reply = (id, value, error = false) => send(error
66
- ? { jsonrpc: '2.0', id, error: { code: -32000, message: value instanceof Error ? value.message : String(value) } }
67
- : { jsonrpc: '2.0', id, result: value });
68
- const handle = async (frame) => {
69
- if (!frame || typeof frame !== 'object' || Array.isArray(frame)) return;
70
- if (frame.method === 'notifications/initialized') return;
71
- if (frame.method === 'notifications/cancelled') {
72
- inFlight.get(frame.params?.requestId)?.abort(frame.params?.reason);
73
- return;
74
- }
75
- if (frame.id === undefined || frame.id === null) {
76
- const session = await sessionFor(selectorFor(frame));
77
- await session.notification(frame);
78
- return;
79
- }
80
- const controller = new AbortController();
81
- inFlight.set(frame.id, controller);
82
- try {
83
- const selector = selectorFor(frame);
84
- const session = await sessionFor(selector);
85
- controller.signal.throwIfAborted();
86
- if (frame.method === 'initialize') {
87
- await reply(frame.id, session.initializeResult);
88
- return;
89
- }
90
- const requestFrame = frame.method === 'tools/call' && session.fileClient
91
- ? await prepareHostFileCall(session.fileClient, frame, controller.signal)
92
- : frame;
93
- controller.signal.throwIfAborted();
94
- let result;
95
- try {
96
- result = await session.request(
97
- { method: requestFrame.method, params: requestFrame.params ?? {} },
98
- { signal: controller.signal },
99
- );
100
- } catch (error) {
101
- if (isMcpSessionLoss(error)) await evictSession(selector, session);
102
- throw error;
103
- }
104
- controller.signal.throwIfAborted();
105
- let response = { jsonrpc: '2.0', id: frame.id, result };
106
- if (frame.method === 'tools/call' && session.fileClient) {
107
- response = await completeHostFileCall(session.fileClient, frame, response, controller.signal);
108
- }
109
- controller.signal.throwIfAborted();
110
- await send(response);
111
- } catch (error) {
112
- await reply(frame.id, error, true);
113
- } finally {
114
- if (inFlight.get(frame.id) === controller) inFlight.delete(frame.id);
115
- }
116
- };
117
- const close = async () => {
118
- for (const controller of inFlight.values()) controller.abort('stdio closed');
119
- await Promise.allSettled([...sessions.values()].map(async (session) => (await session).close()));
120
- sessions.clear();
121
- };
122
- return { handle, close };
123
- }
124
-
125
- export async function runNetworkProxy({
126
- env = process.env,
127
- input = process.stdin,
128
- stdio = new StdioServerTransport(),
129
- profile: profileValue,
130
- hostRecordRoot: hostRecordRootValue,
131
- sessionFactory: sessionFactoryValue,
132
- selectorFor,
133
- installSignalHandlers = true,
134
- } = {}) {
135
- const profile = profileValue ?? hostProfileFromEnv(env);
136
- if (!profile && !sessionFactoryValue) throw new Error('A selected host network profile is required.');
137
- const configPath = env.OURS_MCP_CONFIG || join(env.HOME || homedir(), '.ours-mcp', 'config.json');
138
- const hostRecordRoot = hostRecordRootValue ?? dirname(configPath);
139
- const sessionFactory = sessionFactoryValue ?? createHostSessionFactory({
140
- profile,
141
- hostRecordRoot,
142
- send: (frame) => stdio.send(frame),
143
- });
144
- const bridge = createNetworkBridge({ sessionFactory, send: (frame) => stdio.send(frame), selectorFor });
145
- const tasks = new Set();
146
- const accept = (raw) => {
147
- const values = Array.isArray(raw) ? raw : [raw];
148
- const task = Promise.all(values.map((frame) => bridge.handle(frame)))
149
- .catch((error) => process.stderr.write(`ours: network proxy failed: ${error.message}\n`))
150
- .finally(() => tasks.delete(task));
151
- tasks.add(task);
152
- };
153
- stdio.onmessage = accept;
154
- stdio.onerror = (error) => process.stderr.write(`ours: network stdio failed: ${error.message}\n`);
155
- await stdio.start();
156
-
157
- await new Promise((resolve) => {
158
- let stopped = false;
159
- const finish = () => {
160
- if (stopped) return;
161
- stopped = true;
162
- input.off('end', finish);
163
- input.off('close', finish);
164
- if (installSignalHandlers) {
165
- process.off('SIGINT', finish);
166
- process.off('SIGTERM', finish);
167
- }
168
- resolve();
169
- };
170
- input.once('end', finish);
171
- input.once('close', finish);
172
- if (installSignalHandlers) {
173
- process.once('SIGINT', finish);
174
- process.once('SIGTERM', finish);
175
- }
176
- });
177
- await bridge.close();
178
- await Promise.allSettled([...tasks]);
179
- await stdio.close();
180
- }
181
- export { hostProfileFromEnv };
182
-
183
- export function networkFetchForOwner(profile, ownerInstanceId, { fetchImpl = globalThis.fetch } = {}) {
184
- return async (input, init = {}) => {
185
- const token = readFileSync(profile.credentialPath, 'utf8').trim();
186
- if (!token) throw new Error(`Credential file ${profile.credentialPath} is empty.`);
187
- const headers = new Headers(init.headers);
188
- headers.set('x-ours-api-token', token);
189
- headers.set('x-ours-lease-token', ownerInstanceId);
190
- headers.set('x-ours-session-mode', 'external');
191
- try {
192
- return await fetchImpl(input, { ...init, headers, redirect: 'error' });
193
- } catch (error) {
194
- throw new NetworkTransportError(error);
195
- }
196
- };
197
- }
198
-
199
- export async function createMcpNetworkSession({
200
- profile,
201
- ownerInstanceId,
202
- fileClient,
203
- onNotification,
204
- onClose,
205
- ClientClass = Client,
206
- TransportClass = StreamableHTTPClientTransport,
207
- resultSchema = ResultSchema,
208
- clientInfo = { name: 'ours-codex', version: '2.0.0' },
209
- }) {
210
- const client = new ClientClass(clientInfo);
211
- client.fallbackNotificationHandler = async (notification) => onNotification?.(notification);
212
- let invalidated = false;
213
- let closing;
214
- const evict = () => {
215
- if (invalidated) return;
216
- invalidated = true;
217
- onClose?.();
218
- };
219
- const invalidate = async () => {
220
- evict();
221
- closing ??= Promise.resolve(client.close()).catch(() => {});
222
- await closing;
223
- };
224
- client.onclose = evict;
225
- client.onerror = (error) => { if (isMcpSessionLoss(error)) void invalidate(); };
226
- const ownerHeaders = {
227
- 'x-ours-lease-token': ownerInstanceId,
228
- 'x-ours-session-mode': 'external',
229
- };
230
- const endpoint = new URL('/mcp', profile.endpoint);
231
- const transport = new TransportClass(endpoint, {
232
- fetch: networkFetchForOwner(profile, ownerInstanceId),
233
- requestInit: { headers: ownerHeaders, redirect: 'error' },
234
- reconnectionOptions: {
235
- initialReconnectionDelay: 1000,
236
- maxReconnectionDelay: 30_000,
237
- reconnectionDelayGrowFactor: 1.5,
238
- maxRetries: 5,
239
- },
240
- });
241
- await client.connect(transport);
242
- const instructions = client.getInstructions?.();
243
- const initializeResult = {
244
- protocolVersion: '2025-03-26',
245
- capabilities: client.getServerCapabilities?.() ?? {},
246
- serverInfo: client.getServerVersion?.() ?? { name: 'ours', version: 'unknown' },
247
- ...(instructions ? { instructions } : {}),
248
- };
249
- return {
250
- client,
251
- fileClient,
252
- initializeResult,
253
- request: async (request, options) => {
254
- try {
255
- return await client.request(request, resultSchema, options);
256
- } catch (error) {
257
- if (isMcpSessionLoss(error)) await invalidate();
258
- throw error;
259
- }
260
- },
261
- notification: (notification) => client.notification(notification),
262
- close: invalidate,
263
- };
264
- }
265
-
266
- async function nativeOwnerForAtRoot(profile, nativeSessionId, hostRecordRoot) {
267
- const path = nativeSessionRecordPath(hostRecordRoot, profile.expectedInstanceId, nativeSessionId);
268
- const record = JSON.parse(readFileSync(path, 'utf8'));
269
- if (record?.state !== 'active' || typeof record.ownerInstanceId !== 'string') {
270
- throw new Error(`Native session record at ${path} is not active.`);
271
- }
272
- return record.ownerInstanceId;
273
- }
274
-
275
- export function createHostSessionFactory({
276
- profile,
277
- hostRecordRoot,
278
- send,
279
- nativeClientForImpl = nativeClientForAtRoot,
280
- ownerForImpl = nativeOwnerForAtRoot,
281
- createSessionImpl = createMcpNetworkSession,
282
- }) {
283
- return async (selector, { onClose } = {}) => {
284
- const discovery = selector === '__discovery__';
285
- const fileClient = discovery
286
- ? undefined
287
- : await nativeClientForImpl(profile, selector, hostRecordRoot);
288
- const ownerInstanceId = discovery
289
- ? randomUUID()
290
- : await ownerForImpl(profile, selector, hostRecordRoot);
291
- return createSessionImpl({
292
- profile,
293
- ownerInstanceId,
294
- fileClient,
295
- onNotification: send,
296
- onClose,
297
- });
298
- };
299
- }
300
-
301
- export async function endNetworkNativeSession({
302
- profile,
303
- nativeSessionId,
304
- hostRecordRoot,
305
- endImpl = endNativeSessionAtRoot,
306
- }) {
307
- await endImpl(profile, nativeSessionId, hostRecordRoot);
308
- }