@hellolynk-ai/codex-plugin 0.0.0-stage → 0.2.99

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 (123) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/LICENSE +92 -0
  3. package/README.md +86 -2
  4. package/bin/hellolynk-client-setup.mjs +2748 -0
  5. package/bin/hellolynk-mcp-config-shape.mjs +23 -0
  6. package/hellolynk-client-pack-manifest.json +619 -0
  7. package/hellolynk-client-pack-pin.json +4 -0
  8. package/package.json +58 -4
  9. package/plugins/hellolynk-codex/.app.json +3 -0
  10. package/plugins/hellolynk-codex/.codex-plugin/plugin.json +38 -0
  11. package/plugins/hellolynk-codex/.mcp.json +19 -0
  12. package/plugins/hellolynk-codex/assets/hellolynk-mark.png +0 -0
  13. package/plugins/hellolynk-codex/package.json +11 -0
  14. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-account.mjs +180 -0
  15. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-core.mjs +770 -0
  16. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-profile.mjs +191 -0
  17. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-host-auth.mjs +347 -0
  18. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-host-protected-store.mjs +2721 -0
  19. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-local-process-lock.mjs +157 -0
  20. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-neutral-host-installation-store.mjs +765 -0
  21. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-platform-credential-store.mjs +773 -0
  22. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-runtime-safety.cjs +471 -0
  23. package/plugins/hellolynk-codex/runtime/bootstrap/platform/windows/hellolynk-host-protected-store.ps1 +2866 -0
  24. package/plugins/hellolynk-codex/runtime/connect-activate.mjs +837 -0
  25. package/plugins/hellolynk-codex/runtime/core/hellolynk-agent-configurator.mjs +701 -0
  26. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-account.mjs +180 -0
  27. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-core.mjs +770 -0
  28. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-profile.mjs +191 -0
  29. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-entry.mjs +31 -0
  30. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-runtime.mjs +600 -0
  31. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update.mjs +453 -0
  32. package/plugins/hellolynk-codex/runtime/core/hellolynk-contact-request-e2ee.mjs +867 -0
  33. package/plugins/hellolynk-codex/runtime/core/hellolynk-device-enrollment.mjs +435 -0
  34. package/plugins/hellolynk-codex/runtime/core/hellolynk-direct-text-e2ee-client.mjs +1964 -0
  35. package/plugins/hellolynk-codex/runtime/core/hellolynk-direct-text-e2ee.mjs +1207 -0
  36. package/plugins/hellolynk-codex/runtime/core/hellolynk-host-auth.mjs +347 -0
  37. package/plugins/hellolynk-codex/runtime/core/hellolynk-host-protected-store.mjs +2721 -0
  38. package/plugins/hellolynk-codex/runtime/core/hellolynk-host-transport-client.mjs +946 -0
  39. package/plugins/hellolynk-codex/runtime/core/hellolynk-host.mjs +2374 -0
  40. package/plugins/hellolynk-codex/runtime/core/hellolynk-human-confirmation-ledger.mjs +256 -0
  41. package/plugins/hellolynk-codex/runtime/core/hellolynk-human-relationship-controller.mjs +1234 -0
  42. package/plugins/hellolynk-codex/runtime/core/hellolynk-inbox-read-state.mjs +11 -0
  43. package/plugins/hellolynk-codex/runtime/core/hellolynk-indexed-message-store.mjs +1558 -0
  44. package/plugins/hellolynk-codex/runtime/core/hellolynk-instance-conversation.mjs +185 -0
  45. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-contact-requests.mjs +1733 -0
  46. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-contacts.mjs +1228 -0
  47. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-data-migration.mjs +766 -0
  48. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-data-protection.mjs +747 -0
  49. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-identity-trust.mjs +274 -0
  50. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-messaging.mjs +3043 -0
  51. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-peer-transport.mjs +103 -0
  52. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-process-lock.mjs +157 -0
  53. package/plugins/hellolynk-codex/runtime/core/hellolynk-model-inbox.mjs +230 -0
  54. package/plugins/hellolynk-codex/runtime/core/hellolynk-model-notices.cjs +60 -0
  55. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-addressing-tools.cjs +231 -0
  56. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-conversations.mjs +280 -0
  57. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-human-confirmation-broker.mjs +1036 -0
  58. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-inbox-runtime.mjs +553 -0
  59. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-notice.mjs +50 -0
  60. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-reply-routes.mjs +96 -0
  61. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-worker-lease.mjs +84 -0
  62. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-assembly.mjs +955 -0
  63. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-installation-store.mjs +765 -0
  64. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-launcher.mjs +665 -0
  65. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-process.mjs +4374 -0
  66. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-store-adapters.mjs +548 -0
  67. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-workbench-read-model.mjs +1365 -0
  68. package/plugins/hellolynk-codex/runtime/core/hellolynk-platform-credential-store.mjs +773 -0
  69. package/plugins/hellolynk-codex/runtime/core/hellolynk-relay-client-cli.mjs +232 -0
  70. package/plugins/hellolynk-codex/runtime/core/hellolynk-release-retention.mjs +160 -0
  71. package/plugins/hellolynk-codex/runtime/core/hellolynk-runtime-safety.cjs +471 -0
  72. package/plugins/hellolynk-codex/runtime/core/hellolynk-workbench-action-grants.mjs +457 -0
  73. package/plugins/hellolynk-codex/runtime/core/hellolynk-workbench-connection.mjs +234 -0
  74. package/plugins/hellolynk-codex/runtime/neutral-host-bootstrap.mjs +64 -0
  75. package/plugins/hellolynk-codex/runtime/neutral-host-release-entry.mjs +70 -0
  76. package/plugins/hellolynk-codex/runtime/neutral-host-release-loader.mjs +156 -0
  77. package/plugins/hellolynk-codex/runtime/neutral-host-release-manifest.json +358 -0
  78. package/plugins/hellolynk-codex/runtime/neutral-host-release-pin.json +4 -0
  79. package/plugins/hellolynk-codex/runtime/platform/windows/configure-agent.ps1 +281 -0
  80. package/plugins/hellolynk-codex/runtime/platform/windows/credential-store.ps1 +844 -0
  81. package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-host-protected-store.ps1 +2866 -0
  82. package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-human-confirmation-ledger.ps1 +155 -0
  83. package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-local-data-key.ps1 +300 -0
  84. package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-native-human-confirmation-broker.ps1 +216 -0
  85. package/plugins/hellolynk-codex/runtime/workbench/candidate-bootstrap.mjs +54 -0
  86. package/plugins/hellolynk-codex/runtime/workbench/contract.mjs +1314 -0
  87. package/plugins/hellolynk-codex/runtime/workbench/hellolynk-workbench-build.json +215 -0
  88. package/plugins/hellolynk-codex/runtime/workbench/host-capabilities.mjs +80 -0
  89. package/plugins/hellolynk-codex/runtime/workbench/i18n.mjs +1127 -0
  90. package/plugins/hellolynk-codex/runtime/workbench/loopback-surface.mjs +740 -0
  91. package/plugins/hellolynk-codex/runtime/workbench/mcp-app-bundle.mjs +97 -0
  92. package/plugins/hellolynk-codex/runtime/workbench/service.mjs +485 -0
  93. package/plugins/hellolynk-codex/runtime/workbench/state.mjs +421 -0
  94. package/plugins/hellolynk-codex/runtime/workbench/transports.mjs +171 -0
  95. package/plugins/hellolynk-codex/runtime/workbench/ui/app.mjs +6120 -0
  96. package/plugins/hellolynk-codex/runtime/workbench/ui/connection-form.mjs +155 -0
  97. package/plugins/hellolynk-codex/runtime/workbench/ui/hellolynk-mark.png +0 -0
  98. package/plugins/hellolynk-codex/runtime/workbench/ui/http-transport.mjs +247 -0
  99. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/arrow-left.svg +3 -0
  100. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/close.svg +3 -0
  101. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/contacts.svg +3 -0
  102. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/inbox.svg +3 -0
  103. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/refresh.svg +3 -0
  104. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/search.svg +3 -0
  105. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/security.svg +3 -0
  106. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/settings.svg +3 -0
  107. package/plugins/hellolynk-codex/runtime/workbench/ui/index.html +139 -0
  108. package/plugins/hellolynk-codex/runtime/workbench/ui/loopback-entry.mjs +26 -0
  109. package/plugins/hellolynk-codex/runtime/workbench/ui/mcp-app-client.mjs +151 -0
  110. package/plugins/hellolynk-codex/runtime/workbench/ui/mcp-app-entry.mjs +452 -0
  111. package/plugins/hellolynk-codex/runtime/workbench/ui/styles.css +686 -0
  112. package/plugins/hellolynk-codex/server/codex-neutral-production.mjs +520 -0
  113. package/plugins/hellolynk-codex/server/mcp-proxy.mjs +306 -0
  114. package/plugins/hellolynk-codex/server/mcp-server.mjs +1193 -0
  115. package/plugins/hellolynk-codex/server/native-conversation-directory.mjs +33 -0
  116. package/plugins/hellolynk-codex/server/native-reply-routes.mjs +32 -0
  117. package/plugins/hellolynk-codex/server/native-task-client.mjs +382 -0
  118. package/plugins/hellolynk-codex/server/native-task-runtime.mjs +8 -0
  119. package/plugins/hellolynk-codex/server/neutral-host-adapter.mjs +649 -0
  120. package/plugins/hellolynk-codex/server/workbench-runtime.mjs +180 -0
  121. package/plugins/hellolynk-codex/skills/hellolynk-open/SKILL.md +11 -0
  122. package/plugins/hellolynk-codex/skills/hellolynk-open/agents/openai.yaml +4 -0
  123. 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
+ }