@hellolynk-ai/codex-plugin 0.0.0-stage → 0.2.100
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/.agents/plugins/marketplace.json +20 -0
- package/LICENSE +92 -0
- package/README.md +101 -2
- package/bin/hellolynk-client-setup.mjs +2748 -0
- package/bin/hellolynk-mcp-config-shape.mjs +23 -0
- package/hellolynk-client-pack-manifest.json +619 -0
- package/hellolynk-client-pack-pin.json +4 -0
- package/package.json +58 -4
- package/plugins/hellolynk-codex/.app.json +3 -0
- package/plugins/hellolynk-codex/.codex-plugin/plugin.json +38 -0
- package/plugins/hellolynk-codex/.mcp.json +19 -0
- package/plugins/hellolynk-codex/assets/hellolynk-mark.png +0 -0
- package/plugins/hellolynk-codex/package.json +11 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-account.mjs +180 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-core.mjs +770 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-profile.mjs +191 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-host-auth.mjs +347 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-host-protected-store.mjs +2721 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-local-process-lock.mjs +157 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-neutral-host-installation-store.mjs +765 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-platform-credential-store.mjs +773 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-runtime-safety.cjs +471 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/platform/windows/hellolynk-host-protected-store.ps1 +2866 -0
- package/plugins/hellolynk-codex/runtime/connect-activate.mjs +837 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-agent-configurator.mjs +701 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-account.mjs +180 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-core.mjs +770 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-profile.mjs +191 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-entry.mjs +31 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-runtime.mjs +600 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update.mjs +453 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-contact-request-e2ee.mjs +867 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-device-enrollment.mjs +435 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-direct-text-e2ee-client.mjs +1964 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-direct-text-e2ee.mjs +1207 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-host-auth.mjs +347 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-host-protected-store.mjs +2721 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-host-transport-client.mjs +946 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-host.mjs +2374 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-human-confirmation-ledger.mjs +256 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-human-relationship-controller.mjs +1234 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-inbox-read-state.mjs +11 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-indexed-message-store.mjs +1558 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-instance-conversation.mjs +185 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-contact-requests.mjs +1733 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-contacts.mjs +1228 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-data-migration.mjs +766 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-data-protection.mjs +747 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-identity-trust.mjs +274 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-messaging.mjs +3043 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-peer-transport.mjs +103 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-process-lock.mjs +157 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-model-inbox.mjs +230 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-model-notices.cjs +60 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-addressing-tools.cjs +231 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-conversations.mjs +280 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-human-confirmation-broker.mjs +1036 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-inbox-runtime.mjs +553 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-notice.mjs +50 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-reply-routes.mjs +96 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-worker-lease.mjs +84 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-assembly.mjs +955 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-installation-store.mjs +765 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-launcher.mjs +665 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-process.mjs +4374 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-store-adapters.mjs +548 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-workbench-read-model.mjs +1365 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-platform-credential-store.mjs +773 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-relay-client-cli.mjs +232 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-release-retention.mjs +277 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-runtime-safety.cjs +471 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-workbench-action-grants.mjs +457 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-workbench-connection.mjs +234 -0
- package/plugins/hellolynk-codex/runtime/neutral-host-bootstrap.mjs +64 -0
- package/plugins/hellolynk-codex/runtime/neutral-host-release-entry.mjs +70 -0
- package/plugins/hellolynk-codex/runtime/neutral-host-release-loader.mjs +156 -0
- package/plugins/hellolynk-codex/runtime/neutral-host-release-manifest.json +358 -0
- package/plugins/hellolynk-codex/runtime/neutral-host-release-pin.json +4 -0
- package/plugins/hellolynk-codex/runtime/platform/windows/configure-agent.ps1 +281 -0
- package/plugins/hellolynk-codex/runtime/platform/windows/credential-store.ps1 +844 -0
- package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-host-protected-store.ps1 +2866 -0
- package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-human-confirmation-ledger.ps1 +155 -0
- package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-local-data-key.ps1 +300 -0
- package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-native-human-confirmation-broker.ps1 +216 -0
- package/plugins/hellolynk-codex/runtime/workbench/candidate-bootstrap.mjs +54 -0
- package/plugins/hellolynk-codex/runtime/workbench/contract.mjs +1314 -0
- package/plugins/hellolynk-codex/runtime/workbench/hellolynk-workbench-build.json +215 -0
- package/plugins/hellolynk-codex/runtime/workbench/host-capabilities.mjs +80 -0
- package/plugins/hellolynk-codex/runtime/workbench/i18n.mjs +1127 -0
- package/plugins/hellolynk-codex/runtime/workbench/loopback-surface.mjs +740 -0
- package/plugins/hellolynk-codex/runtime/workbench/mcp-app-bundle.mjs +97 -0
- package/plugins/hellolynk-codex/runtime/workbench/service.mjs +485 -0
- package/plugins/hellolynk-codex/runtime/workbench/state.mjs +421 -0
- package/plugins/hellolynk-codex/runtime/workbench/transports.mjs +171 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/app.mjs +6120 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/connection-form.mjs +155 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/hellolynk-mark.png +0 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/http-transport.mjs +247 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/arrow-left.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/close.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/contacts.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/inbox.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/refresh.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/search.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/security.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/settings.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/index.html +139 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/loopback-entry.mjs +26 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/mcp-app-client.mjs +151 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/mcp-app-entry.mjs +452 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/styles.css +686 -0
- package/plugins/hellolynk-codex/server/codex-neutral-production.mjs +520 -0
- package/plugins/hellolynk-codex/server/mcp-proxy.mjs +306 -0
- package/plugins/hellolynk-codex/server/mcp-server.mjs +1193 -0
- package/plugins/hellolynk-codex/server/native-conversation-directory.mjs +33 -0
- package/plugins/hellolynk-codex/server/native-reply-routes.mjs +32 -0
- package/plugins/hellolynk-codex/server/native-task-client.mjs +382 -0
- package/plugins/hellolynk-codex/server/native-task-runtime.mjs +8 -0
- package/plugins/hellolynk-codex/server/neutral-host-adapter.mjs +649 -0
- package/plugins/hellolynk-codex/server/workbench-runtime.mjs +180 -0
- package/plugins/hellolynk-codex/skills/hellolynk-open/SKILL.md +11 -0
- package/plugins/hellolynk-codex/skills/hellolynk-open/agents/openai.yaml +4 -0
- package/tools/hellolynk-activate-neutral-host-clients.mjs +837 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createClientAccountActions } from "../runtime/core/hellolynk-client-account.mjs";
|
|
5
|
+
import { createWorkbenchConnection, workbenchConnectionError } from "../runtime/core/hellolynk-workbench-connection.mjs";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { runStdioMcpServer } from "./mcp-server.mjs";
|
|
8
|
+
import {
|
|
9
|
+
createCodexNeutralHostAdapter,
|
|
10
|
+
isCodexNeutralHostUnavailable,
|
|
11
|
+
resolveHelloLynkHostMode,
|
|
12
|
+
} from "./neutral-host-adapter.mjs";
|
|
13
|
+
import { createCodexNeutralProduction } from "./codex-neutral-production.mjs";
|
|
14
|
+
import { bootstrapCodexWorkbenchRuntime } from "./workbench-runtime.mjs";
|
|
15
|
+
import { createNativeTaskRuntime } from "./native-task-runtime.mjs";
|
|
16
|
+
import { createNativeTaskClient, inspectCodexExecutorMetadata } from "./native-task-client.mjs";
|
|
17
|
+
import { rememberNativeReplyRoute, rememberNativeCaller, readNativeCaller } from "./native-reply-routes.mjs";
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
export async function runMcpProxy(options = {}) {
|
|
21
|
+
const input = options.input || process.stdin;
|
|
22
|
+
const output = options.output || process.stdout;
|
|
23
|
+
const mode = options.mode == null
|
|
24
|
+
? resolveHelloLynkHostMode(options.env || process.env)
|
|
25
|
+
: resolveHelloLynkHostMode({ HELLOLYNK_HOST_MODE: options.mode });
|
|
26
|
+
if (mode === "neutral") {
|
|
27
|
+
// Codex builds the optional MCP tool catalog on a short deadline. Resolving the
|
|
28
|
+
// protected binding and cold-starting the Host before `initialize` can exceed
|
|
29
|
+
// that deadline, causing an otherwise healthy plugin to be omitted for the
|
|
30
|
+
// whole task. Keep protocol discovery immediate, then warm the Host after
|
|
31
|
+
// the handshake response even when Codex reuses its cached tool catalog.
|
|
32
|
+
const neutralAdapter = options.neutralAdapter || createLazyNeutralAdapter(options);
|
|
33
|
+
const nativeTasksEnabled = (options.nativeTaskClient !== undefined || options.createNativeTaskClient ||
|
|
34
|
+
(!options.capabilityProvider && !options.createNeutralAdapter && !options.neutralAdapter
|
|
35
|
+
&& !options.createNeutralProduction && !options.hostLauncher && !options.discoverNeutralHost
|
|
36
|
+
&& !options.requestNeutralHost));
|
|
37
|
+
let nativeTasks = null;
|
|
38
|
+
let nativeCallerThreadId = null;
|
|
39
|
+
let proxyClosed = false;
|
|
40
|
+
let nativeTransition = Promise.resolve();
|
|
41
|
+
const startNativeTasks = async (executorMetadata = null) => {
|
|
42
|
+
if (proxyClosed || !nativeTasksEnabled) return;
|
|
43
|
+
const context = inspectCodexExecutorMetadata(executorMetadata);
|
|
44
|
+
if (context.conflict) return;
|
|
45
|
+
const observedThreadId = context.threadId;
|
|
46
|
+
let registeredCallerThreadId = null;
|
|
47
|
+
if (observedThreadId || !nativeTasks) {
|
|
48
|
+
try {
|
|
49
|
+
const scope = await neutralAdapter.nativeTaskScope?.();
|
|
50
|
+
if (scope && observedThreadId) await rememberNativeCaller(scope, observedThreadId);
|
|
51
|
+
else if (scope) registeredCallerThreadId = await readNativeCaller(scope);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
// Persistence is optional for a live executor. Keep its existing
|
|
54
|
+
// receive path usable, while leaving a bounded diagnostic on stderr.
|
|
55
|
+
process.stderr.write(`${JSON.stringify({ operation: "native_caller_cache", error: safeCode(error) })}\n`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (nativeCallerThreadId && observedThreadId && nativeCallerThreadId !== observedThreadId) {
|
|
59
|
+
// A long-lived MCP process may serve several legitimate Codex tasks.
|
|
60
|
+
// Finish the old worker before adopting the next executor-owned context.
|
|
61
|
+
await nativeTasks?.close();
|
|
62
|
+
nativeTasks = null;
|
|
63
|
+
nativeCallerThreadId = null;
|
|
64
|
+
}
|
|
65
|
+
if (proxyClosed) return;
|
|
66
|
+
if (!nativeTasks) {
|
|
67
|
+
const client = options.nativeTaskClient === undefined
|
|
68
|
+
? (options.createNativeTaskClient || createNativeTaskClient)({ env: options.env || process.env, executorMetadata, registeredCallerThreadId })
|
|
69
|
+
: options.nativeTaskClient;
|
|
70
|
+
if (!client) return;
|
|
71
|
+
nativeCallerThreadId = observedThreadId || client.callerThreadId;
|
|
72
|
+
nativeTasks = (options.createNativeTaskRuntime || createNativeTaskRuntime)({ adapter: neutralAdapter, native: client });
|
|
73
|
+
}
|
|
74
|
+
await nativeTasks.start();
|
|
75
|
+
};
|
|
76
|
+
const queueNativeStart = (metadata) => {
|
|
77
|
+
const transition = nativeTransition.then(() => startNativeTasks(metadata));
|
|
78
|
+
nativeTransition = transition.catch(() => {});
|
|
79
|
+
return transition;
|
|
80
|
+
};
|
|
81
|
+
const workbenchCandidate = await optionalWorkbenchCandidate(options, neutralAdapter);
|
|
82
|
+
try {
|
|
83
|
+
return await runStdioMcpServer({
|
|
84
|
+
input,
|
|
85
|
+
output,
|
|
86
|
+
neutralAdapter,
|
|
87
|
+
readNativeDeliveryStatus: () => nativeTasks?.readDeliveryStatus?.(),
|
|
88
|
+
async prepareAuthorizedWrite(metadata, message) {
|
|
89
|
+
const context = inspectCodexExecutorMetadata(metadata);
|
|
90
|
+
// A shared MCP process's inherited thread may be stale. Registration
|
|
91
|
+
// needs the exact current executor envelope, never its environment.
|
|
92
|
+
if (!nativeTasksEnabled || context.conflict || !context.threadId) return;
|
|
93
|
+
const scope = await neutralAdapter.nativeTaskScope();
|
|
94
|
+
await rememberNativeReplyRoute(scope, { ...message, threadId: context.threadId });
|
|
95
|
+
},
|
|
96
|
+
onProtocolReady: async () => {
|
|
97
|
+
await neutralAdapter.prewarm();
|
|
98
|
+
await queueNativeStart().catch(() => {});
|
|
99
|
+
},
|
|
100
|
+
onExecutorToolCall: (metadata) => queueNativeStart(metadata),
|
|
101
|
+
...(workbenchCandidate ? { workbenchCandidate } : {}),
|
|
102
|
+
});
|
|
103
|
+
} finally {
|
|
104
|
+
proxyClosed = true;
|
|
105
|
+
await nativeTransition;
|
|
106
|
+
await nativeTasks?.close();
|
|
107
|
+
await neutralAdapter.close();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function createLazyNeutralAdapter(options = {}) {
|
|
113
|
+
let adapterPromise = null;
|
|
114
|
+
let currentAdapter = null;
|
|
115
|
+
let closePromise = null;
|
|
116
|
+
let closed = false;
|
|
117
|
+
const adapterCloses = new WeakMap();
|
|
118
|
+
const retiring = new Set();
|
|
119
|
+
const accounts = createClientAccountActions({ ...options, client: "codex" });
|
|
120
|
+
let accountRevision = accounts.readAccountState().revision;
|
|
121
|
+
let accountReset = null;
|
|
122
|
+
|
|
123
|
+
const closeAdapter = (adapter) => {
|
|
124
|
+
if (!adapterCloses.has(adapter)) {
|
|
125
|
+
const operation = Promise.resolve().then(() => adapter.close?.());
|
|
126
|
+
adapterCloses.set(adapter, operation);
|
|
127
|
+
retiring.add(operation);
|
|
128
|
+
void operation.then(() => retiring.delete(operation), () => retiring.delete(operation));
|
|
129
|
+
}
|
|
130
|
+
return adapterCloses.get(adapter);
|
|
131
|
+
};
|
|
132
|
+
const retireAdapter = async (adapter, pendingAdapter) => {
|
|
133
|
+
if (adapterPromise === pendingAdapter) { adapterPromise = null; currentAdapter = null; }
|
|
134
|
+
await closeAdapter(adapter).catch(() => {});
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const resolveAdapter = async (allowCachedRecovery = true) => {
|
|
138
|
+
const nextRevision = accounts.readAccountState().revision;
|
|
139
|
+
if (!accountReset && accountRevision !== nextRevision) {
|
|
140
|
+
const previousFlight = adapterPromise;
|
|
141
|
+
adapterPromise = null; currentAdapter = null; accountRevision = nextRevision;
|
|
142
|
+
accountReset = Promise.resolve().then(async () => {
|
|
143
|
+
const previous = await previousFlight?.catch(() => null);
|
|
144
|
+
if (previous) await closeAdapter(previous).catch(() => {});
|
|
145
|
+
}).finally(() => { accountReset = null; });
|
|
146
|
+
}
|
|
147
|
+
if (accountReset) await accountReset;
|
|
148
|
+
const generation = accountRevision;
|
|
149
|
+
if (generation !== accounts.readAccountState().revision) throw codeError("workbench_account_changed");
|
|
150
|
+
if (!adapterPromise && retiring.size) await Promise.allSettled([...retiring]);
|
|
151
|
+
if (closed) throw codeError("neutral_adapter_closed");
|
|
152
|
+
const cachedAdapter = currentAdapter;
|
|
153
|
+
if (!adapterPromise) adapterPromise = initializeNeutralAdapter(options);
|
|
154
|
+
const pendingAdapter = adapterPromise;
|
|
155
|
+
let adapter;
|
|
156
|
+
try {
|
|
157
|
+
adapter = await pendingAdapter;
|
|
158
|
+
} catch (error) {
|
|
159
|
+
// Keep concurrent first calls single-flight, but do not retain a rejected
|
|
160
|
+
// initialization forever. The failing call remains fail-closed; only a
|
|
161
|
+
// later tool call may start a fresh initialization attempt.
|
|
162
|
+
if (adapterPromise === pendingAdapter) adapterPromise = null;
|
|
163
|
+
throw workbenchConnectionError(error);
|
|
164
|
+
}
|
|
165
|
+
if (closed || generation !== accounts.readAccountState().revision) {
|
|
166
|
+
await retireAdapter(adapter, pendingAdapter);
|
|
167
|
+
throw codeError("neutral_adapter_closed");
|
|
168
|
+
}
|
|
169
|
+
if (adapter?.state === "dead" || adapter?.state === "closed") {
|
|
170
|
+
await retireAdapter(adapter, pendingAdapter);
|
|
171
|
+
// Only an already usable cached attachment gets one replacement. A new
|
|
172
|
+
// attachment that is dead on arrival fails this operation without a loop.
|
|
173
|
+
if (allowCachedRecovery && adapter === cachedAdapter) return resolveAdapter(false);
|
|
174
|
+
throw codeError("neutral_host_adapter_unavailable");
|
|
175
|
+
}
|
|
176
|
+
if (adapterPromise === pendingAdapter) currentAdapter = adapter;
|
|
177
|
+
return { adapter, pendingAdapter };
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const invoke = async (method, args, minimumArity = 0) => {
|
|
181
|
+
const { adapter, pendingAdapter } = await resolveAdapter();
|
|
182
|
+
if (typeof adapter?.[method] !== "function" || adapter[method].length < minimumArity) {
|
|
183
|
+
throw codeError("neutral_adapter_invalid");
|
|
184
|
+
}
|
|
185
|
+
try { return await adapter[method](...args); }
|
|
186
|
+
catch (error) {
|
|
187
|
+
if (adapter.state === "dead" || isCodexNeutralHostUnavailable(error)) {
|
|
188
|
+
await retireAdapter(adapter, pendingAdapter);
|
|
189
|
+
}
|
|
190
|
+
// A failed command is never replayed. Only a later operation reconnects.
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
const forward = (method) => (...args) => invoke(method, args);
|
|
195
|
+
|
|
196
|
+
return Object.freeze({
|
|
197
|
+
mode: "neutral",
|
|
198
|
+
prewarm() {
|
|
199
|
+
// Discovery must stay immediate. Start privileged initialization only in a
|
|
200
|
+
// later microtask, and deliberately absorb a warm-up failure: an explicit
|
|
201
|
+
// tool call still goes through resolveAdapter(), which clears a rejected
|
|
202
|
+
// attempt and can retry normally.
|
|
203
|
+
return Promise.resolve()
|
|
204
|
+
.then(() => resolveAdapter())
|
|
205
|
+
.then(() => undefined, () => undefined);
|
|
206
|
+
},
|
|
207
|
+
modelInbox: forward("modelInbox"),
|
|
208
|
+
readUnreadCount: forward("readUnreadCount"),
|
|
209
|
+
listAuthorizedContacts: forward("listAuthorizedContacts"),
|
|
210
|
+
listAuthorizedInboxMessages: forward("listAuthorizedInboxMessages"),
|
|
211
|
+
readAuthorizedInboxMessage: forward("readAuthorizedInboxMessage"),
|
|
212
|
+
listPendingMessages: forward("listPendingMessages"),
|
|
213
|
+
nativeTaskScope: forward("nativeTaskScope"),
|
|
214
|
+
claimMessage: forward("claimMessage"),
|
|
215
|
+
claimBatch: forward("claimBatch"),
|
|
216
|
+
inboxBatch: forward("inboxBatch"),
|
|
217
|
+
replyAuthorizedMessage: forward("replyAuthorizedMessage"),
|
|
218
|
+
listAuthorizedContactRequests: forward("listAuthorizedContactRequests"),
|
|
219
|
+
readAuthorizedContactRequest: forward("readAuthorizedContactRequest"),
|
|
220
|
+
sendAuthorizedMessage: forward("sendAuthorizedMessage"),
|
|
221
|
+
readWorkbenchSnapshot: forward("readWorkbenchSnapshot"),
|
|
222
|
+
readWorkbenchChanges: forward("readWorkbenchChanges"),
|
|
223
|
+
async issueWorkbenchActionGrant(command) {
|
|
224
|
+
return invoke("issueWorkbenchActionGrant", [command], 1);
|
|
225
|
+
},
|
|
226
|
+
async executeWorkbenchCommand(command, context) {
|
|
227
|
+
return invoke("executeWorkbenchCommand", [command, context], 2);
|
|
228
|
+
},
|
|
229
|
+
async close() {
|
|
230
|
+
if (closed) return closePromise;
|
|
231
|
+
closed = true;
|
|
232
|
+
const pendingAdapter = adapterPromise;
|
|
233
|
+
const pendingReset = accountReset;
|
|
234
|
+
adapterPromise = null;
|
|
235
|
+
currentAdapter = null;
|
|
236
|
+
closePromise = (async () => {
|
|
237
|
+
// Account reset owns any initialization it detached from adapterPromise.
|
|
238
|
+
// Shutdown must also wait for that flight and its eventual retirement.
|
|
239
|
+
await pendingReset?.catch(() => {});
|
|
240
|
+
const adapter = await pendingAdapter?.catch(() => null);
|
|
241
|
+
if (adapter) await closeAdapter(adapter);
|
|
242
|
+
await Promise.allSettled([...retiring]);
|
|
243
|
+
})();
|
|
244
|
+
return closePromise;
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async function optionalWorkbenchCandidate(options, neutralAdapter) {
|
|
250
|
+
if (options.workbenchCandidate === null) return null;
|
|
251
|
+
if (options.workbenchCandidate !== undefined) return options.workbenchCandidate;
|
|
252
|
+
const bootstrap = options.bootstrapWorkbenchCandidate || bootstrapCodexWorkbenchRuntime;
|
|
253
|
+
if (typeof bootstrap !== "function") return null;
|
|
254
|
+
try {
|
|
255
|
+
return await bootstrap({ adapter: neutralAdapter, connectAccount: options.connectAccount || createWorkbenchConnection({
|
|
256
|
+
client: "codex", releaseRoot: path.dirname(path.dirname(fileURLToPath(import.meta.url))), env: options.env || process.env,
|
|
257
|
+
}) });
|
|
258
|
+
} catch {
|
|
259
|
+
// Discovery stays available when the shared bundle is missing, but the MCP
|
|
260
|
+
// server must fail that UI surface closed instead of restoring the retired
|
|
261
|
+
// UI. Independent neutral model tools remain available.
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async function initializeNeutralAdapter(options) {
|
|
267
|
+
const production = options.capabilityProvider || options.hostLauncher
|
|
268
|
+
? null
|
|
269
|
+
: await (options.createNeutralProduction || createCodexNeutralProduction)({
|
|
270
|
+
env: options.env || process.env,
|
|
271
|
+
});
|
|
272
|
+
const createAdapter = options.createNeutralAdapter || createCodexNeutralHostAdapter;
|
|
273
|
+
return createAdapter({
|
|
274
|
+
capabilityProvider: options.capabilityProvider || production?.capabilityProvider,
|
|
275
|
+
discoverHost: options.discoverNeutralHost,
|
|
276
|
+
hostLauncher: options.hostLauncher || production?.hostLauncher,
|
|
277
|
+
requestHost: options.requestNeutralHost,
|
|
278
|
+
fetchImpl: options.fetchImpl,
|
|
279
|
+
adapterInstanceId: options.adapterInstanceId,
|
|
280
|
+
heartbeatMs: options.heartbeatMs,
|
|
281
|
+
wait: options.wait,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function safeCode(error) {
|
|
286
|
+
const value = String(error?.code || error?.message || "single_host_unavailable")
|
|
287
|
+
.replace(/[^A-Za-z0-9._:-]+/g, "_")
|
|
288
|
+
.replace(/_+/g, "_")
|
|
289
|
+
.replace(/^_+|_+$/g, "")
|
|
290
|
+
.slice(0, 120);
|
|
291
|
+
return value || "single_host_unavailable";
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function codeError(code) {
|
|
295
|
+
const error = new Error(code);
|
|
296
|
+
error.code = code;
|
|
297
|
+
return error;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
301
|
+
if (isMain) {
|
|
302
|
+
runMcpProxy().catch((error) => {
|
|
303
|
+
process.stderr.write(`${JSON.stringify({ ok: false, error: safeCode(error) })}\n`);
|
|
304
|
+
process.exitCode = 1;
|
|
305
|
+
});
|
|
306
|
+
}
|