From 096a236a6ebeca217fe1ec26b6c23cfafbbc3179 Mon Sep 17 00:00:00 2001 From: sawradip Date: Thu, 13 Nov 2025 23:40:56 +0600 Subject: [PATCH] refactor: update GitHub Actions workflow and enhance TypeScript SDK - Renamed workflow job from `detect-and-release` to `detect-release` for clarity. - Introduced separate jobs for publishing Python and TypeScript SDKs, utilizing a reusable workflow. - Updated the `create-release` job to depend on the new publishing jobs. - Enhanced TypeScript SDK with improved error handling, timeout management, and structured response handling. - Added optional `timeoutSeconds` parameter to REST and WebSocket clients for better control over request durations. - Updated peer dependencies in `package.json` to include `better-sqlite3` as optional. - Refactored environment detection logic in the client to handle undefined values more gracefully. --- .github/workflows/create-release.yml | 190 +-- .github/workflows/python-release.yml | 99 ++ .github/workflows/reusable-sdk-release.yml | 58 + .github/workflows/typescript-release.yml | 106 ++ runagent-ts/PUBLISH.md | 73 + runagent-ts/README.md | 255 ++++ runagent-ts/package.json | 9 +- runagent-ts/pnpm-lock.yaml | 1515 ++++++++++++++++++++ runagent-ts/runagent-0.1.26.tgz | Bin 0 -> 49349 bytes runagent-ts/src/client/index.ts | 419 ++++-- runagent-ts/src/http/index.ts | 36 +- runagent-ts/src/rest/index.ts | 140 +- runagent-ts/src/types/index.ts | 36 +- runagent-ts/src/websocket/base.ts | 185 ++- runagent-ts/src/websocket/browser.ts | 49 +- runagent-ts/src/websocket/node.ts | 69 +- sdk_checklist.md | 159 ++ 17 files changed, 2963 insertions(+), 435 deletions(-) create mode 100644 .github/workflows/python-release.yml create mode 100644 .github/workflows/reusable-sdk-release.yml create mode 100644 .github/workflows/typescript-release.yml create mode 100644 runagent-ts/PUBLISH.md create mode 100644 runagent-ts/README.md create mode 100644 runagent-ts/pnpm-lock.yaml create mode 100644 runagent-ts/runagent-0.1.26.tgz create mode 100644 sdk_checklist.md diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 629709a..61910d7 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -1,5 +1,5 @@ # .github/workflows/create-release.yml -name: Create GitHub Release and Publish to PyPI +name: Create GitHub Release and Publish SDKs on: push: branches: [main] # Trigger on ANY push to main (including PR merges) @@ -7,9 +7,13 @@ on: types: [closed] # Also trigger when PRs are merged branches: [main] jobs: - detect-and-release: + detect-release: runs-on: ubuntu-latest if: github.event_name == 'push' || (github.event.pull_request.merged == true) + outputs: + latest_tag: ${{ steps.find-tags.outputs.latest_tag }} + version: ${{ steps.find-tags.outputs.version }} + should_release: ${{ steps.check-release.outputs.should_release }} permissions: contents: write pull-requests: read @@ -81,118 +85,70 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # NEW: Set up Python for PyPI publishing - - name: Set up Python - if: steps.check-release.outputs.should_release == 'true' - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - # NEW: Install build dependencies - - name: Install build dependencies - if: steps.check-release.outputs.should_release == 'true' - run: | - python -m pip install --upgrade pip - python -m pip install build twine - - # NEW: Verify version matches tag - - name: Verify package version matches tag - if: steps.check-release.outputs.should_release == 'true' - run: | - TAG_VERSION="${{ steps.find-tags.outputs.version }}" - - # Check if setup.py exists - if [ -f "setup.py" ]; then - PACKAGE_VERSION=$(python setup.py --version) - # Check if pyproject.toml exists - elif [ -f "pyproject.toml" ]; then - # Extract version from pyproject.toml - PACKAGE_VERSION=$(python -c "import tomllib; data = tomllib.load(open('pyproject.toml', 'rb')); print(data['project']['version'])") - else - echo "❌ Neither setup.py nor pyproject.toml found!" - exit 1 - fi - - echo "Tag version: $TAG_VERSION" - echo "Package version: $PACKAGE_VERSION" - - if [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then - echo "❌ Version mismatch! Tag: $TAG_VERSION, Package: $PACKAGE_VERSION" - exit 1 - else - echo "✅ Version match confirmed: $TAG_VERSION" - fi - - # NEW: Build Python package - - name: Build package - if: steps.check-release.outputs.should_release == 'true' - run: python -m build - - # NEW: Check if package already exists on PyPI - - name: Check if package exists on PyPI - if: steps.check-release.outputs.should_release == 'true' - id: check-pypi - run: | - VERSION="${{ steps.find-tags.outputs.version }}" - - # Get package name from setup.py or pyproject.toml - if [ -f "pyproject.toml" ]; then - PACKAGE_NAME=$(python -c "import tomllib; data = tomllib.load(open('pyproject.toml', 'rb')); print(data['project']['name'])") - elif [ -f "setup.py" ]; then - PACKAGE_NAME=$(python setup.py --name) - else - echo "❌ Cannot determine package name" - exit 1 - fi - - echo "Checking if $PACKAGE_NAME version $VERSION exists on PyPI..." - - # Check if this version already exists on PyPI - HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/$PACKAGE_NAME/$VERSION/json/") - - if [ "$HTTP_CODE" = "200" ]; then - echo "⚠️ Package $PACKAGE_NAME v$VERSION already exists on PyPI, skipping upload" - echo "should_publish_pypi=false" >> $GITHUB_OUTPUT - else - echo "✅ Package $PACKAGE_NAME v$VERSION not found on PyPI, proceeding with upload" - echo "should_publish_pypi=true" >> $GITHUB_OUTPUT - fi - - # NEW: Publish to PyPI using API token - - name: Publish package to PyPI - if: steps.check-release.outputs.should_release == 'true' && steps.check-pypi.outputs.should_publish_pypi == 'true' - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.PYPI_API_TOKEN }} - print-hash: true - - - name: Create main GitHub Release - if: steps.check-release.outputs.should_release == 'true' - uses: softprops/action-gh-release@v1 - with: - tag_name: ${{ steps.find-tags.outputs.latest_tag }} - name: RunAgent v${{ steps.find-tags.outputs.version }} - body: | - # 🚀 RunAgent v${{ steps.find-tags.outputs.version }} - - **Universal AI Agent Platform - All SDKs synchronized at v${{ steps.find-tags.outputs.version }}** - - ## 📋 What's New - - For detailed changes in this release, see [CHANGELOG.md](./CHANGELOG.md). - - This release synchronizes all RunAgent SDKs (Python, JavaScript, Rust, Go) to version ${{ steps.find-tags.outputs.version }}. - - ## 📦 Installation - - ```bash - pip install runagent==${{ steps.find-tags.outputs.version }} - ``` - - draft: false - prerelease: false - generate_release_notes: true # Let GitHub add commit-based notes - files: | - CHANGELOG.md - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + publish-python: + name: Publish Python SDK + needs: detect-release + if: needs.detect-release.outputs.should_release == 'true' + uses: ./.github/workflows/reusable-sdk-release.yml + with: + sdk: python + working-directory: . + tag: ${{ needs.detect-release.outputs.latest_tag }} + version: ${{ needs.detect-release.outputs.version }} + secrets: inherit + + publish-typescript: + name: Publish TypeScript SDK + needs: detect-release + if: needs.detect-release.outputs.should_release == 'true' + uses: ./.github/workflows/reusable-sdk-release.yml + with: + sdk: typescript + working-directory: runagent-ts + tag: ${{ needs.detect-release.outputs.latest_tag }} + version: ${{ needs.detect-release.outputs.version }} + secrets: inherit + + create-release: + name: Create GitHub Release + needs: + - detect-release + - publish-python + - publish-typescript + if: needs.detect-release.outputs.should_release == 'true' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Create main GitHub Release + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ needs.detect-release.outputs.latest_tag }} + name: RunAgent v${{ needs.detect-release.outputs.version }} + body: | + # 🚀 RunAgent v${{ needs.detect-release.outputs.version }} + + **Universal AI Agent Platform - All SDKs synchronized at v${{ needs.detect-release.outputs.version }}** + + ## 📋 What's New + + For detailed changes in this release, see [CHANGELOG.md](./CHANGELOG.md). + + This release synchronizes all RunAgent SDKs (Python, JavaScript, Rust, Go) to version ${{ needs.detect-release.outputs.version }}. + + ## 📦 Installation + + ```bash + pip install runagent==${{ needs.detect-release.outputs.version }} + ``` + + draft: false + prerelease: false + generate_release_notes: true # Let GitHub add commit-based notes + files: | + CHANGELOG.md + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/python-release.yml b/.github/workflows/python-release.yml new file mode 100644 index 0000000..faf35be --- /dev/null +++ b/.github/workflows/python-release.yml @@ -0,0 +1,99 @@ +name: Python SDK Release + +on: + workflow_call: + inputs: + working-directory: + description: Path where build commands should run + required: true + type: string + tag: + description: Git tag for this release (e.g., v0.1.0) + required: true + type: string + version: + description: Package version without the leading v (e.g., 0.1.0) + required: true + type: string + python-version: + description: Python version used to build the package + required: false + type: string + default: "3.11" + secrets: + PYPI_API_TOKEN: + required: true + +jobs: + publish: + name: Publish Python SDK + runs-on: ubuntu-latest + if: inputs.version != '' + defaults: + run: + shell: bash + working-directory: ${{ inputs.working-directory }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Show release context + run: | + echo "Tag: ${{ inputs.tag }}" + echo "Version: ${{ inputs.version }}" + echo "Working directory: ${{ inputs.working-directory }}" + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + + - name: Upgrade pip and install build dependencies + run: | + python -m pip install --upgrade pip + python -m pip install build twine + + - name: Verify package version matches tag + id: verify-python-version + run: | + TAG_VERSION="${{ inputs.version }}" + PACKAGE_VERSION=$(python -c "import tomllib; data = tomllib.load(open('pyproject.toml', 'rb')); print(data['project']['version'])") + echo "Detected package version: $PACKAGE_VERSION" + if [ "$PACKAGE_VERSION" != "$TAG_VERSION" ]; then + echo "❌ Version mismatch! Tag: $TAG_VERSION, Package: $PACKAGE_VERSION" + exit 1 + fi + echo "✅ Python version matches tag" + + - name: Build package + run: python -m build + + - name: Check if package version already exists on PyPI + id: check-python + run: | + VERSION="${{ inputs.version }}" + PACKAGE_NAME=$(python -c "import tomllib; data = tomllib.load(open('pyproject.toml', 'rb')); print(data['project']['name'])") + echo "Package name: $PACKAGE_NAME" + HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/$PACKAGE_NAME/$VERSION/json/") + if [ "$HTTP_CODE" = "200" ]; then + echo "⚠️ $PACKAGE_NAME v$VERSION already exists on PyPI" + echo "should_publish=false" >> "$GITHUB_OUTPUT" + else + echo "✅ $PACKAGE_NAME v$VERSION not found on PyPI" + echo "should_publish=true" >> "$GITHUB_OUTPUT" + fi + echo "package_name=$PACKAGE_NAME" >> "$GITHUB_OUTPUT" + + - name: Publish package to PyPI + if: steps.check-python.outputs.should_publish == 'true' + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_API_TOKEN }} + print-hash: true + + - name: Skip publish (already exists) + if: steps.check-python.outputs.should_publish != 'true' + run: echo "Skipping PyPI publish because version already exists." + diff --git a/.github/workflows/reusable-sdk-release.yml b/.github/workflows/reusable-sdk-release.yml new file mode 100644 index 0000000..0c9b1b8 --- /dev/null +++ b/.github/workflows/reusable-sdk-release.yml @@ -0,0 +1,58 @@ +name: SDK Release Router + +on: + workflow_call: + inputs: + sdk: + description: Identifier for the SDK (e.g., python, typescript) + required: true + type: string + working-directory: + description: Path where build commands should run + required: true + type: string + tag: + description: Git tag for this release (e.g., v0.1.0) + required: true + type: string + version: + description: Package version without the leading v (e.g., 0.1.0) + required: true + type: string + python-version: + description: Python version to use when sdk == python + required: false + type: string + default: "3.11" + node-version: + description: Node.js version to use when sdk == typescript + required: false + type: string + default: "20" + secrets: + PYPI_API_TOKEN: + required: false + NPM_TOKEN: + required: false + +jobs: + python: + if: inputs.sdk == 'python' && inputs.version != '' + uses: ./.github/workflows/python-release.yml + with: + working-directory: ${{ inputs.working-directory }} + tag: ${{ inputs.tag }} + version: ${{ inputs.version }} + python-version: ${{ inputs.python-version }} + secrets: inherit + + typescript: + if: inputs.sdk == 'typescript' && inputs.version != '' + uses: ./.github/workflows/typescript-release.yml + with: + working-directory: ${{ inputs.working-directory }} + tag: ${{ inputs.tag }} + version: ${{ inputs.version }} + node-version: ${{ inputs.node-version }} + secrets: inherit + diff --git a/.github/workflows/typescript-release.yml b/.github/workflows/typescript-release.yml new file mode 100644 index 0000000..eb13a3e --- /dev/null +++ b/.github/workflows/typescript-release.yml @@ -0,0 +1,106 @@ +name: TypeScript SDK Release + +on: + workflow_call: + inputs: + working-directory: + description: Path where build commands should run + required: true + type: string + tag: + description: Git tag for this release (e.g., v0.1.0) + required: true + type: string + version: + description: Package version without the leading v (e.g., 0.1.0) + required: true + type: string + node-version: + description: Node.js version used to build the package + required: false + type: string + default: "20" + secrets: + NPM_TOKEN: + required: false + +jobs: + publish: + name: Publish TypeScript SDK + runs-on: ubuntu-latest + if: inputs.version != '' + permissions: + contents: read + id-token: write + defaults: + run: + shell: bash + working-directory: ${{ inputs.working-directory }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Show release context + run: | + echo "Tag: ${{ inputs.tag }}" + echo "Version: ${{ inputs.version }}" + echo "Working directory: ${{ inputs.working-directory }}" + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.node-version }} + cache: npm + cache-dependency-path: ${{ inputs.working-directory }}/package-lock.json + registry-url: https://registry.npmjs.org + + - name: Ensure npm supports trusted publishing + run: npm install -g npm@^11.5.1 + + - name: Install dependencies + run: npm ci + + - name: Type-check + run: npm run type-check + + - name: Build package + run: npm run build + + - name: Verify package version matches tag + id: verify-ts-version + run: | + TAG_VERSION="${{ inputs.version }}" + PACKAGE_VERSION=$(node -p "require('./package.json').version") + echo "Detected package version: $PACKAGE_VERSION" + if [ "$PACKAGE_VERSION" != "$TAG_VERSION" ]; then + echo "❌ Version mismatch! Tag: $TAG_VERSION, Package: $PACKAGE_VERSION" + exit 1 + fi + echo "✅ TypeScript version matches tag" + + - name: Check if package version already exists on npm + id: check-npm + run: | + VERSION="${{ inputs.version }}" + PACKAGE_NAME=$(node -p "require('./package.json').name") + echo "Package name: $PACKAGE_NAME" + HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/$PACKAGE_NAME/$VERSION") + if [ "$HTTP_CODE" = "200" ]; then + echo "⚠️ $PACKAGE_NAME v$VERSION already exists on npm" + echo "should_publish=false" >> "$GITHUB_OUTPUT" + else + echo "✅ $PACKAGE_NAME v$VERSION not found on npm" + echo "should_publish=true" >> "$GITHUB_OUTPUT" + fi + echo "package_name=$PACKAGE_NAME" >> "$GITHUB_OUTPUT" + + - name: Publish package to npm + if: steps.check-npm.outputs.should_publish == 'true' + run: npm publish --access public + + - name: Skip publish (already exists) + if: steps.check-npm.outputs.should_publish != 'true' + run: echo "Skipping npm publish because version already exists." + diff --git a/runagent-ts/PUBLISH.md b/runagent-ts/PUBLISH.md new file mode 100644 index 0000000..9693819 --- /dev/null +++ b/runagent-ts/PUBLISH.md @@ -0,0 +1,73 @@ +# Publishing `runagent` + +This package ships the TypeScript SDK that mirrors the RunAgent CLI/Python client. Follow these steps whenever you publish an updated build to npm. + +--- + +## 1. Prerequisites + +- npm account with publish rights to the `runagent` package. +- `npm login` already performed in your shell. +- Local environment with Node.js ≥ 18. + +--- + +## 2. Preflight Checklist + +1. **Update version**: bump `"version"` in `package.json` following semver (major/minor/patch). +2. **Changelog**: ensure release notes exist (main repository changelog, or add release notes to the docs site). +3. **Dependencies**: verify `peerDependencies` remain accurate (`ws`, `better-sqlite3` optional). + +--- + +## 3. Build & Test + +```bash +# From runagent-ts/ +npm install # installs dev deps +npm run type-check # TypeScript compile check +npm run build # bundles cjs + esm + dts +``` + +Optional sanity checks: + +```bash +npm pack # creates a local tarball for inspection +``` + +Inspect `dist/` and the pack output to confirm only the expected files are included (`runagent-client.js`, `.cjs`, `index.d.ts`, etc.). + +--- + +## 4. Publish + +The package uses the contents of the `dist/` folder that `vite build` produces. + +```bash +npm publish --access public +``` + +> **Dry run**: add `--dry-run` if you want to verify the payload before publishing. + +If two-factor auth is enabled on npm, complete the OTP prompt. + +--- + +## 5. Post-Publish + +- Tag the release in git (e.g. `git tag sdk-ts-v0.1.27 && git push origin sdk-ts-v0.1.27`). +- Notify the team / update release notes. +- Ensure docs references (CLI docs, SDK docs) point at the new version. + +--- + +## Troubleshooting + +- **`npm ERR! publish fail`**: verify you have publish rights and that the version is new (npm rejects republishing the same version). +- **Bundled `better-sqlite3`**: confirm the dependency stayed as optional peer; browser consumers should not be forced to install it. +- **Missing WebSocket support**: remind Node users to install `ws`, or include that note in the release notes if API changes require it. + +--- + +You’re done! 🚀 + diff --git a/runagent-ts/README.md b/runagent-ts/README.md new file mode 100644 index 0000000..c96cfd6 --- /dev/null +++ b/runagent-ts/README.md @@ -0,0 +1,255 @@ +# RunAgent TypeScript SDK + +The RunAgent TypeScript SDK delivers the same `RunAgentClient` interface found in the Python SDK, tailored for both Node.js services and browser-based applications. Use it to invoke agents you deploy with the RunAgent CLI, whether they run locally on the same machine or remotely on `backend.run-agent.ai`. + +--- + +## Installation + +```bash +npm install runagent +``` + +Optional peer dependencies: + +- `ws` – required for WebSocket streaming in Node.js environments. +- `better-sqlite3` – only needed when you want Node.js clients to auto-discover local agents via the RunAgent registry. Browser builds should not install it. + +```bash +# For Node.js apps that rely on registry discovery: +npm install ws better-sqlite3 +``` + +--- + +## Configuration Overview + +```ts +interface RunAgentConfig { + agentId: string; + entrypointTag: string; + local?: boolean; // default: false in browser, true in Node if not specified + host?: string; // required in browser when local = true (no registry access) + port?: number; // required in browser when local = true (no registry access) + apiKey?: string; // defaults to RUNAGENT_API_KEY env variable + baseUrl?: string; // defaults to RUNAGENT_BASE_URL env variable or https://backend.run-agent.ai + baseSocketUrl?: string; // optional override for WebSocket origin + apiPrefix?: string; // defaults to /api/v1 + timeoutSeconds?: number; // defaults to 300 + extraParams?: Record; // reserved for metadata forwarding + enableRegistry?: boolean; // defaults to true in Node, false in browsers +} +``` + +**Environment variables** + +- `RUNAGENT_API_KEY` – API key for remote agents (Bearer token). +- `RUNAGENT_BASE_URL` – override the default remote base URL (useful for staging/self-hosted deployments). + +For browser builds, you can expose these via bundler secrets or a custom `globalThis.RUNAGENT_ENV` object before initialising the client. + +--- + +## Usage (Node.js) + +### 1. Remote agent + +```ts +import { RunAgentClient } from 'runagent'; + +const client = new RunAgentClient({ + agentId: 'agent-id-from-dashboard', + entrypointTag: 'support_flow', + apiKey: process.env.RUNAGENT_API_KEY, + // Optional: baseUrl defaults to https://backend.run-agent.ai +}); + +await client.initialize(); + +const result = await client.run({ + customer_email: 'alex@example.com', + issue_summary: 'Billing question', +}); + +console.log(result); +``` + +### 2. Remote streaming + +```ts +import { RunAgentClient } from 'runagent'; + +const client = new RunAgentClient({ + agentId: 'agent-id-from-dashboard', + entrypointTag: 'support_flow_stream', + apiKey: process.env.RUNAGENT_API_KEY, +}); + +await client.initialize(); + +for await (const chunk of client.runStream({ transcript: [] })) { + console.log('>>>', chunk); +} +``` + +### 3. Local agent discovery (Node only) + +```ts +import { RunAgentClient } from 'runagent'; + +const client = new RunAgentClient({ + agentId: 'local-agent-id', + entrypointTag: 'minimal', + local: true, + // enableRegistry defaults to true in Node; make sure better-sqlite3 is installed +}); + +await client.initialize(); // looks up ~/.runagent/runagent_local.db +const result = await client.run({ message: 'Hello there' }); +console.log(result); +``` + +If you prefer to bypass the registry (or if `better-sqlite3` isn’t installed), pass the host and port explicitly: + +```ts +const client = new RunAgentClient({ + agentId: 'local-agent-id', + entrypointTag: 'minimal', + local: true, + enableRegistry: false, + host: '127.0.0.1', + port: 8450, +}); +``` + +--- + +## Usage (Browser / Edge Runtimes) + +Browser builds cannot access the local registry or the filesystem. Provide connection details explicitly and set `enableRegistry: false`. + +```ts +import { RunAgentClient } from 'runagent'; + +const client = new RunAgentClient({ + agentId: 'agent-id-from-dashboard', + entrypointTag: 'support_flow', + local: false, + apiKey: window.RUNAGENT_API_KEY, // inject securely via bundler/runtime + enableRegistry: false, +}); + +await client.initialize(); +const result = await client.run({ message: 'Hello!' }); +``` + +For browser streaming: + +```ts +const client = new RunAgentClient({ + agentId: 'agent-id', + entrypointTag: 'support_flow_stream', + apiKey: window.RUNAGENT_API_KEY, + enableRegistry: false, +}); + +await client.initialize(); + +for await (const chunk of client.runStream({ message: 'Need help' })) { + renderChunk(chunk); +} +``` + +> **Tip:** To keep bundles light, Webpack/Vite can tree-shake the optional registry path when `enableRegistry` is `false`. This avoids shipping Node-only dependencies to the browser. + +--- + +## API Reference + +### `RunAgentClient` methods + +- `initialize(): Promise` + Resolves connection details (registry lookup for local Node use, base URLs for remote) and validates that the requested entrypoint exists. + +- `run(inputKwargs?: Record): Promise` + Executes a non-streaming entrypoint via HTTPS and returns deserialized output (matching the Python SDK semantics). + +- `runStream(inputKwargs?: Record): AsyncGenerator` + Streams chunks via WebSocket for entrypoints whose tags end with `_stream`. Throws if you call it on a non-stream entrypoint. + +- `getExtraParams(): Record | undefined` + Returns any metadata passed in `extraParams`. Reserved for forward compatibility. + +### Constructor precedence rules + +1. Explicit constructor values. +2. Environment variables (`RUNAGENT_API_KEY`, `RUNAGENT_BASE_URL`). +3. Defaults (remote base URL, 300 second timeout, registry disabled in browsers). + +--- + +## Security Best Practices + +- Never commit API keys to source control. Use environment variables or secret managers, even on the client side (e.g., provide short-lived tokens from your backend). +- Browsers cannot safely store long-lived keys. Prefer proxying via your backend or issuing ephemeral tokens per session. +- For Node environments, rely on OS-level key storage or `.env` files owned by your deployment platform. + +--- + +## Troubleshooting + +- **`Entrypoint ... is streaming. Use runStream()`** + Call `runStream()` for tags ending in `_stream`. + +- **`Unable to determine host/port` (local mode)** + Install `better-sqlite3` and ensure the agent was started through the CLI (`runagent serve ...`). Otherwise provide `host` and `port` manually. + +- **Authentication failures (401/403)** + Make sure `RUNAGENT_API_KEY` (or `apiKey` in the constructor) corresponds to an account with access to the agent. + +- **WebSocket connection fails in browser** + Confirm your environment allows WSS connections to `backend.run-agent.ai` and set `enableRegistry: false`. + +--- + +## Example Build Targets + +### Vite (Node backend + browser client) + +```ts +// vite.config.ts +export default defineConfig({ + define: { + 'process.env.RUNAGENT_API_KEY': JSON.stringify(process.env.RUNAGENT_API_KEY), + 'process.env.RUNAGENT_BASE_URL': JSON.stringify(process.env.RUNAGENT_BASE_URL), + }, +}); +``` + +### Serverless (Edge Functions, Cloudflare Workers, etc.) + +Edge runtimes are closer to browsers: disable the registry and pass host/base URLs explicitly. + +```ts +const client = new RunAgentClient({ + agentId: env.AGENT_ID, + entrypointTag: 'edge_entry', + apiKey: env.RUNAGENT_API_KEY, + enableRegistry: false, +}); +``` + +--- + +## Version Compatibility + +- SDK version `0.1.26` aligns with the CLI v1+ REST/WebSocket APIs (`/agents/{id}/run` & `/run-stream` routes). +- Breaking changes are communicated in the main RunAgent changelog; the TypeScript SDK follows semantic versioning. + +--- + +## Need Help? + +- Consult the main documentation in `docs/sdk/javascript/`. +- Join the RunAgent community channels or open an issue if you encounter integration problems that aren’t covered here. + diff --git a/runagent-ts/package.json b/runagent-ts/package.json index 45d1929..7ff82f2 100644 --- a/runagent-ts/package.json +++ b/runagent-ts/package.json @@ -34,14 +34,15 @@ "vite-plugin-dts": "^3.0.0" }, "peerDependencies": { - "ws": "^8.18.3" + "ws": "^8.18.3", + "better-sqlite3": "^12.2.0" }, "peerDependenciesMeta": { "ws": { "optional": true + }, + "better-sqlite3": { + "optional": true } - }, - "dependencies": { - "better-sqlite3": "^12.2.0" } } diff --git a/runagent-ts/pnpm-lock.yaml b/runagent-ts/pnpm-lock.yaml new file mode 100644 index 0000000..dd0a7c8 --- /dev/null +++ b/runagent-ts/pnpm-lock.yaml @@ -0,0 +1,1515 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + better-sqlite3: + specifier: ^12.2.0 + version: 12.4.1 + ws: + specifier: ^8.18.3 + version: 8.18.3 + devDependencies: + '@types/better-sqlite3': + specifier: ^7.6.13 + version: 7.6.13 + '@types/node': + specifier: ^24.0.14 + version: 24.10.1 + '@types/ws': + specifier: ^8.18.1 + version: 8.18.1 + typescript: + specifier: ^5.0.0 + version: 5.9.3 + vite: + specifier: ^5.0.0 + version: 5.4.21(@types/node@24.10.1) + vite-plugin-dts: + specifier: ^3.0.0 + version: 3.9.1(@types/node@24.10.1)(rollup@4.53.2)(typescript@5.9.3)(vite@5.4.21(@types/node@24.10.1)) + +packages: + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@microsoft/api-extractor-model@7.28.13': + resolution: {integrity: sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==} + + '@microsoft/api-extractor@7.43.0': + resolution: {integrity: sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==} + hasBin: true + + '@microsoft/tsdoc-config@0.16.2': + resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==} + + '@microsoft/tsdoc@0.14.2': + resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} + + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.53.2': + resolution: {integrity: sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.53.2': + resolution: {integrity: sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.53.2': + resolution: {integrity: sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.53.2': + resolution: {integrity: sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.53.2': + resolution: {integrity: sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.53.2': + resolution: {integrity: sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.53.2': + resolution: {integrity: sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.53.2': + resolution: {integrity: sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.53.2': + resolution: {integrity: sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.53.2': + resolution: {integrity: sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.53.2': + resolution: {integrity: sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.53.2': + resolution: {integrity: sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.53.2': + resolution: {integrity: sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.53.2': + resolution: {integrity: sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.53.2': + resolution: {integrity: sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.53.2': + resolution: {integrity: sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.53.2': + resolution: {integrity: sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.53.2': + resolution: {integrity: sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.53.2': + resolution: {integrity: sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.53.2': + resolution: {integrity: sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.53.2': + resolution: {integrity: sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.53.2': + resolution: {integrity: sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==} + cpu: [x64] + os: [win32] + + '@rushstack/node-core-library@4.0.2': + resolution: {integrity: sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/rig-package@0.5.2': + resolution: {integrity: sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==} + + '@rushstack/terminal@0.10.0': + resolution: {integrity: sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/ts-command-line@4.19.1': + resolution: {integrity: sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==} + + '@types/argparse@1.0.38': + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + + '@types/better-sqlite3@7.6.13': + resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/node@24.10.1': + resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@volar/language-core@1.11.1': + resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==} + + '@volar/source-map@1.11.1': + resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==} + + '@volar/typescript@1.11.1': + resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==} + + '@vue/compiler-core@3.5.24': + resolution: {integrity: sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==} + + '@vue/compiler-dom@3.5.24': + resolution: {integrity: sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==} + + '@vue/language-core@1.8.27': + resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/shared@3.5.24': + resolution: {integrity: sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==} + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + better-sqlite3@12.4.1: + resolution: {integrity: sha512-3yVdyZhklTiNrtg+4WqHpJpFDd+WHTg2oM7UcR80GqL05AOV0xEJzc6qNvFYoEtE+hRp1n9MpN6/+4yhlGkDXQ==} + engines: {node: 20.x || 22.x || 23.x || 24.x} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + + computeds@0.0.1: + resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + minimatch@3.0.8: + resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + muggle-string@0.3.1: + resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + + node-abi@3.80.0: + resolution: {integrity: sha512-LyPuZJcI9HVwzXK1GPxWNzrr+vr8Hp/3UqlmWxxh8p54U1ZbclOqbSog9lWHaCX+dBaiGi6n/hIX+mKu74GmPA==} + engines: {node: '>=10'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + hasBin: true + + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + resolve@1.19.0: + resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + rollup@4.53.2: + resolution: {integrity: sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tar-fs@2.1.4: + resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + + typescript@5.4.2: + resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + validator@13.15.23: + resolution: {integrity: sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==} + engines: {node: '>= 0.10'} + + vite-plugin-dts@3.9.1: + resolution: {integrity: sha512-rVp2KM9Ue22NGWB8dNtWEr+KekN3rIgz1tWD050QnRGlriUCmaDwa7qA5zDEjbXg5lAXhYMSBJtx3q3hQIJZSg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + typescript: '*' + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@5.4.21: + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vue-template-compiler@2.7.16: + resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} + + vue-tsc@1.8.27: + resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==} + hasBin: true + peerDependencies: + typescript: '*' + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + z-schema@5.0.5: + resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} + engines: {node: '>=8.0.0'} + hasBin: true + +snapshots: + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@microsoft/api-extractor-model@7.28.13(@types/node@24.10.1)': + dependencies: + '@microsoft/tsdoc': 0.14.2 + '@microsoft/tsdoc-config': 0.16.2 + '@rushstack/node-core-library': 4.0.2(@types/node@24.10.1) + transitivePeerDependencies: + - '@types/node' + + '@microsoft/api-extractor@7.43.0(@types/node@24.10.1)': + dependencies: + '@microsoft/api-extractor-model': 7.28.13(@types/node@24.10.1) + '@microsoft/tsdoc': 0.14.2 + '@microsoft/tsdoc-config': 0.16.2 + '@rushstack/node-core-library': 4.0.2(@types/node@24.10.1) + '@rushstack/rig-package': 0.5.2 + '@rushstack/terminal': 0.10.0(@types/node@24.10.1) + '@rushstack/ts-command-line': 4.19.1(@types/node@24.10.1) + lodash: 4.17.21 + minimatch: 3.0.8 + resolve: 1.22.11 + semver: 7.5.4 + source-map: 0.6.1 + typescript: 5.4.2 + transitivePeerDependencies: + - '@types/node' + + '@microsoft/tsdoc-config@0.16.2': + dependencies: + '@microsoft/tsdoc': 0.14.2 + ajv: 6.12.6 + jju: 1.4.0 + resolve: 1.19.0 + + '@microsoft/tsdoc@0.14.2': {} + + '@rollup/pluginutils@5.3.0(rollup@4.53.2)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.53.2 + + '@rollup/rollup-android-arm-eabi@4.53.2': + optional: true + + '@rollup/rollup-android-arm64@4.53.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.53.2': + optional: true + + '@rollup/rollup-darwin-x64@4.53.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.53.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.53.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.53.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.53.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.53.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.53.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.53.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.53.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.53.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.53.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.53.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.53.2': + optional: true + + '@rushstack/node-core-library@4.0.2(@types/node@24.10.1)': + dependencies: + fs-extra: 7.0.1 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.11 + semver: 7.5.4 + z-schema: 5.0.5 + optionalDependencies: + '@types/node': 24.10.1 + + '@rushstack/rig-package@0.5.2': + dependencies: + resolve: 1.22.11 + strip-json-comments: 3.1.1 + + '@rushstack/terminal@0.10.0(@types/node@24.10.1)': + dependencies: + '@rushstack/node-core-library': 4.0.2(@types/node@24.10.1) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 24.10.1 + + '@rushstack/ts-command-line@4.19.1(@types/node@24.10.1)': + dependencies: + '@rushstack/terminal': 0.10.0(@types/node@24.10.1) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + + '@types/argparse@1.0.38': {} + + '@types/better-sqlite3@7.6.13': + dependencies: + '@types/node': 24.10.1 + + '@types/estree@1.0.8': {} + + '@types/node@24.10.1': + dependencies: + undici-types: 7.16.0 + + '@types/ws@8.18.1': + dependencies: + '@types/node': 24.10.1 + + '@volar/language-core@1.11.1': + dependencies: + '@volar/source-map': 1.11.1 + + '@volar/source-map@1.11.1': + dependencies: + muggle-string: 0.3.1 + + '@volar/typescript@1.11.1': + dependencies: + '@volar/language-core': 1.11.1 + path-browserify: 1.0.1 + + '@vue/compiler-core@3.5.24': + dependencies: + '@babel/parser': 7.28.5 + '@vue/shared': 3.5.24 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.24': + dependencies: + '@vue/compiler-core': 3.5.24 + '@vue/shared': 3.5.24 + + '@vue/language-core@1.8.27(typescript@5.9.3)': + dependencies: + '@volar/language-core': 1.11.1 + '@volar/source-map': 1.11.1 + '@vue/compiler-dom': 3.5.24 + '@vue/shared': 3.5.24 + computeds: 0.0.1 + minimatch: 9.0.5 + muggle-string: 0.3.1 + path-browserify: 1.0.1 + vue-template-compiler: 2.7.16 + optionalDependencies: + typescript: 5.9.3 + + '@vue/shared@3.5.24': {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + better-sqlite3@12.4.1: + dependencies: + bindings: 1.5.0 + prebuild-install: 7.1.3 + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + chownr@1.1.4: {} + + commander@9.5.0: + optional: true + + computeds@0.0.1: {} + + concat-map@0.0.1: {} + + de-indent@1.0.2: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + deep-extend@0.6.0: {} + + detect-libc@2.1.2: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + entities@4.5.0: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + estree-walker@2.0.2: {} + + expand-template@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + file-uri-to-path@1.0.0: {} + + fs-constants@1.0.0: {} + + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + github-from-package@0.0.0: {} + + graceful-fs@4.2.11: {} + + has-flag@4.0.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + ieee754@1.2.1: {} + + import-lazy@4.0.0: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + jju@1.4.0: {} + + json-schema-traverse@0.4.1: {} + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + kolorist@1.8.0: {} + + lodash.get@4.4.2: {} + + lodash.isequal@4.5.0: {} + + lodash@4.17.21: {} + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + mimic-response@3.1.0: {} + + minimatch@3.0.8: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + mkdirp-classic@0.5.3: {} + + ms@2.1.3: {} + + muggle-string@0.3.1: {} + + nanoid@3.3.11: {} + + napi-build-utils@2.0.0: {} + + node-abi@3.80.0: + dependencies: + semver: 7.7.3 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + path-browserify@1.0.1: {} + + path-parse@1.0.7: {} + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.1.2 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.80.0 + pump: 3.0.3 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.4 + tunnel-agent: 0.6.0 + + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + punycode@2.3.1: {} + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + resolve@1.19.0: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rollup@4.53.2: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.53.2 + '@rollup/rollup-android-arm64': 4.53.2 + '@rollup/rollup-darwin-arm64': 4.53.2 + '@rollup/rollup-darwin-x64': 4.53.2 + '@rollup/rollup-freebsd-arm64': 4.53.2 + '@rollup/rollup-freebsd-x64': 4.53.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.2 + '@rollup/rollup-linux-arm-musleabihf': 4.53.2 + '@rollup/rollup-linux-arm64-gnu': 4.53.2 + '@rollup/rollup-linux-arm64-musl': 4.53.2 + '@rollup/rollup-linux-loong64-gnu': 4.53.2 + '@rollup/rollup-linux-ppc64-gnu': 4.53.2 + '@rollup/rollup-linux-riscv64-gnu': 4.53.2 + '@rollup/rollup-linux-riscv64-musl': 4.53.2 + '@rollup/rollup-linux-s390x-gnu': 4.53.2 + '@rollup/rollup-linux-x64-gnu': 4.53.2 + '@rollup/rollup-linux-x64-musl': 4.53.2 + '@rollup/rollup-openharmony-arm64': 4.53.2 + '@rollup/rollup-win32-arm64-msvc': 4.53.2 + '@rollup/rollup-win32-ia32-msvc': 4.53.2 + '@rollup/rollup-win32-x64-gnu': 4.53.2 + '@rollup/rollup-win32-x64-msvc': 4.53.2 + fsevents: 2.3.3 + + safe-buffer@5.2.1: {} + + semver@7.5.4: + dependencies: + lru-cache: 6.0.0 + + semver@7.7.3: {} + + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + + source-map-js@1.2.1: {} + + source-map@0.6.1: {} + + sprintf-js@1.0.3: {} + + string-argv@0.3.2: {} + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-json-comments@2.0.1: {} + + strip-json-comments@3.1.1: {} + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tar-fs@2.1.4: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.3 + tar-stream: 2.2.0 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + + typescript@5.4.2: {} + + typescript@5.9.3: {} + + undici-types@7.16.0: {} + + universalify@0.1.2: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + validator@13.15.23: {} + + vite-plugin-dts@3.9.1(@types/node@24.10.1)(rollup@4.53.2)(typescript@5.9.3)(vite@5.4.21(@types/node@24.10.1)): + dependencies: + '@microsoft/api-extractor': 7.43.0(@types/node@24.10.1) + '@rollup/pluginutils': 5.3.0(rollup@4.53.2) + '@vue/language-core': 1.8.27(typescript@5.9.3) + debug: 4.4.3 + kolorist: 1.8.0 + magic-string: 0.30.21 + typescript: 5.9.3 + vue-tsc: 1.8.27(typescript@5.9.3) + optionalDependencies: + vite: 5.4.21(@types/node@24.10.1) + transitivePeerDependencies: + - '@types/node' + - rollup + - supports-color + + vite@5.4.21(@types/node@24.10.1): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.6 + rollup: 4.53.2 + optionalDependencies: + '@types/node': 24.10.1 + fsevents: 2.3.3 + + vue-template-compiler@2.7.16: + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + vue-tsc@1.8.27(typescript@5.9.3): + dependencies: + '@volar/typescript': 1.11.1 + '@vue/language-core': 1.8.27(typescript@5.9.3) + semver: 7.7.3 + typescript: 5.9.3 + + wrappy@1.0.2: {} + + ws@8.18.3: {} + + yallist@4.0.0: {} + + z-schema@5.0.5: + dependencies: + lodash.get: 4.4.2 + lodash.isequal: 4.5.0 + validator: 13.15.23 + optionalDependencies: + commander: 9.5.0 diff --git a/runagent-ts/runagent-0.1.26.tgz b/runagent-ts/runagent-0.1.26.tgz new file mode 100644 index 0000000000000000000000000000000000000000..47815bb48519f1c2b6841e02ecb172ee588ab941 GIT binary patch literal 49349 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 literal 0 HcmV?d00001 diff --git a/runagent-ts/src/client/index.ts b/runagent-ts/src/client/index.ts index 6e1c82e..0dad822 100644 --- a/runagent-ts/src/client/index.ts +++ b/runagent-ts/src/client/index.ts @@ -1,8 +1,8 @@ -declare const __IS_NODE__: boolean; -declare const __IS_BROWSER__: boolean; +declare const __IS_NODE__: boolean | undefined; +declare const __IS_BROWSER__: boolean | undefined; -const isNode = __IS_NODE__; -const isBrowser = __IS_BROWSER__; +const isNode = Boolean(__IS_NODE__); +const isBrowser = Boolean(__IS_BROWSER__); import { RestClient } from '../rest/index.js'; import { BrowserWebSocketClient } from '../websocket/browser.js'; @@ -13,65 +13,120 @@ import type { AgentArchitecture, JsonValue, } from '../types/index.js'; -import { RunAgentRegistry } from '../database/index.js'; - -// Environment detection -// const isNode = (() => { -// try { -// return ( -// typeof process !== 'undefined' && -// typeof process.versions !== 'undefined' && -// typeof process.versions.node !== 'undefined' -// ); -// } catch { -// return false; -// } -// })(); - -// const isBrowser = (() => { -// try { -// // eslint-disable-next-line @typescript-eslint/no-explicit-any -// return typeof (globalThis as any).window !== 'undefined'; -// } catch { -// return false; -// } -// })(); +import type { RunAgentRegistry } from '../database/index.js'; type WebSocketClientType = BrowserWebSocketClient | NodeWebSocketClient; +const DEFAULT_REMOTE_BASE_URL = 'https://backend.run-agent.ai'; +const DEFAULT_API_PREFIX = '/api/v1'; +const DEFAULT_TIMEOUT_SECONDS = 300; +const API_KEY_ENV = 'RUNAGENT_API_KEY'; +const BASE_URL_ENV = 'RUNAGENT_BASE_URL'; + +interface ClientEndpoints { + restBaseUrl: string; + socketBaseUrl: string; + apiPrefix: string; + isLocal: boolean; +} + +const sanitizeBaseUrl = (baseUrl: string): string => + baseUrl.replace(/\/$/, ''); + +const toWebSocketBase = (baseUrl: string): string => { + if (baseUrl.startsWith('wss://') || baseUrl.startsWith('ws://')) { + return sanitizeBaseUrl(baseUrl); + } + if (baseUrl.startsWith('https://')) { + return sanitizeBaseUrl(baseUrl.replace(/^https:\/\//, 'wss://')); + } + if (baseUrl.startsWith('http://')) { + return sanitizeBaseUrl(baseUrl.replace(/^http:\/\//, 'ws://')); + } + return `wss://${sanitizeBaseUrl(baseUrl)}`; +}; + +const getEnvVar = (name: string): string | undefined => { + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const globalProcessEnv = (globalThis as any)?.process?.env; + if (globalProcessEnv && typeof globalProcessEnv[name] === 'string') { + return globalProcessEnv[name] as string; + } + } catch { + // Ignore environment probing errors + } + + try { + // Allow browser builds to inject env via globalThis.RUNAGENT_ENV + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const runtimeEnv = (globalThis as any)?.RUNAGENT_ENV; + if (runtimeEnv && typeof runtimeEnv[name] === 'string') { + return runtimeEnv[name] as string; + } + } catch { + // Ignore missing global object + } + + return undefined; +}; + export class RunAgentClient { private serializer: CoreSerializer; private local: boolean; private agentId: string; private entrypointTag: string; - private config: RunAgentConfig; // Store original config - // private apiKey?: string; - private restClient!: RestClient; // Will be initialized in initialize() - private socketClient!: WebSocketClientType; // Will be initialized in initialize() + private config: RunAgentConfig; + private restClient?: RestClient; + private socketClient?: WebSocketClientType; private agentArchitecture?: AgentArchitecture; private static registry: RunAgentRegistry | null = null; - private static registryInitialized: boolean = false; + private static registryInitialized = false; + private initialized = false; + private apiKey?: string; + private baseUrl?: string; + private baseSocketUrl?: string; + private timeoutSeconds: number; + private extraParams?: Record; + private enableRegistry: boolean; - /** - * Get or create registry instance (Node.js only) - */ - private static async getRegistry(): Promise { - if (isBrowser) { - return null; // Registry not available in browser + constructor(config: RunAgentConfig) { + this.serializer = new CoreSerializer(); + this.config = config; + this.agentId = config.agentId; + this.entrypointTag = config.entrypointTag; + this.local = config.local ?? false; + this.timeoutSeconds = config.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS; + this.extraParams = config.extraParams; + this.enableRegistry = config.enableRegistry ?? isNode; + + this.apiKey = config.apiKey ?? getEnvVar(API_KEY_ENV); + this.baseUrl = config.baseUrl ?? getEnvVar(BASE_URL_ENV); + this.baseSocketUrl = config.baseSocketUrl; + + if (!this.baseUrl && !this.local) { + this.baseUrl = DEFAULT_REMOTE_BASE_URL; + } + } + + // ------------------------------------------------------------------ + // Initialization helpers + // ------------------------------------------------------------------ + + private static async getRegistry( + enableRegistry: boolean + ): Promise { + if (!enableRegistry || !isNode) { + return null; } if (!this.registry && !this.registryInitialized) { try { - // Check if registry features are available - const isAvailable = await RunAgentRegistry.isAvailable(); - if (isAvailable) { + const { RunAgentRegistry } = await import('../database/index.js'); + const available = await RunAgentRegistry.isAvailable(); + if (available) { this.registry = new RunAgentRegistry(); await this.registry.initialize(); - console.log('🗃️ Agent registry initialized successfully'); - } else { - console.log( - '📋 Agent registry unavailable (better-sqlite3 not installed)' - ); } } catch (error) { console.warn('📋 Failed to initialize agent registry:', error); @@ -83,14 +138,11 @@ export class RunAgentClient { return this.registry; } - /** - * Try to lookup agent in registry, with graceful fallback - */ private async lookupAgent( agentId: string ): Promise<{ host: string; port: number } | null> { try { - const registry = await RunAgentClient.getRegistry(); + const registry = await RunAgentClient.getRegistry(this.enableRegistry); if (registry) { return registry.lookupAgent(agentId); } @@ -100,77 +152,112 @@ export class RunAgentClient { return null; } - constructor(config: RunAgentConfig) { - this.serializer = new CoreSerializer(); - this.local = config.local ?? false; - this.agentId = config.agentId; - this.entrypointTag = config.entrypointTag; - this.config = config; // Store original config - // this.apiKey = config.apiKey; + private initializeClients(endpoints: ClientEndpoints): void { + this.restClient = new RestClient({ + baseUrl: endpoints.restBaseUrl, + apiKey: endpoints.isLocal ? undefined : this.apiKey, + apiPrefix: endpoints.apiPrefix, + isLocal: endpoints.isLocal, + timeoutSeconds: this.timeoutSeconds, + }); - if (!this.local) { - throw new Error( - 'Non-local (remote) RunAgent deployment is not available yet. Coming soon.' - ); - } + const socketConfig = { + baseSocketUrl: endpoints.socketBaseUrl, + apiKey: endpoints.isLocal ? undefined : this.apiKey, + apiPrefix: endpoints.apiPrefix, + isLocal: endpoints.isLocal, + timeoutSeconds: this.timeoutSeconds, + }; + + this.socketClient = isNode + ? new NodeWebSocketClient(socketConfig) + : new BrowserWebSocketClient(socketConfig); + } - // For browser, initialize clients immediately if host/port provided - if (isBrowser && config.host && config.port) { - this.initializeClients(config.host, config.port); + private async ensureInitialized(): Promise { + if (!this.initialized) { + await this.initialize(); } } async initialize(): Promise { - // Step 1: Determine connection details - let agentHost: string; - let agentPort: number; + if (this.initialized) { + return this; + } - if (isBrowser) { - // Browser: host + port are mandatory for local mode - if (!this.config.host || !this.config.port) { - throw new Error( - "For browser environments, both 'host' and 'port' are required when local=true" + const apiPrefix = this.config.apiPrefix ?? DEFAULT_API_PREFIX; + + if (this.local) { + const resolved = await this.resolveLocalConnection(); + this.initializeClients({ + restBaseUrl: resolved.baseUrl, + socketBaseUrl: resolved.socketBaseUrl, + apiPrefix, + isLocal: true, + }); + } else { + const resolvedBaseUrl = sanitizeBaseUrl( + this.baseUrl ?? DEFAULT_REMOTE_BASE_URL + ); + let socketBaseUrl = sanitizeBaseUrl( + this.baseSocketUrl ?? toWebSocketBase(resolvedBaseUrl) + ); + + if (socketBaseUrl.endsWith(apiPrefix)) { + socketBaseUrl = sanitizeBaseUrl( + socketBaseUrl.slice(0, socketBaseUrl.length - apiPrefix.length) ); } - agentHost = this.config.host; - agentPort = this.config.port; - console.log(`🌐 Browser mode - connecting to: ${agentHost}:${agentPort}`); - // Initialize clients if not already done - if (!this.restClient) { - this.initializeClients(agentHost, agentPort); + if (!socketBaseUrl) { + socketBaseUrl = toWebSocketBase(resolvedBaseUrl); } - } else { - // Node.js: support both direct connection and agent discovery - if (this.config.host && this.config.port) { - // Direct connection mode - agentHost = this.config.host; - agentPort = this.config.port; - console.log(`🔌 Direct connection: ${agentHost}:${agentPort}`); - } else { - // Try agent discovery first - const agentInfo = await this.lookupAgent(this.agentId); - - if (agentInfo) { - agentHost = agentInfo.host; - agentPort = agentInfo.port; - console.log( - `🔍 Discovered agent ${this.agentId}: ${agentHost}:${agentPort}` - ); - } else { - // No registry lookup successful - require explicit config - throw new Error( - `Failed to discover agent ${this.agentId}. ` + - `Please provide explicit 'host' and 'port' in config, or ensure agent is registered.` - ); - } + + this.initializeClients({ + restBaseUrl: resolvedBaseUrl, + socketBaseUrl, + apiPrefix, + isLocal: false, + }); + } + + await this.loadAgentMetadata(); + this.initialized = true; + return this; + } + + private async resolveLocalConnection(): Promise<{ + baseUrl: string; + socketBaseUrl: string; + }> { + let host = this.config.host; + let port = this.config.port; + + if (!host || !port) { + const info = await this.lookupAgent(this.agentId); + if (info) { + host = info.host; + port = info.port; } + } - // Initialize clients - this.initializeClients(agentHost, agentPort); + if (!host || !port) { + throw new Error( + `Unable to determine host/port for local agent ${this.agentId}. ` + + `Provide 'host' and 'port' in RunAgentClient config or register the agent locally.` + ); } - // Step 2: Get agent architecture + const baseUrl = sanitizeBaseUrl(`http://${host}:${port}`); + const socketBaseUrl = sanitizeBaseUrl(`ws://${host}:${port}`); + + return { baseUrl, socketBaseUrl }; + } + + private async loadAgentMetadata(): Promise { + if (!this.restClient) { + throw new Error('REST client not initialized'); + } try { this.agentArchitecture = await this.restClient.getAgentArchitecture( this.agentId @@ -185,47 +272,62 @@ export class RunAgentClient { `Entrypoint \`${this.entrypointTag}\` not found in agent ${this.agentId}` ); } - - console.log(`✅ Agent ${this.agentId} initialized successfully`); - return this; } catch (error) { console.error('❌ Failed to initialize agent:', error); throw error; } } - /** - * Initialize REST and WebSocket clients - */ - private initializeClients(host: string, port: number): void { - const agentBaseUrl = `http://${host}:${port}`; - const agentSocketUrl = `ws://${host}:${port}`; + // ------------------------------------------------------------------ + // Public execution methods + // ------------------------------------------------------------------ - this.restClient = new RestClient({ - baseUrl: agentBaseUrl, - apiPrefix: '/api/v1', - }); + async run( + inputKwargs: Record = {} + ): Promise { + await this.ensureInitialized(); - if (isNode) { - this.socketClient = new NodeWebSocketClient({ - baseSocketUrl: agentSocketUrl, - apiPrefix: '/api/v1', - }); - } else { - this.socketClient = new BrowserWebSocketClient({ - baseSocketUrl: agentSocketUrl, - apiPrefix: '/api/v1', - }); + if (this.entrypointTag.endsWith('_stream')) { + throw new Error( + `Entrypoint \`${this.entrypointTag}\` is streaming. Use runStream() instead.` + ); } + + return this.executeRun(inputKwargs); } - private async _run(inputKwargs: Record): Promise { + async *runStream( + inputKwargs: Record = {} + ): AsyncGenerator { + await this.ensureInitialized(); + + if (!this.entrypointTag.endsWith('_stream')) { + throw new Error( + `Entrypoint \`${this.entrypointTag}\` is not streaming. Use run() instead.` + ); + } + + yield* this.executeStream(inputKwargs); + } + + // ------------------------------------------------------------------ + // Internal execution helpers + // ------------------------------------------------------------------ + + private async executeRun( + inputKwargs: Record + ): Promise { + if (!this.restClient) { + throw new Error('REST client not initialized'); + } + const response = await this.restClient.runAgent( this.agentId, this.entrypointTag, { inputArgs: [], - inputKwargs: inputKwargs, + inputKwargs, + timeoutSeconds: this.timeoutSeconds, } ); @@ -259,32 +361,34 @@ export class RunAgentClient { const errorMessage = typeof response.error === 'string' ? response.error - : (response.error as { message?: string })?.message ?? 'Agent execution failed'; + : (response.error as { message?: string })?.message ?? + 'Agent execution failed'; throw new Error(errorMessage); } - private async *_runStream( + private executeStream( inputKwargs: Record ): AsyncGenerator { - yield* this.socketClient.runStream(this.agentId, this.entrypointTag, { - inputArgs: [], - inputKwargs: inputKwargs, - }); - } - - // Main run method - matches your Python interface exactly! - async run( - inputKwargs: Record - ): Promise> { - if (this.entrypointTag.endsWith('_stream')) { - return this._runStream(inputKwargs); - } else { - return this._run(inputKwargs); + if (!this.socketClient) { + throw new Error('WebSocket client not initialized'); } + + return this.socketClient.runStream( + this.agentId, + this.entrypointTag, + { + inputArgs: [], + inputKwargs, + timeoutSeconds: this.timeoutSeconds, + } + ); } - // Utility methods + // ------------------------------------------------------------------ + // Utility getters + // ------------------------------------------------------------------ + get environment(): 'node' | 'browser' { return isNode ? 'node' : 'browser'; } @@ -297,18 +401,27 @@ export class RunAgentClient { return isBrowser; } + getExtraParams(): Record | undefined { + return this.extraParams; + } + /** - * Check if registry features are available + * Check if registry features are available. */ static async hasRegistry(): Promise { - if (isBrowser) return false; - return await RunAgentRegistry.isAvailable(); + if (!isNode) return false; + try { + const { RunAgentRegistry } = await import('../database/index.js'); + return await RunAgentRegistry.isAvailable(); + } catch { + return false; + } } /** - * Get registry instance (for advanced users) + * Get registry instance (for advanced Node.js users). */ static async getRegistryInstance(): Promise { - return await this.getRegistry(); + return await this.getRegistry(true); } } diff --git a/runagent-ts/src/http/index.ts b/runagent-ts/src/http/index.ts index a6ce79c..fdeafcb 100644 --- a/runagent-ts/src/http/index.ts +++ b/runagent-ts/src/http/index.ts @@ -27,25 +27,25 @@ import { // function fetch(url: string, init?: RequestInit): Promise; // } - export class HttpHandler { - private apiKey?: string; - private baseUrl: string; - private defaultHeaders: Record; - - constructor(apiKey?: string, baseUrl: string = '') { - this.apiKey = apiKey; - this.baseUrl = baseUrl.replace(/\/$/, ''); - - this.defaultHeaders = { - accept: 'application/json', - 'content-type': 'application/json', - }; - - if (this.apiKey) { - this.defaultHeaders['Authorization'] = `Bearer ${this.apiKey}`; - this.defaultHeaders['User-Agent'] = 'RunAgent-TS/1.0'; - } +export class HttpHandler { + private apiKey?: string; + private baseUrl: string; + private defaultHeaders: Record; + + constructor(apiKey?: string, baseUrl: string = '', _isLocal = true) { + this.apiKey = apiKey; + this.baseUrl = baseUrl.replace(/\/$/, ''); + + this.defaultHeaders = { + accept: 'application/json', + 'content-type': 'application/json', + 'User-Agent': 'RunAgent-TS/1.0', + }; + + if (this.apiKey) { + this.defaultHeaders['Authorization'] = `Bearer ${this.apiKey}`; } + } private _getUrl(path: string): string { return `${this.baseUrl}/${path.replace(/^\//, '')}`; diff --git a/runagent-ts/src/rest/index.ts b/runagent-ts/src/rest/index.ts index 48bc318..d8176a5 100644 --- a/runagent-ts/src/rest/index.ts +++ b/runagent-ts/src/rest/index.ts @@ -1,29 +1,46 @@ import { HttpHandler } from '../http/index.js'; +import { + AuthenticationError, + ClientError, + ConnectionError, + ServerError, + ValidationError, +} from '../errors/index.js'; import type { ApiResponse, AgentArchitecture, JsonValue } from '../types/index.js'; interface RunAgentOptions { inputArgs?: unknown[]; inputKwargs?: Record; - executionType?: string; + timeoutSeconds?: number; } interface RestClientConfig { baseUrl?: string; apiKey?: string; apiPrefix?: string; + isLocal?: boolean; + timeoutSeconds?: number; } export class RestClient { private http: HttpHandler; private baseUrl: string; private apiKey?: string; + private defaultTimeoutSeconds: number; constructor(options: RestClientConfig = {}) { - const { baseUrl = 'http://localhost:8080', apiKey, apiPrefix = '/api/v1' } = options; - + const { + baseUrl = 'http://localhost:8080', + apiKey, + apiPrefix = '/api/v1', + isLocal = true, + timeoutSeconds = 300, + } = options; + this.baseUrl = baseUrl.replace(/\/$/, '') + apiPrefix; this.apiKey = apiKey; - this.http = new HttpHandler(this.apiKey, this.baseUrl); + this.defaultTimeoutSeconds = timeoutSeconds; + this.http = new HttpHandler(this.apiKey, this.baseUrl, isLocal); } async runAgent( @@ -31,45 +48,92 @@ export class RestClient { entrypointTag: string, options: RunAgentOptions = {} ): Promise { - const { inputArgs = null, inputKwargs = null } = options; + const { + inputArgs = [], + inputKwargs = {}, + timeoutSeconds = this.defaultTimeoutSeconds, + } = options; + + const requestData = { + entrypoint_tag: entrypointTag, + input_args: inputArgs, + input_kwargs: inputKwargs, + timeout_seconds: timeoutSeconds, + async_execution: false, + } as JsonValue; + + const timeoutMs = timeoutSeconds * 1000 + 10_000; // Add buffer similar to Python SDK try { - console.log(`🤖 Executing agent: ${agentId}`); + const response = await this.http.post( + `/agents/${agentId}/run`, + requestData, + { timeout: timeoutMs } + ); - const requestData = { - input_data: { - input_args: inputArgs, - input_kwargs: inputKwargs, - }, - } as JsonValue; - - - try { - const response = await this.http.post( - `/agents/${agentId}/execute/${entrypointTag}`, - requestData, - { timeout: 120000 } - ); - - const result = await response.json() as ApiResponse; - - if (result.success !== false) { - console.log('✅ Agent execution completed!'); - return result; - } else { - console.log(`❌ Agent execution failed: ${result.error || 'Unknown error'}`); - return result; - } - } catch (error) { + return (await response.json()) as ApiResponse; + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : 'Unknown error'; + + if (error instanceof AuthenticationError) { + const code = + error.statusCode === 403 ? 'PERMISSION_ERROR' : 'AUTHENTICATION_ERROR'; return { success: false, - error: `Agent execution failed: ${error instanceof Error ? error.message : 'Unknown error'}`, + error: { + code, + message: errorMessage, + }, }; } - } catch (error) { + + if (error instanceof ValidationError) { + return { + success: false, + error: { + code: 'VALIDATION_ERROR', + message: errorMessage, + }, + }; + } + + if (error instanceof ConnectionError) { + return { + success: false, + error: { + code: 'CONNECTION_ERROR', + message: errorMessage, + }, + }; + } + + if (error instanceof ServerError) { + return { + success: false, + error: { + code: 'SERVER_ERROR', + message: errorMessage, + }, + }; + } + + if (error instanceof ClientError) { + return { + success: false, + error: { + code: 'CLIENT_ERROR', + message: errorMessage, + }, + }; + } + return { success: false, - error: `Execute agent failed: ${error instanceof Error ? error.message : 'Unknown error'}`, + error: { + code: 'UNKNOWN_ERROR', + message: errorMessage, + }, }; } } @@ -77,9 +141,13 @@ export class RestClient { async getAgentArchitecture(agentId: string): Promise { try { const response = await this.http.get(`/agents/${agentId}/architecture`); - return response.json() as Promise; + return (await response.json()) as AgentArchitecture; } catch (error) { - throw new Error(`Failed to get architecture: ${error instanceof Error ? error.message : 'Unknown error'}`); + throw new Error( + `Failed to get architecture: ${ + error instanceof Error ? error.message : 'Unknown error' + }` + ); } } diff --git a/runagent-ts/src/types/index.ts b/runagent-ts/src/types/index.ts index 40148f4..82fc24c 100644 --- a/runagent-ts/src/types/index.ts +++ b/runagent-ts/src/types/index.ts @@ -1,14 +1,17 @@ export interface RunAgentConfig { - agentId: string; - entrypointTag: string; - local?: boolean; - host?: string; - port?: number; - apiKey?: string; - baseUrl?: string; - baseSocketUrl?: string; - apiPrefix?: string; - } + agentId: string; + entrypointTag: string; + local?: boolean; + host?: string; + port?: number; + apiKey?: string; + baseUrl?: string; + baseSocketUrl?: string; + apiPrefix?: string; + timeoutSeconds?: number; + extraParams?: Record; + enableRegistry?: boolean; +} export interface ApiResponse { success: boolean; @@ -45,13 +48,12 @@ export interface AgentArchitecture { } export interface ExecutionRequest { - action: string; - agent_id: string; - input_data: { - input_args: unknown[]; - input_kwargs: Record; - }; - } + entrypoint_tag: string; + input_args: unknown[]; + input_kwargs: Record; + timeout_seconds?: number; + async_execution?: boolean; +} export interface SerializedObject { content: unknown; diff --git a/runagent-ts/src/websocket/base.ts b/runagent-ts/src/websocket/base.ts index 6989ffc..bb1e77c 100644 --- a/runagent-ts/src/websocket/base.ts +++ b/runagent-ts/src/websocket/base.ts @@ -1,64 +1,91 @@ import { CoreSerializer } from '../serializer/index.js'; -import type { WebSocketMessage, ExecutionRequest } from '../types/index.js'; +import type { ExecutionRequest, JsonValue } from '../types/index.js'; interface WebSocketConfig { baseSocketUrl?: string; apiKey?: string; apiPrefix?: string; + isLocal?: boolean; + timeoutSeconds?: number; } interface RunStreamOptions { inputArgs?: unknown[]; inputKwargs?: Record; + timeoutSeconds?: number; +} + +interface StreamMessage { + type: string; + status?: string; + payload?: unknown; + error?: unknown; } export abstract class BaseWebSocketClient { protected serializer: CoreSerializer; protected baseSocketUrl: string; protected apiKey?: string; + protected apiPrefix: string; + protected isLocal: boolean; + protected timeoutSeconds: number; constructor(config: WebSocketConfig) { - const { baseSocketUrl = 'ws://localhost:8080', apiKey, apiPrefix = '/api/v1' } = config; - + const { + baseSocketUrl = 'ws://localhost:8080', + apiKey, + apiPrefix = '/api/v1', + isLocal = true, + timeoutSeconds = 300, + } = config; + this.baseSocketUrl = baseSocketUrl.replace(/\/$/, '') + apiPrefix; this.apiKey = apiKey; this.serializer = new CoreSerializer(); + this.apiPrefix = apiPrefix; + this.isLocal = isLocal; + this.timeoutSeconds = timeoutSeconds; } - abstract createWebSocket(url: string): unknown | Promise; + abstract createWebSocket( + url: string, + headers?: Record + ): unknown | Promise; async *runStream( agentId: string, entrypointTag: string, options: RunStreamOptions = {} ): AsyncGenerator { - const { inputArgs = null, inputKwargs = null } = options; - const uri = `${this.baseSocketUrl}/agents/${agentId}/execute/${entrypointTag}`; + const { inputArgs = null, inputKwargs = null, timeoutSeconds } = options; + const endpoint = `${this.baseSocketUrl}/agents/${agentId}/run-stream`; + + let uri = endpoint; + if (!this.isLocal && this.apiKey) { + const separator = endpoint.includes('?') ? '&' : '?'; + uri = `${endpoint}${separator}token=${encodeURIComponent(this.apiKey)}`; + } + + const headers = + !this.isLocal && this.apiKey + ? { Authorization: `Bearer ${this.apiKey}` } + : undefined; let websocket: unknown; try { - websocket = await Promise.resolve(this.createWebSocket(uri)); - await this.waitForConnection(websocket); + websocket = await Promise.resolve(this.createWebSocket(uri, headers)); + await this.waitForConnection(websocket); const request: ExecutionRequest = { - action: 'start_stream', - agent_id: agentId, - input_data: { - input_args: inputArgs || [], - input_kwargs: inputKwargs || {}, - }, + entrypoint_tag: entrypointTag, + input_args: inputArgs || [], + input_kwargs: inputKwargs || {}, + timeout_seconds: timeoutSeconds ?? this.timeoutSeconds, + async_execution: false, }; - const startMsg: Partial = { - id: 'stream_start', - type: 'status', - timestamp: new Date().toISOString(), - data: request, - }; - - const serializedMsg = this.serializer.serializeMessage(startMsg); - this.sendMessage(websocket, serializedMsg); + this.sendMessage(websocket, JSON.stringify(request)); yield* this.createWebSocketIterator(websocket); } finally { @@ -73,4 +100,116 @@ export abstract class BaseWebSocketClient { protected abstract isWebSocketOpen(websocket: unknown): boolean; protected abstract closeWebSocket(websocket: unknown): void; protected abstract createWebSocketIterator(websocket: unknown): AsyncGenerator; + + protected parseStreamMessage(raw: string): StreamMessage { + let parsed: unknown; + try { + parsed = JSON.parse(raw); + } catch { + return { type: 'data', payload: raw }; + } + + if (parsed === null || typeof parsed !== 'object') { + return { type: 'data', payload: parsed }; + } + + const message = parsed as Record; + const type = + typeof message.type === 'string' + ? message.type.toLowerCase() + : 'data'; + + const status = + typeof message.status === 'string' + ? message.status + : typeof message.data === 'object' && + message.data !== null && + typeof (message.data as Record).status === 'string' + ? ((message.data as Record).status as string) + : undefined; + + const error = + message.error ?? + message.detail ?? + (typeof message.data === 'object' && + message.data !== null && + 'error' in (message.data as Record) + ? (message.data as Record).error + : undefined); + + const payload = + message.content ?? + message.data ?? + message.payload ?? + message.delta ?? + undefined; + + return { type, status, payload, error }; + } + + protected cleanErrorMessage(error: unknown): string { + if (!error) { + return 'Unknown error'; + } + + let message = + typeof error === 'string' + ? error + : error instanceof Error + ? error.message + : JSON.stringify(error); + + if (!message) { + return 'Unknown error'; + } + + const prefixes = [ + 'Streaming error: ', + 'Stream error: ', + 'Internal server error: ', + 'Server error: ', + 'Database error: ', + 'HTTP Error: ', + 'Error: ', + ]; + + for (const prefix of prefixes) { + if (message.startsWith(prefix)) { + message = message.slice(prefix.length).trim(); + } + } + + message = message.replace(/^\d{3}:\s*/, ''); + + const duplicateIndex = message.toLowerCase().indexOf('; then sent'); + if (duplicateIndex !== -1) { + message = message.slice(0, duplicateIndex).trim(); + } + + return message.trim() || 'Unknown error'; + } + + protected deserializeStreamPayload(payload: unknown): unknown { + if (payload === null || payload === undefined) { + return payload; + } + + if (typeof payload === 'string') { + try { + return this.serializer.deserializeObject(payload); + } catch { + return payload; + } + } + + if (typeof payload === 'object') { + try { + return this.serializer.deserializeObject(payload as JsonValue); + } catch { + return payload; + } + } + + return payload; + } } \ No newline at end of file diff --git a/runagent-ts/src/websocket/browser.ts b/runagent-ts/src/websocket/browser.ts index 7c813dd..03b1f59 100644 --- a/runagent-ts/src/websocket/browser.ts +++ b/runagent-ts/src/websocket/browser.ts @@ -38,7 +38,10 @@ interface IteratorResolverItem { } export class BrowserWebSocketClient extends BaseWebSocketClient { - createWebSocket(url: string): WebSocket { + createWebSocket( + url: string, + _headers?: Record + ): WebSocket { return new WebSocket(url); } @@ -93,39 +96,31 @@ export class BrowserWebSocketClient extends BaseWebSocketClient { const messageHandler = (event: MessageEvent) => { try { - console.log('received=> ', event.data); - const safeMsg = this.serializer.deserializeMessage(event.data as string); + const streamMessage = this.parseStreamMessage(event.data as string); - if (safeMsg.error) { - error = new Error(`Stream error: ${safeMsg.error}`); - rejectAll(error); - return; - } - - if (safeMsg.type === 'status') { - const status = (safeMsg.data as Record)?.status; - if (status === 'stream_completed') { + if (streamMessage.type === 'status') { + if (streamMessage.status === 'stream_completed') { finished = true; resolveAll(); - return; - } else if (status === 'stream_started') { - return; } - } else if (safeMsg.type === 'ERROR') { - error = new Error(`Agent error: ${JSON.stringify(safeMsg.data)}`); + return; + } + + if (streamMessage.type === 'error') { + error = new Error(this.cleanErrorMessage(streamMessage.error)); rejectAll(error); return; - } else { - if (resolvers.length > 0) { - console.log('resolving immediately'); - const resolver = resolvers.shift(); - if (resolver) { - resolver.resolve({ done: false, value: safeMsg.data }); - } - } else { - console.log('queueing message'); - messageQueue.push(safeMsg.data); + } + + const payload = this.deserializeStreamPayload(streamMessage.payload); + + if (resolvers.length > 0) { + const resolver = resolvers.shift(); + if (resolver) { + resolver.resolve({ done: false, value: payload }); } + } else { + messageQueue.push(payload); } } catch (err) { error = err instanceof Error ? err : new Error('Unknown error'); diff --git a/runagent-ts/src/websocket/node.ts b/runagent-ts/src/websocket/node.ts index bae59ae..d600c9b 100644 --- a/runagent-ts/src/websocket/node.ts +++ b/runagent-ts/src/websocket/node.ts @@ -5,13 +5,6 @@ interface IteratorResolverItem { reject: (error: Error) => void; } -interface NodeWebSocket { - on(event: string, callback: (...args: unknown[]) => void): void; - send(data: string): void; - close(): void; - readyState: number; -} - export class NodeWebSocketClient extends BaseWebSocketClient { private WebSocketClass: any = null; @@ -36,13 +29,16 @@ export class NodeWebSocketClient extends BaseWebSocketClient { } } - async createWebSocket(url: string): Promise { + async createWebSocket( + url: string, + headers?: Record + ): Promise { const WebSocket = await this.loadWebSocket(); - return new WebSocket(url) as NodeWebSocket; + return new WebSocket(url, headers ? { headers } : undefined); } protected async waitForConnection(websocket: unknown): Promise { - const ws = websocket as NodeWebSocket; + const ws = websocket as any; return new Promise((resolve, reject) => { ws.on('open', () => resolve()); ws.on('error', (...args: unknown[]) => { @@ -53,23 +49,23 @@ export class NodeWebSocketClient extends BaseWebSocketClient { } protected sendMessage(websocket: unknown, message: string): void { - const ws = websocket as NodeWebSocket; + const ws = websocket as any; ws.send(message); } protected isWebSocketOpen(websocket: unknown): boolean { - const ws = websocket as NodeWebSocket; + const ws = websocket as any; return ws.readyState === 1; // WebSocket.OPEN } protected closeWebSocket(websocket: unknown): void { - const ws = websocket as NodeWebSocket; + const ws = websocket as any; ws.close(); } // Rest of the methods remain the same... protected async *createWebSocketIterator(websocket: unknown): AsyncGenerator { - const ws = websocket as NodeWebSocket; + const ws = websocket as any; const messageQueue: unknown[] = []; const resolvers: IteratorResolverItem[] = []; let finished = false; @@ -96,39 +92,32 @@ export class NodeWebSocketClient extends BaseWebSocketClient { const messageHandler = (...args: unknown[]) => { const data = args[0] as Buffer | string; try { - console.log('received=> ', data.toString()); - const safeMsg = this.serializer.deserializeMessage(data.toString()); + const raw = typeof data === 'string' ? data : data.toString(); + const streamMessage = this.parseStreamMessage(raw); - if (safeMsg.error) { - error = new Error(`Stream error: ${safeMsg.error}`); - rejectAll(error); - return; - } - - if (safeMsg.type === 'status') { - const status = (safeMsg.data as Record)?.status; - if (status === 'stream_completed') { + if (streamMessage.type === 'status') { + if (streamMessage.status === 'stream_completed') { finished = true; resolveAll(); - return; - } else if (status === 'stream_started') { - return; } - } else if (safeMsg.type === 'ERROR') { - error = new Error(`Agent error: ${JSON.stringify(safeMsg.data)}`); + return; + } + + if (streamMessage.type === 'error') { + error = new Error(this.cleanErrorMessage(streamMessage.error)); rejectAll(error); return; - } else { - if (resolvers.length > 0) { - console.log('resolving immediately'); - const resolver = resolvers.shift(); - if (resolver) { - resolver.resolve({ done: false, value: safeMsg.data }); - } - } else { - console.log('queueing message'); - messageQueue.push(safeMsg.data); + } + + const payload = this.deserializeStreamPayload(streamMessage.payload); + + if (resolvers.length > 0) { + const resolver = resolvers.shift(); + if (resolver) { + resolver.resolve({ done: false, value: payload }); } + } else { + messageQueue.push(payload); } } catch (err) { error = err instanceof Error ? err : new Error('Unknown error'); diff --git a/sdk_checklist.md b/sdk_checklist.md new file mode 100644 index 0000000..ac6c9d7 --- /dev/null +++ b/sdk_checklist.md @@ -0,0 +1,159 @@ +## SDK Implementation Guide + +### Purpose And Scope +- **Goal**: every SDK (Python, JS/TS, Go, Rust, future C#/Swift/Flutter, etc.) exposes a single `RunAgentClient` class with `run()` and `run_stream()`; Python CLI is the superset, other SDKs only need client functions. +- **Reference implementation**: Python CLI layers on top of the Python SDK. When unsure, mirror the Python behavior and propose parity improvements if features are missing. +- **Separation of concerns**: the CLI covers deployment/orchestration; SDKs focus purely on invoking deployed agents. + +### Client Initialization Contract +- **Constructor signature** *(language-idiomatic)*: `RunAgentClient({ agent_id, entrypoint_tag, local?, host?, port?, api_key?, base_url?, extra_params? })`. +- **Required inputs**: `agent_id`, `entrypoint_tag`. +- **Optional inputs**: + - `local` (default `false` except Python CLI) indicates whether to auto-discover host/port for co-located agents. + - `host`/`port` override local discovery; required when running on a device that cannot read the local SQLite DB (e.g. browsers, remote SDK consumers). + - `api_key` overrides environment configuration for cloud calls. + - `base_url` overrides the default cloud endpoint. + - `extra_params` is an open-ended key/value bag stored on the client for future metadata use without breaking changes (simply accept and retain it; no mandated behavior yet). + +- **Configuration precedence** (must be explicit in every SDK): + 1. Explicit constructor arguments + 2. Environment variables (`RUNAGENT_API_KEY`, `RUNAGENT_BASE_URL`, etc.) + 3. Library defaults (`https://backend.run-agent.ai`, standard port 8450 for local) + +### Local Agent Discovery (Optional) +- When `local=true` **and** filesystem access is available, read the SQLite DB at `~/.runagent/runagent_local.db`; reuse the Python schema (`agents` table mapping `agent_id → host, port, framework, status`). + ```35:55:runagent/client/client.py + if local: + if host and port: + agent_host = host + agent_port = port + else: + agent_info = self.sdk.db_service.get_agent(agent_id) + ... + agent_host = self.agent_info["host"] + agent_port = self.agent_info["port"] + ``` +- If the agent is missing, raise a clear error telling users to start or register the agent locally. +- SDKs running in sandboxes (browser, mobile, serverless) should skip DB probing and require `host`/`port`. + +### Remote Agent Defaults +- Default REST base URL: `https://backend.run-agent.ai` (append `/api/v1`). +- Default WebSocket base: convert to `wss://backend.run-agent.ai/api/v1`. +- Allow overrides via constructor or `RUNAGENT_BASE_URL` env var. +- Respect per-request overrides for self-hosted or staging deployments. + +### Authentication +- Use Bearer tokens everywhere; reuse the CLI convention (`Authorization: Bearer ${api_key}`) and query-string token fallback for WebSockets. +- Environment variable name: `RUNAGENT_API_KEY`. +- If no API key is present for remote calls, surface a clear error instructing users to set env vars or pass `api_key=`. + +### HTTP `run()` Semantics +- Endpoint: `POST /api/v1/agents/{agent_id}/run`. +- Payload: + ```json + { + "entrypoint_tag": "...", + "input_args": [], + "input_kwargs": {}, + "timeout_seconds": 300, + "async_execution": false + } + ``` +- Deserialize result payloads exactly as the Python client: + - `data.result_data.data` (legacy structured output) + - `data` directly when it’s a stringified artifact +- When errors arrive, mirror the Python `RunAgentExecutionError` structure (code/message/suggestion/details). + ```70:118:runagent/client/client.py + response = self.rest_client.run_agent(...) + if response.get("success"): + ... + else: + error_info = response.get("error") + ... + raise RunAgentExecutionError(code=..., message=..., suggestion=..., details=...) + ``` +- Recommended error taxonomy: `AUTHENTICATION_ERROR`, `PERMISSION_ERROR`, `CONNECTION_ERROR`, `VALIDATION_ERROR`, `SERVER_ERROR`, `UNKNOWN_ERROR`. + +### WebSocket `run_stream()` Semantics +- Endpoint: `GET wss://.../api/v1/agents/{agent_id}/run-stream`. +- Handshake: immediately send JSON body identical to REST payload (minus `async_execution` optional). +- Incoming frames: JSON objects with `type ∈ {status, data, error}`. + - `status=stream_started` (informational), `status=stream_completed` (terminate). + - `data` frames carry `content` (string or JSON); attempt structured deserialization first. + - `error` frames emit exception and stop the stream. + ```61:147:runagent/sdk/socket_client.py + if self.is_local: + uri = f"{self.base_socket_url}/agents/{agent_id}/run-stream" + else: + uri = f"{self.base_socket_url}/agents/{agent_id}/run-stream?token={self.api_key}" + ... + request_data = { "entrypoint_tag": ..., "input_args": ..., "input_kwargs": ..., "timeout_seconds": 600 } + websocket.send(json.dumps(request_data)) + for raw_message in websocket: + message = json.loads(raw_message) + if message["type"] == "data": + ... + ``` +- Provide both sync iterator and async iterator variants when idiomatic for the language. + +### Extra Params Handling +- Accept `extra_params` at construction; store but do not mutate. +- For now, just keep it accessible via a getter to future-proof metadata features (e.g., tracing context, client tags). + +### Error Handling Guidance +- Raise language-idiomatic exceptions derived from a base `RunAgentError`. +- Wrap network-layer issues (timeouts, DNS) into `ConnectionError`. +- When HTTP 401/403 occurs, raise `AuthenticationError` with friendly guidance (e.g., “Set RUNAGENT_API_KEY or pass api_key”). + +### Environment And Config Utilities +- Provide helpers to load env vars (`RUNAGENT_API_KEY`, `RUNAGENT_BASE_URL`, optional future keys). +- Offer a `from_env()` factory for ergonomic initialization. +- Document that constructor args override env vars, which override defaults. + +### Testing Expectations +- Unit tests: mock REST/WebSocket interactions; assert payload shape and error translation. +- Integration tests: optional “local mode” harness reading a test SQLite DB. +- Provide CI examples for each SDK (reference existing Python/Go/Rust tests under `test_scripts/`). + +### Implementation Checklist (Per New SDK) +- [ ] Build `RunAgentClient` with constructor precedence and optional local DB hook. +- [ ] Implement REST `run()` and WebSocket `run_stream()` following payload schemas. +- [ ] Surface consistent error types and messages. +- [ ] Support explicit `api_key`, `base_url`, `host`, `port`. +- [ ] Expose `extra_params` without opinionated behavior. +- [ ] Add environment-based helpers (`from_env`, `configure_from_env`). +- [ ] Include README snippet showing local vs remote usage. +- [ ] Add automated tests for success/error paths. +- [ ] Audit docs to ensure new SDK mirrors this guide. + +### Suggested Documentation Template For SDK Repos +1. Quickstart (init client, call `run`, call `run_stream`). +2. Configuration (env vars, constructor precedence). +3. Local vs remote usage (with/without DB, when to set `local=true`). +4. Authentication setup (API key instructions). +5. Error handling reference table. +6. Advanced topics (custom base URL, extra params, retries). +7. Troubleshooting (common connection/auth issues). + +### References +```24:37:runagent/constants.py +LOCAL_CACHE_DIRECTORY_PATH = "~/.runagent" +... +DATABASE_FILE_NAME = "runagent_local.db" +``` + +```25:118:runagent/client/client.py +class RunAgentClient: + def __init__(..., local: bool = True, host: str = None, port: int = None): + ... +``` + +```61:147:runagent/sdk/socket_client.py + if self.is_local: + uri = f"{self.base_socket_url}/agents/{agent_id}/run-stream" + else: + uri = f"{self.base_socket_url}/agents/{agent_id}/run-stream?token={self.api_key}" + ... +``` + +---