import { defineAction } from "@agent-native/core/action"; import { and, desc, eq, or, sql } from "@agent-native/core/db/schema"; import { z } from "zod"; import { getDb } from "../server/db/index.js"; import { stocks } from "../server/db/schema.js"; function booleanParam(value: string | undefined): boolean | undefined { if (value === undefined) return undefined; return value === "true" || value === "1"; } export default defineAction({ description: "List stock listings, optionally filtered by search text (symbol or company name), exchange, or watchlisted status.", schema: z.object({ search: z .string() .optional() .describe("Filter by symbol or company name (case-insensitive substring match)"), exchange: z.string().optional().describe("Filter by exchange (e.g. NASDAQ, NYSE)"), watchlistedOnly: z .string() .optional() .describe("Set to \"true\" to only return watchlisted stocks"), }), http: { method: "GET" }, readOnly: true, run: async (args, ctx) => { if (!ctx?.userEmail) throw new Error("Authentication required."); const db = getDb(); const conditions = [eq(stocks.ownerEmail, ctx.userEmail)]; if (args.search) { const term = `%${args.search.toLowerCase()}%`; conditions.push( or( sql`lower(${stocks.symbol}) like ${term}`, sql`lower(${stocks.companyName}) like ${term}`, )!, ); } if (args.exchange) conditions.push(eq(stocks.exchange, args.exchange)); if (booleanParam(args.watchlistedOnly)) { conditions.push(eq(stocks.watchlisted, true)); } const rows = await db .select() .from(stocks) .where(and(...conditions)) .orderBy(desc(stocks.watchlisted), stocks.symbol); return { stocks: rows }; }, });