@karlorz/codexhost-cli-win32-arm64 0.13.3-5 → 0.13.3-6
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 +402 -31
- package/app/console-web.js +776 -186
- package/app/cpa-live-voice-hook.js +332 -149
- package/app/desktop-controller.mjs +113 -34
- package/app/host-runtime.mjs +248 -95
- package/app/plugins/antigravity/plugin.mjs +47 -0
- package/app/plugins/claude-code/plugin.mjs +98 -7
- package/app/plugins/codebuddy/plugin.mjs +47 -0
- package/app/plugins/codex-usage/plugin.mjs +47 -0
- package/app/plugins/cursor-cli/plugin.mjs +47 -0
- package/app/plugins/deepseek-harness/plugin.mjs +47 -0
- package/app/plugins/grok/plugin.mjs +86 -2
- package/app/plugins/hermes/plugin.mjs +47 -0
- package/app/plugins/kimi-code/plugin.mjs +47 -0
- package/app/plugins/kiro-cli/plugin.mjs +47 -0
- package/app/plugins/omp/plugin.mjs +47 -0
- package/app/plugins/opencode/plugin.mjs +47 -0
- package/app/plugins/pi/plugin.mjs +47 -0
- package/app/plugins/qoder/plugin.mjs +47 -0
- package/app/plugins/qoder-cn/plugin.mjs +47 -0
- package/app/plugins/workbuddy/plugin.mjs +47 -0
- package/app/plugins/zcode/plugin.mjs +47 -0
- package/app/renderer-extension.js +1918 -741
- 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/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Run Pi and Claude Code as first-class external harnesses inside Codex Desktop.
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install -g @karlorz/codexhost-cli@0.13.3-
|
|
10
|
+
npm install -g @karlorz/codexhost-cli@0.13.3-6
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Do not install this package directly. npm selects it through the optional dependencies of `@karlorz/codexhost-cli`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"schemaVersion":1,"version":"0.13.3-
|
|
1
|
+
{"schemaVersion":1,"version":"0.13.3-6","distribution":"npm","target":"windows-arm64"}
|
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 path19 from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
|
|
11
11
|
// packages/update-manager/dist/runtime-metadata.js
|
|
@@ -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, path20) {
|
|
1667
|
+
if (!path20)
|
|
1668
1668
|
return obj;
|
|
1669
|
-
return
|
|
1669
|
+
return path20.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(path20, 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(path20);
|
|
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, path20 = []) => {
|
|
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 }, [...path20, ...issue2.path]));
|
|
2233
2233
|
} else if (issue2.code === "invalid_key") {
|
|
2234
|
-
processError({ issues: issue2.issues }, [...
|
|
2234
|
+
processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
|
|
2235
2235
|
} else if (issue2.code === "invalid_element") {
|
|
2236
|
-
processError({ issues: issue2.issues }, [...
|
|
2236
|
+
processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
|
|
2237
2237
|
} else {
|
|
2238
|
-
const fullpath = [...
|
|
2238
|
+
const fullpath = [...path20, ...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, path20 = []) => {
|
|
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 }, [...path20, ...issue2.path]));
|
|
2270
2270
|
} else if (issue2.code === "invalid_key") {
|
|
2271
|
-
processError({ issues: issue2.issues }, [...
|
|
2271
|
+
processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
|
|
2272
2272
|
} else if (issue2.code === "invalid_element") {
|
|
2273
|
-
processError({ issues: issue2.issues }, [...
|
|
2273
|
+
processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
|
|
2274
2274
|
} else {
|
|
2275
|
-
const fullpath = [...
|
|
2275
|
+
const fullpath = [...path20, ...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 path20 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2308
|
+
for (const seg of path20) {
|
|
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 path20 = ref.slice(1).split("/").filter(Boolean);
|
|
15001
|
+
if (path20.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 (path20[0] === defsKey) {
|
|
15006
|
+
const key = path20[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",
|
|
@@ -16816,6 +16817,53 @@ var remoteConnectionsReplySchema = external_exports.union([
|
|
|
16816
16817
|
external_exports.strictObject({ error: external_exports.strictObject({ code: external_exports.number().int(), message: external_exports.string() }) })
|
|
16817
16818
|
]);
|
|
16818
16819
|
|
|
16820
|
+
// packages/shared-contracts/dist/voice-settings.js
|
|
16821
|
+
var VOICE_GATEWAY_STATUS_METHOD = "codexhost/voice/gateway-status";
|
|
16822
|
+
var voiceRoutesSchema = external_exports.strictObject({
|
|
16823
|
+
chatExperience: external_exports.enum(["chatgpt", "codex"]),
|
|
16824
|
+
chatConnection: external_exports.enum(["desktop", "gateway"]),
|
|
16825
|
+
workConnection: external_exports.enum(["desktop", "gateway"])
|
|
16826
|
+
});
|
|
16827
|
+
var DEFAULT_VOICE_ROUTES = Object.freeze({
|
|
16828
|
+
chatExperience: "chatgpt",
|
|
16829
|
+
chatConnection: "desktop",
|
|
16830
|
+
workConnection: "gateway"
|
|
16831
|
+
});
|
|
16832
|
+
var voiceSettingsSchema = voiceRoutesSchema.extend({ inject: external_exports.boolean() });
|
|
16833
|
+
var voiceSettingsRequestSchema = external_exports.discriminatedUnion("operation", [
|
|
16834
|
+
external_exports.strictObject({
|
|
16835
|
+
operation: external_exports.literal("get"),
|
|
16836
|
+
runtime: external_exports.strictObject({
|
|
16837
|
+
id: external_exports.string().uuid(),
|
|
16838
|
+
revision: external_exports.string().max(64),
|
|
16839
|
+
activeCall: external_exports.boolean()
|
|
16840
|
+
}).optional()
|
|
16841
|
+
}),
|
|
16842
|
+
external_exports.strictObject({ operation: external_exports.literal("migrate"), routes: voiceRoutesSchema }),
|
|
16843
|
+
external_exports.strictObject({
|
|
16844
|
+
operation: external_exports.literal("set"),
|
|
16845
|
+
settings: voiceSettingsSchema,
|
|
16846
|
+
revision: external_exports.string().max(64)
|
|
16847
|
+
}),
|
|
16848
|
+
external_exports.strictObject({ operation: external_exports.literal("restart"), confirm: external_exports.literal(true) })
|
|
16849
|
+
]);
|
|
16850
|
+
var voiceSettingsStateSchema = external_exports.strictObject({
|
|
16851
|
+
settings: voiceSettingsSchema,
|
|
16852
|
+
initialized: external_exports.boolean(),
|
|
16853
|
+
revision: external_exports.string(),
|
|
16854
|
+
activeInject: external_exports.boolean().nullable(),
|
|
16855
|
+
envOverride: external_exports.enum(["0", "1"]).nullable(),
|
|
16856
|
+
activeEnvOverride: external_exports.enum(["0", "1"]).nullable(),
|
|
16857
|
+
routesActive: external_exports.boolean().nullable(),
|
|
16858
|
+
activeCall: external_exports.boolean().nullable(),
|
|
16859
|
+
gatewayReady: external_exports.boolean().nullable(),
|
|
16860
|
+
restartAvailable: external_exports.boolean(),
|
|
16861
|
+
restart: external_exports.strictObject({
|
|
16862
|
+
phase: external_exports.enum(["idle", "restarting", "succeeded", "failed"]),
|
|
16863
|
+
error: external_exports.string().nullable()
|
|
16864
|
+
})
|
|
16865
|
+
});
|
|
16866
|
+
|
|
16819
16867
|
// packages/shared-contracts/dist/index.js
|
|
16820
16868
|
var workspaceContractVersionSchema = external_exports.literal(WORKSPACE_CONTRACT_VERSION);
|
|
16821
16869
|
|
|
@@ -17164,6 +17212,8 @@ async function readControllerStatus(filePath) {
|
|
|
17164
17212
|
return {
|
|
17165
17213
|
pid: document.pid,
|
|
17166
17214
|
startedAt: document.startedAt,
|
|
17215
|
+
...typeof document.voiceInjection === "boolean" ? { voiceInjection: document.voiceInjection } : {},
|
|
17216
|
+
...document.voiceEnvOverride === "0" || document.voiceEnvOverride === "1" || document.voiceEnvOverride === null ? { voiceEnvOverride: document.voiceEnvOverride } : {},
|
|
17167
17217
|
renderer: {
|
|
17168
17218
|
state: renderer.state,
|
|
17169
17219
|
error: typeof renderer.error === "string" ? renderer.error : null,
|
|
@@ -17311,7 +17361,7 @@ function createConsoleHostClient(descriptorDirectory, options = {}) {
|
|
|
17311
17361
|
headers: { authorization: `Bearer ${host.token}`, "content-type": "application/json" },
|
|
17312
17362
|
body: JSON.stringify({ method, params: params ?? {} }),
|
|
17313
17363
|
signal: AbortSignal.timeout(
|
|
17314
|
-
method === "codexhost/remote/ssh-setup" ? Math.max(timeoutMs, 33e4) : timeoutMs
|
|
17364
|
+
method === VOICE_GATEWAY_STATUS_METHOD ? Math.min(timeoutMs, 2e3) : method === "codexhost/remote/ssh-setup" ? Math.max(timeoutMs, 33e4) : timeoutMs
|
|
17315
17365
|
)
|
|
17316
17366
|
});
|
|
17317
17367
|
const body = await response.json().catch(() => null);
|
|
@@ -17389,22 +17439,22 @@ function inspectInstallation(launcherExecutable) {
|
|
|
17389
17439
|
);
|
|
17390
17440
|
});
|
|
17391
17441
|
}
|
|
17392
|
-
function launchCommand(installation, environment = process.env) {
|
|
17442
|
+
function launchCommand(installation, environment = process.env, action = "launch") {
|
|
17393
17443
|
if (installation.distribution?.distribution === "npm") {
|
|
17394
17444
|
const node = environment.CODEXHOST_NPM_NODE_PATH;
|
|
17395
17445
|
const wrapper = environment.CODEXHOST_NPM_LAUNCHER_PATH;
|
|
17396
17446
|
if (node && wrapper && path13.isAbsolute(node) && path13.isAbsolute(wrapper)) {
|
|
17397
|
-
return { command: node, args: [wrapper] };
|
|
17447
|
+
return { command: node, args: [wrapper, ...action === "restart" ? [action] : []] };
|
|
17398
17448
|
}
|
|
17399
17449
|
return null;
|
|
17400
17450
|
}
|
|
17401
|
-
return installation.launcherExecutable ? { command: installation.launcherExecutable, args: [
|
|
17451
|
+
return installation.launcherExecutable ? { command: installation.launcherExecutable, args: [action] } : null;
|
|
17402
17452
|
}
|
|
17403
17453
|
|
|
17404
17454
|
// packages/console-server/src/open.ts
|
|
17405
17455
|
import { spawn as spawn3 } from "node:child_process";
|
|
17406
17456
|
import { stat as stat4 } from "node:fs/promises";
|
|
17407
|
-
import
|
|
17457
|
+
import path18 from "node:path";
|
|
17408
17458
|
|
|
17409
17459
|
// packages/console-server/src/page.ts
|
|
17410
17460
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
@@ -17454,6 +17504,49 @@ function allowedChange(origin, consoleHeader, port) {
|
|
|
17454
17504
|
return origin === void 0 || origin === `http://127.0.0.1:${port}` || origin === `http://localhost:${port}`;
|
|
17455
17505
|
}
|
|
17456
17506
|
|
|
17507
|
+
// packages/console-server/src/restart.ts
|
|
17508
|
+
var ConsoleRestart = class {
|
|
17509
|
+
constructor(run2, observe, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), now = Date.now, timeoutMs = 12e4) {
|
|
17510
|
+
this.run = run2;
|
|
17511
|
+
this.observe = observe;
|
|
17512
|
+
this.sleep = sleep;
|
|
17513
|
+
this.now = now;
|
|
17514
|
+
this.timeoutMs = timeoutMs;
|
|
17515
|
+
}
|
|
17516
|
+
run;
|
|
17517
|
+
observe;
|
|
17518
|
+
sleep;
|
|
17519
|
+
now;
|
|
17520
|
+
timeoutMs;
|
|
17521
|
+
status = { phase: "idle", error: null };
|
|
17522
|
+
start(previous) {
|
|
17523
|
+
if (this.status.phase === "restarting") throw new Error("Restart is already in progress");
|
|
17524
|
+
this.status = { phase: "restarting", error: null };
|
|
17525
|
+
void this.restart(previous);
|
|
17526
|
+
}
|
|
17527
|
+
async restart(previous) {
|
|
17528
|
+
try {
|
|
17529
|
+
await this.run();
|
|
17530
|
+
const deadline = this.now() + this.timeoutMs;
|
|
17531
|
+
while (this.now() < deadline) {
|
|
17532
|
+
const current = await this.observe();
|
|
17533
|
+
if (current.failure) throw new Error(current.failure);
|
|
17534
|
+
if (current.launcherPid !== null && current.launcherPid !== previous.launcherPid && current.controllerPid !== null && current.controllerPid !== previous.controllerPid && current.installed) {
|
|
17535
|
+
this.status = { phase: "succeeded", error: null };
|
|
17536
|
+
return;
|
|
17537
|
+
}
|
|
17538
|
+
await this.sleep(250);
|
|
17539
|
+
}
|
|
17540
|
+
throw new Error("Restart timed out. Check startup diagnostics before retrying.");
|
|
17541
|
+
} catch (error51) {
|
|
17542
|
+
this.status = {
|
|
17543
|
+
phase: "failed",
|
|
17544
|
+
error: error51 instanceof Error ? error51.message : String(error51)
|
|
17545
|
+
};
|
|
17546
|
+
}
|
|
17547
|
+
}
|
|
17548
|
+
};
|
|
17549
|
+
|
|
17457
17550
|
// packages/console-server/src/server.ts
|
|
17458
17551
|
import { spawn as spawn2 } from "node:child_process";
|
|
17459
17552
|
import { createServer } from "node:http";
|
|
@@ -17862,6 +17955,135 @@ function createConsoleUpdates(options) {
|
|
|
17862
17955
|
});
|
|
17863
17956
|
}
|
|
17864
17957
|
|
|
17958
|
+
// packages/console-server/src/live-voice-inject.ts
|
|
17959
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
17960
|
+
import { mkdir as mkdir4, readFile as readFile9, rename as rename3, rm as rm5, writeFile as writeFile4 } from "node:fs/promises";
|
|
17961
|
+
import path16 from "node:path";
|
|
17962
|
+
var CPA_LIVE_VOICE_ENV = "CODEXHOST_CPA_LIVE_VOICE";
|
|
17963
|
+
var LIVE_VOICE_INJECT_FILE = "live-voice-inject-v1.json";
|
|
17964
|
+
function injectPath(dataDirectory2) {
|
|
17965
|
+
return path16.join(dataDirectory2, LIVE_VOICE_INJECT_FILE);
|
|
17966
|
+
}
|
|
17967
|
+
async function readLiveVoiceInjectState(dataDirectory2, environment = process.env) {
|
|
17968
|
+
const env = environment[CPA_LIVE_VOICE_ENV];
|
|
17969
|
+
const envOverride = env === "0" || env === "1" ? env : null;
|
|
17970
|
+
let inject = true;
|
|
17971
|
+
try {
|
|
17972
|
+
const parsed = JSON.parse(await readFile9(injectPath(dataDirectory2), "utf8"));
|
|
17973
|
+
inject = parsed.version !== 1 || parsed.inject !== false;
|
|
17974
|
+
} catch {
|
|
17975
|
+
inject = true;
|
|
17976
|
+
}
|
|
17977
|
+
const savedInject = inject;
|
|
17978
|
+
if (envOverride === "0") inject = false;
|
|
17979
|
+
if (envOverride === "1") inject = true;
|
|
17980
|
+
return { inject, savedInject, envOverride };
|
|
17981
|
+
}
|
|
17982
|
+
async function writeLiveVoiceInjectFile(dataDirectory2, inject, routes) {
|
|
17983
|
+
if (!routes) {
|
|
17984
|
+
try {
|
|
17985
|
+
const saved = JSON.parse(await readFile9(injectPath(dataDirectory2), "utf8"));
|
|
17986
|
+
if (saved?.version === 1) routes = voiceRoutesSchema.safeParse(saved.routes).data;
|
|
17987
|
+
} catch {
|
|
17988
|
+
}
|
|
17989
|
+
}
|
|
17990
|
+
await mkdir4(dataDirectory2, { recursive: true, mode: 448 });
|
|
17991
|
+
const temporaryPath = path16.join(dataDirectory2, `.${LIVE_VOICE_INJECT_FILE}.${randomUUID3()}.tmp`);
|
|
17992
|
+
try {
|
|
17993
|
+
await writeFile4(
|
|
17994
|
+
temporaryPath,
|
|
17995
|
+
`${JSON.stringify({ version: 1, inject, ...routes ? { routes } : {} }, null, 2)}
|
|
17996
|
+
`,
|
|
17997
|
+
{
|
|
17998
|
+
mode: 384,
|
|
17999
|
+
flag: "wx"
|
|
18000
|
+
}
|
|
18001
|
+
);
|
|
18002
|
+
await rename3(temporaryPath, injectPath(dataDirectory2));
|
|
18003
|
+
} finally {
|
|
18004
|
+
await rm5(temporaryPath, { force: true });
|
|
18005
|
+
}
|
|
18006
|
+
}
|
|
18007
|
+
|
|
18008
|
+
// packages/console-server/src/voice-settings.ts
|
|
18009
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
18010
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
18011
|
+
import path17 from "node:path";
|
|
18012
|
+
var VoiceSettingsConflict = class extends Error {
|
|
18013
|
+
};
|
|
18014
|
+
var VoiceSettingsStore = class {
|
|
18015
|
+
constructor(directory) {
|
|
18016
|
+
this.directory = directory;
|
|
18017
|
+
}
|
|
18018
|
+
directory;
|
|
18019
|
+
#writes = Promise.resolve();
|
|
18020
|
+
#runtime = /* @__PURE__ */ new Map();
|
|
18021
|
+
async read() {
|
|
18022
|
+
let document = null;
|
|
18023
|
+
try {
|
|
18024
|
+
document = JSON.parse(
|
|
18025
|
+
await readFile10(path17.join(this.directory, LIVE_VOICE_INJECT_FILE), "utf8")
|
|
18026
|
+
);
|
|
18027
|
+
} catch (error51) {
|
|
18028
|
+
if (!(error51 instanceof SyntaxError) && error51.code !== "ENOENT")
|
|
18029
|
+
throw new Error("Could not read voice settings");
|
|
18030
|
+
}
|
|
18031
|
+
const routes = document?.version === 1 ? voiceRoutesSchema.safeParse(document.routes).data : void 0;
|
|
18032
|
+
const settings = {
|
|
18033
|
+
...routes ?? DEFAULT_VOICE_ROUTES,
|
|
18034
|
+
inject: document?.version !== 1 || document.inject !== false
|
|
18035
|
+
};
|
|
18036
|
+
const revision = createHash2("sha256").update(JSON.stringify({ settings, initialized: !!routes })).digest("hex");
|
|
18037
|
+
const runtime = [...this.#runtime.values()].filter((entry) => Date.now() - entry.at < 12e3);
|
|
18038
|
+
return {
|
|
18039
|
+
settings,
|
|
18040
|
+
initialized: !!routes,
|
|
18041
|
+
revision,
|
|
18042
|
+
activeCall: runtime.length ? runtime.some((entry) => entry.activeCall) : null,
|
|
18043
|
+
routesActive: runtime.length ? runtime.every((entry) => entry.revision === revision) : null
|
|
18044
|
+
};
|
|
18045
|
+
}
|
|
18046
|
+
report(runtime) {
|
|
18047
|
+
for (const [id, entry] of this.#runtime)
|
|
18048
|
+
if (Date.now() - entry.at >= 12e3) this.#runtime.delete(id);
|
|
18049
|
+
if (this.#runtime.size < 32 || this.#runtime.has(runtime.id))
|
|
18050
|
+
this.#runtime.set(runtime.id, { ...runtime, at: Date.now() });
|
|
18051
|
+
}
|
|
18052
|
+
async setInjection(inject) {
|
|
18053
|
+
const operation = this.#writes.then(async () => {
|
|
18054
|
+
const current = await this.read();
|
|
18055
|
+
if (current.activeCall)
|
|
18056
|
+
throw new VoiceSettingsConflict("End your voice call before applying changes.");
|
|
18057
|
+
await writeLiveVoiceInjectFile(this.directory, inject);
|
|
18058
|
+
});
|
|
18059
|
+
this.#writes = operation.catch(() => void 0);
|
|
18060
|
+
await operation;
|
|
18061
|
+
}
|
|
18062
|
+
async write(input) {
|
|
18063
|
+
const operation = this.#writes.then(async () => {
|
|
18064
|
+
const current = await this.read();
|
|
18065
|
+
if (input.operation === "migrate" && current.initialized) return current;
|
|
18066
|
+
if (input.operation === "set") {
|
|
18067
|
+
if (current.activeCall)
|
|
18068
|
+
throw new VoiceSettingsConflict("End your voice call before applying changes.");
|
|
18069
|
+
if (input.revision !== current.revision)
|
|
18070
|
+
throw new VoiceSettingsConflict(
|
|
18071
|
+
"Voice settings changed in another window. Reload and try again."
|
|
18072
|
+
);
|
|
18073
|
+
}
|
|
18074
|
+
const settings = input.operation === "set" ? input.settings : { ...current.settings, ...input.routes };
|
|
18075
|
+
await writeLiveVoiceInjectFile(this.directory, settings.inject, {
|
|
18076
|
+
chatExperience: settings.chatExperience,
|
|
18077
|
+
chatConnection: settings.chatConnection,
|
|
18078
|
+
workConnection: settings.workConnection
|
|
18079
|
+
});
|
|
18080
|
+
return this.read();
|
|
18081
|
+
});
|
|
18082
|
+
this.#writes = operation.catch(() => void 0);
|
|
18083
|
+
return operation;
|
|
18084
|
+
}
|
|
18085
|
+
};
|
|
18086
|
+
|
|
17865
18087
|
// packages/console-server/src/server.ts
|
|
17866
18088
|
var CONSOLE_SERVICE = "codexhost-console";
|
|
17867
18089
|
var MAX_BODY_BYTES = 16 * 1024;
|
|
@@ -17917,11 +18139,42 @@ function defaultLaunch(command2, args) {
|
|
|
17917
18139
|
});
|
|
17918
18140
|
child.unref();
|
|
17919
18141
|
}
|
|
18142
|
+
function defaultRestart(command2, args) {
|
|
18143
|
+
return new Promise((resolve, reject) => {
|
|
18144
|
+
const child = spawn2(command2, args, { detached: true, stdio: "ignore", windowsHide: true });
|
|
18145
|
+
child.once("error", () => reject(new Error("Could not start the installed restart command")));
|
|
18146
|
+
child.once("spawn", () => {
|
|
18147
|
+
child.unref();
|
|
18148
|
+
resolve();
|
|
18149
|
+
});
|
|
18150
|
+
});
|
|
18151
|
+
}
|
|
17920
18152
|
function startConsoleServer(options) {
|
|
17921
18153
|
const environment = options.environment ?? process.env;
|
|
17922
18154
|
const inspect = options.inspect ?? inspectInstallation;
|
|
17923
18155
|
const launch = options.launch ?? defaultLaunch;
|
|
17924
18156
|
const loadAnnouncement = options.announcement ?? readAnnouncement;
|
|
18157
|
+
const voiceSettings = new VoiceSettingsStore(options.paths.dataDirectory);
|
|
18158
|
+
let restartRequestedAt = 0;
|
|
18159
|
+
const restart = new ConsoleRestart(
|
|
18160
|
+
async () => {
|
|
18161
|
+
restartRequestedAt = Date.now();
|
|
18162
|
+
const command2 = launchCommand(options.installation, environment, "restart");
|
|
18163
|
+
if (!command2) throw new Error("This installation cannot be restarted from the console");
|
|
18164
|
+
await (options.restart ?? defaultRestart)(command2.command, command2.args);
|
|
18165
|
+
},
|
|
18166
|
+
async () => {
|
|
18167
|
+
inspectCache = null;
|
|
18168
|
+
const collected = await collect();
|
|
18169
|
+
const latest = collected.startup[0];
|
|
18170
|
+
return {
|
|
18171
|
+
launcherPid: collected.inspectDocument?.runtime.launcherPid ?? null,
|
|
18172
|
+
controllerPid: collected.controllerAlive ? collected.controller?.pid ?? null : null,
|
|
18173
|
+
installed: collected.controllerAlive && collected.controller?.renderer.state === "installed",
|
|
18174
|
+
failure: latest?.outcome === "failed" && latest.startedAtMs >= restartRequestedAt ? latest.error ?? "Desktop startup failed" : null
|
|
18175
|
+
};
|
|
18176
|
+
}
|
|
18177
|
+
);
|
|
17925
18178
|
let port = options.port;
|
|
17926
18179
|
let inspectCache = null;
|
|
17927
18180
|
let idleTimer;
|
|
@@ -18003,7 +18256,13 @@ function startConsoleServer(options) {
|
|
|
18003
18256
|
launchAvailable: launchCommand(options.installation, environment) !== null,
|
|
18004
18257
|
summary,
|
|
18005
18258
|
issueUrl: reportIssueUrl,
|
|
18006
|
-
hostAvailable: await options.host.available()
|
|
18259
|
+
hostAvailable: await options.host.available(),
|
|
18260
|
+
liveVoice: {
|
|
18261
|
+
...await readLiveVoiceInjectState(options.paths.dataDirectory, environment),
|
|
18262
|
+
activeInject: collected.controllerAlive && typeof controller?.voiceInjection === "boolean" ? controller.voiceInjection : null,
|
|
18263
|
+
activeEnvOverride: collected.controllerAlive ? controller?.voiceEnvOverride ?? null : null
|
|
18264
|
+
},
|
|
18265
|
+
restart: restart.status
|
|
18007
18266
|
};
|
|
18008
18267
|
}
|
|
18009
18268
|
async function diagnosticReport(collected, withLogs = true) {
|
|
@@ -18036,7 +18295,7 @@ function startConsoleServer(options) {
|
|
|
18036
18295
|
scheduleIdleExit();
|
|
18037
18296
|
const url2 = new URL(request.url ?? "/", `http://127.0.0.1:${port}`);
|
|
18038
18297
|
const method = request.method ?? "GET";
|
|
18039
|
-
|
|
18298
|
+
let route = `${method} ${url2.pathname}`;
|
|
18040
18299
|
if (route === "GET /api/health") {
|
|
18041
18300
|
sendJson(response, 200, {
|
|
18042
18301
|
service: CONSOLE_SERVICE,
|
|
@@ -18077,6 +18336,65 @@ function startConsoleServer(options) {
|
|
|
18077
18336
|
return;
|
|
18078
18337
|
}
|
|
18079
18338
|
}
|
|
18339
|
+
if (route === "POST /api/voice-settings") {
|
|
18340
|
+
const parsed = voiceSettingsRequestSchema.safeParse(body);
|
|
18341
|
+
if (!parsed.success) {
|
|
18342
|
+
sendJson(response, 400, { error: "Invalid voice settings request" });
|
|
18343
|
+
return;
|
|
18344
|
+
}
|
|
18345
|
+
const input = parsed.data;
|
|
18346
|
+
if (input.operation === "restart") {
|
|
18347
|
+
if ((await voiceSettings.read()).activeCall) {
|
|
18348
|
+
sendJson(response, 409, { error: "End your voice call before restarting." });
|
|
18349
|
+
return;
|
|
18350
|
+
}
|
|
18351
|
+
route = "POST /api/restart";
|
|
18352
|
+
} else {
|
|
18353
|
+
try {
|
|
18354
|
+
if (input.operation === "get" && input.runtime) voiceSettings.report(input.runtime);
|
|
18355
|
+
if (input.operation === "set") {
|
|
18356
|
+
const current = await readLiveVoiceInjectState(
|
|
18357
|
+
options.paths.dataDirectory,
|
|
18358
|
+
environment
|
|
18359
|
+
);
|
|
18360
|
+
if (current.envOverride && input.settings.inject !== current.savedInject) {
|
|
18361
|
+
sendJson(response, 409, {
|
|
18362
|
+
error: "Voice integration is controlled by this launch environment."
|
|
18363
|
+
});
|
|
18364
|
+
return;
|
|
18365
|
+
}
|
|
18366
|
+
}
|
|
18367
|
+
const saved = input.operation === "get" ? await voiceSettings.read() : await voiceSettings.write(input);
|
|
18368
|
+
const collected = await collect();
|
|
18369
|
+
const controller = collected.controller;
|
|
18370
|
+
const injection = await readLiveVoiceInjectState(
|
|
18371
|
+
options.paths.dataDirectory,
|
|
18372
|
+
environment
|
|
18373
|
+
);
|
|
18374
|
+
let gatewayReady = null;
|
|
18375
|
+
try {
|
|
18376
|
+
const reply = input.operation === "get" && input.runtime ? null : await options.host.request(VOICE_GATEWAY_STATUS_METHOD, {});
|
|
18377
|
+
if (reply && "result" in reply && typeof reply.result === "boolean")
|
|
18378
|
+
gatewayReady = reply.result;
|
|
18379
|
+
} catch {
|
|
18380
|
+
}
|
|
18381
|
+
sendJson(response, 200, {
|
|
18382
|
+
...saved,
|
|
18383
|
+
activeInject: collected.controllerAlive && typeof controller?.voiceInjection === "boolean" ? controller.voiceInjection : null,
|
|
18384
|
+
envOverride: injection.envOverride,
|
|
18385
|
+
activeEnvOverride: collected.controllerAlive ? controller?.voiceEnvOverride ?? null : null,
|
|
18386
|
+
gatewayReady,
|
|
18387
|
+
restartAvailable: !!collected.inspectDocument?.runtime.running && !!launchCommand(options.installation, environment, "restart"),
|
|
18388
|
+
restart: restart.status
|
|
18389
|
+
});
|
|
18390
|
+
} catch (error51) {
|
|
18391
|
+
sendJson(response, error51 instanceof VoiceSettingsConflict ? 409 : 500, {
|
|
18392
|
+
error: error51 instanceof VoiceSettingsConflict ? error51.message : "Could not save or read voice settings"
|
|
18393
|
+
});
|
|
18394
|
+
}
|
|
18395
|
+
return;
|
|
18396
|
+
}
|
|
18397
|
+
}
|
|
18080
18398
|
if (route === "POST /api/shutdown") {
|
|
18081
18399
|
sendJson(response, 200, { ok: true });
|
|
18082
18400
|
setImmediate(exit);
|
|
@@ -18168,6 +18486,59 @@ function startConsoleServer(options) {
|
|
|
18168
18486
|
}
|
|
18169
18487
|
return;
|
|
18170
18488
|
}
|
|
18489
|
+
if (route === "GET /api/live-voice-inject") {
|
|
18490
|
+
sendJson(
|
|
18491
|
+
response,
|
|
18492
|
+
200,
|
|
18493
|
+
await readLiveVoiceInjectState(options.paths.dataDirectory, environment)
|
|
18494
|
+
);
|
|
18495
|
+
return;
|
|
18496
|
+
}
|
|
18497
|
+
if (route === "POST /api/live-voice-inject") {
|
|
18498
|
+
if (typeof body.inject !== "boolean") {
|
|
18499
|
+
sendJson(response, 400, { error: "Expected { inject: boolean }" });
|
|
18500
|
+
return;
|
|
18501
|
+
}
|
|
18502
|
+
try {
|
|
18503
|
+
await voiceSettings.setInjection(body.inject);
|
|
18504
|
+
} catch (error51) {
|
|
18505
|
+
sendJson(response, error51 instanceof VoiceSettingsConflict ? 409 : 500, {
|
|
18506
|
+
error: error51 instanceof VoiceSettingsConflict ? error51.message : "Could not save voice integration"
|
|
18507
|
+
});
|
|
18508
|
+
return;
|
|
18509
|
+
}
|
|
18510
|
+
sendJson(
|
|
18511
|
+
response,
|
|
18512
|
+
200,
|
|
18513
|
+
await readLiveVoiceInjectState(options.paths.dataDirectory, environment)
|
|
18514
|
+
);
|
|
18515
|
+
return;
|
|
18516
|
+
}
|
|
18517
|
+
if (route === "POST /api/restart") {
|
|
18518
|
+
if (body.confirm !== true) {
|
|
18519
|
+
sendJson(response, 400, {
|
|
18520
|
+
error: "Confirm that restart may interrupt voice and running tasks"
|
|
18521
|
+
});
|
|
18522
|
+
return;
|
|
18523
|
+
}
|
|
18524
|
+
const current = await collect();
|
|
18525
|
+
if (!current.inspectDocument?.runtime.running || !launchCommand(options.installation, environment, "restart")) {
|
|
18526
|
+
sendJson(response, 409, { error: "No restartable managed Desktop is running" });
|
|
18527
|
+
return;
|
|
18528
|
+
}
|
|
18529
|
+
if (restart.status.phase === "restarting") {
|
|
18530
|
+
sendJson(response, 409, { error: "Restart is already in progress" });
|
|
18531
|
+
return;
|
|
18532
|
+
}
|
|
18533
|
+
restart.start({
|
|
18534
|
+
launcherPid: current.inspectDocument.runtime.launcherPid,
|
|
18535
|
+
controllerPid: current.controller?.pid ?? null,
|
|
18536
|
+
installed: current.controller?.renderer.state === "installed",
|
|
18537
|
+
failure: null
|
|
18538
|
+
});
|
|
18539
|
+
sendJson(response, 202, restart.status);
|
|
18540
|
+
return;
|
|
18541
|
+
}
|
|
18171
18542
|
if (route === "POST /api/launch") {
|
|
18172
18543
|
const command2 = launchCommand(options.installation, environment);
|
|
18173
18544
|
if (!command2) {
|
|
@@ -18259,7 +18630,7 @@ async function requestShutdown(port) {
|
|
|
18259
18630
|
if (!response.ok) throw new Error(`console shutdown failed: HTTP ${response.status}`);
|
|
18260
18631
|
}
|
|
18261
18632
|
async function consoleBuildId(entryPath2, environment = process.env) {
|
|
18262
|
-
const files = [entryPath2, ...consoleBundleCandidates(
|
|
18633
|
+
const files = [entryPath2, ...consoleBundleCandidates(path18.dirname(entryPath2))];
|
|
18263
18634
|
const parts = [];
|
|
18264
18635
|
for (const file2 of files) {
|
|
18265
18636
|
const metadata = await stat4(file2).catch(() => null);
|
|
@@ -18362,7 +18733,7 @@ async function updateFromCommand(appDirectory2, arguments_) {
|
|
|
18362
18733
|
var IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
18363
18734
|
var HANDOFF_EXIT_DELAY_MS = 500;
|
|
18364
18735
|
var entryPath = fileURLToPath(import.meta.url);
|
|
18365
|
-
var appDirectory =
|
|
18736
|
+
var appDirectory = path19.dirname(entryPath);
|
|
18366
18737
|
function usage() {
|
|
18367
18738
|
console.error(
|
|
18368
18739
|
"usage: console-server open [--no-browser] | console-server ensure | console-server serve | console-server update"
|