diff --git a/.agents/skills/contribute-api/SKILL.md b/.agents/skills/contribute-api/SKILL.md index d28bd9b23..1642815f1 100644 --- a/.agents/skills/contribute-api/SKILL.md +++ b/.agents/skills/contribute-api/SKILL.md @@ -1,6 +1,6 @@ --- name: contribute-api -description: Contribute a new NeMo Fabric public API surface safely, with Rust, CLI, Python, schema, adapter, and documentation parity in mind +description: Contribute a new NVIDIA NeMo Fabric public API surface safely, with Rust, CLI, Python, TypeScript, schema, adapter, and documentation parity in mind author: NVIDIA Corporation and Affiliates license: Apache-2.0 --- @@ -20,8 +20,8 @@ runtime or bindings. ## Default Guidance - Start from the shared Rust core behavior first -- Decide whether the CLI, PyO3 binding, Python SDK, type stubs, schemas, or - adapter contract must expose the new surface +- Decide whether the CLI, PyO3 binding, Python SDK, type stubs, schemas, or the + Python and TypeScript adapter-contract bindings must expose the new surface - Keep every affected public surface in parity - Update docs and examples in the same branch @@ -37,4 +37,5 @@ runtime or bindings. - `validate-change` - `review-doc-style` - `docs/python-sdk-contract.md` +- `schemas/SCHEMA.md` - `justfile` diff --git a/.agents/skills/maintain-ci/SKILL.md b/.agents/skills/maintain-ci/SKILL.md index cb9674392..131b9fe7e 100644 --- a/.agents/skills/maintain-ci/SKILL.md +++ b/.agents/skills/maintain-ci/SKILL.md @@ -29,6 +29,10 @@ reliability, or reproducibility. `actions/cache`. - Use lockfiles or dependency manifests to drive cache invalidation. - Keep deploy and publish permissions isolated to the jobs that need them. +- Publish the TypeScript contract from the dedicated + `publish_typescript.yml` workflow through the protected `npmjs` environment. + Grant `id-token: write` for npm trusted publishing, and do not provide an npm + write token that could mask an OIDC configuration failure. - Read both caller and callee when a workflow uses `workflow_call`. - Keep documentation publish and preview credentials isolated to the Fern docs workflow. @@ -42,8 +46,11 @@ reliability, or reproducibility. - `contents: read` is the default minimum for checkout-based build, test, docs, and packaging jobs. - `pull-requests: read` is required for PR metadata lookup jobs. -- `pages: write` and `id-token: write` should be limited to Pages deployment - jobs and any caller that invokes them through a reusable workflow. +- `pages: write` should be limited to Pages deployment jobs and any caller that + invokes them through a reusable workflow. +- `id-token: write` should be limited to jobs that exchange a GitHub OIDC token + with a protected deployment target, including Pages deployment and the + protected npm publication job. - For reusable workflows, the caller must grant every permission the called jobs require. The callee cannot elevate beyond what the caller provides. @@ -93,6 +100,8 @@ source instead of assuming local success proves remote success. - `.github/workflows/fern-docs.yml` - `.github/workflows/nightly-alpha-tag.yml` - `.github/workflows/publish_rust.yml` +- `.github/workflows/publish_typescript.yml` +- `scripts/ci/publish_typescript_package.py` - `.gitlab-ci.yml` - `RELEASING.md` - `Cargo.lock` diff --git a/.agents/skills/maintain-packaging/SKILL.md b/.agents/skills/maintain-packaging/SKILL.md index 66cd7e32b..1f073d46a 100644 --- a/.agents/skills/maintain-packaging/SKILL.md +++ b/.agents/skills/maintain-packaging/SKILL.md @@ -1,6 +1,6 @@ --- name: maintain-packaging -description: Maintain NeMo Fabric Rust and Python dependencies, package metadata, module paths, native artifacts, lockfiles, license evidence, and release-facing build surfaces +description: Maintain NVIDIA NeMo Fabric Rust, Python, and TypeScript dependencies, package metadata, module paths, native artifacts, lockfiles, license evidence, and release-facing build surfaces author: NVIDIA Corporation and Affiliates license: Apache-2.0 --- @@ -24,9 +24,13 @@ consumed outside the source tree. - Python package metadata in `python/pyproject.toml` - Native extension naming and placement under `python/src/nemo_fabric` - Dependency resolution in `Cargo.lock` and `uv.lock` +- TypeScript adapter-contract metadata and dependency resolution in + `typescript/adapter-contract/package.json` and `package-lock.json` - Documentation tooling metadata in `docs/package.json` and `docs/package-lock.json` - CI workflows, install commands, and example commands +- npm trusted publishing through `.github/workflows/publish_typescript.yml` and + the protected `npmjs` environment - `justfile` build, test, clean, and documentation recipes - Release tags, registry publication, and release-facing documentation in `RELEASING.md` @@ -59,12 +63,17 @@ commitment. `uv run --no-project python scripts/licensing/license_diff.py --base-ref origin/main` after updating manifests and lockfiles, then review added packages and license changes. +- For `typescript/adapter-contract/package-lock.json`, inspect the resolved + package entries and their `license` fields. The adapter-contract package must + keep an empty production dependency graph; build-only dependencies still + require permissive, recorded license evidence. - Regenerate the attribution files with the named pre-commit hooks instead of editing generated output: ```bash uv run pre-commit run --all-files attributions-rust uv run pre-commit run --all-files attributions-python + uv run pre-commit run --all-files attributions-node ``` The license diff is evidence for reviewers. Dependency approvers make @@ -78,6 +87,8 @@ compatibility decisions using the distribution and linkage context. - [ ] CI references the same package names as local workflows - [ ] Public packaging changes are reflected in release-facing docs - [ ] Workspace, Python, and lockfile versions remain aligned where required +- [ ] The TypeScript adapter-contract package version follows the workspace + release version without changing its independent wire contract version - [ ] The editable maturin build still produces `nemo_fabric._native` - [ ] New dependencies are necessary, maintained, and narrower than the viable alternatives @@ -96,6 +107,11 @@ compatibility decisions using the distribution and linkage context. - `uv.lock` - `docs/package.json` - `docs/package-lock.json` +- `typescript/adapter-contract/package.json` +- `typescript/adapter-contract/package-lock.json` +- `.github/workflows/ci_typescript.yml` +- `.github/workflows/publish_typescript.yml` +- `scripts/ci/publish_typescript_package.py` - `.github/workflows/ci_python.yml` - `.github/workflows/ci_rust.yml` - `.pre-commit-config.yaml` diff --git a/.agents/skills/update-project-version/SKILL.md b/.agents/skills/update-project-version/SKILL.md index bd8fb93d6..33df5e11e 100644 --- a/.agents/skills/update-project-version/SKILL.md +++ b/.agents/skills/update-project-version/SKILL.md @@ -1,6 +1,6 @@ --- name: update-project-version -description: Update the NeMo Fabric release version across Cargo, setuptools package metadata, internal Python dependency pins, integration metadata, and lockfiles. Use when bumping, synchronizing, or auditing NeMo Fabric package versions for a release. +description: Update the NVIDIA NeMo Fabric release version across Cargo, Python, and TypeScript package metadata, internal Python dependency pins, integration metadata, and lockfiles. Use when bumping, synchronizing, or auditing NeMo Fabric package versions for a release. author: NVIDIA Corporation and Affiliates license: Apache-2.0 --- @@ -20,7 +20,8 @@ pre-release or build-metadata variants used during packaging. ## Source Of Truth - `Cargo.toml` `[workspace.package].version` is the source of truth for the Rust - workspace and Python build versioning. + workspace and the release version stamped into Python and TypeScript package + metadata. - Keep `Cargo.toml` `[workspace.dependencies]` self-references aligned when the workspace version changes. - `python/pyproject.toml` is the exception among the Python projects: do not add @@ -36,6 +37,8 @@ pre-release or build-metadata variants used during packaging. - All `nemo-fabric-* == ` requirements in the root `pyproject.toml` optional dependencies. - Each adapter's `nemo-fabric-adapters-common == ` dependency. +- Keep `typescript/adapter-contract/package.json` and the root package entries in + its `package-lock.json` aligned with the Cargo SemVer release version. For a normal release, use the same `X.Y.Z` string everywhere. For a prerelease or build-metadata version, use valid Cargo SemVer in `Cargo.toml` and the @@ -45,15 +48,17 @@ versions rather than blindly copying incompatible syntax. ## Workflow -1. Read the current version from `Cargo.toml` and decide the exact Cargo and - Python target version strings. -2. Run `just set-version `. The recipe converts supported Cargo - SemVer prereleases to PEP 440 and updates: +1. Read the current version from `Cargo.toml` and decide the exact Cargo, + Python, and TypeScript target version strings. +2. Run `just set-version `. The recipe preserves the normalized + SemVer for Cargo and TypeScript, converts it to PEP 440 for Python, and + updates: - `Cargo.toml` `[workspace.package].version` - `Cargo.toml` `workspace.dependencies.nemo-fabric-core.version` - The root setuptools `project.version` and every `adapters/**/pyproject.toml` `project.version` - Every internal `nemo-fabric-*` exact-version requirement + - The TypeScript adapter-contract `package.json` and `package-lock.json` - `Cargo.lock` through Cargo metadata resolution - The root, runtime, and adapter `uv.lock` files through `just lock-python` 3. Confirm that `python/pyproject.toml` remains dynamic and unchanged. @@ -62,7 +67,8 @@ versions rather than blindly copying incompatible syntax. If editing the helper code, keep these contracts aligned: -- `set_project_version` must call the Cargo and Python project version helpers. +- `set_project_version` must call the Cargo, Python, and TypeScript project + version helpers. - `set_cargo_workspace_version` must update the workspace version and the `nemo-fabric-core` workspace dependency, then verify every `nemo-fabric-*` workspace package through Cargo metadata. @@ -70,6 +76,8 @@ If editing the helper code, keep these contracts aligned: adapter `pyproject.toml` discovered recursively under `adapters/`, and all internal exact-version pins while rejecting a static version in `python/pyproject.toml`. +- `set_typescript_project_version` must update the package manifest and both + root version entries in the npm lockfile without changing dependency versions. - The `set-version` recipe must run `just lock-python` after source metadata is updated. @@ -81,11 +89,14 @@ If editing the helper code, keep these contracts aligned: `rg -n '^version =|nemo-fabric-[a-z-]+ == ' pyproject.toml adapters --glob 'pyproject.toml'` - Confirm the runtime remains dynamic: `rg -n 'dynamic = \["version"\]' python/pyproject.toml` +- Inspect the TypeScript package and lockfile root versions: + `rg -n '"version":' typescript/adapter-contract/package{,-lock}.json` - Run `cargo check --workspace --locked`. - Run `just build-python` to verify all Python package metadata resolves. - Run `just test-python` when the integration version or Python packaging behavior changes materially. - Run `just wheels` for release-facing validation of every Python wheel. +- Run `just pack-typescript` to verify the stamped TypeScript package metadata. - Run `git diff --check`. ## Avoid @@ -94,6 +105,7 @@ If editing the helper code, keep these contracts aligned: - Adding a literal version to `python/pyproject.toml`; Maturin owns that version. - Updating Python package versions without their exact internal dependency pins. - Forgetting `Cargo.lock`, the root `uv.lock`, or per-project `uv.lock` files. +- Updating the TypeScript package manifest without its npm lockfile root entry. - Blind repository-wide replacement of version-like strings. ## References @@ -106,4 +118,6 @@ If editing the helper code, keep these contracts aligned: - `python/uv.lock` - `adapters/**/pyproject.toml` - `adapters/**/uv.lock` +- `typescript/adapter-contract/package.json` +- `typescript/adapter-contract/package-lock.json` - `justfile` diff --git a/.agents/skills/validate-change/SKILL.md b/.agents/skills/validate-change/SKILL.md index 38c73e908..e511b0bc1 100644 --- a/.agents/skills/validate-change/SKILL.md +++ b/.agents/skills/validate-change/SKILL.md @@ -23,6 +23,8 @@ surfaces touched by a change. test pass. - If Rust code changed, run `cargo fmt --all -- --check` and `just test-rust`. - If Python code or a Python-facing adapter changed, run `just test-python`. +- If the TypeScript adapter contract or one of its source schemas changed, run + `just test-typescript`. - If `crates/fabric-core` changed in a way exposed through Python, run both the Rust and Python suites. - If the PyO3 bridge or package metadata changed, run `just build-python` and @@ -30,10 +32,14 @@ surfaces touched by a change. - If public configuration types changed, confirm the schema snapshot tests in `just test-rust` pass and review generated schema diffs. - If an adapter or integration changed, run its focused tests. -- If a manifest or lockfile changed, run +- If a Cargo or Python manifest or lockfile changed, run `uv run --no-project python scripts/licensing/license_diff.py --base-ref origin/main`, review the transitive license changes, then run the `attributions-rust` and `attributions-python` pre-commit hooks. +- If the TypeScript manifest or npm lockfile changed, inspect the complete npm + dependency tree and license fields, confirm the package still has zero + production dependencies, run the `attributions-node` pre-commit hook, and run + its package and audit checks. - If documentation or examples changed, run `just docs` when practical and verify documented commands against the current repository. - If code changes alter APIs, commands, paths, packaging behavior, telemetry @@ -55,8 +61,9 @@ surfaces touched by a change. - **Harbor integration changed** Run `tests/test_harbor_runner.py`, then `just test-python`. - **Schema or public contract changed** - Run both language suites and review changes under `schemas/` and generated API - references. + Run the Rust, Python, and TypeScript suites and review changes under + `schemas/`, the checked-in Python adapter-contract representations, generated + TypeScript sources, and generated API references. - **Documentation-only change** Use `contribute-docs` and `review-doc-style`. Run `just docs` for docs-site or generated-reference changes. @@ -69,6 +76,7 @@ surfaces touched by a change. ```bash just test-rust just test-python +just test-typescript ``` ## Common Targeted Commands @@ -85,6 +93,11 @@ just build-python just test-python uv run --no-sync pytest -k "" +# TypeScript adapter contract +just build-typescript +just test-typescript +just pack-typescript + # Documentation just docs @@ -92,6 +105,7 @@ just docs uv run --no-project python scripts/licensing/license_diff.py --base-ref origin/main uv run pre-commit run --all-files attributions-rust uv run pre-commit run --all-files attributions-python +uv run pre-commit run --all-files attributions-node # Justfile and patch hygiene just --fmt --check @@ -114,5 +128,7 @@ Before review or handoff: - Build and test recipes: `justfile` - Python CI: `.github/workflows/ci_python.yml` - Rust CI: `.github/workflows/ci_rust.yml` +- TypeScript CI: `.github/workflows/ci_typescript.yml` - Documentation CI: `.github/workflows/fern-docs.yml` - Public Python contract: `docs/python-sdk-contract.md` +- Public adapter contract: `schemas/SCHEMA.md` diff --git a/.github/workflows/ci_typescript.yml b/.github/workflows/ci_typescript.yml new file mode 100644 index 000000000..5755c94f7 --- /dev/null +++ b/.github/workflows/ci_typescript.yml @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: TypeScript + +on: + pull_request: + push: + branches: [main] + +concurrency: + group: ci-typescript-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + test: + name: Test (Node ${{ matrix.node-version }}) + runs-on: ubuntu-24.04 + timeout-minutes: 15 + permissions: + contents: read + strategy: + fail-fast: false + matrix: + node-version: ['20.18.3', '24'] + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 + with: + node-version: ${{ matrix.node-version }} + cache: npm + cache-dependency-path: typescript/adapter-contract/package-lock.json + + - name: Install just + uses: taiki-e/install-action@c070f87102a1c75b3183910f391c1cb887fe13c8 # v2.77.6 + with: + tool: just@1.50.0 + + - name: Test TypeScript contract package + run: just test-typescript diff --git a/.github/workflows/publish_typescript.yml b/.github/workflows/publish_typescript.yml new file mode 100644 index 000000000..0a9c67fcc --- /dev/null +++ b/.github/workflows/publish_typescript.yml @@ -0,0 +1,122 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Publish TypeScript package + +on: + push: + tags: + - 'v*' + - '!v*-alpha*' + +concurrency: + group: publish-typescript + cancel-in-progress: false + +defaults: + run: + shell: bash + +jobs: + publish-typescript: + name: Publish (npmjs.com) + # Stable, beta, and RC tags publish. Alpha tags remain artifact-only. + if: ${{ github.ref_type == 'tag' && !contains(github.ref_name, '-alpha') }} + runs-on: ubuntu-24.04 + timeout-minutes: 15 + permissions: + # actions/checkout needs read access to the tagged repository contents. + contents: read + # npm trusted publishing exchanges this job's GitHub OIDC token for a + # short-lived registry credential; no long-lived npm token is provided. + id-token: write + environment: npmjs + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 + with: + node-version: '24' + package-manager-cache: false + + - name: Install just + uses: taiki-e/install-action@c070f87102a1c75b3183910f391c1cb887fe13c8 # v2.77.6 + with: + tool: just@1.50.0 + + - name: Validate trusted publishing toolchain + run: | + set -euo pipefail + + node --version + npm_version="$(npm --version)" + echo "$npm_version" + NPM_VERSION="$npm_version" node <<'NODE' + const actual = process.env.NPM_VERSION.split('.').map(Number); + const minimum = [11, 5, 1]; + const comparison = actual.findIndex( + (part, index) => part !== minimum[index], + ); + + if ( + actual.length !== minimum.length || + actual.some(Number.isNaN) || + (comparison !== -1 && actual[comparison] < minimum[comparison]) + ) { + throw new Error('npm 11.5.1 or newer is required for trusted publishing'); + } + NODE + + - name: Prepare release metadata + id: release + env: + RELEASE_TAG: ${{ github.ref_name }} + run: | + set -euo pipefail + + version="$(python3 scripts/ci/normalize_release_tag.py "$RELEASE_TAG")" + if [[ "$version" == *+* ]]; then + echo "npm publication does not support build metadata: $version" >&2 + exit 1 + fi + + case "$version" in + *-alpha*) + echo "Alpha releases are not published to npm: $version" >&2 + exit 1 + ;; + *-beta*|*-rc*) dist_tag="next" ;; + *-*) + echo "Unsupported npm prerelease: $version" >&2 + exit 1 + ;; + *) dist_tag="latest" ;; + esac + + python3 scripts/ci/set_typescript_project_version.py "$version" + git diff --exit-code -- \ + typescript/adapter-contract/package.json \ + typescript/adapter-contract/package-lock.json + { + echo "version=$version" + echo "dist_tag=$dist_tag" + } >> "$GITHUB_OUTPUT" + + - name: Test package + run: just test-typescript + + - name: Publish package + env: + NPM_CONFIG_REGISTRY: https://registry.npmjs.org + RELEASE_VERSION: ${{ steps.release.outputs.version }} + RELEASE_DIST_TAG: ${{ steps.release.outputs.dist_tag }} + run: | + set -euo pipefail + python3 scripts/ci/publish_typescript_package.py \ + --package-directory typescript/adapter-contract \ + --version "$RELEASE_VERSION" \ + --dist-tag "$RELEASE_DIST_TAG" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 89886fde8..e8e0da5f2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: name: copyright header entry: python3 scripts/lint/check_copyright.py language: system - files: '\.(rs|py|pyi|toml|yaml|yml|md|mdx|sh)$|\.gitignore$' + files: '\.(rs|py|pyi|toml|yaml|yml|md|mdx|sh|mjs|ts)$|\.gitignore$' exclude: '(/SKILL\.md|node_modules/|target/|\.venv/|^\.github/pull_request_template\.md)$' # Python lint — enforce the flake8-bugbear cached-instance-method rule (B019) @@ -60,10 +60,17 @@ repos: files: '^(uv\.lock|ATTRIBUTIONS-Python\.md)$' pass_filenames: false + - id: attributions-node + name: ATTRIBUTIONS-Node.md (TypeScript package-lock.json) + entry: bash scripts/generate_attributions.sh node + language: system + files: '^(typescript/adapter-contract/package-lock\.json|ATTRIBUTIONS-Node\.md)$' + pass_filenames: false + - id: license-diff name: Dependency license diff entry: uv run --no-project python scripts/licensing/license_diff.py --base-ref HEAD language: system - files: '(^|/)(Cargo\.toml|Cargo\.lock|pyproject\.toml|uv\.lock)$' + files: '(^|/)(Cargo\.toml|Cargo\.lock|pyproject\.toml|uv\.lock)$|^typescript/adapter-contract/package(?:-lock)?\.json$' pass_filenames: false verbose: false diff --git a/AGENTS.md b/AGENTS.md index 3965df669..105ef2cf3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,7 +48,12 @@ These workflow notes keep public documentation, examples, and PR preparation ali with repository expectations. - Update user-facing entry points when public behavior, the `nemo-fabric` package (imported as `nemo_fabric`), examples, or supported bindings change: `README.md`, the Fern docs under `docs/` (navigation in `docs/index.yml`, site config in `fern/docs.yml`), and the adapter/integration READMEs (`adapters/*/README.md`, `python/src/nemo_fabric/integrations/*/README.md`, `examples/README.md`). -- Keep the Python/Rust binding contract current when the public API changes: `docs/sdk/python.mdx`, the JSON Schema notes in `schemas/SCHEMA.md`, the generated references under `docs/reference/api/`, and the integration skills under `skills/` (which restate public contracts and must be kept in parity). Regenerate docs with `just docs` after changing the docs site. +- Keep public bindings current when the API changes: `docs/sdk/python.mdx` for + the Python SDK; `adapter-contract/` and `typescript/adapter-contract/` for the + southbound adapter contract; the JSON Schema notes in `schemas/SCHEMA.md`; + the generated references under `docs/reference/api/`; and the integration + skills under `skills/` (which restate public contracts and must be kept in + parity). Regenerate docs with `just docs` after changing the docs site. - Keep release policy and the end-to-end maintainer workflow in `RELEASING.md`; keep packaging implementation guidance in `.agents/skills/maintain-packaging/SKILL.md`. Do not move release-history policy into user-facing docs or add a duplicate `CHANGELOG.md`. - Keep the stable public wrapper `scripts/generate_api_docs.sh` at the `scripts/` root in docs and examples. Reference namespaced helper paths under `scripts/docs/` only when documenting internal maintenance work. - Use branch prefixes for your work: `feat/`, `fix/`, `docs/`, `test/`, or `refactor/`. diff --git a/ATTRIBUTIONS-Node.md b/ATTRIBUTIONS-Node.md new file mode 100644 index 000000000..e464c58b3 --- /dev/null +++ b/ATTRIBUTIONS-Node.md @@ -0,0 +1,712 @@ + + + +# Third-Party Software Attributions (Node.js) + +This project uses the following third-party npm packages to build the TypeScript adapter contract. +Each package is open-source and licensed under the terms indicated below. + +The published `nemo-fabric-adapter-contract` package has no runtime dependencies. + +This file is automatically generated from **typescript/adapter-contract/package-lock.json** +Please do not edit it directly. Regenerate with `./scripts/generate_attributions.sh node` +## @apidevtools/json-schema-ref-parser - 11.9.3 +**Repository URL**: https://github.com/APIDevTools/json-schema-ref-parser +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +The MIT License (MIT) + +Copyright (c) 2015 James Messinger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` + +## @jsdevtools/ono - 7.1.3 +**Repository URL**: https://github.com/JS-DevTools/ono +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +The MIT License (MIT) + +Copyright (c) 2015 James Messinger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +. +``` + +## @types/json-schema - 7.0.15 +**Repository URL**: https://github.com/DefinitelyTyped/DefinitelyTyped +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +``` + +## @types/lodash - 4.17.25 +**Repository URL**: https://github.com/DefinitelyTyped/DefinitelyTyped +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +``` + +## argparse - 2.0.1 +**Repository URL**: https://github.com/nodeca/argparse +**License Type(s)**: Python-2.0 +### License: https://spdx.org/licenses/Python-2.0.html +``` +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. + +All Python releases are Open Source (see http://www.opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the Internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the Internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +``` + +## fdir - 6.5.0 +**Repository URL**: https://github.com/thecodrr/fdir +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +Copyright 2023 Abdullah Atta + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + +## is-extglob - 2.1.1 +**Repository URL**: https://github.com/jonschlinkert/is-extglob +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +The MIT License (MIT) + +Copyright (c) 2014-2016, Jon Schlinkert + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` + +## is-glob - 4.0.3 +**Repository URL**: https://github.com/micromatch/is-glob +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +The MIT License (MIT) + +Copyright (c) 2014-2017, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` + +## js-yaml - 4.3.1 +**Repository URL**: https://github.com/nodeca/js-yaml +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +(The MIT License) + +Copyright (C) 2011-2015 by Vitaly Puzrin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` + +## json-schema-to-typescript - 15.0.4 +**Repository URL**: https://github.com/bcherny/json-schema-to-typescript +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +Copyright 2022 Boris Cherny + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + +## lodash - 4.18.1 +**Repository URL**: https://github.com/lodash/lodash +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +Copyright OpenJS Foundation and other contributors + +Based on Underscore.js, copyright Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/lodash/lodash + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code displayed within the prose of the +documentation. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +Files located in the node_modules and vendor directories are externally +maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. +``` + +## minimist - 1.2.8 +**Repository URL**: https://github.com/minimistjs/minimist +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + +## picomatch - 4.0.5 +**Repository URL**: https://github.com/micromatch/picomatch +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +The MIT License (MIT) + +Copyright (c) 2017-present, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` + +## prettier - 3.9.6 +**Repository URL**: https://github.com/prettier/prettier +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +Copyright © James Long and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + +## tinyglobby - 0.2.17 +**Repository URL**: https://github.com/SuperchupuDev/tinyglobby +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +MIT License + +Copyright (c) 2024 Madeline Gurriarán + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +## typescript - 5.6.3 +**Repository URL**: https://github.com/microsoft/TypeScript +**License Type(s)**: Apache-2.0 +### License: https://spdx.org/licenses/Apache-2.0.html +``` +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS +``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8ac5816f9..6f6387f49 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,11 +19,12 @@ Install these tools before you start: - **Rust** (stable toolchain) -- install with [rustup](https://rustup.rs/) - **Python** >= 3.11 +- **Node.js** >= 20.18.3 with npm - **uv** -- follow the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/) - **just** >= 1.50.0 -- `cargo install just --locked` -Clone the repository, create a virtual environment, and build the Rust and -Python packages: +Clone the repository, create a virtual environment, and build the Rust, +Python, and TypeScript packages: ```bash git clone https://github.com/NVIDIA/NeMo-Fabric.git @@ -99,17 +100,25 @@ Follow the existing style in the Python SDK, adapters, examples, and tests. Use type annotations for public APIs and keep native binding declarations in sync with their Rust implementations. +### TypeScript + +Use strict TypeScript for the adapter-contract binding. Preserve the JSON wire +property names, run the checked-in generator instead of editing generated +declarations, and keep production dependencies out of the contract package. + ### General -Use the naming conventions appropriate to each language: Rust and Python use -`snake_case` for functions and variables, Rust types use `PascalCase`, and -Python classes use `PascalCase`. +Use the naming conventions appropriate to each language. Rust and Python use +`snake_case` for functions and variables. Rust, Python, and TypeScript types use +`PascalCase`. TypeScript contract properties preserve the wire `snake_case` +names. ## Testing **Run tests for every language surface affected by your changes.** If a change -touches the Rust core or public schemas, run both the Rust and Python suites -because the Python SDK and adapters depend on the native core contract. +touches the Rust core or public adapter-contract schemas, run the Rust, Python, +and TypeScript suites because both language bindings depend on the generated +wire contract. Run the affected test targets through the repository `justfile`: @@ -120,7 +129,10 @@ just test-rust # Python SDK, adapters, integrations, and examples just test-python -# Both suites +# TypeScript adapter contract +just test-typescript + +# All supported language surfaces just test-all ``` @@ -133,8 +145,9 @@ just no_uv=true test-all ``` When adding functionality, include tests in the corresponding Rust crate or in -the relevant area under `tests/`. Public contract changes must keep the checked-in -JSON Schema snapshots and native Python binding declarations synchronized. +the relevant area under `tests/`. Public contract changes must keep the +checked-in JSON Schema snapshots, Python representations, and generated +TypeScript declarations synchronized. ## Documentation Checklist diff --git a/README.md b/README.md index 5ff881a42..e8f5076c1 100644 --- a/README.md +++ b/README.md @@ -261,13 +261,16 @@ through adapters. Use the following reference to compare the integrations: - [Adapter compatibility and guides](adapters/README.md): compare bundled harness support, runtime ownership, telemetry integration, and package guides. +- [Adapter contract](docs/adapter-contract/README.md): build third-party + adapters against the canonical schemas or the dependency-free Python and + TypeScript contract bindings. ## Roadmap -- **Custom harnesses:** Publish the NeMo Fabric adapter contract so third-party - developers can build integrations that are compatible with NeMo Fabric. - Support integrations maintained by NeMo Fabric and compatible third-party - integrations. +- **Custom harnesses:** Publish the NeMo Fabric adapter contract as canonical + schemas and dependency-free language bindings so third-party developers can + build integrations that are compatible with NeMo Fabric. Support integrations + maintained by NeMo Fabric and compatible third-party integrations. - **Custom agents:** Support custom agents built on maintained or third-party harness integrations without requiring an additional, agent-specific adapter. Preserve the normalized NeMo Fabric lifecycle, results, artifacts, and diff --git a/RELEASING.md b/RELEASING.md index 8a43df09c..05da74f35 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -31,6 +31,7 @@ The release pipeline publishes these package surfaces from a tag push: | Ecosystem | Published Surface | |---|---| | crates.io | `nemo-fabric-core`, `nemo-fabric-cli` | +| npm | `nemo-fabric-adapter-contract` | | GitHub Actions | `nemo-fabric`, `nemo-fabric-runtime`, `nemo-fabric-adapters-common`, `nemo-fabric-adapters-claude`, `nemo-fabric-adapters-codex`, `nemo-fabric-adapters-deepagents`, and `nemo-fabric-adapters-hermes` wheel artifacts | | Fern | The documentation site | @@ -46,6 +47,10 @@ NeMo Fabric versions are anchored on the workspace SemVer in the repository root - The root `pyproject.toml` and every `adapters/**/pyproject.toml` carry the Python package versions and internal dependency pins and must stay aligned with the same release version. +- `typescript/adapter-contract/package.json` and its lockfile carry the npm + adapter-contract package version and must stay aligned with the same release + version. The package version is independent of the + `fabric.adapter/v1alpha2` wire contract version. - The `nemo-fabric-runtime` Python package version is derived at packaging time. `python/pyproject.toml` stays `dynamic = ["version"]` in the repository, and Maturin derives the version from `crates/fabric-python/Cargo.toml`, which @@ -143,8 +148,11 @@ The helper updates: `nemo-fabric-core`. 3. [`pyproject.toml`](pyproject.toml), every `adapters/**/pyproject.toml`, and their internal dependency pins to the same release version. -4. [`Cargo.lock`](Cargo.lock), [`uv.lock`](uv.lock), and every Python project +4. [`typescript/adapter-contract/package.json`](typescript/adapter-contract/package.json) + and its npm lockfile. +5. [`Cargo.lock`](Cargo.lock), [`uv.lock`](uv.lock), and every Python project lockfile. + Review docs and snippets that mention explicit versions, including: - [`README.md`](README.md) @@ -164,6 +172,7 @@ repository release, the safest baseline is: uv run pre-commit run --all-files just test-rust just test-python +just test-typescript just docs ``` @@ -179,6 +188,67 @@ place. In a disposable CI workspace that is fine. In a local checkout, restore those temporary manifest edits before continuing if you are not committing them. +## Bootstrap npm Trusted Publishing + +The npm package must exist before npm can bind it to a GitHub trusted publisher. +`nemo-fabric-adapter-contract@0.0.0` was published once as an inert registry +bootstrap. It contains only the package metadata, README, and Apache-2.0 +license; the first supported contract release must be published by the trusted +GitHub Actions workflow. Do not publish or tag `0.0.0` again. npm versions are +immutable. + +The bootstrap was published with `--tag next`. The registry also initialized +`latest` to `0.0.0` because it was the package's first version and rejected +removing that only `latest` tag. Leave both tags on the inert bootstrap: RC and +beta releases move `next`, and the first stable release replaces `latest`. + +Before the first supported TypeScript package release: + +1. Add at least one more NVIDIA maintainer to the package so registry + administration does not depend on the bootstrap publisher's account: + + ```bash + npm owner add nemo-fabric-adapter-contract + ``` + + npm sends each new maintainer an email invitation. The maintainer must accept + it before access is granted. Verify every expected account appears in + `npm owner ls nemo-fabric-adapter-contract` before the release. Maintainers + must use account-level two-factor authentication. +2. Create and protect the GitHub `npmjs` environment. Require the release + approvers who should authorize registry publication, and restrict deployment + tags to `v*`. +3. After `publish_typescript.yml` is present on the default branch, configure + the package's single trusted publisher in npm with these exact, + case-sensitive values: + + - Organization or user: `NVIDIA` + - Repository: `NeMo-Fabric` + - Workflow filename: `publish_typescript.yml` + - Environment: `npmjs` + - Allowed action: `npm publish` + +4. Cut the first genuine release tag and approve the `npmjs` environment when + prompted. The workflow tests, packs, and publishes that release through OIDC; + do not manually pre-publish the release version. On a retry, the workflow + exits without republishing only when its repack has byte-identical integrity + and the expected dist-tag is exact. A mismatch fails closed because npm + versions are immutable; do not overwrite or weaken the check. +5. Confirm the release's provenance on npm. + In the npm package settings, require two-factor authentication and disallow + token publication. Then remove or revoke any local credentials used for the + bootstrap. + +The workflow publishes stable versions with the `latest` dist-tag and beta or +RC versions with `next`. Stable releases do not move `next`; it continues to +identify the most recently published prerelease until a later beta or RC +replaces it. Alpha versions are not published. A retry skips only +when the immutable package version, packed artifact integrity, and expected +dist-tag all match. If any of them differs, the workflow fails so a maintainer +can inspect and repair the registry state explicitly. Publication also fails +rather than moving `latest` or `next` backward when cutting a patch from an +older release line. + ## Cut An RC Tag After the release commit is merged and validated, create and push a signed, @@ -312,6 +382,7 @@ Pushing a valid tag triggers : |---|---| | [`.github/workflows/ci_python.yml`](.github/workflows/ci_python.yml) | For all tags including alpha | | [`.github/workflows/publish_rust.yml`](.github/workflows/publish_rust.yml) | For RC, beta and release tags | +| [`.github/workflows/publish_typescript.yml`](.github/workflows/publish_typescript.yml) | For RC, beta and release tags | | [`.github/workflows/fern-docs.yml`](.github/workflows/fern-docs.yml) | For RC, beta and release tags | The release pipeline then: @@ -324,7 +395,11 @@ The release pipeline then: 3. Publishes `nemo-fabric-core` and `nemo-fabric-cli` to crates.io through trusted publishing for stable, beta, and RC tags. Alpha tags are not published to crates.io. -4. Publishes Fern documentation versions for stable, beta, and RC tags. Alpha +4. Publishes `nemo-fabric-adapter-contract` to npm through trusted + publishing for stable, beta, and RC tags. Stable releases use the `latest` + dist-tag; beta and RC releases use `next`. Alpha tags are not published to + npm. +5. Publishes Fern documentation versions for stable, beta, and RC tags. Alpha tags do not publish a separate documentation version. The workflow boundary is split intentionally: @@ -335,6 +410,9 @@ The workflow boundary is split intentionally: and publishes Fern documentation independently from package CI. - [`.github/workflows/publish_rust.yml`](.github/workflows/publish_rust.yml) owns crates.io publication decisions and credentials. +- [`.github/workflows/publish_typescript.yml`](.github/workflows/publish_typescript.yml) + owns npm publication decisions and requests a short-lived npm credential + through GitHub OIDC. It does not receive an npm write token. ## Publish The GitHub Release Entry @@ -372,5 +450,27 @@ After the release is live, verify: - [`nemo-fabric-adapters-codex`](https://pypi.nvidia.com/nemo-fabric-adapters-codex/) - [`nemo-fabric-adapters-deepagents`](https://pypi.nvidia.com/nemo-fabric-adapters-deepagents/) - [`nemo-fabric-adapters-hermes`](https://pypi.nvidia.com/nemo-fabric-adapters-hermes/) -4. The Fern documentation site shows the expected version and release notes. -5. The GitHub Release page is complete and accurate. +4. The TypeScript contract package is visible on npm with the expected version, + dist-tag, and provenance: + + ```bash + ( + set -euo pipefail + npmjs_registry="https://registry.npmjs.org/" + npm view "nemo-fabric-adapter-contract@" version \ + --registry="$npmjs_registry" + npm view "nemo-fabric-adapter-contract" dist-tags \ + --registry="$npmjs_registry" + verification_dir="$(mktemp -d)" + trap 'rm -rf "$verification_dir"' EXIT + cd "$verification_dir" + npm init --yes --registry="$npmjs_registry" + npm install --ignore-scripts --save-exact \ + --registry="$npmjs_registry" \ + "nemo-fabric-adapter-contract@" + npm audit signatures --registry="$npmjs_registry" + ) + ``` + +5. The Fern documentation site shows the expected version and release notes. +6. The GitHub Release page is complete and accurate. diff --git a/adapter-contract/src/nemo_fabric_adapter_contract/models.py b/adapter-contract/src/nemo_fabric_adapter_contract/models.py index 9fe8a8afd..be1424fc2 100644 --- a/adapter-contract/src/nemo_fabric_adapter_contract/models.py +++ b/adapter-contract/src/nemo_fabric_adapter_contract/models.py @@ -456,14 +456,14 @@ def _validate_artifact_path(value: str | Path) -> None: components = raw.replace("\\", "/").split("/") windows_drive_path = len(raw) >= 2 and raw[0].isalpha() and raw[1] == ":" if ( - not raw + not raw.strip() or path.is_absolute() or raw.startswith(("/", "\\")) or windows_drive_path or ".." in components ): raise ContractValidationError( - "artifact path must be non-empty, relative, and contain no parent traversal", + "artifact path must be nonblank, relative, and contain no parent traversal", path=("path",), ) diff --git a/adapters/common/src/nemo_fabric_adapters/common/utils.py b/adapters/common/src/nemo_fabric_adapters/common/utils.py index 2d0fc1d1d..43e482fcd 100644 --- a/adapters/common/src/nemo_fabric_adapters/common/utils.py +++ b/adapters/common/src/nemo_fabric_adapters/common/utils.py @@ -61,7 +61,7 @@ def validate_http_header(server_name: str, name: str, value: str) -> None: def validate_http_headers(server_name: str, value: dict[str, str]) -> None: """ Validate an MCP custom-header mapping. - + Use this method for harnesses that support environment variable expansion in HTTP headers. For harnesses that don't support environment variable expansion, use expand_http_headers instead. @@ -74,7 +74,7 @@ def validate_http_headers(server_name: str, value: dict[str, str]) -> None: def expand_http_headers(server_name: str, value: dict[str, str]) -> dict[str, str]: """ Expand environment variables and validate an MCP custom-header mapping. - + Use this method instead of validate_http_headers for harnesses that don't support environment variable expansion in HTTP headers. """ diff --git a/crates/fabric-core/src/agent_config.rs b/crates/fabric-core/src/agent_config.rs index c5882a20b..1e97b1dc2 100644 --- a/crates/fabric-core/src/agent_config.rs +++ b/crates/fabric-core/src/agent_config.rs @@ -14,6 +14,7 @@ use crate::config::{ AdapterConfigField, AdapterDescriptor, CapabilityPlan, FabricConfig, InstructionMode, McpAuthenticationConfig, }; +use crate::error::{FabricError, Result}; /// Configuration projected southbound to one adapter target. #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] @@ -65,10 +66,10 @@ pub struct AgentHarnessConfig { #[serde(deny_unknown_fields)] pub struct AgentModelConfig { /// Model provider identifier. - #[schemars(length(min = 1))] + #[schemars(length(min = 1), regex(pattern = r"\S"))] pub provider: String, /// Provider model identifier. - #[schemars(length(min = 1))] + #[schemars(length(min = 1), regex(pattern = r"\S"))] pub model: String, /// Environment variable containing the provider credential. #[serde(default, skip_serializing_if = "Option::is_none")] @@ -249,6 +250,80 @@ pub struct AgentWorkflowConfig { pub extensions: BTreeMap, } +/// Validate a projected adapter configuration before runtime handoff. +pub(crate) fn validate_agent_config(config: &AgentConfig) -> Result<()> { + for (role, model) in &config.models { + require_non_blank( + format!("agent_config.models.{role}.provider"), + &model.provider, + )?; + require_non_blank(format!("agent_config.models.{role}.model"), &model.model)?; + } + if let Some(system) = config + .instructions + .as_ref() + .and_then(|instructions| instructions.system.as_ref()) + { + require_non_blank("agent_config.instructions.system.content", &system.content)?; + } + if config + .runtime + .as_ref() + .is_some_and(|runtime| runtime.max_turns == Some(0)) + { + return invalid_agent_config( + "agent_config.runtime.max_turns", + "must be greater than zero", + ); + } + if let Some(mcp) = &config.mcp { + for (name, server) in &mcp.servers { + require_non_blank( + format!("agent_config.mcp.servers.{name}.transport"), + &server.transport, + )?; + require_non_blank(format!("agent_config.mcp.servers.{name}.url"), &server.url)?; + } + } + if let Some(tools) = &config.tools { + for (name, definition) in &tools.definitions { + require_non_blank( + format!("agent_config.tools.definitions.{name}.kind"), + &definition.kind, + )?; + require_non_blank( + format!("agent_config.tools.definitions.{name}.ref"), + &definition.r#ref, + )?; + } + } + if let Some(workflow) = &config.workflow { + require_non_blank( + "agent_config.workflow.entrypoint.kind", + &workflow.entrypoint.kind, + )?; + require_non_blank( + "agent_config.workflow.entrypoint.ref", + &workflow.entrypoint.r#ref, + )?; + } + Ok(()) +} + +fn require_non_blank(field: impl Into, value: &str) -> Result<()> { + if value.trim().is_empty() { + return invalid_agent_config(field, "must contain a non-whitespace character"); + } + Ok(()) +} + +fn invalid_agent_config(field: impl Into, reason: impl Into) -> Result { + Err(FabricError::InvalidConfig { + field: field.into(), + reason: reason.into(), + }) +} + /// Project a resolved northbound config into the selected adapter target contract. pub(crate) fn project_agent_config( config: &FabricConfig, diff --git a/crates/fabric-core/src/agent_execution.rs b/crates/fabric-core/src/agent_execution.rs index b805c7ab6..7f9780f4c 100644 --- a/crates/fabric-core/src/agent_execution.rs +++ b/crates/fabric-core/src/agent_execution.rs @@ -142,8 +142,8 @@ pub enum AgentRunResultValidationError { /// A successful result included an error. #[error("succeeded result must not include an error")] SucceededWithError, - /// An artifact path was empty, absolute, or contained parent traversal. - #[error("artifact path must be non-empty, relative, and contain no parent traversal: {0}")] + /// An artifact path was blank, absolute, or contained parent traversal. + #[error("artifact path must be non-blank and relative, and contain no parent traversal: {0}")] InvalidArtifactPath(PathBuf), } @@ -174,7 +174,7 @@ impl AgentRunResult { fn is_valid_agent_artifact_path(path: &Path) -> bool { let raw = path.to_string_lossy(); - !raw.is_empty() + raw.chars().any(|character| !character.is_whitespace()) && !path.is_absolute() && !raw.starts_with(['/', '\\']) && !raw @@ -197,7 +197,7 @@ where let path = PathBuf::deserialize(deserializer)?; if !is_valid_agent_artifact_path(&path) { return Err(serde::de::Error::custom( - "artifact path must be non-empty, relative, and contain no parent traversal", + "artifact path must be non-blank and relative, and contain no parent traversal", )); } Ok(path) @@ -206,6 +206,7 @@ where fn agent_artifact_path_schema(generator: &mut SchemaGenerator) -> Schema { let mut schema = String::json_schema(generator); schema.insert("minLength".into(), 1.into()); + schema.insert("pattern".into(), r"\S".into()); schema.insert( "not".into(), serde_json::json!({ @@ -304,6 +305,7 @@ mod tests { fn rejects_unsafe_artifact_paths_during_deserialization() { for path in [ "", + " \t", "/tmp/output", "../output", "nested/../output", diff --git a/crates/fabric-core/src/config.rs b/crates/fabric-core/src/config.rs index 7993d1b3c..fb800a001 100644 --- a/crates/fabric-core/src/config.rs +++ b/crates/fabric-core/src/config.rs @@ -196,13 +196,13 @@ pub struct WorkflowConfig { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct AdapterDescriptor { /// Adapter descriptor contract version. - #[schemars(length(min = 1))] + #[schemars(schema_with = "adapter_contract_version_schema")] pub contract_version: String, /// Unique id for this adapter implementation. - #[schemars(length(min = 1))] + #[schemars(length(min = 1), regex(pattern = r"\S"))] pub adapter_id: String, /// Stable machine-readable harness identifier implemented by this adapter. - #[schemars(length(min = 1))] + #[schemars(length(min = 1), regex(pattern = r"\S"))] pub harness: String, /// Adapter implementation kind. pub adapter_kind: AdapterKind, @@ -242,6 +242,13 @@ pub struct AdapterDescriptor { pub extensions: BTreeMap, } +fn adapter_contract_version_schema(generator: &mut SchemaGenerator) -> Schema { + let mut schema = String::json_schema(generator); + schema.insert("const".into(), ADAPTER_CONTRACT_VERSION.into()); + schema.insert("minLength".into(), 1.into()); + schema +} + fn adapter_extension_schemas_schema(generator: &mut SchemaGenerator) -> Schema { let mut schema = BTreeMap::>::json_schema(generator); @@ -525,12 +532,25 @@ pub enum AdapterConfigField { pub struct AdapterTelemetrySupport { /// Provider-specific telemetry capabilities supported by this adapter. #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + #[schemars(schema_with = "adapter_telemetry_providers_schema")] pub providers: BTreeMap, /// Additive adapter telemetry fields. #[serde(default, flatten)] pub extensions: BTreeMap, } +fn adapter_telemetry_providers_schema(generator: &mut SchemaGenerator) -> Schema { + let mut schema = + BTreeMap::::json_schema(generator); + schema.insert( + "propertyNames".into(), + serde_json::json!({ + "enum": TelemetryProvider::ALL.map(TelemetryProvider::as_str), + }), + ); + schema +} + /// Telemetry capabilities for one adapter-supported provider. #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct AdapterTelemetryProviderSupport { @@ -1295,6 +1315,8 @@ pub enum TelemetryProvider { } impl TelemetryProvider { + const ALL: [Self; 2] = [Self::Relay, Self::Native]; + /// Return the stable configuration value for this provider. pub fn as_str(self) -> &'static str { match self { @@ -2684,7 +2706,7 @@ fn resolve_telemetry_plan( let native_provider = telemetry.providers.get(&TelemetryProvider::Native); let relay = config.relay.as_ref(); let relay_enabled = relay_provider.is_some(); - let providers = [TelemetryProvider::Relay, TelemetryProvider::Native] + let providers = TelemetryProvider::ALL .into_iter() .filter(|provider| telemetry.providers.contains_key(provider)) .collect::>(); diff --git a/crates/fabric-core/src/runtime.rs b/crates/fabric-core/src/runtime.rs index a8c5fb719..d928cdc45 100644 --- a/crates/fabric-core/src/runtime.rs +++ b/crates/fabric-core/src/runtime.rs @@ -19,6 +19,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; +use crate::agent_config::validate_agent_config; use crate::config::{ AdapterConfigInput, AdapterKind, AgentConfig, CapabilityPlan, CapabilityTarget, ControlLocation, EnvironmentOwnership, FabricConfig, RunPlan, TelemetryPlan, @@ -557,6 +558,7 @@ pub fn prepare_environment(plan: &RunPlan) -> Result { /// Start or connect to a harness runtime. pub fn start_runtime(plan: &RunPlan) -> Result { validate_config(&plan.config)?; + validate_agent_config(&plan.agent_config)?; validate_harness_settings(&plan.config, plan.adapter_descriptor.as_ref())?; validate_workflow(&plan.config, plan.adapter_descriptor.as_ref())?; validate_adapter_compatibility(plan)?; @@ -2881,6 +2883,30 @@ for line in sys.stdin: let _ = fs::remove_dir_all(root); } + #[test] + fn local_host_revalidates_southbound_config_before_runtime_start() { + for field in ["provider", "model"] { + let (root, plan) = local_host_plan("success"); + let mut serialized = serde_json::to_value(plan).expect("serialize plan"); + serialized["agent_config"]["models"]["primary"] = serde_json::json!({ + "provider": "nvidia", + "model": "test-model" + }); + serialized["agent_config"]["models"]["primary"][field] = + Value::String(" \t".to_string()); + let plan: RunPlan = serde_json::from_value(serialized).expect("deserialize run plan"); + + let error = start_runtime(&plan).expect_err("start must reject blank agent config"); + assert!(matches!( + error, + FabricError::InvalidConfig { field: actual, .. } + if actual == format!("agent_config.models.primary.{field}") + )); + assert!(!root.join("artifacts").exists()); + let _ = fs::remove_dir_all(root); + } + } + #[test] fn local_host_revalidates_workflow_before_runtime_start() { let (root, mut plan) = local_host_plan("success"); diff --git a/crates/fabric-core/src/schema.rs b/crates/fabric-core/src/schema.rs index c2dd63910..39812e50f 100644 --- a/crates/fabric-core/src/schema.rs +++ b/crates/fabric-core/src/schema.rs @@ -220,6 +220,7 @@ where #[cfg(test)] mod tests { use super::*; + use crate::config::TelemetryProvider; fn schema_dir() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../schemas") @@ -315,12 +316,18 @@ mod tests { } #[test] - fn adapter_descriptor_schema_rejects_empty_identifiers() { + fn adapter_descriptor_schema_matches_runtime_constraints() { let schema = generate_schema(SchemaName::AdapterDescriptor).expect("schema generation"); + assert_eq!( + schema["properties"]["contract_version"]["const"], + crate::ADAPTER_CONTRACT_VERSION + ); assert_eq!(schema["properties"]["contract_version"]["minLength"], 1); assert_eq!(schema["properties"]["adapter_id"]["minLength"], 1); + assert_eq!(schema["properties"]["adapter_id"]["pattern"], r"\S"); assert_eq!(schema["properties"]["harness"]["minLength"], 1); + assert_eq!(schema["properties"]["harness"]["pattern"], r"\S"); assert_eq!( schema["properties"]["settings_schema"]["type"], serde_json::json!(["object", "null"]) @@ -356,11 +363,62 @@ mod tests { "usage" ]) ); + let declared_providers = &schema["$defs"]["AdapterTelemetrySupport"]["properties"]["providers"] + ["propertyNames"]["enum"]; + assert_eq!(declared_providers, &serde_json::json!(["relay", "native"])); + let provider_schema = + serde_json::to_value(schema_for!(TelemetryProvider)).expect("provider schema"); + let derived_providers = Value::Array( + provider_schema["oneOf"] + .as_array() + .expect("provider variants") + .iter() + .map(|variant| variant["const"].clone()) + .collect(), + ); + assert_eq!( + declared_providers, &derived_providers, + "TelemetryProvider::ALL must include every enum variant" + ); + + let validator = jsonschema::validator_for(&schema).expect("valid descriptor schema"); + let descriptor = serde_json::json!({ + "contract_version": crate::ADAPTER_CONTRACT_VERSION, + "adapter_id": "test.fabric.schema", + "harness": "schema-test", + "adapter_kind": "python", + "telemetry": { + "providers": { + "relay": {} + } + } + }); + assert!(validator.is_valid(&descriptor)); + + let mut unsupported_contract = descriptor.clone(); + unsupported_contract["contract_version"] = serde_json::json!("fabric.adapter/v1alpha3"); + assert!(!validator.is_valid(&unsupported_contract)); + + let mut unsupported_provider = descriptor.clone(); + unsupported_provider["telemetry"]["providers"] = serde_json::json!({"custom": {}}); + assert!(!validator.is_valid(&unsupported_provider)); + + for field in ["adapter_id", "harness"] { + let mut blank_identifier = descriptor.clone(); + blank_identifier[field] = serde_json::json!(" \t"); + assert!(!validator.is_valid(&blank_identifier)); + } } #[test] fn adapter_contract_schemas_bound_rust_integer_types() { let config = generate_schema(SchemaName::AgentConfig).expect("schema generation"); + for field in ["provider", "model"] { + assert_eq!( + config["$defs"]["AgentModelConfig"]["properties"][field]["pattern"], + r"\S" + ); + } assert_eq!( config["$defs"]["AgentRuntimeConfig"]["properties"]["max_turns"]["maximum"], u32::MAX @@ -390,6 +448,7 @@ mod tests { "error": {"code": "target_error", "message": "target failed"} }))); for path in [ + " \t", "nested/../output", r"nested\..\output", r"C:\tmp\output", diff --git a/docs/adapter-contract/README.md b/docs/adapter-contract/README.md index 13548794c..081f1a193 100644 --- a/docs/adapter-contract/README.md +++ b/docs/adapter-contract/README.md @@ -81,3 +81,10 @@ Canonical adapter-facing JSON Schemas are published in the repository [`schemas/adapter-contract/` directory](https://github.com/NVIDIA/NeMo-Fabric/tree/main/schemas/adapter-contract). Python adapters can validate the southbound models with `nemo-fabric-adapter-contract` without depending on the NeMo Fabric runtime. +TypeScript adapters can import the descriptor, configuration, runtime-context, +request, and result types from `nemo-fabric-adapter-contract`, matching the +Python package's single model namespace. The request and result types retain +their documented preview status until the typed invocation transport is +negotiated. The TypeScript declarations provide compile-time structure, not +runtime JSON validation; validate untrusted data against the packaged JSON +Schemas. diff --git a/justfile b/justfile index 10480de62..25040459e 100644 --- a/justfile +++ b/justfile @@ -249,14 +249,22 @@ set_python_project_versions() { "$python_executable" scripts/ci/set_python_project_versions.py "$version" } +set_typescript_project_version() { + local version="$1" + local python_executable="" + python_executable="$(uv_python_executable)" + "$python_executable" scripts/ci/set_typescript_project_version.py "$version" +} + set_project_version() { local version="$1" set_cargo_workspace_version "$version" set_python_project_versions "$version" + set_typescript_project_version "$version" } ''' -# Remove local Rust and Python build and test artifacts. +# Remove local Rust, Python, and TypeScript build and test artifacts. clean: #!/usr/bin/env bash shopt -s globstar nullglob @@ -271,6 +279,8 @@ clean: **/coverage.xml \ **/dist \ docs/node_modules \ + typescript/adapter-contract/node_modules \ + typescript/adapter-contract/*.tgz \ target/ \ **/build/ @@ -298,12 +308,28 @@ build-python: --reinstall-package nemo-fabric-runtime fi +# Install the TypeScript adapter contract dependencies from the lockfile. +install-typescript: + npm ci --prefix typescript/adapter-contract --ignore-scripts + +# Build the TypeScript adapter contract using the locked dependency set. +build-typescript: install-typescript + npm run build --prefix typescript/adapter-contract + +# Generate the TypeScript adapter contract from the committed JSON Schemas. +generate-typescript-contract: install-typescript + npm run generate --prefix typescript/adapter-contract + +# Verify the TypeScript adapter contract package tarball. +pack-typescript: install-typescript + npm run pack:check --prefix typescript/adapter-contract + # Generate the JSON Schema files from the Rust configuration types. schemas: cargo run -p nemo-fabric-core --example generate-schemas -- schemas # Build all supported language packages. -build-all: build-rust build-python schemas +build-all: build-rust build-python schemas build-typescript # Create or update the lockfile for every Python project. lock-python: @@ -376,8 +402,12 @@ test-python: test-rust: cargo test --workspace --locked -# Run all Rust and Python tests. -test-all: test-rust test-python +# Run the TypeScript adapter contract checks using the locked dependency set. +test-typescript: install-typescript + npm test --prefix typescript/adapter-contract + +# Run all Rust, Python, and TypeScript tests. +test-all: test-rust test-python test-typescript # Build wheels for every Python project into the repository dist directory. wheels: diff --git a/schemas/SCHEMA.md b/schemas/SCHEMA.md index 9faeb341b..03b197e3c 100644 --- a/schemas/SCHEMA.md +++ b/schemas/SCHEMA.md @@ -8,10 +8,12 @@ SPDX-License-Identifier: Apache-2.0 This directory contains committed JSON Schema snapshots for the public NeMo Fabric contract. The files are generated from the Rust core types, not edited by hand. -The Python SDK exposes Pydantic authoring models for application callers. Those -models are hand-maintained against these Rust-generated schemas for now. When a -schema-backed Rust type changes, update the matching Pydantic model and its -schema-alignment tests in the same change. +The Python adapter-contract package exposes dependency-free dataclasses with +optional Pydantic interoperability. Those models are hand-maintained against +these Rust-generated schemas. The TypeScript adapter-contract package generates +compile-time declarations from the committed schema snapshots. When a +schema-backed Rust type changes, update each applicable language binding and +its parity tests in the same change. ## Directory Layout @@ -35,6 +37,17 @@ An adapter author can treat `adapter-contract/` as the complete schema entry point. The `legacy/` subdirectory contains only the transitional local-host payload used while first-party adapters migrate to the typed execution types. +The language bindings preserve this boundary: + +- Python adapters use `nemo-fabric-adapter-contract` for dependency-free + dataclasses and optional Pydantic models. +- TypeScript adapters use `nemo-fabric-adapter-contract` for the descriptor, + configuration, runtime-context, request, and result types, matching the + Python package's single model namespace. Request and result types retain + their documented preview status until the typed invocation transport is + negotiated. The package also includes these canonical schemas for runtime + validation without selecting a validation-library dependency. + `FabricConfig` is the northbound source of consumer intent. Planning produces the `CapabilityPlan` as routed evidence and projects the fields accepted by the selected descriptor into `AgentConfig`, the authoritative southbound adapter @@ -122,3 +135,13 @@ To add a new schema-backed typed model: Run `cargo test` after regenerating schemas. The snapshot tests compare the committed files against the schemas generated from the current Rust types and fail on accidental drift. + +Regenerate the TypeScript projection after an intentional adapter-contract +schema change: + +```bash +just generate-typescript-contract +``` + +Run `just test-typescript` to check generated-file drift, strict compile-time +fixtures, package contents, and clean-consumer imports. diff --git a/schemas/adapter-contract/adapter-descriptor.schema.json b/schemas/adapter-contract/adapter-descriptor.schema.json index 682b784e0..f7553d983 100644 --- a/schemas/adapter-contract/adapter-descriptor.schema.json +++ b/schemas/adapter-contract/adapter-descriptor.schema.json @@ -198,6 +198,12 @@ "$ref": "#/$defs/AdapterTelemetryProviderSupport" }, "description": "Provider-specific telemetry capabilities supported by this adapter.", + "propertyNames": { + "enum": [ + "relay", + "native" + ] + }, "type": "object" } }, @@ -242,6 +248,7 @@ "adapter_id": { "description": "Unique id for this adapter implementation.", "minLength": 1, + "pattern": "\\S", "type": "string" }, "adapter_kind": { @@ -266,6 +273,7 @@ "description": "NeMo Fabric config areas this adapter consumes or generates." }, "contract_version": { + "const": "fabric.adapter/v1alpha2", "description": "Adapter descriptor contract version.", "minLength": 1, "type": "string" @@ -303,6 +311,7 @@ "harness": { "description": "Stable machine-readable harness identifier implemented by this adapter.", "minLength": 1, + "pattern": "\\S", "type": "string" }, "model_schema": { diff --git a/schemas/adapter-contract/agent-config.schema.json b/schemas/adapter-contract/agent-config.schema.json index 49a5adaeb..8471c5454 100644 --- a/schemas/adapter-contract/agent-config.schema.json +++ b/schemas/adapter-contract/agent-config.schema.json @@ -188,11 +188,13 @@ "model": { "description": "Provider model identifier.", "minLength": 1, + "pattern": "\\S", "type": "string" }, "provider": { "description": "Model provider identifier.", "minLength": 1, + "pattern": "\\S", "type": "string" }, "settings": { diff --git a/schemas/adapter-contract/agent-run-result.schema.json b/schemas/adapter-contract/agent-run-result.schema.json index 0aee5b479..c6ac4081e 100644 --- a/schemas/adapter-contract/agent-run-result.schema.json +++ b/schemas/adapter-contract/agent-run-result.schema.json @@ -46,6 +46,7 @@ } ] }, + "pattern": "\\S", "type": "string" } }, diff --git a/schemas/run-plan.schema.json b/schemas/run-plan.schema.json index dabff1f49..574fde9de 100644 --- a/schemas/run-plan.schema.json +++ b/schemas/run-plan.schema.json @@ -108,6 +108,7 @@ "adapter_id": { "description": "Unique id for this adapter implementation.", "minLength": 1, + "pattern": "\\S", "type": "string" }, "adapter_kind": { @@ -132,6 +133,7 @@ "description": "NeMo Fabric config areas this adapter consumes or generates." }, "contract_version": { + "const": "fabric.adapter/v1alpha2", "description": "Adapter descriptor contract version.", "minLength": 1, "type": "string" @@ -169,6 +171,7 @@ "harness": { "description": "Stable machine-readable harness identifier implemented by this adapter.", "minLength": 1, + "pattern": "\\S", "type": "string" }, "model_schema": { @@ -339,6 +342,12 @@ "$ref": "#/$defs/AdapterTelemetryProviderSupport" }, "description": "Provider-specific telemetry capabilities supported by this adapter.", + "propertyNames": { + "enum": [ + "relay", + "native" + ] + }, "type": "object" } }, @@ -628,11 +637,13 @@ "model": { "description": "Provider model identifier.", "minLength": 1, + "pattern": "\\S", "type": "string" }, "provider": { "description": "Model provider identifier.", "minLength": 1, + "pattern": "\\S", "type": "string" }, "settings": { diff --git a/scripts/ci/publish_typescript_package.py b/scripts/ci/publish_typescript_package.py new file mode 100644 index 000000000..7ad75541d --- /dev/null +++ b/scripts/ci/publish_typescript_package.py @@ -0,0 +1,304 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import argparse +import json +import re +import subprocess +import sys +import time +from collections.abc import Callable, Sequence +from dataclasses import dataclass +from pathlib import Path + + +VERSION_PATTERN = re.compile( + r"^(?P\d+)\.(?P\d+)\.(?P\d+)" + r"(?:-(?P