codex-to-im 1.0.58 → 1.0.60
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 +1584 -656
- package/dist/ui-server.mjs +882 -437
- package/package.json +9 -8
- package/scripts/patch-codex-sdk-windows-hide.js +14 -3
package/dist/ui-server.mjs
CHANGED
|
@@ -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 fs10 = __require("fs");
|
|
4015
4015
|
var PNG = require_png().PNG;
|
|
4016
4016
|
var Utils = require_utils2();
|
|
4017
4017
|
exports.render = function render(qrData, options) {
|
|
@@ -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 = fs10.createWriteStream(path10);
|
|
4067
4067
|
stream.on("error", done);
|
|
4068
4068
|
stream.on("close", done);
|
|
4069
4069
|
exports.renderToFileStream(stream, qrData, options);
|
|
@@ -4130,9 +4130,9 @@ var require_utf8 = __commonJS({
|
|
|
4130
4130
|
cb = options;
|
|
4131
4131
|
options = void 0;
|
|
4132
4132
|
}
|
|
4133
|
-
const
|
|
4133
|
+
const fs10 = __require("fs");
|
|
4134
4134
|
const utf8 = exports.render(qrData, options);
|
|
4135
|
-
|
|
4135
|
+
fs10.writeFile(path10, utf8, cb);
|
|
4136
4136
|
};
|
|
4137
4137
|
}
|
|
4138
4138
|
});
|
|
@@ -4306,10 +4306,10 @@ var require_svg = __commonJS({
|
|
|
4306
4306
|
cb = options;
|
|
4307
4307
|
options = void 0;
|
|
4308
4308
|
}
|
|
4309
|
-
const
|
|
4309
|
+
const fs10 = __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
|
+
fs10.writeFile(path10, xmlStr, cb);
|
|
4313
4313
|
};
|
|
4314
4314
|
}
|
|
4315
4315
|
});
|
|
@@ -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 crypto8 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;
|
|
4677
4779
|
try {
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
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`;
|
|
4818
|
+
try {
|
|
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,55 @@ 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 fs6 from "node:fs";
|
|
6253
6442
|
import path6 from "node:path";
|
|
6254
|
-
import
|
|
6443
|
+
import crypto4 from "node:crypto";
|
|
6255
6444
|
var DATA_DIR = path6.join(CTI_HOME, "data");
|
|
6256
6445
|
var MESSAGES_DIR = path6.join(DATA_DIR, "messages");
|
|
6446
|
+
var SESSIONS_PATH = path6.join(DATA_DIR, "sessions.json");
|
|
6447
|
+
var BINDINGS_PATH = path6.join(DATA_DIR, "bindings.json");
|
|
6448
|
+
var PERMISSIONS_PATH = path6.join(DATA_DIR, "permissions.json");
|
|
6449
|
+
var OFFSETS_PATH = path6.join(DATA_DIR, "offsets.json");
|
|
6450
|
+
var DEDUP_PATH = path6.join(DATA_DIR, "dedup.json");
|
|
6451
|
+
var AUDIT_PATH = path6.join(DATA_DIR, "audit.json");
|
|
6452
|
+
var DEFAULT_DEDUP_TTL_MS = 5 * 60 * 1e3;
|
|
6453
|
+
var INBOUND_DEDUP_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6257
6454
|
function ensureDir(dir) {
|
|
6258
|
-
|
|
6455
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
6259
6456
|
}
|
|
6260
6457
|
function atomicWrite(filePath, data) {
|
|
6261
|
-
const tmp = filePath
|
|
6262
|
-
|
|
6263
|
-
|
|
6458
|
+
const tmp = `${filePath}.${process.pid}.${crypto4.randomUUID()}.tmp`;
|
|
6459
|
+
try {
|
|
6460
|
+
fs6.writeFileSync(tmp, data, "utf-8");
|
|
6461
|
+
fs6.renameSync(tmp, filePath);
|
|
6462
|
+
} finally {
|
|
6463
|
+
try {
|
|
6464
|
+
fs6.rmSync(tmp, { force: true });
|
|
6465
|
+
} catch {
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
6468
|
+
}
|
|
6469
|
+
function dedupTtlMs(key) {
|
|
6470
|
+
return key.startsWith("inbound:") ? INBOUND_DEDUP_TTL_MS : DEFAULT_DEDUP_TTL_MS;
|
|
6264
6471
|
}
|
|
6265
6472
|
function readJson(filePath, fallback) {
|
|
6266
6473
|
try {
|
|
6267
|
-
const raw =
|
|
6474
|
+
const raw = fs6.readFileSync(filePath, "utf-8");
|
|
6268
6475
|
return JSON.parse(raw);
|
|
6269
|
-
} catch {
|
|
6270
|
-
return fallback;
|
|
6476
|
+
} catch (error) {
|
|
6477
|
+
if (error.code === "ENOENT") return fallback;
|
|
6478
|
+
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6 JSON \u6570\u636E\u6587\u4EF6 ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
6271
6479
|
}
|
|
6272
6480
|
}
|
|
6273
6481
|
function writeJson(filePath, data) {
|
|
6274
6482
|
atomicWrite(filePath, JSON.stringify(data, null, 2));
|
|
6275
6483
|
}
|
|
6276
6484
|
function uuid() {
|
|
6277
|
-
return
|
|
6485
|
+
return crypto4.randomUUID();
|
|
6278
6486
|
}
|
|
6279
6487
|
function now() {
|
|
6280
6488
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -6333,38 +6541,38 @@ var JsonFileStore = class {
|
|
|
6333
6541
|
this.reloadSessions();
|
|
6334
6542
|
this.reloadBindings();
|
|
6335
6543
|
const perms = readJson(
|
|
6336
|
-
|
|
6544
|
+
PERMISSIONS_PATH,
|
|
6337
6545
|
{}
|
|
6338
6546
|
);
|
|
6339
6547
|
for (const [id, p] of Object.entries(perms)) {
|
|
6340
6548
|
this.permissionLinks.set(id, p);
|
|
6341
6549
|
}
|
|
6342
6550
|
const offsets = readJson(
|
|
6343
|
-
|
|
6551
|
+
OFFSETS_PATH,
|
|
6344
6552
|
{}
|
|
6345
6553
|
);
|
|
6346
6554
|
for (const [k, v] of Object.entries(offsets)) {
|
|
6347
6555
|
this.offsets.set(k, v);
|
|
6348
6556
|
}
|
|
6349
6557
|
const dedup = readJson(
|
|
6350
|
-
|
|
6558
|
+
DEDUP_PATH,
|
|
6351
6559
|
{}
|
|
6352
6560
|
);
|
|
6353
6561
|
for (const [k, v] of Object.entries(dedup)) {
|
|
6354
6562
|
this.dedupKeys.set(k, v);
|
|
6355
6563
|
}
|
|
6356
|
-
this.auditLog = readJson(
|
|
6564
|
+
this.auditLog = readJson(AUDIT_PATH, []);
|
|
6357
6565
|
}
|
|
6358
6566
|
reloadSessions() {
|
|
6359
6567
|
const sessions = readJson(
|
|
6360
|
-
|
|
6568
|
+
SESSIONS_PATH,
|
|
6361
6569
|
{}
|
|
6362
6570
|
);
|
|
6363
6571
|
this.sessions = new Map(Object.entries(sessions));
|
|
6364
6572
|
}
|
|
6365
6573
|
reloadBindings() {
|
|
6366
6574
|
const bindings = readJson(
|
|
6367
|
-
|
|
6575
|
+
BINDINGS_PATH,
|
|
6368
6576
|
{}
|
|
6369
6577
|
);
|
|
6370
6578
|
const normalized = /* @__PURE__ */ new Map();
|
|
@@ -6378,41 +6586,62 @@ var JsonFileStore = class {
|
|
|
6378
6586
|
}
|
|
6379
6587
|
this.bindings = normalized;
|
|
6380
6588
|
if (changed) {
|
|
6381
|
-
|
|
6589
|
+
withFileLock(BINDINGS_PATH, () => {
|
|
6590
|
+
const latest = readJson(BINDINGS_PATH, {});
|
|
6591
|
+
this.bindings = new Map(Object.values(latest).map((binding) => {
|
|
6592
|
+
const upgraded = upgradeLegacyBinding(binding);
|
|
6593
|
+
return [`${upgraded.channelType}:${upgraded.chatId}`, upgraded];
|
|
6594
|
+
}));
|
|
6595
|
+
this.persistBindings();
|
|
6596
|
+
});
|
|
6382
6597
|
}
|
|
6383
6598
|
}
|
|
6599
|
+
reloadPermissions() {
|
|
6600
|
+
this.permissionLinks = new Map(Object.entries(
|
|
6601
|
+
readJson(PERMISSIONS_PATH, {})
|
|
6602
|
+
));
|
|
6603
|
+
}
|
|
6604
|
+
reloadOffsets() {
|
|
6605
|
+
this.offsets = new Map(Object.entries(readJson(OFFSETS_PATH, {})));
|
|
6606
|
+
}
|
|
6607
|
+
reloadDedup() {
|
|
6608
|
+
this.dedupKeys = new Map(Object.entries(readJson(DEDUP_PATH, {})));
|
|
6609
|
+
}
|
|
6610
|
+
reloadAudit() {
|
|
6611
|
+
this.auditLog = readJson(AUDIT_PATH, []);
|
|
6612
|
+
}
|
|
6384
6613
|
persistSessions() {
|
|
6385
6614
|
writeJson(
|
|
6386
|
-
|
|
6615
|
+
SESSIONS_PATH,
|
|
6387
6616
|
Object.fromEntries(this.sessions)
|
|
6388
6617
|
);
|
|
6389
6618
|
}
|
|
6390
6619
|
persistBindings() {
|
|
6391
6620
|
writeJson(
|
|
6392
|
-
|
|
6621
|
+
BINDINGS_PATH,
|
|
6393
6622
|
Object.fromEntries(this.bindings)
|
|
6394
6623
|
);
|
|
6395
6624
|
}
|
|
6396
6625
|
persistPermissions() {
|
|
6397
6626
|
writeJson(
|
|
6398
|
-
|
|
6627
|
+
PERMISSIONS_PATH,
|
|
6399
6628
|
Object.fromEntries(this.permissionLinks)
|
|
6400
6629
|
);
|
|
6401
6630
|
}
|
|
6402
6631
|
persistOffsets() {
|
|
6403
6632
|
writeJson(
|
|
6404
|
-
|
|
6633
|
+
OFFSETS_PATH,
|
|
6405
6634
|
Object.fromEntries(this.offsets)
|
|
6406
6635
|
);
|
|
6407
6636
|
}
|
|
6408
6637
|
persistDedup() {
|
|
6409
6638
|
writeJson(
|
|
6410
|
-
|
|
6639
|
+
DEDUP_PATH,
|
|
6411
6640
|
Object.fromEntries(this.dedupKeys)
|
|
6412
6641
|
);
|
|
6413
6642
|
}
|
|
6414
6643
|
persistAudit() {
|
|
6415
|
-
writeJson(
|
|
6644
|
+
writeJson(AUDIT_PATH, this.auditLog);
|
|
6416
6645
|
}
|
|
6417
6646
|
persistMessages(sessionId) {
|
|
6418
6647
|
const msgs = this.messages.get(sessionId) || [];
|
|
@@ -6429,6 +6658,32 @@ var JsonFileStore = class {
|
|
|
6429
6658
|
this.messages.set(sessionId, msgs);
|
|
6430
6659
|
return msgs;
|
|
6431
6660
|
}
|
|
6661
|
+
mutateBindings(mutation) {
|
|
6662
|
+
return withFileLock(BINDINGS_PATH, () => {
|
|
6663
|
+
this.reloadBindings();
|
|
6664
|
+
const result = mutation();
|
|
6665
|
+
this.persistBindings();
|
|
6666
|
+
return result;
|
|
6667
|
+
});
|
|
6668
|
+
}
|
|
6669
|
+
mutateSessions(mutation) {
|
|
6670
|
+
return withFileLock(SESSIONS_PATH, () => {
|
|
6671
|
+
this.reloadSessions();
|
|
6672
|
+
const result = mutation();
|
|
6673
|
+
this.persistSessions();
|
|
6674
|
+
return result;
|
|
6675
|
+
});
|
|
6676
|
+
}
|
|
6677
|
+
mutateMessages(sessionId, mutation) {
|
|
6678
|
+
const messagePath = path6.join(MESSAGES_DIR, `${sessionId}.json`);
|
|
6679
|
+
return withFileLock(messagePath, () => {
|
|
6680
|
+
const messages = readJson(messagePath, []);
|
|
6681
|
+
this.messages.set(sessionId, messages);
|
|
6682
|
+
const result = mutation(messages);
|
|
6683
|
+
this.persistMessages(sessionId);
|
|
6684
|
+
return result;
|
|
6685
|
+
});
|
|
6686
|
+
}
|
|
6432
6687
|
// ── Settings ──
|
|
6433
6688
|
refreshSettings() {
|
|
6434
6689
|
if (!this.dynamicSettings) return;
|
|
@@ -6451,66 +6706,65 @@ var JsonFileStore = class {
|
|
|
6451
6706
|
return this.bindings.get(`${channelType}:${chatId}`) ?? null;
|
|
6452
6707
|
}
|
|
6453
6708
|
upsertChannelBinding(data) {
|
|
6454
|
-
this.
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6709
|
+
return this.mutateBindings(() => {
|
|
6710
|
+
const key = `${data.channelType}:${data.chatId}`;
|
|
6711
|
+
const existing = this.bindings.get(key);
|
|
6712
|
+
if (existing) {
|
|
6713
|
+
const updated = {
|
|
6714
|
+
...existing,
|
|
6715
|
+
codepilotSessionId: data.codepilotSessionId,
|
|
6716
|
+
sdkSessionId: data.sdkSessionId ?? existing.sdkSessionId,
|
|
6717
|
+
channelProvider: data.channelProvider ?? existing.channelProvider,
|
|
6718
|
+
channelAlias: data.channelAlias ?? existing.channelAlias,
|
|
6719
|
+
chatUserId: data.chatUserId ?? existing.chatUserId,
|
|
6720
|
+
chatDisplayName: data.chatDisplayName ?? existing.chatDisplayName,
|
|
6721
|
+
workingDirectory: data.workingDirectory,
|
|
6722
|
+
model: data.model,
|
|
6723
|
+
mode: data.mode ?? existing.mode,
|
|
6724
|
+
updatedAt: now()
|
|
6725
|
+
};
|
|
6726
|
+
this.bindings.set(key, updated);
|
|
6727
|
+
return updated;
|
|
6728
|
+
}
|
|
6729
|
+
const binding = {
|
|
6730
|
+
id: uuid(),
|
|
6731
|
+
channelType: data.channelType,
|
|
6732
|
+
channelProvider: data.channelProvider,
|
|
6733
|
+
channelAlias: data.channelAlias,
|
|
6734
|
+
chatId: data.chatId,
|
|
6735
|
+
chatUserId: data.chatUserId,
|
|
6736
|
+
chatDisplayName: data.chatDisplayName,
|
|
6460
6737
|
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,
|
|
6738
|
+
sdkSessionId: data.sdkSessionId ?? "",
|
|
6466
6739
|
workingDirectory: data.workingDirectory,
|
|
6467
6740
|
model: data.model,
|
|
6468
|
-
mode: data.mode
|
|
6741
|
+
mode: data.mode || this.getSetting("bridge_default_mode") || "code",
|
|
6742
|
+
active: true,
|
|
6743
|
+
createdAt: now(),
|
|
6469
6744
|
updatedAt: now()
|
|
6470
6745
|
};
|
|
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;
|
|
6746
|
+
this.bindings.set(key, binding);
|
|
6747
|
+
return binding;
|
|
6748
|
+
});
|
|
6495
6749
|
}
|
|
6496
6750
|
deleteChannelBinding(id) {
|
|
6497
|
-
this.
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
}
|
|
6751
|
+
this.mutateBindings(() => {
|
|
6752
|
+
for (const [key, binding] of this.bindings) {
|
|
6753
|
+
if (binding.id !== id) continue;
|
|
6754
|
+
this.bindings.delete(key);
|
|
6755
|
+
return;
|
|
6756
|
+
}
|
|
6757
|
+
});
|
|
6504
6758
|
}
|
|
6505
6759
|
updateChannelBinding(id, updates) {
|
|
6506
|
-
this.
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6760
|
+
this.mutateBindings(() => {
|
|
6761
|
+
for (const [key, b] of this.bindings) {
|
|
6762
|
+
if (b.id === id) {
|
|
6763
|
+
this.bindings.set(key, { ...b, ...updates, updatedAt: now() });
|
|
6764
|
+
break;
|
|
6765
|
+
}
|
|
6512
6766
|
}
|
|
6513
|
-
}
|
|
6767
|
+
});
|
|
6514
6768
|
}
|
|
6515
6769
|
listChannelBindings(channelType) {
|
|
6516
6770
|
this.reloadBindings();
|
|
@@ -6537,74 +6791,78 @@ var JsonFileStore = class {
|
|
|
6537
6791
|
return null;
|
|
6538
6792
|
}
|
|
6539
6793
|
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
|
-
|
|
6794
|
+
return this.mutateSessions(() => {
|
|
6795
|
+
const timestamp = now();
|
|
6796
|
+
const session = {
|
|
6797
|
+
id: uuid(),
|
|
6798
|
+
name,
|
|
6799
|
+
working_directory: cwd || process.cwd(),
|
|
6800
|
+
model,
|
|
6801
|
+
preferred_mode: mode,
|
|
6802
|
+
system_prompt: systemPrompt,
|
|
6803
|
+
reasoning_effort: options?.reasoningEffort,
|
|
6804
|
+
session_type: options?.sessionType || "normal",
|
|
6805
|
+
hidden: options?.hidden === true,
|
|
6806
|
+
parent_session_id: options?.parentSessionId,
|
|
6807
|
+
expires_at: options?.expiresAt,
|
|
6808
|
+
created_at: timestamp,
|
|
6809
|
+
updated_at: timestamp
|
|
6810
|
+
};
|
|
6811
|
+
this.sessions.set(session.id, session);
|
|
6812
|
+
return session;
|
|
6813
|
+
});
|
|
6560
6814
|
}
|
|
6561
6815
|
updateSessionProviderId(sessionId, providerId) {
|
|
6562
|
-
this.
|
|
6563
|
-
|
|
6564
|
-
|
|
6816
|
+
this.mutateSessions(() => {
|
|
6817
|
+
const s = this.sessions.get(sessionId);
|
|
6818
|
+
if (!s) return;
|
|
6565
6819
|
s.provider_id = providerId;
|
|
6566
6820
|
s.updated_at = now();
|
|
6567
|
-
|
|
6568
|
-
}
|
|
6821
|
+
});
|
|
6569
6822
|
}
|
|
6570
6823
|
updateSession(sessionId, updates, options) {
|
|
6571
|
-
this.
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6824
|
+
this.mutateSessions(() => {
|
|
6825
|
+
const session = this.sessions.get(sessionId);
|
|
6826
|
+
if (!session) return;
|
|
6827
|
+
const next = {
|
|
6828
|
+
...session,
|
|
6829
|
+
...updates,
|
|
6830
|
+
id: session.id,
|
|
6831
|
+
updated_at: options?.touch === false ? session.updated_at : now()
|
|
6832
|
+
};
|
|
6833
|
+
this.sessions.set(sessionId, next);
|
|
6834
|
+
});
|
|
6582
6835
|
}
|
|
6583
6836
|
deleteSession(sessionId) {
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6837
|
+
const messagePath = path6.join(MESSAGES_DIR, `${sessionId}.json`);
|
|
6838
|
+
withFileLocks([SESSIONS_PATH, BINDINGS_PATH, messagePath], () => {
|
|
6839
|
+
this.reloadSessions();
|
|
6840
|
+
this.reloadBindings();
|
|
6841
|
+
this.sessions.delete(sessionId);
|
|
6842
|
+
for (const [key, binding] of this.bindings) {
|
|
6843
|
+
if (binding.codepilotSessionId === sessionId) {
|
|
6844
|
+
this.bindings.delete(key);
|
|
6845
|
+
}
|
|
6590
6846
|
}
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6847
|
+
this.messages.delete(sessionId);
|
|
6848
|
+
try {
|
|
6849
|
+
fs6.rmSync(messagePath, { force: true });
|
|
6850
|
+
} catch {
|
|
6851
|
+
}
|
|
6852
|
+
this.persistSessions();
|
|
6853
|
+
this.persistBindings();
|
|
6854
|
+
});
|
|
6599
6855
|
}
|
|
6600
6856
|
// ── Messages ──
|
|
6601
6857
|
addMessage(sessionId, role, content, _usage) {
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6858
|
+
this.mutateMessages(sessionId, (messages) => {
|
|
6859
|
+
messages.push({ role, content });
|
|
6860
|
+
});
|
|
6605
6861
|
}
|
|
6606
6862
|
getMessages(sessionId, opts) {
|
|
6607
|
-
const
|
|
6863
|
+
const messagePath = path6.join(MESSAGES_DIR, `${sessionId}.json`);
|
|
6864
|
+
const msgs = readJson(messagePath, []);
|
|
6865
|
+
this.messages.set(sessionId, msgs);
|
|
6608
6866
|
if (opts?.limit && opts.limit > 0) {
|
|
6609
6867
|
return { messages: msgs.slice(-opts.limit) };
|
|
6610
6868
|
}
|
|
@@ -6636,61 +6894,62 @@ var JsonFileStore = class {
|
|
|
6636
6894
|
}
|
|
6637
6895
|
}
|
|
6638
6896
|
setSessionRuntimeStatus(_sessionId, _status) {
|
|
6639
|
-
this.
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6897
|
+
this.mutateSessions(() => {
|
|
6898
|
+
const session = this.sessions.get(_sessionId);
|
|
6899
|
+
if (!session) return;
|
|
6900
|
+
const queuedCount = session.queued_count && session.queued_count > 0 ? session.queued_count : 0;
|
|
6901
|
+
let runtimeStatus;
|
|
6902
|
+
if (_status === "running") {
|
|
6903
|
+
runtimeStatus = queuedCount > 0 ? "queued" : "running";
|
|
6904
|
+
} else if (_status === "idle") {
|
|
6905
|
+
runtimeStatus = queuedCount > 0 ? "queued" : "idle";
|
|
6906
|
+
} else {
|
|
6907
|
+
runtimeStatus = session.runtime_status;
|
|
6908
|
+
}
|
|
6909
|
+
const next = {
|
|
6910
|
+
...session,
|
|
6911
|
+
runtime_status: runtimeStatus,
|
|
6912
|
+
last_runtime_update_at: now(),
|
|
6913
|
+
updated_at: now()
|
|
6914
|
+
};
|
|
6915
|
+
this.sessions.set(_sessionId, next);
|
|
6916
|
+
});
|
|
6659
6917
|
}
|
|
6660
6918
|
// ── SDK Session ──
|
|
6661
6919
|
updateSdkSessionId(sessionId, sdkSessionId) {
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
s
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6920
|
+
withFileLocks([SESSIONS_PATH, BINDINGS_PATH], () => {
|
|
6921
|
+
this.reloadSessions();
|
|
6922
|
+
this.reloadBindings();
|
|
6923
|
+
const s = this.sessions.get(sessionId);
|
|
6924
|
+
if (s) {
|
|
6925
|
+
s.sdk_session_id = sdkSessionId;
|
|
6926
|
+
if (sdkSessionId) {
|
|
6927
|
+
s.codex_thread_id = sdkSessionId;
|
|
6928
|
+
s.thread_origin = s.thread_origin || "bridge";
|
|
6929
|
+
} else {
|
|
6930
|
+
delete s.codex_thread_id;
|
|
6931
|
+
if (s.thread_origin !== "desktop") {
|
|
6932
|
+
delete s.thread_origin;
|
|
6933
|
+
}
|
|
6674
6934
|
}
|
|
6935
|
+
s.updated_at = now();
|
|
6675
6936
|
}
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
if (b.codepilotSessionId === sessionId) {
|
|
6681
|
-
this.bindings.set(key, { ...b, sdkSessionId, updatedAt: now() });
|
|
6937
|
+
for (const [key, b] of this.bindings) {
|
|
6938
|
+
if (b.codepilotSessionId === sessionId) {
|
|
6939
|
+
this.bindings.set(key, { ...b, sdkSessionId, updatedAt: now() });
|
|
6940
|
+
}
|
|
6682
6941
|
}
|
|
6683
|
-
|
|
6684
|
-
|
|
6942
|
+
this.persistSessions();
|
|
6943
|
+
this.persistBindings();
|
|
6944
|
+
});
|
|
6685
6945
|
}
|
|
6686
6946
|
updateSessionModel(sessionId, model) {
|
|
6687
|
-
this.
|
|
6688
|
-
|
|
6689
|
-
|
|
6947
|
+
this.mutateSessions(() => {
|
|
6948
|
+
const s = this.sessions.get(sessionId);
|
|
6949
|
+
if (!s) return;
|
|
6690
6950
|
s.model = model;
|
|
6691
6951
|
s.updated_at = now();
|
|
6692
|
-
|
|
6693
|
-
}
|
|
6952
|
+
});
|
|
6694
6953
|
}
|
|
6695
6954
|
syncSdkTasks(_sessionId, _todos) {
|
|
6696
6955
|
}
|
|
@@ -6703,66 +6962,84 @@ var JsonFileStore = class {
|
|
|
6703
6962
|
}
|
|
6704
6963
|
// ── Audit & Dedup ──
|
|
6705
6964
|
insertAuditLog(entry) {
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6965
|
+
withFileLock(AUDIT_PATH, () => {
|
|
6966
|
+
this.reloadAudit();
|
|
6967
|
+
this.auditLog.push({
|
|
6968
|
+
...entry,
|
|
6969
|
+
id: uuid(),
|
|
6970
|
+
createdAt: now()
|
|
6971
|
+
});
|
|
6972
|
+
if (this.auditLog.length > 1e3) {
|
|
6973
|
+
this.auditLog = this.auditLog.slice(-1e3);
|
|
6974
|
+
}
|
|
6975
|
+
this.persistAudit();
|
|
6710
6976
|
});
|
|
6711
|
-
if (this.auditLog.length > 1e3) {
|
|
6712
|
-
this.auditLog = this.auditLog.slice(-1e3);
|
|
6713
|
-
}
|
|
6714
|
-
this.persistAudit();
|
|
6715
6977
|
}
|
|
6716
6978
|
checkDedup(key) {
|
|
6979
|
+
this.reloadDedup();
|
|
6717
6980
|
const ts = this.dedupKeys.get(key);
|
|
6718
6981
|
if (ts === void 0) return false;
|
|
6719
|
-
if (Date.now() - ts >
|
|
6982
|
+
if (Date.now() - ts > dedupTtlMs(key)) {
|
|
6720
6983
|
this.dedupKeys.delete(key);
|
|
6721
6984
|
return false;
|
|
6722
6985
|
}
|
|
6723
6986
|
return true;
|
|
6724
6987
|
}
|
|
6725
6988
|
insertDedup(key) {
|
|
6726
|
-
|
|
6727
|
-
|
|
6989
|
+
withFileLock(DEDUP_PATH, () => {
|
|
6990
|
+
this.reloadDedup();
|
|
6991
|
+
this.dedupKeys.set(key, Date.now());
|
|
6992
|
+
this.persistDedup();
|
|
6993
|
+
});
|
|
6728
6994
|
}
|
|
6729
6995
|
cleanupExpiredDedup() {
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6996
|
+
withFileLock(DEDUP_PATH, () => {
|
|
6997
|
+
this.reloadDedup();
|
|
6998
|
+
const timestamp = Date.now();
|
|
6999
|
+
let changed = false;
|
|
7000
|
+
for (const [key, ts] of this.dedupKeys) {
|
|
7001
|
+
if (timestamp - ts > dedupTtlMs(key)) {
|
|
7002
|
+
this.dedupKeys.delete(key);
|
|
7003
|
+
changed = true;
|
|
7004
|
+
}
|
|
6736
7005
|
}
|
|
6737
|
-
|
|
6738
|
-
|
|
7006
|
+
if (changed) this.persistDedup();
|
|
7007
|
+
});
|
|
6739
7008
|
}
|
|
6740
7009
|
insertOutboundRef(_ref) {
|
|
6741
7010
|
}
|
|
6742
7011
|
// ── Permission Links ──
|
|
6743
7012
|
insertPermissionLink(link) {
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
7013
|
+
withFileLock(PERMISSIONS_PATH, () => {
|
|
7014
|
+
this.reloadPermissions();
|
|
7015
|
+
const record = {
|
|
7016
|
+
permissionRequestId: link.permissionRequestId,
|
|
7017
|
+
chatId: link.chatId,
|
|
7018
|
+
messageId: link.messageId,
|
|
7019
|
+
sessionId: link.sessionId,
|
|
7020
|
+
resolved: false,
|
|
7021
|
+
suggestions: link.suggestions
|
|
7022
|
+
};
|
|
7023
|
+
this.permissionLinks.set(link.permissionRequestId, record);
|
|
7024
|
+
this.persistPermissions();
|
|
7025
|
+
});
|
|
6754
7026
|
}
|
|
6755
7027
|
getPermissionLink(permissionRequestId) {
|
|
7028
|
+
this.reloadPermissions();
|
|
6756
7029
|
return this.permissionLinks.get(permissionRequestId) ?? null;
|
|
6757
7030
|
}
|
|
6758
7031
|
markPermissionLinkResolved(permissionRequestId) {
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
7032
|
+
return withFileLock(PERMISSIONS_PATH, () => {
|
|
7033
|
+
this.reloadPermissions();
|
|
7034
|
+
const link = this.permissionLinks.get(permissionRequestId);
|
|
7035
|
+
if (!link || link.resolved) return false;
|
|
7036
|
+
link.resolved = true;
|
|
7037
|
+
this.persistPermissions();
|
|
7038
|
+
return true;
|
|
7039
|
+
});
|
|
6764
7040
|
}
|
|
6765
7041
|
listPendingPermissionLinksByChat(chatId) {
|
|
7042
|
+
this.reloadPermissions();
|
|
6766
7043
|
const result = [];
|
|
6767
7044
|
for (const link of this.permissionLinks.values()) {
|
|
6768
7045
|
if (link.chatId === chatId && !link.resolved) {
|
|
@@ -6773,23 +7050,27 @@ var JsonFileStore = class {
|
|
|
6773
7050
|
}
|
|
6774
7051
|
// ── Channel Offsets ──
|
|
6775
7052
|
getChannelOffset(key) {
|
|
7053
|
+
this.reloadOffsets();
|
|
6776
7054
|
return this.offsets.get(key) ?? "0";
|
|
6777
7055
|
}
|
|
6778
7056
|
setChannelOffset(key, offset) {
|
|
6779
|
-
|
|
6780
|
-
|
|
7057
|
+
withFileLock(OFFSETS_PATH, () => {
|
|
7058
|
+
this.reloadOffsets();
|
|
7059
|
+
this.offsets.set(key, offset);
|
|
7060
|
+
this.persistOffsets();
|
|
7061
|
+
});
|
|
6781
7062
|
}
|
|
6782
7063
|
};
|
|
6783
7064
|
|
|
6784
7065
|
// src/weixin-login.ts
|
|
6785
7066
|
var import_qrcode = __toESM(require_lib(), 1);
|
|
6786
|
-
import
|
|
7067
|
+
import fs8 from "node:fs";
|
|
6787
7068
|
import path8 from "node:path";
|
|
6788
|
-
import
|
|
7069
|
+
import crypto7 from "node:crypto";
|
|
6789
7070
|
import { spawn as spawn2 } from "node:child_process";
|
|
6790
7071
|
|
|
6791
7072
|
// src/adapters/weixin/weixin-api.ts
|
|
6792
|
-
import
|
|
7073
|
+
import crypto5 from "node:crypto";
|
|
6793
7074
|
|
|
6794
7075
|
// src/adapters/weixin/weixin-types.ts
|
|
6795
7076
|
var DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com";
|
|
@@ -6838,7 +7119,8 @@ async function pollLoginQrStatus(qrcode, baseUrl) {
|
|
|
6838
7119
|
}
|
|
6839
7120
|
|
|
6840
7121
|
// src/weixin-store.ts
|
|
6841
|
-
import
|
|
7122
|
+
import fs7 from "node:fs";
|
|
7123
|
+
import crypto6 from "node:crypto";
|
|
6842
7124
|
import path7 from "node:path";
|
|
6843
7125
|
var DATA_DIR2 = path7.join(CTI_HOME, "data");
|
|
6844
7126
|
var ACCOUNTS_PATH = path7.join(DATA_DIR2, "weixin-accounts.json");
|
|
@@ -6846,19 +7128,27 @@ var CONTEXT_TOKENS_PATH = path7.join(DATA_DIR2, "weixin-context-tokens.json");
|
|
|
6846
7128
|
var DEFAULT_BASE_URL2 = "https://ilinkai.weixin.qq.com";
|
|
6847
7129
|
var DEFAULT_CDN_BASE_URL2 = "https://novac2c.cdn.weixin.qq.com/c2c";
|
|
6848
7130
|
function ensureDir2(dir) {
|
|
6849
|
-
|
|
7131
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
6850
7132
|
}
|
|
6851
7133
|
function atomicWrite2(filePath, data) {
|
|
6852
|
-
const tmpPath = `${filePath}.tmp`;
|
|
6853
|
-
|
|
6854
|
-
|
|
7134
|
+
const tmpPath = `${filePath}.${process.pid}.${crypto6.randomUUID()}.tmp`;
|
|
7135
|
+
try {
|
|
7136
|
+
fs7.writeFileSync(tmpPath, data, "utf-8");
|
|
7137
|
+
fs7.renameSync(tmpPath, filePath);
|
|
7138
|
+
} finally {
|
|
7139
|
+
try {
|
|
7140
|
+
fs7.rmSync(tmpPath, { force: true });
|
|
7141
|
+
} catch {
|
|
7142
|
+
}
|
|
7143
|
+
}
|
|
6855
7144
|
}
|
|
6856
7145
|
function readJson2(filePath, fallback) {
|
|
6857
7146
|
try {
|
|
6858
|
-
const raw =
|
|
7147
|
+
const raw = fs7.readFileSync(filePath, "utf-8");
|
|
6859
7148
|
return JSON.parse(raw);
|
|
6860
|
-
} catch {
|
|
6861
|
-
return fallback;
|
|
7149
|
+
} catch (error) {
|
|
7150
|
+
if (error.code === "ENOENT") return fallback;
|
|
7151
|
+
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u5FAE\u4FE1\u6570\u636E\u6587\u4EF6 ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
6862
7152
|
}
|
|
6863
7153
|
}
|
|
6864
7154
|
function now2() {
|
|
@@ -6907,37 +7197,38 @@ function listWeixinAccounts() {
|
|
|
6907
7197
|
return sortAccountsByRecency(readAccounts());
|
|
6908
7198
|
}
|
|
6909
7199
|
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
|
-
|
|
7200
|
+
return withFileLock(ACCOUNTS_PATH, () => {
|
|
7201
|
+
const accounts = readAccounts();
|
|
7202
|
+
const existing = accounts.find((account2) => account2.accountId === params.accountId);
|
|
7203
|
+
const timestamp = now2();
|
|
7204
|
+
const account = existing ? {
|
|
7205
|
+
...existing,
|
|
7206
|
+
userId: params.userId ?? existing.userId,
|
|
7207
|
+
baseUrl: params.baseUrl ?? existing.baseUrl,
|
|
7208
|
+
cdnBaseUrl: params.cdnBaseUrl ?? existing.cdnBaseUrl,
|
|
7209
|
+
token: params.token ?? existing.token,
|
|
7210
|
+
name: params.name ?? existing.name,
|
|
7211
|
+
enabled: params.enabled ?? existing.enabled,
|
|
7212
|
+
lastLoginAt: timestamp,
|
|
7213
|
+
updatedAt: timestamp
|
|
7214
|
+
} : {
|
|
7215
|
+
accountId: params.accountId,
|
|
7216
|
+
userId: params.userId ?? "",
|
|
7217
|
+
baseUrl: params.baseUrl ?? DEFAULT_BASE_URL2,
|
|
7218
|
+
cdnBaseUrl: params.cdnBaseUrl ?? DEFAULT_CDN_BASE_URL2,
|
|
7219
|
+
token: params.token ?? "",
|
|
7220
|
+
name: params.name ?? params.accountId,
|
|
7221
|
+
enabled: params.enabled ?? true,
|
|
7222
|
+
lastLoginAt: timestamp,
|
|
7223
|
+
createdAt: timestamp,
|
|
7224
|
+
updatedAt: timestamp
|
|
7225
|
+
};
|
|
7226
|
+
writeAccounts([
|
|
7227
|
+
account,
|
|
7228
|
+
...accounts.filter((item) => item.accountId !== account.accountId)
|
|
7229
|
+
]);
|
|
7230
|
+
return account;
|
|
7231
|
+
});
|
|
6941
7232
|
}
|
|
6942
7233
|
|
|
6943
7234
|
// src/weixin-login.ts
|
|
@@ -6949,7 +7240,7 @@ var RUNTIME_DIR = path8.join(CTI_HOME, "runtime");
|
|
|
6949
7240
|
var HTML_PATH = path8.join(RUNTIME_DIR, "weixin-login.html");
|
|
6950
7241
|
var webLoginSessions = /* @__PURE__ */ new Map();
|
|
6951
7242
|
function ensureRuntimeDir() {
|
|
6952
|
-
|
|
7243
|
+
fs8.mkdirSync(RUNTIME_DIR, { recursive: true });
|
|
6953
7244
|
}
|
|
6954
7245
|
function escapeHtml(text2) {
|
|
6955
7246
|
return text2.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
@@ -7262,7 +7553,7 @@ function buildWeixinLoginPopupHtml(sessionId) {
|
|
|
7262
7553
|
async function writeQrHtml(session) {
|
|
7263
7554
|
ensureRuntimeDir();
|
|
7264
7555
|
const qrSvg = await buildQrSvg(session.qrImageUrl);
|
|
7265
|
-
|
|
7556
|
+
fs8.writeFileSync(HTML_PATH, buildQrHtml(qrSvg), "utf-8");
|
|
7266
7557
|
}
|
|
7267
7558
|
function openQrHtml() {
|
|
7268
7559
|
try {
|
|
@@ -7451,7 +7742,7 @@ async function startWeixinLoginWebSession(options = {}) {
|
|
|
7451
7742
|
ensureRuntimeDir();
|
|
7452
7743
|
const config = options.config || {};
|
|
7453
7744
|
const seed = await createSession(0, config.baseUrl);
|
|
7454
|
-
const sessionId =
|
|
7745
|
+
const sessionId = crypto7.randomUUID();
|
|
7455
7746
|
const qrSvg = await buildQrSvg(seed.qrImageUrl);
|
|
7456
7747
|
const session = {
|
|
7457
7748
|
id: sessionId,
|
|
@@ -7533,14 +7824,60 @@ if (isMainModule) {
|
|
|
7533
7824
|
}
|
|
7534
7825
|
|
|
7535
7826
|
// src/codex-models.ts
|
|
7536
|
-
import
|
|
7827
|
+
import fs9 from "node:fs";
|
|
7537
7828
|
import os4 from "node:os";
|
|
7538
7829
|
import path9 from "node:path";
|
|
7539
7830
|
var DEFAULT_CODEX_CONFIG_PATH = path9.join(os4.homedir(), ".codex", "config.toml");
|
|
7540
7831
|
var DEFAULT_CODEX_MODELS_CACHE_PATH = path9.join(os4.homedir(), ".codex", "models_cache.json");
|
|
7832
|
+
var REASONING_LEVEL_DESCRIPTIONS = {
|
|
7833
|
+
low: "Fast responses with lighter reasoning",
|
|
7834
|
+
medium: "Balances speed and reasoning depth for everyday tasks",
|
|
7835
|
+
high: "Greater reasoning depth for complex problems",
|
|
7836
|
+
xhigh: "Extra high reasoning depth for complex problems",
|
|
7837
|
+
max: "Maximum reasoning depth for very hard problems",
|
|
7838
|
+
ultra: "Ultra reasoning depth for the hardest problems"
|
|
7839
|
+
};
|
|
7840
|
+
function buildReasoningLevels(efforts) {
|
|
7841
|
+
return efforts.map((effort) => ({
|
|
7842
|
+
effort,
|
|
7843
|
+
description: REASONING_LEVEL_DESCRIPTIONS[effort] || ""
|
|
7844
|
+
}));
|
|
7845
|
+
}
|
|
7846
|
+
function cloneCodexModel(model) {
|
|
7847
|
+
return {
|
|
7848
|
+
...model,
|
|
7849
|
+
supportedReasoningLevels: model.supportedReasoningLevels.map((level) => ({ ...level }))
|
|
7850
|
+
};
|
|
7851
|
+
}
|
|
7852
|
+
var KNOWN_CODEX_MODEL_FALLBACKS = [
|
|
7853
|
+
{
|
|
7854
|
+
slug: "gpt-5.6-sol",
|
|
7855
|
+
displayName: "GPT-5.6-Sol",
|
|
7856
|
+
visibility: "list",
|
|
7857
|
+
supportedInApi: true,
|
|
7858
|
+
defaultReasoningLevel: "low",
|
|
7859
|
+
supportedReasoningLevels: buildReasoningLevels(["low", "medium", "high", "xhigh", "max", "ultra"])
|
|
7860
|
+
},
|
|
7861
|
+
{
|
|
7862
|
+
slug: "gpt-5.6-terra",
|
|
7863
|
+
displayName: "GPT-5.6-Terra",
|
|
7864
|
+
visibility: "list",
|
|
7865
|
+
supportedInApi: true,
|
|
7866
|
+
defaultReasoningLevel: "medium",
|
|
7867
|
+
supportedReasoningLevels: buildReasoningLevels(["low", "medium", "high", "xhigh", "max", "ultra"])
|
|
7868
|
+
},
|
|
7869
|
+
{
|
|
7870
|
+
slug: "gpt-5.6-luna",
|
|
7871
|
+
displayName: "GPT-5.6-Luna",
|
|
7872
|
+
visibility: "list",
|
|
7873
|
+
supportedInApi: true,
|
|
7874
|
+
defaultReasoningLevel: "medium",
|
|
7875
|
+
supportedReasoningLevels: buildReasoningLevels(["low", "medium", "high", "xhigh", "max"])
|
|
7876
|
+
}
|
|
7877
|
+
];
|
|
7541
7878
|
function readConfiguredCodexModel(configPath = DEFAULT_CODEX_CONFIG_PATH) {
|
|
7542
7879
|
try {
|
|
7543
|
-
const raw =
|
|
7880
|
+
const raw = fs9.readFileSync(configPath, "utf-8");
|
|
7544
7881
|
let inSection = false;
|
|
7545
7882
|
for (const line of raw.split(/\r?\n/)) {
|
|
7546
7883
|
const trimmed = line.trim();
|
|
@@ -7584,7 +7921,7 @@ function parseSupportedReasoningLevels(value) {
|
|
|
7584
7921
|
}
|
|
7585
7922
|
function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
|
|
7586
7923
|
try {
|
|
7587
|
-
const raw =
|
|
7924
|
+
const raw = fs9.readFileSync(cachePath, "utf-8");
|
|
7588
7925
|
const parsed = JSON.parse(raw);
|
|
7589
7926
|
if (!Array.isArray(parsed.models)) return [];
|
|
7590
7927
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -7609,8 +7946,113 @@ function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
|
|
|
7609
7946
|
return [];
|
|
7610
7947
|
}
|
|
7611
7948
|
}
|
|
7612
|
-
function
|
|
7613
|
-
|
|
7949
|
+
function buildSyntheticCodexModel(slug) {
|
|
7950
|
+
const normalized = slug.trim();
|
|
7951
|
+
if (!normalized) return null;
|
|
7952
|
+
return {
|
|
7953
|
+
slug: normalized,
|
|
7954
|
+
displayName: normalized,
|
|
7955
|
+
visibility: "list",
|
|
7956
|
+
supportedInApi: true,
|
|
7957
|
+
supportedReasoningLevels: []
|
|
7958
|
+
};
|
|
7959
|
+
}
|
|
7960
|
+
function listAvailableCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH, additionalModelSlugs = []) {
|
|
7961
|
+
const cachedModels = listCachedCodexModels(cachePath);
|
|
7962
|
+
const hiddenCachedSlugs = new Set(
|
|
7963
|
+
cachedModels.filter((model) => model.visibility === "hide").map((model) => model.slug)
|
|
7964
|
+
);
|
|
7965
|
+
const merged = [];
|
|
7966
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7967
|
+
const addModel = (model, force = false) => {
|
|
7968
|
+
if (!force && hiddenCachedSlugs.has(model.slug)) return;
|
|
7969
|
+
if (seen.has(model.slug)) return;
|
|
7970
|
+
seen.add(model.slug);
|
|
7971
|
+
merged.push(cloneCodexModel(model));
|
|
7972
|
+
};
|
|
7973
|
+
for (const model of cachedModels) {
|
|
7974
|
+
if (model.visibility !== "hide") {
|
|
7975
|
+
addModel(model, true);
|
|
7976
|
+
}
|
|
7977
|
+
}
|
|
7978
|
+
for (const model of KNOWN_CODEX_MODEL_FALLBACKS) {
|
|
7979
|
+
addModel(model);
|
|
7980
|
+
}
|
|
7981
|
+
for (const slug of additionalModelSlugs) {
|
|
7982
|
+
const syntheticModel = buildSyntheticCodexModel(slug);
|
|
7983
|
+
if (syntheticModel) {
|
|
7984
|
+
addModel(syntheticModel, true);
|
|
7985
|
+
}
|
|
7986
|
+
}
|
|
7987
|
+
return merged;
|
|
7988
|
+
}
|
|
7989
|
+
|
|
7990
|
+
// src/ui-http-security.ts
|
|
7991
|
+
var DEFAULT_JSON_BODY_LIMIT_BYTES = 1024 * 1024;
|
|
7992
|
+
var HttpRequestError = class extends Error {
|
|
7993
|
+
constructor(statusCode, message) {
|
|
7994
|
+
super(message);
|
|
7995
|
+
this.statusCode = statusCode;
|
|
7996
|
+
this.name = "HttpRequestError";
|
|
7997
|
+
}
|
|
7998
|
+
};
|
|
7999
|
+
function isMutationMethod(method) {
|
|
8000
|
+
return method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE";
|
|
8001
|
+
}
|
|
8002
|
+
function getExpectedOrigin(headers) {
|
|
8003
|
+
const host = headers.host?.trim();
|
|
8004
|
+
if (!host) throw new HttpRequestError(400, "\u8BF7\u6C42\u7F3A\u5C11 Host header\u3002");
|
|
8005
|
+
return `http://${host}`;
|
|
8006
|
+
}
|
|
8007
|
+
function assertTrustedMutationRequest(request) {
|
|
8008
|
+
if (!isMutationMethod(request.method)) return;
|
|
8009
|
+
const fetchSite = request.headers["sec-fetch-site"];
|
|
8010
|
+
if (fetchSite === "cross-site") {
|
|
8011
|
+
throw new HttpRequestError(403, "\u5DF2\u62D2\u7EDD\u8DE8\u7AD9\u7BA1\u7406\u8BF7\u6C42\u3002");
|
|
8012
|
+
}
|
|
8013
|
+
const origin = request.headers.origin;
|
|
8014
|
+
if (!origin) return;
|
|
8015
|
+
if (origin === "null") {
|
|
8016
|
+
throw new HttpRequestError(403, "\u5DF2\u62D2\u7EDD\u6765\u6E90\u4E0D\u660E\u7684\u7BA1\u7406\u8BF7\u6C42\u3002");
|
|
8017
|
+
}
|
|
8018
|
+
let normalizedOrigin;
|
|
8019
|
+
try {
|
|
8020
|
+
normalizedOrigin = new URL(origin).origin;
|
|
8021
|
+
} catch {
|
|
8022
|
+
throw new HttpRequestError(403, "\u8BF7\u6C42 Origin \u65E0\u6548\u3002");
|
|
8023
|
+
}
|
|
8024
|
+
if (normalizedOrigin !== getExpectedOrigin(request.headers)) {
|
|
8025
|
+
throw new HttpRequestError(403, "\u5DF2\u62D2\u7EDD\u975E\u540C\u6E90\u7BA1\u7406\u8BF7\u6C42\u3002");
|
|
8026
|
+
}
|
|
8027
|
+
}
|
|
8028
|
+
async function readJsonBody(request, maxBytes = DEFAULT_JSON_BODY_LIMIT_BYTES) {
|
|
8029
|
+
const boundedMaxBytes = Math.max(1, maxBytes);
|
|
8030
|
+
const declaredLength = Number(request.headers["content-length"]);
|
|
8031
|
+
if (Number.isFinite(declaredLength) && declaredLength > boundedMaxBytes) {
|
|
8032
|
+
throw new HttpRequestError(413, `\u8BF7\u6C42\u4F53\u8D85\u8FC7 ${boundedMaxBytes} \u5B57\u8282\u9650\u5236\u3002`);
|
|
8033
|
+
}
|
|
8034
|
+
const chunks = [];
|
|
8035
|
+
let receivedBytes = 0;
|
|
8036
|
+
for await (const chunk of request) {
|
|
8037
|
+
const buffer = Buffer.from(chunk);
|
|
8038
|
+
receivedBytes += buffer.length;
|
|
8039
|
+
if (receivedBytes > boundedMaxBytes) {
|
|
8040
|
+
request.resume();
|
|
8041
|
+
throw new HttpRequestError(413, `\u8BF7\u6C42\u4F53\u8D85\u8FC7 ${boundedMaxBytes} \u5B57\u8282\u9650\u5236\u3002`);
|
|
8042
|
+
}
|
|
8043
|
+
chunks.push(buffer);
|
|
8044
|
+
}
|
|
8045
|
+
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
8046
|
+
if (!raw) return {};
|
|
8047
|
+
const contentType = request.headers["content-type"] || "";
|
|
8048
|
+
if (!/^application\/json(?:\s*;|$)/i.test(contentType)) {
|
|
8049
|
+
throw new HttpRequestError(415, "\u8BF7\u6C42\u4F53\u5FC5\u987B\u4F7F\u7528 application/json\u3002");
|
|
8050
|
+
}
|
|
8051
|
+
try {
|
|
8052
|
+
return JSON.parse(raw);
|
|
8053
|
+
} catch {
|
|
8054
|
+
throw new HttpRequestError(400, "\u8BF7\u6C42\u4F53\u4E0D\u662F\u6709\u6548 JSON\u3002");
|
|
8055
|
+
}
|
|
7614
8056
|
}
|
|
7615
8057
|
|
|
7616
8058
|
// src/ui-server.ts
|
|
@@ -7626,8 +8068,8 @@ function parsePreferredPort() {
|
|
|
7626
8068
|
if (!Number.isInteger(raw) || raw <= 0 || raw > 65535) return 4781;
|
|
7627
8069
|
return raw;
|
|
7628
8070
|
}
|
|
7629
|
-
function getAvailableCodexModels() {
|
|
7630
|
-
return
|
|
8071
|
+
function getAvailableCodexModels(additionalModelSlugs = []) {
|
|
8072
|
+
return listAvailableCodexModels(void 0, additionalModelSlugs);
|
|
7631
8073
|
}
|
|
7632
8074
|
function getAvailableCodexModelSlugs(models = getAvailableCodexModels()) {
|
|
7633
8075
|
return new Set(models.map((model) => model.slug));
|
|
@@ -7698,14 +8140,6 @@ function text(response, statusCode, body) {
|
|
|
7698
8140
|
response.writeHead(statusCode, { "Content-Type": "text/plain; charset=utf-8" });
|
|
7699
8141
|
response.end(body);
|
|
7700
8142
|
}
|
|
7701
|
-
async function readJsonBody(request) {
|
|
7702
|
-
const chunks = [];
|
|
7703
|
-
for await (const chunk of request) {
|
|
7704
|
-
chunks.push(Buffer.from(chunk));
|
|
7705
|
-
}
|
|
7706
|
-
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
7707
|
-
return raw ? JSON.parse(raw) : {};
|
|
7708
|
-
}
|
|
7709
8143
|
function asString(value) {
|
|
7710
8144
|
if (typeof value !== "string") return void 0;
|
|
7711
8145
|
const trimmed = value.trim();
|
|
@@ -7786,14 +8220,14 @@ function channelToPayload(channel) {
|
|
|
7786
8220
|
};
|
|
7787
8221
|
}
|
|
7788
8222
|
function generateAccessToken() {
|
|
7789
|
-
return
|
|
8223
|
+
return crypto8.randomBytes(18).toString("base64url");
|
|
7790
8224
|
}
|
|
7791
8225
|
function timingSafeMatch(left, right) {
|
|
7792
8226
|
if (!left || !right) return false;
|
|
7793
8227
|
const leftBuffer = Buffer.from(left);
|
|
7794
8228
|
const rightBuffer = Buffer.from(right);
|
|
7795
8229
|
if (leftBuffer.length !== rightBuffer.length) return false;
|
|
7796
|
-
return
|
|
8230
|
+
return crypto8.timingSafeEqual(leftBuffer, rightBuffer);
|
|
7797
8231
|
}
|
|
7798
8232
|
function parseCookies(request) {
|
|
7799
8233
|
const header = request.headers.cookie;
|
|
@@ -7854,8 +8288,8 @@ function isRemoteAuthenticated(request, config) {
|
|
|
7854
8288
|
return timingSafeMatch(parseCookies(request).get(AUTH_COOKIE_NAME), config.uiAccessToken);
|
|
7855
8289
|
}
|
|
7856
8290
|
function configToPayload(config) {
|
|
7857
|
-
const availableModels = getAvailableCodexModels();
|
|
7858
8291
|
const codexDefaultModel = readConfiguredCodexModel() || "";
|
|
8292
|
+
const availableModels = getAvailableCodexModels([config.defaultModel || "", codexDefaultModel]);
|
|
7859
8293
|
const effectiveModel = config.defaultModel || codexDefaultModel;
|
|
7860
8294
|
return {
|
|
7861
8295
|
runtime: config.runtime,
|
|
@@ -7878,10 +8312,14 @@ function configToPayload(config) {
|
|
|
7878
8312
|
}
|
|
7879
8313
|
function mergeConfig(payload) {
|
|
7880
8314
|
const current = loadConfig();
|
|
7881
|
-
const availableCodexModels = getAvailableCodexModels();
|
|
7882
|
-
const availableCodexModelSlugs = getAvailableCodexModelSlugs(availableCodexModels);
|
|
7883
8315
|
const codexDefaultModel = readConfiguredCodexModel() || "";
|
|
7884
8316
|
const rawDefaultModel = typeof payload.defaultModel === "string" ? payload.defaultModel.trim() : void 0;
|
|
8317
|
+
const availableCodexModels = getAvailableCodexModels([
|
|
8318
|
+
current.defaultModel || "",
|
|
8319
|
+
codexDefaultModel,
|
|
8320
|
+
rawDefaultModel || ""
|
|
8321
|
+
]);
|
|
8322
|
+
const availableCodexModelSlugs = getAvailableCodexModelSlugs(availableCodexModels);
|
|
7885
8323
|
const nextDefaultModel = rawDefaultModel === void 0 ? current.defaultModel : rawDefaultModel === "" ? void 0 : availableCodexModelSlugs.has(rawDefaultModel) ? rawDefaultModel : current.defaultModel;
|
|
7886
8324
|
const currentEffectiveModel = current.defaultModel || codexDefaultModel;
|
|
7887
8325
|
const nextEffectiveModel = nextDefaultModel || codexDefaultModel;
|
|
@@ -11540,6 +11978,11 @@ function renderHtml() {
|
|
|
11540
11978
|
}
|
|
11541
11979
|
var server = http.createServer(async (request, response) => {
|
|
11542
11980
|
try {
|
|
11981
|
+
response.setHeader("X-Content-Type-Options", "nosniff");
|
|
11982
|
+
response.setHeader("X-Frame-Options", "DENY");
|
|
11983
|
+
response.setHeader("Referrer-Policy", "no-referrer");
|
|
11984
|
+
response.setHeader("Cache-Control", "no-store");
|
|
11985
|
+
assertTrustedMutationRequest(request);
|
|
11543
11986
|
const url = new URL(request.url || "/", getUiServerUrl(port));
|
|
11544
11987
|
const config = loadConfig();
|
|
11545
11988
|
const localRequest = isLocalRequest(request);
|
|
@@ -11877,16 +12320,18 @@ var server = http.createServer(async (request, response) => {
|
|
|
11877
12320
|
}
|
|
11878
12321
|
text(response, 404, "Not found");
|
|
11879
12322
|
} catch (error) {
|
|
11880
|
-
|
|
12323
|
+
const statusCode = error instanceof HttpRequestError ? error.statusCode : 500;
|
|
12324
|
+
json(response, statusCode, {
|
|
11881
12325
|
error: error instanceof Error ? error.message : String(error)
|
|
11882
12326
|
});
|
|
11883
12327
|
}
|
|
11884
12328
|
});
|
|
11885
12329
|
async function startServer() {
|
|
11886
12330
|
port = await resolveUiPort(parsePreferredPort());
|
|
12331
|
+
const listenHost = loadConfig().uiAllowLan === true ? "0.0.0.0" : "127.0.0.1";
|
|
11887
12332
|
await new Promise((resolve, reject) => {
|
|
11888
12333
|
server.once("error", reject);
|
|
11889
|
-
server.listen(port,
|
|
12334
|
+
server.listen(port, listenHost, () => {
|
|
11890
12335
|
server.removeListener("error", reject);
|
|
11891
12336
|
resolve();
|
|
11892
12337
|
});
|