diff --git a/.agents/README.md b/.agents/README.md index acca394..90639ae 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -58,3 +58,5 @@ state. A local/runtime result is not cloud acceptance or package publication. - [Public runtime a4](notes/implemented/process/2026-10-25-public-runtime-a4.md) · [verification](evidence/verify/2026-10-25-public-runtime-a4.md). - [Coordinated public runtime a6](notes/proposed/process/2026-10-25-public-runtime-a6.md) · [verification](evidence/verify/2026-10-25-public-runtime-a6.md). + +- [Shared application authentication tests](notes/proposed/feature/2026-10-26-shared-application-auth-tests.md) · [verification](evidence/verify/2026-10-26-shared-application-auth-tests.md). diff --git a/.agents/evidence/verify/2026-10-26-shared-application-auth-tests.md b/.agents/evidence/verify/2026-10-26-shared-application-auth-tests.md new file mode 100644 index 0000000..3fd714b --- /dev/null +++ b/.agents/evidence/verify/2026-10-26-shared-application-auth-tests.md @@ -0,0 +1,21 @@ +# Verification: shared application authentication tests + +- Date: 2026-10-26 +- Result: PASS for the unchanged runtime and shared test contract +- Change: test fixture and ServerClient authHeaders regressions; runtime source unchanged. +- Environment: Node 22.22.2, Python 3.13; source client/server 0.4.0a6 in an isolated virtual environment. + +## Commands run + +- `npm ci --ignore-scripts --no-audit --no-fund` — PASS. +- `npm run typecheck` — PASS. +- `npm test` without the Python runtime on PATH — FAIL: two existing agent-loop tests could not import the server; 27 other tests passed. +- `npm test` with the isolated Python runtime on PATH — PASS: 29 tests including the real local server loop and all five common authentication vectors. + +## Cleanup + +Local server processes were stopped by their existing tests. No cloud resources created. + +## Not verified + +The canonical fixture is byte-identical to Python owner commit 98642ae6712a5017e187cae7952a2b3c819c1ece; CI checks that pinned copy. PR https://github.com/Alibaba-Agent-Sandbox/nodejs-sdk/pull/21 passed both required Node 20 and Node 22 jobs at f25a18ed41a30091f7029b864e621938f8b441db. Owner dependency https://github.com/Alibaba-Agent-Sandbox/python-sdk/pull/142 must land first; latest-head checks/review and merge are verified separately. This is not an npm publication or whole-template cloud claim. diff --git a/.agents/notes/proposed/feature/2026-10-26-shared-application-auth-tests.md b/.agents/notes/proposed/feature/2026-10-26-shared-application-auth-tests.md new file mode 100644 index 0000000..54dad9f --- /dev/null +++ b/.agents/notes/proposed/feature/2026-10-26-shared-application-auth-tests.md @@ -0,0 +1,38 @@ +# Decision: Verify the common application authentication contract + +Status: proposed + +## Problem + +The Node HTTP client, Python client and CLI had separately maintained authentication examples. CLI onboarding improvements should not add commands or configuration responsibilities to this library. + +## Decision + +Consume the Python-owned authentication test vectors and run them against ServerClient.authHeaders. Keep runtime implementation and public exports unchanged. Retain independent SDK installation. + +## API Design + +N/A — additive test fixtures only; constructor-based authentication remains unchanged. + +## Alternatives considered + +- Import the CLI implementation: unnecessary package coupling. +- Add interactive SDK configuration: outside the library's scope. + +## Dependencies + +Canonical source: https://github.com/Alibaba-Agent-Sandbox/python-sdk. Related CLI work: https://github.com/Alibaba-Agent-Sandbox/agent-sandbox/issues/189. + +## Test Strategy + +Run npm ci, typecheck, all Node tests and the real local Python server example. Verify fixture bytes against the pinned owner source in CI. + +## Acceptance criteria + +- Common authentication cases pass without changing gateway/application credential separation. +- SDK setup remains noninteractive and no lifecycle capability is added. +- Tests, source pin, owner evidence and required CI/review pass. + +## Evidence + +- [Verification](../../../evidence/verify/2026-10-26-shared-application-auth-tests.md). diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a335798..27c9299 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,9 +22,11 @@ jobs: - uses: actions/checkout@v4 with: repository: Alibaba-Agent-Sandbox/python-sdk - ref: v0.4.0a6 + ref: 98642ae6712a5017e187cae7952a2b3c819c1ece path: vendor/python-sdk persist-credentials: false + - name: Verify shared application authentication vectors + run: cmp test/fixtures/application-auth-vectors.json vendor/python-sdk/contracts/testing/application-auth-vectors.json - uses: actions/setup-python@v5 with: python-version: "3.12" diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fe3753..58774b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## [Unreleased] +### Tests + +- Consume common application authentication vectors without adding SDK setup commands or changing public exports. + + ### Documentation - Select the verified public Python a4 runtime and matching configuration-source tag; preserve prior a3 verification as history. / 本地示例与配置对照统一公开 Python a4,保留原 a3 验证记录。 diff --git a/README.md b/README.md index d1ea673..5e42b18 100644 --- a/README.md +++ b/README.md @@ -143,3 +143,6 @@ readEnv("sandbox_api_key", process.env); ## License Apache-2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE). + + +The application authentication regression cases are shared with the Python client and CLI from the Python SDK contract source. This library takes credentials through constructor options; it has no interactive setup command. diff --git a/README.zh-CN.md b/README.zh-CN.md index ed49f60..071c297 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -136,3 +136,6 @@ readEnv("sandbox_api_key", process.env); ## 许可 Apache-2.0。见 [LICENSE](LICENSE) 和 [NOTICE](NOTICE)。 + + +应用认证回归数据与 Python 客户端、CLI 共用同一份 Python SDK 契约来源。本库通过构造参数接收凭证,没有交互式配置命令。 diff --git a/test/fixtures/application-auth-vectors.json b/test/fixtures/application-auth-vectors.json new file mode 100644 index 0000000..bed64c6 --- /dev/null +++ b/test/fixtures/application-auth-vectors.json @@ -0,0 +1,52 @@ +{ + "version": 1, + "cases": [ + { + "id": "anonymous", + "headers": {}, + "expected": {} + }, + { + "id": "application", + "headers": {}, + "token": "placeholder-application", + "expected": { + "authorization": "Bearer placeholder-application", + "x-access-token": "placeholder-application" + } + }, + { + "id": "gateway", + "headers": { + "X-Gateway-Token": "placeholder-gateway" + }, + "expected": { + "x-gateway-token": "placeholder-gateway" + } + }, + { + "id": "separate-audiences", + "headers": { + "X-Access-Token": "placeholder-gateway" + }, + "token": "placeholder-application", + "expected": { + "authorization": "Bearer placeholder-application", + "x-access-token": "placeholder-gateway" + } + }, + { + "id": "case-insensitive-gateway", + "headers": { + "x-aCcEsS-tOkEn": "placeholder-gateway", + "X-Gateway-Token": "placeholder-extra" + }, + "token": "placeholder-application", + "expected": { + "authorization": "Bearer placeholder-application", + "x-access-token": "placeholder-gateway", + "x-gateway-token": "placeholder-extra" + } + } + ] +} diff --git a/test/server-client.test.mjs b/test/server-client.test.mjs index 76a5948..a650c0a 100644 --- a/test/server-client.test.mjs +++ b/test/server-client.test.mjs @@ -298,3 +298,13 @@ for (const mode of ["bearer-only", "authorization-stripped"]) { } finally {await client.close(); await server.close();} }); } + +// Canonical source: python-sdk/contracts/testing/application-auth-vectors.json. +const authVectors = JSON.parse((await import('node:fs')).readFileSync(new URL('./fixtures/application-auth-vectors.json', import.meta.url), 'utf8')); +for (const vector of authVectors.cases) { + test(`shared application authentication: ${vector.id}`, async () => { + const client = new ServerClient('https://example.invalid/owned', {token: vector.token, gatewayHeaders: vector.headers}); + assert.deepEqual(Object.fromEntries(Object.entries(client.authHeaders()).map(([key, value]) => [key.toLowerCase(), value])), vector.expected); + await client.close(); + }); +}