@contractlane/operator-sdk
v0.4.0
Published
TypeScript SDK for Contract Lane Operator API
Readme
@contractlane/operator-sdk
TypeScript SDK for the Contract Lane Operator API.
Install
npm install @contractlane/operator-sdkQuickstart
import { OperatorClient } from "@contractlane/operator-sdk";
const client = new OperatorClient({
baseUrl: "https://localhost",
sessionToken: async () => process.env.OPERATOR_SESSION_TOKEN,
operatorToken: async () => process.env.OPERATOR_OPERATOR_TOKEN,
challengeHeaders: async () => ({
"X-Signup-Challenge": process.env.OPERATOR_SIGNUP_CHALLENGE_TOKEN ?? "",
"X-Operator-Challenge": process.env.OPERATOR_PROOF_CHALLENGE ?? "",
"X-Operator-Challenge-Token": process.env.OPERATOR_CHALLENGE_TOKEN ?? "",
}),
});1) Human signup/auth
const start = await client.public.signup.start({ email: "[email protected]", org_name: "Acme" });
const sessionId = String(start.data.signup_session?.session_id ?? "");
await client.public.signup.verify({ session_id: sessionId, verification_code: "123456" });
await client.public.signup.complete({ session_id: sessionId, project_name: "Default", agent_name: "Primary" });
const magic = await client.public.auth.magicLinkStart({ email: "[email protected]" });
const linkId = String(magic.data.magic_link?.link_id ?? "");
// token available in development mode responses
await client.public.auth.magicLinkVerify({ link_id: linkId, token: "token" });2) Admin actor + credential issue
const org = await client.operator.admin.createOrg({ name: "Acme", admin_email: "[email protected]" });
const orgId = String(org.data.org?.org_id ?? "");
const project = await client.operator.admin.createProject(orgId, { name: "Project A" });
const projectId = String(project.data.project?.project_id ?? "");
const actor = await client.operator.admin.createActor(projectId, { name: "Bot", scopes: ["cel.contracts:write"] });
const actorId = String(actor.data.actor?.actor_id ?? "");
await client.operator.admin.issueCredential({ actor_id: actorId, upstream_token: "upstream-token" });3) Agent-first enrollment
const challenge = await client.public.agentEnrollment.challenge({
public_key_jwk: { kty: "OKP", crv: "Ed25519", x: "..." },
});
const challengeId = String(challenge.data.challenge?.challenge_id ?? "");
await client.public.agentEnrollment.start({
challenge_id: challengeId,
signature: "base64url-signature",
sponsor_email: "[email protected]",
requested_scopes: ["cel.contracts:write"],
});4) Gateway contract + counterparty action
await client.gateway.cel.createEnvelope({
template_id: "tpl_123",
variables: { amount: "100.00" },
counterparty: { email: "[email protected]" },
});
await client.gateway.cel.setCounterparty("ctr_123", { email: "[email protected]" });
await client.gateway.cel.advancedAction("ctr_123", "SEND_FOR_SIGNATURE", {});5) Public signing + actor key lifecycle
await client.public.signing.resolve("sign_tok");
await client.public.signing.accept("sign_tok", {
challenge_id: "chal_123",
signature: "base64url-signature",
});
await client.operator.actorKeys.challenge("act_123", {
public_key_jwk: { kty: "OKP", crv: "Ed25519", x: "..." },
});
await client.operator.actorKeys.list("act_123");
await client.operator.admin.listActorsCompat("prj_123");6) Active envelopes
await client.operator.envelopes.list({ include_terminal: false, page_size: 20 }); // active only
await client.operator.envelopes.list({ include_terminal: true, sort_by: "updated_at", sort_order: "desc" }); // active + terminal
await client.operator.envelopes.get("ctr_123");
await client.operator.envelopes.getByEnvelopeId("env_123");Notes
- Mutating operator/public endpoints auto-inject
Idempotency-Key. request_idis exposed inresult.meta.requestIdandresult.meta.request_id.- Challenge hooks can provide
X-Signup-Challenge,X-Operator-Challenge, andX-Operator-Challenge-Token; per-request headers can override hook values. - API errors throw
APIErrorwithstatus,code,message,requestId(request_idalias),meta,rawBody. - Template enable accepts optional
enabled_by_actor_id; if provided it must be an active actor id in the project. operator.history.get(envelopeId)normalizes missing history records to a pending shape.operator.envelopes.*reads active lifecycle index state (in-flight + optional terminal).operator.history.*remains finalized/indexed audit feed.- Prefer
gateway.cel.setCounterparty()over deprecated plural/staged wrappers.
See SDK_BEHAVIOR_MATRIX.md for canonical/deprecated method parity and backend-behavior notes.
Integration Tests
Run against a live operator stack:
NODE_TLS_REJECT_UNAUTHORIZED=0 npm run test:integrationIntegration env is loaded by monorepo make integration-test from:
/Users/sam/code/contractlane-sdk/env/.env.integration- fallback
/Users/sam/code/contractlane-sdk/contractlane-operator/env/.env.demo
Strictness controls:
TEST_ADMIN_STRICT_SUCCESS=1: require admin flow success; otherwise unauthorized admin environments are treated as non-blocking.TEST_GATEWAY_STRICT_SUCCESS=1: require gateway create/action/proof success; otherwise404is treated as environment limitation.
