Add search and RAG workspace flows

This commit is contained in:
2026-03-07 20:34:00 -05:00
parent db01f207a5
commit e20aba998b
35 changed files with 3417 additions and 372 deletions

View File

@@ -0,0 +1,46 @@
import { indexSearchDocuments } from '@/lib/server/search';
function getArg(name: string) {
const prefix = `--${name}=`;
const entry = process.argv.find((value) => value.startsWith(prefix));
return entry ? entry.slice(prefix.length).trim() : null;
}
async function main() {
const source = (getArg('source') ?? 'all').toLowerCase();
const ticker = getArg('ticker')?.toUpperCase() ?? null;
const accessionNumber = getArg('accession') ?? null;
const userId = getArg('user') ?? 'system-backfill';
const sourceKinds: Array<'filing_document' | 'filing_brief' | 'research_note'> | null = source === 'all'
? ['filing_document', 'filing_brief', 'research_note'] as const
: source === 'documents'
? ['filing_document'] as const
: source === 'filings'
? ['filing_brief'] as const
: source === 'research'
? ['research_note'] as const
: null;
if (!sourceKinds) {
throw new Error('Unsupported --source value. Use all, documents, filings, or research.');
}
if (sourceKinds.includes('research_note') && !userId) {
throw new Error('--user is required when backfilling research notes.');
}
const result = await indexSearchDocuments({
userId,
ticker,
accessionNumber,
sourceKinds
});
console.log(JSON.stringify(result, null, 2));
}
main().catch((error) => {
console.error(error instanceof Error ? error.message : error);
process.exitCode = 1;
});

View File

@@ -9,7 +9,7 @@ describe('buildLocalDevConfig', () => {
expect(config.port).toBe('3000');
expect(config.publicOrigin).toBe('http://localhost:3000');
expect(config.env.BETTER_AUTH_BASE_URL).toBe('http://localhost:3000');
expect(config.env.BETTER_AUTH_TRUSTED_ORIGINS).toBe('http://localhost:3000');
expect(config.env.BETTER_AUTH_TRUSTED_ORIGINS).toBe('http://localhost:3000,http://127.0.0.1:3000');
expect(config.env.BETTER_AUTH_SECRET).toBe(LOCAL_DEV_SECRET);
expect(config.env.DATABASE_URL).toBe('file:data/fiscal.sqlite');
expect(config.env.NEXT_PUBLIC_API_URL).toBe('');
@@ -27,12 +27,23 @@ describe('buildLocalDevConfig', () => {
});
expect(config.env.BETTER_AUTH_SECRET).toBe('real-secret');
expect(config.env.BETTER_AUTH_TRUSTED_ORIGINS).toBe('http://localhost:3000,https://fiscal.b11studio.xyz');
expect(config.env.BETTER_AUTH_TRUSTED_ORIGINS)
.toBe('http://localhost:3000,http://127.0.0.1:3000,https://fiscal.b11studio.xyz');
expect(config.env.DATABASE_URL).toBe('file:data/dev.sqlite');
expect(config.overrides.databaseChanged).toBe(false);
expect(config.overrides.workflowChanged).toBe(false);
});
it('trusts both localhost and 127.0.0.1 for loopback public origins', () => {
const config = buildLocalDevConfig({
DEV_PUBLIC_HOST: '127.0.0.1',
PORT: '3412'
});
expect(config.publicOrigin).toBe('http://127.0.0.1:3412');
expect(config.env.BETTER_AUTH_TRUSTED_ORIGINS).toBe('http://127.0.0.1:3412,http://localhost:3412');
});
it('respects an explicit public origin override', () => {
const config = buildLocalDevConfig({
DEV_PUBLIC_ORIGIN: 'https://local.fiscal.test:4444/',

View File

@@ -43,6 +43,34 @@ function toUniqueList(values: string[]) {
return Array.from(new Set(values));
}
function isLoopbackHostname(hostname: string) {
return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1';
}
function replaceOriginHostname(origin: string, hostname: string) {
const url = new URL(origin);
url.hostname = hostname;
url.hash = '';
url.search = '';
const pathName = url.pathname.replace(/\/$/, '');
return `${url.origin}${pathName === '/' ? '' : pathName}`;
}
function buildTrustedOrigins(publicOrigin: string, configuredOrigins: string | undefined) {
const trustedOrigins = [publicOrigin];
const publicOriginUrl = new URL(publicOrigin);
if (isLoopbackHostname(publicOriginUrl.hostname)) {
trustedOrigins.push(replaceOriginHostname(publicOrigin, 'localhost'));
trustedOrigins.push(replaceOriginHostname(publicOrigin, '127.0.0.1'));
}
trustedOrigins.push(...parseCsvList(configuredOrigins));
return toUniqueList(trustedOrigins).join(',');
}
function coercePort(port: string | undefined) {
const parsed = Number.parseInt(port ?? '', 10);
if (Number.isInteger(parsed) && parsed > 0 && parsed <= 65535) {
@@ -113,10 +141,7 @@ export function buildLocalDevConfig(sourceEnv: EnvMap = process.env): LocalDevCo
? DEFAULT_DATABASE_URL
: trim(sourceEnv.DATABASE_URL) ?? DEFAULT_DATABASE_URL;
const secret = trim(sourceEnv.BETTER_AUTH_SECRET);
const trustedOrigins = toUniqueList([
publicOrigin,
...parseCsvList(sourceEnv.BETTER_AUTH_TRUSTED_ORIGINS)
]).join(',');
const trustedOrigins = buildTrustedOrigins(publicOrigin, sourceEnv.BETTER_AUTH_TRUSTED_ORIGINS);
const env: EnvMap = {
...sourceEnv,