补充开发日志查看入口

新增开发窗口项目日志只读查看入口

补充命令预览和Agent日志的用户面测试

同步AI游戏创作App技术方案和共享决策记录
This commit is contained in:
AIGameCreator App
2026-07-03 02:45:23 +08:00
parent c503b15ab5
commit 41b23add83
5 changed files with 145 additions and 7 deletions
+83 -4
View File
@@ -3636,6 +3636,8 @@ export function App() {
const [commandLog, setCommandLog] = useState<string[]>([
`${GAME_CREATION_APP_COMMANDS.length} 个命令已登记权限。`,
]);
const [projectLogStatus, setProjectLogStatus] = useState('未读取');
const [projectLogContent, setProjectLogContent] = useState('');
const [conversationWriteVersion, setConversationWriteVersion] = useState(0);
const savedConversationCountRef = useRef(0);
const savedConversationProjectPathRef = useRef<string | null>(null);
@@ -7934,6 +7936,53 @@ export function App() {
}
}
async function handleProjectLogRead(
relativePath: string,
skipPolicyConfirm = false,
) {
const invoke = resolveTauriInvoke();
if (!invoke) {
setProjectLogStatus('需要在 Tauri App 内运行');
return;
}
const nextProjectPath = resolveChatProjectPath(localProject);
if (!nextProjectPath) {
setProjectLogStatus('请先初始化本地项目');
return;
}
setProjectLogStatus(`正在读取 ${relativePath}`);
try {
if (
!skipPolicyConfirm &&
(await queueProjectPolicyConfirmationIfNeeded(
invoke,
'file.read',
nextProjectPath,
`读取 ${nextProjectPath}/${relativePath}`,
'准备读取项目日志。',
() => void handleProjectLogRead(relativePath, true),
))
) {
setProjectLogStatus('等待确认');
return;
}
const result = await invoke<LocalProjectFileResult>(
'read_local_project_file',
{
projectPath: nextProjectPath,
relativePath,
commandId: 'file.read',
},
);
setProjectLogContent(result.content);
setProjectLogStatus(`已读取:${result.path}`);
setCommandLog((current) => [...current, `file.read ${result.path}`]);
} catch (error) {
setProjectLogContent('');
setProjectLogStatus(error instanceof Error ? error.message : String(error));
}
}
async function executeLimitedCommandList() {
const invoke = resolveTauriInvoke();
let commands: GameCreationAppLimitedRunCommandDescriptor[] = [
@@ -10825,10 +10874,36 @@ export function App() {
aria-label="日志"
>
<header className="panel-header">
<h2></h2>
<button type="button" onClick={refreshLimitedLocalCommands}>
</button>
<h2></h2>
<div className="panel-actions">
<button
type="button"
onClick={() =>
void handleProjectLogRead('.agent/logs/command.log')
}
>
</button>
<button
type="button"
onClick={() =>
void handleProjectLogRead('.agent/logs/preview.log')
}
>
</button>
<button
type="button"
onClick={() =>
void handleProjectLogRead('.agent/logs/agent.log')
}
>
Agent日志
</button>
<button type="button" onClick={refreshLimitedLocalCommands}>
</button>
</div>
</header>
<div className="limited-command-list">
{limitedLocalCommands.map((command) => (
@@ -10842,6 +10917,10 @@ export function App() {
))}
</div>
<p className="status-line">{limitedCommandStatus}</p>
<p className="status-line">{projectLogStatus}</p>
{projectLogContent ? (
<pre className="project-log-content">{projectLogContent}</pre>
) : null}
{commandLog.map((entry, index) => (
<p key={`${entry}-${index}`}>{entry}</p>
))}
+10
View File
@@ -1019,6 +1019,16 @@ h2 {
background: #1f6feb;
}
.project-log-content {
max-height: 180px;
margin: 0 0 10px;
padding: 10px;
overflow: auto;
border: 1px solid #dde3ee;
background: #f8fafd;
white-space: pre-wrap;
}
.preview-frame {
display: grid;
width: 100%;
@@ -11540,7 +11540,7 @@ describe('AI 游戏创作 App 界面边界', () => {
'local-project-draft',
'未命名游戏原型',
);
const invoke = vi.fn(async (command: string) => {
const invoke = vi.fn(async (command: string, args?: Record<string, unknown>) => {
if (command === 'is_local_project_directory_non_empty') {
return false;
}
@@ -11562,6 +11562,29 @@ describe('AI 游戏创作 App 界面边界', () => {
};
}
if (command === 'read_local_project_file') {
if (args?.relativePath === '.agent/logs/command.log') {
return {
path: '.agent/logs/command.log',
absolutePath:
'/tmp/authorized-game/.agent/logs/command.log',
content: 'permission.pending preview.start\npermission.confirm preview.start\n',
};
}
if (args?.relativePath === '.agent/logs/preview.log') {
return {
path: '.agent/logs/preview.log',
absolutePath:
'/tmp/authorized-game/.agent/logs/preview.log',
content: 'preview.start http://127.0.0.1:3210/\n',
};
}
if (args?.relativePath === '.agent/logs/agent.log') {
return {
path: '.agent/logs/agent.log',
absolutePath: '/tmp/authorized-game/.agent/logs/agent.log',
content: 'Planner 正在整理规格\nGenerator 已写入草案\n',
};
}
throw new Error(
'读取文件元数据失败:/tmp/authorized-game/.agent/run.latest.json: No such file or directory (os error 2)',
);
@@ -11610,6 +11633,31 @@ describe('AI 游戏创作 App 界面边界', () => {
await screen.findByText('已打开:/tmp/authorized-game'),
).not.toBeNull();
fireEvent.click(logPanel.getByRole('button', { name: '命令日志' }));
expect(
await screen.findByText('已读取:.agent/logs/command.log'),
).not.toBeNull();
expect(screen.getByText(/permission\.confirm preview\.start/)).not.toBeNull();
expect(invoke).toHaveBeenCalledWith('read_local_project_file', {
projectPath: '/tmp/authorized-game',
relativePath: '.agent/logs/command.log',
commandId: 'file.read',
});
fireEvent.click(logPanel.getByRole('button', { name: '预览日志' }));
expect(
await screen.findByText('已读取:.agent/logs/preview.log'),
).not.toBeNull();
expect(
screen.getByText(/preview\.start http:\/\/127\.0\.0\.1:3210\//),
).not.toBeNull();
fireEvent.click(logPanel.getByRole('button', { name: 'Agent日志' }));
expect(
await screen.findByText('已读取:.agent/logs/agent.log'),
).not.toBeNull();
expect(screen.getByText(/Generator 已写入草案/)).not.toBeNull();
fireEvent.click(logPanel.getByRole('button', { name: '自定义自检' }));
expect(
screen.getByText('运行 自定义自检 于 /tmp/authorized-game'),
@@ -3857,6 +3857,7 @@
- 2026-07-03 调整:主窗口 Agent 状态栏新增“继续说明”,只把 `/agent-resume ` 填入聊天输入框,让用户补充说明后再走原确认流;策略快捷入口新增 conversation.read / conversation.write 确认草稿,同样只填输入框,不直接写 `.agent/policy.json`
- 2026-07-03 调整:主窗口 header 常驻项目摘要只从当前已加载的 manifest / trace 派生任务完成数、ready 数、资产来源分布和最近命令结果;未选择工作区时不显示,不为了摘要额外触发 Tauri 读取或写入,也不把任务、文件、run history 或预览开发面板搬进普通用户窗口。
- 2026-07-03 调整:`/llm-status` 读取到的 agent 级 LLM 配置状态可回填到主窗口 Agent 状态列表和单 Agent 对话头部,显示 provider 类型、模型、流式开关和 API Key 是否已读取;密钥本体仍不能进入聊天、状态列表、manifest、trace 或本地项目文件。
- 2026-07-03 调整:开发窗口日志面板提供 `.agent/logs/command.log``.agent/logs/preview.log``.agent/logs/agent.log` 的只读查看入口,复用 `file.read` 授权策略;普通用户窗口仍只通过聊天 `/read` 和摘要消息查看需要的日志,不新增日志面板。
- 2026-06-25 调整:本地 HTTP 预览静态 `HEAD` 必须返回与 `GET` 相同的真实 `Content-Length`,但不返回 body;浏览器、图片、音频和视频探测不能拿到 `Content-Length: 0` 的假响应。
- 2026-06-25 调整:普通用户通过聊天输入 `/run` 触发待确认 `game.run_local`,确认后只能复用白名单 `game.static_smoke` 自检当前 `game/index.html`,通过后启动 `127.0.0.1` 本地 HTTP 预览。独立执行 `game.static_smoke` 时如果已有 `.agent/run.latest.json`,必须追加 `Playtest / game.static_smoke` trace step,避免“运行了代码但编排 trace 不可见”。
- 2026-06-25 调整:普通用户通过聊天输入 `/trace` 触发只读 `agent.trace_read`,读取 `.agent/run.latest.json` 并在聊天里摘要 loop 轮次、stopReason、nextStep、active / carry-over 任务、repairRoutes、agent 建议命令和最近 step。trace 面板仍只在开发窗口展示,普通用户窗口不新增面板。
File diff suppressed because one or more lines are too long