import { randomUUID } from "node:crypto"; import { defineAction } from "@agent-native/core/action"; import { and, eq } from "@agent-native/core/db/schema"; import { z } from "zod"; import { getDb } from "../server/db/index.js"; import { stocks } from "../server/db/schema.js"; export default defineAction({ description: "Add a new stock listing. Fails if the symbol is already listed for this user.", schema: z.object({ symbol: z.string().min(1).max(10).describe("Ticker symbol, e.g. AAPL"), companyName: z.string().min(1).describe("Company name"), exchange: z .string() .default("NASDAQ") .describe("Exchange, e.g. NASDAQ, NYSE"), sector: z.string().optional().describe("Sector, e.g. Technology"), currency: z.string().default("USD").describe("Currency code, e.g. USD"), price: z.coerce.number().default(0).describe("Current price"), changePercent: z.coerce .number() .default(0) .describe("Price change percent, e.g. 1.5 or -2.3"), marketCap: z.coerce .number() .optional() .describe("Market capitalization"), }), run: async (args, ctx) => { if (!ctx?.userEmail) throw new Error("Authentication required."); const db = getDb(); const symbol = args.symbol.toUpperCase(); const existing = await db .select({ id: stocks.id }) .from(stocks) .where(and(eq(stocks.ownerEmail, ctx.userEmail), eq(stocks.symbol, symbol))); if (existing[0]) { throw new Error(`${symbol} is already listed.`); } const now = new Date().toISOString(); const row = { id: randomUUID(), symbol, companyName: args.companyName, exchange: args.exchange, sector: args.sector ?? null, currency: args.currency, price: args.price, changePercent: args.changePercent, marketCap: args.marketCap ?? null, watchlisted: false, ownerEmail: ctx.userEmail, createdAt: now, updatedAt: now, }; await db.insert(stocks).values(row); return row; }, });