From ce291e32fe68e8258695438d277ecd6cc086bd9b Mon Sep 17 00:00:00 2001 From: anycodes <21079031+anycodes@users.noreply.github.com> Date: Sun, 18 Oct 2026 07:56:27 +0000 Subject: [PATCH 1/2] fix: make Node source installation and runtime example portable --- .agents/README.md | 2 + ...2026-10-18-portable-source-installation.md | 18 +++++++ ...2026-10-18-portable-source-installation.md | 50 +++++++++++++++++++ .github/workflows/ci.yml | 47 +++++++---------- CHANGELOG.md | 4 ++ README.md | 26 ++++++---- README.zh-CN.md | 24 +++++---- examples/README.md | 19 ++++++- examples/agent-loop.mjs | 7 --- test/agent-loop.test.mjs | 28 +++++++++-- 10 files changed, 166 insertions(+), 59 deletions(-) create mode 100644 .agents/evidence/verify/2026-10-18-portable-source-installation.md create mode 100644 .agents/notes/proposed/process/2026-10-18-portable-source-installation.md diff --git a/.agents/README.md b/.agents/README.md index a24651b..b830ca4 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -21,3 +21,5 @@ state. A local/runtime result is not cloud acceptance or package publication. - [Configuration reservations and installation](notes/proposed/process/2026-10-18-config-provider-reservations.md) - [Verification](evidence/verify/2026-10-18-config-provider-reservations.md) + +- [Portable source installation](notes/proposed/process/2026-10-18-portable-source-installation.md) · [verification](evidence/verify/2026-10-18-portable-source-installation.md) diff --git a/.agents/evidence/verify/2026-10-18-portable-source-installation.md b/.agents/evidence/verify/2026-10-18-portable-source-installation.md new file mode 100644 index 0000000..fda9023 --- /dev/null +++ b/.agents/evidence/verify/2026-10-18-portable-source-installation.md @@ -0,0 +1,18 @@ +# Verification: portable Node source installation + +- Date:2026-10-18 +- Result:PARTIAL; local actual-runtime/types/package/docs PASS; hosted gates pending. +- Base:9048f3b; SDK version0.1.0, Node22.22.2. + +Public npm metadata for @agent-sandbox/sdk returned404. New actual-runtime +regression before the fix:1 FAIL; child PYTHONPATH was prefixed with two fixed +workspace paths instead of preserving the explicit caller value. The original +real server loop otherwise completed; temporary wrapper/record were cleaned. +Fixed source now uses installed/runtime caller configuration with no new option. +After fix:npm ci/typecheck PASS; npm test10 PASS,zero failed/skipped, including +both actual Python HTTP/shell/file/process-cleanup scenarios. npm pack produced +agent-sandbox-sdk-0.1.0.tgz with22 files, built ESM/declarations/config JSON and +license/notices. Fresh application local-archive install and public imports PASS. +Changed Markdown local links/ADR format and workflow parse PASS. Configuration +JSON/client implementation unchanged. Hosted Node20/22 CI remains required. +No public package publication or cloud provider acceptance is claimed. diff --git a/.agents/notes/proposed/process/2026-10-18-portable-source-installation.md b/.agents/notes/proposed/process/2026-10-18-portable-source-installation.md new file mode 100644 index 0000000..59c506c --- /dev/null +++ b/.agents/notes/proposed/process/2026-10-18-portable-source-installation.md @@ -0,0 +1,50 @@ +# Decision: Provide a usable Node source package and portable runtime example +Status: proposed + +## Problem + +The public npm package returns404 while docs instruct its installation. The +example prepends fixed development-machine Python paths. No PR CI validates it. +https://github.com/Alibaba-Agent-Sandbox/nodejs-sdk/issues/4 + +## Decision + +Document locked source build/pack/consumer install in both languages. Preserve +caller PYTHONPATH and use an installed Python server. Add a real-runtime +regression and Node20/22 CI with an immutable server source. Clarify that exported +config names do not configure ServerClient or enable builtin retry policies. + +## API Design + +SDK exports, configuration JSON/names/defaults and package version are unchanged. +The example no longer prepends fixed paths; caller-controlled sources and +installed distributions are the supported resolution paths. No package publish. + +## Alternatives considered + +- Keep the npm command as presently available: public metadata returns404. +- Replace one fixed workspace root with another: remains environment-dependent. +- Test only mocked HTTP: omits the actual runtime/example and cleanup. + +## Dependencies + +Pinned Python server e86cb0be24e0083a27396b260298e7cf27b660d6: +https://github.com/Alibaba-Agent-Sandbox/python-sdk/pull/57. +Retry configuration contract: +https://github.com/Alibaba-Agent-Sandbox/python-sdk/issues/60. +Website installation guidance is independently maintained in +https://github.com/Alibaba-Agent-Sandbox/website/pull/2. + +## Test Strategy + +Regression fails before the fix by observing the actual spawned Python process +environment, then passes with real health/shell/files/cleanup. Run types/all +Node tests, npm pack and a fresh consumer import, workflow parse and docs links. +Hosted Node20/22 must pass; a local runtime test is not cloud provider proof. + +## Acceptance criteria + +- Documented source archive installs and imports in a fresh Node application. +- Explicit Python source configuration remains intact; actual example passes. +- Both CI versions run against the pinned real runtime without cloud secrets. +- Bilingual docs and accurate owner evidence accompany the change. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1710fcd..9d0ef18 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: CI +name: Node SDK CI on: pull_request: @@ -10,7 +10,6 @@ permissions: jobs: test: - name: Node.js ${{ matrix.node }} runs-on: ubuntu-latest strategy: fail-fast: false @@ -18,36 +17,26 @@ jobs: node: [20, 22] steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node }} - cache: npm + persist-credentials: false + - uses: actions/checkout@v4 + with: + repository: Alibaba-Agent-Sandbox/python-sdk + ref: e86cb0be24e0083a27396b260298e7cf27b660d6 + path: vendor/python-sdk + persist-credentials: false - uses: actions/setup-python@v5 with: - python-version: "3.12.15" - - name: Check out the matching Python server source - uses: actions/checkout@v4 + python-version: "3.12" + - run: python -m pip install ./vendor/python-sdk/server-sdk + - run: python -c "import agent_sandbox.server" + - uses: actions/setup-node@v4 with: - repository: Alibaba-Agent-Sandbox/python-sdk - ref: 145341d462df2f4753c03a62365114f1872acd1a - path: python-sdk - - name: Install the local server prerequisite - run: python -m pip install ./python-sdk/server-sdk - - name: Compare the complete Python and Node configuration schema - run: | - python - <<'PY' - import json - import subprocess - import sys - from pathlib import Path - - exported = subprocess.check_output( - [sys.executable, "python-sdk/client-sdk/src/agent_sandbox/utils/config_keys.py", "--json"], - text=True, - ) - assert json.loads(exported) == json.loads(Path("src/config-keys.json").read_text()) - print("Complete Python/Node configuration schema matches") - PY + node-version: ${{ matrix.node }} + cache: npm - run: npm ci - run: npm run typecheck - - run: npm test + - name: Test HTTP, configuration and the real Python runtime loop + run: npm test + - name: Verify the package includes built ESM and type exports + run: npm pack --dry-run diff --git a/CHANGELOG.md b/CHANGELOG.md index aacb6a0..8fd97ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ ### Fixed +- Preserve caller Python source configuration in the local runtime example; document usable source-package installation and validate real runtime behavior on Node20/22 CI. Clarify configuration-name exports and retry compatibility. + +### Fixed + - Match the Python configuration schema by leaving the installable `e2b` catalog provider id unreserved while preserving all built-in/core reservations. - Explain the current source-checkout installation path in English and Chinese; diff --git a/README.md b/README.md index 2b27721..5fa919a 100644 --- a/README.md +++ b/README.md @@ -13,24 +13,28 @@ It is **not** a port of `Sandbox.create`, cloud providers, or the server SDK. Cr ## Install -`@agent-sandbox/sdk` is not published to public npm yet (package metadata returned -HTTP 404 on 2026-10-18). Build the `dist/` entry from a checkout: +On2026-10-18 the public npm metadata endpoint for `@agent-sandbox/sdk` returned404. +Build the source package until the matching release is available on your registry: ```bash git clone https://github.com/Alibaba-Agent-Sandbox/nodejs-sdk.git cd nodejs-sdk npm ci npm run build +npm pack ``` -To use the package-name imports below from another project, install that built -checkout as a local dependency with `npm install ../nodejs-sdk` (adjust the path). -The public command `npm install @agent-sandbox/sdk` applies after publication. +This checkout's version0.1.0 produces `agent-sandbox-sdk-0.1.0.tgz` with built +`dist/` exports. In your application, install that generated archive: -For validation, run `npm run typecheck` and `npm test`. The latter builds first, -then runs `node:test`, including a real local server loop. That loop requires -`agent-sandbox-server` in the `python3` environment; it needs no cloud credentials. -The PR workflow checks Node.js 20 and 22 with a pinned Python server source. +```bash +npm install /absolute/path/to/agent-sandbox-sdk-0.1.0.tgz +``` + +When the release exists on your configured registry, `npm install @agent-sandbox/sdk` +uses the same import surface. No publication is implied by this source recipe. +For local development, `npm test` builds and runs `node:test`; the real local +example needs the Python server installed as described in [examples](examples/README.md). ## ServerClient @@ -62,6 +66,8 @@ await server.close(); ## Configuration names +The table exports configuration names. `readEnv` only reads a value; it does not apply these fields to `ServerClient`. Legacy retry fields do not enable builtin SDK HTTP retries. See the [shared compatibility decision](https://github.com/Alibaba-Agent-Sandbox/python-sdk/issues/60). + `src/config-keys.json` is the canonical list, exported from `python -m agent_sandbox.utils.config_keys --json`. Each key has exactly one environment variable. There are no aliases. `readEnv` reads only that name. `configSchema.reserved_provider_ids` follows the @@ -89,7 +95,7 @@ readEnv("sandbox_api_key", process.env); | `sandbox_domain` | core | `ABX_SANDBOX_DOMAIN` | Envd data-plane domain | | `http_timeout` | core | `ABX_HTTP_TIMEOUT` | HTTP request timeout in seconds | | `http2` | core | `ABX_HTTP2` | Use HTTP/2 | -| `max_retries` | core | `ABX_MAX_RETRIES` | Maximum automatic retries | +| `max_retries` | core | `ABX_MAX_RETRIES` | Legacy configuration name; no HTTP retry policy | | `max_connections` | core | `ABX_MAX_CONNECTIONS` | | | `max_keepalive_connections` | core | `ABX_MAX_KEEPALIVE_CONNECTIONS` | | | `keepalive_expiry` | core | `ABX_KEEPALIVE_EXPIRY` | | diff --git a/README.zh-CN.md b/README.zh-CN.md index 4e2f6c7..7e225a0 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -13,23 +13,27 @@ ## 安装 -`@agent-sandbox/sdk` 尚未发布到公共 npm(2026-10-18 查询包元数据返回 HTTP 404)。 -请从独立检出的源码构建 `dist/` 入口: +2026-10-18 查询公开 npm 的 `@agent-sandbox/sdk` 元数据返回404。 +配置的包索引具备对应版本前,请从源码构建安装包: ```bash git clone https://github.com/Alibaba-Agent-Sandbox/nodejs-sdk.git cd nodejs-sdk npm ci npm run build +npm pack ``` -在其他项目中使用下方的包名导入示例时,先用 `npm install ../nodejs-sdk` -把构建好的检出目录作为本地依赖安装(请调整路径)。 -公共安装命令 `npm install @agent-sandbox/sdk` 适用于正式发布之后。 +当前源码版本0.1.0 生成 `agent-sandbox-sdk-0.1.0.tgz`,包含构建后的 `dist/` 导出。 +在应用项目中安装生成的归档: -验证时运行 `npm run typecheck` 和 `npm test`。后者先构建,再执行 `node:test`, -其中包含真实本地服务循环,需要 `python3` 环境已安装 `agent-sandbox-server`,不需要云凭据。 -新增 PR 工作流使用固定的 Python server 源码,检查 Node.js 20 和 22。 +```bash +npm install /absolute/path/to/agent-sandbox-sdk-0.1.0.tgz +``` + +配置的包索引具备对应发布后,可以使用 `npm install @agent-sandbox/sdk`,导入接口一致。 +本说明不代表包已经发布。开发时 `npm test` 会构建并运行 `node:test`;真实本地示例 +需要按[示例说明](examples/README.md)先安装 Python 服务端。 ## ServerClient @@ -61,6 +65,8 @@ await server.close(); ## 配置名 +此表导出配置名称;`readEnv` 只读取值,不会把这些字段自动应用到 `ServerClient`。旧重试字段不会启用内置 SDK HTTP 重试。见[共享兼容边界](https://github.com/Alibaba-Agent-Sandbox/python-sdk/issues/60)。 + `src/config-keys.json` 是规范列表,来自 `python -m agent_sandbox.utils.config_keys --json`。每个 key 只有一个环境变量,没有别名。`readEnv` 只读这个名字。 `configSchema.reserved_provider_ids` 遵循 @@ -88,7 +94,7 @@ readEnv("sandbox_api_key", process.env); | `sandbox_domain` | core | `ABX_SANDBOX_DOMAIN` | envd 数据面域名 | | `http_timeout` | core | `ABX_HTTP_TIMEOUT` | HTTP 请求超时(秒) | | `http2` | core | `ABX_HTTP2` | 启用 HTTP/2 | -| `max_retries` | core | `ABX_MAX_RETRIES` | 最大自动重试次数 | +| `max_retries` | core | `ABX_MAX_RETRIES` | 兼容保留的配置名称;不代表 HTTP 重试策略 | | `max_connections` | core | `ABX_MAX_CONNECTIONS` | | | `max_keepalive_connections` | core | `ABX_MAX_KEEPALIVE_CONNECTIONS` | | | `keepalive_expiry` | core | `ABX_KEEPALIVE_EXPIRY` | | diff --git a/examples/README.md b/examples/README.md index 23f219b..a1ccd8b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -7,7 +7,7 @@ npm run build node examples/agent-loop.mjs ``` -`PYTHONPATH` is `/workspace/python-sdk/server-sdk/src:/workspace/python-sdk/client-sdk/src`. The work directory is `/tmp/abx-agent-loop-node`. No cloud API key is used. The server uses its default endpoint set; `ABX_SERVER_ENABLED_GROUPS` is not set. +Original recorded environment: `PYTHONPATH` was `/workspace/python-sdk/server-sdk/src:/workspace/python-sdk/client-sdk/src`. The work directory is `/tmp/abx-agent-loop-node`. No cloud API key is used. The server uses its default endpoint set; `ABX_SERVER_ENABLED_GROUPS` is not set. Recorded from that command (exit 0). The port is chosen at runtime. @@ -32,3 +32,20 @@ step 4 kill ## 本地 agent 循环 上面的输出就是同一次真实运行的形状。脚本做的四步与 Python 版相同:健康检查、执行 `python3 -c 'print(2 + 2)'`、写入并读回 `result.txt`、发送 `SIGTERM`。这不是 `Sandbox.create`。 + +## Portable runtime setup / 可移植运行环境 + +The current example uses an installed Python runtime and preserves caller +`PYTHONPATH`; it adds no fixed repository paths. Install the server before running: + +```bash +python3 -m pip install 'agent-sandbox-server @ https://github.com/Alibaba-Agent-Sandbox/python-sdk/archive/e86cb0be24e0083a27396b260298e7cf27b660d6.tar.gz#subdirectory=server-sdk' +npm ci +npm test +node examples/agent-loop.mjs +``` + +当前示例使用已安装的 Python 服务端,并原样保留调用方 `PYTHONPATH`,不再添加固定仓库路径。 +先按上面的固定源码安装运行时。开发中的服务端也可通过调用方 `PYTHONPATH` 提供; +上方历史输出的环境路径仅记录原来的运行环境。该示例验证本地 HTTP、命令、文件与进程清理, +不代表云端 `Sandbox.create`。 diff --git a/examples/agent-loop.mjs b/examples/agent-loop.mjs index bc7500b..6f9d30d 100755 --- a/examples/agent-loop.mjs +++ b/examples/agent-loop.mjs @@ -15,11 +15,6 @@ import { fileURLToPath } from "node:url"; import { ServerClient } from "../dist/index.js"; -const PYTHONPATH = [ - "/workspace/python-sdk/server-sdk/src", - "/workspace/python-sdk/client-sdk/src", -].join(path.delimiter); - const DROPPED_ENV = [ "ABX_SERVER_TOKEN", "ABX_SERVER_POLICY", @@ -109,8 +104,6 @@ function childEnv(baseDir) { } for (const name of DROPPED_ENV) delete env[name]; env.ABX_SERVER_BASE_DIR = baseDir; - const prefix = [PYTHONPATH, env.PYTHONPATH ?? ""].filter(Boolean); - env.PYTHONPATH = prefix.join(path.delimiter); return env; } diff --git a/test/agent-loop.test.mjs b/test/agent-loop.test.mjs index 92ba314..8870db8 100644 --- a/test/agent-loop.test.mjs +++ b/test/agent-loop.test.mjs @@ -1,5 +1,7 @@ import assert from "node:assert/strict"; -import { spawn } from "node:child_process"; +import { execFileSync, spawn } from "node:child_process"; +import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; import path from "node:path"; import test from "node:test"; import { fileURLToPath } from "node:url"; @@ -27,11 +29,11 @@ function fromPythonRepr(literal) { return out; } -function runExample() { +function runExample(extraEnv = {}) { return new Promise((resolve, reject) => { const child = spawn(process.execPath, [path.join(root, "examples/agent-loop.mjs")], { cwd: root, - env: process.env, + env: { ...process.env, ...extraEnv }, }); let stdout = ""; let stderr = ""; @@ -50,6 +52,26 @@ function runExample() { }); } +test("agent loop preserves caller Python source configuration", { timeout: 60_000, skip: process.platform === "win32" }, async () => { + const directory = await mkdtemp(path.join(tmpdir(), "node-sdk-pythonpath-")); + const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`; + try { + const bin = path.join(directory, "bin"); + const source = path.join(directory, "explicit-source"); + await mkdir(bin); + await mkdir(source); + const record = path.join(directory, "pythonpath.txt"); + const python = execFileSync("python3", ["-c", "import sys;print(sys.executable)"], { encoding: "utf8" }).trim(); + await writeFile(path.join(bin, "python3"), `#!/bin/sh\nprintf '%s' "$PYTHONPATH" > ${quote(record)}\nexec ${quote(python)} "$@"\n`, { mode: 0o755 }); + const expected = [source, process.env.PYTHONPATH ?? ""].filter(Boolean).join(path.delimiter); + const result = await runExample({ PATH: bin + path.delimiter + process.env.PATH, PYTHONPATH: expected }); + assert.equal(result.code, 0, result.stdout + result.stderr); + assert.equal(await readFile(record, "utf8"), expected); + } finally { + await rm(directory, { recursive: true, force: true }); + } +}); + test("agent loop stdout contains 4\\n and the read text is answer=4\\n", { timeout: 60_000 }, async () => { const { code, stdout, stderr } = await runExample(); assert.equal(code, 0, stdout + stderr); From 9b7473743311d9b2985bc74a8492863e9e7bfc61 Mon Sep 17 00:00:00 2001 From: anycodes <21079031+anycodes@users.noreply.github.com> Date: Sun, 18 Oct 2026 08:18:46 +0000 Subject: [PATCH 2/2] ci: preserve complete shared schema verification --- .../2026-10-18-portable-source-installation.md | 5 +++++ .github/workflows/ci.yml | 15 +++++++++++++++ CHANGELOG.md | 3 --- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.agents/evidence/verify/2026-10-18-portable-source-installation.md b/.agents/evidence/verify/2026-10-18-portable-source-installation.md index fda9023..79a2194 100644 --- a/.agents/evidence/verify/2026-10-18-portable-source-installation.md +++ b/.agents/evidence/verify/2026-10-18-portable-source-installation.md @@ -16,3 +16,8 @@ license/notices. Fresh application local-archive install and public imports PASS Changed Markdown local links/ADR format and workflow parse PASS. Configuration JSON/client implementation unchanged. Hosted Node20/22 CI remains required. No public package publication or cloud provider acceptance is claimed. + +Final diff review retained the existing full Python/Node configuration-schema +comparison in the workflow (not only HTTP/runtime tests). The accepted immutable +Python revision exports a byte-equivalent parsed JSON table locally. The +intermediate workflow omission is corrected before merge; no gate is weakened. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d0ef18..b20c024 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,21 @@ jobs: python-version: "3.12" - run: python -m pip install ./vendor/python-sdk/server-sdk - run: python -c "import agent_sandbox.server" + - name: Compare the complete Python and Node configuration schema + run: | + python - <<'PY_SCHEMA' + import json + import subprocess + import sys + from pathlib import Path + + exported = subprocess.check_output( + [sys.executable, "vendor/python-sdk/client-sdk/src/agent_sandbox/utils/config_keys.py", "--json"], + text=True, + ) + assert json.loads(exported) == json.loads(Path("src/config-keys.json").read_text()) + print("Complete Python/Node configuration schema matches") + PY_SCHEMA - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fd97ee..ba89ca1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,6 @@ ### Fixed - Preserve caller Python source configuration in the local runtime example; document usable source-package installation and validate real runtime behavior on Node20/22 CI. Clarify configuration-name exports and retry compatibility. - -### Fixed - - Match the Python configuration schema by leaving the installable `e2b` catalog provider id unreserved while preserving all built-in/core reservations. - Explain the current source-checkout installation path in English and Chinese;