@karlorz/codexhost-cli-win32-arm64 0.13.3-5 → 0.13.3-7
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 +1 -1
- package/app/codexhost-distribution.json +1 -1
- package/app/console-server.mjs +844 -80
- package/app/console-web.js +1329 -223
- package/app/cpa-live-voice-hook.js +864 -164
- package/app/desktop-controller.mjs +138 -35
- package/app/host-runtime.mjs +1233 -573
- package/app/plugins/antigravity/plugin.mjs +72 -1
- package/app/plugins/claude-code/plugin.mjs +130 -12
- package/app/plugins/codebuddy/plugin.mjs +79 -5
- package/app/plugins/codex-usage/plugin.mjs +178 -67
- package/app/plugins/cursor-cli/plugin.mjs +20727 -44858
- package/app/plugins/deepseek-harness/plugin.mjs +79 -5
- package/app/plugins/grok/plugin.mjs +888 -157
- package/app/plugins/hermes/plugin.mjs +79 -5
- package/app/plugins/kimi-code/plugin.mjs +72 -1
- package/app/plugins/kiro-cli/plugin.mjs +72 -1
- package/app/plugins/omp/plugin.mjs +2335 -2072
- package/app/plugins/opencode/plugin.mjs +79 -5
- package/app/plugins/pi/plugin.mjs +2432 -2276
- package/app/plugins/qoder/plugin.mjs +79 -5
- package/app/plugins/qoder-cn/plugin.mjs +79 -5
- package/app/plugins/workbuddy/plugin.mjs +79 -5
- package/app/plugins/zcode/plugin.mjs +79 -5
- package/app/renderer-extension.js +2977 -912
- package/bin/codexhost.exe +0 -0
- package/libexec/codexhost-node-repl.exe +0 -0
- package/libexec/codexhost-shim.exe +0 -0
- package/libexec/codexhost-updater.exe +0 -0
- package/package.json +1 -1
package/app/console-server.mjs
CHANGED
|
@@ -5,7 +5,7 @@ var __export = (target, all) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// packages/console-server/src/main.ts
|
|
8
|
-
import
|
|
8
|
+
import path21 from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
|
|
11
11
|
// packages/update-manager/dist/runtime-metadata.js
|
|
@@ -891,7 +891,7 @@ function createBackgroundUpdateManager(dependencies = {}) {
|
|
|
891
891
|
}
|
|
892
892
|
|
|
893
893
|
// packages/console-server/src/harnesses.ts
|
|
894
|
-
import
|
|
894
|
+
import path12 from "node:path";
|
|
895
895
|
|
|
896
896
|
// packages/harness-plugin-files/dist/discovery.js
|
|
897
897
|
import { readdir as readdir2, realpath as realpath2 } from "node:fs/promises";
|
|
@@ -1663,10 +1663,10 @@ function mergeDefs(...defs) {
|
|
|
1663
1663
|
function cloneDef(schema) {
|
|
1664
1664
|
return mergeDefs(schema._zod.def);
|
|
1665
1665
|
}
|
|
1666
|
-
function getElementAtPath(obj,
|
|
1667
|
-
if (!
|
|
1666
|
+
function getElementAtPath(obj, path22) {
|
|
1667
|
+
if (!path22)
|
|
1668
1668
|
return obj;
|
|
1669
|
-
return
|
|
1669
|
+
return path22.reduce((acc, key) => acc?.[key], obj);
|
|
1670
1670
|
}
|
|
1671
1671
|
function promiseAllObject(promisesObj) {
|
|
1672
1672
|
const keys = Object.keys(promisesObj);
|
|
@@ -2075,11 +2075,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
2075
2075
|
}
|
|
2076
2076
|
return false;
|
|
2077
2077
|
}
|
|
2078
|
-
function prefixIssues(
|
|
2078
|
+
function prefixIssues(path22, issues) {
|
|
2079
2079
|
return issues.map((iss) => {
|
|
2080
2080
|
var _a3;
|
|
2081
2081
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
2082
|
-
iss.path.unshift(
|
|
2082
|
+
iss.path.unshift(path22);
|
|
2083
2083
|
return iss;
|
|
2084
2084
|
});
|
|
2085
2085
|
}
|
|
@@ -2226,16 +2226,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2226
2226
|
}
|
|
2227
2227
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
2228
2228
|
const fieldErrors = { _errors: [] };
|
|
2229
|
-
const processError = (error52,
|
|
2229
|
+
const processError = (error52, path22 = []) => {
|
|
2230
2230
|
for (const issue2 of error52.issues) {
|
|
2231
2231
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2232
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2232
|
+
issue2.errors.map((issues) => processError({ issues }, [...path22, ...issue2.path]));
|
|
2233
2233
|
} else if (issue2.code === "invalid_key") {
|
|
2234
|
-
processError({ issues: issue2.issues }, [...
|
|
2234
|
+
processError({ issues: issue2.issues }, [...path22, ...issue2.path]);
|
|
2235
2235
|
} else if (issue2.code === "invalid_element") {
|
|
2236
|
-
processError({ issues: issue2.issues }, [...
|
|
2236
|
+
processError({ issues: issue2.issues }, [...path22, ...issue2.path]);
|
|
2237
2237
|
} else {
|
|
2238
|
-
const fullpath = [...
|
|
2238
|
+
const fullpath = [...path22, ...issue2.path];
|
|
2239
2239
|
if (fullpath.length === 0) {
|
|
2240
2240
|
fieldErrors._errors.push(mapper(issue2));
|
|
2241
2241
|
} else {
|
|
@@ -2262,17 +2262,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2262
2262
|
}
|
|
2263
2263
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
2264
2264
|
const result = { errors: [] };
|
|
2265
|
-
const processError = (error52,
|
|
2265
|
+
const processError = (error52, path22 = []) => {
|
|
2266
2266
|
var _a3, _b;
|
|
2267
2267
|
for (const issue2 of error52.issues) {
|
|
2268
2268
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2269
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2269
|
+
issue2.errors.map((issues) => processError({ issues }, [...path22, ...issue2.path]));
|
|
2270
2270
|
} else if (issue2.code === "invalid_key") {
|
|
2271
|
-
processError({ issues: issue2.issues }, [...
|
|
2271
|
+
processError({ issues: issue2.issues }, [...path22, ...issue2.path]);
|
|
2272
2272
|
} else if (issue2.code === "invalid_element") {
|
|
2273
|
-
processError({ issues: issue2.issues }, [...
|
|
2273
|
+
processError({ issues: issue2.issues }, [...path22, ...issue2.path]);
|
|
2274
2274
|
} else {
|
|
2275
|
-
const fullpath = [...
|
|
2275
|
+
const fullpath = [...path22, ...issue2.path];
|
|
2276
2276
|
if (fullpath.length === 0) {
|
|
2277
2277
|
result.errors.push(mapper(issue2));
|
|
2278
2278
|
continue;
|
|
@@ -2304,8 +2304,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2304
2304
|
}
|
|
2305
2305
|
function toDotPath(_path) {
|
|
2306
2306
|
const segs = [];
|
|
2307
|
-
const
|
|
2308
|
-
for (const seg of
|
|
2307
|
+
const path22 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2308
|
+
for (const seg of path22) {
|
|
2309
2309
|
if (typeof seg === "number")
|
|
2310
2310
|
segs.push(`[${seg}]`);
|
|
2311
2311
|
else if (typeof seg === "symbol")
|
|
@@ -14997,13 +14997,13 @@ function resolveRef(ref, ctx) {
|
|
|
14997
14997
|
if (!ref.startsWith("#")) {
|
|
14998
14998
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
14999
14999
|
}
|
|
15000
|
-
const
|
|
15001
|
-
if (
|
|
15000
|
+
const path22 = ref.slice(1).split("/").filter(Boolean);
|
|
15001
|
+
if (path22.length === 0) {
|
|
15002
15002
|
return ctx.rootSchema;
|
|
15003
15003
|
}
|
|
15004
15004
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
15005
|
-
if (
|
|
15006
|
-
const key =
|
|
15005
|
+
if (path22[0] === defsKey) {
|
|
15006
|
+
const key = path22[1];
|
|
15007
15007
|
if (!key || !ctx.defs[key]) {
|
|
15008
15008
|
throw new Error(`Reference not found: ${ref}`);
|
|
15009
15009
|
}
|
|
@@ -15709,6 +15709,7 @@ var consoleOpenResultSchema = external_exports.strictObject({
|
|
|
15709
15709
|
url: external_exports.string().max(2048).regex(/^http:\/\/(?:127\.0\.0\.1|localhost):\d{1,5}\/(?:\?.*)?$/u)
|
|
15710
15710
|
});
|
|
15711
15711
|
var CONSOLE_HOST_METHODS = Object.freeze([
|
|
15712
|
+
"codexhost/voice/gateway-status",
|
|
15712
15713
|
"codexhost/console/remote-connections",
|
|
15713
15714
|
"codexhost/runtime/status",
|
|
15714
15715
|
"codexhost/remote/ssh-setup",
|
|
@@ -15719,6 +15720,8 @@ var CONSOLE_HOST_METHODS = Object.freeze([
|
|
|
15719
15720
|
"codexhost/harness/installation",
|
|
15720
15721
|
"codexhost/harness/launch-settings/get",
|
|
15721
15722
|
"codexhost/harness/launch-settings/set",
|
|
15723
|
+
"codexhost/harness/grok-computer-use/get",
|
|
15724
|
+
"codexhost/harness/grok-computer-use/set",
|
|
15722
15725
|
"codexhost/harness/web-ui/open",
|
|
15723
15726
|
"codexhost/harness/accounts/sources",
|
|
15724
15727
|
"codexhost/harness/accounts/list",
|
|
@@ -16250,7 +16253,9 @@ var harnessInspectionSchema = external_exports.union([
|
|
|
16250
16253
|
var harnessInspectParamsSchema = external_exports.object({
|
|
16251
16254
|
harnessId: harnessIdSchema,
|
|
16252
16255
|
cwd: nonBlankTextSchema2.max(16384).optional(),
|
|
16253
|
-
refresh: external_exports.boolean().optional()
|
|
16256
|
+
refresh: external_exports.boolean().optional(),
|
|
16257
|
+
/** `availability` asks only whether the Harness can run; see `InspectHarnessInput.purpose`. */
|
|
16258
|
+
purpose: external_exports.literal("availability").optional()
|
|
16254
16259
|
}).strict();
|
|
16255
16260
|
var harnessWebUiOpenParamsSchema = external_exports.object({
|
|
16256
16261
|
harnessId: harnessIdSchema
|
|
@@ -16424,6 +16429,26 @@ var harnessLaunchSettingsSchema = external_exports.object({
|
|
|
16424
16429
|
restartRequired: external_exports.boolean()
|
|
16425
16430
|
}).strict();
|
|
16426
16431
|
|
|
16432
|
+
// packages/shared-contracts/dist/grok-computer-use.js
|
|
16433
|
+
var grokComputerUseGetSchema = external_exports.object({}).strict();
|
|
16434
|
+
var grokComputerUseSetSchema = external_exports.object({
|
|
16435
|
+
enabled: external_exports.boolean().optional(),
|
|
16436
|
+
browser: external_exports.boolean().optional()
|
|
16437
|
+
}).strict().refine((value) => value.enabled !== void 0 || value.browser !== void 0, {
|
|
16438
|
+
message: "Expected enabled or browser"
|
|
16439
|
+
});
|
|
16440
|
+
var grokComputerUseSettingsSchema = external_exports.object({
|
|
16441
|
+
enabled: external_exports.boolean(),
|
|
16442
|
+
available: external_exports.boolean(),
|
|
16443
|
+
unavailableReason: external_exports.string().min(1).max(500).optional(),
|
|
16444
|
+
browser: external_exports.boolean().optional(),
|
|
16445
|
+
browserFilesPresent: external_exports.boolean().optional(),
|
|
16446
|
+
browserAvailable: external_exports.boolean().optional(),
|
|
16447
|
+
browserUnavailableReason: external_exports.string().min(1).max(500).optional(),
|
|
16448
|
+
applyFailed: external_exports.boolean().optional(),
|
|
16449
|
+
applyError: external_exports.string().min(1).max(500).optional()
|
|
16450
|
+
}).strict();
|
|
16451
|
+
|
|
16427
16452
|
// packages/shared-contracts/dist/harness-installation.js
|
|
16428
16453
|
var harnessInstallationParamsSchema = external_exports.object({
|
|
16429
16454
|
harnessId: harnessIdSchema,
|
|
@@ -16816,6 +16841,53 @@ var remoteConnectionsReplySchema = external_exports.union([
|
|
|
16816
16841
|
external_exports.strictObject({ error: external_exports.strictObject({ code: external_exports.number().int(), message: external_exports.string() }) })
|
|
16817
16842
|
]);
|
|
16818
16843
|
|
|
16844
|
+
// packages/shared-contracts/dist/voice-settings.js
|
|
16845
|
+
var VOICE_GATEWAY_STATUS_METHOD = "codexhost/voice/gateway-status";
|
|
16846
|
+
var voiceRoutesSchema = external_exports.strictObject({
|
|
16847
|
+
chatExperience: external_exports.enum(["chatgpt", "codex"]),
|
|
16848
|
+
chatConnection: external_exports.enum(["desktop", "gateway"]),
|
|
16849
|
+
workConnection: external_exports.enum(["desktop", "gateway"])
|
|
16850
|
+
});
|
|
16851
|
+
var DEFAULT_VOICE_ROUTES = Object.freeze({
|
|
16852
|
+
chatExperience: "chatgpt",
|
|
16853
|
+
chatConnection: "desktop",
|
|
16854
|
+
workConnection: "gateway"
|
|
16855
|
+
});
|
|
16856
|
+
var voiceSettingsSchema = voiceRoutesSchema.extend({ inject: external_exports.boolean() });
|
|
16857
|
+
var voiceSettingsRequestSchema = external_exports.discriminatedUnion("operation", [
|
|
16858
|
+
external_exports.strictObject({
|
|
16859
|
+
operation: external_exports.literal("get"),
|
|
16860
|
+
runtime: external_exports.strictObject({
|
|
16861
|
+
id: external_exports.string().uuid(),
|
|
16862
|
+
revision: external_exports.string().max(64),
|
|
16863
|
+
activeCall: external_exports.boolean()
|
|
16864
|
+
}).optional()
|
|
16865
|
+
}),
|
|
16866
|
+
external_exports.strictObject({ operation: external_exports.literal("migrate"), routes: voiceRoutesSchema }),
|
|
16867
|
+
external_exports.strictObject({
|
|
16868
|
+
operation: external_exports.literal("set"),
|
|
16869
|
+
settings: voiceSettingsSchema,
|
|
16870
|
+
revision: external_exports.string().max(64)
|
|
16871
|
+
}),
|
|
16872
|
+
external_exports.strictObject({ operation: external_exports.literal("restart"), confirm: external_exports.literal(true) })
|
|
16873
|
+
]);
|
|
16874
|
+
var voiceSettingsStateSchema = external_exports.strictObject({
|
|
16875
|
+
settings: voiceSettingsSchema,
|
|
16876
|
+
initialized: external_exports.boolean(),
|
|
16877
|
+
revision: external_exports.string(),
|
|
16878
|
+
activeInject: external_exports.boolean().nullable(),
|
|
16879
|
+
envOverride: external_exports.enum(["0", "1"]).nullable(),
|
|
16880
|
+
activeEnvOverride: external_exports.enum(["0", "1"]).nullable(),
|
|
16881
|
+
routesActive: external_exports.boolean().nullable(),
|
|
16882
|
+
activeCall: external_exports.boolean().nullable(),
|
|
16883
|
+
gatewayReady: external_exports.boolean().nullable(),
|
|
16884
|
+
restartAvailable: external_exports.boolean(),
|
|
16885
|
+
restart: external_exports.strictObject({
|
|
16886
|
+
phase: external_exports.enum(["idle", "restarting", "succeeded", "failed"]),
|
|
16887
|
+
error: external_exports.string().nullable()
|
|
16888
|
+
})
|
|
16889
|
+
});
|
|
16890
|
+
|
|
16819
16891
|
// packages/shared-contracts/dist/index.js
|
|
16820
16892
|
var workspaceContractVersionSchema = external_exports.literal(WORKSPACE_CONTRACT_VERSION);
|
|
16821
16893
|
|
|
@@ -17009,23 +17081,365 @@ var HarnessLaunchSettingsStore = class {
|
|
|
17009
17081
|
}
|
|
17010
17082
|
};
|
|
17011
17083
|
|
|
17012
|
-
// packages/
|
|
17084
|
+
// packages/harness-plugin-files/dist/grok-computer-use.js
|
|
17085
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
17086
|
+
import { access, mkdir as mkdir5, readFile as readFile5, rename as rename3, rm as rm5, writeFile as writeFile4 } from "node:fs/promises";
|
|
17013
17087
|
import os2 from "node:os";
|
|
17088
|
+
import path10 from "node:path";
|
|
17089
|
+
|
|
17090
|
+
// packages/harness-plugin-files/dist/grok-computer-use-lock.js
|
|
17091
|
+
import { mkdir as mkdir4, rm as rm4 } from "node:fs/promises";
|
|
17014
17092
|
import path9 from "node:path";
|
|
17093
|
+
import { setTimeout as delay3 } from "node:timers/promises";
|
|
17094
|
+
async function withGrokComputerUseLock(dataDirectory3, grokHome2, operation) {
|
|
17095
|
+
const locks = [
|
|
17096
|
+
.../* @__PURE__ */ new Set([
|
|
17097
|
+
path9.join(dataDirectory3, ".grok-computer-use.lock"),
|
|
17098
|
+
path9.join(grokHome2, ".codexhost-cua.lock")
|
|
17099
|
+
])
|
|
17100
|
+
].sort();
|
|
17101
|
+
const acquired = [];
|
|
17102
|
+
const deadline = Date.now() + 5e3;
|
|
17103
|
+
try {
|
|
17104
|
+
for (const lock of locks) {
|
|
17105
|
+
await mkdir4(path9.dirname(lock), { recursive: true, mode: 448 });
|
|
17106
|
+
for (; ; ) {
|
|
17107
|
+
try {
|
|
17108
|
+
await mkdir4(lock, { mode: 448 });
|
|
17109
|
+
acquired.push(lock);
|
|
17110
|
+
break;
|
|
17111
|
+
} catch (error51) {
|
|
17112
|
+
if (error51.code !== "EEXIST")
|
|
17113
|
+
throw error51;
|
|
17114
|
+
if (Date.now() >= deadline)
|
|
17115
|
+
throw new Error("Grok Computer Use settings are locked");
|
|
17116
|
+
await delay3(25);
|
|
17117
|
+
}
|
|
17118
|
+
}
|
|
17119
|
+
}
|
|
17120
|
+
return await operation();
|
|
17121
|
+
} finally {
|
|
17122
|
+
for (const lock of acquired.reverse())
|
|
17123
|
+
await rm4(lock, { recursive: true });
|
|
17124
|
+
}
|
|
17125
|
+
}
|
|
17126
|
+
|
|
17127
|
+
// packages/harness-plugin-files/dist/grok-computer-use.js
|
|
17128
|
+
var GROK_BROWSER_BINDING_SUPPORTED = false;
|
|
17129
|
+
var PREFERENCE_FILE = "grok-computer-use.json";
|
|
17130
|
+
var TABLE = "[mcp_servers.cua_repl]";
|
|
17131
|
+
var ENV_TABLE = "[mcp_servers.cua_repl.env]";
|
|
17132
|
+
var COMPUTER_UNAVAILABLE = "ChatGPT Desktop Computer Use files were not found. Install ChatGPT.app with Computer Use, then try again.";
|
|
17133
|
+
var BROWSER_FILES_UNAVAILABLE = "ChatGPT Desktop in-app browser files were not found. Update ChatGPT.app, then try again.";
|
|
17134
|
+
var BROWSER_UNSUPPORTED = "ChatGPT Desktop does not authorize Grok to use the in-app browser. The preference is saved as intent until Desktop supports an external Harness.";
|
|
17135
|
+
var APPLY_FAILED = "Could not apply Grok Computer Use settings to Grok config.";
|
|
17136
|
+
var GrokComputerUseError = class extends Error {
|
|
17137
|
+
constructor(message) {
|
|
17138
|
+
super(message);
|
|
17139
|
+
this.name = "GrokComputerUseError";
|
|
17140
|
+
}
|
|
17141
|
+
};
|
|
17142
|
+
function dataDirectory(environment) {
|
|
17143
|
+
return environment.CODEXHOST_DATA_DIR ? path10.resolve(environment.CODEXHOST_DATA_DIR) : path10.join(os2.homedir(), ".codexhost");
|
|
17144
|
+
}
|
|
17145
|
+
function grokHome(environment) {
|
|
17146
|
+
return environment.GROK_HOME ? path10.resolve(environment.GROK_HOME) : path10.join(environment.HOME ?? os2.homedir(), ".grok");
|
|
17147
|
+
}
|
|
17148
|
+
function chatgptApp(environment, platform) {
|
|
17149
|
+
if (environment.CODEXHOST_CHATGPT_APP)
|
|
17150
|
+
return path10.resolve(environment.CODEXHOST_CHATGPT_APP);
|
|
17151
|
+
if (platform === "darwin")
|
|
17152
|
+
return "/Applications/ChatGPT.app";
|
|
17153
|
+
return void 0;
|
|
17154
|
+
}
|
|
17155
|
+
async function isFile(target) {
|
|
17156
|
+
try {
|
|
17157
|
+
await access(target);
|
|
17158
|
+
return true;
|
|
17159
|
+
} catch {
|
|
17160
|
+
return false;
|
|
17161
|
+
}
|
|
17162
|
+
}
|
|
17163
|
+
function isNotFound(error51) {
|
|
17164
|
+
return Boolean(error51 && typeof error51 === "object" && "code" in error51 && error51.code === "ENOENT");
|
|
17165
|
+
}
|
|
17166
|
+
async function writeAtomically(file2, content) {
|
|
17167
|
+
await mkdir5(path10.dirname(file2), { recursive: true, mode: 448 });
|
|
17168
|
+
const temporary = `${file2}.${randomUUID3()}.tmp`;
|
|
17169
|
+
try {
|
|
17170
|
+
await writeFile4(temporary, content, { mode: 384, flag: "wx" });
|
|
17171
|
+
await rename3(temporary, file2);
|
|
17172
|
+
} finally {
|
|
17173
|
+
await rm5(temporary, { force: true });
|
|
17174
|
+
}
|
|
17175
|
+
}
|
|
17176
|
+
function launchPaths(environment, platform) {
|
|
17177
|
+
const app = chatgptApp(environment, platform);
|
|
17178
|
+
if (!app)
|
|
17179
|
+
return void 0;
|
|
17180
|
+
const resources = path10.join(app, "Contents", "Resources");
|
|
17181
|
+
const cuaNode = path10.join(resources, "cua_node");
|
|
17182
|
+
return {
|
|
17183
|
+
node: path10.join(cuaNode, "bin", "node"),
|
|
17184
|
+
cuaRepl: path10.join(cuaNode, "lib", "node_modules", "@oai", "cua-repl", "bin", "cua-repl.mjs"),
|
|
17185
|
+
nodeRepl: path10.join(cuaNode, "bin", "node_repl"),
|
|
17186
|
+
nodeModules: path10.join(cuaNode, "lib", "node_modules"),
|
|
17187
|
+
codexCli: path10.join(resources, "codex-cli", "bin", "codex"),
|
|
17188
|
+
skyApp: path10.join(environment.HOME ?? os2.homedir(), ".codex", "computer-use", "Codex Computer Use.app"),
|
|
17189
|
+
codexHome: path10.join(environment.HOME ?? os2.homedir(), ".codex"),
|
|
17190
|
+
browserDesktop: path10.join(cuaNode, "lib", "node_modules", "@oai", "browser-desktop")
|
|
17191
|
+
};
|
|
17192
|
+
}
|
|
17193
|
+
async function sharedRuntimePresent(launch) {
|
|
17194
|
+
const present = await Promise.all([launch.node, launch.cuaRepl, launch.nodeRepl].map(isFile));
|
|
17195
|
+
return present.every(Boolean);
|
|
17196
|
+
}
|
|
17197
|
+
async function computerFilesPresent(launch) {
|
|
17198
|
+
const present = await Promise.all([isFile(launch.skyApp), isFile(launch.codexCli)]);
|
|
17199
|
+
return present.every(Boolean);
|
|
17200
|
+
}
|
|
17201
|
+
async function browserFilesPresent(launch) {
|
|
17202
|
+
return isFile(path10.join(launch.browserDesktop, "package.json"));
|
|
17203
|
+
}
|
|
17204
|
+
async function discoverGrokComputerUseLaunch(environment = process.env, platform = process.platform) {
|
|
17205
|
+
const launch = launchPaths(environment, platform);
|
|
17206
|
+
if (!launch || !await sharedRuntimePresent(launch))
|
|
17207
|
+
return void 0;
|
|
17208
|
+
return launch;
|
|
17209
|
+
}
|
|
17210
|
+
function tableRange(text, header) {
|
|
17211
|
+
const lines = text.split(/\n/);
|
|
17212
|
+
let start = -1;
|
|
17213
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
17214
|
+
if (lines[index] === header) {
|
|
17215
|
+
start = index;
|
|
17216
|
+
break;
|
|
17217
|
+
}
|
|
17218
|
+
}
|
|
17219
|
+
if (start < 0)
|
|
17220
|
+
return void 0;
|
|
17221
|
+
let end = lines.length;
|
|
17222
|
+
for (let index = start + 1; index < lines.length; index += 1) {
|
|
17223
|
+
const line = lines[index] ?? "";
|
|
17224
|
+
if (line.startsWith("[") && line.endsWith("]")) {
|
|
17225
|
+
end = index;
|
|
17226
|
+
break;
|
|
17227
|
+
}
|
|
17228
|
+
}
|
|
17229
|
+
return { start, end };
|
|
17230
|
+
}
|
|
17231
|
+
function removeTables(text, headers) {
|
|
17232
|
+
let current = text;
|
|
17233
|
+
for (const header of headers) {
|
|
17234
|
+
const range = tableRange(current, header);
|
|
17235
|
+
if (!range)
|
|
17236
|
+
continue;
|
|
17237
|
+
const lines = current.split(/\n/);
|
|
17238
|
+
lines.splice(range.start, range.end - range.start);
|
|
17239
|
+
current = lines.join("\n");
|
|
17240
|
+
}
|
|
17241
|
+
return current.replace(/\n{3,}/g, "\n\n");
|
|
17242
|
+
}
|
|
17243
|
+
function cuaReplEnabledInToml(text) {
|
|
17244
|
+
const range = tableRange(text, TABLE);
|
|
17245
|
+
if (!range)
|
|
17246
|
+
return false;
|
|
17247
|
+
const body = text.split(/\n/).slice(range.start, range.end);
|
|
17248
|
+
for (const line of body) {
|
|
17249
|
+
if (/^enabled\s*=\s*false\b/u.test(line))
|
|
17250
|
+
return false;
|
|
17251
|
+
if (/^enabled\s*=\s*true\b/u.test(line))
|
|
17252
|
+
return true;
|
|
17253
|
+
}
|
|
17254
|
+
return true;
|
|
17255
|
+
}
|
|
17256
|
+
function inferredSurfaces(text) {
|
|
17257
|
+
if (!cuaReplEnabledInToml(text))
|
|
17258
|
+
return { enabled: false, browser: false };
|
|
17259
|
+
const range = tableRange(text, ENV_TABLE);
|
|
17260
|
+
const body = range ? text.split(/\n/).slice(range.start, range.end).join("\n") : "";
|
|
17261
|
+
const literal2 = /^CUA_REPL_ENABLED_SURFACES\s*=\s*(["'])([^"']*)\1\s*(?:#.*)?$/mu.exec(body);
|
|
17262
|
+
if (!literal2)
|
|
17263
|
+
return { enabled: true, browser: false };
|
|
17264
|
+
const surfaces = new Set(literal2[2]?.split(",").map((surface) => surface.trim()));
|
|
17265
|
+
return { enabled: surfaces.has("computer"), browser: surfaces.has("browser") };
|
|
17266
|
+
}
|
|
17267
|
+
function stanza(launch, state) {
|
|
17268
|
+
const trusted = `${launch.codexHome}:${launch.nodeModules}`;
|
|
17269
|
+
const services = {};
|
|
17270
|
+
if (state.browser)
|
|
17271
|
+
services.browser = "@oai/browser-desktop/service";
|
|
17272
|
+
if (state.enabled)
|
|
17273
|
+
services.sky = "@oai/sky/service";
|
|
17274
|
+
const surfaces = [state.browser ? "browser" : "", state.enabled ? "computer" : ""].filter(Boolean).join(",");
|
|
17275
|
+
const backends = state.browser ? 'BROWSER_USE_AVAILABLE_BACKENDS = "iab"\n' : "";
|
|
17276
|
+
const active = state.enabled || state.browser;
|
|
17277
|
+
return `${TABLE}
|
|
17278
|
+
command = ${JSON.stringify(launch.node)}
|
|
17279
|
+
args = [${JSON.stringify(launch.cuaRepl)}]
|
|
17280
|
+
enabled = ${active ? "true" : "false"}
|
|
17281
|
+
startup_timeout_sec = 120
|
|
17282
|
+
|
|
17283
|
+
${ENV_TABLE}
|
|
17284
|
+
NODE_REPL_TRUSTED_SERVICES = ${JSON.stringify(JSON.stringify(services))}
|
|
17285
|
+
NODE_REPL_TRUSTED_CODE_PATHS = ${JSON.stringify(trusted)}
|
|
17286
|
+
NODE_REPL_NODE_PATH = ${JSON.stringify(launch.node)}
|
|
17287
|
+
CODEX_HOME = ${JSON.stringify(launch.codexHome)}
|
|
17288
|
+
CUA_REPL_NODE_REPL_PATH = ${JSON.stringify(launch.nodeRepl)}
|
|
17289
|
+
NODE_REPL_NODE_MODULE_DIRS = ${JSON.stringify(launch.nodeModules)}
|
|
17290
|
+
NODE_REPL_NATIVE_PIPE_CONNECT_TIMEOUT_MS = "1000"
|
|
17291
|
+
SKY_CUA_SERVICE_PATH = ${JSON.stringify(launch.skyApp)}
|
|
17292
|
+
CODEX_CLI_PATH = ${JSON.stringify(launch.codexCli)}
|
|
17293
|
+
${backends}CUA_REPL_ENABLED_SURFACES = ${JSON.stringify(surfaces)}
|
|
17294
|
+
`;
|
|
17295
|
+
}
|
|
17296
|
+
async function readPreference(environment) {
|
|
17297
|
+
try {
|
|
17298
|
+
const document = JSON.parse(await readFile5(path10.join(dataDirectory(environment), PREFERENCE_FILE), "utf8"));
|
|
17299
|
+
return {
|
|
17300
|
+
...typeof document.enabled === "boolean" ? { enabled: document.enabled } : {},
|
|
17301
|
+
...typeof document.browser === "boolean" ? { browser: document.browser } : {},
|
|
17302
|
+
...typeof document.applyError === "string" && document.applyError ? { applyError: document.applyError } : {}
|
|
17303
|
+
};
|
|
17304
|
+
} catch (error51) {
|
|
17305
|
+
if (isNotFound(error51))
|
|
17306
|
+
return void 0;
|
|
17307
|
+
throw new GrokComputerUseError("Could not read Grok Computer Use settings");
|
|
17308
|
+
}
|
|
17309
|
+
}
|
|
17310
|
+
async function writePreference(environment, state) {
|
|
17311
|
+
const document = {
|
|
17312
|
+
...typeof state.enabled === "boolean" ? { enabled: state.enabled } : {},
|
|
17313
|
+
...typeof state.browser === "boolean" ? { browser: state.browser } : {},
|
|
17314
|
+
...state.applyError ? { applyError: state.applyError } : {}
|
|
17315
|
+
};
|
|
17316
|
+
await writeAtomically(path10.join(dataDirectory(environment), PREFERENCE_FILE), `${JSON.stringify(document)}
|
|
17317
|
+
`);
|
|
17318
|
+
}
|
|
17319
|
+
async function readGrokConfig(environment) {
|
|
17320
|
+
try {
|
|
17321
|
+
return await readFile5(path10.join(grokHome(environment), "config.toml"), "utf8");
|
|
17322
|
+
} catch (error51) {
|
|
17323
|
+
if (isNotFound(error51))
|
|
17324
|
+
return "";
|
|
17325
|
+
throw new GrokComputerUseError("Could not read Grok config");
|
|
17326
|
+
}
|
|
17327
|
+
}
|
|
17328
|
+
async function writeGrokConfig(environment, text) {
|
|
17329
|
+
await writeAtomically(path10.join(grokHome(environment), "config.toml"), text.endsWith("\n") ? text : `${text}
|
|
17330
|
+
`);
|
|
17331
|
+
}
|
|
17332
|
+
async function currentState(environment, preference) {
|
|
17333
|
+
const resolved = preference ?? await readPreference(environment);
|
|
17334
|
+
const inferred = resolved?.enabled === void 0 ? inferredSurfaces(await readGrokConfig(environment)) : void 0;
|
|
17335
|
+
return {
|
|
17336
|
+
enabled: resolved?.enabled ?? inferred?.enabled ?? false,
|
|
17337
|
+
browser: resolved ? resolved.browser ?? false : inferred?.browser ?? false,
|
|
17338
|
+
...resolved?.applyError ? { applyError: resolved.applyError } : {}
|
|
17339
|
+
};
|
|
17340
|
+
}
|
|
17341
|
+
function settingsFrom(state, files) {
|
|
17342
|
+
const bindable = files.browser && GROK_BROWSER_BINDING_SUPPORTED;
|
|
17343
|
+
return {
|
|
17344
|
+
enabled: state.enabled,
|
|
17345
|
+
available: files.computer,
|
|
17346
|
+
...files.computer ? {} : { unavailableReason: COMPUTER_UNAVAILABLE },
|
|
17347
|
+
browser: state.browser,
|
|
17348
|
+
browserFilesPresent: files.browser,
|
|
17349
|
+
browserAvailable: bindable,
|
|
17350
|
+
...bindable ? {} : {
|
|
17351
|
+
browserUnavailableReason: files.browser ? BROWSER_UNSUPPORTED : BROWSER_FILES_UNAVAILABLE
|
|
17352
|
+
},
|
|
17353
|
+
...state.applyError ? { applyFailed: true, applyError: state.applyError } : {}
|
|
17354
|
+
};
|
|
17355
|
+
}
|
|
17356
|
+
async function surfaceFiles(launch) {
|
|
17357
|
+
if (!launch)
|
|
17358
|
+
return { computer: false, browser: false };
|
|
17359
|
+
const [computer, browser] = await Promise.all([
|
|
17360
|
+
computerFilesPresent(launch),
|
|
17361
|
+
browserFilesPresent(launch)
|
|
17362
|
+
]);
|
|
17363
|
+
return { computer, browser };
|
|
17364
|
+
}
|
|
17365
|
+
async function getGrokComputerUseSettings(environment = process.env, platform = process.platform) {
|
|
17366
|
+
const launch = await discoverGrokComputerUseLaunch(environment, platform);
|
|
17367
|
+
return settingsFrom(await currentState(environment), await surfaceFiles(launch));
|
|
17368
|
+
}
|
|
17369
|
+
async function writeConfigForState(state, launch, files, environment) {
|
|
17370
|
+
const current = await readGrokConfig(environment);
|
|
17371
|
+
const range = tableRange(current, TABLE);
|
|
17372
|
+
const effective = {
|
|
17373
|
+
enabled: state.enabled && files.computer,
|
|
17374
|
+
browser: state.browser && files.browser && GROK_BROWSER_BINDING_SUPPORTED
|
|
17375
|
+
};
|
|
17376
|
+
if (!effective.enabled && !effective.browser && !range)
|
|
17377
|
+
return;
|
|
17378
|
+
if (!launch) {
|
|
17379
|
+
if (!range)
|
|
17380
|
+
return;
|
|
17381
|
+
const lines = current.split(/\n/);
|
|
17382
|
+
const body = lines.slice(range.start, range.end);
|
|
17383
|
+
const replaced = body.map((line) => /^enabled\s*=/u.test(line) ? "enabled = false" : line);
|
|
17384
|
+
if (!replaced.some((line) => /^enabled\s*=/u.test(line))) {
|
|
17385
|
+
replaced.splice(1, 0, "enabled = false");
|
|
17386
|
+
}
|
|
17387
|
+
lines.splice(range.start, range.end - range.start, ...replaced);
|
|
17388
|
+
await writeGrokConfig(environment, lines.join("\n"));
|
|
17389
|
+
return;
|
|
17390
|
+
}
|
|
17391
|
+
const stripped = removeTables(current, [ENV_TABLE, TABLE]).replace(/\s+$/u, "\n");
|
|
17392
|
+
await writeGrokConfig(environment, `${stripped.trimEnd()}
|
|
17393
|
+
|
|
17394
|
+
${stanza(launch, effective)}`);
|
|
17395
|
+
}
|
|
17396
|
+
async function setGrokComputerUse(update, environment = process.env, platform = process.platform) {
|
|
17397
|
+
return withGrokComputerUseLock(dataDirectory(environment), grokHome(environment), async () => {
|
|
17398
|
+
const launch = await discoverGrokComputerUseLaunch(environment, platform);
|
|
17399
|
+
const files = await surfaceFiles(launch);
|
|
17400
|
+
if (update.enabled && !files.computer) {
|
|
17401
|
+
throw new GrokComputerUseError(COMPUTER_UNAVAILABLE);
|
|
17402
|
+
}
|
|
17403
|
+
if (update.browser && !files.browser) {
|
|
17404
|
+
throw new GrokComputerUseError(BROWSER_FILES_UNAVAILABLE);
|
|
17405
|
+
}
|
|
17406
|
+
const previous = await currentState(environment);
|
|
17407
|
+
const state = {
|
|
17408
|
+
enabled: update.enabled ?? previous.enabled,
|
|
17409
|
+
browser: update.browser ?? previous.browser
|
|
17410
|
+
};
|
|
17411
|
+
try {
|
|
17412
|
+
await writeConfigForState(state, launch, files, environment);
|
|
17413
|
+
await writePreference(environment, state);
|
|
17414
|
+
} catch (error51) {
|
|
17415
|
+
await writeConfigForState(previous, launch, files, environment).catch(() => void 0);
|
|
17416
|
+
const applyError = error51 instanceof GrokComputerUseError ? error51.message : APPLY_FAILED;
|
|
17417
|
+
await writePreference(environment, { ...previous, applyError }).catch(() => void 0);
|
|
17418
|
+
if (error51 instanceof GrokComputerUseError)
|
|
17419
|
+
throw error51;
|
|
17420
|
+
throw new GrokComputerUseError(APPLY_FAILED);
|
|
17421
|
+
}
|
|
17422
|
+
return settingsFrom(state, files);
|
|
17423
|
+
});
|
|
17424
|
+
}
|
|
17425
|
+
|
|
17426
|
+
// packages/console-server/src/paths.ts
|
|
17427
|
+
import os3 from "node:os";
|
|
17428
|
+
import path11 from "node:path";
|
|
17015
17429
|
var DEFAULT_CONSOLE_PORT = 4399;
|
|
17016
17430
|
var CONSOLE_PORT_ENV = "CODEXHOST_CONSOLE_PORT";
|
|
17017
17431
|
var LAUNCHER_EXECUTABLE_ENV = "CODEXHOST_LAUNCHER_EXECUTABLE";
|
|
17018
|
-
function
|
|
17019
|
-
return environment.CODEXHOST_DATA_DIR ?
|
|
17432
|
+
function dataDirectory2(environment = process.env) {
|
|
17433
|
+
return environment.CODEXHOST_DATA_DIR ? path11.resolve(environment.CODEXHOST_DATA_DIR) : path11.join(os3.homedir(), ".codexhost");
|
|
17020
17434
|
}
|
|
17021
17435
|
function consolePaths(environment = process.env) {
|
|
17022
|
-
const root =
|
|
17436
|
+
const root = dataDirectory2(environment);
|
|
17023
17437
|
return {
|
|
17024
17438
|
dataDirectory: root,
|
|
17025
|
-
startupRecordFile:
|
|
17026
|
-
controllerStatusFile:
|
|
17027
|
-
logsDirectory:
|
|
17028
|
-
hostDescriptorDirectory:
|
|
17439
|
+
startupRecordFile: path11.join(root, "diagnostics", "launcher-startup-v1.json"),
|
|
17440
|
+
controllerStatusFile: path11.join(root, "diagnostics", "desktop-controller-v1.json"),
|
|
17441
|
+
logsDirectory: path11.join(root, "logs"),
|
|
17442
|
+
hostDescriptorDirectory: path11.join(root, "console", "hosts")
|
|
17029
17443
|
};
|
|
17030
17444
|
}
|
|
17031
17445
|
function consolePort(environment = process.env) {
|
|
@@ -17041,10 +17455,10 @@ function consolePort(environment = process.env) {
|
|
|
17041
17455
|
// packages/console-server/src/harnesses.ts
|
|
17042
17456
|
var HARNESS_PLUGIN_DIRECTORY_ENV = "CODEXHOST_PLUGIN_DIRECTORY";
|
|
17043
17457
|
function harnessPluginRoots(appDirectory2, sourceCheckout, environment = process.env) {
|
|
17044
|
-
const bundled = sourceCheckout ?
|
|
17458
|
+
const bundled = sourceCheckout ? path12.resolve(appDirectory2, "..", "..", "host-runtime", "dist", "plugins") : path12.join(appDirectory2, "plugins");
|
|
17045
17459
|
return [
|
|
17046
17460
|
bundled,
|
|
17047
|
-
environment[HARNESS_PLUGIN_DIRECTORY_ENV] ??
|
|
17461
|
+
environment[HARNESS_PLUGIN_DIRECTORY_ENV] ?? path12.join(dataDirectory2(environment), "plugins")
|
|
17048
17462
|
];
|
|
17049
17463
|
}
|
|
17050
17464
|
var ConsoleHarnessError = class extends Error {
|
|
@@ -17078,7 +17492,8 @@ function createConsoleHarnesses(roots, environment = process.env) {
|
|
|
17078
17492
|
launchCommand: manifest.launchCommand === true,
|
|
17079
17493
|
links: manifest.links ?? null,
|
|
17080
17494
|
icon,
|
|
17081
|
-
launchPath
|
|
17495
|
+
launchPath,
|
|
17496
|
+
...manifest.id === "grok" ? { computerUse: await getGrokComputerUseSettings(environment) } : {}
|
|
17082
17497
|
});
|
|
17083
17498
|
}
|
|
17084
17499
|
return harnesses.sort(
|
|
@@ -17099,17 +17514,27 @@ function createConsoleHarnesses(roots, environment = process.env) {
|
|
|
17099
17514
|
} catch (error51) {
|
|
17100
17515
|
throw new ConsoleHarnessError(400, error51 instanceof Error ? error51.message : String(error51));
|
|
17101
17516
|
}
|
|
17517
|
+
},
|
|
17518
|
+
async setGrokComputerUse(update) {
|
|
17519
|
+
const harness2 = (await list()).find((candidate) => candidate.id === "grok");
|
|
17520
|
+
if (!harness2) throw new ConsoleHarnessError(404, "Harness 'grok' is not installed");
|
|
17521
|
+
try {
|
|
17522
|
+
const computerUse = await setGrokComputerUse(update, environment);
|
|
17523
|
+
return { ...harness2, computerUse };
|
|
17524
|
+
} catch (error51) {
|
|
17525
|
+
throw new ConsoleHarnessError(400, error51 instanceof Error ? error51.message : String(error51));
|
|
17526
|
+
}
|
|
17102
17527
|
}
|
|
17103
17528
|
};
|
|
17104
17529
|
}
|
|
17105
17530
|
|
|
17106
17531
|
// packages/console-server/src/host-client.ts
|
|
17107
|
-
import { readdir as readdir4, readFile as
|
|
17108
|
-
import
|
|
17532
|
+
import { readdir as readdir4, readFile as readFile7, rm as rm6 } from "node:fs/promises";
|
|
17533
|
+
import path14 from "node:path";
|
|
17109
17534
|
|
|
17110
17535
|
// packages/console-server/src/diagnostics.ts
|
|
17111
|
-
import { open as open4, readdir as readdir3, readFile as
|
|
17112
|
-
import
|
|
17536
|
+
import { open as open4, readdir as readdir3, readFile as readFile6, stat as stat3 } from "node:fs/promises";
|
|
17537
|
+
import path13 from "node:path";
|
|
17113
17538
|
var MAX_DIAGNOSTIC_FILE_BYTES = 256 * 1024;
|
|
17114
17539
|
var MAX_LOG_TAIL_BYTES = 256 * 1024;
|
|
17115
17540
|
var LOG_FILE_PATTERN = /^host-runtime-\d{1,10}\.log(?:\.1)?$/u;
|
|
@@ -17117,7 +17542,7 @@ async function readBoundedJson(filePath) {
|
|
|
17117
17542
|
try {
|
|
17118
17543
|
const metadata = await stat3(filePath);
|
|
17119
17544
|
if (!metadata.isFile() || metadata.size > MAX_DIAGNOSTIC_FILE_BYTES) return null;
|
|
17120
|
-
return JSON.parse(await
|
|
17545
|
+
return JSON.parse(await readFile6(filePath, "utf8"));
|
|
17121
17546
|
} catch {
|
|
17122
17547
|
return null;
|
|
17123
17548
|
}
|
|
@@ -17164,6 +17589,8 @@ async function readControllerStatus(filePath) {
|
|
|
17164
17589
|
return {
|
|
17165
17590
|
pid: document.pid,
|
|
17166
17591
|
startedAt: document.startedAt,
|
|
17592
|
+
...typeof document.voiceInjection === "boolean" ? { voiceInjection: document.voiceInjection } : {},
|
|
17593
|
+
...document.voiceEnvOverride === "0" || document.voiceEnvOverride === "1" || document.voiceEnvOverride === null ? { voiceEnvOverride: document.voiceEnvOverride } : {},
|
|
17167
17594
|
renderer: {
|
|
17168
17595
|
state: renderer.state,
|
|
17169
17596
|
error: typeof renderer.error === "string" ? renderer.error : null,
|
|
@@ -17188,7 +17615,7 @@ async function listLogFiles(logsDirectory) {
|
|
|
17188
17615
|
const entries = await Promise.all(
|
|
17189
17616
|
names.filter(isLogFileName).map(async (name) => {
|
|
17190
17617
|
try {
|
|
17191
|
-
const metadata = await stat3(
|
|
17618
|
+
const metadata = await stat3(path13.join(logsDirectory, name));
|
|
17192
17619
|
return metadata.isFile() ? { name, size: metadata.size, modifiedAt: metadata.mtimeMs } : null;
|
|
17193
17620
|
} catch {
|
|
17194
17621
|
return null;
|
|
@@ -17202,7 +17629,7 @@ async function readLogTail(logsDirectory, name, maxBytes = MAX_LOG_TAIL_BYTES) {
|
|
|
17202
17629
|
const limit = Math.max(1, Math.min(maxBytes, MAX_LOG_TAIL_BYTES));
|
|
17203
17630
|
let handle;
|
|
17204
17631
|
try {
|
|
17205
|
-
handle = await open4(
|
|
17632
|
+
handle = await open4(path13.join(logsDirectory, name), "r");
|
|
17206
17633
|
} catch {
|
|
17207
17634
|
return null;
|
|
17208
17635
|
}
|
|
@@ -17283,14 +17710,14 @@ async function findHost(descriptorDirectory, isAlive = processIsAlive2) {
|
|
|
17283
17710
|
}
|
|
17284
17711
|
const hosts = [];
|
|
17285
17712
|
for (const name of names.filter((candidate) => DESCRIPTOR_PATTERN.test(candidate))) {
|
|
17286
|
-
const filePath =
|
|
17713
|
+
const filePath = path14.join(descriptorDirectory, name);
|
|
17287
17714
|
try {
|
|
17288
|
-
const text = await
|
|
17715
|
+
const text = await readFile7(filePath, "utf8");
|
|
17289
17716
|
if (text.length > DESCRIPTOR_MAX_BYTES) continue;
|
|
17290
17717
|
const endpoint = parseDescriptor(JSON.parse(text));
|
|
17291
17718
|
if (!endpoint) continue;
|
|
17292
17719
|
if (isAlive(endpoint.pid)) hosts.push(endpoint);
|
|
17293
|
-
else await
|
|
17720
|
+
else await rm6(filePath, { force: true });
|
|
17294
17721
|
} catch {
|
|
17295
17722
|
continue;
|
|
17296
17723
|
}
|
|
@@ -17311,7 +17738,7 @@ function createConsoleHostClient(descriptorDirectory, options = {}) {
|
|
|
17311
17738
|
headers: { authorization: `Bearer ${host.token}`, "content-type": "application/json" },
|
|
17312
17739
|
body: JSON.stringify({ method, params: params ?? {} }),
|
|
17313
17740
|
signal: AbortSignal.timeout(
|
|
17314
|
-
method === "codexhost/remote/ssh-setup" ? Math.max(timeoutMs, 33e4) : timeoutMs
|
|
17741
|
+
method === VOICE_GATEWAY_STATUS_METHOD ? Math.min(timeoutMs, 2e3) : method === "codexhost/remote/ssh-setup" ? Math.max(timeoutMs, 33e4) : timeoutMs
|
|
17315
17742
|
)
|
|
17316
17743
|
});
|
|
17317
17744
|
const body = await response.json().catch(() => null);
|
|
@@ -17328,12 +17755,12 @@ function createConsoleHostClient(descriptorDirectory, options = {}) {
|
|
|
17328
17755
|
// packages/console-server/src/installation.ts
|
|
17329
17756
|
import { execFile as execFile2 } from "node:child_process";
|
|
17330
17757
|
import { existsSync } from "node:fs";
|
|
17331
|
-
import { readFile as
|
|
17332
|
-
import
|
|
17758
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
17759
|
+
import path15 from "node:path";
|
|
17333
17760
|
async function readDistribution(appDirectory2) {
|
|
17334
17761
|
try {
|
|
17335
17762
|
return parseDistributionMetadata(
|
|
17336
|
-
JSON.parse(await
|
|
17763
|
+
JSON.parse(await readFile8(path15.join(appDirectory2, "codexhost-distribution.json"), "utf8"))
|
|
17337
17764
|
);
|
|
17338
17765
|
} catch (error51) {
|
|
17339
17766
|
if (error51.code === "ENOENT") return null;
|
|
@@ -17341,17 +17768,17 @@ async function readDistribution(appDirectory2) {
|
|
|
17341
17768
|
}
|
|
17342
17769
|
}
|
|
17343
17770
|
function installedLauncherCandidates(appDirectory2, platform = process.platform) {
|
|
17344
|
-
const resourcesRoot =
|
|
17771
|
+
const resourcesRoot = path15.dirname(appDirectory2);
|
|
17345
17772
|
const suffix = platform === "win32" ? ".exe" : "";
|
|
17346
|
-
const candidates = [
|
|
17347
|
-
if (platform === "darwin" &&
|
|
17348
|
-
candidates.unshift(
|
|
17773
|
+
const candidates = [path15.join(resourcesRoot, "bin", `codexhost${suffix}`)];
|
|
17774
|
+
if (platform === "darwin" && path15.basename(resourcesRoot) === "Resources") {
|
|
17775
|
+
candidates.unshift(path15.join(path15.dirname(resourcesRoot), "MacOS", "codexhost"));
|
|
17349
17776
|
}
|
|
17350
17777
|
return candidates;
|
|
17351
17778
|
}
|
|
17352
17779
|
function resolveLauncherExecutable(appDirectory2, environment = process.env, platform = process.platform, exists = existsSync) {
|
|
17353
17780
|
const configured = environment[LAUNCHER_EXECUTABLE_ENV];
|
|
17354
|
-
if (configured &&
|
|
17781
|
+
if (configured && path15.isAbsolute(configured) && exists(configured)) return configured;
|
|
17355
17782
|
return installedLauncherCandidates(appDirectory2, platform).find(exists) ?? null;
|
|
17356
17783
|
}
|
|
17357
17784
|
async function resolveInstallation(appDirectory2, environment = process.env) {
|
|
@@ -17389,26 +17816,26 @@ function inspectInstallation(launcherExecutable) {
|
|
|
17389
17816
|
);
|
|
17390
17817
|
});
|
|
17391
17818
|
}
|
|
17392
|
-
function launchCommand(installation, environment = process.env) {
|
|
17819
|
+
function launchCommand(installation, environment = process.env, action = "launch") {
|
|
17393
17820
|
if (installation.distribution?.distribution === "npm") {
|
|
17394
17821
|
const node = environment.CODEXHOST_NPM_NODE_PATH;
|
|
17395
17822
|
const wrapper = environment.CODEXHOST_NPM_LAUNCHER_PATH;
|
|
17396
|
-
if (node && wrapper &&
|
|
17397
|
-
return { command: node, args: [wrapper] };
|
|
17823
|
+
if (node && wrapper && path15.isAbsolute(node) && path15.isAbsolute(wrapper)) {
|
|
17824
|
+
return { command: node, args: [wrapper, ...action === "restart" ? [action] : []] };
|
|
17398
17825
|
}
|
|
17399
17826
|
return null;
|
|
17400
17827
|
}
|
|
17401
|
-
return installation.launcherExecutable ? { command: installation.launcherExecutable, args: [
|
|
17828
|
+
return installation.launcherExecutable ? { command: installation.launcherExecutable, args: [action] } : null;
|
|
17402
17829
|
}
|
|
17403
17830
|
|
|
17404
17831
|
// packages/console-server/src/open.ts
|
|
17405
17832
|
import { spawn as spawn3 } from "node:child_process";
|
|
17406
17833
|
import { stat as stat4 } from "node:fs/promises";
|
|
17407
|
-
import
|
|
17834
|
+
import path20 from "node:path";
|
|
17408
17835
|
|
|
17409
17836
|
// packages/console-server/src/page.ts
|
|
17410
|
-
import { readFile as
|
|
17411
|
-
import
|
|
17837
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
17838
|
+
import path16 from "node:path";
|
|
17412
17839
|
var CONSOLE_PAGE_HTML = `<!doctype html>
|
|
17413
17840
|
<html lang="en">
|
|
17414
17841
|
<head>
|
|
@@ -17429,14 +17856,14 @@ var MISSING_BUNDLE_SCRIPT = `document.body.textContent = "codexhost console asse
|
|
|
17429
17856
|
`;
|
|
17430
17857
|
function consoleBundleCandidates(appDirectory2) {
|
|
17431
17858
|
return [
|
|
17432
|
-
|
|
17433
|
-
|
|
17859
|
+
path16.join(appDirectory2, "console-web.js"),
|
|
17860
|
+
path16.resolve(appDirectory2, "..", "..", "renderer-extension", "dist", "console.js")
|
|
17434
17861
|
];
|
|
17435
17862
|
}
|
|
17436
17863
|
async function loadConsoleBundle(appDirectory2) {
|
|
17437
17864
|
for (const candidate of consoleBundleCandidates(appDirectory2)) {
|
|
17438
17865
|
try {
|
|
17439
|
-
return await
|
|
17866
|
+
return await readFile9(candidate, "utf8");
|
|
17440
17867
|
} catch {
|
|
17441
17868
|
continue;
|
|
17442
17869
|
}
|
|
@@ -17454,6 +17881,49 @@ function allowedChange(origin, consoleHeader, port) {
|
|
|
17454
17881
|
return origin === void 0 || origin === `http://127.0.0.1:${port}` || origin === `http://localhost:${port}`;
|
|
17455
17882
|
}
|
|
17456
17883
|
|
|
17884
|
+
// packages/console-server/src/restart.ts
|
|
17885
|
+
var ConsoleRestart = class {
|
|
17886
|
+
constructor(run2, observe, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), now = Date.now, timeoutMs = 12e4) {
|
|
17887
|
+
this.run = run2;
|
|
17888
|
+
this.observe = observe;
|
|
17889
|
+
this.sleep = sleep;
|
|
17890
|
+
this.now = now;
|
|
17891
|
+
this.timeoutMs = timeoutMs;
|
|
17892
|
+
}
|
|
17893
|
+
run;
|
|
17894
|
+
observe;
|
|
17895
|
+
sleep;
|
|
17896
|
+
now;
|
|
17897
|
+
timeoutMs;
|
|
17898
|
+
status = { phase: "idle", error: null };
|
|
17899
|
+
start(previous) {
|
|
17900
|
+
if (this.status.phase === "restarting") throw new Error("Restart is already in progress");
|
|
17901
|
+
this.status = { phase: "restarting", error: null };
|
|
17902
|
+
void this.restart(previous);
|
|
17903
|
+
}
|
|
17904
|
+
async restart(previous) {
|
|
17905
|
+
try {
|
|
17906
|
+
await this.run();
|
|
17907
|
+
const deadline = this.now() + this.timeoutMs;
|
|
17908
|
+
while (this.now() < deadline) {
|
|
17909
|
+
const current = await this.observe();
|
|
17910
|
+
if (current.failure) throw new Error(current.failure);
|
|
17911
|
+
if (current.launcherPid !== null && current.launcherPid !== previous.launcherPid && current.controllerPid !== null && current.controllerPid !== previous.controllerPid && current.installed) {
|
|
17912
|
+
this.status = { phase: "succeeded", error: null };
|
|
17913
|
+
return;
|
|
17914
|
+
}
|
|
17915
|
+
await this.sleep(250);
|
|
17916
|
+
}
|
|
17917
|
+
throw new Error("Restart timed out. Check startup diagnostics before retrying.");
|
|
17918
|
+
} catch (error51) {
|
|
17919
|
+
this.status = {
|
|
17920
|
+
phase: "failed",
|
|
17921
|
+
error: error51 instanceof Error ? error51.message : String(error51)
|
|
17922
|
+
};
|
|
17923
|
+
}
|
|
17924
|
+
}
|
|
17925
|
+
};
|
|
17926
|
+
|
|
17457
17927
|
// packages/console-server/src/server.ts
|
|
17458
17928
|
import { spawn as spawn2 } from "node:child_process";
|
|
17459
17929
|
import { createServer } from "node:http";
|
|
@@ -17517,14 +17987,14 @@ function readAnnouncement(fetcher = fetch) {
|
|
|
17517
17987
|
}
|
|
17518
17988
|
|
|
17519
17989
|
// packages/console-server/src/diagnostic-report.ts
|
|
17520
|
-
import
|
|
17990
|
+
import os4 from "node:os";
|
|
17521
17991
|
var ISSUE_REPOSITORY_URL = "https://github.com/karlorz/codex-host";
|
|
17522
17992
|
var ISSUE_ERROR_CHARS = 800;
|
|
17523
17993
|
var ISSUE_URL_MAX_LENGTH = 7e3;
|
|
17524
17994
|
var REPORT_LOG_TAIL_BYTES = 32 * 1024;
|
|
17525
17995
|
var REPORT_LOG_FILES = 2;
|
|
17526
17996
|
var REPORT_STARTUP_RECORDS = 5;
|
|
17527
|
-
function redactHome(text, home =
|
|
17997
|
+
function redactHome(text, home = os4.homedir()) {
|
|
17528
17998
|
if (!home || home === "/" || home.length < 3) return text;
|
|
17529
17999
|
const variants = /* @__PURE__ */ new Set([home, JSON.stringify(home).slice(1, -1)]);
|
|
17530
18000
|
let result = text;
|
|
@@ -17540,7 +18010,7 @@ async function buildDiagnosticReport(input, readTail, now = () => /* @__PURE__ *
|
|
|
17540
18010
|
return {
|
|
17541
18011
|
schemaVersion: 1,
|
|
17542
18012
|
generatedAt: now().toISOString(),
|
|
17543
|
-
platform: { os: process.platform, arch: process.arch, release:
|
|
18013
|
+
platform: { os: process.platform, arch: process.arch, release: os4.release() },
|
|
17544
18014
|
codexhost: {
|
|
17545
18015
|
version: input.distribution?.version ?? input.consoleVersion,
|
|
17546
18016
|
distribution: input.distribution?.distribution ?? null,
|
|
@@ -17616,7 +18086,7 @@ function issueUrl(report, home) {
|
|
|
17616
18086
|
}
|
|
17617
18087
|
|
|
17618
18088
|
// packages/console-server/src/updates.ts
|
|
17619
|
-
import
|
|
18089
|
+
import path17 from "node:path";
|
|
17620
18090
|
var ERROR_MAX_LENGTH = 500;
|
|
17621
18091
|
var ConsoleUpdateError = class extends Error {
|
|
17622
18092
|
constructor(code, message) {
|
|
@@ -17649,13 +18119,13 @@ function installationKind(metadata) {
|
|
|
17649
18119
|
}
|
|
17650
18120
|
function absoluteEnvironmentPath(environment, name) {
|
|
17651
18121
|
const value = environment[name];
|
|
17652
|
-
if (!value || !
|
|
18122
|
+
if (!value || !path17.isAbsolute(value)) {
|
|
17653
18123
|
throw new ConsoleUpdateError(
|
|
17654
18124
|
"unsupported",
|
|
17655
18125
|
`${name} is unavailable; run 'codexhost console' from the npm command to update`
|
|
17656
18126
|
);
|
|
17657
18127
|
}
|
|
17658
|
-
return
|
|
18128
|
+
return path17.normalize(value);
|
|
17659
18129
|
}
|
|
17660
18130
|
function createConsoleUpdates(options) {
|
|
17661
18131
|
const environment = options.environment ?? process.env;
|
|
@@ -17696,13 +18166,13 @@ function createConsoleUpdates(options) {
|
|
|
17696
18166
|
}
|
|
17697
18167
|
}
|
|
17698
18168
|
async function prepare(metadata, target, release, onPrepared) {
|
|
17699
|
-
const resourcesRoot =
|
|
18169
|
+
const resourcesRoot = path17.dirname(target.appDirectory);
|
|
17700
18170
|
const common = {
|
|
17701
18171
|
version: release.version,
|
|
17702
18172
|
launcherPid: options.processId ?? process.pid,
|
|
17703
18173
|
launcherExecutable: options.processExecutable ?? process.execPath,
|
|
17704
18174
|
runtimeDescriptorPath: target.runtimeDescriptorPath,
|
|
17705
|
-
updaterExecutable:
|
|
18175
|
+
updaterExecutable: path17.join(
|
|
17706
18176
|
resourcesRoot,
|
|
17707
18177
|
"libexec",
|
|
17708
18178
|
platform === "win32" ? "codexhost-updater.exe" : "codexhost-updater"
|
|
@@ -17737,13 +18207,13 @@ function createConsoleUpdates(options) {
|
|
|
17737
18207
|
if (kind === "windows-installer") {
|
|
17738
18208
|
return manager.prepareWindowsInstaller({ ...common, artifact, installRoot: resourcesRoot });
|
|
17739
18209
|
}
|
|
17740
|
-
const appPath =
|
|
18210
|
+
const appPath = path17.dirname(path17.dirname(resourcesRoot));
|
|
17741
18211
|
return manager.prepareMacOsDmg({ ...common, artifact, appPath });
|
|
17742
18212
|
}
|
|
17743
18213
|
return Object.freeze({
|
|
17744
18214
|
async check(target, signal) {
|
|
17745
18215
|
const metadata = target.distribution;
|
|
17746
|
-
const runtime = metadata ? null : await readRuntimeMetadata(
|
|
18216
|
+
const runtime = metadata ? null : await readRuntimeMetadata(path17.join(target.appDirectory, "main.js"), environment).catch(
|
|
17747
18217
|
() => null
|
|
17748
18218
|
);
|
|
17749
18219
|
const empty = {
|
|
@@ -17794,7 +18264,7 @@ function createConsoleUpdates(options) {
|
|
|
17794
18264
|
if (!metadata) {
|
|
17795
18265
|
throw new ConsoleUpdateError("unsupported", "A source checkout cannot update itself");
|
|
17796
18266
|
}
|
|
17797
|
-
if (!target.runtimeDescriptorPath || !
|
|
18267
|
+
if (!target.runtimeDescriptorPath || !path17.isAbsolute(target.runtimeDescriptorPath)) {
|
|
17798
18268
|
throw new ConsoleUpdateError("unsupported", "The Launcher runtime path is unavailable");
|
|
17799
18269
|
}
|
|
17800
18270
|
if (handedOff) {
|
|
@@ -17862,6 +18332,135 @@ function createConsoleUpdates(options) {
|
|
|
17862
18332
|
});
|
|
17863
18333
|
}
|
|
17864
18334
|
|
|
18335
|
+
// packages/console-server/src/live-voice-inject.ts
|
|
18336
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
18337
|
+
import { mkdir as mkdir6, readFile as readFile10, rename as rename4, rm as rm7, writeFile as writeFile5 } from "node:fs/promises";
|
|
18338
|
+
import path18 from "node:path";
|
|
18339
|
+
var CPA_LIVE_VOICE_ENV = "CODEXHOST_CPA_LIVE_VOICE";
|
|
18340
|
+
var LIVE_VOICE_INJECT_FILE = "live-voice-inject-v1.json";
|
|
18341
|
+
function injectPath(dataDirectory3) {
|
|
18342
|
+
return path18.join(dataDirectory3, LIVE_VOICE_INJECT_FILE);
|
|
18343
|
+
}
|
|
18344
|
+
async function readLiveVoiceInjectState(dataDirectory3, environment = process.env) {
|
|
18345
|
+
const env = environment[CPA_LIVE_VOICE_ENV];
|
|
18346
|
+
const envOverride = env === "0" || env === "1" ? env : null;
|
|
18347
|
+
let inject = true;
|
|
18348
|
+
try {
|
|
18349
|
+
const parsed = JSON.parse(await readFile10(injectPath(dataDirectory3), "utf8"));
|
|
18350
|
+
inject = parsed.version !== 1 || parsed.inject !== false;
|
|
18351
|
+
} catch {
|
|
18352
|
+
inject = true;
|
|
18353
|
+
}
|
|
18354
|
+
const savedInject = inject;
|
|
18355
|
+
if (envOverride === "0") inject = false;
|
|
18356
|
+
if (envOverride === "1") inject = true;
|
|
18357
|
+
return { inject, savedInject, envOverride };
|
|
18358
|
+
}
|
|
18359
|
+
async function writeLiveVoiceInjectFile(dataDirectory3, inject, routes) {
|
|
18360
|
+
if (!routes) {
|
|
18361
|
+
try {
|
|
18362
|
+
const saved = JSON.parse(await readFile10(injectPath(dataDirectory3), "utf8"));
|
|
18363
|
+
if (saved?.version === 1) routes = voiceRoutesSchema.safeParse(saved.routes).data;
|
|
18364
|
+
} catch {
|
|
18365
|
+
}
|
|
18366
|
+
}
|
|
18367
|
+
await mkdir6(dataDirectory3, { recursive: true, mode: 448 });
|
|
18368
|
+
const temporaryPath = path18.join(dataDirectory3, `.${LIVE_VOICE_INJECT_FILE}.${randomUUID4()}.tmp`);
|
|
18369
|
+
try {
|
|
18370
|
+
await writeFile5(
|
|
18371
|
+
temporaryPath,
|
|
18372
|
+
`${JSON.stringify({ version: 1, inject, ...routes ? { routes } : {} }, null, 2)}
|
|
18373
|
+
`,
|
|
18374
|
+
{
|
|
18375
|
+
mode: 384,
|
|
18376
|
+
flag: "wx"
|
|
18377
|
+
}
|
|
18378
|
+
);
|
|
18379
|
+
await rename4(temporaryPath, injectPath(dataDirectory3));
|
|
18380
|
+
} finally {
|
|
18381
|
+
await rm7(temporaryPath, { force: true });
|
|
18382
|
+
}
|
|
18383
|
+
}
|
|
18384
|
+
|
|
18385
|
+
// packages/console-server/src/voice-settings.ts
|
|
18386
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
18387
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
18388
|
+
import path19 from "node:path";
|
|
18389
|
+
var VoiceSettingsConflict = class extends Error {
|
|
18390
|
+
};
|
|
18391
|
+
var VoiceSettingsStore = class {
|
|
18392
|
+
constructor(directory) {
|
|
18393
|
+
this.directory = directory;
|
|
18394
|
+
}
|
|
18395
|
+
directory;
|
|
18396
|
+
#writes = Promise.resolve();
|
|
18397
|
+
#runtime = /* @__PURE__ */ new Map();
|
|
18398
|
+
async read() {
|
|
18399
|
+
let document = null;
|
|
18400
|
+
try {
|
|
18401
|
+
document = JSON.parse(
|
|
18402
|
+
await readFile11(path19.join(this.directory, LIVE_VOICE_INJECT_FILE), "utf8")
|
|
18403
|
+
);
|
|
18404
|
+
} catch (error51) {
|
|
18405
|
+
if (!(error51 instanceof SyntaxError) && error51.code !== "ENOENT")
|
|
18406
|
+
throw new Error("Could not read voice settings");
|
|
18407
|
+
}
|
|
18408
|
+
const routes = document?.version === 1 ? voiceRoutesSchema.safeParse(document.routes).data : void 0;
|
|
18409
|
+
const settings = {
|
|
18410
|
+
...routes ?? DEFAULT_VOICE_ROUTES,
|
|
18411
|
+
inject: document?.version !== 1 || document.inject !== false
|
|
18412
|
+
};
|
|
18413
|
+
const revision = createHash2("sha256").update(JSON.stringify({ settings, initialized: !!routes })).digest("hex");
|
|
18414
|
+
const runtime = [...this.#runtime.values()].filter((entry) => Date.now() - entry.at < 12e3);
|
|
18415
|
+
return {
|
|
18416
|
+
settings,
|
|
18417
|
+
initialized: !!routes,
|
|
18418
|
+
revision,
|
|
18419
|
+
activeCall: runtime.length ? runtime.some((entry) => entry.activeCall) : null,
|
|
18420
|
+
routesActive: runtime.length ? runtime.every((entry) => entry.revision === revision) : null
|
|
18421
|
+
};
|
|
18422
|
+
}
|
|
18423
|
+
report(runtime) {
|
|
18424
|
+
for (const [id, entry] of this.#runtime)
|
|
18425
|
+
if (Date.now() - entry.at >= 12e3) this.#runtime.delete(id);
|
|
18426
|
+
if (this.#runtime.size < 32 || this.#runtime.has(runtime.id))
|
|
18427
|
+
this.#runtime.set(runtime.id, { ...runtime, at: Date.now() });
|
|
18428
|
+
}
|
|
18429
|
+
async setInjection(inject) {
|
|
18430
|
+
const operation = this.#writes.then(async () => {
|
|
18431
|
+
const current = await this.read();
|
|
18432
|
+
if (current.activeCall)
|
|
18433
|
+
throw new VoiceSettingsConflict("End your voice call before applying changes.");
|
|
18434
|
+
await writeLiveVoiceInjectFile(this.directory, inject);
|
|
18435
|
+
});
|
|
18436
|
+
this.#writes = operation.catch(() => void 0);
|
|
18437
|
+
await operation;
|
|
18438
|
+
}
|
|
18439
|
+
async write(input) {
|
|
18440
|
+
const operation = this.#writes.then(async () => {
|
|
18441
|
+
const current = await this.read();
|
|
18442
|
+
if (input.operation === "migrate" && current.initialized) return current;
|
|
18443
|
+
if (input.operation === "set") {
|
|
18444
|
+
if (current.activeCall)
|
|
18445
|
+
throw new VoiceSettingsConflict("End your voice call before applying changes.");
|
|
18446
|
+
if (input.revision !== current.revision)
|
|
18447
|
+
throw new VoiceSettingsConflict(
|
|
18448
|
+
"Voice settings changed in another window. Reload and try again."
|
|
18449
|
+
);
|
|
18450
|
+
}
|
|
18451
|
+
const settings = input.operation === "set" ? input.settings : { ...current.settings, ...input.routes };
|
|
18452
|
+
await writeLiveVoiceInjectFile(this.directory, settings.inject, {
|
|
18453
|
+
chatExperience: settings.chatExperience,
|
|
18454
|
+
chatConnection: settings.chatConnection,
|
|
18455
|
+
workConnection: settings.workConnection
|
|
18456
|
+
});
|
|
18457
|
+
return this.read();
|
|
18458
|
+
});
|
|
18459
|
+
this.#writes = operation.catch(() => void 0);
|
|
18460
|
+
return operation;
|
|
18461
|
+
}
|
|
18462
|
+
};
|
|
18463
|
+
|
|
17865
18464
|
// packages/console-server/src/server.ts
|
|
17866
18465
|
var CONSOLE_SERVICE = "codexhost-console";
|
|
17867
18466
|
var MAX_BODY_BYTES = 16 * 1024;
|
|
@@ -17917,11 +18516,42 @@ function defaultLaunch(command2, args) {
|
|
|
17917
18516
|
});
|
|
17918
18517
|
child.unref();
|
|
17919
18518
|
}
|
|
18519
|
+
function defaultRestart(command2, args) {
|
|
18520
|
+
return new Promise((resolve, reject) => {
|
|
18521
|
+
const child = spawn2(command2, args, { detached: true, stdio: "ignore", windowsHide: true });
|
|
18522
|
+
child.once("error", () => reject(new Error("Could not start the installed restart command")));
|
|
18523
|
+
child.once("spawn", () => {
|
|
18524
|
+
child.unref();
|
|
18525
|
+
resolve();
|
|
18526
|
+
});
|
|
18527
|
+
});
|
|
18528
|
+
}
|
|
17920
18529
|
function startConsoleServer(options) {
|
|
17921
18530
|
const environment = options.environment ?? process.env;
|
|
17922
18531
|
const inspect = options.inspect ?? inspectInstallation;
|
|
17923
18532
|
const launch = options.launch ?? defaultLaunch;
|
|
17924
18533
|
const loadAnnouncement = options.announcement ?? readAnnouncement;
|
|
18534
|
+
const voiceSettings = new VoiceSettingsStore(options.paths.dataDirectory);
|
|
18535
|
+
let restartRequestedAt = 0;
|
|
18536
|
+
const restart = new ConsoleRestart(
|
|
18537
|
+
async () => {
|
|
18538
|
+
restartRequestedAt = Date.now();
|
|
18539
|
+
const command2 = launchCommand(options.installation, environment, "restart");
|
|
18540
|
+
if (!command2) throw new Error("This installation cannot be restarted from the console");
|
|
18541
|
+
await (options.restart ?? defaultRestart)(command2.command, command2.args);
|
|
18542
|
+
},
|
|
18543
|
+
async () => {
|
|
18544
|
+
inspectCache = null;
|
|
18545
|
+
const collected = await collect();
|
|
18546
|
+
const latest = collected.startup[0];
|
|
18547
|
+
return {
|
|
18548
|
+
launcherPid: collected.inspectDocument?.runtime.launcherPid ?? null,
|
|
18549
|
+
controllerPid: collected.controllerAlive ? collected.controller?.pid ?? null : null,
|
|
18550
|
+
installed: collected.controllerAlive && collected.controller?.renderer.state === "installed",
|
|
18551
|
+
failure: latest?.outcome === "failed" && latest.startedAtMs >= restartRequestedAt ? latest.error ?? "Desktop startup failed" : null
|
|
18552
|
+
};
|
|
18553
|
+
}
|
|
18554
|
+
);
|
|
17925
18555
|
let port = options.port;
|
|
17926
18556
|
let inspectCache = null;
|
|
17927
18557
|
let idleTimer;
|
|
@@ -18003,7 +18633,13 @@ function startConsoleServer(options) {
|
|
|
18003
18633
|
launchAvailable: launchCommand(options.installation, environment) !== null,
|
|
18004
18634
|
summary,
|
|
18005
18635
|
issueUrl: reportIssueUrl,
|
|
18006
|
-
hostAvailable: await options.host.available()
|
|
18636
|
+
hostAvailable: await options.host.available(),
|
|
18637
|
+
liveVoice: {
|
|
18638
|
+
...await readLiveVoiceInjectState(options.paths.dataDirectory, environment),
|
|
18639
|
+
activeInject: collected.controllerAlive && typeof controller?.voiceInjection === "boolean" ? controller.voiceInjection : null,
|
|
18640
|
+
activeEnvOverride: collected.controllerAlive ? controller?.voiceEnvOverride ?? null : null
|
|
18641
|
+
},
|
|
18642
|
+
restart: restart.status
|
|
18007
18643
|
};
|
|
18008
18644
|
}
|
|
18009
18645
|
async function diagnosticReport(collected, withLogs = true) {
|
|
@@ -18036,7 +18672,7 @@ function startConsoleServer(options) {
|
|
|
18036
18672
|
scheduleIdleExit();
|
|
18037
18673
|
const url2 = new URL(request.url ?? "/", `http://127.0.0.1:${port}`);
|
|
18038
18674
|
const method = request.method ?? "GET";
|
|
18039
|
-
|
|
18675
|
+
let route = `${method} ${url2.pathname}`;
|
|
18040
18676
|
if (route === "GET /api/health") {
|
|
18041
18677
|
sendJson(response, 200, {
|
|
18042
18678
|
service: CONSOLE_SERVICE,
|
|
@@ -18077,6 +18713,65 @@ function startConsoleServer(options) {
|
|
|
18077
18713
|
return;
|
|
18078
18714
|
}
|
|
18079
18715
|
}
|
|
18716
|
+
if (route === "POST /api/voice-settings") {
|
|
18717
|
+
const parsed = voiceSettingsRequestSchema.safeParse(body);
|
|
18718
|
+
if (!parsed.success) {
|
|
18719
|
+
sendJson(response, 400, { error: "Invalid voice settings request" });
|
|
18720
|
+
return;
|
|
18721
|
+
}
|
|
18722
|
+
const input = parsed.data;
|
|
18723
|
+
if (input.operation === "restart") {
|
|
18724
|
+
if ((await voiceSettings.read()).activeCall) {
|
|
18725
|
+
sendJson(response, 409, { error: "End your voice call before restarting." });
|
|
18726
|
+
return;
|
|
18727
|
+
}
|
|
18728
|
+
route = "POST /api/restart";
|
|
18729
|
+
} else {
|
|
18730
|
+
try {
|
|
18731
|
+
if (input.operation === "get" && input.runtime) voiceSettings.report(input.runtime);
|
|
18732
|
+
if (input.operation === "set") {
|
|
18733
|
+
const current = await readLiveVoiceInjectState(
|
|
18734
|
+
options.paths.dataDirectory,
|
|
18735
|
+
environment
|
|
18736
|
+
);
|
|
18737
|
+
if (current.envOverride && input.settings.inject !== current.savedInject) {
|
|
18738
|
+
sendJson(response, 409, {
|
|
18739
|
+
error: "Voice integration is controlled by this launch environment."
|
|
18740
|
+
});
|
|
18741
|
+
return;
|
|
18742
|
+
}
|
|
18743
|
+
}
|
|
18744
|
+
const saved = input.operation === "get" ? await voiceSettings.read() : await voiceSettings.write(input);
|
|
18745
|
+
const collected = await collect();
|
|
18746
|
+
const controller = collected.controller;
|
|
18747
|
+
const injection = await readLiveVoiceInjectState(
|
|
18748
|
+
options.paths.dataDirectory,
|
|
18749
|
+
environment
|
|
18750
|
+
);
|
|
18751
|
+
let gatewayReady = null;
|
|
18752
|
+
try {
|
|
18753
|
+
const reply = input.operation === "get" && input.runtime ? null : await options.host.request(VOICE_GATEWAY_STATUS_METHOD, {});
|
|
18754
|
+
if (reply && "result" in reply && typeof reply.result === "boolean")
|
|
18755
|
+
gatewayReady = reply.result;
|
|
18756
|
+
} catch {
|
|
18757
|
+
}
|
|
18758
|
+
sendJson(response, 200, {
|
|
18759
|
+
...saved,
|
|
18760
|
+
activeInject: collected.controllerAlive && typeof controller?.voiceInjection === "boolean" ? controller.voiceInjection : null,
|
|
18761
|
+
envOverride: injection.envOverride,
|
|
18762
|
+
activeEnvOverride: collected.controllerAlive ? controller?.voiceEnvOverride ?? null : null,
|
|
18763
|
+
gatewayReady,
|
|
18764
|
+
restartAvailable: !!collected.inspectDocument?.runtime.running && !!launchCommand(options.installation, environment, "restart"),
|
|
18765
|
+
restart: restart.status
|
|
18766
|
+
});
|
|
18767
|
+
} catch (error51) {
|
|
18768
|
+
sendJson(response, error51 instanceof VoiceSettingsConflict ? 409 : 500, {
|
|
18769
|
+
error: error51 instanceof VoiceSettingsConflict ? error51.message : "Could not save or read voice settings"
|
|
18770
|
+
});
|
|
18771
|
+
}
|
|
18772
|
+
return;
|
|
18773
|
+
}
|
|
18774
|
+
}
|
|
18080
18775
|
if (route === "POST /api/shutdown") {
|
|
18081
18776
|
sendJson(response, 200, { ok: true });
|
|
18082
18777
|
setImmediate(exit);
|
|
@@ -18151,6 +18846,22 @@ function startConsoleServer(options) {
|
|
|
18151
18846
|
sendJson(response, 200, { harnesses: await options.harnesses.list() });
|
|
18152
18847
|
return;
|
|
18153
18848
|
}
|
|
18849
|
+
if (route === "POST /api/harnesses/grok-computer-use") {
|
|
18850
|
+
const parsed = grokComputerUseSetSchema.safeParse(body);
|
|
18851
|
+
if (!parsed.success) {
|
|
18852
|
+
sendJson(response, 400, { error: "Expected { enabled } or { browser }" });
|
|
18853
|
+
return;
|
|
18854
|
+
}
|
|
18855
|
+
try {
|
|
18856
|
+
const harness2 = await options.harnesses.setGrokComputerUse(parsed.data);
|
|
18857
|
+
sendJson(response, 200, { harness: harness2 });
|
|
18858
|
+
} catch (error51) {
|
|
18859
|
+
sendJson(response, error51 instanceof ConsoleHarnessError ? error51.status : 500, {
|
|
18860
|
+
error: error51 instanceof Error ? error51.message : String(error51)
|
|
18861
|
+
});
|
|
18862
|
+
}
|
|
18863
|
+
return;
|
|
18864
|
+
}
|
|
18154
18865
|
if (route === "POST /api/harnesses/launch-path") {
|
|
18155
18866
|
const id = body.id;
|
|
18156
18867
|
const value = body.path;
|
|
@@ -18168,6 +18879,59 @@ function startConsoleServer(options) {
|
|
|
18168
18879
|
}
|
|
18169
18880
|
return;
|
|
18170
18881
|
}
|
|
18882
|
+
if (route === "GET /api/live-voice-inject") {
|
|
18883
|
+
sendJson(
|
|
18884
|
+
response,
|
|
18885
|
+
200,
|
|
18886
|
+
await readLiveVoiceInjectState(options.paths.dataDirectory, environment)
|
|
18887
|
+
);
|
|
18888
|
+
return;
|
|
18889
|
+
}
|
|
18890
|
+
if (route === "POST /api/live-voice-inject") {
|
|
18891
|
+
if (typeof body.inject !== "boolean") {
|
|
18892
|
+
sendJson(response, 400, { error: "Expected { inject: boolean }" });
|
|
18893
|
+
return;
|
|
18894
|
+
}
|
|
18895
|
+
try {
|
|
18896
|
+
await voiceSettings.setInjection(body.inject);
|
|
18897
|
+
} catch (error51) {
|
|
18898
|
+
sendJson(response, error51 instanceof VoiceSettingsConflict ? 409 : 500, {
|
|
18899
|
+
error: error51 instanceof VoiceSettingsConflict ? error51.message : "Could not save voice integration"
|
|
18900
|
+
});
|
|
18901
|
+
return;
|
|
18902
|
+
}
|
|
18903
|
+
sendJson(
|
|
18904
|
+
response,
|
|
18905
|
+
200,
|
|
18906
|
+
await readLiveVoiceInjectState(options.paths.dataDirectory, environment)
|
|
18907
|
+
);
|
|
18908
|
+
return;
|
|
18909
|
+
}
|
|
18910
|
+
if (route === "POST /api/restart") {
|
|
18911
|
+
if (body.confirm !== true) {
|
|
18912
|
+
sendJson(response, 400, {
|
|
18913
|
+
error: "Confirm that restart may interrupt voice and running tasks"
|
|
18914
|
+
});
|
|
18915
|
+
return;
|
|
18916
|
+
}
|
|
18917
|
+
const current = await collect();
|
|
18918
|
+
if (!current.inspectDocument?.runtime.running || !launchCommand(options.installation, environment, "restart")) {
|
|
18919
|
+
sendJson(response, 409, { error: "No restartable managed Desktop is running" });
|
|
18920
|
+
return;
|
|
18921
|
+
}
|
|
18922
|
+
if (restart.status.phase === "restarting") {
|
|
18923
|
+
sendJson(response, 409, { error: "Restart is already in progress" });
|
|
18924
|
+
return;
|
|
18925
|
+
}
|
|
18926
|
+
restart.start({
|
|
18927
|
+
launcherPid: current.inspectDocument.runtime.launcherPid,
|
|
18928
|
+
controllerPid: current.controller?.pid ?? null,
|
|
18929
|
+
installed: current.controller?.renderer.state === "installed",
|
|
18930
|
+
failure: null
|
|
18931
|
+
});
|
|
18932
|
+
sendJson(response, 202, restart.status);
|
|
18933
|
+
return;
|
|
18934
|
+
}
|
|
18171
18935
|
if (route === "POST /api/launch") {
|
|
18172
18936
|
const command2 = launchCommand(options.installation, environment);
|
|
18173
18937
|
if (!command2) {
|
|
@@ -18259,7 +19023,7 @@ async function requestShutdown(port) {
|
|
|
18259
19023
|
if (!response.ok) throw new Error(`console shutdown failed: HTTP ${response.status}`);
|
|
18260
19024
|
}
|
|
18261
19025
|
async function consoleBuildId(entryPath2, environment = process.env) {
|
|
18262
|
-
const files = [entryPath2, ...consoleBundleCandidates(
|
|
19026
|
+
const files = [entryPath2, ...consoleBundleCandidates(path20.dirname(entryPath2))];
|
|
18263
19027
|
const parts = [];
|
|
18264
19028
|
for (const file2 of files) {
|
|
18265
19029
|
const metadata = await stat4(file2).catch(() => null);
|
|
@@ -18267,7 +19031,7 @@ async function consoleBuildId(entryPath2, environment = process.env) {
|
|
|
18267
19031
|
}
|
|
18268
19032
|
const runtime = await readRuntimeMetadata(entryPath2, environment).catch(() => null);
|
|
18269
19033
|
if (runtime?.distribution === "development") parts.push(runtime.version);
|
|
18270
|
-
return JSON.stringify([parts.join("."),
|
|
19034
|
+
return JSON.stringify([parts.join("."), dataDirectory2(environment)]);
|
|
18271
19035
|
}
|
|
18272
19036
|
function startDetachedServer(entryPath2, environment) {
|
|
18273
19037
|
const child = spawn3(process.execPath, [entryPath2, "serve"], {
|
|
@@ -18362,7 +19126,7 @@ async function updateFromCommand(appDirectory2, arguments_) {
|
|
|
18362
19126
|
var IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
18363
19127
|
var HANDOFF_EXIT_DELAY_MS = 500;
|
|
18364
19128
|
var entryPath = fileURLToPath(import.meta.url);
|
|
18365
|
-
var appDirectory =
|
|
19129
|
+
var appDirectory = path21.dirname(entryPath);
|
|
18366
19130
|
function usage() {
|
|
18367
19131
|
console.error(
|
|
18368
19132
|
"usage: console-server open [--no-browser] | console-server ensure | console-server serve | console-server update"
|