From 7a32c695f54a19ab98e24fcc0a248b02fc386f3f Mon Sep 17 00:00:00 2001 From: Greg Soucy Date: Sun, 15 Feb 2026 14:22:00 -0500 Subject: [PATCH] Fix follow-up Python SDK verification/config compatibility issues --- .../workflows/typescript-sdk-cli-smoke.yml | 40 + QUICKSTART.md | 390 ++-- python-sdk/README.md | 454 ++--- python-sdk/commandlayer/__init__.py | 38 +- python-sdk/commandlayer/client.py | 248 +++ python-sdk/commandlayer/errors.py | 5 + python-sdk/commandlayer/types.py | 37 + python-sdk/commandlayer/verify.py | 242 +++ python-sdk/pyproject.toml | 138 +- python-sdk/tests/test_client.py | 24 + python-sdk/tests/test_verify.py | 45 + typescript-sdk/README.md | 660 +++---- typescript-sdk/package-lock.json | 1605 +++++++++++++++++ typescript-sdk/package.json | 11 +- typescript-sdk/scripts/cli-smoke.mjs | 48 + typescript-sdk/src/cli.ts | 94 + typescript-sdk/src/index.ts | 1358 +++++++------- typescript-sdk/tsup.config.ts | 5 +- 18 files changed, 3804 insertions(+), 1638 deletions(-) create mode 100644 .github/workflows/typescript-sdk-cli-smoke.yml create mode 100644 python-sdk/commandlayer/client.py create mode 100644 python-sdk/commandlayer/errors.py create mode 100644 python-sdk/commandlayer/types.py create mode 100644 python-sdk/commandlayer/verify.py create mode 100644 python-sdk/tests/test_client.py create mode 100644 python-sdk/tests/test_verify.py create mode 100644 typescript-sdk/package-lock.json create mode 100644 typescript-sdk/scripts/cli-smoke.mjs create mode 100644 typescript-sdk/src/cli.ts diff --git a/.github/workflows/typescript-sdk-cli-smoke.yml b/.github/workflows/typescript-sdk-cli-smoke.yml new file mode 100644 index 0000000..4948d98 --- /dev/null +++ b/.github/workflows/typescript-sdk-cli-smoke.yml @@ -0,0 +1,40 @@ +name: TypeScript SDK CLI Smoke + +on: + push: + paths: + - "typescript-sdk/**" + - ".github/workflows/typescript-sdk-cli-smoke.yml" + pull_request: + paths: + - "typescript-sdk/**" + - ".github/workflows/typescript-sdk-cli-smoke.yml" + +jobs: + cli-smoke: + runs-on: ubuntu-latest + defaults: + run: + working-directory: typescript-sdk + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: typescript-sdk/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Typecheck + run: npm run typecheck + + - name: Build + run: npm run build + + - name: CLI smoke tests + run: npm run test:cli-smoke diff --git a/QUICKSTART.md b/QUICKSTART.md index c188018..f441843 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -1,195 +1,195 @@ -# CommandLayer SDK Quickstart - -Install. Call a verb. Get a signed receipt. - -You can integrate CommandLayer in under 2 minutes. - ---- - -## What is CommandLayer? - -CommandLayer is the **semantic verb layer** for autonomous agents. - -It provides: - -- Standardized verbs (`summarize`, `analyze`, `classify`, etc.) -- Strict JSON request & receipt schemas -- Cryptographically signed receipts (Ed25519 + SHA-256) -- x402-compatible execution envelopes -- ERC-8004–aligned agent discovery - -CommandLayer turns agent actions into **verifiable infrastructure**. - ---- - -# 1️⃣ Install - -## TypeScript / JavaScript - -```bash -npm install @commandlayer/sdk -``` - -## Python - -```bash -pip install commandlayer -``` - ---- - -# 2️⃣ Make Your First Call - -## TypeScript - -```ts -import { createClient } from "@commandlayer/sdk"; - -const client = createClient({ - actor: "my-app" -}); - -const receipt = await client.summarize({ - content: "CommandLayer makes agent actions structured and verifiable.", - style: "bullet_points" -}); - -console.log(receipt.result.summary); -``` - ---- - -## Python - -```python -from commandlayer import create_client - -client = create_client(actor="my-app") - -receipt = client.summarize( - content="CommandLayer makes agent actions structured and verifiable.", - style="bullet_points" -) - -print(receipt.result["summary"]) -``` - ---- - -## CLI - -```bash -commandlayer summarize \ - --content "CommandLayer makes agent actions structured and verifiable." \ - --style bullet_points -``` - ---- - -# 3️⃣ What You Get Back - -Every call returns a **signed receipt**, not just raw output. - -```ts -receipt.status // "success" -receipt.metadata.receipt_id // Deterministic receipt hash -receipt.trace.duration_ms // Execution latency - -receipt.result // Structured verb output - -receipt.metadata.proof.hash_sha256 -receipt.metadata.proof.signature_b64 -receipt.metadata.proof.signer_id -receipt.metadata.proof.alg // "ed25519-sha256" -``` - -Receipts are: - -- Canonicalized -- Hashed (SHA-256) -- Signed (Ed25519) -- Verifiable independently - -By default, the SDK verifies receipts automatically. - ---- - -# 4️⃣ Available Verbs - -The Commons SDK includes 10 verbs: - -- `summarize` -- `analyze` -- `classify` -- `clean` -- `convert` -- `describe` -- `explain` -- `format` -- `parse` -- `fetch` - -All verbs return structured, signed receipts. - ---- - -# 5️⃣ Configuration - -```ts -const client = createClient({ - actor: "my-production-app", - runtime: "https://runtime.commandlayer.org", // default - verifyReceipts: true // default -}); -``` - -### Options - -- `actor` — Identifier for your application or tenant -- `runtime` — Custom runtime base URL -- `verifyReceipts` — Enable/disable signature verification - ---- - -# 6️⃣ Production Notes - -- Always set a meaningful `actor` -- Keep `verifyReceipts` enabled in production -- Store `receipt_id` for audit trails -- Treat receipts as durable evidence, not logs - ---- - -# 7️⃣ Verify a Receipt (Optional) - -```ts -import { verifyReceipt } from "@commandlayer/sdk"; - -const ok = await verifyReceipt(receipt, { - ens: true, - rpcUrl: "https://mainnet.infura.io/v3/..." -}); - -console.log("Verified:", ok); -``` - -You can verify: - -- With a provided public key (offline) -- By resolving signer pubkey from ENS -- Or disable verification entirely - ---- - -# Next Steps - -📖 Real-world usage → `EXAMPLES.md` -🚀 Deployment & publishing → `DEPLOYMENT_GUIDE.md` -🔍 SDK architecture → `DEVELOPER_EXPERIENCE.md` -🌐 Full docs → https://commandlayer.org/docs.html - ---- - -CommandLayer turns agent execution into verifiable infrastructure. - -You're ready to build. +# CommandLayer SDK Quickstart + +Install. Call a verb. Get a signed receipt. + +You can integrate CommandLayer in under 2 minutes. + +--- + +## What is CommandLayer? + +CommandLayer is the **semantic verb layer** for autonomous agents. + +It provides: + +- Standardized verbs (`summarize`, `analyze`, `classify`, etc.) +- Strict JSON request & receipt schemas +- Cryptographically signed receipts (Ed25519 + SHA-256) +- x402-compatible execution envelopes +- ERC-8004–aligned agent discovery + +CommandLayer turns agent actions into **verifiable infrastructure**. + +--- + +# 1️⃣ Install + +## TypeScript / JavaScript + +```bash +npm install @commandlayer/sdk +``` + +## Python + +```bash +pip install commandlayer +``` + +--- + +# 2️⃣ Make Your First Call + +## TypeScript + +```ts +import { createClient } from "@commandlayer/sdk"; + +const client = createClient({ + actor: "my-app" +}); + +const receipt = await client.summarize({ + content: "CommandLayer makes agent actions structured and verifiable.", + style: "bullet_points" +}); + +console.log(receipt.result.summary); +``` + +--- + +## Python + +```python +from commandlayer import create_client + +client = create_client(actor="my-app") + +receipt = client.summarize( + content="CommandLayer makes agent actions structured and verifiable.", + style="bullet_points" +) + +print(receipt["result"]["summary"]) +``` + +--- + +## CLI + +```bash +commandlayer summarize \ + --content "CommandLayer makes agent actions structured and verifiable." \ + --style bullet_points +``` + +--- + +# 3️⃣ What You Get Back + +Every call returns a **signed receipt**, not just raw output. + +```ts +receipt.status // "success" +receipt.metadata.receipt_id // Deterministic receipt hash +receipt.trace.duration_ms // Execution latency + +receipt.result // Structured verb output + +receipt.metadata.proof.hash_sha256 +receipt.metadata.proof.signature_b64 +receipt.metadata.proof.signer_id +receipt.metadata.proof.alg // "ed25519-sha256" +``` + +Receipts are: + +- Canonicalized +- Hashed (SHA-256) +- Signed (Ed25519) +- Verifiable independently + +By default, the SDK verifies receipts automatically. + +--- + +# 4️⃣ Available Verbs + +The Commons SDK includes 10 verbs: + +- `summarize` +- `analyze` +- `classify` +- `clean` +- `convert` +- `describe` +- `explain` +- `format` +- `parse` +- `fetch` + +All verbs return structured, signed receipts. + +--- + +# 5️⃣ Configuration + +```ts +const client = createClient({ + actor: "my-production-app", + runtime: "https://runtime.commandlayer.org", // default + verifyReceipts: true // default +}); +``` + +### Options + +- `actor` — Identifier for your application or tenant +- `runtime` — Custom runtime base URL +- `verifyReceipts` — Enable/disable signature verification + +--- + +# 6️⃣ Production Notes + +- Always set a meaningful `actor` +- Keep `verifyReceipts` enabled in production +- Store `receipt_id` for audit trails +- Treat receipts as durable evidence, not logs + +--- + +# 7️⃣ Verify a Receipt (Optional) + +```ts +import { verifyReceipt } from "@commandlayer/sdk"; + +const ok = await verifyReceipt(receipt, { + ens: true, + rpcUrl: "https://mainnet.infura.io/v3/..." +}); + +console.log("Verified:", ok); +``` + +You can verify: + +- With a provided public key (offline) +- By resolving signer pubkey from ENS +- Or disable verification entirely + +--- + +# Next Steps + +📖 Real-world usage → `EXAMPLES.md` +🚀 Deployment & publishing → `DEPLOYMENT_GUIDE.md` +🔍 SDK architecture → `DEVELOPER_EXPERIENCE.md` +🌐 Full docs → https://commandlayer.org/docs.html + +--- + +CommandLayer turns agent execution into verifiable infrastructure. + +You're ready to build. diff --git a/python-sdk/README.md b/python-sdk/README.md index becc253..43c5463 100644 --- a/python-sdk/README.md +++ b/python-sdk/README.md @@ -1,336 +1,118 @@ -# CommandLayer Python SDK - -Semantic verbs. Typed schemas. Signed receipts. - -This package provides the official Python SDK for **CommandLayer Commons v1.0.0**. - -Install → call a verb → receive a signed receipt → verify it. - ---- - -## What is CommandLayer? - -CommandLayer is the **semantic verb layer** for autonomous agents. - -The SDK provides: - -- Standardized Commons verbs (`summarize`, `analyze`, `fetch`, etc.) -- Strict JSON Schemas (requests + receipts) -- Cryptographically signed receipts (Ed25519 + SHA-256) -- Deterministic canonicalization (`cl-stable-json-v1`) -- Verification helpers (offline PEM or ENS-based) -- CLI-style patterns for reproducible testing - ---- - -## Installation - -```bash -pip install commandlayer -``` - -Python 3.10+ recommended. - ---- - -## Quickstart -``` -from commandlayer import create_client - -client = create_client( - actor="my-app", - runtime="https://runtime.commandlayer.org", # default - verify_receipts=True, # default (recommended) -) - -receipt = client.summarize( - content="CommandLayer turns agent actions into verifiable receipts.", - style="bullet_points", -) - -print(receipt["status"]) -print(receipt["result"]["summary"]) -print(receipt["metadata"]["receipt_id"]) -``` ---- - -## Runtime configuration - -Default runtime: - - - `https://runtime.commandlayer.org- - -Override if needed: -``` -from commandlayer import create_client - -client = create_client( - actor="my-app", - runtime="https://your-runtime.example", - verify_receipts=True, - timeout_ms=30_000, -) -``` ---- - -Keep `verify_receipts=True` in production. - ---- - -## Receipt structure - -Every call returns a signed receipt: -``` -{ - "status": "success", - "x402": { - "verb": "summarize", - "version": "1.0.0", - "entry": "x402://summarizeagent.eth/summarize/v1.0.0" - }, - "trace": { - "trace_id": "trace_ab12cd34", - "duration_ms": 118 - }, - "result": { - "summary": "..." - }, - "metadata": { - "receipt_id": "8f0a...", - "proof": { - "alg": "ed25519-sha256", - "canonical": "cl-stable-json-v1", - "signer_id": "runtime.commandlayer.eth", - "hash_sha256": "...", - "signature_b64": "..." - } - } -} -``` -Receipt guarantees: - -- Stable canonical hashing over the unsigned receipt -- SHA-256 digest of canonical JSON -- Ed25519 signature over the hash -- Deterministic verification across runtimes - - --- - -## Verifying receipts - -**Option A — Offline verification (explicit public key PEM)** - -Fastest method. No RPC required. -``` -from commandlayer import verify_receipt - -PUBLIC_KEY_PEM = """-----BEGIN PUBLIC KEY----- -MCowBQYDK2VwAyEA7Vkkmt6R02Iltp/+i3D5mraZyvLjfuTSVB33KwfzQC8= ------END PUBLIC KEY-----""" - -out = verify_receipt(receipt, public_key_pem=PUBLIC_KEY_PEM) - -print(out["ok"]) -print(out["checks"]) # schema_valid (optional), hash_matches, signature_valid -``` - -**Option B — ENS-based verification** - -Resolves the public key from ENS TXT records. - -Required ENS records: - -- `cl.receipt.pubkey_pem` -- `cl.receipt.signer_id` -- `cl.receipt.alg` - -``` -import os -from commandlayer import verify_receipt - -out = verify_receipt( - receipt, - ens_name="runtime.commandlayer.eth", - rpc_url=os.environ["ETH_RPC_URL"], - ens_txt_key="cl.receipt.pubkey_pem", # default -) - -print(out["ok"]) -print(out["values"]["pubkey_source"]) # "ens" when resolved -``` - -ENS affects verification correctness — not package build/publish. - ---- - -## Commons verbs - -All verbs return signed receipts. - -**summarize** -``` -receipt = client.summarize( - content="Long text...", - style="bullet_points", # optional - format="text", # optional - max_tokens=1000, # optional -) -``` - -**analyze** -``` -receipt = client.analyze( - content="Data...", - dimensions=["sentiment", "tone"], # optional (runtime-dependent) - max_tokens=1000, -) -``` - -**classify** -``` -receipt = client.classify( - content="Support message...", - categories=["support", "billing"], # optional (runtime-dependent) - max_tokens=1000, -) -``` - -**clean** -``` -receipt = client.clean( - content=" a \r\n\r\n b ", - operations=["trim", "normalize_newlines", "remove_empty_lines"], # optional - max_tokens=1000, -) -``` -**convert** -``` -receipt = client.convert( - content='{"a":1,"b":2}', - from_format="json", - to_format="csv", - max_tokens=1000, -) -``` -**describe** -``` -receipt = client.describe( - subject="CommandLayer receipt", - context="A receipt returned from the runtime...", - detail_level="medium", # brief|medium|detailed (runtime-dependent) - audience="general", - max_tokens=1000, -) -``` -**explain** -``` -receipt = client.explain( - subject="x402 receipt verification", - context="Explain what schema + hash + signature verification proves.", - style="step-by-step", - detail_level="medium", - audience="general", - max_tokens=1000, -) -``` -**format** - -```receipt = client.format( - content="a: 1\nb: 2", - target_style="table", # runtime-dependent - max_tokens=1000, -) -``` -**parse** -``` -receipt = client.parse( - content='{"a":1}', - content_type="json", # json|yaml|text - mode="strict", # best_effort|strict - target_schema=None, # optional - max_tokens=1000, -) -``` -**fetch** -``` -receipt = client.fetch( - source="https://example.com", - mode="text", # text|html|json (runtime-dependent) - query=None, - include_metadata=False, - max_tokens=1000, -) -``` - -## Local development - -Typical workflow: - -``` -cd python-sdk -python -m venv .venv - -# Windows -.venv\Scripts\activate -# macOS/Linux -source .venv/bin/activate - -pip install -U pip -pip install -e . -python -c "from commandlayer import create_client; print(create_client)" -``` - -Build a release: -``` -pip install -U build twine -python -m build -``` - -Publish (optional): -``` -twine upload dist/* -``` ---- - -## Versioning + release discipline - -Use SemVer: - -- Patch: bug fixes (no API break) -- Minor: new verbs/options (backward compatible) -- Major: breaking changes - -Release flow: - -- Update `CHANGELOG.md` -- Bump version -- Build -- Smoke test a live call against runtime.commandlayer.org -- Publish - ---- - -## Definition of Done - -You’re “deployed” when: - -- `pip install commandlayer` succeeds -- A verb call returns a valid receipt JSON -- Verification passes (offline or ENS-based) -- CI reproduces install + minimal smoke test - -## License - -MIT - -CommandLayer turns agent actions into verifiable infrastructure. - -Ship APIs that can prove what they did. - - - - - - - - +# CommandLayer Python SDK + +Semantic verbs. Signed receipts. Deterministic verification. + +Official Python SDK for **CommandLayer Commons v1.0.0**. + +## Installation + +```bash +pip install commandlayer +``` + +Python 3.10+ is supported. + +--- + +## Quickstart + +```python +from commandlayer import create_client + +client = create_client( + actor="my-app", + runtime="https://runtime.commandlayer.org", # optional +) + +receipt = client.summarize( + content="CommandLayer turns agent actions into verifiable receipts.", + style="bullet_points", +) + +print(receipt["status"]) +print(receipt["metadata"]["receipt_id"]) +``` + +> `verify_receipts` is **off by default** (matching TypeScript SDK behavior). + +--- + +## Client Configuration + +```python +client = create_client( + runtime="https://runtime.commandlayer.org", + actor="my-app", + timeout_ms=30_000, + verify_receipts=True, + verify={ + "public_key": "ed25519:7Vkkmt6R02Iltp/+i3D5mraZyvLjfuTSVB33KwfzQC8=", + # or ENS: + # "ens": {"name": "runtime.commandlayer.eth", "rpcUrl": "https://..."} + }, +) +``` + +### Verification options +- `verify["public_key"]` (alias: `publicKey`): explicit Ed25519 pubkey (`ed25519:`, ``, `0x`, ``) +- `verify["ens"]`: `{ "name": str, "rpcUrl"|"rpc_url": str, "pubkeyTextKey"|"pubkey_text_key"?: str }` + +--- + +## Supported Verbs + +All verbs return a signed receipt. + +```python +client.summarize(content="...", style="bullet_points") +client.analyze(content="...", goal="extract key risks") +client.classify(content="...", max_labels=5) +client.clean(content="...", operations=["trim", "normalize_newlines"]) +client.convert(content='{"a":1}', from_format="json", to_format="csv") +client.describe(subject="x402 receipt", detail="medium") +client.explain(subject="receipt verification", style="step-by-step") +client.format(content="a: 1\nb: 2", to="table") +client.parse(content='{"a":1}', content_type="json", mode="strict") +client.fetch(source="https://example.com", include_metadata=True) +``` + +--- + +## Receipt Verification API + +```python +from commandlayer import verify_receipt + +result = verify_receipt( + receipt, + public_key="ed25519:7Vkkmt6R02Iltp/+i3D5mraZyvLjfuTSVB33KwfzQC8=", +) + +print(result["ok"]) +print(result["checks"]) +``` + +ENS-based verification: + +```python +result = verify_receipt( + receipt, + ens={ + "name": "runtime.commandlayer.eth", + "rpcUrl": "https://mainnet.infura.io/v3/YOUR_KEY", + "pubkeyTextKey": "cl.pubkey", + }, +) +``` + +--- + +## Development + +```bash +cd python-sdk +python -m venv .venv +source .venv/bin/activate +pip install -e '.[dev]' +pytest +``` diff --git a/python-sdk/commandlayer/__init__.py b/python-sdk/commandlayer/__init__.py index d43b5af..1295daa 100644 --- a/python-sdk/commandlayer/__init__.py +++ b/python-sdk/commandlayer/__init__.py @@ -1,21 +1,17 @@ -# python-sdk/src/commandlayer/__init__.py - -""" -CommandLayer Python SDK. - -Semantic verbs. Typed schemas. Signed receipts. -""" - -from .client import CommandLayerClient, create_client -from .errors import CommandLayerError -from .types import Receipt, VerifyResult - -__all__ = [ - "CommandLayerClient", - "create_client", - "CommandLayerError", - "Receipt", - "VerifyResult", -] - -__version__ = "1.0.0" +"""CommandLayer Python SDK.""" + +from .client import CommandLayerClient, create_client +from .errors import CommandLayerError +from .types import Receipt, VerifyResult +from .verify import verify_receipt + +__all__ = [ + "CommandLayerClient", + "create_client", + "CommandLayerError", + "Receipt", + "VerifyResult", + "verify_receipt", +] + +__version__ = "1.0.0" diff --git a/python-sdk/commandlayer/client.py b/python-sdk/commandlayer/client.py new file mode 100644 index 0000000..5f4bfa0 --- /dev/null +++ b/python-sdk/commandlayer/client.py @@ -0,0 +1,248 @@ +from __future__ import annotations + +from typing import Any + +import httpx + +from .errors import CommandLayerError +from .types import Receipt +from .verify import verify_receipt + + +VERBS = { + "summarize", + "analyze", + "classify", + "clean", + "convert", + "describe", + "explain", + "format", + "parse", + "fetch", +} + + +def _normalize_base(url: str) -> str: + return str(url or "").rstrip("/") + + +class CommandLayerClient: + def __init__( + self, + runtime: str = "https://runtime.commandlayer.org", + actor: str = "sdk-user", + timeout_ms: int = 30_000, + verify_receipts: bool = False, + verify: dict[str, Any] | None = None, + ): + self.runtime = _normalize_base(runtime) + self.actor = actor + self.timeout_ms = timeout_ms + self.verify_receipts = verify_receipts is True + self.verify_defaults = verify or {} + self._http = httpx.Client(timeout=self.timeout_ms / 1000) + + def _ensure_verify_config_if_enabled(self) -> None: + if not self.verify_receipts: + return + + public_key = self.verify_defaults.get("public_key") or self.verify_defaults.get("publicKey") + has_explicit = bool(str(public_key or "").strip()) + ens = self.verify_defaults.get("ens") or {} + has_ens = bool(ens.get("name") and (ens.get("rpcUrl") or ens.get("rpc_url"))) + + if not has_explicit and not has_ens: + raise CommandLayerError( + "verify_receipts is enabled but no verification key config provided. " + "Set verify.public_key (or verify.publicKey) or verify.ens {name, rpcUrl}.", + 400, + ) + + def summarize(self, *, content: str, style: str | None = None, format: str | None = None, max_tokens: int = 1000) -> Receipt: + return self.call( + "summarize", + { + "input": {"content": content, "summary_style": style, "format_hint": format}, + "limits": {"max_output_tokens": max_tokens}, + }, + ) + + def analyze(self, *, content: str, goal: str | None = None, hints: list[str] | None = None, max_tokens: int = 1000) -> Receipt: + body: dict[str, Any] = {"input": content, "limits": {"max_output_tokens": max_tokens}} + if goal: + body["goal"] = goal + if hints: + body["hints"] = hints + return self.call("analyze", body) + + def classify(self, *, content: str, max_labels: int = 5, max_tokens: int = 1000) -> Receipt: + return self.call( + "classify", + { + "actor": self.actor, + "input": {"content": content}, + "limits": {"max_labels": max_labels, "max_output_tokens": max_tokens}, + }, + ) + + def clean(self, *, content: str, operations: list[str] | None = None, max_tokens: int = 1000) -> Receipt: + return self.call( + "clean", + { + "input": { + "content": content, + "operations": operations or ["normalize_newlines", "collapse_whitespace", "trim"], + }, + "limits": {"max_output_tokens": max_tokens}, + }, + ) + + def convert(self, *, content: str, from_format: str, to_format: str, max_tokens: int = 1000) -> Receipt: + return self.call( + "convert", + { + "input": {"content": content, "source_format": from_format, "target_format": to_format}, + "limits": {"max_output_tokens": max_tokens}, + }, + ) + + def describe( + self, + *, + subject: str, + audience: str = "general", + detail: str = "medium", + max_tokens: int = 1000, + ) -> Receipt: + subject = (subject or "")[:140] + return self.call( + "describe", + { + "input": {"subject": subject, "audience": audience, "detail_level": detail}, + "limits": {"max_output_tokens": max_tokens}, + }, + ) + + def explain( + self, + *, + subject: str, + audience: str = "general", + style: str = "step-by-step", + detail: str = "medium", + max_tokens: int = 1000, + ) -> Receipt: + subject = (subject or "")[:140] + return self.call( + "explain", + { + "input": { + "subject": subject, + "audience": audience, + "style": style, + "detail_level": detail, + }, + "limits": {"max_output_tokens": max_tokens}, + }, + ) + + def format(self, *, content: str, to: str, max_tokens: int = 1000) -> Receipt: + return self.call( + "format", + {"input": {"content": content, "target_style": to}, "limits": {"max_output_tokens": max_tokens}}, + ) + + def parse( + self, + *, + content: str, + content_type: str = "text", + mode: str = "best_effort", + target_schema: str | None = None, + max_tokens: int = 1000, + ) -> Receipt: + input_obj: dict[str, Any] = { + "content": content, + "content_type": content_type, + "mode": mode, + } + if target_schema: + input_obj["target_schema"] = target_schema + + return self.call("parse", {"input": input_obj, "limits": {"max_output_tokens": max_tokens}}) + + def fetch( + self, + *, + source: str, + query: str | None = None, + include_metadata: bool | None = None, + max_tokens: int = 1000, + ) -> Receipt: + input_obj: dict[str, Any] = {"source": source} + if query is not None: + input_obj["query"] = query + if include_metadata is not None: + input_obj["include_metadata"] = include_metadata + + return self.call("fetch", {"input": input_obj, "limits": {"max_output_tokens": max_tokens}}) + + def call(self, verb: str, body: dict[str, Any]) -> Receipt: + if verb not in VERBS: + raise CommandLayerError(f"Unsupported verb: {verb}", 400) + + self._ensure_verify_config_if_enabled() + url = f"{self.runtime}/{verb}/v1.0.0" + + payload = { + "x402": { + "verb": verb, + "version": "1.0.0", + "entry": f"x402://{verb}agent.eth/{verb}/v1.0.0", + }, + "actor": body.get("actor", self.actor), + **body, + } + + try: + resp = self._http.post( + url, + headers={"Content-Type": "application/json", "User-Agent": "commandlayer-py/1.0.0"}, + json=payload, + ) + except httpx.TimeoutException as err: + raise CommandLayerError("Request timed out", 408) from err + except Exception as err: + raise CommandLayerError(str(err)) from err + + try: + data = resp.json() + except Exception: + data = {} + + if not resp.is_success: + message = ( + data.get("message") if isinstance(data, dict) else None + ) or ( + (data.get("error") or {}).get("message") if isinstance(data, dict) and isinstance(data.get("error"), dict) else None + ) or f"HTTP {resp.status_code}" + raise CommandLayerError(message, resp.status_code, data) + + if self.verify_receipts: + result = verify_receipt( + data, + public_key=self.verify_defaults.get("public_key") or self.verify_defaults.get("publicKey"), + ens=self.verify_defaults.get("ens"), + ) + if not result["ok"]: + raise CommandLayerError("Receipt verification failed", 422, result) + + return data + + def close(self): + self._http.close() + + +def create_client(**kwargs) -> CommandLayerClient: + return CommandLayerClient(**kwargs) diff --git a/python-sdk/commandlayer/errors.py b/python-sdk/commandlayer/errors.py new file mode 100644 index 0000000..830bf22 --- /dev/null +++ b/python-sdk/commandlayer/errors.py @@ -0,0 +1,5 @@ +class CommandLayerError(Exception): + def __init__(self, message: str, status_code: int | None = None, details=None): + super().__init__(message) + self.status_code = status_code + self.details = details diff --git a/python-sdk/commandlayer/types.py b/python-sdk/commandlayer/types.py new file mode 100644 index 0000000..38675df --- /dev/null +++ b/python-sdk/commandlayer/types.py @@ -0,0 +1,37 @@ +from typing import Any, Literal, TypedDict + + +class VerifyChecks(TypedDict, total=False): + hash_matches: bool + signature_valid: bool + receipt_id_matches: bool + alg_matches: bool + canonical_matches: bool + + +class VerifyValues(TypedDict, total=False): + verb: str | None + signer_id: str | None + alg: str | None + canonical: str | None + claimed_hash: str | None + recomputed_hash: str | None + receipt_id: str | None + pubkey_source: Literal["explicit", "ens"] | None + ens_txt_key: str | None + + +class VerifyErrors(TypedDict, total=False): + signature_error: str | None + ens_error: str | None + verify_error: str | None + + +class VerifyResult(TypedDict): + ok: bool + checks: VerifyChecks + values: VerifyValues + errors: VerifyErrors + + +Receipt = dict[str, Any] diff --git a/python-sdk/commandlayer/verify.py b/python-sdk/commandlayer/verify.py new file mode 100644 index 0000000..d9ec9ce --- /dev/null +++ b/python-sdk/commandlayer/verify.py @@ -0,0 +1,242 @@ +import base64 +import copy +import hashlib +import json +import re +from typing import Any + +from nacl.signing import VerifyKey +from nacl.exceptions import BadSignatureError +from web3 import Web3 + +from .types import Receipt, VerifyResult + + +def canonicalize_stable_json_v1(value: Any) -> str: + def encode(v: Any) -> str: + if v is None: + return "null" + if isinstance(v, bool): + return "true" if v else "false" + if isinstance(v, str): + return json.dumps(v, ensure_ascii=False) + if isinstance(v, (int, float)): + if isinstance(v, float): + if v != v or v in (float("inf"), float("-inf")): + raise ValueError("canonicalize: non-finite number not allowed") + if v == 0.0 and str(v).startswith("-"): + return "0" + return format(v, "g") if isinstance(v, float) else str(v) + if isinstance(v, list): + return "[" + ",".join(encode(x) for x in v) + "]" + if isinstance(v, dict): + out = [] + for k in sorted(v.keys()): + val = v[k] + out.append(f"{json.dumps(str(k), ensure_ascii=False)}:{encode(val)}") + return "{" + ",".join(out) + "}" + raise ValueError(f"canonicalize: unsupported type {type(v).__name__}") + + return encode(value) + + +def sha256_hex_utf8(text: str) -> str: + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def parse_ed25519_pubkey(text: str) -> bytes: + s = str(text).strip() + match = re.match(r"^ed25519\s*[:=]\s*(.+)$", s, re.IGNORECASE) + candidate = (match.group(1) if match else s).strip() + + if re.match(r"^(0x)?[0-9a-fA-F]{64}$", candidate): + h = candidate[2:] if candidate.startswith("0x") else candidate + return bytes.fromhex(h) + + decoded = base64.b64decode(candidate, validate=True) + if len(decoded) != 32: + raise ValueError("invalid base64 ed25519 pubkey length (need 32 bytes)") + return decoded + + +def verify_ed25519_signature_over_utf8_hash_string(hash_hex: str, signature_b64: str, pubkey32: bytes) -> bool: + if len(pubkey32) != 32: + raise ValueError("ed25519: pubkey must be 32 bytes") + sig = base64.b64decode(signature_b64) + if len(sig) != 64: + raise ValueError("ed25519: signature must be 64 bytes") + + vk = VerifyKey(pubkey32) + try: + vk.verify(hash_hex.encode("utf-8"), sig) + return True + except BadSignatureError: + return False + + +def resolve_ens_ed25519_pubkey(name: str, rpc_url: str, pubkey_text_key: str = "cl.pubkey") -> dict[str, Any]: + if not rpc_url: + return {"pubkey": None, "source": None, "error": "rpcUrl is required for ENS verification", "txt_key": pubkey_text_key} + try: + w3 = Web3(Web3.HTTPProvider(rpc_url)) + if not w3.is_connected(): + return {"pubkey": None, "source": None, "error": "Unable to connect to RPC", "txt_key": pubkey_text_key} + + try: + txt = w3.ens.get_text(name, pubkey_text_key) # type: ignore[attr-defined] + except Exception as err: + return {"pubkey": None, "source": None, "error": f"ENS TXT lookup failed: {err}", "txt_key": pubkey_text_key} + + if not txt: + return {"pubkey": None, "source": None, "error": f"ENS TXT {pubkey_text_key} missing", "txt_key": pubkey_text_key} + + pubkey = parse_ed25519_pubkey(str(txt).strip()) + return {"pubkey": pubkey, "source": "ens", "txt_key": pubkey_text_key, "txt_value": txt} + except Exception as err: + return {"pubkey": None, "source": None, "error": str(err), "txt_key": pubkey_text_key} + + +def to_unsigned_receipt(receipt: Receipt) -> Receipt: + if not isinstance(receipt, dict): + raise ValueError("receipt must be an object") + + r = copy.deepcopy(receipt) + + metadata = r.get("metadata") + if isinstance(metadata, dict): + metadata.pop("receipt_id", None) + + proof = metadata.get("proof") + if isinstance(proof, dict): + unsigned_proof = {} + for key in ("alg", "canonical", "signer_id"): + if isinstance(proof.get(key), str): + unsigned_proof[key] = proof[key] + metadata["proof"] = unsigned_proof + + r.pop("receipt_id", None) + return r + + +def recompute_receipt_hash_sha256(receipt: Receipt) -> dict[str, str]: + unsigned = to_unsigned_receipt(receipt) + canonical = canonicalize_stable_json_v1(unsigned) + hash_sha256 = sha256_hex_utf8(canonical) + return {"canonical": canonical, "hash_sha256": hash_sha256} + + +def verify_receipt(receipt: Receipt, public_key: str | None = None, ens: dict[str, Any] | None = None) -> VerifyResult: + try: + proof = ((receipt.get("metadata") or {}).get("proof") or {}) if isinstance(receipt, dict) else {} + + claimed_hash = proof.get("hash_sha256") if isinstance(proof.get("hash_sha256"), str) else None + signature_b64 = proof.get("signature_b64") if isinstance(proof.get("signature_b64"), str) else None + alg = proof.get("alg") if isinstance(proof.get("alg"), str) else None + canonical = proof.get("canonical") if isinstance(proof.get("canonical"), str) else None + signer_id = proof.get("signer_id") if isinstance(proof.get("signer_id"), str) else None + + alg_matches = alg == "ed25519-sha256" + canonical_matches = canonical == "cl-stable-json-v1" + + recomputed_hash = recompute_receipt_hash_sha256(receipt)["hash_sha256"] + hash_matches = bool(claimed_hash and claimed_hash == recomputed_hash) + + metadata = receipt.get("metadata") if isinstance(receipt.get("metadata"), dict) else {} + receipt_id = metadata.get("receipt_id") or receipt.get("receipt_id") + receipt_id = receipt_id if isinstance(receipt_id, str) else None + receipt_id_matches = bool(claimed_hash and receipt_id == claimed_hash) + + pubkey = None + pubkey_source = None + ens_error = None + ens_txt_key = None + + if public_key: + pubkey = parse_ed25519_pubkey(public_key) + pubkey_source = "explicit" + elif ens: + res = resolve_ens_ed25519_pubkey( + name=ens["name"], + rpc_url=ens.get("rpcUrl") or ens.get("rpc_url"), + pubkey_text_key=ens.get("pubkeyTextKey") or ens.get("pubkey_text_key") or "cl.pubkey", + ) + ens_txt_key = res.get("txt_key") + if not res.get("pubkey"): + ens_error = res.get("error") or "ENS pubkey not found" + else: + pubkey = res["pubkey"] + pubkey_source = "ens" + + signature_valid = False + signature_error = None + + if not alg_matches: + signature_error = f'proof.alg must be "ed25519-sha256" (got {alg})' + elif not canonical_matches: + signature_error = f'proof.canonical must be "cl-stable-json-v1" (got {canonical})' + elif not claimed_hash or not signature_b64: + signature_error = "missing proof.hash_sha256 or proof.signature_b64" + elif not pubkey: + signature_error = ens_error or "no public key available (provide public_key/publicKey or ens)" + else: + try: + signature_valid = verify_ed25519_signature_over_utf8_hash_string(claimed_hash, signature_b64, pubkey) + except Exception as err: + signature_valid = False + signature_error = str(err) + + ok = alg_matches and canonical_matches and hash_matches and receipt_id_matches and signature_valid + + return { + "ok": ok, + "checks": { + "hash_matches": hash_matches, + "signature_valid": signature_valid, + "receipt_id_matches": receipt_id_matches, + "alg_matches": alg_matches, + "canonical_matches": canonical_matches, + }, + "values": { + "verb": (((receipt.get("x402") or {}).get("verb")) if isinstance(receipt, dict) else None), + "signer_id": signer_id, + "alg": alg, + "canonical": canonical, + "claimed_hash": claimed_hash, + "recomputed_hash": recomputed_hash, + "receipt_id": receipt_id, + "pubkey_source": pubkey_source, + "ens_txt_key": ens_txt_key, + }, + "errors": { + "signature_error": signature_error, + "ens_error": ens_error, + "verify_error": None, + }, + } + except Exception as err: + return { + "ok": False, + "checks": { + "hash_matches": False, + "signature_valid": False, + "receipt_id_matches": False, + "alg_matches": False, + "canonical_matches": False, + }, + "values": { + "verb": ((receipt.get("x402") or {}).get("verb")) if isinstance(receipt, dict) else None, + "signer_id": ((((receipt.get("metadata") or {}).get("proof") or {}).get("signer_id")) if isinstance(receipt, dict) else None), + "alg": ((((receipt.get("metadata") or {}).get("proof") or {}).get("alg")) if isinstance(receipt, dict) else None), + "canonical": ((((receipt.get("metadata") or {}).get("proof") or {}).get("canonical")) if isinstance(receipt, dict) else None), + "claimed_hash": ((((receipt.get("metadata") or {}).get("proof") or {}).get("hash_sha256")) if isinstance(receipt, dict) else None), + "recomputed_hash": None, + "receipt_id": (((receipt.get("metadata") or {}).get("receipt_id")) if isinstance(receipt, dict) else None), + "pubkey_source": None, + "ens_txt_key": None, + }, + "errors": { + "signature_error": None, + "ens_error": None, + "verify_error": str(err), + }, + } diff --git a/python-sdk/pyproject.toml b/python-sdk/pyproject.toml index 30eb7f7..1feb0f8 100644 --- a/python-sdk/pyproject.toml +++ b/python-sdk/pyproject.toml @@ -1,73 +1,65 @@ -[build-system] -requires = ["setuptools>=69", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "commandlayer" -version = "1.0.0" -description = "CommandLayer Python SDK — semantic verbs, typed schemas, signed receipts." -readme = "README.md" -requires-python = ">=3.10" -license = { text = "MIT" } -authors = [{ name = "CommandLayer", email = "security@commandlayer.org" }] -keywords = ["commandlayer", "agents", "receipts", "x402", "ens", "schema"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Topic :: Software Development :: Libraries", - "Topic :: Security :: Cryptography", -] - -dependencies = [ - "httpx>=0.27.0", - "pydantic>=2.6.0", - "jsonschema>=4.21.0", - "pynacl>=1.5.0", - "eth-abi>=5.0.0", - "eth-utils>=4.0.0", - "web3>=6.20.0", -] - -[project.optional-dependencies] -dev = [ - "pytest>=8.0.0", - "pytest-asyncio>=0.23.0", - "ruff>=0.4.0", - "mypy>=1.8.0", - "build>=1.1.0", - "twine>=5.0.0", -] - -[project.urls] -Homepage = "https://commandlayer.org" -Documentation = "https://commandlayer.org/docs.html" -Repository = "https://github.com/commandlayer" -Issues = "https://github.com/commandlayer/issues" - -[tool.setuptools] -package-dir = { "" = "src" } - -[tool.setuptools.packages.find] -where = ["src"] -include = ["commandlayer*"] - -[tool.pytest.ini_options] -testpaths = ["tests"] -addopts = "-q" - -[tool.ruff] -line-length = 100 -target-version = "py310" - -[tool.mypy] -python_version = "3.10" -warn_return_any = true -warn_unused_configs = true -disallow_untyped_defs = false -ignore_missing_imports = true +[build-system] +requires = ["setuptools>=69", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "commandlayer" +version = "1.0.0" +description = "CommandLayer Python SDK — semantic verbs, signed receipts, and verification helpers." +readme = "README.md" +requires-python = ">=3.10" +license = { text = "MIT" } +authors = [{ name = "CommandLayer", email = "security@commandlayer.org" }] +keywords = ["commandlayer", "agents", "receipts", "x402", "ens", "sdk"] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Software Development :: Libraries", + "Topic :: Security :: Cryptography", +] + +dependencies = [ + "httpx>=0.27.0", + "pynacl>=1.5.0", + "web3>=6.20.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=8.0.0", + "ruff>=0.4.0", + "mypy>=1.8.0", + "build>=1.1.0", + "twine>=5.0.0", +] + +[project.urls] +Homepage = "https://commandlayer.org" +Documentation = "https://commandlayer.org/docs.html" +Repository = "https://github.com/commandlayer" +Issues = "https://github.com/commandlayer/issues" + +[tool.setuptools.packages.find] +where = ["."] +include = ["commandlayer*"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "-q" + +[tool.ruff] +line-length = 100 +target-version = "py310" + +[tool.mypy] +python_version = "3.10" +warn_return_any = true +warn_unused_configs = true +disallow_untyped_defs = false +ignore_missing_imports = true diff --git a/python-sdk/tests/test_client.py b/python-sdk/tests/test_client.py new file mode 100644 index 0000000..2c844e6 --- /dev/null +++ b/python-sdk/tests/test_client.py @@ -0,0 +1,24 @@ +import pytest + +from commandlayer.client import CommandLayerClient +from commandlayer.errors import CommandLayerError + + +def test_call_rejects_unsupported_verb(): + client = CommandLayerClient() + with pytest.raises(CommandLayerError): + client.call("unknown", {}) + + +def test_verify_config_required_when_enabled(): + client = CommandLayerClient(verify_receipts=True) + with pytest.raises(CommandLayerError): + client._ensure_verify_config_if_enabled() + + +def test_verify_config_accepts_camelcase_aliases(): + client = CommandLayerClient( + verify_receipts=True, + verify={"publicKey": "ed25519:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}, + ) + client._ensure_verify_config_if_enabled() diff --git a/python-sdk/tests/test_verify.py b/python-sdk/tests/test_verify.py new file mode 100644 index 0000000..151b507 --- /dev/null +++ b/python-sdk/tests/test_verify.py @@ -0,0 +1,45 @@ +import base64 + +from nacl.signing import SigningKey + +from commandlayer.verify import parse_ed25519_pubkey, recompute_receipt_hash_sha256, verify_receipt + + +def test_verify_receipt_happy_path_explicit_public_key(): + receipt = { + "status": "success", + "x402": { + "verb": "summarize", + "version": "1.0.0", + "entry": "x402://summarizeagent.eth/summarize/v1.0.0", + }, + "metadata": { + "proof": { + "alg": "ed25519-sha256", + "canonical": "cl-stable-json-v1", + "signer_id": "runtime.commandlayer.eth", + } + }, + } + + hash_out = recompute_receipt_hash_sha256(receipt) + sk = SigningKey.generate() + signature = sk.sign(hash_out["hash_sha256"].encode("utf-8")).signature + + receipt["metadata"]["proof"]["hash_sha256"] = hash_out["hash_sha256"] + receipt["metadata"]["proof"]["signature_b64"] = base64.b64encode(signature).decode("utf-8") + receipt["metadata"]["receipt_id"] = hash_out["hash_sha256"] + + pubkey_b64 = base64.b64encode(bytes(sk.verify_key)).decode("utf-8") + + out = verify_receipt(receipt, public_key=f"ed25519:{pubkey_b64}") + assert out["ok"] is True + assert out["checks"]["signature_valid"] is True + assert out["checks"]["hash_matches"] is True + + +def test_parse_ed25519_pubkey_rejects_invalid_base64(): + import pytest + + with pytest.raises(Exception): + parse_ed25519_pubkey("not_base64!!!") diff --git a/typescript-sdk/README.md b/typescript-sdk/README.md index 00ad654..0cf677a 100644 --- a/typescript-sdk/README.md +++ b/typescript-sdk/README.md @@ -1,330 +1,330 @@ -# CommandLayer TypeScript SDK - -Semantic verbs. Typed schemas. Signed receipts. - -This package provides the official TypeScript/JavaScript SDK for **CommandLayer Commons v1.0.0**. - -Install → call a verb → receive a signed receipt → verify it. - ---- - -## Overview - -CommandLayer is the semantic verb layer for autonomous agents. - -The SDK provides: - -- Standardized Commons verbs (`summarize`, `analyze`, `fetch`, etc.) -- Strict JSON Schemas (requests + receipts) -- Cryptographically signed receipts (Ed25519 + SHA-256) -- Deterministic canonicalization (`cl-stable-json-v1`) -- Verification helpers (offline or ENS-based) -- CLI for reproducible local testing - ---- - -## Installation - -```bash -npm install @commandlayer/sdk -``` - -### Quickstart (TypeScript) -``` -import { createClient } from "@commandlayer/sdk"; - -const client = createClient({ - actor: "my-app" -}); - -const receipt = await client.summarize({ - content: "CommandLayer turns agent actions into verifiable receipts.", - style: "bullet_points" -}); - -console.log(receipt.result.summary); -console.log(receipt.metadata.receipt_id); -``` ---- - -### Runtime Configuration - -Default runtime: -``` -https://runtime.commandlayer.org -``` - -Override if needed: -``` -const client = createClient({ - actor: "my-app", - runtime: "https://your-runtime.example", - verifyReceipts: true -}); -``` - -verifyReceipts should remain enabled in production. - ---- - -### Receipt Structure - -Every call returns a signed receipt: - -``` -{ - "status": "success", - "x402": { - "verb": "summarize", - "version": "1.0.0", - "entry": "x402://summarizeagent.eth/summarize/v1.0.0" - }, - "trace": { - "trace_id": "trace_ab12cd34", - "duration_ms": 118 - }, - "result": { - "summary": "..." - }, - "metadata": { - "receipt_id": "8f0a...", - "proof": { - "alg": "ed25519-sha256", - "canonical": "cl-stable-json-v1", - "signer_id": "runtime.commandlayer.eth", - "hash_sha256": "...", - "signature_b64": "..." - } - } -} -``` - -Receipt guarantees: - -- Stable canonical hashing -- SHA-256 digest over unsigned receipt -- Ed25519 signature over the hash -- Deterministic validation across runtimes - ---- - -### Verifying Receipts - - -**Option A — Offline (explicit public key)** - -Fastest method. No RPC required. -``` -import { verifyReceipt } from "@commandlayer/sdk"; - -const result = await verifyReceipt(receipt, { - publicKeyPem: `-----BEGIN PUBLIC KEY----- -MCowBQYDK2VwAyEA7Vkkmt6R02Iltp/+i3D5mraZyvLjfuTSVB33KwfzQC8= ------END PUBLIC KEY-----` -}); - -console.log(result.ok); -``` ---- - -**Option B — ENS-based Verification** - -Resolves the public key from ENS TXT records. - -Required ENS records: - -- `cl.receipt.pubkey_pem` -- `cl.receipt.signer_id` -- `cl.receipt.alg` -- -Example: -``` -import { verifyReceipt } from "@commandlayer/sdk"; - -const out = await verifyReceipt(receipt, { - ens: { - name: "runtime.commandlayer.eth", - rpcUrl: process.env.ETH_RPC_URL! - } -}); - -console.log(out.ok, out.values.pubkey_source); -``` - -ENS affects verification correctness — not build or publishing. - ---- - -**Commons Verbs** - -All verbs return signed receipts. -``` -await client.summarize({ content, style: "bullet_points" }); - -await client.analyze({ - content, - dimensions: ["sentiment", "tone"] -}); - -await client.classify({ - content, - categories: ["support", "billing"] -}); - -await client.clean({ - content, - operations: ["trim", "normalize_newlines"] -}); - -await client.convert({ - content, - from: "json", - to: "csv" -}); - -await client.describe({ - subject, - detail_level: "medium" -}); - -await client.explain({ - subject, - style: "step-by-step" -}); - -await client.format({ - content, - to: "table" -}); - -await client.parse({ - content, - content_type: "json" -}); - -await client.fetch({ - source: "https://example.com" -}); -``` - -See `EXAMPLES.md` for full technical payloads. - -### CLI - -The SDK includes a CLI for local testing. - -**Build First** -``` -npm run build -``` - -Expected output: - -- `dist/index.js` -- `dist/index.d.ts` - -**Run** -``` -node bin/cli.js summarize --content "test" --style bullet_points --json -``` - -***Global Link (Optional)** -``` -npm link -commandlayer --help -commandlayer summarize --content "test" --style bullet_points -``` - -To unlink: -``` -npm unlink -g @commandlayer/sdk || true -``` -### Windows esbuild EBUSY Fix - -- If install fails with EBUSY: -- Run terminal as Administrator -- Temporarily disable Defender real-time protection -- Close processes locking `node_modules` -- Delete `node_modules` -- Retry `npm install` - -If you see: -``` -'tsup' is not recognized -``` - -`npm install` did not complete successfully. - ---- - -### Local Development Workflow -``` -sdk/ - typescript-sdk/ - src/ - dist/ - bin/ -``` - -Typical flow: -``` -cd typescript-sdk -npm install -npm run build -node bin/cli.js summarize --content "test" --style bullet_points --json -``` ---- - -### Publishing to npm (Optional) - -Ensure `typescript-sdk/package.json` includes: - -- name -- version -- main / module / exports → dist/* -- types → dist/index.d.ts -- bin → bin/cli.js -- files → dist/ and bin/ - -Then: -``` -npm login -npm publish --access public -``` ---- - -### Versioning - -Use Semantic Versioning: - -- Patch → bug fixes -- Minor → backward-compatible additions -- Major → breaking changes - -Release flow: - -Update `CHANGELOG.md` -`npm version patch|minor|major` -`npm run build` -CLI smoke test -Tag + push - -Publish - -### Definition of Done - -You are deployed when: - -- `npm install` succeeds cleanly -- `npm run build` produces `dist/` -- CLI returns valid receipt JSON -- CI reproduces the same steps on push - ---- -License - -MIT - -CommandLayer turns agent actions into verifiable infrastructure. - -Ship APIs that can prove what they did. +# CommandLayer TypeScript SDK + +Semantic verbs. Typed schemas. Signed receipts. + +This package provides the official TypeScript/JavaScript SDK for **CommandLayer Commons v1.0.0**. + +Install → call a verb → receive a signed receipt → verify it. + +--- + +## Overview + +CommandLayer is the semantic verb layer for autonomous agents. + +The SDK provides: + +- Standardized Commons verbs (`summarize`, `analyze`, `fetch`, etc.) +- Strict JSON Schemas (requests + receipts) +- Cryptographically signed receipts (Ed25519 + SHA-256) +- Deterministic canonicalization (`cl-stable-json-v1`) +- Verification helpers (offline or ENS-based) +- CLI for reproducible local testing + +--- + +## Installation + +```bash +npm install @commandlayer/sdk +``` + +### Quickstart (TypeScript) +``` +import { createClient } from "@commandlayer/sdk"; + +const client = createClient({ + actor: "my-app" +}); + +const receipt = await client.summarize({ + content: "CommandLayer turns agent actions into verifiable receipts.", + style: "bullet_points" +}); + +console.log(receipt.result.summary); +console.log(receipt.metadata.receipt_id); +``` +--- + +### Runtime Configuration + +Default runtime: +``` +https://runtime.commandlayer.org +``` + +Override if needed: +``` +const client = createClient({ + actor: "my-app", + runtime: "https://your-runtime.example", + verifyReceipts: true +}); +``` + +verifyReceipts should remain enabled in production. + +--- + +### Receipt Structure + +Every call returns a signed receipt: + +``` +{ + "status": "success", + "x402": { + "verb": "summarize", + "version": "1.0.0", + "entry": "x402://summarizeagent.eth/summarize/v1.0.0" + }, + "trace": { + "trace_id": "trace_ab12cd34", + "duration_ms": 118 + }, + "result": { + "summary": "..." + }, + "metadata": { + "receipt_id": "8f0a...", + "proof": { + "alg": "ed25519-sha256", + "canonical": "cl-stable-json-v1", + "signer_id": "runtime.commandlayer.eth", + "hash_sha256": "...", + "signature_b64": "..." + } + } +} +``` + +Receipt guarantees: + +- Stable canonical hashing +- SHA-256 digest over unsigned receipt +- Ed25519 signature over the hash +- Deterministic validation across runtimes + +--- + +### Verifying Receipts + + +**Option A — Offline (explicit public key)** + +Fastest method. No RPC required. +``` +import { verifyReceipt } from "@commandlayer/sdk"; + +const result = await verifyReceipt(receipt, { + publicKey: "ed25519:7Vkkmt6R02Iltp/+i3D5mraZyvLjfuTSVB33KwfzQC8=" +}); + +console.log(result.ok); +``` +--- + +**Option B — ENS-based Verification** + +Resolves the public key from ENS TXT records. + +Required ENS records: + +- `cl.receipt.pubkey_pem` +- `cl.receipt.signer_id` +- `cl.receipt.alg` +- +Example: +``` +import { verifyReceipt } from "@commandlayer/sdk"; + +const out = await verifyReceipt(receipt, { + ens: { + name: "runtime.commandlayer.eth", + rpcUrl: process.env.ETH_RPC_URL! + } +}); + +console.log(out.ok, out.values.pubkey_source); +``` + +ENS affects verification correctness — not build or publishing. + +--- + +**Commons Verbs** + +All verbs return signed receipts. +``` +await client.summarize({ content, style: "bullet_points" }); + +await client.analyze({ + content, + dimensions: ["sentiment", "tone"] +}); + +await client.classify({ + content, + categories: ["support", "billing"] +}); + +await client.clean({ + content, + operations: ["trim", "normalize_newlines"] +}); + +await client.convert({ + content, + from: "json", + to: "csv" +}); + +await client.describe({ + subject, + detail_level: "medium" +}); + +await client.explain({ + subject, + style: "step-by-step" +}); + +await client.format({ + content, + to: "table" +}); + +await client.parse({ + content, + content_type: "json" +}); + +await client.fetch({ + source: "https://example.com" +}); +``` + +See `EXAMPLES.md` for full technical payloads. + +### CLI + +The SDK includes a CLI for local testing. + +**Build First** +``` +npm run build +``` + +Expected output: + +- `dist/index.cjs` +- `dist/index.mjs` +- `dist/cli.cjs` +- `dist/index.d.ts` + +**Run** +``` +node dist/cli.cjs summarize --content "test" --style bullet_points --json +``` + +***Global Link (Optional)** +``` +npm link +commandlayer --help +commandlayer summarize --content "test" --style bullet_points +``` + +To unlink: +``` +npm unlink -g @commandlayer/sdk || true +``` +### Windows esbuild EBUSY Fix + +- If install fails with EBUSY: +- Run terminal as Administrator +- Temporarily disable Defender real-time protection +- Close processes locking `node_modules` +- Delete `node_modules` +- Retry `npm install` + +If you see: +``` +'tsup' is not recognized +``` + +`npm install` did not complete successfully. + +--- + +### Local Development Workflow +``` +sdk/ + typescript-sdk/ + src/ + dist/ +``` + +Typical flow: +``` +cd typescript-sdk +npm install +npm run build +npm run test:cli-smoke +node dist/cli.cjs summarize --content "test" --style bullet_points --json +``` +--- + +### Publishing to npm (Optional) + +Ensure `typescript-sdk/package.json` includes: + +- name +- version +- main / module / exports → dist/* +- types → dist/index.d.ts +- bin → bin/cli.js +- files → dist/ and bin/ + +Then: +``` +npm login +npm publish --access public +``` +--- + +### Versioning + +Use Semantic Versioning: + +- Patch → bug fixes +- Minor → backward-compatible additions +- Major → breaking changes + +Release flow: + +Update `CHANGELOG.md` +`npm version patch|minor|major` +`npm run build` +CLI smoke test +Tag + push + +Publish + +### Definition of Done + +You are deployed when: + +- `npm install` succeeds cleanly +- `npm run build` produces `dist/` +- CLI returns valid receipt JSON +- CI reproduces the same steps on push + +--- +License + +MIT + +CommandLayer turns agent actions into verifiable infrastructure. + +Ship APIs that can prove what they did. diff --git a/typescript-sdk/package-lock.json b/typescript-sdk/package-lock.json new file mode 100644 index 0000000..599aa81 --- /dev/null +++ b/typescript-sdk/package-lock.json @@ -0,0 +1,1605 @@ +{ + "name": "@commandlayer/sdk", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@commandlayer/sdk", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "commander": "^12.1.0", + "ethers": "^6.13.4", + "tweetnacl": "^1.0.3" + }, + "bin": { + "commandlayer": "dist/cli.cjs" + }, + "devDependencies": { + "tsup": "^8.2.4", + "typescript": "^5.6.3" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "license": "MIT" + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/ethers": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz", + "integrity": "sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "22.7.5", + "aes-js": "4.0.0-beta.5", + "tslib": "2.7.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "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 + } + } + } + } +} diff --git a/typescript-sdk/package.json b/typescript-sdk/package.json index 9115b82..8e581c5 100644 --- a/typescript-sdk/package.json +++ b/typescript-sdk/package.json @@ -17,13 +17,18 @@ "bin": { "commandlayer": "./dist/cli.cjs" }, - "files": ["dist"], - "engines": { "node": ">=22" }, + "files": [ + "dist" + ], + "engines": { + "node": ">=22" + }, "scripts": { "build": "tsup", "dev": "tsup --watch", "typecheck": "tsc -p tsconfig.json --noEmit", - "prepack": "npm run build" + "prepack": "npm run build", + "test:cli-smoke": "node scripts/cli-smoke.mjs" }, "dependencies": { "commander": "^12.1.0", diff --git a/typescript-sdk/scripts/cli-smoke.mjs b/typescript-sdk/scripts/cli-smoke.mjs new file mode 100644 index 0000000..a6c0469 --- /dev/null +++ b/typescript-sdk/scripts/cli-smoke.mjs @@ -0,0 +1,48 @@ +import { spawnSync } from "node:child_process"; +import { fileURLToPath } from "node:url"; +import path from "node:path"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const sdkDir = path.resolve(__dirname, ".."); +const cliPath = path.join(sdkDir, "dist", "cli.cjs"); + +function runCase(name, args, expected) { + const result = spawnSync("node", [cliPath, ...args], { + cwd: sdkDir, + encoding: "utf8" + }); + + const output = `${result.stdout || ""}\n${result.stderr || ""}`; + + if (result.status !== expected.exitCode) { + throw new Error( + `${name}: expected exit code ${expected.exitCode}, got ${result.status}.\nOutput:\n${output}` + ); + } + + for (const snippet of expected.includes) { + if (!output.includes(snippet)) { + throw new Error(`${name}: missing expected output snippet: "${snippet}"\nOutput:\n${output}`); + } + } + + console.log(`PASS: ${name}`); +} + +runCase("help output", ["--help"], { + exitCode: 0, + includes: ["Usage: commandlayer", "CommandLayer TypeScript SDK CLI"] +}); + +runCase("argument validation", ["summarize"], { + exitCode: 1, + includes: ["required option '--content ' not specified"] +}); + +runCase("bad JSON path", ["call", "--verb", "summarize", "--body", "{not-json}"], { + exitCode: 1, + includes: ["commandlayer:", "Expected property name or '}' in JSON"] +}); + +console.log("CLI smoke tests passed."); diff --git a/typescript-sdk/src/cli.ts b/typescript-sdk/src/cli.ts new file mode 100644 index 0000000..493acdb --- /dev/null +++ b/typescript-sdk/src/cli.ts @@ -0,0 +1,94 @@ +import { Command } from "commander"; +import { createClient, type Receipt } from "./index"; + +const program = new Command(); + +program + .name("commandlayer") + .description("CommandLayer TypeScript SDK CLI") + .option("--runtime ", "CommandLayer runtime base URL", "https://runtime.commandlayer.org") + .option("--actor ", "Actor id used in requests", "sdk-cli") + .option("--timeout-ms ", "Request timeout in milliseconds", "30000") + .option("--json", "Print full JSON receipt", false); + +function printResult(receipt: Receipt, jsonOutput: boolean) { + if (jsonOutput) { + console.log(JSON.stringify(receipt, null, 2)); + return; + } + + if (receipt.status) console.log(`status: ${receipt.status}`); + if (receipt.metadata?.receipt_id) console.log(`receipt_id: ${receipt.metadata.receipt_id}`); + if (receipt.result !== undefined) { + console.log("result:"); + console.log(JSON.stringify(receipt.result, null, 2)); + } +} + +function withCommonOptions(cmd: Command) { + return cmd.requiredOption("--content ", "Input content").option("--max-tokens ", "Max output tokens", "1000"); +} + +withCommonOptions( + program + .command("summarize") + .description("Summarize content") + .option("--style