@karlorz/codexhost-cli-win32-arm64 0.13.3-4 → 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 +432 -31
- package/app/console-web.js +806 -186
- package/app/cpa-live-voice-hook.js +16310 -31
- package/app/desktop-controller.mjs +147 -37
- package/app/host-runtime.mjs +3535 -3126
- package/app/plugins/antigravity/plugin.mjs +77 -0
- package/app/plugins/claude-code/plugin.mjs +152 -29
- package/app/plugins/codebuddy/plugin.mjs +109 -32
- package/app/plugins/codex-usage/plugin.mjs +77 -0
- package/app/plugins/cursor-cli/plugin.mjs +120 -43
- package/app/plugins/deepseek-harness/plugin.mjs +82 -5
- package/app/plugins/grok/plugin.mjs +138 -24
- package/app/plugins/hermes/plugin.mjs +77 -0
- package/app/plugins/kimi-code/plugin.mjs +99 -22
- package/app/plugins/kiro-cli/plugin.mjs +99 -22
- package/app/plugins/omp/plugin.mjs +92 -15
- package/app/plugins/opencode/plugin.mjs +77 -0
- package/app/plugins/pi/plugin.mjs +98 -21
- package/app/plugins/qoder/plugin.mjs +85 -8
- package/app/plugins/qoder-cn/plugin.mjs +85 -8
- package/app/plugins/workbuddy/plugin.mjs +109 -32
- package/app/plugins/zcode/plugin.mjs +100 -23
- package/app/renderer-extension.js +2238 -860
- 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
|
}
|
|
@@ -15667,6 +15667,36 @@ var hostInteractionIdSchema = opaqueIdSchema.brand();
|
|
|
15667
15667
|
var threadPrewarmDiscardParamsSchema = external_exports.object({ threadId: hostThreadIdSchema });
|
|
15668
15668
|
var threadPrewarmDiscardResultSchema = external_exports.object({ discarded: external_exports.boolean() });
|
|
15669
15669
|
|
|
15670
|
+
// packages/shared-contracts/dist/realtime-call.js
|
|
15671
|
+
var REALTIME_CALL_FORWARDED_HEADERS = [
|
|
15672
|
+
"openai-alpha",
|
|
15673
|
+
"thread-id",
|
|
15674
|
+
"session-id",
|
|
15675
|
+
"originator",
|
|
15676
|
+
"x-codex-turn-metadata"
|
|
15677
|
+
];
|
|
15678
|
+
var FORWARDED_HEADERS = new Set(REALTIME_CALL_FORWARDED_HEADERS);
|
|
15679
|
+
var REALTIME_CALL_ID_PATTERN = /^rtc_[A-Za-z0-9_-]{1,128}$/u;
|
|
15680
|
+
var requestId = external_exports.string().min(1).max(128);
|
|
15681
|
+
var realtimeCallParamsSchema = external_exports.discriminatedUnion("operation", [
|
|
15682
|
+
external_exports.object({
|
|
15683
|
+
operation: external_exports.literal("create"),
|
|
15684
|
+
requestId,
|
|
15685
|
+
body: external_exports.string().min(1).max(16 << 20),
|
|
15686
|
+
headers: external_exports.record(external_exports.string(), external_exports.string()).default({})
|
|
15687
|
+
}),
|
|
15688
|
+
external_exports.object({ operation: external_exports.literal("cancel"), requestId }),
|
|
15689
|
+
external_exports.object({
|
|
15690
|
+
operation: external_exports.literal("hangup"),
|
|
15691
|
+
callId: external_exports.string().regex(REALTIME_CALL_ID_PATTERN)
|
|
15692
|
+
})
|
|
15693
|
+
]);
|
|
15694
|
+
var realtimeCallResponseSchema = external_exports.object({
|
|
15695
|
+
status: external_exports.number().int().min(200).max(599),
|
|
15696
|
+
body: external_exports.string(),
|
|
15697
|
+
headers: external_exports.record(external_exports.string(), external_exports.string())
|
|
15698
|
+
});
|
|
15699
|
+
|
|
15670
15700
|
// packages/shared-contracts/dist/console.js
|
|
15671
15701
|
var consoleAnnouncementSchema = external_exports.strictObject({
|
|
15672
15702
|
title: external_exports.string().trim().min(1).max(200),
|
|
@@ -15679,6 +15709,7 @@ var consoleOpenResultSchema = external_exports.strictObject({
|
|
|
15679
15709
|
url: external_exports.string().max(2048).regex(/^http:\/\/(?:127\.0\.0\.1|localhost):\d{1,5}\/(?:\?.*)?$/u)
|
|
15680
15710
|
});
|
|
15681
15711
|
var CONSOLE_HOST_METHODS = Object.freeze([
|
|
15712
|
+
"codexhost/voice/gateway-status",
|
|
15682
15713
|
"codexhost/console/remote-connections",
|
|
15683
15714
|
"codexhost/runtime/status",
|
|
15684
15715
|
"codexhost/remote/ssh-setup",
|
|
@@ -16786,6 +16817,53 @@ var remoteConnectionsReplySchema = external_exports.union([
|
|
|
16786
16817
|
external_exports.strictObject({ error: external_exports.strictObject({ code: external_exports.number().int(), message: external_exports.string() }) })
|
|
16787
16818
|
]);
|
|
16788
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
|
+
|
|
16789
16867
|
// packages/shared-contracts/dist/index.js
|
|
16790
16868
|
var workspaceContractVersionSchema = external_exports.literal(WORKSPACE_CONTRACT_VERSION);
|
|
16791
16869
|
|
|
@@ -17134,6 +17212,8 @@ async function readControllerStatus(filePath) {
|
|
|
17134
17212
|
return {
|
|
17135
17213
|
pid: document.pid,
|
|
17136
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 } : {},
|
|
17137
17217
|
renderer: {
|
|
17138
17218
|
state: renderer.state,
|
|
17139
17219
|
error: typeof renderer.error === "string" ? renderer.error : null,
|
|
@@ -17281,7 +17361,7 @@ function createConsoleHostClient(descriptorDirectory, options = {}) {
|
|
|
17281
17361
|
headers: { authorization: `Bearer ${host.token}`, "content-type": "application/json" },
|
|
17282
17362
|
body: JSON.stringify({ method, params: params ?? {} }),
|
|
17283
17363
|
signal: AbortSignal.timeout(
|
|
17284
|
-
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
|
|
17285
17365
|
)
|
|
17286
17366
|
});
|
|
17287
17367
|
const body = await response.json().catch(() => null);
|
|
@@ -17359,22 +17439,22 @@ function inspectInstallation(launcherExecutable) {
|
|
|
17359
17439
|
);
|
|
17360
17440
|
});
|
|
17361
17441
|
}
|
|
17362
|
-
function launchCommand(installation, environment = process.env) {
|
|
17442
|
+
function launchCommand(installation, environment = process.env, action = "launch") {
|
|
17363
17443
|
if (installation.distribution?.distribution === "npm") {
|
|
17364
17444
|
const node = environment.CODEXHOST_NPM_NODE_PATH;
|
|
17365
17445
|
const wrapper = environment.CODEXHOST_NPM_LAUNCHER_PATH;
|
|
17366
17446
|
if (node && wrapper && path13.isAbsolute(node) && path13.isAbsolute(wrapper)) {
|
|
17367
|
-
return { command: node, args: [wrapper] };
|
|
17447
|
+
return { command: node, args: [wrapper, ...action === "restart" ? [action] : []] };
|
|
17368
17448
|
}
|
|
17369
17449
|
return null;
|
|
17370
17450
|
}
|
|
17371
|
-
return installation.launcherExecutable ? { command: installation.launcherExecutable, args: [
|
|
17451
|
+
return installation.launcherExecutable ? { command: installation.launcherExecutable, args: [action] } : null;
|
|
17372
17452
|
}
|
|
17373
17453
|
|
|
17374
17454
|
// packages/console-server/src/open.ts
|
|
17375
17455
|
import { spawn as spawn3 } from "node:child_process";
|
|
17376
17456
|
import { stat as stat4 } from "node:fs/promises";
|
|
17377
|
-
import
|
|
17457
|
+
import path18 from "node:path";
|
|
17378
17458
|
|
|
17379
17459
|
// packages/console-server/src/page.ts
|
|
17380
17460
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
@@ -17424,6 +17504,49 @@ function allowedChange(origin, consoleHeader, port) {
|
|
|
17424
17504
|
return origin === void 0 || origin === `http://127.0.0.1:${port}` || origin === `http://localhost:${port}`;
|
|
17425
17505
|
}
|
|
17426
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
|
+
|
|
17427
17550
|
// packages/console-server/src/server.ts
|
|
17428
17551
|
import { spawn as spawn2 } from "node:child_process";
|
|
17429
17552
|
import { createServer } from "node:http";
|
|
@@ -17832,6 +17955,135 @@ function createConsoleUpdates(options) {
|
|
|
17832
17955
|
});
|
|
17833
17956
|
}
|
|
17834
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
|
+
|
|
17835
18087
|
// packages/console-server/src/server.ts
|
|
17836
18088
|
var CONSOLE_SERVICE = "codexhost-console";
|
|
17837
18089
|
var MAX_BODY_BYTES = 16 * 1024;
|
|
@@ -17887,11 +18139,42 @@ function defaultLaunch(command2, args) {
|
|
|
17887
18139
|
});
|
|
17888
18140
|
child.unref();
|
|
17889
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
|
+
}
|
|
17890
18152
|
function startConsoleServer(options) {
|
|
17891
18153
|
const environment = options.environment ?? process.env;
|
|
17892
18154
|
const inspect = options.inspect ?? inspectInstallation;
|
|
17893
18155
|
const launch = options.launch ?? defaultLaunch;
|
|
17894
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
|
+
);
|
|
17895
18178
|
let port = options.port;
|
|
17896
18179
|
let inspectCache = null;
|
|
17897
18180
|
let idleTimer;
|
|
@@ -17973,7 +18256,13 @@ function startConsoleServer(options) {
|
|
|
17973
18256
|
launchAvailable: launchCommand(options.installation, environment) !== null,
|
|
17974
18257
|
summary,
|
|
17975
18258
|
issueUrl: reportIssueUrl,
|
|
17976
|
-
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
|
|
17977
18266
|
};
|
|
17978
18267
|
}
|
|
17979
18268
|
async function diagnosticReport(collected, withLogs = true) {
|
|
@@ -18006,7 +18295,7 @@ function startConsoleServer(options) {
|
|
|
18006
18295
|
scheduleIdleExit();
|
|
18007
18296
|
const url2 = new URL(request.url ?? "/", `http://127.0.0.1:${port}`);
|
|
18008
18297
|
const method = request.method ?? "GET";
|
|
18009
|
-
|
|
18298
|
+
let route = `${method} ${url2.pathname}`;
|
|
18010
18299
|
if (route === "GET /api/health") {
|
|
18011
18300
|
sendJson(response, 200, {
|
|
18012
18301
|
service: CONSOLE_SERVICE,
|
|
@@ -18047,6 +18336,65 @@ function startConsoleServer(options) {
|
|
|
18047
18336
|
return;
|
|
18048
18337
|
}
|
|
18049
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
|
+
}
|
|
18050
18398
|
if (route === "POST /api/shutdown") {
|
|
18051
18399
|
sendJson(response, 200, { ok: true });
|
|
18052
18400
|
setImmediate(exit);
|
|
@@ -18138,6 +18486,59 @@ function startConsoleServer(options) {
|
|
|
18138
18486
|
}
|
|
18139
18487
|
return;
|
|
18140
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
|
+
}
|
|
18141
18542
|
if (route === "POST /api/launch") {
|
|
18142
18543
|
const command2 = launchCommand(options.installation, environment);
|
|
18143
18544
|
if (!command2) {
|
|
@@ -18229,7 +18630,7 @@ async function requestShutdown(port) {
|
|
|
18229
18630
|
if (!response.ok) throw new Error(`console shutdown failed: HTTP ${response.status}`);
|
|
18230
18631
|
}
|
|
18231
18632
|
async function consoleBuildId(entryPath2, environment = process.env) {
|
|
18232
|
-
const files = [entryPath2, ...consoleBundleCandidates(
|
|
18633
|
+
const files = [entryPath2, ...consoleBundleCandidates(path18.dirname(entryPath2))];
|
|
18233
18634
|
const parts = [];
|
|
18234
18635
|
for (const file2 of files) {
|
|
18235
18636
|
const metadata = await stat4(file2).catch(() => null);
|
|
@@ -18332,7 +18733,7 @@ async function updateFromCommand(appDirectory2, arguments_) {
|
|
|
18332
18733
|
var IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
18333
18734
|
var HANDOFF_EXIT_DELAY_MS = 500;
|
|
18334
18735
|
var entryPath = fileURLToPath(import.meta.url);
|
|
18335
|
-
var appDirectory =
|
|
18736
|
+
var appDirectory = path19.dirname(entryPath);
|
|
18336
18737
|
function usage() {
|
|
18337
18738
|
console.error(
|
|
18338
18739
|
"usage: console-server open [--no-browser] | console-server ensure | console-server serve | console-server update"
|