@mindscraft/branch-video-agent-cli 0.4.3 → 0.4.4
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/README.md +2 -0
- package/dist/playtest/runner.d.ts +2 -0
- package/dist/playtest/runner.js +23 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,6 +97,8 @@ sidecar 使用 `branch-video-playtest/1`,Web 结果必须提供 iframe 内的
|
|
|
97
97
|
CI 顺序为:V3 静态 self-test → 草稿 `playtest run` → 外部发布步骤 → `version get` 精确内容读回。发布内容与已测草稿一致时无需再跑发布版全量测试;内容变化后应重新测试。`playtest run` 自身始终只读,不承担发布。
|
|
98
98
|
|
|
99
99
|
从 `0.4.3` 起,规划器删除被更长场景完整包含的有序前缀场景,保留条件、状态效果与全部目标边;场景数由实际图决定,不固定为 96。重复搜索状态也会合并以减少规划开销。覆盖或路径预算失败仍返回失败,不通过删边取得通过。复测时记录实际可执行文件路径;升级全局 npm 包不会更新任务内复制的旧 CLI。
|
|
100
|
+
|
|
101
|
+
从 `0.4.4` 起,runner 在现有超时内等待“进入游戏”或“开始体验”入口;已开始播放时直接继续,后续节点和路由断言保持不变。
|
|
100
102
|
|
|
101
103
|
AIHub 导入公共库示例:
|
|
102
104
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Frame } from 'playwright';
|
|
1
2
|
import type { PlaytestContract, PlaytestLimits, PlaytestScenario, PlaytestScenarioResult } from './types.js';
|
|
2
3
|
type ScriptRecord = Record<string, any>;
|
|
3
4
|
interface RunnerOptions {
|
|
@@ -14,6 +15,7 @@ interface RunnerOptions {
|
|
|
14
15
|
height: number;
|
|
15
16
|
};
|
|
16
17
|
}
|
|
18
|
+
export declare function startPlayback(frame: Frame, entryNodeId: string, entryNodeType: string, timeout: number): Promise<void>;
|
|
17
19
|
export declare function runPlaytestScenarios(options: RunnerOptions): Promise<PlaytestScenarioResult[]>;
|
|
18
20
|
export declare function createDraftPlayerUrl(baseUrl: string): {
|
|
19
21
|
playerUrl: string;
|
package/dist/playtest/runner.js
CHANGED
|
@@ -111,12 +111,24 @@ async function waitForNode(frame, nodeId, timeout) {
|
|
|
111
111
|
throw new PlaytestError('ROUTE_TARGET_MISMATCH', `Expected node ${nodeId}, reached ${actual || 'none'}`, { expected: nodeId, actual });
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
-
async function startPlayback(frame, entryNodeId, entryNodeType, timeout) {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
114
|
+
export async function startPlayback(frame, entryNodeId, entryNodeType, timeout) {
|
|
115
|
+
const deadline = Date.now() + timeout;
|
|
116
|
+
const remaining = () => {
|
|
117
|
+
const value = deadline - Date.now();
|
|
118
|
+
if (value <= 0)
|
|
119
|
+
throw new PlaytestError('SCENARIO_TIMEOUT', 'Scenario timeout elapsed before entry playback completed');
|
|
120
|
+
return value;
|
|
121
|
+
};
|
|
122
|
+
const entryNode = frame.locator('[data-bv-node-id]').first();
|
|
123
|
+
if (!(await entryNode.isVisible().catch(() => false))) {
|
|
124
|
+
const startButton = frame.getByRole('button', { name: /^(开始体验|进入游戏)$/ }).first();
|
|
125
|
+
await startButton.or(entryNode).filter({ visible: true }).first().waitFor({ state: 'visible', timeout: remaining() });
|
|
126
|
+
if (!(await entryNode.isVisible().catch(() => false)) && await startButton.isVisible().catch(() => false)) {
|
|
127
|
+
await startButton.click({ timeout: remaining() });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
await entryNode.waitFor({ state: 'visible', timeout: remaining() });
|
|
131
|
+
const current = await entryNode.getAttribute('data-bv-node-id');
|
|
120
132
|
if (current !== entryNodeId) {
|
|
121
133
|
const observed = await readObservedMessages(frame);
|
|
122
134
|
const sawEntry = observed.some((message) => JSON.stringify(message.data).includes(entryNodeId));
|
|
@@ -480,6 +492,7 @@ async function runScenario(browser, scenario, options) {
|
|
|
480
492
|
}));
|
|
481
493
|
}
|
|
482
494
|
let timeoutHandle;
|
|
495
|
+
const scenarioDeadline = Date.now() + options.limits.scenarioTimeoutMs;
|
|
483
496
|
const scenarioTimeout = new Promise((_, reject) => {
|
|
484
497
|
timeoutHandle = globalThis.setTimeout(() => reject(new PlaytestError('SCENARIO_TIMEOUT', `Scenario ${scenario.id} exceeded ${options.limits.scenarioTimeoutMs}ms`)), options.limits.scenarioTimeoutMs);
|
|
485
498
|
});
|
|
@@ -490,7 +503,10 @@ async function runScenario(browser, scenario, options) {
|
|
|
490
503
|
const playerFrame = await waitForPlayerFrame(page, options.playerUrl, options.limits.scenarioTimeoutMs);
|
|
491
504
|
await assertPlayerProtocol(playerFrame, options.limits.scenarioTimeoutMs);
|
|
492
505
|
const entryNodeId = String(options.script.graph?.entryNodeId || '');
|
|
493
|
-
|
|
506
|
+
const entryTimeout = scenarioDeadline - Date.now();
|
|
507
|
+
if (entryTimeout <= 0)
|
|
508
|
+
throw new PlaytestError('SCENARIO_TIMEOUT', `Scenario ${scenario.id} exceeded ${options.limits.scenarioTimeoutMs}ms`);
|
|
509
|
+
await startPlayback(playerFrame, entryNodeId, String(getNode(options.script, entryNodeId).type || ''), entryTimeout);
|
|
494
510
|
playerReady = true;
|
|
495
511
|
await assertNodeMedia(playerFrame, getNode(options.script, entryNodeId));
|
|
496
512
|
visitedNodes.push(String(options.script.graph?.entryNodeId || ''));
|