galaxy-opc-plugin 0.2.1 → 0.2.2
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/index.ts +244 -8
- package/package.json +17 -3
- package/src/__tests__/e2e/company-lifecycle.test.ts +399 -0
- package/src/__tests__/integration/business-workflows.test.ts +366 -0
- package/src/__tests__/test-utils.ts +316 -0
- package/src/commands/opc-command.ts +422 -0
- package/src/db/index.ts +3 -0
- package/src/db/migrations.test.ts +324 -0
- package/src/db/migrations.ts +131 -0
- package/src/db/schema.ts +211 -0
- package/src/db/sqlite-adapter.ts +5 -0
- package/src/opc/autonomy-rules.ts +132 -0
- package/src/opc/briefing-builder.ts +1331 -0
- package/src/opc/business-workflows.test.ts +535 -0
- package/src/opc/business-workflows.ts +325 -0
- package/src/opc/context-injector.ts +366 -28
- package/src/opc/event-triggers.ts +472 -0
- package/src/opc/intelligence-engine.ts +702 -0
- package/src/opc/milestone-detector.ts +251 -0
- package/src/opc/proactive-service.ts +179 -0
- package/src/opc/reminder-service.ts +4 -43
- package/src/opc/session-task-tracker.ts +60 -0
- package/src/opc/stage-detector.ts +168 -0
- package/src/opc/task-executor.ts +332 -0
- package/src/opc/task-templates.ts +179 -0
- package/src/tools/document-tool.ts +1176 -0
- package/src/tools/finance-tool.test.ts +238 -0
- package/src/tools/finance-tool.ts +922 -14
- package/src/tools/hr-tool.ts +10 -1
- package/src/tools/legal-tool.test.ts +251 -0
- package/src/tools/legal-tool.ts +26 -4
- package/src/tools/lifecycle-tool.test.ts +231 -0
- package/src/tools/media-tool.ts +156 -1
- package/src/tools/monitoring-tool.ts +134 -1
- package/src/tools/opc-tool.test.ts +250 -0
- package/src/tools/opc-tool.ts +251 -28
- package/src/tools/project-tool.test.ts +218 -0
- package/src/tools/schemas.ts +80 -0
- package/src/tools/search-tool.ts +227 -0
- package/src/tools/staff-tool.ts +395 -2
- package/src/web/config-ui.ts +299 -45
package/src/web/config-ui.ts
CHANGED
|
@@ -751,7 +751,33 @@ function getCss(): string {
|
|
|
751
751
|
+ "\n.view:not(.print-target){display:none!important}"
|
|
752
752
|
+ "\n.card{break-inside:avoid;box-shadow:none;border:1px solid #e5e7eb}"
|
|
753
753
|
+ "\n.stat-card{break-inside:avoid}"
|
|
754
|
-
+ "\n}"
|
|
754
|
+
+ "\n}"
|
|
755
|
+
// Feishu / Channel form styles
|
|
756
|
+
+ "\n.ch-form{display:grid;gap:20px;max-width:520px}"
|
|
757
|
+
+ "\n.ch-field{display:flex;flex-direction:column;gap:5px}"
|
|
758
|
+
+ "\n.ch-field label{font-size:13px;font-weight:600;color:var(--tx);display:flex;align-items:baseline;gap:6px}"
|
|
759
|
+
+ "\n.ch-field label .hint{font-weight:400;color:var(--tx3);font-size:12px}"
|
|
760
|
+
+ "\n.ch-field input,.ch-field select{padding:10px 14px;border:1.5px solid var(--bd);border-radius:8px;font-size:14px;font-family:var(--font);outline:none;transition:border-color .2s,box-shadow .2s;background:var(--card);color:var(--tx);width:100%}"
|
|
761
|
+
+ "\n.ch-field input:focus,.ch-field select:focus{border-color:var(--pri);box-shadow:0 0 0 3px rgba(15,23,42,.08)}"
|
|
762
|
+
+ "\n.ch-field input::placeholder{color:var(--tx3);font-size:13px}"
|
|
763
|
+
+ "\n.ch-field select{appearance:none;-webkit-appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 6l4 4 4-4' stroke='%239ca3af' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right 12px center;padding-right:36px;cursor:pointer}"
|
|
764
|
+
+ "\n.ch-status-dot{display:inline-block;width:10px;height:10px;border-radius:50%;flex-shrink:0}"
|
|
765
|
+
+ "\n.ch-status-row{display:flex;align-items:center;gap:10px;margin-bottom:16px}"
|
|
766
|
+
+ "\n.ch-status-row strong{font-size:15px}"
|
|
767
|
+
+ "\n.ch-info-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:16px}"
|
|
768
|
+
+ "\n.ch-info-item{background:var(--bg);border-radius:8px;padding:12px 14px}"
|
|
769
|
+
+ "\n.ch-info-item .ch-info-label{font-size:11px;color:var(--tx3);text-transform:uppercase;letter-spacing:0.04em;font-weight:500;margin-bottom:4px}"
|
|
770
|
+
+ "\n.ch-info-item .ch-info-value{font-size:14px;font-weight:500;color:var(--tx)}"
|
|
771
|
+
+ "\n.ch-guide-box{background:var(--bg);border-radius:10px;padding:24px;font-size:13px;line-height:2}"
|
|
772
|
+
+ "\n.ch-guide-box ol{padding-left:20px;margin:0}"
|
|
773
|
+
+ "\n.ch-guide-box li{margin-bottom:14px;padding-left:4px}"
|
|
774
|
+
+ "\n.ch-guide-box li strong{color:var(--tx)}"
|
|
775
|
+
+ "\n.ch-guide-box .step-hint{display:block;color:var(--tx3);font-size:12px;margin-top:2px}"
|
|
776
|
+
+ "\n.ch-guide-box code{background:#e8ecf1;padding:2px 8px;border-radius:5px;font-size:12px;font-family:'SF Mono',Consolas,monospace;color:var(--pri-l)}"
|
|
777
|
+
+ "\n.ch-section-title{font-size:17px;font-weight:600;display:flex;align-items:center;gap:8px;margin-bottom:16px}"
|
|
778
|
+
+ "\n.ch-future{margin-top:32px;padding-top:24px;border-top:1px solid var(--bd)}"
|
|
779
|
+
+ "\n.ch-future h2{font-size:15px;font-weight:600;color:var(--tx3);margin-bottom:6px}"
|
|
780
|
+
+ "\n.ch-future p{color:var(--tx3);font-size:13px}";
|
|
755
781
|
}
|
|
756
782
|
|
|
757
783
|
function getBodyHtml(): string {
|
|
@@ -768,6 +794,7 @@ function getBodyHtml(): string {
|
|
|
768
794
|
+ '<a data-view="canvas"><span class="icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="1" y="1" width="14" height="14" rx="1.5" stroke="currentColor" stroke-width="1.5"/><path d="M5 5h6M5 8h6M5 11h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><circle cx="12" cy="11" r="1.5" fill="currentColor"/></svg></span> ' + "OPB \u753B\u5E03" + '</a>'
|
|
769
795
|
+ '<a data-view="finance"><span class="icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12l3-4 3 2 4-6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2 14h12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></span> ' + "\u8D22\u52A1\u603B\u89C8" + '</a>'
|
|
770
796
|
+ '<a data-view="monitoring"><span class="icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="1.5"/><path d="M8 5v3l2 2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></span> ' + "\u76D1\u63A7\u4E2D\u5FC3" + '</a>'
|
|
797
|
+
+ '<a data-view="feishu"><span class="icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M1 8.5C2.5 5 5 3 7.5 2.5c-1 2-1.2 4-.2 6.5L10.5 13l-3.5-1.5C4.5 12.5 2 11.5 1 8.5z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/><circle cx="12.5" cy="5" r="2.5" stroke="currentColor" stroke-width="1.3"/><path d="M12.5 4v2M11.5 5h2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg></span> ' + "\u9891\u9053" + '</a>'
|
|
771
798
|
+ '<a data-view="tools"><span class="icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="3" stroke="currentColor" stroke-width="1.5"/><path d="M8 1v2M8 13v2M1 8h2M13 8h2M3.05 3.05l1.41 1.41M11.54 11.54l1.41 1.41M3.05 12.95l1.41-1.41M11.54 4.46l1.41-1.41" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></span> ' + "\u5DE5\u5177\u7BA1\u7406" + '</a>'
|
|
772
799
|
+ '<a data-view="closure"><span class="icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 1L14 4V8C14 11.3 11.3 14.3 8 15C4.7 14.3 2 11.3 2 8V4L8 1Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M5.5 8l1.5 1.5L10.5 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></span> ' + "\u8D44\u91D1\u95ED\u73AF" + '</a>'
|
|
773
800
|
+ '<a data-view="guide"><span class="icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 2h12v12H2z" stroke="currentColor" stroke-width="1.5" rx="1"/><path d="M5 5h6M5 8h6M5 11h4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></span> ' + "\u4F7F\u7528\u6307\u5357" + '</a>'
|
|
@@ -779,8 +806,8 @@ function getBodyHtml(): string {
|
|
|
779
806
|
+ '<div id="view-company-detail" class="view"><div id="company-detail-content"><div class="skeleton" style="height:200px"></div></div></div>'
|
|
780
807
|
+ '<div id="view-finance" class="view"><div class="page-header" style="display:flex;justify-content:space-between;align-items:flex-start"><div><h1>' + "\u8D22\u52A1\u603B\u89C8" + '</h1><p>' + "\u5E73\u53F0\u6574\u4F53\u8D22\u52A1\u6570\u636E\u5206\u6790" + '</p></div><button class="btn-pdf" onclick="printView(\'finance\')">🖶 \u5bfc\u51fa PDF</button></div><div id="finance-content"><div class="skeleton" style="height:200px"></div></div></div>'
|
|
781
808
|
+ '<div id="view-monitoring" class="view"><div class="page-header"><h1>' + "\u76D1\u63A7\u4E2D\u5FC3" + '</h1><p>' + "\u544A\u8B66\u7BA1\u7406\u4E0E\u8FD0\u8425\u6307\u6807\u76D1\u63A7" + '</p></div><div id="monitoring-content"><div class="skeleton" style="height:200px"></div></div></div>'
|
|
809
|
+
+ '<div id="view-feishu" class="view"><div class="page-header"><h1>' + "\u9891\u9053" + '</h1><p>' + "\u8FDE\u63A5\u98DE\u4E66\u3001\u5FAE\u4FE1\u7B49\u5E73\u53F0\uFF0C\u5728\u804A\u5929\u4E2D\u76F4\u63A5\u7BA1\u7406\u4F60\u7684\u516C\u53F8" + '</p></div><div id="feishu-content"><div class="skeleton" style="height:200px"></div></div></div>'
|
|
782
810
|
+ '<div id="view-tools" class="view"><div class="page-header"><h1>' + "\u5DE5\u5177\u7BA1\u7406" + '</h1><p>' + "\u542F\u7528\u3001\u914D\u7F6E\u5404\u529F\u80FD\u6A21\u5757\uFF0C\u81EA\u5B9A\u4E49\u63D0\u793A\u8BCD\u548C\u4F18\u5148\u7EA7" + '</p></div><div id="tool-list"></div>'
|
|
783
|
-
+ '<div id="webhook-section" class="card" style="margin-top:24px"><div class="card-header"><h3 style="margin:0">' + "\u544A\u8B66 Webhook \u63A8\u9001" + '</h3></div><div class="card-body"><p style="color:var(--tx2);font-size:14px;margin-bottom:12px">' + "\u652F\u6301\u98DE\u4E66\u548C\u4F01\u4E1A\u5FAE\u4FE1 Webhook\uFF0C\u5F53\u6709\u65B0\u544A\u8B66\u65F6\u5B9E\u65F6\u63A8\u9001\u901A\u77E5\u3002\u7559\u7A7A\u5219\u4E0D\u63A8\u9001\u3002" + '</p><div style="display:flex;gap:8px;align-items:center"><input id="webhook-url-input" type="url" class="form-input" placeholder="https://open.feishu.cn/..." style="flex:1"><button class="btn btn-primary" onclick="saveWebhookUrl()" style="white-space:nowrap">' + "\u4FDD\u5B58" + '</button><button class="btn" onclick="testWebhook()" style="white-space:nowrap">' + "\u6D4B\u8BD5" + '</button></div></div></div>'
|
|
784
811
|
+ '</div>'
|
|
785
812
|
+ '<div id="view-guide" class="view"><div id="guide-content"></div></div>'
|
|
786
813
|
+ '<div id="view-canvas" class="view"><div class="page-header"><h1>' + "OPB \u4E00\u4EBA\u4F01\u4E1A\u753B\u5E03" + '</h1><p>' + "\u57FA\u4E8E\u300A\u4E00\u4EBA\u4F01\u4E1A\u65B9\u6CD5\u8BBA 2.0\u300B\u7684 16 \u6A21\u5757\u6218\u7565\u753B\u5E03" + '</p></div><div style="margin-bottom:20px"><div style="position:relative;display:inline-block;min-width:260px"><select id="canvas-company-select" onchange="loadCanvas()" style="appearance:none;-webkit-appearance:none;width:100%;padding:10px 40px 10px 16px;font-size:14px;font-weight:500;color:var(--tx);background:var(--card);border:1.5px solid var(--bd);border-radius:8px;cursor:pointer;outline:none;font-family:var(--font);box-shadow:0 1px 3px rgba(0,0,0,.06)"><option value="">' + "\u2014\u00A0\u00A0\u9009\u62E9\u516C\u53F8\u00A0\u00A0\u2014" + '</option></select><span style="pointer-events:none;position:absolute;right:12px;top:50%;transform:translateY(-50%);color:var(--tx2)"><svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div></div><div id="canvas-content"></div></div>'
|
|
@@ -833,7 +860,7 @@ function getJs(): string {
|
|
|
833
860
|
+ "var companiesState={search:'',status:'',page:1};"
|
|
834
861
|
+ "var currentView='dashboard';"
|
|
835
862
|
+ "\nfunction esc(s){if(s===null||s===undefined)return '';s=String(s);return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\"/g,'"').replace(/'/g,''');}"
|
|
836
|
-
+ "\nfunction showView(name){currentView=name;document.querySelectorAll('.view').forEach(function(v){v.classList.remove('active')});document.querySelectorAll('.sidebar-nav a').forEach(function(a){a.classList.remove('active')});var el=document.getElementById('view-'+name);if(el)el.classList.add('active');var nav=document.querySelector('.sidebar-nav a[data-view=\"'+name+'\"]');if(nav)nav.classList.add('active');if(name==='dashboard')loadDashboard();if(name==='companies')loadCompanies();if(name==='finance')loadFinance();if(name==='monitoring')loadMonitoring();if(name==='tools')loadConfig();if(name==='guide')loadGuide();if(name==='canvas')initCanvasView();}"
|
|
863
|
+
+ "\nfunction showView(name){currentView=name;document.querySelectorAll('.view').forEach(function(v){v.classList.remove('active')});document.querySelectorAll('.sidebar-nav a').forEach(function(a){a.classList.remove('active')});var el=document.getElementById('view-'+name);if(el)el.classList.add('active');var nav=document.querySelector('.sidebar-nav a[data-view=\"'+name+'\"]');if(nav)nav.classList.add('active');if(name==='dashboard')loadDashboard();if(name==='companies')loadCompanies();if(name==='finance')loadFinance();if(name==='monitoring')loadMonitoring();if(name==='tools')loadConfig();if(name==='guide')loadGuide();if(name==='canvas')initCanvasView();if(name==='feishu')loadFeishu();}"
|
|
837
864
|
+ "\nfunction showToast(msg){var t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');setTimeout(function(){t.classList.remove('show')},2000);}"
|
|
838
865
|
+ "\nfunction fmt(n){if(n>=100000000)return(n/100000000).toFixed(2)+' \\u4ebf';if(n>=10000)return(n/10000).toFixed(1)+' \\u4e07';return n.toLocaleString();}"
|
|
839
866
|
+ "\nfunction fmtDate(s){if(!s)return '--';return s.slice(0,10);}"
|
|
@@ -1585,12 +1612,7 @@ function getJs(): string {
|
|
|
1585
1612
|
+ "h+='</div>';});"
|
|
1586
1613
|
+ "h+='</div>';"
|
|
1587
1614
|
+ "list.innerHTML=h;"
|
|
1588
|
-
// ── Webhook URL section — only update the input value ──
|
|
1589
|
-
+ "var wurl=toolConfig['webhook_url']||'';"
|
|
1590
|
-
+ "var winput=document.getElementById('webhook-url-input');if(winput)winput.value=wurl;"
|
|
1591
1615
|
+ "}"
|
|
1592
|
-
+ "\nfunction saveWebhookUrl(){var v=document.getElementById('webhook-url-input').value.trim();toolConfig['webhook_url']=v;saveConfig(function(){showToast(v?'Webhook \\u5df2\\u4fdd\\u5b58':'\\u5df2\\u6e05\\u9664 Webhook');});}"
|
|
1593
|
-
+ "\nfunction testWebhook(){var v=document.getElementById('webhook-url-input').value.trim();if(!v){showToast('\\u8bf7\\u5148\\u8f93\\u5165 Webhook \\u5730\\u5740');return;}fetch('/opc/admin/api/webhook-test',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url:v})}).then(function(r){return r.json()}).then(function(d){showToast(d.ok?'\\u6d4b\\u8bd5\\u6210\\u529f\\uff0c\\u8bf7\\u67e5\\u770b\\u673a\\u5668\\u4eba\\u6d88\\u606f':'\\u6d4b\\u8bd5\\u5931\\u8d25: '+(d.error||''));}).catch(function(){showToast('\\u8bf7\\u6c42\\u5f02\\u5e38');});}"
|
|
1594
1616
|
+ "\nfunction toggleTool(key,enabled){toolConfig[key]=enabled?'enabled':'disabled';saveConfig(function(){showToast((enabled?'\\u5df2\\u542f\\u7528':'\\u5df2\\u7981\\u7528')+' '+key);renderTools();});}"
|
|
1595
1617
|
+ "\nfunction saveToolField(key,field,value){toolConfig[key+'_'+field]=value;saveConfig(function(){showToast('\\u5df2\\u4fdd\\u5b58 '+key+' '+field);});}"
|
|
1596
1618
|
+ "\nfunction toggleToolSettings(key){var el=document.getElementById('tsettings-'+key);if(el)el.classList.toggle('open');}"
|
|
@@ -1757,10 +1779,180 @@ function getSkillsJs(): string {
|
|
|
1757
1779
|
+ "else{showToast('\u5220\u9664\u5931\u8d25: '+(d.error||'\u672a\u77e5\u9519\u8bef'));}})"
|
|
1758
1780
|
+ ".catch(function(){showToast('\u8bf7\u6c42\u5931\u8d25');});}"
|
|
1759
1781
|
// ── hash routing ──
|
|
1760
|
-
+ "\nfunction handleHash(){var hash=window.location.hash.replace('#','');if(!hash||hash==='dashboard'){showView('dashboard');return;}if(hash==='companies'){showView('companies');return;}if(hash==='finance'){showView('finance');return;}if(hash==='monitoring'){showView('monitoring');return;}if(hash==='tools'){showView('tools');return;}if(hash==='closure'){showView('closure');loadClosure();return;}if(hash==='guide'){showView('guide');return;}if(hash==='canvas'){showView('canvas');return;}if(hash.indexOf('company/')===0){var cid=hash.slice(8);showCompany(cid);return;}showView('dashboard');}"
|
|
1782
|
+
+ "\nfunction handleHash(){var hash=window.location.hash.replace('#','');if(!hash||hash==='dashboard'){showView('dashboard');return;}if(hash==='companies'){showView('companies');return;}if(hash==='finance'){showView('finance');return;}if(hash==='monitoring'){showView('monitoring');return;}if(hash==='tools'){showView('tools');return;}if(hash==='closure'){showView('closure');loadClosure();return;}if(hash==='guide'){showView('guide');return;}if(hash==='canvas'){showView('canvas');return;}if(hash==='feishu'){showView('feishu');return;}if(hash.indexOf('company/')===0){var cid=hash.slice(8);showCompany(cid);return;}showView('dashboard');}"
|
|
1783
|
+
+ getFeishuJs()
|
|
1761
1784
|
+ getClosureJs();
|
|
1762
1785
|
}
|
|
1763
1786
|
|
|
1787
|
+
function getFeishuJs(): string {
|
|
1788
|
+
return ""
|
|
1789
|
+
+ "\nfunction loadFeishu(){"
|
|
1790
|
+
+ "var el=document.getElementById('feishu-content');"
|
|
1791
|
+
+ "el.innerHTML='<div class=\"skeleton\" style=\"height:200px\"></div>';"
|
|
1792
|
+
+ "Promise.all(["
|
|
1793
|
+
+ " fetch('/opc/admin/api/feishu/status').then(function(r){return r.json()}),"
|
|
1794
|
+
+ " fetch('/opc/admin/api/feishu/pairing').then(function(r){return r.json()})"
|
|
1795
|
+
+ "]).then(function(results){"
|
|
1796
|
+
+ "var status=results[0];var pairing=results[1];"
|
|
1797
|
+
+ "renderFeishuPage(status,pairing);"
|
|
1798
|
+
+ "}).catch(function(err){el.innerHTML='<div class=\"card\"><div class=\"card-body\"><p style=\"color:var(--err)\">\\u52a0\\u8f7d\\u5931\\u8d25: '+err.message+'</p></div></div>';});}"
|
|
1799
|
+
|
|
1800
|
+
// renderFeishuPage
|
|
1801
|
+
+ "\nfunction renderFeishuPage(status,pairing){"
|
|
1802
|
+
+ "var el=document.getElementById('feishu-content');"
|
|
1803
|
+
+ "var h='';"
|
|
1804
|
+
+ "var dmLabels={'pairing':'\\u9700\\u8981\\u914d\\u5bf9\\u624d\\u80fd\\u79c1\\u804a','open':'\\u4efb\\u4f55\\u4eba\\u90fd\\u53ef\\u4ee5\\u79c1\\u804a'};"
|
|
1805
|
+
|
|
1806
|
+
// 1. Feishu section header
|
|
1807
|
+
+ "h+='<h2 class=\"ch-section-title\">';"
|
|
1808
|
+
+ "h+='<svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\"><path d=\"M3 10.5C5 6 8 3.5 11 3c-1.2 2.5-1.5 5-.5 8L14.5 16l-5-2C6.5 15.5 3.5 14 3 10.5z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/></svg>';"
|
|
1809
|
+
+ "h+='\\u98de\\u4e66</h2>';"
|
|
1810
|
+
|
|
1811
|
+
// 2. Connection status card
|
|
1812
|
+
+ "h+='<div class=\"card\">';"
|
|
1813
|
+
+ "h+='<h3>\\u8fde\\u63a5\\u72b6\\u6001</h3>';"
|
|
1814
|
+
+ "if(status.configured){"
|
|
1815
|
+
+ "h+='<div class=\"ch-status-row\">';"
|
|
1816
|
+
+ "h+='<span class=\"ch-status-dot\" style=\"background:'+(status.enabled?'var(--ok)':'var(--err)') +'\"></span>';"
|
|
1817
|
+
+ "h+='<strong>'+(status.enabled?'\\u5df2\\u8fde\\u63a5':'\\u5df2\\u914d\\u7f6e\\uff0c\\u7b49\\u5f85\\u91cd\\u542f\\u540e\\u751f\\u6548')+'</strong></div>';"
|
|
1818
|
+
+ "h+='<div class=\"ch-info-grid\">';"
|
|
1819
|
+
+ "h+='<div class=\"ch-info-item\"><div class=\"ch-info-label\">\\u5e94\\u7528 ID</div><div class=\"ch-info-value\"><code>'+esc(status.appId)+'</code></div></div>';"
|
|
1820
|
+
+ "h+='<div class=\"ch-info-item\"><div class=\"ch-info-label\">\\u673a\\u5668\\u4eba\\u540d\\u79f0</div><div class=\"ch-info-value\">'+esc(status.botName)+'</div></div>';"
|
|
1821
|
+
+ "h+='<div class=\"ch-info-item\"><div class=\"ch-info-label\">\\u79c1\\u804a\\u6743\\u9650</div><div class=\"ch-info-value\">'+(dmLabels[status.dmPolicy]||esc(status.dmPolicy))+'</div></div>';"
|
|
1822
|
+
+ "h+='<div class=\"ch-info-item\"><div class=\"ch-info-label\">\\u6d41\\u5f0f\\u56de\\u590d</div><div class=\"ch-info-value\">'+(status.streaming?'\\u5df2\\u5f00\\u542f':'\\u5df2\\u5173\\u95ed')+'</div></div>';"
|
|
1823
|
+
+ "h+='</div>';"
|
|
1824
|
+
+ "}else{"
|
|
1825
|
+
+ "h+='<div class=\"ch-status-row\">';"
|
|
1826
|
+
+ "h+='<span class=\"ch-status-dot\" style=\"background:var(--tx3)\"></span>';"
|
|
1827
|
+
+ "h+='<strong>\\u672a\\u914d\\u7f6e</strong></div>';"
|
|
1828
|
+
|
|
1829
|
+
// ── Detailed step-by-step guide for non-technical users ──
|
|
1830
|
+
+ "h+='<div class=\"ch-guide-box\">';"
|
|
1831
|
+
+ "h+='<p style=\"font-weight:600;font-size:14px;margin-bottom:14px\">\\u6309\\u4ee5\\u4e0b\\u6b65\\u9aa4\\u914d\\u7f6e\\u98de\\u4e66\\u673a\\u5668\\u4eba\\uff0c\\u5b8c\\u6210\\u540e\\u4f60\\u5c31\\u53ef\\u4ee5\\u5728\\u98de\\u4e66\\u4e2d\\u76f4\\u63a5\\u548c AI \\u52a9\\u624b\\u5bf9\\u8bdd\\u3001\\u7ba1\\u7406\\u516c\\u53f8\\uff1a</p>';"
|
|
1832
|
+
+ "h+='<ol>';"
|
|
1833
|
+
|
|
1834
|
+
// Step 1
|
|
1835
|
+
+ "h+='<li><strong>\\u6253\\u5f00\\u98de\\u4e66\\u5f00\\u653e\\u5e73\\u53f0</strong><br>';"
|
|
1836
|
+
+ "h+='\\u7528\\u6d4f\\u89c8\\u5668\\u8bbf\\u95ee <a href=\"https://open.feishu.cn\" target=\"_blank\" style=\"color:var(--pri);font-weight:500\">open.feishu.cn</a>\\uff0c\\u767b\\u5f55\\u4f60\\u7684\\u98de\\u4e66\\u8d26\\u53f7\\u3002';"
|
|
1837
|
+
+ "h+='<span class=\"step-hint\">\\u63d0\\u793a\\uff1a\\u5982\\u679c\\u4f60\\u7684\\u98de\\u4e66\\u662f\\u4e2a\\u4eba\\u7248\\uff0c\\u53ef\\u4ee5\\u76f4\\u63a5\\u4f7f\\u7528\\uff1b\\u4f01\\u4e1a\\u7248\\u9700\\u8981\\u7ba1\\u7406\\u5458\\u6743\\u9650\\u3002</span></li>';"
|
|
1838
|
+
|
|
1839
|
+
// Step 2
|
|
1840
|
+
+ "h+='<li><strong>\\u521b\\u5efa\\u5e94\\u7528</strong><br>';"
|
|
1841
|
+
+ "h+='\\u767b\\u5f55\\u540e\\u70b9\\u51fb\\u9875\\u9762\\u4e2d\\u95f4\\u7684\\u300c<strong>\\u521b\\u5efa\\u81ea\\u5efa\\u5e94\\u7528</strong>\\u300d\\u6309\\u94ae\\u3002';"
|
|
1842
|
+
+ "h+='<br>\\u586b\\u5199\\u5e94\\u7528\\u540d\\u79f0\\uff08\\u6bd4\\u5982\\u300c\\u661f\\u73afOPC\\u52a9\\u624b\\u300d\\uff09\\uff0c\\u63cf\\u8ff0\\u53ef\\u4ee5\\u968f\\u4fbf\\u586b\\uff0c\\u7136\\u540e\\u70b9\\u300c\\u521b\\u5efa\\u300d\\u3002</li>';"
|
|
1843
|
+
|
|
1844
|
+
// Step 3
|
|
1845
|
+
+ "h+='<li><strong>\\u590d\\u5236 App ID \\u548c App Secret</strong><br>';"
|
|
1846
|
+
+ "h+='\\u521b\\u5efa\\u5b8c\\u6210\\u540e\\u4f1a\\u8fdb\\u5165\\u5e94\\u7528\\u7684\\u7ba1\\u7406\\u9875\\u9762\\u3002';"
|
|
1847
|
+
+ "h+='<br>\\u5728\\u5de6\\u4fa7\\u83dc\\u5355\\u627e\\u5230\\u300c<strong>\\u51ed\\u8bc1\\u4e0e\\u57fa\\u7840\\u4fe1\\u606f</strong>\\u300d\\uff0c\\u4f60\\u4f1a\\u770b\\u5230 <strong>App ID</strong> \\u548c <strong>App Secret</strong>\\u3002';"
|
|
1848
|
+
+ "h+='<br>\\u70b9\\u51fb\\u590d\\u5236\\u6309\\u94ae\\uff0c\\u628a\\u5b83\\u4eec\\u7c98\\u8d34\\u5230\\u4e0b\\u65b9\\u7684\\u8868\\u5355\\u4e2d\\u3002';"
|
|
1849
|
+
+ "h+='<span class=\"step-hint\">App Secret \\u70b9\\u51fb\\u300c\\u663e\\u793a\\u300d\\u540e\\u624d\\u80fd\\u590d\\u5236\\uff0c\\u5982\\u679c\\u770b\\u4e0d\\u5230\\u8bf7\\u70b9\\u300c\\u91cd\\u7f6e\\u300d\\u751f\\u6210\\u65b0\\u7684\\u3002</span></li>';"
|
|
1850
|
+
|
|
1851
|
+
// Step 4
|
|
1852
|
+
+ "h+='<li><strong>\\u5f00\\u542f\\u673a\\u5668\\u4eba\\u80fd\\u529b</strong><br>';"
|
|
1853
|
+
+ "h+='\\u5728\\u5de6\\u4fa7\\u83dc\\u5355\\u627e\\u5230\\u300c<strong>\\u6dfb\\u52a0\\u5e94\\u7528\\u80fd\\u529b</strong>\\u300d\\uff0c\\u627e\\u5230\\u300c<strong>\\u673a\\u5668\\u4eba</strong>\\u300d\\uff0c\\u70b9\\u51fb\\u300c\\u5f00\\u542f\\u300d\\u3002';"
|
|
1854
|
+
+ "h+='<span class=\"step-hint\">\\u8fd9\\u6837\\u4f60\\u7684\\u5e94\\u7528\\u624d\\u80fd\\u5728\\u98de\\u4e66\\u4e2d\\u63a5\\u6536\\u548c\\u53d1\\u9001\\u6d88\\u606f\\u3002</span></li>';"
|
|
1855
|
+
|
|
1856
|
+
// Step 5
|
|
1857
|
+
+ "h+='<li><strong>\\u914d\\u7f6e\\u4e8b\\u4ef6\\u8ba2\\u9605\\uff08\\u63a5\\u6536\\u7528\\u6237\\u6d88\\u606f\\uff09</strong><br>';"
|
|
1858
|
+
+ "h+='\\u5728\\u5de6\\u4fa7\\u83dc\\u5355\\u627e\\u5230\\u300c<strong>\\u4e8b\\u4ef6\\u4e0e\\u56de\\u8c03</strong>\\u300d\\u3002';"
|
|
1859
|
+
+ "h+='<br>\\u5728\\u300c\\u4e8b\\u4ef6\\u8ba2\\u9605\\u300d\\u680f\\u70b9\\u300c<strong>\\u6dfb\\u52a0\\u4e8b\\u4ef6</strong>\\u300d\\uff0c\\u641c\\u7d22\\u5e76\\u6dfb\\u52a0\\uff1a<code>im.message.receive_v1</code>';"
|
|
1860
|
+
+ "h+='<br>\\u5728\\u4e0a\\u65b9\\u7684\\u300c\\u8bf7\\u6c42\\u5730\\u5740\\u300d\\u586b\\u5165\\u4e8b\\u4ef6\\u56de\\u8c03\\u5730\\u5740\\uff08\\u542f\\u52a8 OpenClaw \\u65f6\\u63a7\\u5236\\u53f0\\u4f1a\\u6253\\u5370\\u8fd9\\u4e2a\\u5730\\u5740\\uff0c\\u683c\\u5f0f\\u7c7b\\u4f3c\\uff1a<code>https://xxx.xxx/feishu/event</code>\\uff09\\u3002';"
|
|
1861
|
+
+ "h+='<span class=\"step-hint\">\\u5982\\u679c\\u8fd8\\u6ca1\\u542f\\u52a8 OpenClaw\\uff0c\\u53ef\\u4ee5\\u5148\\u8df3\\u8fc7\\u8fd9\\u6b65\\uff0c\\u5148\\u586b\\u5199\\u4e0b\\u65b9\\u8868\\u5355\\u4fdd\\u5b58\\uff0c\\u4e0b\\u6b21\\u542f\\u52a8\\u65f6\\u4f1a\\u663e\\u793a\\u5730\\u5740\\u3002</span></li>';"
|
|
1862
|
+
|
|
1863
|
+
// Step 6
|
|
1864
|
+
+ "h+='<li><strong>\\u914d\\u7f6e\\u6743\\u9650</strong><br>';"
|
|
1865
|
+
+ "h+='\\u5728\\u5de6\\u4fa7\\u83dc\\u5355\\u627e\\u5230\\u300c<strong>\\u6743\\u9650\\u7ba1\\u7406</strong>\\u300d\\uff0c\\u70b9\\u300c<strong>\\u5f00\\u901a\\u6743\\u9650</strong>\\u300d\\uff0c\\u641c\\u7d22\\u5e76\\u6dfb\\u52a0\\u4ee5\\u4e0b\\u6743\\u9650\\uff1a';"
|
|
1866
|
+
+ "h+='<br><code>im:message</code>\\uff08\\u83b7\\u53d6\\u4e0e\\u53d1\\u9001\\u6d88\\u606f\\uff09';"
|
|
1867
|
+
+ "h+='<br><code>im:message:send_as_bot</code>\\uff08\\u4ee5\\u673a\\u5668\\u4eba\\u8eab\\u4efd\\u53d1\\u6d88\\u606f\\uff09';"
|
|
1868
|
+
+ "h+='<span class=\"step-hint\">\\u6dfb\\u52a0\\u540e\\u70b9\\u300c\\u6279\\u91cf\\u5f00\\u901a\\u300d\\uff0c\\u4f01\\u4e1a\\u7248\\u53ef\\u80fd\\u9700\\u7ba1\\u7406\\u5458\\u5ba1\\u6279\\u3002</span></li>';"
|
|
1869
|
+
|
|
1870
|
+
// Step 7
|
|
1871
|
+
+ "h+='<li><strong>\\u53d1\\u5e03\\u5e94\\u7528</strong><br>';"
|
|
1872
|
+
+ "h+='\\u5728\\u5de6\\u4fa7\\u83dc\\u5355\\u627e\\u5230\\u300c<strong>\\u5e94\\u7528\\u53d1\\u5e03</strong>\\u300d\\uff0c\\u70b9\\u300c<strong>\\u521b\\u5efa\\u7248\\u672c</strong>\\u300d\\uff0c\\u586b\\u5199\\u7248\\u672c\\u53f7\\uff08\\u5982 1.0.0\\uff09\\u548c\\u66f4\\u65b0\\u8bf4\\u660e\\uff0c\\u70b9\\u300c\\u4fdd\\u5b58\\u300d\\u3002';"
|
|
1873
|
+
+ "h+='<br>\\u7136\\u540e\\u70b9\\u300c\\u7533\\u8bf7\\u53d1\\u5e03\\u300d\\u3002\\u4e2a\\u4eba\\u7248\\u76f4\\u63a5\\u751f\\u6548\\uff0c\\u4f01\\u4e1a\\u7248\\u9700\\u7ba1\\u7406\\u5458\\u5ba1\\u6838\\u3002';"
|
|
1874
|
+
+ "h+='<span class=\"step-hint\">\\u53d1\\u5e03\\u540e\\uff0c\\u5728\\u98de\\u4e66\\u641c\\u7d22\\u4f60\\u7684\\u673a\\u5668\\u4eba\\u540d\\u79f0\\u5c31\\u80fd\\u627e\\u5230\\u5b83\\uff0c\\u53d1\\u6d88\\u606f\\u5c31\\u80fd\\u548c AI \\u52a9\\u624b\\u5bf9\\u8bdd\\u4e86\\u3002</span></li>';"
|
|
1875
|
+
|
|
1876
|
+
// Step 8
|
|
1877
|
+
+ "h+='<li><strong>\\u586b\\u5199\\u4e0b\\u65b9\\u8868\\u5355\\u5e76\\u4fdd\\u5b58</strong><br>';"
|
|
1878
|
+
+ "h+='\\u628a\\u590d\\u5236\\u7684 App ID \\u548c App Secret \\u586b\\u5165\\u4e0b\\u65b9\\u7684\\u300c\\u98de\\u4e66\\u5e94\\u7528\\u914d\\u7f6e\\u300d\\u8868\\u5355\\uff0c\\u70b9\\u300c\\u4fdd\\u5b58\\u914d\\u7f6e\\u300d\\u5373\\u53ef\\u3002</li>';"
|
|
1879
|
+
|
|
1880
|
+
+ "h+='</ol></div>';"
|
|
1881
|
+
+ "}"
|
|
1882
|
+
+ "h+='</div>';"
|
|
1883
|
+
|
|
1884
|
+
// 3. Configuration form card
|
|
1885
|
+
+ "h+='<div class=\"card\">';"
|
|
1886
|
+
+ "h+='<h3>\\u98de\\u4e66\\u5e94\\u7528\\u914d\\u7f6e</h3>';"
|
|
1887
|
+
+ "h+='<div class=\"ch-form\">';"
|
|
1888
|
+
+ "h+='<div class=\"ch-field\"><label>App ID <span class=\"hint\">\\u5728\\u98de\\u4e66\\u5f00\\u653e\\u5e73\\u53f0\\u300c\\u51ed\\u8bc1\\u4e0e\\u57fa\\u7840\\u4fe1\\u606f\\u300d\\u4e2d\\u590d\\u5236</span></label><input id=\"feishu-app-id\" type=\"text\" placeholder=\"cli_xxxxxxxxxx\"></div>';"
|
|
1889
|
+
+ "h+='<div class=\"ch-field\"><label>App Secret <span class=\"hint\">\\u540c\\u4e0a\\uff0c\\u70b9\\u300c\\u663e\\u793a\\u300d\\u540e\\u590d\\u5236</span></label><input id=\"feishu-app-secret\" type=\"password\" placeholder=\"\\u70b9\\u51fb\\u8f93\\u5165\"></div>';"
|
|
1890
|
+
+ "h+='<div class=\"ch-field\"><label>\\u673a\\u5668\\u4eba\\u540d\\u79f0 <span class=\"hint\">\\u663e\\u793a\\u5728\\u98de\\u4e66\\u804a\\u5929\\u4e2d\\u7684\\u540d\\u5b57</span></label><input id=\"feishu-bot-name\" type=\"text\" value=\"\\u661f\\u73afOPC\\u52a9\\u624b\"></div>';"
|
|
1891
|
+
+ "h+='<div class=\"ch-field\"><label>\\u79c1\\u804a\\u6743\\u9650 <span class=\"hint\">\\u8c01\\u53ef\\u4ee5\\u548c\\u673a\\u5668\\u4eba\\u79c1\\u804a</span></label><select id=\"feishu-dm-policy\"><option value=\"pairing\">\\u9700\\u8981\\u914d\\u5bf9\\uff08\\u4ec5\\u5141\\u8bb8\\u7ecf\\u8fc7\\u5ba1\\u6279\\u7684\\u7528\\u6237\\u79c1\\u804a\\uff0c\\u66f4\\u5b89\\u5168\\uff09</option><option value=\"open\">\\u5f00\\u653e\\uff08\\u4efb\\u4f55\\u4eba\\u90fd\\u53ef\\u4ee5\\u76f4\\u63a5\\u548c\\u673a\\u5668\\u4eba\\u5bf9\\u8bdd\\uff09</option></select></div>';"
|
|
1892
|
+
+ "h+='<div><button class=\"btn btn-pri\" onclick=\"saveFeishuConfig()\" style=\"padding:10px 28px;font-size:14px;border-radius:8px\">\\u4fdd\\u5b58\\u914d\\u7f6e</button></div>';"
|
|
1893
|
+
+ "h+='</div></div>';"
|
|
1894
|
+
|
|
1895
|
+
// 4. Pairing management card (only for dmPolicy=pairing)
|
|
1896
|
+
+ "if(status.dmPolicy==='pairing'){"
|
|
1897
|
+
+ "h+='<div class=\"card\">';"
|
|
1898
|
+
+ "h+='<h3>\\u914d\\u5bf9\\u7ba1\\u7406</h3>';"
|
|
1899
|
+
+ "h+='<p style=\"color:var(--tx2);font-size:12px;margin-bottom:16px\">\\u79c1\\u804a\\u6743\\u9650\\u4e3a\\u300c\\u9700\\u8981\\u914d\\u5bf9\\u300d\\u65f6\\uff0c\\u7528\\u6237\\u9996\\u6b21\\u7ed9\\u673a\\u5668\\u4eba\\u53d1\\u6d88\\u606f\\u4f1a\\u8fdb\\u5165\\u5f85\\u5ba1\\u6279\\u5217\\u8868\\uff0c\\u4f60\\u5728\\u6b64\\u5904\\u6279\\u51c6\\u540e\\u624d\\u80fd\\u5bf9\\u8bdd\\u3002</p>';"
|
|
1900
|
+
+ "if(pairing&&(pairing.approved&&pairing.approved.length>0||pairing.pending&&pairing.pending.length>0)){"
|
|
1901
|
+
+ "if(pairing.approved&&pairing.approved.length>0){"
|
|
1902
|
+
+ "h+='<p style=\"font-weight:600;margin-bottom:8px\">\\u5df2\\u6279\\u51c6\\u7684\\u7528\\u6237</p>';"
|
|
1903
|
+
+ "h+='<table style=\"margin-bottom:16px\"><thead><tr><th>\\u7528\\u6237 ID</th><th>\\u5907\\u6ce8</th></tr></thead><tbody>';"
|
|
1904
|
+
+ "pairing.approved.forEach(function(u){h+='<tr><td><code>'+esc(u.openId)+'</code></td><td>'+esc(u.note||'--')+'</td></tr>';});"
|
|
1905
|
+
+ "h+='</tbody></table>';}"
|
|
1906
|
+
+ "if(pairing.pending&&pairing.pending.length>0){"
|
|
1907
|
+
+ "h+='<p style=\"font-weight:600;margin-bottom:8px\">\\u5f85\\u5ba1\\u6279</p>';"
|
|
1908
|
+
+ "h+='<table><thead><tr><th>\\u7528\\u6237 ID</th><th>\\u64cd\\u4f5c</th></tr></thead><tbody>';"
|
|
1909
|
+
+ "pairing.pending.forEach(function(u){h+='<tr><td><code>'+esc(u.openId)+'</code></td><td><button class=\"btn btn-pri btn-sm\" onclick=\"approveFeishuPairing(\\''+esc(u.openId)+'\\',true)\">\\u6279\\u51c6</button> <button class=\"btn btn-sm\" onclick=\"approveFeishuPairing(\\''+esc(u.openId)+'\\',false)\">\\u62d2\\u7edd</button></td></tr>';});"
|
|
1910
|
+
+ "h+='</tbody></table>';}"
|
|
1911
|
+
+ "}else{"
|
|
1912
|
+
+ "h+='<p style=\"color:var(--tx2)\">\\u6682\\u65e0\\u914d\\u5bf9\\u8bb0\\u5f55\\u3002\\u7528\\u6237\\u7ed9\\u673a\\u5668\\u4eba\\u53d1\\u6d88\\u606f\\u540e\\u4f1a\\u81ea\\u52a8\\u51fa\\u73b0\\u5728\\u6b64\\u5904\\u3002</p>';"
|
|
1913
|
+
+ "}"
|
|
1914
|
+
+ "h+='</div>';}"
|
|
1915
|
+
|
|
1916
|
+
// 5. Future channels placeholder
|
|
1917
|
+
+ "h+='<div class=\"ch-future\">';"
|
|
1918
|
+
+ "h+='<h2>\\u66f4\\u591a\\u9891\\u9053</h2>';"
|
|
1919
|
+
+ "h+='<p>\\u5fae\\u4fe1\\u3001\\u9489\\u9489\\u3001Telegram \\u7b49\\u9891\\u9053\\u5373\\u5c06\\u652f\\u6301\\uff0c\\u656c\\u8bf7\\u671f\\u5f85\\u3002</p>';"
|
|
1920
|
+
+ "h+='</div>';"
|
|
1921
|
+
|
|
1922
|
+
// Populate form with existing values
|
|
1923
|
+
+ "if(status.configured){"
|
|
1924
|
+
+ "var appIdInput=document.getElementById('feishu-app-id');"
|
|
1925
|
+
+ "if(appIdInput)appIdInput.placeholder='\\u5df2\\u914d\\u7f6e ('+status.appId+')';"
|
|
1926
|
+
+ "var botInput=document.getElementById('feishu-bot-name');"
|
|
1927
|
+
+ "if(botInput&&status.botName)botInput.value=status.botName;"
|
|
1928
|
+
+ "var policySelect=document.getElementById('feishu-dm-policy');"
|
|
1929
|
+
+ "if(policySelect)policySelect.value=status.dmPolicy||'pairing';"
|
|
1930
|
+
+ "}"
|
|
1931
|
+
|
|
1932
|
+
+ "el.innerHTML=h;}"
|
|
1933
|
+
|
|
1934
|
+
// saveFeishuConfig
|
|
1935
|
+
+ "\nfunction saveFeishuConfig(){"
|
|
1936
|
+
+ "var appId=document.getElementById('feishu-app-id').value.trim();"
|
|
1937
|
+
+ "var appSecret=document.getElementById('feishu-app-secret').value.trim();"
|
|
1938
|
+
+ "var botName=document.getElementById('feishu-bot-name').value.trim();"
|
|
1939
|
+
+ "var dmPolicy=document.getElementById('feishu-dm-policy').value;"
|
|
1940
|
+
+ "if(!appId&&!appSecret){showToast('\\u8bf7\\u586b\\u5199 App ID \\u548c App Secret');return;}"
|
|
1941
|
+
+ "fetch('/opc/admin/api/feishu/config',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({appId:appId,appSecret:appSecret,botName:botName,dmPolicy:dmPolicy})})"
|
|
1942
|
+
+ ".then(function(r){return r.json()}).then(function(d){"
|
|
1943
|
+
+ "if(d.ok){showToast(d.message||'\\u914d\\u7f6e\\u5df2\\u4fdd\\u5b58');setTimeout(function(){loadFeishu();},1000);}"
|
|
1944
|
+
+ "else{showToast('\\u4fdd\\u5b58\\u5931\\u8d25: '+(d.error||''));}"
|
|
1945
|
+
+ "}).catch(function(){showToast('\\u8bf7\\u6c42\\u5931\\u8d25');});}"
|
|
1946
|
+
|
|
1947
|
+
// approveFeishuPairing
|
|
1948
|
+
+ "\nfunction approveFeishuPairing(openId,approve){"
|
|
1949
|
+
+ "fetch('/opc/admin/api/feishu/pairing/approve',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({openId:openId,approve:approve})})"
|
|
1950
|
+
+ ".then(function(r){return r.json()}).then(function(d){"
|
|
1951
|
+
+ "if(d.ok){showToast(approve?'\\u5df2\\u6279\\u51c6':'\\u5df2\\u62d2\\u7edd');loadFeishu();}"
|
|
1952
|
+
+ "else{showToast('\\u64cd\\u4f5c\\u5931\\u8d25: '+(d.error||''));}"
|
|
1953
|
+
+ "}).catch(function(){showToast('\\u8bf7\\u6c42\\u5931\\u8d25');});}";
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1764
1956
|
function getClosureJs(): string {
|
|
1765
1957
|
return ""
|
|
1766
1958
|
+ "\nfunction loadClosure(){"
|
|
@@ -2707,45 +2899,107 @@ export function registerConfigUi(api: OpenClawPluginApi, db: OpcDatabase, gatewa
|
|
|
2707
2899
|
return true;
|
|
2708
2900
|
}
|
|
2709
2901
|
|
|
2710
|
-
//
|
|
2711
|
-
if (pathname === "/opc/admin/api/
|
|
2712
|
-
const
|
|
2713
|
-
const
|
|
2714
|
-
|
|
2715
|
-
|
|
2902
|
+
// ── Feishu Channel APIs ──
|
|
2903
|
+
if (pathname === "/opc/admin/api/feishu/status" && method === "GET") {
|
|
2904
|
+
const cfg = api.runtime.config.loadConfig();
|
|
2905
|
+
const feishuCfg = (cfg as Record<string, unknown>).channels as Record<string, unknown> | undefined;
|
|
2906
|
+
const feishu = feishuCfg?.feishu as Record<string, unknown> | undefined;
|
|
2907
|
+
const accounts = feishu?.accounts as Record<string, Record<string, string>> | undefined;
|
|
2908
|
+
const main = accounts?.main;
|
|
2909
|
+
sendJson(res, {
|
|
2910
|
+
configured: !!(main?.appId && main.appId !== "YOUR_FEISHU_APP_ID"),
|
|
2911
|
+
enabled: feishu?.enabled ?? false,
|
|
2912
|
+
appId: main?.appId ? "***" + main.appId.slice(-4) : "",
|
|
2913
|
+
botName: main?.botName ?? "",
|
|
2914
|
+
dmPolicy: feishu?.dmPolicy ?? "pairing",
|
|
2915
|
+
streaming: feishu?.streaming ?? false,
|
|
2916
|
+
});
|
|
2917
|
+
return true;
|
|
2918
|
+
}
|
|
2919
|
+
|
|
2920
|
+
if (pathname === "/opc/admin/api/feishu/config" && method === "POST") {
|
|
2921
|
+
const body = JSON.parse(await readBody(req)) as {
|
|
2922
|
+
appId?: string; appSecret?: string; botName?: string; dmPolicy?: string;
|
|
2923
|
+
};
|
|
2924
|
+
const cfg = api.runtime.config.loadConfig() as Record<string, unknown>;
|
|
2925
|
+
const channels = (cfg.channels ?? {}) as Record<string, unknown>;
|
|
2926
|
+
const existing = (channels.feishu ?? {}) as Record<string, unknown>;
|
|
2927
|
+
channels.feishu = {
|
|
2928
|
+
...existing,
|
|
2929
|
+
enabled: true,
|
|
2930
|
+
dmPolicy: body.dmPolicy ?? "pairing",
|
|
2931
|
+
groupPolicy: "open",
|
|
2932
|
+
streaming: true,
|
|
2933
|
+
accounts: {
|
|
2934
|
+
main: {
|
|
2935
|
+
appId: body.appId || ((existing.accounts as Record<string, Record<string, string>> | undefined)?.main?.appId ?? ""),
|
|
2936
|
+
appSecret: body.appSecret || ((existing.accounts as Record<string, Record<string, string>> | undefined)?.main?.appSecret ?? ""),
|
|
2937
|
+
botName: body.botName || "\u661F\u73AFOPC\u52A9\u624B",
|
|
2938
|
+
},
|
|
2939
|
+
},
|
|
2940
|
+
};
|
|
2941
|
+
cfg.channels = channels;
|
|
2942
|
+
await api.runtime.config.writeConfigFile(cfg);
|
|
2943
|
+
sendJson(res, { ok: true, message: "\u914D\u7F6E\u5DF2\u4FDD\u5B58\uFF0C\u7CFB\u7EDF\u5C06\u81EA\u52A8\u91CD\u542F\u4EE5\u5E94\u7528\u65B0\u914D\u7F6E" });
|
|
2944
|
+
return true;
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
if (pathname === "/opc/admin/api/feishu/pairing" && method === "GET") {
|
|
2948
|
+
const pairingPath = path.join(os.homedir(), ".openclaw", "oauth", "feishu-pairing.json");
|
|
2949
|
+
let pairing: unknown = { approved: [], pending: [] };
|
|
2950
|
+
try {
|
|
2951
|
+
if (fs.existsSync(pairingPath)) {
|
|
2952
|
+
pairing = JSON.parse(fs.readFileSync(pairingPath, "utf-8"));
|
|
2953
|
+
}
|
|
2954
|
+
} catch { /* ignore */ }
|
|
2955
|
+
sendJson(res, pairing);
|
|
2956
|
+
return true;
|
|
2957
|
+
}
|
|
2958
|
+
|
|
2959
|
+
if (pathname === "/opc/admin/api/feishu/pairing/approve" && method === "POST") {
|
|
2960
|
+
const body = JSON.parse(await readBody(req)) as { openId?: string; approve?: boolean };
|
|
2961
|
+
if (!body.openId) {
|
|
2962
|
+
sendJson(res, { ok: false, error: "openId required" }, 400);
|
|
2716
2963
|
return true;
|
|
2717
2964
|
}
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
? JSON.stringify({ msg_type: "text", content: { text } })
|
|
2724
|
-
: JSON.stringify({ msgtype: "text", text: { content: text } });
|
|
2725
|
-
const parsed = new URL(url);
|
|
2726
|
-
const transport = parsed.protocol === "https:" ? https : http;
|
|
2727
|
-
const reqOut = transport.request(
|
|
2728
|
-
{
|
|
2729
|
-
hostname: parsed.hostname,
|
|
2730
|
-
port: parsed.port || (parsed.protocol === "https:" ? 443 : 80),
|
|
2731
|
-
path: parsed.pathname + parsed.search,
|
|
2732
|
-
method: "POST",
|
|
2733
|
-
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(bodyStr) },
|
|
2734
|
-
},
|
|
2735
|
-
(r) => {
|
|
2736
|
-
r.resume();
|
|
2737
|
-
sendJson(res, r.statusCode && r.statusCode < 400 ? { ok: true } : { ok: false, error: `HTTP ${r.statusCode}` });
|
|
2738
|
-
resolve();
|
|
2739
|
-
},
|
|
2740
|
-
);
|
|
2741
|
-
reqOut.on("error", (err) => { sendJson(res, { ok: false, error: err.message }); resolve(); });
|
|
2742
|
-
reqOut.write(bodyStr);
|
|
2743
|
-
reqOut.end();
|
|
2744
|
-
} catch (err) {
|
|
2745
|
-
sendJson(res, { ok: false, error: err instanceof Error ? err.message : String(err) });
|
|
2746
|
-
resolve();
|
|
2965
|
+
const allowPath = path.join(os.homedir(), ".openclaw", "oauth", "feishu-allowFrom.json");
|
|
2966
|
+
let allowList: string[] = [];
|
|
2967
|
+
try {
|
|
2968
|
+
if (fs.existsSync(allowPath)) {
|
|
2969
|
+
allowList = JSON.parse(fs.readFileSync(allowPath, "utf-8")) as string[];
|
|
2747
2970
|
}
|
|
2748
|
-
}
|
|
2971
|
+
} catch { /* ignore */ }
|
|
2972
|
+
if (body.approve) {
|
|
2973
|
+
if (!allowList.includes(body.openId)) allowList.push(body.openId);
|
|
2974
|
+
} else {
|
|
2975
|
+
allowList = allowList.filter((id) => id !== body.openId);
|
|
2976
|
+
}
|
|
2977
|
+
const dir = path.dirname(allowPath);
|
|
2978
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
2979
|
+
fs.writeFileSync(allowPath, JSON.stringify(allowList, null, 2));
|
|
2980
|
+
// Also update pairing file to move from pending to approved
|
|
2981
|
+
const pairingPath = path.join(os.homedir(), ".openclaw", "oauth", "feishu-pairing.json");
|
|
2982
|
+
try {
|
|
2983
|
+
if (fs.existsSync(pairingPath)) {
|
|
2984
|
+
const pairing = JSON.parse(fs.readFileSync(pairingPath, "utf-8")) as {
|
|
2985
|
+
approved?: { openId: string; note?: string }[];
|
|
2986
|
+
pending?: { openId: string }[];
|
|
2987
|
+
};
|
|
2988
|
+
if (body.approve && pairing.pending) {
|
|
2989
|
+
const found = pairing.pending.find((p) => p.openId === body.openId);
|
|
2990
|
+
if (found) {
|
|
2991
|
+
pairing.pending = pairing.pending.filter((p) => p.openId !== body.openId);
|
|
2992
|
+
pairing.approved = pairing.approved ?? [];
|
|
2993
|
+
pairing.approved.push({ openId: body.openId, note: "" });
|
|
2994
|
+
fs.writeFileSync(pairingPath, JSON.stringify(pairing, null, 2));
|
|
2995
|
+
}
|
|
2996
|
+
} else if (!body.approve && pairing.pending) {
|
|
2997
|
+
pairing.pending = pairing.pending.filter((p) => p.openId !== body.openId);
|
|
2998
|
+
fs.writeFileSync(pairingPath, JSON.stringify(pairing, null, 2));
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
} catch { /* ignore */ }
|
|
3002
|
+
sendJson(res, { ok: true });
|
|
2749
3003
|
return true;
|
|
2750
3004
|
}
|
|
2751
3005
|
|