补充开发日志查看入口
新增开发窗口项目日志只读查看入口 补充命令预览和Agent日志的用户面测试 同步AI游戏创作App技术方案和共享决策记录
This commit is contained in:
@@ -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>
|
||||
))}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user