diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_kr宝塔_仅修复site_db.py b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_kr宝塔_仅修复site_db.py new file mode 100644 index 00000000..ed20ef7a --- /dev/null +++ b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_kr宝塔_仅修复site_db.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""TAT:仅修复 site.db 中 Node 项目启动命令(纯 sqlite3,无 API,约 5s 完成)""" +import base64, json, os, re, sys, time +KR_INSTANCE_ID, REGION = "ins-aw0tnqjo", "ap-guangzhou" + +# 项目名 -> 路径,用于 site.db 更新 project_config +UPDATES = [ + ("玩值大屏", "/www/wwwroot/self/wanzhi/玩值大屏"), + ("玩值大屏", "/www/wwwroot/self/wanzhi/玩值"), + ("tongzhi", "/www/wwwroot/self/wanzhi/tongzhi"), + ("tongzhi", "/www/wwwroot/self/wanzhi/tong"), + ("is_phone", "/www/wwwroot/self/kr/kr-phone"), + ("ai_hair", "/www/wwwroot/client/ai_hair"), + ("神射手", "/www/wwwroot/self/kr/kr-use"), + ("神射手", "/www/wwwroot/self/kr/kr-users"), + ("AITOUFA", "/www/wwwroot/ext/tools/AITOUFA"), + ("AITOUFA", "/www/wwwroot/ext/tools/AITOL"), + ("wzdj", "/www/wwwroot/self/wzdj"), + ("zhiji", "/www/wwwroot/self/zhiji"), + ("ymao", "/www/wwwroot/ext/ymao"), + ("zhaoping", "/www/wwwroot/client/zhaoping"), + ("word", "/www/wwwroot/self/word"), +] + +SHELL = r'''#!/bin/bash +echo "=== 修复 site.db Node 启动命令 ===" +python3 -c " +import json,sqlite3,os +db='/www/server/panel/data/db/site.db' +if not os.path.isfile(db): print('db not found'); exit(1) +conn=sqlite3.connect(db) +c=conn.cursor() +c.execute('SELECT id,name,path,project_config FROM sites WHERE project_type=\"Node\"') +up=0 +for row in c.fetchall(): + sid,nm,path,cfg=row[0],row[1],row[2]or'',row[3]or'{}' + path=(path or'').strip() + try: cfg=json.loads(cfg) + except: cfg={} + proj=cfg.get('path')or cfg.get('project_path')or path + if not proj or not os.path.isdir(proj): + for nm2,proj2 in [('玩值大屏','/www/wwwroot/self/wanzhi/玩值大屏'),('玩值大屏','/www/wwwroot/self/wanzhi/玩值'),('tongzhi','/www/wwwroot/self/wanzhi/tongzhi'),('tongzhi','/www/wwwroot/self/wanzhi/tong'),('神射手','/www/wwwroot/self/kr/kr-use'),('神射手','/www/wwwroot/self/kr/kr-users'),('AITOUFA','/www/wwwroot/ext/tools/AITOUFA'),('AITOUFA','/www/wwwroot/ext/tools/AITOL')]: + if nm==nm2 and os.path.isdir(proj2): proj=proj2; break + if not proj or not os.path.isdir(proj): continue + cmd='cd %s && (pnpm start 2>/dev/null || npm run start)'%proj + # 统一修复为正确启动命令(避免 node /path 等错误格式) + if True: + cfg['project_script']=cfg['run_cmd']=cmd + cfg['path']=proj + c.execute('UPDATE sites SET path=?, project_config=? WHERE id=?',(proj,json.dumps(cfg,ensure_ascii=False),sid)) + up+=1 + print('fix:',nm,'->',proj) +conn.commit() +conn.close() +print('共修复',up,'个') +" +echo "=== 完成 ===" +''' + +def _creds(): + d = os.path.dirname(os.path.abspath(__file__)) + for _ in range(6): + p = os.path.join(d, "运营中枢", "工作台", "00_账号与API索引.md") + if os.path.isfile(p): + t = open(p).read() + sid = skey = None + for L in t.splitlines(): + m = re.search(r"SecretId[^|]*\|\s*`([^`]+)`", L, re.I) + if m and "AKID" in m.group(1): sid = m.group(1).strip() + m = re.search(r"SecretKey\s*\|\s*`([^`]+)`", L, re.I) + if m: skey = m.group(1).strip() + return sid or os.environ.get("TENCENTCLOUD_SECRET_ID"), skey or os.environ.get("TENCENTCLOUD_SECRET_KEY") + d = os.path.dirname(d) + return None, None + +def main(): + sid, skey = _creds() + if not sid or not skey: print("❌ 未配置凭证"); return 1 + from tencentcloud.common import credential + from tencentcloud.tat.v20201028 import tat_client, models + cred = credential.Credential(sid, skey) + cli = tat_client.TatClient(cred, REGION) + req = models.RunCommandRequest() + req.Content = base64.b64encode(SHELL.encode("utf-8")).decode() + req.InstanceIds = [KR_INSTANCE_ID] + req.CommandType = "SHELL" + req.Timeout = 30 + req.CommandName = "kr宝塔_仅修复site_db" + r = cli.RunCommand(req) + print("✅ TAT inv:", r.InvocationId, "(仅修复 site.db,约 5s)") + time.sleep(15) + req2 = models.DescribeInvocationTasksRequest() + f = models.Filter() + f.Name, f.Values = "invocation-id", [r.InvocationId] + req2.Filters = [f] + r2 = cli.DescribeInvocationTasks(req2) + for t in (r2.InvocationTaskSet or []): + print("状态:", getattr(t, "TaskStatus", "")) + tr = getattr(t, "TaskResult", None) + if tr: + d = tr.__dict__ if hasattr(tr, "__dict__") else {} + out = d.get("Output", getattr(tr, "Output", "")) + if out: + try: print(base64.b64decode(out).decode("utf-8", errors="replace")) + except: print(out[:2000]) + return 0 + +if __name__ == "__main__": sys.exit(main()) diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_kr宝塔_仅修复site_db与启动Node.py b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_kr宝塔_仅修复site_db与启动Node.py new file mode 100644 index 00000000..273ef0e5 --- /dev/null +++ b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_kr宝塔_仅修复site_db与启动Node.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""TAT:仅修复 site.db 启动命令 + 批量启动 Node(精简版,避免超时)""" +import base64, json, os, re, sys, time +KR_INSTANCE_ID, REGION = "ins-aw0tnqjo", "ap-guangzhou" + +SCRIPT = r'''#!/bin/bash +# 确保面板 +ss -tlnp 2>/dev/null | grep -q ':9988 ' || { /etc/init.d/bt start 2>/dev/null; sleep 6; } +python3 -c ' +import hashlib,json,os,re,sqlite3,subprocess,time,urllib.request,urllib.parse,ssl +ssl._create_default_https_context=ssl._create_unverified_context +P,K="https://127.0.0.1:9988","qcWubCdlfFjS2b2DMT1lzPFaDfmv1cBT" +def sg(): t=int(time.time()); return {"request_time":t,"request_token":hashlib.md5((str(t)+hashlib.md5(K.encode()).hexdigest()).encode()).hexdigest()} +def post(p,d=None): r=urllib.request.Request(P+p,data=urllib.parse.urlencode({**sg(),**(d or{})}).encode()); return json.loads(urllib.request.urlopen(r,timeout=20).read().decode()) +def pids(port): + try: return {int(x) for x in re.findall(r"pid=(\d+)",subprocess.check_output("ss -tlnp 2>/dev/null|grep \":%s \"||true"%port,shell=True).decode())} + except: return set() +def ports(it): + c=it.get("project_config") or {} + if isinstance(c,str): c=json.loads(c) if c else {} + p=[c["port"]] if c.get("port") else [] + p.extend(int(m) for m in re.findall(r"-p\s*(\d+)",str(c.get("project_script","")))) + return list(set(p)) +FB={"玩值大屏":["/www/wwwroot/self/wanzhi/玩值大屏","/www/wwwroot/self/wanzhi/玩值"],"tongzhi":["/www/wwwroot/self/wanzhi/tongzhi","/www/wwwroot/self/wanzhi/tong"],"神射手":["/www/wwwroot/self/kr/kr-use","/www/wwwroot/self/kr/kr-users"],"AITOUFA":["/www/wwwroot/ext/tools/AITOUFA","/www/wwwroot/ext/tools/AITOL"]} +db="/www/server/panel/data/db/site.db" +if os.path.isfile(db): + conn=sqlite3.connect(db) + c=conn.cursor() + c.execute("SELECT id,name,path,project_config FROM sites WHERE project_type=\"Node\"") + for r in c.fetchall(): + sid,nm,path,cfg=r[0],r[1],r[2],r[3]or"{}" + path=(path or"").strip() + cfg=json.loads(cfg) if cfg else {} + proj=cfg.get("path")or cfg.get("project_path")or path + if not proj or not os.path.isdir(proj): + for p in FB.get(nm,[]): + if os.path.isdir(p): proj=p; break + if not proj or not os.path.isdir(proj): continue + cmd="cd %s && (pnpm start 2>/dev/null || npm run start)"%proj + old=str(cfg.get("project_script")or"").strip() + if "cd " not in old: + cfg["project_script"]=cfg["run_cmd"]=cmd + cfg["path"]=proj + c.execute("UPDATE sites SET path=?,project_config=? WHERE id=?",(proj,json.dumps(cfg,ensure_ascii=False),sid)) + print("fix:",nm) + conn.commit() + conn.close() +items=post("/project/nodejs/get_project_list").get("data")or post("/project/nodejs/get_project_list").get("list")or[] +for it in items: + nm=it.get("name") + if not nm or it.get("run"): continue + try: + for port in ports(it): + for pid in pids(port): subprocess.call("kill -9 %s 2>/dev/null"%pid,shell=True) + pf="/www/server/nodejs/vhost/pids/%s.pid"%nm + if os.path.exists(pf): open(pf,"w").write("0") + post("/project/nodejs/stop_project",{"project_name":nm}) + time.sleep(0.3) + r=post("/project/nodejs/start_project",{"project_name":nm}) + print(nm,":", "OK" if r.get("status") or "成功" in str(r.get("msg","")) else "FAIL") + except: print(nm,": ERR") + time.sleep(1) +time.sleep(5) +r2=post("/project/nodejs/get_project_list") +lst=r2.get("data")or r2.get("list")or[] +print("运行",sum(1 for x in lst if x.get("run")),"/",len(lst)) +' +''' + +def _creds(): + d = os.path.dirname(os.path.abspath(__file__)) + for _ in range(6): + p = os.path.join(d, "运营中枢", "工作台", "00_账号与API索引.md") + if os.path.isfile(p): + t = open(p).read() + sid = skey = None + for L in t.splitlines(): + m = re.search(r"SecretId[^|]*\|\s*`([^`]+)`", L, re.I) + if m and "AKID" in m.group(1): sid = m.group(1).strip() + m = re.search(r"SecretKey\s*\|\s*`([^`]+)`", L, re.I) + if m: skey = m.group(1).strip() + return sid or os.environ.get("TENCENTCLOUD_SECRET_ID"), skey or os.environ.get("TENCENTCLOUD_SECRET_KEY") + d = os.path.dirname(d) + return None, None + +def main(): + sid, skey = _creds() + if not sid or not skey: print("❌ 未配置凭证"); return 1 + from tencentcloud.common import credential + from tencentcloud.tat.v20201028 import tat_client, models + cred = credential.Credential(sid, skey) + cli = tat_client.TatClient(cred, REGION) + req = models.RunCommandRequest() + req.Content = base64.b64encode(SCRIPT.encode("utf-8")).decode() + req.InstanceIds = [KR_INSTANCE_ID] + req.CommandType = "SHELL" + req.Timeout = 120 + req.CommandName = "kr宝塔_仅修复site_db与启动Node" + r = cli.RunCommand(req) + print("✅ TAT inv:", r.InvocationId) + time.sleep(100) + req2 = models.DescribeInvocationTasksRequest() + f = models.Filter() + f.Name, f.Values = "invocation-id", [r.InvocationId] + req2.Filters = [f] + r2 = cli.DescribeInvocationTasks(req2) + for t in (r2.InvocationTaskSet or []): + tr = getattr(t, "TaskResult", None) + if tr: + d = tr.__dict__ if hasattr(tr, "__dict__") else {} + out = d.get("Output", getattr(tr, "Output", "")) + if out: + try: print(base64.b64decode(out).decode("utf-8", errors="replace")) + except: print(out[:3000]) + return 0 + +if __name__ == "__main__": sys.exit(main()) diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_kr宝塔_仅批量启动Node.py b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_kr宝塔_仅批量启动Node.py new file mode 100644 index 00000000..2ef7b9cb --- /dev/null +++ b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_kr宝塔_仅批量启动Node.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""TAT:仅批量启动 kr 宝塔 Node 项目(约 15s)""" +import base64, hashlib, json, os, re, sys, time, urllib.request, urllib.parse, ssl +ssl._create_default_https_context = ssl._create_unverified_context +KR_INSTANCE_ID, REGION = "ins-aw0tnqjo", "ap-guangzhou" +PANEL, K = "https://127.0.0.1:9988", "qcWubCdlfFjS2b2DMT1lzPFaDfmv1cBT" + +def sign(): + t = int(time.time()) + return {"request_time": t, "request_token": hashlib.md5((str(t) + hashlib.md5(K.encode()).hexdigest()).encode()).hexdigest()} +def post(p, d=None): + pl = sign() + if d: pl.update(d) + r = urllib.request.Request(PANEL + p, data=urllib.parse.urlencode(pl).encode()) + with urllib.request.urlopen(r, timeout=15) as resp: + return json.loads(resp.read().decode()) + +def _creds(): + d = os.path.dirname(os.path.abspath(__file__)) + for _ in range(6): + p = os.path.join(d, "运营中枢", "工作台", "00_账号与API索引.md") + if os.path.isfile(p): + t = open(p).read() + sid = skey = None + for L in t.splitlines(): + m = re.search(r"SecretId[^|]*\|\s*`([^`]+)`", L, re.I) + if m and "AKID" in m.group(1): sid = m.group(1).strip() + m = re.search(r"SecretKey\s*\|\s*`([^`]+)`", L, re.I) + if m: skey = m.group(1).strip() + return sid or os.environ.get("TENCENTCLOUD_SECRET_ID"), skey or os.environ.get("TENCENTCLOUD_SECRET_KEY") + d = os.path.dirname(d) + return None, None + +def main(): + sid, skey = _creds() + if not sid or not skey: + print("❌ 未配置凭证"); return 1 + from tencentcloud.common import credential + from tencentcloud.tat.v20201028 import tat_client, models + + SHELL = r'''#!/bin/bash +echo "【0】确保宝塔 9988 监听" +if ! ss -tlnp 2>/dev/null | grep -q ':9988 '; then + /etc/init.d/bt start 2>/dev/null || /www/server/panel/bt start 2>/dev/null || true + sleep 8 +fi +echo "【启动 Node】" +PYTHONUNBUFFERED=1 python3 -u -c " +import hashlib,json,time,urllib.request,urllib.parse,ssl +ssl._create_default_https_context=ssl._create_unverified_context +P,K='https://127.0.0.1:9988','qcWubCdlfFjS2b2DMT1lzPFaDfmv1cBT' +def sg(): + t=int(time.time()) + return {'request_time':t,'request_token':hashlib.md5((str(t)+hashlib.md5(K.encode()).hexdigest()).encode()).hexdigest()} +def post(u,d=None): + pl=sg(); (pl.update(d) if d else None) + r=urllib.request.Request(P+u,data=urllib.parse.urlencode(pl).encode()) + return json.loads(urllib.request.urlopen(r,timeout=15).read().decode()) +items=post('/project/nodejs/get_project_list').get('data')or post('/project/nodejs/get_project_list').get('list')or[] +to_start=[x for x in items if x.get('name') and not x.get('run')] +print('待启动',len(to_start),'个') +for it in to_start: + n=it.get('name') + try: + r=post('/project/nodejs/start_project',{'project_name':n}) + ok=r.get('status') or '成功' in str(r.get('msg','')) + print(n,':','OK' if ok else 'FAIL', r.get('msg','')[:80] if not ok else '') + except Exception as e: print(n,': ERR',str(e)[:30]) + time.sleep(1.5) +items2=post('/project/nodejs/get_project_list').get('data')or post('/project/nodejs/get_project_list').get('list')or[] +run=sum(1 for x in items2 if x.get('run')) +print('运行',run,'/',len(items2)) +uptime +" 2>&1 +''' + cred = credential.Credential(sid, skey) + cli = tat_client.TatClient(cred, REGION) + req = models.RunCommandRequest() + req.Content = base64.b64encode(SHELL.encode("utf-8")).decode() + req.InstanceIds = [KR_INSTANCE_ID] + req.CommandType = "SHELL" + req.Timeout = 50 + req.CommandName = "kr宝塔_仅批量启动Node" + r = cli.RunCommand(req) + print("✅ TAT inv:", r.InvocationId) + time.sleep(25) + req2 = models.DescribeInvocationTasksRequest() + f = models.Filter() + f.Name, f.Values = "invocation-id", [r.InvocationId] + req2.Filters = [f] + for t in (cli.DescribeInvocationTasks(req2).InvocationTaskSet or []): + tr = getattr(t, "TaskResult", None) + if tr: + out = getattr(tr, "Output", tr.__dict__.get("Output", "")) + if out: + print(base64.b64decode(out).decode("utf-8", errors="replace")) + return 0 + +if __name__ == "__main__": sys.exit(main()) diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_kr宝塔_查询Node项目配置.py b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_kr宝塔_查询Node项目配置.py new file mode 100644 index 00000000..af55fa32 --- /dev/null +++ b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_kr宝塔_查询Node项目配置.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""TAT:查询 site.db 中 Node 项目名、路径、project_script""" +import base64, json, os, re, sys, time +KR_INSTANCE_ID, REGION = "ins-aw0tnqjo", "ap-guangzhou" + +SHELL = r'''#!/bin/bash +python3 -c " +import json,sqlite3,os +db='/www/server/panel/data/db/site.db' +if not os.path.isfile(db): print('db not found'); exit(1) +conn=sqlite3.connect(db) +c=conn.cursor() +c.execute('SELECT id,name,path,project_config FROM sites WHERE project_type=\"Node\"') +for row in c.fetchall(): + sid,nm,path,cfg=row[0],row[1],row[2]or'',row[3]or'{}' + try: cfg=json.loads(cfg) + except: cfg={} + proj=cfg.get('path')or path + script=cfg.get('project_script','')[:150] + exist='Y' if proj and os.path.isdir(proj) else 'N' + print(nm,'|',proj[:50],'|',exist,'|',script) +conn.close() +" +''' + +def _creds(): + d = os.path.dirname(os.path.abspath(__file__)) + for _ in range(6): + p = os.path.join(d, "运营中枢", "工作台", "00_账号与API索引.md") + if os.path.isfile(p): + t = open(p).read() + sid = skey = None + for L in t.splitlines(): + m = re.search(r"SecretId[^|]*\|\s*`([^`]+)`", L, re.I) + if m and "AKID" in m.group(1): sid = m.group(1).strip() + m = re.search(r"SecretKey\s*\|\s*`([^`]+)`", L, re.I) + if m: skey = m.group(1).strip() + return sid or os.environ.get("TENCENTCLOUD_SECRET_ID"), skey or os.environ.get("TENCENTCLOUD_SECRET_KEY") + d = os.path.dirname(d) + return None, None + +def main(): + sid, skey = _creds() + if not sid or not skey: print("❌ 未配置凭证"); return 1 + from tencentcloud.common import credential + from tencentcloud.tat.v20201028 import tat_client, models + cred = credential.Credential(sid, skey) + cli = tat_client.TatClient(cred, REGION) + req = models.RunCommandRequest() + req.Content = base64.b64encode(SHELL.encode("utf-8")).decode() + req.InstanceIds = [KR_INSTANCE_ID] + req.CommandType = "SHELL" + req.Timeout = 30 + req.CommandName = "kr宝塔_查询Node配置" + r = cli.RunCommand(req) + print("✅ TAT inv:", r.InvocationId) + time.sleep(12) + req2 = models.DescribeInvocationTasksRequest() + f = models.Filter() + f.Name, f.Values = "invocation-id", [r.InvocationId] + req2.Filters = [f] + r2 = cli.DescribeInvocationTasks(req2) + for t in (r2.InvocationTaskSet or []): + tr = getattr(t, "TaskResult", None) + if tr: + d = tr.__dict__ if hasattr(tr, "__dict__") else {} + out = d.get("Output", getattr(tr, "Output", "")) + if out: + try: print(base64.b64decode(out).decode("utf-8", errors="replace")) + except: print(out[:3000]) + return 0 + +if __name__ == "__main__": sys.exit(main()) diff --git a/运营中枢/工作台/gitea_push_log.md b/运营中枢/工作台/gitea_push_log.md index 22e6b0fe..8e823d33 100644 --- a/运营中枢/工作台/gitea_push_log.md +++ b/运营中枢/工作台/gitea_push_log.md @@ -95,3 +95,4 @@ | 2026-02-22 14:42:04 | 🔄 卡若AI 同步 2026-02-22 14:42 | 更新:金仓、卡木、运营中枢工作台 | 排除 >20MB: 8 个 | | 2026-02-22 14:44:11 | 🔄 卡若AI 同步 2026-02-22 14:43 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 8 个 | | 2026-02-22 14:59:42 | 🔄 卡若AI 同步 2026-02-22 14:59 | 更新:金仓、卡木、运营中枢工作台 | 排除 >20MB: 8 个 | +| 2026-02-22 15:08:03 | 🔄 卡若AI 同步 2026-02-22 15:07 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 8 个 | diff --git a/运营中枢/工作台/代码管理.md b/运营中枢/工作台/代码管理.md index f6003911..8eacdce5 100644 --- a/运营中枢/工作台/代码管理.md +++ b/运营中枢/工作台/代码管理.md @@ -98,3 +98,4 @@ | 2026-02-22 14:42:04 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 14:42 | 更新:金仓、卡木、运营中枢工作台 | 排除 >20MB: 8 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-22 14:44:11 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 14:43 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 8 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-22 14:59:42 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 14:59 | 更新:金仓、卡木、运营中枢工作台 | 排除 >20MB: 8 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | +| 2026-02-22 15:08:03 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 15:07 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 8 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |