- Add stocks table (server/db) with list/get/add/update/delete actions, scoped per user, backing both the chat agent and the /stocks UI via useActionQuery/useActionMutation - Add mobile-first /stocks page (shadcn table/cards, watchlist, search) and wire it into the sidebar, navigation state, and view-screen context - Install the platform's Claude Agent SDK harness (server/plugins, server/routes/api/claude-agent) so the app agent runs on the platform's own Claude subscription instead of the Builder.io gateway; fix the vendored turn.post.ts route to resolve the session and wrap it in runWithRequestContext, since custom /api/ routes aren't auto-wrapped in request context the way /_agent-native/actions/* are - Add shadcn table/badge/dialog/select components (Tabler icons, not lucide)
78 lines
2.0 KiB
TypeScript
78 lines
2.0 KiB
TypeScript
import {
|
|
ensureAdditiveColumns,
|
|
getDbExec,
|
|
runMigrations,
|
|
} from "@agent-native/core/db";
|
|
|
|
import * as schema from "../db/schema.js";
|
|
|
|
const runStocksMigrations = runMigrations(
|
|
[
|
|
{
|
|
version: 1,
|
|
name: "stocks-table",
|
|
sql: `CREATE TABLE IF NOT EXISTS stocks (
|
|
id TEXT PRIMARY KEY,
|
|
symbol TEXT NOT NULL,
|
|
company_name TEXT NOT NULL,
|
|
exchange TEXT NOT NULL DEFAULT 'NASDAQ',
|
|
sector TEXT,
|
|
currency TEXT NOT NULL DEFAULT 'USD',
|
|
price REAL NOT NULL DEFAULT 0,
|
|
change_percent REAL NOT NULL DEFAULT 0,
|
|
market_cap INTEGER,
|
|
watchlisted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
owner_email TEXT NOT NULL,
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
)`,
|
|
},
|
|
{
|
|
version: 2,
|
|
name: "stocks-owner-symbol-unique-index",
|
|
sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_stocks_owner_symbol
|
|
ON stocks (owner_email, symbol)`,
|
|
},
|
|
{
|
|
version: 3,
|
|
name: "stocks-owner-watchlisted-index",
|
|
sql: `CREATE INDEX IF NOT EXISTS idx_stocks_owner_watchlisted
|
|
ON stocks (owner_email, watchlisted)`,
|
|
},
|
|
],
|
|
{ table: "stocks_migrations" },
|
|
);
|
|
|
|
function isDrizzleTable(value: unknown): value is object {
|
|
return (
|
|
!!value &&
|
|
typeof value === "object" &&
|
|
Object.getOwnPropertySymbols(value).some((s) =>
|
|
s.toString().includes("drizzle"),
|
|
)
|
|
);
|
|
}
|
|
|
|
const schemaTables = Object.values(schema).filter(isDrizzleTable);
|
|
|
|
export default async (nitroApp: any): Promise<void> => {
|
|
await runStocksMigrations(nitroApp);
|
|
try {
|
|
const summary = await ensureAdditiveColumns({
|
|
db: getDbExec(),
|
|
tables: schemaTables,
|
|
});
|
|
if (summary.errors.length > 0) {
|
|
console.warn(
|
|
"[db] ensureAdditiveColumns completed with errors:",
|
|
summary.errors,
|
|
);
|
|
}
|
|
} catch (err) {
|
|
console.warn(
|
|
"[db] ensureAdditiveColumns failed (non-fatal):",
|
|
err instanceof Error ? err.message : err,
|
|
);
|
|
}
|
|
};
|