@optima-chat/dev-skills 0.16.7 → 0.16.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/logs.md +1 -1
- package/.claude/commands/query-db.md +6 -1
- package/.claude/skills/discount-codes/SKILL.md +4 -4
- package/.claude/skills/generate-test-token/SKILL.md +8 -4
- package/.claude/skills/logs/SKILL.md +5 -3
- package/.claude/skills/show-env/SKILL.md +1 -1
- package/.claude/skills/yzsgo-e2e/SKILL.md +19 -0
- package/.claude/skills/yzsgo-e2e/SYNC.md +26 -2
- package/.claude/skills/yzsgo-e2e/chat_driver.py +221 -47
- package/.claude/skills/yzsgo-e2e/pull_wire.py +39 -8
- package/.claude/skills/yzsgo-e2e/run_e2e.py +17 -2
- package/.codex/skills/generate-test-token/SKILL.md +3 -2
- package/.codex/skills/logs/SKILL.md +4 -2
- package/.codex/skills/show-env/SKILL.md +4 -2
- package/.codex/skills/yzsgo-e2e/SKILL.md +19 -0
- package/.codex/skills/yzsgo-e2e/SYNC.md +26 -2
- package/.codex/skills/yzsgo-e2e/chat_driver.py +221 -47
- package/.codex/skills/yzsgo-e2e/pull_wire.py +39 -8
- package/.codex/skills/yzsgo-e2e/run_e2e.py +17 -2
- package/AGENTS.md +1 -1
- package/bin/cli.js +3 -3
- package/bin/helpers/billing-http.ts +12 -3
- package/bin/helpers/db-utils.ts +8 -5
- package/bin/helpers/generate-test-token.ts +40 -3
- package/bin/helpers/verify-health.ts +77 -29
- package/dist/bin/helpers/billing-http.js +9 -1
- package/dist/bin/helpers/db-utils.js +8 -2
- package/dist/bin/helpers/generate-test-token.js +40 -3
- package/dist/bin/helpers/verify-health.js +83 -27
- package/package.json +1 -1
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
};
|
|
35
35
|
})();
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.parseArgs = parseArgs;
|
|
37
38
|
/**
|
|
38
39
|
* optima-verify-health —— 服务上线健康探针(L1 DNS / L2 TLS / L3-L5 /health)。
|
|
39
40
|
*
|
|
@@ -52,8 +53,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
52
53
|
*
|
|
53
54
|
* 用法:
|
|
54
55
|
* optima-verify-health user-auth # 默认 cn-prod
|
|
55
|
-
* optima-verify-health user-auth --env prod # 环境 stage|prod|cn|all
|
|
56
|
-
* optima-verify-health --all --env all # stage/prod/cn
|
|
56
|
+
* optima-verify-health user-auth --env prod # 环境 stage|prod|cn-prod|cn-stage|all
|
|
57
|
+
* optima-verify-health --all --env all # stage/prod/cn-prod/cn-stage 四环境矩阵
|
|
57
58
|
* optima-verify-health gateway-core --expect-commit a1b2c3d
|
|
58
59
|
* optima-verify-health --url https://auth.yzsgo.com/health
|
|
59
60
|
* optima-verify-health --all --json # 机器可读,接 CI
|
|
@@ -67,16 +68,28 @@ const https = __importStar(require("node:https"));
|
|
|
67
68
|
// cn-prod 真实 subdomain 抄自 optima-terraform alicloud/stacks/cn-prod-ingress-sae/main.tf。
|
|
68
69
|
// #201 (2026-06-12): yzsgo.com 全量迁移完成,旧 *-cn.optima.chat 路由已下线。
|
|
69
70
|
// cn-stage(阿里云预发)域名 *.stage.optima.chat,抄自 cn-stage-ingress-sae services map。
|
|
71
|
+
// #83 (2026-08-13): AWS stage 是 *.stage.optima.onl(点号),旧的连字符形式 *-stage.optima.onl
|
|
72
|
+
// 已不再路由到服务——三项实测 301 → www.optima.onl,探测恒红并误报成「疑似未部署」。
|
|
73
|
+
// 🔴 别只看 stage 列:agentic-chat 的 prod 也踩同一个坑(见下面那行的注释)。
|
|
70
74
|
const SERVICES = {
|
|
71
|
-
'user-auth': { path: '/health', stage: 'auth
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
'
|
|
76
|
-
'
|
|
77
|
-
|
|
75
|
+
'user-auth': { path: '/health', stage: 'auth.stage.optima.onl', prod: 'auth.optima.onl', 'cn-prod': 'auth.yzsgo.com', 'cn-stage': 'auth.stage.optima.chat' },
|
|
76
|
+
// agentic-chat 的 prod 入口是 www 不是 ai:prod-ecs/variables.tf 里它的 subdomain = "www",
|
|
77
|
+
// 且 main.tf 有一条 ai_to_www_redirect(priority 309) 专门把 ai.optima.onl 301 到 www。
|
|
78
|
+
// 实测 ai.optima.onl/api/health → 301;www.optima.onl/api/health → 200 service=agentic-chat。
|
|
79
|
+
'agentic-chat': { path: '/api/health', stage: 'ai.stage.optima.onl', prod: 'www.optima.onl', 'cn-prod': 'app.yzsgo.com', 'cn-stage': 'app.stage.optima.chat' },
|
|
80
|
+
'commerce-backend': { path: '/health', stage: 'api.stage.optima.onl', prod: 'api.optima.onl', 'cn-prod': 'commerce.yzsgo.com', 'cn-prod_path': '/health/live', 'cn-stage': 'commerce.stage.optima.chat', 'cn-stage_path': '/health/live' },
|
|
81
|
+
// #83: mcp-host 已于 2025-12-18 下线,故不在表内 —— optima-terraform origin/main(f97adbb) 的
|
|
82
|
+
// stage-ecs/variables.tf:146 与 prod-ecs/variables.tf:508 都写着「MCP 工具服务已移除」,
|
|
83
|
+
// 两个 ecs stack 的 services map 里均无该条目;实测四个候选主机名全是壳(mcp.stage.optima.onl
|
|
84
|
+
// 307 → /en-US/health 前端 locale 路由,mcp-stage.optima.onl 与 prod 的 mcp.optima.onl 均
|
|
85
|
+
// 301 → www)。🔴 别再把它加回来:worstOk 是全局单标志(见 main()),留着它会让 `--all --env stage`
|
|
86
|
+
// 与 `--all --env prod` 恒 exit 1,而那个退出码正是接 CI 卡口时唯一被读的东西。
|
|
87
|
+
// 同一约束在 tests/service-matrix-alignment.test.js 里对 show-env 的清单也钉着。
|
|
88
|
+
'gateway-core': { path: '/health', 'cn-prod': 'gw.yzsgo.com', 'cn-stage': 'gw.stage.optima.chat' },
|
|
89
|
+
'optima-scout': { path: '/health', 'cn-prod': 'scout.yzsgo.com', 'cn-stage': 'scout.stage.optima.chat' },
|
|
90
|
+
'optima-skills': { path: '/health', 'cn-prod': 'skills.yzsgo.com', 'cn-stage': 'skills.stage.optima.chat' },
|
|
78
91
|
};
|
|
79
|
-
const ENVS = ['stage', 'prod', 'cn', 'cn-stage'];
|
|
92
|
+
const ENVS = ['stage', 'prod', 'cn-prod', 'cn-stage'];
|
|
80
93
|
const G = '\x1b[32m', R = '\x1b[31m', Y = '\x1b[33m', B = '\x1b[34m', N = '\x1b[0m';
|
|
81
94
|
const MARK = { ok: `${G}✅${N}`, fail: `${R}❌${N}`, warn: `${Y}⚠️ ${N}`, na: `${B}··${N}` };
|
|
82
95
|
const verdict = (ls) => ls.some((l) => l.result === 'fail') ? 'fail' : ls.some((l) => l.result === 'warn') ? 'warn' : 'pass';
|
|
@@ -194,21 +207,66 @@ function resolve(svc, e) {
|
|
|
194
207
|
const path = cfg[`${e}_path`] || cfg.path;
|
|
195
208
|
return [`${svc} [${e}]`, host, path];
|
|
196
209
|
}
|
|
210
|
+
// 🔴 不认识的输入一律报错、绝不静默丢弃(同 query-db.ts 的 parseQueryDbArgs、logs.ts 的 parseArgs):
|
|
211
|
+
// 此前 `--env=stage` 不认、`--env` 漏写取值、多敲一个位置参数都会被静默无视而回落默认 cn-prod ——
|
|
212
|
+
// 想探 stage,实际探的是阿里云生产,还 exit 0。
|
|
213
|
+
function parseArgs(argv) {
|
|
214
|
+
const flags = new Set(), vals = {}, positional = [];
|
|
215
|
+
for (let i = 0; i < argv.length; i++) {
|
|
216
|
+
const arg = argv[i];
|
|
217
|
+
const eq = arg.startsWith('--') ? arg.indexOf('=') : -1;
|
|
218
|
+
const name = eq > 0 ? arg.slice(0, eq) : arg;
|
|
219
|
+
if (['--env', '--expect-commit', '--url'].includes(name)) {
|
|
220
|
+
const v = eq > 0 ? arg.slice(eq + 1) : argv[++i];
|
|
221
|
+
if (!v || v.startsWith('-'))
|
|
222
|
+
throw new Error(`${name} 缺少取值`);
|
|
223
|
+
vals[name] = v;
|
|
224
|
+
}
|
|
225
|
+
else if (['--json', '--strict', '--all', '--help', '-h'].includes(arg))
|
|
226
|
+
flags.add(arg);
|
|
227
|
+
else if (arg.startsWith('-'))
|
|
228
|
+
throw new Error(`未知参数:${arg}`);
|
|
229
|
+
else
|
|
230
|
+
positional.push(arg);
|
|
231
|
+
}
|
|
232
|
+
// 🔴 必须先校验再转型:不校验的话任何不认识的 env 都会让 resolve() 把每个 SvcCfg 键查成
|
|
233
|
+
// undefined,于是落到下面的「无目标」分支,把「环境名打错」报成「该服务无部署」——
|
|
234
|
+
// 一个活着的服务被说成没上线,正是 #83 要根治的那类静默误报。
|
|
235
|
+
let envArg = vals['--env'] || 'cn-prod';
|
|
236
|
+
// 'cn' 是本文件的历史叫法,继续放行但不在 usage 里宣传(同 query-db.ts 的 ENV_ALIASES)。
|
|
237
|
+
if (envArg === 'cn')
|
|
238
|
+
envArg = 'cn-prod';
|
|
239
|
+
if (envArg !== 'all' && !ENVS.includes(envArg))
|
|
240
|
+
throw new Error(`未知环境:${envArg}(可选:${ENVS.join(' | ')} | all)`);
|
|
241
|
+
if (positional.length > 1)
|
|
242
|
+
throw new Error(`多余参数:${positional.slice(1).join(' ')}(一次探一个服务;环境用 --env 指定)`);
|
|
243
|
+
const service = positional[0];
|
|
244
|
+
if (service && !Object.keys(SERVICES).includes(service))
|
|
245
|
+
throw new Error(`未知服务:${service}(可选:${Object.keys(SERVICES).join(' | ')})`);
|
|
246
|
+
const url = vals['--url'], all = flags.has('--all');
|
|
247
|
+
return { help: flags.has('--help') || flags.has('-h') || !(url || all || service), json: flags.has('--json'), strict: flags.has('--strict'), all, env: envArg, expect: vals['--expect-commit'], url, service };
|
|
248
|
+
}
|
|
197
249
|
async function main() {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
250
|
+
let args;
|
|
251
|
+
try {
|
|
252
|
+
args = parseArgs(process.argv.slice(2));
|
|
253
|
+
}
|
|
254
|
+
catch (e) {
|
|
255
|
+
console.error(`❌ ${e.message}`);
|
|
256
|
+
process.exit(2);
|
|
257
|
+
}
|
|
258
|
+
const { json: asJson, strict, expect, env } = args;
|
|
204
259
|
const envs = env === 'all' ? ENVS : [env];
|
|
205
|
-
const positional = argv.filter((x, i) => !x.startsWith('--') && !(i > 0 && argv[i - 1].startsWith('--') && ['--env', '--expect-commit', '--url'].includes(argv[i - 1])));
|
|
206
260
|
let targets = [];
|
|
207
|
-
if (
|
|
208
|
-
|
|
261
|
+
if (args.help) {
|
|
262
|
+
console.log((require('fs').readFileSync(__filename, 'utf-8').match(/\/\*\*[\s\S]*?\*\//)?.[0] || '').replace(/^\s*\*?/gm, ''));
|
|
263
|
+
process.exit(2);
|
|
264
|
+
}
|
|
265
|
+
else if (args.url) {
|
|
266
|
+
const u = new URL(args.url);
|
|
209
267
|
targets = [[u.hostname, u.hostname, u.pathname || '/health']];
|
|
210
268
|
}
|
|
211
|
-
else if (
|
|
269
|
+
else if (args.all) {
|
|
212
270
|
for (const e of envs)
|
|
213
271
|
for (const s of Object.keys(SERVICES)) {
|
|
214
272
|
const t = resolve(s, e);
|
|
@@ -216,17 +274,13 @@ async function main() {
|
|
|
216
274
|
targets.push(t);
|
|
217
275
|
}
|
|
218
276
|
}
|
|
219
|
-
else if (
|
|
277
|
+
else if (args.service) {
|
|
220
278
|
for (const e of envs) {
|
|
221
|
-
const t = resolve(
|
|
279
|
+
const t = resolve(args.service, e);
|
|
222
280
|
if (t)
|
|
223
281
|
targets.push(t);
|
|
224
282
|
}
|
|
225
283
|
}
|
|
226
|
-
else {
|
|
227
|
-
console.log((require('fs').readFileSync(__filename, 'utf-8').match(/\/\*\*[\s\S]*?\*\//)?.[0] || '').replace(/^\s*\*?/gm, ''));
|
|
228
|
-
process.exit(2);
|
|
229
|
-
}
|
|
230
284
|
if (targets.length === 0) {
|
|
231
285
|
console.log(`(无目标:${env} 环境下该服务无部署)`);
|
|
232
286
|
process.exit(2);
|
|
@@ -252,4 +306,6 @@ async function main() {
|
|
|
252
306
|
// 用 exitCode 而非 process.exit():后者会在管道(--json | jq)未 flush 完就退出而截断输出
|
|
253
307
|
process.exitCode = worstOk ? 0 : 1;
|
|
254
308
|
}
|
|
255
|
-
main
|
|
309
|
+
if (require.main === module) {
|
|
310
|
+
main();
|
|
311
|
+
}
|