codex-to-im 1.0.20 → 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -4
- package/README_EN.md +44 -10
- package/config.env.example +22 -9
- package/dist/cli.mjs +272 -3
- package/dist/daemon.mjs +645 -317
- package/dist/ui-server.mjs +1485 -725
- package/docs/install-windows.md +41 -17
- package/package.json +1 -1
- package/scripts/doctor.sh +12 -5
package/dist/daemon.mjs
CHANGED
|
@@ -4231,6 +4231,8 @@ function getBridgeContext() {
|
|
|
4231
4231
|
|
|
4232
4232
|
// src/lib/bridge/channel-adapter.ts
|
|
4233
4233
|
var BaseChannelAdapter = class {
|
|
4234
|
+
/** Human-readable instance alias, when applicable. */
|
|
4235
|
+
alias;
|
|
4234
4236
|
/**
|
|
4235
4237
|
* Answer a callback query (e.g. Telegram inline button press).
|
|
4236
4238
|
* Not all platforms support this — default implementation is a no-op.
|
|
@@ -4239,12 +4241,12 @@ var BaseChannelAdapter = class {
|
|
|
4239
4241
|
}
|
|
4240
4242
|
};
|
|
4241
4243
|
var adapterFactories = /* @__PURE__ */ new Map();
|
|
4242
|
-
function registerAdapterFactory(
|
|
4243
|
-
adapterFactories.set(
|
|
4244
|
+
function registerAdapterFactory(provider, factory) {
|
|
4245
|
+
adapterFactories.set(provider, factory);
|
|
4244
4246
|
}
|
|
4245
|
-
function createAdapter(
|
|
4246
|
-
const factory = adapterFactories.get(
|
|
4247
|
-
return factory ? factory() : null;
|
|
4247
|
+
function createAdapter(instance) {
|
|
4248
|
+
const factory = adapterFactories.get(instance.provider);
|
|
4249
|
+
return factory ? factory(instance) : null;
|
|
4248
4250
|
}
|
|
4249
4251
|
function getRegisteredTypes() {
|
|
4250
4252
|
return Array.from(adapterFactories.keys());
|
|
@@ -4464,6 +4466,7 @@ function tokenShortHash(botToken) {
|
|
|
4464
4466
|
var MEDIA_GROUP_DEBOUNCE_MS = 500;
|
|
4465
4467
|
var TelegramAdapter = class extends BaseChannelAdapter {
|
|
4466
4468
|
channelType = "telegram";
|
|
4469
|
+
provider = "telegram";
|
|
4467
4470
|
running = false;
|
|
4468
4471
|
abortController = null;
|
|
4469
4472
|
queue = [];
|
|
@@ -5079,9 +5082,346 @@ registerAdapterFactory("telegram", () => new TelegramAdapter());
|
|
|
5079
5082
|
|
|
5080
5083
|
// src/lib/bridge/adapters/feishu-adapter.ts
|
|
5081
5084
|
import crypto2 from "crypto";
|
|
5085
|
+
import fs2 from "node:fs";
|
|
5086
|
+
import path2 from "node:path";
|
|
5087
|
+
import * as lark from "@larksuiteoapi/node-sdk";
|
|
5088
|
+
|
|
5089
|
+
// src/config.ts
|
|
5082
5090
|
import fs from "node:fs";
|
|
5091
|
+
import os from "node:os";
|
|
5083
5092
|
import path from "node:path";
|
|
5084
|
-
|
|
5093
|
+
function toFeishuConfig(channel) {
|
|
5094
|
+
return channel?.provider === "feishu" ? channel.config : void 0;
|
|
5095
|
+
}
|
|
5096
|
+
function toWeixinConfig(channel) {
|
|
5097
|
+
return channel?.provider === "weixin" ? channel.config : void 0;
|
|
5098
|
+
}
|
|
5099
|
+
var LEGACY_CTI_HOME = path.join(os.homedir(), ".claude-to-im");
|
|
5100
|
+
var DEFAULT_CTI_HOME = path.join(os.homedir(), ".codex-to-im");
|
|
5101
|
+
var DEFAULT_WORKSPACE_ROOT = path.join(os.homedir(), "cx2im");
|
|
5102
|
+
function resolveDefaultCtiHome() {
|
|
5103
|
+
if (fs.existsSync(DEFAULT_CTI_HOME)) return DEFAULT_CTI_HOME;
|
|
5104
|
+
if (fs.existsSync(LEGACY_CTI_HOME)) return LEGACY_CTI_HOME;
|
|
5105
|
+
return DEFAULT_CTI_HOME;
|
|
5106
|
+
}
|
|
5107
|
+
var CTI_HOME = process.env.CTI_HOME || resolveDefaultCtiHome();
|
|
5108
|
+
var CONFIG_PATH = path.join(CTI_HOME, "config.env");
|
|
5109
|
+
var CONFIG_V2_PATH = path.join(CTI_HOME, "config.v2.json");
|
|
5110
|
+
function expandHomePath(value) {
|
|
5111
|
+
if (!value) return value;
|
|
5112
|
+
if (value === "~") return os.homedir();
|
|
5113
|
+
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
5114
|
+
return path.join(os.homedir(), value.slice(2));
|
|
5115
|
+
}
|
|
5116
|
+
return value;
|
|
5117
|
+
}
|
|
5118
|
+
function parseEnvFile(content) {
|
|
5119
|
+
const entries = /* @__PURE__ */ new Map();
|
|
5120
|
+
for (const line of content.split("\n")) {
|
|
5121
|
+
const trimmed = line.trim();
|
|
5122
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
5123
|
+
const eqIdx = trimmed.indexOf("=");
|
|
5124
|
+
if (eqIdx === -1) continue;
|
|
5125
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
5126
|
+
let value = trimmed.slice(eqIdx + 1).trim();
|
|
5127
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
5128
|
+
value = value.slice(1, -1);
|
|
5129
|
+
}
|
|
5130
|
+
entries.set(key, value);
|
|
5131
|
+
}
|
|
5132
|
+
return entries;
|
|
5133
|
+
}
|
|
5134
|
+
function loadRawConfigEnv() {
|
|
5135
|
+
try {
|
|
5136
|
+
return parseEnvFile(fs.readFileSync(CONFIG_PATH, "utf-8"));
|
|
5137
|
+
} catch {
|
|
5138
|
+
return /* @__PURE__ */ new Map();
|
|
5139
|
+
}
|
|
5140
|
+
}
|
|
5141
|
+
function splitCsv(value) {
|
|
5142
|
+
if (!value) return void 0;
|
|
5143
|
+
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
5144
|
+
}
|
|
5145
|
+
function parsePositiveInt(value) {
|
|
5146
|
+
if (!value) return void 0;
|
|
5147
|
+
const parsed = Number(value);
|
|
5148
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return void 0;
|
|
5149
|
+
return Math.floor(parsed);
|
|
5150
|
+
}
|
|
5151
|
+
function parseSandboxMode(value) {
|
|
5152
|
+
if (value === "read-only" || value === "workspace-write" || value === "danger-full-access") {
|
|
5153
|
+
return value;
|
|
5154
|
+
}
|
|
5155
|
+
return void 0;
|
|
5156
|
+
}
|
|
5157
|
+
function parseReasoningEffort(value) {
|
|
5158
|
+
if (value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh") {
|
|
5159
|
+
return value;
|
|
5160
|
+
}
|
|
5161
|
+
return void 0;
|
|
5162
|
+
}
|
|
5163
|
+
function normalizeFeishuSite(value) {
|
|
5164
|
+
const normalized = (value || "").trim().replace(/\/+$/, "").toLowerCase();
|
|
5165
|
+
if (!normalized) return "feishu";
|
|
5166
|
+
if (normalized === "lark") return "lark";
|
|
5167
|
+
if (normalized === "feishu") return "feishu";
|
|
5168
|
+
if (normalized.includes("open.larksuite.com")) return "lark";
|
|
5169
|
+
return "feishu";
|
|
5170
|
+
}
|
|
5171
|
+
function feishuSiteToApiBaseUrl(site) {
|
|
5172
|
+
return normalizeFeishuSite(site) === "lark" ? "https://open.larksuite.com" : "https://open.feishu.cn";
|
|
5173
|
+
}
|
|
5174
|
+
function nowIso() {
|
|
5175
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
5176
|
+
}
|
|
5177
|
+
function ensureConfigDir() {
|
|
5178
|
+
fs.mkdirSync(CTI_HOME, { recursive: true });
|
|
5179
|
+
}
|
|
5180
|
+
function readConfigV2File() {
|
|
5181
|
+
try {
|
|
5182
|
+
const parsed = JSON.parse(fs.readFileSync(CONFIG_V2_PATH, "utf-8"));
|
|
5183
|
+
if (parsed && parsed.schemaVersion === 2 && parsed.runtime && Array.isArray(parsed.channels)) {
|
|
5184
|
+
return parsed;
|
|
5185
|
+
}
|
|
5186
|
+
return null;
|
|
5187
|
+
} catch {
|
|
5188
|
+
return null;
|
|
5189
|
+
}
|
|
5190
|
+
}
|
|
5191
|
+
function writeConfigV2File(config2) {
|
|
5192
|
+
ensureConfigDir();
|
|
5193
|
+
const tmpPath = CONFIG_V2_PATH + ".tmp";
|
|
5194
|
+
fs.writeFileSync(tmpPath, JSON.stringify(config2, null, 2), { mode: 384 });
|
|
5195
|
+
fs.renameSync(tmpPath, CONFIG_V2_PATH);
|
|
5196
|
+
}
|
|
5197
|
+
function defaultAliasForProvider(provider) {
|
|
5198
|
+
return provider === "feishu" ? "\u98DE\u4E66" : "\u5FAE\u4FE1";
|
|
5199
|
+
}
|
|
5200
|
+
function buildDefaultChannelId(provider) {
|
|
5201
|
+
return `${provider}-default`;
|
|
5202
|
+
}
|
|
5203
|
+
function migrateLegacyEnvToV2(env) {
|
|
5204
|
+
const rawRuntime = env.get("CTI_RUNTIME") || "codex";
|
|
5205
|
+
const runtime = ["claude", "codex", "auto"].includes(rawRuntime) ? rawRuntime : "codex";
|
|
5206
|
+
const enabledChannels = splitCsv(env.get("CTI_ENABLED_CHANNELS")) ?? ["feishu"];
|
|
5207
|
+
const timestamp = nowIso();
|
|
5208
|
+
const channels = [];
|
|
5209
|
+
const hasFeishuConfig = Boolean(
|
|
5210
|
+
env.get("CTI_FEISHU_APP_ID") || env.get("CTI_FEISHU_APP_SECRET") || env.get("CTI_FEISHU_ALLOWED_USERS") || enabledChannels.includes("feishu")
|
|
5211
|
+
);
|
|
5212
|
+
if (hasFeishuConfig) {
|
|
5213
|
+
channels.push({
|
|
5214
|
+
id: buildDefaultChannelId("feishu"),
|
|
5215
|
+
alias: defaultAliasForProvider("feishu"),
|
|
5216
|
+
provider: "feishu",
|
|
5217
|
+
enabled: enabledChannels.includes("feishu"),
|
|
5218
|
+
createdAt: timestamp,
|
|
5219
|
+
updatedAt: timestamp,
|
|
5220
|
+
config: {
|
|
5221
|
+
appId: env.get("CTI_FEISHU_APP_ID") || void 0,
|
|
5222
|
+
appSecret: env.get("CTI_FEISHU_APP_SECRET") || void 0,
|
|
5223
|
+
site: normalizeFeishuSite(env.get("CTI_FEISHU_SITE") || env.get("CTI_FEISHU_DOMAIN")),
|
|
5224
|
+
allowedUsers: splitCsv(env.get("CTI_FEISHU_ALLOWED_USERS")),
|
|
5225
|
+
streamingEnabled: env.has("CTI_FEISHU_STREAMING_ENABLED") ? env.get("CTI_FEISHU_STREAMING_ENABLED") === "true" : true,
|
|
5226
|
+
feedbackMarkdownEnabled: env.has("CTI_FEISHU_COMMAND_MARKDOWN_ENABLED") ? env.get("CTI_FEISHU_COMMAND_MARKDOWN_ENABLED") === "true" : true
|
|
5227
|
+
}
|
|
5228
|
+
});
|
|
5229
|
+
}
|
|
5230
|
+
const hasWeixinConfig = Boolean(
|
|
5231
|
+
env.get("CTI_WEIXIN_BASE_URL") || env.get("CTI_WEIXIN_CDN_BASE_URL") || env.get("CTI_WEIXIN_MEDIA_ENABLED") || enabledChannels.includes("weixin")
|
|
5232
|
+
);
|
|
5233
|
+
if (hasWeixinConfig) {
|
|
5234
|
+
channels.push({
|
|
5235
|
+
id: buildDefaultChannelId("weixin"),
|
|
5236
|
+
alias: defaultAliasForProvider("weixin"),
|
|
5237
|
+
provider: "weixin",
|
|
5238
|
+
enabled: enabledChannels.includes("weixin"),
|
|
5239
|
+
createdAt: timestamp,
|
|
5240
|
+
updatedAt: timestamp,
|
|
5241
|
+
config: {
|
|
5242
|
+
baseUrl: env.get("CTI_WEIXIN_BASE_URL") || void 0,
|
|
5243
|
+
cdnBaseUrl: env.get("CTI_WEIXIN_CDN_BASE_URL") || void 0,
|
|
5244
|
+
mediaEnabled: env.has("CTI_WEIXIN_MEDIA_ENABLED") ? env.get("CTI_WEIXIN_MEDIA_ENABLED") === "true" : void 0,
|
|
5245
|
+
feedbackMarkdownEnabled: env.has("CTI_WEIXIN_COMMAND_MARKDOWN_ENABLED") ? env.get("CTI_WEIXIN_COMMAND_MARKDOWN_ENABLED") === "true" : false
|
|
5246
|
+
}
|
|
5247
|
+
});
|
|
5248
|
+
}
|
|
5249
|
+
return {
|
|
5250
|
+
schemaVersion: 2,
|
|
5251
|
+
runtime: {
|
|
5252
|
+
provider: runtime,
|
|
5253
|
+
defaultWorkspaceRoot: expandHomePath(env.get("CTI_DEFAULT_WORKSPACE_ROOT")) || void 0,
|
|
5254
|
+
defaultModel: env.get("CTI_DEFAULT_MODEL") || void 0,
|
|
5255
|
+
defaultMode: env.get("CTI_DEFAULT_MODE") || "code",
|
|
5256
|
+
historyMessageLimit: parsePositiveInt(env.get("CTI_HISTORY_MESSAGE_LIMIT")) ?? 8,
|
|
5257
|
+
codexSkipGitRepoCheck: env.has("CTI_CODEX_SKIP_GIT_REPO_CHECK") ? env.get("CTI_CODEX_SKIP_GIT_REPO_CHECK") === "true" : true,
|
|
5258
|
+
codexSandboxMode: parseSandboxMode(env.get("CTI_CODEX_SANDBOX_MODE")) ?? "workspace-write",
|
|
5259
|
+
codexReasoningEffort: parseReasoningEffort(env.get("CTI_CODEX_REASONING_EFFORT")) ?? "medium",
|
|
5260
|
+
uiAllowLan: env.get("CTI_UI_ALLOW_LAN") === "true",
|
|
5261
|
+
uiAccessToken: env.get("CTI_UI_ACCESS_TOKEN") || void 0,
|
|
5262
|
+
autoApprove: env.get("CTI_AUTO_APPROVE") === "true"
|
|
5263
|
+
},
|
|
5264
|
+
channels
|
|
5265
|
+
};
|
|
5266
|
+
}
|
|
5267
|
+
function getChannelByProvider(config2, provider) {
|
|
5268
|
+
const preferredId = buildDefaultChannelId(provider);
|
|
5269
|
+
return config2.channels.find((channel) => channel.id === preferredId) || config2.channels.find((channel) => channel.provider === provider);
|
|
5270
|
+
}
|
|
5271
|
+
function expandConfig(v2) {
|
|
5272
|
+
return {
|
|
5273
|
+
schemaVersion: 2,
|
|
5274
|
+
channels: v2.channels,
|
|
5275
|
+
runtime: v2.runtime.provider,
|
|
5276
|
+
enabledChannels: Array.from(new Set(
|
|
5277
|
+
v2.channels.filter((channel) => channel.enabled).map((channel) => channel.provider)
|
|
5278
|
+
)),
|
|
5279
|
+
defaultWorkspaceRoot: v2.runtime.defaultWorkspaceRoot,
|
|
5280
|
+
defaultModel: v2.runtime.defaultModel,
|
|
5281
|
+
defaultMode: v2.runtime.defaultMode || "code",
|
|
5282
|
+
historyMessageLimit: v2.runtime.historyMessageLimit ?? 8,
|
|
5283
|
+
codexSkipGitRepoCheck: v2.runtime.codexSkipGitRepoCheck ?? true,
|
|
5284
|
+
codexSandboxMode: v2.runtime.codexSandboxMode ?? "workspace-write",
|
|
5285
|
+
codexReasoningEffort: v2.runtime.codexReasoningEffort ?? "medium",
|
|
5286
|
+
uiAllowLan: v2.runtime.uiAllowLan === true,
|
|
5287
|
+
uiAccessToken: v2.runtime.uiAccessToken || void 0,
|
|
5288
|
+
autoApprove: v2.runtime.autoApprove === true
|
|
5289
|
+
};
|
|
5290
|
+
}
|
|
5291
|
+
function loadConfig() {
|
|
5292
|
+
const current = readConfigV2File();
|
|
5293
|
+
if (current) return expandConfig(current);
|
|
5294
|
+
const legacyEnv = loadRawConfigEnv();
|
|
5295
|
+
if (legacyEnv.size > 0) {
|
|
5296
|
+
const migrated = migrateLegacyEnvToV2(legacyEnv);
|
|
5297
|
+
writeConfigV2File(migrated);
|
|
5298
|
+
return expandConfig(migrated);
|
|
5299
|
+
}
|
|
5300
|
+
const empty = {
|
|
5301
|
+
schemaVersion: 2,
|
|
5302
|
+
runtime: {
|
|
5303
|
+
provider: "codex",
|
|
5304
|
+
defaultWorkspaceRoot: DEFAULT_WORKSPACE_ROOT,
|
|
5305
|
+
defaultMode: "code",
|
|
5306
|
+
historyMessageLimit: 8,
|
|
5307
|
+
codexSkipGitRepoCheck: true,
|
|
5308
|
+
codexSandboxMode: "workspace-write",
|
|
5309
|
+
codexReasoningEffort: "medium",
|
|
5310
|
+
uiAllowLan: false,
|
|
5311
|
+
autoApprove: false
|
|
5312
|
+
},
|
|
5313
|
+
channels: []
|
|
5314
|
+
};
|
|
5315
|
+
return expandConfig(empty);
|
|
5316
|
+
}
|
|
5317
|
+
function listChannelInstances(config2) {
|
|
5318
|
+
return [...config2?.channels || loadConfig().channels || []];
|
|
5319
|
+
}
|
|
5320
|
+
function findChannelInstance(channelId, config2) {
|
|
5321
|
+
return listChannelInstances(config2).find((channel) => channel.id === channelId);
|
|
5322
|
+
}
|
|
5323
|
+
function configToSettings(config2) {
|
|
5324
|
+
const m = /* @__PURE__ */ new Map();
|
|
5325
|
+
const current = {
|
|
5326
|
+
schemaVersion: 2,
|
|
5327
|
+
runtime: {
|
|
5328
|
+
provider: config2.runtime,
|
|
5329
|
+
defaultMode: config2.defaultMode
|
|
5330
|
+
},
|
|
5331
|
+
channels: config2.channels || []
|
|
5332
|
+
};
|
|
5333
|
+
const feishu = getChannelByProvider(current, "feishu");
|
|
5334
|
+
const weixin = getChannelByProvider(current, "weixin");
|
|
5335
|
+
const feishuConfig = toFeishuConfig(feishu);
|
|
5336
|
+
const weixinConfig = toWeixinConfig(weixin);
|
|
5337
|
+
m.set("remote_bridge_enabled", "true");
|
|
5338
|
+
if (config2.defaultWorkspaceRoot) {
|
|
5339
|
+
m.set("bridge_default_workspace_root", config2.defaultWorkspaceRoot);
|
|
5340
|
+
}
|
|
5341
|
+
if (config2.defaultModel) {
|
|
5342
|
+
m.set("bridge_default_model", config2.defaultModel);
|
|
5343
|
+
m.set("default_model", config2.defaultModel);
|
|
5344
|
+
}
|
|
5345
|
+
m.set("bridge_default_mode", config2.defaultMode);
|
|
5346
|
+
m.set(
|
|
5347
|
+
"bridge_history_message_limit",
|
|
5348
|
+
String(config2.historyMessageLimit && config2.historyMessageLimit > 0 ? config2.historyMessageLimit : 8)
|
|
5349
|
+
);
|
|
5350
|
+
m.set(
|
|
5351
|
+
"bridge_codex_skip_git_repo_check",
|
|
5352
|
+
config2.codexSkipGitRepoCheck === true ? "true" : "false"
|
|
5353
|
+
);
|
|
5354
|
+
m.set(
|
|
5355
|
+
"bridge_codex_sandbox_mode",
|
|
5356
|
+
config2.codexSandboxMode || "workspace-write"
|
|
5357
|
+
);
|
|
5358
|
+
m.set(
|
|
5359
|
+
"bridge_codex_reasoning_effort",
|
|
5360
|
+
config2.codexReasoningEffort || "medium"
|
|
5361
|
+
);
|
|
5362
|
+
m.set(
|
|
5363
|
+
"bridge_channel_instances_json",
|
|
5364
|
+
JSON.stringify(config2.channels || [])
|
|
5365
|
+
);
|
|
5366
|
+
m.set(
|
|
5367
|
+
"bridge_telegram_enabled",
|
|
5368
|
+
config2.enabledChannels.includes("telegram") ? "true" : "false"
|
|
5369
|
+
);
|
|
5370
|
+
if (config2.tgBotToken) m.set("telegram_bot_token", config2.tgBotToken);
|
|
5371
|
+
if (config2.tgAllowedUsers) m.set("telegram_bridge_allowed_users", config2.tgAllowedUsers.join(","));
|
|
5372
|
+
if (config2.tgChatId) m.set("telegram_chat_id", config2.tgChatId);
|
|
5373
|
+
m.set(
|
|
5374
|
+
"bridge_discord_enabled",
|
|
5375
|
+
config2.enabledChannels.includes("discord") ? "true" : "false"
|
|
5376
|
+
);
|
|
5377
|
+
if (config2.discordBotToken) m.set("bridge_discord_bot_token", config2.discordBotToken);
|
|
5378
|
+
if (config2.discordAllowedUsers) m.set("bridge_discord_allowed_users", config2.discordAllowedUsers.join(","));
|
|
5379
|
+
if (config2.discordAllowedChannels) m.set("bridge_discord_allowed_channels", config2.discordAllowedChannels.join(","));
|
|
5380
|
+
if (config2.discordAllowedGuilds) m.set("bridge_discord_allowed_guilds", config2.discordAllowedGuilds.join(","));
|
|
5381
|
+
m.set(
|
|
5382
|
+
"bridge_feishu_enabled",
|
|
5383
|
+
feishu?.enabled === true ? "true" : "false"
|
|
5384
|
+
);
|
|
5385
|
+
if (feishuConfig?.appId) m.set("bridge_feishu_app_id", feishuConfig.appId);
|
|
5386
|
+
if (feishuConfig?.appSecret) m.set("bridge_feishu_app_secret", feishuConfig.appSecret);
|
|
5387
|
+
if (feishuConfig?.site) m.set("bridge_feishu_site", feishuConfig.site);
|
|
5388
|
+
if (feishuConfig?.allowedUsers) m.set("bridge_feishu_allowed_users", feishuConfig.allowedUsers.join(","));
|
|
5389
|
+
m.set(
|
|
5390
|
+
"bridge_feishu_streaming_enabled",
|
|
5391
|
+
feishuConfig?.streamingEnabled === false ? "false" : "true"
|
|
5392
|
+
);
|
|
5393
|
+
m.set(
|
|
5394
|
+
"bridge_feishu_command_markdown_enabled",
|
|
5395
|
+
feishuConfig?.feedbackMarkdownEnabled === false ? "false" : "true"
|
|
5396
|
+
);
|
|
5397
|
+
m.set(
|
|
5398
|
+
"bridge_qq_enabled",
|
|
5399
|
+
config2.enabledChannels.includes("qq") ? "true" : "false"
|
|
5400
|
+
);
|
|
5401
|
+
if (config2.qqAppId) m.set("bridge_qq_app_id", config2.qqAppId);
|
|
5402
|
+
if (config2.qqAppSecret) m.set("bridge_qq_app_secret", config2.qqAppSecret);
|
|
5403
|
+
if (config2.qqAllowedUsers) m.set("bridge_qq_allowed_users", config2.qqAllowedUsers.join(","));
|
|
5404
|
+
if (config2.qqImageEnabled !== void 0) {
|
|
5405
|
+
m.set("bridge_qq_image_enabled", String(config2.qqImageEnabled));
|
|
5406
|
+
}
|
|
5407
|
+
if (config2.qqMaxImageSize !== void 0) {
|
|
5408
|
+
m.set("bridge_qq_max_image_size", String(config2.qqMaxImageSize));
|
|
5409
|
+
}
|
|
5410
|
+
m.set(
|
|
5411
|
+
"bridge_weixin_enabled",
|
|
5412
|
+
weixin?.enabled === true ? "true" : "false"
|
|
5413
|
+
);
|
|
5414
|
+
if (weixinConfig?.mediaEnabled !== void 0) {
|
|
5415
|
+
m.set("bridge_weixin_media_enabled", String(weixinConfig.mediaEnabled));
|
|
5416
|
+
}
|
|
5417
|
+
m.set(
|
|
5418
|
+
"bridge_weixin_command_markdown_enabled",
|
|
5419
|
+
weixinConfig?.feedbackMarkdownEnabled === true ? "true" : "false"
|
|
5420
|
+
);
|
|
5421
|
+
if (weixinConfig?.baseUrl) m.set("bridge_weixin_base_url", weixinConfig.baseUrl);
|
|
5422
|
+
if (weixinConfig?.cdnBaseUrl) m.set("bridge_weixin_cdn_base_url", weixinConfig.cdnBaseUrl);
|
|
5423
|
+
return m;
|
|
5424
|
+
}
|
|
5085
5425
|
|
|
5086
5426
|
// src/lib/bridge/markdown/feishu.ts
|
|
5087
5427
|
function hasComplexMarkdown(text2) {
|
|
@@ -5259,7 +5599,10 @@ var MIME_BY_TYPE = {
|
|
|
5259
5599
|
media: "application/octet-stream"
|
|
5260
5600
|
};
|
|
5261
5601
|
var FeishuAdapter = class extends BaseChannelAdapter {
|
|
5262
|
-
channelType
|
|
5602
|
+
channelType;
|
|
5603
|
+
provider = "feishu";
|
|
5604
|
+
alias;
|
|
5605
|
+
channelConfig;
|
|
5263
5606
|
running = false;
|
|
5264
5607
|
queue = [];
|
|
5265
5608
|
waiters = [];
|
|
@@ -5279,8 +5622,23 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5279
5622
|
cardCreatePromises = /* @__PURE__ */ new Map();
|
|
5280
5623
|
/** Cached tenant token for upload APIs. */
|
|
5281
5624
|
tenantTokenCache = null;
|
|
5625
|
+
constructor(instance) {
|
|
5626
|
+
super();
|
|
5627
|
+
this.channelType = instance?.id || "feishu";
|
|
5628
|
+
this.alias = instance?.alias;
|
|
5629
|
+
this.channelConfig = instance?.config || {};
|
|
5630
|
+
}
|
|
5631
|
+
get appId() {
|
|
5632
|
+
return this.channelConfig.appId?.trim() || "";
|
|
5633
|
+
}
|
|
5634
|
+
get appSecret() {
|
|
5635
|
+
return this.channelConfig.appSecret?.trim() || "";
|
|
5636
|
+
}
|
|
5637
|
+
get site() {
|
|
5638
|
+
return normalizeFeishuSite(this.channelConfig.site);
|
|
5639
|
+
}
|
|
5282
5640
|
isStreamingEnabled() {
|
|
5283
|
-
return
|
|
5641
|
+
return this.channelConfig.streamingEnabled !== false;
|
|
5284
5642
|
}
|
|
5285
5643
|
resolveStreamKey(chatId, streamKey) {
|
|
5286
5644
|
return streamKey?.trim() || chatId;
|
|
@@ -5293,10 +5651,10 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5293
5651
|
console.warn("[feishu-adapter] Cannot start:", configError);
|
|
5294
5652
|
return;
|
|
5295
5653
|
}
|
|
5296
|
-
const appId =
|
|
5297
|
-
const appSecret =
|
|
5298
|
-
const
|
|
5299
|
-
const domain =
|
|
5654
|
+
const appId = this.appId;
|
|
5655
|
+
const appSecret = this.appSecret;
|
|
5656
|
+
const site = this.site;
|
|
5657
|
+
const domain = site === "lark" ? lark.Domain.Lark : lark.Domain.Feishu;
|
|
5300
5658
|
this.restClient = new lark.Client({
|
|
5301
5659
|
appId,
|
|
5302
5660
|
appSecret,
|
|
@@ -5446,7 +5804,9 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5446
5804
|
const callbackMsg = {
|
|
5447
5805
|
messageId: messageId || `card_action_${Date.now()}`,
|
|
5448
5806
|
address: {
|
|
5449
|
-
channelType:
|
|
5807
|
+
channelType: this.channelType,
|
|
5808
|
+
channelProvider: this.provider,
|
|
5809
|
+
channelAlias: this.alias,
|
|
5450
5810
|
chatId,
|
|
5451
5811
|
userId
|
|
5452
5812
|
},
|
|
@@ -5755,18 +6115,11 @@ ${trimmedResponse}`;
|
|
|
5755
6115
|
return this.sendAsPost(message.address.chatId, text2);
|
|
5756
6116
|
}
|
|
5757
6117
|
getOpenApiBaseUrl() {
|
|
5758
|
-
|
|
5759
|
-
if (configured.startsWith("http://") || configured.startsWith("https://")) {
|
|
5760
|
-
return configured;
|
|
5761
|
-
}
|
|
5762
|
-
if (configured.toLowerCase() === "lark") {
|
|
5763
|
-
return "https://open.larksuite.com";
|
|
5764
|
-
}
|
|
5765
|
-
return "https://open.feishu.cn";
|
|
6118
|
+
return feishuSiteToApiBaseUrl(this.site);
|
|
5766
6119
|
}
|
|
5767
6120
|
async getTenantAccessToken() {
|
|
5768
|
-
const appId =
|
|
5769
|
-
const appSecret =
|
|
6121
|
+
const appId = this.appId;
|
|
6122
|
+
const appSecret = this.appSecret;
|
|
5770
6123
|
const domain = this.getOpenApiBaseUrl();
|
|
5771
6124
|
if (!appId || !appSecret) {
|
|
5772
6125
|
throw new Error("Feishu App ID / App Secret not configured");
|
|
@@ -5806,7 +6159,7 @@ ${trimmedResponse}`;
|
|
|
5806
6159
|
return { ok: true, messageId: lastMessageId };
|
|
5807
6160
|
}
|
|
5808
6161
|
async sendAttachment(chatId, attachment, replyToMessageId) {
|
|
5809
|
-
if (!
|
|
6162
|
+
if (!fs2.existsSync(attachment.path)) {
|
|
5810
6163
|
return { ok: false, error: `Attachment not found: ${attachment.path}` };
|
|
5811
6164
|
}
|
|
5812
6165
|
try {
|
|
@@ -5832,10 +6185,10 @@ ${trimmedResponse}`;
|
|
|
5832
6185
|
}
|
|
5833
6186
|
async uploadImage(attachment) {
|
|
5834
6187
|
const token = await this.getTenantAccessToken();
|
|
5835
|
-
const fileName = attachment.name ||
|
|
6188
|
+
const fileName = attachment.name || path2.basename(attachment.path) || "image.png";
|
|
5836
6189
|
const form = new FormData();
|
|
5837
6190
|
form.set("image_type", "message");
|
|
5838
|
-
form.set("image", new Blob([
|
|
6191
|
+
form.set("image", new Blob([fs2.readFileSync(attachment.path)]), fileName);
|
|
5839
6192
|
const response = await fetch(`${this.getOpenApiBaseUrl()}/open-apis/im/v1/images`, {
|
|
5840
6193
|
method: "POST",
|
|
5841
6194
|
headers: { Authorization: `Bearer ${token}` },
|
|
@@ -5849,11 +6202,11 @@ ${trimmedResponse}`;
|
|
|
5849
6202
|
}
|
|
5850
6203
|
async uploadFile(attachment) {
|
|
5851
6204
|
const token = await this.getTenantAccessToken();
|
|
5852
|
-
const fileName = attachment.name ||
|
|
6205
|
+
const fileName = attachment.name || path2.basename(attachment.path) || "attachment.bin";
|
|
5853
6206
|
const form = new FormData();
|
|
5854
6207
|
form.set("file_type", "stream");
|
|
5855
6208
|
form.set("file_name", fileName);
|
|
5856
|
-
form.set("file", new Blob([
|
|
6209
|
+
form.set("file", new Blob([fs2.readFileSync(attachment.path)]), fileName);
|
|
5857
6210
|
const response = await fetch(`${this.getOpenApiBaseUrl()}/open-apis/im/v1/files`, {
|
|
5858
6211
|
method: "POST",
|
|
5859
6212
|
headers: { Authorization: `Bearer ${token}` },
|
|
@@ -6061,16 +6414,14 @@ ${trimmedResponse}`;
|
|
|
6061
6414
|
}
|
|
6062
6415
|
// ── Config & Auth ───────────────────────────────────────────
|
|
6063
6416
|
validateConfig() {
|
|
6064
|
-
const
|
|
6065
|
-
if (
|
|
6066
|
-
const
|
|
6067
|
-
if (!
|
|
6068
|
-
const appSecret = getBridgeContext().store.getSetting("bridge_feishu_app_secret");
|
|
6069
|
-
if (!appSecret) return "bridge_feishu_app_secret not configured";
|
|
6417
|
+
const appId = this.appId;
|
|
6418
|
+
if (!appId) return "Feishu App ID \u672A\u914D\u7F6E";
|
|
6419
|
+
const appSecret = this.appSecret;
|
|
6420
|
+
if (!appSecret) return "Feishu App Secret \u672A\u914D\u7F6E";
|
|
6070
6421
|
return null;
|
|
6071
6422
|
}
|
|
6072
6423
|
isAuthorized(userId, chatId) {
|
|
6073
|
-
const allowedUsers =
|
|
6424
|
+
const allowedUsers = (this.channelConfig.allowedUsers || []).join(",");
|
|
6074
6425
|
if (!allowedUsers) {
|
|
6075
6426
|
return true;
|
|
6076
6427
|
}
|
|
@@ -6120,7 +6471,9 @@ ${trimmedResponse}`;
|
|
|
6120
6471
|
console.log("[feishu-adapter] Group message ignored (bot not @mentioned), chatId:", chatId, "msgId:", msg.message_id);
|
|
6121
6472
|
try {
|
|
6122
6473
|
getBridgeContext().store.insertAuditLog({
|
|
6123
|
-
channelType:
|
|
6474
|
+
channelType: this.channelType,
|
|
6475
|
+
channelProvider: this.provider,
|
|
6476
|
+
channelAlias: this.alias,
|
|
6124
6477
|
chatId,
|
|
6125
6478
|
direction: "inbound",
|
|
6126
6479
|
messageId: msg.message_id,
|
|
@@ -6149,7 +6502,9 @@ ${trimmedResponse}`;
|
|
|
6149
6502
|
text2 = "[image download failed]";
|
|
6150
6503
|
try {
|
|
6151
6504
|
getBridgeContext().store.insertAuditLog({
|
|
6152
|
-
channelType:
|
|
6505
|
+
channelType: this.channelType,
|
|
6506
|
+
channelProvider: this.provider,
|
|
6507
|
+
channelAlias: this.alias,
|
|
6153
6508
|
chatId,
|
|
6154
6509
|
direction: "inbound",
|
|
6155
6510
|
messageId: msg.message_id,
|
|
@@ -6170,7 +6525,9 @@ ${trimmedResponse}`;
|
|
|
6170
6525
|
text2 = `[${messageType} download failed]`;
|
|
6171
6526
|
try {
|
|
6172
6527
|
getBridgeContext().store.insertAuditLog({
|
|
6173
|
-
channelType:
|
|
6528
|
+
channelType: this.channelType,
|
|
6529
|
+
channelProvider: this.provider,
|
|
6530
|
+
channelAlias: this.alias,
|
|
6174
6531
|
chatId,
|
|
6175
6532
|
direction: "inbound",
|
|
6176
6533
|
messageId: msg.message_id,
|
|
@@ -6197,7 +6554,9 @@ ${trimmedResponse}`;
|
|
|
6197
6554
|
if (!text2.trim() && attachments.length === 0) return;
|
|
6198
6555
|
const timestamp = parseInt(msg.create_time, 10) || Date.now();
|
|
6199
6556
|
const address = {
|
|
6200
|
-
channelType:
|
|
6557
|
+
channelType: this.channelType,
|
|
6558
|
+
channelProvider: this.provider,
|
|
6559
|
+
channelAlias: this.alias,
|
|
6201
6560
|
chatId,
|
|
6202
6561
|
userId
|
|
6203
6562
|
};
|
|
@@ -6229,7 +6588,9 @@ ${trimmedResponse}`;
|
|
|
6229
6588
|
try {
|
|
6230
6589
|
const summary = attachments.length > 0 ? `[${attachments.length} attachment(s)] ${text2.slice(0, 150)}` : text2.slice(0, 200);
|
|
6231
6590
|
getBridgeContext().store.insertAuditLog({
|
|
6232
|
-
channelType:
|
|
6591
|
+
channelType: this.channelType,
|
|
6592
|
+
channelProvider: this.provider,
|
|
6593
|
+
channelAlias: this.alias,
|
|
6233
6594
|
chatId,
|
|
6234
6595
|
direction: "inbound",
|
|
6235
6596
|
messageId: msg.message_id,
|
|
@@ -6444,7 +6805,7 @@ ${trimmedResponse}`;
|
|
|
6444
6805
|
}
|
|
6445
6806
|
}
|
|
6446
6807
|
};
|
|
6447
|
-
registerAdapterFactory("feishu", () => new FeishuAdapter());
|
|
6808
|
+
registerAdapterFactory("feishu", (instance) => new FeishuAdapter(instance));
|
|
6448
6809
|
|
|
6449
6810
|
// src/lib/bridge/adapters/discord-adapter.ts
|
|
6450
6811
|
import crypto3 from "crypto";
|
|
@@ -6462,6 +6823,7 @@ async function loadDiscordJs() {
|
|
|
6462
6823
|
}
|
|
6463
6824
|
var DiscordAdapter = class extends BaseChannelAdapter {
|
|
6464
6825
|
channelType = "discord";
|
|
6826
|
+
provider = "discord";
|
|
6465
6827
|
running = false;
|
|
6466
6828
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6467
6829
|
client = null;
|
|
@@ -7048,6 +7410,7 @@ async function sendPrivateMessage(accessToken, params) {
|
|
|
7048
7410
|
// src/lib/bridge/adapters/qq-adapter.ts
|
|
7049
7411
|
var QQAdapter = class extends BaseChannelAdapter {
|
|
7050
7412
|
channelType = "qq";
|
|
7413
|
+
provider = "qq";
|
|
7051
7414
|
_running = false;
|
|
7052
7415
|
queue = [];
|
|
7053
7416
|
waiters = [];
|
|
@@ -7438,218 +7801,6 @@ registerAdapterFactory("qq", () => new QQAdapter());
|
|
|
7438
7801
|
// src/weixin-store.ts
|
|
7439
7802
|
import fs3 from "node:fs";
|
|
7440
7803
|
import path3 from "node:path";
|
|
7441
|
-
|
|
7442
|
-
// src/config.ts
|
|
7443
|
-
import fs2 from "node:fs";
|
|
7444
|
-
import os from "node:os";
|
|
7445
|
-
import path2 from "node:path";
|
|
7446
|
-
var LEGACY_CTI_HOME = path2.join(os.homedir(), ".claude-to-im");
|
|
7447
|
-
var DEFAULT_CTI_HOME = path2.join(os.homedir(), ".codex-to-im");
|
|
7448
|
-
var DEFAULT_WORKSPACE_ROOT = path2.join(os.homedir(), "cx2im");
|
|
7449
|
-
function resolveDefaultCtiHome() {
|
|
7450
|
-
if (fs2.existsSync(DEFAULT_CTI_HOME)) return DEFAULT_CTI_HOME;
|
|
7451
|
-
if (fs2.existsSync(LEGACY_CTI_HOME)) return LEGACY_CTI_HOME;
|
|
7452
|
-
return DEFAULT_CTI_HOME;
|
|
7453
|
-
}
|
|
7454
|
-
var CTI_HOME = process.env.CTI_HOME || resolveDefaultCtiHome();
|
|
7455
|
-
var CONFIG_PATH = path2.join(CTI_HOME, "config.env");
|
|
7456
|
-
function expandHomePath(value) {
|
|
7457
|
-
if (!value) return value;
|
|
7458
|
-
if (value === "~") return os.homedir();
|
|
7459
|
-
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
7460
|
-
return path2.join(os.homedir(), value.slice(2));
|
|
7461
|
-
}
|
|
7462
|
-
return value;
|
|
7463
|
-
}
|
|
7464
|
-
function parseEnvFile(content) {
|
|
7465
|
-
const entries = /* @__PURE__ */ new Map();
|
|
7466
|
-
for (const line of content.split("\n")) {
|
|
7467
|
-
const trimmed = line.trim();
|
|
7468
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
7469
|
-
const eqIdx = trimmed.indexOf("=");
|
|
7470
|
-
if (eqIdx === -1) continue;
|
|
7471
|
-
const key = trimmed.slice(0, eqIdx).trim();
|
|
7472
|
-
let value = trimmed.slice(eqIdx + 1).trim();
|
|
7473
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
7474
|
-
value = value.slice(1, -1);
|
|
7475
|
-
}
|
|
7476
|
-
entries.set(key, value);
|
|
7477
|
-
}
|
|
7478
|
-
return entries;
|
|
7479
|
-
}
|
|
7480
|
-
function loadRawConfigEnv() {
|
|
7481
|
-
try {
|
|
7482
|
-
return parseEnvFile(fs2.readFileSync(CONFIG_PATH, "utf-8"));
|
|
7483
|
-
} catch {
|
|
7484
|
-
return /* @__PURE__ */ new Map();
|
|
7485
|
-
}
|
|
7486
|
-
}
|
|
7487
|
-
function splitCsv(value) {
|
|
7488
|
-
if (!value) return void 0;
|
|
7489
|
-
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
7490
|
-
}
|
|
7491
|
-
function parsePositiveInt(value) {
|
|
7492
|
-
if (!value) return void 0;
|
|
7493
|
-
const parsed = Number(value);
|
|
7494
|
-
if (!Number.isFinite(parsed) || parsed <= 0) return void 0;
|
|
7495
|
-
return Math.floor(parsed);
|
|
7496
|
-
}
|
|
7497
|
-
function parseSandboxMode(value) {
|
|
7498
|
-
if (value === "read-only" || value === "workspace-write" || value === "danger-full-access") {
|
|
7499
|
-
return value;
|
|
7500
|
-
}
|
|
7501
|
-
return void 0;
|
|
7502
|
-
}
|
|
7503
|
-
function parseReasoningEffort(value) {
|
|
7504
|
-
if (value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh") {
|
|
7505
|
-
return value;
|
|
7506
|
-
}
|
|
7507
|
-
return void 0;
|
|
7508
|
-
}
|
|
7509
|
-
function loadConfig() {
|
|
7510
|
-
const env = loadRawConfigEnv();
|
|
7511
|
-
const rawRuntime = env.get("CTI_RUNTIME") || "codex";
|
|
7512
|
-
const runtime = ["claude", "codex", "auto"].includes(rawRuntime) ? rawRuntime : "codex";
|
|
7513
|
-
return {
|
|
7514
|
-
runtime,
|
|
7515
|
-
enabledChannels: splitCsv(env.get("CTI_ENABLED_CHANNELS")) ?? ["feishu"],
|
|
7516
|
-
defaultWorkspaceRoot: expandHomePath(env.get("CTI_DEFAULT_WORKSPACE_ROOT")) || void 0,
|
|
7517
|
-
defaultModel: env.get("CTI_DEFAULT_MODEL") || void 0,
|
|
7518
|
-
defaultMode: env.get("CTI_DEFAULT_MODE") || "code",
|
|
7519
|
-
historyMessageLimit: parsePositiveInt(env.get("CTI_HISTORY_MESSAGE_LIMIT")) ?? 8,
|
|
7520
|
-
codexSkipGitRepoCheck: env.has("CTI_CODEX_SKIP_GIT_REPO_CHECK") ? env.get("CTI_CODEX_SKIP_GIT_REPO_CHECK") === "true" : true,
|
|
7521
|
-
codexSandboxMode: parseSandboxMode(env.get("CTI_CODEX_SANDBOX_MODE")) ?? "workspace-write",
|
|
7522
|
-
codexReasoningEffort: parseReasoningEffort(env.get("CTI_CODEX_REASONING_EFFORT")) ?? "medium",
|
|
7523
|
-
uiAllowLan: env.get("CTI_UI_ALLOW_LAN") === "true",
|
|
7524
|
-
uiAccessToken: env.get("CTI_UI_ACCESS_TOKEN") || void 0,
|
|
7525
|
-
tgBotToken: env.get("CTI_TG_BOT_TOKEN") || void 0,
|
|
7526
|
-
tgChatId: env.get("CTI_TG_CHAT_ID") || void 0,
|
|
7527
|
-
tgAllowedUsers: splitCsv(env.get("CTI_TG_ALLOWED_USERS")),
|
|
7528
|
-
feishuAppId: env.get("CTI_FEISHU_APP_ID") || void 0,
|
|
7529
|
-
feishuAppSecret: env.get("CTI_FEISHU_APP_SECRET") || void 0,
|
|
7530
|
-
feishuDomain: env.get("CTI_FEISHU_DOMAIN") || void 0,
|
|
7531
|
-
feishuAllowedUsers: splitCsv(env.get("CTI_FEISHU_ALLOWED_USERS")),
|
|
7532
|
-
feishuStreamingEnabled: env.has("CTI_FEISHU_STREAMING_ENABLED") ? env.get("CTI_FEISHU_STREAMING_ENABLED") === "true" : true,
|
|
7533
|
-
feishuCommandMarkdownEnabled: env.has("CTI_FEISHU_COMMAND_MARKDOWN_ENABLED") ? env.get("CTI_FEISHU_COMMAND_MARKDOWN_ENABLED") === "true" : true,
|
|
7534
|
-
discordBotToken: env.get("CTI_DISCORD_BOT_TOKEN") || void 0,
|
|
7535
|
-
discordAllowedUsers: splitCsv(env.get("CTI_DISCORD_ALLOWED_USERS")),
|
|
7536
|
-
discordAllowedChannels: splitCsv(
|
|
7537
|
-
env.get("CTI_DISCORD_ALLOWED_CHANNELS")
|
|
7538
|
-
),
|
|
7539
|
-
discordAllowedGuilds: splitCsv(env.get("CTI_DISCORD_ALLOWED_GUILDS")),
|
|
7540
|
-
qqAppId: env.get("CTI_QQ_APP_ID") || void 0,
|
|
7541
|
-
qqAppSecret: env.get("CTI_QQ_APP_SECRET") || void 0,
|
|
7542
|
-
qqAllowedUsers: splitCsv(env.get("CTI_QQ_ALLOWED_USERS")),
|
|
7543
|
-
qqImageEnabled: env.has("CTI_QQ_IMAGE_ENABLED") ? env.get("CTI_QQ_IMAGE_ENABLED") === "true" : void 0,
|
|
7544
|
-
qqMaxImageSize: env.get("CTI_QQ_MAX_IMAGE_SIZE") ? Number(env.get("CTI_QQ_MAX_IMAGE_SIZE")) : void 0,
|
|
7545
|
-
weixinBaseUrl: env.get("CTI_WEIXIN_BASE_URL") || void 0,
|
|
7546
|
-
weixinCdnBaseUrl: env.get("CTI_WEIXIN_CDN_BASE_URL") || void 0,
|
|
7547
|
-
weixinMediaEnabled: env.has("CTI_WEIXIN_MEDIA_ENABLED") ? env.get("CTI_WEIXIN_MEDIA_ENABLED") === "true" : void 0,
|
|
7548
|
-
weixinCommandMarkdownEnabled: env.has("CTI_WEIXIN_COMMAND_MARKDOWN_ENABLED") ? env.get("CTI_WEIXIN_COMMAND_MARKDOWN_ENABLED") === "true" : false,
|
|
7549
|
-
autoApprove: env.get("CTI_AUTO_APPROVE") === "true"
|
|
7550
|
-
};
|
|
7551
|
-
}
|
|
7552
|
-
function configToSettings(config2) {
|
|
7553
|
-
const m = /* @__PURE__ */ new Map();
|
|
7554
|
-
m.set("remote_bridge_enabled", "true");
|
|
7555
|
-
m.set(
|
|
7556
|
-
"bridge_telegram_enabled",
|
|
7557
|
-
config2.enabledChannels.includes("telegram") ? "true" : "false"
|
|
7558
|
-
);
|
|
7559
|
-
if (config2.tgBotToken) m.set("telegram_bot_token", config2.tgBotToken);
|
|
7560
|
-
if (config2.tgAllowedUsers)
|
|
7561
|
-
m.set("telegram_bridge_allowed_users", config2.tgAllowedUsers.join(","));
|
|
7562
|
-
if (config2.tgChatId) m.set("telegram_chat_id", config2.tgChatId);
|
|
7563
|
-
m.set(
|
|
7564
|
-
"bridge_discord_enabled",
|
|
7565
|
-
config2.enabledChannels.includes("discord") ? "true" : "false"
|
|
7566
|
-
);
|
|
7567
|
-
if (config2.discordBotToken)
|
|
7568
|
-
m.set("bridge_discord_bot_token", config2.discordBotToken);
|
|
7569
|
-
if (config2.discordAllowedUsers)
|
|
7570
|
-
m.set("bridge_discord_allowed_users", config2.discordAllowedUsers.join(","));
|
|
7571
|
-
if (config2.discordAllowedChannels)
|
|
7572
|
-
m.set(
|
|
7573
|
-
"bridge_discord_allowed_channels",
|
|
7574
|
-
config2.discordAllowedChannels.join(",")
|
|
7575
|
-
);
|
|
7576
|
-
if (config2.discordAllowedGuilds)
|
|
7577
|
-
m.set(
|
|
7578
|
-
"bridge_discord_allowed_guilds",
|
|
7579
|
-
config2.discordAllowedGuilds.join(",")
|
|
7580
|
-
);
|
|
7581
|
-
m.set(
|
|
7582
|
-
"bridge_feishu_enabled",
|
|
7583
|
-
config2.enabledChannels.includes("feishu") ? "true" : "false"
|
|
7584
|
-
);
|
|
7585
|
-
if (config2.feishuAppId) m.set("bridge_feishu_app_id", config2.feishuAppId);
|
|
7586
|
-
if (config2.feishuAppSecret)
|
|
7587
|
-
m.set("bridge_feishu_app_secret", config2.feishuAppSecret);
|
|
7588
|
-
if (config2.feishuDomain) m.set("bridge_feishu_domain", config2.feishuDomain);
|
|
7589
|
-
if (config2.feishuAllowedUsers)
|
|
7590
|
-
m.set("bridge_feishu_allowed_users", config2.feishuAllowedUsers.join(","));
|
|
7591
|
-
m.set(
|
|
7592
|
-
"bridge_feishu_streaming_enabled",
|
|
7593
|
-
config2.feishuStreamingEnabled === false ? "false" : "true"
|
|
7594
|
-
);
|
|
7595
|
-
m.set(
|
|
7596
|
-
"bridge_feishu_command_markdown_enabled",
|
|
7597
|
-
config2.feishuCommandMarkdownEnabled === false ? "false" : "true"
|
|
7598
|
-
);
|
|
7599
|
-
m.set(
|
|
7600
|
-
"bridge_qq_enabled",
|
|
7601
|
-
config2.enabledChannels.includes("qq") ? "true" : "false"
|
|
7602
|
-
);
|
|
7603
|
-
if (config2.qqAppId) m.set("bridge_qq_app_id", config2.qqAppId);
|
|
7604
|
-
if (config2.qqAppSecret) m.set("bridge_qq_app_secret", config2.qqAppSecret);
|
|
7605
|
-
if (config2.qqAllowedUsers)
|
|
7606
|
-
m.set("bridge_qq_allowed_users", config2.qqAllowedUsers.join(","));
|
|
7607
|
-
if (config2.qqImageEnabled !== void 0)
|
|
7608
|
-
m.set("bridge_qq_image_enabled", String(config2.qqImageEnabled));
|
|
7609
|
-
if (config2.qqMaxImageSize !== void 0)
|
|
7610
|
-
m.set("bridge_qq_max_image_size", String(config2.qqMaxImageSize));
|
|
7611
|
-
m.set(
|
|
7612
|
-
"bridge_weixin_enabled",
|
|
7613
|
-
config2.enabledChannels.includes("weixin") ? "true" : "false"
|
|
7614
|
-
);
|
|
7615
|
-
if (config2.weixinMediaEnabled !== void 0)
|
|
7616
|
-
m.set("bridge_weixin_media_enabled", String(config2.weixinMediaEnabled));
|
|
7617
|
-
m.set(
|
|
7618
|
-
"bridge_weixin_command_markdown_enabled",
|
|
7619
|
-
config2.weixinCommandMarkdownEnabled === true ? "true" : "false"
|
|
7620
|
-
);
|
|
7621
|
-
if (config2.weixinBaseUrl)
|
|
7622
|
-
m.set("bridge_weixin_base_url", config2.weixinBaseUrl);
|
|
7623
|
-
if (config2.weixinCdnBaseUrl)
|
|
7624
|
-
m.set("bridge_weixin_cdn_base_url", config2.weixinCdnBaseUrl);
|
|
7625
|
-
if (config2.defaultWorkspaceRoot) {
|
|
7626
|
-
m.set("bridge_default_workspace_root", config2.defaultWorkspaceRoot);
|
|
7627
|
-
}
|
|
7628
|
-
if (config2.defaultModel) {
|
|
7629
|
-
m.set("bridge_default_model", config2.defaultModel);
|
|
7630
|
-
m.set("default_model", config2.defaultModel);
|
|
7631
|
-
}
|
|
7632
|
-
m.set("bridge_default_mode", config2.defaultMode);
|
|
7633
|
-
m.set(
|
|
7634
|
-
"bridge_history_message_limit",
|
|
7635
|
-
String(config2.historyMessageLimit && config2.historyMessageLimit > 0 ? config2.historyMessageLimit : 8)
|
|
7636
|
-
);
|
|
7637
|
-
m.set(
|
|
7638
|
-
"bridge_codex_skip_git_repo_check",
|
|
7639
|
-
config2.codexSkipGitRepoCheck === true ? "true" : "false"
|
|
7640
|
-
);
|
|
7641
|
-
m.set(
|
|
7642
|
-
"bridge_codex_sandbox_mode",
|
|
7643
|
-
config2.codexSandboxMode || "workspace-write"
|
|
7644
|
-
);
|
|
7645
|
-
m.set(
|
|
7646
|
-
"bridge_codex_reasoning_effort",
|
|
7647
|
-
config2.codexReasoningEffort || "medium"
|
|
7648
|
-
);
|
|
7649
|
-
return m;
|
|
7650
|
-
}
|
|
7651
|
-
|
|
7652
|
-
// src/weixin-store.ts
|
|
7653
7804
|
var DATA_DIR = path3.join(CTI_HOME, "data");
|
|
7654
7805
|
var ACCOUNTS_PATH = path3.join(DATA_DIR, "weixin-accounts.json");
|
|
7655
7806
|
var CONTEXT_TOKENS_PATH = path3.join(DATA_DIR, "weixin-context-tokens.json");
|
|
@@ -13984,7 +14135,10 @@ var DEDUP_MAX3 = 500;
|
|
|
13984
14135
|
var BACKOFF_BASE_MS = 2e3;
|
|
13985
14136
|
var BACKOFF_MAX_MS = 3e4;
|
|
13986
14137
|
var WeixinAdapter = class extends BaseChannelAdapter {
|
|
13987
|
-
channelType
|
|
14138
|
+
channelType;
|
|
14139
|
+
provider = "weixin";
|
|
14140
|
+
alias;
|
|
14141
|
+
channelConfig;
|
|
13988
14142
|
_running = false;
|
|
13989
14143
|
idleLogged = false;
|
|
13990
14144
|
reconcileTimer = null;
|
|
@@ -13997,6 +14151,19 @@ var WeixinAdapter = class extends BaseChannelAdapter {
|
|
|
13997
14151
|
typingTickets = /* @__PURE__ */ new Map();
|
|
13998
14152
|
pendingCursors = /* @__PURE__ */ new Map();
|
|
13999
14153
|
nextBatchId = 1;
|
|
14154
|
+
constructor(instance) {
|
|
14155
|
+
super();
|
|
14156
|
+
this.channelType = instance?.id || "weixin";
|
|
14157
|
+
this.alias = instance?.alias;
|
|
14158
|
+
this.channelConfig = instance?.config || {};
|
|
14159
|
+
}
|
|
14160
|
+
get mediaEnabled() {
|
|
14161
|
+
return this.channelConfig.mediaEnabled === true;
|
|
14162
|
+
}
|
|
14163
|
+
get configuredAccountId() {
|
|
14164
|
+
const value = this.channelConfig.accountId?.trim();
|
|
14165
|
+
return value || void 0;
|
|
14166
|
+
}
|
|
14000
14167
|
async start() {
|
|
14001
14168
|
if (this._running) return;
|
|
14002
14169
|
this._running = true;
|
|
@@ -14072,7 +14239,9 @@ var WeixinAdapter = class extends BaseChannelAdapter {
|
|
|
14072
14239
|
}
|
|
14073
14240
|
}
|
|
14074
14241
|
validateConfig() {
|
|
14075
|
-
const linkedAccounts =
|
|
14242
|
+
const linkedAccounts = this.filterConfiguredAccounts(
|
|
14243
|
+
listWeixinAccounts().filter((account) => account.enabled && account.token)
|
|
14244
|
+
);
|
|
14076
14245
|
if (linkedAccounts.length === 0) {
|
|
14077
14246
|
return "No linked WeChat account. Run the WeChat QR login helper first.";
|
|
14078
14247
|
}
|
|
@@ -14104,7 +14273,9 @@ var WeixinAdapter = class extends BaseChannelAdapter {
|
|
|
14104
14273
|
this.queue.push(message);
|
|
14105
14274
|
}
|
|
14106
14275
|
async reconcileAccounts() {
|
|
14107
|
-
const linkedAccounts =
|
|
14276
|
+
const linkedAccounts = this.filterConfiguredAccounts(
|
|
14277
|
+
listWeixinAccounts().filter((account) => account.enabled && account.token)
|
|
14278
|
+
);
|
|
14108
14279
|
if (linkedAccounts.length === 0 && this.pollAborts.size === 0) {
|
|
14109
14280
|
if (!this.idleLogged) {
|
|
14110
14281
|
console.log("[weixin-adapter] No linked WeChat account is enabled, adapter started but idle");
|
|
@@ -14242,7 +14413,7 @@ var WeixinAdapter = class extends BaseChannelAdapter {
|
|
|
14242
14413
|
const attachments = [];
|
|
14243
14414
|
let failedCount = 0;
|
|
14244
14415
|
let missingVoiceTranscriptCount = 0;
|
|
14245
|
-
const mediaEnabled =
|
|
14416
|
+
const mediaEnabled = this.mediaEnabled;
|
|
14246
14417
|
const account = mediaEnabled ? getWeixinAccount(accountId) : void 0;
|
|
14247
14418
|
const creds = account ? this.accountToCreds(account) : void 0;
|
|
14248
14419
|
for (const item of message.item_list || []) {
|
|
@@ -14294,7 +14465,9 @@ ${failureNote}` : attachments.length > 0 ? failureNote : text2;
|
|
|
14294
14465
|
const inbound = {
|
|
14295
14466
|
messageId: message.message_id || `weixin_${accountId}_${message.seq || Date.now()}`,
|
|
14296
14467
|
address: {
|
|
14297
|
-
channelType:
|
|
14468
|
+
channelType: this.channelType,
|
|
14469
|
+
channelProvider: this.provider,
|
|
14470
|
+
channelAlias: this.alias,
|
|
14298
14471
|
chatId,
|
|
14299
14472
|
userId: message.from_user_id,
|
|
14300
14473
|
displayName: message.from_user_id.slice(0, 12)
|
|
@@ -14325,7 +14498,9 @@ ${failureNote}` : attachments.length > 0 ? failureNote : text2;
|
|
|
14325
14498
|
this.enqueue(inbound);
|
|
14326
14499
|
const summary = attachments.length > 0 ? `[${attachments.length} attachment(s)] ${inbound.text.slice(0, 150)}` : missingVoiceTranscriptCount > 0 && !inbound.text ? "[voice transcription unavailable]" : failedCount > 0 && !inbound.text ? `[${failedCount} attachment(s) failed]` : inbound.text.slice(0, 200);
|
|
14327
14500
|
getBridgeContext().store.insertAuditLog({
|
|
14328
|
-
channelType:
|
|
14501
|
+
channelType: this.channelType,
|
|
14502
|
+
channelProvider: this.provider,
|
|
14503
|
+
channelAlias: this.alias,
|
|
14329
14504
|
chatId,
|
|
14330
14505
|
direction: "inbound",
|
|
14331
14506
|
messageId: inbound.messageId,
|
|
@@ -14388,6 +14563,10 @@ ${failureNote}` : attachments.length > 0 ? failureNote : text2;
|
|
|
14388
14563
|
getBridgeContext().store.setChannelOffset(batch.offsetKey, batch.cursor);
|
|
14389
14564
|
this.pendingCursors.delete(updateId);
|
|
14390
14565
|
}
|
|
14566
|
+
filterConfiguredAccounts(accounts) {
|
|
14567
|
+
if (!this.configuredAccountId) return accounts;
|
|
14568
|
+
return accounts.filter((account) => account.accountId === this.configuredAccountId);
|
|
14569
|
+
}
|
|
14391
14570
|
};
|
|
14392
14571
|
function stripFormatting(text2, parseMode) {
|
|
14393
14572
|
if (parseMode === "HTML") {
|
|
@@ -14398,7 +14577,7 @@ function stripFormatting(text2, parseMode) {
|
|
|
14398
14577
|
}
|
|
14399
14578
|
return text2;
|
|
14400
14579
|
}
|
|
14401
|
-
registerAdapterFactory("weixin", () => new WeixinAdapter());
|
|
14580
|
+
registerAdapterFactory("weixin", (instance) => new WeixinAdapter(instance));
|
|
14402
14581
|
|
|
14403
14582
|
// src/session-bindings.ts
|
|
14404
14583
|
import path5 from "node:path";
|
|
@@ -15082,8 +15261,24 @@ function readDesktopSessionEventStream(threadId) {
|
|
|
15082
15261
|
}
|
|
15083
15262
|
|
|
15084
15263
|
// src/session-bindings.ts
|
|
15085
|
-
function
|
|
15086
|
-
return
|
|
15264
|
+
function asChannelProvider(value) {
|
|
15265
|
+
return value === "feishu" || value === "weixin" ? value : void 0;
|
|
15266
|
+
}
|
|
15267
|
+
function resolveChannelMeta(channelType, provider) {
|
|
15268
|
+
const instance = findChannelInstance(channelType, loadConfig());
|
|
15269
|
+
if (instance) {
|
|
15270
|
+
return {
|
|
15271
|
+
provider: instance.provider,
|
|
15272
|
+
alias: instance.alias
|
|
15273
|
+
};
|
|
15274
|
+
}
|
|
15275
|
+
return {
|
|
15276
|
+
provider,
|
|
15277
|
+
alias: channelType
|
|
15278
|
+
};
|
|
15279
|
+
}
|
|
15280
|
+
function formatChannelLabel(binding) {
|
|
15281
|
+
return binding.channelAlias?.trim() || resolveChannelMeta(binding.channelType, asChannelProvider(binding.channelProvider)).alias || binding.channelType;
|
|
15087
15282
|
}
|
|
15088
15283
|
function formatBindingChatTarget(binding) {
|
|
15089
15284
|
return binding.chatDisplayName?.trim() || binding.chatId;
|
|
@@ -15104,7 +15299,7 @@ function assertBindingTargetAvailable(store, current, opts) {
|
|
|
15104
15299
|
);
|
|
15105
15300
|
if (!conflict) return;
|
|
15106
15301
|
throw new Error(
|
|
15107
|
-
`\u8BE5\u4F1A\u8BDD\u5DF2\u7ED1\u5B9A\u5230 ${formatChannelLabel(conflict
|
|
15302
|
+
`\u8BE5\u4F1A\u8BDD\u5DF2\u7ED1\u5B9A\u5230 ${formatChannelLabel(conflict)} \u804A\u5929 ${formatBindingChatTarget(conflict)}\u3002\u4E00\u4E2A\u4F1A\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u4E2A\u804A\u5929\u3002`
|
|
15108
15303
|
);
|
|
15109
15304
|
}
|
|
15110
15305
|
function getSessionMode(store, session) {
|
|
@@ -15118,8 +15313,11 @@ function bindStoreToSession(store, channelType, chatId, sessionId) {
|
|
|
15118
15313
|
{ channelType, chatId },
|
|
15119
15314
|
{ sessionId: session.id, sdkSessionId: session.sdk_session_id || void 0 }
|
|
15120
15315
|
);
|
|
15316
|
+
const meta = resolveChannelMeta(channelType);
|
|
15121
15317
|
return store.upsertChannelBinding({
|
|
15122
15318
|
channelType,
|
|
15319
|
+
channelProvider: meta.provider,
|
|
15320
|
+
channelAlias: meta.alias,
|
|
15123
15321
|
chatId,
|
|
15124
15322
|
codepilotSessionId: session.id,
|
|
15125
15323
|
sdkSessionId: session.sdk_session_id || "",
|
|
@@ -15134,10 +15332,13 @@ function bindStoreToSdkSession(store, channelType, chatId, sdkSessionId, opts) {
|
|
|
15134
15332
|
{ channelType, chatId },
|
|
15135
15333
|
{ sdkSessionId }
|
|
15136
15334
|
);
|
|
15335
|
+
const meta = resolveChannelMeta(channelType);
|
|
15137
15336
|
const existing = store.findSessionBySdkSessionId(sdkSessionId);
|
|
15138
15337
|
if (existing) {
|
|
15139
15338
|
return store.upsertChannelBinding({
|
|
15140
15339
|
channelType,
|
|
15340
|
+
channelProvider: meta.provider,
|
|
15341
|
+
channelAlias: meta.alias,
|
|
15141
15342
|
chatId,
|
|
15142
15343
|
codepilotSessionId: existing.id,
|
|
15143
15344
|
sdkSessionId,
|
|
@@ -15159,6 +15360,8 @@ function bindStoreToSdkSession(store, channelType, chatId, sdkSessionId, opts) {
|
|
|
15159
15360
|
store.updateSdkSessionId(session.id, sdkSessionId);
|
|
15160
15361
|
return store.upsertChannelBinding({
|
|
15161
15362
|
channelType,
|
|
15363
|
+
channelProvider: meta.provider,
|
|
15364
|
+
channelAlias: meta.alias,
|
|
15162
15365
|
chatId,
|
|
15163
15366
|
codepilotSessionId: session.id,
|
|
15164
15367
|
sdkSessionId,
|
|
@@ -15397,8 +15600,8 @@ function stripOutboundArtifactBlocksForStreaming(text2) {
|
|
|
15397
15600
|
}
|
|
15398
15601
|
return stripped.replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
15399
15602
|
}
|
|
15400
|
-
function supportsOutboundArtifacts(
|
|
15401
|
-
return
|
|
15603
|
+
function supportsOutboundArtifacts(provider) {
|
|
15604
|
+
return provider === "feishu";
|
|
15402
15605
|
}
|
|
15403
15606
|
|
|
15404
15607
|
// src/lib/bridge/conversation-engine.ts
|
|
@@ -15940,9 +16143,9 @@ async function deliver(adapter, message, opts) {
|
|
|
15940
16143
|
} catch {
|
|
15941
16144
|
}
|
|
15942
16145
|
}
|
|
15943
|
-
const limit = PLATFORM_LIMITS[adapter.channelType] || 4096;
|
|
16146
|
+
const limit = PLATFORM_LIMITS[adapter.provider] || PLATFORM_LIMITS[adapter.channelType] || 4096;
|
|
15944
16147
|
let chunks = chunkText2(message.text, limit);
|
|
15945
|
-
if (adapter.
|
|
16148
|
+
if (adapter.provider === "qq" && chunks.length > 3) {
|
|
15946
16149
|
const first2 = chunks.slice(0, 2);
|
|
15947
16150
|
let merged = chunks.slice(2).join("\n");
|
|
15948
16151
|
if (merged.length > limit) {
|
|
@@ -16123,8 +16326,8 @@ async function forwardPermissionRequest(adapter, address, permissionRequestId, t
|
|
|
16123
16326
|
const inputStr = JSON.stringify(toolInput, null, 2);
|
|
16124
16327
|
const truncatedInput = inputStr.length > 300 ? inputStr.slice(0, 300) + "..." : inputStr;
|
|
16125
16328
|
let result;
|
|
16126
|
-
if (adapter.
|
|
16127
|
-
const channelLabel = adapter.
|
|
16329
|
+
if (adapter.provider === "qq" || adapter.provider === "weixin") {
|
|
16330
|
+
const channelLabel = adapter.provider === "weixin" ? "WeChat" : "QQ";
|
|
16128
16331
|
const plainText = [
|
|
16129
16332
|
`Permission Required`,
|
|
16130
16333
|
``,
|
|
@@ -16827,6 +17030,27 @@ function getStreamConfig(channelType = "telegram") {
|
|
|
16827
17030
|
const maxChars = parseInt(store.getSetting(`${prefix}max_chars`) || "", 10) || defaults.maxChars;
|
|
16828
17031
|
return { intervalMs, minDeltaChars, maxChars };
|
|
16829
17032
|
}
|
|
17033
|
+
function listConfiguredChannelInstances() {
|
|
17034
|
+
const { store } = getBridgeContext();
|
|
17035
|
+
const raw = store.getSetting("bridge_channel_instances_json");
|
|
17036
|
+
if (!raw) return [];
|
|
17037
|
+
try {
|
|
17038
|
+
const parsed = JSON.parse(raw);
|
|
17039
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
17040
|
+
} catch {
|
|
17041
|
+
return [];
|
|
17042
|
+
}
|
|
17043
|
+
}
|
|
17044
|
+
function getConfiguredChannelInstance(channelType) {
|
|
17045
|
+
return listConfiguredChannelInstances().find((channel) => channel.id === channelType) || null;
|
|
17046
|
+
}
|
|
17047
|
+
function inferChannelProvider(channelType) {
|
|
17048
|
+
const instance = getConfiguredChannelInstance(channelType);
|
|
17049
|
+
return instance?.provider;
|
|
17050
|
+
}
|
|
17051
|
+
function getChannelProviderKey(channelType) {
|
|
17052
|
+
return inferChannelProvider(channelType) || channelType;
|
|
17053
|
+
}
|
|
16830
17054
|
function parseListIndex(raw) {
|
|
16831
17055
|
const trimmed = raw.trim();
|
|
16832
17056
|
if (!/^\d+$/.test(trimmed)) return null;
|
|
@@ -16909,7 +17133,7 @@ function getSessionDisplayName(session, fallbackDirectory) {
|
|
|
16909
17133
|
if (session?.id) return session.id.slice(0, 8);
|
|
16910
17134
|
return "\u672A\u547D\u540D\u4F1A\u8BDD";
|
|
16911
17135
|
}
|
|
16912
|
-
function
|
|
17136
|
+
function nowIso2() {
|
|
16913
17137
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
16914
17138
|
}
|
|
16915
17139
|
function buildInteractiveStreamKey(sessionId, messageId) {
|
|
@@ -17026,12 +17250,12 @@ function buildDesktopThreadsCommandResponse(desktopSessions, markdown, showAll,
|
|
|
17026
17250
|
);
|
|
17027
17251
|
}
|
|
17028
17252
|
function isFeedbackMarkdownEnabled(channelType) {
|
|
17029
|
-
const
|
|
17030
|
-
if (
|
|
17031
|
-
return
|
|
17253
|
+
const instance = getConfiguredChannelInstance(channelType);
|
|
17254
|
+
if (instance?.provider === "feishu") {
|
|
17255
|
+
return instance.config.feedbackMarkdownEnabled !== false;
|
|
17032
17256
|
}
|
|
17033
|
-
if (
|
|
17034
|
-
return
|
|
17257
|
+
if (instance?.provider === "weixin") {
|
|
17258
|
+
return instance.config.feedbackMarkdownEnabled === true;
|
|
17035
17259
|
}
|
|
17036
17260
|
return false;
|
|
17037
17261
|
}
|
|
@@ -17052,7 +17276,8 @@ function toUserVisibleBindingError(error, fallback) {
|
|
|
17052
17276
|
return fallback;
|
|
17053
17277
|
}
|
|
17054
17278
|
function formatBindingChatLabel(binding) {
|
|
17055
|
-
const
|
|
17279
|
+
const instance = getConfiguredChannelInstance(binding.channelType);
|
|
17280
|
+
const channelLabel = binding.channelAlias || instance?.alias || (binding.channelProvider === "feishu" ? "\u98DE\u4E66" : binding.channelProvider === "weixin" ? "\u5FAE\u4FE1" : binding.channelType);
|
|
17056
17281
|
const chatLabel = binding.chatDisplayName?.trim() || binding.chatId;
|
|
17057
17282
|
return `${channelLabel} \u804A\u5929 ${chatLabel}`;
|
|
17058
17283
|
}
|
|
@@ -17391,7 +17616,7 @@ async function deliverTextResponse(adapter, address, responseText, sessionId, re
|
|
|
17391
17616
|
}
|
|
17392
17617
|
return { ok: true };
|
|
17393
17618
|
}
|
|
17394
|
-
if (parseMode === "Markdown" && adapter.
|
|
17619
|
+
if (parseMode === "Markdown" && adapter.provider === "feishu") {
|
|
17395
17620
|
return deliver(adapter, {
|
|
17396
17621
|
address,
|
|
17397
17622
|
text: responseText,
|
|
@@ -17418,7 +17643,7 @@ async function deliverResponse(adapter, address, responseText, sessionId, replyT
|
|
|
17418
17643
|
if (!captionResult.ok) return captionResult;
|
|
17419
17644
|
lastResult = captionResult;
|
|
17420
17645
|
}
|
|
17421
|
-
if (!supportsOutboundArtifacts(adapter.
|
|
17646
|
+
if (!supportsOutboundArtifacts(adapter.provider)) {
|
|
17422
17647
|
lastResult = await deliverTextResponse(
|
|
17423
17648
|
adapter,
|
|
17424
17649
|
address,
|
|
@@ -17556,7 +17781,7 @@ function syncSessionRuntimeState(sessionId) {
|
|
|
17556
17781
|
store.updateSession(sessionId, {
|
|
17557
17782
|
queued_count: queuedCount,
|
|
17558
17783
|
runtime_status: runtimeStatus,
|
|
17559
|
-
last_runtime_update_at:
|
|
17784
|
+
last_runtime_update_at: nowIso2()
|
|
17560
17785
|
});
|
|
17561
17786
|
}
|
|
17562
17787
|
function incrementQueuedCount(sessionId) {
|
|
@@ -17918,7 +18143,7 @@ function getMirrorStreamingAdapter(subscription) {
|
|
|
17918
18143
|
const state = getState();
|
|
17919
18144
|
const adapter = state.adapters.get(subscription.channelType);
|
|
17920
18145
|
if (!adapter || !adapter.isRunning()) return null;
|
|
17921
|
-
if (subscription.channelType !== "feishu") return null;
|
|
18146
|
+
if (getChannelProviderKey(subscription.channelType) !== "feishu") return null;
|
|
17922
18147
|
if (typeof adapter.onStreamText !== "function" || typeof adapter.onStreamEnd !== "function") {
|
|
17923
18148
|
return null;
|
|
17924
18149
|
}
|
|
@@ -17998,7 +18223,7 @@ async function deliverMirrorTurn(subscription, turn) {
|
|
|
17998
18223
|
renderedStreamText || buildMirrorTitle(title, markdown),
|
|
17999
18224
|
responseParseMode
|
|
18000
18225
|
);
|
|
18001
|
-
if (subscription.channelType === "feishu" && typeof adapter.onStreamEnd === "function") {
|
|
18226
|
+
if (getChannelProviderKey(subscription.channelType) === "feishu" && typeof adapter.onStreamEnd === "function") {
|
|
18002
18227
|
try {
|
|
18003
18228
|
const finalized = await adapter.onStreamEnd(
|
|
18004
18229
|
subscription.chatId,
|
|
@@ -18007,7 +18232,7 @@ async function deliverMirrorTurn(subscription, turn) {
|
|
|
18007
18232
|
turn.streamKey
|
|
18008
18233
|
);
|
|
18009
18234
|
if (finalized) {
|
|
18010
|
-
subscription.lastDeliveredAt = turn.timestamp ||
|
|
18235
|
+
subscription.lastDeliveredAt = turn.timestamp || nowIso2();
|
|
18011
18236
|
return;
|
|
18012
18237
|
}
|
|
18013
18238
|
} catch (error) {
|
|
@@ -18029,7 +18254,7 @@ async function deliverMirrorTurn(subscription, turn) {
|
|
|
18029
18254
|
if (!response.ok) {
|
|
18030
18255
|
throw new Error(response.error || "mirror delivery failed");
|
|
18031
18256
|
}
|
|
18032
|
-
subscription.lastDeliveredAt = turn.timestamp ||
|
|
18257
|
+
subscription.lastDeliveredAt = turn.timestamp || nowIso2();
|
|
18033
18258
|
}
|
|
18034
18259
|
async function deliverMirrorTurns(subscription, turns) {
|
|
18035
18260
|
for (const turn of turns.slice(-MIRROR_EVENT_BATCH_LIMIT)) {
|
|
@@ -18037,7 +18262,7 @@ async function deliverMirrorTurns(subscription, turns) {
|
|
|
18037
18262
|
}
|
|
18038
18263
|
}
|
|
18039
18264
|
function createMirrorTurnState(sessionId, timestamp, turnId) {
|
|
18040
|
-
const safeTimestamp = timestamp ||
|
|
18265
|
+
const safeTimestamp = timestamp || nowIso2();
|
|
18041
18266
|
return {
|
|
18042
18267
|
turnId: turnId || null,
|
|
18043
18268
|
streamKey: buildMirrorStreamKey(sessionId, turnId || null, safeTimestamp),
|
|
@@ -18101,7 +18326,7 @@ function finalizeMirrorTurn(subscription, signature, timestamp, status, preferre
|
|
|
18101
18326
|
userText,
|
|
18102
18327
|
text: text2,
|
|
18103
18328
|
signature,
|
|
18104
|
-
timestamp: timestamp || pendingTurn.lastActivityAt ||
|
|
18329
|
+
timestamp: timestamp || pendingTurn.lastActivityAt || nowIso2(),
|
|
18105
18330
|
status,
|
|
18106
18331
|
...signature.startsWith("timeout:") ? { timedOut: true } : {}
|
|
18107
18332
|
};
|
|
@@ -18336,7 +18561,7 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18336
18561
|
resetMirrorReadState(subscription);
|
|
18337
18562
|
}
|
|
18338
18563
|
watchMirrorFile(subscription, subscription.filePath);
|
|
18339
|
-
subscription.lastReconciledAt =
|
|
18564
|
+
subscription.lastReconciledAt = nowIso2();
|
|
18340
18565
|
if (!subscription.filePath) {
|
|
18341
18566
|
syncMirrorSessionState(subscription.sessionId);
|
|
18342
18567
|
return;
|
|
@@ -18484,6 +18709,51 @@ function notifyAdapterSetChanged() {
|
|
|
18484
18709
|
const { lifecycle } = getBridgeContext();
|
|
18485
18710
|
lifecycle.onBridgeAdaptersChanged?.(getActiveChannelTypes());
|
|
18486
18711
|
}
|
|
18712
|
+
function stableFingerprintValue(value) {
|
|
18713
|
+
if (Array.isArray(value)) {
|
|
18714
|
+
return value.map(stableFingerprintValue);
|
|
18715
|
+
}
|
|
18716
|
+
if (value && typeof value === "object") {
|
|
18717
|
+
return Object.fromEntries(
|
|
18718
|
+
Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, entryValue]) => [key, stableFingerprintValue(entryValue)])
|
|
18719
|
+
);
|
|
18720
|
+
}
|
|
18721
|
+
return value;
|
|
18722
|
+
}
|
|
18723
|
+
function buildAdapterConfigFingerprint(instance) {
|
|
18724
|
+
const normalizedConfig = stableFingerprintValue(instance.config);
|
|
18725
|
+
return JSON.stringify({
|
|
18726
|
+
provider: instance.provider,
|
|
18727
|
+
alias: instance.alias,
|
|
18728
|
+
enabled: instance.enabled,
|
|
18729
|
+
config: normalizedConfig
|
|
18730
|
+
});
|
|
18731
|
+
}
|
|
18732
|
+
function listEnabledAdapterInstances() {
|
|
18733
|
+
const { store } = getBridgeContext();
|
|
18734
|
+
const configured = listConfiguredChannelInstances().filter((channel) => channel.enabled).map((channel) => ({
|
|
18735
|
+
id: channel.id,
|
|
18736
|
+
provider: channel.provider,
|
|
18737
|
+
alias: channel.alias,
|
|
18738
|
+
enabled: channel.enabled,
|
|
18739
|
+
config: channel.config
|
|
18740
|
+
}));
|
|
18741
|
+
const configuredProviders = new Set(configured.map((channel) => channel.provider));
|
|
18742
|
+
for (const provider of getRegisteredTypes()) {
|
|
18743
|
+
if (provider === "feishu" || provider === "weixin") continue;
|
|
18744
|
+
if (configuredProviders.has(provider)) continue;
|
|
18745
|
+
const enabled = store.getSetting(`bridge_${provider}_enabled`) === "true";
|
|
18746
|
+
if (!enabled) continue;
|
|
18747
|
+
configured.push({
|
|
18748
|
+
id: provider,
|
|
18749
|
+
provider,
|
|
18750
|
+
alias: provider,
|
|
18751
|
+
enabled: true,
|
|
18752
|
+
config: {}
|
|
18753
|
+
});
|
|
18754
|
+
}
|
|
18755
|
+
return configured;
|
|
18756
|
+
}
|
|
18487
18757
|
async function stopAdapterInstance(channelType) {
|
|
18488
18758
|
const state = getState();
|
|
18489
18759
|
const adapter = state.adapters.get(channelType);
|
|
@@ -18501,44 +18771,50 @@ async function stopAdapterInstance(channelType) {
|
|
|
18501
18771
|
}
|
|
18502
18772
|
async function syncConfiguredAdapters(options) {
|
|
18503
18773
|
const state = getState();
|
|
18504
|
-
const { store } = getBridgeContext();
|
|
18505
18774
|
let changed = false;
|
|
18506
|
-
|
|
18507
|
-
|
|
18508
|
-
|
|
18509
|
-
|
|
18510
|
-
|
|
18511
|
-
|
|
18512
|
-
|
|
18513
|
-
|
|
18514
|
-
|
|
18515
|
-
|
|
18775
|
+
const desiredInstances = listEnabledAdapterInstances();
|
|
18776
|
+
const desiredKeys = new Set(desiredInstances.map((channel) => channel.id));
|
|
18777
|
+
const desiredFingerprints = new Map(
|
|
18778
|
+
desiredInstances.map((instance) => [instance.id, buildAdapterConfigFingerprint(instance)])
|
|
18779
|
+
);
|
|
18780
|
+
for (const existingKey of Array.from(state.adapters.keys())) {
|
|
18781
|
+
if (desiredKeys.has(existingKey)) continue;
|
|
18782
|
+
await stopAdapterInstance(existingKey);
|
|
18783
|
+
changed = true;
|
|
18784
|
+
}
|
|
18785
|
+
for (const instance of desiredInstances) {
|
|
18786
|
+
const existing = state.adapters.get(instance.id);
|
|
18787
|
+
const desiredFingerprint = desiredFingerprints.get(instance.id) || "";
|
|
18788
|
+
const existingMeta = state.adapterMeta.get(instance.id);
|
|
18789
|
+
if (existing && existingMeta?.configFingerprint === desiredFingerprint) continue;
|
|
18516
18790
|
if (existing) {
|
|
18517
|
-
|
|
18791
|
+
await stopAdapterInstance(instance.id);
|
|
18792
|
+
changed = true;
|
|
18518
18793
|
}
|
|
18519
|
-
const adapter = createAdapter(
|
|
18794
|
+
const adapter = createAdapter(instance);
|
|
18520
18795
|
if (!adapter) continue;
|
|
18521
18796
|
const configError = adapter.validateConfig();
|
|
18522
18797
|
if (configError) {
|
|
18523
|
-
console.warn(`[bridge-manager] ${
|
|
18798
|
+
console.warn(`[bridge-manager] ${instance.id} adapter not valid:`, configError);
|
|
18524
18799
|
continue;
|
|
18525
18800
|
}
|
|
18526
18801
|
try {
|
|
18527
|
-
state.adapters.set(
|
|
18528
|
-
state.adapterMeta.set(
|
|
18802
|
+
state.adapters.set(instance.id, adapter);
|
|
18803
|
+
state.adapterMeta.set(instance.id, {
|
|
18529
18804
|
lastMessageAt: null,
|
|
18530
|
-
lastError: null
|
|
18805
|
+
lastError: null,
|
|
18806
|
+
configFingerprint: desiredFingerprint
|
|
18531
18807
|
});
|
|
18532
18808
|
await adapter.start();
|
|
18533
|
-
console.log(`[bridge-manager] Started adapter: ${
|
|
18809
|
+
console.log(`[bridge-manager] Started adapter: ${instance.id}`);
|
|
18534
18810
|
if (options.startLoops && state.running && adapter.isRunning()) {
|
|
18535
18811
|
runAdapterLoop(adapter);
|
|
18536
18812
|
}
|
|
18537
18813
|
changed = true;
|
|
18538
18814
|
} catch (err) {
|
|
18539
|
-
state.adapters.delete(
|
|
18540
|
-
state.adapterMeta.delete(
|
|
18541
|
-
console.error(`[bridge-manager] Failed to start adapter ${
|
|
18815
|
+
state.adapters.delete(instance.id);
|
|
18816
|
+
state.adapterMeta.delete(instance.id);
|
|
18817
|
+
console.error(`[bridge-manager] Failed to start adapter ${instance.id}:`, err);
|
|
18542
18818
|
}
|
|
18543
18819
|
}
|
|
18544
18820
|
if (changed) {
|
|
@@ -18637,6 +18913,8 @@ function getStatus() {
|
|
|
18637
18913
|
const meta = state.adapterMeta.get(type);
|
|
18638
18914
|
return {
|
|
18639
18915
|
channelType: adapter.channelType,
|
|
18916
|
+
channelProvider: adapter.provider,
|
|
18917
|
+
channelAlias: adapter.alias,
|
|
18640
18918
|
running: adapter.isRunning(),
|
|
18641
18919
|
connectedAt: state.startedAt,
|
|
18642
18920
|
lastMessageAt: meta?.lastMessageAt ?? null,
|
|
@@ -18654,7 +18932,7 @@ function runAdapterLoop(adapter) {
|
|
|
18654
18932
|
try {
|
|
18655
18933
|
const msg = await adapter.consumeOne();
|
|
18656
18934
|
if (!msg) continue;
|
|
18657
|
-
if (msg.callbackData || msg.text.trim().startsWith("/") || isNumericPermissionShortcut(adapter.
|
|
18935
|
+
if (msg.callbackData || msg.text.trim().startsWith("/") || isNumericPermissionShortcut(adapter.provider, msg.text.trim(), msg.address.chatId)) {
|
|
18658
18936
|
await handleMessage(adapter, msg);
|
|
18659
18937
|
} else {
|
|
18660
18938
|
const binding = resolve(msg.address);
|
|
@@ -18669,7 +18947,7 @@ function runAdapterLoop(adapter) {
|
|
|
18669
18947
|
if (abort.signal.aborted) break;
|
|
18670
18948
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
18671
18949
|
console.error(`[bridge-manager] Error in ${adapter.channelType} loop:`, err);
|
|
18672
|
-
const meta = state.adapterMeta.get(adapter.channelType) || { lastMessageAt: null, lastError: null };
|
|
18950
|
+
const meta = state.adapterMeta.get(adapter.channelType) || { lastMessageAt: null, lastError: null, configFingerprint: "" };
|
|
18673
18951
|
meta.lastError = errMsg;
|
|
18674
18952
|
state.adapterMeta.set(adapter.channelType, meta);
|
|
18675
18953
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
@@ -18679,7 +18957,7 @@ function runAdapterLoop(adapter) {
|
|
|
18679
18957
|
if (!abort.signal.aborted) {
|
|
18680
18958
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
18681
18959
|
console.error(`[bridge-manager] ${adapter.channelType} loop crashed:`, err);
|
|
18682
|
-
const meta = state.adapterMeta.get(adapter.channelType) || { lastMessageAt: null, lastError: null };
|
|
18960
|
+
const meta = state.adapterMeta.get(adapter.channelType) || { lastMessageAt: null, lastError: null, configFingerprint: "" };
|
|
18683
18961
|
meta.lastError = errMsg;
|
|
18684
18962
|
state.adapterMeta.set(adapter.channelType, meta);
|
|
18685
18963
|
}
|
|
@@ -18688,7 +18966,7 @@ function runAdapterLoop(adapter) {
|
|
|
18688
18966
|
async function handleMessage(adapter, msg) {
|
|
18689
18967
|
const { store } = getBridgeContext();
|
|
18690
18968
|
const adapterState = getState();
|
|
18691
|
-
const meta = adapterState.adapterMeta.get(adapter.channelType) || { lastMessageAt: null, lastError: null };
|
|
18969
|
+
const meta = adapterState.adapterMeta.get(adapter.channelType) || { lastMessageAt: null, lastError: null, configFingerprint: "" };
|
|
18692
18970
|
meta.lastMessageAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
18693
18971
|
adapterState.adapterMeta.set(adapter.channelType, meta);
|
|
18694
18972
|
const ack = () => {
|
|
@@ -18732,7 +19010,7 @@ async function handleMessage(adapter, msg) {
|
|
|
18732
19010
|
ack();
|
|
18733
19011
|
return;
|
|
18734
19012
|
}
|
|
18735
|
-
if (adapter.
|
|
19013
|
+
if (adapter.provider === "feishu" || adapter.provider === "qq" || adapter.provider === "weixin") {
|
|
18736
19014
|
const normalized = rawText.normalize("NFKC").replace(/[\u200B-\u200D\uFEFF]/g, "").trim();
|
|
18737
19015
|
if (/^[123]$/.test(normalized)) {
|
|
18738
19016
|
const currentBinding = store.getChannelBinding(msg.address.channelType, msg.address.chatId);
|
|
@@ -18852,7 +19130,7 @@ async function handleMessage(adapter, msg) {
|
|
|
18852
19130
|
pendingText: ""
|
|
18853
19131
|
};
|
|
18854
19132
|
}
|
|
18855
|
-
const streamCfg = previewState ? getStreamConfig(adapter.
|
|
19133
|
+
const streamCfg = previewState ? getStreamConfig(adapter.provider) : null;
|
|
18856
19134
|
const previewOnPartialText = previewState && streamCfg ? (fullText) => {
|
|
18857
19135
|
const ps = previewState;
|
|
18858
19136
|
const cfg = streamCfg;
|
|
@@ -19643,6 +19921,35 @@ function uuid() {
|
|
|
19643
19921
|
function now() {
|
|
19644
19922
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
19645
19923
|
}
|
|
19924
|
+
function defaultAliasForProvider2(provider) {
|
|
19925
|
+
if (provider === "feishu") return "\u98DE\u4E66";
|
|
19926
|
+
if (provider === "weixin") return "\u5FAE\u4FE1";
|
|
19927
|
+
return void 0;
|
|
19928
|
+
}
|
|
19929
|
+
function normalizeLegacyBinding(binding) {
|
|
19930
|
+
const config2 = loadConfig();
|
|
19931
|
+
const legacyProvider = binding.channelType === "feishu" || binding.channelType === "weixin" ? binding.channelType : void 0;
|
|
19932
|
+
const resolvedInstance = legacyProvider ? (config2.channels || []).find((channel) => channel.provider === legacyProvider) : findChannelInstance(binding.channelType, config2);
|
|
19933
|
+
if (!resolvedInstance && !legacyProvider) {
|
|
19934
|
+
return {
|
|
19935
|
+
...binding,
|
|
19936
|
+
active: binding.active !== false
|
|
19937
|
+
};
|
|
19938
|
+
}
|
|
19939
|
+
const channelType = resolvedInstance?.id || binding.channelType;
|
|
19940
|
+
const channelProvider = resolvedInstance?.provider || legacyProvider || binding.channelProvider;
|
|
19941
|
+
const channelAlias = resolvedInstance?.alias || binding.channelAlias || defaultAliasForProvider2(channelProvider);
|
|
19942
|
+
return {
|
|
19943
|
+
...binding,
|
|
19944
|
+
channelType,
|
|
19945
|
+
channelProvider,
|
|
19946
|
+
channelAlias,
|
|
19947
|
+
active: binding.active !== false
|
|
19948
|
+
};
|
|
19949
|
+
}
|
|
19950
|
+
function didBindingChange(before, after) {
|
|
19951
|
+
return before.channelType !== after.channelType || before.channelProvider !== after.channelProvider || before.channelAlias !== after.channelAlias || before.active !== false !== after.active;
|
|
19952
|
+
}
|
|
19646
19953
|
var JsonFileStore = class {
|
|
19647
19954
|
settings;
|
|
19648
19955
|
dynamicSettings;
|
|
@@ -19700,7 +20007,19 @@ var JsonFileStore = class {
|
|
|
19700
20007
|
path12.join(DATA_DIR2, "bindings.json"),
|
|
19701
20008
|
{}
|
|
19702
20009
|
);
|
|
19703
|
-
|
|
20010
|
+
const normalized = /* @__PURE__ */ new Map();
|
|
20011
|
+
let changed = false;
|
|
20012
|
+
for (const binding of Object.values(bindings)) {
|
|
20013
|
+
const normalizedBinding = normalizeLegacyBinding(binding);
|
|
20014
|
+
if (didBindingChange(binding, normalizedBinding)) {
|
|
20015
|
+
changed = true;
|
|
20016
|
+
}
|
|
20017
|
+
normalized.set(`${normalizedBinding.channelType}:${normalizedBinding.chatId}`, normalizedBinding);
|
|
20018
|
+
}
|
|
20019
|
+
this.bindings = normalized;
|
|
20020
|
+
if (changed) {
|
|
20021
|
+
this.persistBindings();
|
|
20022
|
+
}
|
|
19704
20023
|
}
|
|
19705
20024
|
persistSessions() {
|
|
19706
20025
|
writeJson(
|
|
@@ -19780,6 +20099,8 @@ var JsonFileStore = class {
|
|
|
19780
20099
|
...existing,
|
|
19781
20100
|
codepilotSessionId: data.codepilotSessionId,
|
|
19782
20101
|
sdkSessionId: data.sdkSessionId ?? existing.sdkSessionId,
|
|
20102
|
+
channelProvider: data.channelProvider ?? existing.channelProvider,
|
|
20103
|
+
channelAlias: data.channelAlias ?? existing.channelAlias,
|
|
19783
20104
|
chatUserId: data.chatUserId ?? existing.chatUserId,
|
|
19784
20105
|
chatDisplayName: data.chatDisplayName ?? existing.chatDisplayName,
|
|
19785
20106
|
workingDirectory: data.workingDirectory,
|
|
@@ -19794,6 +20115,8 @@ var JsonFileStore = class {
|
|
|
19794
20115
|
const binding = {
|
|
19795
20116
|
id: uuid(),
|
|
19796
20117
|
channelType: data.channelType,
|
|
20118
|
+
channelProvider: data.channelProvider,
|
|
20119
|
+
channelAlias: data.channelAlias,
|
|
19797
20120
|
chatId: data.chatId,
|
|
19798
20121
|
chatUserId: data.chatUserId,
|
|
19799
20122
|
chatDisplayName: data.chatDisplayName,
|
|
@@ -20731,6 +21054,9 @@ function writeStatus(info) {
|
|
|
20731
21054
|
function getRunningChannels() {
|
|
20732
21055
|
return getStatus().adapters.map((adapter) => adapter.channelType).sort();
|
|
20733
21056
|
}
|
|
21057
|
+
function getAdapterStatuses() {
|
|
21058
|
+
return getStatus().adapters;
|
|
21059
|
+
}
|
|
20734
21060
|
async function main() {
|
|
20735
21061
|
const config2 = loadConfig();
|
|
20736
21062
|
setupLogger();
|
|
@@ -20758,7 +21084,8 @@ async function main() {
|
|
|
20758
21084
|
pid: process.pid,
|
|
20759
21085
|
runId,
|
|
20760
21086
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
20761
|
-
channels
|
|
21087
|
+
channels,
|
|
21088
|
+
adapters: getAdapterStatuses()
|
|
20762
21089
|
});
|
|
20763
21090
|
console.log(`[codex-to-im] Bridge started (PID: ${process.pid}, channels: ${channels.join(", ")})`);
|
|
20764
21091
|
},
|
|
@@ -20767,12 +21094,13 @@ async function main() {
|
|
|
20767
21094
|
running: true,
|
|
20768
21095
|
pid: process.pid,
|
|
20769
21096
|
runId,
|
|
20770
|
-
channels
|
|
21097
|
+
channels,
|
|
21098
|
+
adapters: getAdapterStatuses()
|
|
20771
21099
|
});
|
|
20772
21100
|
console.log(`[codex-to-im] Active channels updated: ${channels.join(", ") || "none"}`);
|
|
20773
21101
|
},
|
|
20774
21102
|
onBridgeStop: () => {
|
|
20775
|
-
writeStatus({ running: false, channels: [] });
|
|
21103
|
+
writeStatus({ running: false, channels: [], adapters: [] });
|
|
20776
21104
|
console.log("[codex-to-im] Bridge stopped");
|
|
20777
21105
|
}
|
|
20778
21106
|
}
|