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
2 changes: 0 additions & 2 deletions apps/web/src/features/sandbox/NodeList.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ describe("node state", () => {
expect(nodeState(node("a", { online: false }), [], true, core)).toBe("unconfirmed");
expect(nodeState(node("a", { core_url: "https://core-old.example" }), [], false, core)).toBe("old_address");
expect(nodeState(node("a", { online: false, core_url: "https://core-old.example" }), [], false, core)).toBe("old_address");
// A node Core did not enroll, such as a file-managed local one, reports no address: unknown, not old.
expect(nodeState(node("a", { core_url: "" }), [], false, core)).toBe("available");
expect(nodeState(node("a", { online: false, cleanup_pending: 2 }), [], false, core)).toBe("offline");
expect(nodeState(node("a", { provider_ready: false }), [], false, core)).toBe("degraded");
});
Expand Down
5 changes: 2 additions & 3 deletions apps/web/src/features/sandbox/NodeList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ export type NodeState = "unconfirmed" | "old_address" | "offline" | "degraded" |

/**
* Whether a node enrolled with another Core address than the deployment's
* `coreUrl`. An empty address is unknown, not old: a node Core did not enroll,
* such as a file-managed local one, reports none.
* `coreUrl`. While the deployment is unknown, no node is on an old address.
*/
export function onOldAddress(node: SandboxNode, coreUrl: string): boolean {
return Boolean(node.core_url && coreUrl && node.core_url !== coreUrl);
return coreUrl !== "" && node.core_url !== coreUrl;
}

/**
Expand Down
2 changes: 0 additions & 2 deletions apps/web/src/lib/locale-strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,6 @@ export const chinese = {
"Couldn't confirm the sandbox change": "沙箱更改未能确认",
"Sandbox state couldn't be read": "无法读取沙箱状态",
"Nodes": "节点",
"Local nodes run on the Core server. Capacity and counts are reported by Core.": "本地节点运行在 Core 服务器上。容量和资源数量由 Core 上报。",
"Sandbox nodes": "沙箱节点",
"Node": "节点",
"Health": "健康状态",
Expand Down Expand Up @@ -244,7 +243,6 @@ export const chinese = {
"Automatic placement": "自动分配",
"available": "可用",
"unavailable": "不可用",
"Optional. Local nodes run on the Core server. A selected node must be available; Core will not fall back to another node.": "可选。本地节点运行在 Core 服务器上。所选节点必须可用;Core 不会自动改用其他节点。",
"Loading nodes…": "正在加载节点…",
"Retry directory": "重新加载节点目录",
"No nodes are registered.": "尚未注册节点。",
Expand Down
2 changes: 1 addition & 1 deletion contracts/agents-api/core.openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6222,7 +6222,7 @@ paths:
post:
consumes:
- application/json
description: Selects a provider, enforced resource limits and pinned Runtime release. Core derives the deployment's core_url from the installation public URL and rejects a core_url member with 400. E2B returns 409 sandbox_configuration_error while the public URL is loopback. E2B credentials are write-only. E2B may omit resources to adopt the validated template build's CPU and memory, returned in specification.resources. Requires explicit expected_generation, including zero at first setup. Stale retries reject before provider validation. An identical selection at the current generation is a no-op; differing selections and file-managed deployments reject. This does not create compute or execute work.
description: Selects a provider, enforced resource limits and pinned Runtime release. Core derives the deployment's core_url from the installation public URL and rejects a core_url member with 400. E2B returns 409 sandbox_configuration_error while the public URL is loopback. E2B credentials are write-only. E2B may omit resources to adopt the validated template build's CPU and memory, returned in specification.resources. Requires explicit expected_generation, including zero at first setup. Stale retries reject before provider validation. An identical selection at the current generation is a no-op; a differing selection rejects. This does not create compute or execute work.
parameters:
- description: Deployment selection
in: body
Expand Down
2 changes: 1 addition & 1 deletion docs/web/console-api-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ The list carries each harness's configuration, so the console does not read `GET
| Deployment | `GET`, `POST`, `PUT /core/v1/sandbox/deployment` | Read the provider, the read-only `core_url` (`OAC_PUBLIC_URL`, shown in the setup review and never sent), reset state, installation ID and specification; a 409 `sandbox_configuration_error` (E2B with a loopback `public_url`) shows the shared client's fixed safe address-configuration message in the setup wizard, with Managed in System leading to System, and leaves nothing to confirm; initialize the deployment with `resources` and the Docker or microsandbox `runtime` release, or with the E2B account and no `resources` (Core adopts the template build's CPU and memory); change its settings with the expected generation. E2B's `metadata.template_build` (status, CPU, memory, disk) shows on System, the Sandbox configuration summary and Sandbox metrics, and sizes each sandbox when `specification.resources` is missing; microsandbox's `suspension` (idle and retention seconds) shows on System and the Nodes summary |
| E2B discovery | `POST /core/v1/sandbox/providers/e2b/discovery` | The setup wizard lists the templates the entered E2B key can see, then the selected template's ready builds. The key travels only in these request bodies and the deployment write |
| Reset | `POST`, `DELETE /core/v1/sandbox/deployment/reset` | Explicitly clear hosted resources, or cancel the remaining clear at the observed generation; show Core's remaining and offline projection |
| Nodes | `GET /core/v1/sandbox/nodes` | Nodes page; fleet on Overview; node capacity on Sandbox metrics. An online node's `diagnostic` (`docker_unavailable`, `docker_limits_unsupported`, `runtime_image_unavailable`, `kvm_unavailable`, `microsandbox_artifacts_unavailable`, `capacity_insufficient`, `provider_unavailable`; any other value reads as `provider_unavailable`) marks it degraded and names the reason and fix in the help tip beside its status on each of these and on the node's page. A node whose `core_url` (the address it enrolled with) differs from the deployment's `core_url` is named on the Nodes page as bound to an old address, to be removed and added again, and its status there and on its page reads Old address instead of its health; an empty `core_url` (a node Core did not enroll) is unknown, not old. **Add node** follows only the node whose `enrollment_id` equals its command's |
| Nodes | `GET /core/v1/sandbox/nodes` | Nodes page; fleet on Overview; node capacity on Sandbox metrics. An online node's `diagnostic` (`docker_unavailable`, `docker_limits_unsupported`, `runtime_image_unavailable`, `kvm_unavailable`, `microsandbox_artifacts_unavailable`, `capacity_insufficient`, `provider_unavailable`; any other value reads as `provider_unavailable`) marks it degraded and names the reason and fix in the help tip beside its status on each of these and on the node's page. A node whose `core_url` (the address it enrolled with) differs from the deployment's `core_url` is named on the Nodes page as bound to an old address, to be removed and added again, and its status there and on its page reads Old address instead of its health. **Add node** follows only the node whose `enrollment_id` equals its command's |
| Node detail | `GET /core/v1/sandbox/nodes/{node_id}?range=1h\|6h\|24h` | Sandbox metrics node dialog: the host's CPU busy share and memory from its last heartbeat, and their history over the page's range. **Edit node** reads `host.effective_cpu_cores` and `host.total_memory_bytes` to show the host beside each sandbox's size and at most how many of those fit |
| Allocations | `GET /core/v1/sandbox/nodes/{node_id}/allocations` | Nodes page; Sandbox metrics. Under microsandbox, a node's page shows from `compute_phase_changed_at` how long each allocation has been in its compute phase and, while suspended, about when Core reclaims it (that time plus the deployment's `suspension.retention_seconds`); a null time shows a dash |
| Enrollment | `POST /core/v1/sandbox/enrollment-tokens` | **Add node**: the administrator sets the node's sandbox limits (`max_active`; `max_retained` only for microsandbox, equal to `max_active` for Docker) before Core issues a single-use token inside a command that verifies the installer checksum, with the command's `enrollment_id`, which the node it registers reports. The command runs the installer with sudo (a system service) and passes the token on standard input; root runs it directly. No ordinary-user installation or removal entry is exposed, and the log hint always names the system service. The command downloads the installer from the installation's `public_url`. No token is requested until the installation is read, when it cannot be read, when it is `local_only` (or its `public_url` is not an HTTPS origin), or when `/console/config` lists `node_artifacts` without the deployment's provider. The dialog reads both again on opening and when the window regains focus |
Expand Down
4 changes: 2 additions & 2 deletions docs/zh/web/console-api-usage.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "控制台 API 使用"
source: docs/web/console-api-usage.md
source_hash: 50edc63c79976e9aad9590604a0e361aae178144bc8a6009989b2da5d031408d
source_hash: 7318d1d082d19cc1681a2bd91556c4dc7e16b211ae7cd3936b99d63c26949900
---

本页列出各控制台页面读取和写入的 Core 路由,以及控制台如何限定读取范围。[administrator API contract](../../../contracts/agents-api/zh/admin-api.md) 定义了路由、响应结构、分页和审计记录;[API namespaces and credentials](../api/index.md) 定义了本文使用的术语。
Expand Down Expand Up @@ -100,7 +100,7 @@ source_hash: 50edc63c79976e9aad9590604a0e361aae178144bc8a6009989b2da5d031408d
| 部署 | `GET`、`POST`、`PUT /core/v1/sandbox/deployment` | 读取提供商、只读 `core_url`(即 `OAC_PUBLIC_URL`,会显示在设置审核中且绝不发送)、重置状态、安装 ID 和规范;409 `sandbox_configuration_error`(E2B 搭配回环地址形式的 `public_url`)会在设置向导中显示共享客户端固定的安全地址配置消息,并通过 Managed in System 前往 System,且无需确认;使用 `resources` 以及 Docker 或 microsandbox 的 `runtime` release 初始化部署,或者使用 E2B 账户且不提供 `resources`(Core 采用模板构建的 CPU 和内存);使用预期的 generation 更改设置。E2B 的 `metadata.template_build`(状态、CPU、内存、磁盘)会显示在 System、Sandbox 配置摘要和 Sandbox metrics 中;当缺少 `specification.resources` 时,它还会确定每个 Sandbox 的大小;microsandbox 的 `suspension`(空闲和保留秒数)会显示在 System 和 Nodes 摘要中 |
| E2B 发现 | `POST /core/v1/sandbox/providers/e2b/discovery` | 设置向导先列出输入的 E2B 密钥可见的模板,再列出所选模板的可用构建。该密钥只会通过这些请求体和部署写入请求传输 |
| 重置 | `POST`、`DELETE /core/v1/sandbox/deployment/reset` | 显式清除托管资源,或在观测到的 generation 处取消剩余清除;显示 Core 的剩余资源和离线预测 |
| Nodes | `GET /core/v1/sandbox/nodes` | Nodes 页面;Overview 上的机群;Sandbox metrics 中的节点容量。在线节点的 `diagnostic`(`docker_unavailable`、`docker_limits_unsupported`、`runtime_image_unavailable`、`kvm_unavailable`、`microsandbox_artifacts_unavailable`、`capacity_insufficient`、`provider_unavailable`;任何其他值均读取为 `provider_unavailable`)会将其标记为降级,并在上述每个页面及节点页面中,紧邻状态的帮助提示里说明原因和修复方法。如果节点的 `core_url`(其注册时使用的地址)与部署的 `core_url` 不同,Nodes 页面会将其标记为绑定到旧地址,需要移除后重新添加;此时它在该页面和节点页面中的状态会显示 Old address,而不是健康状态;如果 `core_url` 为空(Core 未注册该节点),则状态为未知,而不是旧地址。**Add node** 仅跟踪 `enrollment_id` 与其命令所含 `enrollment_id` 相等的节点 |
| Nodes | `GET /core/v1/sandbox/nodes` | Nodes 页面;Overview 上的机群;Sandbox metrics 中的节点容量。在线节点的 `diagnostic`(`docker_unavailable`、`docker_limits_unsupported`、`runtime_image_unavailable`、`kvm_unavailable`、`microsandbox_artifacts_unavailable`、`capacity_insufficient`、`provider_unavailable`;任何其他值均读取为 `provider_unavailable`)会将其标记为降级,并在上述每个页面及节点页面中,紧邻状态的帮助提示里说明原因和修复方法。如果节点的 `core_url`(其注册时使用的地址)与部署的 `core_url` 不同,Nodes 页面会将其标记为绑定到旧地址,需要移除后重新添加;此时它在该页面和节点页面中的状态会显示 Old address,而不是健康状态。**Add node** 仅跟踪 `enrollment_id` 与其命令所含 `enrollment_id` 相等的节点 |
| 节点详情 | `GET /core/v1/sandbox/nodes/{node_id}?range=1h\|6h\|24h` | Sandbox metrics 节点对话框:主机自最近一次心跳以来的 CPU 忙碌占比和内存使用量,以及页面所选范围内二者的历史记录。**Edit node** 读取 `host.effective_cpu_cores` 和 `host.total_memory_bytes`,用于在每个 Sandbox 大小旁显示主机容量,以及最多可容纳多少个该大小的 Sandbox |
| 分配 | `GET /core/v1/sandbox/nodes/{node_id}/allocations` | Nodes 页面;Sandbox metrics。在 microsandbox 下,节点页面根据 `compute_phase_changed_at` 显示每个分配处于计算阶段的时间,并在分配暂停时估算 Core 回收它的时间(该时间加上部署的 `suspension.retention_seconds`);时间为 null 时显示短横线 |
| 注册 | `POST /core/v1/sandbox/enrollment-tokens` | **Add node**:管理员先设置节点的 Sandbox 限制(`max_active`;`max_retained` 仅适用于 microsandbox,在 Docker 下等于 `max_active`),然后 Core 才会把一次性令牌放入命令中;该命令会验证安装程序校验和,并包含命令的 `enrollment_id`,节点注册时会报告此 ID。命令使用 sudo 运行安装程序(作为系统服务),并通过标准输入传递令牌;以 root 运行时则直接执行。界面不提供普通用户安装或移除入口,日志提示始终指明系统服务。命令从安装的 `public_url` 下载安装程序。只有成功读取安装信息后才会请求令牌;如果安装信息无法读取、安装为 `local_only`(或其 `public_url` 不是 HTTPS 来源),或者 `/console/config` 列出的 `node_artifacts` 不包含部署的提供商,则不会请求令牌。对话框在打开时和窗口重新获得焦点时,会再次读取这两项信息 |
Expand Down
4 changes: 2 additions & 2 deletions services/core/cmd/server/managed_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,11 @@ func (s *managedSetup) configuration(setup deployment.Setup) (execution.Prepared
if err != nil {
return execution.PreparedRuntimeDeployment{}, fmt.Errorf("%w: %v", execution.ErrExecutionUnavailable, err)
}
selected := &execution.RuntimeProvider{InstallationID: setup.InstallationID, ProviderKind: setup.Provider, Generation: setup.Generation, Mode: setup.Mode, AdmissionPaused: setup.AdmissionPaused,
selected := &execution.RuntimeProvider{InstallationID: setup.InstallationID, ProviderKind: setup.Provider, Generation: setup.Generation, Mode: setup.Mode,
CoreURL: s.runtimeAPI, BackendFingerprint: setup.BackendFingerprint, Provider: provider}
if setup.Suspension != nil {
selected.Suspension = &execution.RuntimeSuspensionPolicy{IdleTimeout: time.Duration(setup.Suspension.IdleSeconds) * time.Second,
Retention: time.Duration(setup.Suspension.RetentionSeconds) * time.Second, MaxActive: 4, MaxRetained: 16}
Retention: time.Duration(setup.Suspension.RetentionSeconds) * time.Second}
}
return execution.PreparedRuntimeDeployment{Config: selected, Publish: s.publish}, nil
}
Expand Down
4 changes: 2 additions & 2 deletions services/core/cmd/server/managed_setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,9 @@ func TestManagedSetupPreparesWithoutPublishing(t *testing.T) {
t.Fatal("preparation published or lost candidate configuration")
}
committed := *candidate.Config
committed.Generation, committed.AdmissionPaused = 2, true
committed.Generation = 2
candidate.Publish(&committed)
if got := s.selected.Load(); got.Generation != 2 || got.Config.ProviderKind != "microsandbox" || !got.Config.AdmissionPaused {
if got := s.selected.Load(); got.Generation != 2 || got.Config.ProviderKind != "microsandbox" {
t.Fatal("commit did not publish the validated selection")
}
}
Expand Down
2 changes: 0 additions & 2 deletions services/core/internal/api/core_error_catalog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ var nonAdministrationCodes = []string{
"environment_input_cancelled", "environment_input_expired", "model_provider_required", "sandbox_nodes_preparing", "turn_conflict",
// Machine routes for nodes and native installers.
"installation_authorization_invalid", "installation_unavailable", "invalid_node_credential", "sandbox_node_address_mismatch",
// Removal of the file-managed local node, which the process deployment path owns.
"runtime_local_node_configured",
}

// The shared catalog lists every code an administration caller (/core/v1 or
Expand Down
2 changes: 0 additions & 2 deletions services/core/internal/api/errors_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,6 @@ func writeSandboxError(w http.ResponseWriter, err error) bool {
writeError(w, http.StatusUnauthorized, "invalid_node_credential", "A valid sandbox node enrollment or node credential is required.")
case errors.Is(err, deployment.ErrNodeInUse):
writeError(w, http.StatusConflict, "runtime_node_in_use", "The sandbox node retains allocations, snapshots, reservations or pending cleanup.")
case errors.Is(err, deployment.ErrLocalNodeConfigured):
writeError(w, http.StatusConflict, "runtime_local_node_configured", "The local sandbox node is enabled in deployment configuration. Drain it with the previous release and remove its file-managed configuration before replacing it.")
case errors.Is(err, placement.ErrNodesPreparing):
writeError(w, http.StatusServiceUnavailable, "sandbox_nodes_preparing", "Sandbox nodes are preparing the requested Runtime.")
case errors.Is(err, placement.ErrNodeUnavailable):
Expand Down
1 change: 0 additions & 1 deletion services/core/internal/api/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ func TestConflictErrorsUseConflictType(t *testing.T) {
for err, code := range map[error]string{
deployment.ErrConflict: "sandbox_deployment_conflict",
deployment.ErrNodeInUse: "runtime_node_in_use",
deployment.ErrLocalNodeConfigured: "runtime_local_node_configured",
deployment.ErrNodeAddressMismatch: "sandbox_node_address_mismatch",
sessions.ErrEnvironmentUnavailable: "environment_unavailable",
execution.ErrEnvironmentInputExpired: "environment_input_expired",
Expand Down
2 changes: 1 addition & 1 deletion services/core/internal/api/placement_errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestPlacementErrorsKeepTheirResponses(t *testing.T) {
code, message string
}{
{placement.ErrResetAdmission, http.StatusServiceUnavailable, "sandbox_reset_in_progress", "A sandbox reset is in progress."},
{fmt.Errorf("%w: sandbox creation is paused for provider maintenance", placement.ErrAdmissionClosed), http.StatusConflict, "environment_unavailable", "The environment is no longer available for new input."},
{fmt.Errorf("%w: sandbox installation does not match deployment", placement.ErrAdmissionClosed), http.StatusConflict, "environment_unavailable", "The environment is no longer available for new input."},
{placement.ErrPublicURLUnreachable, http.StatusConflict, "sandbox_configuration_error", placement.ErrPublicURLUnreachable.Error()},
{placement.ErrNodesPreparing, http.StatusServiceUnavailable, "sandbox_nodes_preparing", "Sandbox nodes are preparing the requested Runtime."},
{placement.ErrNodeUnavailable, http.StatusServiceUnavailable, "runtime_node_unavailable", "The selected sandbox node is unavailable or has no capacity."},
Expand Down
Loading
Loading