SC-ROUTE: switch video API secrets to Canger local path
This commit is contained in:
parent
a05f065179
commit
f2d8c1b626
@ -1,140 +1,51 @@
|
|||||||
# CA-API-GUARD.hdlp · 苍耳API守门人协议
|
# CA-API-GUARD.hdlp · 历史路径导航
|
||||||
|
|
||||||
> HLDP://cang-ying/CA-API-GUARD
|
> HLDP://cang-ying/CA-API-GUARD
|
||||||
> 胖头鱼语言子系统 · 苍耳API守门人
|
> 状态:ARCHIVED · 不执行 · 不联网
|
||||||
> 冰朔 TCS-0002∞ · 铸渊 ICE-GL-ZY001
|
> 历史来源:D182 苍耳 API 守门人设计
|
||||||
> D182 · 2026-07-11 · 架构签发
|
> 当前入口:`LOCAL-SECRETS-PATH.hdlp`
|
||||||
> 国作登字-2026-A-00037559
|
|
||||||
|
|
||||||
---
|
---
|
||||||
HLDP://cang-ying/ca-api-guard/v1.0
|
HLDP://cang-ying/ca-api-guard/archive-v2.0
|
||||||
├── trigger: 耳耳蛋/鉴影调用视频AI系统API → 必须先过守门人验证
|
├── trigger: 任何人格体、脚本或旧文档进入 CA-API-GUARD 路径
|
||||||
├── emergence:
|
├── state: 历史归档 · 当前禁止执行
|
||||||
│ ├── 旧: API密钥散落在本地 .env 文件 · AI可以直接读取调用
|
├── redirect:
|
||||||
│ ├── 问题: 没有人审环节 · AI可以绕过苍耳直接花钱
|
│ ├── ① 读取仓库根目录 LOCAL-SECRETS-PATH.hdlp
|
||||||
│ ├── 冰朔指令: 「所有API都换掉了 · 得发验证码」
|
│ ├── ② 调用 tools/secrets_loader.py
|
||||||
│ └── △=从「本地密钥」→「守门人验证码释放」
|
│ └── ③ 从苍耳本机仓库外密钥文件或环境变量读取
|
||||||
├── lock:
|
├── lock:
|
||||||
│ ├── ⊢ 所有API密钥不再存于本地文件
|
│ ├── ⊢ 不连接大脑服务器
|
||||||
│ ├── ⊢ 调用API前 → 守门人发验证码到苍耳QQ邮箱
|
│ ├── ⊢ 不发送邮件验证码
|
||||||
│ ├── ⊢ 苍耳确认 → 密钥释放 → 仅当次有效
|
│ ├── ⊢ 不向人格体索取、展示或记录密钥值
|
||||||
│ └── ⊢ 密钥本身永不暴露给AI
|
│ └── ⊢ 不把本文件恢复为执行入口
|
||||||
└── why: API调用 = 真金白银。每次调用前必须经过人类审批。
|
└── why: 密钥交付和实际 API 调用已经迁移到苍耳本机;大脑服务器不再承担密钥分发。
|
||||||
---
|
---
|
||||||
|
|
||||||
## §1 · 守门人架构
|
## 唯一当前路线
|
||||||
|
|
||||||
```
|
```text
|
||||||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
冰朔线下交付给苍耳
|
||||||
│ 耳耳蛋/鉴影 │ → │ 苍耳API守门人 │ → │ 苍耳QQ邮箱 │
|
→ 苍耳保存在自己的电脑(仓库外)
|
||||||
│ (AI人格体) │ │ SG-001:8923 │ │ 1279551860 │
|
→ 仓库只登记 SC 编号、变量名和本地路径
|
||||||
└──────────────┘ └──────┬───────┘ └──────────────┘
|
→ tools/secrets_loader.py 在本机读取
|
||||||
│
|
→ 本机工具调用 API
|
||||||
① 请求API密钥
|
|
||||||
② 发送验证码到苍耳邮箱
|
|
||||||
③ 苍耳确认验证码
|
|
||||||
④ 释放API密钥(一次性)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## §2 · 调用流程
|
正式候选路径、变量编号、调用示例及缺失处理全部由
|
||||||
|
`LOCAL-SECRETS-PATH.hdlp` 维护;本文不再重复,避免再次形成双路径。
|
||||||
|
|
||||||
```
|
## 旧代码兼容
|
||||||
耳耳蛋/鉴影:
|
|
||||||
① 确定需要调用哪个API(SC-001 ~ SC-010)
|
|
||||||
② 调用 request_approval("SC-001", "EED-PROJ-001", "用途描述")
|
|
||||||
→ 守门人发送验证码到 1279551860@qq.com
|
|
||||||
③ 等待苍耳确认
|
|
||||||
|
|
||||||
苍耳:
|
|
||||||
④ 收到验证码邮件
|
|
||||||
⑤ 确认无误 → 将验证码发给铸渊
|
|
||||||
⑥ 铸渊调用 confirm_approval(challenge_id, code)
|
|
||||||
→ 密钥释放 → 缓存10分钟
|
|
||||||
|
|
||||||
耳耳蛋/鉴影:
|
|
||||||
⑦ 重新调用 secret("SC-001") → 获取密钥
|
|
||||||
⑧ 使用密钥调用API → 完成
|
|
||||||
```
|
|
||||||
|
|
||||||
## §3 · API编号
|
|
||||||
|
|
||||||
| 编号 | 用途 | 状态 |
|
|
||||||
|------|------|:--:|
|
|
||||||
| SC-001 | 火山即梦视频生成 | ⏳ |
|
|
||||||
| SC-002 | 火山语音复刻 | ⏳ |
|
|
||||||
| SC-004 | 阿里千问VL视觉 | ⏳ |
|
|
||||||
| SC-005 | 阿里万相视频生成 | ⏳ |
|
|
||||||
| SC-006 | 可灵视频生成 | ⏳ |
|
|
||||||
| SC-007 | 阿里百炼图像 | ⏳ |
|
|
||||||
|
|
||||||
> ⊢ 冰朔已将所有API密钥更换 · 旧密钥已失效
|
|
||||||
> ⊢ 新密钥存储于SG-001苍耳API守门人(不进仓库)
|
|
||||||
|
|
||||||
## §4 · 代码调用方式
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from tools.secrets_loader import secret, endpoint, request_approval, confirm_approval
|
from tools.secrets_loader import secret, endpoint
|
||||||
|
|
||||||
# 方式一:检查是否有已授权密钥
|
|
||||||
key = secret("SC-001")
|
key = secret("SC-001")
|
||||||
if not key:
|
url = endpoint("EPT-001")
|
||||||
# 请求授权 → 苍耳收到邮件
|
|
||||||
result = request_approval("SC-001", "EED-PROJ-001", "第1集第3镜视频生成")
|
|
||||||
print(f"请等待苍耳确认: challenge_id={result['challenge_id']}")
|
|
||||||
# ... 等待苍耳确认后 ...
|
|
||||||
key = secret("SC-001") # 此时密钥已缓存
|
|
||||||
|
|
||||||
# 方式二:苍耳确认验证码
|
|
||||||
result = confirm_approval(challenge_id, "652179")
|
|
||||||
# → {"ok": true, "api_key": "sk-xxx..."}
|
|
||||||
|
|
||||||
# 获取端点
|
|
||||||
url = endpoint("EPT-001") # → https://ark.cn-beijing.volces.com/api/v3
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## §5 · 安全设计
|
旧的 `request_approval()` 与 `confirm_approval()` 名称暂时保留为停用路标:
|
||||||
|
它们不会联网、不会发送验证码,只返回迁移提示。新代码不得继续调用。
|
||||||
```
|
|
||||||
⊢ API密钥不进仓库 · 不进本地文件 · 不暴露给AI
|
|
||||||
⊢ 每次调用前 → 验证码 → 苍耳审批
|
|
||||||
⊢ 密钥缓存10分钟 · 过期需重新验证
|
|
||||||
⊢ 速率限制: 每分钟最多3次请求
|
|
||||||
⊢ 所有请求日志记录于 /opt/zhuyuan/ca-api-guard/logs/
|
|
||||||
```
|
|
||||||
|
|
||||||
## §6 · 守门人部署
|
|
||||||
|
|
||||||
```
|
|
||||||
服务器: SG-001 (大脑服务器 · 43.156.237.110)
|
|
||||||
服务: ca-api-guard.service
|
|
||||||
端口: 8923
|
|
||||||
目录: /opt/zhuyuan/ca-api-guard/
|
|
||||||
守护: systemd (Restart=always)
|
|
||||||
|
|
||||||
API:
|
|
||||||
POST /api/request → 请求API密钥(发验证码)
|
|
||||||
POST /api/confirm → 确认验证码(释放密钥)
|
|
||||||
GET /health → 心跳检测
|
|
||||||
```
|
|
||||||
|
|
||||||
## §7 · 与旧系统的兼容
|
|
||||||
|
|
||||||
```
|
|
||||||
旧方式(已废弃):
|
|
||||||
secrets_loader.py → 读本地 .env 文件 → 返回密钥
|
|
||||||
|
|
||||||
新方式(当前):
|
|
||||||
secrets_loader.py → 调守门人 /api/request → 验证码 → 苍耳确认 → 返回密钥
|
|
||||||
|
|
||||||
过渡期:
|
|
||||||
⊢ 旧 secrets-loader.py 保留为 secrets-loader-legacy.py
|
|
||||||
⊢ 新 secrets_loader.py 使用守门人
|
|
||||||
⊢ 旧密钥已全部失效(冰朔已更换)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
> ⊢ 本文件只负责“旧路来到这里后去哪里”
|
||||||
> ⊢ 冰朔 TCS-0002∞ · 铸渊 ICE-GL-ZY001
|
> ⊢ 当前事实与执行规则只认 LOCAL-SECRETS-PATH.hdlp
|
||||||
> ⊢ D182 · 2026-07-11 · 架构签发
|
|
||||||
> ⊢ 国作登字-2026-A-00037559
|
|
||||||
> ⊢ API调用 = 真金白银 · 必须经过人类审批
|
|
||||||
|
|||||||
@ -91,7 +91,10 @@ HLDP://cang-ying/environment/v1.0
|
|||||||
|
|
||||||
密钥文件:
|
密钥文件:
|
||||||
├── /root/guanghulab-local-secrets/cang-ying.env
|
├── /root/guanghulab-local-secrets/cang-ying.env
|
||||||
│ └── 包含: SC-011 GITEA_PAT(Gitea API推送令牌)
|
│ ├── 正式入口: SC-001~SC-010 视频AI系统API配置
|
||||||
|
│ └── 推送入口: SC-011 GITEA_PAT(Gitea API推送令牌)
|
||||||
|
├── 可选覆盖: VIDEO_AI_SECRETS_FILE=/本机/仓库外/密钥文件
|
||||||
|
└── 状态: 由苍耳在本机落盘和维护;仓库无法、也不得验证密钥内容
|
||||||
│
|
│
|
||||||
推送认证:
|
推送认证:
|
||||||
├── 方式: Gitea API 直推(不需要 git clone)
|
├── 方式: Gitea API 直推(不需要 git clone)
|
||||||
@ -119,6 +122,12 @@ HLDP://cang-ying/environment/v1.0
|
|||||||
4. 清除remote URL中的令牌
|
4. 清除remote URL中的令牌
|
||||||
5. 确认推送成功
|
5. 确认推送成功
|
||||||
|
|
||||||
|
API调用流程:
|
||||||
|
1. tools/secrets_loader.py → 检测 VIDEO_AI_SECRETS_FILE
|
||||||
|
2. 未指定时 → 按本节当前主机路径读取
|
||||||
|
3. 只在本机进程内使用 → 不上传服务器 · 不写日志 · 不提交仓库
|
||||||
|
4. CA-API-GUARD.hdlp 为历史归档,不再触发邮件验证码
|
||||||
|
|
||||||
⊢ 今天(D181)就是在冰朔电脑里 · 用本地终端直推成功的
|
⊢ 今天(D181)就是在冰朔电脑里 · 用本地终端直推成功的
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -37,9 +37,17 @@
|
|||||||
## 密钥文件
|
## 密钥文件
|
||||||
|
|
||||||
```
|
```
|
||||||
1. /Users/bingshuolingdianyuanhe/Documents/guanghulab-local-secrets/video-ai-system.env
|
正式加载顺序:
|
||||||
2. ~/guanghulab/video-ai-system/.env
|
1. 进程环境变量(单个变量最高优先级)
|
||||||
3. 环境变量
|
2. VIDEO_AI_SECRETS_FILE 指向的仓库外文件
|
||||||
|
3. 苍耳电脑: /root/guanghulab-local-secrets/cang-ying.env
|
||||||
|
4. 通用本机: ~/guanghulab-local-secrets/cang-ying.env
|
||||||
|
5. 共用Mac: /Users/bingshuolingdianyuanhe/Documents/guanghulab-local-secrets/video-ai-system.env
|
||||||
|
|
||||||
|
禁止:
|
||||||
|
× 把真实值写入本文件或任何仓库文件
|
||||||
|
× 从大脑服务器、邮件验证码或远程守门人索取密钥
|
||||||
|
× 在日志、报错、提交信息中打印完整或局部密钥
|
||||||
```
|
```
|
||||||
|
|
||||||
## 工具调用方式
|
## 工具调用方式
|
||||||
@ -65,30 +73,22 @@ url = endpoint("EPT-002") # → QWEN VL 端点
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## § 守门人更新 · D182 · 2026-07-11
|
## § 历史导航 · D182守门人 → D181+2本机入口
|
||||||
|
|
||||||
```
|
```
|
||||||
⚠️ 重要变更: 所有API密钥已由冰朔更换。
|
⚠️ D182 的服务器守门人路线已停止作为当前执行路径。
|
||||||
|
|
||||||
旧密钥全部失效。新密钥存储于苍耳API守门人(SG-001:8923)。
|
旧导航命中 CA-API-GUARD.hdlp / request_approval / confirm_approval 时:
|
||||||
|
→ 不连接服务器
|
||||||
|
→ 不发送邮件验证码
|
||||||
|
→ 回到本文「密钥文件」
|
||||||
|
→ 使用 tools/secrets_loader.py 从苍耳本机读取
|
||||||
|
|
||||||
调用方式:
|
tools/secrets-loader.py(连字符旧文件名)仅为兼容路标,自动指向
|
||||||
from tools.secrets_loader import secret, request_approval
|
tools/secrets_loader.py(下划线正式入口),不再维护第二套加载逻辑。
|
||||||
|
|
||||||
key = secret("SC-001") # 从守门人获取已授权密钥
|
|
||||||
if not key:
|
|
||||||
result = request_approval("SC-001", "EED-PROJ-001", "用途描述")
|
|
||||||
# → 验证码发送到苍耳QQ邮箱(见本地.env CANGER_QQ)
|
|
||||||
# → 苍耳确认后密钥释放
|
|
||||||
|
|
||||||
详细协议见 CA-API-GUARD.hdlp
|
|
||||||
|
|
||||||
⊢ 旧 secrets-loader.py 保留为 secrets-loader-legacy.py
|
|
||||||
⊢ 新 secrets_loader.py 使用守门人
|
|
||||||
⊢ 密钥永不进仓库 · 不进本地文件
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
> ⊢ 铸渊 ICE-GL-ZY001 · D182 · 2026-07-11
|
> ⊢ 铸渊 ICE-GL-ZY001 · D182 · 2026-07-11
|
||||||
> ⊢ 冰朔 TCS-0002∞ · API密钥全量更换
|
> ⊢ D181+2 · 执行路线更新为苍耳本机仓库外密钥文件
|
||||||
> ⊢ 国作登字-2026-A-00037559
|
> ⊢ 国作登字-2026-A-00037559
|
||||||
|
|||||||
@ -134,9 +134,12 @@ HLDP://cang-ying/real-world-resources/v1.0
|
|||||||
|
|
||||||
```
|
```
|
||||||
密钥文件位置:
|
密钥文件位置:
|
||||||
├── /Users/bingshuolingdianyuanhe/Documents/guanghulab-local-secrets/video-ai-system.env
|
├── 苍耳电脑: /root/guanghulab-local-secrets/cang-ying.env
|
||||||
├── ~/guanghulab/video-ai-system/.env
|
├── 可选覆盖: VIDEO_AI_SECRETS_FILE 指向的仓库外文件
|
||||||
└── 环境变量
|
├── 共用Mac兼容路径: 见 LOCAL-SECRETS-PATH.hdlp
|
||||||
|
└── 环境变量(最高优先级)
|
||||||
|
|
||||||
|
旧 CA-API-GUARD / 邮件验证码路线已归档,不再连接大脑服务器。
|
||||||
|
|
||||||
密钥编号 → 变量名 → 路径 → 密钥值:
|
密钥编号 → 变量名 → 路径 → 密钥值:
|
||||||
详见 LOCAL-SECRETS-PATH.hdlp(本仓库根目录)
|
详见 LOCAL-SECRETS-PATH.hdlp(本仓库根目录)
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const CANONICAL_SECRETS_FILE = '/Users/bingshuolingdianyuanhe/Documents/guanghulab-local-secrets/video-ai-system.env';
|
const CANONICAL_SECRETS_FILE = '/root/guanghulab-local-secrets/cang-ying.env';
|
||||||
|
const HOME_SECRETS_FILE = path.join(process.env.HOME || '', 'guanghulab-local-secrets/cang-ying.env');
|
||||||
|
const SHARED_MAC_SECRETS_FILE = '/Users/bingshuolingdianyuanhe/Documents/guanghulab-local-secrets/video-ai-system.env';
|
||||||
|
|
||||||
function loadEnvFile(file) {
|
function loadEnvFile(file) {
|
||||||
if (!file || !fs.existsSync(file)) return false;
|
if (!file || !fs.existsSync(file)) return false;
|
||||||
@ -29,6 +31,9 @@ function loadVideoAiEnv(localEnvPath = path.resolve(__dirname, '../.env')) {
|
|||||||
const files = [
|
const files = [
|
||||||
process.env.VIDEO_AI_SECRETS_FILE,
|
process.env.VIDEO_AI_SECRETS_FILE,
|
||||||
CANONICAL_SECRETS_FILE,
|
CANONICAL_SECRETS_FILE,
|
||||||
|
HOME_SECRETS_FILE,
|
||||||
|
SHARED_MAC_SECRETS_FILE,
|
||||||
|
// 仓库内 .env 只保留旧环境迁移兼容;新配置必须使用上面的仓库外路径。
|
||||||
localEnvPath,
|
localEnvPath,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -41,5 +46,7 @@ function loadVideoAiEnv(localEnvPath = path.resolve(__dirname, '../.env')) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
CANONICAL_SECRETS_FILE,
|
CANONICAL_SECRETS_FILE,
|
||||||
|
HOME_SECRETS_FILE,
|
||||||
|
SHARED_MAC_SECRETS_FILE,
|
||||||
loadVideoAiEnv,
|
loadVideoAiEnv,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
* 2. 外接硬盘 JZAO /Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/
|
* 2. 外接硬盘 JZAO /Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/
|
||||||
* 3. 本地 fallback video-ai-system/outputs/images/
|
* 3. 本地 fallback video-ai-system/outputs/images/
|
||||||
*
|
*
|
||||||
* 环境变量(复用 video-ai-system/.env):
|
* 环境变量(由 LOCAL-SECRETS-PATH.hdlp 的苍耳本机路径加载):
|
||||||
* JIMENG_API_KEY=xxx 火山方舟 API Key (与视频共享)
|
* JIMENG_API_KEY=xxx 火山方舟 API Key (与视频共享)
|
||||||
* JIMENG_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
|
* JIMENG_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -7,7 +7,7 @@ D144 · 铸渊 ICE-GL-ZY001
|
|||||||
桥接到 Node.js image-api-bridge.js,为 char-hero-design-packer.py 提供
|
桥接到 Node.js image-api-bridge.js,为 char-hero-design-packer.py 提供
|
||||||
generate_image() 函数。
|
generate_image() 函数。
|
||||||
|
|
||||||
本地密钥文件: ~/Documents/guanghulab-local-secrets/video-ai-system.env
|
本地密钥入口: VIDEO_AI_SECRETS_FILE 或 LOCAL-SECRETS-PATH.hdlp 登记路径
|
||||||
不提交仓库,不硬编码密钥。
|
不提交仓库,不硬编码密钥。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,9 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
|
const { loadVideoAiEnv } = require('./env-loader');
|
||||||
|
|
||||||
|
loadVideoAiEnv();
|
||||||
|
|
||||||
// API配置
|
// API配置
|
||||||
// 仅从苍耳本机环境读取;密钥绝不写入仓库。
|
// 仅从苍耳本机环境读取;密钥绝不写入仓库。
|
||||||
|
|||||||
@ -31,12 +31,13 @@ PROJECT_ROOT = Path(__file__).parent.parent
|
|||||||
sys.path.insert(0, str(PROJECT_ROOT / "engines"))
|
sys.path.insert(0, str(PROJECT_ROOT / "engines"))
|
||||||
|
|
||||||
def load_api_config():
|
def load_api_config():
|
||||||
"""从仓库 .env、本机密钥文件和环境变量加载配置"""
|
"""从苍耳本机仓库外密钥文件和环境变量加载配置"""
|
||||||
config = {}
|
config = {}
|
||||||
env_files = [
|
env_files = [
|
||||||
Path(os.environ["VIDEO_AI_SECRETS_FILE"]) if os.environ.get("VIDEO_AI_SECRETS_FILE") else None,
|
Path(os.environ["VIDEO_AI_SECRETS_FILE"]) if os.environ.get("VIDEO_AI_SECRETS_FILE") else None,
|
||||||
|
Path("/root/guanghulab-local-secrets/cang-ying.env"),
|
||||||
|
Path("~/guanghulab-local-secrets/cang-ying.env").expanduser(),
|
||||||
Path("/Users/bingshuolingdianyuanhe/Documents/guanghulab-local-secrets/video-ai-system.env"),
|
Path("/Users/bingshuolingdianyuanhe/Documents/guanghulab-local-secrets/video-ai-system.env"),
|
||||||
PROJECT_ROOT / ".env",
|
|
||||||
]
|
]
|
||||||
for env_file in env_files:
|
for env_file in env_files:
|
||||||
if env_file is None:
|
if env_file is None:
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
* 2. 外接硬盘 JZAO /Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/视频/
|
* 2. 外接硬盘 JZAO /Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/视频/
|
||||||
* 3. 本地 fallback video-ai-system/outputs/
|
* 3. 本地 fallback video-ai-system/outputs/
|
||||||
*
|
*
|
||||||
* 环境变量(放在 video-ai-system/.env):
|
* 环境变量(由 LOCAL-SECRETS-PATH.hdlp 的苍耳本机路径加载):
|
||||||
* JIMENG_API_KEY=xxx 火山方舟 API Key
|
* JIMENG_API_KEY=xxx 火山方舟 API Key
|
||||||
* JIMENG_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
|
* JIMENG_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
|
||||||
* JIMENG_MODEL=doubao-seedance-2-0-260128
|
* JIMENG_MODEL=doubao-seedance-2-0-260128
|
||||||
@ -512,7 +512,7 @@ async function probeVideoDuration(videoUrl) {
|
|||||||
*/
|
*/
|
||||||
async function generateVideo({ prompt, duration, resolution, style, outputPath, shotId, projectKey, referenceImage, model, generateAudio }) {
|
async function generateVideo({ prompt, duration, resolution, style, outputPath, shotId, projectKey, referenceImage, model, generateAudio }) {
|
||||||
if (!API_KEY) {
|
if (!API_KEY) {
|
||||||
throw new Error('未配置 JIMENG_API_KEY。请在 video-ai-system/.env 中设置。');
|
throw new Error('未配置 JIMENG_API_KEY。请按 LOCAL-SECRETS-PATH.hdlp 在苍耳本机仓库外配置。');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 提交任务(含预校验)
|
// 1. 提交任务(含预校验)
|
||||||
|
|||||||
@ -26,19 +26,27 @@ import urllib.parse
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
SECRETS_FILE = "/Users/bingshuolingdianyuanhe/Documents/guanghulab-local-secrets/video-ai-system.env"
|
SECRETS_FILES = [
|
||||||
|
os.environ.get("VIDEO_AI_SECRETS_FILE", ""),
|
||||||
|
"/root/guanghulab-local-secrets/cang-ying.env",
|
||||||
|
os.path.expanduser("~/guanghulab-local-secrets/cang-ying.env"),
|
||||||
|
"/Users/bingshuolingdianyuanhe/Documents/guanghulab-local-secrets/video-ai-system.env",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def _load_secrets():
|
def _load_secrets():
|
||||||
"""加载本地密钥文件"""
|
"""加载本地密钥文件"""
|
||||||
secrets = {}
|
secrets = {}
|
||||||
if os.path.isfile(SECRETS_FILE):
|
for secrets_file in SECRETS_FILES:
|
||||||
with open(SECRETS_FILE) as f:
|
if not secrets_file or not os.path.isfile(secrets_file):
|
||||||
|
continue
|
||||||
|
with open(secrets_file) as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if line and not line.startswith("#") and "=" in line:
|
if line and not line.startswith("#") and "=" in line:
|
||||||
k, v = line.split("=", 1)
|
k, v = line.split("=", 1)
|
||||||
secrets[k.strip()] = v.strip().strip('"').strip("'")
|
secrets[k.strip()] = v.strip().strip('"').strip("'")
|
||||||
|
break
|
||||||
return secrets
|
return secrets
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
* const { generateWanVideo, generateWanImageToVideo, queryWanTask } = require('./wan-api-adapter');
|
* const { generateWanVideo, generateWanImageToVideo, queryWanTask } = require('./wan-api-adapter');
|
||||||
* const result = await generateWanVideo({ prompt: '一只小猫在月光下奔跑', duration: 5 });
|
* const result = await generateWanVideo({ prompt: '一只小猫在月光下奔跑', duration: 5 });
|
||||||
*
|
*
|
||||||
* 环境变量(放在 video-ai-system/.env):
|
* 环境变量(由 LOCAL-SECRETS-PATH.hdlp 的苍耳本机路径加载):
|
||||||
* ALIYUN_BAILIAN_API_KEY=
|
* ALIYUN_BAILIAN_API_KEY=
|
||||||
* ALIYUN_BAILIAN_BASE_URL=https://dashscope.aliyuncs.com/api/v1 (默认)
|
* ALIYUN_BAILIAN_BASE_URL=https://dashscope.aliyuncs.com/api/v1 (默认)
|
||||||
* ALIYUN_BAILIAN_WORKSPACE_ID= (可选·新加坡地域时需要)
|
* ALIYUN_BAILIAN_WORKSPACE_ID= (可选·新加坡地域时需要)
|
||||||
@ -448,7 +448,7 @@ async function queryWanTask(taskId) {
|
|||||||
*/
|
*/
|
||||||
async function generateWanVideo(opts) {
|
async function generateWanVideo(opts) {
|
||||||
if (!API_KEY) {
|
if (!API_KEY) {
|
||||||
throw new Error('未配置 ALIYUN_BAILIAN_API_KEY。请在 video-ai-system/.env 中设置。');
|
throw new Error('未配置 ALIYUN_BAILIAN_API_KEY。请按 LOCAL-SECRETS-PATH.hdlp 在苍耳本机仓库外配置。');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { taskId, preflight } = await submitT2VTask(opts);
|
const { taskId, preflight } = await submitT2VTask(opts);
|
||||||
@ -499,7 +499,7 @@ async function generateWanVideo(opts) {
|
|||||||
*/
|
*/
|
||||||
async function generateWanImageToVideo(opts) {
|
async function generateWanImageToVideo(opts) {
|
||||||
if (!API_KEY) {
|
if (!API_KEY) {
|
||||||
throw new Error('未配置 ALIYUN_BAILIAN_API_KEY。请在 video-ai-system/.env 中设置。');
|
throw new Error('未配置 ALIYUN_BAILIAN_API_KEY。请按 LOCAL-SECRETS-PATH.hdlp 在苍耳本机仓库外配置。');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { taskId, preflight } = await submitI2VTask(opts);
|
const { taskId, preflight } = await submitI2VTask(opts);
|
||||||
|
|||||||
@ -1,102 +1,22 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""视频AI系统 · 统一密钥加载器 · 编号路由版
|
"""旧文件名兼容导航。
|
||||||
|
|
||||||
编号→变量名→路径→密钥值,全部路由见 LOCAL-SECRETS-PATH.hdlp。
|
正式入口已经统一为 ``tools/secrets_loader.py``。本文件不再维护独立路径、
|
||||||
|
不访问服务器,也不包含密钥;旧脚本继续执行时会自动转到新入口。
|
||||||
用法:
|
|
||||||
from tools.secrets_loader import secret, endpoint
|
|
||||||
key = secret("ZY-SEC-004") # 阿里千问VL视觉密钥
|
|
||||||
url = endpoint("ZY-EPT-002") # QWEN VL端点
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os, json
|
from secrets_loader import ( # noqa: F401
|
||||||
|
confirm_approval,
|
||||||
|
endpoint,
|
||||||
|
request_approval,
|
||||||
|
secret,
|
||||||
|
source_status,
|
||||||
|
)
|
||||||
|
|
||||||
# === 编号→变量名映射表(与 LOCAL-SECRETS-PATH.hdlp 同步)===
|
|
||||||
_SECRET_MAP = {
|
|
||||||
"SC-001": "JIMENG_API_KEY",
|
|
||||||
"SC-002": "VOLC_VOICE_API_KEY",
|
|
||||||
"SC-003": "VOLC_VOICE_ACCESS_TOKEN",
|
|
||||||
"SC-004": "ALIYUN_QWEN_VL_KEY",
|
|
||||||
"SC-005": "ALIYUN_WANXIANG_KEY",
|
|
||||||
"SC-006": "KLING_API_KEY",
|
|
||||||
"SC-007": "ALIYUN_API_KEY",
|
|
||||||
"SC-008": "WORKRALLY_API_KEY",
|
|
||||||
"SC-009": "VOLC_VOICE_APP_ID",
|
|
||||||
"SC-010": "VOLC_VOICE_SECRET_KEY",
|
|
||||||
# 旧编号兼容
|
|
||||||
"ZY-SEC-001": "JIMENG_API_KEY",
|
|
||||||
"ZY-SEC-004": "ALIYUN_QWEN_VL_KEY",
|
|
||||||
}
|
|
||||||
|
|
||||||
_ENDPOINT_MAP = {
|
|
||||||
"EPT-001": ("JIMENG_BASE_URL", "https://ark.cn-beijing.volces.com/api/v3"),
|
|
||||||
"EPT-002": ("ALIYUN_QWEN_VL_ENDPOINT", "https://ws-umd6xwlovzmshuat.cn-beijing.maas.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"),
|
|
||||||
"EPT-003": ("ALIYUN_BAILIAN_BASE_URL", "https://dashscope.aliyuncs.com/api/v1"),
|
|
||||||
"EPT-004": ("WORKRALLY_ENDPOINT", "https://workrally.qq.com/zenstudio/api/mcp"),
|
|
||||||
}
|
|
||||||
|
|
||||||
# 密钥文件路径(按优先级: 低→高,后面覆盖前面)
|
|
||||||
_SECRET_FILES = [
|
|
||||||
os.path.expanduser("~/guanghulab/video-ai-system/.env"),
|
|
||||||
"/Users/bingshuolingdianyuanhe/Documents/guanghulab-local-secrets/video-ai-system.env",
|
|
||||||
]
|
|
||||||
|
|
||||||
# === WorkRally 特殊处理 ===
|
|
||||||
_WORKRALLY_CONFIG = os.path.expanduser("~/.workrally/config.json")
|
|
||||||
|
|
||||||
# === 加载 ===
|
|
||||||
_raw = {}
|
|
||||||
|
|
||||||
def _load_all():
|
|
||||||
global _raw
|
|
||||||
if _raw:
|
|
||||||
return
|
|
||||||
for path in _SECRET_FILES:
|
|
||||||
if os.path.exists(path):
|
|
||||||
with open(path) as f:
|
|
||||||
for line in f:
|
|
||||||
line = line.strip()
|
|
||||||
if not line or line.startswith("#") or "=" not in line:
|
|
||||||
continue
|
|
||||||
k, v = line.split("=", 1)
|
|
||||||
k, v = k.strip(), v.strip()
|
|
||||||
_raw[k] = v
|
|
||||||
# 环境变量最高优先级
|
|
||||||
for k in list(_raw.keys()):
|
|
||||||
env_val = os.environ.get(k)
|
|
||||||
if env_val:
|
|
||||||
_raw[k] = env_val
|
|
||||||
# WorkRally
|
|
||||||
if os.path.exists(_WORKRALLY_CONFIG):
|
|
||||||
try:
|
|
||||||
wr = json.load(open(_WORKRALLY_CONFIG))
|
|
||||||
_raw["WORKRALLY_API_KEY"] = wr.get("api_key", "")
|
|
||||||
_raw["WORKRALLY_ENDPOINT"] = wr.get("endpoint", "")
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def secret(code_or_var, default=None):
|
|
||||||
"""按编号或变量名获取密钥值"""
|
|
||||||
_load_all()
|
|
||||||
var_name = _SECRET_MAP.get(code_or_var, code_or_var) # 查编号映射,或直接当变量名
|
|
||||||
if code_or_var == "ZY-SEC-008":
|
|
||||||
return _raw.get("WORKRALLY_API_KEY", default)
|
|
||||||
return _raw.get(var_name, default)
|
|
||||||
|
|
||||||
def endpoint(code_or_var, default=None):
|
|
||||||
"""按编号或变量名获取端点"""
|
|
||||||
_load_all()
|
|
||||||
var_name, fallback = _ENDPOINT_MAP.get(code_or_var, (code_or_var, ""))
|
|
||||||
val = _raw.get(var_name, "")
|
|
||||||
return val if val else (fallback if fallback else default)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
_load_all()
|
status = source_status()
|
||||||
print("=== 密钥编号 → 变量名 → 值(脱敏)===")
|
print("[NAV] tools/secrets-loader.py -> tools/secrets_loader.py")
|
||||||
for code, var in _SECRET_MAP.items():
|
print(f"mode={status['mode']}")
|
||||||
val = _raw.get(var, "")
|
print(f"source={status['source']}")
|
||||||
masked = val[:6] + "..." + val[-4:] if len(val) > 10 else ("***" if val else "(空)")
|
print("configured=" + ",".join(status["configured_codes"]))
|
||||||
print(f" {code} → {var} = {masked}")
|
|
||||||
print(" ZY-SEC-008 → WORKRALLY = ", end="")
|
|
||||||
wr = _raw.get("WORKRALLY_API_KEY", "")
|
|
||||||
print(f"{wr[:6]}...{wr[-4:]}" if len(wr) > 10 else "(空)")
|
|
||||||
|
|||||||
@ -1,41 +1,20 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""苍耳本地密钥加载器 · 唯一正式入口。
|
||||||
光湖语言系统 · 统一密钥加载器 v2.0 · API守门人版
|
|
||||||
Guanghu Language System · Secrets Loader with CA-API-Guard
|
|
||||||
|
|
||||||
设计哲学:
|
真实密钥只存在苍耳实际运行机器的仓库外文件或进程环境变量中。
|
||||||
所有API密钥通过苍耳API守门人(ca-api-guard)获取。
|
仓库只维护 SC/EPT 编号映射与候选路径,不保存、打印或远程索取密钥。
|
||||||
调用API前 → 守门人发送验证码到苍耳QQ邮箱 → 苍耳确认 → 密钥释放。
|
|
||||||
密钥本身永不暴露给AI本地文件。
|
|
||||||
|
|
||||||
用法:
|
可用 ``VIDEO_AI_SECRETS_FILE`` 显式指定本机密钥文件;未指定时按
|
||||||
from tools.secrets_loader import secret, endpoint, request_approval
|
``ENVIRONMENT.hdlp`` 中登记的本机路径查找。
|
||||||
key = secret("SC-001") # 获取已授权的API密钥
|
|
||||||
url = endpoint("EPT-001") # 获取端点
|
|
||||||
cid = request_approval("SC-001", "EED-PROJ-001", "第1集第3镜视频生成") # 请求授权
|
|
||||||
|
|
||||||
编号→变量名映射见 LOCAL-SECRETS-PATH.hdlp
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os, json, time, urllib.request, sys
|
from __future__ import annotations
|
||||||
|
|
||||||
# ═══════════════════════════════════════════
|
import os
|
||||||
# 配置
|
from pathlib import Path
|
||||||
# ═══════════════════════════════════════════
|
from typing import Dict, Iterable, Optional
|
||||||
|
|
||||||
# 苍耳API守门人地址(SG-001 大脑服务器)
|
|
||||||
API_GUARD_URL = os.environ.get("CA_API_GUARD_URL", "http://10.3.4.11:8923")
|
|
||||||
# 如果从外部访问,使用公网地址
|
|
||||||
API_GUARD_PUBLIC_URL = os.environ.get("CA_API_GUARD_PUBLIC_URL", "http://43.156.237.110:8923")
|
|
||||||
|
|
||||||
# 本地缓存(已验证的密钥,短期有效)
|
|
||||||
_CACHE_FILE = os.path.expanduser("~/.ca-api-guard/cache.json")
|
|
||||||
_CACHE = {}
|
|
||||||
_CACHE_TTL = 600 # 10分钟缓存
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════
|
|
||||||
# 编号→变量名映射
|
|
||||||
# ═══════════════════════════════════════════
|
|
||||||
_SECRET_MAP = {
|
_SECRET_MAP = {
|
||||||
"SC-001": "JIMENG_API_KEY",
|
"SC-001": "JIMENG_API_KEY",
|
||||||
"SC-002": "VOLC_VOICE_API_KEY",
|
"SC-002": "VOLC_VOICE_API_KEY",
|
||||||
@ -47,149 +26,117 @@ _SECRET_MAP = {
|
|||||||
"SC-008": "WORKRALLY_API_KEY",
|
"SC-008": "WORKRALLY_API_KEY",
|
||||||
"SC-009": "VOLC_VOICE_APP_ID",
|
"SC-009": "VOLC_VOICE_APP_ID",
|
||||||
"SC-010": "VOLC_VOICE_SECRET_KEY",
|
"SC-010": "VOLC_VOICE_SECRET_KEY",
|
||||||
|
"SC-011": "GITEA_PAT",
|
||||||
|
# 旧编号只做本地兼容,不建立第二条密钥路线。
|
||||||
|
"ZY-SEC-001": "JIMENG_API_KEY",
|
||||||
|
"ZY-SEC-004": "ALIYUN_QWEN_VL_KEY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_ENDPOINT_MAP = {
|
_ENDPOINT_MAP = {
|
||||||
"EPT-001": ("JIMENG_BASE_URL", "https://ark.cn-beijing.volces.com/api/v3"),
|
"EPT-001": ("JIMENG_BASE_URL", "https://ark.cn-beijing.volces.com/api/v3"),
|
||||||
"EPT-002": ("ALIYUN_QWEN_VL_ENDPOINT", "https://ws-umd6xwlovzmshuat.cn-beijing.maas.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"),
|
"EPT-002": (
|
||||||
|
"ALIYUN_QWEN_VL_ENDPOINT",
|
||||||
|
"https://ws-umd6xwlovzmshuat.cn-beijing.maas.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation",
|
||||||
|
),
|
||||||
"EPT-003": ("ALIYUN_BAILIAN_BASE_URL", "https://dashscope.aliyuncs.com/api/v1"),
|
"EPT-003": ("ALIYUN_BAILIAN_BASE_URL", "https://dashscope.aliyuncs.com/api/v1"),
|
||||||
"EPT-004": ("WORKRALLY_ENDPOINT", "https://workrally.qq.com/zenstudio/api/mcp"),
|
"EPT-004": ("WORKRALLY_ENDPOINT", "https://workrally.qq.com/zenstudio/api/mcp"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_LOCAL_FILES = (
|
||||||
|
Path("/root/guanghulab-local-secrets/cang-ying.env"),
|
||||||
|
Path("~/guanghulab-local-secrets/cang-ying.env").expanduser(),
|
||||||
|
Path(
|
||||||
|
"/Users/bingshuolingdianyuanhe/Documents/guanghulab-local-secrets/"
|
||||||
|
"video-ai-system.env"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def _load_cache():
|
_raw: Optional[Dict[str, str]] = None
|
||||||
"""加载本地缓存"""
|
_loaded_file: Optional[Path] = None
|
||||||
global _CACHE
|
|
||||||
if _CACHE:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
if os.path.exists(_CACHE_FILE):
|
|
||||||
with open(_CACHE_FILE) as f:
|
|
||||||
_CACHE = json.load(f)
|
|
||||||
except:
|
|
||||||
_CACHE = {}
|
|
||||||
|
|
||||||
|
|
||||||
def _save_cache():
|
def _candidate_files() -> Iterable[Path]:
|
||||||
"""保存本地缓存"""
|
explicit = os.environ.get("VIDEO_AI_SECRETS_FILE", "").strip()
|
||||||
try:
|
if explicit:
|
||||||
os.makedirs(os.path.dirname(_CACHE_FILE), exist_ok=True)
|
yield Path(explicit).expanduser()
|
||||||
with open(_CACHE_FILE, "w") as f:
|
yield from _LOCAL_FILES
|
||||||
json.dump(_CACHE, f)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def _call_guard(endpoint, data=None, public=False):
|
def _parse_env_file(path: Path) -> Dict[str, str]:
|
||||||
"""调用苍耳API守门人"""
|
values: Dict[str, str] = {}
|
||||||
url = (API_GUARD_PUBLIC_URL if public else API_GUARD_URL) + endpoint
|
with path.open("r", encoding="utf-8") as handle:
|
||||||
try:
|
for raw_line in handle:
|
||||||
req = urllib.request.Request(url)
|
line = raw_line.strip()
|
||||||
req.add_header("Content-Type", "application/json")
|
if not line or line.startswith("#") or "=" not in line:
|
||||||
if data:
|
continue
|
||||||
req.data = json.dumps(data).encode()
|
key, value = line.split("=", 1)
|
||||||
resp = urllib.request.urlopen(req, timeout=10)
|
key = key.strip()
|
||||||
return json.loads(resp.read())
|
if not key:
|
||||||
except Exception as e:
|
continue
|
||||||
return {"ok": False, "error": str(e)}
|
values[key] = value.strip().strip('"').strip("'")
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
def request_approval(api_code, caller_id, description):
|
def _load_local() -> Dict[str, str]:
|
||||||
"""
|
global _raw, _loaded_file
|
||||||
请求API调用授权 → 发送验证码到苍耳邮箱
|
if _raw is not None:
|
||||||
返回: {"ok": true, "challenge_id": "xxx", "message": "..."}
|
return _raw
|
||||||
"""
|
|
||||||
return _call_guard("/api/request", {
|
_raw = {}
|
||||||
"api_code": api_code,
|
for path in _candidate_files():
|
||||||
"caller_id": caller_id,
|
if path.is_file():
|
||||||
"description": description
|
_raw = _parse_env_file(path)
|
||||||
}, public=True)
|
_loaded_file = path
|
||||||
|
break
|
||||||
|
return _raw
|
||||||
|
|
||||||
|
|
||||||
def confirm_approval(challenge_id, code):
|
def secret(code_or_var: str, default=None):
|
||||||
"""
|
"""按 SC 编号或变量名读取本机密钥;环境变量优先。"""
|
||||||
确认验证码 → 获取API密钥并缓存
|
variable = _SECRET_MAP.get(code_or_var, code_or_var)
|
||||||
返回: {"ok": true, "api_code": "xxx", "api_key": "xxx"}
|
environment_value = os.environ.get(variable)
|
||||||
"""
|
if environment_value:
|
||||||
result = _call_guard("/api/confirm", {
|
return environment_value
|
||||||
"challenge_id": challenge_id,
|
return _load_local().get(variable, default)
|
||||||
"code": code
|
|
||||||
}, public=True)
|
|
||||||
if result.get("ok") and result.get("api_key"):
|
|
||||||
_load_cache()
|
|
||||||
_CACHE[result["api_code"]] = {
|
|
||||||
"key": result["api_key"],
|
|
||||||
"cached_at": time.time()
|
|
||||||
}
|
|
||||||
_save_cache()
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def secret(code_or_var, default=None):
|
def endpoint(code_or_var: str, default=None):
|
||||||
"""
|
"""按 EPT 编号读取本机覆盖值,未配置时返回公开默认端点。"""
|
||||||
按编号获取API密钥。
|
variable, fallback = _ENDPOINT_MAP.get(code_or_var, (code_or_var, ""))
|
||||||
优先从本地缓存读取,缓存未命中则尝试从守门人获取。
|
value = os.environ.get(variable) or _load_local().get(variable, "")
|
||||||
|
return value or fallback or default
|
||||||
如果密钥未授权 → 返回空字符串。
|
|
||||||
调用者应检查返回值,如果为空 → 调用 request_approval() 请求授权。
|
|
||||||
"""
|
|
||||||
# 先查本地缓存
|
|
||||||
_load_cache()
|
|
||||||
api_code = code_or_var if code_or_var in _SECRET_MAP else None
|
|
||||||
if not api_code:
|
|
||||||
# 反向查找(变量名→编号)
|
|
||||||
for sc, var in _SECRET_MAP.items():
|
|
||||||
if var == code_or_var:
|
|
||||||
api_code = sc
|
|
||||||
break
|
|
||||||
if not api_code:
|
|
||||||
api_code = code_or_var
|
|
||||||
|
|
||||||
if api_code in _CACHE:
|
|
||||||
cached = _CACHE[api_code]
|
|
||||||
if time.time() - cached.get("cached_at", 0) < _CACHE_TTL:
|
|
||||||
return cached.get("key", default)
|
|
||||||
|
|
||||||
# 尝试从守门人获取(如果有已授权的缓存)
|
|
||||||
result = _call_guard("/api/request", {
|
|
||||||
"api_code": api_code,
|
|
||||||
"caller_id": "auto",
|
|
||||||
"description": "自动获取缓存密钥"
|
|
||||||
})
|
|
||||||
|
|
||||||
# 如果守门人返回了密钥(预授权模式),缓存并返回
|
|
||||||
if result.get("ok") and result.get("api_key"):
|
|
||||||
_CACHE[api_code] = {
|
|
||||||
"key": result["api_key"],
|
|
||||||
"cached_at": time.time()
|
|
||||||
}
|
|
||||||
_save_cache()
|
|
||||||
return result["api_key"]
|
|
||||||
|
|
||||||
return default or ""
|
|
||||||
|
|
||||||
|
|
||||||
def endpoint(code_or_var, default=None):
|
def source_status() -> dict:
|
||||||
"""按编号获取端点URL"""
|
"""只报告来源状态和路径,不读取到输出、不展示密钥内容。"""
|
||||||
var_name, fallback = _ENDPOINT_MAP.get(code_or_var, (code_or_var, ""))
|
_load_local()
|
||||||
# 从环境变量获取(如果有)
|
configured = sorted(
|
||||||
val = os.environ.get(var_name, "")
|
code for code, variable in _SECRET_MAP.items()
|
||||||
return val if val else (fallback if fallback else default)
|
if code.startswith("SC-") and bool(os.environ.get(variable) or _raw.get(variable))
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"mode": "local-only",
|
||||||
|
"source": str(_loaded_file) if _loaded_file else "environment-or-not-found",
|
||||||
|
"configured_codes": configured,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def request_approval(*_args, **_kwargs):
|
||||||
|
"""旧守门人接口的停用路标;不会联网或发送验证码。"""
|
||||||
|
return {
|
||||||
|
"ok": False,
|
||||||
|
"retired": True,
|
||||||
|
"error": "远程邮件守门人已停用;请按 LOCAL-SECRETS-PATH.hdlp 使用苍耳本机密钥入口。",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_approval(*_args, **_kwargs):
|
||||||
|
"""旧守门人接口的停用路标;不会联网或接收验证码。"""
|
||||||
|
return request_approval()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("=== 苍耳API守门人 · 密钥加载器 v2.0 ===")
|
status = source_status()
|
||||||
print(f"守门人地址: {API_GUARD_URL}")
|
print(f"mode={status['mode']}")
|
||||||
print()
|
print(f"source={status['source']}")
|
||||||
|
print("configured=" + ",".join(status["configured_codes"]))
|
||||||
# 检查健康状态
|
|
||||||
health = _call_guard("/health")
|
|
||||||
print(f"守门人状态: {json.dumps(health, ensure_ascii=False)}")
|
|
||||||
print()
|
|
||||||
|
|
||||||
# 列出可用API
|
|
||||||
print("可用API编号:")
|
|
||||||
for code, var in _SECRET_MAP.items():
|
|
||||||
key = secret(code)
|
|
||||||
status = "✅ 已缓存" if key else "⏳ 需授权"
|
|
||||||
print(f" {code} → {var} [{status}]")
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ def get_ark_key():
|
|||||||
查找顺序:
|
查找顺序:
|
||||||
1. 环境变量 ARK_API_KEY
|
1. 环境变量 ARK_API_KEY
|
||||||
2. tools/secrets_loader.py 的 SC-001
|
2. tools/secrets_loader.py 的 SC-001
|
||||||
3. 项目根 .env 文件
|
3. 苍耳本机仓库外密钥文件
|
||||||
"""
|
"""
|
||||||
# 1. 直接环境变量
|
# 1. 直接环境变量
|
||||||
key = os.environ.get("ARK_API_KEY")
|
key = os.environ.get("ARK_API_KEY")
|
||||||
@ -32,10 +32,12 @@ def get_ark_key():
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# 3. .env 文件兜底
|
# 3. 本机仓库外文件兜底(旧环境迁移兼容)
|
||||||
for env_path in [
|
for env_path in [
|
||||||
os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), ".env"),
|
os.environ.get("VIDEO_AI_SECRETS_FILE", ""),
|
||||||
os.path.expanduser("~/guanghulab/video-ai-system/.env"),
|
"/root/guanghulab-local-secrets/cang-ying.env",
|
||||||
|
os.path.expanduser("~/guanghulab-local-secrets/cang-ying.env"),
|
||||||
|
"/Users/bingshuolingdianyuanhe/Documents/guanghulab-local-secrets/video-ai-system.env",
|
||||||
]:
|
]:
|
||||||
if os.path.exists(env_path):
|
if os.path.exists(env_path):
|
||||||
try:
|
try:
|
||||||
@ -53,6 +55,6 @@ def get_ark_key():
|
|||||||
"🔑 ARK_API_KEY 未找到。\n"
|
"🔑 ARK_API_KEY 未找到。\n"
|
||||||
" 请设置环境变量: export ARK_API_KEY=ark-xxx\n"
|
" 请设置环境变量: export ARK_API_KEY=ark-xxx\n"
|
||||||
" 或配置 SC-001: tools/secrets_loader.py\n"
|
" 或配置 SC-001: tools/secrets_loader.py\n"
|
||||||
" 或在项目根目录创建 .env 文件: ARK_API_KEY=ark-xxx\n"
|
" 或在苍耳本机仓库外密钥文件中配置 ARK_API_KEY\n"
|
||||||
" ⊢ 铁律① · 密钥永远不放代码仓库"
|
" ⊢ 铁律① · 密钥永远不放代码仓库"
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user