chore(infra): migrate 103 production host IP to 180.159.29.143
Update scripts, docs, nginx configs, and release-gate safety checks after the Studio server public IP changed from 58.38.22.103. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,7 +5,7 @@ description: Package, deploy, verify, and roll back the Memind Portal runtime on
|
||||
|
||||
# Portal 生产发布 / 验证 / 回滚
|
||||
|
||||
103(`john@58.38.22.103`)是唯一生产 Portal 主机。这个 skill 是打包、发布、验证、回滚的**唯一入口**——不要现场发明新流程,不要直接 SSH 到 103 手改代码或手写文件到 `MindSpace/`。
|
||||
103(`john@180.159.29.143`)是唯一生产 Portal 主机。这个 skill 是打包、发布、验证、回滚的**唯一入口**——不要现场发明新流程,不要直接 SSH 到 103 手改代码或手写文件到 `MindSpace/`。
|
||||
|
||||
## 0. 发布前置检查(必须)
|
||||
|
||||
@@ -96,7 +96,7 @@ curl -k -i https://m.tkmind.cn/auth/login \
|
||||
### 3.2 Portal + 九个 goosed 实例(SSH 103)
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 '
|
||||
ssh john@180.159.29.143 '
|
||||
printf "portal="; curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8081/api/status
|
||||
for p in $(seq 18006 18014); do
|
||||
printf "goosed_${p}="
|
||||
@@ -113,7 +113,7 @@ ssh john@58.38.22.103 '
|
||||
### 3.3 日志关键词检查(SSH 103)
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 '
|
||||
ssh john@180.159.29.143 '
|
||||
tail -200 ~/Library/Logs/memind-portal.log |
|
||||
egrep "User auth bootstrap failed|ER_PARSE_ERROR|Cannot find package|Failed to add extension|No such file|Unknown extension|Provider not set|not configured" || echo "no error keywords found"
|
||||
'
|
||||
@@ -164,7 +164,7 @@ bash scripts/release-portal-runtime-prod.sh --skip-tests --yes
|
||||
**紧急方式**(本地仓库回不去、必须直接用远端备份时):
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 '
|
||||
ssh john@180.159.29.143 '
|
||||
launchctl bootout gui/$(id -u)/cn.tkmind.memind-portal 2>/dev/null || true
|
||||
lsof -tiTCP:8081 -sTCP:LISTEN | xargs kill 2>/dev/null || true
|
||||
mv /Users/john/Project/Memind /Users/john/Project/Memind-bad-release-$(date +%s)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# g2.tkmind.cn → 301 跳转到 m.tkmind.cn(保留微信 webhook 直至后台改完服务器 URL)
|
||||
# 安装:/etc/nginx/conf.d/g2.tkmind.cn.conf
|
||||
# 不再依赖 19081 reverse tunnel / 10.10.* 内网地址,直接回源到 58.38.22.103:8081
|
||||
# 不再依赖 19081 reverse tunnel / 10.10.* 内网地址,直接回源到 180.159.29.143:8081
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
@@ -21,7 +21,7 @@ server {
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
location = /webhooks/wechat-mp/messages {
|
||||
proxy_pass http://58.38.22.103:8081;
|
||||
proxy_pass http://180.159.29.143:8081;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host g2.tkmind.cn;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# m.tkmind.cn → 105 nginx 直连 103 Memind Portal (58.38.22.103:8081)
|
||||
# m.tkmind.cn → 105 nginx 直连 103 Memind Portal (180.159.29.143:8081)
|
||||
# 安装:/etc/nginx/conf.d/m.tkmind.cn.conf
|
||||
# 替换旧 ai_mind 配置,作为 Memind H5 正式公网入口
|
||||
# 不再依赖 19081 reverse tunnel / 10.10.* 内网地址,直接回源到 58.38.22.103:8081
|
||||
# 不再依赖 19081 reverse tunnel / 10.10.* 内网地址,直接回源到 180.159.29.143:8081
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
@@ -24,7 +24,7 @@ server {
|
||||
client_max_body_size 100M;
|
||||
|
||||
location = /webhooks/wechat-mp/messages {
|
||||
proxy_pass http://58.38.22.103:8081;
|
||||
proxy_pass http://180.159.29.143:8081;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host m.tkmind.cn;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
@@ -41,7 +41,7 @@ server {
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://58.38.22.103:8081;
|
||||
proxy_pass http://180.159.29.143:8081;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# wechat.m.tkmind.cn → 微信服务号消息推送专用域名
|
||||
# 安装:/etc/nginx/conf.d/wechat.m.tkmind.cn.conf
|
||||
# 不再依赖 19081 reverse tunnel / 10.10.* 内网地址,直接回源到 58.38.22.103:8081
|
||||
# 不再依赖 19081 reverse tunnel / 10.10.* 内网地址,直接回源到 180.159.29.143:8081
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
@@ -21,7 +21,7 @@ server {
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
location = /webhooks/wechat-mp/messages {
|
||||
proxy_pass http://58.38.22.103:8081;
|
||||
proxy_pass http://180.159.29.143:8081;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host m.tkmind.cn;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
### 103
|
||||
|
||||
- 主机:`john@58.38.22.103`
|
||||
- 主机:`john@180.159.29.143`
|
||||
- 主运行根目录:`/Users/john/Project`
|
||||
- 当前业务目录:
|
||||
- `/Users/john/Project/Memind`
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
- Portal release: `20260707-154245-ea4c6b7`
|
||||
- Git commit: `ea4c6b707c2a95c5c63eed4ed5dc02f8a6f786bc`
|
||||
- Branch used for release: `release/rich-page-router-0707`
|
||||
- Production host: `john@58.38.22.103`
|
||||
- Production host: `john@180.159.29.143`
|
||||
|
||||
## 安全边界
|
||||
|
||||
@@ -60,7 +60,7 @@ MEMIND_SESSION_BROKER_METRICS=1
|
||||
103 本机:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 'curl -fsS http://127.0.0.1:8081/api/status'
|
||||
ssh john@180.159.29.143 'curl -fsS http://127.0.0.1:8081/api/status'
|
||||
```
|
||||
|
||||
外部入口:
|
||||
@@ -72,7 +72,7 @@ curl -ksS https://m.tkmind.cn/api/status
|
||||
Manifest:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 'cat /Users/john/Project/Memind/.release-manifest.txt'
|
||||
ssh john@180.159.29.143 'cat /Users/john/Project/Memind/.release-manifest.txt'
|
||||
```
|
||||
|
||||
### 2. 账号级 smoke
|
||||
@@ -195,7 +195,7 @@ Portal runtime 发布会移动旧 live 目录到 archive,再展开新 runtime
|
||||
确认命令:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 '
|
||||
ssh john@180.159.29.143 '
|
||||
/opt/homebrew/bin/docker exec goosed-prod-1 sh -lc "ls -l /Users/john/Project/Memind/MindSpace/<user-id>/public/<file>.html"
|
||||
ls -l /Users/john/Project/Memind/MindSpace/<user-id>/public/<file>.html
|
||||
'
|
||||
@@ -204,7 +204,7 @@ ssh john@58.38.22.103 '
|
||||
修复:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 '
|
||||
ssh john@180.159.29.143 '
|
||||
cd /Users/john/Project/goosed-prod
|
||||
/opt/homebrew/bin/docker compose -f docker-compose.prod.yml up -d --force-recreate
|
||||
'
|
||||
@@ -213,7 +213,7 @@ ssh john@58.38.22.103 '
|
||||
健康检查:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 '
|
||||
ssh john@180.159.29.143 '
|
||||
/opt/homebrew/bin/docker ps --format "{{.Names}} {{.Status}}" | grep goosed-prod | sort -V
|
||||
for p in $(seq 18006 18014); do curl -kfsS "https://127.0.0.1:$p/status" >/dev/null || exit 1; done
|
||||
curl -fsS http://127.0.0.1:8081/api/status
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
### 主机与入口
|
||||
|
||||
- 生产主机:`john@58.38.22.103`
|
||||
- 生产主机:`john@180.159.29.143`
|
||||
- 当前 SSH 端口:`22`
|
||||
- 旧的 `2222` 已关闭,不应再写入文档或脚本
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Local Colima may exist for other workloads, but local Memind/goosed is not suppo
|
||||
|
||||
| Item | Value |
|
||||
|------|-------|
|
||||
| Host | `58.38.22.103` |
|
||||
| Host | `180.159.29.143` |
|
||||
| Live directory | `/Users/john/Project/Memind` |
|
||||
| Service | `cn.tkmind.memind-portal` |
|
||||
| Start script | `/Users/john/Project/Memind/scripts/run-memind-portal-prod.sh` |
|
||||
@@ -39,7 +39,7 @@ Portal must be released as a runtime artifact. Do not edit source directly on 10
|
||||
|
||||
| Item | Value |
|
||||
|------|-------|
|
||||
| Stable edge upstream | `105 nginx -> 58.38.22.103:8081` |
|
||||
| Stable edge upstream | `105 nginx -> 180.159.29.143:8081` |
|
||||
| Identity router | `127.0.0.1:18082` |
|
||||
| Canary edge tunnel | `105 127.0.0.1:19082 -> 103 127.0.0.1:18082` |
|
||||
| Candidate Portal | `127.0.0.1:18081` |
|
||||
@@ -94,8 +94,8 @@ or whose `gitSha` does not match the runtime artifact manifest.
|
||||
**Critical:** Native goosed uses host PostgreSQL on `127.0.0.1:5432`. If PostgreSQL stops, H5 `/agent/start` hangs ~60s. Recovery:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 'bash /Users/john/Project/Memind/scripts/ensure-goose-session-postgres.sh'
|
||||
ssh john@58.38.22.103 '/opt/homebrew/opt/postgresql@17/bin/pg_isready -h 127.0.0.1 -p 5432'
|
||||
ssh john@180.159.29.143 'bash /Users/john/Project/Memind/scripts/ensure-goose-session-postgres.sh'
|
||||
ssh john@180.159.29.143 '/opt/homebrew/opt/postgresql@17/bin/pg_isready -h 127.0.0.1 -p 5432'
|
||||
```
|
||||
|
||||
Port layout (native listens directly on host port):
|
||||
@@ -153,18 +153,18 @@ is legacy compatibility/storage context only. If Portal or goosed config still p
|
||||
In particular, check these before debugging missing folders/pages:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 'grep -E "^(MINDSPACE|MINDSPACE_ROOT|MINDSPACE_SERVICE|MEMIND_SHARED_ROOT)=" /Users/john/Project/Memind/.env /Users/john/Project/goosed-prod/.env 2>/dev/null'
|
||||
ssh john@58.38.22.103 'launchctl print gui/$(id -u)/cn.tkmind.mindspace-service | grep -E "program =|working directory|MINDSPACE|MEMIND_SHARED"'
|
||||
ssh john@58.38.22.103 '/opt/homebrew/bin/docker inspect goosed-prod-1 --format "{{json .Mounts}}"'
|
||||
ssh john@180.159.29.143 'grep -E "^(MINDSPACE|MINDSPACE_ROOT|MINDSPACE_SERVICE|MEMIND_SHARED_ROOT)=" /Users/john/Project/Memind/.env /Users/john/Project/goosed-prod/.env 2>/dev/null'
|
||||
ssh john@180.159.29.143 'launchctl print gui/$(id -u)/cn.tkmind.mindspace-service | grep -E "program =|working directory|MINDSPACE|MEMIND_SHARED"'
|
||||
ssh john@180.159.29.143 '/opt/homebrew/bin/docker inspect goosed-prod-1 --format "{{json .Mounts}}"'
|
||||
```
|
||||
|
||||
## Current verification commands
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 'curl -fsS http://127.0.0.1:8081/api/status'
|
||||
ssh john@58.38.22.103 'curl -fsS http://127.0.0.1:8082/health'
|
||||
ssh john@58.38.22.103 'for p in $(seq 18006 18014); do curl -kfsS https://127.0.0.1:$p/status; echo; done'
|
||||
ssh john@58.38.22.103 'grep ^TKMIND_API_TARGETS= /Users/john/Project/Memind/.env'
|
||||
ssh john@180.159.29.143 'curl -fsS http://127.0.0.1:8081/api/status'
|
||||
ssh john@180.159.29.143 'curl -fsS http://127.0.0.1:8082/health'
|
||||
ssh john@180.159.29.143 'for p in $(seq 18006 18014); do curl -kfsS https://127.0.0.1:$p/status; echo; done'
|
||||
ssh john@180.159.29.143 'grep ^TKMIND_API_TARGETS= /Users/john/Project/Memind/.env'
|
||||
```
|
||||
|
||||
## imgproxy
|
||||
@@ -196,8 +196,8 @@ Install on 103 reads Portal `.env` for signing keys and storage root; launchd pl
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 'curl -fsS http://127.0.0.1:20082/health'
|
||||
ssh john@58.38.22.103 'curl -fsS http://10.10.0.2:20081/health'
|
||||
ssh john@180.159.29.143 'curl -fsS http://127.0.0.1:20082/health'
|
||||
ssh john@180.159.29.143 'curl -fsS http://10.10.0.2:20081/health'
|
||||
curl -fsS https://img.tkmind.cn/health
|
||||
```
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ ssh root@120.26.184.105 '
|
||||
1. 先备份并校验 105 活动的 `m.tkmind.cn.conf` 与 `wechat.m.tkmind.cn.conf`。
|
||||
2. 在 103 启动独立路由器 `18082`,并通过反向隧道只暴露为 105 本机 `19082`。
|
||||
3. 候选、身份路由和隧道全部通过后,脚本才把两份 nginx 上游从
|
||||
`58.38.22.103:8081` 切到 `127.0.0.1:19082`。
|
||||
`180.159.29.143:8081` 切到 `127.0.0.1:19082`。
|
||||
4. 必须先 `nginx -t`,再 reload,并在切换后主动证明候选故障会回落稳定 8081;
|
||||
任一步失败恢复备份并回到稳定入口。
|
||||
5. 回滚只能使用 `scripts/rollback-portal-canary-prod.sh`,禁止在 105 手工 `sed -i`。
|
||||
|
||||
+11
-11
@@ -11,38 +11,38 @@
|
||||
├─ 转发到 103 / Studio 服务器 → Studio portal 127.0.0.1:8081
|
||||
└─ [SSH 正向隧道 127.0.0.1:18080] → 105 portal :8080
|
||||
│
|
||||
105 / Studio 两个 portal ── 都连 ──→ Studio goosed 58.38.22.103:18006(主)
|
||||
└────→ Studio goosed 58.38.22.103:18007(备用/第二实例)
|
||||
105 / Studio 两个 portal ── 都连 ──→ Studio goosed 180.159.29.143:18006(主)
|
||||
└────→ Studio goosed 180.159.29.143:18007(备用/第二实例)
|
||||
MindSpace 服务 ──→ Studio canonical /Users/john/MindSpace (:8082)
|
||||
旧发布页目录 ──兼容/存量──→ /Users/john/Project/Memind/MindSpace
|
||||
```
|
||||
|
||||
- **为什么不用旧的 Cloudflare 直连**:当前入口已经迁到阿里云解析,外网流量先到 105,再由 105 转发到 103 / Studio。
|
||||
- **为什么直接固定公网 IP**:当前服务器互联和部署链路统一使用固定公网地址 `120.26.184.105` / `58.38.22.103`,不再依赖内网地址、域名别名或临时回退链路。
|
||||
- **为什么直接固定公网 IP**:当前服务器互联和部署链路统一使用固定公网地址 `120.26.184.105` / `180.159.29.143`,不再依赖内网地址、域名别名或临时回退链路。
|
||||
|
||||
## 105 / 103 联通约束(硬性要求)
|
||||
|
||||
**要求:域名只做用户入口;服务器互联、健康检查、部署同步统一走固定公网 IP。**
|
||||
|
||||
- 105 运维地址:`120.26.184.105`。
|
||||
- 103 / Studio 运维地址:`58.38.22.103`。
|
||||
- 如果 SSH 别名未更新,直接写 `root@120.26.184.105` / `john@58.38.22.103`。
|
||||
- Studio / goosed 目标直接写 `58.38.22.103`,不要写 `g2.tkmind.cn` 或 105 域名。
|
||||
- 103 / Studio 运维地址:`180.159.29.143`。
|
||||
- 如果 SSH 别名未更新,直接写 `root@120.26.184.105` / `john@180.159.29.143`。
|
||||
- Studio / goosed 目标直接写 `180.159.29.143`,不要写 `g2.tkmind.cn` 或 105 域名。
|
||||
- Studio 对 105 的联通入口是 `127.0.0.1:18080`,不是 `105.tkmind.cn`。
|
||||
|
||||
快速自检(每次操作 105 前):
|
||||
|
||||
```bash
|
||||
ping -c 1 120.26.184.105
|
||||
ping -c 1 58.38.22.103
|
||||
ping -c 1 180.159.29.143
|
||||
ssh root@120.26.184.105 'echo ssh-ok-105'
|
||||
ssh john@58.38.22.103 'echo ssh-ok-103'
|
||||
ssh john@180.159.29.143 'echo ssh-ok-103'
|
||||
curl -s http://127.0.0.1:18080/api/status # 应返回 ok
|
||||
```
|
||||
|
||||
- 禁止:用 `105.tkmind.cn` 做 API 健康检查或同步链路入口。
|
||||
- 禁止:把 105 公网 IP 当成业务上游写进 Caddy / goosed 目标;公网 IP 只保留给外部入口或应急 SSH。
|
||||
- 应急:如果 SSH 别名或本地缓存配置仍残留旧地址,显式设置 `H5_DEPLOY_HOST=root@120.26.184.105`,Studio 目标改为 `john@58.38.22.103`。
|
||||
- 应急:如果 SSH 别名或本地缓存配置仍残留旧地址,显式设置 `H5_DEPLOY_HOST=root@120.26.184.105`,Studio 目标改为 `john@180.159.29.143`。
|
||||
|
||||
## 调灰度比例(最常用)
|
||||
|
||||
@@ -58,7 +58,7 @@ curl -s http://127.0.0.1:18080/api/status # 应返回 ok
|
||||
改完**零停机生效**:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103
|
||||
ssh john@180.159.29.143
|
||||
cd ~/Project/Memind/scripts
|
||||
caddy validate --config g2-lb.Caddyfile # 可选,先校验
|
||||
caddy reload --config g2-lb.Caddyfile # 零停机热重载
|
||||
@@ -101,7 +101,7 @@ curl -s http://127.0.0.1:2019/reverse_proxy/upstreams # 在 103 / Studio 上
|
||||
| 入口反代 | 103 / Studio `scripts/g2-lb.Caddyfile`(LaunchAgent `cn.tkmind.g2-lb`,:8090) | 入口转发 + 健康检查 |
|
||||
| 正向隧道 | 103 / Studio `scripts/memind-fwd-tunnel.sh`(LaunchAgent `cn.tkmind.memind-fwd-tunnel`) | Studio `127.0.0.1:18080` → 105 `:8080` |
|
||||
| 反向隧道 | 103 / Studio `scripts/memind-mac-tunnel.sh`(LaunchAgent `cn.tkmind.memind-tunnel`) | 105 经此挂载 Studio MindSpace 文件 |
|
||||
| 105 portal | 105 systemd `goose-h5`(:8080),`/root/tkmind_go/ui/h5/.env` | 无状态前端,主 Goose 指向 `https://58.38.22.103:18006`,第二 Goose 指向 `https://58.38.22.103:18007` |
|
||||
| 105 portal | 105 systemd `goose-h5`(:8080),`/root/tkmind_go/ui/h5/.env` | 无状态前端,主 Goose 指向 `https://180.159.29.143:18006`,第二 Goose 指向 `https://180.159.29.143:18007` |
|
||||
| 105 文件挂载 | 105 systemd `.mount` → `/mnt/memind-shared`(rclone over 反向隧道) | 发布页/工作区共享只读 |
|
||||
|
||||
## 关键约束(改动前必读)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
| 数据库 | PostgreSQL + pgvector | **MySQL**(`db.mjs:2` `mysql2/promise`) | 没有向量检索能力,Experience 检索需另建 |
|
||||
| DB 连接池 | —— | **`connectionLimit: 10`**(`db.mjs:32`,写死) | 100 并发 × 多查询 → 连接耗尽、请求排队 |
|
||||
| Goose 实例 | "双 Goose 负载均衡" | **同一台 Mac(Studio/103)上的两个进程** `:18006` / `:18007` | 不是负载均衡,是单点的两进程;共享 CPU/内存/libuv |
|
||||
| Goose 运行环境 | —— | 个人开发 Mac `john@58.38.22.103` | 生产 Agent 集群跑在个人 Mac 上 = 最大架构债 |
|
||||
| Goose 运行环境 | —— | 个人开发 Mac `john@180.159.29.143` | 生产 Agent 集群跑在个人 Mac 上 = 最大架构债 |
|
||||
| Session 路由 | —— | DB `goosed_node` 整数下标(0/1),`getSessionNode` @ `user-auth.mjs:651` | 扩到第 3 台直接失效;且该列在 `schema.sql` 里不存在,靠 `columnExists` 运行时动态补 |
|
||||
| 长任务 | —— | 纯 HTTP 流式透传(`tkmind-proxy.mjs`),无队列 | 连接被占满整个执行周期;微信 5s 超时直接死 |
|
||||
| Experience / 上下文 | 共享经验库 | **在各 goosed 进程内存中** | 两实例经验孤岛;重启/部署即丢上下文 |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> **状态:** 草案(本地 v1.49 证据已齐;**未授权**在 103/105 执行任何操作)
|
||||
>
|
||||
> **适用范围:** 103(`58.38.22.103` / Mac Studio arm64)将 goosed 运行时从 **1.41 native** 升级到 **v1.49 TKMind 补丁树**
|
||||
> **适用范围:** 103(`180.159.29.143` / Mac Studio arm64)将 goosed 运行时从 **1.41 native** 升级到 **v1.49 TKMind 补丁树**
|
||||
>
|
||||
> **不包含:** Portal runtime 整包发版(见 [portal-release skill](../.claude/skills/portal-release/SKILL.md))、105 nginx 灰度(见 [release-canary-103.md](./release-canary-103.md))、Memory owner 切换(见 [TKMIND_V1_49_MIGRATION.md](../TKMIND_V1_49_MIGRATION.md))
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
在 **103 上只读**执行,保存到本机 `test/_103_baselines/pre-v149-YYYYMMDD/`:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 '
|
||||
ssh john@180.159.29.143 '
|
||||
date -u +%Y-%m-%dT%H:%M:%SZ
|
||||
cat /Users/john/Project/Memind/.release-manifest.txt
|
||||
grep -E "^(TKMIND_API_TARGETS|GOOSED_RUNTIME|GOOSED_MCP_|GOOSE_SESSION|MEMIND_DEEPSEEK)" /Users/john/Project/Memind/.env | sed "s/=.*/=***/"
|
||||
@@ -153,7 +153,7 @@ Hermit 根目录默认:`/Users/john/Project/tkmind_go-v141-prod-prep/.hermit/r
|
||||
```bash
|
||||
# 本机
|
||||
tar -czf /tmp/goose-v149-hermit-vendor.tgz -C /Users/john/Project/tkmind_go-v141-prod-prep .hermit
|
||||
scp /tmp/goose-v149-hermit-vendor.tgz john@58.38.22.103:/tmp/
|
||||
scp /tmp/goose-v149-hermit-vendor.tgz john@180.159.29.143:/tmp/
|
||||
# 103
|
||||
mkdir -p /Users/john/Project/tkmind_go-v141-prod-prep
|
||||
tar -xzf /tmp/goose-v149-hermit-vendor.tgz -C /Users/john/Project/tkmind_go-v141-prod-prep
|
||||
@@ -345,7 +345,7 @@ Gate:维护窗 commit 须绑定 **Core + Impact Gate report**([production-re
|
||||
|
||||
### Phase 5 — 恢复流量
|
||||
|
||||
- [ ] 105 nginx → `58.38.22.103:8081`(或灰度 `19082` 路径)
|
||||
- [ ] 105 nginx → `180.159.29.143:8081`(或灰度 `19082` 路径)
|
||||
- [ ] 外网 `https://m.tkmind.cn/api/status`
|
||||
|
||||
### Phase 6 — 观察 24–72h
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 103 生产 goosed 迁移计划:Docker/Colima → Native 多实例
|
||||
|
||||
> **状态:** 已执行(2026-07-30 维护窗)
|
||||
> **适用范围:** 103(`58.38.22.103` / Mac Studio)生产环境
|
||||
> **适用范围:** 103(`180.159.29.143` / Mac Studio)生产环境
|
||||
> **目标运行时:** 去掉 goosed 对 Docker/Colima 的依赖,改为 native launchd 多实例(`18006`–`18014`)
|
||||
> **前提:** 允许维护窗口停机;**不要求**进行中 SSE/工具任务不断线
|
||||
> **关联拓扑:** [103-runtime-topology.md](./103-runtime-topology.md)
|
||||
@@ -129,11 +129,11 @@
|
||||
|
||||
```bash
|
||||
# 只读,禁止改 103
|
||||
ssh john@58.38.22.103 'cat /Users/john/Project/Memind/.release-manifest.txt'
|
||||
ssh john@58.38.22.103 'grep ^TKMIND_API_TARGETS= /Users/john/Project/Memind/.env'
|
||||
ssh john@58.38.22.103 'grep ^GOOSED_TAG= /Users/john/Project/goosed-prod/.env'
|
||||
ssh john@58.38.22.103 'for p in $(seq 18006 18014); do curl -kfsS --connect-timeout 2 https://127.0.0.1:$p/status; echo; done'
|
||||
ssh john@58.38.22.103 'curl -fsS http://127.0.0.1:8082/mindspace/v1/contract | head -c 400'
|
||||
ssh john@180.159.29.143 'cat /Users/john/Project/Memind/.release-manifest.txt'
|
||||
ssh john@180.159.29.143 'grep ^TKMIND_API_TARGETS= /Users/john/Project/Memind/.env'
|
||||
ssh john@180.159.29.143 'grep ^GOOSED_TAG= /Users/john/Project/goosed-prod/.env'
|
||||
ssh john@180.159.29.143 'for p in $(seq 18006 18014); do curl -kfsS --connect-timeout 2 https://127.0.0.1:$p/status; echo; done'
|
||||
ssh john@180.159.29.143 'curl -fsS http://127.0.0.1:8082/mindspace/v1/contract | head -c 400'
|
||||
```
|
||||
|
||||
保存到本机 `test/_103_baselines/pre-native-migration-YYYYMMDD/`。
|
||||
@@ -241,7 +241,7 @@ done
|
||||
|
||||
### Phase 5 — 恢复流量
|
||||
|
||||
- [ ] 105 nginx 恢复 `58.38.22.103:8081`(或既定 upstream)
|
||||
- [ ] 105 nginx 恢复 `180.159.29.143:8081`(或既定 upstream)
|
||||
- [ ] 外网抽样:`https://m.tkmind.cn/api/status`
|
||||
- [ ] 维护结束公告
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
4. 生产用户公开入口是 `https://g2.tkmind.cn`,不是裸 IP,也不是本地 localhost。
|
||||
5. 运维、发包、健康检查统一走固定公网地址,不再依赖 `10.10.*` 局域网:
|
||||
- `105`:`120.26.184.105`
|
||||
- `103 / Studio`:`58.38.22.103`
|
||||
- `103 / Studio`:`180.159.29.143`
|
||||
|
||||
## 迁移原则
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
3. `103` 只接收运行产物、继承持久目录、启动服务。
|
||||
4. 生产差异通过 `.env` 和启动环境注入,不通过“线上源码和本地源码不同”来兜底。
|
||||
5. 迁移前必须先打整包备份,且单独备份持久目录。
|
||||
6. 后续所有发布脚本默认都应优先直连 `58.38.22.103`,只有用户明确要求时才允许覆盖 `STUDIO_HOST`。
|
||||
6. 后续所有发布脚本默认都应优先直连 `180.159.29.143`,只有用户明确要求时才允许覆盖 `STUDIO_HOST`。
|
||||
|
||||
## Portal 运行产物
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ G8 不能替代 G0-G7;发布后健康检查也不能为发布前缺失的场
|
||||
|
||||
全场景执行器必须在代码层拒绝以下目标:
|
||||
|
||||
- `58.38.22.103`
|
||||
- `180.159.29.143`
|
||||
- `m.tkmind.cn`
|
||||
- 生产 MySQL、PostgreSQL、Redis、SearXNG、Deep Search 和图片服务地址
|
||||
- 生产用户工作区、发布目录和对象存储
|
||||
@@ -607,7 +607,7 @@ failed = skipped = blocked = unknown = cleanup_failed = 0
|
||||
用户级灰度入口由 `scripts/release-portal-canary-prod.sh` 安装:稳定 Portal 保持 8081,
|
||||
候选 Portal 使用 18081,候选 goosed 使用 18015,身份路由器使用 18082;105 使用独立
|
||||
19082 反向隧道访问路由器。活动 nginx 上游在脚本备份、`nginx -t` 和健康检查后,才允许
|
||||
从稳定 `58.38.22.103:8081` 切到 `127.0.0.1:19082`。灰度身份必须
|
||||
从稳定 `180.159.29.143:8081` 切到 `127.0.0.1:19082`。灰度身份必须
|
||||
使用不可变用户 ID、唯一用户名或微信绑定账号标识;禁止按显示昵称(包括“唐”)匹配。未命中、
|
||||
身份解析失败或候选不可用必须自动回落稳定版本。
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
## 健康验证
|
||||
|
||||
```bash
|
||||
ssh 58.38.22.103 'curl -sf http://127.0.0.1:8081/api/status && echo'
|
||||
ssh 180.159.29.143 'curl -sf http://127.0.0.1:8081/api/status && echo'
|
||||
```
|
||||
|
||||
发版脚本已在 103 上完成 health check(200)。
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
## 健康验证
|
||||
|
||||
```bash
|
||||
ssh 58.38.22.103 'curl -sf http://127.0.0.1:8081/api/status && echo'
|
||||
ssh 180.159.29.143 'curl -sf http://127.0.0.1:8081/api/status && echo'
|
||||
```
|
||||
|
||||
发版脚本已在 103 上完成 health check(200)。
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
## 健康验证
|
||||
|
||||
```bash
|
||||
ssh 58.38.22.103 'curl -sf http://127.0.0.1:8081/api/status && echo'
|
||||
ssh 180.159.29.143 'curl -sf http://127.0.0.1:8081/api/status && echo'
|
||||
```
|
||||
|
||||
**2026-06-30 发版后验证:** `ok`;`.env` 中 `H5_SCHEDULE_ENABLED=1`、`H5_REMINDER_WORKER_ENABLED=1`。
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
## 健康验证
|
||||
|
||||
```bash
|
||||
ssh 58.38.22.103 'curl -sf http://127.0.0.1:8081/api/status && echo'
|
||||
ssh 180.159.29.143 'curl -sf http://127.0.0.1:8081/api/status && echo'
|
||||
```
|
||||
|
||||
**2026-06-30 发版后验证:** `ok`;live 目录为无源码 runtime(`server.mjs` + `dist/` + `scripts/run-memind-portal-prod.sh`)。
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
## 健康验证
|
||||
|
||||
```bash
|
||||
ssh 58.38.22.103 'curl -sf http://127.0.0.1:8081/api/status && echo'
|
||||
ssh 58.38.22.103 'curl -s -o /dev/null -w "brand=%{http_code}\n" http://127.0.0.1:8081/brand/tkmind-icon.png'
|
||||
ssh 180.159.29.143 'curl -sf http://127.0.0.1:8081/api/status && echo'
|
||||
ssh 180.159.29.143 'curl -s -o /dev/null -w "brand=%{http_code}\n" http://127.0.0.1:8081/brand/tkmind-icon.png'
|
||||
```
|
||||
|
||||
**2026-06-30 发版后验证:** `ok`;`brand_icon=200`;live 目录为无源码 runtime(`server.mjs` + `dist/` + `scripts/run-memind-portal-prod.sh`)。
|
||||
|
||||
@@ -6,7 +6,7 @@ Portal 的首个生产动作必须是用户级灰度,不能直接整包替换
|
||||
|
||||
| 组件 | 地址 | 说明 |
|
||||
|---|---|---|
|
||||
| 稳定 Portal | `103:8081` | 105 当前直接回源 `58.38.22.103:8081`;灰度发布不替换该进程 |
|
||||
| 稳定 Portal | `103:8081` | 105 当前直接回源 `180.159.29.143:8081`;灰度发布不替换该进程 |
|
||||
| 灰度路由器 | `103 127.0.0.1:18082` | 避开已占用的 18080;只通过专用灰度隧道暴露给 105 |
|
||||
| 灰度边缘隧道 | `105 127.0.0.1:19082` | 反向 SSH 到 103 的 18082;不复用已退役的 19081 链路 |
|
||||
| 候选 Portal | `127.0.0.1:18081` | 独立、无源码 runtime;使用被动运行时模式 |
|
||||
@@ -69,10 +69,10 @@ bash scripts/release-portal-canary-prod.sh --yes
|
||||
7. 验证不可变身份配置、稳定/候选/依赖健康和路由器诊断。
|
||||
8. 启动独立的 `105:19082 -> 103:18082` 灰度隧道并从 105 验证。
|
||||
9. 短暂阻止新 Agent Run,排空活动任务。
|
||||
10. 逐文件原子替换、验证并 reload 105 nginx,把活动上游从 `58.38.22.103:8081` 切到 `127.0.0.1:19082`。
|
||||
10. 逐文件原子替换、验证并 reload 105 nginx,把活动上游从 `180.159.29.143:8081` 切到 `127.0.0.1:19082`。
|
||||
11. 主动停止候选,证明目标身份会自动回落稳定版,再恢复候选。
|
||||
|
||||
任一步失败都会把 105 nginx 恢复到稳定 `58.38.22.103:8081`,停止灰度隧道和候选服务,
|
||||
任一步失败都会把 105 nginx 恢复到稳定 `180.159.29.143:8081`,停止灰度隧道和候选服务,
|
||||
并保留候选目录与备份证据。活动 nginx 配置变更只能由已提交的发布脚本执行,禁止在线手改。
|
||||
|
||||
## 回滚
|
||||
@@ -83,7 +83,7 @@ bash scripts/rollback-portal-canary-prod.sh --yes
|
||||
|
||||
回滚只执行以下动作:
|
||||
|
||||
- 把 105 两份活动 nginx 上游恢复到稳定 Portal `58.38.22.103:8081` 并验证 reload。
|
||||
- 把 105 两份活动 nginx 上游恢复到稳定 Portal `180.159.29.143:8081` 并验证 reload。
|
||||
- 停止灰度隧道、灰度路由器、候选 Portal、候选 goosed 和 DeepSeek 兼容代理。
|
||||
- 清除发布排水标记。
|
||||
- 验证稳定 Portal 和 105 HTTPS 入口健康。
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
- Studio 上 Goose 是双实例:`18006` 主、`18007` 备用/第二实例。
|
||||
- 105 不跑 Goose;105 只是无状态 H5 前端,代理回 Studio。
|
||||
- Studio 可直接 SSH:`ssh john@58.38.22.103`。不要为了查 Studio 再绕 105。
|
||||
- Studio 可直接 SSH:`ssh john@180.159.29.143`。不要为了查 Studio 再绕 105。
|
||||
- H5 会话权限不是只看数据库,还要看会话启动时实际下发给 Goose 的 `extension_overrides`。
|
||||
|
||||
## 这次案例结论
|
||||
@@ -242,7 +242,7 @@ Session 路由:
|
||||
优先查 Studio:
|
||||
|
||||
```bash
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=8 john@58.38.22.103 \
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=8 john@180.159.29.143 \
|
||||
"cd /Users/john/Project/Memind && ps eww -p \$(pgrep -f 'node .*server.mjs' | head -1) | tr ' ' '\n' | grep '^TKMIND_API_TARGET'"
|
||||
```
|
||||
|
||||
@@ -256,7 +256,7 @@ ssh -i ~/.ssh/id_ed25519 root@120.26.184.105 \
|
||||
### 查生产用户权限
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103
|
||||
ssh john@180.159.29.143
|
||||
cd /Users/john/Project/Memind
|
||||
|
||||
node -e 'process.loadEnvFile(".env"); const mysql=require("mysql2/promise"); (async()=>{ const pool=mysql.createPool(process.env.DATABASE_URL); const userId="a70ff537-8908-486e-9b6c-042e07cc25db"; const [userCaps]=await pool.query("SELECT capability_key, allowed FROM h5_capability_grants WHERE subject_type=\"user\" AND subject_id=? ORDER BY capability_key",[userId]); const [roleCaps]=await pool.query("SELECT capability_key, allowed FROM h5_capability_grants WHERE subject_type=\"role\" AND subject_id=\"user\" ORDER BY capability_key"); const [userPolicies]=await pool.query("SELECT policy_key, policy_value FROM h5_user_policies WHERE subject_type=\"user\" AND subject_id=? ORDER BY policy_key",[userId]); const [rolePolicies]=await pool.query("SELECT policy_key, policy_value FROM h5_user_policies WHERE subject_type=\"role\" AND subject_id=\"user\" ORDER BY policy_key"); console.log(JSON.stringify({userCaps,roleCaps,userPolicies,rolePolicies},null,2)); await pool.end(); })();'
|
||||
@@ -265,7 +265,7 @@ node -e 'process.loadEnvFile(".env"); const mysql=require("mysql2/promise"); (as
|
||||
### 让线上代码直接算最终 session policy
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103
|
||||
ssh john@180.159.29.143
|
||||
cd /Users/john/Project/Memind
|
||||
|
||||
node --input-type=module -e 'import process from "node:process"; process.loadEnvFile(".env"); const { createDbPool } = await import("./db.mjs"); const { createUserAuth } = await import("./user-auth.mjs"); const pool = createDbPool(); const userAuth = createUserAuth(pool, { h5Root: process.cwd() }); const policy = await userAuth.getAgentSessionPolicy("a70ff537-8908-486e-9b6c-042e07cc25db"); console.log(JSON.stringify(policy, null, 2)); await pool.end();'
|
||||
@@ -274,7 +274,7 @@ node --input-type=module -e 'import process from "node:process"; process.loadEnv
|
||||
### 查用户最近 session 及节点分布
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103
|
||||
ssh john@180.159.29.143
|
||||
cd /Users/john/Project/Memind
|
||||
|
||||
node -e 'process.loadEnvFile(".env"); const mysql=require("mysql2/promise"); (async()=>{ const pool=mysql.createPool(process.env.DATABASE_URL); const [rows]=await pool.query("SELECT agent_session_id, user_id, goosed_node, created_at FROM h5_user_sessions WHERE user_id = ? ORDER BY created_at DESC LIMIT 20", ["a70ff537-8908-486e-9b6c-042e07cc25db"]); console.log(JSON.stringify(rows,null,2)); await pool.end(); })();'
|
||||
@@ -290,7 +290,7 @@ node -e 'process.loadEnvFile(".env"); const mysql=require("mysql2/promise"); (as
|
||||
然后查扩展:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 \
|
||||
ssh john@180.159.29.143 \
|
||||
'curl -sk -H "X-Secret-Key: local-dev-secret" https://127.0.0.1:18006/sessions/20260620_45/extensions | python3 -m json.tool'
|
||||
```
|
||||
|
||||
|
||||
+6
-6
@@ -7,7 +7,7 @@
|
||||
|
||||
## 0. 先确认边界
|
||||
|
||||
- 生产 Portal 主机是 `103 / Studio`:`john@58.38.22.103`,默认 SSH 命令不要再写旧端口 `2222`。
|
||||
- 生产 Portal 主机是 `103 / Studio`:`john@180.159.29.143`,默认 SSH 命令不要再写旧端口 `2222`。
|
||||
- `105` 是云侧入口/历史链路,不是 Portal 源码真相;不要 SSH 到 105 直接改业务代码。
|
||||
- Portal 生产目录:`/Users/john/Project/Memind`。
|
||||
- goosed 生产目录:`/Users/john/Project/goosed-prod`。
|
||||
@@ -84,7 +84,7 @@ bash scripts/release-portal-fast-prod.sh --yes
|
||||
- 备份并校验 105 的 `m.tkmind.cn.conf` 与 `wechat.m.tkmind.cn.conf`。
|
||||
- 保持稳定 Portal `103:8081` 不变,启动候选 Portal `18081`、候选 goosed `18015` 和身份路由器 `18082`。
|
||||
- 启动独立 `105:19082 -> 103:18082` 灰度隧道;禁止复用历史 19081 或占用 18080。
|
||||
- `nginx -t` 通过后才把 105 活动上游从 `58.38.22.103:8081` 切到 `127.0.0.1:19082`。
|
||||
- `nginx -t` 通过后才把 105 活动上游从 `180.159.29.143:8081` 切到 `127.0.0.1:19082`。
|
||||
- 健康检查稳定、候选、回落和 105 HTTPS 入口均通过。
|
||||
- 校验 `MindSpace/*/public/*.html` 下载/附件相对链接均已落盘(`check-mindspace-public-links.mjs --downloads-only`)。
|
||||
|
||||
@@ -113,7 +113,7 @@ curl -k -i https://m.tkmind.cn/auth/login \
|
||||
在 103 执行:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 '
|
||||
ssh john@180.159.29.143 '
|
||||
printf "portal="; curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8081/api/status
|
||||
for p in $(seq 18006 18014); do
|
||||
printf "goosed_${p}="
|
||||
@@ -138,7 +138,7 @@ ssh john@58.38.22.103 '
|
||||
日志检查:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 '
|
||||
ssh john@180.159.29.143 '
|
||||
tail -200 ~/Library/Logs/memind-portal.log |
|
||||
egrep "User auth bootstrap failed|ER_PARSE_ERROR|Cannot find package|Failed to add extension|No such file|Unknown extension|Provider not set|not configured" || true
|
||||
'
|
||||
@@ -175,7 +175,7 @@ ER_PARSE_ERROR
|
||||
release worktree 如果有 `node_modules -> /Users/john/.../node_modules` symlink,构建脚本必须 realpath 后复制真实依赖。发布后必须确认线上:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 'cd /Users/john/Project/Memind && test -d node_modules/http-proxy-middleware && echo ok'
|
||||
ssh john@180.159.29.143 'cd /Users/john/Project/Memind && test -d node_modules/http-proxy-middleware && echo ok'
|
||||
```
|
||||
|
||||
否则可能出现:
|
||||
@@ -282,7 +282,7 @@ tkmind/goosed:prod-20260627-2125-mcp
|
||||
重建镜像后必须验:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 '
|
||||
ssh john@180.159.29.143 '
|
||||
cd /Users/john/Project/goosed-prod
|
||||
set -a
|
||||
source .env
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const FORBIDDEN_HOSTS = new Set([
|
||||
'58.38.22.103',
|
||||
'180.159.29.143',
|
||||
'm.tkmind.cn',
|
||||
'plaza.tkmind.cn',
|
||||
]);
|
||||
@@ -33,7 +33,7 @@ export function isProductionTarget(value) {
|
||||
const hostname = extractHostname(text);
|
||||
return FORBIDDEN_HOSTS.has(hostname)
|
||||
|| hostname.endsWith('.tkmind.cn')
|
||||
|| text.includes('58.38.22.103')
|
||||
|| text.includes('180.159.29.143')
|
||||
|| text.includes('/users/john/project/memind');
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
|
||||
test('production IP, public domains, and production paths are refused', () => {
|
||||
for (const target of [
|
||||
'58.38.22.103',
|
||||
'ssh://58.38.22.103',
|
||||
'180.159.29.143',
|
||||
'ssh://180.159.29.143',
|
||||
'https://m.tkmind.cn',
|
||||
'https://plaza.tkmind.cn/page',
|
||||
'/Users/john/Project/Memind',
|
||||
@@ -32,8 +32,8 @@ test('unsafe environment keys are reported without exposing values', () => {
|
||||
assert.throws(
|
||||
() => assertSafeGateEnvironment({
|
||||
env: {
|
||||
DATABASE_URL: 'postgres://secret@58.38.22.103/prod',
|
||||
STUDIO_HOST: '58.38.22.103',
|
||||
DATABASE_URL: 'postgres://secret@180.159.29.143/prod',
|
||||
STUDIO_HOST: '180.159.29.143',
|
||||
},
|
||||
}),
|
||||
(error) => {
|
||||
|
||||
@@ -41,7 +41,7 @@ async function main() {
|
||||
const apply = process.argv.includes('--apply');
|
||||
const enableChatBridge = process.argv.includes('--enable-chat-bridge');
|
||||
const databaseUrl = execSync(
|
||||
"ssh -o BatchMode=yes -o ConnectTimeout=8 john@58.38.22.103 \"grep '^DATABASE_URL=' /Users/john/Project/Memind/.env | cut -d= -f2-\"",
|
||||
"ssh -o BatchMode=yes -o ConnectTimeout=8 john@180.159.29.143 \"grep '^DATABASE_URL=' /Users/john/Project/Memind/.env | cut -d= -f2-\"",
|
||||
{ encoding: 'utf8' },
|
||||
).trim();
|
||||
const pool = mysql.createPool({ uri: databaseUrl, connectionLimit: 2 });
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
HOST="${STUDIO_HOST:-john@58.38.22.103}"
|
||||
HOST="${STUDIO_HOST:-john@180.159.29.143}"
|
||||
REMOTE_ROOT="${STUDIO_REMOTE_ROOT:-/Users/john/Project}"
|
||||
|
||||
say() {
|
||||
|
||||
@@ -13,7 +13,7 @@ const port = process.env.GOOSE_V149_PORT || '18049';
|
||||
const host = process.env.GOOSE_V149_HOST || '127.0.0.1';
|
||||
const secret = process.env.GOOSE_SERVER__SECRET_KEY || 'local-v149-dev-secret';
|
||||
const base = `https://${host}:${port}`;
|
||||
const blockedHosts = ['58.38.22.103', '120.26.184.105'];
|
||||
const blockedHosts = ['180.159.29.143', '120.26.184.105'];
|
||||
|
||||
function usage() {
|
||||
return [
|
||||
|
||||
@@ -9,7 +9,7 @@ const host = process.env.GOOSE_V149_HOST || '127.0.0.1';
|
||||
const secret = process.env.GOOSE_SERVER__SECRET_KEY || 'local-v149-dev-secret';
|
||||
const workingDir = process.env.GOOSE_V149_WORKING_DIR || process.cwd();
|
||||
|
||||
const blockedHosts = ['58.38.22.103', '120.26.184.105'];
|
||||
const blockedHosts = ['180.159.29.143', '120.26.184.105'];
|
||||
if (blockedHosts.includes(host)) {
|
||||
console.error(`GOOSE_V149_AGENT_START_FAIL: refusing production host ${host}`);
|
||||
process.exit(1);
|
||||
|
||||
@@ -11,7 +11,7 @@ const workingDir = process.env.GOOSE_V149_WORKING_DIR || process.cwd();
|
||||
const pollMs = Number(process.env.GOOSE_V149_EXECUTOR_POLL_MS || 500);
|
||||
const timeoutMs = Number(process.env.GOOSE_V149_EXECUTOR_TIMEOUT_MS || 30_000);
|
||||
|
||||
const blockedHosts = ['58.38.22.103', '120.26.184.105'];
|
||||
const blockedHosts = ['180.159.29.143', '120.26.184.105'];
|
||||
if (blockedHosts.includes(host)) {
|
||||
console.error(`GOOSE_V149_EXECUTOR_FAIL: refusing production host ${host}`);
|
||||
process.exit(1);
|
||||
|
||||
@@ -13,7 +13,7 @@ const host = process.env.GOOSE_V149_HOST || '127.0.0.1';
|
||||
const secret = process.env.GOOSE_SERVER__SECRET_KEY || 'local-v149-dev-secret';
|
||||
const workingDir = process.env.GOOSE_V149_WORKING_DIR || process.cwd();
|
||||
const base = `https://${host}:${port}`;
|
||||
const blockedHosts = ['58.38.22.103', '120.26.184.105'];
|
||||
const blockedHosts = ['180.159.29.143', '120.26.184.105'];
|
||||
|
||||
if (blockedHosts.includes(host)) {
|
||||
console.error(`GOOSE_V149_HARNESS_FAIL: refusing production host ${host}`);
|
||||
|
||||
@@ -9,7 +9,7 @@ const host = process.env.GOOSE_V149_HOST || '127.0.0.1';
|
||||
const url = `https://${host}:${port}/status`;
|
||||
const maxBuffer = Number(process.env.GOOSE_V149_CHECK_MAX_BUFFER || 64 * 1024 * 1024);
|
||||
|
||||
const blockedHosts = ['58.38.22.103', '120.26.184.105'];
|
||||
const blockedHosts = ['180.159.29.143', '120.26.184.105'];
|
||||
if (blockedHosts.includes(host)) {
|
||||
console.error(`GOOSE_V149_CHECK_FAIL: refusing production host ${host}`);
|
||||
process.exit(1);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { enforceRealLlmGate } from './goose-v149-real-llm-gate.mjs';
|
||||
|
||||
enforceRealLlmGate('check-goosed-v149-memory-loop.mjs');
|
||||
|
||||
const BLOCKED_DB_HOSTS = ['58.38.22.103', '120.26.184.105', 'rds.aliyuncs.com'];
|
||||
const BLOCKED_DB_HOSTS = ['180.159.29.143', '120.26.184.105', 'rds.aliyuncs.com'];
|
||||
const MARKER = `goose-v149-memory-loop-${Date.now()}`;
|
||||
|
||||
function assertLocalDatabase(env = process.env) {
|
||||
|
||||
@@ -53,7 +53,7 @@ const apiUrl =
|
||||
|| 'https://api.deepseek.com/v1';
|
||||
const replyTimeoutMs = Number(process.env.GOOSE_V149_PROVIDER_REPLY_TIMEOUT_MS || 90_000);
|
||||
|
||||
const blockedHosts = ['58.38.22.103', '120.26.184.105'];
|
||||
const blockedHosts = ['180.159.29.143', '120.26.184.105'];
|
||||
if (blockedHosts.includes(host)) {
|
||||
console.error(`GOOSE_V149_PROVIDER_FAIL: refusing production host ${host}`);
|
||||
process.exit(1);
|
||||
|
||||
@@ -17,7 +17,7 @@ const secret = process.env.GOOSE_SERVER__SECRET_KEY || 'local-v149-dev-secret';
|
||||
const workingDir = process.env.GOOSE_V149_WORKING_DIR || process.cwd();
|
||||
const timeoutMs = Number(process.env.GOOSE_V149_REPLY_TIMEOUT_MS || 30_000);
|
||||
|
||||
const blockedHosts = ['58.38.22.103', '120.26.184.105'];
|
||||
const blockedHosts = ['180.159.29.143', '120.26.184.105'];
|
||||
if (blockedHosts.includes(host)) {
|
||||
console.error(`GOOSE_V149_REPLY_SMOKE_FAIL: refusing production host ${host}`);
|
||||
process.exit(1);
|
||||
|
||||
@@ -19,7 +19,7 @@ const serverPath =
|
||||
const pollMs = Number(process.env.GOOSE_V149_SANDBOX_POLL_MS || 500);
|
||||
const timeoutMs = Number(process.env.GOOSE_V149_SANDBOX_TIMEOUT_MS || 30_000);
|
||||
|
||||
const blockedHosts = ['58.38.22.103', '120.26.184.105'];
|
||||
const blockedHosts = ['180.159.29.143', '120.26.184.105'];
|
||||
if (blockedHosts.includes(host)) {
|
||||
console.error(`GOOSE_V149_SANDBOX_FS_FAIL: refusing production host ${host}`);
|
||||
process.exit(1);
|
||||
|
||||
@@ -10,7 +10,7 @@ import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const HOST = 'john@58.38.22.103';
|
||||
const HOST = 'john@180.159.29.143';
|
||||
const REMOTE_ROOT = '/Users/john/Project/Memind';
|
||||
const TANG = 'a70ff537-8908-486e-9b6c-042e07cc25db';
|
||||
const JOHN_LOCAL = '1c99b83b-0454-474f-a5d2-129d34506a32';
|
||||
|
||||
@@ -4,7 +4,7 @@ set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
HOST="${MEMIND_PROD_HOST:-john@58.38.22.103}"
|
||||
HOST="${MEMIND_PROD_HOST:-john@180.159.29.143}"
|
||||
REMOTE_ROOT="${MEMIND_PROD_ROOT:-/Users/john/Project/Memind}"
|
||||
BUNDLE=".runtime/portal/wechat-mp.bundle.mjs"
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ fail() {
|
||||
|
||||
case "${PG_HOST}" in
|
||||
127.0.0.1|localhost|::1) ;;
|
||||
58.38.22.103|120.26.184.105|*rds.aliyuncs.com*|*tkmind.cn*)
|
||||
180.159.29.143|120.26.184.105|*rds.aliyuncs.com*|*tkmind.cn*)
|
||||
fail "refusing production or remote host: ${PG_HOST}"
|
||||
;;
|
||||
*)
|
||||
|
||||
@@ -10,7 +10,7 @@ import mysql from 'mysql2/promise';
|
||||
|
||||
import { loadMemindEnvFiles } from './memind-runtime-profile.mjs';
|
||||
|
||||
const BLOCKED_DB_HOSTS = new Set(['58.38.22.103', '120.26.184.105', 'rds.aliyuncs.com']);
|
||||
const BLOCKED_DB_HOSTS = new Set(['180.159.29.143', '120.26.184.105', 'rds.aliyuncs.com']);
|
||||
|
||||
function usage() {
|
||||
return [
|
||||
|
||||
@@ -5,4 +5,4 @@ Ali_Secret=YOUR_ALIYUN_ACCESS_KEY_SECRET
|
||||
|
||||
# 可选:证书与 DNS 域名
|
||||
G2_DOMAIN=g2.tkmind.cn
|
||||
G2_PUBLIC_IP=58.38.22.103
|
||||
G2_PUBLIC_IP=180.159.29.143
|
||||
|
||||
@@ -90,7 +90,7 @@ export function prepareGooseV149CheckEnv(env = process.env, rootDir = memindRoot
|
||||
export const GOOSE_CANARY_MODES = new Set(['off', 'all', 'users']);
|
||||
|
||||
const BLOCKED_HOSTS = new Set([
|
||||
'58.38.22.103',
|
||||
'180.159.29.143',
|
||||
'120.26.184.105',
|
||||
'103.tkmind.cn',
|
||||
'105.tkmind.cn',
|
||||
|
||||
@@ -52,7 +52,7 @@ test('resolveGooseTargetForIdentity routes canary users to v149', () => {
|
||||
|
||||
test('assertLoopbackGooseTarget refuses production hosts', () => {
|
||||
assert.throws(
|
||||
() => assertLoopbackGooseTarget('https://58.38.22.103:18006'),
|
||||
() => assertLoopbackGooseTarget('https://180.159.29.143:18006'),
|
||||
/refuses production host/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import { randomUUID } from 'node:crypto';
|
||||
import { Readable } from 'node:stream';
|
||||
import { Agent, fetch } from 'undici';
|
||||
|
||||
const blockedHosts = new Set(['58.38.22.103', '120.26.184.105']);
|
||||
const blockedHosts = new Set(['180.159.29.143', '120.26.184.105']);
|
||||
|
||||
export function createV149Client(options = {}) {
|
||||
const host = options.host ?? process.env.GOOSE_V149_HOST ?? '127.0.0.1';
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# 前置(阿里云控制台):
|
||||
# 1. 云解析 DNS 添加域名 tkmind.cn,NS 改到阿里云(见脚本末尾说明)
|
||||
# 2. 添加 A 记录:g2 -> 58.38.22.103
|
||||
# 2. 添加 A 记录:g2 -> 180.159.29.143
|
||||
# 3. RAM 创建 AccessKey,权限 AliyunDNSFullAccess
|
||||
# 4. cp scripts/g2-aliyun-cert.env.example scripts/g2-aliyun-cert.env 并填入密钥
|
||||
#
|
||||
@@ -92,6 +92,6 @@ echo ""
|
||||
echo "==> 阿里云 DNS 迁移检查清单"
|
||||
echo " 1. 云解析 DNS 已添加 tkmind.cn"
|
||||
echo " 2. 域名注册商 NS 已改为阿里云(例如 vip1.alidns.com / vip2.alidns.com)"
|
||||
echo " 3. A 记录 g2 -> ${G2_PUBLIC_IP:-58.38.22.103}"
|
||||
echo " 3. A 记录 g2 -> ${G2_PUBLIC_IP:-180.159.29.143}"
|
||||
echo " 4. 删除 Cloudflare 上 g2 的 CNAME/代理记录,避免冲突"
|
||||
echo " 5. dig @223.5.5.5 +short ${DOMAIN} A 应返回 ${G2_PUBLIC_IP:-58.38.22.103}"
|
||||
echo " 5. dig @223.5.5.5 +short ${DOMAIN} A 应返回 ${G2_PUBLIC_IP:-180.159.29.143}"
|
||||
|
||||
@@ -254,7 +254,7 @@ ${contentPreview}`;
|
||||
console.log(' 摘要:', pageAfter.summary?.slice(0, 120));
|
||||
console.log(' 内容已变更:', changed ? '是' : '否');
|
||||
|
||||
const publicBase = (process.env.H5_PUBLIC_BASE_URL ?? 'https://58.38.22.103').replace(/\/$/, '');
|
||||
const publicBase = (process.env.H5_PUBLIC_BASE_URL ?? 'https://180.159.29.143').replace(/\/$/, '');
|
||||
const [pubRows] = await pool.query(
|
||||
`SELECT url_slug, status FROM h5_publish_records WHERE id = (SELECT current_publish_id FROM h5_page_records WHERE id = ?)`,
|
||||
[PAGE_ID],
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
HOST="${STUDIO_HOST:-58.38.22.103}"
|
||||
HOST="${STUDIO_HOST:-180.159.29.143}"
|
||||
REMOTE_ROOT="${STUDIO_REMOTE_ROOT:-/Users/john/Project}"
|
||||
RUNTIME_BASE="${DEEP_SEARCH_RUNTIME_BASE:-${REMOTE_ROOT}/deep-search-runtime}"
|
||||
INCOMING="${REMOTE_ROOT}/incoming/deep-search-runtime"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
HOST="${STUDIO_HOST:-john@58.38.22.103}"
|
||||
HOST="${STUDIO_HOST:-john@180.159.29.143}"
|
||||
REMOTE_ROOT="${STUDIO_REMOTE_ROOT:-/Users/john/Project}"
|
||||
RUNTIME_BASE="${REMOTE_ROOT}/imgproxy-runtime"
|
||||
INCOMING="${REMOTE_ROOT}/incoming/imgproxy-runtime"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
HOST="${STUDIO_HOST:-john@58.38.22.103}"
|
||||
HOST="${STUDIO_HOST:-john@180.159.29.143}"
|
||||
REMOTE_ROOT="${STUDIO_REMOTE_ROOT:-/Users/john/Project}"
|
||||
DOCKER_BIN="${DOCKER_BIN:-/opt/homebrew/bin/docker}"
|
||||
RUNTIME_BASE="$REMOTE_ROOT/imgproxy-runtime"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
HOST="${STUDIO_HOST:-58.38.22.103}"
|
||||
HOST="${STUDIO_HOST:-180.159.29.143}"
|
||||
REMOTE_USER_ROOT="${STUDIO_USER_ROOT:-/Users/john}"
|
||||
SHARED_MEMIND_ROOT="${STUDIO_SHARED_MEMIND_ROOT:-/Users/john/Project/Memind}"
|
||||
APP_DIR="${REMOTE_USER_ROOT}/MindSpace"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
HOST="${STUDIO_HOST:-58.38.22.103}"
|
||||
HOST="${STUDIO_HOST:-180.159.29.143}"
|
||||
REMOTE_ROOT="${STUDIO_REMOTE_ROOT:-/Users/john/Project}"
|
||||
STABLE_DIR="${REMOTE_ROOT}/Memind"
|
||||
CANDIDATE_BASE="${REMOTE_ROOT}/Memind-candidates"
|
||||
@@ -216,8 +216,8 @@ ssh -o BatchMode=yes -o ConnectTimeout=15 "${HOST}" \
|
||||
&& ssh -o BatchMode=yes -o ConnectTimeout=10 '${EDGE_HOST}' \
|
||||
\"test -f '${EDGE_MOBILE_CONFIG}' \
|
||||
&& test -f '${EDGE_WECHAT_CONFIG}' \
|
||||
&& grep -q 'proxy_pass http://58.38.22.103:8081;' '${EDGE_MOBILE_CONFIG}' \
|
||||
&& grep -q 'proxy_pass http://58.38.22.103:8081;' '${EDGE_WECHAT_CONFIG}' \
|
||||
&& grep -q 'proxy_pass http://180.159.29.143:8081;' '${EDGE_MOBILE_CONFIG}' \
|
||||
&& grep -q 'proxy_pass http://180.159.29.143:8081;' '${EDGE_WECHAT_CONFIG}' \
|
||||
&& ! grep -q 'proxy_pass http://127.0.0.1:${CANARY_TUNNEL_REMOTE_PORT};' '${EDGE_MOBILE_CONFIG}' \
|
||||
&& ! grep -q 'proxy_pass http://127.0.0.1:${CANARY_TUNNEL_REMOTE_PORT};' '${EDGE_WECHAT_CONFIG}' \
|
||||
&& nginx -t >/dev/null 2>&1 \
|
||||
@@ -426,8 +426,8 @@ restore_edge_to_stable() {
|
||||
cp '${EDGE_MOBILE_BACKUP}' '${EDGE_MOBILE_CONFIG}'
|
||||
cp '${EDGE_WECHAT_BACKUP}' '${EDGE_WECHAT_CONFIG}'
|
||||
fi
|
||||
grep -q 'proxy_pass http://58.38.22.103:8081;' '${EDGE_MOBILE_CONFIG}'
|
||||
grep -q 'proxy_pass http://58.38.22.103:8081;' '${EDGE_WECHAT_CONFIG}'
|
||||
grep -q 'proxy_pass http://180.159.29.143:8081;' '${EDGE_MOBILE_CONFIG}'
|
||||
grep -q 'proxy_pass http://180.159.29.143:8081;' '${EDGE_WECHAT_CONFIG}'
|
||||
! grep -q 'proxy_pass http://127.0.0.1:${CANARY_TUNNEL_REMOTE_PORT};' '${EDGE_MOBILE_CONFIG}'
|
||||
! grep -q 'proxy_pass http://127.0.0.1:${CANARY_TUNNEL_REMOTE_PORT};' '${EDGE_WECHAT_CONFIG}'
|
||||
nginx -t >/dev/null
|
||||
@@ -486,8 +486,8 @@ edge_ssh \
|
||||
"set -euo pipefail
|
||||
test -f '${EDGE_MOBILE_CONFIG}'
|
||||
test -f '${EDGE_WECHAT_CONFIG}'
|
||||
grep -q 'proxy_pass http://58.38.22.103:8081;' '${EDGE_MOBILE_CONFIG}'
|
||||
grep -q 'proxy_pass http://58.38.22.103:8081;' '${EDGE_WECHAT_CONFIG}'
|
||||
grep -q 'proxy_pass http://180.159.29.143:8081;' '${EDGE_MOBILE_CONFIG}'
|
||||
grep -q 'proxy_pass http://180.159.29.143:8081;' '${EDGE_WECHAT_CONFIG}'
|
||||
! grep -q 'proxy_pass http://127.0.0.1:${CANARY_TUNNEL_REMOTE_PORT};' '${EDGE_MOBILE_CONFIG}'
|
||||
! grep -q 'proxy_pass http://127.0.0.1:${CANARY_TUNNEL_REMOTE_PORT};' '${EDGE_WECHAT_CONFIG}'
|
||||
cp '${EDGE_MOBILE_CONFIG}' '${EDGE_MOBILE_BACKUP}'
|
||||
@@ -713,7 +713,7 @@ say "Switch the committed 105 nginx upstreams to the isolated canary tunnel"
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=10 "${EDGE_HOST}" /bin/bash <<EDGE_SWITCH
|
||||
set -euo pipefail
|
||||
for config in '${EDGE_MOBILE_CONFIG}' '${EDGE_WECHAT_CONFIG}'; do
|
||||
before_count="\$(grep -cF 'proxy_pass http://58.38.22.103:8081;' "\${config}")"
|
||||
before_count="\$(grep -cF 'proxy_pass http://180.159.29.143:8081;' "\${config}")"
|
||||
[[ "\${before_count}" -gt 0 ]]
|
||||
temp_config="\$(mktemp "\${config}.canary.XXXXXX")"
|
||||
sed 's#proxy_pass http://58\.38\.22\.103:8081;#proxy_pass http://127.0.0.1:${CANARY_TUNNEL_REMOTE_PORT};#g' \
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
HOST="${STUDIO_HOST:-58.38.22.103}"
|
||||
HOST="${STUDIO_HOST:-180.159.29.143}"
|
||||
REMOTE_ROOT="${STUDIO_REMOTE_ROOT:-/Users/john/Project}"
|
||||
APP_DIR="${REMOTE_ROOT}/Memind"
|
||||
INCOMING_DIR="${REMOTE_ROOT}/incoming/memind-portal-runtime"
|
||||
|
||||
@@ -18,7 +18,7 @@ EOF
|
||||
exit 1
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
HOST="${STUDIO_HOST:-58.38.22.103}"
|
||||
HOST="${STUDIO_HOST:-180.159.29.143}"
|
||||
REMOTE_ROOT="${STUDIO_REMOTE_ROOT:-/Users/john/Project}"
|
||||
APP_NAME="${STUDIO_APP_NAME:-Memind}"
|
||||
APP_DIR="${REMOTE_ROOT}/${APP_NAME}"
|
||||
|
||||
@@ -5,7 +5,7 @@ import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const HOST = 'john@58.38.22.103';
|
||||
const HOST = 'john@180.159.29.143';
|
||||
const REMOTE_ROOT = '/Users/john/Project/Memind';
|
||||
const NODE103 = '/opt/homebrew/opt/node@24/bin/node';
|
||||
const TANG = 'a70ff537-8908-486e-9b6c-042e07cc25db';
|
||||
|
||||
@@ -4,7 +4,7 @@ set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
HOST="${MEMIND_PROD_HOST:-john@58.38.22.103}"
|
||||
HOST="${MEMIND_PROD_HOST:-john@180.159.29.143}"
|
||||
REMOTE_ROOT="${MEMIND_PROD_ROOT:-/Users/john/Project/Memind}"
|
||||
|
||||
echo "[resume-agent-run] stop guard on ${HOST}"
|
||||
|
||||
@@ -5,7 +5,7 @@ import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const HOST = 'john@58.38.22.103';
|
||||
const HOST = 'john@180.159.29.143';
|
||||
const REMOTE_ROOT = '/Users/john/Project/Memind';
|
||||
const NODE103 = '/opt/homebrew/opt/node@24/bin/node';
|
||||
const TANG = 'a70ff537-8908-486e-9b6c-042e07cc25db';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
HOST="${STUDIO_HOST:-58.38.22.103}"
|
||||
HOST="${STUDIO_HOST:-180.159.29.143}"
|
||||
STABLE_DIR="${STUDIO_REMOTE_ROOT:-/Users/john/Project}/Memind"
|
||||
PORTAL_CANDIDATE_LABEL="cn.tkmind.memind-portal-candidate"
|
||||
CANARY_PROXY_LABEL="cn.tkmind.memind-canary-proxy"
|
||||
@@ -44,15 +44,15 @@ ssh -o BatchMode=yes -o ConnectTimeout=10 "${EDGE_HOST}" /bin/bash <<EDGE_ROLLBA
|
||||
set -euo pipefail
|
||||
for config in '${EDGE_MOBILE_CONFIG}' '${EDGE_WECHAT_CONFIG}'; do
|
||||
canary_count="\$(grep -cF 'proxy_pass http://127.0.0.1:${CANARY_TUNNEL_REMOTE_PORT};' "\${config}" || true)"
|
||||
stable_count_before="\$(grep -cF 'proxy_pass http://58.38.22.103:8081;' "\${config}" || true)"
|
||||
stable_count_before="\$(grep -cF 'proxy_pass http://180.159.29.143:8081;' "\${config}" || true)"
|
||||
if [[ "\${canary_count}" -eq 0 ]]; then
|
||||
[[ "\${stable_count_before}" -gt 0 ]]
|
||||
continue
|
||||
fi
|
||||
temp_config="\$(mktemp "\${config}.stable.XXXXXX")"
|
||||
sed 's#proxy_pass http://127\.0\.0\.1:${CANARY_TUNNEL_REMOTE_PORT};#proxy_pass http://58.38.22.103:8081;#g' \
|
||||
sed 's#proxy_pass http://127\.0\.0\.1:${CANARY_TUNNEL_REMOTE_PORT};#proxy_pass http://180.159.29.143:8081;#g' \
|
||||
"\${config}" > "\${temp_config}"
|
||||
stable_count="\$(grep -cF 'proxy_pass http://58.38.22.103:8081;' "\${temp_config}")"
|
||||
stable_count="\$(grep -cF 'proxy_pass http://180.159.29.143:8081;' "\${temp_config}")"
|
||||
[[ "\${stable_count}" -eq \$(( stable_count_before + canary_count )) ]]
|
||||
chown root:root "\${temp_config}"
|
||||
chmod 0644 "\${temp_config}"
|
||||
@@ -76,7 +76,7 @@ rm -f "${STABLE_DIR}/.release-drain"
|
||||
|
||||
curl -fsS http://127.0.0.1:8081/api/status >/dev/null
|
||||
printf 'stable_port=8081\n'
|
||||
printf 'edge_upstream=58.38.22.103:8081\n'
|
||||
printf 'edge_upstream=180.159.29.143:8081\n'
|
||||
REMOTE
|
||||
|
||||
echo "103 canary rollback completed; stable Portal remains active on 8081."
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
HOST="${STUDIO_HOST:-58.38.22.103}"
|
||||
HOST="${STUDIO_HOST:-180.159.29.143}"
|
||||
REMOTE_ROOT="${STUDIO_REMOTE_ROOT:-/Users/john/Project}"
|
||||
APP_DIR="${REMOTE_ROOT}/Memind"
|
||||
BACKUP_DIR="${REMOTE_ROOT}/backups/memind"
|
||||
|
||||
@@ -12,7 +12,7 @@ import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const HOST = 'john@58.38.22.103';
|
||||
const HOST = 'john@180.159.29.143';
|
||||
const REMOTE_ROOT = '/Users/john/Project/Memind';
|
||||
const NODE103 = '/opt/homebrew/opt/node@24/bin/node';
|
||||
const TANG_USER_ID = 'a70ff537-8908-486e-9b6c-042e07cc25db';
|
||||
|
||||
@@ -12,7 +12,7 @@ import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const HOST = 'john@58.38.22.103';
|
||||
const HOST = 'john@180.159.29.143';
|
||||
const REMOTE_ROOT = '/Users/john/Project/Memind';
|
||||
const NODE103 = '/opt/homebrew/opt/node@24/bin/node';
|
||||
const DEFAULT_TANG_USER_ID = 'a70ff537-8908-486e-9b6c-042e07cc25db';
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Copy local deepseek-cp provider config to production and rebind Goose executors.
|
||||
*
|
||||
* Usage (from dev machine):
|
||||
* eval "$(ssh john@58.38.22.103 'grep -E "^(DATABASE_URL|TKMIND_API_TARGET|TKMIND_API_TARGETS|TKMIND_SERVER__SECRET_KEY)=" /Users/john/Project/Memind/.env | sed "s/^/TARGET_/"')"
|
||||
* eval "$(ssh john@180.159.29.143 'grep -E "^(DATABASE_URL|TKMIND_API_TARGET|TKMIND_API_TARGETS|TKMIND_SERVER__SECRET_KEY)=" /Users/john/Project/Memind/.env | sed "s/^/TARGET_/"')"
|
||||
* node scripts/sync-deepseek-cp-prod.mjs
|
||||
*/
|
||||
import fs from 'node:fs';
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# bash scripts/sync-token-optimization-env-103.sh --apply # write + restart portal
|
||||
set -euo pipefail
|
||||
|
||||
HOST="${MEMIND_103_HOST:-john@58.38.22.103}"
|
||||
HOST="${MEMIND_103_HOST:-john@180.159.29.143}"
|
||||
REMOTE_ENV="/Users/john/Project/Memind/.env"
|
||||
APPLY=0
|
||||
if [[ "${1:-}" == "--apply" ]]; then
|
||||
|
||||
@@ -46,7 +46,7 @@ const REMOTE_SCRIPT = [
|
||||
|
||||
function parseArgs(argv) {
|
||||
const options = {
|
||||
host: process.env.STUDIO_HOST ?? '58.38.22.103',
|
||||
host: process.env.STUDIO_HOST ?? '180.159.29.143',
|
||||
commit: null,
|
||||
artifact: path.join(ROOT, '.runtime', 'portal'),
|
||||
};
|
||||
|
||||
@@ -27,7 +27,7 @@ function previewMessage(message, index) {
|
||||
|
||||
async function load103Env() {
|
||||
const { execSync } = await import('node:child_process');
|
||||
const envText = execSync('ssh -o BatchMode=yes john@58.38.22.103 cat /Users/john/Project/Memind/.env', {
|
||||
const envText = execSync('ssh -o BatchMode=yes john@180.159.29.143 cat /Users/john/Project/Memind/.env', {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
const dbLine = envText.match(/^DATABASE_URL=(.+)$/m)?.[1]?.trim().replace(/^["']|["']$/g, '');
|
||||
@@ -49,7 +49,7 @@ async function load103Env() {
|
||||
async function fetchGooseSession(secret, sid) {
|
||||
const { execSync } = await import('node:child_process');
|
||||
const json = execSync(
|
||||
`ssh -o BatchMode=yes john@58.38.22.103 'SECRET=$(grep ^TKMIND_SERVER__SECRET_KEY= /Users/john/Project/Memind/.env | cut -d= -f2- | tr -d "\\""); curl -k -sf "https://127.0.0.1:18006/sessions/${sid}" -H "X-Secret-Key: $SECRET"'`,
|
||||
`ssh -o BatchMode=yes john@180.159.29.143 'SECRET=$(grep ^TKMIND_SERVER__SECRET_KEY= /Users/john/Project/Memind/.env | cut -d= -f2- | tr -d "\\""); curl -k -sf "https://127.0.0.1:18006/sessions/${sid}" -H "X-Secret-Key: $SECRET"'`,
|
||||
{ encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 },
|
||||
);
|
||||
return JSON.parse(json);
|
||||
|
||||
@@ -44,7 +44,7 @@ function fail(label, detail = '') {
|
||||
|
||||
async function loadProdDatabaseUrl() {
|
||||
return execSync(
|
||||
"ssh -o BatchMode=yes -o ConnectTimeout=8 john@58.38.22.103 \"grep '^DATABASE_URL=' /Users/john/Project/Memind/.env | cut -d= -f2-\"",
|
||||
"ssh -o BatchMode=yes -o ConnectTimeout=8 john@180.159.29.143 \"grep '^DATABASE_URL=' /Users/john/Project/Memind/.env | cut -d= -f2-\"",
|
||||
{ encoding: 'utf8' },
|
||||
).trim();
|
||||
}
|
||||
@@ -52,7 +52,7 @@ async function loadProdDatabaseUrl() {
|
||||
async function read103Env(pattern) {
|
||||
try {
|
||||
const out = execSync(
|
||||
`ssh -o BatchMode=yes -o ConnectTimeout=8 john@58.38.22.103 'grep -E "${pattern}" /Users/john/Project/Memind/.env || true'`,
|
||||
`ssh -o BatchMode=yes -o ConnectTimeout=8 john@180.159.29.143 'grep -E "${pattern}" /Users/john/Project/Memind/.env || true'`,
|
||||
{ encoding: 'utf8' },
|
||||
).trim();
|
||||
return out.split('\n').filter(Boolean);
|
||||
|
||||
Reference in New Issue
Block a user