@dexlyai/dexly 0.1.0
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/README.md +61 -0
- package/dist/cli.js +1404 -0
- package/dist/host.js +859 -0
- package/package.json +43 -0
package/dist/host.js
ADDED
|
@@ -0,0 +1,859 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
"use strict";
|
|
4
|
+
var __create = Object.create;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
11
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
|
|
30
|
+
// ../DexlyProtocol/dist/companion.js
|
|
31
|
+
var require_companion = __commonJS({
|
|
32
|
+
"../DexlyProtocol/dist/companion.js"(exports2) {
|
|
33
|
+
"use strict";
|
|
34
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
35
|
+
exports2.DEFAULT_DIRECT_WEBSOCKET_ENDPOINT = exports2.DEXLY_FIXED_ALLOWED_ORIGINS = exports2.DEXLY_DEFAULT_RELEASE_CHANNEL_BY_BUILD = exports2.DEXLY_FIXED_EXTENSION_IDS = exports2.DEXLY_EXTENSION_IDENTITIES = exports2.DEXLY_PROD_EXTENSION_ID = exports2.DEXLY_PROD_EXTENSION_PUBLIC_KEY = exports2.DEXLY_DEV_EXTENSION_ID = exports2.DEXLY_DEV_EXTENSION_PUBLIC_KEY = exports2.DEXLY_CODEX_INSTALL_DOCS_URL = exports2.DEXLY_CODEX_UPGRADE_COMMAND = exports2.DEXLY_CODEX_INSTALL_COMMAND = exports2.DEXLY_COMPANION_CONNECT_COOLDOWN_MS = exports2.DEXLY_COMPANION_GLOBAL_INSTALL_COMMAND = exports2.DEXLY_COMPANION_UPGRADE_COMMAND = exports2.DEXLY_COMPANION_DOCTOR_COMMAND = exports2.DEXLY_COMPANION_INSTALL_COMMAND = exports2.DEXLY_NATIVE_CONNECTION_ENDPOINT = exports2.DEXLY_COMPANION_EXECUTABLE_NAME = exports2.DEXLY_COMPANION_PACKAGE_NAME = exports2.DEXLY_COMPANION_DISPLAY_NAME = exports2.DEXLY_COMPANION_HOST_NAME = void 0;
|
|
36
|
+
exports2.resolveDexlyExtensionBuildChannel = resolveDexlyExtensionBuildChannel;
|
|
37
|
+
exports2.getDexlyExtensionIdentity = getDexlyExtensionIdentity;
|
|
38
|
+
exports2.isDexlyFixedExtensionId = isDexlyFixedExtensionId;
|
|
39
|
+
exports2.toChromeExtensionOrigin = toChromeExtensionOrigin;
|
|
40
|
+
exports2.resolveDexlyExtensionBuildChannelFromExtensionId = resolveDexlyExtensionBuildChannelFromExtensionId;
|
|
41
|
+
exports2.normalizeCompanionReleaseChannel = normalizeCompanionReleaseChannel;
|
|
42
|
+
exports2.DEXLY_COMPANION_HOST_NAME = "ai.dexly.companion";
|
|
43
|
+
exports2.DEXLY_COMPANION_DISPLAY_NAME = "Dexly Companion";
|
|
44
|
+
exports2.DEXLY_COMPANION_PACKAGE_NAME = "@dexlyai/dexly";
|
|
45
|
+
exports2.DEXLY_COMPANION_EXECUTABLE_NAME = "dexly";
|
|
46
|
+
exports2.DEXLY_NATIVE_CONNECTION_ENDPOINT = "native://ai.dexly.companion";
|
|
47
|
+
exports2.DEXLY_COMPANION_INSTALL_COMMAND = "npx -y @dexlyai/dexly install";
|
|
48
|
+
exports2.DEXLY_COMPANION_DOCTOR_COMMAND = "npx -y @dexlyai/dexly doctor";
|
|
49
|
+
exports2.DEXLY_COMPANION_UPGRADE_COMMAND = "npx -y @dexlyai/dexly upgrade";
|
|
50
|
+
exports2.DEXLY_COMPANION_GLOBAL_INSTALL_COMMAND = "npm i -g @dexlyai/dexly && dexly install";
|
|
51
|
+
exports2.DEXLY_COMPANION_CONNECT_COOLDOWN_MS = 15e3;
|
|
52
|
+
exports2.DEXLY_CODEX_INSTALL_COMMAND = "npm install -g @openai/codex";
|
|
53
|
+
exports2.DEXLY_CODEX_UPGRADE_COMMAND = "codex --upgrade";
|
|
54
|
+
exports2.DEXLY_CODEX_INSTALL_DOCS_URL = "https://help.openai.com/en/articles/11096431-openai-codex-ci-getting-started";
|
|
55
|
+
exports2.DEXLY_DEV_EXTENSION_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6fpo7R5tslk/4402QrobrFaQUyW2jk+cgG60FE51YtcQUWtdI8WEyIZBg7bTkpwjt/cvcCWg6B5P/iOZNYoq0o3RxyeqT+8IjuYA0d2kYUaCOoshPsLdEvLTTr3SQ1bg0r2naivw92KxVgZ2fcZuS4kNy6qYhBCWitvTLvDVCnjRN13UT9buskxnsv0Cr+kh7vr/YXA2Boy2jwQyZXEDg9FvnSJF5yEAK6GLzlkvfIKXifxBSMdNaOVp8hKOxR9WjFTotBwZ6mgJXKCNgG+7Xd54xkWRaomU7CEohfvkmmxM9gpJ3S1VqmauogSbkzwCcJQJ1csdU/7TWQ4+hOuKLQIDAQAB";
|
|
56
|
+
exports2.DEXLY_DEV_EXTENSION_ID = "mjihblbjfhdhaidkanpjbnmmcljbafdn";
|
|
57
|
+
exports2.DEXLY_PROD_EXTENSION_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmxkxUCWY3dbeEhjPxW7hOstyhtteCX3blvOTTGoGjju2p+je8eNVo7YDSqlgwRvOQw5N9LWmwb5Ui9wwVhQtLWyYqqq4aTVIAQhUhPDG6gRAUlLMXb/8e+ErWVx707d+aOVTgdc41qA2TVeUelVv/ARjcHL+xcxtk9pcmEMUGnWcZ1kPVx5FTQlLvEpFLVFsTMA6sff/6ba00/S1J28rEUdo2I61Dqku3ceMe3wNmLO4LJ9dj4dLxuYzpF/zH9vEVPWlYgyZY/NGadaAv1rxW4BSboqEOAK54vzcd5FfbxmvuYcRCVyN2A6WRU6sFUFpNrlzTOl+PpogNh+pa2qgxwIDAQAB";
|
|
58
|
+
exports2.DEXLY_PROD_EXTENSION_ID = "mkgnndfbolkjpiijcpkhmgjjhgcibdnp";
|
|
59
|
+
exports2.DEXLY_EXTENSION_IDENTITIES = {
|
|
60
|
+
development: {
|
|
61
|
+
channel: "development",
|
|
62
|
+
label: "development",
|
|
63
|
+
extensionId: exports2.DEXLY_DEV_EXTENSION_ID,
|
|
64
|
+
publicKey: exports2.DEXLY_DEV_EXTENSION_PUBLIC_KEY
|
|
65
|
+
},
|
|
66
|
+
production: {
|
|
67
|
+
channel: "production",
|
|
68
|
+
label: "production",
|
|
69
|
+
extensionId: exports2.DEXLY_PROD_EXTENSION_ID,
|
|
70
|
+
publicKey: exports2.DEXLY_PROD_EXTENSION_PUBLIC_KEY
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
exports2.DEXLY_FIXED_EXTENSION_IDS = [
|
|
74
|
+
exports2.DEXLY_EXTENSION_IDENTITIES.development.extensionId,
|
|
75
|
+
exports2.DEXLY_EXTENSION_IDENTITIES.production.extensionId
|
|
76
|
+
];
|
|
77
|
+
exports2.DEXLY_DEFAULT_RELEASE_CHANNEL_BY_BUILD = {
|
|
78
|
+
development: "beta",
|
|
79
|
+
production: "latest"
|
|
80
|
+
};
|
|
81
|
+
function resolveDexlyExtensionBuildChannel(value) {
|
|
82
|
+
switch (value?.trim().toLowerCase()) {
|
|
83
|
+
case "dev":
|
|
84
|
+
case "development":
|
|
85
|
+
return "development";
|
|
86
|
+
case "prod":
|
|
87
|
+
case "production":
|
|
88
|
+
return "production";
|
|
89
|
+
default:
|
|
90
|
+
return "production";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function getDexlyExtensionIdentity(channel) {
|
|
94
|
+
return exports2.DEXLY_EXTENSION_IDENTITIES[channel];
|
|
95
|
+
}
|
|
96
|
+
function isDexlyFixedExtensionId(extensionId) {
|
|
97
|
+
return typeof extensionId === "string" && exports2.DEXLY_FIXED_EXTENSION_IDS.some((candidate) => candidate === extensionId);
|
|
98
|
+
}
|
|
99
|
+
function toChromeExtensionOrigin(extensionId) {
|
|
100
|
+
return `chrome-extension://${extensionId}/`;
|
|
101
|
+
}
|
|
102
|
+
function resolveDexlyExtensionBuildChannelFromExtensionId(extensionId) {
|
|
103
|
+
return extensionId === exports2.DEXLY_DEV_EXTENSION_ID ? "development" : "production";
|
|
104
|
+
}
|
|
105
|
+
function normalizeCompanionReleaseChannel(value) {
|
|
106
|
+
switch (value?.trim().toLowerCase()) {
|
|
107
|
+
case "latest":
|
|
108
|
+
case "beta":
|
|
109
|
+
case "canary":
|
|
110
|
+
return value.trim().toLowerCase();
|
|
111
|
+
default:
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
exports2.DEXLY_FIXED_ALLOWED_ORIGINS = exports2.DEXLY_FIXED_EXTENSION_IDS.map(toChromeExtensionOrigin);
|
|
116
|
+
exports2.DEFAULT_DIRECT_WEBSOCKET_ENDPOINT = "ws://127.0.0.1:4500";
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// src/host.ts
|
|
121
|
+
var import_node_process4 = __toESM(require("node:process"));
|
|
122
|
+
|
|
123
|
+
// src/host-runtime.ts
|
|
124
|
+
var import_node_process3 = __toESM(require("node:process"));
|
|
125
|
+
|
|
126
|
+
// src/codex-host.ts
|
|
127
|
+
var import_node_process2 = __toESM(require("node:process"));
|
|
128
|
+
var import_node_child_process2 = require("node:child_process");
|
|
129
|
+
var import_node_readline = __toESM(require("node:readline"));
|
|
130
|
+
var import_companion4 = __toESM(require_companion());
|
|
131
|
+
|
|
132
|
+
// src/constants.ts
|
|
133
|
+
var import_node_os = __toESM(require("node:os"));
|
|
134
|
+
var import_node_path = __toESM(require("node:path"));
|
|
135
|
+
var import_companion = __toESM(require_companion());
|
|
136
|
+
var DEXLY_COMPANION_VERSION = true ? "0.1.0" : packageJson.version;
|
|
137
|
+
var DEXLY_COMPANION_DESCRIPTION = "Dexly native bridge for Codex";
|
|
138
|
+
var DEXLY_COMPANION_METADATA_FILE_NAME = "install-metadata.json";
|
|
139
|
+
function defaultInstallRoot(homeDir = import_node_os.default.homedir()) {
|
|
140
|
+
return import_node_path.default.join(homeDir, "Library", "Application Support", "Dexly", "companion");
|
|
141
|
+
}
|
|
142
|
+
function defaultChromeHostManifestDir(homeDir = import_node_os.default.homedir()) {
|
|
143
|
+
return import_node_path.default.join(homeDir, "Library", "Application Support", "Google", "Chrome", "NativeMessagingHosts");
|
|
144
|
+
}
|
|
145
|
+
function defaultChromeAllowedOrigins() {
|
|
146
|
+
return [...import_companion.DEXLY_FIXED_ALLOWED_ORIGINS];
|
|
147
|
+
}
|
|
148
|
+
function currentInstallPath(installRoot) {
|
|
149
|
+
return import_node_path.default.join(installRoot, "current");
|
|
150
|
+
}
|
|
151
|
+
function installMetadataPath(installRoot) {
|
|
152
|
+
return import_node_path.default.join(installRoot, DEXLY_COMPANION_METADATA_FILE_NAME);
|
|
153
|
+
}
|
|
154
|
+
function installedHostScriptPath(installRoot) {
|
|
155
|
+
return import_node_path.default.join(currentInstallPath(installRoot), "dist", "host.js");
|
|
156
|
+
}
|
|
157
|
+
function installedHostLauncherPath(installRoot) {
|
|
158
|
+
return import_node_path.default.join(currentInstallPath(installRoot), "bin", "dexly-companion-host");
|
|
159
|
+
}
|
|
160
|
+
function installedHostManifestPath(manifestDir) {
|
|
161
|
+
return import_node_path.default.join(manifestDir, `${import_companion.DEXLY_COMPANION_HOST_NAME}.json`);
|
|
162
|
+
}
|
|
163
|
+
function buildChromeHostManifest(hostPath, allowedOrigins) {
|
|
164
|
+
return {
|
|
165
|
+
name: import_companion.DEXLY_COMPANION_HOST_NAME,
|
|
166
|
+
description: DEXLY_COMPANION_DESCRIPTION,
|
|
167
|
+
path: hostPath,
|
|
168
|
+
type: "stdio",
|
|
169
|
+
allowed_origins: allowedOrigins
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/management.ts
|
|
174
|
+
var import_companion3 = __toESM(require_companion());
|
|
175
|
+
|
|
176
|
+
// src/install-metadata.ts
|
|
177
|
+
var import_promises = require("node:fs/promises");
|
|
178
|
+
var import_companion2 = __toESM(require_companion());
|
|
179
|
+
function normalizeString(value) {
|
|
180
|
+
return typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
181
|
+
}
|
|
182
|
+
function normalizeToolPaths(value) {
|
|
183
|
+
if (!value || typeof value !== "object") {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
const candidate = value;
|
|
187
|
+
const nodePath = normalizeString(candidate.nodePath);
|
|
188
|
+
const npmPath = normalizeString(candidate.npmPath);
|
|
189
|
+
if (!nodePath || !npmPath) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
nodePath,
|
|
194
|
+
npmPath,
|
|
195
|
+
codexPath: normalizeString(candidate.codexPath)
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
async function loadInstallMetadata(installRoot) {
|
|
199
|
+
try {
|
|
200
|
+
const raw = await (0, import_promises.readFile)(installMetadataPath(installRoot), "utf8");
|
|
201
|
+
const parsed = JSON.parse(raw);
|
|
202
|
+
const tools = normalizeToolPaths(parsed.tools);
|
|
203
|
+
const currentVersion = normalizeString(parsed.currentVersion);
|
|
204
|
+
const installedAt = normalizeString(parsed.installedAt);
|
|
205
|
+
const updatedAt = normalizeString(parsed.updatedAt);
|
|
206
|
+
if (!tools || !currentVersion || !installedAt || !updatedAt) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
const knownGoodVersions = Array.isArray(parsed.knownGoodVersions) ? parsed.knownGoodVersions.filter((value) => typeof value === "string" && value.trim().length > 0) : [currentVersion];
|
|
210
|
+
return {
|
|
211
|
+
schemaVersion: 1,
|
|
212
|
+
packageName: import_companion2.DEXLY_COMPANION_PACKAGE_NAME,
|
|
213
|
+
executableName: import_companion2.DEXLY_COMPANION_EXECUTABLE_NAME,
|
|
214
|
+
currentVersion,
|
|
215
|
+
previousVersion: normalizeString(parsed.previousVersion),
|
|
216
|
+
knownGoodVersions: knownGoodVersions.length > 0 ? knownGoodVersions : [currentVersion],
|
|
217
|
+
currentChannel: normalizeString(parsed.currentChannel),
|
|
218
|
+
installedAt,
|
|
219
|
+
updatedAt,
|
|
220
|
+
tools
|
|
221
|
+
};
|
|
222
|
+
} catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async function saveInstallMetadata(installRoot, metadata) {
|
|
227
|
+
await (0, import_promises.writeFile)(
|
|
228
|
+
installMetadataPath(installRoot),
|
|
229
|
+
`${JSON.stringify(metadata, null, 2)}
|
|
230
|
+
`,
|
|
231
|
+
"utf8"
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/install.ts
|
|
236
|
+
var import_promises2 = require("node:fs/promises");
|
|
237
|
+
var import_node_path3 = __toESM(require("node:path"));
|
|
238
|
+
|
|
239
|
+
// src/tooling.ts
|
|
240
|
+
var import_node_path2 = __toESM(require("node:path"));
|
|
241
|
+
var import_node_process = __toESM(require("node:process"));
|
|
242
|
+
var import_node_child_process = require("node:child_process");
|
|
243
|
+
function detectToolPaths() {
|
|
244
|
+
const nodePath = import_node_process.default.execPath;
|
|
245
|
+
const npmPath = resolveExecutablePath("npm") ?? "npm";
|
|
246
|
+
const codexPath = resolveExecutablePath("codex");
|
|
247
|
+
return {
|
|
248
|
+
nodePath,
|
|
249
|
+
npmPath,
|
|
250
|
+
codexPath
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function resolveExecutablePath(command, env = import_node_process.default.env) {
|
|
254
|
+
const result = (0, import_node_child_process.spawnSync)("which", [command], {
|
|
255
|
+
encoding: "utf8",
|
|
256
|
+
env
|
|
257
|
+
});
|
|
258
|
+
if (result.status !== 0) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
const executablePath = result.stdout.trim();
|
|
262
|
+
return executablePath.length > 0 ? executablePath : null;
|
|
263
|
+
}
|
|
264
|
+
function buildToolEnv(toolPaths, extraPaths = []) {
|
|
265
|
+
const pathEntries = [
|
|
266
|
+
import_node_path2.default.dirname(toolPaths.nodePath),
|
|
267
|
+
import_node_path2.default.dirname(toolPaths.npmPath),
|
|
268
|
+
toolPaths.codexPath ? import_node_path2.default.dirname(toolPaths.codexPath) : null,
|
|
269
|
+
...extraPaths,
|
|
270
|
+
"/usr/local/bin",
|
|
271
|
+
"/opt/homebrew/bin",
|
|
272
|
+
"/usr/bin",
|
|
273
|
+
"/bin",
|
|
274
|
+
"/usr/sbin",
|
|
275
|
+
"/sbin",
|
|
276
|
+
import_node_process.default.env.PATH ?? null
|
|
277
|
+
].filter((entry, index, entries) => typeof entry === "string" && entries.indexOf(entry) === index);
|
|
278
|
+
return {
|
|
279
|
+
...import_node_process.default.env,
|
|
280
|
+
PATH: pathEntries.join(":"),
|
|
281
|
+
...toolPaths.codexPath ? { DEXLY_COMPANION_CODEX_PATH: toolPaths.codexPath } : {}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
async function runCommand(command, args, options = {}) {
|
|
285
|
+
return await new Promise((resolve, reject) => {
|
|
286
|
+
const child = (0, import_node_child_process.spawn)(command, args, {
|
|
287
|
+
cwd: options.cwd,
|
|
288
|
+
env: options.env,
|
|
289
|
+
stdio: "pipe"
|
|
290
|
+
});
|
|
291
|
+
let stdout = "";
|
|
292
|
+
let stderr = "";
|
|
293
|
+
child.stdout.on("data", (chunk) => {
|
|
294
|
+
stdout += (Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)).toString("utf8");
|
|
295
|
+
});
|
|
296
|
+
child.stderr.on("data", (chunk) => {
|
|
297
|
+
stderr += (Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)).toString("utf8");
|
|
298
|
+
});
|
|
299
|
+
child.once("error", (error) => {
|
|
300
|
+
reject(error);
|
|
301
|
+
});
|
|
302
|
+
child.once("exit", (code, signal) => {
|
|
303
|
+
if (code === 0) {
|
|
304
|
+
resolve({ stdout, stderr });
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const detail = stderr.trim() || stdout.trim();
|
|
308
|
+
reject(new Error(
|
|
309
|
+
detail.length > 0 ? `${command} ${args.join(" ")} failed (${code ?? signal ?? "unknown"}): ${detail}` : `${command} ${args.join(" ")} failed (${code ?? signal ?? "unknown"}).`
|
|
310
|
+
));
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
function resolveCodexVersion(codexCommand = import_node_process.default.env.DEXLY_COMPANION_CODEX_PATH ?? "codex", env = import_node_process.default.env) {
|
|
315
|
+
const result = (0, import_node_child_process.spawnSync)(codexCommand, ["--version"], {
|
|
316
|
+
encoding: "utf8",
|
|
317
|
+
env
|
|
318
|
+
});
|
|
319
|
+
if (result.error || result.status !== 0) {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
return result.stdout.trim() || null;
|
|
323
|
+
}
|
|
324
|
+
function resolveNpmGlobalBinDir(npmPath, env = import_node_process.default.env) {
|
|
325
|
+
const result = (0, import_node_child_process.spawnSync)(npmPath, ["config", "get", "prefix"], {
|
|
326
|
+
encoding: "utf8",
|
|
327
|
+
env
|
|
328
|
+
});
|
|
329
|
+
if (result.error || result.status !== 0) {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
const prefix = result.stdout.trim();
|
|
333
|
+
return prefix.length > 0 ? import_node_path2.default.join(prefix, "bin") : null;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/install.ts
|
|
337
|
+
async function rewriteCurrentLauncher(installRoot, toolPaths) {
|
|
338
|
+
const launcherPath = installedHostLauncherPath(installRoot);
|
|
339
|
+
await (0, import_promises2.mkdir)(import_node_path3.default.dirname(launcherPath), { recursive: true });
|
|
340
|
+
await (0, import_promises2.writeFile)(
|
|
341
|
+
launcherPath,
|
|
342
|
+
renderHostLauncher({
|
|
343
|
+
nodePath: toolPaths.nodePath,
|
|
344
|
+
hostScriptPath: installedHostScriptPath(installRoot),
|
|
345
|
+
codexPath: toolPaths.codexPath
|
|
346
|
+
}),
|
|
347
|
+
"utf8"
|
|
348
|
+
);
|
|
349
|
+
await (0, import_promises2.chmod)(launcherPath, 493);
|
|
350
|
+
return launcherPath;
|
|
351
|
+
}
|
|
352
|
+
async function ensureChromeHostManifest(installRoot, manifestDir) {
|
|
353
|
+
const manifestPath = installedHostManifestPath(manifestDir);
|
|
354
|
+
await (0, import_promises2.mkdir)(manifestDir, { recursive: true });
|
|
355
|
+
await (0, import_promises2.writeFile)(
|
|
356
|
+
manifestPath,
|
|
357
|
+
`${JSON.stringify(buildChromeHostManifest(installedHostLauncherPath(installRoot), defaultChromeAllowedOrigins()), null, 2)}
|
|
358
|
+
`,
|
|
359
|
+
"utf8"
|
|
360
|
+
);
|
|
361
|
+
return manifestPath;
|
|
362
|
+
}
|
|
363
|
+
function renderHostLauncher(options) {
|
|
364
|
+
const pathEntries = [
|
|
365
|
+
import_node_path3.default.dirname(options.nodePath),
|
|
366
|
+
options.codexPath ? import_node_path3.default.dirname(options.codexPath) : null,
|
|
367
|
+
"/usr/local/bin",
|
|
368
|
+
"/opt/homebrew/bin",
|
|
369
|
+
"/usr/bin",
|
|
370
|
+
"/bin",
|
|
371
|
+
"/usr/sbin",
|
|
372
|
+
"/sbin"
|
|
373
|
+
].filter((entry, index, entries) => typeof entry === "string" && entries.indexOf(entry) === index);
|
|
374
|
+
const lines = [
|
|
375
|
+
"#!/bin/sh",
|
|
376
|
+
"set -eu",
|
|
377
|
+
`export PATH=${toShellLiteral(pathEntries.join(":"))}`,
|
|
378
|
+
options.codexPath ? `export DEXLY_COMPANION_CODEX_PATH=${toShellLiteral(options.codexPath)}` : null,
|
|
379
|
+
`exec ${toShellLiteral(options.nodePath)} ${toShellLiteral(options.hostScriptPath)} "$@"`
|
|
380
|
+
].filter((line) => typeof line === "string");
|
|
381
|
+
return `${lines.join("\n")}
|
|
382
|
+
`;
|
|
383
|
+
}
|
|
384
|
+
function toShellLiteral(value) {
|
|
385
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// src/management.ts
|
|
389
|
+
async function upgradeInstalledCompanion(options) {
|
|
390
|
+
const installRoot = options.installRoot ?? defaultInstallRoot();
|
|
391
|
+
const metadata = await loadInstallMetadata(installRoot);
|
|
392
|
+
if (!metadata) {
|
|
393
|
+
throw new Error("Dexly Companion install metadata is missing. Reinstall Dexly Companion first.");
|
|
394
|
+
}
|
|
395
|
+
const specifier = options.version?.trim() ? options.version.trim() : options.distTag.trim();
|
|
396
|
+
const targetSpecifier = `${import_companion3.DEXLY_COMPANION_PACKAGE_NAME}@${specifier}`;
|
|
397
|
+
const env = buildToolEnv(metadata.tools);
|
|
398
|
+
await runCommand(metadata.tools.npmPath, [
|
|
399
|
+
"exec",
|
|
400
|
+
"--yes",
|
|
401
|
+
`--package=${targetSpecifier}`,
|
|
402
|
+
import_companion3.DEXLY_COMPANION_EXECUTABLE_NAME,
|
|
403
|
+
"install",
|
|
404
|
+
"--channel",
|
|
405
|
+
options.distTag
|
|
406
|
+
], { env });
|
|
407
|
+
const nextInstallMetadata = await loadInstallMetadata(installRoot);
|
|
408
|
+
if (!nextInstallMetadata) {
|
|
409
|
+
throw new Error("Dexly Companion upgraded, but the updated install metadata could not be read.");
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
previousVersion: metadata.currentVersion,
|
|
413
|
+
currentVersion: nextInstallMetadata.currentVersion,
|
|
414
|
+
targetSpecifier
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
async function installCodexWithCompanion(options) {
|
|
418
|
+
const installRoot = options?.installRoot ?? defaultInstallRoot();
|
|
419
|
+
const manifestDir = options?.manifestDir ?? defaultChromeHostManifestDir();
|
|
420
|
+
const existingMetadata = await loadInstallMetadata(installRoot);
|
|
421
|
+
const toolPaths = existingMetadata?.tools ?? detectToolPaths();
|
|
422
|
+
const env = buildToolEnv(toolPaths);
|
|
423
|
+
await runCommand(toolPaths.npmPath, ["install", "-g", "@openai/codex"], { env });
|
|
424
|
+
const globalBinDir = resolveNpmGlobalBinDir(toolPaths.npmPath, env);
|
|
425
|
+
const nextEnv = buildToolEnv(toolPaths, globalBinDir ? [globalBinDir] : []);
|
|
426
|
+
const codexPath = resolveExecutablePath("codex", nextEnv);
|
|
427
|
+
const codexVersion = resolveCodexVersion(codexPath ?? void 0, nextEnv);
|
|
428
|
+
if (!codexPath || !codexVersion) {
|
|
429
|
+
throw new Error("Codex install completed, but Dexly Companion could not resolve the installed codex binary.");
|
|
430
|
+
}
|
|
431
|
+
const nextToolPaths = {
|
|
432
|
+
...toolPaths,
|
|
433
|
+
codexPath
|
|
434
|
+
};
|
|
435
|
+
await rewriteCurrentLauncher(installRoot, nextToolPaths);
|
|
436
|
+
await ensureChromeHostManifest(installRoot, manifestDir);
|
|
437
|
+
if (existingMetadata) {
|
|
438
|
+
await saveInstallMetadata(installRoot, {
|
|
439
|
+
...existingMetadata,
|
|
440
|
+
tools: nextToolPaths,
|
|
441
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
codexPath,
|
|
446
|
+
codexVersion,
|
|
447
|
+
installCommand: import_companion3.DEXLY_CODEX_INSTALL_COMMAND
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// src/codex-host.ts
|
|
452
|
+
var STDERR_RING_BUFFER_LIMIT = 40;
|
|
453
|
+
var DexlyNativeHost = class {
|
|
454
|
+
send;
|
|
455
|
+
spawnProcess;
|
|
456
|
+
spawnProcessSync;
|
|
457
|
+
codexCommand;
|
|
458
|
+
requestProcessExit;
|
|
459
|
+
child = null;
|
|
460
|
+
childStdoutReader = null;
|
|
461
|
+
expectedChildExit = false;
|
|
462
|
+
stderrLines = [];
|
|
463
|
+
constructor(options) {
|
|
464
|
+
this.send = options.send;
|
|
465
|
+
this.spawnProcess = options.spawnProcess ?? import_node_child_process2.spawn;
|
|
466
|
+
this.spawnProcessSync = options.spawnProcessSync ?? import_node_child_process2.spawnSync;
|
|
467
|
+
this.codexCommand = options.codexCommand ?? import_node_process2.default.env.DEXLY_COMPANION_CODEX_PATH ?? "codex";
|
|
468
|
+
this.requestProcessExit = options.requestProcessExit ?? null;
|
|
469
|
+
}
|
|
470
|
+
async handleMessage(message) {
|
|
471
|
+
switch (message.kind) {
|
|
472
|
+
case "host/health":
|
|
473
|
+
this.send({
|
|
474
|
+
kind: "host/result",
|
|
475
|
+
action: "host/health",
|
|
476
|
+
requestId: message.requestId,
|
|
477
|
+
result: this.buildHealthResult()
|
|
478
|
+
});
|
|
479
|
+
return;
|
|
480
|
+
case "host/connect":
|
|
481
|
+
await this.handleConnect(message);
|
|
482
|
+
return;
|
|
483
|
+
case "host/disconnect":
|
|
484
|
+
await this.stopChild();
|
|
485
|
+
this.send({
|
|
486
|
+
kind: "host/result",
|
|
487
|
+
action: "host/disconnect",
|
|
488
|
+
requestId: message.requestId,
|
|
489
|
+
result: {
|
|
490
|
+
disconnected: true
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
return;
|
|
494
|
+
case "host/update":
|
|
495
|
+
await this.handleUpdate(message);
|
|
496
|
+
return;
|
|
497
|
+
case "host/install-codex":
|
|
498
|
+
await this.handleInstallCodex(message);
|
|
499
|
+
return;
|
|
500
|
+
case "codex/jsonrpc":
|
|
501
|
+
this.forwardJsonRpc(message.payload);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
async close() {
|
|
506
|
+
await this.stopChild();
|
|
507
|
+
}
|
|
508
|
+
buildHealthResult() {
|
|
509
|
+
const codexVersion = this.resolveCodexVersion();
|
|
510
|
+
return {
|
|
511
|
+
ready: codexVersion != null,
|
|
512
|
+
hostVersion: DEXLY_COMPANION_VERSION,
|
|
513
|
+
codexVersion,
|
|
514
|
+
codexInstalled: codexVersion != null,
|
|
515
|
+
capabilities: {
|
|
516
|
+
update: true,
|
|
517
|
+
installCodex: true,
|
|
518
|
+
rollback: true
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
async handleConnect(request) {
|
|
523
|
+
if (this.child) {
|
|
524
|
+
this.send({
|
|
525
|
+
kind: "host/result",
|
|
526
|
+
action: "host/connect",
|
|
527
|
+
requestId: request.requestId,
|
|
528
|
+
result: {
|
|
529
|
+
connected: true,
|
|
530
|
+
...this.buildHealthResult()
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
const codexVersion = this.resolveCodexVersion();
|
|
536
|
+
if (!codexVersion) {
|
|
537
|
+
this.send(this.buildError(
|
|
538
|
+
"host/connect",
|
|
539
|
+
request.requestId,
|
|
540
|
+
"codex_not_found",
|
|
541
|
+
"Codex CLI was not found. Install Codex locally and run Dexly Companion install again."
|
|
542
|
+
));
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
this.stderrLines = [];
|
|
546
|
+
this.expectedChildExit = false;
|
|
547
|
+
try {
|
|
548
|
+
const child = this.spawnProcess(this.codexCommand, ["app-server", "--listen", "stdio://"], {
|
|
549
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
550
|
+
});
|
|
551
|
+
await new Promise((resolve, reject) => {
|
|
552
|
+
let settled = false;
|
|
553
|
+
const finish = (error) => {
|
|
554
|
+
if (settled) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
settled = true;
|
|
558
|
+
child.off("error", handleError);
|
|
559
|
+
child.off("spawn", handleSpawn);
|
|
560
|
+
if (error) {
|
|
561
|
+
reject(error);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
resolve();
|
|
565
|
+
};
|
|
566
|
+
const handleError = (error) => {
|
|
567
|
+
finish(error);
|
|
568
|
+
};
|
|
569
|
+
const handleSpawn = () => {
|
|
570
|
+
finish(null);
|
|
571
|
+
};
|
|
572
|
+
child.once("error", handleError);
|
|
573
|
+
child.once("spawn", handleSpawn);
|
|
574
|
+
});
|
|
575
|
+
this.child = child;
|
|
576
|
+
this.attachChildListeners(child);
|
|
577
|
+
this.send({
|
|
578
|
+
kind: "host/result",
|
|
579
|
+
action: "host/connect",
|
|
580
|
+
requestId: request.requestId,
|
|
581
|
+
result: {
|
|
582
|
+
connected: true,
|
|
583
|
+
hostVersion: DEXLY_COMPANION_VERSION,
|
|
584
|
+
codexVersion,
|
|
585
|
+
codexInstalled: true,
|
|
586
|
+
capabilities: {
|
|
587
|
+
update: true,
|
|
588
|
+
installCodex: true,
|
|
589
|
+
rollback: true
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
} catch (error) {
|
|
594
|
+
this.send(this.buildError(
|
|
595
|
+
"host/connect",
|
|
596
|
+
request.requestId,
|
|
597
|
+
"codex_spawn_failed",
|
|
598
|
+
error instanceof Error ? error.message : "Dexly Companion could not start Codex app-server."
|
|
599
|
+
));
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
async handleUpdate(request) {
|
|
603
|
+
if (this.child) {
|
|
604
|
+
this.send(this.buildError(
|
|
605
|
+
"host/update",
|
|
606
|
+
request.requestId,
|
|
607
|
+
"host_busy",
|
|
608
|
+
"Dexly Companion cannot update while a live Codex session is running."
|
|
609
|
+
));
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
try {
|
|
613
|
+
const result = await upgradeInstalledCompanion({
|
|
614
|
+
distTag: request.params.distTag,
|
|
615
|
+
version: request.params.version ?? null
|
|
616
|
+
});
|
|
617
|
+
this.send({
|
|
618
|
+
kind: "host/result",
|
|
619
|
+
action: "host/update",
|
|
620
|
+
requestId: request.requestId,
|
|
621
|
+
result: {
|
|
622
|
+
updated: true,
|
|
623
|
+
previousVersion: result.previousVersion,
|
|
624
|
+
targetSpecifier: result.targetSpecifier,
|
|
625
|
+
...this.buildHealthResult()
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
this.requestProcessExit?.();
|
|
629
|
+
} catch (error) {
|
|
630
|
+
this.send(this.buildError(
|
|
631
|
+
"host/update",
|
|
632
|
+
request.requestId,
|
|
633
|
+
"host_update_failed",
|
|
634
|
+
error instanceof Error ? error.message : "Dexly Companion could not update itself."
|
|
635
|
+
));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
async handleInstallCodex(request) {
|
|
639
|
+
if (this.child) {
|
|
640
|
+
this.send(this.buildError(
|
|
641
|
+
"host/install-codex",
|
|
642
|
+
request.requestId,
|
|
643
|
+
"host_busy",
|
|
644
|
+
"Dexly Companion cannot install Codex while a live Codex session is running."
|
|
645
|
+
));
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
try {
|
|
649
|
+
const result = await installCodexWithCompanion();
|
|
650
|
+
this.send({
|
|
651
|
+
kind: "host/result",
|
|
652
|
+
action: "host/install-codex",
|
|
653
|
+
requestId: request.requestId,
|
|
654
|
+
result: {
|
|
655
|
+
installed: true,
|
|
656
|
+
installCommand: result.installCommand,
|
|
657
|
+
...this.buildHealthResult()
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
} catch (error) {
|
|
661
|
+
this.send(this.buildError(
|
|
662
|
+
"host/install-codex",
|
|
663
|
+
request.requestId,
|
|
664
|
+
"codex_install_failed",
|
|
665
|
+
error instanceof Error ? error.message : "Dexly Companion could not install Codex."
|
|
666
|
+
));
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
attachChildListeners(child) {
|
|
670
|
+
this.childStdoutReader = import_node_readline.default.createInterface({
|
|
671
|
+
input: child.stdout
|
|
672
|
+
});
|
|
673
|
+
this.childStdoutReader.on("line", (line) => {
|
|
674
|
+
if (!line.trim()) {
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
try {
|
|
678
|
+
this.send({
|
|
679
|
+
kind: "codex/jsonrpc",
|
|
680
|
+
payload: JSON.parse(line)
|
|
681
|
+
});
|
|
682
|
+
} catch {
|
|
683
|
+
this.send(this.buildError(
|
|
684
|
+
null,
|
|
685
|
+
null,
|
|
686
|
+
"codex_protocol_error",
|
|
687
|
+
"Dexly Companion received malformed JSON from Codex app-server.",
|
|
688
|
+
{
|
|
689
|
+
line
|
|
690
|
+
}
|
|
691
|
+
));
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
child.stderr.on("data", (chunk) => {
|
|
695
|
+
const lines = chunk.toString("utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
696
|
+
this.stderrLines.push(...lines);
|
|
697
|
+
if (this.stderrLines.length > STDERR_RING_BUFFER_LIMIT) {
|
|
698
|
+
this.stderrLines.splice(0, this.stderrLines.length - STDERR_RING_BUFFER_LIMIT);
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
child.on("exit", (code, signal) => {
|
|
702
|
+
const stderrTail = this.stderrLines.length > 0 ? this.stderrLines.join("\n") : null;
|
|
703
|
+
this.cleanupChild();
|
|
704
|
+
if (this.expectedChildExit) {
|
|
705
|
+
this.expectedChildExit = false;
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
this.send(this.buildError(
|
|
709
|
+
null,
|
|
710
|
+
null,
|
|
711
|
+
"codex_exit",
|
|
712
|
+
"Codex app-server exited unexpectedly.",
|
|
713
|
+
{
|
|
714
|
+
code,
|
|
715
|
+
signal,
|
|
716
|
+
stderrTail
|
|
717
|
+
}
|
|
718
|
+
));
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
forwardJsonRpc(payload) {
|
|
722
|
+
if (!this.child) {
|
|
723
|
+
this.send(this.buildError(
|
|
724
|
+
null,
|
|
725
|
+
null,
|
|
726
|
+
"codex_not_connected",
|
|
727
|
+
`${import_companion4.DEXLY_COMPANION_DISPLAY_NAME} is not connected to Codex app-server.`
|
|
728
|
+
));
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
this.child.stdin.write(`${JSON.stringify(payload)}
|
|
732
|
+
`);
|
|
733
|
+
}
|
|
734
|
+
async stopChild() {
|
|
735
|
+
const child = this.child;
|
|
736
|
+
if (!child) {
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
this.expectedChildExit = true;
|
|
740
|
+
await new Promise((resolve) => {
|
|
741
|
+
const finish = () => {
|
|
742
|
+
child.off("exit", finish);
|
|
743
|
+
resolve();
|
|
744
|
+
};
|
|
745
|
+
child.once("exit", finish);
|
|
746
|
+
child.kill();
|
|
747
|
+
});
|
|
748
|
+
this.cleanupChild();
|
|
749
|
+
this.expectedChildExit = false;
|
|
750
|
+
}
|
|
751
|
+
cleanupChild() {
|
|
752
|
+
this.childStdoutReader?.close();
|
|
753
|
+
this.childStdoutReader = null;
|
|
754
|
+
this.child = null;
|
|
755
|
+
}
|
|
756
|
+
resolveCodexVersion() {
|
|
757
|
+
const result = this.spawnProcessSync(this.codexCommand, ["--version"], {
|
|
758
|
+
encoding: "utf8"
|
|
759
|
+
});
|
|
760
|
+
if (result.error || result.status !== 0) {
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
return result.stdout.trim() || null;
|
|
764
|
+
}
|
|
765
|
+
buildError(action, requestId, code, message, details) {
|
|
766
|
+
return {
|
|
767
|
+
kind: "host/error",
|
|
768
|
+
action,
|
|
769
|
+
requestId,
|
|
770
|
+
code,
|
|
771
|
+
message,
|
|
772
|
+
details
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
// src/native-protocol.ts
|
|
778
|
+
var import_node_buffer = require("node:buffer");
|
|
779
|
+
function encodeNativeMessage(message) {
|
|
780
|
+
const body = import_node_buffer.Buffer.from(JSON.stringify(message), "utf8");
|
|
781
|
+
const header = import_node_buffer.Buffer.alloc(4);
|
|
782
|
+
header.writeUInt32LE(body.byteLength, 0);
|
|
783
|
+
return import_node_buffer.Buffer.concat([header, body]);
|
|
784
|
+
}
|
|
785
|
+
var NativeMessageParser = class {
|
|
786
|
+
buffered = import_node_buffer.Buffer.alloc(0);
|
|
787
|
+
push(chunk, handleMessage) {
|
|
788
|
+
this.buffered = import_node_buffer.Buffer.concat([this.buffered, chunk]);
|
|
789
|
+
while (this.buffered.byteLength >= 4) {
|
|
790
|
+
const bodyLength = this.buffered.readUInt32LE(0);
|
|
791
|
+
if (this.buffered.byteLength < bodyLength + 4) {
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
const body = this.buffered.subarray(4, bodyLength + 4);
|
|
795
|
+
this.buffered = this.buffered.subarray(bodyLength + 4);
|
|
796
|
+
handleMessage(JSON.parse(body.toString("utf8")));
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
function createNativeMessageReader(input, handleMessage) {
|
|
801
|
+
const parser = new NativeMessageParser();
|
|
802
|
+
const onData = (chunk) => {
|
|
803
|
+
parser.push(import_node_buffer.Buffer.isBuffer(chunk) ? chunk : import_node_buffer.Buffer.from(chunk), handleMessage);
|
|
804
|
+
};
|
|
805
|
+
input.on("data", onData);
|
|
806
|
+
return () => {
|
|
807
|
+
input.off("data", onData);
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
function writeNativeMessage(output, message) {
|
|
811
|
+
output.write(encodeNativeMessage(message));
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// src/host-runtime.ts
|
|
815
|
+
async function runNativeHost() {
|
|
816
|
+
const host = new DexlyNativeHost({
|
|
817
|
+
send: (message) => writeNativeMessage(import_node_process3.default.stdout, message),
|
|
818
|
+
requestProcessExit: () => {
|
|
819
|
+
setTimeout(() => import_node_process3.default.exit(0), 25);
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
import_node_process3.default.stdout.on("error", (error) => {
|
|
823
|
+
if ("code" in error && error.code === "EPIPE") {
|
|
824
|
+
void host.close().finally(() => import_node_process3.default.exit(0));
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
const detachReader = createNativeMessageReader(import_node_process3.default.stdin, (message) => {
|
|
828
|
+
void host.handleMessage(message).catch((error) => {
|
|
829
|
+
writeNativeMessage(import_node_process3.default.stdout, {
|
|
830
|
+
kind: "host/error",
|
|
831
|
+
action: null,
|
|
832
|
+
requestId: null,
|
|
833
|
+
code: "host_runtime_error",
|
|
834
|
+
message: error instanceof Error ? error.message : "Dexly Companion host failed."
|
|
835
|
+
});
|
|
836
|
+
});
|
|
837
|
+
});
|
|
838
|
+
const shutdown = async () => {
|
|
839
|
+
detachReader();
|
|
840
|
+
await host.close();
|
|
841
|
+
};
|
|
842
|
+
import_node_process3.default.stdin.on("end", () => {
|
|
843
|
+
void shutdown().finally(() => import_node_process3.default.exit(0));
|
|
844
|
+
});
|
|
845
|
+
import_node_process3.default.on("SIGINT", () => {
|
|
846
|
+
void shutdown().finally(() => import_node_process3.default.exit(0));
|
|
847
|
+
});
|
|
848
|
+
import_node_process3.default.on("SIGTERM", () => {
|
|
849
|
+
void shutdown().finally(() => import_node_process3.default.exit(0));
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// src/host.ts
|
|
854
|
+
void runNativeHost().catch((error) => {
|
|
855
|
+
const message = error instanceof Error ? error.message : "Dexly Companion host failed.";
|
|
856
|
+
import_node_process4.default.stderr.write(`${message}
|
|
857
|
+
`);
|
|
858
|
+
import_node_process4.default.exit(1);
|
|
859
|
+
});
|