diff --git a/manifest.json b/manifest.json
index cea2096..f5d4cb1 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "渠道应用开发部知识库",
- "version": "2.0.1",
+ "version": "2.0.0",
"description": "通过关键词快速搜索内部文档",
"permissions": ["storage"],
"host_permissions": [
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..3ef0410
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,7 @@
+
+
+## 修订记录
+| 时间 | 版本 | 修订人 | 说明 |
+| ---------- | ------ | ------- | ------------------ |
+| 2025.06.02 | V2.0.0 | Luke.Ye | 完成插件配置化改造 |
+
diff --git a/scripts/llm.js b/scripts/llm.js
index 4f24ef5..8179986 100644
--- a/scripts/llm.js
+++ b/scripts/llm.js
@@ -1,47 +1,48 @@
-const LLM_TOKEN_API = "http://llm.wisdompulse.cn/api/request-token";
-const LLM_WORKSPACES_API = "http://llm.wisdompulse.cn/api/workspaces";
-const LLM_STREAM_API_BASE = "http://llm.wisdompulse.cn/api/workspace";
const LLM_TOKEN_EXPIRE_MS = 23 * 60 * 60 * 1000;
async function resolveJumpLinks(titles) {
if (!titles || titles.length === 0) return {};
+ const config = await loadMergedConfig();
+ const filePathCfg = config.apiConfig.filePath;
+ const publicBase = config.apiConfig.materialPublicUrl.url;
+ const reqField = filePathCfg.requestParams[0].field;
+ const respField = filePathCfg.responseParams[0].field;
+
return new Promise((resolveOuter) => {
chrome.storage.local.get(["token"], async (res) => {
const token = res.token;
- if (!token) {
- console.warn("resolveJumpLinks 缺少 token,跳过");
- return resolveOuter({});
- }
-
+ if (!token) return resolveOuter({});
try {
- const res = await fetch("http://app.wisdompulse.cn/search/file-path", {
+ const body = {};
+ body[reqField] = titles;
+ const resp = await fetch(filePathCfg.url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": token
},
- body: JSON.stringify({ fileNames: titles })
+ body: JSON.stringify(body)
});
- const data = await res.json();
+ const data = await resp.json();
+ const fileMap = data[respField] || {};
const map = {};
- for (const title of Object.keys(data)) {
- if (data[title]) {
- const cleanPath = data[title].replace(/\.md$/, ".html");
- map[title] = `http://share.wisdompulse.cn/public/${cleanPath}`;
+ for (const title of Object.keys(fileMap)) {
+ if (fileMap[title]) {
+ const cleanPath = fileMap[title].replace(/\.md$/, ".html");
+ map[title] = `${publicBase}/${cleanPath}`;
}
}
-
resolveOuter(map);
} catch (e) {
- console.warn("resolveJumpLinks 接口异常:", e);
resolveOuter({});
}
});
});
}
+
async function renderSources(sourceList, sources, jumpMap) {
sourceList.style.marginTop = "12px";
sourceList.style.borderTop = "1px solid #dbe3f3";
@@ -99,76 +100,10 @@ async function renderSources(sourceList, sources, jumpMap) {
ul.appendChild(li);
}
-
sourceList.appendChild(ul);
}
-async function getLLMPassword() {
- return new Promise((resolve) => {
- chrome.storage.local.get(["llmPassword"], (res) => {
- resolve(res.llmPassword || "");
- });
- });
-}
-
-async function getLLMToken(forceRefresh = false) {
- return new Promise((resolve, reject) => {
- chrome.storage.local.get(["llmToken", "llmTokenAt"], async (res) => {
- const now = Date.now();
- if (!forceRefresh && res.llmToken && res.llmTokenAt && now - res.llmTokenAt < LLM_TOKEN_EXPIRE_MS) {
- log("LLM token 命中缓存");
- resolve(res.llmToken);
- } else {
- try {
- const pwd = await getLLMPassword();
- const response = await fetch(LLM_TOKEN_API, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ password: pwd }),
- });
- const data = await response.json();
- if (data.token) {
- chrome.storage.local.set({ llmToken: data.token, llmTokenAt: now });
- resolve(data.token);
- } else {
- reject("LLM token获取失败");
- }
- } catch (e) {
- reject("LLM token接口异常");
- }
- }
- });
- });
-}
-
-async function getLLMWorkspaceSlug(token, forceRefresh = false) {
- return new Promise((resolve, reject) => {
- chrome.storage.local.get(["llmWorkspaceSlug", "llmWorkspaceAt"], async (res) => {
- const now = Date.now();
- if (!forceRefresh && res.llmWorkspaceSlug && res.llmWorkspaceAt && now - res.llmWorkspaceAt < LLM_TOKEN_EXPIRE_MS) {
- resolve(res.llmWorkspaceSlug);
- } else {
- try {
- const response = await fetch(LLM_WORKSPACES_API, {
- method: "GET",
- headers: { Authorization: `Bearer ${token}` },
- });
- const data = await response.json();
- if (data.workspaces && data.workspaces.length > 0 && data.workspaces[0].slug) {
- chrome.storage.local.set({ llmWorkspaceSlug: data.workspaces[0].slug, llmWorkspaceAt: now });
- resolve(data.workspaces[0].slug);
- } else {
- reject("未获取到LLM工作区slug");
- }
- } catch (e) {
- reject("LLM工作区接口异常");
- }
- }
- });
- });
-}
-
-// 代码块复制按钮插入
+// === 复制代码块功能 ===
function wrapHtmlWithCopyBtn(html) {
const wrapper = document.createElement('div');
wrapper.innerHTML = html;
@@ -215,17 +150,13 @@ function bindAllCopyButtons(container) {
btn.innerText = '失败';
setTimeout(() => btn.innerText = '复制', 1200);
}
- if (success) {
- log("[复制代码] 成功,内容:", codeText);
- } else {
- console.error("[复制代码] 失败", error, "内容:", codeText);
+ if (!success) {
alert("复制失败: " + (error ? error : "未知错误"));
}
});
});
}
-// 复制全部回答按钮,从HTML解析ul/li/a结构
function insertCopyFullAnswerBtn(msgBox, answerStr) {
if (msgBox.querySelector('.copy-full-answer-btn')) return;
@@ -294,10 +225,92 @@ function insertCopyFullAnswerBtn(msgBox, answerStr) {
msgBox.appendChild(copyBtn);
}
-// 主体流式渲染 fetchLLMAnswer
+async function getLLMPassword() {
+ return new Promise((resolve) => {
+ chrome.storage.local.get(["llmPassword"], (res) => {
+ resolve(res.llmPassword || "");
+ });
+ });
+}
+
+async function getLLMToken(forceRefresh = false) {
+ const config = await loadMergedConfig();
+ const tokenCfg = config.apiConfig.llmToken;
+ return new Promise((resolve, reject) => {
+ chrome.storage.local.get(["llmToken", "llmTokenAt"], async (res) => {
+ const now = Date.now();
+ if (!forceRefresh && res.llmToken && res.llmTokenAt && now - res.llmTokenAt < LLM_TOKEN_EXPIRE_MS) {
+ resolve(res.llmToken);
+ } else {
+ try {
+ const pwd = await getLLMPassword();
+ const body = {};
+ body[tokenCfg.requestParams[0].field] = pwd;
+
+ const response = await fetch(tokenCfg.url, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(body),
+ });
+
+ const data = await response.json();
+ const tokenField = tokenCfg.responseParams.find(p => p.label.includes("Token")).field;
+ if (data[tokenField]) {
+ chrome.storage.local.set({ llmToken: data[tokenField], llmTokenAt: now });
+ resolve(data[tokenField]);
+ } else {
+ reject("LLM token获取失败");
+ }
+ } catch (e) {
+ reject("LLM token接口异常");
+ }
+ }
+ });
+ });
+}
+
+async function getLLMWorkspaceSlug(token, forceRefresh = false) {
+ const config = await loadMergedConfig();
+ const wsCfg = config.apiConfig.workspaces;
+ const workspacesField = wsCfg.responseParams.find(p => p.field)?.field;
+
+ return new Promise((resolve, reject) => {
+ chrome.storage.local.get(["llmWorkspaceSlug", "llmWorkspaceAt"], async (res) => {
+ const now = Date.now();
+ if (!forceRefresh && res.llmWorkspaceSlug && res.llmWorkspaceAt && now - res.llmWorkspaceAt < LLM_TOKEN_EXPIRE_MS) {
+ resolve(res.llmWorkspaceSlug);
+ } else {
+ try {
+ const response = await fetch(wsCfg.url, {
+ method: "GET",
+ headers: { Authorization: `Bearer ${token}` },
+ });
+
+ const data = await response.json();
+ const list = data[workspacesField] || [];
+ if (list.length > 0 && list[0].slug) {
+ chrome.storage.local.set({ llmWorkspaceSlug: list[0].slug, llmWorkspaceAt: now });
+ resolve(list[0].slug);
+ } else {
+ reject("未获取到LLM工作区slug");
+ }
+ } catch (e) {
+ reject("LLM工作区接口异常");
+ }
+ }
+ });
+ });
+}
+
async function fetchLLMAnswer(question, container, contentDiv) {
if (!question || !question.trim()) return;
+ const config = await loadMergedConfig();
+ const llmChatCfg = config.apiConfig.llmChat;
+ const llmToken = await getLLMToken();
+ const workspaceSlug = await getLLMWorkspaceSlug(llmToken);
+ const url = llmChatCfg.url.replace("${workId}", workspaceSlug);
+
const userItem = document.createElement("div");
userItem.className = "llm-msg-item llm-user";
userItem.innerHTML = `我: ${question}`;
@@ -311,19 +324,18 @@ async function fetchLLMAnswer(question, container, contentDiv) {
let collectedSources = [];
let jumpMap = {};
-
try {
- const token = await getLLMToken();
- const workspace = await getLLMWorkspaceSlug(token);
- const url = `${LLM_STREAM_API_BASE}/${workspace}/stream-chat`;
+ const body = {};
+ body[llmChatCfg.requestParams[0].field] = question;
+ body[llmChatCfg.requestParams[1].field] = [];
const res = await fetch(url, {
method: "POST",
headers: {
- "Authorization": `Bearer ${token}`,
+ "Authorization": `Bearer ${llmToken}`,
"Content-Type": "application/json"
},
- body: JSON.stringify({ message: question, attachments: [] })
+ body: JSON.stringify(body)
});
if (!res.ok || !res.body) {
@@ -345,14 +357,11 @@ async function fetchLLMAnswer(question, container, contentDiv) {
buffer = lines.pop();
for (let line of lines) {
if (line.startsWith("data: ")) {
- const data = line.slice(6);
- const chunk = JSON.parse(data);
-
- // 收集 sources(去重按 id)
- if (chunk.sources && Array.isArray(chunk.sources)) {
- for (const src of chunk.sources) {
- if (!collectedSources.find(s => s.id === src.id)) {
- collectedSources.push(src);
+ const chunk = JSON.parse(line.slice(6));
+ if (chunk.sources) {
+ for (const s of chunk.sources) {
+ if (!collectedSources.find(x => x.id === s.id)) {
+ collectedSources.push(s);
}
}
}
@@ -369,20 +378,20 @@ async function fetchLLMAnswer(question, container, contentDiv) {
if (inThink) {
thinkStr += content.replace(/\n/g, "
");
} else {
- answerStr += content; // 直接累计 markdown 原文
+ answerStr += content;
}
// 渲染 markdown,代码块加复制按钮
let html = "";
try {
- html = window.marked ? marked.parse(answerStr) : answerStr.replace(/\n/g, "
");
+ html = window.marked ? wrapHtmlWithCopyBtn(marked.parse(answerStr)) : answerStr.replace(/\n/g, "
");
} catch (e) {
html = answerStr.replace(/\n/g, "
");
}
msgBox.innerHTML =
(thinkStr ? `