@mseep/wanderlog-mcp 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +282 -0
  3. package/dist/cache/trip-cache.js +87 -0
  4. package/dist/cache/trip-cache.js.map +1 -0
  5. package/dist/config.js +38 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/context.js +12 -0
  8. package/dist/context.js.map +1 -0
  9. package/dist/errors.js +93 -0
  10. package/dist/errors.js.map +1 -0
  11. package/dist/formatters/trip-summary.js +296 -0
  12. package/dist/formatters/trip-summary.js.map +1 -0
  13. package/dist/http.js +165 -0
  14. package/dist/http.js.map +1 -0
  15. package/dist/index.js +48 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/ot/apply.js +282 -0
  18. package/dist/ot/apply.js.map +1 -0
  19. package/dist/resolvers/day.js +83 -0
  20. package/dist/resolvers/day.js.map +1 -0
  21. package/dist/resolvers/place-ref.js +194 -0
  22. package/dist/resolvers/place-ref.js.map +1 -0
  23. package/dist/server.js +164 -0
  24. package/dist/server.js.map +1 -0
  25. package/dist/tools/add-checklist.js +59 -0
  26. package/dist/tools/add-checklist.js.map +1 -0
  27. package/dist/tools/add-expense.js +127 -0
  28. package/dist/tools/add-expense.js.map +1 -0
  29. package/dist/tools/add-hotel.js +92 -0
  30. package/dist/tools/add-hotel.js.map +1 -0
  31. package/dist/tools/add-note.js +63 -0
  32. package/dist/tools/add-note.js.map +1 -0
  33. package/dist/tools/add-place.js +151 -0
  34. package/dist/tools/add-place.js.map +1 -0
  35. package/dist/tools/annotate-place.js +114 -0
  36. package/dist/tools/annotate-place.js.map +1 -0
  37. package/dist/tools/create-trip.js +85 -0
  38. package/dist/tools/create-trip.js.map +1 -0
  39. package/dist/tools/edit-note.js +217 -0
  40. package/dist/tools/edit-note.js.map +1 -0
  41. package/dist/tools/get-guide.js +42 -0
  42. package/dist/tools/get-guide.js.map +1 -0
  43. package/dist/tools/get-trip-url.js +52 -0
  44. package/dist/tools/get-trip-url.js.map +1 -0
  45. package/dist/tools/get-trip.js +43 -0
  46. package/dist/tools/get-trip.js.map +1 -0
  47. package/dist/tools/list-trips.js +32 -0
  48. package/dist/tools/list-trips.js.map +1 -0
  49. package/dist/tools/remove-note.js +107 -0
  50. package/dist/tools/remove-note.js.map +1 -0
  51. package/dist/tools/remove-place.js +95 -0
  52. package/dist/tools/remove-place.js.map +1 -0
  53. package/dist/tools/rename-day.js +64 -0
  54. package/dist/tools/rename-day.js.map +1 -0
  55. package/dist/tools/search-guides.js +172 -0
  56. package/dist/tools/search-guides.js.map +1 -0
  57. package/dist/tools/search-places.js +81 -0
  58. package/dist/tools/search-places.js.map +1 -0
  59. package/dist/tools/shared.js +216 -0
  60. package/dist/tools/shared.js.map +1 -0
  61. package/dist/tools/update-trip-dates.js +211 -0
  62. package/dist/tools/update-trip-dates.js.map +1 -0
  63. package/dist/transport/rest.js +152 -0
  64. package/dist/transport/rest.js.map +1 -0
  65. package/dist/transport/sharedb.js +323 -0
  66. package/dist/transport/sharedb.js.map +1 -0
  67. package/dist/types.js +7 -0
  68. package/dist/types.js.map +1 -0
  69. package/package.json +61 -0
@@ -0,0 +1,81 @@
1
+ import { z } from "zod";
2
+ import { WanderlogError, WanderlogValidationError } from "../errors.js";
3
+ import { findTripCenter } from "./shared.js";
4
+ export const searchPlacesInputSchema = {
5
+ trip_key: z
6
+ .string()
7
+ .min(1)
8
+ .describe("The trip to scope this search to. Search results are geographically biased toward the trip's destination."),
9
+ query: z
10
+ .string()
11
+ .min(1)
12
+ .describe("What to search for. Examples: 'sushi restaurant', 'hiking trail', 'coffee near the hotel'."),
13
+ response_format: z
14
+ .enum(["concise", "detailed"])
15
+ .default("concise")
16
+ .describe("Output verbosity. 'concise' lists name + description only; 'detailed' adds the Google place_id needed for downstream tool calls."),
17
+ };
18
+ export const searchPlacesDescription = `
19
+ Search for real-world places (restaurants, attractions, hotels, parks, landmarks) near the
20
+ destination of a Wanderlog trip. Returns candidate results with names and short descriptions.
21
+
22
+ Use this to resolve user requests like "find a good coffee shop in Queenstown" into specific
23
+ place candidates. Results are geographically biased toward the trip's location, not global.
24
+
25
+ If the user wants to *add* a place to a trip, call this first with concise format to present
26
+ options, then call again with detailed format to get place_ids for downstream actions.
27
+ `.trim();
28
+ export async function searchPlaces(ctx, args) {
29
+ try {
30
+ const entry = await ctx.tripCache.getEntry(args.trip_key);
31
+ const trip = entry.snapshot;
32
+ const center = findTripCenter(trip, entry.geos);
33
+ if (!center) {
34
+ throw new WanderlogValidationError("Cannot determine trip location", "This trip has no associated geo and no existing places.");
35
+ }
36
+ const predictions = await ctx.rest.searchPlacesAutocomplete({
37
+ input: args.query,
38
+ sessionToken: crypto.randomUUID(),
39
+ location: { latitude: center.lat, longitude: center.lng },
40
+ radius: 15000,
41
+ });
42
+ if (predictions.length === 0) {
43
+ return {
44
+ content: [
45
+ {
46
+ type: "text",
47
+ text: `No results for "${args.query}" near ${trip.title}. Try broadening the query.`,
48
+ },
49
+ ],
50
+ };
51
+ }
52
+ const text = formatPredictions(predictions, args.response_format ?? "concise");
53
+ return { content: [{ type: "text", text }] };
54
+ }
55
+ catch (err) {
56
+ const e = err instanceof WanderlogError
57
+ ? err.toUserMessage()
58
+ : `Unexpected error: ${err.message}`;
59
+ return { content: [{ type: "text", text: e }], isError: true };
60
+ }
61
+ }
62
+ function formatPredictions(predictions, format) {
63
+ const top = predictions.slice(0, 8);
64
+ if (format === "concise") {
65
+ return top
66
+ .map((p, i) => {
67
+ const main = p.structured_formatting?.main_text ?? p.description;
68
+ const sub = p.structured_formatting?.secondary_text ?? "";
69
+ return `${i + 1}. ${main}${sub ? ` — ${sub}` : ""}`;
70
+ })
71
+ .join("\n");
72
+ }
73
+ return top
74
+ .map((p, i) => {
75
+ const main = p.structured_formatting?.main_text ?? p.description;
76
+ const sub = p.structured_formatting?.secondary_text ?? "";
77
+ return `${i + 1}. ${main}${sub ? ` — ${sub}` : ""}\n place_id: ${p.place_id}`;
78
+ })
79
+ .join("\n");
80
+ }
81
+ //# sourceMappingURL=search-places.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-places.js","sourceRoot":"","sources":["../../src/tools/search-places.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,216 @@
1
+ import { WanderlogError, WanderlogValidationError } from "../errors.js";
2
+ import { resolveDay } from "../resolvers/day.js";
3
+ import { isPlaceBlock } from "../types.js";
4
+ /**
5
+ * Per-trip mutex — serializes submits against the same trip so concurrent
6
+ * callers can't race each other on the ShareDB version vector. Without this,
7
+ * parallel Promise.all batches of mutations all read the cache at version N
8
+ * simultaneously and submit stale ops that the server rejects as conflicts.
9
+ */
10
+ const submitLocks = new Map();
11
+ async function withSubmitLock(tripKey, fn) {
12
+ const prev = submitLocks.get(tripKey) ?? Promise.resolve();
13
+ // Chain regardless of whether the previous op succeeded or failed —
14
+ // one failed op should not permanently block the queue.
15
+ const next = prev.then(fn, fn);
16
+ // The map always holds a never-rejecting tail so the next caller can chain
17
+ // onto it safely. Dead promises are negligible; the map is keyed by trip.
18
+ submitLocks.set(tripKey, next.catch(() => { }));
19
+ return next;
20
+ }
21
+ /**
22
+ * Submit a JSON0 op array to the server and apply it to the live cache on
23
+ * success. Encapsulates the version handshake so tools don't touch
24
+ * ShareDBClient directly.
25
+ *
26
+ * Rules:
27
+ * - Trip must already be in the cache (caller should have called tripCache.get()).
28
+ * - Per-trip mutex: concurrent calls on the same trip serialize automatically.
29
+ * - Op fails atomically: if submit rejects, the cache is invalidated so the
30
+ * next read refetches a fresh snapshot from the server.
31
+ * - On success, cache.applyLocalOp() is called with the server-accepted version.
32
+ */
33
+ export async function submitOp(ctx, tripKey, ops) {
34
+ return withSubmitLock(tripKey, async () => {
35
+ const client = ctx.pool.get(tripKey);
36
+ if (!client.isSubscribed) {
37
+ throw new WanderlogError(`Trip ${tripKey} is not subscribed — call tripCache.get() first`, "not_subscribed");
38
+ }
39
+ try {
40
+ await submitWithRateLimitRetry(client, ops);
41
+ ctx.tripCache.applyLocalOp(tripKey, ops, client.version);
42
+ }
43
+ catch (err) {
44
+ // Any submit failure leaves our cached view possibly inconsistent with
45
+ // the server. Invalidate so the next get() refetches + resubscribes.
46
+ ctx.tripCache.invalidate(tripKey);
47
+ throw err;
48
+ }
49
+ });
50
+ }
51
+ const RATE_LIMIT_RETRY_DELAYS_MS = [2_000, 4_000, 8_000];
52
+ // A rate-limited op (code 4001) is rejected before the server processes it —
53
+ // it never acks and never applies — so resubmitting the same ops at the same
54
+ // version is safe. Burst mutations (e.g. an LLM building a full itinerary)
55
+ // hit the limit routinely; waiting out the window beats surfacing an error.
56
+ async function submitWithRateLimitRetry(client, ops) {
57
+ let attempt = 0;
58
+ for (;;) {
59
+ try {
60
+ await client.submit(ops);
61
+ return;
62
+ }
63
+ catch (err) {
64
+ const isRateLimit = err instanceof WanderlogError && err.code === "rate_limited";
65
+ if (!isRateLimit || attempt >= RATE_LIMIT_RETRY_DELAYS_MS.length) {
66
+ throw err;
67
+ }
68
+ await new Promise((r) => setTimeout(r, RATE_LIMIT_RETRY_DELAYS_MS[attempt]));
69
+ attempt += 1;
70
+ }
71
+ }
72
+ }
73
+ /** Wanderlog block IDs are 9-digit numeric. */
74
+ export function generateBlockId() {
75
+ return Math.floor(Math.random() * 1_000_000_000);
76
+ }
77
+ export function requireUserId(ctx) {
78
+ if (ctx.userId == null) {
79
+ throw new WanderlogError("User ID not available — auth probe has not completed", "no_user_id");
80
+ }
81
+ return ctx.userId;
82
+ }
83
+ /**
84
+ * Build a newly-inserted place block matching Wanderlog's schema.
85
+ * Based on the shape captured in HAR during real trip-add operations.
86
+ */
87
+ export function buildPlaceBlock(place, userId, extras = {}) {
88
+ const base = {
89
+ id: generateBlockId(),
90
+ type: "place",
91
+ place,
92
+ text: { ops: [{ insert: "\n" }] },
93
+ addedBy: { type: "user", userId },
94
+ imageSize: "small",
95
+ upvotedBy: [],
96
+ travelMode: null,
97
+ attachments: [],
98
+ };
99
+ if (extras.hotel) {
100
+ base.hotel = {
101
+ checkIn: extras.hotel.checkIn,
102
+ checkOut: extras.hotel.checkOut,
103
+ travelerNames: extras.hotel.travelerNames ?? [],
104
+ confirmationNumber: extras.hotel.confirmationNumber ?? null,
105
+ };
106
+ }
107
+ if (extras.startTime)
108
+ base.startTime = extras.startTime;
109
+ if (extras.endTime)
110
+ base.endTime = extras.endTime;
111
+ return base;
112
+ }
113
+ /**
114
+ * Finds the "Places to visit" section (the default normal+placeList section
115
+ * at the top of every trip). Returns its index in trip.itinerary.sections.
116
+ */
117
+ export function findPlacesToVisitSection(trip) {
118
+ for (let i = 0; i < trip.itinerary.sections.length; i++) {
119
+ const s = trip.itinerary.sections[i];
120
+ if (s.type === "normal" &&
121
+ s.mode === "placeList" &&
122
+ (s.heading === "Places to visit" || s.heading === "")) {
123
+ return { index: i, section: s };
124
+ }
125
+ }
126
+ return null;
127
+ }
128
+ /** Finds the first hotels-type section in the trip. */
129
+ export function findHotelsSection(trip) {
130
+ for (let i = 0; i < trip.itinerary.sections.length; i++) {
131
+ const s = trip.itinerary.sections[i];
132
+ if (s.type === "hotels")
133
+ return { index: i, section: s };
134
+ }
135
+ return null;
136
+ }
137
+ /**
138
+ * Finds a day section by ISO date. Returns null if no matching section exists
139
+ * (e.g. the date is outside the trip range).
140
+ */
141
+ export function findDaySectionByDate(trip, isoDate) {
142
+ for (let i = 0; i < trip.itinerary.sections.length; i++) {
143
+ const s = trip.itinerary.sections[i];
144
+ if (s.mode === "dayPlan" && s.date === isoDate) {
145
+ return { index: i, section: s };
146
+ }
147
+ }
148
+ return null;
149
+ }
150
+ /**
151
+ * Returns a search-biasing location for the trip. Tries in order:
152
+ * 1. The first place block with geometry (most specific)
153
+ * 2. The trip's first associated geo (from /api/tripPlans/{key} resources)
154
+ * 3. Null if both are absent
155
+ */
156
+ export function findTripCenter(trip, geos) {
157
+ for (const section of trip.itinerary.sections) {
158
+ for (const block of section.blocks) {
159
+ if (!isPlaceBlock(block))
160
+ continue;
161
+ const loc = block.place.geometry?.location;
162
+ if (loc)
163
+ return loc;
164
+ }
165
+ }
166
+ const first = geos?.[0];
167
+ if (first)
168
+ return { lat: first.latitude, lng: first.longitude };
169
+ return null;
170
+ }
171
+ /**
172
+ * Resolves the target section for adding a block — either a specific day
173
+ * or the "Places to visit" list. Shared by add-place, add-note, add-checklist.
174
+ */
175
+ export function findTargetSection(trip, day) {
176
+ if (day) {
177
+ const daySection = resolveDay(trip, day);
178
+ const found = findDaySectionByDate(trip, daySection.date);
179
+ if (!found) {
180
+ throw new WanderlogValidationError(`Day ${day} not found in trip`);
181
+ }
182
+ return { index: found.index, section: found.section, label: `day ${daySection.date}` };
183
+ }
184
+ const places = findPlacesToVisitSection(trip);
185
+ if (!places) {
186
+ throw new WanderlogError("Trip has no 'Places to visit' list", "no_places_section", "This is unexpected — Wanderlog usually creates one automatically. Try adding to a specific day instead.");
187
+ }
188
+ return { index: places.index, section: places.section, label: "places to visit" };
189
+ }
190
+ /** Build a note block matching the shape captured from the Wanderlog UI. */
191
+ export function buildNoteBlock(userId) {
192
+ return {
193
+ id: generateBlockId(),
194
+ type: "note",
195
+ text: { ops: [{ insert: "\n" }] },
196
+ addedBy: { type: "user", userId },
197
+ attachments: [],
198
+ };
199
+ }
200
+ /** Build a checklist block with pre-populated items. */
201
+ export function buildChecklistBlock(items, title, userId) {
202
+ const checklistItems = items.map((text) => ({
203
+ id: generateBlockId(),
204
+ checked: false,
205
+ text: { ops: [{ insert: `${text}\n` }] },
206
+ }));
207
+ return {
208
+ id: generateBlockId(),
209
+ type: "checklist",
210
+ items: checklistItems,
211
+ title,
212
+ addedBy: { type: "user", userId },
213
+ attachments: [],
214
+ };
215
+ }
216
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/tools/shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,211 @@
1
+ import { z } from "zod";
2
+ import { WanderlogError, WanderlogValidationError } from "../errors.js";
3
+ import { generateBlockId, submitOp } from "./shared.js";
4
+ export const updateTripDatesInputSchema = {
5
+ trip_key: z.string().min(1).describe("The trip to update."),
6
+ start_date: z
7
+ .string()
8
+ .regex(/^\d{4}-\d{2}-\d{2}$/, "must be YYYY-MM-DD")
9
+ .describe("New first day of the trip, YYYY-MM-DD."),
10
+ end_date: z
11
+ .string()
12
+ .regex(/^\d{4}-\d{2}-\d{2}$/, "must be YYYY-MM-DD")
13
+ .describe("New last day of the trip, YYYY-MM-DD. Must be >= start_date."),
14
+ force: z
15
+ .boolean()
16
+ .default(false)
17
+ .describe("If true, allows the update even when it would delete days that currently contain places. DEFAULT false — the tool refuses destructive removes with a helpful error listing what would be lost, so the user can move content first."),
18
+ };
19
+ export const updateTripDatesDescription = `
20
+ Changes the date range of an existing Wanderlog trip. Preserves content on days that remain
21
+ in the new range (they're kept in place with their blocks intact), adds empty day sections for
22
+ newly-included days, and removes day sections for days no longer in range.
23
+
24
+ SAFETY: If the new range would delete days that currently contain places or notes, the tool
25
+ refuses by default and returns a list of the content that would be lost. Pass force: true to
26
+ override, or move the content to other days first.
27
+
28
+ Does not affect the Hotels, Flights, Transit, or Places-to-visit sections — only the
29
+ per-day sections are added/removed.
30
+ `.trim();
31
+ /**
32
+ * Expands an inclusive date range [start, end] into an array of ISO dates.
33
+ * Uses UTC to avoid DST / timezone surprises.
34
+ */
35
+ export function enumerateDates(start, end) {
36
+ const startMs = Date.UTC(Number.parseInt(start.slice(0, 4), 10), Number.parseInt(start.slice(5, 7), 10) - 1, Number.parseInt(start.slice(8, 10), 10));
37
+ const endMs = Date.UTC(Number.parseInt(end.slice(0, 4), 10), Number.parseInt(end.slice(5, 7), 10) - 1, Number.parseInt(end.slice(8, 10), 10));
38
+ if (endMs < startMs)
39
+ return [];
40
+ const dates = [];
41
+ const dayMs = 24 * 60 * 60 * 1000;
42
+ for (let t = startMs; t <= endMs; t += dayMs) {
43
+ const d = new Date(t);
44
+ const yyyy = d.getUTCFullYear();
45
+ const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
46
+ const dd = String(d.getUTCDate()).padStart(2, "0");
47
+ dates.push(`${yyyy}-${mm}-${dd}`);
48
+ }
49
+ return dates;
50
+ }
51
+ /** Builds an empty dayPlan section matching Wanderlog's real shape. */
52
+ export function buildEmptyDaySection(date) {
53
+ return {
54
+ id: generateBlockId(),
55
+ type: "normal",
56
+ mode: "dayPlan",
57
+ heading: "",
58
+ // Server-side validation requires text on sections — an li without it gets
59
+ // silently dropped (no ack, submit times out).
60
+ text: { ops: [{ insert: "\n" }] },
61
+ date,
62
+ blocks: [],
63
+ placeMarkerColor: "#3498db",
64
+ placeMarkerIcon: "map-marker",
65
+ };
66
+ }
67
+ /**
68
+ * Finds the array index at which a new dayPlan section with `newDate`
69
+ * should be inserted to keep the day sections in ascending date order.
70
+ * Returns `sections.length` if it belongs at the end.
71
+ */
72
+ export function findDayInsertIndex(sections, newDate) {
73
+ for (let i = 0; i < sections.length; i++) {
74
+ const s = sections[i];
75
+ if (s.mode === "dayPlan" && s.date && s.date > newDate) {
76
+ return i;
77
+ }
78
+ }
79
+ return sections.length;
80
+ }
81
+ export function diffDays(trip, newStart, newEnd) {
82
+ const targetDates = new Set(enumerateDates(newStart, newEnd));
83
+ const currentByDate = new Map();
84
+ trip.itinerary.sections.forEach((section, index) => {
85
+ if (section.mode === "dayPlan" && section.date) {
86
+ currentByDate.set(section.date, { index, section });
87
+ }
88
+ });
89
+ const toRemove = [];
90
+ for (const [date, { index, section }] of currentByDate) {
91
+ if (!targetDates.has(date)) {
92
+ toRemove.push({ date, index, section });
93
+ }
94
+ }
95
+ const toAdd = [];
96
+ for (const date of targetDates) {
97
+ if (!currentByDate.has(date))
98
+ toAdd.push(date);
99
+ }
100
+ toAdd.sort();
101
+ return { toAdd, toRemove };
102
+ }
103
+ /**
104
+ * Pure op-builder. Given a trip and new dates, produces the JSON0 ops required
105
+ * to bring the trip into the new state, without touching the network.
106
+ *
107
+ * Throws WanderlogValidationError with a helpful list if destructive removes
108
+ * would drop non-empty day sections and `force` is false.
109
+ */
110
+ export function buildUpdateDatesOps(trip, newStartDate, newEndDate, force) {
111
+ if (newEndDate < newStartDate) {
112
+ throw new WanderlogValidationError(`end_date (${newEndDate}) is before start_date (${newStartDate})`);
113
+ }
114
+ const diff = diffDays(trip, newStartDate, newEndDate);
115
+ // Safety: check for destructive removes
116
+ if (!force) {
117
+ const nonEmpty = diff.toRemove.filter((r) => r.section.blocks && r.section.blocks.length > 0);
118
+ if (nonEmpty.length > 0) {
119
+ const lines = nonEmpty
120
+ .sort((a, b) => a.date.localeCompare(b.date))
121
+ .map((r) => ` ${r.date}: ${r.section.blocks.length} block(s)`)
122
+ .join("\n");
123
+ throw new WanderlogValidationError(`Shortening "${trip.title}" (${trip.startDate} → ${trip.endDate}) to ${newStartDate} → ${newEndDate} would delete content from ${nonEmpty.length} day(s):\n${lines}`, {
124
+ hint: "Pass force: true to delete anyway, or move the content to other days first.",
125
+ followUps: [
126
+ "Retry wanderlog_update_trip_dates with force: true if the user confirms the deletions are OK.",
127
+ "Or call wanderlog_remove_place to clear the affected days before shortening.",
128
+ ],
129
+ });
130
+ }
131
+ }
132
+ const ops = [];
133
+ // 1. Deletions in reverse index order so each ld doesn't invalidate the next
134
+ const removesByDesc = [...diff.toRemove].sort((a, b) => b.index - a.index);
135
+ for (const r of removesByDesc) {
136
+ ops.push({
137
+ p: ["itinerary", "sections", r.index],
138
+ ld: r.section,
139
+ });
140
+ }
141
+ // 2. Insertions — compute positions against the post-deletion state.
142
+ // Simulate the array locally so multi-insert cases get correct indices.
143
+ const removedSet = new Set(diff.toRemove.map((r) => r.section));
144
+ const simulated = trip.itinerary.sections.filter((s) => !removedSet.has(s));
145
+ for (const date of diff.toAdd) {
146
+ const newSection = buildEmptyDaySection(date);
147
+ const insertIndex = findDayInsertIndex(simulated, date);
148
+ ops.push({
149
+ p: ["itinerary", "sections", insertIndex],
150
+ li: newSection,
151
+ });
152
+ simulated.splice(insertIndex, 0, newSection);
153
+ }
154
+ // 3. Top-level field updates — od+oi pairs (what we've observed in ShareDB)
155
+ if (trip.startDate !== newStartDate) {
156
+ ops.push({
157
+ p: ["startDate"],
158
+ od: trip.startDate,
159
+ oi: newStartDate,
160
+ });
161
+ }
162
+ if (trip.endDate !== newEndDate) {
163
+ ops.push({
164
+ p: ["endDate"],
165
+ od: trip.endDate,
166
+ oi: newEndDate,
167
+ });
168
+ }
169
+ const newDays = enumerateDates(newStartDate, newEndDate).length;
170
+ if (trip.days !== newDays) {
171
+ ops.push({
172
+ p: ["days"],
173
+ od: trip.days,
174
+ oi: newDays,
175
+ });
176
+ }
177
+ return ops;
178
+ }
179
+ export async function updateTripDates(ctx, args) {
180
+ try {
181
+ const trip = await ctx.tripCache.get(args.trip_key);
182
+ const ops = buildUpdateDatesOps(trip, args.start_date, args.end_date, args.force ?? false);
183
+ if (ops.length === 0) {
184
+ return {
185
+ content: [
186
+ {
187
+ type: "text",
188
+ text: `"${trip.title}" already has dates ${args.start_date} → ${args.end_date}. No changes made.`,
189
+ },
190
+ ],
191
+ };
192
+ }
193
+ await submitOp(ctx, args.trip_key, ops);
194
+ const diff = diffDays(trip, args.start_date, args.end_date);
195
+ const summary = [`Updated "${trip.title}" to ${args.start_date} → ${args.end_date}.`];
196
+ if (diff.toAdd.length > 0) {
197
+ summary.push(` Added ${diff.toAdd.length} day(s): ${diff.toAdd.join(", ")}`);
198
+ }
199
+ if (diff.toRemove.length > 0) {
200
+ summary.push(` Removed ${diff.toRemove.length} day(s): ${diff.toRemove.map((r) => r.date).join(", ")}`);
201
+ }
202
+ return { content: [{ type: "text", text: summary.join("\n") }] };
203
+ }
204
+ catch (err) {
205
+ const msg = err instanceof WanderlogError
206
+ ? err.toUserMessage()
207
+ : `Unexpected error: ${err.message}`;
208
+ return { content: [{ type: "text", text: msg }], isError: true };
209
+ }
210
+ }
211
+ //# sourceMappingURL=update-trip-dates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-trip-dates.js","sourceRoot":"","sources":["../../src/tools/update-trip-dates.ts"],"names":[],"mappings":"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"}