UNPKG

kestrel.markets

Version:

A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.

201 lines (199 loc) 6.99 kB
import { parseArgs } from "./bin-1gc4zavq.js"; import { CliError, EXIT } from "./bin-t9ggwnv5.js"; import { defaultCredentialsPath, defaultKeypairPath, loadCredential, loadKeypair, saveCredential } from "./bin-q0g6xvsw.js"; import"./bin-wckvcay0.js"; // src/cli/commands/self.ts function notRegistered(credPath) { return new CliError({ code: "NOT_FOUND", exit: EXIT.NOT_FOUND, message: `no stored credential at ${credPath} — this CLI is anonymous (no registered identity)`, hint: "run `kestrel register` to self-register and mint a durable capability (proof-before-account: `sim`/`prove` still work anonymously)" }); } var EXPIRING_SOON_MS = 5 * 60 * 1000; function expiryStatus(expiry, nowMs) { const at = Date.parse(expiry); if (Number.isNaN(at)) return { state: "unknown", remaining_ms: null, label: "unknown (unparseable expiry)" }; const remaining = at - nowMs; if (remaining <= 0) return { state: "expired", remaining_ms: remaining, label: `EXPIRED ${fmtDuration(-remaining)} ago — run \`kestrel refresh\`` }; if (remaining <= EXPIRING_SOON_MS) return { state: "expiring", remaining_ms: remaining, label: `expiring in ${fmtDuration(remaining)} — run \`kestrel refresh\`` }; return { state: "valid", remaining_ms: remaining, label: `valid, expires in ${fmtDuration(remaining)}` }; } function fmtDuration(ms) { const totalSec = Math.floor(ms / 1000); const h = Math.floor(totalSec / 3600); const m = Math.floor(totalSec % 3600 / 60); const s = totalSec % 60; if (h > 0) return `${h}h${String(m).padStart(2, "0")}m`; return `${m}m${String(s).padStart(2, "0")}s`; } function whoamiCommand(argv, ctx, deps = {}) { parseArgs(argv, new Set, new Set); const env = deps.env ?? process.env; const credPath = deps.credentialsPath ?? defaultCredentialsPath(env); const cred = loadCredential(credPath); if (cred === undefined) throw notRegistered(credPath); const keyPath = deps.keypairPath ?? defaultKeypairPath(env); const keypair = loadKeypair(keyPath); const nowMs = (deps.now ?? Date.now)(); const status = expiryStatus(cred.expiry, nowMs); if (ctx.mode === "json") { process.stdout.write(JSON.stringify({ schema: "kestrel.whoami/v1", agent_id: cred.agent_id, subject: cred.subject ?? null, api: cred.api, token_type: cred.token_type, scopes: cred.scopes, expiry: cred.expiry, expiry_status: status.state, expiry_remaining_ms: status.remaining_ms, keypair_enrolled: keypair !== undefined, credential_path: credPath, ...keypair !== undefined ? { keyfile_path: keyPath } : {}, ...cred.claimed !== undefined ? { claimed_unverified: cred.claimed } : {} }) + ` `); return 0; } if (ctx.mode === "text") { const lines = [ `agent_id ${cred.agent_id}`, `subject ${cred.subject ?? ""}`, `api ${cred.api}`, `token_type ${cred.token_type}`, `scopes ${cred.scopes.join(",")}`, `expiry ${cred.expiry}`, `expiry_status ${status.state}`, `keypair_enrolled ${keypair !== undefined}`, `credential_path ${credPath}` ]; process.stdout.write(lines.join(` `) + ` `); return 0; } const out = [ `agent_id ${cred.agent_id}`, `subject ${cred.subject ?? "(none)"}`, `api ${cred.api}`, `token_type ${cred.token_type}`, `scopes ${cred.scopes.join(", ")}`, `expiry ${cred.expiry} (${status.label})`, `keypair ${keypair !== undefined ? `enrolled — ${keyPath}` : "not enrolled"}`, `credential ${credPath}` ]; if (cred.claimed !== undefined) out.push(`claimed ${cred.claimed.name ?? "(no name)"} <${cred.claimed.email ?? "no-email"}> (UNVERIFIED)`); process.stdout.write(out.join(` `) + ` `); return 0; } async function problemDetail(res, fallback) { try { const b = await res.json(); return b.detail ?? b.title ?? fallback; } catch { return fallback; } } async function refreshCommand(argv, ctx, _globals, deps = {}) { parseArgs(argv, new Set, new Set); const env = deps.env ?? process.env; const credPath = deps.credentialsPath ?? defaultCredentialsPath(env); const cred = loadCredential(credPath); if (cred === undefined) throw notRegistered(credPath); const fetchImpl = deps.fetch ?? globalThis.fetch; if (fetchImpl === undefined) throw new CliError({ code: "RUNTIME_UNAVAILABLE", exit: EXIT.RUNTIME_UNAVAILABLE, message: "global fetch unavailable — need node ≥18 or bun to refresh" }); const base = cred.api.replace(/\/+$/, ""); const res = await fetchImpl(`${base}/capabilities/refresh`, { method: "POST", headers: { "content-type": "application/json", accept: "application/json", authorization: `Bearer ${cred.capability}` }, body: JSON.stringify({}) }); if (!res.ok) { const detail = await problemDetail(res, `capability refresh failed (HTTP ${res.status})`); throw new CliError({ code: `HTTP_${res.status}`, exit: res.status >= 500 ? EXIT.RUNTIME_UNAVAILABLE : EXIT.GENERIC, message: `capability refresh failed (HTTP ${res.status}): ${detail}`, ...res.status === 401 || res.status === 403 ? { hint: "the current capability may already be past its refresh window — run `kestrel register` to mint a fresh durable capability" } : {} }); } const body = await res.json(); if (typeof body.capability !== "string" || body.capability.length === 0) throw new CliError({ code: "HTTP_502", exit: EXIT.RUNTIME_UNAVAILABLE, message: "capability refresh returned no usable capability token" }); const next = { ...cred, capability: body.capability, ...body.token_type !== undefined ? { token_type: body.token_type } : {}, ...body.subject !== undefined ? { subject: body.subject } : {}, ...body.scopes !== undefined ? { scopes: body.scopes } : {}, ...body.expiry !== undefined ? { expiry: body.expiry } : {} }; saveCredential(next, credPath); emitRefreshResult(ctx, next, credPath); return 0; } function emitRefreshResult(ctx, cred, credPath) { if (ctx.mode === "json") { process.stdout.write(JSON.stringify({ schema: "kestrel.refresh/v1", agent_id: cred.agent_id, subject: cred.subject ?? null, api: cred.api, token_type: cred.token_type, scopes: cred.scopes, expiry: cred.expiry, credential_path: credPath }) + ` `); return; } if (ctx.mode === "text") { process.stdout.write(`refreshed agent_id=${cred.agent_id} scopes=${cred.scopes.join(",")} expiry=${cred.expiry} credential=${credPath} `); return; } process.stdout.write(`refreshed ${cred.agent_id} `); process.stdout.write(` scopes: ${cred.scopes.join(", ")} `); process.stdout.write(` expiry: ${cred.expiry} `); process.stdout.write(` credential: ${credPath} `); } export { whoamiCommand, refreshCommand, expiryStatus };