mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 23:46:35 +00:00
feat(assistant): expenses, shares, wind, journey tracks and Dawarich checks as MCP tools
- list_budget_items lists a trip's expenses with their ids, payers and splits, page by page so no page is cut down on its way to a model; get_budget_per_person answers who paid and owes what. Both in a new budget-items.mcp.ts, checked like their REST twins. - get_wind_forecast reads the hourly wind, gusts and direction of a day. - get_journey_tracks lists a journey's GPX tracks; the line for drawing rides under a key the provider never receives. - The Dawarich wishlist scan and the countries the recordings suggest become tools, with their confirming writes, in DawarichAtlasMcp in the Atlas group; the shared time window check refuses alike on REST and MCP, and the model never sees when the user was last seen in a city. - The journal's map, tracks and recorded days load with the journey group instead of riding in the core of every step. - The prompt points at list_budget_items for expense ids (the trip summary never carried them) and names the shares and the wind tools; each new read has its own live step.
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
DawarichSettingsDto,
|
||||
DawarichSuggestionStateDto,
|
||||
} from './dawarich.dto';
|
||||
import { parseTimeWindow } from './dawarich.helpers';
|
||||
import { DawarichService } from './dawarich.service';
|
||||
import {
|
||||
Body,
|
||||
@@ -176,7 +177,7 @@ export class DawarichController {
|
||||
|
||||
@Get('atlas/suggestions')
|
||||
async atlasSuggestions(@CurrentUser() user: User, @Query('from') from?: string, @Query('to') to?: string) {
|
||||
const window = parseWindow(from, to);
|
||||
const window = parseTimeWindow(from, to);
|
||||
return this.guardAsync(() => this.suggestions.atlasSuggestions(user.id, window.from, window.to));
|
||||
}
|
||||
|
||||
@@ -215,7 +216,7 @@ export class DawarichController {
|
||||
@Query('to') to?: string,
|
||||
@Query('offset') offset?: string,
|
||||
) {
|
||||
const window = parseWindow(from, to);
|
||||
const window = parseTimeWindow(from, to);
|
||||
return this.guardAsync(() =>
|
||||
this.tracks.forWindow(user.id, window.from.toISOString(), window.to.toISOString(), parseOffset(offset)),
|
||||
);
|
||||
@@ -310,25 +311,6 @@ function parseDate(raw: string | undefined): string | undefined {
|
||||
return raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* An explicit instant window. Both ends are required together: a half-open
|
||||
* range would silently become "everything since the epoch" on one side, which
|
||||
* is the request that makes an instance time out.
|
||||
*/
|
||||
function parseWindow(from: string | undefined, to: string | undefined): { from: Date; to: Date } {
|
||||
const start = from ? new Date(from) : null;
|
||||
const end = to ? new Date(to) : null;
|
||||
if (!start || !end || !Number.isFinite(start.getTime()) || !Number.isFinite(end.getTime())) {
|
||||
throw new HttpException({ error: 'from and to are required ISO timestamps' }, 400);
|
||||
}
|
||||
if (end <= start) throw new HttpException({ error: 'to must be after from' }, 400);
|
||||
const MAX_DAYS = 400;
|
||||
if (end.getTime() - start.getTime() > MAX_DAYS * 86_400_000) {
|
||||
throw new HttpException({ error: `Window must be ${MAX_DAYS} days or less` }, 400);
|
||||
}
|
||||
return { from: start, to: end };
|
||||
}
|
||||
|
||||
/** The originating client's socket id, so a broadcast does not echo back to it. */
|
||||
function socketId(req: Request): string | undefined {
|
||||
const header = req.headers['x-socket-id'];
|
||||
|
||||
Reference in New Issue
Block a user