已开启
[Feature]: sandbox_http 适配 ADX yaml 契约接口设计方案 #9
WanYihui创建于  9月28日
WanYihui
9月28日 创建

Paired: GitHub #15 ↔ GitCode #9

sandbox_http 适配 ADX yaml 契约接口设计方案(agentos 分支)

背景描述

executor 的既有沙箱 HTTP 服务由 sandbox_http.py 提供(前缀 /v1/sandbox/sandboxes,7 个路由:create、delete、execute、files_read、files_write、files_list、files_search),底层为 yr RPC(SandboxManager → Sandbox → SandboxInstance,沙箱内 native Python I/O),仅回环、无凭证。与两份契约对比的结论:create/delete 对应管理面端点但出入参/状态码不一致;execute 与 files 4 项能力以 invoke action 形态提供;files/search 由契约 file.search action 承载。

设计思路

适配策略(P1/P2)与耦合约束

executor 基线(executor/ 全套源码与 tests/executor/ 测试基线)已在 agentos 分支存在,适配改造落在 agentos 分支;契约取自上游 master 的 platform/api/openapi/(file.search 已收录最新版)。外部依赖仅一个:yr 运行时 SDK(import yr,部署前提,代码不解决)。

  • P1 适配层:
    • sandbox_http.py:yaml 三端点全套 + 字段接通 + 旧路由不落地(SANDBOX_ROUTES/legacy dispatch/405 判定/PUT raw-body 读取不进入最终形态)
    • http_server.py:5 个方法分发点统一走 sandbox_api_kind() 前缀判定;_write_json 不改(复用既有 extra_headers 能力)
    • tests/executor/test_sandbox_yaml_api.py 新建(三端点契约行为 + 字段接通 + 旧前缀 404 回归)
    • test_http_server.py 移除 legacy 路由用例,保留文件/exec/mkdir/chunked 等非沙箱用例(个别通用用例改走新端点)
  • P2 契约侧:无契约改动——上游 master 的 data-plane.yaml 已收录 file.search(action 枚举 + FileSearchArgs/FileSearchResult/FileSearchRequest 完整 schema),本方案按该契约适配

路由收敛

7 路由收敛为 3 个 yaml 端点:POST /api/environment/v1/environments(create)、DELETE /api/environment/v1/environments/{environmentId}(delete)、POST /direct/{environmentId}/invoke(execute 与 files 系列含 files/search 合并为单端点 action 分发,分发模型与 execd normalize_sandbox_action 归一化表一致,dispatch.rs:399)。旧前缀路由不保留,/v1/sandbox/sandboxes* 一律 404。删除范围为 URL 路由与 dispatch;_build_sandbox_options 保留并按契约接通。

适配层位置

所有契约差异的转换(字段改名、hex 编解码、Envelope 封装、错误模型、状态码)全部在 sandbox_http.py 适配层完成;sandbox_manager.py 零改动;sandbox.py 除命名实例 namespace 钉扎(见设计思路·直接接通)与 search_files 扩展(depth 剪枝参数 + 返回扫描条目计数)外,底层签名与返回结构不变。

认证与请求标识

requestId 承担幂等语义(同一逻辑操作重试须保持同一 ID):管理面用 X-Request-Id 头(缺省服务端生成 UUID;成功与错误响应均在响应头 echo,错误响应的生效 ID 同时落入 ErrorEnvelope 的 error.requestId);数据面用 X-ADX-Request-ID 头或 body requestId(优先级:头 > body > 服务端生成)。trace_id 走 X-Trace-ID 头(私有约定,契约外——header 不受 body 的 additionalProperties: false 约束):适配层读取后传底层 RPC 追踪,并复用于 agent 进出日志(追踪统一走该头);不参与幂等。服务维持 loopback-only(非回环 403),不实现凭证校验(原生 API Server 的认证不适用于本机场景;若未来对外暴露需补认证)。

响应与错误模型

  • 管理面(create/delete)成功响应统一 Envelope,data 为 base64 编码的各操作结果 JSON:
{"code": 200, "message": "ok", "data": "<base64(JSON 业务结果)>"}
  • 管理面全部错误统一 ErrorEnvelope(响应头同样回显 X-Request-Id),符号码映射:
{"code": <HTTP 状态码>, "message": "...", "data": null,
 "error": {"code": "<符号码>", "retry": "...", "outcome": "...", "requestId": "..."}}
HTTP error.code retry outcome
400 invalid never not_started
404 not_found never terminal
405 method_not_allowed never not_started
413 too_large never not_started
500 internal after_backoff unknown
  • 数据面(invoke)返回 raw action JSON、无 envelope;action 级失败(如文件不存在)返回 HTTP 200,错误在 body 内;400/404/405/413/500 的错误体为 {"error": "..."}(405 保留 Allow 头);429(file.search 结果超限)错误体为 {entries, error_code, error} 失败形态(见 API-3)。
  • 405 分支(管理面与数据面)及未注册方法(PATCH/HEAD/OPTIONS/TRACE/CONNECT)不读请求体:写响应后断开 keep-alive,防同连接复用时下一请求读到上一请求的 body 残留导致协议错位。
  • 404/405 为 HTTP 标准行为且落在 yaml "4XX" 通配响应内。413/512MB 为本服务的体积上限(契约外):本服务仅监听回环、调用方直连,该上限直接生效。

create 请求体接通(13 个契约字段)

  • 私有字段收口:3 个业务私有字段(upstream、working_dir、user)经 extra_config 携带——适配层提取三键映射到底层 SandboxCreateOptions 顶层字段(三键在底层各有专门处理:upstream→反向隧道 :995、working_dir→runtime_env :934、user→host_user :912),剩余键经底层现成的 custom_extensions["extra_config"] 通路(:936)透传后端;执行器类型由契约顶层 runtime 字段承载(runtime→底层执行器选择 :906);body 顶层出现 sandbox_type/upstream/working_dir/user/trace_id,或 extra_config 内出现 sandbox_type 时返回 400 并提示正确位置(sandbox_type→runtime,upstream/working_dir/user→extra_config,trace_id→X-Trace-ID 头)。
  • 直接接通:name → SandboxCreateOptions.name → opt.name(:921)。命名实例的后续 invoke 一律按 {ns}-{name} 寻址(libruntime 由 functionMeta 的 ns 组合目标 id),而 create 侧仅在 invoke options 携带 namespace 时才给 designated id 加前缀——故适配层须在命名创建时钉住 opt.namespace(:926,取运行时配置的 namespace,取不到回退平台默认值 yr_defalut_namespace),保证注册名与寻址名一致。idleTimeoutSeconds → SandboxCreateOptions.idle_timeout(校验非负、允许 0;idle_timeout 为兼容别名,主名优先)。
  • 翻译接通(适配层翻译规则):
    • rootfs:字符串直传 SandboxCreateOptions.rootfs(底层 :938;字符串内容归调用方,同时携带 mounts 返回 400);对象由适配层组装为 rootfs JSON 后以字符串传入(子键原样保留透传后端,mounts 并入);仅传 image 时维持现有底层组装路径(:940-949)。
    • mounts:契约 mounts 数组原样并入 rootfs JSON 的 mounts 键(与底层 host_dir 组装结构位一致,:944-948)随 rootfs 透传后端;本机不做挂载动作;仅对象形态 rootfs 或 image 可携带。
    • snapshotId:适配层分流——存在时改走 Sandbox(checkpoint_id=snapshotId)(既有 restore 链路,sandbox.py:832)→ get_instance_id() → sandbox_manager.register()(既有登记入口,底层与 manager 零改动);此时其余 create 参数忽略。
    • tunnel:proxyPort → SandboxCreateOptions.proxy_port;enabled: true 须搭配 extra_config.upstream(本机隧道由 upstream 驱动)否则 400;enabled: false 显式关断;缺省由 upstream 自然驱动。

涉及到的对外API

接口修改汇总表("原接口"列为现行接口到契约接口的映射)

原接口(现行) 对应接口 修改点
POST /v1/sandbox/sandboxes POST /api/environment/v1/environments URL 变更;新增 X-Request-Id 头(生成/echo);请求体收口为契约形态——私有 3 字段(upstream/working_dir/user)经 extra_config 携带、执行器类型由顶层 runtime 字段承载、trace 走 X-Trace-ID、接通 name/rootfs/mounts/snapshotId/tunnel/idleTimeoutSeconds;成功响应 Envelope 化;错误全量 ErrorEnvelope 化;SSE 不实现(遗留)
DELETE /v1/sandbox/sandboxes/{id} DELETE /api/environment/v1/environments/{environmentId} URL 与路径参数名变更;X-Request-Id 头;not-found 从 200+success:false 改为 404 + ErrorEnvelope;成功响应 Envelope 化
POST /v1/sandbox/sandboxes/{id}/execute POST /direct/{environmentId}/invoke(action=process.exec,process.run 同义) URL/请求方式/参数位置重构(body 顶层→args);cmd/cwd/envs 与 command/working_dir/env 为契约并列字段(同报时前者优先,见 API-3 action-1;cmd 数组形态直执行为契约外扩展);timeout 校验放宽为 ≥0;返回 returncode→exit_code;action 级失败改 HTTP 200 + body error
GET .../files/read POST /direct/{environmentId}/invoke(action=file.read 整读) URL/请求方式重构(query→args);mode→binary;二进制编码 base64→hex;响应改为 {data, error}
PUT .../files/write POST /direct/{environmentId}/invoke(action=file.write 整写) URL/请求方式重构(raw body→args.data);mode→binary(追加模式 a/ab 由 append 参数承接,见 action-3);编码 base64→hex;空内容由原 400 放宽为可写空文件;响应改为 {name, path, type, size, error}
GET .../files/list POST /direct/{environmentId}/invoke(action=file.list) URL/请求方式重构;recursive/max_depth(默认 20,仅递归时生效)→depth(默认 1);include_files/include_dirs 无对应参数(取消);条目结构对齐 execd(type 类别化、modified_time 转 unix 秒、permissions 恒 null——py 底层无权限数据)
GET .../files/search POST /direct/{environmentId}/invoke(action=file.search) 调用方迁移至 invoke(file.search)(参数见 API-3 action-5);契约已收录完整 schema,按最新契约适配

API-1 创建环境:POST /api/environment/v1/environments

请求头:

参数 位置 必填 默认值 说明
X-Request-Id header 否 服务端生成 UUID;成功与错误响应均 echo 回响应头 请求标识(幂等键:重试须同 ID + 同 body)
X-Trace-ID header 否 "" 链路追踪(私有约定,契约外):适配层读取后传底层 RPC,并复用于 agent 日志;格式 1–128 字符、限 A-Z a-z 0-9 _ - . :(底层校验)
Content-Type header 是 无 application/json

请求体(13 个契约字段;其余 23 个平台侧字段不消费,见遗留):

参数 类型 必填 默认值 说明
name string 否 无 沙箱名称,直通底层;适配层同时钉住 namespace(见设计思路·直接接通)
image string 否 无 镜像
runtime string 否 "" 运行时/执行器类型,适配层直通底层执行器选择;空串走底层默认执行器
rootfs string 或 object 否 无 根文件系统:字符串直传底层;对象 {type, imageurl, ...} 由适配层组装 rootfs JSON(mounts 并入);与 image 二选一
cpu int 否 无 CPU(millicores)
memory int 否 无 内存(MiB)
env map<string,string> 否 无 环境变量;指定后整体替换默认环境(不合并)
ports string[] 否 无 端口声明;元素格式(proto:port)由底层在创建期校验,无效元素随创建失败 500
idleTimeoutSeconds int ≥0 否 0 空闲观察窗(秒),0 关闭空闲回收;idle_timeout 为兼容别名(主名优先)
mounts Mount[] 否 无 挂载声明:适配层原样并入 rootfs JSON 的 mounts 键透传后端;本机不做挂载动作;须搭配对象形态 rootfs 或 image(字符串 rootfs 不接受 mounts)
snapshotId string 否 无 从快照创建:适配层分流到 Sandbox(checkpoint_id=...) + manager.register;此时其余参数忽略
tunnel object 否 无 {enabled, proxyPort}:proxyPort → 底层 proxy_port;enabled=true 须搭配 extra_config.upstream(否则 400);enabled=false 显式关断
extra_config object 否 无 契约定义的运行时自定义键通道:识别 upstream/working_dir/user 三键映射底层对应字段,剩余键透传后端

body 顶层出现 sandbox_type/upstream/working_dir/user/trace_id,或 extra_config 内出现 sandbox_type 时返回 400 并提示正确位置(sandbox_type→顶层 runtime,upstream/working_dir/user→extra_config,trace_id→X-Trace-ID 头)。

三键中 user 接受任意 Docker Config.User 形式(如 "1000"、"1000:1000"、"root"),仅 runtime="docker" 时生效;upstream 为本机反向隧道上游地址(host:port)。

请求示例:

POST /api/environment/v1/environments
X-Request-Id: req-create-013
X-Trace-ID: trace-abc
{"image": "ubuntu:22.04", "cpu": 2000, "memory": 4096, "ports": ["8080"], "idleTimeoutSeconds": 600,
 "runtime": "supervisor", "extra_config": {"working_dir": "/ws"}}

返回值示例(成功,HTTP 200,响应头 echo X-Request-Id;data 为 base64 编码的 JSON):

{"code": 200, "message": "ok",
 "data": "eyJlbnZpcm9ubWVudElkIjoic2J4LTEyMyIsInN0YXR1cyI6InJ1bm5pbmciLCJyZXF1ZXN0SWQiOiJyZXEtY3JlYXRlLTAxMyJ9"}
// data 明文:{"environmentId":"sbx-123","status":"running","requestId":"req-create-013"}

其中 environmentId 取底层 create 返回的 instance_id,status 恒为 "running",requestId 取本次请求 ID。

返回值示例(失败,HTTP 400,响应头同样 echo X-Request-Id):

{"code": 400, "message": "cpu must be an integer", "data": null,
 "error": {"code": "invalid", "retry": "never", "outcome": "not_started", "requestId": "req-create-013"}}

状态码:200 成功(Envelope);400/405/413/500 失败(ErrorEnvelope;405 断开 keep-alive)。


API-2 删除环境:DELETE /api/environment/v1/environments/{environmentId}

路径参数:environmentId(string,必填,即底层 instance_id)。请求头 X-Request-Id(可选,成功与错误响应均 echo)。无请求体。

请求示例:

DELETE /api/environment/v1/environments/sbx-123

返回值示例(成功,HTTP 200):

{"code": 200, "message": "ok", "data": "eyJzdWNjZXNzIjp0cnVlfQ=="}
// data 明文:{"success":true}

返回值示例(沙箱不存在,HTTP 404):

{"code": 404, "message": "sandbox sbx-123 not found", "data": null,
 "error": {"code": "not_found", "retry": "never", "outcome": "terminal", "requestId": "req-del-007"}}

状态码:200 成功(Envelope);404/500 及其余 4XX/5XX 失败(ErrorEnvelope)。


API-3 运行时动作分发:POST /direct/{environmentId}/invoke

统一承载原 execute / files_read / files_write / files_list / files_search。响应为 raw action JSON、无 envelope;action 级失败返回 HTTP 200、错误在 body。各 action 的 args 参数以数据面契约的 per-action Args schema 为准(ExecArgs、FileReadArgs 等),下表与契约一致(file.search 的参数与错误形态见 action-5)。

公共参数:

参数 位置 必填 默认值 说明
environmentId path 是 无 环境实例 ID(即底层 instance_id)
X-ADX-Processing-Timeout-Ms header 否 无 平台侧参数:Ingress 统一处理超时,不透传到本服务
X-ADX-Request-ID header 否 见 requestId 请求标识(幂等键),优先级:头 > body.requestId > 服务端生成
X-Trace-ID header 否 "" 链路追踪(私有约定,契约外);格式 1–128 字符、限 A-Z a-z 0-9 _ - . :(底层校验)
action body 是 无 枚举(6 个):process.exec、process.run、file.read、file.write、file.list、file.search;契约其余 22 个 action 返回 400
args body 否 {} 各 action 参数,见下
requestId body 否 服务端生成 UUID 同 X-ADX-Request-ID 的 body 形式

action-1 process.exec / process.run(命令执行;两个 action 名同义——契约 ProcessExecRequest 共用同一请求/结果 schema,服务对二者行为完全一致):

args 参数 类型 必填 默认值 说明
cmd string 或 array<string> 与 command 二选一 无 命令;与 command 同报时优先。字符串(须非空)经沙箱内 /bin/sh -c 执行;数组不经 shell 直接执行(契约外扩展),空数组与元素非法由运行期判定、错误随 action 级失败走 200
command string 或 array<string> 否 无 命令的并列字段;与 cmd 同报时被其覆盖
cwd string 否 无 工作目录;与 working_dir 同报时优先;须已存在,本覆盖不创建目录(契约语义)
working_dir string 否 无 cwd 的并列字段
envs object 否 无 环境变量;与 env 同报时优先;指定后整体替换沙箱默认环境(不合并)
env object 否 无 envs 的并列字段
timeout number ≥0 否 无(不限) 超时秒数;契约语义:省略不限,0 为立即超时
{"action": "process.exec", "args": {"cmd": "ls -la", "cwd": "/ws", "timeout": 300}, "requestId": "invoke-001"}
→ HTTP 200 {"stdout": "total 4\n...", "stderr": "", "exit_code": 0}
// RPC 失败(仍 200):{"stdout": "", "stderr": "execute failed: ...", "exit_code": -1}

契约 ExecResult 含可选 exit_signal(POSIX 信号号);py 底层无信号数据,恒缺省(可选字段,契约合法,与 permissions 恒 null 同属两实现已知差异)。

action-2 file.read:

args 参数 类型 必填 默认值 说明
path string 是 无 文件绝对路径
binary bool 否 false true 时 data 为 hex 编码;false 为 UTF-8 文本
{"action": "file.read", "args": {"path": "/etc/hostname", "binary": false}}
→ HTTP 200 {"data": "host-123\n", "error": null}
// action 级失败(仍 200):{"data": null, "error": "No such file or directory"}

action-3 file.write:

args 参数 类型 必填 默认值 说明
path string 是 无 目标路径(自动创建父目录)
data string 是 无 内容(可为空串——写空文件);binary=true 时为 hex
binary bool 否 false 同 file.read
append bool 否 false 追加写(契约外扩展):true 时不截断原文件、追加到末尾,与 binary 组合映射底层 a/ab
{"action": "file.write", "args": {"path": "/ws/a.txt", "data": "hello", "binary": false}}
→ HTTP 200 {"name": "a.txt", "path": "/ws/a.txt", "type": "file", "size": 5, "error": null}

action-4 file.list(目录列表):

args 参数 类型 必填 默认值 说明
path string 是 无 目录路径
depth int 否 1(clamp 1–64) 递归深度(返回相对深度 ≤ depth 的条目)
{"action": "file.list", "args": {"path": "/ws", "depth": 2}}
→ HTTP 200 {"entries": [
    {"name": "a.py", "path": "/ws/a.py", "type": "file", "size": 1024,
     "permissions": null, "modified_time": 1695441203.5}], "error": null}

条目说明:permissions 恒为 null(py 底层条目无权限数据;execd 实现返回八进制权限串,属两实现的已知差异);modified_time 为 unix 秒(由底层本地时区格式化字符串转换);至多返回 10000 条、超出静默截断,不可读子目录跳过(底层既有行为)。depth 语义与 file.search 一致;适配层按底层计数口径换算(底层自根 0 层计数、current_depth > max_depth 才停止,故传 max_depth = depth - 1)。

action-5 file.search(递归文件搜索;契约已收录完整 schema,args 键必填):

args 参数 类型 必填 默认值 说明
path string 是 无 搜索根目录(≤4096 UTF-8 字节);根不存在或非目录返回空结果
pattern string 否 * 大小写敏感的文件名或完整路径 glob(≤1024 字节),支持 *、? 与字符类,* 也匹配路径分隔符
exclude_patterns string[] 否 [] 排除 glob 列表(≤64 条、各 ≤1024 字节),只过滤结果、不剪枝遍历
depth int 否 64(clamp 1–64) 递归深度上限(遍历相对深度 ≤ depth 的条目)
max_results int 否 1000(clamp 1–10000) 结果条数上限,超出返回 429
max_entries int 否 100000(clamp 1–1000000) 扫描条目上限;校验接受,py 底层不执行扫描上限,不触发 429
timeout_ms int 否 10000(clamp 1–30000) 搜索超时毫秒数;校验接受,py 底层无搜索超时机制,不触发 408

语义:不跟随子目录符号链接;结果按 path 排序;失败不返回部分结果。响应:成功 {"entries": [EntryInfo...], "scanned_entries": <遍历遇到的条目数,文件与目录、不含根,受 depth 约束>, "error": null};失败 {"entries": [], "error_code": "<符号码>", "error": "<描述>"},符号码枚举 INVALID_ARGUMENT/FILE_NOT_FOUND/NOT_A_DIRECTORY/PERMISSION_DENIED/FILESYSTEM_ERROR/RESOURCE_EXHAUSTED/DEADLINE_EXCEEDED——数量超限(max_results 超限,RESOURCE_EXHAUSTED)返回 HTTP 429;本服务实际产生的失败码为 FILESYSTEM_ERROR(RPC 失败,随 HTTP 200),契约枚举的其余符号码不产生(参数非法走传输级 400、根不存在/非目录返回空结果)。

实现:py 底层 search_files(sandbox/filesystem.py:fnmatch 递归、仅文件)按 depth 剪枝并统计扫描条目数(scanned:遍历遇到的文件与目录总数,不含根);pattern 双形态(文件名或含 / 的完整路径,匹配路径统一 posix 分隔)与 exclude 过滤、path 排序、max_results 超限判定(429)在适配层完成;条目结构同 file.list。py 实现与 execd 的已知差异:max_entries/timeout_ms 经校验接受,但 py 底层不执行扫描上限与搜索超时,不触发对应的 429/408(同 permissions 恒 null 的已知差异)。

{"action": "file.search", "args": {"path": "/ws", "pattern": "*.log", "exclude_patterns": ["tmp*"]}}
→ HTTP 200 {"entries": [
    {"name": "a.log", "path": "/ws/a.log", "type": "file", "size": 1024,
     "permissions": null, "modified_time": 1695441203.5}], "scanned_entries": 3, "error": null}
// action 级失败(仍 200):{"entries": [], "error_code": "FILESYSTEM_ERROR", "error": "search files failed: ..."}

invoke 状态码:200(含 action 级失败);400(未知/未支持 action、args 非法、JSON 格式错误);404(实例不存在、旧前缀 URL);405(带 Allow 头,断开 keep-alive);413(超 512MB);429(file.search 结果超 max_results,RESOURCE_EXHAUSTED);500——429 的错误体为 file.search 失败形态 {entries, error_code, error},其余错误体均为 {"error": "..."}。


本期不实现的 yaml 接口清单(yaml 有、本服务无;仅列出,不处理)

管理面 environment.yaml(11 个操作):capabilities、resources 列表、/api/instances 视图、pause、resume、reload、network、快照创建(env 维度)、快照列表(租户维度)、快照获取/删除。另 admin keys 与 global-scheduler 端点属平台管理域,不在本服务范围。

数据面 data-plane.yaml 端点(7 个):healthz、upload、upload/status、upload/commit、download、pty、/api/environment/v1/commands/watch。

invoke 其余 action(22 个):进程管理 9(process.start/get/poll/wait/kill/list/capabilities/stdin/send_stdin)、文件系统 8(file.exists/remove/rename/mkdir/make_dir/stat/read_chunk/write_chunk)、Shell 会话 4(shell.create/run/poll/close)、其他 1(entrypoint.poll)。

与其他模块的相关性描述

  • 原生链路(API Server / Coordinator / adxlet / sandboxd / execd):零依赖、并行共存——本服务不调它们的任何接口,也不被其发现(沙箱身份空间独立:yr instance_id vs Environment ID 目录)。后续整体替换时停用原生链路即可。
  • yr 运行时:唯一外部依赖——沙箱创建/操作全经 yr RPC(SandboxInstance.options().invoke 构造、read_file/write_file/list_files/search_files/execute.invoke、terminate);部署前提是运行环境 yr 可达。
  • http_server.py(host handler):5 个方法分发点统一走 sandbox_api_kind() 前缀判定接入新端点;_write_json 未改动(复用既有 extra_headers 能力,Envelope/错误分支在 sandbox_http 适配层)。
  • sandbox_manager.py(沙箱管理):零改动。sandbox.py(沙箱底层):签名与返回结构不变;字段接通复用底层既有参数(name、extra_config、rootfs、proxy_port)与 Sandbox(checkpoint_id=...) restore 链路、manager.register 登记入口;改动为命名创建时钉住 invoke namespace(见设计思路·直接接通)与 search_files 扩展 depth 剪枝及扫描计数(filesystem.py _search_files_impl 对应实现)。
  • 现有调用方:按汇总表迁移,破坏性变更逐点排查。
  • 构建体系:executor 自带 pyproject.toml/setup.py 独立构建;平台的 Rust workspace/deployment 不动。

测试设计与测试计划

单元测试:

  • 基线用例:test_file_handler、test_probe、test_process_manager、test_runtime 原样;test_http_server 保留文件/exec/mkdir/chunked 等非沙箱用例。
  • test_sandbox_yaml_api.py 新建:覆盖三端点路由与错误模型(405 Allow/404 判定、405 未读 body 断开 keep-alive 的连接复用回归)、create 字段校验与 X-Request-Id 生成/echo(含错误响应)、delete Envelope 与 404 ErrorEnvelope、invoke 6 action 行为(参数并列字段与同报优先级、cmd 数组形态直执行、hex 往返、append 追加写、条目转换与 file.list depth 边界、action 级失败 200、file.search 契约参数与错误形态——pattern 默认值/文件名与完整路径双形态匹配、exclude 只过滤、symlink 目录不跟随、max_results 超限 429、scanned_entries 遍历计数与深度剪枝、失败体 error_code)、旧前缀 404 回归与字段接通(runtime 映射、idleTimeoutSeconds 校验与 idle_timeout 别名、extra_config 三键映射、rootfs/mounts 组装与字符串 rootfs 携带 mounts 拒绝、snapshotId 分流、tunnel 校验、字段位置 400 判定(body 顶层 5 字段与 extra_config.sandbox_type)、X-Trace-ID 读取)。

集成测试计划:

  • yr 运行时真实联调:6 个 action 经 yr RPC 在真实沙箱内验证(create → exec/read/write/list/search → delete 全链路)。集成脚本套件按端点分四脚本(create/invoke-exec/invoke-files/delete)+ run-all 一键全量,另有 smoke 三端点串联冒烟。

其他信息

遗留问题(本期不做,特别标明):

  • create 的 SSE 流式响应:契约支持 Accept: text/event-stream,本服务恒返回 JSON(调用方文档声明);实现需将 _write_json 的一次性写出改造为长连接分帧写出,工作量独立成期。
  • 23 个平台侧字段不消费:namespace、user、group、cwd、cpu_limit、mem_limit、storageMb、storage_limit_mb、labels、idleAction、pauseOptions、restartPolicy、failover、inheritEntrypoint、readinessProbe、healthcheckProbe、network、dataPlane、scheduleAffinities、createTimeoutSeconds、scheduleTimeoutSeconds、initCallTimeoutSeconds、xpu——调度/准入/网络/探针/空闲策略类,属原生链路(API Server)职责,本服务不消费;后续仅补类型校验。其中 user/group 为 Conch 工具运行时身份(须成对,不能与 inheritEntrypoint/snapshotId 组合),cwd 为 execd 构造命令目录;沙箱执行身份走 extra_config.user(Docker Config.User)。
  • py 与 execd 的 file.search 实现差异:max_entries/timeout_ms 不触发对应 429/408(py 底层不执行扫描上限与超时),见 API-3 action-5;与 permissions 恒 null 同属两实现已知差异。

涉及文件列表(agentos 分支,按阶段):

  • P1 适配层改造:executor/src/yr/agentexecutor/sandbox_http.py(yaml 三端点 + 字段接通 + 旧路由不落地)、executor/src/yr/agentexecutor/http_server.py(分发点统一 sandbox_api_kind())、executor/src/yr/agentexecutor/sandbox/sandbox.py(命名创建钉住 invoke namespace;search_files 扩展 depth 与扫描计数)、executor/src/yr/agentexecutor/sandbox/filesystem.py(_search_files_impl 深度剪枝、扫描计数与 symlink 目录不跟随)、tests/executor/test_sandbox_yaml_api.py(新建)、tests/executor/test_http_server.py(剥离 legacy 用例)
  • P2 契约:无文件改动——file.search 已由上游 master 收录进 data-plane.yaml,服务侧按该契约对齐

依据文件:agentos 分支 executor/(基线源码与构建配置)、tests/executor/;上游 master platform/api/openapi/{environment.yaml, data-plane.yaml}(契约;environment.yaml 为管理面契约,file.search 已收录)与 platform/api/http/runtime-data.md(数据面运行时契约文档);platform/runtime/execd/src/runtime/{dispatch.rs, fs.rs}(invoke action 契约参照)。

likedislike
openJiuwen-bot成员
9月28日 评论:

欢迎来到 openJiuwen 社区

Hey @pikachuwan , 感谢你对社区的贡献.

机器人使用手册

有关指令的使用,可以点击 此处 查看详情。开发人员可以在每个PR或Issue下方评论特定指令来触发机器人任务。

likedislike
Oopenjiuwen-sync成员
9月28日 修改了issue 的描述
此处折叠了20条事件消息 查看更多
Oopenjiuwen-sync成员
5 天前 修改了issue 的描述