codex-to-im 1.0.22 → 1.0.26
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/README_EN.md +105 -172
- package/SECURITY.md +3 -7
- package/dist/cli.mjs +1 -7
- package/dist/daemon.mjs +31 -91
- package/dist/ui-server.mjs +201 -591
- package/docs/codex-to-im-prd.md +1 -1
- package/docs/codex-to-im-shared-thread-design.md +8 -8
- package/docs/install-windows.md +5 -9
- package/package.json +1 -1
- package/references/troubleshooting.md +1 -1
- package/scripts/daemon.sh +6 -6
- package/scripts/doctor.sh +2 -2
- package/scripts/supervisor-macos.sh +1 -1
- package/scripts/supervisor-windows.ps1 +8 -8
- package/config.env.example +0 -138
package/dist/ui-server.mjs
CHANGED
|
@@ -1572,10 +1572,10 @@ var require_segments = __commonJS({
|
|
|
1572
1572
|
const segs = getSegmentsFromString(data, Utils.isKanjiModeEnabled());
|
|
1573
1573
|
const nodes = buildNodes(segs);
|
|
1574
1574
|
const graph = buildGraph(nodes, version);
|
|
1575
|
-
const
|
|
1575
|
+
const path9 = dijkstra.find_path(graph.map, "start", "end");
|
|
1576
1576
|
const optimizedSegs = [];
|
|
1577
|
-
for (let i = 1; i <
|
|
1578
|
-
optimizedSegs.push(graph.table[
|
|
1577
|
+
for (let i = 1; i < path9.length - 1; i++) {
|
|
1578
|
+
optimizedSegs.push(graph.table[path9[i]].node);
|
|
1579
1579
|
}
|
|
1580
1580
|
return exports.fromArray(mergeSegments(optimizedSegs));
|
|
1581
1581
|
};
|
|
@@ -4011,7 +4011,7 @@ var require_utils2 = __commonJS({
|
|
|
4011
4011
|
// node_modules/qrcode/lib/renderer/png.js
|
|
4012
4012
|
var require_png2 = __commonJS({
|
|
4013
4013
|
"node_modules/qrcode/lib/renderer/png.js"(exports) {
|
|
4014
|
-
var
|
|
4014
|
+
var fs8 = __require("fs");
|
|
4015
4015
|
var PNG = require_png().PNG;
|
|
4016
4016
|
var Utils = require_utils2();
|
|
4017
4017
|
exports.render = function render(qrData, options) {
|
|
@@ -4052,7 +4052,7 @@ var require_png2 = __commonJS({
|
|
|
4052
4052
|
});
|
|
4053
4053
|
png.pack();
|
|
4054
4054
|
};
|
|
4055
|
-
exports.renderToFile = function renderToFile(
|
|
4055
|
+
exports.renderToFile = function renderToFile(path9, qrData, options, cb) {
|
|
4056
4056
|
if (typeof cb === "undefined") {
|
|
4057
4057
|
cb = options;
|
|
4058
4058
|
options = void 0;
|
|
@@ -4063,7 +4063,7 @@ var require_png2 = __commonJS({
|
|
|
4063
4063
|
called = true;
|
|
4064
4064
|
cb.apply(null, args);
|
|
4065
4065
|
};
|
|
4066
|
-
const stream =
|
|
4066
|
+
const stream = fs8.createWriteStream(path9);
|
|
4067
4067
|
stream.on("error", done);
|
|
4068
4068
|
stream.on("close", done);
|
|
4069
4069
|
exports.renderToFileStream(stream, qrData, options);
|
|
@@ -4125,14 +4125,14 @@ var require_utf8 = __commonJS({
|
|
|
4125
4125
|
}
|
|
4126
4126
|
return output;
|
|
4127
4127
|
};
|
|
4128
|
-
exports.renderToFile = function renderToFile(
|
|
4128
|
+
exports.renderToFile = function renderToFile(path9, qrData, options, cb) {
|
|
4129
4129
|
if (typeof cb === "undefined") {
|
|
4130
4130
|
cb = options;
|
|
4131
4131
|
options = void 0;
|
|
4132
4132
|
}
|
|
4133
|
-
const
|
|
4133
|
+
const fs8 = __require("fs");
|
|
4134
4134
|
const utf8 = exports.render(qrData, options);
|
|
4135
|
-
|
|
4135
|
+
fs8.writeFile(path9, utf8, cb);
|
|
4136
4136
|
};
|
|
4137
4137
|
}
|
|
4138
4138
|
});
|
|
@@ -4253,7 +4253,7 @@ var require_svg_tag = __commonJS({
|
|
|
4253
4253
|
return str;
|
|
4254
4254
|
}
|
|
4255
4255
|
function qrToPath(data, size, margin) {
|
|
4256
|
-
let
|
|
4256
|
+
let path9 = "";
|
|
4257
4257
|
let moveBy = 0;
|
|
4258
4258
|
let newRow = false;
|
|
4259
4259
|
let lineLength = 0;
|
|
@@ -4264,19 +4264,19 @@ var require_svg_tag = __commonJS({
|
|
|
4264
4264
|
if (data[i]) {
|
|
4265
4265
|
lineLength++;
|
|
4266
4266
|
if (!(i > 0 && col > 0 && data[i - 1])) {
|
|
4267
|
-
|
|
4267
|
+
path9 += newRow ? svgCmd("M", col + margin, 0.5 + row + margin) : svgCmd("m", moveBy, 0);
|
|
4268
4268
|
moveBy = 0;
|
|
4269
4269
|
newRow = false;
|
|
4270
4270
|
}
|
|
4271
4271
|
if (!(col + 1 < size && data[i + 1])) {
|
|
4272
|
-
|
|
4272
|
+
path9 += svgCmd("h", lineLength);
|
|
4273
4273
|
lineLength = 0;
|
|
4274
4274
|
}
|
|
4275
4275
|
} else {
|
|
4276
4276
|
moveBy++;
|
|
4277
4277
|
}
|
|
4278
4278
|
}
|
|
4279
|
-
return
|
|
4279
|
+
return path9;
|
|
4280
4280
|
}
|
|
4281
4281
|
exports.render = function render(qrData, options, cb) {
|
|
4282
4282
|
const opts = Utils.getOptions(options);
|
|
@@ -4284,10 +4284,10 @@ var require_svg_tag = __commonJS({
|
|
|
4284
4284
|
const data = qrData.modules.data;
|
|
4285
4285
|
const qrcodesize = size + opts.margin * 2;
|
|
4286
4286
|
const bg = !opts.color.light.a ? "" : "<path " + getColorAttrib(opts.color.light, "fill") + ' d="M0 0h' + qrcodesize + "v" + qrcodesize + 'H0z"/>';
|
|
4287
|
-
const
|
|
4287
|
+
const path9 = "<path " + getColorAttrib(opts.color.dark, "stroke") + ' d="' + qrToPath(data, size, opts.margin) + '"/>';
|
|
4288
4288
|
const viewBox = 'viewBox="0 0 ' + qrcodesize + " " + qrcodesize + '"';
|
|
4289
4289
|
const width = !opts.width ? "" : 'width="' + opts.width + '" height="' + opts.width + '" ';
|
|
4290
|
-
const svgTag = '<svg xmlns="http://www.w3.org/2000/svg" ' + width + viewBox + ' shape-rendering="crispEdges">' + bg +
|
|
4290
|
+
const svgTag = '<svg xmlns="http://www.w3.org/2000/svg" ' + width + viewBox + ' shape-rendering="crispEdges">' + bg + path9 + "</svg>\n";
|
|
4291
4291
|
if (typeof cb === "function") {
|
|
4292
4292
|
cb(null, svgTag);
|
|
4293
4293
|
}
|
|
@@ -4301,15 +4301,15 @@ var require_svg = __commonJS({
|
|
|
4301
4301
|
"node_modules/qrcode/lib/renderer/svg.js"(exports) {
|
|
4302
4302
|
var svgTagRenderer = require_svg_tag();
|
|
4303
4303
|
exports.render = svgTagRenderer.render;
|
|
4304
|
-
exports.renderToFile = function renderToFile(
|
|
4304
|
+
exports.renderToFile = function renderToFile(path9, qrData, options, cb) {
|
|
4305
4305
|
if (typeof cb === "undefined") {
|
|
4306
4306
|
cb = options;
|
|
4307
4307
|
options = void 0;
|
|
4308
4308
|
}
|
|
4309
|
-
const
|
|
4309
|
+
const fs8 = __require("fs");
|
|
4310
4310
|
const svgTag = exports.render(qrData, options);
|
|
4311
4311
|
const xmlStr = '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' + svgTag;
|
|
4312
|
-
|
|
4312
|
+
fs8.writeFile(path9, xmlStr, cb);
|
|
4313
4313
|
};
|
|
4314
4314
|
}
|
|
4315
4315
|
});
|
|
@@ -4467,8 +4467,8 @@ var require_server = __commonJS({
|
|
|
4467
4467
|
cb
|
|
4468
4468
|
};
|
|
4469
4469
|
}
|
|
4470
|
-
function getTypeFromFilename(
|
|
4471
|
-
return
|
|
4470
|
+
function getTypeFromFilename(path9) {
|
|
4471
|
+
return path9.slice((path9.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
|
|
4472
4472
|
}
|
|
4473
4473
|
function getRendererFromType(type) {
|
|
4474
4474
|
switch (type) {
|
|
@@ -4532,17 +4532,17 @@ var require_server = __commonJS({
|
|
|
4532
4532
|
const renderer = getRendererFromType(params.opts.type);
|
|
4533
4533
|
return render(renderer.renderToBuffer, text2, params);
|
|
4534
4534
|
};
|
|
4535
|
-
exports.toFile = function toFile(
|
|
4536
|
-
if (typeof
|
|
4535
|
+
exports.toFile = function toFile(path9, text2, opts, cb) {
|
|
4536
|
+
if (typeof path9 !== "string" || !(typeof text2 === "string" || typeof text2 === "object")) {
|
|
4537
4537
|
throw new Error("Invalid argument");
|
|
4538
4538
|
}
|
|
4539
4539
|
if (arguments.length < 3 && !canPromise()) {
|
|
4540
4540
|
throw new Error("Too few arguments provided");
|
|
4541
4541
|
}
|
|
4542
4542
|
const params = checkParams(text2, opts, cb);
|
|
4543
|
-
const type = params.opts.type || getTypeFromFilename(
|
|
4543
|
+
const type = params.opts.type || getTypeFromFilename(path9);
|
|
4544
4544
|
const renderer = getRendererFromType(type);
|
|
4545
|
-
const renderToFile = renderer.renderToFile.bind(null,
|
|
4545
|
+
const renderToFile = renderer.renderToFile.bind(null, path9);
|
|
4546
4546
|
return render(renderToFile, text2, params);
|
|
4547
4547
|
};
|
|
4548
4548
|
exports.toFileStream = function toFileStream(stream, text2, opts) {
|
|
@@ -4568,8 +4568,7 @@ var require_lib = __commonJS({
|
|
|
4568
4568
|
import http from "node:http";
|
|
4569
4569
|
import crypto4 from "node:crypto";
|
|
4570
4570
|
import net from "node:net";
|
|
4571
|
-
import
|
|
4572
|
-
import fs9 from "node:fs";
|
|
4571
|
+
import os5 from "node:os";
|
|
4573
4572
|
|
|
4574
4573
|
// src/config.ts
|
|
4575
4574
|
import fs from "node:fs";
|
|
@@ -4581,15 +4580,9 @@ function toFeishuConfig(channel) {
|
|
|
4581
4580
|
function toWeixinConfig(channel) {
|
|
4582
4581
|
return channel?.provider === "weixin" ? channel.config : void 0;
|
|
4583
4582
|
}
|
|
4584
|
-
var LEGACY_CTI_HOME = path.join(os.homedir(), ".claude-to-im");
|
|
4585
4583
|
var DEFAULT_CTI_HOME = path.join(os.homedir(), ".codex-to-im");
|
|
4586
4584
|
var DEFAULT_WORKSPACE_ROOT = path.join(os.homedir(), "cx2im");
|
|
4587
|
-
|
|
4588
|
-
if (fs.existsSync(DEFAULT_CTI_HOME)) return DEFAULT_CTI_HOME;
|
|
4589
|
-
if (fs.existsSync(LEGACY_CTI_HOME)) return LEGACY_CTI_HOME;
|
|
4590
|
-
return DEFAULT_CTI_HOME;
|
|
4591
|
-
}
|
|
4592
|
-
var CTI_HOME = process.env.CTI_HOME || resolveDefaultCtiHome();
|
|
4585
|
+
var CTI_HOME = process.env.CTI_HOME || DEFAULT_CTI_HOME;
|
|
4593
4586
|
var CONFIG_PATH = path.join(CTI_HOME, "config.env");
|
|
4594
4587
|
var CONFIG_V2_PATH = path.join(CTI_HOME, "config.v2.json");
|
|
4595
4588
|
function expandHomePath(value) {
|
|
@@ -4996,350 +4989,10 @@ function configToSettings(config) {
|
|
|
4996
4989
|
return m;
|
|
4997
4990
|
}
|
|
4998
4991
|
|
|
4999
|
-
// src/
|
|
5000
|
-
var PendingPermissions = class {
|
|
5001
|
-
pending = /* @__PURE__ */ new Map();
|
|
5002
|
-
timeoutMs = 5 * 60 * 1e3;
|
|
5003
|
-
// 5 minutes
|
|
5004
|
-
waitFor(toolUseID) {
|
|
5005
|
-
return new Promise((resolve) => {
|
|
5006
|
-
const timer = setTimeout(() => {
|
|
5007
|
-
this.pending.delete(toolUseID);
|
|
5008
|
-
resolve({ behavior: "deny", message: "Permission request timed out" });
|
|
5009
|
-
}, this.timeoutMs);
|
|
5010
|
-
this.pending.set(toolUseID, { resolve, timer });
|
|
5011
|
-
});
|
|
5012
|
-
}
|
|
5013
|
-
resolve(permissionRequestId, resolution) {
|
|
5014
|
-
const entry = this.pending.get(permissionRequestId);
|
|
5015
|
-
if (!entry) return false;
|
|
5016
|
-
clearTimeout(entry.timer);
|
|
5017
|
-
if (resolution.behavior === "allow") {
|
|
5018
|
-
entry.resolve({ behavior: "allow" });
|
|
5019
|
-
} else {
|
|
5020
|
-
entry.resolve({ behavior: "deny", message: resolution.message || "Denied by user" });
|
|
5021
|
-
}
|
|
5022
|
-
this.pending.delete(permissionRequestId);
|
|
5023
|
-
return true;
|
|
5024
|
-
}
|
|
5025
|
-
denyAll() {
|
|
5026
|
-
for (const [, entry] of this.pending) {
|
|
5027
|
-
clearTimeout(entry.timer);
|
|
5028
|
-
entry.resolve({ behavior: "deny", message: "Bridge shutting down" });
|
|
5029
|
-
}
|
|
5030
|
-
this.pending.clear();
|
|
5031
|
-
}
|
|
5032
|
-
get size() {
|
|
5033
|
-
return this.pending.size;
|
|
5034
|
-
}
|
|
5035
|
-
};
|
|
5036
|
-
|
|
5037
|
-
// src/codex-provider.ts
|
|
4992
|
+
// src/desktop-sessions.ts
|
|
5038
4993
|
import fs2 from "node:fs";
|
|
5039
4994
|
import os2 from "node:os";
|
|
5040
4995
|
import path2 from "node:path";
|
|
5041
|
-
|
|
5042
|
-
// src/sse-utils.ts
|
|
5043
|
-
function sseEvent(type, data) {
|
|
5044
|
-
const payload = typeof data === "string" ? data : JSON.stringify(data);
|
|
5045
|
-
return `data: ${JSON.stringify({ type, data: payload })}
|
|
5046
|
-
`;
|
|
5047
|
-
}
|
|
5048
|
-
|
|
5049
|
-
// src/codex-provider.ts
|
|
5050
|
-
var MIME_EXT = {
|
|
5051
|
-
"image/png": ".png",
|
|
5052
|
-
"image/jpeg": ".jpg",
|
|
5053
|
-
"image/jpg": ".jpg",
|
|
5054
|
-
"image/gif": ".gif",
|
|
5055
|
-
"image/webp": ".webp"
|
|
5056
|
-
};
|
|
5057
|
-
function toApprovalPolicy(permissionMode) {
|
|
5058
|
-
switch (permissionMode) {
|
|
5059
|
-
case "never":
|
|
5060
|
-
return "never";
|
|
5061
|
-
case "acceptEdits":
|
|
5062
|
-
return "on-failure";
|
|
5063
|
-
case "plan":
|
|
5064
|
-
return "on-request";
|
|
5065
|
-
case "default":
|
|
5066
|
-
return "on-request";
|
|
5067
|
-
default:
|
|
5068
|
-
return "on-request";
|
|
5069
|
-
}
|
|
5070
|
-
}
|
|
5071
|
-
function shouldSkipGitRepoCheck() {
|
|
5072
|
-
return process.env.CTI_CODEX_SKIP_GIT_REPO_CHECK === "true";
|
|
5073
|
-
}
|
|
5074
|
-
function normalizeSandboxMode(mode) {
|
|
5075
|
-
if (mode === "read-only" || mode === "workspace-write" || mode === "danger-full-access") {
|
|
5076
|
-
return mode;
|
|
5077
|
-
}
|
|
5078
|
-
return "workspace-write";
|
|
5079
|
-
}
|
|
5080
|
-
function normalizeReasoningEffort(value) {
|
|
5081
|
-
if (value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh") {
|
|
5082
|
-
return value;
|
|
5083
|
-
}
|
|
5084
|
-
return void 0;
|
|
5085
|
-
}
|
|
5086
|
-
function shouldRetryFreshThread(message) {
|
|
5087
|
-
const lower = message.toLowerCase();
|
|
5088
|
-
return lower.includes("resuming session with different model") || lower.includes("no such session") || lower.includes("resume") && lower.includes("session");
|
|
5089
|
-
}
|
|
5090
|
-
var CodexProvider = class {
|
|
5091
|
-
constructor(pendingPerms) {
|
|
5092
|
-
this.pendingPerms = pendingPerms;
|
|
5093
|
-
}
|
|
5094
|
-
sdk = null;
|
|
5095
|
-
codex = null;
|
|
5096
|
-
/** Maps session IDs to Codex thread IDs for resume. */
|
|
5097
|
-
threadIds = /* @__PURE__ */ new Map();
|
|
5098
|
-
/**
|
|
5099
|
-
* Lazily load the Codex SDK. Throws a clear error if the installation is incomplete.
|
|
5100
|
-
*/
|
|
5101
|
-
async ensureSDK() {
|
|
5102
|
-
if (this.sdk && this.codex) {
|
|
5103
|
-
return { sdk: this.sdk, codex: this.codex };
|
|
5104
|
-
}
|
|
5105
|
-
try {
|
|
5106
|
-
this.sdk = await Function('return import("@openai/codex-sdk")')();
|
|
5107
|
-
} catch {
|
|
5108
|
-
throw new Error(
|
|
5109
|
-
"[CodexProvider] @openai/codex-sdk is missing from this codex-to-im installation. Reinstall codex-to-im or run npm install in the project root."
|
|
5110
|
-
);
|
|
5111
|
-
}
|
|
5112
|
-
const apiKey = process.env.CTI_CODEX_API_KEY || process.env.CODEX_API_KEY || process.env.OPENAI_API_KEY || void 0;
|
|
5113
|
-
const baseUrl = process.env.CTI_CODEX_BASE_URL || void 0;
|
|
5114
|
-
const CodexClass = this.sdk.Codex;
|
|
5115
|
-
this.codex = new CodexClass({
|
|
5116
|
-
...apiKey ? { apiKey } : {},
|
|
5117
|
-
...baseUrl ? { baseUrl } : {}
|
|
5118
|
-
});
|
|
5119
|
-
return { sdk: this.sdk, codex: this.codex };
|
|
5120
|
-
}
|
|
5121
|
-
streamChat(params) {
|
|
5122
|
-
const self = this;
|
|
5123
|
-
return new ReadableStream({
|
|
5124
|
-
start(controller) {
|
|
5125
|
-
(async () => {
|
|
5126
|
-
const tempFiles = [];
|
|
5127
|
-
try {
|
|
5128
|
-
const { codex } = await self.ensureSDK();
|
|
5129
|
-
const inMemoryThreadId = self.threadIds.get(params.sessionId);
|
|
5130
|
-
let savedThreadId = inMemoryThreadId || params.sdkSessionId || void 0;
|
|
5131
|
-
const approvalPolicy = toApprovalPolicy(params.permissionMode);
|
|
5132
|
-
const sandboxMode = normalizeSandboxMode(params.sandboxMode);
|
|
5133
|
-
const modelReasoningEffort = normalizeReasoningEffort(params.modelReasoningEffort);
|
|
5134
|
-
const threadOptions = {
|
|
5135
|
-
...params.forceModel && params.model ? { model: params.model } : {},
|
|
5136
|
-
...params.workingDirectory ? { workingDirectory: params.workingDirectory } : {},
|
|
5137
|
-
...shouldSkipGitRepoCheck() ? { skipGitRepoCheck: true } : {},
|
|
5138
|
-
sandboxMode,
|
|
5139
|
-
...modelReasoningEffort ? { modelReasoningEffort } : {},
|
|
5140
|
-
approvalPolicy
|
|
5141
|
-
};
|
|
5142
|
-
const imageFiles = params.files?.filter(
|
|
5143
|
-
(f) => f.type.startsWith("image/")
|
|
5144
|
-
) ?? [];
|
|
5145
|
-
let input;
|
|
5146
|
-
if (imageFiles.length > 0) {
|
|
5147
|
-
const parts = [
|
|
5148
|
-
{ type: "text", text: params.prompt }
|
|
5149
|
-
];
|
|
5150
|
-
for (const file of imageFiles) {
|
|
5151
|
-
if (file.filePath && fs2.existsSync(file.filePath)) {
|
|
5152
|
-
parts.push({ type: "local_image", path: file.filePath });
|
|
5153
|
-
continue;
|
|
5154
|
-
}
|
|
5155
|
-
const ext = MIME_EXT[file.type] || ".png";
|
|
5156
|
-
const tmpPath = path2.join(os2.tmpdir(), `cti-img-${Date.now()}-${Math.random().toString(36).slice(2)}${ext}`);
|
|
5157
|
-
fs2.writeFileSync(tmpPath, Buffer.from(file.data, "base64"));
|
|
5158
|
-
tempFiles.push(tmpPath);
|
|
5159
|
-
parts.push({ type: "local_image", path: tmpPath });
|
|
5160
|
-
}
|
|
5161
|
-
input = parts;
|
|
5162
|
-
} else {
|
|
5163
|
-
input = params.prompt;
|
|
5164
|
-
}
|
|
5165
|
-
let retryFresh = false;
|
|
5166
|
-
while (true) {
|
|
5167
|
-
let thread;
|
|
5168
|
-
if (savedThreadId) {
|
|
5169
|
-
try {
|
|
5170
|
-
thread = codex.resumeThread(savedThreadId, threadOptions);
|
|
5171
|
-
} catch {
|
|
5172
|
-
thread = codex.startThread(threadOptions);
|
|
5173
|
-
}
|
|
5174
|
-
} else {
|
|
5175
|
-
thread = codex.startThread(threadOptions);
|
|
5176
|
-
}
|
|
5177
|
-
let sawAnyEvent = false;
|
|
5178
|
-
try {
|
|
5179
|
-
const { events } = await thread.runStreamed(input, {
|
|
5180
|
-
signal: params.abortController?.signal
|
|
5181
|
-
});
|
|
5182
|
-
for await (const event of events) {
|
|
5183
|
-
sawAnyEvent = true;
|
|
5184
|
-
if (params.abortController?.signal.aborted) {
|
|
5185
|
-
break;
|
|
5186
|
-
}
|
|
5187
|
-
switch (event.type) {
|
|
5188
|
-
case "thread.started": {
|
|
5189
|
-
const threadId = event.thread_id;
|
|
5190
|
-
self.threadIds.set(params.sessionId, threadId);
|
|
5191
|
-
controller.enqueue(sseEvent("status", {
|
|
5192
|
-
session_id: threadId
|
|
5193
|
-
}));
|
|
5194
|
-
break;
|
|
5195
|
-
}
|
|
5196
|
-
case "item.completed": {
|
|
5197
|
-
const item = event.item;
|
|
5198
|
-
self.handleCompletedItem(controller, item);
|
|
5199
|
-
break;
|
|
5200
|
-
}
|
|
5201
|
-
case "turn.completed": {
|
|
5202
|
-
const usage = event.usage;
|
|
5203
|
-
const threadId = self.threadIds.get(params.sessionId);
|
|
5204
|
-
controller.enqueue(sseEvent("result", {
|
|
5205
|
-
usage: usage ? {
|
|
5206
|
-
input_tokens: usage.input_tokens ?? 0,
|
|
5207
|
-
output_tokens: usage.output_tokens ?? 0,
|
|
5208
|
-
cache_read_input_tokens: usage.cached_input_tokens ?? 0
|
|
5209
|
-
} : void 0,
|
|
5210
|
-
...threadId ? { session_id: threadId } : {}
|
|
5211
|
-
}));
|
|
5212
|
-
break;
|
|
5213
|
-
}
|
|
5214
|
-
case "turn.failed": {
|
|
5215
|
-
const error = event.message;
|
|
5216
|
-
controller.enqueue(sseEvent("error", error || "Turn failed"));
|
|
5217
|
-
break;
|
|
5218
|
-
}
|
|
5219
|
-
case "error": {
|
|
5220
|
-
const error = event.message;
|
|
5221
|
-
controller.enqueue(sseEvent("error", error || "Thread error"));
|
|
5222
|
-
break;
|
|
5223
|
-
}
|
|
5224
|
-
}
|
|
5225
|
-
}
|
|
5226
|
-
break;
|
|
5227
|
-
} catch (err) {
|
|
5228
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
5229
|
-
if (savedThreadId && !retryFresh && !sawAnyEvent && shouldRetryFreshThread(message)) {
|
|
5230
|
-
console.warn("[codex-provider] Resume failed, retrying with a fresh thread:", message);
|
|
5231
|
-
savedThreadId = void 0;
|
|
5232
|
-
retryFresh = true;
|
|
5233
|
-
continue;
|
|
5234
|
-
}
|
|
5235
|
-
throw err;
|
|
5236
|
-
}
|
|
5237
|
-
}
|
|
5238
|
-
controller.close();
|
|
5239
|
-
} catch (err) {
|
|
5240
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
5241
|
-
console.error("[codex-provider] Error:", err instanceof Error ? err.stack || err.message : err);
|
|
5242
|
-
try {
|
|
5243
|
-
controller.enqueue(sseEvent("error", message));
|
|
5244
|
-
controller.close();
|
|
5245
|
-
} catch {
|
|
5246
|
-
}
|
|
5247
|
-
} finally {
|
|
5248
|
-
for (const tmp of tempFiles) {
|
|
5249
|
-
try {
|
|
5250
|
-
fs2.unlinkSync(tmp);
|
|
5251
|
-
} catch {
|
|
5252
|
-
}
|
|
5253
|
-
}
|
|
5254
|
-
}
|
|
5255
|
-
})();
|
|
5256
|
-
}
|
|
5257
|
-
});
|
|
5258
|
-
}
|
|
5259
|
-
/**
|
|
5260
|
-
* Map a completed Codex item to SSE events.
|
|
5261
|
-
*/
|
|
5262
|
-
handleCompletedItem(controller, item) {
|
|
5263
|
-
const itemType = item.type;
|
|
5264
|
-
switch (itemType) {
|
|
5265
|
-
case "agent_message": {
|
|
5266
|
-
const text2 = item.text || "";
|
|
5267
|
-
if (text2) {
|
|
5268
|
-
controller.enqueue(sseEvent("text", text2));
|
|
5269
|
-
}
|
|
5270
|
-
break;
|
|
5271
|
-
}
|
|
5272
|
-
case "command_execution": {
|
|
5273
|
-
const toolId = item.id || `tool-${Date.now()}`;
|
|
5274
|
-
const command = item.command || "";
|
|
5275
|
-
const output = item.aggregated_output || "";
|
|
5276
|
-
const exitCode = item.exit_code;
|
|
5277
|
-
const isError = exitCode != null && exitCode !== 0;
|
|
5278
|
-
controller.enqueue(sseEvent("tool_use", {
|
|
5279
|
-
id: toolId,
|
|
5280
|
-
name: "Bash",
|
|
5281
|
-
input: { command }
|
|
5282
|
-
}));
|
|
5283
|
-
const resultContent = output || (isError ? `Exit code: ${exitCode}` : "Done");
|
|
5284
|
-
controller.enqueue(sseEvent("tool_result", {
|
|
5285
|
-
tool_use_id: toolId,
|
|
5286
|
-
content: resultContent,
|
|
5287
|
-
is_error: isError
|
|
5288
|
-
}));
|
|
5289
|
-
break;
|
|
5290
|
-
}
|
|
5291
|
-
case "file_change": {
|
|
5292
|
-
const toolId = item.id || `tool-${Date.now()}`;
|
|
5293
|
-
const changes = item.changes || [];
|
|
5294
|
-
const summary = changes.map((c) => `${c.kind}: ${c.path}`).join("\n");
|
|
5295
|
-
controller.enqueue(sseEvent("tool_use", {
|
|
5296
|
-
id: toolId,
|
|
5297
|
-
name: "Edit",
|
|
5298
|
-
input: { files: changes }
|
|
5299
|
-
}));
|
|
5300
|
-
controller.enqueue(sseEvent("tool_result", {
|
|
5301
|
-
tool_use_id: toolId,
|
|
5302
|
-
content: summary || "File changes applied",
|
|
5303
|
-
is_error: false
|
|
5304
|
-
}));
|
|
5305
|
-
break;
|
|
5306
|
-
}
|
|
5307
|
-
case "mcp_tool_call": {
|
|
5308
|
-
const toolId = item.id || `tool-${Date.now()}`;
|
|
5309
|
-
const server2 = item.server || "";
|
|
5310
|
-
const tool = item.tool || "";
|
|
5311
|
-
const args = item.arguments;
|
|
5312
|
-
const result = item.result;
|
|
5313
|
-
const error = item.error;
|
|
5314
|
-
const resultContent = result?.content ?? result?.structured_content;
|
|
5315
|
-
const resultText = typeof resultContent === "string" ? resultContent : resultContent ? JSON.stringify(resultContent) : void 0;
|
|
5316
|
-
controller.enqueue(sseEvent("tool_use", {
|
|
5317
|
-
id: toolId,
|
|
5318
|
-
name: `mcp__${server2}__${tool}`,
|
|
5319
|
-
input: args
|
|
5320
|
-
}));
|
|
5321
|
-
controller.enqueue(sseEvent("tool_result", {
|
|
5322
|
-
tool_use_id: toolId,
|
|
5323
|
-
content: error?.message || resultText || "Done",
|
|
5324
|
-
is_error: !!error
|
|
5325
|
-
}));
|
|
5326
|
-
break;
|
|
5327
|
-
}
|
|
5328
|
-
case "reasoning": {
|
|
5329
|
-
const text2 = item.text || "";
|
|
5330
|
-
if (text2) {
|
|
5331
|
-
controller.enqueue(sseEvent("status", { reasoning: text2 }));
|
|
5332
|
-
}
|
|
5333
|
-
break;
|
|
5334
|
-
}
|
|
5335
|
-
}
|
|
5336
|
-
}
|
|
5337
|
-
};
|
|
5338
|
-
|
|
5339
|
-
// src/desktop-sessions.ts
|
|
5340
|
-
import fs3 from "node:fs";
|
|
5341
|
-
import os3 from "node:os";
|
|
5342
|
-
import path3 from "node:path";
|
|
5343
4996
|
import crypto from "node:crypto";
|
|
5344
4997
|
import { DatabaseSync } from "node:sqlite";
|
|
5345
4998
|
var ACTIVE_WINDOW_MS = 15 * 60 * 1e3;
|
|
@@ -5347,31 +5000,31 @@ var MAX_SESSION_META_BYTES = 4 * 1024 * 1024;
|
|
|
5347
5000
|
var MAX_SESSION_TITLE_SCAN_BYTES = 512 * 1024;
|
|
5348
5001
|
var TITLE_MAX_CHARS = 72;
|
|
5349
5002
|
function getCodexHome() {
|
|
5350
|
-
return process.env.CODEX_HOME ||
|
|
5003
|
+
return process.env.CODEX_HOME || path2.join(os2.homedir(), ".codex");
|
|
5351
5004
|
}
|
|
5352
5005
|
function getCodexSessionsRoot() {
|
|
5353
|
-
return
|
|
5006
|
+
return path2.join(getCodexHome(), "sessions");
|
|
5354
5007
|
}
|
|
5355
5008
|
function getArchivedSessionsRoot() {
|
|
5356
|
-
return
|
|
5009
|
+
return path2.join(getCodexHome(), "archived_sessions");
|
|
5357
5010
|
}
|
|
5358
5011
|
function getSessionIndexPath() {
|
|
5359
|
-
return
|
|
5012
|
+
return path2.join(getCodexHome(), "session_index.jsonl");
|
|
5360
5013
|
}
|
|
5361
5014
|
function getCodexGlobalStatePath() {
|
|
5362
|
-
return
|
|
5015
|
+
return path2.join(getCodexHome(), ".codex-global-state.json");
|
|
5363
5016
|
}
|
|
5364
5017
|
function getDesktopStateDbPath() {
|
|
5365
5018
|
const codexHome = getCodexHome();
|
|
5366
5019
|
let entries;
|
|
5367
5020
|
try {
|
|
5368
|
-
entries =
|
|
5021
|
+
entries = fs2.readdirSync(codexHome, { withFileTypes: true });
|
|
5369
5022
|
} catch {
|
|
5370
5023
|
return null;
|
|
5371
5024
|
}
|
|
5372
|
-
const candidates = entries.filter((entry) => entry.isFile() && /^state_\d+\.sqlite$/i.test(entry.name)).map((entry) =>
|
|
5025
|
+
const candidates = entries.filter((entry) => entry.isFile() && /^state_\d+\.sqlite$/i.test(entry.name)).map((entry) => path2.join(codexHome, entry.name)).sort((left, right) => {
|
|
5373
5026
|
try {
|
|
5374
|
-
return
|
|
5027
|
+
return fs2.statSync(right).mtimeMs - fs2.statSync(left).mtimeMs;
|
|
5375
5028
|
} catch {
|
|
5376
5029
|
return 0;
|
|
5377
5030
|
}
|
|
@@ -5385,21 +5038,21 @@ function extractThreadIdFromRolloutName(name) {
|
|
|
5385
5038
|
function normalizeComparablePath(value) {
|
|
5386
5039
|
if (!value) return "";
|
|
5387
5040
|
const stripped = value.replace(/^\\\\\?\\/, "");
|
|
5388
|
-
return
|
|
5041
|
+
return path2.resolve(stripped).replace(/[\\/]+$/, "").toLowerCase();
|
|
5389
5042
|
}
|
|
5390
5043
|
function isInternalSkillWorkspace(cwd) {
|
|
5391
5044
|
const normalizedCwd = normalizeComparablePath(cwd);
|
|
5392
5045
|
if (!normalizedCwd) return false;
|
|
5393
|
-
const skillsRoot = normalizeComparablePath(
|
|
5046
|
+
const skillsRoot = normalizeComparablePath(path2.join(getCodexHome(), "skills"));
|
|
5394
5047
|
if (!skillsRoot) return false;
|
|
5395
5048
|
return normalizedCwd === skillsRoot || normalizedCwd.startsWith(`${skillsRoot}\\`) || normalizedCwd.startsWith(`${skillsRoot}/`);
|
|
5396
5049
|
}
|
|
5397
5050
|
function loadSavedWorkspaceRoots() {
|
|
5398
5051
|
const statePath = getCodexGlobalStatePath();
|
|
5399
|
-
if (!
|
|
5052
|
+
if (!fs2.existsSync(statePath)) return null;
|
|
5400
5053
|
let parsed;
|
|
5401
5054
|
try {
|
|
5402
|
-
parsed = JSON.parse(
|
|
5055
|
+
parsed = JSON.parse(fs2.readFileSync(statePath, "utf-8"));
|
|
5403
5056
|
} catch {
|
|
5404
5057
|
return null;
|
|
5405
5058
|
}
|
|
@@ -5414,10 +5067,10 @@ function isWithinSavedWorkspaceRoots(cwd, roots) {
|
|
|
5414
5067
|
}
|
|
5415
5068
|
function loadArchivedThreadIds() {
|
|
5416
5069
|
const archivedRoot = getArchivedSessionsRoot();
|
|
5417
|
-
if (!
|
|
5070
|
+
if (!fs2.existsSync(archivedRoot)) return /* @__PURE__ */ new Set();
|
|
5418
5071
|
let entries;
|
|
5419
5072
|
try {
|
|
5420
|
-
entries =
|
|
5073
|
+
entries = fs2.readdirSync(archivedRoot, { withFileTypes: true });
|
|
5421
5074
|
} catch {
|
|
5422
5075
|
return /* @__PURE__ */ new Set();
|
|
5423
5076
|
}
|
|
@@ -5430,13 +5083,13 @@ function loadArchivedThreadIds() {
|
|
|
5430
5083
|
return ids;
|
|
5431
5084
|
}
|
|
5432
5085
|
function readFirstLine(filePath, maxBytes = MAX_SESSION_META_BYTES) {
|
|
5433
|
-
const fd =
|
|
5086
|
+
const fd = fs2.openSync(filePath, "r");
|
|
5434
5087
|
try {
|
|
5435
5088
|
const chunks = [];
|
|
5436
5089
|
let bytesReadTotal = 0;
|
|
5437
5090
|
const buffer = Buffer.alloc(4096);
|
|
5438
5091
|
while (bytesReadTotal < maxBytes) {
|
|
5439
|
-
const bytesRead =
|
|
5092
|
+
const bytesRead = fs2.readSync(fd, buffer, 0, buffer.length, bytesReadTotal);
|
|
5440
5093
|
if (bytesRead <= 0) break;
|
|
5441
5094
|
const slice = Buffer.from(buffer.subarray(0, bytesRead));
|
|
5442
5095
|
chunks.push(slice);
|
|
@@ -5449,36 +5102,36 @@ function readFirstLine(filePath, maxBytes = MAX_SESSION_META_BYTES) {
|
|
|
5449
5102
|
}
|
|
5450
5103
|
return Buffer.concat(chunks).toString("utf-8").split(/\r?\n/, 1)[0] || "";
|
|
5451
5104
|
} finally {
|
|
5452
|
-
|
|
5105
|
+
fs2.closeSync(fd);
|
|
5453
5106
|
}
|
|
5454
5107
|
}
|
|
5455
5108
|
function readFilePrefix(filePath, maxBytes = MAX_SESSION_TITLE_SCAN_BYTES) {
|
|
5456
|
-
const fd =
|
|
5109
|
+
const fd = fs2.openSync(filePath, "r");
|
|
5457
5110
|
try {
|
|
5458
5111
|
const buffer = Buffer.alloc(Math.min(maxBytes, 64 * 1024));
|
|
5459
5112
|
const chunks = [];
|
|
5460
5113
|
let offset = 0;
|
|
5461
5114
|
while (offset < maxBytes) {
|
|
5462
5115
|
const bytesToRead = Math.min(buffer.length, maxBytes - offset);
|
|
5463
|
-
const bytesRead =
|
|
5116
|
+
const bytesRead = fs2.readSync(fd, buffer, 0, bytesToRead, offset);
|
|
5464
5117
|
if (bytesRead <= 0) break;
|
|
5465
5118
|
chunks.push(Buffer.from(buffer.subarray(0, bytesRead)));
|
|
5466
5119
|
offset += bytesRead;
|
|
5467
5120
|
}
|
|
5468
5121
|
return Buffer.concat(chunks).toString("utf-8");
|
|
5469
5122
|
} finally {
|
|
5470
|
-
|
|
5123
|
+
fs2.closeSync(fd);
|
|
5471
5124
|
}
|
|
5472
5125
|
}
|
|
5473
5126
|
function walkSessionFiles(dirPath, target) {
|
|
5474
5127
|
let entries;
|
|
5475
5128
|
try {
|
|
5476
|
-
entries =
|
|
5129
|
+
entries = fs2.readdirSync(dirPath, { withFileTypes: true });
|
|
5477
5130
|
} catch {
|
|
5478
5131
|
return;
|
|
5479
5132
|
}
|
|
5480
5133
|
for (const entry of entries) {
|
|
5481
|
-
const entryPath =
|
|
5134
|
+
const entryPath = path2.join(dirPath, entry.name);
|
|
5482
5135
|
if (entry.isDirectory()) {
|
|
5483
5136
|
walkSessionFiles(entryPath, target);
|
|
5484
5137
|
continue;
|
|
@@ -5496,10 +5149,10 @@ function isDesktopLike(meta) {
|
|
|
5496
5149
|
}
|
|
5497
5150
|
function loadThreadIndexEntries(archivedThreadIds) {
|
|
5498
5151
|
const indexPath = getSessionIndexPath();
|
|
5499
|
-
if (!
|
|
5152
|
+
if (!fs2.existsSync(indexPath)) return /* @__PURE__ */ new Map();
|
|
5500
5153
|
let content = "";
|
|
5501
5154
|
try {
|
|
5502
|
-
content =
|
|
5155
|
+
content = fs2.readFileSync(indexPath, "utf-8");
|
|
5503
5156
|
} catch {
|
|
5504
5157
|
return /* @__PURE__ */ new Map();
|
|
5505
5158
|
}
|
|
@@ -5539,7 +5192,7 @@ function parseUpdatedAtValue(value) {
|
|
|
5539
5192
|
}
|
|
5540
5193
|
function loadVisibleDesktopThreads(limit) {
|
|
5541
5194
|
const dbPath = getDesktopStateDbPath();
|
|
5542
|
-
if (!dbPath || !
|
|
5195
|
+
if (!dbPath || !fs2.existsSync(dbPath)) return null;
|
|
5543
5196
|
let db = null;
|
|
5544
5197
|
try {
|
|
5545
5198
|
db = new DatabaseSync(dbPath, { readOnly: true });
|
|
@@ -5585,7 +5238,7 @@ function buildFallbackTitle(threadId, filePath, cwd) {
|
|
|
5585
5238
|
}
|
|
5586
5239
|
} catch {
|
|
5587
5240
|
}
|
|
5588
|
-
const dirName = trimTitle(
|
|
5241
|
+
const dirName = trimTitle(path2.basename(cwd || ""));
|
|
5589
5242
|
if (dirName) return dirName;
|
|
5590
5243
|
return `Session ${threadId.slice(0, 8)}`;
|
|
5591
5244
|
}
|
|
@@ -5606,7 +5259,7 @@ function parseDesktopSession(filePath, threadIndexEntries, archivedThreadIds) {
|
|
|
5606
5259
|
}
|
|
5607
5260
|
let stat;
|
|
5608
5261
|
try {
|
|
5609
|
-
stat =
|
|
5262
|
+
stat = fs2.statSync(filePath);
|
|
5610
5263
|
} catch {
|
|
5611
5264
|
return null;
|
|
5612
5265
|
}
|
|
@@ -5681,7 +5334,7 @@ function isSessionEventLine(line) {
|
|
|
5681
5334
|
}
|
|
5682
5335
|
function listDesktopSessions(limit) {
|
|
5683
5336
|
const root = getCodexSessionsRoot();
|
|
5684
|
-
if (!
|
|
5337
|
+
if (!fs2.existsSync(root)) return [];
|
|
5685
5338
|
const archivedThreadIds = loadArchivedThreadIds();
|
|
5686
5339
|
const threadIndexEntries = loadThreadIndexEntries(archivedThreadIds);
|
|
5687
5340
|
const savedWorkspaceRoots = loadSavedWorkspaceRoots();
|
|
@@ -5728,7 +5381,7 @@ function isArchivedDesktopThread(threadId) {
|
|
|
5728
5381
|
}
|
|
5729
5382
|
|
|
5730
5383
|
// src/session-bindings.ts
|
|
5731
|
-
import
|
|
5384
|
+
import path3 from "node:path";
|
|
5732
5385
|
function asChannelProvider(value) {
|
|
5733
5386
|
return value === "feishu" || value === "weixin" ? value : void 0;
|
|
5734
5387
|
}
|
|
@@ -5774,13 +5427,13 @@ function getSessionName(session) {
|
|
|
5774
5427
|
if (session.session_type === "draft") return "\u4E34\u65F6\u8349\u7A3F\u7EBF\u7A0B";
|
|
5775
5428
|
if (session.session_type === "history_summary") return "\u5386\u53F2\u6458\u8981\u7EBF\u7A0B";
|
|
5776
5429
|
if (session.name?.trim()) return session.name.trim();
|
|
5777
|
-
if (session.working_directory) return
|
|
5430
|
+
if (session.working_directory) return path3.basename(session.working_directory);
|
|
5778
5431
|
return session.id.slice(0, 8);
|
|
5779
5432
|
}
|
|
5780
5433
|
function getSessionMode(store, session) {
|
|
5781
5434
|
return session.preferred_mode || store.getSetting("bridge_default_mode") || "code";
|
|
5782
5435
|
}
|
|
5783
|
-
function bindStoreToSession(store, channelType, chatId, sessionId) {
|
|
5436
|
+
function bindStoreToSession(store, channelType, chatId, sessionId, chatMeta) {
|
|
5784
5437
|
const session = store.getSession(sessionId);
|
|
5785
5438
|
if (!session) return null;
|
|
5786
5439
|
assertBindingTargetAvailable(
|
|
@@ -5794,6 +5447,8 @@ function bindStoreToSession(store, channelType, chatId, sessionId) {
|
|
|
5794
5447
|
channelProvider: meta.provider,
|
|
5795
5448
|
channelAlias: meta.alias,
|
|
5796
5449
|
chatId,
|
|
5450
|
+
chatUserId: chatMeta?.chatUserId,
|
|
5451
|
+
chatDisplayName: chatMeta?.chatDisplayName,
|
|
5797
5452
|
codepilotSessionId: session.id,
|
|
5798
5453
|
sdkSessionId: session.sdk_session_id || "",
|
|
5799
5454
|
workingDirectory: session.working_directory,
|
|
@@ -5815,6 +5470,8 @@ function bindStoreToSdkSession(store, channelType, chatId, sdkSessionId, opts) {
|
|
|
5815
5470
|
channelProvider: meta.provider,
|
|
5816
5471
|
channelAlias: meta.alias,
|
|
5817
5472
|
chatId,
|
|
5473
|
+
chatUserId: opts?.chatUserId,
|
|
5474
|
+
chatDisplayName: opts?.chatDisplayName,
|
|
5818
5475
|
codepilotSessionId: existing.id,
|
|
5819
5476
|
sdkSessionId,
|
|
5820
5477
|
workingDirectory: opts?.workingDirectory || existing.working_directory,
|
|
@@ -5824,7 +5481,7 @@ function bindStoreToSdkSession(store, channelType, chatId, sdkSessionId, opts) {
|
|
|
5824
5481
|
}
|
|
5825
5482
|
const workingDirectory = opts?.workingDirectory || "";
|
|
5826
5483
|
const model = opts?.model || store.getSetting("bridge_default_model") || "";
|
|
5827
|
-
const baseName = opts?.displayName || (workingDirectory ?
|
|
5484
|
+
const baseName = opts?.displayName || (workingDirectory ? path3.basename(workingDirectory) : sdkSessionId.slice(0, 8));
|
|
5828
5485
|
const session = store.createSession(
|
|
5829
5486
|
`Desktop: ${baseName}`,
|
|
5830
5487
|
model,
|
|
@@ -5838,6 +5495,8 @@ function bindStoreToSdkSession(store, channelType, chatId, sdkSessionId, opts) {
|
|
|
5838
5495
|
channelProvider: meta.provider,
|
|
5839
5496
|
channelAlias: meta.alias,
|
|
5840
5497
|
chatId,
|
|
5498
|
+
chatUserId: opts?.chatUserId,
|
|
5499
|
+
chatDisplayName: opts?.chatDisplayName,
|
|
5841
5500
|
codepilotSessionId: session.id,
|
|
5842
5501
|
sdkSessionId,
|
|
5843
5502
|
workingDirectory: workingDirectory || session.working_directory,
|
|
@@ -5906,13 +5565,20 @@ function updateBindingTarget(store, bindingId, targetKey) {
|
|
|
5906
5565
|
const desktop = getDesktopSessionByThreadId(threadId);
|
|
5907
5566
|
bindStoreToSdkSession(store, binding.channelType, binding.chatId, threadId, desktop ? {
|
|
5908
5567
|
workingDirectory: desktop.cwd,
|
|
5909
|
-
displayName: desktop.title
|
|
5568
|
+
displayName: desktop.title,
|
|
5569
|
+
chatUserId: binding.chatUserId,
|
|
5570
|
+
chatDisplayName: binding.chatDisplayName
|
|
5910
5571
|
} : {
|
|
5911
|
-
workingDirectory: binding.workingDirectory
|
|
5572
|
+
workingDirectory: binding.workingDirectory,
|
|
5573
|
+
chatUserId: binding.chatUserId,
|
|
5574
|
+
chatDisplayName: binding.chatDisplayName
|
|
5912
5575
|
});
|
|
5913
5576
|
} else if (targetKey.startsWith("session:")) {
|
|
5914
5577
|
const sessionId = targetKey.slice("session:".length);
|
|
5915
|
-
const updated2 = bindStoreToSession(store, binding.channelType, binding.chatId, sessionId
|
|
5578
|
+
const updated2 = bindStoreToSession(store, binding.channelType, binding.chatId, sessionId, {
|
|
5579
|
+
chatUserId: binding.chatUserId,
|
|
5580
|
+
chatDisplayName: binding.chatDisplayName
|
|
5581
|
+
});
|
|
5916
5582
|
if (!updated2) {
|
|
5917
5583
|
throw new Error("Session not found.");
|
|
5918
5584
|
}
|
|
@@ -5934,37 +5600,37 @@ function removeBinding(store, bindingId) {
|
|
|
5934
5600
|
}
|
|
5935
5601
|
|
|
5936
5602
|
// src/service-manager.ts
|
|
5937
|
-
import
|
|
5938
|
-
import
|
|
5939
|
-
import
|
|
5603
|
+
import fs3 from "node:fs";
|
|
5604
|
+
import os3 from "node:os";
|
|
5605
|
+
import path4 from "node:path";
|
|
5940
5606
|
import { spawn } from "node:child_process";
|
|
5941
5607
|
import { fileURLToPath } from "node:url";
|
|
5942
|
-
var moduleDir =
|
|
5943
|
-
var packageRoot =
|
|
5944
|
-
var runtimeDir =
|
|
5945
|
-
var logsDir =
|
|
5946
|
-
var bridgePidFile =
|
|
5947
|
-
var bridgeStatusFile =
|
|
5948
|
-
var uiStatusFile =
|
|
5608
|
+
var moduleDir = path4.dirname(fileURLToPath(import.meta.url));
|
|
5609
|
+
var packageRoot = path4.resolve(moduleDir, "..");
|
|
5610
|
+
var runtimeDir = path4.join(CTI_HOME, "runtime");
|
|
5611
|
+
var logsDir = path4.join(CTI_HOME, "logs");
|
|
5612
|
+
var bridgePidFile = path4.join(runtimeDir, "bridge.pid");
|
|
5613
|
+
var bridgeStatusFile = path4.join(runtimeDir, "status.json");
|
|
5614
|
+
var uiStatusFile = path4.join(runtimeDir, "ui-server.json");
|
|
5949
5615
|
var uiPort = 4781;
|
|
5950
5616
|
var bridgeAutostartTaskName = "CodexToIMBridge";
|
|
5951
|
-
var bridgeAutostartLauncherFile =
|
|
5952
|
-
var npmUninstallLogFile =
|
|
5617
|
+
var bridgeAutostartLauncherFile = path4.join(runtimeDir, "bridge-autostart.ps1");
|
|
5618
|
+
var npmUninstallLogFile = path4.join(runtimeDir, "npm-uninstall.log");
|
|
5953
5619
|
var WINDOWS_HIDE = process.platform === "win32" ? { windowsHide: true } : {};
|
|
5954
5620
|
function ensureDirs() {
|
|
5955
|
-
|
|
5956
|
-
|
|
5621
|
+
fs3.mkdirSync(runtimeDir, { recursive: true });
|
|
5622
|
+
fs3.mkdirSync(logsDir, { recursive: true });
|
|
5957
5623
|
}
|
|
5958
5624
|
function readJsonFile(filePath, fallback) {
|
|
5959
5625
|
try {
|
|
5960
|
-
return JSON.parse(
|
|
5626
|
+
return JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
5961
5627
|
} catch {
|
|
5962
5628
|
return fallback;
|
|
5963
5629
|
}
|
|
5964
5630
|
}
|
|
5965
5631
|
function readPid(filePath) {
|
|
5966
5632
|
try {
|
|
5967
|
-
const raw =
|
|
5633
|
+
const raw = fs3.readFileSync(filePath, "utf-8").trim();
|
|
5968
5634
|
const pid = Number(raw);
|
|
5969
5635
|
return Number.isFinite(pid) ? pid : void 0;
|
|
5970
5636
|
} catch {
|
|
@@ -5984,7 +5650,7 @@ function sleep(ms) {
|
|
|
5984
5650
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5985
5651
|
}
|
|
5986
5652
|
function getCurrentWindowsUser() {
|
|
5987
|
-
const user = process.env.USERNAME ||
|
|
5653
|
+
const user = process.env.USERNAME || os3.userInfo().username;
|
|
5988
5654
|
const domain = process.env.USERDOMAIN;
|
|
5989
5655
|
return domain ? `${domain}\\${user}` : user;
|
|
5990
5656
|
}
|
|
@@ -6085,12 +5751,12 @@ async function startBridge() {
|
|
|
6085
5751
|
ensureDirs();
|
|
6086
5752
|
const current = getBridgeStatus();
|
|
6087
5753
|
if (current.running) return current;
|
|
6088
|
-
const daemonEntry =
|
|
6089
|
-
if (!
|
|
5754
|
+
const daemonEntry = path4.join(packageRoot, "dist", "daemon.mjs");
|
|
5755
|
+
if (!fs3.existsSync(daemonEntry)) {
|
|
6090
5756
|
throw new Error(`Daemon bundle not found at ${daemonEntry}. Run npm run build first.`);
|
|
6091
5757
|
}
|
|
6092
|
-
const stdoutFd =
|
|
6093
|
-
const stderrFd =
|
|
5758
|
+
const stdoutFd = fs3.openSync(path4.join(logsDir, "bridge-launcher.out.log"), "a");
|
|
5759
|
+
const stderrFd = fs3.openSync(path4.join(logsDir, "bridge-launcher.err.log"), "a");
|
|
6094
5760
|
const child = spawn(process.execPath, [daemonEntry], {
|
|
6095
5761
|
cwd: packageRoot,
|
|
6096
5762
|
detached: true,
|
|
@@ -6193,37 +5859,37 @@ async function getBridgeAutostartStatus() {
|
|
|
6193
5859
|
}
|
|
6194
5860
|
function getBridgeLogs(lines = 200) {
|
|
6195
5861
|
ensureDirs();
|
|
6196
|
-
const filePath =
|
|
6197
|
-
if (!
|
|
6198
|
-
const all =
|
|
5862
|
+
const filePath = path4.join(logsDir, "bridge.log");
|
|
5863
|
+
if (!fs3.existsSync(filePath)) return "";
|
|
5864
|
+
const all = fs3.readFileSync(filePath, "utf-8").split(/\r?\n/);
|
|
6199
5865
|
return all.slice(Math.max(0, all.length - lines)).join("\n");
|
|
6200
5866
|
}
|
|
6201
5867
|
function writeUiServerStatus(status) {
|
|
6202
5868
|
ensureDirs();
|
|
6203
|
-
|
|
5869
|
+
fs3.writeFileSync(uiStatusFile, JSON.stringify(status, null, 2), "utf-8");
|
|
6204
5870
|
}
|
|
6205
5871
|
async function installCodexIntegration() {
|
|
6206
|
-
const sourceSkill =
|
|
6207
|
-
if (!
|
|
5872
|
+
const sourceSkill = path4.join(packageRoot, "SKILL.md");
|
|
5873
|
+
if (!fs3.existsSync(sourceSkill)) {
|
|
6208
5874
|
throw new Error(`SKILL.md not found at ${sourceSkill}`);
|
|
6209
5875
|
}
|
|
6210
|
-
const skillsDir =
|
|
6211
|
-
const targetDir =
|
|
6212
|
-
|
|
6213
|
-
if (
|
|
5876
|
+
const skillsDir = path4.join(os3.homedir(), ".codex", "skills");
|
|
5877
|
+
const targetDir = path4.join(skillsDir, "codex-to-im");
|
|
5878
|
+
fs3.mkdirSync(skillsDir, { recursive: true });
|
|
5879
|
+
if (fs3.existsSync(targetDir)) {
|
|
6214
5880
|
return { targetDir, method: "existing" };
|
|
6215
5881
|
}
|
|
6216
5882
|
try {
|
|
6217
|
-
|
|
5883
|
+
fs3.symlinkSync(packageRoot, targetDir, process.platform === "win32" ? "junction" : "dir");
|
|
6218
5884
|
return { targetDir, method: "junction" };
|
|
6219
5885
|
} catch {
|
|
6220
|
-
|
|
5886
|
+
fs3.cpSync(packageRoot, targetDir, {
|
|
6221
5887
|
recursive: true,
|
|
6222
5888
|
filter: (source) => {
|
|
6223
|
-
const relative =
|
|
5889
|
+
const relative = path4.relative(packageRoot, source);
|
|
6224
5890
|
if (!relative) return true;
|
|
6225
|
-
if (relative === ".git" || relative.startsWith(`.git${
|
|
6226
|
-
if (relative === "node_modules" || relative.startsWith(`node_modules${
|
|
5891
|
+
if (relative === ".git" || relative.startsWith(`.git${path4.sep}`)) return false;
|
|
5892
|
+
if (relative === "node_modules" || relative.startsWith(`node_modules${path4.sep}`)) return false;
|
|
6227
5893
|
return true;
|
|
6228
5894
|
}
|
|
6229
5895
|
});
|
|
@@ -6231,27 +5897,27 @@ async function installCodexIntegration() {
|
|
|
6231
5897
|
}
|
|
6232
5898
|
}
|
|
6233
5899
|
function isCodexIntegrationInstalled() {
|
|
6234
|
-
const targetDir =
|
|
6235
|
-
return
|
|
5900
|
+
const targetDir = path4.join(os3.homedir(), ".codex", "skills", "codex-to-im");
|
|
5901
|
+
return fs3.existsSync(path4.join(targetDir, "SKILL.md"));
|
|
6236
5902
|
}
|
|
6237
5903
|
|
|
6238
5904
|
// src/store.ts
|
|
6239
|
-
import
|
|
6240
|
-
import
|
|
5905
|
+
import fs4 from "node:fs";
|
|
5906
|
+
import path5 from "node:path";
|
|
6241
5907
|
import crypto2 from "node:crypto";
|
|
6242
|
-
var DATA_DIR =
|
|
6243
|
-
var MESSAGES_DIR =
|
|
5908
|
+
var DATA_DIR = path5.join(CTI_HOME, "data");
|
|
5909
|
+
var MESSAGES_DIR = path5.join(DATA_DIR, "messages");
|
|
6244
5910
|
function ensureDir(dir) {
|
|
6245
|
-
|
|
5911
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
6246
5912
|
}
|
|
6247
5913
|
function atomicWrite(filePath, data) {
|
|
6248
5914
|
const tmp = filePath + ".tmp";
|
|
6249
|
-
|
|
6250
|
-
|
|
5915
|
+
fs4.writeFileSync(tmp, data, "utf-8");
|
|
5916
|
+
fs4.renameSync(tmp, filePath);
|
|
6251
5917
|
}
|
|
6252
5918
|
function readJson(filePath, fallback) {
|
|
6253
5919
|
try {
|
|
6254
|
-
const raw =
|
|
5920
|
+
const raw = fs4.readFileSync(filePath, "utf-8");
|
|
6255
5921
|
return JSON.parse(raw);
|
|
6256
5922
|
} catch {
|
|
6257
5923
|
return fallback;
|
|
@@ -6271,10 +5937,12 @@ function defaultAliasForProvider2(provider) {
|
|
|
6271
5937
|
if (provider === "weixin") return "\u5FAE\u4FE1";
|
|
6272
5938
|
return void 0;
|
|
6273
5939
|
}
|
|
6274
|
-
function
|
|
5940
|
+
function upgradeLegacyBinding(binding) {
|
|
6275
5941
|
const config = loadConfig();
|
|
6276
|
-
const
|
|
6277
|
-
const
|
|
5942
|
+
const exactInstance = findChannelInstance(binding.channelType, config);
|
|
5943
|
+
const hasInstanceMetadata = Boolean(binding.channelProvider || binding.channelAlias);
|
|
5944
|
+
const legacyProvider = !exactInstance && !hasInstanceMetadata && (binding.channelType === "feishu" || binding.channelType === "weixin") ? binding.channelType : void 0;
|
|
5945
|
+
const resolvedInstance = exactInstance || (legacyProvider ? (config.channels || []).find((channel) => channel.provider === legacyProvider) : void 0);
|
|
6278
5946
|
if (!resolvedInstance && !legacyProvider) {
|
|
6279
5947
|
return {
|
|
6280
5948
|
...binding,
|
|
@@ -6318,44 +5986,44 @@ var JsonFileStore = class {
|
|
|
6318
5986
|
this.reloadSessions();
|
|
6319
5987
|
this.reloadBindings();
|
|
6320
5988
|
const perms = readJson(
|
|
6321
|
-
|
|
5989
|
+
path5.join(DATA_DIR, "permissions.json"),
|
|
6322
5990
|
{}
|
|
6323
5991
|
);
|
|
6324
5992
|
for (const [id, p] of Object.entries(perms)) {
|
|
6325
5993
|
this.permissionLinks.set(id, p);
|
|
6326
5994
|
}
|
|
6327
5995
|
const offsets = readJson(
|
|
6328
|
-
|
|
5996
|
+
path5.join(DATA_DIR, "offsets.json"),
|
|
6329
5997
|
{}
|
|
6330
5998
|
);
|
|
6331
5999
|
for (const [k, v] of Object.entries(offsets)) {
|
|
6332
6000
|
this.offsets.set(k, v);
|
|
6333
6001
|
}
|
|
6334
6002
|
const dedup = readJson(
|
|
6335
|
-
|
|
6003
|
+
path5.join(DATA_DIR, "dedup.json"),
|
|
6336
6004
|
{}
|
|
6337
6005
|
);
|
|
6338
6006
|
for (const [k, v] of Object.entries(dedup)) {
|
|
6339
6007
|
this.dedupKeys.set(k, v);
|
|
6340
6008
|
}
|
|
6341
|
-
this.auditLog = readJson(
|
|
6009
|
+
this.auditLog = readJson(path5.join(DATA_DIR, "audit.json"), []);
|
|
6342
6010
|
}
|
|
6343
6011
|
reloadSessions() {
|
|
6344
6012
|
const sessions = readJson(
|
|
6345
|
-
|
|
6013
|
+
path5.join(DATA_DIR, "sessions.json"),
|
|
6346
6014
|
{}
|
|
6347
6015
|
);
|
|
6348
6016
|
this.sessions = new Map(Object.entries(sessions));
|
|
6349
6017
|
}
|
|
6350
6018
|
reloadBindings() {
|
|
6351
6019
|
const bindings = readJson(
|
|
6352
|
-
|
|
6020
|
+
path5.join(DATA_DIR, "bindings.json"),
|
|
6353
6021
|
{}
|
|
6354
6022
|
);
|
|
6355
6023
|
const normalized = /* @__PURE__ */ new Map();
|
|
6356
6024
|
let changed = false;
|
|
6357
6025
|
for (const binding of Object.values(bindings)) {
|
|
6358
|
-
const normalizedBinding =
|
|
6026
|
+
const normalizedBinding = upgradeLegacyBinding(binding);
|
|
6359
6027
|
if (didBindingChange(binding, normalizedBinding)) {
|
|
6360
6028
|
changed = true;
|
|
6361
6029
|
}
|
|
@@ -6368,47 +6036,47 @@ var JsonFileStore = class {
|
|
|
6368
6036
|
}
|
|
6369
6037
|
persistSessions() {
|
|
6370
6038
|
writeJson(
|
|
6371
|
-
|
|
6039
|
+
path5.join(DATA_DIR, "sessions.json"),
|
|
6372
6040
|
Object.fromEntries(this.sessions)
|
|
6373
6041
|
);
|
|
6374
6042
|
}
|
|
6375
6043
|
persistBindings() {
|
|
6376
6044
|
writeJson(
|
|
6377
|
-
|
|
6045
|
+
path5.join(DATA_DIR, "bindings.json"),
|
|
6378
6046
|
Object.fromEntries(this.bindings)
|
|
6379
6047
|
);
|
|
6380
6048
|
}
|
|
6381
6049
|
persistPermissions() {
|
|
6382
6050
|
writeJson(
|
|
6383
|
-
|
|
6051
|
+
path5.join(DATA_DIR, "permissions.json"),
|
|
6384
6052
|
Object.fromEntries(this.permissionLinks)
|
|
6385
6053
|
);
|
|
6386
6054
|
}
|
|
6387
6055
|
persistOffsets() {
|
|
6388
6056
|
writeJson(
|
|
6389
|
-
|
|
6057
|
+
path5.join(DATA_DIR, "offsets.json"),
|
|
6390
6058
|
Object.fromEntries(this.offsets)
|
|
6391
6059
|
);
|
|
6392
6060
|
}
|
|
6393
6061
|
persistDedup() {
|
|
6394
6062
|
writeJson(
|
|
6395
|
-
|
|
6063
|
+
path5.join(DATA_DIR, "dedup.json"),
|
|
6396
6064
|
Object.fromEntries(this.dedupKeys)
|
|
6397
6065
|
);
|
|
6398
6066
|
}
|
|
6399
6067
|
persistAudit() {
|
|
6400
|
-
writeJson(
|
|
6068
|
+
writeJson(path5.join(DATA_DIR, "audit.json"), this.auditLog);
|
|
6401
6069
|
}
|
|
6402
6070
|
persistMessages(sessionId) {
|
|
6403
6071
|
const msgs = this.messages.get(sessionId) || [];
|
|
6404
|
-
writeJson(
|
|
6072
|
+
writeJson(path5.join(MESSAGES_DIR, `${sessionId}.json`), msgs);
|
|
6405
6073
|
}
|
|
6406
6074
|
loadMessages(sessionId) {
|
|
6407
6075
|
if (this.messages.has(sessionId)) {
|
|
6408
6076
|
return this.messages.get(sessionId);
|
|
6409
6077
|
}
|
|
6410
6078
|
const msgs = readJson(
|
|
6411
|
-
|
|
6079
|
+
path5.join(MESSAGES_DIR, `${sessionId}.json`),
|
|
6412
6080
|
[]
|
|
6413
6081
|
);
|
|
6414
6082
|
this.messages.set(sessionId, msgs);
|
|
@@ -6576,7 +6244,7 @@ var JsonFileStore = class {
|
|
|
6576
6244
|
}
|
|
6577
6245
|
this.messages.delete(sessionId);
|
|
6578
6246
|
try {
|
|
6579
|
-
|
|
6247
|
+
fs4.rmSync(path5.join(MESSAGES_DIR, `${sessionId}.json`), { force: true });
|
|
6580
6248
|
} catch {
|
|
6581
6249
|
}
|
|
6582
6250
|
this.persistSessions();
|
|
@@ -6759,8 +6427,8 @@ var JsonFileStore = class {
|
|
|
6759
6427
|
|
|
6760
6428
|
// src/weixin-login.ts
|
|
6761
6429
|
var import_qrcode = __toESM(require_lib(), 1);
|
|
6762
|
-
import
|
|
6763
|
-
import
|
|
6430
|
+
import fs6 from "node:fs";
|
|
6431
|
+
import path7 from "node:path";
|
|
6764
6432
|
import { spawn as spawn2 } from "node:child_process";
|
|
6765
6433
|
|
|
6766
6434
|
// src/adapters/weixin/weixin-api.ts
|
|
@@ -6813,24 +6481,24 @@ async function pollLoginQrStatus(qrcode, baseUrl) {
|
|
|
6813
6481
|
}
|
|
6814
6482
|
|
|
6815
6483
|
// src/weixin-store.ts
|
|
6816
|
-
import
|
|
6817
|
-
import
|
|
6818
|
-
var DATA_DIR2 =
|
|
6819
|
-
var ACCOUNTS_PATH =
|
|
6820
|
-
var CONTEXT_TOKENS_PATH =
|
|
6484
|
+
import fs5 from "node:fs";
|
|
6485
|
+
import path6 from "node:path";
|
|
6486
|
+
var DATA_DIR2 = path6.join(CTI_HOME, "data");
|
|
6487
|
+
var ACCOUNTS_PATH = path6.join(DATA_DIR2, "weixin-accounts.json");
|
|
6488
|
+
var CONTEXT_TOKENS_PATH = path6.join(DATA_DIR2, "weixin-context-tokens.json");
|
|
6821
6489
|
var DEFAULT_BASE_URL2 = "https://ilinkai.weixin.qq.com";
|
|
6822
6490
|
var DEFAULT_CDN_BASE_URL2 = "https://novac2c.cdn.weixin.qq.com/c2c";
|
|
6823
6491
|
function ensureDir2(dir) {
|
|
6824
|
-
|
|
6492
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
6825
6493
|
}
|
|
6826
6494
|
function atomicWrite2(filePath, data) {
|
|
6827
6495
|
const tmpPath = `${filePath}.tmp`;
|
|
6828
|
-
|
|
6829
|
-
|
|
6496
|
+
fs5.writeFileSync(tmpPath, data, "utf-8");
|
|
6497
|
+
fs5.renameSync(tmpPath, filePath);
|
|
6830
6498
|
}
|
|
6831
6499
|
function readJson2(filePath, fallback) {
|
|
6832
6500
|
try {
|
|
6833
|
-
const raw =
|
|
6501
|
+
const raw = fs5.readFileSync(filePath, "utf-8");
|
|
6834
6502
|
return JSON.parse(raw);
|
|
6835
6503
|
} catch {
|
|
6836
6504
|
return fallback;
|
|
@@ -6942,10 +6610,10 @@ function deleteWeixinContextTokensByAccount(accountId) {
|
|
|
6942
6610
|
var MAX_REFRESHES = 3;
|
|
6943
6611
|
var QR_TTL_MS = 5 * 6e4;
|
|
6944
6612
|
var POLL_INTERVAL_MS = 3e3;
|
|
6945
|
-
var RUNTIME_DIR =
|
|
6946
|
-
var HTML_PATH =
|
|
6613
|
+
var RUNTIME_DIR = path7.join(CTI_HOME, "runtime");
|
|
6614
|
+
var HTML_PATH = path7.join(RUNTIME_DIR, "weixin-login.html");
|
|
6947
6615
|
function ensureRuntimeDir() {
|
|
6948
|
-
|
|
6616
|
+
fs6.mkdirSync(RUNTIME_DIR, { recursive: true });
|
|
6949
6617
|
}
|
|
6950
6618
|
function escapeHtml(text2) {
|
|
6951
6619
|
return text2.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
@@ -7045,7 +6713,7 @@ async function writeQrHtml(session) {
|
|
|
7045
6713
|
margin: 0,
|
|
7046
6714
|
width: 300
|
|
7047
6715
|
});
|
|
7048
|
-
|
|
6716
|
+
fs6.writeFileSync(HTML_PATH, buildQrHtml(session, qrSvg), "utf-8");
|
|
7049
6717
|
}
|
|
7050
6718
|
function openQrHtml() {
|
|
7051
6719
|
try {
|
|
@@ -7163,7 +6831,7 @@ async function runWeixinLogin(config = {}) {
|
|
|
7163
6831
|
}
|
|
7164
6832
|
var isMainModule = (() => {
|
|
7165
6833
|
const entry = process.argv[1];
|
|
7166
|
-
return !!entry &&
|
|
6834
|
+
return !!entry && path7.resolve(entry) === path7.resolve(new URL(import.meta.url).pathname);
|
|
7167
6835
|
})();
|
|
7168
6836
|
if (isMainModule) {
|
|
7169
6837
|
runWeixinLogin().catch((err) => {
|
|
@@ -7173,14 +6841,14 @@ if (isMainModule) {
|
|
|
7173
6841
|
}
|
|
7174
6842
|
|
|
7175
6843
|
// src/codex-models.ts
|
|
7176
|
-
import
|
|
7177
|
-
import
|
|
7178
|
-
import
|
|
7179
|
-
var DEFAULT_CODEX_CONFIG_PATH =
|
|
7180
|
-
var DEFAULT_CODEX_MODELS_CACHE_PATH =
|
|
6844
|
+
import fs7 from "node:fs";
|
|
6845
|
+
import os4 from "node:os";
|
|
6846
|
+
import path8 from "node:path";
|
|
6847
|
+
var DEFAULT_CODEX_CONFIG_PATH = path8.join(os4.homedir(), ".codex", "config.toml");
|
|
6848
|
+
var DEFAULT_CODEX_MODELS_CACHE_PATH = path8.join(os4.homedir(), ".codex", "models_cache.json");
|
|
7181
6849
|
function readConfiguredCodexModel(configPath = DEFAULT_CODEX_CONFIG_PATH) {
|
|
7182
6850
|
try {
|
|
7183
|
-
const raw =
|
|
6851
|
+
const raw = fs7.readFileSync(configPath, "utf-8");
|
|
7184
6852
|
let inSection = false;
|
|
7185
6853
|
for (const line of raw.split(/\r?\n/)) {
|
|
7186
6854
|
const trimmed = line.trim();
|
|
@@ -7202,7 +6870,7 @@ function readConfiguredCodexModel(configPath = DEFAULT_CODEX_CONFIG_PATH) {
|
|
|
7202
6870
|
}
|
|
7203
6871
|
function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
|
|
7204
6872
|
try {
|
|
7205
|
-
const raw =
|
|
6873
|
+
const raw = fs7.readFileSync(cachePath, "utf-8");
|
|
7206
6874
|
const parsed = JSON.parse(raw);
|
|
7207
6875
|
if (!Array.isArray(parsed.models)) return [];
|
|
7208
6876
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -7405,7 +7073,7 @@ function isLocalRequest(request) {
|
|
|
7405
7073
|
return isLoopbackAddress(getRemoteAddress(request));
|
|
7406
7074
|
}
|
|
7407
7075
|
function getLanUrls(currentPort) {
|
|
7408
|
-
const interfaces =
|
|
7076
|
+
const interfaces = os5.networkInterfaces();
|
|
7409
7077
|
const urls = /* @__PURE__ */ new Set();
|
|
7410
7078
|
for (const records of Object.values(interfaces)) {
|
|
7411
7079
|
for (const record of records || []) {
|
|
@@ -7713,53 +7381,6 @@ function deleteChannelInstance(current, channelId) {
|
|
|
7713
7381
|
enabledChannels: Array.from(new Set(nextChannels.filter((channel) => channel.enabled).map((channel) => channel.provider)))
|
|
7714
7382
|
};
|
|
7715
7383
|
}
|
|
7716
|
-
async function testCodexConnection(config) {
|
|
7717
|
-
const provider = new CodexProvider(new PendingPermissions());
|
|
7718
|
-
const abortController = new AbortController();
|
|
7719
|
-
const timeout = setTimeout(() => abortController.abort(), 3e4);
|
|
7720
|
-
const workingDirectory = config.defaultWorkspaceRoot || DEFAULT_WORKSPACE_ROOT;
|
|
7721
|
-
fs9.mkdirSync(workingDirectory, { recursive: true });
|
|
7722
|
-
try {
|
|
7723
|
-
const stream = provider.streamChat({
|
|
7724
|
-
prompt: "Reply with the single word OK.",
|
|
7725
|
-
sessionId: `ui-test-${Date.now()}`,
|
|
7726
|
-
workingDirectory,
|
|
7727
|
-
permissionMode: "plan",
|
|
7728
|
-
abortController
|
|
7729
|
-
});
|
|
7730
|
-
const reader = stream.getReader();
|
|
7731
|
-
let responseText = "";
|
|
7732
|
-
let raw = "";
|
|
7733
|
-
while (true) {
|
|
7734
|
-
const { done, value } = await reader.read();
|
|
7735
|
-
if (done) break;
|
|
7736
|
-
raw += value;
|
|
7737
|
-
const lines = value.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
7738
|
-
for (const line of lines) {
|
|
7739
|
-
if (!line.startsWith("data: ")) continue;
|
|
7740
|
-
const parsed = JSON.parse(line.slice(6));
|
|
7741
|
-
if (parsed.type === "text") {
|
|
7742
|
-
responseText += parsed.data;
|
|
7743
|
-
}
|
|
7744
|
-
if (parsed.type === "error") {
|
|
7745
|
-
return { ok: false, message: parsed.data, raw };
|
|
7746
|
-
}
|
|
7747
|
-
}
|
|
7748
|
-
}
|
|
7749
|
-
return {
|
|
7750
|
-
ok: true,
|
|
7751
|
-
message: responseText.trim() || "Codex SDK \u5DF2\u8FDE\u901A\uFF0C\u4F46\u6D4B\u8BD5\u6CA1\u6709\u8FD4\u56DE\u6587\u672C\u3002",
|
|
7752
|
-
raw
|
|
7753
|
-
};
|
|
7754
|
-
} catch (error) {
|
|
7755
|
-
return {
|
|
7756
|
-
ok: false,
|
|
7757
|
-
message: error instanceof Error ? error.message : String(error)
|
|
7758
|
-
};
|
|
7759
|
-
} finally {
|
|
7760
|
-
clearTimeout(timeout);
|
|
7761
|
-
}
|
|
7762
|
-
}
|
|
7763
7384
|
function renderLoginHtml() {
|
|
7764
7385
|
return `<!doctype html>
|
|
7765
7386
|
<html lang="zh-CN">
|
|
@@ -8067,7 +7688,7 @@ function renderHtml() {
|
|
|
8067
7688
|
|
|
8068
7689
|
.status-grid {
|
|
8069
7690
|
display: grid;
|
|
8070
|
-
grid-template-columns: repeat(
|
|
7691
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
8071
7692
|
gap: 16px;
|
|
8072
7693
|
margin-bottom: 20px;
|
|
8073
7694
|
}
|
|
@@ -8098,6 +7719,13 @@ function renderHtml() {
|
|
|
8098
7719
|
word-break: break-word;
|
|
8099
7720
|
}
|
|
8100
7721
|
|
|
7722
|
+
.status-meta {
|
|
7723
|
+
margin-top: 8px;
|
|
7724
|
+
color: var(--muted);
|
|
7725
|
+
font-size: 12px;
|
|
7726
|
+
line-height: 1.45;
|
|
7727
|
+
}
|
|
7728
|
+
|
|
8101
7729
|
.panel {
|
|
8102
7730
|
padding: 20px;
|
|
8103
7731
|
}
|
|
@@ -9074,33 +8702,30 @@ function renderHtml() {
|
|
|
9074
8702
|
|
|
9075
8703
|
<section class="status-grid">
|
|
9076
8704
|
<div class="status-card">
|
|
9077
|
-
<strong
|
|
8705
|
+
<strong>\u6865\u63A5\u670D\u52A1</strong>
|
|
9078
8706
|
<div class="status-value" id="bridgeStatus">-</div>
|
|
8707
|
+
<div class="status-meta" id="bridgeStatusMeta">-</div>
|
|
9079
8708
|
</div>
|
|
9080
8709
|
<div class="status-card">
|
|
9081
|
-
<strong
|
|
8710
|
+
<strong>\u6865\u63A5\u670D\u52A1\u5F00\u673A\u81EA\u542F\u52A8</strong>
|
|
9082
8711
|
<div class="status-value" id="autostartStatus">-</div>
|
|
9083
8712
|
</div>
|
|
9084
8713
|
<div class="status-card">
|
|
9085
|
-
<strong>Codex
|
|
8714
|
+
<strong>Codex \u6280\u80FD</strong>
|
|
9086
8715
|
<div class="status-value" id="integrationStatus">-</div>
|
|
9087
8716
|
</div>
|
|
9088
8717
|
<div class="status-card">
|
|
9089
|
-
<strong
|
|
8718
|
+
<strong>\u8FD0\u884C\u65F6</strong>
|
|
9090
8719
|
<div class="status-value" id="runtimeStatus">-</div>
|
|
9091
8720
|
</div>
|
|
9092
8721
|
<div class="status-card">
|
|
9093
|
-
<strong
|
|
8722
|
+
<strong>\u684C\u9762\u4F1A\u8BDD</strong>
|
|
9094
8723
|
<div class="status-value" id="desktopSessionCount">-</div>
|
|
9095
8724
|
</div>
|
|
9096
8725
|
<div class="status-card">
|
|
9097
|
-
<strong
|
|
8726
|
+
<strong>\u804A\u5929\u7ED1\u5B9A</strong>
|
|
9098
8727
|
<div class="status-value" id="bindingCount">-</div>
|
|
9099
8728
|
</div>
|
|
9100
|
-
<div class="status-card">
|
|
9101
|
-
<strong>Config Home</strong>
|
|
9102
|
-
<div class="status-value" id="homeStatus" style="font-size: 14px;">-</div>
|
|
9103
|
-
</div>
|
|
9104
8729
|
</section>
|
|
9105
8730
|
|
|
9106
8731
|
<div class="overview-grid">
|
|
@@ -9108,24 +8733,23 @@ function renderHtml() {
|
|
|
9108
8733
|
<div class="panel-header">
|
|
9109
8734
|
<div>
|
|
9110
8735
|
<h2>\u8FD0\u884C\u63A7\u5236</h2>
|
|
9111
|
-
<p>\u4FDD\u5B58\u914D\u7F6E\u540E\uFF0C\u53EF\u4EE5\u76F4\u63A5\u5728\u8FD9\u91CC\u542F\u505C
|
|
8736
|
+
<p>\u4FDD\u5B58\u914D\u7F6E\u540E\uFF0C\u53EF\u4EE5\u76F4\u63A5\u5728\u8FD9\u91CC\u542F\u505C\u6865\u63A5\u670D\u52A1\u6216\u5237\u65B0\u6574\u4F53\u72B6\u6001\u3002</p>
|
|
9112
8737
|
</div>
|
|
9113
8738
|
</div>
|
|
9114
8739
|
<div class="actions">
|
|
9115
|
-
<button class="primary" id="startBridgeBtn">\u542F\u52A8
|
|
9116
|
-
<button id="stopBridgeBtn">\u505C\u6B62
|
|
9117
|
-
<button id="restartBridgeBtn">\u91CD\u542F
|
|
9118
|
-
<button id="testCodexBtn">\u6D4B\u8BD5 Codex</button>
|
|
8740
|
+
<button class="primary" id="startBridgeBtn">\u542F\u52A8\u6865\u63A5\u670D\u52A1</button>
|
|
8741
|
+
<button id="stopBridgeBtn">\u505C\u6B62\u6865\u63A5\u670D\u52A1</button>
|
|
8742
|
+
<button id="restartBridgeBtn">\u91CD\u542F\u6865\u63A5\u670D\u52A1</button>
|
|
9119
8743
|
<button id="refreshBtn">\u5237\u65B0\u72B6\u6001</button>
|
|
9120
8744
|
</div>
|
|
9121
8745
|
|
|
9122
8746
|
<div class="panel-block">
|
|
9123
8747
|
<p class="panel-subtitle">\u5F53\u524D\u80FD\u529B</p>
|
|
9124
|
-
<div class="notice">\u5DF2\u63A5\u901A\uFF1A\u4FDD\u5B58\u914D\u7F6E\u3001\u540E\u53F0\u542F\u505C\u3001\u98DE\u4E66\u51ED\u636E\u6D4B\u8BD5\u3001\u5FAE\u4FE1\u626B\u7801\
|
|
8748
|
+
<div class="notice">\u5DF2\u63A5\u901A\uFF1A\u4FDD\u5B58\u914D\u7F6E\u3001\u540E\u53F0\u542F\u505C\u3001\u98DE\u4E66\u51ED\u636E\u6D4B\u8BD5\u3001\u5FAE\u4FE1\u626B\u7801\u3001\u684C\u9762\u4F1A\u8BDD\u53D1\u73B0\u3001IM \u7ED1\u5B9A\u67E5\u770B\u4E0E\u7F51\u9875\u4FA7\u5207\u6362\u3002</div>
|
|
9125
8749
|
</div>
|
|
9126
8750
|
|
|
9127
8751
|
<div class="panel-block">
|
|
9128
|
-
<p class="panel-subtitle"
|
|
8752
|
+
<p class="panel-subtitle">\u6865\u63A5\u670D\u52A1\u5F00\u673A\u81EA\u542F\u52A8</p>
|
|
9129
8753
|
<div class="notice" id="autostartNotice">\u6B63\u5728\u68C0\u67E5\u5F53\u524D Windows \u4EFB\u52A1\u8BA1\u5212\u7A0B\u5E8F\u72B6\u6001\u2026</div>
|
|
9130
8754
|
<div class="actions" style="margin-top: 12px;">
|
|
9131
8755
|
<button id="refreshAutostartBtn">\u5237\u65B0\u5F00\u673A\u81EA\u542F\u52A8\u72B6\u6001</button>
|
|
@@ -9133,10 +8757,10 @@ function renderHtml() {
|
|
|
9133
8757
|
</div>
|
|
9134
8758
|
|
|
9135
8759
|
<div class="panel-block">
|
|
9136
|
-
<p class="panel-subtitle">\u53EF\u9009 Codex
|
|
9137
|
-
<div class="notice"
|
|
8760
|
+
<p class="panel-subtitle">\u53EF\u9009 Codex \u6280\u80FD</p>
|
|
8761
|
+
<div class="notice">\u6865\u63A5\u670D\u52A1\u4E0D\u518D\u6CE8\u5165\u53D1\u9001\u9644\u4EF6\u7684\u63D0\u793A\u8BCD\u3002\u9700\u8981\u8BA9 Codex \u77E5\u9053\u201C\u53EF\u4EE5\u628A\u672C\u5730\u56FE\u7247/\u6587\u4EF6\u56DE\u53D1\u5230 IM\u201D\u65F6\uFF0C\u8BF7\u5B89\u88C5\u8FD9\u4E2A\u53EF\u9009\u6280\u80FD\u3002</div>
|
|
9138
8762
|
<div class="actions" style="margin-top: 12px;">
|
|
9139
|
-
<button id="installIntegrationBtn">\u5B89\u88C5\u53EF\u9009 Codex
|
|
8763
|
+
<button id="installIntegrationBtn">\u5B89\u88C5\u53EF\u9009 Codex \u6280\u80FD</button>
|
|
9140
8764
|
</div>
|
|
9141
8765
|
</div>
|
|
9142
8766
|
|
|
@@ -9330,7 +8954,7 @@ function renderHtml() {
|
|
|
9330
8954
|
</div>
|
|
9331
8955
|
</div>
|
|
9332
8956
|
|
|
9333
|
-
<div class="notice" style="margin-bottom: 16px;">\u6700\u77ED\u4F7F\u7528\u8DEF\u5F84\uFF1A\u5148\u53D1 <code>/t</code> \u67E5\u770B\u6700\u8FD1\u4F1A\u8BDD\uFF0C\u518D\u53D1 <code>/t 1</code> \u63A5\u7BA1\uFF1B\u4E4B\u540E\u76F4\u63A5\u53D1\u9001\u6587\u672C\u5373\u53EF\u7EE7\u7EED\u5F53\u524D\u4F1A\u8BDD\u3002
|
|
8957
|
+
<div class="notice" style="margin-bottom: 16px;">\u6700\u77ED\u4F7F\u7528\u8DEF\u5F84\uFF1A\u5148\u53D1 <code>/t</code> \u67E5\u770B\u6700\u8FD1\u4F1A\u8BDD\uFF0C\u518D\u53D1 <code>/t 1</code> \u63A5\u7BA1\uFF1B\u4E4B\u540E\u76F4\u63A5\u53D1\u9001\u6587\u672C\u5373\u53EF\u7EE7\u7EED\u5F53\u524D\u4F1A\u8BDD\u3002</div>
|
|
9334
8958
|
|
|
9335
8959
|
<div class="command-sections">
|
|
9336
8960
|
<section class="command-section">
|
|
@@ -10390,14 +10014,15 @@ function renderHtml() {
|
|
|
10390
10014
|
state.autostartStatus = status.autostart || null;
|
|
10391
10015
|
state.weixinAccounts = status.weixin && Array.isArray(status.weixin.linkedAccounts) ? status.weixin.linkedAccounts : [];
|
|
10392
10016
|
fillForm(config);
|
|
10393
|
-
const
|
|
10394
|
-
|
|
10395
|
-
|
|
10396
|
-
document.getElementById('
|
|
10017
|
+
const adapters = adapterStatuses();
|
|
10018
|
+
const runningAdapters = adapters.filter((item) => item.running);
|
|
10019
|
+
document.getElementById('bridgeStatus').textContent = status.bridge.running ? '\u8FD0\u884C\u4E2D' : '\u5DF2\u505C\u6B62';
|
|
10020
|
+
document.getElementById('bridgeStatusMeta').textContent = adapters.length
|
|
10021
|
+
? ('\u8FD0\u884C\u5B9E\u4F8B ' + runningAdapters.length + ' / ' + adapters.length)
|
|
10022
|
+
: '\u5F53\u524D\u6CA1\u6709\u901A\u9053\u5B9E\u4F8B\u5728\u8FD0\u884C';
|
|
10397
10023
|
renderAutostartStatus(status.autostart || null);
|
|
10398
10024
|
document.getElementById('integrationStatus').textContent = status.codexIntegrationInstalled ? '\u5DF2\u5B89\u88C5' : '\u672A\u5B89\u88C5';
|
|
10399
10025
|
document.getElementById('runtimeStatus').textContent = config.runtime || 'codex';
|
|
10400
|
-
document.getElementById('homeStatus').textContent = status.home;
|
|
10401
10026
|
document.getElementById('overviewHomeStatus').textContent = status.home;
|
|
10402
10027
|
document.getElementById('packageRoot').textContent = status.packageRoot;
|
|
10403
10028
|
renderBindings({
|
|
@@ -10415,7 +10040,7 @@ function renderHtml() {
|
|
|
10415
10040
|
valueEl.textContent = '\u4E0D\u652F\u6301';
|
|
10416
10041
|
noticeEl.textContent = status && status.error
|
|
10417
10042
|
? status.error
|
|
10418
|
-
: '\u5F53\u524D\u7CFB\u7EDF\u6682\u4E0D\u652F\u6301
|
|
10043
|
+
: '\u5F53\u524D\u7CFB\u7EDF\u6682\u4E0D\u652F\u6301\u6865\u63A5\u670D\u52A1\u5F00\u673A\u81EA\u542F\u52A8\u3002';
|
|
10419
10044
|
refreshBtn.disabled = true;
|
|
10420
10045
|
return;
|
|
10421
10046
|
}
|
|
@@ -10714,7 +10339,7 @@ function renderHtml() {
|
|
|
10714
10339
|
try {
|
|
10715
10340
|
await saveConfig();
|
|
10716
10341
|
const result = await api('/api/bridge/start', { method: 'POST' });
|
|
10717
|
-
showMessage('opsMessage', 'success', '
|
|
10342
|
+
showMessage('opsMessage', 'success', '\u6865\u63A5\u670D\u52A1\u5DF2\u542F\u52A8\u3002PID: ' + (result.status.pid || '-'));
|
|
10718
10343
|
await loadStatus();
|
|
10719
10344
|
await loadBindings();
|
|
10720
10345
|
await loadLogs();
|
|
@@ -10727,7 +10352,7 @@ function renderHtml() {
|
|
|
10727
10352
|
document.getElementById('stopBridgeBtn').addEventListener('click', async () => {
|
|
10728
10353
|
try {
|
|
10729
10354
|
await api('/api/bridge/stop', { method: 'POST' });
|
|
10730
|
-
showMessage('opsMessage', 'success', '
|
|
10355
|
+
showMessage('opsMessage', 'success', '\u6865\u63A5\u670D\u52A1\u5DF2\u505C\u6B62\u3002');
|
|
10731
10356
|
await loadStatus();
|
|
10732
10357
|
await loadBindings();
|
|
10733
10358
|
} catch (error) {
|
|
@@ -10739,7 +10364,7 @@ function renderHtml() {
|
|
|
10739
10364
|
try {
|
|
10740
10365
|
await saveConfig();
|
|
10741
10366
|
const result = await api('/api/bridge/restart', { method: 'POST' });
|
|
10742
|
-
showMessage('opsMessage', 'success', '
|
|
10367
|
+
showMessage('opsMessage', 'success', '\u6865\u63A5\u670D\u52A1\u5DF2\u91CD\u542F\u3002PID: ' + (result.status.pid || '-'));
|
|
10743
10368
|
await loadStatus();
|
|
10744
10369
|
await loadBindings();
|
|
10745
10370
|
await loadLogs();
|
|
@@ -10749,16 +10374,6 @@ function renderHtml() {
|
|
|
10749
10374
|
}
|
|
10750
10375
|
});
|
|
10751
10376
|
|
|
10752
|
-
document.getElementById('testCodexBtn').addEventListener('click', async () => {
|
|
10753
|
-
try {
|
|
10754
|
-
await saveConfig();
|
|
10755
|
-
const result = await api('/api/test/codex', { method: 'POST' });
|
|
10756
|
-
showMessage('opsMessage', result.ok ? 'success' : 'error', result.message);
|
|
10757
|
-
} catch (error) {
|
|
10758
|
-
showMessage('opsMessage', 'error', error.message);
|
|
10759
|
-
}
|
|
10760
|
-
});
|
|
10761
|
-
|
|
10762
10377
|
document.getElementById('refreshBtn').addEventListener('click', async () => {
|
|
10763
10378
|
try {
|
|
10764
10379
|
await loadStatus();
|
|
@@ -11096,11 +10711,6 @@ var server = http.createServer(async (request, response) => {
|
|
|
11096
10711
|
});
|
|
11097
10712
|
return;
|
|
11098
10713
|
}
|
|
11099
|
-
if (request.method === "POST" && url.pathname === "/api/test/codex") {
|
|
11100
|
-
const result = await testCodexConnection(loadConfig());
|
|
11101
|
-
json(response, 200, result);
|
|
11102
|
-
return;
|
|
11103
|
-
}
|
|
11104
10714
|
if (request.method === "POST" && url.pathname === "/api/install-codex-integration") {
|
|
11105
10715
|
const result = await installCodexIntegration();
|
|
11106
10716
|
json(response, 200, result);
|