Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions frontend/src/adk/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,9 @@ export class RuntimeProbeError extends Error {
}
}

const PRIVATE_RUNTIME_UNREACHABLE_MESSAGE =
"Runtime 已部署成功,但当前 Studio 无法访问私网 Runtime。请使用已绑定相同 VPC 的 Studio 访问,或改用公网 / 公网+VPC 部署。";

async function runtimeProxyErrorCode(response: Response): Promise<string> {
try {
const payload = (await response.clone().json()) as {
Expand All @@ -434,6 +437,12 @@ export async function fetchRemoteApps(
if (ep?.runtimeId && runtimeErrorCode === "runtime_access_denied") {
throw new RuntimeAccessDeniedError();
}
if (
ep?.runtimeId &&
runtimeErrorCode === "runtime_private_endpoint_unreachable"
) {
throw new RuntimeProbeError(PRIVATE_RUNTIME_UNREACHABLE_MESSAGE);
}
if (ep?.runtimeId && res.status === 404) {
throw new RuntimeProbeError(
"该 Runtime 的 Agent Server 未提供连接接口,请确认 Runtime 已就绪且版本兼容。",
Expand Down
11 changes: 11 additions & 0 deletions frontend/tests/studioAccess.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const connectionsSource = read("adk/connections.ts");
const selectorSource = read("ui/AgentSelector.tsx");
const sidebarSource = read("ui/Sidebar.tsx");
const stylesSource = read("styles.css");
const cliFrontendSource = readFileSync(
new URL("../../veadk/cli/cli_frontend.py", import.meta.url),
"utf8",
);

test("Studio access fails closed until the server-derived role is known", () => {
assert.match(clientSource, /export type StudioRole = "admin" \| "developer" \| "user"/);
Expand Down Expand Up @@ -54,6 +58,13 @@ test("runtime selection obeys the server-granted scope", () => {
test("runtime authorization failures are not reported as unsupported", () => {
assert.match(clientSource, /response\.clone\(\)\.json\(\)/);
assert.match(clientSource, /runtime_access_denied/);
assert.match(clientSource, /runtime_private_endpoint_unreachable/);
assert.match(clientSource, /Runtime 已部署成功,但当前 Studio 无法访问私网 Runtime/);
assert.match(cliFrontendSource, /endpoint_network_type == "private"[\s\S]*?runtime_private_endpoint_unreachable/);
assert.match(cliFrontendSource, /def _runtime_proxy_should_retry_probe[\s\S]*?normalized == "list-apps"[\s\S]*?normalized\.startswith\("web\/agent-info\/"\)/);
assert.match(cliFrontendSource, /parts\[0\] == "apps"[\s\S]*?parts\[2\] == "users"[\s\S]*?parts\[4\] == "sessions"/);
assert.match(cliFrontendSource, /max_attempts = 10 if retry_probe else 1/);
assert.match(cliFrontendSource, /runtime-proxy probe retry/);
assert.match(clientSource, /res\.status === 404[\s\S]*?RuntimeProbeError/);
assert.match(clientSource, /res\.status === 401 \|\| res\.status === 403/);
assert.match(clientSource, /error instanceof RuntimeAccessDeniedError \|\|[\s\S]*?error instanceof RuntimeProbeError/);
Expand Down
Loading
Loading