Merge branch 'master' into feat/agc_call_header
This commit is contained in:
@@ -97,7 +97,8 @@ async fn download_agc_update(
|
||||
expected_sha256: Option<String>,
|
||||
expected_size: Option<u64>,
|
||||
) -> Result<String, String> {
|
||||
let parsed = url::Url::parse(download_url.trim()).map_err(|_| "更新下载地址无效".to_string())?;
|
||||
let parsed =
|
||||
url::Url::parse(download_url.trim()).map_err(|_| "更新下载地址无效".to_string())?;
|
||||
if parsed.scheme() != "https" || parsed.host_str() != Some(AGC_UPDATE_OSS_HOST) {
|
||||
return Err("更新下载地址必须来自受信任的 OSS".to_string());
|
||||
}
|
||||
@@ -215,8 +216,8 @@ async fn download_agc_update(
|
||||
let _ = fs::remove_file(&temporary);
|
||||
return Err(format!("提交更新文件失败:{error}"));
|
||||
}
|
||||
let relaunch_path = std::env::current_exe()
|
||||
.map_err(|error| format!("无法定位客户端程序:{error}"))?;
|
||||
let relaunch_path =
|
||||
std::env::current_exe().map_err(|error| format!("无法定位客户端程序:{error}"))?;
|
||||
launch_agc_installer(&target, &relaunch_path)?;
|
||||
app.exit(0);
|
||||
Ok(target.to_string_lossy().into_owned())
|
||||
|
||||
@@ -177,7 +177,7 @@ function assertNativeShellDependencyVersionGuardrails() {
|
||||
"'eas-cli': '^20.3.0'",
|
||||
"assertPackageLockVersion('apps/mobile-shell', 'eas-cli', '20.3.0')",
|
||||
]) {
|
||||
if (!mobileShellConfigCheckSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(mobileShellConfigCheckSource, snippet)) {
|
||||
throw new Error(
|
||||
`mobile shell dependency guardrail drifted: missing ${snippet}`,
|
||||
);
|
||||
@@ -201,7 +201,7 @@ function assertNativeShellDependencyVersionGuardrails() {
|
||||
"['tauri', '2.11.2']",
|
||||
'tauri-plugin-single-instance = { version = "2.4.2", features = ["deep-link"] }',
|
||||
]) {
|
||||
if (!desktopShellConfigCheckSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(desktopShellConfigCheckSource, snippet)) {
|
||||
throw new Error(
|
||||
`desktop shell dependency guardrail drifted: missing ${snippet}`,
|
||||
);
|
||||
@@ -2440,7 +2440,7 @@ function assertAiGameCreatorShellUserDevBoundary() {
|
||||
'if (!import.meta.env.DEV)',
|
||||
"return params.has('dev') || window.location.hash === '#dev';",
|
||||
]) {
|
||||
if (!aiGameCreatorShellAppModelSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(aiGameCreatorShellAppModelSource, snippet)) {
|
||||
throw new Error(
|
||||
`AI game creator developer mode boundary drifted: missing ${snippet}`,
|
||||
);
|
||||
@@ -2451,7 +2451,7 @@ function assertAiGameCreatorShellUserDevBoundary() {
|
||||
'{devMode ? (',
|
||||
'className="developer-pane"',
|
||||
]) {
|
||||
if (!aiGameCreatorShellAppSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(aiGameCreatorShellAppSource, snippet)) {
|
||||
throw new Error(
|
||||
`AI game creator user/dev UI boundary drifted: missing ${snippet}`,
|
||||
);
|
||||
@@ -2525,7 +2525,9 @@ function assertAiGameCreatorShellUserDevBoundary() {
|
||||
'sandbox="allow-scripts allow-same-origin allow-forms allow-pointer-lock"',
|
||||
'src={embeddedUrl}',
|
||||
]) {
|
||||
if (!aiGameCreatorLocalGamePreviewFrameSource.includes(snippet)) {
|
||||
if (
|
||||
!sourceIncludesSnippet(aiGameCreatorLocalGamePreviewFrameSource, snippet)
|
||||
) {
|
||||
throw new Error(
|
||||
`AI game creator embedded preview boundary drifted: missing ${snippet}`,
|
||||
);
|
||||
@@ -2535,7 +2537,7 @@ function assertAiGameCreatorShellUserDevBoundary() {
|
||||
"await invoke<LocalPreviewStatus>(\n 'activate_local_game_preview'",
|
||||
'已切换到客户端运行视图',
|
||||
]) {
|
||||
if (!aiGameCreatorShellAppSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(aiGameCreatorShellAppSource, snippet)) {
|
||||
throw new Error(
|
||||
`AI game creator client preview activation drifted: missing ${snippet}`,
|
||||
);
|
||||
@@ -2545,7 +2547,7 @@ function assertAiGameCreatorShellUserDevBoundary() {
|
||||
'fn activate_local_game_preview(',
|
||||
'preview_open_url(&status)?;',
|
||||
]) {
|
||||
if (!aiGameCreatorPreviewRustSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(aiGameCreatorPreviewRustSource, snippet)) {
|
||||
throw new Error(
|
||||
`AI game creator client preview command drifted: missing ${snippet}`,
|
||||
);
|
||||
@@ -2576,7 +2578,7 @@ function assertAiGameCreatorShellUserDevBoundary() {
|
||||
'项目总控对话窗口仅在开发构建中可用',
|
||||
'index.html?supervisor-chat&projectPath=',
|
||||
]) {
|
||||
if (!aiGameCreatorShellTauriSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(aiGameCreatorShellTauriSource, snippet)) {
|
||||
throw new Error(
|
||||
`AI game creator supervisor chat window must stay developer-only: ${snippet}`,
|
||||
);
|
||||
@@ -3242,17 +3244,20 @@ function assertH5HostBridgePayloadBoundaries() {
|
||||
);
|
||||
}
|
||||
if (
|
||||
!h5HostBridgeSource.includes(
|
||||
!sourceIncludesSnippet(
|
||||
h5HostBridgeSource,
|
||||
'function normalizeNativeAppPageUrl(url: string)',
|
||||
) ||
|
||||
!h5HostBridgeSource.includes("trimmedUrl.startsWith('//')") ||
|
||||
!h5HostBridgeSource.includes(
|
||||
!sourceIncludesSnippet(h5HostBridgeSource, "trimmedUrl.startsWith('//')") ||
|
||||
!sourceIncludesSnippet(
|
||||
h5HostBridgeSource,
|
||||
'nativePageUrl.origin !== HOST_BRIDGE_PUBLIC_WEB_ORIGIN',
|
||||
) ||
|
||||
!h5HostBridgeSource.includes(
|
||||
!sourceIncludesSnippet(
|
||||
h5HostBridgeSource,
|
||||
'const normalizedUrl = normalizeNativeAppPageUrl(url);',
|
||||
) ||
|
||||
!h5HostBridgeSource.includes('{ url: normalizedUrl },')
|
||||
!sourceIncludesSnippet(h5HostBridgeSource, 'url: normalizedUrl,')
|
||||
) {
|
||||
throw new Error(
|
||||
'H5 HostBridge facade must reject unsafe native app navigation targets before sending navigation.openNativePage',
|
||||
@@ -3281,7 +3286,7 @@ function assertH5HostBridgePayloadBoundaries() {
|
||||
"rejects.not.toThrow(\n 'private native detail',",
|
||||
'expect(consoleError.mock.calls.flat()).not.toContain(navigationError)',
|
||||
]) {
|
||||
if (!h5HostBridgeTestSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(h5HostBridgeTestSource, snippet)) {
|
||||
throw new Error(
|
||||
`H5 HostBridge navigation failure test must include ${snippet}`,
|
||||
);
|
||||
@@ -3556,7 +3561,7 @@ function assertH5NativeAppMessageSourceBoundaries() {
|
||||
'if (!isNativeInjectedMessageEvent(event))',
|
||||
];
|
||||
for (const snippet of requiredSourceSnippets) {
|
||||
if (!nativeAppHostBridgeSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(nativeAppHostBridgeSource, snippet)) {
|
||||
throw new Error(
|
||||
`H5 native app transport must verify injected message source and origin: ${snippet}`,
|
||||
);
|
||||
@@ -3571,7 +3576,7 @@ function assertH5NativeAppMessageSourceBoundaries() {
|
||||
'expect(listener).not.toHaveBeenCalled();',
|
||||
];
|
||||
for (const snippet of requiredTestSnippets) {
|
||||
if (!nativeAppHostBridgeTestSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(nativeAppHostBridgeTestSource, snippet)) {
|
||||
throw new Error(
|
||||
`H5 native app transport source boundary test must include ${snippet}`,
|
||||
);
|
||||
@@ -3844,7 +3849,7 @@ function assertNativeShellCapabilityPlan() {
|
||||
'let response = resolve_host_bridge_request(request(method));',
|
||||
'assert_eq!(error.code, "unsupported_method");',
|
||||
]) {
|
||||
if (!desktopDispatchSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(desktopDispatchSource, snippet)) {
|
||||
throw new Error(
|
||||
'desktop dispatch test must derive unsupported methods from Rust capability list',
|
||||
);
|
||||
@@ -4147,7 +4152,7 @@ function sourceIncludesSnippet(source, snippet) {
|
||||
.replace(/\s+/g, ' ')
|
||||
.replace(/\s*([()[\]{}])\s*/g, '$1')
|
||||
.replace(/\s*,\s*/g, ',')
|
||||
.replace(/,\)/g, ')')
|
||||
.replace(/,([)}\]])/g, '$1')
|
||||
.trim();
|
||||
return source.includes(snippet) || compact(source).includes(compact(snippet));
|
||||
}
|
||||
@@ -4421,7 +4426,7 @@ function assertWechatPaymentResultBoundaries() {
|
||||
"logWechatPayFailure('requestVirtualPayment unavailable')",
|
||||
"logWechatPayFailure('requestVirtualPayment failed', error)",
|
||||
]) {
|
||||
if (!paymentSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(paymentSource, snippet)) {
|
||||
throw new Error(`wechat payment bridge must include ${snippet}`);
|
||||
}
|
||||
}
|
||||
@@ -4451,7 +4456,7 @@ function assertWechatPaymentResultBoundaries() {
|
||||
"expect(console.error).toHaveBeenCalledWith(\n '[wechat-pay] requestVirtualPayment failed'",
|
||||
'expect(console.error.mock.calls.flat()).not.toContain(payError)',
|
||||
]) {
|
||||
if (!paymentTestSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(paymentTestSource, snippet)) {
|
||||
throw new Error(`wechat payment bridge test must include ${snippet}`);
|
||||
}
|
||||
}
|
||||
@@ -4605,7 +4610,7 @@ function assertWechatWebViewPageEventBoundaries() {
|
||||
"expect(console.info).toHaveBeenCalledWith('[web-view] message')",
|
||||
'expect(console.info.mock.calls.flat()).not.toContain(webViewDetail)',
|
||||
]) {
|
||||
if (!webViewTestSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(webViewTestSource, snippet)) {
|
||||
throw new Error(
|
||||
`wechat web-view page event boundary test must include ${snippet}`,
|
||||
);
|
||||
@@ -4632,7 +4637,7 @@ function assertWechatShareGridFailureBoundaries() {
|
||||
'reject(new Error(WECHAT_SHARE_GRID_SAVE_UNAVAILABLE_MESSAGE))',
|
||||
'errorMessage: WECHAT_SHARE_GRID_SAVE_UNAVAILABLE_MESSAGE',
|
||||
]) {
|
||||
if (!shareGridSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(shareGridSource, snippet)) {
|
||||
throw new Error(`wechat share-grid shell must include ${snippet}`);
|
||||
}
|
||||
}
|
||||
@@ -4659,7 +4664,7 @@ function assertWechatShareGridFailureBoundaries() {
|
||||
"expect(consoleError).toHaveBeenCalledWith('[share-grid] save failed')",
|
||||
"expect(consoleError.mock.calls.flat()).not.toContain('private native download detail')",
|
||||
]) {
|
||||
if (!shareGridTestSource.includes(snippet)) {
|
||||
if (!sourceIncludesSnippet(shareGridTestSource, snippet)) {
|
||||
throw new Error(
|
||||
`wechat share-grid boundary test must include ${snippet}`,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user