From c89a1849a74ae858300bc1a157251562f6a99e6b Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 22 Jul 2025 12:10:19 -0700 Subject: [PATCH] Improve log fetching and Discord command handling --- packages/spike/src/ai.ts | 1 - packages/spike/src/discord/commands.ts | 52 ++++++++++++++------------ packages/spike/src/discord/events.ts | 5 +++ packages/spike/src/render.ts | 15 +++++--- packages/whiteboard/src/tools.ts | 1 - 5 files changed, 43 insertions(+), 31 deletions(-) diff --git a/packages/spike/src/ai.ts b/packages/spike/src/ai.ts index 7e2dc5f..df4eefb 100644 --- a/packages/spike/src/ai.ts +++ b/packages/spike/src/ai.ts @@ -67,7 +67,6 @@ const respond = async ( console.error(`💥 ${content}`, error) history.push(system(content)) - console.log(`🌭 sending`, content) return content } } diff --git a/packages/spike/src/discord/commands.ts b/packages/spike/src/discord/commands.ts index 30ce4fb..a5d6324 100644 --- a/packages/spike/src/discord/commands.ts +++ b/packages/spike/src/discord/commands.ts @@ -12,7 +12,6 @@ import { getLogs } from "../render" export const runCommand = async (interaction: Interaction) => { if (!interaction.isChatInputCommand()) return - console.log(`🌭 HI RUNNING`) const command = commands.find((cmd) => cmd.command.name === interaction.commandName) ensure(command, `Command ${interaction.commandName} not found`) @@ -33,7 +32,6 @@ export const runCommand = async (interaction: Interaction) => { export const registerCommands = async (client: Client) => { const commandList = commands.map((cmd) => cmd.command) - console.log(`🌭 Registering commands: ${commandList.map((cmd) => cmd.name).join(", ")}`) await client.application!.commands.set(commandList) } @@ -53,34 +51,42 @@ const commands: { command: SlashCommandOptionsOnlyBuilder; execute: ExecuteComma { name: "Build", value: "build" }, { name: "Deploy", value: "deploy" } ) + ) + .addNumberOption((option) => + option + .setName("limit") + .setDescription("The number of logs to fetch (default: 20)") + .setMinValue(3) + .setMaxValue(100) + .setRequired(false) ), - // .addStringOption((option) => - // option - // .setName("type") - // .setRequired(true) - // .addChoices( - // { name: "App", value: "app" }, - // { name: "Build", value: "build" }, - // { name: "Deploy", value: "deploy" } - // ) - // ), - // .addNumberOption((option) => - // option - // .setName("limit") - // .setDescription("Number of logs to fetch") - // .setMinValue(1) - // .setMaxValue(100) - // .setRequired(false) - // ), async execute(interaction) { - console.log(`🌭 in`) await interaction.deferReply() - console.log(`🌭 out`) const type = interaction.options.getString("type", true) const limit = interaction.options.getNumber("limit") ?? undefined + await interaction.editReply(`Fetching ${type} logs...`) const logs = await getLogs(type as any, limit) - await interaction.editReply(JSON.stringify(logs, null, 2)) + + if (logs.length === 0) { + await interaction.editReply("No logs found.") + return + } else { + let content = "" + for (const log of [ + ...logs, + "\nSee all logs at https://dashboard.render.com/web/srv-d1vrdqmuk2gs73eop8o0/logs", + ]) { + // Account for the opening/closing ``` in length calculation + if (content.length + log.length >= 1990) { + await interaction.followUp({ content: content, flags: ["SuppressEmbeds"] }) + content = log + } else { + content += log + "\n" + } + } + await interaction.followUp({ content: content, flags: ["SuppressEmbeds"] }) + } }, }, ] diff --git a/packages/spike/src/discord/events.ts b/packages/spike/src/discord/events.ts index 52322b0..99746fb 100644 --- a/packages/spike/src/discord/events.ts +++ b/packages/spike/src/discord/events.ts @@ -1,8 +1,13 @@ import { respondToUserMessage } from "../ai" import { storeEvaluation } from "../eval" import { ActivityType, type Client } from "discord.js" +import { runCommand } from "./commands" export const listenForEvents = (client: Client) => { + client.on("interactionCreate", async (interaction) => { + return runCommand(interaction) + }) + client.on("messageCreate", async (msg) => { if (msg.author.bot) return diff --git a/packages/spike/src/render.ts b/packages/spike/src/render.ts index 1aa7864..3afcfb3 100644 --- a/packages/spike/src/render.ts +++ b/packages/spike/src/render.ts @@ -1,8 +1,9 @@ import { ensure } from "@workshop/shared/utils" +import { bold, time } from "discord.js" -export const getLogs = async (type: "app" | "request" | "build" = "app", limit = 100) => { - const ownerId = "tea-d1vamb95pdvs73d1sgtg" - const resourceId = "srv-d1vrdqmuk2gs73eop8o0" +export const getLogs = async (type: "app" | "request" | "build" = "app", limit = 20) => { + const ownerId = "tea-d1vamb95pdvs73d1sgtg" // owner ID for the Render project (from the render api) + const resourceId = "srv-d1vrdqmuk2gs73eop8o0" // resource ID for the Render service (from the render api) const url = new URL("https://api.render.com/v1/logs") url.searchParams.set("type", type) @@ -11,7 +12,6 @@ export const getLogs = async (type: "app" | "request" | "build" = "app", limit = url.searchParams.set("resource", resourceId) url.searchParams.set("limit", String(limit)) - console.log(`🌭 Fetching logs from Render: ${url.toString()}`) const response = await fetch(url.toString(), { method: "GET", headers: { @@ -26,8 +26,11 @@ export const getLogs = async (type: "app" | "request" | "build" = "app", limit = const data = (await response.json()) as any ensure(data.logs, "Expected logs to be an array") const logs = data.logs.map((log: any) => { - const { id, labels, ...rest } = log - return rest + const { timestamp, message } = log + + const unixTimestamp = Math.floor(new Date(timestamp).getTime() / 1000) + const cleanMessage = message.replace(/\x1b\[[0-9;]*m/g, "") + return `${time(unixTimestamp, "t")}: ${cleanMessage}` }) return logs diff --git a/packages/whiteboard/src/tools.ts b/packages/whiteboard/src/tools.ts index 47e1ced..e1bb67c 100644 --- a/packages/whiteboard/src/tools.ts +++ b/packages/whiteboard/src/tools.ts @@ -21,7 +21,6 @@ export const tools = [ const response = await searchImages(pixabayApiKey, input.imageQuery, { per_page: 10 }) const hit = response.hits[0]! - console.log(`🌭`, `Find the 2d bounding box for this "${input.whereToOverlay}"`) const image = await Bun.file("public/whiteboard.png").arrayBuffer() const boundingBox = await getGeminiResponse( image,