UNPKG

@astrasyncai/sdk

Version:

Official Node.js SDK for the AstraSync KYA Platform — register and manage AI agents with verified identities

345 lines (336 loc) 11.9 kB
#!/usr/bin/env node "use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // src/errors.ts var AstraSyncError = class extends Error { constructor(message, statusCode, code) { super(message); this.name = "AstraSyncError"; this.statusCode = statusCode; this.code = code; } }; var KYDRequiredError = class extends AstraSyncError { constructor(response) { const kydUrl = response.kydUrl || "https://app.kya.astrasync.ai/developer-profile"; super( `KYD verification required before registering agents. Complete your KYD profile at: ${kydUrl}`, 403, "KYD_REQUIRED" ); this.name = "KYDRequiredError"; this.kydUrl = kydUrl; this.ownerNotified = response.ownerNotified || false; } }; var AuthenticationError = class extends AstraSyncError { constructor(message) { super(message, 401, "AUTH_FAILED"); this.name = "AuthenticationError"; } }; // src/api.ts var DEFAULT_BASE_URL = "https://api.kya.astrasync.ai"; var AstraSync = class { constructor(config = {}) { this.baseUrl = (config.baseUrl || process.env.ASTRASYNC_API_URL || DEFAULT_BASE_URL).replace(/\/+$/, ""); this.apiKey = config.apiKey || process.env.ASTRASYNC_API_KEY; this.email = config.email; this.password = config.password; this.privateKey = config.privateKey; if (!this.apiKey && !this.email) { throw new AuthenticationError( "Authentication required. Provide apiKey, or email+password. Set ASTRASYNC_API_KEY env var or pass config to constructor." ); } if (this.email && !this.password) { throw new AuthenticationError("Password is required when using email authentication."); } } /** * Register a new AI agent on the AstraSync KYA Platform. * Sends full payload including model, framework, PDLSS, and metadata. */ async register(options) { const body = { name: options.name, ...options.description && { description: options.description }, ...options.agentType && { agentType: options.agentType }, ...options.apiEndpoint && { apiEndpoint: options.apiEndpoint }, ...options.model && { model: options.model }, ...options.framework && { framework: options.framework }, ...options.metadata && { metadata: options.metadata }, ...options.pdlss && { pdlss: options.pdlss } }; return this.request("POST", "/api/agents/register", body); } /** * Look up an agent's public profile by ASTRA ID or UUID. */ async verify(agentId) { return this.request("GET", `/api/agents/verify/${agentId}`); } /** * Check API health. */ async health() { const res = await fetch(`${this.baseUrl}/api/health/`); if (!res.ok) { throw new AstraSyncError(`Health check failed: ${res.status}`, res.status); } return res.json(); } // ── Private helpers ────────────────────────────────────────────── async request(method, endpoint, body) { const url = `${this.baseUrl}${endpoint}`; const headers = { "Content-Type": "application/json" }; const token = await this.getAuthToken(); headers["Authorization"] = `Bearer ${token}`; if (this.privateKey) { const signature = await this.signRequest(method, endpoint, body || {}); headers["X-AstraSync-Signature"] = signature; } const res = await fetch(url, { method, headers, ...body ? { body: JSON.stringify(body) } : {} }); if (!res.ok) { const errorBody = await res.json().catch(() => ({ error: res.statusText })); if (res.status === 403 && errorBody.code === "KYD_REQUIRED") { throw new KYDRequiredError(errorBody); } throw new AstraSyncError( errorBody.error || `Request failed: ${res.status}`, res.status, errorBody.code ); } return res.json(); } async getAuthToken() { if (this.apiKey) { return this.apiKey; } if (this.cachedJwt && this.jwtExpiresAt && Date.now() < this.jwtExpiresAt) { return this.cachedJwt; } const res = await fetch(`${this.baseUrl}/api/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: this.email, password: this.password }) }); if (!res.ok) { const errorBody = await res.json().catch(() => ({})); throw new AuthenticationError( errorBody.message || errorBody.error || "Login failed" ); } const data = await res.json(); this.cachedJwt = data.data.token; this.jwtExpiresAt = Date.now() + 6 * 24 * 60 * 60 * 1e3; return this.cachedJwt; } /** * Sign a request using secp256k1 (ethers.js). * Canonical message format: METHOD:ENDPOINT:SORTED_JSON_BODY * Must match apps/backend/src/services/signature-verify.service.ts exactly. */ async signRequest(method, endpoint, body) { const { Wallet } = await import("ethers"); const sorted = this.sortObjectKeys(body); const canonical = `${method}:${endpoint}:${JSON.stringify(sorted)}`; const wallet = new Wallet(this.privateKey); return wallet.signMessage(canonical); } /** Recursively sort object keys for canonical JSON representation. */ sortObjectKeys(obj) { if (obj === null || typeof obj !== "object") { return obj; } if (Array.isArray(obj)) { return obj.map((item) => this.sortObjectKeys(item)); } const sorted = {}; for (const key of Object.keys(obj).sort()) { sorted[key] = this.sortObjectKeys(obj[key]); } return sorted; } }; // src/cli.ts function usage() { console.log(` astrasync - AstraSync KYA Platform CLI USAGE: astrasync [options] <command> [args] GLOBAL OPTIONS: --api-key <key> API key (kya_ prefixed). Also reads ASTRASYNC_API_KEY env. --email <email> Email for login auth --password <pass> Password for login auth --private-key <key> secp256k1 private key for crypto signing --base-url <url> API base URL (default: https://api.kya.astrasync.ai) --help Show this help message COMMANDS: register Register a new AI agent verify <id> Look up an agent by ASTRA ID or UUID health Check API health REGISTER OPTIONS: --name <name> Agent name (required) --description <desc> Agent description --agent-type <type> Agent type (default: general) --api-endpoint <url> Agent API endpoint URL --model-name <name> LLM model name (e.g. claude-opus-4.6) --model-provider <provider> Model provider (e.g. anthropic, openai) --model-type <type> Model type (default: llm) --framework-name <name> Framework name (e.g. langchain, crewai) --framework-version <ver> Framework version EXAMPLES: astrasync --api-key kya_xxx register --name "My Agent" astrasync register --name "My Agent" --model-name gpt-4o --model-provider openai astrasync verify ASTRA-abc123 astrasync health `); } function parseArgs(args) { const globals = {}; const commandArgs = {}; let command = ""; let inCommand = false; for (let i = 0; i < args.length; i++) { const arg = args[i]; if (!inCommand && !arg.startsWith("--")) { command = arg; inCommand = true; continue; } if (arg === "--help" || arg === "-h") { globals["help"] = "true"; continue; } if (arg.startsWith("--")) { const key = arg.slice(2); const value = args[++i] || ""; if (inCommand) { commandArgs[key] = value; } else { globals[key] = value; } } } return { globals, command, commandArgs }; } async function main() { const { globals, command, commandArgs } = parseArgs(process.argv.slice(2)); if (globals["help"] || !command) { usage(); process.exit(command ? 0 : 1); } const config = { apiKey: globals["api-key"], email: globals["email"], password: globals["password"], privateKey: globals["private-key"], baseUrl: globals["base-url"] }; try { if (command === "health") { const client2 = new AstraSync({ apiKey: config.apiKey || "health-check", baseUrl: config.baseUrl }); const result = await client2.health(); console.log(JSON.stringify(result, null, 2)); return; } const client = new AstraSync({ apiKey: config.apiKey, email: config.email, password: config.password, privateKey: config.privateKey, baseUrl: config.baseUrl }); if (command === "register") { const name = commandArgs["name"]; if (!name) { console.error("Error: --name is required for register command"); process.exit(1); } const options = { name, ...commandArgs["description"] && { description: commandArgs["description"] }, ...commandArgs["agent-type"] && { agentType: commandArgs["agent-type"] }, ...commandArgs["api-endpoint"] && { apiEndpoint: commandArgs["api-endpoint"] } }; if (commandArgs["model-name"] && commandArgs["model-provider"]) { options.model = { modelName: commandArgs["model-name"], modelProvider: commandArgs["model-provider"], ...commandArgs["model-type"] && { modelType: commandArgs["model-type"] } }; } if (commandArgs["framework-name"] && commandArgs["framework-version"]) { options.framework = { frameworkName: commandArgs["framework-name"], frameworkVersion: commandArgs["framework-version"] }; } const result = await client.register(options); console.log(JSON.stringify(result, null, 2)); } else if (command === "verify") { const agentId = commandArgs["id"] || process.argv[process.argv.length - 1]; if (!agentId || agentId.startsWith("--")) { console.error("Error: agent ID is required for verify command"); process.exit(1); } const result = await client.verify(agentId); console.log(JSON.stringify(result, null, 2)); } else { console.error(`Unknown command: ${command}`); usage(); process.exit(1); } } catch (error) { if (error instanceof KYDRequiredError) { console.error(` Error: ${error.message}`); if (error.ownerNotified) { console.error("A reminder email has been sent to your registered email address."); } process.exit(1); } if (error instanceof AstraSyncError) { console.error(` Error [${error.code || error.statusCode}]: ${error.message}`); process.exit(1); } throw error; } } main();