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 { createInterface } from "node:readline"; |
| #3 | import chalk from "chalk"; |
| #4 | const CLEAR = "\x1b[2J\x1b[H"; |
| #5 | const HIDE_CURSOR = "\x1b[?25l"; |
| #6 | const SHOW_CURSOR = "\x1b[?25h"; |
| #7 | const SPARK = ["_", ".", "-", "~", "^", "*", "#", "@"]; |
| #8 | const display = { |
| #9 | goblin: false, |
| #10 | lastTick: 0, |
| #11 | totalTicks: 0, |
| #12 | price: 0, |
| #13 | priceHistory: [], |
| #14 | lastDecision: null, |
| #15 | lastOutcome: "", |
| #16 | totalPnl: 0, |
| #17 | cash: 0, |
| #18 | openPositions: 0, |
| #19 | consecutiveLosses: 0, |
| #20 | log: [], |
| #21 | done: false, |
| #22 | killswitch: false, |
| #23 | }; |
| #24 | function visibleLength(value) { |
| #25 | return value.replace(/\x1b\[[0-9;]*m/g, "").length; |
| #26 | } |
| #27 | function sparkline(prices, width = 30) { |
| #28 | if (prices.length < 2) |
| #29 | return chalk.gray(".".repeat(width)); |
| #30 | const slice = prices.slice(-width); |
| #31 | const min = Math.min(...slice); |
| #32 | const max = Math.max(...slice); |
| #33 | const range = max - min || 1; |
| #34 | return slice |
| #35 | .map((price, index) => { |
| #36 | const idx = Math.round(((price - min) / range) * (SPARK.length - 1)); |
| #37 | const bar = SPARK[idx] ?? "-"; |
| #38 | const prev = slice[Math.max(0, index - 1)] ?? price; |
| #39 | return price >= prev ? chalk.green(bar) : chalk.red(bar); |
| #40 | }) |
| #41 | .join(""); |
| #42 | } |
| #43 | function pnlColor(n) { |
| #44 | const s = `${n >= 0 ? "+" : ""}${n.toLocaleString()} lamports`; |
| #45 | return n >= 0 ? chalk.green(s) : chalk.red(s); |
| #46 | } |
| #47 | function row(content, width) { |
| #48 | return chalk.magenta("|") + content + " ".repeat(Math.max(0, width - visibleLength(content))) + chalk.magenta("|"); |
| #49 | } |
| #50 | function render() { |
| #51 | const width = Math.max(80, process.stdout.columns ?? 100) - 2; |
| #52 | const border = chalk.magenta("=".repeat(width)); |
| #53 | const lines = []; |
| #54 | const title = display.goblin ? " GOBLIN MODE - OODA Paper Loop / devnet / no keys " : " DARK RALPH - OODA Paper Loop / devnet / no keys "; |
| #55 | const pct = display.totalTicks > 0 ? display.lastTick / display.totalTicks : 0; |
| #56 | const barW = Math.max(12, width - 28); |
| #57 | const filled = Math.round(pct * barW); |
| #58 | const progress = `${chalk.cyan("#".repeat(filled))}${chalk.gray("-".repeat(barW - filled))}`; |
| #59 | const dec = display.lastDecision; |
| #60 | const actionColor = dec?.action === "open" ? chalk.green : dec?.action === "close" ? chalk.red : chalk.gray; |
| #61 | lines.push(chalk.magenta("+") + border + chalk.magenta("+")); |
| #62 | lines.push(row(chalk.bold.magenta(title), width)); |
| #63 | lines.push(chalk.magenta("+") + border + chalk.magenta("+")); |
| #64 | lines.push(row(` Tick ${display.lastTick}/${display.totalTicks} [${progress}] ${Math.round(pct * 100)}%`, width)); |
| #65 | lines.push(row(` Price ${(display.price / 1000).toFixed(3)} ${sparkline(display.priceHistory, Math.min(44, width - 24))}`, width)); |
| #66 | lines.push(row(` Decision ${actionColor((dec?.action ?? "--").toUpperCase())} ${chalk.white((dec?.reason ?? "waiting").slice(0, 82))} ${chalk.cyan(display.lastOutcome)}`, width)); |
| #67 | lines.push(chalk.magenta("+") + border + chalk.magenta("+")); |
| #68 | lines.push(row(` PnL ${pnlColor(display.totalPnl)} | Cash ${chalk.cyan(display.cash.toLocaleString())} lam | Positions ${chalk.yellow(display.openPositions)} | Losses ${display.consecutiveLosses > 0 ? chalk.red(display.consecutiveLosses) : chalk.gray("0")}`, width)); |
| #69 | lines.push(chalk.magenta("+") + border + chalk.magenta("+")); |
| #70 | for (const entry of display.log.slice(-7)) |
| #71 | lines.push(row(entry, width)); |
| #72 | if (display.done) { |
| #73 | lines.push(chalk.magenta("+") + border + chalk.magenta("+")); |
| #74 | lines.push(row(display.killswitch ? chalk.red.bold(" KILLSWITCH TRIGGERED") : chalk.green.bold(" Loop complete - journal written"), width)); |
| #75 | } |
| #76 | lines.push(chalk.magenta("+") + border + chalk.magenta("+")); |
| #77 | process.stdout.write(`${CLEAR}${lines.join("\n")}\n`); |
| #78 | } |
| #79 | process.stdout.write(HIDE_CURSOR); |
| #80 | process.on("exit", () => process.stdout.write(SHOW_CURSOR)); |
| #81 | process.on("SIGINT", () => { |
| #82 | process.stdout.write(SHOW_CURSOR); |
| #83 | process.exit(0); |
| #84 | }); |
| #85 | const rl = createInterface({ input: process.stdin }); |
| #86 | rl.on("line", (line) => { |
| #87 | if (!line.trim()) |
| #88 | return; |
| #89 | try { |
| #90 | const ev = JSON.parse(line); |
| #91 | if (ev.event === "start") { |
| #92 | display.totalTicks = ev.ticks ?? 50; |
| #93 | display.goblin = Boolean(ev.goblin); |
| #94 | } |
| #95 | else if (ev.event === "tick") { |
| #96 | display.lastTick = ev.tick ?? display.lastTick; |
| #97 | display.price = ev.price ?? display.price; |
| #98 | display.priceHistory.push(display.price); |
| #99 | if (display.priceHistory.length > 60) |
| #100 | display.priceHistory.shift(); |
| #101 | display.lastDecision = ev.decision ?? display.lastDecision; |
| #102 | display.lastOutcome = ev.outcome ?? ""; |
| #103 | display.totalPnl = ev.total_pnl_lamports ?? display.totalPnl; |
| #104 | display.cash = ev.cash_lamports ?? display.cash; |
| #105 | display.openPositions = ev.positions ?? display.openPositions; |
| #106 | display.consecutiveLosses = ev.consecutive_losses ?? display.consecutiveLosses; |
| #107 | display.goblin = Boolean(ev.goblin); |
| #108 | const action = ev.decision?.action ?? "hold"; |
| #109 | const color = action === "open" ? chalk.green : action === "close" ? chalk.red : chalk.gray; |
| #110 | display.log.push(` ${chalk.gray(new Date(ev.now ?? "").toTimeString().slice(0, 8))} [${chalk.yellow(`T${ev.tick}`)}] ${color(action.toUpperCase().padEnd(5))} ${chalk.white((ev.decision?.reason ?? "").slice(0, 70))}`); |
| #111 | if (ev.molt_note) |
| #112 | display.log.push(` ${chalk.magenta("MOLT")} ${ev.molt_note}`); |
| #113 | } |
| #114 | else if (ev.event === "killswitch") { |
| #115 | display.killswitch = true; |
| #116 | display.done = true; |
| #117 | } |
| #118 | else if (ev.event === "done") { |
| #119 | display.done = true; |
| #120 | } |
| #121 | render(); |
| #122 | } |
| #123 | catch { |
| #124 | // Keep the TUI resilient to non-JSON stderr when piped incorrectly. |
| #125 | } |
| #126 | }); |
| #127 | rl.on("close", () => { |
| #128 | display.done = true; |
| #129 | render(); |
| #130 | process.stdout.write(SHOW_CURSOR); |
| #131 | }); |
| #132 | //# sourceMappingURL=tui.js.map |