codex-to-im 1.0.21 → 1.0.23
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 +100 -8
- package/dist/daemon.mjs +30 -90
- package/dist/ui-server.mjs +201 -593
- 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/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,36 +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 =
|
|
5617
|
+
var bridgeAutostartLauncherFile = path4.join(runtimeDir, "bridge-autostart.ps1");
|
|
5618
|
+
var npmUninstallLogFile = path4.join(runtimeDir, "npm-uninstall.log");
|
|
5952
5619
|
var WINDOWS_HIDE = process.platform === "win32" ? { windowsHide: true } : {};
|
|
5953
5620
|
function ensureDirs() {
|
|
5954
|
-
|
|
5955
|
-
|
|
5621
|
+
fs3.mkdirSync(runtimeDir, { recursive: true });
|
|
5622
|
+
fs3.mkdirSync(logsDir, { recursive: true });
|
|
5956
5623
|
}
|
|
5957
5624
|
function readJsonFile(filePath, fallback) {
|
|
5958
5625
|
try {
|
|
5959
|
-
return JSON.parse(
|
|
5626
|
+
return JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
5960
5627
|
} catch {
|
|
5961
5628
|
return fallback;
|
|
5962
5629
|
}
|
|
5963
5630
|
}
|
|
5964
5631
|
function readPid(filePath) {
|
|
5965
5632
|
try {
|
|
5966
|
-
const raw =
|
|
5633
|
+
const raw = fs3.readFileSync(filePath, "utf-8").trim();
|
|
5967
5634
|
const pid = Number(raw);
|
|
5968
5635
|
return Number.isFinite(pid) ? pid : void 0;
|
|
5969
5636
|
} catch {
|
|
@@ -5983,7 +5650,7 @@ function sleep(ms) {
|
|
|
5983
5650
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5984
5651
|
}
|
|
5985
5652
|
function getCurrentWindowsUser() {
|
|
5986
|
-
const user = process.env.USERNAME ||
|
|
5653
|
+
const user = process.env.USERNAME || os3.userInfo().username;
|
|
5987
5654
|
const domain = process.env.USERDOMAIN;
|
|
5988
5655
|
return domain ? `${domain}\\${user}` : user;
|
|
5989
5656
|
}
|
|
@@ -6084,12 +5751,12 @@ async function startBridge() {
|
|
|
6084
5751
|
ensureDirs();
|
|
6085
5752
|
const current = getBridgeStatus();
|
|
6086
5753
|
if (current.running) return current;
|
|
6087
|
-
const daemonEntry =
|
|
6088
|
-
if (!
|
|
5754
|
+
const daemonEntry = path4.join(packageRoot, "dist", "daemon.mjs");
|
|
5755
|
+
if (!fs3.existsSync(daemonEntry)) {
|
|
6089
5756
|
throw new Error(`Daemon bundle not found at ${daemonEntry}. Run npm run build first.`);
|
|
6090
5757
|
}
|
|
6091
|
-
const stdoutFd =
|
|
6092
|
-
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");
|
|
6093
5760
|
const child = spawn(process.execPath, [daemonEntry], {
|
|
6094
5761
|
cwd: packageRoot,
|
|
6095
5762
|
detached: true,
|
|
@@ -6192,37 +5859,37 @@ async function getBridgeAutostartStatus() {
|
|
|
6192
5859
|
}
|
|
6193
5860
|
function getBridgeLogs(lines = 200) {
|
|
6194
5861
|
ensureDirs();
|
|
6195
|
-
const filePath =
|
|
6196
|
-
if (!
|
|
6197
|
-
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/);
|
|
6198
5865
|
return all.slice(Math.max(0, all.length - lines)).join("\n");
|
|
6199
5866
|
}
|
|
6200
5867
|
function writeUiServerStatus(status) {
|
|
6201
5868
|
ensureDirs();
|
|
6202
|
-
|
|
5869
|
+
fs3.writeFileSync(uiStatusFile, JSON.stringify(status, null, 2), "utf-8");
|
|
6203
5870
|
}
|
|
6204
5871
|
async function installCodexIntegration() {
|
|
6205
|
-
const sourceSkill =
|
|
6206
|
-
if (!
|
|
5872
|
+
const sourceSkill = path4.join(packageRoot, "SKILL.md");
|
|
5873
|
+
if (!fs3.existsSync(sourceSkill)) {
|
|
6207
5874
|
throw new Error(`SKILL.md not found at ${sourceSkill}`);
|
|
6208
5875
|
}
|
|
6209
|
-
const skillsDir =
|
|
6210
|
-
const targetDir =
|
|
6211
|
-
|
|
6212
|
-
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)) {
|
|
6213
5880
|
return { targetDir, method: "existing" };
|
|
6214
5881
|
}
|
|
6215
5882
|
try {
|
|
6216
|
-
|
|
5883
|
+
fs3.symlinkSync(packageRoot, targetDir, process.platform === "win32" ? "junction" : "dir");
|
|
6217
5884
|
return { targetDir, method: "junction" };
|
|
6218
5885
|
} catch {
|
|
6219
|
-
|
|
5886
|
+
fs3.cpSync(packageRoot, targetDir, {
|
|
6220
5887
|
recursive: true,
|
|
6221
5888
|
filter: (source) => {
|
|
6222
|
-
const relative =
|
|
5889
|
+
const relative = path4.relative(packageRoot, source);
|
|
6223
5890
|
if (!relative) return true;
|
|
6224
|
-
if (relative === ".git" || relative.startsWith(`.git${
|
|
6225
|
-
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;
|
|
6226
5893
|
return true;
|
|
6227
5894
|
}
|
|
6228
5895
|
});
|
|
@@ -6230,27 +5897,27 @@ async function installCodexIntegration() {
|
|
|
6230
5897
|
}
|
|
6231
5898
|
}
|
|
6232
5899
|
function isCodexIntegrationInstalled() {
|
|
6233
|
-
const targetDir =
|
|
6234
|
-
return
|
|
5900
|
+
const targetDir = path4.join(os3.homedir(), ".codex", "skills", "codex-to-im");
|
|
5901
|
+
return fs3.existsSync(path4.join(targetDir, "SKILL.md"));
|
|
6235
5902
|
}
|
|
6236
5903
|
|
|
6237
5904
|
// src/store.ts
|
|
6238
|
-
import
|
|
6239
|
-
import
|
|
5905
|
+
import fs4 from "node:fs";
|
|
5906
|
+
import path5 from "node:path";
|
|
6240
5907
|
import crypto2 from "node:crypto";
|
|
6241
|
-
var DATA_DIR =
|
|
6242
|
-
var MESSAGES_DIR =
|
|
5908
|
+
var DATA_DIR = path5.join(CTI_HOME, "data");
|
|
5909
|
+
var MESSAGES_DIR = path5.join(DATA_DIR, "messages");
|
|
6243
5910
|
function ensureDir(dir) {
|
|
6244
|
-
|
|
5911
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
6245
5912
|
}
|
|
6246
5913
|
function atomicWrite(filePath, data) {
|
|
6247
5914
|
const tmp = filePath + ".tmp";
|
|
6248
|
-
|
|
6249
|
-
|
|
5915
|
+
fs4.writeFileSync(tmp, data, "utf-8");
|
|
5916
|
+
fs4.renameSync(tmp, filePath);
|
|
6250
5917
|
}
|
|
6251
5918
|
function readJson(filePath, fallback) {
|
|
6252
5919
|
try {
|
|
6253
|
-
const raw =
|
|
5920
|
+
const raw = fs4.readFileSync(filePath, "utf-8");
|
|
6254
5921
|
return JSON.parse(raw);
|
|
6255
5922
|
} catch {
|
|
6256
5923
|
return fallback;
|
|
@@ -6270,10 +5937,12 @@ function defaultAliasForProvider2(provider) {
|
|
|
6270
5937
|
if (provider === "weixin") return "\u5FAE\u4FE1";
|
|
6271
5938
|
return void 0;
|
|
6272
5939
|
}
|
|
6273
|
-
function
|
|
5940
|
+
function upgradeLegacyBinding(binding) {
|
|
6274
5941
|
const config = loadConfig();
|
|
6275
|
-
const
|
|
6276
|
-
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);
|
|
6277
5946
|
if (!resolvedInstance && !legacyProvider) {
|
|
6278
5947
|
return {
|
|
6279
5948
|
...binding,
|
|
@@ -6317,44 +5986,44 @@ var JsonFileStore = class {
|
|
|
6317
5986
|
this.reloadSessions();
|
|
6318
5987
|
this.reloadBindings();
|
|
6319
5988
|
const perms = readJson(
|
|
6320
|
-
|
|
5989
|
+
path5.join(DATA_DIR, "permissions.json"),
|
|
6321
5990
|
{}
|
|
6322
5991
|
);
|
|
6323
5992
|
for (const [id, p] of Object.entries(perms)) {
|
|
6324
5993
|
this.permissionLinks.set(id, p);
|
|
6325
5994
|
}
|
|
6326
5995
|
const offsets = readJson(
|
|
6327
|
-
|
|
5996
|
+
path5.join(DATA_DIR, "offsets.json"),
|
|
6328
5997
|
{}
|
|
6329
5998
|
);
|
|
6330
5999
|
for (const [k, v] of Object.entries(offsets)) {
|
|
6331
6000
|
this.offsets.set(k, v);
|
|
6332
6001
|
}
|
|
6333
6002
|
const dedup = readJson(
|
|
6334
|
-
|
|
6003
|
+
path5.join(DATA_DIR, "dedup.json"),
|
|
6335
6004
|
{}
|
|
6336
6005
|
);
|
|
6337
6006
|
for (const [k, v] of Object.entries(dedup)) {
|
|
6338
6007
|
this.dedupKeys.set(k, v);
|
|
6339
6008
|
}
|
|
6340
|
-
this.auditLog = readJson(
|
|
6009
|
+
this.auditLog = readJson(path5.join(DATA_DIR, "audit.json"), []);
|
|
6341
6010
|
}
|
|
6342
6011
|
reloadSessions() {
|
|
6343
6012
|
const sessions = readJson(
|
|
6344
|
-
|
|
6013
|
+
path5.join(DATA_DIR, "sessions.json"),
|
|
6345
6014
|
{}
|
|
6346
6015
|
);
|
|
6347
6016
|
this.sessions = new Map(Object.entries(sessions));
|
|
6348
6017
|
}
|
|
6349
6018
|
reloadBindings() {
|
|
6350
6019
|
const bindings = readJson(
|
|
6351
|
-
|
|
6020
|
+
path5.join(DATA_DIR, "bindings.json"),
|
|
6352
6021
|
{}
|
|
6353
6022
|
);
|
|
6354
6023
|
const normalized = /* @__PURE__ */ new Map();
|
|
6355
6024
|
let changed = false;
|
|
6356
6025
|
for (const binding of Object.values(bindings)) {
|
|
6357
|
-
const normalizedBinding =
|
|
6026
|
+
const normalizedBinding = upgradeLegacyBinding(binding);
|
|
6358
6027
|
if (didBindingChange(binding, normalizedBinding)) {
|
|
6359
6028
|
changed = true;
|
|
6360
6029
|
}
|
|
@@ -6367,47 +6036,47 @@ var JsonFileStore = class {
|
|
|
6367
6036
|
}
|
|
6368
6037
|
persistSessions() {
|
|
6369
6038
|
writeJson(
|
|
6370
|
-
|
|
6039
|
+
path5.join(DATA_DIR, "sessions.json"),
|
|
6371
6040
|
Object.fromEntries(this.sessions)
|
|
6372
6041
|
);
|
|
6373
6042
|
}
|
|
6374
6043
|
persistBindings() {
|
|
6375
6044
|
writeJson(
|
|
6376
|
-
|
|
6045
|
+
path5.join(DATA_DIR, "bindings.json"),
|
|
6377
6046
|
Object.fromEntries(this.bindings)
|
|
6378
6047
|
);
|
|
6379
6048
|
}
|
|
6380
6049
|
persistPermissions() {
|
|
6381
6050
|
writeJson(
|
|
6382
|
-
|
|
6051
|
+
path5.join(DATA_DIR, "permissions.json"),
|
|
6383
6052
|
Object.fromEntries(this.permissionLinks)
|
|
6384
6053
|
);
|
|
6385
6054
|
}
|
|
6386
6055
|
persistOffsets() {
|
|
6387
6056
|
writeJson(
|
|
6388
|
-
|
|
6057
|
+
path5.join(DATA_DIR, "offsets.json"),
|
|
6389
6058
|
Object.fromEntries(this.offsets)
|
|
6390
6059
|
);
|
|
6391
6060
|
}
|
|
6392
6061
|
persistDedup() {
|
|
6393
6062
|
writeJson(
|
|
6394
|
-
|
|
6063
|
+
path5.join(DATA_DIR, "dedup.json"),
|
|
6395
6064
|
Object.fromEntries(this.dedupKeys)
|
|
6396
6065
|
);
|
|
6397
6066
|
}
|
|
6398
6067
|
persistAudit() {
|
|
6399
|
-
writeJson(
|
|
6068
|
+
writeJson(path5.join(DATA_DIR, "audit.json"), this.auditLog);
|
|
6400
6069
|
}
|
|
6401
6070
|
persistMessages(sessionId) {
|
|
6402
6071
|
const msgs = this.messages.get(sessionId) || [];
|
|
6403
|
-
writeJson(
|
|
6072
|
+
writeJson(path5.join(MESSAGES_DIR, `${sessionId}.json`), msgs);
|
|
6404
6073
|
}
|
|
6405
6074
|
loadMessages(sessionId) {
|
|
6406
6075
|
if (this.messages.has(sessionId)) {
|
|
6407
6076
|
return this.messages.get(sessionId);
|
|
6408
6077
|
}
|
|
6409
6078
|
const msgs = readJson(
|
|
6410
|
-
|
|
6079
|
+
path5.join(MESSAGES_DIR, `${sessionId}.json`),
|
|
6411
6080
|
[]
|
|
6412
6081
|
);
|
|
6413
6082
|
this.messages.set(sessionId, msgs);
|
|
@@ -6575,7 +6244,7 @@ var JsonFileStore = class {
|
|
|
6575
6244
|
}
|
|
6576
6245
|
this.messages.delete(sessionId);
|
|
6577
6246
|
try {
|
|
6578
|
-
|
|
6247
|
+
fs4.rmSync(path5.join(MESSAGES_DIR, `${sessionId}.json`), { force: true });
|
|
6579
6248
|
} catch {
|
|
6580
6249
|
}
|
|
6581
6250
|
this.persistSessions();
|
|
@@ -6758,8 +6427,8 @@ var JsonFileStore = class {
|
|
|
6758
6427
|
|
|
6759
6428
|
// src/weixin-login.ts
|
|
6760
6429
|
var import_qrcode = __toESM(require_lib(), 1);
|
|
6761
|
-
import
|
|
6762
|
-
import
|
|
6430
|
+
import fs6 from "node:fs";
|
|
6431
|
+
import path7 from "node:path";
|
|
6763
6432
|
import { spawn as spawn2 } from "node:child_process";
|
|
6764
6433
|
|
|
6765
6434
|
// src/adapters/weixin/weixin-api.ts
|
|
@@ -6812,24 +6481,24 @@ async function pollLoginQrStatus(qrcode, baseUrl) {
|
|
|
6812
6481
|
}
|
|
6813
6482
|
|
|
6814
6483
|
// src/weixin-store.ts
|
|
6815
|
-
import
|
|
6816
|
-
import
|
|
6817
|
-
var DATA_DIR2 =
|
|
6818
|
-
var ACCOUNTS_PATH =
|
|
6819
|
-
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");
|
|
6820
6489
|
var DEFAULT_BASE_URL2 = "https://ilinkai.weixin.qq.com";
|
|
6821
6490
|
var DEFAULT_CDN_BASE_URL2 = "https://novac2c.cdn.weixin.qq.com/c2c";
|
|
6822
6491
|
function ensureDir2(dir) {
|
|
6823
|
-
|
|
6492
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
6824
6493
|
}
|
|
6825
6494
|
function atomicWrite2(filePath, data) {
|
|
6826
6495
|
const tmpPath = `${filePath}.tmp`;
|
|
6827
|
-
|
|
6828
|
-
|
|
6496
|
+
fs5.writeFileSync(tmpPath, data, "utf-8");
|
|
6497
|
+
fs5.renameSync(tmpPath, filePath);
|
|
6829
6498
|
}
|
|
6830
6499
|
function readJson2(filePath, fallback) {
|
|
6831
6500
|
try {
|
|
6832
|
-
const raw =
|
|
6501
|
+
const raw = fs5.readFileSync(filePath, "utf-8");
|
|
6833
6502
|
return JSON.parse(raw);
|
|
6834
6503
|
} catch {
|
|
6835
6504
|
return fallback;
|
|
@@ -6941,10 +6610,10 @@ function deleteWeixinContextTokensByAccount(accountId) {
|
|
|
6941
6610
|
var MAX_REFRESHES = 3;
|
|
6942
6611
|
var QR_TTL_MS = 5 * 6e4;
|
|
6943
6612
|
var POLL_INTERVAL_MS = 3e3;
|
|
6944
|
-
var RUNTIME_DIR =
|
|
6945
|
-
var HTML_PATH =
|
|
6613
|
+
var RUNTIME_DIR = path7.join(CTI_HOME, "runtime");
|
|
6614
|
+
var HTML_PATH = path7.join(RUNTIME_DIR, "weixin-login.html");
|
|
6946
6615
|
function ensureRuntimeDir() {
|
|
6947
|
-
|
|
6616
|
+
fs6.mkdirSync(RUNTIME_DIR, { recursive: true });
|
|
6948
6617
|
}
|
|
6949
6618
|
function escapeHtml(text2) {
|
|
6950
6619
|
return text2.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
@@ -7044,7 +6713,7 @@ async function writeQrHtml(session) {
|
|
|
7044
6713
|
margin: 0,
|
|
7045
6714
|
width: 300
|
|
7046
6715
|
});
|
|
7047
|
-
|
|
6716
|
+
fs6.writeFileSync(HTML_PATH, buildQrHtml(session, qrSvg), "utf-8");
|
|
7048
6717
|
}
|
|
7049
6718
|
function openQrHtml() {
|
|
7050
6719
|
try {
|
|
@@ -7162,7 +6831,7 @@ async function runWeixinLogin(config = {}) {
|
|
|
7162
6831
|
}
|
|
7163
6832
|
var isMainModule = (() => {
|
|
7164
6833
|
const entry = process.argv[1];
|
|
7165
|
-
return !!entry &&
|
|
6834
|
+
return !!entry && path7.resolve(entry) === path7.resolve(new URL(import.meta.url).pathname);
|
|
7166
6835
|
})();
|
|
7167
6836
|
if (isMainModule) {
|
|
7168
6837
|
runWeixinLogin().catch((err) => {
|
|
@@ -7172,14 +6841,14 @@ if (isMainModule) {
|
|
|
7172
6841
|
}
|
|
7173
6842
|
|
|
7174
6843
|
// src/codex-models.ts
|
|
7175
|
-
import
|
|
7176
|
-
import
|
|
7177
|
-
import
|
|
7178
|
-
var DEFAULT_CODEX_CONFIG_PATH =
|
|
7179
|
-
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");
|
|
7180
6849
|
function readConfiguredCodexModel(configPath = DEFAULT_CODEX_CONFIG_PATH) {
|
|
7181
6850
|
try {
|
|
7182
|
-
const raw =
|
|
6851
|
+
const raw = fs7.readFileSync(configPath, "utf-8");
|
|
7183
6852
|
let inSection = false;
|
|
7184
6853
|
for (const line of raw.split(/\r?\n/)) {
|
|
7185
6854
|
const trimmed = line.trim();
|
|
@@ -7201,7 +6870,7 @@ function readConfiguredCodexModel(configPath = DEFAULT_CODEX_CONFIG_PATH) {
|
|
|
7201
6870
|
}
|
|
7202
6871
|
function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
|
|
7203
6872
|
try {
|
|
7204
|
-
const raw =
|
|
6873
|
+
const raw = fs7.readFileSync(cachePath, "utf-8");
|
|
7205
6874
|
const parsed = JSON.parse(raw);
|
|
7206
6875
|
if (!Array.isArray(parsed.models)) return [];
|
|
7207
6876
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -7404,7 +7073,7 @@ function isLocalRequest(request) {
|
|
|
7404
7073
|
return isLoopbackAddress(getRemoteAddress(request));
|
|
7405
7074
|
}
|
|
7406
7075
|
function getLanUrls(currentPort) {
|
|
7407
|
-
const interfaces =
|
|
7076
|
+
const interfaces = os5.networkInterfaces();
|
|
7408
7077
|
const urls = /* @__PURE__ */ new Set();
|
|
7409
7078
|
for (const records of Object.values(interfaces)) {
|
|
7410
7079
|
for (const record of records || []) {
|
|
@@ -7712,53 +7381,6 @@ function deleteChannelInstance(current, channelId) {
|
|
|
7712
7381
|
enabledChannels: Array.from(new Set(nextChannels.filter((channel) => channel.enabled).map((channel) => channel.provider)))
|
|
7713
7382
|
};
|
|
7714
7383
|
}
|
|
7715
|
-
async function testCodexConnection(config) {
|
|
7716
|
-
const provider = new CodexProvider(new PendingPermissions());
|
|
7717
|
-
const abortController = new AbortController();
|
|
7718
|
-
const timeout = setTimeout(() => abortController.abort(), 3e4);
|
|
7719
|
-
const workingDirectory = config.defaultWorkspaceRoot || DEFAULT_WORKSPACE_ROOT;
|
|
7720
|
-
fs9.mkdirSync(workingDirectory, { recursive: true });
|
|
7721
|
-
try {
|
|
7722
|
-
const stream = provider.streamChat({
|
|
7723
|
-
prompt: "Reply with the single word OK.",
|
|
7724
|
-
sessionId: `ui-test-${Date.now()}`,
|
|
7725
|
-
workingDirectory,
|
|
7726
|
-
permissionMode: "plan",
|
|
7727
|
-
abortController
|
|
7728
|
-
});
|
|
7729
|
-
const reader = stream.getReader();
|
|
7730
|
-
let responseText = "";
|
|
7731
|
-
let raw = "";
|
|
7732
|
-
while (true) {
|
|
7733
|
-
const { done, value } = await reader.read();
|
|
7734
|
-
if (done) break;
|
|
7735
|
-
raw += value;
|
|
7736
|
-
const lines = value.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
7737
|
-
for (const line of lines) {
|
|
7738
|
-
if (!line.startsWith("data: ")) continue;
|
|
7739
|
-
const parsed = JSON.parse(line.slice(6));
|
|
7740
|
-
if (parsed.type === "text") {
|
|
7741
|
-
responseText += parsed.data;
|
|
7742
|
-
}
|
|
7743
|
-
if (parsed.type === "error") {
|
|
7744
|
-
return { ok: false, message: parsed.data, raw };
|
|
7745
|
-
}
|
|
7746
|
-
}
|
|
7747
|
-
}
|
|
7748
|
-
return {
|
|
7749
|
-
ok: true,
|
|
7750
|
-
message: responseText.trim() || "Codex SDK \u5DF2\u8FDE\u901A\uFF0C\u4F46\u6D4B\u8BD5\u6CA1\u6709\u8FD4\u56DE\u6587\u672C\u3002",
|
|
7751
|
-
raw
|
|
7752
|
-
};
|
|
7753
|
-
} catch (error) {
|
|
7754
|
-
return {
|
|
7755
|
-
ok: false,
|
|
7756
|
-
message: error instanceof Error ? error.message : String(error)
|
|
7757
|
-
};
|
|
7758
|
-
} finally {
|
|
7759
|
-
clearTimeout(timeout);
|
|
7760
|
-
}
|
|
7761
|
-
}
|
|
7762
7384
|
function renderLoginHtml() {
|
|
7763
7385
|
return `<!doctype html>
|
|
7764
7386
|
<html lang="zh-CN">
|
|
@@ -8066,7 +7688,7 @@ function renderHtml() {
|
|
|
8066
7688
|
|
|
8067
7689
|
.status-grid {
|
|
8068
7690
|
display: grid;
|
|
8069
|
-
grid-template-columns: repeat(
|
|
7691
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
8070
7692
|
gap: 16px;
|
|
8071
7693
|
margin-bottom: 20px;
|
|
8072
7694
|
}
|
|
@@ -8097,6 +7719,13 @@ function renderHtml() {
|
|
|
8097
7719
|
word-break: break-word;
|
|
8098
7720
|
}
|
|
8099
7721
|
|
|
7722
|
+
.status-meta {
|
|
7723
|
+
margin-top: 8px;
|
|
7724
|
+
color: var(--muted);
|
|
7725
|
+
font-size: 12px;
|
|
7726
|
+
line-height: 1.45;
|
|
7727
|
+
}
|
|
7728
|
+
|
|
8100
7729
|
.panel {
|
|
8101
7730
|
padding: 20px;
|
|
8102
7731
|
}
|
|
@@ -9073,33 +8702,30 @@ function renderHtml() {
|
|
|
9073
8702
|
|
|
9074
8703
|
<section class="status-grid">
|
|
9075
8704
|
<div class="status-card">
|
|
9076
|
-
<strong
|
|
8705
|
+
<strong>\u6865\u63A5\u670D\u52A1</strong>
|
|
9077
8706
|
<div class="status-value" id="bridgeStatus">-</div>
|
|
8707
|
+
<div class="status-meta" id="bridgeStatusMeta">-</div>
|
|
9078
8708
|
</div>
|
|
9079
8709
|
<div class="status-card">
|
|
9080
|
-
<strong
|
|
8710
|
+
<strong>\u6865\u63A5\u670D\u52A1\u5F00\u673A\u81EA\u542F\u52A8</strong>
|
|
9081
8711
|
<div class="status-value" id="autostartStatus">-</div>
|
|
9082
8712
|
</div>
|
|
9083
8713
|
<div class="status-card">
|
|
9084
|
-
<strong>Codex
|
|
8714
|
+
<strong>Codex \u6280\u80FD</strong>
|
|
9085
8715
|
<div class="status-value" id="integrationStatus">-</div>
|
|
9086
8716
|
</div>
|
|
9087
8717
|
<div class="status-card">
|
|
9088
|
-
<strong
|
|
8718
|
+
<strong>\u8FD0\u884C\u65F6</strong>
|
|
9089
8719
|
<div class="status-value" id="runtimeStatus">-</div>
|
|
9090
8720
|
</div>
|
|
9091
8721
|
<div class="status-card">
|
|
9092
|
-
<strong
|
|
8722
|
+
<strong>\u684C\u9762\u4F1A\u8BDD</strong>
|
|
9093
8723
|
<div class="status-value" id="desktopSessionCount">-</div>
|
|
9094
8724
|
</div>
|
|
9095
8725
|
<div class="status-card">
|
|
9096
|
-
<strong
|
|
8726
|
+
<strong>\u804A\u5929\u7ED1\u5B9A</strong>
|
|
9097
8727
|
<div class="status-value" id="bindingCount">-</div>
|
|
9098
8728
|
</div>
|
|
9099
|
-
<div class="status-card">
|
|
9100
|
-
<strong>Config Home</strong>
|
|
9101
|
-
<div class="status-value" id="homeStatus" style="font-size: 14px;">-</div>
|
|
9102
|
-
</div>
|
|
9103
8729
|
</section>
|
|
9104
8730
|
|
|
9105
8731
|
<div class="overview-grid">
|
|
@@ -9107,24 +8733,23 @@ function renderHtml() {
|
|
|
9107
8733
|
<div class="panel-header">
|
|
9108
8734
|
<div>
|
|
9109
8735
|
<h2>\u8FD0\u884C\u63A7\u5236</h2>
|
|
9110
|
-
<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>
|
|
9111
8737
|
</div>
|
|
9112
8738
|
</div>
|
|
9113
8739
|
<div class="actions">
|
|
9114
|
-
<button class="primary" id="startBridgeBtn">\u542F\u52A8
|
|
9115
|
-
<button id="stopBridgeBtn">\u505C\u6B62
|
|
9116
|
-
<button id="restartBridgeBtn">\u91CD\u542F
|
|
9117
|
-
<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>
|
|
9118
8743
|
<button id="refreshBtn">\u5237\u65B0\u72B6\u6001</button>
|
|
9119
8744
|
</div>
|
|
9120
8745
|
|
|
9121
8746
|
<div class="panel-block">
|
|
9122
8747
|
<p class="panel-subtitle">\u5F53\u524D\u80FD\u529B</p>
|
|
9123
|
-
<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>
|
|
9124
8749
|
</div>
|
|
9125
8750
|
|
|
9126
8751
|
<div class="panel-block">
|
|
9127
|
-
<p class="panel-subtitle"
|
|
8752
|
+
<p class="panel-subtitle">\u6865\u63A5\u670D\u52A1\u5F00\u673A\u81EA\u542F\u52A8</p>
|
|
9128
8753
|
<div class="notice" id="autostartNotice">\u6B63\u5728\u68C0\u67E5\u5F53\u524D Windows \u4EFB\u52A1\u8BA1\u5212\u7A0B\u5E8F\u72B6\u6001\u2026</div>
|
|
9129
8754
|
<div class="actions" style="margin-top: 12px;">
|
|
9130
8755
|
<button id="refreshAutostartBtn">\u5237\u65B0\u5F00\u673A\u81EA\u542F\u52A8\u72B6\u6001</button>
|
|
@@ -9132,10 +8757,10 @@ function renderHtml() {
|
|
|
9132
8757
|
</div>
|
|
9133
8758
|
|
|
9134
8759
|
<div class="panel-block">
|
|
9135
|
-
<p class="panel-subtitle">\u53EF\u9009 Codex
|
|
9136
|
-
<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>
|
|
9137
8762
|
<div class="actions" style="margin-top: 12px;">
|
|
9138
|
-
<button id="installIntegrationBtn">\u5B89\u88C5\u53EF\u9009 Codex
|
|
8763
|
+
<button id="installIntegrationBtn">\u5B89\u88C5\u53EF\u9009 Codex \u6280\u80FD</button>
|
|
9139
8764
|
</div>
|
|
9140
8765
|
</div>
|
|
9141
8766
|
|
|
@@ -9329,7 +8954,7 @@ function renderHtml() {
|
|
|
9329
8954
|
</div>
|
|
9330
8955
|
</div>
|
|
9331
8956
|
|
|
9332
|
-
<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>
|
|
9333
8958
|
|
|
9334
8959
|
<div class="command-sections">
|
|
9335
8960
|
<section class="command-section">
|
|
@@ -10389,14 +10014,15 @@ function renderHtml() {
|
|
|
10389
10014
|
state.autostartStatus = status.autostart || null;
|
|
10390
10015
|
state.weixinAccounts = status.weixin && Array.isArray(status.weixin.linkedAccounts) ? status.weixin.linkedAccounts : [];
|
|
10391
10016
|
fillForm(config);
|
|
10392
|
-
const
|
|
10393
|
-
|
|
10394
|
-
|
|
10395
|
-
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';
|
|
10396
10023
|
renderAutostartStatus(status.autostart || null);
|
|
10397
10024
|
document.getElementById('integrationStatus').textContent = status.codexIntegrationInstalled ? '\u5DF2\u5B89\u88C5' : '\u672A\u5B89\u88C5';
|
|
10398
10025
|
document.getElementById('runtimeStatus').textContent = config.runtime || 'codex';
|
|
10399
|
-
document.getElementById('homeStatus').textContent = status.home;
|
|
10400
10026
|
document.getElementById('overviewHomeStatus').textContent = status.home;
|
|
10401
10027
|
document.getElementById('packageRoot').textContent = status.packageRoot;
|
|
10402
10028
|
renderBindings({
|
|
@@ -10414,7 +10040,7 @@ function renderHtml() {
|
|
|
10414
10040
|
valueEl.textContent = '\u4E0D\u652F\u6301';
|
|
10415
10041
|
noticeEl.textContent = status && status.error
|
|
10416
10042
|
? status.error
|
|
10417
|
-
: '\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';
|
|
10418
10044
|
refreshBtn.disabled = true;
|
|
10419
10045
|
return;
|
|
10420
10046
|
}
|
|
@@ -10713,7 +10339,7 @@ function renderHtml() {
|
|
|
10713
10339
|
try {
|
|
10714
10340
|
await saveConfig();
|
|
10715
10341
|
const result = await api('/api/bridge/start', { method: 'POST' });
|
|
10716
|
-
showMessage('opsMessage', 'success', '
|
|
10342
|
+
showMessage('opsMessage', 'success', '\u6865\u63A5\u670D\u52A1\u5DF2\u542F\u52A8\u3002PID: ' + (result.status.pid || '-'));
|
|
10717
10343
|
await loadStatus();
|
|
10718
10344
|
await loadBindings();
|
|
10719
10345
|
await loadLogs();
|
|
@@ -10726,7 +10352,7 @@ function renderHtml() {
|
|
|
10726
10352
|
document.getElementById('stopBridgeBtn').addEventListener('click', async () => {
|
|
10727
10353
|
try {
|
|
10728
10354
|
await api('/api/bridge/stop', { method: 'POST' });
|
|
10729
|
-
showMessage('opsMessage', 'success', '
|
|
10355
|
+
showMessage('opsMessage', 'success', '\u6865\u63A5\u670D\u52A1\u5DF2\u505C\u6B62\u3002');
|
|
10730
10356
|
await loadStatus();
|
|
10731
10357
|
await loadBindings();
|
|
10732
10358
|
} catch (error) {
|
|
@@ -10738,7 +10364,7 @@ function renderHtml() {
|
|
|
10738
10364
|
try {
|
|
10739
10365
|
await saveConfig();
|
|
10740
10366
|
const result = await api('/api/bridge/restart', { method: 'POST' });
|
|
10741
|
-
showMessage('opsMessage', 'success', '
|
|
10367
|
+
showMessage('opsMessage', 'success', '\u6865\u63A5\u670D\u52A1\u5DF2\u91CD\u542F\u3002PID: ' + (result.status.pid || '-'));
|
|
10742
10368
|
await loadStatus();
|
|
10743
10369
|
await loadBindings();
|
|
10744
10370
|
await loadLogs();
|
|
@@ -10748,16 +10374,6 @@ function renderHtml() {
|
|
|
10748
10374
|
}
|
|
10749
10375
|
});
|
|
10750
10376
|
|
|
10751
|
-
document.getElementById('testCodexBtn').addEventListener('click', async () => {
|
|
10752
|
-
try {
|
|
10753
|
-
await saveConfig();
|
|
10754
|
-
const result = await api('/api/test/codex', { method: 'POST' });
|
|
10755
|
-
showMessage('opsMessage', result.ok ? 'success' : 'error', result.message);
|
|
10756
|
-
} catch (error) {
|
|
10757
|
-
showMessage('opsMessage', 'error', error.message);
|
|
10758
|
-
}
|
|
10759
|
-
});
|
|
10760
|
-
|
|
10761
10377
|
document.getElementById('refreshBtn').addEventListener('click', async () => {
|
|
10762
10378
|
try {
|
|
10763
10379
|
await loadStatus();
|
|
@@ -10883,9 +10499,6 @@ function renderHtml() {
|
|
|
10883
10499
|
showMessage('opsMessage', 'error', error.message);
|
|
10884
10500
|
});
|
|
10885
10501
|
|
|
10886
|
-
setInterval(() => {
|
|
10887
|
-
loadBindings().catch(() => {});
|
|
10888
|
-
}, 4000);
|
|
10889
10502
|
</script>
|
|
10890
10503
|
</body>
|
|
10891
10504
|
</html>`;
|
|
@@ -11098,11 +10711,6 @@ var server = http.createServer(async (request, response) => {
|
|
|
11098
10711
|
});
|
|
11099
10712
|
return;
|
|
11100
10713
|
}
|
|
11101
|
-
if (request.method === "POST" && url.pathname === "/api/test/codex") {
|
|
11102
|
-
const result = await testCodexConnection(loadConfig());
|
|
11103
|
-
json(response, 200, result);
|
|
11104
|
-
return;
|
|
11105
|
-
}
|
|
11106
10714
|
if (request.method === "POST" && url.pathname === "/api/install-codex-integration") {
|
|
11107
10715
|
const result = await installCodexIntegration();
|
|
11108
10716
|
json(response, 200, result);
|