feat: 添加 K8S 部署功能
- 新增 src/k8s-client.ts: K8S 客户端封装 - 新增 src/deploy.ts: 部署/删除/状态查询逻辑 - 新增 scripts/deploy.ts: 命令行部署工具 - 更新 .env.example: 添加 K8S 配置项 - 安装 @kubernetes/client-node@1.3.0 依赖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -7,3 +7,11 @@ ANTHROPIC_BASE_URL=https://api.anthropic.com
|
||||
|
||||
# 服务端口(默认 3000)
|
||||
PORT=3000
|
||||
|
||||
# K8S 部署配置
|
||||
K8S_API_URL=http://10.0.0.52:23972
|
||||
K8S_API_TOKEN=your_fixed_token_here
|
||||
|
||||
# 域名配置
|
||||
DEV_HOST="*.dev.d8d.fun"
|
||||
DEV_TLS_SECRET_NAME="dev-d8d-tls"
|
||||
|
||||
167
_bmad-output/implementation-artifacts/tech-spec-k8s-deploy.md
Normal file
167
_bmad-output/implementation-artifacts/tech-spec-k8s-deploy.md
Normal file
@@ -0,0 +1,167 @@
|
||||
---
|
||||
title: 'K8S 部署脚本'
|
||||
slug: 'k8s-deploy'
|
||||
created: '2026-03-20T07:00:00.000Z'
|
||||
status: 'ready-for-dev'
|
||||
stepsCompleted: [1, 2, 3, 4]
|
||||
tech_stack: ['TypeScript', '@kubernetes/client-node', 'Node.js', 'dotenv', 'express']
|
||||
files_to_modify:
|
||||
- 'package.json'
|
||||
- 'scripts/deploy.ts'
|
||||
code_patterns:
|
||||
- 'K8S 客户端: @kubernetes/client-node SDK 封装'
|
||||
- '部署流程: Deployment → Service → Ingress'
|
||||
- 'EIP: k8s.aliyun.com/eci-open-enable-eip 自动创建'
|
||||
- '抢占式实例: 未启用(可选优化)'
|
||||
- '命名空间: d8d-proxy (专用命名空间)'
|
||||
- '实例规格: ecs.t5-lc2m1.nano (完整字符串)'
|
||||
- 'Ingress 域名: {name}-{port}.{hostSuffix}'
|
||||
test_patterns: []
|
||||
---
|
||||
|
||||
# Tech-Spec: K8S 部署脚本
|
||||
|
||||
**Created:** 2026-03-20T07:00:00.000Z
|
||||
|
||||
## Overview
|
||||
|
||||
### Problem Statement
|
||||
|
||||
Claude API 代理服务已完成,需要添加 K8S 部署脚本来动态创建和管理 Pod 实例。每个 Pod 需要独立的出口 IP (通过 EIP),以便分散请求绕过官方 API 的 IP 并发限制。
|
||||
|
||||
### Solution
|
||||
|
||||
1. 安装 `@kubernetes/client-node` 依赖
|
||||
2. 使用已实现的 `src/k8s-client.ts` K8S 客户端封装
|
||||
3. 使用已实现的 `src/deploy.ts` 部署模块
|
||||
4. 创建 `scripts/deploy.ts` 命令行入口
|
||||
|
||||
### Scope
|
||||
|
||||
**In Scope:**
|
||||
- 安装 @kubernetes/client-node 依赖
|
||||
- 命令行部署工具
|
||||
- 部署脚本:创建/删除 Deployment、Service、Ingress
|
||||
- 自动 EIP 创建支持
|
||||
- 专用命名空间 `d8d-proxy`
|
||||
- 健康检查配置
|
||||
|
||||
**Out of Scope:**
|
||||
- API Key 池管理(由 D8D 主项目处理)
|
||||
- 复杂的监控和告警
|
||||
- HPA 自动扩缩容
|
||||
|
||||
## Context for Development
|
||||
|
||||
### Codebase Patterns
|
||||
|
||||
**已实现的文件:**
|
||||
|
||||
| 文件 | 状态 | 描述 |
|
||||
|------|------|------|
|
||||
| `src/k8s-client.ts` | ✅ 已完成 | K8S SDK 封装类 |
|
||||
| `src/deploy.ts` | ✅ 已完成 | 部署/删除/状态查询功能 |
|
||||
| `.env.example` | ✅ 已完成 | K8S 环境变量配置 |
|
||||
| `package.json` | ❌ 缺少依赖 | 需添加 @kubernetes/client-node |
|
||||
| `scripts/deploy.ts` | ❌ 未创建 | 命令行入口 |
|
||||
|
||||
**阿里云 ECI 注解(已实现写法):**
|
||||
- `k8s.aliyun.com/eci-use-specs: "ecs.t5-lc2m1.nano"` - 指定 ECI 规格
|
||||
- `k8s.aliyun.com/eci-open-enable-eip: "true"` - 自动创建 EIP
|
||||
- `k8s.aliyun.com/eci-eip-bandwidth: "5"` - EIP 带宽 5Mbps
|
||||
- `k8s.aliyun.com/eci-eip-internet-charge-type: "PayByBandwidth"` - 按带宽计费
|
||||
|
||||
### Files to Reference
|
||||
|
||||
| File | Purpose |
|
||||
| ---- | ------- |
|
||||
| `src/k8s-client.ts` | K8S SDK 封装(已实现) |
|
||||
| `src/deploy.ts` | 部署模块(已实现) |
|
||||
|
||||
### Technical Decisions
|
||||
|
||||
- **命名空间**: `d8d-proxy` (专用,避免与 d8d-dev/d8d-prd 冲突)
|
||||
- **K8S 客户端**: 使用 @kubernetes/client-node SDK
|
||||
- **认证方式**: K8S_API_URL + K8S_API_TOKEN 环境变量
|
||||
- **EIP 配置**: 自动创建(通过 ECI annotation)
|
||||
- **容器规格**: `ecs.t5-lc2m1.nano` (0.5 CPU, 1G 内存)
|
||||
- **Ingress 域名**: `{name}-{port}.{hostSuffix}`
|
||||
- **镜像**: registry.cn-beijing.aliyuncs.com/d8dcloud/claude-api-proxy:${VERSION}
|
||||
- **健康检查**: /health 端点
|
||||
- **Ingress**: 无需 SSO 认证(代理服务)
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Tasks
|
||||
|
||||
**Task 1: 安装 K8S 依赖**
|
||||
- [ ] 执行 `pnpm add @kubernetes/client-node`
|
||||
- [ ] 验证依赖安装成功
|
||||
|
||||
**Task 2: 创建命令行入口**
|
||||
- [ ] File: `scripts/deploy.ts`
|
||||
- [ ] Action: 创建命令行工具,支持以下命令:
|
||||
- `deploy <name> <image>` - 部署代理服务
|
||||
- `delete <name>` - 删除代理服务
|
||||
- `status <name>` - 查询服务状态
|
||||
- [ ] Notes: 使用 tsx 运行,参考参数:
|
||||
- `--replicas` 副本数(默认 1)
|
||||
- `--port` 端口(默认 3000)
|
||||
- `--eip-bandwidth` EIP 带宽(默认 5)
|
||||
|
||||
**Task 3: 添加 npm 脚本**
|
||||
- [ ] File: `package.json`
|
||||
- [ ] Action: 添加部署相关脚本:
|
||||
```json
|
||||
"deploy": "tsx scripts/deploy.ts"
|
||||
```
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- [ ] **AC1: 依赖安装成功**
|
||||
- Given: 执行 pnpm install
|
||||
- When: 安装 @kubernetes/client-node
|
||||
- Then: 依赖正确安装,TypeScript 编译无错误
|
||||
|
||||
- [ ] **AC2: 部署命令可执行**
|
||||
- Given: 环境变量 K8S_API_URL 和 K8S_API_TOKEN 已配置
|
||||
- When: 执行 `pnpm deploy <name> <image>`
|
||||
- Then: 成功创建 Deployment、Service、Ingress,返回访问 URL
|
||||
|
||||
- [ ] **AC3: EIP 自动创建**
|
||||
- Given: 部署时启用 EIP 选项(默认启用)
|
||||
- When: Pod 启动
|
||||
- Then: 自动创建并绑定 EIP
|
||||
|
||||
- [ ] **AC4: 删除功能正常**
|
||||
- Given: 已部署的代理服务
|
||||
- When: 执行 `pnpm deploy delete <name>`
|
||||
- Then: 清理所有 K8S 资源(Deployment、Service、Ingress)
|
||||
|
||||
- [ ] **AC5: 状态查询正常**
|
||||
- Given: 已部署的代理服务
|
||||
- When: 执行 `pnpm deploy status <name>`
|
||||
- Then: 返回服务运行状态和访问 URL
|
||||
|
||||
## Additional Context
|
||||
|
||||
### Dependencies
|
||||
|
||||
**新增依赖:**
|
||||
- @kubernetes/client-node - K8S 官方 Node.js SDK
|
||||
|
||||
### Testing Strategy
|
||||
|
||||
**手动测试步骤:**
|
||||
1. 配置 `.env` 文件中的 K8S 环境变量
|
||||
2. 执行 `pnpm deploy claude-proxy-1 registry.cn-beijing.aliyuncs.com/d8dcloud/claude-api-proxy:v0.0.1`
|
||||
3. 验证 Deployment、Service、Ingress 创建成功
|
||||
4. 访问返回的 HTTPS URL 验证服务可用
|
||||
5. 执行删除命令验证资源清理
|
||||
|
||||
### Notes
|
||||
|
||||
- 命名空间独立,避免与 d8d-dev/d8d-prd 冲突
|
||||
- 代理服务无需 SSO 认证
|
||||
- EIP 自动创建简化了 IP 管理
|
||||
- 当前未启用抢占式实例,后续可优化成本
|
||||
@@ -6,7 +6,8 @@
|
||||
"scripts": {
|
||||
"build": "tsc && tsc-alias",
|
||||
"start": "node dist/index.js",
|
||||
"dev": "tsx watch src/index.ts"
|
||||
"dev": "tsx watch src/index.ts",
|
||||
"deploy": "tsx scripts/deploy.ts"
|
||||
},
|
||||
"keywords": [
|
||||
"claude",
|
||||
@@ -16,6 +17,7 @@
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@kubernetes/client-node": "1.3.0",
|
||||
"axios": "^1.7.9",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^4.21.2"
|
||||
|
||||
426
pnpm-lock.yaml
generated
426
pnpm-lock.yaml
generated
@@ -8,6 +8,9 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@kubernetes/client-node':
|
||||
specifier: 1.3.0
|
||||
version: 1.3.0
|
||||
axios:
|
||||
specifier: ^1.7.9
|
||||
version: 1.13.6
|
||||
@@ -192,6 +195,21 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@jsep-plugin/assignment@1.3.0':
|
||||
resolution: {integrity: sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==}
|
||||
engines: {node: '>= 10.16.0'}
|
||||
peerDependencies:
|
||||
jsep: ^0.4.0||^1.0.0
|
||||
|
||||
'@jsep-plugin/regex@1.0.4':
|
||||
resolution: {integrity: sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==}
|
||||
engines: {node: '>= 10.16.0'}
|
||||
peerDependencies:
|
||||
jsep: ^0.4.0||^1.0.0
|
||||
|
||||
'@kubernetes/client-node@1.3.0':
|
||||
resolution: {integrity: sha512-IE0yrIpOT97YS5fg2QpzmPzm8Wmcdf4ueWMn+FiJSI3jgTTQT1u+LUhoYpdfhdHAVxdrNsaBg2C0UXSnOgMoCQ==}
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -219,6 +237,12 @@ packages:
|
||||
'@types/http-errors@2.0.5':
|
||||
resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==}
|
||||
|
||||
'@types/js-yaml@4.0.9':
|
||||
resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
|
||||
|
||||
'@types/node-fetch@2.6.13':
|
||||
resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==}
|
||||
|
||||
'@types/node@22.19.15':
|
||||
resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==}
|
||||
|
||||
@@ -234,14 +258,24 @@ packages:
|
||||
'@types/serve-static@2.2.0':
|
||||
resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==}
|
||||
|
||||
'@types/stream-buffers@3.0.8':
|
||||
resolution: {integrity: sha512-J+7VaHKNvlNPJPEJXX/fKa9DZtR/xPMwuIbe+yNOwp1YB+ApUOBv2aUpEoBJEi8nJgbgs1x8e73ttg0r1rSUdw==}
|
||||
|
||||
accepts@1.3.8:
|
||||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
agent-base@7.1.4:
|
||||
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
anymatch@3.1.3:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
array-flatten@1.1.1:
|
||||
resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
|
||||
|
||||
@@ -255,6 +289,52 @@ packages:
|
||||
axios@1.13.6:
|
||||
resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==}
|
||||
|
||||
b4a@1.8.0:
|
||||
resolution: {integrity: sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==}
|
||||
peerDependencies:
|
||||
react-native-b4a: '*'
|
||||
peerDependenciesMeta:
|
||||
react-native-b4a:
|
||||
optional: true
|
||||
|
||||
bare-events@2.8.2:
|
||||
resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==}
|
||||
peerDependencies:
|
||||
bare-abort-controller: '*'
|
||||
peerDependenciesMeta:
|
||||
bare-abort-controller:
|
||||
optional: true
|
||||
|
||||
bare-fs@4.5.6:
|
||||
resolution: {integrity: sha512-1QovqDrR80Pmt5HPAsMsXTCFcDYr+NSUKW6nd6WO5v0JBmnItc/irNRzm2KOQ5oZ69P37y+AMujNyNtG+1Rggw==}
|
||||
engines: {bare: '>=1.16.0'}
|
||||
peerDependencies:
|
||||
bare-buffer: '*'
|
||||
peerDependenciesMeta:
|
||||
bare-buffer:
|
||||
optional: true
|
||||
|
||||
bare-os@3.8.0:
|
||||
resolution: {integrity: sha512-Dc9/SlwfxkXIGYhvMQNUtKaXCaGkZYGcd1vuNUUADVqzu4/vQfvnMkYYOUnt2VwQ2AqKr/8qAVFRtwETljgeFg==}
|
||||
engines: {bare: '>=1.14.0'}
|
||||
|
||||
bare-path@3.0.0:
|
||||
resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==}
|
||||
|
||||
bare-stream@2.10.0:
|
||||
resolution: {integrity: sha512-DOPZF/DDcDruKDA43cOw6e9Quq5daua7ygcAwJE/pKJsRWhgSSemi7qVNGE5kyDIxIeN1533G/zfbvWX7Wcb9w==}
|
||||
peerDependencies:
|
||||
bare-buffer: '*'
|
||||
bare-events: '*'
|
||||
peerDependenciesMeta:
|
||||
bare-buffer:
|
||||
optional: true
|
||||
bare-events:
|
||||
optional: true
|
||||
|
||||
bare-url@2.4.0:
|
||||
resolution: {integrity: sha512-NSTU5WN+fy/L0DDenfE8SXQna4voXuW0FHM7wH8i3/q9khUSchfPbPezO4zSFMnDGIf9YE+mt/RWhZgNRKRIXA==}
|
||||
|
||||
binary-extensions@2.3.0:
|
||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -314,6 +394,15 @@ packages:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
debug@4.4.3:
|
||||
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
delayed-stream@1.0.0:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
@@ -345,6 +434,9 @@ packages:
|
||||
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
end-of-stream@1.4.5:
|
||||
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
||||
|
||||
es-define-property@1.0.1:
|
||||
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -373,10 +465,16 @@ packages:
|
||||
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
events-universal@1.0.1:
|
||||
resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==}
|
||||
|
||||
express@4.22.1:
|
||||
resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
|
||||
fast-fifo@1.3.2:
|
||||
resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==}
|
||||
|
||||
fast-glob@3.3.3:
|
||||
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
|
||||
engines: {node: '>=8.6.0'}
|
||||
@@ -456,6 +554,10 @@ packages:
|
||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
hpagent@1.2.0:
|
||||
resolution: {integrity: sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
http-errors@2.0.1:
|
||||
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -471,6 +573,10 @@ packages:
|
||||
inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
|
||||
ip-address@10.1.0:
|
||||
resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
ipaddr.js@1.9.1:
|
||||
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
|
||||
engines: {node: '>= 0.10'}
|
||||
@@ -491,6 +597,27 @@ packages:
|
||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
|
||||
isomorphic-ws@5.0.0:
|
||||
resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==}
|
||||
peerDependencies:
|
||||
ws: '*'
|
||||
|
||||
jose@6.2.2:
|
||||
resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==}
|
||||
|
||||
js-yaml@4.1.1:
|
||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||
hasBin: true
|
||||
|
||||
jsep@1.4.0:
|
||||
resolution: {integrity: sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==}
|
||||
engines: {node: '>= 10.16.0'}
|
||||
|
||||
jsonpath-plus@10.4.0:
|
||||
resolution: {integrity: sha512-T92WWatJXmhBbKsgH/0hl+jxjdXrifi5IKeMY02DWggRxX0UElcbVzPlmgLTbvsPeW1PasQ6xE2Q75stkhGbsA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
hasBin: true
|
||||
|
||||
math-intrinsics@1.1.0:
|
||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -541,10 +668,22 @@ packages:
|
||||
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
node-fetch@2.7.0:
|
||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
peerDependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
|
||||
normalize-path@3.0.0:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
oauth4webapi@3.8.5:
|
||||
resolution: {integrity: sha512-A8jmyUckVhRJj5lspguklcl90Ydqk61H3dcU0oLhH3Yv13KpAliKTt5hknpGGPZSSfOwGyraNEFmofDYH+1kSg==}
|
||||
|
||||
object-inspect@1.13.4:
|
||||
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -553,6 +692,12 @@ packages:
|
||||
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
|
||||
openid-client@6.8.2:
|
||||
resolution: {integrity: sha512-uOvTCndr4udZsKihJ68H9bUICrriHdUVJ6Az+4Ns6cW55rwM5h0bjVIzDz2SxgOI84LKjFyjOFvERLzdTUROGA==}
|
||||
|
||||
parseurl@1.3.3:
|
||||
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -579,6 +724,9 @@ packages:
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
pump@3.0.4:
|
||||
resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
|
||||
|
||||
qs@6.14.2:
|
||||
resolution: {integrity: sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==}
|
||||
engines: {node: '>=0.6'}
|
||||
@@ -609,6 +757,9 @@ packages:
|
||||
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
|
||||
rfc4648@1.5.4:
|
||||
resolution: {integrity: sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==}
|
||||
|
||||
run-parallel@1.2.0:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
|
||||
@@ -649,10 +800,41 @@ packages:
|
||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
smart-buffer@4.2.0:
|
||||
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
|
||||
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
|
||||
|
||||
socks-proxy-agent@8.0.5:
|
||||
resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
socks@2.8.7:
|
||||
resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==}
|
||||
engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
|
||||
|
||||
statuses@2.0.2:
|
||||
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
stream-buffers@3.0.3:
|
||||
resolution: {integrity: sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
|
||||
streamx@2.25.0:
|
||||
resolution: {integrity: sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==}
|
||||
|
||||
tar-fs@3.1.2:
|
||||
resolution: {integrity: sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==}
|
||||
|
||||
tar-stream@3.1.8:
|
||||
resolution: {integrity: sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==}
|
||||
|
||||
teex@1.0.1:
|
||||
resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==}
|
||||
|
||||
text-decoder@1.2.7:
|
||||
resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
@@ -661,6 +843,9 @@ packages:
|
||||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
|
||||
tsc-alias@1.8.16:
|
||||
resolution: {integrity: sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g==}
|
||||
engines: {node: '>=16.20.2'}
|
||||
@@ -695,6 +880,27 @@ packages:
|
||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
|
||||
whatwg-url@5.0.0:
|
||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||
|
||||
wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
ws@8.19.0:
|
||||
resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
snapshots:
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.4':
|
||||
@@ -775,6 +981,41 @@ snapshots:
|
||||
'@esbuild/win32-x64@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@jsep-plugin/assignment@1.3.0(jsep@1.4.0)':
|
||||
dependencies:
|
||||
jsep: 1.4.0
|
||||
|
||||
'@jsep-plugin/regex@1.0.4(jsep@1.4.0)':
|
||||
dependencies:
|
||||
jsep: 1.4.0
|
||||
|
||||
'@kubernetes/client-node@1.3.0':
|
||||
dependencies:
|
||||
'@types/js-yaml': 4.0.9
|
||||
'@types/node': 22.19.15
|
||||
'@types/node-fetch': 2.6.13
|
||||
'@types/stream-buffers': 3.0.8
|
||||
form-data: 4.0.5
|
||||
hpagent: 1.2.0
|
||||
isomorphic-ws: 5.0.0(ws@8.19.0)
|
||||
js-yaml: 4.1.1
|
||||
jsonpath-plus: 10.4.0
|
||||
node-fetch: 2.7.0
|
||||
openid-client: 6.8.2
|
||||
rfc4648: 1.5.4
|
||||
socks-proxy-agent: 8.0.5
|
||||
stream-buffers: 3.0.3
|
||||
tar-fs: 3.1.2
|
||||
ws: 8.19.0
|
||||
transitivePeerDependencies:
|
||||
- bare-abort-controller
|
||||
- bare-buffer
|
||||
- bufferutil
|
||||
- encoding
|
||||
- react-native-b4a
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
dependencies:
|
||||
'@nodelib/fs.stat': 2.0.5
|
||||
@@ -811,6 +1052,13 @@ snapshots:
|
||||
|
||||
'@types/http-errors@2.0.5': {}
|
||||
|
||||
'@types/js-yaml@4.0.9': {}
|
||||
|
||||
'@types/node-fetch@2.6.13':
|
||||
dependencies:
|
||||
'@types/node': 22.19.15
|
||||
form-data: 4.0.5
|
||||
|
||||
'@types/node@22.19.15':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
@@ -828,16 +1076,24 @@ snapshots:
|
||||
'@types/http-errors': 2.0.5
|
||||
'@types/node': 22.19.15
|
||||
|
||||
'@types/stream-buffers@3.0.8':
|
||||
dependencies:
|
||||
'@types/node': 22.19.15
|
||||
|
||||
accepts@1.3.8:
|
||||
dependencies:
|
||||
mime-types: 2.1.35
|
||||
negotiator: 0.6.3
|
||||
|
||||
agent-base@7.1.4: {}
|
||||
|
||||
anymatch@3.1.3:
|
||||
dependencies:
|
||||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
|
||||
argparse@2.0.1: {}
|
||||
|
||||
array-flatten@1.1.1: {}
|
||||
|
||||
array-union@2.1.0: {}
|
||||
@@ -852,6 +1108,41 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
|
||||
b4a@1.8.0: {}
|
||||
|
||||
bare-events@2.8.2: {}
|
||||
|
||||
bare-fs@4.5.6:
|
||||
dependencies:
|
||||
bare-events: 2.8.2
|
||||
bare-path: 3.0.0
|
||||
bare-stream: 2.10.0(bare-events@2.8.2)
|
||||
bare-url: 2.4.0
|
||||
fast-fifo: 1.3.2
|
||||
transitivePeerDependencies:
|
||||
- bare-abort-controller
|
||||
- react-native-b4a
|
||||
|
||||
bare-os@3.8.0: {}
|
||||
|
||||
bare-path@3.0.0:
|
||||
dependencies:
|
||||
bare-os: 3.8.0
|
||||
|
||||
bare-stream@2.10.0(bare-events@2.8.2):
|
||||
dependencies:
|
||||
streamx: 2.25.0
|
||||
teex: 1.0.1
|
||||
optionalDependencies:
|
||||
bare-events: 2.8.2
|
||||
transitivePeerDependencies:
|
||||
- bare-abort-controller
|
||||
- react-native-b4a
|
||||
|
||||
bare-url@2.4.0:
|
||||
dependencies:
|
||||
bare-path: 3.0.0
|
||||
|
||||
binary-extensions@2.3.0: {}
|
||||
|
||||
body-parser@1.20.4:
|
||||
@@ -919,6 +1210,10 @@ snapshots:
|
||||
dependencies:
|
||||
ms: 2.0.0
|
||||
|
||||
debug@4.4.3:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
delayed-stream@1.0.0: {}
|
||||
|
||||
depd@2.0.0: {}
|
||||
@@ -941,6 +1236,10 @@ snapshots:
|
||||
|
||||
encodeurl@2.0.0: {}
|
||||
|
||||
end-of-stream@1.4.5:
|
||||
dependencies:
|
||||
once: 1.4.0
|
||||
|
||||
es-define-property@1.0.1: {}
|
||||
|
||||
es-errors@1.3.0: {}
|
||||
@@ -989,6 +1288,12 @@ snapshots:
|
||||
|
||||
etag@1.8.1: {}
|
||||
|
||||
events-universal@1.0.1:
|
||||
dependencies:
|
||||
bare-events: 2.8.2
|
||||
transitivePeerDependencies:
|
||||
- bare-abort-controller
|
||||
|
||||
express@4.22.1:
|
||||
dependencies:
|
||||
accepts: 1.3.8
|
||||
@@ -1025,6 +1330,8 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
fast-fifo@1.3.2: {}
|
||||
|
||||
fast-glob@3.3.3:
|
||||
dependencies:
|
||||
'@nodelib/fs.stat': 2.0.5
|
||||
@@ -1119,6 +1426,8 @@ snapshots:
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
|
||||
hpagent@1.2.0: {}
|
||||
|
||||
http-errors@2.0.1:
|
||||
dependencies:
|
||||
depd: 2.0.0
|
||||
@@ -1135,6 +1444,8 @@ snapshots:
|
||||
|
||||
inherits@2.0.4: {}
|
||||
|
||||
ip-address@10.1.0: {}
|
||||
|
||||
ipaddr.js@1.9.1: {}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
@@ -1149,6 +1460,24 @@ snapshots:
|
||||
|
||||
is-number@7.0.0: {}
|
||||
|
||||
isomorphic-ws@5.0.0(ws@8.19.0):
|
||||
dependencies:
|
||||
ws: 8.19.0
|
||||
|
||||
jose@6.2.2: {}
|
||||
|
||||
js-yaml@4.1.1:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
jsep@1.4.0: {}
|
||||
|
||||
jsonpath-plus@10.4.0:
|
||||
dependencies:
|
||||
'@jsep-plugin/assignment': 1.3.0(jsep@1.4.0)
|
||||
'@jsep-plugin/regex': 1.0.4(jsep@1.4.0)
|
||||
jsep: 1.4.0
|
||||
|
||||
math-intrinsics@1.1.0: {}
|
||||
|
||||
media-typer@0.3.0: {}
|
||||
@@ -1180,14 +1509,29 @@ snapshots:
|
||||
|
||||
negotiator@0.6.3: {}
|
||||
|
||||
node-fetch@2.7.0:
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
|
||||
normalize-path@3.0.0: {}
|
||||
|
||||
oauth4webapi@3.8.5: {}
|
||||
|
||||
object-inspect@1.13.4: {}
|
||||
|
||||
on-finished@2.4.1:
|
||||
dependencies:
|
||||
ee-first: 1.1.1
|
||||
|
||||
once@1.4.0:
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
|
||||
openid-client@6.8.2:
|
||||
dependencies:
|
||||
jose: 6.2.2
|
||||
oauth4webapi: 3.8.5
|
||||
|
||||
parseurl@1.3.3: {}
|
||||
|
||||
path-to-regexp@0.1.12: {}
|
||||
@@ -1207,6 +1551,11 @@ snapshots:
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
pump@3.0.4:
|
||||
dependencies:
|
||||
end-of-stream: 1.4.5
|
||||
once: 1.4.0
|
||||
|
||||
qs@6.14.2:
|
||||
dependencies:
|
||||
side-channel: 1.1.0
|
||||
@@ -1232,6 +1581,8 @@ snapshots:
|
||||
|
||||
reusify@1.1.0: {}
|
||||
|
||||
rfc4648@1.5.4: {}
|
||||
|
||||
run-parallel@1.2.0:
|
||||
dependencies:
|
||||
queue-microtask: 1.2.3
|
||||
@@ -1299,14 +1650,78 @@ snapshots:
|
||||
|
||||
slash@3.0.0: {}
|
||||
|
||||
smart-buffer@4.2.0: {}
|
||||
|
||||
socks-proxy-agent@8.0.5:
|
||||
dependencies:
|
||||
agent-base: 7.1.4
|
||||
debug: 4.4.3
|
||||
socks: 2.8.7
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
socks@2.8.7:
|
||||
dependencies:
|
||||
ip-address: 10.1.0
|
||||
smart-buffer: 4.2.0
|
||||
|
||||
statuses@2.0.2: {}
|
||||
|
||||
stream-buffers@3.0.3: {}
|
||||
|
||||
streamx@2.25.0:
|
||||
dependencies:
|
||||
events-universal: 1.0.1
|
||||
fast-fifo: 1.3.2
|
||||
text-decoder: 1.2.7
|
||||
transitivePeerDependencies:
|
||||
- bare-abort-controller
|
||||
- react-native-b4a
|
||||
|
||||
tar-fs@3.1.2:
|
||||
dependencies:
|
||||
pump: 3.0.4
|
||||
tar-stream: 3.1.8
|
||||
optionalDependencies:
|
||||
bare-fs: 4.5.6
|
||||
bare-path: 3.0.0
|
||||
transitivePeerDependencies:
|
||||
- bare-abort-controller
|
||||
- bare-buffer
|
||||
- react-native-b4a
|
||||
|
||||
tar-stream@3.1.8:
|
||||
dependencies:
|
||||
b4a: 1.8.0
|
||||
bare-fs: 4.5.6
|
||||
fast-fifo: 1.3.2
|
||||
streamx: 2.25.0
|
||||
transitivePeerDependencies:
|
||||
- bare-abort-controller
|
||||
- bare-buffer
|
||||
- react-native-b4a
|
||||
|
||||
teex@1.0.1:
|
||||
dependencies:
|
||||
streamx: 2.25.0
|
||||
transitivePeerDependencies:
|
||||
- bare-abort-controller
|
||||
- react-native-b4a
|
||||
|
||||
text-decoder@1.2.7:
|
||||
dependencies:
|
||||
b4a: 1.8.0
|
||||
transitivePeerDependencies:
|
||||
- react-native-b4a
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
toidentifier@1.0.1: {}
|
||||
|
||||
tr46@0.0.3: {}
|
||||
|
||||
tsc-alias@1.8.16:
|
||||
dependencies:
|
||||
chokidar: 3.6.0
|
||||
@@ -1338,3 +1753,14 @@ snapshots:
|
||||
utils-merge@1.0.1: {}
|
||||
|
||||
vary@1.1.2: {}
|
||||
|
||||
webidl-conversions@3.0.1: {}
|
||||
|
||||
whatwg-url@5.0.0:
|
||||
dependencies:
|
||||
tr46: 0.0.3
|
||||
webidl-conversions: 3.0.1
|
||||
|
||||
wrappy@1.0.2: {}
|
||||
|
||||
ws@8.19.0: {}
|
||||
|
||||
173
scripts/deploy.ts
Normal file
173
scripts/deploy.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* K8S 部署命令行工具
|
||||
* 用于部署 Claude API 代理服务到 ECI
|
||||
*
|
||||
* 用法:
|
||||
* pnpm deploy <name> <image> [options] - 部署代理服务
|
||||
* pnpm deploy delete <name> - 删除代理服务
|
||||
* pnpm deploy status <name> - 查询服务状态
|
||||
*
|
||||
* 选项:
|
||||
* --replicas <n> 副本数 (默认: 1)
|
||||
* --port <port> 容器端口 (默认: 3000)
|
||||
* --eip-bandwidth <n> EIP 带宽 Mbps (默认: 5)
|
||||
*/
|
||||
|
||||
import { config } from 'dotenv';
|
||||
config();
|
||||
|
||||
import { deployProxy, deleteProxy, getProxyStatus } from '../src/deploy';
|
||||
|
||||
// 解析命令行参数
|
||||
function parseArgs() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length === 0) {
|
||||
printUsage();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const command = args[0];
|
||||
|
||||
// 解析选项
|
||||
const getOption = (name: string, defaultValue: string): string => {
|
||||
const idx = args.indexOf(name);
|
||||
if (idx !== -1 && idx + 1 < args.length) {
|
||||
return args[idx + 1];
|
||||
}
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
const getOptionNumber = (name: string, defaultValue: number): number => {
|
||||
return Number(getOption(name, String(defaultValue)));
|
||||
};
|
||||
|
||||
return {
|
||||
command,
|
||||
name: args[1],
|
||||
image: args[2],
|
||||
options: {
|
||||
replicas: getOptionNumber('--replicas', 1),
|
||||
port: getOptionNumber('--port', 3000),
|
||||
eipBandwidth: getOptionNumber('--eip-bandwidth', 5),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function printUsage() {
|
||||
console.log(`
|
||||
K8S 部署命令行工具 - Claude API 代理服务
|
||||
|
||||
用法:
|
||||
pnpm deploy <name> <image> [options] 部署代理服务
|
||||
pnpm deploy delete <name> 删除代理服务
|
||||
pnpm deploy status <name> 查询服务状态
|
||||
|
||||
选项:
|
||||
--replicas <n> 副本数 (默认: 1)
|
||||
--port <port> 容器端口 (默认: 3000)
|
||||
--eip-bandwidth <n> EIP 带宽 Mbps (默认: 5)
|
||||
|
||||
示例:
|
||||
# 部署单个代理实例
|
||||
pnpm deploy claude-proxy-1 registry.cn-beijing.aliyuncs.com/d8dcloud/claude-api-proxy:v0.0.1
|
||||
|
||||
# 部署 3 个副本
|
||||
pnpm deploy claude-proxy-1 registry.cn-beijing.aliyuncs.com/d8dcloud/claude-api-proxy:v0.0.1 --replicas 3
|
||||
|
||||
# 删除服务
|
||||
pnpm deploy delete claude-proxy-1
|
||||
|
||||
# 查询状态
|
||||
pnpm deploy status claude-proxy-1
|
||||
`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const { command, name, image, options } = parseArgs();
|
||||
|
||||
// 检查环境变量
|
||||
if (!process.env.K8S_API_URL || !process.env.K8S_API_TOKEN) {
|
||||
console.error('❌ 错误: 请配置 K8S_API_URL 和 K8S_API_TOKEN 环境变量');
|
||||
console.error(' 可以在 .env 文件中配置或通过环境变量传入');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
switch (command) {
|
||||
case 'delete': {
|
||||
if (!name) {
|
||||
console.error('❌ 错误: 请指定服务名称');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`🗑️ 正在删除服务 ${name}...`);
|
||||
const result = await deleteProxy(name);
|
||||
if (result.success) {
|
||||
console.log(`✅ ${result.message}`);
|
||||
} else {
|
||||
console.error(`❌ ${result.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'status': {
|
||||
if (!name) {
|
||||
console.error('❌ 错误: 请指定服务名称');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`📊 查询服务状态 ${name}...`);
|
||||
const status = await getProxyStatus(name);
|
||||
if (status.exists) {
|
||||
console.log(`✅ 服务状态: ${status.status}`);
|
||||
if (status.hosts && status.hosts.length > 0) {
|
||||
console.log(`🌐 访问地址: ${status.hosts.join(', ')}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`⚠️ 服务 ${name} 不存在`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
// deploy 命令
|
||||
if (!name || !image) {
|
||||
console.error('❌ 错误: 请指定服务名称和镜像');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`🚀 正在部署 ${name}...`);
|
||||
console.log(` 镜像: ${image}`);
|
||||
console.log(` 副本: ${options.replicas}`);
|
||||
console.log(` 端口: ${options.port}`);
|
||||
console.log(` EIP 带宽: ${options.eipBandwidth} Mbps`);
|
||||
|
||||
const result = await deployProxy({
|
||||
name,
|
||||
image,
|
||||
replicas: options.replicas,
|
||||
port: options.port,
|
||||
eipBandwidth: options.eipBandwidth,
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
console.log(`\n✅ ${result.message}`);
|
||||
if (result.hosts && result.hosts.length > 0) {
|
||||
console.log(`\n🌐 访问地址:`);
|
||||
result.hosts.forEach(host => console.log(` ${host}`));
|
||||
}
|
||||
} else {
|
||||
console.error(`\n❌ ${result.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ 执行失败:', error instanceof Error ? error.message : error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
373
src/deploy.ts
Normal file
373
src/deploy.ts
Normal file
@@ -0,0 +1,373 @@
|
||||
/**
|
||||
* K8S 部署模块 - 用于部署 Claude API 代理服务到 ECI
|
||||
*/
|
||||
|
||||
import { config } from 'dotenv';
|
||||
config();
|
||||
|
||||
import { K8sClient, V1Deployment, V1Ingress, V1IngressRule, V1ServicePort } from './k8s-client';
|
||||
|
||||
// 环境变量
|
||||
const K8S_API_URL = process.env.K8S_API_URL || '';
|
||||
const K8S_API_TOKEN = process.env.K8S_API_TOKEN || '';
|
||||
const DEV_HOST = process.env.DEV_HOST || 'dev.d8d.fun';
|
||||
const TLS_SECRET_NAME = process.env.TLS_SECRET_NAME || 'd8d-tls';
|
||||
|
||||
// 专用命名空间
|
||||
const NAMESPACE = 'd8d-proxy';
|
||||
|
||||
// 初始化 K8S 客户端
|
||||
export const k8sClient = new K8sClient(K8S_API_URL, K8S_API_TOKEN);
|
||||
|
||||
export interface DeployProxyParams {
|
||||
/** 容器组名称(唯一标识) */
|
||||
name: string;
|
||||
/** Docker 镜像 */
|
||||
image: string;
|
||||
/** 副本数 */
|
||||
replicas?: number;
|
||||
/** 容器端口 */
|
||||
port?: number;
|
||||
/** ECI 实例规格 */
|
||||
instanceType?: string;
|
||||
/** 是否自动创建 EIP */
|
||||
autoEip?: boolean;
|
||||
/** EIP 带宽 (Mbps) */
|
||||
eipBandwidth?: number;
|
||||
/** 环境变量 */
|
||||
env?: Record<string, string>;
|
||||
/** 标签 */
|
||||
labels?: Record<string, string>;
|
||||
/** 域名后缀 */
|
||||
hostSuffix?: string;
|
||||
}
|
||||
|
||||
export interface DeployProxyResult {
|
||||
success: boolean;
|
||||
message: string;
|
||||
hosts?: string[];
|
||||
deploymentName?: string;
|
||||
serviceName?: string;
|
||||
ingressName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 部署 Claude API 代理服务
|
||||
*/
|
||||
export async function deployProxy(params: DeployProxyParams): Promise<DeployProxyResult> {
|
||||
const {
|
||||
name,
|
||||
image,
|
||||
replicas = 1,
|
||||
port = 3000,
|
||||
instanceType = 'ecs.t5-lc2m1.nano',
|
||||
autoEip = true,
|
||||
eipBandwidth = 5,
|
||||
env = {},
|
||||
labels = {},
|
||||
hostSuffix = DEV_HOST,
|
||||
} = params;
|
||||
|
||||
const deploymentName = name;
|
||||
const serviceName = `service-${name}`;
|
||||
const ingressName = `${name}-ingress`;
|
||||
|
||||
// 清理域名后缀
|
||||
const cleanHostSuffix = hostSuffix.replace(/^\*\./, '').replace(/^\.+/, '');
|
||||
|
||||
// 构建域名
|
||||
const host = `${name}-${port}.${cleanHostSuffix}`;
|
||||
|
||||
try {
|
||||
// 1. 创建 Deployment
|
||||
const deployment: V1Deployment = {
|
||||
metadata: {
|
||||
name: deploymentName,
|
||||
labels: {
|
||||
app: name,
|
||||
'proxy-container': 'true',
|
||||
...labels,
|
||||
},
|
||||
annotations: {
|
||||
// ECI 实例规格
|
||||
'k8s.aliyun.com/eci-use-specs': instanceType,
|
||||
// 自动创建 EIP
|
||||
...(autoEip && {
|
||||
'k8s.aliyun.com/eci-open-enable-eip': 'true',
|
||||
'k8s.aliyun.com/eci-eip-bandwidth': String(eipBandwidth),
|
||||
'k8s.aliyun.com/eci-eip-internet-charge-type': 'PayByBandwidth',
|
||||
}),
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
replicas,
|
||||
selector: {
|
||||
matchLabels: {
|
||||
app: name,
|
||||
},
|
||||
},
|
||||
template: {
|
||||
metadata: {
|
||||
labels: {
|
||||
app: name,
|
||||
'proxy-container': 'true',
|
||||
...labels,
|
||||
},
|
||||
annotations: {
|
||||
// ECI 实例规格
|
||||
'k8s.aliyun.com/eci-use-specs': instanceType,
|
||||
// 自动创建 EIP
|
||||
...(autoEip && {
|
||||
'k8s.aliyun.com/eci-open-enable-eip': 'true',
|
||||
'k8s.aliyun.com/eci-eip-bandwidth': String(eipBandwidth),
|
||||
'k8s.aliyun.com/eci-eip-internet-charge-type': 'PayByBandwidth',
|
||||
}),
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
automountServiceAccountToken: false,
|
||||
enableServiceLinks: false,
|
||||
nodeSelector: {
|
||||
type: 'virtual-kubelet',
|
||||
},
|
||||
containers: [
|
||||
{
|
||||
name: name,
|
||||
image: image,
|
||||
ports: [
|
||||
{
|
||||
containerPort: port,
|
||||
protocol: 'TCP',
|
||||
},
|
||||
],
|
||||
env: Object.entries(env).map(([key, value]) => ({
|
||||
name: key,
|
||||
value,
|
||||
})),
|
||||
resources: {
|
||||
requests: {
|
||||
cpu: '250m',
|
||||
memory: '256Mi',
|
||||
},
|
||||
},
|
||||
readinessProbe: {
|
||||
httpGet: {
|
||||
path: '/health',
|
||||
port: port,
|
||||
},
|
||||
initialDelaySeconds: 5,
|
||||
periodSeconds: 10,
|
||||
},
|
||||
livenessProbe: {
|
||||
httpGet: {
|
||||
path: '/health',
|
||||
port: port,
|
||||
},
|
||||
initialDelaySeconds: 15,
|
||||
periodSeconds: 20,
|
||||
},
|
||||
},
|
||||
],
|
||||
tolerations: [
|
||||
{
|
||||
key: 'alibabacloud.com/eci',
|
||||
operator: 'Exists',
|
||||
effect: 'NoSchedule',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await k8sClient.createDeployment({
|
||||
json: {
|
||||
namespace: NAMESPACE,
|
||||
deployment,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Deployment ${deploymentName} 创建成功`);
|
||||
|
||||
// 2. 创建 Service
|
||||
const ports: V1ServicePort[] = [
|
||||
{
|
||||
port: port,
|
||||
targetPort: port,
|
||||
name: 'http',
|
||||
},
|
||||
];
|
||||
|
||||
await k8sClient.createService({
|
||||
json: {
|
||||
namespace: NAMESPACE,
|
||||
service: {
|
||||
metadata: {
|
||||
name: serviceName,
|
||||
labels: {
|
||||
app: name,
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
type: 'ClusterIP',
|
||||
selector: {
|
||||
app: name,
|
||||
},
|
||||
ports,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Service ${serviceName} 创建成功`);
|
||||
|
||||
// 3. 创建 Ingress
|
||||
const ingressRule: V1IngressRule = {
|
||||
host,
|
||||
http: {
|
||||
paths: [
|
||||
{
|
||||
path: '/',
|
||||
pathType: 'Prefix',
|
||||
backend: {
|
||||
service: {
|
||||
name: serviceName,
|
||||
port: {
|
||||
number: port,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const ingress: V1Ingress = {
|
||||
metadata: {
|
||||
name: ingressName,
|
||||
annotations: {
|
||||
'nginx.ingress.kubernetes.io/proxy-body-size': '50m',
|
||||
'nginx.ingress.kubernetes.io/proxy-read-timeout': '300',
|
||||
'nginx.ingress.kubernetes.io/proxy-send-timeout': '300',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
tls: [
|
||||
{
|
||||
hosts: [host],
|
||||
secretName: TLS_SECRET_NAME,
|
||||
},
|
||||
],
|
||||
rules: [ingressRule],
|
||||
},
|
||||
};
|
||||
|
||||
await k8sClient.createIngress({
|
||||
json: {
|
||||
namespace: NAMESPACE,
|
||||
ingress,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Ingress ${ingressName} 创建成功`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `代理服务 ${name} 部署成功`,
|
||||
hosts: [`https://${host}`],
|
||||
deploymentName,
|
||||
serviceName,
|
||||
ingressName,
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error(`❌ 部署失败: ${errorMessage}`);
|
||||
return {
|
||||
success: false,
|
||||
message: `部署失败: ${errorMessage}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除代理服务
|
||||
*/
|
||||
export async function deleteProxy(name: string): Promise<{ success: boolean; message: string }> {
|
||||
const ingressName = `${name}-ingress`;
|
||||
const serviceName = `service-${name}`;
|
||||
|
||||
try {
|
||||
// 1. 删除 Ingress
|
||||
await k8sClient.deleteIngress({
|
||||
param: {
|
||||
namespace: NAMESPACE,
|
||||
ingressName,
|
||||
},
|
||||
}).catch(err => console.warn(`删除 Ingress 失败: ${err.message}`));
|
||||
|
||||
// 2. 删除 Service
|
||||
await k8sClient.deleteService({
|
||||
param: {
|
||||
namespace: NAMESPACE,
|
||||
serviceName,
|
||||
},
|
||||
}).catch(err => console.warn(`删除 Service 失败: ${err.message}`));
|
||||
|
||||
// 3. 删除 Deployment
|
||||
await k8sClient.deleteDeployment({
|
||||
param: {
|
||||
namespace: NAMESPACE,
|
||||
name,
|
||||
},
|
||||
}).catch(err => console.warn(`删除 Deployment 失败: ${err.message}`));
|
||||
|
||||
console.log(`✅ 代理服务 ${name} 删除成功`);
|
||||
return { success: true, message: `代理服务 ${name} 删除成功` };
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
return { success: false, message: `删除失败: ${errorMessage}` };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理服务状态
|
||||
*/
|
||||
export async function getProxyStatus(name: string): Promise<{
|
||||
exists: boolean;
|
||||
status?: string;
|
||||
hosts?: string[];
|
||||
}> {
|
||||
try {
|
||||
const deployment = await k8sClient.getDeployment({
|
||||
param: {
|
||||
namespace: NAMESPACE,
|
||||
name,
|
||||
},
|
||||
});
|
||||
|
||||
const readyReplicas = deployment.status?.readyReplicas || 0;
|
||||
const replicas = deployment.spec?.replicas || 1;
|
||||
|
||||
// 获取 Ingress 域名
|
||||
const ingressResult = await k8sClient.getIngress({
|
||||
param: {
|
||||
namespace: NAMESPACE,
|
||||
ingressName: `${name}-ingress`,
|
||||
},
|
||||
});
|
||||
|
||||
const hosts = ingressResult.ingress?.spec?.rules?.map(r => `https://${r.host}`) || [];
|
||||
|
||||
return {
|
||||
exists: true,
|
||||
status: readyReplicas >= replicas ? 'Running' : 'Pending',
|
||||
hosts,
|
||||
};
|
||||
} catch (error) {
|
||||
const errorCode = (error as { code?: number })?.code;
|
||||
if (errorCode === 404) {
|
||||
return { exists: false };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
138
src/k8s-client.ts
Normal file
138
src/k8s-client.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import * as k8s from '@kubernetes/client-node';
|
||||
|
||||
export type V1Deployment = k8s.V1Deployment;
|
||||
export type V1DeploymentList = k8s.V1DeploymentList;
|
||||
export type V1Ingress = k8s.V1Ingress;
|
||||
export type V1IngressRule = k8s.V1IngressRule;
|
||||
export type V1Pod = k8s.V1Pod;
|
||||
export type V1Service = k8s.V1Service;
|
||||
export type V1ServicePort = k8s.V1ServicePort;
|
||||
|
||||
export class K8sClient {
|
||||
private kubeConfig: k8s.KubeConfig;
|
||||
|
||||
constructor(baseUrl: string, token: string) {
|
||||
this.kubeConfig = new k8s.KubeConfig();
|
||||
this.kubeConfig.loadFromOptions({
|
||||
clusters: [{ name: 'cluster', server: baseUrl }],
|
||||
users: [{ name: 'user', authProvider: { name: 'token', config: { token } } }],
|
||||
contexts: [{ name: 'ctx', cluster: 'cluster', user: 'user' }],
|
||||
currentContext: 'ctx',
|
||||
});
|
||||
}
|
||||
|
||||
private getCoreV1Api() {
|
||||
return this.kubeConfig.makeApiClient(k8s.CoreV1Api);
|
||||
}
|
||||
|
||||
private getAppsV1Api() {
|
||||
return this.kubeConfig.makeApiClient(k8s.AppsV1Api);
|
||||
}
|
||||
|
||||
private getNetworkingV1Api() {
|
||||
return this.kubeConfig.makeApiClient(k8s.NetworkingV1Api);
|
||||
}
|
||||
|
||||
async getPods(args: { param: { namespace?: string }; query?: { labelSelector?: string; fieldSelector?: string; limit?: number; continue?: string } }): Promise<{ pods: k8s.V1Pod[] }> {
|
||||
try {
|
||||
const ns = args.param.namespace || 'default';
|
||||
const res = await this.getCoreV1Api().listNamespacedPod({ namespace: ns, labelSelector: args.query?.labelSelector, fieldSelector: args.query?.fieldSelector, limit: args.query?.limit, _continue: args.query?.continue });
|
||||
return { pods: res.items || [] };
|
||||
} catch { return { pods: [] }; }
|
||||
}
|
||||
|
||||
async getPod(args: { param: { namespace: string; podName: string } }): Promise<k8s.V1Pod> {
|
||||
return this.getCoreV1Api().readNamespacedPod({ name: args.param.podName, namespace: args.param.namespace });
|
||||
}
|
||||
|
||||
async createPod(args: { json: { namespace: string; pod: V1Pod } }): Promise<{ success: boolean; message: string; pod?: V1Pod }> {
|
||||
try {
|
||||
const res = await this.getCoreV1Api().createNamespacedPod({ namespace: args.json.namespace, body: args.json.pod });
|
||||
return { success: true, message: 'Pod 创建成功', pod: res };
|
||||
} catch (e) { return { success: false, message: `创建 Pod 失败: ${e}` }; }
|
||||
}
|
||||
|
||||
async deletePod(args: { param: { namespace: string; podName: string } }): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
await this.getCoreV1Api().deleteNamespacedPod({ name: args.param.podName, namespace: args.param.namespace });
|
||||
return { success: true, message: 'Pod 删除成功' };
|
||||
} catch (e) { return { success: false, message: `删除 Pod 失败: ${e}` }; }
|
||||
}
|
||||
|
||||
async getDeployments(args: { param: { namespace: string }; query?: { limit?: number; continue?: string } }): Promise<V1DeploymentList> {
|
||||
try {
|
||||
const res = await this.getAppsV1Api().listNamespacedDeployment({ namespace: args.param.namespace, limit: args.query?.limit, _continue: args.query?.continue });
|
||||
return { items: res.items || [], metadata: res.metadata || {} };
|
||||
} catch { return { items: [], metadata: {} }; }
|
||||
}
|
||||
|
||||
async getDeployment(args: { param: { namespace: string; name: string } }): Promise<V1Deployment> {
|
||||
return this.getAppsV1Api().readNamespacedDeployment({ name: args.param.name, namespace: args.param.namespace });
|
||||
}
|
||||
|
||||
async createDeployment(args: { json: { namespace: string; deployment: V1Deployment } }): Promise<{ success: boolean; message: string; deployment?: V1Deployment }> {
|
||||
try {
|
||||
const res = await this.getAppsV1Api().createNamespacedDeployment({ namespace: args.json.namespace, body: args.json.deployment });
|
||||
return { success: true, message: 'Deployment 创建成功', deployment: res };
|
||||
} catch (e) { return { success: false, message: `创建 Deployment 失败: ${e}` }; }
|
||||
}
|
||||
|
||||
async deleteDeployment(args: { param: { namespace: string; name: string } }): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
await this.getAppsV1Api().deleteNamespacedDeployment({ name: args.param.name, namespace: args.param.namespace });
|
||||
return { success: true, message: 'Deployment 删除成功' };
|
||||
} catch (e) { return { success: false, message: `删除 Deployment 失败: ${e}` }; }
|
||||
}
|
||||
|
||||
async restartDeployment(args: { param: { namespace: string; name: string } }): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
const pods = await this.getPods({ param: { namespace: args.param.namespace }, query: { labelSelector: `app=${args.param.name}` } });
|
||||
for (const pod of pods.pods) {
|
||||
if (pod.metadata?.name) await this.deletePod({ param: { namespace: args.param.namespace, podName: pod.metadata.name } });
|
||||
}
|
||||
return { success: true, message: 'Deployment 重启成功' };
|
||||
} catch (e) { return { success: false, message: `重启 Deployment 失败: ${e}` }; }
|
||||
}
|
||||
|
||||
async createService(args: { json: { namespace: string; service: V1Service } }): Promise<{ success: boolean; message: string; service?: V1Service }> {
|
||||
try {
|
||||
const res = await this.getCoreV1Api().createNamespacedService({ namespace: args.json.namespace, body: args.json.service });
|
||||
return { success: true, message: 'Service 创建成功', service: res };
|
||||
} catch (e) { return { success: false, message: `创建 Service 失败: ${e}` }; }
|
||||
}
|
||||
|
||||
async deleteService(args: { param: { namespace: string; serviceName: string } }): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
await this.getCoreV1Api().deleteNamespacedService({ name: args.param.serviceName, namespace: args.param.namespace });
|
||||
return { success: true, message: 'Service 删除成功' };
|
||||
} catch (e) { return { success: false, message: `删除 Service 失败: ${e}` }; }
|
||||
}
|
||||
|
||||
async getService(args: { param: { namespace: string; serviceName: string } }): Promise<{ service?: V1Service }> {
|
||||
try {
|
||||
const res = await this.getCoreV1Api().readNamespacedService({ name: args.param.serviceName, namespace: args.param.namespace });
|
||||
return { service: res };
|
||||
} catch { return {}; }
|
||||
}
|
||||
|
||||
async createIngress(args: { json: { namespace: string; ingress: V1Ingress } }): Promise<{ success: boolean; message: string; ingress?: V1Ingress }> {
|
||||
try {
|
||||
const res = await this.getNetworkingV1Api().createNamespacedIngress({ namespace: args.json.namespace, body: args.json.ingress });
|
||||
return { success: true, message: 'Ingress 创建成功', ingress: res };
|
||||
} catch (e) { return { success: false, message: `创建 Ingress 失败: ${e}` }; }
|
||||
}
|
||||
|
||||
async deleteIngress(args: { param: { namespace: string; ingressName: string } }): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
await this.getNetworkingV1Api().deleteNamespacedIngress({ name: args.param.ingressName, namespace: args.param.namespace });
|
||||
return { success: true, message: 'Ingress 删除成功' };
|
||||
} catch (e) { return { success: false, message: `删除 Ingress 失败: ${e}` }; }
|
||||
}
|
||||
|
||||
async getIngress(args: { param: { namespace: string; ingressName: string } }): Promise<{ ingress?: V1Ingress }> {
|
||||
try {
|
||||
const res = await this.getNetworkingV1Api().readNamespacedIngress({ name: args.param.ingressName, namespace: args.param.namespace });
|
||||
return { ingress: res };
|
||||
} catch { return {}; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user