@numa-tech/numa 1.25.0 → 1.26.0
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/business-issuer.js +3 -1
- package/dist/business-issuer.js.map +1 -1
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/kb/catalog.js +33 -2
- package/dist/kb/catalog.js.map +1 -1
- package/dist/kb/chat-stream.d.ts +5 -0
- package/dist/kb/chat-stream.js +61 -0
- package/dist/kb/chat-stream.js.map +1 -0
- package/dist/kb/client.d.ts +75 -6
- package/dist/kb/client.js +104 -7
- package/dist/kb/client.js.map +1 -1
- package/dist/kb/commands.js +92 -7
- package/dist/kb/commands.js.map +1 -1
- package/dist/kb/config.d.ts +14 -2
- package/dist/kb/config.js +21 -7
- package/dist/kb/config.js.map +1 -1
- package/dist/kb/management-commands.d.ts +7 -0
- package/dist/kb/management-commands.js +86 -0
- package/dist/kb/management-commands.js.map +1 -0
- package/dist/kb/management-schemas.d.ts +106 -0
- package/dist/kb/management-schemas.js +33 -0
- package/dist/kb/management-schemas.js.map +1 -0
- package/dist/kb/transfers.d.ts +4 -0
- package/dist/kb/transfers.js +11 -4
- package/dist/kb/transfers.js.map +1 -1
- package/dist/kb/upload-decisions.d.ts +28 -0
- package/dist/kb/upload-decisions.js +23 -0
- package/dist/kb/upload-decisions.js.map +1 -0
- package/dist/kb/upload-state.d.ts +1 -0
- package/dist/kb/upload-state.js +1 -0
- package/dist/kb/upload-state.js.map +1 -1
- package/dist/kb/web-login.d.ts +10 -0
- package/dist/kb/web-login.js +25 -0
- package/dist/kb/web-login.js.map +1 -0
- package/dist/kb/web-page.d.ts +3 -0
- package/dist/kb/web-page.js +114 -0
- package/dist/kb/web-page.js.map +1 -0
- package/dist/kb/web-queue.d.ts +61 -0
- package/dist/kb/web-queue.js +101 -0
- package/dist/kb/web-queue.js.map +1 -0
- package/dist/kb/web.d.ts +26 -0
- package/dist/kb/web.js +234 -0
- package/dist/kb/web.js.map +1 -0
- package/dist/local-web/context-menu.d.ts +2 -0
- package/dist/local-web/context-menu.js +53 -0
- package/dist/local-web/context-menu.js.map +1 -0
- package/dist/local-web/file-workspace-style.d.ts +2 -0
- package/dist/local-web/file-workspace-style.js +58 -0
- package/dist/local-web/file-workspace-style.js.map +1 -0
- package/dist/local-web/file-workspace.d.ts +2 -0
- package/dist/local-web/file-workspace.js +39 -0
- package/dist/local-web/file-workspace.js.map +1 -0
- package/dist/local-web/security.d.ts +6 -0
- package/dist/local-web/security.js +51 -0
- package/dist/local-web/security.js.map +1 -0
- package/dist/login-recovery.js +7 -2
- package/dist/login-recovery.js.map +1 -1
- package/dist/oss/transfer-queue.d.ts +7 -51
- package/dist/oss/transfer-queue.js +15 -308
- package/dist/oss/transfer-queue.js.map +1 -1
- package/dist/oss/web-page.js +21 -103
- package/dist/oss/web-page.js.map +1 -1
- package/dist/oss/web.js +4 -45
- package/dist/oss/web.js.map +1 -1
- package/dist/platform-profile.d.ts +25 -1
- package/dist/platform-profile.js +44 -1
- package/dist/platform-profile.js.map +1 -1
- package/dist/transfers/persistent-queue.d.ts +65 -0
- package/dist/transfers/persistent-queue.js +312 -0
- package/dist/transfers/persistent-queue.js.map +1 -0
- package/docs/design/kb-capability-upgrade.md +36 -0
- package/docs/design/kb-web-workspace.md +97 -0
- package/docs/kb-access.md +72 -0
- package/package.json +9 -7
- package/scripts/export-kb-contract.py +25 -6
- package/src/kb/openapi-contract.json +2297 -566
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { LOCAL_CONTEXT_MENU_JS } from '../local-web/context-menu.js';
|
|
2
|
+
import { FILE_WORKSPACE_CSS } from '../local-web/file-workspace-style.js';
|
|
3
|
+
import { FILE_WORKSPACE_JS } from '../local-web/file-workspace.js';
|
|
4
|
+
import { OSS_REFRESH_ICON } from '../oss/web-icons.js';
|
|
5
|
+
export const KB_WEB_CSS = FILE_WORKSPACE_CSS + String.raw `
|
|
6
|
+
.kb-collections{display:flex;flex-direction:column;gap:6px;margin-top:18px}.kb-collections button{text-align:left;white-space:normal;overflow-wrap:anywhere}.kb-collections button[aria-current=true]{background:#e1ebe2;border-color:#9fbba1}.kb-status{display:inline-block;padding:3px 7px;border-radius:4px;background:#edf5ee;color:#24573d}.kb-actions{display:flex;gap:7px;flex-wrap:wrap}.kb-detail{white-space:pre-wrap;overflow-wrap:anywhere;font-size:12px;line-height:1.8}.task-ingestion{font-size:12px;margin-top:8px;line-height:1.7;color:#24573d}.tenant-label{white-space:nowrap}button:focus-visible,input:focus-visible,select:focus-visible{outline:2px solid #227b54;outline-offset:3px}@media(max-width:820px){.sidebar{display:flex;max-height:210px;flex-shrink:0}.kb-collections{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));margin-top:10px}.workspace{overflow:auto}}
|
|
7
|
+
`;
|
|
8
|
+
export const KB_WEB_HTML = String.raw `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Numa · 知识库文档</title><link rel="icon" href="data:,"><link rel="stylesheet" href="/app.css"><script src="/app.js" defer></script></head><body>
|
|
9
|
+
<header class="topbar"><strong>Numa · 知识库文档</strong><span class="topnote">管理文档与上传任务</span><button id="tasks-toggle">传输与入库 <span id="tasks-count">0</span></button><button id="service-stop">停止本地服务</button></header>
|
|
10
|
+
<section class="authorization"><label for="environment">Namespace</label><select id="environment" aria-label="Namespace"></select><button id="environment-login" hidden>登录当前环境</button><label class="tenant-label" for="tenant">租户</label><select id="tenant" aria-label="租户"></select><button id="refresh" aria-label="刷新文档">${OSS_REFRESH_ICON}</button></section>
|
|
11
|
+
<p id="notice" class="notice" role="status" hidden></p>
|
|
12
|
+
<main class="workspace"><aside class="sidebar"><h2>知识库</h2><div id="collections" class="kb-collections"></div></aside>
|
|
13
|
+
<section class="files"><div class="page-heading"><div><h1 id="heading">选择知识库</h1><p id="collection-info" class="scope"></p></div><div class="upload-actions"><button id="upload-file" class="primary" disabled>上传文件</button><button id="upload-folder" disabled>上传目录</button></div></div>
|
|
14
|
+
<form id="search-form" class="toolbar"><input id="search" type="search" placeholder="搜索文档名称" aria-label="文档名称"><button type="submit">搜索</button></form>
|
|
15
|
+
<div class="table-wrap"><table><thead><tr><th>文档</th><th class="size-cell">大小</th><th class="date-cell">处理状态</th></tr></thead><tbody id="documents"></tbody></table><p id="empty" class="empty">请先选择知识库。</p></div>
|
|
16
|
+
<footer class="list-footer"><span id="page-info"></span><div><button id="previous" disabled>上一页</button><button id="next" disabled>下一页</button></div></footer></section>
|
|
17
|
+
<aside class="preview-panel"><div class="preview-heading"><h2>文档详情</h2></div><div id="detail" class="preview-body"><p>选择文档,查看状态和版本。</p></div><div class="preview-actions"><button id="download" disabled>下载原件</button><button id="delete" class="danger" disabled>删除文档</button></div></aside></main>
|
|
18
|
+
<section id="tasks-panel" class="tasks-panel" aria-label="传输与入库任务" hidden><div class="tasks-heading"><h2>传输与入库</h2><button id="tasks-close" aria-label="关闭任务面板">关闭</button></div><p class="tasks-note">关闭页面不影响上传;停止本地服务会暂停传输。已提交的入库任务继续在服务端处理。恢复上传会跳过已完成文件,未完成文件重新传输。</p><p id="tasks-connection" role="status"></p><div id="tasks" class="tasks-list"></div></section>
|
|
19
|
+
<dialog id="delete-dialog"><h2>删除文档</h2><p id="delete-message"></p><p>将通过知识库服务删除文档,并清理相关文件与索引。</p><div class="dialog-actions"><button id="delete-cancel">取消</button><button id="delete-confirm" class="destructive">确认删除</button></div></dialog>
|
|
20
|
+
<div id="file-menu" class="file-menu" role="menu" aria-label="文档操作" hidden></div>
|
|
21
|
+
</body></html>`;
|
|
22
|
+
export const KB_WEB_JS = String.raw `'use strict';
|
|
23
|
+
${LOCAL_CONTEXT_MENU_JS}
|
|
24
|
+
${FILE_WORKSPACE_JS}
|
|
25
|
+
const $=id=>document.getElementById(id);
|
|
26
|
+
const entryFragment=new URLSearchParams(location.hash.slice(1));
|
|
27
|
+
let token=entryFragment.get('token')||sessionStorage.getItem('numa-kb-web-token')||'';
|
|
28
|
+
if(token)sessionStorage.setItem('numa-kb-web-token',token);history.replaceState(null,'',location.pathname);
|
|
29
|
+
let autoLogin=true,loginReturn=entryFragment.get('login')==='return',environment='',environmentChoices=[],tenant='',collection='',collections=[],documents=[],selected=null,offset=0,epoch=0,taskBusy=false,stopped=false,deleting=null;
|
|
30
|
+
const bytes=n=>n>=1048576?(n/1048576).toFixed(1)+' MiB':n>=1024?(n/1024).toFixed(1)+' KiB':String(n||0)+' B';
|
|
31
|
+
const status=s=>({queued:'等待上传',running:'传输中',paused:'已暂停',pausing:'暂停中',cancelling:'停止中',cancelled:'已停止',completed:'已完成',failed:'失败',submitted:'已提交入库',processing:'处理中',indexed:'已索引',pending:'待处理',archived:'已入库',needs_resolution:'需要处理冲突',partial_failed:'部分失败',reused:'已复用',skipped:'已跳过'})[s]||String(s||'未知');
|
|
32
|
+
const node=(tag,text,cls)=>{const e=document.createElement(tag);if(text!==undefined)e.textContent=String(text);if(cls)e.className=cls;return e;};
|
|
33
|
+
function notice(message){$('notice').hidden=!message;$('notice').textContent=message||'';}
|
|
34
|
+
async function api(path,body,targetEnvironment=environment){const route=path+(path.includes('?')?'&':'?')+new URLSearchParams({environment:targetEnvironment});const r=await fetch(route,{method:body===undefined?'GET':'POST',headers:{Authorization:'Bearer '+token,...(body===undefined?{}:{'Content-Type':'application/json'})},...(body===undefined?{}:{body:JSON.stringify(body)})});const d=await r.json();if(!r.ok||!d.ok){const error=new Error((d.error?.message||'请求未完成,请刷新后重试。')+' ['+(d.error?.code||'kb_web_error')+']');error.code=d.error?.code;error.status=r.status;throw error;}return d.data;}
|
|
35
|
+
function run(work){Promise.resolve().then(work).catch(e=>notice(e.message));}
|
|
36
|
+
const documentMenu=createLocalContextMenu($('file-menu'));
|
|
37
|
+
function clearDocument(){documentMenu.close();selected=null;$('download').disabled=true;$('delete').disabled=true;$('detail').replaceChildren(node('p','选择文档,查看状态和版本。'));}
|
|
38
|
+
function selectDocument(d){selected=d;$('download').disabled=false;$('delete').disabled=d.can_manage_versions!==true;const box=$('detail');box.replaceChildren(node('h3',d.filename||d.document_id));const dl=node('dl');for(const [k,v] of [['状态',status(d.status)],['版本',d.version_number??d.version??'—'],['大小',bytes(d.file_size_bytes??d.size_bytes)],['更新时间',d.updated_at||'—']]){dl.append(node('dt',k),node('dd',v));}box.append(dl);if(d.summary)box.append(node('p',d.summary));}
|
|
39
|
+
function openDocumentMenu(event,d,anchor){
|
|
40
|
+
documentMenu.begin(event,anchor);selectDocument(d);
|
|
41
|
+
documentMenu.item('查看详情',()=>selectDocument(d));
|
|
42
|
+
documentMenu.item('下载原件…',()=>$('download').onclick());
|
|
43
|
+
documentMenu.item('删除文档…',()=>$('delete').onclick(),{danger:true,disabled:d.can_manage_versions!==true,onDisabled:()=>notice('当前身份没有此文档的管理权限。')});
|
|
44
|
+
documentMenu.show(event,anchor);
|
|
45
|
+
}
|
|
46
|
+
async function loadDocuments(){const version=++epoch;clearDocument();$('documents').replaceChildren();$('empty').hidden=false;$('empty').textContent=collection?'正在加载文档…':'请先选择知识库。';$('previous').disabled=true;$('next').disabled=true;if(!collection)return;
|
|
47
|
+
const rows=await api('/api/documents?'+new URLSearchParams({tenant,collection,offset:String(offset),q:$('search').value}));if(version!==epoch)return;documents=rows;$('empty').hidden=rows.length>0;$('empty').textContent='此知识库暂无匹配文档。';
|
|
48
|
+
for(const d of rows)$('documents').append(workspaceFileRow({name:d.filename||d.document_id,title:d.filename||d.document_id,onOpen:()=>selectDocument(d),onMenu:(e,anchor)=>openDocumentMenu(e,d,anchor),cells:[[bytes(d.file_size_bytes??d.size_bytes),'size-cell'],[status(d.status),'date-cell']]}));
|
|
49
|
+
$('page-info').textContent=rows.length?'第 '+(offset+1)+'–'+(offset+rows.length)+' 条':'暂无文档';$('previous').disabled=offset===0;$('next').disabled=rows.length<100;
|
|
50
|
+
}
|
|
51
|
+
async function selectCollection(c){collection=String(c.collection_id);offset=0;$('heading').textContent=c.name||collection;$('collection-info').textContent=c.description||'';$('upload-file').disabled=c.can_upload!==true;$('upload-folder').disabled=c.can_upload!==true;for(const b of $('collections').children)b.setAttribute('aria-current',String(b.dataset.id===collection));await loadDocuments();}
|
|
52
|
+
async function loadCollections(){const version=++epoch;collection='';collections=[];clearDocument();$('documents').replaceChildren();$('collections').replaceChildren();$('heading').textContent='选择知识库';$('collection-info').textContent='';$('upload-file').disabled=true;$('upload-folder').disabled=true;
|
|
53
|
+
const result=await api('/api/collections?'+new URLSearchParams({tenant}));if(version!==epoch)return;collections=result.collections;
|
|
54
|
+
for(const c of collections){const b=node('button',c.name||c.collection_id);b.dataset.id=c.collection_id;b.onclick=()=>run(()=>selectCollection(c));$('collections').append(b);}if(collections.length)await selectCollection(collections[0]);else{$('empty').hidden=false;$('empty').textContent='当前租户下没有可访问的知识库。';}
|
|
55
|
+
}
|
|
56
|
+
const transferCards=new Map(),transferBusy=new Set();
|
|
57
|
+
async function transferAction(id,action,targetEnvironment){
|
|
58
|
+
const key=targetEnvironment+'\0'+id;if(transferBusy.has(key))return;transferBusy.add(key);
|
|
59
|
+
const card=transferCards.get(key);if(card)for(const button of card.actions.values())button.disabled=true;
|
|
60
|
+
try{await api('/api/transfers/'+id+'/'+action,{},targetEnvironment);}catch(e){notice(e.message);}finally{transferBusy.delete(key);await refreshTasks();}
|
|
61
|
+
}
|
|
62
|
+
async function refreshTasks(){
|
|
63
|
+
if(taskBusy||stopped)return;taskBusy=true;const targetEnvironment=environment;
|
|
64
|
+
try{
|
|
65
|
+
const rows=await api('/api/transfers',undefined,targetEnvironment);if(targetEnvironment!==environment)return;
|
|
66
|
+
$('tasks-count').textContent=String(rows.filter(x=>!['completed','cancelled'].includes(x.status)).length);$('tasks-connection').textContent='';
|
|
67
|
+
const present=new Set(rows.map(t=>targetEnvironment+'\0'+t.id));
|
|
68
|
+
for(const [key,card] of transferCards)if(!present.has(key)){card.root.remove();transferCards.delete(key);}
|
|
69
|
+
for(const t of rows){
|
|
70
|
+
const key=targetEnvironment+'\0'+t.id;let card=transferCards.get(key);
|
|
71
|
+
if(!card){card=createWorkspaceTransferCard(t.id,(id,action)=>transferAction(id,action,targetEnvironment),{pause:'暂停',resume:'恢复',cancel:'停止传输'});transferCards.set(key,card);$('tasks').append(card.root);}
|
|
72
|
+
updateWorkspaceTransferCard(card,t,{title:t.label,meta:(t.direction==='upload'?'上传':'下载')+' · '+status(t.status)+' · 租户 '+t.tenant+' · 知识库 '+t.collection,progress:bytes(t.transferred)+' / '+bytes(t.size),stats:bytes(t.transferred)+' / '+bytes(t.size)},transferBusy.has(key));
|
|
73
|
+
if(t.batches?.length&&!$('tasks-panel').hidden&&!card.ingestionBusy){
|
|
74
|
+
card.ingestionBusy=true;const detail=card.extra;
|
|
75
|
+
if(!detail.textContent)detail.textContent='已提交 '+t.batches.length+' 个入库批次';
|
|
76
|
+
api('/api/ingestion/'+encodeURIComponent(t.id),undefined,targetEnvironment).then(tasks=>{detail.textContent=tasks.map(x=>'入库:'+status(x.task_status||x.status)+' · 完成 '+(x.completed_count||0)+' / '+(x.total_count||0)).join(';');}).catch(()=>{detail.textContent='入库状态暂不可用,可稍后刷新。';}).finally(()=>{card.ingestionBusy=false;});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}catch(e){if(targetEnvironment===environment)$('tasks-connection').textContent=e.message;}finally{taskBusy=false;}
|
|
80
|
+
}
|
|
81
|
+
const loginWaits=new Map();
|
|
82
|
+
function loginEnvironment(id,explicit=false,resume=false){
|
|
83
|
+
if(loginWaits.has(id))return loginWaits.get(id);
|
|
84
|
+
const pending=(async()=>{
|
|
85
|
+
let state=await api('/api/login',undefined,id);
|
|
86
|
+
if(state.status==='idle'||state.status==='succeeded'&&!resume||explicit&&state.status!=='pending')state=await api('/api/login',{},id);
|
|
87
|
+
if(environment===id)notice('正在登录当前环境,请在浏览器完成认证,完成后自动返回知识库。');
|
|
88
|
+
while(state.status==='pending'&&!stopped){await new Promise(resolve=>setTimeout(resolve,500));state=await api('/api/login',undefined,id);}
|
|
89
|
+
if(stopped)return;
|
|
90
|
+
if(state.status!=='succeeded')throw new Error(state.code==='kb_identity_changed'?'登录账号已改变,请重启工作台。':state.code==='kb_auth_failed'?'登录已完成,但当前账号没有 KB 访问权限。':'登录未完成,请点击“登录当前环境”重试。');
|
|
91
|
+
if(environment===id)await selectEnvironment(id,false);
|
|
92
|
+
})().catch(error=>{if(environment===id)notice(error.message);}).finally(()=>{loginWaits.delete(id);});
|
|
93
|
+
loginWaits.set(id,pending);return pending;
|
|
94
|
+
}
|
|
95
|
+
$('environment-login').onclick=()=>run(()=>loginEnvironment(environment,true));
|
|
96
|
+
async function selectEnvironment(id,allowLogin=true){
|
|
97
|
+
environment=id;tenant='';epoch++;collection='';collections=[];clearDocument();$('documents').replaceChildren();$('collections').replaceChildren();$('tenant').replaceChildren();$('tasks').replaceChildren();transferCards.clear();$('tasks-count').textContent='0';$('heading').textContent='选择知识库';$('upload-file').disabled=true;$('upload-folder').disabled=true;
|
|
98
|
+
const chosen=environmentChoices.find(e=>e.id===id);$('environment-login').hidden=!chosen?.loginAvailable;notice('');
|
|
99
|
+
const version=epoch;let result;try{result=await api('/api/tenants',undefined,id);}catch(e){if(version!==epoch)return;const required=e.code==='kb_auth_required'||e.code==='kb_auth_failed'&&e.status===401;if(required&&chosen?.loginAvailable&&allowLogin&&autoLogin){await loginEnvironment(id);return;}notice(e.message);return;}if(version!==epoch)return;
|
|
100
|
+
for(const t of result.items){const option=node('option',t.name);option.value=t.tenant_id;$('tenant').append(option);}
|
|
101
|
+
tenant=result.items.some(t=>t.tenant_id===result.current_tenant_id)?result.current_tenant_id:result.items[0]?.tenant_id||'';$('tenant').value=tenant;
|
|
102
|
+
if(tenant)await loadCollections();else notice('当前身份没有可访问的租户。');await refreshTasks();
|
|
103
|
+
}
|
|
104
|
+
$('environment').onchange=()=>run(()=>selectEnvironment($('environment').value));
|
|
105
|
+
$('tenant').onchange=()=>run(async()=>{tenant=$('tenant').value;await loadCollections();});
|
|
106
|
+
$('refresh').onclick=()=>run(loadDocuments);$('search-form').onsubmit=e=>{e.preventDefault();offset=0;run(loadDocuments);};$('previous').onclick=()=>{offset=Math.max(0,offset-100);run(loadDocuments);};$('next').onclick=()=>{offset+=100;run(loadDocuments);};
|
|
107
|
+
for(const [id,folder] of [['upload-file',false],['upload-folder',true]])$(id).onclick=()=>run(async()=>{const targetEnvironment=environment,selection={tenant,collection,folder};$('upload-file').disabled=true;$('upload-folder').disabled=true;try{await api('/api/upload',selection,targetEnvironment);$('tasks-panel').hidden=false;await refreshTasks();}finally{const c=collections.find(c=>c.collection_id===collection);$('upload-file').disabled=c?.can_upload!==true;$('upload-folder').disabled=c?.can_upload!==true;}});
|
|
108
|
+
$('download').onclick=()=>run(async()=>{if(!selected)return;const targetEnvironment=environment,choice={tenant,collection,document:selected.document_id};await api('/api/download',choice,targetEnvironment);$('tasks-panel').hidden=false;await refreshTasks();});
|
|
109
|
+
$('delete').onclick=()=>{if(!selected)return;deleting={environment,tenant,document:selected.document_id};$('delete-message').textContent='删除“'+(selected.filename||selected.document_id)+'”? 环境:'+(environmentChoices.find(e=>e.id===environment)?.name||'当前默认配置')+';租户:'+tenant+'。';$('delete-dialog').showModal();};$('delete-cancel').onclick=()=>$('delete-dialog').close();$('delete-confirm').onclick=()=>run(async()=>{$('delete-confirm').disabled=true;try{await api('/api/delete',{tenant:deleting.tenant,document:deleting.document,confirmation:'DELETE_DOCUMENT'},deleting.environment);$('delete-dialog').close();await loadDocuments();}finally{$('delete-confirm').disabled=false;}});
|
|
110
|
+
$('tasks-toggle').onclick=()=>{$('tasks-panel').hidden=!$('tasks-panel').hidden;void refreshTasks();};$('tasks-close').onclick=()=>{$('tasks-panel').hidden=true;};
|
|
111
|
+
$('service-stop').onclick=()=>run(async()=>{if(!confirm('停止本地服务并暂停尚未完成的传输?已提交的入库任务将继续。'))return;await api('/api/shutdown',{confirmation:'STOP_SERVICE'});stopped=true;notice('本地服务已停止,可重新运行 numa kb web 继续管理文档。');});
|
|
112
|
+
run(async()=>{if(!token)throw new Error('请使用 CLI 打开的完整本地链接进入。');const result=await api('/api/environments');environmentChoices=result.items;autoLogin=result.autoLogin!==false;for(const e of result.items){const option=node('option',e.namespace||e.name);option.value=e.id;$('environment').append(option);}const initial=result.items.some(e=>e.id===entryFragment.get('environment'))?entryFragment.get('environment'):result.defaultEnvironment;$('environment').value=initial;if(loginReturn){environment=initial;await loginEnvironment(initial,false,true);}else await selectEnvironment(initial);});setInterval(()=>{void refreshTasks();},3000);
|
|
113
|
+
`;
|
|
114
|
+
//# sourceMappingURL=web-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-page.js","sourceRoot":"","sources":["../../src/kb/web-page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,MAAM,CAAC,MAAM,UAAU,GAAC,kBAAkB,GAAC,MAAM,CAAC,GAAG,CAAA;;CAEpD,CAAC;AACF,MAAM,CAAC,MAAM,WAAW,GAAC,MAAM,CAAC,GAAG,CAAA;;gUAE6R,gBAAgB;;;;;;;;;;;eAWjU,CAAC;AAChB,MAAM,CAAC,MAAM,SAAS,GAAC,MAAM,CAAC,GAAG,CAAA;EAC/B,qBAAqB;EACrB,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyFlB,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { PersistentTransferQueue, type BaseContext } from '../transfers/persistent-queue.js';
|
|
3
|
+
import { KbClient } from './client.js';
|
|
4
|
+
import { uploadToKb, downloadFromKb } from './transfers.js';
|
|
5
|
+
declare const jobSchema: z.ZodObject<{
|
|
6
|
+
id: z.ZodString;
|
|
7
|
+
direction: z.ZodEnum<{
|
|
8
|
+
download: "download";
|
|
9
|
+
upload: "upload";
|
|
10
|
+
}>;
|
|
11
|
+
tenant: z.ZodString;
|
|
12
|
+
collection: z.ZodString;
|
|
13
|
+
document: z.ZodString;
|
|
14
|
+
label: z.ZodString;
|
|
15
|
+
localPath: z.ZodString;
|
|
16
|
+
binding: z.ZodString;
|
|
17
|
+
size: z.ZodNumber;
|
|
18
|
+
transferred: z.ZodNumber;
|
|
19
|
+
status: z.ZodEnum<{
|
|
20
|
+
cancelled: "cancelled";
|
|
21
|
+
cancelling: "cancelling";
|
|
22
|
+
completed: "completed";
|
|
23
|
+
failed: "failed";
|
|
24
|
+
paused: "paused";
|
|
25
|
+
pausing: "pausing";
|
|
26
|
+
queued: "queued";
|
|
27
|
+
running: "running";
|
|
28
|
+
}>;
|
|
29
|
+
createdAt: z.ZodString;
|
|
30
|
+
updatedAt: z.ZodString;
|
|
31
|
+
error: z.ZodOptional<z.ZodString>;
|
|
32
|
+
phase: z.ZodString;
|
|
33
|
+
batches: z.ZodArray<z.ZodObject<{
|
|
34
|
+
batch_id: z.ZodString;
|
|
35
|
+
kind: z.ZodEnum<{
|
|
36
|
+
archive: "archive";
|
|
37
|
+
batch: "batch";
|
|
38
|
+
}>;
|
|
39
|
+
status: z.ZodString;
|
|
40
|
+
}, z.core.$strip>>;
|
|
41
|
+
}, z.core.$strict>;
|
|
42
|
+
type KbJob = z.infer<typeof jobSchema>;
|
|
43
|
+
interface Options {
|
|
44
|
+
directory: string;
|
|
45
|
+
client(tenant: string): Promise<KbClient>;
|
|
46
|
+
upload?: typeof uploadToKb;
|
|
47
|
+
download?: typeof downloadFromKb;
|
|
48
|
+
}
|
|
49
|
+
export declare class KbWebQueue extends PersistentTransferQueue<KbJob, BaseContext> {
|
|
50
|
+
private readonly kb;
|
|
51
|
+
constructor(kb: Options);
|
|
52
|
+
uploads(tenant: string, collection: string, paths: string[], signal?: AbortSignal): Promise<(import("../transfers/persistent-queue.js").PersistentJob & Record<string, unknown> & {
|
|
53
|
+
bytesPerSecond: number;
|
|
54
|
+
})[]>;
|
|
55
|
+
download(tenant: string, collection: string, document: string, folder: string): Promise<(import("../transfers/persistent-queue.js").PersistentJob & Record<string, unknown> & {
|
|
56
|
+
bytesPerSecond: number;
|
|
57
|
+
})[]>;
|
|
58
|
+
ingestion(id: string): Promise<import("./types.js").KbTask[]>;
|
|
59
|
+
private job;
|
|
60
|
+
}
|
|
61
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { lstat } from 'node:fs/promises';
|
|
3
|
+
import { basename, isAbsolute, join } from 'node:path';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { PersistentTransferQueue } from '../transfers/persistent-queue.js';
|
|
6
|
+
import { KbError } from './errors.js';
|
|
7
|
+
import { scanUploadFiles, uploadToKb, downloadFromKb } from './transfers.js';
|
|
8
|
+
import { readUploadState } from './upload-state.js';
|
|
9
|
+
const jobSchema = z.object({ id: z.string().uuid(), direction: z.enum(['upload', 'download']), tenant: z.string().min(1).max(128), collection: z.string().max(256), document: z.string().max(256), label: z.string().max(8192), localPath: z.string().max(8192), binding: z.string().regex(/^[a-f0-9]{64}$/), size: z.number().nonnegative().safe(), transferred: z.number().nonnegative(), status: z.enum(['queued', 'running', 'pausing', 'paused', 'cancelling', 'cancelled', 'completed', 'failed']), createdAt: z.string().datetime(), updatedAt: z.string().datetime(), error: z.string().max(2048).optional(), phase: z.string().max(40), batches: z.array(z.object({ batch_id: z.string(), kind: z.enum(['archive', 'batch']), status: z.string() })).max(10000) }).strict();
|
|
10
|
+
export class KbWebQueue extends PersistentTransferQueue {
|
|
11
|
+
kb;
|
|
12
|
+
constructor(kb) {
|
|
13
|
+
const statePath = (j) => join(kb.directory, `${j.id}.upload.json`);
|
|
14
|
+
const client = async (j) => { const c = await kb.client(j.tenant); if (c.binding !== j.binding)
|
|
15
|
+
throw new KbError('Target changed.', 'kb_state_binding_mismatch'); return c; };
|
|
16
|
+
super({ directory: kb.directory, maxConcurrent: 3, stateSchema: z.object({ version: z.literal(1), jobs: z.array(jobSchema).max(10000) }).strict(),
|
|
17
|
+
validate: j => { jobSchema.parse(j); if (!isAbsolute(j.localPath))
|
|
18
|
+
throw new Error('invalid local path'); },
|
|
19
|
+
context: (_j, base) => base,
|
|
20
|
+
conflicts: (a, b) => a.direction === b.direction && a.tenant === b.tenant && a.collection === b.collection && a.localPath === b.localPath,
|
|
21
|
+
view: j => { const { binding: _binding, ...view } = j; return view; },
|
|
22
|
+
safeError: e => e instanceof KbError && /^kb_[a-z_]+$/.test(e.code) ? e.code : 'kb_transfer_failed',
|
|
23
|
+
worker: { run: async (j, ctx) => {
|
|
24
|
+
const c = await client(j);
|
|
25
|
+
if (j.direction === 'download') {
|
|
26
|
+
const result = await (kb.download ?? downloadFromKb)(c, { documentId: j.document, output: j.localPath, signal: ctx.signal, onProgress: p => { j.phase = p.phase; ctx.progress(p.transferred ?? 0); } });
|
|
27
|
+
if (typeof result.size_bytes === 'number')
|
|
28
|
+
j.size = result.size_bytes;
|
|
29
|
+
j.phase = 'downloaded';
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const state = statePath(j);
|
|
33
|
+
const exists = await lstat(state).then(() => true, e => { if (e.code === 'ENOENT')
|
|
34
|
+
return false; throw e; });
|
|
35
|
+
const progress = new Map();
|
|
36
|
+
const result = await (kb.upload ?? uploadToKb)(c, { path: j.localPath, collectionId: j.collection, stateFile: state, resume: exists, signal: ctx.signal, concurrency: 3, onProgress: p => {
|
|
37
|
+
j.phase = p.phase;
|
|
38
|
+
if (p.filename && p.transferred !== undefined) {
|
|
39
|
+
progress.set(p.filename, p.transferred);
|
|
40
|
+
ctx.progress([...progress.values()].reduce((a, b) => a + b, 0));
|
|
41
|
+
}
|
|
42
|
+
} });
|
|
43
|
+
j.batches = result.batches;
|
|
44
|
+
j.phase = 'submitted';
|
|
45
|
+
await ctx.save();
|
|
46
|
+
}, cancel: async (j) => {
|
|
47
|
+
if (j.direction === 'download')
|
|
48
|
+
return;
|
|
49
|
+
const c = await client(j);
|
|
50
|
+
const path = statePath(j);
|
|
51
|
+
if (!await lstat(path).then(() => true, e => { if (e.code === 'ENOENT')
|
|
52
|
+
return false; throw e; }))
|
|
53
|
+
return;
|
|
54
|
+
const state = await readUploadState(path);
|
|
55
|
+
// Cancelling local transfers must never cancel or delete an already submitted ingestion job.
|
|
56
|
+
for (const batch of state.batches)
|
|
57
|
+
if (batch.sessionId) {
|
|
58
|
+
const session = await c.getUpload(batch.sessionId);
|
|
59
|
+
if (session.status === 'uploading')
|
|
60
|
+
await c.cancelUpload(batch.sessionId);
|
|
61
|
+
if (session.status === 'submitted')
|
|
62
|
+
j.batches.push({ batch_id: session.batch_id, kind: 'archive', status: 'submitted' });
|
|
63
|
+
}
|
|
64
|
+
j.phase = j.batches.length ? 'partially_submitted' : 'cancelled';
|
|
65
|
+
} }
|
|
66
|
+
});
|
|
67
|
+
this.kb = kb;
|
|
68
|
+
}
|
|
69
|
+
async uploads(tenant, collection, paths, signal) {
|
|
70
|
+
const c = await this.kb.client(tenant);
|
|
71
|
+
const jobs = [];
|
|
72
|
+
// Plan before enqueue: a bad selection never leaves half a queue behind.
|
|
73
|
+
for (const path of paths) {
|
|
74
|
+
const files = await scanUploadFiles(path, signal);
|
|
75
|
+
for (const file of files)
|
|
76
|
+
jobs.push(this.job(c, tenant, collection, '', file.path, file.size_bytes, 'upload'));
|
|
77
|
+
}
|
|
78
|
+
signal?.throwIfAborted();
|
|
79
|
+
return this.enqueue(jobs);
|
|
80
|
+
}
|
|
81
|
+
async download(tenant, collection, document, folder) {
|
|
82
|
+
const c = await this.kb.client(tenant), ticket = await c.downloadTicket(document);
|
|
83
|
+
if (!ticket.filename || basename(ticket.filename) !== ticket.filename || /[\\/\u0000-\u001f]/u.test(ticket.filename) || ['.', '..'].includes(ticket.filename))
|
|
84
|
+
throw new KbError('Invalid filename.', 'kb_input_invalid', 400);
|
|
85
|
+
return this.enqueue([this.job(c, tenant, collection, document, join(folder, ticket.filename), ticket.size_bytes ?? 0, 'download')]);
|
|
86
|
+
}
|
|
87
|
+
async ingestion(id) {
|
|
88
|
+
const job = this.jobs.find(j => j.id === id);
|
|
89
|
+
if (!job)
|
|
90
|
+
throw new KbError('Task not found.', 'kb_input_invalid', 404);
|
|
91
|
+
const c = await this.kb.client(job.tenant);
|
|
92
|
+
if (c.binding !== job.binding)
|
|
93
|
+
throw new KbError('Target changed.', 'kb_state_binding_mismatch');
|
|
94
|
+
return Promise.all(job.batches.map(b => c.getTask(b.batch_id, b.kind)));
|
|
95
|
+
}
|
|
96
|
+
job(c, tenant, collection, document, localPath, size, direction) {
|
|
97
|
+
const date = new Date().toISOString();
|
|
98
|
+
return { id: randomUUID(), tenant, collection, document, localPath, label: basename(localPath), binding: c.binding, size, transferred: 0, status: 'queued', createdAt: date, updatedAt: date, phase: 'queued', direction, batches: [] };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=web-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-queue.js","sourceRoot":"","sources":["../../src/kb/web-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAoB,MAAM,kCAAkC,CAAC;AAE7F,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,SAAS,GAAC,CAAC,CAAC,MAAM,CAAC,EAAC,EAAE,EAAC,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAC,SAAS,EAAC,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAC,UAAU,CAAC,CAAC,EAAC,MAAM,EAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAC,UAAU,EAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAC,QAAQ,EAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAC,KAAK,EAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAC,SAAS,EAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAC,OAAO,EAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAC,IAAI,EAAC,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,EAAC,WAAW,EAAC,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,EAAC,MAAM,EAAC,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAC,SAAS,EAAC,SAAS,EAAC,QAAQ,EAAC,YAAY,EAAC,WAAW,EAAC,WAAW,EAAC,QAAQ,CAAC,CAAC,EAAC,SAAS,EAAC,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAC,SAAS,EAAC,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAC,KAAK,EAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAC,KAAK,EAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAC,OAAO,EAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAC,QAAQ,EAAC,CAAC,CAAC,MAAM,EAAE,EAAC,IAAI,EAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAC,OAAO,CAAC,CAAC,EAAC,MAAM,EAAC,CAAC,CAAC,MAAM,EAAE,EAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAC,CAAC,CAAC,MAAM,EAAE,CAAC;AAGlsB,MAAM,OAAO,UAAW,SAAQ,uBAA0C;IAC5C,EAAE;IAA/B,YAA6B,EAAU;QACrC,MAAM,SAAS,GAAC,CAAC,CAAO,EAAC,EAAE,CAAA,IAAI,CAAC,EAAE,CAAC,SAAS,EAAC,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;QACpE,MAAM,MAAM,GAAC,KAAK,EAAC,CAAO,EAAC,EAAE,GAAC,MAAM,CAAC,GAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA,IAAG,CAAC,CAAC,OAAO,KAAG,CAAC,CAAC,OAAO;YAAC,MAAM,IAAI,OAAO,CAAC,iBAAiB,EAAC,2BAA2B,CAAC,CAAC,CAAA,OAAO,CAAC,CAAC,CAAA,CAAC,CAAC;QACrK,KAAK,CAAC,EAAC,SAAS,EAAC,EAAE,CAAC,SAAS,EAAC,aAAa,EAAC,CAAC,EAAC,WAAW,EAAC,CAAC,CAAC,MAAM,CAAC,EAAC,OAAO,EAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAC,IAAI,EAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAC,CAAC,CAAC,MAAM,EAAE;YACpI,QAAQ,EAAC,CAAC,CAAA,EAAE,GAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA,IAAG,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;gBAAC,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAA,CAAC;YACnG,OAAO,EAAC,CAAC,EAAE,EAAC,IAAI,EAAC,EAAE,CAAA,IAAI;YACvB,SAAS,EAAC,CAAC,CAAC,EAAC,CAAC,EAAC,EAAE,CAAA,CAAC,CAAC,SAAS,KAAG,CAAC,CAAC,SAAS,IAAE,CAAC,CAAC,MAAM,KAAG,CAAC,CAAC,MAAM,IAAE,CAAC,CAAC,UAAU,KAAG,CAAC,CAAC,UAAU,IAAE,CAAC,CAAC,SAAS,KAAG,CAAC,CAAC,SAAS;YACvH,IAAI,EAAC,CAAC,CAAA,EAAE,GAAC,MAAM,EAAC,OAAO,EAAC,QAAQ,EAAC,GAAG,IAAI,EAAC,GAAC,CAAC,CAAC,CAAA,OAAO,IAAI,CAAC,CAAA,CAAC;YACzD,SAAS,EAAC,CAAC,CAAA,EAAE,CAAA,CAAC,YAAY,OAAO,IAAE,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA,CAAC,CAAA,CAAC,CAAC,IAAI,CAAA,CAAC,CAAA,oBAAoB;YAC1F,MAAM,EAAC,EAAC,GAAG,EAAC,KAAK,EAAC,CAAC,EAAC,GAAG,EAAC,EAAE;oBACxB,MAAM,CAAC,GAAC,MAAM,MAAM,CAAC,CAAC,CAAC,CAAC;oBACxB,IAAG,CAAC,CAAC,SAAS,KAAG,UAAU,EAAE,CAAC;wBAC5B,MAAM,MAAM,GAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,IAAE,cAAc,CAAC,CAAC,CAAC,EAAC,EAAC,UAAU,EAAC,CAAC,CAAC,QAAQ,EAAC,MAAM,EAAC,CAAC,CAAC,SAAS,EAAC,MAAM,EAAC,GAAG,CAAC,MAAM,EAAC,UAAU,EAAC,CAAC,CAAA,EAAE,GAAC,CAAC,CAAC,KAAK,GAAC,CAAC,CAAC,KAAK,CAAC,CAAA,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,IAAE,CAAC,CAAC,CAAC,CAAA,CAAC,EAAC,CAAC,CAAC;wBACjL,IAAG,OAAO,MAAM,CAAC,UAAU,KAAG,QAAQ;4BAAC,CAAC,CAAC,IAAI,GAAC,MAAM,CAAC,UAAU,CAAC;wBAAA,CAAC,CAAC,KAAK,GAAC,YAAY,CAAC;wBAAA,OAAO;oBAC9F,CAAC;oBACD,MAAM,KAAK,GAAC,SAAS,CAAC,CAAC,CAAC,CAAC;oBAAA,MAAM,MAAM,GAAC,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAE,EAAE,CAAA,IAAI,EAAC,CAAC,CAAA,EAAE,GAAC,IAAG,CAAC,CAAC,IAAI,KAAG,QAAQ;wBAAC,OAAO,KAAK,CAAC,CAAA,MAAM,CAAC,CAAC,CAAA,CAAC,CAAC,CAAC;oBACxH,MAAM,QAAQ,GAAC,IAAI,GAAG,EAAiB,CAAC;oBACxC,MAAM,MAAM,GAAC,MAAM,CAAC,EAAE,CAAC,MAAM,IAAE,UAAU,CAAC,CAAC,CAAC,EAAC,EAAC,IAAI,EAAC,CAAC,CAAC,SAAS,EAAC,YAAY,EAAC,CAAC,CAAC,UAAU,EAAC,SAAS,EAAC,KAAK,EAAC,MAAM,EAAC,MAAM,EAAC,MAAM,EAAC,GAAG,CAAC,MAAM,EAAC,WAAW,EAAC,CAAC,EAAC,UAAU,EAAC,CAAC,CAAA,EAAE;4BACnK,CAAC,CAAC,KAAK,GAAC,CAAC,CAAC,KAAK,CAAC;4BAAA,IAAG,CAAC,CAAC,QAAQ,IAAE,CAAC,CAAC,WAAW,KAAG,SAAS,EAAC,CAAC;gCAAA,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAC,CAAC,CAAC,WAAW,CAAC,CAAC;gCAAA,GAAG,CAAC,QAAQ,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAC,CAAC,EAAC,EAAE,CAAA,CAAC,GAAC,CAAC,EAAC,CAAC,CAAC,CAAC,CAAC;4BAAA,CAAC;wBAC9J,CAAC,EAAC,CAAC,CAAC;oBACJ,CAAC,CAAC,OAAO,GAAC,MAAM,CAAC,OAAO,CAAC;oBAAA,CAAC,CAAC,KAAK,GAAC,WAAW,CAAC;oBAAA,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;gBAChE,CAAC,EAAC,MAAM,EAAC,KAAK,EAAC,CAAC,EAAC,EAAE;oBACjB,IAAG,CAAC,CAAC,SAAS,KAAG,UAAU;wBAAC,OAAO;oBACnC,MAAM,CAAC,GAAC,MAAM,MAAM,CAAC,CAAC,CAAC,CAAC;oBACxB,MAAM,IAAI,GAAC,SAAS,CAAC,CAAC,CAAC,CAAC;oBAAA,IAAG,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAE,EAAE,CAAA,IAAI,EAAC,CAAC,CAAA,EAAE,GAAC,IAAG,CAAC,CAAC,IAAI,KAAG,QAAQ;wBAAC,OAAO,KAAK,CAAC,CAAA,MAAM,CAAC,CAAC,CAAA,CAAC,CAAC;wBAAC,OAAO;oBACpH,MAAM,KAAK,GAAC,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;oBACxC,6FAA6F;oBAC7F,KAAI,MAAM,KAAK,IAAI,KAAK,CAAC,OAAO;wBAAE,IAAG,KAAK,CAAC,SAAS,EAAC,CAAC;4BAAA,MAAM,OAAO,GAAC,MAAM,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;4BAAA,IAAG,OAAO,CAAC,MAAM,KAAG,WAAW;gCAAC,MAAM,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;4BAAA,IAAG,OAAO,CAAC,MAAM,KAAG,WAAW;gCAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAC,OAAO,CAAC,QAAQ,EAAC,IAAI,EAAC,SAAS,EAAC,MAAM,EAAC,WAAW,EAAC,CAAC,CAAC;wBAAA,CAAC;oBAC5R,CAAC,CAAC,KAAK,GAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAA,CAAC,CAAA,qBAAqB,CAAA,CAAC,CAAA,WAAW,CAAC;gBAC7D,CAAC,EAAC;SACH,CAAC,CAAC;kBA9BwB,EAAE;IA+B/B,CAAC;IACD,KAAK,CAAC,OAAO,CAAC,MAAa,EAAC,UAAiB,EAAC,KAAc,EAAC,MAAmB;QAC9E,MAAM,CAAC,GAAC,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAAA,MAAM,IAAI,GAAS,EAAE,CAAC;QAC3D,yEAAyE;QACzE,KAAI,MAAM,IAAI,IAAI,KAAK,EAAC,CAAC;YAAA,MAAM,KAAK,GAAC,MAAM,eAAe,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC;YAAA,KAAI,MAAM,IAAI,IAAI,KAAK;gBAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAC,MAAM,EAAC,UAAU,EAAC,EAAE,EAAC,IAAI,CAAC,IAAI,EAAC,IAAI,CAAC,UAAU,EAAC,QAAQ,CAAC,CAAC,CAAC;QAAA,CAAC;QAChL,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,MAAa,EAAC,UAAiB,EAAC,QAAe,EAAC,MAAa;QAC1E,MAAM,CAAC,GAAC,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAAC,MAAM,GAAC,MAAM,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC7E,IAAG,CAAC,MAAM,CAAC,QAAQ,IAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAG,MAAM,CAAC,QAAQ,IAAE,qBAAqB,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAE,CAAC,GAAG,EAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC;YAAC,MAAM,IAAI,OAAO,CAAC,mBAAmB,EAAC,kBAAkB,EAAC,GAAG,CAAC,CAAC;QAClN,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAC,MAAM,EAAC,UAAU,EAAC,QAAQ,EAAC,IAAI,CAAC,MAAM,EAAC,MAAM,CAAC,QAAQ,CAAC,EAAC,MAAM,CAAC,UAAU,IAAE,CAAC,EAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC7H,CAAC;IACD,KAAK,CAAC,SAAS,CAAC,EAAS;QACvB,MAAM,GAAG,GAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA,EAAE,CAAA,CAAC,CAAC,EAAE,KAAG,EAAE,CAAC,CAAC;QAAA,IAAG,CAAC,GAAG;YAAC,MAAM,IAAI,OAAO,CAAC,iBAAiB,EAAC,kBAAkB,EAAC,GAAG,CAAC,CAAC;QAC3G,MAAM,CAAC,GAAC,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAAA,IAAG,CAAC,CAAC,OAAO,KAAG,GAAG,CAAC,OAAO;YAAC,MAAM,IAAI,OAAO,CAAC,iBAAiB,EAAC,2BAA2B,CAAC,CAAC;QACrI,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA,EAAE,CAAA,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IACO,GAAG,CAAC,CAAU,EAAC,MAAa,EAAC,UAAiB,EAAC,QAAe,EAAC,SAAgB,EAAC,IAAW,EAAC,SAA4B;QAC9H,MAAM,IAAI,GAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAAA,OAAO,EAAC,EAAE,EAAC,UAAU,EAAE,EAAC,MAAM,EAAC,UAAU,EAAC,QAAQ,EAAC,SAAS,EAAC,KAAK,EAAC,QAAQ,CAAC,SAAS,CAAC,EAAC,OAAO,EAAC,CAAC,CAAC,OAAO,EAAC,IAAI,EAAC,WAAW,EAAC,CAAC,EAAC,MAAM,EAAC,QAAQ,EAAC,SAAS,EAAC,IAAI,EAAC,SAAS,EAAC,IAAI,EAAC,KAAK,EAAC,QAAQ,EAAC,SAAS,EAAC,OAAO,EAAC,EAAE,EAAC,CAAC;IACrP,CAAC;CACD"}
|
package/dist/kb/web.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type LocalPicker } from '../oss/local-picker.js';
|
|
2
|
+
import { KbClient } from './client.js';
|
|
3
|
+
export interface KbWebEnvironment {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
namespace?: string;
|
|
7
|
+
endpoint: string;
|
|
8
|
+
client(): Promise<KbClient>;
|
|
9
|
+
login?: (returnUrl: string, signal: AbortSignal) => Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export interface KbWebOptions {
|
|
12
|
+
client: KbClient;
|
|
13
|
+
environments?: KbWebEnvironment[];
|
|
14
|
+
defaultEnvironment?: string;
|
|
15
|
+
directory?: string;
|
|
16
|
+
picker?: LocalPicker;
|
|
17
|
+
port?: number;
|
|
18
|
+
openBrowser?: boolean;
|
|
19
|
+
browserOpen?: (url: string) => Promise<unknown>;
|
|
20
|
+
}
|
|
21
|
+
export declare function startKbWeb(options: KbWebOptions): Promise<{
|
|
22
|
+
url: string;
|
|
23
|
+
browserOpened: boolean;
|
|
24
|
+
close: () => Promise<void>;
|
|
25
|
+
waitUntilClosed: () => Promise<void>;
|
|
26
|
+
}>;
|
package/dist/kb/web.js
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import open from 'open';
|
|
5
|
+
import { appConfigDirectory } from '../app-config.js';
|
|
6
|
+
import { localWebHeaders, strictLocalJson, requireLocalOrigin, requireLocalAuthorization } from '../local-web/security.js';
|
|
7
|
+
import { createLocalPicker } from '../oss/local-picker.js';
|
|
8
|
+
import { OssError } from '../oss/errors.js';
|
|
9
|
+
import { KbError, sanitizeKbOutput } from './errors.js';
|
|
10
|
+
import { KbWebQueue } from './web-queue.js';
|
|
11
|
+
import { needsKbLogin } from './web-login.js';
|
|
12
|
+
import { KB_WEB_HTML, KB_WEB_CSS, KB_WEB_JS } from './web-page.js';
|
|
13
|
+
const fail = (status = 400) => new KbError('Invalid request.', 'kb_input_invalid', status);
|
|
14
|
+
export async function startKbWeb(options) {
|
|
15
|
+
if (options.port !== undefined && (!Number.isInteger(options.port) || options.port < 0 || options.port > 65535))
|
|
16
|
+
throw fail();
|
|
17
|
+
const picker = options.picker ?? createLocalPicker();
|
|
18
|
+
const defaultEnvironment = options.defaultEnvironment ?? '';
|
|
19
|
+
const environments = options.environments ?? [{ id: defaultEnvironment, name: '当前默认配置', endpoint: options.client.baseUrl, client: async () => options.client }];
|
|
20
|
+
if (new Set(environments.map(e => e.id)).size !== environments.length || !environments.some(e => e.id === defaultEnvironment))
|
|
21
|
+
throw fail();
|
|
22
|
+
async function createContext(environment) {
|
|
23
|
+
const root = await environment.client(), subject = (await root.identity()).id;
|
|
24
|
+
const namespace = createHash('sha256').update(JSON.stringify(environment.id ? [environment.id, root.binding, subject] : [root.binding, subject])).digest('hex');
|
|
25
|
+
const directory = join(options.directory ?? appConfigDirectory(), 'kb-transfers', namespace);
|
|
26
|
+
async function tenants() { if ((await root.identity()).id !== subject)
|
|
27
|
+
throw new KbError('Identity changed; restart kb web.', 'kb_identity_changed', 409); return root.listTenants(); }
|
|
28
|
+
async function client(tenant) { if (!(await tenants()).items.some(x => x.tenant_id === tenant))
|
|
29
|
+
throw new KbError('Tenant forbidden.', 'kb_auth_failed', 403); return root.forTenant(tenant); }
|
|
30
|
+
const queue = new KbWebQueue({ directory, client });
|
|
31
|
+
await queue.initialize();
|
|
32
|
+
return { tenants, client, queue };
|
|
33
|
+
}
|
|
34
|
+
const contexts = new Map();
|
|
35
|
+
function context(id) {
|
|
36
|
+
const environment = environments.find(e => e.id === id);
|
|
37
|
+
if (!environment)
|
|
38
|
+
throw fail();
|
|
39
|
+
let pending = contexts.get(id);
|
|
40
|
+
if (!pending) {
|
|
41
|
+
pending = createContext(environment);
|
|
42
|
+
contexts.set(id, pending);
|
|
43
|
+
void pending.catch(() => { if (contexts.get(id) === pending)
|
|
44
|
+
contexts.delete(id); });
|
|
45
|
+
}
|
|
46
|
+
return pending;
|
|
47
|
+
}
|
|
48
|
+
async function closeQueues() { const results = await Promise.allSettled([...contexts.values()].map(async (c) => (await c).queue.close())); const failed = results.find(r => r.status === 'rejected'); if (failed?.status === 'rejected')
|
|
49
|
+
throw failed.reason; }
|
|
50
|
+
try {
|
|
51
|
+
await context(defaultEnvironment);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (!needsKbLogin(error) || !environments.find(e => e.id === defaultEnvironment)?.login)
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
const preparations = new AbortController();
|
|
58
|
+
const token = randomBytes(32).toString('base64url');
|
|
59
|
+
let origin = '', closed = false;
|
|
60
|
+
const logins = new Map();
|
|
61
|
+
let activeLogin;
|
|
62
|
+
function beginLogin(id) {
|
|
63
|
+
const environment = environments.find(e => e.id === id);
|
|
64
|
+
if (!environment?.login)
|
|
65
|
+
throw new KbError('Browser login unavailable.', 'kb_interactive_login_disabled', 400);
|
|
66
|
+
if (logins.get(id)?.status === 'pending')
|
|
67
|
+
return logins.get(id);
|
|
68
|
+
if (activeLogin)
|
|
69
|
+
throw new KbError('Another environment is logging in.', 'kb_login_busy', 409);
|
|
70
|
+
const state = { status: 'pending' };
|
|
71
|
+
logins.set(id, state);
|
|
72
|
+
const returnUrl = origin + '/#' + new URLSearchParams({ token, environment: id, login: 'return' });
|
|
73
|
+
activeLogin = (async () => {
|
|
74
|
+
try {
|
|
75
|
+
await environment.login(returnUrl, preparations.signal);
|
|
76
|
+
if (closed)
|
|
77
|
+
return;
|
|
78
|
+
// Existing queues retain their original subject; a different account must restart the workspace.
|
|
79
|
+
const contextAfterLogin = await context(id);
|
|
80
|
+
await contextAfterLogin.tenants();
|
|
81
|
+
state.status = 'succeeded';
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
state.status = 'failed';
|
|
85
|
+
state.code = error instanceof KbError && ['kb_identity_changed', 'kb_auth_failed'].includes(error.code) ? error.code : 'kb_login_failed';
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
activeLogin = undefined;
|
|
89
|
+
}
|
|
90
|
+
})();
|
|
91
|
+
return state;
|
|
92
|
+
}
|
|
93
|
+
let resolved;
|
|
94
|
+
const done = new Promise(r => { resolved = r; });
|
|
95
|
+
function json(res, status, data) { localWebHeaders(res, 'application/json; charset=utf-8'); res.writeHead(status); res.end(JSON.stringify(data)); }
|
|
96
|
+
const field = (body, name) => { if (typeof body[name] !== 'string' || !body[name] || String(body[name]).length > 500)
|
|
97
|
+
throw fail(); return body[name]; };
|
|
98
|
+
const server = createServer((req, res) => {
|
|
99
|
+
void (async () => {
|
|
100
|
+
const url = new URL(req.url ?? '/', origin);
|
|
101
|
+
requireLocalOrigin(req, origin, url.pathname === '/');
|
|
102
|
+
const assets = { '/': ['text/html; charset=utf-8', KB_WEB_HTML], '/app.css': ['text/css; charset=utf-8', KB_WEB_CSS], '/app.js': ['text/javascript; charset=utf-8', KB_WEB_JS] };
|
|
103
|
+
if (req.method === 'GET' && Object.hasOwn(assets, url.pathname)) {
|
|
104
|
+
const [type, body] = assets[url.pathname];
|
|
105
|
+
localWebHeaders(res, type);
|
|
106
|
+
res.end(body);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
requireLocalAuthorization(req, origin, token);
|
|
110
|
+
if (closed)
|
|
111
|
+
throw new KbError('Closing.', 'kb_web_closing', 503);
|
|
112
|
+
if (req.method === 'GET' && url.pathname === '/api/environments') {
|
|
113
|
+
json(res, 200, { ok: true, data: { defaultEnvironment, autoLogin: options.openBrowser !== false, items: environments.map(({ id, name, namespace, endpoint, login }) => ({ id, name, namespace, endpoint, loginAvailable: Boolean(login) })) } });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (url.pathname === '/api/login') {
|
|
117
|
+
const id = url.searchParams.get('environment') ?? defaultEnvironment;
|
|
118
|
+
if (!environments.some(e => e.id === id))
|
|
119
|
+
throw fail();
|
|
120
|
+
if (req.method === 'POST') {
|
|
121
|
+
await strictLocalJson(req, []);
|
|
122
|
+
json(res, 200, { ok: true, data: beginLogin(id) });
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (req.method === 'GET') {
|
|
126
|
+
json(res, 200, { ok: true, data: logins.get(id) ?? { status: 'idle' } });
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
throw fail(405);
|
|
130
|
+
}
|
|
131
|
+
if (req.method === 'POST' && url.pathname === '/api/shutdown') {
|
|
132
|
+
const body = await strictLocalJson(req, ['confirmation']);
|
|
133
|
+
if (body.confirmation !== 'STOP_SERVICE')
|
|
134
|
+
throw fail();
|
|
135
|
+
res.once('finish', () => { void close().catch(() => { process.stderr.write('任务记录保存失败,请检查本地任务目录。\n'); }); });
|
|
136
|
+
json(res, 200, { ok: true, data: { stopped: true } });
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const { tenants, client, queue } = await context(url.searchParams.get('environment') ?? defaultEnvironment);
|
|
140
|
+
let value;
|
|
141
|
+
if (req.method === 'GET' && url.pathname === '/api/tenants')
|
|
142
|
+
value = await tenants();
|
|
143
|
+
else if (req.method === 'GET' && url.pathname === '/api/collections')
|
|
144
|
+
value = await (await client(url.searchParams.get('tenant') ?? '')).listCollections();
|
|
145
|
+
else if (req.method === 'GET' && url.pathname === '/api/documents')
|
|
146
|
+
value = await (await client(url.searchParams.get('tenant') ?? '')).listDocuments(url.searchParams.get('collection') ?? '', Number(url.searchParams.get('offset') ?? 0), url.searchParams.get('q') ?? '');
|
|
147
|
+
else if (req.method === 'GET' && url.pathname === '/api/transfers') {
|
|
148
|
+
await tenants();
|
|
149
|
+
value = queue.list();
|
|
150
|
+
}
|
|
151
|
+
else if (req.method === 'GET' && url.pathname.startsWith('/api/ingestion/'))
|
|
152
|
+
value = await queue.ingestion(url.pathname.slice('/api/ingestion/'.length));
|
|
153
|
+
else if (req.method === 'POST' && url.pathname === '/api/upload') {
|
|
154
|
+
const body = await strictLocalJson(req, ['tenant', 'collection', 'folder']);
|
|
155
|
+
const tenant = field(body, 'tenant'), collection = field(body, 'collection');
|
|
156
|
+
if (typeof body.folder !== 'boolean')
|
|
157
|
+
throw fail();
|
|
158
|
+
const c = await client(tenant), collections = await c.listCollections();
|
|
159
|
+
if (!collections.collections.some(x => x.collection_id === collection && x.can_upload === true))
|
|
160
|
+
throw new KbError('Upload forbidden.', 'kb_auth_failed', 403);
|
|
161
|
+
const selected = body.folder ? await picker.pickFolder('upload') : await picker.pickFiles();
|
|
162
|
+
value = selected ? await queue.uploads(tenant, collection, Array.isArray(selected) ? selected : [selected], preparations.signal) : [];
|
|
163
|
+
}
|
|
164
|
+
else if (req.method === 'POST' && url.pathname === '/api/download') {
|
|
165
|
+
const body = await strictLocalJson(req, ['tenant', 'collection', 'document']);
|
|
166
|
+
const tenant = field(body, 'tenant'), collection = field(body, 'collection'), document = field(body, 'document');
|
|
167
|
+
await (await client(tenant)).downloadTicket(document);
|
|
168
|
+
const folder = await picker.pickFolder('download');
|
|
169
|
+
value = folder ? await queue.download(tenant, collection, document, folder) : [];
|
|
170
|
+
}
|
|
171
|
+
else if (req.method === 'POST' && url.pathname === '/api/delete') {
|
|
172
|
+
const body = await strictLocalJson(req, ['tenant', 'document', 'confirmation']);
|
|
173
|
+
if (body.confirmation !== 'DELETE_DOCUMENT')
|
|
174
|
+
throw fail();
|
|
175
|
+
await (await client(field(body, 'tenant'))).deleteDocument(field(body, 'document'));
|
|
176
|
+
value = { deleted: true };
|
|
177
|
+
}
|
|
178
|
+
else if (req.method === 'POST' && /^\/api\/transfers\/[^/]+\/(pause|resume|cancel)$/.test(url.pathname)) {
|
|
179
|
+
await tenants();
|
|
180
|
+
await strictLocalJson(req, []);
|
|
181
|
+
const [, , , id, action] = url.pathname.split('/');
|
|
182
|
+
await queue.action(id, action);
|
|
183
|
+
value = { ok: true };
|
|
184
|
+
}
|
|
185
|
+
else
|
|
186
|
+
throw fail(404);
|
|
187
|
+
// No tokens or signed tickets are returned by any local business endpoint.
|
|
188
|
+
json(res, 200, { ok: true, data: sanitizeKbOutput(value) });
|
|
189
|
+
})().catch(error => {
|
|
190
|
+
if (res.destroyed)
|
|
191
|
+
return;
|
|
192
|
+
if (res.headersSent) {
|
|
193
|
+
res.destroy();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const known = error instanceof KbError || error instanceof OssError;
|
|
197
|
+
const status = needsKbLogin(error) ? 401 : known && error.status && error.status >= 400 ? error.status : 502;
|
|
198
|
+
const code = known && /^(kb|oss)_[a-z_]+$/.test(error.code) ? error.code : 'kb_web_error';
|
|
199
|
+
if (code === 'oss_web_origin') {
|
|
200
|
+
json(res, status, { ok: false, error: { code, message: '请求来源校验未通过,请从 CLI 提供的本地工作台地址重新打开。' } });
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const messages = { kb_auth_unavailable: '暂时无法获取登录凭据,请检查身份源连接或配置;未自动重新登录。', kb_invalid_response: 'KB 接口返回格式与客户端不兼容,请更新客户端或报告此错误。', kb_network_error: '无法连接 KB 服务或请求超时,请稍后重试。', kb_auth_required: '当前环境尚未登录或登录已过期,请登录当前环境。', kb_login_busy: '另一个环境正在登录,请完成后再试。', kb_login_failed: '登录未完成,请重试登录。', kb_write_result_unknown: '服务端写入结果尚未确认,请先刷新核对,避免重复提交。', kb_identity_changed: '登录身份已改变,请重新启动工作台。' };
|
|
204
|
+
json(res, status, { ok: false, error: { code, message: messages[code] ?? (status === 401 ? '登录已失效,请在 CLI 重新登录后再试。' : status === 403 ? '当前身份没有此操作权限。' : status === 409 ? '状态已变化,请刷新后重试。' : '操作未完成,请根据错误码核对接口或任务状态。') } });
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
let closing;
|
|
208
|
+
function close() { return closing ??= (async () => { closed = true; preparations.abort(); picker.close?.(); try {
|
|
209
|
+
await activeLogin;
|
|
210
|
+
await closeQueues();
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
server.closeAllConnections();
|
|
214
|
+
await new Promise(resolve => server.close(() => resolve()));
|
|
215
|
+
resolved();
|
|
216
|
+
} })(); }
|
|
217
|
+
try {
|
|
218
|
+
await new Promise((resolve, reject) => { server.once('error', reject); server.listen(options.port ?? 0, '127.0.0.1', () => { server.off('error', reject); resolve(); }); });
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
await closeQueues();
|
|
222
|
+
throw e;
|
|
223
|
+
}
|
|
224
|
+
const address = server.address();
|
|
225
|
+
if (!address || typeof address === 'string') {
|
|
226
|
+
await close();
|
|
227
|
+
throw fail();
|
|
228
|
+
}
|
|
229
|
+
origin = `http://127.0.0.1:${address.port}`;
|
|
230
|
+
const url = `${origin}/#token=${token}`;
|
|
231
|
+
const browserOpened = options.openBrowser !== false && await (options.browserOpen ?? open)(url).then(() => true, () => false);
|
|
232
|
+
return { url, browserOpened, close, waitUntilClosed: () => done };
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=web.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/kb/web.ts"],"names":[],"mappings":"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"}
|