repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
public Clawd ADK gateway launch mirror
stars
latest
clone command
git clone gitlawb://did:key:z6Mkq5mY...iFZ5/my-project-publ...git clone gitlawb://did:key:z6Mkq5mY.../my-project-publ...2fa351d6docs: add automaton and perps launch sources16d ago| #1 | #!/usr/bin/env node |
| #2 | import "dotenv/config"; |
| #3 | import { buildPerpsFrontendStatus } from "./frontend.js"; |
| #4 | import { ClawdPerpsRuntime } from "./marketMaker.js"; |
| #5 | import { handleTelegramPerpsCommand } from "./telegram.js"; |
| #6 | import { ImperialClient } from "./imperialAgent.js"; |
| #7 | import { buildOnchainMarketMaker, buildOnchainMarketMakerPlan, getOnchainMarketMakerStatus, runOnchainMarketMaker, } from "./onchainMarketMaker.js"; |
| #8 | import { buildTwammAutomation, buildTwammBuildPlan, buildTwammCrankPlan, buildTwammTestPlan, getTwammAutomationStatus, runTwammCrank, } from "./twammAutomation.js"; |
| #9 | import { buildClawdOiCoreSignal, buildOiRiskGate } from "./signals/oi-core.js"; |
| #10 | function parseArgs(argv) { |
| #11 | const [command = "status", ...tail] = argv; |
| #12 | const rest = []; |
| #13 | const options = {}; |
| #14 | for (let i = 0; i < tail.length; i++) { |
| #15 | const item = tail[i]; |
| #16 | if (!item.startsWith("--")) { |
| #17 | rest.push(item); |
| #18 | continue; |
| #19 | } |
| #20 | const [rawKey, inlineValue] = item.slice(2).split("=", 2); |
| #21 | const next = tail[i + 1]; |
| #22 | if (inlineValue !== undefined) { |
| #23 | options[rawKey] = inlineValue; |
| #24 | } |
| #25 | else if (next && !next.startsWith("--")) { |
| #26 | options[rawKey] = next; |
| #27 | i++; |
| #28 | } |
| #29 | else { |
| #30 | options[rawKey] = true; |
| #31 | } |
| #32 | } |
| #33 | return { command, rest, options }; |
| #34 | } |
| #35 | function repoRoot() { |
| #36 | return process.env.CLAWD_REPO_ROOT || process.cwd(); |
| #37 | } |
| #38 | function asNumber(value, fallback) { |
| #39 | if (typeof value !== "string") |
| #40 | return fallback; |
| #41 | const parsed = Number(value); |
| #42 | return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback; |
| #43 | } |
| #44 | function asBoolean(value) { |
| #45 | if (typeof value === "boolean") |
| #46 | return value; |
| #47 | return typeof value === "string" && ["1", "true", "yes", "on"].includes(value.toLowerCase()); |
| #48 | } |
| #49 | function parseSignalMode(value) { |
| #50 | if (typeof value !== "string") |
| #51 | return "paper"; |
| #52 | if (["observe", "paper", "dry-run", "confirm-each", "auto-execute"].includes(value)) { |
| #53 | return value; |
| #54 | } |
| #55 | return "paper"; |
| #56 | } |
| #57 | function parseDurationMs(value, fallbackMs) { |
| #58 | if (typeof value !== "string") |
| #59 | return fallbackMs; |
| #60 | const match = value.trim().match(/^(\d+(?:\.\d+)?)(ms|s|m)?$/i); |
| #61 | if (!match) |
| #62 | return fallbackMs; |
| #63 | const amount = Number(match[1]); |
| #64 | const unit = (match[2] ?? "ms").toLowerCase(); |
| #65 | if (!Number.isFinite(amount) || amount <= 0) |
| #66 | return fallbackMs; |
| #67 | if (unit === "m") |
| #68 | return Math.round(amount * 60_000); |
| #69 | if (unit === "s") |
| #70 | return Math.round(amount * 1000); |
| #71 | return Math.round(amount); |
| #72 | } |
| #73 | function parseSymbols(value) { |
| #74 | if (typeof value !== "string") |
| #75 | return undefined; |
| #76 | const symbols = value |
| #77 | .split(",") |
| #78 | .map((symbol) => symbol.trim().toUpperCase()) |
| #79 | .filter(Boolean); |
| #80 | return symbols.length ? symbols : undefined; |
| #81 | } |
| #82 | function printJson(data) { |
| #83 | console.log(JSON.stringify(data, null, 2)); |
| #84 | } |
| #85 | function printHelp() { |
| #86 | console.log(`clawd-agents-perps |
| #87 | |
| #88 | Usage: |
| #89 | clawd-agents-perps status |
| #90 | clawd-agents-perps frontend |
| #91 | clawd-agents-perps telegram "/perps" |
| #92 | clawd-agents-perps vulcan |
| #93 | clawd-agents-perps paper-long SOL --notional 100 |
| #94 | clawd-agents-perps paper-short SOL --notional 100 |
| #95 | clawd-agents-perps live-long SOL --notional 100 --leverage 2 |
| #96 | clawd-agents-perps live-short SOL --notional 100 --leverage 2 |
| #97 | clawd-agents-perps imperial-health |
| #98 | clawd-agents-perps imperial-scan --symbols SOL,BTC,ETH --size 100 |
| #99 | clawd-agents-perps imperial-cycle SOL --size 100 |
| #100 | clawd-agents-perps signal oi SOL-PERP --rpc-url "$CLAWD_RPC_URL" --lookback 5m -o json |
| #101 | clawd-agents-perps signal watch SOL-PERP --rpc-url "$CLAWD_RPC_URL" --interval 5s --mode paper |
| #102 | clawd-agents-perps signal risk-gate SOL-PERP --notional 500 --side long |
| #103 | clawd-agents-perps onchain-mm status |
| #104 | clawd-agents-perps onchain-mm build |
| #105 | clawd-agents-perps onchain-mm plan --market <pubkey> --ticker SOL-USD |
| #106 | clawd-agents-perps onchain-mm run --market <pubkey> --yes |
| #107 | clawd-agents-perps twamm status |
| #108 | clawd-agents-perps twamm build |
| #109 | clawd-agents-perps twamm crank-plan --token-a <mint> --token-b <mint> |
| #110 | clawd-agents-perps twamm crank --token-a <mint> --token-b <mint> --yes |
| #111 | |
| #112 | Safety: |
| #113 | Defaults are observe/paper. Live previews remain blocked unless the runtime |
| #114 | is explicitly armed with LIVE_TRADING=true, OPERATOR_CONFIRMED=true, and |
| #115 | PERPS_SIM_ONLY=false. Imperial order submission also requires IMPERIAL_LIVE=true. |
| #116 | `); |
| #117 | } |
| #118 | function printSignalHelp() { |
| #119 | console.log(`clawd-agents-perps signal |
| #120 | |
| #121 | Usage: |
| #122 | clawd-agents-perps signal oi <symbol> [--rpc-url <url>] [--api-url <url>] [--lookback 5m] [--mode paper] [-o json] [--mock] |
| #123 | clawd-agents-perps signal watch <symbol> [--rpc-url <url>] [--interval 5s] [--mode paper] [--mock] |
| #124 | clawd-agents-perps signal risk-gate <symbol> --notional 500 --side long [--mode paper] [--mock] |
| #125 | |
| #126 | Safety: |
| #127 | This is an observe/risk signal. It never submits orders. Modes only affect |
| #128 | the suggested action payload and stay paper-first by default. |
| #129 | `); |
| #130 | } |
| #131 | function printTwammHelp() { |
| #132 | console.log(`clawd-agents-perps twamm |
| #133 | |
| #134 | Usage: |
| #135 | clawd-agents-perps twamm status |
| #136 | clawd-agents-perps twamm build [--skip-app-install] |
| #137 | clawd-agents-perps twamm build-plan [--skip-app-install] |
| #138 | clawd-agents-perps twamm test-plan [--cargo] |
| #139 | clawd-agents-perps twamm crank-plan [--rpc-url <url>] [--token-a <mint>] [--token-b <mint>] [--wallet <path>] [--once] |
| #140 | clawd-agents-perps twamm crank --token-a <mint> --token-b <mint> --yes |
| #141 | |
| #142 | Environment: |
| #143 | CLAWD_TWAMM_ROOT Path to Perps/twamm-master |
| #144 | CLAWD_TWAMM_RPC_URL RPC alias/url, default SOLANA_RPC_URL/local |
| #145 | CLAWD_TWAMM_TOKEN_A_MINT Default first token mint |
| #146 | CLAWD_TWAMM_TOKEN_B_MINT Default second token mint |
| #147 | CLAWD_TWAMM_LIVE=true and OPERATOR_CONFIRMED=true required for crank |
| #148 | `); |
| #149 | } |
| #150 | function printOnchainMmHelp() { |
| #151 | console.log(`clawd-agents-perps onchain-mm |
| #152 | |
| #153 | Usage: |
| #154 | clawd-agents-perps onchain-mm status |
| #155 | clawd-agents-perps onchain-mm build [--release] |
| #156 | clawd-agents-perps onchain-mm plan [--market <pubkey>] [--ticker SOL-USD] [--rpc-url local] |
| #157 | clawd-agents-perps onchain-mm run --market <pubkey> --yes |
| #158 | |
| #159 | Environment: |
| #160 | CLAWD_ONCHAIN_MM_ROOT Path to Perps/phoenix-onchain-market-maker-master |
| #161 | CLAWD_ONCHAIN_MM_MARKET Phoenix market pubkey |
| #162 | CLAWD_ONCHAIN_MM_TICKER Coinbase ticker, default SOL-USD |
| #163 | CLAWD_ONCHAIN_MM_RPC_URL RPC alias/url, default local/SOLANA_RPC_URL |
| #164 | CLAWD_ONCHAIN_MM_LIVE=true and OPERATOR_CONFIRMED=true required for run |
| #165 | `); |
| #166 | } |
| #167 | async function main() { |
| #168 | const parsed = parseArgs(process.argv.slice(2)); |
| #169 | const createRuntime = () => new ClawdPerpsRuntime(undefined, repoRoot()); |
| #170 | if (parsed.command === "signal") { |
| #171 | const subcommand = parsed.rest[0] || "oi"; |
| #172 | const symbol = parsed.rest[1] || "SOL-PERP"; |
| #173 | const signalOptions = { |
| #174 | symbol, |
| #175 | apiUrl: typeof parsed.options["api-url"] === "string" ? parsed.options["api-url"] : undefined, |
| #176 | rpcUrl: typeof parsed.options["rpc-url"] === "string" ? parsed.options["rpc-url"] : undefined, |
| #177 | mode: parseSignalMode(parsed.options.mode), |
| #178 | maxSpreadBps: asNumber(parsed.options["max-spread-bps"], 25), |
| #179 | maxFundingAbs: asNumber(parsed.options["max-funding-abs"], 0.0025), |
| #180 | minDepthUsd: asNumber(parsed.options["min-depth-usd"], 25_000), |
| #181 | mock: asBoolean(parsed.options.mock), |
| #182 | }; |
| #183 | switch (subcommand) { |
| #184 | case "help": |
| #185 | case "--help": |
| #186 | case "-h": |
| #187 | printSignalHelp(); |
| #188 | return; |
| #189 | case "oi": { |
| #190 | printJson(await buildClawdOiCoreSignal(signalOptions)); |
| #191 | return; |
| #192 | } |
| #193 | case "risk-gate": { |
| #194 | const signal = await buildClawdOiCoreSignal(signalOptions); |
| #195 | printJson(buildOiRiskGate({ |
| #196 | signal, |
| #197 | notionalUsdc: asNumber(parsed.options.notional, 500), |
| #198 | side: parsed.options.side === "short" ? "short" : "long", |
| #199 | })); |
| #200 | return; |
| #201 | } |
| #202 | case "watch": { |
| #203 | const intervalMs = parseDurationMs(parsed.options.interval, 5000); |
| #204 | const iterations = asNumber(parsed.options.iterations, Number.POSITIVE_INFINITY); |
| #205 | let previous; |
| #206 | let count = 0; |
| #207 | while (count < iterations) { |
| #208 | const signal = await buildClawdOiCoreSignal({ ...signalOptions, previous }); |
| #209 | printJson(signal); |
| #210 | previous = { |
| #211 | ts: signal.ts, |
| #212 | symbol: signal.symbol, |
| #213 | markPrice: signal.market.markPrice, |
| #214 | indexPrice: signal.market.indexPrice, |
| #215 | openInterestUsd: signal.market.openInterestUsd, |
| #216 | fundingRate: signal.market.fundingRate, |
| #217 | depthUsd: signal.market.depthUsd, |
| #218 | longOiUsd: signal.market.longOiUsd, |
| #219 | shortOiUsd: signal.market.shortOiUsd, |
| #220 | }; |
| #221 | count++; |
| #222 | if (count >= iterations) |
| #223 | return; |
| #224 | await new Promise((resolve) => setTimeout(resolve, intervalMs)); |
| #225 | } |
| #226 | return; |
| #227 | } |
| #228 | default: |
| #229 | console.error(`Unknown signal command: ${subcommand}`); |
| #230 | printSignalHelp(); |
| #231 | process.exitCode = 1; |
| #232 | return; |
| #233 | } |
| #234 | } |
| #235 | if (parsed.command === "onchain-mm") { |
| #236 | const subcommand = parsed.rest[0] || "status"; |
| #237 | const runOptions = { |
| #238 | market: typeof parsed.options.market === "string" ? parsed.options.market : undefined, |
| #239 | ticker: typeof parsed.options.ticker === "string" ? parsed.options.ticker : undefined, |
| #240 | rpcUrl: typeof parsed.options["rpc-url"] === "string" ? parsed.options["rpc-url"] : undefined, |
| #241 | keypairPath: typeof parsed.options["keypair-path"] === "string" ? parsed.options["keypair-path"] : undefined, |
| #242 | quoteEdgeBps: asNumber(parsed.options["quote-edge-bps"], 3), |
| #243 | quoteSize: asNumber(parsed.options["quote-size"], 100_000_000), |
| #244 | refreshMs: asNumber(parsed.options["refresh-ms"], 2000), |
| #245 | priceImprovement: typeof parsed.options["price-improvement"] === "string" ? parsed.options["price-improvement"] : undefined, |
| #246 | postOnly: parsed.options["post-only"] !== false, |
| #247 | release: Boolean(parsed.options.release), |
| #248 | yes: Boolean(parsed.options.yes), |
| #249 | }; |
| #250 | switch (subcommand) { |
| #251 | case "help": |
| #252 | case "--help": |
| #253 | case "-h": |
| #254 | printOnchainMmHelp(); |
| #255 | return; |
| #256 | case "status": |
| #257 | printJson(getOnchainMarketMakerStatus()); |
| #258 | return; |
| #259 | case "build": |
| #260 | case "install": |
| #261 | printJson(buildOnchainMarketMaker({ release: Boolean(parsed.options.release) })); |
| #262 | return; |
| #263 | case "plan": |
| #264 | printJson(buildOnchainMarketMakerPlan(runOptions)); |
| #265 | return; |
| #266 | case "run": |
| #267 | runOnchainMarketMaker(runOptions); |
| #268 | return; |
| #269 | default: |
| #270 | console.error(`Unknown onchain-mm command: ${subcommand}`); |
| #271 | printOnchainMmHelp(); |
| #272 | process.exitCode = 1; |
| #273 | return; |
| #274 | } |
| #275 | } |
| #276 | if (parsed.command === "twamm") { |
| #277 | const subcommand = parsed.rest[0] || "status"; |
| #278 | const crankOptions = { |
| #279 | rpcUrl: typeof parsed.options["rpc-url"] === "string" ? parsed.options["rpc-url"] : undefined, |
| #280 | tokenAMint: typeof parsed.options["token-a"] === "string" ? parsed.options["token-a"] : undefined, |
| #281 | tokenBMint: typeof parsed.options["token-b"] === "string" ? parsed.options["token-b"] : undefined, |
| #282 | walletPath: typeof parsed.options.wallet === "string" ? parsed.options.wallet : undefined, |
| #283 | once: Boolean(parsed.options.once), |
| #284 | yes: Boolean(parsed.options.yes), |
| #285 | }; |
| #286 | switch (subcommand) { |
| #287 | case "help": |
| #288 | case "--help": |
| #289 | case "-h": |
| #290 | printTwammHelp(); |
| #291 | return; |
| #292 | case "status": |
| #293 | printJson(getTwammAutomationStatus()); |
| #294 | return; |
| #295 | case "build-plan": |
| #296 | printJson(buildTwammBuildPlan({ skipAppInstall: Boolean(parsed.options["skip-app-install"]) })); |
| #297 | return; |
| #298 | case "build": |
| #299 | case "install": |
| #300 | printJson(buildTwammAutomation({ skipAppInstall: Boolean(parsed.options["skip-app-install"]) })); |
| #301 | return; |
| #302 | case "test-plan": |
| #303 | printJson(buildTwammTestPlan({ anchor: !parsed.options.cargo, cargo: Boolean(parsed.options.cargo) })); |
| #304 | return; |
| #305 | case "crank-plan": |
| #306 | case "plan": |
| #307 | printJson(buildTwammCrankPlan(crankOptions)); |
| #308 | return; |
| #309 | case "crank": |
| #310 | case "run": |
| #311 | runTwammCrank(crankOptions); |
| #312 | return; |
| #313 | default: |
| #314 | console.error(`Unknown twamm command: ${subcommand}`); |
| #315 | printTwammHelp(); |
| #316 | process.exitCode = 1; |
| #317 | return; |
| #318 | } |
| #319 | } |
| #320 | switch (parsed.command) { |
| #321 | case "help": |
| #322 | case "--help": |
| #323 | case "-h": |
| #324 | printHelp(); |
| #325 | return; |
| #326 | case "status": |
| #327 | case "health": { |
| #328 | const runtime = createRuntime(); |
| #329 | printJson(await runtime.getRuntimeHealth()); |
| #330 | return; |
| #331 | } |
| #332 | case "frontend": { |
| #333 | const runtime = createRuntime(); |
| #334 | printJson(await buildPerpsFrontendStatus(runtime)); |
| #335 | return; |
| #336 | } |
| #337 | case "telegram": { |
| #338 | const runtime = createRuntime(); |
| #339 | printJson(await handleTelegramPerpsCommand(runtime, parsed.rest.join(" ") || "/perps")); |
| #340 | return; |
| #341 | } |
| #342 | case "vulcan": { |
| #343 | const runtime = createRuntime(); |
| #344 | printJson(await runtime.getVulcanCatalogSummary()); |
| #345 | return; |
| #346 | } |
| #347 | case "paper-long": { |
| #348 | const runtime = createRuntime(); |
| #349 | printJson(runtime.previewPaperTrade(parsed.rest[0] || "SOL", "buy", asNumber(parsed.options.notional, 100))); |
| #350 | return; |
| #351 | } |
| #352 | case "paper-short": { |
| #353 | const runtime = createRuntime(); |
| #354 | printJson(runtime.previewPaperTrade(parsed.rest[0] || "SOL", "sell", asNumber(parsed.options.notional, 100))); |
| #355 | return; |
| #356 | } |
| #357 | case "live-long": { |
| #358 | const runtime = createRuntime(); |
| #359 | printJson(runtime.previewLiveTrade(parsed.rest[0] || "SOL", "buy", asNumber(parsed.options.notional, 100), asNumber(parsed.options.leverage, 1))); |
| #360 | return; |
| #361 | } |
| #362 | case "live-short": { |
| #363 | const runtime = createRuntime(); |
| #364 | printJson(runtime.previewLiveTrade(parsed.rest[0] || "SOL", "sell", asNumber(parsed.options.notional, 100), asNumber(parsed.options.leverage, 1))); |
| #365 | return; |
| #366 | } |
| #367 | case "imperial-health": { |
| #368 | const client = new ImperialClient(); |
| #369 | printJson(await client.healthCheck()); |
| #370 | return; |
| #371 | } |
| #372 | case "imperial-scan": { |
| #373 | const symbols = parseSymbols(parsed.options.symbols); |
| #374 | const client = new ImperialClient(symbols ? { allowedSymbols: symbols } : undefined); |
| #375 | printJson(await client.runScan({ |
| #376 | sizeUsd: asNumber(parsed.options.size, 100), |
| #377 | autoRoute: Boolean(parsed.options["auto-route"]), |
| #378 | })); |
| #379 | return; |
| #380 | } |
| #381 | case "imperial-cycle": { |
| #382 | const client = new ImperialClient(); |
| #383 | printJson(await client.runCycle(parsed.rest[0] || "SOL", { |
| #384 | sizeUsd: asNumber(parsed.options.size, 100), |
| #385 | autoRoute: Boolean(parsed.options["auto-route"]), |
| #386 | })); |
| #387 | return; |
| #388 | } |
| #389 | default: |
| #390 | console.error(`Unknown command: ${parsed.command}`); |
| #391 | printHelp(); |
| #392 | process.exitCode = 1; |
| #393 | } |
| #394 | } |
| #395 | main().catch((error) => { |
| #396 | console.error(error instanceof Error ? error.message : String(error)); |
| #397 | process.exit(1); |
| #398 | }); |
| #399 | //# sourceMappingURL=cli.js.map |