feat(agent): add read-only PocketBase loaders

The only module in the agent layer that touches the network. Kept
separate from formatting so tool output is unit-testable offline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
DEls 2026-08-29 20:25:39 +02:00
parent 15ecf1af05
commit 7d02ef1e3a

92
src/lib/agent/load.ts Normal file
View file

@ -0,0 +1,92 @@
/**
* PocketBase reads for the agent tool layer.
*
* The ONLY module here that touches the network. Every function is a
* read — no create/update/delete, enforced by read-only.test.ts.
*
* Deliberately returns raw-ish records; shaping lives in ./format so it
* can be unit-tested without a PocketBase instance.
*/
import { pbAdmin } from "@/lib/pb";
import type { SiteRef } from "./resolve";
export async function loadSiteRefs(): Promise<SiteRef[]> {
const pb = await pbAdmin();
const sites = await pb.collection("sites").getFullList({
fields: "id,name,project_code",
sort: "name",
});
return sites.map((s) => ({
id: s.id,
name: s.name ?? "",
projectCode: s.project_code ?? "",
}));
}
/** Everything the site financial roll-up needs, for one site. */
export async function loadSiteBundle(siteId: string) {
const pb = await pbAdmin();
const [site, fixedCosts, timesheets, employees, purchaseOrders, fuelLogs, income] =
await Promise.all([
pb.collection("sites").getOne(siteId),
pb.collection("fixed_costs").getFullList({ filter: `site='${siteId}'` }),
pb.collection("timesheets").getFullList(),
pb.collection("employees").getFullList({
fields: "id,rate_per_hour,site,name",
}),
pb.collection("purchase_orders").getFullList({ filter: `site='${siteId}'` }),
pb.collection("fuel_logs").getFullList({ filter: `site='${siteId}'` }),
pb.collection("income_claims").getFullList({ filter: `site='${siteId}'` }),
]);
return { site, fixedCosts, timesheets, employees, purchaseOrders, fuelLogs, income };
}
/** Every site plus the collections needed to roll all of them up at once. */
export async function loadAllSitesBundle() {
const pb = await pbAdmin();
const [sites, fixedCosts, timesheets, employees, purchaseOrders, fuelLogs, income] =
await Promise.all([
pb.collection("sites").getFullList({ sort: "name" }),
pb.collection("fixed_costs").getFullList(),
pb.collection("timesheets").getFullList(),
pb.collection("employees").getFullList({
fields: "id,rate_per_hour,site,name",
}),
pb.collection("purchase_orders").getFullList({
fields: "id,site,total_value,is_verified",
}),
pb.collection("fuel_logs").getFullList({
fields: "id,site,litres,date_logged",
}),
pb.collection("income_claims").getFullList({
fields: "id,site,paid_on,period_end",
}),
]);
return { sites, fixedCosts, timesheets, employees, purchaseOrders, fuelLogs, income };
}
/** Vehicles + fuel logs with vehicle expanded, for the attention roll-up. */
export async function loadFleetBundle() {
const pb = await pbAdmin();
const [vehicles, fuelLogs] = await Promise.all([
pb.collection("vehicles").getFullList({ sort: "description" }),
pb.collection("fuel_logs").getFullList({ expand: "vehicle" }),
]);
return { vehicles, fuelLogs };
}
/** Counts of records awaiting human verification. */
export async function loadPendingVerification() {
const pb = await pbAdmin();
const [pos, dns] = await Promise.all([
pb.collection("purchase_orders").getFullList({
filter: "is_verified=false",
fields: "id,purchase_order_number,supplier,total_value,site",
}),
pb.collection("delivery_notes").getFullList({
filter: "is_verified=false",
fields: "id,delivery_note_number,processing_status,date_received,site",
}),
]);
return { pos, dns };
}