Skip to content
This repository was archived by the owner on May 24, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
# CommandLayer SDK

Official SDK repo for the current-line CommandLayer Commons receipt contract (`1.1.0`).
CommandLayer turns AI/runtime actions into verifiable receipts you can run and audit from one SDK.

```bash
npm install @commandlayer/sdk
```

```ts
import { commandlayer } from "@commandlayer/sdk";
const receipt = await commandlayer.run("summarize", {
text: "Agent receipts prove what happened."
});
const result = await commandlayer.verify(receipt);
console.log(receipt);
console.log(result.valid ?? result.ok);
```

For advanced usage (custom clients, ENS/public key verification, and protocol details), see the docs below.

## What this repo now treats as canonical

Expand Down
14 changes: 14 additions & 0 deletions examples/quickstart-node/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Quickstart (Node.js)

Install dependencies and run:

```bash
npm install
COMMANDLAYER_PUBLIC_KEY='ed25519:BASE64_PUBLIC_KEY' npm start
```

This example:
1. Runs a sample `summarize` action with `commandlayer.run(...)`.
2. Prints the returned receipt payload.
3. Verifies the receipt with `commandlayer.verify(...)`.
4. Prints whether verification passed.
13 changes: 13 additions & 0 deletions examples/quickstart-node/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { commandlayer } from "@commandlayer/sdk";

const receipt = await commandlayer.run("summarize", {
text: "Agent receipts prove what happened."
});

console.log("Receipt:", JSON.stringify(receipt, null, 2));

const verification = await commandlayer.verify(receipt, {
publicKey: process.env.COMMANDLAYER_PUBLIC_KEY
});

console.log("Verification passed:", verification.ok);
11 changes: 11 additions & 0 deletions examples/quickstart-node/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "commandlayer-quickstart-node",
"private": true,
"type": "module",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"@commandlayer/sdk": "latest"
}
}
18 changes: 17 additions & 1 deletion typescript-sdk/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
# CommandLayer TypeScript SDK

Current-line TypeScript SDK for the CommandLayer Commons receipt contract (`1.1.0`).
CommandLayer turns AI/runtime actions into verifiable receipts with a simple run + verify API.

```bash
npm install @commandlayer/sdk
```

```ts
import { commandlayer } from "@commandlayer/sdk";
const receipt = await commandlayer.run("summarize", {
text: "Agent receipts prove what happened."
});
const result = await commandlayer.verify(receipt);
console.log(receipt);
console.log(result.valid ?? result.ok);
```

For advanced usage and lower-level APIs, continue below.

## What is canonical

Expand Down
47 changes: 43 additions & 4 deletions typescript-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ export type CanonicalReceipt<T = unknown> = {
[k: string]: unknown;
};

export type ReceiptProtocolMetadata = {
verb: string;
version: string;
[k: string]: unknown;
};

export type RuntimeMetadata = {
trace_id?: string;
parent_trace_id?: string | null;
Expand All @@ -77,11 +83,11 @@ export type RuntimeMetadata = {
};

export type CommandResponse<TResult = unknown, TError = unknown> = {
receipt: CanonicalReceipt<TResult, TError>;
receipt: CanonicalReceipt<TResult>;
runtime_metadata?: RuntimeMetadata;
};

export type LegacyBlendedReceipt<TResult = unknown, TError = unknown> = CanonicalReceipt<TResult, TError> & {
export type LegacyBlendedReceipt<TResult = unknown, TError = unknown> = CanonicalReceipt<TResult> & {
trace?: RuntimeMetadata;
};

Expand Down Expand Up @@ -115,18 +121,23 @@ export type VerifyResult = {
verify_error?: string | null;
};
};
export type HighLevelVerifyResult = VerifyResult & { valid: boolean };

export type EnsVerifyOptions = { name: string; rpcUrl: string };
export type SignerKeyResolution = { algorithm: "ed25519"; kid: string; rawPublicKeyBytes: Uint8Array };
export type VerifyOptions = { publicKey?: string; ens?: EnsVerifyOptions };
export type ClientOptions = {
baseUrl?: string;
runtime?: string;
actor?: string;
timeoutMs?: number;
fetchImpl?: typeof fetch;
verifyReceipts?: boolean;
verify?: VerifyOptions;
};
export type RunOptions = {
actor?: string;
};

export type CommonsRequestEnvelope<TBody extends Record<string, unknown> = Record<string, unknown>> = {
x402: ReceiptProtocolMetadata;
Expand Down Expand Up @@ -295,6 +306,7 @@ function extractReceipt(subject: CanonicalReceipt | CommandResponse | LegacyBlen

export function extractReceiptVerb(subject: CanonicalReceipt | CommandResponse | LegacyBlendedReceipt): string | null {
const receipt = extractReceipt(subject);
if (typeof receipt.verb === "string") return receipt.verb;
return isRecord(receipt.x402) && typeof receipt.x402.verb === "string" ? receipt.x402.verb : null;
}

Expand Down Expand Up @@ -349,6 +361,7 @@ export async function verifyReceipt(receiptLike: CanonicalReceipt | CommandRespo
const { hash_sha256: recomputedHash } = recomputeReceiptHashSha256(receipt);
const hashMatches = claimedHash === recomputedHash;
const receiptId = typeof receipt.metadata?.receipt_id === "string" ? receipt.metadata.receipt_id : null;
const receiptIdPresent = receiptId !== null;
const receiptIdMatches = !receiptId || !claimedHash ? true : receiptId === claimedHash;

let pubkey: Uint8Array | null = null;
Expand Down Expand Up @@ -396,7 +409,7 @@ export async function verifyReceipt(receiptLike: CanonicalReceipt | CommandRespo
},
values: {
verb: getReceiptVerb(receipt),
signer_id,
signer_id: signerId,
alg,
canonical,
claimed_hash: claimedHash,
Expand Down Expand Up @@ -444,7 +457,7 @@ export class CommandLayerClient {
verifyDefaults?: VerifyOptions;

constructor(opts: ClientOptions = {}) {
this.runtime = normalizeBase(opts.runtime || DEFAULT_RUNTIME);
this.runtime = normalizeBase(opts.baseUrl || opts.runtime || DEFAULT_RUNTIME);
this.actor = opts.actor || "sdk-user";
this.timeoutMs = opts.timeoutMs ?? 30_000;
this.fetchImpl = opts.fetchImpl || fetch;
Expand Down Expand Up @@ -509,6 +522,20 @@ export class CommandLayerClient {
return this.call("fetch", { input: { source: opts.source, ...(opts.query !== undefined ? { query: opts.query } : {}), ...(opts.include_metadata !== undefined ? { include_metadata: opts.include_metadata } : {}) }, limits: { max_output_tokens: opts.maxTokens ?? 1000 } });
}

async run(action: Verb, input: Record<string, unknown>, options: RunOptions = {}): Promise<CommandResponse> {
return this.call(action, {
...(options.actor ? { actor: options.actor } : {}),
input
});
}

async verify(receipt: CanonicalReceipt | CommandResponse, options: VerifyOptions = {}): Promise<VerifyResult> {
return verifyReceipt(receipt, {
...(this.verifyDefaults || {}),
...options
});
}

async call(verb: Verb, body: Record<string, unknown>): Promise<CommandResponse> {
if (!isVerb(verb)) throw new CommandLayerError(`Unsupported verb: ${verb}`, 400);
this.ensureVerifyConfigIfEnabled();
Expand Down Expand Up @@ -559,3 +586,15 @@ export class CommandLayerClient {
export function createClient(opts: ClientOptions = {}) {
return new CommandLayerClient(opts);
}

const defaultClient = createClient();

export const commandlayer = {
run(action: Verb, input: Record<string, unknown>, options?: RunOptions) {
return defaultClient.run(action, input, options);
},
async verify(receipt: CanonicalReceipt | CommandResponse, options?: VerifyOptions): Promise<HighLevelVerifyResult> {
const result = await defaultClient.verify(receipt, options);
return { ...result, valid: result.ok };
}
};
54 changes: 54 additions & 0 deletions typescript-sdk/tests/high-level-api.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import test from "node:test";
import assert from "node:assert/strict";
import { createRequire } from "node:module";

const require = createRequire(import.meta.url);
const { createClient, commandlayer, CommandLayerClient, verifyReceipt } = require("../dist/index.cjs");

test("createClient uses default baseUrl", () => {
const client = createClient();
assert.equal(client.runtime, "https://runtime.commandlayer.org");
});

test("custom baseUrl override works", () => {
const client = createClient({ baseUrl: "https://example.com/runtime/" });
assert.equal(client.runtime, "https://example.com/runtime");
});

test("commandlayer.run delegates to client.run", async () => {
const originalRun = CommandLayerClient.prototype.run;
CommandLayerClient.prototype.run = async function(action, input, options) {
return { receipt: { status: "success" }, runtime_metadata: { action, input, options } };
};

try {
const result = await commandlayer.run("summarize", { text: "hello" });
assert.equal(result.runtime_metadata.action, "summarize");
assert.deepEqual(result.runtime_metadata.input, { text: "hello" });
} finally {
CommandLayerClient.prototype.run = originalRun;
}
});

test("commandlayer.verify delegates to verification logic", async () => {
const originalVerify = CommandLayerClient.prototype.verify;
const sentinel = { ok: true, checks: {}, values: {}, errors: {} };

CommandLayerClient.prototype.verify = async function(receipt, options) {
assert.equal(typeof verifyReceipt, "function");
assert.deepEqual(receipt, { receipt: { status: "success" } });
assert.deepEqual(options, { publicKey: "ed25519:abc" });
return sentinel;
};

try {
const result = await commandlayer.verify(
{ receipt: { status: "success" } },
{ publicKey: "ed25519:abc" }
);
assert.equal(result.ok, true);
assert.equal(result.valid, true);
} finally {
CommandLayerClient.prototype.verify = originalVerify;
}
});
Loading