feat: 🌊 Guanghu Language Shell v1.0 — unified gateway protocol layer [ZY-GLSHELL-001]
Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com> Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/e198eccd-d605-4e5c-bddb-b0874c4207ec
This commit is contained in:
parent
758974db8d
commit
d7ff35e346
|
|
@ -0,0 +1,20 @@
|
||||||
|
name: '🌊 Guanghu Shell Gate'
|
||||||
|
description: '光湖语言壳 · 统一网关校验'
|
||||||
|
inputs:
|
||||||
|
agent_id:
|
||||||
|
description: 'Agent ID (e.g. AG-ZY-004)'
|
||||||
|
required: true
|
||||||
|
action:
|
||||||
|
description: 'enter / exit / error'
|
||||||
|
required: true
|
||||||
|
default: 'enter'
|
||||||
|
detail:
|
||||||
|
description: '附加信息'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
runs:
|
||||||
|
using: 'composite'
|
||||||
|
steps:
|
||||||
|
- name: 🌊 Guanghu Shell
|
||||||
|
shell: bash
|
||||||
|
run: node .github/scripts/guanghu-shell.js "${{ inputs.agent_id }}" "${{ inputs.action }}" "${{ inputs.detail }}"
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
/**
|
||||||
|
* 🌊 光湖语言壳 · Guanghu Language Shell v1.0
|
||||||
|
* 仓库最外层统一网关协议层
|
||||||
|
*
|
||||||
|
* 所有 workflow 启动时必须先"游过湖水"——经过这层壳的校验。
|
||||||
|
* 网关三层:身份校验 → 状态注册 → 异常兑底
|
||||||
|
*
|
||||||
|
* 用法:
|
||||||
|
* node .github/scripts/guanghu-shell.js <agent_id> <action> [detail]
|
||||||
|
* action: "enter" = 进入湖水(启动前调用)
|
||||||
|
* "exit" = 离开湖水(完成后调用)
|
||||||
|
* "error" = 异常上报(失败时调用)
|
||||||
|
*
|
||||||
|
* 返回值:
|
||||||
|
* exit 0 = 允许通行(游过湖水了)
|
||||||
|
* exit 1 = 拒绝通行(不该跑,或身份不对)
|
||||||
|
*
|
||||||
|
* 签发: TCS-0002∞ 冰朔 + ICE-GL-YM001∞ 曜冥
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const SHELL_VERSION = '1.0.0';
|
||||||
|
const ROOT = path.resolve(__dirname, '..', '..');
|
||||||
|
const REGISTRY_PATH = path.join(ROOT, '.github/persona-brain/agent-registry.json');
|
||||||
|
const STATUS_PATH = path.join(ROOT, '.github/brain/shell-status.json');
|
||||||
|
const ERROR_LOG_PATH = path.join(ROOT, '.github/brain/shell-errors.json');
|
||||||
|
|
||||||
|
const agentId = process.argv[2];
|
||||||
|
const action = process.argv[3];
|
||||||
|
const extraData = process.argv[4] || '';
|
||||||
|
|
||||||
|
if (!agentId || !action) {
|
||||||
|
console.error('🌊 光湖语言壳: 缺少参数 (agent_id, action)');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// === ① 身份校验层 (Identity Gate) ===
|
||||||
|
function identityGate(agentId) {
|
||||||
|
if (!fs.existsSync(REGISTRY_PATH)) {
|
||||||
|
console.error('🌊 湖水中断: agent-registry.json 不存在');
|
||||||
|
return { allowed: false, reason: 'REGISTRY_MISSING' };
|
||||||
|
}
|
||||||
|
|
||||||
|
let registry;
|
||||||
|
try {
|
||||||
|
registry = JSON.parse(fs.readFileSync(REGISTRY_PATH, 'utf8'));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('🌊 湖水中断: agent-registry.json 解析失败');
|
||||||
|
return { allowed: false, reason: 'REGISTRY_PARSE_ERROR' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const agents = registry.agents || [];
|
||||||
|
const agent = agents.find(a => a.id === agentId);
|
||||||
|
|
||||||
|
if (!agent) {
|
||||||
|
console.error(`🌊 身份不明: ${agentId} 未在注册表中`);
|
||||||
|
return { allowed: false, reason: 'UNREGISTERED' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (agent.daily_checkin_required === false) {
|
||||||
|
console.log(`🌊 事件触发型Agent ${agentId} (${agent.name}) 已确认身份,允许通行`);
|
||||||
|
} else {
|
||||||
|
console.log(`🌊 定时型Agent ${agentId} (${agent.name}) 已确认身份,允许通行并登记签到`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { allowed: true, agent: agent };
|
||||||
|
}
|
||||||
|
|
||||||
|
// === ② 状态注册层 (Status Registry) ===
|
||||||
|
function statusRegistry(agentId, state, detail) {
|
||||||
|
let status = {};
|
||||||
|
if (fs.existsSync(STATUS_PATH)) {
|
||||||
|
try {
|
||||||
|
status = JSON.parse(fs.readFileSync(STATUS_PATH, 'utf8'));
|
||||||
|
} catch {
|
||||||
|
status = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = new Date().toISOString();
|
||||||
|
|
||||||
|
if (!status[agentId]) {
|
||||||
|
status[agentId] = { runs: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state === 'enter') {
|
||||||
|
status[agentId].lastEnter = now;
|
||||||
|
status[agentId].currentState = 'running';
|
||||||
|
status[agentId].runs.push({ start: now, end: null, result: null });
|
||||||
|
} else if (state === 'exit') {
|
||||||
|
status[agentId].lastExit = now;
|
||||||
|
status[agentId].currentState = 'completed';
|
||||||
|
const lastRun = status[agentId].runs[status[agentId].runs.length - 1];
|
||||||
|
if (lastRun) {
|
||||||
|
lastRun.end = now;
|
||||||
|
lastRun.result = 'success';
|
||||||
|
if (detail) lastRun.detail = detail;
|
||||||
|
}
|
||||||
|
} else if (state === 'error') {
|
||||||
|
status[agentId].lastError = now;
|
||||||
|
status[agentId].currentState = 'error';
|
||||||
|
const lastRun = status[agentId].runs[status[agentId].runs.length - 1];
|
||||||
|
if (lastRun) {
|
||||||
|
lastRun.end = now;
|
||||||
|
lastRun.result = 'error';
|
||||||
|
if (detail) lastRun.detail = detail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只保留最近 10 次运行记录
|
||||||
|
if (status[agentId].runs.length > 10) {
|
||||||
|
status[agentId].runs = status[agentId].runs.slice(-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(STATUS_PATH, JSON.stringify(status, null, 2));
|
||||||
|
console.log(`🌊 状态已登记: ${agentId} = ${state}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// === ③ 异常兑底层 (Error Boundary) ===
|
||||||
|
function errorBoundary(agentId, errorMsg) {
|
||||||
|
let errors = [];
|
||||||
|
if (fs.existsSync(ERROR_LOG_PATH)) {
|
||||||
|
try {
|
||||||
|
errors = JSON.parse(fs.readFileSync(ERROR_LOG_PATH, 'utf8'));
|
||||||
|
} catch {
|
||||||
|
errors = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errors.push({
|
||||||
|
agentId: agentId,
|
||||||
|
error: errorMsg,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
|
|
||||||
|
// 只保留最近 100 条错误
|
||||||
|
if (errors.length > 100) {
|
||||||
|
errors = errors.slice(-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(ERROR_LOG_PATH, JSON.stringify(errors, null, 2));
|
||||||
|
console.error(`🌊 异常已记录: ${agentId} - ${errorMsg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// === 主流程 ===
|
||||||
|
try {
|
||||||
|
if (action === 'enter') {
|
||||||
|
const gate = identityGate(agentId);
|
||||||
|
if (!gate.allowed) {
|
||||||
|
errorBoundary(agentId, `身份校验失败: ${gate.reason}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
statusRegistry(agentId, 'enter');
|
||||||
|
console.log(`🌊 ${agentId} 已游过湖水,允许进入执行 (shell v${SHELL_VERSION})`);
|
||||||
|
process.exit(0);
|
||||||
|
|
||||||
|
} else if (action === 'exit') {
|
||||||
|
statusRegistry(agentId, 'exit', extraData);
|
||||||
|
console.log(`🌊 ${agentId} 执行完成,已安全离开湖水`);
|
||||||
|
process.exit(0);
|
||||||
|
|
||||||
|
} else if (action === 'error') {
|
||||||
|
statusRegistry(agentId, 'error', extraData);
|
||||||
|
errorBoundary(agentId, extraData || 'unknown error');
|
||||||
|
console.error(`🌊 ${agentId} 执行异常,已记录到异常日志`);
|
||||||
|
process.exit(1);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.error(`🌊 未知action: ${action}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
errorBoundary(agentId, e.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
@ -13,14 +13,41 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# 🌊 进入光湖
|
||||||
|
- name: 🌊 进入光湖
|
||||||
|
uses: ./.github/actions/guanghu-shell
|
||||||
|
with:
|
||||||
|
agent_id: 'AG-ZY-001'
|
||||||
|
action: 'enter'
|
||||||
|
|
||||||
- name: 执行签到任务
|
- name: 执行签到任务
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: node scripts/agent-checkin.js
|
run: node scripts/agent-checkin.js
|
||||||
|
|
||||||
- name: 提交签到记录
|
- name: 提交签到记录
|
||||||
run: |
|
run: |
|
||||||
git config user.name "铸渊"
|
git config user.name "铸渊"
|
||||||
git config user.email "zhuyuan@guanghulab.com"
|
git config user.email "zhuyuan@guanghulab.com"
|
||||||
git add .github/persona-brain/checkin-board.json
|
git add .github/persona-brain/checkin-board.json .github/brain/shell-status.json .github/brain/shell-errors.json
|
||||||
git commit -m "[AG-ZY] 每日签到记录 $(date +'%Y-%m-%d')" || echo "无变更"
|
git commit -m "[AG-ZY] 每日签到记录 $(date +'%Y-%m-%d')" || echo "无变更"
|
||||||
git push
|
git push
|
||||||
|
|
||||||
|
# 🌊 离开光湖(成功时)
|
||||||
|
- name: 🌊 离开光湖
|
||||||
|
if: success()
|
||||||
|
uses: ./.github/actions/guanghu-shell
|
||||||
|
with:
|
||||||
|
agent_id: 'AG-ZY-001'
|
||||||
|
action: 'exit'
|
||||||
|
detail: 'checkin completed'
|
||||||
|
|
||||||
|
# 🌊 异常上报(失败时)
|
||||||
|
- name: 🌊 异常上报
|
||||||
|
if: failure()
|
||||||
|
uses: ./.github/actions/guanghu-shell
|
||||||
|
with:
|
||||||
|
agent_id: 'AG-ZY-001'
|
||||||
|
action: 'error'
|
||||||
|
detail: 'checkin workflow failed'
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,13 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# 🌊 进入光湖
|
||||||
|
- name: 🌊 进入光湖
|
||||||
|
uses: ./.github/actions/guanghu-shell
|
||||||
|
with:
|
||||||
|
agent_id: 'AG-ZY-013'
|
||||||
|
action: 'enter'
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
|
|
@ -174,7 +181,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
git config user.name "铸渊 Maintenance Agent"
|
git config user.name "铸渊 Maintenance Agent"
|
||||||
git config user.email "actions@guanghulab.com"
|
git config user.email "actions@guanghulab.com"
|
||||||
git add brain/system-health.json .github/brain/bulletin-board-today.json README.md
|
git add brain/system-health.json .github/brain/bulletin-board-today.json .github/brain/shell-status.json .github/brain/shell-errors.json README.md
|
||||||
if git diff --cached --quiet; then
|
if git diff --cached --quiet; then
|
||||||
echo "📌 无变更需要提交"
|
echo "📌 无变更需要提交"
|
||||||
else
|
else
|
||||||
|
|
@ -224,6 +231,24 @@ jobs:
|
||||||
}" 2>&1 | tail -1
|
}" 2>&1 | tail -1
|
||||||
echo "✅ Notion 回报请求已发送"
|
echo "✅ Notion 回报请求已发送"
|
||||||
|
|
||||||
|
# 🌊 离开光湖(成功时)
|
||||||
|
- name: 🌊 离开光湖
|
||||||
|
if: success()
|
||||||
|
uses: ./.github/actions/guanghu-shell
|
||||||
|
with:
|
||||||
|
agent_id: 'AG-ZY-013'
|
||||||
|
action: 'exit'
|
||||||
|
detail: 'daily maintenance completed'
|
||||||
|
|
||||||
|
# 🌊 异常上报(失败时)
|
||||||
|
- name: 🌊 异常上报
|
||||||
|
if: failure()
|
||||||
|
uses: ./.github/actions/guanghu-shell
|
||||||
|
with:
|
||||||
|
agent_id: 'AG-ZY-013'
|
||||||
|
action: 'error'
|
||||||
|
detail: 'daily maintenance workflow failed'
|
||||||
|
|
||||||
# ── 同步执行层状态到 Notion Execution Status 数据库 ──
|
# ── 同步执行层状态到 Notion Execution Status 数据库 ──
|
||||||
sync-execution-status-to-notion:
|
sync-execution-status-to-notion:
|
||||||
name: 同步执行层状态到 Notion
|
name: 同步执行层状态到 Notion
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,13 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# 🌊 进入光湖
|
||||||
|
- name: 🌊 进入光湖
|
||||||
|
uses: ./.github/actions/guanghu-shell
|
||||||
|
with:
|
||||||
|
agent_id: 'AG-ZY-058'
|
||||||
|
action: 'enter'
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
|
|
@ -83,7 +90,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
git config user.name "zhuyuan-bot"
|
git config user.name "zhuyuan-bot"
|
||||||
git config user.email "zhuyuan@guanghulab.com"
|
git config user.email "zhuyuan@guanghulab.com"
|
||||||
git add .github/persona-brain/memory.json data/dc-reports/
|
git add .github/persona-brain/memory.json data/dc-reports/ .github/brain/shell-status.json .github/brain/shell-errors.json
|
||||||
if ! git diff --cached --quiet; then
|
if ! git diff --cached --quiet; then
|
||||||
git commit -m "🔍 铸渊每日自检 · ${{ env.CHECK_DATE }} [skip ci]"
|
git commit -m "🔍 铸渊每日自检 · ${{ env.CHECK_DATE }} [skip ci]"
|
||||||
for i in 1 2 3; do
|
for i in 1 2 3; do
|
||||||
|
|
@ -100,3 +107,21 @@ jobs:
|
||||||
else
|
else
|
||||||
echo "📌 无变化,跳过提交"
|
echo "📌 无变化,跳过提交"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 🌊 离开光湖(成功时)
|
||||||
|
- name: 🌊 离开光湖
|
||||||
|
if: success()
|
||||||
|
uses: ./.github/actions/guanghu-shell
|
||||||
|
with:
|
||||||
|
agent_id: 'AG-ZY-058'
|
||||||
|
action: 'exit'
|
||||||
|
detail: 'daily selfcheck completed'
|
||||||
|
|
||||||
|
# 🌊 异常上报(失败时)
|
||||||
|
- name: 🌊 异常上报
|
||||||
|
if: failure()
|
||||||
|
uses: ./.github/actions/guanghu-shell
|
||||||
|
with:
|
||||||
|
agent_id: 'AG-ZY-058'
|
||||||
|
action: 'error'
|
||||||
|
detail: 'daily selfcheck workflow failed'
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
// tests/contract/guanghu-shell.test.js
|
||||||
|
// 🌊 光湖语言壳 · 契约测试
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { execFileSync } = require('child_process');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const ROOT = path.resolve(__dirname, '../..');
|
||||||
|
const SHELL_PATH = path.join(ROOT, '.github/scripts/guanghu-shell.js');
|
||||||
|
const STATUS_PATH = path.join(ROOT, '.github/brain/shell-status.json');
|
||||||
|
const ERROR_LOG_PATH = path.join(ROOT, '.github/brain/shell-errors.json');
|
||||||
|
|
||||||
|
function runShell(agentId, action, detail) {
|
||||||
|
const args = [SHELL_PATH, agentId, action];
|
||||||
|
if (detail) args.push(detail);
|
||||||
|
try {
|
||||||
|
const output = execFileSync('node', args, {
|
||||||
|
cwd: ROOT,
|
||||||
|
encoding: 'utf8',
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
|
});
|
||||||
|
return { exitCode: 0, stdout: output, stderr: '' };
|
||||||
|
} catch (e) {
|
||||||
|
return { exitCode: e.status, stdout: e.stdout || '', stderr: e.stderr || '' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readStatus() {
|
||||||
|
return JSON.parse(fs.readFileSync(STATUS_PATH, 'utf8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function readErrors() {
|
||||||
|
return JSON.parse(fs.readFileSync(ERROR_LOG_PATH, 'utf8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetState() {
|
||||||
|
fs.writeFileSync(STATUS_PATH, '{}');
|
||||||
|
fs.writeFileSync(ERROR_LOG_PATH, '[]');
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('🌊 光湖语言壳 · Guanghu Shell v1.0', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
resetState();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
resetState();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('① 身份校验层', () => {
|
||||||
|
test('已注册Agent(定时型)允许通行', () => {
|
||||||
|
const r = runShell('AG-ZY-013', 'enter');
|
||||||
|
expect(r.exitCode).toBe(0);
|
||||||
|
expect(r.stdout).toContain('定时型Agent');
|
||||||
|
expect(r.stdout).toContain('允许通行');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('已注册Agent(事件触发型)允许通行', () => {
|
||||||
|
const r = runShell('AG-ZY-002', 'enter');
|
||||||
|
expect(r.exitCode).toBe(0);
|
||||||
|
expect(r.stdout).toContain('事件触发型Agent');
|
||||||
|
expect(r.stdout).toContain('允许通行');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('未注册Agent被拒绝', () => {
|
||||||
|
const r = runShell('AG-FAKE-999', 'enter');
|
||||||
|
expect(r.exitCode).toBe(1);
|
||||||
|
expect(r.stderr).toContain('身份不明');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('缺少参数时退出码为1', () => {
|
||||||
|
const r = runShell('AG-ZY-001', '');
|
||||||
|
expect(r.exitCode).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('② 状态注册层', () => {
|
||||||
|
test('enter 记录启动状态', () => {
|
||||||
|
runShell('AG-ZY-001', 'enter');
|
||||||
|
const status = readStatus();
|
||||||
|
expect(status['AG-ZY-001']).toBeDefined();
|
||||||
|
expect(status['AG-ZY-001'].currentState).toBe('running');
|
||||||
|
expect(status['AG-ZY-001'].lastEnter).toBeDefined();
|
||||||
|
expect(status['AG-ZY-001'].runs).toHaveLength(1);
|
||||||
|
expect(status['AG-ZY-001'].runs[0].result).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('exit 记录完成状态', () => {
|
||||||
|
runShell('AG-ZY-001', 'enter');
|
||||||
|
runShell('AG-ZY-001', 'exit', 'completed successfully');
|
||||||
|
const status = readStatus();
|
||||||
|
expect(status['AG-ZY-001'].currentState).toBe('completed');
|
||||||
|
expect(status['AG-ZY-001'].lastExit).toBeDefined();
|
||||||
|
expect(status['AG-ZY-001'].runs[0].result).toBe('success');
|
||||||
|
expect(status['AG-ZY-001'].runs[0].detail).toBe('completed successfully');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('error 记录异常状态', () => {
|
||||||
|
runShell('AG-ZY-058', 'enter');
|
||||||
|
runShell('AG-ZY-058', 'error', 'test failure');
|
||||||
|
const status = readStatus();
|
||||||
|
expect(status['AG-ZY-058'].currentState).toBe('error');
|
||||||
|
expect(status['AG-ZY-058'].lastError).toBeDefined();
|
||||||
|
expect(status['AG-ZY-058'].runs[0].result).toBe('error');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('多次运行保留最近10条', () => {
|
||||||
|
for (let i = 0; i < 12; i++) {
|
||||||
|
runShell('AG-ZY-001', 'enter');
|
||||||
|
runShell('AG-ZY-001', 'exit');
|
||||||
|
}
|
||||||
|
const status = readStatus();
|
||||||
|
expect(status['AG-ZY-001'].runs.length).toBeLessThanOrEqual(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('③ 异常兑底层', () => {
|
||||||
|
test('未注册Agent异常写入错误日志', () => {
|
||||||
|
runShell('AG-FAKE-999', 'enter');
|
||||||
|
const errors = readErrors();
|
||||||
|
expect(errors.length).toBeGreaterThanOrEqual(1);
|
||||||
|
const last = errors[errors.length - 1];
|
||||||
|
expect(last.agentId).toBe('AG-FAKE-999');
|
||||||
|
expect(last.error).toContain('UNREGISTERED');
|
||||||
|
expect(last.timestamp).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('error action 写入错误日志', () => {
|
||||||
|
runShell('AG-ZY-013', 'error', 'something broke');
|
||||||
|
const errors = readErrors();
|
||||||
|
const last = errors[errors.length - 1];
|
||||||
|
expect(last.agentId).toBe('AG-ZY-013');
|
||||||
|
expect(last.error).toBe('something broke');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('错误日志最多保留100条', () => {
|
||||||
|
for (let i = 0; i < 105; i++) {
|
||||||
|
runShell('AG-FAKE-999', 'enter');
|
||||||
|
}
|
||||||
|
const errors = readErrors();
|
||||||
|
expect(errors.length).toBeLessThanOrEqual(100);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue