codex-to-im 1.0.59 → 1.0.61
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/dist/cli.mjs +211 -74
- package/dist/daemon.mjs +1685 -707
- package/dist/ui-server.mjs +857 -463
- package/package.json +9 -8
- package/scripts/patch-codex-sdk-windows-hide.js +14 -3
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 path11 = 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 < path11.length - 1; i++) {
|
|
1578
|
+
optimizedSegs.push(graph.table[path11[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 fs11 = __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(path11, 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 = fs11.createWriteStream(path11);
|
|
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(path11, qrData, options, cb) {
|
|
4129
4129
|
if (typeof cb === "undefined") {
|
|
4130
4130
|
cb = options;
|
|
4131
4131
|
options = void 0;
|
|
4132
4132
|
}
|
|
4133
|
-
const
|
|
4133
|
+
const fs11 = __require("fs");
|
|
4134
4134
|
const utf8 = exports.render(qrData, options);
|
|
4135
|
-
|
|
4135
|
+
fs11.writeFile(path11, 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 path11 = "";
|
|
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
|
+
path11 += 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
|
+
path11 += svgCmd("h", lineLength);
|
|
4273
4273
|
lineLength = 0;
|
|
4274
4274
|
}
|
|
4275
4275
|
} else {
|
|
4276
4276
|
moveBy++;
|
|
4277
4277
|
}
|
|
4278
4278
|
}
|
|
4279
|
-
return
|
|
4279
|
+
return path11;
|
|
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 path11 = "<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 + path11 + "</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(path11, qrData, options, cb) {
|
|
4305
4305
|
if (typeof cb === "undefined") {
|
|
4306
4306
|
cb = options;
|
|
4307
4307
|
options = void 0;
|
|
4308
4308
|
}
|
|
4309
|
-
const
|
|
4309
|
+
const fs11 = __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
|
+
fs11.writeFile(path11, 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(path11) {
|
|
4471
|
+
return path11.slice((path11.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(path11, text2, opts, cb) {
|
|
4536
|
+
if (typeof path11 !== "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(path11);
|
|
4544
4544
|
const renderer = getRendererFromType(type);
|
|
4545
|
-
const renderToFile = renderer.renderToFile.bind(null,
|
|
4545
|
+
const renderToFile = renderer.renderToFile.bind(null, path11);
|
|
4546
4546
|
return render(renderToFile, text2, params);
|
|
4547
4547
|
};
|
|
4548
4548
|
exports.toFileStream = function toFileStream(stream, text2, opts) {
|
|
@@ -4566,12 +4566,13 @@ var require_lib = __commonJS({
|
|
|
4566
4566
|
|
|
4567
4567
|
// src/ui-server.ts
|
|
4568
4568
|
import http from "node:http";
|
|
4569
|
-
import
|
|
4569
|
+
import crypto9 from "node:crypto";
|
|
4570
4570
|
import net from "node:net";
|
|
4571
4571
|
import os5 from "node:os";
|
|
4572
4572
|
|
|
4573
4573
|
// src/config.ts
|
|
4574
|
-
import
|
|
4574
|
+
import fs2 from "node:fs";
|
|
4575
|
+
import crypto2 from "node:crypto";
|
|
4575
4576
|
import os from "node:os";
|
|
4576
4577
|
import path from "node:path";
|
|
4577
4578
|
|
|
@@ -4598,10 +4599,109 @@ function normalizeChannelId(value) {
|
|
|
4598
4599
|
return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "channel";
|
|
4599
4600
|
}
|
|
4600
4601
|
|
|
4602
|
+
// src/file-lock.ts
|
|
4603
|
+
import crypto from "node:crypto";
|
|
4604
|
+
import fs from "node:fs";
|
|
4605
|
+
var DEFAULT_LOCK_TIMEOUT_MS = 5e3;
|
|
4606
|
+
var DEFAULT_STALE_LOCK_MS = 3e4;
|
|
4607
|
+
var LOCK_RETRY_INTERVAL_MS = 25;
|
|
4608
|
+
var WAIT_ARRAY = new Int32Array(new SharedArrayBuffer(4));
|
|
4609
|
+
var ACTIVE_LOCKS = /* @__PURE__ */ new Map();
|
|
4610
|
+
function sleepSync(ms) {
|
|
4611
|
+
Atomics.wait(WAIT_ARRAY, 0, 0, Math.max(1, ms));
|
|
4612
|
+
}
|
|
4613
|
+
function isProcessAlive(pid) {
|
|
4614
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
4615
|
+
try {
|
|
4616
|
+
process.kill(pid, 0);
|
|
4617
|
+
return true;
|
|
4618
|
+
} catch (error) {
|
|
4619
|
+
return error.code === "EPERM";
|
|
4620
|
+
}
|
|
4621
|
+
}
|
|
4622
|
+
function removeStaleLock(lockPath, staleAfterMs) {
|
|
4623
|
+
try {
|
|
4624
|
+
const stat = fs.statSync(lockPath);
|
|
4625
|
+
if (Date.now() - stat.mtimeMs < staleAfterMs) return false;
|
|
4626
|
+
let ownerPid = 0;
|
|
4627
|
+
try {
|
|
4628
|
+
const parsed = JSON.parse(fs.readFileSync(lockPath, "utf8"));
|
|
4629
|
+
ownerPid = typeof parsed.pid === "number" ? parsed.pid : 0;
|
|
4630
|
+
} catch {
|
|
4631
|
+
}
|
|
4632
|
+
if (ownerPid && isProcessAlive(ownerPid)) return false;
|
|
4633
|
+
fs.rmSync(lockPath, { force: true });
|
|
4634
|
+
return true;
|
|
4635
|
+
} catch (error) {
|
|
4636
|
+
return error.code === "ENOENT";
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4639
|
+
function withFileLock(targetPath, operation, options) {
|
|
4640
|
+
const lockPath = `${targetPath}.lock`;
|
|
4641
|
+
const activeDepth = ACTIVE_LOCKS.get(lockPath) || 0;
|
|
4642
|
+
if (activeDepth > 0) {
|
|
4643
|
+
ACTIVE_LOCKS.set(lockPath, activeDepth + 1);
|
|
4644
|
+
try {
|
|
4645
|
+
return operation();
|
|
4646
|
+
} finally {
|
|
4647
|
+
const nextDepth = (ACTIVE_LOCKS.get(lockPath) || 1) - 1;
|
|
4648
|
+
if (nextDepth > 0) ACTIVE_LOCKS.set(lockPath, nextDepth);
|
|
4649
|
+
else ACTIVE_LOCKS.delete(lockPath);
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
const timeoutMs = Math.max(0, options?.timeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS);
|
|
4653
|
+
const staleAfterMs = Math.max(timeoutMs, options?.staleAfterMs ?? DEFAULT_STALE_LOCK_MS);
|
|
4654
|
+
const deadline = Date.now() + timeoutMs;
|
|
4655
|
+
const record = {
|
|
4656
|
+
token: crypto.randomUUID(),
|
|
4657
|
+
pid: process.pid,
|
|
4658
|
+
createdAt: Date.now()
|
|
4659
|
+
};
|
|
4660
|
+
let handle = null;
|
|
4661
|
+
while (handle === null) {
|
|
4662
|
+
try {
|
|
4663
|
+
handle = fs.openSync(lockPath, "wx", 384);
|
|
4664
|
+
fs.writeFileSync(handle, JSON.stringify(record), "utf8");
|
|
4665
|
+
} catch (error) {
|
|
4666
|
+
const code = error.code;
|
|
4667
|
+
if (code !== "EEXIST") throw error;
|
|
4668
|
+
if (removeStaleLock(lockPath, staleAfterMs)) continue;
|
|
4669
|
+
if (Date.now() >= deadline) {
|
|
4670
|
+
throw new Error(`Timed out waiting for file lock: ${lockPath}`);
|
|
4671
|
+
}
|
|
4672
|
+
sleepSync(Math.min(LOCK_RETRY_INTERVAL_MS, Math.max(1, deadline - Date.now())));
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
try {
|
|
4676
|
+
ACTIVE_LOCKS.set(lockPath, 1);
|
|
4677
|
+
return operation();
|
|
4678
|
+
} finally {
|
|
4679
|
+
ACTIVE_LOCKS.delete(lockPath);
|
|
4680
|
+
try {
|
|
4681
|
+
fs.closeSync(handle);
|
|
4682
|
+
} catch {
|
|
4683
|
+
}
|
|
4684
|
+
try {
|
|
4685
|
+
const current = JSON.parse(fs.readFileSync(lockPath, "utf8"));
|
|
4686
|
+
if (current.token === record.token) fs.rmSync(lockPath, { force: true });
|
|
4687
|
+
} catch {
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
function withFileLocks(targetPaths, operation) {
|
|
4692
|
+
const paths = Array.from(new Set(targetPaths)).sort();
|
|
4693
|
+
const acquire = (index) => {
|
|
4694
|
+
if (index >= paths.length) return operation();
|
|
4695
|
+
return withFileLock(paths[index], () => acquire(index + 1));
|
|
4696
|
+
};
|
|
4697
|
+
return acquire(0);
|
|
4698
|
+
}
|
|
4699
|
+
|
|
4601
4700
|
// src/config.ts
|
|
4602
4701
|
function isSupportedChannelProvider(value) {
|
|
4603
4702
|
return value === "feishu" || value === "weixin";
|
|
4604
4703
|
}
|
|
4704
|
+
var RAW_CHANNELS = Symbol("rawConfigV2Channels");
|
|
4605
4705
|
function toFeishuConfig(channel) {
|
|
4606
4706
|
return channel?.provider === "feishu" ? channel.config : void 0;
|
|
4607
4707
|
}
|
|
@@ -4641,7 +4741,7 @@ function parseEnvFile(content) {
|
|
|
4641
4741
|
}
|
|
4642
4742
|
function loadRawConfigEnv() {
|
|
4643
4743
|
try {
|
|
4644
|
-
return parseEnvFile(
|
|
4744
|
+
return parseEnvFile(fs2.readFileSync(CONFIG_PATH, "utf-8"));
|
|
4645
4745
|
} catch {
|
|
4646
4746
|
return /* @__PURE__ */ new Map();
|
|
4647
4747
|
}
|
|
@@ -4671,26 +4771,66 @@ function nowIso() {
|
|
|
4671
4771
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
4672
4772
|
}
|
|
4673
4773
|
function ensureConfigDir() {
|
|
4674
|
-
|
|
4774
|
+
fs2.mkdirSync(CTI_HOME, { recursive: true });
|
|
4675
4775
|
}
|
|
4676
4776
|
function readConfigV2File() {
|
|
4777
|
+
if (!fs2.existsSync(CONFIG_V2_PATH)) return null;
|
|
4778
|
+
let content;
|
|
4779
|
+
try {
|
|
4780
|
+
content = fs2.readFileSync(CONFIG_V2_PATH, "utf-8");
|
|
4781
|
+
} catch (error) {
|
|
4782
|
+
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u914D\u7F6E\u6587\u4EF6 ${CONFIG_V2_PATH}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4783
|
+
}
|
|
4784
|
+
let raw;
|
|
4785
|
+
try {
|
|
4786
|
+
raw = JSON.parse(content);
|
|
4787
|
+
} catch (error) {
|
|
4788
|
+
throw new Error(`\u914D\u7F6E\u6587\u4EF6 ${CONFIG_V2_PATH} \u4E0D\u662F\u6709\u6548 JSON\uFF0C\u5DF2\u62D2\u7EDD\u8986\u76D6: ${error instanceof Error ? error.message : String(error)}`);
|
|
4789
|
+
}
|
|
4790
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
4791
|
+
throw new Error(`\u914D\u7F6E\u6587\u4EF6 ${CONFIG_V2_PATH} \u7684\u6839\u8282\u70B9\u5FC5\u987B\u662F\u5BF9\u8C61\uFF0C\u5DF2\u62D2\u7EDD\u8986\u76D6\u3002`);
|
|
4792
|
+
}
|
|
4793
|
+
if (raw.schemaVersion !== 2) {
|
|
4794
|
+
throw new Error(`\u4E0D\u652F\u6301\u914D\u7F6E schemaVersion=${String(raw.schemaVersion)}\uFF0C\u5DF2\u62D2\u7EDD\u7528\u5F53\u524D\u7248\u672C\u8986\u76D6\u3002`);
|
|
4795
|
+
}
|
|
4796
|
+
if (!raw.runtime || typeof raw.runtime !== "object" || Array.isArray(raw.runtime)) {
|
|
4797
|
+
throw new Error(`\u914D\u7F6E\u6587\u4EF6 ${CONFIG_V2_PATH} \u7F3A\u5C11\u6709\u6548\u7684 runtime \u5BF9\u8C61\uFF0C\u5DF2\u62D2\u7EDD\u8986\u76D6\u3002`);
|
|
4798
|
+
}
|
|
4799
|
+
if (!Array.isArray(raw.channels)) {
|
|
4800
|
+
throw new Error(`\u914D\u7F6E\u6587\u4EF6 ${CONFIG_V2_PATH} \u7F3A\u5C11 channels \u6570\u7EC4\uFF0C\u5DF2\u62D2\u7EDD\u8986\u76D6\u3002`);
|
|
4801
|
+
}
|
|
4802
|
+
const rawChannels = raw.channels;
|
|
4803
|
+
const parsed = {
|
|
4804
|
+
...raw,
|
|
4805
|
+
schemaVersion: 2,
|
|
4806
|
+
runtime: {
|
|
4807
|
+
...raw.runtime,
|
|
4808
|
+
provider: normalizeRuntimeProvider(raw.runtime.provider)
|
|
4809
|
+
},
|
|
4810
|
+
channels: normalizeChannelInstances(rawChannels)
|
|
4811
|
+
};
|
|
4812
|
+
Object.defineProperty(parsed, RAW_CHANNELS, { value: rawChannels, enumerable: false });
|
|
4813
|
+
return parsed;
|
|
4814
|
+
}
|
|
4815
|
+
function atomicWriteFile(filePath, content) {
|
|
4816
|
+
ensureConfigDir();
|
|
4817
|
+
const tmpPath = `${filePath}.${process.pid}.${crypto2.randomUUID()}.tmp`;
|
|
4677
4818
|
try {
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4819
|
+
fs2.writeFileSync(tmpPath, content, { mode: 384 });
|
|
4820
|
+
fs2.renameSync(tmpPath, filePath);
|
|
4821
|
+
} finally {
|
|
4822
|
+
try {
|
|
4823
|
+
fs2.rmSync(tmpPath, { force: true });
|
|
4824
|
+
} catch {
|
|
4683
4825
|
}
|
|
4684
|
-
return null;
|
|
4685
|
-
} catch {
|
|
4686
|
-
return null;
|
|
4687
4826
|
}
|
|
4688
4827
|
}
|
|
4689
4828
|
function writeConfigV2File(config) {
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4829
|
+
const serialized = {
|
|
4830
|
+
...config,
|
|
4831
|
+
channels: config[RAW_CHANNELS] || config.channels
|
|
4832
|
+
};
|
|
4833
|
+
atomicWriteFile(CONFIG_V2_PATH, JSON.stringify(serialized, null, 2));
|
|
4694
4834
|
}
|
|
4695
4835
|
function defaultAliasForProvider(provider) {
|
|
4696
4836
|
return provider === "feishu" ? "\u98DE\u4E66" : "\u5FAE\u4FE1";
|
|
@@ -4711,6 +4851,7 @@ function normalizeChannelInstances(value) {
|
|
|
4711
4851
|
const config = record.config && typeof record.config === "object" ? record.config : {};
|
|
4712
4852
|
const timestamp = nowIso();
|
|
4713
4853
|
return [{
|
|
4854
|
+
...record,
|
|
4714
4855
|
id: normalizeChannelId(
|
|
4715
4856
|
typeof record.id === "string" && record.id.trim() ? record.id : buildDefaultChannelId(provider)
|
|
4716
4857
|
),
|
|
@@ -4811,13 +4952,45 @@ function expandConfig(v2) {
|
|
|
4811
4952
|
uiAccessToken: v2.runtime.uiAccessToken || void 0
|
|
4812
4953
|
};
|
|
4813
4954
|
}
|
|
4955
|
+
function mergeRawChannelRecords(current, channels) {
|
|
4956
|
+
const remaining = new Map(channels.map((channel) => [channel.id, channel]));
|
|
4957
|
+
const rawChannels = current?.[RAW_CHANNELS] || current?.channels || [];
|
|
4958
|
+
const merged = [];
|
|
4959
|
+
for (const rawEntry of rawChannels) {
|
|
4960
|
+
if (!rawEntry || typeof rawEntry !== "object" || Array.isArray(rawEntry)) continue;
|
|
4961
|
+
const rawRecord = rawEntry;
|
|
4962
|
+
if (!isSupportedChannelProvider(rawRecord.provider)) {
|
|
4963
|
+
merged.push(rawEntry);
|
|
4964
|
+
continue;
|
|
4965
|
+
}
|
|
4966
|
+
const rawId = normalizeChannelId(
|
|
4967
|
+
typeof rawRecord.id === "string" && rawRecord.id.trim() ? rawRecord.id : buildDefaultChannelId(rawRecord.provider)
|
|
4968
|
+
);
|
|
4969
|
+
const replacement = remaining.get(rawId);
|
|
4970
|
+
if (!replacement) continue;
|
|
4971
|
+
remaining.delete(rawId);
|
|
4972
|
+
const rawConfig = rawRecord.config && typeof rawRecord.config === "object" && !Array.isArray(rawRecord.config) ? rawRecord.config : {};
|
|
4973
|
+
merged.push({
|
|
4974
|
+
...rawRecord,
|
|
4975
|
+
...replacement,
|
|
4976
|
+
config: {
|
|
4977
|
+
...rawConfig,
|
|
4978
|
+
...replacement.config
|
|
4979
|
+
}
|
|
4980
|
+
});
|
|
4981
|
+
}
|
|
4982
|
+
merged.push(...remaining.values());
|
|
4983
|
+
return merged;
|
|
4984
|
+
}
|
|
4814
4985
|
function buildV2FileFromExpandedConfig(config, current) {
|
|
4815
4986
|
const hasExplicitChannels = Array.isArray(config.channels);
|
|
4816
4987
|
let channels = hasExplicitChannels ? [...config.channels || []] : [...current?.channels || []];
|
|
4817
4988
|
channels = normalizeChannelInstances(channels);
|
|
4818
|
-
|
|
4989
|
+
const next = {
|
|
4990
|
+
...current || {},
|
|
4819
4991
|
schemaVersion: 2,
|
|
4820
4992
|
runtime: {
|
|
4993
|
+
...current?.runtime || {},
|
|
4821
4994
|
provider: config.runtime,
|
|
4822
4995
|
defaultWorkspaceRoot: config.defaultWorkspaceRoot,
|
|
4823
4996
|
defaultModel: config.defaultModel,
|
|
@@ -4837,33 +5010,42 @@ function buildV2FileFromExpandedConfig(config, current) {
|
|
|
4837
5010
|
alias: channel.alias?.trim() || defaultAliasForProvider(channel.provider)
|
|
4838
5011
|
}))
|
|
4839
5012
|
};
|
|
5013
|
+
Object.defineProperty(next, RAW_CHANNELS, {
|
|
5014
|
+
value: mergeRawChannelRecords(current, next.channels),
|
|
5015
|
+
enumerable: false
|
|
5016
|
+
});
|
|
5017
|
+
return next;
|
|
4840
5018
|
}
|
|
4841
5019
|
function loadConfig() {
|
|
4842
5020
|
const current = readConfigV2File();
|
|
4843
5021
|
if (current) return expandConfig(current);
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
5022
|
+
return withFileLock(CONFIG_V2_PATH, () => {
|
|
5023
|
+
const concurrentlyCreated = readConfigV2File();
|
|
5024
|
+
if (concurrentlyCreated) return expandConfig(concurrentlyCreated);
|
|
5025
|
+
const legacyEnv = loadRawConfigEnv();
|
|
5026
|
+
if (legacyEnv.size > 0) {
|
|
5027
|
+
const migrated = migrateLegacyEnvToV2(legacyEnv);
|
|
5028
|
+
writeConfigV2File(migrated);
|
|
5029
|
+
return expandConfig(migrated);
|
|
5030
|
+
}
|
|
5031
|
+
const empty = {
|
|
5032
|
+
schemaVersion: 2,
|
|
5033
|
+
runtime: {
|
|
5034
|
+
provider: "codex",
|
|
5035
|
+
defaultWorkspaceRoot: DEFAULT_WORKSPACE_ROOT,
|
|
5036
|
+
defaultMode: "code",
|
|
5037
|
+
historyMessageLimit: 8,
|
|
5038
|
+
streamStatusIdleStartSeconds: DEFAULT_STREAM_STATUS_IDLE_START_SECONDS,
|
|
5039
|
+
streamStatusCheckIntervalSeconds: DEFAULT_STREAM_STATUS_CHECK_INTERVAL_SECONDS,
|
|
5040
|
+
codexSkipGitRepoCheck: true,
|
|
5041
|
+
codexSandboxMode: "workspace-write",
|
|
5042
|
+
codexReasoningEffort: "medium",
|
|
5043
|
+
uiAllowLan: false
|
|
5044
|
+
},
|
|
5045
|
+
channels: []
|
|
5046
|
+
};
|
|
5047
|
+
return expandConfig(empty);
|
|
5048
|
+
});
|
|
4867
5049
|
}
|
|
4868
5050
|
function formatEnvLine(key, value) {
|
|
4869
5051
|
if (value === void 0 || value === "") return "";
|
|
@@ -4871,64 +5053,63 @@ function formatEnvLine(key, value) {
|
|
|
4871
5053
|
`;
|
|
4872
5054
|
}
|
|
4873
5055
|
function saveConfig(config) {
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
fs.renameSync(tmpPath, CONFIG_PATH);
|
|
5056
|
+
withFileLock(CONFIG_V2_PATH, () => {
|
|
5057
|
+
const current = readConfigV2File();
|
|
5058
|
+
const next = buildV2FileFromExpandedConfig(config, current);
|
|
5059
|
+
writeConfigV2File(next);
|
|
5060
|
+
let out = "";
|
|
5061
|
+
out += formatEnvLine("CTI_RUNTIME", next.runtime.provider);
|
|
5062
|
+
out += formatEnvLine(
|
|
5063
|
+
"CTI_ENABLED_CHANNELS",
|
|
5064
|
+
Array.from(new Set(next.channels.filter((channel) => channel.enabled).map((channel) => channel.provider))).join(",")
|
|
5065
|
+
);
|
|
5066
|
+
out += formatEnvLine("CTI_DEFAULT_WORKSPACE_ROOT", next.runtime.defaultWorkspaceRoot);
|
|
5067
|
+
out += formatEnvLine("CTI_DEFAULT_MODEL", next.runtime.defaultModel);
|
|
5068
|
+
out += formatEnvLine("CTI_DEFAULT_MODE", next.runtime.defaultMode);
|
|
5069
|
+
if (next.runtime.historyMessageLimit !== void 0) {
|
|
5070
|
+
out += formatEnvLine("CTI_HISTORY_MESSAGE_LIMIT", String(next.runtime.historyMessageLimit));
|
|
5071
|
+
}
|
|
5072
|
+
if (next.runtime.streamStatusIdleStartSeconds !== void 0) {
|
|
5073
|
+
out += formatEnvLine("CTI_STREAM_STATUS_IDLE_START_SECONDS", String(next.runtime.streamStatusIdleStartSeconds));
|
|
5074
|
+
}
|
|
5075
|
+
if (next.runtime.streamStatusCheckIntervalSeconds !== void 0) {
|
|
5076
|
+
out += formatEnvLine("CTI_STREAM_STATUS_CHECK_INTERVAL_SECONDS", String(next.runtime.streamStatusCheckIntervalSeconds));
|
|
5077
|
+
}
|
|
5078
|
+
if (next.runtime.codexSkipGitRepoCheck !== void 0) {
|
|
5079
|
+
out += formatEnvLine("CTI_CODEX_SKIP_GIT_REPO_CHECK", String(next.runtime.codexSkipGitRepoCheck));
|
|
5080
|
+
}
|
|
5081
|
+
out += formatEnvLine("CTI_CODEX_SANDBOX_MODE", next.runtime.codexSandboxMode);
|
|
5082
|
+
out += formatEnvLine("CTI_CODEX_REASONING_EFFORT", next.runtime.codexReasoningEffort);
|
|
5083
|
+
out += formatEnvLine("CTI_UI_ALLOW_LAN", String(next.runtime.uiAllowLan === true));
|
|
5084
|
+
out += formatEnvLine("CTI_UI_ACCESS_TOKEN", next.runtime.uiAccessToken);
|
|
5085
|
+
const feishu = getChannelByProvider(next, "feishu");
|
|
5086
|
+
const feishuConfig = toFeishuConfig(feishu);
|
|
5087
|
+
if (feishuConfig) {
|
|
5088
|
+
out += formatEnvLine("CTI_FEISHU_APP_ID", feishuConfig.appId);
|
|
5089
|
+
out += formatEnvLine("CTI_FEISHU_APP_SECRET", feishuConfig.appSecret);
|
|
5090
|
+
out += formatEnvLine("CTI_FEISHU_SITE", feishuConfig.site);
|
|
5091
|
+
out += formatEnvLine("CTI_FEISHU_ALLOWED_USERS", feishuConfig.allowedUsers?.join(","));
|
|
5092
|
+
if (feishuConfig.streamingEnabled !== void 0) {
|
|
5093
|
+
out += formatEnvLine("CTI_FEISHU_STREAMING_ENABLED", String(feishuConfig.streamingEnabled));
|
|
5094
|
+
}
|
|
5095
|
+
if (feishuConfig.feedbackMarkdownEnabled !== void 0) {
|
|
5096
|
+
out += formatEnvLine("CTI_FEISHU_COMMAND_MARKDOWN_ENABLED", String(feishuConfig.feedbackMarkdownEnabled));
|
|
5097
|
+
}
|
|
5098
|
+
}
|
|
5099
|
+
const weixin = getChannelByProvider(next, "weixin");
|
|
5100
|
+
const weixinConfig = toWeixinConfig(weixin);
|
|
5101
|
+
if (weixinConfig) {
|
|
5102
|
+
out += formatEnvLine("CTI_WEIXIN_BASE_URL", weixinConfig.baseUrl);
|
|
5103
|
+
out += formatEnvLine("CTI_WEIXIN_CDN_BASE_URL", weixinConfig.cdnBaseUrl);
|
|
5104
|
+
if (weixinConfig.mediaEnabled !== void 0) {
|
|
5105
|
+
out += formatEnvLine("CTI_WEIXIN_MEDIA_ENABLED", String(weixinConfig.mediaEnabled));
|
|
5106
|
+
}
|
|
5107
|
+
if (weixinConfig.feedbackMarkdownEnabled !== void 0) {
|
|
5108
|
+
out += formatEnvLine("CTI_WEIXIN_COMMAND_MARKDOWN_ENABLED", String(weixinConfig.feedbackMarkdownEnabled));
|
|
5109
|
+
}
|
|
5110
|
+
}
|
|
5111
|
+
atomicWriteFile(CONFIG_PATH, out);
|
|
5112
|
+
});
|
|
4932
5113
|
}
|
|
4933
5114
|
function listChannelInstances(config) {
|
|
4934
5115
|
return [...config?.channels || loadConfig().channels || []];
|
|
@@ -5025,10 +5206,10 @@ function configToSettings(config) {
|
|
|
5025
5206
|
}
|
|
5026
5207
|
|
|
5027
5208
|
// src/desktop-sessions.ts
|
|
5028
|
-
import
|
|
5209
|
+
import fs3 from "node:fs";
|
|
5029
5210
|
import os2 from "node:os";
|
|
5030
5211
|
import path2 from "node:path";
|
|
5031
|
-
import
|
|
5212
|
+
import crypto3 from "node:crypto";
|
|
5032
5213
|
import { DatabaseSync } from "node:sqlite";
|
|
5033
5214
|
var ACTIVE_WINDOW_MS = 15 * 60 * 1e3;
|
|
5034
5215
|
var MAX_SESSION_META_BYTES = 4 * 1024 * 1024;
|
|
@@ -5053,13 +5234,13 @@ function getDesktopStateDbPath() {
|
|
|
5053
5234
|
const codexHome = getCodexHome();
|
|
5054
5235
|
let entries;
|
|
5055
5236
|
try {
|
|
5056
|
-
entries =
|
|
5237
|
+
entries = fs3.readdirSync(codexHome, { withFileTypes: true });
|
|
5057
5238
|
} catch {
|
|
5058
5239
|
return null;
|
|
5059
5240
|
}
|
|
5060
5241
|
const candidates = entries.filter((entry) => entry.isFile() && /^state_\d+\.sqlite$/i.test(entry.name)).map((entry) => path2.join(codexHome, entry.name)).sort((left, right) => {
|
|
5061
5242
|
try {
|
|
5062
|
-
return
|
|
5243
|
+
return fs3.statSync(right).mtimeMs - fs3.statSync(left).mtimeMs;
|
|
5063
5244
|
} catch {
|
|
5064
5245
|
return 0;
|
|
5065
5246
|
}
|
|
@@ -5084,10 +5265,10 @@ function isInternalSkillWorkspace(cwd) {
|
|
|
5084
5265
|
}
|
|
5085
5266
|
function loadSavedWorkspaceRoots() {
|
|
5086
5267
|
const statePath = getCodexGlobalStatePath();
|
|
5087
|
-
if (!
|
|
5268
|
+
if (!fs3.existsSync(statePath)) return null;
|
|
5088
5269
|
let parsed;
|
|
5089
5270
|
try {
|
|
5090
|
-
parsed = JSON.parse(
|
|
5271
|
+
parsed = JSON.parse(fs3.readFileSync(statePath, "utf-8"));
|
|
5091
5272
|
} catch {
|
|
5092
5273
|
return null;
|
|
5093
5274
|
}
|
|
@@ -5102,10 +5283,10 @@ function isWithinSavedWorkspaceRoots(cwd, roots) {
|
|
|
5102
5283
|
}
|
|
5103
5284
|
function loadArchivedThreadIds() {
|
|
5104
5285
|
const archivedRoot = getArchivedSessionsRoot();
|
|
5105
|
-
if (!
|
|
5286
|
+
if (!fs3.existsSync(archivedRoot)) return /* @__PURE__ */ new Set();
|
|
5106
5287
|
let entries;
|
|
5107
5288
|
try {
|
|
5108
|
-
entries =
|
|
5289
|
+
entries = fs3.readdirSync(archivedRoot, { withFileTypes: true });
|
|
5109
5290
|
} catch {
|
|
5110
5291
|
return /* @__PURE__ */ new Set();
|
|
5111
5292
|
}
|
|
@@ -5118,13 +5299,13 @@ function loadArchivedThreadIds() {
|
|
|
5118
5299
|
return ids;
|
|
5119
5300
|
}
|
|
5120
5301
|
function readFirstLine(filePath, maxBytes = MAX_SESSION_META_BYTES) {
|
|
5121
|
-
const fd =
|
|
5302
|
+
const fd = fs3.openSync(filePath, "r");
|
|
5122
5303
|
try {
|
|
5123
5304
|
const chunks = [];
|
|
5124
5305
|
let bytesReadTotal = 0;
|
|
5125
5306
|
const buffer = Buffer.alloc(4096);
|
|
5126
5307
|
while (bytesReadTotal < maxBytes) {
|
|
5127
|
-
const bytesRead =
|
|
5308
|
+
const bytesRead = fs3.readSync(fd, buffer, 0, buffer.length, bytesReadTotal);
|
|
5128
5309
|
if (bytesRead <= 0) break;
|
|
5129
5310
|
const slice = Buffer.from(buffer.subarray(0, bytesRead));
|
|
5130
5311
|
chunks.push(slice);
|
|
@@ -5137,31 +5318,31 @@ function readFirstLine(filePath, maxBytes = MAX_SESSION_META_BYTES) {
|
|
|
5137
5318
|
}
|
|
5138
5319
|
return Buffer.concat(chunks).toString("utf-8").split(/\r?\n/, 1)[0] || "";
|
|
5139
5320
|
} finally {
|
|
5140
|
-
|
|
5321
|
+
fs3.closeSync(fd);
|
|
5141
5322
|
}
|
|
5142
5323
|
}
|
|
5143
5324
|
function readFilePrefix(filePath, maxBytes = MAX_SESSION_TITLE_SCAN_BYTES) {
|
|
5144
|
-
const fd =
|
|
5325
|
+
const fd = fs3.openSync(filePath, "r");
|
|
5145
5326
|
try {
|
|
5146
5327
|
const buffer = Buffer.alloc(Math.min(maxBytes, 64 * 1024));
|
|
5147
5328
|
const chunks = [];
|
|
5148
5329
|
let offset = 0;
|
|
5149
5330
|
while (offset < maxBytes) {
|
|
5150
5331
|
const bytesToRead = Math.min(buffer.length, maxBytes - offset);
|
|
5151
|
-
const bytesRead =
|
|
5332
|
+
const bytesRead = fs3.readSync(fd, buffer, 0, bytesToRead, offset);
|
|
5152
5333
|
if (bytesRead <= 0) break;
|
|
5153
5334
|
chunks.push(Buffer.from(buffer.subarray(0, bytesRead)));
|
|
5154
5335
|
offset += bytesRead;
|
|
5155
5336
|
}
|
|
5156
5337
|
return Buffer.concat(chunks).toString("utf-8");
|
|
5157
5338
|
} finally {
|
|
5158
|
-
|
|
5339
|
+
fs3.closeSync(fd);
|
|
5159
5340
|
}
|
|
5160
5341
|
}
|
|
5161
5342
|
function walkSessionFiles(dirPath, target) {
|
|
5162
5343
|
let entries;
|
|
5163
5344
|
try {
|
|
5164
|
-
entries =
|
|
5345
|
+
entries = fs3.readdirSync(dirPath, { withFileTypes: true });
|
|
5165
5346
|
} catch {
|
|
5166
5347
|
return;
|
|
5167
5348
|
}
|
|
@@ -5184,10 +5365,10 @@ function isDesktopLike(meta) {
|
|
|
5184
5365
|
}
|
|
5185
5366
|
function loadThreadIndexEntries(archivedThreadIds) {
|
|
5186
5367
|
const indexPath = getSessionIndexPath();
|
|
5187
|
-
if (!
|
|
5368
|
+
if (!fs3.existsSync(indexPath)) return /* @__PURE__ */ new Map();
|
|
5188
5369
|
let content = "";
|
|
5189
5370
|
try {
|
|
5190
|
-
content =
|
|
5371
|
+
content = fs3.readFileSync(indexPath, "utf-8");
|
|
5191
5372
|
} catch {
|
|
5192
5373
|
return /* @__PURE__ */ new Map();
|
|
5193
5374
|
}
|
|
@@ -5227,7 +5408,7 @@ function parseUpdatedAtValue(value) {
|
|
|
5227
5408
|
}
|
|
5228
5409
|
function loadVisibleDesktopThreads(limit) {
|
|
5229
5410
|
const dbPath = getDesktopStateDbPath();
|
|
5230
|
-
if (!dbPath || !
|
|
5411
|
+
if (!dbPath || !fs3.existsSync(dbPath)) return null;
|
|
5231
5412
|
let db = null;
|
|
5232
5413
|
try {
|
|
5233
5414
|
db = new DatabaseSync(dbPath, { readOnly: true });
|
|
@@ -5294,7 +5475,7 @@ function parseDesktopSession(filePath, threadIndexEntries, archivedThreadIds) {
|
|
|
5294
5475
|
}
|
|
5295
5476
|
let stat;
|
|
5296
5477
|
try {
|
|
5297
|
-
stat =
|
|
5478
|
+
stat = fs3.statSync(filePath);
|
|
5298
5479
|
} catch {
|
|
5299
5480
|
return null;
|
|
5300
5481
|
}
|
|
@@ -5369,7 +5550,7 @@ function isSessionEventLine(line) {
|
|
|
5369
5550
|
}
|
|
5370
5551
|
function listDesktopSessions(limit) {
|
|
5371
5552
|
const root = getCodexSessionsRoot();
|
|
5372
|
-
if (!
|
|
5553
|
+
if (!fs3.existsSync(root)) return [];
|
|
5373
5554
|
const archivedThreadIds = loadArchivedThreadIds();
|
|
5374
5555
|
const threadIndexEntries = loadThreadIndexEntries(archivedThreadIds);
|
|
5375
5556
|
const savedWorkspaceRoots = loadSavedWorkspaceRoots();
|
|
@@ -5407,9 +5588,17 @@ function listDesktopSessions(limit) {
|
|
|
5407
5588
|
}
|
|
5408
5589
|
return sessions.sort((a, b) => b.lastEventAt.localeCompare(a.lastEventAt)).slice(0, typeof limit === "number" && Number.isFinite(limit) && limit > 0 ? Math.max(1, Math.floor(limit)) : void 0);
|
|
5409
5590
|
}
|
|
5591
|
+
var DESKTOP_SESSION_LOOKUP_CACHE_TTL_MS = 1e3;
|
|
5592
|
+
var desktopSessionLookupCache = null;
|
|
5410
5593
|
function getDesktopSessionByThreadId(threadId) {
|
|
5411
|
-
const
|
|
5412
|
-
|
|
5594
|
+
const timestamp = Date.now();
|
|
5595
|
+
if (!desktopSessionLookupCache || desktopSessionLookupCache.expiresAt <= timestamp) {
|
|
5596
|
+
desktopSessionLookupCache = {
|
|
5597
|
+
expiresAt: timestamp + DESKTOP_SESSION_LOOKUP_CACHE_TTL_MS,
|
|
5598
|
+
sessions: new Map(listDesktopSessions().map((session) => [session.threadId, session]))
|
|
5599
|
+
};
|
|
5600
|
+
}
|
|
5601
|
+
return desktopSessionLookupCache.sessions.get(threadId) || null;
|
|
5413
5602
|
}
|
|
5414
5603
|
function isArchivedDesktopThread(threadId) {
|
|
5415
5604
|
return loadArchivedThreadIds().has(threadId);
|
|
@@ -5727,25 +5916,25 @@ function removeBinding(store, bindingId) {
|
|
|
5727
5916
|
}
|
|
5728
5917
|
|
|
5729
5918
|
// src/service-manager.ts
|
|
5730
|
-
import
|
|
5919
|
+
import fs5 from "node:fs";
|
|
5731
5920
|
import os3 from "node:os";
|
|
5732
5921
|
import path5 from "node:path";
|
|
5733
5922
|
import { spawn } from "node:child_process";
|
|
5734
5923
|
import { fileURLToPath } from "node:url";
|
|
5735
5924
|
|
|
5736
5925
|
// src/bridge-instance-lock.ts
|
|
5737
|
-
import
|
|
5926
|
+
import fs4 from "node:fs";
|
|
5738
5927
|
import path4 from "node:path";
|
|
5739
5928
|
var runtimeDir = path4.join(CTI_HOME, "runtime");
|
|
5740
5929
|
var bridgeInstanceLockFile = path4.join(runtimeDir, "bridge.instance.lock");
|
|
5741
5930
|
function readJsonFile(filePath, fallback) {
|
|
5742
5931
|
try {
|
|
5743
|
-
return JSON.parse(
|
|
5932
|
+
return JSON.parse(fs4.readFileSync(filePath, "utf-8"));
|
|
5744
5933
|
} catch {
|
|
5745
5934
|
return fallback;
|
|
5746
5935
|
}
|
|
5747
5936
|
}
|
|
5748
|
-
function
|
|
5937
|
+
function isProcessAlive2(pid) {
|
|
5749
5938
|
if (!pid) return false;
|
|
5750
5939
|
try {
|
|
5751
5940
|
process.kill(pid, 0);
|
|
@@ -5761,11 +5950,11 @@ function readBridgeInstanceLock(filePath = bridgeInstanceLockFile) {
|
|
|
5761
5950
|
if (!Number.isFinite(pid) || pid <= 0 || !createdAt) return null;
|
|
5762
5951
|
return { pid, createdAt };
|
|
5763
5952
|
}
|
|
5764
|
-
function clearStaleBridgeInstanceLock(filePath = bridgeInstanceLockFile, isAlive =
|
|
5953
|
+
function clearStaleBridgeInstanceLock(filePath = bridgeInstanceLockFile, isAlive = isProcessAlive2) {
|
|
5765
5954
|
const existing = readBridgeInstanceLock(filePath);
|
|
5766
5955
|
if (existing && isAlive(existing.pid)) return;
|
|
5767
5956
|
try {
|
|
5768
|
-
|
|
5957
|
+
fs4.unlinkSync(filePath);
|
|
5769
5958
|
} catch {
|
|
5770
5959
|
}
|
|
5771
5960
|
}
|
|
@@ -5786,26 +5975,26 @@ var npmUninstallLogFile = path5.join(runtimeDir2, "npm-uninstall.log");
|
|
|
5786
5975
|
var WINDOWS_HIDE = process.platform === "win32" ? { windowsHide: true } : {};
|
|
5787
5976
|
var BRIDGE_START_LOCK_STALE_MS = 3e4;
|
|
5788
5977
|
function ensureDirs() {
|
|
5789
|
-
|
|
5790
|
-
|
|
5978
|
+
fs5.mkdirSync(runtimeDir2, { recursive: true });
|
|
5979
|
+
fs5.mkdirSync(logsDir, { recursive: true });
|
|
5791
5980
|
}
|
|
5792
5981
|
function readJsonFile2(filePath, fallback) {
|
|
5793
5982
|
try {
|
|
5794
|
-
return JSON.parse(
|
|
5983
|
+
return JSON.parse(fs5.readFileSync(filePath, "utf-8"));
|
|
5795
5984
|
} catch {
|
|
5796
5985
|
return fallback;
|
|
5797
5986
|
}
|
|
5798
5987
|
}
|
|
5799
5988
|
function readPid(filePath) {
|
|
5800
5989
|
try {
|
|
5801
|
-
const raw =
|
|
5990
|
+
const raw = fs5.readFileSync(filePath, "utf-8").trim();
|
|
5802
5991
|
const pid = Number(raw);
|
|
5803
5992
|
return Number.isFinite(pid) ? pid : void 0;
|
|
5804
5993
|
} catch {
|
|
5805
5994
|
return void 0;
|
|
5806
5995
|
}
|
|
5807
5996
|
}
|
|
5808
|
-
function
|
|
5997
|
+
function isProcessAlive3(pid) {
|
|
5809
5998
|
if (!pid) return false;
|
|
5810
5999
|
try {
|
|
5811
6000
|
process.kill(pid, 0);
|
|
@@ -5823,7 +6012,7 @@ function collectTrackedBridgePids(bridgePid, statusPid, instanceLockPid) {
|
|
|
5823
6012
|
}
|
|
5824
6013
|
return [...unique];
|
|
5825
6014
|
}
|
|
5826
|
-
function resolveTrackedBridgePid(bridgePid, statusPid, instanceLockPid, isAlive =
|
|
6015
|
+
function resolveTrackedBridgePid(bridgePid, statusPid, instanceLockPid, isAlive = isProcessAlive3) {
|
|
5827
6016
|
if (isAlive(bridgePid)) return bridgePid;
|
|
5828
6017
|
if (isAlive(statusPid)) return statusPid;
|
|
5829
6018
|
if (isAlive(instanceLockPid)) return instanceLockPid;
|
|
@@ -5839,7 +6028,7 @@ function getTrackedBridgePids(status) {
|
|
|
5839
6028
|
}
|
|
5840
6029
|
function clearBridgePidFile() {
|
|
5841
6030
|
try {
|
|
5842
|
-
|
|
6031
|
+
fs5.unlinkSync(bridgePidFile);
|
|
5843
6032
|
} catch {
|
|
5844
6033
|
}
|
|
5845
6034
|
}
|
|
@@ -5854,7 +6043,7 @@ function isBridgeStartLockStale(lock, options = {}) {
|
|
|
5854
6043
|
if (!lock) return true;
|
|
5855
6044
|
const nowMs = options.nowMs ?? Date.now();
|
|
5856
6045
|
const staleMs = options.staleMs ?? BRIDGE_START_LOCK_STALE_MS;
|
|
5857
|
-
const isAlive = options.isAlive ??
|
|
6046
|
+
const isAlive = options.isAlive ?? isProcessAlive3;
|
|
5858
6047
|
const createdAtMs = Date.parse(lock.createdAt);
|
|
5859
6048
|
if (!Number.isFinite(createdAtMs)) return true;
|
|
5860
6049
|
if (!isAlive(lock.pid)) return true;
|
|
@@ -5870,7 +6059,7 @@ function tryAcquireBridgeStartLock(options = {}) {
|
|
|
5870
6059
|
};
|
|
5871
6060
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
5872
6061
|
try {
|
|
5873
|
-
|
|
6062
|
+
fs5.writeFileSync(filePath, JSON.stringify(payload, null, 2), { encoding: "utf-8", flag: "wx" });
|
|
5874
6063
|
return { acquired: true };
|
|
5875
6064
|
} catch (error) {
|
|
5876
6065
|
const code = error.code;
|
|
@@ -5884,7 +6073,7 @@ function tryAcquireBridgeStartLock(options = {}) {
|
|
|
5884
6073
|
return { acquired: false, holderPid: existing2?.pid };
|
|
5885
6074
|
}
|
|
5886
6075
|
try {
|
|
5887
|
-
|
|
6076
|
+
fs5.unlinkSync(filePath);
|
|
5888
6077
|
} catch {
|
|
5889
6078
|
}
|
|
5890
6079
|
}
|
|
@@ -5896,14 +6085,14 @@ function releaseBridgeStartLock(filePath = bridgeStartLockFile, ownerPid = proce
|
|
|
5896
6085
|
const existing = readBridgeStartLock(filePath);
|
|
5897
6086
|
if (!existing) {
|
|
5898
6087
|
try {
|
|
5899
|
-
|
|
6088
|
+
fs5.unlinkSync(filePath);
|
|
5900
6089
|
} catch {
|
|
5901
6090
|
}
|
|
5902
6091
|
return;
|
|
5903
6092
|
}
|
|
5904
6093
|
if (existing.pid !== ownerPid) return;
|
|
5905
6094
|
try {
|
|
5906
|
-
|
|
6095
|
+
fs5.unlinkSync(filePath);
|
|
5907
6096
|
} catch {
|
|
5908
6097
|
}
|
|
5909
6098
|
}
|
|
@@ -5966,7 +6155,7 @@ function getBridgeStatus() {
|
|
|
5966
6155
|
status.pid,
|
|
5967
6156
|
readBridgeInstanceLock()?.pid
|
|
5968
6157
|
);
|
|
5969
|
-
if (!
|
|
6158
|
+
if (!isProcessAlive3(pid)) {
|
|
5970
6159
|
return {
|
|
5971
6160
|
...status,
|
|
5972
6161
|
pid,
|
|
@@ -5981,7 +6170,7 @@ function getBridgeStatus() {
|
|
|
5981
6170
|
}
|
|
5982
6171
|
function getUiServerStatus() {
|
|
5983
6172
|
const status = readJsonFile2(uiStatusFile, { running: false, port: uiPort });
|
|
5984
|
-
if (!
|
|
6173
|
+
if (!isProcessAlive3(status.pid)) {
|
|
5985
6174
|
return {
|
|
5986
6175
|
...status,
|
|
5987
6176
|
running: false,
|
|
@@ -6051,7 +6240,7 @@ async function waitForBridgeStartupTurn(timeoutMs = 2e4) {
|
|
|
6051
6240
|
async function startBridge() {
|
|
6052
6241
|
ensureDirs();
|
|
6053
6242
|
const current = getBridgeStatus();
|
|
6054
|
-
const extraAlivePids = getTrackedBridgePids(current).filter((pid) => pid !== current.pid &&
|
|
6243
|
+
const extraAlivePids = getTrackedBridgePids(current).filter((pid) => pid !== current.pid && isProcessAlive3(pid));
|
|
6055
6244
|
if (current.running && extraAlivePids.length === 0) return current;
|
|
6056
6245
|
if (current.running && extraAlivePids.length > 0) {
|
|
6057
6246
|
await stopBridge();
|
|
@@ -6076,17 +6265,17 @@ async function startBridge() {
|
|
|
6076
6265
|
startLockHeld = true;
|
|
6077
6266
|
try {
|
|
6078
6267
|
const currentAfterLock = getBridgeStatus();
|
|
6079
|
-
const extraAlivePidsAfterLock = getTrackedBridgePids(currentAfterLock).filter((pid) => pid !== currentAfterLock.pid &&
|
|
6268
|
+
const extraAlivePidsAfterLock = getTrackedBridgePids(currentAfterLock).filter((pid) => pid !== currentAfterLock.pid && isProcessAlive3(pid));
|
|
6080
6269
|
if (currentAfterLock.running && extraAlivePidsAfterLock.length === 0) return currentAfterLock;
|
|
6081
6270
|
if (currentAfterLock.running && extraAlivePidsAfterLock.length > 0) {
|
|
6082
6271
|
await stopBridge();
|
|
6083
6272
|
}
|
|
6084
6273
|
const daemonEntry = path5.join(packageRoot, "dist", "daemon.mjs");
|
|
6085
|
-
if (!
|
|
6274
|
+
if (!fs5.existsSync(daemonEntry)) {
|
|
6086
6275
|
throw new Error(`Daemon bundle not found at ${daemonEntry}. Run npm run build first.`);
|
|
6087
6276
|
}
|
|
6088
|
-
const stdoutFd =
|
|
6089
|
-
const stderrFd =
|
|
6277
|
+
const stdoutFd = fs5.openSync(path5.join(logsDir, "bridge-launcher.out.log"), "a");
|
|
6278
|
+
const stderrFd = fs5.openSync(path5.join(logsDir, "bridge-launcher.err.log"), "a");
|
|
6090
6279
|
const child = spawn(process.execPath, [daemonEntry], {
|
|
6091
6280
|
cwd: packageRoot,
|
|
6092
6281
|
detached: true,
|
|
@@ -6110,7 +6299,7 @@ async function startBridge() {
|
|
|
6110
6299
|
}
|
|
6111
6300
|
async function stopBridge() {
|
|
6112
6301
|
const status = readJsonFile2(bridgeStatusFile, { running: false });
|
|
6113
|
-
const pids = getTrackedBridgePids(status).filter((pid) =>
|
|
6302
|
+
const pids = getTrackedBridgePids(status).filter((pid) => isProcessAlive3(pid));
|
|
6114
6303
|
if (pids.length === 0) {
|
|
6115
6304
|
clearBridgePidFile();
|
|
6116
6305
|
clearStaleBridgeInstanceLock();
|
|
@@ -6135,7 +6324,7 @@ async function stopBridge() {
|
|
|
6135
6324
|
}
|
|
6136
6325
|
const startedAt = Date.now();
|
|
6137
6326
|
while (Date.now() - startedAt < 1e4) {
|
|
6138
|
-
if (pids.every((pid) => !
|
|
6327
|
+
if (pids.every((pid) => !isProcessAlive3(pid))) {
|
|
6139
6328
|
clearBridgePidFile();
|
|
6140
6329
|
clearStaleBridgeInstanceLock();
|
|
6141
6330
|
return getBridgeStatus();
|
|
@@ -6207,30 +6396,30 @@ async function getBridgeAutostartStatus() {
|
|
|
6207
6396
|
function getBridgeLogs(lines = 200) {
|
|
6208
6397
|
ensureDirs();
|
|
6209
6398
|
const filePath = path5.join(logsDir, "bridge.log");
|
|
6210
|
-
if (!
|
|
6211
|
-
const all =
|
|
6399
|
+
if (!fs5.existsSync(filePath)) return "";
|
|
6400
|
+
const all = fs5.readFileSync(filePath, "utf-8").split(/\r?\n/);
|
|
6212
6401
|
return all.slice(Math.max(0, all.length - lines)).join("\n");
|
|
6213
6402
|
}
|
|
6214
6403
|
function writeUiServerStatus(status) {
|
|
6215
6404
|
ensureDirs();
|
|
6216
|
-
|
|
6405
|
+
fs5.writeFileSync(uiStatusFile, JSON.stringify(status, null, 2), "utf-8");
|
|
6217
6406
|
}
|
|
6218
6407
|
async function installCodexIntegration() {
|
|
6219
6408
|
const sourceSkill = path5.join(packageRoot, "SKILL.md");
|
|
6220
|
-
if (!
|
|
6409
|
+
if (!fs5.existsSync(sourceSkill)) {
|
|
6221
6410
|
throw new Error(`SKILL.md not found at ${sourceSkill}`);
|
|
6222
6411
|
}
|
|
6223
6412
|
const skillsDir = path5.join(os3.homedir(), ".codex", "skills");
|
|
6224
6413
|
const targetDir = path5.join(skillsDir, "codex-to-im");
|
|
6225
|
-
|
|
6226
|
-
if (
|
|
6414
|
+
fs5.mkdirSync(skillsDir, { recursive: true });
|
|
6415
|
+
if (fs5.existsSync(targetDir)) {
|
|
6227
6416
|
return { targetDir, method: "existing" };
|
|
6228
6417
|
}
|
|
6229
6418
|
try {
|
|
6230
|
-
|
|
6419
|
+
fs5.symlinkSync(packageRoot, targetDir, process.platform === "win32" ? "junction" : "dir");
|
|
6231
6420
|
return { targetDir, method: "junction" };
|
|
6232
6421
|
} catch {
|
|
6233
|
-
|
|
6422
|
+
fs5.cpSync(packageRoot, targetDir, {
|
|
6234
6423
|
recursive: true,
|
|
6235
6424
|
filter: (source) => {
|
|
6236
6425
|
const relative = path5.relative(packageRoot, source);
|
|
@@ -6245,36 +6434,91 @@ async function installCodexIntegration() {
|
|
|
6245
6434
|
}
|
|
6246
6435
|
function isCodexIntegrationInstalled() {
|
|
6247
6436
|
const targetDir = path5.join(os3.homedir(), ".codex", "skills", "codex-to-im");
|
|
6248
|
-
return
|
|
6437
|
+
return fs5.existsSync(path5.join(targetDir, "SKILL.md"));
|
|
6249
6438
|
}
|
|
6250
6439
|
|
|
6251
6440
|
// src/store.ts
|
|
6252
|
-
import
|
|
6441
|
+
import fs7 from "node:fs";
|
|
6442
|
+
import path7 from "node:path";
|
|
6443
|
+
import crypto5 from "node:crypto";
|
|
6444
|
+
|
|
6445
|
+
// src/atomic-file.ts
|
|
6446
|
+
import crypto4 from "node:crypto";
|
|
6447
|
+
import fs6 from "node:fs";
|
|
6253
6448
|
import path6 from "node:path";
|
|
6254
|
-
|
|
6255
|
-
var
|
|
6256
|
-
|
|
6449
|
+
var RETRYABLE_RENAME_CODES = /* @__PURE__ */ new Set(["EACCES", "EBUSY", "EPERM"]);
|
|
6450
|
+
var WAIT_ARRAY2 = new Int32Array(new SharedArrayBuffer(4));
|
|
6451
|
+
function defaultSleepSync(delayMs) {
|
|
6452
|
+
Atomics.wait(WAIT_ARRAY2, 0, 0, Math.max(1, delayMs));
|
|
6453
|
+
}
|
|
6454
|
+
function isRetryableRenameError(error) {
|
|
6455
|
+
return RETRYABLE_RENAME_CODES.has(error?.code || "");
|
|
6456
|
+
}
|
|
6457
|
+
function renameFileWithRetrySync(source, destination, options = {}) {
|
|
6458
|
+
const maxAttempts = Math.max(1, options.maxAttempts ?? 8);
|
|
6459
|
+
const baseDelayMs = Math.max(1, options.baseDelayMs ?? 15);
|
|
6460
|
+
const renameSync = options.renameSync ?? fs6.renameSync;
|
|
6461
|
+
const sleepSync2 = options.sleepSync ?? defaultSleepSync;
|
|
6462
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
6463
|
+
try {
|
|
6464
|
+
renameSync(source, destination);
|
|
6465
|
+
return;
|
|
6466
|
+
} catch (error) {
|
|
6467
|
+
if (!isRetryableRenameError(error) || attempt === maxAttempts - 1) {
|
|
6468
|
+
throw error;
|
|
6469
|
+
}
|
|
6470
|
+
sleepSync2(Math.min(250, baseDelayMs * 2 ** attempt));
|
|
6471
|
+
}
|
|
6472
|
+
}
|
|
6473
|
+
}
|
|
6474
|
+
function atomicWriteFileSync(filePath, data) {
|
|
6475
|
+
fs6.mkdirSync(path6.dirname(filePath), { recursive: true });
|
|
6476
|
+
const tmpPath = `${filePath}.${process.pid}.${crypto4.randomUUID()}.tmp`;
|
|
6477
|
+
try {
|
|
6478
|
+
fs6.writeFileSync(tmpPath, data, "utf-8");
|
|
6479
|
+
renameFileWithRetrySync(tmpPath, filePath);
|
|
6480
|
+
} finally {
|
|
6481
|
+
try {
|
|
6482
|
+
fs6.rmSync(tmpPath, { force: true });
|
|
6483
|
+
} catch {
|
|
6484
|
+
}
|
|
6485
|
+
}
|
|
6486
|
+
}
|
|
6487
|
+
|
|
6488
|
+
// src/store.ts
|
|
6489
|
+
var DATA_DIR = path7.join(CTI_HOME, "data");
|
|
6490
|
+
var MESSAGES_DIR = path7.join(DATA_DIR, "messages");
|
|
6491
|
+
var SESSIONS_PATH = path7.join(DATA_DIR, "sessions.json");
|
|
6492
|
+
var BINDINGS_PATH = path7.join(DATA_DIR, "bindings.json");
|
|
6493
|
+
var PERMISSIONS_PATH = path7.join(DATA_DIR, "permissions.json");
|
|
6494
|
+
var OFFSETS_PATH = path7.join(DATA_DIR, "offsets.json");
|
|
6495
|
+
var DEDUP_PATH = path7.join(DATA_DIR, "dedup.json");
|
|
6496
|
+
var AUDIT_PATH = path7.join(DATA_DIR, "audit.json");
|
|
6497
|
+
var DEFAULT_DEDUP_TTL_MS = 5 * 60 * 1e3;
|
|
6498
|
+
var INBOUND_DEDUP_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6257
6499
|
function ensureDir(dir) {
|
|
6258
|
-
|
|
6500
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
6259
6501
|
}
|
|
6260
6502
|
function atomicWrite(filePath, data) {
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6503
|
+
atomicWriteFileSync(filePath, data);
|
|
6504
|
+
}
|
|
6505
|
+
function dedupTtlMs(key) {
|
|
6506
|
+
return key.startsWith("inbound:") ? INBOUND_DEDUP_TTL_MS : DEFAULT_DEDUP_TTL_MS;
|
|
6264
6507
|
}
|
|
6265
6508
|
function readJson(filePath, fallback) {
|
|
6266
6509
|
try {
|
|
6267
|
-
const raw =
|
|
6510
|
+
const raw = fs7.readFileSync(filePath, "utf-8");
|
|
6268
6511
|
return JSON.parse(raw);
|
|
6269
|
-
} catch {
|
|
6270
|
-
return fallback;
|
|
6512
|
+
} catch (error) {
|
|
6513
|
+
if (error.code === "ENOENT") return fallback;
|
|
6514
|
+
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6 JSON \u6570\u636E\u6587\u4EF6 ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
6271
6515
|
}
|
|
6272
6516
|
}
|
|
6273
6517
|
function writeJson(filePath, data) {
|
|
6274
6518
|
atomicWrite(filePath, JSON.stringify(data, null, 2));
|
|
6275
6519
|
}
|
|
6276
6520
|
function uuid() {
|
|
6277
|
-
return
|
|
6521
|
+
return crypto5.randomUUID();
|
|
6278
6522
|
}
|
|
6279
6523
|
function now() {
|
|
6280
6524
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -6333,38 +6577,38 @@ var JsonFileStore = class {
|
|
|
6333
6577
|
this.reloadSessions();
|
|
6334
6578
|
this.reloadBindings();
|
|
6335
6579
|
const perms = readJson(
|
|
6336
|
-
|
|
6580
|
+
PERMISSIONS_PATH,
|
|
6337
6581
|
{}
|
|
6338
6582
|
);
|
|
6339
6583
|
for (const [id, p] of Object.entries(perms)) {
|
|
6340
6584
|
this.permissionLinks.set(id, p);
|
|
6341
6585
|
}
|
|
6342
6586
|
const offsets = readJson(
|
|
6343
|
-
|
|
6587
|
+
OFFSETS_PATH,
|
|
6344
6588
|
{}
|
|
6345
6589
|
);
|
|
6346
6590
|
for (const [k, v] of Object.entries(offsets)) {
|
|
6347
6591
|
this.offsets.set(k, v);
|
|
6348
6592
|
}
|
|
6349
6593
|
const dedup = readJson(
|
|
6350
|
-
|
|
6594
|
+
DEDUP_PATH,
|
|
6351
6595
|
{}
|
|
6352
6596
|
);
|
|
6353
6597
|
for (const [k, v] of Object.entries(dedup)) {
|
|
6354
6598
|
this.dedupKeys.set(k, v);
|
|
6355
6599
|
}
|
|
6356
|
-
this.auditLog = readJson(
|
|
6600
|
+
this.auditLog = readJson(AUDIT_PATH, []);
|
|
6357
6601
|
}
|
|
6358
6602
|
reloadSessions() {
|
|
6359
6603
|
const sessions = readJson(
|
|
6360
|
-
|
|
6604
|
+
SESSIONS_PATH,
|
|
6361
6605
|
{}
|
|
6362
6606
|
);
|
|
6363
6607
|
this.sessions = new Map(Object.entries(sessions));
|
|
6364
6608
|
}
|
|
6365
6609
|
reloadBindings() {
|
|
6366
6610
|
const bindings = readJson(
|
|
6367
|
-
|
|
6611
|
+
BINDINGS_PATH,
|
|
6368
6612
|
{}
|
|
6369
6613
|
);
|
|
6370
6614
|
const normalized = /* @__PURE__ */ new Map();
|
|
@@ -6378,57 +6622,104 @@ var JsonFileStore = class {
|
|
|
6378
6622
|
}
|
|
6379
6623
|
this.bindings = normalized;
|
|
6380
6624
|
if (changed) {
|
|
6381
|
-
|
|
6625
|
+
withFileLock(BINDINGS_PATH, () => {
|
|
6626
|
+
const latest = readJson(BINDINGS_PATH, {});
|
|
6627
|
+
this.bindings = new Map(Object.values(latest).map((binding) => {
|
|
6628
|
+
const upgraded = upgradeLegacyBinding(binding);
|
|
6629
|
+
return [`${upgraded.channelType}:${upgraded.chatId}`, upgraded];
|
|
6630
|
+
}));
|
|
6631
|
+
this.persistBindings();
|
|
6632
|
+
});
|
|
6382
6633
|
}
|
|
6383
6634
|
}
|
|
6635
|
+
reloadPermissions() {
|
|
6636
|
+
this.permissionLinks = new Map(Object.entries(
|
|
6637
|
+
readJson(PERMISSIONS_PATH, {})
|
|
6638
|
+
));
|
|
6639
|
+
}
|
|
6640
|
+
reloadOffsets() {
|
|
6641
|
+
this.offsets = new Map(Object.entries(readJson(OFFSETS_PATH, {})));
|
|
6642
|
+
}
|
|
6643
|
+
reloadDedup() {
|
|
6644
|
+
this.dedupKeys = new Map(Object.entries(readJson(DEDUP_PATH, {})));
|
|
6645
|
+
}
|
|
6646
|
+
reloadAudit() {
|
|
6647
|
+
this.auditLog = readJson(AUDIT_PATH, []);
|
|
6648
|
+
}
|
|
6384
6649
|
persistSessions() {
|
|
6385
6650
|
writeJson(
|
|
6386
|
-
|
|
6651
|
+
SESSIONS_PATH,
|
|
6387
6652
|
Object.fromEntries(this.sessions)
|
|
6388
6653
|
);
|
|
6389
6654
|
}
|
|
6390
6655
|
persistBindings() {
|
|
6391
6656
|
writeJson(
|
|
6392
|
-
|
|
6657
|
+
BINDINGS_PATH,
|
|
6393
6658
|
Object.fromEntries(this.bindings)
|
|
6394
6659
|
);
|
|
6395
6660
|
}
|
|
6396
6661
|
persistPermissions() {
|
|
6397
6662
|
writeJson(
|
|
6398
|
-
|
|
6663
|
+
PERMISSIONS_PATH,
|
|
6399
6664
|
Object.fromEntries(this.permissionLinks)
|
|
6400
6665
|
);
|
|
6401
6666
|
}
|
|
6402
6667
|
persistOffsets() {
|
|
6403
6668
|
writeJson(
|
|
6404
|
-
|
|
6669
|
+
OFFSETS_PATH,
|
|
6405
6670
|
Object.fromEntries(this.offsets)
|
|
6406
6671
|
);
|
|
6407
6672
|
}
|
|
6408
6673
|
persistDedup() {
|
|
6409
6674
|
writeJson(
|
|
6410
|
-
|
|
6675
|
+
DEDUP_PATH,
|
|
6411
6676
|
Object.fromEntries(this.dedupKeys)
|
|
6412
6677
|
);
|
|
6413
6678
|
}
|
|
6414
6679
|
persistAudit() {
|
|
6415
|
-
writeJson(
|
|
6680
|
+
writeJson(AUDIT_PATH, this.auditLog);
|
|
6416
6681
|
}
|
|
6417
6682
|
persistMessages(sessionId) {
|
|
6418
6683
|
const msgs = this.messages.get(sessionId) || [];
|
|
6419
|
-
writeJson(
|
|
6684
|
+
writeJson(path7.join(MESSAGES_DIR, `${sessionId}.json`), msgs);
|
|
6420
6685
|
}
|
|
6421
6686
|
loadMessages(sessionId) {
|
|
6422
6687
|
if (this.messages.has(sessionId)) {
|
|
6423
6688
|
return this.messages.get(sessionId);
|
|
6424
6689
|
}
|
|
6425
6690
|
const msgs = readJson(
|
|
6426
|
-
|
|
6691
|
+
path7.join(MESSAGES_DIR, `${sessionId}.json`),
|
|
6427
6692
|
[]
|
|
6428
6693
|
);
|
|
6429
6694
|
this.messages.set(sessionId, msgs);
|
|
6430
6695
|
return msgs;
|
|
6431
6696
|
}
|
|
6697
|
+
mutateBindings(mutation) {
|
|
6698
|
+
return withFileLock(BINDINGS_PATH, () => {
|
|
6699
|
+
this.reloadBindings();
|
|
6700
|
+
const result = mutation();
|
|
6701
|
+
this.persistBindings();
|
|
6702
|
+
return result;
|
|
6703
|
+
});
|
|
6704
|
+
}
|
|
6705
|
+
mutateSessions(mutation) {
|
|
6706
|
+
return withFileLock(SESSIONS_PATH, () => {
|
|
6707
|
+
this.reloadSessions();
|
|
6708
|
+
const result = mutation();
|
|
6709
|
+
this.persistSessions();
|
|
6710
|
+
return result;
|
|
6711
|
+
});
|
|
6712
|
+
}
|
|
6713
|
+
mutateMessages(sessionId, mutation) {
|
|
6714
|
+
const messagePath = path7.join(MESSAGES_DIR, `${sessionId}.json`);
|
|
6715
|
+
return withFileLock(messagePath, () => {
|
|
6716
|
+
const messages = readJson(messagePath, []);
|
|
6717
|
+
this.messages.set(sessionId, messages);
|
|
6718
|
+
const result = mutation(messages);
|
|
6719
|
+
this.persistMessages(sessionId);
|
|
6720
|
+
return result;
|
|
6721
|
+
});
|
|
6722
|
+
}
|
|
6432
6723
|
// ── Settings ──
|
|
6433
6724
|
refreshSettings() {
|
|
6434
6725
|
if (!this.dynamicSettings) return;
|
|
@@ -6451,66 +6742,65 @@ var JsonFileStore = class {
|
|
|
6451
6742
|
return this.bindings.get(`${channelType}:${chatId}`) ?? null;
|
|
6452
6743
|
}
|
|
6453
6744
|
upsertChannelBinding(data) {
|
|
6454
|
-
this.
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6745
|
+
return this.mutateBindings(() => {
|
|
6746
|
+
const key = `${data.channelType}:${data.chatId}`;
|
|
6747
|
+
const existing = this.bindings.get(key);
|
|
6748
|
+
if (existing) {
|
|
6749
|
+
const updated = {
|
|
6750
|
+
...existing,
|
|
6751
|
+
codepilotSessionId: data.codepilotSessionId,
|
|
6752
|
+
sdkSessionId: data.sdkSessionId ?? existing.sdkSessionId,
|
|
6753
|
+
channelProvider: data.channelProvider ?? existing.channelProvider,
|
|
6754
|
+
channelAlias: data.channelAlias ?? existing.channelAlias,
|
|
6755
|
+
chatUserId: data.chatUserId ?? existing.chatUserId,
|
|
6756
|
+
chatDisplayName: data.chatDisplayName ?? existing.chatDisplayName,
|
|
6757
|
+
workingDirectory: data.workingDirectory,
|
|
6758
|
+
model: data.model,
|
|
6759
|
+
mode: data.mode ?? existing.mode,
|
|
6760
|
+
updatedAt: now()
|
|
6761
|
+
};
|
|
6762
|
+
this.bindings.set(key, updated);
|
|
6763
|
+
return updated;
|
|
6764
|
+
}
|
|
6765
|
+
const binding = {
|
|
6766
|
+
id: uuid(),
|
|
6767
|
+
channelType: data.channelType,
|
|
6768
|
+
channelProvider: data.channelProvider,
|
|
6769
|
+
channelAlias: data.channelAlias,
|
|
6770
|
+
chatId: data.chatId,
|
|
6771
|
+
chatUserId: data.chatUserId,
|
|
6772
|
+
chatDisplayName: data.chatDisplayName,
|
|
6460
6773
|
codepilotSessionId: data.codepilotSessionId,
|
|
6461
|
-
sdkSessionId: data.sdkSessionId ??
|
|
6462
|
-
channelProvider: data.channelProvider ?? existing.channelProvider,
|
|
6463
|
-
channelAlias: data.channelAlias ?? existing.channelAlias,
|
|
6464
|
-
chatUserId: data.chatUserId ?? existing.chatUserId,
|
|
6465
|
-
chatDisplayName: data.chatDisplayName ?? existing.chatDisplayName,
|
|
6774
|
+
sdkSessionId: data.sdkSessionId ?? "",
|
|
6466
6775
|
workingDirectory: data.workingDirectory,
|
|
6467
6776
|
model: data.model,
|
|
6468
|
-
mode: data.mode
|
|
6777
|
+
mode: data.mode || this.getSetting("bridge_default_mode") || "code",
|
|
6778
|
+
active: true,
|
|
6779
|
+
createdAt: now(),
|
|
6469
6780
|
updatedAt: now()
|
|
6470
6781
|
};
|
|
6471
|
-
this.bindings.set(key,
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
}
|
|
6475
|
-
const binding = {
|
|
6476
|
-
id: uuid(),
|
|
6477
|
-
channelType: data.channelType,
|
|
6478
|
-
channelProvider: data.channelProvider,
|
|
6479
|
-
channelAlias: data.channelAlias,
|
|
6480
|
-
chatId: data.chatId,
|
|
6481
|
-
chatUserId: data.chatUserId,
|
|
6482
|
-
chatDisplayName: data.chatDisplayName,
|
|
6483
|
-
codepilotSessionId: data.codepilotSessionId,
|
|
6484
|
-
sdkSessionId: data.sdkSessionId ?? "",
|
|
6485
|
-
workingDirectory: data.workingDirectory,
|
|
6486
|
-
model: data.model,
|
|
6487
|
-
mode: data.mode || this.getSetting("bridge_default_mode") || "code",
|
|
6488
|
-
active: true,
|
|
6489
|
-
createdAt: now(),
|
|
6490
|
-
updatedAt: now()
|
|
6491
|
-
};
|
|
6492
|
-
this.bindings.set(key, binding);
|
|
6493
|
-
this.persistBindings();
|
|
6494
|
-
return binding;
|
|
6782
|
+
this.bindings.set(key, binding);
|
|
6783
|
+
return binding;
|
|
6784
|
+
});
|
|
6495
6785
|
}
|
|
6496
6786
|
deleteChannelBinding(id) {
|
|
6497
|
-
this.
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
}
|
|
6787
|
+
this.mutateBindings(() => {
|
|
6788
|
+
for (const [key, binding] of this.bindings) {
|
|
6789
|
+
if (binding.id !== id) continue;
|
|
6790
|
+
this.bindings.delete(key);
|
|
6791
|
+
return;
|
|
6792
|
+
}
|
|
6793
|
+
});
|
|
6504
6794
|
}
|
|
6505
6795
|
updateChannelBinding(id, updates) {
|
|
6506
|
-
this.
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6796
|
+
this.mutateBindings(() => {
|
|
6797
|
+
for (const [key, b] of this.bindings) {
|
|
6798
|
+
if (b.id === id) {
|
|
6799
|
+
this.bindings.set(key, { ...b, ...updates, updatedAt: now() });
|
|
6800
|
+
break;
|
|
6801
|
+
}
|
|
6512
6802
|
}
|
|
6513
|
-
}
|
|
6803
|
+
});
|
|
6514
6804
|
}
|
|
6515
6805
|
listChannelBindings(channelType) {
|
|
6516
6806
|
this.reloadBindings();
|
|
@@ -6537,74 +6827,78 @@ var JsonFileStore = class {
|
|
|
6537
6827
|
return null;
|
|
6538
6828
|
}
|
|
6539
6829
|
createSession(name, model, systemPrompt, cwd, mode, options) {
|
|
6540
|
-
this.
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6830
|
+
return this.mutateSessions(() => {
|
|
6831
|
+
const timestamp = now();
|
|
6832
|
+
const session = {
|
|
6833
|
+
id: uuid(),
|
|
6834
|
+
name,
|
|
6835
|
+
working_directory: cwd || process.cwd(),
|
|
6836
|
+
model,
|
|
6837
|
+
preferred_mode: mode,
|
|
6838
|
+
system_prompt: systemPrompt,
|
|
6839
|
+
reasoning_effort: options?.reasoningEffort,
|
|
6840
|
+
session_type: options?.sessionType || "normal",
|
|
6841
|
+
hidden: options?.hidden === true,
|
|
6842
|
+
parent_session_id: options?.parentSessionId,
|
|
6843
|
+
expires_at: options?.expiresAt,
|
|
6844
|
+
created_at: timestamp,
|
|
6845
|
+
updated_at: timestamp
|
|
6846
|
+
};
|
|
6847
|
+
this.sessions.set(session.id, session);
|
|
6848
|
+
return session;
|
|
6849
|
+
});
|
|
6560
6850
|
}
|
|
6561
6851
|
updateSessionProviderId(sessionId, providerId) {
|
|
6562
|
-
this.
|
|
6563
|
-
|
|
6564
|
-
|
|
6852
|
+
this.mutateSessions(() => {
|
|
6853
|
+
const s = this.sessions.get(sessionId);
|
|
6854
|
+
if (!s) return;
|
|
6565
6855
|
s.provider_id = providerId;
|
|
6566
6856
|
s.updated_at = now();
|
|
6567
|
-
|
|
6568
|
-
}
|
|
6857
|
+
});
|
|
6569
6858
|
}
|
|
6570
6859
|
updateSession(sessionId, updates, options) {
|
|
6571
|
-
this.
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6860
|
+
this.mutateSessions(() => {
|
|
6861
|
+
const session = this.sessions.get(sessionId);
|
|
6862
|
+
if (!session) return;
|
|
6863
|
+
const next = {
|
|
6864
|
+
...session,
|
|
6865
|
+
...updates,
|
|
6866
|
+
id: session.id,
|
|
6867
|
+
updated_at: options?.touch === false ? session.updated_at : now()
|
|
6868
|
+
};
|
|
6869
|
+
this.sessions.set(sessionId, next);
|
|
6870
|
+
});
|
|
6582
6871
|
}
|
|
6583
6872
|
deleteSession(sessionId) {
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6873
|
+
const messagePath = path7.join(MESSAGES_DIR, `${sessionId}.json`);
|
|
6874
|
+
withFileLocks([SESSIONS_PATH, BINDINGS_PATH, messagePath], () => {
|
|
6875
|
+
this.reloadSessions();
|
|
6876
|
+
this.reloadBindings();
|
|
6877
|
+
this.sessions.delete(sessionId);
|
|
6878
|
+
for (const [key, binding] of this.bindings) {
|
|
6879
|
+
if (binding.codepilotSessionId === sessionId) {
|
|
6880
|
+
this.bindings.delete(key);
|
|
6881
|
+
}
|
|
6590
6882
|
}
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6883
|
+
this.messages.delete(sessionId);
|
|
6884
|
+
try {
|
|
6885
|
+
fs7.rmSync(messagePath, { force: true });
|
|
6886
|
+
} catch {
|
|
6887
|
+
}
|
|
6888
|
+
this.persistSessions();
|
|
6889
|
+
this.persistBindings();
|
|
6890
|
+
});
|
|
6599
6891
|
}
|
|
6600
6892
|
// ── Messages ──
|
|
6601
6893
|
addMessage(sessionId, role, content, _usage) {
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6894
|
+
this.mutateMessages(sessionId, (messages) => {
|
|
6895
|
+
messages.push({ role, content });
|
|
6896
|
+
});
|
|
6605
6897
|
}
|
|
6606
6898
|
getMessages(sessionId, opts) {
|
|
6607
|
-
const
|
|
6899
|
+
const messagePath = path7.join(MESSAGES_DIR, `${sessionId}.json`);
|
|
6900
|
+
const msgs = readJson(messagePath, []);
|
|
6901
|
+
this.messages.set(sessionId, msgs);
|
|
6608
6902
|
if (opts?.limit && opts.limit > 0) {
|
|
6609
6903
|
return { messages: msgs.slice(-opts.limit) };
|
|
6610
6904
|
}
|
|
@@ -6636,61 +6930,62 @@ var JsonFileStore = class {
|
|
|
6636
6930
|
}
|
|
6637
6931
|
}
|
|
6638
6932
|
setSessionRuntimeStatus(_sessionId, _status) {
|
|
6639
|
-
this.
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6933
|
+
this.mutateSessions(() => {
|
|
6934
|
+
const session = this.sessions.get(_sessionId);
|
|
6935
|
+
if (!session) return;
|
|
6936
|
+
const queuedCount = session.queued_count && session.queued_count > 0 ? session.queued_count : 0;
|
|
6937
|
+
let runtimeStatus;
|
|
6938
|
+
if (_status === "running") {
|
|
6939
|
+
runtimeStatus = queuedCount > 0 ? "queued" : "running";
|
|
6940
|
+
} else if (_status === "idle") {
|
|
6941
|
+
runtimeStatus = queuedCount > 0 ? "queued" : "idle";
|
|
6942
|
+
} else {
|
|
6943
|
+
runtimeStatus = session.runtime_status;
|
|
6944
|
+
}
|
|
6945
|
+
const next = {
|
|
6946
|
+
...session,
|
|
6947
|
+
runtime_status: runtimeStatus,
|
|
6948
|
+
last_runtime_update_at: now(),
|
|
6949
|
+
updated_at: now()
|
|
6950
|
+
};
|
|
6951
|
+
this.sessions.set(_sessionId, next);
|
|
6952
|
+
});
|
|
6659
6953
|
}
|
|
6660
6954
|
// ── SDK Session ──
|
|
6661
6955
|
updateSdkSessionId(sessionId, sdkSessionId) {
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
s
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6956
|
+
withFileLocks([SESSIONS_PATH, BINDINGS_PATH], () => {
|
|
6957
|
+
this.reloadSessions();
|
|
6958
|
+
this.reloadBindings();
|
|
6959
|
+
const s = this.sessions.get(sessionId);
|
|
6960
|
+
if (s) {
|
|
6961
|
+
s.sdk_session_id = sdkSessionId;
|
|
6962
|
+
if (sdkSessionId) {
|
|
6963
|
+
s.codex_thread_id = sdkSessionId;
|
|
6964
|
+
s.thread_origin = s.thread_origin || "bridge";
|
|
6965
|
+
} else {
|
|
6966
|
+
delete s.codex_thread_id;
|
|
6967
|
+
if (s.thread_origin !== "desktop") {
|
|
6968
|
+
delete s.thread_origin;
|
|
6969
|
+
}
|
|
6674
6970
|
}
|
|
6971
|
+
s.updated_at = now();
|
|
6675
6972
|
}
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
if (b.codepilotSessionId === sessionId) {
|
|
6681
|
-
this.bindings.set(key, { ...b, sdkSessionId, updatedAt: now() });
|
|
6973
|
+
for (const [key, b] of this.bindings) {
|
|
6974
|
+
if (b.codepilotSessionId === sessionId) {
|
|
6975
|
+
this.bindings.set(key, { ...b, sdkSessionId, updatedAt: now() });
|
|
6976
|
+
}
|
|
6682
6977
|
}
|
|
6683
|
-
|
|
6684
|
-
|
|
6978
|
+
this.persistSessions();
|
|
6979
|
+
this.persistBindings();
|
|
6980
|
+
});
|
|
6685
6981
|
}
|
|
6686
6982
|
updateSessionModel(sessionId, model) {
|
|
6687
|
-
this.
|
|
6688
|
-
|
|
6689
|
-
|
|
6983
|
+
this.mutateSessions(() => {
|
|
6984
|
+
const s = this.sessions.get(sessionId);
|
|
6985
|
+
if (!s) return;
|
|
6690
6986
|
s.model = model;
|
|
6691
6987
|
s.updated_at = now();
|
|
6692
|
-
|
|
6693
|
-
}
|
|
6988
|
+
});
|
|
6694
6989
|
}
|
|
6695
6990
|
syncSdkTasks(_sessionId, _todos) {
|
|
6696
6991
|
}
|
|
@@ -6703,66 +6998,84 @@ var JsonFileStore = class {
|
|
|
6703
6998
|
}
|
|
6704
6999
|
// ── Audit & Dedup ──
|
|
6705
7000
|
insertAuditLog(entry) {
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
7001
|
+
withFileLock(AUDIT_PATH, () => {
|
|
7002
|
+
this.reloadAudit();
|
|
7003
|
+
this.auditLog.push({
|
|
7004
|
+
...entry,
|
|
7005
|
+
id: uuid(),
|
|
7006
|
+
createdAt: now()
|
|
7007
|
+
});
|
|
7008
|
+
if (this.auditLog.length > 1e3) {
|
|
7009
|
+
this.auditLog = this.auditLog.slice(-1e3);
|
|
7010
|
+
}
|
|
7011
|
+
this.persistAudit();
|
|
6710
7012
|
});
|
|
6711
|
-
if (this.auditLog.length > 1e3) {
|
|
6712
|
-
this.auditLog = this.auditLog.slice(-1e3);
|
|
6713
|
-
}
|
|
6714
|
-
this.persistAudit();
|
|
6715
7013
|
}
|
|
6716
7014
|
checkDedup(key) {
|
|
7015
|
+
this.reloadDedup();
|
|
6717
7016
|
const ts = this.dedupKeys.get(key);
|
|
6718
7017
|
if (ts === void 0) return false;
|
|
6719
|
-
if (Date.now() - ts >
|
|
7018
|
+
if (Date.now() - ts > dedupTtlMs(key)) {
|
|
6720
7019
|
this.dedupKeys.delete(key);
|
|
6721
7020
|
return false;
|
|
6722
7021
|
}
|
|
6723
7022
|
return true;
|
|
6724
7023
|
}
|
|
6725
7024
|
insertDedup(key) {
|
|
6726
|
-
|
|
6727
|
-
|
|
7025
|
+
withFileLock(DEDUP_PATH, () => {
|
|
7026
|
+
this.reloadDedup();
|
|
7027
|
+
this.dedupKeys.set(key, Date.now());
|
|
7028
|
+
this.persistDedup();
|
|
7029
|
+
});
|
|
6728
7030
|
}
|
|
6729
7031
|
cleanupExpiredDedup() {
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
7032
|
+
withFileLock(DEDUP_PATH, () => {
|
|
7033
|
+
this.reloadDedup();
|
|
7034
|
+
const timestamp = Date.now();
|
|
7035
|
+
let changed = false;
|
|
7036
|
+
for (const [key, ts] of this.dedupKeys) {
|
|
7037
|
+
if (timestamp - ts > dedupTtlMs(key)) {
|
|
7038
|
+
this.dedupKeys.delete(key);
|
|
7039
|
+
changed = true;
|
|
7040
|
+
}
|
|
6736
7041
|
}
|
|
6737
|
-
|
|
6738
|
-
|
|
7042
|
+
if (changed) this.persistDedup();
|
|
7043
|
+
});
|
|
6739
7044
|
}
|
|
6740
7045
|
insertOutboundRef(_ref) {
|
|
6741
7046
|
}
|
|
6742
7047
|
// ── Permission Links ──
|
|
6743
7048
|
insertPermissionLink(link) {
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
7049
|
+
withFileLock(PERMISSIONS_PATH, () => {
|
|
7050
|
+
this.reloadPermissions();
|
|
7051
|
+
const record = {
|
|
7052
|
+
permissionRequestId: link.permissionRequestId,
|
|
7053
|
+
chatId: link.chatId,
|
|
7054
|
+
messageId: link.messageId,
|
|
7055
|
+
sessionId: link.sessionId,
|
|
7056
|
+
resolved: false,
|
|
7057
|
+
suggestions: link.suggestions
|
|
7058
|
+
};
|
|
7059
|
+
this.permissionLinks.set(link.permissionRequestId, record);
|
|
7060
|
+
this.persistPermissions();
|
|
7061
|
+
});
|
|
6754
7062
|
}
|
|
6755
7063
|
getPermissionLink(permissionRequestId) {
|
|
7064
|
+
this.reloadPermissions();
|
|
6756
7065
|
return this.permissionLinks.get(permissionRequestId) ?? null;
|
|
6757
7066
|
}
|
|
6758
7067
|
markPermissionLinkResolved(permissionRequestId) {
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
7068
|
+
return withFileLock(PERMISSIONS_PATH, () => {
|
|
7069
|
+
this.reloadPermissions();
|
|
7070
|
+
const link = this.permissionLinks.get(permissionRequestId);
|
|
7071
|
+
if (!link || link.resolved) return false;
|
|
7072
|
+
link.resolved = true;
|
|
7073
|
+
this.persistPermissions();
|
|
7074
|
+
return true;
|
|
7075
|
+
});
|
|
6764
7076
|
}
|
|
6765
7077
|
listPendingPermissionLinksByChat(chatId) {
|
|
7078
|
+
this.reloadPermissions();
|
|
6766
7079
|
const result = [];
|
|
6767
7080
|
for (const link of this.permissionLinks.values()) {
|
|
6768
7081
|
if (link.chatId === chatId && !link.resolved) {
|
|
@@ -6773,23 +7086,27 @@ var JsonFileStore = class {
|
|
|
6773
7086
|
}
|
|
6774
7087
|
// ── Channel Offsets ──
|
|
6775
7088
|
getChannelOffset(key) {
|
|
7089
|
+
this.reloadOffsets();
|
|
6776
7090
|
return this.offsets.get(key) ?? "0";
|
|
6777
7091
|
}
|
|
6778
7092
|
setChannelOffset(key, offset) {
|
|
6779
|
-
|
|
6780
|
-
|
|
7093
|
+
withFileLock(OFFSETS_PATH, () => {
|
|
7094
|
+
this.reloadOffsets();
|
|
7095
|
+
this.offsets.set(key, offset);
|
|
7096
|
+
this.persistOffsets();
|
|
7097
|
+
});
|
|
6781
7098
|
}
|
|
6782
7099
|
};
|
|
6783
7100
|
|
|
6784
7101
|
// src/weixin-login.ts
|
|
6785
7102
|
var import_qrcode = __toESM(require_lib(), 1);
|
|
6786
|
-
import
|
|
6787
|
-
import
|
|
6788
|
-
import
|
|
7103
|
+
import fs9 from "node:fs";
|
|
7104
|
+
import path9 from "node:path";
|
|
7105
|
+
import crypto8 from "node:crypto";
|
|
6789
7106
|
import { spawn as spawn2 } from "node:child_process";
|
|
6790
7107
|
|
|
6791
7108
|
// src/adapters/weixin/weixin-api.ts
|
|
6792
|
-
import
|
|
7109
|
+
import crypto6 from "node:crypto";
|
|
6793
7110
|
|
|
6794
7111
|
// src/adapters/weixin/weixin-types.ts
|
|
6795
7112
|
var DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com";
|
|
@@ -6838,27 +7155,36 @@ async function pollLoginQrStatus(qrcode, baseUrl) {
|
|
|
6838
7155
|
}
|
|
6839
7156
|
|
|
6840
7157
|
// src/weixin-store.ts
|
|
6841
|
-
import
|
|
6842
|
-
import
|
|
6843
|
-
|
|
6844
|
-
var
|
|
6845
|
-
var
|
|
7158
|
+
import fs8 from "node:fs";
|
|
7159
|
+
import crypto7 from "node:crypto";
|
|
7160
|
+
import path8 from "node:path";
|
|
7161
|
+
var DATA_DIR2 = path8.join(CTI_HOME, "data");
|
|
7162
|
+
var ACCOUNTS_PATH = path8.join(DATA_DIR2, "weixin-accounts.json");
|
|
7163
|
+
var CONTEXT_TOKENS_PATH = path8.join(DATA_DIR2, "weixin-context-tokens.json");
|
|
6846
7164
|
var DEFAULT_BASE_URL2 = "https://ilinkai.weixin.qq.com";
|
|
6847
7165
|
var DEFAULT_CDN_BASE_URL2 = "https://novac2c.cdn.weixin.qq.com/c2c";
|
|
6848
7166
|
function ensureDir2(dir) {
|
|
6849
|
-
|
|
7167
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
6850
7168
|
}
|
|
6851
7169
|
function atomicWrite2(filePath, data) {
|
|
6852
|
-
const tmpPath = `${filePath}.tmp`;
|
|
6853
|
-
|
|
6854
|
-
|
|
7170
|
+
const tmpPath = `${filePath}.${process.pid}.${crypto7.randomUUID()}.tmp`;
|
|
7171
|
+
try {
|
|
7172
|
+
fs8.writeFileSync(tmpPath, data, "utf-8");
|
|
7173
|
+
fs8.renameSync(tmpPath, filePath);
|
|
7174
|
+
} finally {
|
|
7175
|
+
try {
|
|
7176
|
+
fs8.rmSync(tmpPath, { force: true });
|
|
7177
|
+
} catch {
|
|
7178
|
+
}
|
|
7179
|
+
}
|
|
6855
7180
|
}
|
|
6856
7181
|
function readJson2(filePath, fallback) {
|
|
6857
7182
|
try {
|
|
6858
|
-
const raw =
|
|
7183
|
+
const raw = fs8.readFileSync(filePath, "utf-8");
|
|
6859
7184
|
return JSON.parse(raw);
|
|
6860
|
-
} catch {
|
|
6861
|
-
return fallback;
|
|
7185
|
+
} catch (error) {
|
|
7186
|
+
if (error.code === "ENOENT") return fallback;
|
|
7187
|
+
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u5FAE\u4FE1\u6570\u636E\u6587\u4EF6 ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
6862
7188
|
}
|
|
6863
7189
|
}
|
|
6864
7190
|
function now2() {
|
|
@@ -6907,37 +7233,38 @@ function listWeixinAccounts() {
|
|
|
6907
7233
|
return sortAccountsByRecency(readAccounts());
|
|
6908
7234
|
}
|
|
6909
7235
|
function upsertWeixinAccount(params) {
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
7236
|
+
return withFileLock(ACCOUNTS_PATH, () => {
|
|
7237
|
+
const accounts = readAccounts();
|
|
7238
|
+
const existing = accounts.find((account2) => account2.accountId === params.accountId);
|
|
7239
|
+
const timestamp = now2();
|
|
7240
|
+
const account = existing ? {
|
|
7241
|
+
...existing,
|
|
7242
|
+
userId: params.userId ?? existing.userId,
|
|
7243
|
+
baseUrl: params.baseUrl ?? existing.baseUrl,
|
|
7244
|
+
cdnBaseUrl: params.cdnBaseUrl ?? existing.cdnBaseUrl,
|
|
7245
|
+
token: params.token ?? existing.token,
|
|
7246
|
+
name: params.name ?? existing.name,
|
|
7247
|
+
enabled: params.enabled ?? existing.enabled,
|
|
7248
|
+
lastLoginAt: timestamp,
|
|
7249
|
+
updatedAt: timestamp
|
|
7250
|
+
} : {
|
|
7251
|
+
accountId: params.accountId,
|
|
7252
|
+
userId: params.userId ?? "",
|
|
7253
|
+
baseUrl: params.baseUrl ?? DEFAULT_BASE_URL2,
|
|
7254
|
+
cdnBaseUrl: params.cdnBaseUrl ?? DEFAULT_CDN_BASE_URL2,
|
|
7255
|
+
token: params.token ?? "",
|
|
7256
|
+
name: params.name ?? params.accountId,
|
|
7257
|
+
enabled: params.enabled ?? true,
|
|
7258
|
+
lastLoginAt: timestamp,
|
|
7259
|
+
createdAt: timestamp,
|
|
7260
|
+
updatedAt: timestamp
|
|
7261
|
+
};
|
|
7262
|
+
writeAccounts([
|
|
7263
|
+
account,
|
|
7264
|
+
...accounts.filter((item) => item.accountId !== account.accountId)
|
|
7265
|
+
]);
|
|
7266
|
+
return account;
|
|
7267
|
+
});
|
|
6941
7268
|
}
|
|
6942
7269
|
|
|
6943
7270
|
// src/weixin-login.ts
|
|
@@ -6945,11 +7272,11 @@ var MAX_REFRESHES = 3;
|
|
|
6945
7272
|
var QR_TTL_MS = 5 * 6e4;
|
|
6946
7273
|
var POLL_INTERVAL_MS = 3e3;
|
|
6947
7274
|
var WEB_SESSION_TTL_MS = 15 * 6e4;
|
|
6948
|
-
var RUNTIME_DIR =
|
|
6949
|
-
var HTML_PATH =
|
|
7275
|
+
var RUNTIME_DIR = path9.join(CTI_HOME, "runtime");
|
|
7276
|
+
var HTML_PATH = path9.join(RUNTIME_DIR, "weixin-login.html");
|
|
6950
7277
|
var webLoginSessions = /* @__PURE__ */ new Map();
|
|
6951
7278
|
function ensureRuntimeDir() {
|
|
6952
|
-
|
|
7279
|
+
fs9.mkdirSync(RUNTIME_DIR, { recursive: true });
|
|
6953
7280
|
}
|
|
6954
7281
|
function escapeHtml(text2) {
|
|
6955
7282
|
return text2.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
@@ -7262,7 +7589,7 @@ function buildWeixinLoginPopupHtml(sessionId) {
|
|
|
7262
7589
|
async function writeQrHtml(session) {
|
|
7263
7590
|
ensureRuntimeDir();
|
|
7264
7591
|
const qrSvg = await buildQrSvg(session.qrImageUrl);
|
|
7265
|
-
|
|
7592
|
+
fs9.writeFileSync(HTML_PATH, buildQrHtml(qrSvg), "utf-8");
|
|
7266
7593
|
}
|
|
7267
7594
|
function openQrHtml() {
|
|
7268
7595
|
try {
|
|
@@ -7451,7 +7778,7 @@ async function startWeixinLoginWebSession(options = {}) {
|
|
|
7451
7778
|
ensureRuntimeDir();
|
|
7452
7779
|
const config = options.config || {};
|
|
7453
7780
|
const seed = await createSession(0, config.baseUrl);
|
|
7454
|
-
const sessionId =
|
|
7781
|
+
const sessionId = crypto8.randomUUID();
|
|
7455
7782
|
const qrSvg = await buildQrSvg(seed.qrImageUrl);
|
|
7456
7783
|
const session = {
|
|
7457
7784
|
id: sessionId,
|
|
@@ -7523,7 +7850,7 @@ async function runWeixinLogin(config = {}) {
|
|
|
7523
7850
|
}
|
|
7524
7851
|
var isMainModule = (() => {
|
|
7525
7852
|
const entry = process.argv[1];
|
|
7526
|
-
return !!entry &&
|
|
7853
|
+
return !!entry && path9.resolve(entry) === path9.resolve(new URL(import.meta.url).pathname);
|
|
7527
7854
|
})();
|
|
7528
7855
|
if (isMainModule) {
|
|
7529
7856
|
runWeixinLogin().catch((err) => {
|
|
@@ -7533,11 +7860,11 @@ if (isMainModule) {
|
|
|
7533
7860
|
}
|
|
7534
7861
|
|
|
7535
7862
|
// src/codex-models.ts
|
|
7536
|
-
import
|
|
7863
|
+
import fs10 from "node:fs";
|
|
7537
7864
|
import os4 from "node:os";
|
|
7538
|
-
import
|
|
7539
|
-
var DEFAULT_CODEX_CONFIG_PATH =
|
|
7540
|
-
var DEFAULT_CODEX_MODELS_CACHE_PATH =
|
|
7865
|
+
import path10 from "node:path";
|
|
7866
|
+
var DEFAULT_CODEX_CONFIG_PATH = path10.join(os4.homedir(), ".codex", "config.toml");
|
|
7867
|
+
var DEFAULT_CODEX_MODELS_CACHE_PATH = path10.join(os4.homedir(), ".codex", "models_cache.json");
|
|
7541
7868
|
var REASONING_LEVEL_DESCRIPTIONS = {
|
|
7542
7869
|
low: "Fast responses with lighter reasoning",
|
|
7543
7870
|
medium: "Balances speed and reasoning depth for everyday tasks",
|
|
@@ -7586,7 +7913,7 @@ var KNOWN_CODEX_MODEL_FALLBACKS = [
|
|
|
7586
7913
|
];
|
|
7587
7914
|
function readConfiguredCodexModel(configPath = DEFAULT_CODEX_CONFIG_PATH) {
|
|
7588
7915
|
try {
|
|
7589
|
-
const raw =
|
|
7916
|
+
const raw = fs10.readFileSync(configPath, "utf-8");
|
|
7590
7917
|
let inSection = false;
|
|
7591
7918
|
for (const line of raw.split(/\r?\n/)) {
|
|
7592
7919
|
const trimmed = line.trim();
|
|
@@ -7630,7 +7957,7 @@ function parseSupportedReasoningLevels(value) {
|
|
|
7630
7957
|
}
|
|
7631
7958
|
function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
|
|
7632
7959
|
try {
|
|
7633
|
-
const raw =
|
|
7960
|
+
const raw = fs10.readFileSync(cachePath, "utf-8");
|
|
7634
7961
|
const parsed = JSON.parse(raw);
|
|
7635
7962
|
if (!Array.isArray(parsed.models)) return [];
|
|
7636
7963
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -7696,6 +8023,74 @@ function listAvailableCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH, a
|
|
|
7696
8023
|
return merged;
|
|
7697
8024
|
}
|
|
7698
8025
|
|
|
8026
|
+
// src/ui-http-security.ts
|
|
8027
|
+
var DEFAULT_JSON_BODY_LIMIT_BYTES = 1024 * 1024;
|
|
8028
|
+
var HttpRequestError = class extends Error {
|
|
8029
|
+
constructor(statusCode, message) {
|
|
8030
|
+
super(message);
|
|
8031
|
+
this.statusCode = statusCode;
|
|
8032
|
+
this.name = "HttpRequestError";
|
|
8033
|
+
}
|
|
8034
|
+
};
|
|
8035
|
+
function isMutationMethod(method) {
|
|
8036
|
+
return method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE";
|
|
8037
|
+
}
|
|
8038
|
+
function getExpectedOrigin(headers) {
|
|
8039
|
+
const host = headers.host?.trim();
|
|
8040
|
+
if (!host) throw new HttpRequestError(400, "\u8BF7\u6C42\u7F3A\u5C11 Host header\u3002");
|
|
8041
|
+
return `http://${host}`;
|
|
8042
|
+
}
|
|
8043
|
+
function assertTrustedMutationRequest(request) {
|
|
8044
|
+
if (!isMutationMethod(request.method)) return;
|
|
8045
|
+
const fetchSite = request.headers["sec-fetch-site"];
|
|
8046
|
+
if (fetchSite === "cross-site") {
|
|
8047
|
+
throw new HttpRequestError(403, "\u5DF2\u62D2\u7EDD\u8DE8\u7AD9\u7BA1\u7406\u8BF7\u6C42\u3002");
|
|
8048
|
+
}
|
|
8049
|
+
const origin = request.headers.origin;
|
|
8050
|
+
if (!origin) return;
|
|
8051
|
+
if (origin === "null") {
|
|
8052
|
+
throw new HttpRequestError(403, "\u5DF2\u62D2\u7EDD\u6765\u6E90\u4E0D\u660E\u7684\u7BA1\u7406\u8BF7\u6C42\u3002");
|
|
8053
|
+
}
|
|
8054
|
+
let normalizedOrigin;
|
|
8055
|
+
try {
|
|
8056
|
+
normalizedOrigin = new URL(origin).origin;
|
|
8057
|
+
} catch {
|
|
8058
|
+
throw new HttpRequestError(403, "\u8BF7\u6C42 Origin \u65E0\u6548\u3002");
|
|
8059
|
+
}
|
|
8060
|
+
if (normalizedOrigin !== getExpectedOrigin(request.headers)) {
|
|
8061
|
+
throw new HttpRequestError(403, "\u5DF2\u62D2\u7EDD\u975E\u540C\u6E90\u7BA1\u7406\u8BF7\u6C42\u3002");
|
|
8062
|
+
}
|
|
8063
|
+
}
|
|
8064
|
+
async function readJsonBody(request, maxBytes = DEFAULT_JSON_BODY_LIMIT_BYTES) {
|
|
8065
|
+
const boundedMaxBytes = Math.max(1, maxBytes);
|
|
8066
|
+
const declaredLength = Number(request.headers["content-length"]);
|
|
8067
|
+
if (Number.isFinite(declaredLength) && declaredLength > boundedMaxBytes) {
|
|
8068
|
+
throw new HttpRequestError(413, `\u8BF7\u6C42\u4F53\u8D85\u8FC7 ${boundedMaxBytes} \u5B57\u8282\u9650\u5236\u3002`);
|
|
8069
|
+
}
|
|
8070
|
+
const chunks = [];
|
|
8071
|
+
let receivedBytes = 0;
|
|
8072
|
+
for await (const chunk of request) {
|
|
8073
|
+
const buffer = Buffer.from(chunk);
|
|
8074
|
+
receivedBytes += buffer.length;
|
|
8075
|
+
if (receivedBytes > boundedMaxBytes) {
|
|
8076
|
+
request.resume();
|
|
8077
|
+
throw new HttpRequestError(413, `\u8BF7\u6C42\u4F53\u8D85\u8FC7 ${boundedMaxBytes} \u5B57\u8282\u9650\u5236\u3002`);
|
|
8078
|
+
}
|
|
8079
|
+
chunks.push(buffer);
|
|
8080
|
+
}
|
|
8081
|
+
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
8082
|
+
if (!raw) return {};
|
|
8083
|
+
const contentType = request.headers["content-type"] || "";
|
|
8084
|
+
if (!/^application\/json(?:\s*;|$)/i.test(contentType)) {
|
|
8085
|
+
throw new HttpRequestError(415, "\u8BF7\u6C42\u4F53\u5FC5\u987B\u4F7F\u7528 application/json\u3002");
|
|
8086
|
+
}
|
|
8087
|
+
try {
|
|
8088
|
+
return JSON.parse(raw);
|
|
8089
|
+
} catch {
|
|
8090
|
+
throw new HttpRequestError(400, "\u8BF7\u6C42\u4F53\u4E0D\u662F\u6709\u6548 JSON\u3002");
|
|
8091
|
+
}
|
|
8092
|
+
}
|
|
8093
|
+
|
|
7699
8094
|
// src/ui-server.ts
|
|
7700
8095
|
var port = 4781;
|
|
7701
8096
|
var serverStartTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -7781,14 +8176,6 @@ function text(response, statusCode, body) {
|
|
|
7781
8176
|
response.writeHead(statusCode, { "Content-Type": "text/plain; charset=utf-8" });
|
|
7782
8177
|
response.end(body);
|
|
7783
8178
|
}
|
|
7784
|
-
async function readJsonBody(request) {
|
|
7785
|
-
const chunks = [];
|
|
7786
|
-
for await (const chunk of request) {
|
|
7787
|
-
chunks.push(Buffer.from(chunk));
|
|
7788
|
-
}
|
|
7789
|
-
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
7790
|
-
return raw ? JSON.parse(raw) : {};
|
|
7791
|
-
}
|
|
7792
8179
|
function asString(value) {
|
|
7793
8180
|
if (typeof value !== "string") return void 0;
|
|
7794
8181
|
const trimmed = value.trim();
|
|
@@ -7869,14 +8256,14 @@ function channelToPayload(channel) {
|
|
|
7869
8256
|
};
|
|
7870
8257
|
}
|
|
7871
8258
|
function generateAccessToken() {
|
|
7872
|
-
return
|
|
8259
|
+
return crypto9.randomBytes(18).toString("base64url");
|
|
7873
8260
|
}
|
|
7874
8261
|
function timingSafeMatch(left, right) {
|
|
7875
8262
|
if (!left || !right) return false;
|
|
7876
8263
|
const leftBuffer = Buffer.from(left);
|
|
7877
8264
|
const rightBuffer = Buffer.from(right);
|
|
7878
8265
|
if (leftBuffer.length !== rightBuffer.length) return false;
|
|
7879
|
-
return
|
|
8266
|
+
return crypto9.timingSafeEqual(leftBuffer, rightBuffer);
|
|
7880
8267
|
}
|
|
7881
8268
|
function parseCookies(request) {
|
|
7882
8269
|
const header = request.headers.cookie;
|
|
@@ -11627,6 +12014,11 @@ function renderHtml() {
|
|
|
11627
12014
|
}
|
|
11628
12015
|
var server = http.createServer(async (request, response) => {
|
|
11629
12016
|
try {
|
|
12017
|
+
response.setHeader("X-Content-Type-Options", "nosniff");
|
|
12018
|
+
response.setHeader("X-Frame-Options", "DENY");
|
|
12019
|
+
response.setHeader("Referrer-Policy", "no-referrer");
|
|
12020
|
+
response.setHeader("Cache-Control", "no-store");
|
|
12021
|
+
assertTrustedMutationRequest(request);
|
|
11630
12022
|
const url = new URL(request.url || "/", getUiServerUrl(port));
|
|
11631
12023
|
const config = loadConfig();
|
|
11632
12024
|
const localRequest = isLocalRequest(request);
|
|
@@ -11964,16 +12356,18 @@ var server = http.createServer(async (request, response) => {
|
|
|
11964
12356
|
}
|
|
11965
12357
|
text(response, 404, "Not found");
|
|
11966
12358
|
} catch (error) {
|
|
11967
|
-
|
|
12359
|
+
const statusCode = error instanceof HttpRequestError ? error.statusCode : 500;
|
|
12360
|
+
json(response, statusCode, {
|
|
11968
12361
|
error: error instanceof Error ? error.message : String(error)
|
|
11969
12362
|
});
|
|
11970
12363
|
}
|
|
11971
12364
|
});
|
|
11972
12365
|
async function startServer() {
|
|
11973
12366
|
port = await resolveUiPort(parsePreferredPort());
|
|
12367
|
+
const listenHost = loadConfig().uiAllowLan === true ? "0.0.0.0" : "127.0.0.1";
|
|
11974
12368
|
await new Promise((resolve, reject) => {
|
|
11975
12369
|
server.once("error", reject);
|
|
11976
|
-
server.listen(port,
|
|
12370
|
+
server.listen(port, listenHost, () => {
|
|
11977
12371
|
server.removeListener("error", reject);
|
|
11978
12372
|
resolve();
|
|
11979
12373
|
});
|